deepline 0.2.55 → 0.2.56
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 +14 -0
- package/dist/bundling-sources/sdk/src/http.ts +19 -1
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/stream-reconnect.ts +6 -0
- package/dist/bundling-sources/sdk/src/types.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +146 -12
- package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +6 -3
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1631 -748
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +20 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +81 -52
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +146 -6
- package/dist/bundling-sources/shared_libs/play-runtime/gateway-auth-session.ts +56 -22
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +14 -11
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +21 -2
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +81 -21
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +27 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +49 -10
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +96 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +341 -67
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +8 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-pg.ts +17 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver.ts +4 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +17 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +16 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-writer.ts +25 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +80 -1
- package/dist/bundling-sources/shared_libs/security/safe-fetch.ts +9 -0
- package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +85 -48
- package/dist/cli/index.js +409 -51
- package/dist/cli/index.mjs +389 -25
- package/dist/index.d.mts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +29 -2
- package/dist/index.mjs +29 -2
- package/package.json +1 -1
|
@@ -2172,6 +2172,14 @@ export class DeeplineClient {
|
|
|
2172
2172
|
: {}),
|
|
2173
2173
|
...(request.force ? { force: true } : {}),
|
|
2174
2174
|
...(forceToolRefresh ? { forceToolRefresh: true } : {}),
|
|
2175
|
+
...(typeof request.maxConcurrentExternalCalls === 'number'
|
|
2176
|
+
? {
|
|
2177
|
+
maxConcurrentExternalCalls: request.maxConcurrentExternalCalls,
|
|
2178
|
+
}
|
|
2179
|
+
: {}),
|
|
2180
|
+
...(typeof request.maxConcurrentRows === 'number'
|
|
2181
|
+
? { maxConcurrentRows: request.maxConcurrentRows }
|
|
2182
|
+
: {}),
|
|
2175
2183
|
...(typeof request.waitForCompletionMs === 'number'
|
|
2176
2184
|
? { waitForCompletionMs: request.waitForCompletionMs }
|
|
2177
2185
|
: {}),
|
|
@@ -2247,6 +2255,12 @@ export class DeeplineClient {
|
|
|
2247
2255
|
: {}),
|
|
2248
2256
|
...(request.force ? { force: true } : {}),
|
|
2249
2257
|
...(forceToolRefresh ? { forceToolRefresh: true } : {}),
|
|
2258
|
+
...(typeof request.maxConcurrentExternalCalls === 'number'
|
|
2259
|
+
? { maxConcurrentExternalCalls: request.maxConcurrentExternalCalls }
|
|
2260
|
+
: {}),
|
|
2261
|
+
...(typeof request.maxConcurrentRows === 'number'
|
|
2262
|
+
? { maxConcurrentRows: request.maxConcurrentRows }
|
|
2263
|
+
: {}),
|
|
2250
2264
|
...(typeof request.waitForCompletionMs === 'number'
|
|
2251
2265
|
? { waitForCompletionMs: request.waitForCompletionMs }
|
|
2252
2266
|
: {}),
|
|
@@ -652,10 +652,28 @@ export class HttpClient {
|
|
|
652
652
|
if (error instanceof AuthError || error instanceof DeeplineError) {
|
|
653
653
|
throw error;
|
|
654
654
|
}
|
|
655
|
-
|
|
655
|
+
const normalized =
|
|
656
|
+
error instanceof Error ? error : new Error(String(error));
|
|
657
|
+
if (isAbortLikeError(normalized) && options?.signal?.aborted) {
|
|
658
|
+
throw new DeeplineError(
|
|
659
|
+
`Stream from ${this.config.baseUrl} was aborted by the caller.`,
|
|
660
|
+
undefined,
|
|
661
|
+
'ABORTED',
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
lastError = normalized;
|
|
656
665
|
}
|
|
657
666
|
}
|
|
658
667
|
|
|
668
|
+
if (lastError && isAbortLikeError(lastError)) {
|
|
669
|
+
throw new DeeplineError(
|
|
670
|
+
withCoworkNetworkHint(
|
|
671
|
+
`Unable to stream from ${this.config.baseUrl}. The remote stream was interrupted.`,
|
|
672
|
+
),
|
|
673
|
+
undefined,
|
|
674
|
+
'PLAY_STREAM_NETWORK_ABORTED',
|
|
675
|
+
);
|
|
676
|
+
}
|
|
659
677
|
throw new DeeplineError(
|
|
660
678
|
withCoworkNetworkHint(
|
|
661
679
|
lastError?.message
|
|
@@ -160,7 +160,7 @@ export const SDK_RELEASE = {
|
|
|
160
160
|
// 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
|
|
161
161
|
// exposed storage-dependent synchronous access. This deliberate minor
|
|
162
162
|
// release keeps lazy paging semantics independent of row residency.
|
|
163
|
-
version: '0.2.
|
|
163
|
+
version: '0.2.56',
|
|
164
164
|
contracts: {
|
|
165
165
|
api: {
|
|
166
166
|
name: 'sdk-http-api',
|
|
@@ -30,6 +30,12 @@ export function streamReconnectDelayMs(attempt: number): number {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function isTransientPlayStreamError(error: unknown): boolean {
|
|
33
|
+
if (
|
|
34
|
+
error instanceof DeeplineError &&
|
|
35
|
+
error.code === 'PLAY_STREAM_NETWORK_ABORTED'
|
|
36
|
+
) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
33
39
|
if (error instanceof DeeplineError && typeof error.statusCode === 'number') {
|
|
34
40
|
// Server-shaped errors with a definite status code are NOT transient by
|
|
35
41
|
// pattern — only network-level failures are. 5xx counts as transient
|
|
@@ -1488,6 +1488,13 @@ export interface StartPlayRunRequest {
|
|
|
1488
1488
|
force?: boolean;
|
|
1489
1489
|
/** Explicit cache-bypass flag for durable dataset and tool-call reuse. */
|
|
1490
1490
|
forceToolRefresh?: boolean;
|
|
1491
|
+
/**
|
|
1492
|
+
* Per-run ceiling for concurrently resident provider-tool executions and
|
|
1493
|
+
* direct ctx.fetch calls. The server validates the supported range.
|
|
1494
|
+
*/
|
|
1495
|
+
maxConcurrentExternalCalls?: number;
|
|
1496
|
+
/** Run-wide default and ceiling for live dataset-map row resolvers. */
|
|
1497
|
+
maxConcurrentRows?: number;
|
|
1491
1498
|
/** Optionally let the start request wait briefly and return a terminal result. */
|
|
1492
1499
|
waitForCompletionMs?: number;
|
|
1493
1500
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createWriteStream } from 'node:fs';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { stat } from 'node:fs/promises';
|
|
3
4
|
import { Readable } from 'node:stream';
|
|
4
5
|
import { pipeline } from 'node:stream/promises';
|
|
@@ -39,13 +40,18 @@ import type {
|
|
|
39
40
|
PlayRunnerRateStateReleaseInput,
|
|
40
41
|
} from '@shared_libs/play-runtime/protocol';
|
|
41
42
|
import {
|
|
43
|
+
PLAY_RUNTIME_TRANSPORT_ATTEMPT_HEADER,
|
|
42
44
|
PLAY_RUNTIME_CONTRACT,
|
|
43
45
|
PLAY_RUNTIME_CONTRACT_HEADER,
|
|
44
46
|
} from '@shared_libs/play-runtime/runtime-contract';
|
|
45
47
|
import { PLAY_RUNTIME_API_COMPAT_PATH } from '@shared_libs/play-runtime/runtime-api-paths';
|
|
46
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
PLAY_RUNTIME_TEST_FAULT_HEADER,
|
|
50
|
+
recognizedRuntimeTestFaultCount,
|
|
51
|
+
} from '@shared_libs/play-runtime/test-runtime-seams';
|
|
47
52
|
import { vercelProtectionBypassHeaders } from '@shared_libs/play-runtime/vercel-protection';
|
|
48
53
|
import type { RuntimeReceiptAction } from '@shared_libs/play-runtime/runtime-actions';
|
|
54
|
+
import { RUNTIME_CAPACITY_POLICY } from '@shared_libs/play-runtime/runtime-capacity-policy';
|
|
49
55
|
|
|
50
56
|
export type StoredPlayArtifactPayload = {
|
|
51
57
|
sourceCode: string;
|
|
@@ -244,7 +250,8 @@ function applyRetryJitter(delayMs: number): number {
|
|
|
244
250
|
const half = delayMs / 2;
|
|
245
251
|
return Math.round(half + Math.random() * half);
|
|
246
252
|
}
|
|
247
|
-
const APP_RUNTIME_API_DEFAULT_REQUEST_TIMEOUT_MS =
|
|
253
|
+
const APP_RUNTIME_API_DEFAULT_REQUEST_TIMEOUT_MS =
|
|
254
|
+
RUNTIME_CAPACITY_POLICY.receiptGateway.requestTimeoutMs;
|
|
248
255
|
const APP_RUNTIME_RECEIPT_RETRY_TELEMETRY_TAG =
|
|
249
256
|
'[perf][worker.receipt_api.transport]';
|
|
250
257
|
const RUN_STATUS_LEDGER_SNAPSHOT_CACHE_LIMIT = 1_000;
|
|
@@ -254,10 +261,103 @@ const runStatusLedgerSnapshots = new Map<string, PlayRunLedgerSnapshot>();
|
|
|
254
261
|
// ingestion skips overlap within that same attempt.
|
|
255
262
|
const runLogChannelSentCounts = new Map<string, number>();
|
|
256
263
|
const runStatusUpdateChains = new Map<string, Promise<void>>();
|
|
264
|
+
const receiptClaimResponseTimeoutFaults = new Map<string, number>();
|
|
265
|
+
const receiptClaimQueryHoldFaults = new Set<string>();
|
|
266
|
+
const RECEIPT_CLAIM_RESPONSE_TIMEOUT_FAULT_LIMIT = 1_000;
|
|
257
267
|
const APP_RUNTIME_LOG_EVENT_MAX_JSON_BYTES = 128 * 1024;
|
|
258
268
|
const APP_RUNTIME_EVENT_BATCH_MAX_JSON_BYTES = 512 * 1024;
|
|
259
269
|
const appRuntimeTextEncoder = new TextEncoder();
|
|
260
270
|
|
|
271
|
+
function shouldSuppressBulkClaimResponse(input: {
|
|
272
|
+
body: RuntimeApiRequest;
|
|
273
|
+
runtimeTestFaultHeader?: string | null;
|
|
274
|
+
}): boolean {
|
|
275
|
+
if (
|
|
276
|
+
input.body.action !== 'claim_runtime_step_receipts' ||
|
|
277
|
+
input.body.keys.length < 128
|
|
278
|
+
) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
const requested = recognizedRuntimeTestFaultCount(
|
|
282
|
+
input.runtimeTestFaultHeader,
|
|
283
|
+
'receipt_claim_response_timeout',
|
|
284
|
+
);
|
|
285
|
+
if (requested <= 0) return false;
|
|
286
|
+
const key = `${input.body.runId}:${input.body.runAttempt ?? 0}`;
|
|
287
|
+
const consumed = receiptClaimResponseTimeoutFaults.get(key) ?? 0;
|
|
288
|
+
if (consumed >= requested) return false;
|
|
289
|
+
receiptClaimResponseTimeoutFaults.set(key, consumed + 1);
|
|
290
|
+
while (
|
|
291
|
+
receiptClaimResponseTimeoutFaults.size >
|
|
292
|
+
RECEIPT_CLAIM_RESPONSE_TIMEOUT_FAULT_LIMIT
|
|
293
|
+
) {
|
|
294
|
+
const oldest = receiptClaimResponseTimeoutFaults.keys().next().value;
|
|
295
|
+
if (typeof oldest !== 'string') break;
|
|
296
|
+
receiptClaimResponseTimeoutFaults.delete(oldest);
|
|
297
|
+
}
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function runtimeTestFaultHeaderForRequest(input: {
|
|
302
|
+
body: RuntimeApiRequest;
|
|
303
|
+
runtimeTestFaultHeader?: string | null;
|
|
304
|
+
}): string | null {
|
|
305
|
+
const header = input.runtimeTestFaultHeader?.trim();
|
|
306
|
+
if (!header) return null;
|
|
307
|
+
if (
|
|
308
|
+
input.body.action !== 'claim_runtime_step_receipts' ||
|
|
309
|
+
input.body.keys.length < 128 ||
|
|
310
|
+
recognizedRuntimeTestFaultCount(
|
|
311
|
+
header,
|
|
312
|
+
'receipt_claim_query_hold_once_ms',
|
|
313
|
+
) <= 0
|
|
314
|
+
) {
|
|
315
|
+
return header;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const key = `${input.body.runId}:${input.body.runAttempt ?? 0}`;
|
|
319
|
+
if (!receiptClaimQueryHoldFaults.has(key)) {
|
|
320
|
+
receiptClaimQueryHoldFaults.add(key);
|
|
321
|
+
while (
|
|
322
|
+
receiptClaimQueryHoldFaults.size >
|
|
323
|
+
RECEIPT_CLAIM_RESPONSE_TIMEOUT_FAULT_LIMIT
|
|
324
|
+
) {
|
|
325
|
+
const oldest = receiptClaimQueryHoldFaults.values().next().value;
|
|
326
|
+
if (typeof oldest !== 'string') break;
|
|
327
|
+
receiptClaimQueryHoldFaults.delete(oldest);
|
|
328
|
+
}
|
|
329
|
+
return header;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const filtered = header
|
|
333
|
+
.split(',')
|
|
334
|
+
.map((part) => part.trim())
|
|
335
|
+
.filter(
|
|
336
|
+
(part) =>
|
|
337
|
+
part &&
|
|
338
|
+
part.split(':', 1)[0]?.trim() !== 'receipt_claim_query_hold_once_ms',
|
|
339
|
+
)
|
|
340
|
+
.join(',');
|
|
341
|
+
return filtered || null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async function suppressBulkClaimResponseUntilTimeout(
|
|
345
|
+
signal: AbortSignal,
|
|
346
|
+
): Promise<never> {
|
|
347
|
+
if (signal.aborted) {
|
|
348
|
+
throw signal.reason ?? new Error('Runtime receipt request was aborted.');
|
|
349
|
+
}
|
|
350
|
+
return await new Promise<never>((_resolve, reject) => {
|
|
351
|
+
const onAbort = () => {
|
|
352
|
+
signal.removeEventListener('abort', onAbort);
|
|
353
|
+
reject(
|
|
354
|
+
signal.reason ?? new Error('Runtime receipt request was aborted.'),
|
|
355
|
+
);
|
|
356
|
+
};
|
|
357
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
261
361
|
function splitRunEventForAppRuntime(
|
|
262
362
|
event: PlayRunLedgerEvent,
|
|
263
363
|
): PlayRunLedgerEvent[] {
|
|
@@ -633,6 +733,7 @@ type AppRuntimeRetryFailureKind =
|
|
|
633
733
|
|
|
634
734
|
type AppRuntimeRetryFailure = {
|
|
635
735
|
attempt: number;
|
|
736
|
+
transportAttemptId: string;
|
|
636
737
|
failureKind: AppRuntimeRetryFailureKind;
|
|
637
738
|
attemptElapsedMs: number;
|
|
638
739
|
retryDelayMs: number;
|
|
@@ -674,6 +775,7 @@ function appRuntimeTransportFailureKind(
|
|
|
674
775
|
function recordAppRuntimeRetryFailure(input: {
|
|
675
776
|
telemetry: AppRuntimeRetryTelemetry;
|
|
676
777
|
attempt: number;
|
|
778
|
+
transportAttemptId: string;
|
|
677
779
|
failureKind: AppRuntimeRetryFailureKind;
|
|
678
780
|
attemptStartedAt: number;
|
|
679
781
|
retryDelayMs: number;
|
|
@@ -685,6 +787,7 @@ function recordAppRuntimeRetryFailure(input: {
|
|
|
685
787
|
if (input.telemetry.failures.length < 8) {
|
|
686
788
|
input.telemetry.failures.push({
|
|
687
789
|
attempt: input.attempt,
|
|
790
|
+
transportAttemptId: input.transportAttemptId,
|
|
688
791
|
failureKind: input.failureKind,
|
|
689
792
|
attemptElapsedMs: Date.now() - input.attemptStartedAt,
|
|
690
793
|
retryDelayMs: input.retryDelayMs,
|
|
@@ -734,6 +837,7 @@ function emitAppRuntimeRetrySummary(input: {
|
|
|
734
837
|
async function retryAppRuntimeBodyTimeoutOrThrow(input: {
|
|
735
838
|
action: RuntimeApiRequest['action'];
|
|
736
839
|
attempt: number;
|
|
840
|
+
transportAttemptId: string;
|
|
737
841
|
maxAttempts: number;
|
|
738
842
|
error: unknown;
|
|
739
843
|
telemetry: AppRuntimeRetryTelemetry;
|
|
@@ -746,6 +850,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
|
|
|
746
850
|
recordAppRuntimeRetryFailure({
|
|
747
851
|
telemetry: input.telemetry,
|
|
748
852
|
attempt: input.attempt,
|
|
853
|
+
transportAttemptId: input.transportAttemptId,
|
|
749
854
|
failureKind: 'response_body_error',
|
|
750
855
|
attemptStartedAt: input.attemptStartedAt,
|
|
751
856
|
retryDelayMs: 0,
|
|
@@ -775,6 +880,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
|
|
|
775
880
|
recordAppRuntimeRetryFailure({
|
|
776
881
|
telemetry: input.telemetry,
|
|
777
882
|
attempt: input.attempt,
|
|
883
|
+
transportAttemptId: input.transportAttemptId,
|
|
778
884
|
failureKind: 'response_body_timeout',
|
|
779
885
|
attemptStartedAt: input.attemptStartedAt,
|
|
780
886
|
retryDelayMs,
|
|
@@ -786,6 +892,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
|
|
|
786
892
|
recordAppRuntimeRetryFailure({
|
|
787
893
|
telemetry: input.telemetry,
|
|
788
894
|
attempt: input.attempt,
|
|
895
|
+
transportAttemptId: input.transportAttemptId,
|
|
789
896
|
failureKind: 'response_body_timeout',
|
|
790
897
|
attemptStartedAt: input.attemptStartedAt,
|
|
791
898
|
retryDelayMs: 0,
|
|
@@ -802,6 +909,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
|
|
|
802
909
|
throw new AppRuntimeApiTransportError({
|
|
803
910
|
action: input.action,
|
|
804
911
|
attempts: input.attempt,
|
|
912
|
+
transportAttemptId: input.transportAttemptId,
|
|
805
913
|
cause: input.error,
|
|
806
914
|
boundaryLabel: input.boundaryLabel,
|
|
807
915
|
});
|
|
@@ -818,22 +926,28 @@ function runtimeApiBoundaryLabel(
|
|
|
818
926
|
export class AppRuntimeApiTransportError extends Error {
|
|
819
927
|
readonly action: RuntimeApiRequest['action'];
|
|
820
928
|
readonly attempts: number;
|
|
929
|
+
readonly transportAttemptId: string | null;
|
|
821
930
|
|
|
822
931
|
constructor(input: {
|
|
823
932
|
action: RuntimeApiRequest['action'];
|
|
824
933
|
attempts: number;
|
|
934
|
+
transportAttemptId?: string | null;
|
|
825
935
|
cause: unknown;
|
|
826
936
|
boundaryLabel?: string;
|
|
827
937
|
}) {
|
|
828
938
|
const causeMessage =
|
|
829
939
|
input.cause instanceof Error ? input.cause.message : String(input.cause);
|
|
830
940
|
super(
|
|
831
|
-
`${input.boundaryLabel ?? 'App runtime API'} transport exhausted action=${input.action} attempts=${input.attempts}: ${causeMessage}
|
|
941
|
+
`${input.boundaryLabel ?? 'App runtime API'} transport exhausted action=${input.action} attempts=${input.attempts}: ${causeMessage}` +
|
|
942
|
+
(input.transportAttemptId
|
|
943
|
+
? ` (transport_attempt=${input.transportAttemptId})`
|
|
944
|
+
: ''),
|
|
832
945
|
{ cause: input.cause },
|
|
833
946
|
);
|
|
834
947
|
this.name = 'AppRuntimeApiTransportError';
|
|
835
948
|
this.action = input.action;
|
|
836
949
|
this.attempts = input.attempts;
|
|
950
|
+
this.transportAttemptId = input.transportAttemptId ?? null;
|
|
837
951
|
}
|
|
838
952
|
}
|
|
839
953
|
|
|
@@ -874,6 +988,7 @@ export class AppRuntimeApiResponseError extends Error {
|
|
|
874
988
|
|
|
875
989
|
const APP_RUNTIME_CAPACITY_ERROR_CODES = new Set([
|
|
876
990
|
'receipt_db_admission_backpressure',
|
|
991
|
+
'runtime_receipt_claim_timeout',
|
|
877
992
|
'scheduler_capacity',
|
|
878
993
|
'runtime_postgres_admission_backpressure',
|
|
879
994
|
'RUNTIME_SCHEDULER_SATURATED',
|
|
@@ -970,7 +1085,15 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
970
1085
|
|
|
971
1086
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
972
1087
|
const attemptStartedAt = Date.now();
|
|
1088
|
+
const transportAttemptId = randomUUID();
|
|
1089
|
+
const requestSignal = context.signal
|
|
1090
|
+
? AbortSignal.any([context.signal, AbortSignal.timeout(requestTimeoutMs)])
|
|
1091
|
+
: AbortSignal.timeout(requestTimeoutMs);
|
|
973
1092
|
let response: Response;
|
|
1093
|
+
const runtimeTestFaultHeader = runtimeTestFaultHeaderForRequest({
|
|
1094
|
+
body,
|
|
1095
|
+
runtimeTestFaultHeader: context.runtimeTestFaultHeader,
|
|
1096
|
+
});
|
|
974
1097
|
try {
|
|
975
1098
|
response = await runtimeFetch(resolveAppRuntimeApiUrl(context), {
|
|
976
1099
|
method: 'POST',
|
|
@@ -978,21 +1101,16 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
978
1101
|
'content-type': 'application/json',
|
|
979
1102
|
authorization: `Bearer ${token}`,
|
|
980
1103
|
[PLAY_RUNTIME_CONTRACT_HEADER]: String(PLAY_RUNTIME_CONTRACT),
|
|
1104
|
+
[PLAY_RUNTIME_TRANSPORT_ATTEMPT_HEADER]: transportAttemptId,
|
|
981
1105
|
...vercelHeaders,
|
|
982
|
-
...(
|
|
1106
|
+
...(runtimeTestFaultHeader
|
|
983
1107
|
? {
|
|
984
|
-
[PLAY_RUNTIME_TEST_FAULT_HEADER]:
|
|
985
|
-
context.runtimeTestFaultHeader.trim(),
|
|
1108
|
+
[PLAY_RUNTIME_TEST_FAULT_HEADER]: runtimeTestFaultHeader,
|
|
986
1109
|
}
|
|
987
1110
|
: {}),
|
|
988
1111
|
},
|
|
989
1112
|
body: JSON.stringify(body),
|
|
990
|
-
signal:
|
|
991
|
-
? AbortSignal.any([
|
|
992
|
-
context.signal,
|
|
993
|
-
AbortSignal.timeout(requestTimeoutMs),
|
|
994
|
-
])
|
|
995
|
-
: AbortSignal.timeout(requestTimeoutMs),
|
|
1113
|
+
signal: requestSignal,
|
|
996
1114
|
});
|
|
997
1115
|
} catch (error) {
|
|
998
1116
|
if (context.signal?.aborted) {
|
|
@@ -1008,6 +1126,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1008
1126
|
recordAppRuntimeRetryFailure({
|
|
1009
1127
|
telemetry: retryTelemetry,
|
|
1010
1128
|
attempt,
|
|
1129
|
+
transportAttemptId,
|
|
1011
1130
|
failureKind: appRuntimeTransportFailureKind(error),
|
|
1012
1131
|
attemptStartedAt,
|
|
1013
1132
|
retryDelayMs,
|
|
@@ -1018,6 +1137,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1018
1137
|
recordAppRuntimeRetryFailure({
|
|
1019
1138
|
telemetry: retryTelemetry,
|
|
1020
1139
|
attempt,
|
|
1140
|
+
transportAttemptId,
|
|
1021
1141
|
failureKind: appRuntimeTransportFailureKind(error),
|
|
1022
1142
|
attemptStartedAt,
|
|
1023
1143
|
retryDelayMs: 0,
|
|
@@ -1033,6 +1153,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1033
1153
|
throw new AppRuntimeApiTransportError({
|
|
1034
1154
|
action: body.action,
|
|
1035
1155
|
attempts: attempt,
|
|
1156
|
+
transportAttemptId,
|
|
1036
1157
|
cause: error,
|
|
1037
1158
|
boundaryLabel,
|
|
1038
1159
|
});
|
|
@@ -1040,6 +1161,14 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1040
1161
|
if (response.ok) {
|
|
1041
1162
|
try {
|
|
1042
1163
|
const parsed = (await response.json()) as TResponse;
|
|
1164
|
+
if (
|
|
1165
|
+
shouldSuppressBulkClaimResponse({
|
|
1166
|
+
body,
|
|
1167
|
+
runtimeTestFaultHeader: context.runtimeTestFaultHeader,
|
|
1168
|
+
})
|
|
1169
|
+
) {
|
|
1170
|
+
await suppressBulkClaimResponseUntilTimeout(requestSignal);
|
|
1171
|
+
}
|
|
1043
1172
|
emitAppRuntimeRetrySummary({
|
|
1044
1173
|
telemetry: retryTelemetry,
|
|
1045
1174
|
action: body.action,
|
|
@@ -1053,6 +1182,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1053
1182
|
await retryAppRuntimeBodyTimeoutOrThrow({
|
|
1054
1183
|
action: body.action,
|
|
1055
1184
|
attempt,
|
|
1185
|
+
transportAttemptId,
|
|
1056
1186
|
maxAttempts,
|
|
1057
1187
|
error,
|
|
1058
1188
|
telemetry: retryTelemetry,
|
|
@@ -1074,6 +1204,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1074
1204
|
recordAppRuntimeRetryFailure({
|
|
1075
1205
|
telemetry: retryTelemetry,
|
|
1076
1206
|
attempt,
|
|
1207
|
+
transportAttemptId,
|
|
1077
1208
|
failureKind: 'response_body_timeout',
|
|
1078
1209
|
attemptStartedAt,
|
|
1079
1210
|
retryDelayMs: 0,
|
|
@@ -1095,6 +1226,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1095
1226
|
await retryAppRuntimeBodyTimeoutOrThrow({
|
|
1096
1227
|
action: body.action,
|
|
1097
1228
|
attempt,
|
|
1229
|
+
transportAttemptId,
|
|
1098
1230
|
maxAttempts,
|
|
1099
1231
|
error,
|
|
1100
1232
|
telemetry: retryTelemetry,
|
|
@@ -1135,6 +1267,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1135
1267
|
recordAppRuntimeRetryFailure({
|
|
1136
1268
|
telemetry: retryTelemetry,
|
|
1137
1269
|
attempt,
|
|
1270
|
+
transportAttemptId,
|
|
1138
1271
|
failureKind: 'retryable_http',
|
|
1139
1272
|
attemptStartedAt,
|
|
1140
1273
|
retryDelayMs,
|
|
@@ -1146,6 +1279,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1146
1279
|
recordAppRuntimeRetryFailure({
|
|
1147
1280
|
telemetry: retryTelemetry,
|
|
1148
1281
|
attempt,
|
|
1282
|
+
transportAttemptId,
|
|
1149
1283
|
failureKind: isRetryableAppRuntimeResponse({
|
|
1150
1284
|
action: body.action,
|
|
1151
1285
|
status: response.status,
|
|
@@ -52,8 +52,7 @@ function planExecutionChunks<TRequest>(
|
|
|
52
52
|
for (const request of requests) {
|
|
53
53
|
if (
|
|
54
54
|
current.length > 0 &&
|
|
55
|
-
(current.length >= maxUnits ||
|
|
56
|
-
(weightCap != null && weight >= weightCap))
|
|
55
|
+
(current.length >= maxUnits || (weightCap != null && weight >= weightCap))
|
|
57
56
|
) {
|
|
58
57
|
chunks.push(current);
|
|
59
58
|
current = [];
|
|
@@ -91,6 +90,8 @@ export async function executeChunkedRequests<TRequest, TResult>(input: {
|
|
|
91
90
|
onChunkComplete?: (
|
|
92
91
|
results: Array<ChunkExecutionResult<TRequest, TResult>>,
|
|
93
92
|
) => void | Promise<void>;
|
|
93
|
+
/** Keep settled entries for the returned aggregate. Defaults to true. */
|
|
94
|
+
retainResults?: boolean;
|
|
94
95
|
}): Promise<Array<ChunkExecutionResult<TRequest, TResult>>> {
|
|
95
96
|
const results: Array<ChunkExecutionResult<TRequest, TResult>> = [];
|
|
96
97
|
|
|
@@ -105,7 +106,9 @@ export async function executeChunkedRequests<TRequest, TResult>(input: {
|
|
|
105
106
|
const notify = async (
|
|
106
107
|
entry: ChunkExecutionResult<TRequest, TResult>,
|
|
107
108
|
): Promise<void> => {
|
|
108
|
-
|
|
109
|
+
if (input.retainResults !== false) {
|
|
110
|
+
results.push(entry);
|
|
111
|
+
}
|
|
109
112
|
notifyChain = notifyChain.then(
|
|
110
113
|
async () => await input.onChunkComplete?.([entry]),
|
|
111
114
|
);
|