Add --throttle to pgbench
Each client is throttled to the specified rate, which can be expressed in
tps or in time (s, ms, us). Throttling is achieved by scheduling
transactions along a Poisson-distribution.
This is an update of the previous proposal which fix a typo in the sgml
documentation.
The use case of the option is to be able to generate a continuous gentle
load for functional tests, eg in a practice session with students or for
testing features on a laptop.
--
Fabien.
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index bc01f07..0142ed0 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -137,6 +137,12 @@ int unlogged_tables = 0;
double sample_rate = 0.0;
/*
+ * whether clients are throttled to a given rate, expressed as a delay in us.
+ * 0, the default means no throttling.
+ */
+int64 throttle = 0;
+
+/*
* tablespace selection
*/
char *tablespace = NULL;
@@ -204,6 +210,8 @@ typedef struct
int nvariables;
instr_time txn_begin; /* used for measuring transaction latencies */
instr_time stmt_begin; /* used for measuring statement latencies */
+ int64 trigger; /* previous/next throttling (us) */
+ bool throttled; /* whether current transaction was throttled */
int use_file; /* index in sql_files for this client */
bool prepared[MAX_FILES];
} CState;
@@ -361,6 +369,9 @@ usage(void)
" -S perform SELECT-only transactions\n"
" -t NUM number of transactions each client runs (default: 10)\n"
" -T NUM duration of benchmark test in seconds\n"
+ " -H SPEC, --throttle SPEC\n"
+ " delay in second to throttle each client\n"
+ " sample specs: 0.025 40tps 25ms 25000us\n"
" -v vacuum all four standard tables before tests\n"
"\nCommon options:\n"
" -d print debugging output\n"
@@ -1027,7 +1038,7 @@ top:
}
}
- if (commands[st->state]->type == SQL_COMMAND)
+ if (!st->throttled && commands[st->state]->type == SQL_COMMAND)
{
/*
* Read and discard the query result; note this is not included in
@@ -1049,26 +1060,54 @@ top:
discard_response(st);
}
+ /* some stuff done at the end */
if (commands[st->state + 1] == NULL)
{
- if (is_connect)
+ /* disconnect if required and needed */
+ if (is_connect && st->con)
{
PQfinish(st->con);
st->con = NULL;
}
- ++st->cnt;
- if ((st->cnt >= nxacts && duration <= 0) || timer_exceeded)
- return clientDone(st, true); /* exit success */
+ /* update transaction counter once, and possibly end */
+ if (!st->throttled)
+ {
+ ++st->cnt;
+ if ((st->cnt >= nxacts && duration <= 0) || timer_exceeded)
+ return clientDone(st, true); /* exit success */
+ }
+
+ /* handle throttling once, as the last post-transaction stuff */
+ if (throttle && !st->throttled)
+ {
+ /* compute delay to approximate a Poisson distribution
+ * 1000000 => 13.8 .. 0 multiplier
+ * if transactions are too slow or a given wait shorter than
+ * a transaction, the next transaction will start right away.
+ */
+ int64 wait = (int64)
+ throttle * -log(getrand(thread, 1, 1000000)/1000000.0);
+ st->trigger += wait;
+ st->sleeping = 1;
+ st->until = st->trigger;
+ st->throttled = true;
+ if (debug)
+ fprintf(stderr, "client %d throttling %d us\n",
+ st->id, (int) wait);
+ return true;
+ }
}
/* increment state counter */
st->state++;
if (commands[st->state] == NULL)
{
+ /* reset */
st->state = 0;
st->use_file = (int) getrand(thread, 0, num_files - 1);
commands = sql_files[st->use_file];
+ st->throttled = false;
}
}
@@ -2086,6 +2125,7 @@ main(int argc, char **argv)
{"unlogged-tables", no_argument, &unlogged_tables, 1},
{"sampling-rate", required_argument, NULL, 4},
{"aggregate-interval", required_argument, NULL, 5},
+ {"throttle", required_argument, NULL, 'H'},
{NULL, 0, NULL, 0}
};
@@ -2152,7 +2192,7 @@ main(int argc, char **argv)
state = (CState *) pg_malloc(sizeof(CState));
memset(state, 0, sizeof(CState));
- while ((c = getopt_long(argc, argv, "ih:nvp:dqSNc:j:Crs:t:T:U:lf:D:F:M:", long_options, &optindex)) != -1)
+ while ((c = getopt_long(argc, argv, "ih:nvp:dqSNc:j:Crs:t:T:U:lf:D:F:M:H:", long_options, &optindex)) != -1)
{
switch (c)
{
@@ -2307,6 +2347,26 @@ main(int argc, char **argv)
exit(1);
}
break;
+ case 'H':
+ {
+ /* get a double from the beginning of option value */
+ double throttle_value = atof(optarg);
+ if (throttle_value <= 0.0)
+ {
+ fprintf(stderr, "invalid throttle value: %s\n", optarg);
+ exit(1);
+ }
+ /* rough handling of possible units */
+ if (strstr(optarg, "us"))
+ throttle = (int64) throttle_value;
+ else if (strstr(optarg, "ms"))
+ throttle = (int64) (1000.0 * throttle_value);
+ else if (strstr(optarg, "tps"))
+ throttle = (int64) (1000000.0 / throttle_value);
+ else /* assume that default is in second */
+ throttle = (int64) (1000000.0 * throttle_value);
+ }
+ break;
case 0:
/* This covers long options which take no argument. */
break;
@@ -2533,6 +2593,18 @@ main(int argc, char **argv)
INSTR_TIME_SET_CURRENT(start_time);
srandom((unsigned int) INSTR_TIME_GET_MICROSEC(start_time));
+ /* initial throttling setup with regular increasing delays */
+ if (throttle)
+ {
+ int delay = throttle / nclients;
+ for (i=0; i<nclients; i++)
+ {
+ state[i].trigger = INSTR_TIME_GET_MICROSEC(start_time) + i*delay;
+ state[i].sleeping = 1;
+ state[i].until = state[i].trigger;
+ }
+ }
+
/* process builtin SQL scripts */
switch (ttype)
{
diff --git a/doc/src/sgml/pgbench.sgml b/doc/src/sgml/pgbench.sgml
index 79b4baf..b5c6c1c 100644
--- a/doc/src/sgml/pgbench.sgml
+++ b/doc/src/sgml/pgbench.sgml
@@ -310,6 +310,26 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
</varlistentry>
<varlistentry>
+ <term><option>-H</option> <replaceable>rate</></term>
+ <term><option>--throttle</option> <replaceable>rate</></term>
+ <listitem>
+ <para>
+ Do client transaction throttling at the specified rate instead of
+ maximizing the load.
+ Each client connection targets this rate by starting transactions
+ along a Poisson-distributed event time line.
+ Obviously, the targetted rate must be below the maximum possible rate
+ of the system.
+ Example equivalent <replaceable>rate</> specifications which aim at
+ 40 transactions-per-second, that is one transaction every 25 ms:
+ <literal>0.025</>, <literal>0.025s</>, <literal>25ms</>,
+ <literal>25000us</> and finally <literal>40tps</>.
+ Default is no throttling.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-j</option> <replaceable>threads</></term>
<listitem>
<para>
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers