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,95 @@
|
|
|
1
|
+
/** Assign durable rotation positions to every pending group lacking one. */
|
|
2
|
+
export async function ensurePostgresGroupStates(tx, ctx, queue, sequence) {
|
|
3
|
+
const assigned = await tx `
|
|
4
|
+
WITH unsequenced AS (
|
|
5
|
+
SELECT job.group_id, MIN(job.group_order) AS first_order
|
|
6
|
+
FROM bunqueue_jobs AS job
|
|
7
|
+
LEFT JOIN bunqueue_group_state AS groups
|
|
8
|
+
ON groups.namespace = job.namespace AND groups.queue = job.queue
|
|
9
|
+
AND groups.group_id = job.group_id
|
|
10
|
+
WHERE job.namespace = ${ctx.config.namespace} AND job.queue = ${queue}
|
|
11
|
+
AND job.group_id IS NOT NULL AND groups.last_served IS NULL
|
|
12
|
+
AND job.state IN ('waiting', 'prioritized', 'delayed')
|
|
13
|
+
GROUP BY job.group_id
|
|
14
|
+
), ordered AS (
|
|
15
|
+
SELECT group_id,
|
|
16
|
+
row_number() OVER (ORDER BY first_order, group_id) AS position
|
|
17
|
+
FROM unsequenced
|
|
18
|
+
)
|
|
19
|
+
INSERT INTO bunqueue_group_state (namespace, queue, group_id, last_served)
|
|
20
|
+
SELECT ${ctx.config.namespace}, ${queue}, group_id, ${sequence} + position
|
|
21
|
+
FROM ordered
|
|
22
|
+
ON CONFLICT (namespace, queue, group_id) DO UPDATE
|
|
23
|
+
SET last_served = EXCLUDED.last_served
|
|
24
|
+
WHERE bunqueue_group_state.last_served IS NULL
|
|
25
|
+
RETURNING group_id
|
|
26
|
+
`;
|
|
27
|
+
if (assigned.length === 0)
|
|
28
|
+
return sequence;
|
|
29
|
+
const next = sequence + assigned.length;
|
|
30
|
+
await tx `
|
|
31
|
+
UPDATE bunqueue_queue_state SET group_sequence = ${next}
|
|
32
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
33
|
+
`;
|
|
34
|
+
return next;
|
|
35
|
+
}
|
|
36
|
+
/** Reset fixed windows when they expire or a worker changes the effective defaults. */
|
|
37
|
+
export async function refreshPostgresGroupRateWindows(tx, ctx, queue, options, now) {
|
|
38
|
+
if (!options?.limit)
|
|
39
|
+
return;
|
|
40
|
+
const { max, duration } = options.limit;
|
|
41
|
+
await tx `
|
|
42
|
+
UPDATE bunqueue_group_state AS groups
|
|
43
|
+
SET rate_window_started_at = ${now}, rate_count = 0,
|
|
44
|
+
rate_effective_max = COALESCE(groups.rate_limit, ${max}),
|
|
45
|
+
rate_effective_duration_ms = COALESCE(groups.rate_duration_ms, ${duration})
|
|
46
|
+
WHERE groups.namespace = ${ctx.config.namespace} AND groups.queue = ${queue}
|
|
47
|
+
AND EXISTS (
|
|
48
|
+
SELECT 1 FROM bunqueue_jobs AS job
|
|
49
|
+
WHERE job.namespace = groups.namespace AND job.queue = groups.queue
|
|
50
|
+
AND job.group_id = groups.group_id
|
|
51
|
+
AND job.state IN ('waiting', 'prioritized', 'delayed')
|
|
52
|
+
)
|
|
53
|
+
AND (
|
|
54
|
+
groups.rate_window_started_at IS NULL
|
|
55
|
+
OR groups.rate_effective_max IS DISTINCT FROM COALESCE(groups.rate_limit, ${max})
|
|
56
|
+
OR groups.rate_effective_duration_ms IS DISTINCT FROM
|
|
57
|
+
COALESCE(groups.rate_duration_ms, ${duration})
|
|
58
|
+
OR groups.rate_window_started_at + COALESCE(groups.rate_duration_ms, ${duration}) <= ${now}
|
|
59
|
+
)
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
/** Consume group budgets and move the durable round-robin cursor after row locks are secured. */
|
|
63
|
+
export async function commitPostgresGroupClaims(tx, ctx, input) {
|
|
64
|
+
const { queue, rows, options, sequence } = input;
|
|
65
|
+
const grouped = rows.filter((row) => row.group_id !== null);
|
|
66
|
+
if (grouped.length === 0)
|
|
67
|
+
return;
|
|
68
|
+
const turns = new Map();
|
|
69
|
+
for (let index = 0; index < grouped.length; index++) {
|
|
70
|
+
const groupId = grouped[index].group_id;
|
|
71
|
+
const current = turns.get(groupId);
|
|
72
|
+
turns.set(groupId, {
|
|
73
|
+
last: sequence + index + 1,
|
|
74
|
+
count: (current?.count ?? 0) + 1,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const entries = [...turns.entries()];
|
|
78
|
+
await tx `
|
|
79
|
+
UPDATE bunqueue_group_state AS groups
|
|
80
|
+
SET last_served = batch.last_served,
|
|
81
|
+
rate_count = groups.rate_count + CASE WHEN ${options?.limit !== undefined}
|
|
82
|
+
THEN batch.claimed_count ELSE 0 END
|
|
83
|
+
FROM unnest(
|
|
84
|
+
${tx.array(entries.map(([groupId]) => groupId), 'TEXT')},
|
|
85
|
+
${tx.array(entries.map(([, value]) => value.last), 'BIGINT')},
|
|
86
|
+
${tx.array(entries.map(([, value]) => value.count), 'INTEGER')}
|
|
87
|
+
) AS batch(group_id, last_served, claimed_count)
|
|
88
|
+
WHERE groups.namespace = ${ctx.config.namespace} AND groups.queue = ${queue}
|
|
89
|
+
AND groups.group_id = batch.group_id
|
|
90
|
+
`;
|
|
91
|
+
await tx `
|
|
92
|
+
UPDATE bunqueue_queue_state SET group_sequence = ${sequence + grouped.length}
|
|
93
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Correctness-critical group schema and idempotent v19 repair operations. */
|
|
2
|
+
export declare const POSTGRES_GROUP_SCHEMA = "\nCREATE SEQUENCE IF NOT EXISTS bunqueue_group_order_seq AS BIGINT CACHE 1;\nALTER SEQUENCE bunqueue_group_order_seq\n AS BIGINT INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE CACHE 1;\n\nALTER TABLE bunqueue_jobs ADD COLUMN IF NOT EXISTS group_order BIGINT;\nALTER TABLE bunqueue_jobs\n ALTER COLUMN group_order TYPE BIGINT USING group_order::bigint,\n ALTER COLUMN group_order DROP DEFAULT,\n ALTER COLUMN group_order DROP NOT NULL;\n\nDO $group_order_backfill$\nDECLARE\n target RECORD;\nBEGIN\n PERFORM setval(\n 'bunqueue_group_order_seq',\n GREATEST(\n (SELECT last_value FROM bunqueue_group_order_seq),\n COALESCE((SELECT MAX(group_order) FROM bunqueue_jobs), 0),\n 1\n ),\n TRUE\n );\n FOR target IN\n SELECT namespace, id\n FROM bunqueue_jobs\n WHERE group_id IS NOT NULL AND group_order IS NULL\n ORDER BY created_at, id, namespace\n LOOP\n UPDATE bunqueue_jobs\n SET group_order = nextval('bunqueue_group_order_seq')\n WHERE namespace = target.namespace AND id = target.id;\n END LOOP;\nEND;\n$group_order_backfill$;\n\nCREATE TABLE IF NOT EXISTS bunqueue_group_state (\n namespace TEXT NOT NULL,\n queue TEXT NOT NULL,\n group_id TEXT NOT NULL,\n rate_limit BIGINT,\n rate_duration_ms BIGINT,\n rate_window_started_at BIGINT,\n rate_count BIGINT NOT NULL DEFAULT 0,\n rate_effective_max BIGINT,\n rate_effective_duration_ms BIGINT,\n concurrency_limit BIGINT,\n last_served BIGINT,\n PRIMARY KEY (namespace, queue, group_id)\n);\nALTER TABLE bunqueue_group_state\n ADD COLUMN IF NOT EXISTS namespace TEXT,\n ADD COLUMN IF NOT EXISTS queue TEXT,\n ADD COLUMN IF NOT EXISTS group_id TEXT,\n ADD COLUMN IF NOT EXISTS rate_limit BIGINT,\n ADD COLUMN IF NOT EXISTS rate_duration_ms BIGINT,\n ADD COLUMN IF NOT EXISTS rate_window_started_at BIGINT,\n ADD COLUMN IF NOT EXISTS rate_count BIGINT DEFAULT 0,\n ADD COLUMN IF NOT EXISTS rate_effective_max BIGINT,\n ADD COLUMN IF NOT EXISTS rate_effective_duration_ms BIGINT,\n ADD COLUMN IF NOT EXISTS concurrency_limit BIGINT,\n ADD COLUMN IF NOT EXISTS last_served BIGINT;\nALTER TABLE bunqueue_group_state\n ALTER COLUMN namespace TYPE TEXT USING namespace::text,\n ALTER COLUMN queue TYPE TEXT USING queue::text,\n ALTER COLUMN group_id TYPE TEXT USING group_id::text,\n ALTER COLUMN rate_limit TYPE BIGINT USING rate_limit::bigint,\n ALTER COLUMN rate_duration_ms TYPE BIGINT USING rate_duration_ms::bigint,\n ALTER COLUMN rate_window_started_at TYPE BIGINT USING rate_window_started_at::bigint,\n ALTER COLUMN rate_count TYPE BIGINT USING rate_count::bigint,\n ALTER COLUMN rate_effective_max TYPE BIGINT USING rate_effective_max::bigint,\n ALTER COLUMN rate_effective_duration_ms TYPE BIGINT USING rate_effective_duration_ms::bigint,\n ALTER COLUMN concurrency_limit TYPE BIGINT USING concurrency_limit::bigint,\n ALTER COLUMN last_served TYPE BIGINT USING last_served::bigint;\nUPDATE bunqueue_group_state SET rate_count = 0 WHERE rate_count IS NULL;\nALTER TABLE bunqueue_group_state\n ALTER COLUMN namespace DROP DEFAULT,\n ALTER COLUMN namespace SET NOT NULL,\n ALTER COLUMN queue DROP DEFAULT,\n ALTER COLUMN queue SET NOT NULL,\n ALTER COLUMN group_id DROP DEFAULT,\n ALTER COLUMN group_id SET NOT NULL,\n ALTER COLUMN rate_limit DROP DEFAULT,\n ALTER COLUMN rate_limit DROP NOT NULL,\n ALTER COLUMN rate_duration_ms DROP DEFAULT,\n ALTER COLUMN rate_duration_ms DROP NOT NULL,\n ALTER COLUMN rate_window_started_at DROP DEFAULT,\n ALTER COLUMN rate_window_started_at DROP NOT NULL,\n ALTER COLUMN rate_count SET DEFAULT 0,\n ALTER COLUMN rate_count SET NOT NULL,\n ALTER COLUMN rate_effective_max DROP DEFAULT,\n ALTER COLUMN rate_effective_max DROP NOT NULL,\n ALTER COLUMN rate_effective_duration_ms DROP DEFAULT,\n ALTER COLUMN rate_effective_duration_ms DROP NOT NULL,\n ALTER COLUMN concurrency_limit DROP DEFAULT,\n ALTER COLUMN concurrency_limit DROP NOT NULL,\n ALTER COLUMN last_served DROP DEFAULT,\n ALTER COLUMN last_served DROP NOT NULL;\n\nDO $group_state_primary_key$\nDECLARE\n constraint_name TEXT;\nBEGIN\n IF NOT EXISTS (\n SELECT 1\n FROM pg_constraint AS constraint_state\n JOIN pg_index AS index_state ON index_state.indexrelid = constraint_state.conindid\n JOIN pg_class AS index_class ON index_class.oid = index_state.indexrelid\n JOIN pg_am AS access_method ON access_method.oid = index_class.relam\n WHERE constraint_state.conrelid = to_regclass('bunqueue_group_state')\n AND constraint_state.contype = 'p'\n AND constraint_state.convalidated\n AND NOT constraint_state.condeferrable\n AND NOT constraint_state.condeferred\n AND index_state.indisprimary AND index_state.indisunique\n AND index_state.indisvalid AND index_state.indisready\n AND access_method.amname = 'btree'\n AND ARRAY(\n SELECT attribute.attname::text\n FROM unnest(constraint_state.conkey) WITH ORDINALITY AS key(attnum, position)\n JOIN pg_attribute AS attribute\n ON attribute.attrelid = constraint_state.conrelid\n AND attribute.attnum = key.attnum\n ORDER BY key.position\n ) = ARRAY['namespace', 'queue', 'group_id']\n ) THEN\n FOR constraint_name IN\n SELECT conname FROM pg_constraint\n WHERE conrelid = to_regclass('bunqueue_group_state')\n AND (contype = 'p' OR conname = 'bunqueue_group_state_pkey')\n LOOP\n EXECUTE format(\n 'ALTER TABLE bunqueue_group_state DROP CONSTRAINT %I',\n constraint_name\n );\n END LOOP;\n IF to_regclass('bunqueue_group_state_pkey') IS NOT NULL THEN\n DROP INDEX bunqueue_group_state_pkey;\n END IF;\n ALTER TABLE bunqueue_group_state\n ADD CONSTRAINT bunqueue_group_state_pkey PRIMARY KEY (namespace, queue, group_id);\n END IF;\nEND;\n$group_state_primary_key$;\n\nDROP INDEX IF EXISTS bunqueue_jobs_group_ready_idx;\nCREATE INDEX bunqueue_jobs_group_ready_idx\n ON bunqueue_jobs(namespace, queue, group_id, run_at ASC, group_order ASC, id ASC)\n WHERE group_id IS NOT NULL AND state IN ('waiting', 'prioritized', 'delayed');\nDROP INDEX IF EXISTS bunqueue_group_state_rotation_idx;\nCREATE INDEX bunqueue_group_state_rotation_idx\n ON bunqueue_group_state(namespace, queue, last_served, group_id);\n";
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/** Correctness-critical group schema and idempotent v19 repair operations. */
|
|
2
|
+
export const POSTGRES_GROUP_SCHEMA = `
|
|
3
|
+
CREATE SEQUENCE IF NOT EXISTS bunqueue_group_order_seq AS BIGINT CACHE 1;
|
|
4
|
+
ALTER SEQUENCE bunqueue_group_order_seq
|
|
5
|
+
AS BIGINT INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE CACHE 1;
|
|
6
|
+
|
|
7
|
+
ALTER TABLE bunqueue_jobs ADD COLUMN IF NOT EXISTS group_order BIGINT;
|
|
8
|
+
ALTER TABLE bunqueue_jobs
|
|
9
|
+
ALTER COLUMN group_order TYPE BIGINT USING group_order::bigint,
|
|
10
|
+
ALTER COLUMN group_order DROP DEFAULT,
|
|
11
|
+
ALTER COLUMN group_order DROP NOT NULL;
|
|
12
|
+
|
|
13
|
+
DO $group_order_backfill$
|
|
14
|
+
DECLARE
|
|
15
|
+
target RECORD;
|
|
16
|
+
BEGIN
|
|
17
|
+
PERFORM setval(
|
|
18
|
+
'bunqueue_group_order_seq',
|
|
19
|
+
GREATEST(
|
|
20
|
+
(SELECT last_value FROM bunqueue_group_order_seq),
|
|
21
|
+
COALESCE((SELECT MAX(group_order) FROM bunqueue_jobs), 0),
|
|
22
|
+
1
|
|
23
|
+
),
|
|
24
|
+
TRUE
|
|
25
|
+
);
|
|
26
|
+
FOR target IN
|
|
27
|
+
SELECT namespace, id
|
|
28
|
+
FROM bunqueue_jobs
|
|
29
|
+
WHERE group_id IS NOT NULL AND group_order IS NULL
|
|
30
|
+
ORDER BY created_at, id, namespace
|
|
31
|
+
LOOP
|
|
32
|
+
UPDATE bunqueue_jobs
|
|
33
|
+
SET group_order = nextval('bunqueue_group_order_seq')
|
|
34
|
+
WHERE namespace = target.namespace AND id = target.id;
|
|
35
|
+
END LOOP;
|
|
36
|
+
END;
|
|
37
|
+
$group_order_backfill$;
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS bunqueue_group_state (
|
|
40
|
+
namespace TEXT NOT NULL,
|
|
41
|
+
queue TEXT NOT NULL,
|
|
42
|
+
group_id TEXT NOT NULL,
|
|
43
|
+
rate_limit BIGINT,
|
|
44
|
+
rate_duration_ms BIGINT,
|
|
45
|
+
rate_window_started_at BIGINT,
|
|
46
|
+
rate_count BIGINT NOT NULL DEFAULT 0,
|
|
47
|
+
rate_effective_max BIGINT,
|
|
48
|
+
rate_effective_duration_ms BIGINT,
|
|
49
|
+
concurrency_limit BIGINT,
|
|
50
|
+
last_served BIGINT,
|
|
51
|
+
PRIMARY KEY (namespace, queue, group_id)
|
|
52
|
+
);
|
|
53
|
+
ALTER TABLE bunqueue_group_state
|
|
54
|
+
ADD COLUMN IF NOT EXISTS namespace TEXT,
|
|
55
|
+
ADD COLUMN IF NOT EXISTS queue TEXT,
|
|
56
|
+
ADD COLUMN IF NOT EXISTS group_id TEXT,
|
|
57
|
+
ADD COLUMN IF NOT EXISTS rate_limit BIGINT,
|
|
58
|
+
ADD COLUMN IF NOT EXISTS rate_duration_ms BIGINT,
|
|
59
|
+
ADD COLUMN IF NOT EXISTS rate_window_started_at BIGINT,
|
|
60
|
+
ADD COLUMN IF NOT EXISTS rate_count BIGINT DEFAULT 0,
|
|
61
|
+
ADD COLUMN IF NOT EXISTS rate_effective_max BIGINT,
|
|
62
|
+
ADD COLUMN IF NOT EXISTS rate_effective_duration_ms BIGINT,
|
|
63
|
+
ADD COLUMN IF NOT EXISTS concurrency_limit BIGINT,
|
|
64
|
+
ADD COLUMN IF NOT EXISTS last_served BIGINT;
|
|
65
|
+
ALTER TABLE bunqueue_group_state
|
|
66
|
+
ALTER COLUMN namespace TYPE TEXT USING namespace::text,
|
|
67
|
+
ALTER COLUMN queue TYPE TEXT USING queue::text,
|
|
68
|
+
ALTER COLUMN group_id TYPE TEXT USING group_id::text,
|
|
69
|
+
ALTER COLUMN rate_limit TYPE BIGINT USING rate_limit::bigint,
|
|
70
|
+
ALTER COLUMN rate_duration_ms TYPE BIGINT USING rate_duration_ms::bigint,
|
|
71
|
+
ALTER COLUMN rate_window_started_at TYPE BIGINT USING rate_window_started_at::bigint,
|
|
72
|
+
ALTER COLUMN rate_count TYPE BIGINT USING rate_count::bigint,
|
|
73
|
+
ALTER COLUMN rate_effective_max TYPE BIGINT USING rate_effective_max::bigint,
|
|
74
|
+
ALTER COLUMN rate_effective_duration_ms TYPE BIGINT USING rate_effective_duration_ms::bigint,
|
|
75
|
+
ALTER COLUMN concurrency_limit TYPE BIGINT USING concurrency_limit::bigint,
|
|
76
|
+
ALTER COLUMN last_served TYPE BIGINT USING last_served::bigint;
|
|
77
|
+
UPDATE bunqueue_group_state SET rate_count = 0 WHERE rate_count IS NULL;
|
|
78
|
+
ALTER TABLE bunqueue_group_state
|
|
79
|
+
ALTER COLUMN namespace DROP DEFAULT,
|
|
80
|
+
ALTER COLUMN namespace SET NOT NULL,
|
|
81
|
+
ALTER COLUMN queue DROP DEFAULT,
|
|
82
|
+
ALTER COLUMN queue SET NOT NULL,
|
|
83
|
+
ALTER COLUMN group_id DROP DEFAULT,
|
|
84
|
+
ALTER COLUMN group_id SET NOT NULL,
|
|
85
|
+
ALTER COLUMN rate_limit DROP DEFAULT,
|
|
86
|
+
ALTER COLUMN rate_limit DROP NOT NULL,
|
|
87
|
+
ALTER COLUMN rate_duration_ms DROP DEFAULT,
|
|
88
|
+
ALTER COLUMN rate_duration_ms DROP NOT NULL,
|
|
89
|
+
ALTER COLUMN rate_window_started_at DROP DEFAULT,
|
|
90
|
+
ALTER COLUMN rate_window_started_at DROP NOT NULL,
|
|
91
|
+
ALTER COLUMN rate_count SET DEFAULT 0,
|
|
92
|
+
ALTER COLUMN rate_count SET NOT NULL,
|
|
93
|
+
ALTER COLUMN rate_effective_max DROP DEFAULT,
|
|
94
|
+
ALTER COLUMN rate_effective_max DROP NOT NULL,
|
|
95
|
+
ALTER COLUMN rate_effective_duration_ms DROP DEFAULT,
|
|
96
|
+
ALTER COLUMN rate_effective_duration_ms DROP NOT NULL,
|
|
97
|
+
ALTER COLUMN concurrency_limit DROP DEFAULT,
|
|
98
|
+
ALTER COLUMN concurrency_limit DROP NOT NULL,
|
|
99
|
+
ALTER COLUMN last_served DROP DEFAULT,
|
|
100
|
+
ALTER COLUMN last_served DROP NOT NULL;
|
|
101
|
+
|
|
102
|
+
DO $group_state_primary_key$
|
|
103
|
+
DECLARE
|
|
104
|
+
constraint_name TEXT;
|
|
105
|
+
BEGIN
|
|
106
|
+
IF NOT EXISTS (
|
|
107
|
+
SELECT 1
|
|
108
|
+
FROM pg_constraint AS constraint_state
|
|
109
|
+
JOIN pg_index AS index_state ON index_state.indexrelid = constraint_state.conindid
|
|
110
|
+
JOIN pg_class AS index_class ON index_class.oid = index_state.indexrelid
|
|
111
|
+
JOIN pg_am AS access_method ON access_method.oid = index_class.relam
|
|
112
|
+
WHERE constraint_state.conrelid = to_regclass('bunqueue_group_state')
|
|
113
|
+
AND constraint_state.contype = 'p'
|
|
114
|
+
AND constraint_state.convalidated
|
|
115
|
+
AND NOT constraint_state.condeferrable
|
|
116
|
+
AND NOT constraint_state.condeferred
|
|
117
|
+
AND index_state.indisprimary AND index_state.indisunique
|
|
118
|
+
AND index_state.indisvalid AND index_state.indisready
|
|
119
|
+
AND access_method.amname = 'btree'
|
|
120
|
+
AND ARRAY(
|
|
121
|
+
SELECT attribute.attname::text
|
|
122
|
+
FROM unnest(constraint_state.conkey) WITH ORDINALITY AS key(attnum, position)
|
|
123
|
+
JOIN pg_attribute AS attribute
|
|
124
|
+
ON attribute.attrelid = constraint_state.conrelid
|
|
125
|
+
AND attribute.attnum = key.attnum
|
|
126
|
+
ORDER BY key.position
|
|
127
|
+
) = ARRAY['namespace', 'queue', 'group_id']
|
|
128
|
+
) THEN
|
|
129
|
+
FOR constraint_name IN
|
|
130
|
+
SELECT conname FROM pg_constraint
|
|
131
|
+
WHERE conrelid = to_regclass('bunqueue_group_state')
|
|
132
|
+
AND (contype = 'p' OR conname = 'bunqueue_group_state_pkey')
|
|
133
|
+
LOOP
|
|
134
|
+
EXECUTE format(
|
|
135
|
+
'ALTER TABLE bunqueue_group_state DROP CONSTRAINT %I',
|
|
136
|
+
constraint_name
|
|
137
|
+
);
|
|
138
|
+
END LOOP;
|
|
139
|
+
IF to_regclass('bunqueue_group_state_pkey') IS NOT NULL THEN
|
|
140
|
+
DROP INDEX bunqueue_group_state_pkey;
|
|
141
|
+
END IF;
|
|
142
|
+
ALTER TABLE bunqueue_group_state
|
|
143
|
+
ADD CONSTRAINT bunqueue_group_state_pkey PRIMARY KEY (namespace, queue, group_id);
|
|
144
|
+
END IF;
|
|
145
|
+
END;
|
|
146
|
+
$group_state_primary_key$;
|
|
147
|
+
|
|
148
|
+
DROP INDEX IF EXISTS bunqueue_jobs_group_ready_idx;
|
|
149
|
+
CREATE INDEX bunqueue_jobs_group_ready_idx
|
|
150
|
+
ON bunqueue_jobs(namespace, queue, group_id, run_at ASC, group_order ASC, id ASC)
|
|
151
|
+
WHERE group_id IS NOT NULL AND state IN ('waiting', 'prioritized', 'delayed');
|
|
152
|
+
DROP INDEX IF EXISTS bunqueue_group_state_rotation_idx;
|
|
153
|
+
CREATE INDEX bunqueue_group_state_rotation_idx
|
|
154
|
+
ON bunqueue_group_state(namespace, queue, last_served, group_id);
|
|
155
|
+
`;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/** Verify every catalog property required by durable group scheduling. */
|
|
2
|
+
export async function hasCurrentPostgresGroupSchema(tx) {
|
|
3
|
+
const [schema] = await tx `
|
|
4
|
+
WITH expected_columns(table_name, column_name, type_name, not_null, default_kind) AS (
|
|
5
|
+
VALUES
|
|
6
|
+
('bunqueue_jobs', 'group_order', 'bigint', FALSE, 'none'),
|
|
7
|
+
('bunqueue_group_state', 'namespace', 'text', TRUE, 'none'),
|
|
8
|
+
('bunqueue_group_state', 'queue', 'text', TRUE, 'none'),
|
|
9
|
+
('bunqueue_group_state', 'group_id', 'text', TRUE, 'none'),
|
|
10
|
+
('bunqueue_group_state', 'rate_limit', 'bigint', FALSE, 'none'),
|
|
11
|
+
('bunqueue_group_state', 'rate_duration_ms', 'bigint', FALSE, 'none'),
|
|
12
|
+
('bunqueue_group_state', 'rate_window_started_at', 'bigint', FALSE, 'none'),
|
|
13
|
+
('bunqueue_group_state', 'rate_count', 'bigint', TRUE, 'zero'),
|
|
14
|
+
('bunqueue_group_state', 'rate_effective_max', 'bigint', FALSE, 'none'),
|
|
15
|
+
('bunqueue_group_state', 'rate_effective_duration_ms', 'bigint', FALSE, 'none'),
|
|
16
|
+
('bunqueue_group_state', 'concurrency_limit', 'bigint', FALSE, 'none'),
|
|
17
|
+
('bunqueue_group_state', 'last_served', 'bigint', FALSE, 'none')
|
|
18
|
+
), expected_indexes(
|
|
19
|
+
index_name, table_name, columns, descending, predicate
|
|
20
|
+
) AS (
|
|
21
|
+
VALUES
|
|
22
|
+
('bunqueue_jobs_group_ready_idx', 'bunqueue_jobs',
|
|
23
|
+
ARRAY['namespace', 'queue', 'group_id', 'run_at', 'group_order', 'id'],
|
|
24
|
+
ARRAY[FALSE, FALSE, FALSE, FALSE, FALSE, FALSE],
|
|
25
|
+
'group_idisnotnullandstate=anyarray[''waiting''::text,''prioritized''::text,' ||
|
|
26
|
+
'''delayed''::text]'),
|
|
27
|
+
('bunqueue_group_state_rotation_idx', 'bunqueue_group_state',
|
|
28
|
+
ARRAY['namespace', 'queue', 'last_served', 'group_id'],
|
|
29
|
+
ARRAY[FALSE, FALSE, FALSE, FALSE], '')
|
|
30
|
+
)
|
|
31
|
+
SELECT
|
|
32
|
+
to_regclass('bunqueue_group_state') IS NOT NULL
|
|
33
|
+
AND EXISTS (
|
|
34
|
+
SELECT 1 FROM pg_sequence
|
|
35
|
+
WHERE seqrelid = to_regclass('bunqueue_group_order_seq')
|
|
36
|
+
AND seqtypid = to_regtype('bigint')
|
|
37
|
+
AND seqincrement = 1 AND seqmin = 1
|
|
38
|
+
AND seqmax = 9223372036854775807
|
|
39
|
+
AND NOT seqcycle AND seqcache = 1
|
|
40
|
+
)
|
|
41
|
+
AND (
|
|
42
|
+
SELECT COALESCE(bool_and(COALESCE(
|
|
43
|
+
attribute.atttypid = to_regtype(expected.type_name)
|
|
44
|
+
AND attribute.attnotnull = expected.not_null
|
|
45
|
+
AND CASE expected.default_kind
|
|
46
|
+
WHEN 'zero' THEN regexp_replace(
|
|
47
|
+
lower(COALESCE(pg_get_expr(defaults.adbin, defaults.adrelid), '')),
|
|
48
|
+
'[()[:space:]]', '', 'g'
|
|
49
|
+
) IN ('0', '0::bigint')
|
|
50
|
+
ELSE defaults.oid IS NULL
|
|
51
|
+
END,
|
|
52
|
+
FALSE
|
|
53
|
+
)), FALSE)
|
|
54
|
+
FROM expected_columns AS expected
|
|
55
|
+
LEFT JOIN pg_attribute AS attribute
|
|
56
|
+
ON attribute.attrelid = to_regclass(expected.table_name)
|
|
57
|
+
AND attribute.attname = expected.column_name
|
|
58
|
+
AND NOT attribute.attisdropped
|
|
59
|
+
LEFT JOIN pg_attrdef AS defaults
|
|
60
|
+
ON defaults.adrelid = attribute.attrelid
|
|
61
|
+
AND defaults.adnum = attribute.attnum
|
|
62
|
+
)
|
|
63
|
+
AND (
|
|
64
|
+
SELECT COALESCE(bool_and(COALESCE(
|
|
65
|
+
indexes.indrelid = to_regclass(expected.table_name)
|
|
66
|
+
AND indexes.indisvalid AND indexes.indisready
|
|
67
|
+
AND NOT indexes.indisunique AND NOT indexes.indisexclusion
|
|
68
|
+
AND indexes.indnkeyatts = cardinality(expected.columns)
|
|
69
|
+
AND indexes.indnatts = indexes.indnkeyatts
|
|
70
|
+
AND indexes.indexprs IS NULL
|
|
71
|
+
AND access_method.amname = 'btree'
|
|
72
|
+
AND ARRAY(
|
|
73
|
+
SELECT attribute.attname::text
|
|
74
|
+
FROM unnest(indexes.indkey::smallint[]) WITH ORDINALITY AS key(attnum, position)
|
|
75
|
+
JOIN pg_attribute AS attribute
|
|
76
|
+
ON attribute.attrelid = indexes.indrelid
|
|
77
|
+
AND attribute.attnum = key.attnum
|
|
78
|
+
ORDER BY key.position
|
|
79
|
+
) = expected.columns
|
|
80
|
+
AND ARRAY(
|
|
81
|
+
SELECT (option & 1) = 1
|
|
82
|
+
FROM unnest(indexes.indoption::smallint[])
|
|
83
|
+
WITH ORDINALITY AS ordering(option, position)
|
|
84
|
+
ORDER BY ordering.position
|
|
85
|
+
) = expected.descending
|
|
86
|
+
AND regexp_replace(
|
|
87
|
+
lower(COALESCE(pg_get_expr(indexes.indpred, indexes.indrelid), '')),
|
|
88
|
+
'[()[:space:]]', '', 'g'
|
|
89
|
+
) = expected.predicate,
|
|
90
|
+
FALSE
|
|
91
|
+
)), FALSE)
|
|
92
|
+
FROM expected_indexes AS expected
|
|
93
|
+
LEFT JOIN pg_class AS index_class
|
|
94
|
+
ON index_class.oid = to_regclass(expected.index_name)
|
|
95
|
+
LEFT JOIN pg_index AS indexes ON indexes.indexrelid = index_class.oid
|
|
96
|
+
LEFT JOIN pg_am AS access_method ON access_method.oid = index_class.relam
|
|
97
|
+
)
|
|
98
|
+
AND EXISTS (
|
|
99
|
+
SELECT 1
|
|
100
|
+
FROM pg_constraint AS constraint_state
|
|
101
|
+
JOIN pg_index AS index_state ON index_state.indexrelid = constraint_state.conindid
|
|
102
|
+
JOIN pg_class AS index_class ON index_class.oid = index_state.indexrelid
|
|
103
|
+
JOIN pg_am AS access_method ON access_method.oid = index_class.relam
|
|
104
|
+
WHERE constraint_state.conrelid = to_regclass('bunqueue_group_state')
|
|
105
|
+
AND constraint_state.contype = 'p'
|
|
106
|
+
AND constraint_state.convalidated
|
|
107
|
+
AND NOT constraint_state.condeferrable
|
|
108
|
+
AND NOT constraint_state.condeferred
|
|
109
|
+
AND index_state.indisprimary AND index_state.indisunique
|
|
110
|
+
AND index_state.indisvalid AND index_state.indisready
|
|
111
|
+
AND index_state.indimmediate
|
|
112
|
+
AND index_state.indnkeyatts = 3 AND index_state.indnatts = 3
|
|
113
|
+
AND index_state.indexprs IS NULL AND index_state.indpred IS NULL
|
|
114
|
+
AND access_method.amname = 'btree'
|
|
115
|
+
AND ARRAY(
|
|
116
|
+
SELECT attribute.attname::text
|
|
117
|
+
FROM unnest(constraint_state.conkey)
|
|
118
|
+
WITH ORDINALITY AS key(attnum, position)
|
|
119
|
+
JOIN pg_attribute AS attribute
|
|
120
|
+
ON attribute.attrelid = constraint_state.conrelid
|
|
121
|
+
AND attribute.attnum = key.attnum
|
|
122
|
+
ORDER BY key.position
|
|
123
|
+
) = ARRAY['namespace', 'queue', 'group_id']
|
|
124
|
+
) AS valid
|
|
125
|
+
`;
|
|
126
|
+
return schema.valid;
|
|
127
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { databaseNow } from './context';
|
|
2
|
+
async function pruneBatch(tx, ctx, batchSize) {
|
|
3
|
+
const now = await databaseNow(tx);
|
|
4
|
+
const rows = await tx `
|
|
5
|
+
SELECT groups.queue, groups.group_id
|
|
6
|
+
FROM bunqueue_group_state AS groups
|
|
7
|
+
WHERE groups.namespace = ${ctx.config.namespace}
|
|
8
|
+
AND NOT EXISTS (
|
|
9
|
+
SELECT 1 FROM bunqueue_jobs AS job
|
|
10
|
+
WHERE job.namespace = groups.namespace
|
|
11
|
+
AND job.queue = groups.queue
|
|
12
|
+
AND job.group_id = groups.group_id
|
|
13
|
+
AND job.state IN (
|
|
14
|
+
'waiting', 'prioritized', 'delayed', 'waiting-children', 'active'
|
|
15
|
+
)
|
|
16
|
+
)
|
|
17
|
+
AND (
|
|
18
|
+
groups.last_served IS NOT NULL
|
|
19
|
+
OR (
|
|
20
|
+
groups.rate_limit IS NULL
|
|
21
|
+
AND groups.rate_duration_ms IS NULL
|
|
22
|
+
AND groups.concurrency_limit IS NULL
|
|
23
|
+
AND (
|
|
24
|
+
groups.rate_window_started_at IS NULL
|
|
25
|
+
OR (
|
|
26
|
+
groups.rate_effective_duration_ms IS NOT NULL
|
|
27
|
+
AND groups.rate_window_started_at + groups.rate_effective_duration_ms <= ${now}
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
ORDER BY groups.queue, groups.group_id
|
|
33
|
+
LIMIT ${batchSize}
|
|
34
|
+
FOR UPDATE OF groups SKIP LOCKED
|
|
35
|
+
`;
|
|
36
|
+
if (rows.length === 0)
|
|
37
|
+
return { deleted: 0, selected: 0 };
|
|
38
|
+
const queues = rows.map(({ queue }) => queue);
|
|
39
|
+
const groupIds = rows.map(({ group_id }) => group_id);
|
|
40
|
+
await tx `
|
|
41
|
+
UPDATE bunqueue_group_state AS groups
|
|
42
|
+
SET last_served = NULL
|
|
43
|
+
FROM unnest(
|
|
44
|
+
${tx.array(queues, 'TEXT')},
|
|
45
|
+
${tx.array(groupIds, 'TEXT')}
|
|
46
|
+
) AS target(queue, group_id)
|
|
47
|
+
WHERE groups.namespace = ${ctx.config.namespace}
|
|
48
|
+
AND groups.queue = target.queue
|
|
49
|
+
AND groups.group_id = target.group_id
|
|
50
|
+
`;
|
|
51
|
+
const deleted = await tx `
|
|
52
|
+
DELETE FROM bunqueue_group_state AS groups
|
|
53
|
+
USING unnest(
|
|
54
|
+
${tx.array(queues, 'TEXT')},
|
|
55
|
+
${tx.array(groupIds, 'TEXT')}
|
|
56
|
+
) AS target(queue, group_id)
|
|
57
|
+
WHERE groups.namespace = ${ctx.config.namespace}
|
|
58
|
+
AND groups.queue = target.queue
|
|
59
|
+
AND groups.group_id = target.group_id
|
|
60
|
+
AND groups.rate_limit IS NULL
|
|
61
|
+
AND groups.rate_duration_ms IS NULL
|
|
62
|
+
AND groups.concurrency_limit IS NULL
|
|
63
|
+
AND (
|
|
64
|
+
groups.rate_window_started_at IS NULL
|
|
65
|
+
OR (
|
|
66
|
+
groups.rate_effective_duration_ms IS NOT NULL
|
|
67
|
+
AND groups.rate_window_started_at + groups.rate_effective_duration_ms <= ${now}
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
AND NOT EXISTS (
|
|
71
|
+
SELECT 1 FROM bunqueue_jobs AS job
|
|
72
|
+
WHERE job.namespace = groups.namespace
|
|
73
|
+
AND job.queue = groups.queue
|
|
74
|
+
AND job.group_id = groups.group_id
|
|
75
|
+
AND job.state IN (
|
|
76
|
+
'waiting', 'prioritized', 'delayed', 'waiting-children', 'active'
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
RETURNING groups.group_id
|
|
80
|
+
`;
|
|
81
|
+
return { deleted: deleted.length, selected: rows.length };
|
|
82
|
+
}
|
|
83
|
+
/** Reclaim bounded batches of inactive policyless group scheduler state. */
|
|
84
|
+
export async function prunePostgresGroupStates(ctx, batchSize = 1000, maxBatches = 10) {
|
|
85
|
+
let deleted = 0;
|
|
86
|
+
for (let batch = 0; batch < maxBatches; batch++) {
|
|
87
|
+
const result = await ctx.sql.begin((tx) => pruneBatch(tx, ctx, batchSize));
|
|
88
|
+
deleted += result.deleted;
|
|
89
|
+
if (result.selected < batchSize)
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
return deleted;
|
|
93
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type GroupRateLimitOverride } from '../../../domain/types/group';
|
|
2
|
+
import { type PostgresContext } from './context';
|
|
3
|
+
export declare function getPostgresGroupJobsCount(ctx: PostgresContext, queue: string, groupId?: string): Promise<number>;
|
|
4
|
+
export declare function getPostgresGroupActiveCount(ctx: PostgresContext, queue: string, groupId: string): Promise<number>;
|
|
5
|
+
export declare function setPostgresGroupRateLimit(ctx: PostgresContext, queue: string, groupId: string, max: number, duration: number): Promise<void>;
|
|
6
|
+
export declare function getPostgresGroupRateLimit(ctx: PostgresContext, queue: string, groupId: string): Promise<GroupRateLimitOverride | null>;
|
|
7
|
+
export declare function removePostgresGroupRateLimit(ctx: PostgresContext, queue: string, groupId: string): Promise<number>;
|
|
8
|
+
export declare function getPostgresGroupRateLimitTtl(ctx: PostgresContext, queue: string, groupId: string, maxJobs?: number): Promise<number>;
|
|
9
|
+
export declare function setPostgresGroupConcurrency(ctx: PostgresContext, queue: string, groupId: string, concurrency: number): Promise<void>;
|
|
10
|
+
export declare function getPostgresGroupConcurrency(ctx: PostgresContext, queue: string, groupId: string): Promise<number | null>;
|
|
11
|
+
export declare function removePostgresGroupConcurrency(ctx: PostgresContext, queue: string, groupId: string): Promise<number>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { assertGroupId, assertPositiveSafeInteger, } from '../../../domain/types/group';
|
|
2
|
+
import { databaseNow } from './context';
|
|
3
|
+
export async function getPostgresGroupJobsCount(ctx, queue, groupId) {
|
|
4
|
+
if (groupId !== undefined)
|
|
5
|
+
assertGroupId(groupId);
|
|
6
|
+
const [row] = groupId === undefined
|
|
7
|
+
? await ctx.sql `
|
|
8
|
+
SELECT COUNT(*)::bigint AS count FROM bunqueue_jobs
|
|
9
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
10
|
+
AND group_id IS NOT NULL AND state IN ('waiting', 'prioritized', 'delayed')
|
|
11
|
+
`
|
|
12
|
+
: await ctx.sql `
|
|
13
|
+
SELECT COUNT(*)::bigint AS count FROM bunqueue_jobs
|
|
14
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
15
|
+
AND group_id = ${groupId} AND state IN ('waiting', 'prioritized', 'delayed')
|
|
16
|
+
`;
|
|
17
|
+
return Number(row.count);
|
|
18
|
+
}
|
|
19
|
+
export async function getPostgresGroupActiveCount(ctx, queue, groupId) {
|
|
20
|
+
assertGroupId(groupId);
|
|
21
|
+
const now = await databaseNow(ctx.sql);
|
|
22
|
+
const [row] = await ctx.sql `
|
|
23
|
+
SELECT COUNT(*)::bigint AS count FROM bunqueue_jobs
|
|
24
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
|
|
25
|
+
AND group_id = ${groupId} AND state = 'active' AND lease_until > ${now}
|
|
26
|
+
`;
|
|
27
|
+
return Number(row.count);
|
|
28
|
+
}
|
|
29
|
+
export async function setPostgresGroupRateLimit(ctx, queue, groupId, max, duration) {
|
|
30
|
+
assertGroupId(groupId);
|
|
31
|
+
assertPositiveSafeInteger(max, 'max');
|
|
32
|
+
assertPositiveSafeInteger(duration, 'duration');
|
|
33
|
+
await ctx.sql `
|
|
34
|
+
INSERT INTO bunqueue_group_state (
|
|
35
|
+
namespace, queue, group_id, rate_limit, rate_duration_ms
|
|
36
|
+
) VALUES (${ctx.config.namespace}, ${queue}, ${groupId}, ${max}, ${duration})
|
|
37
|
+
ON CONFLICT (namespace, queue, group_id) DO UPDATE
|
|
38
|
+
SET rate_limit = EXCLUDED.rate_limit,
|
|
39
|
+
rate_duration_ms = EXCLUDED.rate_duration_ms,
|
|
40
|
+
rate_window_started_at = NULL,
|
|
41
|
+
rate_count = 0,
|
|
42
|
+
rate_effective_max = NULL,
|
|
43
|
+
rate_effective_duration_ms = NULL
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
export async function getPostgresGroupRateLimit(ctx, queue, groupId) {
|
|
47
|
+
assertGroupId(groupId);
|
|
48
|
+
const rows = await ctx.sql `
|
|
49
|
+
SELECT rate_limit, rate_duration_ms, rate_window_started_at, rate_count,
|
|
50
|
+
rate_effective_duration_ms, concurrency_limit
|
|
51
|
+
FROM bunqueue_group_state
|
|
52
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
|
|
53
|
+
`;
|
|
54
|
+
const row = rows[0];
|
|
55
|
+
if (!row || row.rate_limit === null || row.rate_duration_ms === null)
|
|
56
|
+
return null;
|
|
57
|
+
return { max: Number(row.rate_limit), duration: Number(row.rate_duration_ms) };
|
|
58
|
+
}
|
|
59
|
+
export async function removePostgresGroupRateLimit(ctx, queue, groupId) {
|
|
60
|
+
assertGroupId(groupId);
|
|
61
|
+
const rows = await ctx.sql `
|
|
62
|
+
UPDATE bunqueue_group_state
|
|
63
|
+
SET rate_limit = NULL, rate_duration_ms = NULL, rate_window_started_at = NULL,
|
|
64
|
+
rate_count = 0, rate_effective_max = NULL, rate_effective_duration_ms = NULL
|
|
65
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
|
|
66
|
+
AND rate_limit IS NOT NULL
|
|
67
|
+
RETURNING group_id
|
|
68
|
+
`;
|
|
69
|
+
return rows.length;
|
|
70
|
+
}
|
|
71
|
+
export async function getPostgresGroupRateLimitTtl(ctx, queue, groupId, maxJobs) {
|
|
72
|
+
assertGroupId(groupId);
|
|
73
|
+
const rows = await ctx.sql `
|
|
74
|
+
SELECT rate_limit, rate_duration_ms, rate_window_started_at, rate_count,
|
|
75
|
+
rate_effective_duration_ms, concurrency_limit
|
|
76
|
+
FROM bunqueue_group_state
|
|
77
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
|
|
78
|
+
`;
|
|
79
|
+
const row = rows[0];
|
|
80
|
+
if (!row?.rate_window_started_at || row.rate_effective_duration_ms === null)
|
|
81
|
+
return -2;
|
|
82
|
+
if (maxJobs !== undefined && Number(row.rate_count) < maxJobs)
|
|
83
|
+
return 0;
|
|
84
|
+
const remaining = Number(row.rate_window_started_at) +
|
|
85
|
+
Number(row.rate_effective_duration_ms) -
|
|
86
|
+
(await databaseNow(ctx.sql));
|
|
87
|
+
return remaining > 0 ? remaining : -2;
|
|
88
|
+
}
|
|
89
|
+
export async function setPostgresGroupConcurrency(ctx, queue, groupId, concurrency) {
|
|
90
|
+
assertGroupId(groupId);
|
|
91
|
+
assertPositiveSafeInteger(concurrency, 'concurrency');
|
|
92
|
+
await ctx.sql `
|
|
93
|
+
INSERT INTO bunqueue_group_state (namespace, queue, group_id, concurrency_limit)
|
|
94
|
+
VALUES (${ctx.config.namespace}, ${queue}, ${groupId}, ${concurrency})
|
|
95
|
+
ON CONFLICT (namespace, queue, group_id) DO UPDATE
|
|
96
|
+
SET concurrency_limit = EXCLUDED.concurrency_limit
|
|
97
|
+
`;
|
|
98
|
+
}
|
|
99
|
+
export async function getPostgresGroupConcurrency(ctx, queue, groupId) {
|
|
100
|
+
assertGroupId(groupId);
|
|
101
|
+
const rows = await ctx.sql `
|
|
102
|
+
SELECT concurrency_limit FROM bunqueue_group_state
|
|
103
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
|
|
104
|
+
`;
|
|
105
|
+
const value = rows[0]?.concurrency_limit;
|
|
106
|
+
return value === undefined || value === null ? null : Number(value);
|
|
107
|
+
}
|
|
108
|
+
export async function removePostgresGroupConcurrency(ctx, queue, groupId) {
|
|
109
|
+
assertGroupId(groupId);
|
|
110
|
+
const rows = await ctx.sql `
|
|
111
|
+
UPDATE bunqueue_group_state SET concurrency_limit = NULL
|
|
112
|
+
WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
|
|
113
|
+
AND concurrency_limit IS NOT NULL
|
|
114
|
+
RETURNING group_id
|
|
115
|
+
`;
|
|
116
|
+
return rows.length;
|
|
117
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const FLOW_METADATA_KEYS = [
|
|
2
|
+
'__parentId',
|
|
3
|
+
'__parentQueue',
|
|
4
|
+
'__childrenIds',
|
|
5
|
+
'__flowParentId',
|
|
6
|
+
'__flowParentIds',
|
|
7
|
+
];
|
|
8
|
+
function dataRecord(value) {
|
|
9
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
10
|
+
? value
|
|
11
|
+
: null;
|
|
12
|
+
}
|
|
13
|
+
/** Merge user data while retaining the flow metadata owned by the broker. */
|
|
14
|
+
export function updatedPostgresJobData(job, data) {
|
|
15
|
+
const current = dataRecord(job.data);
|
|
16
|
+
const metadata = current
|
|
17
|
+
? Object.fromEntries(FLOW_METADATA_KEYS.filter((key) => Object.hasOwn(current, key)).map((key) => [
|
|
18
|
+
key,
|
|
19
|
+
current[key],
|
|
20
|
+
]))
|
|
21
|
+
: {};
|
|
22
|
+
if (Object.keys(metadata).length === 0)
|
|
23
|
+
return data;
|
|
24
|
+
const next = dataRecord(data);
|
|
25
|
+
if (!next)
|
|
26
|
+
throw new Error('flow job data must be an object');
|
|
27
|
+
for (const key of Object.keys(next)) {
|
|
28
|
+
if (key.startsWith('__'))
|
|
29
|
+
throw new Error(`flow job data key is reserved: ${key}`);
|
|
30
|
+
}
|
|
31
|
+
return { ...next, ...metadata };
|
|
32
|
+
}
|