Better migration locking behaviour
This commit is contained in:
parent
5aced1b8b1
commit
6ab4c1e4d3
@ -2,4 +2,12 @@ CREATE TABLE IF NOT EXISTS migrations (
|
|||||||
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
name text UNIQUE NOT NULL,
|
name text UNIQUE NOT NULL,
|
||||||
applied_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
|
applied_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS migrations_lock (
|
||||||
|
id integer PRIMARY KEY,
|
||||||
|
is_locked boolean NOT NULL DEFAULT false
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO migrations_lock (id, is_locked) VALUES (1, false)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
3
sql/migrations/lock.sql
Normal file
3
sql/migrations/lock.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
WITH rows as (
|
||||||
|
UPDATE migrations_lock SET is_locked = true WHERE is_locked = false RETURNING 1
|
||||||
|
) SELECT COUNT(*) FROM rows;
|
1
sql/migrations/unlock.sql
Normal file
1
sql/migrations/unlock.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
UPDATE migrations_lock SET is_locked = false WHERE is_locked = true;
|
@ -19,6 +19,8 @@ import logger from "@kredens/logger";
|
|||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
import { IDatabase, IMain } from "pg-promise";
|
import { IDatabase, IMain } from "pg-promise";
|
||||||
|
|
||||||
|
export class LockError extends Error {}
|
||||||
|
|
||||||
export class MigrationRepository {
|
export class MigrationRepository {
|
||||||
private db: IDatabase<any>;
|
private db: IDatabase<any>;
|
||||||
|
|
||||||
@ -30,7 +32,19 @@ export class MigrationRepository {
|
|||||||
await this.db.none(sql.create);
|
await this.db.none(sql.create);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async lock() {
|
||||||
|
const count = await this.db.one(sql.lock);
|
||||||
|
if (+count.count !== 1) {
|
||||||
|
throw new LockError("Failed to acquire migration lock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async unlock() {
|
||||||
|
return this.db.none(sql.unlock);
|
||||||
|
}
|
||||||
|
|
||||||
public async apply() {
|
public async apply() {
|
||||||
|
await this.lock();
|
||||||
const applied = (await this.applied()).map(m => m.name);
|
const applied = (await this.applied()).map(m => m.name);
|
||||||
const toApply = sql.patches.filter(
|
const toApply = sql.patches.filter(
|
||||||
p => p.up.isSome() && !applied.find(o => o === p.name)
|
p => p.up.isSome() && !applied.find(o => o === p.name)
|
||||||
@ -45,6 +59,7 @@ export class MigrationRepository {
|
|||||||
})
|
})
|
||||||
.orLazy(() => Promise.resolve());
|
.orLazy(() => Promise.resolve());
|
||||||
}
|
}
|
||||||
|
await this.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async applied(): Promise<Migration[]> {
|
public async applied(): Promise<Migration[]> {
|
||||||
|
@ -24,6 +24,8 @@ const migrations = {
|
|||||||
applied: sql("migrations/applied.sql"),
|
applied: sql("migrations/applied.sql"),
|
||||||
apply: sql("migrations/apply.sql"),
|
apply: sql("migrations/apply.sql"),
|
||||||
create: sql("migrations/create.sql"),
|
create: sql("migrations/create.sql"),
|
||||||
|
lock: sql("migrations/lock.sql"),
|
||||||
|
unlock: sql("migrations/unlock.sql"),
|
||||||
patches: subdirs(path.join("migrations", "patches")).map(patchName => ({
|
patches: subdirs(path.join("migrations", "patches")).map(patchName => ({
|
||||||
down: ifExists(
|
down: ifExists(
|
||||||
path.join("migrations", "patches", patchName, "down.sql")
|
path.join("migrations", "patches", patchName, "down.sql")
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
"@kredens"
|
"@kredens"
|
||||||
],
|
],
|
||||||
"no-implicit-dependencies": [true, ["@kredens"]],
|
"no-implicit-dependencies": [true, ["@kredens"]],
|
||||||
"object-literal-sort-keys": [true, "match-declaration-order-only", "shorthand-first"]
|
"object-literal-sort-keys": [true, "match-declaration-order-only", "shorthand-first"],
|
||||||
|
"max-classes-per-file": false
|
||||||
},
|
},
|
||||||
"rulesDirectory": []
|
"rulesDirectory": []
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user