Skip to content

Commit ea72543

Browse files
committed
fix(migrate-ts): runner — no client leak if advisory-lock acquire throws; correct applied_at cast
1 parent 97c2a7b commit ea72543

1 file changed

Lines changed: 13 additions & 5 deletions

File tree

server/typescript/packages/migrate-ts/src/runner/pg-history-store.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class PgHistoryStore implements HistoryStore {
5555
version: String(row["version"]),
5656
name: String(row["name"]),
5757
checksum: String(row["checksum"]),
58-
appliedAt: new Date(row["applied_at"] as string).toISOString(),
58+
appliedAt: new Date(row["applied_at"] as Date | string).toISOString(),
5959
executionMs: Number(row["execution_ms"]),
6060
success: Boolean(row["success"]),
6161
}));
@@ -78,10 +78,18 @@ export class PgHistoryStore implements HistoryStore {
7878
}
7979

8080
async acquireLock(): Promise<void> {
81-
this.lockClient = await this.pool.connect();
82-
// Session-level advisory lock (not transaction-level) so CREATE INDEX
83-
// CONCURRENTLY in a migration does not deadlock against the lock.
84-
await this.lockClient.query("SELECT pg_advisory_lock($1::bigint)", [this.lockKey]);
81+
const client = await this.pool.connect();
82+
try {
83+
// Session-level advisory lock (not transaction-level) so CREATE INDEX
84+
// CONCURRENTLY in a migration does not deadlock against the lock.
85+
await client.query("SELECT pg_advisory_lock($1::bigint)", [this.lockKey]);
86+
// Only adopt the client once the lock is actually held — so lockClient
87+
// is non-null iff the lock is acquired, and a failed acquire does not leak.
88+
this.lockClient = client;
89+
} catch (e) {
90+
client.release();
91+
throw e;
92+
}
8593
}
8694

8795
async releaseLock(): Promise<void> {

0 commit comments

Comments
 (0)