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.
@@ -1091,6 +1091,8 @@ var DataBusTraceReporter = class {
1091
1091
  // Bucketed histogram: bucket index = floor(delayMs / 50), capped at 19.
1092
1092
  latencyBuckets = new Array(LATENCY_BUCKET_COUNT).fill(0);
1093
1093
  latencySumMs = 0;
1094
+ dedupAccepted = 0;
1095
+ dedupSuppressed = 0;
1094
1096
  constructor(options, now = Date.now) {
1095
1097
  this.enabled = options?.enabled ?? false;
1096
1098
  this.mode = options?.mode ?? "all";
@@ -1166,6 +1168,13 @@ var DataBusTraceReporter = class {
1166
1168
  this.latencyBuckets[bucketIndex] = (this.latencyBuckets[bucketIndex] ?? 0) + 1;
1167
1169
  this.latencySumMs += delayMs;
1168
1170
  }
1171
+ /** Record deduplication outcomes for the next metrics window. */
1172
+ recordDedupAccepted() {
1173
+ if (this.metricsActive) this.dedupAccepted += 1;
1174
+ }
1175
+ recordDedupSuppressed() {
1176
+ if (this.metricsActive) this.dedupSuppressed += 1;
1177
+ }
1169
1178
  /** True when metrics recording is active: enabled and mode includes metrics.
1170
1179
  * Extracted so the four record / flush methods share one guard expression
1171
1180
  * instead of repeating `!this.enabled || this.mode === 'events'` at each. */
@@ -1179,7 +1188,7 @@ var DataBusTraceReporter = class {
1179
1188
  }
1180
1189
  flushNow() {
1181
1190
  const timestamp = this.now();
1182
- if (this.received > 0 || this.dispatched > 0) {
1191
+ if (this.received > 0 || this.dispatched > 0 || this.dedupAccepted > 0 || this.dedupSuppressed > 0) {
1183
1192
  const samples = this.latencySamples;
1184
1193
  this.emit({
1185
1194
  type: "message_metrics",
@@ -1193,6 +1202,8 @@ var DataBusTraceReporter = class {
1193
1202
  dispatchP50Ms: roundMs(percentileMs(this.latencyBuckets, samples, 0.5)),
1194
1203
  dispatchP95Ms: roundMs(percentileMs(this.latencyBuckets, samples, 0.95)),
1195
1204
  dispatchMaxMs: roundMs(percentileMs(this.latencyBuckets, samples, 1)),
1205
+ dedupAccepted: this.dedupAccepted,
1206
+ dedupSuppressed: this.dedupSuppressed,
1196
1207
  timestamp
1197
1208
  });
1198
1209
  this.resetMetrics();
@@ -1207,6 +1218,8 @@ var DataBusTraceReporter = class {
1207
1218
  this.receivedAt.clear();
1208
1219
  this.latencyBuckets.fill(0);
1209
1220
  this.latencySumMs = 0;
1221
+ this.dedupAccepted = 0;
1222
+ this.dedupSuppressed = 0;
1210
1223
  }
1211
1224
  emit(event) {
1212
1225
  try {
@@ -1257,6 +1270,7 @@ var CrossTabDataBus = class _CrossTabDataBus {
1257
1270
  replayBuffers;
1258
1271
  replayMaxPerTopic;
1259
1272
  replayPersistence;
1273
+ replayRetentionMs;
1260
1274
  replayHydration;
1261
1275
  initialConfig;
1262
1276
  hasInitialConfig;
@@ -1304,6 +1318,10 @@ var CrossTabDataBus = class _CrossTabDataBus {
1304
1318
  this.replayMaxPerTopic = replay?.maxPerTopic ?? DEFAULT_REPLAY_MAX_PER_TOPIC;
1305
1319
  this.replayBuffers = replay ? /* @__PURE__ */ new Map() : null;
1306
1320
  this.replayPersistence = replay?.persistence ?? null;
1321
+ this.replayRetentionMs = replay?.retentionMs;
1322
+ if (this.replayRetentionMs !== void 0 && (!Number.isFinite(this.replayRetentionMs) || this.replayRetentionMs <= 0)) {
1323
+ throw new TypeError("replay.retentionMs must be a positive finite number.");
1324
+ }
1307
1325
  this.replayHydration = this.hydrateReplay();
1308
1326
  const { autoStart, initialConfig, trace, transport, dedup, ...clusterOptions } = options;
1309
1327
  this.transport = transport;
@@ -1663,10 +1681,12 @@ var CrossTabDataBus = class _CrossTabDataBus {
1663
1681
  if (this.seenMessageIds.has(message.messageId)) {
1664
1682
  this.trace.event({ type: "reliability", operation: "dedup_suppressed", topic: message.topic });
1665
1683
  this.dedupSuppressed += 1;
1684
+ this.trace.recordDedupSuppressed();
1666
1685
  return true;
1667
1686
  }
1668
1687
  this.seenMessageIds.set(message.messageId, now);
1669
1688
  this.dedupAccepted += 1;
1689
+ this.trace.recordDedupAccepted();
1670
1690
  while (this.seenMessageIds.size > this.dedupMaxEntries) {
1671
1691
  const oldest = this.seenMessageIds.keys().next().value;
1672
1692
  if (oldest === void 0) break;
@@ -1701,6 +1721,9 @@ var CrossTabDataBus = class _CrossTabDataBus {
1701
1721
  if (buffer.length > this.replayMaxPerTopic) buffer.shift();
1702
1722
  if (this.replayPersistence) {
1703
1723
  void this.replayPersistence.append(storedMessage).catch((error) => this.reportError(error));
1724
+ if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {
1725
+ void this.replayPersistence.clearBefore(Date.now() - this.replayRetentionMs).catch((error) => this.reportError(error));
1726
+ }
1704
1727
  }
1705
1728
  }
1706
1729
  async hydrateReplay() {
@@ -1708,6 +1731,9 @@ var CrossTabDataBus = class _CrossTabDataBus {
1708
1731
  return;
1709
1732
  }
1710
1733
  try {
1734
+ if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {
1735
+ await this.replayPersistence.clearBefore(Date.now() - this.replayRetentionMs);
1736
+ }
1711
1737
  for (const message of await this.replayPersistence.load()) {
1712
1738
  let buffer = this.replayBuffers.get(message.topic);
1713
1739
  if (!buffer) {