deepline 0.1.167 → 0.1.169
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/coordinator-entry.ts +317 -26
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +99 -6
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +219 -73
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +119 -33
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +2 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-instance-create.ts +3 -0
- package/dist/bundling-sources/sdk/src/client.ts +29 -1
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/column-names.ts +50 -8
- package/dist/bundling-sources/shared_libs/play-data-plane/sheet-contract.ts +40 -1
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +1 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +202 -45
- package/dist/cli/index.js +70 -113
- package/dist/cli/index.mjs +70 -113
- package/dist/{compiler-manifest-VhtM9n24.d.mts → compiler-manifest-OwORQ07f.d.mts} +1 -0
- package/dist/{compiler-manifest-VhtM9n24.d.ts → compiler-manifest-OwORQ07f.d.ts} +1 -0
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +26 -5
- package/dist/index.mjs +26 -5
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/package.json +1 -1
|
@@ -112,6 +112,7 @@ export type PlayWorkflowParams = {
|
|
|
112
112
|
dynamicWorkerCode?: string | null;
|
|
113
113
|
executorToken: string;
|
|
114
114
|
baseUrl: string;
|
|
115
|
+
integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
|
|
115
116
|
orgId: string;
|
|
116
117
|
userEmail: string;
|
|
117
118
|
userId?: string | null;
|
|
@@ -628,6 +629,7 @@ type DynamicWorkflowMetadata = {
|
|
|
628
629
|
artifactStorageKey: string;
|
|
629
630
|
artifactHash?: string | null;
|
|
630
631
|
dynamicWorkerCode?: string | null;
|
|
632
|
+
integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
|
|
631
633
|
packagedFiles?: Array<{
|
|
632
634
|
playPath: string;
|
|
633
635
|
storageKey: string;
|
|
@@ -640,9 +642,19 @@ type DynamicWorkflowMetadata = {
|
|
|
640
642
|
const SUBMIT_INITIAL_STATE_MAX_WAIT_MS = 0;
|
|
641
643
|
const SUBMIT_INITIAL_STATE_POLL_MS = 50;
|
|
642
644
|
const WORKFLOW_RETRY_STATE_TTL_MS = 60 * 60 * 1000;
|
|
645
|
+
const WORKFLOW_SUBMIT_DUPLICATE_WAIT_MS = 55_000;
|
|
646
|
+
const WORKFLOW_SUBMIT_DUPLICATE_POLL_MS = 250;
|
|
643
647
|
const COORDINATOR_WORKER_MEMORY_LIMIT_MESSAGE =
|
|
644
648
|
'Worker memory limit hit before the run could finish. For CSV enrich, use --output or --in-place for automatic batches; otherwise rerun smaller --rows ranges.';
|
|
645
649
|
|
|
650
|
+
function normalizeIntegrationMode(
|
|
651
|
+
value: unknown,
|
|
652
|
+
): 'live' | 'eval_stub' | 'fixture' | null {
|
|
653
|
+
return value === 'live' || value === 'eval_stub' || value === 'fixture'
|
|
654
|
+
? value
|
|
655
|
+
: null;
|
|
656
|
+
}
|
|
657
|
+
|
|
646
658
|
function buildDynamicWorkflowMetadata(
|
|
647
659
|
params: PlayWorkflowParams,
|
|
648
660
|
): DynamicWorkflowMetadata {
|
|
@@ -652,6 +664,7 @@ function buildDynamicWorkflowMetadata(
|
|
|
652
664
|
artifactStorageKey: params.artifactStorageKey,
|
|
653
665
|
artifactHash: params.artifactHash ?? null,
|
|
654
666
|
dynamicWorkerCode: params.dynamicWorkerCode ?? null,
|
|
667
|
+
integrationMode: normalizeIntegrationMode(params.integrationMode),
|
|
655
668
|
packagedFiles: normalizePackagedFiles(params.packagedFiles),
|
|
656
669
|
};
|
|
657
670
|
}
|
|
@@ -879,6 +892,72 @@ async function resolveWorkflowInstanceIdForRun(
|
|
|
879
892
|
: workflowInstanceId(runId);
|
|
880
893
|
}
|
|
881
894
|
|
|
895
|
+
async function claimWorkflowSubmit(input: {
|
|
896
|
+
env: CoordinatorEnv;
|
|
897
|
+
runId: string;
|
|
898
|
+
}): Promise<{
|
|
899
|
+
claimed: boolean;
|
|
900
|
+
status: 'creating' | 'created' | null;
|
|
901
|
+
instanceId: string | null;
|
|
902
|
+
}> {
|
|
903
|
+
const body = await callRunScopedControl<{
|
|
904
|
+
claimed?: unknown;
|
|
905
|
+
status?: unknown;
|
|
906
|
+
instanceId?: unknown;
|
|
907
|
+
}>(input.env, input.runId, '/workflow-submit-claim', {
|
|
908
|
+
method: 'POST',
|
|
909
|
+
body: JSON.stringify({
|
|
910
|
+
runId: input.runId,
|
|
911
|
+
ttlMs: WORKFLOW_RETRY_STATE_TTL_MS,
|
|
912
|
+
}),
|
|
913
|
+
});
|
|
914
|
+
return {
|
|
915
|
+
claimed: body.claimed === true,
|
|
916
|
+
status:
|
|
917
|
+
body.status === 'creating' || body.status === 'created'
|
|
918
|
+
? body.status
|
|
919
|
+
: null,
|
|
920
|
+
instanceId: typeof body.instanceId === 'string' ? body.instanceId : null,
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
async function releaseWorkflowSubmitClaim(input: {
|
|
925
|
+
env: CoordinatorEnv;
|
|
926
|
+
runId: string;
|
|
927
|
+
}): Promise<void> {
|
|
928
|
+
await callRunScopedControl<{ ok?: unknown }>(
|
|
929
|
+
input.env,
|
|
930
|
+
input.runId,
|
|
931
|
+
'/workflow-submit-release',
|
|
932
|
+
{
|
|
933
|
+
method: 'POST',
|
|
934
|
+
body: JSON.stringify({ runId: input.runId }),
|
|
935
|
+
},
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
async function waitForWorkflowSubmitCreated(input: {
|
|
940
|
+
env: CoordinatorEnv;
|
|
941
|
+
runId: string;
|
|
942
|
+
initialClaim: Awaited<ReturnType<typeof claimWorkflowSubmit>>;
|
|
943
|
+
timeoutMs: number;
|
|
944
|
+
pollMs: number;
|
|
945
|
+
}): Promise<Awaited<ReturnType<typeof claimWorkflowSubmit>>> {
|
|
946
|
+
let claim = input.initialClaim;
|
|
947
|
+
const deadline = Date.now() + input.timeoutMs;
|
|
948
|
+
while (claim.status !== 'created' || !claim.instanceId) {
|
|
949
|
+
if (Date.now() >= deadline) {
|
|
950
|
+
return claim;
|
|
951
|
+
}
|
|
952
|
+
await sleep(input.pollMs);
|
|
953
|
+
claim = await claimWorkflowSubmit({
|
|
954
|
+
env: input.env,
|
|
955
|
+
runId: input.runId,
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
return claim;
|
|
959
|
+
}
|
|
960
|
+
|
|
882
961
|
function assertEncryptedPreloadedDbSessions(
|
|
883
962
|
sessions: PreloadedRuntimeDbSession[],
|
|
884
963
|
): void {
|
|
@@ -1715,7 +1794,7 @@ async function preloadChildRuntimeDbSessions(input: {
|
|
|
1715
1794
|
return sessions;
|
|
1716
1795
|
}
|
|
1717
1796
|
|
|
1718
|
-
async function
|
|
1797
|
+
async function registerChildRunWithRuntime(input: {
|
|
1719
1798
|
env: CoordinatorEnv;
|
|
1720
1799
|
baseUrl: string;
|
|
1721
1800
|
childExecutorToken: string;
|
|
@@ -1723,6 +1802,9 @@ async function registerInlineChildRunWithRuntime(input: {
|
|
|
1723
1802
|
childPlayName: string;
|
|
1724
1803
|
manifest: PlayRuntimeManifest;
|
|
1725
1804
|
governance: PlayCallGovernanceSnapshot;
|
|
1805
|
+
runtimeBackend: string;
|
|
1806
|
+
schedulerBackend: string;
|
|
1807
|
+
executionProfile: string;
|
|
1726
1808
|
}): Promise<void> {
|
|
1727
1809
|
const response = await input.env.HARNESS.runtimeApiCall({
|
|
1728
1810
|
executorToken: input.childExecutorToken,
|
|
@@ -1743,9 +1825,9 @@ async function registerInlineChildRunWithRuntime(input: {
|
|
|
1743
1825
|
artifactStorageKey: input.manifest.artifactStorageKey,
|
|
1744
1826
|
artifactHash: input.manifest.artifactHash,
|
|
1745
1827
|
graphHash: input.manifest.graphHash,
|
|
1746
|
-
runtimeBackend:
|
|
1747
|
-
schedulerBackend:
|
|
1748
|
-
executionProfile:
|
|
1828
|
+
runtimeBackend: input.runtimeBackend,
|
|
1829
|
+
schedulerBackend: input.schedulerBackend,
|
|
1830
|
+
executionProfile: input.executionProfile,
|
|
1749
1831
|
...(typeof input.manifest.maxCreditsPerRun === 'number'
|
|
1750
1832
|
? { maxCreditsPerRun: input.manifest.maxCreditsPerRun }
|
|
1751
1833
|
: {}),
|
|
@@ -1761,6 +1843,43 @@ async function registerInlineChildRunWithRuntime(input: {
|
|
|
1761
1843
|
}
|
|
1762
1844
|
}
|
|
1763
1845
|
|
|
1846
|
+
async function markRegisteredChildRunFailed(input: {
|
|
1847
|
+
env: CoordinatorEnv;
|
|
1848
|
+
baseUrl: string;
|
|
1849
|
+
childExecutorToken: string;
|
|
1850
|
+
childRunId: string;
|
|
1851
|
+
error: unknown;
|
|
1852
|
+
}): Promise<void> {
|
|
1853
|
+
const message =
|
|
1854
|
+
input.error instanceof Error ? input.error.message : String(input.error);
|
|
1855
|
+
const response = await input.env.HARNESS.runtimeApiCall({
|
|
1856
|
+
executorToken: input.childExecutorToken,
|
|
1857
|
+
baseUrl: input.baseUrl,
|
|
1858
|
+
path: '/api/v2/plays/internal/runtime',
|
|
1859
|
+
headers: { 'x-deepline-request-id': crypto.randomUUID() },
|
|
1860
|
+
timeoutMs: 15_000,
|
|
1861
|
+
body: {
|
|
1862
|
+
action: 'append_run_events',
|
|
1863
|
+
playId: input.childRunId,
|
|
1864
|
+
events: [
|
|
1865
|
+
{
|
|
1866
|
+
type: 'run.failed',
|
|
1867
|
+
runId: input.childRunId,
|
|
1868
|
+
source: 'coordinator',
|
|
1869
|
+
occurredAt: Date.now(),
|
|
1870
|
+
error: `Child workflow submit failed: ${message}`,
|
|
1871
|
+
} satisfies PlayRunLedgerEvent,
|
|
1872
|
+
],
|
|
1873
|
+
},
|
|
1874
|
+
});
|
|
1875
|
+
if (response.status < 200 || response.status >= 300) {
|
|
1876
|
+
const text = response.body ?? '';
|
|
1877
|
+
throw new Error(
|
|
1878
|
+
`Inline child run failure mark failed ${response.status}: ${text.slice(0, 800)}`,
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1764
1883
|
type CoordinatorRuntimeApiTiming = {
|
|
1765
1884
|
phase: string;
|
|
1766
1885
|
ms: number;
|
|
@@ -2094,6 +2213,7 @@ function buildChildWorkflowParams(input: {
|
|
|
2094
2213
|
dynamicWorkerCode,
|
|
2095
2214
|
executorToken: childToken,
|
|
2096
2215
|
baseUrl,
|
|
2216
|
+
integrationMode: normalizeIntegrationMode(body.integrationMode),
|
|
2097
2217
|
orgId,
|
|
2098
2218
|
userEmail: typeof body.userEmail === 'string' ? body.userEmail : '',
|
|
2099
2219
|
userId: typeof body.userId === 'string' ? body.userId : null,
|
|
@@ -2121,6 +2241,7 @@ function runRequestFromPlayWorkflowParams(
|
|
|
2121
2241
|
callbackUrl: params.baseUrl,
|
|
2122
2242
|
executorToken: params.executorToken,
|
|
2123
2243
|
baseUrl: params.baseUrl,
|
|
2244
|
+
integrationMode: normalizeIntegrationMode(params.integrationMode),
|
|
2124
2245
|
orgId: params.orgId,
|
|
2125
2246
|
playName: params.playName,
|
|
2126
2247
|
graphHash: params.graphHash,
|
|
@@ -2630,22 +2751,63 @@ async function submitChildWorkflowThroughCoordinator(input: {
|
|
|
2630
2751
|
preloadedDbSessions.length > 0 ? preloadedDbSessions : null,
|
|
2631
2752
|
});
|
|
2632
2753
|
|
|
2633
|
-
const
|
|
2634
|
-
|
|
2635
|
-
runId: childRunId,
|
|
2636
|
-
action: 'submit',
|
|
2637
|
-
request: new Request(
|
|
2638
|
-
`https://deepline.coordinator.internal/workflow/${encodeURIComponent(
|
|
2639
|
-
childRunId,
|
|
2640
|
-
)}/submit`,
|
|
2641
|
-
{
|
|
2642
|
-
method: 'POST',
|
|
2643
|
-
headers: { 'content-type': 'application/json' },
|
|
2644
|
-
body: JSON.stringify(params),
|
|
2645
|
-
},
|
|
2646
|
-
),
|
|
2754
|
+
const registerStartedAt = Date.now();
|
|
2755
|
+
await registerChildRunWithRuntime({
|
|
2647
2756
|
env: input.env,
|
|
2757
|
+
baseUrl,
|
|
2758
|
+
childExecutorToken: childToken,
|
|
2759
|
+
childRunId,
|
|
2760
|
+
childPlayName,
|
|
2761
|
+
manifest,
|
|
2762
|
+
governance,
|
|
2763
|
+
runtimeBackend: 'cf_workflows_dynamic_worker',
|
|
2764
|
+
schedulerBackend: 'cf_workflows',
|
|
2765
|
+
executionProfile: 'workers_edge',
|
|
2648
2766
|
});
|
|
2767
|
+
trace(
|
|
2768
|
+
'coordinator.child_submit_register_run',
|
|
2769
|
+
registerStartedAt,
|
|
2770
|
+
manifest.graphHash,
|
|
2771
|
+
{
|
|
2772
|
+
childRunId,
|
|
2773
|
+
childPlayName,
|
|
2774
|
+
},
|
|
2775
|
+
);
|
|
2776
|
+
|
|
2777
|
+
const workflowSubmitStartedAt = Date.now();
|
|
2778
|
+
let response: Response;
|
|
2779
|
+
try {
|
|
2780
|
+
response = await handleWorkflowRoute({
|
|
2781
|
+
runId: childRunId,
|
|
2782
|
+
action: 'submit',
|
|
2783
|
+
request: new Request(
|
|
2784
|
+
`https://deepline.coordinator.internal/workflow/${encodeURIComponent(
|
|
2785
|
+
childRunId,
|
|
2786
|
+
)}/submit`,
|
|
2787
|
+
{
|
|
2788
|
+
method: 'POST',
|
|
2789
|
+
headers: { 'content-type': 'application/json' },
|
|
2790
|
+
body: JSON.stringify(params),
|
|
2791
|
+
},
|
|
2792
|
+
),
|
|
2793
|
+
env: input.env,
|
|
2794
|
+
});
|
|
2795
|
+
} catch (error) {
|
|
2796
|
+
await markRegisteredChildRunFailed({
|
|
2797
|
+
env: input.env,
|
|
2798
|
+
baseUrl,
|
|
2799
|
+
childExecutorToken: childToken,
|
|
2800
|
+
childRunId,
|
|
2801
|
+
error,
|
|
2802
|
+
}).catch((markError) => {
|
|
2803
|
+
console.error('[coordinator] child workflow submit failure mark failed', {
|
|
2804
|
+
childRunId,
|
|
2805
|
+
error:
|
|
2806
|
+
markError instanceof Error ? markError.message : String(markError),
|
|
2807
|
+
});
|
|
2808
|
+
});
|
|
2809
|
+
throw error;
|
|
2810
|
+
}
|
|
2649
2811
|
trace(
|
|
2650
2812
|
'coordinator.child_submit_workflow',
|
|
2651
2813
|
workflowSubmitStartedAt,
|
|
@@ -2653,6 +2815,21 @@ async function submitChildWorkflowThroughCoordinator(input: {
|
|
|
2653
2815
|
{ childRunId, status: response.status },
|
|
2654
2816
|
);
|
|
2655
2817
|
const responseText = await response.text().catch(() => '');
|
|
2818
|
+
if (!response.ok) {
|
|
2819
|
+
await markRegisteredChildRunFailed({
|
|
2820
|
+
env: input.env,
|
|
2821
|
+
baseUrl,
|
|
2822
|
+
childExecutorToken: childToken,
|
|
2823
|
+
childRunId,
|
|
2824
|
+
error: `workflow submit returned ${response.status}: ${responseText.slice(0, 800)}`,
|
|
2825
|
+
}).catch((markError) => {
|
|
2826
|
+
console.error('[coordinator] child workflow submit failure mark failed', {
|
|
2827
|
+
childRunId,
|
|
2828
|
+
error:
|
|
2829
|
+
markError instanceof Error ? markError.message : String(markError),
|
|
2830
|
+
});
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2656
2833
|
return {
|
|
2657
2834
|
response,
|
|
2658
2835
|
responseText,
|
|
@@ -3025,6 +3202,9 @@ export class DynamicWorkflow extends WorkflowEntrypoint<
|
|
|
3025
3202
|
typeof metadata.dynamicWorkerCode === 'string'
|
|
3026
3203
|
? metadata.dynamicWorkerCode
|
|
3027
3204
|
: null,
|
|
3205
|
+
integrationMode: normalizeIntegrationMode(
|
|
3206
|
+
metadata.integrationMode,
|
|
3207
|
+
),
|
|
3028
3208
|
packagedFiles: normalizePackagedFiles(metadata.packagedFiles),
|
|
3029
3209
|
},
|
|
3030
3210
|
trace,
|
|
@@ -3612,6 +3792,7 @@ async function handleWorkflowRoute(input: {
|
|
|
3612
3792
|
ms: Date.now() - submitStartedAt,
|
|
3613
3793
|
graphHash: params.graphHash ?? null,
|
|
3614
3794
|
extra: {
|
|
3795
|
+
integrationMode: normalizeIntegrationMode(params.integrationMode),
|
|
3615
3796
|
hasDynamicWorkerCode: Boolean(params.dynamicWorkerCode),
|
|
3616
3797
|
dynamicWorkerBytes:
|
|
3617
3798
|
typeof params.dynamicWorkerCode === 'string'
|
|
@@ -3624,6 +3805,7 @@ async function handleWorkflowRoute(input: {
|
|
|
3624
3805
|
ms: Date.now() - parseStartedAt,
|
|
3625
3806
|
graphHash: params.graphHash ?? null,
|
|
3626
3807
|
extra: {
|
|
3808
|
+
integrationMode: normalizeIntegrationMode(params.integrationMode),
|
|
3627
3809
|
hasDynamicWorkerCode: Boolean(params.dynamicWorkerCode),
|
|
3628
3810
|
dynamicWorkerBytes:
|
|
3629
3811
|
typeof params.dynamicWorkerCode === 'string'
|
|
@@ -3746,6 +3928,92 @@ async function handleWorkflowRoute(input: {
|
|
|
3746
3928
|
workflowParams.submittedAt = Date.now();
|
|
3747
3929
|
let instance: WorkflowInstance | null = null;
|
|
3748
3930
|
try {
|
|
3931
|
+
const submitClaimStartedAt = Date.now();
|
|
3932
|
+
const submitClaim = await claimWorkflowSubmit({
|
|
3933
|
+
env,
|
|
3934
|
+
runId: submittedRunId,
|
|
3935
|
+
});
|
|
3936
|
+
recordSubmitTiming({
|
|
3937
|
+
phase: 'coordinator.workflow_submit_claim',
|
|
3938
|
+
ms: Date.now() - submitClaimStartedAt,
|
|
3939
|
+
graphHash: params.graphHash ?? null,
|
|
3940
|
+
extra: {
|
|
3941
|
+
claimed: submitClaim.claimed,
|
|
3942
|
+
status: submitClaim.status,
|
|
3943
|
+
instanceId: submitClaim.instanceId ?? defaultInstanceId,
|
|
3944
|
+
},
|
|
3945
|
+
});
|
|
3946
|
+
if (!submitClaim.claimed) {
|
|
3947
|
+
const duplicateWaitStartedAt = Date.now();
|
|
3948
|
+
const resolvedSubmitClaim = await waitForWorkflowSubmitCreated({
|
|
3949
|
+
env,
|
|
3950
|
+
runId: submittedRunId,
|
|
3951
|
+
initialClaim: submitClaim,
|
|
3952
|
+
timeoutMs: WORKFLOW_SUBMIT_DUPLICATE_WAIT_MS,
|
|
3953
|
+
pollMs: WORKFLOW_SUBMIT_DUPLICATE_POLL_MS,
|
|
3954
|
+
});
|
|
3955
|
+
recordSubmitTiming({
|
|
3956
|
+
phase: 'coordinator.workflow_submit_duplicate_wait',
|
|
3957
|
+
ms: Date.now() - duplicateWaitStartedAt,
|
|
3958
|
+
graphHash: params.graphHash ?? null,
|
|
3959
|
+
extra: {
|
|
3960
|
+
status: resolvedSubmitClaim.status,
|
|
3961
|
+
instanceId: resolvedSubmitClaim.instanceId ?? null,
|
|
3962
|
+
},
|
|
3963
|
+
});
|
|
3964
|
+
const totalMs = Date.now() - submitStartedAt;
|
|
3965
|
+
if (
|
|
3966
|
+
resolvedSubmitClaim.status !== 'created' ||
|
|
3967
|
+
!resolvedSubmitClaim.instanceId
|
|
3968
|
+
) {
|
|
3969
|
+
recordSubmitTiming({
|
|
3970
|
+
phase: 'coordinator.submit_total',
|
|
3971
|
+
ms: totalMs,
|
|
3972
|
+
graphHash: params.graphHash ?? null,
|
|
3973
|
+
extra: { duplicate: true, pending: true },
|
|
3974
|
+
});
|
|
3975
|
+
return Response.json(
|
|
3976
|
+
{
|
|
3977
|
+
runId,
|
|
3978
|
+
status: 'pending',
|
|
3979
|
+
workflowInstanceId: null,
|
|
3980
|
+
instanceState: {
|
|
3981
|
+
status: resolvedSubmitClaim.status ?? 'creating',
|
|
3982
|
+
},
|
|
3983
|
+
retryAfterMs: 250,
|
|
3984
|
+
runtimeDeployVersion:
|
|
3985
|
+
env.CF_VERSION_METADATA?.id ??
|
|
3986
|
+
env.DEEPLINE_COORDINATOR_DEPLOY_MARKER ??
|
|
3987
|
+
null,
|
|
3988
|
+
coordinatorTimings,
|
|
3989
|
+
},
|
|
3990
|
+
{ status: 503, headers: { 'retry-after': '1' } },
|
|
3991
|
+
);
|
|
3992
|
+
}
|
|
3993
|
+
recordSubmitTiming({
|
|
3994
|
+
phase: 'coordinator.submit_total',
|
|
3995
|
+
ms: totalMs,
|
|
3996
|
+
graphHash: params.graphHash ?? null,
|
|
3997
|
+
extra: { duplicate: true },
|
|
3998
|
+
});
|
|
3999
|
+
recordSubmitTiming({
|
|
4000
|
+
phase: 'coordinator.submit_accepted',
|
|
4001
|
+
ms: totalMs,
|
|
4002
|
+
graphHash: params.graphHash ?? null,
|
|
4003
|
+
extra: { duplicate: true },
|
|
4004
|
+
});
|
|
4005
|
+
return Response.json({
|
|
4006
|
+
runId,
|
|
4007
|
+
status: 'submitted',
|
|
4008
|
+
workflowInstanceId: resolvedSubmitClaim.instanceId,
|
|
4009
|
+
instanceState: { status: resolvedSubmitClaim.status },
|
|
4010
|
+
runtimeDeployVersion:
|
|
4011
|
+
env.CF_VERSION_METADATA?.id ??
|
|
4012
|
+
env.DEEPLINE_COORDINATOR_DEPLOY_MARKER ??
|
|
4013
|
+
null,
|
|
4014
|
+
coordinatorTimings,
|
|
4015
|
+
});
|
|
4016
|
+
}
|
|
3749
4017
|
const dispatchStartedAt = Date.now();
|
|
3750
4018
|
const createStartedAt = Date.now();
|
|
3751
4019
|
recordSubmitTiming({
|
|
@@ -3762,6 +4030,12 @@ async function handleWorkflowRoute(input: {
|
|
|
3762
4030
|
params: workflowParams,
|
|
3763
4031
|
}),
|
|
3764
4032
|
getExisting: () => env.PLAY_WORKFLOW.get(defaultInstanceId),
|
|
4033
|
+
onCreateFailure: async () => {
|
|
4034
|
+
await releaseWorkflowSubmitClaim({
|
|
4035
|
+
env,
|
|
4036
|
+
runId: submittedRunId,
|
|
4037
|
+
});
|
|
4038
|
+
},
|
|
3765
4039
|
});
|
|
3766
4040
|
instance = createResult.instance;
|
|
3767
4041
|
const workflowCreatedAt = Date.now();
|
|
@@ -3771,18 +4045,35 @@ async function handleWorkflowRoute(input: {
|
|
|
3771
4045
|
graphHash: params.graphHash ?? null,
|
|
3772
4046
|
extra: { instanceId: instance.id, startMode: createResult.startMode },
|
|
3773
4047
|
});
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
4048
|
+
try {
|
|
4049
|
+
await recordWorkflowInstanceId({
|
|
4050
|
+
env,
|
|
4051
|
+
runId: submittedRunId,
|
|
4052
|
+
instanceId: instance.id,
|
|
4053
|
+
});
|
|
4054
|
+
} catch (error) {
|
|
3779
4055
|
console.warn('[coordinator] workflow instance id record failed', {
|
|
3780
4056
|
runId: submittedRunId,
|
|
3781
4057
|
instanceId: instance?.id ?? null,
|
|
3782
4058
|
error: error instanceof Error ? error.message : String(error),
|
|
3783
4059
|
});
|
|
3784
|
-
|
|
3785
|
-
|
|
4060
|
+
await releaseWorkflowSubmitClaim({
|
|
4061
|
+
env,
|
|
4062
|
+
runId: submittedRunId,
|
|
4063
|
+
}).catch((releaseError) => {
|
|
4064
|
+
console.warn(
|
|
4065
|
+
'[coordinator] workflow submit claim release after instance record failure failed',
|
|
4066
|
+
{
|
|
4067
|
+
runId: submittedRunId,
|
|
4068
|
+
error:
|
|
4069
|
+
releaseError instanceof Error
|
|
4070
|
+
? releaseError.message
|
|
4071
|
+
: String(releaseError),
|
|
4072
|
+
},
|
|
4073
|
+
);
|
|
4074
|
+
});
|
|
4075
|
+
throw error;
|
|
4076
|
+
}
|
|
3786
4077
|
recordSubmitTiming({
|
|
3787
4078
|
phase: 'coordinator.dispatch_workflow',
|
|
3788
4079
|
ms: Date.now() - dispatchStartedAt,
|
|
@@ -3790,7 +4081,7 @@ async function handleWorkflowRoute(input: {
|
|
|
3790
4081
|
extra: {
|
|
3791
4082
|
startMode: 'direct_workflow_create',
|
|
3792
4083
|
workflowCreateMode: createResult.startMode,
|
|
3793
|
-
instanceIdRecord: '
|
|
4084
|
+
instanceIdRecord: 'recorded',
|
|
3794
4085
|
},
|
|
3795
4086
|
});
|
|
3796
4087
|
const initialWaitMsRaw = Number(
|
|
@@ -102,6 +102,15 @@ type WorkflowInstanceState = {
|
|
|
102
102
|
expiresAt: number;
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
type WorkflowSubmitClaimState = {
|
|
106
|
+
runId: string;
|
|
107
|
+
status: 'creating' | 'created';
|
|
108
|
+
instanceId?: string;
|
|
109
|
+
claimedAt: number;
|
|
110
|
+
updatedAt: number;
|
|
111
|
+
expiresAt: number;
|
|
112
|
+
};
|
|
113
|
+
|
|
105
114
|
type WorkflowDbSessionsState = {
|
|
106
115
|
runId: string;
|
|
107
116
|
sessions: PreloadedRuntimeDbSession[];
|
|
@@ -357,6 +366,10 @@ export class PlayDedup implements DurableObject {
|
|
|
357
366
|
return await this.handleWorkflowInstancePut(req);
|
|
358
367
|
case '/workflow-instance-get':
|
|
359
368
|
return await this.handleWorkflowInstanceGet(req);
|
|
369
|
+
case '/workflow-submit-claim':
|
|
370
|
+
return await this.handleWorkflowSubmitClaim(req);
|
|
371
|
+
case '/workflow-submit-release':
|
|
372
|
+
return await this.handleWorkflowSubmitRelease(req);
|
|
360
373
|
case '/run-retry-claim':
|
|
361
374
|
return await this.handleRunRetryClaim(req);
|
|
362
375
|
case '/db-sessions-put':
|
|
@@ -787,12 +800,22 @@ export class PlayDedup implements DurableObject {
|
|
|
787
800
|
body.ttlMs > 0
|
|
788
801
|
? Math.max(60_000, Math.min(body.ttlMs, WORKFLOW_RUN_STATE_TTL_MS))
|
|
789
802
|
: WORKFLOW_RUN_STATE_TTL_MS;
|
|
790
|
-
await this.state.storage.put(
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
803
|
+
await this.state.storage.put({
|
|
804
|
+
'workflow-instance': {
|
|
805
|
+
runId,
|
|
806
|
+
instanceId,
|
|
807
|
+
updatedAt: now,
|
|
808
|
+
expiresAt: now + ttlMs,
|
|
809
|
+
} satisfies WorkflowInstanceState,
|
|
810
|
+
'workflow-submit-claim': {
|
|
811
|
+
runId,
|
|
812
|
+
status: 'created',
|
|
813
|
+
instanceId,
|
|
814
|
+
claimedAt: now,
|
|
815
|
+
updatedAt: now,
|
|
816
|
+
expiresAt: now + ttlMs,
|
|
817
|
+
} satisfies WorkflowSubmitClaimState,
|
|
818
|
+
});
|
|
796
819
|
return new Response(JSON.stringify({ ok: true }), {
|
|
797
820
|
headers: { 'content-type': 'application/json' },
|
|
798
821
|
});
|
|
@@ -821,6 +844,76 @@ export class PlayDedup implements DurableObject {
|
|
|
821
844
|
});
|
|
822
845
|
}
|
|
823
846
|
|
|
847
|
+
private async handleWorkflowSubmitClaim(req: Request): Promise<Response> {
|
|
848
|
+
const body = (await req.json().catch(() => null)) as {
|
|
849
|
+
runId?: unknown;
|
|
850
|
+
ttlMs?: unknown;
|
|
851
|
+
} | null;
|
|
852
|
+
const runId = typeof body?.runId === 'string' ? body.runId : '';
|
|
853
|
+
if (!runId) {
|
|
854
|
+
return new Response('runId is required', { status: 400 });
|
|
855
|
+
}
|
|
856
|
+
const now = Date.now();
|
|
857
|
+
const ttlMs =
|
|
858
|
+
typeof body?.ttlMs === 'number' &&
|
|
859
|
+
Number.isFinite(body.ttlMs) &&
|
|
860
|
+
body.ttlMs > 0
|
|
861
|
+
? Math.max(60_000, Math.min(body.ttlMs, WORKFLOW_RUN_STATE_TTL_MS))
|
|
862
|
+
: WORKFLOW_RUN_STATE_TTL_MS;
|
|
863
|
+
let response: {
|
|
864
|
+
claimed: boolean;
|
|
865
|
+
status: WorkflowSubmitClaimState['status'];
|
|
866
|
+
instanceId: string | null;
|
|
867
|
+
} = { claimed: true, status: 'creating', instanceId: null };
|
|
868
|
+
await this.state.blockConcurrencyWhile(async () => {
|
|
869
|
+
const key = 'workflow-submit-claim';
|
|
870
|
+
const existing =
|
|
871
|
+
await this.state.storage.get<WorkflowSubmitClaimState>(key);
|
|
872
|
+
if (existing?.runId === runId && existing.expiresAt > now) {
|
|
873
|
+
response = {
|
|
874
|
+
claimed: false,
|
|
875
|
+
status: existing.status,
|
|
876
|
+
instanceId: existing.instanceId ?? null,
|
|
877
|
+
};
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
const state = {
|
|
881
|
+
runId,
|
|
882
|
+
status: 'creating',
|
|
883
|
+
claimedAt: now,
|
|
884
|
+
updatedAt: now,
|
|
885
|
+
expiresAt: now + ttlMs,
|
|
886
|
+
} satisfies WorkflowSubmitClaimState;
|
|
887
|
+
await this.state.storage.put(key, state);
|
|
888
|
+
});
|
|
889
|
+
return new Response(JSON.stringify(response), {
|
|
890
|
+
headers: { 'content-type': 'application/json' },
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
private async handleWorkflowSubmitRelease(req: Request): Promise<Response> {
|
|
895
|
+
const body = (await req.json().catch(() => null)) as {
|
|
896
|
+
runId?: unknown;
|
|
897
|
+
} | null;
|
|
898
|
+
const runId = typeof body?.runId === 'string' ? body.runId : '';
|
|
899
|
+
if (!runId) {
|
|
900
|
+
return new Response('runId is required', { status: 400 });
|
|
901
|
+
}
|
|
902
|
+
let released = false;
|
|
903
|
+
await this.state.blockConcurrencyWhile(async () => {
|
|
904
|
+
const key = 'workflow-submit-claim';
|
|
905
|
+
const existing =
|
|
906
|
+
await this.state.storage.get<WorkflowSubmitClaimState>(key);
|
|
907
|
+
if (existing?.runId === runId && existing.status === 'creating') {
|
|
908
|
+
await this.state.storage.delete(key);
|
|
909
|
+
released = true;
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
return new Response(JSON.stringify({ ok: true, released }), {
|
|
913
|
+
headers: { 'content-type': 'application/json' },
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
|
|
824
917
|
private async handleDbSessionsPut(req: Request): Promise<Response> {
|
|
825
918
|
const body = (await req.json().catch(() => null)) as {
|
|
826
919
|
runId?: unknown;
|