@secondlayer/shared 11.12.4 → 11.12.5
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.
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
import { onControlPlane } from "../src/db/migration-role.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `webhook_deliveries.webhook_id` has carried no FK since the table was
|
|
6
|
+
* created (0057, as `subscription_deliveries`) — only `outbox_id` got one
|
|
7
|
+
* (0077, `ON DELETE SET NULL`, kept on purpose: delivery history outlives
|
|
8
|
+
* outbox compaction). Deleting a webhook left its delivery history orphaned
|
|
9
|
+
* (1,186 rows had built up in the CI-mirror DB); the dashboard/API never
|
|
10
|
+
* show them, so they just sat there until the 30-day retention sweep.
|
|
11
|
+
*
|
|
12
|
+
* `deliveries_sub_idx (webhook_id, dispatched_at DESC)` already covers the
|
|
13
|
+
* cascade lookup — it predates this migration (0057) and survived the
|
|
14
|
+
* 0126 rename under its old name, so no new index is needed here.
|
|
15
|
+
*
|
|
16
|
+
* `webhook_deliveries` lives on the TARGET (control-plane) DB alongside
|
|
17
|
+
* `webhooks`, like `webhook_outbox` — gate with `onControlPlane`, same as
|
|
18
|
+
* 0139.
|
|
19
|
+
*
|
|
20
|
+
* NOT VALID + a separate VALIDATE keeps the exclusive lock short on a big
|
|
21
|
+
* table: NOT VALID only takes the fast lock to add the constraint (new rows
|
|
22
|
+
* are checked immediately), then VALIDATE scans existing rows without
|
|
23
|
+
* blocking writers the whole time.
|
|
24
|
+
*/
|
|
25
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
26
|
+
await sql`SET lock_timeout = '30s'`.execute(db);
|
|
27
|
+
await onControlPlane(async () => {
|
|
28
|
+
const existing = await sql<{ exists: boolean }>`
|
|
29
|
+
SELECT EXISTS (
|
|
30
|
+
SELECT 1 FROM pg_constraint
|
|
31
|
+
WHERE conname = 'webhook_deliveries_webhook_id_fkey'
|
|
32
|
+
) AS exists
|
|
33
|
+
`.execute(db);
|
|
34
|
+
if (existing.rows[0]?.exists) return;
|
|
35
|
+
|
|
36
|
+
await sql`
|
|
37
|
+
DELETE FROM webhook_deliveries d
|
|
38
|
+
WHERE NOT EXISTS (SELECT 1 FROM webhooks w WHERE w.id = d.webhook_id)
|
|
39
|
+
`.execute(db);
|
|
40
|
+
|
|
41
|
+
await sql`
|
|
42
|
+
ALTER TABLE webhook_deliveries
|
|
43
|
+
ADD CONSTRAINT webhook_deliveries_webhook_id_fkey
|
|
44
|
+
FOREIGN KEY (webhook_id) REFERENCES webhooks(id) ON DELETE CASCADE
|
|
45
|
+
NOT VALID
|
|
46
|
+
`.execute(db);
|
|
47
|
+
|
|
48
|
+
await sql`
|
|
49
|
+
ALTER TABLE webhook_deliveries
|
|
50
|
+
VALIDATE CONSTRAINT webhook_deliveries_webhook_id_fkey
|
|
51
|
+
`.execute(db);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
56
|
+
await sql`SET lock_timeout = '30s'`.execute(db);
|
|
57
|
+
await onControlPlane(async () => {
|
|
58
|
+
// Deleted orphans are not restored.
|
|
59
|
+
await sql`
|
|
60
|
+
ALTER TABLE webhook_deliveries
|
|
61
|
+
DROP CONSTRAINT IF EXISTS webhook_deliveries_webhook_id_fkey
|
|
62
|
+
`.execute(db);
|
|
63
|
+
});
|
|
64
|
+
}
|