effect-dynamodb 1.28.0 → 1.30.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/dist/Batch.d.ts.map +1 -1
  2. package/dist/Batch.js +3 -2
  3. package/dist/Batch.js.map +1 -1
  4. package/dist/Entity.d.ts +24 -1
  5. package/dist/Entity.d.ts.map +1 -1
  6. package/dist/Entity.js +29 -15
  7. package/dist/Entity.js.map +1 -1
  8. package/dist/EventStore.d.ts +8 -2
  9. package/dist/EventStore.d.ts.map +1 -1
  10. package/dist/EventStore.js +35 -12
  11. package/dist/EventStore.js.map +1 -1
  12. package/dist/Transaction.d.ts.map +1 -1
  13. package/dist/Transaction.js +5 -2
  14. package/dist/Transaction.js.map +1 -1
  15. package/dist/change-feed/ChangeFeed.d.ts +203 -0
  16. package/dist/change-feed/ChangeFeed.d.ts.map +1 -0
  17. package/dist/change-feed/ChangeFeed.js +498 -0
  18. package/dist/change-feed/ChangeFeed.js.map +1 -0
  19. package/dist/change-feed/CheckpointStore.d.ts +113 -0
  20. package/dist/change-feed/CheckpointStore.d.ts.map +1 -0
  21. package/dist/change-feed/CheckpointStore.js +247 -0
  22. package/dist/change-feed/CheckpointStore.js.map +1 -0
  23. package/dist/change-feed/DynamoStreamsClient.d.ts +71 -0
  24. package/dist/change-feed/DynamoStreamsClient.d.ts.map +1 -0
  25. package/dist/change-feed/DynamoStreamsClient.js +92 -0
  26. package/dist/change-feed/DynamoStreamsClient.js.map +1 -0
  27. package/dist/change-feed/Errors.d.ts +84 -0
  28. package/dist/change-feed/Errors.d.ts.map +1 -0
  29. package/dist/change-feed/Errors.js +40 -0
  30. package/dist/change-feed/Errors.js.map +1 -0
  31. package/dist/change-feed/index.d.ts +36 -0
  32. package/dist/change-feed/index.d.ts.map +1 -0
  33. package/dist/change-feed/index.js +36 -0
  34. package/dist/change-feed/index.js.map +1 -0
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/internal/EntityOps.d.ts +1 -1
  40. package/dist/internal/EntityOps.d.ts.map +1 -1
  41. package/dist/internal/EntityOps.js.map +1 -1
  42. package/dist/internal/EventProjection.d.ts +220 -29
  43. package/dist/internal/EventProjection.d.ts.map +1 -1
  44. package/dist/internal/EventProjection.js +221 -308
  45. package/dist/internal/EventProjection.js.map +1 -1
  46. package/dist/internal/MultiProjection.d.ts +255 -0
  47. package/dist/internal/MultiProjection.d.ts.map +1 -0
  48. package/dist/internal/MultiProjection.js +377 -0
  49. package/dist/internal/MultiProjection.js.map +1 -0
  50. package/dist/internal/ProjectionEngine.d.ts +253 -0
  51. package/dist/internal/ProjectionEngine.d.ts.map +1 -0
  52. package/dist/internal/ProjectionEngine.js +850 -0
  53. package/dist/internal/ProjectionEngine.js.map +1 -0
  54. package/dist/internal/TransactWriteOps.d.ts.map +1 -1
  55. package/dist/internal/TransactWriteOps.js +12 -3
  56. package/dist/internal/TransactWriteOps.js.map +1 -1
  57. package/dist/internal/TransactableOps.d.ts +44 -1
  58. package/dist/internal/TransactableOps.d.ts.map +1 -1
  59. package/dist/internal/TransactableOps.js +52 -2
  60. package/dist/internal/TransactableOps.js.map +1 -1
  61. package/package.json +14 -3
@@ -0,0 +1,498 @@
1
+ /**
2
+ * A polled change feed: an Effect `Stream` over a DynamoDB stream's records,
3
+ * for consumers outside Lambda and EventBridge Pipes (#156). See
4
+ * `docs/designs/change-feed.md`.
5
+ *
6
+ * @module
7
+ */
8
+ import { Cause, Clock, Deferred, Duration, Effect, Option, Queue, Random, Result, Schedule, Semaphore, Stream, } from "effect";
9
+ import { processBatch, } from "../internal/EventProjection.js";
10
+ import { CheckpointStore } from "./CheckpointStore.js";
11
+ import { DynamoStreamsClient } from "./DynamoStreamsClient.js";
12
+ import { ChangeFeedLeaseLost, ChangeFeedTrimmed, } from "./Errors.js";
13
+ const isTransient = (error) => error._tag === "ThrottlingError" || error._tag === "InternalServerError";
14
+ /**
15
+ * A change feed over `options.streamArn`: a `Stream` of {@link ChangeFeedBatch}es.
16
+ *
17
+ * - **Shards** are found with `DescribeStream` (paginated), at start and every
18
+ * `describeInterval`; a drained shard's children are looked up with
19
+ * `ShardFilter` (`CHILD_SHARDS`), falling back to a full describe where the
20
+ * filter is unsupported (DynamoDB Local) or finds nothing yet.
21
+ * - **A parent shard is read to its end before its children**, so one item's
22
+ * records stay in order across a split. A closed shard is drained, its last
23
+ * batch acknowledged, checkpointed as finished, and dropped.
24
+ * - **Shards are read concurrently**, each in order with one batch in flight:
25
+ * a shard reads again only once its batch is acknowledged, so a slow
26
+ * consumer slows polling.
27
+ * `maxConcurrentReads` bounds the reads in flight across shards.
28
+ * - **Checkpoints** move only when a batch is acknowledged — at least once.
29
+ * A shard resumes after its checkpoint (`AFTER_SEQUENCE_NUMBER`). An expired
30
+ * iterator is re-acquired from the shard's position; a position past the
31
+ * stream's retention fails with `ChangeFeedTrimmed`.
32
+ * - **Throttling and transient errors** retry with jittered backoff. An idle
33
+ * shard polls every `pollInterval`.
34
+ * - **The lease** keeps a second process from consuming the same feed.
35
+ *
36
+ * The stream ends when the stream is disabled and every shard is drained;
37
+ * otherwise it runs until interrupted.
38
+ *
39
+ * Throws `[EDD-9083]` for options it can't honour: `lease.renewEvery` of half
40
+ * `lease.duration` or more, a `batchSize` outside 1–1000, a
41
+ * `maxConcurrentReads` that is not a positive integer.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const feed = changeFeed({ streamArn, startingPosition: "TRIM_HORIZON" })
46
+ * yield* feed.pipe(
47
+ * Stream.mapEffect((batch) => handle(batch.records).pipe(Effect.andThen(batch.commit()))),
48
+ * Stream.runDrain,
49
+ * )
50
+ * ```
51
+ */
52
+ export const changeFeed = (options) => {
53
+ validate(options);
54
+ return Stream.callback((queue) => run(options, queue).pipe(Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.void : Queue.failCause(queue, cause))));
55
+ };
56
+ /** The lease's `duration` and `renewEvery`, defaults applied. */
57
+ const leaseTiming = (lease) => {
58
+ const duration = Duration.fromInputUnsafe(lease.duration ?? "30 seconds");
59
+ const renewEvery = Duration.fromInputUnsafe(lease.renewEvery ?? Duration.millis(Math.max(1, Math.floor(Duration.toMillis(duration) / 3))));
60
+ return { duration, renewEvery };
61
+ };
62
+ /** Refuse options the feed can't honour — `[EDD-9083]`. */
63
+ const validate = (options) => {
64
+ const refuse = (problem) => {
65
+ throw new Error(`[EDD-9083] changeFeed("${options.streamArn}"): ${problem}`);
66
+ };
67
+ if (options.lease !== false && options.lease !== undefined) {
68
+ const { duration, renewEvery } = leaseTiming(options.lease);
69
+ if (!(Duration.toMillis(renewEvery) > 0))
70
+ refuse("lease.renewEvery must be positive.");
71
+ if (Duration.toMillis(renewEvery) * 2 >= Duration.toMillis(duration)) {
72
+ refuse(`lease.renewEvery (${Duration.toMillis(renewEvery)} ms) must be less than half of ` +
73
+ `lease.duration (${Duration.toMillis(duration)} ms), to leave time to retry a ` +
74
+ "renewal the store fails to write before the lease lapses.");
75
+ }
76
+ }
77
+ const reads = options.maxConcurrentReads;
78
+ if (reads !== undefined && !(Number.isInteger(reads) && reads >= 1)) {
79
+ refuse(`maxConcurrentReads must be a positive integer, got ${reads}.`);
80
+ }
81
+ const size = options.batchSize;
82
+ if (size !== undefined && !(Number.isInteger(size) && size >= 1 && size <= 1000)) {
83
+ refuse(`batchSize must be an integer from 1 to 1000, got ${size}.`);
84
+ }
85
+ };
86
+ const run = (options, queue) => Effect.gen(function* () {
87
+ const client = yield* DynamoStreamsClient;
88
+ const store = yield* CheckpointStore;
89
+ const scope = yield* Effect.scope;
90
+ const { streamArn } = options;
91
+ const feed = { streamArn, consumer: options.consumer ?? "default" };
92
+ const pollInterval = Duration.fromInputUnsafe(options.pollInterval ?? "1 second");
93
+ const describeInterval = Duration.fromInputUnsafe(options.describeInterval ?? "30 seconds");
94
+ const batchSize = options.batchSize ?? 100;
95
+ const initialBackoff = Duration.fromInputUnsafe(options.backoff?.initial ?? "100 millis");
96
+ const maxBackoff = Duration.fromInputUnsafe(options.backoff?.max ?? "10 seconds");
97
+ const backoff = Schedule.jittered(Schedule.min([Schedule.exponential(initialBackoff, 2), Schedule.spaced(maxBackoff)]));
98
+ /** Retry throttling and transient errors without limit. */
99
+ const transient = (effect) => Effect.retry(effect, { schedule: backoff, while: isTransient });
100
+ /** Fail the stream with a forked fiber's failure. */
101
+ const failStream = (effect) => effect.pipe(Effect.asVoid, Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.void : Queue.failCause(queue, cause)));
102
+ /** Bounds the shard reads in flight; held for one call, not across a backoff. */
103
+ const reads = yield* Semaphore.make(options.maxConcurrentReads ?? 16);
104
+ const read = (effect) => transient(Semaphore.withPermit(reads, effect));
105
+ /** A jittered delay growing per consecutive failure, capped at `backoff.max`. */
106
+ const retryDelay = (failures) => Effect.map(Random.nextBetween(0.8, 1.2), (jitter) => Duration.millis(Math.min(Duration.toMillis(maxBackoff), Duration.toMillis(initialBackoff) * 2 ** failures) * jitter));
107
+ // -- Lease -------------------------------------------------------------
108
+ /** Set once the lease is lost: acknowledgements then fail, writing nothing. */
109
+ let leaseLost;
110
+ if (options.lease !== false) {
111
+ const lease = options.lease ?? {};
112
+ const { duration, renewEvery } = leaseTiming(lease);
113
+ const owner = lease.owner ?? globalThis.crypto.randomUUID();
114
+ const acquire = store.acquireLease(feed, owner, duration);
115
+ yield* lease.onHeld === "wait"
116
+ ? Effect.retry(acquire, {
117
+ schedule: Schedule.spaced(renewEvery),
118
+ while: (error) => error._tag === "ChangeFeedLeaseHeld",
119
+ })
120
+ : acquire;
121
+ yield* Effect.addFinalizer(() => Effect.ignore(store.releaseLease(feed, owner)));
122
+ let renewedAt = yield* Clock.currentTimeMillis;
123
+ const lost = (error) => {
124
+ leaseLost = new ChangeFeedLeaseLost({ streamArn, consumer: feed.consumer, owner });
125
+ return Effect.fail(error);
126
+ };
127
+ /**
128
+ * Renew the lease. A store failure is retried until the lease is about
129
+ * to lapse — `renewEvery` before it expires — and a lease taken over is
130
+ * not retried; either way the lease counts as lost.
131
+ */
132
+ const renew = Effect.gen(function* () {
133
+ const deadline = renewedAt + Duration.toMillis(duration) - Duration.toMillis(renewEvery);
134
+ for (let attempt = 0;; attempt++) {
135
+ const at = yield* Clock.currentTimeMillis;
136
+ const result = yield* Effect.result(store.renewLease(feed, owner, duration));
137
+ if (Result.isSuccess(result)) {
138
+ renewedAt = at;
139
+ return;
140
+ }
141
+ const now = yield* Clock.currentTimeMillis;
142
+ if (result.failure._tag !== "CheckpointStoreError" || now >= deadline) {
143
+ return yield* lost(result.failure);
144
+ }
145
+ const delay = yield* retryDelay(attempt);
146
+ yield* Effect.sleep(Duration.millis(Math.min(Duration.toMillis(delay), deadline - now)));
147
+ }
148
+ });
149
+ yield* Effect.forkIn(failStream(Effect.forever(Effect.sleep(renewEvery).pipe(Effect.andThen(renew)))), scope);
150
+ }
151
+ // -- Shards ------------------------------------------------------------
152
+ const checkpoints = yield* store.load(feed);
153
+ const fresh = checkpoints.size === 0;
154
+ /** Every shard described, by id — updated by each describe (a shard closes). */
155
+ const known = new Map();
156
+ const started = new Set();
157
+ const finished = new Set();
158
+ for (const [shardId, checkpoint] of checkpoints) {
159
+ if (checkpoint._tag === "Finished")
160
+ finished.add(shardId);
161
+ }
162
+ /** At a fresh `LATEST` start, the shards open at that moment. */
163
+ const latest = new Set();
164
+ let status;
165
+ let shardFilter = true;
166
+ /** Shards missing from a describe that a probe found still there, all closed parents. */
167
+ const probedClosed = new Set();
168
+ /**
169
+ * Asks the describer for a full describe soon: drained shards that find no
170
+ * children share one describe instead of each running their own.
171
+ */
172
+ const wake = yield* Queue.dropping(1);
173
+ const absorb = (shards) => {
174
+ for (const shard of shards)
175
+ if (shard.ShardId !== undefined)
176
+ known.set(shard.ShardId, shard);
177
+ };
178
+ /** `DescribeStream`, transient errors retried (it raises no iterator errors). */
179
+ const describe = (input) => transient(client.describeStream(input)).pipe(Effect.catchTags({
180
+ ShardIteratorExpired: (e) => Effect.die(e),
181
+ StreamDataTrimmed: (e) => Effect.die(e),
182
+ }));
183
+ const describeAll = Effect.gen(function* () {
184
+ const shards = [];
185
+ let ExclusiveStartShardId;
186
+ do {
187
+ const page = yield* describe({
188
+ StreamArn: streamArn,
189
+ ...(ExclusiveStartShardId === undefined ? {} : { ExclusiveStartShardId }),
190
+ });
191
+ shards.push(...(page.StreamDescription?.Shards ?? []));
192
+ status = page.StreamDescription?.StreamStatus ?? status;
193
+ ExclusiveStartShardId = page.StreamDescription?.LastEvaluatedShardId;
194
+ } while (ExclusiveStartShardId !== undefined);
195
+ absorb(shards);
196
+ // Forget finished shards that have left the stream (24 h after closing),
197
+ // so a long-running feed does not accumulate them.
198
+ const listed = new Set(shards.flatMap((shard) => (shard.ShardId === undefined ? [] : [shard.ShardId])));
199
+ for (const shardId of [...known.keys()]) {
200
+ if (!listed.has(shardId) && finished.has(shardId)) {
201
+ known.delete(shardId);
202
+ started.delete(shardId);
203
+ finished.delete(shardId);
204
+ }
205
+ }
206
+ return listed;
207
+ });
208
+ /**
209
+ * The children of a drained shard — with `ShardFilter`, or, where the
210
+ * filter is unsupported or finds none yet, the describer's next full
211
+ * describe, which it runs soon (shared by every shard drained meanwhile).
212
+ */
213
+ const discoverChildren = (parentId) => Effect.gen(function* () {
214
+ if (shardFilter) {
215
+ const filtered = yield* describe({
216
+ StreamArn: streamArn,
217
+ ShardFilter: { Type: "CHILD_SHARDS", ShardId: parentId },
218
+ }).pipe(Effect.map(Option.some), Effect.catchTag("DynamoValidationError", () => {
219
+ shardFilter = false;
220
+ return Effect.succeed(Option.none());
221
+ }));
222
+ if (Option.isSome(filtered)) {
223
+ const description = filtered.value.StreamDescription;
224
+ status = description?.StreamStatus ?? status;
225
+ const children = (description?.Shards ?? []).filter((s) => s.ParentShardId === parentId);
226
+ absorb(children);
227
+ if (children.length > 0)
228
+ return;
229
+ }
230
+ }
231
+ Queue.offerUnsafe(wake, undefined);
232
+ });
233
+ const isOpen = (shardId) => !probedClosed.has(shardId) &&
234
+ known.get(shardId)?.SequenceNumberRange?.EndingSequenceNumber === undefined;
235
+ const startPosition = (shardId) => {
236
+ const checkpoint = checkpoints.get(shardId);
237
+ if (checkpoint?._tag === "Sequence") {
238
+ return { type: "AFTER_SEQUENCE_NUMBER", sequenceNumber: checkpoint.sequenceNumber };
239
+ }
240
+ return { type: latest.has(shardId) ? "LATEST" : "TRIM_HORIZON" };
241
+ };
242
+ /** Hand a batch to the consumer and wait for its acknowledgement. */
243
+ const deliver = (shardId, records) => Effect.gen(function* () {
244
+ const ack = yield* Deferred.make();
245
+ const sequences = records.map((record) => record.dynamodb?.SequenceNumber ?? "");
246
+ const first = sequences[0];
247
+ const last = sequences[sequences.length - 1];
248
+ // Acknowledgements run one at a time; the first whose checkpoint is
249
+ // written settles the batch, and a failed write leaves it unsettled, so
250
+ // a retry writes again.
251
+ const lock = yield* Semaphore.make(1);
252
+ let settled = false;
253
+ const settle = (checkpoint, result) => Semaphore.withPermit(lock, Effect.suspend(() => {
254
+ if (settled)
255
+ return Effect.void;
256
+ if (leaseLost !== undefined)
257
+ return Effect.fail(leaseLost);
258
+ return (checkpoint === undefined
259
+ ? Effect.void
260
+ : store.commit(feed, shardId, { _tag: "Sequence", sequenceNumber: checkpoint })).pipe(Effect.andThen(Effect.suspend(() => {
261
+ settled = true;
262
+ return Deferred.succeed(ack, result);
263
+ })), Effect.asVoid);
264
+ }));
265
+ const commit = () => settle(last, { _tag: "Continue" });
266
+ const complete = (response) => Effect.suspend(() => {
267
+ if (response.batchItemFailures.length === 0)
268
+ return commit();
269
+ const failed = new Set(response.batchItemFailures.map((f) => f.itemIdentifier));
270
+ const inBatch = new Set(sequences);
271
+ // An identifier naming no record fails the whole batch, as Lambda does.
272
+ const at = [...failed].some((id) => !inBatch.has(id))
273
+ ? 0
274
+ : sequences.findIndex((sequence) => failed.has(sequence));
275
+ const acknowledged = at === 0 ? undefined : sequences[at - 1];
276
+ return settle(acknowledged, { _tag: "Retry", from: sequences[at], acknowledged });
277
+ });
278
+ yield* Queue.offer(queue, {
279
+ streamArn,
280
+ shardId,
281
+ records,
282
+ firstSequenceNumber: first,
283
+ lastSequenceNumber: last,
284
+ commit,
285
+ complete,
286
+ });
287
+ return yield* Deferred.await(ack);
288
+ });
289
+ /** Read one shard to its end (it closed and every record was acknowledged). */
290
+ const readShard = (shardId, start) => Effect.gen(function* () {
291
+ let position = start;
292
+ let acknowledged = start.type === "AFTER_SEQUENCE_NUMBER" ? start.sequenceNumber : undefined;
293
+ let iterator;
294
+ let failures = 0;
295
+ let misses = 0;
296
+ const trimmed = (cause) => new ChangeFeedTrimmed({ streamArn, shardId, sequenceNumber: acknowledged, cause });
297
+ while (true) {
298
+ if (iterator === undefined) {
299
+ const acquired = yield* read(client.getShardIterator({
300
+ StreamArn: streamArn,
301
+ ShardId: shardId,
302
+ ShardIteratorType: position.type,
303
+ ...("sequenceNumber" in position
304
+ ? { SequenceNumber: position.sequenceNumber }
305
+ : {}),
306
+ })).pipe(Effect.catchTag("StreamDataTrimmed", (e) => Effect.fail(trimmed(e.cause))), Effect.catchTag("ShardIteratorExpired", (e) => Effect.die(e)));
307
+ iterator = acquired.ShardIterator;
308
+ if (iterator === undefined) {
309
+ // No iterator. A closed shard has nothing left to read; for one
310
+ // the stream still lists as open, refresh the list and retry.
311
+ const listed = yield* describeAll;
312
+ if (!listed.has(shardId)) {
313
+ return yield* trimmed(new Error(`Shard ${shardId} is no longer in the stream`));
314
+ }
315
+ if (!isOpen(shardId))
316
+ return;
317
+ yield* Effect.sleep(yield* retryDelay(misses++));
318
+ continue;
319
+ }
320
+ misses = 0;
321
+ }
322
+ const page = yield* read(client.getRecords({ ShardIterator: iterator, Limit: batchSize })).pipe(Effect.map(Option.some), Effect.catchTag("ShardIteratorExpired", () => Effect.succeed(Option.none())), Effect.catchTag("StreamDataTrimmed", (e) => Effect.fail(trimmed(e.cause))));
323
+ if (Option.isNone(page)) {
324
+ // Re-acquire from the shard's position.
325
+ iterator = undefined;
326
+ continue;
327
+ }
328
+ const records = page.value.Records ?? [];
329
+ if (records.length > 0) {
330
+ const ack = yield* deliver(shardId, records);
331
+ if (ack._tag === "Retry") {
332
+ if (ack.acknowledged !== undefined)
333
+ acknowledged = ack.acknowledged;
334
+ position = { type: "AT_SEQUENCE_NUMBER", sequenceNumber: ack.from };
335
+ iterator = undefined;
336
+ yield* Effect.sleep(yield* retryDelay(failures++));
337
+ continue;
338
+ }
339
+ failures = 0;
340
+ acknowledged = records[records.length - 1].dynamodb?.SequenceNumber ?? acknowledged;
341
+ if (acknowledged !== undefined) {
342
+ position = { type: "AFTER_SEQUENCE_NUMBER", sequenceNumber: acknowledged };
343
+ }
344
+ }
345
+ const next = page.value.NextShardIterator;
346
+ // No next iterator: the shard is closed and read to its end.
347
+ if (next === undefined)
348
+ return;
349
+ iterator = next;
350
+ if (records.length === 0 && isOpen(shardId))
351
+ yield* Effect.sleep(pollInterval);
352
+ }
353
+ });
354
+ /** End the stream once the stream is disabled and every shard is drained. */
355
+ const maybeEnd = Effect.suspend(() => status === "DISABLED" &&
356
+ [...known.keys()].every((shardId) => finished.has(shardId)) &&
357
+ [...started].every((shardId) => finished.has(shardId))
358
+ ? Effect.asVoid(Queue.end(queue))
359
+ : Effect.void);
360
+ /** Start every shard whose parent is drained (or gone). */
361
+ const schedule = Effect.suspend(() => {
362
+ const ready = [];
363
+ for (const [shardId, shard] of known) {
364
+ if (started.has(shardId) || finished.has(shardId))
365
+ continue;
366
+ const parent = shard.ParentShardId;
367
+ if (parent !== undefined && known.has(parent) && !finished.has(parent))
368
+ continue;
369
+ started.add(shardId);
370
+ ready.push(shardId);
371
+ }
372
+ return Effect.forEach(ready, (shardId) => Effect.forkIn(failStream(shardLifecycle(shardId)), scope), { discard: true });
373
+ });
374
+ const shardLifecycle = (shardId) => Effect.gen(function* () {
375
+ yield* readShard(shardId, startPosition(shardId));
376
+ yield* store.commit(feed, shardId, { _tag: "Finished" });
377
+ finished.add(shardId);
378
+ yield* discoverChildren(shardId);
379
+ yield* schedule;
380
+ yield* maybeEnd;
381
+ });
382
+ /**
383
+ * Whether a shard missing from a describe is still in the stream — its
384
+ * trim horizon still yields an iterator. Gone: not found, or trimmed.
385
+ */
386
+ const exists = (shardId) => read(client.getShardIterator({
387
+ StreamArn: streamArn,
388
+ ShardId: shardId,
389
+ ShardIteratorType: "TRIM_HORIZON",
390
+ })).pipe(Effect.as(true), Effect.catchTags({
391
+ ResourceNotFoundError: () => Effect.succeed(false),
392
+ StreamDataTrimmed: () => Effect.succeed(false),
393
+ ShardIteratorExpired: (e) => Effect.die(e),
394
+ }));
395
+ let listed = yield* describeAll;
396
+ /**
397
+ * Shards this consumer may still owe records from that the describe did
398
+ * not list: shards with a `Sequence` checkpoint, and the unfinished parents
399
+ * of listed shards it has not started.
400
+ */
401
+ const unlisted = () => {
402
+ const ids = new Map();
403
+ for (const [shardId, checkpoint] of checkpoints) {
404
+ if (checkpoint._tag === "Sequence" && !listed.has(shardId))
405
+ ids.set(shardId, "checkpoint");
406
+ }
407
+ for (const [shardId, shard] of known) {
408
+ const parent = shard.ParentShardId;
409
+ if (parent !== undefined &&
410
+ !checkpoints.has(shardId) &&
411
+ !listed.has(parent) &&
412
+ !finished.has(parent) &&
413
+ !ids.has(parent)) {
414
+ ids.set(parent, "parent");
415
+ }
416
+ }
417
+ return ids;
418
+ };
419
+ let missing = unlisted();
420
+ // A describe may omit a shard that is still there: describe once more,
421
+ // then probe what is still missing before treating it as gone.
422
+ if (missing.size > 0) {
423
+ listed = yield* describeAll;
424
+ missing = unlisted();
425
+ }
426
+ for (const [shardId, kind] of missing) {
427
+ if (yield* exists(shardId)) {
428
+ // Still there: read it like any other shard, before its children.
429
+ known.set(shardId, { ShardId: shardId });
430
+ if (kind === "parent")
431
+ probedClosed.add(shardId);
432
+ }
433
+ else if (fresh) {
434
+ // Gone before this consumer started: nothing of it is owed. Recorded,
435
+ // so a restart can tell it from a lost one.
436
+ yield* store.commit(feed, shardId, { _tag: "Finished" });
437
+ finished.add(shardId);
438
+ }
439
+ else {
440
+ // Gone while this consumer still owed records from it (downtime past
441
+ // the 24-hour retention): its unread records are lost.
442
+ const checkpoint = checkpoints.get(shardId);
443
+ return yield* new ChangeFeedTrimmed({
444
+ streamArn,
445
+ shardId,
446
+ sequenceNumber: checkpoint?._tag === "Sequence" ? checkpoint.sequenceNumber : undefined,
447
+ cause: new Error(`Shard ${shardId} is no longer in the stream, and was not read to its end`),
448
+ });
449
+ }
450
+ }
451
+ if (fresh && options.startingPosition === "LATEST") {
452
+ for (const [shardId] of known) {
453
+ if (isOpen(shardId)) {
454
+ latest.add(shardId);
455
+ }
456
+ else {
457
+ yield* store.commit(feed, shardId, { _tag: "Finished" });
458
+ finished.add(shardId);
459
+ }
460
+ }
461
+ }
462
+ yield* schedule;
463
+ yield* maybeEnd;
464
+ // The describer: every `describeInterval`, or soon after a drained shard
465
+ // found no children — at most one describe per `pollInterval` for those.
466
+ yield* Effect.forkIn(failStream(Effect.forever(Effect.gen(function* () {
467
+ yield* Effect.raceFirst(Effect.sleep(describeInterval), Queue.take(wake));
468
+ yield* Queue.clear(wake);
469
+ yield* describeAll;
470
+ yield* schedule;
471
+ yield* maybeEnd;
472
+ yield* Effect.sleep(pollInterval);
473
+ }))), scope);
474
+ });
475
+ /**
476
+ * Run a change feed into projections: each batch goes through
477
+ * `EventStore.processBatch(batch.records, { projections, source: "lambda" })`,
478
+ * then `batch.complete(result.response)` — the checkpoint moves past the
479
+ * batch, or to the record before its earliest failed record, which the shard
480
+ * reads again. Batches of different shards are processed concurrently.
481
+ *
482
+ * Runs until the feed ends (its stream disabled and drained) or fails.
483
+ *
484
+ * @example
485
+ * ```ts
486
+ * yield* runChangeFeed(changeFeed({ streamArn }), {
487
+ * projections: { summaries: OrderSummaryProjection },
488
+ * })
489
+ * ```
490
+ */
491
+ export const runChangeFeed = (feed, options) => {
492
+ const { onBatch, ...processOptions } = options;
493
+ return feed.pipe(Stream.mapEffect((batch) => processBatch(batch.records, {
494
+ ...processOptions,
495
+ source: "lambda",
496
+ }).pipe(Effect.tap((result) => (onBatch === undefined ? Effect.void : onBatch(batch, result))), Effect.flatMap((result) => batch.complete(result.response))), { concurrency: "unbounded", unordered: true }), Stream.runDrain);
497
+ };
498
+ //# sourceMappingURL=ChangeFeed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChangeFeed.js","sourceRoot":"","sources":["../../src/change-feed/ChangeFeed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * CheckpointStore — where a change feed keeps its per-shard checkpoints and
3
+ * its lease. Two implementations: in memory, and a DynamoDB table.
4
+ *
5
+ * @module
6
+ */
7
+ import type { CreateTableCommandInput } from "@aws-sdk/client-dynamodb";
8
+ import { Context, Duration, Effect, Layer } from "effect";
9
+ import { DynamoClient } from "../DynamoClient.js";
10
+ import { ChangeFeedLeaseHeld, ChangeFeedLeaseLost, CheckpointStoreError } from "./Errors.js";
11
+ /**
12
+ * One consumer of one stream: the unit a checkpoint and a lease belong to.
13
+ * `consumer` names the application, so several applications can consume one
14
+ * stream with one checkpoint table.
15
+ */
16
+ export interface FeedId {
17
+ readonly streamArn: string;
18
+ readonly consumer: string;
19
+ }
20
+ /**
21
+ * A shard's checkpoint: the last sequence number whose record was
22
+ * acknowledged, or `Finished` once the shard closed and every record was
23
+ * acknowledged (or, at a fresh `LATEST` start, when it was closed already).
24
+ */
25
+ export type ShardCheckpoint = {
26
+ readonly _tag: "Sequence";
27
+ readonly sequenceNumber: string;
28
+ } | {
29
+ readonly _tag: "Finished";
30
+ };
31
+ /** The operations a change feed needs from its checkpoint store. */
32
+ export interface CheckpointStoreService {
33
+ /** Every shard checkpoint of the feed, by shard id. */
34
+ readonly load: (feed: FeedId) => Effect.Effect<ReadonlyMap<string, ShardCheckpoint>, CheckpointStoreError>;
35
+ /** Record a shard's checkpoint. */
36
+ readonly commit: (feed: FeedId, shardId: string, checkpoint: ShardCheckpoint) => Effect.Effect<void, CheckpointStoreError>;
37
+ /**
38
+ * Take the feed's lease for `duration` — when there is none, it expired, or
39
+ * `owner` holds it already.
40
+ */
41
+ readonly acquireLease: (feed: FeedId, owner: string, duration: Duration.Duration) => Effect.Effect<void, ChangeFeedLeaseHeld | CheckpointStoreError>;
42
+ /** Extend `owner`'s lease by `duration` from now; fails when another owner holds it. */
43
+ readonly renewLease: (feed: FeedId, owner: string, duration: Duration.Duration) => Effect.Effect<void, ChangeFeedLeaseLost | CheckpointStoreError>;
44
+ /** Give up `owner`'s lease (nothing when another owner holds it). */
45
+ readonly releaseLease: (feed: FeedId, owner: string) => Effect.Effect<void, CheckpointStoreError>;
46
+ }
47
+ /** A checkpoint store held in memory — for tests and single-process feeds that may start over. */
48
+ export declare const makeMemory: () => CheckpointStoreService;
49
+ /** Options of {@link makeDynamoDB}. */
50
+ export interface DynamoDBCheckpointStoreOptions {
51
+ /** The checkpoint table — see {@link tableDefinition} for its key schema. */
52
+ readonly tableName: string;
53
+ /**
54
+ * How long a `Finished` checkpoint is kept (its `ttl` attribute, honoured
55
+ * when TTL is enabled on the table). A closed shard leaves the stream 24
56
+ * hours after it closes. Default: 2 days; at least {@link MIN_FINISHED_TTL}
57
+ * (28 hours), `[EDD-9084]` otherwise.
58
+ */
59
+ readonly finishedTtl?: Duration.Input | undefined;
60
+ }
61
+ /**
62
+ * The checkpoint table's `CreateTable` input: `pk` (S, HASH) + `sk` (S, RANGE),
63
+ * on-demand billing. A shard checkpoint is `{ pk: "<consumer>#<streamArn>",
64
+ * sk: "shard#<shardId>", sequenceNumber | finished, updatedAt, ttl? }`; the
65
+ * lease is `{ pk, sk: "lease", owner, expiresAt }`. Enable TTL on `ttl` to
66
+ * expire `Finished` checkpoints.
67
+ */
68
+ export declare const tableDefinition: (tableName: string) => CreateTableCommandInput & {
69
+ readonly TableName: string;
70
+ };
71
+ /** A checkpoint store on a DynamoDB table, through `DynamoClient`. */
72
+ /**
73
+ * The shortest `finishedTtl` accepted: 28 hours. A `Finished` checkpoint must
74
+ * outlive every shard that depends on it being there — the shard itself, listed
75
+ * up to 24 hours after it closes, and the children of a parent already gone at
76
+ * a fresh start, which can stay listed for a shard's lifetime (about 4 hours)
77
+ * plus those 24 hours. A checkpoint expired earlier makes a restart report
78
+ * `ChangeFeedTrimmed` for records that were never lost.
79
+ */
80
+ export declare const MIN_FINISHED_TTL: Duration.Duration;
81
+ export declare const makeDynamoDB: (options: DynamoDBCheckpointStoreOptions) => Effect.Effect<CheckpointStoreService, never, DynamoClient>;
82
+ declare const CheckpointStore_base: Context.ServiceClass<CheckpointStore, "@effect-dynamodb/CheckpointStore", CheckpointStoreService>;
83
+ /**
84
+ * Effect `Context.Service` for the change feed's checkpoint store.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * import { CheckpointStore } from "effect-dynamodb/change-feed"
89
+ *
90
+ * // In memory (tests, or a feed that may start over)
91
+ * const Memory = CheckpointStore.layerMemory
92
+ * // In a DynamoDB table (needs DynamoClient)
93
+ * const Durable = CheckpointStore.layerDynamoDB({ tableName: "change-feed-checkpoints" })
94
+ * ```
95
+ */
96
+ export declare class CheckpointStore extends CheckpointStore_base {
97
+ /** A fresh in-memory store per layer build; its lease is process-local. */
98
+ static readonly layerMemory: Layer.Layer<CheckpointStore>;
99
+ /** The shortest `finishedTtl` accepted — see {@link MIN_FINISHED_TTL}. */
100
+ static readonly minFinishedTtl: Duration.Duration;
101
+ /** A store on a DynamoDB table — see {@link tableDefinition}. */
102
+ static readonly layerDynamoDB: (options: DynamoDBCheckpointStoreOptions) => Layer.Layer<CheckpointStore, never, DynamoClient>;
103
+ /** {@link tableDefinition}: the checkpoint table's `CreateTable` input. */
104
+ static readonly tableDefinition: (tableName: string) => CreateTableCommandInput & {
105
+ readonly TableName: string;
106
+ };
107
+ /** {@link makeMemory}: an in-memory store, as a value. */
108
+ static readonly makeMemory: () => CheckpointStoreService;
109
+ /** {@link makeDynamoDB}: a DynamoDB-table store, as a value. */
110
+ static readonly makeDynamoDB: (options: DynamoDBCheckpointStoreOptions) => Effect.Effect<CheckpointStoreService, never, DynamoClient>;
111
+ }
112
+ export {};
113
+ //# sourceMappingURL=CheckpointStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckpointStore.d.ts","sourceRoot":"","sources":["../../src/change-feed/CheckpointStore.ts"],"names":[],"mappings":"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"}