bunqueue 2.8.60 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -76
- package/dist/application/background/dlq.js +2 -0
- package/dist/application/contextFactory.js +5 -0
- package/dist/application/dlqManager.d.ts +10 -0
- package/dist/application/dlqManager.js +47 -9
- package/dist/application/dlqRetry.js +59 -38
- package/dist/application/eventsManager.d.ts +1 -1
- package/dist/application/eventsManager.js +30 -26
- package/dist/application/operations/jobManagement.d.ts +4 -0
- package/dist/application/operations/jobMoveOperations.js +9 -4
- package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
- package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
- package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
- package/dist/application/postgres-queue-manager/cloud.js +55 -0
- package/dist/application/postgres-queue-manager/config.d.ts +5 -0
- package/dist/application/postgres-queue-manager/config.js +1 -0
- package/dist/application/postgres-queue-manager/control.d.ts +26 -0
- package/dist/application/postgres-queue-manager/control.js +186 -0
- package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
- package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
- package/dist/application/postgres-queue-manager/delivery.d.ts +19 -0
- package/dist/application/postgres-queue-manager/delivery.js +80 -0
- package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
- package/dist/application/postgres-queue-manager/lease.js +88 -0
- package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
- package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
- package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
- package/dist/application/postgres-queue-manager/maintenance.js +100 -0
- package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
- package/dist/application/postgres-queue-manager/operationGate.js +96 -0
- package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
- package/dist/application/postgres-queue-manager/operations.js +194 -0
- package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
- package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
- package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
- package/dist/application/postgres-queue-manager/queries.js +231 -0
- package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
- package/dist/application/postgres-queue-manager/relationships.js +45 -0
- package/dist/application/postgres-queue-manager/services.d.ts +31 -0
- package/dist/application/postgres-queue-manager/services.js +149 -0
- package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
- package/dist/application/postgres-queue-manager/snapshot.js +236 -0
- package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
- package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
- package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
- package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
- package/dist/application/postgres-queue-manager/state.d.ts +47 -0
- package/dist/application/postgres-queue-manager/state.js +273 -0
- package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
- package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
- package/dist/application/postgresQueueManager.d.ts +2 -0
- package/dist/application/postgresQueueManager.js +1 -0
- package/dist/application/queue-manager/flow-failures.js +4 -0
- package/dist/application/queue-manager/limits.d.ts +2 -0
- package/dist/application/queue-manager/limits.js +4 -0
- package/dist/application/queue-manager/observability.js +15 -3
- package/dist/application/queue-manager/queries.d.ts +7 -0
- package/dist/application/queue-manager/queries.js +9 -0
- package/dist/application/queue-manager/services.d.ts +1 -1
- package/dist/application/queue-manager/services.js +4 -2
- package/dist/application/queue-manager/state.js +16 -1
- package/dist/cli/commands/server.js +1 -1
- package/dist/client/forwarder.d.ts +6 -4
- package/dist/client/forwarder.js +5 -3
- package/dist/client/queue/dlq.d.ts +2 -0
- package/dist/client/queue/dlq.js +14 -0
- package/dist/client/queue/dlqOps.d.ts +2 -0
- package/dist/client/queue/dlqOps.js +4 -0
- package/dist/client/queue/helpers.js +2 -0
- package/dist/client/queue/runtime/configuration.d.ts +4 -0
- package/dist/client/queue/runtime/configuration.js +8 -0
- package/dist/client/worker/runtime/execution.js +3 -8
- package/dist/client/worker/runtime/polling.d.ts +1 -0
- package/dist/client/worker/runtime/polling.js +9 -0
- package/dist/config/resolve.d.ts +14 -0
- package/dist/config/resolve.js +50 -5
- package/dist/config/types.d.ts +18 -0
- package/dist/domain/queue/dlqShard.d.ts +2 -0
- package/dist/domain/queue/dlqShard.js +16 -0
- package/dist/domain/queue/shard/dlq.d.ts +1 -0
- package/dist/domain/queue/shard/dlq.js +3 -0
- package/dist/domain/types/commands/dlq.d.ts +5 -0
- package/dist/domain/types/commands/union.d.ts +1 -1
- package/dist/domain/types/cron.d.ts +5 -0
- package/dist/domain/types/cron.js +14 -3
- package/dist/domain/types/dlq.d.ts +1 -1
- package/dist/domain/types/dlq.js +1 -2
- package/dist/domain/types/jobs/model.d.ts +1 -1
- package/dist/infrastructure/cloud/commandHandler.js +5 -3
- package/dist/infrastructure/cloud/commands/integrations.js +11 -11
- package/dist/infrastructure/cloud/commands/jobs.js +48 -40
- package/dist/infrastructure/cloud/commands/queues.js +61 -75
- package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
- package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
- package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
- package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
- package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
- package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
- package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
- package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
- package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
- package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
- package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
- package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
- package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
- package/dist/infrastructure/cloud/snapshot/config.js +43 -0
- package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
- package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
- package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
- package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
- package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
- package/dist/infrastructure/cloud/statsRefresh.js +2 -1
- package/dist/infrastructure/cloud/types/command.d.ts +2 -2
- package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/admission.js +193 -0
- package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
- package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
- package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
- package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
- package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
- package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
- package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/batchAdmission.js +110 -0
- package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
- package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
- package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
- package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/claimBatch.js +56 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.js +198 -0
- package/dist/infrastructure/persistence/postgres/claims.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/claims.js +137 -0
- package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
- package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
- package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
- package/dist/infrastructure/persistence/postgres/codec.js +82 -0
- package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
- package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
- package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
- package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
- package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/connection.js +15 -0
- package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
- package/dist/infrastructure/persistence/postgres/context.js +74 -0
- package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
- package/dist/infrastructure/persistence/postgres/control.js +105 -0
- package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/crons.js +205 -0
- package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
- package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
- package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
- package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
- package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
- package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
- package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
- package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
- package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
- package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
- package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
- package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
- package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
- package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +253 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +24 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +111 -0
- package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
- package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
- package/dist/infrastructure/persistence/postgres/events.js +249 -0
- package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
- package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
- package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
- package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
- package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
- package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
- package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/logs.js +70 -0
- package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
- package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
- package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
- package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
- package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
- package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/policies.js +18 -0
- package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
- package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
- package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
- package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
- package/dist/infrastructure/persistence/postgres/queries.js +152 -0
- package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/queueDestruction.js +154 -0
- package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
- package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
- package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
- package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
- package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
- package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
- package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
- package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
- package/dist/infrastructure/persistence/postgres/runtime.d.ts +46 -0
- package/dist/infrastructure/persistence/postgres/runtime.js +236 -0
- package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
- package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
- package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/schema.js +257 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.js +221 -0
- package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
- package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
- package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
- package/dist/infrastructure/persistence/postgres/store.d.ts +90 -0
- package/dist/infrastructure/persistence/postgres/store.js +136 -0
- package/dist/infrastructure/persistence/postgres/telemetry.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/telemetry.js +88 -0
- package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
- package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
- package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
- package/dist/infrastructure/persistence/postgres/types.d.ts +175 -0
- package/dist/infrastructure/persistence/postgres/types.js +1 -0
- package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
- package/dist/infrastructure/persistence/postgres/workers.js +126 -0
- package/dist/infrastructure/persistence/postgres.d.ts +2 -0
- package/dist/infrastructure/persistence/postgres.js +1 -0
- package/dist/infrastructure/persistence/sqlite/jobs.js +2 -0
- package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
- package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
- package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
- package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
- package/dist/infrastructure/scheduler/cron/validation.js +11 -0
- package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
- package/dist/infrastructure/scheduler/cronParser.js +144 -11
- package/dist/infrastructure/server/bootstrap.d.ts +2 -3
- package/dist/infrastructure/server/bootstrap.js +33 -59
- package/dist/infrastructure/server/errors.d.ts +4 -0
- package/dist/infrastructure/server/errors.js +54 -0
- package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
- package/dist/infrastructure/server/handler-routes/control.js +3 -1
- package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
- package/dist/infrastructure/server/handler.js +9 -11
- package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
- package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
- package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
- package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
- package/dist/infrastructure/server/handlers/core.js +32 -31
- package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
- package/dist/infrastructure/server/handlers/cron.js +60 -59
- package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
- package/dist/infrastructure/server/handlers/dashboard.js +21 -8
- package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
- package/dist/infrastructure/server/handlers/dlq.js +35 -9
- package/dist/infrastructure/server/handlers/flow.js +2 -1
- package/dist/infrastructure/server/handlers/management.d.ts +5 -5
- package/dist/infrastructure/server/handlers/management.js +36 -9
- package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
- package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
- package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
- package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
- package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
- package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
- package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
- package/dist/infrastructure/server/handlers/pushBatchValidation.js +52 -7
- package/dist/infrastructure/server/handlers/query.d.ts +1 -1
- package/dist/infrastructure/server/handlers/query.js +5 -4
- package/dist/infrastructure/server/http.js +2 -2
- package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
- package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
- package/dist/infrastructure/server/httpEndpoints.js +18 -7
- package/dist/infrastructure/server/httpRouteQueues.js +11 -1
- package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
- package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
- package/dist/infrastructure/server/sseHandler.js +8 -3
- package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
- package/dist/infrastructure/server/storageAdapter.js +61 -0
- package/dist/infrastructure/server/storageManager.d.ts +12 -0
- package/dist/infrastructure/server/storageManager.js +102 -0
- package/dist/infrastructure/server/tcp.js +2 -3
- package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
- package/dist/infrastructure/server/ws/snapshots.js +19 -4
- package/dist/infrastructure/server/wsHandler.js +10 -5
- package/dist/main.js +1 -1
- package/dist/mcp/backend/embedded/services.js +2 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +2 -2
- package/dist/shared/storageHealth.d.ts +9 -0
- package/dist/shared/storageHealth.js +12 -0
- package/package.json +74 -65
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { PostgresQueueStore, } from '../../infrastructure/persistence/postgres';
|
|
2
|
+
import { QueueManager } from '../queueManager';
|
|
3
|
+
import { stopBackgroundTasks } from '../backgroundTasks';
|
|
4
|
+
import { PostgresDeferredWriteQueue, throwDeferredWriteErrors } from './deferredWrites';
|
|
5
|
+
import { applyPostgresEventMetrics, hydratePostgresLifetimeMetrics, postgresEventType, } from './lifetimeMetrics';
|
|
6
|
+
import { PostgresQueueSnapshot } from './snapshot';
|
|
7
|
+
import { PostgresStartupEventBuffer } from './startupEventBuffer';
|
|
8
|
+
import { PostgresOperationGate } from './operationGate';
|
|
9
|
+
import { applyPostgresJobProjection, PostgresProjectionRefreshes } from './projectionRefreshes';
|
|
10
|
+
export class PostgresQueueManagerState extends QueueManager {
|
|
11
|
+
postgresStore;
|
|
12
|
+
postgresSnapshot;
|
|
13
|
+
activeTokens = new Map();
|
|
14
|
+
postgresReady;
|
|
15
|
+
refreshes = new Map();
|
|
16
|
+
dirtyQueues = new Set();
|
|
17
|
+
queueEventVersions = new Map();
|
|
18
|
+
snapshotEventBuffer = new PostgresStartupEventBuffer();
|
|
19
|
+
terminalMetricBaselineCommitSeq = 0;
|
|
20
|
+
unsubscribeEvent;
|
|
21
|
+
unsubscribeInvalidation;
|
|
22
|
+
writes = new PostgresDeferredWriteQueue();
|
|
23
|
+
operations;
|
|
24
|
+
projectionRefreshes;
|
|
25
|
+
shutdownPromise = null;
|
|
26
|
+
shutdownComplete = false;
|
|
27
|
+
baseStopped = false;
|
|
28
|
+
stopQueueRefreshes = false;
|
|
29
|
+
constructor(config) {
|
|
30
|
+
const { postgres, ...baseConfig } = config;
|
|
31
|
+
super(baseConfig);
|
|
32
|
+
if (this.backgroundTaskHandles)
|
|
33
|
+
stopBackgroundTasks(this.backgroundTaskHandles);
|
|
34
|
+
this.postgresSnapshot = new PostgresQueueSnapshot(this.config.maxCompletedJobs, this.config.maxJobResults);
|
|
35
|
+
this.postgresStore = new PostgresQueueStore({
|
|
36
|
+
...postgres,
|
|
37
|
+
maxQueueEvents: this.config.maxQueueEvents,
|
|
38
|
+
maxMetricDataPoints: this.config.maxMetricDataPoints,
|
|
39
|
+
maxCompletedJobs: this.config.maxCompletedJobs,
|
|
40
|
+
maxJobResults: this.config.maxJobResults,
|
|
41
|
+
});
|
|
42
|
+
this.operations = new PostgresOperationGate(this.postgresStore.config.maxConcurrentOperations, this.postgresStore.config.maxQueuedOperations);
|
|
43
|
+
this.projectionRefreshes = new PostgresProjectionRefreshes((requests) => this.postgresStore.loadJobProjections(requests), (id, projection) => applyPostgresJobProjection(this.postgresSnapshot, this.activeTokens, id, projection), (queue, id, error) => this.postgresStore.reportProjectionRefresh(queue, id, error), this.postgresStore.config.pollIntervalMs);
|
|
44
|
+
this.unsubscribeEvent = this.postgresStore.onEvent((event) => this.onStoreEvent(event));
|
|
45
|
+
this.unsubscribeInvalidation = this.postgresStore.onInvalidation((queue) => {
|
|
46
|
+
this.scheduleQueueRefresh(queue);
|
|
47
|
+
});
|
|
48
|
+
this.postgresReady = this.initializePostgres();
|
|
49
|
+
}
|
|
50
|
+
async waitUntilReady() {
|
|
51
|
+
await this.postgresReady;
|
|
52
|
+
}
|
|
53
|
+
async flushPostgresWrites() {
|
|
54
|
+
await this.postgresReady;
|
|
55
|
+
await this.writes.flush();
|
|
56
|
+
}
|
|
57
|
+
async shutdownPostgres() {
|
|
58
|
+
if (this.shutdownComplete)
|
|
59
|
+
return;
|
|
60
|
+
if (!this.shutdownPromise) {
|
|
61
|
+
const attempt = this.performPostgresShutdown()
|
|
62
|
+
.then(() => {
|
|
63
|
+
this.shutdownComplete = true;
|
|
64
|
+
})
|
|
65
|
+
.finally(() => {
|
|
66
|
+
if (this.shutdownPromise === attempt && !this.shutdownComplete) {
|
|
67
|
+
this.shutdownPromise = null;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
this.shutdownPromise = attempt;
|
|
71
|
+
}
|
|
72
|
+
await this.shutdownPromise;
|
|
73
|
+
}
|
|
74
|
+
shutdown() {
|
|
75
|
+
void this.shutdownPostgres().catch(() => undefined);
|
|
76
|
+
}
|
|
77
|
+
async performPostgresShutdown() {
|
|
78
|
+
this.stopQueueRefreshes = true;
|
|
79
|
+
this.dirtyQueues.clear();
|
|
80
|
+
this.projectionRefreshes.close();
|
|
81
|
+
const operationDrain = this.operations.closeAndDrain();
|
|
82
|
+
await operationDrain;
|
|
83
|
+
this.stopBaseRuntime();
|
|
84
|
+
this.unsubscribeEvent();
|
|
85
|
+
this.unsubscribeInvalidation();
|
|
86
|
+
const errors = await this.writes.closeAndDrain();
|
|
87
|
+
try {
|
|
88
|
+
await this.postgresReady;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
errors.push(error);
|
|
92
|
+
}
|
|
93
|
+
await Promise.allSettled([...this.refreshes.values(), this.projectionRefreshes.drain()]);
|
|
94
|
+
try {
|
|
95
|
+
await this.postgresStore.close();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
errors.push(error);
|
|
99
|
+
}
|
|
100
|
+
throwDeferredWriteErrors(errors, 'PostgreSQL shutdown failed');
|
|
101
|
+
}
|
|
102
|
+
tokenFor(id, token) {
|
|
103
|
+
return token ?? this.activeTokens.get(id) ?? this.postgresSnapshot.get(id)?.token ?? null;
|
|
104
|
+
}
|
|
105
|
+
forgetToken(id) {
|
|
106
|
+
this.activeTokens.delete(id);
|
|
107
|
+
}
|
|
108
|
+
runPostgresOperation(operation) {
|
|
109
|
+
return this.operations.run(operation);
|
|
110
|
+
}
|
|
111
|
+
async refreshJob(id, queue) {
|
|
112
|
+
const targetQueue = queue ?? this.postgresSnapshot.get(id)?.job.queue ?? '';
|
|
113
|
+
try {
|
|
114
|
+
await this.projectionRefreshes.refreshNow(id, targetQueue);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
this.projectionRefreshes.request(id, targetQueue);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async fetchPostgresJob(id) {
|
|
121
|
+
const queue = this.postgresSnapshot.get(id)?.job.queue ?? '';
|
|
122
|
+
return (await this.projectionRefreshes.refreshNow(id, queue)).row;
|
|
123
|
+
}
|
|
124
|
+
async refreshJobs(ids, queue) {
|
|
125
|
+
const requests = [...new Set(ids)].map((id) => ({
|
|
126
|
+
id,
|
|
127
|
+
queue: queue ?? this.postgresSnapshot.get(id)?.job.queue ?? '',
|
|
128
|
+
}));
|
|
129
|
+
try {
|
|
130
|
+
await this.projectionRefreshes.refreshManyNow(requests);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
for (const request of requests)
|
|
134
|
+
this.projectionRefreshes.request(request.id, request.queue);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async refreshQueue(queue) {
|
|
138
|
+
const eventVersion = this.queueEventVersions.get(queue) ?? 0;
|
|
139
|
+
const { rows, state, exists, results } = await this.postgresStore.loadQueueReadModel(queue);
|
|
140
|
+
if (eventVersion !== (this.queueEventVersions.get(queue) ?? 0)) {
|
|
141
|
+
this.dirtyQueues.add(queue);
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
this.projectionRefreshes.supersedeQueue(queue);
|
|
145
|
+
if (!exists) {
|
|
146
|
+
for (const id of this.postgresSnapshot.removeQueue(queue))
|
|
147
|
+
this.activeTokens.delete(id);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
this.postgresSnapshot.replaceQueue(queue, rows, state, results);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
async refreshQueueAfterCommit(queue) {
|
|
154
|
+
try {
|
|
155
|
+
if (!(await this.refreshQueue(queue)))
|
|
156
|
+
this.scheduleQueueRefresh(queue);
|
|
157
|
+
else
|
|
158
|
+
this.postgresStore.reportQueueRefresh(queue, null);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.postgresStore.reportQueueRefresh(queue, error);
|
|
162
|
+
this.scheduleQueueRefresh(queue);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
enqueueWrite(operation) {
|
|
166
|
+
this.writes.enqueue(operation);
|
|
167
|
+
}
|
|
168
|
+
async initializePostgres() {
|
|
169
|
+
await this.postgresStore.initialize();
|
|
170
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
171
|
+
const buffer = this.snapshotEventBuffer;
|
|
172
|
+
if (!buffer)
|
|
173
|
+
return;
|
|
174
|
+
buffer.reset();
|
|
175
|
+
const { rows, results, crons, states } = await this.postgresStore.loadManagerSnapshot();
|
|
176
|
+
const events = buffer.take();
|
|
177
|
+
if (!events && attempt === 0)
|
|
178
|
+
continue;
|
|
179
|
+
this.postgresSnapshot.hydrate(rows);
|
|
180
|
+
this.postgresSnapshot.hydrateResults(results);
|
|
181
|
+
this.cronScheduler.load([...crons]);
|
|
182
|
+
for (const state of states)
|
|
183
|
+
this.postgresSnapshot.setQueueState(state);
|
|
184
|
+
for (const event of events ?? []) {
|
|
185
|
+
this.postgresSnapshot.apply(event);
|
|
186
|
+
applyPostgresEventMetrics(this.metrics, this.perQueueMetrics, event, false);
|
|
187
|
+
}
|
|
188
|
+
await this.postgresStore.finalizeLifetimeMetrics((lifetimeMetrics) => {
|
|
189
|
+
hydratePostgresLifetimeMetrics(this.metrics, this.perQueueMetrics, lifetimeMetrics);
|
|
190
|
+
this.terminalMetricBaselineCommitSeq = lifetimeMetrics.baselineCommitSeq;
|
|
191
|
+
this.snapshotEventBuffer = null;
|
|
192
|
+
this.projectionRefreshes.start();
|
|
193
|
+
});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
scheduleQueueRefresh(queue) {
|
|
198
|
+
if (this.stopQueueRefreshes)
|
|
199
|
+
return;
|
|
200
|
+
this.dirtyQueues.add(queue);
|
|
201
|
+
if (this.refreshes.has(queue))
|
|
202
|
+
return;
|
|
203
|
+
const refresh = this.postgresReady
|
|
204
|
+
.then(() => this.refreshDirtyQueue(queue))
|
|
205
|
+
.catch(() => {
|
|
206
|
+
this.dirtyQueues.delete(queue);
|
|
207
|
+
})
|
|
208
|
+
.finally(() => {
|
|
209
|
+
this.refreshes.delete(queue);
|
|
210
|
+
if (this.dirtyQueues.has(queue))
|
|
211
|
+
this.scheduleQueueRefresh(queue);
|
|
212
|
+
});
|
|
213
|
+
this.refreshes.set(queue, refresh);
|
|
214
|
+
}
|
|
215
|
+
async refreshDirtyQueue(queue) {
|
|
216
|
+
let retryDelay = this.postgresStore.config.pollIntervalMs;
|
|
217
|
+
while (!this.stopQueueRefreshes && this.dirtyQueues.delete(queue)) {
|
|
218
|
+
try {
|
|
219
|
+
const applied = await this.refreshQueue(queue);
|
|
220
|
+
if (!applied && !this.stopQueueRefreshes) {
|
|
221
|
+
await Bun.sleep(this.postgresStore.config.pollIntervalMs);
|
|
222
|
+
}
|
|
223
|
+
this.postgresStore.reportQueueRefresh(queue, null);
|
|
224
|
+
retryDelay = this.postgresStore.config.pollIntervalMs;
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
this.postgresStore.reportQueueRefresh(queue, error);
|
|
228
|
+
if (this.stopQueueRefreshes)
|
|
229
|
+
return;
|
|
230
|
+
this.dirtyQueues.add(queue);
|
|
231
|
+
await Bun.sleep(retryDelay);
|
|
232
|
+
retryDelay = Math.min(retryDelay * 2, 1_000);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
onStoreEvent(event) {
|
|
237
|
+
this.queueEventVersions.set(event.queue, (this.queueEventVersions.get(event.queue) ?? 0) + 1);
|
|
238
|
+
const startupBuffer = this.snapshotEventBuffer;
|
|
239
|
+
startupBuffer?.capture(event);
|
|
240
|
+
if (startupBuffer)
|
|
241
|
+
this.postgresSnapshot.apply(event);
|
|
242
|
+
if (startupBuffer?.overflowed)
|
|
243
|
+
this.scheduleQueueRefresh(event.queue);
|
|
244
|
+
else
|
|
245
|
+
this.projectionRefreshes.request(event.jobId, event.queue);
|
|
246
|
+
const mapped = postgresEventType(event.type);
|
|
247
|
+
if (!mapped)
|
|
248
|
+
return;
|
|
249
|
+
if (!startupBuffer) {
|
|
250
|
+
applyPostgresEventMetrics(this.metrics, this.perQueueMetrics, event, event.commitSeq > this.terminalMetricBaselineCommitSeq);
|
|
251
|
+
}
|
|
252
|
+
this.eventsManager.broadcast({
|
|
253
|
+
eventType: mapped,
|
|
254
|
+
queue: event.queue,
|
|
255
|
+
jobId: event.jobId,
|
|
256
|
+
timestamp: event.occurredAt,
|
|
257
|
+
...(Object.hasOwn(event, 'result') && { data: event.result }),
|
|
258
|
+
...(event.error !== undefined && { error: event.error }),
|
|
259
|
+
...(mapped === 'failed' && { terminal: event.state === 'failed' }),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
applyPostgresClaim(claim) {
|
|
263
|
+
this.projectionRefreshes.supersede(claim.job.id);
|
|
264
|
+
this.activeTokens.set(claim.job.id, claim.token);
|
|
265
|
+
this.postgresSnapshot.claim(claim);
|
|
266
|
+
}
|
|
267
|
+
stopBaseRuntime() {
|
|
268
|
+
if (this.baseStopped)
|
|
269
|
+
return;
|
|
270
|
+
this.baseStopped = true;
|
|
271
|
+
super.shutdown();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FailureReason } from '../../domain/types/dlq';
|
|
2
|
+
import type { JobId } from '../../domain/types/job';
|
|
3
|
+
import type { AckBatchOutcome, AckOutcome, CompletionOptions } from '../types/ack';
|
|
4
|
+
import { PostgresQueueManagerQueries } from './queries';
|
|
5
|
+
/** Terminal delivery transitions drained before the PostgreSQL pool closes. */
|
|
6
|
+
export declare class PostgresQueueManagerTerminalDelivery extends PostgresQueueManagerQueries {
|
|
7
|
+
ack(id: JobId, result?: unknown, token?: string, options?: CompletionOptions): Promise<AckOutcome>;
|
|
8
|
+
ackBatch(ids: JobId[], tokens?: string[]): Promise<AckBatchOutcome>;
|
|
9
|
+
ackBatchWithResults(items: Array<{
|
|
10
|
+
id: JobId;
|
|
11
|
+
result: unknown;
|
|
12
|
+
token?: string;
|
|
13
|
+
} & CompletionOptions>): Promise<AckBatchOutcome>;
|
|
14
|
+
fail(id: JobId, error?: string, token?: string, unrecoverable?: boolean, stack?: string[], removeOnFail?: boolean): Promise<AckOutcome>;
|
|
15
|
+
failWithReason(id: JobId, error: string | undefined, reason: FailureReason): Promise<AckOutcome>;
|
|
16
|
+
private requireToken;
|
|
17
|
+
private ackPostgres;
|
|
18
|
+
private completeBatch;
|
|
19
|
+
private failPostgres;
|
|
20
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { applyPostgresBatchCompletion } from './batchSnapshot';
|
|
2
|
+
import { PostgresQueueManagerQueries } from './queries';
|
|
3
|
+
/** Terminal delivery transitions drained before the PostgreSQL pool closes. */
|
|
4
|
+
export class PostgresQueueManagerTerminalDelivery extends PostgresQueueManagerQueries {
|
|
5
|
+
async ack(id, result, token, options = {}) {
|
|
6
|
+
return await this.runPostgresOperation(() => this.ackPostgres(id, result, token, options));
|
|
7
|
+
}
|
|
8
|
+
async ackBatch(ids, tokens) {
|
|
9
|
+
return await this.runPostgresOperation(() => this.completeBatch(ids.map((id, index) => ({ id, token: tokens?.[index] }))));
|
|
10
|
+
}
|
|
11
|
+
async ackBatchWithResults(items) {
|
|
12
|
+
return await this.runPostgresOperation(() => this.completeBatch(items));
|
|
13
|
+
}
|
|
14
|
+
// oxlint-disable-next-line max-params -- public compatibility signature carries failure policy
|
|
15
|
+
async fail(id, error, token, unrecoverable = false, stack, removeOnFail) {
|
|
16
|
+
return await this.runPostgresOperation(() => this.failPostgres(id, error, token, { unrecoverable, stack, removeOnFail }));
|
|
17
|
+
}
|
|
18
|
+
async failWithReason(id, error, reason) {
|
|
19
|
+
return await this.runPostgresOperation(() => this.failPostgres(id, error, undefined, {
|
|
20
|
+
unrecoverable: reason === "timeout" /* FailureReason.Timeout */,
|
|
21
|
+
failureReason: reason,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
requireToken(id, supplied) {
|
|
25
|
+
const token = this.tokenFor(id, supplied);
|
|
26
|
+
if (!token)
|
|
27
|
+
throw new Error(`Lock token required for job ${String(id)}`);
|
|
28
|
+
return token;
|
|
29
|
+
}
|
|
30
|
+
async ackPostgres(id, result, token, options) {
|
|
31
|
+
await this.postgresReady;
|
|
32
|
+
const transition = await this.postgresStore.complete(id, this.requireToken(id, token), result, options.removeOnComplete);
|
|
33
|
+
this.forgetToken(id);
|
|
34
|
+
await this.refreshJob(id);
|
|
35
|
+
if (transition.applied)
|
|
36
|
+
return;
|
|
37
|
+
if (transition.alreadyFinalized)
|
|
38
|
+
return { applied: false, reason: 'already-finalized' };
|
|
39
|
+
throw new Error(`Invalid or expired lock token for job ${String(id)}`);
|
|
40
|
+
}
|
|
41
|
+
async completeBatch(items) {
|
|
42
|
+
if (items.length === 0)
|
|
43
|
+
return;
|
|
44
|
+
if (new Set(items.map((item) => String(item.id))).size === items.length) {
|
|
45
|
+
await this.postgresReady;
|
|
46
|
+
const transitions = await this.postgresStore.completeMany(items.map((item) => ({
|
|
47
|
+
id: item.id,
|
|
48
|
+
token: this.requireToken(item.id, item.token),
|
|
49
|
+
result: item.result,
|
|
50
|
+
removeOnComplete: item.removeOnComplete,
|
|
51
|
+
})));
|
|
52
|
+
const repeatQueues = new Set();
|
|
53
|
+
const ignoredIds = [];
|
|
54
|
+
const ignoredIndices = [];
|
|
55
|
+
for (let index = 0; index < transitions.length; index++) {
|
|
56
|
+
const transition = transitions[index];
|
|
57
|
+
this.forgetToken(items[index].id);
|
|
58
|
+
const repeatQueue = applyPostgresBatchCompletion(this.postgresSnapshot, items[index], transition);
|
|
59
|
+
if (repeatQueue)
|
|
60
|
+
repeatQueues.add(repeatQueue);
|
|
61
|
+
if (!transition.applied) {
|
|
62
|
+
ignoredIndices.push(index);
|
|
63
|
+
ignoredIds.push(items[index].id);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
await Promise.all([
|
|
67
|
+
...ignoredIds.map((id) => this.refreshJob(id)),
|
|
68
|
+
...[...repeatQueues].map((queue) => this.refreshQueueAfterCommit(queue)),
|
|
69
|
+
]);
|
|
70
|
+
return ignoredIndices.length === 0
|
|
71
|
+
? undefined
|
|
72
|
+
: { ignoredIndices, ignoredIds: ignoredIndices.map((index) => items[index].id) };
|
|
73
|
+
}
|
|
74
|
+
const ignoredIndices = [];
|
|
75
|
+
for (let index = 0; index < items.length; index++) {
|
|
76
|
+
const outcome = await this.ackPostgres(items[index].id, items[index].result, items[index].token, items[index]);
|
|
77
|
+
if (outcome)
|
|
78
|
+
ignoredIndices.push(index);
|
|
79
|
+
}
|
|
80
|
+
return ignoredIndices.length === 0
|
|
81
|
+
? undefined
|
|
82
|
+
: { ignoredIndices, ignoredIds: ignoredIndices.map((index) => items[index].id) };
|
|
83
|
+
}
|
|
84
|
+
async failPostgres(id, error, token, options) {
|
|
85
|
+
await this.postgresReady;
|
|
86
|
+
const transition = await this.postgresStore.fail({
|
|
87
|
+
id,
|
|
88
|
+
token: this.requireToken(id, token),
|
|
89
|
+
error,
|
|
90
|
+
...options,
|
|
91
|
+
});
|
|
92
|
+
this.forgetToken(id);
|
|
93
|
+
await this.refreshJob(id);
|
|
94
|
+
if (transition.applied)
|
|
95
|
+
return;
|
|
96
|
+
if (transition.alreadyFinalized)
|
|
97
|
+
return { applied: false, reason: 'already-finalized' };
|
|
98
|
+
throw new Error(`Invalid or expired lock token for job ${String(id)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PostgresQueueManagerOperations as PostgresQueueManager } from './postgres-queue-manager/operations';
|
|
@@ -9,6 +9,10 @@ export class QueueManagerFlowFailures extends QueueManagerDependencyRuntime {
|
|
|
9
9
|
this.jobIndex.delete(entry.job.id);
|
|
10
10
|
this.jobResults.delete(entry.job.id);
|
|
11
11
|
this.jobLogs.delete(entry.job.id);
|
|
12
|
+
this.dependencyResults.releaseConsumer(entry.job.id);
|
|
13
|
+
if (entry.job.customId && this.customIdMap.get(entry.job.customId) === entry.job.id) {
|
|
14
|
+
this.customIdMap.delete(entry.job.customId);
|
|
15
|
+
}
|
|
12
16
|
}
|
|
13
17
|
this.storage?.purgeDlqEntries(entry.job.queue, [entry.job.id], ownsTerminal ? [entry.job.id] : [], false);
|
|
14
18
|
}
|
|
@@ -16,6 +16,8 @@ export declare class QueueManagerLimits extends QueueManagerControl {
|
|
|
16
16
|
retryDlq(queue: string, jobId?: JobId, limit?: number): number;
|
|
17
17
|
retryDlqByFilter(queue: string, filter: DlqFilter): number;
|
|
18
18
|
purgeDlq(queue: string): number;
|
|
19
|
+
/** Remove one terminal DLQ job without re-enqueuing it. */
|
|
20
|
+
removeDlqJob(queue: string, jobId: JobId): boolean;
|
|
19
21
|
retryCompleted(queue: string, jobId?: JobId, options?: {
|
|
20
22
|
limit?: number;
|
|
21
23
|
timestamp?: number;
|
|
@@ -37,6 +37,10 @@ export class QueueManagerLimits extends QueueManagerControl {
|
|
|
37
37
|
purgeDlq(queue) {
|
|
38
38
|
return dlqOps.purgeDlqJobs(queue, this.contextFactory.getDlqContext());
|
|
39
39
|
}
|
|
40
|
+
/** Remove one terminal DLQ job without re-enqueuing it. */
|
|
41
|
+
removeDlqJob(queue, jobId) {
|
|
42
|
+
return dlqOps.removeDlqJob(queue, jobId, this.contextFactory.getDlqContext());
|
|
43
|
+
}
|
|
40
44
|
retryCompleted(queue, jobId, options) {
|
|
41
45
|
return dlqOps.retryCompletedJobs(queue, this.contextFactory.getRetryCompletedContext(), jobId, options);
|
|
42
46
|
}
|
|
@@ -2,6 +2,7 @@ import * as logsOps from '../jobLogsManager';
|
|
|
2
2
|
import { generatePrometheusMetrics } from '../metricsExporter';
|
|
3
3
|
import { selectPrometheusQueues } from '../prometheusOperationalMetrics';
|
|
4
4
|
import * as statsMgr from '../statsManager';
|
|
5
|
+
import { isStorageDegraded } from '../../shared/storageHealth';
|
|
5
6
|
import { QueueManagerStats } from './stats';
|
|
6
7
|
export class QueueManagerObservability extends QueueManagerStats {
|
|
7
8
|
getQueueMetrics(queue, type, start = 0, end = -1) {
|
|
@@ -25,9 +26,20 @@ export class QueueManagerObservability extends QueueManagerStats {
|
|
|
25
26
|
getPrometheusMetrics() {
|
|
26
27
|
const storageStatus = this.getStorageStatus();
|
|
27
28
|
const memory = process.memoryUsage();
|
|
28
|
-
const queueSelection = selectPrometheusQueues(this.
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const queueSelection = selectPrometheusQueues(this.listQueues(), this.config.maxPrometheusQueues);
|
|
30
|
+
const countsByQueue = this.getQueueJobCountsBatch(queueSelection.selected);
|
|
31
|
+
const perQueueStats = new Map([...countsByQueue].map(([queue, counts]) => [
|
|
32
|
+
queue,
|
|
33
|
+
{
|
|
34
|
+
waiting: counts.waiting,
|
|
35
|
+
prioritized: counts.prioritized,
|
|
36
|
+
delayed: counts.delayed,
|
|
37
|
+
active: counts.active,
|
|
38
|
+
dlq: counts.failed,
|
|
39
|
+
},
|
|
40
|
+
]));
|
|
41
|
+
return generatePrometheusMetrics(this.getStats(), this.workerManager, this.webhookManager, perQueueStats, {
|
|
42
|
+
storageDegraded: isStorageDegraded(storageStatus),
|
|
31
43
|
storageDiskFull: storageStatus.diskFull,
|
|
32
44
|
...(this.storage && { sqliteDatabaseSizeBytes: this.storage.getSize() }),
|
|
33
45
|
processHeapUsedBytes: memory.heapUsed,
|
|
@@ -4,6 +4,13 @@ export declare class QueueManagerQueries extends QueueManagerLocks {
|
|
|
4
4
|
getJob(jobId: JobId): Promise<Job | null>;
|
|
5
5
|
getJobState(jobId: JobId): Promise<string>;
|
|
6
6
|
getResult(jobId: JobId): unknown;
|
|
7
|
+
/** Distinguish a completed generation from a valid undefined result. */
|
|
8
|
+
getCompletionAsync(jobId: JobId): Promise<{
|
|
9
|
+
found: boolean;
|
|
10
|
+
result: unknown;
|
|
11
|
+
}>;
|
|
12
|
+
/** Async result port used by transports; memory and SQLite remain synchronous internally. */
|
|
13
|
+
getResultAsync(jobId: JobId): Promise<unknown>;
|
|
7
14
|
getChildrenValues(parentJobId: JobId): Promise<Record<string, unknown>>;
|
|
8
15
|
updateJobParent(childJobId: JobId, parentJobId: JobId): Promise<void>;
|
|
9
16
|
getJobByCustomId(customId: string): Job | null;
|
|
@@ -14,6 +14,15 @@ export class QueueManagerQueries extends QueueManagerLocks {
|
|
|
14
14
|
getResult(jobId) {
|
|
15
15
|
return queryOps.getJobResult(jobId, this.contextFactory.getQueryContext());
|
|
16
16
|
}
|
|
17
|
+
/** Distinguish a completed generation from a valid undefined result. */
|
|
18
|
+
async getCompletionAsync(jobId) {
|
|
19
|
+
const found = this.completedJobs.has(jobId);
|
|
20
|
+
return { found, result: found ? this.getResult(jobId) : undefined };
|
|
21
|
+
}
|
|
22
|
+
/** Async result port used by transports; memory and SQLite remain synchronous internally. */
|
|
23
|
+
async getResultAsync(jobId) {
|
|
24
|
+
return (await this.getCompletionAsync(jobId)).result;
|
|
25
|
+
}
|
|
17
26
|
async getChildrenValues(parentJobId) {
|
|
18
27
|
const job = await this.getJob(parentJobId);
|
|
19
28
|
if (!job?.childrenIds || job.childrenIds.length === 0)
|
|
@@ -12,7 +12,7 @@ export declare class QueueManagerServices extends QueueManagerObservability {
|
|
|
12
12
|
setDashboardEmit(fn: (event: string, data: Record<string, unknown>) => void): void;
|
|
13
13
|
emitDashboardEvent(event: string, data: Record<string, unknown>): void;
|
|
14
14
|
subscribe(callback: (event: JobEvent) => void): () => void;
|
|
15
|
-
waitForJobCompletion(jobId: JobId, timeoutMs: number): Promise<boolean>;
|
|
15
|
+
waitForJobCompletion(jobId: JobId, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
|
|
16
16
|
registerWorker(name: string, queues: string[], concurrency?: number, options?: CreateWorkerOptions): import("../../domain/types/worker").Worker;
|
|
17
17
|
unregisterWorker(workerId: string): boolean;
|
|
18
18
|
unregisterWorkersByClientId(clientId: string): number;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { assertValidCronInput } from '../../infrastructure/scheduler/cron/validation';
|
|
1
2
|
import { shardIndex } from '../../shared/hash';
|
|
2
3
|
import { QueueManagerObservability } from './observability';
|
|
3
4
|
export class QueueManagerServices extends QueueManagerObservability {
|
|
4
5
|
addCron(input) {
|
|
6
|
+
assertValidCronInput(input);
|
|
5
7
|
if (input.preventOverlap) {
|
|
6
8
|
this.removeOrphanedCronJob(input.queue, input.uniqueKey ?? `cron:${input.name}`);
|
|
7
9
|
}
|
|
@@ -54,8 +56,8 @@ export class QueueManagerServices extends QueueManagerObservability {
|
|
|
54
56
|
subscribe(callback) {
|
|
55
57
|
return this.eventsManager.subscribe(callback);
|
|
56
58
|
}
|
|
57
|
-
waitForJobCompletion(jobId, timeoutMs) {
|
|
58
|
-
return this.eventsManager.waitForJobCompletion(jobId, timeoutMs);
|
|
59
|
+
waitForJobCompletion(jobId, timeoutMs, signal) {
|
|
60
|
+
return this.eventsManager.waitForJobCompletion(jobId, timeoutMs, signal);
|
|
59
61
|
}
|
|
60
62
|
registerWorker(name, queues, concurrency, options) {
|
|
61
63
|
const worker = this.workerManager.register(name, queues, concurrency, options);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Shard } from '../../domain/queue/shard';
|
|
2
2
|
import { SqliteStorage } from '../../infrastructure/persistence/sqlite';
|
|
3
3
|
import { CronScheduler } from '../../infrastructure/scheduler/cronScheduler';
|
|
4
|
+
import { assertPersistedCronsSupported } from '../../infrastructure/scheduler/cron/persisted';
|
|
4
5
|
import { RWLock } from '../../shared/lock';
|
|
5
6
|
import { SHARD_COUNT } from '../../shared/hash';
|
|
6
7
|
import { BoundedMap, BoundedSet, LRUMap } from '../../shared/lru';
|
|
@@ -83,6 +84,20 @@ export class QueueManagerState {
|
|
|
83
84
|
constructor(config = {}) {
|
|
84
85
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
85
86
|
this.storage = config.dataPath ? new SqliteStorage({ path: config.dataPath }) : null;
|
|
87
|
+
let persistedCrons;
|
|
88
|
+
try {
|
|
89
|
+
persistedCrons = this.storage?.loadCronJobs() ?? [];
|
|
90
|
+
assertPersistedCronsSupported(persistedCrons);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
try {
|
|
94
|
+
this.storage?.close();
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Preserve the read/validation error that made construction fail.
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
86
101
|
this.telemetryJournal = new QueueTelemetryJournal(this.storage, this.config.maxQueueEvents, this.config.maxMetricDataPoints);
|
|
87
102
|
this.completedJobsData = new BoundedMap(this.config.maxCompletedJobs);
|
|
88
103
|
this.completedJobs = new BoundedSet(this.config.maxCompletedJobs, (id) => {
|
|
@@ -138,7 +153,7 @@ export class QueueManagerState {
|
|
|
138
153
|
}
|
|
139
154
|
this.recoveryStats = { queues: this.queueNamesCache.size, jobs: this.jobIndex.size };
|
|
140
155
|
if (this.storage)
|
|
141
|
-
this.cronScheduler.load(
|
|
156
|
+
this.cronScheduler.load(persistedCrons);
|
|
142
157
|
this.backgroundTaskHandles = bgTasks.startBackgroundTasks(this.contextFactory.getBackgroundContext(), this.cronScheduler);
|
|
143
158
|
}
|
|
144
159
|
}
|
|
@@ -106,5 +106,5 @@ export async function runServer(args, showHelp) {
|
|
|
106
106
|
const mergedConfig = applyCliFlags(fileConfig, flags);
|
|
107
107
|
const config = resolveServerConfig(mergedConfig);
|
|
108
108
|
// Same full server as the bare `bunqueue` entry (shared bootstrap)
|
|
109
|
-
bootServer(mergedConfig, config);
|
|
109
|
+
await bootServer(mergedConfig, config);
|
|
110
110
|
}
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* The edge pattern: jobs buffer in the local queue (typically embedded
|
|
6
6
|
* SQLite on a gateway); the Forwarder drains them to a central server over
|
|
7
7
|
* TCP/TLS. A remote failure makes the local job fail → local retry/backoff →
|
|
8
|
-
* local DLQ
|
|
8
|
+
* local DLQ. With a persistent local `dataPath`, queued work survives uplink
|
|
9
|
+
* outages and process restarts; memory-only sources provide no such guarantee.
|
|
9
10
|
*
|
|
10
11
|
* Idempotency: every forwarded job carries the deterministic remote jobId
|
|
11
|
-
* `fwd:<localQueueKey>:<localJobId>`. The server dedupes custom jobIds
|
|
12
|
-
*
|
|
12
|
+
* `fwd:<localQueueKey>:<localJobId>`. The server dedupes custom jobIds while
|
|
13
|
+
* their ownership is retained; after bounded retention or removal, downstream
|
|
14
|
+
* effects must still tolerate at-least-once delivery.
|
|
13
15
|
*/
|
|
14
16
|
import { EventEmitter } from 'events';
|
|
15
17
|
import type { ConnectionOptions, QueueOptions } from './types';
|
|
@@ -21,7 +23,7 @@ export interface ForwardOptions {
|
|
|
21
23
|
queue?: string;
|
|
22
24
|
/** Parallel forwards (default: 4) */
|
|
23
25
|
concurrency?: number;
|
|
24
|
-
/** Push
|
|
26
|
+
/** Push with durable: true (bypasses SQLite buffering; PostgreSQL is already transactional). */
|
|
25
27
|
durable?: boolean;
|
|
26
28
|
}
|
|
27
29
|
/** Source queue identity + config, provided by Queue.forward() */
|
package/dist/client/forwarder.js
CHANGED
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* The edge pattern: jobs buffer in the local queue (typically embedded
|
|
6
6
|
* SQLite on a gateway); the Forwarder drains them to a central server over
|
|
7
7
|
* TCP/TLS. A remote failure makes the local job fail → local retry/backoff →
|
|
8
|
-
* local DLQ
|
|
8
|
+
* local DLQ. With a persistent local `dataPath`, queued work survives uplink
|
|
9
|
+
* outages and process restarts; memory-only sources provide no such guarantee.
|
|
9
10
|
*
|
|
10
11
|
* Idempotency: every forwarded job carries the deterministic remote jobId
|
|
11
|
-
* `fwd:<localQueueKey>:<localJobId>`. The server dedupes custom jobIds
|
|
12
|
-
*
|
|
12
|
+
* `fwd:<localQueueKey>:<localJobId>`. The server dedupes custom jobIds while
|
|
13
|
+
* their ownership is retained; after bounded retention or removal, downstream
|
|
14
|
+
* effects must still tolerate at-least-once delivery.
|
|
13
15
|
*/
|
|
14
16
|
import { EventEmitter } from 'events';
|
|
15
17
|
import { Worker } from './worker';
|
|
@@ -52,6 +52,8 @@ export declare function purgeDlq(ctx: DlqContext): number;
|
|
|
52
52
|
* purge-then-assert-empty patterns need this variant.
|
|
53
53
|
*/
|
|
54
54
|
export declare function purgeDlqAsync(ctx: DlqContext): Promise<number>;
|
|
55
|
+
/** Remove one DLQ entry without re-enqueuing it. */
|
|
56
|
+
export declare function removeDlqJobAsync(ctx: DlqContext, id: string): Promise<boolean>;
|
|
55
57
|
/** Retry completed job */
|
|
56
58
|
export declare function retryCompleted(ctx: DlqContext, id?: string): number;
|
|
57
59
|
/** Retry completed job (async) */
|
package/dist/client/queue/dlq.js
CHANGED
|
@@ -197,6 +197,20 @@ export async function purgeDlqAsync(ctx) {
|
|
|
197
197
|
return 0;
|
|
198
198
|
return (response.count ?? 0);
|
|
199
199
|
}
|
|
200
|
+
/** Remove one DLQ entry without re-enqueuing it. */
|
|
201
|
+
export async function removeDlqJobAsync(ctx, id) {
|
|
202
|
+
if (ctx.embedded)
|
|
203
|
+
return dlqOps.removeDlqJobEmbedded(ctx.name, id);
|
|
204
|
+
if (!ctx.tcp)
|
|
205
|
+
return false;
|
|
206
|
+
const response = await ctx.tcp.send({ cmd: 'RemoveDlqJob', queue: ctx.name, jobId: id });
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
const error = typeof response.error === 'string' ? response.error : 'Failed to remove the DLQ job';
|
|
209
|
+
throw new Error(error);
|
|
210
|
+
}
|
|
211
|
+
const payload = response;
|
|
212
|
+
return payload.data?.removed === true;
|
|
213
|
+
}
|
|
200
214
|
/** Retry completed job */
|
|
201
215
|
export function retryCompleted(ctx, id) {
|
|
202
216
|
if (ctx.embedded) {
|