@effect-app/infra 4.0.0-beta.256 → 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 +18 -0
- package/dist/WorkflowEngineCosmos.d.ts +29 -0
- package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
- package/dist/WorkflowEngineCosmos.js +521 -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/_check-agg-infer.test-d.d.ts +2 -0
- package/test/dist/_check-agg-infer.test-d.d.ts.map +1 -0
- package/test/dist/_check-agg-infer.test-d.js +19 -0
- package/test/dist/_check-proj-infer.test-d.d.ts +2 -0
- package/test/dist/_check-proj-infer.test-d.d.ts.map +1 -0
- package/test/dist/_check-proj-infer.test-d.js +16 -0
- package/test/dist/_check-tighten.test-d.d.ts +2 -0
- package/test/dist/_check-tighten.test-d.d.ts.map +1 -0
- package/test/dist/_check-tighten.test-d.js +21 -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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @effect-app/infra
|
|
2
2
|
|
|
3
|
+
## 4.0.0-beta.258
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0520dd4: Add Cosmos DB backed `WorkflowEngine` adapter (`layerCosmos` in `WorkflowEngineCosmos.ts`). Persists workflow state in a single container partitioned by `executionId` so per-execution writes share a partition key (TransactionalBatch-eligible). Optimistic concurrency via `_etag` + `IfMatch` on Replace; first-writer-wins via create-only batch ops for activity results and durable-deferred completions; a persisted _suspended_ activity is overwritten via upsert on resume. Values crossing the storage boundary round-trip through `Schema` codecs (`S.fromJsonString(S.toCodecJson(...))`) using the workflow's own `payloadSchema` / `successSchema` / `errorSchema` for typed values, and the cluster engine's opaque `AnyOrVoid` codec for activity / deferred payloads. Includes time-bound lease + heartbeat fiber, scope-bound recovery poller for crashed-driver takeover, and cross-partition clock poller for restart-survivable durable timers.
|
|
8
|
+
- 0ddda6f: Add SQLite backed `WorkflowEngine` adapter (`layerSqlite` in `WorkflowEngineSqlite.ts`). Persists workflow state across `workflow_exec` / `workflow_activity` / `workflow_deferred` / `workflow_clock` tables via `SqlClient`. Uses `sql.withTransaction` for atomicity, etag-based optimistic concurrency (`UPDATE ... WHERE etag = ? RETURNING etag`), and `INSERT ... ON CONFLICT DO NOTHING RETURNING` for first-writer-wins on activity / deferred / clock writes. Values crossing the storage boundary round-trip through `Schema` codecs (`S.fromJsonString(S.toCodecJson(...))`) using the workflow's own `payloadSchema` / `successSchema` / `errorSchema` for typed values, and the cluster engine's opaque `AnyOrVoid` codec for activity / deferred payloads. Includes time-bound lease + heartbeat fiber, scope-bound recovery poller for crashed-driver takeover, and cross-partition clock poller for restart-survivable durable timers.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- effect-app@4.0.0-beta.258
|
|
13
|
+
|
|
14
|
+
## 4.0.0-beta.257
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [e71eb78]
|
|
19
|
+
- effect-app@4.0.0-beta.257
|
|
20
|
+
|
|
3
21
|
## 4.0.0-beta.256
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as Layer from "effect-app/Layer";
|
|
2
|
+
import * as Duration from "effect/Duration";
|
|
3
|
+
import * as Redacted from "effect/Redacted";
|
|
4
|
+
import { WorkflowEngine } from "effect/unstable/workflow/WorkflowEngine";
|
|
5
|
+
export interface WorkflowEngineCosmosConfig {
|
|
6
|
+
readonly url: Redacted.Redacted<string>;
|
|
7
|
+
readonly dbName: string;
|
|
8
|
+
readonly prefix?: string;
|
|
9
|
+
/** Lease duration before claim considered stale. Default 30s. */
|
|
10
|
+
readonly leaseTtl?: Duration.Duration;
|
|
11
|
+
/** Renewal cadence — should be < leaseTtl. Default 10s. */
|
|
12
|
+
readonly heartbeatInterval?: Duration.Duration;
|
|
13
|
+
/** Cadence for scanning stale leases. Default 15s. Set to `Duration.zero` to disable. */
|
|
14
|
+
readonly recoveryInterval?: Duration.Duration;
|
|
15
|
+
/** Cadence for scanning due clocks. Default 5s. Set to `Duration.zero` to disable. */
|
|
16
|
+
readonly clockPollInterval?: Duration.Duration;
|
|
17
|
+
/** Stable worker identity; defaults to a random UUID per process. */
|
|
18
|
+
readonly workerId?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Cosmos DB backed `WorkflowEngine` layer.
|
|
22
|
+
*
|
|
23
|
+
* Per-execution writes share a partition key (TransactionalBatch-eligible) and
|
|
24
|
+
* use OCC via `_etag`/IfMatch, giving first-writer-wins semantics for activity
|
|
25
|
+
* results, durable-deferred completions, and exec-state transitions. All
|
|
26
|
+
* persisted payloads/results/exits are round-tripped through schema codecs.
|
|
27
|
+
*/
|
|
28
|
+
export declare const layerCosmos: (cfg: WorkflowEngineCosmosConfig) => Layer.Layer<WorkflowEngine>;
|
|
29
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV29ya2Zsb3dFbmdpbmVDb3Ntb3MuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9Xb3JrZmxvd0VuZ2luZUNvc21vcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFpQ0EsT0FBTyxLQUFLLEtBQUssTUFBTSxrQkFBa0IsQ0FBQTtBQUd6QyxPQUFPLEtBQUssUUFBUSxNQUFNLGlCQUFpQixDQUFBO0FBSTNDLE9BQU8sS0FBSyxRQUFRLE1BQU0saUJBQWlCLENBQUE7QUFJM0MsT0FBTyxFQUE0QixjQUFjLEVBQW9CLE1BQU0seUNBQXlDLENBQUE7QUFNcEgsTUFBTSxXQUFXLDBCQUEwQjtJQUN6QyxRQUFRLENBQUMsR0FBRyxFQUFFLFFBQVEsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUE7SUFDdkMsUUFBUSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUE7SUFDdkIsUUFBUSxDQUFDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQTtJQUN4QixpRUFBaUU7SUFDakUsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxRQUFRLENBQUE7SUFDckMsMkRBQTJEO0lBQzNELFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxRQUFRLENBQUE7SUFDOUMseUZBQXlGO0lBQ3pGLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxRQUFRLENBQUE7SUFDN0Msc0ZBQXNGO0lBQ3RGLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxRQUFRLENBQUE7SUFDOUMscUVBQXFFO0lBQ3JFLFFBQVEsQ0FBQyxRQUFRLENBQUMsRUFBRSxNQUFNLENBQUE7Q0FDM0I7QUFtb0JEOzs7Ozs7O0dBT0c7QUFDSCxlQUFPLE1BQU0sV0FBVyxRQUFTLDBCQUEwQixLQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUdSLENBQUEifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkflowEngineCosmos.d.ts","sourceRoot":"","sources":["../src/WorkflowEngineCosmos.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAA;AAGzC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAI3C,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAI3C,OAAO,EAA4B,cAAc,EAAoB,MAAM,yCAAyC,CAAA;AAMpH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAA;IACrC,2DAA2D;IAC3D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAA;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAA;IAC7C,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAA;IAC9C,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAmoBD;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,QAAS,0BAA0B,KAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAGR,CAAA"}
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cosmos DB backed {@link WorkflowEngine} implementation.
|
|
3
|
+
*
|
|
4
|
+
* Persists workflow state in a single container partitioned by `executionId`
|
|
5
|
+
* so per-execution writes share a partition key (eligible for Cosmos
|
|
6
|
+
* TransactionalBatch). Optimistic concurrency is enforced with `_etag` +
|
|
7
|
+
* `IfMatch` on Replace, and create-only batch ops give first-writer-wins
|
|
8
|
+
* semantics for activity results and durable-deferred completions.
|
|
9
|
+
*
|
|
10
|
+
* Durability — everything that crosses the storage boundary is round-tripped
|
|
11
|
+
* through schema codecs (`S.fromJsonString(S.toCodecJson(...))`), exactly like
|
|
12
|
+
* the cluster engine, instead of dumping live runtime objects as JSON:
|
|
13
|
+
*
|
|
14
|
+
* - The workflow payload and the top-level workflow result are encoded with the
|
|
15
|
+
* workflow's own `payloadSchema` / `successSchema` / `errorSchema`, so typed
|
|
16
|
+
* values (dates, branded ids, schema classes) survive a restart.
|
|
17
|
+
* - Activity results flow through the engine already encoded, so they are
|
|
18
|
+
* persisted with an opaque `Workflow.Result({ success: AnyOrVoid, error:
|
|
19
|
+
* AnyOrVoid })` codec — same trick the cluster `ActivityRpc` uses.
|
|
20
|
+
* - Durable-deferred exits and clock completions use an opaque `Exit` codec.
|
|
21
|
+
*
|
|
22
|
+
* Crash recovery: each driver holds a time-bound lease (`worker` +
|
|
23
|
+
* `leaseExpiresAt`) on the exec doc and renews it via a heartbeat fiber. A
|
|
24
|
+
* scope-bound recovery poller queries for exec docs whose lease has lapsed and
|
|
25
|
+
* re-drives them in the local process, decoding the persisted payload and
|
|
26
|
+
* picking up persisted activity results from where the crashed driver left off.
|
|
27
|
+
*
|
|
28
|
+
* Durable clocks: `scheduleClock` writes a clock doc (`fireAt`, `deferredName`)
|
|
29
|
+
* and arms an in-process timer. A cross-partition clock poller fires any clock
|
|
30
|
+
* whose `fireAt` is due, completing the deferred idempotently (create-only) and
|
|
31
|
+
* deleting the doc. Survives restarts.
|
|
32
|
+
*/
|
|
33
|
+
import * as Effect from "effect-app/Effect";
|
|
34
|
+
import * as Layer from "effect-app/Layer";
|
|
35
|
+
import * as Option from "effect-app/Option";
|
|
36
|
+
import * as S from "effect-app/Schema";
|
|
37
|
+
import * as Duration from "effect/Duration";
|
|
38
|
+
import * as Exit from "effect/Exit";
|
|
39
|
+
import * as Fiber from "effect/Fiber";
|
|
40
|
+
import * as FiberMap from "effect/FiberMap";
|
|
41
|
+
import * as Redacted from "effect/Redacted";
|
|
42
|
+
import * as Schedule from "effect/Schedule";
|
|
43
|
+
import * as Workflow from "effect/unstable/workflow/Workflow";
|
|
44
|
+
import { makeUnsafe, WorkflowEngine, WorkflowInstance } from "effect/unstable/workflow/WorkflowEngine";
|
|
45
|
+
import { randomUUID } from "node:crypto";
|
|
46
|
+
import { CosmosClient, CosmosClientLayer } from "./cosmos-client.js";
|
|
47
|
+
import { OptimisticConcurrencyException } from "./errors.js";
|
|
48
|
+
import { annotateCosmosResponse, annotateDb } from "./otel.js";
|
|
49
|
+
const execId = "exec";
|
|
50
|
+
const activityKey = (name, attempt) => `activity::${name}::${attempt}`;
|
|
51
|
+
const deferredKey = (name) => `deferred::${name}`;
|
|
52
|
+
const clockKey = (name) => `clock::${name}`;
|
|
53
|
+
const isOptimisticStatus = (code) => code === 409 || code === 412 || code === 404;
|
|
54
|
+
// --- Storage codecs ----------------------------------------------------------
|
|
55
|
+
// Values flowing through the engine's activity / deferred boundary are already
|
|
56
|
+
// schema-encoded, so the structure is round-tripped while the payload stays
|
|
57
|
+
// opaque (mirrors the cluster engine's `AnyOrVoid` usage).
|
|
58
|
+
const AnyOrVoid = S.Union([S.Any, S.Void]);
|
|
59
|
+
const ActivityResultCodec = S.fromJsonString(S.toCodecJson(Workflow.Result({ success: AnyOrVoid, error: AnyOrVoid })));
|
|
60
|
+
const DeferredExitCodec = S.fromJsonString(S.toCodecJson(S.Exit(AnyOrVoid, AnyOrVoid, S.Defect)));
|
|
61
|
+
const encodeActivityResult = (r) => Effect.orDie(S.encodeEffect(ActivityResultCodec)(r));
|
|
62
|
+
const decodeActivityResult = (s) => Effect.orDie(S.decodeEffect(ActivityResultCodec)(s));
|
|
63
|
+
const encodeDeferredExit = (e) => Effect.orDie(S.encodeEffect(DeferredExitCodec)(e));
|
|
64
|
+
const decodeDeferredExit = (s) => Effect.orDie(S.decodeEffect(DeferredExitCodec)(s));
|
|
65
|
+
const makeCosmosWorkflowEngine = Effect.fnUntraced(function* (cfg) {
|
|
66
|
+
const { db } = yield* CosmosClient;
|
|
67
|
+
const containerId = `${cfg.prefix ?? ""}workflow-engine`;
|
|
68
|
+
yield* Effect.promise(() => db.containers.createIfNotExists({
|
|
69
|
+
id: containerId,
|
|
70
|
+
partitionKey: { paths: ["/_partitionKey"], version: 2 }
|
|
71
|
+
}));
|
|
72
|
+
const container = db.container(containerId);
|
|
73
|
+
const scope = yield* Effect.scope;
|
|
74
|
+
const workerId = cfg.workerId ?? randomUUID();
|
|
75
|
+
const leaseTtl = cfg.leaseTtl ?? Duration.seconds(30);
|
|
76
|
+
const heartbeatInterval = cfg.heartbeatInterval ?? Duration.seconds(10);
|
|
77
|
+
const recoveryInterval = cfg.recoveryInterval ?? Duration.seconds(15);
|
|
78
|
+
const clockPollInterval = cfg.clockPollInterval ?? Duration.seconds(5);
|
|
79
|
+
const annotate = (operation, executionId) => annotateDb({
|
|
80
|
+
operation,
|
|
81
|
+
system: "cosmosdb",
|
|
82
|
+
collection: containerId,
|
|
83
|
+
entity: "workflow",
|
|
84
|
+
extra: executionId !== undefined
|
|
85
|
+
? { "azure.cosmosdb.operation.partition_key": executionId, "app.entity.id": executionId }
|
|
86
|
+
: undefined
|
|
87
|
+
});
|
|
88
|
+
const workflows = new Map();
|
|
89
|
+
const locals = new Map();
|
|
90
|
+
const clocks = yield* FiberMap.make();
|
|
91
|
+
// Per-workflow codecs for the typed payload + top-level result. Cached by
|
|
92
|
+
// workflow name; derived from the workflow's own schemas so typed values
|
|
93
|
+
// (dates, branded ids, schema classes) survive the storage round-trip.
|
|
94
|
+
const makePayloadCodec = (workflow) => S.fromJsonString(S.toCodecJson(workflow.payloadSchema));
|
|
95
|
+
const payloadCodecCache = new Map();
|
|
96
|
+
const payloadCodecFor = (workflow) => {
|
|
97
|
+
let c = payloadCodecCache.get(workflow.name);
|
|
98
|
+
if (!c) {
|
|
99
|
+
c = makePayloadCodec(workflow);
|
|
100
|
+
payloadCodecCache.set(workflow.name, c);
|
|
101
|
+
}
|
|
102
|
+
return c;
|
|
103
|
+
};
|
|
104
|
+
const makeResultCodec = (workflow) => S.fromJsonString(S.toCodecJson(Workflow.Result({ success: workflow.successSchema, error: workflow.errorSchema })));
|
|
105
|
+
const resultCodecCache = new Map();
|
|
106
|
+
const resultCodecFor = (workflow) => {
|
|
107
|
+
let c = resultCodecCache.get(workflow.name);
|
|
108
|
+
if (!c) {
|
|
109
|
+
c = makeResultCodec(workflow);
|
|
110
|
+
resultCodecCache.set(workflow.name, c);
|
|
111
|
+
}
|
|
112
|
+
return c;
|
|
113
|
+
};
|
|
114
|
+
const encodePayload = (workflow, payload) => Effect.orDie(S.encodeEffect(payloadCodecFor(workflow))(payload));
|
|
115
|
+
const decodePayload = (workflow, s) => Effect.orDie(S.decodeEffect(payloadCodecFor(workflow))(s));
|
|
116
|
+
const encodeResult = (workflow, r) => Effect.orDie(S.encodeEffect(resultCodecFor(workflow))(r));
|
|
117
|
+
const decodeResult = (workflow, s) => Effect.orDie(S.decodeEffect(resultCodecFor(workflow))(s));
|
|
118
|
+
// --- Cosmos primitives -------------------------------------------------
|
|
119
|
+
const readExec = (executionId) => Effect
|
|
120
|
+
.gen(function* () {
|
|
121
|
+
const resp = yield* Effect.promise(() => container.item(execId, executionId).read());
|
|
122
|
+
yield* annotateCosmosResponse({ requestCharge: resp.requestCharge, statusCode: resp.statusCode });
|
|
123
|
+
return Option.fromNullishOr(resp.resource).pipe(Option.map((r) => ({ ...r, _etag: resp.etag })));
|
|
124
|
+
})
|
|
125
|
+
.pipe(annotate("readExec", executionId));
|
|
126
|
+
const replaceExec = (doc) => Effect
|
|
127
|
+
.gen(function* () {
|
|
128
|
+
const resp = yield* Effect.promise(() => container.item(execId, doc._partitionKey).replace(doc, {
|
|
129
|
+
accessCondition: { type: "IfMatch", condition: doc._etag ?? "" }
|
|
130
|
+
}));
|
|
131
|
+
yield* annotateCosmosResponse({ requestCharge: resp.requestCharge, statusCode: resp.statusCode });
|
|
132
|
+
if (isOptimisticStatus(resp.statusCode)) {
|
|
133
|
+
return yield* new OptimisticConcurrencyException({
|
|
134
|
+
type: "workflow.exec",
|
|
135
|
+
id: doc._partitionKey,
|
|
136
|
+
code: resp.statusCode
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return { ...doc, _etag: resp.etag };
|
|
140
|
+
})
|
|
141
|
+
.pipe(annotate("replaceExec", doc._partitionKey));
|
|
142
|
+
// Atomic create-or-noop using a single-op batch — returns true if created.
|
|
143
|
+
const createIfMissing = (body) => Effect.gen(function* () {
|
|
144
|
+
const resp = yield* Effect.promise(() => container.items.batch([{ operationType: "Create", resourceBody: body }], body._partitionKey));
|
|
145
|
+
const r = resp.result?.[0];
|
|
146
|
+
const code = r?.statusCode ?? 0;
|
|
147
|
+
if (code === 201)
|
|
148
|
+
return true;
|
|
149
|
+
if (code === 409)
|
|
150
|
+
return false;
|
|
151
|
+
return yield* Effect.die(new Error(`workflow-engine cosmos createIfMissing for ${body.id} failed: ${code}`));
|
|
152
|
+
});
|
|
153
|
+
// Last-writer-wins upsert — used to overwrite a persisted *suspended* activity
|
|
154
|
+
// result once it finally completes (create-only ops can't transition it).
|
|
155
|
+
const upsert = (body) => Effect.gen(function* () {
|
|
156
|
+
const resp = yield* Effect.promise(() => container.items.upsert(body));
|
|
157
|
+
yield* annotateCosmosResponse({ requestCharge: resp.requestCharge, statusCode: resp.statusCode });
|
|
158
|
+
});
|
|
159
|
+
const readPoint = (id, executionId) => Effect.promise(() => container.item(id, executionId).read()).pipe(Effect.map((r) => Option.fromNullishOr(r.resource)));
|
|
160
|
+
// --- Workflow result helpers ------------------------------------------
|
|
161
|
+
const completeResult = (workflow, state) => state.status === "complete" && state.completedResult
|
|
162
|
+
? Effect.map(decodeResult(workflow, state.completedResult), Option.some)
|
|
163
|
+
: Effect.succeedNone;
|
|
164
|
+
// --- Lease / claim ----------------------------------------------------
|
|
165
|
+
const leaseActive = (state, now) => state.worker !== undefined
|
|
166
|
+
&& state.worker !== workerId
|
|
167
|
+
&& state.leaseExpiresAt !== undefined
|
|
168
|
+
&& Date.parse(state.leaseExpiresAt) > now;
|
|
169
|
+
/**
|
|
170
|
+
* Try to claim a lease on `state`. Returns the updated doc on success, `None`
|
|
171
|
+
* if another worker holds an active lease, or on OCC conflict (caller may
|
|
172
|
+
* retry by re-reading).
|
|
173
|
+
*/
|
|
174
|
+
const tryClaim = (state) => Effect.gen(function* () {
|
|
175
|
+
const now = Date.now();
|
|
176
|
+
if (leaseActive(state, now))
|
|
177
|
+
return Option.none();
|
|
178
|
+
const updated = {
|
|
179
|
+
...state,
|
|
180
|
+
worker: workerId,
|
|
181
|
+
leaseExpiresAt: new Date(now + Duration.toMillis(leaseTtl)).toISOString()
|
|
182
|
+
};
|
|
183
|
+
return yield* replaceExec(updated).pipe(Effect.map(Option.some), Effect.catchTag("OptimisticConcurrencyException", () => Effect.succeed(Option.none())));
|
|
184
|
+
});
|
|
185
|
+
/**
|
|
186
|
+
* Renew lease until the local fiber stops or another worker takes the claim.
|
|
187
|
+
* Best-effort: failures are swallowed; loop simply retries on next tick.
|
|
188
|
+
*/
|
|
189
|
+
const heartbeat = (executionId) => Effect.gen(function* () {
|
|
190
|
+
while (true) {
|
|
191
|
+
yield* Effect.sleep(heartbeatInterval);
|
|
192
|
+
const local = locals.get(executionId);
|
|
193
|
+
const polled = local?.fiber?.pollUnsafe();
|
|
194
|
+
if (!local?.fiber || polled)
|
|
195
|
+
return;
|
|
196
|
+
const cur = yield* readExec(executionId).pipe(Effect.catchCause(() => Effect.succeed(Option.none())));
|
|
197
|
+
if (Option.isNone(cur))
|
|
198
|
+
continue;
|
|
199
|
+
const state = cur.value;
|
|
200
|
+
if (state.status === "complete" || state.worker !== workerId)
|
|
201
|
+
return;
|
|
202
|
+
yield* replaceExec({
|
|
203
|
+
...state,
|
|
204
|
+
leaseExpiresAt: new Date(Date.now() + Duration.toMillis(leaseTtl)).toISOString()
|
|
205
|
+
})
|
|
206
|
+
.pipe(Effect.catchTag("OptimisticConcurrencyException", () => Effect.void), Effect.catchCause(() => Effect.void));
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
// --- Drive logic -------------------------------------------------------
|
|
210
|
+
const drive = (executionId, payload, parent, entry) => Effect.gen(function* () {
|
|
211
|
+
let local = locals.get(executionId);
|
|
212
|
+
if (local?.fiber) {
|
|
213
|
+
const polled = local.fiber.pollUnsafe();
|
|
214
|
+
const stillRunning = !polled;
|
|
215
|
+
const completedNotResume = polled && polled._tag === "Success" && polled.value._tag === "Complete";
|
|
216
|
+
if (stillRunning || completedNotResume)
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const stateOpt = yield* readExec(executionId);
|
|
220
|
+
if (Option.isNone(stateOpt) || stateOpt.value.status === "complete")
|
|
221
|
+
return;
|
|
222
|
+
// Best-effort claim: takes lease so recovery poller leaves us alone.
|
|
223
|
+
// Failure is tolerated — local fiber still drives; OCC guards persisted
|
|
224
|
+
// state so split-brain stays correct.
|
|
225
|
+
const claimed = yield* tryClaim(stateOpt.value);
|
|
226
|
+
const state = Option.isSome(claimed) ? claimed.value : stateOpt.value;
|
|
227
|
+
const instance = WorkflowInstance.initial(entry.workflow, executionId);
|
|
228
|
+
instance.interrupted = state.interrupted;
|
|
229
|
+
if (!local) {
|
|
230
|
+
local = { instance, fiber: undefined, parent };
|
|
231
|
+
locals.set(executionId, local);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
local.instance = instance;
|
|
235
|
+
}
|
|
236
|
+
const onComplete = Effect.fnUntraced(function* (result) {
|
|
237
|
+
const current = yield* readExec(executionId);
|
|
238
|
+
if (Option.isNone(current) || current.value.status === "complete")
|
|
239
|
+
return;
|
|
240
|
+
const isComplete = result._tag === "Complete";
|
|
241
|
+
const completedResult = isComplete ? yield* encodeResult(entry.workflow, result) : undefined;
|
|
242
|
+
yield* replaceExec({
|
|
243
|
+
...current.value,
|
|
244
|
+
status: isComplete ? "complete" : current.value.status,
|
|
245
|
+
suspended: result._tag === "Suspended",
|
|
246
|
+
interrupted: instance.interrupted,
|
|
247
|
+
completedResult,
|
|
248
|
+
// Release lease on completion so the doc isn't seen as orphaned.
|
|
249
|
+
worker: isComplete ? undefined : current.value.worker,
|
|
250
|
+
leaseExpiresAt: isComplete ? undefined : current.value.leaseExpiresAt
|
|
251
|
+
})
|
|
252
|
+
.pipe(Effect.catchTag("OptimisticConcurrencyException", () => Effect.void));
|
|
253
|
+
if (parent && isComplete) {
|
|
254
|
+
yield* Effect.forkIn(driveById(parent), scope);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
local.fiber = yield* entry.execute(payload, executionId).pipe(Effect.onExit(() => {
|
|
258
|
+
if (!instance.interrupted)
|
|
259
|
+
return Effect.void;
|
|
260
|
+
instance.suspended = false;
|
|
261
|
+
return Effect.withFiber((fiber) => Effect.interruptible(Fiber.interrupt(fiber)));
|
|
262
|
+
}), Workflow.intoResult, Effect.provideService(WorkflowInstance, instance), Effect.provideService(WorkflowEngine, engine), Effect.tap(onComplete), Effect.forkIn(entry.scope));
|
|
263
|
+
if (Option.isSome(claimed)) {
|
|
264
|
+
yield* Effect.forkIn(heartbeat(executionId), scope);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
const driveById = (executionId) => Effect.gen(function* () {
|
|
268
|
+
const stateOpt = yield* readExec(executionId);
|
|
269
|
+
if (Option.isNone(stateOpt))
|
|
270
|
+
return;
|
|
271
|
+
const state = stateOpt.value;
|
|
272
|
+
const entry = workflows.get(state.workflowName);
|
|
273
|
+
if (!entry)
|
|
274
|
+
return;
|
|
275
|
+
const payload = yield* decodePayload(entry.workflow, state.payload);
|
|
276
|
+
yield* drive(executionId, payload, state.parent, entry);
|
|
277
|
+
});
|
|
278
|
+
// --- Clock firing -----------------------------------------------------
|
|
279
|
+
// Persist deferred completion (first-writer-wins via createIfMissing),
|
|
280
|
+
// resume the workflow, then clean up the clock doc.
|
|
281
|
+
const fireClock = (doc) => Effect.gen(function* () {
|
|
282
|
+
const created = yield* createIfMissing({
|
|
283
|
+
id: deferredKey(doc.deferredName),
|
|
284
|
+
_partitionKey: doc._partitionKey,
|
|
285
|
+
type: "deferred",
|
|
286
|
+
exit: yield* encodeDeferredExit(Exit.void)
|
|
287
|
+
})
|
|
288
|
+
.pipe(annotate("clockFire", doc._partitionKey));
|
|
289
|
+
if (created)
|
|
290
|
+
yield* driveById(doc._partitionKey);
|
|
291
|
+
yield* Effect.promise(() => container.item(doc.id, doc._partitionKey).delete()).pipe(Effect.catchCause(() => Effect.void));
|
|
292
|
+
});
|
|
293
|
+
// --- Encoded engine ----------------------------------------------------
|
|
294
|
+
const encoded = {
|
|
295
|
+
register: Effect.fnUntraced(function* (workflow, execute) {
|
|
296
|
+
workflows.set(workflow.name, {
|
|
297
|
+
workflow,
|
|
298
|
+
execute,
|
|
299
|
+
scope: yield* Effect.scope
|
|
300
|
+
});
|
|
301
|
+
}),
|
|
302
|
+
execute: Effect.fnUntraced(function* (workflow, options) {
|
|
303
|
+
const entry = workflows.get(workflow.name);
|
|
304
|
+
if (!entry) {
|
|
305
|
+
return yield* Effect.orDie(Effect.fail(`Workflow ${workflow.name} is not registered`));
|
|
306
|
+
}
|
|
307
|
+
const initial = {
|
|
308
|
+
id: execId,
|
|
309
|
+
_partitionKey: options.executionId,
|
|
310
|
+
type: "exec",
|
|
311
|
+
workflowName: workflow.name,
|
|
312
|
+
payload: yield* encodePayload(workflow, options.payload),
|
|
313
|
+
parent: options.parent?.executionId,
|
|
314
|
+
status: "running",
|
|
315
|
+
suspended: false,
|
|
316
|
+
interrupted: false
|
|
317
|
+
};
|
|
318
|
+
const created = yield* createIfMissing(initial).pipe(annotate("execute.claim", options.executionId));
|
|
319
|
+
if (created || !locals.has(options.executionId)) {
|
|
320
|
+
yield* drive(options.executionId, options.payload, options.parent?.executionId, entry);
|
|
321
|
+
}
|
|
322
|
+
if (options.discard)
|
|
323
|
+
return undefined;
|
|
324
|
+
const local = locals.get(options.executionId);
|
|
325
|
+
if (local?.fiber) {
|
|
326
|
+
return (yield* Fiber.join(local.fiber));
|
|
327
|
+
}
|
|
328
|
+
// Foreign-owned execution: poll until exec doc reports complete.
|
|
329
|
+
while (true) {
|
|
330
|
+
const cur = yield* readExec(options.executionId);
|
|
331
|
+
if (Option.isSome(cur)) {
|
|
332
|
+
const c = yield* completeResult(workflow, cur.value);
|
|
333
|
+
if (Option.isSome(c))
|
|
334
|
+
return c.value;
|
|
335
|
+
}
|
|
336
|
+
yield* Effect.sleep(Duration.millis(500));
|
|
337
|
+
}
|
|
338
|
+
}),
|
|
339
|
+
poll: (workflow, executionId) => Effect.gen(function* () {
|
|
340
|
+
const local = locals.get(executionId);
|
|
341
|
+
if (local?.fiber) {
|
|
342
|
+
const exit = local.fiber.pollUnsafe();
|
|
343
|
+
if (!exit)
|
|
344
|
+
return Option.none();
|
|
345
|
+
if (exit._tag !== "Success")
|
|
346
|
+
return yield* Effect.die(exit.cause);
|
|
347
|
+
return Option.some(exit.value);
|
|
348
|
+
}
|
|
349
|
+
const state = yield* readExec(executionId);
|
|
350
|
+
if (Option.isNone(state))
|
|
351
|
+
return Option.none();
|
|
352
|
+
return yield* completeResult(workflow, state.value);
|
|
353
|
+
}),
|
|
354
|
+
interrupt: Effect.fnUntraced(function* (_workflow, executionId) {
|
|
355
|
+
const local = locals.get(executionId);
|
|
356
|
+
if (local)
|
|
357
|
+
local.instance.interrupted = true;
|
|
358
|
+
const current = yield* readExec(executionId);
|
|
359
|
+
if (Option.isNone(current) || current.value.status === "complete")
|
|
360
|
+
return;
|
|
361
|
+
yield* replaceExec({ ...current.value, interrupted: true }).pipe(Effect.catchTag("OptimisticConcurrencyException", () => Effect.void));
|
|
362
|
+
yield* driveById(executionId);
|
|
363
|
+
}),
|
|
364
|
+
interruptUnsafe: Effect.fnUntraced(function* (_workflow, executionId) {
|
|
365
|
+
const local = locals.get(executionId);
|
|
366
|
+
if (local)
|
|
367
|
+
local.instance.interrupted = true;
|
|
368
|
+
const current = yield* readExec(executionId);
|
|
369
|
+
if (Option.isSome(current) && current.value.status !== "complete") {
|
|
370
|
+
yield* replaceExec({ ...current.value, interrupted: true }).pipe(Effect.catchTag("OptimisticConcurrencyException", () => Effect.void));
|
|
371
|
+
}
|
|
372
|
+
if (local?.fiber)
|
|
373
|
+
yield* Fiber.interrupt(local.fiber);
|
|
374
|
+
}),
|
|
375
|
+
resume: (_workflow, executionId) => driveById(executionId),
|
|
376
|
+
activityExecute: Effect.fnUntraced(function* (activity, attempt) {
|
|
377
|
+
const instance = yield* WorkflowInstance;
|
|
378
|
+
const id = activityKey(activity.name, attempt);
|
|
379
|
+
const existing = yield* readPoint(id, instance.executionId).pipe(annotate("activityRead", instance.executionId));
|
|
380
|
+
if (Option.isSome(existing)) {
|
|
381
|
+
const prev = yield* decodeActivityResult(existing.value.result);
|
|
382
|
+
// A completed activity is replayed from its persisted result; a
|
|
383
|
+
// suspended one must re-run (it parked on a clock/deferred).
|
|
384
|
+
if (prev._tag === "Complete")
|
|
385
|
+
return prev;
|
|
386
|
+
}
|
|
387
|
+
const activityInstance = WorkflowInstance.initial(instance.workflow, instance.executionId);
|
|
388
|
+
activityInstance.interrupted = instance.interrupted;
|
|
389
|
+
const result = yield* activity.executeEncoded.pipe(Workflow.intoResult, Effect.provideService(WorkflowInstance, activityInstance));
|
|
390
|
+
const doc = {
|
|
391
|
+
id,
|
|
392
|
+
_partitionKey: instance.executionId,
|
|
393
|
+
type: "activity",
|
|
394
|
+
result: yield* encodeActivityResult(result)
|
|
395
|
+
};
|
|
396
|
+
if (Option.isSome(existing)) {
|
|
397
|
+
// Overwrite the previously persisted *suspended* doc with the new result.
|
|
398
|
+
yield* upsert(doc).pipe(annotate("activityPersist", instance.executionId));
|
|
399
|
+
return result;
|
|
400
|
+
}
|
|
401
|
+
// First-writer-wins: if persistence loses the race, use the persisted result.
|
|
402
|
+
const persisted = yield* createIfMissing(doc).pipe(annotate("activityPersist", instance.executionId));
|
|
403
|
+
if (persisted)
|
|
404
|
+
return result;
|
|
405
|
+
const winner = yield* readPoint(id, instance.executionId);
|
|
406
|
+
if (Option.isSome(winner)) {
|
|
407
|
+
const w = yield* decodeActivityResult(winner.value.result);
|
|
408
|
+
if (w._tag === "Complete")
|
|
409
|
+
return w;
|
|
410
|
+
}
|
|
411
|
+
return result;
|
|
412
|
+
}),
|
|
413
|
+
deferredResult: Effect.fnUntraced(function* (deferred) {
|
|
414
|
+
const instance = yield* WorkflowInstance;
|
|
415
|
+
const got = yield* readPoint(deferredKey(deferred.name), instance.executionId).pipe(annotate("deferredRead", instance.executionId));
|
|
416
|
+
if (Option.isNone(got))
|
|
417
|
+
return Option.none();
|
|
418
|
+
return Option.some(yield* decodeDeferredExit(got.value.exit));
|
|
419
|
+
}),
|
|
420
|
+
deferredDone: Effect.fnUntraced(function* (options) {
|
|
421
|
+
const created = yield* createIfMissing({
|
|
422
|
+
id: deferredKey(options.deferredName),
|
|
423
|
+
_partitionKey: options.executionId,
|
|
424
|
+
type: "deferred",
|
|
425
|
+
exit: yield* encodeDeferredExit(options.exit)
|
|
426
|
+
})
|
|
427
|
+
.pipe(annotate("deferredPersist", options.executionId));
|
|
428
|
+
if (!created)
|
|
429
|
+
return;
|
|
430
|
+
yield* driveById(options.executionId);
|
|
431
|
+
}),
|
|
432
|
+
scheduleClock: (workflow, options) => {
|
|
433
|
+
const fireAt = new Date(Date.now() + Duration.toMillis(options.clock.duration)).toISOString();
|
|
434
|
+
const clockDoc = {
|
|
435
|
+
id: clockKey(options.clock.name),
|
|
436
|
+
_partitionKey: options.executionId,
|
|
437
|
+
type: "clock",
|
|
438
|
+
workflowName: workflow.name,
|
|
439
|
+
deferredName: options.clock.deferred.name,
|
|
440
|
+
fireAt
|
|
441
|
+
};
|
|
442
|
+
return Effect.gen(function* () {
|
|
443
|
+
yield* createIfMissing(clockDoc).pipe(annotate("clockPersist", options.executionId));
|
|
444
|
+
// Fast-path in-process timer. If this process dies, the clock poller
|
|
445
|
+
// picks up the persisted doc and fires the deferred.
|
|
446
|
+
yield* fireClock(clockDoc).pipe(Effect.delay(options.clock.duration), FiberMap.run(clocks, `${options.executionId}/${options.clock.name}`, { onlyIfMissing: true }), Effect.asVoid);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
const engine = makeUnsafe(encoded);
|
|
451
|
+
// --- Recovery poller --------------------------------------------------
|
|
452
|
+
// Scan for executions whose lease has lapsed (or was never set) and
|
|
453
|
+
// re-drive them locally. driveById will go through claim → fork fiber,
|
|
454
|
+
// resuming activities from persisted results.
|
|
455
|
+
if (Duration.toMillis(recoveryInterval) > 0) {
|
|
456
|
+
const recoverStep = Effect
|
|
457
|
+
.gen(function* () {
|
|
458
|
+
const nowIso = new Date().toISOString();
|
|
459
|
+
const stale = yield* Effect.promise(() => container
|
|
460
|
+
.items
|
|
461
|
+
.query({
|
|
462
|
+
query: "SELECT c._partitionKey, c.workflowName FROM c WHERE c.type = 'exec' AND c.status = 'running' AND (NOT IS_DEFINED(c.leaseExpiresAt) OR c.leaseExpiresAt <= @now)",
|
|
463
|
+
parameters: [{ name: "@now", value: nowIso }]
|
|
464
|
+
})
|
|
465
|
+
.fetchAll());
|
|
466
|
+
for (const row of stale.resources) {
|
|
467
|
+
if (!workflows.has(row.workflowName))
|
|
468
|
+
continue;
|
|
469
|
+
const local = locals.get(row._partitionKey);
|
|
470
|
+
if (local?.fiber && !local.fiber.pollUnsafe())
|
|
471
|
+
continue;
|
|
472
|
+
yield* Effect.forkIn(driveById(row._partitionKey), scope);
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
.pipe(annotate("recoveryScan"), Effect.catchCause(() => Effect.void));
|
|
476
|
+
yield* recoverStep.pipe(Effect.repeat(Schedule.spaced(recoveryInterval)), Effect.forkIn(scope));
|
|
477
|
+
}
|
|
478
|
+
// --- Clock poller -----------------------------------------------------
|
|
479
|
+
// Cross-partition scan for clocks whose fireAt is due. Fires the deferred
|
|
480
|
+
// via createIfMissing (idempotent) so multiple pollers across processes
|
|
481
|
+
// converge. Also acts as the restart recovery path for clocks scheduled
|
|
482
|
+
// before a crash.
|
|
483
|
+
if (Duration.toMillis(clockPollInterval) > 0) {
|
|
484
|
+
const clockStep = Effect
|
|
485
|
+
.gen(function* () {
|
|
486
|
+
const nowIso = new Date().toISOString();
|
|
487
|
+
const due = yield* Effect.promise(() => container
|
|
488
|
+
.items
|
|
489
|
+
.query({
|
|
490
|
+
query: "SELECT c.id, c._partitionKey, c.workflowName, c.deferredName FROM c WHERE c.type = 'clock' AND c.fireAt <= @now",
|
|
491
|
+
parameters: [{ name: "@now", value: nowIso }]
|
|
492
|
+
})
|
|
493
|
+
.fetchAll());
|
|
494
|
+
for (const row of due.resources) {
|
|
495
|
+
yield* Effect.forkIn(fireClock({
|
|
496
|
+
id: row.id,
|
|
497
|
+
_partitionKey: row._partitionKey,
|
|
498
|
+
type: "clock",
|
|
499
|
+
workflowName: row.workflowName,
|
|
500
|
+
deferredName: row.deferredName,
|
|
501
|
+
fireAt: nowIso
|
|
502
|
+
}), scope);
|
|
503
|
+
}
|
|
504
|
+
})
|
|
505
|
+
.pipe(annotate("clockScan"), Effect.catchCause(() => Effect.void));
|
|
506
|
+
yield* clockStep.pipe(Effect.repeat(Schedule.spaced(clockPollInterval)), Effect.forkIn(scope));
|
|
507
|
+
}
|
|
508
|
+
return engine;
|
|
509
|
+
});
|
|
510
|
+
/**
|
|
511
|
+
* Cosmos DB backed `WorkflowEngine` layer.
|
|
512
|
+
*
|
|
513
|
+
* Per-execution writes share a partition key (TransactionalBatch-eligible) and
|
|
514
|
+
* use OCC via `_etag`/IfMatch, giving first-writer-wins semantics for activity
|
|
515
|
+
* results, durable-deferred completions, and exec-state transitions. All
|
|
516
|
+
* persisted payloads/results/exits are round-tripped through schema codecs.
|
|
517
|
+
*/
|
|
518
|
+
export const layerCosmos = (cfg) => Layer
|
|
519
|
+
.effect(WorkflowEngine)(makeCosmosWorkflowEngine(cfg))
|
|
520
|
+
.pipe(Layer.provide(CosmosClientLayer(Redacted.value(cfg.url), cfg.dbName)));
|
|
521
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV29ya2Zsb3dFbmdpbmVDb3Ntb3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvV29ya2Zsb3dFbmdpbmVDb3Ntb3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0ErQkc7QUFDSCxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFBO0FBQzNDLE9BQU8sS0FBSyxLQUFLLE1BQU0sa0JBQWtCLENBQUE7QUFDekMsT0FBTyxLQUFLLE1BQU0sTUFBTSxtQkFBbUIsQ0FBQTtBQUMzQyxPQUFPLEtBQUssQ0FBQyxNQUFNLG1CQUFtQixDQUFBO0FBQ3RDLE9BQU8sS0FBSyxRQUFRLE1BQU0saUJBQWlCLENBQUE7QUFDM0MsT0FBTyxLQUFLLElBQUksTUFBTSxhQUFhLENBQUE7QUFDbkMsT0FBTyxLQUFLLEtBQUssTUFBTSxjQUFjLENBQUE7QUFDckMsT0FBTyxLQUFLLFFBQVEsTUFBTSxpQkFBaUIsQ0FBQTtBQUMzQyxPQUFPLEtBQUssUUFBUSxNQUFNLGlCQUFpQixDQUFBO0FBQzNDLE9BQU8sS0FBSyxRQUFRLE1BQU0saUJBQWlCLENBQUE7QUFFM0MsT0FBTyxLQUFLLFFBQVEsTUFBTSxtQ0FBbUMsQ0FBQTtBQUM3RCxPQUFPLEVBQWdCLFVBQVUsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQTtBQUNwSCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQTtBQUNwRSxPQUFPLEVBQUUsOEJBQThCLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDNUQsT0FBTyxFQUFFLHNCQUFzQixFQUFFLFVBQVUsRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQStEOUQsTUFBTSxNQUFNLEdBQUcsTUFBZSxDQUFBO0FBQzlCLE1BQU0sV0FBVyxHQUFHLENBQUMs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
|