2 # This script implements a basic benchmarking and regression testing
8 # find Koha's Perl modules
9 # test carefully before changing this
11 eval { require "$FindBin::Bin/kohalib.pl" };
15 use HTTPD::Bench::ApacheBench;
23 my ($help, $steps, $baseurl, $max_tries, $user, $password,$short_print);
29 'password:s' => \$password,
30 'maxtries:s' => \$max_tries,
31 'short' => \$short_print,
34 $max_tries=20 unless $max_tries;
35 # if steps not provided, run all tests
36 $steps='0123456789' unless $steps;
38 # if short is set, we will only give number for direct inclusion on the wiki
39 my $short_ms="|-\n|ON\n";
40 my $short_psec="|-\n|ON\n";
42 if ($help || !$baseurl || !$user || !$password) {
44 This script runs a benchmark of the staff interface. It benchmark 6 different pages:
45 \t1- the staff main page
46 \t2- the catalog detail page, with a random biblionumber
47 \t3- the catalog search page, using a term retrieved from one of the 10 first title/author in the database
48 \t4- the patron detail page, with a random borrowernumber
49 \t5- the patron search page, searching for "Jean"
50 \t6- the circulation itself, doing check-out and check-in of random items to random patrons
52 \t0 all those tests at once
55 \tsteps = which steps you want to run.
56 \t\tDon't use it if you want to run all tests. enter 125 if you want to run tests 1, 2 and 5
57 \t\tThe "all those tests at once" is numbered 0,and will run all tests previously run.
58 \t\tIf you run only one step, it's useless to run the 0, you'll get the same result.
59 \turl = the URL or your staff interface
61 \tpassword = Koha password
62 \tmaxtries = how many tries you want to do. Defaulted to 20
64 SAMPLE : ./benchmark_staff.pl --url=http://yourstaff.org/cgi-bin/koha/ --user=test --password=test --steps=12
72 # Authenticate via our handy dandy RESTful services
74 my $ua = LWP::UserAgent->new();
75 my $cookie_jar = HTTP::Cookies->new();
77 $ua->cookie_jar($cookie_jar);
78 my $resp = $ua->post( "$baseurl"."/svc/authentication" , {userid =>$user, password => $password} );
79 if( $resp->is_success ) {
80 $cookie_jar->extract_cookies( $resp );
81 $cookie = $cookie_jar->as_string;
82 unless ($short_print) {
83 print "Authentication successful\n";
84 print "Auth:\n $resp->content" if $debug;
88 # remove some unnecessary garbage from the cookie
89 $cookie =~ s/ path_spec; discard; version=0//;
90 $cookie =~ s/Set-Cookie3: //;
92 # Get some data to work with
93 my $dbh=C4::Context->dbh();
94 # grab some borrowernumbers
95 my $sth = $dbh->prepare("select max(borrowernumber) from borrowers");
97 my ($borrowernumber_max) = $sth->fetchrow;
99 for (my $i=1;$i<=$max_tries;$i++) {
100 my $rand_borrowernumber = int(rand($borrowernumber_max)+1);
101 push @borrowers,"$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber";
104 # grab some biblionumbers
105 $sth = $dbh->prepare("select max(biblionumber) from biblio");
107 my ($biblionumber_max) = $sth->fetchrow;
109 for (my $i=1;$i<=$max_tries;$i++) {
110 my $rand_biblionumber = int(rand($biblionumber_max)+1);
111 push @biblios,"$baseurl/catalogue/detail.pl?biblionumber=$rand_biblionumber";
114 # grab some title and author, for random search
115 $sth = $dbh->prepare ("SELECT title, author FROM biblio LIMIT 10");
119 while (($title,$author)=$sth->fetchrow) {
120 push @searchwords,split / /, $author;
121 push @searchwords,split / /, $title;
124 $sth = $dbh->prepare("select max(itemnumber) from items");
126 # find the biggest itemnumber
127 my ($itemnumber_max) = $sth->fetchrow;
130 unless ($short_print) {
131 print "--------------\n";
132 print "Koha STAFF benchmarking utility\n";
133 print "--------------\n";
134 print "Benchmarking with $max_tries occurences of each operation and $concurrency concurrent sessions \n";
137 # the global benchmark we do at the end...
139 my $b = HTTPD::Bench::ApacheBench->new;
140 $b->concurrency( $concurrency );
143 # STEP 1: mainpage : (very) low RDBMS dependency
146 my $b0 = HTTPD::Bench::ApacheBench->new;
147 $b0->concurrency( $concurrency ); my @mainpage;
148 unless ($short_print) {
149 print "Step 1: staff client main page ";
151 for (my $i=1;$i<=$max_tries;$i++) {
152 push @mainpage,"$baseurl/mainpage.pl";
154 my $run0 = HTTPD::Bench::ApacheBench::Run->new
155 ({ urls => \@mainpage,
156 cookies => [$cookie],
161 # send HTTP request sequences to server and time responses
165 $short_ms.= "|".$b0->total_time."\n";
166 $short_psec.="|".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)."\n";
168 print ("\t".$b0->total_time."ms\t".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)." pages/sec\n");
169 print "ALERT : ".$b0->total_responses_failed." failures\n" if $b0->total_responses_failed;
172 print "Skipping step 1\n";
179 my $b1 = HTTPD::Bench::ApacheBench->new;
180 $b1->concurrency( $concurrency );
182 unless ($short_print) {
183 print "Step 2: catalog detail page ";
185 my $run1 = HTTPD::Bench::ApacheBench::Run->new
186 ({ urls => \@biblios,
187 cookies => [$cookie],
192 # send HTTP request sequences to server and time responses
196 $short_ms.= "|".$b1->total_time."\n";
197 $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
199 print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n");
200 print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed;
203 print "Skipping step 2\n";
209 my $b1 = HTTPD::Bench::ApacheBench->new;
210 $b1->concurrency( $concurrency );
211 unless ($short_print) {
212 print "Step 3: catalogue search ";
215 for (my $i=1;$i<=$max_tries;$i++) {
216 push @searches,"$baseurl/catalogue/search.pl?q=".@searchwords[int(rand(scalar @searchwords))];
218 my $run1 = HTTPD::Bench::ApacheBench::Run->new
219 ({ urls => \@searches,
220 cookies => [$cookie],
225 # send HTTP request sequences to server and time responses
229 $short_ms.= "|".$b1->total_time."\n";
230 $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
232 print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n");
233 print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed;
236 print "Skipping step 3\n";
242 my $b2 = HTTPD::Bench::ApacheBench->new;
243 $b2->concurrency( $concurrency );
244 unless ($short_print) {
245 print "Step 4: patron detail page ";
247 my $run2 = HTTPD::Bench::ApacheBench::Run->new
248 ({ urls => \@borrowers,
249 cookies => [$cookie],
254 # send HTTP request sequences to server and time responses
258 $short_ms.= "|".$b2->total_time."\n";
259 $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
261 print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
264 print "Skipping step 4\n";
268 # STEP 5: borrowers search
271 my $b2 = HTTPD::Bench::ApacheBench->new;
272 $b2->concurrency( $concurrency );
273 unless ($short_print) {
274 print "Step 5: patron search page ";
276 for (my $i=1;$i<=$max_tries;$i++) {
277 # print "$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber\n";
278 push @borrowers,"$baseurl/members/member.pl?member=jean";
280 my $run2 = HTTPD::Bench::ApacheBench::Run->new
281 ({ urls => \@borrowers,
282 cookies => [$cookie],
287 # send HTTP request sequences to server and time responses
290 $short_ms.= "|".$b2->total_time."\n";
291 $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
293 print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
296 print "Skipping step 5\n";
300 # STEP 6: issue (& then return) books
303 my $b3 = HTTPD::Bench::ApacheBench->new;
304 $b3->concurrency( $concurrency );
305 my $b4 = HTTPD::Bench::ApacheBench->new;
306 $b4->concurrency( $concurrency );
310 unless ($short_print) {
311 print "Step 6a circulation (checkouts) ";
313 $sth = $dbh->prepare("SELECT barcode FROM items WHERE itemnumber=?");
314 my $sth2 = $dbh->prepare("SELECT borrowernumber FROM borrowers WHERE borrowernumber=?");
315 for (my $i=1;$i<=$max_tries;$i++) {
316 my $rand_borrowernumber;
317 # check that the borrowernumber exist
318 until ($rand_borrowernumber) {
319 $rand_borrowernumber = int(rand($borrowernumber_max)+1);
320 $sth2->execute($rand_borrowernumber);
321 ($rand_borrowernumber) = $sth2->fetchrow;
323 # find a barcode & check it exists
325 until ($rand_barcode) {
326 my $rand_itemnumber = int(rand($itemnumber_max)+1);
327 $sth->execute($rand_itemnumber);
328 ($rand_barcode) = uri_escape_utf8($sth->fetchrow());
330 push @issues,"$baseurl/circ/circulation.pl?borrowernumber=$rand_borrowernumber&barcode=$rand_barcode&issueconfirmed=1";
331 push @returns,"$baseurl/circ/returns.pl?barcode=$rand_barcode";
333 my $run3 = HTTPD::Bench::ApacheBench::Run->new
335 cookies => [$cookie],
340 # send HTTP request sequences to server and time responses
344 $short_ms.= "|".$b3->total_time."\n";
345 $short_psec.="|".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)."\n";
347 print ("\t".$b3->total_time."ms\t".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)." checkouts/sec\n");
349 unless ($short_print) {
350 print "Step 6b circulation (checkins) ";
352 my $run4 = HTTPD::Bench::ApacheBench::Run->new
353 ({ urls => \@returns,
354 cookies => [$cookie],
359 # send HTTP request sequences to server and time responses
363 $short_ms.= "|".$b4->total_time."\n";
364 $short_psec.="|".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)."\n";
366 print ("\t".$b4->total_time."ms\t".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)." checkins/sec\n");
369 print "Skipping step 6\n";
373 unless ($short_print) {
374 print "all transactions at once ";
378 $short_ms.= "|".$b->total_time."\n";
379 $short_psec.="|".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)."\n";
381 print ("\t".$b->total_time."ms\t".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)." operations/sec\n");
384 print "Skipping 'testing all transactions at once'\n (step 0)";
388 print $short_ms."\n=====\n".$short_psec."\n";