@sixb/sqlite 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.
- package/dist/action-run-storage.d.ts +4 -9
- package/dist/action-run-storage.d.ts.map +1 -1
- package/dist/index.js +248 -284
- package/dist/index.js.map +4 -4
- package/dist/migrations.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/action-run-storage.ts +104 -442
- package/src/migrations/060-terminal-action-runs.sql +149 -0
- package/src/migrations.ts +2 -0
|
@@ -0,0 +1,149 @@
|
|
|
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. SQLite cannot
|
|
3
|
+
-- alter a CHECK constraint, so the table is rebuilt.
|
|
4
|
+
CREATE TABLE action_runs_v2 (
|
|
5
|
+
project_id TEXT NOT NULL,
|
|
6
|
+
id TEXT NOT NULL,
|
|
7
|
+
execution_id TEXT NOT NULL,
|
|
8
|
+
action_id TEXT NOT NULL,
|
|
9
|
+
subject_kind TEXT NOT NULL CHECK (subject_kind IN ('none', 'object')),
|
|
10
|
+
object_type_id TEXT,
|
|
11
|
+
primary_id TEXT,
|
|
12
|
+
status TEXT NOT NULL CHECK (status IN ('succeeded', 'failed')),
|
|
13
|
+
phase TEXT NOT NULL CHECK (phase IN ('validation', 'writeback', 'edits', 'commit', 'effects')),
|
|
14
|
+
started_at TEXT NOT NULL,
|
|
15
|
+
finished_at TEXT NOT NULL,
|
|
16
|
+
params TEXT NOT NULL,
|
|
17
|
+
idempotency_key TEXT NOT NULL,
|
|
18
|
+
writeback_status TEXT CHECK (writeback_status IS NULL OR writeback_status IN ('succeeded', 'failed')),
|
|
19
|
+
writeback_completed_at TEXT,
|
|
20
|
+
writeback_result TEXT,
|
|
21
|
+
writeback_error TEXT CHECK (writeback_error IS NULL OR json_valid(writeback_error)),
|
|
22
|
+
effects_status TEXT CHECK (effects_status IS NULL OR effects_status IN ('succeeded', 'failed')),
|
|
23
|
+
effects_completed_at TEXT,
|
|
24
|
+
effects_error TEXT CHECK (effects_error IS NULL OR json_valid(effects_error)),
|
|
25
|
+
error TEXT CHECK (error IS NULL OR json_valid(error)),
|
|
26
|
+
CHECK (
|
|
27
|
+
(subject_kind = 'none' AND object_type_id IS NULL AND primary_id IS NULL)
|
|
28
|
+
OR (subject_kind = 'object' AND object_type_id IS NOT NULL AND primary_id IS NOT NULL)
|
|
29
|
+
),
|
|
30
|
+
CHECK ((status = 'failed') = (error IS NOT NULL)),
|
|
31
|
+
PRIMARY KEY (project_id, id),
|
|
32
|
+
UNIQUE (project_id, execution_id)
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
INSERT INTO action_runs_v2 (
|
|
36
|
+
project_id, id, execution_id, action_id, subject_kind, object_type_id, primary_id, status,
|
|
37
|
+
phase, started_at, finished_at, params, idempotency_key, writeback_status,
|
|
38
|
+
writeback_completed_at, writeback_result, writeback_error, effects_status,
|
|
39
|
+
effects_completed_at, effects_error, error
|
|
40
|
+
)
|
|
41
|
+
SELECT
|
|
42
|
+
project_id, id, execution_id, action_id, subject_kind, object_type_id, primary_id,
|
|
43
|
+
migrated_status, migrated_phase,
|
|
44
|
+
-- A run starts when it was queued at the latest.
|
|
45
|
+
COALESCE(started_at, queued_at),
|
|
46
|
+
migrated_finished_at,
|
|
47
|
+
params, idempotency_key, writeback_status,
|
|
48
|
+
CASE
|
|
49
|
+
WHEN writeback_status IS NULL THEN NULL
|
|
50
|
+
ELSE COALESCE(writeback_completed_at, migrated_finished_at)
|
|
51
|
+
END,
|
|
52
|
+
writeback_result, writeback_error,
|
|
53
|
+
CASE WHEN effects_abandoned THEN 'failed' ELSE effects_status END,
|
|
54
|
+
CASE
|
|
55
|
+
WHEN effects_abandoned THEN migrated_at
|
|
56
|
+
WHEN effects_status IS NULL THEN NULL
|
|
57
|
+
ELSE COALESCE(effects_completed_at, migrated_finished_at)
|
|
58
|
+
END,
|
|
59
|
+
CASE
|
|
60
|
+
WHEN effects_abandoned THEN json_object(
|
|
61
|
+
'code', 'internal.unexpected',
|
|
62
|
+
'message', 'Abandoned: Actions now run synchronously; these effects never finished.',
|
|
63
|
+
'retryable', json('false'),
|
|
64
|
+
'at', migrated_at,
|
|
65
|
+
'details', json_object('actionId', action_id, 'runId', id, 'phase', 'effects')
|
|
66
|
+
)
|
|
67
|
+
ELSE effects_error
|
|
68
|
+
END,
|
|
69
|
+
-- A failure names the phase its run ended in.
|
|
70
|
+
CASE
|
|
71
|
+
WHEN migrated_error IS NULL THEN NULL
|
|
72
|
+
WHEN json_extract(migrated_error, '$.details.phase') IS migrated_phase THEN migrated_error
|
|
73
|
+
ELSE json_set(migrated_error, '$.details.phase', migrated_phase)
|
|
74
|
+
END
|
|
75
|
+
FROM (
|
|
76
|
+
SELECT
|
|
77
|
+
*,
|
|
78
|
+
CASE
|
|
79
|
+
WHEN status = 'succeeded' OR edits_committed THEN 'succeeded'
|
|
80
|
+
ELSE 'failed'
|
|
81
|
+
END AS migrated_status,
|
|
82
|
+
CASE
|
|
83
|
+
WHEN edits_committed THEN CASE WHEN phase = 'effects' THEN 'effects' ELSE 'commit' END
|
|
84
|
+
-- The phases `request`, `enqueue`, and `cancelled` are gone: a run that stopped in one of
|
|
85
|
+
-- them never got past validation.
|
|
86
|
+
WHEN phase IN ('validation', 'writeback', 'edits', 'commit', 'effects') THEN phase
|
|
87
|
+
ELSE 'validation'
|
|
88
|
+
END AS migrated_phase,
|
|
89
|
+
CASE
|
|
90
|
+
WHEN edits_committed THEN committed_at
|
|
91
|
+
WHEN status IN ('queued', 'running') THEN migrated_at
|
|
92
|
+
ELSE COALESCE(finished_at, migrated_at)
|
|
93
|
+
END AS migrated_finished_at,
|
|
94
|
+
edits_committed AND phase = 'effects' AND effects_status IS NULL AS effects_abandoned,
|
|
95
|
+
CASE
|
|
96
|
+
WHEN edits_committed THEN NULL
|
|
97
|
+
WHEN status IN ('queued', 'running') THEN json_object(
|
|
98
|
+
'code', 'internal.unexpected',
|
|
99
|
+
'message', 'Abandoned: Actions now run synchronously; this run never finished.',
|
|
100
|
+
'retryable', json('false'),
|
|
101
|
+
'at', migrated_at,
|
|
102
|
+
'details', json_object('actionId', action_id, 'runId', id)
|
|
103
|
+
)
|
|
104
|
+
-- Every failed run carries a failure. One recorded before failures were kept explains
|
|
105
|
+
-- itself.
|
|
106
|
+
WHEN status IN ('failed', 'cancelled') AND error IS NULL THEN json_object(
|
|
107
|
+
'code', 'internal.unexpected',
|
|
108
|
+
'message', 'This run failed before Sixb recorded why.',
|
|
109
|
+
'retryable', json('false'),
|
|
110
|
+
'at', COALESCE(finished_at, migrated_at),
|
|
111
|
+
'details', json_object('actionId', action_id, 'runId', id)
|
|
112
|
+
)
|
|
113
|
+
-- An enqueue failure, which no run can meet any more, reads as the unexpected error it
|
|
114
|
+
-- now is.
|
|
115
|
+
WHEN json_extract(error, '$.code') = 'queue.enqueue_failed'
|
|
116
|
+
THEN json_set(error, '$.code', 'internal.unexpected', '$.retryable', json('false'))
|
|
117
|
+
ELSE error
|
|
118
|
+
END AS migrated_error
|
|
119
|
+
FROM (
|
|
120
|
+
SELECT
|
|
121
|
+
run.*,
|
|
122
|
+
committed.committed_at,
|
|
123
|
+
strftime('%Y-%m-%dT%H:%M:%fZ', 'now') AS migrated_at,
|
|
124
|
+
-- A run left running after its edits committed, as a crash between the commit and the end
|
|
125
|
+
-- of the run left it, succeeded: its edits and their outbox facts are in the commit. A run
|
|
126
|
+
-- that stopped during its effects keeps that phase, and records effects that never ended.
|
|
127
|
+
-- Any other run left queued or running never finished, and nothing will resume it. A
|
|
128
|
+
-- cancelled run failed, and keeps the failure it recorded.
|
|
129
|
+
run.status IN ('queued', 'running') AND committed.committed_at IS NOT NULL
|
|
130
|
+
AS edits_committed
|
|
131
|
+
FROM action_runs AS run
|
|
132
|
+
LEFT JOIN ontology_commits AS committed
|
|
133
|
+
ON committed.origin_kind = 'action'
|
|
134
|
+
AND committed.project_id = run.project_id
|
|
135
|
+
AND committed.origin_run_id = run.id
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
DROP TABLE action_runs;
|
|
140
|
+
ALTER TABLE action_runs_v2 RENAME TO action_runs;
|
|
141
|
+
|
|
142
|
+
CREATE INDEX idx_action_runs_project_started
|
|
143
|
+
ON action_runs(project_id, started_at DESC);
|
|
144
|
+
CREATE INDEX idx_action_runs_project_action_started
|
|
145
|
+
ON action_runs(project_id, action_id, started_at DESC);
|
|
146
|
+
CREATE INDEX idx_action_runs_project_object_started
|
|
147
|
+
ON action_runs(project_id, object_type_id, primary_id, started_at DESC);
|
|
148
|
+
CREATE INDEX idx_action_runs_project_status_started
|
|
149
|
+
ON action_runs(project_id, status, started_at DESC);
|
package/src/migrations.ts
CHANGED
|
@@ -271,6 +271,7 @@ export const sqliteStorageMigrations = defineMigrations({
|
|
|
271
271
|
sqliteSql("057-reranking-model-kind", rerankingModelKindSql),
|
|
272
272
|
sqliteSql("058-file-download-grants", fileDownloadGrantsSql),
|
|
273
273
|
sqliteSql("059-drained-source-versions", drainedSourceVersionsSql),
|
|
274
|
+
sqliteSql("060-terminal-action-runs", terminalActionRunsSql),
|
|
274
275
|
],
|
|
275
276
|
})
|
|
276
277
|
|
|
@@ -597,3 +598,4 @@ import fileDownloadGrantsSql from "./migrations/058-file-download-grants.sql" wi
|
|
|
597
598
|
import drainedSourceVersionsSql from "./migrations/059-drained-source-versions.sql" with {
|
|
598
599
|
type: "text",
|
|
599
600
|
}
|
|
601
|
+
import terminalActionRunsSql from "./migrations/060-terminal-action-runs.sql" with { type: "text" }
|