@peerbit/shared-log 15.0.0 → 16.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.
package/dist/src/index.js CHANGED
@@ -66,7 +66,7 @@ import { NativeBackboneWriteThroughBlockStore } from "./native-write-through-blo
66
66
  import { PeerSessionRegistry } from "./peer-session.js";
67
67
  import { PIDReplicationController } from "./pid.js";
68
68
  import { EntryReplicatedU32, EntryReplicatedU64, ReplicationIntent, ReplicationRangeIndexableU32, ReplicationRangeIndexableU64, ReplicationRangeMessage, appromixateCoverage, calculateCoverage, countCoveringRangesSameOwner, createAssignedRangesQuery, debounceAggregationChanges, getAllMergeCandiates, getCoverSet, getSamples, isEntryReplicated, isMatured, isReplicationRangeMessage, mergeRanges, minimumWidthToCover, shouldAssigneToRangeBoundary as shouldAssignToRangeBoundary, toRebalance, } from "./ranges.js";
69
- import { ReplicationAnnouncementCoordinator, isTransientReplicationAnnouncementError, } from "./replication-announcement.js";
69
+ import { ReplicationAnnouncementCoordinator } from "./replication-announcement.js";
70
70
  import { createReplicationDomainHash, } from "./replication-domain-hash.js";
71
71
  import { createReplicationDomainTime, } from "./replication-domain-time.js";
72
72
  import {} from "./replication-domain.js";
@@ -74,7 +74,6 @@ import { ReplicationInfoV2ReceiveCoordinator } from "./replication-info-v2-recei
74
74
  import { ReplicationInfoV2SendCoordinator } from "./replication-info-v2-send.js";
75
75
  import { AbsoluteReplicas, AddedReplicationInfoV2Message, AddedReplicationSegmentMessage, AllReplicatingSegmentsMessage, FullReplicationInfoV2Message, MinReplicas, ReplicationError, ReplicationPingMessage, RequestReplicationInfoMessage, RequestReplicationInfoV2Message, ResponseRoleMessage, StoppedReplicating, StoppedReplicationInfoV2Message, decodeReplicas, encodeReplicas, isReplicationInfoV2Message, maxReplicas, } from "./replication.js";
76
76
  import { ReplicatorLivenessMonitor } from "./replicator-liveness.js";
77
- import { Observer, Replicator } from "./role.js";
78
77
  import { createSyncronizer } from "./sync/factory.js";
79
78
  import { emitSyncProfileDuration, emitSyncProfileEvent, syncProfileStart, } from "./sync/profile.js";
80
79
  import { ConfirmEntriesMessage, SYNC_MESSAGE_PRIORITY, SimpleSyncronizer, } from "./sync/simple.js";
@@ -682,24 +681,17 @@ let SharedLog = (() => {
682
681
  _pendingIHave;
683
682
  // public key hash to range id to range
684
683
  pendingMaturity; // map of peerId to timeout
685
- // Stage-4 KEEP-OLD verdict (fence B8, split by role). The watermark's
686
- // FENCING role rejecting late replication-info across unsubscribe and
687
- // eviction races is fully subsumed by the per-peer receive epoch plus
688
- // the blocked set and session identity: every unsubscribe-path `now` write
689
- // is preceded in the same synchronous block by a blocked-add, so an
690
- // admitted handler can never observe one. Its intra-epoch ORDERING role is
691
- // NOT subsumed: within one (lifecycle, session, epoch, unblocked) regime
692
- // the epoch token is constant across every message from the peer, so only
693
- // the two apply-lane timestamp comparisons can drop an older reset
694
- // delivered after a newer add (unordered pubsub / retransmits) — an
695
- // identity token carries no order. Deletion is blocked until
696
- // replication-info messages carry sender-authoritative sequence numbers
697
- // (stage-5 schema change); the `receive admission replication-info
698
- // ordering watermark` pins fail if the read sites are removed before then.
699
- latestReplicationInfoMessage;
684
+ // The legacy replication-info ordering watermark (fence B8) is deleted:
685
+ // its FENCING role was subsumed by the per-peer receive epoch, blocked set
686
+ // and session identity, and its intra-epoch ORDERING role existed only for
687
+ // the legacy apply lanes. The V2 lane orders by sender-authoritative
688
+ // sequence numbers, and legacy frames are dropped unconditionally at the
689
+ // B1 gate before any side effect.
700
690
  // The replication-info blocked set (fence B5) lives on the peer-session
701
691
  // registry: unsubscribed peers whose replication-info is ignored until a
702
692
  // reconnect barrier commits. See PeerSessionRegistry._replicationInfoBlockedPeers.
693
+ // V2 recovery scheduler state, one row per open peer session (B12: the
694
+ // legacy request scheduler that shared this map is deleted).
703
695
  _replicationInfoRequestByPeer;
704
696
  _replicationInfoApplyQueueByPeer;
705
697
  // One in-flight targeted subscriber-snapshot request per session-less peer.
@@ -841,7 +833,6 @@ let SharedLog = (() => {
841
833
  for (const hash of this._checkedPrune?.retries.keys() ?? []) {
842
834
  this._checkedPrune.clearRetry(hash);
843
835
  }
844
- this._announcements.cancelCurrentReplicationStateAnnouncementRetry();
845
836
  this.joinWarmup.cancelAllJoinWarmupTargets();
846
837
  for (const timer of this._repairRetryTimers) {
847
838
  clearTimeout(timer);
@@ -1707,25 +1698,9 @@ let SharedLog = (() => {
1707
1698
  }
1708
1699
  createReplicationAnnouncementCoordinator() {
1709
1700
  return new ReplicationAnnouncementCoordinator({
1710
- // Route re-entrant queueing through the owner so coordinator spies keep
1711
- // observing it (the poison guard assertions in events.spec.ts depend on
1712
- // this).
1713
- queueCurrentReplicationStateAnnouncementRepair: () => this._announcements.queueCurrentReplicationStateAnnouncementRepair(),
1714
- queueCurrentReplicationStateAnnouncementRetry: (error) => this._announcements.queueCurrentReplicationStateAnnouncementRetry(error),
1715
1701
  enqueueReplicationInfoV2: (message) => this._v2Send.enqueue(message),
1716
- isLegacyReplicationInfoEnabled: () => this.legacyReplicationInfoEnabled,
1717
- isClosed: () => this.closed,
1718
- getCloseSignal: () => this._closeController.signal,
1719
- getMyReplicationSegments: () => this.getMyReplicationSegments(),
1720
- validatePersistedReplicationRangeSnapshot: (ranges) => this.validatePersistedReplicationRangeSnapshot(ranges),
1721
- getSubscribers: () => this.node.services.pubsub.getSubscribers(this.topic),
1722
- getSelfHash: () => this.node.identity.publicKey.hashcode(),
1723
- isBlockedPeer: (hash) => this._peerSessions.isReplicationInfoBlocked(hash),
1724
- getRpc: () => this.rpc,
1725
1702
  captureReplicationOwnershipLifecycle: () => this.captureReplicationOwnershipLifecycle(),
1726
1703
  throwIfReplicationOwnershipLifecycleInactive: (controller) => this.throwIfReplicationOwnershipLifecycleInactive(controller),
1727
- isAdaptiveReplicating: () => this._isAdaptiveReplicating,
1728
- callRebalanceParticipationDebounced: () => this.rebalanceParticipationDebounced?.call(),
1729
1704
  });
1730
1705
  }
1731
1706
  createReplicationInfoV2SendCoordinator() {
@@ -1885,7 +1860,6 @@ let SharedLog = (() => {
1885
1860
  this._admittedPruneRemoves = new Set();
1886
1861
  this._pendingIHave = new Map();
1887
1862
  this._pendingIHaveCallbacks = new Set();
1888
- this.latestReplicationInfoMessage = new Map();
1889
1863
  this._replicationInfoRequestByPeer = new Map();
1890
1864
  this._subscriberSnapshotRequestsByPeer = new Map();
1891
1865
  this._replicationInfoApplyQueueByPeer = new Map();
@@ -1985,25 +1959,9 @@ let SharedLog = (() => {
1985
1959
  this._nativeStrictDurableDocumentRecoveryDeferred ??= false;
1986
1960
  this._nativeStrictDurableTransactionsClosing ??= false;
1987
1961
  }
1988
- get compatibility() {
1989
- // B12: the open option was removed and any defined value rejects at
1990
- // open(); this is permanently undefined and dies with the residual
1991
- // gates in a later cleanup stage.
1992
- return this._logProperties?.compatibility;
1993
- }
1994
- /**
1995
- * Legacy replication-info is an explicit compatibility fallback. Current
1996
- * logs never infer or re-enable it from a remote peer's capabilities.
1997
- */
1998
- get legacyReplicationInfoEnabled() {
1999
- return this.compatibility !== undefined && this.compatibility < 10;
2000
- }
2001
1962
  get isAdaptiveReplicating() {
2002
1963
  return this._isAdaptiveReplicating;
2003
1964
  }
2004
- get v8Behaviour() {
2005
- return (this.compatibility ?? Number.MAX_VALUE) < 9;
2006
- }
2007
1965
  getFanoutChannelOptions(options) {
2008
1966
  return {
2009
1967
  ...DEFAULT_SHARED_LOG_FANOUT_CHANNEL_OPTIONS,
@@ -2692,8 +2650,7 @@ let SharedLog = (() => {
2692
2650
  !delivery &&
2693
2651
  !requireRecipients &&
2694
2652
  (leaders.size === 0 || (leaders.size === 1 && leaders.has(selfHash)))) {
2695
- const allowSubscriberFallback = this.syncronizer instanceof SimpleSyncronizer ||
2696
- (this.compatibility ?? Number.MAX_VALUE) < 10;
2653
+ const allowSubscriberFallback = this.syncronizer instanceof SimpleSyncronizer;
2697
2654
  if (!allowSubscriberFallback) {
2698
2655
  return;
2699
2656
  }
@@ -2816,8 +2773,7 @@ let SharedLog = (() => {
2816
2773
  // sweeps can still backfill peers that missed the initial delivery.
2817
2774
  const set = new Set(leaders.keys());
2818
2775
  let hasRemotePeers = set.has(selfHash) ? set.size > 1 : set.size > 0;
2819
- const allowSubscriberFallback = this.syncronizer instanceof SimpleSyncronizer ||
2820
- (this.compatibility ?? Number.MAX_VALUE) < 10;
2776
+ const allowSubscriberFallback = this.syncronizer instanceof SimpleSyncronizer;
2821
2777
  if (!hasRemotePeers && allowSubscriberFallback) {
2822
2778
  try {
2823
2779
  const subscribers = await this._getTopicSubscribers(this.topic);
@@ -3185,21 +3141,6 @@ let SharedLog = (() => {
3185
3141
  context: this.cloneLeaderSelectionContext(context),
3186
3142
  };
3187
3143
  }
3188
- // @deprecated
3189
- getRoleFromReplicationSegments(segments) {
3190
- if (segments.length > 1) {
3191
- throw new Error("More than one replication segment found. Can only use one segment for compatbility with v8");
3192
- }
3193
- if (segments.length > 0) {
3194
- const segment = segments[0].toReplicationRange();
3195
- return new Replicator({
3196
- factor: segment.factor / MAX_U32,
3197
- offset: segment.offset / MAX_U32,
3198
- });
3199
- }
3200
- // TODO this is not accurate but might be good enough
3201
- return new Observer();
3202
- }
3203
3144
  isTerminating() {
3204
3145
  return (this.acceptsParentAttachments === false ||
3205
3146
  this.closed ||
@@ -3398,10 +3339,7 @@ let SharedLog = (() => {
3398
3339
  this.rebalanceParticipationDebounced = rebalanceParticipationDebounced;
3399
3340
  }
3400
3341
  onRebalanceParticipationError(error) {
3401
- if (this.closed ||
3402
- isNotStartedError(error) ||
3403
- (isTransientReplicationAnnouncementError(error) &&
3404
- this._announcements._replicationAnnouncementRetryPending)) {
3342
+ if (this.closed || isNotStartedError(error)) {
3405
3343
  return;
3406
3344
  }
3407
3345
  // Debounced invocations run from an un-awaited timer. Throwing here would
@@ -9175,10 +9113,7 @@ let SharedLog = (() => {
9175
9113
  this._logProperties = options;
9176
9114
  this.domain = options?.domain
9177
9115
  ? options.domain(this)
9178
- : createReplicationDomainHash(options?.compatibility !== undefined &&
9179
- options.compatibility < 10
9180
- ? "u32"
9181
- : "u64")(this);
9116
+ : createReplicationDomainHash("u64")(this);
9182
9117
  this.indexableDomain = createIndexableDomainFromResolution(this.domain.resolution);
9183
9118
  this._respondToIHaveTimeout = options?.respondToIHaveTimeout ?? 2e4;
9184
9119
  this._checkedPrune = new CheckedPruneCoordinator();
@@ -9186,7 +9121,6 @@ let SharedLog = (() => {
9186
9121
  this._admittedPruneRemoves = new Set();
9187
9122
  this._pendingIHave = new Map();
9188
9123
  this._pendingIHaveCallbacks = new Set();
9189
- this.latestReplicationInfoMessage = new Map();
9190
9124
  this._replicationInfoRequestByPeer = new Map();
9191
9125
  this._subscriberSnapshotRequestsByPeer = new Map();
9192
9126
  // Terminal close/drop drains the previous lifecycle before another open can
@@ -9255,7 +9189,6 @@ let SharedLog = (() => {
9255
9189
  this._liveness.resetForOpen();
9256
9190
  this._lastLocalAppendAt = 0;
9257
9191
  this._announcements ??= this.createReplicationAnnouncementCoordinator();
9258
- this._announcements.resetForOpen();
9259
9192
  this._v2Receive ??= this.createReplicationInfoV2ReceiveCoordinator();
9260
9193
  this._v2Receive.resetForOpen();
9261
9194
  this._v2Send ??= this.createReplicationInfoV2SendCoordinator();
@@ -9297,13 +9230,6 @@ let SharedLog = (() => {
9297
9230
  throw new Error("waitForReplicatorRequestMaxAttempts must be a positive number");
9298
9231
  }
9299
9232
  this._closeController = new AbortController();
9300
- if (this.legacyReplicationInfoEnabled) {
9301
- this._announcements.setupReplicationAnnouncementRetryFunction();
9302
- this._announcements.setupReplicationAnnouncementRepairFunction();
9303
- }
9304
- else {
9305
- this._announcements.cancelCurrentReplicationStateAnnouncementRetry();
9306
- }
9307
9233
  this._closeController.signal.addEventListener("abort", () => {
9308
9234
  for (const [_peer, state] of this._replicationInfoRequestByPeer) {
9309
9235
  if (state.timer)
@@ -9658,7 +9584,6 @@ let SharedLog = (() => {
9658
9584
  peerSupportsRawExchangeHeads: (peer) => this.peerSupportsRawExchangeHeads(peer),
9659
9585
  sendRawExchangeHeads: (hashes, to, sendOptions) => this.trySendFusedRawExchangeHeads(hashes, to, sendOptions),
9660
9586
  warn,
9661
- compatibility: this._logProperties?.compatibility,
9662
9587
  resolution: this.domain.resolution,
9663
9588
  sync: options?.sync,
9664
9589
  syncronizer: options?.syncronizer,
@@ -10399,7 +10324,7 @@ let SharedLog = (() => {
10399
10324
  cleanupPeerDisconnectTracking(peerHash, ownershipLifecycleController = this.captureReplicationOwnershipLifecycle()) {
10400
10325
  const peerSession = this._peerSessions.current(peerHash);
10401
10326
  const preserveV2Session = peerSession?.phase === "open" && peerSession.isActive();
10402
- if (this.legacyReplicationInfoEnabled || !preserveV2Session) {
10327
+ if (!preserveV2Session) {
10403
10328
  this.cancelReplicationInfoRequests(peerHash);
10404
10329
  }
10405
10330
  this._liveness._replicatorLivenessFailures.delete(peerHash);
@@ -10425,11 +10350,8 @@ let SharedLog = (() => {
10425
10350
  }
10426
10351
  advanceReplicationInfoRecoveryEpoch(peerHash) {
10427
10352
  // Handlers admitted before a successful peer removal must not restore state
10428
- // when they eventually reach the apply lane. Reset the sender's
10429
- // ordering watermark with the local epoch so a later arrival can be
10430
- // accepted without comparing its clock to this receiver's clock.
10353
+ // when they eventually reach the apply lane.
10431
10354
  const receiveEpoch = this._peerSessions.advanceReceiveEpoch(peerHash);
10432
- this.latestReplicationInfoMessage.delete(peerHash);
10433
10355
  const peerSession = this._peerSessions.current(peerHash);
10434
10356
  if (peerSession?.phase === "open") {
10435
10357
  this._v2Receive.advanceRecovery({
@@ -11138,13 +11060,6 @@ let SharedLog = (() => {
11138
11060
  firstError ??= error;
11139
11061
  }
11140
11062
  };
11141
- // A borsh-deserialized instance that is closed before ever being opened
11142
- // has no coordinators (they are created in open()); the old inline code
11143
- // only touched plain fields here and never threw.
11144
- captureSync(() => this._announcements?.cancelCurrentReplicationStateAnnouncementRetry());
11145
- if (this._announcements) {
11146
- this._announcements.replicationAnnouncementRetryDebounced = undefined;
11147
- }
11148
11063
  captureSync(() => {
11149
11064
  if (this._wireSyncSession) {
11150
11065
  this._wireSyncSession.unregisterTopic(this.topic);
@@ -11227,7 +11142,6 @@ let SharedLog = (() => {
11227
11142
  captureSync(() => {
11228
11143
  this._pendingIHave?.clear();
11229
11144
  this._pendingIHaveCallbacks?.clear();
11230
- this.latestReplicationInfoMessage?.clear();
11231
11145
  this._peerSessions?.clearReceiveEpochsForClose();
11232
11146
  this._peerSessions?.clearCleanupGatesForClose();
11233
11147
  this._activeReceiveHandlersByPeer?.clear();
@@ -11317,7 +11231,6 @@ let SharedLog = (() => {
11317
11231
  await pruneRemoveTerminalFence.drained;
11318
11232
  await this.drainPendingIHaveCallbacks();
11319
11233
  this.ensureNativeDurabilityRuntimeState();
11320
- this._announcements.cancelCurrentReplicationStateAnnouncementRetry();
11321
11234
  }
11322
11235
  catch (error) {
11323
11236
  // The terminal preamble has already disabled parent attachments and the
@@ -11360,17 +11273,7 @@ let SharedLog = (() => {
11360
11273
  }
11361
11274
  }, 2_000);
11362
11275
  try {
11363
- const reset = new AllReplicatingSegmentsMessage({ segments: [] });
11364
- const resets = [this._v2Send.sendTerminalReset(abort.signal)];
11365
- if (this.legacyReplicationInfoEnabled) {
11366
- resets.push(this.rpc
11367
- .send(reset, {
11368
- priority: CONVERGENCE_MESSAGE_PRIORITY,
11369
- signal: abort.signal,
11370
- })
11371
- .catch(() => { }));
11372
- }
11373
- await Promise.all(resets);
11276
+ await this._v2Send.sendTerminalReset(abort.signal);
11374
11277
  }
11375
11278
  finally {
11376
11279
  clearTimeout(abortTimer);
@@ -11462,7 +11365,6 @@ let SharedLog = (() => {
11462
11365
  await replicationRangeTerminalFence.drained;
11463
11366
  await pruneRemoveTerminalFence.drained;
11464
11367
  await this.drainPendingIHaveCallbacks();
11465
- this._announcements.cancelCurrentReplicationStateAnnouncementRetry();
11466
11368
  }
11467
11369
  catch (error) {
11468
11370
  // The terminal preamble is not safely reversible. Preserve the fence until
@@ -11489,17 +11391,7 @@ let SharedLog = (() => {
11489
11391
  }
11490
11392
  }, 2_000);
11491
11393
  try {
11492
- const reset = new AllReplicatingSegmentsMessage({ segments: [] });
11493
- const resets = [this._v2Send.sendTerminalReset(abort.signal)];
11494
- if (this.legacyReplicationInfoEnabled) {
11495
- resets.push(this.rpc
11496
- .send(reset, {
11497
- priority: CONVERGENCE_MESSAGE_PRIORITY,
11498
- signal: abort.signal,
11499
- })
11500
- .catch(() => { }));
11501
- }
11502
- await Promise.all(resets);
11394
+ await this._v2Send.sendTerminalReset(abort.signal);
11503
11395
  }
11504
11396
  finally {
11505
11397
  clearTimeout(abortTimer);
@@ -11923,15 +11815,15 @@ let SharedLog = (() => {
11923
11815
  if (!context.from) {
11924
11816
  throw new Error("Missing from in update role message");
11925
11817
  }
11926
- if (!this.legacyReplicationInfoEnabled &&
11927
- (msg instanceof RequestReplicationInfoMessage ||
11928
- msg instanceof ResponseRoleMessage ||
11929
- msg instanceof AllReplicatingSegmentsMessage ||
11930
- msg instanceof AddedReplicationSegmentMessage ||
11931
- msg instanceof StoppedReplicating)) {
11932
- // These variants remain registered decode tombstones, but current logs
11933
- // fail closed before leases, synchronizer work, liveness, watermarks or
11934
- // mutations. Only an explicit pre-v10 compatibility open admits them.
11818
+ if (msg instanceof RequestReplicationInfoMessage ||
11819
+ msg instanceof ResponseRoleMessage ||
11820
+ msg instanceof AllReplicatingSegmentsMessage ||
11821
+ msg instanceof AddedReplicationSegmentMessage ||
11822
+ msg instanceof StoppedReplicating) {
11823
+ // These variants remain registered decode tombstones, but logs fail
11824
+ // closed unconditionally before leases, synchronizer work, liveness,
11825
+ // watermarks or mutations (B12: the compatibility opens that once
11826
+ // admitted them reject at open()).
11935
11827
  return;
11936
11828
  }
11937
11829
  // Snapshot receive ownership before any async handler gets a chance to
@@ -11961,29 +11853,20 @@ let SharedLog = (() => {
11961
11853
  const receiveOwnershipRevision = this._instanceLifecycle?._receiveOwnershipRevision ?? 0;
11962
11854
  const receiveOwnershipLifecycleController = this.captureReplicationOwnershipLifecycle();
11963
11855
  const receiveReplicationInfoReceiveEpoch = this._peerSessions.receiveEpoch(receiveFromHash);
11964
- if (msg instanceof ResponseRoleMessage) {
11965
- msg = msg.toReplicationInfoMessage(); // migration
11966
- }
11967
- if (msg instanceof AllReplicatingSegmentsMessage ||
11968
- msg instanceof AddedReplicationSegmentMessage ||
11969
- msg instanceof FullReplicationInfoV2Message ||
11856
+ if (msg instanceof FullReplicationInfoV2Message ||
11970
11857
  msg instanceof AddedReplicationInfoV2Message) {
11971
11858
  // Bound decoded untrusted vectors before per-peer/global mutation
11972
11859
  // queues, trusted-replicator authorization, or liveness side effects.
11973
11860
  this.validateReplicationRangeAnnouncement(msg.segments);
11974
11861
  }
11975
- else if (msg instanceof StoppedReplicating ||
11976
- msg instanceof StoppedReplicationInfoV2Message) {
11862
+ else if (msg instanceof StoppedReplicationInfoV2Message) {
11977
11863
  // Bound the raw decoded vector before deduplication can hide the
11978
11864
  // allocation cost, and before liveness or apply-queue side effects.
11979
11865
  this.validateStoppedReplicationAnnouncement(msg.segmentIds);
11980
11866
  }
11981
11867
  if (!context.from.equals(this.node.identity.publicKey) &&
11982
11868
  !(msg instanceof RequestReplicationInfoV2Message) &&
11983
- !isReplicationInfoV2Message(msg) &&
11984
- !(msg instanceof AllReplicatingSegmentsMessage) &&
11985
- !(msg instanceof AddedReplicationSegmentMessage) &&
11986
- !(msg instanceof StoppedReplicating)) {
11869
+ !isReplicationInfoV2Message(msg)) {
11987
11870
  this._liveness.markReplicatorActivity(receiveFromHash);
11988
11871
  }
11989
11872
  const syncProfile = this._logProperties?.sync?.profile;
@@ -13405,16 +13288,6 @@ let SharedLog = (() => {
13405
13288
  else if (msg instanceof ReplicationPingMessage) {
13406
13289
  // No-op: used as an ACKed unicast liveness probe.
13407
13290
  }
13408
- else if (msg instanceof RequestReplicationInfoMessage) {
13409
- await this.handleRequestReplicationInfo(msg, laneRequestContext, lane);
13410
- }
13411
- else if (msg instanceof AllReplicatingSegmentsMessage ||
13412
- msg instanceof AddedReplicationSegmentMessage) {
13413
- await this.handleReplicationInfoAnnouncement(msg, laneRequestContext, lane);
13414
- }
13415
- else if (msg instanceof StoppedReplicating) {
13416
- await this.handleStoppedReplicating(msg, laneRequestContext, lane);
13417
- }
13418
13291
  else {
13419
13292
  throw new Error("Unexpected message");
13420
13293
  }
@@ -13600,68 +13473,6 @@ let SharedLog = (() => {
13600
13473
  }
13601
13474
  }
13602
13475
  }
13603
- async handleRequestReplicationInfo(_msg, context, lane) {
13604
- const receiveFromHash = lane.fromHash;
13605
- const receiveSession = lane.session;
13606
- const receiveReplicationLifecycleController = lane.lifecycleController;
13607
- if (context.from.equals(this.node.identity.publicKey)) {
13608
- return;
13609
- }
13610
- const replicationLifecycleController = receiveReplicationLifecycleController;
13611
- if (!replicationLifecycleController ||
13612
- !this._peerSessions.isReceiveAdmissionOpen(receiveFromHash, receiveSession, replicationLifecycleController)) {
13613
- return;
13614
- }
13615
- let replicationSegments;
13616
- try {
13617
- replicationSegments = await this.getMyReplicationSegments();
13618
- }
13619
- catch (error) {
13620
- if (!this._peerSessions.isReceiveAdmissionOpen(receiveFromHash, receiveSession, replicationLifecycleController) &&
13621
- isNotStartedError(error)) {
13622
- return;
13623
- }
13624
- throw error;
13625
- }
13626
- if (!this._peerSessions.isReceiveAdmissionOpen(receiveFromHash, receiveSession, replicationLifecycleController)) {
13627
- return;
13628
- }
13629
- const segments = replicationSegments.map((x) => x.toReplicationRange());
13630
- this.validatePersistedReplicationRangeSnapshot(segments);
13631
- this._v2Send.enqueueSnapshotForPeer(receiveFromHash);
13632
- await this.rpc
13633
- .send(new AllReplicatingSegmentsMessage({ segments }), {
13634
- mode: new AcknowledgeDelivery({
13635
- to: [context.from],
13636
- redundancy: 1,
13637
- }),
13638
- signal: replicationLifecycleController.signal,
13639
- })
13640
- .catch((error) => this.handleReplicationLifecycleSendError(error, replicationLifecycleController));
13641
- if (!this._peerSessions.isReceiveAdmissionOpen(receiveFromHash, receiveSession, replicationLifecycleController)) {
13642
- return;
13643
- }
13644
- // for backwards compatibility (v8) remove this when we are sure that all nodes are v9+
13645
- if (this.v8Behaviour) {
13646
- const role = this.getRoleFromReplicationSegments(replicationSegments);
13647
- if (role instanceof Replicator) {
13648
- const fixedSettings = !this._isAdaptiveReplicating;
13649
- if (fixedSettings) {
13650
- await this.rpc
13651
- .send(new ResponseRoleMessage({
13652
- role,
13653
- }), {
13654
- mode: new SilentDelivery({
13655
- to: [context.from],
13656
- redundancy: 1,
13657
- }),
13658
- signal: replicationLifecycleController.signal,
13659
- })
13660
- .catch((error) => this.handleReplicationLifecycleSendError(error, replicationLifecycleController));
13661
- }
13662
- }
13663
- }
13664
- }
13665
13476
  async handleReplicationInfoV2Announcement(msg, context, lane) {
13666
13477
  const from = context.from;
13667
13478
  const fromHash = lane.fromHash;
@@ -13797,187 +13608,12 @@ let SharedLog = (() => {
13797
13608
  // A committed V2 announcement is applied progress: the peer answers,
13798
13609
  // so recovery re-solicitation may restart from the base interval.
13799
13610
  this.resetReplicationInfoV2RecoveryEscalation(fromHash);
13800
- if (msg instanceof FullReplicationInfoV2Message &&
13801
- this.legacyReplicationInfoEnabled) {
13802
- this.cancelReplicationInfoRequests(fromHash);
13803
- }
13804
13611
  });
13805
13612
  }
13806
13613
  finally {
13807
13614
  this._v2Receive.release(admission);
13808
13615
  }
13809
13616
  }
13810
- async handleReplicationInfoAnnouncement(msg, context, lane) {
13811
- const receiveFromHash = lane.fromHash;
13812
- const receiveSession = lane.session;
13813
- const receiveReplicationLifecycleController = lane.lifecycleController;
13814
- const receiveReplicationInfoReceiveEpoch = lane.receiveEpoch;
13815
- const peerReceiveLease = lane.lease;
13816
- if (context.from.equals(this.node.identity.publicKey)) {
13817
- return;
13818
- }
13819
- const replicationInfoMessage = msg;
13820
- // Process replication updates even if the sender isn't yet considered "ready" by
13821
- // `Program.waitFor()`. Dropping these messages can lead to missing replicator info
13822
- // (and downstream `waitForReplicator()` timeouts) under timing-sensitive joins.
13823
- const from = context.from;
13824
- const fromHash = from.hashcode();
13825
- if (this._v2Receive.isLegacyCutover(receiveSession)) {
13826
- if (receiveSession) {
13827
- this._v2Receive.noteLegacyAnnouncement({
13828
- peerHash: fromHash,
13829
- peerSession: receiveSession,
13830
- receiveEpoch: receiveReplicationInfoReceiveEpoch,
13831
- senderTransportSession: context.message.header.session,
13832
- transportTimestamp: context.message.header.timestamp,
13833
- message: msg,
13834
- });
13835
- }
13836
- return;
13837
- }
13838
- // Pre-lane gate: lifecycle -> receive-epoch -> blocked, exactly the
13839
- // legacy order. isMembershipActiveFor is the unit-pinned fold of
13840
- // isReplicationLifecycleActive; isReceiveEpochCurrent is the
13841
- // relocated `===`-with-`?? null`. The DELIBERATE absence of a
13842
- // subscription-epoch term is preserved: the lease already validated
13843
- // it, and the in-lane recheck owns post-await staleness.
13844
- if (!this._instanceLifecycle.isMembershipActiveFor(receiveReplicationLifecycleController) ||
13845
- !this._peerSessions.isReceiveEpochCurrent(receiveFromHash, receiveReplicationInfoReceiveEpoch) ||
13846
- this._peerSessions.isReplicationInfoBlocked(fromHash)) {
13847
- return;
13848
- }
13849
- const messageTimestamp = context.message.header.timestamp;
13850
- peerReceiveLease.release();
13851
- await this.withReplicationInfoApplyQueue(fromHash, async () => {
13852
- try {
13853
- // The peer may have unsubscribed after this message was queued.
13854
- // In-lane gate: lifecycle -> subscription-epoch -> receive-epoch
13855
- // -> blocked, term for term as before the session migration.
13856
- if (!this._instanceLifecycle.isMembershipActiveFor(receiveReplicationLifecycleController) ||
13857
- !this._peerSessions.isCurrent(fromHash, receiveSession) ||
13858
- !this._peerSessions.isReceiveEpochCurrent(fromHash, receiveReplicationInfoReceiveEpoch) ||
13859
- this._peerSessions.isReplicationInfoBlocked(fromHash)) {
13860
- return;
13861
- }
13862
- if (receiveSession && this._v2Receive.isLegacyCutover(receiveSession)) {
13863
- this._v2Receive.noteLegacyAnnouncement({
13864
- peerHash: fromHash,
13865
- peerSession: receiveSession,
13866
- receiveEpoch: receiveReplicationInfoReceiveEpoch,
13867
- senderTransportSession: context.message.header.session,
13868
- transportTimestamp: context.message.header.timestamp,
13869
- message: msg,
13870
- });
13871
- return;
13872
- }
13873
- // Process in-order to avoid races where repeated reset messages arrive
13874
- // concurrently and trigger spurious "added" diffs / rebalancing.
13875
- const prev = this.latestReplicationInfoMessage.get(fromHash);
13876
- if (prev && prev > messageTimestamp) {
13877
- return;
13878
- }
13879
- this.latestReplicationInfoMessage.set(fromHash, messageTimestamp);
13880
- if (this.closed) {
13881
- return;
13882
- }
13883
- const reset = msg instanceof AllReplicatingSegmentsMessage;
13884
- const result = await this.addReplicationRange(replicationInfoMessage.segments.map((x) => x.toReplicationRangeIndexable(from)), from, {
13885
- reset,
13886
- checkDuplicates: true,
13887
- timestamp: Number(messageTimestamp),
13888
- allowLegacyOrderedReplacementPairs: msg instanceof AddedReplicationSegmentMessage,
13889
- });
13890
- if (result === undefined) {
13891
- return;
13892
- }
13893
- this._liveness.markReplicatorActivity(fromHash);
13894
- // If the peer reports any replication segments, stop re-requesting.
13895
- // (Empty reports can be transient during startup.)
13896
- if (replicationInfoMessage.segments.length > 0) {
13897
- this.cancelReplicationInfoRequests(fromHash);
13898
- }
13899
- }
13900
- catch (e) {
13901
- if (isNotStartedError(e)) {
13902
- return;
13903
- }
13904
- logger.error(`Failed to apply replication settings from '${fromHash}': ${e?.message ?? e}`);
13905
- }
13906
- });
13907
- }
13908
- async handleStoppedReplicating(msg, context, lane) {
13909
- const receiveFromHash = lane.fromHash;
13910
- const receiveSession = lane.session;
13911
- const receiveReplicationLifecycleController = lane.lifecycleController;
13912
- const receiveReplicationInfoReceiveEpoch = lane.receiveEpoch;
13913
- const peerReceiveLease = lane.lease;
13914
- const from = context.from;
13915
- const segmentIds = msg.segmentIds;
13916
- if (from.equals(this.node.identity.publicKey)) {
13917
- return;
13918
- }
13919
- const fromHash = from.hashcode();
13920
- if (this._v2Receive.isLegacyCutover(receiveSession)) {
13921
- if (receiveSession) {
13922
- this._v2Receive.noteLegacyAnnouncement({
13923
- peerHash: fromHash,
13924
- peerSession: receiveSession,
13925
- receiveEpoch: receiveReplicationInfoReceiveEpoch,
13926
- senderTransportSession: context.message.header.session,
13927
- transportTimestamp: context.message.header.timestamp,
13928
- message: msg,
13929
- });
13930
- }
13931
- return;
13932
- }
13933
- // Same pre-lane gate shape as Added/All above (and the same
13934
- // intentional absence of a subscription-epoch term).
13935
- if (!this._instanceLifecycle.isMembershipActiveFor(receiveReplicationLifecycleController) ||
13936
- !this._peerSessions.isReceiveEpochCurrent(receiveFromHash, receiveReplicationInfoReceiveEpoch) ||
13937
- this._peerSessions.isReplicationInfoBlocked(fromHash)) {
13938
- return;
13939
- }
13940
- const messageTimestamp = context.message.header.timestamp;
13941
- peerReceiveLease.release();
13942
- await this.withReplicationInfoApplyQueue(fromHash, async () => {
13943
- if (!this._instanceLifecycle.isMembershipActiveFor(receiveReplicationLifecycleController) ||
13944
- !this._peerSessions.isCurrent(fromHash, receiveSession) ||
13945
- !this._peerSessions.isReceiveEpochCurrent(fromHash, receiveReplicationInfoReceiveEpoch) ||
13946
- this._peerSessions.isReplicationInfoBlocked(fromHash)) {
13947
- return;
13948
- }
13949
- if (receiveSession && this._v2Receive.isLegacyCutover(receiveSession)) {
13950
- this._v2Receive.noteLegacyAnnouncement({
13951
- peerHash: fromHash,
13952
- peerSession: receiveSession,
13953
- receiveEpoch: receiveReplicationInfoReceiveEpoch,
13954
- senderTransportSession: context.message.header.session,
13955
- transportTimestamp: context.message.header.timestamp,
13956
- message: msg,
13957
- });
13958
- return;
13959
- }
13960
- const previousTimestamp = this.latestReplicationInfoMessage.get(fromHash);
13961
- if (previousTimestamp && previousTimestamp > messageTimestamp) {
13962
- return;
13963
- }
13964
- this.latestReplicationInfoMessage.set(fromHash, messageTimestamp);
13965
- if (this.closed) {
13966
- return;
13967
- }
13968
- const rangesToRemove = await this.resolveReplicationRangesFromIdsAndKey(segmentIds, from);
13969
- await this.removeReplicationRanges(rangesToRemove, from);
13970
- this._liveness.markReplicatorActivity(fromHash);
13971
- const timestamp = BigInt(+new Date());
13972
- for (const range of rangesToRemove) {
13973
- this.replicationChangeDebounceFn.add({
13974
- range,
13975
- type: "removed",
13976
- timestamp,
13977
- });
13978
- }
13979
- });
13980
- }
13981
13617
  async calculateTotalParticipation(options) {
13982
13618
  if (options?.sum) {
13983
13619
  const ranges = await this.replicationIndex.iterate().all();
@@ -14334,36 +13970,21 @@ let SharedLog = (() => {
14334
13970
  return;
14335
13971
  }
14336
13972
  requestAttempts++;
14337
- if (this.legacyReplicationInfoEnabled) {
14338
- this.rpc
14339
- .send(new RequestReplicationInfoMessage(), {
14340
- mode: new AcknowledgeDelivery({ redundancy: 1, to: [key] }),
14341
- })
14342
- .catch((e) => {
14343
- // Best-effort: missing peers / unopened RPC should not fail the wait logic.
14344
- if (isNotStartedError(e)) {
14345
- return;
14346
- }
14347
- logger.error(e?.toString?.() ?? String(e));
13973
+ const peerHash = key.hashcode();
13974
+ const peerSession = this._peerSessions.current(peerHash);
13975
+ if (peerSession?.phase === "open") {
13976
+ this._v2Receive.resumeParkedRequest({
13977
+ peerHash,
13978
+ peerSession,
13979
+ receiveEpoch: this._peerSessions.receiveEpoch(peerHash),
14348
13980
  });
14349
13981
  }
14350
- else {
14351
- const peerHash = key.hashcode();
14352
- const peerSession = this._peerSessions.current(peerHash);
14353
- if (peerSession?.phase === "open") {
14354
- this._v2Receive.resumeParkedRequest({
14355
- peerHash,
14356
- peerSession,
14357
- receiveEpoch: this._peerSessions.receiveEpoch(peerHash),
14358
- });
14359
- }
14360
- else if (peerSession === null || peerSession.phase === "departing") {
14361
- // A peer can be known to routing before its SharedLog topic
14362
- // subscription has been observed. Legacy requests used to bootstrap
14363
- // that case directly; V2 needs an authoritative Subscribe snapshot
14364
- // before it can create a fenced PeerSession and request a Full.
14365
- requestSubscriberSnapshot();
14366
- }
13982
+ else if (peerSession === null || peerSession.phase === "departing") {
13983
+ // A peer can be known to routing before its SharedLog topic
13984
+ // subscription has been observed. Legacy requests used to bootstrap
13985
+ // that case directly; V2 needs an authoritative Subscribe snapshot
13986
+ // before it can create a fenced PeerSession and request a Full.
13987
+ requestSubscriberSnapshot();
14367
13988
  }
14368
13989
  if (requestAttempts < maxRequestAttempts) {
14369
13990
  requestTimer = setTimeout(requestReplicationInfo, requestIntervalMs);
@@ -16185,7 +15806,7 @@ let SharedLog = (() => {
16185
15806
  */
16186
15807
  resetReplicationInfoV2RecoveryEscalation(peerHash) {
16187
15808
  const state = this._replicationInfoRequestByPeer.get(peerHash);
16188
- if (!state || state.peerSession === undefined) {
15809
+ if (!state) {
16189
15810
  return;
16190
15811
  }
16191
15812
  state.attempts = 0;
@@ -16274,65 +15895,20 @@ let SharedLog = (() => {
16274
15895
  };
16275
15896
  tick();
16276
15897
  }
15898
+ /**
15899
+ * Collapsed B12 shell: the legacy request-polling body (bounded
15900
+ * RequestReplicationInfoMessage ticks) is deleted; V2 recovery is the
15901
+ * only scheduler. Retained as a named seam rather than inlined at the
15902
+ * callers because the liveness monitor wiring and several suites
15903
+ * stub/spy it by name.
15904
+ */
16277
15905
  scheduleReplicationInfoRequests(peer, replicationLifecycleController = this._instanceLifecycle
16278
15906
  ?.membershipLifecycleController) {
16279
15907
  if (!replicationLifecycleController ||
16280
15908
  !this.isReplicationLifecycleActive(replicationLifecycleController)) {
16281
15909
  return;
16282
15910
  }
16283
- if (!this.legacyReplicationInfoEnabled) {
16284
- this.scheduleReplicationInfoV2Recovery(peer, replicationLifecycleController);
16285
- return;
16286
- }
16287
- const peerHash = peer.hashcode();
16288
- const requestStates = this._replicationInfoRequestByPeer;
16289
- if (requestStates.has(peerHash)) {
16290
- return;
16291
- }
16292
- const state = {
16293
- attempts: 0,
16294
- };
16295
- requestStates.set(peerHash, state);
16296
- const cancel = () => {
16297
- if (requestStates.get(peerHash) !== state) {
16298
- return;
16299
- }
16300
- if (state.timer) {
16301
- clearTimeout(state.timer);
16302
- }
16303
- requestStates.delete(peerHash);
16304
- };
16305
- const intervalMs = Math.max(50, this.waitForReplicatorRequestIntervalMs);
16306
- const maxAttempts = this.waitForReplicatorRequestMaxAttempts ??
16307
- Math.max(WAIT_FOR_REPLICATOR_REQUEST_MIN_ATTEMPTS, Math.ceil(this.waitForReplicatorTimeout / intervalMs));
16308
- const tick = () => {
16309
- if (!this.isReplicationLifecycleActive(replicationLifecycleController)) {
16310
- cancel();
16311
- return;
16312
- }
16313
- state.attempts++;
16314
- this.rpc
16315
- .send(new RequestReplicationInfoMessage(), {
16316
- mode: new AcknowledgeDelivery({ redundancy: 1, to: [peer] }),
16317
- signal: replicationLifecycleController.signal,
16318
- })
16319
- .catch((e) => {
16320
- // Best-effort: missing peers / unopened RPC should not fail join flows.
16321
- if (isNotStartedError(e) ||
16322
- (replicationLifecycleController.signal.aborted &&
16323
- e instanceof AbortError)) {
16324
- return;
16325
- }
16326
- logger.error(e?.toString?.() ?? String(e));
16327
- });
16328
- if (state.attempts >= maxAttempts) {
16329
- cancel();
16330
- return;
16331
- }
16332
- state.timer = setTimeout(tick, intervalMs);
16333
- state.timer.unref?.();
16334
- };
16335
- tick();
15911
+ this.scheduleReplicationInfoV2Recovery(peer, replicationLifecycleController);
16336
15912
  }
16337
15913
  async handleSubscriptionChange(publicKey, topics, subscribed, subscriptionEpoch, subscriptionTransportSession) {
16338
15914
  if (!topics.includes(this.topic)) {
@@ -16395,10 +15971,6 @@ let SharedLog = (() => {
16395
15971
  !ownsSubscriptionEpoch()) {
16396
15972
  return;
16397
15973
  }
16398
- // The timestamp watermark belongs to the previous subscription epoch.
16399
- // Sender clocks are not synchronized, so carrying a local unsubscribe
16400
- // timestamp forward could reject every valid announcement after reconnect.
16401
- this.latestReplicationInfoMessage.delete(peerHash);
16402
15974
  this._pendingReplicatorLeaveByPeer.delete(peerHash);
16403
15975
  const openingCapabilities = this._openingSyncCapabilitiesByPeer.get(peerHash);
16404
15976
  if (openingCapabilities?.epoch === expectedSubscriptionEpoch) {
@@ -16441,11 +16013,6 @@ let SharedLog = (() => {
16441
16013
  this._peerSessions.blockReplicationInfo(peerHash);
16442
16014
  const disconnectedWarmupSession = this.joinWarmup._warmupSessionsByTarget.get(peerHash) ?? null;
16443
16015
  this.joinWarmup.cancelJoinWarmupTarget(peerHash);
16444
- const now = BigInt(+new Date());
16445
- const previous = this.latestReplicationInfoMessage.get(peerHash);
16446
- if (!previous || previous < now) {
16447
- this.latestReplicationInfoMessage.set(peerHash, now);
16448
- }
16449
16016
  let removed = false;
16450
16017
  try {
16451
16018
  // Unsubscribe can race with the peer's final replication reset message.
@@ -16487,84 +16054,20 @@ let SharedLog = (() => {
16487
16054
  this.promoteReplicationInfoV2ReceiveCapability(publicKey, expectedSubscriptionEpoch);
16488
16055
  // Decode, sender and authenticated apply readiness are separate bits. An
16489
16056
  // ACKed capability advert is the local half of receiver-led negotiation;
16490
- // readiness is promoted only after the legacy startup path has completed.
16491
- // This preserves mixed-version discovery without putting capability ACK
16492
- // latency on the subscription callback's critical path.
16057
+ // readiness is promoted through the coordinator once the ACK arrives.
16058
+ // This keeps capability ACK latency off the subscription callback's
16059
+ // critical path.
16493
16060
  const receiveEpoch = this._peerSessions.receiveEpoch(peerHash);
16494
- const localCapabilityAdvertisement = this._v2Receive.advertiseLocalCapability({
16061
+ // B12: there is no legacy startup work to order ahead of RequestV2, so
16062
+ // the two-phase legacy barrier is gone — an ACKed advert promotes
16063
+ // readiness through the coordinator as soon as it lands.
16064
+ this._v2Receive.advertiseLocalCapability({
16495
16065
  target: publicKey,
16496
16066
  peerSession: expectedSubscriptionEpoch,
16497
16067
  receiveEpoch,
16498
16068
  signal: replicationLifecycleController.signal,
16499
16069
  });
16500
- if (!this.legacyReplicationInfoEnabled) {
16501
- // Current logs have no legacy startup work to order ahead of RequestV2.
16502
- // Releasing is synchronous and exact-session fenced; the ACK may still
16503
- // arrive later and promote readiness through the coordinator.
16504
- localCapabilityAdvertisement.releaseLegacyBarrier();
16505
- this.scheduleReplicationInfoV2Recovery(publicKey, replicationLifecycleController);
16506
- return;
16507
- }
16508
- try {
16509
- let replicationSegments;
16510
- try {
16511
- replicationSegments = await this.getMyReplicationSegments();
16512
- }
16513
- catch (error) {
16514
- if (!this.isReplicationLifecycleActive(replicationLifecycleController) &&
16515
- isNotStartedError(error)) {
16516
- return;
16517
- }
16518
- throw error;
16519
- }
16520
- if (!this.isReplicationLifecycleActive(replicationLifecycleController) ||
16521
- !ownsSubscriptionEpoch()) {
16522
- return;
16523
- }
16524
- if (replicationSegments.length > 0) {
16525
- const segments = replicationSegments.map((x) => x.toReplicationRange());
16526
- this.validatePersistedReplicationRangeSnapshot(segments);
16527
- await this.rpc
16528
- .send(new AllReplicatingSegmentsMessage({
16529
- segments,
16530
- }), {
16531
- mode: new AcknowledgeDelivery({
16532
- redundancy: 1,
16533
- to: [publicKey],
16534
- }),
16535
- signal: replicationLifecycleController.signal,
16536
- })
16537
- .catch((error) => this.handleReplicationLifecycleSendError(error, replicationLifecycleController));
16538
- if (!this.isReplicationLifecycleActive(replicationLifecycleController) ||
16539
- !ownsSubscriptionEpoch()) {
16540
- return;
16541
- }
16542
- if (this.v8Behaviour) {
16543
- // for backwards compatibility
16544
- await this.rpc
16545
- .send(new ResponseRoleMessage({
16546
- role: this.getRoleFromReplicationSegments(replicationSegments),
16547
- }), {
16548
- mode: new AcknowledgeDelivery({
16549
- redundancy: 1,
16550
- to: [publicKey],
16551
- }),
16552
- signal: replicationLifecycleController.signal,
16553
- })
16554
- .catch((error) => this.handleReplicationLifecycleSendError(error, replicationLifecycleController));
16555
- }
16556
- }
16557
- // Keep legacy request-based discovery independent of the capability ACK.
16558
- // This makes mixed-version joins resilient to timing-sensitive delivery/order
16559
- // issues where we may miss the remote peer's initial announcement.
16560
- if (this.isReplicationLifecycleActive(replicationLifecycleController) &&
16561
- ownsSubscriptionEpoch()) {
16562
- this.scheduleReplicationInfoRequests(publicKey, replicationLifecycleController);
16563
- }
16564
- }
16565
- finally {
16566
- localCapabilityAdvertisement.releaseLegacyBarrier();
16567
- }
16070
+ this.scheduleReplicationInfoV2Recovery(publicKey, replicationLifecycleController);
16568
16071
  }
16569
16072
  getClampedReplicas(customValue) {
16570
16073
  if (!customValue) {
@@ -17540,14 +17043,6 @@ let SharedLog = (() => {
17540
17043
  const subscriptionEpoch = this._peerSessions.rotate(fromHash, "departing");
17541
17044
  this._peerSessions.blockReplicationInfo(fromHash);
17542
17045
  this._recentRepairDispatch.delete(fromHash);
17543
- // Keep a per-peer timestamp watermark when we observe an unsubscribe. This
17544
- // prevents late/out-of-order replication-info messages from re-introducing
17545
- // stale segments for a peer that has already left the topic.
17546
- const now = BigInt(+new Date());
17547
- const prev = this.latestReplicationInfoMessage.get(fromHash);
17548
- if (!prev || prev < now) {
17549
- this.latestReplicationInfoMessage.set(fromHash, now);
17550
- }
17551
17046
  this.invalidateSharedLogTopicSubscribersCache();
17552
17047
  return this.handleSubscriptionChange(evt.detail.from, evt.detail.topics, false, subscriptionEpoch);
17553
17048
  }
@@ -17663,22 +17158,13 @@ let SharedLog = (() => {
17663
17158
  if (!canReplicate) {
17664
17159
  return false;
17665
17160
  }
17666
- try {
17667
- await this.startAnnounceReplicating([dynamicRange], {
17668
- checkDuplicates: false,
17669
- reset: false,
17670
- shouldApply: isCurrent,
17671
- }, ownershipLifecycleController);
17672
- if (!isCurrent())
17673
- return false;
17674
- }
17675
- catch (error) {
17676
- if (isTransientReplicationAnnouncementError(error) &&
17677
- this._announcements._replicationAnnouncementRetryPending) {
17678
- return false;
17679
- }
17680
- throw error;
17681
- }
17161
+ await this.startAnnounceReplicating([dynamicRange], {
17162
+ checkDuplicates: false,
17163
+ reset: false,
17164
+ shouldApply: isCurrent,
17165
+ }, ownershipLifecycleController);
17166
+ if (!isCurrent())
17167
+ return false;
17682
17168
  /* await this._updateRole(newRole, onRoleChange); */
17683
17169
  if (isCurrent()) {
17684
17170
  void rebalanceParticipationDebounced?.call();