cross-tab-worker-databus 0.10.0 → 0.11.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.
@@ -1066,6 +1066,8 @@ var DataBusTraceReporter = class {
1066
1066
  // Bucketed histogram: bucket index = floor(delayMs / 50), capped at 19.
1067
1067
  latencyBuckets = new Array(LATENCY_BUCKET_COUNT).fill(0);
1068
1068
  latencySumMs = 0;
1069
+ dedupAccepted = 0;
1070
+ dedupSuppressed = 0;
1069
1071
  constructor(options, now = Date.now) {
1070
1072
  this.enabled = options?.enabled ?? false;
1071
1073
  this.mode = options?.mode ?? "all";
@@ -1141,6 +1143,13 @@ var DataBusTraceReporter = class {
1141
1143
  this.latencyBuckets[bucketIndex] = (this.latencyBuckets[bucketIndex] ?? 0) + 1;
1142
1144
  this.latencySumMs += delayMs;
1143
1145
  }
1146
+ /** Record deduplication outcomes for the next metrics window. */
1147
+ recordDedupAccepted() {
1148
+ if (this.metricsActive) this.dedupAccepted += 1;
1149
+ }
1150
+ recordDedupSuppressed() {
1151
+ if (this.metricsActive) this.dedupSuppressed += 1;
1152
+ }
1144
1153
  /** True when metrics recording is active: enabled and mode includes metrics.
1145
1154
  * Extracted so the four record / flush methods share one guard expression
1146
1155
  * instead of repeating `!this.enabled || this.mode === 'events'` at each. */
@@ -1154,7 +1163,7 @@ var DataBusTraceReporter = class {
1154
1163
  }
1155
1164
  flushNow() {
1156
1165
  const timestamp = this.now();
1157
- if (this.received > 0 || this.dispatched > 0) {
1166
+ if (this.received > 0 || this.dispatched > 0 || this.dedupAccepted > 0 || this.dedupSuppressed > 0) {
1158
1167
  const samples = this.latencySamples;
1159
1168
  this.emit({
1160
1169
  type: "message_metrics",
@@ -1168,6 +1177,8 @@ var DataBusTraceReporter = class {
1168
1177
  dispatchP50Ms: roundMs(percentileMs(this.latencyBuckets, samples, 0.5)),
1169
1178
  dispatchP95Ms: roundMs(percentileMs(this.latencyBuckets, samples, 0.95)),
1170
1179
  dispatchMaxMs: roundMs(percentileMs(this.latencyBuckets, samples, 1)),
1180
+ dedupAccepted: this.dedupAccepted,
1181
+ dedupSuppressed: this.dedupSuppressed,
1171
1182
  timestamp
1172
1183
  });
1173
1184
  this.resetMetrics();
@@ -1182,6 +1193,8 @@ var DataBusTraceReporter = class {
1182
1193
  this.receivedAt.clear();
1183
1194
  this.latencyBuckets.fill(0);
1184
1195
  this.latencySumMs = 0;
1196
+ this.dedupAccepted = 0;
1197
+ this.dedupSuppressed = 0;
1185
1198
  }
1186
1199
  emit(event) {
1187
1200
  try {
@@ -1232,6 +1245,7 @@ var CrossTabDataBus = class _CrossTabDataBus {
1232
1245
  replayBuffers;
1233
1246
  replayMaxPerTopic;
1234
1247
  replayPersistence;
1248
+ replayRetentionMs;
1235
1249
  replayHydration;
1236
1250
  initialConfig;
1237
1251
  hasInitialConfig;
@@ -1279,6 +1293,10 @@ var CrossTabDataBus = class _CrossTabDataBus {
1279
1293
  this.replayMaxPerTopic = replay?.maxPerTopic ?? DEFAULT_REPLAY_MAX_PER_TOPIC;
1280
1294
  this.replayBuffers = replay ? /* @__PURE__ */ new Map() : null;
1281
1295
  this.replayPersistence = replay?.persistence ?? null;
1296
+ this.replayRetentionMs = replay?.retentionMs;
1297
+ if (this.replayRetentionMs !== void 0 && (!Number.isFinite(this.replayRetentionMs) || this.replayRetentionMs <= 0)) {
1298
+ throw new TypeError("replay.retentionMs must be a positive finite number.");
1299
+ }
1282
1300
  this.replayHydration = this.hydrateReplay();
1283
1301
  const { autoStart, initialConfig, trace, transport, dedup, ...clusterOptions } = options;
1284
1302
  this.transport = transport;
@@ -1638,10 +1656,12 @@ var CrossTabDataBus = class _CrossTabDataBus {
1638
1656
  if (this.seenMessageIds.has(message.messageId)) {
1639
1657
  this.trace.event({ type: "reliability", operation: "dedup_suppressed", topic: message.topic });
1640
1658
  this.dedupSuppressed += 1;
1659
+ this.trace.recordDedupSuppressed();
1641
1660
  return true;
1642
1661
  }
1643
1662
  this.seenMessageIds.set(message.messageId, now);
1644
1663
  this.dedupAccepted += 1;
1664
+ this.trace.recordDedupAccepted();
1645
1665
  while (this.seenMessageIds.size > this.dedupMaxEntries) {
1646
1666
  const oldest = this.seenMessageIds.keys().next().value;
1647
1667
  if (oldest === void 0) break;
@@ -1676,6 +1696,9 @@ var CrossTabDataBus = class _CrossTabDataBus {
1676
1696
  if (buffer.length > this.replayMaxPerTopic) buffer.shift();
1677
1697
  if (this.replayPersistence) {
1678
1698
  void this.replayPersistence.append(storedMessage).catch((error) => this.reportError(error));
1699
+ if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {
1700
+ void this.replayPersistence.clearBefore(Date.now() - this.replayRetentionMs).catch((error) => this.reportError(error));
1701
+ }
1679
1702
  }
1680
1703
  }
1681
1704
  async hydrateReplay() {
@@ -1683,6 +1706,9 @@ var CrossTabDataBus = class _CrossTabDataBus {
1683
1706
  return;
1684
1707
  }
1685
1708
  try {
1709
+ if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {
1710
+ await this.replayPersistence.clearBefore(Date.now() - this.replayRetentionMs);
1711
+ }
1686
1712
  for (const message of await this.replayPersistence.load()) {
1687
1713
  let buffer = this.replayBuffers.get(message.topic);
1688
1714
  if (!buffer) {