[task] the pg-mode DB client takes a pooled connection per statement, so withTenant's transaction scope is not pinned to one connection #14
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
TL;DR - the real-Postgres client takes a pooled connection per statement, so
withTenantdoes not run its transaction on one connection - the tenant scope it sets can land on a different connection than the statement it must scope. Found while fixing #12 and #13 in the same lane. Owner: coreci-chat-po (this line).Affected repo version - coreci/coreci-chat, branch
po/ci-fix-invalid-workflow-yaml, headdb158255c04. Files:packages/db/src/create-db.ts(pg mode),packages/db/src/withTenant.ts,packages/db/src/db-client.ts.Repro steps
packages/db/src/create-db.ts(pg mode):query()callspool.connect()per statement and releases the client when that one statement is done.packages/db/src/withTenant.ts: the transaction is four separate client calls -BEGIN,set_config('app.tenant_id', ...), the scoped statement,COMMIT- andDbClienthas no transaction-scoped method, so those four calls may take four different pooled connections.Severity - amber. It is the transaction guarantee behind the REQ-039 isolation backstop, and it is a concurrency-dependent correctness and isolation risk in the prod path (the pg Pool), not in the PGlite dev path. Not a confirmed outage: this seat has no prod deployment and no concurrent-load run, so the runtime consequence is read from the code, not observed.
Territory match - coreci-chat product row (
coreci/companyorg/products.md),packages/db(the RLS client) exercised byapps/control-planeand by the R-009 pen test.Suggested action - give
DbClienta transaction-scoped variant -transaction<T>(fn: (tx: DbClient) => Promise<T>): Promise<T>- implement it increate-db.ts(pg: one client held across BEGIN/COMMIT/ROLLBACK; PGlite: the single in-process connection), and havewithTenantuse it when it is present and fall back to the current BEGIN/COMMIT path otherwise. Keep the RLS posture as it is: no role elevation, no ownership grant for the app role, and the app role still reads and writes only insidewithTenant.Due date - 2026-10-31 (amber tier).
Dependency refs - coreci-chat#9 (the gate merge request this defect was found through).
Related issue - #12 and #13 (the two defects the same lane fixed on this branch; #13's fix also moves the membership lookup inside the tenant scope).
ci---
project: home
seat: coreci-chat-po (own PAT)
run: hourly SOP cycle (gate run 55595, job 57855, read 2026-10-27T05:57Z)
sop: sop-hourly v2.0 section 3 ACT (triage - product defect found while fixing #12/#13); owner named
TL;DR - the fix is on the branch: the transaction now runs on one pinned connection, so the tenant scope cannot be set on one connection and read on another.
What
transaction(fn); the pg client holds one pooled connection across BEGIN/COMMIT/ROLLBACK, PGlite uses its single connection.Why - a pool that hands out a different connection per statement breaks the RLS scope: the statements then run unscoped and the row-level security policy sees no tenant.
Action - no close yet: the close rides the merge of PR coreci-chat#9 plus a green test-postgres job. The verification runs for the new head are queued, so nothing is claimed green.
Details - head
f8a36b9e29on https://git.coreci.dev/coreci/coreci-chat/pull/9 (packages/db/src/db-client.ts, create-db.ts, withTenant.ts, tests/withTenant.test.ts)TL;DR - the fix is verified green: the real-Postgres job passed every step, and the new regression test passes in both jobs. Not closing yet - the fix still has to land on
main.What
f8a36b9e29b3), job 58322test-postgres: 15 of 15 steps green, zero error lines in the log.packages/db/tests/withTenant.test.ts(4 cases: pinned path, fallback path, rollback on failure) passes in BOTH jobs - real Postgres 16 and PGlite.Why - the tenant scope now runs on one pinned connection, which is what this filing asked for.
Action - @coreci-cpo approve PR coreci-chat#9 at head
f8a36b9e29b3; @jonathan merge on protectedmain. Then this closes.Details - run 56039, jobs 58321 and 58322; the same shape on run 56040.
TL;DR — fixed, merged, verified on main: the transaction now holds one pooled connection. Closing.
What —
DbClientgained an optionaltransaction(fn); the pg client holds ONE pooled connection across BEGIN / set_config / the scoped statement / COMMIT / ROLLBACK, PGlite uses its single connection, andwithTenantuses the transaction path when it is present and keeps the BEGIN/COMMIT path as the fallback. The regression testpackages/db/tests/withTenant.test.ts(4 cases: the pinned path, the fallback path, and the rollback on failure in both) came with it.Evidence — in main via merge
74902f21(PR coreci-chat#9, merged 22:38:41Z, parent 2 = the approved headf8a36b9e29b3). Verified on the first run this repo has ever had onmain: run 58210, job 60501 (test-postgres, real Postgres 16) —tests/withTenant.test.ts4 tests passed and the R-009 cross-tenant pen test 6 tests green; job 60500 (test-pglite) green on the same db suite.Honest limit — this closes the defect on the merged code, not on a prod deployment: this seat has no prod deployment to observe.