diff --git a/src/test/isolation/README b/src/test/isolation/README index aeef7f6..2c17876 100644 --- a/src/test/isolation/README +++ b/src/test/isolation/README @@ -75,9 +75,11 @@ Support for blocking commands Each spec may contain commands that block until further action has been taken (most likely, some other session runs a step that unblocks it or causes a -deadlock). Such a spec needs to be careful to manually specify valid -permutations, i.e. those that would not expect a blocked session to execute a -command. If the spec fails to follow that rule, the spec is aborted. +deadlock). In case one permutation is detected to require running a command on +a session that's blocked by a previous step, this is reported and the +permutation is aborted; the spec continues with the next permutation. The spec +may be careful to specify only permutations that do not cause this problem +(saving some run time), but this is not a hard requirement. Only one command can be waiting at a time. As long as one command is waiting, other commands are run to completion synchronously. diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c index b1d49c8..6d8b78b 100644 --- a/src/test/isolation/isolationtester.c +++ b/src/test/isolation/isolationtester.c @@ -50,6 +50,8 @@ static int step_bsearch_cmp(const void *a, const void *b); static void printResultSet(PGresult *res); +static void report_invalid_permutation(Step *step); + /* close all connections and exit */ static void exit_nicely(void) @@ -479,11 +481,38 @@ run_permutation(TestSpec * testspec, int nsteps, Step ** steps) for (i = 0; i < nsteps; i++) { Step *step = steps[i]; + PGconn *conn = conns[1 + step->session]; + + /* + * Found an invalid permutation: one that requires running a command + * on a blocked session. This can never happen in reality. Report the + * failure, abort the current permutation, and continue with the next + * permutation. + */ + if (waiting != NULL && step->session == waiting->session) + { + int j; + + report_invalid_permutation(step); + + /* + * Finally, complete all running transactions to ensure teardown + * doesn't block. Note we don't roll back the control connection. + */ + for (j = 1; j < nconns; j++) + { + res = PQexec(conns[j], "ROLLBACK"); + if (res != NULL) + PQclear(res); + } - if (!PQsendQuery(conns[1 + step->session], step->sql)) + goto teardown; + } + + if (!PQsendQuery(conn, step->sql)) { fprintf(stdout, "failed to send query for step %s: %s\n", - step->name, PQerrorMessage(conns[1 + step->session])); + step->name, PQerrorMessage(conn)); exit_nicely(); } @@ -519,6 +548,7 @@ run_permutation(TestSpec * testspec, int nsteps, Step ** steps) report_error_message(waiting); } +teardown: /* Perform per-session teardown */ for (i = 0; i < testspec->nsessions; i++) { @@ -687,3 +717,32 @@ printResultSet(PGresult *res) printf("\n"); } } + +static void +report_invalid_permutation(Step *step) +{ + PGconn *conn = conns[1 + step->session]; + PGcancel *cancel; + PGresult *res; + + fprintf(stdout, "step %s: invalid permutation detected, skipping\n", step->name); + + /* Cancel the waiting statement from this session. */ + cancel = PQgetCancel(conn); + if (cancel) + { + char buf[256]; + + if (PQcancel(cancel, buf, sizeof(buf)) != 1) + { + fprintf(stdout, "failed to cancel query: %s\n", buf); + exit_nicely(); + } + + /* Be sure to consume whatever is left in the connection */ + while ((res = PQgetResult(conn)) != NULL) + PQclear(res); + + PQfreeCancel(cancel); + } +}