@sixb/pg 0.1.18 → 0.1.19

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.
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAGV,YAAY,EACZ,aAAa,EACb,oBAAoB,EACrB,MAAM,oBAAoB,CAAA;AAqH3B,OAAO,KAAK,EAAe,GAAG,EAAa,MAAM,aAAa,CAAA;AAG9D,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC;AAED,eAAO,MAAM,2BAA2B,wBAAwB,CAAA;AAEhE,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,wBAAwB,CAAC,CAE1F;AAED,wBAAgB,MAAM,CACpB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC/D,OAAO,GAAE,oBAAyB,GACjC,aAAa,CAAC,wBAAwB,CAAC,CAEzC;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,GACjB,SAAS,eAAe,EAAE,CAQ5B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;IACjB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,CAAA;CAC5D,GAAG,eAAe,CAqBlB;AAED,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxF;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAKrD;AAuKD,eAAO,MAAM,yBAAyB,wCAgEpC,CAAA"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAGV,YAAY,EACZ,aAAa,EACb,oBAAoB,EACrB,MAAM,oBAAoB,CAAA;AAqH3B,OAAO,KAAK,EAAe,GAAG,EAAa,MAAM,aAAa,CAAA;AAG9D,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC;AAED,eAAO,MAAM,2BAA2B,wBAAwB,CAAA;AAEhE,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,wBAAwB,CAAC,CAE1F;AAED,wBAAgB,MAAM,CACpB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC/D,OAAO,GAAE,oBAAyB,GACjC,aAAa,CAAC,wBAAwB,CAAC,CAEzC;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,GACjB,SAAS,eAAe,EAAE,CAQ5B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;IACjB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,CAAA;CAC5D,GAAG,eAAe,CAqBlB;AAED,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxF;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAKrD;AAuKD,eAAO,MAAM,yBAAyB,wCAiEpC,CAAA"}
@@ -1,22 +1,15 @@
1
- import type { ActionRunRecord, ActionRunStorage, EnterActionRunPhaseInput, ExecutionStorage, FinishActionRunInput, ListActionRunsInput, ListActionRunsResult, LockActionMaterializationRunInput, QueueActionRunInput, RecordActionEffectsInput, RecordActionWritebackInput, StartActionRunInput } from "@sixb/core/storage";
1
+ import type { ActionRunRecord, ActionRunStorage, ExecutionStorage, ListActionRunsInput, ListActionRunsResult, RecordActionEffectsInput, RecordActionRunInput } from "@sixb/core/storage";
2
2
  import { type PgStoreClient } from "./transactions";
3
3
  export declare class PgActionRunStorage implements ActionRunStorage {
4
4
  private readonly sql;
5
5
  private readonly executions;
6
6
  constructor(sql: PgStoreClient, executions: ExecutionStorage);
7
- lockForMaterialization(input: LockActionMaterializationRunInput): Promise<ActionRunRecord>;
8
- queue(input: QueueActionRunInput): Promise<ActionRunRecord>;
9
- private requeueAfterEnqueueFailure;
10
- start(input: StartActionRunInput): Promise<ActionRunRecord>;
11
- enterPhase(input: EnterActionRunPhaseInput): Promise<ActionRunRecord>;
12
- recordWriteback(input: RecordActionWritebackInput): Promise<ActionRunRecord>;
7
+ record(input: RecordActionRunInput): Promise<ActionRunRecord>;
13
8
  recordEffects(input: RecordActionEffectsInput): Promise<ActionRunRecord>;
14
- finish(input: FinishActionRunInput): Promise<ActionRunRecord>;
15
9
  getById(params: {
16
10
  projectId: string;
17
11
  id: string;
18
12
  }): Promise<ActionRunRecord | null>;
19
13
  list(input: ListActionRunsInput): Promise<ListActionRunsResult>;
20
- private requireRunningRun;
21
14
  }
22
15
  //# sourceMappingURL=pg-action-run-storage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pg-action-run-storage.d.ts","sourceRoot":"","sources":["../src/pg-action-run-storage.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAKV,eAAe,EACf,gBAAgB,EAEhB,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACpB,MAAM,oBAAoB,CAAA;AAI3B,OAAO,EAAE,KAAK,aAAa,EAAoB,MAAM,gBAAgB,CAAA;AAErE,qBAAa,kBAAmB,YAAW,gBAAgB;IAEvD,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,GAAG,EAAE,aAAa,EAClB,UAAU,EAAE,gBAAgB;IAGzC,sBAAsB,CAAC,KAAK,EAAE,iCAAiC,GAAG,OAAO,CAAC,eAAe,CAAC;IAyB1F,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;YAuEnD,0BAA0B;IAyClC,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;IA8B3D,UAAU,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,eAAe,CAAC;IAerE,eAAe,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,eAAe,CAAC;IAqC5E,aAAa,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,eAAe,CAAC;IA+BxE,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqC7D,OAAO,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IASnF,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAkHvD,iBAAiB;CAwBhC"}
1
+ {"version":3,"file":"pg-action-run-storage.d.ts","sourceRoot":"","sources":["../src/pg-action-run-storage.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAIV,eAAe,EAEf,gBAAgB,EAEhB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACrB,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,KAAK,aAAa,EAAoB,MAAM,gBAAgB,CAAA;AAErE,qBAAa,kBAAmB,YAAW,gBAAgB;IAEvD,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,GAAG,EAAE,aAAa,EAClB,UAAU,EAAE,gBAAgB;IAGzC,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAuE7D,aAAa,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,eAAe,CAAC;IA2BxE,OAAO,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IASnF,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAiHtE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sixb/pg",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "PostgreSQL storage provider for Sixb",
5
5
  "keywords": [
6
6
  "sixb",
@@ -40,10 +40,10 @@
40
40
  "postgres": "^3.4.5"
41
41
  },
42
42
  "peerDependencies": {
43
- "@sixb/core": "0.1.18"
43
+ "@sixb/core": "0.1.19"
44
44
  },
45
45
  "devDependencies": {
46
- "@sixb/core": "0.1.18",
46
+ "@sixb/core": "0.1.19",
47
47
  "@types/bun": "latest",
48
48
  "typescript": "^5.7.0"
49
49
  },
@@ -0,0 +1,150 @@
1
+ -- Actions run in the request that asks for them and record their run once, when it ends. No run
2
+ -- is queued, running, or cancelled any more, and every run has started and finished.
3
+
4
+ -- A run left running after its edits committed, as a crash between the commit and the end of the
5
+ -- run left it, succeeded: its edits and their outbox facts are in the commit. A run that stopped
6
+ -- during its effects keeps that phase, and records effects that never ended.
7
+ UPDATE action_runs AS run
8
+ SET
9
+ status = 'succeeded',
10
+ error = NULL,
11
+ finished_at = committed.committed_at,
12
+ phase = CASE WHEN run.phase = 'effects' THEN 'effects' ELSE 'commit' END,
13
+ effects_status = CASE
14
+ WHEN run.phase = 'effects' AND run.effects_status IS NULL THEN 'failed'
15
+ ELSE run.effects_status
16
+ END,
17
+ effects_completed_at = CASE
18
+ WHEN run.phase = 'effects' AND run.effects_status IS NULL THEN now()
19
+ ELSE run.effects_completed_at
20
+ END,
21
+ effects_error = CASE
22
+ WHEN run.phase = 'effects' AND run.effects_status IS NULL THEN jsonb_build_object(
23
+ 'code', 'internal.unexpected',
24
+ 'message', 'Abandoned: Actions now run synchronously; these effects never finished.',
25
+ 'retryable', false,
26
+ 'at', to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
27
+ 'details', jsonb_build_object('actionId', run.action_id, 'runId', run.id, 'phase', 'effects')
28
+ )
29
+ ELSE run.effects_error
30
+ END
31
+ FROM ontology_commits AS committed
32
+ WHERE run.status IN ('queued', 'running')
33
+ AND committed.origin_kind = 'action'
34
+ AND committed.project_id = run.project_id
35
+ AND committed.origin_run_id = run.id;
36
+
37
+ -- Any other run left queued or running never finished, and nothing will resume it.
38
+ UPDATE action_runs
39
+ SET
40
+ status = 'failed',
41
+ finished_at = now(),
42
+ error = jsonb_build_object(
43
+ 'code', 'internal.unexpected',
44
+ 'message', 'Abandoned: Actions now run synchronously; this run never finished.',
45
+ 'retryable', false,
46
+ 'at', to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
47
+ 'details', jsonb_build_object('actionId', action_id, 'runId', id)
48
+ )
49
+ WHERE status IN ('queued', 'running');
50
+
51
+ -- A cancelled run failed: it keeps the failure it recorded.
52
+ UPDATE action_runs SET status = 'failed' WHERE status = 'cancelled';
53
+
54
+ -- Every failed run carries a failure. One recorded before failures were kept explains itself.
55
+ UPDATE action_runs
56
+ SET error = jsonb_build_object(
57
+ 'code', 'internal.unexpected',
58
+ 'message', 'This run failed before Sixb recorded why.',
59
+ 'retryable', false,
60
+ 'at', to_char(COALESCE(finished_at, now()) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
61
+ 'details', jsonb_build_object('actionId', action_id, 'runId', id)
62
+ )
63
+ WHERE status = 'failed' AND error IS NULL;
64
+
65
+ -- The phases `request`, `enqueue`, and `cancelled` are gone: a run that stopped in one of them
66
+ -- never got past validation. A run starts when it was queued at the latest.
67
+ UPDATE action_runs
68
+ SET
69
+ phase = CASE
70
+ WHEN phase IN ('validation', 'writeback', 'edits', 'commit', 'effects') THEN phase
71
+ ELSE 'validation'
72
+ END,
73
+ started_at = COALESCE(started_at, queued_at),
74
+ finished_at = COALESCE(finished_at, now()),
75
+ writeback_completed_at = CASE
76
+ WHEN writeback_status IS NULL THEN NULL
77
+ ELSE COALESCE(writeback_completed_at, finished_at, now())
78
+ END,
79
+ effects_completed_at = CASE
80
+ WHEN effects_status IS NULL THEN NULL
81
+ ELSE COALESCE(effects_completed_at, finished_at, now())
82
+ END
83
+ WHERE phase IS NULL
84
+ OR phase NOT IN ('validation', 'writeback', 'edits', 'commit', 'effects')
85
+ OR started_at IS NULL
86
+ OR finished_at IS NULL
87
+ OR (writeback_status IS NOT NULL AND writeback_completed_at IS NULL)
88
+ OR (effects_status IS NOT NULL AND effects_completed_at IS NULL);
89
+
90
+ -- A failure names the phase its run ended in, and only Action codes: an enqueue failure, which no
91
+ -- run can meet any more, reads as the unexpected error it now is.
92
+ UPDATE action_runs
93
+ SET error = jsonb_set(
94
+ CASE
95
+ WHEN error->>'code' = 'queue.enqueue_failed'
96
+ THEN error || '{"code": "internal.unexpected", "retryable": false}'::jsonb
97
+ ELSE error
98
+ END,
99
+ '{details,phase}',
100
+ to_jsonb(phase)
101
+ )
102
+ WHERE error IS NOT NULL
103
+ AND (
104
+ error->'details'->>'phase' IS DISTINCT FROM phase
105
+ OR error->>'code' = 'queue.enqueue_failed'
106
+ );
107
+
108
+ -- The status and phase checks were unnamed. Drop exactly the two that admit the removed values.
109
+ DO $$
110
+ DECLARE lifecycle_constraint RECORD;
111
+ BEGIN
112
+ FOR lifecycle_constraint IN
113
+ SELECT conname
114
+ FROM pg_constraint
115
+ WHERE conrelid = 'action_runs'::regclass
116
+ AND contype = 'c'
117
+ AND (
118
+ pg_get_constraintdef(oid) LIKE '%''queued''%'
119
+ OR pg_get_constraintdef(oid) LIKE '%''enqueue''%'
120
+ )
121
+ LOOP
122
+ EXECUTE format('ALTER TABLE action_runs DROP CONSTRAINT %I', lifecycle_constraint.conname);
123
+ END LOOP;
124
+ END $$;
125
+
126
+ -- The listing indexes ordered runs by `COALESCE(started_at, queued_at)`.
127
+ DROP INDEX idx_action_runs_project_started;
128
+ DROP INDEX idx_action_runs_project_action_started;
129
+ DROP INDEX idx_action_runs_project_object_started;
130
+ DROP INDEX idx_action_runs_project_status_started;
131
+
132
+ ALTER TABLE action_runs
133
+ DROP COLUMN queued_at,
134
+ ALTER COLUMN phase SET NOT NULL,
135
+ ALTER COLUMN started_at SET NOT NULL,
136
+ ALTER COLUMN finished_at SET NOT NULL,
137
+ ADD CONSTRAINT action_runs_status_check CHECK (status IN ('succeeded', 'failed')),
138
+ ADD CONSTRAINT action_runs_phase_check CHECK (
139
+ phase IN ('validation', 'writeback', 'edits', 'commit', 'effects')
140
+ ),
141
+ ADD CONSTRAINT action_runs_failure_check CHECK ((status = 'failed') = (error IS NOT NULL));
142
+
143
+ CREATE INDEX idx_action_runs_project_started
144
+ ON action_runs (project_id, started_at DESC);
145
+ CREATE INDEX idx_action_runs_project_action_started
146
+ ON action_runs (project_id, action_id, started_at DESC);
147
+ CREATE INDEX idx_action_runs_project_object_started
148
+ ON action_runs (project_id, object_type_id, primary_id, started_at DESC);
149
+ CREATE INDEX idx_action_runs_project_status_started
150
+ ON action_runs (project_id, status, started_at DESC);
package/src/migrations.ts CHANGED
@@ -431,6 +431,7 @@ export const postgresStorageMigrations = defineMigrations<PostgresMigrationConte
431
431
  pgSql("058-reranking-model-kind", rerankingModelKindSql),
432
432
  pgSql("059-file-download-grants", fileDownloadGrantsSql),
433
433
  pgSql("060-drained-source-versions", drainedSourceVersionsSql),
434
+ pgSql("061-terminal-action-runs", terminalActionRunsSql),
434
435
  ],
435
436
  })
436
437
 
@@ -459,3 +460,4 @@ import fileDownloadGrantsSql from "./migrations/059-file-download-grants.sql" wi
459
460
  import drainedSourceVersionsSql from "./migrations/060-drained-source-versions.sql" with {
460
461
  type: "text",
461
462
  }
463
+ import terminalActionRunsSql from "./migrations/061-terminal-action-runs.sql" with { type: "text" }