Skip to content

Commit 85482d2

Browse files
author
ald_productions
committed
fix(migration): Ensure that only ONE server can attempt to apply migrations at a time
Resolves a race condition in which non-bundled environments can attempt to run migrations simultaniously. When this occurs, the migrations can break.
1 parent 12ac900 commit 85482d2

1 file changed

Lines changed: 10 additions & 4 deletions

File tree

src/database/Database.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ if (!process.env.DATABASE && !isHeadlessProcess) {
4747
const dbConnectionString = process.env.DATABASE!;
4848
export const DatabaseType = isHeadlessProcess ? "postgres" : dbConnectionString.split(":")[0]?.replace("+srv", "");
4949
const applyMigrations = process.env.APPLY_DB_MIGRATIONS !== "false";
50-
50+
const MIGRATIONLOCK = 1;
5151
export const DataSourceOptions = isHeadlessProcess
5252
? (undefined as unknown as DataSource)
5353
: new DataSource({
@@ -109,19 +109,25 @@ export async function initDatabase(): Promise<DataSource> {
109109
}
110110
};
111111
if (applyMigrations) {
112+
const qr = dbConnection.createQueryRunner();
113+
/*
114+
The transaction lock ensures that exactly one server is attempting to run migrations at a time.
115+
It is session-specific, so should be released if a crash occurs. It is also blocking, so all
116+
servers can run their logic.
117+
*/
118+
await qr.query(`Select pg_advisory_lock(${MIGRATIONLOCK})`);
112119
if (!(await dbExists())) {
113120
console.log("[Database] This appears to be a fresh database. Running initial DDL.");
114-
const qr = dbConnection.createQueryRunner();
115121
const initialPath = path.join(__dirname, "migration", DatabaseType + "-initial.js");
116122
if (fs.existsSync(initialPath)) {
117123
console.log("[Database] Found initial migration file, running it.");
118124
await new (require(`./migration/${DatabaseType}-initial`).initial0)().up(qr);
119125
} else console.log("[Database] No initial migration file found at '", initialPath, "', skipping.");
120-
await qr.release();
121126
}
122-
123127
console.log("[Database] Applying missing migrations, if any.", process.env.APPLY_DB_MIGRATIONS);
124128
await dbConnection.runMigrations();
129+
await qr.query(`Select pg_advisory_unlock(${MIGRATIONLOCK})`);
130+
await qr.release();
125131
} else {
126132
console.log("[Database] Skipping migrations as per config.");
127133
while (!(await dbExists())) {

0 commit comments

Comments
 (0)