bunqueue 2.8.59 → 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 +109 -77
- package/dist/application/background/dlq.js +2 -0
- package/dist/application/contextFactory.js +5 -0
- package/dist/application/dlqManager.d.ts +11 -1
- package/dist/application/dlqManager.js +48 -10
- 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/lockManager.js +1 -1
- 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/ack.js +1 -1
- package/dist/application/queue-manager/delivery.js +1 -1
- 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/application/statsManager.d.ts +1 -1
- package/dist/application/statsManager.js +1 -1
- package/dist/cli/commands/server.js +1 -1
- package/dist/client/events.js +2 -2
- package/dist/client/flow.js +0 -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/operations/add/single.js +1 -1
- package/dist/client/queue/runtime/configuration.d.ts +4 -0
- package/dist/client/queue/runtime/configuration.js +8 -0
- package/dist/client/sandboxed/runtime/state.js +2 -2
- package/dist/client/tcp/connection.d.ts +1 -1
- package/dist/client/tcp/connection.js +1 -1
- package/dist/client/tcp/runtime/state.js +2 -2
- package/dist/client/types/metrics.d.ts +1 -1
- package/dist/client/worker/runtime/control.js +0 -1
- package/dist/client/worker/runtime/execution.js +3 -8
- package/dist/client/worker/runtime/lifecycle.js +0 -1
- package/dist/client/worker/runtime/polling.d.ts +1 -0
- package/dist/client/worker/runtime/polling.js +9 -0
- package/dist/client/worker/runtime/state.js +3 -3
- package/dist/client/workflow/compensationChild.d.ts +2 -0
- package/dist/client/workflow/compensationChild.js +3 -1
- package/dist/client/workflow/compensationPass.d.ts +1 -0
- package/dist/client/workflow/compensationPass.js +18 -2
- package/dist/client/workflow/compensator.d.ts +2 -1
- package/dist/client/workflow/compensator.js +11 -3
- package/dist/client/workflow/engine.js +1 -1
- package/dist/client/workflow/executionFence.d.ts +15 -0
- package/dist/client/workflow/executionFence.js +29 -0
- package/dist/client/workflow/executor.d.ts +5 -11
- package/dist/client/workflow/executor.js +48 -47
- package/dist/client/workflow/executorLifecycle.d.ts +1 -0
- package/dist/client/workflow/executorLifecycle.js +41 -10
- package/dist/client/workflow/executorNodes.d.ts +1 -0
- package/dist/client/workflow/executorNodes.js +45 -16
- package/dist/client/workflow/executorQueue.d.ts +9 -0
- package/dist/client/workflow/executorQueue.js +15 -0
- package/dist/client/workflow/forEachRunner.d.ts +4 -0
- package/dist/client/workflow/forEachRunner.js +70 -0
- package/dist/client/workflow/loops.d.ts +4 -5
- package/dist/client/workflow/loops.js +27 -93
- package/dist/client/workflow/mapRunner.d.ts +1 -1
- package/dist/client/workflow/mapRunner.js +11 -2
- package/dist/client/workflow/recovery.d.ts +1 -0
- package/dist/client/workflow/recovery.js +24 -18
- package/dist/client/workflow/rollbackControl.d.ts +1 -0
- package/dist/client/workflow/rollbackControl.js +5 -2
- package/dist/client/workflow/runner.d.ts +3 -1
- package/dist/client/workflow/runner.js +26 -3
- package/dist/client/workflow/stepTypes.js +1 -1
- package/dist/client/workflow/subWorkflowRunner.d.ts +1 -0
- package/dist/client/workflow/subWorkflowRunner.js +7 -0
- package/dist/client/workflow/waitFor.d.ts +4 -0
- package/dist/client/workflow/waitFor.js +23 -11
- package/dist/client/workflow/workflowDecisions.d.ts +1 -1
- package/dist/client/workflow/workflowDecisions.js +3 -1
- 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/persistence/sqlite.d.ts +1 -1
- 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/jobs.js +0 -3
- package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
- package/dist/infrastructure/server/handler.js +9 -11
- package/dist/infrastructure/server/handlerRoutes.d.ts +2 -2
- package/dist/infrastructure/server/handlerRoutes.js +2 -2
- 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 +20 -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 +85 -74
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { jobId } from '../../../domain/types/job';
|
|
2
|
+
import { decodePostgresJob, encodePostgresValue } from './codec';
|
|
3
|
+
import { databaseNow, recordPostgresEvent } from './context';
|
|
4
|
+
import { lockPostgresDestructionIdentities, partitionPostgresDestructionCandidates, } from './dependencyDestruction';
|
|
5
|
+
import { lockPostgresBrokerSession } from './brokerSessions';
|
|
6
|
+
function isProtectedCron(row) {
|
|
7
|
+
return decodePostgresJob(row).job.uniqueKey?.startsWith('cron:') === true;
|
|
8
|
+
}
|
|
9
|
+
export async function discardPostgresProtectedCronLease(tx, ctx, row, now) {
|
|
10
|
+
if (!isProtectedCron(row))
|
|
11
|
+
return false;
|
|
12
|
+
const id = jobId(row.id);
|
|
13
|
+
const { protectedIds } = await partitionPostgresDestructionCandidates(tx, ctx, [id], [id]);
|
|
14
|
+
if (protectedIds.length > 0)
|
|
15
|
+
return false;
|
|
16
|
+
const job = decodePostgresJob(row).job;
|
|
17
|
+
await tx `
|
|
18
|
+
DELETE FROM bunqueue_job_logs
|
|
19
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${row.id}
|
|
20
|
+
`;
|
|
21
|
+
await tx `
|
|
22
|
+
DELETE FROM bunqueue_flow_failures
|
|
23
|
+
WHERE namespace = ${ctx.config.namespace} AND parent_id = ${row.id}
|
|
24
|
+
`;
|
|
25
|
+
await tx `
|
|
26
|
+
DELETE FROM bunqueue_dependencies
|
|
27
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${row.id}
|
|
28
|
+
`;
|
|
29
|
+
await tx `
|
|
30
|
+
DELETE FROM bunqueue_jobs
|
|
31
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${row.id}
|
|
32
|
+
`;
|
|
33
|
+
await recordPostgresEvent(tx, ctx, {
|
|
34
|
+
queue: job.queue,
|
|
35
|
+
type: 'removed',
|
|
36
|
+
jobId: job.id,
|
|
37
|
+
occurredAt: now,
|
|
38
|
+
job,
|
|
39
|
+
state: 'active',
|
|
40
|
+
removed: true,
|
|
41
|
+
});
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
async function releaseLeaseRow(tx, ctx, row, now) {
|
|
45
|
+
if (await discardPostgresProtectedCronLease(tx, ctx, row, now))
|
|
46
|
+
return;
|
|
47
|
+
const job = decodePostgresJob(row).job;
|
|
48
|
+
job.startedAt = null;
|
|
49
|
+
job.runAt = Math.min(job.runAt, now);
|
|
50
|
+
const state = job.priority > 0 ? 'prioritized' : 'waiting';
|
|
51
|
+
await tx `
|
|
52
|
+
UPDATE bunqueue_jobs
|
|
53
|
+
SET payload = ${encodePostgresValue(job)}, state = ${state}, run_at = ${job.runAt},
|
|
54
|
+
started_at = NULL, lease_owner = NULL, lease_token = NULL, lease_until = NULL,
|
|
55
|
+
lease_broker_id = NULL, lease_broker_session_id = NULL,
|
|
56
|
+
lease_renewals = 0, version = version + 1
|
|
57
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${row.id}
|
|
58
|
+
`;
|
|
59
|
+
await recordPostgresEvent(tx, ctx, {
|
|
60
|
+
queue: job.queue,
|
|
61
|
+
type: 'retried',
|
|
62
|
+
jobId: job.id,
|
|
63
|
+
occurredAt: now,
|
|
64
|
+
job,
|
|
65
|
+
state,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Release a never-renewed connection lease without racing pooled heartbeats. */
|
|
69
|
+
export async function releasePostgresClientLease(ctx, id, token) {
|
|
70
|
+
return await ctx.sql.begin(async (tx) => {
|
|
71
|
+
await lockPostgresBrokerSession(tx, ctx);
|
|
72
|
+
await lockPostgresDestructionIdentities(tx, ctx, [id]);
|
|
73
|
+
const now = await databaseNow(tx);
|
|
74
|
+
const rows = await tx `
|
|
75
|
+
SELECT * FROM bunqueue_jobs
|
|
76
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
77
|
+
FOR UPDATE
|
|
78
|
+
`;
|
|
79
|
+
const row = rows[0];
|
|
80
|
+
if (row?.state !== 'active' ||
|
|
81
|
+
row.lease_token !== token ||
|
|
82
|
+
Number(row.lease_until ?? 0) <= now ||
|
|
83
|
+
row.lease_renewals > 0) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
await releaseLeaseRow(tx, ctx, row, now);
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** Return or discard every active lease currently coordinated by this broker. */
|
|
91
|
+
export async function releasePostgresBrokerLeases(ctx) {
|
|
92
|
+
return await ctx.sql.begin(async (tx) => {
|
|
93
|
+
const now = await databaseNow(tx);
|
|
94
|
+
const candidates = await tx `
|
|
95
|
+
SELECT id FROM bunqueue_jobs
|
|
96
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
97
|
+
AND state = 'active'
|
|
98
|
+
AND lease_broker_id = ${ctx.config.brokerId}
|
|
99
|
+
AND lease_broker_session_id = ${ctx.config.brokerSessionId}
|
|
100
|
+
ORDER BY id
|
|
101
|
+
`;
|
|
102
|
+
const ids = candidates.map((row) => jobId(row.id));
|
|
103
|
+
if (ids.length === 0)
|
|
104
|
+
return 0;
|
|
105
|
+
await lockPostgresDestructionIdentities(tx, ctx, ids);
|
|
106
|
+
const rows = await tx `
|
|
107
|
+
SELECT * FROM bunqueue_jobs
|
|
108
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
109
|
+
AND state = 'active'
|
|
110
|
+
AND lease_broker_id = ${ctx.config.brokerId}
|
|
111
|
+
AND lease_broker_session_id = ${ctx.config.brokerSessionId}
|
|
112
|
+
AND id = ANY(${tx.array(ids.map(String), 'TEXT')})
|
|
113
|
+
ORDER BY id
|
|
114
|
+
FOR UPDATE
|
|
115
|
+
`;
|
|
116
|
+
for (const row of rows)
|
|
117
|
+
await releaseLeaseRow(tx, ctx, row, now);
|
|
118
|
+
return rows.length;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { JobId } from '../../../domain/types/job';
|
|
2
|
+
import { type PostgresContext } from './context';
|
|
3
|
+
/** Renew one fenced lease and transfer its broker responsibility atomically. */
|
|
4
|
+
export declare function renewPostgresLease(ctx: PostgresContext, id: JobId, token: string, durationMs: number): Promise<number | null>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { decodePostgresJob, encodePostgresValue } from './codec';
|
|
2
|
+
import { databaseNow } from './context';
|
|
3
|
+
import { lockPostgresBrokerSession } from './brokerSessions';
|
|
4
|
+
/** Renew one fenced lease and transfer its broker responsibility atomically. */
|
|
5
|
+
export async function renewPostgresLease(ctx, id, token, durationMs) {
|
|
6
|
+
return await ctx.sql.begin(async (tx) => {
|
|
7
|
+
await lockPostgresBrokerSession(tx, ctx);
|
|
8
|
+
const now = await databaseNow(tx);
|
|
9
|
+
const rows = await tx `
|
|
10
|
+
SELECT * FROM bunqueue_jobs
|
|
11
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
12
|
+
AND state = 'active' AND lease_token = ${token} AND lease_until > ${now}
|
|
13
|
+
FOR UPDATE
|
|
14
|
+
`;
|
|
15
|
+
const row = rows[0];
|
|
16
|
+
if (!row)
|
|
17
|
+
return null;
|
|
18
|
+
const job = decodePostgresJob(row).job;
|
|
19
|
+
job.lastHeartbeat = now;
|
|
20
|
+
const requestedLeaseUntil = now + Math.max(1, durationMs);
|
|
21
|
+
const leaseUntil = row.timeout === null
|
|
22
|
+
? requestedLeaseUntil
|
|
23
|
+
: Math.min(requestedLeaseUntil, Number(row.started_at ?? now) + Number(row.timeout));
|
|
24
|
+
await tx `
|
|
25
|
+
UPDATE bunqueue_jobs
|
|
26
|
+
SET payload = ${encodePostgresValue(job)}, lease_until = ${leaseUntil},
|
|
27
|
+
lease_broker_id = ${ctx.config.brokerId},
|
|
28
|
+
lease_broker_session_id = ${ctx.config.brokerSessionId},
|
|
29
|
+
lease_renewals = lease_renewals + 1,
|
|
30
|
+
version = version + 1
|
|
31
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
32
|
+
`;
|
|
33
|
+
return leaseUntil;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PostgresContext, PostgresReadSql } from './context';
|
|
2
|
+
export interface PostgresQueueLifetimeTotals {
|
|
3
|
+
readonly queue: string;
|
|
4
|
+
readonly totalCompleted: bigint;
|
|
5
|
+
readonly totalFailed: bigint;
|
|
6
|
+
}
|
|
7
|
+
export interface PostgresLifetimeMetricsSnapshot {
|
|
8
|
+
readonly baselineEventId: number;
|
|
9
|
+
readonly baselineCommitSeq: number;
|
|
10
|
+
readonly totalCompleted: bigint;
|
|
11
|
+
readonly totalFailed: bigint;
|
|
12
|
+
readonly queues: readonly PostgresQueueLifetimeTotals[];
|
|
13
|
+
}
|
|
14
|
+
/** Load terminal lifetime counters and their matching event boundary atomically. */
|
|
15
|
+
export declare function loadPostgresLifetimeMetrics(ctx: PostgresContext, sql?: PostgresReadSql): Promise<PostgresLifetimeMetricsSnapshot>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Load terminal lifetime counters and their matching event boundary atomically. */
|
|
2
|
+
export async function loadPostgresLifetimeMetrics(ctx, sql) {
|
|
3
|
+
const load = async (readSql) => {
|
|
4
|
+
const [[baseline], rows] = await Promise.all([
|
|
5
|
+
readSql `
|
|
6
|
+
SELECT
|
|
7
|
+
(SELECT MAX(id) FROM bunqueue_events
|
|
8
|
+
WHERE namespace = ${ctx.config.namespace}) AS event_id,
|
|
9
|
+
(SELECT MAX(commit_seq) FROM bunqueue_event_commits
|
|
10
|
+
WHERE namespace = ${ctx.config.namespace}) AS commit_seq
|
|
11
|
+
`,
|
|
12
|
+
readSql `
|
|
13
|
+
SELECT queue, metric_type, total_count
|
|
14
|
+
FROM bunqueue_metric_totals
|
|
15
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
16
|
+
ORDER BY queue, metric_type
|
|
17
|
+
`,
|
|
18
|
+
]);
|
|
19
|
+
const queues = new Map();
|
|
20
|
+
let totalCompleted = 0n;
|
|
21
|
+
let totalFailed = 0n;
|
|
22
|
+
for (const row of rows) {
|
|
23
|
+
const count = BigInt(row.total_count);
|
|
24
|
+
const totals = queues.get(row.queue) ?? { totalCompleted: 0n, totalFailed: 0n };
|
|
25
|
+
if (row.metric_type === 'completed') {
|
|
26
|
+
totals.totalCompleted = count;
|
|
27
|
+
totalCompleted += count;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
totals.totalFailed = count;
|
|
31
|
+
totalFailed += count;
|
|
32
|
+
}
|
|
33
|
+
queues.set(row.queue, totals);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
baselineEventId: Number(baseline.event_id ?? 0),
|
|
37
|
+
baselineCommitSeq: Number(baseline.commit_seq ?? 0),
|
|
38
|
+
totalCompleted,
|
|
39
|
+
totalFailed,
|
|
40
|
+
queues: [...queues].map(([queue, totals]) => ({ queue, ...totals })),
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
if (sql)
|
|
44
|
+
return await load(sql);
|
|
45
|
+
return await ctx.sql.begin('isolation level repeatable read read only', load);
|
|
46
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PostgresContext } from './context';
|
|
2
|
+
import { type PostgresLifetimeMetricsSnapshot } from './lifetimeMetrics';
|
|
3
|
+
/** Fence terminal counters against the same commit sequencer used by journal writers. */
|
|
4
|
+
export declare function finalizePostgresLifetimeMetrics(ctx: PostgresContext, apply: (snapshot: PostgresLifetimeMetricsSnapshot) => void): Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { postgresAdvisoryLockName } from './advisoryLocks';
|
|
2
|
+
import { loadPostgresLifetimeMetrics, } from './lifetimeMetrics';
|
|
3
|
+
/** Fence terminal counters against the same commit sequencer used by journal writers. */
|
|
4
|
+
export async function finalizePostgresLifetimeMetrics(ctx, apply) {
|
|
5
|
+
const lockName = postgresAdvisoryLockName('event-commit', ctx.config.namespace);
|
|
6
|
+
await ctx.sql.begin(async (tx) => {
|
|
7
|
+
await tx `
|
|
8
|
+
SELECT pg_advisory_xact_lock(hashtextextended(${lockName}, 0))
|
|
9
|
+
`;
|
|
10
|
+
const snapshot = await loadPostgresLifetimeMetrics(ctx, tx);
|
|
11
|
+
apply(snapshot);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { JobId } from '../../../domain/types/job';
|
|
2
|
+
import type { JobLogEntry } from '../../../domain/types/worker';
|
|
3
|
+
import { type PostgresContext } from './context';
|
|
4
|
+
export declare function addPostgresJobLog(ctx: PostgresContext, id: JobId, message: string, level: JobLogEntry['level'], maxLogs?: number): Promise<boolean>;
|
|
5
|
+
export declare function getPostgresJobLogs(ctx: PostgresContext, id: JobId): Promise<JobLogEntry[]>;
|
|
6
|
+
export declare function clearPostgresJobLogs(ctx: PostgresContext, id: JobId, keepLogs?: number): Promise<void>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { databaseNow } from './context';
|
|
2
|
+
export async function addPostgresJobLog(ctx, id, message, level, maxLogs = 100) {
|
|
3
|
+
return await ctx.sql.begin(async (tx) => {
|
|
4
|
+
const jobs = await tx `
|
|
5
|
+
SELECT id FROM bunqueue_jobs
|
|
6
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
7
|
+
FOR UPDATE
|
|
8
|
+
`;
|
|
9
|
+
if (!jobs[0])
|
|
10
|
+
return false;
|
|
11
|
+
const now = await databaseNow(tx);
|
|
12
|
+
const rows = await tx `
|
|
13
|
+
INSERT INTO bunqueue_job_logs (namespace, job_id, timestamp, level, message)
|
|
14
|
+
VALUES (${ctx.config.namespace}, ${String(id)}, ${now}, ${level}, ${message})
|
|
15
|
+
RETURNING id
|
|
16
|
+
`;
|
|
17
|
+
if (!rows[0])
|
|
18
|
+
return false;
|
|
19
|
+
await tx `
|
|
20
|
+
DELETE FROM bunqueue_job_logs
|
|
21
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${String(id)}
|
|
22
|
+
AND id NOT IN (
|
|
23
|
+
SELECT id FROM bunqueue_job_logs
|
|
24
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${String(id)}
|
|
25
|
+
ORDER BY id DESC
|
|
26
|
+
LIMIT ${Math.max(1, maxLogs)}
|
|
27
|
+
)
|
|
28
|
+
`;
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export async function getPostgresJobLogs(ctx, id) {
|
|
33
|
+
const rows = await ctx.sql `
|
|
34
|
+
SELECT timestamp, level, message
|
|
35
|
+
FROM bunqueue_job_logs
|
|
36
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${String(id)}
|
|
37
|
+
ORDER BY id
|
|
38
|
+
`;
|
|
39
|
+
return rows.map((row) => ({
|
|
40
|
+
timestamp: Number(row.timestamp),
|
|
41
|
+
level: row.level,
|
|
42
|
+
message: row.message,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
export async function clearPostgresJobLogs(ctx, id, keepLogs) {
|
|
46
|
+
await ctx.sql.begin(async (tx) => {
|
|
47
|
+
await tx `
|
|
48
|
+
SELECT id FROM bunqueue_jobs
|
|
49
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ${String(id)}
|
|
50
|
+
FOR UPDATE
|
|
51
|
+
`;
|
|
52
|
+
if (keepLogs === undefined || keepLogs <= 0) {
|
|
53
|
+
await tx `
|
|
54
|
+
DELETE FROM bunqueue_job_logs
|
|
55
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${String(id)}
|
|
56
|
+
`;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await tx `
|
|
60
|
+
DELETE FROM bunqueue_job_logs
|
|
61
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${String(id)}
|
|
62
|
+
AND id NOT IN (
|
|
63
|
+
SELECT id FROM bunqueue_job_logs
|
|
64
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ${String(id)}
|
|
65
|
+
ORDER BY id DESC
|
|
66
|
+
LIMIT ${Math.floor(keepLogs)}
|
|
67
|
+
)
|
|
68
|
+
`;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type JobId } from '../../../domain/types/job';
|
|
2
|
+
import { type PostgresContext } from './context';
|
|
3
|
+
/** Remove old jobs with the same state grouping used by Queue.clean(). */
|
|
4
|
+
export declare function cleanPostgresQueue(ctx: PostgresContext, queue: string, graceMs: number, state?: string, limit?: number): Promise<JobId[]>;
|
|
5
|
+
/** Permanently remove every failed job currently visible in one queue. */
|
|
6
|
+
export declare function purgePostgresDlq(ctx: PostgresContext, queue: string): Promise<JobId[]>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { jobId } from '../../../domain/types/job';
|
|
2
|
+
import { decodePostgresJob } from './codec';
|
|
3
|
+
import { databaseNow, recordPostgresEvent } from './context';
|
|
4
|
+
import { lockPostgresDestructionCandidates, partitionPostgresDestructionCandidates, } from './dependencyDestruction';
|
|
5
|
+
async function prepareDeletableRows(tx, ctx, candidateIds, load) {
|
|
6
|
+
if (candidateIds.length === 0)
|
|
7
|
+
return [];
|
|
8
|
+
await lockPostgresDestructionCandidates(tx, ctx, candidateIds);
|
|
9
|
+
const rows = await load(candidateIds);
|
|
10
|
+
const plannedIds = rows.map((row) => jobId(row.id));
|
|
11
|
+
const { deletableIds } = await partitionPostgresDestructionCandidates(tx, ctx, plannedIds, plannedIds);
|
|
12
|
+
const deletable = new Set(deletableIds.map(String));
|
|
13
|
+
return rows.filter((row) => deletable.has(row.id));
|
|
14
|
+
}
|
|
15
|
+
async function deleteRows(tx, ctx, rows) {
|
|
16
|
+
if (rows.length === 0)
|
|
17
|
+
return [];
|
|
18
|
+
const ids = rows.map((row) => row.id);
|
|
19
|
+
await tx `
|
|
20
|
+
DELETE FROM bunqueue_job_logs
|
|
21
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ANY(${tx.array(ids, 'TEXT')})
|
|
22
|
+
`;
|
|
23
|
+
await tx `
|
|
24
|
+
DELETE FROM bunqueue_flow_failures
|
|
25
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
26
|
+
AND parent_id = ANY(${tx.array(ids, 'TEXT')})
|
|
27
|
+
`;
|
|
28
|
+
await tx `
|
|
29
|
+
DELETE FROM bunqueue_dependencies
|
|
30
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ANY(${tx.array(ids, 'TEXT')})
|
|
31
|
+
`;
|
|
32
|
+
await tx `
|
|
33
|
+
DELETE FROM bunqueue_completions
|
|
34
|
+
WHERE namespace = ${ctx.config.namespace} AND job_id = ANY(${tx.array(ids, 'TEXT')})
|
|
35
|
+
`;
|
|
36
|
+
await tx `
|
|
37
|
+
DELETE FROM bunqueue_jobs
|
|
38
|
+
WHERE namespace = ${ctx.config.namespace} AND id = ANY(${tx.array(ids, 'TEXT')})
|
|
39
|
+
`;
|
|
40
|
+
const now = await databaseNow(tx);
|
|
41
|
+
for (const row of rows) {
|
|
42
|
+
const job = decodePostgresJob(row).job;
|
|
43
|
+
await recordPostgresEvent(tx, ctx, {
|
|
44
|
+
queue: job.queue,
|
|
45
|
+
type: 'removed',
|
|
46
|
+
jobId: job.id,
|
|
47
|
+
occurredAt: now,
|
|
48
|
+
job,
|
|
49
|
+
state: row.state,
|
|
50
|
+
removed: true,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return ids.map(jobId);
|
|
54
|
+
}
|
|
55
|
+
/** Remove old jobs with the same state grouping used by Queue.clean(). */
|
|
56
|
+
export async function cleanPostgresQueue(ctx, queue, graceMs, state, limit = 1000) {
|
|
57
|
+
const cap = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 0;
|
|
58
|
+
if (cap === 0 || state === 'active')
|
|
59
|
+
return [];
|
|
60
|
+
return await ctx.sql.begin(async (tx) => {
|
|
61
|
+
const threshold = (await databaseNow(tx)) - graceMs;
|
|
62
|
+
let rows;
|
|
63
|
+
if (state === 'completed') {
|
|
64
|
+
const candidates = await tx `
|
|
65
|
+
SELECT id FROM bunqueue_jobs
|
|
66
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
67
|
+
AND state = 'completed' AND COALESCE(completed_at, created_at) <= ${threshold}
|
|
68
|
+
ORDER BY COALESCE(completed_at, created_at), id
|
|
69
|
+
LIMIT ${cap}
|
|
70
|
+
`;
|
|
71
|
+
rows = await prepareDeletableRows(tx, ctx, candidates.map((row) => jobId(row.id)), (ids) => tx `
|
|
72
|
+
SELECT * FROM bunqueue_jobs
|
|
73
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
74
|
+
AND state = 'completed' AND COALESCE(completed_at, created_at) <= ${threshold}
|
|
75
|
+
AND id = ANY(${tx.array(ids.map(String), 'TEXT')})
|
|
76
|
+
ORDER BY COALESCE(completed_at, created_at), id
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
79
|
+
else if (state === 'failed') {
|
|
80
|
+
const candidates = await tx `
|
|
81
|
+
SELECT id FROM bunqueue_jobs
|
|
82
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
83
|
+
AND state = 'failed' AND COALESCE(completed_at, created_at) <= ${threshold}
|
|
84
|
+
ORDER BY COALESCE(completed_at, created_at), id
|
|
85
|
+
LIMIT ${cap}
|
|
86
|
+
`;
|
|
87
|
+
rows = await prepareDeletableRows(tx, ctx, candidates.map((row) => jobId(row.id)), (ids) => tx `
|
|
88
|
+
SELECT * FROM bunqueue_jobs
|
|
89
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
90
|
+
AND state = 'failed' AND COALESCE(completed_at, created_at) <= ${threshold}
|
|
91
|
+
AND id = ANY(${tx.array(ids.map(String), 'TEXT')})
|
|
92
|
+
ORDER BY COALESCE(completed_at, created_at), id
|
|
93
|
+
`);
|
|
94
|
+
}
|
|
95
|
+
else if (state === undefined ||
|
|
96
|
+
state === 'wait' ||
|
|
97
|
+
state === 'waiting' ||
|
|
98
|
+
state === 'delayed' ||
|
|
99
|
+
state === 'prioritized' ||
|
|
100
|
+
state === 'paused') {
|
|
101
|
+
const candidates = await tx `
|
|
102
|
+
SELECT id FROM bunqueue_jobs
|
|
103
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
104
|
+
AND state IN ('waiting', 'prioritized', 'delayed') AND created_at <= ${threshold}
|
|
105
|
+
ORDER BY created_at, id
|
|
106
|
+
LIMIT ${cap}
|
|
107
|
+
`;
|
|
108
|
+
rows = await prepareDeletableRows(tx, ctx, candidates.map((row) => jobId(row.id)), (ids) => tx `
|
|
109
|
+
SELECT * FROM bunqueue_jobs
|
|
110
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
111
|
+
AND state IN ('waiting', 'prioritized', 'delayed') AND created_at <= ${threshold}
|
|
112
|
+
AND id = ANY(${tx.array(ids.map(String), 'TEXT')})
|
|
113
|
+
ORDER BY created_at, id
|
|
114
|
+
`);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
return await deleteRows(tx, ctx, rows);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/** Permanently remove every failed job currently visible in one queue. */
|
|
123
|
+
export async function purgePostgresDlq(ctx, queue) {
|
|
124
|
+
return await ctx.sql.begin(async (tx) => {
|
|
125
|
+
const candidates = await tx `
|
|
126
|
+
SELECT id FROM bunqueue_jobs
|
|
127
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND state = 'failed'
|
|
128
|
+
ORDER BY completed_at, id
|
|
129
|
+
`;
|
|
130
|
+
const rows = await prepareDeletableRows(tx, ctx, candidates.map((row) => jobId(row.id)), (ids) => tx `
|
|
131
|
+
SELECT * FROM bunqueue_jobs
|
|
132
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND state = 'failed'
|
|
133
|
+
AND id = ANY(${tx.array(ids.map(String), 'TEXT')})
|
|
134
|
+
ORDER BY completed_at, id
|
|
135
|
+
`);
|
|
136
|
+
return await deleteRows(tx, ctx, rows);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type MaintenanceReporter = (subsystem: string, error: unknown) => void;
|
|
2
|
+
/** Serialize maintenance per subsystem and drain every admitted execution on close. */
|
|
3
|
+
export declare class PostgresMaintenanceFlights {
|
|
4
|
+
private readonly report;
|
|
5
|
+
private readonly flights;
|
|
6
|
+
private accepting;
|
|
7
|
+
constructor(report: MaintenanceReporter);
|
|
8
|
+
run(subsystem: string, operation: () => Promise<unknown>): Promise<void>;
|
|
9
|
+
close(): void;
|
|
10
|
+
drain(): Promise<void>;
|
|
11
|
+
private replacePending;
|
|
12
|
+
private runLoop;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Serialize maintenance per subsystem and drain every admitted execution on close. */
|
|
2
|
+
export class PostgresMaintenanceFlights {
|
|
3
|
+
report;
|
|
4
|
+
flights = new Map();
|
|
5
|
+
accepting = true;
|
|
6
|
+
constructor(report) {
|
|
7
|
+
this.report = report;
|
|
8
|
+
}
|
|
9
|
+
run(subsystem, operation) {
|
|
10
|
+
if (!this.accepting)
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
const existing = this.flights.get(subsystem);
|
|
13
|
+
if (existing)
|
|
14
|
+
return this.replacePending(existing, operation);
|
|
15
|
+
let resolve;
|
|
16
|
+
const result = new Promise((accept) => {
|
|
17
|
+
resolve = accept;
|
|
18
|
+
});
|
|
19
|
+
const first = { operation, waiters: [resolve] };
|
|
20
|
+
const flight = {};
|
|
21
|
+
flight.pending = first;
|
|
22
|
+
flight.loop = this.runLoop(subsystem, flight);
|
|
23
|
+
this.flights.set(subsystem, flight);
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
close() {
|
|
27
|
+
this.accepting = false;
|
|
28
|
+
}
|
|
29
|
+
async drain() {
|
|
30
|
+
await Promise.all([...this.flights.values()].map(({ loop }) => loop));
|
|
31
|
+
}
|
|
32
|
+
replacePending(flight, operation) {
|
|
33
|
+
let resolve;
|
|
34
|
+
const result = new Promise((accept) => {
|
|
35
|
+
resolve = accept;
|
|
36
|
+
});
|
|
37
|
+
if (flight.pending) {
|
|
38
|
+
flight.pending.operation = operation;
|
|
39
|
+
flight.pending.waiters.push(resolve);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
flight.pending = { operation, waiters: [resolve] };
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
async runLoop(subsystem, flight) {
|
|
47
|
+
while (flight.pending) {
|
|
48
|
+
const current = flight.pending;
|
|
49
|
+
flight.pending = null;
|
|
50
|
+
try {
|
|
51
|
+
await current.operation();
|
|
52
|
+
this.report(subsystem, null);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
this.report(subsystem, error);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
for (const resolve of current.waiters)
|
|
59
|
+
resolve();
|
|
60
|
+
}
|
|
61
|
+
await Promise.resolve();
|
|
62
|
+
}
|
|
63
|
+
if (this.flights.get(subsystem) === flight)
|
|
64
|
+
this.flights.delete(subsystem);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TransactionSQL } from 'bun';
|
|
2
|
+
import type { QueueMetricType } from '../../../domain/types/metrics';
|
|
3
|
+
import type { PostgresContext } from './context';
|
|
4
|
+
export interface PostgresMetricAddition {
|
|
5
|
+
readonly queue: string;
|
|
6
|
+
readonly count: number;
|
|
7
|
+
}
|
|
8
|
+
/** Update exact durable metrics while minimizing time spent holding shared hot rows. */
|
|
9
|
+
export declare function recordPostgresMetricAdditions(tx: TransactionSQL, ctx: PostgresContext, type: QueueMetricType, inputs: readonly PostgresMetricAddition[], now: number): Promise<void>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
function normalizeAdditions(inputs) {
|
|
2
|
+
const counts = new Map();
|
|
3
|
+
for (const input of inputs) {
|
|
4
|
+
if (input.count <= 0)
|
|
5
|
+
continue;
|
|
6
|
+
counts.set(input.queue, (counts.get(input.queue) ?? 0) + input.count);
|
|
7
|
+
}
|
|
8
|
+
return [...counts]
|
|
9
|
+
.map(([queue, count]) => ({ queue, count }))
|
|
10
|
+
.sort((left, right) => left.queue.localeCompare(right.queue));
|
|
11
|
+
}
|
|
12
|
+
async function pruneMetricBuckets(tx, ctx, queues, type, minute) {
|
|
13
|
+
const metricLimit = ctx.config.maxMetricDataPoints;
|
|
14
|
+
await tx `
|
|
15
|
+
DELETE FROM bunqueue_metric_buckets
|
|
16
|
+
WHERE namespace = ${ctx.config.namespace}
|
|
17
|
+
AND queue = ANY(${tx.array([...queues], 'TEXT')})
|
|
18
|
+
AND metric_type = ${type}
|
|
19
|
+
${metricLimit === 0 ? tx `` : tx `AND minute < ${minute - metricLimit + 1}`}
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
async function updateTotalsWithoutBuckets(tx, ctx, type, additions, now) {
|
|
23
|
+
await tx `
|
|
24
|
+
INSERT INTO bunqueue_metric_totals
|
|
25
|
+
(namespace, queue, metric_type, total_count, prev_ts, prev_count)
|
|
26
|
+
SELECT ${ctx.config.namespace}, batch.queue, ${type}, batch.addition, ${now},
|
|
27
|
+
batch.addition
|
|
28
|
+
FROM unnest(
|
|
29
|
+
${tx.array(additions.map(({ queue }) => queue), 'TEXT')},
|
|
30
|
+
${tx.array(additions.map(({ count }) => count), 'INTEGER')}
|
|
31
|
+
) AS batch(queue, addition)
|
|
32
|
+
ORDER BY batch.queue
|
|
33
|
+
ON CONFLICT (namespace, queue, metric_type) DO UPDATE SET
|
|
34
|
+
total_count = bunqueue_metric_totals.total_count + excluded.total_count,
|
|
35
|
+
prev_ts = GREATEST(bunqueue_metric_totals.prev_ts, excluded.prev_ts),
|
|
36
|
+
prev_count = CASE
|
|
37
|
+
WHEN excluded.prev_ts / 60000 > bunqueue_metric_totals.prev_ts / 60000
|
|
38
|
+
THEN excluded.prev_count
|
|
39
|
+
WHEN excluded.prev_ts / 60000 = bunqueue_metric_totals.prev_ts / 60000
|
|
40
|
+
THEN bunqueue_metric_totals.prev_count + excluded.prev_count
|
|
41
|
+
ELSE bunqueue_metric_totals.prev_count
|
|
42
|
+
END
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
async function updateBucketsAndTotals(tx, ctx, type, additions, now) {
|
|
46
|
+
const minute = Math.floor(now / 60_000);
|
|
47
|
+
await tx `
|
|
48
|
+
WITH incoming AS MATERIALIZED (
|
|
49
|
+
SELECT batch.queue, batch.addition
|
|
50
|
+
FROM unnest(
|
|
51
|
+
${tx.array(additions.map(({ queue }) => queue), 'TEXT')},
|
|
52
|
+
${tx.array(additions.map(({ count }) => count), 'INTEGER')}
|
|
53
|
+
) AS batch(queue, addition)
|
|
54
|
+
ORDER BY batch.queue
|
|
55
|
+
), buckets AS (
|
|
56
|
+
INSERT INTO bunqueue_metric_buckets
|
|
57
|
+
(namespace, queue, metric_type, minute, count)
|
|
58
|
+
SELECT ${ctx.config.namespace}, incoming.queue, ${type}, ${minute}, incoming.addition
|
|
59
|
+
FROM incoming
|
|
60
|
+
ORDER BY incoming.queue
|
|
61
|
+
ON CONFLICT (namespace, queue, metric_type, minute)
|
|
62
|
+
DO UPDATE SET count = bunqueue_metric_buckets.count + excluded.count
|
|
63
|
+
RETURNING queue, count
|
|
64
|
+
)
|
|
65
|
+
INSERT INTO bunqueue_metric_totals
|
|
66
|
+
(namespace, queue, metric_type, total_count, prev_ts, prev_count)
|
|
67
|
+
SELECT ${ctx.config.namespace}, incoming.queue, ${type}, incoming.addition, ${now},
|
|
68
|
+
buckets.count
|
|
69
|
+
FROM incoming
|
|
70
|
+
JOIN buckets USING (queue)
|
|
71
|
+
ORDER BY incoming.queue
|
|
72
|
+
ON CONFLICT (namespace, queue, metric_type) DO UPDATE SET
|
|
73
|
+
total_count = bunqueue_metric_totals.total_count + excluded.total_count,
|
|
74
|
+
prev_ts = GREATEST(bunqueue_metric_totals.prev_ts, excluded.prev_ts),
|
|
75
|
+
prev_count = CASE
|
|
76
|
+
WHEN excluded.prev_ts / 60000 >= bunqueue_metric_totals.prev_ts / 60000
|
|
77
|
+
THEN excluded.prev_count
|
|
78
|
+
ELSE bunqueue_metric_totals.prev_count
|
|
79
|
+
END
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
/** Update exact durable metrics while minimizing time spent holding shared hot rows. */
|
|
83
|
+
export async function recordPostgresMetricAdditions(tx, ctx, type, inputs, now) {
|
|
84
|
+
const additions = normalizeAdditions(inputs);
|
|
85
|
+
if (additions.length === 0)
|
|
86
|
+
return;
|
|
87
|
+
const minute = Math.floor(now / 60_000);
|
|
88
|
+
await pruneMetricBuckets(tx, ctx, additions.map(({ queue }) => queue), type, minute);
|
|
89
|
+
if (ctx.config.maxMetricDataPoints === 0) {
|
|
90
|
+
await updateTotalsWithoutBuckets(tx, ctx, type, additions, now);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await updateBucketsAndTotals(tx, ctx, type, additions, now);
|
|
94
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type JobId } from '../../../domain/types/job';
|
|
2
|
+
import { type PostgresContext } from './context';
|
|
3
|
+
export declare function updatePostgresJobData(ctx: PostgresContext, id: JobId, data: unknown): Promise<boolean>;
|
|
4
|
+
export declare function updatePostgresProgress(ctx: PostgresContext, id: JobId, progress: number, message?: string): Promise<boolean>;
|
|
5
|
+
export declare function changePostgresPriority(ctx: PostgresContext, id: JobId, priority: number, lifo?: boolean): Promise<boolean>;
|
|
6
|
+
export declare function changePostgresDelay(ctx: PostgresContext, id: JobId, runAt: number, token?: string): Promise<boolean>;
|
|
7
|
+
export declare function promotePostgresJob(ctx: PostgresContext, id: JobId): Promise<boolean>;
|
|
8
|
+
export declare function movePostgresJobToWaitingChildren(ctx: PostgresContext, id: JobId, token?: string): Promise<boolean>;
|
|
9
|
+
export declare function clearPostgresJobUniqueKey(ctx: PostgresContext, id: JobId): Promise<boolean>;
|