deepline 0.1.243 → 0.1.244
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 +1 -1
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +5 -25
- package/dist/bundling-sources/sdk/src/release.ts +23 -21
- package/dist/bundling-sources/sdk/src/types.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/backend.ts +2 -21
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -6
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +6 -13
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +9 -26
- package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +1 -2
- package/dist/bundling-sources/shared_libs/play-runtime/map-row-identity.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +191 -29
- package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +1 -36
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/index.ts +0 -7
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +1 -12
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-pg.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver.ts +5 -15
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +6 -21
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +3 -4
- package/dist/bundling-sources/shared_libs/plays/artifact-kind-guard.ts +16 -29
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +4 -8
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +21 -712
- package/dist/bundling-sources/shared_libs/plays/bundling/limits.ts +1 -18
- package/dist/cli/index.js +29 -71
- package/dist/cli/index.mjs +29 -71
- package/dist/{compiler-manifest-CYcwzSOJ.d.ts → compiler-manifest-DlE7dnRm.d.mts} +2 -8
- package/dist/{compiler-manifest-CYcwzSOJ.d.mts → compiler-manifest-DlE7dnRm.d.ts} +2 -8
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +24 -22
- package/dist/index.mjs +24 -22
- package/dist/plays/bundle-play-file.d.mts +7 -22
- package/dist/plays/bundle-play-file.d.ts +7 -22
- package/dist/plays/bundle-play-file.mjs +26 -614
- package/package.json +2 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +0 -4504
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +0 -1386
- package/dist/bundling-sources/apps/play-runner-workers/src/durable-object-deploy-handoff.ts +0 -30
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +0 -8385
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/README.md +0 -21
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +0 -251
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/coordinator-progress.ts +0 -289
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/csv-rows.ts +0 -85
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/customer-console.ts +0 -23
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/dataset-handles.ts +0 -528
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +0 -229
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/ledger-event-batches.ts +0 -81
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/live-progress.ts +0 -22
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +0 -920
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-latency-profile.ts +0 -90
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/output-datasets.ts +0 -475
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +0 -570
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/result-dataset-persistence.ts +0 -50
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/row-isolation.ts +0 -5
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/run-work-dispatcher.ts +0 -519
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -107
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +0 -2541
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-http-errors.ts +0 -7
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +0 -227
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/work-budget.ts +0 -130
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/worker-platform-budget.ts +0 -39
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/workflow-preview.ts +0 -26
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime-api-retry.ts +0 -23
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-instance-create.ts +0 -47
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +0 -167
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry.ts +0 -91
- package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +0 -329
- package/dist/bundling-sources/sdk/src/worker-play-entry.ts +0 -240
- package/dist/bundling-sources/shared_libs/play-runtime/governor/coordinator-rate-state-backend.ts +0 -252
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -217
|
@@ -1,519 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
shouldYieldWorkerWorkBudget,
|
|
3
|
-
type WorkerWorkBudgetMeter,
|
|
4
|
-
type WorkerWorkBudgetSnapshot,
|
|
5
|
-
type WorkerWorkBudgetYieldLimits,
|
|
6
|
-
type WorkerWorkBudgetYieldReason,
|
|
7
|
-
} from './work-budget';
|
|
8
|
-
|
|
9
|
-
export type WorkerMapWorkYield = {
|
|
10
|
-
kind: 'map_chunk';
|
|
11
|
-
mapName: string;
|
|
12
|
-
nextChunkIndex: number;
|
|
13
|
-
nextChunkStart: number;
|
|
14
|
-
reason: WorkerWorkBudgetYieldReason;
|
|
15
|
-
attempt: number;
|
|
16
|
-
budget: WorkerWorkBudgetSnapshot;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type WorkerMapWorkYieldResult = {
|
|
20
|
-
// `freshBudget` means the yield crossed a real platform boundary that resets
|
|
21
|
-
// the per-invocation resource budget (subrequests/CPU). Under Cloudflare
|
|
22
|
-
// Workflows a completed `workflowStep.sleep` re-enters `run()` as a NEW
|
|
23
|
-
// Worker invocation with a fresh per-invocation subrequest budget (completed
|
|
24
|
-
// steps replay from the durable cache without re-issuing subrequests), so the
|
|
25
|
-
// native workflow yield reports `true`. It is NOT keyed on isolate identity:
|
|
26
|
-
// Cloudflare reuses warm isolates across invocations, so a fresh-budget
|
|
27
|
-
// resume commonly shares the pre-sleep isolate. When no durable step boundary
|
|
28
|
-
// is available (non-workflow execution), the yield cannot reset the platform
|
|
29
|
-
// budget and reports `false`, which the dispatcher escalates and then fails
|
|
30
|
-
// loudly instead of silently overflowing.
|
|
31
|
-
freshBudget: boolean;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type WorkerMapWorkBatch<TRawRow, TExecutableRow> = {
|
|
35
|
-
rawRows: TRawRow[];
|
|
36
|
-
rows: TExecutableRow[];
|
|
37
|
-
chunkStart: number;
|
|
38
|
-
chunkIndex: number;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export type WorkerRunWorkDispatcherTrace = {
|
|
42
|
-
phase: string;
|
|
43
|
-
ms?: number;
|
|
44
|
-
extra?: Record<string, unknown>;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export type WorkerRunWorkDispatcherOptions = {
|
|
48
|
-
budgetMeter: WorkerWorkBudgetMeter;
|
|
49
|
-
nowMs: () => number;
|
|
50
|
-
yieldLimits?: WorkerWorkBudgetYieldLimits;
|
|
51
|
-
maxStaleYieldAttempts?: number;
|
|
52
|
-
recordTrace?: (trace: WorkerRunWorkDispatcherTrace) => void;
|
|
53
|
-
yieldBetweenChunks?: (
|
|
54
|
-
yieldPoint: WorkerMapWorkYield,
|
|
55
|
-
) => Promise<WorkerMapWorkYieldResult>;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export type WorkerRunMapBatchesInput<TRawRow, TExecutableRow, TResult> = {
|
|
59
|
-
mapName: string;
|
|
60
|
-
chunks: AsyncIterable<TRawRow[]> | Iterable<TRawRow[]>;
|
|
61
|
-
maxConcurrentChunks?: number;
|
|
62
|
-
maxResidentBatchBytes?: number;
|
|
63
|
-
prepareBatch: (
|
|
64
|
-
rawRows: TRawRow[],
|
|
65
|
-
chunkStart: number,
|
|
66
|
-
chunkIndex: number,
|
|
67
|
-
) => TExecutableRow[];
|
|
68
|
-
estimateRawBatchResidentBytes?: (
|
|
69
|
-
rawRows: TRawRow[],
|
|
70
|
-
chunkStart: number,
|
|
71
|
-
chunkIndex: number,
|
|
72
|
-
) => number;
|
|
73
|
-
executeBatch: (
|
|
74
|
-
batch: WorkerMapWorkBatch<TRawRow, TExecutableRow>,
|
|
75
|
-
) => Promise<TResult>;
|
|
76
|
-
estimateBatchSubrequests?: (
|
|
77
|
-
batch: WorkerMapWorkBatch<TRawRow, TExecutableRow>,
|
|
78
|
-
) => number;
|
|
79
|
-
estimateBatchResidentBytes?: (
|
|
80
|
-
batch: WorkerMapWorkBatch<TRawRow, TExecutableRow>,
|
|
81
|
-
) => number;
|
|
82
|
-
onBatchComplete: (
|
|
83
|
-
result: TResult,
|
|
84
|
-
batch: WorkerMapWorkBatch<TRawRow, TExecutableRow>,
|
|
85
|
-
) => Promise<boolean | void> | boolean | void;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export type WorkerRunMapBatchesResult = {
|
|
89
|
-
chunksSeen: number;
|
|
90
|
-
chunksExecuted: number;
|
|
91
|
-
yieldPoints: WorkerMapWorkYield[];
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export class WorkerBudgetExhaustedError extends Error {
|
|
95
|
-
readonly mapName: string;
|
|
96
|
-
readonly reason: WorkerWorkBudgetYieldReason;
|
|
97
|
-
readonly attempts: number;
|
|
98
|
-
readonly budget: WorkerWorkBudgetSnapshot;
|
|
99
|
-
|
|
100
|
-
constructor(input: {
|
|
101
|
-
mapName: string;
|
|
102
|
-
reason: WorkerWorkBudgetYieldReason;
|
|
103
|
-
attempts: number;
|
|
104
|
-
budget: WorkerWorkBudgetSnapshot;
|
|
105
|
-
}) {
|
|
106
|
-
super(
|
|
107
|
-
`Runtime dispatcher cannot continue ctx.dataset("${input.mapName}") after ${input.reason} budget pressure without a fresh platform budget.`,
|
|
108
|
-
);
|
|
109
|
-
this.name = 'WorkerBudgetExhaustedError';
|
|
110
|
-
this.mapName = input.mapName;
|
|
111
|
-
this.reason = input.reason;
|
|
112
|
-
this.attempts = input.attempts;
|
|
113
|
-
this.budget = input.budget;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const DEFAULT_STALE_YIELD_ATTEMPTS = 3;
|
|
118
|
-
export const WORKER_RUN_DISPATCHER_DEFAULT_MAX_RESIDENT_ROW_BYTES =
|
|
119
|
-
32 * 1024 * 1024;
|
|
120
|
-
|
|
121
|
-
export class WorkerRunWorkDispatcher {
|
|
122
|
-
constructor(private readonly options: WorkerRunWorkDispatcherOptions) {}
|
|
123
|
-
|
|
124
|
-
private async yieldForBudgetPressure(input: {
|
|
125
|
-
mapName: string;
|
|
126
|
-
chunkIndex: number;
|
|
127
|
-
chunkStart: number;
|
|
128
|
-
reason: WorkerWorkBudgetYieldReason;
|
|
129
|
-
snapshot: WorkerWorkBudgetSnapshot;
|
|
130
|
-
startedAt: number;
|
|
131
|
-
yieldPoints: WorkerMapWorkYield[];
|
|
132
|
-
}): Promise<void> {
|
|
133
|
-
const maxAttempts =
|
|
134
|
-
this.options.maxStaleYieldAttempts ?? DEFAULT_STALE_YIELD_ATTEMPTS;
|
|
135
|
-
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
136
|
-
const yieldPoint: WorkerMapWorkYield = {
|
|
137
|
-
kind: 'map_chunk',
|
|
138
|
-
mapName: input.mapName,
|
|
139
|
-
nextChunkIndex: input.chunkIndex,
|
|
140
|
-
nextChunkStart: input.chunkStart,
|
|
141
|
-
reason: input.reason,
|
|
142
|
-
attempt,
|
|
143
|
-
budget: input.snapshot,
|
|
144
|
-
};
|
|
145
|
-
input.yieldPoints.push(yieldPoint);
|
|
146
|
-
this.options.recordTrace?.({
|
|
147
|
-
phase: 'runner.work_dispatcher.yield',
|
|
148
|
-
ms: this.options.nowMs() - input.startedAt,
|
|
149
|
-
extra: {
|
|
150
|
-
mapName: input.mapName,
|
|
151
|
-
nextChunkIndex: yieldPoint.nextChunkIndex,
|
|
152
|
-
nextChunkStart: yieldPoint.nextChunkStart,
|
|
153
|
-
reason: yieldPoint.reason,
|
|
154
|
-
attempt: yieldPoint.attempt,
|
|
155
|
-
budgetCounts: yieldPoint.budget.counts,
|
|
156
|
-
elapsedMs: yieldPoint.budget.elapsedMs,
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
const yieldResult = await this.options.yieldBetweenChunks?.(yieldPoint);
|
|
160
|
-
if (yieldResult?.freshBudget) {
|
|
161
|
-
this.options.budgetMeter.resetWindow();
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
this.options.recordTrace?.({
|
|
165
|
-
phase: 'runner.work_dispatcher.stale_yield',
|
|
166
|
-
ms: this.options.nowMs() - input.startedAt,
|
|
167
|
-
extra: {
|
|
168
|
-
mapName: input.mapName,
|
|
169
|
-
nextChunkIndex: yieldPoint.nextChunkIndex,
|
|
170
|
-
nextChunkStart: yieldPoint.nextChunkStart,
|
|
171
|
-
reason: yieldPoint.reason,
|
|
172
|
-
attempt: yieldPoint.attempt,
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
if (attempt === maxAttempts - 1) {
|
|
176
|
-
throw new WorkerBudgetExhaustedError({
|
|
177
|
-
mapName: input.mapName,
|
|
178
|
-
reason: yieldPoint.reason,
|
|
179
|
-
attempts: maxAttempts,
|
|
180
|
-
budget: yieldPoint.budget,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async runMapBatches<TRawRow, TExecutableRow, TResult>(
|
|
187
|
-
input: WorkerRunMapBatchesInput<TRawRow, TExecutableRow, TResult>,
|
|
188
|
-
): Promise<WorkerRunMapBatchesResult> {
|
|
189
|
-
const startedAt = this.options.nowMs();
|
|
190
|
-
const maxConcurrentChunks = Math.max(
|
|
191
|
-
1,
|
|
192
|
-
Math.floor(input.maxConcurrentChunks ?? 1),
|
|
193
|
-
);
|
|
194
|
-
let chunkStart = 0;
|
|
195
|
-
let chunkIndex = 0;
|
|
196
|
-
let chunksSeen = 0;
|
|
197
|
-
let chunksExecuted = 0;
|
|
198
|
-
const yieldPoints: WorkerMapWorkYield[] = [];
|
|
199
|
-
const iterator = toAsyncIterator(input.chunks);
|
|
200
|
-
let next: IteratorResult<TRawRow[]> | null = null;
|
|
201
|
-
const maxResidentBatchBytes =
|
|
202
|
-
input.maxResidentBatchBytes ??
|
|
203
|
-
WORKER_RUN_DISPATCHER_DEFAULT_MAX_RESIDENT_ROW_BYTES;
|
|
204
|
-
const peekNext = async (): Promise<IteratorResult<TRawRow[]>> => {
|
|
205
|
-
next ??= await iterator.next();
|
|
206
|
-
return next;
|
|
207
|
-
};
|
|
208
|
-
const consumeNext = () => {
|
|
209
|
-
next = null;
|
|
210
|
-
};
|
|
211
|
-
type InFlightChunk = {
|
|
212
|
-
batch: WorkerMapWorkBatch<TRawRow, TExecutableRow>;
|
|
213
|
-
projectedSubrequests: number;
|
|
214
|
-
residentBytes: number;
|
|
215
|
-
result: Promise<TResult>;
|
|
216
|
-
};
|
|
217
|
-
const inFlight: InFlightChunk[] = [];
|
|
218
|
-
let shouldStop = false;
|
|
219
|
-
const inFlightResidentBytes = () =>
|
|
220
|
-
inFlight.reduce((total, chunk) => total + chunk.residentBytes, 0);
|
|
221
|
-
const estimateResidentBytes = (
|
|
222
|
-
batch: WorkerMapWorkBatch<TRawRow, TExecutableRow>,
|
|
223
|
-
): number =>
|
|
224
|
-
Math.max(0, Math.floor(input.estimateBatchResidentBytes?.(batch) ?? 0));
|
|
225
|
-
const estimateRawResidentBytes = (
|
|
226
|
-
rawRows: TRawRow[],
|
|
227
|
-
start: number,
|
|
228
|
-
index: number,
|
|
229
|
-
): number =>
|
|
230
|
-
Math.max(
|
|
231
|
-
0,
|
|
232
|
-
Math.floor(
|
|
233
|
-
input.estimateRawBatchResidentBytes?.(rawRows, start, index) ?? 0,
|
|
234
|
-
),
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
const launchAvailableChunks = async () => {
|
|
238
|
-
while (
|
|
239
|
-
maxConcurrentChunks > 1 &&
|
|
240
|
-
!shouldStop &&
|
|
241
|
-
inFlight.length < maxConcurrentChunks
|
|
242
|
-
) {
|
|
243
|
-
const admissionYieldDecision = shouldYieldWorkerWorkBudget({
|
|
244
|
-
meter: this.options.budgetMeter,
|
|
245
|
-
limits: this.options.yieldLimits,
|
|
246
|
-
});
|
|
247
|
-
if (
|
|
248
|
-
admissionYieldDecision.shouldYield &&
|
|
249
|
-
admissionYieldDecision.reason
|
|
250
|
-
) {
|
|
251
|
-
if (inFlight.length > 0) return;
|
|
252
|
-
await this.yieldForBudgetPressure({
|
|
253
|
-
mapName: input.mapName,
|
|
254
|
-
chunkIndex,
|
|
255
|
-
chunkStart,
|
|
256
|
-
reason: admissionYieldDecision.reason,
|
|
257
|
-
snapshot: admissionYieldDecision.snapshot,
|
|
258
|
-
startedAt,
|
|
259
|
-
yieldPoints,
|
|
260
|
-
});
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
const current = await peekNext();
|
|
264
|
-
if (current.done) return;
|
|
265
|
-
const rawRows = current.value;
|
|
266
|
-
if (rawRows.length === 0) {
|
|
267
|
-
chunksSeen += 1;
|
|
268
|
-
consumeNext();
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
const rawResidentBytes = estimateRawResidentBytes(
|
|
272
|
-
rawRows,
|
|
273
|
-
chunkStart,
|
|
274
|
-
chunkIndex,
|
|
275
|
-
);
|
|
276
|
-
if (
|
|
277
|
-
rawResidentBytes > 0 &&
|
|
278
|
-
inFlightResidentBytes() + rawResidentBytes > maxResidentBatchBytes
|
|
279
|
-
) {
|
|
280
|
-
if (inFlight.length > 0) return;
|
|
281
|
-
throw new WorkerResidentRowBudgetExceededError({
|
|
282
|
-
mapName: input.mapName,
|
|
283
|
-
chunkIndex,
|
|
284
|
-
residentBytes: rawResidentBytes,
|
|
285
|
-
limitBytes: maxResidentBatchBytes,
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
const rows = input.prepareBatch(rawRows, chunkStart, chunkIndex);
|
|
289
|
-
if (rows.length === 0) {
|
|
290
|
-
chunksSeen += 1;
|
|
291
|
-
consumeNext();
|
|
292
|
-
chunkStart += rawRows.length;
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
const batch = {
|
|
296
|
-
rawRows,
|
|
297
|
-
rows,
|
|
298
|
-
chunkStart,
|
|
299
|
-
chunkIndex,
|
|
300
|
-
};
|
|
301
|
-
const residentBytes = estimateResidentBytes(batch);
|
|
302
|
-
const projectedResidentBytes = inFlightResidentBytes() + residentBytes;
|
|
303
|
-
if (projectedResidentBytes > maxResidentBatchBytes) {
|
|
304
|
-
if (inFlight.length > 0) return;
|
|
305
|
-
throw new WorkerResidentRowBudgetExceededError({
|
|
306
|
-
mapName: input.mapName,
|
|
307
|
-
chunkIndex,
|
|
308
|
-
residentBytes,
|
|
309
|
-
limitBytes: maxResidentBatchBytes,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
const projectedSubrequests = Math.max(
|
|
313
|
-
0,
|
|
314
|
-
Math.floor(input.estimateBatchSubrequests?.(batch) ?? 0),
|
|
315
|
-
);
|
|
316
|
-
const alreadyProjected = inFlight.reduce(
|
|
317
|
-
(total, chunk) => total + chunk.projectedSubrequests,
|
|
318
|
-
0,
|
|
319
|
-
);
|
|
320
|
-
const preChunkYieldDecision = shouldYieldWorkerWorkBudget({
|
|
321
|
-
meter: this.options.budgetMeter,
|
|
322
|
-
limits: this.options.yieldLimits,
|
|
323
|
-
projectedSubrequests: alreadyProjected + projectedSubrequests,
|
|
324
|
-
});
|
|
325
|
-
if (preChunkYieldDecision.shouldYield && preChunkYieldDecision.reason) {
|
|
326
|
-
if (inFlight.length > 0) return;
|
|
327
|
-
await this.yieldForBudgetPressure({
|
|
328
|
-
mapName: input.mapName,
|
|
329
|
-
chunkIndex,
|
|
330
|
-
chunkStart,
|
|
331
|
-
reason: preChunkYieldDecision.reason,
|
|
332
|
-
snapshot: preChunkYieldDecision.snapshot,
|
|
333
|
-
startedAt,
|
|
334
|
-
yieldPoints,
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
const result = input.executeBatch(batch);
|
|
338
|
-
result.catch(() => undefined);
|
|
339
|
-
inFlight.push({
|
|
340
|
-
batch,
|
|
341
|
-
projectedSubrequests,
|
|
342
|
-
residentBytes,
|
|
343
|
-
result,
|
|
344
|
-
});
|
|
345
|
-
chunksSeen += 1;
|
|
346
|
-
consumeNext();
|
|
347
|
-
chunkStart += rawRows.length;
|
|
348
|
-
chunkIndex += 1;
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
if (maxConcurrentChunks <= 1) {
|
|
353
|
-
while (true) {
|
|
354
|
-
const current = await peekNext();
|
|
355
|
-
if (current.done) break;
|
|
356
|
-
const rawRows = current.value;
|
|
357
|
-
chunksSeen += 1;
|
|
358
|
-
consumeNext();
|
|
359
|
-
if (rawRows.length === 0) {
|
|
360
|
-
continue;
|
|
361
|
-
}
|
|
362
|
-
const rows = input.prepareBatch(rawRows, chunkStart, chunkIndex);
|
|
363
|
-
if (rows.length === 0) {
|
|
364
|
-
chunkStart += rawRows.length;
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
const batch = {
|
|
368
|
-
rawRows,
|
|
369
|
-
rows,
|
|
370
|
-
chunkStart,
|
|
371
|
-
chunkIndex,
|
|
372
|
-
};
|
|
373
|
-
const preChunkYieldDecision = shouldYieldWorkerWorkBudget({
|
|
374
|
-
meter: this.options.budgetMeter,
|
|
375
|
-
limits: this.options.yieldLimits,
|
|
376
|
-
projectedSubrequests: input.estimateBatchSubrequests?.(batch),
|
|
377
|
-
});
|
|
378
|
-
if (preChunkYieldDecision.shouldYield && preChunkYieldDecision.reason) {
|
|
379
|
-
await this.yieldForBudgetPressure({
|
|
380
|
-
mapName: input.mapName,
|
|
381
|
-
chunkIndex,
|
|
382
|
-
chunkStart,
|
|
383
|
-
reason: preChunkYieldDecision.reason,
|
|
384
|
-
snapshot: preChunkYieldDecision.snapshot,
|
|
385
|
-
startedAt,
|
|
386
|
-
yieldPoints,
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
const result = await input.executeBatch(batch);
|
|
390
|
-
chunksExecuted += 1;
|
|
391
|
-
const shouldStop = await input.onBatchComplete(result, batch);
|
|
392
|
-
chunkStart += rawRows.length;
|
|
393
|
-
chunkIndex += 1;
|
|
394
|
-
if (shouldStop === true) break;
|
|
395
|
-
const upcoming = await peekNext();
|
|
396
|
-
|
|
397
|
-
const yieldDecision = shouldYieldWorkerWorkBudget({
|
|
398
|
-
meter: this.options.budgetMeter,
|
|
399
|
-
limits: this.options.yieldLimits,
|
|
400
|
-
});
|
|
401
|
-
if (
|
|
402
|
-
!upcoming.done &&
|
|
403
|
-
yieldDecision.shouldYield &&
|
|
404
|
-
yieldDecision.reason
|
|
405
|
-
) {
|
|
406
|
-
await this.yieldForBudgetPressure({
|
|
407
|
-
mapName: input.mapName,
|
|
408
|
-
chunkIndex,
|
|
409
|
-
chunkStart,
|
|
410
|
-
reason: yieldDecision.reason,
|
|
411
|
-
snapshot: yieldDecision.snapshot,
|
|
412
|
-
startedAt,
|
|
413
|
-
yieldPoints,
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
} else {
|
|
418
|
-
// Bounded worker pool: keep up to K `executeBatch` calls in flight, but
|
|
419
|
-
// funnel their results through a single serialized `onBatchComplete` chain
|
|
420
|
-
// in submission (FIFO) order so counter aggregation, preview/cache
|
|
421
|
-
// ordering, and progress ordering stay deterministic regardless of which
|
|
422
|
-
// chunk's provider work settles first.
|
|
423
|
-
let pendingError: unknown = null;
|
|
424
|
-
const drainInFlight = async () => {
|
|
425
|
-
if (inFlight.length > 0) {
|
|
426
|
-
await Promise.allSettled(inFlight.map((chunk) => chunk.result));
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
const launchOrAbort = async () => {
|
|
430
|
-
try {
|
|
431
|
-
await launchAvailableChunks();
|
|
432
|
-
} catch (error) {
|
|
433
|
-
pendingError = error;
|
|
434
|
-
shouldStop = true;
|
|
435
|
-
await drainInFlight();
|
|
436
|
-
throw error;
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
await launchOrAbort();
|
|
440
|
-
while (inFlight.length > 0) {
|
|
441
|
-
const nextChunk = inFlight.shift()!;
|
|
442
|
-
try {
|
|
443
|
-
const result = await nextChunk.result;
|
|
444
|
-
chunksExecuted += 1;
|
|
445
|
-
const batchShouldStop = await input.onBatchComplete(
|
|
446
|
-
result,
|
|
447
|
-
nextChunk.batch,
|
|
448
|
-
);
|
|
449
|
-
if (batchShouldStop === true) {
|
|
450
|
-
shouldStop = true;
|
|
451
|
-
}
|
|
452
|
-
} catch (error) {
|
|
453
|
-
// A chunk error aborts the map (matching the sequential path), but we
|
|
454
|
-
// fail loud only after the chunks already in flight settle so no
|
|
455
|
-
// detached executeBatch promise keeps writing behind our back.
|
|
456
|
-
pendingError = error;
|
|
457
|
-
shouldStop = true;
|
|
458
|
-
break;
|
|
459
|
-
}
|
|
460
|
-
if (!shouldStop) {
|
|
461
|
-
await launchOrAbort();
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
await drainInFlight();
|
|
465
|
-
if (pendingError) throw pendingError;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
this.options.recordTrace?.({
|
|
469
|
-
phase: 'runner.work_dispatcher.total',
|
|
470
|
-
ms: this.options.nowMs() - startedAt,
|
|
471
|
-
extra: {
|
|
472
|
-
mapName: input.mapName,
|
|
473
|
-
chunksSeen,
|
|
474
|
-
chunksExecuted,
|
|
475
|
-
maxConcurrentChunks,
|
|
476
|
-
yieldPoints: yieldPoints.length,
|
|
477
|
-
},
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
return { chunksSeen, chunksExecuted, yieldPoints };
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
export class WorkerResidentRowBudgetExceededError extends Error {
|
|
485
|
-
readonly mapName: string;
|
|
486
|
-
readonly chunkIndex: number;
|
|
487
|
-
readonly residentBytes: number;
|
|
488
|
-
readonly limitBytes: number;
|
|
489
|
-
|
|
490
|
-
constructor(input: {
|
|
491
|
-
mapName: string;
|
|
492
|
-
chunkIndex: number;
|
|
493
|
-
residentBytes: number;
|
|
494
|
-
limitBytes: number;
|
|
495
|
-
}) {
|
|
496
|
-
super(
|
|
497
|
-
`Runtime dispatcher cannot admit ctx.dataset("${input.mapName}") chunk ${input.chunkIndex}: estimated resident row payload ${input.residentBytes} bytes exceeds the ${input.limitBytes} byte cap.`,
|
|
498
|
-
);
|
|
499
|
-
this.name = 'WorkerResidentRowBudgetExceededError';
|
|
500
|
-
this.mapName = input.mapName;
|
|
501
|
-
this.chunkIndex = input.chunkIndex;
|
|
502
|
-
this.residentBytes = input.residentBytes;
|
|
503
|
-
this.limitBytes = input.limitBytes;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function toAsyncIterator<T>(
|
|
508
|
-
input: AsyncIterable<T> | Iterable<T>,
|
|
509
|
-
): AsyncIterator<T> {
|
|
510
|
-
if (Symbol.asyncIterator in input) {
|
|
511
|
-
return input[Symbol.asyncIterator]();
|
|
512
|
-
}
|
|
513
|
-
const iterator = input[Symbol.iterator]();
|
|
514
|
-
return {
|
|
515
|
-
async next() {
|
|
516
|
-
return iterator.next();
|
|
517
|
-
},
|
|
518
|
-
};
|
|
519
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-play Worker tool batch executor.
|
|
3
|
-
*
|
|
4
|
-
* This is intentionally local to the Worker bundle. Pulling the shared
|
|
5
|
-
* executor module back in would widen the bundle graph for a tiny helper.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type ToolBatchRequest = {
|
|
9
|
-
runId: string;
|
|
10
|
-
orgId: string;
|
|
11
|
-
toolId: string;
|
|
12
|
-
operation: string;
|
|
13
|
-
provider: string;
|
|
14
|
-
items: Array<{
|
|
15
|
-
itemKey: string;
|
|
16
|
-
payload: Record<string, unknown>;
|
|
17
|
-
inputHash?: string | null;
|
|
18
|
-
}>;
|
|
19
|
-
waterfallId?: string | null;
|
|
20
|
-
stageId?: string | null;
|
|
21
|
-
fieldName?: string | null;
|
|
22
|
-
mapName?: string | null;
|
|
23
|
-
chunkIndex?: number | null;
|
|
24
|
-
userProvidedRateLimitKey?: string | null;
|
|
25
|
-
providerBatchSize: number;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
type ToolBatchItemResult = {
|
|
29
|
-
itemKey: string;
|
|
30
|
-
result: unknown;
|
|
31
|
-
cached?: boolean;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export function createToolBatchExecutor(input: {
|
|
35
|
-
executeProviderBatch(batch: {
|
|
36
|
-
request: ToolBatchRequest;
|
|
37
|
-
batchIndex: number;
|
|
38
|
-
idempotencyKeys: string[];
|
|
39
|
-
rateLimitKey: string;
|
|
40
|
-
items: ToolBatchRequest['items'];
|
|
41
|
-
}): Promise<ToolBatchItemResult[]>;
|
|
42
|
-
}) {
|
|
43
|
-
return {
|
|
44
|
-
async executeToolBatch(request: ToolBatchRequest) {
|
|
45
|
-
const providerBatchSize = Math.max(
|
|
46
|
-
1,
|
|
47
|
-
Math.floor(request.providerBatchSize),
|
|
48
|
-
);
|
|
49
|
-
const batches: Array<ToolBatchRequest['items']> = [];
|
|
50
|
-
for (
|
|
51
|
-
let index = 0;
|
|
52
|
-
index < request.items.length;
|
|
53
|
-
index += providerBatchSize
|
|
54
|
-
) {
|
|
55
|
-
batches.push(request.items.slice(index, index + providerBatchSize));
|
|
56
|
-
}
|
|
57
|
-
const results: ToolBatchItemResult[] = [];
|
|
58
|
-
for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
|
|
59
|
-
const items = batches[batchIndex]!;
|
|
60
|
-
results.push(
|
|
61
|
-
...(await input.executeProviderBatch({
|
|
62
|
-
request,
|
|
63
|
-
batchIndex,
|
|
64
|
-
items,
|
|
65
|
-
rateLimitKey: [
|
|
66
|
-
request.orgId,
|
|
67
|
-
request.provider,
|
|
68
|
-
request.operation,
|
|
69
|
-
request.userProvidedRateLimitKey ?? '',
|
|
70
|
-
].join(':'),
|
|
71
|
-
idempotencyKeys: items.map((item) =>
|
|
72
|
-
[
|
|
73
|
-
request.runId,
|
|
74
|
-
request.mapName ?? '',
|
|
75
|
-
request.chunkIndex ?? '',
|
|
76
|
-
item.itemKey,
|
|
77
|
-
request.fieldName ?? '',
|
|
78
|
-
request.waterfallId ?? '',
|
|
79
|
-
request.stageId ?? '',
|
|
80
|
-
item.inputHash ?? stableStringify(item.payload),
|
|
81
|
-
].join(':'),
|
|
82
|
-
),
|
|
83
|
-
})),
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
runId: request.runId,
|
|
88
|
-
toolId: request.toolId,
|
|
89
|
-
operation: request.operation,
|
|
90
|
-
provider: request.provider,
|
|
91
|
-
batchCount: batches.length,
|
|
92
|
-
itemCount: request.items.length,
|
|
93
|
-
results,
|
|
94
|
-
};
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function stableStringify(value: unknown): string {
|
|
100
|
-
if (value === null || typeof value !== 'object') return JSON.stringify(value);
|
|
101
|
-
if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
|
|
102
|
-
const record = value as Record<string, unknown>;
|
|
103
|
-
return `{${Object.keys(record)
|
|
104
|
-
.sort()
|
|
105
|
-
.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
|
|
106
|
-
.join(',')}}`;
|
|
107
|
-
}
|