@adonis-agora/durable 1.0.0-rc.1 → 1.0.0-rc.11
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/CHANGELOG.md +131 -0
- package/dist/assets/spa/assets/index-B2kIfzsc.css +1 -0
- package/dist/assets/spa/assets/index-Bgf_8IJ0.js +16 -0
- package/dist/assets/spa/assets/index-Bgf_8IJ0.js.map +1 -0
- package/dist/assets/spa/index.html +2 -2
- package/dist/commands/durable_worker.d.ts +3 -3
- package/dist/commands/durable_worker.js +7 -7
- package/dist/commands/durable_worker.js.map +1 -1
- package/dist/commands/runs.d.ts.map +1 -1
- package/dist/commands/runs.js +4 -2
- package/dist/commands/runs.js.map +1 -1
- package/dist/providers/dashboard_provider.d.ts +20 -54
- package/dist/providers/dashboard_provider.d.ts.map +1 -1
- package/dist/providers/dashboard_provider.js +87 -262
- package/dist/providers/dashboard_provider.js.map +1 -1
- package/dist/providers/durable_provider.d.ts.map +1 -1
- package/dist/providers/durable_provider.js +38 -33
- package/dist/providers/durable_provider.js.map +1 -1
- package/dist/src/commands/runs.js +1 -1
- package/dist/src/commands/runs.js.map +1 -1
- package/dist/src/concurrency.d.ts.map +1 -1
- package/dist/src/concurrency.js +1 -0
- package/dist/src/concurrency.js.map +1 -1
- package/dist/src/config_types.d.ts +12 -12
- package/dist/src/config_types.d.ts.map +1 -1
- package/dist/src/control-flow-signal.d.ts +5 -4
- package/dist/src/control-flow-signal.d.ts.map +1 -1
- package/dist/src/control-flow-signal.js +5 -4
- package/dist/src/control-flow-signal.js.map +1 -1
- package/dist/src/dashboard/access_denied_page.d.ts +5 -7
- package/dist/src/dashboard/access_denied_page.d.ts.map +1 -1
- package/dist/src/dashboard/access_denied_page.js +2 -6
- package/dist/src/dashboard/access_denied_page.js.map +1 -1
- package/dist/src/dashboard/console_authorize.d.ts +76 -0
- package/dist/src/dashboard/console_authorize.d.ts.map +1 -0
- package/dist/src/dashboard/console_authorize.js +101 -0
- package/dist/src/dashboard/console_authorize.js.map +1 -0
- package/dist/src/dashboard/define_config.d.ts +44 -81
- package/dist/src/dashboard/define_config.d.ts.map +1 -1
- package/dist/src/dashboard/define_config.js +16 -112
- package/dist/src/dashboard/define_config.js.map +1 -1
- package/dist/src/dashboard/diagnostics-recorder.d.ts +1 -1
- package/dist/src/dashboard/diagnostics-recorder.js +1 -1
- package/dist/src/dashboard/gateway-adapter.d.ts +6 -6
- package/dist/src/dashboard/gateway-adapter.d.ts.map +1 -1
- package/dist/src/dashboard/gateway-adapter.js +5 -5
- package/dist/src/dashboard/gateway-adapter.js.map +1 -1
- package/dist/src/dashboard/handlers.d.ts +4 -4
- package/dist/src/dashboard/handlers.d.ts.map +1 -1
- package/dist/src/dashboard/handlers.js +11 -7
- package/dist/src/dashboard/handlers.js.map +1 -1
- package/dist/src/dashboard/index.d.ts +6 -9
- package/dist/src/dashboard/index.d.ts.map +1 -1
- package/dist/src/dashboard/index.js +5 -7
- package/dist/src/dashboard/index.js.map +1 -1
- package/dist/src/dashboard/openapi.d.ts.map +1 -1
- package/dist/src/dashboard/openapi.js +12 -2
- package/dist/src/dashboard/openapi.js.map +1 -1
- package/dist/src/dashboard/run-filter.d.ts.map +1 -1
- package/dist/src/dashboard/run-filter.js +1 -0
- package/dist/src/dashboard/run-filter.js.map +1 -1
- package/dist/src/define_config.d.ts +10 -12
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +9 -4
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/diagnostics/index.d.ts +11 -0
- package/dist/src/diagnostics/index.d.ts.map +1 -0
- package/dist/src/diagnostics/index.js +6 -0
- package/dist/src/diagnostics/index.js.map +1 -0
- package/dist/src/diagnostics/transport.d.ts +71 -0
- package/dist/src/diagnostics/transport.d.ts.map +1 -0
- package/dist/src/diagnostics/transport.js +188 -0
- package/dist/src/diagnostics/transport.js.map +1 -0
- package/dist/src/diagnostics-bridge.d.ts +2 -1
- package/dist/src/diagnostics-bridge.d.ts.map +1 -1
- package/dist/src/diagnostics-bridge.js +7 -1
- package/dist/src/diagnostics-bridge.js.map +1 -1
- package/dist/src/engine.d.ts +14 -9
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +137 -52
- package/dist/src/engine.js.map +1 -1
- package/dist/src/errors.d.ts +9 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +13 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/event-trigger-bridge.d.ts +5 -14
- package/dist/src/event-trigger-bridge.d.ts.map +1 -1
- package/dist/src/event-trigger-bridge.js +11 -99
- package/dist/src/event-trigger-bridge.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/interfaces.d.ts +24 -8
- package/dist/src/interfaces.d.ts.map +1 -1
- package/dist/src/interfaces.js +8 -0
- package/dist/src/interfaces.js.map +1 -1
- package/dist/src/origin.js +3 -3
- package/dist/src/role_bindings.d.ts +8 -8
- package/dist/src/role_bindings.js +8 -8
- package/dist/src/run-gateway/interface.d.ts +4 -3
- package/dist/src/run-gateway/interface.d.ts.map +1 -1
- package/dist/src/run-gateway/proxy-run-gateway.d.ts +1 -1
- package/dist/src/run-gateway/proxy-run-gateway.js +1 -1
- package/dist/src/run-gateway/run-request-responder.d.ts +1 -1
- package/dist/src/run-gateway/store-run-gateway.d.ts +4 -4
- package/dist/src/run-gateway/store-run-gateway.d.ts.map +1 -1
- package/dist/src/run-gateway/store-run-gateway.js +4 -4
- package/dist/src/run-gateway/store-run-gateway.js.map +1 -1
- package/dist/src/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler.js +2 -1
- package/dist/src/scheduler.js.map +1 -1
- package/dist/src/services/main.d.ts +3 -3
- package/dist/src/services/main.js +6 -6
- package/dist/src/services/main.js.map +1 -1
- package/dist/src/singleton-admission.d.ts.map +1 -1
- package/dist/src/singleton-admission.js +2 -0
- package/dist/src/singleton-admission.js.map +1 -1
- package/dist/src/singleton-gate.d.ts +1 -1
- package/dist/src/singleton-gate.d.ts.map +1 -1
- package/dist/src/singleton-gate.js +4 -3
- package/dist/src/singleton-gate.js.map +1 -1
- package/dist/src/stores/lucid-pg-hot-path.d.ts +6 -2
- package/dist/src/stores/lucid-pg-hot-path.d.ts.map +1 -1
- package/dist/src/stores/lucid-pg-hot-path.js +7 -3
- package/dist/src/stores/lucid-pg-hot-path.js.map +1 -1
- package/dist/src/stores/lucid.d.ts +15 -2
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +191 -49
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/testing/in-memory-state-store.js +3 -3
- package/dist/src/testing/in-memory-state-store.js.map +1 -1
- package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
- package/dist/src/testing-kit/state-store-conformance.js +13 -0
- package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
- package/dist/src/testing-kit/transport-conformance.d.ts.map +1 -1
- package/dist/src/testing-kit/transport-conformance.js +5 -1
- package/dist/src/testing-kit/transport-conformance.js.map +1 -1
- package/dist/src/transports/bullmq/bullmq-transport.d.ts +1 -1
- package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -1
- package/dist/src/transports/bullmq/bullmq-transport.js +14 -3
- package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -1
- package/dist/src/transports/db.d.ts.map +1 -1
- package/dist/src/transports/db.js +42 -3
- package/dist/src/transports/db.js.map +1 -1
- package/dist/src/transports/queue.d.ts +2 -2
- package/dist/src/transports/queue.d.ts.map +1 -1
- package/dist/src/transports/queue.js +49 -8
- package/dist/src/transports/queue.js.map +1 -1
- package/dist/src/workflow-discovery.d.ts +1 -1
- package/dist/src/workflow-discovery.d.ts.map +1 -1
- package/dist/src/workflow-discovery.js +4 -7
- package/dist/src/workflow-discovery.js.map +1 -1
- package/dist/src/workflow-events.d.ts +20 -51
- package/dist/src/workflow-events.d.ts.map +1 -1
- package/dist/src/workflow-events.js +12 -30
- package/dist/src/workflow-events.js.map +1 -1
- package/dist/src/workflow-ref.d.ts +1 -1
- package/dist/src/workflow-turn.d.ts.map +1 -1
- package/dist/src/workflow-turn.js +18 -1
- package/dist/src/workflow-turn.js.map +1 -1
- package/dist/stubs/config/durable_dashboard.stub +9 -29
- package/package.json +12 -2
- package/skills/durable-cluster/SKILL.md +34 -22
- package/skills/durable-reliability/SKILL.md +2 -2
- package/dist/assets/spa/assets/index-70hGzOnT.css +0 -1
- package/dist/assets/spa/assets/index-CJjMAUau.js +0 -16
- package/dist/assets/spa/assets/index-CJjMAUau.js.map +0 -1
- package/dist/src/dashboard/auth.d.ts +0 -134
- package/dist/src/dashboard/auth.d.ts.map +0 -1
- package/dist/src/dashboard/auth.js +0 -156
- package/dist/src/dashboard/auth.js.map +0 -1
- package/dist/src/dashboard/login_page.d.ts +0 -36
- package/dist/src/dashboard/login_page.d.ts.map +0 -1
- package/dist/src/dashboard/login_page.js +0 -158
- package/dist/src/dashboard/login_page.js.map +0 -1
- package/dist/src/dashboard/session_cookie.d.ts +0 -45
- package/dist/src/dashboard/session_cookie.d.ts.map +0 -1
- package/dist/src/dashboard/session_cookie.js +0 -97
- package/dist/src/dashboard/session_cookie.js.map +0 -1
|
@@ -14,9 +14,13 @@ export class LucidPgHotPath {
|
|
|
14
14
|
this.knex = knex;
|
|
15
15
|
this.prepared = prepared;
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* The hot path for `client`, or `undefined` when it is not Postgres, is being debugged, or is a
|
|
19
|
+
* transaction: a transaction's one connection is knex's to order (its savepoints included), and a
|
|
20
|
+
* statement sent to it directly would overlap knex's.
|
|
21
|
+
*/
|
|
18
22
|
static for(client, options = {}) {
|
|
19
|
-
if (client.dialect.name !== 'postgres' || client.debug)
|
|
23
|
+
if (client.dialect.name !== 'postgres' || client.debug || client.isTransaction)
|
|
20
24
|
return undefined;
|
|
21
25
|
const knex = client.getWriteClient();
|
|
22
26
|
if (typeof knex?.client?.acquireConnection !== 'function')
|
|
@@ -26,7 +30,7 @@ export class LucidPgHotPath {
|
|
|
26
30
|
/**
|
|
27
31
|
* Run one statement, written with `?` placeholders (the store's texts carry no other `?`);
|
|
28
32
|
* resolves its rows as the driver decoded them, and the affected-row count. The connection comes
|
|
29
|
-
* from the Knex client's own pool
|
|
33
|
+
* from the Knex client's own pool.
|
|
30
34
|
*/
|
|
31
35
|
async run(sql, values) {
|
|
32
36
|
const text = numbered(sql);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lucid-pg-hot-path.js","sourceRoot":"","sources":["../../../src/stores/lucid-pg-hot-path.ts"],"names":[],"mappings":"AA+CA,gDAAgD;AAChD,SAAS,IAAI,CAAC,IAAY;IACxB,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,OAAO,cAAc;IAEN,IAAI;IACJ,QAAQ;IAF3B,YACmB,IAAc,EACd,QAAiB;oBADjB,IAAI;wBACJ,QAAQ;IACxB,CAAC;IAEJ
|
|
1
|
+
{"version":3,"file":"lucid-pg-hot-path.js","sourceRoot":"","sources":["../../../src/stores/lucid-pg-hot-path.ts"],"names":[],"mappings":"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"}
|
|
@@ -59,12 +59,25 @@ export declare class LucidStateStore implements StateStore {
|
|
|
59
59
|
private client;
|
|
60
60
|
/**
|
|
61
61
|
* The Postgres hot path (see `lucid-pg-hot-path.ts`): the statements a turn and a step result run
|
|
62
|
-
* on every step, as constant SQL. `undefined` on any other dialect,
|
|
63
|
-
* Resolved per call, like {@link client}
|
|
62
|
+
* on every step, as constant SQL. `undefined` on any other dialect, with Lucid's `debug` on, or
|
|
63
|
+
* under a (global) transaction. Resolved per call, like {@link client}.
|
|
64
64
|
*/
|
|
65
65
|
private hot;
|
|
66
|
+
/**
|
|
67
|
+
* The hot reads with explicit column lists: each table's base columns plus the later-wave ones it
|
|
68
|
+
* has, probed once (an older schema without them keeps working) and again after this store's own
|
|
69
|
+
* {@link ensureSchema}. A column another process adds later is not read until the next probe, and a
|
|
70
|
+
* statement already prepared keeps its shape.
|
|
71
|
+
*/
|
|
72
|
+
private hotReadStatements;
|
|
73
|
+
private hotReads;
|
|
66
74
|
/** Idempotently provision the durable tables (called on boot when `autoSchema` is on). */
|
|
67
75
|
ensureSchema(): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Insert the run, or reject with {@link RunAlreadyExistsError} when its id is taken. The insert
|
|
78
|
+
* writes nothing on a conflict instead of failing, so a run that already exists never aborts a
|
|
79
|
+
* surrounding transaction (an app's test `wrapInGlobalTransaction`, on Postgres).
|
|
80
|
+
*/
|
|
68
81
|
createRun(run: WorkflowRun): Promise<void>;
|
|
69
82
|
/**
|
|
70
83
|
* Run `write` in a transaction only when `patch` needs one: a terminal settle reads the run's tags
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lucid.d.ts","sourceRoot":"","sources":["../../../src/stores/lucid.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lucid.d.ts","sourceRoot":"","sources":["../../../src/stores/lucid.ts"],"names":[],"mappings":"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"}
|
package/dist/src/stores/lucid.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { RunAlreadyExistsError } from '../errors.js';
|
|
3
|
+
import { RECOVERABLE_RUN_STATUSES, SETTLEABLE_RUN_STATUSES } from '../interfaces.js';
|
|
2
4
|
import { namespaceFilterValues, normalizeNamespace } from '../namespaces.js';
|
|
3
5
|
import { mergeRunFacetRows } from '../run-facets.js';
|
|
4
6
|
import { runPageWindow } from '../run-pagination.js';
|
|
@@ -51,19 +53,59 @@ export class LucidStateStore {
|
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
55
|
* The Postgres hot path (see `lucid-pg-hot-path.ts`): the statements a turn and a step result run
|
|
54
|
-
* on every step, as constant SQL. `undefined` on any other dialect,
|
|
55
|
-
* Resolved per call, like {@link client}
|
|
56
|
+
* on every step, as constant SQL. `undefined` on any other dialect, with Lucid's `debug` on, or
|
|
57
|
+
* under a (global) transaction. Resolved per call, like {@link client}.
|
|
56
58
|
*/
|
|
57
59
|
hot() {
|
|
60
|
+
// Not on a transaction (an app's global one): see `LucidPgHotPath.for`.
|
|
58
61
|
return LucidPgHotPath.for(this.client(), {
|
|
59
62
|
preparedStatements: this.options.preparedStatements,
|
|
60
63
|
});
|
|
61
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The hot reads with explicit column lists: each table's base columns plus the later-wave ones it
|
|
67
|
+
* has, probed once (an older schema without them keeps working) and again after this store's own
|
|
68
|
+
* {@link ensureSchema}. A column another process adds later is not read until the next probe, and a
|
|
69
|
+
* statement already prepared keeps its shape.
|
|
70
|
+
*/
|
|
71
|
+
hotReadStatements;
|
|
72
|
+
hotReads(hot) {
|
|
73
|
+
this.hotReadStatements ??= hot
|
|
74
|
+
.rows(COLUMNS_PRESENT_SQL, [
|
|
75
|
+
[DURABLE_TABLES.runs, DURABLE_TABLES.checkpoints],
|
|
76
|
+
[...RUN_OPTIONAL_COLUMNS, ...CHECKPOINT_OPTIONAL_COLUMNS],
|
|
77
|
+
])
|
|
78
|
+
.then((rows) => {
|
|
79
|
+
const present = new Set(rows.map((r) => `${String(r.table_name)}.${String(r.column_name)}`));
|
|
80
|
+
const has = (table) => (c) => present.has(`${table}.${c}`);
|
|
81
|
+
const runCols = [
|
|
82
|
+
...RUN_BASE_COLUMNS,
|
|
83
|
+
...RUN_OPTIONAL_COLUMNS.filter(has(DURABLE_TABLES.runs)),
|
|
84
|
+
];
|
|
85
|
+
const checkpointCols = [
|
|
86
|
+
...CHECKPOINT_BASE_COLUMNS,
|
|
87
|
+
...CHECKPOINT_OPTIONAL_COLUMNS.filter(has(DURABLE_TABLES.checkpoints)),
|
|
88
|
+
];
|
|
89
|
+
return hotReadSql(runCols.map(quote).join(', '), checkpointCols.map(quote).join(', '));
|
|
90
|
+
})
|
|
91
|
+
.catch((error) => {
|
|
92
|
+
this.hotReadStatements = undefined; // a failed probe is retried, never cached
|
|
93
|
+
throw error;
|
|
94
|
+
});
|
|
95
|
+
return this.hotReadStatements;
|
|
96
|
+
}
|
|
62
97
|
/** Idempotently provision the durable tables (called on boot when `autoSchema` is on). */
|
|
63
98
|
async ensureSchema() {
|
|
64
99
|
await createDurableTables(this.db, this.connectionName, this.options.collation !== undefined ? { collation: this.options.collation } : {});
|
|
100
|
+
// The tables may have gained columns: the next hot read probes them again.
|
|
101
|
+
this.hotReadStatements = undefined;
|
|
65
102
|
}
|
|
66
103
|
// --- runs ---------------------------------------------------------------
|
|
104
|
+
/**
|
|
105
|
+
* Insert the run, or reject with {@link RunAlreadyExistsError} when its id is taken. The insert
|
|
106
|
+
* writes nothing on a conflict instead of failing, so a run that already exists never aborts a
|
|
107
|
+
* surrounding transaction (an app's test `wrapInGlobalTransaction`, on Postgres).
|
|
108
|
+
*/
|
|
67
109
|
async createRun(run) {
|
|
68
110
|
if (normalizeAttributeRows(run.id, run.searchAttributes).length === 0) {
|
|
69
111
|
// Nothing to index: the insert, then clear any attribute rows a deleted run of this id left,
|
|
@@ -72,17 +114,23 @@ export class LucidStateStore {
|
|
|
72
114
|
if (hot) {
|
|
73
115
|
const row = runToRow(run);
|
|
74
116
|
const keys = Object.keys(row);
|
|
75
|
-
await hot.
|
|
117
|
+
const inserted = await hot.rows(insertRunIfAbsentSql(keys), keys.map((k) => row[k]));
|
|
118
|
+
if (inserted.length === 0)
|
|
119
|
+
throw new RunAlreadyExistsError(run.id);
|
|
76
120
|
await hot.run(DELETE_ATTRIBUTES_SQL, [run.id]);
|
|
77
121
|
return;
|
|
78
122
|
}
|
|
79
123
|
const client = this.client();
|
|
80
|
-
await client
|
|
124
|
+
if (!(await insertIfAbsent(client, DURABLE_TABLES.runs, runToRow(run), ['id']))) {
|
|
125
|
+
throw new RunAlreadyExistsError(run.id);
|
|
126
|
+
}
|
|
81
127
|
await client.from(DURABLE_TABLES.attributes).where('run_id', run.id).delete();
|
|
82
128
|
return;
|
|
83
129
|
}
|
|
84
130
|
await this.client().transaction(async (trx) => {
|
|
85
|
-
await trx
|
|
131
|
+
if (!(await insertIfAbsent(trx, DURABLE_TABLES.runs, runToRow(run), ['id']))) {
|
|
132
|
+
throw new RunAlreadyExistsError(run.id);
|
|
133
|
+
}
|
|
86
134
|
await this.reindexAttributes(trx, run.id, run.searchAttributes);
|
|
87
135
|
});
|
|
88
136
|
}
|
|
@@ -277,9 +325,10 @@ export class LucidStateStore {
|
|
|
277
325
|
async getRun(runId) {
|
|
278
326
|
const hot = this.hot();
|
|
279
327
|
if (hot) {
|
|
328
|
+
const sql = await this.hotReads(hot);
|
|
280
329
|
const rows = this.scopeNamespace === undefined
|
|
281
|
-
? await hot.rows(
|
|
282
|
-
: await hot.rows(
|
|
330
|
+
? await hot.rows(sql.get, [runId])
|
|
331
|
+
: await hot.rows(sql.getScoped, [runId, namespaceFilterValues(this.scopeNamespace)]);
|
|
283
332
|
return rows[0] ? rowToRun(rows[0]) : null;
|
|
284
333
|
}
|
|
285
334
|
const row = await this.fenced(this.client().from(DURABLE_TABLES.runs).where('id', runId)).first();
|
|
@@ -323,7 +372,7 @@ export class LucidStateStore {
|
|
|
323
372
|
async getCheckpoint(runId, seq) {
|
|
324
373
|
const hot = this.hot();
|
|
325
374
|
if (hot) {
|
|
326
|
-
const rows = await hot.rows(
|
|
375
|
+
const rows = await hot.rows((await this.hotReads(hot)).getCheckpoint, [runId, seq]);
|
|
327
376
|
return rows[0] ? rowToCheckpoint(rows[0]) : null;
|
|
328
377
|
}
|
|
329
378
|
const row = await this.client()
|
|
@@ -587,27 +636,7 @@ export class LucidStateStore {
|
|
|
587
636
|
const values = keys.map((k) => row[k] ?? null);
|
|
588
637
|
return (await hot.rows(insertCheckpointIfAbsentSql(keys), values)).length > 0;
|
|
589
638
|
}
|
|
590
|
-
|
|
591
|
-
const inserted = (await client
|
|
592
|
-
.insertQuery()
|
|
593
|
-
.table(DURABLE_TABLES.checkpoints)
|
|
594
|
-
.knexQuery.insert(row)
|
|
595
|
-
.onConflict(['run_id', 'seq'])
|
|
596
|
-
.ignore()
|
|
597
|
-
.returning('seq'));
|
|
598
|
-
return inserted.length > 0;
|
|
599
|
-
}
|
|
600
|
-
// MySQL and SQLite: a plain INSERT, whose duplicate-key error is the "already there" answer (an
|
|
601
|
-
// `INSERT IGNORE` would also swallow every other error, and reports no row count through Knex).
|
|
602
|
-
try {
|
|
603
|
-
await client.table(DURABLE_TABLES.checkpoints).insert(row);
|
|
604
|
-
return true;
|
|
605
|
-
}
|
|
606
|
-
catch (err) {
|
|
607
|
-
if (isDuplicateKey(err))
|
|
608
|
-
return false;
|
|
609
|
-
throw err;
|
|
610
|
-
}
|
|
639
|
+
return insertIfAbsent(client, DURABLE_TABLES.checkpoints, row, ['run_id', 'seq']);
|
|
611
640
|
}
|
|
612
641
|
/** Persist the latest worker heartbeat on the step's checkpoint row (see the interface doc).
|
|
613
642
|
* A targeted two-column UPDATE — never an upsert: a beat racing the checkpoint's own insert (or
|
|
@@ -639,7 +668,9 @@ export class LucidStateStore {
|
|
|
639
668
|
}
|
|
640
669
|
// --- recovery / dispatch queries ----------------------------------------
|
|
641
670
|
async listIncompleteRuns(namespace) {
|
|
642
|
-
const q = this.fenced(this.client()
|
|
671
|
+
const q = this.fenced(this.client()
|
|
672
|
+
.from(DURABLE_TABLES.runs)
|
|
673
|
+
.whereIn('status', [...RECOVERABLE_RUN_STATUSES]));
|
|
643
674
|
if (namespace !== undefined)
|
|
644
675
|
q.whereIn('namespace', namespaceFilterValues(namespace));
|
|
645
676
|
const rows = await q;
|
|
@@ -652,7 +683,7 @@ export class LucidStateStore {
|
|
|
652
683
|
// instance id (a rebooted instance's own leftovers; the engine filters what it is executing).
|
|
653
684
|
const q = this.fenced(this.client()
|
|
654
685
|
.from(DURABLE_TABLES.runs)
|
|
655
|
-
.
|
|
686
|
+
.whereIn('status', [...RECOVERABLE_RUN_STATUSES])
|
|
656
687
|
.andWhere((sub) => {
|
|
657
688
|
sub.whereNull('locked_until').orWhere('locked_until', '<=', nowMs);
|
|
658
689
|
if (owner !== undefined)
|
|
@@ -767,9 +798,10 @@ export class LucidStateStore {
|
|
|
767
798
|
async tryLockRunAndGet(runId, owner, leaseUntilMs, nowMs) {
|
|
768
799
|
const hot = this.hot();
|
|
769
800
|
if (hot) {
|
|
801
|
+
const sql = await this.hotReads(hot);
|
|
770
802
|
const rows = this.scopeNamespace === undefined
|
|
771
|
-
? await hot.rows(
|
|
772
|
-
: await hot.rows(
|
|
803
|
+
? await hot.rows(sql.lockAndGet, [owner, leaseUntilMs, runId, nowMs])
|
|
804
|
+
: await hot.rows(sql.lockAndGetScoped, [
|
|
773
805
|
owner,
|
|
774
806
|
leaseUntilMs,
|
|
775
807
|
runId,
|
|
@@ -1568,6 +1600,43 @@ export class LucidStateStore {
|
|
|
1568
1600
|
}
|
|
1569
1601
|
}
|
|
1570
1602
|
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Serialize the store's work on a transaction client. A transaction is ONE connection: two store
|
|
1605
|
+
* calls in flight at once (a background run pickup next to a request's own write, under an app's test
|
|
1606
|
+
* `wrapInGlobalTransaction`) would send overlapping queries down it, which `pg` deprecates (and 9.0
|
|
1607
|
+
* removes), and a statement could land inside a savepoint the other call is rolling back. So while
|
|
1608
|
+
* the store's client is a transaction, each call waits for the previous one on that transaction to
|
|
1609
|
+
* finish. Re-entrant: a store method calling another one in the same call does not wait for itself.
|
|
1610
|
+
* A store on a plain connection is untouched (its pool hands each query its own connection).
|
|
1611
|
+
*/
|
|
1612
|
+
const transactionTails = new WeakMap();
|
|
1613
|
+
const holdingTransaction = new AsyncLocalStorage();
|
|
1614
|
+
function serializeOnTransactions(proto) {
|
|
1615
|
+
const AsyncFunction = (async () => { }).constructor;
|
|
1616
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
1617
|
+
if (name === 'constructor')
|
|
1618
|
+
continue;
|
|
1619
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, name);
|
|
1620
|
+
const original = descriptor?.value;
|
|
1621
|
+
if (typeof original !== 'function' || !(original instanceof AsyncFunction))
|
|
1622
|
+
continue;
|
|
1623
|
+
const method = original;
|
|
1624
|
+
Object.defineProperty(proto, name, {
|
|
1625
|
+
...descriptor,
|
|
1626
|
+
value: function (...args) {
|
|
1627
|
+
const client = this.client();
|
|
1628
|
+
if (!client.isTransaction || holdingTransaction.getStore() === client) {
|
|
1629
|
+
return method.apply(this, args);
|
|
1630
|
+
}
|
|
1631
|
+
const previous = transactionTails.get(client) ?? Promise.resolve();
|
|
1632
|
+
const run = previous.then(() => holdingTransaction.run(client, () => method.apply(this, args)), () => holdingTransaction.run(client, () => method.apply(this, args)));
|
|
1633
|
+
transactionTails.set(client, run.catch(() => undefined));
|
|
1634
|
+
return run;
|
|
1635
|
+
},
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
serializeOnTransactions(LucidStateStore.prototype);
|
|
1571
1640
|
/**
|
|
1572
1641
|
* Escape LIKE metacharacters (`%`, `_`) — and the escape character itself — using `!` as the ESCAPE
|
|
1573
1642
|
* char. `!` because a backslash inside a SQL string literal is dialect-divergent (MySQL treats it as
|
|
@@ -1664,24 +1733,71 @@ function escapeLike(value) {
|
|
|
1664
1733
|
const RUNS_T = quote(DURABLE_TABLES.runs);
|
|
1665
1734
|
const CHECKPOINTS_T = quote(DURABLE_TABLES.checkpoints);
|
|
1666
1735
|
const CHECKPOINT_LIST = CHECKPOINT_COLUMNS_BUT_INPUT.map(quote).join(', ');
|
|
1667
|
-
|
|
1668
|
-
|
|
1736
|
+
/**
|
|
1737
|
+
* The runs columns every schema has. The hot path names its columns instead of `*`: a named prepared
|
|
1738
|
+
* statement's result type is fixed when it is parsed, so a `*` statement fails ("cached plan must
|
|
1739
|
+
* not change result type") once a column is added under it — another pod's `ensureSchema` during a
|
|
1740
|
+
* rolling upgrade.
|
|
1741
|
+
*/
|
|
1742
|
+
const RUN_BASE_COLUMNS = [
|
|
1743
|
+
'id',
|
|
1744
|
+
'workflow',
|
|
1745
|
+
'workflow_version',
|
|
1746
|
+
'status',
|
|
1747
|
+
'namespace',
|
|
1748
|
+
'input',
|
|
1749
|
+
'output',
|
|
1750
|
+
'error',
|
|
1751
|
+
'wake_at',
|
|
1752
|
+
'locked_by',
|
|
1753
|
+
'locked_until',
|
|
1754
|
+
'recovery_attempts',
|
|
1755
|
+
'tags',
|
|
1756
|
+
'search_attributes',
|
|
1757
|
+
'priority',
|
|
1758
|
+
'created_at',
|
|
1759
|
+
'updated_at',
|
|
1760
|
+
];
|
|
1761
|
+
/** Runs columns that arrived in later schema waves: read only when the table has them. */
|
|
1762
|
+
const RUN_OPTIONAL_COLUMNS = ['origin', 'awaiting_decision_task_id', 'wake_requested_at'];
|
|
1763
|
+
/** Checkpoint columns that arrived in later schema waves: read only when the table has them. */
|
|
1764
|
+
const CHECKPOINT_OPTIONAL_COLUMNS = [
|
|
1765
|
+
'queue',
|
|
1766
|
+
'parallel_group',
|
|
1767
|
+
'last_heartbeat_at',
|
|
1768
|
+
'heartbeat_progress',
|
|
1769
|
+
];
|
|
1770
|
+
const CHECKPOINT_BASE_COLUMNS = [
|
|
1771
|
+
...CHECKPOINT_COLUMNS_BUT_INPUT.filter((c) => !CHECKPOINT_OPTIONAL_COLUMNS.includes(c)),
|
|
1772
|
+
'input',
|
|
1773
|
+
];
|
|
1774
|
+
const COLUMNS_PRESENT_SQL = `select "table_name", "column_name" from information_schema.columns where "table_schema" = current_schema() and "table_name" = any(?) and "column_name" = any(?)`;
|
|
1775
|
+
const BUFFERED_SIGNAL_LIST = ['id', 'token', 'payload'].map(quote).join(', ');
|
|
1776
|
+
const BUFFERED_EVENT_LIST = ['id', 'name', 'payload', 'published_at'].map(quote).join(', ');
|
|
1777
|
+
/** {@link HotReads} for the runs column list `cols` and checkpoints column list `checkpointCols`
|
|
1778
|
+
* (each quoted, comma-joined). */
|
|
1779
|
+
function hotReadSql(cols, checkpointCols) {
|
|
1780
|
+
return {
|
|
1781
|
+
getCheckpoint: shapedSql(`checkpoint-get:${checkpointCols}`, () => `select ${checkpointCols} from ${CHECKPOINTS_T} where "run_id" = ? and "seq" = ? limit 1`),
|
|
1782
|
+
get: shapedSql(`run-get:${cols}`, () => `select ${cols} from ${RUNS_T} where "id" = ? limit 1`),
|
|
1783
|
+
getScoped: shapedSql(`run-get-scoped:${cols}`, () => `select ${cols} from ${RUNS_T} where "id" = ? and "namespace" = any(?) limit 1`),
|
|
1784
|
+
lockAndGet: shapedSql(`run-lock:${cols}`, () => `update ${RUNS_T} set "locked_by" = ?, "locked_until" = ? where "id" = ? and ("locked_until" is null or "locked_until" <= ?) returning ${cols}`),
|
|
1785
|
+
lockAndGetScoped: shapedSql(`run-lock-scoped:${cols}`, () => `update ${RUNS_T} set "locked_by" = ?, "locked_until" = ? where "id" = ? and ("locked_until" is null or "locked_until" <= ?) and "namespace" = any(?) returning ${cols}`),
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1669
1788
|
const RUN_IN_STATUSES_SQL = `select 1 from ${RUNS_T} where "id" = ? and "status" = any(?) limit 1`;
|
|
1670
|
-
const LOCK_AND_GET_SQL = `update ${RUNS_T} set "locked_by" = ?, "locked_until" = ? where "id" = ? and ("locked_until" is null or "locked_until" <= ?) returning *`;
|
|
1671
|
-
const LOCK_AND_GET_SCOPED_SQL = `update ${RUNS_T} set "locked_by" = ?, "locked_until" = ? where "id" = ? and ("locked_until" is null or "locked_until" <= ?) and "namespace" = any(?) returning *`;
|
|
1672
1789
|
const RELEASE_LOCK_SQL = `update ${RUNS_T} set "locked_by" = null, "locked_until" = null where "id" = ?`;
|
|
1673
1790
|
const RELEASE_OWN_LOCK_SQL = `update ${RUNS_T} set "locked_by" = null, "locked_until" = null where "id" = ? and "locked_by" = ?`;
|
|
1674
1791
|
const RELEASE_UNLESS_WAKE_SQL = `update ${RUNS_T} set "locked_by" = null, "locked_until" = null where "id" = ? and "locked_by" = ? and "wake_requested_at" is null`;
|
|
1675
1792
|
const LOCKED_BY_SQL = `select "locked_by" from ${RUNS_T} where "id" = ? limit 1`;
|
|
1676
1793
|
const RENEW_LOCK_SQL = `update ${RUNS_T} set "locked_until" = ? where "id" = ? and "locked_by" = ?`;
|
|
1677
1794
|
const DELETE_ATTRIBUTES_SQL = `delete from ${quote(DURABLE_TABLES.attributes)} where "run_id" = ?`;
|
|
1678
|
-
const GET_CHECKPOINT_SQL = `select * from ${CHECKPOINTS_T} where "run_id" = ? and "seq" = ? limit 1`;
|
|
1679
1795
|
const CHECKPOINTS_FOR_SETTLE_SQL = `select ${CHECKPOINT_LIST} from ${CHECKPOINTS_T} where "run_id" = ? and "seq" = any(?)`;
|
|
1680
1796
|
const REPLAY_CHECKPOINTS_SQL = `select ${CHECKPOINT_LIST} from ${CHECKPOINTS_T} where "run_id" = ? order by "seq" asc`;
|
|
1681
1797
|
const COUNT_CHECKPOINTS_SQL = `select count(*) as "count" from ${CHECKPOINTS_T} where "run_id" = ? and "seq" >= ? and "seq" <= ? and "status" = any(?)`;
|
|
1682
|
-
const OLDEST_BUFFERED_SIGNAL_SQL = `select
|
|
1798
|
+
const OLDEST_BUFFERED_SIGNAL_SQL = `select ${BUFFERED_SIGNAL_LIST} from ${quote(DURABLE_TABLES.bufferedSignals)} where "token" = ? order by "id" asc limit 1`;
|
|
1683
1799
|
const DELETE_BUFFERED_SIGNAL_SQL = `delete from ${quote(DURABLE_TABLES.bufferedSignals)} where "id" = ?`;
|
|
1684
|
-
const BUFFERED_EVENTS_SQL = `select
|
|
1800
|
+
const BUFFERED_EVENTS_SQL = `select ${BUFFERED_EVENT_LIST} from ${quote(DURABLE_TABLES.bufferedEvents)} where "name" = ? order by "published_at" asc limit ?`;
|
|
1685
1801
|
/** Statements whose column list is the row's own keys, built once per key set. */
|
|
1686
1802
|
const shaped = new Map();
|
|
1687
1803
|
function shapedSql(key, build) {
|
|
@@ -1699,6 +1815,10 @@ function insertSql(table, keys) {
|
|
|
1699
1815
|
return `insert into ${quote(table)} (${cols}) values (${keys.map(() => '?').join(', ')})`;
|
|
1700
1816
|
});
|
|
1701
1817
|
}
|
|
1818
|
+
/** {@link insertSql} of a run that writes nothing when its id is taken, returning the id it wrote. */
|
|
1819
|
+
function insertRunIfAbsentSql(keys) {
|
|
1820
|
+
return shapedSql(`insert-run-if-absent:${keys.join(',')}`, () => `${insertSql(DURABLE_TABLES.runs, keys)} on conflict ("id") do nothing returning "id"`);
|
|
1821
|
+
}
|
|
1702
1822
|
/** {@link insertSql} of a checkpoint that writes nothing when its position is taken. */
|
|
1703
1823
|
function insertCheckpointIfAbsentSql(keys) {
|
|
1704
1824
|
return shapedSql(`insert-if-absent:${keys.join(',')}`, () => `${insertSql(DURABLE_TABLES.checkpoints, keys)} on conflict ("run_id", "seq") do nothing returning "seq"`);
|
|
@@ -1758,14 +1878,36 @@ async function settleResultsReturningPg(client, hot, results) {
|
|
|
1758
1878
|
}
|
|
1759
1879
|
return out;
|
|
1760
1880
|
}
|
|
1761
|
-
/**
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1881
|
+
/**
|
|
1882
|
+
* Insert `row` into `table` unless a row with the same `key` exists, as a statement that never
|
|
1883
|
+
* fails on the conflict, and say whether it wrote. A failing statement would abort a surrounding
|
|
1884
|
+
* Postgres transaction, so the conflict is never detected by catching a duplicate-key error.
|
|
1885
|
+
*
|
|
1886
|
+
* - Postgres and SQLite: `INSERT … ON CONFLICT (key) DO NOTHING RETURNING key`, a row back when it wrote.
|
|
1887
|
+
* - MySQL: `INSERT … ON DUPLICATE KEY UPDATE key = IF(LAST_INSERT_ID(1), key, key)`, which leaves an
|
|
1888
|
+
* existing row as it is and reports `insertId` 1 for it, 0 for a row it wrote (`table` has no
|
|
1889
|
+
* AUTO_INCREMENT). The affected-row count cannot tell them apart (mysql2 counts found rows), and
|
|
1890
|
+
* `INSERT IGNORE` would turn a value the column cannot hold into a silent truncation.
|
|
1891
|
+
*/
|
|
1892
|
+
async function insertIfAbsent(client, table, row, key) {
|
|
1893
|
+
if (client.dialect.name === 'mysql') {
|
|
1894
|
+
const first = key[0];
|
|
1895
|
+
const knex = client.getWriteClient();
|
|
1896
|
+
const { sql, bindings } = client
|
|
1897
|
+
.insertQuery()
|
|
1898
|
+
.table(table)
|
|
1899
|
+
.knexQuery.insert(row)
|
|
1900
|
+
.onConflict(key)
|
|
1901
|
+
.merge({ [first]: knex.raw('if(last_insert_id(1), ??, ??)', [first, first]) })
|
|
1902
|
+
.toSQL()
|
|
1903
|
+
.toNative();
|
|
1904
|
+
const result = (await client.rawQuery(sql, bindings));
|
|
1905
|
+
const header = (Array.isArray(result) ? result[0] : result);
|
|
1906
|
+
return Number(header?.insertId ?? 0) === 0;
|
|
1907
|
+
}
|
|
1908
|
+
const insert = client.insertQuery().table(table).knexQuery.insert(row).onConflict(key);
|
|
1909
|
+
const inserted = (await insert.ignore().returning(key[0]));
|
|
1910
|
+
return inserted.length > 0;
|
|
1769
1911
|
}
|
|
1770
1912
|
function checkpointKey(runId, seq) {
|
|
1771
1913
|
return `${runId}\u0000${seq}`;
|