@adonis-agora/durable 0.49.1 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/CHANGELOG.md +279 -0
  2. package/README.md +53 -22
  3. package/dist/assets/spa/assets/index-DupjrK7N.js +16 -0
  4. package/dist/assets/spa/assets/index-DupjrK7N.js.map +1 -0
  5. package/dist/assets/spa/index.html +1 -1
  6. package/dist/commands/durable_worker.d.ts.map +1 -1
  7. package/dist/commands/durable_worker.js +9 -8
  8. package/dist/commands/durable_worker.js.map +1 -1
  9. package/dist/commands/work.d.ts.map +1 -1
  10. package/dist/commands/work.js +3 -1
  11. package/dist/commands/work.js.map +1 -1
  12. package/dist/configure.d.ts.map +1 -1
  13. package/dist/configure.js +1 -0
  14. package/dist/configure.js.map +1 -1
  15. package/dist/providers/dashboard_provider.d.ts +2 -0
  16. package/dist/providers/dashboard_provider.d.ts.map +1 -1
  17. package/dist/providers/dashboard_provider.js +50 -6
  18. package/dist/providers/dashboard_provider.js.map +1 -1
  19. package/dist/providers/durable_provider.d.ts.map +1 -1
  20. package/dist/providers/durable_provider.js +146 -35
  21. package/dist/providers/durable_provider.js.map +1 -1
  22. package/dist/src/admission-redis/redis-admission-backend.d.ts.map +1 -1
  23. package/dist/src/admission-redis/redis-admission-backend.js +3 -1
  24. package/dist/src/admission-redis/redis-admission-backend.js.map +1 -1
  25. package/dist/src/ambient-step.d.ts +24 -0
  26. package/dist/src/ambient-step.d.ts.map +1 -0
  27. package/dist/src/ambient-step.js +42 -0
  28. package/dist/src/ambient-step.js.map +1 -0
  29. package/dist/src/base-workflow.d.ts +10 -1
  30. package/dist/src/base-workflow.d.ts.map +1 -1
  31. package/dist/src/base-workflow.js.map +1 -1
  32. package/dist/src/codec-state-store.d.ts +28 -2
  33. package/dist/src/codec-state-store.d.ts.map +1 -1
  34. package/dist/src/codec-state-store.js +61 -3
  35. package/dist/src/codec-state-store.js.map +1 -1
  36. package/dist/src/commands/worker.d.ts +8 -3
  37. package/dist/src/commands/worker.d.ts.map +1 -1
  38. package/dist/src/commands/worker.js +35 -8
  39. package/dist/src/commands/worker.js.map +1 -1
  40. package/dist/src/config_types.d.ts +6 -0
  41. package/dist/src/config_types.d.ts.map +1 -1
  42. package/dist/src/control-plane-redis/redis-control-plane.d.ts.map +1 -1
  43. package/dist/src/control-plane-redis/redis-control-plane.js +22 -6
  44. package/dist/src/control-plane-redis/redis-control-plane.js.map +1 -1
  45. package/dist/src/control-planes/factory.d.ts +6 -0
  46. package/dist/src/control-planes/factory.d.ts.map +1 -1
  47. package/dist/src/control-planes/factory.js +1 -0
  48. package/dist/src/control-planes/factory.js.map +1 -1
  49. package/dist/src/dashboard/gateway-adapter.d.ts +15 -5
  50. package/dist/src/dashboard/gateway-adapter.d.ts.map +1 -1
  51. package/dist/src/dashboard/gateway-adapter.js +71 -7
  52. package/dist/src/dashboard/gateway-adapter.js.map +1 -1
  53. package/dist/src/dashboard/handlers.d.ts +53 -1
  54. package/dist/src/dashboard/handlers.d.ts.map +1 -1
  55. package/dist/src/dashboard/handlers.js +200 -20
  56. package/dist/src/dashboard/handlers.js.map +1 -1
  57. package/dist/src/dashboard/index.d.ts +2 -2
  58. package/dist/src/dashboard/index.d.ts.map +1 -1
  59. package/dist/src/dashboard/index.js +2 -2
  60. package/dist/src/dashboard/index.js.map +1 -1
  61. package/dist/src/dashboard/openapi.d.ts.map +1 -1
  62. package/dist/src/dashboard/openapi.js +71 -1
  63. package/dist/src/dashboard/openapi.js.map +1 -1
  64. package/dist/src/define_config.d.ts +122 -8
  65. package/dist/src/define_config.d.ts.map +1 -1
  66. package/dist/src/define_config.js.map +1 -1
  67. package/dist/src/diagnostics-bridge.d.ts +16 -3
  68. package/dist/src/diagnostics-bridge.d.ts.map +1 -1
  69. package/dist/src/diagnostics-bridge.js +9 -3
  70. package/dist/src/diagnostics-bridge.js.map +1 -1
  71. package/dist/src/discovery-roots.d.ts +92 -0
  72. package/dist/src/discovery-roots.d.ts.map +1 -0
  73. package/dist/src/discovery-roots.js +150 -0
  74. package/dist/src/discovery-roots.js.map +1 -0
  75. package/dist/src/engine.d.ts +428 -21
  76. package/dist/src/engine.d.ts.map +1 -1
  77. package/dist/src/engine.js +1566 -164
  78. package/dist/src/engine.js.map +1 -1
  79. package/dist/src/entities.d.ts +36 -1
  80. package/dist/src/entities.d.ts.map +1 -1
  81. package/dist/src/entities.js +65 -4
  82. package/dist/src/entities.js.map +1 -1
  83. package/dist/src/errors.d.ts +46 -0
  84. package/dist/src/errors.d.ts.map +1 -1
  85. package/dist/src/errors.js +48 -0
  86. package/dist/src/errors.js.map +1 -1
  87. package/dist/src/event-trigger-bridge.d.ts +8 -0
  88. package/dist/src/event-trigger-bridge.d.ts.map +1 -1
  89. package/dist/src/event-trigger-bridge.js +24 -3
  90. package/dist/src/event-trigger-bridge.js.map +1 -1
  91. package/dist/src/handshake/announced.d.ts +27 -0
  92. package/dist/src/handshake/announced.d.ts.map +1 -1
  93. package/dist/src/handshake/announced.js +62 -0
  94. package/dist/src/handshake/announced.js.map +1 -1
  95. package/dist/src/handshake/directory.d.ts +92 -0
  96. package/dist/src/handshake/directory.d.ts.map +1 -0
  97. package/dist/src/handshake/directory.js +139 -0
  98. package/dist/src/handshake/directory.js.map +1 -0
  99. package/dist/src/hooks/barrel-sources.d.ts +28 -0
  100. package/dist/src/hooks/barrel-sources.d.ts.map +1 -0
  101. package/dist/src/hooks/barrel-sources.js +92 -0
  102. package/dist/src/hooks/barrel-sources.js.map +1 -0
  103. package/dist/src/hooks/steps.d.ts +12 -3
  104. package/dist/src/hooks/steps.d.ts.map +1 -1
  105. package/dist/src/hooks/steps.js +7 -11
  106. package/dist/src/hooks/steps.js.map +1 -1
  107. package/dist/src/hooks/workflows.d.ts +12 -3
  108. package/dist/src/hooks/workflows.d.ts.map +1 -1
  109. package/dist/src/hooks/workflows.js +7 -13
  110. package/dist/src/hooks/workflows.js.map +1 -1
  111. package/dist/src/index.d.ts +12 -3
  112. package/dist/src/index.d.ts.map +1 -1
  113. package/dist/src/index.js +12 -3
  114. package/dist/src/index.js.map +1 -1
  115. package/dist/src/interfaces.d.ts +309 -21
  116. package/dist/src/interfaces.d.ts.map +1 -1
  117. package/dist/src/interfaces.js +10 -0
  118. package/dist/src/interfaces.js.map +1 -1
  119. package/dist/src/metrics.d.ts.map +1 -1
  120. package/dist/src/metrics.js +5 -0
  121. package/dist/src/metrics.js.map +1 -1
  122. package/dist/src/namespaces.d.ts +17 -0
  123. package/dist/src/namespaces.d.ts.map +1 -0
  124. package/dist/src/namespaces.js +24 -0
  125. package/dist/src/namespaces.js.map +1 -0
  126. package/dist/src/on-error.d.ts +4 -1
  127. package/dist/src/on-error.d.ts.map +1 -1
  128. package/dist/src/on-error.js +15 -2
  129. package/dist/src/on-error.js.map +1 -1
  130. package/dist/src/otel/durable-metrics.d.ts.map +1 -1
  131. package/dist/src/otel/durable-metrics.js +5 -0
  132. package/dist/src/otel/durable-metrics.js.map +1 -1
  133. package/dist/src/otel/durable-otel.d.ts.map +1 -1
  134. package/dist/src/otel/durable-otel.js +5 -0
  135. package/dist/src/otel/durable-otel.js.map +1 -1
  136. package/dist/src/pollers.d.ts +10 -1
  137. package/dist/src/pollers.d.ts.map +1 -1
  138. package/dist/src/pollers.js +15 -5
  139. package/dist/src/pollers.js.map +1 -1
  140. package/dist/src/protocol.d.ts +12 -0
  141. package/dist/src/protocol.d.ts.map +1 -1
  142. package/dist/src/protocol.js +27 -1
  143. package/dist/src/protocol.js.map +1 -1
  144. package/dist/src/remote-event.d.ts +6 -0
  145. package/dist/src/remote-event.d.ts.map +1 -0
  146. package/dist/src/remote-event.js +19 -0
  147. package/dist/src/remote-event.js.map +1 -0
  148. package/dist/src/remote-workflow-executor.d.ts +34 -12
  149. package/dist/src/remote-workflow-executor.d.ts.map +1 -1
  150. package/dist/src/remote-workflow-executor.js +42 -40
  151. package/dist/src/remote-workflow-executor.js.map +1 -1
  152. package/dist/src/run-detail-summary.d.ts +90 -3
  153. package/dist/src/run-detail-summary.d.ts.map +1 -1
  154. package/dist/src/run-detail-summary.js +360 -2
  155. package/dist/src/run-detail-summary.js.map +1 -1
  156. package/dist/src/run-facets.d.ts +48 -0
  157. package/dist/src/run-facets.d.ts.map +1 -0
  158. package/dist/src/run-facets.js +100 -0
  159. package/dist/src/run-facets.js.map +1 -0
  160. package/dist/src/run-gateway/interface.d.ts +27 -1
  161. package/dist/src/run-gateway/interface.d.ts.map +1 -1
  162. package/dist/src/run-gateway/proxy-run-gateway.d.ts +32 -4
  163. package/dist/src/run-gateway/proxy-run-gateway.d.ts.map +1 -1
  164. package/dist/src/run-gateway/proxy-run-gateway.js +101 -5
  165. package/dist/src/run-gateway/proxy-run-gateway.js.map +1 -1
  166. package/dist/src/run-gateway/run-request-responder.d.ts +18 -5
  167. package/dist/src/run-gateway/run-request-responder.d.ts.map +1 -1
  168. package/dist/src/run-gateway/run-request-responder.js +155 -32
  169. package/dist/src/run-gateway/run-request-responder.js.map +1 -1
  170. package/dist/src/run-gateway/store-run-gateway.d.ts +23 -1
  171. package/dist/src/run-gateway/store-run-gateway.d.ts.map +1 -1
  172. package/dist/src/run-gateway/store-run-gateway.js +38 -0
  173. package/dist/src/run-gateway/store-run-gateway.js.map +1 -1
  174. package/dist/src/scheduler.d.ts +44 -3
  175. package/dist/src/scheduler.d.ts.map +1 -1
  176. package/dist/src/scheduler.js +36 -1
  177. package/dist/src/scheduler.js.map +1 -1
  178. package/dist/src/search-attributes-schema.d.ts +44 -0
  179. package/dist/src/search-attributes-schema.d.ts.map +1 -0
  180. package/dist/src/search-attributes-schema.js +51 -0
  181. package/dist/src/search-attributes-schema.js.map +1 -0
  182. package/dist/src/singleton-admission.d.ts +75 -0
  183. package/dist/src/singleton-admission.d.ts.map +1 -0
  184. package/dist/src/singleton-admission.js +156 -0
  185. package/dist/src/singleton-admission.js.map +1 -0
  186. package/dist/src/singleton-gate.d.ts +23 -8
  187. package/dist/src/singleton-gate.d.ts.map +1 -1
  188. package/dist/src/singleton-gate.js +59 -12
  189. package/dist/src/singleton-gate.js.map +1 -1
  190. package/dist/src/standard-schema.d.ts +86 -0
  191. package/dist/src/standard-schema.d.ts.map +1 -0
  192. package/dist/src/standard-schema.js +33 -0
  193. package/dist/src/standard-schema.js.map +1 -0
  194. package/dist/src/step-discovery.d.ts +21 -11
  195. package/dist/src/step-discovery.d.ts.map +1 -1
  196. package/dist/src/step-discovery.js +86 -45
  197. package/dist/src/step-discovery.js.map +1 -1
  198. package/dist/src/step-ref.d.ts +38 -12
  199. package/dist/src/step-ref.d.ts.map +1 -1
  200. package/dist/src/step-ref.js +5 -13
  201. package/dist/src/step-ref.js.map +1 -1
  202. package/dist/src/stores/factory.d.ts +6 -0
  203. package/dist/src/stores/factory.d.ts.map +1 -1
  204. package/dist/src/stores/factory.js +4 -1
  205. package/dist/src/stores/factory.js.map +1 -1
  206. package/dist/src/stores/lucid-helpers.d.ts +2 -0
  207. package/dist/src/stores/lucid-helpers.d.ts.map +1 -1
  208. package/dist/src/stores/lucid-helpers.js +7 -0
  209. package/dist/src/stores/lucid-helpers.js.map +1 -1
  210. package/dist/src/stores/lucid-mappers.d.ts +25 -0
  211. package/dist/src/stores/lucid-mappers.d.ts.map +1 -1
  212. package/dist/src/stores/lucid-mappers.js +81 -18
  213. package/dist/src/stores/lucid-mappers.js.map +1 -1
  214. package/dist/src/stores/lucid-schema.d.ts +59 -1
  215. package/dist/src/stores/lucid-schema.d.ts.map +1 -1
  216. package/dist/src/stores/lucid-schema.js +271 -25
  217. package/dist/src/stores/lucid-schema.js.map +1 -1
  218. package/dist/src/stores/lucid.d.ts +61 -2
  219. package/dist/src/stores/lucid.d.ts.map +1 -1
  220. package/dist/src/stores/lucid.js +346 -41
  221. package/dist/src/stores/lucid.js.map +1 -1
  222. package/dist/src/telescope/dashboard.d.ts.map +1 -1
  223. package/dist/src/telescope/dashboard.js +30 -1
  224. package/dist/src/telescope/dashboard.js.map +1 -1
  225. package/dist/src/telescope/data-providers.d.ts +19 -1
  226. package/dist/src/telescope/data-providers.d.ts.map +1 -1
  227. package/dist/src/telescope/data-providers.js +119 -3
  228. package/dist/src/telescope/data-providers.js.map +1 -1
  229. package/dist/src/telescope/extension.d.ts.map +1 -1
  230. package/dist/src/telescope/extension.js +2 -1
  231. package/dist/src/telescope/extension.js.map +1 -1
  232. package/dist/src/telescope/index.d.ts +1 -1
  233. package/dist/src/telescope/index.d.ts.map +1 -1
  234. package/dist/src/telescope/index.js +1 -1
  235. package/dist/src/telescope/index.js.map +1 -1
  236. package/dist/src/testing/in-memory-state-store.d.ts +44 -15
  237. package/dist/src/testing/in-memory-state-store.d.ts.map +1 -1
  238. package/dist/src/testing/in-memory-state-store.js +204 -41
  239. package/dist/src/testing/in-memory-state-store.js.map +1 -1
  240. package/dist/src/testing-kit/checkpoint-aggregation-contract.d.ts +16 -0
  241. package/dist/src/testing-kit/checkpoint-aggregation-contract.d.ts.map +1 -0
  242. package/dist/src/testing-kit/checkpoint-aggregation-contract.js +228 -0
  243. package/dist/src/testing-kit/checkpoint-aggregation-contract.js.map +1 -0
  244. package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
  245. package/dist/src/testing-kit/state-store-conformance.js +355 -3
  246. package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
  247. package/dist/src/transport-pool.d.ts +15 -1
  248. package/dist/src/transport-pool.d.ts.map +1 -1
  249. package/dist/src/transport-pool.js +50 -4
  250. package/dist/src/transport-pool.js.map +1 -1
  251. package/dist/src/transports/adaptive-concurrency.d.ts +168 -0
  252. package/dist/src/transports/adaptive-concurrency.d.ts.map +1 -0
  253. package/dist/src/transports/adaptive-concurrency.js +353 -0
  254. package/dist/src/transports/adaptive-concurrency.js.map +1 -0
  255. package/dist/src/transports/bullmq/adaptive-concurrency.d.ts +1 -165
  256. package/dist/src/transports/bullmq/adaptive-concurrency.d.ts.map +1 -1
  257. package/dist/src/transports/bullmq/adaptive-concurrency.js +3 -351
  258. package/dist/src/transports/bullmq/adaptive-concurrency.js.map +1 -1
  259. package/dist/src/transports/bullmq/bullmq-transport.d.ts +49 -1
  260. package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -1
  261. package/dist/src/transports/bullmq/bullmq-transport.js +330 -38
  262. package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -1
  263. package/dist/src/transports/bullmq/deps.d.ts +18 -2
  264. package/dist/src/transports/bullmq/deps.d.ts.map +1 -1
  265. package/dist/src/transports/bullmq/deps.js +44 -2
  266. package/dist/src/transports/bullmq/deps.js.map +1 -1
  267. package/dist/src/transports/db-helpers.d.ts +39 -0
  268. package/dist/src/transports/db-helpers.d.ts.map +1 -1
  269. package/dist/src/transports/db-helpers.js +73 -0
  270. package/dist/src/transports/db-helpers.js.map +1 -1
  271. package/dist/src/transports/db-remote.d.ts +53 -0
  272. package/dist/src/transports/db-remote.d.ts.map +1 -0
  273. package/dist/src/transports/db-remote.js +147 -0
  274. package/dist/src/transports/db-remote.js.map +1 -0
  275. package/dist/src/transports/db-schema.d.ts +44 -1
  276. package/dist/src/transports/db-schema.d.ts.map +1 -1
  277. package/dist/src/transports/db-schema.js +227 -88
  278. package/dist/src/transports/db-schema.js.map +1 -1
  279. package/dist/src/transports/db.d.ts +189 -6
  280. package/dist/src/transports/db.d.ts.map +1 -1
  281. package/dist/src/transports/db.js +1291 -137
  282. package/dist/src/transports/db.js.map +1 -1
  283. package/dist/src/transports/event-emitter.d.ts +8 -0
  284. package/dist/src/transports/event-emitter.d.ts.map +1 -1
  285. package/dist/src/transports/event-emitter.js +36 -3
  286. package/dist/src/transports/event-emitter.js.map +1 -1
  287. package/dist/src/transports/factory.d.ts +110 -5
  288. package/dist/src/transports/factory.d.ts.map +1 -1
  289. package/dist/src/transports/factory.js +56 -9
  290. package/dist/src/transports/factory.js.map +1 -1
  291. package/dist/src/transports/queue-mock-adapter.d.ts +7 -2
  292. package/dist/src/transports/queue-mock-adapter.d.ts.map +1 -1
  293. package/dist/src/transports/queue-mock-adapter.js +18 -2
  294. package/dist/src/transports/queue-mock-adapter.js.map +1 -1
  295. package/dist/src/transports/queue-remote.d.ts +146 -0
  296. package/dist/src/transports/queue-remote.d.ts.map +1 -0
  297. package/dist/src/transports/queue-remote.js +398 -0
  298. package/dist/src/transports/queue-remote.js.map +1 -0
  299. package/dist/src/transports/queue.d.ts +159 -6
  300. package/dist/src/transports/queue.d.ts.map +1 -1
  301. package/dist/src/transports/queue.js +747 -37
  302. package/dist/src/transports/queue.js.map +1 -1
  303. package/dist/src/transports/task-failure.d.ts +31 -0
  304. package/dist/src/transports/task-failure.d.ts.map +1 -0
  305. package/dist/src/transports/task-failure.js +50 -0
  306. package/dist/src/transports/task-failure.js.map +1 -0
  307. package/dist/src/workflow-als.d.ts +1 -1
  308. package/dist/src/workflow-als.d.ts.map +1 -1
  309. package/dist/src/workflow-ctx.d.ts +10 -0
  310. package/dist/src/workflow-ctx.d.ts.map +1 -1
  311. package/dist/src/workflow-ctx.js +97 -15
  312. package/dist/src/workflow-ctx.js.map +1 -1
  313. package/dist/src/workflow-discovery.d.ts +32 -41
  314. package/dist/src/workflow-discovery.d.ts.map +1 -1
  315. package/dist/src/workflow-discovery.js +65 -88
  316. package/dist/src/workflow-discovery.js.map +1 -1
  317. package/dist/src/workflow-ref.d.ts +26 -0
  318. package/dist/src/workflow-ref.d.ts.map +1 -1
  319. package/dist/src/workflow-ref.js.map +1 -1
  320. package/dist/stubs/config/durable.stub +6 -0
  321. package/dist/stubs/config/durable_dashboard.stub +7 -3
  322. package/dist/stubs/database/migrations/create_durable_tables.stub +2 -1
  323. package/dist/stubs/database/migrations/create_durable_transport_messages.stub +43 -0
  324. package/dist/stubs/database/migrations/create_durable_transport_tables.stub +12 -9
  325. package/dist/stubs/database/migrations/create_durable_transport_worker_keys.stub +1 -1
  326. package/dist/stubs/make/workflow/main.stub +1 -1
  327. package/package.json +6 -4
  328. package/skills/durable-cluster/SKILL.md +80 -11
  329. package/skills/durable-determinism/SKILL.md +11 -5
  330. package/skills/durable-reliability/SKILL.md +77 -37
  331. package/skills/durable-setup/SKILL.md +51 -22
  332. package/skills/durable-transports-stores/SKILL.md +128 -18
  333. package/skills/durable-workflows/SKILL.md +83 -18
  334. package/dist/assets/spa/assets/index-DdB9zxuX.js +0 -16
  335. package/dist/assets/spa/assets/index-DdB9zxuX.js.map +0 -1
@@ -0,0 +1,398 @@
1
+ import { cachedLiveKeys, NO_WORKER_KEYS } from './worker-keys.js';
2
+ /**
3
+ * Naming and bookkeeping for the parts of the store-less (P4) protocol that BullMQ carries on Redis
4
+ * PUB/SUB and that a point-to-point `@adonisjs/queue` adapter cannot broadcast: run replies and tenant
5
+ * events. {@link import('./queue.js').QueueTransport} delivers both through per-instance INBOX queues:
6
+ *
7
+ * - **Run replies** — every requesting instance consumes its own `${P}:run-replies:<instance>`. The
8
+ * requester stamps its instance id (`replyTo`) on the run-request / start-run job it pushes; the
9
+ * control-plane instance that pops the job remembers `correlationId → replyTo` while the handler runs,
10
+ * so the responder's `publishRunReply` lands on exactly the requester's inbox. Nothing is broadcast.
11
+ * - **Tenant events** — every instance subscribed to a tenant sends an {@link InboxRegistration} onto
12
+ * `${P}:inbox-registrations` when it subscribes and every third of the TTL after that. Whichever
13
+ * control-plane instance pops it records a TTL'd `${P}-tenant-subscriber:<tenant>:<instance>` key in
14
+ * the CONTROL PLANE's {@link WorkerKeyStore} (the one its worker descriptors live in, shared by its
15
+ * replicas). A publisher reads the live subscribers of the event's tenant from that store and pushes
16
+ * one copy onto each one's `${P}:tenant-events:<instance>` inbox. The subscriber itself needs no store,
17
+ * which is what lets a store-less tenant pod live-tail.
18
+ *
19
+ * The same registration keeps a TTL'd `${P}-inbox:<instance>` liveness key for every instance that
20
+ * consumes an inbox, which the publisher-side janitor uses to drain the inbox of an instance that died
21
+ * (see {@link InboxRegistry}).
22
+ *
23
+ * A worker with no key store of its own (a store-less tenant pod) advertises its descriptor and
24
+ * heartbeat keys the same way: as {@link KeyAdvertisement}s the control plane records — restricted to
25
+ * the `-worker-descriptor:` / `-worker-heartbeat:` keyspace — so the control plane still sees it.
26
+ *
27
+ * Segments taken from the wire or from user config (tenant, instance id) are URI-encoded inside KEYS, so
28
+ * neither can contain the `:` separator and one tenant's key prefix can never match another's.
29
+ */
30
+ /** Default TTL (ms) of the subscriber and inbox liveness keys; re-announced every third of it. */
31
+ export const DEFAULT_INBOX_TTL_MS = 30_000;
32
+ /** Default reuse window (ms) of the publisher's read of the live tenant subscribers. Short on purpose:
33
+ * a subscriber that just registered misses the events published inside this window. */
34
+ export const DEFAULT_TENANT_SUBSCRIBERS_CACHE_MS = 1_000;
35
+ /** Default cadence (ms) of the janitor that drains the inboxes of dead instances. */
36
+ export const DEFAULT_INBOX_SWEEP_INTERVAL_MS = 30_000;
37
+ /** How many jobs one janitor round pops from one dead inbox (the next round continues). */
38
+ const DRAIN_PER_SWEEP = 1_000;
39
+ /** Bounds on what a registration read off the wire may ask for. */
40
+ const MAX_REGISTRATION_TTL_MS = 10 * 60_000;
41
+ const MAX_REGISTRATION_TENANTS = 1_000;
42
+ const enc = (segment) => encodeURIComponent(segment);
43
+ /** `${P}:run-replies:<instance>` — the inbox a requesting instance consumes its {@link RunReply}s from. */
44
+ export function runRepliesQueue(prefix, instanceId) {
45
+ return `${prefix}:run-replies:${instanceId}`;
46
+ }
47
+ /** `${P}:tenant-events:<instance>` — the inbox a subscribing instance consumes its tenant events from. */
48
+ export function tenantEventsQueue(prefix, instanceId) {
49
+ return `${prefix}:tenant-events:${instanceId}`;
50
+ }
51
+ /** `${P}:inbox-registrations` — where instances announce their inboxes and subscriptions. */
52
+ export function inboxRegistrationsQueue(prefix) {
53
+ return `${prefix}:inbox-registrations`;
54
+ }
55
+ /** The key prefix of every live subscriber of `tenant`. */
56
+ export function tenantSubscriberKeyPrefix(prefix, tenant) {
57
+ return `${prefix}-tenant-subscriber:${enc(tenant)}:`;
58
+ }
59
+ /** `${P}-tenant-subscriber:<tenant>:<instance>` — "this instance wants `tenant`'s events". */
60
+ export function tenantSubscriberKey(prefix, tenant, instanceId) {
61
+ return `${tenantSubscriberKeyPrefix(prefix, tenant)}${enc(instanceId)}`;
62
+ }
63
+ /** `${P}-inbox:<instance>` — "this instance is alive and consuming its inboxes". */
64
+ export function inboxKey(prefix, instanceId) {
65
+ return `${prefix}-inbox:${enc(instanceId)}`;
66
+ }
67
+ function ownerOf(value) {
68
+ try {
69
+ const parsed = JSON.parse(value);
70
+ return typeof parsed.instanceId === 'string' ? parsed.instanceId : undefined;
71
+ }
72
+ catch {
73
+ return undefined;
74
+ }
75
+ }
76
+ const isStringArray = (v) => Array.isArray(v) && v.length <= MAX_REGISTRATION_TENANTS && v.every((t) => typeof t === 'string');
77
+ /** Narrow a key advertisement read off the wire; anything malformed is `undefined` (ignored). */
78
+ export function parseAdvertisement(raw) {
79
+ if (typeof raw !== 'object' || raw === null)
80
+ return undefined;
81
+ const r = raw;
82
+ if (r.kind !== 'advertise' || !Array.isArray(r.put) || !isStringArray(r.remove))
83
+ return undefined;
84
+ if (r.put.length > MAX_REGISTRATION_TENANTS)
85
+ return undefined;
86
+ const put = [];
87
+ for (const entry of r.put) {
88
+ const e = entry;
89
+ if (!e ||
90
+ typeof e.key !== 'string' ||
91
+ typeof e.value !== 'string' ||
92
+ typeof e.ttlMs !== 'number' ||
93
+ !Number.isFinite(e.ttlMs) ||
94
+ e.ttlMs <= 0) {
95
+ return undefined;
96
+ }
97
+ put.push({ key: e.key, value: e.value, ttlMs: Math.min(e.ttlMs, MAX_REGISTRATION_TTL_MS) });
98
+ }
99
+ return { kind: 'advertise', put, remove: r.remove };
100
+ }
101
+ /** Narrow a registration read off the wire; anything malformed is `undefined` (ignored). */
102
+ export function parseRegistration(raw) {
103
+ if (typeof raw !== 'object' || raw === null)
104
+ return undefined;
105
+ const r = raw;
106
+ if (typeof r.instanceId !== 'string' || r.instanceId === '')
107
+ return undefined;
108
+ if (typeof r.ttlMs !== 'number' || !Number.isFinite(r.ttlMs) || r.ttlMs <= 0)
109
+ return undefined;
110
+ if (typeof r.inbox !== 'boolean' || !isStringArray(r.tenants))
111
+ return undefined;
112
+ if (r.withdraw !== undefined && !isStringArray(r.withdraw))
113
+ return undefined;
114
+ return {
115
+ instanceId: r.instanceId,
116
+ ttlMs: Math.min(r.ttlMs, MAX_REGISTRATION_TTL_MS),
117
+ inbox: r.inbox,
118
+ tenants: r.tenants,
119
+ withdraw: r.withdraw,
120
+ };
121
+ }
122
+ /**
123
+ * The registry side of the inbox design, in three parts:
124
+ *
125
+ * - **subscriber** — what this instance announces (its subscriptions and its inbox liveness), re-sent
126
+ * every third of the TTL while it lasts;
127
+ * - **recorder** (control plane) — turns announcements into TTL'd keys in its worker-key store;
128
+ * - **publisher** (control plane) — reads a tenant's live subscribers, and runs the janitor that drains
129
+ * inboxes it pushed to whose owner stopped announcing itself.
130
+ *
131
+ * Dead instances: a subscriber that dies stops announcing, so within the TTL its keys expire and
132
+ * publishers stop pushing to it — its inbox stops growing. What was pushed meanwhile is drained by the
133
+ * janitor of each publisher that pushed it (tracked in memory): once the owner's `-inbox:` key is gone
134
+ * and the last push is older than the TTL, the publisher pops and completes what is left. A publisher
135
+ * that restarts forgets what it tracked, so a crash of BOTH sides can leave at most a TTL's worth of
136
+ * jobs behind.
137
+ *
138
+ * Without a real key store on the control plane ({@link NO_WORKER_KEYS}) nothing can be recorded or
139
+ * read: tenant events reach no one, and the janitor stays off (it could not tell a live inbox from a
140
+ * dead one).
141
+ */
142
+ export class InboxRegistry {
143
+ #options;
144
+ #now;
145
+ #enabled;
146
+ #subscribers;
147
+ /** Tenants this instance currently subscribes to. */
148
+ #tenants = new Set();
149
+ /** Whether this instance consumes an inbox (so it keeps announcing its liveness). */
150
+ #inboxActive = false;
151
+ #refreshTimer;
152
+ /** Inboxes this instance pushed to: queue → its owner and the time of the last push. */
153
+ #pushed = new Map();
154
+ #sweepTimer;
155
+ #sweeping;
156
+ #warned = false;
157
+ #warnedAdvertisement = false;
158
+ constructor(options) {
159
+ this.#options = options;
160
+ this.#now = options.now ?? Date.now;
161
+ this.#enabled = options.store !== NO_WORKER_KEYS;
162
+ this.#subscribers = cachedLiveKeys(options.store, options.subscribersCacheMs, this.#now);
163
+ }
164
+ // ---------------------------------------------------------------------------
165
+ // subscriber
166
+ // ---------------------------------------------------------------------------
167
+ #announce(withdraw) {
168
+ void this.#options
169
+ .announce({
170
+ instanceId: this.#options.instanceId,
171
+ ttlMs: this.#options.ttlMs,
172
+ inbox: this.#inboxActive,
173
+ tenants: [...this.#tenants],
174
+ ...(withdraw ? { withdraw } : {}),
175
+ })
176
+ .catch((err) => this.#options.onError(err));
177
+ }
178
+ /** Mark this instance as an inbox consumer (announced now and on every refresh). */
179
+ activateInbox() {
180
+ if (this.#inboxActive)
181
+ return;
182
+ this.#inboxActive = true;
183
+ this.#announce();
184
+ this.#ensureRefresh();
185
+ }
186
+ /** Subscribe to `tenant`'s events (idempotent). */
187
+ subscribe(tenant) {
188
+ if (this.#tenants.has(tenant))
189
+ return;
190
+ this.#tenants.add(tenant);
191
+ this.#inboxActive = true;
192
+ this.#announce();
193
+ this.#ensureRefresh();
194
+ }
195
+ /** End the subscription to `tenant` (its last handler went away). */
196
+ unsubscribe(tenant) {
197
+ if (!this.#tenants.delete(tenant))
198
+ return;
199
+ this.#announce([tenant]);
200
+ }
201
+ #ensureRefresh() {
202
+ if (this.#refreshTimer)
203
+ return;
204
+ this.#refreshTimer = setInterval(() => this.#announce(), Math.max(Math.floor(this.#options.ttlMs / 3), 1));
205
+ this.#refreshTimer.unref?.();
206
+ }
207
+ /**
208
+ * A {@link DescriptorRedis} that forwards a worker's descriptor / heartbeat writes to the control
209
+ * plane instead of a local store. Writes made in the same tick go out as ONE message (a worker beats
210
+ * every token at once). A failed send is reported, never thrown into the worker's heartbeat.
211
+ */
212
+ remoteKeyWriter() {
213
+ let batch;
214
+ const enqueue = (mutate) => {
215
+ if (!batch) {
216
+ const pending = { kind: 'advertise', put: [], remove: [] };
217
+ batch = pending;
218
+ mutate(pending);
219
+ return Promise.resolve().then(() => {
220
+ batch = undefined;
221
+ return this.#options.announce(pending).catch((err) => this.#options.onError(err));
222
+ });
223
+ }
224
+ mutate(batch);
225
+ return Promise.resolve();
226
+ };
227
+ return {
228
+ set: (key, value, _mode, ttlSeconds) => enqueue((b) => b.put.push({ key, value, ttlMs: ttlSeconds * 1000 })),
229
+ del: (...keys) => enqueue((b) => b.remove.push(...keys)),
230
+ };
231
+ }
232
+ // ---------------------------------------------------------------------------
233
+ // recorder (control plane)
234
+ // ---------------------------------------------------------------------------
235
+ /** Record a message read off `${P}:inbox-registrations` as TTL'd keys in the store. */
236
+ async record(raw) {
237
+ const advertisement = parseAdvertisement(raw);
238
+ if (advertisement)
239
+ return this.#recordAdvertisement(advertisement);
240
+ const registration = parseRegistration(raw);
241
+ if (!registration)
242
+ return; // malformed: nothing safe to record
243
+ if (!this.#enabled) {
244
+ if (!this.#warned) {
245
+ this.#warned = true;
246
+ this.#options.onError(new Error('[QueueTransport] received a tenant-event subscription but has no worker-key store to ' +
247
+ 'record it in (transports.queue() defaults to the app database); tenant events cannot ' +
248
+ 'reach any subscriber.'));
249
+ }
250
+ return;
251
+ }
252
+ const prefix = this.#options.prefix();
253
+ const { instanceId, ttlMs } = registration;
254
+ const value = JSON.stringify({ instanceId });
255
+ const expiresAt = this.#now() + ttlMs;
256
+ const removed = (registration.withdraw ?? []).map((t) => tenantSubscriberKey(prefix, t, instanceId));
257
+ if (!registration.inbox)
258
+ removed.push(inboxKey(prefix, instanceId));
259
+ if (removed.length > 0)
260
+ await this.#options.store.remove(removed);
261
+ if (registration.inbox)
262
+ await this.#options.store.put(inboxKey(prefix, instanceId), value, expiresAt);
263
+ for (const tenant of registration.tenants) {
264
+ await this.#options.store.put(tenantSubscriberKey(prefix, tenant, instanceId), value, expiresAt);
265
+ }
266
+ }
267
+ /** Record a forwarded worker advertisement — only keys of the worker advertisement keyspace. */
268
+ async #recordAdvertisement(ad) {
269
+ if (!this.#enabled) {
270
+ if (!this.#warnedAdvertisement) {
271
+ this.#warnedAdvertisement = true;
272
+ this.#options.onError(new Error('[QueueTransport] received a forwarded worker advertisement but has no worker-key store ' +
273
+ 'to record it in (transports.queue() defaults to the app database); the store-less ' +
274
+ "pod's workers will not appear in groupHealth, the dashboard or handshake negotiation."));
275
+ }
276
+ return;
277
+ }
278
+ const prefix = this.#options.prefix();
279
+ const allowed = (key) => key.startsWith(`${prefix}-worker-descriptor:`) ||
280
+ key.startsWith(`${prefix}-worker-heartbeat:`);
281
+ const removed = ad.remove.filter(allowed);
282
+ if (removed.length > 0)
283
+ await this.#options.store.remove(removed);
284
+ const now = this.#now();
285
+ for (const { key, value, ttlMs } of ad.put) {
286
+ if (allowed(key))
287
+ await this.#options.store.put(key, value, now + ttlMs);
288
+ }
289
+ }
290
+ // ---------------------------------------------------------------------------
291
+ // publisher (control plane)
292
+ // ---------------------------------------------------------------------------
293
+ /** The instance ids with a live subscription to `tenant` (deduplicated; malformed values skipped). */
294
+ async subscribersOf(tenant) {
295
+ if (!this.#enabled)
296
+ return [];
297
+ const marker = tenantSubscriberKeyPrefix(this.#options.prefix(), tenant);
298
+ const owners = new Set();
299
+ for (const { key, value } of await this.#subscribers()) {
300
+ if (!key.startsWith(marker))
301
+ continue;
302
+ const owner = ownerOf(value);
303
+ if (owner !== undefined)
304
+ owners.add(owner);
305
+ }
306
+ return [...owners];
307
+ }
308
+ /** Record a push onto `owner`'s inbox `queue`, so the janitor can drain it if `owner` dies. */
309
+ notePush(queue, owner) {
310
+ if (!this.#enabled || this.#options.sweepIntervalMs <= 0)
311
+ return;
312
+ this.#pushed.set(queue, { owner, lastPushAt: this.#now() });
313
+ if (this.#sweepTimer)
314
+ return;
315
+ this.#sweepTimer = setInterval(() => {
316
+ if (this.#sweeping)
317
+ return;
318
+ const round = this.sweep().finally(() => {
319
+ if (this.#sweeping === round)
320
+ this.#sweeping = undefined;
321
+ });
322
+ this.#sweeping = round;
323
+ }, this.#options.sweepIntervalMs);
324
+ this.#sweepTimer.unref?.();
325
+ }
326
+ /**
327
+ * One janitor round: for every inbox pushed to more than a TTL ago, drain it when its owner no longer
328
+ * has a live `-inbox:` key, or stop tracking it when the owner is alive (it consumed it). Reads the
329
+ * store fresh — never the publisher cache — so a decision to drain is never stale.
330
+ */
331
+ async sweep() {
332
+ if (this.#pushed.size === 0)
333
+ return;
334
+ const now = this.#now();
335
+ let live;
336
+ try {
337
+ live = await this.#options.store.live(now);
338
+ }
339
+ catch (err) {
340
+ this.#options.onError(err);
341
+ return;
342
+ }
343
+ const inboxPrefix = inboxKey(this.#options.prefix(), '');
344
+ const alive = new Set();
345
+ for (const { key, value } of live) {
346
+ if (!key.startsWith(inboxPrefix))
347
+ continue;
348
+ const owner = ownerOf(value);
349
+ if (owner !== undefined)
350
+ alive.add(owner);
351
+ }
352
+ for (const [queue, { owner, lastPushAt }] of [...this.#pushed]) {
353
+ if (now - lastPushAt < this.#options.ttlMs)
354
+ continue;
355
+ if (alive.has(owner)) {
356
+ this.#pushed.delete(queue);
357
+ continue;
358
+ }
359
+ try {
360
+ const emptied = await this.#options.drain(queue, DRAIN_PER_SWEEP);
361
+ // A push that raced the drain re-armed the entry with a newer time: keep tracking it then.
362
+ if (emptied && this.#pushed.get(queue)?.lastPushAt === lastPushAt)
363
+ this.#pushed.delete(queue);
364
+ }
365
+ catch (err) {
366
+ this.#options.onError(err);
367
+ }
368
+ }
369
+ }
370
+ // ---------------------------------------------------------------------------
371
+ // shutdown
372
+ // ---------------------------------------------------------------------------
373
+ /** Stop the timers and withdraw what this instance announced (best-effort, one last announcement). */
374
+ async close() {
375
+ if (this.#refreshTimer)
376
+ clearInterval(this.#refreshTimer);
377
+ this.#refreshTimer = undefined;
378
+ if (this.#sweepTimer)
379
+ clearInterval(this.#sweepTimer);
380
+ this.#sweepTimer = undefined;
381
+ await this.#sweeping;
382
+ if (!this.#inboxActive && this.#tenants.size === 0)
383
+ return;
384
+ const withdraw = [...this.#tenants];
385
+ this.#tenants.clear();
386
+ this.#inboxActive = false;
387
+ await this.#options
388
+ .announce({
389
+ instanceId: this.#options.instanceId,
390
+ ttlMs: this.#options.ttlMs,
391
+ inbox: false,
392
+ tenants: [],
393
+ withdraw,
394
+ })
395
+ .catch((err) => this.#options.onError(err));
396
+ }
397
+ }
398
+ //# sourceMappingURL=queue-remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-remote.js","sourceRoot":"","sources":["../../../src/transports/queue-remote.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAuB,MAAM,kBAAkB,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,kGAAkG;AAClG,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAC3C;wFACwF;AACxF,MAAM,CAAC,MAAM,mCAAmC,GAAG,KAAK,CAAC;AACzD,qFAAqF;AACrF,MAAM,CAAC,MAAM,+BAA+B,GAAG,MAAM,CAAC;AACtD,2FAA2F;AAC3F,MAAM,eAAe,GAAG,KAAK,CAAC;AAC9B,mEAAmE;AACnE,MAAM,uBAAuB,GAAG,EAAE,GAAG,MAAM,CAAC;AAC5C,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAEvC,MAAM,GAAG,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAErE,2GAA2G;AAC3G,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,UAAkB;IAChE,OAAO,GAAG,MAAM,gBAAgB,UAAU,EAAE,CAAC;AAC/C,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,UAAkB;IAClE,OAAO,GAAG,MAAM,kBAAkB,UAAU,EAAE,CAAC;AACjD,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,OAAO,GAAG,MAAM,sBAAsB,CAAC;AACzC,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,yBAAyB,CAAC,MAAc,EAAE,MAAc;IACtE,OAAO,GAAG,MAAM,sBAAsB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;AACvD,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,MAAc,EAAE,UAAkB;IACpF,OAAO,GAAG,yBAAyB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,UAAkB;IACzD,OAAO,GAAG,MAAM,UAAU,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;AAC9C,CAAC;AAiCD,SAAS,OAAO,CAAC,KAAa;IAC5B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAA2B,CAAC;QAC3D,OAAO,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,CAAU,EAAiB,EAAE,CAClD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,wBAAwB,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;AAEpG,iGAAiG;AACjG,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC9D,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAClG,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,wBAAwB;QAAE,OAAO,SAAS,CAAC;IAC9D,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,GAAgB,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,KAAuC,CAAC;QAClD,IACE,CAAC,CAAC;YACF,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;YACzB,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;YAC3B,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;YAC3B,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;YACzB,CAAC,CAAC,KAAK,IAAI,CAAC,EACZ,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,uBAAuB,CAAC,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACtD,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,iBAAiB,CAAC,GAAY;IAC5C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC9D,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,CAAC,UAAU,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC9E,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/F,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAChF,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7E,OAAO;QACL,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,uBAAuB,CAAC;QACjD,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;KACrB,CAAC;AACJ,CAAC;AAkBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,aAAa;IACf,QAAQ,CAAuB;IAC/B,IAAI,CAAe;IACnB,QAAQ,CAAU;IAClB,YAAY,CAAkD;IACvE,qDAAqD;IAC5C,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,qFAAqF;IACrF,YAAY,GAAG,KAAK,CAAC;IACrB,aAAa,CAA6C;IAC1D,wFAAwF;IAC/E,OAAO,GAAG,IAAI,GAAG,EAAiD,CAAC;IAC5E,WAAW,CAA6C;IACxD,SAAS,CAA4B;IACrC,OAAO,GAAG,KAAK,CAAC;IAChB,oBAAoB,GAAG,KAAK,CAAC;IAE7B,YAAY,OAA6B;QACvC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,KAAK,KAAK,cAAc,CAAC;QACjD,IAAI,CAAC,YAAY,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3F,CAAC;IAED,8EAA8E;IAC9E,aAAa;IACb,8EAA8E;IAE9E,SAAS,CAAC,QAAmB;QAC3B,KAAK,IAAI,CAAC,QAAQ;aACf,QAAQ,CAAC;YACR,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU;YACpC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK;YAC1B,KAAK,EAAE,IAAI,CAAC,YAAY;YACxB,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC3B,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClC,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,oFAAoF;IACpF,aAAa;QACX,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QAC9B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAED,mDAAmD;IACnD,SAAS,CAAC,MAAc;QACtB,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO;QACtC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAED,qEAAqE;IACrE,WAAW,CAAC,MAAc;QACxB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;YAAE,OAAO;QAC1C,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3B,CAAC;IAED,cAAc;QACZ,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO;QAC/B,IAAI,CAAC,aAAa,GAAG,WAAW,CAC9B,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,EACtB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CACjD,CAAC;QACF,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,eAAe;QACb,IAAI,KAAmC,CAAC;QACxC,MAAM,OAAO,GAAG,CAAC,MAAqC,EAAiB,EAAE;YACvE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,OAAO,GAAqB,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;gBAC7E,KAAK,GAAG,OAAO,CAAC;gBAChB,MAAM,CAAC,OAAO,CAAC,CAAC;gBAChB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;oBACjC,KAAK,GAAG,SAAS,CAAC;oBAClB,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;gBACpF,CAAC,CAAC,CAAC;YACL,CAAC;YACD,MAAM,CAAC,KAAK,CAAC,CAAC;YACd,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC,CAAC;QACF,OAAO;YACL,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,CACrC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC;YACtE,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,2BAA2B;IAC3B,8EAA8E;IAE9E,uFAAuF;IACvF,KAAK,CAAC,MAAM,CAAC,GAAY;QACvB,MAAM,aAAa,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,aAAa;YAAE,OAAO,IAAI,CAAC,oBAAoB,CAAC,aAAa,CAAC,CAAC;QACnE,MAAM,YAAY,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY;YAAE,OAAO,CAAC,oCAAoC;QAC/D,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,IAAI,CAAC,QAAQ,CAAC,OAAO,CACnB,IAAI,KAAK,CACP,uFAAuF;oBACrF,uFAAuF;oBACvF,uBAAuB,CAC1B,CACF,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACtC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAA0B,CAAC,CAAC;QACrE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC;QACtC,MAAM,OAAO,GAAG,CAAC,YAAY,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACtD,mBAAmB,CAAC,MAAM,EAAE,CAAC,EAAE,UAAU,CAAC,CAC3C,CAAC;QACF,IAAI,CAAC,YAAY,CAAC,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;QACpE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAClE,IAAI,YAAY,CAAC,KAAK;YACpB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAChF,KAAK,MAAM,MAAM,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;YAC1C,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAC3B,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAC/C,KAAK,EACL,SAAS,CACV,CAAC;QACJ,CAAC;IACH,CAAC;IAED,gGAAgG;IAChG,KAAK,CAAC,oBAAoB,CAAC,EAAoB;QAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAC/B,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;gBACjC,IAAI,CAAC,QAAQ,CAAC,OAAO,CACnB,IAAI,KAAK,CACP,yFAAyF;oBACvF,oFAAoF;oBACpF,uFAAuF,CAC1F,CACF,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE,CAC9B,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,qBAAqB,CAAC;YAC9C,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,oBAAoB,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC;YAC3C,IAAI,OAAO,CAAC,GAAG,CAAC;gBAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,KAAK,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,4BAA4B;IAC5B,8EAA8E;IAE9E,sGAAsG;IACtG,KAAK,CAAC,aAAa,CAAC,MAAc;QAChC,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;QACjC,KAAK,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,MAAM,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,SAAS;YACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,IAAI,KAAK,KAAK,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;IACrB,CAAC;IAED,+FAA+F;IAC/F,QAAQ,CAAC,KAAa,EAAE,KAAa;QACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,eAAe,IAAI,CAAC;YAAE,OAAO;QACjE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC5D,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAC7B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;YAClC,IAAI,IAAI,CAAC,SAAS;gBAAE,OAAO;YAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;gBACtC,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK;oBAAE,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;YAC3D,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACzB,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAClC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,IAAsC,CAAC;QAC3C,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QACD,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;QAChC,KAAK,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC;YAClC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC;gBAAE,SAAS;YAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,IAAI,KAAK,KAAK,SAAS;gBAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC;QACD,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/D,IAAI,GAAG,GAAG,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK;gBAAE,SAAS;YACrD,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC3B,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;gBAClE,2FAA2F;gBAC3F,IAAI,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,UAAU,KAAK,UAAU;oBAC/D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,WAAW;IACX,8EAA8E;IAE9E,sGAAsG;IACtG,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,aAAa;YAAE,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1D,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;QAC/B,IAAI,IAAI,CAAC,WAAW;YAAE,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,MAAM,IAAI,CAAC,SAAS,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QAC3D,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,MAAM,IAAI,CAAC,QAAQ;aAChB,QAAQ,CAAC;YACR,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU;YACpC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK;YAC1B,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,EAAE;YACX,QAAQ;SACT,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;CACF"}
@@ -1,8 +1,10 @@
1
1
  import type { AcquiredJob, AdapterFactory } from '@adonisjs/queue/types';
2
2
  import type { WorkerDescriptor } from '../handshake/descriptor.js';
3
- import { type ControlMessage, type ControlPlane, type Heartbeat, type RemoteTask, type StepResult, type Transport } from '../interfaces.js';
3
+ import { type ControlMessage, type ControlPlane, type GroupHealth, type Heartbeat, type RemoteTask, type RunReply, type RunRequest, type StartRunMessage, type StepResult, type TenantEvent, type Transport, type WorkflowDecision, type WorkflowStepEvent, type WorkflowTask } from '../interfaces.js';
4
4
  import { type StepHandler } from '../protocol.js';
5
5
  import type { DescriptorRedis } from '../worker-runtime/registry.js';
6
+ import { type WorkflowTurnHandler } from '../workflow-turn.js';
7
+ import { type ConcurrencyOption } from './adaptive-concurrency.js';
6
8
  import { type WorkerKeyStore } from './worker-keys.js';
7
9
  /**
8
10
  * The wire payloads carried as a queue job's `payload`. Everything that crosses the queue is plain
@@ -19,6 +21,15 @@ export type TaskPayload = RemoteTask;
19
21
  export type ResultPayload = StepResult;
20
22
  export type HeartbeatPayload = Heartbeat;
21
23
  export type ControlPayload = ControlMessage;
24
+ /** A run-request / start-run as it crosses the queue: the DTO plus the requester's instance id, so the
25
+ * control plane can route the reply to that requester's inbox (see `queue-remote.ts`). Private to this
26
+ * transport's wire — the shared {@link RunRequest} / {@link StartRunMessage} DTOs are unchanged. */
27
+ export type RunRequestPayload = RunRequest & {
28
+ replyTo?: string;
29
+ };
30
+ export type StartRunPayload = StartRunMessage & {
31
+ replyTo?: string;
32
+ };
22
33
  /**
23
34
  * The lease a `@boringnode/queue` >= 0.8 adapter (`@adonisjs/queue` 0.7) hands out with every claim:
24
35
  * `completeJob` / `retryJob` / `failJob` take `{ id, leaseToken }` instead of a bare job id, so only the
@@ -77,6 +88,30 @@ export interface QueueTransportOptions {
77
88
  namespace?: string;
78
89
  /** Poll interval (ms) for the result/task/heartbeat/control loops. Default 200ms. */
79
90
  pollIntervalMs?: number;
91
+ /**
92
+ * How many jobs of ONE step name this worker runs at once. Default 1 (one at a time, the historical
93
+ * behavior). Each handler name's task loop keeps up to this many jobs in flight from a single poll
94
+ * loop — more slots do not add polling — and every in-flight claim is renewed and drained on
95
+ * `close()` like a single one.
96
+ *
97
+ * `'adaptive'`, or `{ mode: 'adaptive', min, max, start, tickMs, ramCeilingPct, cpuCeilingPct }`,
98
+ * lets the same {@link AdaptiveController} the BullMQ transport uses move the limit at runtime (from
99
+ * step latency, errors/stalls, RSS and optionally CPU); every task loop reads the live limit on each
100
+ * tick. Either way the limit, in-flight count, RSS, CPU, throughput and p95 ride this worker's
101
+ * liveness heartbeat (see {@link QueueTransport.groupHealth}).
102
+ */
103
+ concurrency?: ConcurrencyOption;
104
+ /** How many step results the engine side settles at once. Default 1. */
105
+ resultConcurrency?: number;
106
+ /**
107
+ * Idle backoff for the task, heartbeat and control loops, as a multiple of {@link pollIntervalMs}:
108
+ * after consecutive empty polls the sleep doubles up to `idleBackoffMax × pollIntervalMs`, and any
109
+ * poll that finds a job resets it. Default 4 (an idle loop polls every 800ms instead of 200ms),
110
+ * which bounds the backend load of an idle worker serving many step names at the cost of up to that
111
+ * much pickup latency for the first job after a quiet spell. `1` disables it. The results loop never
112
+ * backs off: a result is on the critical path of every step.
113
+ */
114
+ idleBackoffMax?: number;
80
115
  /**
81
116
  * How often (ms) the stalled-job reclaim sweep runs. Reclaim is ON by default (default 30s); set to
82
117
  * `0` to disable it entirely. This is a coarse background check, NOT run on every poll tick — see
@@ -85,10 +120,16 @@ export interface QueueTransportOptions {
85
120
  stalledCheckIntervalMs?: number;
86
121
  /**
87
122
  * How old (ms) a claim must be before the reclaim sweep presumes its worker dead and re-delivers the
88
- * job. Default 30min — intentionally generous, because the claim's `acquiredAt` is never renewed
89
- * while the worker processes: a legitimately long step holds one claim for its entire duration, and
90
- * re-delivery double-runs a step whose worker is merely slow. Raise it above your longest step. Only used when the adapter implements
91
- * `recoverStalledJobs` and the sweep is enabled.
123
+ * job. Only used when the adapter implements `recoverStalledJobs` and the sweep is enabled.
124
+ *
125
+ * - On an adapter that renews claims (`adapter.renewJobs` — every `@adonisjs/queue` 0.6/0.7 driver):
126
+ * default 2min. This transport renews every in-flight claim each half threshold, so the threshold
127
+ * bounds how long a worker may go SILENT (crashed, or its event loop blocked), not how long a step
128
+ * may run.
129
+ * - On an adapter that cannot (a custom one without `renewJobs`): default 30min — intentionally
130
+ * generous, because the claim is never renewed and a legitimately long step holds it for its entire
131
+ * duration; re-delivery double-runs a step whose worker is merely slow. Raise it above your longest
132
+ * step.
92
133
  */
93
134
  stalledThresholdMs?: number;
94
135
  /**
@@ -112,8 +153,36 @@ export interface QueueTransportOptions {
112
153
  * advertised, and the engine routes without descriptors.
113
154
  */
114
155
  workerKeys?: WorkerKeyStore;
156
+ /**
157
+ * How long `close()` waits (ms) for jobs already in a handler before handing the rest back to the
158
+ * broker. Default 5000 — the same option, and default, as the `bullmq` and `db` transports.
159
+ */
160
+ closeTimeoutMs?: number;
115
161
  /** How long a read of {@link workerKeys} is reused (ms). Default 5000; `0` reads every time. */
116
162
  workerKeysCacheMs?: number;
163
+ /**
164
+ * How often (ms) a worker refreshes the liveness key of each step name it serves. Default 10000;
165
+ * each key lives at least 35s (three intervals when longer), so a worker that dies drops out of
166
+ * {@link QueueTransport.groupHealth} within that.
167
+ */
168
+ workerHeartbeatIntervalMs?: number;
169
+ /**
170
+ * TTL (ms) of what an instance announces to the control plane for the store-less protocol: each
171
+ * tenant it subscribes to (`onTenantEvent`) and that its inboxes are being consumed. The control plane
172
+ * records both as keys in its {@link workerKeys} store; the instance re-announces every third of the
173
+ * TTL. A subscriber that dies stops receiving pushes within about this long. Default 30000.
174
+ */
175
+ inboxTtlMs?: number;
176
+ /**
177
+ * How long a publisher reuses its read of a tenant's live subscribers (ms). A subscription made
178
+ * within this window of an event may miss it. Default 1000; `0` reads the store on every event.
179
+ */
180
+ tenantSubscribersCacheMs?: number;
181
+ /**
182
+ * How often (ms) a publisher drains the inboxes it pushed run replies / tenant events to whose owner
183
+ * no longer advertises itself (a dead tenant pod). Default 30000; `0` disables the janitor.
184
+ */
185
+ inboxSweepIntervalMs?: number;
117
186
  }
118
187
  /**
119
188
  * A `@adonisjs/queue`-backed {@link Transport}. Remote steps are dispatched to a per-group task
@@ -127,6 +196,12 @@ export interface QueueTransportOptions {
127
196
  * is single-consumer (point-to-point), so it is correct for a single engine instance but does NOT
128
197
  * fan out to every pod the way a real pub/sub does.
129
198
  *
199
+ * It also carries remote workflow turns (`dispatchWorkflowTask` / `handleWorkflow` / `onDecision`,
200
+ * streamed step events) and the store-less tenant-pod protocol (start-run, run-request, run replies,
201
+ * tenant events), so a `tenant` pod can reach a `control-plane` over `@adonisjs/queue` alone. Run
202
+ * replies and tenant events, which BullMQ broadcasts on Redis PUB/SUB, ride per-instance inbox queues
203
+ * here — see `queue-remote.ts`.
204
+ *
130
205
  * Run one instance engine-side (`onResult` + `dispatch`) and one per worker process (`handle()`).
131
206
  * The wire payloads are the documented `RemoteTask`/`StepResult` JSON, so non-Node workers on the
132
207
  * same queues interoperate.
@@ -136,9 +211,30 @@ export interface QueueTransportOptions {
136
211
  */
137
212
  export declare class QueueTransport implements Transport, ControlPlane {
138
213
  #private;
214
+ /**
215
+ * Per-group worker health, for `engine.workerHealth()` and the dashboard/Telescope worker panels.
216
+ * Present only when a {@link QueueTransportOptions.workerKeys} store is configured (the
217
+ * `transports.queue()` default) — without one no worker can advertise liveness, and reporting
218
+ * "backlog, zero workers" for every group would be a false alarm.
219
+ *
220
+ * - `depth` is the task queue's PENDING count (`adapter.sizeOf`), the jobs waiting for a worker.
221
+ * Unlike BullMQ's waiting+active+delayed+prioritized it leaves out jobs in flight and delayed
222
+ * retries: the `@adonisjs/queue` adapter contract has no count for those. Priorities are included
223
+ * (they are pending jobs). An adapter without `sizeOf` reports `0`.
224
+ * - `liveWorkers` are the unexpired `${P}-worker-heartbeat:<group>:<instance>` keys, each with the
225
+ * worker's {@link WorkerStatus} when it carries one (this transport's own beats do).
226
+ */
227
+ readonly groupHealth?: (group: string) => Promise<GroupHealth>;
139
228
  constructor(options: QueueTransportOptions);
140
- /** The writer a worker runtime's registry advertises its descriptor and heartbeat through. */
229
+ /** The writer a worker runtime's registry advertises its descriptor and heartbeat through: the key
230
+ * store when this instance has one. A store-less tenant pod (see {@link useTenantRole}) with no key
231
+ * store forwards it to the control plane instead, which records it in its own — so the control plane
232
+ * still sees the pod's workers. Otherwise, with no store, nothing is advertised. */
141
233
  createDescriptorRedis(): DescriptorRedis;
234
+ /** Declare that this transport backs a store-less `tenant` pod (the provider calls it): worker
235
+ * advertisement is then forwarded to the control plane when this instance has no key store. Only a
236
+ * tenant pod is guaranteed a control plane that records the forwarded keys. */
237
+ useTenantRole(): void;
142
238
  /** The live worker descriptors advertised on `token`. */
143
239
  listWorkerDescriptors(token: string): Promise<WorkerDescriptor[]>;
144
240
  /** The routing tokens a live worker beats on. */
@@ -181,12 +277,69 @@ export declare class QueueTransport implements Transport, ControlPlane {
181
277
  * dispatches by (see `engine.callRemote`). Re-registering a name just swaps the handler fn.
182
278
  */
183
279
  handle(name: string, fn: StepHandler): void;
280
+ /**
281
+ * Register a WORKFLOW turn consumer (worker side). Shares the step task loop of the workflow name's
282
+ * routing token — the same queue {@link dispatchWorkflowTask} targets — and tells the two job kinds
283
+ * apart BY SHAPE: a `workflow`-shaped job runs `turn(task)` and its {@link WorkflowDecision} is pushed
284
+ * onto `${P}:decisions`. Re-registering swaps the turn fn.
285
+ */
286
+ handleWorkflow(name: string, turn: WorkflowTurnHandler): void;
184
287
  /** Worker side: publish a liveness heartbeat for an in-flight long step. */
185
288
  heartbeat(beat: Heartbeat): Promise<void>;
186
289
  onResult(handler: (result: StepResult) => Promise<void>): void;
187
290
  onHeartbeat(handler: (beat: Heartbeat) => Promise<void>): void;
188
291
  publishControl(msg: ControlMessage): Promise<void>;
189
292
  onControl(handler: (msg: ControlMessage) => void): void;
293
+ /** engine → workflow worker: a {@link WorkflowTask} on the workflow's routing-token task queue (job
294
+ * `workflow`), the queue a {@link handleWorkflow} consumer polls. */
295
+ dispatchWorkflowTask(task: WorkflowTask): Promise<void>;
296
+ /**
297
+ * engine ← workflow worker: consume {@link WorkflowDecision}s from `${P}:decisions`. ONE loop per
298
+ * transport; every registered handler sees every decision (the engine applies one by run id and drops
299
+ * any its run no longer awaits), so several remote workflows can share the transport.
300
+ */
301
+ onDecision(handler: (decision: WorkflowDecision) => Promise<void>): void;
302
+ /** workflow worker → engine: a local step's lifecycle on `${P}:step-events` (job `stepEvent`),
303
+ * point-to-point so ONE engine instance checkpoints each event once. */
304
+ dispatchStepEvent(event: WorkflowStepEvent): Promise<void>;
305
+ /** engine ← workflow worker: consume streamed step events. Idempotent — a second call is a no-op. */
306
+ onStepEvent(handler: (event: WorkflowStepEvent) => Promise<void>): void;
307
+ /** tenant → control plane: enqueue a {@link StartRunMessage} on `${P}:start-run` (job `startRun`),
308
+ * stamped with this instance as the reply target. */
309
+ dispatchStartRun(msg: StartRunMessage): Promise<void>;
310
+ /** control plane ← tenant: consume {@link StartRunMessage}s. Idempotent — a second call is a no-op. */
311
+ onStartRun(handler: (msg: StartRunMessage) => Promise<void>): void;
312
+ /** tenant → control plane: enqueue a {@link RunRequest} on `${P}:run-request` (job `runRequest`),
313
+ * stamped with this instance as the reply target. */
314
+ dispatchRunRequest(msg: RunRequest): Promise<void>;
315
+ /** control plane ← tenant: consume {@link RunRequest}s. Idempotent — a second call is a no-op. */
316
+ onRunRequest(handler: (msg: RunRequest) => Promise<void>): void;
317
+ /**
318
+ * control plane → tenant: push a correlated {@link RunReply} onto the inbox of the instance that sent
319
+ * the request (`${P}:run-replies:<instance>`). Only the instance that consumed the request knows the
320
+ * requester, so this must run while that request is being handled — which is what the responder does.
321
+ * A reply with no known requester (a request that carried no `replyTo`) is dropped and reported.
322
+ */
323
+ publishRunReply(reply: RunReply): Promise<void>;
324
+ /**
325
+ * tenant ← control plane: consume this instance's reply inbox. Idempotent — a second call is a no-op
326
+ * (one gateway per transport matches every reply by id). The inbox is private to this instance, so it
327
+ * is consumed even when consumers are deferred: no other process could take its jobs.
328
+ */
329
+ onRunReply(handler: (reply: RunReply) => void): void;
330
+ /**
331
+ * control plane → tenant: push a {@link TenantEvent} onto the inbox of every instance with a live
332
+ * subscription to `evt.tenant` (none: nothing is pushed). Publishes are serialized, so each
333
+ * subscriber receives one publisher's events in publish order.
334
+ */
335
+ publishTenantEvent(evt: TenantEvent): Promise<void>;
336
+ /**
337
+ * tenant ← control plane: subscribe to `tenant`'s events. Announces the subscription to the control
338
+ * plane (re-announced every third of {@link QueueTransportOptions.inboxTtlMs} while it lasts) and
339
+ * consumes this instance's event inbox. Needs no store on this side. Returns an unsubscribe fn that
340
+ * detaches only this handler; the last one withdraws the subscription.
341
+ */
342
+ onTenantEvent(tenant: string, handler: (evt: TenantEvent) => void): () => void;
190
343
  /**
191
344
  * Stop every poll loop, WAIT (bounded) for the job already in a handler's hands, and hand any
192
345
  * still-unfinished job back to the broker before destroying the adapter. Without the wait, a