@peerbit/shared-log 16.0.25 → 16.0.27

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.
@@ -55,6 +55,7 @@ type ApplicationConfirmationTarget = {
55
55
  type ApplicationConfirmationWaiter = {
56
56
  revision: bigint;
57
57
  minPeers: number;
58
+ startedAt: number;
58
59
  target?: ApplicationConfirmationTarget;
59
60
  resolve: () => void;
60
61
  reject: (error: unknown) => void;
@@ -63,6 +64,37 @@ type ApplicationConfirmationWaiter = {
63
64
  onAbort?: () => void;
64
65
  };
65
66
 
67
+ export type ReplicationInfoV2SendDiagnostic = Readonly<{
68
+ state: "absent" | "current" | "stale";
69
+ /** Current-state classification; no historical packet/drop log is retained. */
70
+ reason:
71
+ | "state-absent"
72
+ | "state-retiring"
73
+ | "peer-session-mismatch"
74
+ | "transport-session-mismatch"
75
+ | "destination-not-current"
76
+ | "destination-not-open"
77
+ | "ownership-inactive"
78
+ | "retry-backoff"
79
+ | "send-in-flight"
80
+ | "send-pending"
81
+ | "confirmation-in-flight"
82
+ | "latest-applied"
83
+ | "latest-unconfirmed";
84
+ established?: boolean;
85
+ suspended?: boolean;
86
+ currentRevision: string;
87
+ pendingRevision?: string;
88
+ inFlightRevision?: string;
89
+ inFlightSequence?: string;
90
+ appliedRevision?: string;
91
+ confirmationRevision?: string;
92
+ confirmationSequence?: string;
93
+ retryAttempts?: number;
94
+ confirmationWaiters: number;
95
+ oldestConfirmationAgeMs?: number;
96
+ }>;
97
+
66
98
  export type ReplicationInfoV2ConfirmationOptions = {
67
99
  /** Distinct current peers that must report durable application. Defaults to 1. */
68
100
  minPeers?: number;
@@ -460,6 +492,7 @@ export class ReplicationInfoV2SendCoordinator<R extends "u32" | "u64"> {
460
492
  const waiter: ApplicationConfirmationWaiter = {
461
493
  revision: this._revision,
462
494
  minPeers: properties.minPeers,
495
+ startedAt: Date.now(),
463
496
  target: properties.target ? { ...properties.target } : undefined,
464
497
  resolve,
465
498
  reject,
@@ -488,6 +521,137 @@ export class ReplicationInfoV2SendCoordinator<R extends "u32" | "u64"> {
488
521
  });
489
522
  }
490
523
 
524
+ /**
525
+ * Bounded, read-only state for persisted-readiness troubleshooting. Opaque
526
+ * session/challenge values and peer maps are intentionally excluded.
527
+ */
528
+ diagnosePeer(target: {
529
+ peerHash: string;
530
+ peerSession?: object;
531
+ receiverTransportSession?: bigint;
532
+ }): ReplicationInfoV2SendDiagnostic {
533
+ const state = this._sendStates.get(target.peerHash);
534
+ let matchingConfirmationCount = 0;
535
+ let oldestStartedAt: number | undefined;
536
+ for (const waiter of this._confirmations) {
537
+ if (
538
+ waiter.target?.peerHash !== target.peerHash ||
539
+ (target.peerSession !== undefined &&
540
+ waiter.target.peerSession !== target.peerSession) ||
541
+ (target.receiverTransportSession !== undefined &&
542
+ waiter.target.receiverTransportSession !==
543
+ target.receiverTransportSession)
544
+ ) {
545
+ continue;
546
+ }
547
+ matchingConfirmationCount++;
548
+ oldestStartedAt =
549
+ oldestStartedAt === undefined
550
+ ? waiter.startedAt
551
+ : Math.min(oldestStartedAt, waiter.startedAt);
552
+ }
553
+ const common = {
554
+ currentRevision: this._revision.toString(),
555
+ confirmationWaiters: Math.min(
556
+ this.maxConfirmationWaiters,
557
+ matchingConfirmationCount,
558
+ ),
559
+ ...(oldestStartedAt === undefined
560
+ ? {}
561
+ : {
562
+ oldestConfirmationAgeMs: Math.min(
563
+ MAX_TIMER_MS,
564
+ Math.max(0, Date.now() - oldestStartedAt),
565
+ ),
566
+ }),
567
+ };
568
+ if (!state) {
569
+ return Object.freeze({
570
+ state: this._retiringWorkersByPeer.has(target.peerHash)
571
+ ? ("stale" as const)
572
+ : ("absent" as const),
573
+ reason: this._retiringWorkersByPeer.has(target.peerHash)
574
+ ? ("state-retiring" as const)
575
+ : ("state-absent" as const),
576
+ ...common,
577
+ });
578
+ }
579
+
580
+ let status: "current" | "stale" = "current";
581
+ let reason: ReplicationInfoV2SendDiagnostic["reason"];
582
+ if (
583
+ target.peerSession !== undefined &&
584
+ state.peerSession !== target.peerSession
585
+ ) {
586
+ status = "stale";
587
+ reason = "peer-session-mismatch";
588
+ } else if (
589
+ target.receiverTransportSession !== undefined &&
590
+ state.receiverTransportSession !== target.receiverTransportSession
591
+ ) {
592
+ status = "stale";
593
+ reason = "transport-session-mismatch";
594
+ } else if (!this.isDestinationCurrent(state)) {
595
+ status = "stale";
596
+ reason = "destination-not-current";
597
+ } else if (!this.isDestinationReady(state)) {
598
+ reason = "destination-not-open";
599
+ } else if (
600
+ !this.deps.isReplicationOwnershipLifecycleActive(
601
+ state.ownershipLifecycleController,
602
+ )
603
+ ) {
604
+ reason = "ownership-inactive";
605
+ } else if (state.suspended || state.retryTimer !== undefined) {
606
+ reason = "retry-backoff";
607
+ } else if (state.applicationConfirmationRequest !== undefined) {
608
+ reason = "confirmation-in-flight";
609
+ } else if (state.inFlightRevision !== undefined) {
610
+ reason = "send-in-flight";
611
+ } else if (state.pending !== undefined) {
612
+ reason = "send-pending";
613
+ } else if (
614
+ state.appliedRevision !== undefined &&
615
+ state.appliedRevision >= this._revision
616
+ ) {
617
+ reason = "latest-applied";
618
+ } else {
619
+ reason = "latest-unconfirmed";
620
+ }
621
+
622
+ return Object.freeze({
623
+ state: status,
624
+ reason,
625
+ established: state.established,
626
+ suspended: state.suspended,
627
+ ...common,
628
+ ...(state.pending === undefined
629
+ ? {}
630
+ : { pendingRevision: state.pending.revision.toString() }),
631
+ ...(state.inFlightRevision === undefined
632
+ ? {}
633
+ : { inFlightRevision: state.inFlightRevision.toString() }),
634
+ ...(state.inFlightSequence === undefined
635
+ ? {}
636
+ : { inFlightSequence: state.inFlightSequence.toString() }),
637
+ ...(state.appliedRevision === undefined
638
+ ? {}
639
+ : { appliedRevision: state.appliedRevision.toString() }),
640
+ ...(state.applicationConfirmationRequest === undefined
641
+ ? {}
642
+ : {
643
+ confirmationRevision:
644
+ state.applicationConfirmationRequest.revision.toString(),
645
+ confirmationSequence:
646
+ state.applicationConfirmationRequest.sequence.toString(),
647
+ }),
648
+ retryAttempts: Math.min(
649
+ MAX_BACKOFF_EXPONENT + 1,
650
+ Math.max(0, state.retryAttempts),
651
+ ),
652
+ });
653
+ }
654
+
491
655
  /**
492
656
  * Wait until the latest committed local replication revision is reported as
493
657
  * durably applied by the configured number of current peers. Revisions newer