deepline 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/cli/index.js +171 -47
- package/dist/cli/index.mjs +169 -45
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +360 -41
- package/dist/repo/apps/play-runner-workers/src/entry.ts +101 -42
- package/dist/repo/sdk/src/plays/harness-stub.ts +2 -20
- package/dist/repo/sdk/src/version.ts +1 -1
- package/package.json +1 -1
|
@@ -232,7 +232,7 @@ interface CoordinatorEnv {
|
|
|
232
232
|
/**
|
|
233
233
|
* Service binding to the long-lived Play Harness Worker
|
|
234
234
|
* (apps/play-harness-worker). Provides typed RPC access to leaf-level
|
|
235
|
-
* helpers (
|
|
235
|
+
* helpers (runtime-API HTTP forwarder, Neon dataset IO, …) that we
|
|
236
236
|
* deliberately keep OUT of every per-graphHash play bundle.
|
|
237
237
|
*
|
|
238
238
|
* Optional: when missing (e.g. an older deploy that hasn't been wired
|
|
@@ -547,10 +547,10 @@ const WORKFLOW_POOL_READY_POLL_MS = 250;
|
|
|
547
547
|
const WORKFLOW_POOL_REFILL_ON_MISS_TIMEOUT_MS = 2_500;
|
|
548
548
|
const WORKFLOW_POOL_REFILL_ON_MISS_MIN_AVAILABLE = 4;
|
|
549
549
|
const WORKFLOW_POOL_CONTROL_TIMEOUT_MS = 750;
|
|
550
|
+
const WORKFLOW_POOL_START_ACK_TIMEOUT_MS = 750;
|
|
551
|
+
const WORKFLOW_POOL_START_ACK_POLL_MS = 25;
|
|
550
552
|
const SUBMIT_INITIAL_STATE_MAX_WAIT_MS = 0;
|
|
551
553
|
const SUBMIT_INITIAL_STATE_POLL_MS = 50;
|
|
552
|
-
const WORKFLOW_POOL_DISABLED_REASON =
|
|
553
|
-
'Cloudflare Workflows start runs directly; waitForEvent is reserved for real durable external waits.';
|
|
554
554
|
function buildDynamicWorkflowMetadata(
|
|
555
555
|
params: PlayWorkflowParams,
|
|
556
556
|
): DynamicWorkflowMetadata {
|
|
@@ -623,7 +623,7 @@ function readWorkflowTraceContext(event: unknown): {
|
|
|
623
623
|
}
|
|
624
624
|
|
|
625
625
|
function workflowPoolEnabled(): boolean {
|
|
626
|
-
return
|
|
626
|
+
return WORKFLOW_POOL_TARGET_SIZE > 0;
|
|
627
627
|
}
|
|
628
628
|
|
|
629
629
|
function workflowPoolTargetSize(): number {
|
|
@@ -889,8 +889,33 @@ async function mapRunToWorkflowInstance(input: {
|
|
|
889
889
|
env: CoordinatorEnv;
|
|
890
890
|
runId: string;
|
|
891
891
|
instanceId: string;
|
|
892
|
-
|
|
893
|
-
|
|
892
|
+
started?: boolean;
|
|
893
|
+
}): Promise<boolean> {
|
|
894
|
+
const body = await callWorkflowPool<{ mapped?: unknown }>(
|
|
895
|
+
input.env,
|
|
896
|
+
'/pool-map-run',
|
|
897
|
+
{
|
|
898
|
+
method: 'POST',
|
|
899
|
+
body: JSON.stringify({
|
|
900
|
+
runId: input.runId,
|
|
901
|
+
instanceId: input.instanceId,
|
|
902
|
+
started: input.started === true,
|
|
903
|
+
version: WORKFLOW_POOL_PROTOCOL_VERSION,
|
|
904
|
+
}),
|
|
905
|
+
},
|
|
906
|
+
);
|
|
907
|
+
return body.mapped !== false;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
async function blockWorkflowPoolRun(input: {
|
|
911
|
+
env: CoordinatorEnv;
|
|
912
|
+
runId: string;
|
|
913
|
+
instanceId: string;
|
|
914
|
+
}): Promise<{ blocked: boolean; started: boolean }> {
|
|
915
|
+
const body = await callWorkflowPool<{
|
|
916
|
+
blocked?: unknown;
|
|
917
|
+
started?: unknown;
|
|
918
|
+
}>(input.env, '/pool-block-run', {
|
|
894
919
|
method: 'POST',
|
|
895
920
|
body: JSON.stringify({
|
|
896
921
|
runId: input.runId,
|
|
@@ -898,6 +923,82 @@ async function mapRunToWorkflowInstance(input: {
|
|
|
898
923
|
version: WORKFLOW_POOL_PROTOCOL_VERSION,
|
|
899
924
|
}),
|
|
900
925
|
});
|
|
926
|
+
return {
|
|
927
|
+
blocked: body.blocked === true,
|
|
928
|
+
started: body.started === true,
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
async function readWorkflowPoolRunMapping(input: {
|
|
933
|
+
env: CoordinatorEnv;
|
|
934
|
+
runId: string;
|
|
935
|
+
}): Promise<{ instanceId: string | null; startedAt: number | null }> {
|
|
936
|
+
const body = await callWorkflowPool<{
|
|
937
|
+
instanceId?: unknown;
|
|
938
|
+
startedAt?: unknown;
|
|
939
|
+
}>(
|
|
940
|
+
input.env,
|
|
941
|
+
`/pool-resolve-run?runId=${encodeURIComponent(input.runId)}&version=${encodeURIComponent(
|
|
942
|
+
WORKFLOW_POOL_PROTOCOL_VERSION,
|
|
943
|
+
)}`,
|
|
944
|
+
).catch(() => ({ instanceId: null, startedAt: null }));
|
|
945
|
+
return {
|
|
946
|
+
instanceId:
|
|
947
|
+
typeof body.instanceId === 'string' && body.instanceId
|
|
948
|
+
? body.instanceId
|
|
949
|
+
: null,
|
|
950
|
+
startedAt:
|
|
951
|
+
typeof body.startedAt === 'number' && Number.isFinite(body.startedAt)
|
|
952
|
+
? body.startedAt
|
|
953
|
+
: null,
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
async function waitForWorkflowPoolStartAck(input: {
|
|
958
|
+
env: CoordinatorEnv;
|
|
959
|
+
runId: string;
|
|
960
|
+
instanceId: string;
|
|
961
|
+
timeoutMs: number;
|
|
962
|
+
}): Promise<{
|
|
963
|
+
acknowledged: boolean;
|
|
964
|
+
ms: number;
|
|
965
|
+
polls: number;
|
|
966
|
+
startedAt: number | null;
|
|
967
|
+
mappedInstanceId: string | null;
|
|
968
|
+
}> {
|
|
969
|
+
const startedAt = Date.now();
|
|
970
|
+
let polls = 0;
|
|
971
|
+
let latestMapping: { instanceId: string | null; startedAt: number | null } = {
|
|
972
|
+
instanceId: null,
|
|
973
|
+
startedAt: null,
|
|
974
|
+
};
|
|
975
|
+
while (Date.now() - startedAt < input.timeoutMs) {
|
|
976
|
+
polls += 1;
|
|
977
|
+
latestMapping = await readWorkflowPoolRunMapping({
|
|
978
|
+
env: input.env,
|
|
979
|
+
runId: input.runId,
|
|
980
|
+
});
|
|
981
|
+
if (
|
|
982
|
+
latestMapping.instanceId === input.instanceId &&
|
|
983
|
+
latestMapping.startedAt !== null
|
|
984
|
+
) {
|
|
985
|
+
return {
|
|
986
|
+
acknowledged: true,
|
|
987
|
+
ms: Date.now() - startedAt,
|
|
988
|
+
polls,
|
|
989
|
+
startedAt: latestMapping.startedAt,
|
|
990
|
+
mappedInstanceId: latestMapping.instanceId,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
await sleep(WORKFLOW_POOL_START_ACK_POLL_MS);
|
|
994
|
+
}
|
|
995
|
+
return {
|
|
996
|
+
acknowledged: false,
|
|
997
|
+
ms: Date.now() - startedAt,
|
|
998
|
+
polls,
|
|
999
|
+
startedAt: latestMapping.startedAt,
|
|
1000
|
+
mappedInstanceId: latestMapping.instanceId,
|
|
1001
|
+
};
|
|
901
1002
|
}
|
|
902
1003
|
|
|
903
1004
|
async function resolveWorkflowInstanceIdForRun(
|
|
@@ -907,15 +1008,8 @@ async function resolveWorkflowInstanceIdForRun(
|
|
|
907
1008
|
if (!workflowPoolEnabled()) {
|
|
908
1009
|
return workflowInstanceId(runId);
|
|
909
1010
|
}
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
`/pool-resolve-run?runId=${encodeURIComponent(runId)}&version=${encodeURIComponent(
|
|
913
|
-
WORKFLOW_POOL_PROTOCOL_VERSION,
|
|
914
|
-
)}`,
|
|
915
|
-
).catch(() => ({ instanceId: null }));
|
|
916
|
-
return typeof body.instanceId === 'string' && body.instanceId
|
|
917
|
-
? body.instanceId
|
|
918
|
-
: workflowInstanceId(runId);
|
|
1011
|
+
const mapping = await readWorkflowPoolRunMapping({ env, runId });
|
|
1012
|
+
return mapping.instanceId ? mapping.instanceId : workflowInstanceId(runId);
|
|
919
1013
|
}
|
|
920
1014
|
|
|
921
1015
|
async function clearWorkflowPool(env: CoordinatorEnv): Promise<number> {
|
|
@@ -927,7 +1021,10 @@ async function clearWorkflowPool(env: CoordinatorEnv): Promise<number> {
|
|
|
927
1021
|
);
|
|
928
1022
|
await Promise.all(
|
|
929
1023
|
entries.map(async (entry) => {
|
|
930
|
-
const instance = await env
|
|
1024
|
+
const instance = await getWorkflowPoolInstance(env, entry.id);
|
|
1025
|
+
if (!instance) {
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
931
1028
|
try {
|
|
932
1029
|
await instance.terminate().catch(() => undefined);
|
|
933
1030
|
} finally {
|
|
@@ -942,6 +1039,27 @@ function workflowStatusName(status: InstanceStatus | null): string {
|
|
|
942
1039
|
return typeof status?.status === 'string' ? status.status : 'unknown';
|
|
943
1040
|
}
|
|
944
1041
|
|
|
1042
|
+
function isWorkflowInstanceNotFoundError(error: unknown): boolean {
|
|
1043
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1044
|
+
return /not[ _]found|not_found|does not exist|no such instance|404/i.test(
|
|
1045
|
+
message,
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
async function getWorkflowPoolInstance(
|
|
1050
|
+
env: CoordinatorEnv,
|
|
1051
|
+
instanceId: string,
|
|
1052
|
+
): Promise<WorkflowInstance | null> {
|
|
1053
|
+
try {
|
|
1054
|
+
return await env.PLAY_WORKFLOW.get(instanceId);
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
if (isWorkflowInstanceNotFoundError(error)) {
|
|
1057
|
+
return null;
|
|
1058
|
+
}
|
|
1059
|
+
throw error;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
945
1063
|
function workflowPoolStatusIsReady(statusName: string): boolean {
|
|
946
1064
|
// This is only a liveness guard. Readiness itself comes from the pooled
|
|
947
1065
|
// Workflow calling /pool-ready after waitForEvent("play_start") has been
|
|
@@ -1024,7 +1142,11 @@ async function refillWorkflowPoolOnce(
|
|
|
1024
1142
|
const promotedIds: string[] = [];
|
|
1025
1143
|
const removedIds: string[] = [];
|
|
1026
1144
|
for (const entry of warmingEntries) {
|
|
1027
|
-
const instance = await env
|
|
1145
|
+
const instance = await getWorkflowPoolInstance(env, entry.id);
|
|
1146
|
+
if (!instance) {
|
|
1147
|
+
removedIds.push(entry.id);
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1028
1150
|
try {
|
|
1029
1151
|
if (entry.state === 'ready' && entry.readyAt !== null) {
|
|
1030
1152
|
promotedIds.push(entry.id);
|
|
@@ -1251,7 +1373,21 @@ async function submitViaPooledWorkflow(input: {
|
|
|
1251
1373
|
return null;
|
|
1252
1374
|
}
|
|
1253
1375
|
|
|
1254
|
-
const instance = await input.env
|
|
1376
|
+
const instance = await getWorkflowPoolInstance(input.env, pooledInstanceId);
|
|
1377
|
+
if (!instance) {
|
|
1378
|
+
await blockWorkflowPoolRun({
|
|
1379
|
+
env: input.env,
|
|
1380
|
+
runId: input.params.runId,
|
|
1381
|
+
instanceId: pooledInstanceId,
|
|
1382
|
+
}).catch(() => undefined);
|
|
1383
|
+
input.recordSubmitTiming({
|
|
1384
|
+
phase: 'coordinator.workflow_pool_ready_check',
|
|
1385
|
+
ms: Date.now() - leaseStartedAt,
|
|
1386
|
+
graphHash: input.params.graphHash ?? null,
|
|
1387
|
+
extra: { instanceId: pooledInstanceId, status: 'missing' },
|
|
1388
|
+
});
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1255
1391
|
const readyCheckStartedAt = Date.now();
|
|
1256
1392
|
const status = await instance.status().catch(() => null);
|
|
1257
1393
|
const statusName = workflowStatusName(status);
|
|
@@ -1262,6 +1398,11 @@ async function submitViaPooledWorkflow(input: {
|
|
|
1262
1398
|
extra: { instanceId: pooledInstanceId, status: statusName },
|
|
1263
1399
|
});
|
|
1264
1400
|
if (!workflowPoolStatusIsReady(statusName)) {
|
|
1401
|
+
await blockWorkflowPoolRun({
|
|
1402
|
+
env: input.env,
|
|
1403
|
+
runId: input.params.runId,
|
|
1404
|
+
instanceId: pooledInstanceId,
|
|
1405
|
+
}).catch(() => undefined);
|
|
1265
1406
|
await instance.terminate().catch(() => undefined);
|
|
1266
1407
|
disposeRpcStub(instance);
|
|
1267
1408
|
return null;
|
|
@@ -1273,6 +1414,11 @@ async function submitViaPooledWorkflow(input: {
|
|
|
1273
1414
|
payload: buildDispatcherEnvelope(input.params),
|
|
1274
1415
|
});
|
|
1275
1416
|
} catch (error) {
|
|
1417
|
+
await blockWorkflowPoolRun({
|
|
1418
|
+
env: input.env,
|
|
1419
|
+
runId: input.params.runId,
|
|
1420
|
+
instanceId: pooledInstanceId,
|
|
1421
|
+
}).catch(() => undefined);
|
|
1276
1422
|
disposeRpcStub(instance);
|
|
1277
1423
|
console.warn('[coordinator.workflow_pool] sendEvent failed; falling back', {
|
|
1278
1424
|
runId: input.params.runId,
|
|
@@ -1287,7 +1433,63 @@ async function submitViaPooledWorkflow(input: {
|
|
|
1287
1433
|
graphHash: input.params.graphHash ?? null,
|
|
1288
1434
|
extra: { instanceId: pooledInstanceId },
|
|
1289
1435
|
});
|
|
1290
|
-
|
|
1436
|
+
const ack = await waitForWorkflowPoolStartAck({
|
|
1437
|
+
env: input.env,
|
|
1438
|
+
runId: input.params.runId,
|
|
1439
|
+
instanceId: pooledInstanceId,
|
|
1440
|
+
timeoutMs: WORKFLOW_POOL_START_ACK_TIMEOUT_MS,
|
|
1441
|
+
});
|
|
1442
|
+
if (ack.acknowledged) {
|
|
1443
|
+
input.recordSubmitTiming({
|
|
1444
|
+
phase: 'coordinator.workflow_pool_start_ack',
|
|
1445
|
+
ms: ack.ms,
|
|
1446
|
+
graphHash: input.params.graphHash ?? null,
|
|
1447
|
+
extra: {
|
|
1448
|
+
acknowledged: true,
|
|
1449
|
+
instanceId: pooledInstanceId,
|
|
1450
|
+
polls: ack.polls,
|
|
1451
|
+
startedAt: ack.startedAt,
|
|
1452
|
+
},
|
|
1453
|
+
});
|
|
1454
|
+
return instance;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
const blockStartedAt = Date.now();
|
|
1458
|
+
const block = await blockWorkflowPoolRun({
|
|
1459
|
+
env: input.env,
|
|
1460
|
+
runId: input.params.runId,
|
|
1461
|
+
instanceId: pooledInstanceId,
|
|
1462
|
+
}).catch(() => ({ blocked: false, started: false }));
|
|
1463
|
+
input.recordSubmitTiming({
|
|
1464
|
+
phase: 'coordinator.workflow_pool_start_ack',
|
|
1465
|
+
ms: ack.ms,
|
|
1466
|
+
graphHash: input.params.graphHash ?? null,
|
|
1467
|
+
extra: {
|
|
1468
|
+
acknowledged: block.started,
|
|
1469
|
+
instanceId: pooledInstanceId,
|
|
1470
|
+
polls: ack.polls,
|
|
1471
|
+
startedAt: ack.startedAt,
|
|
1472
|
+
mappedInstanceId: ack.mappedInstanceId,
|
|
1473
|
+
blocked: block.blocked,
|
|
1474
|
+
blockMs: Date.now() - blockStartedAt,
|
|
1475
|
+
},
|
|
1476
|
+
});
|
|
1477
|
+
if (block.started) {
|
|
1478
|
+
return instance;
|
|
1479
|
+
}
|
|
1480
|
+
await instance.terminate().catch(() => undefined);
|
|
1481
|
+
disposeRpcStub(instance);
|
|
1482
|
+
input.recordSubmitTiming({
|
|
1483
|
+
phase: 'coordinator.workflow_pool_fallback',
|
|
1484
|
+
ms: Date.now() - sendStartedAt,
|
|
1485
|
+
graphHash: input.params.graphHash ?? null,
|
|
1486
|
+
extra: {
|
|
1487
|
+
reason: 'start_ack_timeout',
|
|
1488
|
+
instanceId: pooledInstanceId,
|
|
1489
|
+
ackTimeoutMs: WORKFLOW_POOL_START_ACK_TIMEOUT_MS,
|
|
1490
|
+
},
|
|
1491
|
+
});
|
|
1492
|
+
return null;
|
|
1291
1493
|
}
|
|
1292
1494
|
|
|
1293
1495
|
function readWorkflowPayload(event: unknown): Record<string, unknown> | null {
|
|
@@ -2186,9 +2388,35 @@ export class DynamicWorkflow extends WorkflowEntrypoint<
|
|
|
2186
2388
|
dispatchedEvent = {
|
|
2187
2389
|
payload: startEvent.payload,
|
|
2188
2390
|
timestamp: startEvent.timestamp,
|
|
2189
|
-
instanceId: workflowEvent.instanceId,
|
|
2391
|
+
instanceId: workflowEvent.instanceId ?? pooledPayload.poolId,
|
|
2190
2392
|
};
|
|
2191
2393
|
const dispatchedTrace = readWorkflowTraceContext(dispatchedEvent);
|
|
2394
|
+
const mapped = await mapRunToWorkflowInstance({
|
|
2395
|
+
env: this.env,
|
|
2396
|
+
runId: dispatchedTrace.runId,
|
|
2397
|
+
instanceId: pooledPayload.poolId,
|
|
2398
|
+
started: true,
|
|
2399
|
+
}).catch((error) => {
|
|
2400
|
+
console.warn('[coordinator.workflow_pool] start ack failed', {
|
|
2401
|
+
poolId: pooledPayload.poolId,
|
|
2402
|
+
runId: dispatchedTrace.runId,
|
|
2403
|
+
message: error instanceof Error ? error.message : String(error),
|
|
2404
|
+
});
|
|
2405
|
+
return false;
|
|
2406
|
+
});
|
|
2407
|
+
if (!mapped) {
|
|
2408
|
+
trace({
|
|
2409
|
+
runId: dispatchedTrace.runId,
|
|
2410
|
+
phase: 'coordinator.workflow_pool_start_blocked',
|
|
2411
|
+
ms: 0,
|
|
2412
|
+
graphHash: dispatchedTrace.graphHash,
|
|
2413
|
+
extra: {
|
|
2414
|
+
instanceId: pooledPayload.poolId,
|
|
2415
|
+
eventType: startEvent.type,
|
|
2416
|
+
},
|
|
2417
|
+
});
|
|
2418
|
+
return { ok: false, blocked: true, runId: dispatchedTrace.runId };
|
|
2419
|
+
}
|
|
2192
2420
|
const eventDeliveryMs = Math.max(
|
|
2193
2421
|
0,
|
|
2194
2422
|
Date.now() - startEvent.timestamp.getTime(),
|
|
@@ -2472,7 +2700,15 @@ const coordinatorEntrypoint = {
|
|
|
2472
2700
|
const entries = await listWorkflowPoolEntries(env);
|
|
2473
2701
|
const detailed = [];
|
|
2474
2702
|
for (const entry of entries) {
|
|
2475
|
-
const instance = await env
|
|
2703
|
+
const instance = await getWorkflowPoolInstance(env, entry.id);
|
|
2704
|
+
if (!instance) {
|
|
2705
|
+
detailed.push({
|
|
2706
|
+
...entry,
|
|
2707
|
+
status: 'missing',
|
|
2708
|
+
mappedStatus: 'failed',
|
|
2709
|
+
});
|
|
2710
|
+
continue;
|
|
2711
|
+
}
|
|
2476
2712
|
try {
|
|
2477
2713
|
const status = await instance.status().catch(() => null);
|
|
2478
2714
|
detailed.push({
|
|
@@ -2536,6 +2772,14 @@ const coordinatorEntrypoint = {
|
|
|
2536
2772
|
async tail(events: unknown[], env: CoordinatorEnv): Promise<void> {
|
|
2537
2773
|
await flushTailRunLogs(events, env);
|
|
2538
2774
|
},
|
|
2775
|
+
async scheduled(
|
|
2776
|
+
_controller: unknown,
|
|
2777
|
+
env: CoordinatorEnv,
|
|
2778
|
+
ctx?: ExecutionContext,
|
|
2779
|
+
): Promise<void> {
|
|
2780
|
+
if (!workflowPoolEnabled()) return;
|
|
2781
|
+
ctx?.waitUntil(refillWorkflowPool(env).catch(() => undefined));
|
|
2782
|
+
},
|
|
2539
2783
|
};
|
|
2540
2784
|
|
|
2541
2785
|
export default coordinatorEntrypoint;
|
|
@@ -2698,36 +2942,100 @@ async function handleWorkflowRoute(input: {
|
|
|
2698
2942
|
'Start apps/play-harness-worker before the coordinator or fix wrangler.toml services.',
|
|
2699
2943
|
);
|
|
2700
2944
|
}
|
|
2945
|
+
const preloadedDbSessions = Array.isArray(params.preloadedDbSessions)
|
|
2946
|
+
? params.preloadedDbSessions
|
|
2947
|
+
: [];
|
|
2948
|
+
if (preloadedDbSessions.length > 0 && params.executorToken) {
|
|
2949
|
+
const prewarmStartedAt = Date.now();
|
|
2950
|
+
recordSubmitTiming({
|
|
2951
|
+
phase: 'coordinator.harness_prewarm_postgres_start',
|
|
2952
|
+
ms: 0,
|
|
2953
|
+
graphHash: params.graphHash ?? null,
|
|
2954
|
+
extra: { sessions: preloadedDbSessions.length },
|
|
2955
|
+
});
|
|
2956
|
+
const prewarmPromise = env.HARNESS.prewarmPostgresSessions({
|
|
2957
|
+
executorToken: params.executorToken,
|
|
2958
|
+
sessions: preloadedDbSessions,
|
|
2959
|
+
})
|
|
2960
|
+
.then((result) => {
|
|
2961
|
+
recordSubmitTiming({
|
|
2962
|
+
phase: 'coordinator.harness_prewarm_postgres',
|
|
2963
|
+
ms: Date.now() - prewarmStartedAt,
|
|
2964
|
+
graphHash: params.graphHash ?? null,
|
|
2965
|
+
extra: {
|
|
2966
|
+
status: 'ok',
|
|
2967
|
+
sessions: result.sessions,
|
|
2968
|
+
},
|
|
2969
|
+
});
|
|
2970
|
+
})
|
|
2971
|
+
.catch((error: unknown) => {
|
|
2972
|
+
recordSubmitTiming({
|
|
2973
|
+
phase: 'coordinator.harness_prewarm_postgres',
|
|
2974
|
+
ms: Date.now() - prewarmStartedAt,
|
|
2975
|
+
graphHash: params.graphHash ?? null,
|
|
2976
|
+
extra: {
|
|
2977
|
+
status: 'failed',
|
|
2978
|
+
sessions: preloadedDbSessions.length,
|
|
2979
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2980
|
+
},
|
|
2981
|
+
});
|
|
2982
|
+
});
|
|
2983
|
+
input.ctx?.waitUntil(prewarmPromise);
|
|
2984
|
+
}
|
|
2701
2985
|
let instance: WorkflowInstance | null = null;
|
|
2702
2986
|
try {
|
|
2703
|
-
const
|
|
2987
|
+
const statusEventStartedAt = Date.now();
|
|
2988
|
+
await appendCoordinatorRunEvent(env, {
|
|
2989
|
+
runId: submittedRunId,
|
|
2990
|
+
type: 'status',
|
|
2991
|
+
status: 'running',
|
|
2992
|
+
ts: Date.now(),
|
|
2993
|
+
});
|
|
2704
2994
|
recordSubmitTiming({
|
|
2705
|
-
phase: 'coordinator.
|
|
2706
|
-
ms:
|
|
2995
|
+
phase: 'coordinator.submit_status_event',
|
|
2996
|
+
ms: Date.now() - statusEventStartedAt,
|
|
2707
2997
|
graphHash: params.graphHash ?? null,
|
|
2708
|
-
extra: {
|
|
2709
|
-
usedPool: false,
|
|
2710
|
-
disabled: true,
|
|
2711
|
-
reason: WORKFLOW_POOL_DISABLED_REASON,
|
|
2712
|
-
},
|
|
2713
2998
|
});
|
|
2714
|
-
const
|
|
2715
|
-
|
|
2999
|
+
const dispatchStartedAt = Date.now();
|
|
3000
|
+
const poolAttemptStartedAt = Date.now();
|
|
3001
|
+
instance = await submitViaPooledWorkflow({
|
|
2716
3002
|
env,
|
|
2717
|
-
id: defaultInstanceId,
|
|
2718
3003
|
params,
|
|
3004
|
+
recordSubmitTiming,
|
|
2719
3005
|
});
|
|
2720
3006
|
recordSubmitTiming({
|
|
2721
|
-
phase: 'coordinator.
|
|
2722
|
-
ms: Date.now() -
|
|
3007
|
+
phase: 'coordinator.workflow_pool_attempt',
|
|
3008
|
+
ms: Date.now() - poolAttemptStartedAt,
|
|
2723
3009
|
graphHash: params.graphHash ?? null,
|
|
2724
|
-
extra: {
|
|
3010
|
+
extra: {
|
|
3011
|
+
usedPool: Boolean(instance),
|
|
3012
|
+
enabled: workflowPoolEnabled(),
|
|
3013
|
+
},
|
|
2725
3014
|
});
|
|
3015
|
+
if (!instance) {
|
|
3016
|
+
const createStartedAt = Date.now();
|
|
3017
|
+
instance = await createDynamicWorkflowInstance({
|
|
3018
|
+
env,
|
|
3019
|
+
id: defaultInstanceId,
|
|
3020
|
+
params,
|
|
3021
|
+
});
|
|
3022
|
+
recordSubmitTiming({
|
|
3023
|
+
phase: 'coordinator.workflow_create',
|
|
3024
|
+
ms: Date.now() - createStartedAt,
|
|
3025
|
+
graphHash: params.graphHash ?? null,
|
|
3026
|
+
extra: { instanceId: instance.id },
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
2726
3029
|
recordSubmitTiming({
|
|
2727
3030
|
phase: 'coordinator.dispatch_workflow',
|
|
2728
3031
|
ms: Date.now() - dispatchStartedAt,
|
|
2729
3032
|
graphHash: params.graphHash ?? null,
|
|
2730
|
-
extra: {
|
|
3033
|
+
extra: {
|
|
3034
|
+
startMode:
|
|
3035
|
+
instance.id === defaultInstanceId
|
|
3036
|
+
? 'direct_workflow_create'
|
|
3037
|
+
: 'pooled_workflow_start_event',
|
|
3038
|
+
},
|
|
2731
3039
|
});
|
|
2732
3040
|
const initialWaitMsRaw = Number(
|
|
2733
3041
|
new URL(request.url).searchParams.get('initialWaitMs') ?? '0',
|
|
@@ -2761,6 +3069,9 @@ async function handleWorkflowRoute(input: {
|
|
|
2761
3069
|
ms: totalMs,
|
|
2762
3070
|
graphHash: params.graphHash ?? null,
|
|
2763
3071
|
});
|
|
3072
|
+
if (workflowPoolEnabled() && instance.id === defaultInstanceId) {
|
|
3073
|
+
input.ctx?.waitUntil(refillWorkflowPool(env).catch(() => undefined));
|
|
3074
|
+
}
|
|
2764
3075
|
return Response.json({
|
|
2765
3076
|
runId,
|
|
2766
3077
|
status: 'submitted',
|
|
@@ -3016,11 +3327,19 @@ async function handleWorkflowRoute(input: {
|
|
|
3016
3327
|
afterSeq,
|
|
3017
3328
|
timeoutMs: waitMs,
|
|
3018
3329
|
}).catch(() => null);
|
|
3330
|
+
const rawEvents = eventResult?.events ?? [];
|
|
3331
|
+
const terminalEventIndex = rawEvents.findIndex(
|
|
3332
|
+
(event) => event.type === 'terminal',
|
|
3333
|
+
);
|
|
3334
|
+
const events =
|
|
3335
|
+
terminalEventIndex >= 0
|
|
3336
|
+
? rawEvents.slice(0, terminalEventIndex + 1)
|
|
3337
|
+
: rawEvents;
|
|
3019
3338
|
const coordinatorTrace =
|
|
3020
|
-
includeTrace &&
|
|
3339
|
+
includeTrace && events.length
|
|
3021
3340
|
? await listCoordinatorPerfTrace(env, runId).catch(() => [])
|
|
3022
3341
|
: [];
|
|
3023
|
-
const terminalEvent =
|
|
3342
|
+
const terminalEvent = events.find(
|
|
3024
3343
|
(event): event is Extract<CoordinatorRunEvent, { type: 'terminal' }> =>
|
|
3025
3344
|
event.type === 'terminal',
|
|
3026
3345
|
);
|
|
@@ -3036,7 +3355,7 @@ async function handleWorkflowRoute(input: {
|
|
|
3036
3355
|
completedAt: terminalEvent.ts,
|
|
3037
3356
|
liveLogs: sanitizeLiveLogLines(terminalEvent.liveLogs),
|
|
3038
3357
|
liveNodeProgress: terminalEvent.liveNodeProgress ?? null,
|
|
3039
|
-
events
|
|
3358
|
+
events,
|
|
3040
3359
|
latestSeq: eventResult?.latestSeq ?? afterSeq,
|
|
3041
3360
|
wait: null,
|
|
3042
3361
|
coordinatorObserve: {
|
|
@@ -3052,7 +3371,7 @@ async function handleWorkflowRoute(input: {
|
|
|
3052
3371
|
return Response.json({
|
|
3053
3372
|
runId,
|
|
3054
3373
|
status: 'running',
|
|
3055
|
-
events
|
|
3374
|
+
events,
|
|
3056
3375
|
latestSeq: eventResult?.latestSeq ?? afterSeq,
|
|
3057
3376
|
wait: null,
|
|
3058
3377
|
coordinatorObserve: {
|
|
@@ -3250,7 +3569,7 @@ function stableHash(value: string): string {
|
|
|
3250
3569
|
return (hash >>> 0).toString(36);
|
|
3251
3570
|
}
|
|
3252
3571
|
|
|
3253
|
-
const DYNAMIC_PLAY_WORKER_HARNESS_VERSION = '
|
|
3572
|
+
const DYNAMIC_PLAY_WORKER_HARNESS_VERSION = 'h16-coordinator-only-prewarm';
|
|
3254
3573
|
const DYNAMIC_WORKER_BUNDLED_CODE_CACHE_MAX_ENTRIES = 64;
|
|
3255
3574
|
const dynamicWorkerBundledCodeCache = new Map<string, string>();
|
|
3256
3575
|
|