@effect-app/infra 4.0.0-beta.257 → 4.0.0-beta.258
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 +11 -0
- package/dist/WorkflowEngineSqlite.d.ts +24 -0
- package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
- package/dist/WorkflowEngineSqlite.js +550 -0
- package/package.json +7 -206
- package/src/WorkflowEngineCosmos.ts +719 -0
- package/src/WorkflowEngineSqlite.ts +813 -0
- package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
- package/test/workflow-engine-cosmos.test.ts +354 -0
- package/test/workflow-engine-sqlite.test.ts +299 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite backed {@link WorkflowEngine} implementation.
|
|
3
|
+
*
|
|
4
|
+
* Persists workflow state across four tables:
|
|
5
|
+
* - `workflow_exec` — one row per execution; tracks status, lease, etag
|
|
6
|
+
* - `workflow_activity` — recorded activity results keyed by (exec, name, attempt)
|
|
7
|
+
* - `workflow_deferred` — durable deferred completions keyed by (exec, name)
|
|
8
|
+
* - `workflow_clock` — scheduled clocks with `fire_at`
|
|
9
|
+
*
|
|
10
|
+
* Atomicity: multi-statement operations are wrapped in `sql.withTransaction`
|
|
11
|
+
* (BEGIN/COMMIT) so concurrent writers do not observe partial state.
|
|
12
|
+
*
|
|
13
|
+
* Optimistic concurrency:
|
|
14
|
+
* - exec state transitions use `UPDATE ... WHERE etag = ? RETURNING etag`;
|
|
15
|
+
* a zero-row result is an `OptimisticConcurrencyException`.
|
|
16
|
+
* - activity / deferred / clock inserts use `INSERT ... ON CONFLICT DO
|
|
17
|
+
* NOTHING RETURNING ...` for first-writer-wins semantics across drivers.
|
|
18
|
+
*
|
|
19
|
+
* Durability — everything that crosses the storage boundary is round-tripped
|
|
20
|
+
* through schema codecs (`S.fromJsonString(S.toCodecJson(...))`), exactly like
|
|
21
|
+
* the cluster engine:
|
|
22
|
+
*
|
|
23
|
+
* - The workflow payload and the top-level `Workflow.Result` are encoded with
|
|
24
|
+
* the workflow's own `payloadSchema` / `successSchema` / `errorSchema`, so
|
|
25
|
+
* typed values (dates, branded ids, schema classes) survive a restart.
|
|
26
|
+
* - Activity results flow through the engine already encoded, so they are
|
|
27
|
+
* persisted with an opaque `Workflow.Result({ success: AnyOrVoid, error:
|
|
28
|
+
* AnyOrVoid })` codec — same trick the cluster `ActivityRpc` uses.
|
|
29
|
+
* - Durable-deferred exits use an opaque `Exit` codec.
|
|
30
|
+
*
|
|
31
|
+
* Crash recovery: each driver holds a time-bound lease on the exec row,
|
|
32
|
+
* renewed by a heartbeat fiber. A scope-bound recovery poller re-drives any
|
|
33
|
+
* exec whose lease has lapsed. A clock poller fires due clocks even when
|
|
34
|
+
* the in-process timer is missing (e.g. after a restart).
|
|
35
|
+
*/
|
|
36
|
+
import * as Effect from "effect-app/Effect";
|
|
37
|
+
import * as Layer from "effect-app/Layer";
|
|
38
|
+
import * as Option from "effect-app/Option";
|
|
39
|
+
import * as S from "effect-app/Schema";
|
|
40
|
+
import * as Duration from "effect/Duration";
|
|
41
|
+
import * as Exit from "effect/Exit";
|
|
42
|
+
import * as Fiber from "effect/Fiber";
|
|
43
|
+
import * as FiberMap from "effect/FiberMap";
|
|
44
|
+
import * as Schedule from "effect/Schedule";
|
|
45
|
+
import { SqlClient } from "effect/unstable/sql";
|
|
46
|
+
import * as Workflow from "effect/unstable/workflow/Workflow";
|
|
47
|
+
import { makeUnsafe, WorkflowEngine, WorkflowInstance } from "effect/unstable/workflow/WorkflowEngine";
|
|
48
|
+
import { randomUUID } from "node:crypto";
|
|
49
|
+
import { OptimisticConcurrencyException } from "./errors.js";
|
|
50
|
+
import { annotateDb } from "./otel.js";
|
|
51
|
+
const parseExec = (row) => ({
|
|
52
|
+
executionId: row.execution_id,
|
|
53
|
+
workflowName: row.workflow_name,
|
|
54
|
+
payload: row.payload,
|
|
55
|
+
parent: row.parent ?? undefined,
|
|
56
|
+
status: row.status,
|
|
57
|
+
suspended: row.suspended !== 0,
|
|
58
|
+
interrupted: row.interrupted !== 0,
|
|
59
|
+
completedResult: row.completed_result ?? undefined,
|
|
60
|
+
worker: row.worker ?? undefined,
|
|
61
|
+
leaseExpiresAt: row.lease_expires_at ?? undefined,
|
|
62
|
+
etag: row.etag
|
|
63
|
+
});
|
|
64
|
+
// --- Storage codecs ---------------------------------------------------------
|
|
65
|
+
// Values flowing through the engine's activity / deferred boundary are already
|
|
66
|
+
// schema-encoded, so the structure is round-tripped while the payload stays
|
|
67
|
+
// opaque (mirrors the cluster engine's `AnyOrVoid` usage).
|
|
68
|
+
const AnyOrVoid = S.Union([S.Any, S.Void]);
|
|
69
|
+
const ActivityResultCodec = S.fromJsonString(S.toCodecJson(Workflow.Result({ success: AnyOrVoid, error: AnyOrVoid })));
|
|
70
|
+
const DeferredExitCodec = S.fromJsonString(S.toCodecJson(S.Exit(AnyOrVoid, AnyOrVoid, S.Defect)));
|
|
71
|
+
const encodeActivityResult = (r) => Effect.orDie(S.encodeEffect(ActivityResultCodec)(r));
|
|
72
|
+
const decodeActivityResult = (s) => Effect.orDie(S.decodeEffect(ActivityResultCodec)(s));
|
|
73
|
+
const encodeDeferredExit = (e) => Effect.orDie(S.encodeEffect(DeferredExitCodec)(e));
|
|
74
|
+
const decodeDeferredExit = (s) => Effect.orDie(S.decodeEffect(DeferredExitCodec)(s));
|
|
75
|
+
const makeSqliteWorkflowEngine = Effect.fnUntraced(function* (cfg) {
|
|
76
|
+
const sql = yield* SqlClient.SqlClient;
|
|
77
|
+
const scope = yield* Effect.scope;
|
|
78
|
+
const prefix = cfg.prefix ?? "";
|
|
79
|
+
const execTable = `${prefix}workflow_exec`;
|
|
80
|
+
const activityTable = `${prefix}workflow_activity`;
|
|
81
|
+
const deferredTable = `${prefix}workflow_deferred`;
|
|
82
|
+
const clockTable = `${prefix}workflow_clock`;
|
|
83
|
+
const workerId = cfg.workerId ?? randomUUID();
|
|
84
|
+
const leaseTtl = cfg.leaseTtl ?? Duration.seconds(30);
|
|
85
|
+
const heartbeatInterval = cfg.heartbeatInterval ?? Duration.seconds(10);
|
|
86
|
+
const recoveryInterval = cfg.recoveryInterval ?? Duration.seconds(15);
|
|
87
|
+
const clockPollInterval = cfg.clockPollInterval ?? Duration.seconds(5);
|
|
88
|
+
const annotate = (operation, executionId) => annotateDb({
|
|
89
|
+
operation,
|
|
90
|
+
system: "sqlite",
|
|
91
|
+
collection: execTable,
|
|
92
|
+
entity: "workflow",
|
|
93
|
+
extra: executionId !== undefined ? { "app.entity.id": executionId } : undefined
|
|
94
|
+
});
|
|
95
|
+
const exec = (query, params = []) => sql.unsafe(query, params).pipe(Effect.orDie);
|
|
96
|
+
// --- Schema -----------------------------------------------------------
|
|
97
|
+
yield* exec(`CREATE TABLE IF NOT EXISTS "${execTable}" (
|
|
98
|
+
execution_id TEXT PRIMARY KEY,
|
|
99
|
+
workflow_name TEXT NOT NULL,
|
|
100
|
+
payload TEXT NOT NULL,
|
|
101
|
+
parent TEXT,
|
|
102
|
+
status TEXT NOT NULL,
|
|
103
|
+
suspended INTEGER NOT NULL DEFAULT 0,
|
|
104
|
+
interrupted INTEGER NOT NULL DEFAULT 0,
|
|
105
|
+
completed_result TEXT,
|
|
106
|
+
worker TEXT,
|
|
107
|
+
lease_expires_at INTEGER,
|
|
108
|
+
etag TEXT NOT NULL
|
|
109
|
+
)`);
|
|
110
|
+
yield* exec(`CREATE INDEX IF NOT EXISTS "${execTable}_recovery" ON "${execTable}" (status, lease_expires_at)`);
|
|
111
|
+
yield* exec(`CREATE TABLE IF NOT EXISTS "${activityTable}" (
|
|
112
|
+
execution_id TEXT NOT NULL,
|
|
113
|
+
name TEXT NOT NULL,
|
|
114
|
+
attempt INTEGER NOT NULL,
|
|
115
|
+
result TEXT NOT NULL,
|
|
116
|
+
PRIMARY KEY (execution_id, name, attempt)
|
|
117
|
+
)`);
|
|
118
|
+
yield* exec(`CREATE TABLE IF NOT EXISTS "${deferredTable}" (
|
|
119
|
+
execution_id TEXT NOT NULL,
|
|
120
|
+
name TEXT NOT NULL,
|
|
121
|
+
exit TEXT NOT NULL,
|
|
122
|
+
PRIMARY KEY (execution_id, name)
|
|
123
|
+
)`);
|
|
124
|
+
yield* exec(`CREATE TABLE IF NOT EXISTS "${clockTable}" (
|
|
125
|
+
execution_id TEXT NOT NULL,
|
|
126
|
+
name TEXT NOT NULL,
|
|
127
|
+
workflow_name TEXT NOT NULL,
|
|
128
|
+
deferred_name TEXT NOT NULL,
|
|
129
|
+
fire_at INTEGER NOT NULL,
|
|
130
|
+
PRIMARY KEY (execution_id, name)
|
|
131
|
+
)`);
|
|
132
|
+
yield* exec(`CREATE INDEX IF NOT EXISTS "${clockTable}_due" ON "${clockTable}" (fire_at)`);
|
|
133
|
+
const workflows = new Map();
|
|
134
|
+
const locals = new Map();
|
|
135
|
+
const clocks = yield* FiberMap.make();
|
|
136
|
+
// Per-workflow codecs for the typed payload + top-level result. Cached by
|
|
137
|
+
// workflow name; derived from the workflow's own schemas so typed values
|
|
138
|
+
// (dates, branded ids, schema classes) survive the storage round-trip.
|
|
139
|
+
const makePayloadCodec = (workflow) => S.fromJsonString(S.toCodecJson(workflow.payloadSchema));
|
|
140
|
+
const payloadCodecCache = new Map();
|
|
141
|
+
const payloadCodecFor = (workflow) => {
|
|
142
|
+
let c = payloadCodecCache.get(workflow.name);
|
|
143
|
+
if (!c) {
|
|
144
|
+
c = makePayloadCodec(workflow);
|
|
145
|
+
payloadCodecCache.set(workflow.name, c);
|
|
146
|
+
}
|
|
147
|
+
return c;
|
|
148
|
+
};
|
|
149
|
+
const makeResultCodec = (workflow) => S.fromJsonString(S.toCodecJson(Workflow.Result({ success: workflow.successSchema, error: workflow.errorSchema })));
|
|
150
|
+
const resultCodecCache = new Map();
|
|
151
|
+
const resultCodecFor = (workflow) => {
|
|
152
|
+
let c = resultCodecCache.get(workflow.name);
|
|
153
|
+
if (!c) {
|
|
154
|
+
c = makeResultCodec(workflow);
|
|
155
|
+
resultCodecCache.set(workflow.name, c);
|
|
156
|
+
}
|
|
157
|
+
return c;
|
|
158
|
+
};
|
|
159
|
+
const encodePayload = (workflow, payload) => Effect.orDie(S.encodeEffect(payloadCodecFor(workflow))(payload));
|
|
160
|
+
const decodePayload = (workflow, s) => Effect.orDie(S.decodeEffect(payloadCodecFor(workflow))(s));
|
|
161
|
+
const encodeResult = (workflow, r) => Effect.orDie(S.encodeEffect(resultCodecFor(workflow))(r));
|
|
162
|
+
const decodeResult = (workflow, s) => Effect.orDie(S.decodeEffect(resultCodecFor(workflow))(s));
|
|
163
|
+
// --- Core SQL operations ----------------------------------------------
|
|
164
|
+
const readExec = (executionId) => exec(`SELECT * FROM "${execTable}" WHERE execution_id = ?`, [executionId])
|
|
165
|
+
.pipe(Effect.map((rows) => {
|
|
166
|
+
const r = rows[0];
|
|
167
|
+
return r ? Option.some(parseExec(r)) : Option.none();
|
|
168
|
+
}), annotate("readExec", executionId));
|
|
169
|
+
/**
|
|
170
|
+
* OCC-guarded write. Generates a fresh etag on success; returns
|
|
171
|
+
* `OptimisticConcurrencyException` when no row matches the prior etag.
|
|
172
|
+
*/
|
|
173
|
+
const replaceExec = (state, next) => Effect
|
|
174
|
+
.gen(function* () {
|
|
175
|
+
const newEtag = randomUUID();
|
|
176
|
+
const merged = { ...state, ...next, etag: newEtag };
|
|
177
|
+
const rows = yield* exec(`UPDATE "${execTable}"
|
|
178
|
+
SET status = ?,
|
|
179
|
+
suspended = ?,
|
|
180
|
+
interrupted = ?,
|
|
181
|
+
completed_result = ?,
|
|
182
|
+
worker = ?,
|
|
183
|
+
lease_expires_at = ?,
|
|
184
|
+
etag = ?
|
|
185
|
+
WHERE execution_id = ? AND etag = ?
|
|
186
|
+
RETURNING etag`, [
|
|
187
|
+
merged.status,
|
|
188
|
+
merged.suspended ? 1 : 0,
|
|
189
|
+
merged.interrupted ? 1 : 0,
|
|
190
|
+
merged.completedResult ?? null,
|
|
191
|
+
merged.worker ?? null,
|
|
192
|
+
merged.leaseExpiresAt ?? null,
|
|
193
|
+
newEtag,
|
|
194
|
+
state.executionId,
|
|
195
|
+
state.etag
|
|
196
|
+
]);
|
|
197
|
+
if (rows.length === 0) {
|
|
198
|
+
return yield* new OptimisticConcurrencyException({
|
|
199
|
+
type: "workflow.exec",
|
|
200
|
+
id: state.executionId,
|
|
201
|
+
code: 412
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return merged;
|
|
205
|
+
})
|
|
206
|
+
.pipe(annotate("replaceExec", state.executionId));
|
|
207
|
+
const createExec = (initial) => exec(`INSERT INTO "${execTable}"
|
|
208
|
+
(execution_id, workflow_name, payload, parent, status, suspended, interrupted, etag)
|
|
209
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
210
|
+
ON CONFLICT DO NOTHING
|
|
211
|
+
RETURNING execution_id`, [
|
|
212
|
+
initial.executionId,
|
|
213
|
+
initial.workflowName,
|
|
214
|
+
initial.payload,
|
|
215
|
+
initial.parent ?? null,
|
|
216
|
+
initial.status,
|
|
217
|
+
initial.suspended ? 1 : 0,
|
|
218
|
+
initial.interrupted ? 1 : 0,
|
|
219
|
+
initial.etag
|
|
220
|
+
])
|
|
221
|
+
.pipe(Effect.map((rows) => rows.length > 0), annotate("createExec", initial.executionId));
|
|
222
|
+
// First-writer-wins persistence of an activity result; returns true if this
|
|
223
|
+
// call won, false if another writer beat us to the (exec, name, attempt) row.
|
|
224
|
+
const createActivity = (executionId, name, attempt, encoded) => exec(`INSERT INTO "${activityTable}" (execution_id, name, attempt, result)
|
|
225
|
+
VALUES (?, ?, ?, ?)
|
|
226
|
+
ON CONFLICT DO NOTHING
|
|
227
|
+
RETURNING execution_id`, [executionId, name, attempt, encoded])
|
|
228
|
+
.pipe(Effect.map((rows) => rows.length > 0));
|
|
229
|
+
// Overwrites a previously persisted *suspended* activity result so the next
|
|
230
|
+
// attempt can record its real outcome.
|
|
231
|
+
const upsertActivity = (executionId, name, attempt, encoded) => exec(`INSERT INTO "${activityTable}" (execution_id, name, attempt, result)
|
|
232
|
+
VALUES (?, ?, ?, ?)
|
|
233
|
+
ON CONFLICT(execution_id, name, attempt) DO UPDATE SET result = excluded.result`, [executionId, name, attempt, encoded])
|
|
234
|
+
.pipe(Effect.asVoid);
|
|
235
|
+
const readActivity = (executionId, name, attempt) => exec(`SELECT result FROM "${activityTable}" WHERE execution_id = ? AND name = ? AND attempt = ?`, [executionId, name, attempt])
|
|
236
|
+
.pipe(Effect.map((rows) => {
|
|
237
|
+
const r = rows[0];
|
|
238
|
+
return r ? Option.some(r.result) : Option.none();
|
|
239
|
+
}));
|
|
240
|
+
const createDeferred = (executionId, name, encoded) => exec(`INSERT INTO "${deferredTable}" (execution_id, name, exit)
|
|
241
|
+
VALUES (?, ?, ?)
|
|
242
|
+
ON CONFLICT DO NOTHING
|
|
243
|
+
RETURNING execution_id`, [executionId, name, encoded])
|
|
244
|
+
.pipe(Effect.map((rows) => rows.length > 0));
|
|
245
|
+
const readDeferred = (executionId, name) => exec(`SELECT exit FROM "${deferredTable}" WHERE execution_id = ? AND name = ?`, [executionId, name])
|
|
246
|
+
.pipe(Effect.map((rows) => {
|
|
247
|
+
const r = rows[0];
|
|
248
|
+
return r ? Option.some(r.exit) : Option.none();
|
|
249
|
+
}));
|
|
250
|
+
const insertClock = (executionId, name, workflowName, deferredName, fireAt) => exec(`INSERT INTO "${clockTable}" (execution_id, name, workflow_name, deferred_name, fire_at)
|
|
251
|
+
VALUES (?, ?, ?, ?, ?)
|
|
252
|
+
ON CONFLICT DO NOTHING
|
|
253
|
+
RETURNING execution_id`, [executionId, name, workflowName, deferredName, fireAt])
|
|
254
|
+
.pipe(Effect.map((rows) => rows.length > 0));
|
|
255
|
+
const deleteClock = (executionId, name) => exec(`DELETE FROM "${clockTable}" WHERE execution_id = ? AND name = ?`, [executionId, name]);
|
|
256
|
+
// --- Workflow result helpers ------------------------------------------
|
|
257
|
+
const completeResult = (workflow, state) => state.status === "complete" && state.completedResult
|
|
258
|
+
? Effect.map(decodeResult(workflow, state.completedResult), Option.some)
|
|
259
|
+
: Effect.succeedNone;
|
|
260
|
+
// --- Lease / claim ----------------------------------------------------
|
|
261
|
+
const leaseActive = (state, now) => state.worker !== undefined
|
|
262
|
+
&& state.worker !== workerId
|
|
263
|
+
&& state.leaseExpiresAt !== undefined
|
|
264
|
+
&& state.leaseExpiresAt > now;
|
|
265
|
+
const tryClaim = (state) => Effect.gen(function* () {
|
|
266
|
+
const now = Date.now();
|
|
267
|
+
if (leaseActive(state, now))
|
|
268
|
+
return Option.none();
|
|
269
|
+
return yield* replaceExec(state, {
|
|
270
|
+
worker: workerId,
|
|
271
|
+
leaseExpiresAt: now + Duration.toMillis(leaseTtl)
|
|
272
|
+
})
|
|
273
|
+
.pipe(Effect.map(Option.some), Effect.catchTag("OptimisticConcurrencyException", () => Effect.succeed(Option.none())));
|
|
274
|
+
});
|
|
275
|
+
const heartbeat = (executionId) => Effect.gen(function* () {
|
|
276
|
+
while (true) {
|
|
277
|
+
yield* Effect.sleep(heartbeatInterval);
|
|
278
|
+
const local = locals.get(executionId);
|
|
279
|
+
const polled = local?.fiber?.pollUnsafe();
|
|
280
|
+
if (!local?.fiber || polled)
|
|
281
|
+
return;
|
|
282
|
+
const cur = yield* readExec(executionId).pipe(Effect.catchCause(() => Effect.succeed(Option.none())));
|
|
283
|
+
if (Option.isNone(cur))
|
|
284
|
+
continue;
|
|
285
|
+
const state = cur.value;
|
|
286
|
+
if (state.status === "complete" || state.worker !== workerId)
|
|
287
|
+
return;
|
|
288
|
+
yield* replaceExec(state, {
|
|
289
|
+
leaseExpiresAt: Date.now() + Duration.toMillis(leaseTtl)
|
|
290
|
+
})
|
|
291
|
+
.pipe(Effect.catchTag("OptimisticConcurrencyException", () => Effect.void), Effect.catchCause(() => Effect.void));
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
// --- Drive logic ------------------------------------------------------
|
|
295
|
+
const drive = (executionId, payload, parent, entry) => Effect.gen(function* () {
|
|
296
|
+
let local = locals.get(executionId);
|
|
297
|
+
if (local?.fiber) {
|
|
298
|
+
const polled = local.fiber.pollUnsafe();
|
|
299
|
+
const stillRunning = !polled;
|
|
300
|
+
const completedNotResume = polled && polled._tag === "Success" && polled.value._tag === "Complete";
|
|
301
|
+
if (stillRunning || completedNotResume)
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const stateOpt = yield* readExec(executionId);
|
|
305
|
+
if (Option.isNone(stateOpt) || stateOpt.value.status === "complete")
|
|
306
|
+
return;
|
|
307
|
+
const claimed = yield* tryClaim(stateOpt.value);
|
|
308
|
+
const state = Option.isSome(claimed) ? claimed.value : stateOpt.value;
|
|
309
|
+
const instance = WorkflowInstance.initial(entry.workflow, executionId);
|
|
310
|
+
instance.interrupted = state.interrupted;
|
|
311
|
+
if (!local) {
|
|
312
|
+
local = { instance, fiber: undefined, parent };
|
|
313
|
+
locals.set(executionId, local);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
local.instance = instance;
|
|
317
|
+
}
|
|
318
|
+
const onComplete = Effect.fnUntraced(function* (result) {
|
|
319
|
+
const current = yield* readExec(executionId);
|
|
320
|
+
if (Option.isNone(current) || current.value.status === "complete")
|
|
321
|
+
return;
|
|
322
|
+
const isComplete = result._tag === "Complete";
|
|
323
|
+
const completedResult = isComplete ? yield* encodeResult(entry.workflow, result) : undefined;
|
|
324
|
+
yield* replaceExec(current.value, {
|
|
325
|
+
status: isComplete ? "complete" : current.value.status,
|
|
326
|
+
suspended: result._tag === "Suspended",
|
|
327
|
+
interrupted: instance.interrupted,
|
|
328
|
+
completedResult,
|
|
329
|
+
worker: isComplete ? undefined : current.value.worker,
|
|
330
|
+
leaseExpiresAt: isComplete ? undefined : current.value.leaseExpiresAt
|
|
331
|
+
})
|
|
332
|
+
.pipe(Effect.catchTag("OptimisticConcurrencyException", () => Effect.void));
|
|
333
|
+
if (parent && isComplete) {
|
|
334
|
+
yield* Effect.forkIn(driveById(parent), scope);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
local.fiber = yield* entry.execute(payload, executionId).pipe(Effect.onExit(() => {
|
|
338
|
+
if (!instance.interrupted)
|
|
339
|
+
return Effect.void;
|
|
340
|
+
instance.suspended = false;
|
|
341
|
+
return Effect.withFiber((fiber) => Effect.interruptible(Fiber.interrupt(fiber)));
|
|
342
|
+
}), Workflow.intoResult, Effect.provideService(WorkflowInstance, instance), Effect.provideService(WorkflowEngine, engine), Effect.tap(onComplete), Effect.forkIn(entry.scope));
|
|
343
|
+
if (Option.isSome(claimed)) {
|
|
344
|
+
yield* Effect.forkIn(heartbeat(executionId), scope);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
const driveById = (executionId) => Effect.gen(function* () {
|
|
348
|
+
const stateOpt = yield* readExec(executionId);
|
|
349
|
+
if (Option.isNone(stateOpt))
|
|
350
|
+
return;
|
|
351
|
+
const state = stateOpt.value;
|
|
352
|
+
const entry = workflows.get(state.workflowName);
|
|
353
|
+
if (!entry)
|
|
354
|
+
return;
|
|
355
|
+
const payload = yield* decodePayload(entry.workflow, state.payload);
|
|
356
|
+
yield* drive(executionId, payload, state.parent, entry);
|
|
357
|
+
});
|
|
358
|
+
// --- Clock firing -----------------------------------------------------
|
|
359
|
+
const fireClock = (executionId, name, deferredName) => Effect
|
|
360
|
+
.gen(function* () {
|
|
361
|
+
const encoded = yield* encodeDeferredExit(Exit.void);
|
|
362
|
+
const inserted = yield* sql
|
|
363
|
+
.withTransaction(Effect.gen(function* () {
|
|
364
|
+
const got = yield* createDeferred(executionId, deferredName, encoded);
|
|
365
|
+
yield* deleteClock(executionId, name);
|
|
366
|
+
return got;
|
|
367
|
+
}))
|
|
368
|
+
.pipe(Effect.orDie);
|
|
369
|
+
if (inserted)
|
|
370
|
+
yield* driveById(executionId);
|
|
371
|
+
})
|
|
372
|
+
.pipe(annotate("clockFire", executionId));
|
|
373
|
+
// --- Encoded engine ---------------------------------------------------
|
|
374
|
+
const encoded = {
|
|
375
|
+
register: Effect.fnUntraced(function* (workflow, execute) {
|
|
376
|
+
workflows.set(workflow.name, {
|
|
377
|
+
workflow,
|
|
378
|
+
execute,
|
|
379
|
+
scope: yield* Effect.scope
|
|
380
|
+
});
|
|
381
|
+
}),
|
|
382
|
+
execute: Effect.fnUntraced(function* (workflow, options) {
|
|
383
|
+
const entry = workflows.get(workflow.name);
|
|
384
|
+
if (!entry) {
|
|
385
|
+
return yield* Effect.orDie(Effect.fail(`Workflow ${workflow.name} is not registered`));
|
|
386
|
+
}
|
|
387
|
+
const initial = {
|
|
388
|
+
executionId: options.executionId,
|
|
389
|
+
workflowName: workflow.name,
|
|
390
|
+
payload: yield* encodePayload(workflow, options.payload),
|
|
391
|
+
parent: options.parent?.executionId,
|
|
392
|
+
status: "running",
|
|
393
|
+
suspended: false,
|
|
394
|
+
interrupted: false,
|
|
395
|
+
completedResult: undefined,
|
|
396
|
+
worker: undefined,
|
|
397
|
+
leaseExpiresAt: undefined,
|
|
398
|
+
etag: randomUUID()
|
|
399
|
+
};
|
|
400
|
+
yield* createExec(initial);
|
|
401
|
+
yield* drive(options.executionId, options.payload, options.parent?.executionId, entry);
|
|
402
|
+
if (options.discard)
|
|
403
|
+
return undefined;
|
|
404
|
+
const local = locals.get(options.executionId);
|
|
405
|
+
if (local?.fiber) {
|
|
406
|
+
return (yield* Fiber.join(local.fiber));
|
|
407
|
+
}
|
|
408
|
+
// Foreign-driver fallback: poll the persisted result until completion.
|
|
409
|
+
while (true) {
|
|
410
|
+
const cur = yield* readExec(options.executionId);
|
|
411
|
+
if (Option.isSome(cur)) {
|
|
412
|
+
const r = yield* completeResult(workflow, cur.value);
|
|
413
|
+
if (Option.isSome(r))
|
|
414
|
+
return r.value;
|
|
415
|
+
}
|
|
416
|
+
yield* Effect.sleep(Duration.millis(500));
|
|
417
|
+
}
|
|
418
|
+
}),
|
|
419
|
+
poll: (workflow, executionId) => Effect.gen(function* () {
|
|
420
|
+
const local = locals.get(executionId);
|
|
421
|
+
if (local?.fiber) {
|
|
422
|
+
const exitVal = local.fiber.pollUnsafe();
|
|
423
|
+
if (!exitVal)
|
|
424
|
+
return Option.none();
|
|
425
|
+
if (exitVal._tag !== "Success")
|
|
426
|
+
return yield* Effect.die(exitVal.cause);
|
|
427
|
+
return Option.some(exitVal.value);
|
|
428
|
+
}
|
|
429
|
+
const state = yield* readExec(executionId);
|
|
430
|
+
if (Option.isNone(state))
|
|
431
|
+
return Option.none();
|
|
432
|
+
return yield* completeResult(workflow, state.value);
|
|
433
|
+
}),
|
|
434
|
+
interrupt: Effect.fnUntraced(function* (_workflow, executionId) {
|
|
435
|
+
const local = locals.get(executionId);
|
|
436
|
+
if (local)
|
|
437
|
+
local.instance.interrupted = true;
|
|
438
|
+
const current = yield* readExec(executionId);
|
|
439
|
+
if (Option.isNone(current) || current.value.status === "complete")
|
|
440
|
+
return;
|
|
441
|
+
yield* replaceExec(current.value, { interrupted: true }).pipe(Effect.catchTag("OptimisticConcurrencyException", () => Effect.void));
|
|
442
|
+
yield* driveById(executionId);
|
|
443
|
+
}),
|
|
444
|
+
interruptUnsafe: Effect.fnUntraced(function* (_workflow, executionId) {
|
|
445
|
+
const local = locals.get(executionId);
|
|
446
|
+
if (local)
|
|
447
|
+
local.instance.interrupted = true;
|
|
448
|
+
const current = yield* readExec(executionId);
|
|
449
|
+
if (Option.isSome(current) && current.value.status !== "complete") {
|
|
450
|
+
yield* replaceExec(current.value, { interrupted: true }).pipe(Effect.catchTag("OptimisticConcurrencyException", () => Effect.void));
|
|
451
|
+
}
|
|
452
|
+
if (local?.fiber)
|
|
453
|
+
yield* Fiber.interrupt(local.fiber);
|
|
454
|
+
}),
|
|
455
|
+
resume: (_workflow, executionId) => driveById(executionId),
|
|
456
|
+
activityExecute: Effect.fnUntraced(function* (activity, attempt) {
|
|
457
|
+
const instance = yield* WorkflowInstance;
|
|
458
|
+
const existing = yield* readActivity(instance.executionId, activity.name, attempt);
|
|
459
|
+
if (Option.isSome(existing)) {
|
|
460
|
+
const prev = yield* decodeActivityResult(existing.value);
|
|
461
|
+
// A completed activity is replayed from its persisted result; a
|
|
462
|
+
// suspended one must re-run (it parked on a clock/deferred).
|
|
463
|
+
if (prev._tag === "Complete")
|
|
464
|
+
return prev;
|
|
465
|
+
}
|
|
466
|
+
const activityInstance = WorkflowInstance.initial(instance.workflow, instance.executionId);
|
|
467
|
+
activityInstance.interrupted = instance.interrupted;
|
|
468
|
+
const result = yield* activity.executeEncoded.pipe(Workflow.intoResult, Effect.provideService(WorkflowInstance, activityInstance));
|
|
469
|
+
const encodedResult = yield* encodeActivityResult(result);
|
|
470
|
+
if (Option.isSome(existing)) {
|
|
471
|
+
// Overwrite the previously persisted *suspended* result.
|
|
472
|
+
yield* upsertActivity(instance.executionId, activity.name, attempt, encodedResult);
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
// First-writer-wins: if persistence loses the race, use the persisted result.
|
|
476
|
+
const persisted = yield* createActivity(instance.executionId, activity.name, attempt, encodedResult);
|
|
477
|
+
if (persisted)
|
|
478
|
+
return result;
|
|
479
|
+
const winner = yield* readActivity(instance.executionId, activity.name, attempt);
|
|
480
|
+
if (Option.isSome(winner)) {
|
|
481
|
+
const w = yield* decodeActivityResult(winner.value);
|
|
482
|
+
if (w._tag === "Complete")
|
|
483
|
+
return w;
|
|
484
|
+
}
|
|
485
|
+
return result;
|
|
486
|
+
}),
|
|
487
|
+
deferredResult: Effect.fnUntraced(function* (deferred) {
|
|
488
|
+
const instance = yield* WorkflowInstance;
|
|
489
|
+
const got = yield* readDeferred(instance.executionId, deferred.name);
|
|
490
|
+
if (Option.isNone(got))
|
|
491
|
+
return Option.none();
|
|
492
|
+
return Option.some(yield* decodeDeferredExit(got.value));
|
|
493
|
+
}),
|
|
494
|
+
deferredDone: Effect.fnUntraced(function* (options) {
|
|
495
|
+
const encoded = yield* encodeDeferredExit(options.exit);
|
|
496
|
+
const inserted = yield* createDeferred(options.executionId, options.deferredName, encoded);
|
|
497
|
+
if (!inserted)
|
|
498
|
+
return;
|
|
499
|
+
yield* driveById(options.executionId);
|
|
500
|
+
}),
|
|
501
|
+
scheduleClock: (workflow, options) => {
|
|
502
|
+
const fireAt = Date.now() + Duration.toMillis(options.clock.duration);
|
|
503
|
+
return Effect.gen(function* () {
|
|
504
|
+
yield* insertClock(options.executionId, options.clock.name, workflow.name, options.clock.deferred.name, fireAt);
|
|
505
|
+
yield* fireClock(options.executionId, options.clock.name, options.clock.deferred.name).pipe(Effect.delay(options.clock.duration), FiberMap.run(clocks, `${options.executionId}/${options.clock.name}`, { onlyIfMissing: true }), Effect.asVoid);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
const engine = makeUnsafe(encoded);
|
|
510
|
+
// --- Recovery poller --------------------------------------------------
|
|
511
|
+
if (Duration.toMillis(recoveryInterval) > 0) {
|
|
512
|
+
const recoverStep = Effect
|
|
513
|
+
.gen(function* () {
|
|
514
|
+
const rows = yield* exec(`SELECT execution_id, workflow_name FROM "${execTable}"
|
|
515
|
+
WHERE status = 'running' AND (lease_expires_at IS NULL OR lease_expires_at <= ?)
|
|
516
|
+
LIMIT 100`, [Date.now()]);
|
|
517
|
+
for (const row of rows) {
|
|
518
|
+
if (!workflows.has(row.workflow_name))
|
|
519
|
+
continue;
|
|
520
|
+
const local = locals.get(row.execution_id);
|
|
521
|
+
if (local?.fiber && !local.fiber.pollUnsafe())
|
|
522
|
+
continue;
|
|
523
|
+
yield* Effect.forkIn(driveById(row.execution_id), scope);
|
|
524
|
+
}
|
|
525
|
+
})
|
|
526
|
+
.pipe(annotate("recoveryScan"), Effect.catchCause(() => Effect.void));
|
|
527
|
+
yield* recoverStep.pipe(Effect.repeat(Schedule.spaced(recoveryInterval)), Effect.forkIn(scope));
|
|
528
|
+
}
|
|
529
|
+
// --- Clock poller -----------------------------------------------------
|
|
530
|
+
if (Duration.toMillis(clockPollInterval) > 0) {
|
|
531
|
+
const clockStep = Effect
|
|
532
|
+
.gen(function* () {
|
|
533
|
+
const rows = yield* exec(`SELECT execution_id, name, deferred_name FROM "${clockTable}"
|
|
534
|
+
WHERE fire_at <= ?
|
|
535
|
+
LIMIT 100`, [Date.now()]);
|
|
536
|
+
for (const row of rows) {
|
|
537
|
+
yield* Effect.forkIn(fireClock(row.execution_id, row.name, row.deferred_name), scope);
|
|
538
|
+
}
|
|
539
|
+
})
|
|
540
|
+
.pipe(annotate("clockScan"), Effect.catchCause(() => Effect.void));
|
|
541
|
+
yield* clockStep.pipe(Effect.repeat(Schedule.spaced(clockPollInterval)), Effect.forkIn(scope));
|
|
542
|
+
}
|
|
543
|
+
return engine;
|
|
544
|
+
});
|
|
545
|
+
/**
|
|
546
|
+
* SQLite backed `WorkflowEngine` layer. Requires an ambient `SqlClient`
|
|
547
|
+
* (`@effect/sql-sqlite-node` or a compatible client).
|
|
548
|
+
*/
|
|
549
|
+
export const layerSqlite = (cfg = {}) => Layer.effect(WorkflowEngine)(makeSqliteWorkflowEngine(cfg));
|
|
550
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV29ya2Zsb3dFbmdpbmVTcWxpdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvV29ya2Zsb3dFbmdpbmVTcWxpdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQ0c7QUFDSCxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFBO0FBQzNDLE9BQU8sS0FBSyxLQUFLLE1BQU0sa0JBQWtCLENBQUE7QUFDekMsT0FBTyxLQUFLLE1BQU0sTUFBTSxtQkFBbUIsQ0FBQTtBQUMzQyxPQUFPLEtBQUssQ0FBQyxNQUFNLG1CQUFtQixDQUFBO0FBQ3RDLE9BQU8sS0FBSyxRQUFRLE1BQU0saUJBQWlCLENBQUE7QUFDM0MsT0FBTyxLQUFLLElBQUksTUFBTSxhQUFhLENBQUE7QUFDbkMsT0FBTyxLQUFLLEtBQUssTUFBTSxjQUFjLENBQUE7QUFDckMsT0FBTyxLQUFLLFFBQVEsTUFBTSxpQkFBaUIsQ0FBQTtBQUMzQyxPQUFPLEtBQUssUUFBUSxNQUFNLGlCQUFpQixDQUFBO0FBRTNDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQTtBQUMvQyxPQUFPLEtBQUssUUFBUSxNQUFNLG1DQUFtQyxDQUFBO0FBQzdELE9BQU8sRUFBZ0IsVUFBVSxFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHlDQUF5QyxDQUFBO0FBQ3BILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDeEMsT0FBTyxFQUFFLDhCQUE4QixFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQzVELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFpRHRDLE1BQU0sU0FBUyxHQUFHLENBQUMsR0FBWSxFQUFhLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLFdBQVcsRUFBRSxHQUFHLENBQUMsWUFBWTtJQUM3QixZQUFZLEVBQUUsR0FBRyxDQUFDLGFBQWE7SUFD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
|