@lostgradient/weft 0.7.0 → 0.9.0
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/README.md +9 -3
- package/dist/cli/generated/operation-client.generated.d.ts +1 -0
- package/dist/cli-main.js +78 -78
- package/dist/core/context/activity-retry-state.d.ts +6 -5
- package/dist/core/context/activity-retry-state.js +31 -21
- package/dist/core/context/durable-activity.d.ts +117 -0
- package/dist/core/context/durable-activity.js +79 -0
- package/dist/core/context/durable-operations.js +1 -1
- package/dist/core/context/index.d.ts +1 -0
- package/dist/core/context/index.js +2 -0
- package/dist/core/context/operation-request.d.ts +17 -0
- package/dist/core/context/parallel-operations.js +1 -0
- package/dist/core/context/run-operation-cached-request.d.ts +8 -0
- package/dist/core/context/run-operation-cached-request.js +59 -0
- package/dist/core/context/run-operation.d.ts +23 -5
- package/dist/core/context/run-operation.js +63 -47
- package/dist/core/context/speculative-child.js +1 -0
- package/dist/core/context/types.d.ts +1 -0
- package/dist/core/engine/activity-heartbeat-tracking.d.ts +8 -17
- package/dist/core/engine/activity-heartbeat-tracking.js +12 -2
- package/dist/core/engine/activity-reconciliation.d.ts +1 -0
- package/dist/core/engine/activity-reconciliation.js +7 -2
- package/dist/core/engine/anonymous-signal-sequence.js +6 -6
- package/dist/core/engine/async-activity-completion.d.ts +5 -7
- package/dist/core/engine/async-activity-completion.js +13 -9
- package/dist/core/engine/bulk-operations-purge.js +2 -1
- package/dist/core/engine/bulk-operations.js +18 -36
- package/dist/core/engine/callback-checkpoint-persistence.d.ts +3 -0
- package/dist/core/engine/callback-checkpoint-persistence.js +25 -0
- package/dist/core/engine/callback-creators-bundles.js +5 -2
- package/dist/core/engine/checkpoint-io.d.ts +4 -1
- package/dist/core/engine/checkpoint-io.js +19 -10
- package/dist/core/engine/completed-review-storage.d.ts +2 -1
- package/dist/core/engine/completed-review-storage.js +4 -3
- package/dist/core/engine/disposal.js +1 -0
- package/dist/core/engine/engine-internal-types.d.ts +1 -0
- package/dist/core/engine/index.d.ts +31 -0
- package/dist/core/engine/index.js +19 -10
- package/dist/core/engine/inline-launch-queue.js +1 -1
- package/dist/core/engine/internals.d.ts +25 -2
- package/dist/core/engine/lease-manager.js +2 -2
- package/dist/core/engine/lifecycle/fork-helpers.js +1 -0
- package/dist/core/engine/lifecycle/resume.js +6 -0
- package/dist/core/engine/lifecycle/start-exec.d.ts +2 -2
- package/dist/core/engine/lifecycle/start-exec.js +6 -4
- package/dist/core/engine/lifecycle/start.js +1 -0
- package/dist/core/engine/lifecycle/transition.js +6 -0
- package/dist/core/engine/memo-durable-activity.d.ts +11 -0
- package/dist/core/engine/memo-durable-activity.js +282 -0
- package/dist/core/engine/operations-activity.d.ts +6 -2
- package/dist/core/engine/operations-activity.js +24 -10
- package/dist/core/engine/operations-data.d.ts +4 -1
- package/dist/core/engine/operations-data.js +3 -3
- package/dist/core/engine/operations-time.d.ts +2 -2
- package/dist/core/engine/operations-time.js +40 -6
- package/dist/core/engine/reviews.js +7 -4
- package/dist/core/engine/schedule-timer.js +2 -0
- package/dist/core/engine/schedules.js +3 -0
- package/dist/core/engine/storage-io.js +1 -1
- package/dist/core/engine/termination/cleanup.js +15 -13
- package/dist/core/engine/termination/finalizer-activity.d.ts +1 -1
- package/dist/core/engine/termination/finalizer-activity.js +5 -1
- package/dist/core/engine/termination/finalizer.js +1 -1
- package/dist/core/engine/validation/schedule-options.d.ts +9 -0
- package/dist/core/engine/validation/schedule-options.js +59 -0
- package/dist/core/engine/validation/schedule.d.ts +3 -8
- package/dist/core/engine/validation/schedule.js +40 -56
- package/dist/core/engine/validation.js +1 -0
- package/dist/core/execution-strategy.d.ts +1 -0
- package/dist/core/inline-execution-strategy.context-options.d.ts +1 -0
- package/dist/core/inline-execution-strategy.context-options.js +3 -0
- package/dist/core/json.js +1 -1
- package/dist/core/types/activity.d.ts +11 -0
- package/dist/core/types/checkpoint.d.ts +1 -0
- package/dist/core/types/schedules.d.ts +10 -0
- package/dist/core/types/state.d.ts +6 -0
- package/dist/core/types/workflow-context.d.ts +13 -0
- package/dist/core/weft-error.d.ts +1 -1
- package/dist/core/weft-error.js +2 -0
- package/dist/core/worker-execution-strategy.d.ts +1 -0
- package/dist/core/worker-inbound-message.d.ts +1 -0
- package/dist/core/worker-inbound-message.js +3 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +6 -0
- package/dist/json-schema.js +2 -2
- package/dist/mcp/cli.js +32 -32
- package/dist/observability/index.js +2 -2
- package/dist/runtime/portable.d.ts +1 -1
- package/dist/server/handler.js +27 -27
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +32 -32
- package/dist/server/operations/create-schedule.d.ts +2 -0
- package/dist/server/operations/create-schedule.js +13 -2
- package/dist/server/rest-binding.d.ts +1 -1
- package/dist/server/runtime/task-dispatch.js +6 -0
- package/dist/server/runtime/task-polling.js +9 -1
- package/dist/server/runtime/task-reconciliation.js +2 -1
- package/dist/server/runtime/websocket-worker.js +7 -2
- package/dist/server/serve-internals.d.ts +28 -0
- package/dist/server/serve-internals.js +4 -2
- package/dist/server/task-queue-types.d.ts +2 -0
- package/dist/server/task-state.d.ts +4 -0
- package/dist/service-worker/index.d.ts +15 -14
- package/dist/service-worker/index.js +28 -28
- package/dist/service-worker/scheduler.d.ts +41 -1
- package/dist/service-worker/setup.d.ts +18 -1
- package/dist/service-worker/setup.js +7 -4
- package/dist/storage/auto.d.ts +36 -18
- package/dist/storage/auto.js +1 -1
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/indexeddb.d.ts +6 -1
- package/dist/storage/indexeddb.js +1 -1
- package/dist/storage/interface.js +1 -1
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +1 -1
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +1 -1
- package/dist/storage/typed-storage.d.ts +1 -1
- package/dist/storage/typed-storage.js +1 -1
- package/dist/storage/web-extension.d.ts +1 -1
- package/dist/storage/web-extension.js +1 -1
- package/dist/testing/index.js +32 -32
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/execute-with-interceptors.d.ts +9 -3
- package/dist/worker/execute-with-interceptors.js +14 -2
- package/dist/worker/long-poll.d.ts +2 -1
- package/dist/worker/long-poll.js +1 -1
- package/dist/worker/protocol-messages.d.ts +2 -0
- package/dist/worker/protocol-schemas.d.ts +8 -0
- package/dist/worker/protocol-schemas.js +1 -0
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/remote-activity-context.d.ts +2 -0
- package/dist/workers/activity-runner.d.ts +2 -0
- package/dist/workers/activity-runner.js +9 -1
- package/dist/workers/workflow-runner.d.ts +2 -1
- package/dist/workers/workflow-runner.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { hashString } from "../../runtime/portable.js";
|
|
2
|
+
import {
|
|
3
|
+
DurableActivityScopeError,
|
|
4
|
+
DurableActivityUnsupportedError,
|
|
5
|
+
runWithDurableActivityScope
|
|
6
|
+
} from "../context/durable-activity.js";
|
|
7
|
+
import {
|
|
8
|
+
readOrInitActivityRetrySleepFireAt,
|
|
9
|
+
runActivityWithRetryAtStep
|
|
10
|
+
} from "../context/run-operation.js";
|
|
11
|
+
import { AsyncActivityDeferral } from "./async-activity-completion.js";
|
|
12
|
+
import {
|
|
13
|
+
executeActivityOperationResult
|
|
14
|
+
} from "./operations-activity.js";
|
|
15
|
+
import { registerSleepResolver } from "./operations-time.js";
|
|
16
|
+
import { callMemoFunction } from "./state-utilities.js";
|
|
17
|
+
export async function callMemoFunctionWithDurableActivityScope(internals, workflowId, operation, callbacks) {
|
|
18
|
+
const context = internals.inlineStrategy?.getContext(workflowId);
|
|
19
|
+
if (context === void 0 || typeof operation.step !== "number" || !Number.isSafeInteger(operation.step) || operation.step < 0 || callbacks.getActivityOperationCallbacks === void 0)
|
|
20
|
+
return callMemoFunction(operation.fn);
|
|
21
|
+
const memoOperation = { ...operation, step: operation.step };
|
|
22
|
+
return new MemoDurableActivityScope(internals, workflowId, memoOperation, context, callbacks.getActivityOperationCallbacks(), callbacks.persistCheckpoint).run(() => callMemoFunction(operation.fn));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class MemoDurableActivityScope {
|
|
26
|
+
#internals;
|
|
27
|
+
#workflowId;
|
|
28
|
+
#operation;
|
|
29
|
+
#context;
|
|
30
|
+
#activityCallbacks;
|
|
31
|
+
#persistCheckpoint;
|
|
32
|
+
#scopeAbortController = new AbortController;
|
|
33
|
+
#pendingPromises = new Set;
|
|
34
|
+
#identityPrefix;
|
|
35
|
+
#activePromise;
|
|
36
|
+
#activeNonCancellableWriteCount = 0;
|
|
37
|
+
#closed = !1;
|
|
38
|
+
#closeReason;
|
|
39
|
+
#nextOrdinal = 0;
|
|
40
|
+
constructor(internals, workflowId, operation, context, activityCallbacks, persistCheckpoint) {
|
|
41
|
+
this.#internals = internals;
|
|
42
|
+
this.#workflowId = workflowId;
|
|
43
|
+
this.#operation = operation;
|
|
44
|
+
this.#context = context;
|
|
45
|
+
this.#activityCallbacks = activityCallbacks;
|
|
46
|
+
this.#persistCheckpoint = persistCheckpoint;
|
|
47
|
+
this.#identityPrefix = `memo:${operation.step}:${hashString(operation.key)}`;
|
|
48
|
+
this.#internals.workflowTypeByWorkflowId.set(workflowId, context.workflowType);
|
|
49
|
+
}
|
|
50
|
+
async run(execute) {
|
|
51
|
+
const cleanupAbortForwarding = this.#forwardAbortSignals();
|
|
52
|
+
try {
|
|
53
|
+
const result = await runWithDurableActivityScope(this, execute);
|
|
54
|
+
if (this.#pendingPromises.size > 0) {
|
|
55
|
+
const error = new DurableActivityScopeError("durableActivity() calls started inside ctx.memo() must be awaited before the memo callback returns.");
|
|
56
|
+
await this.#closeAndDrain(error);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
this.#close();
|
|
60
|
+
return result;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
await this.#closeAndDrain(error);
|
|
63
|
+
throw error;
|
|
64
|
+
} finally {
|
|
65
|
+
cleanupAbortForwarding();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
dispatch(invocation) {
|
|
69
|
+
try {
|
|
70
|
+
this.#throwIfClosed();
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return Promise.reject(error);
|
|
73
|
+
}
|
|
74
|
+
if (this.#activePromise !== void 0)
|
|
75
|
+
return Promise.reject(new DurableActivityScopeError("durableActivity() calls inside one ctx.memo() scope must be awaited sequentially. Start the next durableActivity() call after the previous promise settles."));
|
|
76
|
+
const ordinal = this.#nextOrdinal;
|
|
77
|
+
this.#nextOrdinal += 1;
|
|
78
|
+
const execution = this.#executeInvocation(invocation, ordinal);
|
|
79
|
+
this.#activePromise = execution;
|
|
80
|
+
this.#pendingPromises.add(execution);
|
|
81
|
+
execution.finally(() => {
|
|
82
|
+
if (this.#activePromise === execution)
|
|
83
|
+
this.#activePromise = void 0;
|
|
84
|
+
this.#pendingPromises.delete(execution);
|
|
85
|
+
}).catch(() => {});
|
|
86
|
+
execution.catch(() => {});
|
|
87
|
+
return execution;
|
|
88
|
+
}
|
|
89
|
+
async#executeInvocation(invocation, ordinal) {
|
|
90
|
+
const activityStateKey = this.#activityStateKey(ordinal), generator = runActivityWithRetryAtStep(this.#context, invocation.activity, invocation.arguments, this.#operation.step, {
|
|
91
|
+
activityStateKey,
|
|
92
|
+
cacheResultStep: !1,
|
|
93
|
+
retryStateKey: activityStateKey,
|
|
94
|
+
retrySleep: (duration, nextAttempt) => this.#retrySleepOperation(duration, ordinal, nextAttempt)
|
|
95
|
+
});
|
|
96
|
+
let next = generator.next();
|
|
97
|
+
while (!next.done)
|
|
98
|
+
try {
|
|
99
|
+
const result = await this.#executeYieldedOperation(next.value, invocation, ordinal);
|
|
100
|
+
next = generator.next(result);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (error instanceof AsyncActivityDeferral)
|
|
103
|
+
throw new DurableActivityUnsupportedError("ActivityContext.completeAsync() is not supported from durableActivity(). Use yield* ctx.run() for async-completion activities.");
|
|
104
|
+
next = generator.throw(error);
|
|
105
|
+
}
|
|
106
|
+
return next.value;
|
|
107
|
+
}
|
|
108
|
+
async#executeYieldedOperation(operation, invocation, ordinal) {
|
|
109
|
+
this.#throwIfClosed();
|
|
110
|
+
if (operation.type === "activity")
|
|
111
|
+
return this.#executeActivityOperation(operation, invocation.callerStack, ordinal);
|
|
112
|
+
if (operation.type === "sleep") {
|
|
113
|
+
await this.#raceWithScopeAbort(this.#executeDurableRetrySleepOperation(operation));
|
|
114
|
+
this.#throwIfClosed();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
throw new DurableActivityScopeError(`durableActivity() retry driver yielded unsupported operation "${operation.type}".`);
|
|
118
|
+
}
|
|
119
|
+
#executeActivityOperation(request, callerStack, ordinal) {
|
|
120
|
+
const attempt = typeof request.attempt === "number" ? request.attempt : 1, operation = {
|
|
121
|
+
...request,
|
|
122
|
+
operationId: this.#activityOperationId(ordinal, attempt),
|
|
123
|
+
callerStack
|
|
124
|
+
};
|
|
125
|
+
return this.#raceWithScopeAbort(executeActivityOperationResult(this.#internals, this.#workflowId, operation, this.#activityCallbacks, this.#scopeAbortController.signal, void 0, {
|
|
126
|
+
reconciliationCompletion: "immediate-fenced",
|
|
127
|
+
beforeImmediateReconciliationCommit: () => this.#beginNonCancellableWrite()
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
async#executeDurableRetrySleepOperation(operation) {
|
|
131
|
+
const finishCheckpointWrite = this.#beginNonCancellableWrite();
|
|
132
|
+
try {
|
|
133
|
+
await this.#persistCheckpoint(this.#workflowId, operation);
|
|
134
|
+
} finally {
|
|
135
|
+
finishCheckpointWrite();
|
|
136
|
+
}
|
|
137
|
+
this.#throwIfClosed();
|
|
138
|
+
if (operation.scheduledFireAt <= this.#internals.options.getNow())
|
|
139
|
+
return;
|
|
140
|
+
const { promise, resolve } = Promise.withResolvers(), finishTimerWrite = this.#beginNonCancellableWrite();
|
|
141
|
+
try {
|
|
142
|
+
await this.#internals.scheduler.schedule({
|
|
143
|
+
id: `sleep:${operation.operationId}`,
|
|
144
|
+
workflowId: this.#workflowId,
|
|
145
|
+
fireAt: operation.scheduledFireAt,
|
|
146
|
+
kind: "sleep"
|
|
147
|
+
});
|
|
148
|
+
} finally {
|
|
149
|
+
finishTimerWrite();
|
|
150
|
+
}
|
|
151
|
+
this.#throwIfClosed();
|
|
152
|
+
registerSleepResolver(this.#internals, this.#workflowId, operation.operationId, resolve, operation.scheduledFireAt);
|
|
153
|
+
await promise;
|
|
154
|
+
}
|
|
155
|
+
#activityOperationId(ordinal, attempt) {
|
|
156
|
+
return `${this.#activityStateKey(ordinal)}:activity:${attempt}`;
|
|
157
|
+
}
|
|
158
|
+
#retrySleepOperationId(ordinal, nextAttempt) {
|
|
159
|
+
return `${this.#activityStateKey(ordinal)}:retry-sleep:${nextAttempt}`;
|
|
160
|
+
}
|
|
161
|
+
*#retrySleepOperation(duration, ordinal, nextAttempt) {
|
|
162
|
+
const operationId = this.#retrySleepOperationId(ordinal, nextAttempt);
|
|
163
|
+
yield {
|
|
164
|
+
type: "sleep",
|
|
165
|
+
operationId,
|
|
166
|
+
duration,
|
|
167
|
+
scheduledFireAt: this.#readOrInitRetrySleepFireAt(operationId, duration)
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
#readOrInitRetrySleepFireAt(operationId, duration) {
|
|
171
|
+
return readOrInitActivityRetrySleepFireAt(this.#context, operationId, duration);
|
|
172
|
+
}
|
|
173
|
+
#activityStateKey(ordinal) {
|
|
174
|
+
if (!Number.isSafeInteger(ordinal) || ordinal < 0)
|
|
175
|
+
throw new DurableActivityScopeError(`Invalid durableActivity() call ordinal ${String(ordinal)} for ctx.memo() step ${String(this.#operation.step)}.`);
|
|
176
|
+
return `${this.#identityPrefix}:call:${String(ordinal)}`;
|
|
177
|
+
}
|
|
178
|
+
#raceWithScopeAbort(operation) {
|
|
179
|
+
operation.catch(() => {});
|
|
180
|
+
this.#throwIfClosed();
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
let settled = !1;
|
|
183
|
+
const signal = this.#scopeAbortController.signal, cleanup = () => {
|
|
184
|
+
signal.removeEventListener("abort", onAbort);
|
|
185
|
+
}, settle = (callback) => {
|
|
186
|
+
if (settled)
|
|
187
|
+
return;
|
|
188
|
+
settled = !0;
|
|
189
|
+
cleanup();
|
|
190
|
+
callback();
|
|
191
|
+
}, onAbort = () => {
|
|
192
|
+
if (this.#activeNonCancellableWriteCount > 0)
|
|
193
|
+
return;
|
|
194
|
+
settle(() => reject(this.#abortError()));
|
|
195
|
+
};
|
|
196
|
+
signal.addEventListener("abort", onAbort, { once: !0 });
|
|
197
|
+
if (signal.aborted) {
|
|
198
|
+
onAbort();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
operation.then((value) => {
|
|
202
|
+
settle(() => {
|
|
203
|
+
if (this.#closed || signal.aborted) {
|
|
204
|
+
reject(this.#abortError());
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
resolve(value);
|
|
208
|
+
});
|
|
209
|
+
}).catch((error) => {
|
|
210
|
+
settle(() => reject(error));
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
#beginNonCancellableWrite() {
|
|
215
|
+
this.#throwIfClosed();
|
|
216
|
+
this.#activeNonCancellableWriteCount += 1;
|
|
217
|
+
let finished = !1;
|
|
218
|
+
return () => {
|
|
219
|
+
if (finished)
|
|
220
|
+
return;
|
|
221
|
+
finished = !0;
|
|
222
|
+
this.#activeNonCancellableWriteCount -= 1;
|
|
223
|
+
if (this.#closed && this.#activeNonCancellableWriteCount === 0 && !this.#scopeAbortController.signal.aborted)
|
|
224
|
+
this.#scopeAbortController.abort(this.#abortError());
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
#forwardAbortSignals() {
|
|
228
|
+
const signals = [this.#context.signal, this.#internals.abortController.signal], listeningSignals = [], onAbort = () => {
|
|
229
|
+
this.#close(new DurableActivityScopeError("durableActivity() scope closed before completion."));
|
|
230
|
+
}, cleanup = () => {
|
|
231
|
+
for (const signal of listeningSignals)
|
|
232
|
+
signal.removeEventListener("abort", onAbort);
|
|
233
|
+
};
|
|
234
|
+
for (const signal of signals) {
|
|
235
|
+
if (signal.aborted) {
|
|
236
|
+
onAbort();
|
|
237
|
+
return cleanup;
|
|
238
|
+
}
|
|
239
|
+
signal.addEventListener("abort", onAbort, { once: !0 });
|
|
240
|
+
listeningSignals.push(signal);
|
|
241
|
+
}
|
|
242
|
+
return cleanup;
|
|
243
|
+
}
|
|
244
|
+
#close(reason) {
|
|
245
|
+
if (this.#closed)
|
|
246
|
+
return;
|
|
247
|
+
this.#closed = !0;
|
|
248
|
+
this.#closeReason = toScopeError(reason);
|
|
249
|
+
if (this.#activeNonCancellableWriteCount === 0 && !this.#scopeAbortController.signal.aborted)
|
|
250
|
+
this.#scopeAbortController.abort(this.#closeReason);
|
|
251
|
+
}
|
|
252
|
+
async#closeAndDrain(reason) {
|
|
253
|
+
this.#close(reason);
|
|
254
|
+
if (this.#pendingPromises.size === 0)
|
|
255
|
+
return;
|
|
256
|
+
await Promise.allSettled(this.#pendingPromises);
|
|
257
|
+
}
|
|
258
|
+
#throwIfClosed() {
|
|
259
|
+
if (this.#closed || this.#scopeAbortController.signal.aborted)
|
|
260
|
+
throw this.#abortError();
|
|
261
|
+
}
|
|
262
|
+
#abortError() {
|
|
263
|
+
if (this.#closeReason !== void 0)
|
|
264
|
+
return this.#closeReason;
|
|
265
|
+
const reason = this.#scopeAbortController.signal.reason;
|
|
266
|
+
return reason instanceof Error ? reason : new DurableActivityScopeError("durableActivity() scope closed before completion.");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function toScopeError(reason) {
|
|
270
|
+
if (reason instanceof Error)
|
|
271
|
+
return reason;
|
|
272
|
+
if (reason === void 0)
|
|
273
|
+
return new DurableActivityScopeError("durableActivity() scope closed before completion.");
|
|
274
|
+
return new DurableActivityScopeError(formatCloseReason(reason));
|
|
275
|
+
}
|
|
276
|
+
function formatCloseReason(reason) {
|
|
277
|
+
if (typeof reason === "string")
|
|
278
|
+
return reason;
|
|
279
|
+
if (typeof reason === "number" || typeof reason === "boolean" || typeof reason === "bigint")
|
|
280
|
+
return reason.toString();
|
|
281
|
+
return "durableActivity() scope closed before completion.";
|
|
282
|
+
}
|
|
@@ -12,6 +12,10 @@ export type ActivityFunctionWithMetadata = ((...arguments_: unknown[]) => unknow
|
|
|
12
12
|
type ActivityOperation = Extract<ContextOperationRequest, {
|
|
13
13
|
type: 'activity';
|
|
14
14
|
}>;
|
|
15
|
+
export interface ActivityExecutionOptions {
|
|
16
|
+
reconciliationCompletion?: 'stage-with-workflow-commit' | 'immediate-fenced';
|
|
17
|
+
beforeImmediateReconciliationCommit?: () => void | (() => void);
|
|
18
|
+
}
|
|
15
19
|
export type ActivityOperationCallbacks = {
|
|
16
20
|
runOperationWithResult: (workflowId: string, operation: ActivityOperation, execute: () => Promise<unknown>) => Promise<void>;
|
|
17
21
|
finalizePendingTimelineEntry: (workflowId: string, status: 'completed' | 'failed', value: unknown) => void;
|
|
@@ -21,13 +25,13 @@ export type ActivityOperationCallbacks = {
|
|
|
21
25
|
};
|
|
22
26
|
export declare function buildActivityVerification(_internals: EngineInternals, activityName: string, verify: ActivityFunctionWithMetadata['verify'], context: ReturnType<typeof buildActivityVerificationContext>, result: unknown): Promise<void>;
|
|
23
27
|
export declare function buildActivityCompensation(internals: EngineInternals, workflowId: string, operation: ActivityOperation, result: unknown): (() => Promise<void>) | undefined;
|
|
24
|
-
export declare function invokeWorkerActivity(internals: EngineInternals, operationId: string, activityName: string, input: unknown, attempt: number): Promise<unknown>;
|
|
28
|
+
export declare function invokeWorkerActivity(internals: EngineInternals, operationId: string, activityName: string, input: unknown, attempt: number, workflowExecutionToken: string | undefined, activityAttemptToken: string | undefined): Promise<unknown>;
|
|
25
29
|
export declare function invokeInlineActivity(internals: EngineInternals, workflowId: string, operation: ActivityOperation, activityContext: ActivityContext, _activityName: string, input: unknown): unknown;
|
|
26
30
|
/**
|
|
27
31
|
* Execute an activity function, dispatching to a Web Worker pool when
|
|
28
32
|
* `activityExecution` is configured, or running inline on the main thread.
|
|
29
33
|
*/
|
|
30
34
|
export declare function executeActivity(internals: EngineInternals, workflowId: string, operation: ActivityOperation, callbacks: ActivityOperationCallbacks, attempt?: number, coordinatorSignal?: AbortSignal): Promise<unknown>;
|
|
31
|
-
export declare function executeActivityOperationResult(internals: EngineInternals, workflowId: string, operation: ActivityOperation, callbacks: ActivityOperationCallbacks, coordinatorSignal?: AbortSignal, speculativeState?: SpeculativeExecutionState): Promise<unknown>;
|
|
35
|
+
export declare function executeActivityOperationResult(internals: EngineInternals, workflowId: string, operation: ActivityOperation, callbacks: ActivityOperationCallbacks, coordinatorSignal?: AbortSignal, speculativeState?: SpeculativeExecutionState, executionOptions?: ActivityExecutionOptions): Promise<unknown>;
|
|
32
36
|
export declare function processActivityOperation(internals: EngineInternals, workflowId: string, operation: ActivityOperation, callbacks: ActivityOperationCallbacks): Promise<void>;
|
|
33
37
|
export {};
|
|
@@ -8,6 +8,7 @@ import { resolvePerAttemptTimeout, withPerAttemptTimeout } from "./activity-per-
|
|
|
8
8
|
import {
|
|
9
9
|
buildActivityReconciliationReference,
|
|
10
10
|
buildActivityVerificationContext,
|
|
11
|
+
commitActivityReconciliationTransitionWithFencedWrite,
|
|
11
12
|
createCompletedActivityReconciliationRecord,
|
|
12
13
|
resolveActivityIdempotencyKey,
|
|
13
14
|
resolveStartedActivityReconciliationRecord,
|
|
@@ -39,7 +40,7 @@ export function buildActivityCompensation(internals, workflowId, operation, resu
|
|
|
39
40
|
await activity.compensate?.(operation.input, result);
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
|
-
export async function invokeWorkerActivity(internals, operationId, activityName, input, attempt) {
|
|
43
|
+
export async function invokeWorkerActivity(internals, operationId, activityName, input, attempt, workflowExecutionToken, activityAttemptToken) {
|
|
43
44
|
const dispatcher = internals.activityWorkerDispatcher;
|
|
44
45
|
if (!dispatcher)
|
|
45
46
|
throw Error(`No activity worker dispatcher available for "${activityName}"`);
|
|
@@ -47,7 +48,9 @@ export async function invokeWorkerActivity(internals, operationId, activityName,
|
|
|
47
48
|
operationId,
|
|
48
49
|
activityName,
|
|
49
50
|
input,
|
|
50
|
-
attempt
|
|
51
|
+
attempt,
|
|
52
|
+
...workflowExecutionToken !== void 0 && { workflowExecutionToken },
|
|
53
|
+
...activityAttemptToken !== void 0 && { activityAttemptToken }
|
|
51
54
|
});
|
|
52
55
|
if (result.status === "failed") {
|
|
53
56
|
const error = Error(result.error);
|
|
@@ -69,12 +72,15 @@ function getActivityAttempt(operation) {
|
|
|
69
72
|
const attempt = operation.attempt;
|
|
70
73
|
return typeof attempt === "number" && Number.isInteger(attempt) && attempt > 0 ? attempt : 1;
|
|
71
74
|
}
|
|
75
|
+
function getActivityStateKey(operation) {
|
|
76
|
+
return operation.activityStateKey ?? operation.step ?? 0;
|
|
77
|
+
}
|
|
72
78
|
export async function executeActivity(internals, workflowId, operation, callbacks, attempt = getActivityAttempt(operation), coordinatorSignal) {
|
|
73
|
-
const activityInput = operation.input,
|
|
74
|
-
warnIfRetryMissingHeartbeat(internals, workflowId,
|
|
75
|
-
const asyncToken = deriveAsyncActivityToken(workflowId,
|
|
79
|
+
const activityInput = operation.input, activityStateKey = getActivityStateKey(operation), workflowExecutionToken = operation.workflowExecutionToken;
|
|
80
|
+
warnIfRetryMissingHeartbeat(internals, workflowId, activityStateKey, attempt);
|
|
81
|
+
const asyncToken = deriveAsyncActivityToken(workflowId, activityStateKey, attempt), { perAttemptTimeoutMs, attemptAbortController, activitySignal } = resolvePerAttemptTimeout(internals, workflowId, operation, coordinatorSignal), activityContext = buildActivityContext(internals, workflowId, workflowExecutionToken, activityStateKey, attempt, activitySignal, () => {
|
|
76
82
|
throw new AsyncActivityDeferral(asyncToken);
|
|
77
|
-
}), invokeActivity = internals.activityWorkerDispatcher ? (activityName, input) => invokeWorkerActivity(internals, operation.operationId, activityName, input, attempt) : (activityName, input) => withPerAttemptTimeout(invokeInlineActivity(internals, workflowId, operation, activityContext, activityName, input), perAttemptTimeoutMs, activityName, attempt, attemptAbortController), composedActivity = callbacks.getComposedActivityInterceptor(), executeWithActivityInterceptors = async (activityName, input, headers) => {
|
|
83
|
+
}), activityAttemptToken = activityContext.activityAttemptToken, invokeActivity = internals.activityWorkerDispatcher ? (activityName, input) => invokeWorkerActivity(internals, operation.operationId, activityName, input, attempt, workflowExecutionToken, activityAttemptToken) : (activityName, input) => withPerAttemptTimeout(invokeInlineActivity(internals, workflowId, operation, activityContext, activityName, input), perAttemptTimeoutMs, activityName, attempt, attemptAbortController), composedActivity = callbacks.getComposedActivityInterceptor(), executeWithActivityInterceptors = async (activityName, input, headers) => {
|
|
78
84
|
if (!composedActivity)
|
|
79
85
|
return invokeActivity(activityName, input);
|
|
80
86
|
const activityInterception = {
|
|
@@ -107,7 +113,7 @@ export async function executeActivity(internals, workflowId, operation, callback
|
|
|
107
113
|
copyActivityHeadersToOperation(operation, headers);
|
|
108
114
|
return result;
|
|
109
115
|
}
|
|
110
|
-
export async function executeActivityOperationResult(internals, workflowId, operation, callbacks, coordinatorSignal, speculativeState) {
|
|
116
|
+
export async function executeActivityOperationResult(internals, workflowId, operation, callbacks, coordinatorSignal, speculativeState, executionOptions = {}) {
|
|
111
117
|
const activity = getActivityFunctionWithMetadata(internals, workflowId, operation), idempotencyKey = resolveActivityIdempotencyKey(activity, operation), operationAttempt = getActivityAttempt(operation);
|
|
112
118
|
if (idempotencyKey !== void 0) {
|
|
113
119
|
const reference = await buildActivityReconciliationReference(workflowId, operation.activityName, idempotencyKey), started = await resolveStartedActivityReconciliationRecord(internals, workflowId, operation, reference, activity, idempotencyKey, operationAttempt);
|
|
@@ -119,16 +125,24 @@ export async function executeActivityOperationResult(internals, workflowId, oper
|
|
|
119
125
|
validateActivityResultForReconciliation(result, internals.options.payloadSizePolicy.maxBytes);
|
|
120
126
|
await finalizeActivityResult(internals, workflowId, operation, result, activity, idempotencyKey, started.attempt, speculativeState, !0);
|
|
121
127
|
const completedRecord = createCompletedActivityReconciliationRecord(started, result, internals.options.getNow());
|
|
122
|
-
|
|
128
|
+
if (executionOptions.reconciliationCompletion === "immediate-fenced") {
|
|
129
|
+
const finishImmediateReconciliationCommit = executionOptions.beforeImmediateReconciliationCommit?.();
|
|
130
|
+
try {
|
|
131
|
+
await commitActivityReconciliationTransitionWithFencedWrite(internals, reference, started, completedRecord);
|
|
132
|
+
} finally {
|
|
133
|
+
finishImmediateReconciliationCommit?.();
|
|
134
|
+
}
|
|
135
|
+
} else
|
|
136
|
+
stageActivityReconciliationTransitionWithAtomicWorkflowCommit(internals, workflowId, reference, started, completedRecord);
|
|
123
137
|
if (!speculativeState)
|
|
124
|
-
clearLastHeartbeatForStep(internals, workflowId, operation
|
|
138
|
+
clearLastHeartbeatForStep(internals, workflowId, getActivityStateKey(operation));
|
|
125
139
|
return result;
|
|
126
140
|
}
|
|
127
141
|
const result = await executeActivity(internals, workflowId, operation, callbacks, operationAttempt, coordinatorSignal);
|
|
128
142
|
assertPayloadWithinLimit(result, internals.options.payloadSizePolicy.maxBytes, "activity result");
|
|
129
143
|
await finalizeActivityResult(internals, workflowId, operation, result, activity, idempotencyKey, operationAttempt, speculativeState);
|
|
130
144
|
if (!speculativeState)
|
|
131
|
-
clearLastHeartbeatForStep(internals, workflowId, operation
|
|
145
|
+
clearLastHeartbeatForStep(internals, workflowId, getActivityStateKey(operation));
|
|
132
146
|
return result;
|
|
133
147
|
}
|
|
134
148
|
async function finalizeActivityResult(internals, workflowId, operation, result, activity, idempotencyKey, attempt, speculativeState, awaitSpeculativeVerification = !1) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ContextOperationRequest } from '../context.ts';
|
|
2
2
|
import type { EngineInternals } from './internals.ts';
|
|
3
|
+
import type { ActivityOperationCallbacks } from './operations-activity.ts';
|
|
3
4
|
import type { OperationWithCallerStack } from './operations-router.ts';
|
|
4
5
|
type MemoOperation = Extract<ContextOperationRequest, {
|
|
5
6
|
type: 'memo';
|
|
@@ -15,8 +16,10 @@ type ArchiveOperation = Extract<ContextOperationRequest, {
|
|
|
15
16
|
}>;
|
|
16
17
|
export type DataOperationCallbacks = {
|
|
17
18
|
runOperationWithResult: (workflowId: string, operation: OperationWithCallerStack, execute: () => Promise<unknown>) => Promise<void>;
|
|
19
|
+
persistCheckpoint: (workflowId: string, operation: ContextOperationRequest) => Promise<void>;
|
|
20
|
+
getActivityOperationCallbacks?: () => ActivityOperationCallbacks;
|
|
18
21
|
};
|
|
19
|
-
export declare function processMemoOperation(
|
|
22
|
+
export declare function processMemoOperation(internals: EngineInternals, workflowId: string, operation: MemoOperation, callbacks: DataOperationCallbacks): Promise<void>;
|
|
20
23
|
export declare function processOffloadOperation(internals: EngineInternals, workflowId: string, operation: OffloadOperation, callbacks: DataOperationCallbacks): Promise<void>;
|
|
21
24
|
export declare function processLoadOperation(internals: EngineInternals, workflowId: string, operation: LoadOperation, callbacks: DataOperationCallbacks): Promise<void>;
|
|
22
25
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { KEYS } from "../../storage/interface.js";
|
|
2
2
|
import { decode, encode } from "../codec.js";
|
|
3
|
-
import {
|
|
4
|
-
export async function processMemoOperation(
|
|
5
|
-
return callbacks.runOperationWithResult(workflowId, operation, async () =>
|
|
3
|
+
import { callMemoFunctionWithDurableActivityScope } from "./memo-durable-activity.js";
|
|
4
|
+
export async function processMemoOperation(internals, workflowId, operation, callbacks) {
|
|
5
|
+
return callbacks.runOperationWithResult(workflowId, operation, async () => callMemoFunctionWithDurableActivityScope(internals, workflowId, operation, callbacks));
|
|
6
6
|
}
|
|
7
7
|
export async function processOffloadOperation(internals, workflowId, operation, callbacks) {
|
|
8
8
|
return callbacks.runOperationWithResult(workflowId, operation, async () => {
|
|
@@ -12,7 +12,7 @@ export type TimeOperationCallbacks = {
|
|
|
12
12
|
loadWorkflowState: (workflowId: string) => Promise<WorkflowState | null>;
|
|
13
13
|
failWorkflow: (workflowId: string, error: Error) => Promise<void>;
|
|
14
14
|
runSerializedWorkflowStateWrite: <Result>(workflowId: string, writeOperation: () => Promise<Result>) => Promise<Result>;
|
|
15
|
-
beginWorkflowExecution: (workflowId: string, workflowType: string, input: unknown, checkpoint: Checkpoint, executionDeadline: number | undefined, executionStateOwnerId: string, registration: RegistrationEntry) => void;
|
|
15
|
+
beginWorkflowExecution: (workflowId: string, workflowExecutionToken: string | undefined, workflowType: string, input: unknown, checkpoint: Checkpoint, executionDeadline: number | undefined, executionStateOwnerId: string, registration: RegistrationEntry) => void;
|
|
16
16
|
workflowVersionTupleFromState: (state: WorkflowState) => WorkflowVersionTuple;
|
|
17
17
|
setWorkflowStartHeaders: (workflowId: string, headers: Map<string, string> | undefined) => void;
|
|
18
18
|
loadWorkflowStartHeaders: (workflowId: string) => Promise<Map<string, string> | undefined>;
|
|
@@ -25,7 +25,7 @@ export type TimeOperationCallbacks = {
|
|
|
25
25
|
};
|
|
26
26
|
export declare function createDelayedStartTimerEntry(_internals: EngineInternals, workflowId: string, scheduledStartAt: number, options: StartOptions | undefined, callbacks: Pick<TimeOperationCallbacks, 'parseStartOptionDuration'>): TimerEntry;
|
|
27
27
|
export declare function processSleepOperation(internals: EngineInternals, workflowId: string, operation: SleepOperation, callbacks: Pick<TimeOperationCallbacks, 'completeOperation' | 'loadWorkflowState'>): Promise<void>;
|
|
28
|
-
export declare function registerSleepResolver(internals: EngineInternals, workflowId: string, operationId: string, resolve: () => void): void;
|
|
28
|
+
export declare function registerSleepResolver(internals: EngineInternals, workflowId: string, operationId: string, resolve: () => void, scheduledFireAt: number): void;
|
|
29
29
|
export declare function startDelayedWorkflow(internals: EngineInternals, entry: TimerEntry, callbacks: Pick<TimeOperationCallbacks, 'beginWorkflowExecution' | 'dispatchEvent' | 'failWorkflow' | 'handleCleanupError' | 'loadWorkflowStartHeaders' | 'loadWorkflowState' | 'runSerializedWorkflowStateWrite' | 'setWorkflowStartHeaders' | 'workflowVersionTupleFromState'>): Promise<void>;
|
|
30
30
|
export declare function handleTimerFired(internals: EngineInternals, entry: TimerEntry, callbacks: Pick<TimeOperationCallbacks, 'failWorkflow' | 'handleCleanupError' | 'loadWorkflowStartHeaders' | 'loadWorkflowState' | 'runDeferredTerminalCleanup' | 'runWorkflowFinalizer' | 'runSerializedWorkflowStateWrite' | 'handleScheduleTimer' | 'setWorkflowStartHeaders' | 'timeout' | 'beginWorkflowExecution' | 'dispatchEvent' | 'workflowVersionTupleFromState'>): Promise<void>;
|
|
31
31
|
export {};
|
|
@@ -16,6 +16,18 @@ export function createDelayedStartTimerEntry(_internals, workflowId, scheduledSt
|
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
+
function sleepTimerFiredEarly(internals, workflowId, operation) {
|
|
20
|
+
const workflowMarkers = internals.sleepTimersFiredWithoutResolver.get(workflowId);
|
|
21
|
+
if (!workflowMarkers)
|
|
22
|
+
return !1;
|
|
23
|
+
const markedFireAt = workflowMarkers.get(operation.operationId);
|
|
24
|
+
if (markedFireAt === void 0)
|
|
25
|
+
return !1;
|
|
26
|
+
workflowMarkers.delete(operation.operationId);
|
|
27
|
+
if (workflowMarkers.size === 0)
|
|
28
|
+
internals.sleepTimersFiredWithoutResolver.delete(workflowId);
|
|
29
|
+
return markedFireAt >= operation.scheduledFireAt;
|
|
30
|
+
}
|
|
19
31
|
export async function processSleepOperation(internals, workflowId, operation, callbacks) {
|
|
20
32
|
if (operation.scheduledFireAt <= internals.options.getNow()) {
|
|
21
33
|
callbacks.completeOperation(workflowId, void 0);
|
|
@@ -28,13 +40,24 @@ export async function processSleepOperation(internals, workflowId, operation, ca
|
|
|
28
40
|
fireAt: operation.scheduledFireAt,
|
|
29
41
|
kind: "sleep"
|
|
30
42
|
});
|
|
31
|
-
registerSleepResolver(internals, workflowId, operation.operationId, resolve);
|
|
43
|
+
registerSleepResolver(internals, workflowId, operation.operationId, resolve, operation.scheduledFireAt);
|
|
44
|
+
const resolverKey = `${workflowId}:${operation.operationId}`;
|
|
45
|
+
if (sleepTimerFiredEarly(internals, workflowId, operation) && internals.sleepResolvers.has(resolverKey))
|
|
46
|
+
resolveSleepTimer(internals, {
|
|
47
|
+
id: `sleep:${operation.operationId}`,
|
|
48
|
+
workflowId,
|
|
49
|
+
fireAt: operation.scheduledFireAt,
|
|
50
|
+
kind: "sleep"
|
|
51
|
+
});
|
|
32
52
|
await promise;
|
|
33
53
|
if ((await callbacks.loadWorkflowState(workflowId))?.status === "running")
|
|
34
54
|
callbacks.completeOperation(workflowId, void 0);
|
|
35
55
|
}
|
|
36
|
-
export function registerSleepResolver(internals, workflowId, operationId, resolve) {
|
|
37
|
-
internals.sleepResolvers.set(`${workflowId}:${operationId}`,
|
|
56
|
+
export function registerSleepResolver(internals, workflowId, operationId, resolve, scheduledFireAt) {
|
|
57
|
+
internals.sleepResolvers.set(`${workflowId}:${operationId}`, {
|
|
58
|
+
resolve,
|
|
59
|
+
fireAt: scheduledFireAt
|
|
60
|
+
});
|
|
38
61
|
let workflowOperations = internals.sleepResolversByWorkflow.get(workflowId);
|
|
39
62
|
if (!workflowOperations) {
|
|
40
63
|
workflowOperations = new Set;
|
|
@@ -94,7 +117,7 @@ export async function startDelayedWorkflow(internals, entry, callbacks) {
|
|
|
94
117
|
internals.checkpoints.set(entry.workflowId, checkpoint);
|
|
95
118
|
internals.workflowVersionTuples.set(entry.workflowId, callbacks.workflowVersionTupleFromState(runningState));
|
|
96
119
|
callbacks.setWorkflowStartHeaders(entry.workflowId, await callbacks.loadWorkflowStartHeaders(entry.workflowId));
|
|
97
|
-
callbacks.beginWorkflowExecution(entry.workflowId, runningState.type, runningState.input, checkpoint, executionDeadline, runningState.executionStateOwnerId ?? entry.workflowId, registration);
|
|
120
|
+
callbacks.beginWorkflowExecution(entry.workflowId, runningState.workflowExecutionToken, runningState.type, runningState.input, checkpoint, executionDeadline, runningState.executionStateOwnerId ?? entry.workflowId, registration);
|
|
98
121
|
}
|
|
99
122
|
async function loadDelayedWorkflowCheckpoint(internals, entry, callbacks) {
|
|
100
123
|
const checkpointBytes = await internals.storage.get(KEYS.checkpoint(entry.workflowId));
|
|
@@ -168,11 +191,22 @@ async function handleReviewTimer(internals, entry, callbacks) {
|
|
|
168
191
|
}
|
|
169
192
|
function resolveSleepTimer(internals, entry) {
|
|
170
193
|
const operationId = entry.id.replace("sleep:", ""), resolverKey = `${entry.workflowId}:${operationId}`, resolver = internals.sleepResolvers.get(resolverKey);
|
|
171
|
-
if (!resolver)
|
|
194
|
+
if (!resolver) {
|
|
195
|
+
let workflowMarkers = internals.sleepTimersFiredWithoutResolver.get(entry.workflowId);
|
|
196
|
+
if (!workflowMarkers) {
|
|
197
|
+
workflowMarkers = new Map;
|
|
198
|
+
internals.sleepTimersFiredWithoutResolver.set(entry.workflowId, workflowMarkers);
|
|
199
|
+
}
|
|
200
|
+
const existing = workflowMarkers.get(operationId);
|
|
201
|
+
if (existing === void 0 || entry.fireAt > existing)
|
|
202
|
+
workflowMarkers.set(operationId, entry.fireAt);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (entry.fireAt < resolver.fireAt)
|
|
172
206
|
return;
|
|
173
207
|
internals.sleepResolvers.delete(resolverKey);
|
|
174
208
|
untrackSleepResolver(internals, entry.workflowId, operationId);
|
|
175
|
-
resolver();
|
|
209
|
+
resolver.resolve();
|
|
176
210
|
}
|
|
177
211
|
function untrackSleepResolver(internals, workflowId, operationId) {
|
|
178
212
|
const workflowOperations = internals.sleepResolversByWorkflow.get(workflowId);
|
|
@@ -3,6 +3,7 @@ import { ReviewCompletedEvent, ReviewRequestedEvent } from "../review/events.js"
|
|
|
3
3
|
import {
|
|
4
4
|
ReviewTimeoutError
|
|
5
5
|
} from "../review/index.js";
|
|
6
|
+
import { stageAtomicWorkflowCommitSideEffects } from "./checkpoint-side-effects.js";
|
|
6
7
|
import {
|
|
7
8
|
deleteCompletedReviewsForWorkflow,
|
|
8
9
|
listCompletedReviewsFromStorage,
|
|
@@ -26,7 +27,7 @@ async function listPendingReviews(internals, filter) {
|
|
|
26
27
|
return reviews;
|
|
27
28
|
}
|
|
28
29
|
async function dispatchCompletedReview(internals, reviewKey, reviewData, decisionResult, dispatchEvent) {
|
|
29
|
-
await persistCompletedReviewRecord(internals
|
|
30
|
+
await persistCompletedReviewRecord(internals, reviewKey, reviewData, decisionResult);
|
|
30
31
|
dispatchEvent(new ReviewCompletedEvent(reviewData.workflowId, reviewData.reviewId, decisionResult.decision, decisionResult.reviewer, decisionResult.timestamp - reviewData.createdAt));
|
|
31
32
|
}
|
|
32
33
|
export async function listReviews(internals, filter = {}) {
|
|
@@ -96,9 +97,11 @@ export async function handleReviewEscalationTimer(internals, workflowId, reviewI
|
|
|
96
97
|
if (entry.id === `review-timeout:${reviewId}`) {
|
|
97
98
|
internals.reviewWaiters.delete(waiterKey);
|
|
98
99
|
untrackWaiterKey(internals.reviewWaitersByWorkflow, workflowId, waiterKey);
|
|
99
|
-
const elapsed = internals.options.getNow() - reviewRequest.createdAt;
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
const elapsed = internals.options.getNow() - reviewRequest.createdAt, timeoutError = new ReviewTimeoutError(reviewId, elapsed);
|
|
101
|
+
stageAtomicWorkflowCommitSideEffects(internals, workflowId, {
|
|
102
|
+
operations: [{ type: "delete", key: KEYS.review(workflowId, reviewId) }],
|
|
103
|
+
conditions: []
|
|
104
|
+
});
|
|
102
105
|
await callbacks.failWorkflow(workflowId, timeoutError);
|
|
103
106
|
resolve({ ok: !1, error: timeoutError });
|
|
104
107
|
return !0;
|
|
@@ -12,6 +12,8 @@ class ScheduleStatePersistenceError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
export async function handleScheduleTimer(internals, entry, callbacks) {
|
|
15
|
+
if (internals.deposed)
|
|
16
|
+
return;
|
|
15
17
|
const state = await loadScheduleState(internals, entry.workflowId);
|
|
16
18
|
if (!isCurrentActiveScheduleTimer(state, entry))
|
|
17
19
|
return;
|
|
@@ -35,6 +35,9 @@ export async function schedule(internals, type, input, spec, options) {
|
|
|
35
35
|
id: scheduleId,
|
|
36
36
|
workflowType: type,
|
|
37
37
|
input,
|
|
38
|
+
...normalizedOptions.description !== void 0 && {
|
|
39
|
+
description: normalizedOptions.description
|
|
40
|
+
},
|
|
38
41
|
...cadenceFields,
|
|
39
42
|
status: "active",
|
|
40
43
|
overlap: normalizedOptions.overlap,
|
|
@@ -93,7 +93,7 @@ export async function writeScheduleState(internals, state, options) {
|
|
|
93
93
|
kind: "schedule"
|
|
94
94
|
}));
|
|
95
95
|
}
|
|
96
|
-
await internals
|
|
96
|
+
await commitFencedEngineWrite(internals, operations, [], () => Error(`Schedule state commit for schedule "${state.id}" lost its precondition.`));
|
|
97
97
|
}
|
|
98
98
|
export async function loadWorkflowStartHeaders(internals, workflowId) {
|
|
99
99
|
const bytes = await internals.storage.get(KEYS.workflowHeaders(workflowId));
|
|
@@ -43,23 +43,25 @@ function cleanupTrackedWaiter(internals, workflowId, kind) {
|
|
|
43
43
|
}
|
|
44
44
|
function cleanupSleepResolvers(internals, workflowId) {
|
|
45
45
|
const sleepOps = internals.sleepResolversByWorkflow.get(workflowId);
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
if (sleepOps) {
|
|
47
|
+
for (const operationId of sleepOps) {
|
|
48
|
+
const key = `${workflowId}:${operationId}`, resolver = internals.sleepResolvers.get(key);
|
|
49
|
+
if (resolver)
|
|
50
|
+
resolver.resolve();
|
|
51
|
+
internals.sleepResolvers.delete(key);
|
|
52
|
+
}
|
|
53
|
+
internals.sleepResolversByWorkflow.delete(workflowId);
|
|
53
54
|
}
|
|
54
|
-
internals.
|
|
55
|
+
internals.sleepTimersFiredWithoutResolver.delete(workflowId);
|
|
55
56
|
}
|
|
56
57
|
function evictSleepResolversWithoutResolving(internals, workflowId) {
|
|
57
58
|
const sleepOps = internals.sleepResolversByWorkflow.get(workflowId);
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
internals.
|
|
62
|
-
|
|
59
|
+
if (sleepOps) {
|
|
60
|
+
for (const operationId of sleepOps)
|
|
61
|
+
internals.sleepResolvers.delete(`${workflowId}:${operationId}`);
|
|
62
|
+
internals.sleepResolversByWorkflow.delete(workflowId);
|
|
63
|
+
}
|
|
64
|
+
internals.sleepTimersFiredWithoutResolver.delete(workflowId);
|
|
63
65
|
}
|
|
64
66
|
function cleanupConditionWaiters(internals, workflowId) {
|
|
65
67
|
const resolver = internals.conditionWaiters.get(workflowId);
|
|
@@ -50,4 +50,4 @@ export type FinalizerAttemptResult = {
|
|
|
50
50
|
* @param attempt - 1-based attempt number, for the per-attempt timeout error message
|
|
51
51
|
* @param shutdownSignal - the engine's dispose/shutdown abort signal; aborting it stops a cooperating finalizer
|
|
52
52
|
*/
|
|
53
|
-
export declare function runFinalizerActivity(finalizer: RunnableFinalizer, input: unknown, attempt: number, shutdownSignal: AbortSignal): Promise<FinalizerAttemptResult>;
|
|
53
|
+
export declare function runFinalizerActivity(finalizer: RunnableFinalizer, input: unknown, attempt: number, shutdownSignal: AbortSignal, workflowExecutionToken?: string): Promise<FinalizerAttemptResult>;
|