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;
22 my ($help, $steps, $baseurl, $max_tries, $user, $password,$short_print);
28 'password:s' => \$password,
29 'maxtries:s' => \$max_tries,
30 'short' => \$short_print,
33 $max_tries=20 unless $max_tries;
34 # if steps not provided, run all tests
35 $steps='0123456789' unless $steps;
37 # if short is set, we will only give number for direct inclusion on the wiki
38 my $short_ms="|-\n|ON\n";
39 my $short_psec="|-\n|ON\n";
41 if ($help || !$baseurl || !$user || !$password) {
43 This script runs a benchmark of the staff interface. It benchmark 6 different pages:
44 \t1- the staff main page
45 \t2- the catalog detail page, with a random biblionumber
46 \t3- the catalog search page, using a term retrieved from one of the 10 first title/author in the database
47 \t4- the patron detail page, with a random borrowernumber
48 \t5- the patron search page, searching for "Jean"
49 \t6- the circulation itself, doing check-out and check-in of random items to random patrons
51 \t0 all those tests at once
54 \tsteps = which steps you want to run.
55 \t\tDon't use it if you want to run all tests. enter 125 if you want to run tests 1, 2 and 5
56 \t\tThe "all those tests at once" is numbered 0,and will run all tests previously run.
57 \t\tIf you run only one step, it's useless to run the 0, you'll get the same result.
58 \turl = the URL or your staff interface
60 \tpassword = Koha password
61 \tmaxtries = how many tries you want to do. Defaulted to 20
63 SAMPLE : ./benchmark_staff.pl --url=http://yourstaff.org/cgi-bin/koha/ --user=test --password=test --steps=12
71 # Authenticate via our handy dandy RESTful services
73 my $ua = LWP::UserAgent->new();
74 my $cookie_jar = HTTP::Cookies->new();
76 $ua->cookie_jar($cookie_jar);
77 my $resp = $ua->post( "$baseurl"."/svc/authentication" , {userid =>$user, password => $password} );
78 if( $resp->is_success ) {
79 $cookie_jar->extract_cookies( $resp );
80 $cookie = $cookie_jar->as_string;
81 unless ($short_print) {
82 print "Authentication successful\n";
83 print "Auth:\n $resp->content" if $debug;
87 # remove some unnecessary garbage from the cookie
88 $cookie =~ s/ path_spec; discard; version=0//;
89 $cookie =~ s/Set-Cookie3: //;
91 # Get some data to work with
92 my $dbh=C4::Context->dbh();
93 # grab some borrowernumbers
94 my $sth = $dbh->prepare("select max(borrowernumber) from borrowers");
96 my ($borrowernumber_max) = $sth->fetchrow;
98 for (my $i=1;$i<=$max_tries;$i++) {
99 my $rand_borrowernumber = int(rand($borrowernumber_max)+1);
100 push @borrowers,"$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber";
103 # grab some biblionumbers
104 $sth = $dbh->prepare("select max(biblionumber) from biblio");
106 my ($biblionumber_max) = $sth->fetchrow;
108 for (my $i=1;$i<=$max_tries;$i++) {
109 my $rand_biblionumber = int(rand($biblionumber_max)+1);
110 push @biblios,"$baseurl/catalogue/detail.pl?biblionumber=$rand_biblionumber";
113 # grab some title and author, for random search
114 $sth = $dbh->prepare ("SELECT title, author FROM biblio LIMIT 10");
118 while (($title,$author)=$sth->fetchrow) {
119 push @searchwords,split / /, $author;
120 push @searchwords,split / /, $title;
123 $sth = $dbh->prepare("select max(itemnumber) from items");
125 # find the biggest itemnumber
126 my ($itemnumber_max) = $sth->fetchrow;
129 unless ($short_print) {
130 print "--------------\n";
131 print "Koha STAFF benchmarking utility\n";
132 print "--------------\n";
133 print "Benchmarking with $max_tries occurences of each operation and $concurrency concurrent sessions \n";
136 # the global benchmark we do at the end...
138 my $b = HTTPD::Bench::ApacheBench->new;
139 $b->concurrency( $concurrency );
142 # STEP 1: mainpage : (very) low RDBMS dependency
145 my $b0 = HTTPD::Bench::ApacheBench->new;
146 $b0->concurrency( $concurrency ); my @mainpage;
147 unless ($short_print) {
148 print "Step 1: staff client main page ";
150 for (my $i=1;$i<=$max_tries;$i++) {
151 push @mainpage,"$baseurl/mainpage.pl";
153 my $run0 = HTTPD::Bench::ApacheBench::Run->new
154 ({ urls => \@mainpage,
155 cookies => [$cookie],
160 # send HTTP request sequences to server and time responses
164 $short_ms.= "|".$b0->total_time."\n";
165 $short_psec.="|".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)."\n";
167 print ("\t".$b0->total_time."ms\t".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)." pages/sec\n");
168 print "ALERT : ".$b0->total_responses_failed." failures\n" if $b0->total_responses_failed;
171 print "Skipping step 1\n";
178 my $b1 = HTTPD::Bench::ApacheBench->new;
179 $b1->concurrency( $concurrency );
181 unless ($short_print) {
182 print "Step 2: catalog detail page ";
184 my $run1 = HTTPD::Bench::ApacheBench::Run->new
185 ({ urls => \@biblios,
186 cookies => [$cookie],
191 # send HTTP request sequences to server and time responses
195 $short_ms.= "|".$b1->total_time."\n";
196 $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
198 print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n");
199 print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed;
202 print "Skipping step 2\n";
208 my $b1 = HTTPD::Bench::ApacheBench->new;
209 $b1->concurrency( $concurrency );
210 unless ($short_print) {
211 print "Step 3: catalogue search ";
214 for (my $i=1;$i<=$max_tries;$i++) {
215 push @searches,"$baseurl/catalogue/search.pl?q=".@searchwords[int(rand(scalar @searchwords))];
217 my $run1 = HTTPD::Bench::ApacheBench::Run->new
218 ({ urls => \@searches,
219 cookies => [$cookie],
224 # send HTTP request sequences to server and time responses
228 $short_ms.= "|".$b1->total_time."\n";
229 $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
231 print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n");
232 print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed;
235 print "Skipping step 3\n";
241 my $b2 = HTTPD::Bench::ApacheBench->new;
242 $b2->concurrency( $concurrency );
243 unless ($short_print) {
244 print "Step 5: patron detail page ";
246 my $run2 = HTTPD::Bench::ApacheBench::Run->new
247 ({ urls => \@borrowers,
248 cookies => [$cookie],
253 # send HTTP request sequences to server and time responses
257 $short_ms.= "|".$b2->total_time."\n";
258 $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
260 print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
263 print "Skipping step 4\n";
267 # STEP 5: borrowers search
270 my $b2 = HTTPD::Bench::ApacheBench->new;
271 $b2->concurrency( $concurrency );
272 unless ($short_print) {
273 print "Step 5: patron search page ";
275 for (my $i=1;$i<=$max_tries;$i++) {
276 # print "$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber\n";
277 push @borrowers,"$baseurl/members/member.pl?member=jean";
279 my $run2 = HTTPD::Bench::ApacheBench::Run->new
280 ({ urls => \@borrowers,
281 cookies => [$cookie],
286 # send HTTP request sequences to server and time responses
289 $short_ms.= "|".$b2->total_time."\n";
290 $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
292 print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
295 print "Skipping step 5\n";
299 # STEP 6: issue (& then return) books
302 my $b3 = HTTPD::Bench::ApacheBench->new;
303 $b3->concurrency( $concurrency );
304 my $b4 = HTTPD::Bench::ApacheBench->new;
305 $b4->concurrency( $concurrency );
309 unless ($short_print) {
310 print "Step 6a circulation (checkouts) ";
312 $sth = $dbh->prepare("SELECT barcode FROM items WHERE itemnumber=?");
313 my $sth2 = $dbh->prepare("SELECT borrowernumber FROM borrowers WHERE borrowernumber=?");
314 for (my $i=1;$i<=$max_tries;$i++) {
315 my $rand_borrowernumber;
316 # check that the borrowernumber exist
317 until ($rand_borrowernumber) {
318 $rand_borrowernumber = int(rand($borrowernumber_max)+1);
319 $sth2->execute($rand_borrowernumber);
320 ($rand_borrowernumber) = $sth2->fetchrow;
322 # find a barcode & check it exists
324 until ($rand_barcode) {
325 my $rand_itemnumber = int(rand($itemnumber_max)+1);
326 $sth->execute($rand_itemnumber);
327 ($rand_barcode) = $sth->fetchrow();
329 push @issues,"$baseurl/circ/circulation.pl?borrowernumber=$rand_borrowernumber&barcode=$rand_barcode&issueconfirmed=1";
330 push @returns,"$baseurl/circ/returns.pl?barcode=$rand_barcode";
332 my $run3 = HTTPD::Bench::ApacheBench::Run->new
334 cookies => [$cookie],
339 # send HTTP request sequences to server and time responses
343 $short_ms.= "|".$b3->total_time."\n";
344 $short_psec.="|".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)."\n";
346 print ("\t".$b3->total_time."ms\t".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)." checkouts/sec\n");
348 unless ($short_print) {
349 print "Step 6b circulation (checkins) ";
351 my $run4 = HTTPD::Bench::ApacheBench::Run->new
352 ({ urls => \@returns,
353 cookies => [$cookie],
358 # send HTTP request sequences to server and time responses
362 $short_ms.= "|".$b4->total_time."\n";
363 $short_psec.="|".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)."\n";
365 print ("\t".$b4->total_time."ms\t".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)." checkins/sec\n");
368 print "Skipping step 6\n";
372 unless ($short_print) {
373 print "all transactions at once ";
377 $short_ms.= "|".$b->total_time."\n";
378 $short_psec.="|".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)."\n";
380 print ("\t".$b->total_time."ms\t".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)." operations/sec\n");
383 print "Skipping 'testing all transactions at once'\n (step 0)";
387 print $short_ms."\n=====\n".$short_psec."\n";