@abgov/nx-adsp 13.7.2 → 13.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -105,6 +105,11 @@ describe('Express Service Generator', () => {
|
|
|
105
105
|
expect(host.exists('apps/test/src/db/schema.ts')).toBeTruthy();
|
|
106
106
|
expect(host.exists('apps/test/src/database.ts')).toBeTruthy();
|
|
107
107
|
expect(host.exists('apps/test/src/migrate.ts')).toBeTruthy();
|
|
108
|
+
const migrateTs = host.read('apps/test/src/migrate.ts').toString();
|
|
109
|
+
// Guards against concurrent replicas racing drizzle-orm's migrate(), which has
|
|
110
|
+
// no built-in protection (drizzle-team/drizzle-orm#874).
|
|
111
|
+
expect(migrateTs).toContain('pg_advisory_lock');
|
|
112
|
+
expect(migrateTs).toContain('pg_advisory_unlock');
|
|
108
113
|
expect(host.exists('apps/test/drizzle.config.ts')).toBeTruthy();
|
|
109
114
|
expect(host.exists('apps/test/scripts/dev-db.sh')).toBeTruthy();
|
|
110
115
|
expect(host.exists('apps/test/.env.example')).toBeTruthy();
|
|
@@ -4,6 +4,13 @@ import { migrate } from 'drizzle-orm/node-postgres/migrator';
|
|
|
4
4
|
import { Pool } from 'pg';
|
|
5
5
|
|
|
6
6
|
const MIGRATIONS_FOLDER = 'drizzle';
|
|
7
|
+
// Arbitrary, stable key — advisory locks are scoped per-database, not shared
|
|
8
|
+
// across a Postgres instance (confirmed via the pg_locks documentation: "the
|
|
9
|
+
// same advisory lock key can be held simultaneously in different databases
|
|
10
|
+
// ... they don't conflict across DB boundaries"), so this only needs to be
|
|
11
|
+
// unique within this app's own database, not across every app sharing a
|
|
12
|
+
// sandbox Postgres instance.
|
|
13
|
+
const MIGRATION_LOCK_KEY = 8812345;
|
|
7
14
|
|
|
8
15
|
// Standalone migration runner — the deployment runs this as an init container
|
|
9
16
|
// (`node migrate.js`) before the app starts. It uses only drizzle-orm + pg,
|
|
@@ -25,11 +32,24 @@ async function main() {
|
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
const pool = new Pool({ connectionString });
|
|
35
|
+
// drizzle-orm's migrate() has no protection against concurrent execution
|
|
36
|
+
// (github.com/drizzle-team/drizzle-orm/issues/874, open, acknowledged by
|
|
37
|
+
// its maintainers) — it reads the last-applied migration with a plain
|
|
38
|
+
// SELECT before opening a transaction, so two replicas starting at once can
|
|
39
|
+
// both see "nothing applied yet" and both try to run the same migration.
|
|
40
|
+
// A session-scoped advisory lock on a dedicated connection serializes
|
|
41
|
+
// concurrent runs of this script: whichever loses blocks here until the
|
|
42
|
+
// winner releases the lock, then proceeds against an already-migrated
|
|
43
|
+
// database — a safe no-op, since __drizzle_migrations already records it.
|
|
44
|
+
const lockClient = await pool.connect();
|
|
28
45
|
try {
|
|
46
|
+
await lockClient.query('SELECT pg_advisory_lock($1)', [MIGRATION_LOCK_KEY]);
|
|
29
47
|
await migrate(drizzle(pool), { migrationsFolder: MIGRATIONS_FOLDER });
|
|
30
48
|
// eslint-disable-next-line no-console
|
|
31
49
|
console.log('Migrations applied.');
|
|
32
50
|
} finally {
|
|
51
|
+
await lockClient.query('SELECT pg_advisory_unlock($1)', [MIGRATION_LOCK_KEY]);
|
|
52
|
+
lockClient.release();
|
|
33
53
|
await pool.end();
|
|
34
54
|
}
|
|
35
55
|
}
|