diff options
Diffstat (limited to 'src/bin/pgbench/pgbench.c')
| -rw-r--r-- | src/bin/pgbench/pgbench.c | 117 |
1 files changed, 95 insertions, 22 deletions
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 1515ed405ba..d8764ba6fe0 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -402,14 +402,15 @@ typedef struct StatsData * directly successful transactions (they were successfully completed on * the first try). * - * A failed transaction is defined as unsuccessfully retried transactions. - * It can be one of two types: - * - * failed (the number of failed transactions) = + * 'failed' (the number of failed transactions) = * 'serialization_failures' (they got a serialization error and were not - * successfully retried) + + * successfully retried) + * 'deadlock_failures' (they got a deadlock error and were not - * successfully retried). + * successfully retried) + + * 'other_sql_failures' (they failed on the first try or after retries + * due to a SQL error other than serialization or + * deadlock; they are counted as a failed transaction + * only when --continue-on-error is specified). * * If the transaction was retried after a serialization or a deadlock * error this does not guarantee that this retry was successful. Thus @@ -421,7 +422,7 @@ typedef struct StatsData * * 'retried' (number of all retried transactions) = * successfully retried transactions + - * failed transactions. + * unsuccessful retried transactions. *---------- */ int64 cnt; /* number of successful transactions, not @@ -440,6 +441,11 @@ typedef struct StatsData int64 deadlock_failures; /* number of transactions that were not * successfully retried after a deadlock * error */ + int64 other_sql_failures; /* number of failed transactions for + * reasons other than + * serialization/deadlock failure, which + * is counted if --continue-on-error is + * specified */ SimpleStats latency; SimpleStats lag; } StatsData; @@ -457,6 +463,7 @@ typedef enum EStatus { ESTATUS_NO_ERROR = 0, ESTATUS_META_COMMAND_ERROR, + ESTATUS_CONN_ERROR, /* SQL errors */ ESTATUS_SERIALIZATION_ERROR, @@ -770,6 +777,7 @@ static int64 total_weight = 0; static bool verbose_errors = false; /* print verbose messages of all errors */ static bool exit_on_abort = false; /* exit when any client is aborted */ +static bool continue_on_error = false; /* continue after errors */ /* Builtin test scripts */ typedef struct BuiltinScript @@ -949,6 +957,7 @@ usage(void) " -T, --time=NUM duration of benchmark test in seconds\n" " -v, --vacuum-all vacuum all four standard tables before tests\n" " --aggregate-interval=NUM aggregate data over NUM seconds\n" + " --continue-on-error continue running after an SQL error\n" " --exit-on-abort exit when any client is aborted\n" " --failures-detailed report the failures grouped by basic types\n" " --log-prefix=PREFIX prefix for transaction time log file\n" @@ -1467,6 +1476,7 @@ initStats(StatsData *sd, pg_time_usec_t start) sd->retried = 0; sd->serialization_failures = 0; sd->deadlock_failures = 0; + sd->other_sql_failures = 0; initSimpleStats(&sd->latency); initSimpleStats(&sd->lag); } @@ -1516,6 +1526,9 @@ accumStats(StatsData *stats, bool skipped, double lat, double lag, case ESTATUS_DEADLOCK_ERROR: stats->deadlock_failures++; break; + case ESTATUS_OTHER_SQL_ERROR: + stats->other_sql_failures++; + break; default: /* internal error which should never occur */ pg_fatal("unexpected error status: %d", estatus); @@ -3231,11 +3244,43 @@ sendCommand(CState *st, Command *command) } /* - * Get the error status from the error code. + * Read and discard all available results from the connection. + */ +static void +discardAvailableResults(CState *st) +{ + PGresult *res = NULL; + + for (;;) + { + res = PQgetResult(st->con); + + /* + * Read and discard results until PQgetResult() returns NULL (no more + * results) or a connection failure is detected. If the pipeline + * status is PQ_PIPELINE_ABORTED, more results may still be available + * even after PQgetResult() returns NULL, so continue reading in that + * case. + */ + if ((res == NULL && PQpipelineStatus(st->con) != PQ_PIPELINE_ABORTED) || + PQstatus(st->con) == CONNECTION_BAD) + break; + + PQclear(res); + } + PQclear(res); +} + +/* + * Determine the error status based on the connection status and error code. */ static EStatus -getSQLErrorStatus(const char *sqlState) +getSQLErrorStatus(CState *st, const char *sqlState) { + discardAvailableResults(st); + if (PQstatus(st->con) == CONNECTION_BAD) + return ESTATUS_CONN_ERROR; + if (sqlState != NULL) { if (strcmp(sqlState, ERRCODE_T_R_SERIALIZATION_FAILURE) == 0) @@ -3258,6 +3303,17 @@ canRetryError(EStatus estatus) } /* + * Returns true if --continue-on-error is specified and this error allows + * processing to continue. + */ +static bool +canContinueOnError(EStatus estatus) +{ + return (continue_on_error && + estatus == ESTATUS_OTHER_SQL_ERROR); +} + +/* * Process query response from the backend. * * If varprefix is not NULL, it's the variable name prefix where to store @@ -3375,9 +3431,9 @@ readCommandResponse(CState *st, MetaCommand meta, char *varprefix) case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: - st->estatus = getSQLErrorStatus(PQresultErrorField(res, - PG_DIAG_SQLSTATE)); - if (canRetryError(st->estatus)) + st->estatus = getSQLErrorStatus(st, PQresultErrorField(res, + PG_DIAG_SQLSTATE)); + if (canRetryError(st->estatus) || canContinueOnError(st->estatus)) { if (verbose_errors) commandError(st, PQresultErrorMessage(res)); @@ -3409,11 +3465,7 @@ readCommandResponse(CState *st, MetaCommand meta, char *varprefix) error: PQclear(res); PQclear(next_res); - do - { - res = PQgetResult(st->con); - PQclear(res); - } while (res); + discardAvailableResults(st); return false; } @@ -4041,7 +4093,7 @@ advanceConnectionState(TState *thread, CState *st, StatsData *agg) if (PQpipelineStatus(st->con) != PQ_PIPELINE_ON) st->state = CSTATE_END_COMMAND; } - else if (canRetryError(st->estatus)) + else if (canRetryError(st->estatus) || canContinueOnError(st->estatus)) st->state = CSTATE_ERROR; else st->state = CSTATE_ABORTED; @@ -4562,7 +4614,8 @@ static int64 getFailures(const StatsData *stats) { return (stats->serialization_failures + - stats->deadlock_failures); + stats->deadlock_failures + + stats->other_sql_failures); } /* @@ -4582,6 +4635,8 @@ getResultString(bool skipped, EStatus estatus) return "serialization"; case ESTATUS_DEADLOCK_ERROR: return "deadlock"; + case ESTATUS_OTHER_SQL_ERROR: + return "other"; default: /* internal error which should never occur */ pg_fatal("unexpected error status: %d", estatus); @@ -4637,6 +4692,7 @@ doLog(TState *thread, CState *st, int64 skipped = 0; int64 serialization_failures = 0; int64 deadlock_failures = 0; + int64 other_sql_failures = 0; int64 retried = 0; int64 retries = 0; @@ -4677,10 +4733,12 @@ doLog(TState *thread, CState *st, { serialization_failures = agg->serialization_failures; deadlock_failures = agg->deadlock_failures; + other_sql_failures = agg->other_sql_failures; } - fprintf(logfile, " " INT64_FORMAT " " INT64_FORMAT, + fprintf(logfile, " " INT64_FORMAT " " INT64_FORMAT " " INT64_FORMAT, serialization_failures, - deadlock_failures); + deadlock_failures, + other_sql_failures); fputc('\n', logfile); @@ -6319,6 +6377,7 @@ printProgressReport(TState *threads, int64 test_start, pg_time_usec_t now, cur.serialization_failures += threads[i].stats.serialization_failures; cur.deadlock_failures += threads[i].stats.deadlock_failures; + cur.other_sql_failures += threads[i].stats.other_sql_failures; } /* we count only actually executed transactions */ @@ -6461,7 +6520,8 @@ printResults(StatsData *total, /* * Remaining stats are nonsensical if we failed to execute any xacts due - * to others than serialization or deadlock errors + * to other than serialization or deadlock errors and --continue-on-error + * is not set. */ if (total_cnt <= 0) return; @@ -6477,6 +6537,9 @@ printResults(StatsData *total, printf("number of deadlock failures: " INT64_FORMAT " (%.3f%%)\n", total->deadlock_failures, 100.0 * total->deadlock_failures / total_cnt); + printf("number of other failures: " INT64_FORMAT " (%.3f%%)\n", + total->other_sql_failures, + 100.0 * total->other_sql_failures / total_cnt); } /* it can be non-zero only if max_tries is not equal to one */ @@ -6580,6 +6643,10 @@ printResults(StatsData *total, sstats->deadlock_failures, (100.0 * sstats->deadlock_failures / script_total_cnt)); + printf(" - number of other failures: " INT64_FORMAT " (%.3f%%)\n", + sstats->other_sql_failures, + (100.0 * sstats->other_sql_failures / + script_total_cnt)); } /* @@ -6739,6 +6806,7 @@ main(int argc, char **argv) {"verbose-errors", no_argument, NULL, 15}, {"exit-on-abort", no_argument, NULL, 16}, {"debug", no_argument, NULL, 17}, + {"continue-on-error", no_argument, NULL, 18}, {NULL, 0, NULL, 0} }; @@ -7092,6 +7160,10 @@ main(int argc, char **argv) case 17: /* debug */ pg_logging_increase_verbosity(); break; + case 18: /* continue-on-error */ + benchmarking_option_set = true; + continue_on_error = true; + break; default: /* getopt_long already emitted a complaint */ pg_log_error_hint("Try \"%s --help\" for more information.", progname); @@ -7447,6 +7519,7 @@ main(int argc, char **argv) stats.retried += thread->stats.retried; stats.serialization_failures += thread->stats.serialization_failures; stats.deadlock_failures += thread->stats.deadlock_failures; + stats.other_sql_failures += thread->stats.other_sql_failures; latency_late += thread->latency_late; conn_total_duration += thread->conn_duration; |
