bunqueue 2.8.60 → 2.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (427) hide show
  1. package/README.md +114 -78
  2. package/dist/application/background/dlq.js +2 -0
  3. package/dist/application/background/recovery/active.d.ts +1 -0
  4. package/dist/application/background/recovery/active.js +12 -0
  5. package/dist/application/background/recovery/index.js +3 -1
  6. package/dist/application/background/recovery/pending.js +1 -0
  7. package/dist/application/cleanupTasks.js +4 -16
  8. package/dist/application/contextFactory.js +8 -0
  9. package/dist/application/dlqManager.d.ts +10 -0
  10. package/dist/application/dlqManager.js +48 -9
  11. package/dist/application/dlqRetry.js +60 -38
  12. package/dist/application/eventsManager.d.ts +9 -2
  13. package/dist/application/eventsManager.js +106 -55
  14. package/dist/application/operations/ackHelpers.d.ts +1 -4
  15. package/dist/application/operations/ackHelpers.js +39 -14
  16. package/dist/application/operations/flowPush.js +1 -0
  17. package/dist/application/operations/flowValidation.js +4 -4
  18. package/dist/application/operations/jobManagement.d.ts +4 -0
  19. package/dist/application/operations/jobMoveOperations.js +9 -4
  20. package/dist/application/operations/pull.d.ts +7 -2
  21. package/dist/application/operations/pull.js +15 -57
  22. package/dist/application/operations/pullFinalization.d.ts +6 -0
  23. package/dist/application/operations/pullFinalization.js +69 -0
  24. package/dist/application/operations/pullStateTransition.d.ts +8 -7
  25. package/dist/application/operations/pullStateTransition.js +25 -18
  26. package/dist/application/operations/push.js +3 -0
  27. package/dist/application/operations/pushBatch.js +14 -7
  28. package/dist/application/operations/pushContext.d.ts +2 -1
  29. package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
  30. package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
  31. package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
  32. package/dist/application/postgres-queue-manager/cloud.js +55 -0
  33. package/dist/application/postgres-queue-manager/config.d.ts +5 -0
  34. package/dist/application/postgres-queue-manager/config.js +1 -0
  35. package/dist/application/postgres-queue-manager/control.d.ts +30 -0
  36. package/dist/application/postgres-queue-manager/control.js +210 -0
  37. package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
  38. package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
  39. package/dist/application/postgres-queue-manager/delivery.d.ts +20 -0
  40. package/dist/application/postgres-queue-manager/delivery.js +83 -0
  41. package/dist/application/postgres-queue-manager/groupQueries.d.ts +10 -0
  42. package/dist/application/postgres-queue-manager/groupQueries.js +40 -0
  43. package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
  44. package/dist/application/postgres-queue-manager/lease.js +88 -0
  45. package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
  46. package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
  47. package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
  48. package/dist/application/postgres-queue-manager/maintenance.js +100 -0
  49. package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
  50. package/dist/application/postgres-queue-manager/operationGate.js +96 -0
  51. package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
  52. package/dist/application/postgres-queue-manager/operations.js +194 -0
  53. package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
  54. package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
  55. package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
  56. package/dist/application/postgres-queue-manager/queries.js +231 -0
  57. package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
  58. package/dist/application/postgres-queue-manager/relationships.js +45 -0
  59. package/dist/application/postgres-queue-manager/services.d.ts +31 -0
  60. package/dist/application/postgres-queue-manager/services.js +149 -0
  61. package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
  62. package/dist/application/postgres-queue-manager/snapshot.js +236 -0
  63. package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
  64. package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
  65. package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
  66. package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
  67. package/dist/application/postgres-queue-manager/state.d.ts +47 -0
  68. package/dist/application/postgres-queue-manager/state.js +273 -0
  69. package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
  70. package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
  71. package/dist/application/postgresQueueManager.d.ts +2 -0
  72. package/dist/application/postgresQueueManager.js +1 -0
  73. package/dist/application/queue-manager/control.js +1 -0
  74. package/dist/application/queue-manager/delivery.d.ts +5 -4
  75. package/dist/application/queue-manager/delivery.js +15 -8
  76. package/dist/application/queue-manager/flow-failures.js +4 -0
  77. package/dist/application/queue-manager/limits.d.ts +13 -0
  78. package/dist/application/queue-manager/limits.js +65 -0
  79. package/dist/application/queue-manager/observability.js +15 -3
  80. package/dist/application/queue-manager/queries.d.ts +7 -0
  81. package/dist/application/queue-manager/queries.js +9 -0
  82. package/dist/application/queue-manager/services.d.ts +1 -1
  83. package/dist/application/queue-manager/services.js +4 -2
  84. package/dist/application/queue-manager/state.js +17 -2
  85. package/dist/application/queueTelemetryJournal.d.ts +2 -0
  86. package/dist/application/queueTelemetryJournal.js +23 -0
  87. package/dist/application/types/ack.d.ts +3 -1
  88. package/dist/cli/commandRouter.d.ts +1 -1
  89. package/dist/cli/commands/server.js +1 -1
  90. package/dist/client/flowPush.js +6 -0
  91. package/dist/client/forwarder.d.ts +6 -4
  92. package/dist/client/forwarder.js +5 -3
  93. package/dist/client/groupId.d.ts +1 -0
  94. package/dist/client/groupId.js +1 -0
  95. package/dist/client/index.d.ts +1 -1
  96. package/dist/client/jobHelpers.js +1 -0
  97. package/dist/client/queue/dlq.d.ts +2 -0
  98. package/dist/client/queue/dlq.js +14 -0
  99. package/dist/client/queue/dlqOps.d.ts +2 -0
  100. package/dist/client/queue/dlqOps.js +4 -0
  101. package/dist/client/queue/helpers.js +2 -0
  102. package/dist/client/queue/operations/add/bulk.js +4 -4
  103. package/dist/client/queue/operations/add/payload.d.ts +1 -0
  104. package/dist/client/queue/operations/add/payload.js +7 -1
  105. package/dist/client/queue/operations/add/single.js +3 -3
  106. package/dist/client/queue/operations/groups.d.ts +21 -0
  107. package/dist/client/queue/operations/groups.js +88 -0
  108. package/dist/client/queue/runtime/configuration.d.ts +4 -0
  109. package/dist/client/queue/runtime/configuration.js +8 -0
  110. package/dist/client/queue/runtime/queries.d.ts +13 -0
  111. package/dist/client/queue/runtime/queries.js +31 -0
  112. package/dist/client/queue/runtime/state.d.ts +4 -0
  113. package/dist/client/queue/runtime/state.js +1 -0
  114. package/dist/client/types/index.d.ts +2 -2
  115. package/dist/client/types/options.d.ts +4 -0
  116. package/dist/client/types/worker.d.ts +8 -0
  117. package/dist/client/worker/runtime/execution.js +4 -8
  118. package/dist/client/worker/runtime/options.js +3 -0
  119. package/dist/client/worker/runtime/polling.d.ts +1 -0
  120. package/dist/client/worker/runtime/polling.js +9 -0
  121. package/dist/client/worker/types/options.d.ts +2 -1
  122. package/dist/client/worker/workerPull.d.ts +2 -0
  123. package/dist/client/worker/workerPull.js +6 -4
  124. package/dist/config/resolve.d.ts +14 -0
  125. package/dist/config/resolve.js +50 -5
  126. package/dist/config/types.d.ts +18 -0
  127. package/dist/domain/job/groupFifoOrder.d.ts +9 -0
  128. package/dist/domain/job/groupFifoOrder.js +26 -0
  129. package/dist/domain/queue/dlqShard.d.ts +2 -0
  130. package/dist/domain/queue/dlqShard.js +16 -0
  131. package/dist/domain/queue/groupLimiterManager.d.ts +23 -0
  132. package/dist/domain/queue/groupLimiterManager.js +107 -0
  133. package/dist/domain/queue/groupScheduler.d.ts +31 -0
  134. package/dist/domain/queue/groupScheduler.js +256 -0
  135. package/dist/domain/queue/groupSchedulerOrder.d.ts +6 -0
  136. package/dist/domain/queue/groupSchedulerOrder.js +30 -0
  137. package/dist/domain/queue/priorityQueue.d.ts +13 -23
  138. package/dist/domain/queue/priorityQueue.js +53 -42
  139. package/dist/domain/queue/shard/dlq.d.ts +1 -0
  140. package/dist/domain/queue/shard/dlq.js +3 -0
  141. package/dist/domain/queue/shard/keys.d.ts +1 -0
  142. package/dist/domain/queue/shard/keys.js +25 -2
  143. package/dist/domain/queue/shard/lifecycle.js +4 -0
  144. package/dist/domain/queue/shard/limits.d.ts +20 -1
  145. package/dist/domain/queue/shard/limits.js +56 -0
  146. package/dist/domain/queue/shard/state.d.ts +5 -0
  147. package/dist/domain/queue/shard/state.js +9 -1
  148. package/dist/domain/types/commands/core.d.ts +3 -0
  149. package/dist/domain/types/commands/dlq.d.ts +5 -0
  150. package/dist/domain/types/commands/limits.d.ts +41 -0
  151. package/dist/domain/types/commands/union.d.ts +1 -1
  152. package/dist/domain/types/cron.d.ts +5 -0
  153. package/dist/domain/types/cron.js +14 -3
  154. package/dist/domain/types/dlq.d.ts +1 -1
  155. package/dist/domain/types/dlq.js +1 -2
  156. package/dist/domain/types/group.d.ts +21 -0
  157. package/dist/domain/types/group.js +78 -0
  158. package/dist/domain/types/jobs/model.d.ts +1 -1
  159. package/dist/domain/types/priorityQueue.d.ts +1 -0
  160. package/dist/infrastructure/cloud/commandHandler.js +5 -3
  161. package/dist/infrastructure/cloud/commands/integrations.js +11 -11
  162. package/dist/infrastructure/cloud/commands/jobs.js +48 -40
  163. package/dist/infrastructure/cloud/commands/queues.js +61 -75
  164. package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
  165. package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
  166. package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
  167. package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
  168. package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
  169. package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
  170. package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
  171. package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
  172. package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
  173. package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
  174. package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
  175. package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
  176. package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
  177. package/dist/infrastructure/cloud/snapshot/config.js +43 -0
  178. package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
  179. package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
  180. package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
  181. package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
  182. package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
  183. package/dist/infrastructure/cloud/statsRefresh.js +2 -1
  184. package/dist/infrastructure/cloud/types/command.d.ts +2 -2
  185. package/dist/infrastructure/persistence/jobOptionsBlob.d.ts +1 -0
  186. package/dist/infrastructure/persistence/jobOptionsBlob.js +10 -2
  187. package/dist/infrastructure/persistence/migrations.js +11 -0
  188. package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
  189. package/dist/infrastructure/persistence/postgres/admission.js +196 -0
  190. package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
  191. package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
  192. package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
  193. package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
  194. package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
  195. package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
  196. package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
  197. package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
  198. package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
  199. package/dist/infrastructure/persistence/postgres/batchAdmission.js +128 -0
  200. package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
  201. package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
  202. package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
  203. package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
  204. package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
  205. package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
  206. package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +14 -0
  207. package/dist/infrastructure/persistence/postgres/claimBatch.js +69 -0
  208. package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +11 -0
  209. package/dist/infrastructure/persistence/postgres/claimSelection.js +140 -0
  210. package/dist/infrastructure/persistence/postgres/claims.d.ts +10 -0
  211. package/dist/infrastructure/persistence/postgres/claims.js +153 -0
  212. package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
  213. package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
  214. package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
  215. package/dist/infrastructure/persistence/postgres/codec.js +82 -0
  216. package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
  217. package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
  218. package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
  219. package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
  220. package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
  221. package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
  222. package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
  223. package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
  224. package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
  225. package/dist/infrastructure/persistence/postgres/connection.js +15 -0
  226. package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
  227. package/dist/infrastructure/persistence/postgres/context.js +74 -0
  228. package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
  229. package/dist/infrastructure/persistence/postgres/control.js +105 -0
  230. package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
  231. package/dist/infrastructure/persistence/postgres/crons.js +205 -0
  232. package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
  233. package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
  234. package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
  235. package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
  236. package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
  237. package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
  238. package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
  239. package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
  240. package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
  241. package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
  242. package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
  243. package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
  244. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
  245. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
  246. package/dist/infrastructure/persistence/postgres/eventCatchupCursors.d.ts +32 -0
  247. package/dist/infrastructure/persistence/postgres/eventCatchupCursors.js +66 -0
  248. package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
  249. package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
  250. package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
  251. package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
  252. package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
  253. package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +267 -0
  254. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +26 -0
  255. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +129 -0
  256. package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
  257. package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
  258. package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
  259. package/dist/infrastructure/persistence/postgres/events.js +276 -0
  260. package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
  261. package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
  262. package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
  263. package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
  264. package/dist/infrastructure/persistence/postgres/groupClaims.d.ts +15 -0
  265. package/dist/infrastructure/persistence/postgres/groupClaims.js +95 -0
  266. package/dist/infrastructure/persistence/postgres/groupSchema.d.ts +2 -0
  267. package/dist/infrastructure/persistence/postgres/groupSchema.js +155 -0
  268. package/dist/infrastructure/persistence/postgres/groupSchemaFingerprint.d.ts +3 -0
  269. package/dist/infrastructure/persistence/postgres/groupSchemaFingerprint.js +127 -0
  270. package/dist/infrastructure/persistence/postgres/groupStateRetention.d.ts +3 -0
  271. package/dist/infrastructure/persistence/postgres/groupStateRetention.js +93 -0
  272. package/dist/infrastructure/persistence/postgres/groups.d.ts +11 -0
  273. package/dist/infrastructure/persistence/postgres/groups.js +117 -0
  274. package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
  275. package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
  276. package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
  277. package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
  278. package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
  279. package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
  280. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
  281. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
  282. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
  283. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
  284. package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
  285. package/dist/infrastructure/persistence/postgres/logs.js +70 -0
  286. package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
  287. package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
  288. package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
  289. package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
  290. package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
  291. package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
  292. package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
  293. package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
  294. package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
  295. package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
  296. package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
  297. package/dist/infrastructure/persistence/postgres/policies.js +18 -0
  298. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
  299. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
  300. package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
  301. package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
  302. package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
  303. package/dist/infrastructure/persistence/postgres/queries.js +152 -0
  304. package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
  305. package/dist/infrastructure/persistence/postgres/queueDestruction.js +158 -0
  306. package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
  307. package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
  308. package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
  309. package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
  310. package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
  311. package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
  312. package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
  313. package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
  314. package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
  315. package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
  316. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
  317. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
  318. package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
  319. package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
  320. package/dist/infrastructure/persistence/postgres/runtime.d.ts +45 -0
  321. package/dist/infrastructure/persistence/postgres/runtime.js +238 -0
  322. package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
  323. package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
  324. package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
  325. package/dist/infrastructure/persistence/postgres/schema.js +261 -0
  326. package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
  327. package/dist/infrastructure/persistence/postgres/schemaInitialization.js +226 -0
  328. package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
  329. package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
  330. package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
  331. package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
  332. package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
  333. package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
  334. package/dist/infrastructure/persistence/postgres/store.d.ts +100 -0
  335. package/dist/infrastructure/persistence/postgres/store.js +180 -0
  336. package/dist/infrastructure/persistence/postgres/telemetry.d.ts +8 -0
  337. package/dist/infrastructure/persistence/postgres/telemetry.js +100 -0
  338. package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
  339. package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
  340. package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
  341. package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
  342. package/dist/infrastructure/persistence/postgres/types.d.ts +176 -0
  343. package/dist/infrastructure/persistence/postgres/types.js +1 -0
  344. package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
  345. package/dist/infrastructure/persistence/postgres/workers.js +126 -0
  346. package/dist/infrastructure/persistence/postgres.d.ts +2 -0
  347. package/dist/infrastructure/persistence/postgres.js +1 -0
  348. package/dist/infrastructure/persistence/schema.d.ts +2 -2
  349. package/dist/infrastructure/persistence/schema.js +12 -1
  350. package/dist/infrastructure/persistence/sqlite/control.d.ts +14 -0
  351. package/dist/infrastructure/persistence/sqlite/control.js +69 -0
  352. package/dist/infrastructure/persistence/sqlite/jobs.d.ts +12 -0
  353. package/dist/infrastructure/persistence/sqlite/jobs.js +38 -0
  354. package/dist/infrastructure/persistence/sqlite/telemetry.d.ts +2 -1
  355. package/dist/infrastructure/persistence/sqlite/telemetry.js +5 -64
  356. package/dist/infrastructure/persistence/sqlite/telemetryWrites.d.ts +4 -0
  357. package/dist/infrastructure/persistence/sqlite/telemetryWrites.js +123 -0
  358. package/dist/infrastructure/persistence/sqliteSerializer.js +2 -0
  359. package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
  360. package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
  361. package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
  362. package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
  363. package/dist/infrastructure/scheduler/cron/validation.js +11 -0
  364. package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
  365. package/dist/infrastructure/scheduler/cronParser.js +144 -11
  366. package/dist/infrastructure/server/bootstrap.d.ts +2 -3
  367. package/dist/infrastructure/server/bootstrap.js +33 -59
  368. package/dist/infrastructure/server/errors.d.ts +4 -0
  369. package/dist/infrastructure/server/errors.js +54 -0
  370. package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
  371. package/dist/infrastructure/server/handler-routes/control.js +16 -1
  372. package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
  373. package/dist/infrastructure/server/handler.js +9 -11
  374. package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
  375. package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
  376. package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
  377. package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
  378. package/dist/infrastructure/server/handlers/core.d.ts +0 -5
  379. package/dist/infrastructure/server/handlers/core.js +47 -49
  380. package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
  381. package/dist/infrastructure/server/handlers/cron.js +60 -59
  382. package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
  383. package/dist/infrastructure/server/handlers/dashboard.js +21 -8
  384. package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
  385. package/dist/infrastructure/server/handlers/dlq.js +35 -9
  386. package/dist/infrastructure/server/handlers/flow.js +2 -1
  387. package/dist/infrastructure/server/handlers/groups.d.ts +11 -0
  388. package/dist/infrastructure/server/handlers/groups.js +55 -0
  389. package/dist/infrastructure/server/handlers/management.d.ts +5 -5
  390. package/dist/infrastructure/server/handlers/management.js +36 -9
  391. package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
  392. package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
  393. package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
  394. package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
  395. package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
  396. package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
  397. package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
  398. package/dist/infrastructure/server/handlers/pushBatchValidation.js +56 -8
  399. package/dist/infrastructure/server/handlers/query.d.ts +1 -1
  400. package/dist/infrastructure/server/handlers/query.js +5 -4
  401. package/dist/infrastructure/server/http.js +2 -2
  402. package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
  403. package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
  404. package/dist/infrastructure/server/httpEndpoints.js +18 -7
  405. package/dist/infrastructure/server/httpRouteQueues.js +11 -1
  406. package/dist/infrastructure/server/protocol/validation.d.ts +1 -0
  407. package/dist/infrastructure/server/protocol/validation.js +1 -0
  408. package/dist/infrastructure/server/protocol.d.ts +1 -1
  409. package/dist/infrastructure/server/protocol.js +1 -1
  410. package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
  411. package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
  412. package/dist/infrastructure/server/sseHandler.js +8 -3
  413. package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
  414. package/dist/infrastructure/server/storageAdapter.js +61 -0
  415. package/dist/infrastructure/server/storageManager.d.ts +12 -0
  416. package/dist/infrastructure/server/storageManager.js +102 -0
  417. package/dist/infrastructure/server/tcp.js +2 -3
  418. package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
  419. package/dist/infrastructure/server/ws/snapshots.js +19 -4
  420. package/dist/infrastructure/server/wsHandler.js +10 -5
  421. package/dist/main.js +1 -1
  422. package/dist/mcp/backend/embedded/services.js +2 -1
  423. package/dist/mcp/index.d.ts +2 -2
  424. package/dist/mcp/index.js +2 -2
  425. package/dist/shared/storageHealth.d.ts +9 -0
  426. package/dist/shared/storageHealth.js +12 -0
  427. package/package.json +75 -65
@@ -0,0 +1,205 @@
1
+ import { createCronJob } from '../../../domain/types/cron';
2
+ import { createJob, generateJobId } from '../../../domain/types/job';
3
+ import { expandCronShortcut, getNextCronRun, getNextIntervalRun } from '../../scheduler/cronParser';
4
+ import { assertPersistedCronSupported } from '../../scheduler/cron/persisted';
5
+ import { assertValidCronInput } from '../../scheduler/cron/validation';
6
+ import { postgresAdvisoryLockName } from './advisoryLocks';
7
+ import { admitPostgresJob } from './admission';
8
+ import { decodePostgresValue, encodePostgresValue } from './codec';
9
+ import { databaseNow } from './context';
10
+ import { hasActivePostgresWorker } from './workers';
11
+ import { postgresBrokerStaleMs } from './brokerSessions';
12
+ export function decodePostgresCron(row) {
13
+ const cron = decodePostgresValue(row.payload, null, `postgresCron:${row.name}`);
14
+ if (!cron)
15
+ throw new Error(`Corrupt PostgreSQL cron payload for ${row.name}`);
16
+ cron.nextRun = Number(row.next_run);
17
+ cron.executions = row.executions;
18
+ assertPersistedCronSupported(cron);
19
+ return cron;
20
+ }
21
+ function validateInput(input) {
22
+ assertValidCronInput(input);
23
+ }
24
+ function nextRun(input, now) {
25
+ if (input.schedule) {
26
+ return getNextCronRun(expandCronShortcut(input.schedule), now, input.timezone);
27
+ }
28
+ return getNextIntervalRun(input.repeatEvery, now);
29
+ }
30
+ export async function upsertPostgresCron(ctx, input) {
31
+ validateInput(input);
32
+ return await ctx.sql.begin(async (tx) => {
33
+ const existing = await tx `
34
+ SELECT name, payload, next_run, executions, max_limit
35
+ FROM bunqueue_crons
36
+ WHERE namespace = ${ctx.config.namespace} AND name = ${input.name}
37
+ FOR UPDATE
38
+ `;
39
+ const now = await databaseNow(tx);
40
+ const cron = createCronJob(input, input.immediately && !existing[0] ? now : nextRun(input, now));
41
+ cron.executions = existing[0]?.executions ?? 0;
42
+ await tx `
43
+ INSERT INTO bunqueue_crons
44
+ (namespace, name, payload, next_run, executions, max_limit, updated_at)
45
+ VALUES
46
+ (${ctx.config.namespace}, ${cron.name}, ${encodePostgresValue(cron)}, ${cron.nextRun},
47
+ ${cron.executions}, ${cron.maxLimit}, ${now})
48
+ ON CONFLICT (namespace, name) DO UPDATE SET
49
+ payload = excluded.payload,
50
+ next_run = excluded.next_run,
51
+ executions = excluded.executions,
52
+ max_limit = excluded.max_limit,
53
+ updated_at = excluded.updated_at
54
+ `;
55
+ return cron;
56
+ });
57
+ }
58
+ export async function getPostgresCron(ctx, name) {
59
+ const rows = await ctx.sql `
60
+ SELECT name, payload, next_run, executions, max_limit
61
+ FROM bunqueue_crons
62
+ WHERE namespace = ${ctx.config.namespace} AND name = ${name}
63
+ `;
64
+ return rows[0] ? decodePostgresCron(rows[0]) : undefined;
65
+ }
66
+ export async function listPostgresCrons(ctx, sql = ctx.sql) {
67
+ const rows = await sql `
68
+ SELECT name, payload, next_run, executions, max_limit
69
+ FROM bunqueue_crons
70
+ WHERE namespace = ${ctx.config.namespace}
71
+ ORDER BY name
72
+ `;
73
+ return rows.map(decodePostgresCron);
74
+ }
75
+ export async function removePostgresCron(ctx, name) {
76
+ const rows = await ctx.sql `
77
+ DELETE FROM bunqueue_crons
78
+ WHERE namespace = ${ctx.config.namespace} AND name = ${name}
79
+ RETURNING name
80
+ `;
81
+ return rows.length > 0;
82
+ }
83
+ function spawnedInput(cron) {
84
+ const uniqueKey = cron.uniqueKey ?? (cron.preventOverlap ? `cron:${cron.name}` : undefined);
85
+ const options = cron.jobOptions;
86
+ return {
87
+ name: cron.jobName,
88
+ data: cron.data,
89
+ priority: cron.priority,
90
+ uniqueKey,
91
+ dedup: cron.dedup ?? undefined,
92
+ maxAttempts: options?.maxAttempts,
93
+ backoff: options?.backoff,
94
+ timeout: options?.timeout,
95
+ delay: options?.delay,
96
+ stallTimeout: options?.stallTimeout,
97
+ removeOnComplete: options?.removeOnComplete,
98
+ removeOnFail: options?.removeOnFail,
99
+ };
100
+ }
101
+ function followingRun(cron, now) {
102
+ if (cron.schedule) {
103
+ return getNextCronRun(expandCronShortcut(cron.schedule), now, cron.timezone ?? undefined);
104
+ }
105
+ const interval = cron.repeatEvery;
106
+ const scheduled = getNextIntervalRun(interval, cron.nextRun);
107
+ return scheduled <= now ? getNextIntervalRun(interval, now) : scheduled;
108
+ }
109
+ /** Recalculate missed rows only on the oldest live broker for this namespace. */
110
+ export async function reconcilePostgresCronsOnStartup(ctx) {
111
+ const lockName = postgresAdvisoryLockName('cron-startup', ctx.config.namespace);
112
+ return await ctx.sql.begin(async (tx) => {
113
+ await tx `
114
+ SELECT pg_advisory_xact_lock(hashtextextended(${lockName}, 0))
115
+ `;
116
+ const now = await databaseNow(tx);
117
+ const [leader] = await tx `
118
+ SELECT
119
+ broker_id = ${ctx.config.brokerId}
120
+ AND session_id IS NOT DISTINCT FROM ${ctx.config.brokerSessionId} AS elected
121
+ FROM bunqueue_brokers
122
+ WHERE namespace = ${ctx.config.namespace}
123
+ AND heartbeat_at > ${now - postgresBrokerStaleMs(ctx)}
124
+ ORDER BY started_at, broker_id, session_id NULLS FIRST
125
+ LIMIT 1
126
+ `;
127
+ if (!leader?.elected)
128
+ return 0;
129
+ const rows = await tx `
130
+ SELECT name, payload, next_run, executions, max_limit
131
+ FROM bunqueue_crons
132
+ WHERE namespace = ${ctx.config.namespace} AND next_run < ${now}
133
+ ORDER BY next_run, name
134
+ FOR UPDATE
135
+ `;
136
+ let reconciled = 0;
137
+ for (const row of rows) {
138
+ const cron = decodePostgresCron(row);
139
+ if (!cron.skipMissedOnRestart && !cron.skipIfNoWorker)
140
+ continue;
141
+ cron.nextRun = cron.schedule
142
+ ? getNextCronRun(expandCronShortcut(cron.schedule), now, cron.timezone ?? undefined)
143
+ : getNextIntervalRun(cron.repeatEvery, now);
144
+ await tx `
145
+ UPDATE bunqueue_crons
146
+ SET payload = ${encodePostgresValue(cron)}, next_run = ${cron.nextRun}, updated_at = ${now}
147
+ WHERE namespace = ${ctx.config.namespace} AND name = ${cron.name}
148
+ `;
149
+ reconciled++;
150
+ }
151
+ return reconciled;
152
+ });
153
+ }
154
+ async function fireCron(tx, ctx, row, now) {
155
+ const cron = decodePostgresCron(row);
156
+ const admitted = await admitPostgresJob(tx, ctx, createJob(generateJobId(), cron.queue, spawnedInput(cron), now), { linkParent: false });
157
+ if (!admitted.inserted && cron.preventOverlap) {
158
+ await skipCron(tx, ctx, cron, now);
159
+ return false;
160
+ }
161
+ cron.executions++;
162
+ cron.nextRun = followingRun(cron, now);
163
+ await tx `
164
+ UPDATE bunqueue_crons
165
+ SET payload = ${encodePostgresValue(cron)}, next_run = ${cron.nextRun},
166
+ executions = ${cron.executions}, updated_at = ${now}
167
+ WHERE namespace = ${ctx.config.namespace} AND name = ${cron.name}
168
+ `;
169
+ return true;
170
+ }
171
+ async function skipCron(tx, ctx, cron, now) {
172
+ cron.nextRun = followingRun(cron, now);
173
+ await tx `
174
+ UPDATE bunqueue_crons
175
+ SET payload = ${encodePostgresValue(cron)}, next_run = ${cron.nextRun}, updated_at = ${now}
176
+ WHERE namespace = ${ctx.config.namespace} AND name = ${cron.name}
177
+ `;
178
+ }
179
+ /** Advance and enqueue each due cron once under PostgreSQL row locks. */
180
+ export async function processPostgresCrons(ctx, limit = 100) {
181
+ return await ctx.sql.begin(async (tx) => {
182
+ const now = await databaseNow(tx);
183
+ const rows = await tx `
184
+ SELECT name, payload, next_run, executions, max_limit
185
+ FROM bunqueue_crons
186
+ WHERE namespace = ${ctx.config.namespace}
187
+ AND next_run <= ${now}
188
+ AND (max_limit IS NULL OR executions < max_limit)
189
+ ORDER BY next_run, name
190
+ LIMIT ${Math.max(1, limit)}
191
+ FOR UPDATE SKIP LOCKED
192
+ `;
193
+ let fired = 0;
194
+ for (const row of rows) {
195
+ const cron = decodePostgresCron(row);
196
+ if (cron.skipIfNoWorker && !(await hasActivePostgresWorker(tx, ctx, cron.queue, now))) {
197
+ await skipCron(tx, ctx, cron, now);
198
+ continue;
199
+ }
200
+ if (await fireCron(tx, ctx, row, now))
201
+ fired++;
202
+ }
203
+ return fired;
204
+ });
205
+ }
@@ -0,0 +1,9 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import { type PostgresContext } from './context';
3
+ import type { PostgresJobRow, PostgresStoredJob } from './types';
4
+ /** Clear a live deduplication owner and publish the new authoritative payload. */
5
+ export declare function clearPostgresDeduplication(tx: TransactionSQL, ctx: PostgresContext, row: PostgresJobRow, now: number): Promise<PostgresStoredJob>;
6
+ /** Persist a newer extend window so every broker observes the same deduplication metadata. */
7
+ export declare function extendPostgresDeduplication(tx: TransactionSQL, ctx: PostgresContext, row: PostgresJobRow, ttl: number, now: number): Promise<PostgresStoredJob>;
8
+ /** Publish deletion of a pending generation replaced within the same transaction. */
9
+ export declare function recordPostgresDeduplicationRemoval(tx: TransactionSQL, ctx: PostgresContext, row: PostgresJobRow, now: number): Promise<void>;
@@ -0,0 +1,47 @@
1
+ import { decodePostgresJob, encodePostgresValue } from './codec';
2
+ import { recordPostgresEvent } from './context';
3
+ /** Clear a live deduplication owner and publish the new authoritative payload. */
4
+ export async function clearPostgresDeduplication(tx, ctx, row, now) {
5
+ const stored = decodePostgresJob(row);
6
+ const updatedJob = { ...stored.job, uniqueKey: null };
7
+ await tx `
8
+ UPDATE bunqueue_jobs
9
+ SET payload = ${encodePostgresValue(updatedJob)}, unique_key = NULL,
10
+ unique_expires_at = NULL, version = version + 1
11
+ WHERE namespace = ${ctx.config.namespace} AND id = ${row.id}
12
+ `;
13
+ await recordPostgresEvent(tx, ctx, {
14
+ queue: updatedJob.queue,
15
+ type: 'updated',
16
+ jobId: updatedJob.id,
17
+ occurredAt: now,
18
+ job: updatedJob,
19
+ state: row.state,
20
+ });
21
+ return { ...stored, job: updatedJob, version: stored.version + 1 };
22
+ }
23
+ /** Persist a newer extend window so every broker observes the same deduplication metadata. */
24
+ export async function extendPostgresDeduplication(tx, ctx, row, ttl, now) {
25
+ const stored = decodePostgresJob(row);
26
+ const updatedJob = { ...stored.job, deduplicationTtl: ttl };
27
+ await tx `
28
+ UPDATE bunqueue_jobs
29
+ SET payload = ${encodePostgresValue(updatedJob)}, unique_expires_at = ${now + ttl},
30
+ version = version + 1
31
+ WHERE namespace = ${ctx.config.namespace} AND id = ${row.id}
32
+ `;
33
+ return { ...stored, job: updatedJob, version: stored.version + 1 };
34
+ }
35
+ /** Publish deletion of a pending generation replaced within the same transaction. */
36
+ export async function recordPostgresDeduplicationRemoval(tx, ctx, row, now) {
37
+ const stored = decodePostgresJob(row);
38
+ await recordPostgresEvent(tx, ctx, {
39
+ queue: stored.job.queue,
40
+ type: 'removed',
41
+ jobId: stored.job.id,
42
+ occurredAt: now,
43
+ job: stored.job,
44
+ state: stored.state,
45
+ removed: true,
46
+ });
47
+ }
@@ -0,0 +1,14 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import { type JobId } from '../../../domain/types/job';
3
+ import type { PostgresContext } from './context';
4
+ /** Acquire candidate identity locks in the same order as dependency admission. */
5
+ export declare function lockPostgresDestructionIdentities(tx: TransactionSQL, ctx: PostgresContext, ids: readonly JobId[]): Promise<void>;
6
+ /** Lock candidate rows and every live consumer row in deterministic order. */
7
+ export declare function lockPostgresDestructionRows(tx: TransactionSQL, ctx: PostgresContext, ids: readonly JobId[]): Promise<void>;
8
+ /** Acquire identity locks before any target or consumer row lock. */
9
+ export declare function lockPostgresDestructionCandidates(tx: TransactionSQL, ctx: PostgresContext, ids: readonly JobId[]): Promise<void>;
10
+ /** Split a revalidated delete set without orphaning consumers that survive the command. */
11
+ export declare function partitionPostgresDestructionCandidates(tx: TransactionSQL, ctx: PostgresContext, ids: readonly JobId[], deletingConsumerIds?: readonly JobId[]): Promise<{
12
+ deletableIds: JobId[];
13
+ protectedIds: JobId[];
14
+ }>;
@@ -0,0 +1,74 @@
1
+ import { jobId } from '../../../domain/types/job';
2
+ import { lockPostgresDependencyCompletions } from './dependencyPromotion';
3
+ const LIVE_CONSUMER_STATES = [
4
+ 'waiting',
5
+ 'prioritized',
6
+ 'delayed',
7
+ 'waiting-children',
8
+ 'active',
9
+ ];
10
+ function uniqueIds(ids) {
11
+ return [...new Set(ids.map(String))].sort();
12
+ }
13
+ /** Acquire candidate identity locks in the same order as dependency admission. */
14
+ export async function lockPostgresDestructionIdentities(tx, ctx, ids) {
15
+ const candidates = uniqueIds(ids);
16
+ if (candidates.length === 0)
17
+ return;
18
+ await lockPostgresDependencyCompletions(tx, ctx, candidates.map(jobId));
19
+ }
20
+ /** Lock candidate rows and every live consumer row in deterministic order. */
21
+ export async function lockPostgresDestructionRows(tx, ctx, ids) {
22
+ const candidates = uniqueIds(ids);
23
+ if (candidates.length === 0)
24
+ return;
25
+ const consumers = await tx `
26
+ SELECT DISTINCT consumer.id
27
+ FROM bunqueue_dependencies AS dependency
28
+ JOIN bunqueue_jobs AS consumer
29
+ ON consumer.namespace = dependency.namespace AND consumer.id = dependency.job_id
30
+ WHERE dependency.namespace = ${ctx.config.namespace}
31
+ AND dependency.dependency_id = ANY(${tx.array(candidates, 'TEXT')})
32
+ AND consumer.state = ANY(${tx.array([...LIVE_CONSUMER_STATES], 'TEXT')})
33
+ ORDER BY consumer.id
34
+ `;
35
+ const rowIds = [...new Set([...candidates, ...consumers.map((row) => row.id)])].sort();
36
+ await tx `
37
+ SELECT id FROM bunqueue_jobs
38
+ WHERE namespace = ${ctx.config.namespace}
39
+ AND id = ANY(${tx.array(rowIds, 'TEXT')})
40
+ ORDER BY id
41
+ FOR UPDATE
42
+ `;
43
+ }
44
+ /** Acquire identity locks before any target or consumer row lock. */
45
+ export async function lockPostgresDestructionCandidates(tx, ctx, ids) {
46
+ await lockPostgresDestructionIdentities(tx, ctx, ids);
47
+ await lockPostgresDestructionRows(tx, ctx, ids);
48
+ }
49
+ /** Split a revalidated delete set without orphaning consumers that survive the command. */
50
+ export async function partitionPostgresDestructionCandidates(tx, ctx, ids, deletingConsumerIds = ids) {
51
+ const candidates = uniqueIds(ids);
52
+ if (candidates.length === 0)
53
+ return { deletableIds: [], protectedIds: [] };
54
+ const deleting = uniqueIds(deletingConsumerIds);
55
+ const rows = await tx `
56
+ SELECT DISTINCT dependency.dependency_id
57
+ FROM bunqueue_dependencies AS dependency
58
+ JOIN bunqueue_jobs AS consumer
59
+ ON consumer.namespace = dependency.namespace AND consumer.id = dependency.job_id
60
+ WHERE dependency.namespace = ${ctx.config.namespace}
61
+ AND dependency.dependency_id = ANY(${tx.array(candidates, 'TEXT')})
62
+ AND consumer.state = ANY(${tx.array([...LIVE_CONSUMER_STATES], 'TEXT')})
63
+ AND (
64
+ ${deleting.length} = 0
65
+ OR consumer.id <> ALL(${tx.array(deleting, 'TEXT')})
66
+ )
67
+ ORDER BY dependency.dependency_id
68
+ `;
69
+ const protectedSet = new Set(rows.map((row) => row.dependency_id));
70
+ return {
71
+ deletableIds: candidates.filter((id) => !protectedSet.has(id)).map(jobId),
72
+ protectedIds: candidates.filter((id) => protectedSet.has(id)).map(jobId),
73
+ };
74
+ }
@@ -0,0 +1,29 @@
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 { PostgresJobState } from './types';
5
+ type DependencyReadyState = Extract<PostgresJobState, 'waiting' | 'prioritized' | 'delayed'>;
6
+ /** Serialize completion evidence readers and writers for the supplied generations. */
7
+ export declare function lockPostgresDependencyCompletions(tx: TransactionSQL, ctx: PostgresContext, dependencyIds: readonly JobId[]): Promise<void>;
8
+ /** Safely probe a late-discovered identity without inverting row/advisory lock order. */
9
+ export declare function tryLockPostgresDependencyCompletions(tx: TransactionSQL, ctx: PostgresContext, dependencyIds: readonly JobId[]): Promise<boolean>;
10
+ /** Reject dependency references that have no job row or durable completion evidence. */
11
+ export declare function assertPostgresDependenciesExist(tx: TransactionSQL, ctx: PostgresContext, dependencyIds: readonly JobId[]): Promise<void>;
12
+ /** Read dependency evidence while the caller holds every corresponding completion lock. */
13
+ export declare function postgresDependenciesSatisfied(tx: TransactionSQL, ctx: PostgresContext, dependencyIds: readonly JobId[]): Promise<boolean>;
14
+ /** Pure scheduling policy shared by completion-time promotion and pull-time repair. */
15
+ export declare function postgresDependencyReadyState(job: Job, now: number): DependencyReadyState;
16
+ /**
17
+ * Discover and serialize every dependency consumer before completion locks child rows.
18
+ * The returned immutable plan is safe to execute after completion evidence is stored.
19
+ */
20
+ export declare function lockPostgresCompletionConsumers(tx: TransactionSQL, ctx: PostgresContext, dependencyIds: readonly JobId[]): Promise<readonly JobId[]>;
21
+ /** Build a completion-scoped command after acquiring its parent locks. */
22
+ export declare function planPostgresDependencyCompletion(tx: TransactionSQL, ctx: PostgresContext, dependencyIds: readonly JobId[]): Promise<{
23
+ promote(now: number): Promise<JobId[]>;
24
+ }>;
25
+ /** Promote all resolved members of a previously locked completion-consumer plan. */
26
+ export declare function promotePostgresCompletionConsumers(tx: TransactionSQL, ctx: PostgresContext, parentIds: readonly JobId[], now: number): Promise<JobId[]>;
27
+ /** Pull-time convergence path for rows left behind by interrupted or legacy writers. */
28
+ export declare function repairResolvedPostgresDependencyConsumers(tx: TransactionSQL, ctx: PostgresContext, queue: string, now: number): Promise<JobId[]>;
29
+ export {};
@@ -0,0 +1,212 @@
1
+ import { jobId, MAX_TIMELINE_ENTRIES } from '../../../domain/types/job';
2
+ import { postgresAdvisoryLockName } from './advisoryLocks';
3
+ import { recordPostgresJobEvents } from './batchEvents';
4
+ import { decodePostgresJob, encodePostgresValue, postgresByteaBase64 } from './codec';
5
+ import { lockPostgresFlowParents } from './flowFailures';
6
+ function uniqueIds(ids) {
7
+ return [...new Set(ids.map(String))].sort();
8
+ }
9
+ /** Serialize completion evidence readers and writers for the supplied generations. */
10
+ export async function lockPostgresDependencyCompletions(tx, ctx, dependencyIds) {
11
+ const lockNames = uniqueIds(dependencyIds).map((id) => postgresAdvisoryLockName('dependency-completion', ctx.config.namespace, id));
12
+ if (lockNames.length === 0)
13
+ return;
14
+ await tx `
15
+ WITH ordered_ids AS MATERIALIZED (
16
+ SELECT DISTINCT hashtextextended(lock_name, 0) AS lock_key
17
+ FROM unnest(${tx.array(lockNames, 'TEXT')}) AS dependency(lock_name)
18
+ ORDER BY lock_key
19
+ ), locked_ids AS MATERIALIZED (
20
+ SELECT lock_key, pg_advisory_xact_lock(lock_key) AS acquired
21
+ FROM ordered_ids
22
+ ORDER BY lock_key
23
+ )
24
+ SELECT COUNT(*) FROM locked_ids
25
+ `;
26
+ }
27
+ /** Safely probe a late-discovered identity without inverting row/advisory lock order. */
28
+ export async function tryLockPostgresDependencyCompletions(tx, ctx, dependencyIds) {
29
+ const lockNames = uniqueIds(dependencyIds).map((id) => postgresAdvisoryLockName('dependency-completion', ctx.config.namespace, id));
30
+ if (lockNames.length === 0)
31
+ return true;
32
+ const [row] = await tx `
33
+ WITH ordered_ids AS MATERIALIZED (
34
+ SELECT DISTINCT hashtextextended(lock_name, 0) AS lock_key
35
+ FROM unnest(${tx.array(lockNames, 'TEXT')}) AS dependency(lock_name)
36
+ ORDER BY lock_key
37
+ ), locked_ids AS MATERIALIZED (
38
+ SELECT lock_key, pg_try_advisory_xact_lock(lock_key) AS acquired
39
+ FROM ordered_ids
40
+ ORDER BY lock_key
41
+ )
42
+ SELECT COALESCE(BOOL_AND(acquired), TRUE) AS acquired FROM locked_ids
43
+ `;
44
+ return row.acquired;
45
+ }
46
+ /** Reject dependency references that have no job row or durable completion evidence. */
47
+ export async function assertPostgresDependenciesExist(tx, ctx, dependencyIds) {
48
+ const ids = [...new Set(dependencyIds.map(String))];
49
+ if (ids.length === 0)
50
+ return;
51
+ const rows = await tx `
52
+ WITH requested AS MATERIALIZED (
53
+ SELECT dependency_id, ordinal
54
+ FROM unnest(${tx.array(ids, 'TEXT')}) WITH ORDINALITY
55
+ AS dependency(dependency_id, ordinal)
56
+ )
57
+ SELECT requested.dependency_id
58
+ FROM requested
59
+ WHERE NOT EXISTS (
60
+ SELECT 1 FROM bunqueue_jobs AS job
61
+ WHERE job.namespace = ${ctx.config.namespace} AND job.id = requested.dependency_id
62
+ ) AND NOT EXISTS (
63
+ SELECT 1 FROM bunqueue_completions AS completion
64
+ WHERE completion.namespace = ${ctx.config.namespace}
65
+ AND completion.job_id = requested.dependency_id
66
+ )
67
+ ORDER BY requested.ordinal
68
+ LIMIT 1
69
+ `;
70
+ if (rows[0])
71
+ throw new Error(`Dependency job not found: ${rows[0].dependency_id}`);
72
+ }
73
+ /** Read dependency evidence while the caller holds every corresponding completion lock. */
74
+ export async function postgresDependenciesSatisfied(tx, ctx, dependencyIds) {
75
+ const ids = uniqueIds(dependencyIds);
76
+ if (ids.length === 0)
77
+ return true;
78
+ const [row] = await tx `
79
+ SELECT COUNT(DISTINCT dependency_id)::bigint AS count
80
+ FROM (
81
+ SELECT job_id AS dependency_id
82
+ FROM bunqueue_completions
83
+ WHERE namespace = ${ctx.config.namespace}
84
+ AND job_id = ANY(${tx.array(ids, 'TEXT')})
85
+ UNION
86
+ SELECT id AS dependency_id
87
+ FROM bunqueue_jobs
88
+ WHERE namespace = ${ctx.config.namespace}
89
+ AND state = 'completed'
90
+ AND id = ANY(${tx.array(ids, 'TEXT')})
91
+ ) completed_dependencies
92
+ `;
93
+ return Number(row.count) === ids.length;
94
+ }
95
+ /** Pure scheduling policy shared by completion-time promotion and pull-time repair. */
96
+ export function postgresDependencyReadyState(job, now) {
97
+ if (job.runAt > now)
98
+ return 'delayed';
99
+ return job.priority > 0 ? 'prioritized' : 'waiting';
100
+ }
101
+ function preparePromotion(row, now) {
102
+ const job = decodePostgresJob(row).job;
103
+ const state = postgresDependencyReadyState(job, now);
104
+ if (job.timeline.at(-1)?.state !== state && job.timeline.length < MAX_TIMELINE_ENTRIES) {
105
+ job.timeline.push({ state, timestamp: now });
106
+ }
107
+ return { job, state, payload: encodePostgresValue(job) };
108
+ }
109
+ async function consumerIdsForDependencies(tx, ctx, dependencyIds) {
110
+ const ids = uniqueIds(dependencyIds);
111
+ if (ids.length === 0)
112
+ return [];
113
+ const rows = await tx `
114
+ SELECT DISTINCT dependency.job_id AS parent_id
115
+ FROM bunqueue_dependencies AS dependency
116
+ JOIN bunqueue_jobs AS parent
117
+ ON parent.namespace = dependency.namespace AND parent.id = dependency.job_id
118
+ WHERE dependency.namespace = ${ctx.config.namespace}
119
+ AND dependency.dependency_id = ANY(${tx.array(ids, 'TEXT')})
120
+ AND parent.state = 'waiting-children'
121
+ ORDER BY dependency.job_id
122
+ `;
123
+ return rows.map((row) => jobId(row.parent_id));
124
+ }
125
+ async function resolvedRows(tx, ctx, parentIds) {
126
+ const ids = uniqueIds(parentIds);
127
+ if (ids.length === 0)
128
+ return [];
129
+ return await tx `
130
+ SELECT parent.*
131
+ FROM bunqueue_jobs AS parent
132
+ WHERE parent.namespace = ${ctx.config.namespace}
133
+ AND parent.id = ANY(${tx.array(ids, 'TEXT')})
134
+ AND parent.state = 'waiting-children'
135
+ AND NOT EXISTS (
136
+ SELECT 1
137
+ FROM bunqueue_dependencies AS dependency
138
+ WHERE dependency.namespace = parent.namespace
139
+ AND dependency.job_id = parent.id
140
+ AND NOT EXISTS (
141
+ SELECT 1 FROM bunqueue_completions AS completion
142
+ WHERE completion.namespace = dependency.namespace
143
+ AND completion.job_id = dependency.dependency_id
144
+ )
145
+ )
146
+ ORDER BY parent.id
147
+ FOR UPDATE OF parent
148
+ `;
149
+ }
150
+ async function persistPromotions(tx, ctx, prepared) {
151
+ if (prepared.length === 0)
152
+ return;
153
+ await tx `
154
+ UPDATE bunqueue_jobs AS parent
155
+ SET payload = decode(batch.payload_base64, 'base64'),
156
+ state = batch.state,
157
+ version = parent.version + 1
158
+ FROM unnest(
159
+ ${tx.array(prepared.map(({ job }) => String(job.id)), 'TEXT')},
160
+ ${tx.array(prepared.map(({ payload }) => postgresByteaBase64(payload)), 'TEXT')},
161
+ ${tx.array(prepared.map(({ state }) => state), 'TEXT')}
162
+ ) AS batch(id, payload_base64, state)
163
+ WHERE parent.namespace = ${ctx.config.namespace} AND parent.id = batch.id
164
+ `;
165
+ }
166
+ /**
167
+ * Discover and serialize every dependency consumer before completion locks child rows.
168
+ * The returned immutable plan is safe to execute after completion evidence is stored.
169
+ */
170
+ export async function lockPostgresCompletionConsumers(tx, ctx, dependencyIds) {
171
+ const parentIds = await consumerIdsForDependencies(tx, ctx, dependencyIds);
172
+ await lockPostgresFlowParents(tx, ctx, parentIds);
173
+ return parentIds;
174
+ }
175
+ /** Build a completion-scoped command after acquiring its parent locks. */
176
+ export async function planPostgresDependencyCompletion(tx, ctx, dependencyIds) {
177
+ await lockPostgresDependencyCompletions(tx, ctx, dependencyIds);
178
+ const parentIds = await lockPostgresCompletionConsumers(tx, ctx, dependencyIds);
179
+ return {
180
+ promote: (now) => promotePostgresCompletionConsumers(tx, ctx, parentIds, now),
181
+ };
182
+ }
183
+ /** Promote all resolved members of a previously locked completion-consumer plan. */
184
+ export async function promotePostgresCompletionConsumers(tx, ctx, parentIds, now) {
185
+ const prepared = (await resolvedRows(tx, ctx, parentIds)).map((row) => preparePromotion(row, now));
186
+ await persistPromotions(tx, ctx, prepared);
187
+ await recordPostgresJobEvents(tx, ctx, prepared.map(({ job, state }) => ({ job, type: 'retried', state })), now);
188
+ return prepared.map(({ job }) => job.id);
189
+ }
190
+ /** Pull-time convergence path for rows left behind by interrupted or legacy writers. */
191
+ export async function repairResolvedPostgresDependencyConsumers(tx, ctx, queue, now) {
192
+ const rows = await tx `
193
+ SELECT parent.id AS parent_id
194
+ FROM bunqueue_jobs AS parent
195
+ WHERE parent.namespace = ${ctx.config.namespace}
196
+ AND parent.queue = ${queue}
197
+ AND parent.state = 'waiting-children'
198
+ AND NOT EXISTS (
199
+ SELECT 1 FROM bunqueue_dependencies AS dependency
200
+ WHERE dependency.namespace = parent.namespace AND dependency.job_id = parent.id
201
+ AND NOT EXISTS (
202
+ SELECT 1 FROM bunqueue_completions AS completion
203
+ WHERE completion.namespace = dependency.namespace
204
+ AND completion.job_id = dependency.dependency_id
205
+ )
206
+ )
207
+ ORDER BY parent.id
208
+ `;
209
+ const parentIds = rows.map((row) => jobId(row.parent_id));
210
+ await lockPostgresFlowParents(tx, ctx, parentIds);
211
+ return await promotePostgresCompletionConsumers(tx, ctx, parentIds, now);
212
+ }
@@ -0,0 +1,4 @@
1
+ import { type JobId } from '../../../domain/types/job';
2
+ import { type PostgresContext } from './context';
3
+ export declare function removePostgresJob(ctx: PostgresContext, id: JobId, token?: string): Promise<boolean>;
4
+ export declare function retryPostgresTerminalJob(ctx: PostgresContext, id: JobId, requestedTimestamp?: number): Promise<boolean>;