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,95 @@
1
+ /** Assign durable rotation positions to every pending group lacking one. */
2
+ export async function ensurePostgresGroupStates(tx, ctx, queue, sequence) {
3
+ const assigned = await tx `
4
+ WITH unsequenced AS (
5
+ SELECT job.group_id, MIN(job.group_order) AS first_order
6
+ FROM bunqueue_jobs AS job
7
+ LEFT JOIN bunqueue_group_state AS groups
8
+ ON groups.namespace = job.namespace AND groups.queue = job.queue
9
+ AND groups.group_id = job.group_id
10
+ WHERE job.namespace = ${ctx.config.namespace} AND job.queue = ${queue}
11
+ AND job.group_id IS NOT NULL AND groups.last_served IS NULL
12
+ AND job.state IN ('waiting', 'prioritized', 'delayed')
13
+ GROUP BY job.group_id
14
+ ), ordered AS (
15
+ SELECT group_id,
16
+ row_number() OVER (ORDER BY first_order, group_id) AS position
17
+ FROM unsequenced
18
+ )
19
+ INSERT INTO bunqueue_group_state (namespace, queue, group_id, last_served)
20
+ SELECT ${ctx.config.namespace}, ${queue}, group_id, ${sequence} + position
21
+ FROM ordered
22
+ ON CONFLICT (namespace, queue, group_id) DO UPDATE
23
+ SET last_served = EXCLUDED.last_served
24
+ WHERE bunqueue_group_state.last_served IS NULL
25
+ RETURNING group_id
26
+ `;
27
+ if (assigned.length === 0)
28
+ return sequence;
29
+ const next = sequence + assigned.length;
30
+ await tx `
31
+ UPDATE bunqueue_queue_state SET group_sequence = ${next}
32
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
33
+ `;
34
+ return next;
35
+ }
36
+ /** Reset fixed windows when they expire or a worker changes the effective defaults. */
37
+ export async function refreshPostgresGroupRateWindows(tx, ctx, queue, options, now) {
38
+ if (!options?.limit)
39
+ return;
40
+ const { max, duration } = options.limit;
41
+ await tx `
42
+ UPDATE bunqueue_group_state AS groups
43
+ SET rate_window_started_at = ${now}, rate_count = 0,
44
+ rate_effective_max = COALESCE(groups.rate_limit, ${max}),
45
+ rate_effective_duration_ms = COALESCE(groups.rate_duration_ms, ${duration})
46
+ WHERE groups.namespace = ${ctx.config.namespace} AND groups.queue = ${queue}
47
+ AND EXISTS (
48
+ SELECT 1 FROM bunqueue_jobs AS job
49
+ WHERE job.namespace = groups.namespace AND job.queue = groups.queue
50
+ AND job.group_id = groups.group_id
51
+ AND job.state IN ('waiting', 'prioritized', 'delayed')
52
+ )
53
+ AND (
54
+ groups.rate_window_started_at IS NULL
55
+ OR groups.rate_effective_max IS DISTINCT FROM COALESCE(groups.rate_limit, ${max})
56
+ OR groups.rate_effective_duration_ms IS DISTINCT FROM
57
+ COALESCE(groups.rate_duration_ms, ${duration})
58
+ OR groups.rate_window_started_at + COALESCE(groups.rate_duration_ms, ${duration}) <= ${now}
59
+ )
60
+ `;
61
+ }
62
+ /** Consume group budgets and move the durable round-robin cursor after row locks are secured. */
63
+ export async function commitPostgresGroupClaims(tx, ctx, input) {
64
+ const { queue, rows, options, sequence } = input;
65
+ const grouped = rows.filter((row) => row.group_id !== null);
66
+ if (grouped.length === 0)
67
+ return;
68
+ const turns = new Map();
69
+ for (let index = 0; index < grouped.length; index++) {
70
+ const groupId = grouped[index].group_id;
71
+ const current = turns.get(groupId);
72
+ turns.set(groupId, {
73
+ last: sequence + index + 1,
74
+ count: (current?.count ?? 0) + 1,
75
+ });
76
+ }
77
+ const entries = [...turns.entries()];
78
+ await tx `
79
+ UPDATE bunqueue_group_state AS groups
80
+ SET last_served = batch.last_served,
81
+ rate_count = groups.rate_count + CASE WHEN ${options?.limit !== undefined}
82
+ THEN batch.claimed_count ELSE 0 END
83
+ FROM unnest(
84
+ ${tx.array(entries.map(([groupId]) => groupId), 'TEXT')},
85
+ ${tx.array(entries.map(([, value]) => value.last), 'BIGINT')},
86
+ ${tx.array(entries.map(([, value]) => value.count), 'INTEGER')}
87
+ ) AS batch(group_id, last_served, claimed_count)
88
+ WHERE groups.namespace = ${ctx.config.namespace} AND groups.queue = ${queue}
89
+ AND groups.group_id = batch.group_id
90
+ `;
91
+ await tx `
92
+ UPDATE bunqueue_queue_state SET group_sequence = ${sequence + grouped.length}
93
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
94
+ `;
95
+ }
@@ -0,0 +1,2 @@
1
+ /** Correctness-critical group schema and idempotent v19 repair operations. */
2
+ export declare const POSTGRES_GROUP_SCHEMA = "\nCREATE SEQUENCE IF NOT EXISTS bunqueue_group_order_seq AS BIGINT CACHE 1;\nALTER SEQUENCE bunqueue_group_order_seq\n AS BIGINT INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE CACHE 1;\n\nALTER TABLE bunqueue_jobs ADD COLUMN IF NOT EXISTS group_order BIGINT;\nALTER TABLE bunqueue_jobs\n ALTER COLUMN group_order TYPE BIGINT USING group_order::bigint,\n ALTER COLUMN group_order DROP DEFAULT,\n ALTER COLUMN group_order DROP NOT NULL;\n\nDO $group_order_backfill$\nDECLARE\n target RECORD;\nBEGIN\n PERFORM setval(\n 'bunqueue_group_order_seq',\n GREATEST(\n (SELECT last_value FROM bunqueue_group_order_seq),\n COALESCE((SELECT MAX(group_order) FROM bunqueue_jobs), 0),\n 1\n ),\n TRUE\n );\n FOR target IN\n SELECT namespace, id\n FROM bunqueue_jobs\n WHERE group_id IS NOT NULL AND group_order IS NULL\n ORDER BY created_at, id, namespace\n LOOP\n UPDATE bunqueue_jobs\n SET group_order = nextval('bunqueue_group_order_seq')\n WHERE namespace = target.namespace AND id = target.id;\n END LOOP;\nEND;\n$group_order_backfill$;\n\nCREATE TABLE IF NOT EXISTS bunqueue_group_state (\n namespace TEXT NOT NULL,\n queue TEXT NOT NULL,\n group_id TEXT NOT NULL,\n rate_limit BIGINT,\n rate_duration_ms BIGINT,\n rate_window_started_at BIGINT,\n rate_count BIGINT NOT NULL DEFAULT 0,\n rate_effective_max BIGINT,\n rate_effective_duration_ms BIGINT,\n concurrency_limit BIGINT,\n last_served BIGINT,\n PRIMARY KEY (namespace, queue, group_id)\n);\nALTER TABLE bunqueue_group_state\n ADD COLUMN IF NOT EXISTS namespace TEXT,\n ADD COLUMN IF NOT EXISTS queue TEXT,\n ADD COLUMN IF NOT EXISTS group_id TEXT,\n ADD COLUMN IF NOT EXISTS rate_limit BIGINT,\n ADD COLUMN IF NOT EXISTS rate_duration_ms BIGINT,\n ADD COLUMN IF NOT EXISTS rate_window_started_at BIGINT,\n ADD COLUMN IF NOT EXISTS rate_count BIGINT DEFAULT 0,\n ADD COLUMN IF NOT EXISTS rate_effective_max BIGINT,\n ADD COLUMN IF NOT EXISTS rate_effective_duration_ms BIGINT,\n ADD COLUMN IF NOT EXISTS concurrency_limit BIGINT,\n ADD COLUMN IF NOT EXISTS last_served BIGINT;\nALTER TABLE bunqueue_group_state\n ALTER COLUMN namespace TYPE TEXT USING namespace::text,\n ALTER COLUMN queue TYPE TEXT USING queue::text,\n ALTER COLUMN group_id TYPE TEXT USING group_id::text,\n ALTER COLUMN rate_limit TYPE BIGINT USING rate_limit::bigint,\n ALTER COLUMN rate_duration_ms TYPE BIGINT USING rate_duration_ms::bigint,\n ALTER COLUMN rate_window_started_at TYPE BIGINT USING rate_window_started_at::bigint,\n ALTER COLUMN rate_count TYPE BIGINT USING rate_count::bigint,\n ALTER COLUMN rate_effective_max TYPE BIGINT USING rate_effective_max::bigint,\n ALTER COLUMN rate_effective_duration_ms TYPE BIGINT USING rate_effective_duration_ms::bigint,\n ALTER COLUMN concurrency_limit TYPE BIGINT USING concurrency_limit::bigint,\n ALTER COLUMN last_served TYPE BIGINT USING last_served::bigint;\nUPDATE bunqueue_group_state SET rate_count = 0 WHERE rate_count IS NULL;\nALTER TABLE bunqueue_group_state\n ALTER COLUMN namespace DROP DEFAULT,\n ALTER COLUMN namespace SET NOT NULL,\n ALTER COLUMN queue DROP DEFAULT,\n ALTER COLUMN queue SET NOT NULL,\n ALTER COLUMN group_id DROP DEFAULT,\n ALTER COLUMN group_id SET NOT NULL,\n ALTER COLUMN rate_limit DROP DEFAULT,\n ALTER COLUMN rate_limit DROP NOT NULL,\n ALTER COLUMN rate_duration_ms DROP DEFAULT,\n ALTER COLUMN rate_duration_ms DROP NOT NULL,\n ALTER COLUMN rate_window_started_at DROP DEFAULT,\n ALTER COLUMN rate_window_started_at DROP NOT NULL,\n ALTER COLUMN rate_count SET DEFAULT 0,\n ALTER COLUMN rate_count SET NOT NULL,\n ALTER COLUMN rate_effective_max DROP DEFAULT,\n ALTER COLUMN rate_effective_max DROP NOT NULL,\n ALTER COLUMN rate_effective_duration_ms DROP DEFAULT,\n ALTER COLUMN rate_effective_duration_ms DROP NOT NULL,\n ALTER COLUMN concurrency_limit DROP DEFAULT,\n ALTER COLUMN concurrency_limit DROP NOT NULL,\n ALTER COLUMN last_served DROP DEFAULT,\n ALTER COLUMN last_served DROP NOT NULL;\n\nDO $group_state_primary_key$\nDECLARE\n constraint_name TEXT;\nBEGIN\n IF NOT EXISTS (\n SELECT 1\n FROM pg_constraint AS constraint_state\n JOIN pg_index AS index_state ON index_state.indexrelid = constraint_state.conindid\n JOIN pg_class AS index_class ON index_class.oid = index_state.indexrelid\n JOIN pg_am AS access_method ON access_method.oid = index_class.relam\n WHERE constraint_state.conrelid = to_regclass('bunqueue_group_state')\n AND constraint_state.contype = 'p'\n AND constraint_state.convalidated\n AND NOT constraint_state.condeferrable\n AND NOT constraint_state.condeferred\n AND index_state.indisprimary AND index_state.indisunique\n AND index_state.indisvalid AND index_state.indisready\n AND access_method.amname = 'btree'\n AND ARRAY(\n SELECT attribute.attname::text\n FROM unnest(constraint_state.conkey) WITH ORDINALITY AS key(attnum, position)\n JOIN pg_attribute AS attribute\n ON attribute.attrelid = constraint_state.conrelid\n AND attribute.attnum = key.attnum\n ORDER BY key.position\n ) = ARRAY['namespace', 'queue', 'group_id']\n ) THEN\n FOR constraint_name IN\n SELECT conname FROM pg_constraint\n WHERE conrelid = to_regclass('bunqueue_group_state')\n AND (contype = 'p' OR conname = 'bunqueue_group_state_pkey')\n LOOP\n EXECUTE format(\n 'ALTER TABLE bunqueue_group_state DROP CONSTRAINT %I',\n constraint_name\n );\n END LOOP;\n IF to_regclass('bunqueue_group_state_pkey') IS NOT NULL THEN\n DROP INDEX bunqueue_group_state_pkey;\n END IF;\n ALTER TABLE bunqueue_group_state\n ADD CONSTRAINT bunqueue_group_state_pkey PRIMARY KEY (namespace, queue, group_id);\n END IF;\nEND;\n$group_state_primary_key$;\n\nDROP INDEX IF EXISTS bunqueue_jobs_group_ready_idx;\nCREATE INDEX bunqueue_jobs_group_ready_idx\n ON bunqueue_jobs(namespace, queue, group_id, run_at ASC, group_order ASC, id ASC)\n WHERE group_id IS NOT NULL AND state IN ('waiting', 'prioritized', 'delayed');\nDROP INDEX IF EXISTS bunqueue_group_state_rotation_idx;\nCREATE INDEX bunqueue_group_state_rotation_idx\n ON bunqueue_group_state(namespace, queue, last_served, group_id);\n";
@@ -0,0 +1,155 @@
1
+ /** Correctness-critical group schema and idempotent v19 repair operations. */
2
+ export const POSTGRES_GROUP_SCHEMA = `
3
+ CREATE SEQUENCE IF NOT EXISTS bunqueue_group_order_seq AS BIGINT CACHE 1;
4
+ ALTER SEQUENCE bunqueue_group_order_seq
5
+ AS BIGINT INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE CACHE 1;
6
+
7
+ ALTER TABLE bunqueue_jobs ADD COLUMN IF NOT EXISTS group_order BIGINT;
8
+ ALTER TABLE bunqueue_jobs
9
+ ALTER COLUMN group_order TYPE BIGINT USING group_order::bigint,
10
+ ALTER COLUMN group_order DROP DEFAULT,
11
+ ALTER COLUMN group_order DROP NOT NULL;
12
+
13
+ DO $group_order_backfill$
14
+ DECLARE
15
+ target RECORD;
16
+ BEGIN
17
+ PERFORM setval(
18
+ 'bunqueue_group_order_seq',
19
+ GREATEST(
20
+ (SELECT last_value FROM bunqueue_group_order_seq),
21
+ COALESCE((SELECT MAX(group_order) FROM bunqueue_jobs), 0),
22
+ 1
23
+ ),
24
+ TRUE
25
+ );
26
+ FOR target IN
27
+ SELECT namespace, id
28
+ FROM bunqueue_jobs
29
+ WHERE group_id IS NOT NULL AND group_order IS NULL
30
+ ORDER BY created_at, id, namespace
31
+ LOOP
32
+ UPDATE bunqueue_jobs
33
+ SET group_order = nextval('bunqueue_group_order_seq')
34
+ WHERE namespace = target.namespace AND id = target.id;
35
+ END LOOP;
36
+ END;
37
+ $group_order_backfill$;
38
+
39
+ CREATE TABLE IF NOT EXISTS bunqueue_group_state (
40
+ namespace TEXT NOT NULL,
41
+ queue TEXT NOT NULL,
42
+ group_id TEXT NOT NULL,
43
+ rate_limit BIGINT,
44
+ rate_duration_ms BIGINT,
45
+ rate_window_started_at BIGINT,
46
+ rate_count BIGINT NOT NULL DEFAULT 0,
47
+ rate_effective_max BIGINT,
48
+ rate_effective_duration_ms BIGINT,
49
+ concurrency_limit BIGINT,
50
+ last_served BIGINT,
51
+ PRIMARY KEY (namespace, queue, group_id)
52
+ );
53
+ ALTER TABLE bunqueue_group_state
54
+ ADD COLUMN IF NOT EXISTS namespace TEXT,
55
+ ADD COLUMN IF NOT EXISTS queue TEXT,
56
+ ADD COLUMN IF NOT EXISTS group_id TEXT,
57
+ ADD COLUMN IF NOT EXISTS rate_limit BIGINT,
58
+ ADD COLUMN IF NOT EXISTS rate_duration_ms BIGINT,
59
+ ADD COLUMN IF NOT EXISTS rate_window_started_at BIGINT,
60
+ ADD COLUMN IF NOT EXISTS rate_count BIGINT DEFAULT 0,
61
+ ADD COLUMN IF NOT EXISTS rate_effective_max BIGINT,
62
+ ADD COLUMN IF NOT EXISTS rate_effective_duration_ms BIGINT,
63
+ ADD COLUMN IF NOT EXISTS concurrency_limit BIGINT,
64
+ ADD COLUMN IF NOT EXISTS last_served BIGINT;
65
+ ALTER TABLE bunqueue_group_state
66
+ ALTER COLUMN namespace TYPE TEXT USING namespace::text,
67
+ ALTER COLUMN queue TYPE TEXT USING queue::text,
68
+ ALTER COLUMN group_id TYPE TEXT USING group_id::text,
69
+ ALTER COLUMN rate_limit TYPE BIGINT USING rate_limit::bigint,
70
+ ALTER COLUMN rate_duration_ms TYPE BIGINT USING rate_duration_ms::bigint,
71
+ ALTER COLUMN rate_window_started_at TYPE BIGINT USING rate_window_started_at::bigint,
72
+ ALTER COLUMN rate_count TYPE BIGINT USING rate_count::bigint,
73
+ ALTER COLUMN rate_effective_max TYPE BIGINT USING rate_effective_max::bigint,
74
+ ALTER COLUMN rate_effective_duration_ms TYPE BIGINT USING rate_effective_duration_ms::bigint,
75
+ ALTER COLUMN concurrency_limit TYPE BIGINT USING concurrency_limit::bigint,
76
+ ALTER COLUMN last_served TYPE BIGINT USING last_served::bigint;
77
+ UPDATE bunqueue_group_state SET rate_count = 0 WHERE rate_count IS NULL;
78
+ ALTER TABLE bunqueue_group_state
79
+ ALTER COLUMN namespace DROP DEFAULT,
80
+ ALTER COLUMN namespace SET NOT NULL,
81
+ ALTER COLUMN queue DROP DEFAULT,
82
+ ALTER COLUMN queue SET NOT NULL,
83
+ ALTER COLUMN group_id DROP DEFAULT,
84
+ ALTER COLUMN group_id SET NOT NULL,
85
+ ALTER COLUMN rate_limit DROP DEFAULT,
86
+ ALTER COLUMN rate_limit DROP NOT NULL,
87
+ ALTER COLUMN rate_duration_ms DROP DEFAULT,
88
+ ALTER COLUMN rate_duration_ms DROP NOT NULL,
89
+ ALTER COLUMN rate_window_started_at DROP DEFAULT,
90
+ ALTER COLUMN rate_window_started_at DROP NOT NULL,
91
+ ALTER COLUMN rate_count SET DEFAULT 0,
92
+ ALTER COLUMN rate_count SET NOT NULL,
93
+ ALTER COLUMN rate_effective_max DROP DEFAULT,
94
+ ALTER COLUMN rate_effective_max DROP NOT NULL,
95
+ ALTER COLUMN rate_effective_duration_ms DROP DEFAULT,
96
+ ALTER COLUMN rate_effective_duration_ms DROP NOT NULL,
97
+ ALTER COLUMN concurrency_limit DROP DEFAULT,
98
+ ALTER COLUMN concurrency_limit DROP NOT NULL,
99
+ ALTER COLUMN last_served DROP DEFAULT,
100
+ ALTER COLUMN last_served DROP NOT NULL;
101
+
102
+ DO $group_state_primary_key$
103
+ DECLARE
104
+ constraint_name TEXT;
105
+ BEGIN
106
+ IF NOT EXISTS (
107
+ SELECT 1
108
+ FROM pg_constraint AS constraint_state
109
+ JOIN pg_index AS index_state ON index_state.indexrelid = constraint_state.conindid
110
+ JOIN pg_class AS index_class ON index_class.oid = index_state.indexrelid
111
+ JOIN pg_am AS access_method ON access_method.oid = index_class.relam
112
+ WHERE constraint_state.conrelid = to_regclass('bunqueue_group_state')
113
+ AND constraint_state.contype = 'p'
114
+ AND constraint_state.convalidated
115
+ AND NOT constraint_state.condeferrable
116
+ AND NOT constraint_state.condeferred
117
+ AND index_state.indisprimary AND index_state.indisunique
118
+ AND index_state.indisvalid AND index_state.indisready
119
+ AND access_method.amname = 'btree'
120
+ AND ARRAY(
121
+ SELECT attribute.attname::text
122
+ FROM unnest(constraint_state.conkey) WITH ORDINALITY AS key(attnum, position)
123
+ JOIN pg_attribute AS attribute
124
+ ON attribute.attrelid = constraint_state.conrelid
125
+ AND attribute.attnum = key.attnum
126
+ ORDER BY key.position
127
+ ) = ARRAY['namespace', 'queue', 'group_id']
128
+ ) THEN
129
+ FOR constraint_name IN
130
+ SELECT conname FROM pg_constraint
131
+ WHERE conrelid = to_regclass('bunqueue_group_state')
132
+ AND (contype = 'p' OR conname = 'bunqueue_group_state_pkey')
133
+ LOOP
134
+ EXECUTE format(
135
+ 'ALTER TABLE bunqueue_group_state DROP CONSTRAINT %I',
136
+ constraint_name
137
+ );
138
+ END LOOP;
139
+ IF to_regclass('bunqueue_group_state_pkey') IS NOT NULL THEN
140
+ DROP INDEX bunqueue_group_state_pkey;
141
+ END IF;
142
+ ALTER TABLE bunqueue_group_state
143
+ ADD CONSTRAINT bunqueue_group_state_pkey PRIMARY KEY (namespace, queue, group_id);
144
+ END IF;
145
+ END;
146
+ $group_state_primary_key$;
147
+
148
+ DROP INDEX IF EXISTS bunqueue_jobs_group_ready_idx;
149
+ CREATE INDEX bunqueue_jobs_group_ready_idx
150
+ ON bunqueue_jobs(namespace, queue, group_id, run_at ASC, group_order ASC, id ASC)
151
+ WHERE group_id IS NOT NULL AND state IN ('waiting', 'prioritized', 'delayed');
152
+ DROP INDEX IF EXISTS bunqueue_group_state_rotation_idx;
153
+ CREATE INDEX bunqueue_group_state_rotation_idx
154
+ ON bunqueue_group_state(namespace, queue, last_served, group_id);
155
+ `;
@@ -0,0 +1,3 @@
1
+ import type { TransactionSQL } from 'bun';
2
+ /** Verify every catalog property required by durable group scheduling. */
3
+ export declare function hasCurrentPostgresGroupSchema(tx: TransactionSQL): Promise<boolean>;
@@ -0,0 +1,127 @@
1
+ /** Verify every catalog property required by durable group scheduling. */
2
+ export async function hasCurrentPostgresGroupSchema(tx) {
3
+ const [schema] = await tx `
4
+ WITH expected_columns(table_name, column_name, type_name, not_null, default_kind) AS (
5
+ VALUES
6
+ ('bunqueue_jobs', 'group_order', 'bigint', FALSE, 'none'),
7
+ ('bunqueue_group_state', 'namespace', 'text', TRUE, 'none'),
8
+ ('bunqueue_group_state', 'queue', 'text', TRUE, 'none'),
9
+ ('bunqueue_group_state', 'group_id', 'text', TRUE, 'none'),
10
+ ('bunqueue_group_state', 'rate_limit', 'bigint', FALSE, 'none'),
11
+ ('bunqueue_group_state', 'rate_duration_ms', 'bigint', FALSE, 'none'),
12
+ ('bunqueue_group_state', 'rate_window_started_at', 'bigint', FALSE, 'none'),
13
+ ('bunqueue_group_state', 'rate_count', 'bigint', TRUE, 'zero'),
14
+ ('bunqueue_group_state', 'rate_effective_max', 'bigint', FALSE, 'none'),
15
+ ('bunqueue_group_state', 'rate_effective_duration_ms', 'bigint', FALSE, 'none'),
16
+ ('bunqueue_group_state', 'concurrency_limit', 'bigint', FALSE, 'none'),
17
+ ('bunqueue_group_state', 'last_served', 'bigint', FALSE, 'none')
18
+ ), expected_indexes(
19
+ index_name, table_name, columns, descending, predicate
20
+ ) AS (
21
+ VALUES
22
+ ('bunqueue_jobs_group_ready_idx', 'bunqueue_jobs',
23
+ ARRAY['namespace', 'queue', 'group_id', 'run_at', 'group_order', 'id'],
24
+ ARRAY[FALSE, FALSE, FALSE, FALSE, FALSE, FALSE],
25
+ 'group_idisnotnullandstate=anyarray[''waiting''::text,''prioritized''::text,' ||
26
+ '''delayed''::text]'),
27
+ ('bunqueue_group_state_rotation_idx', 'bunqueue_group_state',
28
+ ARRAY['namespace', 'queue', 'last_served', 'group_id'],
29
+ ARRAY[FALSE, FALSE, FALSE, FALSE], '')
30
+ )
31
+ SELECT
32
+ to_regclass('bunqueue_group_state') IS NOT NULL
33
+ AND EXISTS (
34
+ SELECT 1 FROM pg_sequence
35
+ WHERE seqrelid = to_regclass('bunqueue_group_order_seq')
36
+ AND seqtypid = to_regtype('bigint')
37
+ AND seqincrement = 1 AND seqmin = 1
38
+ AND seqmax = 9223372036854775807
39
+ AND NOT seqcycle AND seqcache = 1
40
+ )
41
+ AND (
42
+ SELECT COALESCE(bool_and(COALESCE(
43
+ attribute.atttypid = to_regtype(expected.type_name)
44
+ AND attribute.attnotnull = expected.not_null
45
+ AND CASE expected.default_kind
46
+ WHEN 'zero' THEN regexp_replace(
47
+ lower(COALESCE(pg_get_expr(defaults.adbin, defaults.adrelid), '')),
48
+ '[()[:space:]]', '', 'g'
49
+ ) IN ('0', '0::bigint')
50
+ ELSE defaults.oid IS NULL
51
+ END,
52
+ FALSE
53
+ )), FALSE)
54
+ FROM expected_columns AS expected
55
+ LEFT JOIN pg_attribute AS attribute
56
+ ON attribute.attrelid = to_regclass(expected.table_name)
57
+ AND attribute.attname = expected.column_name
58
+ AND NOT attribute.attisdropped
59
+ LEFT JOIN pg_attrdef AS defaults
60
+ ON defaults.adrelid = attribute.attrelid
61
+ AND defaults.adnum = attribute.attnum
62
+ )
63
+ AND (
64
+ SELECT COALESCE(bool_and(COALESCE(
65
+ indexes.indrelid = to_regclass(expected.table_name)
66
+ AND indexes.indisvalid AND indexes.indisready
67
+ AND NOT indexes.indisunique AND NOT indexes.indisexclusion
68
+ AND indexes.indnkeyatts = cardinality(expected.columns)
69
+ AND indexes.indnatts = indexes.indnkeyatts
70
+ AND indexes.indexprs IS NULL
71
+ AND access_method.amname = 'btree'
72
+ AND ARRAY(
73
+ SELECT attribute.attname::text
74
+ FROM unnest(indexes.indkey::smallint[]) WITH ORDINALITY AS key(attnum, position)
75
+ JOIN pg_attribute AS attribute
76
+ ON attribute.attrelid = indexes.indrelid
77
+ AND attribute.attnum = key.attnum
78
+ ORDER BY key.position
79
+ ) = expected.columns
80
+ AND ARRAY(
81
+ SELECT (option & 1) = 1
82
+ FROM unnest(indexes.indoption::smallint[])
83
+ WITH ORDINALITY AS ordering(option, position)
84
+ ORDER BY ordering.position
85
+ ) = expected.descending
86
+ AND regexp_replace(
87
+ lower(COALESCE(pg_get_expr(indexes.indpred, indexes.indrelid), '')),
88
+ '[()[:space:]]', '', 'g'
89
+ ) = expected.predicate,
90
+ FALSE
91
+ )), FALSE)
92
+ FROM expected_indexes AS expected
93
+ LEFT JOIN pg_class AS index_class
94
+ ON index_class.oid = to_regclass(expected.index_name)
95
+ LEFT JOIN pg_index AS indexes ON indexes.indexrelid = index_class.oid
96
+ LEFT JOIN pg_am AS access_method ON access_method.oid = index_class.relam
97
+ )
98
+ AND EXISTS (
99
+ SELECT 1
100
+ FROM pg_constraint AS constraint_state
101
+ JOIN pg_index AS index_state ON index_state.indexrelid = constraint_state.conindid
102
+ JOIN pg_class AS index_class ON index_class.oid = index_state.indexrelid
103
+ JOIN pg_am AS access_method ON access_method.oid = index_class.relam
104
+ WHERE constraint_state.conrelid = to_regclass('bunqueue_group_state')
105
+ AND constraint_state.contype = 'p'
106
+ AND constraint_state.convalidated
107
+ AND NOT constraint_state.condeferrable
108
+ AND NOT constraint_state.condeferred
109
+ AND index_state.indisprimary AND index_state.indisunique
110
+ AND index_state.indisvalid AND index_state.indisready
111
+ AND index_state.indimmediate
112
+ AND index_state.indnkeyatts = 3 AND index_state.indnatts = 3
113
+ AND index_state.indexprs IS NULL AND index_state.indpred IS NULL
114
+ AND access_method.amname = 'btree'
115
+ AND ARRAY(
116
+ SELECT attribute.attname::text
117
+ FROM unnest(constraint_state.conkey)
118
+ WITH ORDINALITY AS key(attnum, position)
119
+ JOIN pg_attribute AS attribute
120
+ ON attribute.attrelid = constraint_state.conrelid
121
+ AND attribute.attnum = key.attnum
122
+ ORDER BY key.position
123
+ ) = ARRAY['namespace', 'queue', 'group_id']
124
+ ) AS valid
125
+ `;
126
+ return schema.valid;
127
+ }
@@ -0,0 +1,3 @@
1
+ import { type PostgresContext } from './context';
2
+ /** Reclaim bounded batches of inactive policyless group scheduler state. */
3
+ export declare function prunePostgresGroupStates(ctx: PostgresContext, batchSize?: number, maxBatches?: number): Promise<number>;
@@ -0,0 +1,93 @@
1
+ import { databaseNow } from './context';
2
+ async function pruneBatch(tx, ctx, batchSize) {
3
+ const now = await databaseNow(tx);
4
+ const rows = await tx `
5
+ SELECT groups.queue, groups.group_id
6
+ FROM bunqueue_group_state AS groups
7
+ WHERE groups.namespace = ${ctx.config.namespace}
8
+ AND NOT EXISTS (
9
+ SELECT 1 FROM bunqueue_jobs AS job
10
+ WHERE job.namespace = groups.namespace
11
+ AND job.queue = groups.queue
12
+ AND job.group_id = groups.group_id
13
+ AND job.state IN (
14
+ 'waiting', 'prioritized', 'delayed', 'waiting-children', 'active'
15
+ )
16
+ )
17
+ AND (
18
+ groups.last_served IS NOT NULL
19
+ OR (
20
+ groups.rate_limit IS NULL
21
+ AND groups.rate_duration_ms IS NULL
22
+ AND groups.concurrency_limit IS NULL
23
+ AND (
24
+ groups.rate_window_started_at IS NULL
25
+ OR (
26
+ groups.rate_effective_duration_ms IS NOT NULL
27
+ AND groups.rate_window_started_at + groups.rate_effective_duration_ms <= ${now}
28
+ )
29
+ )
30
+ )
31
+ )
32
+ ORDER BY groups.queue, groups.group_id
33
+ LIMIT ${batchSize}
34
+ FOR UPDATE OF groups SKIP LOCKED
35
+ `;
36
+ if (rows.length === 0)
37
+ return { deleted: 0, selected: 0 };
38
+ const queues = rows.map(({ queue }) => queue);
39
+ const groupIds = rows.map(({ group_id }) => group_id);
40
+ await tx `
41
+ UPDATE bunqueue_group_state AS groups
42
+ SET last_served = NULL
43
+ FROM unnest(
44
+ ${tx.array(queues, 'TEXT')},
45
+ ${tx.array(groupIds, 'TEXT')}
46
+ ) AS target(queue, group_id)
47
+ WHERE groups.namespace = ${ctx.config.namespace}
48
+ AND groups.queue = target.queue
49
+ AND groups.group_id = target.group_id
50
+ `;
51
+ const deleted = await tx `
52
+ DELETE FROM bunqueue_group_state AS groups
53
+ USING unnest(
54
+ ${tx.array(queues, 'TEXT')},
55
+ ${tx.array(groupIds, 'TEXT')}
56
+ ) AS target(queue, group_id)
57
+ WHERE groups.namespace = ${ctx.config.namespace}
58
+ AND groups.queue = target.queue
59
+ AND groups.group_id = target.group_id
60
+ AND groups.rate_limit IS NULL
61
+ AND groups.rate_duration_ms IS NULL
62
+ AND groups.concurrency_limit IS NULL
63
+ AND (
64
+ groups.rate_window_started_at IS NULL
65
+ OR (
66
+ groups.rate_effective_duration_ms IS NOT NULL
67
+ AND groups.rate_window_started_at + groups.rate_effective_duration_ms <= ${now}
68
+ )
69
+ )
70
+ AND NOT EXISTS (
71
+ SELECT 1 FROM bunqueue_jobs AS job
72
+ WHERE job.namespace = groups.namespace
73
+ AND job.queue = groups.queue
74
+ AND job.group_id = groups.group_id
75
+ AND job.state IN (
76
+ 'waiting', 'prioritized', 'delayed', 'waiting-children', 'active'
77
+ )
78
+ )
79
+ RETURNING groups.group_id
80
+ `;
81
+ return { deleted: deleted.length, selected: rows.length };
82
+ }
83
+ /** Reclaim bounded batches of inactive policyless group scheduler state. */
84
+ export async function prunePostgresGroupStates(ctx, batchSize = 1000, maxBatches = 10) {
85
+ let deleted = 0;
86
+ for (let batch = 0; batch < maxBatches; batch++) {
87
+ const result = await ctx.sql.begin((tx) => pruneBatch(tx, ctx, batchSize));
88
+ deleted += result.deleted;
89
+ if (result.selected < batchSize)
90
+ break;
91
+ }
92
+ return deleted;
93
+ }
@@ -0,0 +1,11 @@
1
+ import { type GroupRateLimitOverride } from '../../../domain/types/group';
2
+ import { type PostgresContext } from './context';
3
+ export declare function getPostgresGroupJobsCount(ctx: PostgresContext, queue: string, groupId?: string): Promise<number>;
4
+ export declare function getPostgresGroupActiveCount(ctx: PostgresContext, queue: string, groupId: string): Promise<number>;
5
+ export declare function setPostgresGroupRateLimit(ctx: PostgresContext, queue: string, groupId: string, max: number, duration: number): Promise<void>;
6
+ export declare function getPostgresGroupRateLimit(ctx: PostgresContext, queue: string, groupId: string): Promise<GroupRateLimitOverride | null>;
7
+ export declare function removePostgresGroupRateLimit(ctx: PostgresContext, queue: string, groupId: string): Promise<number>;
8
+ export declare function getPostgresGroupRateLimitTtl(ctx: PostgresContext, queue: string, groupId: string, maxJobs?: number): Promise<number>;
9
+ export declare function setPostgresGroupConcurrency(ctx: PostgresContext, queue: string, groupId: string, concurrency: number): Promise<void>;
10
+ export declare function getPostgresGroupConcurrency(ctx: PostgresContext, queue: string, groupId: string): Promise<number | null>;
11
+ export declare function removePostgresGroupConcurrency(ctx: PostgresContext, queue: string, groupId: string): Promise<number>;
@@ -0,0 +1,117 @@
1
+ import { assertGroupId, assertPositiveSafeInteger, } from '../../../domain/types/group';
2
+ import { databaseNow } from './context';
3
+ export async function getPostgresGroupJobsCount(ctx, queue, groupId) {
4
+ if (groupId !== undefined)
5
+ assertGroupId(groupId);
6
+ const [row] = groupId === undefined
7
+ ? await ctx.sql `
8
+ SELECT COUNT(*)::bigint AS count FROM bunqueue_jobs
9
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
10
+ AND group_id IS NOT NULL AND state IN ('waiting', 'prioritized', 'delayed')
11
+ `
12
+ : await ctx.sql `
13
+ SELECT COUNT(*)::bigint AS count FROM bunqueue_jobs
14
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
15
+ AND group_id = ${groupId} AND state IN ('waiting', 'prioritized', 'delayed')
16
+ `;
17
+ return Number(row.count);
18
+ }
19
+ export async function getPostgresGroupActiveCount(ctx, queue, groupId) {
20
+ assertGroupId(groupId);
21
+ const now = await databaseNow(ctx.sql);
22
+ const [row] = await ctx.sql `
23
+ SELECT COUNT(*)::bigint AS count FROM bunqueue_jobs
24
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue}
25
+ AND group_id = ${groupId} AND state = 'active' AND lease_until > ${now}
26
+ `;
27
+ return Number(row.count);
28
+ }
29
+ export async function setPostgresGroupRateLimit(ctx, queue, groupId, max, duration) {
30
+ assertGroupId(groupId);
31
+ assertPositiveSafeInteger(max, 'max');
32
+ assertPositiveSafeInteger(duration, 'duration');
33
+ await ctx.sql `
34
+ INSERT INTO bunqueue_group_state (
35
+ namespace, queue, group_id, rate_limit, rate_duration_ms
36
+ ) VALUES (${ctx.config.namespace}, ${queue}, ${groupId}, ${max}, ${duration})
37
+ ON CONFLICT (namespace, queue, group_id) DO UPDATE
38
+ SET rate_limit = EXCLUDED.rate_limit,
39
+ rate_duration_ms = EXCLUDED.rate_duration_ms,
40
+ rate_window_started_at = NULL,
41
+ rate_count = 0,
42
+ rate_effective_max = NULL,
43
+ rate_effective_duration_ms = NULL
44
+ `;
45
+ }
46
+ export async function getPostgresGroupRateLimit(ctx, queue, groupId) {
47
+ assertGroupId(groupId);
48
+ const rows = await ctx.sql `
49
+ SELECT rate_limit, rate_duration_ms, rate_window_started_at, rate_count,
50
+ rate_effective_duration_ms, concurrency_limit
51
+ FROM bunqueue_group_state
52
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
53
+ `;
54
+ const row = rows[0];
55
+ if (!row || row.rate_limit === null || row.rate_duration_ms === null)
56
+ return null;
57
+ return { max: Number(row.rate_limit), duration: Number(row.rate_duration_ms) };
58
+ }
59
+ export async function removePostgresGroupRateLimit(ctx, queue, groupId) {
60
+ assertGroupId(groupId);
61
+ const rows = await ctx.sql `
62
+ UPDATE bunqueue_group_state
63
+ SET rate_limit = NULL, rate_duration_ms = NULL, rate_window_started_at = NULL,
64
+ rate_count = 0, rate_effective_max = NULL, rate_effective_duration_ms = NULL
65
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
66
+ AND rate_limit IS NOT NULL
67
+ RETURNING group_id
68
+ `;
69
+ return rows.length;
70
+ }
71
+ export async function getPostgresGroupRateLimitTtl(ctx, queue, groupId, maxJobs) {
72
+ assertGroupId(groupId);
73
+ const rows = await ctx.sql `
74
+ SELECT rate_limit, rate_duration_ms, rate_window_started_at, rate_count,
75
+ rate_effective_duration_ms, concurrency_limit
76
+ FROM bunqueue_group_state
77
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
78
+ `;
79
+ const row = rows[0];
80
+ if (!row?.rate_window_started_at || row.rate_effective_duration_ms === null)
81
+ return -2;
82
+ if (maxJobs !== undefined && Number(row.rate_count) < maxJobs)
83
+ return 0;
84
+ const remaining = Number(row.rate_window_started_at) +
85
+ Number(row.rate_effective_duration_ms) -
86
+ (await databaseNow(ctx.sql));
87
+ return remaining > 0 ? remaining : -2;
88
+ }
89
+ export async function setPostgresGroupConcurrency(ctx, queue, groupId, concurrency) {
90
+ assertGroupId(groupId);
91
+ assertPositiveSafeInteger(concurrency, 'concurrency');
92
+ await ctx.sql `
93
+ INSERT INTO bunqueue_group_state (namespace, queue, group_id, concurrency_limit)
94
+ VALUES (${ctx.config.namespace}, ${queue}, ${groupId}, ${concurrency})
95
+ ON CONFLICT (namespace, queue, group_id) DO UPDATE
96
+ SET concurrency_limit = EXCLUDED.concurrency_limit
97
+ `;
98
+ }
99
+ export async function getPostgresGroupConcurrency(ctx, queue, groupId) {
100
+ assertGroupId(groupId);
101
+ const rows = await ctx.sql `
102
+ SELECT concurrency_limit FROM bunqueue_group_state
103
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
104
+ `;
105
+ const value = rows[0]?.concurrency_limit;
106
+ return value === undefined || value === null ? null : Number(value);
107
+ }
108
+ export async function removePostgresGroupConcurrency(ctx, queue, groupId) {
109
+ assertGroupId(groupId);
110
+ const rows = await ctx.sql `
111
+ UPDATE bunqueue_group_state SET concurrency_limit = NULL
112
+ WHERE namespace = ${ctx.config.namespace} AND queue = ${queue} AND group_id = ${groupId}
113
+ AND concurrency_limit IS NOT NULL
114
+ RETURNING group_id
115
+ `;
116
+ return rows.length;
117
+ }
@@ -0,0 +1,3 @@
1
+ import type { Job } from '../../../domain/types/job';
2
+ /** Merge user data while retaining the flow metadata owned by the broker. */
3
+ export declare function updatedPostgresJobData(job: Job, data: unknown): unknown;
@@ -0,0 +1,32 @@
1
+ const FLOW_METADATA_KEYS = [
2
+ '__parentId',
3
+ '__parentQueue',
4
+ '__childrenIds',
5
+ '__flowParentId',
6
+ '__flowParentIds',
7
+ ];
8
+ function dataRecord(value) {
9
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
10
+ ? value
11
+ : null;
12
+ }
13
+ /** Merge user data while retaining the flow metadata owned by the broker. */
14
+ export function updatedPostgresJobData(job, data) {
15
+ const current = dataRecord(job.data);
16
+ const metadata = current
17
+ ? Object.fromEntries(FLOW_METADATA_KEYS.filter((key) => Object.hasOwn(current, key)).map((key) => [
18
+ key,
19
+ current[key],
20
+ ]))
21
+ : {};
22
+ if (Object.keys(metadata).length === 0)
23
+ return data;
24
+ const next = dataRecord(data);
25
+ if (!next)
26
+ throw new Error('flow job data must be an object');
27
+ for (const key of Object.keys(next)) {
28
+ if (key.startsWith('__'))
29
+ throw new Error(`flow job data key is reserved: ${key}`);
30
+ }
31
+ return { ...next, ...metadata };
32
+ }