bunqueue 2.8.60 → 2.9.2

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