deepline 0.3.113 → 0.3.115
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 +3 -1
- package/dist/bundling-sources/sdk/src/compat.ts +167 -11
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/skills-version.ts +27 -1
- package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +6 -2
- package/dist/bundling-sources/shared_libs/observability/dlq.ts +942 -0
- package/dist/bundling-sources/shared_libs/observability/queue-health.ts +361 -6
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +24 -24
- package/dist/bundling-sources/shared_libs/observability/scheduled-work-manual.ts +2 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-work.ts +29 -1
- package/dist/bundling-sources/shared_libs/play-runtime/admission-transport-policy.ts +91 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/direct-admission-deadline.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/internal-bootstrap-token.ts +325 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-admission-outcome.ts +134 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/contracts.ts +13 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/index.ts +4 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/postgres-adapter.ts +13 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-webhook-admission-probe.ts +32 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-webhook-identity.ts +28 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-cron-fire-task.ts +100 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +313 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +111 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +67 -8
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1734 -147
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +74 -13
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-queue-health.ts +245 -81
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/submit-admission.ts +79 -15
- package/dist/bundling-sources/shared_libs/play-runtime/webhook-receipt-retry.ts +21 -21
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +4 -1
- package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +64 -0
- package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +1 -1
- package/dist/bundling-sources/shared_libs/product-notifications/events.ts +20 -1
- package/dist/bundling-sources/shared_libs/security/receipt-admission-token.ts +224 -0
- package/dist/cli/index.js +574 -114
- package/dist/cli/index.mjs +592 -133
- package/dist/index.d.mts +54 -1
- package/dist/index.d.ts +54 -1
- package/dist/index.js +8 -3
- package/dist/index.mjs +8 -3
- package/dist/install-integrity.json +8 -0
- package/dist/plays/bundle-play-file.mjs +1 -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
|
@@ -34,11 +34,24 @@ const RECOVERY_FAILURE_RETRY_MAX_MS = 60_000;
|
|
|
34
34
|
export const NOTIFY_UNAVAILABLE_SAFETY_WAKE_MS = 1_000;
|
|
35
35
|
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* One projector can keep this many independent Run Ledger deliveries in
|
|
38
|
+
* flight after it has found durable work. Each delivery owns only short
|
|
39
|
+
* Postgres checkouts: claim, token-context read, and acknowledgement. It does
|
|
40
|
+
* not hold a scheduler connection across either gateway HTTP request. This is
|
|
41
|
+
* therefore a network/projection concurrency budget, deliberately independent
|
|
42
|
+
* of the projector's much smaller Postgres pool.
|
|
43
|
+
*
|
|
44
|
+
* A single successful probe starts a bounded wave; an idle projector still
|
|
45
|
+
* performs only one indexed head read per safety wake. `SKIP LOCKED` leases
|
|
46
|
+
* distinct runs, retaining strict causal ordering within every run.
|
|
40
47
|
*/
|
|
41
|
-
|
|
48
|
+
// `run.created` has one event per admitted Run, so a burst must project
|
|
49
|
+
// independently rather than wait for execution. Each Fly delivery first asks
|
|
50
|
+
// the launch-pinned Vercel deployment for a lease-fenced executor token,
|
|
51
|
+
// though. Keep that remote fan-out inside the Vercel control-plane budget:
|
|
52
|
+
// the Fly checkout is short, but 128 simultaneous token requests previously
|
|
53
|
+
// saturated the shared serverless scheduler pool before projection began.
|
|
54
|
+
export const RUN_LEDGER_OUTBOX_RECOVERY_MAX_IN_FLIGHT = 32;
|
|
42
55
|
|
|
43
56
|
type RecoveryTimer = ReturnType<typeof setTimeout>;
|
|
44
57
|
|
|
@@ -226,12 +239,33 @@ export function startRunLedgerOutboxRecovery(
|
|
|
226
239
|
timer.unref?.();
|
|
227
240
|
};
|
|
228
241
|
|
|
229
|
-
const drain = async (
|
|
242
|
+
const drain = async (reason: string): Promise<void> => {
|
|
230
243
|
while (requested && !closed) {
|
|
231
244
|
requested = false;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
245
|
+
// Probe first so an idle projector issues one small indexed read per
|
|
246
|
+
// wake rather than `parallelism` identical empty scans. Once the probe
|
|
247
|
+
// claims a row, the remaining slots can safely lease unrelated runs;
|
|
248
|
+
// each dispatcher releases its database client before gateway I/O.
|
|
249
|
+
let first: PostgresSchedulerOutboxProjectionResult;
|
|
250
|
+
try {
|
|
251
|
+
first = await deps.dispatchOnce();
|
|
252
|
+
} catch (error) {
|
|
253
|
+
scheduleFailureRetry('dispatch', error);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const settled = [
|
|
257
|
+
{
|
|
258
|
+
status: 'fulfilled' as const,
|
|
259
|
+
value: first,
|
|
260
|
+
},
|
|
261
|
+
...(first.attempted === 0
|
|
262
|
+
? []
|
|
263
|
+
: await Promise.allSettled(
|
|
264
|
+
Array.from({ length: parallelism - 1 }, () =>
|
|
265
|
+
deps.dispatchOnce(),
|
|
266
|
+
),
|
|
267
|
+
)),
|
|
268
|
+
];
|
|
235
269
|
const rejected = settled.find(
|
|
236
270
|
(result): result is PromiseRejectedResult =>
|
|
237
271
|
result.status === 'rejected',
|
|
@@ -251,6 +285,18 @@ export function startRunLedgerOutboxRecovery(
|
|
|
251
285
|
(total, result) => total + result.retrying,
|
|
252
286
|
0,
|
|
253
287
|
);
|
|
288
|
+
const attempted = results.reduce(
|
|
289
|
+
(total, result) => total + result.attempted,
|
|
290
|
+
0,
|
|
291
|
+
);
|
|
292
|
+
const projected = results.reduce(
|
|
293
|
+
(total, result) => total + result.projected,
|
|
294
|
+
0,
|
|
295
|
+
);
|
|
296
|
+
const blocked = results.reduce(
|
|
297
|
+
(total, result) => total + result.failed,
|
|
298
|
+
0,
|
|
299
|
+
);
|
|
254
300
|
|
|
255
301
|
if (retrying > 0) {
|
|
256
302
|
// The failed row owns its own durable retry clock. Do NOT stop this
|
|
@@ -263,11 +309,25 @@ export function startRunLedgerOutboxRecovery(
|
|
|
263
309
|
failureCount = 0;
|
|
264
310
|
}
|
|
265
311
|
|
|
312
|
+
if (attempted > 0) {
|
|
313
|
+
// This is the durable queue's high-signal throughput event. It makes
|
|
314
|
+
// a delayed public Run observable as a projector/outbox problem,
|
|
315
|
+
// without emitting one noisy log line per normal database checkout.
|
|
316
|
+
logger.info('[run-ledger-outbox.recovery_wave]', {
|
|
317
|
+
reason,
|
|
318
|
+
parallelism,
|
|
319
|
+
attempted,
|
|
320
|
+
projected,
|
|
321
|
+
retrying,
|
|
322
|
+
blocked,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
266
326
|
// Continue until no event was claimable. A transient failure schedules
|
|
267
327
|
// its own durable retry, so the next pass naturally skips that run while
|
|
268
328
|
// moving unrelated runs forward. A permanent failure is settled once and
|
|
269
329
|
// likewise cannot block another run's causal prefix.
|
|
270
|
-
if (
|
|
330
|
+
if (attempted > 0) {
|
|
271
331
|
requested = true;
|
|
272
332
|
continue;
|
|
273
333
|
}
|
|
@@ -324,10 +384,11 @@ export function startRunLedgerOutboxRecoveryController(input: {
|
|
|
324
384
|
{ tokenAuthority: 'gateway', limit: 8 },
|
|
325
385
|
schedulerOptions,
|
|
326
386
|
),
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
387
|
+
// `dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope` releases each
|
|
388
|
+
// short scheduler checkout before it calls the Vercel gateway. Keep the
|
|
389
|
+
// durable projection wave large enough for a webhook burst without
|
|
390
|
+
// expanding the process' Postgres connection budget.
|
|
391
|
+
parallelism: RUN_LEDGER_OUTBOX_RECOVERY_MAX_IN_FLIGHT,
|
|
331
392
|
readNextDueAt: () =>
|
|
332
393
|
withRuntimeSchedulerClient(input.poolOptions, (client) =>
|
|
333
394
|
readPostgresSchedulerOutboxNextDueAt(client, schedulerOptions),
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-queue-health.ts
CHANGED
|
@@ -9,6 +9,11 @@ import {
|
|
|
9
9
|
type QueueHealth,
|
|
10
10
|
type QueueObservation,
|
|
11
11
|
} from '../../observability/queue-health';
|
|
12
|
+
import {
|
|
13
|
+
dlqInventoryFromQueueObservation,
|
|
14
|
+
getDlqTransitionPaths,
|
|
15
|
+
type DlqInventory,
|
|
16
|
+
} from '../../observability/dlq';
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* Bounded, read-only health projection for the durable runtime queues.
|
|
@@ -59,13 +64,15 @@ export type RuntimeQueueFlow = {
|
|
|
59
64
|
|
|
60
65
|
export type RuntimeQueueHealthProjection = {
|
|
61
66
|
id: string;
|
|
62
|
-
kind: 'absurd' | 'run_ledger_outbox' | 'sandbox_cleanup';
|
|
67
|
+
kind: 'absurd' | 'run_ledger_outbox' | 'compute_billing' | 'sandbox_cleanup';
|
|
63
68
|
source: 'absurd' | 'runtime_postgres';
|
|
64
69
|
observedAt: string;
|
|
65
70
|
status: RuntimeQueueHealthStatus;
|
|
66
71
|
statusReason: string;
|
|
67
72
|
/** Shared scheduler-neutral contract consumed by transports and dashboards. */
|
|
68
73
|
observation: QueueObservation;
|
|
74
|
+
/** Shared DLQ contract consumed by operators and the admin CLI. */
|
|
75
|
+
dlq: DlqInventory;
|
|
69
76
|
health: QueueHealth;
|
|
70
77
|
depth: RuntimeQueueDepth;
|
|
71
78
|
oldestAgeMs: RuntimeQueueOldestAge;
|
|
@@ -86,6 +93,8 @@ export type RuntimeQueueHealthProjection = {
|
|
|
86
93
|
export type RuntimeQueueHealthSnapshot = {
|
|
87
94
|
observedAt: string;
|
|
88
95
|
staleAfterMs: number;
|
|
96
|
+
/** True when the control-plane lane inventory was capped before sampling. */
|
|
97
|
+
inventoryTruncated?: boolean;
|
|
89
98
|
queues: RuntimeQueueHealthProjection[];
|
|
90
99
|
summary: {
|
|
91
100
|
green: number;
|
|
@@ -96,6 +105,8 @@ export type RuntimeQueueHealthSnapshot = {
|
|
|
96
105
|
};
|
|
97
106
|
|
|
98
107
|
export type RuntimeQueueHealthOptions = {
|
|
108
|
+
/** Explicit deployment label carried into the shared DLQ inventory. */
|
|
109
|
+
environment?: string;
|
|
99
110
|
schema?: string;
|
|
100
111
|
now?: Date;
|
|
101
112
|
staleAfterMs?: number;
|
|
@@ -103,6 +114,8 @@ export type RuntimeQueueHealthOptions = {
|
|
|
103
114
|
failAgeMs?: number;
|
|
104
115
|
/** Restrict Absurd rows to these queue names. Registry values are validated. */
|
|
105
116
|
absurdQueueNames?: string[];
|
|
117
|
+
/** The caller could not enumerate every live lane within the bounded cap. */
|
|
118
|
+
absurdQueueInventoryTruncated?: boolean;
|
|
106
119
|
maxAbsurdQueues?: number;
|
|
107
120
|
};
|
|
108
121
|
|
|
@@ -134,6 +147,8 @@ type CountRow = {
|
|
|
134
147
|
in_flight_truncated?: boolean;
|
|
135
148
|
blocked_truncated?: boolean;
|
|
136
149
|
dead_lettered_truncated?: boolean;
|
|
150
|
+
/** False for durable worksets that expose blocked quarantine, not a DLQ. */
|
|
151
|
+
dead_lettered_available?: boolean;
|
|
137
152
|
flow_available?: boolean;
|
|
138
153
|
flow_unavailable_reason?: string | null;
|
|
139
154
|
};
|
|
@@ -164,7 +179,9 @@ function numberValue(value: string | number | null | undefined): number {
|
|
|
164
179
|
function ageValue(value: string | number | null | undefined): number | null {
|
|
165
180
|
if (value === null || value === undefined) return null;
|
|
166
181
|
const parsed = numberValue(value);
|
|
167
|
-
|
|
182
|
+
// Zero is a real age for freshly admitted work. Keep it distinct from
|
|
183
|
+
// NULL, which means that the bucket is empty and should render as N/A.
|
|
184
|
+
return Math.floor(parsed);
|
|
168
185
|
}
|
|
169
186
|
|
|
170
187
|
function optionsWithDefaults(
|
|
@@ -246,6 +263,7 @@ function projectionFromRow(input: {
|
|
|
246
263
|
source: RuntimeQueueHealthProjection['source'];
|
|
247
264
|
row: CountRow;
|
|
248
265
|
observedAt: Date;
|
|
266
|
+
environment?: string;
|
|
249
267
|
options: RuntimeQueueHealthThresholds;
|
|
250
268
|
}): RuntimeQueueHealthProjection {
|
|
251
269
|
const depth: RuntimeQueueDepth = {
|
|
@@ -262,6 +280,7 @@ function projectionFromRow(input: {
|
|
|
262
280
|
blocked: ageValue(input.row.oldest_blocked_ms),
|
|
263
281
|
deadLettered: ageValue(input.row.oldest_dead_lettered_ms),
|
|
264
282
|
};
|
|
283
|
+
const deadLetteredAvailable = input.row.dead_lettered_available !== false;
|
|
265
284
|
const lifecycle = {
|
|
266
285
|
started: numberValue(input.row.started),
|
|
267
286
|
completed: numberValue(input.row.completed),
|
|
@@ -299,7 +318,9 @@ function projectionFromRow(input: {
|
|
|
299
318
|
? 'Run admitted Play runtime tasks to terminal state'
|
|
300
319
|
: input.kind === 'run_ledger_outbox'
|
|
301
320
|
? 'Deliver causal Run Ledger events to control-plane projections'
|
|
302
|
-
:
|
|
321
|
+
: input.kind === 'compute_billing'
|
|
322
|
+
? 'Settle durable compute billing obligations after a runtime run ends'
|
|
323
|
+
: 'Complete durable sandbox deletion obligations',
|
|
303
324
|
unit: 'items',
|
|
304
325
|
kind: input.kind === 'absurd' ? 'fifo' : 'leased_workset',
|
|
305
326
|
ageSlo: {
|
|
@@ -328,20 +349,21 @@ function projectionFromRow(input: {
|
|
|
328
349
|
delayed: measureDepth(depth.delayed, input.row.delayed_truncated),
|
|
329
350
|
inFlight: measureDepth(depth.inFlight, input.row.in_flight_truncated),
|
|
330
351
|
blocked: measureDepth(depth.blocked, input.row.blocked_truncated),
|
|
331
|
-
deadLettered:
|
|
332
|
-
depth.deadLettered,
|
|
333
|
-
|
|
334
|
-
),
|
|
352
|
+
deadLettered: deadLetteredAvailable
|
|
353
|
+
? measureDepth(depth.deadLettered, input.row.dead_lettered_truncated)
|
|
354
|
+
: notApplicable<number>('This workset has blocked quarantine, not a DLQ.'),
|
|
335
355
|
},
|
|
336
356
|
oldestAgeMs: {
|
|
337
357
|
ready: measureAge(oldestAgeMs.ready, input.row.ready_truncated),
|
|
338
358
|
delayed: measureAge(oldestAgeMs.delayed, input.row.delayed_truncated),
|
|
339
359
|
inFlight: measureAge(oldestAgeMs.inFlight, input.row.in_flight_truncated),
|
|
340
360
|
blocked: measureAge(oldestAgeMs.blocked, input.row.blocked_truncated),
|
|
341
|
-
deadLettered:
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
361
|
+
deadLettered: deadLetteredAvailable
|
|
362
|
+
? measureAge(
|
|
363
|
+
oldestAgeMs.deadLettered,
|
|
364
|
+
input.row.dead_lettered_truncated,
|
|
365
|
+
)
|
|
366
|
+
: notApplicable<number>('This workset has blocked quarantine, not a DLQ.'),
|
|
345
367
|
},
|
|
346
368
|
flow: {
|
|
347
369
|
admitted: flowAvailable
|
|
@@ -369,6 +391,20 @@ function projectionFromRow(input: {
|
|
|
369
391
|
observation,
|
|
370
392
|
input.options.now.getTime(),
|
|
371
393
|
);
|
|
394
|
+
const dlq = dlqInventoryFromQueueObservation({
|
|
395
|
+
queueId: input.id,
|
|
396
|
+
environment:
|
|
397
|
+
input.environment?.trim() ||
|
|
398
|
+
process.env.DEEPLINE_ENV?.trim() ||
|
|
399
|
+
'unknown',
|
|
400
|
+
source: input.source,
|
|
401
|
+
observation,
|
|
402
|
+
paths: getDlqTransitionPaths({
|
|
403
|
+
queueId: input.id,
|
|
404
|
+
owner: 'runtime',
|
|
405
|
+
source: input.source,
|
|
406
|
+
}),
|
|
407
|
+
});
|
|
372
408
|
return {
|
|
373
409
|
id: input.id,
|
|
374
410
|
kind: input.kind,
|
|
@@ -377,6 +413,7 @@ function projectionFromRow(input: {
|
|
|
377
413
|
status: derived.status,
|
|
378
414
|
statusReason: derived.statusReason,
|
|
379
415
|
observation,
|
|
416
|
+
dlq,
|
|
380
417
|
health,
|
|
381
418
|
depth,
|
|
382
419
|
oldestAgeMs,
|
|
@@ -465,6 +502,7 @@ function absurdProjectionQuery(queueTable: string): string {
|
|
|
465
502
|
0::bigint AS completed,
|
|
466
503
|
0::bigint AS failed,
|
|
467
504
|
0::bigint AS retried,
|
|
505
|
+
true AS dead_lettered_available,
|
|
468
506
|
false AS flow_available,
|
|
469
507
|
'Absurd run history has no bounded exact rolling flow projection'::text AS flow_unavailable_reason
|
|
470
508
|
FROM sampled
|
|
@@ -474,114 +512,216 @@ function absurdProjectionQuery(queueTable: string): string {
|
|
|
474
512
|
function outboxProjectionQuery(schema: string): string {
|
|
475
513
|
const table = `${schema}."outbox"`;
|
|
476
514
|
return `
|
|
477
|
-
WITH
|
|
478
|
-
SELECT
|
|
479
|
-
CASE
|
|
480
|
-
WHEN lease_owner IS NULL AND coalesce(next_attempt_at, created_at) <= $1::timestamptz THEN 'ready'
|
|
481
|
-
WHEN lease_owner IS NULL THEN 'delayed'
|
|
482
|
-
WHEN lease_expires_at > $1::timestamptz THEN 'in_flight'
|
|
483
|
-
ELSE 'blocked'
|
|
484
|
-
END AS bucket,
|
|
485
|
-
CASE
|
|
486
|
-
WHEN lease_owner IS NULL AND coalesce(next_attempt_at, created_at) > $1::timestamptz
|
|
487
|
-
THEN coalesce(next_attempt_at, created_at)
|
|
488
|
-
WHEN lease_owner IS NULL THEN created_at
|
|
489
|
-
WHEN lease_expires_at > $1::timestamptz
|
|
490
|
-
THEN coalesce(delivery_batch_started_at, created_at)
|
|
491
|
-
ELSE coalesce(lease_expires_at, created_at)
|
|
492
|
-
END AS age_at
|
|
515
|
+
WITH ready AS (
|
|
516
|
+
SELECT coalesce(next_attempt_at, created_at) AS age_at
|
|
493
517
|
FROM ${table}
|
|
494
|
-
WHERE delivered_at IS NULL
|
|
518
|
+
WHERE delivered_at IS NULL
|
|
519
|
+
AND failed_at IS NULL
|
|
520
|
+
AND lease_owner IS NULL
|
|
521
|
+
AND coalesce(next_attempt_at, created_at) <= $1::timestamptz
|
|
495
522
|
ORDER BY coalesce(next_attempt_at, created_at), created_at, event_id
|
|
496
523
|
LIMIT $2::int
|
|
497
|
-
),
|
|
498
|
-
SELECT
|
|
524
|
+
), delayed AS (
|
|
525
|
+
SELECT coalesce(next_attempt_at, created_at) AS age_at
|
|
526
|
+
FROM ${table}
|
|
527
|
+
WHERE delivered_at IS NULL
|
|
528
|
+
AND failed_at IS NULL
|
|
529
|
+
AND lease_owner IS NULL
|
|
530
|
+
AND coalesce(next_attempt_at, created_at) > $1::timestamptz
|
|
531
|
+
ORDER BY coalesce(next_attempt_at, created_at), created_at, event_id
|
|
532
|
+
LIMIT $2::int
|
|
533
|
+
), in_flight AS (
|
|
534
|
+
SELECT coalesce(delivery_batch_started_at, created_at) AS age_at
|
|
535
|
+
FROM ${table}
|
|
536
|
+
WHERE delivered_at IS NULL
|
|
537
|
+
AND failed_at IS NULL
|
|
538
|
+
AND lease_owner IS NOT NULL
|
|
539
|
+
AND lease_expires_at > $1::timestamptz
|
|
540
|
+
ORDER BY lease_expires_at, created_at, event_id
|
|
541
|
+
LIMIT $2::int
|
|
542
|
+
), blocked AS (
|
|
543
|
+
SELECT coalesce(lease_expires_at, created_at) AS age_at
|
|
544
|
+
FROM ${table}
|
|
545
|
+
WHERE delivered_at IS NULL
|
|
546
|
+
AND failed_at IS NULL
|
|
547
|
+
AND lease_owner IS NOT NULL
|
|
548
|
+
AND (lease_expires_at IS NULL OR lease_expires_at <= $1::timestamptz)
|
|
549
|
+
ORDER BY coalesce(lease_expires_at, created_at), created_at, event_id
|
|
550
|
+
LIMIT $2::int
|
|
551
|
+
), dead_lettered AS (
|
|
552
|
+
SELECT coalesce(failed_at, created_at) AS age_at
|
|
499
553
|
FROM ${table}
|
|
500
554
|
WHERE delivered_at IS NULL AND failed_at IS NOT NULL
|
|
501
555
|
ORDER BY failed_at, event_id
|
|
502
556
|
LIMIT $2::int
|
|
503
|
-
), sampled AS (
|
|
504
|
-
SELECT * FROM pending_sample
|
|
505
|
-
UNION ALL
|
|
506
|
-
SELECT * FROM dead_sample
|
|
507
557
|
)
|
|
508
558
|
SELECT
|
|
509
|
-
least(count(*)
|
|
510
|
-
least(count(*)
|
|
511
|
-
least(count(*)
|
|
512
|
-
least(count(*)
|
|
513
|
-
least(count(*)
|
|
514
|
-
max(${intervalExpr('age_at')})
|
|
515
|
-
max(${intervalExpr('age_at')})
|
|
516
|
-
max(${intervalExpr('age_at')})
|
|
517
|
-
max(${intervalExpr('age_at')})
|
|
518
|
-
max(${intervalExpr('age_at')})
|
|
519
|
-
count(*)
|
|
520
|
-
count(*)
|
|
521
|
-
count(*)
|
|
522
|
-
count(*)
|
|
523
|
-
count(*)
|
|
559
|
+
least((SELECT count(*) FROM ready), $3) AS ready,
|
|
560
|
+
least((SELECT count(*) FROM delayed), $3) AS delayed,
|
|
561
|
+
least((SELECT count(*) FROM in_flight), $3) AS in_flight,
|
|
562
|
+
least((SELECT count(*) FROM blocked), $3) AS blocked,
|
|
563
|
+
least((SELECT count(*) FROM dead_lettered), $3) AS dead_lettered,
|
|
564
|
+
(SELECT max(${intervalExpr('age_at')}) FROM ready) AS oldest_ready_ms,
|
|
565
|
+
(SELECT max(${intervalExpr('age_at')}) FROM delayed) AS oldest_delayed_ms,
|
|
566
|
+
(SELECT max(${intervalExpr('age_at')}) FROM in_flight) AS oldest_in_flight_ms,
|
|
567
|
+
(SELECT max(${intervalExpr('age_at')}) FROM blocked) AS oldest_blocked_ms,
|
|
568
|
+
(SELECT max(${intervalExpr('age_at')}) FROM dead_lettered) AS oldest_dead_lettered_ms,
|
|
569
|
+
(SELECT count(*) FROM ready) > $3 AS ready_truncated,
|
|
570
|
+
(SELECT count(*) FROM delayed) > $3 AS delayed_truncated,
|
|
571
|
+
(SELECT count(*) FROM in_flight) > $3 AS in_flight_truncated,
|
|
572
|
+
(SELECT count(*) FROM blocked) > $3 AS blocked_truncated,
|
|
573
|
+
(SELECT count(*) FROM dead_lettered) > $3 AS dead_lettered_truncated,
|
|
524
574
|
0::bigint AS admitted,
|
|
525
575
|
0::bigint AS started,
|
|
526
576
|
0::bigint AS completed,
|
|
527
577
|
0::bigint AS failed,
|
|
528
578
|
0::bigint AS retried,
|
|
579
|
+
true AS dead_lettered_available,
|
|
529
580
|
false AS flow_available,
|
|
530
581
|
'Outbox health has no bounded exact rolling flow projection'::text AS flow_unavailable_reason
|
|
531
|
-
FROM sampled
|
|
532
582
|
`;
|
|
533
583
|
}
|
|
534
584
|
|
|
535
585
|
function cleanupProjectionQuery(schema: string): string {
|
|
536
586
|
const table = `${schema}."sandbox_cleanup_jobs_v2"`;
|
|
537
587
|
return `
|
|
538
|
-
WITH
|
|
588
|
+
WITH ready AS (
|
|
589
|
+
SELECT created_at AS age_at
|
|
590
|
+
FROM ${table}
|
|
591
|
+
WHERE status <> 'deleted'
|
|
592
|
+
AND blocked_at IS NULL
|
|
593
|
+
AND lease_owner IS NULL
|
|
594
|
+
AND status <> 'deleting'
|
|
595
|
+
AND available_at <= $1::timestamptz
|
|
596
|
+
ORDER BY available_at, created_at, provider, sandbox_id
|
|
597
|
+
LIMIT $2::int
|
|
598
|
+
), delayed AS (
|
|
599
|
+
SELECT available_at AS age_at
|
|
600
|
+
FROM ${table}
|
|
601
|
+
WHERE status <> 'deleted'
|
|
602
|
+
AND blocked_at IS NULL
|
|
603
|
+
AND lease_owner IS NULL
|
|
604
|
+
AND status <> 'deleting'
|
|
605
|
+
AND available_at > $1::timestamptz
|
|
606
|
+
ORDER BY available_at, created_at, provider, sandbox_id
|
|
607
|
+
LIMIT $2::int
|
|
608
|
+
), in_flight AS (
|
|
609
|
+
SELECT coalesce(updated_at, created_at) AS age_at
|
|
610
|
+
FROM ${table}
|
|
611
|
+
WHERE status <> 'deleted'
|
|
612
|
+
AND blocked_at IS NULL
|
|
613
|
+
AND lease_owner IS NOT NULL
|
|
614
|
+
AND lease_expires_at > $1::timestamptz
|
|
615
|
+
ORDER BY lease_expires_at, created_at, provider, sandbox_id
|
|
616
|
+
LIMIT $2::int
|
|
617
|
+
), blocked AS (
|
|
539
618
|
SELECT
|
|
540
|
-
CASE
|
|
541
|
-
WHEN blocked_at IS NOT NULL THEN 'blocked'
|
|
542
|
-
WHEN lease_owner IS NOT NULL AND lease_expires_at > $1::timestamptz THEN 'in_flight'
|
|
543
|
-
WHEN lease_owner IS NOT NULL OR status = 'deleting' THEN 'blocked'
|
|
544
|
-
WHEN available_at > $1::timestamptz THEN 'delayed'
|
|
545
|
-
ELSE 'ready'
|
|
546
|
-
END AS bucket,
|
|
547
619
|
CASE
|
|
548
620
|
WHEN blocked_at IS NOT NULL THEN blocked_at
|
|
549
|
-
|
|
550
|
-
THEN coalesce(updated_at, created_at)
|
|
551
|
-
WHEN lease_owner IS NOT NULL OR status = 'deleting'
|
|
552
|
-
THEN coalesce(lease_expires_at, created_at)
|
|
553
|
-
WHEN available_at > $1::timestamptz THEN available_at
|
|
554
|
-
ELSE created_at
|
|
621
|
+
ELSE coalesce(lease_expires_at, created_at)
|
|
555
622
|
END AS age_at
|
|
556
623
|
FROM ${table}
|
|
557
624
|
WHERE status <> 'deleted'
|
|
558
|
-
|
|
625
|
+
AND (
|
|
626
|
+
blocked_at IS NOT NULL
|
|
627
|
+
OR lease_owner IS NOT NULL AND (lease_expires_at IS NULL OR lease_expires_at <= $1::timestamptz)
|
|
628
|
+
OR lease_owner IS NULL AND status = 'deleting'
|
|
629
|
+
)
|
|
630
|
+
ORDER BY coalesce(blocked_at, lease_expires_at, created_at), created_at, provider, sandbox_id
|
|
559
631
|
LIMIT $2::int
|
|
560
632
|
)
|
|
561
633
|
SELECT
|
|
562
|
-
least(count(*)
|
|
563
|
-
least(count(*)
|
|
564
|
-
least(count(*)
|
|
565
|
-
least(count(*)
|
|
634
|
+
least((SELECT count(*) FROM ready), $3) AS ready,
|
|
635
|
+
least((SELECT count(*) FROM delayed), $3) AS delayed,
|
|
636
|
+
least((SELECT count(*) FROM in_flight), $3) AS in_flight,
|
|
637
|
+
least((SELECT count(*) FROM blocked), $3) AS blocked,
|
|
566
638
|
0::bigint AS dead_lettered,
|
|
567
|
-
max(${intervalExpr('age_at')})
|
|
568
|
-
max(${intervalExpr('age_at')})
|
|
569
|
-
max(${intervalExpr('age_at')})
|
|
570
|
-
max(${intervalExpr('age_at')})
|
|
639
|
+
(SELECT max(${intervalExpr('age_at')}) FROM ready) AS oldest_ready_ms,
|
|
640
|
+
(SELECT max(${intervalExpr('age_at')}) FROM delayed) AS oldest_delayed_ms,
|
|
641
|
+
(SELECT max(${intervalExpr('age_at')}) FROM in_flight) AS oldest_in_flight_ms,
|
|
642
|
+
(SELECT max(${intervalExpr('age_at')}) FROM blocked) AS oldest_blocked_ms,
|
|
571
643
|
NULL::bigint AS oldest_dead_lettered_ms,
|
|
572
|
-
count(*) > $3 AS ready_truncated,
|
|
573
|
-
count(*) > $3 AS delayed_truncated,
|
|
574
|
-
count(*) > $3 AS in_flight_truncated,
|
|
575
|
-
count(*) > $3 AS blocked_truncated,
|
|
644
|
+
(SELECT count(*) FROM ready) > $3 AS ready_truncated,
|
|
645
|
+
(SELECT count(*) FROM delayed) > $3 AS delayed_truncated,
|
|
646
|
+
(SELECT count(*) FROM in_flight) > $3 AS in_flight_truncated,
|
|
647
|
+
(SELECT count(*) FROM blocked) > $3 AS blocked_truncated,
|
|
576
648
|
false AS dead_lettered_truncated,
|
|
577
649
|
0::bigint AS admitted,
|
|
578
650
|
0::bigint AS started,
|
|
579
651
|
0::bigint AS completed,
|
|
580
652
|
0::bigint AS failed,
|
|
581
653
|
0::bigint AS retried,
|
|
654
|
+
false AS dead_lettered_available,
|
|
582
655
|
false AS flow_available,
|
|
583
656
|
'Sandbox cleanup health has no bounded exact rolling flow projection'::text AS flow_unavailable_reason
|
|
584
|
-
|
|
657
|
+
`;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function computeBillingProjectionQuery(schema: string): string {
|
|
661
|
+
const table = `${schema}."compute_billing_jobs_v2"`;
|
|
662
|
+
return `
|
|
663
|
+
WITH ready AS (
|
|
664
|
+
SELECT created_at AS age_at
|
|
665
|
+
FROM ${table}
|
|
666
|
+
WHERE status <> 'settled'
|
|
667
|
+
AND blocked_at IS NULL
|
|
668
|
+
AND lease_owner IS NULL
|
|
669
|
+
AND available_at <= $1::timestamptz
|
|
670
|
+
ORDER BY available_at, created_at, run_id
|
|
671
|
+
LIMIT $2::int
|
|
672
|
+
), delayed AS (
|
|
673
|
+
SELECT available_at AS age_at
|
|
674
|
+
FROM ${table}
|
|
675
|
+
WHERE status <> 'settled'
|
|
676
|
+
AND blocked_at IS NULL
|
|
677
|
+
AND lease_owner IS NULL
|
|
678
|
+
AND available_at > $1::timestamptz
|
|
679
|
+
ORDER BY available_at, created_at, run_id
|
|
680
|
+
LIMIT $2::int
|
|
681
|
+
), in_flight AS (
|
|
682
|
+
SELECT coalesce(updated_at, created_at) AS age_at
|
|
683
|
+
FROM ${table}
|
|
684
|
+
WHERE status <> 'settled'
|
|
685
|
+
AND blocked_at IS NULL
|
|
686
|
+
AND lease_owner IS NOT NULL
|
|
687
|
+
AND lease_expires_at > $1::timestamptz
|
|
688
|
+
ORDER BY lease_expires_at, created_at, run_id
|
|
689
|
+
LIMIT $2::int
|
|
690
|
+
), blocked AS (
|
|
691
|
+
SELECT coalesce(blocked_at, lease_expires_at, created_at) AS age_at
|
|
692
|
+
FROM ${table}
|
|
693
|
+
WHERE status <> 'settled'
|
|
694
|
+
AND (
|
|
695
|
+
blocked_at IS NOT NULL
|
|
696
|
+
OR lease_owner IS NOT NULL AND (lease_expires_at IS NULL OR lease_expires_at <= $1::timestamptz)
|
|
697
|
+
)
|
|
698
|
+
ORDER BY coalesce(blocked_at, lease_expires_at, created_at), created_at, run_id
|
|
699
|
+
LIMIT $2::int
|
|
700
|
+
)
|
|
701
|
+
SELECT
|
|
702
|
+
least((SELECT count(*) FROM ready), $3) AS ready,
|
|
703
|
+
least((SELECT count(*) FROM delayed), $3) AS delayed,
|
|
704
|
+
least((SELECT count(*) FROM in_flight), $3) AS in_flight,
|
|
705
|
+
least((SELECT count(*) FROM blocked), $3) AS blocked,
|
|
706
|
+
0::bigint AS dead_lettered,
|
|
707
|
+
(SELECT max(${intervalExpr('age_at')}) FROM ready) AS oldest_ready_ms,
|
|
708
|
+
(SELECT max(${intervalExpr('age_at')}) FROM delayed) AS oldest_delayed_ms,
|
|
709
|
+
(SELECT max(${intervalExpr('age_at')}) FROM in_flight) AS oldest_in_flight_ms,
|
|
710
|
+
(SELECT max(${intervalExpr('age_at')}) FROM blocked) AS oldest_blocked_ms,
|
|
711
|
+
NULL::bigint AS oldest_dead_lettered_ms,
|
|
712
|
+
(SELECT count(*) FROM ready) > $3 AS ready_truncated,
|
|
713
|
+
(SELECT count(*) FROM delayed) > $3 AS delayed_truncated,
|
|
714
|
+
(SELECT count(*) FROM in_flight) > $3 AS in_flight_truncated,
|
|
715
|
+
(SELECT count(*) FROM blocked) > $3 AS blocked_truncated,
|
|
716
|
+
false AS dead_lettered_truncated,
|
|
717
|
+
0::bigint AS admitted,
|
|
718
|
+
0::bigint AS started,
|
|
719
|
+
0::bigint AS completed,
|
|
720
|
+
0::bigint AS failed,
|
|
721
|
+
0::bigint AS retried,
|
|
722
|
+
false AS dead_lettered_available,
|
|
723
|
+
false AS flow_available,
|
|
724
|
+
'Compute billing health has no bounded exact rolling flow projection'::text AS flow_unavailable_reason
|
|
585
725
|
`;
|
|
586
726
|
}
|
|
587
727
|
|
|
@@ -649,9 +789,13 @@ export async function readRuntimeQueueHealthFromClient(
|
|
|
649
789
|
)
|
|
650
790
|
: await client.query<{ queue_name: string }>(
|
|
651
791
|
`SELECT queue_name FROM absurd.queues ORDER BY queue_name LIMIT $1::int`,
|
|
652
|
-
[maxQueues],
|
|
792
|
+
[maxQueues + 1],
|
|
653
793
|
);
|
|
654
|
-
const
|
|
794
|
+
const registryTruncated =
|
|
795
|
+
requestedNames === undefined && registry.rows.length > maxQueues;
|
|
796
|
+
const queueNames = registry.rows
|
|
797
|
+
.slice(0, maxQueues)
|
|
798
|
+
.map((row) => row.queue_name);
|
|
655
799
|
if (requestedNames) {
|
|
656
800
|
const found = new Set(queueNames);
|
|
657
801
|
const missing = requestedNames.filter((name) => !found.has(name));
|
|
@@ -674,16 +818,22 @@ export async function readRuntimeQueueHealthFromClient(
|
|
|
674
818
|
source: 'absurd',
|
|
675
819
|
row: rowFromResult(result),
|
|
676
820
|
observedAt: now,
|
|
821
|
+
environment: input.environment,
|
|
677
822
|
options,
|
|
678
823
|
}),
|
|
679
824
|
);
|
|
680
825
|
}
|
|
681
|
-
const [outbox, cleanup] = await Promise.all([
|
|
826
|
+
const [outbox, computeBilling, cleanup] = await Promise.all([
|
|
682
827
|
client.query<CountRow>(outboxProjectionQuery(schema), [
|
|
683
828
|
now,
|
|
684
829
|
sampleLimit + 1,
|
|
685
830
|
sampleLimit,
|
|
686
831
|
]),
|
|
832
|
+
client.query<CountRow>(computeBillingProjectionQuery(schema), [
|
|
833
|
+
now,
|
|
834
|
+
sampleLimit + 1,
|
|
835
|
+
sampleLimit,
|
|
836
|
+
]),
|
|
687
837
|
client.query<CountRow>(cleanupProjectionQuery(schema), [
|
|
688
838
|
now,
|
|
689
839
|
sampleLimit + 1,
|
|
@@ -697,6 +847,16 @@ export async function readRuntimeQueueHealthFromClient(
|
|
|
697
847
|
source: 'runtime_postgres',
|
|
698
848
|
row: rowFromResult(outbox),
|
|
699
849
|
observedAt: now,
|
|
850
|
+
environment: input.environment,
|
|
851
|
+
options,
|
|
852
|
+
}),
|
|
853
|
+
projectionFromRow({
|
|
854
|
+
id: 'runtime:compute-billing-jobs-v2',
|
|
855
|
+
kind: 'compute_billing',
|
|
856
|
+
source: 'runtime_postgres',
|
|
857
|
+
row: rowFromResult(computeBilling),
|
|
858
|
+
observedAt: now,
|
|
859
|
+
environment: input.environment,
|
|
700
860
|
options,
|
|
701
861
|
}),
|
|
702
862
|
projectionFromRow({
|
|
@@ -705,6 +865,7 @@ export async function readRuntimeQueueHealthFromClient(
|
|
|
705
865
|
source: 'runtime_postgres',
|
|
706
866
|
row: rowFromResult(cleanup),
|
|
707
867
|
observedAt: now,
|
|
868
|
+
environment: input.environment,
|
|
708
869
|
options,
|
|
709
870
|
}),
|
|
710
871
|
);
|
|
@@ -717,6 +878,9 @@ export async function readRuntimeQueueHealthFromClient(
|
|
|
717
878
|
return {
|
|
718
879
|
observedAt: now.toISOString(),
|
|
719
880
|
staleAfterMs: options.staleAfterMs,
|
|
881
|
+
...(input.absurdQueueInventoryTruncated || registryTruncated
|
|
882
|
+
? { inventoryTruncated: true }
|
|
883
|
+
: {}),
|
|
720
884
|
queues: projections,
|
|
721
885
|
summary,
|
|
722
886
|
};
|