deepline 0.3.289 → 0.3.290
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +30 -10
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +108 -12
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +6 -5
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-event-catalog.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +31 -3
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +23 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +30 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +38 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +22 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool-options.ts +145 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +1 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -139
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +19 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +37 -15
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +7 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +961 -281
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +55 -25
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +97 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +33 -14
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +452 -43
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +10 -1
- package/dist/bundling-sources/shared_libs/play-runtime/test-admission-batching.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +75 -28
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.mjs +3 -1
- package/dist/index.js +3 -1
- package/dist/index.mjs +3 -1
- package/dist/install-integrity.json +3 -0
- 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
|
@@ -11,7 +11,6 @@ import { RUN_EVENT_KIND } from '../run-event-catalog';
|
|
|
11
11
|
* absurd-specific wiring (queue, event names, checkpoint boundary shaping).
|
|
12
12
|
*/
|
|
13
13
|
import { createHash } from 'node:crypto';
|
|
14
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
15
14
|
import type {
|
|
16
15
|
PlayCheckpoint,
|
|
17
16
|
PlayExecutionEvent,
|
|
@@ -22,11 +21,6 @@ import type { PlayRunLedgerEvent } from '../run-ledger';
|
|
|
22
21
|
import type { PlaySchedulerSubmitInput } from '../scheduler-backend';
|
|
23
22
|
import type { WorkerRuntimeApiContext } from '../app-runtime-api';
|
|
24
23
|
import { RUNTIME_CAPACITY_POLICY } from '../runtime-capacity-policy';
|
|
25
|
-
import {
|
|
26
|
-
PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS,
|
|
27
|
-
PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES,
|
|
28
|
-
PLAY_ADMISSION_MAX_PARALLELISM,
|
|
29
|
-
} from '../admission-transport-policy';
|
|
30
24
|
import { assertKnownResolvedBoundary } from '../checkpoint-boundaries';
|
|
31
25
|
import type { Absurd, Log, RetryStrategy } from '@deepline/absurd/sdk';
|
|
32
26
|
import { isIsolatedRuntimeSchedulerSchema } from '../runtime-scheduler-topology';
|
|
@@ -37,8 +31,6 @@ import {
|
|
|
37
31
|
} from './postgres';
|
|
38
32
|
import { createAbsurdForRuntimeScheduler } from './absurd-pool';
|
|
39
33
|
import {
|
|
40
|
-
RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS,
|
|
41
|
-
resolveWorkflowRuntimePostgresUrl,
|
|
42
34
|
withRuntimeSchedulerClient,
|
|
43
35
|
type RuntimeSchedulerPoolOptions,
|
|
44
36
|
} from './postgres-pool';
|
|
@@ -548,137 +540,14 @@ export type AbsurdRuntimeResult = {
|
|
|
548
540
|
error?: string;
|
|
549
541
|
};
|
|
550
542
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
* Vercel Fluid invocations share a process and its scheduler pool. A single
|
|
560
|
-
* client serializes unrelated launches, reads, and critical lease lookups;
|
|
561
|
-
* one held connection can make every other request hit its acquire timeout.
|
|
562
|
-
* Use the shared two-client control-plane budget so another request can make
|
|
563
|
-
* progress while keeping each instance's PgBouncer footprint bounded.
|
|
564
|
-
*/
|
|
565
|
-
export const ABSURD_SERVERLESS_POOL_MAX_CLIENTS =
|
|
566
|
-
RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS;
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* A Vercel isolate may survive long after a webhook has returned. Keep its
|
|
570
|
-
* scheduler clients only long enough to reuse them for an immediate follow-up,
|
|
571
|
-
* then return idle PgBouncer clients before the source's delayed Workpool
|
|
572
|
-
* recovery is due. The worker fleet keeps the ordinary 30-second idle lifetime.
|
|
573
|
-
*/
|
|
574
|
-
export const ABSURD_SERVERLESS_POOL_IDLE_TIMEOUT_MS = 1_000;
|
|
575
|
-
|
|
576
|
-
// Ordinary Vercel requests share the bounded control-plane pool above. The
|
|
577
|
-
// private batch adapter is different: Workpool elects one global up-to-1,024-
|
|
578
|
-
// source collection before entering this context, so one batch request may use
|
|
579
|
-
// its larger bounded scheduler capacity without multiplying it per source.
|
|
580
|
-
// Other Workpool adapters can still overlap during rollout.
|
|
581
|
-
type ServerlessAdmissionPoolContext = {
|
|
582
|
-
maxClients: number;
|
|
583
|
-
maxAcquireQueueDepth: number;
|
|
584
|
-
maxAcquireQueueWaitMs: number;
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
const serverlessAdmissionPoolContext =
|
|
588
|
-
new AsyncLocalStorage<ServerlessAdmissionPoolContext>();
|
|
589
|
-
|
|
590
|
-
export async function withServerlessAdmissionPoolMaxClients<T>(input: {
|
|
591
|
-
maxClients: number;
|
|
592
|
-
maxAcquireQueueDepth?: number;
|
|
593
|
-
maxAcquireQueueWaitMs?: number;
|
|
594
|
-
fn: () => Promise<T>;
|
|
595
|
-
}): Promise<T> {
|
|
596
|
-
if (
|
|
597
|
-
!Number.isInteger(input.maxClients) ||
|
|
598
|
-
input.maxClients < 1 ||
|
|
599
|
-
input.maxClients > PLAY_ADMISSION_MAX_PARALLELISM
|
|
600
|
-
) {
|
|
601
|
-
throw new Error(
|
|
602
|
-
'Serverless admission pool capacity is outside the supported range.',
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
const maxAcquireQueueDepth =
|
|
606
|
-
input.maxAcquireQueueDepth ?? PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES;
|
|
607
|
-
const maxAcquireQueueWaitMs =
|
|
608
|
-
input.maxAcquireQueueWaitMs ?? PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS;
|
|
609
|
-
if (
|
|
610
|
-
!Number.isSafeInteger(maxAcquireQueueDepth) ||
|
|
611
|
-
maxAcquireQueueDepth < 1 ||
|
|
612
|
-
maxAcquireQueueDepth > PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES
|
|
613
|
-
) {
|
|
614
|
-
throw new Error(
|
|
615
|
-
'Serverless admission acquire queue capacity is outside the supported range.',
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
if (
|
|
619
|
-
!Number.isSafeInteger(maxAcquireQueueWaitMs) ||
|
|
620
|
-
maxAcquireQueueWaitMs < 5_000 ||
|
|
621
|
-
maxAcquireQueueWaitMs > PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS
|
|
622
|
-
) {
|
|
623
|
-
throw new Error(
|
|
624
|
-
'Serverless admission acquire queue wait is outside the supported range.',
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
return await serverlessAdmissionPoolContext.run(
|
|
628
|
-
{
|
|
629
|
-
maxClients: input.maxClients,
|
|
630
|
-
maxAcquireQueueDepth,
|
|
631
|
-
maxAcquireQueueWaitMs,
|
|
632
|
-
},
|
|
633
|
-
input.fn,
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
/**
|
|
638
|
-
* The private batch adapter owns this request-local scheduler pool. Reserve
|
|
639
|
-
* one client for executor-token and other critical control-plane traffic; the
|
|
640
|
-
* rest may submit the independently idempotent batch sources in parallel.
|
|
641
|
-
*/
|
|
642
|
-
export function serverlessAdmissionSubmitMaxActive(): number | null {
|
|
643
|
-
const context = serverlessAdmissionPoolContext.getStore();
|
|
644
|
-
return context === undefined ? null : Math.max(1, context.maxClients - 1);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Resolve the runtime scheduler Postgres pool options for the absurd control
|
|
649
|
-
* plane. Loud failure over silent fallback: the absurd worker and scheduler
|
|
650
|
-
* only run under Node where `WORKFLOW_RUNTIME_POSTGRES` is configured.
|
|
651
|
-
*/
|
|
652
|
-
export function resolveAbsurdPoolOptions(
|
|
653
|
-
explicitUrl?: string | null,
|
|
654
|
-
): RuntimeSchedulerPoolOptions {
|
|
655
|
-
const url = explicitUrl?.trim() || resolveWorkflowRuntimePostgresUrl();
|
|
656
|
-
if (!url) {
|
|
657
|
-
throw new Error(
|
|
658
|
-
'absurd scheduler backend requires WORKFLOW_RUNTIME_POSTGRES (runtime ' +
|
|
659
|
-
'scheduler Postgres URL). Refusing to fall back to local execution.',
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
const serverless = process.env.VERCEL?.trim();
|
|
663
|
-
const context = serverlessAdmissionPoolContext.getStore();
|
|
664
|
-
return {
|
|
665
|
-
url,
|
|
666
|
-
applicationName: 'deepline-absurd-runtime',
|
|
667
|
-
maxClients:
|
|
668
|
-
serverless || context !== undefined
|
|
669
|
-
? (context?.maxClients ?? ABSURD_SERVERLESS_POOL_MAX_CLIENTS)
|
|
670
|
-
: ABSURD_RUNTIME_POOL_MAX_CLIENTS,
|
|
671
|
-
...(serverless || context !== undefined
|
|
672
|
-
? { idleTimeoutMs: ABSURD_SERVERLESS_POOL_IDLE_TIMEOUT_MS }
|
|
673
|
-
: {}),
|
|
674
|
-
...(context
|
|
675
|
-
? {
|
|
676
|
-
maxAcquireQueueDepth: context.maxAcquireQueueDepth,
|
|
677
|
-
maxAcquireQueueWaitMs: context.maxAcquireQueueWaitMs,
|
|
678
|
-
}
|
|
679
|
-
: {}),
|
|
680
|
-
};
|
|
681
|
-
}
|
|
543
|
+
export {
|
|
544
|
+
ABSURD_RUNTIME_POOL_MAX_CLIENTS,
|
|
545
|
+
ABSURD_SERVERLESS_POOL_MAX_CLIENTS,
|
|
546
|
+
ABSURD_SERVERLESS_POOL_IDLE_TIMEOUT_MS,
|
|
547
|
+
withServerlessAdmissionPoolMaxClients,
|
|
548
|
+
serverlessAdmissionSubmitMaxActive,
|
|
549
|
+
resolveAbsurdPoolOptions,
|
|
550
|
+
} from './absurd-pool-options';
|
|
682
551
|
|
|
683
552
|
export function normalizeAbsurdSchedulerOptions(
|
|
684
553
|
options: { url?: string | null; schema?: string | null } = {},
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* connection. Notifications are strictly a wakeup hint — the tables remain
|
|
23
23
|
* the source of truth and every waiter still reads the DB.
|
|
24
24
|
*/
|
|
25
|
+
import { attachDatabasePool } from '@vercel/functions/db-connections';
|
|
25
26
|
import { Client, Pool } from 'pg';
|
|
26
27
|
import {
|
|
27
28
|
RuntimeSchedulerPgBouncerCapacityError,
|
|
@@ -175,6 +176,8 @@ export const RUNTIME_SCHEDULER_POOL_DEFAULTS = {
|
|
|
175
176
|
keepAliveIntervalMs: 60_000,
|
|
176
177
|
} as const;
|
|
177
178
|
|
|
179
|
+
export const RUNTIME_SCHEDULER_SERVERLESS_IDLE_TIMEOUT_MS = 1_000;
|
|
180
|
+
|
|
178
181
|
/**
|
|
179
182
|
* A control-plane process only needs a few short scheduler transactions at
|
|
180
183
|
* once. This shared cap keeps horizontally scaled workers and the receipt
|
|
@@ -197,8 +200,8 @@ export type RuntimeSchedulerPoolOptions = {
|
|
|
197
200
|
maxClients?: number;
|
|
198
201
|
/**
|
|
199
202
|
* Serverless launchers must not pin a scarce PgBouncer client between
|
|
200
|
-
* requests.
|
|
201
|
-
*
|
|
203
|
+
* requests. Vercel pools share one short idle lifetime; this override only
|
|
204
|
+
* applies to long-lived processes.
|
|
202
205
|
*/
|
|
203
206
|
idleTimeoutMs?: number;
|
|
204
207
|
/**
|
|
@@ -447,6 +450,7 @@ export class RuntimeSchedulerPoolAdmission {
|
|
|
447
450
|
|
|
448
451
|
type PoolEntry = {
|
|
449
452
|
pool: Pool;
|
|
453
|
+
databasePoolAttached?: boolean;
|
|
450
454
|
maxClients: number;
|
|
451
455
|
maxAcquireQueueDepth: number;
|
|
452
456
|
admission: RuntimeSchedulerPoolAdmission;
|
|
@@ -723,7 +727,7 @@ function getPoolEntry(options: RuntimeSchedulerPoolOptions): PoolEntry {
|
|
|
723
727
|
assertRuntimeSchedulerHotPathUrl(options.url);
|
|
724
728
|
const maxClients =
|
|
725
729
|
options.maxClients ?? RUNTIME_SCHEDULER_POOL_DEFAULTS.maxClients;
|
|
726
|
-
const
|
|
730
|
+
const requestedIdleTimeoutMs =
|
|
727
731
|
options.idleTimeoutMs ?? RUNTIME_SCHEDULER_POOL_DEFAULTS.idleTimeoutMs;
|
|
728
732
|
const maxAcquireQueueDepth =
|
|
729
733
|
options.maxAcquireQueueDepth ??
|
|
@@ -738,11 +742,17 @@ function getPoolEntry(options: RuntimeSchedulerPoolOptions): PoolEntry {
|
|
|
738
742
|
'instead of widening a single process pool.',
|
|
739
743
|
);
|
|
740
744
|
}
|
|
741
|
-
if (
|
|
745
|
+
if (
|
|
746
|
+
!Number.isSafeInteger(requestedIdleTimeoutMs) ||
|
|
747
|
+
requestedIdleTimeoutMs < 0
|
|
748
|
+
) {
|
|
742
749
|
throw new Error(
|
|
743
|
-
`Runtime scheduler pool idleTimeoutMs=${
|
|
750
|
+
`Runtime scheduler pool idleTimeoutMs=${requestedIdleTimeoutMs} must be a non-negative integer.`,
|
|
744
751
|
);
|
|
745
752
|
}
|
|
753
|
+
const idleTimeoutMs = process.env.VERCEL?.trim()
|
|
754
|
+
? RUNTIME_SCHEDULER_SERVERLESS_IDLE_TIMEOUT_MS
|
|
755
|
+
: requestedIdleTimeoutMs;
|
|
746
756
|
if (
|
|
747
757
|
!Number.isSafeInteger(maxAcquireQueueDepth) ||
|
|
748
758
|
maxAcquireQueueDepth < 1 ||
|
|
@@ -1049,6 +1059,10 @@ export async function withRuntimeSchedulerClient<T>(
|
|
|
1049
1059
|
let queryCount = 0;
|
|
1050
1060
|
let queryMs = 0;
|
|
1051
1061
|
try {
|
|
1062
|
+
if (!entry.databasePoolAttached) {
|
|
1063
|
+
attachDatabasePool(entry.pool);
|
|
1064
|
+
entry.databasePoolAttached = true;
|
|
1065
|
+
}
|
|
1052
1066
|
throwIfSchedulerAborted(signal);
|
|
1053
1067
|
const result = await fn({
|
|
1054
1068
|
query: async (sql, params) => {
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { RUN_EVENT_KIND } from '../run-event-catalog';
|
|
|
14
14
|
* Runtime Sheet row durability belongs to the context-level row writer.
|
|
15
15
|
*/
|
|
16
16
|
import { PlayStepLifecycleTracker } from '../step-lifecycle-tracker';
|
|
17
|
+
import { isDurableDirectRunnerLog } from '../log-provenance';
|
|
17
18
|
import {
|
|
18
19
|
updateRunStatusViaAppRuntime,
|
|
19
20
|
type RuntimeStatusUpdate,
|
|
@@ -268,18 +269,7 @@ function shouldPublishDurableLogEvent(
|
|
|
268
269
|
| Pick<PlayRunnerLogEvent, 'source' | 'line'>
|
|
269
270
|
| { source?: unknown; line?: unknown },
|
|
270
271
|
): boolean {
|
|
271
|
-
|
|
272
|
-
// create/upload/execute). Keep those in worker logs while still storing
|
|
273
|
-
// play-authored ctx.log events in the durable Run Log Stream. The one
|
|
274
|
-
// exception is the structured performance marker: it is bounded (one
|
|
275
|
-
// terminal phase observation per runner phase), run-scoped, and is the
|
|
276
|
-
// canonical black-box regression signal for dispatcher throughput. Do not
|
|
277
|
-
// broaden this to arbitrary worker output.
|
|
278
|
-
return (
|
|
279
|
-
mode !== 'direct_worker' ||
|
|
280
|
-
event.source !== 'worker' ||
|
|
281
|
-
(typeof event.line === 'string' && event.line.startsWith('[perf-trace] '))
|
|
282
|
-
);
|
|
272
|
+
return mode !== 'direct_worker' || isDurableDirectRunnerLog(event);
|
|
283
273
|
}
|
|
284
274
|
|
|
285
275
|
function shouldPublishTerminalResultLog(
|
|
@@ -305,14 +295,25 @@ type TerminalLogReplayPayload = NonNullable<
|
|
|
305
295
|
function terminalReplayEntriesForMode(input: {
|
|
306
296
|
resultLogs: readonly string[];
|
|
307
297
|
resultLogContexts?: readonly (PlayLogContext | null)[];
|
|
298
|
+
resultLogIds?: readonly string[];
|
|
308
299
|
durableLogMode: PostgresSchedulerDurableLogMode;
|
|
309
|
-
}): Array<{
|
|
300
|
+
}): Array<{
|
|
301
|
+
line: string;
|
|
302
|
+
context: PlayLogContext | null;
|
|
303
|
+
entryId: string | null;
|
|
304
|
+
}> {
|
|
310
305
|
if (
|
|
311
306
|
input.resultLogContexts !== undefined &&
|
|
312
307
|
input.resultLogContexts.length !== input.resultLogs.length
|
|
313
308
|
) {
|
|
314
309
|
throw new TypeError('Terminal log contexts must align with result logs.');
|
|
315
310
|
}
|
|
311
|
+
if (
|
|
312
|
+
input.resultLogIds &&
|
|
313
|
+
input.resultLogIds.length !== input.resultLogs.length
|
|
314
|
+
) {
|
|
315
|
+
throw new TypeError('Terminal log identities must align with result logs.');
|
|
316
|
+
}
|
|
316
317
|
return input.resultLogs.flatMap((rawLine, index) => {
|
|
317
318
|
const line = rawLine.trim();
|
|
318
319
|
if (
|
|
@@ -325,6 +326,7 @@ function terminalReplayEntriesForMode(input: {
|
|
|
325
326
|
{
|
|
326
327
|
line,
|
|
327
328
|
context: normalizePlayLogContext(input.resultLogContexts?.[index]),
|
|
329
|
+
entryId: input.resultLogIds?.[index] ?? null,
|
|
328
330
|
},
|
|
329
331
|
];
|
|
330
332
|
});
|
|
@@ -333,6 +335,7 @@ function terminalReplayEntriesForMode(input: {
|
|
|
333
335
|
function terminalReplayPayloadForMode(input: {
|
|
334
336
|
resultLogs: readonly string[];
|
|
335
337
|
resultLogContexts?: readonly (PlayLogContext | null)[];
|
|
338
|
+
resultLogIds?: readonly string[];
|
|
336
339
|
durableLogMode: PostgresSchedulerDurableLogMode;
|
|
337
340
|
liveLogs: readonly string[];
|
|
338
341
|
liveLogContexts: readonly (PlayLogContext | null)[];
|
|
@@ -351,6 +354,7 @@ function terminalReplayPayloadForMode(input: {
|
|
|
351
354
|
let totalCount = entries.length;
|
|
352
355
|
if (
|
|
353
356
|
input.durableLogMode === 'direct_worker' &&
|
|
357
|
+
!input.resultLogIds &&
|
|
354
358
|
!input.replayAllTerminalLogs
|
|
355
359
|
) {
|
|
356
360
|
const observedCounts = new Map<string, number>();
|
|
@@ -380,6 +384,9 @@ function terminalReplayPayloadForMode(input: {
|
|
|
380
384
|
return {
|
|
381
385
|
lines,
|
|
382
386
|
...(lineContexts.some(Boolean) ? { lineContexts } : {}),
|
|
387
|
+
...(input.resultLogIds
|
|
388
|
+
? { lineIds: entries.map((entry) => entry.entryId) }
|
|
389
|
+
: {}),
|
|
383
390
|
...(input.replayAllTerminalLogs ? { dedupeMode: 'exact' as const } : {}),
|
|
384
391
|
...(typeof input.producerAttempt === 'number'
|
|
385
392
|
? { producerAttempt: input.producerAttempt }
|
|
@@ -439,6 +446,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
439
446
|
let nodeProgressSealed = false;
|
|
440
447
|
let liveLogs: string[] = [];
|
|
441
448
|
let liveLogContexts: Array<PlayLogContext | null> = [];
|
|
449
|
+
let liveLogIds: Array<string | null> = [];
|
|
442
450
|
let liveLogsTotalCount = input.initialLiveLogTotalCount ?? 0;
|
|
443
451
|
let liveLogsDirty = false;
|
|
444
452
|
let workCounters: RuntimeStatusUpdate['workCounters'];
|
|
@@ -541,6 +549,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
541
549
|
: undefined;
|
|
542
550
|
const nextLogs = liveLogs.slice();
|
|
543
551
|
const nextLogContexts = liveLogContexts.slice();
|
|
552
|
+
const nextLogIds = liveLogIds.slice();
|
|
544
553
|
const nextLogTotalCount = liveLogsTotalCount;
|
|
545
554
|
const nextDatasetLifecycleEvents = includeProgress
|
|
546
555
|
? pendingDatasetLifecycleEvents
|
|
@@ -566,7 +575,11 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
566
575
|
liveNodeProgress: nodeProgressSealed ? {} : nextProgress,
|
|
567
576
|
liveLogs: nextLogs,
|
|
568
577
|
liveLogContexts: nextLogContexts,
|
|
578
|
+
liveLogIds: nextLogIds,
|
|
569
579
|
liveLogTotalCount: nextLogTotalCount,
|
|
580
|
+
...(input.initialLiveLogTotalCount !== undefined
|
|
581
|
+
? { liveLogBatchOffset: nextLogTotalCount - nextLogs.length }
|
|
582
|
+
: {}),
|
|
570
583
|
liveLogProducerAttempt: producerAttempt,
|
|
571
584
|
datasetLifecycleEvents: nextDatasetLifecycleEvents,
|
|
572
585
|
activityObservations: nextActivityObservations,
|
|
@@ -667,7 +680,11 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
667
680
|
void flush({ force }).catch(() => undefined);
|
|
668
681
|
};
|
|
669
682
|
|
|
670
|
-
const appendLiveLog = (
|
|
683
|
+
const appendLiveLog = (
|
|
684
|
+
line: string,
|
|
685
|
+
context?: unknown,
|
|
686
|
+
entryId?: unknown,
|
|
687
|
+
) => {
|
|
671
688
|
const trimmed = line.trim();
|
|
672
689
|
if (!trimmed) {
|
|
673
690
|
return;
|
|
@@ -675,8 +692,10 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
675
692
|
liveLogsTotalCount += 1;
|
|
676
693
|
liveLogs = [...liveLogs, trimmed];
|
|
677
694
|
liveLogContexts = [...liveLogContexts, normalizePlayLogContext(context)];
|
|
695
|
+
liveLogIds = [...liveLogIds, typeof entryId === 'string' ? entryId : null];
|
|
678
696
|
if (liveLogs.length > LIVE_LOG_TAIL_LIMIT) {
|
|
679
697
|
liveLogs = liveLogs.slice(liveLogs.length - LIVE_LOG_TAIL_LIMIT);
|
|
698
|
+
liveLogIds = liveLogIds.slice(liveLogIds.length - LIVE_LOG_TAIL_LIMIT);
|
|
680
699
|
liveLogContexts = liveLogContexts.slice(
|
|
681
700
|
liveLogContexts.length - LIVE_LOG_TAIL_LIMIT,
|
|
682
701
|
);
|
|
@@ -1321,13 +1340,14 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
1321
1340
|
line?: unknown;
|
|
1322
1341
|
source?: unknown;
|
|
1323
1342
|
context?: unknown;
|
|
1343
|
+
entryId?: unknown;
|
|
1324
1344
|
};
|
|
1325
1345
|
const rawLine = logEvent.line;
|
|
1326
1346
|
if (typeof rawLine !== 'string' || !rawLine) {
|
|
1327
1347
|
return;
|
|
1328
1348
|
}
|
|
1329
1349
|
if (shouldPublishDurableLogEvent(durableLogMode, logEvent)) {
|
|
1330
|
-
appendLiveLog(rawLine, logEvent.context);
|
|
1350
|
+
appendLiveLog(rawLine, logEvent.context, logEvent.entryId);
|
|
1331
1351
|
}
|
|
1332
1352
|
markMapStartedFromLog(rawLine);
|
|
1333
1353
|
markMapStartedFromPerfLog(rawLine);
|
|
@@ -1629,6 +1649,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
1629
1649
|
: terminalReplayPayloadForMode({
|
|
1630
1650
|
resultLogs: result.logs,
|
|
1631
1651
|
resultLogContexts: result.logContexts,
|
|
1652
|
+
resultLogIds: result.logIds,
|
|
1632
1653
|
durableLogMode,
|
|
1633
1654
|
liveLogs,
|
|
1634
1655
|
liveLogContexts,
|
|
@@ -1832,6 +1853,7 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
|
1832
1853
|
liveNodeProgress: finalUpdate.liveNodeProgress,
|
|
1833
1854
|
liveLogs,
|
|
1834
1855
|
liveLogContexts: finalUpdate.liveLogContexts,
|
|
1856
|
+
liveLogIds: finalUpdate.liveLogIds,
|
|
1835
1857
|
liveLogsChannelOffset: Math.max(0, liveLogTotalCount - liveLogs.length),
|
|
1836
1858
|
liveLogsProducerAttempt:
|
|
1837
1859
|
finalUpdate.liveLogProducerAttempt ?? input.attempt,
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_ADVISORY_PROVIDER_MAX_RPS,
|
|
12
12
|
DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS,
|
|
13
|
+
DEFAULT_UNKNOWN_PROVIDER_MIN_RPS,
|
|
13
14
|
PROVIDER_AIMD_COOLDOWN_CAP_MS,
|
|
14
15
|
type PacingRule,
|
|
15
16
|
} from '../governor/rate-state-backend';
|
|
@@ -431,7 +432,8 @@ function normalizeRules(
|
|
|
431
432
|
* per second; `capacity` is one window of burst. The adaptive `[min,max]` range
|
|
432
433
|
* is derived value-first so the same wire shape drives both declared and
|
|
433
434
|
* default-pacing buckets:
|
|
434
|
-
* - min_rps =
|
|
435
|
+
* - min_rps = 2 for the unknown shared-provider scope
|
|
436
|
+
* - min_rps = min(5/60, max) for declared rules
|
|
435
437
|
* (a lower explicit adaptive maximum remains authoritative)
|
|
436
438
|
* - max_rps = max(base, 100) (catalogued-rate recommendation)
|
|
437
439
|
* - max_rps = explicit adaptiveMaxRps when supplied
|
|
@@ -454,11 +456,11 @@ function seedForRule(rule: AcquirePacingRule): RateBucketSeed {
|
|
|
454
456
|
rule.adaptiveMaxRps != null && rule.adaptiveMaxRps > 0
|
|
455
457
|
? Math.max(rule.adaptiveMaxRps, base)
|
|
456
458
|
: Math.max(base, DEFAULT_ADVISORY_PROVIDER_MAX_RPS);
|
|
457
|
-
//
|
|
458
|
-
// scopes
|
|
459
|
+
// The unknown-provider seed is a guess, not a quota floor. Declared provider
|
|
460
|
+
// scopes keep the five-RPM floor, capped by an explicit adaptive maximum.
|
|
459
461
|
const minRps =
|
|
460
462
|
rule.ruleId === 'default:provider-scope'
|
|
461
|
-
?
|
|
463
|
+
? DEFAULT_UNKNOWN_PROVIDER_MIN_RPS
|
|
462
464
|
: Math.min(MIN_ADAPTIVE_RPS_FLOOR, maxRps);
|
|
463
465
|
return {
|
|
464
466
|
rule_id: rule.ruleId,
|
|
@@ -472,7 +474,7 @@ function seedForRule(rule: AcquirePacingRule): RateBucketSeed {
|
|
|
472
474
|
|
|
473
475
|
function minimumRateRpsForRules(rules: readonly AcquirePacingRule[]): number {
|
|
474
476
|
if (rules.some((rule) => rule.ruleId === 'default:provider-scope')) {
|
|
475
|
-
return
|
|
477
|
+
return DEFAULT_UNKNOWN_PROVIDER_MIN_RPS;
|
|
476
478
|
}
|
|
477
479
|
const explicitAdaptiveMaxima = rules.flatMap((rule) => {
|
|
478
480
|
if (rule.adaptiveMaxRps == null || rule.adaptiveMaxRps <= 0) return [];
|