deepline 0.1.182 → 0.1.184
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/apps/play-runner-workers/src/child-play-await.ts +203 -6
- package/dist/bundling-sources/apps/play-runner-workers/src/child-play-submit.ts +8 -1
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +639 -109
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +3 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +1752 -1899
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +116 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +542 -78
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +292 -11
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/run-work-dispatcher.ts +519 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +2381 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +18 -5
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/work-budget.ts +130 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/worker-platform-budget.ts +39 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +2 -0
- package/dist/bundling-sources/sdk/src/client.ts +41 -0
- package/dist/bundling-sources/sdk/src/http.ts +23 -8
- package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +56 -3
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +133 -10
- package/dist/bundling-sources/shared_libs/play-runtime/auth-scope-resolver.ts +92 -0
- package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +4 -4
- package/dist/bundling-sources/shared_libs/play-runtime/batching-types.ts +8 -0
- package/dist/bundling-sources/shared_libs/play-runtime/child-run-id.ts +101 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1612 -287
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +105 -6
- package/dist/bundling-sources/shared_libs/play-runtime/dedup-backend.ts +0 -0
- package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +116 -16
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +186 -20
- package/dist/bundling-sources/shared_libs/play-runtime/dynamic-worker-version.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-ledger-store.ts +133 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +245 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -5
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +14 -0
- package/dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts +52 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +124 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +6 -2
- package/dist/bundling-sources/shared_libs/play-runtime/row-isolation.ts +48 -2
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +158 -15
- package/dist/bundling-sources/shared_libs/play-runtime/run-terminal-source.ts +45 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-paths.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +2038 -262
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +42 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-attempt-state-machine.ts +183 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-errors.ts +88 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +8 -1
- package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +19 -1
- package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +343 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +437 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +83 -6
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +28 -11
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +38 -6
- package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +24 -17
- package/dist/cli/index.js +76 -17
- package/dist/cli/index.mjs +76 -17
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +63 -9
- package/dist/index.mjs +63 -9
- package/dist/plays/bundle-play-file.mjs +20 -6
- package/package.json +1 -1
|
@@ -3,10 +3,20 @@ import {
|
|
|
3
3
|
type WorkReceipt,
|
|
4
4
|
type WorkReceiptClaim,
|
|
5
5
|
type WorkReceiptCommand,
|
|
6
|
+
type WorkReceiptFailureKind,
|
|
6
7
|
type WorkReceiptGetCommand,
|
|
7
8
|
type WorkReceiptStatus,
|
|
8
|
-
type WorkReceiptStore,
|
|
9
9
|
} from '../../../../shared_libs/play-runtime/work-receipts';
|
|
10
|
+
import type { ExecutionLedgerStore } from '../../../../shared_libs/play-runtime/execution-ledger-store';
|
|
11
|
+
import {
|
|
12
|
+
PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
|
|
13
|
+
runtimeLeaseHeartbeatIntervalMs,
|
|
14
|
+
} from '../../../../shared_libs/play-runtime/lease-policy';
|
|
15
|
+
import {
|
|
16
|
+
isPlayExecutionSuspendedError,
|
|
17
|
+
isPlayRowExecutionSuspendedError,
|
|
18
|
+
} from '../../../../shared_libs/play-runtime/suspension';
|
|
19
|
+
import type { WorkerWorkBudgetMeter } from './work-budget';
|
|
10
20
|
|
|
11
21
|
export type RuntimeReceiptStatus = WorkReceiptStatus;
|
|
12
22
|
|
|
@@ -17,14 +27,34 @@ export type WorkerRuntimeReceiptGetCommand = WorkReceiptGetCommand;
|
|
|
17
27
|
|
|
18
28
|
export type WorkerRuntimeReceiptClaim = WorkReceiptClaim;
|
|
19
29
|
|
|
20
|
-
export type WorkerRuntimeReceiptStore =
|
|
30
|
+
export type WorkerRuntimeReceiptStore = Pick<
|
|
31
|
+
ExecutionLedgerStore,
|
|
32
|
+
'claimReceipt' | 'completeReceipt' | 'failReceipt'
|
|
33
|
+
> &
|
|
34
|
+
Partial<
|
|
35
|
+
Pick<
|
|
36
|
+
ExecutionLedgerStore,
|
|
37
|
+
| 'getReceipt'
|
|
38
|
+
| 'claimReceipts'
|
|
39
|
+
| 'completeReceipts'
|
|
40
|
+
| 'failReceipts'
|
|
41
|
+
| 'releaseReceipt'
|
|
42
|
+
| 'heartbeatReceipts'
|
|
43
|
+
>
|
|
44
|
+
>;
|
|
21
45
|
|
|
22
46
|
type RuntimeReceiptContext = {
|
|
23
47
|
orgId?: string | null;
|
|
24
48
|
playName: string;
|
|
25
49
|
runId: string;
|
|
50
|
+
runAttempt?: number | null;
|
|
26
51
|
key: string;
|
|
27
52
|
receiptStore: WorkerRuntimeReceiptStore;
|
|
53
|
+
budgetMeter?: WorkerWorkBudgetMeter;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type WorkerRuntimeReceiptExecutionContext = {
|
|
57
|
+
leaseId: string | null;
|
|
28
58
|
};
|
|
29
59
|
|
|
30
60
|
const WORKER_RECEIPT_DEFAULT_WAIT_MS = 300_000;
|
|
@@ -32,6 +62,9 @@ const WORKER_RECEIPT_WAIT_DELAY_MS = 250;
|
|
|
32
62
|
const WORKER_RECEIPT_WAIT_MAX_ATTEMPTS = Math.ceil(
|
|
33
63
|
WORKER_RECEIPT_DEFAULT_WAIT_MS / WORKER_RECEIPT_WAIT_DELAY_MS,
|
|
34
64
|
);
|
|
65
|
+
const WORKER_RECEIPT_HEARTBEAT_INTERVAL_MS = runtimeLeaseHeartbeatIntervalMs(
|
|
66
|
+
PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
|
|
67
|
+
);
|
|
35
68
|
|
|
36
69
|
class RuntimeReceiptWaitTimeoutError extends Error {
|
|
37
70
|
constructor(key: string) {
|
|
@@ -40,6 +73,32 @@ class RuntimeReceiptWaitTimeoutError extends Error {
|
|
|
40
73
|
}
|
|
41
74
|
}
|
|
42
75
|
|
|
76
|
+
export class RuntimeLeaseLostError extends Error {
|
|
77
|
+
readonly keys: string[];
|
|
78
|
+
readonly runId: string;
|
|
79
|
+
readonly leaseId: string;
|
|
80
|
+
|
|
81
|
+
constructor(input: {
|
|
82
|
+
keys: string[];
|
|
83
|
+
runId: string;
|
|
84
|
+
leaseId: string;
|
|
85
|
+
cause?: unknown;
|
|
86
|
+
}) {
|
|
87
|
+
const keySummary =
|
|
88
|
+
input.keys.length === 1
|
|
89
|
+
? input.keys[0]!
|
|
90
|
+
: `${input.keys.length} receipts`;
|
|
91
|
+
super(
|
|
92
|
+
`Runtime receipt ${keySummary} lost lease ${input.leaseId} before execution finished.`,
|
|
93
|
+
{ cause: input.cause },
|
|
94
|
+
);
|
|
95
|
+
this.name = 'RuntimeLeaseLostError';
|
|
96
|
+
this.keys = input.keys;
|
|
97
|
+
this.runId = input.runId;
|
|
98
|
+
this.leaseId = input.leaseId;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
43
102
|
function scopedReceiptKey(input: {
|
|
44
103
|
orgId?: string | null;
|
|
45
104
|
playName: string;
|
|
@@ -60,6 +119,15 @@ function errorMessage(error: unknown): string {
|
|
|
60
119
|
return error instanceof Error ? error.message : String(error);
|
|
61
120
|
}
|
|
62
121
|
|
|
122
|
+
function runtimeReceiptFailureKindForError(
|
|
123
|
+
error: unknown,
|
|
124
|
+
): WorkReceiptFailureKind {
|
|
125
|
+
return isPlayExecutionSuspendedError(error) ||
|
|
126
|
+
isPlayRowExecutionSuspendedError(error)
|
|
127
|
+
? 'repairable'
|
|
128
|
+
: 'terminal';
|
|
129
|
+
}
|
|
130
|
+
|
|
63
131
|
async function waitForReusableReceipt<T>(input: {
|
|
64
132
|
key: string;
|
|
65
133
|
playName: string;
|
|
@@ -94,6 +162,141 @@ async function waitForReusableReceipt<T>(input: {
|
|
|
94
162
|
throw new RuntimeReceiptWaitTimeoutError(input.key);
|
|
95
163
|
}
|
|
96
164
|
|
|
165
|
+
type WorkerRuntimeReceiptHeartbeatTarget = {
|
|
166
|
+
key: string | null | undefined;
|
|
167
|
+
leaseId: string | null | undefined;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
function heartbeatGroups(
|
|
171
|
+
targets: WorkerRuntimeReceiptHeartbeatTarget[],
|
|
172
|
+
): Array<{ leaseId: string; keys: string[] }> {
|
|
173
|
+
const byLeaseId = new Map<string, string[]>();
|
|
174
|
+
for (const target of targets) {
|
|
175
|
+
const key = target.key?.trim();
|
|
176
|
+
const leaseId = target.leaseId?.trim();
|
|
177
|
+
if (!key || !leaseId) continue;
|
|
178
|
+
const keys = byLeaseId.get(leaseId);
|
|
179
|
+
if (keys) {
|
|
180
|
+
keys.push(key);
|
|
181
|
+
} else {
|
|
182
|
+
byLeaseId.set(leaseId, [key]);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return Array.from(byLeaseId, ([leaseId, keys]) => ({ leaseId, keys }));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function buildLeaseLostError(input: {
|
|
189
|
+
keys: string[];
|
|
190
|
+
runId: string;
|
|
191
|
+
leaseId: string;
|
|
192
|
+
cause?: unknown;
|
|
193
|
+
}): RuntimeLeaseLostError {
|
|
194
|
+
return new RuntimeLeaseLostError(input);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function executeWithWorkerReceiptHeartbeats<T>(input: {
|
|
198
|
+
playName: string;
|
|
199
|
+
runId: string;
|
|
200
|
+
runAttempt?: number | null;
|
|
201
|
+
receiptStore?: WorkerRuntimeReceiptStore | null;
|
|
202
|
+
budgetMeter?: WorkerWorkBudgetMeter;
|
|
203
|
+
targets: WorkerRuntimeReceiptHeartbeatTarget[];
|
|
204
|
+
heartbeatIntervalMs?: number;
|
|
205
|
+
execute: () => Promise<T> | T;
|
|
206
|
+
}): Promise<T> {
|
|
207
|
+
const groups = heartbeatGroups(input.targets);
|
|
208
|
+
const heartbeatReceipts = input.receiptStore?.heartbeatReceipts;
|
|
209
|
+
if (groups.length === 0 || !heartbeatReceipts) {
|
|
210
|
+
return await input.execute();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const heartbeatIntervalMs =
|
|
214
|
+
input.heartbeatIntervalMs ?? WORKER_RECEIPT_HEARTBEAT_INTERVAL_MS;
|
|
215
|
+
if (!Number.isFinite(heartbeatIntervalMs) || heartbeatIntervalMs <= 0) {
|
|
216
|
+
throw new Error('Runtime receipt heartbeat interval must be positive.');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let stopped = false;
|
|
220
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
221
|
+
let rejectLeaseLost!: (error: RuntimeLeaseLostError) => void;
|
|
222
|
+
const leaseLost = new Promise<never>((_, reject) => {
|
|
223
|
+
rejectLeaseLost = reject;
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const stop = () => {
|
|
227
|
+
stopped = true;
|
|
228
|
+
if (timeout !== null) {
|
|
229
|
+
clearTimeout(timeout);
|
|
230
|
+
timeout = null;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const heartbeatOnce = async () => {
|
|
235
|
+
for (const group of groups) {
|
|
236
|
+
input.budgetMeter?.count('heartbeat');
|
|
237
|
+
const receipts = await heartbeatReceipts({
|
|
238
|
+
playName: input.playName,
|
|
239
|
+
runId: input.runId,
|
|
240
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
241
|
+
leaseId: group.leaseId,
|
|
242
|
+
keys: group.keys,
|
|
243
|
+
});
|
|
244
|
+
for (let index = 0; index < group.keys.length; index += 1) {
|
|
245
|
+
const receipt = receipts[index];
|
|
246
|
+
if (
|
|
247
|
+
!receipt ||
|
|
248
|
+
receipt.status !== 'running' ||
|
|
249
|
+
receipt.leaseId !== group.leaseId
|
|
250
|
+
) {
|
|
251
|
+
throw buildLeaseLostError({
|
|
252
|
+
keys: group.keys,
|
|
253
|
+
runId: input.runId,
|
|
254
|
+
leaseId: group.leaseId,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const schedule = () => {
|
|
262
|
+
timeout = setTimeout(() => {
|
|
263
|
+
void (async () => {
|
|
264
|
+
try {
|
|
265
|
+
await heartbeatOnce();
|
|
266
|
+
} catch (error) {
|
|
267
|
+
stop();
|
|
268
|
+
if (error instanceof RuntimeLeaseLostError) {
|
|
269
|
+
rejectLeaseLost(error);
|
|
270
|
+
} else {
|
|
271
|
+
rejectLeaseLost(
|
|
272
|
+
buildLeaseLostError({
|
|
273
|
+
keys: groups.flatMap((group) => group.keys),
|
|
274
|
+
runId: input.runId,
|
|
275
|
+
leaseId: groups[0]?.leaseId ?? 'unknown',
|
|
276
|
+
cause: error,
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (!stopped) {
|
|
283
|
+
schedule();
|
|
284
|
+
}
|
|
285
|
+
})();
|
|
286
|
+
}, heartbeatIntervalMs);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
await heartbeatOnce();
|
|
290
|
+
schedule();
|
|
291
|
+
const execution = Promise.resolve().then(input.execute);
|
|
292
|
+
execution.catch(() => {});
|
|
293
|
+
try {
|
|
294
|
+
return await Promise.race([execution, leaseLost]);
|
|
295
|
+
} finally {
|
|
296
|
+
stop();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
97
300
|
function shouldRepairSameRunRunningReceipt(input: {
|
|
98
301
|
repairRunningReceiptForSameRun?: boolean;
|
|
99
302
|
currentRunId: string;
|
|
@@ -111,19 +314,65 @@ async function executeAndPersistReceipt<T>(input: {
|
|
|
111
314
|
key: string;
|
|
112
315
|
playName: string;
|
|
113
316
|
runId: string;
|
|
114
|
-
|
|
317
|
+
runAttempt?: number | null;
|
|
318
|
+
leaseId?: string | null;
|
|
319
|
+
execute: (context: WorkerRuntimeReceiptExecutionContext) => Promise<T> | T;
|
|
115
320
|
receiptStore: WorkerRuntimeReceiptStore;
|
|
116
321
|
ownership: 'claimed' | 'reconciled';
|
|
322
|
+
budgetMeter?: WorkerWorkBudgetMeter;
|
|
323
|
+
heartbeatIntervalMs?: number;
|
|
117
324
|
}): Promise<T> {
|
|
118
325
|
let output: T;
|
|
119
326
|
try {
|
|
120
|
-
|
|
327
|
+
const leaseId = input.leaseId?.trim() || null;
|
|
328
|
+
output = await executeWithWorkerReceiptHeartbeats({
|
|
329
|
+
playName: input.playName,
|
|
330
|
+
runId: input.runId,
|
|
331
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
332
|
+
receiptStore: input.receiptStore,
|
|
333
|
+
budgetMeter: input.budgetMeter,
|
|
334
|
+
heartbeatIntervalMs: input.heartbeatIntervalMs,
|
|
335
|
+
targets: [{ key: input.key, leaseId }],
|
|
336
|
+
execute: () =>
|
|
337
|
+
input.execute({
|
|
338
|
+
leaseId,
|
|
339
|
+
}),
|
|
340
|
+
});
|
|
121
341
|
} catch (error) {
|
|
342
|
+
if (error instanceof RuntimeLeaseLostError) {
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
if (
|
|
346
|
+
isPlayExecutionSuspendedError(error) ||
|
|
347
|
+
isPlayRowExecutionSuspendedError(error)
|
|
348
|
+
) {
|
|
349
|
+
if (!input.receiptStore.releaseReceipt) {
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Runtime receipt ${input.key} suspended but the receipt store cannot release ownership: ${errorMessage(error)}`,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
const released = await input.receiptStore.releaseReceipt({
|
|
355
|
+
playName: input.playName,
|
|
356
|
+
runId: input.runId,
|
|
357
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
358
|
+
key: input.key,
|
|
359
|
+
...(input.leaseId ? { leaseId: input.leaseId } : {}),
|
|
360
|
+
});
|
|
361
|
+
if (!released || released.status !== 'pending') {
|
|
362
|
+
throw new Error(
|
|
363
|
+
`Runtime receipt ${input.key} suspended but failed to release ownership: ${errorMessage(error)}`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
throw error;
|
|
367
|
+
}
|
|
122
368
|
const failed = await input.receiptStore.failReceipt({
|
|
123
369
|
playName: input.playName,
|
|
124
370
|
runId: input.runId,
|
|
371
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
125
372
|
key: input.key,
|
|
373
|
+
...(input.leaseId ? { leaseId: input.leaseId } : {}),
|
|
126
374
|
error: errorMessage(error),
|
|
375
|
+
failureKind: runtimeReceiptFailureKindForError(error),
|
|
127
376
|
});
|
|
128
377
|
if (!failed) {
|
|
129
378
|
throw new Error(
|
|
@@ -136,16 +385,20 @@ async function executeAndPersistReceipt<T>(input: {
|
|
|
136
385
|
const completed = await input.receiptStore.completeReceipt({
|
|
137
386
|
playName: input.playName,
|
|
138
387
|
runId: input.runId,
|
|
388
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
139
389
|
key: input.key,
|
|
390
|
+
...(input.leaseId ? { leaseId: input.leaseId } : {}),
|
|
140
391
|
output,
|
|
141
392
|
});
|
|
142
|
-
if (!completed) {
|
|
143
|
-
return output;
|
|
144
|
-
}
|
|
145
393
|
if (
|
|
146
|
-
|
|
147
|
-
completed.
|
|
394
|
+
!completed ||
|
|
395
|
+
(completed.status !== 'completed' && completed.status !== 'skipped')
|
|
148
396
|
) {
|
|
397
|
+
throw new Error(
|
|
398
|
+
`Runtime receipt ${input.key} lost receipt ownership before completion.`,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
if (completed.output !== undefined) {
|
|
149
402
|
return receiptOutput<T>(completed);
|
|
150
403
|
}
|
|
151
404
|
return output;
|
|
@@ -153,11 +406,13 @@ async function executeAndPersistReceipt<T>(input: {
|
|
|
153
406
|
|
|
154
407
|
export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
155
408
|
input: RuntimeReceiptContext & {
|
|
156
|
-
execute: () => Promise<T> | T;
|
|
409
|
+
execute: (context: WorkerRuntimeReceiptExecutionContext) => Promise<T> | T;
|
|
157
410
|
repairRunningReceiptForSameRun?: boolean;
|
|
158
411
|
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
159
412
|
runningReceiptWaitMaxAttempts?: number;
|
|
160
413
|
runningReceiptWaitDelayMs?: number;
|
|
414
|
+
runtimeReceiptHeartbeatIntervalMs?: number;
|
|
415
|
+
runtimeReceiptLeaseTtlMs?: number;
|
|
161
416
|
reclaimRunning?: boolean;
|
|
162
417
|
},
|
|
163
418
|
): Promise<T> {
|
|
@@ -166,13 +421,19 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
166
421
|
const claimed = await receiptStore.claimReceipt({
|
|
167
422
|
playName: input.playName,
|
|
168
423
|
runId: input.runId,
|
|
424
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
169
425
|
key,
|
|
426
|
+
leaseAware: true,
|
|
427
|
+
leaseTtlMs: input.runtimeReceiptLeaseTtlMs,
|
|
170
428
|
...(input.reclaimRunning === true ? { reclaimRunning: true } : {}),
|
|
171
429
|
});
|
|
172
430
|
if (claimed.disposition === 'reused') {
|
|
173
431
|
return receiptOutput<T>(claimed.receipt);
|
|
174
432
|
}
|
|
175
|
-
if (
|
|
433
|
+
if (
|
|
434
|
+
claimed.disposition === 'running' ||
|
|
435
|
+
claimed.disposition === 'blocked_active_lease'
|
|
436
|
+
) {
|
|
176
437
|
if (
|
|
177
438
|
shouldRepairSameRunRunningReceipt({
|
|
178
439
|
repairRunningReceiptForSameRun: input.repairRunningReceiptForSameRun,
|
|
@@ -183,7 +444,10 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
183
444
|
const repaired = await receiptStore.claimReceipt({
|
|
184
445
|
playName: input.playName,
|
|
185
446
|
runId: input.runId,
|
|
447
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
186
448
|
key,
|
|
449
|
+
leaseAware: true,
|
|
450
|
+
leaseTtlMs: input.runtimeReceiptLeaseTtlMs,
|
|
187
451
|
reclaimRunning: true,
|
|
188
452
|
});
|
|
189
453
|
if (repaired.disposition === 'reused') {
|
|
@@ -199,9 +463,13 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
199
463
|
key,
|
|
200
464
|
playName: input.playName,
|
|
201
465
|
runId: input.runId,
|
|
466
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
467
|
+
leaseId: repaired.receipt.leaseId ?? null,
|
|
202
468
|
execute: input.execute,
|
|
203
469
|
receiptStore,
|
|
204
470
|
ownership: 'claimed',
|
|
471
|
+
budgetMeter: input.budgetMeter,
|
|
472
|
+
heartbeatIntervalMs: input.runtimeReceiptHeartbeatIntervalMs,
|
|
205
473
|
});
|
|
206
474
|
}
|
|
207
475
|
}
|
|
@@ -226,7 +494,10 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
226
494
|
const repaired = await receiptStore.claimReceipt({
|
|
227
495
|
playName: input.playName,
|
|
228
496
|
runId: input.runId,
|
|
497
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
229
498
|
key,
|
|
499
|
+
leaseAware: true,
|
|
500
|
+
leaseTtlMs: input.runtimeReceiptLeaseTtlMs,
|
|
230
501
|
reclaimRunning: true,
|
|
231
502
|
});
|
|
232
503
|
if (repaired.disposition === 'reused') {
|
|
@@ -242,9 +513,15 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
242
513
|
key,
|
|
243
514
|
playName: input.playName,
|
|
244
515
|
runId: input.runId,
|
|
516
|
+
...(input.runAttempt != null
|
|
517
|
+
? { runAttempt: input.runAttempt }
|
|
518
|
+
: {}),
|
|
519
|
+
leaseId: repaired.receipt.leaseId ?? null,
|
|
245
520
|
execute: input.execute,
|
|
246
521
|
receiptStore,
|
|
247
522
|
ownership: 'claimed',
|
|
523
|
+
budgetMeter: input.budgetMeter,
|
|
524
|
+
heartbeatIntervalMs: input.runtimeReceiptHeartbeatIntervalMs,
|
|
248
525
|
});
|
|
249
526
|
}
|
|
250
527
|
}
|
|
@@ -261,8 +538,12 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
261
538
|
key,
|
|
262
539
|
playName: input.playName,
|
|
263
540
|
runId: input.runId,
|
|
541
|
+
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
542
|
+
leaseId: claimed.receipt.leaseId ?? null,
|
|
264
543
|
execute: input.execute,
|
|
265
544
|
receiptStore,
|
|
266
545
|
ownership: 'claimed',
|
|
546
|
+
budgetMeter: input.budgetMeter,
|
|
547
|
+
heartbeatIntervalMs: input.runtimeReceiptHeartbeatIntervalMs,
|
|
267
548
|
});
|
|
268
549
|
}
|