bunqueue 2.8.60 → 2.9.2
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 +114 -78
- package/dist/application/background/dlq.js +2 -0
- package/dist/application/background/recovery/active.d.ts +1 -0
- package/dist/application/background/recovery/active.js +12 -0
- package/dist/application/background/recovery/index.js +3 -1
- package/dist/application/background/recovery/pending.js +1 -0
- package/dist/application/cleanupTasks.js +4 -16
- package/dist/application/contextFactory.js +8 -0
- package/dist/application/dlqManager.d.ts +10 -0
- package/dist/application/dlqManager.js +48 -9
- package/dist/application/dlqRetry.js +60 -38
- package/dist/application/eventsManager.d.ts +9 -2
- package/dist/application/eventsManager.js +106 -55
- package/dist/application/operations/ackHelpers.d.ts +1 -4
- package/dist/application/operations/ackHelpers.js +39 -14
- package/dist/application/operations/flowPush.js +1 -0
- package/dist/application/operations/flowValidation.js +4 -4
- package/dist/application/operations/jobManagement.d.ts +4 -0
- package/dist/application/operations/jobMoveOperations.js +9 -4
- package/dist/application/operations/pull.d.ts +7 -2
- package/dist/application/operations/pull.js +15 -57
- package/dist/application/operations/pullFinalization.d.ts +6 -0
- package/dist/application/operations/pullFinalization.js +69 -0
- package/dist/application/operations/pullStateTransition.d.ts +8 -7
- package/dist/application/operations/pullStateTransition.js +25 -18
- package/dist/application/operations/push.js +3 -0
- package/dist/application/operations/pushBatch.js +14 -7
- package/dist/application/operations/pushContext.d.ts +2 -1
- 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 +30 -0
- package/dist/application/postgres-queue-manager/control.js +210 -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 +20 -0
- package/dist/application/postgres-queue-manager/delivery.js +83 -0
- package/dist/application/postgres-queue-manager/groupQueries.d.ts +10 -0
- package/dist/application/postgres-queue-manager/groupQueries.js +40 -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/control.js +1 -0
- package/dist/application/queue-manager/delivery.d.ts +5 -4
- package/dist/application/queue-manager/delivery.js +15 -8
- package/dist/application/queue-manager/flow-failures.js +4 -0
- package/dist/application/queue-manager/limits.d.ts +13 -0
- package/dist/application/queue-manager/limits.js +65 -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 +17 -2
- package/dist/application/queueTelemetryJournal.d.ts +2 -0
- package/dist/application/queueTelemetryJournal.js +23 -0
- package/dist/application/types/ack.d.ts +3 -1
- package/dist/cli/commandRouter.d.ts +1 -1
- package/dist/cli/commands/server.js +1 -1
- package/dist/client/flowPush.js +6 -0
- package/dist/client/forwarder.d.ts +6 -4
- package/dist/client/forwarder.js +5 -3
- package/dist/client/groupId.d.ts +1 -0
- package/dist/client/groupId.js +1 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/jobHelpers.js +1 -0
- 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/operations/add/bulk.js +4 -4
- package/dist/client/queue/operations/add/payload.d.ts +1 -0
- package/dist/client/queue/operations/add/payload.js +7 -1
- package/dist/client/queue/operations/add/single.js +3 -3
- package/dist/client/queue/operations/groups.d.ts +21 -0
- package/dist/client/queue/operations/groups.js +88 -0
- package/dist/client/queue/runtime/configuration.d.ts +4 -0
- package/dist/client/queue/runtime/configuration.js +8 -0
- package/dist/client/queue/runtime/queries.d.ts +13 -0
- package/dist/client/queue/runtime/queries.js +31 -0
- package/dist/client/queue/runtime/state.d.ts +4 -0
- package/dist/client/queue/runtime/state.js +1 -0
- package/dist/client/types/index.d.ts +2 -2
- package/dist/client/types/options.d.ts +4 -0
- package/dist/client/types/worker.d.ts +8 -0
- package/dist/client/worker/runtime/execution.js +4 -8
- package/dist/client/worker/runtime/options.js +3 -0
- package/dist/client/worker/runtime/polling.d.ts +1 -0
- package/dist/client/worker/runtime/polling.js +9 -0
- package/dist/client/worker/types/options.d.ts +2 -1
- package/dist/client/worker/workerPull.d.ts +2 -0
- package/dist/client/worker/workerPull.js +6 -4
- 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/job/groupFifoOrder.d.ts +9 -0
- package/dist/domain/job/groupFifoOrder.js +26 -0
- package/dist/domain/queue/dlqShard.d.ts +2 -0
- package/dist/domain/queue/dlqShard.js +16 -0
- package/dist/domain/queue/groupLimiterManager.d.ts +23 -0
- package/dist/domain/queue/groupLimiterManager.js +107 -0
- package/dist/domain/queue/groupScheduler.d.ts +31 -0
- package/dist/domain/queue/groupScheduler.js +256 -0
- package/dist/domain/queue/groupSchedulerOrder.d.ts +6 -0
- package/dist/domain/queue/groupSchedulerOrder.js +30 -0
- package/dist/domain/queue/priorityQueue.d.ts +13 -23
- package/dist/domain/queue/priorityQueue.js +53 -42
- package/dist/domain/queue/shard/dlq.d.ts +1 -0
- package/dist/domain/queue/shard/dlq.js +3 -0
- package/dist/domain/queue/shard/keys.d.ts +1 -0
- package/dist/domain/queue/shard/keys.js +25 -2
- package/dist/domain/queue/shard/lifecycle.js +4 -0
- package/dist/domain/queue/shard/limits.d.ts +20 -1
- package/dist/domain/queue/shard/limits.js +56 -0
- package/dist/domain/queue/shard/state.d.ts +5 -0
- package/dist/domain/queue/shard/state.js +9 -1
- package/dist/domain/types/commands/core.d.ts +3 -0
- package/dist/domain/types/commands/dlq.d.ts +5 -0
- package/dist/domain/types/commands/limits.d.ts +41 -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/group.d.ts +21 -0
- package/dist/domain/types/group.js +78 -0
- package/dist/domain/types/jobs/model.d.ts +1 -1
- package/dist/domain/types/priorityQueue.d.ts +1 -0
- 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/jobOptionsBlob.d.ts +1 -0
- package/dist/infrastructure/persistence/jobOptionsBlob.js +10 -2
- package/dist/infrastructure/persistence/migrations.js +11 -0
- package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/admission.js +196 -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 +128 -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 +14 -0
- package/dist/infrastructure/persistence/postgres/claimBatch.js +69 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.js +140 -0
- package/dist/infrastructure/persistence/postgres/claims.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/claims.js +153 -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/eventCatchupCursors.d.ts +32 -0
- package/dist/infrastructure/persistence/postgres/eventCatchupCursors.js +66 -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 +267 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +26 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +129 -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 +276 -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/groupClaims.d.ts +15 -0
- package/dist/infrastructure/persistence/postgres/groupClaims.js +95 -0
- package/dist/infrastructure/persistence/postgres/groupSchema.d.ts +2 -0
- package/dist/infrastructure/persistence/postgres/groupSchema.js +155 -0
- package/dist/infrastructure/persistence/postgres/groupSchemaFingerprint.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/groupSchemaFingerprint.js +127 -0
- package/dist/infrastructure/persistence/postgres/groupStateRetention.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/groupStateRetention.js +93 -0
- package/dist/infrastructure/persistence/postgres/groups.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/groups.js +117 -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 +158 -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 +45 -0
- package/dist/infrastructure/persistence/postgres/runtime.js +238 -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 +261 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.js +226 -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 +100 -0
- package/dist/infrastructure/persistence/postgres/store.js +180 -0
- package/dist/infrastructure/persistence/postgres/telemetry.d.ts +8 -0
- package/dist/infrastructure/persistence/postgres/telemetry.js +100 -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 +176 -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/schema.d.ts +2 -2
- package/dist/infrastructure/persistence/schema.js +12 -1
- package/dist/infrastructure/persistence/sqlite/control.d.ts +14 -0
- package/dist/infrastructure/persistence/sqlite/control.js +69 -0
- package/dist/infrastructure/persistence/sqlite/jobs.d.ts +12 -0
- package/dist/infrastructure/persistence/sqlite/jobs.js +38 -0
- package/dist/infrastructure/persistence/sqlite/telemetry.d.ts +2 -1
- package/dist/infrastructure/persistence/sqlite/telemetry.js +5 -64
- package/dist/infrastructure/persistence/sqlite/telemetryWrites.d.ts +4 -0
- package/dist/infrastructure/persistence/sqlite/telemetryWrites.js +123 -0
- package/dist/infrastructure/persistence/sqliteSerializer.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 +16 -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.d.ts +0 -5
- package/dist/infrastructure/server/handlers/core.js +47 -49
- 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/groups.d.ts +11 -0
- package/dist/infrastructure/server/handlers/groups.js +55 -0
- 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 +56 -8
- 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/protocol/validation.d.ts +1 -0
- package/dist/infrastructure/server/protocol/validation.js +1 -0
- package/dist/infrastructure/server/protocol.d.ts +1 -1
- package/dist/infrastructure/server/protocol.js +1 -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 +75 -65
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { createJob, generateJobId, MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
|
|
2
|
+
import { buildRepeatSuccessor } from '../../../application/repeatJobs';
|
|
3
|
+
import { decodePostgresJob, encodePostgresValue, postgresByteaBase64 } from './codec';
|
|
4
|
+
import { databaseNow } from './context';
|
|
5
|
+
import { admitPostgresJob } from './admission';
|
|
6
|
+
import { recordPostgresCompletionEvents } from './completionEvents';
|
|
7
|
+
import { planPostgresDependencyCompletion } from './dependencyPromotion';
|
|
8
|
+
import { runPostgresTransactionWithRetry } from './transactionRetry';
|
|
9
|
+
import { tryLockPostgresRepeatQueues } from './queueLifecycle';
|
|
10
|
+
function invalidToken(id) {
|
|
11
|
+
return new Error(`Invalid or expired lock token for job ${id}`);
|
|
12
|
+
}
|
|
13
|
+
function prepareCompletions(rows, inputs, now) {
|
|
14
|
+
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
15
|
+
const applied = [];
|
|
16
|
+
const results = inputs.map((input) => {
|
|
17
|
+
const row = byId.get(String(input.id));
|
|
18
|
+
if (!row)
|
|
19
|
+
throw invalidToken(String(input.id));
|
|
20
|
+
if (row.state === 'completed' || row.state === 'failed') {
|
|
21
|
+
return {
|
|
22
|
+
applied: false,
|
|
23
|
+
alreadyFinalized: true,
|
|
24
|
+
job: decodePostgresJob(row).job,
|
|
25
|
+
state: row.state,
|
|
26
|
+
removed: false,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (row.state !== 'active' ||
|
|
30
|
+
row.lease_token !== input.token ||
|
|
31
|
+
Number(row.lease_until ?? 0) <= now) {
|
|
32
|
+
throw invalidToken(String(input.id));
|
|
33
|
+
}
|
|
34
|
+
const job = decodePostgresJob(row).job;
|
|
35
|
+
job.completedAt = now;
|
|
36
|
+
job.progress = 100;
|
|
37
|
+
if (job.timeline.length < MAX_TIMELINE_ENTRIES) {
|
|
38
|
+
job.timeline.push({ state: 'completed', timestamp: now });
|
|
39
|
+
}
|
|
40
|
+
const removed = job.removeOnComplete || input.removeOnComplete === true;
|
|
41
|
+
applied.push({
|
|
42
|
+
input,
|
|
43
|
+
job,
|
|
44
|
+
payload: encodePostgresValue(job),
|
|
45
|
+
resultPayload: encodePostgresValue(input.result),
|
|
46
|
+
removed,
|
|
47
|
+
});
|
|
48
|
+
return { applied: true, alreadyFinalized: false, job, state: 'completed', removed };
|
|
49
|
+
});
|
|
50
|
+
return { applied, results };
|
|
51
|
+
}
|
|
52
|
+
async function lockRows(tx, ctx, inputs) {
|
|
53
|
+
const ids = inputs.map((input) => String(input.id)).sort();
|
|
54
|
+
return await tx `
|
|
55
|
+
SELECT * FROM bunqueue_jobs
|
|
56
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
57
|
+
AND id = ANY(${tx.array(ids, 'TEXT')})
|
|
58
|
+
ORDER BY id
|
|
59
|
+
FOR UPDATE
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
async function storeResults(tx, ctx, applied, now) {
|
|
63
|
+
await tx `
|
|
64
|
+
INSERT INTO bunqueue_completions (namespace, job_id, queue, completed_at, result)
|
|
65
|
+
SELECT
|
|
66
|
+
${ctx.config.namespace}, batch.id, batch.queue, ${now},
|
|
67
|
+
decode(batch.result_base64, 'base64')
|
|
68
|
+
FROM unnest(
|
|
69
|
+
${tx.array(applied.map(({ job }) => String(job.id)), 'TEXT')},
|
|
70
|
+
${tx.array(applied.map(({ job }) => job.queue), 'TEXT')},
|
|
71
|
+
${tx.array(applied.map(({ resultPayload }) => postgresByteaBase64(resultPayload)), 'TEXT')}
|
|
72
|
+
) AS batch(id, queue, result_base64)
|
|
73
|
+
ON CONFLICT (namespace, job_id) DO UPDATE SET
|
|
74
|
+
queue = excluded.queue,
|
|
75
|
+
completed_at = excluded.completed_at,
|
|
76
|
+
result = excluded.result
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
async function updateRetainedJobs(tx, ctx, applied, now) {
|
|
80
|
+
const retained = applied.filter((item) => !item.removed);
|
|
81
|
+
if (retained.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
await tx `
|
|
84
|
+
UPDATE bunqueue_jobs AS job
|
|
85
|
+
SET payload = decode(batch.payload_base64, 'base64'),
|
|
86
|
+
state = 'completed',
|
|
87
|
+
completed_at = ${now},
|
|
88
|
+
result = decode(batch.result_base64, 'base64'),
|
|
89
|
+
lease_owner = NULL,
|
|
90
|
+
lease_token = NULL,
|
|
91
|
+
lease_broker_id = NULL, lease_broker_session_id = NULL,
|
|
92
|
+
lease_until = NULL,
|
|
93
|
+
dlq_retry_state = NULL,
|
|
94
|
+
version = job.version + 1
|
|
95
|
+
FROM unnest(
|
|
96
|
+
${tx.array(retained.map(({ job }) => String(job.id)), 'TEXT')},
|
|
97
|
+
${tx.array(retained.map(({ payload }) => postgresByteaBase64(payload)), 'TEXT')},
|
|
98
|
+
${tx.array(retained.map(({ resultPayload }) => postgresByteaBase64(resultPayload)), 'TEXT')}
|
|
99
|
+
) AS batch(id, payload_base64, result_base64)
|
|
100
|
+
WHERE job.namespace = ${ctx.config.namespace} AND job.id = batch.id
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
async function deleteRemovedJobs(tx, ctx, applied) {
|
|
104
|
+
const ids = applied.filter((item) => item.removed).map(({ job }) => String(job.id));
|
|
105
|
+
if (ids.length === 0)
|
|
106
|
+
return;
|
|
107
|
+
await tx `
|
|
108
|
+
DELETE FROM bunqueue_job_logs
|
|
109
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ANY(${tx.array(ids, 'TEXT')})
|
|
110
|
+
`;
|
|
111
|
+
await tx `
|
|
112
|
+
DELETE FROM bunqueue_dependencies
|
|
113
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ANY(${tx.array(ids, 'TEXT')})
|
|
114
|
+
`;
|
|
115
|
+
await tx `
|
|
116
|
+
DELETE FROM bunqueue_jobs
|
|
117
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ANY(${tx.array(ids, 'TEXT')})
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
async function createRepeatSuccessors(tx, ctx, applied, now) {
|
|
121
|
+
for (const { job } of applied) {
|
|
122
|
+
const shouldRepeat = job.repeat !== null &&
|
|
123
|
+
(job.repeat.limit === undefined || job.repeat.count < job.repeat.limit);
|
|
124
|
+
const successorInput = shouldRepeat ? buildRepeatSuccessor(job, now) : null;
|
|
125
|
+
if (!successorInput)
|
|
126
|
+
continue;
|
|
127
|
+
const successor = createJob(generateJobId(), job.queue, successorInput, now);
|
|
128
|
+
const admitted = await admitPostgresJob(tx, ctx, successor, {
|
|
129
|
+
linkParent: false,
|
|
130
|
+
queueLifecycleLocksHeld: true,
|
|
131
|
+
});
|
|
132
|
+
await tx `
|
|
133
|
+
INSERT INTO bunqueue_repeat_links (namespace, original_id, successor_id)
|
|
134
|
+
VALUES (${ctx.config.namespace}, ${String(job.id)}, ${String(admitted.job.id)})
|
|
135
|
+
ON CONFLICT (namespace, original_id) DO UPDATE SET successor_id = excluded.successor_id
|
|
136
|
+
`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Complete a unique ACK batch atomically with set-based persistence. */
|
|
140
|
+
export async function completePostgresJobs(ctx, inputs) {
|
|
141
|
+
if (inputs.length === 0)
|
|
142
|
+
return [];
|
|
143
|
+
if (new Set(inputs.map((input) => String(input.id))).size !== inputs.length) {
|
|
144
|
+
throw new Error('PostgreSQL batch completion requires unique job IDs');
|
|
145
|
+
}
|
|
146
|
+
return await runPostgresTransactionWithRetry(ctx, async (tx) => {
|
|
147
|
+
const now = await databaseNow(tx);
|
|
148
|
+
const dependencyCompletion = await planPostgresDependencyCompletion(tx, ctx, inputs.map(({ id }) => id));
|
|
149
|
+
const rows = await lockRows(tx, ctx, inputs);
|
|
150
|
+
const { applied, results } = prepareCompletions(rows, inputs, now);
|
|
151
|
+
if (applied.length === 0)
|
|
152
|
+
return results;
|
|
153
|
+
const repeatQueues = applied.flatMap(({ job }) => {
|
|
154
|
+
const repeats = job.repeat !== null &&
|
|
155
|
+
(job.repeat.limit === undefined || job.repeat.count < job.repeat.limit);
|
|
156
|
+
return repeats ? [job.queue] : [];
|
|
157
|
+
});
|
|
158
|
+
if (!(await tryLockPostgresRepeatQueues(tx, ctx, repeatQueues))) {
|
|
159
|
+
throw new Error('A repeat queue is being obliterated; retry batch completion');
|
|
160
|
+
}
|
|
161
|
+
const ids = applied.map(({ job }) => String(job.id));
|
|
162
|
+
await tx `
|
|
163
|
+
DELETE FROM bunqueue_flow_failures
|
|
164
|
+
WHERE namespace = ${ctx.config.namespace} AND parent_id = ANY(${tx.array(ids, 'TEXT')})
|
|
165
|
+
`;
|
|
166
|
+
await storeResults(tx, ctx, applied, now);
|
|
167
|
+
await updateRetainedJobs(tx, ctx, applied, now);
|
|
168
|
+
await deleteRemovedJobs(tx, ctx, applied);
|
|
169
|
+
await recordPostgresCompletionEvents(tx, ctx, applied.map(({ job, input, removed }) => ({ job, result: input.result, removed })), now);
|
|
170
|
+
await dependencyCompletion.promote(now);
|
|
171
|
+
await createRepeatSuccessors(tx, ctx, applied, now);
|
|
172
|
+
return results;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TransactionSQL } from 'bun';
|
|
2
|
+
import type { Job } from '../../../domain/types/job';
|
|
3
|
+
import type { PostgresContext } from './context';
|
|
4
|
+
import type { PostgresJobState } from './types';
|
|
5
|
+
export interface PostgresBatchJobEventInput {
|
|
6
|
+
readonly job: Job;
|
|
7
|
+
readonly type: string;
|
|
8
|
+
readonly state?: PostgresJobState;
|
|
9
|
+
readonly result?: unknown;
|
|
10
|
+
readonly error?: string;
|
|
11
|
+
readonly removed?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Persist job events while amortizing retention and wakeups per affected queue. */
|
|
14
|
+
export declare function recordPostgresJobEvents(tx: TransactionSQL, ctx: PostgresContext, inputs: readonly PostgresBatchJobEventInput[], now: number): Promise<void>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { eventPayload, postgresByteaBase64 } from './codec';
|
|
2
|
+
import { recordPostgresEventPruneWatermarks, summarizePostgresPrunedEvents, } from './eventPruneWatermarks';
|
|
3
|
+
import { prunePostgresQueueEvents, tryLockPostgresEventRetention } from './eventRetention';
|
|
4
|
+
import { POSTGRES_EVENT_CHANNEL } from './schema';
|
|
5
|
+
function payloadForEvent(input) {
|
|
6
|
+
return eventPayload({
|
|
7
|
+
job: input.job,
|
|
8
|
+
...(input.state && { state: input.state }),
|
|
9
|
+
...(Object.hasOwn(input, 'result') && { result: input.result }),
|
|
10
|
+
...(input.error !== undefined && { error: input.error }),
|
|
11
|
+
...(input.removed !== undefined && { removed: input.removed }),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
async function insertEvents(tx, ctx, inputs, now) {
|
|
15
|
+
const inserted = await tx `
|
|
16
|
+
INSERT INTO bunqueue_events
|
|
17
|
+
(namespace, queue, event_type, job_id, occurred_at, payload)
|
|
18
|
+
SELECT
|
|
19
|
+
${ctx.config.namespace}, batch.queue, batch.event_type, batch.job_id, ${now},
|
|
20
|
+
decode(batch.payload_base64, 'base64')
|
|
21
|
+
FROM unnest(
|
|
22
|
+
${tx.array(inputs.map((input) => input.job.queue), 'TEXT')},
|
|
23
|
+
${tx.array(inputs.map((input) => input.type), 'TEXT')},
|
|
24
|
+
${tx.array(inputs.map((input) => String(input.job.id)), 'TEXT')},
|
|
25
|
+
${tx.array(inputs.map((input) => postgresByteaBase64(payloadForEvent(input))), 'TEXT')}
|
|
26
|
+
) WITH ORDINALITY AS batch(queue, event_type, job_id, payload_base64, position)
|
|
27
|
+
ORDER BY batch.position
|
|
28
|
+
RETURNING id
|
|
29
|
+
`;
|
|
30
|
+
const ids = inserted.map((row) => Number(row.id));
|
|
31
|
+
return { startId: Math.min(...ids), endId: Math.max(...ids) };
|
|
32
|
+
}
|
|
33
|
+
async function trimEvents(tx, ctx, queues, sourceEventId) {
|
|
34
|
+
const watermarks = [];
|
|
35
|
+
const skippedQueues = new Set();
|
|
36
|
+
for (const queue of [...queues].sort()) {
|
|
37
|
+
if (!(await tryLockPostgresEventRetention(tx, ctx, queue))) {
|
|
38
|
+
skippedQueues.add(queue);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const pruned = await prunePostgresQueueEvents(tx, ctx, queue, ctx.config.maxQueueEvents);
|
|
42
|
+
const prune = summarizePostgresPrunedEvents(pruned);
|
|
43
|
+
if (prune.prunedThrough > 0)
|
|
44
|
+
watermarks.push({ queue, sourceEventId, ...prune });
|
|
45
|
+
}
|
|
46
|
+
await recordPostgresEventPruneWatermarks(tx, ctx, watermarks);
|
|
47
|
+
return {
|
|
48
|
+
watermarks: new Map(watermarks.map(({ queue, prunedThrough }) => [queue, prunedThrough])),
|
|
49
|
+
skippedQueues,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Persist job events while amortizing retention and wakeups per affected queue. */
|
|
53
|
+
export async function recordPostgresJobEvents(tx, ctx, inputs, now) {
|
|
54
|
+
if (inputs.length === 0)
|
|
55
|
+
return;
|
|
56
|
+
const queues = [...new Set(inputs.map((input) => input.job.queue))];
|
|
57
|
+
const eventRange = await insertEvents(tx, ctx, inputs, now);
|
|
58
|
+
const { watermarks, skippedQueues } = await trimEvents(tx, ctx, queues, eventRange.endId);
|
|
59
|
+
for (const queue of queues) {
|
|
60
|
+
const prunedThrough = watermarks.get(queue);
|
|
61
|
+
await tx.notify(POSTGRES_EVENT_CHANNEL, JSON.stringify({
|
|
62
|
+
namespace: ctx.config.namespace,
|
|
63
|
+
queue,
|
|
64
|
+
brokerId: ctx.config.brokerId,
|
|
65
|
+
eventStartId: eventRange.startId,
|
|
66
|
+
eventEndId: eventRange.endId,
|
|
67
|
+
...(prunedThrough !== undefined && { prunedThrough }),
|
|
68
|
+
...(skippedQueues.has(queue) && { retentionRequested: true }),
|
|
69
|
+
...(watermarks.size > 0 && { scanPruneWatermarks: true }),
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TransactionSQL } from 'bun';
|
|
2
|
+
import { type PostgresContext } from './context';
|
|
3
|
+
export declare class PostgresBrokerIdentityInUseError extends Error {
|
|
4
|
+
constructor(brokerId: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class PostgresBrokerSessionFencedError extends Error {
|
|
7
|
+
constructor(brokerId: string);
|
|
8
|
+
}
|
|
9
|
+
export declare function postgresBrokerStaleMs(ctx: PostgresContext): number;
|
|
10
|
+
export declare function registerPostgresBroker(ctx: PostgresContext): Promise<void>;
|
|
11
|
+
export declare function lockPostgresBrokerSession(tx: TransactionSQL, ctx: PostgresContext): Promise<void>;
|
|
12
|
+
export declare function heartbeatPostgresBroker(ctx: PostgresContext): Promise<void>;
|
|
13
|
+
export declare function unregisterPostgresBroker(ctx: PostgresContext): Promise<void>;
|
|
14
|
+
export declare function purgeStalePostgresBrokers(ctx: PostgresContext): Promise<number>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { databaseNow } from './context';
|
|
2
|
+
export class PostgresBrokerIdentityInUseError extends Error {
|
|
3
|
+
constructor(brokerId) {
|
|
4
|
+
super(`PostgreSQL brokerId "${brokerId}" is already active`);
|
|
5
|
+
this.name = 'PostgresBrokerIdentityInUseError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class PostgresBrokerSessionFencedError extends Error {
|
|
9
|
+
constructor(brokerId) {
|
|
10
|
+
super(`PostgreSQL broker session for "${brokerId}" has been fenced`);
|
|
11
|
+
this.name = 'PostgresBrokerSessionFencedError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function postgresBrokerStaleMs(ctx) {
|
|
15
|
+
const heartbeatMs = Math.max(1000, Math.floor(ctx.config.leaseDurationMs / 3));
|
|
16
|
+
return Math.max(ctx.config.leaseDurationMs, heartbeatMs * 3);
|
|
17
|
+
}
|
|
18
|
+
export async function registerPostgresBroker(ctx) {
|
|
19
|
+
const now = await databaseNow(ctx.sql);
|
|
20
|
+
const staleBefore = now - postgresBrokerStaleMs(ctx);
|
|
21
|
+
const [existing] = await ctx.sql `
|
|
22
|
+
SELECT broker_id, session_id, heartbeat_at FROM bunqueue_brokers
|
|
23
|
+
WHERE namespace = ${ctx.config.namespace} AND broker_id = ${ctx.config.brokerId}
|
|
24
|
+
`;
|
|
25
|
+
if (existing &&
|
|
26
|
+
existing.session_id !== ctx.config.brokerSessionId &&
|
|
27
|
+
Number(existing.heartbeat_at) > staleBefore) {
|
|
28
|
+
throw new PostgresBrokerIdentityInUseError(ctx.config.brokerId);
|
|
29
|
+
}
|
|
30
|
+
const rows = await ctx.sql.begin(async (tx) => {
|
|
31
|
+
const transactionNow = await databaseNow(tx);
|
|
32
|
+
return await tx `
|
|
33
|
+
INSERT INTO bunqueue_brokers AS broker
|
|
34
|
+
(namespace, broker_id, session_id, started_at, heartbeat_at)
|
|
35
|
+
VALUES
|
|
36
|
+
(${ctx.config.namespace}, ${ctx.config.brokerId}, ${ctx.config.brokerSessionId},
|
|
37
|
+
${transactionNow}, ${transactionNow})
|
|
38
|
+
ON CONFLICT (namespace, broker_id) DO UPDATE SET
|
|
39
|
+
session_id = excluded.session_id,
|
|
40
|
+
started_at = excluded.started_at,
|
|
41
|
+
heartbeat_at = excluded.heartbeat_at
|
|
42
|
+
WHERE broker.session_id IS NOT DISTINCT FROM excluded.session_id
|
|
43
|
+
OR broker.heartbeat_at <= ${transactionNow - postgresBrokerStaleMs(ctx)}
|
|
44
|
+
RETURNING broker_id
|
|
45
|
+
`;
|
|
46
|
+
});
|
|
47
|
+
if (rows.length === 0)
|
|
48
|
+
throw new PostgresBrokerIdentityInUseError(ctx.config.brokerId);
|
|
49
|
+
}
|
|
50
|
+
export async function lockPostgresBrokerSession(tx, ctx) {
|
|
51
|
+
const rows = await tx `
|
|
52
|
+
SELECT broker_id FROM bunqueue_brokers
|
|
53
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
54
|
+
AND broker_id = ${ctx.config.brokerId}
|
|
55
|
+
AND session_id = ${ctx.config.brokerSessionId}
|
|
56
|
+
FOR SHARE
|
|
57
|
+
`;
|
|
58
|
+
if (rows.length === 0)
|
|
59
|
+
throw new PostgresBrokerSessionFencedError(ctx.config.brokerId);
|
|
60
|
+
}
|
|
61
|
+
export async function heartbeatPostgresBroker(ctx) {
|
|
62
|
+
const now = await databaseNow(ctx.sql);
|
|
63
|
+
const rows = await ctx.sql `
|
|
64
|
+
UPDATE bunqueue_brokers SET heartbeat_at = ${now}
|
|
65
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
66
|
+
AND broker_id = ${ctx.config.brokerId}
|
|
67
|
+
AND session_id = ${ctx.config.brokerSessionId}
|
|
68
|
+
RETURNING broker_id
|
|
69
|
+
`;
|
|
70
|
+
if (rows.length === 0)
|
|
71
|
+
throw new PostgresBrokerSessionFencedError(ctx.config.brokerId);
|
|
72
|
+
}
|
|
73
|
+
export async function unregisterPostgresBroker(ctx) {
|
|
74
|
+
await ctx.sql `
|
|
75
|
+
DELETE FROM bunqueue_brokers
|
|
76
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
77
|
+
AND broker_id = ${ctx.config.brokerId}
|
|
78
|
+
AND session_id = ${ctx.config.brokerSessionId}
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
export async function purgeStalePostgresBrokers(ctx) {
|
|
82
|
+
const now = await databaseNow(ctx.sql);
|
|
83
|
+
const rows = await ctx.sql `
|
|
84
|
+
DELETE FROM bunqueue_brokers
|
|
85
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
86
|
+
AND heartbeat_at <= ${now - postgresBrokerStaleMs(ctx)}
|
|
87
|
+
AND NOT (
|
|
88
|
+
broker_id = ${ctx.config.brokerId}
|
|
89
|
+
AND session_id IS NOT DISTINCT FROM ${ctx.config.brokerSessionId}
|
|
90
|
+
)
|
|
91
|
+
RETURNING broker_id
|
|
92
|
+
`;
|
|
93
|
+
return rows.length;
|
|
94
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TransactionSQL } from 'bun';
|
|
2
|
+
import type { GroupPullOptions } from '../../../domain/types/group';
|
|
3
|
+
import type { PostgresContext } from './context';
|
|
4
|
+
import type { ClaimedPostgresJob } from './types';
|
|
5
|
+
/** Claim one queue batch after its queue-state row has been locked. */
|
|
6
|
+
export declare function claimPostgresCandidates(tx: TransactionSQL, ctx: PostgresContext, input: {
|
|
7
|
+
queue: string;
|
|
8
|
+
capacity: number;
|
|
9
|
+
owner: string;
|
|
10
|
+
requestedLeaseMs: number;
|
|
11
|
+
now: number;
|
|
12
|
+
groupOptions?: GroupPullOptions;
|
|
13
|
+
groupSequence: number;
|
|
14
|
+
}): Promise<ClaimedPostgresJob[]>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { generateLockToken, MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
|
|
2
|
+
import { recordPostgresJobEvents } from './batchEvents';
|
|
3
|
+
import { selectPostgresClaimCandidates } from './claimSelection';
|
|
4
|
+
import { decodePostgresJob, encodePostgresValue, postgresByteaBase64 } from './codec';
|
|
5
|
+
import { commitPostgresGroupClaims } from './groupClaims';
|
|
6
|
+
function prepareClaims(rows, input) {
|
|
7
|
+
const { owner, requestedLeaseMs, now, brokerId } = input;
|
|
8
|
+
return rows.map((row) => {
|
|
9
|
+
const job = decodePostgresJob(row).job;
|
|
10
|
+
const token = String(generateLockToken());
|
|
11
|
+
const leaseDuration = Math.max(1, Math.min(requestedLeaseMs, job.timeout === null ? requestedLeaseMs : job.timeout, job.stallTimeout === null ? requestedLeaseMs : job.stallTimeout));
|
|
12
|
+
const leaseUntil = now + leaseDuration;
|
|
13
|
+
job.startedAt = now;
|
|
14
|
+
job.lastHeartbeat = now;
|
|
15
|
+
if (job.timeline.length < MAX_TIMELINE_ENTRIES) {
|
|
16
|
+
job.timeline.push({ state: 'active', timestamp: now, worker: owner });
|
|
17
|
+
}
|
|
18
|
+
return { job, token, owner, brokerId, leaseUntil };
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function persistClaims(tx, ctx, claimed, now) {
|
|
22
|
+
await tx `
|
|
23
|
+
UPDATE bunqueue_jobs AS job
|
|
24
|
+
SET state = 'active',
|
|
25
|
+
payload = decode(batch.payload_base64, 'base64'),
|
|
26
|
+
started_at = ${now},
|
|
27
|
+
lease_owner = ${claimed[0].owner},
|
|
28
|
+
lease_broker_id = ${ctx.config.brokerId},
|
|
29
|
+
lease_broker_session_id = ${ctx.config.brokerSessionId},
|
|
30
|
+
lease_token = batch.token,
|
|
31
|
+
lease_until = batch.lease_until,
|
|
32
|
+
lease_renewals = 0,
|
|
33
|
+
version = job.version + 1
|
|
34
|
+
FROM unnest(
|
|
35
|
+
${tx.array(claimed.map(({ job }) => String(job.id)), 'TEXT')},
|
|
36
|
+
${tx.array(claimed.map(({ job }) => postgresByteaBase64(encodePostgresValue(job))), 'TEXT')},
|
|
37
|
+
${tx.array(claimed.map(({ token }) => token), 'TEXT')},
|
|
38
|
+
${tx.array(claimed.map(({ leaseUntil }) => leaseUntil), 'BIGINT')}
|
|
39
|
+
) AS batch(id, payload_base64, token, lease_until)
|
|
40
|
+
WHERE job.namespace = ${ctx.config.namespace} AND job.id = batch.id
|
|
41
|
+
`;
|
|
42
|
+
await recordPostgresJobEvents(tx, ctx, claimed.map(({ job }) => ({ job, type: 'pulled', state: 'active' })), now);
|
|
43
|
+
}
|
|
44
|
+
/** Claim one queue batch after its queue-state row has been locked. */
|
|
45
|
+
export async function claimPostgresCandidates(tx, ctx, input) {
|
|
46
|
+
const { queue, capacity, owner, requestedLeaseMs, now, groupOptions, groupSequence } = input;
|
|
47
|
+
const rows = await selectPostgresClaimCandidates(tx, ctx, {
|
|
48
|
+
queue,
|
|
49
|
+
now,
|
|
50
|
+
capacity,
|
|
51
|
+
options: groupOptions,
|
|
52
|
+
});
|
|
53
|
+
const claimed = prepareClaims(rows, {
|
|
54
|
+
owner,
|
|
55
|
+
requestedLeaseMs,
|
|
56
|
+
now,
|
|
57
|
+
brokerId: ctx.config.brokerId,
|
|
58
|
+
});
|
|
59
|
+
if (claimed.length > 0) {
|
|
60
|
+
await commitPostgresGroupClaims(tx, ctx, {
|
|
61
|
+
queue,
|
|
62
|
+
rows,
|
|
63
|
+
options: groupOptions,
|
|
64
|
+
sequence: groupSequence,
|
|
65
|
+
});
|
|
66
|
+
await persistClaims(tx, ctx, claimed, now);
|
|
67
|
+
}
|
|
68
|
+
return claimed;
|
|
69
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TransactionSQL } from 'bun';
|
|
2
|
+
import type { GroupPullOptions } from '../../../domain/types/group';
|
|
3
|
+
import type { PostgresContext } from './context';
|
|
4
|
+
import type { PostgresJobRow } from './types';
|
|
5
|
+
export declare function hasClaimablePostgresGroup(tx: TransactionSQL, ctx: PostgresContext, queue: string, now: number): Promise<boolean>;
|
|
6
|
+
export declare function selectPostgresClaimCandidates(tx: TransactionSQL, ctx: PostgresContext, input: {
|
|
7
|
+
queue: string;
|
|
8
|
+
now: number;
|
|
9
|
+
capacity: number;
|
|
10
|
+
options?: GroupPullOptions;
|
|
11
|
+
}): Promise<PostgresJobRow[]>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export async function hasClaimablePostgresGroup(tx, ctx, queue, now) {
|
|
2
|
+
const [row] = await tx `
|
|
3
|
+
SELECT EXISTS (
|
|
4
|
+
SELECT 1 FROM bunqueue_jobs AS job
|
|
5
|
+
WHERE job.namespace = ${ctx.config.namespace} AND job.queue = ${queue}
|
|
6
|
+
AND job.group_id IS NOT NULL
|
|
7
|
+
AND job.state IN ('waiting', 'prioritized', 'delayed')
|
|
8
|
+
AND job.run_at <= ${now}
|
|
9
|
+
AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
|
|
10
|
+
AND NOT EXISTS (
|
|
11
|
+
SELECT 1 FROM bunqueue_dependencies AS dependency
|
|
12
|
+
WHERE dependency.namespace = job.namespace AND dependency.job_id = job.id
|
|
13
|
+
AND NOT EXISTS (
|
|
14
|
+
SELECT 1 FROM bunqueue_completions AS completion
|
|
15
|
+
WHERE completion.namespace = dependency.namespace
|
|
16
|
+
AND completion.job_id = dependency.dependency_id
|
|
17
|
+
)
|
|
18
|
+
)
|
|
19
|
+
) AS exists
|
|
20
|
+
`;
|
|
21
|
+
return row.exists;
|
|
22
|
+
}
|
|
23
|
+
async function selectUngroupedCandidates(tx, ctx, queue, now, capacity) {
|
|
24
|
+
return await tx `
|
|
25
|
+
WITH selected AS (
|
|
26
|
+
SELECT job.id, job.priority, job.lifo, job.run_at
|
|
27
|
+
FROM bunqueue_jobs AS job
|
|
28
|
+
WHERE job.namespace = ${ctx.config.namespace} AND job.queue = ${queue}
|
|
29
|
+
AND job.group_id IS NULL
|
|
30
|
+
AND job.state IN ('waiting', 'prioritized', 'delayed')
|
|
31
|
+
AND job.run_at <= ${now}
|
|
32
|
+
AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
|
|
33
|
+
AND NOT EXISTS (
|
|
34
|
+
SELECT 1 FROM bunqueue_dependencies AS dependency
|
|
35
|
+
WHERE dependency.namespace = job.namespace AND dependency.job_id = job.id
|
|
36
|
+
AND NOT EXISTS (
|
|
37
|
+
SELECT 1 FROM bunqueue_completions AS completion
|
|
38
|
+
WHERE completion.namespace = dependency.namespace
|
|
39
|
+
AND completion.job_id = dependency.dependency_id
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
ORDER BY job.priority DESC,
|
|
43
|
+
CASE WHEN job.lifo THEN 0 ELSE 1 END,
|
|
44
|
+
CASE WHEN job.lifo THEN job.id END DESC,
|
|
45
|
+
CASE WHEN NOT job.lifo THEN job.run_at END,
|
|
46
|
+
job.id
|
|
47
|
+
LIMIT ${capacity}
|
|
48
|
+
FOR UPDATE OF job SKIP LOCKED
|
|
49
|
+
)
|
|
50
|
+
SELECT job.* FROM bunqueue_jobs AS job
|
|
51
|
+
JOIN selected ON selected.id = job.id
|
|
52
|
+
WHERE job.namespace = ${ctx.config.namespace}
|
|
53
|
+
ORDER BY selected.priority DESC,
|
|
54
|
+
CASE WHEN selected.lifo THEN 0 ELSE 1 END,
|
|
55
|
+
CASE WHEN selected.lifo THEN selected.id END DESC,
|
|
56
|
+
CASE WHEN NOT selected.lifo THEN selected.run_at END,
|
|
57
|
+
selected.id
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
async function selectGroupedCandidates(tx, ctx, input) {
|
|
61
|
+
const { queue, now, capacity, options } = input;
|
|
62
|
+
const concurrency = options?.concurrency ?? null;
|
|
63
|
+
const rateMax = options?.limit?.max ?? null;
|
|
64
|
+
return await tx `
|
|
65
|
+
WITH ready AS (
|
|
66
|
+
SELECT job.id, job.group_id,
|
|
67
|
+
row_number() OVER (
|
|
68
|
+
PARTITION BY job.group_id ORDER BY job.run_at, job.group_order, job.id
|
|
69
|
+
) AS group_position
|
|
70
|
+
FROM bunqueue_jobs AS job
|
|
71
|
+
WHERE job.namespace = ${ctx.config.namespace} AND job.queue = ${queue}
|
|
72
|
+
AND job.group_id IS NOT NULL
|
|
73
|
+
AND job.state IN ('waiting', 'prioritized', 'delayed')
|
|
74
|
+
AND job.run_at <= ${now}
|
|
75
|
+
AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
|
|
76
|
+
AND NOT EXISTS (
|
|
77
|
+
SELECT 1 FROM bunqueue_dependencies AS dependency
|
|
78
|
+
WHERE dependency.namespace = job.namespace AND dependency.job_id = job.id
|
|
79
|
+
AND NOT EXISTS (
|
|
80
|
+
SELECT 1 FROM bunqueue_completions AS completion
|
|
81
|
+
WHERE completion.namespace = dependency.namespace
|
|
82
|
+
AND completion.job_id = dependency.dependency_id
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
), active AS (
|
|
86
|
+
SELECT group_id, COUNT(*)::integer AS count
|
|
87
|
+
FROM bunqueue_jobs
|
|
88
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
89
|
+
AND group_id IS NOT NULL AND state = 'active' AND lease_until > ${now}
|
|
90
|
+
GROUP BY group_id
|
|
91
|
+
), capacities AS (
|
|
92
|
+
SELECT groups.group_id, groups.last_served,
|
|
93
|
+
LEAST(
|
|
94
|
+
${capacity},
|
|
95
|
+
CASE WHEN CAST(${concurrency} AS BIGINT) IS NULL THEN ${capacity}
|
|
96
|
+
ELSE GREATEST(
|
|
97
|
+
0,
|
|
98
|
+
COALESCE(groups.concurrency_limit, CAST(${concurrency} AS BIGINT))
|
|
99
|
+
- COALESCE(active.count, 0)
|
|
100
|
+
)
|
|
101
|
+
END,
|
|
102
|
+
CASE WHEN CAST(${rateMax} AS BIGINT) IS NULL THEN ${capacity}
|
|
103
|
+
ELSE GREATEST(
|
|
104
|
+
0,
|
|
105
|
+
COALESCE(groups.rate_limit, CAST(${rateMax} AS BIGINT)) - groups.rate_count
|
|
106
|
+
)
|
|
107
|
+
END
|
|
108
|
+
) AS available
|
|
109
|
+
FROM bunqueue_group_state AS groups
|
|
110
|
+
LEFT JOIN active ON active.group_id = groups.group_id
|
|
111
|
+
WHERE groups.namespace = ${ctx.config.namespace} AND groups.queue = ${queue}
|
|
112
|
+
), selected AS (
|
|
113
|
+
SELECT ready.id, ready.group_id, ready.group_position, capacities.last_served
|
|
114
|
+
FROM ready JOIN capacities ON capacities.group_id = ready.group_id
|
|
115
|
+
WHERE ready.group_position <= capacities.available
|
|
116
|
+
ORDER BY ready.group_position, capacities.last_served NULLS FIRST, ready.group_id
|
|
117
|
+
LIMIT ${capacity}
|
|
118
|
+
)
|
|
119
|
+
SELECT job.* FROM bunqueue_jobs AS job
|
|
120
|
+
JOIN selected ON selected.id = job.id
|
|
121
|
+
WHERE job.namespace = ${ctx.config.namespace}
|
|
122
|
+
AND job.state IN ('waiting', 'prioritized', 'delayed')
|
|
123
|
+
AND job.run_at <= ${now}
|
|
124
|
+
ORDER BY selected.group_position, selected.last_served NULLS FIRST, selected.group_id
|
|
125
|
+
FOR UPDATE OF job SKIP LOCKED
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
128
|
+
export async function selectPostgresClaimCandidates(tx, ctx, input) {
|
|
129
|
+
const { queue, now, capacity, options } = input;
|
|
130
|
+
const ungrouped = await selectUngroupedCandidates(tx, ctx, queue, now, capacity);
|
|
131
|
+
if (ungrouped.length >= capacity)
|
|
132
|
+
return ungrouped;
|
|
133
|
+
const grouped = await selectGroupedCandidates(tx, ctx, {
|
|
134
|
+
queue,
|
|
135
|
+
now,
|
|
136
|
+
capacity: capacity - ungrouped.length,
|
|
137
|
+
options,
|
|
138
|
+
});
|
|
139
|
+
return [...ungrouped, ...grouped];
|
|
140
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GroupPullOptions } from '../../../domain/types/group';
|
|
2
|
+
import { type PostgresContext } from './context';
|
|
3
|
+
import type { ClaimedPostgresJob } from './types';
|
|
4
|
+
export declare function claimPostgresJobs(ctx: PostgresContext, input: {
|
|
5
|
+
queue: string;
|
|
6
|
+
count: number;
|
|
7
|
+
owner: string;
|
|
8
|
+
leaseDurationMs: number;
|
|
9
|
+
groupOptions?: GroupPullOptions;
|
|
10
|
+
}): Promise<ClaimedPostgresJob[]>;
|