test(api): let a shutdown's closed connections leave pg_stat_activity #100

Merged
Zayin_Admin merged 2 commits from fix/flaky-shutdown-connection-check into main 2026-10-14 04:41:38 +00:00
Member

Fixes the flaky migration-smoke failure in run #238, on PR #99, which only changes CLAUDE.md:

FAIL apps/api/tests/module-database.integration.ts > … > stops cleanly: …
AssertionError: expected 1 to be +0      (module-database.integration.ts:764)

Cause. After running.shutdown(), four tests immediately count the database's rows in pg_stat_activity and expect zero. 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. On a loaded shared runner, an immediate count sometimes sees it. Nothing actually leaks.

Reproduced. Against postgres:18.4-alpine3.24 limited to 0.05 CPU, the backend was still listed right after pool.end() resolved in 7 of 300 trials, and gone about 1 ms later. With no CPU limit: 0 of 300.

Fix (tests only). A connectionsLeftAfterShutdown() helper waits up to 3 s for the count to reach zero. It's used by all four post-shutdown checks: one in module-database.integration.ts, three in api-http.integration.ts. A real leak is still caught: an idle pooled client stays listed until pg-pool's 10 s idle timeout (pg-pool 3.14's default; Core's pools don't override it), well past the 3 s deadline. No production code changes.

Verified. pnpm test:migrations:integration passes (401/401, live PostgreSQL). pnpm verify passes (2,639 tests).

Related but separate: #97 is the other flaky test seen in this job (the 1 s signed-URL expiry). It's not touched here.

🤖 Generated with Claude Code

Fixes the flaky `migration-smoke` failure in [run #238](https://git.zayin.net/Zayin/Ekklesia/actions/runs/294), on PR #99, which only changes `CLAUDE.md`: ``` FAIL apps/api/tests/module-database.integration.ts > … > stops cleanly: … AssertionError: expected 1 to be +0 (module-database.integration.ts:764) ``` **Cause.** After `running.shutdown()`, four tests immediately count the database's rows in `pg_stat_activity` and expect zero. 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. On a loaded shared runner, an immediate count sometimes sees it. Nothing actually leaks. **Reproduced.** Against `postgres:18.4-alpine3.24` limited to 0.05 CPU, the backend was still listed right after `pool.end()` resolved in **7 of 300** trials, and gone about 1 ms later. With no CPU limit: 0 of 300. **Fix (tests only).** A `connectionsLeftAfterShutdown()` helper waits up to 3 s for the count to reach zero. It's used by all four post-shutdown checks: one in `module-database.integration.ts`, three in `api-http.integration.ts`. A real leak is still caught: an idle pooled client stays listed until pg-pool's 10 s idle timeout (`pg-pool` 3.14's default; Core's pools don't override it), well past the 3 s deadline. No production code changes. **Verified.** `pnpm test:migrations:integration` passes (401/401, live PostgreSQL). `pnpm verify` passes (2,639 tests). Related but separate: #97 is the other flaky test seen in this job (the 1 s signed-URL expiry). It's not touched here. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ekklesia-bot added 1 commit 2026-10-14 04:13:33 +00:00
test(api): let a shutdown's closed connections leave pg_stat_activity
CI / verify (pull_request) Successful in 1m16s
CI / infrastructure-smoke (pull_request) Successful in 40s
CI / package-release (pull_request) Successful in 57s
CI / image-build (pull_request) Successful in 1m1s
CI / migration-smoke (pull_request) Successful in 2m30s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / package-publication (pull_request) Successful in 55s
CI / deploy-smoke (split) (pull_request) Successful in 2m41s
f2fc069e66
Four integration tests shut the API down and immediately count the
database's rows in pg_stat_activity, expecting zero. 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. On a
loaded runner an immediate count saw one, failing migration-smoke on a
docs-only PR (Ekklesia CI run #238, module-database.integration.ts:764,
"expected 1 to be +0").

Reproduced against postgres:18.4-alpine3.24 limited to 0.05 CPU: after
pool.end() resolved, the backend was still listed in 7 of 300 trials,
and gone about a millisecond later.

The checks now wait up to 3 s for the count to reach zero. A connection
that is genuinely left open is still caught: an idle pooled client
stays listed for pg-pool's 10 s idle timeout, far past the deadline.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Zayin_Admin added 1 commit 2026-10-14 04:23:12 +00:00
Merge branch 'main' into fix/flaky-shutdown-connection-check
CI / verify (pull_request) Successful in 1m14s
CI / infrastructure-smoke (pull_request) Successful in 40s
CI / package-release (pull_request) Successful in 50s
CI / migration-smoke (pull_request) Successful in 2m24s
CI / image-build (pull_request) Successful in 1m2s
CI / deploy-smoke (same-origin) (pull_request) Successful in 45s
CI / package-publication (pull_request) Successful in 58s
CI / deploy-smoke (split) (pull_request) Successful in 2m36s
bef516796e
Zayin_Admin merged commit 44430b5d59 into main 2026-10-14 04:41:38 +00:00
Zayin_Admin deleted branch fix/flaky-shutdown-connection-check 2026-10-14 04:41:38 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Zayin/Ekklesia#100