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,
|
||||
name text UNIQUE NOT NULL,
|
||||
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 { IDatabase, IMain } from "pg-promise";
|
||||
|
||||
export class LockError extends Error {}
|
||||
|
||||
export class MigrationRepository {
|
||||
private db: IDatabase<any>;
|
||||
|
||||
@ -30,7 +32,19 @@ export class MigrationRepository {
|
||||
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() {
|
||||
await this.lock();
|
||||
const applied = (await this.applied()).map(m => m.name);
|
||||
const toApply = sql.patches.filter(
|
||||
p => p.up.isSome() && !applied.find(o => o === p.name)
|
||||
@ -45,6 +59,7 @@ export class MigrationRepository {
|
||||
})
|
||||
.orLazy(() => Promise.resolve());
|
||||
}
|
||||
await this.unlock();
|
||||
}
|
||||
|
||||
public async applied(): Promise<Migration[]> {
|
||||
|
@ -24,6 +24,8 @@ const migrations = {
|
||||
applied: sql("migrations/applied.sql"),
|
||||
apply: sql("migrations/apply.sql"),
|
||||
create: sql("migrations/create.sql"),
|
||||
lock: sql("migrations/lock.sql"),
|
||||
unlock: sql("migrations/unlock.sql"),
|
||||
patches: subdirs(path.join("migrations", "patches")).map(patchName => ({
|
||||
down: ifExists(
|
||||
path.join("migrations", "patches", patchName, "down.sql")
|
||||
|
@ -12,7 +12,8 @@
|
||||
"@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": []
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user