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
@@ -7,6 +7,7 @@ import { hostname, arch, platform, cpus } from 'os';
7
7
  import { throughputTracker } from '../../application/throughputTracker';
8
8
  import { latencyTracker } from '../../application/latencyTracker';
9
9
  import { VERSION } from '../../shared/version';
10
+ import { clientStorageStatus } from '../../shared/storageHealth';
10
11
  /** Cached hostname — computed once */
11
12
  const HOST = hostname();
12
13
  /** Cached runtime info — computed once */
@@ -22,7 +23,7 @@ export function buildStatsRefresh(qm) {
22
23
  const rates = throughputTracker.getRates();
23
24
  const percentiles = latencyTracker.getPercentiles();
24
25
  const averages = latencyTracker.getAverages();
25
- const storage = qm.getStorageStatus();
26
+ const storage = clientStorageStatus(qm.getStorageStatus());
26
27
  const mem = process.memoryUsage();
27
28
  const workerStats = qm.workerManager.getStats();
28
29
  const queueNames = qm.listQueues();
@@ -1,4 +1,4 @@
1
- import type { QueueManager } from '../../../application/queueManager';
1
+ import type { CloudQueueAdapter } from '../queueAdapter/types';
2
2
  import type { CloudSnapshot } from './snapshot';
3
3
  /** Incoming command from the dashboard. */
4
4
  export interface CloudCommand {
@@ -41,4 +41,4 @@ export interface CommandContext {
41
41
  getSnapshot?: () => Promise<CloudSnapshot>;
42
42
  triggerBackup?: () => Promise<unknown>;
43
43
  }
44
- export type CloudCommandHandler = (queueManager: QueueManager, command: CloudCommand, context?: CommandContext) => unknown;
44
+ export type CloudCommandHandler = (adapter: CloudQueueAdapter, command: CloudCommand, context?: CommandContext) => unknown;
@@ -11,6 +11,7 @@ export interface StoredJobOptions {
11
11
  debounceId: string | null;
12
12
  debounceTtl: number | null;
13
13
  durable: boolean;
14
+ groupFifoOrder: string | null;
14
15
  }
15
16
  /** Persist generation policies that do not have dedicated legacy columns. */
16
17
  export declare function encodeJobOptions(job: Job): Uint8Array;
@@ -1,3 +1,4 @@
1
+ import { getGroupFifoOrder } from '../../domain/job/groupFifoOrder';
1
2
  import { storageLog } from '../../shared/logger';
2
3
  import { decodeMessagePack, encodeMessagePack } from '../../shared/msgpack';
3
4
  const DEFAULTS = {
@@ -12,10 +13,14 @@ const DEFAULTS = {
12
13
  debounceId: null,
13
14
  debounceTtl: null,
14
15
  durable: false,
16
+ groupFifoOrder: null,
15
17
  };
18
+ function validStoredOrder(value) {
19
+ return typeof value === 'string' && /^[1-9]\d*$/.test(value) ? value : null;
20
+ }
16
21
  /** Persist generation policies that do not have dedicated legacy columns. */
17
22
  export function encodeJobOptions(job) {
18
- return encodeMessagePack({
23
+ const options = {
19
24
  backoffConfig: job.backoffConfig,
20
25
  repeat: job.repeat,
21
26
  stackTraceLimit: job.stackTraceLimit,
@@ -27,7 +32,9 @@ export function encodeJobOptions(job) {
27
32
  debounceId: job.debounceId,
28
33
  debounceTtl: job.debounceTtl,
29
34
  durable: job.durable ?? false,
30
- });
35
+ };
36
+ const order = getGroupFifoOrder(job);
37
+ return encodeMessagePack(order === null ? options : { ...options, groupFifoOrder: String(order) });
31
38
  }
32
39
  /** Decode current blobs while giving pre-v34 rows their historical defaults. */
33
40
  export function decodeJobOptions(blob, context) {
@@ -47,6 +54,7 @@ export function decodeJobOptions(blob, context) {
47
54
  debounceId: decoded.debounceId ?? null,
48
55
  debounceTtl: decoded.debounceTtl ?? null,
49
56
  durable: decoded.durable ?? false,
57
+ groupFifoOrder: validStoredOrder(decoded.groupFifoOrder),
50
58
  };
51
59
  }
52
60
  catch (error) {
@@ -117,4 +117,15 @@ CREATE INDEX IF NOT EXISTS idx_queue_metric_buckets_window
117
117
  `,
118
118
  // Persist repeat-chain and advanced per-generation job policies.
119
119
  34: 'ALTER TABLE jobs ADD COLUMN extended_options BLOB;',
120
+ // Persist per-group rate and concurrency overrides.
121
+ 35: `
122
+ CREATE TABLE IF NOT EXISTS group_state (
123
+ queue TEXT NOT NULL,
124
+ group_id TEXT NOT NULL,
125
+ rate_limit INTEGER,
126
+ rate_duration INTEGER,
127
+ concurrency_limit INTEGER,
128
+ PRIMARY KEY (queue, group_id)
129
+ );
130
+ `,
120
131
  };
@@ -0,0 +1,18 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import { type Job, type JobId } from '../../../domain/types/job';
3
+ import { type PostgresContext } from './context';
4
+ import type { PostgresAdmissionResult } from './admissionResult';
5
+ export interface AdmitPostgresJobOptions {
6
+ readonly rejectExisting?: boolean;
7
+ readonly linkParent?: boolean;
8
+ readonly dependencyLocksHeld?: boolean;
9
+ readonly queueLifecycleLocksHeld?: boolean;
10
+ readonly dependencyExistenceCheckDeferred?: boolean;
11
+ readonly completionConsumerExemptions?: readonly JobId[];
12
+ /** Flow caller already holds identity locks and batches completion, queue, and event writes. */
13
+ readonly preparedFlow?: {
14
+ readonly now: number;
15
+ };
16
+ }
17
+ /** Insert one job inside the caller's transaction and return its authoritative generation. */
18
+ export declare function admitPostgresJob(tx: TransactionSQL, ctx: PostgresContext, job: Job, options?: AdmitPostgresJobOptions): Promise<PostgresAdmissionResult>;
@@ -0,0 +1,196 @@
1
+ import { MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
2
+ import { decodePostgresJob, encodePostgresValue, postgresStateForJob } from './codec';
3
+ import { databaseNow, recordPostgresEvent } from './context';
4
+ import { clearPostgresDeduplication, extendPostgresDeduplication, recordPostgresDeduplicationRemoval, } from './deduplication';
5
+ import { updatePostgresJobParentInTransaction } from './relationships';
6
+ import { assertPostgresDependenciesExist, lockPostgresDependencyCompletions, postgresDependenciesSatisfied, tryLockPostgresDependencyCompletions, } from './dependencyPromotion';
7
+ import { assertNoLivePostgresCompletionConsumers, retirePostgresCompletionGenerations, } from './completionLifecycle';
8
+ import { lockPostgresAdmissionKeys } from './batchAdmission';
9
+ import { linkedPostgresParentQueue, lockPostgresAdmissionParent } from './admissionParent';
10
+ import { lockPostgresAdmissionQueues, registerPostgresAdmissionQueues } from './queueLifecycle';
11
+ function isLive(state) {
12
+ return ['waiting', 'prioritized', 'delayed', 'waiting-children', 'active'].includes(state);
13
+ }
14
+ async function findById(tx, ctx, id) {
15
+ const rows = await tx `
16
+ SELECT * FROM bunqueue_jobs
17
+ WHERE namespace = ${ctx.config.namespace} AND id = ${id}
18
+ FOR UPDATE
19
+ `;
20
+ return rows[0] ? decodePostgresJob(rows[0]) : null;
21
+ }
22
+ async function findByUniqueKey(tx, ctx, job) {
23
+ if (!job.uniqueKey)
24
+ return null;
25
+ const rows = await tx `
26
+ SELECT * FROM bunqueue_jobs
27
+ WHERE namespace = ${ctx.config.namespace}
28
+ AND queue = ${job.queue}
29
+ AND unique_key = ${job.uniqueKey}
30
+ AND state IN ('waiting', 'prioritized', 'delayed', 'waiting-children', 'active')
31
+ ORDER BY created_at, id
32
+ LIMIT 1
33
+ FOR UPDATE
34
+ `;
35
+ return rows[0] ?? null;
36
+ }
37
+ async function deleteTerminalGeneration(tx, ctx, id) {
38
+ await tx `
39
+ DELETE FROM bunqueue_job_logs
40
+ WHERE namespace = ${ctx.config.namespace} AND job_id = ${id}
41
+ `;
42
+ await tx `
43
+ DELETE FROM bunqueue_repeat_links
44
+ WHERE namespace = ${ctx.config.namespace}
45
+ AND (original_id = ${id} OR successor_id = ${id})
46
+ `;
47
+ await tx `
48
+ DELETE FROM bunqueue_flow_failures
49
+ WHERE namespace = ${ctx.config.namespace} AND parent_id = ${id}
50
+ `;
51
+ await tx `
52
+ DELETE FROM bunqueue_dependencies
53
+ WHERE namespace = ${ctx.config.namespace} AND job_id = ${id}
54
+ `;
55
+ await tx `
56
+ DELETE FROM bunqueue_completions
57
+ WHERE namespace = ${ctx.config.namespace} AND job_id = ${id}
58
+ `;
59
+ await tx `
60
+ DELETE FROM bunqueue_jobs
61
+ WHERE namespace = ${ctx.config.namespace} AND id = ${id}
62
+ `;
63
+ }
64
+ async function insertDependencies(tx, ctx, job) {
65
+ for (const dependencyId of new Set(job.dependsOn.map(String))) {
66
+ await tx `
67
+ INSERT INTO bunqueue_dependencies (namespace, job_id, dependency_id)
68
+ VALUES (${ctx.config.namespace}, ${String(job.id)}, ${dependencyId})
69
+ ON CONFLICT DO NOTHING
70
+ `;
71
+ }
72
+ }
73
+ async function insertRow(tx, ctx, job, state, now) {
74
+ await tx `
75
+ INSERT INTO bunqueue_jobs (
76
+ namespace, id, queue, payload, state, priority, lifo, run_at, created_at,
77
+ started_at, completed_at, attempts, max_attempts, ttl, timeout, unique_key,
78
+ unique_expires_at, custom_id, group_id, group_order, parent_id
79
+ ) VALUES (
80
+ ${ctx.config.namespace}, ${String(job.id)}, ${job.queue}, ${encodePostgresValue(job)},
81
+ ${state}, ${job.priority}, ${job.lifo}, ${job.runAt}, ${job.createdAt},
82
+ ${job.startedAt}, ${job.completedAt}, ${job.attempts}, ${job.maxAttempts}, ${job.ttl},
83
+ ${job.timeout}, ${job.uniqueKey},
84
+ ${job.uniqueKey && job.deduplicationTtl !== null ? now + job.deduplicationTtl : null},
85
+ ${job.customId}, ${job.groupId},
86
+ CASE WHEN CAST(${job.groupId} AS TEXT) IS NULL
87
+ THEN NULL ELSE nextval('bunqueue_group_order_seq') END,
88
+ ${job.parentId}
89
+ )
90
+ `;
91
+ await insertDependencies(tx, ctx, job);
92
+ }
93
+ /** Insert one job inside the caller's transaction and return its authoritative generation. */
94
+ export async function admitPostgresJob(tx, ctx, job, options = {}) {
95
+ const parentQueue = options.linkParent === false ? null : linkedPostgresParentQueue(job);
96
+ if (!options.queueLifecycleLocksHeld) {
97
+ await lockPostgresAdmissionQueues(tx, ctx, [job.queue]);
98
+ }
99
+ if (!options.dependencyLocksHeld) {
100
+ await lockPostgresDependencyCompletions(tx, ctx, [job.id, ...job.dependsOn]);
101
+ }
102
+ if (parentQueue)
103
+ await lockPostgresAdmissionParent(tx, ctx, job, parentQueue);
104
+ if (!options.preparedFlow)
105
+ await lockPostgresAdmissionKeys(tx, ctx, [job]);
106
+ const now = options.preparedFlow?.now ?? (await databaseNow(tx));
107
+ const existingById = await findById(tx, ctx, String(job.id));
108
+ if (existingById) {
109
+ if (options.rejectExisting)
110
+ throw new Error(`Flow job ${String(job.id)} already exists`);
111
+ if (isLive(existingById.state)) {
112
+ return { job: existingById.job, inserted: false, state: existingById.state };
113
+ }
114
+ }
115
+ let existingByKeyRow = await findByUniqueKey(tx, ctx, job);
116
+ if (existingByKeyRow &&
117
+ existingByKeyRow.unique_expires_at !== null &&
118
+ Number(existingByKeyRow.unique_expires_at) <= now) {
119
+ await clearPostgresDeduplication(tx, ctx, existingByKeyRow, now);
120
+ existingByKeyRow = null;
121
+ }
122
+ if (existingByKeyRow) {
123
+ let existingByKey = decodePostgresJob(existingByKeyRow);
124
+ if (job.deduplicationReplace) {
125
+ if (existingByKeyRow.state === 'active') {
126
+ await clearPostgresDeduplication(tx, ctx, existingByKeyRow, now);
127
+ }
128
+ else {
129
+ const acquired = await tryLockPostgresDependencyCompletions(tx, ctx, [
130
+ existingByKey.job.id,
131
+ ]);
132
+ if (!acquired) {
133
+ await recordPostgresEvent(tx, ctx, {
134
+ queue: existingByKey.job.queue,
135
+ type: 'duplicated',
136
+ jobId: existingByKey.job.id,
137
+ occurredAt: now,
138
+ job: existingByKey.job,
139
+ state: existingByKey.state,
140
+ });
141
+ return { job: existingByKey.job, inserted: false, state: existingByKey.state };
142
+ }
143
+ await assertNoLivePostgresCompletionConsumers(tx, ctx, [existingByKey.job.id]);
144
+ await deleteTerminalGeneration(tx, ctx, existingByKeyRow.id);
145
+ await recordPostgresDeduplicationRemoval(tx, ctx, existingByKeyRow, now);
146
+ }
147
+ }
148
+ else {
149
+ if (job.deduplicationExtend && job.deduplicationTtl !== null) {
150
+ existingByKey = await extendPostgresDeduplication(tx, ctx, existingByKeyRow, job.deduplicationTtl, now);
151
+ }
152
+ await recordPostgresEvent(tx, ctx, {
153
+ queue: existingByKey.job.queue,
154
+ type: 'duplicated',
155
+ jobId: existingByKey.job.id,
156
+ occurredAt: now,
157
+ job: existingByKey.job,
158
+ state: existingByKey.state,
159
+ });
160
+ return { job: existingByKey.job, inserted: false, state: existingByKey.state };
161
+ }
162
+ }
163
+ if (existingById) {
164
+ await assertNoLivePostgresCompletionConsumers(tx, ctx, [job.id], options.completionConsumerExemptions);
165
+ await deleteTerminalGeneration(tx, ctx, String(job.id));
166
+ }
167
+ else if (!options.preparedFlow) {
168
+ await retirePostgresCompletionGenerations(tx, ctx, [job.id], options.completionConsumerExemptions);
169
+ }
170
+ if (!options.dependencyExistenceCheckDeferred) {
171
+ await assertPostgresDependenciesExist(tx, ctx, job.dependsOn);
172
+ }
173
+ if (job.runAt === job.createdAt)
174
+ job.runAt = now;
175
+ const state = postgresStateForJob(job, await postgresDependenciesSatisfied(tx, ctx, job.dependsOn), now);
176
+ const timelineIndex = job.timeline.length < MAX_TIMELINE_ENTRIES ? job.timeline.length : undefined;
177
+ if (timelineIndex !== undefined)
178
+ job.timeline.push({ state, timestamp: now });
179
+ await insertRow(tx, ctx, job, state, now);
180
+ if (!options.preparedFlow)
181
+ await registerPostgresAdmissionQueues(tx, ctx, [job.queue]);
182
+ const pushedEventId = options.preparedFlow
183
+ ? undefined
184
+ : await recordPostgresEvent(tx, ctx, {
185
+ queue: job.queue,
186
+ type: 'pushed',
187
+ jobId: job.id,
188
+ occurredAt: now,
189
+ job,
190
+ state,
191
+ });
192
+ if (parentQueue && job.parentId) {
193
+ await updatePostgresJobParentInTransaction(tx, ctx, job.id, job.parentId, true);
194
+ }
195
+ return { job, inserted: true, state, pushedEventId, timelineIndex };
196
+ }
@@ -0,0 +1,5 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import type { Job } from '../../../domain/types/job';
3
+ import type { PostgresContext } from './context';
4
+ export declare function linkedPostgresParentQueue(job: Job): string | null;
5
+ export declare function lockPostgresAdmissionParent(tx: TransactionSQL, ctx: PostgresContext, job: Job, expectedQueue: string): Promise<void>;
@@ -0,0 +1,35 @@
1
+ import { lockPostgresFlowParent } from './flowFailures';
2
+ export function linkedPostgresParentQueue(job) {
3
+ if (!job.parentId ||
4
+ typeof job.data !== 'object' ||
5
+ job.data === null ||
6
+ Array.isArray(job.data)) {
7
+ return null;
8
+ }
9
+ const data = job.data;
10
+ if (String(data.__parentId) !== String(job.parentId))
11
+ return null;
12
+ return typeof data.__parentQueue === 'string' && data.__parentQueue.length > 0
13
+ ? data.__parentQueue
14
+ : null;
15
+ }
16
+ export async function lockPostgresAdmissionParent(tx, ctx, job, expectedQueue) {
17
+ const parentId = job.parentId;
18
+ if (parentId === job.id)
19
+ throw new Error('A job cannot be its own parent');
20
+ await lockPostgresFlowParent(tx, ctx, parentId);
21
+ const rows = await tx `
22
+ SELECT queue, state FROM bunqueue_jobs
23
+ WHERE namespace = ${ctx.config.namespace} AND id = ${String(parentId)}
24
+ FOR UPDATE
25
+ `;
26
+ const parent = rows[0];
27
+ if (!parent)
28
+ throw new Error(`Parent job not found: ${String(parentId)}`);
29
+ if (parent.queue !== expectedQueue) {
30
+ throw new Error(`Parent job ${String(parentId)} belongs to queue ${parent.queue}, not ${expectedQueue}`);
31
+ }
32
+ if (['active', 'completed', 'failed'].includes(parent.state)) {
33
+ throw new Error(`Parent job ${String(parentId)} is not linkable`);
34
+ }
35
+ }
@@ -0,0 +1,9 @@
1
+ import type { Job } from '../../../domain/types/job';
2
+ import type { PostgresJobState } from './types';
3
+ export interface PostgresAdmissionResult {
4
+ readonly job: Job;
5
+ readonly inserted: boolean;
6
+ readonly state: PostgresJobState;
7
+ readonly pushedEventId?: number;
8
+ readonly timelineIndex?: number;
9
+ }
@@ -0,0 +1,12 @@
1
+ import type { AtomicFlowBatchInput } from '../../../domain/types/flow';
2
+ import { type Job } from '../../../domain/types/job';
3
+ import { PostgresQueueStoreRuntime } from './runtime';
4
+ /** Atomic single, batch, and flow admission strategy shared by the store facade. */
5
+ export declare class PostgresAdmissionStore extends PostgresQueueStoreRuntime {
6
+ insert(job: Job): Promise<{
7
+ job: Job;
8
+ inserted: boolean;
9
+ }>;
10
+ insertMany(jobs: readonly Job[], rejectExisting?: boolean): Promise<Job[]>;
11
+ insertFlow(batch: AtomicFlowBatchInput): Promise<Job[]>;
12
+ }
@@ -0,0 +1,101 @@
1
+ import { validateAtomicFlowBatch } from '../../../application/operations/flowValidation';
2
+ import { createJob } from '../../../domain/types/job';
3
+ import { admitPostgresJob } from './admission';
4
+ import { recordPostgresJobEvents } from './batchEvents';
5
+ import { admitPostgresJobsBatch, canBatchAdmitPostgresJobs, lockPostgresAdmissionKeys, PostgresBatchAdmissionConflict, } from './batchAdmission';
6
+ import { retirePostgresCompletionGenerations } from './completionLifecycle';
7
+ import { databaseNow } from './context';
8
+ import { assertPostgresDependenciesExist, lockPostgresDependencyCompletions, } from './dependencyPromotion';
9
+ import { lockPostgresFlowParents } from './flowFailures';
10
+ import { PostgresQueueStoreRuntime } from './runtime';
11
+ import { reconcilePostgresSerialAdmissions } from './serialAdmission';
12
+ import { lockPostgresAdmissionQueues, registerPostgresAdmissionQueues } from './queueLifecycle';
13
+ import { runPostgresTransactionWithRetry } from './transactionRetry';
14
+ function admissionDependencyIds(jobs) {
15
+ return jobs.flatMap((job) => [job.id, ...job.dependsOn]);
16
+ }
17
+ function admissionParentIds(jobs) {
18
+ return jobs.flatMap((job) => (job.parentId ? [job.parentId] : []));
19
+ }
20
+ function replaySafeAdmissionJob(job) {
21
+ return { ...job, timeline: [...job.timeline] };
22
+ }
23
+ /** Atomic single, batch, and flow admission strategy shared by the store facade. */
24
+ export class PostgresAdmissionStore extends PostgresQueueStoreRuntime {
25
+ async insert(job) {
26
+ await this.initialize();
27
+ return await runPostgresTransactionWithRetry(this.context, (tx) => admitPostgresJob(tx, this.context, replaySafeAdmissionJob(job)));
28
+ }
29
+ async insertMany(jobs, rejectExisting = false) {
30
+ await this.initialize();
31
+ const insertSerially = () => runPostgresTransactionWithRetry(this.context, async (tx) => {
32
+ await lockPostgresAdmissionQueues(tx, this.context, jobs.map((job) => job.queue));
33
+ await lockPostgresDependencyCompletions(tx, this.context, admissionDependencyIds(jobs));
34
+ await lockPostgresFlowParents(tx, this.context, admissionParentIds(jobs));
35
+ await lockPostgresAdmissionKeys(tx, this.context, jobs);
36
+ const admissions = [];
37
+ const insertedJobIds = [];
38
+ const insertedDependencies = [];
39
+ for (const source of jobs) {
40
+ const job = replaySafeAdmissionJob(source);
41
+ const admitted = await admitPostgresJob(tx, this.context, job, {
42
+ rejectExisting,
43
+ dependencyLocksHeld: true,
44
+ queueLifecycleLocksHeld: true,
45
+ dependencyExistenceCheckDeferred: true,
46
+ completionConsumerExemptions: insertedJobIds,
47
+ });
48
+ admissions.push(admitted);
49
+ if (admitted.inserted) {
50
+ insertedJobIds.push(job.id);
51
+ insertedDependencies.push(...job.dependsOn);
52
+ }
53
+ }
54
+ await assertPostgresDependenciesExist(tx, this.context, insertedDependencies);
55
+ return await reconcilePostgresSerialAdmissions(tx, this.context, admissions);
56
+ });
57
+ if (!canBatchAdmitPostgresJobs(jobs))
58
+ return await insertSerially();
59
+ try {
60
+ return await runPostgresTransactionWithRetry(this.context, (tx) => admitPostgresJobsBatch(tx, this.context, jobs));
61
+ }
62
+ catch (error) {
63
+ if (!(error instanceof PostgresBatchAdmissionConflict))
64
+ throw error;
65
+ return await insertSerially();
66
+ }
67
+ }
68
+ async insertFlow(batch) {
69
+ validateAtomicFlowBatch(batch);
70
+ await this.initialize();
71
+ return await runPostgresTransactionWithRetry(this.context, async (tx) => {
72
+ const now = await databaseNow(tx);
73
+ const created = batch.jobs.map((planned) => createJob(planned.id, planned.queue, planned.input, now));
74
+ await lockPostgresAdmissionQueues(tx, this.context, created.map((job) => job.queue));
75
+ await lockPostgresDependencyCompletions(tx, this.context, admissionDependencyIds(created));
76
+ await retirePostgresCompletionGenerations(tx, this.context, created.map((job) => job.id));
77
+ await lockPostgresFlowParents(tx, this.context, admissionParentIds(created));
78
+ await lockPostgresAdmissionKeys(tx, this.context, created);
79
+ const admissionNow = await databaseNow(tx);
80
+ const jobs = [];
81
+ const admissions = [];
82
+ for (const job of created) {
83
+ const admitted = await admitPostgresJob(tx, this.context, job, {
84
+ rejectExisting: true,
85
+ linkParent: false,
86
+ dependencyLocksHeld: true,
87
+ queueLifecycleLocksHeld: true,
88
+ dependencyExistenceCheckDeferred: true,
89
+ preparedFlow: { now: admissionNow },
90
+ });
91
+ admissions.push(admitted);
92
+ jobs.push(admitted.job);
93
+ }
94
+ await assertPostgresDependenciesExist(tx, this.context, created.flatMap((job) => job.dependsOn));
95
+ const inserted = admissions.filter((admission) => admission.inserted);
96
+ await registerPostgresAdmissionQueues(tx, this.context, inserted.map(({ job }) => job.queue));
97
+ await recordPostgresJobEvents(tx, this.context, inserted.map(({ job, state }) => ({ job, state, type: 'pushed' })), admissionNow);
98
+ return jobs;
99
+ });
100
+ }
101
+ }
@@ -0,0 +1,2 @@
1
+ /** Build an unambiguous domain-separated advisory-lock identity. */
2
+ export declare function postgresAdvisoryLockName(domain: string, ...components: string[]): string;
@@ -0,0 +1,13 @@
1
+ function postgresCharacterLength(value) {
2
+ // oxlint-disable-next-line typescript/no-misused-spread -- PostgreSQL length(text) counts code points.
3
+ return [...value].length;
4
+ }
5
+ /** Build an unambiguous domain-separated advisory-lock identity. */
6
+ export function postgresAdvisoryLockName(domain, ...components) {
7
+ const base = `bunqueue:${domain}`;
8
+ if (components.length === 0)
9
+ return base;
10
+ return `${base}:${components
11
+ .map((component) => `${postgresCharacterLength(component)}:${component}`)
12
+ .join('')}`;
13
+ }
@@ -0,0 +1,11 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import { type Job } from '../../../domain/types/job';
3
+ import { type PostgresContext } from './context';
4
+ export declare class PostgresBatchAdmissionConflict extends Error {
5
+ constructor();
6
+ }
7
+ export declare function canBatchAdmitPostgresJobs(jobs: readonly Job[]): boolean;
8
+ /** Serialize every conflicting batch key in one canonically ordered round trip. */
9
+ export declare function lockPostgresAdmissionKeys(tx: TransactionSQL, ctx: PostgresContext, jobs: readonly Job[]): Promise<void>;
10
+ /** Admit independent jobs atomically with one insert and one event write. */
11
+ export declare function admitPostgresJobsBatch(tx: TransactionSQL, ctx: PostgresContext, jobs: readonly Job[]): Promise<Job[]>;
@@ -0,0 +1,128 @@
1
+ import { MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
2
+ import { postgresAdvisoryLockName } from './advisoryLocks';
3
+ import { recordPostgresJobEvents } from './batchEvents';
4
+ import { encodePostgresValue, postgresStateForJob } from './codec';
5
+ import { databaseNow } from './context';
6
+ import { retirePostgresCompletionGenerations } from './completionLifecycle';
7
+ import { lockPostgresDependencyCompletions } from './dependencyPromotion';
8
+ import { lockPostgresAdmissionQueues, registerPostgresAdmissionQueues } from './queueLifecycle';
9
+ export class PostgresBatchAdmissionConflict extends Error {
10
+ constructor() {
11
+ super('PostgreSQL batch admission encountered an existing job ID');
12
+ this.name = 'PostgresBatchAdmissionConflict';
13
+ }
14
+ }
15
+ export function canBatchAdmitPostgresJobs(jobs) {
16
+ if (jobs.length < 2)
17
+ return false;
18
+ if (new Set(jobs.map((job) => String(job.id))).size !== jobs.length)
19
+ return false;
20
+ return jobs.every((job) => job.dependsOn.length === 0 && job.parentId === null);
21
+ }
22
+ function admissionKeys(jobs) {
23
+ return [
24
+ ...new Set(jobs.flatMap((job) => [
25
+ `id:${String(job.id)}`,
26
+ ...(job.uniqueKey ? [`dedup:${job.queue}:${job.uniqueKey}`] : []),
27
+ ])),
28
+ ].sort();
29
+ }
30
+ /** Serialize every conflicting batch key in one canonically ordered round trip. */
31
+ export async function lockPostgresAdmissionKeys(tx, ctx, jobs) {
32
+ const lockNames = admissionKeys(jobs).map((key) => postgresAdvisoryLockName('admission', ctx.config.namespace, key));
33
+ if (lockNames.length === 0)
34
+ return;
35
+ await tx `
36
+ WITH ordered_keys AS MATERIALIZED (
37
+ SELECT DISTINCT hashtextextended(lock_name, 0) AS lock_key
38
+ FROM unnest(${tx.array(lockNames, 'TEXT')}) AS admission(lock_name)
39
+ ORDER BY lock_key
40
+ ), locked_keys AS MATERIALIZED (
41
+ SELECT lock_key, pg_advisory_xact_lock(lock_key) AS acquired
42
+ FROM ordered_keys
43
+ ORDER BY lock_key
44
+ )
45
+ SELECT COUNT(*) FROM locked_keys
46
+ `;
47
+ }
48
+ function prepareAdmissions(jobs, now) {
49
+ return jobs.map((job) => {
50
+ const preparedJob = { ...job, timeline: [...job.timeline] };
51
+ if (preparedJob.runAt === preparedJob.createdAt)
52
+ preparedJob.runAt = now;
53
+ const state = postgresStateForJob(preparedJob, true, now);
54
+ if (preparedJob.timeline.length < MAX_TIMELINE_ENTRIES) {
55
+ preparedJob.timeline.push({ state, timestamp: now });
56
+ }
57
+ return { job: preparedJob, state };
58
+ });
59
+ }
60
+ function toRecords(prepared, namespace, now, groupOrders) {
61
+ return prepared.map(({ job, state }, position) => ({
62
+ namespace,
63
+ id: String(job.id),
64
+ queue: job.queue,
65
+ payload: encodePostgresValue(job),
66
+ state,
67
+ priority: job.priority,
68
+ lifo: job.lifo,
69
+ run_at: job.runAt,
70
+ created_at: job.createdAt,
71
+ started_at: job.startedAt,
72
+ completed_at: job.completedAt,
73
+ attempts: job.attempts,
74
+ max_attempts: job.maxAttempts,
75
+ ttl: job.ttl,
76
+ timeout: job.timeout,
77
+ unique_key: job.uniqueKey,
78
+ unique_expires_at: job.uniqueKey && job.deduplicationTtl !== null ? now + job.deduplicationTtl : null,
79
+ custom_id: job.customId,
80
+ group_id: job.groupId,
81
+ group_order: groupOrders.get(position) ?? null,
82
+ }));
83
+ }
84
+ async function allocateGroupOrders(tx, prepared) {
85
+ const positions = prepared.flatMap(({ job }, position) => job.groupId === null ? [] : [position]);
86
+ if (positions.length === 0)
87
+ return new Map();
88
+ const rows = await tx `
89
+ WITH ordered AS MATERIALIZED (
90
+ SELECT position
91
+ FROM unnest(${tx.array(positions, 'INTEGER')}) AS requested(position)
92
+ ORDER BY position
93
+ )
94
+ SELECT position, nextval('bunqueue_group_order_seq') AS group_order
95
+ FROM ordered
96
+ ORDER BY position
97
+ `;
98
+ return new Map(rows.map(({ position, group_order }) => [position, group_order]));
99
+ }
100
+ async function insertRows(tx, ctx, prepared, now) {
101
+ const groupOrders = await allocateGroupOrders(tx, prepared);
102
+ const records = toRecords(prepared, ctx.config.namespace, now, groupOrders);
103
+ let insertedCount = 0;
104
+ for (let offset = 0; offset < records.length; offset += 1000) {
105
+ const batch = records.slice(offset, offset + 1000);
106
+ const inserted = await tx `
107
+ INSERT INTO bunqueue_jobs ${tx(batch, 'namespace', 'id', 'queue', 'payload', 'state', 'priority', 'lifo', 'run_at', 'created_at', 'started_at', 'completed_at', 'attempts', 'max_attempts', 'ttl', 'timeout', 'unique_key', 'unique_expires_at', 'custom_id', 'group_id', 'group_order')}
108
+ ON CONFLICT DO NOTHING
109
+ RETURNING id
110
+ `;
111
+ insertedCount += inserted.length;
112
+ }
113
+ if (insertedCount !== prepared.length)
114
+ throw new PostgresBatchAdmissionConflict();
115
+ }
116
+ /** Admit independent jobs atomically with one insert and one event write. */
117
+ export async function admitPostgresJobsBatch(tx, ctx, jobs) {
118
+ await lockPostgresAdmissionQueues(tx, ctx, jobs.map((job) => job.queue));
119
+ await lockPostgresDependencyCompletions(tx, ctx, jobs.map((job) => job.id));
120
+ await lockPostgresAdmissionKeys(tx, ctx, jobs);
121
+ const now = await databaseNow(tx);
122
+ const prepared = prepareAdmissions(jobs, now);
123
+ await insertRows(tx, ctx, prepared, now);
124
+ await registerPostgresAdmissionQueues(tx, ctx, prepared.map(({ job }) => job.queue));
125
+ await retirePostgresCompletionGenerations(tx, ctx, jobs.map((job) => job.id));
126
+ await recordPostgresJobEvents(tx, ctx, prepared.map(({ job, state }) => ({ job, type: 'pushed', state })), now);
127
+ return prepared.map(({ job }) => job);
128
+ }
@@ -0,0 +1,4 @@
1
+ import { type PostgresContext } from './context';
2
+ import type { PostgresBatchCompletionResult, PostgresCompletionInput } from './types';
3
+ /** Complete a unique ACK batch atomically with set-based persistence. */
4
+ export declare function completePostgresJobs(ctx: PostgresContext, inputs: readonly PostgresCompletionInput[]): Promise<PostgresBatchCompletionResult[]>;