@kairos-es/feed 0.0.0-stage → 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 (132) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +109 -2
  3. package/dist/cjs/Feed.js +6 -0
  4. package/dist/cjs/Feed.js.map +1 -0
  5. package/dist/cjs/FeedSink.js +54 -0
  6. package/dist/cjs/FeedSink.js.map +1 -0
  7. package/dist/cjs/FeedSyncStore.js +10 -0
  8. package/dist/cjs/FeedSyncStore.js.map +1 -0
  9. package/dist/cjs/deliver.js +86 -0
  10. package/dist/cjs/deliver.js.map +1 -0
  11. package/dist/cjs/errors.js +114 -0
  12. package/dist/cjs/errors.js.map +1 -0
  13. package/dist/cjs/identifiers.js +63 -0
  14. package/dist/cjs/identifiers.js.map +1 -0
  15. package/dist/cjs/inMemoryFeed.js +328 -0
  16. package/dist/cjs/inMemoryFeed.js.map +1 -0
  17. package/dist/cjs/inMemorySink.js +142 -0
  18. package/dist/cjs/inMemorySink.js.map +1 -0
  19. package/dist/cjs/index.js +184 -0
  20. package/dist/cjs/index.js.map +1 -0
  21. package/dist/cjs/inspect.js +68 -0
  22. package/dist/cjs/inspect.js.map +1 -0
  23. package/dist/cjs/readiness.js +100 -0
  24. package/dist/cjs/readiness.js.map +1 -0
  25. package/dist/cjs/reconcile.js +164 -0
  26. package/dist/cjs/reconcile.js.map +1 -0
  27. package/dist/cjs/runFeedSync.js +603 -0
  28. package/dist/cjs/runFeedSync.js.map +1 -0
  29. package/dist/cjs/tuning.js +60 -0
  30. package/dist/cjs/tuning.js.map +1 -0
  31. package/dist/dts-cjs/Feed.d.ts +99 -0
  32. package/dist/dts-cjs/Feed.d.ts.map +1 -0
  33. package/dist/dts-cjs/FeedSink.d.ts +189 -0
  34. package/dist/dts-cjs/FeedSink.d.ts.map +1 -0
  35. package/dist/dts-cjs/FeedSyncStore.d.ts +281 -0
  36. package/dist/dts-cjs/FeedSyncStore.d.ts.map +1 -0
  37. package/dist/dts-cjs/deliver.d.ts +16 -0
  38. package/dist/dts-cjs/deliver.d.ts.map +1 -0
  39. package/dist/dts-cjs/errors.d.ts +131 -0
  40. package/dist/dts-cjs/errors.d.ts.map +1 -0
  41. package/dist/dts-cjs/identifiers.d.ts +67 -0
  42. package/dist/dts-cjs/identifiers.d.ts.map +1 -0
  43. package/dist/dts-cjs/inMemoryFeed.d.ts +32 -0
  44. package/dist/dts-cjs/inMemoryFeed.d.ts.map +1 -0
  45. package/dist/dts-cjs/inMemorySink.d.ts +61 -0
  46. package/dist/dts-cjs/inMemorySink.d.ts.map +1 -0
  47. package/dist/dts-cjs/index.d.ts +63 -0
  48. package/dist/dts-cjs/index.d.ts.map +1 -0
  49. package/dist/dts-cjs/inspect.d.ts +71 -0
  50. package/dist/dts-cjs/inspect.d.ts.map +1 -0
  51. package/dist/dts-cjs/package.json +3 -0
  52. package/dist/dts-cjs/readiness.d.ts +84 -0
  53. package/dist/dts-cjs/readiness.d.ts.map +1 -0
  54. package/dist/dts-cjs/reconcile.d.ts +68 -0
  55. package/dist/dts-cjs/reconcile.d.ts.map +1 -0
  56. package/dist/dts-cjs/runFeedSync.d.ts +220 -0
  57. package/dist/dts-cjs/runFeedSync.d.ts.map +1 -0
  58. package/dist/dts-cjs/tuning.d.ts +40 -0
  59. package/dist/dts-cjs/tuning.d.ts.map +1 -0
  60. package/dist/dts-esm/Feed.d.ts +99 -0
  61. package/dist/dts-esm/Feed.d.ts.map +1 -0
  62. package/dist/dts-esm/FeedSink.d.ts +189 -0
  63. package/dist/dts-esm/FeedSink.d.ts.map +1 -0
  64. package/dist/dts-esm/FeedSyncStore.d.ts +281 -0
  65. package/dist/dts-esm/FeedSyncStore.d.ts.map +1 -0
  66. package/dist/dts-esm/deliver.d.ts +16 -0
  67. package/dist/dts-esm/deliver.d.ts.map +1 -0
  68. package/dist/dts-esm/errors.d.ts +131 -0
  69. package/dist/dts-esm/errors.d.ts.map +1 -0
  70. package/dist/dts-esm/identifiers.d.ts +67 -0
  71. package/dist/dts-esm/identifiers.d.ts.map +1 -0
  72. package/dist/dts-esm/inMemoryFeed.d.ts +32 -0
  73. package/dist/dts-esm/inMemoryFeed.d.ts.map +1 -0
  74. package/dist/dts-esm/inMemorySink.d.ts +61 -0
  75. package/dist/dts-esm/inMemorySink.d.ts.map +1 -0
  76. package/dist/dts-esm/index.d.ts +63 -0
  77. package/dist/dts-esm/index.d.ts.map +1 -0
  78. package/dist/dts-esm/inspect.d.ts +71 -0
  79. package/dist/dts-esm/inspect.d.ts.map +1 -0
  80. package/dist/dts-esm/package.json +3 -0
  81. package/dist/dts-esm/readiness.d.ts +84 -0
  82. package/dist/dts-esm/readiness.d.ts.map +1 -0
  83. package/dist/dts-esm/reconcile.d.ts +68 -0
  84. package/dist/dts-esm/reconcile.d.ts.map +1 -0
  85. package/dist/dts-esm/runFeedSync.d.ts +220 -0
  86. package/dist/dts-esm/runFeedSync.d.ts.map +1 -0
  87. package/dist/dts-esm/tuning.d.ts +40 -0
  88. package/dist/dts-esm/tuning.d.ts.map +1 -0
  89. package/dist/esm/Feed.js +2 -0
  90. package/dist/esm/Feed.js.map +1 -0
  91. package/dist/esm/FeedSink.js +46 -0
  92. package/dist/esm/FeedSink.js.map +1 -0
  93. package/dist/esm/FeedSyncStore.js +3 -0
  94. package/dist/esm/FeedSyncStore.js.map +1 -0
  95. package/dist/esm/deliver.js +78 -0
  96. package/dist/esm/deliver.js.map +1 -0
  97. package/dist/esm/errors.js +98 -0
  98. package/dist/esm/errors.js.map +1 -0
  99. package/dist/esm/identifiers.js +56 -0
  100. package/dist/esm/identifiers.js.map +1 -0
  101. package/dist/esm/inMemoryFeed.js +320 -0
  102. package/dist/esm/inMemoryFeed.js.map +1 -0
  103. package/dist/esm/inMemorySink.js +134 -0
  104. package/dist/esm/inMemorySink.js.map +1 -0
  105. package/dist/esm/index.js +62 -0
  106. package/dist/esm/index.js.map +1 -0
  107. package/dist/esm/inspect.js +58 -0
  108. package/dist/esm/inspect.js.map +1 -0
  109. package/dist/esm/package.json +4 -0
  110. package/dist/esm/readiness.js +92 -0
  111. package/dist/esm/readiness.js.map +1 -0
  112. package/dist/esm/reconcile.js +153 -0
  113. package/dist/esm/reconcile.js.map +1 -0
  114. package/dist/esm/runFeedSync.js +595 -0
  115. package/dist/esm/runFeedSync.js.map +1 -0
  116. package/dist/esm/tuning.js +51 -0
  117. package/dist/esm/tuning.js.map +1 -0
  118. package/package.json +34 -3
  119. package/src/Feed.ts +111 -0
  120. package/src/FeedSink.ts +130 -0
  121. package/src/FeedSyncStore.ts +326 -0
  122. package/src/deliver.ts +108 -0
  123. package/src/errors.ts +134 -0
  124. package/src/identifiers.ts +92 -0
  125. package/src/inMemoryFeed.ts +582 -0
  126. package/src/inMemorySink.ts +215 -0
  127. package/src/index.ts +136 -0
  128. package/src/inspect.ts +114 -0
  129. package/src/readiness.ts +137 -0
  130. package/src/reconcile.ts +224 -0
  131. package/src/runFeedSync.ts +882 -0
  132. package/src/tuning.ts +65 -0
@@ -0,0 +1,603 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.runFeedSync = void 0;
7
+ var _core = require("@kairos-es/core");
8
+ var _effect = require("effect");
9
+ var _deliver = require("./deliver.js");
10
+ var _errors = require("./errors.js");
11
+ var _FeedSink = require("./FeedSink.js");
12
+ var _identifiers = require("./identifiers.js");
13
+ var _reconcile = require("./reconcile.js");
14
+ var _tuning = require("./tuning.js");
15
+ /**
16
+ * `runFeedSync` — the sync worker: one per feed and sink, holding the lease,
17
+ * copying the feed's rows to the sink and moving its cursor only once the sink
18
+ * confirms them (ADR-0019).
19
+ *
20
+ * ## The lifecycle
21
+ *
22
+ * A worker runs in SESSIONS. A session takes the lease, then loops:
23
+ *
24
+ * 1. Read the cursor and the feed's generation. No cursor, or a cursor at another
25
+ * generation, means the sink holds a copy of something the feed no longer is,
26
+ * so the session RESYNCS (below) and loops.
27
+ * 2. Otherwise it TAILS: the rows after the cursor, through core's generic
28
+ * subscribe machine with `emit: 'page'`, so each page is sent as it is read and
29
+ * the worker holds one page in memory, never the backlog. Each page is checked
30
+ * against the generation first, and so is every poll interval on its own, since
31
+ * a reset that writes nothing yields no page; a generation that moved since the
32
+ * cursor was saved ends the tail, and step 1 sees the mismatch.
33
+ *
34
+ * Every page goes through one delivery: send, park the keys the sink refuses with
35
+ * its reasons, send the rest again, confirm. Then ONE compare-and-set moves the
36
+ * cursor past the whole page and moves the parked list with it — the refused keys
37
+ * parked, every key that was taken unparked — and fenced on the lease holder, so a
38
+ * worker that lost its lease moves nothing. Then tombstones every sink has passed
39
+ * are collected.
40
+ *
41
+ * The rules that sequence keeps, each a consequence of a sink not being
42
+ * transactional:
43
+ *
44
+ * - **The cursor moves only after `confirm`.** A crash between `apply` and
45
+ * `confirm` resends the batch; a sink is idempotent, so that costs a resend and
46
+ * nothing else.
47
+ * - **A refused key does not hold the feed up.** It is parked and the cursor moves
48
+ * past it. It is retried when its row next changes, because a change gives the
49
+ * row a `seq` above the cursor, and it leaves the parked list once it is sent
50
+ * successfully — a tombstone for a parked key included, which is how a refused
51
+ * record that is then deleted stops being parked without anyone acting.
52
+ * - **A retryable failure keeps the cursor.** The session ends, and supervision
53
+ * (below) starts a new one after a back-off no shorter than the sink's
54
+ * `retryAfter`; the new session sends the same page again.
55
+ *
56
+ * ## Resync
57
+ *
58
+ * Every row, page by page from the origin, goes into `resync.begin`'s fresh
59
+ * target; then the generation is checked once more, the target is published over
60
+ * the live copy, and the cursor is saved at the new generation with the parked list
61
+ * REPLACED by the refusals the resync met. Resync progress is held in memory only,
62
+ * so a crash restarts it from the origin, with a new `begin` that drops the
63
+ * half-built target the crash left.
64
+ *
65
+ * ### Waiting for the materialisation
66
+ *
67
+ * The feed's end is not the rebuild's end. After a reset the materialisation
68
+ * replays the log and writes the feed as it goes, so a resync that reaches the
69
+ * feed's last row has reached wherever the replay had got to, and publishing there
70
+ * swaps a complete live copy for a partial one. `readiness.ts` owns that argument.
71
+ * So `runFeedSync` takes an optional `readiness`: asked once as each attempt
72
+ * starts, it hands back a probe, and the attempt does not publish until the probe
73
+ * says the materialisation has caught up. Until then the attempt polls: once per
74
+ * poll interval it checks the generation, abandoning the attempt as stale if a
75
+ * reset moved it again, asks the probe, and reads the feed on from where it left
76
+ * off, loading what the replay wrote meanwhile. The probe is asked BEFORE that
77
+ * read, so the read that follows a `true` sees every row the materialisation had
78
+ * committed by the time it answered, since a feed row commits with the checkpoint
79
+ * that produced it. The lease is still checked immediately before `publish`, and
80
+ * the generation once more just before that, however long the wait was.
81
+ *
82
+ * With no `readiness` the attempt publishes at the feed's end, which is exactly the
83
+ * behaviour above, and search can serve a PARTIAL copy for as long as the replay
84
+ * takes. An in-place rebuild has no publish; there the wait holds the closing
85
+ * reconcile and the cursor save instead, so the reconcile never deletes a record
86
+ * the replay has not reached yet. Without a `readiness` an in-place rebuild
87
+ * reconciles at the feed's end too, and so deletes from the live copy every record
88
+ * the replay had not yet written again; the tail sends each back as the replay
89
+ * reaches it.
90
+ *
91
+ * The wait is UNBOUNDED. A probe that never says yes holds the attempt for good,
92
+ * with no error and no restart, because publishing early is the very failure the
93
+ * wait exists to prevent and no timeout could tell a stuck replay from a long one.
94
+ * It is made visible instead: `inspectFeed` reports the sink `resyncDue` for as long
95
+ * as it lasts, since the cursor stays at the old generation, and every
96
+ * `HELD_WAIT_WARN_POLLS` poll intervals (`tuning.ts`) the attempt logs a warning,
97
+ * "feed sync rebuild held on readiness", with the feed, the sink, the generation,
98
+ * the polls so far and how long it has been held.
99
+ *
100
+ * Each `begin` names a target of its own, never one an earlier attempt used, and
101
+ * that is what bounds a lease lapse here. A worker that has lost its lease can
102
+ * still land one call after the new holder's (`FeedSyncStore.ts` says when). Were
103
+ * the target named after its generation alone, that call could be a `publish` of
104
+ * the NEW holder's half-built target, which then goes on loading into a fresh one
105
+ * holding only the later pages, publishes it, and saves its cursor at the head:
106
+ * whole pages lost, and nothing to bring them back. With a target per attempt, a
107
+ * stale `publish` moves only the stale worker's own target — or fails, retryably,
108
+ * when a newer `begin` has dropped it — and a stale `begin` drops the new holder's
109
+ * target, whose `publish` then fails retryably and whose resync starts again.
110
+ * What a stale `publish` of its own target still costs is `FeedSyncStore.ts`'s to
111
+ * say, with the rest of the lease's window.
112
+ *
113
+ * `begin`, `load`, `apply` and `publish` each run behind `ensureHeld`; `confirm`
114
+ * and a reconcile's key listing change nothing, and do not.
115
+ *
116
+ * Tombstones are sent as deletes, although the target starts empty. The feed does
117
+ * not stand still while a resync pages through it: a key loaded on an early page
118
+ * can be deleted by the read model before the resync is done, and its tombstone
119
+ * then sits at a `seq` a LATER page reads. Dropping it would load the key, save
120
+ * the cursor past its delete, and publish a target that holds a record the feed no
121
+ * longer has, with nothing ever coming to remove it. A tombstone for a key the
122
+ * target never held costs a send and changes nothing, since a delete of an absent
123
+ * key is a no-op by the sink laws.
124
+ *
125
+ * The same movement decides the parked list. A key can be met more than once in
126
+ * one resync — refused on an early page, then changed and met again later — so
127
+ * the list saved is what each key's LAST change in the resync came to: parked if
128
+ * that change was refused, absent if it was taken. Keeping every refusal the
129
+ * resync met would leave parked a key the target holds, or one the feed deleted.
130
+ *
131
+ * A sink with no `resync` is resynced IN PLACE: every row is sent to the live copy,
132
+ * live rows as upserts and tombstones as deletes, and then a reconcile deletes what
133
+ * the feed no longer holds. The live copy serves a mixture of old and new from the
134
+ * first page on, which is the price of a target with no atomic swap. Its refusals
135
+ * land in the parked list it saves like any resync's, with two consequences of
136
+ * writing over the live copy: a refused upsert leaves the PREVIOUS generation's
137
+ * document of that key live, and an orphan whose delete the closing reconcile was
138
+ * refused stays live too. Both are parked with the sink's reason, the orphan at the
139
+ * origin `seq` since it has no row, and the closing reconcile's report goes to
140
+ * `onReconcile` like a scheduled one's. A sink with neither part could never be
141
+ * rebuilt, and is refused at construction.
142
+ *
143
+ * ## Supervision
144
+ *
145
+ * Every failure a session meets ends it, and the worker's own loop decides what
146
+ * next. It follows `@kairos-es/automation`'s daemon rather than generalising the
147
+ * read side's supervisor, whose own doc says a progress signal the caller owns does
148
+ * not qualify — and the signal here, a cursor advance, is the worker's own.
149
+ *
150
+ * - `LeaseHeld`: another worker is running. This one waits a poll interval and
151
+ * tries again, and the wait is not a failure: a standby worker is a healthy one,
152
+ * and it takes over within a poll interval of the first worker's lease going.
153
+ * - Anything else: a back-off of `restartMinDelay` doubling to `restartMaxDelay`,
154
+ * scaled by a jitter drawn from `Random` so a fleet of workers does not retry in
155
+ * lockstep, and never shorter than a `SinkRetryable`'s `retryAfter`. A restart
156
+ * that follows no cursor advance since the previous restart counts towards
157
+ * `maxNoProgressRestarts`; reaching it ends the worker with `FeedSyncStalled`,
158
+ * for the caller to report. Any advance resets the count and the curve.
159
+ *
160
+ * Defects are not caught: they are bugs, in the driver or in a sink or store that
161
+ * should have classified its fault, and supervision must not retry a bug.
162
+ *
163
+ * ## The scope
164
+ *
165
+ * The worker never returns. Run it in a fibre of its own beside the projections,
166
+ * and close the scope to stop it: each session runs in a child scope of the
167
+ * caller's, so closing the caller's scope releases the lease.
168
+ */
169
+
170
+ /** The tail's signal that the generation moved under it. Never leaves a session. */
171
+ class GenerationMoved extends /*#__PURE__*/_effect.Data.TaggedError('GenerationMoved') {}
172
+ /** The jitter range applied to every restart back-off. */
173
+ const JITTER_LOW = 0.8;
174
+ const JITTER_HIGH = 1.2;
175
+ /**
176
+ * Run the sync worker for `options.source` and `options.sink`; see the module doc.
177
+ * Dies at once, before taking the lease, on a sink that has neither `resync` nor
178
+ * `reconcile`, on a `reconcileEvery` for a sink with no `reconcile`, and on tuning
179
+ * that is out of range.
180
+ */
181
+ const runFeedSync = options => _effect.Effect.gen(function* () {
182
+ const config = yield* _effect.Effect.sync(() => resolveOptions(options));
183
+ const parent = yield* _effect.Effect.scope;
184
+ const progress = yield* _effect.Ref.make(0);
185
+ const id = {
186
+ feed: options.source.feed,
187
+ sink: options.sink.name
188
+ };
189
+ let seen = 0;
190
+ let restarts = 0;
191
+ for (;;) {
192
+ const child = yield* _effect.Scope.fork(parent);
193
+ const failure = yield* _effect.Effect.flip(session(options, config, progress).pipe(_effect.Scope.use(child)));
194
+ if (failure._tag === 'LeaseHeld') {
195
+ yield* _effect.Effect.sleep(config.pollInterval);
196
+ continue;
197
+ }
198
+ const advanced = yield* _effect.Ref.get(progress);
199
+ if (advanced !== seen) {
200
+ seen = advanced;
201
+ restarts = 0;
202
+ }
203
+ restarts += 1;
204
+ if (restarts >= config.maxNoProgressRestarts) {
205
+ yield* _effect.Effect.logError('feed sync stalled', {
206
+ feed: id.feed,
207
+ sink: id.sink,
208
+ restarts,
209
+ reason: String(failure)
210
+ });
211
+ return yield* new _errors.FeedSyncStalled({
212
+ ...id,
213
+ restarts,
214
+ reason: failure
215
+ });
216
+ }
217
+ const backoff = yield* restartDelay(config, restarts);
218
+ const delay = failure._tag === 'SinkRetryable' && failure.retryAfter !== undefined ? _effect.Duration.max(backoff, failure.retryAfter) : backoff;
219
+ yield* _effect.Effect.logWarning('feed sync restarting', {
220
+ feed: id.feed,
221
+ sink: id.sink,
222
+ restarts,
223
+ delayMillis: _effect.Duration.toMillis(delay),
224
+ reason: String(failure)
225
+ });
226
+ if (config.onRestart !== undefined) {
227
+ yield* config.onRestart({
228
+ ...id,
229
+ restarts,
230
+ delay,
231
+ reason: failure
232
+ });
233
+ }
234
+ yield* _effect.Effect.sleep(delay);
235
+ }
236
+ });
237
+ exports.runFeedSync = runFeedSync;
238
+ const resolveOptions = options => {
239
+ const where = `runFeedSync(feed '${options.source.feed}', sink '${options.sink.name}')`;
240
+ const {
241
+ resync,
242
+ reconcile
243
+ } = options.sink;
244
+ const rebuild = rebuildOf(where, resync, reconcile);
245
+ if (options.reconcileEvery !== undefined && reconcile === undefined) {
246
+ throw new Error(`${where}: reconcileEvery is set, and the sink has no reconcile`);
247
+ }
248
+ const tuning = options.tuning ?? {};
249
+ const pageSize = tuning.pageSize ?? _tuning.DEFAULT_FEED_PAGE_SIZE;
250
+ const limit = (0, _tuning.readLimit)(pageSize);
251
+ const maxNoProgressRestarts = tuning.maxNoProgressRestarts ?? _tuning.DEFAULT_FEED_MAX_NO_PROGRESS_RESTARTS;
252
+ if (!Number.isInteger(maxNoProgressRestarts) || maxNoProgressRestarts <= 0) {
253
+ throw new Error(`${where}: maxNoProgressRestarts must be a positive integer`);
254
+ }
255
+ const pollInterval = (0, _tuning.positiveDuration)('pollInterval', tuning.pollInterval ?? _tuning.DEFAULT_FEED_POLL_INTERVAL);
256
+ const restartMinDelay = (0, _tuning.positiveDuration)('restartMinDelay', tuning.restartMinDelay ?? _tuning.DEFAULT_FEED_RESTART_MIN_DELAY);
257
+ const restartMaxDelay = (0, _tuning.positiveDuration)('restartMaxDelay', tuning.restartMaxDelay ?? _tuning.DEFAULT_FEED_RESTART_MAX_DELAY);
258
+ return {
259
+ pollInterval,
260
+ pageSize,
261
+ limit,
262
+ restartMinDelay,
263
+ restartMaxDelay,
264
+ maxNoProgressRestarts,
265
+ onRestart: tuning.onRestart,
266
+ onReconcile: tuning.onReconcile,
267
+ rebuild,
268
+ periodic: options.reconcileEvery === undefined || reconcile === undefined ? _effect.Option.none() : _effect.Option.some({
269
+ every: (0, _tuning.positiveDuration)('reconcileEvery', options.reconcileEvery),
270
+ reconcile
271
+ }),
272
+ readiness: _effect.Option.fromUndefinedOr(options.readiness)
273
+ };
274
+ };
275
+ /** The sink's {@link Rebuild}, refusing a sink that could never be rebuilt. */
276
+ const rebuildOf = (where, resync, reconcile) => {
277
+ if (resync !== undefined) {
278
+ return {
279
+ _tag: 'Swap',
280
+ resync
281
+ };
282
+ }
283
+ if (reconcile !== undefined) {
284
+ return {
285
+ _tag: 'InPlace',
286
+ reconcile
287
+ };
288
+ }
289
+ throw new Error(`${where}: the sink has neither resync nor reconcile, so it could never be rebuilt after a reset`);
290
+ };
291
+ /**
292
+ * The delay before restart `n` (1-based): `min(cap, min × 2^(n-1))`, scaled by a
293
+ * jitter drawn from `Random`, so a seeded `Random` makes it repeat.
294
+ */
295
+ const restartDelay = (config, n) => _effect.Effect.map(_effect.Random.nextBetween(JITTER_LOW, JITTER_HIGH), jitter => {
296
+ const raw = _effect.Duration.toMillis(config.restartMinDelay) * 2 ** (n - 1);
297
+ const capped = Math.min(raw, _effect.Duration.toMillis(config.restartMaxDelay));
298
+ return _effect.Duration.millis(Math.round(capped * jitter));
299
+ });
300
+ /**
301
+ * Read the feed's generation and say whether it is still `held`. The ONE place the
302
+ * driver asks whether a reset has happened: before choosing to tail or resync, per
303
+ * tail page, per poll interval of the tail's watch, per poll interval of a resync
304
+ * waiting on its readiness, and before a resync publishes.
305
+ * A `held` of `None` — no cursor yet — counts as moved, since the sink then holds
306
+ * a copy of no generation at all.
307
+ */
308
+ const checkGeneration = (source, held) => _effect.Effect.map(source.generation, current => ({
309
+ current,
310
+ moved: _effect.Option.isNone(held) || held.value !== current
311
+ }));
312
+ const makeSessionContext = (options, config, progress, lease) => {
313
+ const {
314
+ source,
315
+ sink,
316
+ store
317
+ } = options;
318
+ return {
319
+ source,
320
+ sink,
321
+ config,
322
+ lease,
323
+ deliverRows: (send, rows) => (0, _deliver.deliver)({
324
+ send,
325
+ confirm: sink.confirm,
326
+ lease,
327
+ pending: _effect.Array.map(rows, row => ({
328
+ change: (0, _FeedSink.changeOf)(row),
329
+ seq: row.seq
330
+ }))
331
+ }),
332
+ advance: args => _effect.Effect.gen(function* () {
333
+ yield* store.casCursor({
334
+ feed: source.feed,
335
+ sink: sink.name,
336
+ holder: lease.holder,
337
+ expected: args.expected,
338
+ next: args.next,
339
+ parked: args.parked
340
+ });
341
+ yield* _effect.Ref.update(progress, n => n + 1);
342
+ yield* store.collectTombstones(source.feed);
343
+ }),
344
+ unparkGone: isGone => (0, _reconcile.unparkGone)(store, {
345
+ feed: source.feed,
346
+ sink: sink.name
347
+ }, isGone),
348
+ reconcile: reconcile => (0, _reconcile.reconcileWith)({
349
+ source,
350
+ sink,
351
+ reconcile,
352
+ lease,
353
+ pageSize: config.pageSize
354
+ })
355
+ };
356
+ };
357
+ /** One session: the lease, then resync or tail, forever, until something fails. */
358
+ const session = (options, config, progress) => _effect.Effect.gen(function* () {
359
+ const id = {
360
+ feed: options.source.feed,
361
+ sink: options.sink.name
362
+ };
363
+ const lease = yield* options.store.acquireLease(id);
364
+ const ctx = makeSessionContext(options, config, progress, lease);
365
+ const step = _effect.Effect.gen(function* () {
366
+ const cursor = yield* options.store.readCursor(id);
367
+ const check = yield* checkGeneration(ctx.source, _effect.Option.map(cursor, c => c.generation));
368
+ if (_effect.Option.isSome(cursor) && !check.moved) {
369
+ return yield* tail(ctx, cursor.value).pipe(_effect.Effect.catchTag('GenerationMoved', () => _effect.Effect.void));
370
+ }
371
+ return yield* resync(ctx, {
372
+ generation: check.current,
373
+ previous: cursor
374
+ });
375
+ });
376
+ return yield* _effect.Effect.forever(step);
377
+ });
378
+ /** The tail: pages after the cursor, each delivered then advanced past. */
379
+ const tail = (ctx, start) => _effect.Effect.gen(function* () {
380
+ const {
381
+ source,
382
+ sink,
383
+ config
384
+ } = ctx;
385
+ const current = yield* _effect.Ref.make(start);
386
+ // Fails `GenerationMoved` once the feed has left the cursor's generation.
387
+ const ensureGeneration = _effect.Effect.gen(function* () {
388
+ const cursor = yield* _effect.Ref.get(current);
389
+ const check = yield* checkGeneration(source, _effect.Option.some(cursor.generation));
390
+ if (check.moved) {
391
+ return yield* new GenerationMoved();
392
+ }
393
+ return cursor;
394
+ });
395
+ const onPage = rows => _effect.Effect.gen(function* () {
396
+ const cursor = yield* ensureGeneration;
397
+ const delivered = yield* ctx.deliverRows(sink.apply, rows);
398
+ const next = {
399
+ generation: cursor.generation,
400
+ seq: _effect.Array.lastNonEmpty(rows).seq
401
+ };
402
+ yield* ctx.advance({
403
+ expected: _effect.Option.some(cursor),
404
+ next,
405
+ parked: {
406
+ _tag: 'Amend',
407
+ park: delivered.parked,
408
+ unpark: delivered.sent
409
+ }
410
+ });
411
+ yield* _effect.Ref.set(current, next);
412
+ });
413
+ // A periodic reconcile parks nothing of its own: an orphan it could not delete
414
+ // is still in the copy, so the next period finds it and tries again, and the
415
+ // report says so meanwhile. What it does owe the parked list is every park of a
416
+ // key now gone from the sink, or `inspectFeed` would go on reporting a refusal
417
+ // for a record that is no longer there; `unparkGone` says why its failure only
418
+ // warns.
419
+ const onReconcile = reconcile => _effect.Effect.gen(function* () {
420
+ const {
421
+ report,
422
+ isGone
423
+ } = yield* ctx.reconcile(reconcile);
424
+ if (config.onReconcile !== undefined) {
425
+ yield* config.onReconcile(report);
426
+ }
427
+ yield* ctx.unparkGone(isGone);
428
+ });
429
+ const pages = (0, _core.subscribeItems)({
430
+ fetch: source.changesAfter,
431
+ cursorOf: row => row.seq,
432
+ initial: start.seq,
433
+ ...(source.listen === undefined ? {} : {
434
+ listen: source.listen
435
+ }),
436
+ pollInterval: _effect.Duration.toMillis(config.pollInterval),
437
+ catchUpPageSize: config.pageSize,
438
+ emit: 'page'
439
+ }).pipe(_effect.Stream.chunks, _effect.Stream.map(rows => ({
440
+ _tag: 'Page',
441
+ rows
442
+ })));
443
+ // A reset that writes no row would otherwise go unseen: the tail checks the
444
+ // generation per PAGE, and an emptied feed yields no page. So the generation is
445
+ // also read once per poll interval on its own, and a move ends the tail.
446
+ const generationWatch = _effect.Stream.tick(config.pollInterval).pipe(_effect.Stream.drop(1), _effect.Stream.as({
447
+ _tag: 'Watch'
448
+ }));
449
+ const reconcileTicks = _effect.Option.match(config.periodic, {
450
+ onNone: () => _effect.Stream.empty,
451
+ onSome: ({
452
+ every,
453
+ reconcile
454
+ }) => _effect.Stream.tick(every).pipe(
455
+ // `tick` fires once at the start; a reconcile is due one period in.
456
+ _effect.Stream.drop(1), _effect.Stream.as({
457
+ _tag: 'Reconcile',
458
+ reconcile
459
+ }))
460
+ });
461
+ // One consumer, so a page, a watch and a reconcile never overlap: nothing else
462
+ // sends while a reconcile runs, which is what lets it run under this lease.
463
+ yield* _effect.Stream.runForEach(_effect.Stream.merge(pages, _effect.Stream.merge(generationWatch, reconcileTicks)), event => event._tag === 'Page' ? onPage(event.rows) : event._tag === 'Watch' ? _effect.Effect.asVoid(ensureGeneration) : onReconcile(event.reconcile));
464
+ });
465
+ /** What a swap's `publish` sent: no record at all. */
466
+ const NOTHING_SENT = {
467
+ sent: [],
468
+ parked: []
469
+ };
470
+ const startRebuild = (ctx, generation) => {
471
+ const {
472
+ rebuild
473
+ } = ctx.config;
474
+ if (rebuild._tag === 'InPlace') {
475
+ return _effect.Effect.succeed({
476
+ send: ctx.sink.apply,
477
+ finish: _effect.Effect.gen(function* () {
478
+ const {
479
+ report
480
+ } = yield* ctx.reconcile(rebuild.reconcile);
481
+ if (ctx.config.onReconcile !== undefined) {
482
+ yield* ctx.config.onReconcile(report);
483
+ }
484
+ // An orphan has no row, so its park carries the origin as its `seq`: there
485
+ // is no change of it to point at, and `retryParked` on a key with no row
486
+ // simply unparks it.
487
+ const parkedAt = yield* _effect.DateTime.now;
488
+ return {
489
+ sent: report.deleted,
490
+ parked: report.refused.map(({
491
+ key,
492
+ reason
493
+ }) => ({
494
+ key,
495
+ seq: _identifiers.FEED_ORIGIN,
496
+ reason,
497
+ parkedAt
498
+ }))
499
+ };
500
+ })
501
+ });
502
+ }
503
+ const {
504
+ resync
505
+ } = rebuild;
506
+ return _effect.Effect.gen(function* () {
507
+ // `begin` destroys: it empties a target and drops every other one, which can
508
+ // be the half-built target of the worker that holds the lease now. So it is a
509
+ // send like any other, and runs only under a lease just seen held.
510
+ yield* ctx.lease.ensureHeld;
511
+ const target = yield* resync.begin(generation);
512
+ return {
513
+ send: batch => resync.load(target, batch),
514
+ finish: _effect.Effect.as(_effect.Effect.andThen(ctx.lease.ensureHeld, resync.publish(target)), NOTHING_SENT)
515
+ };
516
+ });
517
+ };
518
+ /** Always ready: the probe of a worker given no `readiness`. */
519
+ const ALWAYS_READY = /*#__PURE__*/_effect.Effect.succeed(true);
520
+ /** A full resync at `generation`; see the module doc. */
521
+ const resync = (ctx, at) => _effect.Effect.gen(function* () {
522
+ const {
523
+ source,
524
+ config
525
+ } = ctx;
526
+ // Asked before anything is read, so the point the probe judges by is fixed
527
+ // before the first page: see the module doc's "Waiting for the materialisation".
528
+ const ready = yield* _effect.Option.match(config.readiness, {
529
+ onNone: () => _effect.Effect.succeed(ALWAYS_READY),
530
+ onSome: readiness => readiness(at.generation)
531
+ });
532
+ const rebuilding = yield* startRebuild(ctx, at.generation);
533
+ const stale = _effect.Effect.map(checkGeneration(source, _effect.Option.some(at.generation)), check => check.moved);
534
+ // By key, so a later page's outcome for a key replaces an earlier one's: the
535
+ // list saved is what each key's LAST change in this resync came to.
536
+ const parked = new Map();
537
+ const settle = delivered => {
538
+ for (const sent of delivered.sent) {
539
+ parked.delete(sent);
540
+ }
541
+ for (const record of delivered.parked) {
542
+ parked.set(record.key, record);
543
+ }
544
+ };
545
+ let last = _identifiers.FEED_ORIGIN;
546
+ const heldSince = yield* _effect.Clock.currentTimeMillis;
547
+ let polls = 0;
548
+ for (;;) {
549
+ // Before the read, so a `true` covers every row the read then finds.
550
+ const caughtUp = yield* ready;
551
+ for (;;) {
552
+ const rows = yield* source.changesAfter(last, config.limit);
553
+ if (!_effect.Array.isReadonlyArrayNonEmpty(rows)) {
554
+ break;
555
+ }
556
+ // Tombstones included: see the module doc's Resync section.
557
+ settle(yield* ctx.deliverRows(rebuilding.send, rows));
558
+ last = _effect.Array.lastNonEmpty(rows).seq;
559
+ if (rows.length < config.pageSize) {
560
+ break;
561
+ }
562
+ }
563
+ if (caughtUp) {
564
+ break;
565
+ }
566
+ yield* _effect.Effect.sleep(config.pollInterval);
567
+ if (yield* stale) {
568
+ // Reset again while this waited: abandon the attempt, as below.
569
+ return;
570
+ }
571
+ polls += 1;
572
+ // The wait has no bound — see the module doc — so a probe that never says
573
+ // yes must at least be seen not to. Every string or number, never a bigint,
574
+ // so a logger that JSON-encodes the line keeps it.
575
+ if (polls % _tuning.HELD_WAIT_WARN_POLLS === 0) {
576
+ yield* _effect.Effect.logWarning('feed sync rebuild held on readiness', {
577
+ feed: source.feed,
578
+ sink: ctx.sink.name,
579
+ generation: String(at.generation),
580
+ polls,
581
+ heldMillis: (yield* _effect.Clock.currentTimeMillis) - heldSince
582
+ });
583
+ }
584
+ }
585
+ if (yield* stale) {
586
+ // Reset again while this ran: what was loaded is already stale. Leave the
587
+ // cursor alone, and the session's next step starts the resync over.
588
+ return;
589
+ }
590
+ settle(yield* rebuilding.finish);
591
+ yield* ctx.advance({
592
+ expected: at.previous,
593
+ next: {
594
+ generation: at.generation,
595
+ seq: last
596
+ },
597
+ parked: {
598
+ _tag: 'Replace',
599
+ park: Array.from(parked.values())
600
+ }
601
+ });
602
+ });
603
+ //# sourceMappingURL=runFeedSync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runFeedSync.js","names":["_core","require","_effect","_deliver","_errors","_FeedSink","_identifiers","_reconcile","_tuning","GenerationMoved","Data","TaggedError","JITTER_LOW","JITTER_HIGH","runFeedSync","options","Effect","gen","config","sync","resolveOptions","parent","scope","progress","Ref","make","id","feed","source","sink","name","seen","restarts","child","Scope","fork","failure","flip","session","pipe","use","_tag","sleep","pollInterval","advanced","get","maxNoProgressRestarts","logError","reason","String","FeedSyncStalled","backoff","restartDelay","delay","retryAfter","undefined","Duration","max","logWarning","delayMillis","toMillis","onRestart","exports","where","resync","reconcile","rebuild","rebuildOf","reconcileEvery","Error","tuning","pageSize","DEFAULT_FEED_PAGE_SIZE","limit","readLimit","DEFAULT_FEED_MAX_NO_PROGRESS_RESTARTS","Number","isInteger","positiveDuration","DEFAULT_FEED_POLL_INTERVAL","restartMinDelay","DEFAULT_FEED_RESTART_MIN_DELAY","restartMaxDelay","DEFAULT_FEED_RESTART_MAX_DELAY","onReconcile","periodic","Option","none","some","every","readiness","fromUndefinedOr","n","map","Random","nextBetween","jitter","raw","capped","Math","min","millis","round","checkGeneration","held","generation","current","moved","isNone","value","makeSessionContext","lease","store","deliverRows","send","rows","deliver","confirm","pending","Arr","row","change","changeOf","seq","advance","args","casCursor","holder","expected","next","parked","update","collectTombstones","unparkGone","isGone","reconcileWith","acquireLease","ctx","step","cursor","readCursor","check","c","isSome","tail","catchTag","void","previous","forever","start","ensureGeneration","onPage","delivered","apply","lastNonEmpty","park","unpark","sent","set","report","pages","subscribeItems","fetch","changesAfter","cursorOf","initial","listen","catchUpPageSize","emit","Stream","chunks","generationWatch","tick","drop","as","reconcileTicks","match","onNone","empty","onSome","runForEach","merge","event","asVoid","NOTHING_SENT","startRebuild","succeed","finish","parkedAt","DateTime","now","deleted","refused","key","FEED_ORIGIN","ensureHeld","target","begin","batch","load","andThen","publish","ALWAYS_READY","at","ready","rebuilding","stale","Map","settle","delete","record","last","heldSince","Clock","currentTimeMillis","polls","caughtUp","isReadonlyArrayNonEmpty","length","HELD_WAIT_WARN_POLLS","heldMillis","Array","from","values"],"sources":["../../src/runFeedSync.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}