]> granicus.if.org Git - postgresql/commitdiff
Change the way latency is calculated with pgbench --rate option.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 11 Sep 2014 09:40:01 +0000 (12:40 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 11 Sep 2014 09:57:21 +0000 (12:57 +0300)
The reported latency values now include the "schedule lag" time, that is,
the time between the transaction's scheduled start time and the time it
actually started. This relates better to a model where requests arrive at a
certain rate, and we are interested in the response time to the end user or
application, rather than the response time of the database itself.

Also, when --rate is used, include the schedule lag time in the log output.

The --rate option is new in 9.4, so backpatch to 9.4. It seems better to
make this change in 9.4, while we're still in the beta period, than ship a
9.4 version that calculates the values differently than 9.5.

contrib/pgbench/pgbench.c
doc/src/sgml/pgbench.sgml

index 4aa8a5031a09bda403447eb1ac76c1906cb64894..e4b75b266e6a8120cbe556210f21cfc2712429e4 100644 (file)
@@ -204,10 +204,10 @@ typedef struct
                                                                 * sent */
        int                     sleeping;               /* 1 indicates that the client is napping */
        bool            throttling;             /* whether nap is for throttling */
-       int64           until;                  /* napping until (usec) */
        Variable   *variables;          /* array of variable definitions */
        int                     nvariables;
-       instr_time      txn_begin;              /* used for measuring transaction latencies */
+       int64           txn_scheduled;  /* scheduled start time of transaction (usec) */
+       instr_time      txn_begin;              /* used for measuring schedule lag times */
        instr_time      stmt_begin;             /* used for measuring statement latencies */
        int64           txn_latencies;  /* cumulated latencies */
        int64           txn_sqlats;             /* cumulated square latencies */
@@ -278,12 +278,17 @@ typedef struct
 
        long            start_time;             /* when does the interval start */
        int                     cnt;                    /* number of transactions */
-       double          min_duration;   /* min/max durations */
-       double          max_duration;
-       double          sum;                    /* sum(duration), sum(duration^2) - for
+
+       double          min_latency;    /* min/max latencies */
+       double          max_latency;
+       double          sum_latency;    /* sum(latency), sum(latency^2) - for
                                                                 * estimates */
-       double          sum2;
+       double          sum2_latency;
 
+       double          min_lag;
+       double          max_lag;
+       double          sum_lag;                /* sum(lag) */
+       double          sum2_lag;               /* sum(lag*lag) */
 } AggVals;
 
 static Command **sql_files[MAX_FILES]; /* SQL script files */
@@ -892,12 +897,18 @@ agg_vals_init(AggVals *aggs, instr_time start)
 {
        /* basic counters */
        aggs->cnt = 0;                          /* number of transactions */
-       aggs->sum = 0;                          /* SUM(duration) */
-       aggs->sum2 = 0;                         /* SUM(duration*duration) */
+       aggs->sum_latency = 0;          /* SUM(latency) */
+       aggs->sum2_latency = 0;                         /* SUM(latency*latency) */
 
        /* min and max transaction duration */
-       aggs->min_duration = 0;
-       aggs->max_duration = 0;
+       aggs->min_latency = 0;
+       aggs->max_latency = 0;
+
+       /* schedule lag counters */
+       aggs->sum_lag = 0;
+       aggs->sum2_lag = 0;
+       aggs->min_lag = 0;
+       aggs->max_lag = 0;
 
        /* start of the current interval */
        aggs->start_time = INSTR_TIME_GET_DOUBLE(start);
@@ -940,7 +951,7 @@ top:
 
                thread->throttle_trigger += wait;
 
-               st->until = thread->throttle_trigger;
+               st->txn_scheduled = thread->throttle_trigger;
                st->sleeping = 1;
                st->throttling = true;
                st->is_throttled = true;
@@ -956,13 +967,13 @@ top:
 
                INSTR_TIME_SET_CURRENT(now);
                now_us = INSTR_TIME_GET_MICROSEC(now);
-               if (st->until <= now_us)
+               if (st->txn_scheduled <= now_us)
                {
                        st->sleeping = 0;       /* Done sleeping, go ahead with next command */
                        if (st->throttling)
                        {
                                /* Measure lag of throttled transaction relative to target */
-                               int64           lag = now_us - st->until;
+                               int64           lag = now_us - st->txn_scheduled;
 
                                thread->throttle_lag += lag;
                                if (lag > thread->throttle_lag_max)
@@ -976,6 +987,11 @@ top:
 
        if (st->listen)
        {                                                       /* are we receiver? */
+               instr_time      now;
+               bool            now_valid = false;
+
+               INSTR_TIME_SET_ZERO(now); /* initialize to keep compiler quiet */
+
                if (commands[st->state]->type == SQL_COMMAND)
                {
                        if (debug)
@@ -995,10 +1011,13 @@ top:
                 */
                if (is_latencies)
                {
-                       instr_time      now;
                        int                     cnum = commands[st->state]->command_num;
 
-                       INSTR_TIME_SET_CURRENT(now);
+                       if (!now_valid)
+                       {
+                               INSTR_TIME_SET_CURRENT(now);
+                               now_valid = true;
+                       }
                        INSTR_TIME_ACCUM_DIFF(thread->exec_elapsed[cnum],
                                                                  now, st->stmt_begin);
                        thread->exec_count[cnum]++;
@@ -1007,12 +1026,16 @@ top:
                /* transaction finished: record latency under progress or throttling */
                if ((progress || throttle_delay) && commands[st->state + 1] == NULL)
                {
-                       instr_time      diff;
                        int64           latency;
 
-                       INSTR_TIME_SET_CURRENT(diff);
-                       INSTR_TIME_SUBTRACT(diff, st->txn_begin);
-                       latency = INSTR_TIME_GET_MICROSEC(diff);
+                       if (!now_valid)
+                       {
+                               INSTR_TIME_SET_CURRENT(now);
+                               now_valid = true;
+                       }
+
+                       latency = INSTR_TIME_GET_MICROSEC(now) - st->txn_scheduled;
+
                        st->txn_latencies += latency;
 
                        /*
@@ -1030,9 +1053,8 @@ top:
                 */
                if (logfile && commands[st->state + 1] == NULL)
                {
-                       instr_time      now;
-                       instr_time      diff;
-                       double          usec;
+                       double          lag;
+                       double          latency;
 
                        /*
                         * write the log entry if this row belongs to the random sample,
@@ -1041,10 +1063,13 @@ top:
                        if (sample_rate == 0.0 ||
                                pg_erand48(thread->random_state) <= sample_rate)
                        {
-                               INSTR_TIME_SET_CURRENT(now);
-                               diff = now;
-                               INSTR_TIME_SUBTRACT(diff, st->txn_begin);
-                               usec = (double) INSTR_TIME_GET_MICROSEC(diff);
+                               if (!now_valid)
+                               {
+                                       INSTR_TIME_SET_CURRENT(now);
+                                       now_valid = true;
+                               }
+                               latency = (double) (INSTR_TIME_GET_MICROSEC(now) - st->txn_scheduled);
+                               lag = (double) (INSTR_TIME_GET_MICROSEC(st->txn_begin) - st->txn_scheduled);
 
                                /* should we aggregate the results or not? */
                                if (agg_interval > 0)
@@ -1056,15 +1081,27 @@ top:
                                        if (agg->start_time + agg_interval >= INSTR_TIME_GET_DOUBLE(now))
                                        {
                                                agg->cnt += 1;
-                                               agg->sum += usec;
-                                               agg->sum2 += usec * usec;
+                                               agg->sum_latency += latency;
+                                               agg->sum2_latency += latency * latency;
 
                                                /* first in this aggregation interval */
-                                               if ((agg->cnt == 1) || (usec < agg->min_duration))
-                                                       agg->min_duration = usec;
+                                               if ((agg->cnt == 1) || (latency < agg->min_latency))
+                                                       agg->min_latency = latency;
+
+                                               if ((agg->cnt == 1) || (latency > agg->max_latency))
+                                                       agg->max_latency = latency;
+
+                                               /* and the same for schedule lag */
+                                               if (throttle_delay)
+                                               {
+                                                       agg->sum_lag += lag;
+                                                       agg->sum2_lag += lag * lag;
 
-                                               if ((agg->cnt == 1) || (usec > agg->max_duration))
-                                                       agg->max_duration = usec;
+                                                       if ((agg->cnt == 1) || (lag < agg->min_lag))
+                                                               agg->min_lag = lag;
+                                                       if ((agg->cnt == 1) || (lag > agg->max_lag))
+                                                               agg->max_lag = lag;
+                                               }
                                        }
                                        else
                                        {
@@ -1080,23 +1117,34 @@ top:
                                                         * ifdef in usage), so we don't need to handle
                                                         * this in a special way (see below).
                                                         */
-                                                       fprintf(logfile, "%ld %d %.0f %.0f %.0f %.0f\n",
+                                                       fprintf(logfile, "%ld %d %.0f %.0f %.0f %.0f",
                                                                        agg->start_time,
                                                                        agg->cnt,
-                                                                       agg->sum,
-                                                                       agg->sum2,
-                                                                       agg->min_duration,
-                                                                       agg->max_duration);
+                                                                       agg->sum_latency,
+                                                                       agg->sum2_latency,
+                                                                       agg->min_latency,
+                                                                       agg->max_latency);
+                                                       if (throttle_delay)
+                                                               fprintf(logfile, " %.0f %.0f %.0f %.0f",
+                                                                               agg->sum_lag,
+                                                                               agg->sum2_lag,
+                                                                               agg->min_lag,
+                                                                               agg->max_lag);
+                                                       fputc('\n', logfile);
 
                                                        /* move to the next inteval */
                                                        agg->start_time = agg->start_time + agg_interval;
 
                                                        /* reset for "no transaction" intervals */
                                                        agg->cnt = 0;
-                                                       agg->min_duration = 0;
-                                                       agg->max_duration = 0;
-                                                       agg->sum = 0;
-                                                       agg->sum2 = 0;
+                                                       agg->min_latency = 0;
+                                                       agg->max_latency = 0;
+                                                       agg->sum_latency = 0;
+                                                       agg->sum2_latency = 0;
+                                                       agg->min_lag = 0;
+                                                       agg->max_lag = 0;
+                                                       agg->sum_lag = 0;
+                                                       agg->sum2_lag = 0;
                                                }
 
                                                /*
@@ -1104,10 +1152,14 @@ top:
                                                 * current)
                                                 */
                                                agg->cnt = 1;
-                                               agg->min_duration = usec;
-                                               agg->max_duration = usec;
-                                               agg->sum = usec;
-                                               agg->sum2 = usec * usec;
+                                               agg->min_latency = latency;
+                                               agg->max_latency = latency;
+                                               agg->sum_latency = latency;
+                                               agg->sum2_latency = latency * latency;
+                                               agg->min_lag = lag;
+                                               agg->max_lag = lag;
+                                               agg->sum_lag = lag;
+                                               agg->sum2_lag = lag * lag;
                                        }
                                }
                                else
@@ -1119,8 +1171,8 @@ top:
                                         * This is more than we really ought to know about
                                         * instr_time
                                         */
-                                       fprintf(logfile, "%d %d %.0f %d %ld %ld\n",
-                                                       st->id, st->cnt, usec, st->use_file,
+                                       fprintf(logfile, "%d %d %.0f %d %ld %ld",
+                                                       st->id, st->cnt, latency, st->use_file,
                                                        (long) now.tv_sec, (long) now.tv_usec);
 #else
 
@@ -1128,9 +1180,12 @@ top:
                                         * On Windows, instr_time doesn't provide a timestamp
                                         * anyway
                                         */
-                                       fprintf(logfile, "%d %d %.0f %d 0 0\n",
+                                       fprintf(logfile, "%d %d %.0f %d 0 0",
                                                        st->id, st->cnt, usec, st->use_file);
 #endif
+                                       if (throttle_delay)
+                                               fprintf(logfile, " %.0f", lag);
+                                       fputc('\n', logfile);
                                }
                        }
                }
@@ -1219,8 +1274,17 @@ top:
 
        /* Record transaction start time under logging, progress or throttling */
        if ((logfile || progress || throttle_delay) && st->state == 0)
+       {
                INSTR_TIME_SET_CURRENT(st->txn_begin);
 
+               /*
+                * When not throttling, this is also the transaction's scheduled start
+                * time.
+                */
+               if (!throttle_delay)
+                       st->txn_scheduled = INSTR_TIME_GET_MICROSEC(st->txn_begin);
+       }
+
        /* Record statement start time if per-command latencies are requested */
        if (is_latencies)
                INSTR_TIME_SET_CURRENT(st->stmt_begin);
@@ -1489,7 +1553,7 @@ top:
                                usec *= 1000000;
 
                        INSTR_TIME_SET_CURRENT(now);
-                       st->until = INSTR_TIME_GET_MICROSEC(now) + usec;
+                       st->txn_scheduled = INSTR_TIME_GET_MICROSEC(now) + usec;
                        st->sleeping = 1;
 
                        st->listen = 1;
@@ -3108,7 +3172,7 @@ threadRun(void *arg)
                                                now_usec = INSTR_TIME_GET_MICROSEC(now);
                                        }
 
-                                       this_usec = st->until - now_usec;
+                                       this_usec = st->txn_scheduled - now_usec;
                                        if (min_usec > this_usec)
                                                min_usec = this_usec;
                                }
index 408e7fb194145a65f9e6ce9546453035bc3f1577..4187fc1a0e3d7217db9ccc411e4b4f971de68bd2 100644 (file)
@@ -403,8 +403,10 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
         Show progress report every <literal>sec</> seconds.  The report
         includes the time since the beginning of the run, the tps since the
         last report, and the transaction latency average and standard
-        deviation since the last report.  Under throttling (<option>-R</>), it
-        also includes the average schedule lag time since the last report.
+        deviation since the last report.  Under throttling (<option>-R</>),
+        the latency is computed with respect to the transaction scheduled
+        start time, not the actual transaction beginning time, thus it also
+        includes the average schedule lag time.
        </para>
       </listitem>
      </varlistentry>
@@ -440,29 +442,25 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
         possible for later ones to catch up again.
        </para>
        <para>
-        When throttling is active, the average and maximum transaction
-        schedule lag time are reported in ms.  This is the delay between
-        the original scheduled transaction time and the actual transaction
-        start times.  The schedule lag shows whether a transaction was
-        started on time or late.  Once a client starts running behind its
-        schedule, every following transaction can continue to be penalized
-        for schedule lag.  If faster transactions are able to catch up, it's
-        possible for them to get back on schedule again.  The lag measurement
-        of every transaction is shown when pgbench is run with debugging
-        output.
+        When throttling is active, the transaction latency reported at the
+        end of the run is calculated from the scheduled start times, so it
+        includes the time each transaction had to wait for the previous
+        transaction to finish. The wait time is called the schedule lag time,
+        and its average and maximum are also reported separately. The
+        transaction latency with respect to the actual transaction start time,
+        i.e. the time spent executing the transaction in the database, can be
+        computed by subtracting the schedule lag time from the reported
+        latency.
        </para>
+
        <para>
-        High rate limit schedule lag values, that is lag values that are large
-        compared to the actual transaction latency, indicate that something is
-        amiss in the throttling process.  High schedule lag can highlight a subtle
-        problem there even if the target rate limit is met in the end.  One
-        possible cause of schedule lag is insufficient pgbench threads to
-        handle all of the clients.  To improve that, consider reducing the
-        number of clients, increasing the number of threads in pgbench, or
-        running pgbench on a separate host.  Another possibility is that the
-        database is not keeping up with the load at some point.  When that
-        happens, you will have to reduce the expected transaction rate to
-        lower schedule lag.
+        A high schedule lag time is an indication that the system cannot
+        process transactions at the specified rate, with the chosen number of
+        clients and threads. When the average transaction execution time is
+        longer than the scheduled interval between each transaction, each
+        successive transaction will fall further behind, and the schedule lag
+        time will keep increasing the longer the test run is. When that
+        happens, you will have to reduce the specified transaction rate.
        </para>
       </listitem>
      </varlistentry>
@@ -888,7 +886,7 @@ END;
    The format of the log is:
 
 <synopsis>
-<replaceable>client_id</> <replaceable>transaction_no</> <replaceable>time</> <replaceable>file_no</> <replaceable>time_epoch</> <replaceable>time_us</>
+<replaceable>client_id</> <replaceable>transaction_no</> <replaceable>time</> <replaceable>file_no</> <replaceable>time_epoch</> <replaceable>time_us</> [<replaceable>schedule_lag</replaceable>]
 </synopsis>
 
    where <replaceable>time</> is the total elapsed transaction time in microseconds,
@@ -898,7 +896,10 @@ END;
    UNIX epoch format timestamp and an offset
    in microseconds (suitable for creating an ISO 8601
    timestamp with fractional seconds) showing when
-   the transaction completed.
+   the transaction completed. The last field, <replaceable>schedule_lag</>, is
+   the difference between the transaction's scheduled start time, and the
+   time it actually started, in microseconds. It is only present when the
+   <option>--rate</> option is used.
   </para>
 
   <para>
@@ -924,7 +925,7 @@ END;
    With the <option>--aggregate-interval</option> option, the logs use a bit different format:
 
 <synopsis>
-<replaceable>interval_start</> <replaceable>num_of_transactions</> <replaceable>latency_sum</> <replaceable>latency_2_sum</> <replaceable>min_latency</> <replaceable>max_latency</>
+<replaceable>interval_start</> <replaceable>num_of_transactions</> <replaceable>latency_sum</> <replaceable>latency_2_sum</> <replaceable>min_latency</> <replaceable>max_latency</> [<replaceable>lag_sum</> <replaceable>lag_2_sum</> <replaceable>min_lag</> <replaceable>max_lag</>]
 </synopsis>
 
    where <replaceable>interval_start</> is the start of the interval (UNIX epoch
@@ -935,7 +936,11 @@ END;
    <replaceable>latency_2_sum</> is a sum of 2nd powers of latencies. The last two
    fields are <replaceable>min_latency</> - a minimum latency within the interval, and
    <replaceable>max_latency</> - maximum latency within the interval. A transaction is
-   counted into the interval when it was committed.
+   counted into the interval when it was committed. The last four fields, 
+   <replaceable>lag_sum</>, <replaceable>lag_2_sum</>, <replaceable>min_lag</>, and <replaceable>max_lag</>, are only present if the --rate option is used.
+   They are calculated from the time each transaction had to wait for the
+   previous one to finish, i.e. the difference between each transaction's
+   scheduled start time and the time it actually started.
   </para>
 
   <para>