@ddd-ts/event-sourcing-firestore 0.0.42 → 0.0.43

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.
package/dist/index.d.ts CHANGED
@@ -7,5 +7,5 @@ export { FirestoreEventLakeAggregateStore, MakeFirestoreEventLakeAggregateStore,
7
7
  export { FirestoreProjectedStreamStorageLayer, FirestoreLakeSourceFilter, FirestoreStreamSourceFilter, } from "./firestore.projected-stream.storage-layer";
8
8
  export { FirestoreProjectedStreamReader } from "./firestore.projected-stream.reader";
9
9
  export { FirestoreSnapshotter } from "./firestore.snapshotter";
10
- export { FirestoreProjector, FirestoreQueueStore, Task, AlreadyEnqueuedError, ClaimerId, } from "./projection/firestore.projector";
10
+ export { FirestoreProjector, FirestoreQueueStore, Task, AlreadyEnqueuedError, ClaimerId, type ProjectorLogger, } from "./projection/firestore.projector";
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AACtF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,gCAAgC,EAAE,MAAM,wCAAwC,CAAC;AAC1F,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,OAAO,EACL,kCAAkC,EAClC,sCAAsC,GACvC,MAAM,0CAA0C,CAAC;AAElD,OAAO,EACL,gCAAgC,EAChC,oCAAoC,GACrC,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EACL,oCAAoC,EACpC,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AAErF,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,IAAI,EACJ,oBAAoB,EACpB,SAAS,GACV,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AACtF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,gCAAgC,EAAE,MAAM,wCAAwC,CAAC;AAC1F,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,OAAO,EACL,kCAAkC,EAClC,sCAAsC,GACvC,MAAM,0CAA0C,CAAC;AAElD,OAAO,EACL,gCAAgC,EAChC,oCAAoC,GACrC,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EACL,oCAAoC,EACpC,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AAErF,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,IAAI,EACJ,oBAAoB,EACpB,SAAS,EACT,KAAK,eAAe,GACrB,MAAM,kCAAkC,CAAC"}
@@ -1,7 +1,13 @@
1
- import { Firestore, WriteBatch } from "firebase-admin/firestore";
2
- import { type IEsEvent, type ISavedChange, EventId, ProjectedStreamReader, Cursor, CheckpointId, ESProjection, type IFact, type Serialized, Lock } from "@ddd-ts/core";
1
+ import { CheckpointId, Cursor, ESProjection, EventId, type IEsEvent, type IFact, type ISavedChange, Lock, ProjectedStreamReader, type Serialized } from "@ddd-ts/core";
3
2
  import { Mapping, MicrosecondTimestamp } from "@ddd-ts/shape";
4
3
  import { DefaultConverter, FirestoreTransaction } from "@ddd-ts/store-firestore";
4
+ import { Firestore, WriteBatch } from "firebase-admin/firestore";
5
+ export interface ProjectorLogger {
6
+ debug(message: string, context?: Record<string, unknown>): void;
7
+ info(message: string, context?: Record<string, unknown>): void;
8
+ warn(message: string, context?: Record<string, unknown>): void;
9
+ error(message: string, context?: Record<string, unknown>): void;
10
+ }
5
11
  interface FirestoreProjectorConfig {
6
12
  retry: {
7
13
  attempts: number;
@@ -12,7 +18,10 @@ interface FirestoreProjectorConfig {
12
18
  enqueue: {
13
19
  batchSize: number;
14
20
  };
21
+ logger?: ProjectorLogger;
22
+ /** @deprecated Use `logger.error` instead */
15
23
  onProcessError: (error: Error) => void;
24
+ /** @deprecated Use `logger.error` instead */
16
25
  onEnqueueError: (error: Error) => void;
17
26
  }
18
27
  export declare class FirestoreProjector {
@@ -22,10 +31,14 @@ export declare class FirestoreProjector {
22
31
  config: FirestoreProjectorConfig;
23
32
  _unclaim: boolean;
24
33
  constructor(projection: ESProjection<IEsEvent>, reader: ProjectedStreamReader<IEsEvent>, queue: FirestoreQueueStore, config?: FirestoreProjectorConfig);
34
+ private get logger();
25
35
  breathe(): AsyncGenerator<readonly [number, () => void], void, unknown>;
26
- private eventCoordinators;
27
- private getEventCoordinator;
36
+ private processingCheckpoints;
37
+ private coalescedCounts;
38
+ private pendingCursors;
39
+ private prunePendingCursors;
28
40
  handle(savedChange: ISavedChange<IEsEvent>): Promise<void>;
41
+ private handleOne;
29
42
  private getCursor;
30
43
  private attempt;
31
44
  private getQueueHead;
@@ -1 +1 @@
1
- {"version":3,"file":"firestore.projector.d.ts","sourceRoot":"","sources":["../../src/projection/firestore.projector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"firestore.projector.d.ts","sourceRoot":"","sources":["../../src/projection/firestore.projector.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  const require_runtime = require('../_virtual/_rolldown/runtime.js');
2
- const require_event_coordinator = require('./event-coordinator.js');
3
2
  let _ddd_ts_core = require("@ddd-ts/core");
4
3
  let _ddd_ts_store_firestore = require("@ddd-ts/store-firestore");
5
4
  let firebase_admin_firestore = require("firebase-admin/firestore");
@@ -18,6 +17,12 @@ const TaskState = {
18
17
  };
19
18
  const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
20
19
  const RETENTION = _ddd_ts_shape.MicrosecondTimestamp.MONTH;
20
+ const defaultLogger = {
21
+ debug: (msg, ctx) => console.debug(msg, ctx ?? ""),
22
+ info: (msg, ctx) => console.info(msg, ctx ?? ""),
23
+ warn: (msg, ctx) => console.warn(msg, ctx ?? ""),
24
+ error: (msg, ctx) => console.error(msg, ctx ?? "")
25
+ };
21
26
  var FirestoreProjector = class {
22
27
  _unclaim = true;
23
28
  constructor(projection, reader, queue, config = {
@@ -28,6 +33,7 @@ var FirestoreProjector = class {
28
33
  backoff: 1.5
29
34
  },
30
35
  enqueue: { batchSize: 100 },
36
+ logger: defaultLogger,
31
37
  onProcessError: (error) => {
32
38
  console.error("Error processing event:", error);
33
39
  },
@@ -40,6 +46,9 @@ var FirestoreProjector = class {
40
46
  this.queue = queue;
41
47
  this.config = config;
42
48
  }
49
+ get logger() {
50
+ return this.config.logger ?? defaultLogger;
51
+ }
43
52
  async *breathe() {
44
53
  const { attempts, minDelay, maxDelay, backoff } = this.config.retry;
45
54
  for (let i = 0; i < attempts; i++) {
@@ -52,32 +61,106 @@ var FirestoreProjector = class {
52
61
  await wait((backoff * i + 1) * minDelay + jitter);
53
62
  }
54
63
  }
55
- eventCoordinators = /* @__PURE__ */ new Map();
56
- getEventCoordinator(checkpointId) {
64
+ processingCheckpoints = /* @__PURE__ */ new Map();
65
+ coalescedCounts = /* @__PURE__ */ new Map();
66
+ pendingCursors = /* @__PURE__ */ new Map();
67
+ prunePendingCursors(checkpointId, eventIds) {
57
68
  const key = checkpointId.serialize();
58
- let coordinator = this.eventCoordinators.get(key);
59
- if (!coordinator) {
60
- coordinator = new require_event_coordinator.EventCoordinator();
61
- coordinator.onEmpty(() => this.eventCoordinators.delete(key));
62
- this.eventCoordinators.set(key, coordinator);
63
- }
64
- return coordinator;
69
+ const pending = this.pendingCursors.get(key);
70
+ if (!pending) return;
71
+ for (const eventId of eventIds) pending.delete(eventId.serialize());
65
72
  }
66
73
  async handle(savedChange) {
67
- const checkpointId = this.projection.getCheckpointId(savedChange);
68
- const eventCoordinator = this.getEventCoordinator(checkpointId);
69
- eventCoordinator.addEvent(savedChange);
70
- await eventCoordinator.waitCurrentEvent();
71
- if (!eventCoordinator.canProceed(savedChange)) {
72
- eventCoordinator.cleanEvent(savedChange);
74
+ const key = this.projection.getCheckpointId(savedChange).serialize();
75
+ const cursor = await this.getCursor(savedChange);
76
+ if (!cursor) throw new Error(`Cursor not found for event ${savedChange.id.serialize()}`);
77
+ if (this.processingCheckpoints.has(key)) {
78
+ if (!this.pendingCursors.has(key)) this.pendingCursors.set(key, /* @__PURE__ */ new Map());
79
+ const eventId = savedChange.id.serialize();
80
+ this.pendingCursors.get(key).set(eventId, {
81
+ savedChange,
82
+ cursor
83
+ });
84
+ const existing = this.processingCheckpoints.get(key);
85
+ if (!existing || cursor.isAfter(existing.cursor)) this.processingCheckpoints.set(key, {
86
+ savedChange,
87
+ cursor
88
+ });
89
+ const coalesced = (this.coalescedCounts.get(key) ?? 0) + 1;
90
+ this.coalescedCounts.set(key, coalesced);
91
+ this.logger.debug(`debounced: Checkpoint<${key}> is processing, Event<${eventId}> coalesced (${coalesced} pending)`, {
92
+ checkpointId: key,
93
+ eventId,
94
+ coalesced
95
+ });
73
96
  return;
74
97
  }
75
- const disposeEventCoordinator = eventCoordinator.start(savedChange);
76
- const target = await this.getCursor(savedChange);
77
- if (!target) {
78
- disposeEventCoordinator();
79
- throw new Error(`Cursor not found for event ${savedChange.id.serialize()}`);
98
+ this.processingCheckpoints.set(key, null);
99
+ try {
100
+ await this.handleOne(savedChange, cursor);
101
+ const nextPending = () => {
102
+ const debounced = this.processingCheckpoints.get(key);
103
+ if (debounced) {
104
+ this.processingCheckpoints.set(key, null);
105
+ return {
106
+ type: "debounced",
107
+ savedChange: debounced.savedChange,
108
+ cursor: debounced.cursor,
109
+ eventId: debounced.savedChange.id.serialize()
110
+ };
111
+ }
112
+ const remaining = this.pendingCursors.get(key);
113
+ if (remaining && remaining.size > 0) {
114
+ const [eventId, straggler] = remaining.entries().next().value;
115
+ remaining.delete(eventId);
116
+ return {
117
+ type: "straggler",
118
+ savedChange: straggler.savedChange,
119
+ cursor: straggler.cursor,
120
+ eventId
121
+ };
122
+ }
123
+ return null;
124
+ };
125
+ let iterations = 0;
126
+ for (let next = nextPending(); next; next = nextPending()) {
127
+ iterations++;
128
+ if (iterations > 10) this.logger.warn(`high-iteration: Checkpoint<${key}> re-run loop at iteration ${iterations}`, {
129
+ checkpointId: key,
130
+ iterations
131
+ });
132
+ if (next.type === "debounced") this.logger.debug(`re-run: Checkpoint<${key}> iteration ${iterations}, targeting Event<${next.eventId}>`, {
133
+ checkpointId: key,
134
+ iterations,
135
+ eventId: next.eventId
136
+ });
137
+ else {
138
+ const remaining = this.pendingCursors.get(key);
139
+ this.logger.info(`straggler: Checkpoint<${key}> handling Event<${next.eventId}> not covered by any slice (${remaining?.size ?? 0} remaining)`, {
140
+ checkpointId: key,
141
+ eventId: next.eventId,
142
+ remainingStragglers: remaining?.size ?? 0
143
+ });
144
+ }
145
+ await this.handleOne(next.savedChange, next.cursor);
146
+ }
147
+ } finally {
148
+ this.processingCheckpoints.delete(key);
149
+ this.pendingCursors.delete(key);
150
+ this.coalescedCounts.delete(key);
80
151
  }
152
+ }
153
+ async handleOne(savedChange, target) {
154
+ const checkpointId = this.projection.getCheckpointId(savedChange);
155
+ const eventId = savedChange.id.serialize();
156
+ const checkpointKey = checkpointId.serialize();
157
+ const startedAt = Date.now();
158
+ this.logger.info(`processing: Checkpoint<${checkpointKey}> targeting Event<${eventId}>`, {
159
+ checkpointId: checkpointKey,
160
+ eventId,
161
+ target: target.toString(),
162
+ targetRef: target.ref
163
+ });
81
164
  const errors = [];
82
165
  for await (const [attempt, reset] of this.breathe()) {
83
166
  const source = this.projection.getSource(savedChange);
@@ -88,13 +171,24 @@ var FirestoreProjector = class {
88
171
  }
89
172
  if (status === Status.SUCCESS) {
90
173
  await this.queue.cleanup(checkpointId);
91
- disposeEventCoordinator();
174
+ const durationMs = Date.now() - startedAt;
175
+ this.logger.info(`processed: Checkpoint<${checkpointKey}> caught up to Event<${eventId}> in ${durationMs}ms`, {
176
+ checkpointId: checkpointKey,
177
+ eventId,
178
+ durationMs
179
+ });
92
180
  return;
93
181
  }
94
182
  errors.push(message);
95
183
  }
96
- disposeEventCoordinator();
97
- throw new Error(`Failed to handle event ${savedChange.id.serialize()}: ${errors.join(", ")}`);
184
+ const { attempts } = this.config.retry;
185
+ this.logger.error(`failed: Checkpoint<${checkpointKey}> exhausted ${attempts} retries for Event<${eventId}>`, {
186
+ checkpointId: checkpointKey,
187
+ eventId,
188
+ attempts,
189
+ errors
190
+ });
191
+ throw new Error(`Failed to handle event ${eventId}: ${errors.join(", ")}`);
98
192
  }
99
193
  async getCursor(savedChange) {
100
194
  return await this.reader.getCursor(savedChange);
@@ -108,7 +202,10 @@ var FirestoreProjector = class {
108
202
  }
109
203
  if (!isTargetAfterHead) {
110
204
  const processed = await this.checkIsProcessed(checkpointId, target);
111
- if (processed === TaskState.PROCESSED) return [Status.SUCCESS, "Target event already processed"];
205
+ if (processed === TaskState.PROCESSED) {
206
+ this.prunePendingCursors(checkpointId, [target.eventId]);
207
+ return [Status.SUCCESS, "Target event already processed"];
208
+ }
112
209
  if (processed === TaskState.MISSING) {
113
210
  const [status, message] = await this.enqueueOne(checkpointId, target);
114
211
  if (status === Status.DEFERRED) return [Status.FAILURE, message];
@@ -137,14 +234,25 @@ var FirestoreProjector = class {
137
234
  const settings = this.projection.getTaskSettings(e);
138
235
  return Task.new(e, settings);
139
236
  });
140
- return await this.queue.enqueue(checkpointId, tasks);
237
+ const result = await this.queue.enqueue(checkpointId, tasks);
238
+ const [status] = result;
239
+ if (status === Status.SUCCESS) this.prunePendingCursors(checkpointId, tasks.map((t) => t.id));
240
+ const checkpointKey = checkpointId.serialize();
241
+ this.logger.debug(`enqueued: Checkpoint<${checkpointKey}> added ${tasks.length} tasks to queue`, {
242
+ checkpointId: checkpointKey,
243
+ count: tasks.length
244
+ });
245
+ return result;
141
246
  }
142
247
  async enqueueOne(checkpointId, target) {
143
248
  const event = await this.reader.get(target);
144
249
  if (!event) throw new Error(`Event not found for cursor ${target.ref}`);
145
250
  const settings = this.projection.getTaskSettings(event);
146
251
  const task = Task.new(event, settings);
147
- return await this.queue.enqueue(checkpointId, [task]);
252
+ const result = await this.queue.enqueue(checkpointId, [task]);
253
+ const [status] = result;
254
+ if (status === Status.SUCCESS) this.prunePendingCursors(checkpointId, [task.id]);
255
+ return result;
148
256
  }
149
257
  async checkIsProcessed(checkpointId, cursor) {
150
258
  return await this.queue.isProcessed(checkpointId, cursor);
@@ -174,6 +282,13 @@ var FirestoreProjector = class {
174
282
  if ((await this.projection.process(filtered, context)).some((id) => id?.equals(targetEventId))) return [Status.SUCCESS, "Target event processed successfully"];
175
283
  return [Status.DEFERRED, "Target event not processed yet"];
176
284
  } catch (e) {
285
+ const checkpointKey = checkpointId.serialize();
286
+ const errorMessage = e instanceof Error ? e.message : String(e);
287
+ const truncated = errorMessage.length > 200 ? `${errorMessage.slice(0, 200)}...` : errorMessage;
288
+ this.logger.error(`error: Checkpoint<${checkpointKey}> processing failed: ${truncated}`, {
289
+ checkpointId: checkpointKey,
290
+ error: e
291
+ });
177
292
  this.config.onProcessError(e);
178
293
  if (this._unclaim) await this.queue.unclaim(checkpointId, tasks);
179
294
  if (!hasTarget) return [Status.DEFERRED, "Target event not in claimed batch, deferring"];
@@ -496,7 +611,9 @@ var Task = class Task extends (0, _ddd_ts_shape.Shape)({
496
611
  static deserializeWithLastUpdateTime(data, timestamp) {
497
612
  return Task.deserialize({
498
613
  ...data,
499
- lastUpdateTime: timestamp
614
+ lastUpdateTime: timestamp,
615
+ claimIds: data.claimIds || [],
616
+ claimsMetadata: data.claimsMetadata || {}
500
617
  });
501
618
  }
502
619
  static batch(tasks) {
@@ -1,4 +1,3 @@
1
- import { EventCoordinator } from "./event-coordinator.mjs";
2
1
  import { Cursor, EventId, Lock, ProjectedStreamReader } from "@ddd-ts/core";
3
2
  import { DefaultConverter } from "@ddd-ts/store-firestore";
4
3
  import { FieldValue, Timestamp } from "firebase-admin/firestore";
@@ -17,6 +16,12 @@ const TaskState = {
17
16
  };
18
17
  const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
19
18
  const RETENTION = MicrosecondTimestamp.MONTH;
19
+ const defaultLogger = {
20
+ debug: (msg, ctx) => console.debug(msg, ctx ?? ""),
21
+ info: (msg, ctx) => console.info(msg, ctx ?? ""),
22
+ warn: (msg, ctx) => console.warn(msg, ctx ?? ""),
23
+ error: (msg, ctx) => console.error(msg, ctx ?? "")
24
+ };
20
25
  var FirestoreProjector = class {
21
26
  _unclaim = true;
22
27
  constructor(projection, reader, queue, config = {
@@ -27,6 +32,7 @@ var FirestoreProjector = class {
27
32
  backoff: 1.5
28
33
  },
29
34
  enqueue: { batchSize: 100 },
35
+ logger: defaultLogger,
30
36
  onProcessError: (error) => {
31
37
  console.error("Error processing event:", error);
32
38
  },
@@ -39,6 +45,9 @@ var FirestoreProjector = class {
39
45
  this.queue = queue;
40
46
  this.config = config;
41
47
  }
48
+ get logger() {
49
+ return this.config.logger ?? defaultLogger;
50
+ }
42
51
  async *breathe() {
43
52
  const { attempts, minDelay, maxDelay, backoff } = this.config.retry;
44
53
  for (let i = 0; i < attempts; i++) {
@@ -51,32 +60,106 @@ var FirestoreProjector = class {
51
60
  await wait((backoff * i + 1) * minDelay + jitter);
52
61
  }
53
62
  }
54
- eventCoordinators = /* @__PURE__ */ new Map();
55
- getEventCoordinator(checkpointId) {
63
+ processingCheckpoints = /* @__PURE__ */ new Map();
64
+ coalescedCounts = /* @__PURE__ */ new Map();
65
+ pendingCursors = /* @__PURE__ */ new Map();
66
+ prunePendingCursors(checkpointId, eventIds) {
56
67
  const key = checkpointId.serialize();
57
- let coordinator = this.eventCoordinators.get(key);
58
- if (!coordinator) {
59
- coordinator = new EventCoordinator();
60
- coordinator.onEmpty(() => this.eventCoordinators.delete(key));
61
- this.eventCoordinators.set(key, coordinator);
62
- }
63
- return coordinator;
68
+ const pending = this.pendingCursors.get(key);
69
+ if (!pending) return;
70
+ for (const eventId of eventIds) pending.delete(eventId.serialize());
64
71
  }
65
72
  async handle(savedChange) {
66
- const checkpointId = this.projection.getCheckpointId(savedChange);
67
- const eventCoordinator = this.getEventCoordinator(checkpointId);
68
- eventCoordinator.addEvent(savedChange);
69
- await eventCoordinator.waitCurrentEvent();
70
- if (!eventCoordinator.canProceed(savedChange)) {
71
- eventCoordinator.cleanEvent(savedChange);
73
+ const key = this.projection.getCheckpointId(savedChange).serialize();
74
+ const cursor = await this.getCursor(savedChange);
75
+ if (!cursor) throw new Error(`Cursor not found for event ${savedChange.id.serialize()}`);
76
+ if (this.processingCheckpoints.has(key)) {
77
+ if (!this.pendingCursors.has(key)) this.pendingCursors.set(key, /* @__PURE__ */ new Map());
78
+ const eventId = savedChange.id.serialize();
79
+ this.pendingCursors.get(key).set(eventId, {
80
+ savedChange,
81
+ cursor
82
+ });
83
+ const existing = this.processingCheckpoints.get(key);
84
+ if (!existing || cursor.isAfter(existing.cursor)) this.processingCheckpoints.set(key, {
85
+ savedChange,
86
+ cursor
87
+ });
88
+ const coalesced = (this.coalescedCounts.get(key) ?? 0) + 1;
89
+ this.coalescedCounts.set(key, coalesced);
90
+ this.logger.debug(`debounced: Checkpoint<${key}> is processing, Event<${eventId}> coalesced (${coalesced} pending)`, {
91
+ checkpointId: key,
92
+ eventId,
93
+ coalesced
94
+ });
72
95
  return;
73
96
  }
74
- const disposeEventCoordinator = eventCoordinator.start(savedChange);
75
- const target = await this.getCursor(savedChange);
76
- if (!target) {
77
- disposeEventCoordinator();
78
- throw new Error(`Cursor not found for event ${savedChange.id.serialize()}`);
97
+ this.processingCheckpoints.set(key, null);
98
+ try {
99
+ await this.handleOne(savedChange, cursor);
100
+ const nextPending = () => {
101
+ const debounced = this.processingCheckpoints.get(key);
102
+ if (debounced) {
103
+ this.processingCheckpoints.set(key, null);
104
+ return {
105
+ type: "debounced",
106
+ savedChange: debounced.savedChange,
107
+ cursor: debounced.cursor,
108
+ eventId: debounced.savedChange.id.serialize()
109
+ };
110
+ }
111
+ const remaining = this.pendingCursors.get(key);
112
+ if (remaining && remaining.size > 0) {
113
+ const [eventId, straggler] = remaining.entries().next().value;
114
+ remaining.delete(eventId);
115
+ return {
116
+ type: "straggler",
117
+ savedChange: straggler.savedChange,
118
+ cursor: straggler.cursor,
119
+ eventId
120
+ };
121
+ }
122
+ return null;
123
+ };
124
+ let iterations = 0;
125
+ for (let next = nextPending(); next; next = nextPending()) {
126
+ iterations++;
127
+ if (iterations > 10) this.logger.warn(`high-iteration: Checkpoint<${key}> re-run loop at iteration ${iterations}`, {
128
+ checkpointId: key,
129
+ iterations
130
+ });
131
+ if (next.type === "debounced") this.logger.debug(`re-run: Checkpoint<${key}> iteration ${iterations}, targeting Event<${next.eventId}>`, {
132
+ checkpointId: key,
133
+ iterations,
134
+ eventId: next.eventId
135
+ });
136
+ else {
137
+ const remaining = this.pendingCursors.get(key);
138
+ this.logger.info(`straggler: Checkpoint<${key}> handling Event<${next.eventId}> not covered by any slice (${remaining?.size ?? 0} remaining)`, {
139
+ checkpointId: key,
140
+ eventId: next.eventId,
141
+ remainingStragglers: remaining?.size ?? 0
142
+ });
143
+ }
144
+ await this.handleOne(next.savedChange, next.cursor);
145
+ }
146
+ } finally {
147
+ this.processingCheckpoints.delete(key);
148
+ this.pendingCursors.delete(key);
149
+ this.coalescedCounts.delete(key);
79
150
  }
151
+ }
152
+ async handleOne(savedChange, target) {
153
+ const checkpointId = this.projection.getCheckpointId(savedChange);
154
+ const eventId = savedChange.id.serialize();
155
+ const checkpointKey = checkpointId.serialize();
156
+ const startedAt = Date.now();
157
+ this.logger.info(`processing: Checkpoint<${checkpointKey}> targeting Event<${eventId}>`, {
158
+ checkpointId: checkpointKey,
159
+ eventId,
160
+ target: target.toString(),
161
+ targetRef: target.ref
162
+ });
80
163
  const errors = [];
81
164
  for await (const [attempt, reset] of this.breathe()) {
82
165
  const source = this.projection.getSource(savedChange);
@@ -87,13 +170,24 @@ var FirestoreProjector = class {
87
170
  }
88
171
  if (status === Status.SUCCESS) {
89
172
  await this.queue.cleanup(checkpointId);
90
- disposeEventCoordinator();
173
+ const durationMs = Date.now() - startedAt;
174
+ this.logger.info(`processed: Checkpoint<${checkpointKey}> caught up to Event<${eventId}> in ${durationMs}ms`, {
175
+ checkpointId: checkpointKey,
176
+ eventId,
177
+ durationMs
178
+ });
91
179
  return;
92
180
  }
93
181
  errors.push(message);
94
182
  }
95
- disposeEventCoordinator();
96
- throw new Error(`Failed to handle event ${savedChange.id.serialize()}: ${errors.join(", ")}`);
183
+ const { attempts } = this.config.retry;
184
+ this.logger.error(`failed: Checkpoint<${checkpointKey}> exhausted ${attempts} retries for Event<${eventId}>`, {
185
+ checkpointId: checkpointKey,
186
+ eventId,
187
+ attempts,
188
+ errors
189
+ });
190
+ throw new Error(`Failed to handle event ${eventId}: ${errors.join(", ")}`);
97
191
  }
98
192
  async getCursor(savedChange) {
99
193
  return await this.reader.getCursor(savedChange);
@@ -107,7 +201,10 @@ var FirestoreProjector = class {
107
201
  }
108
202
  if (!isTargetAfterHead) {
109
203
  const processed = await this.checkIsProcessed(checkpointId, target);
110
- if (processed === TaskState.PROCESSED) return [Status.SUCCESS, "Target event already processed"];
204
+ if (processed === TaskState.PROCESSED) {
205
+ this.prunePendingCursors(checkpointId, [target.eventId]);
206
+ return [Status.SUCCESS, "Target event already processed"];
207
+ }
111
208
  if (processed === TaskState.MISSING) {
112
209
  const [status, message] = await this.enqueueOne(checkpointId, target);
113
210
  if (status === Status.DEFERRED) return [Status.FAILURE, message];
@@ -136,14 +233,25 @@ var FirestoreProjector = class {
136
233
  const settings = this.projection.getTaskSettings(e);
137
234
  return Task.new(e, settings);
138
235
  });
139
- return await this.queue.enqueue(checkpointId, tasks);
236
+ const result = await this.queue.enqueue(checkpointId, tasks);
237
+ const [status] = result;
238
+ if (status === Status.SUCCESS) this.prunePendingCursors(checkpointId, tasks.map((t) => t.id));
239
+ const checkpointKey = checkpointId.serialize();
240
+ this.logger.debug(`enqueued: Checkpoint<${checkpointKey}> added ${tasks.length} tasks to queue`, {
241
+ checkpointId: checkpointKey,
242
+ count: tasks.length
243
+ });
244
+ return result;
140
245
  }
141
246
  async enqueueOne(checkpointId, target) {
142
247
  const event = await this.reader.get(target);
143
248
  if (!event) throw new Error(`Event not found for cursor ${target.ref}`);
144
249
  const settings = this.projection.getTaskSettings(event);
145
250
  const task = Task.new(event, settings);
146
- return await this.queue.enqueue(checkpointId, [task]);
251
+ const result = await this.queue.enqueue(checkpointId, [task]);
252
+ const [status] = result;
253
+ if (status === Status.SUCCESS) this.prunePendingCursors(checkpointId, [task.id]);
254
+ return result;
147
255
  }
148
256
  async checkIsProcessed(checkpointId, cursor) {
149
257
  return await this.queue.isProcessed(checkpointId, cursor);
@@ -173,6 +281,13 @@ var FirestoreProjector = class {
173
281
  if ((await this.projection.process(filtered, context)).some((id) => id?.equals(targetEventId))) return [Status.SUCCESS, "Target event processed successfully"];
174
282
  return [Status.DEFERRED, "Target event not processed yet"];
175
283
  } catch (e) {
284
+ const checkpointKey = checkpointId.serialize();
285
+ const errorMessage = e instanceof Error ? e.message : String(e);
286
+ const truncated = errorMessage.length > 200 ? `${errorMessage.slice(0, 200)}...` : errorMessage;
287
+ this.logger.error(`error: Checkpoint<${checkpointKey}> processing failed: ${truncated}`, {
288
+ checkpointId: checkpointKey,
289
+ error: e
290
+ });
176
291
  this.config.onProcessError(e);
177
292
  if (this._unclaim) await this.queue.unclaim(checkpointId, tasks);
178
293
  if (!hasTarget) return [Status.DEFERRED, "Target event not in claimed batch, deferring"];
@@ -495,7 +610,9 @@ var Task = class Task extends Shape({
495
610
  static deserializeWithLastUpdateTime(data, timestamp) {
496
611
  return Task.deserialize({
497
612
  ...data,
498
- lastUpdateTime: timestamp
613
+ lastUpdateTime: timestamp,
614
+ claimIds: data.claimIds || [],
615
+ claimsMetadata: data.claimsMetadata || {}
499
616
  });
500
617
  }
501
618
  static batch(tasks) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ddd-ts/event-sourcing-firestore",
3
- "version": "0.0.42",
3
+ "version": "0.0.43",
4
4
  "types": "dist/index.d.ts",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -10,19 +10,19 @@
10
10
  "dist"
11
11
  ],
12
12
  "dependencies": {
13
- "@ddd-ts/core": "0.0.42",
14
- "@ddd-ts/shape": "0.0.42",
15
- "@ddd-ts/store-firestore": "0.0.42",
16
- "@ddd-ts/traits": "0.0.42",
17
- "@ddd-ts/types": "0.0.42",
13
+ "@ddd-ts/core": "0.0.43",
14
+ "@ddd-ts/shape": "0.0.43",
15
+ "@ddd-ts/store-firestore": "0.0.43",
16
+ "@ddd-ts/traits": "0.0.43",
17
+ "@ddd-ts/types": "0.0.43",
18
18
  "@opentelemetry/api": "1.6.0",
19
19
  "firebase-admin": "^13.2.0"
20
20
  },
21
21
  "devDependencies": {
22
- "@ddd-ts/shape": "0.0.42",
23
- "@ddd-ts/tests": "0.0.42",
24
- "@ddd-ts/tools": "0.0.42",
25
- "@ddd-ts/types": "0.0.42",
22
+ "@ddd-ts/shape": "0.0.43",
23
+ "@ddd-ts/tests": "0.0.43",
24
+ "@ddd-ts/tools": "0.0.43",
25
+ "@ddd-ts/types": "0.0.43",
26
26
  "@types/jest": "^29.5.1"
27
27
  },
28
28
  "exports": {
@@ -1,16 +0,0 @@
1
- import type { IEsEvent, ISavedChange } from "@ddd-ts/core";
2
- export declare class EventCoordinator {
3
- private eventProcessing;
4
- private currentEventId;
5
- private lastEvent;
6
- private isRunning;
7
- private _onEmpty;
8
- addEvent(event: ISavedChange<IEsEvent>): void;
9
- start(event: ISavedChange<IEsEvent>): () => void;
10
- waitCurrentEvent(): Promise<void>;
11
- cleanEvent(event: ISavedChange<IEsEvent>): void;
12
- canProceed(event: ISavedChange<IEsEvent>): boolean;
13
- onEmpty(callback: () => void): void;
14
- checkEmpty(): void;
15
- }
16
- //# sourceMappingURL=event-coordinator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-coordinator.d.ts","sourceRoot":"","sources":["../../src/projection/event-coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG3D,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,eAAe,CAAkD;IACzE,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAY;IAE5B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC;IAYtC,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC;IAU7B,gBAAgB;IAMtB,UAAU,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC;IAYxC,UAAU,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC;IAMxC,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI;IAI5B,UAAU;CAKX"}
@@ -1,47 +0,0 @@
1
- const require_promise_with_resolvers = require('../utils/promise-with-resolvers.js');
2
-
3
- //#region src/projection/event-coordinator.ts
4
- var EventCoordinator = class {
5
- eventProcessing = /* @__PURE__ */ new Map();
6
- currentEventId = null;
7
- lastEvent = null;
8
- isRunning = false;
9
- _onEmpty = () => {};
10
- addEvent(event) {
11
- const eventId = event.id.serialize();
12
- this.eventProcessing.set(eventId, require_promise_with_resolvers.promiseWithResolvers());
13
- if (this.lastEvent === null || this.lastEvent.revision < event.revision) this.lastEvent = event;
14
- }
15
- start(event) {
16
- if (this.isRunning) throw new Error("Event processing already in progress");
17
- this.currentEventId = event.id.serialize();
18
- this.isRunning = true;
19
- return () => this.cleanEvent(event);
20
- }
21
- async waitCurrentEvent() {
22
- if (!this.currentEventId) return;
23
- await this.eventProcessing.get(this.currentEventId)?.promise;
24
- }
25
- cleanEvent(event) {
26
- const eventId = event.id.serialize();
27
- this.eventProcessing.get(eventId)?.resolve();
28
- this.eventProcessing.delete(eventId);
29
- this.isRunning = false;
30
- this.currentEventId = null;
31
- this.checkEmpty();
32
- }
33
- canProceed(event) {
34
- if (this.isRunning) return false;
35
- const eventId = event.id.serialize();
36
- return this.lastEvent?.id.serialize() === eventId;
37
- }
38
- onEmpty(callback) {
39
- this._onEmpty = callback;
40
- }
41
- checkEmpty() {
42
- if (this.eventProcessing.size === 0) this._onEmpty();
43
- }
44
- };
45
-
46
- //#endregion
47
- exports.EventCoordinator = EventCoordinator;
@@ -1,47 +0,0 @@
1
- import { promiseWithResolvers } from "../utils/promise-with-resolvers.mjs";
2
-
3
- //#region src/projection/event-coordinator.ts
4
- var EventCoordinator = class {
5
- eventProcessing = /* @__PURE__ */ new Map();
6
- currentEventId = null;
7
- lastEvent = null;
8
- isRunning = false;
9
- _onEmpty = () => {};
10
- addEvent(event) {
11
- const eventId = event.id.serialize();
12
- this.eventProcessing.set(eventId, promiseWithResolvers());
13
- if (this.lastEvent === null || this.lastEvent.revision < event.revision) this.lastEvent = event;
14
- }
15
- start(event) {
16
- if (this.isRunning) throw new Error("Event processing already in progress");
17
- this.currentEventId = event.id.serialize();
18
- this.isRunning = true;
19
- return () => this.cleanEvent(event);
20
- }
21
- async waitCurrentEvent() {
22
- if (!this.currentEventId) return;
23
- await this.eventProcessing.get(this.currentEventId)?.promise;
24
- }
25
- cleanEvent(event) {
26
- const eventId = event.id.serialize();
27
- this.eventProcessing.get(eventId)?.resolve();
28
- this.eventProcessing.delete(eventId);
29
- this.isRunning = false;
30
- this.currentEventId = null;
31
- this.checkEmpty();
32
- }
33
- canProceed(event) {
34
- if (this.isRunning) return false;
35
- const eventId = event.id.serialize();
36
- return this.lastEvent?.id.serialize() === eventId;
37
- }
38
- onEmpty(callback) {
39
- this._onEmpty = callback;
40
- }
41
- checkEmpty() {
42
- if (this.eventProcessing.size === 0) this._onEmpty();
43
- }
44
- };
45
-
46
- //#endregion
47
- export { EventCoordinator };
@@ -1,7 +0,0 @@
1
- export interface PromiseResolvers<T> {
2
- promise: Promise<T>;
3
- resolve: (value: T) => void;
4
- reject: (reason?: any) => void;
5
- }
6
- export declare const promiseWithResolvers: <T>() => PromiseResolvers<T>;
7
- //# sourceMappingURL=promise-with-resolvers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"promise-with-resolvers.d.ts","sourceRoot":"","sources":["../../src/utils/promise-with-resolvers.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;CAChC;AAED,eAAO,MAAM,oBAAoB,GAAI,CAAC,OAAK,gBAAgB,CAAC,CAAC,CAQ5D,CAAC"}
@@ -1,17 +0,0 @@
1
-
2
- //#region src/utils/promise-with-resolvers.ts
3
- const promiseWithResolvers = () => {
4
- let resolve;
5
- let reject;
6
- return {
7
- promise: new Promise((res, rej) => {
8
- resolve = res;
9
- reject = rej;
10
- }),
11
- resolve,
12
- reject
13
- };
14
- };
15
-
16
- //#endregion
17
- exports.promiseWithResolvers = promiseWithResolvers;
@@ -1,16 +0,0 @@
1
- //#region src/utils/promise-with-resolvers.ts
2
- const promiseWithResolvers = () => {
3
- let resolve;
4
- let reject;
5
- return {
6
- promise: new Promise((res, rej) => {
7
- resolve = res;
8
- reject = rej;
9
- }),
10
- resolve,
11
- reject
12
- };
13
- };
14
-
15
- //#endregion
16
- export { promiseWithResolvers };