Skip to content

Commit 1e1c591

Browse files
authored
fix(drizzle): surface connection errors in beginTransaction instead of hanging forever (#16220)
When `beginTransaction` fails to establish a database connection (e.g. due to network issues or expired AWS IAM tokens), the function hangs forever instead of throwing an error. This happens because `drizzle.transaction()` rejects when the connection fails, but the old `.catch(() => { // swallow })` silently ate the error. Since the transaction callback never ran, `transactionReady()` was never called, leaving the outer `await` permanently suspended. Callers never receive an error and the operation never completes. The fix replaces the swallowed `.catch` with one that rejects the outer promise, so the existing `try/catch` block can log the real error and re-throw it to callers. ## What happened in practice In practice, this issue resulted in the job queue to randomly stop processing jobs, if a previous cron hangs during `beginTransaction`. The only way to get it to start processing jobs again was to restart the node process. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1213983836266037
1 parent c5e0e02 commit 1e1c591

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

packages/drizzle/src/transactions/beginTransaction.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
1717
let transaction: DrizzleTransaction
1818

1919
let transactionReady: () => void
20+
let transactionFailed: (err: unknown) => void
2021

2122
// Await initialization here
2223
// Prevent race conditions where the adapter may be
@@ -44,13 +45,17 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
4445
transactionReady()
4546
})
4647
}, options || this.transactionOptions)
47-
.catch(() => {
48-
// swallow
48+
.catch((err) => {
49+
// Connection failed before callback ran - reject instead of hanging forever
50+
transactionFailed(err)
4951
})
5052

5153
// Need to wait until the transaction is ready
5254
// before binding its `resolve` and `reject` methods below
53-
await new Promise<void>((resolve) => (transactionReady = resolve))
55+
await new Promise<void>((res, rej) => {
56+
transactionReady = res
57+
transactionFailed = rej
58+
})
5459

5560
this.sessions[id] = {
5661
db: transaction,

test/database/int.spec.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2321,6 +2321,49 @@ describe('database', () => {
23212321
})
23222322
}
23232323

2324+
it(
2325+
'should throw error when beginTransaction fails to connect (drizzle)',
2326+
{ db: (adapter) => adapter.startsWith('postgres') || adapter === 'supabase' },
2327+
async () => {
2328+
const db = payload.db as unknown as Record<string, unknown>
2329+
const originalDrizzle = db.drizzle
2330+
try {
2331+
db.drizzle = {
2332+
transaction: () => Promise.reject(new Error('connection refused')),
2333+
}
2334+
2335+
await expect(() => payload.db.beginTransaction()).rejects.toThrow(/connection refused/)
2336+
} finally {
2337+
db.drizzle = originalDrizzle
2338+
}
2339+
},
2340+
)
2341+
2342+
it(
2343+
'should throw error when beginTransaction fails to connect (mongo)',
2344+
{
2345+
db: (adapter) =>
2346+
adapter === 'mongodb' || adapter === 'mongodb-atlas' || adapter === 'documentdb',
2347+
},
2348+
async () => {
2349+
const db = payload.db as unknown as Record<string, unknown>
2350+
const originalConnection = db.connection
2351+
try {
2352+
db.connection = {
2353+
getClient: () => ({
2354+
startSession: () => {
2355+
throw new Error('connection refused')
2356+
},
2357+
}),
2358+
}
2359+
2360+
await expect(() => payload.db.beginTransaction()).rejects.toThrow(/connection refused/)
2361+
} finally {
2362+
db.connection = originalConnection
2363+
}
2364+
},
2365+
)
2366+
23242367
describe('disableTransaction', () => {
23252368
let disabledTransactionPost
23262369
beforeAll(async () => {

0 commit comments

Comments
 (0)