deepline 0.1.295 → 0.1.297
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/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +237 -167
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +15 -0
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +3 -94
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +209 -22
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-writer.ts +662 -0
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
|
@@ -30,10 +30,7 @@ export type RuntimeResourceSnapshot = {
|
|
|
30
30
|
};
|
|
31
31
|
sheetFlush: {
|
|
32
32
|
acquired: number;
|
|
33
|
-
active: number;
|
|
34
|
-
waiting: number;
|
|
35
33
|
bytes: number;
|
|
36
|
-
admissionWaitMsEwma: number | null;
|
|
37
34
|
latencyMsEwma: number | null;
|
|
38
35
|
};
|
|
39
36
|
providers: Record<
|
|
@@ -62,11 +59,6 @@ export interface RuntimeResourceGovernor {
|
|
|
62
59
|
toolId: string;
|
|
63
60
|
signal?: AbortSignal;
|
|
64
61
|
}): Promise<RuntimeResourceLease>;
|
|
65
|
-
acquireSheetFlush(input?: {
|
|
66
|
-
estimatedBytes?: number | null;
|
|
67
|
-
rowCount?: number | null;
|
|
68
|
-
signal?: AbortSignal;
|
|
69
|
-
}): Promise<RuntimeResourceLease>;
|
|
70
62
|
suggestedToolParallelism(toolId: string, fallback: number): Promise<number>;
|
|
71
63
|
resolveRowConcurrency(requested?: number): number;
|
|
72
64
|
reportProviderBackpressure(input: {
|
|
@@ -98,76 +90,6 @@ function createEwma(alpha = 0.2): Ewma {
|
|
|
98
90
|
};
|
|
99
91
|
}
|
|
100
92
|
|
|
101
|
-
// Sheet persistence has its own local admission lane. Receipt lifecycle calls
|
|
102
|
-
// are admitted by the receipt sink/gateway and must not queue behind a large
|
|
103
|
-
// Runtime Sheet flush in the runner. Two flushes keep the database busy while
|
|
104
|
-
// bounding resident payloads and gateway/pool pressure (ADR 0012).
|
|
105
|
-
const MAX_CONCURRENT_SHEET_FLUSHES = 2;
|
|
106
|
-
|
|
107
|
-
type SheetFlushWaiter = {
|
|
108
|
-
resolve: (lease: RuntimeResourceLease) => void;
|
|
109
|
-
reject: (error: unknown) => void;
|
|
110
|
-
signal?: AbortSignal;
|
|
111
|
-
onAbort?: () => void;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
class SheetFlushAdmission {
|
|
115
|
-
#active = 0;
|
|
116
|
-
#waiters: SheetFlushWaiter[] = [];
|
|
117
|
-
|
|
118
|
-
get active(): number {
|
|
119
|
-
return this.#active;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
get waiting(): number {
|
|
123
|
-
return this.#waiters.length;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
acquire(signal?: AbortSignal): Promise<RuntimeResourceLease> {
|
|
127
|
-
if (signal?.aborted) {
|
|
128
|
-
return Promise.reject(signal.reason ?? new Error('Sheet flush aborted'));
|
|
129
|
-
}
|
|
130
|
-
if (this.#active < MAX_CONCURRENT_SHEET_FLUSHES) {
|
|
131
|
-
this.#active += 1;
|
|
132
|
-
return Promise.resolve(this.#lease());
|
|
133
|
-
}
|
|
134
|
-
return new Promise((resolve, reject) => {
|
|
135
|
-
const waiter: SheetFlushWaiter = {
|
|
136
|
-
resolve,
|
|
137
|
-
reject,
|
|
138
|
-
...(signal ? { signal } : {}),
|
|
139
|
-
};
|
|
140
|
-
if (signal) {
|
|
141
|
-
waiter.onAbort = () => {
|
|
142
|
-
const index = this.#waiters.indexOf(waiter);
|
|
143
|
-
if (index >= 0) this.#waiters.splice(index, 1);
|
|
144
|
-
reject(signal.reason ?? new Error('Sheet flush aborted'));
|
|
145
|
-
};
|
|
146
|
-
signal.addEventListener('abort', waiter.onAbort, { once: true });
|
|
147
|
-
}
|
|
148
|
-
this.#waiters.push(waiter);
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
#lease(): RuntimeResourceLease {
|
|
153
|
-
let released = false;
|
|
154
|
-
return {
|
|
155
|
-
release: () => {
|
|
156
|
-
if (released) return;
|
|
157
|
-
released = true;
|
|
158
|
-
while (this.#waiters.length > 0) {
|
|
159
|
-
const waiter = this.#waiters.shift()!;
|
|
160
|
-
waiter.signal?.removeEventListener('abort', waiter.onAbort!);
|
|
161
|
-
if (waiter.signal?.aborted) continue;
|
|
162
|
-
waiter.resolve(this.#lease());
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
this.#active = Math.max(0, this.#active - 1);
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
93
|
function elapsedSince(startedAt: number): number {
|
|
172
94
|
return Math.max(0, Date.now() - startedAt);
|
|
173
95
|
}
|
|
@@ -183,8 +105,6 @@ export function createRuntimeResourceGovernor(input: {
|
|
|
183
105
|
const rowAdmissionWait = createEwma();
|
|
184
106
|
const toolAdmissionWait = createEwma();
|
|
185
107
|
const sheetFlushLatency = createEwma();
|
|
186
|
-
const sheetFlushAdmissionWait = createEwma();
|
|
187
|
-
const sheetFlushAdmission = new SheetFlushAdmission();
|
|
188
108
|
const providers = new Map<
|
|
189
109
|
string,
|
|
190
110
|
{
|
|
@@ -223,6 +143,9 @@ export function createRuntimeResourceGovernor(input: {
|
|
|
223
143
|
rowAdmissionWait.observe(observation.rowAdmissionWaitMs);
|
|
224
144
|
toolAdmissionWait.observe(observation.toolAdmissionWaitMs);
|
|
225
145
|
sheetFlushLatency.observe(observation.sheetFlushLatencyMs);
|
|
146
|
+
if (observation.sheetFlushLatencyMs != null) {
|
|
147
|
+
sheetFlushCounters.acquired += 1;
|
|
148
|
+
}
|
|
226
149
|
if (observation.rowEstimatedBytes != null) {
|
|
227
150
|
rowCounters.estimatedBytes += Math.max(
|
|
228
151
|
0,
|
|
@@ -301,17 +224,6 @@ export function createRuntimeResourceGovernor(input: {
|
|
|
301
224
|
);
|
|
302
225
|
},
|
|
303
226
|
|
|
304
|
-
async acquireSheetFlush(sheetInput) {
|
|
305
|
-
const startedAt = Date.now();
|
|
306
|
-
const lease = await sheetFlushAdmission.acquire(sheetInput?.signal);
|
|
307
|
-
sheetFlushCounters.acquired += 1;
|
|
308
|
-
sheetFlushAdmissionWait.observe(elapsedSince(startedAt));
|
|
309
|
-
observe({
|
|
310
|
-
sheetFlushBytes: sheetInput?.estimatedBytes ?? null,
|
|
311
|
-
});
|
|
312
|
-
return lease;
|
|
313
|
-
},
|
|
314
|
-
|
|
315
227
|
suggestedToolParallelism(toolId, fallback) {
|
|
316
228
|
return input.executionGovernor.suggestedParallelism(toolId, fallback);
|
|
317
229
|
},
|
|
@@ -344,10 +256,7 @@ export function createRuntimeResourceGovernor(input: {
|
|
|
344
256
|
},
|
|
345
257
|
sheetFlush: {
|
|
346
258
|
acquired: sheetFlushCounters.acquired,
|
|
347
|
-
active: sheetFlushAdmission.active,
|
|
348
|
-
waiting: sheetFlushAdmission.waiting,
|
|
349
259
|
bytes: sheetFlushCounters.bytes,
|
|
350
|
-
admissionWaitMsEwma: sheetFlushAdmissionWait.value,
|
|
351
260
|
latencyMsEwma: sheetFlushLatency.value,
|
|
352
261
|
},
|
|
353
262
|
providers: Object.fromEntries(
|
|
@@ -6456,12 +6456,44 @@ type CompleteRuntimeMapRowChunksInput = {
|
|
|
6456
6456
|
};
|
|
6457
6457
|
|
|
6458
6458
|
export type RuntimeMapRowsWriteResult = {
|
|
6459
|
+
/** Submitted row identities durably accepted, including idempotent replays. */
|
|
6460
|
+
committedKeys: string[];
|
|
6461
|
+
/** Submitted row identities rejected because a newer write owns the row. */
|
|
6462
|
+
staleKeys: string[];
|
|
6463
|
+
/** Physical row mutations. Idempotent commits do not increment this count. */
|
|
6459
6464
|
updated: number;
|
|
6465
|
+
/** Compatibility alias retained for callers that still call stale rows fenced. */
|
|
6460
6466
|
fencedKeys: string[];
|
|
6461
6467
|
staleDropped?: number;
|
|
6462
6468
|
staleDroppedKeys?: string[];
|
|
6463
6469
|
};
|
|
6464
6470
|
|
|
6471
|
+
type RuntimeMapRowsMutationResult = {
|
|
6472
|
+
updated: number;
|
|
6473
|
+
fencedKeys: string[];
|
|
6474
|
+
conflictKeys: string[];
|
|
6475
|
+
};
|
|
6476
|
+
|
|
6477
|
+
function classifyRuntimeMapRowsWrite(input: {
|
|
6478
|
+
submittedKeys: Iterable<string>;
|
|
6479
|
+
updated: number;
|
|
6480
|
+
fencedKeys: Iterable<string>;
|
|
6481
|
+
}): RuntimeMapRowsWriteResult {
|
|
6482
|
+
const staleKeys = [...new Set(input.fencedKeys)];
|
|
6483
|
+
const staleSet = new Set(staleKeys);
|
|
6484
|
+
const committedKeys = [...new Set(input.submittedKeys)].filter(
|
|
6485
|
+
(key) => !staleSet.has(key),
|
|
6486
|
+
);
|
|
6487
|
+
return {
|
|
6488
|
+
committedKeys,
|
|
6489
|
+
staleKeys,
|
|
6490
|
+
updated: input.updated,
|
|
6491
|
+
fencedKeys: staleKeys,
|
|
6492
|
+
staleDropped: staleKeys.length,
|
|
6493
|
+
staleDroppedKeys: staleKeys,
|
|
6494
|
+
};
|
|
6495
|
+
}
|
|
6496
|
+
|
|
6465
6497
|
function writableEnrichedRuntimeSheetAttemptSql(
|
|
6466
6498
|
tableAlias: string,
|
|
6467
6499
|
attemptOwnerRunIdExpression: string,
|
|
@@ -6480,9 +6512,10 @@ async function completeRuntimeMapRowChunks(
|
|
|
6480
6512
|
client: RuntimeQueryClient,
|
|
6481
6513
|
session: RuntimePostgresSession,
|
|
6482
6514
|
input: CompleteRuntimeMapRowChunksInput,
|
|
6483
|
-
): Promise<
|
|
6515
|
+
): Promise<RuntimeMapRowsMutationResult> {
|
|
6484
6516
|
let updated = 0;
|
|
6485
6517
|
const fencedKeys: string[] = [];
|
|
6518
|
+
const conflictKeys: string[] = [];
|
|
6486
6519
|
for (const chunk of input.chunks) {
|
|
6487
6520
|
const chunkKeys = chunk.map((row) => row.key);
|
|
6488
6521
|
const chunkInputIndexes = chunk.map((row) => row.input_index);
|
|
@@ -6515,6 +6548,7 @@ async function completeRuntimeMapRowChunks(
|
|
|
6515
6548
|
updated: number;
|
|
6516
6549
|
matched_keys: string[];
|
|
6517
6550
|
fenced_keys: string[];
|
|
6551
|
+
conflict_keys: string[];
|
|
6518
6552
|
}>(
|
|
6519
6553
|
`WITH updates AS (
|
|
6520
6554
|
SELECT key_values._key,
|
|
@@ -6540,6 +6574,31 @@ async function completeRuntimeMapRowChunks(
|
|
|
6540
6574
|
JOIN ${sheetTable(session)} AS target
|
|
6541
6575
|
ON target._key = updates._key
|
|
6542
6576
|
),
|
|
6577
|
+
same_version_conflicts AS (
|
|
6578
|
+
SELECT updates.matched_key AS _key
|
|
6579
|
+
FROM matched_updates AS updates
|
|
6580
|
+
JOIN ${sheetTable(session)} AS target
|
|
6581
|
+
ON target._key = updates.matched_key
|
|
6582
|
+
WHERE $13::bigint IS NOT NULL
|
|
6583
|
+
AND target._write_version = $13::bigint
|
|
6584
|
+
AND target._run_id = $5::text
|
|
6585
|
+
AND target._status IN ('enriched', 'failed')
|
|
6586
|
+
AND (
|
|
6587
|
+
EXISTS (
|
|
6588
|
+
SELECT 1
|
|
6589
|
+
FROM unnest($8::text[]) AS field_values(field)
|
|
6590
|
+
WHERE target._cell_meta -> field_values.field ->> 'runId' = $5::text
|
|
6591
|
+
)
|
|
6592
|
+
OR (
|
|
6593
|
+
coalesce(target._attempt_owner_run_id, target._run_id) = $10::text
|
|
6594
|
+
AND COALESCE(target._attempt_seq, 0) = $12::integer
|
|
6595
|
+
)
|
|
6596
|
+
)
|
|
6597
|
+
AND (
|
|
6598
|
+
target._status <> 'enriched'
|
|
6599
|
+
OR (${targetChangedPatchedCellSql})
|
|
6600
|
+
)
|
|
6601
|
+
),
|
|
6543
6602
|
applied_rows AS (
|
|
6544
6603
|
UPDATE ${sheetTable(session)} AS target
|
|
6545
6604
|
SET _status = 'enriched',
|
|
@@ -6556,6 +6615,7 @@ async function completeRuntimeMapRowChunks(
|
|
|
6556
6615
|
_cell_meta = ${mergeRuntimeCellMetaPatchSql('target._cell_meta', 'updates.cell_meta_patch')}${input.physicalUpdateSetSql}
|
|
6557
6616
|
FROM matched_updates AS updates
|
|
6558
6617
|
WHERE target._key = updates.matched_key
|
|
6618
|
+
AND NOT EXISTS (SELECT 1 FROM same_version_conflicts)
|
|
6559
6619
|
AND ($13::bigint IS NULL OR target._write_version = $13::bigint)
|
|
6560
6620
|
AND ($13::bigint IS NOT NULL OR target._status <> 'enriched')
|
|
6561
6621
|
AND NOT (${newerTerminalRuntimeSheetRowSql('target', '$11::timestamptz', '$12::integer', '$9::text', '$10::text')})
|
|
@@ -6691,6 +6751,7 @@ async function completeRuntimeMapRowChunks(
|
|
|
6691
6751
|
(SELECT count(*)::int FROM applied_rows) AS updated,
|
|
6692
6752
|
coalesce((SELECT array_agg(matched_key) FROM matched_updates), '{}'::text[]) AS matched_keys,
|
|
6693
6753
|
coalesce((SELECT array_agg(_key) FROM fenced_rows), '{}'::text[]) AS fenced_keys,
|
|
6754
|
+
coalesce((SELECT array_agg(_key) FROM same_version_conflicts), '{}'::text[]) AS conflict_keys,
|
|
6694
6755
|
(SELECT count(*)::int FROM summary_delta) AS summary_delta_count,
|
|
6695
6756
|
(SELECT count(*)::int FROM column_delta) AS column_delta_count`,
|
|
6696
6757
|
[
|
|
@@ -6711,12 +6772,18 @@ async function completeRuntimeMapRowChunks(
|
|
|
6711
6772
|
);
|
|
6712
6773
|
const matchedKeys = new Set(rows[0]?.matched_keys ?? []);
|
|
6713
6774
|
const chunkFencedKeys = rows[0]?.fenced_keys ?? [];
|
|
6775
|
+
const chunkConflictKeys = rows[0]?.conflict_keys ?? [];
|
|
6714
6776
|
// An exact-version replay that already produced the same terminal value is
|
|
6715
6777
|
// accepted idempotently even when SQL has no physical mutation to apply.
|
|
6716
6778
|
// Stale versions remain excluded through fenced_keys.
|
|
6717
6779
|
const appliedCount = Number(rows[0]?.updated ?? 0);
|
|
6718
6780
|
updated += appliedCount;
|
|
6719
6781
|
fencedKeys.push(...chunkFencedKeys);
|
|
6782
|
+
conflictKeys.push(...chunkConflictKeys);
|
|
6783
|
+
|
|
6784
|
+
if (chunkConflictKeys.length > 0) {
|
|
6785
|
+
continue;
|
|
6786
|
+
}
|
|
6720
6787
|
|
|
6721
6788
|
if (matchedKeys.size === chunk.length) {
|
|
6722
6789
|
continue;
|
|
@@ -6735,17 +6802,19 @@ async function completeRuntimeMapRowChunks(
|
|
|
6735
6802
|
);
|
|
6736
6803
|
updated += repaired.updated;
|
|
6737
6804
|
fencedKeys.push(...repaired.fencedKeys);
|
|
6805
|
+
conflictKeys.push(...repaired.conflictKeys);
|
|
6738
6806
|
}
|
|
6739
|
-
return { updated, fencedKeys };
|
|
6807
|
+
return { updated, fencedKeys, conflictKeys };
|
|
6740
6808
|
}
|
|
6741
6809
|
|
|
6742
6810
|
async function completeRuntimeMapRowChunksWithInputIndexRepair(
|
|
6743
6811
|
client: RuntimeQueryClient,
|
|
6744
6812
|
session: RuntimePostgresSession,
|
|
6745
6813
|
input: CompleteRuntimeMapRowChunksInput,
|
|
6746
|
-
): Promise<
|
|
6814
|
+
): Promise<RuntimeMapRowsMutationResult> {
|
|
6747
6815
|
let updated = 0;
|
|
6748
6816
|
const fencedKeys: string[] = [];
|
|
6817
|
+
const conflictKeys: string[] = [];
|
|
6749
6818
|
for (const chunk of input.chunks) {
|
|
6750
6819
|
const chunkKeys = chunk.map((row) => row.key);
|
|
6751
6820
|
const chunkInputIndexes = chunk.map((row) => row.input_index);
|
|
@@ -6770,6 +6839,7 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
|
|
|
6770
6839
|
const { rows } = await client.query<{
|
|
6771
6840
|
_key?: string;
|
|
6772
6841
|
fenced_keys?: string[];
|
|
6842
|
+
conflict_keys?: string[];
|
|
6773
6843
|
updated?: number;
|
|
6774
6844
|
}>(
|
|
6775
6845
|
`WITH updates AS (
|
|
@@ -6788,6 +6858,7 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
|
|
|
6788
6858
|
matched_updates AS (
|
|
6789
6859
|
SELECT DISTINCT ON (target._key)
|
|
6790
6860
|
target._key AS matched_key,
|
|
6861
|
+
updates._key AS submitted_key,
|
|
6791
6862
|
updates.data_patch,
|
|
6792
6863
|
updates.cell_meta_patch
|
|
6793
6864
|
FROM updates
|
|
@@ -6800,10 +6871,37 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
|
|
|
6800
6871
|
)
|
|
6801
6872
|
ORDER BY target._key, (target._key = updates._key) DESC
|
|
6802
6873
|
),
|
|
6874
|
+
same_version_conflicts AS (
|
|
6875
|
+
SELECT updates.submitted_key AS _key
|
|
6876
|
+
FROM matched_updates AS updates
|
|
6877
|
+
JOIN ${sheetTable(session)} AS target
|
|
6878
|
+
ON target._key = updates.matched_key
|
|
6879
|
+
WHERE $13::bigint IS NOT NULL
|
|
6880
|
+
AND target._write_version = $13::bigint
|
|
6881
|
+
AND target._run_id = $5::text
|
|
6882
|
+
AND target._status IN ('enriched', 'failed')
|
|
6883
|
+
AND (
|
|
6884
|
+
EXISTS (
|
|
6885
|
+
SELECT 1
|
|
6886
|
+
FROM unnest($8::text[]) AS field_values(field)
|
|
6887
|
+
WHERE target._cell_meta -> field_values.field ->> 'runId' = $5::text
|
|
6888
|
+
)
|
|
6889
|
+
OR (
|
|
6890
|
+
coalesce(target._attempt_owner_run_id, target._run_id) = $10::text
|
|
6891
|
+
AND COALESCE(target._attempt_seq, 0) = $12::integer
|
|
6892
|
+
)
|
|
6893
|
+
)
|
|
6894
|
+
AND (
|
|
6895
|
+
target._status <> 'enriched'
|
|
6896
|
+
OR (${targetChangedPatchedCellSql})
|
|
6897
|
+
)
|
|
6898
|
+
),
|
|
6803
6899
|
applied_rows AS (
|
|
6804
6900
|
UPDATE ${sheetTable(session)} AS target
|
|
6805
6901
|
SET _status = 'enriched',
|
|
6806
6902
|
_run_id = $5::text,
|
|
6903
|
+
_writer_run_id = $5::text,
|
|
6904
|
+
_write_version = COALESCE($13::bigint, target._write_version),
|
|
6807
6905
|
_error = NULL,
|
|
6808
6906
|
_attempt_id = $9::text,
|
|
6809
6907
|
_attempt_owner_run_id = $10::text,
|
|
@@ -6815,6 +6913,9 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
|
|
|
6815
6913
|
FROM matched_updates AS updates, ${sheetTable(session)} AS prev
|
|
6816
6914
|
WHERE target._key = updates.matched_key
|
|
6817
6915
|
AND prev._key = target._key
|
|
6916
|
+
AND NOT EXISTS (SELECT 1 FROM same_version_conflicts)
|
|
6917
|
+
AND ($13::bigint IS NULL OR target._write_version = $13::bigint)
|
|
6918
|
+
AND ($13::bigint IS NOT NULL OR target._status <> 'enriched')
|
|
6818
6919
|
AND NOT (${newerTerminalRuntimeSheetRowSql('target', '$11::timestamptz', '$12::integer', '$9::text', '$10::text')})
|
|
6819
6920
|
AND NOT (${sameOwnerTerminalEpochSql})
|
|
6820
6921
|
AND (
|
|
@@ -6842,15 +6943,17 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
|
|
|
6842
6943
|
FROM applied_rows
|
|
6843
6944
|
),
|
|
6844
6945
|
fenced_rows AS (
|
|
6845
|
-
SELECT updates.
|
|
6946
|
+
SELECT updates.submitted_key AS _key
|
|
6846
6947
|
FROM matched_updates AS updates
|
|
6847
6948
|
JOIN ${sheetTable(session)} AS target
|
|
6848
6949
|
ON target._key = updates.matched_key
|
|
6849
6950
|
WHERE NOT EXISTS (
|
|
6850
6951
|
SELECT 1 FROM applied_rows WHERE applied_rows._key = updates.matched_key
|
|
6851
|
-
|
|
6952
|
+
)
|
|
6852
6953
|
AND (
|
|
6853
|
-
($
|
|
6954
|
+
($13::bigint IS NOT NULL AND target._write_version <> $13::bigint)
|
|
6955
|
+
OR ($13::bigint IS NULL AND target._status = 'enriched')
|
|
6956
|
+
OR (${newerTerminalRuntimeSheetRowSql('target', '$11::timestamptz', '$12::integer', '$9::text', '$10::text')})
|
|
6854
6957
|
OR NOT (
|
|
6855
6958
|
($9::text IS NULL AND target._run_id = $5::text)
|
|
6856
6959
|
OR ($9::text IS NOT NULL AND ${activeRuntimeSheetAttemptFenceSql('target', '$9::text', '$10::text', '$11::timestamptz', '$12::integer')})
|
|
@@ -6941,7 +7044,8 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
|
|
|
6941
7044
|
)
|
|
6942
7045
|
SELECT
|
|
6943
7046
|
(SELECT count(*)::int FROM applied_rows) AS updated,
|
|
6944
|
-
coalesce((SELECT array_agg(_key) FROM fenced_rows), '{}'::text[]) AS fenced_keys
|
|
7047
|
+
coalesce((SELECT array_agg(_key) FROM fenced_rows), '{}'::text[]) AS fenced_keys,
|
|
7048
|
+
coalesce((SELECT array_agg(_key) FROM same_version_conflicts), '{}'::text[]) AS conflict_keys`,
|
|
6945
7049
|
[
|
|
6946
7050
|
chunkKeys,
|
|
6947
7051
|
chunkInputIndexes,
|
|
@@ -6955,12 +7059,14 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
|
|
|
6955
7059
|
input.attemptOwnerRunId,
|
|
6956
7060
|
input.attemptExpiresAt,
|
|
6957
7061
|
input.attemptSeq,
|
|
7062
|
+
input.writeVersion,
|
|
6958
7063
|
],
|
|
6959
7064
|
);
|
|
6960
7065
|
updated += Number(rows[0]?.updated ?? 0);
|
|
6961
7066
|
fencedKeys.push(...(rows[0]?.fenced_keys ?? []));
|
|
7067
|
+
conflictKeys.push(...(rows[0]?.conflict_keys ?? []));
|
|
6962
7068
|
}
|
|
6963
|
-
return { updated, fencedKeys };
|
|
7069
|
+
return { updated, fencedKeys, conflictKeys };
|
|
6964
7070
|
}
|
|
6965
7071
|
|
|
6966
7072
|
async function insertMissingCompletedMapRowChunks(
|
|
@@ -7140,6 +7246,7 @@ async function failRuntimeMapRowChunks(
|
|
|
7140
7246
|
input: {
|
|
7141
7247
|
chunks: RuntimePreparedFailedRow[][];
|
|
7142
7248
|
physicalUpdateSetSql: string;
|
|
7249
|
+
physicalColumnProjections: PhysicalSheetColumnProjection[];
|
|
7143
7250
|
forceTerminal?: boolean;
|
|
7144
7251
|
runId: string;
|
|
7145
7252
|
attemptId: string | null;
|
|
@@ -7150,15 +7257,21 @@ async function failRuntimeMapRowChunks(
|
|
|
7150
7257
|
normalizedPlayName: string;
|
|
7151
7258
|
normalizedTableNamespace: string;
|
|
7152
7259
|
},
|
|
7153
|
-
): Promise<
|
|
7260
|
+
): Promise<RuntimeMapRowsMutationResult> {
|
|
7154
7261
|
let updated = 0;
|
|
7155
7262
|
const fencedKeys: string[] = [];
|
|
7263
|
+
const conflictKeys: string[] = [];
|
|
7156
7264
|
for (const chunk of input.chunks) {
|
|
7157
7265
|
const chunkKeys = chunk.map((row) => row.key);
|
|
7158
7266
|
const chunkInputIndexes = chunk.map((row) => row.input_index);
|
|
7159
7267
|
const chunkDataPatches = chunk.map((row) => row.data_patch_json);
|
|
7160
7268
|
const chunkCellMetaPatches = chunk.map((row) => row.cell_meta_patch_json);
|
|
7161
7269
|
const chunkErrors = chunk.map((row) => row.error);
|
|
7270
|
+
const targetChangedPatchedCellSql = changedPatchedCellSql(
|
|
7271
|
+
'target',
|
|
7272
|
+
'updates.data_patch',
|
|
7273
|
+
input.physicalColumnProjections,
|
|
7274
|
+
);
|
|
7162
7275
|
// Per-field failed-cell counts for the column summary, computed from the
|
|
7163
7276
|
// cell meta patches (a failed row records exactly which cell failed).
|
|
7164
7277
|
const failedCellCounts = new Map<string, number>();
|
|
@@ -7180,6 +7293,7 @@ async function failRuntimeMapRowChunks(
|
|
|
7180
7293
|
const { rows } = await client.query<{
|
|
7181
7294
|
updated?: number;
|
|
7182
7295
|
fenced_keys?: string[];
|
|
7296
|
+
conflict_keys?: string[];
|
|
7183
7297
|
}>(
|
|
7184
7298
|
`WITH updates AS (
|
|
7185
7299
|
SELECT key_values._key,
|
|
@@ -7213,6 +7327,32 @@ async function failRuntimeMapRowChunks(
|
|
|
7213
7327
|
)
|
|
7214
7328
|
ORDER BY target._key, (target._key = updates._key) DESC
|
|
7215
7329
|
),
|
|
7330
|
+
same_version_conflicts AS (
|
|
7331
|
+
SELECT updates.matched_key AS _key
|
|
7332
|
+
FROM matched_updates AS updates
|
|
7333
|
+
JOIN ${sheetTable(session)} AS target
|
|
7334
|
+
ON target._key = updates.matched_key
|
|
7335
|
+
WHERE $16::bigint IS NOT NULL
|
|
7336
|
+
AND target._write_version = $16::bigint
|
|
7337
|
+
AND target._run_id = $6::text
|
|
7338
|
+
AND target._status IN ('enriched', 'failed')
|
|
7339
|
+
AND (
|
|
7340
|
+
EXISTS (
|
|
7341
|
+
SELECT 1
|
|
7342
|
+
FROM unnest($9::text[]) AS field_values(field)
|
|
7343
|
+
WHERE target._cell_meta -> field_values.field ->> 'runId' = $6::text
|
|
7344
|
+
)
|
|
7345
|
+
OR (
|
|
7346
|
+
coalesce(target._attempt_owner_run_id, target._run_id) = $12::text
|
|
7347
|
+
AND COALESCE(target._attempt_seq, 0) = $14::integer
|
|
7348
|
+
)
|
|
7349
|
+
)
|
|
7350
|
+
AND (
|
|
7351
|
+
target._status <> 'failed'
|
|
7352
|
+
OR target._error IS DISTINCT FROM updates.error
|
|
7353
|
+
OR (${targetChangedPatchedCellSql})
|
|
7354
|
+
)
|
|
7355
|
+
),
|
|
7216
7356
|
applied_rows AS (
|
|
7217
7357
|
UPDATE ${sheetTable(session)} AS target
|
|
7218
7358
|
SET _status = 'failed',
|
|
@@ -7229,6 +7369,7 @@ async function failRuntimeMapRowChunks(
|
|
|
7229
7369
|
_cell_meta = ${mergeRuntimeCellMetaPatchSql('target._cell_meta', 'updates.cell_meta_patch')}${input.physicalUpdateSetSql}
|
|
7230
7370
|
FROM matched_updates AS updates, ${sheetTable(session)} AS prev
|
|
7231
7371
|
WHERE target._key = updates.matched_key
|
|
7372
|
+
AND NOT EXISTS (SELECT 1 FROM same_version_conflicts)
|
|
7232
7373
|
AND ($16::bigint IS NULL OR target._write_version = $16::bigint)
|
|
7233
7374
|
AND prev._key = target._key
|
|
7234
7375
|
AND ($15::boolean OR target._status <> 'enriched')
|
|
@@ -7237,6 +7378,14 @@ async function failRuntimeMapRowChunks(
|
|
|
7237
7378
|
($11::text IS NULL AND target._run_id = $6::text)
|
|
7238
7379
|
OR ($11::text IS NOT NULL AND ${activeRuntimeSheetAttemptFenceSql('target', '$11::text', '$12::text', '$13::timestamptz', '$14::integer')})
|
|
7239
7380
|
)
|
|
7381
|
+
AND NOT (
|
|
7382
|
+
$16::bigint IS NOT NULL
|
|
7383
|
+
AND target._write_version = $16::bigint
|
|
7384
|
+
AND target._run_id = $6::text
|
|
7385
|
+
AND target._status = 'failed'
|
|
7386
|
+
AND target._error IS NOT DISTINCT FROM updates.error
|
|
7387
|
+
AND NOT (${targetChangedPatchedCellSql})
|
|
7388
|
+
)
|
|
7240
7389
|
RETURNING target._key, prev._status AS prev_status
|
|
7241
7390
|
),
|
|
7242
7391
|
applied_count AS (
|
|
@@ -7326,7 +7475,8 @@ async function failRuntimeMapRowChunks(
|
|
|
7326
7475
|
)
|
|
7327
7476
|
SELECT
|
|
7328
7477
|
(SELECT count(*)::int FROM applied_rows) AS updated,
|
|
7329
|
-
coalesce((SELECT array_agg(_key) FROM fenced_rows), '{}'::text[]) AS fenced_keys
|
|
7478
|
+
coalesce((SELECT array_agg(_key) FROM fenced_rows), '{}'::text[]) AS fenced_keys,
|
|
7479
|
+
coalesce((SELECT array_agg(_key) FROM same_version_conflicts), '{}'::text[]) AS conflict_keys`,
|
|
7330
7480
|
[
|
|
7331
7481
|
chunkKeys,
|
|
7332
7482
|
chunkInputIndexes,
|
|
@@ -7348,8 +7498,9 @@ async function failRuntimeMapRowChunks(
|
|
|
7348
7498
|
);
|
|
7349
7499
|
updated += Number(rows[0]?.updated ?? 0);
|
|
7350
7500
|
fencedKeys.push(...(rows[0]?.fenced_keys ?? []));
|
|
7501
|
+
conflictKeys.push(...(rows[0]?.conflict_keys ?? []));
|
|
7351
7502
|
}
|
|
7352
|
-
return { updated, fencedKeys };
|
|
7503
|
+
return { updated, fencedKeys, conflictKeys };
|
|
7353
7504
|
}
|
|
7354
7505
|
|
|
7355
7506
|
/**
|
|
@@ -7382,6 +7533,13 @@ export async function completeRuntimeMapRows(
|
|
|
7382
7533
|
forceFailedRows?: boolean;
|
|
7383
7534
|
},
|
|
7384
7535
|
): Promise<RuntimeMapRowsWriteResult> {
|
|
7536
|
+
if (input.rows.length === 0) {
|
|
7537
|
+
return classifyRuntimeMapRowsWrite({
|
|
7538
|
+
submittedKeys: [],
|
|
7539
|
+
updated: 0,
|
|
7540
|
+
fencedKeys: [],
|
|
7541
|
+
});
|
|
7542
|
+
}
|
|
7385
7543
|
if (context.dbSessionStrategy === 'gateway_only') {
|
|
7386
7544
|
const keyedRows = input.rows.map((row) => {
|
|
7387
7545
|
if (row.key) return row;
|
|
@@ -7395,13 +7553,27 @@ export async function completeRuntimeMapRows(
|
|
|
7395
7553
|
runId: input.runId,
|
|
7396
7554
|
outputFields: input.outputFields ?? [],
|
|
7397
7555
|
});
|
|
7398
|
-
|
|
7556
|
+
const result = await postRuntimeApi<
|
|
7557
|
+
Partial<RuntimeMapRowsWriteResult> &
|
|
7558
|
+
Pick<RuntimeMapRowsWriteResult, 'updated' | 'fencedKeys'>
|
|
7559
|
+
>(context, {
|
|
7399
7560
|
action: 'runtime_sheet_complete_map_rows',
|
|
7400
7561
|
input: { ...input, rows },
|
|
7401
7562
|
});
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7563
|
+
const submittedKeys = keyedRows
|
|
7564
|
+
.map((row) => row.key)
|
|
7565
|
+
.filter((key): key is string => Boolean(key));
|
|
7566
|
+
const staleKeys =
|
|
7567
|
+
result.staleKeys ?? result.staleDroppedKeys ?? result.fencedKeys;
|
|
7568
|
+
return {
|
|
7569
|
+
...classifyRuntimeMapRowsWrite({
|
|
7570
|
+
submittedKeys,
|
|
7571
|
+
updated: result.updated,
|
|
7572
|
+
fencedKeys: staleKeys,
|
|
7573
|
+
}),
|
|
7574
|
+
// Preserve the gateway's compatibility classification during rollout.
|
|
7575
|
+
fencedKeys: result.fencedKeys,
|
|
7576
|
+
};
|
|
7405
7577
|
}
|
|
7406
7578
|
const sheetContract = augmentSheetContractWithDatasetFields({
|
|
7407
7579
|
contract: input.sheetContract,
|
|
@@ -7441,7 +7613,11 @@ export async function completeRuntimeMapRows(
|
|
|
7441
7613
|
uniqueRows.set(resolvedKey, { ...row, key: resolvedKey });
|
|
7442
7614
|
}
|
|
7443
7615
|
if (uniqueRows.size === 0) {
|
|
7444
|
-
return {
|
|
7616
|
+
return classifyRuntimeMapRowsWrite({
|
|
7617
|
+
submittedKeys: [],
|
|
7618
|
+
updated: 0,
|
|
7619
|
+
fencedKeys: [],
|
|
7620
|
+
});
|
|
7445
7621
|
}
|
|
7446
7622
|
|
|
7447
7623
|
const projections = physicalSheetColumnProjections(sheetContract);
|
|
@@ -7526,14 +7702,16 @@ export async function completeRuntimeMapRows(
|
|
|
7526
7702
|
return {
|
|
7527
7703
|
updated: updated.updated + inserted.inserted,
|
|
7528
7704
|
fencedKeys: updated.fencedKeys,
|
|
7705
|
+
conflictKeys: updated.conflictKeys,
|
|
7529
7706
|
};
|
|
7530
7707
|
})()
|
|
7531
|
-
: { updated: 0, fencedKeys: [] };
|
|
7708
|
+
: { updated: 0, fencedKeys: [], conflictKeys: [] };
|
|
7532
7709
|
const failed =
|
|
7533
7710
|
failedRows.length > 0
|
|
7534
7711
|
? await failRuntimeMapRowChunks(client, session, {
|
|
7535
7712
|
chunks: failedChunks,
|
|
7536
7713
|
physicalUpdateSetSql,
|
|
7714
|
+
physicalColumnProjections: projections,
|
|
7537
7715
|
forceTerminal: input.forceFailedRows === true,
|
|
7538
7716
|
runId: input.runId,
|
|
7539
7717
|
attemptId,
|
|
@@ -7546,13 +7724,22 @@ export async function completeRuntimeMapRows(
|
|
|
7546
7724
|
input.tableNamespace,
|
|
7547
7725
|
),
|
|
7548
7726
|
})
|
|
7549
|
-
: { updated: 0, fencedKeys: [] };
|
|
7550
|
-
|
|
7727
|
+
: { updated: 0, fencedKeys: [], conflictKeys: [] };
|
|
7728
|
+
const conflictKeys = [...completed.conflictKeys, ...failed.conflictKeys];
|
|
7729
|
+
if (conflictKeys.length > 0) {
|
|
7730
|
+
throw new Error(
|
|
7731
|
+
`Runtime Sheet received the same write version with a different terminal payload for row(s): ${[
|
|
7732
|
+
...new Set(conflictKeys),
|
|
7733
|
+
]
|
|
7734
|
+
.slice(0, 10)
|
|
7735
|
+
.join(', ')}.`,
|
|
7736
|
+
);
|
|
7737
|
+
}
|
|
7738
|
+
return classifyRuntimeMapRowsWrite({
|
|
7739
|
+
submittedKeys: uniqueRows.keys(),
|
|
7551
7740
|
updated: completed.updated + failed.updated,
|
|
7552
7741
|
fencedKeys: [...completed.fencedKeys, ...failed.fencedKeys],
|
|
7553
|
-
|
|
7554
|
-
staleDroppedKeys: [...completed.fencedKeys, ...failed.fencedKeys],
|
|
7555
|
-
};
|
|
7742
|
+
});
|
|
7556
7743
|
},
|
|
7557
7744
|
);
|
|
7558
7745
|
}
|