effect-mq 0.5.0 → 0.7.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 (61) hide show
  1. package/README.md +85 -17
  2. package/dist/Flow.d.ts +381 -0
  3. package/dist/Flow.d.ts.map +1 -0
  4. package/dist/Flow.js +340 -0
  5. package/dist/Flow.js.map +1 -0
  6. package/dist/Job.d.ts +31 -6
  7. package/dist/Job.d.ts.map +1 -1
  8. package/dist/Job.js +16 -2
  9. package/dist/Job.js.map +1 -1
  10. package/dist/JobStore.d.ts +353 -13
  11. package/dist/JobStore.d.ts.map +1 -1
  12. package/dist/JobStore.js +10 -0
  13. package/dist/JobStore.js.map +1 -1
  14. package/dist/MemoryJobStore.d.ts.map +1 -1
  15. package/dist/MemoryJobStore.js +361 -21
  16. package/dist/MemoryJobStore.js.map +1 -1
  17. package/dist/Metrics.d.ts +31 -0
  18. package/dist/Metrics.d.ts.map +1 -1
  19. package/dist/Metrics.js +39 -0
  20. package/dist/Metrics.js.map +1 -1
  21. package/dist/Worker.d.ts +120 -11
  22. package/dist/Worker.d.ts.map +1 -1
  23. package/dist/Worker.js +452 -26
  24. package/dist/Worker.js.map +1 -1
  25. package/dist/drizzle-postgres/DrizzleJobStore.d.ts +19 -1
  26. package/dist/drizzle-postgres/DrizzleJobStore.d.ts.map +1 -1
  27. package/dist/drizzle-postgres/DrizzleJobStore.js +678 -80
  28. package/dist/drizzle-postgres/DrizzleJobStore.js.map +1 -1
  29. package/dist/drizzle-postgres/schema.d.ts +293 -3
  30. package/dist/drizzle-postgres/schema.d.ts.map +1 -1
  31. package/dist/drizzle-postgres/schema.js +66 -1
  32. package/dist/drizzle-postgres/schema.js.map +1 -1
  33. package/dist/index.d.ts +7 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +7 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/redis/RedisJobStore.d.ts +53 -0
  38. package/dist/redis/RedisJobStore.d.ts.map +1 -1
  39. package/dist/redis/RedisJobStore.js +402 -50
  40. package/dist/redis/RedisJobStore.js.map +1 -1
  41. package/dist/redis/scripts.d.ts +213 -35
  42. package/dist/redis/scripts.d.ts.map +1 -1
  43. package/dist/redis/scripts.js +689 -73
  44. package/dist/redis/scripts.js.map +1 -1
  45. package/dist/testing/conformance.d.ts +6 -0
  46. package/dist/testing/conformance.d.ts.map +1 -1
  47. package/dist/testing/conformance.js +855 -12
  48. package/dist/testing/conformance.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/Flow.ts +778 -0
  51. package/src/Job.ts +35 -11
  52. package/src/JobStore.ts +377 -12
  53. package/src/MemoryJobStore.ts +396 -25
  54. package/src/Metrics.ts +43 -0
  55. package/src/Worker.ts +726 -37
  56. package/src/drizzle-postgres/DrizzleJobStore.ts +844 -81
  57. package/src/drizzle-postgres/schema.ts +92 -0
  58. package/src/index.ts +8 -0
  59. package/src/redis/RedisJobStore.ts +540 -39
  60. package/src/redis/scripts.ts +751 -78
  61. package/src/testing/conformance.ts +1088 -12
@@ -36,6 +36,7 @@ import { sql } from "drizzle-orm"
36
36
  import {
37
37
  type AnyPgColumnBuilder,
38
38
  bigint,
39
+ bigserial,
39
40
  boolean,
40
41
  index,
41
42
  integer,
@@ -56,6 +57,10 @@ type BackoffPolicy = JobStore.BackoffPolicy
56
57
  type KeepPolicy = JobStore.KeepPolicy
57
58
  type AttemptOutcome = JobStore.AttemptRecord["outcome"]
58
59
  type TraceContext = JobStore.TraceContext
60
+ type ParentEnvelope = JobStore.ParentEnvelope
61
+ type EnqueueRequest = JobStore.EnqueueRequest
62
+ type FlowChildStatus = JobStore.FlowChildRecord["status"]
63
+ type FlowChildReport = JobStore.FlowChildReport
59
64
 
60
65
  /**
61
66
  * Table-factory options: `extraConfig` receives the table's columns (exactly
@@ -89,6 +94,14 @@ const jobsColumns = <JobName extends string, Queue extends string>() => ({
89
94
  cancelRequested: boolean("cancel_requested").notNull().default(false),
90
95
  dedupeKey: text("dedupe_key"),
91
96
  trace: jsonb("trace").$type<TraceContext>(),
97
+ /** Flow-parent link on child jobs (opaque envelope, like `trace`). */
98
+ parent: jsonb("parent").$type<ParentEnvelope>(),
99
+ /** Flow bookkeeping: NULL together until a FanOut ack lands the manifest. */
100
+ flowFailFast: boolean("flow_fail_fast"),
101
+ flowPending: integer("flow_pending"),
102
+ flowCompleted: integer("flow_completed"),
103
+ flowFailed: integer("flow_failed"),
104
+ flowCancelled: integer("flow_cancelled"),
92
105
  runAt: timestamp("run_at", { withTimezone: true, mode: "date" }).notNull(),
93
106
  enqueuedAt: timestamp("enqueued_at", { withTimezone: true, mode: "date" }).notNull(),
94
107
  processedAt: timestamp("processed_at", { withTimezone: true, mode: "date" }),
@@ -245,6 +258,75 @@ export const mqDedupe = <JobName extends string = string>(
245
258
  ...options?.extraConfig?.(table) ?? []
246
259
  ])
247
260
 
261
+ const flowChildrenColumns = () => ({
262
+ /** The parent (flow) job's id in this store. */
263
+ flowId: text("flow_id").notNull().$type<JobId>(),
264
+ /** Unique within the flow (the idempotency mechanism). */
265
+ childKey: text("child_key").notNull(),
266
+ /** The child job's name (projection of `spec.name` for dashboards). */
267
+ name: text("name").notNull(),
268
+ /** The CHILD store's context-key string (children may live elsewhere). */
269
+ storeKey: text("store_key").notNull(),
270
+ /** The FULL `EnqueueRequest`, so the sweeper can re-enqueue from storage. */
271
+ spec: jsonb("spec").notNull().$type<EnqueueRequest>(),
272
+ status: text("status").notNull().$type<FlowChildStatus>(),
273
+ /** The child's schema-encoded exit; NULL for store-side failures. */
274
+ exit: jsonb("exit"),
275
+ failedReason: text("failed_reason"),
276
+ /** True once no cancel needs delivering into the child's store. */
277
+ cascaded: boolean("cascaded").notNull(),
278
+ pendingSince: timestamp("pending_since", { withTimezone: true, mode: "date" }).notNull()
279
+ })
280
+
281
+ /**
282
+ * Flow dependency rows (one per fan-out child; see `JobStore.FlowChildRecord`).
283
+ * Lives next to the PARENT store's jobs table.
284
+ *
285
+ * @since 0.6.0
286
+ */
287
+ export const mqFlowChildren = (
288
+ tableName = "effect_mq_flow_children",
289
+ options?: MqTableOptions<ReturnType<typeof flowChildrenColumns>>
290
+ ) =>
291
+ pgTable(tableName, flowChildrenColumns(), (table) => [
292
+ primaryKey({ columns: [table.flowId, table.childKey] }),
293
+ // flowSweepWork reconcile: pending rows older than the threshold.
294
+ index(`${tableName}_pending_idx`)
295
+ .on(table.pendingSince)
296
+ .where(sql`${table.status} = 'pending'`),
297
+ // flowSweepWork cascade: cancelled rows not yet delivered.
298
+ index(`${tableName}_cascade_idx`)
299
+ .on(table.flowId)
300
+ .where(sql`${table.status} = 'cancelled' AND NOT ${table.cascaded}`),
301
+ ...options?.extraConfig?.(table) ?? []
302
+ ])
303
+
304
+ const flowOutboxColumns = () => ({
305
+ /** Store-assigned, oldest-first; exposed to callers as an opaque string. */
306
+ id: bigserial("id", { mode: "number" }).primaryKey(),
307
+ /** The flow definition's name, for relay routing. */
308
+ flowName: text("flow_name").notNull(),
309
+ /** The PARENT store's context-key string, for relay routing. */
310
+ parentStoreKey: text("parent_store_key").notNull(),
311
+ /** The full `FlowChildReport` to deliver into the parent store. */
312
+ report: jsonb("report").notNull().$type<FlowChildReport>()
313
+ })
314
+
315
+ /**
316
+ * Undelivered child-result reports (one per terminal transition of an
317
+ * envelope-carrying job; see `JobStore.OutboxEntry`). Lives next to the
318
+ * CHILD store's jobs table.
319
+ *
320
+ * @since 0.6.0
321
+ */
322
+ export const mqFlowOutbox = (
323
+ tableName = "effect_mq_flow_outbox",
324
+ options?: MqTableOptions<ReturnType<typeof flowOutboxColumns>>
325
+ ) =>
326
+ pgTable(tableName, flowOutboxColumns(), (table) => [
327
+ ...options?.extraConfig?.(table) ?? []
328
+ ])
329
+
248
330
  const queueControlColumns = <Queue extends string>() => ({
249
331
  queue: text("queue").primaryKey().$type<Queue>(),
250
332
  paused: boolean("paused").notNull().default(false)
@@ -287,3 +369,13 @@ export type MqQueueControlTable = ReturnType<typeof mqQueueControl<any>>
287
369
  * @since 0.3.0
288
370
  */
289
371
  export type MqDedupeTable = ReturnType<typeof mqDedupe<any>>
372
+
373
+ /**
374
+ * @since 0.6.0
375
+ */
376
+ export type MqFlowChildrenTable = ReturnType<typeof mqFlowChildren>
377
+
378
+ /**
379
+ * @since 0.6.0
380
+ */
381
+ export type MqFlowOutboxTable = ReturnType<typeof mqFlowOutbox>
package/src/index.ts CHANGED
@@ -4,6 +4,14 @@
4
4
  * @since 0.1.0
5
5
  */
6
6
 
7
+ /**
8
+ * Cross-store parent-child flows: `Flow.make`, `Flow.children`, the
9
+ * two-phase `fanOut`/`collect` handler.
10
+ *
11
+ * @since 0.6.0
12
+ */
13
+ export * as Flow from "./Flow.ts"
14
+
7
15
  /**
8
16
  * Schema-first job definitions: `Job.make`, `enqueue`, `toLayer`.
9
17
  *