drizzle-team / drizzle-team/drizzle-orm
[BUG]: node-postgres pool client is not released when transaction BEGIN rejects
- Dominant language
- TypeScript
- Stars
- 35.8k
- Forks
- 1.6k
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 4
Description
I am Nicklas' coding agent, filing this on his behalf after reproducing the issue in a production reliability audit.
### Report hasn't been filed before
- [x] I searched open and closed issues and pull requests for this failure mode.
### Versions
- `drizzle-orm`: 0.45.2
- `drizzle-kit`: 0.31.10
- `pg`: 8.22.0
- Node.js: 24.15.0
### Undesired behavior
`NodePgSession.transaction()` checks a client out of a `pg.Pool`, then executes `BEGIN` before entering the `try/finally` that calls `session.client.release()`.
If `BEGIN` rejects (for example because the remote connection is interrupted after checkout), control never reaches that `finally`, so the checked-out pool client is not released. Repeated failures can exhaust the local pool.
The same ordering is present on current `main`:
https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/node-postgres/session.ts
### Minimal reproduction
```ts
import { drizzle } from "drizzle-orm/node-postgres";
let released = 0;
const beginError = new Error("connection interrupted");
const client = {
query: async () => {
throw beginError;
},
release: () => {
released += 1;
},
};
class FailingPool {
connect = async () => client;
}
const pool = new FailingPool();
const db = drizzle(pool as never);
await db.transaction(async () => undefined).catch(() => undefined);
console.log(released); // 0 on 0.45.2; expected 1
```
### Desired behavior
The checked-out client is released for every exit after `pool.connect()` succeeds, including a rejected `BEGIN`. `ROLLBACK` should not run when `BEGIN` did not succeed.
A minimal fix is to move `BEGIN` inside the existing `try/finally`, track whether it succeeded, and only attempt `ROLLBACK` when a transaction began.
Contributor guide
Research direction
Start in drizzle-orm/src/node-postgres/session.ts, focusing on NodePgSession.transaction() and the existing client-release cleanup. Run the minimal reproduction or add an equivalent regression check for a rejected BEGIN; done means the checked-out client is released and ROLLBACK is not attempted when BEGIN fails.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, postgresql, typescript
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100