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
package/README.md CHANGED
@@ -13,8 +13,8 @@
13
13
  </p>
14
14
 
15
15
  <p align="center">
16
- High-performance job queue for Bun. Built for AI agents and automation.<br/>
17
- Zero external infrastructure. MCP-native. TypeScript-first.
16
+ High-performance job queue for Bun. Memory or one-file SQLite; PostgreSQL 15–18 multi-broker when you scale.<br/>
17
+ DLQ, cron, SQLite S3 backups, and native MCP. Built for AI agents and automation. No Redis.
18
18
  </p>
19
19
 
20
20
  <p align="center">
@@ -48,14 +48,13 @@ await app.add('send', { to: 'alice@example.com' });
48
48
  ```
49
49
 
50
50
  That's it. Queue + Worker in one object, persisted to a single SQLite file.
51
- No Redis, no config, no setup. The install is 5.5 MB, 7 packages, 2 runtime
52
- dependencies (`croner` + `msgpackr`) — SQLite, S3, HTTP and WebSocket are Bun
53
- built-ins.
51
+ No Redis, no config, no setup. `msgpackr` is the only runtime dependency;
52
+ cron, SQLite, S3, HTTP and WebSocket use Bun's built-ins.
54
53
 
55
54
  ### Not on Bun? Run the server, connect from anywhere
56
55
 
57
- The queue also runs as a standalone server one command, nothing else to
58
- operate:
56
+ The queue also runs as a standalone server. Memory is the zero-configuration
57
+ default; SQLite is the zero-infrastructure persistent option:
59
58
 
60
59
  ```bash
61
60
  # in-memory without --data-path; pass it to persist jobs to SQLite
@@ -67,9 +66,28 @@ docker run -d -p 6789:6789 -p 6790:6790 \
67
66
  ghcr.io/egeominotti/bunqueue:latest
68
67
  ```
69
68
 
70
- Release images also carry the exact package version. For reproducible
71
- deployments, pin `ghcr.io/egeominotti/bunqueue:2.8.60`; `latest` points to the
72
- same multi-arch image at release time.
69
+ For multiple active brokers, the repository includes a topology pinned to
70
+ PostgreSQL 18.6:
71
+
72
+ ```bash
73
+ POSTGRES_PASSWORD='replace-me' \
74
+ BUNQUEUE_POSTGRES_URL='postgres://bunqueue:replace-me@postgres:5432/bunqueue' \
75
+ docker compose -f docker-compose.postgres.yml up --build -d
76
+ ```
77
+
78
+ It starts two brokers against one database/namespace. PostgreSQL is server-only;
79
+ embedded mode keeps using memory/SQLite. Supply both Compose values when the
80
+ password changes, percent-encoding reserved characters in the URL only. MySQL
81
+ is not supported. CI validates PostgreSQL 15, 16, 17, and the pinned/recommended
82
+ 18.6 release. See the
83
+ [storage guide](https://bunqueue.dev/guide/databases/).
84
+
85
+ Every completed release publishes `ghcr.io/egeominotti/bunqueue` with the exact
86
+ version tag alongside `latest`, the commit SHA and a build timestamp, so pin the
87
+ version tag for reproducible deployments. Confirm the tag exists before pinning
88
+ it, because a release whose pipeline does not complete pushes no image;
89
+ `docker buildx imagetools inspect ghcr.io/egeominotti/bunqueue:<tag>` resolves
90
+ the digest.
73
91
 
74
92
  Then produce and process from the language you already use:
75
93
 
@@ -80,7 +98,7 @@ npm install bunqueue-client # Node.js ≥ 20, Deno ≥ 2, Bun, Cloudflare Wor
80
98
  ```typescript
81
99
  import { Queue, Worker } from 'bunqueue-client';
82
100
 
83
- const queue = new Queue('emails'); // localhost:6789 by default
101
+ const queue = new Queue('emails'); // localhost:6789 by default
84
102
  await queue.add('welcome', { to: 'user@example.com' });
85
103
 
86
104
  new Worker('emails', async (job) => ({ sent: true }), { concurrency: 10 });
@@ -96,14 +114,19 @@ Python, PHP, Go, Rust and Elixir clients speak the same protocol — see
96
114
 
97
115
  ## Why bunqueue?
98
116
 
99
- | Library | Requires | AI-native |
100
- | ------------ | ----------- | --------- |
101
- | BullMQ | Redis | No |
102
- | Agenda | MongoDB | No |
103
- | pg-boss | PostgreSQL | No |
104
- | **bunqueue** | **Nothing** | **Yes** |
105
-
106
- - **Zero external infrastructure** — one process, one SQLite file. `cp` to back up
117
+ | Library | Requires | AI-native |
118
+ | ------------ | ------------------------------------------ | --------- |
119
+ | BullMQ | Redis | No |
120
+ | Agenda | MongoDB | No |
121
+ | pg-boss | PostgreSQL | No |
122
+ | **bunqueue** | **Nothing (memory/SQLite) · PostgreSQL optional** | **Yes** |
123
+
124
+ - **Zero external infrastructure by default** — memory by default; one SQLite
125
+ file when local persistence is configured
126
+ - **PostgreSQL 15–18 multi-broker mode** — PostgreSQL 18.6 is recommended;
127
+ database-authoritative claims, fenced
128
+ leases, shared limits, cron, workers, job-state/lifecycle metrics, and failover state
129
+ ([tested three-broker Docker example](https://bunqueue.dev/examples/postgres-multibroker/))
107
130
  - **BullMQ-compatible API** — same `Queue`, `Worker`, `QueueEvents`; [migrating takes minutes](https://bunqueue.dev/guide/migration/)
108
131
  - **MCP server included** — 73 tools; AI agents get full queue control out of the box
109
132
  - **Everything server-side** — retries with backoff, priorities, cron, rate limits, dead letter queue
@@ -111,24 +134,24 @@ Python, PHP, Go, Rust and Elixir clients speak the same protocol — see
111
134
  in-memory batch push, 186K jobs/sec public on-disk Embedded `addBulk`, and
112
135
  159K jobs/sec TCP `PUSHB`; [methodology and distributions](https://bunqueue.dev/guide/benchmarks/)
113
136
 
114
- **Great for:** single-server deployments, AI agents that need a scheduler,
115
- prototypes and MVPs, embedded use cases (CLI tools, edge, serverless), teams
137
+ **Great for:** embedded and single-server deployments, PostgreSQL-backed broker
138
+ fleets, AI agents that need a scheduler, edge/serverless spooling, and teams
116
139
  that don't want to operate Redis.
117
140
 
118
- **Not ideal for:** multi-region distributed systems requiring HA or automatic
119
- failover today. If you already run Redis and BullMQ works for you, keep it.
141
+ **Not ideal for:** multi-region consensus or deployments that require MySQL as
142
+ the queue store. If you already run Redis and BullMQ works for you, keep it.
120
143
 
121
144
  [When to choose bunqueue →](https://bunqueue.dev/guide/comparison/)
122
145
 
123
146
  ## Two Modes
124
147
 
125
- | | Embedded | Server (TCP) |
126
- | ---------------- | ------------------------------------- | -------------------------------------------- |
127
- | **How it works** | Queue runs inside your process | Standalone server, clients connect via TCP |
128
- | **Setup** | `bun add bunqueue` | `docker run` or `bunqueue start` |
129
- | **Performance** | 186K jobs/sec on-disk `addBulk`; 729K internal in-memory batch | 159K jobs/sec TCP `PUSHB`; 17K jobs/sec worker drain |
130
- | **Best for** | Single-process apps, CLIs, serverless | Multiple workers, separate producer/consumer |
131
- | **Scaling** | Same process only | Multiple clients across machines |
148
+ | | Embedded | Server (TCP) |
149
+ | ---------------- | -------------------------------------------------------------- | -------------------------------------------------------- |
150
+ | **How it works** | Queue runs inside your process | Standalone server, clients connect via TCP |
151
+ | **Setup** | `bun add bunqueue` | `docker run` or `bunqueue start` |
152
+ | **Performance** | 186K jobs/sec on-disk `addBulk`; 729K internal in-memory batch | SQLite: 159K TCP `PUSHB`; 17K jobs/sec worker drain |
153
+ | **Best for** | Single-process apps, CLIs, serverless | Multiple workers, separate producer/consumer |
154
+ | **Scaling** | Same process only | Multiple clients; multiple brokers with PostgreSQL 15–18 |
132
155
 
133
156
  ### Embedded
134
157
 
@@ -180,18 +203,20 @@ await queue.add('process', { data: 'hello' });
180
203
 
181
204
  ## One Queue, Any Language (SDKs)
182
205
 
183
- The server does all the heavy lifting. Official client SDKs speak the native
184
- TCP protocol with full feature parity, so producers and workers can live
185
- anywhere in your stack — add a job from TypeScript, process it from Python:
186
-
187
- | Where your code runs | Install |
188
- | -------------------- | ------- |
189
- | **Node.js 20, Deno ≥ 2, Bun, Cloudflare Workers** | [`npm install bunqueue-client`](https://www.npmjs.com/package/bunqueue-client) |
190
- | **Python ≥ 3.9** | [`pip install bunqueue-client`](https://pypi.org/project/bunqueue-client/) |
191
- | **PHP8.1** | [`composer require bunqueue/client`](https://packagist.org/packages/bunqueue/client) |
192
- | **Go1.26.5** | `go get github.com/egeominotti/bunqueue/sdk/go` |
193
- | **Rust ≥ 1.85** | [`cargo add bunqueue-client`](https://crates.io/crates/bunqueue-client) |
194
- | **Elixir ≥ 1.15** | Hex coming soon — today: use [`sdk/elixir`](./sdk/elixir) as a path dependency |
206
+ The server does all the heavy lifting. Official client SDKs share the
207
+ protocol-conformant core Queue, Worker, and Flow surface, so producers and
208
+ workers can live anywhere in your stack — add a job from TypeScript, process it
209
+ from Python. Language-specific capabilities are tracked in the
210
+ [SDK guide](https://bunqueue.dev/guide/sdks/).
211
+
212
+ | Where your code runs | Install |
213
+ | --------------------------------------------------- | -------------------------------------------------------------------------------------- |
214
+ | **Node.js20, Deno ≥ 2, Bun, Cloudflare Workers** | [`npm install bunqueue-client`](https://www.npmjs.com/package/bunqueue-client) |
215
+ | **Python3.9** | [`pip install bunqueue-client`](https://pypi.org/project/bunqueue-client/) |
216
+ | **PHP8.1** | [`composer require bunqueue/client`](https://packagist.org/packages/bunqueue/client) |
217
+ | **Go ≥ 1.26.5** | `go get github.com/egeominotti/bunqueue/sdk/go` |
218
+ | **Rust ≥ 1.85** | [`cargo add bunqueue-client`](https://crates.io/crates/bunqueue-client) |
219
+ | **Elixir ≥ 1.15** | Hex coming soon — today: use [`sdk/elixir`](./sdk/elixir) as a path dependency |
195
220
 
196
221
  ```typescript
197
222
  // Node.js / Deno / Cloudflare Workers
@@ -237,21 +262,24 @@ const root = await flows.add({
237
262
  ],
238
263
  });
239
264
 
240
- console.log(root.job.id, root.children?.map(({ job }) => job.id));
265
+ console.log(
266
+ root.job.id,
267
+ root.children?.map(({ job }) => job.id)
268
+ );
241
269
  await flows.close();
242
270
  ```
243
271
 
244
272
  The repository records the contracts and the test strategy beside each
245
273
  implementation:
246
274
 
247
- | SDK | Runtime invariants | Generated tests | Mutation engine |
248
- | --- | --- | --- | --- |
249
- | [TypeScript](./sdk/typescript/README.md) | [contract](./sdk/typescript/INVARIANTS.md) | fast-check | none¹ |
250
- | [Python](./sdk/python/README.md) | [contract](./sdk/python/INVARIANTS.md) | Hypothesis | mutmut |
251
- | [PHP](./sdk/php/README.md) | [contract](./sdk/php/INVARIANTS.md) | Eris | Infection |
252
- | [Go](./sdk/go/README.md) | [contract](./sdk/go/INVARIANTS.md) | Rapid | Gremlins |
253
- | [Rust](./sdk/rust/README.md) | [contract](./sdk/rust/INVARIANTS.md) | proptest | cargo-mutants |
254
- | [Elixir](./sdk/elixir/README.md) | [contract](./sdk/elixir/INVARIANTS.md) | StreamData | Muex |
275
+ | SDK | Runtime invariants | Generated tests | Mutation engine |
276
+ | ---------------------------------------- | ------------------------------------------ | --------------- | --------------- |
277
+ | [TypeScript](./sdk/typescript/README.md) | [contract](./sdk/typescript/INVARIANTS.md) | fast-check | none¹ |
278
+ | [Python](./sdk/python/README.md) | [contract](./sdk/python/INVARIANTS.md) | Hypothesis | mutmut |
279
+ | [PHP](./sdk/php/README.md) | [contract](./sdk/php/INVARIANTS.md) | Eris | Infection |
280
+ | [Go](./sdk/go/README.md) | [contract](./sdk/go/INVARIANTS.md) | Rapid | Gremlins |
281
+ | [Rust](./sdk/rust/README.md) | [contract](./sdk/rust/INVARIANTS.md) | proptest | cargo-mutants |
282
+ | [Elixir](./sdk/elixir/README.md) | [contract](./sdk/elixir/INVARIANTS.md) | StreamData | Muex |
255
283
 
256
284
  ¹ The TypeScript SDK has no mutation engine. StrykerJS was removed because its
257
285
  dependency graph produced every advisory the weekly audit had to answer for,
@@ -314,17 +342,25 @@ human-in-the-loop signals — built on bunqueue, no new infrastructure:
314
342
  import { Workflow, Engine } from 'bunqueue/workflow';
315
343
 
316
344
  const orderFlow = new Workflow('order-pipeline')
317
- .step('reserve-stock', async () => {
318
- await inventory.reserve();
319
- return { reserved: true };
320
- }, {
321
- compensate: async () => await inventory.release(), // auto-rollback on failure
322
- })
323
- .step('charge', async () => {
324
- return { txId: await payments.charge() };
325
- }, {
326
- compensate: async () => await payments.refund(),
327
- })
345
+ .step(
346
+ 'reserve-stock',
347
+ async () => {
348
+ await inventory.reserve();
349
+ return { reserved: true };
350
+ },
351
+ {
352
+ compensate: async () => await inventory.release(), // auto-rollback on failure
353
+ }
354
+ )
355
+ .step(
356
+ 'charge',
357
+ async () => {
358
+ return { txId: await payments.charge() };
359
+ },
360
+ {
361
+ compensate: async () => await payments.refund(),
362
+ }
363
+ )
328
364
  .waitFor('manager-approval', { timeout: 86_400_000 }) // human-in-the-loop
329
365
  .step('confirm', async (ctx) => {
330
366
  return { txId: (ctx.steps['charge'] as { txId: string }).txId };
@@ -336,13 +372,13 @@ const run = await engine.start('order-pipeline', { orderId: 'ORD-1' });
336
372
  await engine.signal(run.id, 'manager-approval', { approved: true });
337
373
  ```
338
374
 
339
- | | **bunqueue** | **Temporal** | **Inngest** | **Trigger.dev** |
340
- |---|---|---|---|---|
341
- | **Infrastructure** | None (embedded) | PostgreSQL + 7 services | Cloud-only | Redis + PostgreSQL |
342
- | **Saga compensation** | Built-in | Manual | Manual | Manual |
343
- | **Human-in-the-loop** | `.waitFor()` | Signals API | `step.waitForEvent()` | Waitpoint tokens |
344
- | **Self-hosted** | Zero-config | Complex | No | Complex |
345
- | **Pricing** | Free (MIT) | Free / Cloud $$ | Per-execution | Free tier, then $50/mo+ |
375
+ | | **bunqueue** | **Temporal** | **Inngest** | **Trigger.dev** |
376
+ | --------------------- | --------------- | ----------------------- | --------------------- | ----------------------- |
377
+ | **Infrastructure** | None (embedded) | PostgreSQL + 7 services | Cloud-only | Redis + PostgreSQL |
378
+ | **Saga compensation** | Built-in | Manual | Manual | Manual |
379
+ | **Human-in-the-loop** | `.waitFor()` | Signals API | `step.waitForEvent()` | Waitpoint tokens |
380
+ | **Self-hosted** | Zero-config | Complex | No | Complex |
381
+ | **Pricing** | Free (MIT) | Free / Cloud $$ | Per-execution | Free tier, then $50/mo+ |
346
382
 
347
383
  Also included: nested workflows, `doUntil`/`doWhile` loops, `forEach` over
348
384
  dynamic lists, schema validation (Zod, ArkType, Valibot or any `.parse()`),
@@ -374,8 +410,8 @@ claude mcp add bunqueue -- bunx bunqueue-mcp
374
410
  }
375
411
  ```
376
412
 
377
- Then just ask: *"Schedule a cleanup job every day at 3 AM"* · *"Show me all
378
- failed jobs and retry them"* · *"Set rate limit to 50/sec on api-calls"*.
413
+ Then just ask: _"Schedule a cleanup job every day at 3 AM"_ · _"Show me all
414
+ failed jobs and retry them"_ · _"Set rate limit to 50/sec on api-calls"_.
379
415
 
380
416
  [MCP guide →](https://bunqueue.dev/guide/mcp/)
381
417
 
@@ -399,13 +435,13 @@ https://github.com/user-attachments/assets/e8a8d38e-b4a6-4dc8-8360-876c0f24d116
399
435
 
400
436
  Native Ryzen 9 9950X3D, Bun 1.3.14; medians from repeated fresh processes:
401
437
 
402
- | Workload | Mode | Median | Persistence |
403
- | --- | --- | ---: | --- |
404
- | Internal batched push, 1M jobs | Embedded | 729,395 jobs/sec | In-memory, no `dataPath` |
405
- | Public sustained `addBulk`, 50K cell | Embedded | 186,384 jobs/sec | On-disk buffered SQLite |
406
- | `PUSHB`, fresh 50K sample | TCP | 158,779 jobs/sec | On-disk buffered SQLite |
407
- | No-work worker drain, concurrency 50 | TCP | 17,256 jobs/sec | Full pull/process/ACK |
408
- | Linear Workflow Engine | Embedded / TCP | 2,700 / 3,187 workflows/sec | Workflow SQLite + 3 queue nodes |
438
+ | Workload | Mode | Median | Persistence |
439
+ | ------------------------------------ | -------------- | --------------------------: | ------------------------------- |
440
+ | Internal batched push, 1M jobs | Embedded | 729,395 jobs/sec | In-memory, no `dataPath` |
441
+ | Public sustained `addBulk`, 50K cell | Embedded | 186,384 jobs/sec | On-disk buffered SQLite |
442
+ | `PUSHB`, fresh 50K sample | TCP | 158,779 jobs/sec | On-disk buffered SQLite |
443
+ | No-work worker drain, concurrency 50 | TCP | 17,256 jobs/sec | Full pull/process/ACK |
444
+ | Linear Workflow Engine | Embedded / TCP | 2,700 / 3,187 workflows/sec | Workflow SQLite + 3 queue nodes |
409
445
 
410
446
  These operations do different work; the internal in-memory result is not an
411
447
  SQLite or public-API claim. Run `bun run bench`, `bun run bench:tcp`, or
@@ -5,6 +5,8 @@ export function performDlqMaintenance(ctx) {
5
5
  shards: ctx.shards,
6
6
  jobIndex: ctx.jobIndex,
7
7
  jobResults: ctx.jobResults,
8
+ dependencyResults: ctx.dependencyResults,
9
+ customIdMap: ctx.customIdMap,
8
10
  jobLogs: ctx.jobLogs,
9
11
  storage: ctx.storage,
10
12
  };
@@ -1,4 +1,5 @@
1
1
  import { type JobId } from '../../../domain/types/job';
2
2
  import type { BackgroundContext } from '../../types';
3
+ export declare function restoreGroupPolicies(ctx: BackgroundContext, groupStates: ReturnType<NonNullable<BackgroundContext['storage']>['loadGroupState']>): void;
3
4
  export declare function restoreRecoveryPolicies(ctx: BackgroundContext, queueStates: ReturnType<NonNullable<BackgroundContext['storage']>['loadQueueState']>): void;
4
5
  export declare function recoverActiveJobs(ctx: BackgroundContext, dlqJobIds: Set<JobId>, now: number): void;
@@ -3,6 +3,18 @@ import { calculateBackoff } from '../../../domain/types/job';
3
3
  import { isCorruptDependsOn } from '../../../infrastructure/persistence/sqliteSerializer';
4
4
  import { shardIndex } from '../../../shared/hash';
5
5
  import { quarantineCorruptDependsOn, RECOVERY_BATCH_SIZE } from './shared';
6
+ export function restoreGroupPolicies(ctx, groupStates) {
7
+ for (const state of groupStates) {
8
+ const shard = ctx.shards[shardIndex(state.queue)];
9
+ if (state.rateLimit !== null && state.rateDuration !== null) {
10
+ shard.setGroupRateLimit(state.queue, state.groupId, state.rateLimit, state.rateDuration);
11
+ }
12
+ if (state.concurrencyLimit !== null) {
13
+ shard.setGroupConcurrency(state.queue, state.groupId, state.concurrencyLimit);
14
+ }
15
+ ctx.registerQueueName(state.queue);
16
+ }
17
+ }
6
18
  export function restoreRecoveryPolicies(ctx, queueStates) {
7
19
  for (const queueState of queueStates) {
8
20
  if (queueState.stallConfig) {
@@ -1,5 +1,5 @@
1
1
  import { reconcileDependencyCompletionPins } from '../../dependencyCompletions';
2
- import { recoverActiveJobs, restoreRecoveryPolicies } from './active';
2
+ import { recoverActiveJobs, restoreGroupPolicies, restoreRecoveryPolicies } from './active';
3
3
  import { recoverPendingJobs } from './pending';
4
4
  import { recoverCompletedJobs, restoreDlq, restoreQueueState } from './restore';
5
5
  export function recover(ctx) {
@@ -11,7 +11,9 @@ export function recover(ctx) {
11
11
  completedInDatabase.add(record.jobId);
12
12
  const dlqJobIds = ctx.storage.loadDlqJobIds();
13
13
  const queueStates = ctx.storage.loadQueueState();
14
+ const groupStates = ctx.storage.loadGroupState();
14
15
  restoreRecoveryPolicies(ctx, queueStates);
16
+ restoreGroupPolicies(ctx, groupStates);
15
17
  const now = Date.now();
16
18
  recoverActiveJobs(ctx, dlqJobIds, now);
17
19
  recoverPendingJobs(ctx, completedInDatabase, now);
@@ -18,6 +18,7 @@ export function recoverPendingJobs(ctx, completedInDatabase, now) {
18
18
  corruptPendingJobs.push(job);
19
19
  continue;
20
20
  }
21
+ shard.assignGroupFifoOrder(job);
21
22
  const hasDependencies = job.dependsOn && job.dependsOn.length > 0;
22
23
  const recoveredState = persistedJobState(job);
23
24
  const wasAlreadyPromoted = recoveredState === 'waiting' ||
@@ -26,7 +26,7 @@ export async function cleanup(ctx) {
26
26
  }
27
27
  await cleanOrphanedProcessingEntries(ctx, now, stallTimeout);
28
28
  await cleanStaleWaitingDependencies(ctx, now);
29
- cleanUniqueKeysAndGroups(ctx);
29
+ cleanUniqueKeysAndGroups(ctx, now);
30
30
  cleanStalledCandidates(ctx);
31
31
  await cleanOrphanedJobIndex(ctx);
32
32
  cleanOrphanedJobLocks(ctx);
@@ -103,9 +103,10 @@ async function cleanStaleWaitingDependencies(ctx, now) {
103
103
  }
104
104
  }
105
105
  }
106
- function cleanUniqueKeysAndGroups(ctx) {
106
+ function cleanUniqueKeysAndGroups(ctx, now) {
107
107
  for (let i = 0; i < SHARD_COUNT; i++) {
108
108
  const shard = ctx.shards[i];
109
+ shard.cleanExpiredGroupWindows(now);
109
110
  // Clean expired unique keys
110
111
  shard.cleanExpiredUniqueKeys();
111
112
  // Trim if too many keys remain
@@ -121,19 +122,6 @@ function cleanUniqueKeysAndGroups(ctx) {
121
122
  }
122
123
  }
123
124
  }
124
- // Clean orphaned active groups
125
- for (const [_queueName, groups] of shard.activeGroups) {
126
- if (groups.size > 1000) {
127
- const toRemove = Math.floor(groups.size / 2);
128
- const iter = groups.values();
129
- for (let j = 0; j < toRemove; j++) {
130
- const { value, done } = iter.next();
131
- if (done)
132
- break;
133
- groups.delete(value);
134
- }
135
- }
136
- }
137
125
  }
138
126
  }
139
127
  function cleanStalledCandidates(ctx) {
@@ -244,7 +232,7 @@ function cleanEmptyQueues(ctx) {
244
232
  shard.dlq.delete(queueName);
245
233
  shard.uniqueKeys.delete(queueName);
246
234
  shard.queueState.delete(queueName);
247
- shard.activeGroups.delete(queueName);
235
+ shard.clearEmptyGroupRuntime(queueName);
248
236
  shard.clearQueueLimiters(queueName);
249
237
  shard.stallConfig.delete(queueName);
250
238
  shard.dlqConfig.delete(queueName);
@@ -102,6 +102,7 @@ export class ContextFactory {
102
102
  jobIndex: this.deps.jobIndex,
103
103
  totalPushed: this.deps.metrics.totalPushed,
104
104
  broadcast: this.deps.eventsManager.broadcast.bind(this.deps.eventsManager),
105
+ broadcastBatch: this.deps.eventsManager.broadcastBatch.bind(this.deps.eventsManager),
105
106
  dashboardEmit: this.callbacks.emitDashboardEvent,
106
107
  registerQueueName: this.callbacks.registerQueueName,
107
108
  };
@@ -116,6 +117,7 @@ export class ContextFactory {
116
117
  jobIndex: this.deps.jobIndex,
117
118
  totalPulled: this.deps.metrics.totalPulled,
118
119
  broadcast: this.deps.eventsManager.broadcast.bind(this.deps.eventsManager),
120
+ broadcastBatch: this.deps.eventsManager.broadcastBatch.bind(this.deps.eventsManager),
119
121
  dashboardEmit: this.callbacks.emitDashboardEvent,
120
122
  };
121
123
  }
@@ -139,6 +141,7 @@ export class ContextFactory {
139
141
  totalFailed: this.deps.metrics.totalFailed,
140
142
  perQueueMetrics: this.deps.perQueueMetrics,
141
143
  broadcast: this.deps.eventsManager.broadcast.bind(this.deps.eventsManager),
144
+ broadcastBatch: this.deps.eventsManager.broadcastBatch.bind(this.deps.eventsManager),
142
145
  onJobCompleted: this.callbacks.onJobCompleted,
143
146
  onJobFailed: this.callbacks.onJobFailed,
144
147
  onJobsCompleted: this.callbacks.onJobsCompleted,
@@ -161,6 +164,7 @@ export class ContextFactory {
161
164
  jobLocks: this.deps.jobLocks,
162
165
  clientJobs: this.deps.clientJobs,
163
166
  dependencyResults: this.deps.dependencyResults,
167
+ customIdMap: this.deps.customIdMap,
164
168
  depCompletions: this.deps.depCompletions,
165
169
  maxDependencyCompletions: this.deps.config.maxCompletedJobs,
166
170
  webhookManager: this.deps.webhookManager,
@@ -188,6 +192,8 @@ export class ContextFactory {
188
192
  shards: this.deps.shards,
189
193
  jobIndex: this.deps.jobIndex,
190
194
  jobResults: this.deps.jobResults,
195
+ dependencyResults: this.deps.dependencyResults,
196
+ customIdMap: this.deps.customIdMap,
191
197
  jobLogs: this.deps.jobLogs,
192
198
  storage: this.deps.storage,
193
199
  };
@@ -201,6 +207,8 @@ export class ContextFactory {
201
207
  completedJobs: this.deps.completedJobs,
202
208
  completedJobsData: this.deps.completedJobsData,
203
209
  jobResults: this.deps.jobResults,
210
+ dependencyResults: this.deps.dependencyResults,
211
+ customIdMap: this.deps.customIdMap,
204
212
  };
205
213
  }
206
214
  getLogsContext() {
@@ -14,6 +14,14 @@ export interface DlqContext {
14
14
  jobResults: {
15
15
  delete(id: JobId): boolean;
16
16
  };
17
+ dependencyResults: {
18
+ releaseConsumer(id: JobId): void;
19
+ };
20
+ customIdMap: {
21
+ get(id: string): JobId | undefined;
22
+ set(id: string, jobId: JobId): void;
23
+ delete(id: string): boolean;
24
+ };
17
25
  jobLogs: {
18
26
  delete(id: JobId): boolean;
19
27
  };
@@ -30,6 +38,8 @@ export declare function getDlqStats(queue: string, ctx: DlqContext): DlqStats;
30
38
  export declare function purgeExpiredDlq(queue: string, ctx: DlqContext): number;
31
39
  /** Purge all jobs from DLQ */
32
40
  export declare function purgeDlqJobs(queue: string, ctx: DlqContext): number;
41
+ /** Remove one DLQ entry without re-enqueuing it. */
42
+ export declare function removeDlqJob(queue: string, id: JobId, ctx: DlqContext): boolean;
33
43
  /** Configure DLQ for a queue */
34
44
  export declare function configureDlq(queue: string, ctx: DlqContext, config: Partial<DlqConfig>): void;
35
45
  /** Get DLQ configuration */
@@ -83,6 +83,40 @@ export function purgeDlqJobs(queue, ctx) {
83
83
  shard.clearDlq(queue);
84
84
  return entries.length;
85
85
  }
86
+ /** Remove one DLQ entry without re-enqueuing it. */
87
+ export function removeDlqJob(queue, id, ctx) {
88
+ const shard = ctx.shards[shardIndex(queue)];
89
+ const entries = shard.getDlqEntries(queue).filter((entry) => entry.job.id === id);
90
+ if (entries.length === 0)
91
+ return false;
92
+ const location = ctx.jobIndex.get(id);
93
+ const terminalJobIds = location === undefined || (location.type === 'dlq' && location.queueName === queue) ? [id] : [];
94
+ // Keep the in-memory entry visible until the durable transaction succeeds.
95
+ // This prevents a failed SQLite write from leaving memory and disk out of sync.
96
+ ctx.storage?.purgeDlqEntries(queue, [id], terminalJobIds, false);
97
+ shard.removeAllFromDlq(queue, id);
98
+ cleanupTerminalEntries(queue, entries, terminalJobIds, ctx);
99
+ return true;
100
+ }
101
+ function cleanupTerminalEntries(queue, entries, terminalJobIds, ctx) {
102
+ const terminalIds = new Set(terminalJobIds);
103
+ for (const entry of entries) {
104
+ if (!terminalIds.has(entry.job.id))
105
+ continue;
106
+ const customId = entry.job.customId;
107
+ if (customId && ctx.customIdMap.get(customId) === entry.job.id) {
108
+ ctx.customIdMap.delete(customId);
109
+ }
110
+ }
111
+ for (const jobId of terminalJobIds) {
112
+ const location = ctx.jobIndex.get(jobId);
113
+ if (location?.type === 'dlq' && location.queueName === queue)
114
+ ctx.jobIndex.delete(jobId);
115
+ ctx.jobResults.delete(jobId);
116
+ ctx.jobLogs.delete(jobId);
117
+ ctx.dependencyResults.releaseConsumer(jobId);
118
+ }
119
+ }
86
120
  /** Remove every durable and global reference owned by terminal DLQ entries. */
87
121
  function cleanupPurgedEntries(queue, entries, ctx, clearQueue) {
88
122
  const jobIds = [...new Set(entries.map((entry) => entry.job.id))];
@@ -95,14 +129,7 @@ function cleanupPurgedEntries(queue, entries, ctx, clearQueue) {
95
129
  // A newer live generation can reuse the same custom id; its jobs row and
96
130
  // auxiliary state must survive cleanup of the stale terminal generation.
97
131
  ctx.storage?.purgeDlqEntries(queue, jobIds, terminalJobIds, clearQueue);
98
- for (const jobId of terminalJobIds) {
99
- const location = ctx.jobIndex.get(jobId);
100
- if (location?.type === 'dlq' && location.queueName === queue) {
101
- ctx.jobIndex.delete(jobId);
102
- }
103
- ctx.jobResults.delete(jobId);
104
- ctx.jobLogs.delete(jobId);
105
- }
132
+ cleanupTerminalEntries(queue, entries, terminalJobIds, ctx);
106
133
  }
107
134
  /** Configure DLQ for a queue */
108
135
  export function configureDlq(queue, ctx, config) {
@@ -145,6 +172,13 @@ export function retryCompletedJobs(queue, ctx, jobId, options = {}) {
145
172
  }
146
173
  /** Requeue a single completed job */
147
174
  function requeueCompletedJob(job, ctx) {
175
+ const currentLocation = ctx.jobIndex.get(job.id);
176
+ if (currentLocation?.type !== 'completed' || currentLocation.queueName !== job.queue)
177
+ return 0;
178
+ const shard = ctx.shards[shardIndex(job.queue)];
179
+ const keyOwner = job.uniqueKey ? shard.getUniqueKeyEntry(job.queue, job.uniqueKey) : null;
180
+ if (keyOwner && keyOwner.jobId !== job.id)
181
+ return 0;
148
182
  const now = Date.now();
149
183
  const timeline = [...job.timeline];
150
184
  if (timeline.length < MAX_TIMELINE_ENTRIES) {
@@ -162,11 +196,11 @@ function requeueCompletedJob(job, ctx) {
162
196
  timeline,
163
197
  };
164
198
  setDlqRetryState(requeued, null);
199
+ shard.assignGroupFifoOrder(requeued);
165
200
  // Commit the durable transition before publishing the new in-memory state.
166
201
  // A SQLite failure therefore leaves the completed generation authoritative.
167
202
  ctx.storage?.requeueCompletedJob(requeued);
168
203
  const idx = shardIndex(requeued.queue);
169
- const shard = ctx.shards[idx];
170
204
  shard.getQueue(requeued.queue).push(requeued);
171
205
  shard.incrementQueued(requeued.id, false, requeued.createdAt, requeued.queue, requeued.runAt);
172
206
  ctx.jobIndex.set(requeued.id, {
@@ -177,6 +211,11 @@ function requeueCompletedJob(job, ctx) {
177
211
  ctx.completedJobs.delete(requeued.id);
178
212
  ctx.completedJobsData.delete(requeued.id);
179
213
  ctx.jobResults.delete(requeued.id);
214
+ if (requeued.customId)
215
+ ctx.customIdMap.set(requeued.customId, requeued.id);
216
+ if (requeued.uniqueKey) {
217
+ shard.registerUniqueKeyWithTtl(requeued.queue, requeued.uniqueKey, requeued.id, requeued.deduplicationTtl ?? undefined);
218
+ }
180
219
  shard.notify(requeued.queue);
181
220
  return 1;
182
221
  }