@kairos-es/read 0.0.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 (82) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +538 -0
  3. package/dist/cjs/EventLogDurability.js +184 -0
  4. package/dist/cjs/EventLogDurability.js.map +1 -0
  5. package/dist/cjs/ProjectionRunner.js +478 -0
  6. package/dist/cjs/ProjectionRunner.js.map +1 -0
  7. package/dist/cjs/ProjectionStore.js +233 -0
  8. package/dist/cjs/ProjectionStore.js.map +1 -0
  9. package/dist/cjs/foldIntoRef.js +36 -0
  10. package/dist/cjs/foldIntoRef.js.map +1 -0
  11. package/dist/cjs/inMemoryProjectionStore.js +138 -0
  12. package/dist/cjs/inMemoryProjectionStore.js.map +1 -0
  13. package/dist/cjs/index.js +128 -0
  14. package/dist/cjs/index.js.map +1 -0
  15. package/dist/cjs/projectionWiringFault.js +532 -0
  16. package/dist/cjs/projectionWiringFault.js.map +1 -0
  17. package/dist/cjs/runProjection.js +117 -0
  18. package/dist/cjs/runProjection.js.map +1 -0
  19. package/dist/cjs/runProjections.js +144 -0
  20. package/dist/cjs/runProjections.js.map +1 -0
  21. package/dist/cjs/superviseOnProgress.js +580 -0
  22. package/dist/cjs/superviseOnProgress.js.map +1 -0
  23. package/dist/cjs/testing.js +143 -0
  24. package/dist/cjs/testing.js.map +1 -0
  25. package/dist/dts/EventLogDurability.d.ts +182 -0
  26. package/dist/dts/EventLogDurability.d.ts.map +1 -0
  27. package/dist/dts/ProjectionRunner.d.ts +557 -0
  28. package/dist/dts/ProjectionRunner.d.ts.map +1 -0
  29. package/dist/dts/ProjectionStore.d.ts +475 -0
  30. package/dist/dts/ProjectionStore.d.ts.map +1 -0
  31. package/dist/dts/foldIntoRef.d.ts +39 -0
  32. package/dist/dts/foldIntoRef.d.ts.map +1 -0
  33. package/dist/dts/inMemoryProjectionStore.d.ts +11 -0
  34. package/dist/dts/inMemoryProjectionStore.d.ts.map +1 -0
  35. package/dist/dts/index.d.ts +185 -0
  36. package/dist/dts/index.d.ts.map +1 -0
  37. package/dist/dts/projectionWiringFault.d.ts +260 -0
  38. package/dist/dts/projectionWiringFault.d.ts.map +1 -0
  39. package/dist/dts/runProjection.d.ts +185 -0
  40. package/dist/dts/runProjection.d.ts.map +1 -0
  41. package/dist/dts/runProjections.d.ts +480 -0
  42. package/dist/dts/runProjections.d.ts.map +1 -0
  43. package/dist/dts/superviseOnProgress.d.ts +587 -0
  44. package/dist/dts/superviseOnProgress.d.ts.map +1 -0
  45. package/dist/dts/testing.d.ts +207 -0
  46. package/dist/dts/testing.d.ts.map +1 -0
  47. package/dist/esm/EventLogDurability.js +175 -0
  48. package/dist/esm/EventLogDurability.js.map +1 -0
  49. package/dist/esm/ProjectionRunner.js +468 -0
  50. package/dist/esm/ProjectionRunner.js.map +1 -0
  51. package/dist/esm/ProjectionStore.js +223 -0
  52. package/dist/esm/ProjectionStore.js.map +1 -0
  53. package/dist/esm/foldIntoRef.js +29 -0
  54. package/dist/esm/foldIntoRef.js.map +1 -0
  55. package/dist/esm/inMemoryProjectionStore.js +131 -0
  56. package/dist/esm/inMemoryProjectionStore.js.map +1 -0
  57. package/dist/esm/index.js +185 -0
  58. package/dist/esm/index.js.map +1 -0
  59. package/dist/esm/package.json +4 -0
  60. package/dist/esm/projectionWiringFault.js +524 -0
  61. package/dist/esm/projectionWiringFault.js.map +1 -0
  62. package/dist/esm/runProjection.js +109 -0
  63. package/dist/esm/runProjection.js.map +1 -0
  64. package/dist/esm/runProjections.js +137 -0
  65. package/dist/esm/runProjections.js.map +1 -0
  66. package/dist/esm/superviseOnProgress.js +571 -0
  67. package/dist/esm/superviseOnProgress.js.map +1 -0
  68. package/dist/esm/testing.js +133 -0
  69. package/dist/esm/testing.js.map +1 -0
  70. package/package.json +41 -0
  71. package/src/EventLogDurability.ts +201 -0
  72. package/src/ProjectionRunner.ts +923 -0
  73. package/src/ProjectionStore.ts +528 -0
  74. package/src/foldIntoRef.ts +63 -0
  75. package/src/inMemoryProjectionStore.ts +163 -0
  76. package/src/index.ts +218 -0
  77. package/src/projectionWiringFault.ts +694 -0
  78. package/src/runProjection.ts +270 -0
  79. package/src/runProjections.ts +623 -0
  80. package/src/superviseOnProgress.ts +897 -0
  81. package/src/testing.ts +290 -0
  82. package/testing/package.json +6 -0
@@ -0,0 +1,580 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.superviseOnProgress = exports.ProjectionStalled = exports.MaxNoProgressRestarts = exports.DEFAULT_RESTART_RESET_AFTER = exports.DEFAULT_RESTART_MIN_DELAY = exports.DEFAULT_RESTART_MAX_DELAY = void 0;
7
+ var _core = require("@kairos-es/core");
8
+ var _effect = require("effect");
9
+ /**
10
+ * `superviseOnProgress` — the projection runner's SUPERVISOR: retry an effect for
11
+ * as long as an EXTERNALLY OBSERVED progress signal keeps moving, and give up when
12
+ * it stops.
13
+ *
14
+ * ## The mechanism
15
+ *
16
+ * A daemon that fails has to be restarted, and something has to decide when
17
+ * restarting has become pointless. The usual answer counts failures, which
18
+ * cannot tell "this keeps failing and nothing is happening" from "this keeps
19
+ * failing and the work is getting done anyway" — and those two want opposite
20
+ * decisions. So this module keys the decision on a SIGNAL the supervised effect
21
+ * does not own: a `Position` read from somewhere else, monotone by construction,
22
+ * whose movement means work landed no matter who landed it. Two mechanisms, kept
23
+ * deliberately apart:
24
+ *
25
+ * - **The schedule decides HOW FAST to restart** — a capped, jittered,
26
+ * sawtoothing exponential that never terminates on its own.
27
+ * - **The circuit-breaker decides WHETHER to keep restarting** — by re-reading
28
+ * `progress` after every failure and counting only the CONSECUTIVE restarts
29
+ * that moved it not at all.
30
+ *
31
+ * Splitting them is what makes a competing writer behave. Two processes
32
+ * accidentally maintaining one materialisation — a deploy overlapping its
33
+ * predecessor, one store passed twice — both fail on every batch, the loser's
34
+ * guarded checkpoint advance being superseded every time; but the WINNER keeps
35
+ * advancing the shared stored position, so every one of the loser's restarts
36
+ * observes movement, its counter resets, and it resynchronises from the winner's
37
+ * position for as long as the winner keeps working: bounded duplicate effort, at
38
+ * the schedule's rate, with no hot loop and no spurious give-up. Only a signal
39
+ * that is genuinely stuck — a poison event whose apply always fails, a
40
+ * persistently broken view store — trips the breaker and fails
41
+ * `ProjectionStalled`.
42
+ *
43
+ * "Accidentally" is the word and it is doing work, because ONE read model
44
+ * materialised N ways is a DELIBERATE shape (`runProjections`) and is not this case
45
+ * at all. Those N runners share a `ProjectionId` but each owns its own checkpoint in
46
+ * its OWN store, so they never contend, no guard is ever lost between them, and
47
+ * nothing here is being relied on to make them safe. The tolerated fault above is
48
+ * two writers over ONE cursor, which is what the construction gate's collision rung
49
+ * refuses when it is a wiring rather than an overlapping deploy — this mechanism
50
+ * degrades gracefully under it and is deliberately not the place it is caught.
51
+ *
52
+ * ## Two observability channels, and which one is primary
53
+ *
54
+ * Every supervision outcome is reported twice: to a HOOK — `onRestart` on every
55
+ * restart, `onStalled` once at the give-up — and to a LOG LINE beside it. The
56
+ * hooks are the primary channel and carry the fault ITSELF, untouched. The log
57
+ * lines are the thin lossy default: every annotation on them is a string or a
58
+ * number — JSON-SAFE, that is, rather than uniformly stringy, because
59
+ * `effect@3.22`'s `Logger.json` throws outright on a `bigint` nested in an
60
+ * annotation value and `CheckpointSuperseded` carries one (the measurement, for
61
+ * each shipped logger, is at the annotations in `shouldRestart`). The one numeric
62
+ * annotation, `noProgressRestarts`, stays numeric on purpose: a serialiser has no
63
+ * quarrel with it and an aggregator can filter and graph on it, both of which a
64
+ * `String` around it would give up for nothing. The fault goes out through
65
+ * `String`, which is to say through the FAULT: `Error.prototype.toString` is
66
+ * `name: message`, so a fault with something to say sets `message` — as all four of
67
+ * the runner's own faults do, `CheckpointSuperseded` included, its `bigint`
68
+ * `expected` interpolated into a `string` and so hidden from every serialiser — and
69
+ * one with nothing to say beyond its tag renders as its tag. The supervisor needs
70
+ * no knowledge of the supervised effect's `E` to label it — it is generic in `E` —
71
+ * and no caller has to supply anything for the purpose.
72
+ *
73
+ * ## Why it is a module of its own, and why it is PACKAGE-INTERNAL
74
+ *
75
+ * Not because the mechanism is on offer for reuse. It is deliberately absent from
76
+ * `@kairos-es/read`'s entry point, and its signature says what it supervises: a
77
+ * `CheckpointKey` names the supervised thing, a `Position` is the signal, a
78
+ * `ProjectionStoreError` is what reading that signal can fail with, and
79
+ * `ProjectionStalled` is a projection-shaped failure on
80
+ * `ProjectionRunner.fibre`'s error channel.
81
+ *
82
+ * It is separate because it is the hardest thing in this package to test THROUGH
83
+ * the pipeline, and the repo has the before and after. Pinning the five-minute
84
+ * default no-progress budget — a quantity about supervision and nothing else —
85
+ * cost ~122 lines while the mechanism lived inside `runProjection`: an in-memory
86
+ * event store, a view store decorated never to commit, a read model, a log-capture
87
+ * layer, and a hand-driven loop stepping the clock some 1200 times. Against a stub
88
+ * it costs 52, and 22 of those are code: one `Effect` yielding a `Position`, one
89
+ * effect that fails, one `TestClock.adjust`, and the rest of the case is the
90
+ * arithmetic it asserts written out. (The figure is re-measured rather than
91
+ * inherited — it read ~37 until the three restart `Duration`s became explicit
92
+ * arguments and the case gained a comment saying why. The ratio is what the
93
+ * argument turns on, and that has not moved.) Every other subtle behaviour is
94
+ * pinned the same way — the
95
+ * `ORIGIN` seeding, the breaker being consulted before the sleep, the interruption
96
+ * trap on the re-read, the counter resetting on progress — because this module
97
+ * knows nothing of `subscribe`, of decoding, of micro-batching, of committing, of
98
+ * slices or of the `Serializer`. Those cases are three suites over one stub
99
+ * (`test/superviseOnProgress.{pacing,breaker,observability}.test.ts`, sharing
100
+ * `test/superviseOnProgress.fixture.ts`), split along the two mechanisms this
101
+ * section keeps apart plus the channel their outcomes leave through. A SEPARATE
102
+ * module bought all of that; a GENERAL one was never needed for any of it.
103
+ *
104
+ * And there is no second consumer here, nor one in prospect. The mechanism needs a
105
+ * caller with a failing effect worth restarting AND a progress signal owned by
106
+ * somebody else; in this library only a projection has both. A command runner's
107
+ * progress IS its own success. The store's poll retry lives in
108
+ * `@kairos-es/store-postgres` and already has a bounded retry with no supervisor
109
+ * over it. A lease renewer for competing consumers is out of scope by ADR-0007.
110
+ *
111
+ * Should such a consumer appear, generalising then is a smaller change than
112
+ * maintaining the claim now — so the cost is recorded here rather than
113
+ * re-derived: `key` becomes a bare `string` label, losing the two discrete
114
+ * `projection`/`partition` annotations an operator greps on (or forcing a generic
115
+ * annotation shape onto every caller); `Position`'s `!==` becomes an
116
+ * `Equivalence` on the signal type; `ProjectionStoreError` becomes a fourth type
117
+ * parameter, and collapsing it to `unknown` instead is what would cost the
118
+ * interruption trap its argument, since "`catchAll` touches only the typed
119
+ * `ProjectionStoreError`" stops being a claim a reader can check against the
120
+ * signature; and `ProjectionStalled` has to be renamed or made generic.
121
+ *
122
+ * ## Platform-neutral
123
+ *
124
+ * No `node:*`, no `Date`, no host RNG: the backoff is `Duration`s scheduled
125
+ * through the `Clock` and the jitter is Effect's `Random` service, so
126
+ * `TestClock` drives all of it.
127
+ */
128
+
129
+ /**
130
+ * Consecutive no-progress restarts before the supervisor gives up: a branded
131
+ * POSITIVE integer.
132
+ *
133
+ * Branded because at `0` the breaker's `restarts >= maxNoProgressRestarts` test
134
+ * is true on the FIRST failure, so the supervisor gives up before it has retried
135
+ * once and before `onRestart` has ever fired — a `ProjectionStalled` for a fault
136
+ * that a single 100-millisecond retry would have cleared, and the flat
137
+ * contradiction of the five-minute budget `DEFAULT_MAX_NO_PROGRESS_RESTARTS`
138
+ * documents. Negative and fractional counts fail the same test the same way.
139
+ *
140
+ * `>= 1` rather than `>= 0` for that reason: "give up immediately" is not a
141
+ * supervision policy this module offers, and a caller who wants one has
142
+ * `Fiber.interrupt`.
143
+ *
144
+ * A distinct brand from `BatchSize` despite the identical refinement, because
145
+ * the two are still writable side by side in one flat options literal, are both
146
+ * bare integers, and differ by an order of magnitude in meaning — nominality is
147
+ * what makes a transposition fail to typecheck rather than silently retune the
148
+ * runner. That the two now tune different mechanisms in different modules is a
149
+ * second reason for the distinction, not a replacement for the first.
150
+ *
151
+ * Construct with `MaxNoProgressRestarts.make(n)` — and retune it by the WALL
152
+ * CLOCK the figure buys, per `DEFAULT_MAX_NO_PROGRESS_RESTARTS`.
153
+ */
154
+ const MaxNoProgressRestarts = exports.MaxNoProgressRestarts = /*#__PURE__*/_effect.Schema.Int.pipe(/*#__PURE__*/_effect.Schema.positive(), /*#__PURE__*/_effect.Schema.brand('MaxNoProgressRestarts'));
155
+ /**
156
+ * Fast enough that a transient blip is invisible, slow enough not to spin.
157
+ *
158
+ * Exported — like the two below and unlike `DEFAULT_MAX_NO_PROGRESS_RESTARTS` —
159
+ * because `resolveProjection` is what RESOLVES it, for both entry points: the
160
+ * construction gate judges the
161
+ * figure that will actually run, so the resolution has to happen before the
162
+ * supervisor is built and the default has to be readable from there. The
163
+ * documentation stays HERE, against the sawtooth arithmetic the three of them
164
+ * define together, because that is what the figure is chosen for.
165
+ */
166
+ const DEFAULT_RESTART_MIN_DELAY = exports.DEFAULT_RESTART_MIN_DELAY = '100 millis';
167
+ /**
168
+ * The store's own poll retry exhausts after roughly 40 seconds of sustained
169
+ * transient faults before `subscribe` dies, so a 30-second ceiling keeps a
170
+ * restart in the same order of magnitude as the fault it is recovering from —
171
+ * long enough not to hammer a struggling database, short enough that recovery is
172
+ * not measured in minutes.
173
+ *
174
+ * Exported for the reason `DEFAULT_RESTART_MIN_DELAY` gives.
175
+ */
176
+ const DEFAULT_RESTART_MAX_DELAY = exports.DEFAULT_RESTART_MAX_DELAY = '30 seconds';
177
+ /**
178
+ * A minute of ELAPSED retrying, after which the pacing starts over from
179
+ * `restartMinDelay`.
180
+ *
181
+ * `Schedule.resetAfter` keys on `Schedule.elapsed` — the time since the schedule
182
+ * (or its own last reset) began, NOT the time since the last decision, verified
183
+ * against `effect@3.22`'s `internal/schedule.ts` — and both readings of that are
184
+ * wanted. A run that stays healthy for a minute pushes `elapsed` past the
185
+ * threshold on its way to the next fault, so its backoff starts from
186
+ * `restartMinDelay` again instead of inheriting a ceiling from a fault it
187
+ * recovered from hours ago. And a SUSTAINED outage, whose attempts fail fast,
188
+ * sawtooths back down to `restartMinDelay` every cycle rather than pinning at
189
+ * `restartMaxDelay` for ever, so a store that comes back is noticed within
190
+ * `restartMinDelay` rather than up to `restartMaxDelay` later.
191
+ *
192
+ * Exported for the reason `DEFAULT_RESTART_MIN_DELAY` gives.
193
+ */
194
+ const DEFAULT_RESTART_RESET_AFTER = exports.DEFAULT_RESTART_RESET_AFTER = '60 seconds';
195
+ /**
196
+ * FORTY zero-progress restarts, chosen for the WALL CLOCK it buys — roughly five
197
+ * minutes — rather than for the count, which on its own means nothing.
198
+ *
199
+ * What the figure has to satisfy is `ProjectionStalled`'s own claim: a view store
200
+ * "broken rather than merely unavailable". So the budget must be long enough that
201
+ * a merely-unavailable view store — a failover, a rolling restart, a brief
202
+ * partition — is back inside it, and comfortably longer than the ~40 seconds the
203
+ * event store itself spends retrying before `subscribe` dies. The arithmetic is
204
+ * written out rather than asserted, because the pacing above is not obvious and
205
+ * the next reader should be able to CHECK the claim:
206
+ *
207
+ * Unjittered, the restart schedule sleeps `0.1, 0.2, 0.4, 0.8, 1.6, 3.2, 6.4,
208
+ * 12.8, 25.6, 30` seconds — the exponential, then the `restartMaxDelay` cap — and
209
+ * then starts over: by the eleventh decision 81.1s have elapsed, past
210
+ * `restartResetAfter`, so `resetAfter` resets the schedule. The pacing is
211
+ * therefore a SAWTOOTH of ten sleeps summing 81.1s. A give-up at the Nth
212
+ * consecutive no-progress failure has slept N-1 times (the breaker is consulted
213
+ * BEFORE each sleep), so 40 restarts is three whole cycles (243.3s) plus the
214
+ * first nine of a fourth (51.1s): 294.4s ≈ 4.9 minutes, which the ±20% jitter
215
+ * spreads to roughly 4.5–5.3. `superviseOnProgress.pacing.test.ts` pins that budget
216
+ * under a `TestClock` — and, beside it, the N-1 that this arithmetic turns on — so a
217
+ * retuning cannot quietly shrink either.
218
+ *
219
+ * Five restarts — the figure this shipped with first — bought 1.5s, which made a
220
+ * ten-second blip a PERMANENT stall and flatly contradicted the documentation
221
+ * above. That is the mistake this figure exists to avoid: if you lower it,
222
+ * recompute the wall clock.
223
+ *
224
+ * Two things make the effective budget longer still. When it is the SUBSCRIPTION
225
+ * that keeps dying rather than the view store refusing to answer, each attempt
226
+ * also burns the store's own ~40-second poll-retry budget before dying. And
227
+ * restarts that DO move the progress signal never count at all, so a busy
228
+ * projection that restarts often can never trip this however long it runs.
229
+ *
230
+ * PRIVATE, where the three restart `Duration`s beside it are exported: the count is
231
+ * BRANDED, so `MaxNoProgressRestarts.make(n)` refused a bad value at the caller's
232
+ * own line and there is nothing left for the construction gate to judge. Nobody
233
+ * outside needs to resolve it, so it is resolved here.
234
+ */
235
+ const DEFAULT_MAX_NO_PROGRESS_RESTARTS = /*#__PURE__*/MaxNoProgressRestarts.make(40);
236
+ /**
237
+ * The supervisor gave up: `noProgressRestarts` consecutive restarts moved the
238
+ * progress signal not at all.
239
+ *
240
+ * This is the only way a supervised daemon's fibre ever fails, and for a
241
+ * projection it means one of two things: a POISON EVENT (an `apply` or a decode
242
+ * that fails deterministically on the event at the checkpoint, so every restart
243
+ * re-reads it and dies again), or a view store still not answering after the
244
+ * WHOLE no-progress budget — broken, that is, rather than merely unavailable.
245
+ * That distinction is only as true as the budget makes it, which is why the
246
+ * default is sized at roughly five minutes of retrying rather than at a tidy
247
+ * restart count (`DEFAULT_MAX_NO_PROGRESS_RESTARTS`). Both need a human; neither
248
+ * is fixed by retrying, which is why the supervisor stops instead of retrying for
249
+ * ever and calling that resilience.
250
+ *
251
+ * Getting it in FRONT of that human is `onStalled`'s job, and the reason that
252
+ * hook exists: this failure lands on the daemon's error channel, and the wiring
253
+ * this library recommends discards the daemon. Beside the hook there is one
254
+ * `Effect.logError`, and nothing else.
255
+ *
256
+ * `position` is the stuck signal — the position everything is failing just
257
+ * after — so the offending event is one query away. `reason` is the last
258
+ * underlying failure, kept as `unknown` because it spans the supervised effect's
259
+ * whole `E`, which this module is generic in.
260
+ */
261
+ class ProjectionStalled extends /*#__PURE__*/_effect.Data.TaggedError('ProjectionStalled') {
262
+ /**
263
+ * The two facts that make a stall diagnosable — how much budget was spent, and
264
+ * where it was spent — as the `Error` field that says why, so the fault renders
265
+ * ITSELF.
266
+ *
267
+ * This class needs the field more than any other fault the read side raises,
268
+ * because of WHERE it lands. `ProjectionStalled` is what `ProjectionRunner.fibre`
269
+ * fails with, which makes it the only one of them a consumer meets without going
270
+ * anywhere near the `ProjectionStore` port: `CheckpointSuperseded` and
271
+ * `ProjectionStoreError` sit on `commit`'s public signature too, but reaching them
272
+ * means calling or implementing that port, whereas the `Fiber.await` route the
273
+ * runner's own documentation recommends ends at whatever this getter says — from a
274
+ * daemon the application only forked.
275
+ *
276
+ * `Data.TaggedError` sets `name` to the tag and leaves `message` empty, and
277
+ * `Cause.pretty` substitutes its own `'An error has occurred'` for an empty one —
278
+ * so unfilled, a caller who did exactly what they were told to do reads
279
+ * `'ProjectionStalled: An error has occurred'` about the single failure this whole
280
+ * module exists to report. Filled, the same call yields a sentence naming the
281
+ * budget that was spent and the position everything is failing just after, which
282
+ * is the first thing an operator queries the log at.
283
+ *
284
+ * ## Why THESE two fields, and why interpolating them is safe
285
+ *
286
+ * `noProgressRestarts` is a `number`. `position` is a branded `bigint`, and
287
+ * TEMPLATE INTERPOLATION of a `bigint` yields its decimal digits — so what leaves
288
+ * this getter is typed `string` and carries no `bigint` for any serialiser to
289
+ * meet. That distinction is worth being exact about, because this package
290
+ * measures a real `bigint` hazard and it is easy to over-apply: `Logger.json`
291
+ * throws on a `bigint` NESTED IN AN ANNOTATION VALUE and loses the whole line, so
292
+ * the reduction in `shouldRestart` hands the logger `String(stored)`. The hazard
293
+ * is about the values a logger is handed, never about a `string` rendered from
294
+ * one — and `String(fault)` is precisely how a fault carrying a `bigint` field
295
+ * crosses that boundary intact.
296
+ *
297
+ * `reason` is deliberately NOT in the label. It spans the supervised effect's
298
+ * whole `E` and arrives as `unknown`, so anything this class said about it would
299
+ * be a guess at somebody else's value — and there is no need to guess: the raw
300
+ * fault reaches `onStalled` untouched, and the give-up log line beside it already
301
+ * carries `String(reason)`, which is the same self-rendering rule applied one
302
+ * level down.
303
+ *
304
+ * A GETTER over two existing fields rather than a `message` field, so neither can
305
+ * disagree with the label. Why a prototype accessor is safe here, how far filling
306
+ * the field reaches and the structured shape it leaves a machine to parse are set
307
+ * out once for all four of these getters on `ProjectionStoreError` in
308
+ * `ProjectionStore.ts`.
309
+ */
310
+ get message() {
311
+ return `no checkpoint progress in ${this.noProgressRestarts} consecutive restarts (stuck at ${this.position})`;
312
+ }
313
+ }
314
+ /**
315
+ * Supervise `run`: retry it while `options.progress` moves, give up with
316
+ * `ProjectionStalled` when it does not.
317
+ *
318
+ * Generic in the supervised effect's `A`, `E` and `R`. The supervisor reads the
319
+ * failure only through `String` and hands it to `onRestart`/`onStalled` as
320
+ * `unknown`, so it has no reason to constrain `E`; its own error channel is exactly
321
+ * `ProjectionStalled`, because the breaker collapses everything else into another
322
+ * restart; and `R` passes through untouched, nothing here adding a requirement,
323
+ * which is what keeps either entry point's requirement list the pipeline's own.
324
+ *
325
+ * ONE CALL rather than a curried supervisor later applied to an attempt. The rank-2
326
+ * intermediate bought exactly one thing: the tuning could be validated before the
327
+ * attempt existed, which fixed the ORDER a mis-tuned supervisor was reported in
328
+ * relative to the runner's other construction-time checks. That argument is now
329
+ * gone outright rather than merely outweighed — the tuning is judged by
330
+ * `projectionWiringFault` before a supervisor, an attempt or a fibre exists, in an
331
+ * order that module declares — so what a curried form would leave behind is a type
332
+ * whose only application sat a line from its construction.
333
+ *
334
+ * It validates NOTHING, which is deliberate rather than an omission. Every
335
+ * construction-time rejection on the read side has ONE home — the wiring gate, whose
336
+ * own single caller is the shared `prepareProjection` phase both entry points run
337
+ * before anything is built — and this module is
338
+ * package-internal with exactly one caller, so a check here would be a SECOND home
339
+ * for one rule: two sentences for one mistake, free to drift, each reachable only by
340
+ * whichever of the two modules its author happened to open. What arrives here has
341
+ * therefore already been resolved and judged, which is why the three restart
342
+ * `Duration`s are REQUIRED on `SuperviseOnProgressOptions` and why nothing below
343
+ * reaches for a default. The mistake still lands as a DEFECT at construction,
344
+ * exactly as `assertServableQuery`'s does; the gate is merely where.
345
+ *
346
+ * Nothing is taken for RENDERING the supervised effect's `E` on the two log lines:
347
+ * the `reason` annotation is `String(fault)`, so the fault labels itself. That is a
348
+ * real reduction rather than a floor, because `Error.prototype.toString` is
349
+ * `name: message` and `Data.TaggedError` sets `name` to the tag — so a fault that
350
+ * fills `message` renders as `'SomeTag: why'`, and one that does not renders as
351
+ * `'SomeTag'`. A caller wanting a better label sets `message` on its own error,
352
+ * where the knowledge belongs and where `Cause.pretty` and every other reader
353
+ * benefit from it too, rather than handing a table to a supervisor that would be
354
+ * the only thing to use it.
355
+ *
356
+ * The returned effect is re-runnable: the two supervision `Ref`s are created INSIDE
357
+ * it rather than here, so every run starts a fresh supervision session instead of
358
+ * inheriting the breaker state a previous run left behind.
359
+ */
360
+ exports.ProjectionStalled = ProjectionStalled;
361
+ const superviseOnProgress = (run, options) => {
362
+ // The three restart durations are TAKEN, not defaulted, while the COUNT keeps its
363
+ // default: `SuperviseOnProgressOptions` above argues that asymmetry in full.
364
+ const {
365
+ key,
366
+ progress,
367
+ restartMinDelay,
368
+ restartMaxDelay,
369
+ restartResetAfter
370
+ } = options;
371
+ const maxNoProgressRestarts = options.maxNoProgressRestarts ?? DEFAULT_MAX_NO_PROGRESS_RESTARTS;
372
+ const onRestart = options.onRestart ?? (() => _effect.Effect.void);
373
+ const onStalled = options.onStalled ?? (() => _effect.Effect.void);
374
+ /**
375
+ * The restart pacing: a capped exponential that NEVER TERMINATES on its own.
376
+ *
377
+ * `Schedule.union` continues while EITHER arm continues and uses the SHORTER
378
+ * of the two delays, so unioning an unbounded exponential with a fixed
379
+ * `spaced(restartMaxDelay)` yields exponential growth up to the cap and then
380
+ * a flat cap — with no `recurs` arm, because the give-up decision belongs to
381
+ * the circuit-breaker (which knows whether anything is progressing) and not
382
+ * to a schedule (which only knows how many times it has fired).
383
+ *
384
+ * `jittered` because restarts are correlated: a database blip restarts every
385
+ * projection in the deployment at the same instant, and an unjittered backoff
386
+ * would march them all back in lockstep.
387
+ *
388
+ * `resetAfter` keys on ELAPSED retrying time rather than on the length of the
389
+ * last run (`Schedule.elapsed`, verified against the installed source), which
390
+ * buys two things at once: a run that stays up for `restartResetAfter` starts
391
+ * its next backoff from `restartMinDelay` instead of inheriting a ceiling from
392
+ * a fault it recovered from hours ago, and a sustained outage sawtooths from
393
+ * `restartMinDelay` up to the cap and back rather than pinning at the cap for
394
+ * ever. The sawtooth is also what makes the no-progress budget computable —
395
+ * the arithmetic is on `DEFAULT_MAX_NO_PROGRESS_RESTARTS`, and a test pins it.
396
+ *
397
+ * Effect v3 caveat: this is the THREE-type-parameter `Schedule<Out, In, R>`
398
+ * that `effect@3` exports. The currently-published `Schedule` docs show a
399
+ * newer four-type-parameter shape — do not copy them; this is written against
400
+ * the installed type definitions.
401
+ */
402
+ const restartSchedule = _effect.Schedule.union(_effect.Schedule.exponential(restartMinDelay, 2), _effect.Schedule.spaced(restartMaxDelay)).pipe(_effect.Schedule.jittered, _effect.Schedule.resetAfter(restartResetAfter));
403
+ return _effect.Effect.gen(function* () {
404
+ // Supervision state, owned OUTSIDE the retried effect so it survives a
405
+ // restart — which is the whole point: the breaker's question is about the
406
+ // sequence of restarts, not about any one of them.
407
+ const noProgressRestarts = yield* _effect.Ref.make(0);
408
+ // The signal observed at the previous restart. Seeded with `ORIGIN` rather
409
+ // than with a read of `progress` (which is not read until the first
410
+ // failure): a read model resuming from a non-zero durable checkpoint
411
+ // therefore scores its FIRST failure as progress and gets one extra restart
412
+ // before the counter engages. That errs towards retrying, which is the safe
413
+ // direction for a breaker.
414
+ const lastRestartPosition = yield* _effect.Ref.make(_core.ORIGIN);
415
+ /**
416
+ * The circuit-breaker, evaluated on every failure and BEFORE the backoff
417
+ * sleep: does the progress signal show that anything is moving?
418
+ *
419
+ * Keying on the signal rather than on a failure count or on the error's tag
420
+ * is what makes competing runners behave — the module doc sets out that
421
+ * story in full. Conversely a poison event or a broken view store leaves the
422
+ * position pinned however many times the run restarts, so the breaker trips
423
+ * on exactly the cases retrying cannot fix.
424
+ */
425
+ const shouldRestart = reason => _effect.Effect.gen(function* () {
426
+ const previous = yield* _effect.Ref.get(lastRestartPosition);
427
+ // A view store too broken to answer `readCheckpoint` cannot report
428
+ // progress either, so a failed re-read is treated as "unchanged" and
429
+ // counts towards the breaker. That is the conservative reading: the
430
+ // alternative (retrying for ever because we cannot tell) is exactly the
431
+ // silent stall the breaker exists to end.
432
+ //
433
+ // `catchAll`, not `orElseSucceed`: the latter falls back on any cause
434
+ // carrying no defect, which includes INTERRUPTION, so a scope closing
435
+ // mid-re-read would be absorbed here — the daemon would go on to count a
436
+ // restart, log it and call `onRestart` while it was being torn down, and
437
+ // a teardown arriving on the last of the budget would report a
438
+ // `ProjectionStalled` that never happened — and now PAGE somebody about
439
+ // it, since `onStalled` fires on that branch. It is the same trap
440
+ // `catchAllDefect` avoids at the runner's stream boundary. `catchAll`
441
+ // touches only the typed `ProjectionStoreError`.
442
+ const stored = yield* _effect.Effect.catchAll(progress, () => _effect.Effect.succeed(previous));
443
+ const progressed = stored !== previous;
444
+ const restarts = progressed ? 0 : (yield* _effect.Ref.get(noProgressRestarts)) + 1;
445
+ yield* _effect.Ref.set(noProgressRestarts, restarts);
446
+ yield* _effect.Ref.set(lastRestartPosition, stored);
447
+ // ## The log line is the LOSSY channel; the hooks are the faithful one
448
+ //
449
+ // Both supervision outcomes now reach a programmatic consumer carrying
450
+ // the fault ITSELF — `onRestart` at the foot of this function,
451
+ // `onStalled` just below — so this block is no longer the only record of
452
+ // what happened. It is the convenience channel: flat, greppable, and
453
+ // reduced to JSON-safe scalars on purpose. Anything that needs a fault's
454
+ // FIELDS takes a hook; anything that needs a line in a log takes this.
455
+ //
456
+ // ## Why every value is JSON-SAFE, MEASURED rather than assumed
457
+ //
458
+ // The bound is serialisability, not stringiness: `noProgressRestarts`
459
+ // below goes out as a NUMBER, which no serialiser objects to and which a
460
+ // log aggregator can filter, threshold and graph on — properties a
461
+ // `String` around it would throw away in exchange for nothing. What has
462
+ // to be reduced is the value class a serialiser genuinely chokes on.
463
+ //
464
+ // A `bigint` is not JSON-serialisable and these faults carry them
465
+ // (`CheckpointSuperseded.expected` is a `Position`). What each logger
466
+ // `effect@3.22` ships actually does with one was established by running
467
+ // it, not read off the documentation:
468
+ //
469
+ // - `Logger.json` special-cases a TOP-LEVEL `bigint` annotation value
470
+ // (`internal/logger.ts`'s `structuredMessage`), but a `bigint` NESTED
471
+ // inside an annotation's object survives `Inspectable.toJSON` into the
472
+ // final `Inspectable.stringifyCircular`, which throws `TypeError: Do
473
+ // not know how to serialize a BigInt`. Nothing catches it, so the throw
474
+ // takes the WHOLE line — every other annotation with it — and lands as
475
+ // a defect inside the supervisor, on the one path whose entire job is
476
+ // to make failures visible.
477
+ // - The DEFAULT logger (`stringLogger`) does not throw: it renders each
478
+ // annotation through `Inspectable.toStringUnknown`, whose `try/catch`
479
+ // falls back to `String(value)`. The same loss, quieter — the offending
480
+ // annotation degrades to a bare tag, or to `[object Object]`.
481
+ // - `Logger.pretty` hands the value to `console.log` and prints `42n`.
482
+ //
483
+ // So the reduction happens HERE, before the logger sees anything.
484
+ // `position` is `String`ed rather than handed over as a `Position`
485
+ // despite all three shipped loggers coping with a bare `bigint`: they
486
+ // render it identically to `String`, so there is nothing to win, and a
487
+ // third-party logger that JSON-encodes its annotation map naively is a
488
+ // real thing.
489
+ //
490
+ // `reason` is `String`ed too, and that is the WHOLE reduction — no table
491
+ // of a caller's fault union, because the faults can label themselves.
492
+ // `Error.prototype.toString` is `name: message` and `Data.TaggedError`
493
+ // sets `name` to the tag, so a fault that fills `message` arrives as
494
+ // `'ProjectionStoreError: connection reset'` and one that does not arrives
495
+ // as its bare tag. This module is generic in `E`, so that is also all it
496
+ // could honestly do: the choice of what to say is the fault's, made once
497
+ // on the class, and `Cause.pretty` and `String(fault)` everywhere else
498
+ // read the same field.
499
+ const annotations = {
500
+ projection: key.projection,
501
+ partition: key.partition,
502
+ position: String(stored),
503
+ noProgressRestarts: restarts,
504
+ reason: String(reason)
505
+ };
506
+ if (restarts >= maxNoProgressRestarts) {
507
+ // Logged HERE, not by the caller, because a stalled projection must
508
+ // never be silent: `runProjection` returns an infallible effect, so
509
+ // unless somebody awaits the fibre the `ProjectionStalled` failure has
510
+ // no other route to an operator.
511
+ //
512
+ // `Effect.annotateLogs`, not a second argument to `logError`. Effect's
513
+ // log constructors are VARIADIC IN THE MESSAGE (`logError: (...message:
514
+ // ReadonlyArray<any>) => Effect<void>`), so an object passed second is
515
+ // a second message and not an annotation at all — it used to arrive as
516
+ // a JSON blob wedged inside the line rather than as the structured
517
+ // fields the reduction above was built for. Annotating puts each field
518
+ // where a logger can render it in its own idiom: `reason=… position=…`
519
+ // under the default logger, discrete keys under `annotations` in
520
+ // `Logger.json`. That is the logger owning the presentation, which is
521
+ // its job; all this module owes it is values it can serialise.
522
+ yield* _effect.Effect.annotateLogs(_effect.Effect.logError('kairos-es projection stalled: giving up after consecutive restarts with no checkpoint progress'), annotations);
523
+ // ...and then the SEAM, because a log line is not an alert. The
524
+ // give-up is the outcome nothing recovers from, so it gets the hook
525
+ // the restart already had — an application can fail a health check,
526
+ // exit non-zero or page from here. `onStalled`'s doc carries the
527
+ // asymmetry argument in full.
528
+ //
529
+ // AFTER the log, deliberately: the log is the guaranteed route out and
530
+ // a defect from an application's hook must not be able to silence it.
531
+ // The payload is the `ProjectionStalled` this give-up is about to
532
+ // produce, field for field and value for value — `stored` is what was
533
+ // just written to `lastRestartPosition`, `restarts` what was just
534
+ // written to `noProgressRestarts`, and `reason` is the failure the
535
+ // `catchAll` below will carry — so a hook and a caller awaiting the
536
+ // daemon never see two versions of one stall. The RAW fault rather
537
+ // than the log label, for the same reason the annotations take the
538
+ // label: a callback can read a `bigint`, a serialiser cannot.
539
+ yield* onStalled({
540
+ key,
541
+ noProgressRestarts: restarts,
542
+ position: stored,
543
+ reason
544
+ });
545
+ return false;
546
+ }
547
+ yield* _effect.Effect.annotateLogs(_effect.Effect.logWarning('kairos-es projection restarting from its stored checkpoint'), annotations);
548
+ yield* onRestart({
549
+ key,
550
+ reason,
551
+ noProgressRestarts: restarts
552
+ });
553
+ return true;
554
+ });
555
+ /**
556
+ * `Effect.retry`'s `while` IS the breaker, which is why the retry can exit
557
+ * in exactly one way: the schedule never terminates, so the only path out
558
+ * with a failure is `shouldRestart` returning `false`. The residual error is
559
+ * therefore always the give-up case, and mapping it to `ProjectionStalled`
560
+ * is total rather than a fallback for an unreachable branch.
561
+ *
562
+ * A run that *succeeds* ends the daemon quietly. For a projection that is
563
+ * the honest translation of "there is nothing left to read", which the
564
+ * shipped stores' live tails never say.
565
+ */
566
+ return yield* _effect.Effect.retry(run, {
567
+ schedule: restartSchedule,
568
+ while: shouldRestart
569
+ }).pipe(_effect.Effect.catchAll(reason => _effect.Effect.gen(function* () {
570
+ return yield* new ProjectionStalled({
571
+ key,
572
+ noProgressRestarts: yield* _effect.Ref.get(noProgressRestarts),
573
+ position: yield* _effect.Ref.get(lastRestartPosition),
574
+ reason
575
+ });
576
+ })));
577
+ });
578
+ };
579
+ exports.superviseOnProgress = superviseOnProgress;
580
+ //# sourceMappingURL=superviseOnProgress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"superviseOnProgress.js","names":["_core","require","_effect","MaxNoProgressRestarts","exports","Schema","Int","pipe","positive","brand","DEFAULT_RESTART_MIN_DELAY","DEFAULT_RESTART_MAX_DELAY","DEFAULT_RESTART_RESET_AFTER","DEFAULT_MAX_NO_PROGRESS_RESTARTS","make","ProjectionStalled","Data","TaggedError","message","noProgressRestarts","position","superviseOnProgress","run","options","key","progress","restartMinDelay","restartMaxDelay","restartResetAfter","maxNoProgressRestarts","onRestart","Effect","void","onStalled","restartSchedule","Schedule","union","exponential","spaced","jittered","resetAfter","gen","Ref","lastRestartPosition","ORIGIN","shouldRestart","reason","previous","get","stored","catchAll","succeed","progressed","restarts","set","annotations","projection","partition","String","annotateLogs","logError","logWarning","retry","schedule","while"],"sources":["../../src/superviseOnProgress.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}