bunqueue 2.8.59 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/README.md +109 -77
  2. package/dist/application/background/dlq.js +2 -0
  3. package/dist/application/contextFactory.js +5 -0
  4. package/dist/application/dlqManager.d.ts +11 -1
  5. package/dist/application/dlqManager.js +48 -10
  6. package/dist/application/dlqRetry.js +59 -38
  7. package/dist/application/eventsManager.d.ts +1 -1
  8. package/dist/application/eventsManager.js +30 -26
  9. package/dist/application/lockManager.js +1 -1
  10. package/dist/application/operations/jobManagement.d.ts +4 -0
  11. package/dist/application/operations/jobMoveOperations.js +9 -4
  12. package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
  13. package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
  14. package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
  15. package/dist/application/postgres-queue-manager/cloud.js +55 -0
  16. package/dist/application/postgres-queue-manager/config.d.ts +5 -0
  17. package/dist/application/postgres-queue-manager/config.js +1 -0
  18. package/dist/application/postgres-queue-manager/control.d.ts +26 -0
  19. package/dist/application/postgres-queue-manager/control.js +186 -0
  20. package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
  21. package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
  22. package/dist/application/postgres-queue-manager/delivery.d.ts +19 -0
  23. package/dist/application/postgres-queue-manager/delivery.js +80 -0
  24. package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
  25. package/dist/application/postgres-queue-manager/lease.js +88 -0
  26. package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
  27. package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
  28. package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
  29. package/dist/application/postgres-queue-manager/maintenance.js +100 -0
  30. package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
  31. package/dist/application/postgres-queue-manager/operationGate.js +96 -0
  32. package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
  33. package/dist/application/postgres-queue-manager/operations.js +194 -0
  34. package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
  35. package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
  36. package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
  37. package/dist/application/postgres-queue-manager/queries.js +231 -0
  38. package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
  39. package/dist/application/postgres-queue-manager/relationships.js +45 -0
  40. package/dist/application/postgres-queue-manager/services.d.ts +31 -0
  41. package/dist/application/postgres-queue-manager/services.js +149 -0
  42. package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
  43. package/dist/application/postgres-queue-manager/snapshot.js +236 -0
  44. package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
  45. package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
  46. package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
  47. package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
  48. package/dist/application/postgres-queue-manager/state.d.ts +47 -0
  49. package/dist/application/postgres-queue-manager/state.js +273 -0
  50. package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
  51. package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
  52. package/dist/application/postgresQueueManager.d.ts +2 -0
  53. package/dist/application/postgresQueueManager.js +1 -0
  54. package/dist/application/queue-manager/ack.js +1 -1
  55. package/dist/application/queue-manager/delivery.js +1 -1
  56. package/dist/application/queue-manager/flow-failures.js +4 -0
  57. package/dist/application/queue-manager/limits.d.ts +2 -0
  58. package/dist/application/queue-manager/limits.js +4 -0
  59. package/dist/application/queue-manager/observability.js +15 -3
  60. package/dist/application/queue-manager/queries.d.ts +7 -0
  61. package/dist/application/queue-manager/queries.js +9 -0
  62. package/dist/application/queue-manager/services.d.ts +1 -1
  63. package/dist/application/queue-manager/services.js +4 -2
  64. package/dist/application/queue-manager/state.js +16 -1
  65. package/dist/application/statsManager.d.ts +1 -1
  66. package/dist/application/statsManager.js +1 -1
  67. package/dist/cli/commands/server.js +1 -1
  68. package/dist/client/events.js +2 -2
  69. package/dist/client/flow.js +0 -1
  70. package/dist/client/forwarder.d.ts +6 -4
  71. package/dist/client/forwarder.js +5 -3
  72. package/dist/client/queue/dlq.d.ts +2 -0
  73. package/dist/client/queue/dlq.js +14 -0
  74. package/dist/client/queue/dlqOps.d.ts +2 -0
  75. package/dist/client/queue/dlqOps.js +4 -0
  76. package/dist/client/queue/helpers.js +2 -0
  77. package/dist/client/queue/operations/add/single.js +1 -1
  78. package/dist/client/queue/runtime/configuration.d.ts +4 -0
  79. package/dist/client/queue/runtime/configuration.js +8 -0
  80. package/dist/client/sandboxed/runtime/state.js +2 -2
  81. package/dist/client/tcp/connection.d.ts +1 -1
  82. package/dist/client/tcp/connection.js +1 -1
  83. package/dist/client/tcp/runtime/state.js +2 -2
  84. package/dist/client/types/metrics.d.ts +1 -1
  85. package/dist/client/worker/runtime/control.js +0 -1
  86. package/dist/client/worker/runtime/execution.js +3 -8
  87. package/dist/client/worker/runtime/lifecycle.js +0 -1
  88. package/dist/client/worker/runtime/polling.d.ts +1 -0
  89. package/dist/client/worker/runtime/polling.js +9 -0
  90. package/dist/client/worker/runtime/state.js +3 -3
  91. package/dist/client/workflow/compensationChild.d.ts +2 -0
  92. package/dist/client/workflow/compensationChild.js +3 -1
  93. package/dist/client/workflow/compensationPass.d.ts +1 -0
  94. package/dist/client/workflow/compensationPass.js +18 -2
  95. package/dist/client/workflow/compensator.d.ts +2 -1
  96. package/dist/client/workflow/compensator.js +11 -3
  97. package/dist/client/workflow/engine.js +1 -1
  98. package/dist/client/workflow/executionFence.d.ts +15 -0
  99. package/dist/client/workflow/executionFence.js +29 -0
  100. package/dist/client/workflow/executor.d.ts +5 -11
  101. package/dist/client/workflow/executor.js +48 -47
  102. package/dist/client/workflow/executorLifecycle.d.ts +1 -0
  103. package/dist/client/workflow/executorLifecycle.js +41 -10
  104. package/dist/client/workflow/executorNodes.d.ts +1 -0
  105. package/dist/client/workflow/executorNodes.js +45 -16
  106. package/dist/client/workflow/executorQueue.d.ts +9 -0
  107. package/dist/client/workflow/executorQueue.js +15 -0
  108. package/dist/client/workflow/forEachRunner.d.ts +4 -0
  109. package/dist/client/workflow/forEachRunner.js +70 -0
  110. package/dist/client/workflow/loops.d.ts +4 -5
  111. package/dist/client/workflow/loops.js +27 -93
  112. package/dist/client/workflow/mapRunner.d.ts +1 -1
  113. package/dist/client/workflow/mapRunner.js +11 -2
  114. package/dist/client/workflow/recovery.d.ts +1 -0
  115. package/dist/client/workflow/recovery.js +24 -18
  116. package/dist/client/workflow/rollbackControl.d.ts +1 -0
  117. package/dist/client/workflow/rollbackControl.js +5 -2
  118. package/dist/client/workflow/runner.d.ts +3 -1
  119. package/dist/client/workflow/runner.js +26 -3
  120. package/dist/client/workflow/stepTypes.js +1 -1
  121. package/dist/client/workflow/subWorkflowRunner.d.ts +1 -0
  122. package/dist/client/workflow/subWorkflowRunner.js +7 -0
  123. package/dist/client/workflow/waitFor.d.ts +4 -0
  124. package/dist/client/workflow/waitFor.js +23 -11
  125. package/dist/client/workflow/workflowDecisions.d.ts +1 -1
  126. package/dist/client/workflow/workflowDecisions.js +3 -1
  127. package/dist/config/resolve.d.ts +14 -0
  128. package/dist/config/resolve.js +50 -5
  129. package/dist/config/types.d.ts +18 -0
  130. package/dist/domain/queue/dlqShard.d.ts +2 -0
  131. package/dist/domain/queue/dlqShard.js +16 -0
  132. package/dist/domain/queue/shard/dlq.d.ts +1 -0
  133. package/dist/domain/queue/shard/dlq.js +3 -0
  134. package/dist/domain/types/commands/dlq.d.ts +5 -0
  135. package/dist/domain/types/commands/union.d.ts +1 -1
  136. package/dist/domain/types/cron.d.ts +5 -0
  137. package/dist/domain/types/cron.js +14 -3
  138. package/dist/domain/types/dlq.d.ts +1 -1
  139. package/dist/domain/types/dlq.js +1 -2
  140. package/dist/domain/types/jobs/model.d.ts +1 -1
  141. package/dist/infrastructure/cloud/commandHandler.js +5 -3
  142. package/dist/infrastructure/cloud/commands/integrations.js +11 -11
  143. package/dist/infrastructure/cloud/commands/jobs.js +48 -40
  144. package/dist/infrastructure/cloud/commands/queues.js +61 -75
  145. package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
  146. package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
  147. package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
  148. package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
  149. package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
  150. package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
  151. package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
  152. package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
  153. package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
  154. package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
  155. package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
  156. package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
  157. package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
  158. package/dist/infrastructure/cloud/snapshot/config.js +43 -0
  159. package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
  160. package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
  161. package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
  162. package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
  163. package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
  164. package/dist/infrastructure/cloud/statsRefresh.js +2 -1
  165. package/dist/infrastructure/cloud/types/command.d.ts +2 -2
  166. package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
  167. package/dist/infrastructure/persistence/postgres/admission.js +193 -0
  168. package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
  169. package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
  170. package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
  171. package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
  172. package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
  173. package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
  174. package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
  175. package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
  176. package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
  177. package/dist/infrastructure/persistence/postgres/batchAdmission.js +110 -0
  178. package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
  179. package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
  180. package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
  181. package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
  182. package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
  183. package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
  184. package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +11 -0
  185. package/dist/infrastructure/persistence/postgres/claimBatch.js +56 -0
  186. package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +4 -0
  187. package/dist/infrastructure/persistence/postgres/claimSelection.js +198 -0
  188. package/dist/infrastructure/persistence/postgres/claims.d.ts +3 -0
  189. package/dist/infrastructure/persistence/postgres/claims.js +137 -0
  190. package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
  191. package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
  192. package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
  193. package/dist/infrastructure/persistence/postgres/codec.js +82 -0
  194. package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
  195. package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
  196. package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
  197. package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
  198. package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
  199. package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
  200. package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
  201. package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
  202. package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
  203. package/dist/infrastructure/persistence/postgres/connection.js +15 -0
  204. package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
  205. package/dist/infrastructure/persistence/postgres/context.js +74 -0
  206. package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
  207. package/dist/infrastructure/persistence/postgres/control.js +105 -0
  208. package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
  209. package/dist/infrastructure/persistence/postgres/crons.js +205 -0
  210. package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
  211. package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
  212. package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
  213. package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
  214. package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
  215. package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
  216. package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
  217. package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
  218. package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
  219. package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
  220. package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
  221. package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
  222. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
  223. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
  224. package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
  225. package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
  226. package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
  227. package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
  228. package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
  229. package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +253 -0
  230. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +24 -0
  231. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +111 -0
  232. package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
  233. package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
  234. package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
  235. package/dist/infrastructure/persistence/postgres/events.js +249 -0
  236. package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
  237. package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
  238. package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
  239. package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
  240. package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
  241. package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
  242. package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
  243. package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
  244. package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
  245. package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
  246. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
  247. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
  248. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
  249. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
  250. package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
  251. package/dist/infrastructure/persistence/postgres/logs.js +70 -0
  252. package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
  253. package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
  254. package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
  255. package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
  256. package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
  257. package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
  258. package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
  259. package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
  260. package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
  261. package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
  262. package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
  263. package/dist/infrastructure/persistence/postgres/policies.js +18 -0
  264. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
  265. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
  266. package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
  267. package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
  268. package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
  269. package/dist/infrastructure/persistence/postgres/queries.js +152 -0
  270. package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
  271. package/dist/infrastructure/persistence/postgres/queueDestruction.js +154 -0
  272. package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
  273. package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
  274. package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
  275. package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
  276. package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
  277. package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
  278. package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
  279. package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
  280. package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
  281. package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
  282. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
  283. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
  284. package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
  285. package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
  286. package/dist/infrastructure/persistence/postgres/runtime.d.ts +46 -0
  287. package/dist/infrastructure/persistence/postgres/runtime.js +236 -0
  288. package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
  289. package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
  290. package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
  291. package/dist/infrastructure/persistence/postgres/schema.js +257 -0
  292. package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
  293. package/dist/infrastructure/persistence/postgres/schemaInitialization.js +221 -0
  294. package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
  295. package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
  296. package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
  297. package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
  298. package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
  299. package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
  300. package/dist/infrastructure/persistence/postgres/store.d.ts +90 -0
  301. package/dist/infrastructure/persistence/postgres/store.js +136 -0
  302. package/dist/infrastructure/persistence/postgres/telemetry.d.ts +6 -0
  303. package/dist/infrastructure/persistence/postgres/telemetry.js +88 -0
  304. package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
  305. package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
  306. package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
  307. package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
  308. package/dist/infrastructure/persistence/postgres/types.d.ts +175 -0
  309. package/dist/infrastructure/persistence/postgres/types.js +1 -0
  310. package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
  311. package/dist/infrastructure/persistence/postgres/workers.js +126 -0
  312. package/dist/infrastructure/persistence/postgres.d.ts +2 -0
  313. package/dist/infrastructure/persistence/postgres.js +1 -0
  314. package/dist/infrastructure/persistence/sqlite/jobs.js +2 -0
  315. package/dist/infrastructure/persistence/sqlite.d.ts +1 -1
  316. package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
  317. package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
  318. package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
  319. package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
  320. package/dist/infrastructure/scheduler/cron/validation.js +11 -0
  321. package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
  322. package/dist/infrastructure/scheduler/cronParser.js +144 -11
  323. package/dist/infrastructure/server/bootstrap.d.ts +2 -3
  324. package/dist/infrastructure/server/bootstrap.js +33 -59
  325. package/dist/infrastructure/server/errors.d.ts +4 -0
  326. package/dist/infrastructure/server/errors.js +54 -0
  327. package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
  328. package/dist/infrastructure/server/handler-routes/control.js +3 -1
  329. package/dist/infrastructure/server/handler-routes/jobs.js +0 -3
  330. package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
  331. package/dist/infrastructure/server/handler.js +9 -11
  332. package/dist/infrastructure/server/handlerRoutes.d.ts +2 -2
  333. package/dist/infrastructure/server/handlerRoutes.js +2 -2
  334. package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
  335. package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
  336. package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
  337. package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
  338. package/dist/infrastructure/server/handlers/core.js +32 -31
  339. package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
  340. package/dist/infrastructure/server/handlers/cron.js +60 -59
  341. package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
  342. package/dist/infrastructure/server/handlers/dashboard.js +21 -8
  343. package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
  344. package/dist/infrastructure/server/handlers/dlq.js +35 -9
  345. package/dist/infrastructure/server/handlers/flow.js +2 -1
  346. package/dist/infrastructure/server/handlers/management.d.ts +5 -5
  347. package/dist/infrastructure/server/handlers/management.js +36 -9
  348. package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
  349. package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
  350. package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
  351. package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
  352. package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
  353. package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
  354. package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
  355. package/dist/infrastructure/server/handlers/pushBatchValidation.js +52 -7
  356. package/dist/infrastructure/server/handlers/query.d.ts +1 -1
  357. package/dist/infrastructure/server/handlers/query.js +5 -4
  358. package/dist/infrastructure/server/http.js +2 -2
  359. package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
  360. package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
  361. package/dist/infrastructure/server/httpEndpoints.js +20 -7
  362. package/dist/infrastructure/server/httpRouteQueues.js +11 -1
  363. package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
  364. package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
  365. package/dist/infrastructure/server/sseHandler.js +8 -3
  366. package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
  367. package/dist/infrastructure/server/storageAdapter.js +61 -0
  368. package/dist/infrastructure/server/storageManager.d.ts +12 -0
  369. package/dist/infrastructure/server/storageManager.js +102 -0
  370. package/dist/infrastructure/server/tcp.js +2 -3
  371. package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
  372. package/dist/infrastructure/server/ws/snapshots.js +19 -4
  373. package/dist/infrastructure/server/wsHandler.js +10 -5
  374. package/dist/main.js +1 -1
  375. package/dist/mcp/backend/embedded/services.js +2 -1
  376. package/dist/mcp/index.d.ts +2 -2
  377. package/dist/mcp/index.js +2 -2
  378. package/dist/shared/storageHealth.d.ts +9 -0
  379. package/dist/shared/storageHealth.js +12 -0
  380. package/package.json +85 -74
@@ -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;
@@ -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,193 @@
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, 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}, ${job.parentId}
86
+ )
87
+ `;
88
+ await insertDependencies(tx, ctx, job);
89
+ }
90
+ /** Insert one job inside the caller's transaction and return its authoritative generation. */
91
+ export async function admitPostgresJob(tx, ctx, job, options = {}) {
92
+ const parentQueue = options.linkParent === false ? null : linkedPostgresParentQueue(job);
93
+ if (!options.queueLifecycleLocksHeld) {
94
+ await lockPostgresAdmissionQueues(tx, ctx, [job.queue]);
95
+ }
96
+ if (!options.dependencyLocksHeld) {
97
+ await lockPostgresDependencyCompletions(tx, ctx, [job.id, ...job.dependsOn]);
98
+ }
99
+ if (parentQueue)
100
+ await lockPostgresAdmissionParent(tx, ctx, job, parentQueue);
101
+ if (!options.preparedFlow)
102
+ await lockPostgresAdmissionKeys(tx, ctx, [job]);
103
+ const now = options.preparedFlow?.now ?? (await databaseNow(tx));
104
+ const existingById = await findById(tx, ctx, String(job.id));
105
+ if (existingById) {
106
+ if (options.rejectExisting)
107
+ throw new Error(`Flow job ${String(job.id)} already exists`);
108
+ if (isLive(existingById.state)) {
109
+ return { job: existingById.job, inserted: false, state: existingById.state };
110
+ }
111
+ }
112
+ let existingByKeyRow = await findByUniqueKey(tx, ctx, job);
113
+ if (existingByKeyRow &&
114
+ existingByKeyRow.unique_expires_at !== null &&
115
+ Number(existingByKeyRow.unique_expires_at) <= now) {
116
+ await clearPostgresDeduplication(tx, ctx, existingByKeyRow, now);
117
+ existingByKeyRow = null;
118
+ }
119
+ if (existingByKeyRow) {
120
+ let existingByKey = decodePostgresJob(existingByKeyRow);
121
+ if (job.deduplicationReplace) {
122
+ if (existingByKeyRow.state === 'active') {
123
+ await clearPostgresDeduplication(tx, ctx, existingByKeyRow, now);
124
+ }
125
+ else {
126
+ const acquired = await tryLockPostgresDependencyCompletions(tx, ctx, [
127
+ existingByKey.job.id,
128
+ ]);
129
+ if (!acquired) {
130
+ await recordPostgresEvent(tx, ctx, {
131
+ queue: existingByKey.job.queue,
132
+ type: 'duplicated',
133
+ jobId: existingByKey.job.id,
134
+ occurredAt: now,
135
+ job: existingByKey.job,
136
+ state: existingByKey.state,
137
+ });
138
+ return { job: existingByKey.job, inserted: false, state: existingByKey.state };
139
+ }
140
+ await assertNoLivePostgresCompletionConsumers(tx, ctx, [existingByKey.job.id]);
141
+ await deleteTerminalGeneration(tx, ctx, existingByKeyRow.id);
142
+ await recordPostgresDeduplicationRemoval(tx, ctx, existingByKeyRow, now);
143
+ }
144
+ }
145
+ else {
146
+ if (job.deduplicationExtend && job.deduplicationTtl !== null) {
147
+ existingByKey = await extendPostgresDeduplication(tx, ctx, existingByKeyRow, job.deduplicationTtl, now);
148
+ }
149
+ await recordPostgresEvent(tx, ctx, {
150
+ queue: existingByKey.job.queue,
151
+ type: 'duplicated',
152
+ jobId: existingByKey.job.id,
153
+ occurredAt: now,
154
+ job: existingByKey.job,
155
+ state: existingByKey.state,
156
+ });
157
+ return { job: existingByKey.job, inserted: false, state: existingByKey.state };
158
+ }
159
+ }
160
+ if (existingById) {
161
+ await assertNoLivePostgresCompletionConsumers(tx, ctx, [job.id], options.completionConsumerExemptions);
162
+ await deleteTerminalGeneration(tx, ctx, String(job.id));
163
+ }
164
+ else if (!options.preparedFlow) {
165
+ await retirePostgresCompletionGenerations(tx, ctx, [job.id], options.completionConsumerExemptions);
166
+ }
167
+ if (!options.dependencyExistenceCheckDeferred) {
168
+ await assertPostgresDependenciesExist(tx, ctx, job.dependsOn);
169
+ }
170
+ if (job.runAt === job.createdAt)
171
+ job.runAt = now;
172
+ const state = postgresStateForJob(job, await postgresDependenciesSatisfied(tx, ctx, job.dependsOn), now);
173
+ const timelineIndex = job.timeline.length < MAX_TIMELINE_ENTRIES ? job.timeline.length : undefined;
174
+ if (timelineIndex !== undefined)
175
+ job.timeline.push({ state, timestamp: now });
176
+ await insertRow(tx, ctx, job, state, now);
177
+ if (!options.preparedFlow)
178
+ await registerPostgresAdmissionQueues(tx, ctx, [job.queue]);
179
+ const pushedEventId = options.preparedFlow
180
+ ? undefined
181
+ : await recordPostgresEvent(tx, ctx, {
182
+ queue: job.queue,
183
+ type: 'pushed',
184
+ jobId: job.id,
185
+ occurredAt: now,
186
+ job,
187
+ state,
188
+ });
189
+ if (parentQueue && job.parentId) {
190
+ await updatePostgresJobParentInTransaction(tx, ctx, job.id, job.parentId, true);
191
+ }
192
+ return { job, inserted: true, state, pushedEventId, timelineIndex };
193
+ }
@@ -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,110 @@
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) {
61
+ return prepared.map(({ job, state }) => ({
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
+ }));
82
+ }
83
+ async function insertRows(tx, ctx, prepared, now) {
84
+ const records = toRecords(prepared, ctx.config.namespace, now);
85
+ let insertedCount = 0;
86
+ for (let offset = 0; offset < records.length; offset += 1000) {
87
+ const batch = records.slice(offset, offset + 1000);
88
+ const inserted = await tx `
89
+ 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')}
90
+ ON CONFLICT DO NOTHING
91
+ RETURNING id
92
+ `;
93
+ insertedCount += inserted.length;
94
+ }
95
+ if (insertedCount !== prepared.length)
96
+ throw new PostgresBatchAdmissionConflict();
97
+ }
98
+ /** Admit independent jobs atomically with one insert and one event write. */
99
+ export async function admitPostgresJobsBatch(tx, ctx, jobs) {
100
+ await lockPostgresAdmissionQueues(tx, ctx, jobs.map((job) => job.queue));
101
+ await lockPostgresDependencyCompletions(tx, ctx, jobs.map((job) => job.id));
102
+ await lockPostgresAdmissionKeys(tx, ctx, jobs);
103
+ const now = await databaseNow(tx);
104
+ const prepared = prepareAdmissions(jobs, now);
105
+ await insertRows(tx, ctx, prepared, now);
106
+ await registerPostgresAdmissionQueues(tx, ctx, prepared.map(({ job }) => job.queue));
107
+ await retirePostgresCompletionGenerations(tx, ctx, jobs.map((job) => job.id));
108
+ await recordPostgresJobEvents(tx, ctx, prepared.map(({ job, state }) => ({ job, type: 'pushed', state })), now);
109
+ return prepared.map(({ job }) => job);
110
+ }
@@ -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[]>;