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
;
24 my ($help, $steps, $baseurl, $max_tries, $user, $password,$short_print);
30 'password:s' => \
$password,
31 'maxtries:s' => \
$max_tries,
32 'short' => \
$short_print,
35 $max_tries=20 unless $max_tries;
36 # if steps not provided, run all tests
37 $steps='0123456789' unless $steps;
39 # if short is set, we will only give number for direct inclusion on the wiki
40 my $short_ms="|-\n|ON\n";
41 my $short_psec="|-\n|ON\n";
43 if ($help || !$baseurl || !$user || !$password) {
45 This script runs a benchmark of the staff interface. It benchmarks 6 different pages:
46 \t1- the staff main page
47 \t2- the catalog detail page, with a random biblionumber
48 \t3- the catalog search page, using a term retrieved from one of the 10 first titles/authors in the database
49 \t4- the patron detail page, with a random borrowernumber
50 \t5- the patron search page, searching for "Jean"
51 \t6- the circulation itself, doing check-out and check-in of random items to random patrons
53 \t0 all those tests at once
56 \tsteps = which steps you want to run.
57 \t\tDon't use it if you want to run all tests. enter 125 if you want to run tests 1, 2 and 5
58 \t\tThe "all those tests at once" is numbered 0,and will run all tests previously run.
59 \t\tIf you run only one step, it's useless to run the 0, you'll get the same result.
60 \turl = the URL or your staff interface
62 \tpassword = Koha password
63 \tmaxtries = how many tries you want to do. Defaulted to 20
65 SAMPLE : ./benchmark_staff.pl --url=http://yourstaff.org/cgi-bin/koha/ --user=test --password=test --steps=12
73 # Authenticate via our handy dandy RESTful services
75 my $ua = LWP
::UserAgent
->new();
76 my $cookie_jar = HTTP
::Cookies
->new();
78 $ua->cookie_jar($cookie_jar);
79 my $resp = $ua->post( "$baseurl"."/svc/authentication" , {userid
=>$user, password
=> $password} );
80 if( $resp->is_success and $resp->content =~ m
|<status
>ok
</status
>| ) {
81 $cookie_jar->extract_cookies( $resp );
82 $cookie = $cookie_jar->as_string;
83 unless ($short_print) {
84 print "Authentication successful\n";
85 print "Auth:\n $resp->content" if $debug;
87 } elsif ( $resp->is_success ) {
88 die "Authentication failure: bad login/password";
90 die "Authentication failure: \n\t" . $resp->status_line;
93 die "You cannot use the database administrator account to launch this script"
94 unless defined Koha
::Patrons
->find( { userid
=> $user } );
96 # remove some unnecessary garbage from the cookie
97 $cookie =~ s/ path_spec; discard; version=0//;
98 $cookie =~ s/Set-Cookie3: //;
100 # Get some data to work with
101 my $dbh=C4
::Context
->dbh();
102 # grab some borrowernumbers
103 my $sth = $dbh->prepare("select max(borrowernumber) from borrowers");
105 my ($borrowernumber_max) = $sth->fetchrow;
107 for (my $i=1;$i<=$max_tries;$i++) {
108 my $rand_borrowernumber = int(rand($borrowernumber_max)+1);
109 push @borrowers,"$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber";
112 # grab some biblionumbers
113 $sth = $dbh->prepare("select max(biblionumber) from biblio");
115 my ($biblionumber_max) = $sth->fetchrow;
117 for (my $i=1;$i<=$max_tries;$i++) {
118 my $rand_biblionumber = int(rand($biblionumber_max)+1);
119 push @biblios,"$baseurl/catalogue/detail.pl?biblionumber=$rand_biblionumber";
122 # grab some title and author, for random search
123 $sth = $dbh->prepare ("SELECT title, author FROM biblio LIMIT 10");
127 while (($title,$author)=$sth->fetchrow) {
128 push @searchwords,split / /, $author//'';
129 push @searchwords,split / /, $title//'';
132 $sth = $dbh->prepare("select max(itemnumber) from items");
134 # find the biggest itemnumber
135 my ($itemnumber_max) = $sth->fetchrow;
138 unless ($short_print) {
139 print "--------------\n";
140 print "Koha STAFF benchmarking utility\n";
141 print "--------------\n";
142 print "Benchmarking with $max_tries occurrences of each operation and $concurrency concurrent sessions \n";
145 # the global benchmark we do at the end...
147 my $b = HTTPD
::Bench
::ApacheBench
->new;
148 $b->concurrency( $concurrency );
151 # STEP 1: mainpage : (very) low RDBMS dependency
154 my $b0 = HTTPD
::Bench
::ApacheBench
->new;
155 $b0->concurrency( $concurrency ); my @mainpage;
156 unless ($short_print) {
157 print "Step 1: staff interface main page ";
159 for (my $i=1;$i<=$max_tries;$i++) {
160 push @mainpage,"$baseurl/mainpage.pl";
162 my $run0 = HTTPD
::Bench
::ApacheBench
::Run
->new
163 ({ urls
=> \
@mainpage,
164 cookies
=> [$cookie],
169 # send HTTP request sequences to server and time responses
173 $short_ms.= "|".$b0->total_time."\n";
174 $short_psec.="|".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)."\n";
176 print ("\t".$b0->total_time."ms\t".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)." pages/sec\n");
177 print "ALERT : ".$b0->total_responses_failed." failures\n" if $b0->total_responses_failed;
180 print "Skipping step 1\n";
187 my $b1 = HTTPD
::Bench
::ApacheBench
->new;
188 $b1->concurrency( $concurrency );
190 unless ($short_print) {
191 print "Step 2: catalog detail page ";
193 my $run1 = HTTPD
::Bench
::ApacheBench
::Run
->new
194 ({ urls
=> \
@biblios,
195 cookies
=> [$cookie],
200 # send HTTP request sequences to server and time responses
204 $short_ms.= "|".$b1->total_time."\n";
205 $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
207 print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n");
208 print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed;
211 print "Skipping step 2\n";
217 my $b1 = HTTPD
::Bench
::ApacheBench
->new;
218 $b1->concurrency( $concurrency );
219 unless ($short_print) {
220 print "Step 3: catalogue search ";
223 for (my $i=1;$i<=$max_tries;$i++) {
224 push @searches,"$baseurl/catalogue/search.pl?q=".@searchwords[int(rand(scalar @searchwords))];
226 my $run1 = HTTPD
::Bench
::ApacheBench
::Run
->new
227 ({ urls
=> \
@searches,
228 cookies
=> [$cookie],
233 # send HTTP request sequences to server and time responses
237 $short_ms.= "|".$b1->total_time."\n";
238 $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
240 print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n");
241 print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed;
244 print "Skipping step 3\n";
250 my $b2 = HTTPD
::Bench
::ApacheBench
->new;
251 $b2->concurrency( $concurrency );
252 unless ($short_print) {
253 print "Step 4: patron detail page ";
255 my $run2 = HTTPD
::Bench
::ApacheBench
::Run
->new
256 ({ urls
=> \
@borrowers,
257 cookies
=> [$cookie],
262 # send HTTP request sequences to server and time responses
266 $short_ms.= "|".$b2->total_time."\n";
267 $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
269 print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
272 print "Skipping step 4\n";
276 # STEP 5: borrowers search
279 my $b2 = HTTPD
::Bench
::ApacheBench
->new;
280 $b2->concurrency( $concurrency );
281 unless ($short_print) {
282 print "Step 5: patron search page ";
284 for (my $i=1;$i<=$max_tries;$i++) {
285 # print "$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber\n";
286 push @borrowers,"$baseurl/members/member.pl?member=jean";
288 my $run2 = HTTPD
::Bench
::ApacheBench
::Run
->new
289 ({ urls
=> \
@borrowers,
290 cookies
=> [$cookie],
295 # send HTTP request sequences to server and time responses
298 $short_ms.= "|".$b2->total_time."\n";
299 $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
301 print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
304 print "Skipping step 5\n";
308 # STEP 6: issue (& then return) books
311 my $b3 = HTTPD
::Bench
::ApacheBench
->new;
312 $b3->concurrency( $concurrency );
313 my $b4 = HTTPD
::Bench
::ApacheBench
->new;
314 $b4->concurrency( $concurrency );
318 unless ($short_print) {
319 print "Step 6a circulation (checkouts) ";
321 $sth = $dbh->prepare("SELECT barcode FROM items WHERE itemnumber=?");
322 my $sth2 = $dbh->prepare("SELECT borrowernumber FROM borrowers WHERE borrowernumber=?");
323 for (my $i=1;$i<=$max_tries;$i++) {
324 my $rand_borrowernumber;
325 # check that the borrowernumber exist
326 until ($rand_borrowernumber) {
327 $rand_borrowernumber = int(rand($borrowernumber_max)+1);
328 $sth2->execute($rand_borrowernumber);
329 ($rand_borrowernumber) = $sth2->fetchrow;
331 # find a barcode & check it exists
333 until ($rand_barcode) {
334 my $rand_itemnumber = int(rand($itemnumber_max)+1);
335 $sth->execute($rand_itemnumber);
336 ($rand_barcode) = uri_escape_utf8
($sth->fetchrow());
338 push @issues,"$baseurl/circ/circulation.pl?borrowernumber=$rand_borrowernumber&barcode=$rand_barcode&issueconfirmed=1";
339 push @returns,"$baseurl/circ/returns.pl?barcode=$rand_barcode";
341 my $run3 = HTTPD
::Bench
::ApacheBench
::Run
->new
343 cookies
=> [$cookie],
348 # send HTTP request sequences to server and time responses
352 $short_ms.= "|".$b3->total_time."\n";
353 $short_psec.="|".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)."\n";
355 print ("\t".$b3->total_time."ms\t".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)." checkouts/sec\n");
357 unless ($short_print) {
358 print "Step 6b circulation (checkins) ";
360 my $run4 = HTTPD
::Bench
::ApacheBench
::Run
->new
361 ({ urls
=> \
@returns,
362 cookies
=> [$cookie],
367 # send HTTP request sequences to server and time responses
371 $short_ms.= "|".$b4->total_time."\n";
372 $short_psec.="|".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)."\n";
374 print ("\t".$b4->total_time."ms\t".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)." checkins/sec\n");
377 print "Skipping step 6\n";
381 unless ($short_print) {
382 print "all transactions at once ";
386 $short_ms.= "|".$b->total_time."\n";
387 $short_psec.="|".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)."\n";
389 print ("\t".$b->total_time."ms\t".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)." operations/sec\n");
392 print "Skipping 'testing all transactions at once'\n (step 0)";
396 print $short_ms."\n=====\n".$short_psec."\n";