diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c index f5ac3b1..19c3b18 100644 --- a/contrib/pgbench/pgbench.c +++ b/contrib/pgbench/pgbench.c @@ -145,6 +145,7 @@ char *index_tablespace = NULL; #define naccounts 100000 bool use_log; /* log transaction latencies to a file */ +int use_log_agg; /* log aggregates instead of individual transactions */ bool is_connect; /* establish connection for each transaction */ bool is_latencies; /* report per-command latencies */ int main_pid; /* main process id used in log filename */ @@ -240,6 +241,18 @@ typedef struct char *argv[MAX_ARGS]; /* command word list */ } Command; +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 estimates */ + double sum2; + +} AggVals; + static Command **sql_files[MAX_FILES]; /* SQL script files */ static int num_files; /* number of script files */ static int num_commands = 0; /* total number of Command structs */ @@ -364,6 +377,8 @@ usage(void) " -f FILENAME read transaction script from FILENAME\n" " -j NUM number of threads (default: 1)\n" " -l write transaction times to log file\n" + " --aggregate-interval NUM\n" + " aggregate data over NUM seconds\n" " -M simple|extended|prepared\n" " protocol for submitting queries to server (default: simple)\n" " -n do not run VACUUM before tests\n" @@ -817,9 +832,22 @@ clientDone(CState *st, bool ok) return false; /* always false */ } +static +void agg_vals_init(AggVals * aggs, instr_time start) +{ + aggs->cnt = 0; + aggs->sum = 0; + aggs->sum2 = 0; + + aggs->min_duration = 3600 * 1000000.0; /* one hour */ + aggs->max_duration = 0; + + aggs->start_time = INSTR_TIME_GET_DOUBLE(start); +} + /* return false iff client should be disconnected */ static bool -doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile) +doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile, AggVals * agg) { PGresult *res; Command **commands; @@ -881,17 +909,70 @@ top: diff = now; INSTR_TIME_SUBTRACT(diff, st->txn_begin); usec = (double) INSTR_TIME_GET_MICROSEC(diff); - + + /* should we aggregate the results or not? */ + if (use_log_agg) + { + + /* are we still in the same interval? if yes, accumulate the + * values (print them otherwise) */ + if (agg->start_time + use_log_agg >= INSTR_TIME_GET_DOUBLE(now)) + { + + /* accumulate */ + agg->cnt += 1; + + agg->min_duration = (usec < agg->min_duration) ? usec : agg->min_duration; + agg->max_duration = (usec > agg->max_duration) ? usec : agg->max_duration; + + agg->sum += usec; + agg->sum2 += usec * usec; + + } + else + { + + /* print */ +#ifndef WIN32 + /* This is more than we really ought to know about instr_time */ + fprintf(logfile, "%ld %d %.0f %.0f %.0f %.0f\n", + agg->start_time, agg->cnt, agg->sum, agg->sum2, + agg->min_duration, agg->max_duration); +#else + /* On Windows, instr_time doesn't provide a timestamp anyway */ + // FIXME +// fprintf(logfile, "%d %ld %.0f %d 0 0\n", +// st->id, aggvals.start_time, aggvals.cnt, usec, st->use_file); +#endif + /* and now reset the values (include the current) */ + agg->cnt = 1; + agg->min_duration = usec; + agg->max_duration = usec; + + agg->sum = usec; + agg->sum2 = usec * usec; + + /* move to the next interval (there may be transactions longer than + * the desired interval */ + while (agg->start_time + use_log_agg < INSTR_TIME_GET_DOUBLE(now)) + agg->start_time = agg->start_time + use_log_agg; + } + + } + else + { + /* no, print raw transactions */ #ifndef WIN32 - /* 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, - (long) now.tv_sec, (long) now.tv_usec); + /* 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, + (long) now.tv_sec, (long) now.tv_usec); #else - /* On Windows, instr_time doesn't provide a timestamp anyway */ - fprintf(logfile, "%d %d %.0f %d 0 0\n", - st->id, st->cnt, usec, st->use_file); + /* On Windows, instr_time doesn't provide a timestamp anyway */ + fprintf(logfile, "%d %d %.0f %d 0 0\n", + st->id, st->cnt, usec, st->use_file); #endif + } } if (commands[st->state]->type == SQL_COMMAND) @@ -1920,6 +2001,7 @@ main(int argc, char **argv) {"index-tablespace", required_argument, NULL, 3}, {"tablespace", required_argument, NULL, 2}, {"unlogged-tables", no_argument, &unlogged_tables, 1}, + {"aggregate-interval", required_argument, NULL, 5}, {NULL, 0, NULL, 0} }; @@ -2125,6 +2207,14 @@ main(int argc, char **argv) case 3: /* index-tablespace */ index_tablespace = optarg; break; + case 5: + use_log_agg = atoi(optarg); + if (use_log_agg <= 0) + { + fprintf(stderr, "invalid number of seconds for aggregation: %d\n", use_log_agg); + exit(1); + } + break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); @@ -2160,6 +2250,21 @@ main(int argc, char **argv) exit(1); } + if (use_log_agg > 0 && (! use_log)) { + fprintf(stderr, "log aggregation is allowed only when actually logging transactions\n"); + exit(1); + } + + if ((duration > 0) && (use_log_agg > duration)) { + fprintf(stderr, "number of seconds for aggregation (%d) must not be higher that test duration (%d)\n", use_log_agg, duration); + exit(1); + } + + if ((duration > 0) && (use_log_agg > 0) && (duration % use_log_agg != 0)) { + fprintf(stderr, "duration (%d) must be a multiple of aggregation interval (%d)\n", duration, use_log_agg); + exit(1); + } + /* * is_latencies only works with multiple threads in thread-based * implementations, not fork-based ones, because it supposes that the @@ -2418,7 +2523,9 @@ threadRun(void *arg) int nstate = thread->nstate; int remains = nstate; /* number of remaining clients */ int i; - + + AggVals aggs; + result = xmalloc(sizeof(TResult)); INSTR_TIME_SET_ZERO(result->conn_time); @@ -2454,6 +2561,8 @@ threadRun(void *arg) INSTR_TIME_SET_CURRENT(result->conn_time); INSTR_TIME_SUBTRACT(result->conn_time, thread->start_time); + agg_vals_init(&aggs, thread->start_time); + /* send start up queries in async manner */ for (i = 0; i < nstate; i++) { @@ -2462,7 +2571,7 @@ threadRun(void *arg) int prev_ecnt = st->ecnt; st->use_file = getrand(thread, 0, num_files - 1); - if (!doCustom(thread, st, &result->conn_time, logfile)) + if (!doCustom(thread, st, &result->conn_time, logfile, &aggs)) remains--; /* I've aborted */ if (st->ecnt > prev_ecnt && commands[st->state]->type == META_COMMAND) @@ -2564,7 +2673,7 @@ threadRun(void *arg) if (st->con && (FD_ISSET(PQsocket(st->con), &input_mask) || commands[st->state]->type == META_COMMAND)) { - if (!doCustom(thread, st, &result->conn_time, logfile)) + if (!doCustom(thread, st, &result->conn_time, logfile, &aggs)) remains--; /* I've aborted */ } @@ -2591,7 +2700,6 @@ done: return result; } - /* * Support for duration option: set timer_exceeded after so many seconds. */ @@ -2785,4 +2893,4 @@ pthread_join(pthread_t th, void **thread_return) return 0; } -#endif /* WIN32 */ +#endif /* WIN32 */ \ No newline at end of file diff --git a/doc/src/sgml/pgbench.sgml b/doc/src/sgml/pgbench.sgml index 437fcea..014810b 100644 --- a/doc/src/sgml/pgbench.sgml +++ b/doc/src/sgml/pgbench.sgml @@ -317,6 +317,18 @@ pgbench options dbname + seconds + + + Length of aggregation interval (in seconds). May be used only together + with -l - with this option, the log contains + per-interval summary (number of transactions, min/max latency and two + additional fields useful for variance estimation). + + + + + querymode @@ -712,8 +724,9 @@ END; Per-Transaction Logging - With the + + Aggregated Logging + + + With the option, the logs use a bit different format: + + +interval_start num_of_transactions latency_sum latency_2_sum min_latency max_latency + + + where interval_start is the start of the interval (UNIX epoch + format timestamp), num_of_transactions is the number of transactions + within the interval, latency_sum is a sum of latencies + (so you can compute average latency easily). The following two fields are useful + for variance estimation - latency_sum is a sum of latencies and + latency_2_sum is a sum of 2nd powers of latencies. The last two + fields are min_latency - a minimum latency within the interval, and + max_latency - maximum latency within the interval. A transaction is + counted into the interval when it was committed. + + + + Here is example outputs: + +1345828501 5601 1542744 483552416 61 2573 +1345828503 7884 1979812 565806736 60 1479 +1345828505 7208 1979422 567277552 59 1391 +1345828507 7685 1980268 569784714 60 1398 +1345828509 7073 1979779 573489941 236 1411 + + + + Notice that while the plain (unaggregated) log file contains index + of the custom script files, the aggregated log does not. Therefore if + you need per script data, you need to aggregate the data on your own. + +