Merge pull request 'test(api): let a shutdown's closed connections leave pg_stat_activity' (#100) from fix/flaky-shutdown-connection-check into main
CI / verify (push) Successful in 1m10s
CI / infrastructure-smoke (push) Successful in 35s
CI / package-release (push) Successful in 49s
CI / image-build (push) Successful in 32s
CI / migration-smoke (push) Successful in 2m20s
CI / deploy-smoke (same-origin) (push) Successful in 55s
CI / deploy-smoke (split) (push) Successful in 2m25s
CI / package-publication (push) Successful in 1m2s
CI / verify (push) Successful in 1m10s
CI / infrastructure-smoke (push) Successful in 35s
CI / package-release (push) Successful in 49s
CI / image-build (push) Successful in 32s
CI / migration-smoke (push) Successful in 2m20s
CI / deploy-smoke (same-origin) (push) Successful in 55s
CI / deploy-smoke (split) (push) Successful in 2m25s
CI / package-publication (push) Successful in 1m2s
Reviewed-on: #100 Reviewed-by: Zayin_Admin <[email protected]>
This commit was merged in pull request #100.
This commit is contained in:
@@ -208,6 +208,29 @@ async function activeConnectionCount(databaseName: string): Promise<number> {
|
||||
}
|
||||
}
|
||||
|
||||
/** How long a shutdown's closed connections may take to leave `pg_stat_activity`. Far below pg's
|
||||
* 10 s idle timeout, so a connection that is genuinely still open is still caught. */
|
||||
const SHUTDOWN_SETTLE_MS = 3_000;
|
||||
|
||||
/**
|
||||
* The connections left on `databaseName` once a shutdown has had a moment to land.
|
||||
*
|
||||
* PostgreSQL ends a backend asynchronously: `pool.end()` resolves once the client side has closed,
|
||||
* and the server can still list that backend for a moment afterwards. On a loaded CI runner that
|
||||
* moment was long enough for an immediate count to see one. So this waits, briefly, for the count to
|
||||
* reach zero; a leaked pooled client would stay idle and listed far past the deadline.
|
||||
*/
|
||||
async function connectionsLeftAfterShutdown(databaseName: string): Promise<number> {
|
||||
const deadline = Date.now() + SHUTDOWN_SETTLE_MS;
|
||||
for (;;) {
|
||||
const count = await activeConnectionCount(databaseName);
|
||||
if (count === 0 || Date.now() >= deadline) {
|
||||
return count;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
}
|
||||
|
||||
async function createDisposableDatabase(label: string): Promise<{ name: string; url: string }> {
|
||||
const name = makeDatabaseName(label);
|
||||
await createDatabase(name);
|
||||
@@ -1349,7 +1372,7 @@ describe.sequential("live API — signup, sign-in, principal, sign-out", () => {
|
||||
await running.shutdown();
|
||||
|
||||
await expect(fetch(origin, { signal: AbortSignal.timeout(1_000) })).rejects.toBeTruthy();
|
||||
const remainingConnections = await activeConnectionCount(database.name);
|
||||
const remainingConnections = await connectionsLeftAfterShutdown(database.name);
|
||||
expect(remainingConnections).toBe(0);
|
||||
|
||||
await dropDatabase(database.name);
|
||||
@@ -2230,7 +2253,7 @@ describe.sequential("live API — platform owner and delegated grant management"
|
||||
|
||||
it("shuts down cleanly, closing the one shared pool", async () => {
|
||||
await running.shutdown();
|
||||
const remainingConnections = await activeConnectionCount(database.name);
|
||||
const remainingConnections = await connectionsLeftAfterShutdown(database.name);
|
||||
expect(remainingConnections).toBe(0);
|
||||
await dropDatabase(database.name);
|
||||
});
|
||||
@@ -2837,7 +2860,7 @@ describe.sequential("live API — installed module permission end-to-end", () =>
|
||||
|
||||
it("shuts down cleanly, then restarting with the module absent rejects new grants for its former permission, returns NOT_FOUND for its former route, while the historical grant remains listable", async () => {
|
||||
await running.shutdown();
|
||||
const remainingConnections = await activeConnectionCount(database.name);
|
||||
const remainingConnections = await connectionsLeftAfterShutdown(database.name);
|
||||
expect(remainingConnections).toBe(0);
|
||||
// Exactly one stop, matching the exactly-one register/start already asserted above.
|
||||
expect(recorder.events.filter((event) => event === "calendar:stop")).toHaveLength(1);
|
||||
|
||||
@@ -195,6 +195,29 @@ async function activeConnectionCount(databaseName: string): Promise<number> {
|
||||
}
|
||||
}
|
||||
|
||||
/** How long a shutdown's closed connections may take to leave `pg_stat_activity`. Far below pg's
|
||||
* 10 s idle timeout, so a connection that is genuinely still open is still caught. */
|
||||
const SHUTDOWN_SETTLE_MS = 3_000;
|
||||
|
||||
/**
|
||||
* The connections left on `databaseName` once a shutdown has had a moment to land.
|
||||
*
|
||||
* PostgreSQL ends a backend asynchronously: `pool.end()` resolves once the client side has closed,
|
||||
* and the server can still list that backend for a moment afterwards. On a loaded CI runner that
|
||||
* moment was long enough for an immediate count to see one. So this waits, briefly, for the count to
|
||||
* reach zero; a leaked pooled client would stay idle and listed far past the deadline.
|
||||
*/
|
||||
async function connectionsLeftAfterShutdown(databaseName: string): Promise<number> {
|
||||
const deadline = Date.now() + SHUTDOWN_SETTLE_MS;
|
||||
for (;;) {
|
||||
const count = await activeConnectionCount(databaseName);
|
||||
if (count === 0 || Date.now() >= deadline) {
|
||||
return count;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
}
|
||||
|
||||
async function createDisposableDatabase(label: string): Promise<{ name: string; url: string }> {
|
||||
const name = makeDatabaseName(label);
|
||||
await createDatabase(name);
|
||||
@@ -760,7 +783,7 @@ describe.sequential("live API — module database access through ModuleRuntimeSe
|
||||
|
||||
await running.shutdown();
|
||||
|
||||
const remainingConnections = await activeConnectionCount(database.name);
|
||||
const remainingConnections = await connectionsLeftAfterShutdown(database.name);
|
||||
expect(remainingConnections).toBe(0);
|
||||
|
||||
const eventsAfterShutdown = readRecorder();
|
||||
|
||||
Reference in New Issue
Block a user