diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index 8c202bf..799dfcd 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -137,6 +137,12 @@ int unlogged_tables = 0;
double sample_rate = 0.0;
/*
+ * When clients are throttled to a given rate limit, this is the target delay
+ * to reach that rate in usec. 0 is the default and means no throttling.
+ */
+int64 throttle_delay = 0;
+
+/*
* tablespace selection
*/
char *tablespace = NULL;
@@ -205,6 +211,9 @@ 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 */
+ int64 throttle_lag; /* transaction lag behind throttling */
int use_file; /* index in sql_files for this client */
bool prepared[MAX_FILES];
} CState;
@@ -348,6 +357,8 @@ usage(void)
" -D VARNAME=VALUE\n"
" define variable for use by custom script\n"
" -f FILENAME read transaction script from FILENAME\n"
+ " -R SPEC, --rate SPEC\n"
+ " target rate per client in transactions per second\n"
" -j NUM number of threads (default: 1)\n"
" -l write transaction times to log file\n"
" -M simple|extended|prepared\n"
@@ -902,13 +913,41 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile, AggVa
top:
commands = sql_files[st->use_file];
+ /* handle throttling once per transaction by inserting a sleep.
+ * this is simpler than doing it at the end.
+ */
+ if (throttle_delay && ! 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_delay * -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 "INT64_FORMAT" us\n",
+ st->id, wait);
+ }
+
if (st->sleeping)
{ /* are we sleeping? */
instr_time now;
+ int64 now_us;
INSTR_TIME_SET_CURRENT(now);
- if (st->until <= INSTR_TIME_GET_MICROSEC(now))
+ now_us = INSTR_TIME_GET_MICROSEC(now);
+ if (st->until <= now_us)
+ {
st->sleeping = 0; /* Done sleeping, go ahead with next command */
+ if (throttle_delay && st->state==0)
+ /* measure lag of throttled transaction */
+ st->throttle_lag += (now_us - st->until);
+ }
else
return true; /* Still sleeping, nothing to do here */
}
@@ -1095,6 +1134,7 @@ top:
st->state = 0;
st->use_file = (int) getrand(thread, 0, num_files - 1);
commands = sql_files[st->use_file];
+ st->throttled = false;
}
}
@@ -2015,7 +2055,8 @@ process_builtin(char *tb)
/* print out results */
static void
-printResults(int ttype, int normal_xacts, int nclients,
+printResults(int ttype, int normal_xacts,
+ CState *clients, int nclients,
TState *threads, int nthreads,
instr_time total_time, instr_time conn_total_time)
{
@@ -2055,6 +2096,23 @@ printResults(int ttype, int normal_xacts, int nclients,
printf("number of transactions actually processed: %d\n",
normal_xacts);
}
+
+ if (throttle_delay)
+ {
+ /* Report average transaction lag under throttling, i.e. the delay
+ between scheduled and actual start times for the transaction.
+ The measured lag may be linked to the thread/client load,
+ the database load, or the Poisson throttling process.
+ should it report the maximum encountered lag?
+ */
+ int64 throttle_lag = 0;
+ int c;
+ for (c = 0; c < nclients; c++)
+ throttle_lag += clients[c].throttle_lag;
+ printf("average transaction lag: %.3f ms\n",
+ 0.001 * throttle_lag / normal_xacts);
+ }
+
printf("tps = %f (including connections establishing)\n", tps_include);
printf("tps = %f (excluding connections establishing)\n", tps_exclude);
@@ -2115,6 +2173,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},
+ {"rate", required_argument, NULL, 'R'},
{NULL, 0, NULL, 0}
};
@@ -2181,7 +2240,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:R:", long_options, &optindex)) != -1)
{
switch (c)
{
@@ -2336,6 +2395,19 @@ main(int argc, char **argv)
exit(1);
}
break;
+ case 'R':
+ {
+ /* get a double from the beginning of option value */
+ double throttle_value = atof(optarg);
+ if (throttle_value <= 0.0)
+ {
+ fprintf(stderr, "invalid rate limit: %s\n", optarg);
+ exit(1);
+ }
+ /* Invert rate limit into a time offset */
+ throttle_delay = (int64) (1000000.0 / throttle_value);
+ }
+ break;
case 0:
/* This covers long options which take no argument. */
break;
@@ -2626,6 +2698,14 @@ main(int argc, char **argv)
/* get start up time */
INSTR_TIME_SET_CURRENT(start_time);
+ /* set initial client throttling trigger */
+ if (throttle_delay)
+ {
+ state[0].trigger = INSTR_TIME_GET_MICROSEC(start_time);
+ for (i = 1; i < nclients; i++)
+ state[i].trigger = state[0].trigger;
+ }
+
/* set alarm if duration is specified. */
if (duration > 0)
setalarm(duration);
@@ -2680,7 +2760,7 @@ main(int argc, char **argv)
/* get end time */
INSTR_TIME_SET_CURRENT(total_time);
INSTR_TIME_SUBTRACT(total_time, start_time);
- printResults(ttype, total_xacts, nclients, threads, nthreads,
+ printResults(ttype, total_xacts, state, nclients, threads, nthreads,
total_time, conn_total_time);
return 0;
diff --git a/doc/src/sgml/pgbench.sgml b/doc/src/sgml/pgbench.sgml
index e9900d3..ee7e216 100644
--- a/doc/src/sgml/pgbench.sgml
+++ b/doc/src/sgml/pgbench.sgml
@@ -310,6 +310,25 @@ pgbench options> dbname>
+ rate>
+ rate>
+
+
+ Execute client transactions targeting the specified rate instead of
+ running as fast as possible (the default). The rate is given in
+ transactions per second and is per client. If the targeted rate is
+ above the maximum possible rate these transactions can execute at,
+ the rate limit won't have any impact on results.
+
+ Each client connection targets this rate by starting transactions
+ along a Poisson-distributed event time line. When a rate limit is
+ active, the average transaction lag time (the delay between
+ the scheduled and actual transaction start times) is reported in ms.
+
+
+
+
+ threads>