Lines 1-388
Link Here
|
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 |
|
8 |
use Getopt::Long qw( GetOptions ); |
9 |
use HTTPD::Bench::ApacheBench; |
10 |
use LWP::UserAgent; |
11 |
use HTTP::Cookies; |
12 |
use C4::Context; |
13 |
use URI::Escape qw( uri_escape_utf8 ); |
14 |
use Koha::Patrons; |
15 |
|
16 |
my ($help, $steps, $baseurl, $max_tries, $user, $password,$short_print); |
17 |
GetOptions( |
18 |
'help' => \$help, |
19 |
'steps:s' => \$steps, |
20 |
'url:s' => \$baseurl, |
21 |
'user:s' => \$user, |
22 |
'password:s' => \$password, |
23 |
'maxtries:s' => \$max_tries, |
24 |
'short' => \$short_print, |
25 |
); |
26 |
my $concurrency = 30; |
27 |
$max_tries=20 unless $max_tries; |
28 |
# if steps not provided, run all tests |
29 |
$steps='0123456789' unless $steps; |
30 |
|
31 |
# if short is set, we will only give number for direct inclusion on the wiki |
32 |
my $short_ms="|-\n|ON\n"; |
33 |
my $short_psec="|-\n|ON\n"; |
34 |
|
35 |
if ($help || !$baseurl || !$user || !$password) { |
36 |
print <<EOF |
37 |
This script runs a benchmark of the staff interface. It benchmarks 6 different pages: |
38 |
\t1- the staff main page |
39 |
\t2- the catalog detail page, with a random biblionumber |
40 |
\t3- the catalog search page, using a term retrieved from one of the 10 first titles/authors in the database |
41 |
\t4- the patron detail page, with a random borrowernumber |
42 |
\t5- the patron search page, searching for "Jean" |
43 |
\t6- the circulation itself, doing check-out and check-in of random items to random patrons |
44 |
|
45 |
\t0 all those tests at once |
46 |
parameters : |
47 |
\thelp = this screen |
48 |
\tsteps = which steps you want to run. |
49 |
\t\tDon't use it if you want to run all tests. enter 125 if you want to run tests 1, 2 and 5 |
50 |
\t\tThe "all those tests at once" is numbered 0,and will run all tests previously run. |
51 |
\t\tIf you run only one step, it's useless to run the 0, you'll get the same result. |
52 |
\turl = the URL or your staff interface |
53 |
\tlogin = Koha login |
54 |
\tpassword = Koha password |
55 |
\tmaxtries = how many tries you want to do. Defaulted to 20 |
56 |
|
57 |
SAMPLE : ./benchmark_staff.pl --url=http://yourstaff.org/cgi-bin/koha/ --user=test --password=test --steps=12 |
58 |
|
59 |
EOF |
60 |
; |
61 |
exit; |
62 |
} |
63 |
|
64 |
|
65 |
# Authenticate via our handy dandy RESTful services |
66 |
# and grab a cookie |
67 |
my $ua = LWP::UserAgent->new(); |
68 |
my $cookie_jar = HTTP::Cookies->new(); |
69 |
my $cookie; |
70 |
$ua->cookie_jar($cookie_jar); |
71 |
my $resp = $ua->post( "$baseurl"."/svc/authentication" , {userid =>$user, password => $password} ); |
72 |
if( $resp->is_success and $resp->content =~ m|<status>ok</status>| ) { |
73 |
$cookie_jar->extract_cookies( $resp ); |
74 |
$cookie = $cookie_jar->as_string; |
75 |
unless ($short_print) { |
76 |
print "Authentication successful\n"; |
77 |
} |
78 |
} elsif ( $resp->is_success ) { |
79 |
die "Authentication failure: bad login/password"; |
80 |
} else { |
81 |
die "Authentication failure: \n\t" . $resp->status_line; |
82 |
} |
83 |
|
84 |
die "You cannot use the database administrator account to launch this script" |
85 |
unless defined Koha::Patrons->find( { userid => $user } ); |
86 |
|
87 |
# remove some unnecessary garbage from the cookie |
88 |
$cookie =~ s/ path_spec; discard; version=0//; |
89 |
$cookie =~ s/Set-Cookie3: //; |
90 |
|
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"); |
95 |
$sth->execute; |
96 |
my ($borrowernumber_max) = $sth->fetchrow; |
97 |
my @borrowers; |
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"; |
101 |
} |
102 |
|
103 |
# grab some biblionumbers |
104 |
$sth = $dbh->prepare("select max(biblionumber) from biblio"); |
105 |
$sth->execute; |
106 |
my ($biblionumber_max) = $sth->fetchrow; |
107 |
my @biblios; |
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"; |
111 |
} |
112 |
|
113 |
# grab some title and author, for random search |
114 |
$sth = $dbh->prepare ("SELECT title, author FROM biblio LIMIT 10"); |
115 |
$sth->execute; |
116 |
my ($title,$author); |
117 |
my @searchwords; |
118 |
while (($title,$author)=$sth->fetchrow) { |
119 |
push @searchwords,split / /, $author//''; |
120 |
push @searchwords,split / /, $title//''; |
121 |
} |
122 |
|
123 |
$sth = $dbh->prepare("select max(itemnumber) from items"); |
124 |
$sth->execute; |
125 |
# find the biggest itemnumber |
126 |
my ($itemnumber_max) = $sth->fetchrow; |
127 |
|
128 |
$|=1; |
129 |
unless ($short_print) { |
130 |
print "--------------\n"; |
131 |
print "Koha STAFF benchmarking utility\n"; |
132 |
print "--------------\n"; |
133 |
print "Benchmarking with $max_tries occurrences of each operation and $concurrency concurrent sessions \n"; |
134 |
} |
135 |
# |
136 |
# the global benchmark we do at the end... |
137 |
# |
138 |
my $b = HTTPD::Bench::ApacheBench->new; |
139 |
$b->concurrency( $concurrency ); |
140 |
my $ro; |
141 |
# |
142 |
# STEP 1: mainpage : (very) low RDBMS dependency |
143 |
# |
144 |
if ($steps=~ /1/) { |
145 |
my $b0 = HTTPD::Bench::ApacheBench->new; |
146 |
$b0->concurrency( $concurrency ); my @mainpage; |
147 |
unless ($short_print) { |
148 |
print "Step 1: staff interface main page "; |
149 |
} |
150 |
for (my $i=1;$i<=$max_tries;$i++) { |
151 |
push @mainpage,"$baseurl/mainpage.pl"; |
152 |
} |
153 |
my $run0 = HTTPD::Bench::ApacheBench::Run->new |
154 |
({ urls => \@mainpage, |
155 |
cookies => [$cookie], |
156 |
}); |
157 |
$b0->add_run($run0); |
158 |
$b->add_run($run0); |
159 |
|
160 |
# send HTTP request sequences to server and time responses |
161 |
$ro = $b0->execute; |
162 |
# calculate hits/sec |
163 |
if ($short_print) { |
164 |
$short_ms.= "|".$b0->total_time."\n"; |
165 |
$short_psec.="|".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)."\n"; |
166 |
} else { |
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; |
169 |
} |
170 |
} else { |
171 |
print "Skipping step 1\n"; |
172 |
} |
173 |
|
174 |
# |
175 |
# STEP 2: biblios |
176 |
# |
177 |
if ($steps=~ /2/) { |
178 |
my $b1 = HTTPD::Bench::ApacheBench->new; |
179 |
$b1->concurrency( $concurrency ); |
180 |
|
181 |
unless ($short_print) { |
182 |
print "Step 2: catalog detail page "; |
183 |
} |
184 |
my $run1 = HTTPD::Bench::ApacheBench::Run->new |
185 |
({ urls => \@biblios, |
186 |
cookies => [$cookie], |
187 |
}); |
188 |
$b1->add_run($run1); |
189 |
$b->add_run($run1); |
190 |
|
191 |
# send HTTP request sequences to server and time responses |
192 |
$ro = $b1->execute; |
193 |
# calculate hits/sec |
194 |
if ($short_print) { |
195 |
$short_ms.= "|".$b1->total_time."\n"; |
196 |
$short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n"; |
197 |
} else { |
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; |
200 |
} |
201 |
} else { |
202 |
print "Skipping step 2\n"; |
203 |
} |
204 |
# |
205 |
# STEP 3: search |
206 |
# |
207 |
if ($steps=~ /3/) { |
208 |
my $b1 = HTTPD::Bench::ApacheBench->new; |
209 |
$b1->concurrency( $concurrency ); |
210 |
unless ($short_print) { |
211 |
print "Step 3: catalogue search "; |
212 |
} |
213 |
my @searches; |
214 |
for (my $i=1;$i<=$max_tries;$i++) { |
215 |
push @searches,"$baseurl/catalogue/search.pl?q=".@searchwords[int(rand(scalar @searchwords))]; |
216 |
} |
217 |
my $run1 = HTTPD::Bench::ApacheBench::Run->new |
218 |
({ urls => \@searches, |
219 |
cookies => [$cookie], |
220 |
}); |
221 |
$b1->add_run($run1); |
222 |
$b->add_run($run1); |
223 |
|
224 |
# send HTTP request sequences to server and time responses |
225 |
$ro = $b1->execute; |
226 |
# calculate hits/sec |
227 |
if ($short_print) { |
228 |
$short_ms.= "|".$b1->total_time."\n"; |
229 |
$short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n"; |
230 |
} else { |
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; |
233 |
} |
234 |
} else { |
235 |
print "Skipping step 3\n"; |
236 |
} |
237 |
# |
238 |
# STEP 4: borrowers |
239 |
# |
240 |
if ($steps=~ /4/) { |
241 |
my $b2 = HTTPD::Bench::ApacheBench->new; |
242 |
$b2->concurrency( $concurrency ); |
243 |
unless ($short_print) { |
244 |
print "Step 4: patron detail page "; |
245 |
} |
246 |
my $run2 = HTTPD::Bench::ApacheBench::Run->new |
247 |
({ urls => \@borrowers, |
248 |
cookies => [$cookie], |
249 |
}); |
250 |
$b2->add_run($run2); |
251 |
$b->add_run($run2); |
252 |
|
253 |
# send HTTP request sequences to server and time responses |
254 |
$ro = $b2->execute; |
255 |
# calculate hits/sec |
256 |
if ($short_print) { |
257 |
$short_ms.= "|".$b2->total_time."\n"; |
258 |
$short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n"; |
259 |
} else { |
260 |
print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n"); |
261 |
} |
262 |
} else { |
263 |
print "Skipping step 4\n"; |
264 |
} |
265 |
|
266 |
# |
267 |
# STEP 5: borrowers search |
268 |
# |
269 |
if ($steps=~ /5/) { |
270 |
my $b2 = HTTPD::Bench::ApacheBench->new; |
271 |
$b2->concurrency( $concurrency ); |
272 |
unless ($short_print) { |
273 |
print "Step 5: patron search page "; |
274 |
} |
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"; |
278 |
} |
279 |
my $run2 = HTTPD::Bench::ApacheBench::Run->new |
280 |
({ urls => \@borrowers, |
281 |
cookies => [$cookie], |
282 |
}); |
283 |
$b2->add_run($run2); |
284 |
$b->add_run($run2); |
285 |
|
286 |
# send HTTP request sequences to server and time responses |
287 |
$ro = $b2->execute; |
288 |
if ($short_print) { |
289 |
$short_ms.= "|".$b2->total_time."\n"; |
290 |
$short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n"; |
291 |
} else { |
292 |
print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n"); |
293 |
} |
294 |
} else { |
295 |
print "Skipping step 5\n"; |
296 |
} |
297 |
|
298 |
# |
299 |
# STEP 6: issue (& then return) books |
300 |
# |
301 |
if ($steps=~ /6/) { |
302 |
my $b3 = HTTPD::Bench::ApacheBench->new; |
303 |
$b3->concurrency( $concurrency ); |
304 |
my $b4 = HTTPD::Bench::ApacheBench->new; |
305 |
$b4->concurrency( $concurrency ); |
306 |
|
307 |
my @issues; |
308 |
my @returns; |
309 |
unless ($short_print) { |
310 |
print "Step 6a circulation (checkouts) "; |
311 |
} |
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; |
321 |
} |
322 |
# find a barcode & check it exists |
323 |
my $rand_barcode; |
324 |
until ($rand_barcode) { |
325 |
my $rand_itemnumber = int(rand($itemnumber_max)+1); |
326 |
$sth->execute($rand_itemnumber); |
327 |
($rand_barcode) = uri_escape_utf8($sth->fetchrow()); |
328 |
} |
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"; |
331 |
} |
332 |
my $run3 = HTTPD::Bench::ApacheBench::Run->new |
333 |
({ urls => \@issues, |
334 |
cookies => [$cookie], |
335 |
}); |
336 |
$b3->add_run($run3); |
337 |
$b->add_run($run3); |
338 |
|
339 |
# send HTTP request sequences to server and time responses |
340 |
$ro = $b3->execute; |
341 |
# calculate hits/sec |
342 |
if ($short_print) { |
343 |
$short_ms.= "|".$b3->total_time."\n"; |
344 |
$short_psec.="|".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)."\n"; |
345 |
} else { |
346 |
print ("\t".$b3->total_time."ms\t".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)." checkouts/sec\n"); |
347 |
} |
348 |
unless ($short_print) { |
349 |
print "Step 6b circulation (checkins) "; |
350 |
} |
351 |
my $run4 = HTTPD::Bench::ApacheBench::Run->new |
352 |
({ urls => \@returns, |
353 |
cookies => [$cookie], |
354 |
}); |
355 |
$b4->add_run($run4); |
356 |
$b->add_run($run4); |
357 |
|
358 |
# send HTTP request sequences to server and time responses |
359 |
$ro = $b4->execute; |
360 |
# calculate hits/sec |
361 |
if ($short_print) { |
362 |
$short_ms.= "|".$b4->total_time."\n"; |
363 |
$short_psec.="|".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)."\n"; |
364 |
} else { |
365 |
print ("\t".$b4->total_time."ms\t".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)." checkins/sec\n"); |
366 |
} |
367 |
} else { |
368 |
print "Skipping step 6\n"; |
369 |
} |
370 |
|
371 |
if ($steps=~ /0/) { |
372 |
unless ($short_print) { |
373 |
print "all transactions at once "; |
374 |
} |
375 |
$ro = $b->execute; |
376 |
if ($short_print) { |
377 |
$short_ms.= "|".$b->total_time."\n"; |
378 |
$short_psec.="|".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)."\n"; |
379 |
} else { |
380 |
print ("\t".$b->total_time."ms\t".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)." operations/sec\n"); |
381 |
} |
382 |
} else { |
383 |
print "Skipping 'testing all transactions at once'\n (step 0)"; |
384 |
} |
385 |
|
386 |
if ($short_print) { |
387 |
print $short_ms."\n=====\n".$short_psec."\n"; |
388 |
} |