Bug 10572: Add phone to message_transport_types table for new installs
[koha.git] / misc / load_testing / benchmark_staff.pl
1 #!/usr/bin/perl
2 # This script implements a basic benchmarking and regression testing
3 # utility for Koha
4
5 use strict;
6 use warnings;
7 BEGIN {
8     # find Koha's Perl modules
9     # test carefully before changing this
10     use FindBin;
11     eval { require "$FindBin::Bin/kohalib.pl" };
12 }
13
14 use Getopt::Long;
15 use HTTPD::Bench::ApacheBench;
16 use LWP::UserAgent;
17 use Data::Dumper;
18 use HTTP::Cookies;
19 use C4::Context;
20 use C4::Debug;
21 use URI::Escape;
22
23 my ($help, $steps, $baseurl, $max_tries, $user, $password,$short_print);
24 GetOptions(
25     'help'    => \$help,
26     'steps:s'   => \$steps,
27     'url:s' => \$baseurl,
28     'user:s' => \$user,
29     'password:s' => \$password,
30     'maxtries:s' => \$max_tries,
31     'short' => \$short_print,
32 );
33 my $concurrency = 30;
34 $max_tries=20 unless $max_tries;
35 # if steps not provided, run all tests
36 $steps='0123456789' unless $steps;
37
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";
41
42 if ($help || !$baseurl || !$user || !$password) {
43     print <<EOF
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
51
52 \t0 all those tests at once
53 parameters :
54 \thelp = this screen
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
60 \tlogin = Koha login
61 \tpassword = Koha password
62 \tmaxtries = how many tries you want to do. Defaulted to 20
63
64 SAMPLE : ./benchmark_staff.pl --url=http://yourstaff.org/cgi-bin/koha/ --user=test --password=test --steps=12
65
66 EOF
67 ;
68 exit;
69 }
70
71
72 # Authenticate via our handy dandy RESTful services
73 # and grab a cookie
74 my $ua = LWP::UserAgent->new();
75 my $cookie_jar = HTTP::Cookies->new();
76 my $cookie;
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;
85     }
86 } elsif ( $resp->is_success ) {
87     die "Authentication failure: bad login/password";
88 } else {
89     die "Authentication failure: \n\t" . $resp->status_line;
90 }
91
92 # remove some unnecessary garbage from the cookie
93 $cookie =~ s/ path_spec; discard; version=0//;
94 $cookie =~ s/Set-Cookie3: //;
95
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");
100 $sth->execute;
101 my ($borrowernumber_max) = $sth->fetchrow;
102 my @borrowers;
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";
106 }
107
108 # grab some biblionumbers
109 $sth = $dbh->prepare("select max(biblionumber) from biblio");
110 $sth->execute;
111 my ($biblionumber_max) = $sth->fetchrow;
112 my @biblios;
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";
116 }
117
118 # grab some title and author, for random search
119 $sth = $dbh->prepare ("SELECT title, author FROM biblio LIMIT 10");
120 $sth->execute;
121 my ($title,$author);
122 my @searchwords;
123 while (($title,$author)=$sth->fetchrow) {
124     push @searchwords,split / /, $author;
125     push @searchwords,split / /, $title;
126 }
127
128 $sth = $dbh->prepare("select max(itemnumber) from items");
129 $sth->execute;
130 # find the biggest itemnumber
131 my ($itemnumber_max) = $sth->fetchrow;
132
133 $|=1;
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";
139 }
140 #
141 # the global benchmark we do at the end...
142 #
143 my $b = HTTPD::Bench::ApacheBench->new;
144 $b->concurrency( $concurrency );
145 my $ro;
146 #
147 # STEP 1: mainpage : (very) low RDBMS dependency
148 #
149 if ($steps=~ /1/) {
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     ";
154     }
155     for (my $i=1;$i<=$max_tries;$i++) {
156         push @mainpage,"$baseurl/mainpage.pl";
157     }
158     my $run0 = HTTPD::Bench::ApacheBench::Run->new
159         ({ urls => \@mainpage,
160            cookies => [$cookie],
161         });
162     $b0->add_run($run0);
163     $b->add_run($run0);
164
165     # send HTTP request sequences to server and time responses
166     $ro = $b0->execute;
167     # calculate hits/sec
168     if ($short_print) {
169         $short_ms.= "|".$b0->total_time."\n";
170         $short_psec.="|".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)."\n";
171     } else {
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;
174     }
175 } else {
176     print "Skipping step 1\n";
177 }
178
179 #
180 # STEP 2: biblios
181 #
182 if ($steps=~ /2/) {
183     my $b1 = HTTPD::Bench::ApacheBench->new;
184     $b1->concurrency( $concurrency );
185
186     unless ($short_print) {
187         print "Step 2: catalog detail page        ";
188     }
189     my $run1 = HTTPD::Bench::ApacheBench::Run->new
190         ({ urls => \@biblios,
191            cookies => [$cookie],
192         });
193     $b1->add_run($run1);
194     $b->add_run($run1);
195
196     # send HTTP request sequences to server and time responses
197     $ro = $b1->execute;
198     # calculate hits/sec
199     if ($short_print) {
200         $short_ms.= "|".$b1->total_time."\n";
201         $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
202     } else {
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;
205     }
206 } else {
207     print "Skipping step 2\n";
208 }
209 #
210 # STEP 3: search
211 #
212 if ($steps=~ /3/) {
213     my $b1 = HTTPD::Bench::ApacheBench->new;
214     $b1->concurrency( $concurrency );
215     unless ($short_print) {
216         print "Step 3: catalogue search               ";
217     }
218     my @searches;
219     for (my $i=1;$i<=$max_tries;$i++) {
220         push @searches,"$baseurl/catalogue/search.pl?q=".@searchwords[int(rand(scalar @searchwords))];
221     }
222     my $run1 = HTTPD::Bench::ApacheBench::Run->new
223         ({ urls => \@searches,
224            cookies => [$cookie],
225         });
226     $b1->add_run($run1);
227     $b->add_run($run1);
228
229     # send HTTP request sequences to server and time responses
230     $ro = $b1->execute;
231     # calculate hits/sec
232     if ($short_print) {
233         $short_ms.= "|".$b1->total_time."\n";
234         $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n";
235     } else {
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;
238     }
239 } else {
240     print "Skipping step 3\n";
241 }
242 #
243 # STEP 4: borrowers
244 #
245 if ($steps=~ /4/) {
246     my $b2 = HTTPD::Bench::ApacheBench->new;
247     $b2->concurrency( $concurrency );
248     unless ($short_print) {
249         print "Step 4: patron detail page         ";
250     }
251     my $run2 = HTTPD::Bench::ApacheBench::Run->new
252         ({ urls => \@borrowers,
253            cookies => [$cookie],
254         });
255     $b2->add_run($run2);
256     $b->add_run($run2);
257
258     # send HTTP request sequences to server and time responses
259     $ro = $b2->execute;
260     # calculate hits/sec
261     if ($short_print) {
262         $short_ms.= "|".$b2->total_time."\n";
263         $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
264     } else {
265         print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
266     }
267 } else {
268     print "Skipping step 4\n";
269 }
270
271 #
272 # STEP 5: borrowers search
273 #
274 if ($steps=~ /5/) {
275     my $b2 = HTTPD::Bench::ApacheBench->new;
276     $b2->concurrency( $concurrency );
277     unless ($short_print) {
278         print "Step 5: patron search page             ";
279     }
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";
283     }
284     my $run2 = HTTPD::Bench::ApacheBench::Run->new
285         ({ urls => \@borrowers,
286            cookies => [$cookie],
287         });
288     $b2->add_run($run2);
289     $b->add_run($run2);
290
291     # send HTTP request sequences to server and time responses
292     $ro = $b2->execute;
293     if ($short_print) {
294         $short_ms.= "|".$b2->total_time."\n";
295         $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n";
296     } else {
297         print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n");
298     }
299 } else {
300     print "Skipping step 5\n";
301 }
302
303 #
304 # STEP 6: issue (& then return) books
305 #
306 if ($steps=~ /6/) {
307     my $b3 = HTTPD::Bench::ApacheBench->new;
308     $b3->concurrency( $concurrency );
309     my $b4 = HTTPD::Bench::ApacheBench->new;
310     $b4->concurrency( $concurrency );
311
312     my @issues;
313     my @returns;
314     unless ($short_print) {
315         print "Step 6a circulation (checkouts)        ";
316     }
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;
326         }
327         # find a barcode & check it exists
328         my $rand_barcode;
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());
333         }
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";
336     }
337     my $run3 = HTTPD::Bench::ApacheBench::Run->new
338         ({ urls => \@issues,
339            cookies => [$cookie],
340         });
341     $b3->add_run($run3);
342     $b->add_run($run3);
343
344     # send HTTP request sequences to server and time responses
345     $ro = $b3->execute;
346     # calculate hits/sec
347     if ($short_print) {
348         $short_ms.= "|".$b3->total_time."\n";
349         $short_psec.="|".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)."\n";
350     } else {
351         print ("\t".$b3->total_time."ms\t".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)." checkouts/sec\n");
352     }
353     unless ($short_print) {
354         print "Step 6b circulation (checkins)         ";
355     }
356     my $run4 = HTTPD::Bench::ApacheBench::Run->new
357         ({ urls => \@returns,
358            cookies => [$cookie],
359         });
360     $b4->add_run($run4);
361     $b->add_run($run4);
362
363     # send HTTP request sequences to server and time responses
364     $ro = $b4->execute;
365     # calculate hits/sec
366     if ($short_print) {
367         $short_ms.= "|".$b4->total_time."\n";
368         $short_psec.="|".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)."\n";
369     } else {
370         print ("\t".$b4->total_time."ms\t".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)." checkins/sec\n");
371     }
372 } else {
373     print "Skipping step 6\n";
374 }
375
376 if ($steps=~ /0/) {
377     unless ($short_print) {
378         print "all transactions at once               ";
379     }
380     $ro = $b->execute;
381     if ($short_print) {
382         $short_ms.= "|".$b->total_time."\n";
383         $short_psec.="|".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)."\n";
384     } else {
385         print ("\t".$b->total_time."ms\t".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)." operations/sec\n");
386     }
387 } else {
388     print "Skipping 'testing all transactions at once'\n (step 0)";
389 }
390
391 if ($short_print) {
392 print $short_ms."\n=====\n".$short_psec."\n";
393 }