From df80c6358c7f4ec98c1ac90ad4c333e01bb3e9d5 Mon Sep 17 00:00:00 2001 From: Paul Poulain Date: Fri, 2 Dec 2011 17:31:41 +0100 Subject: [PATCH] Bug 6015, new script for load testing This benchmark_staff.pl script is based on the previous benchmark_circulation.pl script As it does not test only circulation, the renaming was necessary. The script has many enhancement compared to the benchmark_circulation.pl one The benchmark_staff will run a benchmark on the following pages: * mainpage.pl * catalogue/detail.pl * catalogue/search.pl * members/member.pl (search on a name) * members/member.pl (search on a 1st letter) * circulation/circulation.pl and return.pl (check-out and check-in) * all those steps at the same time run the script without any parameter to get syntax --- misc/load_testing/benchmark_staff.pl | 388 +++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 misc/load_testing/benchmark_staff.pl diff --git a/misc/load_testing/benchmark_staff.pl b/misc/load_testing/benchmark_staff.pl new file mode 100644 index 0000000000..ba659c926f --- /dev/null +++ b/misc/load_testing/benchmark_staff.pl @@ -0,0 +1,388 @@ +#!/usr/bin/perl +# This script implements a basic benchmarking and regression testing +# utility for Koha + +use strict; +use warnings; +BEGIN { + # find Koha's Perl modules + # test carefully before changing this + use FindBin; + eval { require "$FindBin::Bin/kohalib.pl" }; +} + +use Getopt::Long; +use HTTPD::Bench::ApacheBench; +use LWP::UserAgent; +use Data::Dumper; +use HTTP::Cookies; +use C4::Context; +use C4::Debug; + +my ($help, $steps, $baseurl, $max_tries, $user, $password,$short_print); +GetOptions( + 'help' => \$help, + 'steps:s' => \$steps, + 'url:s' => \$baseurl, + 'user:s' => \$user, + 'password:s' => \$password, + 'maxtries:s' => \$max_tries, + 'short' => \$short_print, +); +my $concurrency = 30; +$max_tries=20 unless $max_tries; +# if steps not provided, run all tests +$steps='0123456789' unless $steps; + +# if short is set, we will only give number for direct inclusion on the wiki +my $short_ms="|-\n|ON\n"; +my $short_psec="|-\n|ON\n"; + +if ($help || !$baseurl || !$user || !$password) { + print <new(); +my $cookie_jar = HTTP::Cookies->new(); +my $cookie; +$ua->cookie_jar($cookie_jar); +my $resp = $ua->post( "$baseurl"."/svc/authentication" , {userid =>$user, password => $password} ); +if( $resp->is_success ) { + $cookie_jar->extract_cookies( $resp ); + $cookie = $cookie_jar->as_string; + unless ($short_print) { + print "Authentication successful\n"; + print "Auth:\n $resp->content" if $debug; + } +} + +# remove some unnecessary garbage from the cookie +$cookie =~ s/ path_spec; discard; version=0//; +$cookie =~ s/Set-Cookie3: //; + +# Get some data to work with +my $dbh=C4::Context->dbh(); +# grab some borrowernumbers +my $sth = $dbh->prepare("select max(borrowernumber) from borrowers"); +$sth->execute; +my ($borrowernumber_max) = $sth->fetchrow; +my @borrowers; +for (my $i=1;$i<=$max_tries;$i++) { + my $rand_borrowernumber = int(rand($borrowernumber_max)+1); + push @borrowers,"$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber"; +} + +# grab some biblionumbers +$sth = $dbh->prepare("select max(biblionumber) from biblio"); +$sth->execute; +my ($biblionumber_max) = $sth->fetchrow; +my @biblios; +for (my $i=1;$i<=$max_tries;$i++) { + my $rand_biblionumber = int(rand($biblionumber_max)+1); + push @biblios,"$baseurl/catalogue/detail.pl?biblionumber=$rand_biblionumber"; +} + +# grab some title and author, for random search +$sth = $dbh->prepare ("SELECT title, author FROM biblio LIMIT 10"); +$sth->execute; +my ($title,$author); +my @searchwords; +while (($title,$author)=$sth->fetchrow) { + push @searchwords,split / /, $author; + push @searchwords,split / /, $title; +} + +$sth = $dbh->prepare("select max(itemnumber) from items"); +$sth->execute; +# find the biggest itemnumber +my ($itemnumber_max) = $sth->fetchrow; + +$|=1; +unless ($short_print) { + print "--------------\n"; + print "Koha STAFF benchmarking utility\n"; + print "--------------\n"; + print "Benchmarking with $max_tries occurences of each operation and $concurrency concurrent sessions \n"; +} +# +# the global benchmark we do at the end... +# +my $b = HTTPD::Bench::ApacheBench->new; +$b->concurrency( $concurrency ); +my $ro; +# +# STEP 1: mainpage : (very) low RDBMS dependency +# +if ($steps=~ /1/) { + my $b0 = HTTPD::Bench::ApacheBench->new; + $b0->concurrency( $concurrency ); my @mainpage; + unless ($short_print) { + print "Step 1: staff client main page "; + } + for (my $i=1;$i<=$max_tries;$i++) { + push @mainpage,"$baseurl/mainpage.pl"; + } + my $run0 = HTTPD::Bench::ApacheBench::Run->new + ({ urls => \@mainpage, + cookies => [$cookie], + }); + $b0->add_run($run0); + $b->add_run($run0); + + # send HTTP request sequences to server and time responses + $ro = $b0->execute; + # calculate hits/sec + if ($short_print) { + $short_ms.= "|".$b0->total_time."\n"; + $short_psec.="|".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)."\n"; + } else { + print ("\t".$b0->total_time."ms\t".(int((1000*$b0->total_requests/$b0->total_time)*1000)/1000)." pages/sec\n"); + print "ALERT : ".$b0->total_responses_failed." failures\n" if $b0->total_responses_failed; + } +} else { + print "Skipping step 1\n"; +} + +# +# STEP 2: biblios +# +if ($steps=~ /2/) { + my $b1 = HTTPD::Bench::ApacheBench->new; + $b1->concurrency( $concurrency ); + + unless ($short_print) { + print "Step 2: catalog detail page "; + } + my $run1 = HTTPD::Bench::ApacheBench::Run->new + ({ urls => \@biblios, + cookies => [$cookie], + }); + $b1->add_run($run1); + $b->add_run($run1); + + # send HTTP request sequences to server and time responses + $ro = $b1->execute; + # calculate hits/sec + if ($short_print) { + $short_ms.= "|".$b1->total_time."\n"; + $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n"; + } else { + print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n"); + print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed; + } +} else { + print "Skipping step 2\n"; +} +# +# STEP 3: search +# +if ($steps=~ /3/) { + my $b1 = HTTPD::Bench::ApacheBench->new; + $b1->concurrency( $concurrency ); + unless ($short_print) { + print "Step 3: catalogue search "; + } + my @searches; + for (my $i=1;$i<=$max_tries;$i++) { + push @searches,"$baseurl/catalogue/search.pl?q=".@searchwords[int(rand(scalar @searchwords))]; + } + my $run1 = HTTPD::Bench::ApacheBench::Run->new + ({ urls => \@searches, + cookies => [$cookie], + }); + $b1->add_run($run1); + $b->add_run($run1); + + # send HTTP request sequences to server and time responses + $ro = $b1->execute; + # calculate hits/sec + if ($short_print) { + $short_ms.= "|".$b1->total_time."\n"; + $short_psec.="|".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)."\n"; + } else { + print ("\t".$b1->total_time."ms\t".(int((1000*$b1->total_requests/$b1->total_time)*1000)/1000)." biblios/sec\n"); + print "ALERT : ".$b1->total_responses_failed." failures\n" if $b1->total_responses_failed; + } +} else { + print "Skipping step 3\n"; +} +# +# STEP 4: borrowers +# +if ($steps=~ /4/) { + my $b2 = HTTPD::Bench::ApacheBench->new; + $b2->concurrency( $concurrency ); + unless ($short_print) { + print "Step 5: patron detail page "; + } + my $run2 = HTTPD::Bench::ApacheBench::Run->new + ({ urls => \@borrowers, + cookies => [$cookie], + }); + $b2->add_run($run2); + $b->add_run($run2); + + # send HTTP request sequences to server and time responses + $ro = $b2->execute; + # calculate hits/sec + if ($short_print) { + $short_ms.= "|".$b2->total_time."\n"; + $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n"; + } else { + print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n"); + } +} else { + print "Skipping step 4\n"; +} + +# +# STEP 5: borrowers search +# +if ($steps=~ /5/) { + my $b2 = HTTPD::Bench::ApacheBench->new; + $b2->concurrency( $concurrency ); + unless ($short_print) { + print "Step 5: patron search page "; + } + for (my $i=1;$i<=$max_tries;$i++) { + # print "$baseurl/members/moremember.pl?borrowernumber=$rand_borrowernumber\n"; + push @borrowers,"$baseurl/members/member.pl?member=jean"; + } + my $run2 = HTTPD::Bench::ApacheBench::Run->new + ({ urls => \@borrowers, + cookies => [$cookie], + }); + $b2->add_run($run2); + $b->add_run($run2); + + # send HTTP request sequences to server and time responses + $ro = $b2->execute; + if ($short_print) { + $short_ms.= "|".$b2->total_time."\n"; + $short_psec.="|".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)."\n"; + } else { + print ("\t".$b2->total_time."ms\t".(int((1000*$b2->total_requests/$b2->total_time)*1000)/1000)." borrowers/sec\n"); + } +} else { + print "Skipping step 5\n"; +} + +# +# STEP 6: issue (& then return) books +# +if ($steps=~ /6/) { + my $b3 = HTTPD::Bench::ApacheBench->new; + $b3->concurrency( $concurrency ); + my $b4 = HTTPD::Bench::ApacheBench->new; + $b4->concurrency( $concurrency ); + + my @issues; + my @returns; + unless ($short_print) { + print "Step 6a circulation (checkouts) "; + } + $sth = $dbh->prepare("SELECT barcode FROM items WHERE itemnumber=?"); + my $sth2 = $dbh->prepare("SELECT borrowernumber FROM borrowers WHERE borrowernumber=?"); + for (my $i=1;$i<=$max_tries;$i++) { + my $rand_borrowernumber; + # check that the borrowernumber exist + until ($rand_borrowernumber) { + $rand_borrowernumber = int(rand($borrowernumber_max)+1); + $sth2->execute($rand_borrowernumber); + ($rand_borrowernumber) = $sth2->fetchrow; + } + # find a barcode & check it exists + my $rand_barcode; + until ($rand_barcode) { + my $rand_itemnumber = int(rand($itemnumber_max)+1); + $sth->execute($rand_itemnumber); + ($rand_barcode) = $sth->fetchrow(); + } + push @issues,"$baseurl/circ/circulation.pl?borrowernumber=$rand_borrowernumber&barcode=$rand_barcode&issueconfirmed=1"; + push @returns,"$baseurl/circ/returns.pl?barcode=$rand_barcode"; + } + my $run3 = HTTPD::Bench::ApacheBench::Run->new + ({ urls => \@issues, + cookies => [$cookie], + }); + $b3->add_run($run3); + $b->add_run($run3); + + # send HTTP request sequences to server and time responses + $ro = $b3->execute; + # calculate hits/sec + if ($short_print) { + $short_ms.= "|".$b3->total_time."\n"; + $short_psec.="|".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)."\n"; + } else { + print ("\t".$b3->total_time."ms\t".(int((1000*$b3->total_requests/$b3->total_time)*1000)/1000)." checkouts/sec\n"); + } + unless ($short_print) { + print "Step 6b circulation (checkins) "; + } + my $run4 = HTTPD::Bench::ApacheBench::Run->new + ({ urls => \@returns, + cookies => [$cookie], + }); + $b4->add_run($run4); + $b->add_run($run4); + + # send HTTP request sequences to server and time responses + $ro = $b4->execute; + # calculate hits/sec + if ($short_print) { + $short_ms.= "|".$b4->total_time."\n"; + $short_psec.="|".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)."\n"; + } else { + print ("\t".$b4->total_time."ms\t".(int((1000*$b4->total_requests/$b4->total_time)*1000)/1000)." checkins/sec\n"); + } +} else { + print "Skipping step 6\n"; +} + +if ($steps=~ /0/) { + unless ($short_print) { + print "all transactions at once "; + } + $ro = $b->execute; + if ($short_print) { + $short_ms.= "|".$b->total_time."\n"; + $short_psec.="|".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)."\n"; + } else { + print ("\t".$b->total_time."ms\t".(int((1000*$b->total_requests/$b->total_time)*1000)/1000)." operations/sec\n"); + } +} else { + print "Skipping 'testing all transactions at once'\n (step 0)"; +} + +if ($short_print) { +print $short_ms."\n=====\n".$short_psec."\n"; +} -- 2.39.5