aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2024-03-13 19:53:49 +0100
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2024-03-13 19:55:09 +0100
commit1ee910ce437188eab40eddf32dc7d81952e99f82 (patch)
tree1630625fd490b9c8b4e9de57486dfdd99006093a
parentdbfc44716596073b99e093a04e29e774a518f520 (diff)
downloadpostgresql-1ee910ce437188eab40eddf32dc7d81952e99f82.tar.gz
postgresql-1ee910ce437188eab40eddf32dc7d81952e99f82.zip
Hopefully make libpq_pipeline's new cancel test more reliable
The newly introduced cancel test in libpq_pipeline was flaky. It's not completely clear why, but one option is that the check for "active" was actually seeing the active state for the previous query. This change should address any such race condition by first waiting until the connection is reported as idle. Author: Jelte Fennema-Nio <me@jeltef.nl> Discussion: https://postgr.es/m/CAGECzQRvmUK5-d68A+cm+fgmfht9Dv2uZ28-qq3QiaF6EAZqPQ@mail.gmail.com
-rw-r--r--src/test/modules/libpq_pipeline/libpq_pipeline.c78
1 files changed, 49 insertions, 29 deletions
diff --git a/src/test/modules/libpq_pipeline/libpq_pipeline.c b/src/test/modules/libpq_pipeline/libpq_pipeline.c
index 1fe15ee8899..c353dba9c70 100644
--- a/src/test/modules/libpq_pipeline/libpq_pipeline.c
+++ b/src/test/modules/libpq_pipeline/libpq_pipeline.c
@@ -114,50 +114,38 @@ confirm_query_canceled_impl(int line, PGconn *conn)
PQconsumeInput(conn);
}
-#define send_cancellable_query(conn, monitorConn) \
- send_cancellable_query_impl(__LINE__, conn, monitorConn)
+/*
+ * Using monitorConn, query pg_stat_activity to see that the connection with
+ * the given PID is in the given state. We never stop until it does.
+ */
static void
-send_cancellable_query_impl(int line, PGconn *conn, PGconn *monitorConn)
+wait_for_connection_state(int line, PGconn *monitorConn, int procpid, char *state)
{
- const char *env_wait;
- const Oid paramTypes[1] = {INT4OID};
- int procpid = PQbackendPID(conn);
-
- env_wait = getenv("PG_TEST_TIMEOUT_DEFAULT");
- if (env_wait == NULL)
- env_wait = "180";
+ const Oid paramTypes[] = {INT4OID, TEXTOID};
+ const char *paramValues[2];
+ char *pidstr = psprintf("%d", procpid);
- if (PQsendQueryParams(conn, "SELECT pg_sleep($1)", 1, paramTypes,
- &env_wait, NULL, NULL, 0) != 1)
- pg_fatal_impl(line, "failed to send query: %s", PQerrorMessage(conn));
+ paramValues[0] = pidstr;
+ paramValues[1] = state;
- /*
- * Wait until the query is actually running. Otherwise sending a
- * cancellation request might not cancel the query due to race conditions.
- */
while (true)
{
- char *value;
PGresult *res;
- const char *paramValues[1];
- char pidval[16];
-
- snprintf(pidval, 16, "%d", procpid);
- paramValues[0] = pidval;
+ char *value;
res = PQexecParams(monitorConn,
"SELECT count(*) FROM pg_stat_activity WHERE "
- "pid = $1 AND state = 'active'",
- 1, NULL, paramValues, NULL, NULL, 1);
+ "pid = $1 AND state = $2",
+ 2, paramTypes, paramValues, NULL, NULL, 1);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
- pg_fatal("could not query pg_stat_activity: %s", PQerrorMessage(monitorConn));
+ pg_fatal_impl(line, "could not query pg_stat_activity: %s", PQerrorMessage(monitorConn));
if (PQntuples(res) != 1)
- pg_fatal("unexpected number of rows received: %d", PQntuples(res));
+ pg_fatal_impl(line, "unexpected number of rows received: %d", PQntuples(res));
if (PQnfields(res) != 1)
- pg_fatal("unexpected number of columns received: %d", PQnfields(res));
+ pg_fatal_impl(line, "unexpected number of columns received: %d", PQnfields(res));
value = PQgetvalue(res, 0, 0);
- if (*value != '0')
+ if (value[0] != '0')
{
PQclear(res);
break;
@@ -167,6 +155,38 @@ send_cancellable_query_impl(int line, PGconn *conn, PGconn *monitorConn)
/* wait 10ms before polling again */
pg_usleep(10000);
}
+
+ pfree(pidstr);
+}
+
+#define send_cancellable_query(conn, monitorConn) \
+ send_cancellable_query_impl(__LINE__, conn, monitorConn)
+static void
+send_cancellable_query_impl(int line, PGconn *conn, PGconn *monitorConn)
+{
+ const char *env_wait;
+ const Oid paramTypes[1] = {INT4OID};
+
+ /*
+ * Wait for the connection to be idle, so that our check for an active
+ * connection below is reliable, instead of possibly seeing an outdated
+ * state.
+ */
+ wait_for_connection_state(line, monitorConn, PQbackendPID(conn), "idle");
+
+ env_wait = getenv("PG_TEST_TIMEOUT_DEFAULT");
+ if (env_wait == NULL)
+ env_wait = "180";
+
+ if (PQsendQueryParams(conn, "SELECT pg_sleep($1)", 1, paramTypes,
+ &env_wait, NULL, NULL, 0) != 1)
+ pg_fatal_impl(line, "failed to send query: %s", PQerrorMessage(conn));
+
+ /*
+ * Wait for the query to start, because if the query is not running yet
+ * the cancel request that we send won't have any effect.
+ */
+ wait_for_connection_state(line, monitorConn, PQbackendPID(conn), "active");
}
/*