deepline 0.3.128 → 0.3.129
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 +34 -9
- package/dist/bundling-sources/sdk/src/http.ts +22 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +265 -2
- package/dist/bundling-sources/shared_libs/play-runtime/bounded-batch-function.ts +682 -0
- package/dist/bundling-sources/shared_libs/play-runtime/child-execution-strategy.ts +21 -5
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5390 -1732
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +107 -1
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +229 -7
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +25 -4
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-capacity.ts +71 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +4 -1
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +694 -28
- package/dist/bundling-sources/shared_libs/play-runtime/egress-bandwidth-probe.ts +167 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execute-server-timing.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-receipt-output-reference.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-response-size.ts +27 -0
- package/dist/bundling-sources/shared_libs/play-runtime/gateway-postgres-admission.ts +66 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +425 -71
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +210 -32
- package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +27 -13
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +19 -6
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +61 -2
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +33 -1
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +42 -1
- package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +11 -11
- package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +49 -16
- package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +1653 -98
- package/dist/bundling-sources/shared_libs/play-runtime/memory-ledger.ts +807 -0
- package/dist/bundling-sources/shared_libs/play-runtime/memory-pressure-signal.ts +138 -0
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +85 -3
- package/dist/bundling-sources/shared_libs/play-runtime/persistence-latch.ts +60 -2
- package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +46 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +70 -27
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +56 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/adaptive-provider-capacity-controller.ts +192 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/epoch-aimd-rate-controller.ts +194 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/fixture-capacity-simulator.ts +220 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/gradient2-concurrency-controller.ts +214 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-capacity-policy.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-concurrency-policy.ts +30 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-rate-policy.ts +95 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-throttle-classifier.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +1535 -11
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +49 -2
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +55 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/egress-probe.ts +221 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +339 -29
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +28 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +192 -165
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +90 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +281 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +27 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +19 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +108 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +11 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +30 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-compression.ts +276 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +723 -115
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +98 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-environment.ts +41 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-flow.ts +448 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-map-row-feed.ts +215 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +161 -14
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-value.ts +184 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +607 -121
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-placement-policy.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-writer.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +15 -2
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +71 -14
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +288 -119
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -6
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +28 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/canonical-integration-operation-store.ts +179 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +80 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +31 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-store-contract.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-reference.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-retirement.ts +193 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-storage.ts +420 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +134 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +1657 -95
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +885 -73
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-completion-coalescer.ts +452 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +601 -42
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +371 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +86 -7
- package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +153 -22
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/index.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/receipt-cohort.ts +188 -38
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +28 -13
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +44 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +56 -4
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +16 -10
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +4 -1
- package/dist/cli/index.js +250 -38
- package/dist/cli/index.mjs +250 -38
- package/dist/{compiler-manifest-DfSnbCTW.d.mts → compiler-manifest-BIqRyj5m.d.mts} +1 -1
- package/dist/{compiler-manifest-DfSnbCTW.d.ts → compiler-manifest-BIqRyj5m.d.ts} +1 -1
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +231 -35
- package/dist/index.mjs +231 -35
- package/dist/install-integrity.json +31 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/bundle-play-file.mjs +65 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts
CHANGED
|
@@ -33,6 +33,7 @@ export async function acceptRunnerTerminal(
|
|
|
33
33
|
attempt: number;
|
|
34
34
|
authority?: RunnerTerminalAttemptAuthority | null;
|
|
35
35
|
result: unknown;
|
|
36
|
+
terminalProgressEvents?: readonly unknown[];
|
|
36
37
|
},
|
|
37
38
|
options?: { schema?: string },
|
|
38
39
|
): Promise<RunnerTerminalIngressOutcome> {
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import {
|
|
3
|
+
normalizeRuntimeReceiptValueInlineFields,
|
|
4
|
+
RuntimeReceiptValueReadError,
|
|
5
|
+
type RuntimeReceiptValueInlineFields,
|
|
6
|
+
type RuntimeReceiptValueReference,
|
|
7
|
+
} from '../runtime-receipt-value';
|
|
8
|
+
import { BoundedBatchFunction } from '../bounded-batch-function';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Keep small receipts in the same Postgres row. Larger JSON bodies move once
|
|
12
|
+
* to the receipt-owned object namespace, so the row and gateway batch keep a
|
|
13
|
+
* compact manifest. The boundary is deliberately an injected policy in tests.
|
|
14
|
+
*/
|
|
15
|
+
export const RUNTIME_RECEIPT_VALUE_INLINE_MAX_BYTES = 256 * 1024;
|
|
16
|
+
|
|
17
|
+
/** Shared object-store surface for all immutable runtime JSON values. */
|
|
18
|
+
export type RuntimeValueBlobStore = {
|
|
19
|
+
exists(key: string): Promise<boolean>;
|
|
20
|
+
put(input: { key: string; body: Buffer; contentType: string }): Promise<void>;
|
|
21
|
+
get(key: string): Promise<Buffer>;
|
|
22
|
+
remove(key: string): Promise<void>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Backward-compatible receipt-specific name for the shared object store. */
|
|
26
|
+
export type RuntimeReceiptValueBlobStore = RuntimeValueBlobStore;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Foundation owns value references and bounded I/O admission; the deployment
|
|
30
|
+
* injects its object-store adapter. This keeps R2 credentials and transport
|
|
31
|
+
* mechanics at the gateway edge, without a reverse package dependency.
|
|
32
|
+
*/
|
|
33
|
+
export function requireRuntimeValueBlobStore(
|
|
34
|
+
store: RuntimeValueBlobStore | undefined,
|
|
35
|
+
): RuntimeValueBlobStore {
|
|
36
|
+
if (!store) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'Runtime value storage requires an explicit object-store adapter.',
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return store;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type RuntimeReceiptValueStorageInput = {
|
|
45
|
+
orgId: string;
|
|
46
|
+
receiptKey: string;
|
|
47
|
+
serialized: string;
|
|
48
|
+
inlineFields?: RuntimeReceiptValueInlineFields;
|
|
49
|
+
inlineMaxBytes?: number;
|
|
50
|
+
store?: RuntimeReceiptValueBlobStore;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type PersistedRuntimeReceiptValue =
|
|
54
|
+
| { kind: 'inline'; outputJson: string }
|
|
55
|
+
| {
|
|
56
|
+
kind: 'reference';
|
|
57
|
+
outputJson: null;
|
|
58
|
+
outputRef: RuntimeReceiptValueReference;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type RuntimeReceiptValuePersister = (input: {
|
|
62
|
+
orgId: string;
|
|
63
|
+
receiptKey: string;
|
|
64
|
+
serialized: string;
|
|
65
|
+
inlineFields?: RuntimeReceiptValueInlineFields;
|
|
66
|
+
}) => Promise<PersistedRuntimeReceiptValue>;
|
|
67
|
+
|
|
68
|
+
export type RuntimeReceiptValueReader = (input: {
|
|
69
|
+
orgId: string;
|
|
70
|
+
receiptKey: string;
|
|
71
|
+
reference: RuntimeReceiptValueReference;
|
|
72
|
+
}) => Promise<unknown>;
|
|
73
|
+
|
|
74
|
+
function sha256(value: string | Buffer): string {
|
|
75
|
+
return createHash('sha256').update(value).digest('hex');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function requiredNonempty(value: string, label: string): string {
|
|
79
|
+
const normalized = value.trim();
|
|
80
|
+
if (!normalized) throw new Error(`${label} must be non-empty.`);
|
|
81
|
+
return normalized;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function nonnegativeInteger(value: number, label: string): number {
|
|
85
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
86
|
+
throw new Error(`${label} must be a non-negative safe integer.`);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function positiveInteger(value: number, label: string): number {
|
|
92
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
93
|
+
throw new Error(`${label} must be a positive safe integer.`);
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function receiptValueBindings(input: { orgId: string; receiptKey: string }): {
|
|
99
|
+
orgBinding: string;
|
|
100
|
+
receiptBinding: string;
|
|
101
|
+
} {
|
|
102
|
+
return {
|
|
103
|
+
orgBinding: sha256(`runtime-receipt-value/org/v1/${input.orgId}`),
|
|
104
|
+
receiptBinding: sha256(
|
|
105
|
+
`runtime-receipt-value/receipt/v1/${input.orgId}\0${input.receiptKey}`,
|
|
106
|
+
),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function runtimeReceiptValueStorageKey(input: {
|
|
111
|
+
orgId: string;
|
|
112
|
+
receiptKey: string;
|
|
113
|
+
digest: string;
|
|
114
|
+
}): string {
|
|
115
|
+
const orgId = requiredNonempty(input.orgId, 'orgId');
|
|
116
|
+
const receiptKey = requiredNonempty(input.receiptKey, 'receiptKey');
|
|
117
|
+
if (!/^[a-f0-9]{64}$/i.test(input.digest)) {
|
|
118
|
+
throw new Error('digest must be a SHA-256 hex digest.');
|
|
119
|
+
}
|
|
120
|
+
const { orgBinding, receiptBinding } = receiptValueBindings({
|
|
121
|
+
orgId,
|
|
122
|
+
receiptKey,
|
|
123
|
+
});
|
|
124
|
+
return `runtime/receipt-values/v1/${orgBinding}/${receiptBinding}/${input.digest}.json`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function referenceForBody(input: {
|
|
128
|
+
orgId: string;
|
|
129
|
+
receiptKey: string;
|
|
130
|
+
body: Buffer;
|
|
131
|
+
inlineFields?: RuntimeReceiptValueInlineFields;
|
|
132
|
+
}): RuntimeReceiptValueReference {
|
|
133
|
+
const orgId = requiredNonempty(input.orgId, 'orgId');
|
|
134
|
+
const receiptKey = requiredNonempty(input.receiptKey, 'receiptKey');
|
|
135
|
+
const digest = sha256(input.body);
|
|
136
|
+
const { orgBinding, receiptBinding } = receiptValueBindings({
|
|
137
|
+
orgId,
|
|
138
|
+
receiptKey,
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
storage: 'r2',
|
|
142
|
+
codec: 'json',
|
|
143
|
+
key: runtimeReceiptValueStorageKey({ orgId, receiptKey, digest }),
|
|
144
|
+
digest,
|
|
145
|
+
byteLength: input.body.byteLength,
|
|
146
|
+
orgBinding,
|
|
147
|
+
receiptBinding,
|
|
148
|
+
...(input.inlineFields ? { inlineFields: input.inlineFields } : {}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Store a body under its deterministic immutable key before its receipt row
|
|
154
|
+
* points at it. If a PUT response is lost after object creation, the exact
|
|
155
|
+
* object is discovered and reused; a caller never regenerates provider work to
|
|
156
|
+
* repair that ambiguity.
|
|
157
|
+
*/
|
|
158
|
+
export async function persistRuntimeReceiptValue(
|
|
159
|
+
input: RuntimeReceiptValueStorageInput,
|
|
160
|
+
): Promise<PersistedRuntimeReceiptValue> {
|
|
161
|
+
const orgId = requiredNonempty(input.orgId, 'orgId');
|
|
162
|
+
const receiptKey = requiredNonempty(input.receiptKey, 'receiptKey');
|
|
163
|
+
const body = Buffer.from(input.serialized, 'utf8');
|
|
164
|
+
const inlineMaxBytes = positiveInteger(
|
|
165
|
+
input.inlineMaxBytes ?? RUNTIME_RECEIPT_VALUE_INLINE_MAX_BYTES,
|
|
166
|
+
'inlineMaxBytes',
|
|
167
|
+
);
|
|
168
|
+
const inlineFields =
|
|
169
|
+
input.inlineFields === undefined
|
|
170
|
+
? undefined
|
|
171
|
+
: normalizeRuntimeReceiptValueInlineFields(input.inlineFields);
|
|
172
|
+
if (body.byteLength <= inlineMaxBytes) {
|
|
173
|
+
return { kind: 'inline', outputJson: input.serialized };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const store = requireRuntimeValueBlobStore(input.store);
|
|
177
|
+
const outputRef = referenceForBody({ orgId, receiptKey, body, inlineFields });
|
|
178
|
+
try {
|
|
179
|
+
await store.put({
|
|
180
|
+
key: outputRef.key,
|
|
181
|
+
body,
|
|
182
|
+
contentType: 'application/json',
|
|
183
|
+
});
|
|
184
|
+
} catch (error) {
|
|
185
|
+
// An immutable deterministic object that is observable after a failed PUT
|
|
186
|
+
// is the same attempted publication. Treat it as a recovered write, never
|
|
187
|
+
// as permission to call the provider again.
|
|
188
|
+
if (!(await store.exists(outputRef.key))) throw error;
|
|
189
|
+
}
|
|
190
|
+
return { kind: 'reference', outputJson: null, outputRef };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Build one process-scoped persister for a Fly gateway. It batches admission
|
|
195
|
+
* only: R2 has no multi-PUT, so each physical call remains one immutable PUT,
|
|
196
|
+
* while the common primitive bounds queued and active bytes across requests.
|
|
197
|
+
*/
|
|
198
|
+
export function createRuntimeReceiptValuePersister(
|
|
199
|
+
input: {
|
|
200
|
+
store?: RuntimeReceiptValueBlobStore;
|
|
201
|
+
inlineMaxBytes?: number;
|
|
202
|
+
} = {},
|
|
203
|
+
): RuntimeReceiptValuePersister {
|
|
204
|
+
const inlineMaxBytes =
|
|
205
|
+
input.inlineMaxBytes ?? RUNTIME_RECEIPT_VALUE_INLINE_MAX_BYTES;
|
|
206
|
+
type Write = {
|
|
207
|
+
orgId: string;
|
|
208
|
+
receiptKey: string;
|
|
209
|
+
serialized: string;
|
|
210
|
+
inlineFields?: RuntimeReceiptValueInlineFields;
|
|
211
|
+
};
|
|
212
|
+
const writes = new BoundedBatchFunction<
|
|
213
|
+
Write,
|
|
214
|
+
PersistedRuntimeReceiptValue,
|
|
215
|
+
'r2'
|
|
216
|
+
>({
|
|
217
|
+
keyId: () => 'r2',
|
|
218
|
+
estimateBytes: (value) => Buffer.byteLength(value.serialized, 'utf8'),
|
|
219
|
+
maxBatchItems: 4,
|
|
220
|
+
maxBatchBytes: 12 * 1024 * 1024,
|
|
221
|
+
maxBatchUnits: 4,
|
|
222
|
+
maxRetainedItems: 32,
|
|
223
|
+
maxRetainedBytes: 24 * 1024 * 1024,
|
|
224
|
+
maxRetainedUnits: 32,
|
|
225
|
+
maxInFlightBatches: 2,
|
|
226
|
+
flushMs: 0,
|
|
227
|
+
execute: async ({ values }) =>
|
|
228
|
+
await Promise.all(
|
|
229
|
+
values.map(
|
|
230
|
+
async (value) =>
|
|
231
|
+
await persistRuntimeReceiptValue({
|
|
232
|
+
...value,
|
|
233
|
+
inlineMaxBytes,
|
|
234
|
+
...(input.store ? { store: input.store } : {}),
|
|
235
|
+
}),
|
|
236
|
+
),
|
|
237
|
+
),
|
|
238
|
+
});
|
|
239
|
+
return async (value) => {
|
|
240
|
+
if (Buffer.byteLength(value.serialized, 'utf8') <= inlineMaxBytes) {
|
|
241
|
+
return await persistRuntimeReceiptValue({
|
|
242
|
+
...value,
|
|
243
|
+
inlineMaxBytes,
|
|
244
|
+
...(input.store ? { store: input.store } : {}),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return await writes.submit({ key: 'r2', value });
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Build one process-scoped reader for a Fly gateway. R2 has no multi-GET,
|
|
253
|
+
* so the batch is an admission boundary rather than a protocol claim: it
|
|
254
|
+
* keeps active and queued body bytes bounded across every inbound request
|
|
255
|
+
* while allowing independent immutable objects to use the connection pool.
|
|
256
|
+
*/
|
|
257
|
+
export function createRuntimeReceiptValueReader(
|
|
258
|
+
input: { store?: RuntimeReceiptValueBlobStore } = {},
|
|
259
|
+
): RuntimeReceiptValueReader {
|
|
260
|
+
type Read = {
|
|
261
|
+
orgId: string;
|
|
262
|
+
receiptKey: string;
|
|
263
|
+
reference: RuntimeReceiptValueReference;
|
|
264
|
+
};
|
|
265
|
+
const reads = new BoundedBatchFunction<Read, unknown, 'r2'>({
|
|
266
|
+
keyId: () => 'r2',
|
|
267
|
+
estimateBytes: (value) => value.reference.byteLength,
|
|
268
|
+
maxBatchItems: 4,
|
|
269
|
+
maxBatchBytes: 12 * 1024 * 1024,
|
|
270
|
+
maxBatchUnits: 4,
|
|
271
|
+
maxRetainedItems: 32,
|
|
272
|
+
maxRetainedBytes: 24 * 1024 * 1024,
|
|
273
|
+
maxRetainedUnits: 32,
|
|
274
|
+
maxInFlightBatches: 2,
|
|
275
|
+
flushMs: 0,
|
|
276
|
+
execute: async ({ values }) =>
|
|
277
|
+
await Promise.all(
|
|
278
|
+
values.map(
|
|
279
|
+
async (value) =>
|
|
280
|
+
await readRuntimeReceiptValue({
|
|
281
|
+
...value,
|
|
282
|
+
...(input.store ? { store: input.store } : {}),
|
|
283
|
+
}),
|
|
284
|
+
),
|
|
285
|
+
),
|
|
286
|
+
});
|
|
287
|
+
return async (value) => await reads.submit({ key: 'r2', value });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Verify that a compact reference is bound to the authenticated receipt that
|
|
292
|
+
* is about to store it. Execution relays use the same proof before a receipt
|
|
293
|
+
* gateway accepts their already-persisted result reference.
|
|
294
|
+
*/
|
|
295
|
+
export function assertRuntimeReceiptValueReferenceScope(input: {
|
|
296
|
+
orgId: string;
|
|
297
|
+
receiptKey: string;
|
|
298
|
+
reference: RuntimeReceiptValueReference;
|
|
299
|
+
}): void {
|
|
300
|
+
const orgId = requiredNonempty(input.orgId, 'orgId');
|
|
301
|
+
const receiptKey = requiredNonempty(input.receiptKey, 'receiptKey');
|
|
302
|
+
const bindings = receiptValueBindings({ orgId, receiptKey });
|
|
303
|
+
if (
|
|
304
|
+
input.reference.orgBinding !== bindings.orgBinding ||
|
|
305
|
+
input.reference.receiptBinding !== bindings.receiptBinding ||
|
|
306
|
+
input.reference.key !==
|
|
307
|
+
runtimeReceiptValueStorageKey({
|
|
308
|
+
orgId,
|
|
309
|
+
receiptKey,
|
|
310
|
+
digest: input.reference.digest,
|
|
311
|
+
})
|
|
312
|
+
) {
|
|
313
|
+
throw new RuntimeReceiptValueReadError(
|
|
314
|
+
'scope_mismatch',
|
|
315
|
+
'Runtime receipt value reference is outside the authenticated receipt scope.',
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function isMissingRuntimeReceiptValue(error: unknown): boolean {
|
|
321
|
+
if (!error || typeof error !== 'object') return false;
|
|
322
|
+
const record = error as Record<string, unknown>;
|
|
323
|
+
const code = String(record.code ?? record.name ?? '');
|
|
324
|
+
if (code === 'NoSuchKey' || code === 'NotFound') return true;
|
|
325
|
+
const metadata = record.$metadata;
|
|
326
|
+
return (
|
|
327
|
+
Boolean(metadata) &&
|
|
328
|
+
typeof metadata === 'object' &&
|
|
329
|
+
(metadata as Record<string, unknown>).httpStatusCode === 404
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Read, authenticate, size-check, and decode a referenced JSON receipt. */
|
|
334
|
+
export async function readRuntimeReceiptValue(input: {
|
|
335
|
+
orgId: string;
|
|
336
|
+
receiptKey: string;
|
|
337
|
+
reference: RuntimeReceiptValueReference;
|
|
338
|
+
store?: RuntimeReceiptValueBlobStore;
|
|
339
|
+
}): Promise<unknown> {
|
|
340
|
+
assertRuntimeReceiptValueReferenceScope(input);
|
|
341
|
+
const store = requireRuntimeValueBlobStore(input.store);
|
|
342
|
+
let body: Buffer;
|
|
343
|
+
try {
|
|
344
|
+
body = await store.get(input.reference.key);
|
|
345
|
+
} catch (error) {
|
|
346
|
+
throw new RuntimeReceiptValueReadError(
|
|
347
|
+
isMissingRuntimeReceiptValue(error) ? 'missing' : 'unavailable',
|
|
348
|
+
`Runtime receipt value could not be read: ${
|
|
349
|
+
error instanceof Error ? error.message : 'unknown storage error'
|
|
350
|
+
}`,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
if (
|
|
354
|
+
body.byteLength !==
|
|
355
|
+
nonnegativeInteger(input.reference.byteLength, 'byteLength') ||
|
|
356
|
+
sha256(body) !== input.reference.digest
|
|
357
|
+
) {
|
|
358
|
+
throw new RuntimeReceiptValueReadError(
|
|
359
|
+
'corrupt',
|
|
360
|
+
'Runtime receipt value failed its stored size or digest check.',
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
return JSON.parse(body.toString('utf8')) as unknown;
|
|
365
|
+
} catch {
|
|
366
|
+
throw new RuntimeReceiptValueReadError(
|
|
367
|
+
'corrupt',
|
|
368
|
+
'Runtime receipt value is not valid JSON for its declared codec.',
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { statfsSync } from 'node:fs';
|
|
2
2
|
import { cpus } from 'node:os';
|
|
3
|
+
import { getHeapStatistics } from 'node:v8';
|
|
3
4
|
|
|
4
5
|
type ReadFile = (path: string, encoding: 'utf8') => string;
|
|
5
6
|
|
|
@@ -8,13 +9,62 @@ export type WorkerResourceMetrics = {
|
|
|
8
9
|
cpuLimitCores: number;
|
|
9
10
|
memoryUsageBytes: number;
|
|
10
11
|
memoryLimitBytes: number;
|
|
12
|
+
memoryLimitSource: 'cgroup' | 'process_fallback';
|
|
11
13
|
memoryUtilizationRatio: number;
|
|
14
|
+
heapUsedBytes: number;
|
|
15
|
+
heapLimitBytes: number;
|
|
16
|
+
heapUtilizationRatio: number;
|
|
12
17
|
filesystemUsageBytes: number;
|
|
13
18
|
filesystemAvailableBytes: number;
|
|
14
19
|
filesystemTotalBytes: number;
|
|
15
20
|
filesystemUtilizationRatio: number;
|
|
16
21
|
};
|
|
17
22
|
|
|
23
|
+
export const WORKER_CPU_PRESSURE_RATIO = 0.95;
|
|
24
|
+
export const WORKER_MEMORY_PRESSURE_RATIO = 0.9;
|
|
25
|
+
export const WORKER_HEAP_PRESSURE_RATIO = 0.88;
|
|
26
|
+
|
|
27
|
+
export type WorkerResourcePressureReason = 'cpu' | 'memory' | 'heap';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resource pressure is an admission signal, not a terminal outcome. A worker
|
|
31
|
+
* should stop claiming new durable work while the scaler replaces it; the
|
|
32
|
+
* engine still owns already-claimed tasks and drains them before close.
|
|
33
|
+
*
|
|
34
|
+
* Memory pressure requires a real cgroup limit. A local process fallback uses
|
|
35
|
+
* RSS as both the observed value and its fallback limit, so it must never
|
|
36
|
+
* masquerade as a saturated container.
|
|
37
|
+
*/
|
|
38
|
+
export function workerResourcePressureReasons(
|
|
39
|
+
metrics: Pick<
|
|
40
|
+
WorkerResourceMetrics,
|
|
41
|
+
| 'cpuUtilizationRatio'
|
|
42
|
+
| 'memoryLimitSource'
|
|
43
|
+
| 'memoryUtilizationRatio'
|
|
44
|
+
| 'heapUtilizationRatio'
|
|
45
|
+
>,
|
|
46
|
+
thresholds: Readonly<{
|
|
47
|
+
cpu?: number;
|
|
48
|
+
memory?: number;
|
|
49
|
+
heap?: number;
|
|
50
|
+
}> = {},
|
|
51
|
+
): WorkerResourcePressureReason[] {
|
|
52
|
+
const cpuThreshold = thresholds.cpu ?? WORKER_CPU_PRESSURE_RATIO;
|
|
53
|
+
const memoryThreshold =
|
|
54
|
+
thresholds.memory ?? WORKER_MEMORY_PRESSURE_RATIO;
|
|
55
|
+
const heapThreshold = thresholds.heap ?? WORKER_HEAP_PRESSURE_RATIO;
|
|
56
|
+
const reasons: WorkerResourcePressureReason[] = [];
|
|
57
|
+
if (metrics.cpuUtilizationRatio >= cpuThreshold) reasons.push('cpu');
|
|
58
|
+
if (
|
|
59
|
+
metrics.memoryLimitSource === 'cgroup' &&
|
|
60
|
+
metrics.memoryUtilizationRatio >= memoryThreshold
|
|
61
|
+
) {
|
|
62
|
+
reasons.push('memory');
|
|
63
|
+
}
|
|
64
|
+
if (metrics.heapUtilizationRatio >= heapThreshold) reasons.push('heap');
|
|
65
|
+
return reasons;
|
|
66
|
+
}
|
|
67
|
+
|
|
18
68
|
/** A zero-sized filesystem report means the probe itself is unavailable; a
|
|
19
69
|
* nonzero filesystem with zero free bytes is the most urgent pressure state. */
|
|
20
70
|
export function hasFilesystemPressure(
|
|
@@ -60,6 +110,15 @@ export function renderRuntimeProcessResourceMetrics(input: {
|
|
|
60
110
|
'# HELP deepline_runtime_process_memory_utilization_percent Container memory utilization normalized to its memory limit.',
|
|
61
111
|
'# TYPE deepline_runtime_process_memory_utilization_percent gauge',
|
|
62
112
|
`deepline_runtime_process_memory_utilization_percent{${labels}} ${input.metrics.memoryUtilizationRatio * 100}`,
|
|
113
|
+
'# HELP deepline_runtime_process_heap_used_bytes V8 heap currently used.',
|
|
114
|
+
'# TYPE deepline_runtime_process_heap_used_bytes gauge',
|
|
115
|
+
`deepline_runtime_process_heap_used_bytes{${labels}} ${input.metrics.heapUsedBytes}`,
|
|
116
|
+
'# HELP deepline_runtime_process_heap_limit_bytes V8 heap limit.',
|
|
117
|
+
'# TYPE deepline_runtime_process_heap_limit_bytes gauge',
|
|
118
|
+
`deepline_runtime_process_heap_limit_bytes{${labels}} ${input.metrics.heapLimitBytes}`,
|
|
119
|
+
'# HELP deepline_runtime_process_heap_utilization_percent V8 heap utilization normalized to its heap limit.',
|
|
120
|
+
'# TYPE deepline_runtime_process_heap_utilization_percent gauge',
|
|
121
|
+
`deepline_runtime_process_heap_utilization_percent{${labels}} ${input.metrics.heapUtilizationRatio * 100}`,
|
|
63
122
|
'# HELP deepline_runtime_process_filesystem_usage_bytes Local /tmp filesystem space used.',
|
|
64
123
|
'# TYPE deepline_runtime_process_filesystem_usage_bytes gauge',
|
|
65
124
|
`deepline_runtime_process_filesystem_usage_bytes{${labels}} ${input.metrics.filesystemUsageBytes}`,
|
|
@@ -158,11 +217,13 @@ export function createWorkerResourceMetricsReader(input: {
|
|
|
158
217
|
now?: () => number;
|
|
159
218
|
cpuCount?: () => number;
|
|
160
219
|
memoryUsage?: () => NodeJS.MemoryUsage;
|
|
220
|
+
heapLimit?: () => number;
|
|
161
221
|
statfs?: (path: string) => FileSystemStats;
|
|
162
222
|
}) {
|
|
163
223
|
const now = input.now ?? Date.now;
|
|
164
224
|
const cpuCount = input.cpuCount ?? (() => cpus().length || 1);
|
|
165
225
|
const memoryUsage = input.memoryUsage ?? process.memoryUsage;
|
|
226
|
+
const heapLimit = input.heapLimit ?? (() => getHeapStatistics().heap_size_limit);
|
|
166
227
|
const statfs = input.statfs ?? statfsSync;
|
|
167
228
|
let previousCpu: CpuSample | null = null;
|
|
168
229
|
|
|
@@ -208,7 +269,8 @@ export function createWorkerResourceMetricsReader(input: {
|
|
|
208
269
|
previousCpu = { usageMicroseconds: cpuUsageMicroseconds, sampledAtMs };
|
|
209
270
|
}
|
|
210
271
|
|
|
211
|
-
const
|
|
272
|
+
const processMemory = memoryUsage();
|
|
273
|
+
const fallbackMemory = processMemory.rss;
|
|
212
274
|
const filesystem = (() => {
|
|
213
275
|
try {
|
|
214
276
|
return parseFileSystemStats(statfs('/tmp'));
|
|
@@ -216,27 +278,44 @@ export function createWorkerResourceMetricsReader(input: {
|
|
|
216
278
|
return parseFileSystemStats(null);
|
|
217
279
|
}
|
|
218
280
|
})();
|
|
219
|
-
const
|
|
281
|
+
const cgroupMemoryUsage =
|
|
220
282
|
read('/sys/fs/cgroup/memory.current') ??
|
|
221
|
-
|
|
222
|
-
|
|
283
|
+
read('/sys/fs/cgroup/memory/memory.usage_in_bytes');
|
|
284
|
+
const cgroupMemoryLimit =
|
|
285
|
+
read('/sys/fs/cgroup/memory.max') ??
|
|
286
|
+
read('/sys/fs/cgroup/memory/memory.limit_in_bytes');
|
|
287
|
+
const memoryUsageBytes = parseCgroupLimit(
|
|
288
|
+
cgroupMemoryUsage ?? '',
|
|
223
289
|
fallbackMemory,
|
|
224
290
|
);
|
|
225
291
|
const memoryLimitBytes = parseCgroupLimit(
|
|
226
|
-
|
|
227
|
-
read('/sys/fs/cgroup/memory/memory.limit_in_bytes') ??
|
|
228
|
-
'',
|
|
292
|
+
cgroupMemoryLimit ?? '',
|
|
229
293
|
fallbackMemory,
|
|
230
294
|
);
|
|
295
|
+
const heapUsedBytes = finiteNonNegative(
|
|
296
|
+
processMemory.heapUsed,
|
|
297
|
+
0,
|
|
298
|
+
);
|
|
299
|
+
const heapLimitBytes = finiteNonNegative(heapLimit(), 0);
|
|
231
300
|
return {
|
|
232
301
|
cpuUtilizationRatio,
|
|
233
302
|
cpuLimitCores,
|
|
234
303
|
memoryUsageBytes,
|
|
235
304
|
memoryLimitBytes,
|
|
305
|
+
memoryLimitSource:
|
|
306
|
+
cgroupMemoryLimit === null ||
|
|
307
|
+
cgroupMemoryLimit === undefined ||
|
|
308
|
+
cgroupMemoryLimit.trim() === 'max'
|
|
309
|
+
? 'process_fallback'
|
|
310
|
+
: 'cgroup',
|
|
236
311
|
memoryUtilizationRatio:
|
|
237
312
|
memoryLimitBytes > 0
|
|
238
313
|
? Math.min(1, memoryUsageBytes / memoryLimitBytes)
|
|
239
314
|
: 0,
|
|
315
|
+
heapUsedBytes,
|
|
316
|
+
heapLimitBytes,
|
|
317
|
+
heapUtilizationRatio:
|
|
318
|
+
heapLimitBytes > 0 ? Math.min(1, heapUsedBytes / heapLimitBytes) : 0,
|
|
240
319
|
filesystemUsageBytes: filesystem.usageBytes,
|
|
241
320
|
filesystemAvailableBytes: filesystem.availableBytes,
|
|
242
321
|
filesystemTotalBytes: filesystem.totalBytes,
|