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 and $resp->content =~ m|<status>ok</status>| ) {
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;
86 } elsif ( $resp->is_success ) {
87 die "Authentication failure: bad login/password";
89 die "Authentication failure: \n\t" . $resp->status_line;
92 # remove some unnecessary garbage from the cookie
93 $cookie =~ s/ path_spec; discard; version=0//;
94 $cookie =~ s/Set-Cookie3: //;
96 # Get some data to work with
97 my $dbh=C4::Context->dbh();
98 # grab some borrowernumbers
99 my $sth = $dbh->prepare("select max(borrowernumber) from borrowers");
101 my ($borrowernumber_max) = $sth->fetchrow;
103 for (my $i=1;$i<=$max_tries;$i++) {
104 my $rand_borrowernumber = int(rand($borrowernumber_max)+1);
105 push @borrowers,"$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber";
108 # grab some biblionumbers
109 $sth = $dbh->prepare("select max(biblionumber) from biblio");
111 my ($biblionumber_max) = $sth->fetchrow;
113 for (my $i=1;$i<=$max_tries;$i++) {
114 my $rand_biblionumber = int(rand($biblionumber_max)+1);
115 push @biblios,"$baseurl/catalogue/detail.pl?biblionumber=$rand_biblionumber";
118 # grab some title and author, for random search
119 $sth = $dbh->prepare ("SELECT title, author FROM biblio LIMIT 10");
123 while (($title,$author)=$sth->fetchrow) {
124 push @searchwords,split / /, $author;
125 push @searchwords,split / /, $title;
128 $sth = $dbh->prepare("select max(itemnumber) from items");
130 # find the biggest itemnumber
131 my ($itemnumber_max) = $sth->fetchrow;
134 unless ($short_print) {
135 print "--------------\n";
136 print "Koha STAFF benchmarking utility\n";
137 print "--------------\n";
138 print "Benchmarking with $max_tries occurences of each operation and $concurrency concurrent sessions \n";
141 # the global benchmark we do at the end...
143 my $b = HTTPD::Bench::ApacheBench->new;
144 $b->concurrency( $concurrency );
147 # STEP 1: mainpage : (very) low RDBMS dependency
150 my $b0 = HTTPD::Bench::ApacheBench->new;
151 $b0->concurrency( $concurrency ); my @mainpage;
152 unless ($short_print) {
153 print "Step 1: staff client main page ";
155 for (my $i=1;$i<=$max_tries;$i++) {
156 push @mainpage,"$baseurl/mainpage.pl";
158 my $run0 = HTTPD::Bench::ApacheBench::Run->new
159 ({ urls => \@mainpage,
160 cookies => [$cookie],
165 # send HTTP request sequences to server and time responses
169 $short_ms.= "|".$b0->total_time."\n";
170 $short_psec.="|".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)."\n";
172 print ("\t".$b0->total_time."ms\t".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)." pages/sec\n");
173 print "ALERT : ".$b0->total_responses_failed." failures\n" if $b0->total_responses_failed;
176 print "Skipping step 1\n";
183 my $b1 = HTTPD::Bench::ApacheBench->new;
184 $b1->concurrency( $concurrency );
186 unless ($short_print) {
187 print "Step 2: catalog detail page ";
189 my $run1 = HTTPD::Bench::ApacheBench::Run->new
190 ({ urls => \@biblios,
191 cookies => [$cookie],
196 # send HTTP request sequences to server and time responses
200 $short_ms.= "|".$b1->total_time."\n";
201 $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
203 print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n");
204 print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed;
207 print "Skipping step 2\n";
213 my $b1 = HTTPD::Bench::ApacheBench->new;
214 $b1->concurrency( $concurrency );
215 unless ($short_print) {
216 print "Step 3: catalogue search ";
219 for (my $i=1;$i<=$max_tries;$i++) {
220 push @searches,"$baseurl/catalogue/search.pl?q=".@searchwords[int(rand(scalar @searchwords))];
222 my $run1 = HTTPD::Bench::ApacheBench::Run->new
223 ({ urls => \@searches,
224 cookies => [$cookie],
229 # send HTTP request sequences to server and time responses
233 $short_ms.= "|".$b1->total_time."\n";
234 $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
236 print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n");
237 print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed;
240 print "Skipping step 3\n";
246 my $b2 = HTTPD::Bench::ApacheBench->new;
247 $b2->concurrency( $concurrency );
248 unless ($short_print) {
249 print "Step 4: patron detail page ";
251 my $run2 = HTTPD::Bench::ApacheBench::Run->new
252 ({ urls => \@borrowers,
253 cookies => [$cookie],
258 # send HTTP request sequences to server and time responses
262 $short_ms.= "|".$b2->total_time."\n";
263 $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
265 print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
268 print "Skipping step 4\n";
272 # STEP 5: borrowers search
275 my $b2 = HTTPD::Bench::ApacheBench->new;
276 $b2->concurrency( $concurrency );
277 unless ($short_print) {
278 print "Step 5: patron search page ";
280 for (my $i=1;$i<=$max_tries;$i++) {
281 # print "$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber\n";
282 push @borrowers,"$baseurl/members/member.pl?member=jean";
284 my $run2 = HTTPD::Bench::ApacheBench::Run->new
285 ({ urls => \@borrowers,
286 cookies => [$cookie],
291 # send HTTP request sequences to server and time responses
294 $short_ms.= "|".$b2->total_time."\n";
295 $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
297 print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
300 print "Skipping step 5\n";
304 # STEP 6: issue (& then return) books
307 my $b3 = HTTPD::Bench::ApacheBench->new;
308 $b3->concurrency( $concurrency );
309 my $b4 = HTTPD::Bench::ApacheBench->new;
310 $b4->concurrency( $concurrency );
314 unless ($short_print) {
315 print "Step 6a circulation (checkouts) ";
317 $sth = $dbh->prepare("SELECT barcode FROM items WHERE itemnumber=?");
318 my $sth2 = $dbh->prepare("SELECT borrowernumber FROM borrowers WHERE borrowernumber=?");
319 for (my $i=1;$i<=$max_tries;$i++) {
320 my $rand_borrowernumber;
321 # check that the borrowernumber exist
322 until ($rand_borrowernumber) {
323 $rand_borrowernumber = int(rand($borrowernumber_max)+1);
324 $sth2->execute($rand_borrowernumber);
325 ($rand_borrowernumber) = $sth2->fetchrow;
327 # find a barcode & check it exists
329 until ($rand_barcode) {
330 my $rand_itemnumber = int(rand($itemnumber_max)+1);
331 $sth->execute($rand_itemnumber);
332 ($rand_barcode) = uri_escape_utf8($sth->fetchrow());
334 push @issues,"$baseurl/circ/circulation.pl?borrowernumber=$rand_borrowernumber&barcode=$rand_barcode&issueconfirmed=1";
335 push @returns,"$baseurl/circ/returns.pl?barcode=$rand_barcode";
337 my $run3 = HTTPD::Bench::ApacheBench::Run->new
339 cookies => [$cookie],
344 # send HTTP request sequences to server and time responses
348 $short_ms.= "|".$b3->total_time."\n";
349 $short_psec.="|".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)."\n";
351 print ("\t".$b3->total_time."ms\t".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)." checkouts/sec\n");
353 unless ($short_print) {
354 print "Step 6b circulation (checkins) ";
356 my $run4 = HTTPD::Bench::ApacheBench::Run->new
357 ({ urls => \@returns,
358 cookies => [$cookie],
363 # send HTTP request sequences to server and time responses
367 $short_ms.= "|".$b4->total_time."\n";
368 $short_psec.="|".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)."\n";
370 print ("\t".$b4->total_time."ms\t".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)." checkins/sec\n");
373 print "Skipping step 6\n";
377 unless ($short_print) {
378 print "all transactions at once ";
382 $short_ms.= "|".$b->total_time."\n";
383 $short_psec.="|".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)."\n";
385 print ("\t".$b->total_time."ms\t".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)." operations/sec\n");
388 print "Skipping 'testing all transactions at once'\n (step 0)";
392 print $short_ms."\n=====\n".$short_psec."\n";