deepline 0.3.391 → 0.3.393
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 +156 -24
- package/dist/bundling-sources/sdk/src/compat.ts +9 -0
- package/dist/bundling-sources/sdk/src/http.ts +5 -1
- package/dist/bundling-sources/sdk/src/index.ts +5 -0
- package/dist/bundling-sources/sdk/src/plays/csv-data-input.ts +3 -0
- package/dist/bundling-sources/sdk/src/plays/run-request.ts +182 -6
- package/dist/bundling-sources/sdk/src/release.ts +4 -1
- package/dist/bundling-sources/sdk/src/types.ts +15 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/request-warnings.ts +64 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/runtime-sheet-storage-contract.ts +315 -0
- package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +41 -8
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +418 -44
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +36 -3
- package/dist/bundling-sources/shared_libs/play-runtime/durable-invocation-fence.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +11 -3
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/gateway-adapter.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +19 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-identity.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +158 -10
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +235 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +92 -58
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +7 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +30 -7
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1161 -316
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +28 -15
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +24 -3
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +31 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +37 -4
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +172 -79
- package/dist/bundling-sources/shared_libs/plays/extension-bridge.ts +57 -0
- package/dist/bundling-sources/shared_libs/plays/run-summary-types.ts +1 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +173 -1
- package/dist/cli/{auth-6QET7NFP.mjs → auth-5PXHA232.mjs} +4 -4
- package/dist/cli/auth-P3MVDTXM.js +39 -0
- package/dist/cli/{billing-KJAGTCFR.js → billing-JFB2XLSO.js} +101 -74
- package/dist/cli/{billing-HAQ2VDQX.mjs → billing-Z2N7ZAGM.mjs} +32 -5
- package/dist/cli/{chunk-556BMGEB.js → chunk-2HAD745V.js} +242 -183
- package/dist/cli/{chunk-HFTOOZGN.mjs → chunk-2LGC4EFT.mjs} +3 -3
- package/dist/cli/{chunk-QCWIORKR.mjs → chunk-4PT2VA3A.mjs} +2 -2
- package/dist/cli/{chunk-KA7X75HC.js → chunk-4XX6443T.js} +176 -87
- package/dist/cli/{chunk-YBZY3EZF.mjs → chunk-5KWKZIQR.mjs} +4 -4
- package/dist/cli/{chunk-VH4B5OYM.mjs → chunk-5PRVMXKB.mjs} +27 -2
- package/dist/cli/{chunk-DSWSEHRX.mjs → chunk-6I73X254.mjs} +1 -1
- package/dist/cli/{chunk-6L4P7Z6C.mjs → chunk-7HR7BGZL.mjs} +13 -3
- package/dist/cli/{chunk-4S6X74A3.js → chunk-AARX5L4B.js} +2 -2
- package/dist/cli/{chunk-MHLCMJ2E.js → chunk-CDFH2DYL.js} +20 -19
- package/dist/cli/{chunk-PFPP4BKS.mjs → chunk-FQ6VHPW4.mjs} +2 -2
- package/dist/cli/{chunk-5JLQPVNX.js → chunk-GSK6NHVM.js} +1 -0
- package/dist/cli/{chunk-TLHF54VX.mjs → chunk-HLEESTCH.mjs} +1 -1
- package/dist/cli/{chunk-EDWJY6T4.mjs → chunk-HQXL5526.mjs} +1 -0
- package/dist/cli/{chunk-Q6EOHNRH.mjs → chunk-JI2B4RZP.mjs} +119 -6
- package/dist/cli/{chunk-I3MKXASV.mjs → chunk-KIYU2TGL.mjs} +2 -2
- package/dist/cli/{chunk-Y6VOGHF2.mjs → chunk-KZZC33PY.mjs} +0 -181
- package/dist/cli/{chunk-XBPLTHGB.js → chunk-L2SYLGRP.js} +8 -8
- package/dist/cli/{chunk-K3ODOHOI.mjs → chunk-LMMEAMA7.mjs} +3 -2
- package/dist/cli/{chunk-KWF4HJF7.mjs → chunk-LZPCDB7U.mjs} +3 -2
- package/dist/cli/{chunk-I7EVBLTW.js → chunk-M7Y7JYZX.js} +32 -7
- package/dist/cli/{chunk-QGKPTBRR.js → chunk-MS6GUPBB.js} +3 -3
- package/dist/cli/{chunk-ME2DZAHC.js → chunk-OXNOAD6Y.js} +26 -26
- package/dist/cli/{chunk-XEV5LMS2.js → chunk-PTDECJGP.js} +50 -50
- package/dist/cli/{chunk-5O4B6AUN.js → chunk-PWSWAN7Q.js} +25 -2
- package/dist/cli/{chunk-DRZFTRZJ.mjs → chunk-Q26LUSQ2.mjs} +79 -20
- package/dist/cli/{chunk-3VN4FTE3.js → chunk-RDXFGPJU.js} +1 -182
- package/dist/cli/{chunk-7E66A24V.mjs → chunk-RKMB3N7K.mjs} +25 -2
- package/dist/cli/{chunk-WLODHQ7S.js → chunk-RWHWF5ZJ.js} +3 -2
- package/dist/cli/{chunk-H3DCZOXR.mjs → chunk-STNYIMIV.mjs} +97 -8
- package/dist/cli/chunk-UBKA33KY.mjs +310 -0
- package/dist/cli/{chunk-PAIMOGCM.js → chunk-UHNDVD3P.js} +125 -12
- package/dist/cli/{chunk-H2ER7QWR.mjs → chunk-UOGZUA24.mjs} +180 -18
- package/dist/cli/{chunk-RTH26NF3.js → chunk-VIQJIX2P.js} +73 -63
- package/dist/cli/{chunk-ED42XCOW.js → chunk-VIWE6WRK.js} +629 -467
- package/dist/cli/{chunk-RA2SDTSX.js → chunk-W4WOMXR3.js} +15 -15
- package/dist/cli/chunk-XDVT3GOM.js +310 -0
- package/dist/cli/{chunk-UOSKJIXE.js → chunk-Z3OOZM5L.js} +8 -8
- package/dist/cli/{csv-V7UHOBFE.mjs → csv-KJWZHKCH.mjs} +3 -3
- package/dist/cli/{csv-2WSF5TEM.js → csv-P3Z3YE7H.js} +12 -12
- package/dist/cli/{db-TD375E4N.mjs → db-NCM4CQIS.mjs} +6 -5
- package/dist/cli/{db-42RL4FZD.js → db-NUZGJNQC.js} +31 -30
- package/dist/cli/{describe-GPUKAWOZ.mjs → describe-WT6MM2GO.mjs} +18 -17
- package/dist/cli/{describe-7WFCBZUY.js → describe-YGSEN5WC.js} +25 -24
- package/dist/cli/{executions-LGVG3FPH.js → executions-PCOLHN7Q.js} +8 -7
- package/dist/cli/{executions-M7O63JNA.mjs → executions-XS2IPJ4W.mjs} +6 -5
- package/dist/cli/{feedback-PZZA2M4S.js → feedback-4MSH7TFU.js} +7 -7
- package/dist/cli/{feedback-6PBSKVB2.mjs → feedback-CCLTS5SV.mjs} +3 -3
- package/dist/cli/{git-2VIFM5DI.mjs → git-K2DHPMPC.mjs} +4 -4
- package/dist/cli/git-RNM45FX4.js +23 -0
- package/dist/cli/index.js +96 -92
- package/dist/cli/index.mjs +46 -42
- package/dist/cli/{legacy-noops-DPUBZI7N.mjs → legacy-noops-B5S4AR3P.mjs} +4 -4
- package/dist/cli/{legacy-noops-IQ6QTZNZ.js → legacy-noops-GPNOPFXV.js} +7 -7
- package/dist/cli/{monitors-KBZVQG2U.mjs → monitors-JLMAIPRG.mjs} +6 -5
- package/dist/cli/{monitors-6GWAZFYV.js → monitors-ZAWD7NSN.js} +87 -86
- package/dist/cli/{org-JKGTHXOZ.js → org-P2KN4CES.js} +66 -65
- package/dist/cli/{org-CP3VSJBX.mjs → org-T3JTHP4G.mjs} +6 -5
- package/dist/cli/{play-DD2ME4L4.mjs → play-NK6BLRGL.mjs} +12 -11
- package/dist/cli/play-PDXR6JFB.js +158 -0
- package/dist/cli/{providers-JUNOFKI7.mjs → providers-3NQRA7IT.mjs} +6 -5
- package/dist/cli/{providers-P34QYYG6.js → providers-5QRZN3DT.js} +9 -8
- package/dist/cli/{quickstart-N6SWOK7E.mjs → quickstart-PIA2JGPV.mjs} +3 -3
- package/dist/cli/{quickstart-UL3DEFA4.js → quickstart-R7NUZGNW.js} +5 -5
- package/dist/cli/run-identity-G257GQED.js +25 -0
- package/dist/cli/run-identity-MRNCEGV2.mjs +25 -0
- package/dist/cli/{search-SC7I56HX.mjs → search-AIDIIGLY.mjs} +7 -6
- package/dist/cli/search-QRWL7MFM.js +27 -0
- package/dist/cli/{secrets-6ZJE7ZPM.js → secrets-BT3PGMWN.js} +15 -14
- package/dist/cli/{secrets-RTARNYF5.mjs → secrets-OQNHL45O.mjs} +6 -5
- package/dist/cli/{sessions-6IZ2XHGW.js → sessions-EYXWOSWK.js} +5 -5
- package/dist/cli/{sessions-7RKV2T3O.mjs → sessions-SF7WJPJP.mjs} +4 -4
- package/dist/cli/{settings-YX3XJDXE.js → settings-BA3GHBOD.js} +76 -75
- package/dist/cli/{settings-V6PXA6HY.mjs → settings-Y57JTFDD.mjs} +9 -8
- package/dist/cli/{setup-V2O4UJNT.mjs → setup-7GR5GBHA.mjs} +6 -6
- package/dist/cli/{setup-CQ5VRNNU.js → setup-RERXYC66.js} +41 -41
- package/dist/cli/{skills-VHJZWEHA.mjs → skills-SJWIQAV5.mjs} +4 -4
- package/dist/cli/skills-V7EY75HB.js +28 -0
- package/dist/cli/tools-H5HLBCHH.js +41 -0
- package/dist/cli/{tools-OD5WKEWY.mjs → tools-ZF3JXPNL.mjs} +10 -9
- package/dist/cli/update-472HA3LT.js +28 -0
- package/dist/cli/{update-CRMNOMJ3.mjs → update-FFFYQAOJ.mjs} +6 -6
- package/dist/cli/{workflow-XHORSD5T.js → workflow-SZJVQH4T.js} +17 -16
- package/dist/cli/{workflow-EVDYNCDD.mjs → workflow-XVFEEGAS.mjs} +7 -6
- package/dist/{client-DpzZi4fU.d.mts → client-DU5dtHDy.d.mts} +157 -4
- package/dist/{client-BhK9kix9.d.ts → client-mCE88hC4.d.ts} +157 -4
- package/dist/{compiler-manifest-CY2v8ab0.d.ts → compiler-manifest-DE4RdAiY.d.mts} +61 -1
- package/dist/{compiler-manifest-CY2v8ab0.d.mts → compiler-manifest-DE4RdAiY.d.ts} +61 -1
- package/dist/{index-B9fBZH1g.d.mts → index-BI6nWgaV.d.mts} +1 -1
- package/dist/{index-BZ6uzZJl.d.ts → index-Bi-f7HFT.d.ts} +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +120 -29
- package/dist/index.mjs +111 -20
- package/dist/install-integrity.json +94 -88
- package/dist/plays/bundle-play-file.d.mts +3 -3
- package/dist/plays/bundle-play-file.d.ts +3 -3
- package/dist/plays/bundle-play-file.mjs +2 -2
- package/dist/plays/csv-data-input.d.mts +2 -0
- package/dist/plays/csv-data-input.d.ts +2 -0
- package/dist/plays/csv-data-input.js +2 -2
- package/dist/plays/csv-data-input.mjs +1 -1
- package/dist/plays/run-request.d.mts +2 -2
- package/dist/plays/run-request.d.ts +2 -2
- package/dist/plays/run-request.js +5 -2
- package/dist/plays/run-request.mjs +4 -1
- package/dist/release.d.mts +4 -1
- package/dist/release.d.ts +4 -1
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli/auth-6ZUH42ZA.js +0 -39
- package/dist/cli/git-ZSJD3BID.js +0 -23
- package/dist/cli/play-G5GVMIBN.js +0 -157
- package/dist/cli/search-W4GXWTOM.js +0 -26
- package/dist/cli/skills-LBZPJTPI.js +0 -28
- package/dist/cli/tools-TQNM473H.js +0 -40
- package/dist/cli/update-QY6GAK6B.js +0 -28
|
@@ -46,6 +46,11 @@ import {
|
|
|
46
46
|
} from '../play-data-plane/sheet-column-storage';
|
|
47
47
|
import {
|
|
48
48
|
RUNTIME_SHEET_CELL_META_COLUMN,
|
|
49
|
+
RUNTIME_SHEET_RUN_MEMBERSHIP_TABLE,
|
|
50
|
+
RUNTIME_SHEET_RUN_MEMBERSHIP_WRITES_ENABLED,
|
|
51
|
+
appendRuntimeSheetRunRowMembershipBatch,
|
|
52
|
+
isRuntimeSheetRunMembershipStorageAvailable,
|
|
53
|
+
type RuntimeSheetRunRowMembership,
|
|
49
54
|
runtimeSheetResultFreshnessSql,
|
|
50
55
|
runtimeSheetResultAdmissionSql,
|
|
51
56
|
} from '../play-data-plane/runtime-sheet-storage-contract';
|
|
@@ -66,6 +71,7 @@ import type {
|
|
|
66
71
|
} from './db-session';
|
|
67
72
|
import {
|
|
68
73
|
derivePlayRowIdentity,
|
|
74
|
+
stableStringify,
|
|
69
75
|
normalizePlayNameForSheet,
|
|
70
76
|
normalizeTableNamespace,
|
|
71
77
|
} from '../plays/row-identity';
|
|
@@ -503,6 +509,8 @@ export type PrepareRuntimeSheetResult = {
|
|
|
503
509
|
attemptExpiresAt?: string;
|
|
504
510
|
attemptSeq?: number;
|
|
505
511
|
writeVersion?: number;
|
|
512
|
+
/** Optional reader-first capture capability; absent retains legacy behavior. */
|
|
513
|
+
membershipCapture?: boolean;
|
|
506
514
|
};
|
|
507
515
|
|
|
508
516
|
export type RuntimeSheetTiming = {
|
|
@@ -645,6 +653,10 @@ type RuntimeApiActionRequest =
|
|
|
645
653
|
action: keyof typeof RUNTIME_SHEET_ADMISSION_CAPABILITIES;
|
|
646
654
|
input: Parameters<typeof startRuntimeSheetDataset>[1];
|
|
647
655
|
}
|
|
656
|
+
| {
|
|
657
|
+
action: 'runtime_sheet_finish_membership';
|
|
658
|
+
input: Parameters<typeof finishRuntimeSheetRunMembership>[1];
|
|
659
|
+
}
|
|
648
660
|
| {
|
|
649
661
|
action: 'runtime_sheet_complete_map_rows';
|
|
650
662
|
input: Parameters<typeof completeRuntimeMapRows>[1];
|
|
@@ -2872,6 +2884,151 @@ async function registerRuntimeDataset(
|
|
|
2872
2884
|
);
|
|
2873
2885
|
}
|
|
2874
2886
|
|
|
2887
|
+
async function runtimeSheetMembershipCaptureAvailable(
|
|
2888
|
+
client: RuntimeQueryClient,
|
|
2889
|
+
session: RuntimePostgresSession,
|
|
2890
|
+
): Promise<boolean> {
|
|
2891
|
+
if (!RUNTIME_SHEET_RUN_MEMBERSHIP_WRITES_ENABLED) return false;
|
|
2892
|
+
return isRuntimeSheetRunMembershipStorageAvailable(
|
|
2893
|
+
async <Row>(sql: string, params?: readonly unknown[]) => {
|
|
2894
|
+
const result = await client.query(sql, params ? [...params] : undefined);
|
|
2895
|
+
return { rows: result.rows as Row[] };
|
|
2896
|
+
},
|
|
2897
|
+
session.postgres.schema,
|
|
2898
|
+
true,
|
|
2899
|
+
);
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
async function persistRuntimeSheetMembershipBatch(
|
|
2903
|
+
client: RuntimeQueryClient,
|
|
2904
|
+
session: RuntimePostgresSession,
|
|
2905
|
+
rows: Array<Omit<RuntimeSheetRunRowMembership, 'recordedAt'>>,
|
|
2906
|
+
) {
|
|
2907
|
+
await appendRuntimeSheetRunRowMembershipBatch(
|
|
2908
|
+
async <Row>(sql: string, params?: readonly unknown[]) => {
|
|
2909
|
+
const result = await client.query(sql, params ? [...params] : undefined);
|
|
2910
|
+
return { rows: result.rows as Row[] };
|
|
2911
|
+
},
|
|
2912
|
+
rows,
|
|
2913
|
+
session.postgres.schema,
|
|
2914
|
+
);
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
async function captureRuntimeSheetTerminalMembership(
|
|
2918
|
+
client: RuntimeQueryClient,
|
|
2919
|
+
session: RuntimePostgresSession,
|
|
2920
|
+
input: {
|
|
2921
|
+
runId: string;
|
|
2922
|
+
tableNamespace: string;
|
|
2923
|
+
sheetContract: PlaySheetContract;
|
|
2924
|
+
keys: string[];
|
|
2925
|
+
},
|
|
2926
|
+
): Promise<void> {
|
|
2927
|
+
if (input.keys.length === 0) return;
|
|
2928
|
+
const datasetId = createRuntimeDatasetId(
|
|
2929
|
+
session.playName,
|
|
2930
|
+
input.tableNamespace,
|
|
2931
|
+
);
|
|
2932
|
+
// Read canonical values AFTER the fenced mutation, inside its transaction.
|
|
2933
|
+
// A stale submitted body must never become a fabricated historical result.
|
|
2934
|
+
const result = await client.query<Record<string, unknown>>(
|
|
2935
|
+
`SELECT target.*, admission.input_digest AS membership_input_digest, admission.source_ref AS membership_source_ref
|
|
2936
|
+
FROM ${sheetTable(session)} target
|
|
2937
|
+
JOIN ${fqRuntimeTable(session, RUNTIME_SHEET_RUN_MEMBERSHIP_TABLE)} admission
|
|
2938
|
+
ON admission.run_id = $1 AND admission.dataset_id = $2 AND admission.row_key = target._key AND admission.phase = 'admitted'
|
|
2939
|
+
WHERE target._key = ANY($3::text[]) AND target._run_id = $1 AND target._status IN ('enriched', 'failed')`,
|
|
2940
|
+
[input.runId, datasetId, input.keys],
|
|
2941
|
+
);
|
|
2942
|
+
const snapshots = result.rows.map((raw) => {
|
|
2943
|
+
const attemptSeq =
|
|
2944
|
+
raw._attempt_seq == null ? null : Number(raw._attempt_seq);
|
|
2945
|
+
const rowVersion = Number(raw._version);
|
|
2946
|
+
const data = mapRuntimePostgresRow({
|
|
2947
|
+
raw,
|
|
2948
|
+
sheetContract: input.sheetContract,
|
|
2949
|
+
}).data;
|
|
2950
|
+
delete data[DEEPLINE_CELL_META_FIELD];
|
|
2951
|
+
return {
|
|
2952
|
+
runId: input.runId,
|
|
2953
|
+
datasetId,
|
|
2954
|
+
rowKey: String(raw._key),
|
|
2955
|
+
phase: 'terminal' as const,
|
|
2956
|
+
generation: `attempt:${attemptSeq ?? 'legacy'}:version:${rowVersion}`,
|
|
2957
|
+
attemptSeq,
|
|
2958
|
+
rowVersion,
|
|
2959
|
+
inputIndex: raw._input_index == null ? null : Number(raw._input_index),
|
|
2960
|
+
inputDigest: String(raw.membership_input_digest),
|
|
2961
|
+
status: raw._status as 'enriched' | 'failed',
|
|
2962
|
+
data,
|
|
2963
|
+
source: {
|
|
2964
|
+
...(raw.membership_source_ref as Record<string, unknown>),
|
|
2965
|
+
...(raw._error == null ? {} : { rowError: String(raw._error) }),
|
|
2966
|
+
},
|
|
2967
|
+
};
|
|
2968
|
+
});
|
|
2969
|
+
await persistRuntimeSheetMembershipBatch(client, session, snapshots);
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
/** Named completion is called only after admission advertised capture. */
|
|
2973
|
+
export async function finishRuntimeSheetRunMembership(
|
|
2974
|
+
context: RuntimeApiContext & { playName: string },
|
|
2975
|
+
input: {
|
|
2976
|
+
runId: string;
|
|
2977
|
+
tableNamespace: string;
|
|
2978
|
+
sheetContract: PlaySheetContract;
|
|
2979
|
+
expectedRows: number;
|
|
2980
|
+
},
|
|
2981
|
+
): Promise<{ captured: boolean; complete: boolean }> {
|
|
2982
|
+
if (!Number.isSafeInteger(input.expectedRows) || input.expectedRows < 0)
|
|
2983
|
+
throw new Error('Membership expectedRows must be a non-negative integer.');
|
|
2984
|
+
if (context.dbSessionStrategy === 'gateway_only')
|
|
2985
|
+
return postRuntimeApi(context, {
|
|
2986
|
+
action: 'runtime_sheet_finish_membership',
|
|
2987
|
+
input,
|
|
2988
|
+
});
|
|
2989
|
+
const session = requireRuntimePostgresSession(
|
|
2990
|
+
await getRuntimeDbSession(context, {
|
|
2991
|
+
tableNamespace: input.tableNamespace,
|
|
2992
|
+
logicalTable: 'sheet_rows',
|
|
2993
|
+
operations: ['rows.read', 'rows.upsert'],
|
|
2994
|
+
limits: {},
|
|
2995
|
+
}),
|
|
2996
|
+
);
|
|
2997
|
+
return withRuntimeSheetQueryClient(
|
|
2998
|
+
context,
|
|
2999
|
+
session,
|
|
3000
|
+
{
|
|
3001
|
+
playName: context.playName,
|
|
3002
|
+
tableNamespace: input.tableNamespace,
|
|
3003
|
+
sheetContract: input.sheetContract,
|
|
3004
|
+
transactional: true,
|
|
3005
|
+
},
|
|
3006
|
+
async (client) => {
|
|
3007
|
+
if (!(await runtimeSheetMembershipCaptureAvailable(client, session)))
|
|
3008
|
+
return { captured: false, complete: false };
|
|
3009
|
+
const datasetId = createRuntimeDatasetId(
|
|
3010
|
+
session.playName,
|
|
3011
|
+
input.tableNamespace,
|
|
3012
|
+
);
|
|
3013
|
+
const result = await client.query<{ complete: boolean }>(
|
|
3014
|
+
`UPDATE ${runDatasetCatalogTable(session)} catalog
|
|
3015
|
+
SET membership_expected_rows = $3,
|
|
3016
|
+
membership_completed_at = CASE WHEN
|
|
3017
|
+
(SELECT count(*) FROM ${fqRuntimeTable(session, RUNTIME_SHEET_RUN_MEMBERSHIP_TABLE)} WHERE run_id = $1 AND dataset_id = $2 AND phase = 'admitted') = $3
|
|
3018
|
+
AND (SELECT count(DISTINCT row_key) FROM ${fqRuntimeTable(session, RUNTIME_SHEET_RUN_MEMBERSHIP_TABLE)} WHERE run_id = $1 AND dataset_id = $2 AND phase = 'terminal') = $3
|
|
3019
|
+
THEN coalesce(catalog.membership_completed_at, now()) ELSE NULL END
|
|
3020
|
+
WHERE run_id = $1 AND dataset_id = $2 AND membership_started_at IS NOT NULL
|
|
3021
|
+
RETURNING membership_completed_at IS NOT NULL AS complete`,
|
|
3022
|
+
[input.runId, datasetId, input.expectedRows],
|
|
3023
|
+
);
|
|
3024
|
+
return {
|
|
3025
|
+
captured: result.rows.length === 1,
|
|
3026
|
+
complete: result.rows[0]?.complete === true,
|
|
3027
|
+
};
|
|
3028
|
+
},
|
|
3029
|
+
);
|
|
3030
|
+
}
|
|
3031
|
+
|
|
2875
3032
|
function workReceiptTable(session: RuntimePostgresSession): string {
|
|
2876
3033
|
return fqRuntimeTable(
|
|
2877
3034
|
session,
|
|
@@ -6877,7 +7034,11 @@ export async function startRuntimeSheetDataset(
|
|
|
6877
7034
|
const normalizeStartedAt = Date.now();
|
|
6878
7035
|
const uniqueRows = new Map<
|
|
6879
7036
|
string,
|
|
6880
|
-
{
|
|
7037
|
+
{
|
|
7038
|
+
row: Record<string, unknown>;
|
|
7039
|
+
inputIndex: number | null;
|
|
7040
|
+
source: Record<string, unknown>;
|
|
7041
|
+
}
|
|
6881
7042
|
>();
|
|
6882
7043
|
for (const row of input.rows) {
|
|
6883
7044
|
// Materializes projected CSV aliases as visible cells and drops internal
|
|
@@ -6890,6 +7051,13 @@ export async function startRuntimeSheetDataset(
|
|
|
6890
7051
|
if (key && !uniqueRows.has(key)) {
|
|
6891
7052
|
uniqueRows.set(key, {
|
|
6892
7053
|
row: sanitizePostgresJsonValue(cleanedRow),
|
|
7054
|
+
source:
|
|
7055
|
+
row.__deeplineSourceRow && typeof row.__deeplineSourceRow === 'object'
|
|
7056
|
+
? (sanitizePostgresJsonValue(row.__deeplineSourceRow) as Record<
|
|
7057
|
+
string,
|
|
7058
|
+
unknown
|
|
7059
|
+
>)
|
|
7060
|
+
: {},
|
|
6893
7061
|
inputIndex: normalizeRuntimeMapInputIndex(
|
|
6894
7062
|
row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.inputIndex],
|
|
6895
7063
|
),
|
|
@@ -6901,6 +7069,7 @@ export async function startRuntimeSheetDataset(
|
|
|
6901
7069
|
([key, entry], inputIndex) => ({
|
|
6902
7070
|
key,
|
|
6903
7071
|
row: entry.row,
|
|
7072
|
+
source: entry.source,
|
|
6904
7073
|
inputIndex: entry.inputIndex ?? inputOffset + inputIndex,
|
|
6905
7074
|
}),
|
|
6906
7075
|
);
|
|
@@ -7135,6 +7304,60 @@ export async function startRuntimeSheetDataset(
|
|
|
7135
7304
|
force: input.force === true,
|
|
7136
7305
|
...prepared,
|
|
7137
7306
|
});
|
|
7307
|
+
const membershipCapture = await runtimeSheetMembershipCaptureAvailable(
|
|
7308
|
+
client,
|
|
7309
|
+
session,
|
|
7310
|
+
);
|
|
7311
|
+
if (membershipCapture) {
|
|
7312
|
+
const dispositions = new Set(
|
|
7313
|
+
prepared.rowDispositions
|
|
7314
|
+
.filter(
|
|
7315
|
+
(row) =>
|
|
7316
|
+
row.disposition === 'pending' ||
|
|
7317
|
+
row.disposition === 'completed',
|
|
7318
|
+
)
|
|
7319
|
+
.map((row) => row.key),
|
|
7320
|
+
);
|
|
7321
|
+
const admitted = rowEntries.filter((entry) =>
|
|
7322
|
+
dispositions.has(entry.key),
|
|
7323
|
+
);
|
|
7324
|
+
await client.query(
|
|
7325
|
+
`UPDATE ${runDatasetCatalogTable(session)} SET membership_started_at = coalesce(membership_started_at, now()) WHERE run_id = $1 AND dataset_id = $2`,
|
|
7326
|
+
[
|
|
7327
|
+
input.runId,
|
|
7328
|
+
createRuntimeDatasetId(session.playName, input.tableNamespace),
|
|
7329
|
+
],
|
|
7330
|
+
);
|
|
7331
|
+
await persistRuntimeSheetMembershipBatch(
|
|
7332
|
+
client,
|
|
7333
|
+
session,
|
|
7334
|
+
admitted.map((entry) => ({
|
|
7335
|
+
runId: input.runId,
|
|
7336
|
+
datasetId: createRuntimeDatasetId(
|
|
7337
|
+
session.playName,
|
|
7338
|
+
input.tableNamespace,
|
|
7339
|
+
),
|
|
7340
|
+
rowKey: entry.key,
|
|
7341
|
+
phase: 'admitted',
|
|
7342
|
+
generation: 'admitted',
|
|
7343
|
+
attemptSeq: null,
|
|
7344
|
+
rowVersion: null,
|
|
7345
|
+
inputIndex: entry.inputIndex,
|
|
7346
|
+
inputDigest: createHash('sha256')
|
|
7347
|
+
.update(stableStringify(entry.row))
|
|
7348
|
+
.digest('hex'),
|
|
7349
|
+
status: 'pending',
|
|
7350
|
+
data: entry.row,
|
|
7351
|
+
source: entry.source,
|
|
7352
|
+
})),
|
|
7353
|
+
);
|
|
7354
|
+
await captureRuntimeSheetTerminalMembership(client, session, {
|
|
7355
|
+
runId: input.runId,
|
|
7356
|
+
tableNamespace: input.tableNamespace,
|
|
7357
|
+
sheetContract: input.sheetContract,
|
|
7358
|
+
keys: admitted.map((entry) => entry.key),
|
|
7359
|
+
});
|
|
7360
|
+
}
|
|
7138
7361
|
timings.push({
|
|
7139
7362
|
phase: 'build_result',
|
|
7140
7363
|
ms: Date.now() - buildStartedAt,
|
|
@@ -7146,6 +7369,7 @@ export async function startRuntimeSheetDataset(
|
|
|
7146
7369
|
});
|
|
7147
7370
|
return {
|
|
7148
7371
|
...built,
|
|
7372
|
+
membershipCapture,
|
|
7149
7373
|
attemptExpiresAt,
|
|
7150
7374
|
attemptSeq,
|
|
7151
7375
|
writeVersion,
|
|
@@ -8982,11 +9206,20 @@ export async function completeRuntimeMapRows(
|
|
|
8982
9206
|
}),
|
|
8983
9207
|
)
|
|
8984
9208
|
: { updated: 0, fencedRows: [] };
|
|
8985
|
-
|
|
9209
|
+
const classified = classifyRuntimeMapRowsWrite({
|
|
8986
9210
|
submittedKeys: uniqueRows.keys(),
|
|
8987
9211
|
updated: completed.updated + failed.updated,
|
|
8988
9212
|
fencedRows: [...completed.fencedRows, ...failed.fencedRows],
|
|
8989
9213
|
});
|
|
9214
|
+
if (await runtimeSheetMembershipCaptureAvailable(client, session)) {
|
|
9215
|
+
await captureRuntimeSheetTerminalMembership(client, session, {
|
|
9216
|
+
runId: input.runId,
|
|
9217
|
+
tableNamespace: input.tableNamespace,
|
|
9218
|
+
sheetContract,
|
|
9219
|
+
keys: classified.committedKeys,
|
|
9220
|
+
});
|
|
9221
|
+
}
|
|
9222
|
+
return classified;
|
|
8990
9223
|
},
|
|
8991
9224
|
)
|
|
8992
9225
|
.then((result) => {
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts
CHANGED
|
@@ -178,6 +178,9 @@ import {
|
|
|
178
178
|
readPostgresSchedulerRunnerLiveness,
|
|
179
179
|
readPostgresSchedulerRuntimeResourcesForRun,
|
|
180
180
|
isPostgresSchedulerRunnerProgressAttemptCurrent,
|
|
181
|
+
appendPostgresRunLedgerEvents,
|
|
182
|
+
readPostgresSchedulerRunnerTerminalReceivedAtMs,
|
|
183
|
+
claimPostgresSchedulerWaitingSuspensionAttemptForFailure,
|
|
181
184
|
requestComputeBillingDispatchToken,
|
|
182
185
|
blockPostgresSchedulerCronSchedule,
|
|
183
186
|
PostgresSchedulerCronScheduleConfigurationError,
|
|
@@ -233,6 +236,7 @@ import {
|
|
|
233
236
|
import { runWithAbsurdSchedulerQuerySignal } from './absurd-pool';
|
|
234
237
|
import {
|
|
235
238
|
buildFailedTerminalSettledProgressEvents,
|
|
239
|
+
collectGatewayRunnerTerminalProgressLedgerEvents,
|
|
236
240
|
createPostgresSchedulerProgressReporter,
|
|
237
241
|
type PostgresSchedulerProgressReporter,
|
|
238
242
|
} from './postgres-progress';
|
|
@@ -3261,11 +3265,57 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3261
3265
|
terminalLogProducerAttempt: input.terminalLogProducerAttempt,
|
|
3262
3266
|
}),
|
|
3263
3267
|
});
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3268
|
+
const retainedDetachedSuspension =
|
|
3269
|
+
input.fromDetachedRunner && suspension.kind !== 'detached_runner';
|
|
3270
|
+
let finalProgressRetained = false;
|
|
3271
|
+
if (retainedDetachedSuspension) {
|
|
3272
|
+
await runWithAbsurdExecutionLeaseHeartbeat({
|
|
3273
|
+
ctx,
|
|
3274
|
+
runId,
|
|
3275
|
+
operation: () =>
|
|
3276
|
+
deps.runWithClient(async (client) => {
|
|
3277
|
+
const runnerAttempt =
|
|
3278
|
+
input.terminalLogProducerAttempt ?? generation;
|
|
3279
|
+
const acceptedAt =
|
|
3280
|
+
await readPostgresSchedulerRunnerTerminalReceivedAtMs(
|
|
3281
|
+
client,
|
|
3282
|
+
{ runId, attempt: runnerAttempt },
|
|
3283
|
+
options,
|
|
3284
|
+
);
|
|
3285
|
+
if (acceptedAt !== null) {
|
|
3286
|
+
const events =
|
|
3287
|
+
await collectGatewayRunnerTerminalProgressLedgerEvents({
|
|
3288
|
+
launch,
|
|
3289
|
+
attempt: runnerAttempt,
|
|
3290
|
+
result: input.runnerResult,
|
|
3291
|
+
events: [],
|
|
3292
|
+
liveLogOffset: 0,
|
|
3293
|
+
suspensionAcceptedAtMs: acceptedAt,
|
|
3294
|
+
});
|
|
3295
|
+
if (events.length > 0) {
|
|
3296
|
+
await appendPostgresRunLedgerEvents(
|
|
3297
|
+
client,
|
|
3298
|
+
{ orgId: launch.orgId, runId, events },
|
|
3299
|
+
options,
|
|
3300
|
+
);
|
|
3301
|
+
}
|
|
3302
|
+
finalProgressRetained = true;
|
|
3303
|
+
}
|
|
3304
|
+
// The pushed result proves this exact attempt's runner exited.
|
|
3305
|
+
// Persist its provider-scoped obligation before publishing a wait
|
|
3306
|
+
// that can outlive this worker. Only the cleanup lease deletes it.
|
|
3307
|
+
await enqueuePostgresSchedulerSandboxCleanupForAttempt(
|
|
3308
|
+
client,
|
|
3309
|
+
{ runId, attempt: runnerAttempt },
|
|
3310
|
+
options,
|
|
3311
|
+
);
|
|
3312
|
+
}),
|
|
3313
|
+
});
|
|
3314
|
+
}
|
|
3315
|
+
// Local and historical results still require final publication before
|
|
3316
|
+
// advancing their checkpoint. Otherwise all final facts and cleanup are
|
|
3317
|
+
// durable, so progress transport cannot hide the typed waiting state.
|
|
3318
|
+
if (suspension.kind !== 'detached_runner' && !finalProgressRetained) {
|
|
3269
3319
|
await finalizeSuspensionProgress();
|
|
3270
3320
|
}
|
|
3271
3321
|
const outcome = await createPlayRunAttemptSuspensionJob({
|
|
@@ -3284,6 +3334,9 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3284
3334
|
options,
|
|
3285
3335
|
transition: 'waiting',
|
|
3286
3336
|
});
|
|
3337
|
+
if (finalProgressRetained) {
|
|
3338
|
+
await finalizeSuspensionProgress();
|
|
3339
|
+
}
|
|
3287
3340
|
},
|
|
3288
3341
|
resumed: async (lifecycle) =>
|
|
3289
3342
|
await projectLifecycleOutbox({
|
|
@@ -3695,9 +3748,6 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3695
3748
|
),
|
|
3696
3749
|
),
|
|
3697
3750
|
inspectDetachedRunner: deps.inspectDetachedRunner,
|
|
3698
|
-
deleteRuntimeSandbox: deps.deleteRuntimeSandbox,
|
|
3699
|
-
deleteRuntimeSandboxRef: deps.deleteRuntimeSandboxRef,
|
|
3700
|
-
sandboxGcTimeoutMs: deps.sandboxGcTimeoutMs,
|
|
3701
3751
|
}),
|
|
3702
3752
|
});
|
|
3703
3753
|
} catch (error) {
|
|
@@ -3800,9 +3850,6 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3800
3850
|
inspectDetachedRunner: deps.inspectDetachedRunner,
|
|
3801
3851
|
readDetachedRunnerRuntimeCompletion:
|
|
3802
3852
|
deps.readDetachedRunnerRuntimeCompletion,
|
|
3803
|
-
deleteRuntimeSandbox: deps.deleteRuntimeSandbox,
|
|
3804
|
-
deleteRuntimeSandboxRef: deps.deleteRuntimeSandboxRef,
|
|
3805
|
-
sandboxGcTimeoutMs: deps.sandboxGcTimeoutMs,
|
|
3806
3853
|
});
|
|
3807
3854
|
} else {
|
|
3808
3855
|
if (backendExecutions > 0) {
|
|
@@ -4824,10 +4871,9 @@ export async function enforceRuntimeTrafficPolicyBeforeSandboxStart(input: {
|
|
|
4824
4871
|
* terminal transaction;
|
|
4825
4872
|
* - thrown outcomes -> `releaseAbsurdAttemptRuntimeState` in the caller's
|
|
4826
4873
|
* catch (B4/FIX 3);
|
|
4827
|
-
* -
|
|
4828
|
-
*
|
|
4829
|
-
*
|
|
4830
|
-
* window.
|
|
4874
|
+
* - suspended results -> the existing durable cleanup controller, after
|
|
4875
|
+
* `resolveRunnerSuspension` persists its obligation before typed waiting.
|
|
4876
|
+
* Provider deletion never delays publishing the user's wait.
|
|
4831
4877
|
*/
|
|
4832
4878
|
async function resolveDetachedRunnerOutcome(input: {
|
|
4833
4879
|
runId: string;
|
|
@@ -4845,9 +4891,6 @@ async function resolveDetachedRunnerOutcome(input: {
|
|
|
4845
4891
|
} | null>;
|
|
4846
4892
|
inspectDetachedRunner?: AbsurdRuntimeWorkerDeps['inspectDetachedRunner'];
|
|
4847
4893
|
readDetachedRunnerRuntimeCompletion?: AbsurdRuntimeWorkerDeps['readDetachedRunnerRuntimeCompletion'];
|
|
4848
|
-
deleteRuntimeSandbox: AbsurdRuntimeWorkerDeps['deleteRuntimeSandbox'];
|
|
4849
|
-
deleteRuntimeSandboxRef?: AbsurdRuntimeWorkerDeps['deleteRuntimeSandboxRef'];
|
|
4850
|
-
sandboxGcTimeoutMs?: number;
|
|
4851
4894
|
}): Promise<PlayRunnerResult> {
|
|
4852
4895
|
const { suspension } = input;
|
|
4853
4896
|
const runtimeLimitSeconds =
|
|
@@ -4873,40 +4916,10 @@ async function resolveDetachedRunnerOutcome(input: {
|
|
|
4873
4916
|
}),
|
|
4874
4917
|
);
|
|
4875
4918
|
};
|
|
4876
|
-
const gcSandboxIfSuspended = async (result: PlayRunnerResult) => {
|
|
4877
|
-
if (result.status !== 'suspended') return;
|
|
4878
|
-
try {
|
|
4879
|
-
const deleteResult = await deleteRuntimeSandboxBounded<
|
|
4880
|
-
boolean | RuntimeSandboxDeleteOutcome
|
|
4881
|
-
>({
|
|
4882
|
-
deleteRuntimeSandbox: input.deleteRuntimeSandboxRef
|
|
4883
|
-
? () =>
|
|
4884
|
-
input.deleteRuntimeSandboxRef!(
|
|
4885
|
-
runtimeSandboxRefFromSuspension(suspension),
|
|
4886
|
-
)
|
|
4887
|
-
: input.deleteRuntimeSandbox,
|
|
4888
|
-
sandboxId: suspension.sandboxId,
|
|
4889
|
-
timeoutMs: input.sandboxGcTimeoutMs ?? ABSURD_SANDBOX_GC_TIMEOUT_MS,
|
|
4890
|
-
});
|
|
4891
|
-
requireRuntimeSandboxDeleted(
|
|
4892
|
-
runtimeSandboxRefFromSuspension(suspension),
|
|
4893
|
-
deleteResult,
|
|
4894
|
-
);
|
|
4895
|
-
} catch (error) {
|
|
4896
|
-
console.warn('[absurd-runtime.worker] detached_sandbox_gc_failed', {
|
|
4897
|
-
runId: input.runId,
|
|
4898
|
-
runnerAttempt: suspension.runnerAttempt,
|
|
4899
|
-
sandboxId: suspension.sandboxId,
|
|
4900
|
-
stage: 'suspended_result',
|
|
4901
|
-
error: error instanceof Error ? error.message : String(error),
|
|
4902
|
-
});
|
|
4903
|
-
}
|
|
4904
|
-
};
|
|
4905
4919
|
const settle = async (
|
|
4906
4920
|
result: PlayRunnerResult,
|
|
4907
4921
|
): Promise<PlayRunnerResult> => {
|
|
4908
4922
|
await recordWorkCompletion(result);
|
|
4909
|
-
await gcSandboxIfSuspended(result);
|
|
4910
4923
|
return result;
|
|
4911
4924
|
};
|
|
4912
4925
|
const settleFirstWriteWinner = async (
|
|
@@ -5548,6 +5561,7 @@ async function salvageAbsurdFinalAttemptTerminal(input: {
|
|
|
5548
5561
|
) {
|
|
5549
5562
|
try {
|
|
5550
5563
|
result = await input.deps.runWithClient(async (client) => {
|
|
5564
|
+
let protectWaitingSuspensionFailure = false;
|
|
5551
5565
|
// A detached-runner wake keeps its attempt `waiting` until setup
|
|
5552
5566
|
// succeeds. Take that same fence to `running` (the overdue-ceiling
|
|
5553
5567
|
// claim's transition) so the failure terminal can be written. When
|
|
@@ -5555,22 +5569,42 @@ async function salvageAbsurdFinalAttemptTerminal(input: {
|
|
|
5555
5569
|
// fence or pushed terminal owns it — the ordinary fenced path below
|
|
5556
5570
|
// decides.
|
|
5557
5571
|
if (input.detachedRecovery) {
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5572
|
+
const detachedClaimed =
|
|
5573
|
+
await claimPostgresSchedulerWaitingDetachedAttemptForTerminal(
|
|
5574
|
+
client,
|
|
5575
|
+
{
|
|
5576
|
+
runId: input.runId,
|
|
5577
|
+
attempt: input.generation,
|
|
5578
|
+
leaseToken: input.leaseToken,
|
|
5579
|
+
},
|
|
5580
|
+
input.options,
|
|
5581
|
+
);
|
|
5582
|
+
if (!detachedClaimed) {
|
|
5583
|
+
const preparation =
|
|
5584
|
+
await claimPostgresSchedulerWaitingSuspensionAttemptForFailure(
|
|
5585
|
+
client,
|
|
5586
|
+
{
|
|
5587
|
+
runId: input.runId,
|
|
5588
|
+
attempt: input.generation,
|
|
5589
|
+
leaseToken: input.leaseToken,
|
|
5590
|
+
},
|
|
5591
|
+
input.options,
|
|
5592
|
+
);
|
|
5593
|
+
if (preparation === 'fence_lost')
|
|
5594
|
+
return { status: 'idle' as const };
|
|
5595
|
+
protectWaitingSuspensionFailure = preparation === 'prepared';
|
|
5596
|
+
}
|
|
5567
5597
|
}
|
|
5568
5598
|
return await executePostgresSchedulerClaim(
|
|
5569
5599
|
client,
|
|
5570
5600
|
claim,
|
|
5571
5601
|
async () => failedResult,
|
|
5572
5602
|
input.detachedRecovery
|
|
5573
|
-
? {
|
|
5603
|
+
? {
|
|
5604
|
+
...input.options,
|
|
5605
|
+
preserveRunnerTerminal: true,
|
|
5606
|
+
protectWaitingSuspensionFailure,
|
|
5607
|
+
}
|
|
5574
5608
|
: input.options,
|
|
5575
5609
|
);
|
|
5576
5610
|
});
|
|
@@ -1086,7 +1086,13 @@ export async function withRuntimeSchedulerClient<T>(
|
|
|
1086
1086
|
try {
|
|
1087
1087
|
const queryResult = await poolClient.query(sql, params);
|
|
1088
1088
|
throwIfSchedulerAborted(signal);
|
|
1089
|
-
return
|
|
1089
|
+
// Simple-protocol transactions return one result per command.
|
|
1090
|
+
// Keep the same rows contract for callers of either protocol.
|
|
1091
|
+
return {
|
|
1092
|
+
rows: Array.isArray(queryResult)
|
|
1093
|
+
? queryResult.flatMap((result) => result.rows)
|
|
1094
|
+
: queryResult.rows,
|
|
1095
|
+
};
|
|
1090
1096
|
} catch (error) {
|
|
1091
1097
|
// node-postgres' query timeout and an AbortSignal reject the
|
|
1092
1098
|
// promise, but the server may still be processing the SQL.
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts
CHANGED
|
@@ -39,6 +39,7 @@ import { runtimeWorkCountersFromStats } from '../runtime-work-counters';
|
|
|
39
39
|
import {
|
|
40
40
|
buildPlayRunLedgerEventsFromStatusPatch,
|
|
41
41
|
createEmptyPlayRunLedgerSnapshot,
|
|
42
|
+
buildTerminalLogReplayEvents,
|
|
42
43
|
type PlayMapRowActivitySource,
|
|
43
44
|
type PlayRunLedgerEvent,
|
|
44
45
|
} from '../run-ledger';
|
|
@@ -1902,8 +1903,9 @@ export type DirectWorkerProgressReporter = PostgresSchedulerProgressReporter;
|
|
|
1902
1903
|
* network boundary. The returned events are stored in the same Postgres
|
|
1903
1904
|
* terminal outbox row as `run.completed`, so the projector can append the
|
|
1904
1905
|
* final step/log/activity facts and the terminal fact in one ordered Convex
|
|
1905
|
-
* mutation.
|
|
1906
|
-
*
|
|
1906
|
+
* mutation. A worker can also retain a suspended runner's complete final
|
|
1907
|
+
* progress before advancing its checkpoint, using the immutable acceptance
|
|
1908
|
+
* timestamp for the same outbox identity across crash or response-loss replay.
|
|
1907
1909
|
*/
|
|
1908
1910
|
export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
1909
1911
|
launch: PlaySchedulerSubmitInput;
|
|
@@ -1911,14 +1913,17 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
|
1911
1913
|
result: PlayRunnerResult;
|
|
1912
1914
|
events: unknown[];
|
|
1913
1915
|
liveLogOffset: number;
|
|
1916
|
+
suspensionAcceptedAtMs?: number;
|
|
1914
1917
|
}): Promise<PlayRunLedgerEvent[]> {
|
|
1915
1918
|
const captured: { update?: RuntimeStatusUpdate } = {};
|
|
1916
|
-
const
|
|
1919
|
+
const retainedSuspension = input.suspensionAcceptedAtMs !== undefined;
|
|
1920
|
+
const reporter = createPostgresSchedulerProgressReporter({
|
|
1917
1921
|
launch: input.launch,
|
|
1918
1922
|
producerAttempt: input.attempt,
|
|
1919
1923
|
initialLiveLogTotalCount: input.liveLogOffset,
|
|
1920
1924
|
isCurrentAttempt: () => true,
|
|
1921
1925
|
attemptAlreadyVerified: true,
|
|
1926
|
+
durableLogMode: retainedSuspension ? 'progress_reporter' : 'direct_worker',
|
|
1922
1927
|
publishStatusUpdate: async (update) => {
|
|
1923
1928
|
captured.update = update;
|
|
1924
1929
|
},
|
|
@@ -1929,6 +1934,7 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
|
1929
1934
|
input.launch.contractSnapshot,
|
|
1930
1935
|
);
|
|
1931
1936
|
if (
|
|
1937
|
+
!retainedSuspension &&
|
|
1932
1938
|
pipeline?.docflow?.nodes.some(
|
|
1933
1939
|
(node) => node.kind === 'decision' && !node.sketch,
|
|
1934
1940
|
)
|
|
@@ -1977,11 +1983,14 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
|
1977
1983
|
await reporter.finalize(input.result, {
|
|
1978
1984
|
status: 'running',
|
|
1979
1985
|
required: true,
|
|
1980
|
-
skipTerminalLogReplay:
|
|
1986
|
+
skipTerminalLogReplay: !retainedSuspension,
|
|
1987
|
+
replayAllTerminalLogs: retainedSuspension,
|
|
1981
1988
|
terminalLogProducerAttempt: input.attempt,
|
|
1982
1989
|
});
|
|
1983
1990
|
const finalUpdate = captured.update;
|
|
1984
1991
|
if (!finalUpdate) return [];
|
|
1992
|
+
const checkpointAt =
|
|
1993
|
+
input.suspensionAcceptedAtMs ?? finalUpdate.lastCheckpointAt;
|
|
1985
1994
|
|
|
1986
1995
|
const liveLogs = finalUpdate.liveLogs ?? [];
|
|
1987
1996
|
const liveLogTotalCount =
|
|
@@ -1990,7 +1999,7 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
|
1990
1999
|
patch: {
|
|
1991
2000
|
runId: input.launch.runId,
|
|
1992
2001
|
status: 'running',
|
|
1993
|
-
lastCheckpointAt:
|
|
2002
|
+
lastCheckpointAt: checkpointAt,
|
|
1994
2003
|
liveNodeProgress: finalUpdate.liveNodeProgress,
|
|
1995
2004
|
liveLogs,
|
|
1996
2005
|
liveLogContexts: finalUpdate.liveLogContexts,
|
|
@@ -2011,7 +2020,7 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
|
2011
2020
|
type: RUN_EVENT_KIND.workCounters,
|
|
2012
2021
|
runId: input.launch.runId,
|
|
2013
2022
|
source: 'worker',
|
|
2014
|
-
occurredAt:
|
|
2023
|
+
occurredAt: checkpointAt ?? Date.now(),
|
|
2015
2024
|
...finalUpdate.workCounters,
|
|
2016
2025
|
});
|
|
2017
2026
|
}
|
|
@@ -2045,5 +2054,19 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
|
2045
2054
|
occurredAt: observation.observedAt,
|
|
2046
2055
|
observation,
|
|
2047
2056
|
}));
|
|
2048
|
-
|
|
2057
|
+
const replay = finalUpdate.terminalLogReplay;
|
|
2058
|
+
const terminalLogEvents = replay
|
|
2059
|
+
? buildTerminalLogReplayEvents({
|
|
2060
|
+
runId: input.launch.runId,
|
|
2061
|
+
...replay,
|
|
2062
|
+
occurredAt: checkpointAt ?? undefined,
|
|
2063
|
+
liveLogTotalCount: replay.totalCount,
|
|
2064
|
+
})
|
|
2065
|
+
: [];
|
|
2066
|
+
return [
|
|
2067
|
+
...progressEvents,
|
|
2068
|
+
...datasetEvents,
|
|
2069
|
+
...activityEvents,
|
|
2070
|
+
...terminalLogEvents,
|
|
2071
|
+
];
|
|
2049
2072
|
}
|