deepline 0.3.236 → 0.3.237
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 +6 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +264 -120
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +219 -46
- package/dist/cli/index.js +148 -53
- package/dist/cli/index.mjs +148 -53
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -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
|
@@ -2047,6 +2047,8 @@ export type BillingRecentUsageEntry = {
|
|
|
2047
2047
|
| 'pending'
|
|
2048
2048
|
| 'free'
|
|
2049
2049
|
| 'failed';
|
|
2050
|
+
/** Final billing truth may be unknown even after execution completes. */
|
|
2051
|
+
charge_finality?: 'final' | 'pending' | 'unknown';
|
|
2050
2052
|
rough_usd_cost: number | null;
|
|
2051
2053
|
created_at: string;
|
|
2052
2054
|
};
|
|
@@ -2076,6 +2078,10 @@ export type BillingUsageResult = {
|
|
|
2076
2078
|
run_id: string | null;
|
|
2077
2079
|
request_id?: string;
|
|
2078
2080
|
total: number;
|
|
2081
|
+
/** Only true when total covers the complete matching history. */
|
|
2082
|
+
total_is_exact?: boolean;
|
|
2083
|
+
/** Proven matching entries, without assuming unscanned rows match. */
|
|
2084
|
+
total_lower_bound?: number;
|
|
2079
2085
|
offset: number;
|
|
2080
2086
|
limit: number;
|
|
2081
2087
|
next_offset: number;
|
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.237',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
206
206
|
packageCapabilities: {
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts
CHANGED
|
@@ -136,6 +136,8 @@ import {
|
|
|
136
136
|
completePostgresSchedulerComputeBillingSessionBootstrap,
|
|
137
137
|
deferPostgresSchedulerAttemptForCapacity,
|
|
138
138
|
enqueuePostgresSchedulerSandboxCleanupForAttempt,
|
|
139
|
+
enqueuePostgresSchedulerSandboxCleanupForRun,
|
|
140
|
+
hasPostgresSchedulerRecoverableDetachedRunner,
|
|
139
141
|
executePostgresSchedulerClaim,
|
|
140
142
|
ensurePostgresSchedulerTerminalComputeBillingJob,
|
|
141
143
|
failPostgresSchedulerAttempt,
|
|
@@ -760,7 +762,7 @@ export type AbsurdRuntimeWorkerDeps = {
|
|
|
760
762
|
deleteRuntimeSandboxRef?: (
|
|
761
763
|
ref: RuntimeSandboxRef,
|
|
762
764
|
) => Promise<RuntimeSandboxDeleteOutcome>;
|
|
763
|
-
deleteRuntimeSandboxWithOutcome
|
|
765
|
+
deleteRuntimeSandboxWithOutcome?: (
|
|
764
766
|
sandboxId: string,
|
|
765
767
|
expectedOrganizationId: string | null,
|
|
766
768
|
) => Promise<DaytonaSandboxDeleteOutcome>;
|
|
@@ -885,40 +887,48 @@ function buildDefaultDeps(absurd: Absurd): AbsurdRuntimeWorkerDeps {
|
|
|
885
887
|
* of silently leaving the CLI stale.
|
|
886
888
|
*/
|
|
887
889
|
async function projectLifecycleOutbox(input: {
|
|
890
|
+
ctx: TaskContext;
|
|
888
891
|
deps: AbsurdRuntimeWorkerDeps;
|
|
889
892
|
launch: PlaySchedulerSubmitInput;
|
|
890
893
|
lifecycle: PostgresSchedulerLifecycleOutboxEvent;
|
|
891
894
|
options?: Pick<PostgresSchedulerOptions, 'schema'>;
|
|
892
895
|
transition: 'waiting' | 'resumed';
|
|
893
896
|
}): Promise<void> {
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
897
|
+
await runWithAbsurdExecutionLeaseHeartbeat({
|
|
898
|
+
ctx: input.ctx,
|
|
899
|
+
runId: input.lifecycle.runId,
|
|
900
|
+
operation: async (signal) => {
|
|
901
|
+
try {
|
|
902
|
+
const result = await input.deps.projectOutbox(
|
|
903
|
+
input.launch,
|
|
904
|
+
input.lifecycle.runId,
|
|
905
|
+
input.options,
|
|
906
|
+
input.lifecycle.eventId,
|
|
907
|
+
signal,
|
|
908
|
+
);
|
|
909
|
+
if (result && !result.targetDelivered) {
|
|
910
|
+
console.error('[run-ledger-outbox.lifecycle_projection_pending]', {
|
|
911
|
+
runId: input.lifecycle.runId,
|
|
912
|
+
eventId: input.lifecycle.eventId,
|
|
913
|
+
transition: input.transition,
|
|
914
|
+
projected: result.projected,
|
|
915
|
+
failed: result.failed,
|
|
916
|
+
failedEventIds: result.failedEventIds,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
} catch (error) {
|
|
920
|
+
// Scheduler truth and the outbox row were committed atomically before this
|
|
921
|
+
// handoff. Keep the durable continuation live so recovery can heal a
|
|
922
|
+
// transient app-runtime failure, but do not hide the visibility gap.
|
|
923
|
+
console.error('[run-ledger-outbox.lifecycle_projection_failed]', {
|
|
924
|
+
runId: input.lifecycle.runId,
|
|
925
|
+
eventId: input.lifecycle.eventId,
|
|
926
|
+
transition: input.transition,
|
|
927
|
+
error: error instanceof Error ? error.message : String(error),
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
},
|
|
931
|
+
});
|
|
922
932
|
}
|
|
923
933
|
|
|
924
934
|
function nonJsonResponseTitle(
|
|
@@ -2422,23 +2432,26 @@ async function executeAbsurdAttemptAdapter(
|
|
|
2422
2432
|
let executionLaunch = launch;
|
|
2423
2433
|
try {
|
|
2424
2434
|
const tokenStartedAt = performance.now();
|
|
2425
|
-
const executorToken = await
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2435
|
+
const executorToken = await runWithAbsurdExecutionLeaseHeartbeat({
|
|
2436
|
+
ctx,
|
|
2437
|
+
runId,
|
|
2438
|
+
operation: () =>
|
|
2439
|
+
deps.mintAttemptExecutorToken({
|
|
2440
|
+
launch,
|
|
2441
|
+
attempt: generation,
|
|
2442
|
+
leaseToken,
|
|
2443
|
+
schedulerSchema: options?.schema ?? null,
|
|
2444
|
+
}),
|
|
2445
|
+
}).finally(() => {
|
|
2446
|
+
console.info(
|
|
2447
|
+
'[absurd-runtime.worker.phase] ' +
|
|
2448
|
+
JSON.stringify({
|
|
2449
|
+
runId,
|
|
2450
|
+
phase: 'executor_token_mint',
|
|
2451
|
+
totalMs: performance.now() - tokenStartedAt,
|
|
2452
|
+
}),
|
|
2453
|
+
);
|
|
2454
|
+
});
|
|
2442
2455
|
executionLaunch = {
|
|
2443
2456
|
...launch,
|
|
2444
2457
|
executorToken,
|
|
@@ -2459,12 +2472,56 @@ async function executeAbsurdAttemptAdapter(
|
|
|
2459
2472
|
runtimeApi,
|
|
2460
2473
|
});
|
|
2461
2474
|
} catch (error) {
|
|
2462
|
-
if (
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2475
|
+
if (error instanceof SuspendTask || error instanceof DeferTask) throw error;
|
|
2476
|
+
let detached: Awaited<
|
|
2477
|
+
ReturnType<typeof resumePostgresSchedulerDetachedRunnerAttempt>
|
|
2478
|
+
>;
|
|
2479
|
+
try {
|
|
2480
|
+
detached = await deps.runWithClient((client) =>
|
|
2481
|
+
resumePostgresSchedulerDetachedRunnerAttempt(
|
|
2482
|
+
client,
|
|
2483
|
+
{ runId, attempt: generation, leaseToken },
|
|
2484
|
+
options,
|
|
2485
|
+
),
|
|
2486
|
+
);
|
|
2487
|
+
} catch (recoveryError) {
|
|
2488
|
+
console.error(
|
|
2489
|
+
'[absurd-runtime.worker] detached_runner_recovery_unavailable',
|
|
2490
|
+
JSON.stringify({
|
|
2491
|
+
runId,
|
|
2492
|
+
attempt: generation,
|
|
2493
|
+
engineTaskId: ctx.taskID,
|
|
2494
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2495
|
+
recoveryError:
|
|
2496
|
+
recoveryError instanceof Error
|
|
2497
|
+
? recoveryError.message
|
|
2498
|
+
: String(recoveryError),
|
|
2499
|
+
cleanupAuthorized: false,
|
|
2500
|
+
}),
|
|
2501
|
+
);
|
|
2502
|
+
throw new AggregateError(
|
|
2503
|
+
[error, recoveryError],
|
|
2504
|
+
'Scheduler could not prove detached runner cleanup authority.',
|
|
2505
|
+
);
|
|
2506
|
+
}
|
|
2507
|
+
if (detached) {
|
|
2508
|
+
console.error(
|
|
2509
|
+
'[absurd-runtime.worker] detached_runner_handoff_recovered',
|
|
2510
|
+
JSON.stringify({
|
|
2511
|
+
runId,
|
|
2512
|
+
attempt: generation,
|
|
2513
|
+
engineAttempt: ctx.attempt,
|
|
2514
|
+
engineTaskId: ctx.taskID,
|
|
2515
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2516
|
+
cleanupAuthorized: false,
|
|
2517
|
+
}),
|
|
2518
|
+
);
|
|
2519
|
+
throw new DeferTask(
|
|
2520
|
+
`Detached runner ${runId}/${generation} retains execution after scheduler handoff failure.`,
|
|
2521
|
+
1,
|
|
2522
|
+
);
|
|
2523
|
+
}
|
|
2524
|
+
if (error instanceof CancelledTask || error instanceof FailedTask) {
|
|
2468
2525
|
// Park/cancel signals belong to the engine; never treat them as leg
|
|
2469
2526
|
// failures.
|
|
2470
2527
|
throw error;
|
|
@@ -2786,11 +2843,6 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
2786
2843
|
`absurd runtime worker: suspended runner result for ${runId} carried no suspension.`,
|
|
2787
2844
|
);
|
|
2788
2845
|
}
|
|
2789
|
-
await input.progressReporter.finalize(input.runnerResult, {
|
|
2790
|
-
status: 'running',
|
|
2791
|
-
replayAllTerminalLogs: input.fromDetachedRunner,
|
|
2792
|
-
terminalLogProducerAttempt: input.terminalLogProducerAttempt,
|
|
2793
|
-
});
|
|
2794
2846
|
const suspendedCheckpoint = absurdCheckpointBase(
|
|
2795
2847
|
input.runnerResult.checkpoint ?? checkpoint ?? null,
|
|
2796
2848
|
);
|
|
@@ -2837,16 +2889,29 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
2837
2889
|
store: attemptStore,
|
|
2838
2890
|
engine,
|
|
2839
2891
|
lifecycle: {
|
|
2840
|
-
waiting: async (lifecycle) =>
|
|
2892
|
+
waiting: async (lifecycle) => {
|
|
2893
|
+
await runWithAbsurdExecutionLeaseHeartbeat({
|
|
2894
|
+
ctx,
|
|
2895
|
+
runId,
|
|
2896
|
+
operation: () =>
|
|
2897
|
+
input.progressReporter.finalize(input.runnerResult, {
|
|
2898
|
+
status: 'running',
|
|
2899
|
+
replayAllTerminalLogs: input.fromDetachedRunner,
|
|
2900
|
+
terminalLogProducerAttempt: input.terminalLogProducerAttempt,
|
|
2901
|
+
}),
|
|
2902
|
+
});
|
|
2841
2903
|
await projectLifecycleOutbox({
|
|
2904
|
+
ctx,
|
|
2842
2905
|
deps,
|
|
2843
2906
|
launch,
|
|
2844
2907
|
lifecycle,
|
|
2845
2908
|
options,
|
|
2846
2909
|
transition: 'waiting',
|
|
2847
|
-
})
|
|
2910
|
+
});
|
|
2911
|
+
},
|
|
2848
2912
|
resumed: async (lifecycle) =>
|
|
2849
2913
|
await projectLifecycleOutbox({
|
|
2914
|
+
ctx,
|
|
2850
2915
|
deps,
|
|
2851
2916
|
launch,
|
|
2852
2917
|
lifecycle,
|
|
@@ -2941,6 +3006,7 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
2941
3006
|
return { ok: true, runId, schedulerStatus: 'idle' };
|
|
2942
3007
|
}
|
|
2943
3008
|
await projectLifecycleOutbox({
|
|
3009
|
+
ctx,
|
|
2944
3010
|
deps,
|
|
2945
3011
|
launch,
|
|
2946
3012
|
lifecycle: marked,
|
|
@@ -2987,6 +3053,7 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
2987
3053
|
return { ok: true, runId, schedulerStatus: 'idle' };
|
|
2988
3054
|
}
|
|
2989
3055
|
await projectLifecycleOutbox({
|
|
3056
|
+
ctx,
|
|
2990
3057
|
deps,
|
|
2991
3058
|
launch,
|
|
2992
3059
|
lifecycle: running,
|
|
@@ -3068,6 +3135,16 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3068
3135
|
error: unknown,
|
|
3069
3136
|
runnerConfig: PlayRunnerExecutionConfig,
|
|
3070
3137
|
): Promise<void> => {
|
|
3138
|
+
if (
|
|
3139
|
+
await deps.runWithClient((client) =>
|
|
3140
|
+
hasPostgresSchedulerRecoverableDetachedRunner(
|
|
3141
|
+
client,
|
|
3142
|
+
{ runId, attempt: generation, leaseToken },
|
|
3143
|
+
options,
|
|
3144
|
+
),
|
|
3145
|
+
)
|
|
3146
|
+
)
|
|
3147
|
+
return;
|
|
3071
3148
|
const cleanupTerminalReason =
|
|
3072
3149
|
error instanceof CancelledTask ? 'lease_expired' : 'runner_failed';
|
|
3073
3150
|
await deps
|
|
@@ -3127,33 +3204,52 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3127
3204
|
),
|
|
3128
3205
|
)
|
|
3129
3206
|
.catch(() => attemptResources);
|
|
3130
|
-
//
|
|
3131
|
-
//
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
(await persistAttemptSandboxCleanupBeforeBestEffortGc({
|
|
3138
|
-
deps,
|
|
3139
|
-
runId,
|
|
3140
|
-
attempt: generation,
|
|
3207
|
+
// All provider deletion goes through the durable cleanup lease, including
|
|
3208
|
+
// retired generations. Never pass a read-only resource snapshot to raw GC.
|
|
3209
|
+
if (!attemptResourceReadFailed) {
|
|
3210
|
+
await deps.runWithClient((client) =>
|
|
3211
|
+
enqueuePostgresSchedulerSandboxCleanupForRun(
|
|
3212
|
+
client,
|
|
3213
|
+
{ runId },
|
|
3141
3214
|
options,
|
|
3142
|
-
|
|
3215
|
+
),
|
|
3216
|
+
);
|
|
3217
|
+
const environments = new Set(
|
|
3218
|
+
allResources.flatMap((resource) => {
|
|
3219
|
+
if (!runtimeSandboxRefFromResource(resource)) return [];
|
|
3220
|
+
const value = resource as {
|
|
3221
|
+
runtimeEnvironment?: string;
|
|
3222
|
+
daytonaEnvironment?: string;
|
|
3223
|
+
};
|
|
3224
|
+
return [
|
|
3225
|
+
value.runtimeEnvironment === 'preview' ||
|
|
3226
|
+
value.daytonaEnvironment === 'preview'
|
|
3227
|
+
? ('preview' as const)
|
|
3228
|
+
: ('production' as const),
|
|
3229
|
+
];
|
|
3230
|
+
}),
|
|
3231
|
+
);
|
|
3232
|
+
for (const environment of environments) {
|
|
3233
|
+
await drainPostgresSchedulerSandboxCleanupOnce({
|
|
3234
|
+
runWithClient: deps.runWithClient,
|
|
3235
|
+
deleteRuntimeSandbox: deps.deleteRuntimeSandbox,
|
|
3236
|
+
deleteRuntimeSandboxRef: deps.deleteRuntimeSandboxRef,
|
|
3237
|
+
deleteRuntimeSandboxWithOutcome: deps.deleteRuntimeSandboxWithOutcome,
|
|
3238
|
+
readSandboxCrashDiagnostic: deps.readSandboxCrashDiagnostic,
|
|
3239
|
+
leaseOwner: `attempt-error:${runId}:${generation}`,
|
|
3240
|
+
runId,
|
|
3241
|
+
environment,
|
|
3242
|
+
schedulerOptions: options,
|
|
3243
|
+
timeoutMs: deps.sandboxGcTimeoutMs,
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3143
3247
|
const runtimeStateCleanup = releaseAbsurdAttemptRuntimeState({
|
|
3144
3248
|
config: runnerConfig,
|
|
3145
3249
|
runId,
|
|
3146
3250
|
runAttempt: generation,
|
|
3147
3251
|
resources: attemptResources,
|
|
3148
|
-
sandboxRefs: runtimeSandboxResourceRefs(allResources),
|
|
3149
3252
|
reason: cleanupTerminalReason,
|
|
3150
|
-
deleteRuntimeSandbox: immediateSandboxGcAllowed
|
|
3151
|
-
? deps.deleteRuntimeSandbox
|
|
3152
|
-
: undefined,
|
|
3153
|
-
deleteRuntimeSandboxRef: immediateSandboxGcAllowed
|
|
3154
|
-
? deps.deleteRuntimeSandboxRef
|
|
3155
|
-
: undefined,
|
|
3156
|
-
sandboxGcTimeoutMs: deps.sandboxGcTimeoutMs,
|
|
3157
3253
|
});
|
|
3158
3254
|
if (ctx.maxAttempts !== null && ctx.attempt >= ctx.maxAttempts) {
|
|
3159
3255
|
// A final engine attempt cannot retry. Its durable failed terminal must
|
|
@@ -3190,30 +3286,41 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3190
3286
|
pendingDetachedRunner = null;
|
|
3191
3287
|
let runnerResult: PlayRunnerResult;
|
|
3192
3288
|
try {
|
|
3193
|
-
runnerResult = await
|
|
3289
|
+
runnerResult = await runWithAbsurdExecutionLeaseHeartbeat({
|
|
3290
|
+
ctx,
|
|
3194
3291
|
runId,
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3292
|
+
operation: () =>
|
|
3293
|
+
resolveDetachedRunnerOutcome({
|
|
3294
|
+
runId,
|
|
3295
|
+
suspension: detached,
|
|
3296
|
+
checkpoint: absurdCheckpointBase(checkpoint),
|
|
3297
|
+
readRunnerTerminal,
|
|
3298
|
+
recordRunnerTerminal,
|
|
3299
|
+
readRunnerLiveness: () =>
|
|
3300
|
+
deps.runWithClient((client) =>
|
|
3301
|
+
readPostgresSchedulerRunnerLiveness(
|
|
3302
|
+
client,
|
|
3303
|
+
{ runId, attempt: detached.runnerAttempt },
|
|
3304
|
+
options,
|
|
3305
|
+
),
|
|
3306
|
+
),
|
|
3307
|
+
inspectDetachedRunner: deps.inspectDetachedRunner,
|
|
3308
|
+
deleteRuntimeSandbox: deps.deleteRuntimeSandbox,
|
|
3309
|
+
deleteRuntimeSandboxRef: deps.deleteRuntimeSandboxRef,
|
|
3310
|
+
sandboxGcTimeoutMs: deps.sandboxGcTimeoutMs,
|
|
3311
|
+
}),
|
|
3211
3312
|
});
|
|
3212
3313
|
} catch (error) {
|
|
3213
3314
|
// The terminal fast path skips normal runner setup. Build its cleanup
|
|
3214
3315
|
// context only on failure, then preserve the same pre-retry resource
|
|
3215
3316
|
// release and sandbox-GC fence as the full execution path.
|
|
3216
|
-
const preparedRunnerConfig = await
|
|
3317
|
+
const preparedRunnerConfig = await runWithAbsurdExecutionLeaseHeartbeat(
|
|
3318
|
+
{
|
|
3319
|
+
ctx,
|
|
3320
|
+
runId,
|
|
3321
|
+
operation: () => prepareRunnerConfig(claim),
|
|
3322
|
+
},
|
|
3323
|
+
);
|
|
3217
3324
|
try {
|
|
3218
3325
|
await cleanupAfterExecutionError(
|
|
3219
3326
|
error,
|
|
@@ -3261,7 +3368,11 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3261
3368
|
}
|
|
3262
3369
|
|
|
3263
3370
|
const progressReporter = deps.createProgressReporter(launch, generation);
|
|
3264
|
-
const preparedRunnerConfig = await
|
|
3371
|
+
const preparedRunnerConfig = await runWithAbsurdExecutionLeaseHeartbeat({
|
|
3372
|
+
ctx,
|
|
3373
|
+
runId,
|
|
3374
|
+
operation: () => prepareRunnerConfig(claim),
|
|
3375
|
+
});
|
|
3265
3376
|
const runnerConfig = preparedRunnerConfig.runnerConfig;
|
|
3266
3377
|
|
|
3267
3378
|
let runnerResult: PlayRunnerResult;
|
|
@@ -5028,6 +5139,9 @@ export function createAbsurdRuntimeTasks(
|
|
|
5028
5139
|
if (deps?.deleteRuntimeSandbox && !deps.deleteRuntimeSandboxRef) {
|
|
5029
5140
|
resolved.deleteRuntimeSandboxRef = undefined;
|
|
5030
5141
|
}
|
|
5142
|
+
if (deps?.deleteRuntimeSandbox && !deps.deleteRuntimeSandboxWithOutcome) {
|
|
5143
|
+
resolved.deleteRuntimeSandboxWithOutcome = undefined;
|
|
5144
|
+
}
|
|
5031
5145
|
absurd.registerTask({ name: ABSURD_PLAY_RUN_TASK, queue }, (params, ctx) =>
|
|
5032
5146
|
executeAbsurdRuntimeLaunch(
|
|
5033
5147
|
params as AbsurdPlayRunTaskParams,
|
|
@@ -5145,11 +5259,15 @@ async function readOpenAbsurdEngineRunId(input: {
|
|
|
5145
5259
|
* can win that same terminalization race while a replacement is starting. In
|
|
5146
5260
|
* that case Absurd rejects `complete_run` because the engine is already
|
|
5147
5261
|
* terminal; that is an idempotent success, not a reason to flap the new worker.
|
|
5262
|
+
* A receipt can also arrive before its wake is claimed. Retire that pending or
|
|
5263
|
+
* sleeping task with the existing cancellation primitive after scheduler truth
|
|
5264
|
+
* is durable, rather than failing recovery because complete_run needs a claim.
|
|
5148
5265
|
*/
|
|
5149
5266
|
async function completeAbsurdEngineRunIfStillRunning(input: {
|
|
5150
5267
|
client: PostgresSchedulerQueryClient;
|
|
5151
5268
|
queue: string;
|
|
5152
5269
|
absurdRunId: string;
|
|
5270
|
+
absurdTaskId: string;
|
|
5153
5271
|
result: unknown;
|
|
5154
5272
|
}): Promise<boolean> {
|
|
5155
5273
|
try {
|
|
@@ -5178,6 +5296,13 @@ async function completeAbsurdEngineRunIfStillRunning(input: {
|
|
|
5178
5296
|
) {
|
|
5179
5297
|
return false;
|
|
5180
5298
|
}
|
|
5299
|
+
if (state === 'pending' || state === 'sleeping') {
|
|
5300
|
+
return await cancelAbsurdEngineTaskAfterSchedulerTerminal({
|
|
5301
|
+
client: input.client,
|
|
5302
|
+
queue: input.queue,
|
|
5303
|
+
absurdTaskId: input.absurdTaskId,
|
|
5304
|
+
});
|
|
5305
|
+
}
|
|
5181
5306
|
throw error;
|
|
5182
5307
|
}
|
|
5183
5308
|
}
|
|
@@ -5231,9 +5356,9 @@ async function cancelAbsurdEngineTaskAfterSchedulerTerminal(input: {
|
|
|
5231
5356
|
* its eventual lease reclaim would allocate a fresh scheduler generation and
|
|
5232
5357
|
* re-run a play that has already completed. This recovery uses the original
|
|
5233
5358
|
* attempt lease and the exact same terminal path as the normal wake leg, then
|
|
5234
|
-
*
|
|
5235
|
-
*
|
|
5236
|
-
*
|
|
5359
|
+
* closes an engine run when it still exists. Completed and failed receipts
|
|
5360
|
+
* remain authoritative even after the final engine claim expires. Suspended
|
|
5361
|
+
* receipts retain the normal continuation handoff.
|
|
5237
5362
|
*/
|
|
5238
5363
|
export async function recoverAbsurdRuntimeCompletedRunnerTerminals(input: {
|
|
5239
5364
|
client: PostgresSchedulerQueryClient;
|
|
@@ -5253,6 +5378,7 @@ export async function recoverAbsurdRuntimeCompletedRunnerTerminals(input: {
|
|
|
5253
5378
|
{
|
|
5254
5379
|
limit: input.limit,
|
|
5255
5380
|
absurdReleaseId: input.absurdReleaseId,
|
|
5381
|
+
includeFailed: true,
|
|
5256
5382
|
},
|
|
5257
5383
|
options,
|
|
5258
5384
|
);
|
|
@@ -5261,7 +5387,10 @@ export async function recoverAbsurdRuntimeCompletedRunnerTerminals(input: {
|
|
|
5261
5387
|
|
|
5262
5388
|
for (const terminal of terminals) {
|
|
5263
5389
|
const runnerResult = parseDetachedRunnerResult(terminal.result);
|
|
5264
|
-
if (
|
|
5390
|
+
if (
|
|
5391
|
+
runnerResult?.status !== 'completed' &&
|
|
5392
|
+
runnerResult?.status !== 'failed'
|
|
5393
|
+
) {
|
|
5265
5394
|
skipped += 1;
|
|
5266
5395
|
continue;
|
|
5267
5396
|
}
|
|
@@ -5270,10 +5399,6 @@ export async function recoverAbsurdRuntimeCompletedRunnerTerminals(input: {
|
|
|
5270
5399
|
queue: input.queue,
|
|
5271
5400
|
schedulerRunId: terminal.runId,
|
|
5272
5401
|
});
|
|
5273
|
-
if (!absurdRun) {
|
|
5274
|
-
skipped += 1;
|
|
5275
|
-
continue;
|
|
5276
|
-
}
|
|
5277
5402
|
const launch = await readPostgresSchedulerLaunch(
|
|
5278
5403
|
input.client,
|
|
5279
5404
|
terminal.runId,
|
|
@@ -5292,6 +5417,16 @@ export async function recoverAbsurdRuntimeCompletedRunnerTerminals(input: {
|
|
|
5292
5417
|
let schedulerStatus: 'completed' | 'failed';
|
|
5293
5418
|
let terminalizedByAnotherWorker = false;
|
|
5294
5419
|
try {
|
|
5420
|
+
await markPostgresSchedulerAttemptRunning(
|
|
5421
|
+
input.client,
|
|
5422
|
+
{
|
|
5423
|
+
runId: terminal.runId,
|
|
5424
|
+
attempt: terminal.attempt,
|
|
5425
|
+
leaseToken: terminal.leaseToken,
|
|
5426
|
+
preserveHeartbeatAt: true,
|
|
5427
|
+
},
|
|
5428
|
+
options,
|
|
5429
|
+
);
|
|
5295
5430
|
const settled = await executePostgresSchedulerClaim(
|
|
5296
5431
|
input.client,
|
|
5297
5432
|
{
|
|
@@ -5321,22 +5456,26 @@ export async function recoverAbsurdRuntimeCompletedRunnerTerminals(input: {
|
|
|
5321
5456
|
terminal.runId,
|
|
5322
5457
|
options,
|
|
5323
5458
|
);
|
|
5324
|
-
if (observed?.status !== 'completed'
|
|
5325
|
-
|
|
5459
|
+
if (observed?.status !== 'completed' && observed?.status !== 'failed')
|
|
5460
|
+
throw error;
|
|
5461
|
+
schedulerStatus = observed.status;
|
|
5326
5462
|
terminalizedByAnotherWorker = true;
|
|
5327
5463
|
}
|
|
5328
5464
|
|
|
5329
|
-
const engineCompleted =
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5465
|
+
const engineCompleted = absurdRun
|
|
5466
|
+
? await completeAbsurdEngineRunIfStillRunning({
|
|
5467
|
+
client: input.client,
|
|
5468
|
+
queue: input.queue,
|
|
5469
|
+
absurdRunId: absurdRun.runId,
|
|
5470
|
+
absurdTaskId: absurdRun.taskId,
|
|
5471
|
+
result: {
|
|
5472
|
+
recovered: terminalizedByAnotherWorker
|
|
5473
|
+
? 'completed_runner_terminal_race'
|
|
5474
|
+
: 'completed_runner_terminal',
|
|
5475
|
+
schedulerStatus,
|
|
5476
|
+
},
|
|
5477
|
+
})
|
|
5478
|
+
: false;
|
|
5340
5479
|
if (terminalizedByAnotherWorker) {
|
|
5341
5480
|
if (engineCompleted) recovered += 1;
|
|
5342
5481
|
else skipped += 1;
|
|
@@ -6358,6 +6497,7 @@ function unavailableSandboxCrashDiagnostic(
|
|
|
6358
6497
|
async function claimPostgresSchedulerSandboxCleanupBatch(input: {
|
|
6359
6498
|
runWithClient: AbsurdRuntimeWorkerDeps['runWithClient'];
|
|
6360
6499
|
leaseOwner: string;
|
|
6500
|
+
runId?: string;
|
|
6361
6501
|
environment: 'preview' | 'production';
|
|
6362
6502
|
schedulerOptions: Pick<PostgresSchedulerOptions, 'schema'>;
|
|
6363
6503
|
limit: number;
|
|
@@ -6368,6 +6508,7 @@ async function claimPostgresSchedulerSandboxCleanupBatch(input: {
|
|
|
6368
6508
|
client,
|
|
6369
6509
|
{
|
|
6370
6510
|
leaseOwner: input.leaseOwner,
|
|
6511
|
+
runId: input.runId,
|
|
6371
6512
|
environment: input.environment,
|
|
6372
6513
|
limit: input.limit,
|
|
6373
6514
|
providers: input.providers,
|
|
@@ -6655,7 +6796,8 @@ async function processPostgresSchedulerSandboxCleanupJob(input: {
|
|
|
6655
6796
|
provider: job.provider,
|
|
6656
6797
|
sandboxId: job.sandboxId,
|
|
6657
6798
|
cleanupAttempt: job.attemptCount,
|
|
6658
|
-
|
|
6799
|
+
runStatusAtClaim: job.runStatusAtClaim,
|
|
6800
|
+
cleanupAuthority: 'durable_cleanup_claim',
|
|
6659
6801
|
providerOutcome: outcome.providerOutcome,
|
|
6660
6802
|
completionOutcome: outcome.completion,
|
|
6661
6803
|
});
|
|
@@ -6681,6 +6823,7 @@ export async function drainPostgresSchedulerSandboxCleanupOnce(input: {
|
|
|
6681
6823
|
deleteRuntimeSandboxRef?: AbsurdRuntimeWorkerDeps['deleteRuntimeSandboxRef'];
|
|
6682
6824
|
deleteRuntimeSandboxWithOutcome?: AbsurdRuntimeWorkerDeps['deleteRuntimeSandboxWithOutcome'];
|
|
6683
6825
|
leaseOwner: string;
|
|
6826
|
+
runId?: string;
|
|
6684
6827
|
environment: 'preview' | 'production';
|
|
6685
6828
|
schedulerOptions?: Pick<PostgresSchedulerOptions, 'schema'>;
|
|
6686
6829
|
limit?: number;
|
|
@@ -6702,6 +6845,7 @@ export async function drainPostgresSchedulerSandboxCleanupOnce(input: {
|
|
|
6702
6845
|
const jobs = await claimPostgresSchedulerSandboxCleanupBatch({
|
|
6703
6846
|
runWithClient: input.runWithClient,
|
|
6704
6847
|
leaseOwner: input.leaseOwner,
|
|
6848
|
+
runId: input.runId,
|
|
6705
6849
|
environment: input.environment,
|
|
6706
6850
|
schedulerOptions: options,
|
|
6707
6851
|
limit,
|