Add pgench: a TPC-B like benchmarking tool

This commit is contained in:
Tatsuo Ishii 2000-01-15 12:38:09 +00:00
parent bfbd58ce13
commit a765db409b
5 changed files with 1079 additions and 0 deletions

View File

@ -83,3 +83,7 @@ userlock -
vacuumlo -
Remove orphaned large objects
by Peter T Mount <peter@retep.org.uk>
pgbench -
TPC-B like benchmarking tool
by Tatsuo Ishii <t-ishii@sra.co.jp>

23
contrib/pgbench/Makefile Normal file
View File

@ -0,0 +1,23 @@
# $Header: /cvsroot/pgsql/contrib/pgbench/Makefile,v 1.1 2000/01/15 12:38:09 ishii Exp $
SRCDIR= ../../src
include $(SRCDIR)/Makefile.global
CFLAGS:= -I$(LIBPQDIR) $(CFLAGS)
TARGET = pgbench
OBJS = pgbench.o
all:: $(TARGET)
$(TARGET): $(OBJS)
$(CC) -o $(TARGET) $(OBJS) -L$(LIBPQDIR) -lpq $(LDFLAGS)
install: $(TARGET)
$(INSTALL) $(INSTL_EXE_OPTS) $(TARGET)$(X) $(BINDIR)/$(TARGET)$(X)
clean:
$(RM) $(TARGET)$(X) $(OBJS)
distclean: clean

149
contrib/pgbench/README Normal file
View File

@ -0,0 +1,149 @@
pgbench 1.2 README 2000/1/15 Tatsuo Ishii (t-ishii@sra.co.jp)
o What is pgbench?
pgbench is a simple program to run a benchmark test sort of
"TPC-B". pgbench is a client application of PostgreSQL and runs
with PostgreSQL only. It performs lots of small and simple
transactions including select/update/insert operations then
calculates number of transactions successfully completed within a
second (transactions per second, tps). Targeting data includes a
table with at least 100k tuples.
Example outputs from pgbench look like:
number of clients: 4
number of transactions per client: 100
number of processed transactions: 400/400
tps = 19.875015(including connections establishing)
tps = 20.098827(excluding connections establishing)
Similar program called "JDBCBench" already exists, but it requires
Java that may not be available on every platform. Moreover some
people concerned about the overhead of Java that might lead
inaccurate results. So I decided to write in pure C, and named
it "pgbench."
o features of pgbench
- pgbench is written in C using libpq only. So it is very portable
and easy to install.
- pgbench can simulate concurrent connections using asynchronous
capability of libpq. No threading is required.
o How to install pgbench
(1) Edit the first line in Makefile
POSTGRESHOME = /usr/local/pgsql
so that it points to the directory where PostgreSQL installed.
(2) Run configure
(3) Run make. You will see an executable file "pgbench" there.
o How to use pgbench?
(1) Initialize database by:
pgbench -i <dbname>
where <dbname> is the name of database. pgbench uses four tables
accounts, branches, history and tellers. These tables will be
destroyed. Be very carefully if you have tables having same
names. Default test data contains:
table # of tuples
-------------------------
branches 1
tellers 10
accounts 100000
history 0
You can increase the number of tuples by using -s option. See
below.
(2) Run the benchmark test
pgbench <dbname>
The default configuration is:
number of clients: 1
number of transactions per client: 10
o options
pgbench has number of options.
-h hostname
hostname where the backend is running. If this option
is omitted, pgbench will connect to the localhost via
Unix domain socket.
-p port
the port number that the backend is accepting. default is
5432.
-c number_of_clients
Number of clients simulated. default is 1.
-t number_of_transactions
Number of transactions each client runs. default is 10.
-s scaling_factor
this should be used with -i (initialize) option.
number of tuples generated will be multiple of the
scaling factor. For example, -s 100 will imply 10M
(10,000,000) tuples in the accounts table.
default is 1.
-n
No vacuuming and cleaning the history table prior the
test is performed.
-v
Do vacuuming before testing. This will take some time.
Without both -n and -v pgbench will vacuum tellers and
branches tables only.
-S
Perform select only transactions instead of TPC-B.
-d
debug option.
o What is the "transaction" actually performed in pgbench?
(1) begin;
(2) update accounts set abalance = abalance + :delta where aid = :aid;
(3) select abalance from accounts where aid = :aid;
(4) update tellers set tbalance = tbalance + :delta where tid = :tid;
(5) update branches set bbalance = bbalance + :delta where bid = :bid;
(6) insert into history(tid,bid,aid,delta) values(:tid,:bid,:aid,:delta);
(7) end;
o License?
Basically it is same as BSD license. See pgbench.c for more details.
o History
2000/1/15 pgbench-1.2 contributed to PostgreSQL
* Add -v option
1999/09/29 pgbench-1.1 released
* Apply cygwin patches contributed by Yutaka Tanida
* More robust when backends die
* Add -S option (select only)
1999/09/04 pgbench-1.0 released

166
contrib/pgbench/README.jis Normal file
View File

@ -0,0 +1,166 @@
pgbench 1.2 README 2000/1/15 Tatsuo Ishii (t-ishii@sra.co.jp)
$B"#(Bpgbench $B$H$O!)(B
pgbench $B$O(B TPC-B$B$K;w$?%Y%s%A%^!<%/%F%9%H$r9T$J$&%W%m%0%i%`$G$9!#:#$N$H(B
$B$3$m(B PostgreSQL $B@lMQ$G$9!#(B
pgbench $B$O(B select/update/insert $B$r4^$`%H%i%s%6%/%7%g%s$r<B9T$7!"A4BN$N(B
$B<B9T;~4V$H<B:]$K40N;$7$?%H%i%s%6%/%7%g%s$N?t$+$i(B 1 $BIC4V$K<B9T$G$-$?%H(B
$B%i%s%6%/%7%g%s?t(B (tps) $B$rI=<($7$^$9!#=hM}$NBP>]$H$J$k%F!<%V%k$O%G%U%)(B
$B%k%H$G$O(B 10$BK|%?%W%k$N%G!<%?$r4^$_$^$9!#(B
$B<B:]$NI=<($O0J2<$N$h$&$J46$8$G$9!#(B
number of clients: 4
number of transactions per client: 100
number of processed transactions: 400/400
tps = 19.875015(including connections establishing)
tps = 20.098827(excluding connections establishing)
pgbench $B$O(B JDBCBench $B$H$$$&!"$b$H$b$H$O(B MySQL $BMQ$K=q$+$l$?(B JDBC $BMQ$N%Y(B
$B%s%A%^!<%/%W%m%0%i%`$r;29M$K:n@.$5$l$^$7$?!#(B
$B"#(Bpgbench $B$NFCD'(B
o C $B8@8l$H(B libpq $B$@$1$G=q$+$l$F$$$k$N$G0\?"@-$,9b$/!"4JC1$K%$%s%9%H!<(B
$B%k$G$-$^$9!#(B
o pgbench $B$O(B libpq $B$NHsF14|=hM}5!G=$r;H$C$F%^%k%A%f!<%64D6-$r%7%_%e%l!<(B
$B%H$7$^$9!#MF0W$KF1;~@\B34D6-$r%F%9%H$G$-$^$9!#(B
$B"#(Bpgbench $B$N%$%s%9%H!<%k(B
Makefile$B$N0lHV>e$K$"$k(B
POSTGRESHOME = /usr/local/pgsql
$B$rI,MW$K1~$8$F=$@5$7!"(Bconfigure;make $B$9$k$@$1$G$9!#(B
$B"#(Bpgbench $B$N;H$$J}(B
$B4pK\E*$J;H$$J}$O!"(B
$ pgbench [$B%G!<%?%Y!<%9L>(B]
$B$G$9!#%G!<%?%Y!<%9L>$r>JN,$9$k$H!"%f!<%6L>$HF1$8%G!<%?%Y!<%9$r;XDj$7$?(B
$B$b$N$H$_$J$7$^$9!#%G!<%?%Y!<%9$O8e=R$N(B -i $B%*%W%7%g%s$r;H$C$F$"$i$+$8$a(B
$B=i4|2=$7$F$*$/I,MW$,$"$j$^$9!#(B
pgbench $B$K$O$$$m$$$m$J%*%W%7%g%s$,$"$j$^$9!#(B
-h $B%[%9%HL>(B PostgreSQL$B$N%G!<%?%Y!<%9%G!<%b%s(B postmaster $B$NF0(B
$B$$$F$$$k%[%9%HL>$r;XDj$7$^$9!#>JN,$9$k$H<+%[%9%H$K(B Unix domain
socket $B$G@\B3$7$^$9!#(B
-p $B%]!<%HHV9f(B postmaster $B$N;HMQ$9$k%]!<%HHV9f$r;XDj$7$^$9!#>JN,$9$k$H(B 5432
$B$,;XDj$5$l$?$b$N$H$_$J$7$^$9!#(B
-c $B%/%i%$%"%s%H?t(B $BF1;~<B9T%/%i%$%"%s%H?t$r;XDj$7$^$9!#>JN,;~$O(B
1 $B$H$J$j$^$9!#(Bpgbench $B$OF1;~<B9T%/%i%$%"%s%HKh$K(B
$B%U%!%$%k%G%#%9%/%j%W%?$r;HMQ$9$k$N$G!";HMQ2DG=(B
$B%U%!%$%k%G%#%9%/%j%W%??t$r1[$($k%/%i%$%"%s%H?t$O(B
$B;XDj$G$-$^$;$s!#;HMQ2DG=%U%!%$%k%G%#%9%/%j%W%??t(B
$B$O(B limit $B$d(B ulimit $B%3%^%s%I$GCN$k$3$H$,$G$-$^$9!#(B
-t $B%H%i%s%6%/%7%g%s?t(B $B3F%/%i%$%"%s%H$,<B9T$9$k%H%i%s%6%/%7%g%s?t$r(B
$B;XDj$7$^$9!#>JN,;~$O(B 10 $B$H$J$j$^$9!#(B
-s $B%9%1!<%j%s%0%U%!%/%?!<(B
-i $B%*%W%7%g%s$H0l=o$K;HMQ$7$^$9!#(B
$B%9%1!<%j%s%0%U%!%/%?!<$O(B1$B0J>e$N@0?t!#%9%1!<%j%s%0%U%!(B
$B%/%?!<$rJQ$($k$3$H$K$h$j!"%F%9%H$NBP>]$H$J$k%F!<%V%k$N(B
$BBg$-$5$,(B 10$BK|(B x [$B%9%1!<%j%s%0%U%!%/%?!<(B]$B$K$J$j$^$9!#(B
$B%G%U%)%k%H$N%9%1!<%j%s%0%U%!%/%?!<$O(B 1 $B$G$9!#(B
-v $B$3$N%*%W%7%g%s$r;XDj$9$k$H!"%Y%s%A%^!<%/3+;OA0$K(B vacuum $B$H(B
history $B$N%/%j%"$r9T$J$$$^$9!#(B-v $B$H(B -n $B$r>JN,$9$k$H!"(B
$B:G>.8B$N(B vacuum $B$J$I$r9T$$$^$9!#$9$J$o$A!"(Bhistory $B$N:o=|!"(B
$B$H(B history, branches, history $B$N(B vacuum $B$r9T$$$^$9!#(B
$B$3$l$O!"(Bvacuum $B$N;~4V$r:G>.8B$K$7$J$,$i!"%Q%U%)!<%^%s%9$K(B
$B1F6A$9$k%4%_A]=|$r8z2LE*$K9T$$$^$9!#DL>o$O(B -v $B$H(B -n $B$r(B
$B>JN,$9$k$3$H$r$*$9$9$a$7$^$9!#(B
-n $B$3$N%*%W%7%g%s$r;XDj$9$k$H!"%Y%s%A%^!<%/3+;OA0$K(B vacuum $B$H(B
history $B$N%/%j%"$r9T$J$$$^$;$s!#(B
-S TPC-B$B$N%H%i%s%6%/%7%g%s$G$O$J$/!"8!:w$N$_$N%H%i%s%6%/%7%g%s$r(B
$B<B9T$7$^$9!#8!:w%9%T!<%I$rB,Dj$7$?$$$H$-$K;H$$$^$9!#(B
-d $B%G%P%C%0%*%W%7%g%s!#MM!9$J>pJs$,I=<($5$l$^$9!#(B
$B"#%G!<%?%Y!<%9$N=i4|2=(B
pgbench $B$G%Y%s%A%^!<%/%F%9%H$r<B;\$9$k$?$a$K$O!"$"$i$+$8$a%G!<%?%Y!<%9(B
$B$r=i4|2=$7!"%F%9%H%G!<%?$r:n$kI,MW$,$"$j$^$9!#(B
$ pgbench -i [$B%G!<%?%Y!<%9L>(B]
$B$3$l$K$h$j0J2<$N%F!<%V%k$,:n$i$l$^$9(B($B%9%1!<%j%s%0%U%!%/%?!<(B == 1 $B$N>l9g(B)$B!#(B
$B!vCm0U!v(B
$BF1$8L>A0$N%F!<%V%k$,$"$k$H:o=|$5$l$F$7$^$&$N$G$4Cm0U2<$5$$!*!*(B
$B%F!<%V%kL>(B $B%?%W%k?t(B
-------------------------
branches 1
tellers 10
accounts 100000
history 0
$B%9%1!<%j%s%0%U%!%/%?!<$r(B 10,100,1000 $B$J$I$KJQ99$9$k$H!">e5-%?%W%k?t$O(B
$B$=$l$K1~$8$F(B10$BG\!"(B100$BG\!"(B1000$BG\$K$J$j$^$9!#$?$H$($P!"%9%1!<%j%s%0%U%!(B
$B%/%?!<$r(B 10 $B$H$9$k$H!"(B
$B%F!<%V%kL>(B $B%?%W%k?t(B
-------------------------
branches 10
tellers 100
accounts 1000000
history 0
$B$K$J$j$^$9!#(B
$B"#!V%H%i%s%6%/%7%g%s!W$NDj5A(B
pgbench $B$G$O!"0J2<$N%7!<%1%s%9$rA4It40N;$7$F(B1$B%H%i%s%6%/%7%g%s$H?t$($F(B
$B$$$^$9!#(B
(1) begin;
(2) update accounts set abalance = abalance + :delta where aid = :aid;
$B$3$3$G!"(B:delta$B$O(B1$B$+$i(B1000$B$^$G$NCM$r<h$kMp?t!"(B:aid $B$O(B 1$B$+$i(B100000$B$^$G(B
$B$NCM$r<h$kMp?t$G$9!#0J2<!"Mp?t$NCM$O$=$l$>$l$3$N%H%i%s%6%/%7%g%s$N(B
$BCf$G$OF1$8CM$r;H$$$^$9!#(B
(3) select abalance from accounts where aid = :aid;
$B$3$3$G$O(B1$B7o$@$18!:w$5$l$^$9!#(B
(4) update tellers set tbalance = tbalance + :delta where tid = :tid;
$B$3$3$G(B :tid $B$O(B 1$B$+$i(B10$B$N4V$NCM$r$H$kMp?t$G$9!#(B
(5) update branches set bbalance = bbalance + :delta where bid = :bid;
$B$3$3$G(B :bid $B$O(B 1 $B$+$i(B[$B%9%1%j%s%0%U%!%/%?!<(B]$B$N4V$NCM$r<h$kMp?t$G$9!#(B
(6) insert into history(tid,bid,aid,delta) values(:tid,:bid,:aid,:delta);
(7) end;
$B"#:n<T$H%i%$%;%s%9>r7o(B
pgbench $B$O@P0f(B $BC#IW$K$h$C$F=q$+$l$^$7$?!#%i%$%;%s%9>r7o$O(B pgbench.c $B$N(B
$BKAF,$K=q$$$F$"$j$^$9!#$3$N>r7o$r<i$k8B$jL5=~$GMxMQ$7!"$^$?<+M3$K:FG[IU(B
$B$G$-$^$9!#(B
$B"#2~DjMzNr(B
2000/1/15 pgbench-1.2 $B$O(B PostgreSQL $B$K(B contribute $B$5$l$^$7$?!#(B
* -v $B%*%W%7%g%sDI2C(B
1999/09/29 pgbench-1.1 $B%j%j!<%9(B
* $BC+ED$5$s$K$h$k(Bcygwin$BBP1~%Q%C%A<h$j9~$_(B
* $B%P%C%/%(%s%I%/%i%C%7%e;~$NBP1~(B
* -S $B%*%W%7%g%sDI2C(B
1999/09/04 pgbench-1.0 $B%j%j!<%9(B

737
contrib/pgbench/pgbench.c Normal file
View File

@ -0,0 +1,737 @@
/*
* $Header: /cvsroot/pgsql/contrib/pgbench/pgbench.c,v 1.1 2000/01/15 12:38:09 ishii Exp $
*
* pgbench: a simple TPC-B like benchmark program for PostgreSQL
* written by Tatsuo Ishii
*
* Copyright (c) 2000 Tatsuo Ishii
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of the
* author not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. The author makes no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*/
#include "config.h"
#include <stdio.h>
#include "postgres.h"
#include "libpq-fe.h"
#include <errno.h>
#ifdef WIN32
#include "win32.h"
#else
#include <sys/time.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
/* for getrlimit */
#include <sys/resource.h>
#endif /* WIN32 */
/********************************************************************
* some configurable parameters */
#define MAXCLIENTS 1024 /* max number of clients allowed */
int nclients = 1; /* default number of simulated clients */
int nxacts = 10; /* default number of transactions per clients */
/*
* scaling factor. for example, tps = 10 will make 1000000 tuples of
* accounts table.
*/
int tps = 1;
/*
* end of configurable parameters
*********************************************************************/
#define nbranches 1
#define ntellers 10
#define naccounts 100000
int remains; /* number of remained clients */
typedef struct {
PGconn *con; /* connection handle to DB */
int state; /* state No. */
int cnt; /* xacts count */
int ecnt; /* error count */
int listen; /* none 0 indicates that an async query has been sent */
int aid; /* account id for this transaction */
int bid; /* branch id for this transaction */
int tid; /* teller id for this transaction */
int delta;
int abalance;
} CState;
static void usage() {
fprintf(stderr,"usage: pgbench [-h hostname][-p port][-c nclients][-t ntransactions][-s scaling_factor][-n][-v][-S][-d][dbname]\n");
fprintf(stderr,"(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor][-d][dbname]\n");
}
/* random number generator */
static int getrand(int min, int max) {
return(min+(int)(max*1.0*rand()/(RAND_MAX+1.0)));
}
/* throw away response from backend */
static void discard_response(CState *state) {
PGresult *res;
do {
res = PQgetResult(state->con);
if (res)
PQclear(res);
} while(res);
}
static int check(CState *state, PGresult *res, int n, int good)
{
CState *st = &state[n];
if (res && PQresultStatus(res) != good) {
fprintf(stderr,"Client %d aborted in state %d: %s",n,st->state,PQerrorMessage(st->con));
remains--; /* I've aborted */
PQfinish(st->con);
st->con = NULL;
return(-1);
}
return(0);
}
/* process a transaction */
static void doOne(CState *state, int n, int debug) {
char sql[256];
PGresult *res;
CState *st = &state[n];
if (st->listen) { /* are we receiver? */
if (debug) {
fprintf(stderr,"client %d receiving\n",n);
}
while (PQisBusy(st->con) == TRUE) {
if (!PQconsumeInput(st->con)) { /* there's something wrong */
fprintf(stderr, "Client %d aborted in state %d. Probably the backend died while processing.\n",n, st->state);
remains--; /* I've aborted */
PQfinish(st->con);
st->con = NULL;
return;
}
}
switch (st->state) {
case 0: /* response to "begin" */
res = PQgetResult(st->con);
if (check(state, res, n, PGRES_COMMAND_OK)) {
return;
}
PQclear(res);
discard_response(st);
break;
case 1: /* response to "update accounts..." */
res = PQgetResult(st->con);
if (check(state, res, n, PGRES_COMMAND_OK)) {
return;
}
PQclear(res);
discard_response(st);
break;
case 2: /* response to "select abalance ..." */
res = PQgetResult(st->con);
if (check(state, res, n, PGRES_TUPLES_OK)) {
return;
}
PQclear(res);
discard_response(st);
break;
case 3: /* response to "update tellers ..." */
res = PQgetResult(st->con);
if (check(state, res, n, PGRES_COMMAND_OK)) {
return;
}
PQclear(res);
discard_response(st);
break;
case 4: /* response to "update branches ..." */
res = PQgetResult(st->con);
if (check(state, res, n, PGRES_COMMAND_OK)) {
return;
}
PQclear(res);
discard_response(st);
break;
case 5: /* response to "insert into history ..." */
res = PQgetResult(st->con);
if (check(state, res, n, PGRES_COMMAND_OK)) {
return;
}
PQclear(res);
discard_response(st);
break;
case 6: /* response to "end" */
res = PQgetResult(st->con);
if (check(state, res, n, PGRES_COMMAND_OK)) {
return;
}
PQclear(res);
discard_response(st);
if (++st->cnt >= nxacts) {
remains--; /* I've done */
PQfinish(st->con);
st->con = NULL;
return;
}
break;
}
/* increment state counter */
st->state++;
if (st->state > 6) {
st->state = 0;
}
}
switch (st->state) {
case 0: /* about to start */
strcpy(sql,"begin");
st->aid = getrand(1,naccounts*tps);
st->bid = getrand(1,nbranches*tps);
st->tid = getrand(1,ntellers*tps);
st->delta = getrand(1,1000);
break;
case 1:
sprintf(sql,"update accounts set abalance = abalance + %d where aid = %d\n",st->delta,st->aid);
break;
case 2:
sprintf(sql,"select abalance from accounts where aid = %d",st->aid);
break;
case 3:
sprintf(sql,"update tellers set tbalance = tbalance + %d where tid = %d\n",
st->delta,st->tid);
break;
case 4:
sprintf(sql,"update branches set bbalance = bbalance + %d where bid = %d",st->delta,st->bid);
break;
case 5:
sprintf(sql,"insert into history(tid,bid,aid,delta,time) values(%d,%d,%d,%d,'now')",
st->tid,st->bid,st->aid,st->delta);
break;
case 6:
strcpy(sql,"end");
break;
}
if (debug) {
fprintf(stderr,"client %d sending %s\n",n,sql);
}
if (PQsendQuery(st->con, sql) == 0) {
if (debug) {
fprintf(stderr, "PQsendQuery(%s)failed\n",sql);
}
st->ecnt++;
} else {
st->listen++; /* flags that should be listned */
}
}
/* process a select only transaction */
static void doSelectOnly(CState *state, int n, int debug) {
char sql[256];
PGresult *res;
CState *st = &state[n];
if (st->listen) { /* are we receiver? */
if (debug) {
fprintf(stderr,"client %d receiving\n",n);
}
while (PQisBusy(st->con) == TRUE) {
if (!PQconsumeInput(st->con)) { /* there's something wrong */
fprintf(stderr, "Client %d aborted in state %d. Probably the backend died while processing.\n",n, st->state);
remains--; /* I've aborted */
PQfinish(st->con);
st->con = NULL;
return;
}
}
switch (st->state) {
case 0: /* response to "select abalance ..." */
res = PQgetResult(st->con);
if (check(state, res, n, PGRES_TUPLES_OK)) {
return;
}
PQclear(res);
discard_response(st);
if (++st->cnt >= nxacts) {
remains--; /* I've done */
PQfinish(st->con);
st->con = NULL;
return;
}
break;
}
/* increment state counter */
st->state++;
if (st->state > 0) {
st->state = 0;
}
}
switch (st->state) {
case 0:
st->aid = getrand(1,naccounts*tps);
sprintf(sql,"select abalance from accounts where aid = %d",st->aid);
break;
}
if (debug) {
fprintf(stderr,"client %d sending %s\n",n,sql);
}
if (PQsendQuery(st->con, sql) == 0) {
if (debug) {
fprintf(stderr, "PQsendQuery(%s)failed\n",sql);
}
st->ecnt++;
} else {
st->listen++; /* flags that should be listned */
}
}
/* discard connections */
static void disconnect_all(CState *state) {
int i;
for (i=0;i<nclients;i++) {
if (state[i].con) {
PQfinish(state[i].con);
}
}
}
/* create tables and setup data */
static void init(char *pghost, char *pgport,char *dbName) {
PGconn *con;
PGresult *res;
static char *DDLs[] = {
"drop table branches",
"create table branches(bid int, primary key(bid),bbalance int,filler char(88))",
"drop table tellers",
"create table tellers(tid int, primary key(tid),bid int,tbalance int,filler char(84))",
"drop table accounts",
"create table accounts(aid int,primary key(aid),bid int,abalance int,filler char(84))",
"drop table history",
"create table history(tid int,bid int,aid int,delta int,time timestamp,filler char(22))"};
char sql[256];
int i;
con = PQsetdb(pghost, pgport, NULL, NULL, dbName);
if (PQstatus(con) == CONNECTION_BAD) {
fprintf(stderr, "Connection to database '%s' on %s failed.\n", dbName,pghost);
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
for (i=0;i<(sizeof(DDLs)/sizeof(char *));i++) {
res = PQexec(con,DDLs[i]);
if (strncmp(DDLs[i],"drop",4) && PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
}
res = PQexec(con,"begin");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
for(i = 0; i < nbranches * tps; i++) {
sprintf(sql,"insert into branches(bid,bbalance) values(%d,0)",i+1);
res = PQexec(con,sql);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
}
for(i = 0; i < ntellers * tps; i++) {
sprintf(sql,"insert into tellers(tid,bid,tbalance) values (%d,%d,0)"
,i+1,i/ntellers+1);
res = PQexec(con,sql);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
}
res = PQexec(con,"copy accounts from stdin");
if (PQresultStatus(res) != PGRES_COPY_IN) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
fprintf(stderr,"creating tables...\n");
for(i = 0; i < naccounts*tps; i++) {
int j = i + 1;
sprintf(sql,"%d\t%d\t%d\t\n",i+1,(i+1)/naccounts,0);
if (PQputline(con,sql)) {
fprintf(stderr,"PQputline failed\n");
exit(1);
}
if (j % 10000 == 0) {
fprintf(stderr,"%d tuples done.\n",j);
}
}
if (PQputline(con,"\\.\n")) {
fprintf(stderr,"very last PQputline failed\n");
exit(1);
}
if (PQendcopy(con)) {
fprintf(stderr,"PQendcopy failed\n");
exit(1);
}
res = PQexec(con,"end");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
/* vacuum */
fprintf(stderr,"vacuum...");
res = PQexec(con,"vacuum analyze");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
fprintf(stderr,"done.\n");
PQfinish(con);
}
/* print out results */
static void printResults(
int ttype, CState *state,
struct timeval *tv1,struct timeval *tv2,
struct timeval *tv3) {
double t1,t2;
int i;
int normal_xacts = 0;
for (i=0;i<nclients;i++) {
normal_xacts += state[i].cnt;
}
t1 = (tv3->tv_sec - tv1->tv_sec)*1000000.0+(tv3->tv_usec - tv1->tv_usec);
t1 = normal_xacts*1000000.0/t1;
t2 = (tv3->tv_sec - tv2->tv_sec)*1000000.0+(tv3->tv_usec - tv2->tv_usec);
t2 = normal_xacts*1000000.0/t2;
printf("transaction type: %s\n",ttype==0?"TPC-B (sort of)":"SELECT only");
printf("scaling factor: %d\n",tps);
printf("number of clients: %d\n",nclients);
printf("number of transactions per client: %d\n",nxacts);
printf("number of transactions actually processed: %d/%d\n",normal_xacts,nxacts*nclients);
printf("tps = %f(including connections establishing)\n",t1);
printf("tps = %f(excluding connections establishing)\n",t2);
}
int main(int argc, char **argv) {
extern char *optarg;
extern int optind, opterr, optopt;
int c;
char *pghost = "";
char *pgport = "";
char *dbName;
int is_init_mode = 0; /* initialize mode? */
int is_no_vacuum = 0; /* no vacuum at all before testing? */
int is_full_vacuum = 0; /* do full vacuum before testing? */
int debug = 0; /* debug flag */
int ttype = 0; /* transaction type. 0: TPC-B, 1: SELECT only */
static CState state[MAXCLIENTS]; /* clients status */
struct timeval tv1; /* start up time */
struct timeval tv2; /* after establishing all connections to the backend */
struct timeval tv3; /* end time */
int i;
fd_set input_mask;
int nsocks; /* return from select(2) */
int maxsock; /* max socket number to be waited */
#ifndef __CYGWIN32__
struct rlimit rlim;
#endif
PGconn *con;
PGresult *res;
while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:S")) != EOF) {
switch (c) {
case 'i':
is_init_mode++;
break;
case 'h':
pghost = optarg;
break;
case 'n':
is_no_vacuum++;
break;
case 'v':
is_full_vacuum++;
break;
case 'p':
pgport = optarg;
break;
case 'd':
debug++;
break;
case 'S':
ttype = 1;
break;
case 'c':
nclients = atoi(optarg);
if (nclients <= 0 || nclients > MAXCLIENTS) {
fprintf(stderr,"wrong number of clients: %d\n",nclients);
exit(1);
}
#ifndef __CYGWIN32__
#ifdef HAVE_RLIMIT_NOFILE /* most platform uses RLIMIT_NOFILE */
if (getrlimit(RLIMIT_NOFILE,&rlim) == -1) {
#else /* but BSD doesn't ... */
if (getrlimit(RLIMIT_OFILE,&rlim) == -1) {
#endif /* HAVE_RLIMIT_NOFILE */
fprintf(stderr,"getrlimit failed. reason: %s\n",strerror(errno));
exit(1);
}
if (rlim.rlim_cur <= (nclients+2)) {
fprintf(stderr,"You need at least %d open files resource but you are only allowed to use %ld.\n",nclients+2,rlim.rlim_cur);
fprintf(stderr,"Use limit/ulimt to increase the limit before using pgbench.\n");
exit(1);
}
#endif /* #ifndef __CYGWIN32__ */
break;
case 's':
tps = atoi(optarg);
if (tps <= 0) {
fprintf(stderr,"wrong scaling factor: %d\n",tps);
exit(1);
}
break;
case 't':
nxacts = atoi(optarg);
if (nxacts <= 0) {
fprintf(stderr,"wrong number of transactions: %d\n",nxacts);
exit(1);
}
break;
default:
usage();
exit(1);
break;
}
}
if (argc > optind) {
dbName = argv[optind];
} else {
dbName = getenv("USER");
if (dbName == NULL) {
dbName = "";
}
}
if (is_init_mode) {
init(pghost, pgport, dbName);
exit(0);
}
remains = nclients;
if (debug) {
printf("pghost: %s pgport: %s nclients: %d nxacts: %d dbName: %s\n",
pghost, pgport, nclients, nxacts, dbName);
}
/* opening connection... */
con = PQsetdb(pghost, pgport, NULL, NULL, dbName);
if (PQstatus(con) == CONNECTION_BAD) {
fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
/* get the scaling factor that should be same as count(*) from branches... */
res = PQexec(con,"select count(*) from branches");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
tps = atoi(PQgetvalue(res, 0, 0));
if (tps < 0) {
fprintf(stderr,"count(*) from branches invalid (%d)\n",tps);
exit(1);
}
PQclear(res);
if (!is_no_vacuum) {
fprintf(stderr,"starting vacuum...");
res = PQexec(con,"vacuum branches");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
res = PQexec(con,"vacuum tellers");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
res = PQexec(con,"delete from history");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
res = PQexec(con,"vacuum history");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
fprintf(stderr,"end.\n");
if (is_full_vacuum) {
fprintf(stderr,"starting full vacuum...");
res = PQexec(con,"vacuum analyze accounts");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
fprintf(stderr,"end.\n");
}
}
PQfinish(con);
/* set random seed */
gettimeofday(&tv1, 0);
srand((uint)tv1.tv_usec);
/* get start up time */
gettimeofday(&tv1, 0);
/* make connections to the database */
for (i=0;i<nclients;i++) {
state[i].con = PQsetdb(pghost, pgport, NULL, NULL, dbName);
if (PQstatus(state[i].con) == CONNECTION_BAD) {
fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
fprintf(stderr, "%s", PQerrorMessage(state[i].con));
exit(1);
}
}
/* time after connections set up */
gettimeofday(&tv2, 0);
/* send start up quries in async manner */
for (i=0;i<nclients;i++) {
if (ttype == 0) {
doOne(state, i, debug);
} else if (ttype == 1) {
doSelectOnly(state, i, debug);
}
}
for (;;) {
if (remains <= 0) { /* all done ? */
disconnect_all(state);
/* get end time */
gettimeofday(&tv3, 0);
printResults(ttype, state, &tv1,&tv2,&tv3);
exit(0);
}
FD_ZERO(&input_mask);
maxsock = 0;
for (i=0;i<nclients;i++) {
if (state[i].con) {
int sock = PQsocket(state[i].con);
if (sock < 0) {
fprintf(stderr,"Client %d: PQsock failed\n",i);
disconnect_all(state);
exit(1);
}
FD_SET(sock, &input_mask);
if (maxsock < sock) {
maxsock = sock;
}
}
}
if ((nsocks = select(maxsock +1, &input_mask, (fd_set *)NULL,
(fd_set *)NULL, (struct timeval *)NULL)) < 0) {
if (errno == EINTR) {
continue;
}
/* must be something wrong */
disconnect_all(state);
fprintf(stderr,"select failed: %s\n",strerror(errno));
exit(1);
} else if (nsocks == 0) { /* timeout */
fprintf(stderr,"select timeout\n");
for (i=0;i<nclients;i++) {
fprintf(stderr,"client %d:state %d cnt %d ecnt %d listen %d\n",
i,state[i].state,state[i].cnt,state[i].ecnt,state[i].listen);
}
exit(0);
}
/* ok, backend returns reply */
for (i=0;i<nclients;i++) {
if (state[i].con && FD_ISSET(PQsocket(state[i].con), &input_mask)) {
if (ttype == 0) {
doOne(state, i, debug);
} else if (ttype == 1) {
doSelectOnly(state, i, debug);
}
}
}
}
}