bunqueue 2.8.60 → 2.9.0
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/README.md +108 -76
- package/dist/application/background/dlq.js +2 -0
- package/dist/application/contextFactory.js +5 -0
- package/dist/application/dlqManager.d.ts +10 -0
- package/dist/application/dlqManager.js +47 -9
- package/dist/application/dlqRetry.js +59 -38
- package/dist/application/eventsManager.d.ts +1 -1
- package/dist/application/eventsManager.js +30 -26
- package/dist/application/operations/jobManagement.d.ts +4 -0
- package/dist/application/operations/jobMoveOperations.js +9 -4
- package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
- package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
- package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
- package/dist/application/postgres-queue-manager/cloud.js +55 -0
- package/dist/application/postgres-queue-manager/config.d.ts +5 -0
- package/dist/application/postgres-queue-manager/config.js +1 -0
- package/dist/application/postgres-queue-manager/control.d.ts +26 -0
- package/dist/application/postgres-queue-manager/control.js +186 -0
- package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
- package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
- package/dist/application/postgres-queue-manager/delivery.d.ts +19 -0
- package/dist/application/postgres-queue-manager/delivery.js +80 -0
- package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
- package/dist/application/postgres-queue-manager/lease.js +88 -0
- package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
- package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
- package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
- package/dist/application/postgres-queue-manager/maintenance.js +100 -0
- package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
- package/dist/application/postgres-queue-manager/operationGate.js +96 -0
- package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
- package/dist/application/postgres-queue-manager/operations.js +194 -0
- package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
- package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
- package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
- package/dist/application/postgres-queue-manager/queries.js +231 -0
- package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
- package/dist/application/postgres-queue-manager/relationships.js +45 -0
- package/dist/application/postgres-queue-manager/services.d.ts +31 -0
- package/dist/application/postgres-queue-manager/services.js +149 -0
- package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
- package/dist/application/postgres-queue-manager/snapshot.js +236 -0
- package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
- package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
- package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
- package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
- package/dist/application/postgres-queue-manager/state.d.ts +47 -0
- package/dist/application/postgres-queue-manager/state.js +273 -0
- package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
- package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
- package/dist/application/postgresQueueManager.d.ts +2 -0
- package/dist/application/postgresQueueManager.js +1 -0
- package/dist/application/queue-manager/flow-failures.js +4 -0
- package/dist/application/queue-manager/limits.d.ts +2 -0
- package/dist/application/queue-manager/limits.js +4 -0
- package/dist/application/queue-manager/observability.js +15 -3
- package/dist/application/queue-manager/queries.d.ts +7 -0
- package/dist/application/queue-manager/queries.js +9 -0
- package/dist/application/queue-manager/services.d.ts +1 -1
- package/dist/application/queue-manager/services.js +4 -2
- package/dist/application/queue-manager/state.js +16 -1
- package/dist/cli/commands/server.js +1 -1
- package/dist/client/forwarder.d.ts +6 -4
- package/dist/client/forwarder.js +5 -3
- package/dist/client/queue/dlq.d.ts +2 -0
- package/dist/client/queue/dlq.js +14 -0
- package/dist/client/queue/dlqOps.d.ts +2 -0
- package/dist/client/queue/dlqOps.js +4 -0
- package/dist/client/queue/helpers.js +2 -0
- package/dist/client/queue/runtime/configuration.d.ts +4 -0
- package/dist/client/queue/runtime/configuration.js +8 -0
- package/dist/client/worker/runtime/execution.js +3 -8
- package/dist/client/worker/runtime/polling.d.ts +1 -0
- package/dist/client/worker/runtime/polling.js +9 -0
- package/dist/config/resolve.d.ts +14 -0
- package/dist/config/resolve.js +50 -5
- package/dist/config/types.d.ts +18 -0
- package/dist/domain/queue/dlqShard.d.ts +2 -0
- package/dist/domain/queue/dlqShard.js +16 -0
- package/dist/domain/queue/shard/dlq.d.ts +1 -0
- package/dist/domain/queue/shard/dlq.js +3 -0
- package/dist/domain/types/commands/dlq.d.ts +5 -0
- package/dist/domain/types/commands/union.d.ts +1 -1
- package/dist/domain/types/cron.d.ts +5 -0
- package/dist/domain/types/cron.js +14 -3
- package/dist/domain/types/dlq.d.ts +1 -1
- package/dist/domain/types/dlq.js +1 -2
- package/dist/domain/types/jobs/model.d.ts +1 -1
- package/dist/infrastructure/cloud/commandHandler.js +5 -3
- package/dist/infrastructure/cloud/commands/integrations.js +11 -11
- package/dist/infrastructure/cloud/commands/jobs.js +48 -40
- package/dist/infrastructure/cloud/commands/queues.js +61 -75
- package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
- package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
- package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
- package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
- package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
- package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
- package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
- package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
- package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
- package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
- package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
- package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
- package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
- package/dist/infrastructure/cloud/snapshot/config.js +43 -0
- package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
- package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
- package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
- package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
- package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
- package/dist/infrastructure/cloud/statsRefresh.js +2 -1
- package/dist/infrastructure/cloud/types/command.d.ts +2 -2
- package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/admission.js +193 -0
- package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
- package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
- package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
- package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
- package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
- package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
- package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/batchAdmission.js +110 -0
- package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
- package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
- package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
- package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/claimBatch.js +56 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.js +198 -0
- package/dist/infrastructure/persistence/postgres/claims.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/claims.js +137 -0
- package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
- package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
- package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
- package/dist/infrastructure/persistence/postgres/codec.js +82 -0
- package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
- package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
- package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
- package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
- package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/connection.js +15 -0
- package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
- package/dist/infrastructure/persistence/postgres/context.js +74 -0
- package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
- package/dist/infrastructure/persistence/postgres/control.js +105 -0
- package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/crons.js +205 -0
- package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
- package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
- package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
- package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
- package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
- package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
- package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
- package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
- package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
- package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
- package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
- package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
- package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
- package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +253 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +24 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +111 -0
- package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
- package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
- package/dist/infrastructure/persistence/postgres/events.js +249 -0
- package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
- package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
- package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
- package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
- package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
- package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
- package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/logs.js +70 -0
- package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
- package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
- package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
- package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
- package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
- package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/policies.js +18 -0
- package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
- package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
- package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
- package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
- package/dist/infrastructure/persistence/postgres/queries.js +152 -0
- package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/queueDestruction.js +154 -0
- package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
- package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
- package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
- package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
- package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
- package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
- package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
- package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
- package/dist/infrastructure/persistence/postgres/runtime.d.ts +46 -0
- package/dist/infrastructure/persistence/postgres/runtime.js +236 -0
- package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
- package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
- package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/schema.js +257 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.js +221 -0
- package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
- package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
- package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
- package/dist/infrastructure/persistence/postgres/store.d.ts +90 -0
- package/dist/infrastructure/persistence/postgres/store.js +136 -0
- package/dist/infrastructure/persistence/postgres/telemetry.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/telemetry.js +88 -0
- package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
- package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
- package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
- package/dist/infrastructure/persistence/postgres/types.d.ts +175 -0
- package/dist/infrastructure/persistence/postgres/types.js +1 -0
- package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
- package/dist/infrastructure/persistence/postgres/workers.js +126 -0
- package/dist/infrastructure/persistence/postgres.d.ts +2 -0
- package/dist/infrastructure/persistence/postgres.js +1 -0
- package/dist/infrastructure/persistence/sqlite/jobs.js +2 -0
- package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
- package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
- package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
- package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
- package/dist/infrastructure/scheduler/cron/validation.js +11 -0
- package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
- package/dist/infrastructure/scheduler/cronParser.js +144 -11
- package/dist/infrastructure/server/bootstrap.d.ts +2 -3
- package/dist/infrastructure/server/bootstrap.js +33 -59
- package/dist/infrastructure/server/errors.d.ts +4 -0
- package/dist/infrastructure/server/errors.js +54 -0
- package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
- package/dist/infrastructure/server/handler-routes/control.js +3 -1
- package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
- package/dist/infrastructure/server/handler.js +9 -11
- package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
- package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
- package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
- package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
- package/dist/infrastructure/server/handlers/core.js +32 -31
- package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
- package/dist/infrastructure/server/handlers/cron.js +60 -59
- package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
- package/dist/infrastructure/server/handlers/dashboard.js +21 -8
- package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
- package/dist/infrastructure/server/handlers/dlq.js +35 -9
- package/dist/infrastructure/server/handlers/flow.js +2 -1
- package/dist/infrastructure/server/handlers/management.d.ts +5 -5
- package/dist/infrastructure/server/handlers/management.js +36 -9
- package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
- package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
- package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
- package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
- package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
- package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
- package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
- package/dist/infrastructure/server/handlers/pushBatchValidation.js +52 -7
- package/dist/infrastructure/server/handlers/query.d.ts +1 -1
- package/dist/infrastructure/server/handlers/query.js +5 -4
- package/dist/infrastructure/server/http.js +2 -2
- package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
- package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
- package/dist/infrastructure/server/httpEndpoints.js +18 -7
- package/dist/infrastructure/server/httpRouteQueues.js +11 -1
- package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
- package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
- package/dist/infrastructure/server/sseHandler.js +8 -3
- package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
- package/dist/infrastructure/server/storageAdapter.js +61 -0
- package/dist/infrastructure/server/storageManager.d.ts +12 -0
- package/dist/infrastructure/server/storageManager.js +102 -0
- package/dist/infrastructure/server/tcp.js +2 -3
- package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
- package/dist/infrastructure/server/ws/snapshots.js +19 -4
- package/dist/infrastructure/server/wsHandler.js +10 -5
- package/dist/main.js +1 -1
- package/dist/mcp/backend/embedded/services.js +2 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +2 -2
- package/dist/shared/storageHealth.d.ts +9 -0
- package/dist/shared/storageHealth.js +12 -0
- package/package.json +74 -65
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
|
|
2
|
+
import { decodePostgresJob, encodePostgresValue } from './codec';
|
|
3
|
+
import { databaseNow, recordPostgresEvent } from './context';
|
|
4
|
+
import { updatedPostgresJobData } from './jobData';
|
|
5
|
+
import { updatePostgresRepeatSuccessorData } from './repeatMutations';
|
|
6
|
+
async function lockJob(tx, ctx, id) {
|
|
7
|
+
const rows = await tx `
|
|
8
|
+
SELECT * FROM bunqueue_jobs
|
|
9
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
10
|
+
FOR UPDATE
|
|
11
|
+
`;
|
|
12
|
+
return rows[0] ?? null;
|
|
13
|
+
}
|
|
14
|
+
function pendingState(job, now) {
|
|
15
|
+
if (job.dependsOn.length > 0)
|
|
16
|
+
return 'waiting-children';
|
|
17
|
+
if (job.runAt > now)
|
|
18
|
+
return 'delayed';
|
|
19
|
+
return job.priority > 0 ? 'prioritized' : 'waiting';
|
|
20
|
+
}
|
|
21
|
+
async function saveJob(tx, ctx, input) {
|
|
22
|
+
const { job, state, eventType, now } = input;
|
|
23
|
+
const rows = await tx `
|
|
24
|
+
UPDATE bunqueue_jobs
|
|
25
|
+
SET payload = ${encodePostgresValue(job)}, state = ${state}, priority = ${job.priority},
|
|
26
|
+
lifo = ${job.lifo}, run_at = ${job.runAt}, started_at = ${job.startedAt},
|
|
27
|
+
completed_at = ${job.completedAt}, attempts = ${job.attempts},
|
|
28
|
+
lease_owner = CASE WHEN ${state} = 'active' THEN lease_owner ELSE NULL END,
|
|
29
|
+
lease_broker_id = CASE WHEN ${state} = 'active' THEN lease_broker_id ELSE NULL END,
|
|
30
|
+
lease_broker_session_id = CASE
|
|
31
|
+
WHEN ${state} = 'active' THEN lease_broker_session_id ELSE NULL
|
|
32
|
+
END,
|
|
33
|
+
lease_token = CASE WHEN ${state} = 'active' THEN lease_token ELSE NULL END,
|
|
34
|
+
lease_until = CASE WHEN ${state} = 'active' THEN lease_until ELSE NULL END,
|
|
35
|
+
version = version + 1
|
|
36
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(job.id)}
|
|
37
|
+
RETURNING *
|
|
38
|
+
`;
|
|
39
|
+
await recordPostgresEvent(tx, ctx, {
|
|
40
|
+
queue: job.queue,
|
|
41
|
+
type: eventType,
|
|
42
|
+
jobId: job.id,
|
|
43
|
+
occurredAt: now,
|
|
44
|
+
job,
|
|
45
|
+
state,
|
|
46
|
+
});
|
|
47
|
+
return decodePostgresJob(rows[0]);
|
|
48
|
+
}
|
|
49
|
+
export async function updatePostgresJobData(ctx, id, data) {
|
|
50
|
+
return await ctx.sql.begin(async (tx) => {
|
|
51
|
+
const row = await lockJob(tx, ctx, id);
|
|
52
|
+
if (!row || row.state === 'completed' || row.state === 'failed') {
|
|
53
|
+
return await updatePostgresRepeatSuccessorData(tx, ctx, id, data);
|
|
54
|
+
}
|
|
55
|
+
const stored = decodePostgresJob(row);
|
|
56
|
+
const job = { ...stored.job, data: updatedPostgresJobData(stored.job, data) };
|
|
57
|
+
const now = await databaseNow(tx);
|
|
58
|
+
await saveJob(tx, ctx, {
|
|
59
|
+
job,
|
|
60
|
+
state: stored.state,
|
|
61
|
+
eventType: 'updated',
|
|
62
|
+
now,
|
|
63
|
+
});
|
|
64
|
+
return true;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export async function updatePostgresProgress(ctx, id, progress, message) {
|
|
68
|
+
return await ctx.sql.begin(async (tx) => {
|
|
69
|
+
const row = await lockJob(tx, ctx, id);
|
|
70
|
+
if (row?.state !== 'active')
|
|
71
|
+
return false;
|
|
72
|
+
const stored = decodePostgresJob(row);
|
|
73
|
+
const now = await databaseNow(tx);
|
|
74
|
+
stored.job.progress = Math.max(0, Math.min(100, progress));
|
|
75
|
+
stored.job.progressMessage = message === undefined ? stored.job.progressMessage : message;
|
|
76
|
+
stored.job.lastHeartbeat = now;
|
|
77
|
+
await saveJob(tx, ctx, {
|
|
78
|
+
job: stored.job,
|
|
79
|
+
state: stored.state,
|
|
80
|
+
eventType: 'progress',
|
|
81
|
+
now,
|
|
82
|
+
});
|
|
83
|
+
return true;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export async function changePostgresPriority(ctx, id, priority, lifo) {
|
|
87
|
+
return await ctx.sql.begin(async (tx) => {
|
|
88
|
+
const row = await lockJob(tx, ctx, id);
|
|
89
|
+
if (!row || !['waiting', 'prioritized', 'delayed'].includes(row.state))
|
|
90
|
+
return false;
|
|
91
|
+
const stored = decodePostgresJob(row);
|
|
92
|
+
const job = { ...stored.job, priority, lifo: lifo ?? stored.job.lifo };
|
|
93
|
+
const now = await databaseNow(tx);
|
|
94
|
+
const state = pendingState(job, now);
|
|
95
|
+
await saveJob(tx, ctx, { job, state, eventType: 'priority', now });
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export async function changePostgresDelay(ctx, id, runAt, token) {
|
|
100
|
+
return await ctx.sql.begin(async (tx) => {
|
|
101
|
+
const row = await lockJob(tx, ctx, id);
|
|
102
|
+
if (!row || !['waiting', 'prioritized', 'delayed', 'active'].includes(row.state))
|
|
103
|
+
return false;
|
|
104
|
+
const now = await databaseNow(tx);
|
|
105
|
+
if (row.state === 'active' &&
|
|
106
|
+
(row.lease_token !== token || Number(row.lease_until ?? 0) <= now)) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const stored = decodePostgresJob(row);
|
|
110
|
+
stored.job.runAt = runAt;
|
|
111
|
+
stored.job.startedAt = null;
|
|
112
|
+
const state = pendingState(stored.job, now);
|
|
113
|
+
if (stored.job.timeline.length < MAX_TIMELINE_ENTRIES) {
|
|
114
|
+
stored.job.timeline.push({ state, timestamp: now });
|
|
115
|
+
}
|
|
116
|
+
await saveJob(tx, ctx, { job: stored.job, state, eventType: 'delayed', now });
|
|
117
|
+
return true;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
export async function promotePostgresJob(ctx, id) {
|
|
121
|
+
return await changePostgresDelay(ctx, id, await databaseNow(ctx.sql));
|
|
122
|
+
}
|
|
123
|
+
export async function movePostgresJobToWaitingChildren(ctx, id, token) {
|
|
124
|
+
return await ctx.sql.begin(async (tx) => {
|
|
125
|
+
const row = await lockJob(tx, ctx, id);
|
|
126
|
+
if (!row || !['waiting', 'prioritized', 'delayed', 'active'].includes(row.state))
|
|
127
|
+
return false;
|
|
128
|
+
const now = await databaseNow(tx);
|
|
129
|
+
if (row.state === 'active' &&
|
|
130
|
+
(row.lease_token !== token || Number(row.lease_until ?? 0) <= now)) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
const job = decodePostgresJob(row).job;
|
|
134
|
+
job.startedAt = null;
|
|
135
|
+
if (job.timeline.length < MAX_TIMELINE_ENTRIES) {
|
|
136
|
+
job.timeline.push({ state: 'waiting-children', timestamp: now });
|
|
137
|
+
}
|
|
138
|
+
await saveJob(tx, ctx, {
|
|
139
|
+
job,
|
|
140
|
+
state: 'waiting-children',
|
|
141
|
+
eventType: 'waiting-children',
|
|
142
|
+
now,
|
|
143
|
+
});
|
|
144
|
+
return true;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
export async function clearPostgresJobUniqueKey(ctx, id) {
|
|
148
|
+
return await ctx.sql.begin(async (tx) => {
|
|
149
|
+
const row = await lockJob(tx, ctx, id);
|
|
150
|
+
if (!row?.unique_key)
|
|
151
|
+
return false;
|
|
152
|
+
const job = decodePostgresJob(row).job;
|
|
153
|
+
const updated = { ...job, uniqueKey: null };
|
|
154
|
+
const now = await databaseNow(tx);
|
|
155
|
+
await tx `
|
|
156
|
+
UPDATE bunqueue_jobs
|
|
157
|
+
SET payload = ${encodePostgresValue(updated)}, unique_key = NULL,
|
|
158
|
+
unique_expires_at = NULL, version = version + 1
|
|
159
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
160
|
+
`;
|
|
161
|
+
await recordPostgresEvent(tx, ctx, {
|
|
162
|
+
queue: job.queue,
|
|
163
|
+
type: 'updated',
|
|
164
|
+
jobId: id,
|
|
165
|
+
occurredAt: now,
|
|
166
|
+
job: updated,
|
|
167
|
+
state: row.state,
|
|
168
|
+
});
|
|
169
|
+
return true;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type PostgresContext } from './context';
|
|
2
|
+
import type { PostgresFailureInput, PostgresTransitionResult } from './types';
|
|
3
|
+
export { completePostgresJob } from './completionOutcome';
|
|
4
|
+
export declare function failPostgresJob(ctx: PostgresContext, input: PostgresFailureInput): Promise<PostgresTransitionResult>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { calculateBackoff, canRetry, MAX_TIMELINE_ENTRIES, normalizeStacktrace, } from '../../../domain/types/job';
|
|
2
|
+
import { createDlqEntry, getDlqRetryState, recordJobFailureAttempt, } from '../../../domain/types/dlq';
|
|
3
|
+
import { decodePostgresJob, encodePostgresValue } from './codec';
|
|
4
|
+
import { databaseNow, recordPostgresEvent, runPostgresPostCommitMaintenance, } from './context';
|
|
5
|
+
import { applyPostgresFlowFailure, clearPostgresParentFlowFailures, lockPostgresJobFlowParent, } from './flowFailures';
|
|
6
|
+
import { enforcePostgresDlqLimit } from './dlqLifecycle';
|
|
7
|
+
import { getPostgresQueuePolicies } from './policies';
|
|
8
|
+
import { partitionPostgresDestructionCandidates } from './dependencyDestruction';
|
|
9
|
+
import { runPostgresTransactionWithRetry } from './transactionRetry';
|
|
10
|
+
export { completePostgresJob } from './completionOutcome';
|
|
11
|
+
async function lockActiveJob(tx, ctx, id) {
|
|
12
|
+
const rows = await tx `
|
|
13
|
+
SELECT * FROM bunqueue_jobs
|
|
14
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
15
|
+
FOR UPDATE
|
|
16
|
+
`;
|
|
17
|
+
return rows[0] ?? null;
|
|
18
|
+
}
|
|
19
|
+
function notApplied(row) {
|
|
20
|
+
return {
|
|
21
|
+
applied: false,
|
|
22
|
+
alreadyFinalized: row?.state === 'completed' || row?.state === 'failed',
|
|
23
|
+
job: row ? decodePostgresJob(row).job : null,
|
|
24
|
+
state: row?.state ?? null,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function assertToken(row, token, now) {
|
|
28
|
+
return row.state === 'active' && row.lease_token === token && Number(row.lease_until ?? 0) > now;
|
|
29
|
+
}
|
|
30
|
+
async function deleteLiveJob(tx, ctx, id) {
|
|
31
|
+
await tx `
|
|
32
|
+
DELETE FROM bunqueue_job_logs
|
|
33
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${String(id)}
|
|
34
|
+
`;
|
|
35
|
+
await tx `
|
|
36
|
+
DELETE FROM bunqueue_dependencies
|
|
37
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${String(id)}
|
|
38
|
+
`;
|
|
39
|
+
await tx `
|
|
40
|
+
DELETE FROM bunqueue_jobs
|
|
41
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
function failureReason(value, terminal) {
|
|
45
|
+
switch (value) {
|
|
46
|
+
case "explicit_fail" /* FailureReason.ExplicitFail */:
|
|
47
|
+
case "max_attempts_exceeded" /* FailureReason.MaxAttemptsExceeded */:
|
|
48
|
+
case "timeout" /* FailureReason.Timeout */:
|
|
49
|
+
case "stalled" /* FailureReason.Stalled */:
|
|
50
|
+
case "ttl_expired" /* FailureReason.TtlExpired */:
|
|
51
|
+
case "worker_lost" /* FailureReason.WorkerLost */:
|
|
52
|
+
case "unknown" /* FailureReason.Unknown */:
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
return terminal ? "max_attempts_exceeded" /* FailureReason.MaxAttemptsExceeded */ : "explicit_fail" /* FailureReason.ExplicitFail */;
|
|
56
|
+
}
|
|
57
|
+
async function persistRetry(tx, ctx, job, now) {
|
|
58
|
+
job.runAt = now + calculateBackoff(job);
|
|
59
|
+
job.startedAt = null;
|
|
60
|
+
const state = job.runAt > now ? 'delayed' : job.priority > 0 ? 'prioritized' : 'waiting';
|
|
61
|
+
if (job.timeline.length < MAX_TIMELINE_ENTRIES) {
|
|
62
|
+
job.timeline.push({ state, timestamp: now, attempt: job.attempts + 1 });
|
|
63
|
+
}
|
|
64
|
+
await tx `
|
|
65
|
+
UPDATE bunqueue_jobs
|
|
66
|
+
SET payload = ${encodePostgresValue(job)}, state = ${state}, attempts = ${job.attempts},
|
|
67
|
+
run_at = ${job.runAt}, started_at = NULL, lease_owner = NULL, lease_token = NULL,
|
|
68
|
+
lease_broker_id = NULL, lease_broker_session_id = NULL,
|
|
69
|
+
lease_until = NULL, error = NULL, failure_reason = NULL,
|
|
70
|
+
dlq_entry = NULL,
|
|
71
|
+
dlq_retry_state = ${encodePostgresValue(getDlqRetryState(job))},
|
|
72
|
+
version = version + 1
|
|
73
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(job.id)}
|
|
74
|
+
`;
|
|
75
|
+
await recordPostgresEvent(tx, ctx, {
|
|
76
|
+
queue: job.queue,
|
|
77
|
+
type: 'retried',
|
|
78
|
+
jobId: job.id,
|
|
79
|
+
occurredAt: now,
|
|
80
|
+
job,
|
|
81
|
+
state,
|
|
82
|
+
dlqEntry: null,
|
|
83
|
+
dlqRetryState: getDlqRetryState(job),
|
|
84
|
+
});
|
|
85
|
+
return state;
|
|
86
|
+
}
|
|
87
|
+
async function persistTerminalFailure(tx, ctx, input) {
|
|
88
|
+
const { job, reason, error, now, remove } = input;
|
|
89
|
+
const { dlq } = await getPostgresQueuePolicies(tx, ctx, job.queue);
|
|
90
|
+
const entry = createDlqEntry(job, reason, error ?? null, dlq, now);
|
|
91
|
+
const { protectedIds } = remove
|
|
92
|
+
? await partitionPostgresDestructionCandidates(tx, ctx, [job.id], [job.id])
|
|
93
|
+
: { protectedIds: [] };
|
|
94
|
+
const removed = remove && protectedIds.length === 0;
|
|
95
|
+
if (removed) {
|
|
96
|
+
await deleteLiveJob(tx, ctx, job.id);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
await tx `
|
|
100
|
+
UPDATE bunqueue_jobs
|
|
101
|
+
SET payload = ${encodePostgresValue(job)}, state = 'failed', attempts = ${job.attempts},
|
|
102
|
+
completed_at = ${now}, lease_owner = NULL, lease_token = NULL, lease_until = NULL,
|
|
103
|
+
lease_broker_id = NULL, lease_broker_session_id = NULL,
|
|
104
|
+
error = ${error ?? null}, failure_reason = ${reason},
|
|
105
|
+
dlq_entry = ${encodePostgresValue(entry)}, dlq_retry_state = NULL,
|
|
106
|
+
version = version + 1
|
|
107
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(job.id)}
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
dlqEntry: removed ? null : entry,
|
|
112
|
+
maxEntries: removed ? null : dlq.maxEntries,
|
|
113
|
+
removed,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export async function failPostgresJob(ctx, input) {
|
|
117
|
+
const result = await runPostgresTransactionWithRetry(ctx, async (tx) => {
|
|
118
|
+
await lockPostgresJobFlowParent(tx, ctx, input.id);
|
|
119
|
+
const row = await lockActiveJob(tx, ctx, input.id);
|
|
120
|
+
const now = await databaseNow(tx);
|
|
121
|
+
if (!row || !assertToken(row, input.token, now)) {
|
|
122
|
+
return { transition: notApplied(row), dlqLimits: new Map() };
|
|
123
|
+
}
|
|
124
|
+
const job = decodePostgresJob(row).job;
|
|
125
|
+
job.attempts++;
|
|
126
|
+
job.stacktrace = normalizeStacktrace(input.stack, job.stackTraceLimit);
|
|
127
|
+
const willRetry = !input.unrecoverable && canRetry(job);
|
|
128
|
+
const reason = failureReason(input.failureReason, !willRetry);
|
|
129
|
+
let nextState = 'failed';
|
|
130
|
+
let dlqEntry = null;
|
|
131
|
+
let dlqMaxEntries = null;
|
|
132
|
+
let removed = false;
|
|
133
|
+
if (job.timeline.length < MAX_TIMELINE_ENTRIES) {
|
|
134
|
+
job.timeline.push({
|
|
135
|
+
state: 'failed',
|
|
136
|
+
timestamp: now,
|
|
137
|
+
error: input.error,
|
|
138
|
+
attempt: job.attempts,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (willRetry) {
|
|
142
|
+
recordJobFailureAttempt(job, reason, input.error ?? null, now);
|
|
143
|
+
nextState = await persistRetry(tx, ctx, job, now);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
job.completedAt = now;
|
|
147
|
+
const persisted = await persistTerminalFailure(tx, ctx, {
|
|
148
|
+
job,
|
|
149
|
+
reason,
|
|
150
|
+
error: input.error,
|
|
151
|
+
now,
|
|
152
|
+
remove: job.removeOnFail || input.removeOnFail === true,
|
|
153
|
+
});
|
|
154
|
+
dlqEntry = persisted.dlqEntry;
|
|
155
|
+
dlqMaxEntries = persisted.maxEntries;
|
|
156
|
+
removed = persisted.removed;
|
|
157
|
+
}
|
|
158
|
+
await recordPostgresEvent(tx, ctx, {
|
|
159
|
+
queue: job.queue,
|
|
160
|
+
type: 'failed',
|
|
161
|
+
jobId: job.id,
|
|
162
|
+
occurredAt: now,
|
|
163
|
+
job,
|
|
164
|
+
state: nextState,
|
|
165
|
+
error: input.error,
|
|
166
|
+
removed,
|
|
167
|
+
dlqEntry,
|
|
168
|
+
dlqRetryState: willRetry ? getDlqRetryState(job) : null,
|
|
169
|
+
});
|
|
170
|
+
const dlqLimits = new Map();
|
|
171
|
+
if (!willRetry) {
|
|
172
|
+
await clearPostgresParentFlowFailures(tx, ctx, job.id);
|
|
173
|
+
const request = await applyPostgresFlowFailure(tx, ctx, job, input.error, now);
|
|
174
|
+
if (request)
|
|
175
|
+
dlqLimits.set(request.queue, request.maxEntries);
|
|
176
|
+
}
|
|
177
|
+
if (dlqMaxEntries !== null) {
|
|
178
|
+
dlqLimits.set(job.queue, dlqMaxEntries);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
transition: {
|
|
182
|
+
applied: true,
|
|
183
|
+
alreadyFinalized: false,
|
|
184
|
+
job,
|
|
185
|
+
state: nextState,
|
|
186
|
+
},
|
|
187
|
+
dlqLimits,
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
for (const [queue, maxEntries] of [...result.dlqLimits].sort(([left], [right]) => left.localeCompare(right))) {
|
|
191
|
+
await runPostgresPostCommitMaintenance(ctx, `dlq-limit:${queue}`, () => enforcePostgresDlqLimit(ctx, queue, maxEntries));
|
|
192
|
+
}
|
|
193
|
+
return result.transition;
|
|
194
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SQL, TransactionSQL } from 'bun';
|
|
2
|
+
import { type DlqConfig } from '../../../domain/types/dlq';
|
|
3
|
+
import { type StallConfig } from '../../../domain/types/stall';
|
|
4
|
+
import type { PostgresContext } from './context';
|
|
5
|
+
export interface PostgresQueuePolicies {
|
|
6
|
+
readonly stall: StallConfig;
|
|
7
|
+
readonly dlq: DlqConfig;
|
|
8
|
+
}
|
|
9
|
+
export declare function decodePostgresDlqConfig(queue: string, value: Uint8Array | null): DlqConfig;
|
|
10
|
+
/** Read effective queue policies inside an existing transaction when supplied. */
|
|
11
|
+
export declare function getPostgresQueuePolicies(sql: SQL | TransactionSQL, ctx: PostgresContext, queue: string): Promise<PostgresQueuePolicies>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DEFAULT_DLQ_CONFIG } from '../../../domain/types/dlq';
|
|
2
|
+
import { DEFAULT_STALL_CONFIG } from '../../../domain/types/stall';
|
|
3
|
+
import { decodePostgresValue } from './codec';
|
|
4
|
+
export function decodePostgresDlqConfig(queue, value) {
|
|
5
|
+
return decodePostgresValue(value, DEFAULT_DLQ_CONFIG, `dlqConfig:${queue}`);
|
|
6
|
+
}
|
|
7
|
+
/** Read effective queue policies inside an existing transaction when supplied. */
|
|
8
|
+
export async function getPostgresQueuePolicies(sql, ctx, queue) {
|
|
9
|
+
const rows = await sql `
|
|
10
|
+
SELECT stall_config, dlq_config
|
|
11
|
+
FROM bunqueue_queue_state
|
|
12
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
13
|
+
`;
|
|
14
|
+
return {
|
|
15
|
+
stall: decodePostgresValue(rows[0]?.stall_config ?? null, DEFAULT_STALL_CONFIG, `stallConfig:${queue}`),
|
|
16
|
+
dlq: decodePostgresDlqConfig(queue, rows[0]?.dlq_config ?? null),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type MaintenanceReporter = (subsystem: string, error: unknown) => void;
|
|
2
|
+
/** Coalesce, serialize, and retry idempotent work after a committed transition. */
|
|
3
|
+
export declare class PostgresPostCommitMaintenance {
|
|
4
|
+
private readonly report;
|
|
5
|
+
private readonly retryDelayMs;
|
|
6
|
+
private readonly flights;
|
|
7
|
+
private timer;
|
|
8
|
+
private closed;
|
|
9
|
+
constructor(report: MaintenanceReporter, retryDelayMs: number);
|
|
10
|
+
run(subsystem: string, operation: () => Promise<unknown>): Promise<void>;
|
|
11
|
+
close(): void;
|
|
12
|
+
drain(): Promise<void>;
|
|
13
|
+
private startFlight;
|
|
14
|
+
private runLoop;
|
|
15
|
+
private scheduleRetry;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** Coalesce, serialize, and retry idempotent work after a committed transition. */
|
|
2
|
+
export class PostgresPostCommitMaintenance {
|
|
3
|
+
report;
|
|
4
|
+
retryDelayMs;
|
|
5
|
+
flights = new Map();
|
|
6
|
+
timer = null;
|
|
7
|
+
closed = false;
|
|
8
|
+
constructor(report, retryDelayMs) {
|
|
9
|
+
this.report = report;
|
|
10
|
+
this.retryDelayMs = retryDelayMs;
|
|
11
|
+
}
|
|
12
|
+
run(subsystem, operation) {
|
|
13
|
+
if (this.closed)
|
|
14
|
+
return Promise.resolve();
|
|
15
|
+
let resolve;
|
|
16
|
+
const result = new Promise((accept) => {
|
|
17
|
+
resolve = accept;
|
|
18
|
+
});
|
|
19
|
+
const existing = this.flights.get(subsystem);
|
|
20
|
+
if (existing) {
|
|
21
|
+
if (existing.pending) {
|
|
22
|
+
existing.pending.operation = operation;
|
|
23
|
+
existing.pending.retry = false;
|
|
24
|
+
existing.pending.waiters.push(resolve);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
existing.pending = { operation, waiters: [resolve], retry: false };
|
|
28
|
+
}
|
|
29
|
+
this.startFlight(subsystem, existing);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
const flight = {
|
|
33
|
+
pending: { operation, waiters: [resolve], retry: false },
|
|
34
|
+
running: false,
|
|
35
|
+
loop: null,
|
|
36
|
+
};
|
|
37
|
+
this.flights.set(subsystem, flight);
|
|
38
|
+
this.startFlight(subsystem, flight);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
close() {
|
|
42
|
+
this.closed = true;
|
|
43
|
+
if (this.timer)
|
|
44
|
+
clearTimeout(this.timer);
|
|
45
|
+
this.timer = null;
|
|
46
|
+
for (const [subsystem, flight] of this.flights) {
|
|
47
|
+
if (flight.pending?.retry)
|
|
48
|
+
flight.pending = null;
|
|
49
|
+
if (flight.pending)
|
|
50
|
+
this.startFlight(subsystem, flight);
|
|
51
|
+
else if (!flight.running)
|
|
52
|
+
this.flights.delete(subsystem);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async drain() {
|
|
56
|
+
while (this.flights.size > 0) {
|
|
57
|
+
const loops = [...this.flights.values()].flatMap(({ loop }) => (loop ? [loop] : []));
|
|
58
|
+
if (loops.length === 0)
|
|
59
|
+
return;
|
|
60
|
+
await Promise.all(loops);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
startFlight(subsystem, flight) {
|
|
64
|
+
if (flight.running || !flight.pending)
|
|
65
|
+
return;
|
|
66
|
+
flight.running = true;
|
|
67
|
+
const loop = this.runLoop(subsystem, flight).finally(() => {
|
|
68
|
+
flight.running = false;
|
|
69
|
+
flight.loop = null;
|
|
70
|
+
if (flight.pending && (!this.closed || !flight.pending.retry)) {
|
|
71
|
+
this.startFlight(subsystem, flight);
|
|
72
|
+
}
|
|
73
|
+
else if (!flight.pending && this.flights.get(subsystem) === flight) {
|
|
74
|
+
this.flights.delete(subsystem);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
flight.loop = loop;
|
|
78
|
+
}
|
|
79
|
+
async runLoop(subsystem, flight) {
|
|
80
|
+
while (flight.pending) {
|
|
81
|
+
const current = flight.pending;
|
|
82
|
+
flight.pending = null;
|
|
83
|
+
let failure = null;
|
|
84
|
+
try {
|
|
85
|
+
await current.operation();
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
failure = error;
|
|
89
|
+
}
|
|
90
|
+
for (const resolve of current.waiters)
|
|
91
|
+
resolve();
|
|
92
|
+
const superseded = flight.pending !== null;
|
|
93
|
+
if (this.closed)
|
|
94
|
+
continue;
|
|
95
|
+
if (failure === null) {
|
|
96
|
+
if (!superseded)
|
|
97
|
+
this.report(subsystem, null);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (superseded)
|
|
101
|
+
continue;
|
|
102
|
+
this.report(subsystem, failure);
|
|
103
|
+
flight.pending = { operation: current.operation, waiters: [], retry: true };
|
|
104
|
+
this.scheduleRetry();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
scheduleRetry() {
|
|
109
|
+
if (this.closed || this.timer)
|
|
110
|
+
return;
|
|
111
|
+
this.timer = setTimeout(() => {
|
|
112
|
+
this.timer = null;
|
|
113
|
+
for (const [subsystem, flight] of this.flights)
|
|
114
|
+
this.startFlight(subsystem, flight);
|
|
115
|
+
}, this.retryDelayMs);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jobId, MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
|
|
2
|
+
import { decodePostgresJob, encodePostgresValue } from './codec';
|
|
3
|
+
import { databaseNow, recordPostgresEvent } from './context';
|
|
4
|
+
export async function promotePostgresJobs(ctx, queue, count) {
|
|
5
|
+
const limit = count === undefined ? 2_147_483_647 : Math.max(0, count);
|
|
6
|
+
if (limit === 0)
|
|
7
|
+
return [];
|
|
8
|
+
return await ctx.sql.begin(async (tx) => {
|
|
9
|
+
const now = await databaseNow(tx);
|
|
10
|
+
const rows = await tx `
|
|
11
|
+
SELECT * FROM bunqueue_jobs
|
|
12
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
13
|
+
AND queue = ${queue}
|
|
14
|
+
AND state = 'delayed'
|
|
15
|
+
ORDER BY created_at, id
|
|
16
|
+
LIMIT ${limit}
|
|
17
|
+
FOR UPDATE SKIP LOCKED
|
|
18
|
+
`;
|
|
19
|
+
for (const row of rows) {
|
|
20
|
+
const job = decodePostgresJob(row).job;
|
|
21
|
+
job.runAt = now;
|
|
22
|
+
const state = job.priority > 0 ? 'prioritized' : 'waiting';
|
|
23
|
+
if (job.timeline.length < MAX_TIMELINE_ENTRIES) {
|
|
24
|
+
job.timeline.push({ state, timestamp: now });
|
|
25
|
+
}
|
|
26
|
+
await tx `
|
|
27
|
+
UPDATE bunqueue_jobs
|
|
28
|
+
SET payload = ${encodePostgresValue(job)}, run_at = ${now}, state = ${state},
|
|
29
|
+
version = version + 1
|
|
30
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${row.id}
|
|
31
|
+
`;
|
|
32
|
+
await recordPostgresEvent(tx, ctx, {
|
|
33
|
+
queue,
|
|
34
|
+
type: 'retried',
|
|
35
|
+
jobId: job.id,
|
|
36
|
+
occurredAt: now,
|
|
37
|
+
job,
|
|
38
|
+
state,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return rows.map((row) => jobId(row.id));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DlqEntry } from '../../../domain/types/dlq';
|
|
2
|
+
import type { Job, JobId } from '../../../domain/types/job';
|
|
3
|
+
import type { PostgresContext } from './context';
|
|
4
|
+
import type { PostgresReadSql } from './context';
|
|
5
|
+
import type { PostgresCounts, PostgresJobState, PostgresStoredJob } from './types';
|
|
6
|
+
export declare function getPostgresJob(ctx: PostgresContext, id: JobId): Promise<PostgresStoredJob | null>;
|
|
7
|
+
export declare function getPostgresJobs(ctx: PostgresContext, ids: readonly JobId[], sql?: PostgresReadSql): Promise<PostgresStoredJob[]>;
|
|
8
|
+
/** Return dependency ids that have neither a job row nor durable completion evidence. */
|
|
9
|
+
export declare function findMissingPostgresDependencies(ctx: PostgresContext, ids: readonly JobId[]): Promise<JobId[]>;
|
|
10
|
+
export interface ListPostgresJobsOptions {
|
|
11
|
+
readonly states?: readonly PostgresJobState[];
|
|
12
|
+
readonly offset?: number;
|
|
13
|
+
readonly limit?: number;
|
|
14
|
+
readonly asc?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function listPostgresJobs(ctx: PostgresContext, queue: string, options?: ListPostgresJobsOptions, sql?: PostgresReadSql): Promise<PostgresStoredJob[]>;
|
|
17
|
+
export declare function loadAllPostgresJobs(ctx: PostgresContext, sql?: PostgresReadSql): Promise<PostgresStoredJob[]>;
|
|
18
|
+
export declare function getPostgresCounts(ctx: PostgresContext, queue?: string): Promise<PostgresCounts>;
|
|
19
|
+
export declare function listPostgresQueues(ctx: PostgresContext, sql?: PostgresReadSql): Promise<string[]>;
|
|
20
|
+
export declare function listPostgresDlq(ctx: PostgresContext, queue: string, limit?: number): Promise<DlqEntry[]>;
|
|
21
|
+
export declare function latestPostgresEventId(ctx: PostgresContext): Promise<number>;
|
|
22
|
+
export declare function jobsOnly(rows: readonly PostgresStoredJob[]): Job[];
|