[Pg]: Fix unawaited promise chains in queryWithCache and bun-sql execute#5465
[Pg]: Fix unawaited promise chains in queryWithCache and bun-sql execute#5465nathanschram wants to merge 1 commit intodrizzle-team:betafrom
Conversation
…he and bun-sql execute
Three fixes for unawaited promise chains that cause race conditions
with Bun's event loop in standalone scripts:
1. queryWithCache skip path: `return query().catch(...)` creates an
unawaited promise chain. Changed to `try { return await query() }`.
This matches the main branch's implementation in pg-core/session.ts.
2. queryWithCache invalidate path: `return Promise.all([...]).then().catch()`
similarly creates an unawaited chain. Changed to properly await
Promise.all and destructure the result.
3. BunSQLPreparedQuery.execute() Path B: the async callback passed to
queryWithCache returns `client.unsafe().values()` without `await`.
Since Bun SQL queries are lazy (they extend Promise but defer
execution until .then() is called), the missing await means
I/O registration is deferred to a microtask. Added `await` to
match Path A which already uses `return await client.unsafe(...)`.
Fixes drizzle-team#5451
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Heads up on a related finding from further testing on #5451. The reporter tested these patches and they work on warm start (pre-seeded DB) but fail on cold start (empty DB where an INSERT happens before the SELECT). Digging into the compiled npm package ( this.cache !== void 0 || is(this.cache, NoopCache)This always evaluates true for NoopCache, routing all queries through The await fixes in this PR are still correct and needed (the unawaited chains in skip/invalidate/bun-sql are real bugs), but on the published package they're insufficient alone because queries never reach the skip path due to the stale condition. When this PR merges against the current beta HEAD, the condition fix should already be included since the source has it. But worth verifying the compiled output to make sure it doesn't regress - the mismatch between Docker reproduction: PR patches only = cold-start fail. PR patches + condition fix = 4/4 cold-start pass. |
Summary
Fixes unawaited promise chains in
PgAsyncPreparedQuery.queryWithCache()andBunSQLPreparedQuery.execute()that cause race conditions with Bun's event loop, leading to silent process exits (code 0) in standalone scripts.Fixes #5451
Changes
1.
queryWithCacheskip path (pg-core/async/session.ts)Before:
return query().catch(...)- returns an unawaited promise chain from anasyncmethod.After:
try { return await query(); } catch (e) { ... }- properly awaits the query callback.This matches the main branch's implementation in
pg-core/session.tswhich already usestry { return await query(); }.2.
queryWithCacheinvalidate path (pg-core/async/session.ts)Before:
return Promise.all([query(), cache.onMutate(...)]).then((res) => res[0]).catch(...)- returns an unawaited.then().catch()chain.After:
const [result] = await Promise.all([...]); return result;withtry/catch- properly awaits the Promise.all result.3.
BunSQLPreparedQuery.execute()Path B (bun-sql/postgres/session.ts)Before: The async callback passed to
queryWithCachereturnsclient.unsafe(query, params).values()withoutawait.After:
return await client.unsafe(query, params).values()- explicit await on the lazy Bun SQL query.This matches Path A in the same method, which already uses
return await client.unsafe(query, params). Bun SQL queries are lazy - they extendPromisebut defer execution until.then()is called. Withoutawait, the I/O registration is deferred to a microtask, creating a window where Bun's event loop can exit before the query starts.Root cause analysis
The reporter's script runs
INSERTfollowed bySELECTusing the Core Query Builder (db.select().from()). TheSELECTgoes throughqueryWithCache's skip path, which returnedquery().catch(...)withoutawait. Inside the callback,client.unsafe().values()also lackedawait. The combination of unawaited promise chains creates microtask boundaries where Bun's event loop sees no pending I/O and exits cleanly with code 0.Other APIs (
db.execute(sql),db.query.*.findMany(),db.$count()) work because they either bypassqueryWithCacheentirely (executeRqbV2) or use Path A which already has explicitawait.Testing
Reproduced and verified in Docker (Bun 1.3.10, PostgreSQL 18) using the reporter's MRE:
return await query()): 8/8 passbun:sqlwithout Drizzle: 8/8 pass (confirms bug is in Drizzle, not Bun)