Skip to content

Commit 0857c8a

Browse files
committed
fix(db): record superseded migrations after successful upgrades
1 parent 5ff4ab2 commit 0857c8a

4 files changed

Lines changed: 29 additions & 5 deletions

File tree

packages/db/script-migrations/0016_backfill_search_vectors.postgres.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,30 @@ describe.runIf(Boolean(databaseUrl))('search projection upgrade in PostgreSQL',
330330
FROM generate_series(1, 1001) n`)
331331
await sql`DELETE FROM embedding_search WHERE id > 'upgrade-0501' AND id LIKE 'upgrade-%'`
332332
await sql`DELETE FROM embedding_keyword_search WHERE id LIKE 'upgrade-%'`
333+
await sql.unsafe(`CREATE FUNCTION cancel_projection_upgrade() RETURNS trigger LANGUAGE plpgsql AS $$
334+
BEGIN
335+
IF NEW.id = 'upgrade-0750' THEN
336+
RAISE EXCEPTION 'Synthetic upgrade cancellation' USING ERRCODE = '57014';
337+
END IF;
338+
RETURN NEW;
339+
END;
340+
$$`)
341+
await sql.unsafe(`CREATE TRIGGER cancel_projection_upgrade BEFORE INSERT OR UPDATE ON embedding_search
342+
FOR EACH ROW EXECUTE FUNCTION cancel_projection_upgrade()`)
343+
try {
344+
await expect(runScriptMigrations(sql)).rejects.toMatchObject({ code: '57014' })
345+
expect(await sql`SELECT name FROM script_migrations WHERE name >= '0015'`).toHaveLength(0)
346+
} finally {
347+
await sql.unsafe('DROP TRIGGER cancel_projection_upgrade ON embedding_search')
348+
await sql.unsafe('DROP FUNCTION cancel_projection_upgrade()')
349+
}
333350
await runScriptMigrations(sql)
334351
expect(
335352
await sql`SELECT name FROM script_migrations WHERE name >= '0015' ORDER BY name`
336-
).toEqual([{ name: '0016_backfill_search_vectors' }])
353+
).toEqual([
354+
{ name: '0015_backfill_embedding_search' },
355+
{ name: '0016_backfill_search_vectors' },
356+
])
337357
const [{ complete }] = await sql`SELECT count(*)::int AS complete FROM embedding e
338358
JOIN embedding_search s ON s.id = e.id JOIN embedding_keyword_search k ON k.id = e.id
339359
WHERE e.id LIKE 'upgrade-%' AND s."binary" = binary_quantize(e.embedding)::bit(1536)

packages/db/script-migrations/0016_backfill_search_vectors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ export async function buildSearchIndexes(sql: Sql): Promise<void> {
231231

232232
export const backfillSearchVectorsMigration: ScriptMigration = {
233233
name: '0016_backfill_search_vectors',
234+
supersedes: ['0015_backfill_embedding_search'],
234235
async up(sql) {
235236
const rows = await backfillSearchVectors(sql)
236237
const keywordRows = await backfillSearchKeywords(sql)

packages/db/script-migrations/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,11 @@ export async function runScriptMigrations(sql: Sql): Promise<void> {
9999
console.log(`Applying script migration ${migration.name}...`)
100100
const startedAt = Date.now()
101101
await migration.up(sql)
102-
await sql`
103-
INSERT INTO script_migrations (name) VALUES (${migration.name})
104-
ON CONFLICT (name) DO NOTHING
105-
`
102+
await sql.begin(async (tx) => {
103+
for (const name of [migration.name, ...(migration.supersedes ?? [])]) {
104+
await tx`INSERT INTO script_migrations (name) VALUES (${name}) ON CONFLICT (name) DO NOTHING`
105+
}
106+
})
106107
console.log(`Script migration ${migration.name} applied in ${Date.now() - startedAt}ms.`)
107108
}
108109
}

packages/db/script-migrations/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import type { Sql } from 'postgres'
2424
export interface ScriptMigration {
2525
/** Unique stable identifier recorded in `script_migrations`; never rename after release. */
2626
name: string
27+
/** Earlier, unregistered migrations whose work this migration fully completes. Recorded only on success. */
28+
supersedes?: readonly string[]
2729
/** Env vars the migration needs; the runner throws before `up` if any is unset. */
2830
requiredEnv?: readonly string[]
2931
/**

0 commit comments

Comments
 (0)