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.
Files changed (324) hide show
  1. package/README.md +108 -76
  2. package/dist/application/background/dlq.js +2 -0
  3. package/dist/application/contextFactory.js +5 -0
  4. package/dist/application/dlqManager.d.ts +10 -0
  5. package/dist/application/dlqManager.js +47 -9
  6. package/dist/application/dlqRetry.js +59 -38
  7. package/dist/application/eventsManager.d.ts +1 -1
  8. package/dist/application/eventsManager.js +30 -26
  9. package/dist/application/operations/jobManagement.d.ts +4 -0
  10. package/dist/application/operations/jobMoveOperations.js +9 -4
  11. package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
  12. package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
  13. package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
  14. package/dist/application/postgres-queue-manager/cloud.js +55 -0
  15. package/dist/application/postgres-queue-manager/config.d.ts +5 -0
  16. package/dist/application/postgres-queue-manager/config.js +1 -0
  17. package/dist/application/postgres-queue-manager/control.d.ts +26 -0
  18. package/dist/application/postgres-queue-manager/control.js +186 -0
  19. package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
  20. package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
  21. package/dist/application/postgres-queue-manager/delivery.d.ts +19 -0
  22. package/dist/application/postgres-queue-manager/delivery.js +80 -0
  23. package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
  24. package/dist/application/postgres-queue-manager/lease.js +88 -0
  25. package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
  26. package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
  27. package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
  28. package/dist/application/postgres-queue-manager/maintenance.js +100 -0
  29. package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
  30. package/dist/application/postgres-queue-manager/operationGate.js +96 -0
  31. package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
  32. package/dist/application/postgres-queue-manager/operations.js +194 -0
  33. package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
  34. package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
  35. package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
  36. package/dist/application/postgres-queue-manager/queries.js +231 -0
  37. package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
  38. package/dist/application/postgres-queue-manager/relationships.js +45 -0
  39. package/dist/application/postgres-queue-manager/services.d.ts +31 -0
  40. package/dist/application/postgres-queue-manager/services.js +149 -0
  41. package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
  42. package/dist/application/postgres-queue-manager/snapshot.js +236 -0
  43. package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
  44. package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
  45. package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
  46. package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
  47. package/dist/application/postgres-queue-manager/state.d.ts +47 -0
  48. package/dist/application/postgres-queue-manager/state.js +273 -0
  49. package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
  50. package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
  51. package/dist/application/postgresQueueManager.d.ts +2 -0
  52. package/dist/application/postgresQueueManager.js +1 -0
  53. package/dist/application/queue-manager/flow-failures.js +4 -0
  54. package/dist/application/queue-manager/limits.d.ts +2 -0
  55. package/dist/application/queue-manager/limits.js +4 -0
  56. package/dist/application/queue-manager/observability.js +15 -3
  57. package/dist/application/queue-manager/queries.d.ts +7 -0
  58. package/dist/application/queue-manager/queries.js +9 -0
  59. package/dist/application/queue-manager/services.d.ts +1 -1
  60. package/dist/application/queue-manager/services.js +4 -2
  61. package/dist/application/queue-manager/state.js +16 -1
  62. package/dist/cli/commands/server.js +1 -1
  63. package/dist/client/forwarder.d.ts +6 -4
  64. package/dist/client/forwarder.js +5 -3
  65. package/dist/client/queue/dlq.d.ts +2 -0
  66. package/dist/client/queue/dlq.js +14 -0
  67. package/dist/client/queue/dlqOps.d.ts +2 -0
  68. package/dist/client/queue/dlqOps.js +4 -0
  69. package/dist/client/queue/helpers.js +2 -0
  70. package/dist/client/queue/runtime/configuration.d.ts +4 -0
  71. package/dist/client/queue/runtime/configuration.js +8 -0
  72. package/dist/client/worker/runtime/execution.js +3 -8
  73. package/dist/client/worker/runtime/polling.d.ts +1 -0
  74. package/dist/client/worker/runtime/polling.js +9 -0
  75. package/dist/config/resolve.d.ts +14 -0
  76. package/dist/config/resolve.js +50 -5
  77. package/dist/config/types.d.ts +18 -0
  78. package/dist/domain/queue/dlqShard.d.ts +2 -0
  79. package/dist/domain/queue/dlqShard.js +16 -0
  80. package/dist/domain/queue/shard/dlq.d.ts +1 -0
  81. package/dist/domain/queue/shard/dlq.js +3 -0
  82. package/dist/domain/types/commands/dlq.d.ts +5 -0
  83. package/dist/domain/types/commands/union.d.ts +1 -1
  84. package/dist/domain/types/cron.d.ts +5 -0
  85. package/dist/domain/types/cron.js +14 -3
  86. package/dist/domain/types/dlq.d.ts +1 -1
  87. package/dist/domain/types/dlq.js +1 -2
  88. package/dist/domain/types/jobs/model.d.ts +1 -1
  89. package/dist/infrastructure/cloud/commandHandler.js +5 -3
  90. package/dist/infrastructure/cloud/commands/integrations.js +11 -11
  91. package/dist/infrastructure/cloud/commands/jobs.js +48 -40
  92. package/dist/infrastructure/cloud/commands/queues.js +61 -75
  93. package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
  94. package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
  95. package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
  96. package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
  97. package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
  98. package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
  99. package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
  100. package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
  101. package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
  102. package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
  103. package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
  104. package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
  105. package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
  106. package/dist/infrastructure/cloud/snapshot/config.js +43 -0
  107. package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
  108. package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
  109. package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
  110. package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
  111. package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
  112. package/dist/infrastructure/cloud/statsRefresh.js +2 -1
  113. package/dist/infrastructure/cloud/types/command.d.ts +2 -2
  114. package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
  115. package/dist/infrastructure/persistence/postgres/admission.js +193 -0
  116. package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
  117. package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
  118. package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
  119. package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
  120. package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
  121. package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
  122. package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
  123. package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
  124. package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
  125. package/dist/infrastructure/persistence/postgres/batchAdmission.js +110 -0
  126. package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
  127. package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
  128. package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
  129. package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
  130. package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
  131. package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
  132. package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +11 -0
  133. package/dist/infrastructure/persistence/postgres/claimBatch.js +56 -0
  134. package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +4 -0
  135. package/dist/infrastructure/persistence/postgres/claimSelection.js +198 -0
  136. package/dist/infrastructure/persistence/postgres/claims.d.ts +3 -0
  137. package/dist/infrastructure/persistence/postgres/claims.js +137 -0
  138. package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
  139. package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
  140. package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
  141. package/dist/infrastructure/persistence/postgres/codec.js +82 -0
  142. package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
  143. package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
  144. package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
  145. package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
  146. package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
  147. package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
  148. package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
  149. package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
  150. package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
  151. package/dist/infrastructure/persistence/postgres/connection.js +15 -0
  152. package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
  153. package/dist/infrastructure/persistence/postgres/context.js +74 -0
  154. package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
  155. package/dist/infrastructure/persistence/postgres/control.js +105 -0
  156. package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
  157. package/dist/infrastructure/persistence/postgres/crons.js +205 -0
  158. package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
  159. package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
  160. package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
  161. package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
  162. package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
  163. package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
  164. package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
  165. package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
  166. package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
  167. package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
  168. package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
  169. package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
  170. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
  171. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
  172. package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
  173. package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
  174. package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
  175. package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
  176. package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
  177. package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +253 -0
  178. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +24 -0
  179. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +111 -0
  180. package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
  181. package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
  182. package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
  183. package/dist/infrastructure/persistence/postgres/events.js +249 -0
  184. package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
  185. package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
  186. package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
  187. package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
  188. package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
  189. package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
  190. package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
  191. package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
  192. package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
  193. package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
  194. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
  195. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
  196. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
  197. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
  198. package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
  199. package/dist/infrastructure/persistence/postgres/logs.js +70 -0
  200. package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
  201. package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
  202. package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
  203. package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
  204. package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
  205. package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
  206. package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
  207. package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
  208. package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
  209. package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
  210. package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
  211. package/dist/infrastructure/persistence/postgres/policies.js +18 -0
  212. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
  213. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
  214. package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
  215. package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
  216. package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
  217. package/dist/infrastructure/persistence/postgres/queries.js +152 -0
  218. package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
  219. package/dist/infrastructure/persistence/postgres/queueDestruction.js +154 -0
  220. package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
  221. package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
  222. package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
  223. package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
  224. package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
  225. package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
  226. package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
  227. package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
  228. package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
  229. package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
  230. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
  231. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
  232. package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
  233. package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
  234. package/dist/infrastructure/persistence/postgres/runtime.d.ts +46 -0
  235. package/dist/infrastructure/persistence/postgres/runtime.js +236 -0
  236. package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
  237. package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
  238. package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
  239. package/dist/infrastructure/persistence/postgres/schema.js +257 -0
  240. package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
  241. package/dist/infrastructure/persistence/postgres/schemaInitialization.js +221 -0
  242. package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
  243. package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
  244. package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
  245. package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
  246. package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
  247. package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
  248. package/dist/infrastructure/persistence/postgres/store.d.ts +90 -0
  249. package/dist/infrastructure/persistence/postgres/store.js +136 -0
  250. package/dist/infrastructure/persistence/postgres/telemetry.d.ts +6 -0
  251. package/dist/infrastructure/persistence/postgres/telemetry.js +88 -0
  252. package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
  253. package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
  254. package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
  255. package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
  256. package/dist/infrastructure/persistence/postgres/types.d.ts +175 -0
  257. package/dist/infrastructure/persistence/postgres/types.js +1 -0
  258. package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
  259. package/dist/infrastructure/persistence/postgres/workers.js +126 -0
  260. package/dist/infrastructure/persistence/postgres.d.ts +2 -0
  261. package/dist/infrastructure/persistence/postgres.js +1 -0
  262. package/dist/infrastructure/persistence/sqlite/jobs.js +2 -0
  263. package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
  264. package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
  265. package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
  266. package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
  267. package/dist/infrastructure/scheduler/cron/validation.js +11 -0
  268. package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
  269. package/dist/infrastructure/scheduler/cronParser.js +144 -11
  270. package/dist/infrastructure/server/bootstrap.d.ts +2 -3
  271. package/dist/infrastructure/server/bootstrap.js +33 -59
  272. package/dist/infrastructure/server/errors.d.ts +4 -0
  273. package/dist/infrastructure/server/errors.js +54 -0
  274. package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
  275. package/dist/infrastructure/server/handler-routes/control.js +3 -1
  276. package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
  277. package/dist/infrastructure/server/handler.js +9 -11
  278. package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
  279. package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
  280. package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
  281. package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
  282. package/dist/infrastructure/server/handlers/core.js +32 -31
  283. package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
  284. package/dist/infrastructure/server/handlers/cron.js +60 -59
  285. package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
  286. package/dist/infrastructure/server/handlers/dashboard.js +21 -8
  287. package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
  288. package/dist/infrastructure/server/handlers/dlq.js +35 -9
  289. package/dist/infrastructure/server/handlers/flow.js +2 -1
  290. package/dist/infrastructure/server/handlers/management.d.ts +5 -5
  291. package/dist/infrastructure/server/handlers/management.js +36 -9
  292. package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
  293. package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
  294. package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
  295. package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
  296. package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
  297. package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
  298. package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
  299. package/dist/infrastructure/server/handlers/pushBatchValidation.js +52 -7
  300. package/dist/infrastructure/server/handlers/query.d.ts +1 -1
  301. package/dist/infrastructure/server/handlers/query.js +5 -4
  302. package/dist/infrastructure/server/http.js +2 -2
  303. package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
  304. package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
  305. package/dist/infrastructure/server/httpEndpoints.js +18 -7
  306. package/dist/infrastructure/server/httpRouteQueues.js +11 -1
  307. package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
  308. package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
  309. package/dist/infrastructure/server/sseHandler.js +8 -3
  310. package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
  311. package/dist/infrastructure/server/storageAdapter.js +61 -0
  312. package/dist/infrastructure/server/storageManager.d.ts +12 -0
  313. package/dist/infrastructure/server/storageManager.js +102 -0
  314. package/dist/infrastructure/server/tcp.js +2 -3
  315. package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
  316. package/dist/infrastructure/server/ws/snapshots.js +19 -4
  317. package/dist/infrastructure/server/wsHandler.js +10 -5
  318. package/dist/main.js +1 -1
  319. package/dist/mcp/backend/embedded/services.js +2 -1
  320. package/dist/mcp/index.d.ts +2 -2
  321. package/dist/mcp/index.js +2 -2
  322. package/dist/shared/storageHealth.d.ts +9 -0
  323. package/dist/shared/storageHealth.js +12 -0
  324. package/package.json +74 -65
@@ -0,0 +1,174 @@
1
+ import { createJob, generateJobId, MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
2
+ import { buildRepeatSuccessor } from '../../../application/repeatJobs';
3
+ import { decodePostgresJob, encodePostgresValue, postgresByteaBase64 } from './codec';
4
+ import { databaseNow } from './context';
5
+ import { admitPostgresJob } from './admission';
6
+ import { recordPostgresCompletionEvents } from './completionEvents';
7
+ import { planPostgresDependencyCompletion } from './dependencyPromotion';
8
+ import { runPostgresTransactionWithRetry } from './transactionRetry';
9
+ import { tryLockPostgresRepeatQueues } from './queueLifecycle';
10
+ function invalidToken(id) {
11
+ return new Error(`Invalid or expired lock token for job ${id}`);
12
+ }
13
+ function prepareCompletions(rows, inputs, now) {
14
+ const byId = new Map(rows.map((row) => [row.id, row]));
15
+ const applied = [];
16
+ const results = inputs.map((input) => {
17
+ const row = byId.get(String(input.id));
18
+ if (!row)
19
+ throw invalidToken(String(input.id));
20
+ if (row.state === 'completed' || row.state === 'failed') {
21
+ return {
22
+ applied: false,
23
+ alreadyFinalized: true,
24
+ job: decodePostgresJob(row).job,
25
+ state: row.state,
26
+ removed: false,
27
+ };
28
+ }
29
+ if (row.state !== 'active' ||
30
+ row.lease_token !== input.token ||
31
+ Number(row.lease_until ?? 0) <= now) {
32
+ throw invalidToken(String(input.id));
33
+ }
34
+ const job = decodePostgresJob(row).job;
35
+ job.completedAt = now;
36
+ job.progress = 100;
37
+ if (job.timeline.length < MAX_TIMELINE_ENTRIES) {
38
+ job.timeline.push({ state: 'completed', timestamp: now });
39
+ }
40
+ const removed = job.removeOnComplete || input.removeOnComplete === true;
41
+ applied.push({
42
+ input,
43
+ job,
44
+ payload: encodePostgresValue(job),
45
+ resultPayload: encodePostgresValue(input.result),
46
+ removed,
47
+ });
48
+ return { applied: true, alreadyFinalized: false, job, state: 'completed', removed };
49
+ });
50
+ return { applied, results };
51
+ }
52
+ async function lockRows(tx, ctx, inputs) {
53
+ const ids = inputs.map((input) => String(input.id)).sort();
54
+ return await tx `
55
+ SELECT * FROM bunqueue_jobs
56
+ WHERE namespace = ${ctx.config.namespace}
57
+ AND id = ANY(${tx.array(ids, 'TEXT')})
58
+ ORDER BY id
59
+ FOR UPDATE
60
+ `;
61
+ }
62
+ async function storeResults(tx, ctx, applied, now) {
63
+ await tx `
64
+ INSERT INTO bunqueue_completions (namespace, job_id, queue, completed_at, result)
65
+ SELECT
66
+ ${ctx.config.namespace}, batch.id, batch.queue, ${now},
67
+ decode(batch.result_base64, 'base64')
68
+ FROM unnest(
69
+ ${tx.array(applied.map(({ job }) => String(job.id)), 'TEXT')},
70
+ ${tx.array(applied.map(({ job }) => job.queue), 'TEXT')},
71
+ ${tx.array(applied.map(({ resultPayload }) => postgresByteaBase64(resultPayload)), 'TEXT')}
72
+ ) AS batch(id, queue, result_base64)
73
+ ON CONFLICT (namespace, job_id) DO UPDATE SET
74
+ queue = excluded.queue,
75
+ completed_at = excluded.completed_at,
76
+ result = excluded.result
77
+ `;
78
+ }
79
+ async function updateRetainedJobs(tx, ctx, applied, now) {
80
+ const retained = applied.filter((item) => !item.removed);
81
+ if (retained.length === 0)
82
+ return;
83
+ await tx `
84
+ UPDATE bunqueue_jobs AS job
85
+ SET payload = decode(batch.payload_base64, 'base64'),
86
+ state = 'completed',
87
+ completed_at = ${now},
88
+ result = decode(batch.result_base64, 'base64'),
89
+ lease_owner = NULL,
90
+ lease_token = NULL,
91
+ lease_broker_id = NULL, lease_broker_session_id = NULL,
92
+ lease_until = NULL,
93
+ dlq_retry_state = NULL,
94
+ version = job.version + 1
95
+ FROM unnest(
96
+ ${tx.array(retained.map(({ job }) => String(job.id)), 'TEXT')},
97
+ ${tx.array(retained.map(({ payload }) => postgresByteaBase64(payload)), 'TEXT')},
98
+ ${tx.array(retained.map(({ resultPayload }) => postgresByteaBase64(resultPayload)), 'TEXT')}
99
+ ) AS batch(id, payload_base64, result_base64)
100
+ WHERE job.namespace = ${ctx.config.namespace} AND job.id = batch.id
101
+ `;
102
+ }
103
+ async function deleteRemovedJobs(tx, ctx, applied) {
104
+ const ids = applied.filter((item) => item.removed).map(({ job }) => String(job.id));
105
+ if (ids.length === 0)
106
+ return;
107
+ await tx `
108
+ DELETE FROM bunqueue_job_logs
109
+ WHERE namespace = ${ctx.config.namespace} AND job_id = ANY(${tx.array(ids, 'TEXT')})
110
+ `;
111
+ await tx `
112
+ DELETE FROM bunqueue_dependencies
113
+ WHERE namespace = ${ctx.config.namespace} AND job_id = ANY(${tx.array(ids, 'TEXT')})
114
+ `;
115
+ await tx `
116
+ DELETE FROM bunqueue_jobs
117
+ WHERE namespace = ${ctx.config.namespace} AND id = ANY(${tx.array(ids, 'TEXT')})
118
+ `;
119
+ }
120
+ async function createRepeatSuccessors(tx, ctx, applied, now) {
121
+ for (const { job } of applied) {
122
+ const shouldRepeat = job.repeat !== null &&
123
+ (job.repeat.limit === undefined || job.repeat.count < job.repeat.limit);
124
+ const successorInput = shouldRepeat ? buildRepeatSuccessor(job, now) : null;
125
+ if (!successorInput)
126
+ continue;
127
+ const successor = createJob(generateJobId(), job.queue, successorInput, now);
128
+ const admitted = await admitPostgresJob(tx, ctx, successor, {
129
+ linkParent: false,
130
+ queueLifecycleLocksHeld: true,
131
+ });
132
+ await tx `
133
+ INSERT INTO bunqueue_repeat_links (namespace, original_id, successor_id)
134
+ VALUES (${ctx.config.namespace}, ${String(job.id)}, ${String(admitted.job.id)})
135
+ ON CONFLICT (namespace, original_id) DO UPDATE SET successor_id = excluded.successor_id
136
+ `;
137
+ }
138
+ }
139
+ /** Complete a unique ACK batch atomically with set-based persistence. */
140
+ export async function completePostgresJobs(ctx, inputs) {
141
+ if (inputs.length === 0)
142
+ return [];
143
+ if (new Set(inputs.map((input) => String(input.id))).size !== inputs.length) {
144
+ throw new Error('PostgreSQL batch completion requires unique job IDs');
145
+ }
146
+ return await runPostgresTransactionWithRetry(ctx, async (tx) => {
147
+ const now = await databaseNow(tx);
148
+ const dependencyCompletion = await planPostgresDependencyCompletion(tx, ctx, inputs.map(({ id }) => id));
149
+ const rows = await lockRows(tx, ctx, inputs);
150
+ const { applied, results } = prepareCompletions(rows, inputs, now);
151
+ if (applied.length === 0)
152
+ return results;
153
+ const repeatQueues = applied.flatMap(({ job }) => {
154
+ const repeats = job.repeat !== null &&
155
+ (job.repeat.limit === undefined || job.repeat.count < job.repeat.limit);
156
+ return repeats ? [job.queue] : [];
157
+ });
158
+ if (!(await tryLockPostgresRepeatQueues(tx, ctx, repeatQueues))) {
159
+ throw new Error('A repeat queue is being obliterated; retry batch completion');
160
+ }
161
+ const ids = applied.map(({ job }) => String(job.id));
162
+ await tx `
163
+ DELETE FROM bunqueue_flow_failures
164
+ WHERE namespace = ${ctx.config.namespace} AND parent_id = ANY(${tx.array(ids, 'TEXT')})
165
+ `;
166
+ await storeResults(tx, ctx, applied, now);
167
+ await updateRetainedJobs(tx, ctx, applied, now);
168
+ await deleteRemovedJobs(tx, ctx, applied);
169
+ await recordPostgresCompletionEvents(tx, ctx, applied.map(({ job, input, removed }) => ({ job, result: input.result, removed })), now);
170
+ await dependencyCompletion.promote(now);
171
+ await createRepeatSuccessors(tx, ctx, applied, now);
172
+ return results;
173
+ });
174
+ }
@@ -0,0 +1,14 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import type { Job } from '../../../domain/types/job';
3
+ import type { PostgresContext } from './context';
4
+ import type { PostgresJobState } from './types';
5
+ export interface PostgresBatchJobEventInput {
6
+ readonly job: Job;
7
+ readonly type: string;
8
+ readonly state?: PostgresJobState;
9
+ readonly result?: unknown;
10
+ readonly error?: string;
11
+ readonly removed?: boolean;
12
+ }
13
+ /** Persist job events while amortizing retention and wakeups per affected queue. */
14
+ export declare function recordPostgresJobEvents(tx: TransactionSQL, ctx: PostgresContext, inputs: readonly PostgresBatchJobEventInput[], now: number): Promise<void>;
@@ -0,0 +1,72 @@
1
+ import { eventPayload, postgresByteaBase64 } from './codec';
2
+ import { recordPostgresEventPruneWatermarks, summarizePostgresPrunedEvents, } from './eventPruneWatermarks';
3
+ import { prunePostgresQueueEvents, tryLockPostgresEventRetention } from './eventRetention';
4
+ import { POSTGRES_EVENT_CHANNEL } from './schema';
5
+ function payloadForEvent(input) {
6
+ return eventPayload({
7
+ job: input.job,
8
+ ...(input.state && { state: input.state }),
9
+ ...(Object.hasOwn(input, 'result') && { result: input.result }),
10
+ ...(input.error !== undefined && { error: input.error }),
11
+ ...(input.removed !== undefined && { removed: input.removed }),
12
+ });
13
+ }
14
+ async function insertEvents(tx, ctx, inputs, now) {
15
+ const inserted = await tx `
16
+ INSERT INTO bunqueue_events
17
+ (namespace, queue, event_type, job_id, occurred_at, payload)
18
+ SELECT
19
+ ${ctx.config.namespace}, batch.queue, batch.event_type, batch.job_id, ${now},
20
+ decode(batch.payload_base64, 'base64')
21
+ FROM unnest(
22
+ ${tx.array(inputs.map((input) => input.job.queue), 'TEXT')},
23
+ ${tx.array(inputs.map((input) => input.type), 'TEXT')},
24
+ ${tx.array(inputs.map((input) => String(input.job.id)), 'TEXT')},
25
+ ${tx.array(inputs.map((input) => postgresByteaBase64(payloadForEvent(input))), 'TEXT')}
26
+ ) WITH ORDINALITY AS batch(queue, event_type, job_id, payload_base64, position)
27
+ ORDER BY batch.position
28
+ RETURNING id
29
+ `;
30
+ const ids = inserted.map((row) => Number(row.id));
31
+ return { startId: Math.min(...ids), endId: Math.max(...ids) };
32
+ }
33
+ async function trimEvents(tx, ctx, queues, sourceEventId) {
34
+ const watermarks = [];
35
+ const skippedQueues = new Set();
36
+ for (const queue of [...queues].sort()) {
37
+ if (!(await tryLockPostgresEventRetention(tx, ctx, queue))) {
38
+ skippedQueues.add(queue);
39
+ continue;
40
+ }
41
+ const pruned = await prunePostgresQueueEvents(tx, ctx, queue, ctx.config.maxQueueEvents);
42
+ const prune = summarizePostgresPrunedEvents(pruned);
43
+ if (prune.prunedThrough > 0)
44
+ watermarks.push({ queue, sourceEventId, ...prune });
45
+ }
46
+ await recordPostgresEventPruneWatermarks(tx, ctx, watermarks);
47
+ return {
48
+ watermarks: new Map(watermarks.map(({ queue, prunedThrough }) => [queue, prunedThrough])),
49
+ skippedQueues,
50
+ };
51
+ }
52
+ /** Persist job events while amortizing retention and wakeups per affected queue. */
53
+ export async function recordPostgresJobEvents(tx, ctx, inputs, now) {
54
+ if (inputs.length === 0)
55
+ return;
56
+ const queues = [...new Set(inputs.map((input) => input.job.queue))];
57
+ const eventRange = await insertEvents(tx, ctx, inputs, now);
58
+ const { watermarks, skippedQueues } = await trimEvents(tx, ctx, queues, eventRange.endId);
59
+ for (const queue of queues) {
60
+ const prunedThrough = watermarks.get(queue);
61
+ await tx.notify(POSTGRES_EVENT_CHANNEL, JSON.stringify({
62
+ namespace: ctx.config.namespace,
63
+ queue,
64
+ brokerId: ctx.config.brokerId,
65
+ eventStartId: eventRange.startId,
66
+ eventEndId: eventRange.endId,
67
+ ...(prunedThrough !== undefined && { prunedThrough }),
68
+ ...(skippedQueues.has(queue) && { retentionRequested: true }),
69
+ ...(watermarks.size > 0 && { scanPruneWatermarks: true }),
70
+ }));
71
+ }
72
+ }
@@ -0,0 +1,14 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import { type PostgresContext } from './context';
3
+ export declare class PostgresBrokerIdentityInUseError extends Error {
4
+ constructor(brokerId: string);
5
+ }
6
+ export declare class PostgresBrokerSessionFencedError extends Error {
7
+ constructor(brokerId: string);
8
+ }
9
+ export declare function postgresBrokerStaleMs(ctx: PostgresContext): number;
10
+ export declare function registerPostgresBroker(ctx: PostgresContext): Promise<void>;
11
+ export declare function lockPostgresBrokerSession(tx: TransactionSQL, ctx: PostgresContext): Promise<void>;
12
+ export declare function heartbeatPostgresBroker(ctx: PostgresContext): Promise<void>;
13
+ export declare function unregisterPostgresBroker(ctx: PostgresContext): Promise<void>;
14
+ export declare function purgeStalePostgresBrokers(ctx: PostgresContext): Promise<number>;
@@ -0,0 +1,94 @@
1
+ import { databaseNow } from './context';
2
+ export class PostgresBrokerIdentityInUseError extends Error {
3
+ constructor(brokerId) {
4
+ super(`PostgreSQL brokerId "${brokerId}" is already active`);
5
+ this.name = 'PostgresBrokerIdentityInUseError';
6
+ }
7
+ }
8
+ export class PostgresBrokerSessionFencedError extends Error {
9
+ constructor(brokerId) {
10
+ super(`PostgreSQL broker session for "${brokerId}" has been fenced`);
11
+ this.name = 'PostgresBrokerSessionFencedError';
12
+ }
13
+ }
14
+ export function postgresBrokerStaleMs(ctx) {
15
+ const heartbeatMs = Math.max(1000, Math.floor(ctx.config.leaseDurationMs / 3));
16
+ return Math.max(ctx.config.leaseDurationMs, heartbeatMs * 3);
17
+ }
18
+ export async function registerPostgresBroker(ctx) {
19
+ const now = await databaseNow(ctx.sql);
20
+ const staleBefore = now - postgresBrokerStaleMs(ctx);
21
+ const [existing] = await ctx.sql `
22
+ SELECT broker_id, session_id, heartbeat_at FROM bunqueue_brokers
23
+ WHERE namespace = ${ctx.config.namespace} AND broker_id = ${ctx.config.brokerId}
24
+ `;
25
+ if (existing &&
26
+ existing.session_id !== ctx.config.brokerSessionId &&
27
+ Number(existing.heartbeat_at) > staleBefore) {
28
+ throw new PostgresBrokerIdentityInUseError(ctx.config.brokerId);
29
+ }
30
+ const rows = await ctx.sql.begin(async (tx) => {
31
+ const transactionNow = await databaseNow(tx);
32
+ return await tx `
33
+ INSERT INTO bunqueue_brokers AS broker
34
+ (namespace, broker_id, session_id, started_at, heartbeat_at)
35
+ VALUES
36
+ (${ctx.config.namespace}, ${ctx.config.brokerId}, ${ctx.config.brokerSessionId},
37
+ ${transactionNow}, ${transactionNow})
38
+ ON CONFLICT (namespace, broker_id) DO UPDATE SET
39
+ session_id = excluded.session_id,
40
+ started_at = excluded.started_at,
41
+ heartbeat_at = excluded.heartbeat_at
42
+ WHERE broker.session_id IS NOT DISTINCT FROM excluded.session_id
43
+ OR broker.heartbeat_at <= ${transactionNow - postgresBrokerStaleMs(ctx)}
44
+ RETURNING broker_id
45
+ `;
46
+ });
47
+ if (rows.length === 0)
48
+ throw new PostgresBrokerIdentityInUseError(ctx.config.brokerId);
49
+ }
50
+ export async function lockPostgresBrokerSession(tx, ctx) {
51
+ const rows = await tx `
52
+ SELECT broker_id FROM bunqueue_brokers
53
+ WHERE namespace = ${ctx.config.namespace}
54
+ AND broker_id = ${ctx.config.brokerId}
55
+ AND session_id = ${ctx.config.brokerSessionId}
56
+ FOR SHARE
57
+ `;
58
+ if (rows.length === 0)
59
+ throw new PostgresBrokerSessionFencedError(ctx.config.brokerId);
60
+ }
61
+ export async function heartbeatPostgresBroker(ctx) {
62
+ const now = await databaseNow(ctx.sql);
63
+ const rows = await ctx.sql `
64
+ UPDATE bunqueue_brokers SET heartbeat_at = ${now}
65
+ WHERE namespace = ${ctx.config.namespace}
66
+ AND broker_id = ${ctx.config.brokerId}
67
+ AND session_id = ${ctx.config.brokerSessionId}
68
+ RETURNING broker_id
69
+ `;
70
+ if (rows.length === 0)
71
+ throw new PostgresBrokerSessionFencedError(ctx.config.brokerId);
72
+ }
73
+ export async function unregisterPostgresBroker(ctx) {
74
+ await ctx.sql `
75
+ DELETE FROM bunqueue_brokers
76
+ WHERE namespace = ${ctx.config.namespace}
77
+ AND broker_id = ${ctx.config.brokerId}
78
+ AND session_id = ${ctx.config.brokerSessionId}
79
+ `;
80
+ }
81
+ export async function purgeStalePostgresBrokers(ctx) {
82
+ const now = await databaseNow(ctx.sql);
83
+ const rows = await ctx.sql `
84
+ DELETE FROM bunqueue_brokers
85
+ WHERE namespace = ${ctx.config.namespace}
86
+ AND heartbeat_at <= ${now - postgresBrokerStaleMs(ctx)}
87
+ AND NOT (
88
+ broker_id = ${ctx.config.brokerId}
89
+ AND session_id IS NOT DISTINCT FROM ${ctx.config.brokerSessionId}
90
+ )
91
+ RETURNING broker_id
92
+ `;
93
+ return rows.length;
94
+ }
@@ -0,0 +1,11 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import type { PostgresContext } from './context';
3
+ import type { ClaimedPostgresJob } from './types';
4
+ /** Claim one queue batch after its queue-state row has been locked. */
5
+ export declare function claimPostgresCandidates(tx: TransactionSQL, ctx: PostgresContext, input: {
6
+ queue: string;
7
+ capacity: number;
8
+ owner: string;
9
+ requestedLeaseMs: number;
10
+ now: number;
11
+ }): Promise<ClaimedPostgresJob[]>;
@@ -0,0 +1,56 @@
1
+ import { generateLockToken, MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
2
+ import { recordPostgresJobEvents } from './batchEvents';
3
+ import { selectPostgresClaimCandidates } from './claimSelection';
4
+ import { decodePostgresJob, encodePostgresValue, postgresByteaBase64 } from './codec';
5
+ function prepareClaims(rows, input) {
6
+ const { owner, requestedLeaseMs, now, brokerId } = input;
7
+ return rows.map((row) => {
8
+ const job = decodePostgresJob(row).job;
9
+ const token = String(generateLockToken());
10
+ const leaseDuration = Math.max(1, Math.min(requestedLeaseMs, job.timeout === null ? requestedLeaseMs : job.timeout, job.stallTimeout === null ? requestedLeaseMs : job.stallTimeout));
11
+ const leaseUntil = now + leaseDuration;
12
+ job.startedAt = now;
13
+ job.lastHeartbeat = now;
14
+ if (job.timeline.length < MAX_TIMELINE_ENTRIES) {
15
+ job.timeline.push({ state: 'active', timestamp: now, worker: owner });
16
+ }
17
+ return { job, token, owner, brokerId, leaseUntil };
18
+ });
19
+ }
20
+ async function persistClaims(tx, ctx, claimed, now) {
21
+ await tx `
22
+ UPDATE bunqueue_jobs AS job
23
+ SET state = 'active',
24
+ payload = decode(batch.payload_base64, 'base64'),
25
+ started_at = ${now},
26
+ lease_owner = ${claimed[0].owner},
27
+ lease_broker_id = ${ctx.config.brokerId},
28
+ lease_broker_session_id = ${ctx.config.brokerSessionId},
29
+ lease_token = batch.token,
30
+ lease_until = batch.lease_until,
31
+ lease_renewals = 0,
32
+ version = job.version + 1
33
+ FROM unnest(
34
+ ${tx.array(claimed.map(({ job }) => String(job.id)), 'TEXT')},
35
+ ${tx.array(claimed.map(({ job }) => postgresByteaBase64(encodePostgresValue(job))), 'TEXT')},
36
+ ${tx.array(claimed.map(({ token }) => token), 'TEXT')},
37
+ ${tx.array(claimed.map(({ leaseUntil }) => leaseUntil), 'BIGINT')}
38
+ ) AS batch(id, payload_base64, token, lease_until)
39
+ WHERE job.namespace = ${ctx.config.namespace} AND job.id = batch.id
40
+ `;
41
+ await recordPostgresJobEvents(tx, ctx, claimed.map(({ job }) => ({ job, type: 'pulled', state: 'active' })), now);
42
+ }
43
+ /** Claim one queue batch after its queue-state row has been locked. */
44
+ export async function claimPostgresCandidates(tx, ctx, input) {
45
+ const { queue, capacity, owner, requestedLeaseMs, now } = input;
46
+ const rows = await selectPostgresClaimCandidates(tx, ctx, queue, now, capacity);
47
+ const claimed = prepareClaims(rows, {
48
+ owner,
49
+ requestedLeaseMs,
50
+ now,
51
+ brokerId: ctx.config.brokerId,
52
+ });
53
+ if (claimed.length > 0)
54
+ await persistClaims(tx, ctx, claimed, now);
55
+ return claimed;
56
+ }
@@ -0,0 +1,4 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import type { PostgresContext } from './context';
3
+ import type { PostgresJobRow } from './types';
4
+ export declare function selectPostgresClaimCandidates(tx: TransactionSQL, ctx: PostgresContext, queue: string, now: number, capacity: number): Promise<PostgresJobRow[]>;
@@ -0,0 +1,198 @@
1
+ async function hasClaimableGroupedJob(tx, ctx, queue, now) {
2
+ const [row] = await tx `
3
+ SELECT EXISTS (
4
+ SELECT 1
5
+ FROM bunqueue_jobs AS job
6
+ WHERE job.namespace = ${ctx.config.namespace}
7
+ AND job.queue = ${queue}
8
+ AND job.group_id IS NOT NULL
9
+ AND job.state IN ('waiting', 'prioritized', 'delayed')
10
+ AND job.run_at <= ${now}
11
+ AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
12
+ AND NOT EXISTS (
13
+ SELECT 1
14
+ FROM bunqueue_dependencies AS dependency
15
+ WHERE dependency.namespace = job.namespace
16
+ AND dependency.job_id = job.id
17
+ AND NOT EXISTS (
18
+ SELECT 1 FROM bunqueue_completions AS completion
19
+ WHERE completion.namespace = dependency.namespace
20
+ AND completion.job_id = dependency.dependency_id
21
+ )
22
+ )
23
+ AND NOT EXISTS (
24
+ SELECT 1 FROM bunqueue_jobs AS active_group
25
+ WHERE active_group.namespace = job.namespace
26
+ AND active_group.queue = job.queue
27
+ AND active_group.group_id = job.group_id
28
+ AND active_group.state = 'active'
29
+ AND active_group.lease_until > ${now}
30
+ )
31
+ ) AS exists
32
+ `;
33
+ return row.exists;
34
+ }
35
+ async function hasClaimableLifoJob(tx, ctx, queue, now) {
36
+ const [row] = await tx `
37
+ SELECT EXISTS (
38
+ SELECT 1 FROM bunqueue_jobs AS job
39
+ WHERE job.namespace = ${ctx.config.namespace}
40
+ AND job.queue = ${queue}
41
+ AND job.group_id IS NULL
42
+ AND job.lifo
43
+ AND job.state IN ('waiting', 'prioritized', 'delayed')
44
+ AND job.run_at <= ${now}
45
+ AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
46
+ ) AS exists
47
+ `;
48
+ return row.exists;
49
+ }
50
+ async function selectFifoCandidates(tx, ctx, queue, now, capacity) {
51
+ return await tx `
52
+ WITH selected AS (
53
+ SELECT job.id, job.priority, job.run_at
54
+ FROM bunqueue_jobs AS job
55
+ WHERE job.namespace = ${ctx.config.namespace}
56
+ AND job.queue = ${queue}
57
+ AND job.group_id IS NULL
58
+ AND NOT job.lifo
59
+ AND job.state IN ('waiting', 'prioritized', 'delayed')
60
+ AND job.run_at <= ${now}
61
+ AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
62
+ AND NOT EXISTS (
63
+ SELECT 1 FROM bunqueue_dependencies AS dependency
64
+ WHERE dependency.namespace = job.namespace
65
+ AND dependency.job_id = job.id
66
+ AND NOT EXISTS (
67
+ SELECT 1 FROM bunqueue_completions AS completion
68
+ WHERE completion.namespace = dependency.namespace
69
+ AND completion.job_id = dependency.dependency_id
70
+ )
71
+ )
72
+ ORDER BY job.priority DESC, job.run_at ASC, job.id ASC
73
+ LIMIT ${capacity}
74
+ FOR UPDATE OF job SKIP LOCKED
75
+ )
76
+ SELECT job.*
77
+ FROM bunqueue_jobs AS job
78
+ JOIN selected ON selected.id = job.id
79
+ WHERE job.namespace = ${ctx.config.namespace}
80
+ ORDER BY selected.priority DESC, selected.run_at ASC, selected.id ASC
81
+ `;
82
+ }
83
+ async function selectUngroupedCandidates(tx, ctx, queue, now, capacity) {
84
+ return await tx `
85
+ WITH selected AS (
86
+ SELECT job.id, job.priority, job.lifo, job.run_at
87
+ FROM bunqueue_jobs AS job
88
+ WHERE job.namespace = ${ctx.config.namespace}
89
+ AND job.queue = ${queue}
90
+ AND job.group_id IS NULL
91
+ AND job.state IN ('waiting', 'prioritized', 'delayed')
92
+ AND job.run_at <= ${now}
93
+ AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
94
+ AND NOT EXISTS (
95
+ SELECT 1 FROM bunqueue_dependencies AS dependency
96
+ WHERE dependency.namespace = job.namespace
97
+ AND dependency.job_id = job.id
98
+ AND NOT EXISTS (
99
+ SELECT 1 FROM bunqueue_completions AS completion
100
+ WHERE completion.namespace = dependency.namespace
101
+ AND completion.job_id = dependency.dependency_id
102
+ )
103
+ )
104
+ ORDER BY
105
+ job.priority DESC,
106
+ CASE WHEN job.lifo THEN 0 ELSE 1 END ASC,
107
+ CASE WHEN job.lifo THEN job.id END DESC,
108
+ CASE WHEN NOT job.lifo THEN job.run_at END ASC,
109
+ job.id ASC
110
+ LIMIT ${capacity}
111
+ FOR UPDATE OF job SKIP LOCKED
112
+ )
113
+ SELECT job.*
114
+ FROM bunqueue_jobs AS job
115
+ JOIN selected ON selected.id = job.id
116
+ WHERE job.namespace = ${ctx.config.namespace}
117
+ ORDER BY
118
+ selected.priority DESC,
119
+ CASE WHEN selected.lifo THEN 0 ELSE 1 END ASC,
120
+ CASE WHEN selected.lifo THEN selected.id END DESC,
121
+ CASE WHEN NOT selected.lifo THEN selected.run_at END ASC,
122
+ selected.id ASC
123
+ `;
124
+ }
125
+ async function selectGroupedCandidates(tx, ctx, queue, now, capacity) {
126
+ return await tx `
127
+ WITH eligible AS (
128
+ SELECT job.id, job.priority, job.lifo, job.run_at,
129
+ row_number() OVER (
130
+ PARTITION BY CASE
131
+ WHEN job.group_id IS NULL THEN 'job:' || job.id ELSE 'group:' || job.group_id
132
+ END
133
+ ORDER BY job.priority DESC,
134
+ CASE WHEN job.lifo THEN 0 ELSE 1 END ASC,
135
+ CASE WHEN job.lifo THEN job.id END DESC,
136
+ CASE WHEN NOT job.lifo THEN job.run_at END ASC, job.id ASC
137
+ ) AS group_position
138
+ FROM bunqueue_jobs AS job
139
+ WHERE job.namespace = ${ctx.config.namespace}
140
+ AND job.queue = ${queue}
141
+ AND job.state IN ('waiting', 'prioritized', 'delayed')
142
+ AND job.run_at <= ${now}
143
+ AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
144
+ AND NOT EXISTS (
145
+ SELECT 1 FROM bunqueue_dependencies AS dependency
146
+ WHERE dependency.namespace = job.namespace AND dependency.job_id = job.id
147
+ AND NOT EXISTS (
148
+ SELECT 1 FROM bunqueue_completions AS completion
149
+ WHERE completion.namespace = dependency.namespace
150
+ AND completion.job_id = dependency.dependency_id
151
+ )
152
+ )
153
+ AND (job.group_id IS NULL OR NOT EXISTS (
154
+ SELECT 1 FROM bunqueue_jobs AS active_group
155
+ WHERE active_group.namespace = job.namespace AND active_group.queue = job.queue
156
+ AND active_group.group_id = job.group_id AND active_group.state = 'active'
157
+ AND active_group.lease_until > ${now}
158
+ ))
159
+ )
160
+ SELECT job.*
161
+ FROM bunqueue_jobs AS job
162
+ JOIN eligible ON eligible.id = job.id
163
+ WHERE job.namespace = ${ctx.config.namespace}
164
+ AND eligible.group_position = 1
165
+ AND job.state IN ('waiting', 'prioritized', 'delayed')
166
+ AND job.run_at <= ${now}
167
+ AND (job.ttl IS NULL OR job.created_at + job.ttl >= ${now})
168
+ AND NOT EXISTS (
169
+ SELECT 1 FROM bunqueue_dependencies AS dependency
170
+ WHERE dependency.namespace = job.namespace AND dependency.job_id = job.id
171
+ AND NOT EXISTS (
172
+ SELECT 1 FROM bunqueue_completions AS completion
173
+ WHERE completion.namespace = dependency.namespace
174
+ AND completion.job_id = dependency.dependency_id
175
+ )
176
+ )
177
+ AND (job.group_id IS NULL OR NOT EXISTS (
178
+ SELECT 1 FROM bunqueue_jobs AS active_group
179
+ WHERE active_group.namespace = job.namespace AND active_group.queue = job.queue
180
+ AND active_group.group_id = job.group_id AND active_group.state = 'active'
181
+ AND active_group.lease_until > ${now}
182
+ ))
183
+ ORDER BY job.priority DESC,
184
+ CASE WHEN job.lifo THEN 0 ELSE 1 END ASC,
185
+ CASE WHEN job.lifo THEN job.id END DESC,
186
+ CASE WHEN NOT job.lifo THEN job.run_at END ASC, job.id ASC
187
+ LIMIT ${capacity}
188
+ FOR UPDATE OF job SKIP LOCKED
189
+ `;
190
+ }
191
+ export async function selectPostgresClaimCandidates(tx, ctx, queue, now, capacity) {
192
+ if (await hasClaimableGroupedJob(tx, ctx, queue, now)) {
193
+ return await selectGroupedCandidates(tx, ctx, queue, now, capacity);
194
+ }
195
+ return (await hasClaimableLifoJob(tx, ctx, queue, now))
196
+ ? await selectUngroupedCandidates(tx, ctx, queue, now, capacity)
197
+ : await selectFifoCandidates(tx, ctx, queue, now, capacity);
198
+ }
@@ -0,0 +1,3 @@
1
+ import { type PostgresContext } from './context';
2
+ import type { ClaimedPostgresJob } from './types';
3
+ export declare function claimPostgresJobs(ctx: PostgresContext, queue: string, count: number, owner: string, leaseDurationMs: number): Promise<ClaimedPostgresJob[]>;