New submission for the next commit fest.

This new version also reports the average lag time, i.e. the delay between scheduled and actual transaction start times. This may help detect whether things went smothly, or if at some time some delay was introduced because of the load and some catchup was done afterwards.

Question 1: should it report the maximum lang encountered?

Question 2: the next step would be to have the current lag shown under option --progress, but that would mean having a combined --throttle --progress patch submission, or maybe dependencies between patches.

--
Fabien.
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index 8ff6623..9b5adc2 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;
@@ -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,9 @@ usage(void)
 		   "  -D VARNAME=VALUE\n"
 		   "               define variable for use by custom script\n"
 		   "  -f FILENAME  read transaction script from FILENAME\n"
+		   "  -H SPEC, --throttle SPEC\n"
+		   "               delay in second to throttle each client\n"
+		   "               sample specs: 0.025 40tps 25ms 25000us\n"
 		   "  -j NUM       number of threads (default: 1)\n"
 		   "  -l           write transaction times to log file\n"
 		   "  -M simple|extended|prepared\n"
@@ -902,13 +914,40 @@ 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 && ! 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 "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 && st->state==0)
+				/* measure lag of throttled transaction */
+				st->throttle_lag += (now_us - st->until);
+		}
 		else
 			return true;		/* Still sleeping, nothing to do here */
 	}
@@ -1091,6 +1130,7 @@ top:
 			st->state = 0;
 			st->use_file = (int) getrand(thread, 0, num_files - 1);
 			commands = sql_files[st->use_file];
+			st->throttled = false;
 		}
 	}
 
@@ -2012,7 +2052,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)
 {
@@ -2052,6 +2093,23 @@ printResults(int ttype, int normal_xacts, int nclients,
 		printf("number of transactions actually processed: %d\n",
 			   normal_xacts);
 	}
+
+	if (throttle)
+	{
+		/* 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);
 
@@ -2112,6 +2170,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}
 	};
 
@@ -2178,7 +2237,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)
 		{
@@ -2333,6 +2392,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;
@@ -2623,6 +2702,14 @@ main(int argc, char **argv)
 	/* get start up time */
 	INSTR_TIME_SET_CURRENT(start_time);
 
+	/* set initial client throttling trigger */
+	if (throttle)
+	{
+		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);
@@ -2677,7 +2764,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..a5c0911 100644
--- a/doc/src/sgml/pgbench.sgml
+++ b/doc/src/sgml/pgbench.sgml
@@ -310,6 +310,28 @@ 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 targeted 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</>.
+	Under throttling, the average transaction lag time (delay between
+	scheduled and actual transaction start times) is reported in ms.
+        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

Reply via email to