@peerbit/shared-log 16.0.28 → 16.0.30

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
@@ -186,7 +186,7 @@ const emitAdvisorySyncProfileDuration = (profile, startedAt, event) => {
186
186
  emitSyncProfileDuration(profile, startedAt, event);
187
187
  }
188
188
  catch {
189
- // Diagnostics must not change open or provider-resolution correctness.
189
+ // Advisory diagnostics must not change replication or lifecycle behavior.
190
190
  }
191
191
  };
192
192
  const canUseOptionalNativeModuleImports = () => {
@@ -1964,7 +1964,7 @@ let SharedLog = (() => {
1964
1964
  bumpSimpleFallbackPasses: () => {
1965
1965
  this._repairMetrics["join-warmup"].simpleFallbackPasses += 1;
1966
1966
  },
1967
- sendEntriesSimple: (target, entries, options) => this.sendRepairEntriesWithTransport(target, entries, "simple", options),
1967
+ sendEntriesSimple: (target, entries, options) => this.sendRepairEntriesWithTransport(target, entries, "simple", options, "join-warmup"),
1968
1968
  logError: (error) => logger.error(error),
1969
1969
  });
1970
1970
  }
@@ -3836,6 +3836,23 @@ let SharedLog = (() => {
3836
3836
  current.peerSession === captured.peerSession);
3837
3837
  };
3838
3838
  try {
3839
+ if (!isPeerRoundCurrent())
3840
+ return;
3841
+ if (!this._v2Send.hasCurrentStateForPeer({
3842
+ peerHash: peer,
3843
+ peerSession: captured.peerSession,
3844
+ receiverTransportSession: captured.capabilitySession,
3845
+ })) {
3846
+ // Preflight can precede replacement or loss of the sender
3847
+ // stream. Delivery must recover its own exact current
3848
+ // binding before waiting for application confirmation.
3849
+ this._v2Receive.reAdvertiseLocalCapabilityForRemoteFull({
3850
+ peerHash: peer,
3851
+ peerSession: captured.peerSession,
3852
+ receiveEpoch: captured.receiveEpoch,
3853
+ signal: roundSignal,
3854
+ });
3855
+ }
3839
3856
  await this._v2Send.confirmLatestForPeer({
3840
3857
  peerHash: peer,
3841
3858
  peerSession: captured.peerSession,
@@ -6840,47 +6857,88 @@ let SharedLog = (() => {
6840
6857
  signal,
6841
6858
  });
6842
6859
  }
6843
- async sendRepairEntriesWithTransport(target, entries, transport, options) {
6860
+ async sendRepairEntriesWithTransport(target, entries, transport, options, mode) {
6844
6861
  const isStillCurrent = options?.isStillCurrent ?? (() => true);
6845
6862
  if (!isStillCurrent()) {
6846
6863
  return;
6847
6864
  }
6848
6865
  const unknownEntries = new Map();
6849
6866
  const knownHashes = [];
6850
- for (const [hash, entry] of entries) {
6851
- if ((options?.bypassRecentKnownPeers ||
6852
- !this.isEntryRecentlyKnownByPeer(hash, target, RECENT_KNOWN_REPAIR_SUPPRESSION_MS)) &&
6853
- (options?.bypassKnownPeers || !this.isEntryKnownByPeer(hash, target))) {
6854
- unknownEntries.set(hash, entry);
6867
+ const profile = this._logProperties?.sync?.profile;
6868
+ const startedAt = syncProfileStart(profile);
6869
+ const inputEntries = profile ? entries.size : 0;
6870
+ let selectedEntries = 0;
6871
+ let lastObservedCurrent = true;
6872
+ let outcome = "stale";
6873
+ try {
6874
+ for (const [hash, entry] of entries) {
6875
+ if ((options?.bypassRecentKnownPeers ||
6876
+ !this.isEntryRecentlyKnownByPeer(hash, target, RECENT_KNOWN_REPAIR_SUPPRESSION_MS)) &&
6877
+ (options?.bypassKnownPeers || !this.isEntryKnownByPeer(hash, target))) {
6878
+ unknownEntries.set(hash, entry);
6879
+ }
6880
+ else {
6881
+ knownHashes.push(hash);
6882
+ }
6883
+ }
6884
+ // A custom synchronizer may mutate the Map once it receives it.
6885
+ if (profile)
6886
+ selectedEntries = unknownEntries.size;
6887
+ if (!isStillCurrent())
6888
+ return;
6889
+ this.clearRepairFrontierHashes(target, knownHashes);
6890
+ if (unknownEntries.size === 0) {
6891
+ outcome = "known-suppressed";
6892
+ return;
6893
+ }
6894
+ if (transport === "simple") {
6895
+ // Observe only checks the lower path already makes, without adding
6896
+ // lifecycle decisions or wrapping the disabled-profiling path.
6897
+ const dispatchIsStillCurrent = profile
6898
+ ? () => (lastObservedCurrent = isStillCurrent())
6899
+ : isStillCurrent;
6900
+ // Fallback repair does not wait for the maybe-sync round trip.
6901
+ await this.pushRepairEntries(target, unknownEntries, dispatchIsStillCurrent, options?.signal);
6855
6902
  }
6856
6903
  else {
6857
- knownHashes.push(hash);
6904
+ const syncEntries = this._logProperties?.sync?.priority
6905
+ ? this._coordinates.materializeRepairDispatchEntries(unknownEntries)
6906
+ : unknownEntries;
6907
+ if (!isStillCurrent())
6908
+ return;
6909
+ await this.syncronizer.onMaybeMissingEntries({
6910
+ entries: syncEntries,
6911
+ targets: [target],
6912
+ signal: options?.signal,
6913
+ });
6858
6914
  }
6915
+ outcome = !lastObservedCurrent
6916
+ ? "stale"
6917
+ : options?.signal?.aborted
6918
+ ? "cancelled"
6919
+ : "dispatched";
6859
6920
  }
6860
- if (!isStillCurrent()) {
6861
- return;
6862
- }
6863
- this.clearRepairFrontierHashes(target, knownHashes);
6864
- if (unknownEntries.size === 0) {
6865
- return;
6866
- }
6867
- if (transport === "simple") {
6868
- // Fallback repair should not depend on the target completing the
6869
- // RequestMaybeSync -> ResponseMaybeSync round trip.
6870
- await this.pushRepairEntries(target, unknownEntries, isStillCurrent, options?.signal);
6871
- return;
6921
+ catch (error) {
6922
+ outcome = "error";
6923
+ throw error;
6872
6924
  }
6873
- const syncEntries = this._logProperties?.sync?.priority
6874
- ? this._coordinates.materializeRepairDispatchEntries(unknownEntries)
6875
- : unknownEntries;
6876
- if (!isStillCurrent()) {
6877
- return;
6925
+ finally {
6926
+ if (profile) {
6927
+ emitAdvisorySyncProfileDuration(profile, startedAt, {
6928
+ name: "sharedLog.repair.dispatch",
6929
+ component: "shared-log",
6930
+ entries: inputEntries,
6931
+ count: selectedEntries,
6932
+ targets: 1,
6933
+ details: {
6934
+ mode,
6935
+ transport,
6936
+ outcome,
6937
+ knownSuppressedEntries: knownHashes.length,
6938
+ },
6939
+ });
6940
+ }
6878
6941
  }
6879
- await this.syncronizer.onMaybeMissingEntries({
6880
- entries: syncEntries,
6881
- targets: [target],
6882
- signal: options?.signal,
6883
- });
6884
6942
  }
6885
6943
  async sendMaybeMissingEntriesNow(target, entries, options, repairLifecycleController = this._instanceLifecycle
6886
6944
  ?.ownershipLifecycleController) {
@@ -6938,7 +6996,7 @@ let SharedLog = (() => {
6938
6996
  bypassRecentKnownPeers: bypassKnownPeerHints,
6939
6997
  isStillCurrent: () => this.isRepairLifecycleActive(repairLifecycleController),
6940
6998
  signal: repairLifecycleController.signal,
6941
- })).catch((error) => logger.error(error));
6999
+ }, options.mode)).catch((error) => logger.error(error));
6942
7000
  }
6943
7001
  ensureRepairFrontierRunner(mode, target, retryScheduleMs, repairLifecycleController = this._instanceLifecycle
6944
7002
  ?.ownershipLifecycleController) {
@@ -7198,7 +7256,7 @@ let SharedLog = (() => {
7198
7256
  bypassRecentKnownPeers: bypassKnownPeerHints,
7199
7257
  isStillCurrent: () => this.isRepairLifecycleActive(repairLifecycleController),
7200
7258
  signal: repairLifecycleController.signal,
7201
- })).catch((error) => logger.error(error));
7259
+ }, options.mode)).catch((error) => logger.error(error));
7202
7260
  };
7203
7261
  const delayedJoinWarmupRetries = [];
7204
7262
  retrySchedule.forEach((delayMs, index) => {
@@ -7303,6 +7361,18 @@ let SharedLog = (() => {
7303
7361
  }
7304
7362
  async runRepairSweep(repairLifecycleController = this._instanceLifecycle
7305
7363
  ?.ownershipLifecycleController) {
7364
+ const profile = this._logProperties?.sync?.profile;
7365
+ const startedAt = syncProfileStart(profile);
7366
+ const profileCounts = profile
7367
+ ? {
7368
+ passes: 0,
7369
+ inputEntries: 0,
7370
+ nativePasses: 0,
7371
+ repairCandidates: 0,
7372
+ repairBatches: 0,
7373
+ outcome: "stale",
7374
+ }
7375
+ : undefined;
7306
7376
  try {
7307
7377
  while (this.isRepairLifecycleActive(repairLifecycleController)) {
7308
7378
  if (!this.isRepairLifecycleActive(repairLifecycleController)) {
@@ -7334,8 +7404,12 @@ let SharedLog = (() => {
7334
7404
  };
7335
7405
  pruneStaleJoinWarmupPeers();
7336
7406
  if (pendingModes.size === 0) {
7407
+ if (profileCounts)
7408
+ profileCounts.outcome = "completed";
7337
7409
  return;
7338
7410
  }
7411
+ if (profileCounts)
7412
+ profileCounts.passes += 1;
7339
7413
  const optimisticGidPeersByMode = new Map();
7340
7414
  const optimisticGidPeersConsumedByMode = new Map();
7341
7415
  for (const mode of pendingModes) {
@@ -7406,6 +7480,10 @@ let SharedLog = (() => {
7406
7480
  }
7407
7481
  return;
7408
7482
  }
7483
+ if (profileCounts) {
7484
+ profileCounts.repairCandidates += entries.size;
7485
+ profileCounts.repairBatches += 1;
7486
+ }
7409
7487
  this.dispatchMaybeMissingEntries(target, entries, {
7410
7488
  bypassRecentDedupe: true,
7411
7489
  bypassKnownPeerHints: mode === "churn" ||
@@ -7456,6 +7534,10 @@ let SharedLog = (() => {
7456
7534
  if ((this._nativeBackbone ?? this._nativeSharedLogState) &&
7457
7535
  residentEntriesByHash &&
7458
7536
  !this.hasCustomFindLeaders()) {
7537
+ if (profileCounts) {
7538
+ profileCounts.nativePasses += 1;
7539
+ profileCounts.inputEntries += residentEntriesByHash.size;
7540
+ }
7459
7541
  const repairDispatchPlan = pruneStaleJoinWarmupPeers()
7460
7542
  ? await this.planResidentRepairDispatchBatch({
7461
7543
  pendingModes,
@@ -7488,6 +7570,8 @@ let SharedLog = (() => {
7488
7570
  !iterator.done() &&
7489
7571
  pruneStaleJoinWarmupPeers()) {
7490
7572
  const entries = await iterator.next(REPAIR_SWEEP_ENTRY_BATCH_SIZE);
7573
+ if (profileCounts)
7574
+ profileCounts.inputEntries += entries.length;
7491
7575
  if (!this.isRepairLifecycleActive(repairLifecycleController)) {
7492
7576
  return;
7493
7577
  }
@@ -7602,6 +7686,8 @@ let SharedLog = (() => {
7602
7686
  }
7603
7687
  }
7604
7688
  catch (error) {
7689
+ if (profileCounts)
7690
+ profileCounts.outcome = "error";
7605
7691
  if (this.isRepairLifecycleActive(repairLifecycleController) &&
7606
7692
  !isNotStartedError(error)) {
7607
7693
  logger.error(`Repair sweep failed: ${error?.message ?? error}`);
@@ -7618,6 +7704,21 @@ let SharedLog = (() => {
7618
7704
  void this.runRepairSweep(repairLifecycleController);
7619
7705
  }
7620
7706
  }
7707
+ if (profileCounts) {
7708
+ emitAdvisorySyncProfileDuration(profile, startedAt, {
7709
+ name: "sharedLog.placement.pass",
7710
+ component: "shared-log",
7711
+ entries: profileCounts.inputEntries,
7712
+ count: profileCounts.repairCandidates,
7713
+ details: {
7714
+ phase: "repair-sweep",
7715
+ outcome: profileCounts.outcome,
7716
+ passes: profileCounts.passes,
7717
+ nativePasses: profileCounts.nativePasses,
7718
+ repairBatches: profileCounts.repairBatches,
7719
+ },
7720
+ });
7721
+ }
7621
7722
  }
7622
7723
  }
7623
7724
  async pruneDebouncedFnAddIfNotKeeping(args, ownershipLifecycleController = this.captureReplicationOwnershipLifecycle(), additionalCurrentCheck) {
@@ -14374,10 +14475,11 @@ let SharedLog = (() => {
14374
14475
  const rawExistingStartedAt = syncProfileStart(syncProfile);
14375
14476
  const rawExistingHashes = await this.log.hasMany(msg.heads.map((head) => head.hash));
14376
14477
  if (syncProfile) {
14377
- emitSyncProfileDuration(syncProfile, rawExistingStartedAt, {
14478
+ emitAdvisorySyncProfileDuration(syncProfile, rawExistingStartedAt, {
14378
14479
  name: "sharedLog.rawReceive.existingHeads",
14379
14480
  component: "shared-log",
14380
14481
  entries: msg.heads.length,
14482
+ count: rawExistingHashes.size,
14381
14483
  messages: 1,
14382
14484
  });
14383
14485
  }
@@ -14694,10 +14796,11 @@ let SharedLog = (() => {
14694
14796
  ? undefined
14695
14797
  : await this.log.hasMany(headHashes);
14696
14798
  if (syncProfile) {
14697
- emitSyncProfileDuration(syncProfile, existingStartedAt, {
14799
+ emitAdvisorySyncProfileDuration(syncProfile, existingStartedAt, {
14698
14800
  name: "sharedLog.receive.existingHeads",
14699
14801
  component: "shared-log",
14700
14802
  entries: heads.length,
14803
+ count: existingHashes?.size,
14701
14804
  messages: 1,
14702
14805
  details: { rawMaterializedKnownMissing },
14703
14806
  });
@@ -17264,6 +17367,23 @@ let SharedLog = (() => {
17264
17367
  if (!continueWait())
17265
17368
  return;
17266
17369
  const recoveryTarget = confirmationRecoveryTarget;
17370
+ if (confirmationController && recoveryTarget) {
17371
+ if (this._peerSessions.current(peerHash) !==
17372
+ recoveryTarget.peerSession ||
17373
+ this._peerSessions.receiveEpoch(peerHash) !==
17374
+ recoveryTarget.receiveEpoch ||
17375
+ this._peerSyncCapabilitySessions.get(peerHash) !==
17376
+ recoveryTarget.capabilitySession ||
17377
+ !this.uniqueReplicators.has(peerHash)) {
17378
+ // Events can be coalesced while application confirmation waits.
17379
+ // Release the obsolete target on the recovery tick as well,
17380
+ // including loss of replicator eligibility within one session.
17381
+ // A temporary receive reservation keeps this same target alive;
17382
+ // the final readiness inspection still checks every admission gate.
17383
+ confirmationRecoveryTarget = undefined;
17384
+ confirmationController.abort(new AbortError("Persisted-receipt readiness target changed"));
17385
+ }
17386
+ }
17267
17387
  this.nudgePersistedReceiptPeerReadiness(key, operationSignal, recoveryTarget && Date.now() >= recoveryTarget.notBefore
17268
17388
  ? recoveryTarget
17269
17389
  : undefined);
@@ -20395,6 +20515,17 @@ let SharedLog = (() => {
20395
20515
  warmupSessions.get(target);
20396
20516
  const areJoinWarmupGenerationsCurrent = () => isOwnershipLifecycleCurrent() &&
20397
20517
  [...warmupPeers].every(isCurrentJoinWarmupTarget);
20518
+ const profile = this._logProperties?.sync?.profile;
20519
+ const profileStartedAt = syncProfileStart(profile);
20520
+ const profileCounts = profile
20521
+ ? {
20522
+ examinedEntries: 0,
20523
+ repairCandidates: 0,
20524
+ repairBatches: 0,
20525
+ pruneScan: false,
20526
+ outcome: "stale",
20527
+ }
20528
+ : undefined;
20398
20529
  try {
20399
20530
  const uncheckedDeliver = new Map();
20400
20531
  const flushUncheckedDeliverTarget = (target) => {
@@ -20415,6 +20546,10 @@ let SharedLog = (() => {
20415
20546
  : isWarmupTarget
20416
20547
  ? "join-warmup"
20417
20548
  : "join-authoritative";
20549
+ if (profileCounts) {
20550
+ profileCounts.repairCandidates += entries.size;
20551
+ profileCounts.repairBatches += 1;
20552
+ }
20418
20553
  this.dispatchMaybeMissingEntries(target, entries, {
20419
20554
  bypassRecentDedupe: isWarmupTarget || forceFreshDelivery,
20420
20555
  bypassKnownPeerHints: forceFreshDelivery ||
@@ -20453,6 +20588,8 @@ let SharedLog = (() => {
20453
20588
  for await (const entryReplicated of toRebalance(immediateRebalanceChanges, this.entryCoordinatesIndex, this.recentlyRebalanced, {
20454
20589
  forceFresh: forceFreshDelivery || useJoinWarmupFastPath,
20455
20590
  })) {
20591
+ if (profileCounts)
20592
+ profileCounts.examinedEntries += 1;
20456
20593
  if (!isOwnershipLifecycleCurrent() ||
20457
20594
  (useJoinWarmupFastPath && !areJoinWarmupGenerationsCurrent())) {
20458
20595
  break;
@@ -20649,6 +20786,8 @@ let SharedLog = (() => {
20649
20786
  change.type === "removed" ||
20650
20787
  change.type === "replaced"));
20651
20788
  if (shouldRunLocalPruneScan) {
20789
+ if (profileCounts)
20790
+ profileCounts.pruneScan = true;
20652
20791
  throwIfOwnershipLifecycleInactive();
20653
20792
  // Adaptive range changes and fixed zero-width updates can make already-indexed
20654
20793
  // local heads prunable even when the incremental rebalance scan misses them
@@ -20667,6 +20806,8 @@ let SharedLog = (() => {
20667
20806
  return false;
20668
20807
  }
20669
20808
  }
20809
+ if (profileCounts)
20810
+ profileCounts.outcome = "completed";
20670
20811
  return changed;
20671
20812
  }
20672
20813
  catch (error) {
@@ -20676,9 +20817,30 @@ let SharedLog = (() => {
20676
20817
  if (isNotStartedError(error)) {
20677
20818
  return false; // we are not started yet, so no changes
20678
20819
  }
20820
+ if (profileCounts)
20821
+ profileCounts.outcome = "error";
20679
20822
  logger.error(error.toString());
20680
20823
  throw error;
20681
20824
  }
20825
+ finally {
20826
+ if (profileCounts) {
20827
+ emitAdvisorySyncProfileDuration(profile, profileStartedAt, {
20828
+ name: "sharedLog.placement.pass",
20829
+ component: "shared-log",
20830
+ entries: profileCounts.examinedEntries,
20831
+ count: profileCounts.repairCandidates,
20832
+ details: {
20833
+ phase: "range-change",
20834
+ outcome: profileCounts.outcome,
20835
+ changes: changes.length,
20836
+ repairBatches: profileCounts.repairBatches,
20837
+ pruneScan: profileCounts.pruneScan,
20838
+ forceFreshDelivery,
20839
+ joinWarmupFastPath: useJoinWarmupFastPath,
20840
+ },
20841
+ });
20842
+ }
20843
+ }
20682
20844
  }
20683
20845
  async _onUnsubscription(evt) {
20684
20846
  logger.trace(`Peer disconnected '${evt.detail.from.hashcode()}' from '${JSON.stringify(evt.detail.topics.map((x) => x))} '`);
@@ -20707,6 +20869,13 @@ let SharedLog = (() => {
20707
20869
  await this.handleSubscriptionChange(evt.detail.from, evt.detail.topics, true, subscriptionEpoch, evt.detail.session);
20708
20870
  }
20709
20871
  async rebalanceParticipation(ownershipLifecycleController = this.captureReplicationOwnershipLifecycle(), rebalanceParticipationDebounced = this.rebalanceParticipationDebounced) {
20872
+ const profile = this._isAdaptiveReplicating
20873
+ ? this._logProperties?.sync?.profile
20874
+ : undefined;
20875
+ const profileStartedAt = syncProfileStart(profile);
20876
+ const profileDetails = profile
20877
+ ? { outcome: "stale", idleRemainingMs: 0 }
20878
+ : undefined;
20710
20879
  // Stage 3: the lifecycle owns all three identity terms. `lifecycle` may
20711
20880
  // go stale later; its deps late-bind to the host, so the debouncer term
20712
20881
  // still reads the current host field, and the role term can disagree
@@ -20738,6 +20907,11 @@ let SharedLog = (() => {
20738
20907
  }
20739
20908
  if (this._isAdaptiveReplicating) {
20740
20909
  if (this.shouldDelayAdaptiveRebalance()) {
20910
+ if (profileDetails) {
20911
+ profileDetails.outcome = "idle-deferred";
20912
+ profileDetails.idleRemainingMs = Math.max(0, this.adaptiveRebalanceIdleMs -
20913
+ (Date.now() - this._lastLocalAppendAt));
20914
+ }
20741
20915
  if (isCurrent()) {
20742
20916
  void rebalanceParticipationDebounced?.call();
20743
20917
  }
@@ -20747,11 +20921,18 @@ let SharedLog = (() => {
20747
20921
  const usedMemory = await this.getMemoryUsage();
20748
20922
  if (!isCurrent())
20749
20923
  return false;
20924
+ if (profileDetails) {
20925
+ profileDetails.storageUsedBytes = usedMemory;
20926
+ profileDetails.storageObjectiveBytes =
20927
+ this.replicationController.maxMemoryLimit;
20928
+ }
20750
20929
  this.scheduleReplicationStatusRefreshForStorage(usedMemory);
20751
20930
  let dynamicRange = await this.getDynamicRange();
20752
20931
  if (!isCurrent())
20753
20932
  return false;
20754
20933
  if (!dynamicRange) {
20934
+ if (profileDetails)
20935
+ profileDetails.outcome = "not-permitted";
20755
20936
  return; // not allowed to replicate
20756
20937
  }
20757
20938
  if (this.replicationController.maxMemoryLimit != null &&
@@ -20771,13 +20952,24 @@ let SharedLog = (() => {
20771
20952
  const totalParticipation = await this.calculateTotalParticipation();
20772
20953
  if (!isCurrent())
20773
20954
  return false;
20955
+ const cpuUsage = this.cpuUsage?.value();
20956
+ const stepStartedAt = syncProfileStart(profile);
20774
20957
  const newFactor = this.replicationController.step({
20775
20958
  memoryUsage: usedMemory,
20776
20959
  currentFactor: dynamicRange.widthNormalized,
20777
20960
  totalFactor: totalParticipation, // TODO use this._totalParticipation when flakiness is fixed
20778
20961
  peerCount: peersSize,
20779
- cpuUsage: this.cpuUsage?.value(),
20962
+ cpuUsage,
20780
20963
  });
20964
+ if (profileDetails) {
20965
+ profileDetails.preStepMs = stepStartedAt - profileStartedAt;
20966
+ profileDetails.stepMs = syncProfileStart(profile) - stepStartedAt;
20967
+ profileDetails.currentFactor = dynamicRange.widthNormalized;
20968
+ profileDetails.proposedFactor = newFactor;
20969
+ profileDetails.totalFactor = totalParticipation;
20970
+ profileDetails.controllerPeerCount = peersSize;
20971
+ profileDetails.cpuUsage = cpuUsage;
20972
+ }
20781
20973
  const absoluteDifference = Math.abs(dynamicRange.widthNormalized - newFactor);
20782
20974
  const relativeDifference = absoluteDifference /
20783
20975
  Math.max(dynamicRange.widthNormalized, RECALCULATE_PARTICIPATION_RELATIVE_DENOMINATOR_FLOOR);
@@ -20805,8 +20997,11 @@ let SharedLog = (() => {
20805
20997
  if (!isCurrent())
20806
20998
  return false;
20807
20999
  if (!canReplicate) {
21000
+ if (profileDetails)
21001
+ profileDetails.outcome = "not-permitted";
20808
21002
  return false;
20809
21003
  }
21004
+ const applyStartedAt = syncProfileStart(profile);
20810
21005
  await this.startAnnounceReplicating([dynamicRange], {
20811
21006
  checkDuplicates: false,
20812
21007
  reset: false,
@@ -20814,6 +21009,10 @@ let SharedLog = (() => {
20814
21009
  }, ownershipLifecycleController);
20815
21010
  if (!isCurrent())
20816
21011
  return false;
21012
+ if (profileDetails) {
21013
+ profileDetails.outcome = "apply-settled";
21014
+ profileDetails.applyMs = syncProfileStart(profile) - applyStartedAt;
21015
+ }
20817
21016
  /* await this._updateRole(newRole, onRoleChange); */
20818
21017
  if (isCurrent()) {
20819
21018
  void rebalanceParticipationDebounced?.call();
@@ -20821,6 +21020,8 @@ let SharedLog = (() => {
20821
21020
  return true;
20822
21021
  }
20823
21022
  else {
21023
+ if (profileDetails)
21024
+ profileDetails.outcome = "unchanged";
20824
21025
  if (isCurrent()) {
20825
21026
  void rebalanceParticipationDebounced?.call();
20826
21027
  }
@@ -20829,13 +21030,27 @@ let SharedLog = (() => {
20829
21030
  }
20830
21031
  return false;
20831
21032
  };
20832
- const resp = await fn().catch((error) => {
20833
- if (isNotStartedError(error) || isClosedStoreRace(error)) {
20834
- return false;
21033
+ try {
21034
+ return await fn().catch((error) => {
21035
+ if (isNotStartedError(error) || isClosedStoreRace(error)) {
21036
+ if (profileDetails)
21037
+ profileDetails.outcome = "stale";
21038
+ return false;
21039
+ }
21040
+ if (profileDetails)
21041
+ profileDetails.outcome = "error";
21042
+ throw error;
21043
+ });
21044
+ }
21045
+ finally {
21046
+ if (profileDetails) {
21047
+ emitAdvisorySyncProfileDuration(profile, profileStartedAt, {
21048
+ name: "sharedLog.adaptive.rebalance",
21049
+ component: "shared-log",
21050
+ details: profileDetails,
21051
+ });
20835
21052
  }
20836
- throw error;
20837
- });
20838
- return resp;
21053
+ }
20839
21054
  }
20840
21055
  getDynamicRangeOffset() {
20841
21056
  const options = this._logProperties