fix: advisory lock in initSchema() prevents deadlock on concurrent DDL
When multiple processes call initSchema() concurrently (e.g., test setup + CLI subprocess, or parallel workers during E2E tests), the schema SQL's DROP TRIGGER + CREATE TRIGGER statements acquire AccessExclusiveLock on different tables, causing deadlocks. Fix: pg_advisory_lock(42) serializes all initSchema() calls within the same database. The lock is session-scoped and released in a finally block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,13 @@ export async function disconnect(): Promise<void> {
|
|||||||
|
|
||||||
export async function initSchema(): Promise<void> {
|
export async function initSchema(): Promise<void> {
|
||||||
const conn = getConnection();
|
const conn = getConnection();
|
||||||
await conn.unsafe(SCHEMA_SQL);
|
// Advisory lock prevents concurrent initSchema() calls from deadlocking
|
||||||
|
await conn`SELECT pg_advisory_lock(42)`;
|
||||||
|
try {
|
||||||
|
await conn.unsafe(SCHEMA_SQL);
|
||||||
|
} finally {
|
||||||
|
await conn`SELECT pg_advisory_unlock(42)`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function withTransaction<T>(fn: (tx: ReturnType<typeof postgres>) => Promise<T>): Promise<T> {
|
export async function withTransaction<T>(fn: (tx: ReturnType<typeof postgres>) => Promise<T>): Promise<T> {
|
||||||
|
|||||||
@@ -57,12 +57,19 @@ export class PostgresEngine implements BrainEngine {
|
|||||||
|
|
||||||
async initSchema(): Promise<void> {
|
async initSchema(): Promise<void> {
|
||||||
const conn = this.sql;
|
const conn = this.sql;
|
||||||
await conn.unsafe(SCHEMA_SQL);
|
// Advisory lock prevents concurrent initSchema() calls from deadlocking
|
||||||
|
// on DDL statements (DROP TRIGGER + CREATE TRIGGER acquire AccessExclusiveLock)
|
||||||
|
await conn`SELECT pg_advisory_lock(42)`;
|
||||||
|
try {
|
||||||
|
await conn.unsafe(SCHEMA_SQL);
|
||||||
|
|
||||||
// Run any pending migrations automatically
|
// Run any pending migrations automatically
|
||||||
const { applied } = await runMigrations(this);
|
const { applied } = await runMigrations(this);
|
||||||
if (applied > 0) {
|
if (applied > 0) {
|
||||||
console.log(` ${applied} migration(s) applied`);
|
console.log(` ${applied} migration(s) applied`);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await conn`SELECT pg_advisory_unlock(42)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user