deepline 0.3.128 → 0.3.129
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/sdk/src/client.ts +34 -9
- package/dist/bundling-sources/sdk/src/http.ts +22 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +265 -2
- package/dist/bundling-sources/shared_libs/play-runtime/bounded-batch-function.ts +682 -0
- package/dist/bundling-sources/shared_libs/play-runtime/child-execution-strategy.ts +21 -5
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5390 -1732
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +107 -1
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +229 -7
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +25 -4
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-capacity.ts +71 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +4 -1
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +694 -28
- package/dist/bundling-sources/shared_libs/play-runtime/egress-bandwidth-probe.ts +167 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execute-server-timing.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-receipt-output-reference.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-response-size.ts +27 -0
- package/dist/bundling-sources/shared_libs/play-runtime/gateway-postgres-admission.ts +66 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +425 -71
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +210 -32
- package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +27 -13
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +19 -6
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +61 -2
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +33 -1
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +42 -1
- package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +11 -11
- package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +49 -16
- package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +1653 -98
- package/dist/bundling-sources/shared_libs/play-runtime/memory-ledger.ts +807 -0
- package/dist/bundling-sources/shared_libs/play-runtime/memory-pressure-signal.ts +138 -0
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +85 -3
- package/dist/bundling-sources/shared_libs/play-runtime/persistence-latch.ts +60 -2
- package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +46 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +70 -27
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +56 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/adaptive-provider-capacity-controller.ts +192 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/epoch-aimd-rate-controller.ts +194 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/fixture-capacity-simulator.ts +220 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/gradient2-concurrency-controller.ts +214 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-capacity-policy.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-concurrency-policy.ts +30 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-rate-policy.ts +95 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-throttle-classifier.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +1535 -11
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +49 -2
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +55 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/egress-probe.ts +221 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +339 -29
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +28 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +192 -165
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +90 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +281 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +27 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +19 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +108 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +11 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +30 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-compression.ts +276 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +723 -115
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +98 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-environment.ts +41 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-flow.ts +448 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-map-row-feed.ts +215 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +161 -14
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-value.ts +184 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +607 -121
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-placement-policy.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-writer.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +15 -2
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +71 -14
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +288 -119
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -6
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +28 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/canonical-integration-operation-store.ts +179 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +80 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +31 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-store-contract.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-reference.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-retirement.ts +193 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-storage.ts +420 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +134 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +1657 -95
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +885 -73
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-completion-coalescer.ts +452 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +601 -42
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +371 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +86 -7
- package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +153 -22
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/index.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/receipt-cohort.ts +188 -38
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +28 -13
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +44 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +56 -4
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +16 -10
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +4 -1
- package/dist/cli/index.js +250 -38
- package/dist/cli/index.mjs +250 -38
- package/dist/{compiler-manifest-DfSnbCTW.d.mts → compiler-manifest-BIqRyj5m.d.mts} +1 -1
- package/dist/{compiler-manifest-DfSnbCTW.d.ts → compiler-manifest-BIqRyj5m.d.ts} +1 -1
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +231 -35
- package/dist/index.mjs +231 -35
- package/dist/install-integrity.json +31 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/bundle-play-file.mjs +65 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -41,6 +41,8 @@ import {
|
|
|
41
41
|
runtimeLimitExceededFailure,
|
|
42
42
|
} from '../run-failure';
|
|
43
43
|
import { createPlayContext } from '../context';
|
|
44
|
+
import type { RuntimeApiResponseMeta } from '../app-runtime-api';
|
|
45
|
+
import { onMemoryPressure } from '../memory-pressure-signal';
|
|
44
46
|
import {
|
|
45
47
|
shouldEmitProgressSidecarEvent,
|
|
46
48
|
shouldEmitStdoutEvent,
|
|
@@ -58,6 +60,7 @@ import {
|
|
|
58
60
|
createRunnerProgressPusher,
|
|
59
61
|
type RunnerProgressPusher,
|
|
60
62
|
} from './runtime-progress-push';
|
|
63
|
+
import { collectStartupEgressProbeLines } from './egress-probe';
|
|
61
64
|
import {
|
|
62
65
|
pushRunnerTerminal,
|
|
63
66
|
runnerTerminalResultForTransport,
|
|
@@ -92,6 +95,12 @@ import {
|
|
|
92
95
|
} from '../dataset-lifecycle-log';
|
|
93
96
|
import {
|
|
94
97
|
assertCustomerPlayReturnWithinLimit,
|
|
98
|
+
RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES,
|
|
99
|
+
RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_ITEMS,
|
|
100
|
+
RUNTIME_RECEIPT_COMPLETION_MAX_IN_FLIGHT_BATCHES,
|
|
101
|
+
RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_BYTES,
|
|
102
|
+
RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_ITEMS,
|
|
103
|
+
RUNTIME_RECEIPT_CONTROL_MAX_IN_FLIGHT_BATCHES,
|
|
95
104
|
RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES,
|
|
96
105
|
} from '../output-size-limits';
|
|
97
106
|
import {
|
|
@@ -100,7 +109,10 @@ import {
|
|
|
100
109
|
readRuntimeSheetDatasetRowKeys,
|
|
101
110
|
readRuntimeSheetDatasetRows,
|
|
102
111
|
releaseRuntimeWorkReceipts,
|
|
112
|
+
RuntimeSheetAttemptSupersededError,
|
|
103
113
|
startRuntimeSheetDataset,
|
|
114
|
+
supersededRuntimeMapRows,
|
|
115
|
+
type RuntimeMapRowsWriteResult,
|
|
104
116
|
} from '../runtime-api';
|
|
105
117
|
import { installPgRuntimePoolDriver } from '../runtime-pg-driver-pg';
|
|
106
118
|
import { createRuntimeToolAuthScopeDigestResolver } from '../auth-scope-resolver';
|
|
@@ -141,6 +153,32 @@ function loadSafeOutboundFetch(): Promise<SafeOutboundFetchModule> {
|
|
|
141
153
|
return safeOutboundFetchModule;
|
|
142
154
|
}
|
|
143
155
|
|
|
156
|
+
type ReceiptGatewayResponseObserverTarget = Pick<
|
|
157
|
+
ReturnType<typeof createPlayContext>,
|
|
158
|
+
'observeRuntimeGatewayResponse'
|
|
159
|
+
>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The receipt adapter is created before the Play Context because the Context
|
|
163
|
+
* needs its durable handlers. Keep the response observer late-bound so those
|
|
164
|
+
* completion requests still contribute their gateway/transport split to the
|
|
165
|
+
* Context's terminal runtime summary.
|
|
166
|
+
*/
|
|
167
|
+
export function createReceiptGatewayResponseObserver(): {
|
|
168
|
+
attach: (target: ReceiptGatewayResponseObserverTarget) => void;
|
|
169
|
+
onResponseMeta: (meta: RuntimeApiResponseMeta) => void;
|
|
170
|
+
} {
|
|
171
|
+
let target: ReceiptGatewayResponseObserverTarget | null = null;
|
|
172
|
+
return {
|
|
173
|
+
attach: (nextTarget) => {
|
|
174
|
+
target = nextTarget;
|
|
175
|
+
},
|
|
176
|
+
onResponseMeta: (meta) => {
|
|
177
|
+
target?.observeRuntimeGatewayResponse(meta);
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
144
182
|
async function safeNodeOutboundFetch(
|
|
145
183
|
input: string | URL,
|
|
146
184
|
init?: RequestInit,
|
|
@@ -250,6 +288,36 @@ const RUNTIME_RECEIPT_GATEWAY_REQUEST_TIMEOUT_MS =
|
|
|
250
288
|
RUNTIME_CAPACITY_POLICY.receiptGateway.requestTimeoutMs;
|
|
251
289
|
const RETRY_SAFE_EVENT_WAIT_ARM_TOOLS = new Set(['test_wait_for_event']);
|
|
252
290
|
|
|
291
|
+
/**
|
|
292
|
+
* The Context already bounds receipt completion to sixteen flights. The
|
|
293
|
+
* adapter is the same receipt writer at the gateway boundary, so leaving its
|
|
294
|
+
* default single flight serializes those independent batches again after they
|
|
295
|
+
* have left the Context. Keep the two writers on the same bounded lanes and
|
|
296
|
+
* make the adapter's retained command envelope explicit.
|
|
297
|
+
*/
|
|
298
|
+
export function playRunnerReceiptStoreAdapterOptions() {
|
|
299
|
+
return {
|
|
300
|
+
maxBatchSize: RUNTIME_RECEIPT_GATEWAY_BATCH_SIZE,
|
|
301
|
+
maxFlushMs: RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_WAIT_MS,
|
|
302
|
+
maxInFlightBatches:
|
|
303
|
+
RUNTIME_RECEIPT_COMPLETION_MAX_IN_FLIGHT_BATCHES +
|
|
304
|
+
RUNTIME_RECEIPT_CONTROL_MAX_IN_FLIGHT_BATCHES,
|
|
305
|
+
maxBufferedItems:
|
|
306
|
+
RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_ITEMS +
|
|
307
|
+
RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_ITEMS,
|
|
308
|
+
maxBufferedBytes:
|
|
309
|
+
RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES +
|
|
310
|
+
RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_BYTES,
|
|
311
|
+
maxCompletionInFlightBatches:
|
|
312
|
+
RUNTIME_RECEIPT_COMPLETION_MAX_IN_FLIGHT_BATCHES,
|
|
313
|
+
maxControlInFlightBatches: RUNTIME_RECEIPT_CONTROL_MAX_IN_FLIGHT_BATCHES,
|
|
314
|
+
maxCompletionBufferedItems: RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_ITEMS,
|
|
315
|
+
maxCompletionBufferedBytes: RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES,
|
|
316
|
+
maxControlBufferedItems: RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_ITEMS,
|
|
317
|
+
maxControlBufferedBytes: RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_BYTES,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
253
321
|
// The one receipt backend: one Play Durability Store Adapter bound to the
|
|
254
322
|
// co-located receipt gateway. There is no host-injected or app-runtime path.
|
|
255
323
|
type RuntimeStepReceiptHandlers = RuntimeReceiptStoreHandlers;
|
|
@@ -296,8 +364,9 @@ export function assertRuntimeSheetAttemptPersistedTerminalRows(input: {
|
|
|
296
364
|
fencedKeys?: string[];
|
|
297
365
|
}): void {
|
|
298
366
|
// Kept as a temporary source-compatible seam for callers compiled against
|
|
299
|
-
// the lease model. A partial update
|
|
300
|
-
//
|
|
367
|
+
// the lease model. A partial update is classified from the terminal-write
|
|
368
|
+
// result (`assertRuntimeSheetTerminalWriteNotSuperseded`), never from this
|
|
369
|
+
// count.
|
|
301
370
|
void input;
|
|
302
371
|
}
|
|
303
372
|
|
|
@@ -320,6 +389,99 @@ export function deterministicRuntimeSheetAttemptId(input: {
|
|
|
320
389
|
return `sheet-attempt:${digest}`;
|
|
321
390
|
}
|
|
322
391
|
|
|
392
|
+
/** The sheet's admission version for one table; a value, not a wire marker. */
|
|
393
|
+
type SheetWriteVersionValue = number;
|
|
394
|
+
|
|
395
|
+
export type RunnerRuntimeSheetAttempt = {
|
|
396
|
+
attemptId: string;
|
|
397
|
+
attemptOwnerRunId: string;
|
|
398
|
+
attemptExpiresAt: string;
|
|
399
|
+
attemptSeq: number;
|
|
400
|
+
writeVersion?: SheetWriteVersionValue;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* The sheet attempt record to keep after a map start admitted one source page
|
|
405
|
+
* of a table. A runtime-sheet-backed map admits its source one page at a time
|
|
406
|
+
* while earlier pages' rows are still executing, and every terminal write is
|
|
407
|
+
* fenced by the write version the runner sends. If each page minted its own
|
|
408
|
+
* version, every completion flushed after the next page's admission would
|
|
409
|
+
* carry a version the earlier rows were never admitted under and be rejected
|
|
410
|
+
* as stale (the 2026-09-18 20,000-row incident: 4,350 rows executed, 45
|
|
411
|
+
* persisted). So the first admission's write version is requested again for
|
|
412
|
+
* every later page (see `runtimeSheetStartWriteVersion`) and must come back
|
|
413
|
+
* unchanged; anything else means the sheet no longer belongs to this runner.
|
|
414
|
+
*/
|
|
415
|
+
export function runtimeSheetAttemptForMapStart(input: {
|
|
416
|
+
tableNamespace: string;
|
|
417
|
+
runId: string;
|
|
418
|
+
existing: RunnerRuntimeSheetAttempt | undefined;
|
|
419
|
+
mapStartResult: {
|
|
420
|
+
attemptId: string;
|
|
421
|
+
attemptOwnerRunId: string;
|
|
422
|
+
attemptExpiresAt: string;
|
|
423
|
+
attemptSeq?: number | null;
|
|
424
|
+
writeVersion?: SheetWriteVersionValue | null;
|
|
425
|
+
};
|
|
426
|
+
runnerRunAttempt: number;
|
|
427
|
+
}): RunnerRuntimeSheetAttempt {
|
|
428
|
+
const requested = input.existing?.writeVersion;
|
|
429
|
+
const returned = input.mapStartResult.writeVersion ?? undefined;
|
|
430
|
+
if (requested != null && returned !== requested) {
|
|
431
|
+
throw new Error(
|
|
432
|
+
`Runtime sheet start for table "${input.tableNamespace}" (run ${input.runId}) ` +
|
|
433
|
+
`was asked to keep write version ${requested} for a later source page but ` +
|
|
434
|
+
`returned ${returned ?? 'none'}; the runner cannot persist rows under two ` +
|
|
435
|
+
`write versions, so this map cannot continue.`,
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
attemptId: input.mapStartResult.attemptId,
|
|
440
|
+
attemptOwnerRunId: input.mapStartResult.attemptOwnerRunId,
|
|
441
|
+
attemptExpiresAt: input.mapStartResult.attemptExpiresAt,
|
|
442
|
+
attemptSeq: input.mapStartResult.attemptSeq ?? input.runnerRunAttempt,
|
|
443
|
+
...(returned != null ? { writeVersion: returned } : {}),
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/** The write version a later source page of the same table must be admitted under. */
|
|
448
|
+
export function runtimeSheetStartWriteVersion(
|
|
449
|
+
existing: RunnerRuntimeSheetAttempt | undefined,
|
|
450
|
+
): { writeVersion: SheetWriteVersionValue } | Record<string, never> {
|
|
451
|
+
return existing?.writeVersion != null
|
|
452
|
+
? { writeVersion: existing.writeVersion }
|
|
453
|
+
: {};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Terminal writes rejected as stale for any reason other than a verified
|
|
458
|
+
* same-run settlement or an unversioned already-terminal row mean the sheet
|
|
459
|
+
* rows belong to a different write version than this attempt's: a newer
|
|
460
|
+
* admission (rerun, resumed attempt, concurrent run) owns them. Throw so the
|
|
461
|
+
* map fails and the persistence latch trips, instead of logging a perf line
|
|
462
|
+
* and spending provider calls on rows that can never be persisted.
|
|
463
|
+
*/
|
|
464
|
+
export function assertRuntimeSheetTerminalWriteNotSuperseded(input: {
|
|
465
|
+
tableNamespace: string;
|
|
466
|
+
runId: string;
|
|
467
|
+
writeVersion: SheetWriteVersionValue | null;
|
|
468
|
+
submittedRows: number;
|
|
469
|
+
result: Pick<RuntimeMapRowsWriteResult, 'staleKeys' | 'staleRows'>;
|
|
470
|
+
recoveredStaleKeys: Iterable<string>;
|
|
471
|
+
}): void {
|
|
472
|
+
const supersededRows = supersededRuntimeMapRows(input.result, {
|
|
473
|
+
exceptKeys: input.recoveredStaleKeys,
|
|
474
|
+
});
|
|
475
|
+
if (supersededRows.length === 0) return;
|
|
476
|
+
throw new RuntimeSheetAttemptSupersededError({
|
|
477
|
+
tableNamespace: input.tableNamespace,
|
|
478
|
+
runId: input.runId,
|
|
479
|
+
writeVersionValue: input.writeVersion,
|
|
480
|
+
submittedRows: input.submittedRows,
|
|
481
|
+
supersededRows,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
323
485
|
async function vercelBypassHeaders(
|
|
324
486
|
config: PlayRunnerExecutionConfig,
|
|
325
487
|
): Promise<Record<string, string>> {
|
|
@@ -882,6 +1044,13 @@ async function createStreamingCsvDataset(input: {
|
|
|
882
1044
|
export interface ExecutePlayRunnerOptions {
|
|
883
1045
|
emitEvent?: (event: PlayRunnerEvent) => void;
|
|
884
1046
|
signal?: AbortSignal;
|
|
1047
|
+
/** Stops future row admission while already admitted rows can persist. */
|
|
1048
|
+
rowAdmissionSignal?: AbortSignal;
|
|
1049
|
+
/** Runner-local terminal state made available to the deadline drain. */
|
|
1050
|
+
onRuntimeReady?: (runtime: {
|
|
1051
|
+
log: (line: string) => void;
|
|
1052
|
+
timeoutResult: (runtimeLimitSeconds: number) => PlayRunnerResult;
|
|
1053
|
+
}) => void;
|
|
885
1054
|
}
|
|
886
1055
|
|
|
887
1056
|
function emitRunnerEvent(
|
|
@@ -1646,6 +1815,18 @@ async function releaseRuntimeLeasesOnAppRunnerSettlement(input: {
|
|
|
1646
1815
|
}
|
|
1647
1816
|
}
|
|
1648
1817
|
|
|
1818
|
+
function terminalLogsWithPhaseSummary(ctx: {
|
|
1819
|
+
getLogs(): string[];
|
|
1820
|
+
getRuntimeResourceSnapshot(): unknown;
|
|
1821
|
+
getLogDiagnostics(): unknown;
|
|
1822
|
+
}): string[] {
|
|
1823
|
+
return [
|
|
1824
|
+
...ctx.getLogs(),
|
|
1825
|
+
`[runtime.logs] ${JSON.stringify(ctx.getLogDiagnostics())}`,
|
|
1826
|
+
`[runtime.phase_summary] ${JSON.stringify(ctx.getRuntimeResourceSnapshot())}`,
|
|
1827
|
+
];
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1649
1830
|
async function run(
|
|
1650
1831
|
config: PlayRunnerExecutionConfig,
|
|
1651
1832
|
options?: ExecutePlayRunnerOptions,
|
|
@@ -1690,6 +1871,7 @@ async function run(
|
|
|
1690
1871
|
vercelProtectionBypassToken: config.context.vercelProtectionBypassToken,
|
|
1691
1872
|
runtimeTestFaultHeader: config.context.runtimeTestFaultHeader ?? null,
|
|
1692
1873
|
};
|
|
1874
|
+
const receiptGatewayResponseObserver = createReceiptGatewayResponseObserver();
|
|
1693
1875
|
const receiptGatewayBaseUrl = config.context.receiptGatewayBaseUrl?.trim();
|
|
1694
1876
|
// Receipt state has exactly two backends: handlers provided by the host
|
|
1695
1877
|
// process (inline executor) or the co-located receipt gateway named by the
|
|
@@ -1701,6 +1883,7 @@ async function run(
|
|
|
1701
1883
|
baseUrl: receiptGatewayBaseUrl,
|
|
1702
1884
|
boundary: 'receipt_gateway' as const,
|
|
1703
1885
|
requestTimeoutMs: RUNTIME_RECEIPT_GATEWAY_REQUEST_TIMEOUT_MS,
|
|
1886
|
+
onResponseMeta: receiptGatewayResponseObserver.onResponseMeta,
|
|
1704
1887
|
}
|
|
1705
1888
|
: null;
|
|
1706
1889
|
const canUseRuntimeStepReceipts = playRunnerRuntimeReceiptsEnabled(
|
|
@@ -1718,8 +1901,7 @@ async function run(
|
|
|
1718
1901
|
config.context.runId ??
|
|
1719
1902
|
config.context.workflowId ??
|
|
1720
1903
|
'anonymous-run',
|
|
1721
|
-
|
|
1722
|
-
maxFlushMs: RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_WAIT_MS,
|
|
1904
|
+
...playRunnerReceiptStoreAdapterOptions(),
|
|
1723
1905
|
onRetryTelemetry: (line) => emitSystemLog(line, options),
|
|
1724
1906
|
})
|
|
1725
1907
|
: null;
|
|
@@ -1836,6 +2018,7 @@ async function run(
|
|
|
1836
2018
|
});
|
|
1837
2019
|
const ctx = createPlayContext({
|
|
1838
2020
|
artifactRuntimeContract,
|
|
2021
|
+
rowAdmissionSignal: options?.rowAdmissionSignal,
|
|
1839
2022
|
executorToken: config.context.executorToken,
|
|
1840
2023
|
baseUrl: config.context.baseUrl,
|
|
1841
2024
|
// Runtime control stays on baseUrl (the receipt gateway); provider calls
|
|
@@ -2164,6 +2347,15 @@ async function run(
|
|
|
2164
2347
|
`Missing sheet contract for play "${playName}" and table namespace "${tableNamespace}".`,
|
|
2165
2348
|
);
|
|
2166
2349
|
}
|
|
2350
|
+
const attemptKey = runtimeSheetAttemptKey({
|
|
2351
|
+
playName,
|
|
2352
|
+
runId,
|
|
2353
|
+
tableNamespace,
|
|
2354
|
+
});
|
|
2355
|
+
// A later source page of a table this runner already admitted keeps the
|
|
2356
|
+
// first page's write version: rows of earlier pages are still executing
|
|
2357
|
+
// and their terminal writes are fenced by it.
|
|
2358
|
+
const existingAttempt = runtimeSheetAttempts.get(attemptKey);
|
|
2167
2359
|
const mapStartResult = await retryRuntimeSheetPersistence({
|
|
2168
2360
|
description: `runtime map start table=${tableNamespace}`,
|
|
2169
2361
|
log: (line) => emitWorkerLog(line, options),
|
|
@@ -2205,6 +2397,7 @@ async function run(
|
|
|
2205
2397
|
}),
|
|
2206
2398
|
attemptOwnerRunId: runId,
|
|
2207
2399
|
attemptSeq: runnerRunAttempt,
|
|
2400
|
+
...runtimeSheetStartWriteVersion(existingAttempt),
|
|
2208
2401
|
...(playRunnerContextRuntimeSheetForce(config.context)
|
|
2209
2402
|
? { force: true }
|
|
2210
2403
|
: {}),
|
|
@@ -2274,14 +2467,20 @@ async function run(
|
|
|
2274
2467
|
mapStartResult.attemptExpiresAt
|
|
2275
2468
|
) {
|
|
2276
2469
|
runtimeSheetAttempts.set(
|
|
2277
|
-
|
|
2278
|
-
{
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2470
|
+
attemptKey,
|
|
2471
|
+
runtimeSheetAttemptForMapStart({
|
|
2472
|
+
tableNamespace,
|
|
2473
|
+
runId,
|
|
2474
|
+
existing: existingAttempt,
|
|
2475
|
+
mapStartResult: {
|
|
2476
|
+
attemptId: mapStartResult.attemptId,
|
|
2477
|
+
attemptOwnerRunId: mapStartResult.attemptOwnerRunId,
|
|
2478
|
+
attemptExpiresAt: mapStartResult.attemptExpiresAt,
|
|
2479
|
+
attemptSeq: mapStartResult.attemptSeq,
|
|
2480
|
+
writeVersion: mapStartResult.writeVersion,
|
|
2481
|
+
},
|
|
2482
|
+
runnerRunAttempt,
|
|
2483
|
+
}),
|
|
2285
2484
|
);
|
|
2286
2485
|
}
|
|
2287
2486
|
emitWorkerLog(
|
|
@@ -2487,6 +2686,22 @@ async function run(
|
|
|
2487
2686
|
`[info] dropped ${staleKeys.size} stale runtime sheet write(s) for table=${input.tableNamespace}; ${recoveredStaleKeys.length} same-run terminal write(s) were accepted idempotently`,
|
|
2488
2687
|
options,
|
|
2489
2688
|
);
|
|
2689
|
+
try {
|
|
2690
|
+
assertRuntimeSheetTerminalWriteNotSuperseded({
|
|
2691
|
+
tableNamespace: input.tableNamespace,
|
|
2692
|
+
runId,
|
|
2693
|
+
writeVersion: attempt?.writeVersion ?? null,
|
|
2694
|
+
submittedRows: input.rows.length,
|
|
2695
|
+
result,
|
|
2696
|
+
recoveredStaleKeys,
|
|
2697
|
+
});
|
|
2698
|
+
} catch (error) {
|
|
2699
|
+
emitWorkerLog(
|
|
2700
|
+
`[error] ${error instanceof Error ? error.message : String(error)}`,
|
|
2701
|
+
options,
|
|
2702
|
+
);
|
|
2703
|
+
throw error;
|
|
2704
|
+
}
|
|
2490
2705
|
}
|
|
2491
2706
|
const failedRows = input.rows.filter(
|
|
2492
2707
|
(row) =>
|
|
@@ -2511,7 +2726,9 @@ async function run(
|
|
|
2511
2726
|
failedRows: 0,
|
|
2512
2727
|
};
|
|
2513
2728
|
// Only exact-version accepted rows belong to this run's lifecycle
|
|
2514
|
-
// settlement.
|
|
2729
|
+
// settlement. The stale keys that reach this point were all recovered as
|
|
2730
|
+
// same-run terminal settlements, counted when they first committed, so
|
|
2731
|
+
// they stay excluded here.
|
|
2515
2732
|
settlement.persistedRows += committedTerminalRows;
|
|
2516
2733
|
settlement.succeededRows += succeededRows;
|
|
2517
2734
|
settlement.failedRows += failedRows;
|
|
@@ -2527,6 +2744,7 @@ async function run(
|
|
|
2527
2744
|
};
|
|
2528
2745
|
},
|
|
2529
2746
|
});
|
|
2747
|
+
receiptGatewayResponseObserver.attach(ctx);
|
|
2530
2748
|
|
|
2531
2749
|
const csvSourceCache = new Map<string, ReplayableCsvSource>();
|
|
2532
2750
|
const csvDatasetCache = new Map<
|
|
@@ -2666,6 +2884,53 @@ async function run(
|
|
|
2666
2884
|
`[perf] context setup elapsed_ms=${Date.now() - contextSetupStartedAt}`,
|
|
2667
2885
|
options,
|
|
2668
2886
|
);
|
|
2887
|
+
for (const line of await collectStartupEgressProbeLines({
|
|
2888
|
+
// A local/direct executor can use baseUrl without a receipt gateway.
|
|
2889
|
+
// Probe only the explicit data-plane hop.
|
|
2890
|
+
receiptGatewayBaseUrl: config.context.receiptGatewayBaseUrl ?? null,
|
|
2891
|
+
executionRelayBaseUrl: config.context.executionGatewayBaseUrl ?? null,
|
|
2892
|
+
executorToken: config.context.executorToken ?? null,
|
|
2893
|
+
})) {
|
|
2894
|
+
ctx.logRunnerDiagnostic(line);
|
|
2895
|
+
}
|
|
2896
|
+
const logRecovery = () => {
|
|
2897
|
+
const runId = config.context.runId ?? config.context.workflowId;
|
|
2898
|
+
for (const saved of [...runtimeMapDatasetSettlements.values()].slice(
|
|
2899
|
+
0,
|
|
2900
|
+
16,
|
|
2901
|
+
)) {
|
|
2902
|
+
ctx.logRunnerDiagnostic(
|
|
2903
|
+
formatDatasetAvailabilityLog({
|
|
2904
|
+
runId: saved.runId,
|
|
2905
|
+
dataset: saved,
|
|
2906
|
+
persistedRows: saved.persistedRows,
|
|
2907
|
+
}),
|
|
2908
|
+
);
|
|
2909
|
+
}
|
|
2910
|
+
if (runId) {
|
|
2911
|
+
ctx.logRunnerDiagnostic(
|
|
2912
|
+
`Run stopped before returning. Inspect saved datasets: deepline runs get ${runId} --json. Export saved data before rerunning unfinished work.`,
|
|
2913
|
+
);
|
|
2914
|
+
}
|
|
2915
|
+
};
|
|
2916
|
+
options?.onRuntimeReady?.({
|
|
2917
|
+
log: (line) => ctx.logRunnerDiagnostic(line),
|
|
2918
|
+
timeoutResult: (runtimeLimitSeconds) => {
|
|
2919
|
+
logRecovery();
|
|
2920
|
+
return {
|
|
2921
|
+
status: 'failed',
|
|
2922
|
+
error: runtimeLimitExceededCause(runtimeLimitSeconds),
|
|
2923
|
+
errors: [runtimeLimitExceededFailure(runtimeLimitSeconds)],
|
|
2924
|
+
logs: terminalLogsWithPhaseSummary(ctx),
|
|
2925
|
+
stats: ctx.getStats(),
|
|
2926
|
+
steps: ctx.getSteps(),
|
|
2927
|
+
tableNamespace: null,
|
|
2928
|
+
totalRows,
|
|
2929
|
+
inserted,
|
|
2930
|
+
skipped,
|
|
2931
|
+
};
|
|
2932
|
+
},
|
|
2933
|
+
});
|
|
2669
2934
|
|
|
2670
2935
|
const playInput: PlayRunInputPayload =
|
|
2671
2936
|
runtimeInputAlias && !Array.isArray(runtimeInput)
|
|
@@ -2683,6 +2948,7 @@ async function run(
|
|
|
2683
2948
|
runtimeInput: playInput,
|
|
2684
2949
|
options,
|
|
2685
2950
|
});
|
|
2951
|
+
ctx.assertRowAdmissionOpen();
|
|
2686
2952
|
// A durable ctx boundary deliberately unwinds execution. Customer code can
|
|
2687
2953
|
// catch that error, so the context keeps a sticky handoff that remains
|
|
2688
2954
|
// authoritative until this invocation returns it to the scheduler.
|
|
@@ -2850,7 +3116,7 @@ async function run(
|
|
|
2850
3116
|
reason: 'completed',
|
|
2851
3117
|
});
|
|
2852
3118
|
|
|
2853
|
-
const logs = ctx
|
|
3119
|
+
const logs = terminalLogsWithPhaseSummary(ctx);
|
|
2854
3120
|
const runtimeDiagnostics = Array.from(
|
|
2855
3121
|
new Map(
|
|
2856
3122
|
logs
|
|
@@ -2888,9 +3154,13 @@ async function run(
|
|
|
2888
3154
|
// sticky handoff is still authoritative until this runner returns it to
|
|
2889
3155
|
// the scheduler. Resolve that precedence before classifying the terminal
|
|
2890
3156
|
// result so a replacement error cannot turn a suspended run into failed.
|
|
2891
|
-
let error: unknown =
|
|
3157
|
+
let error: unknown = options?.rowAdmissionSignal?.aborted
|
|
3158
|
+
? options.rowAdmissionSignal.reason
|
|
3159
|
+
: caughtError;
|
|
2892
3160
|
try {
|
|
2893
|
-
|
|
3161
|
+
if (!options?.rowAdmissionSignal?.aborted) {
|
|
3162
|
+
ctx.assertNoPendingDurableSuspension();
|
|
3163
|
+
}
|
|
2894
3164
|
} catch (suspension) {
|
|
2895
3165
|
error = suspension;
|
|
2896
3166
|
}
|
|
@@ -2921,7 +3191,7 @@ async function run(
|
|
|
2921
3191
|
return {
|
|
2922
3192
|
status: 'suspended',
|
|
2923
3193
|
suspension: error.suspension,
|
|
2924
|
-
logs: ctx
|
|
3194
|
+
logs: terminalLogsWithPhaseSummary(ctx),
|
|
2925
3195
|
stats: ctx.getStats(),
|
|
2926
3196
|
steps: ctx.getSteps(),
|
|
2927
3197
|
checkpoint: ctx.getCheckpoint(),
|
|
@@ -2949,11 +3219,12 @@ async function run(
|
|
|
2949
3219
|
|
|
2950
3220
|
ensureSourceMapSupport();
|
|
2951
3221
|
const failure = normalizePlayRunFailure(error);
|
|
3222
|
+
logRecovery();
|
|
2952
3223
|
return {
|
|
2953
3224
|
status: 'failed',
|
|
2954
3225
|
error: formatRunnerFailure(error),
|
|
2955
3226
|
errors: [failure],
|
|
2956
|
-
logs: ctx
|
|
3227
|
+
logs: terminalLogsWithPhaseSummary(ctx),
|
|
2957
3228
|
stats: ctx.getStats(),
|
|
2958
3229
|
steps: ctx.getSteps(),
|
|
2959
3230
|
checkpoint: ctx.getCheckpoint(),
|
|
@@ -3220,6 +3491,10 @@ async function main() {
|
|
|
3220
3491
|
// is reachable instead of relying only on the detached ceiling salvage.
|
|
3221
3492
|
runnerTerminalPushContext = terminalPushContextFromConfig(parsedConfig);
|
|
3222
3493
|
const executionController = new AbortController();
|
|
3494
|
+
const rowAdmissionController = new AbortController();
|
|
3495
|
+
let runtimeState:
|
|
3496
|
+
| Parameters<NonNullable<ExecutePlayRunnerOptions['onRuntimeReady']>>[0]
|
|
3497
|
+
| undefined;
|
|
3223
3498
|
const heartbeat = startRunnerPushHeartbeatFromConfig(parsedConfig, () => {
|
|
3224
3499
|
executionController.abort(
|
|
3225
3500
|
new Error('Play runner stopped by the runtime heartbeat channel.'),
|
|
@@ -3238,14 +3513,31 @@ async function main() {
|
|
|
3238
3513
|
runnerProgressPusher = startRunnerProgressPusherFromConfig(config);
|
|
3239
3514
|
const runtimeDeadline = startConfiguredPlayRunnerRuntimeDeadline({
|
|
3240
3515
|
config,
|
|
3516
|
+
onDrain: (runtimeLimitSeconds) => {
|
|
3517
|
+
runtimeState?.log(
|
|
3518
|
+
`[runtime.log_transport] ${JSON.stringify(runnerProgressPusher?.snapshot() ?? null)}`,
|
|
3519
|
+
);
|
|
3520
|
+
runtimeState?.log(
|
|
3521
|
+
`Runtime limit approaching (${runtimeLimitSeconds}s configured); stopping new rows and saving started work for up to 4 seconds.`,
|
|
3522
|
+
);
|
|
3523
|
+
rowAdmissionController.abort(
|
|
3524
|
+
new Error(runtimeLimitExceededCause(runtimeLimitSeconds)),
|
|
3525
|
+
);
|
|
3526
|
+
},
|
|
3241
3527
|
onDeadline: async (runtimeLimitSeconds) => {
|
|
3528
|
+
runtimeState?.log(
|
|
3529
|
+
`[runtime.log_transport] ${JSON.stringify(runnerProgressPusher?.snapshot() ?? null)}`,
|
|
3530
|
+
);
|
|
3242
3531
|
executionController.abort(
|
|
3243
3532
|
new Error(
|
|
3244
3533
|
`Play runner reached its ${runtimeLimitSeconds}-second runtime limit.`,
|
|
3245
3534
|
),
|
|
3246
3535
|
);
|
|
3247
3536
|
heartbeat?.stop();
|
|
3248
|
-
await terminateRunnerForRuntimeDeadline(
|
|
3537
|
+
await terminateRunnerForRuntimeDeadline(
|
|
3538
|
+
runtimeLimitSeconds,
|
|
3539
|
+
runtimeState?.timeoutResult(runtimeLimitSeconds),
|
|
3540
|
+
);
|
|
3249
3541
|
},
|
|
3250
3542
|
onError: (error) => {
|
|
3251
3543
|
console.error('[play-runner.runtime-deadline] terminal_failed', {
|
|
@@ -3263,6 +3555,10 @@ async function main() {
|
|
|
3263
3555
|
try {
|
|
3264
3556
|
result = await executePlayRunner(config, {
|
|
3265
3557
|
signal: executionController.signal,
|
|
3558
|
+
rowAdmissionSignal: rowAdmissionController.signal,
|
|
3559
|
+
onRuntimeReady: (runtime) => {
|
|
3560
|
+
runtimeState = runtime;
|
|
3561
|
+
},
|
|
3266
3562
|
});
|
|
3267
3563
|
} catch (error) {
|
|
3268
3564
|
executionFailed = true;
|
|
@@ -3586,7 +3882,7 @@ function terminateRunnerForHeartbeatStop(
|
|
|
3586
3882
|
? stop.terminal === 'cancelled'
|
|
3587
3883
|
? 'Play run was cancelled; sandbox runner self-terminated on the receipt gateway heartbeat cancellation channel.'
|
|
3588
3884
|
: 'Play run already failed in the scheduler; sandbox runner self-terminated on the receipt gateway heartbeat cancellation channel.'
|
|
3589
|
-
: `Sandbox runner could not reach the receipt gateway for ${stop.reason === 'unreachable' ? stop.sinceMs : 0}ms (past the lease window); self-terminating — the parked attempt's ceiling timeout salvages this result from the captured output. ${formatRunnerGatewayTransportSummary(stop.reason === 'unreachable' ? stop.gatewayTransport : undefined)}`;
|
|
3885
|
+
: `RUNTIME_CONTROL_PLANE_UNAVAILABLE: Sandbox runner could not reach the receipt gateway for ${stop.reason === 'unreachable' ? stop.sinceMs : 0}ms (past the lease window); self-terminating — the parked attempt's ceiling timeout salvages this result from the captured output. ${formatRunnerGatewayTransportSummary(stop.reason === 'unreachable' ? stop.gatewayTransport : undefined)}`;
|
|
3590
3886
|
const failedResult = runnerTerminalResultForTransport({
|
|
3591
3887
|
status: 'failed',
|
|
3592
3888
|
error,
|
|
@@ -3606,17 +3902,20 @@ function terminateRunnerForHeartbeatStop(
|
|
|
3606
3902
|
|
|
3607
3903
|
async function terminateRunnerForRuntimeDeadline(
|
|
3608
3904
|
runtimeLimitSeconds: number,
|
|
3905
|
+
snapshot?: PlayRunnerResult,
|
|
3609
3906
|
): Promise<never> {
|
|
3610
3907
|
const error = runtimeLimitExceededCause(runtimeLimitSeconds);
|
|
3611
|
-
const failedResult = runnerTerminalResultForTransport(
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3908
|
+
const failedResult = runnerTerminalResultForTransport(
|
|
3909
|
+
snapshot ?? {
|
|
3910
|
+
status: 'failed',
|
|
3911
|
+
error,
|
|
3912
|
+
errors: [runtimeLimitExceededFailure(runtimeLimitSeconds, error)],
|
|
3913
|
+
logs: [],
|
|
3914
|
+
stats: {},
|
|
3915
|
+
steps: [],
|
|
3916
|
+
tableNamespace: null,
|
|
3917
|
+
},
|
|
3918
|
+
);
|
|
3620
3919
|
// Stop customer code immediately. The structured stdout event is captured by
|
|
3621
3920
|
// the Daytona shell wrapper, whose crash epilogue owns the bounded gateway
|
|
3622
3921
|
// push during the outer grace period. Awaiting network/progress work here
|
|
@@ -3630,6 +3929,17 @@ async function terminateRunnerForRuntimeDeadline(
|
|
|
3630
3929
|
);
|
|
3631
3930
|
}
|
|
3632
3931
|
|
|
3932
|
+
// The external watchdog samples the sandbox cgroup and raises SIGUSR2 at its
|
|
3933
|
+
// pressure threshold. Register at module load: Node would otherwise terminate
|
|
3934
|
+
// before map admission can react and release retained result bodies.
|
|
3935
|
+
onMemoryPressure((sample) => {
|
|
3936
|
+
emitWorkerLog(
|
|
3937
|
+
`[memory] pressure signal received usage=${sample.usageBytes ?? 'unknown'} limit=${sample.limitBytes ?? 'unknown'}`,
|
|
3938
|
+
);
|
|
3939
|
+
// The shared sandbox argv enables explicit collection for this recovery path.
|
|
3940
|
+
(globalThis as { gc?: () => void }).gc?.();
|
|
3941
|
+
});
|
|
3942
|
+
|
|
3633
3943
|
export function runPlayRunnerMain(): void {
|
|
3634
3944
|
main().catch(async (error) => {
|
|
3635
3945
|
ensureSourceMapSupport();
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
export interface PlayRunnerRuntimeDeadlineOptions {
|
|
8
8
|
limitMs: number;
|
|
9
9
|
onDeadline: () => void | Promise<void>;
|
|
10
|
+
/** Stop new work while leaving a bounded window for durable writes. */
|
|
11
|
+
onDrain?: () => void;
|
|
12
|
+
drainLeadMs?: number;
|
|
10
13
|
onError?: (error: unknown) => void;
|
|
11
14
|
setTimeoutImpl?: (callback: () => void, delayMs: number) => unknown;
|
|
12
15
|
clearTimeoutImpl?: (handle: unknown) => void;
|
|
@@ -22,6 +25,7 @@ export interface PlayRunnerRuntimeDeadlineHandle {
|
|
|
22
25
|
export function startConfiguredPlayRunnerRuntimeDeadline(input: {
|
|
23
26
|
config: Pick<PlayRunnerExecutionConfig, 'context'>;
|
|
24
27
|
onDeadline: (runtimeLimitSeconds: number) => void | Promise<void>;
|
|
28
|
+
onDrain?: (runtimeLimitSeconds: number) => void;
|
|
25
29
|
onError?: (error: unknown) => void;
|
|
26
30
|
setTimeoutImpl?: (callback: () => void, delayMs: number) => unknown;
|
|
27
31
|
clearTimeoutImpl?: (handle: unknown) => void;
|
|
@@ -33,7 +37,13 @@ export function startConfiguredPlayRunnerRuntimeDeadline(input: {
|
|
|
33
37
|
);
|
|
34
38
|
const runtimeLimitSeconds = runtimeLimits.timeoutSeconds;
|
|
35
39
|
return startPlayRunnerRuntimeDeadline({
|
|
36
|
-
|
|
40
|
+
// Reserve one second for captured terminal stdout before the independent
|
|
41
|
+
// watchdog's hard deadline. Legacy callers keep their exact deadline.
|
|
42
|
+
limitMs: runtimeLimitSeconds * 1_000 - (input.onDrain ? 1_000 : 0),
|
|
43
|
+
onDrain: input.onDrain
|
|
44
|
+
? () => input.onDrain!(runtimeLimitSeconds)
|
|
45
|
+
: undefined,
|
|
46
|
+
drainLeadMs: 4_000,
|
|
37
47
|
onDeadline: () => input.onDeadline(runtimeLimitSeconds),
|
|
38
48
|
onError: input.onError,
|
|
39
49
|
setTimeoutImpl: input.setTimeoutImpl,
|
|
@@ -62,6 +72,22 @@ export function startPlayRunnerRuntimeDeadline(
|
|
|
62
72
|
const deadlineSettled = new Promise<void>((resolve) => {
|
|
63
73
|
settleDeadline = resolve;
|
|
64
74
|
});
|
|
75
|
+
const drainHandle = options.onDrain
|
|
76
|
+
? setTimeoutImpl(
|
|
77
|
+
() => {
|
|
78
|
+
if (fired || completionClaimed) return;
|
|
79
|
+
try {
|
|
80
|
+
options.onDrain?.();
|
|
81
|
+
} catch (error) {
|
|
82
|
+
options.onError?.(error);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
Math.max(
|
|
86
|
+
1,
|
|
87
|
+
limitMs - Math.min(options.drainLeadMs ?? 4_000, limitMs / 2),
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
: null;
|
|
65
91
|
const handle = setTimeoutImpl(() => {
|
|
66
92
|
if (fired || completionClaimed) return;
|
|
67
93
|
fired = true;
|
|
@@ -77,6 +103,7 @@ export function startPlayRunnerRuntimeDeadline(
|
|
|
77
103
|
if (fired || completionClaimed) return false;
|
|
78
104
|
completionClaimed = true;
|
|
79
105
|
clearTimeoutImpl(handle);
|
|
106
|
+
if (drainHandle !== null) clearTimeoutImpl(drainHandle);
|
|
80
107
|
return true;
|
|
81
108
|
},
|
|
82
109
|
deadlineSettled,
|