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,18 @@
1
+ import type { PostgresContext } from './context';
2
+ import type { PostgresStoreEvent } from './types';
3
+ export interface PostgresEventJournalCursor {
4
+ readonly commitSeq: number;
5
+ readonly eventId: number;
6
+ }
7
+ export interface PostgresJournalEvent {
8
+ readonly commitSeq: number;
9
+ readonly event: PostgresStoreEvent;
10
+ }
11
+ export declare function latestPostgresEventJournalBaseline(ctx: PostgresContext): Promise<{
12
+ commitSeq: number;
13
+ latestEventId: number;
14
+ }>;
15
+ /** Load events in commit order, independent of physical BIGSERIAL allocation. */
16
+ export declare function loadPostgresJournalEventsAfter(ctx: PostgresContext, cursor: PostgresEventJournalCursor, limit?: number): Promise<PostgresJournalEvent[]>;
17
+ /** Remove commit envelopes after every referenced event and watermark is gone. */
18
+ export declare function prunePostgresEventCommits(ctx: PostgresContext, limit?: number, maxBatches?: number): Promise<number>;
@@ -0,0 +1,76 @@
1
+ import { decodePostgresEvent, numeric } from './codec';
2
+ export async function latestPostgresEventJournalBaseline(ctx) {
3
+ const [row] = await ctx.sql `
4
+ SELECT
5
+ (
6
+ SELECT MAX(commit_seq) FROM bunqueue_event_commits
7
+ WHERE namespace = ${ctx.config.namespace} AND commit_seq IS NOT NULL
8
+ ) AS commit_seq,
9
+ (
10
+ SELECT MAX(id) FROM bunqueue_events
11
+ WHERE namespace = ${ctx.config.namespace}
12
+ ) AS latest_event_id
13
+ `;
14
+ return {
15
+ commitSeq: numeric(row.commit_seq) ?? 0,
16
+ latestEventId: numeric(row.latest_event_id) ?? 0,
17
+ };
18
+ }
19
+ /** Load events in commit order, independent of physical BIGSERIAL allocation. */
20
+ export async function loadPostgresJournalEventsAfter(ctx, cursor, limit = 1000) {
21
+ const rows = await ctx.sql `
22
+ SELECT event.id, event.queue, event.event_type, event.job_id,
23
+ event.occurred_at, event.payload, journal.commit_seq
24
+ FROM bunqueue_event_commits AS journal
25
+ JOIN bunqueue_events AS event
26
+ ON event.namespace = journal.namespace
27
+ AND event.transaction_id = journal.transaction_id
28
+ WHERE journal.namespace = ${ctx.config.namespace}
29
+ AND journal.commit_seq IS NOT NULL
30
+ AND (journal.commit_seq, event.id) > (${cursor.commitSeq}, ${cursor.eventId})
31
+ ORDER BY journal.commit_seq, event.id
32
+ LIMIT ${Math.max(1, limit)}
33
+ `;
34
+ return rows.map((row) => ({
35
+ commitSeq: numeric(row.commit_seq) ?? 0,
36
+ event: decodePostgresEvent(row),
37
+ }));
38
+ }
39
+ /** Remove commit envelopes after every referenced event and watermark is gone. */
40
+ export async function prunePostgresEventCommits(ctx, limit = 10_000, maxBatches = 8) {
41
+ const batchSize = Math.max(1, limit);
42
+ const batchLimit = Math.max(1, maxBatches);
43
+ let total = 0;
44
+ for (let batch = 0; batch < batchLimit; batch++) {
45
+ const deleted = await ctx.sql `
46
+ WITH candidates AS MATERIALIZED (
47
+ SELECT journal.namespace, journal.transaction_id
48
+ FROM bunqueue_event_commits AS journal
49
+ WHERE journal.namespace = ${ctx.config.namespace}
50
+ AND journal.commit_seq IS NOT NULL
51
+ AND NOT EXISTS (
52
+ SELECT 1 FROM bunqueue_events AS event
53
+ WHERE event.namespace = journal.namespace
54
+ AND event.transaction_id = journal.transaction_id
55
+ )
56
+ AND NOT EXISTS (
57
+ SELECT 1 FROM bunqueue_event_prune_watermarks AS watermark
58
+ WHERE watermark.namespace = journal.namespace
59
+ AND watermark.transaction_id = journal.transaction_id
60
+ )
61
+ ORDER BY journal.commit_seq
62
+ LIMIT ${batchSize}
63
+ FOR UPDATE SKIP LOCKED
64
+ )
65
+ DELETE FROM bunqueue_event_commits AS journal
66
+ USING candidates
67
+ WHERE journal.namespace = candidates.namespace
68
+ AND journal.transaction_id = candidates.transaction_id
69
+ RETURNING journal.transaction_id
70
+ `;
71
+ total += deleted.length;
72
+ if (deleted.length < batchSize)
73
+ break;
74
+ }
75
+ return total;
76
+ }
@@ -0,0 +1,5 @@
1
+ export declare const POSTGRES_REGISTER_EVENT_ROWS_BODY = "BEGIN\n INSERT INTO bunqueue_event_commits (namespace, transaction_id)\n SELECT DISTINCT namespace, transaction_id FROM new_event_rows\n ORDER BY namespace, transaction_id\n ON CONFLICT DO NOTHING;\n RETURN NULL;\nEND;";
2
+ export declare const POSTGRES_REGISTER_WATERMARK_ROWS_BODY = "BEGIN\n INSERT INTO bunqueue_event_commits (namespace, transaction_id)\n SELECT DISTINCT namespace, transaction_id FROM new_watermark_rows\n ORDER BY namespace, transaction_id\n ON CONFLICT DO NOTHING;\n RETURN NULL;\nEND;";
3
+ export declare const POSTGRES_ASSIGN_EVENT_COMMIT_BODY = "DECLARE\n assigned_commit_seq BIGINT;\nBEGIN\n PERFORM pg_advisory_xact_lock(hashtextextended(\n 'bunqueue:event-commit:' || length(NEW.namespace)::text || ':' || NEW.namespace,\n 0\n ));\n assigned_commit_seq := nextval('bunqueue_event_commit_seq');\n\n UPDATE bunqueue_event_commits\n SET commit_seq = assigned_commit_seq\n WHERE namespace = NEW.namespace\n AND transaction_id = NEW.transaction_id\n AND commit_seq IS NULL;\n UPDATE bunqueue_event_prune_watermarks\n SET commit_seq = assigned_commit_seq,\n pruned_commit_seq = CASE\n WHEN prunes_current_transaction THEN GREATEST(\n COALESCE(pruned_commit_seq, 0), assigned_commit_seq\n )\n ELSE pruned_commit_seq\n END,\n self_pruned_commit_seq = CASE\n WHEN prunes_current_transaction THEN GREATEST(\n COALESCE(self_pruned_commit_seq, 0), assigned_commit_seq\n )\n ELSE self_pruned_commit_seq\n END,\n prunes_current_transaction = FALSE\n WHERE namespace = NEW.namespace\n AND transaction_id = NEW.transaction_id\n AND commit_seq IS NULL;\n RETURN NULL;\nEND;";
4
+ /** Commit-time journal sequencer installed by the PostgreSQL schema migration. */
5
+ export declare const POSTGRES_EVENT_JOURNAL_SCHEMA = "\nCREATE SEQUENCE IF NOT EXISTS bunqueue_event_commit_seq AS BIGINT CACHE 1;\nALTER SEQUENCE bunqueue_event_commit_seq\n AS BIGINT INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE CACHE 1;\n\nCREATE TABLE IF NOT EXISTS bunqueue_event_commits (\n namespace TEXT NOT NULL,\n transaction_id BIGINT NOT NULL,\n commit_seq BIGINT,\n PRIMARY KEY (namespace, transaction_id)\n);\n\nALTER TABLE bunqueue_event_commits\n ADD COLUMN IF NOT EXISTS namespace TEXT;\nALTER TABLE bunqueue_event_commits\n ADD COLUMN IF NOT EXISTS transaction_id BIGINT;\nALTER TABLE bunqueue_event_commits\n ADD COLUMN IF NOT EXISTS commit_seq BIGINT;\nALTER TABLE bunqueue_event_commits\n ALTER COLUMN namespace TYPE TEXT USING namespace::text,\n ALTER COLUMN transaction_id TYPE BIGINT USING transaction_id::bigint,\n ALTER COLUMN commit_seq TYPE BIGINT USING commit_seq::bigint;\n\nALTER TABLE bunqueue_events\n ADD COLUMN IF NOT EXISTS transaction_id BIGINT;\nALTER TABLE bunqueue_events\n ALTER COLUMN transaction_id DROP DEFAULT;\nALTER TABLE bunqueue_events\n ALTER COLUMN transaction_id TYPE BIGINT USING transaction_id::bigint;\nALTER TABLE bunqueue_events\n ALTER COLUMN transaction_id SET DEFAULT (pg_current_xact_id()::text::bigint);\n\nALTER TABLE bunqueue_event_prune_watermarks\n ADD COLUMN IF NOT EXISTS transaction_id BIGINT;\nALTER TABLE bunqueue_event_prune_watermarks\n ADD COLUMN IF NOT EXISTS commit_seq BIGINT;\nALTER TABLE bunqueue_event_prune_watermarks\n ADD COLUMN IF NOT EXISTS pruned_commit_seq BIGINT;\nALTER TABLE bunqueue_event_prune_watermarks\n ADD COLUMN IF NOT EXISTS self_pruned_commit_seq BIGINT;\nALTER TABLE bunqueue_event_prune_watermarks\n ADD COLUMN IF NOT EXISTS prunes_current_transaction BOOLEAN NOT NULL DEFAULT FALSE;\nALTER TABLE bunqueue_event_prune_watermarks\n ALTER COLUMN transaction_id DROP DEFAULT,\n ALTER COLUMN pruned_commit_seq DROP DEFAULT,\n ALTER COLUMN self_pruned_commit_seq DROP DEFAULT,\n ALTER COLUMN prunes_current_transaction DROP DEFAULT;\nALTER TABLE bunqueue_event_prune_watermarks\n ALTER COLUMN transaction_id TYPE BIGINT USING transaction_id::bigint,\n ALTER COLUMN commit_seq TYPE BIGINT USING commit_seq::bigint,\n ALTER COLUMN pruned_commit_seq TYPE BIGINT USING pruned_commit_seq::bigint,\n ALTER COLUMN self_pruned_commit_seq TYPE BIGINT USING self_pruned_commit_seq::bigint,\n ALTER COLUMN prunes_current_transaction TYPE BOOLEAN\n USING prunes_current_transaction::boolean;\nALTER TABLE bunqueue_event_prune_watermarks\n ALTER COLUMN transaction_id SET DEFAULT (pg_current_xact_id()::text::bigint);\nALTER TABLE bunqueue_event_prune_watermarks\n ALTER COLUMN pruned_commit_seq SET DEFAULT 0,\n ALTER COLUMN self_pruned_commit_seq SET DEFAULT 0,\n ALTER COLUMN prunes_current_transaction SET DEFAULT FALSE;\n\nUPDATE bunqueue_events\nSET transaction_id = -id\nWHERE transaction_id IS NULL;\nUPDATE bunqueue_event_prune_watermarks\nSET transaction_id = COALESCE(transaction_id, -source_event_id),\n commit_seq = COALESCE(commit_seq, source_event_id),\n pruned_commit_seq = COALESCE(pruned_commit_seq, commit_seq, source_event_id),\n self_pruned_commit_seq = COALESCE(self_pruned_commit_seq, 0)\nWHERE transaction_id IS NULL OR commit_seq IS NULL OR pruned_commit_seq IS NULL\n OR self_pruned_commit_seq IS NULL;\n\nALTER TABLE bunqueue_events\n ALTER COLUMN transaction_id SET NOT NULL;\nALTER TABLE bunqueue_event_commits\n ALTER COLUMN namespace SET NOT NULL,\n ALTER COLUMN transaction_id SET NOT NULL;\nALTER TABLE bunqueue_event_prune_watermarks\n ALTER COLUMN transaction_id SET NOT NULL,\n ALTER COLUMN pruned_commit_seq SET NOT NULL,\n ALTER COLUMN self_pruned_commit_seq SET NOT NULL,\n ALTER COLUMN prunes_current_transaction SET NOT NULL;\n\nDO $migration$\nBEGIN\n IF EXISTS (\n SELECT 1 FROM pg_attribute\n WHERE attrelid = to_regclass('bunqueue_events')\n AND attname = 'commit_seq' AND NOT attisdropped\n ) THEN\n EXECUTE $sql$\n INSERT INTO bunqueue_event_commits (namespace, transaction_id, commit_seq)\n SELECT namespace, transaction_id, MAX(commit_seq)\n FROM bunqueue_events\n WHERE commit_seq IS NOT NULL\n GROUP BY namespace, transaction_id\n ON CONFLICT (namespace, transaction_id) DO UPDATE SET\n commit_seq = GREATEST(\n COALESCE(bunqueue_event_commits.commit_seq, 0),\n excluded.commit_seq\n )\n $sql$;\n ELSE\n INSERT INTO bunqueue_event_commits (namespace, transaction_id, commit_seq)\n SELECT namespace, transaction_id, MAX(id)\n FROM bunqueue_events\n GROUP BY namespace, transaction_id\n ON CONFLICT (namespace, transaction_id) DO NOTHING;\n END IF;\nEND;\n$migration$;\n\nINSERT INTO bunqueue_event_commits (namespace, transaction_id, commit_seq)\nSELECT namespace, transaction_id, MAX(commit_seq)\nFROM bunqueue_event_prune_watermarks\nWHERE commit_seq IS NOT NULL\nGROUP BY namespace, transaction_id\nON CONFLICT (namespace, transaction_id) DO UPDATE SET\n commit_seq = GREATEST(\n COALESCE(bunqueue_event_commits.commit_seq, 0),\n excluded.commit_seq\n );\n\nDO $block$\nDECLARE\n journal_floor BIGINT := 0;\n legacy_head_floor BIGINT := 0;\n sequence_floor BIGINT := 0;\nBEGIN\n SELECT COALESCE(MAX(commit_seq), 0) INTO journal_floor\n FROM bunqueue_event_commits;\n SELECT last_value INTO sequence_floor FROM bunqueue_event_commit_seq;\n IF to_regclass('bunqueue_event_commit_heads') IS NOT NULL THEN\n EXECUTE 'SELECT COALESCE(MAX(commit_seq), 0) FROM bunqueue_event_commit_heads'\n INTO legacy_head_floor;\n END IF;\n PERFORM setval(\n 'bunqueue_event_commit_seq',\n GREATEST(journal_floor, legacy_head_floor, sequence_floor, 1),\n TRUE\n );\nEND;\n$block$;\n\nDROP TABLE IF EXISTS bunqueue_event_commit_heads;\n\nDROP TRIGGER IF EXISTS bunqueue_events_register_commit ON bunqueue_events;\nDROP TRIGGER IF EXISTS bunqueue_watermarks_insert_register_commit\n ON bunqueue_event_prune_watermarks;\nDROP TRIGGER IF EXISTS bunqueue_watermarks_update_register_commit\n ON bunqueue_event_prune_watermarks;\nDROP TABLE IF EXISTS bunqueue_event_commit_assignments CASCADE;\nDROP INDEX IF EXISTS bunqueue_events_commit_idx;\nDROP INDEX IF EXISTS bunqueue_events_pending_commit_idx;\nALTER TABLE bunqueue_events DROP COLUMN IF EXISTS commit_seq;\n\nDROP INDEX IF EXISTS bunqueue_events_transaction_idx;\nDROP INDEX IF EXISTS bunqueue_event_commits_replay_idx;\nDROP INDEX IF EXISTS bunqueue_event_prune_watermarks_commit_idx;\nDROP INDEX IF EXISTS bunqueue_event_prune_watermarks_pending_commit_idx;\nDROP INDEX IF EXISTS bunqueue_event_prune_watermarks_transaction_idx;\n\nCREATE INDEX bunqueue_events_transaction_idx\n ON bunqueue_events(namespace, transaction_id, id);\nCREATE INDEX bunqueue_event_commits_replay_idx\n ON bunqueue_event_commits(namespace, commit_seq, transaction_id)\n WHERE commit_seq IS NOT NULL;\nCREATE INDEX bunqueue_event_prune_watermarks_commit_idx\n ON bunqueue_event_prune_watermarks(namespace, queue, commit_seq DESC)\n WHERE commit_seq IS NOT NULL;\nCREATE INDEX bunqueue_event_prune_watermarks_pending_commit_idx\n ON bunqueue_event_prune_watermarks(namespace, transaction_id)\n WHERE commit_seq IS NULL;\nCREATE INDEX bunqueue_event_prune_watermarks_transaction_idx\n ON bunqueue_event_prune_watermarks(namespace, transaction_id);\n\nCREATE OR REPLACE FUNCTION bunqueue_register_event_rows()\nRETURNS TRIGGER LANGUAGE plpgsql AS $function$\nBEGIN\n INSERT INTO bunqueue_event_commits (namespace, transaction_id)\n SELECT DISTINCT namespace, transaction_id FROM new_event_rows\n ORDER BY namespace, transaction_id\n ON CONFLICT DO NOTHING;\n RETURN NULL;\nEND;\n$function$;\n\nCREATE OR REPLACE FUNCTION bunqueue_register_watermark_rows()\nRETURNS TRIGGER LANGUAGE plpgsql AS $function$\nBEGIN\n INSERT INTO bunqueue_event_commits (namespace, transaction_id)\n SELECT DISTINCT namespace, transaction_id FROM new_watermark_rows\n ORDER BY namespace, transaction_id\n ON CONFLICT DO NOTHING;\n RETURN NULL;\nEND;\n$function$;\n\nCREATE OR REPLACE FUNCTION bunqueue_assign_event_commit()\nRETURNS TRIGGER LANGUAGE plpgsql AS $function$\nDECLARE\n assigned_commit_seq BIGINT;\nBEGIN\n PERFORM pg_advisory_xact_lock(hashtextextended(\n 'bunqueue:event-commit:' || length(NEW.namespace)::text || ':' || NEW.namespace,\n 0\n ));\n assigned_commit_seq := nextval('bunqueue_event_commit_seq');\n\n UPDATE bunqueue_event_commits\n SET commit_seq = assigned_commit_seq\n WHERE namespace = NEW.namespace\n AND transaction_id = NEW.transaction_id\n AND commit_seq IS NULL;\n UPDATE bunqueue_event_prune_watermarks\n SET commit_seq = assigned_commit_seq,\n pruned_commit_seq = CASE\n WHEN prunes_current_transaction THEN GREATEST(\n COALESCE(pruned_commit_seq, 0), assigned_commit_seq\n )\n ELSE pruned_commit_seq\n END,\n self_pruned_commit_seq = CASE\n WHEN prunes_current_transaction THEN GREATEST(\n COALESCE(self_pruned_commit_seq, 0), assigned_commit_seq\n )\n ELSE self_pruned_commit_seq\n END,\n prunes_current_transaction = FALSE\n WHERE namespace = NEW.namespace\n AND transaction_id = NEW.transaction_id\n AND commit_seq IS NULL;\n RETURN NULL;\nEND;\n$function$;\n\nDROP TRIGGER IF EXISTS bunqueue_events_register_commit ON bunqueue_events;\nCREATE TRIGGER bunqueue_events_register_commit\nAFTER INSERT ON bunqueue_events\nREFERENCING NEW TABLE AS new_event_rows\nFOR EACH STATEMENT EXECUTE FUNCTION bunqueue_register_event_rows();\n\nDROP TRIGGER IF EXISTS bunqueue_watermarks_insert_register_commit\n ON bunqueue_event_prune_watermarks;\nCREATE TRIGGER bunqueue_watermarks_insert_register_commit\nAFTER INSERT ON bunqueue_event_prune_watermarks\nREFERENCING NEW TABLE AS new_watermark_rows\nFOR EACH STATEMENT EXECUTE FUNCTION bunqueue_register_watermark_rows();\n\nDROP TRIGGER IF EXISTS bunqueue_watermarks_update_register_commit\n ON bunqueue_event_prune_watermarks;\nCREATE TRIGGER bunqueue_watermarks_update_register_commit\nAFTER UPDATE ON bunqueue_event_prune_watermarks\nREFERENCING NEW TABLE AS new_watermark_rows\nFOR EACH STATEMENT EXECUTE FUNCTION bunqueue_register_watermark_rows();\n\nDROP TRIGGER IF EXISTS bunqueue_assign_event_commit\n ON bunqueue_event_commits;\nCREATE CONSTRAINT TRIGGER bunqueue_assign_event_commit\nAFTER INSERT ON bunqueue_event_commits\nDEFERRABLE INITIALLY DEFERRED\nFOR EACH ROW EXECUTE FUNCTION bunqueue_assign_event_commit();\n";
@@ -0,0 +1,267 @@
1
+ export const POSTGRES_REGISTER_EVENT_ROWS_BODY = `BEGIN
2
+ INSERT INTO bunqueue_event_commits (namespace, transaction_id)
3
+ SELECT DISTINCT namespace, transaction_id FROM new_event_rows
4
+ ORDER BY namespace, transaction_id
5
+ ON CONFLICT DO NOTHING;
6
+ RETURN NULL;
7
+ END;`;
8
+ export const POSTGRES_REGISTER_WATERMARK_ROWS_BODY = `BEGIN
9
+ INSERT INTO bunqueue_event_commits (namespace, transaction_id)
10
+ SELECT DISTINCT namespace, transaction_id FROM new_watermark_rows
11
+ ORDER BY namespace, transaction_id
12
+ ON CONFLICT DO NOTHING;
13
+ RETURN NULL;
14
+ END;`;
15
+ export const POSTGRES_ASSIGN_EVENT_COMMIT_BODY = `DECLARE
16
+ assigned_commit_seq BIGINT;
17
+ BEGIN
18
+ PERFORM pg_advisory_xact_lock(hashtextextended(
19
+ 'bunqueue:event-commit:' || length(NEW.namespace)::text || ':' || NEW.namespace,
20
+ 0
21
+ ));
22
+ assigned_commit_seq := nextval('bunqueue_event_commit_seq');
23
+
24
+ UPDATE bunqueue_event_commits
25
+ SET commit_seq = assigned_commit_seq
26
+ WHERE namespace = NEW.namespace
27
+ AND transaction_id = NEW.transaction_id
28
+ AND commit_seq IS NULL;
29
+ UPDATE bunqueue_event_prune_watermarks
30
+ SET commit_seq = assigned_commit_seq,
31
+ pruned_commit_seq = CASE
32
+ WHEN prunes_current_transaction THEN GREATEST(
33
+ COALESCE(pruned_commit_seq, 0), assigned_commit_seq
34
+ )
35
+ ELSE pruned_commit_seq
36
+ END,
37
+ self_pruned_commit_seq = CASE
38
+ WHEN prunes_current_transaction THEN GREATEST(
39
+ COALESCE(self_pruned_commit_seq, 0), assigned_commit_seq
40
+ )
41
+ ELSE self_pruned_commit_seq
42
+ END,
43
+ prunes_current_transaction = FALSE
44
+ WHERE namespace = NEW.namespace
45
+ AND transaction_id = NEW.transaction_id
46
+ AND commit_seq IS NULL;
47
+ RETURN NULL;
48
+ END;`;
49
+ /** Commit-time journal sequencer installed by the PostgreSQL schema migration. */
50
+ export const POSTGRES_EVENT_JOURNAL_SCHEMA = `
51
+ CREATE SEQUENCE IF NOT EXISTS bunqueue_event_commit_seq AS BIGINT CACHE 1;
52
+ ALTER SEQUENCE bunqueue_event_commit_seq
53
+ AS BIGINT INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE CACHE 1;
54
+
55
+ CREATE TABLE IF NOT EXISTS bunqueue_event_commits (
56
+ namespace TEXT NOT NULL,
57
+ transaction_id BIGINT NOT NULL,
58
+ commit_seq BIGINT,
59
+ PRIMARY KEY (namespace, transaction_id)
60
+ );
61
+
62
+ ALTER TABLE bunqueue_event_commits
63
+ ADD COLUMN IF NOT EXISTS namespace TEXT;
64
+ ALTER TABLE bunqueue_event_commits
65
+ ADD COLUMN IF NOT EXISTS transaction_id BIGINT;
66
+ ALTER TABLE bunqueue_event_commits
67
+ ADD COLUMN IF NOT EXISTS commit_seq BIGINT;
68
+ ALTER TABLE bunqueue_event_commits
69
+ ALTER COLUMN namespace TYPE TEXT USING namespace::text,
70
+ ALTER COLUMN transaction_id TYPE BIGINT USING transaction_id::bigint,
71
+ ALTER COLUMN commit_seq TYPE BIGINT USING commit_seq::bigint;
72
+
73
+ ALTER TABLE bunqueue_events
74
+ ADD COLUMN IF NOT EXISTS transaction_id BIGINT;
75
+ ALTER TABLE bunqueue_events
76
+ ALTER COLUMN transaction_id DROP DEFAULT;
77
+ ALTER TABLE bunqueue_events
78
+ ALTER COLUMN transaction_id TYPE BIGINT USING transaction_id::bigint;
79
+ ALTER TABLE bunqueue_events
80
+ ALTER COLUMN transaction_id SET DEFAULT (pg_current_xact_id()::text::bigint);
81
+
82
+ ALTER TABLE bunqueue_event_prune_watermarks
83
+ ADD COLUMN IF NOT EXISTS transaction_id BIGINT;
84
+ ALTER TABLE bunqueue_event_prune_watermarks
85
+ ADD COLUMN IF NOT EXISTS commit_seq BIGINT;
86
+ ALTER TABLE bunqueue_event_prune_watermarks
87
+ ADD COLUMN IF NOT EXISTS pruned_commit_seq BIGINT;
88
+ ALTER TABLE bunqueue_event_prune_watermarks
89
+ ADD COLUMN IF NOT EXISTS self_pruned_commit_seq BIGINT;
90
+ ALTER TABLE bunqueue_event_prune_watermarks
91
+ ADD COLUMN IF NOT EXISTS prunes_current_transaction BOOLEAN NOT NULL DEFAULT FALSE;
92
+ ALTER TABLE bunqueue_event_prune_watermarks
93
+ ALTER COLUMN transaction_id DROP DEFAULT,
94
+ ALTER COLUMN pruned_commit_seq DROP DEFAULT,
95
+ ALTER COLUMN self_pruned_commit_seq DROP DEFAULT,
96
+ ALTER COLUMN prunes_current_transaction DROP DEFAULT;
97
+ ALTER TABLE bunqueue_event_prune_watermarks
98
+ ALTER COLUMN transaction_id TYPE BIGINT USING transaction_id::bigint,
99
+ ALTER COLUMN commit_seq TYPE BIGINT USING commit_seq::bigint,
100
+ ALTER COLUMN pruned_commit_seq TYPE BIGINT USING pruned_commit_seq::bigint,
101
+ ALTER COLUMN self_pruned_commit_seq TYPE BIGINT USING self_pruned_commit_seq::bigint,
102
+ ALTER COLUMN prunes_current_transaction TYPE BOOLEAN
103
+ USING prunes_current_transaction::boolean;
104
+ ALTER TABLE bunqueue_event_prune_watermarks
105
+ ALTER COLUMN transaction_id SET DEFAULT (pg_current_xact_id()::text::bigint);
106
+ ALTER TABLE bunqueue_event_prune_watermarks
107
+ ALTER COLUMN pruned_commit_seq SET DEFAULT 0,
108
+ ALTER COLUMN self_pruned_commit_seq SET DEFAULT 0,
109
+ ALTER COLUMN prunes_current_transaction SET DEFAULT FALSE;
110
+
111
+ UPDATE bunqueue_events
112
+ SET transaction_id = -id
113
+ WHERE transaction_id IS NULL;
114
+ UPDATE bunqueue_event_prune_watermarks
115
+ SET transaction_id = COALESCE(transaction_id, -source_event_id),
116
+ commit_seq = COALESCE(commit_seq, source_event_id),
117
+ pruned_commit_seq = COALESCE(pruned_commit_seq, commit_seq, source_event_id),
118
+ self_pruned_commit_seq = COALESCE(self_pruned_commit_seq, 0)
119
+ WHERE transaction_id IS NULL OR commit_seq IS NULL OR pruned_commit_seq IS NULL
120
+ OR self_pruned_commit_seq IS NULL;
121
+
122
+ ALTER TABLE bunqueue_events
123
+ ALTER COLUMN transaction_id SET NOT NULL;
124
+ ALTER TABLE bunqueue_event_commits
125
+ ALTER COLUMN namespace SET NOT NULL,
126
+ ALTER COLUMN transaction_id SET NOT NULL;
127
+ ALTER TABLE bunqueue_event_prune_watermarks
128
+ ALTER COLUMN transaction_id SET NOT NULL,
129
+ ALTER COLUMN pruned_commit_seq SET NOT NULL,
130
+ ALTER COLUMN self_pruned_commit_seq SET NOT NULL,
131
+ ALTER COLUMN prunes_current_transaction SET NOT NULL;
132
+
133
+ DO $migration$
134
+ BEGIN
135
+ IF EXISTS (
136
+ SELECT 1 FROM pg_attribute
137
+ WHERE attrelid = to_regclass('bunqueue_events')
138
+ AND attname = 'commit_seq' AND NOT attisdropped
139
+ ) THEN
140
+ EXECUTE $sql$
141
+ INSERT INTO bunqueue_event_commits (namespace, transaction_id, commit_seq)
142
+ SELECT namespace, transaction_id, MAX(commit_seq)
143
+ FROM bunqueue_events
144
+ WHERE commit_seq IS NOT NULL
145
+ GROUP BY namespace, transaction_id
146
+ ON CONFLICT (namespace, transaction_id) DO UPDATE SET
147
+ commit_seq = GREATEST(
148
+ COALESCE(bunqueue_event_commits.commit_seq, 0),
149
+ excluded.commit_seq
150
+ )
151
+ $sql$;
152
+ ELSE
153
+ INSERT INTO bunqueue_event_commits (namespace, transaction_id, commit_seq)
154
+ SELECT namespace, transaction_id, MAX(id)
155
+ FROM bunqueue_events
156
+ GROUP BY namespace, transaction_id
157
+ ON CONFLICT (namespace, transaction_id) DO NOTHING;
158
+ END IF;
159
+ END;
160
+ $migration$;
161
+
162
+ INSERT INTO bunqueue_event_commits (namespace, transaction_id, commit_seq)
163
+ SELECT namespace, transaction_id, MAX(commit_seq)
164
+ FROM bunqueue_event_prune_watermarks
165
+ WHERE commit_seq IS NOT NULL
166
+ GROUP BY namespace, transaction_id
167
+ ON CONFLICT (namespace, transaction_id) DO UPDATE SET
168
+ commit_seq = GREATEST(
169
+ COALESCE(bunqueue_event_commits.commit_seq, 0),
170
+ excluded.commit_seq
171
+ );
172
+
173
+ DO $block$
174
+ DECLARE
175
+ journal_floor BIGINT := 0;
176
+ legacy_head_floor BIGINT := 0;
177
+ sequence_floor BIGINT := 0;
178
+ BEGIN
179
+ SELECT COALESCE(MAX(commit_seq), 0) INTO journal_floor
180
+ FROM bunqueue_event_commits;
181
+ SELECT last_value INTO sequence_floor FROM bunqueue_event_commit_seq;
182
+ IF to_regclass('bunqueue_event_commit_heads') IS NOT NULL THEN
183
+ EXECUTE 'SELECT COALESCE(MAX(commit_seq), 0) FROM bunqueue_event_commit_heads'
184
+ INTO legacy_head_floor;
185
+ END IF;
186
+ PERFORM setval(
187
+ 'bunqueue_event_commit_seq',
188
+ GREATEST(journal_floor, legacy_head_floor, sequence_floor, 1),
189
+ TRUE
190
+ );
191
+ END;
192
+ $block$;
193
+
194
+ DROP TABLE IF EXISTS bunqueue_event_commit_heads;
195
+
196
+ DROP TRIGGER IF EXISTS bunqueue_events_register_commit ON bunqueue_events;
197
+ DROP TRIGGER IF EXISTS bunqueue_watermarks_insert_register_commit
198
+ ON bunqueue_event_prune_watermarks;
199
+ DROP TRIGGER IF EXISTS bunqueue_watermarks_update_register_commit
200
+ ON bunqueue_event_prune_watermarks;
201
+ DROP TABLE IF EXISTS bunqueue_event_commit_assignments CASCADE;
202
+ DROP INDEX IF EXISTS bunqueue_events_commit_idx;
203
+ DROP INDEX IF EXISTS bunqueue_events_pending_commit_idx;
204
+ ALTER TABLE bunqueue_events DROP COLUMN IF EXISTS commit_seq;
205
+
206
+ DROP INDEX IF EXISTS bunqueue_events_transaction_idx;
207
+ DROP INDEX IF EXISTS bunqueue_event_commits_replay_idx;
208
+ DROP INDEX IF EXISTS bunqueue_event_prune_watermarks_commit_idx;
209
+ DROP INDEX IF EXISTS bunqueue_event_prune_watermarks_pending_commit_idx;
210
+ DROP INDEX IF EXISTS bunqueue_event_prune_watermarks_transaction_idx;
211
+
212
+ CREATE INDEX bunqueue_events_transaction_idx
213
+ ON bunqueue_events(namespace, transaction_id, id);
214
+ CREATE INDEX bunqueue_event_commits_replay_idx
215
+ ON bunqueue_event_commits(namespace, commit_seq, transaction_id)
216
+ WHERE commit_seq IS NOT NULL;
217
+ CREATE INDEX bunqueue_event_prune_watermarks_commit_idx
218
+ ON bunqueue_event_prune_watermarks(namespace, queue, commit_seq DESC)
219
+ WHERE commit_seq IS NOT NULL;
220
+ CREATE INDEX bunqueue_event_prune_watermarks_pending_commit_idx
221
+ ON bunqueue_event_prune_watermarks(namespace, transaction_id)
222
+ WHERE commit_seq IS NULL;
223
+ CREATE INDEX bunqueue_event_prune_watermarks_transaction_idx
224
+ ON bunqueue_event_prune_watermarks(namespace, transaction_id);
225
+
226
+ CREATE OR REPLACE FUNCTION bunqueue_register_event_rows()
227
+ RETURNS TRIGGER LANGUAGE plpgsql AS $function$
228
+ ${POSTGRES_REGISTER_EVENT_ROWS_BODY}
229
+ $function$;
230
+
231
+ CREATE OR REPLACE FUNCTION bunqueue_register_watermark_rows()
232
+ RETURNS TRIGGER LANGUAGE plpgsql AS $function$
233
+ ${POSTGRES_REGISTER_WATERMARK_ROWS_BODY}
234
+ $function$;
235
+
236
+ CREATE OR REPLACE FUNCTION bunqueue_assign_event_commit()
237
+ RETURNS TRIGGER LANGUAGE plpgsql AS $function$
238
+ ${POSTGRES_ASSIGN_EVENT_COMMIT_BODY}
239
+ $function$;
240
+
241
+ DROP TRIGGER IF EXISTS bunqueue_events_register_commit ON bunqueue_events;
242
+ CREATE TRIGGER bunqueue_events_register_commit
243
+ AFTER INSERT ON bunqueue_events
244
+ REFERENCING NEW TABLE AS new_event_rows
245
+ FOR EACH STATEMENT EXECUTE FUNCTION bunqueue_register_event_rows();
246
+
247
+ DROP TRIGGER IF EXISTS bunqueue_watermarks_insert_register_commit
248
+ ON bunqueue_event_prune_watermarks;
249
+ CREATE TRIGGER bunqueue_watermarks_insert_register_commit
250
+ AFTER INSERT ON bunqueue_event_prune_watermarks
251
+ REFERENCING NEW TABLE AS new_watermark_rows
252
+ FOR EACH STATEMENT EXECUTE FUNCTION bunqueue_register_watermark_rows();
253
+
254
+ DROP TRIGGER IF EXISTS bunqueue_watermarks_update_register_commit
255
+ ON bunqueue_event_prune_watermarks;
256
+ CREATE TRIGGER bunqueue_watermarks_update_register_commit
257
+ AFTER UPDATE ON bunqueue_event_prune_watermarks
258
+ REFERENCING NEW TABLE AS new_watermark_rows
259
+ FOR EACH STATEMENT EXECUTE FUNCTION bunqueue_register_watermark_rows();
260
+
261
+ DROP TRIGGER IF EXISTS bunqueue_assign_event_commit
262
+ ON bunqueue_event_commits;
263
+ CREATE CONSTRAINT TRIGGER bunqueue_assign_event_commit
264
+ AFTER INSERT ON bunqueue_event_commits
265
+ DEFERRABLE INITIALLY DEFERRED
266
+ FOR EACH ROW EXECUTE FUNCTION bunqueue_assign_event_commit();
267
+ `;
@@ -0,0 +1,26 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import type { PostgresContext } from './context';
3
+ export interface PostgresEventPruneWatermarkInput {
4
+ readonly queue: string;
5
+ readonly sourceEventId: number;
6
+ readonly prunedThrough: number;
7
+ readonly prunedCommitSeq?: number;
8
+ readonly prunesCurrentTransaction?: boolean;
9
+ }
10
+ export interface PostgresEventPruneWatermark extends PostgresEventPruneWatermarkInput {
11
+ readonly commitSeq: number;
12
+ readonly prunedCommitSeq: number;
13
+ /** Newest commit that pruned part of its own queue events, 0 when none did. */
14
+ readonly selfPrunedCommitSeq: number;
15
+ }
16
+ export declare function maxPostgresEventId(rows: readonly {
17
+ id: number | string | bigint;
18
+ }[]): number;
19
+ export declare function summarizePostgresPrunedEvents(rows: readonly {
20
+ id: number | string | bigint;
21
+ commit_seq: number | string | bigint | null;
22
+ }[]): Pick<PostgresEventPruneWatermarkInput, 'prunedThrough' | 'prunedCommitSeq' | 'prunesCurrentTransaction'>;
23
+ /** Record durable evidence that queue event history was pruned in this transaction. */
24
+ export declare function recordPostgresEventPruneWatermarks(tx: TransactionSQL, ctx: PostgresContext, inputs: readonly PostgresEventPruneWatermarkInput[]): Promise<void>;
25
+ export declare function loadPostgresEventPruneWatermarks(ctx: PostgresContext): Promise<PostgresEventPruneWatermark[]>;
26
+ export declare function deletePostgresEventPruneWatermark(tx: TransactionSQL, ctx: PostgresContext, queue: string): Promise<void>;
@@ -0,0 +1,129 @@
1
+ import { numeric } from './codec';
2
+ export function maxPostgresEventId(rows) {
3
+ return rows.reduce((latest, row) => Math.max(latest, numeric(row.id) ?? 0), 0);
4
+ }
5
+ export function summarizePostgresPrunedEvents(rows) {
6
+ return {
7
+ prunedThrough: maxPostgresEventId(rows),
8
+ prunedCommitSeq: rows.reduce((latest, row) => Math.max(latest, numeric(row.commit_seq) ?? 0), 0),
9
+ prunesCurrentTransaction: rows.some((row) => row.commit_seq === null),
10
+ };
11
+ }
12
+ function normalize(watermarks) {
13
+ const byQueue = new Map();
14
+ for (const watermark of watermarks) {
15
+ const current = byQueue.get(watermark.queue);
16
+ byQueue.set(watermark.queue, current
17
+ ? {
18
+ queue: watermark.queue,
19
+ sourceEventId: Math.max(current.sourceEventId, watermark.sourceEventId),
20
+ prunedThrough: Math.max(current.prunedThrough, watermark.prunedThrough),
21
+ prunedCommitSeq: Math.max(current.prunedCommitSeq ?? 0, watermark.prunedCommitSeq ?? 0),
22
+ prunesCurrentTransaction: current.prunesCurrentTransaction === true ||
23
+ watermark.prunesCurrentTransaction === true,
24
+ }
25
+ : watermark);
26
+ }
27
+ return [...byQueue.values()].sort((left, right) => left.queue.localeCompare(right.queue));
28
+ }
29
+ /** Record durable evidence that queue event history was pruned in this transaction. */
30
+ export async function recordPostgresEventPruneWatermarks(tx, ctx, inputs) {
31
+ const watermarks = normalize(inputs).filter(({ sourceEventId, prunedThrough }) => sourceEventId > 0 && prunedThrough > 0);
32
+ if (watermarks.length === 0)
33
+ return;
34
+ await tx `
35
+ WITH incoming AS (
36
+ SELECT batch.queue, batch.source_event_id, batch.pruned_through,
37
+ batch.pruned_commit_seq, batch.prunes_current_transaction
38
+ FROM unnest(
39
+ ${tx.array(watermarks.map(({ queue }) => queue), 'TEXT')},
40
+ ${tx.array(watermarks.map(({ sourceEventId }) => sourceEventId), 'BIGINT')},
41
+ ${tx.array(watermarks.map(({ prunedThrough }) => prunedThrough), 'BIGINT')},
42
+ ${tx.array(watermarks.map(({ prunedCommitSeq }) => prunedCommitSeq ?? 0), 'BIGINT')},
43
+ ${tx.array(watermarks.map(({ prunesCurrentTransaction }) => prunesCurrentTransaction ?? false), 'BOOLEAN')}
44
+ ) AS batch(
45
+ queue, source_event_id, pruned_through, pruned_commit_seq,
46
+ prunes_current_transaction
47
+ )
48
+ ), inserted AS (
49
+ INSERT INTO bunqueue_event_prune_watermarks
50
+ (namespace, queue, source_event_id, pruned_through, pruned_commit_seq,
51
+ self_pruned_commit_seq, prunes_current_transaction)
52
+ SELECT ${ctx.config.namespace}, queue, source_event_id, pruned_through,
53
+ GREATEST(
54
+ pruned_commit_seq,
55
+ COALESCE((
56
+ SELECT MAX(existing.pruned_commit_seq)
57
+ FROM bunqueue_event_prune_watermarks AS existing
58
+ WHERE existing.namespace = ${ctx.config.namespace}
59
+ AND existing.queue = incoming.queue
60
+ ), 0)
61
+ ),
62
+ COALESCE((
63
+ SELECT MAX(existing.self_pruned_commit_seq)
64
+ FROM bunqueue_event_prune_watermarks AS existing
65
+ WHERE existing.namespace = ${ctx.config.namespace}
66
+ AND existing.queue = incoming.queue
67
+ ), 0),
68
+ prunes_current_transaction OR COALESCE((
69
+ SELECT bool_or(existing.prunes_current_transaction)
70
+ FROM bunqueue_event_prune_watermarks AS existing
71
+ WHERE existing.namespace = ${ctx.config.namespace}
72
+ AND existing.queue = incoming.queue
73
+ AND existing.commit_seq IS NULL
74
+ ), FALSE)
75
+ FROM incoming
76
+ ON CONFLICT (namespace, queue, source_event_id) DO UPDATE SET
77
+ pruned_through = GREATEST(
78
+ bunqueue_event_prune_watermarks.pruned_through,
79
+ excluded.pruned_through
80
+ ),
81
+ pruned_commit_seq = GREATEST(
82
+ COALESCE(bunqueue_event_prune_watermarks.pruned_commit_seq, 0),
83
+ COALESCE(excluded.pruned_commit_seq, 0)
84
+ ),
85
+ self_pruned_commit_seq = GREATEST(
86
+ COALESCE(bunqueue_event_prune_watermarks.self_pruned_commit_seq, 0),
87
+ COALESCE(excluded.self_pruned_commit_seq, 0)
88
+ ),
89
+ prunes_current_transaction =
90
+ bunqueue_event_prune_watermarks.prunes_current_transaction
91
+ OR excluded.prunes_current_transaction,
92
+ transaction_id = pg_current_xact_id()::text::bigint,
93
+ commit_seq = NULL
94
+ RETURNING namespace, queue, source_event_id, pruned_through
95
+ )
96
+ DELETE FROM bunqueue_event_prune_watermarks AS previous
97
+ USING inserted AS latest
98
+ WHERE previous.namespace = latest.namespace
99
+ AND previous.queue = latest.queue
100
+ AND previous.source_event_id <> latest.source_event_id
101
+ AND previous.pruned_through <= latest.pruned_through
102
+ `;
103
+ }
104
+ export async function loadPostgresEventPruneWatermarks(ctx) {
105
+ const rows = await ctx.sql `
106
+ SELECT DISTINCT ON (queue)
107
+ queue, source_event_id, pruned_through, commit_seq,
108
+ MAX(pruned_commit_seq) OVER (PARTITION BY queue) AS pruned_commit_seq,
109
+ MAX(self_pruned_commit_seq) OVER (PARTITION BY queue) AS self_pruned_commit_seq
110
+ FROM bunqueue_event_prune_watermarks
111
+ WHERE namespace = ${ctx.config.namespace}
112
+ AND commit_seq IS NOT NULL
113
+ ORDER BY queue, commit_seq DESC, source_event_id DESC
114
+ `;
115
+ return rows.map((row) => ({
116
+ queue: row.queue,
117
+ sourceEventId: numeric(row.source_event_id) ?? 0,
118
+ prunedThrough: numeric(row.pruned_through) ?? 0,
119
+ commitSeq: numeric(row.commit_seq) ?? 0,
120
+ prunedCommitSeq: numeric(row.pruned_commit_seq) ?? 0,
121
+ selfPrunedCommitSeq: numeric(row.self_pruned_commit_seq) ?? 0,
122
+ }));
123
+ }
124
+ export async function deletePostgresEventPruneWatermark(tx, ctx, queue) {
125
+ await tx `
126
+ DELETE FROM bunqueue_event_prune_watermarks
127
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
128
+ `;
129
+ }
@@ -0,0 +1,18 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ import type { PostgresContext } from './context';
3
+ export interface PostgresPrunedEventRow {
4
+ readonly id: number | string | bigint;
5
+ readonly commit_seq: number | string | bigint | null;
6
+ }
7
+ /** Serialize one autonomous queue-retention sweep without joining job lock domains. */
8
+ export declare function lockPostgresEventRetention(tx: TransactionSQL, ctx: PostgresContext, queue: string): Promise<void>;
9
+ /** Never wait for a late inline retention lock after job locks are already held. */
10
+ export declare function tryLockPostgresEventRetention(tx: TransactionSQL, ctx: PostgresContext, queue: string): Promise<boolean>;
11
+ /**
12
+ * Retain the newest queue events while locking overlapping prune candidates in
13
+ * one deterministic order. The materialized lock step prevents concurrent
14
+ * brokers from deleting the same physical tuples in conflicting scan orders.
15
+ */
16
+ export declare function prunePostgresQueueEvents(tx: TransactionSQL, ctx: PostgresContext, queue: string, maxLength: number): Promise<PostgresPrunedEventRow[]>;
17
+ /** Remove a queue's old outbox history in the same tuple order used by retention. */
18
+ export declare function deletePostgresQueueEventHistory(tx: TransactionSQL, ctx: PostgresContext, queue: string): Promise<number>;
@@ -0,0 +1,76 @@
1
+ import { postgresAdvisoryLockName } from './advisoryLocks';
2
+ /** Serialize one autonomous queue-retention sweep without joining job lock domains. */
3
+ export async function lockPostgresEventRetention(tx, ctx, queue) {
4
+ const lockName = postgresAdvisoryLockName('event-retention', ctx.config.namespace, queue);
5
+ await tx `
6
+ SELECT pg_advisory_xact_lock(hashtextextended(${lockName}, 0))
7
+ `;
8
+ }
9
+ /** Never wait for a late inline retention lock after job locks are already held. */
10
+ export async function tryLockPostgresEventRetention(tx, ctx, queue) {
11
+ const lockName = postgresAdvisoryLockName('event-retention', ctx.config.namespace, queue);
12
+ const [row] = await tx `
13
+ SELECT pg_try_advisory_xact_lock(hashtextextended(${lockName}, 0)) AS acquired
14
+ `;
15
+ return row.acquired;
16
+ }
17
+ /**
18
+ * Retain the newest queue events while locking overlapping prune candidates in
19
+ * one deterministic order. The materialized lock step prevents concurrent
20
+ * brokers from deleting the same physical tuples in conflicting scan orders.
21
+ */
22
+ export async function prunePostgresQueueEvents(tx, ctx, queue, maxLength) {
23
+ return await tx `
24
+ WITH cutoff AS MATERIALIZED (
25
+ SELECT id
26
+ FROM bunqueue_events
27
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
28
+ ORDER BY id DESC
29
+ OFFSET ${maxLength}
30
+ LIMIT 1
31
+ ), candidates AS MATERIALIZED (
32
+ SELECT event.id
33
+ FROM bunqueue_events AS event
34
+ CROSS JOIN cutoff
35
+ WHERE event.namespace = ${ctx.config.namespace} AND event.queue = ${queue}
36
+ AND event.id <= cutoff.id
37
+ ORDER BY event.id
38
+ FOR UPDATE OF event
39
+ ), removed AS (
40
+ DELETE FROM bunqueue_events AS event
41
+ USING candidates
42
+ WHERE event.id = candidates.id
43
+ RETURNING event.id, event.namespace, event.transaction_id
44
+ )
45
+ SELECT removed.id, journal.commit_seq
46
+ FROM removed
47
+ LEFT JOIN bunqueue_event_commits AS journal
48
+ ON journal.namespace = removed.namespace
49
+ AND journal.transaction_id = removed.transaction_id
50
+ ORDER BY removed.id
51
+ `;
52
+ }
53
+ /** Remove a queue's old outbox history in the same tuple order used by retention. */
54
+ export async function deletePostgresQueueEventHistory(tx, ctx, queue) {
55
+ const removed = await tx `
56
+ WITH candidates AS MATERIALIZED (
57
+ SELECT event.id
58
+ FROM bunqueue_events AS event
59
+ WHERE event.namespace = ${ctx.config.namespace}
60
+ AND (
61
+ event.queue = ${queue}
62
+ OR (
63
+ event.event_type = 'queue-batch-overflow'
64
+ AND event.job_id = ${`__queue__:${queue}`}
65
+ )
66
+ )
67
+ ORDER BY event.id
68
+ FOR UPDATE OF event
69
+ )
70
+ DELETE FROM bunqueue_events AS event
71
+ USING candidates
72
+ WHERE event.id = candidates.id
73
+ RETURNING event.id
74
+ `;
75
+ return removed.length;
76
+ }