deepline 0.1.210 → 0.1.211
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +53 -1
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +15 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-latency-profile.ts +90 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +161 -99
- package/dist/bundling-sources/sdk/src/play.ts +2 -0
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +343 -83
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +42 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +5 -1
- package/dist/bundling-sources/shared_libs/play-runtime/single-flight.ts +31 -0
- package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +12 -1
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +41 -5
- package/dist/cli/index.js +93 -12
- package/dist/cli/index.mjs +93 -12
- package/dist/index.d.mts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +22 -5
- package/dist/index.mjs +22 -5
- package/package.json +1 -1
|
@@ -166,6 +166,7 @@ import {
|
|
|
166
166
|
createWorkerWorkBudgetMeter,
|
|
167
167
|
type WorkerWorkBudgetMeter,
|
|
168
168
|
} from './runtime/work-budget';
|
|
169
|
+
import { MapLatencyProfile } from './runtime/map-latency-profile';
|
|
169
170
|
import {
|
|
170
171
|
WORKER_RUN_DISPATCHER_DEFAULT_MAX_RESIDENT_ROW_BYTES,
|
|
171
172
|
WorkerRunWorkDispatcher,
|
|
@@ -4598,6 +4599,11 @@ function createMinimalWorkerCtx(
|
|
|
4598
4599
|
opts?: WorkerMapOptions,
|
|
4599
4600
|
): Promise<unknown> => {
|
|
4600
4601
|
const mapStartedAt = nowMs();
|
|
4602
|
+
const mapLatencyProfile = new MapLatencyProfile(
|
|
4603
|
+
req.testPolicyOverrides?.mapLatencyProfile === true,
|
|
4604
|
+
name,
|
|
4605
|
+
mapStartedAt,
|
|
4606
|
+
);
|
|
4601
4607
|
const mapNodeId = `map:${name}`;
|
|
4602
4608
|
const inputRows = rows;
|
|
4603
4609
|
const rowCountHint = datasetRowCountHint(inputRows);
|
|
@@ -4935,6 +4941,7 @@ function createMinimalWorkerCtx(
|
|
|
4935
4941
|
blockedRows: prepared.blockedRows.length,
|
|
4936
4942
|
},
|
|
4937
4943
|
});
|
|
4944
|
+
mapLatencyProfile.recordPhase('prepare_rows', nowMs() - prepareStartedAt);
|
|
4938
4945
|
const progressTotalRows = rowCountHint ?? chunkRows.length;
|
|
4939
4946
|
const preparedCompletedRows = Math.min(
|
|
4940
4947
|
progressTotalRows,
|
|
@@ -5215,7 +5222,10 @@ function createMinimalWorkerCtx(
|
|
|
5215
5222
|
deserializeDurableValue: deserializeDurableStepValue,
|
|
5216
5223
|
nowMs,
|
|
5217
5224
|
batchGraceMs: req.testPolicyOverrides?.batchGraceMs,
|
|
5218
|
-
recordPerfTrace: (trace) =>
|
|
5225
|
+
recordPerfTrace: (trace) => {
|
|
5226
|
+
mapLatencyProfile.recordToolTrace(trace);
|
|
5227
|
+
recordRunnerPerfTrace({ req, ...trace });
|
|
5228
|
+
},
|
|
5219
5229
|
abortSignal,
|
|
5220
5230
|
onRequestsSettled: reportSettledToolRequests,
|
|
5221
5231
|
callbacks,
|
|
@@ -5505,9 +5515,16 @@ function createMinimalWorkerCtx(
|
|
|
5505
5515
|
if (circuitError) throw circuitError;
|
|
5506
5516
|
const myIndex = idx++;
|
|
5507
5517
|
if (myIndex >= rowsToExecute.length) return;
|
|
5518
|
+
const rowQueueStartedAt = mapLatencyProfile.enabled ? nowMs() : 0;
|
|
5508
5519
|
const rowSlot = await governor.acquireRowSlot({
|
|
5509
5520
|
signal: abortSignal,
|
|
5510
5521
|
});
|
|
5522
|
+
if (mapLatencyProfile.enabled) {
|
|
5523
|
+
mapLatencyProfile.recordRowQueue(nowMs() - rowQueueStartedAt);
|
|
5524
|
+
}
|
|
5525
|
+
const rowExecutionStartedAt = mapLatencyProfile.enabled
|
|
5526
|
+
? nowMs()
|
|
5527
|
+
: 0;
|
|
5511
5528
|
let rowMarkedActive = false;
|
|
5512
5529
|
try {
|
|
5513
5530
|
startedExecutedRows += 1;
|
|
@@ -5744,6 +5761,11 @@ function createMinimalWorkerCtx(
|
|
|
5744
5761
|
// single clean run failure.
|
|
5745
5762
|
}
|
|
5746
5763
|
} finally {
|
|
5764
|
+
if (mapLatencyProfile.enabled) {
|
|
5765
|
+
mapLatencyProfile.recordRowExecution(
|
|
5766
|
+
nowMs() - rowExecutionStartedAt,
|
|
5767
|
+
);
|
|
5768
|
+
}
|
|
5747
5769
|
if (rowMarkedActive) {
|
|
5748
5770
|
activeExecutedRows = Math.max(0, activeExecutedRows - 1);
|
|
5749
5771
|
reportExecutionHeartbeat(false);
|
|
@@ -5796,6 +5818,10 @@ function createMinimalWorkerCtx(
|
|
|
5796
5818
|
concurrency,
|
|
5797
5819
|
},
|
|
5798
5820
|
});
|
|
5821
|
+
mapLatencyProfile.recordPhase(
|
|
5822
|
+
'worker_execution',
|
|
5823
|
+
nowMs() - workersStartedAt,
|
|
5824
|
+
);
|
|
5799
5825
|
const buildRowFailureSamples = () =>
|
|
5800
5826
|
failedRowEntries
|
|
5801
5827
|
.map((failure, executedIndex) =>
|
|
@@ -5923,6 +5949,10 @@ function createMinimalWorkerCtx(
|
|
|
5923
5949
|
rowsToExecute: rowsToExecute.length,
|
|
5924
5950
|
},
|
|
5925
5951
|
});
|
|
5952
|
+
mapLatencyProfile.recordPhase(
|
|
5953
|
+
'persist_rows',
|
|
5954
|
+
nowMs() - persistRowsStartedAt,
|
|
5955
|
+
);
|
|
5926
5956
|
} catch (error) {
|
|
5927
5957
|
if (
|
|
5928
5958
|
workflowStep &&
|
|
@@ -6028,6 +6058,7 @@ function createMinimalWorkerCtx(
|
|
|
6028
6058
|
ms: nowMs() - hashStartedAt,
|
|
6029
6059
|
extra: { mapName: name, chunkIndex, rows: out.length },
|
|
6030
6060
|
});
|
|
6061
|
+
mapLatencyProfile.recordPhase('hash', nowMs() - hashStartedAt);
|
|
6031
6062
|
recordRunnerPerfTrace({
|
|
6032
6063
|
req,
|
|
6033
6064
|
phase: 'runner.map_chunk.total',
|
|
@@ -6643,6 +6674,27 @@ function createMinimalWorkerCtx(
|
|
|
6643
6674
|
chunks: dispatchResult.chunksExecuted,
|
|
6644
6675
|
},
|
|
6645
6676
|
});
|
|
6677
|
+
if (mapLatencyProfile.enabled) {
|
|
6678
|
+
const endedAt = nowMs();
|
|
6679
|
+
const summary = mapLatencyProfile.summary(endedAt, {
|
|
6680
|
+
chunks: dispatchResult.chunksExecuted,
|
|
6681
|
+
rows: totalRowsWritten,
|
|
6682
|
+
});
|
|
6683
|
+
recordRunnerPerfTrace({
|
|
6684
|
+
req,
|
|
6685
|
+
phase: 'runner.map.profile',
|
|
6686
|
+
ms: endedAt - mapStartedAt,
|
|
6687
|
+
extra: summary,
|
|
6688
|
+
});
|
|
6689
|
+
// Local Absurd does not always forward dynamic-worker perf traces. Keep
|
|
6690
|
+
// one bounded copy in the durable run log for the diagnostic command.
|
|
6691
|
+
emitEvent({
|
|
6692
|
+
type: 'log',
|
|
6693
|
+
level: 'info',
|
|
6694
|
+
message: `[perf] map_latency_profile ${JSON.stringify(summary)}`,
|
|
6695
|
+
ts: endedAt,
|
|
6696
|
+
});
|
|
6697
|
+
}
|
|
6646
6698
|
return dataset;
|
|
6647
6699
|
};
|
|
6648
6700
|
|
|
@@ -337,6 +337,21 @@ function countToolSubstep(substep: PlayStaticSubstep): MapToolStats {
|
|
|
337
337
|
minProviderBranchCoalescedCount(branchStats),
|
|
338
338
|
];
|
|
339
339
|
}
|
|
340
|
+
if (substep.kind === 'loop') {
|
|
341
|
+
// The static extractor records every switch arm in `steps` when a loop
|
|
342
|
+
// walks a fixed provider list. Each arm can execute on a different
|
|
343
|
+
// iteration, so using the conditional's max-one-branch estimate would
|
|
344
|
+
// budget a seven-provider waterfall as one provider call per row.
|
|
345
|
+
return substep.steps.reduce<MapToolStats>((stats, step) => {
|
|
346
|
+
const stepStats =
|
|
347
|
+
step.type === 'control_flow' &&
|
|
348
|
+
step.kind === 'conditional' &&
|
|
349
|
+
step.steps.length > 0
|
|
350
|
+
? countToolSubsteps(step.steps)
|
|
351
|
+
: countToolSubstep(step);
|
|
352
|
+
return addStats(stats, stepStats);
|
|
353
|
+
}, [0, 0, 0, 0, 0]);
|
|
354
|
+
}
|
|
340
355
|
return countToolSubsteps(substep.steps);
|
|
341
356
|
}
|
|
342
357
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
type ToolTrace = {
|
|
2
|
+
phase: string;
|
|
3
|
+
ms?: number;
|
|
4
|
+
extra?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type Totals = Record<string, number>;
|
|
8
|
+
|
|
9
|
+
function add(totals: Totals, key: string, ms: number | undefined): void {
|
|
10
|
+
if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) return;
|
|
11
|
+
totals[key] = (totals[key] ?? 0) + ms;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Opt-in map profiler. It aggregates in memory and produces one terminal
|
|
16
|
+
* trace, so normal runs do not pay per-row/per-tool telemetry cost.
|
|
17
|
+
*/
|
|
18
|
+
export class MapLatencyProfile {
|
|
19
|
+
private readonly phaseWorkMs: Totals = {};
|
|
20
|
+
private readonly toolWorkMs: Totals = {};
|
|
21
|
+
private readonly counts: Totals = {};
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
readonly enabled: boolean,
|
|
25
|
+
private readonly mapName: string,
|
|
26
|
+
private readonly startedAtMs: number,
|
|
27
|
+
) {}
|
|
28
|
+
|
|
29
|
+
recordPhase(phase: string, ms: number, count = 1): void {
|
|
30
|
+
if (!this.enabled) return;
|
|
31
|
+
add(this.phaseWorkMs, phase, ms);
|
|
32
|
+
this.counts[`${phase}_count`] =
|
|
33
|
+
(this.counts[`${phase}_count`] ?? 0) + count;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
recordRowQueue(ms: number): void {
|
|
37
|
+
this.recordPhase('row_slot_queue', ms);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
recordRowExecution(ms: number): void {
|
|
41
|
+
this.recordPhase('row_execution', ms);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
recordToolTrace(trace: ToolTrace): void {
|
|
45
|
+
if (!this.enabled) return;
|
|
46
|
+
switch (trace.phase) {
|
|
47
|
+
case 'runner.tool.request':
|
|
48
|
+
add(this.toolWorkMs, 'request_end_to_end', trace.ms);
|
|
49
|
+
this.counts.tool_requests = (this.counts.tool_requests ?? 0) + 1;
|
|
50
|
+
break;
|
|
51
|
+
case 'runner.tool.drain':
|
|
52
|
+
add(this.toolWorkMs, 'drain', trace.ms);
|
|
53
|
+
this.counts.tool_drains = (this.counts.tool_drains ?? 0) + 1;
|
|
54
|
+
break;
|
|
55
|
+
case 'runner.tool.group':
|
|
56
|
+
add(this.toolWorkMs, 'group', trace.ms);
|
|
57
|
+
this.counts.tool_groups = (this.counts.tool_groups ?? 0) + 1;
|
|
58
|
+
break;
|
|
59
|
+
case 'runner.tool.receipt_prepare':
|
|
60
|
+
add(this.toolWorkMs, 'receipt_prepare', trace.ms);
|
|
61
|
+
break;
|
|
62
|
+
case 'runner.tool.admission':
|
|
63
|
+
add(this.toolWorkMs, 'admission_wait', trace.ms);
|
|
64
|
+
break;
|
|
65
|
+
case 'runner.tool.provider':
|
|
66
|
+
add(this.toolWorkMs, 'provider_execute', trace.ms);
|
|
67
|
+
break;
|
|
68
|
+
case 'runner.tool.receipt_complete':
|
|
69
|
+
add(this.toolWorkMs, 'receipt_complete', trace.ms);
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
summary(endedAtMs: number, input: { chunks: number; rows: number }) {
|
|
77
|
+
return {
|
|
78
|
+
mapName: this.mapName,
|
|
79
|
+
mapWallMs: Math.max(0, endedAtMs - this.startedAtMs),
|
|
80
|
+
chunks: input.chunks,
|
|
81
|
+
rows: input.rows,
|
|
82
|
+
// These are summed work durations. They can exceed mapWallMs when rows
|
|
83
|
+
// or provider calls overlap; the terminal trace must not mislabel them
|
|
84
|
+
// as a wall-clock decomposition.
|
|
85
|
+
phaseWorkMs: this.phaseWorkMs,
|
|
86
|
+
toolWorkMs: this.toolWorkMs,
|
|
87
|
+
counts: this.counts,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -2000,8 +2000,14 @@ export class WorkerToolBatchScheduler {
|
|
|
2000
2000
|
toolId: string,
|
|
2001
2001
|
requests: WorkerToolBatchRequest[],
|
|
2002
2002
|
): Promise<void> {
|
|
2003
|
+
const receiptPrepareStartedAt = this.options.nowMs();
|
|
2003
2004
|
const { claimedRequests, deferredClaimedRequests } =
|
|
2004
2005
|
await this.prepareDurableToolRequests(requests);
|
|
2006
|
+
this.options.recordPerfTrace?.({
|
|
2007
|
+
phase: 'runner.tool.receipt_prepare',
|
|
2008
|
+
ms: this.options.nowMs() - receiptPrepareStartedAt,
|
|
2009
|
+
extra: { toolId, requests: requests.length },
|
|
2010
|
+
});
|
|
2005
2011
|
await this.executeClaimedToolRequests(
|
|
2006
2012
|
toolId,
|
|
2007
2013
|
requests.length,
|
|
@@ -2071,6 +2077,7 @@ export class WorkerToolBatchScheduler {
|
|
|
2071
2077
|
// charges tool budget, holds a global tool-concurrency slot, and
|
|
2072
2078
|
// applies per-(org,provider) pacing before the call runs.
|
|
2073
2079
|
let slot: { release: () => void };
|
|
2080
|
+
const admissionStartedAt = this.options.nowMs();
|
|
2074
2081
|
try {
|
|
2075
2082
|
slot = await this.options.governor.acquireToolSlot(toolId, {
|
|
2076
2083
|
signal: this.options.abortSignal,
|
|
@@ -2082,6 +2089,11 @@ export class WorkerToolBatchScheduler {
|
|
|
2082
2089
|
);
|
|
2083
2090
|
return;
|
|
2084
2091
|
}
|
|
2092
|
+
this.options.recordPerfTrace?.({
|
|
2093
|
+
phase: 'runner.tool.admission',
|
|
2094
|
+
ms: this.options.nowMs() - admissionStartedAt,
|
|
2095
|
+
extra: { toolId, requests: 1 },
|
|
2096
|
+
});
|
|
2085
2097
|
try {
|
|
2086
2098
|
let result: unknown;
|
|
2087
2099
|
try {
|
|
@@ -2100,59 +2112,68 @@ export class WorkerToolBatchScheduler {
|
|
|
2100
2112
|
claimed,
|
|
2101
2113
|
})
|
|
2102
2114
|
: null;
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
this.options.
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
this.
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2115
|
+
const providerStartedAt = this.options.nowMs();
|
|
2116
|
+
try {
|
|
2117
|
+
result = await executeWithWorkerReceiptHeartbeats({
|
|
2118
|
+
playName: this.options.req.playName,
|
|
2119
|
+
runId: this.options.req.runId,
|
|
2120
|
+
runAttempt: this.options.req.runAttempt ?? 0,
|
|
2121
|
+
...(this.options.receiptStore
|
|
2122
|
+
? { receiptStore: this.options.receiptStore }
|
|
2123
|
+
: {}),
|
|
2124
|
+
budgetMeter: this.options.budgetMeter,
|
|
2125
|
+
heartbeatIntervalMs:
|
|
2126
|
+
this.options.runtimeReceiptHeartbeatIntervalMs,
|
|
2127
|
+
targets: claimed.receiptKey
|
|
2128
|
+
? [
|
|
2129
|
+
{
|
|
2130
|
+
key: claimed.receiptKey,
|
|
2131
|
+
leaseId: claimed.receiptLeaseId,
|
|
2132
|
+
},
|
|
2133
|
+
]
|
|
2134
|
+
: [],
|
|
2135
|
+
execute: () =>
|
|
2136
|
+
this.options.executeTool({
|
|
2137
|
+
id: request.id,
|
|
2138
|
+
toolId,
|
|
2139
|
+
input: request.input,
|
|
2140
|
+
workflowStep: request.workflowStep,
|
|
2141
|
+
callbacks: this.options.callbacks,
|
|
2142
|
+
onProviderBackpressure: (retryAfterMs) =>
|
|
2143
|
+
this.reportBackpressure(toolId, retryAfterMs),
|
|
2144
|
+
onRetryAttempt: () =>
|
|
2145
|
+
this.options.governor.chargeBudget('retry'),
|
|
2146
|
+
transientHttpRetrySafe:
|
|
2147
|
+
toolContract?.retrySafeTransientHttp === true,
|
|
2148
|
+
...(durableReceiptKey
|
|
2149
|
+
? {
|
|
2150
|
+
durableCallReceiptKey: durableReceiptKey,
|
|
2151
|
+
executionAuthScopeDigest:
|
|
2152
|
+
request.executionAuthScopeDigest,
|
|
2153
|
+
providerIdempotencyKey:
|
|
2154
|
+
providerIdempotencyKeyForClaimed({
|
|
2155
|
+
req: this.options.req,
|
|
2156
|
+
claimed,
|
|
2157
|
+
}),
|
|
2158
|
+
}
|
|
2159
|
+
: {}),
|
|
2160
|
+
runtimeTimeoutMs: resolveWorkerToolRuntimeTimeoutMs(
|
|
2161
|
+
[claimed],
|
|
2162
|
+
{
|
|
2163
|
+
resolveOwnerTimeoutMs: (request) =>
|
|
2164
|
+
request.runtimeTimeoutMs,
|
|
2165
|
+
},
|
|
2166
|
+
),
|
|
2167
|
+
directOptions: request.directOptions,
|
|
2168
|
+
}),
|
|
2169
|
+
});
|
|
2170
|
+
} finally {
|
|
2171
|
+
this.options.recordPerfTrace?.({
|
|
2172
|
+
phase: 'runner.tool.provider',
|
|
2173
|
+
ms: this.options.nowMs() - providerStartedAt,
|
|
2174
|
+
extra: { toolId, requests: 1 },
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2156
2177
|
} catch (error) {
|
|
2157
2178
|
if (error instanceof RuntimeLeaseLostError) {
|
|
2158
2179
|
this.rejectRequests(claimed, error);
|
|
@@ -2173,10 +2194,16 @@ export class WorkerToolBatchScheduler {
|
|
|
2173
2194
|
return;
|
|
2174
2195
|
}
|
|
2175
2196
|
try {
|
|
2197
|
+
const receiptCompleteStartedAt = this.options.nowMs();
|
|
2176
2198
|
this.settleRequests(
|
|
2177
2199
|
claimed,
|
|
2178
2200
|
await this.completeDurableToolRequest(claimed, result),
|
|
2179
2201
|
);
|
|
2202
|
+
this.options.recordPerfTrace?.({
|
|
2203
|
+
phase: 'runner.tool.receipt_complete',
|
|
2204
|
+
ms: this.options.nowMs() - receiptCompleteStartedAt,
|
|
2205
|
+
extra: { toolId, requests: 1 },
|
|
2206
|
+
});
|
|
2180
2207
|
} catch (receiptError) {
|
|
2181
2208
|
this.rejectRequests(claimed, receiptError);
|
|
2182
2209
|
}
|
|
@@ -2261,12 +2288,22 @@ export class WorkerToolBatchScheduler {
|
|
|
2261
2288
|
}
|
|
2262
2289
|
// One provider call per batch -> one tool slot (budget + global
|
|
2263
2290
|
// concurrency + per-(org,provider) pacing) around the whole batch.
|
|
2291
|
+
const admissionStartedAt = this.options.nowMs();
|
|
2264
2292
|
const slot = await this.options.governor.acquireToolSlot(
|
|
2265
2293
|
batch.batchOperation,
|
|
2266
2294
|
{
|
|
2267
2295
|
signal: this.options.abortSignal,
|
|
2268
2296
|
},
|
|
2269
2297
|
);
|
|
2298
|
+
this.options.recordPerfTrace?.({
|
|
2299
|
+
phase: 'runner.tool.admission',
|
|
2300
|
+
ms: this.options.nowMs() - admissionStartedAt,
|
|
2301
|
+
extra: {
|
|
2302
|
+
toolId: batch.batchOperation,
|
|
2303
|
+
requests: batch.memberRequests.length,
|
|
2304
|
+
batched: true,
|
|
2305
|
+
},
|
|
2306
|
+
});
|
|
2270
2307
|
try {
|
|
2271
2308
|
await this.markAdmittedToolRequestsRunning(batch.memberRequests);
|
|
2272
2309
|
this.options.budgetMeter?.count('provider');
|
|
@@ -2281,54 +2318,69 @@ export class WorkerToolBatchScheduler {
|
|
|
2281
2318
|
batchOperation: batch.batchOperation,
|
|
2282
2319
|
receiptKeys,
|
|
2283
2320
|
});
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
+
const providerStartedAt = this.options.nowMs();
|
|
2322
|
+
try {
|
|
2323
|
+
return await executeWithWorkerReceiptHeartbeats({
|
|
2324
|
+
playName: this.options.req.playName,
|
|
2325
|
+
runId: this.options.req.runId,
|
|
2326
|
+
runAttempt: this.options.req.runAttempt ?? 0,
|
|
2327
|
+
...(this.options.receiptStore
|
|
2328
|
+
? { receiptStore: this.options.receiptStore }
|
|
2329
|
+
: {}),
|
|
2330
|
+
budgetMeter: this.options.budgetMeter,
|
|
2331
|
+
heartbeatIntervalMs:
|
|
2332
|
+
this.options.runtimeReceiptHeartbeatIntervalMs,
|
|
2333
|
+
targets: batch.memberRequests.map((member) => ({
|
|
2334
|
+
key: member.receiptKey,
|
|
2335
|
+
leaseId: member.receiptLeaseId,
|
|
2336
|
+
})),
|
|
2337
|
+
execute: () =>
|
|
2338
|
+
this.options.executeTool({
|
|
2339
|
+
id: `batch:${batch.memberRequests.map((request) => request.request.id).join('|')}`,
|
|
2340
|
+
toolId: batch.batchOperation,
|
|
2341
|
+
input: batch.batchPayload,
|
|
2342
|
+
callbacks: this.options.callbacks,
|
|
2343
|
+
onProviderBackpressure: (retryAfterMs) =>
|
|
2344
|
+
this.reportBackpressure(input.sourceToolId, retryAfterMs),
|
|
2345
|
+
onRetryAttempt: () =>
|
|
2346
|
+
this.options.governor.chargeBudget('retry'),
|
|
2347
|
+
transientHttpRetrySafe:
|
|
2348
|
+
toolContract?.retrySafeTransientHttp === true,
|
|
2349
|
+
durableCallReceiptKey: aggregateReceiptKey,
|
|
2350
|
+
executionAuthScopeDigest:
|
|
2351
|
+
batch.memberRequests[0]?.request.executionAuthScopeDigest ??
|
|
2352
|
+
null,
|
|
2353
|
+
providerIdempotencyKey: buildBatchProviderIdempotencyKey({
|
|
2354
|
+
aggregateReceiptKey,
|
|
2355
|
+
receiptKeys,
|
|
2356
|
+
providerIdempotencyKeys: batch.memberRequests.map(
|
|
2357
|
+
(member) =>
|
|
2358
|
+
providerIdempotencyKeyForClaimed({
|
|
2359
|
+
req: this.options.req,
|
|
2360
|
+
claimed: member,
|
|
2361
|
+
}),
|
|
2362
|
+
),
|
|
2363
|
+
}),
|
|
2364
|
+
runtimeTimeoutMs: resolveWorkerToolRuntimeTimeoutMs(
|
|
2365
|
+
batch.memberRequests,
|
|
2366
|
+
{
|
|
2367
|
+
resolveOwnerTimeoutMs: (request) =>
|
|
2368
|
+
request.runtimeTimeoutMs,
|
|
2369
|
+
},
|
|
2321
2370
|
),
|
|
2322
2371
|
}),
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2372
|
+
});
|
|
2373
|
+
} finally {
|
|
2374
|
+
this.options.recordPerfTrace?.({
|
|
2375
|
+
phase: 'runner.tool.provider',
|
|
2376
|
+
ms: this.options.nowMs() - providerStartedAt,
|
|
2377
|
+
extra: {
|
|
2378
|
+
toolId: batch.batchOperation,
|
|
2379
|
+
requests: batch.memberRequests.length,
|
|
2380
|
+
batched: true,
|
|
2381
|
+
},
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2332
2384
|
} finally {
|
|
2333
2385
|
slot.release();
|
|
2334
2386
|
}
|
|
@@ -2389,6 +2441,7 @@ export class WorkerToolBatchScheduler {
|
|
|
2389
2441
|
: entry.request.memberRequests.map(() => null);
|
|
2390
2442
|
let completedResults: WorkerToolCompletionResult[];
|
|
2391
2443
|
try {
|
|
2444
|
+
const receiptCompleteStartedAt = this.options.nowMs();
|
|
2392
2445
|
completedResults = await this.completeDurableToolRequests(
|
|
2393
2446
|
entry.request.memberRequests.map((claimed, index) => ({
|
|
2394
2447
|
claimed,
|
|
@@ -2399,6 +2452,15 @@ export class WorkerToolBatchScheduler {
|
|
|
2399
2452
|
),
|
|
2400
2453
|
})),
|
|
2401
2454
|
);
|
|
2455
|
+
this.options.recordPerfTrace?.({
|
|
2456
|
+
phase: 'runner.tool.receipt_complete',
|
|
2457
|
+
ms: this.options.nowMs() - receiptCompleteStartedAt,
|
|
2458
|
+
extra: {
|
|
2459
|
+
toolId: entry.request.batchOperation,
|
|
2460
|
+
requests: entry.request.memberRequests.length,
|
|
2461
|
+
batched: true,
|
|
2462
|
+
},
|
|
2463
|
+
});
|
|
2402
2464
|
} catch (receiptError) {
|
|
2403
2465
|
for (const claimed of entry.request.memberRequests) {
|
|
2404
2466
|
this.rejectRequests(claimed, receiptError);
|
|
@@ -173,6 +173,8 @@ export type PlayBindings = {
|
|
|
173
173
|
* older clients can continue to register revisions during the migration.
|
|
174
174
|
*/
|
|
175
175
|
description?: string;
|
|
176
|
+
/** Allow compilers to bundle this named handler directly without a child run. */
|
|
177
|
+
inline?: boolean;
|
|
176
178
|
/** Optional per-run billing controls enforced by the runtime. */
|
|
177
179
|
billing?: {
|
|
178
180
|
/** Stop the run before a billed action would push total run credits above this cap. */
|
|
@@ -106,10 +106,10 @@ export const SDK_RELEASE = {
|
|
|
106
106
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
107
107
|
// fields shipped in 0.1.153.
|
|
108
108
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
109
|
-
version: '0.1.
|
|
109
|
+
version: '0.1.211',
|
|
110
110
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
111
111
|
supportPolicy: {
|
|
112
|
-
latest: '0.1.
|
|
112
|
+
latest: '0.1.211',
|
|
113
113
|
minimumSupported: '0.1.53',
|
|
114
114
|
deprecatedBelow: '0.1.53',
|
|
115
115
|
commandMinimumSupported: [
|