@peerbit/shared-log 13.2.24 → 13.2.26
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/checked-prune.d.ts +12 -0
- package/dist/src/checked-prune.d.ts.map +1 -1
- package/dist/src/checked-prune.js +33 -0
- package/dist/src/checked-prune.js.map +1 -1
- package/dist/src/coordinate-persistence.d.ts +211 -0
- package/dist/src/coordinate-persistence.d.ts.map +1 -0
- package/dist/src/coordinate-persistence.js +1133 -0
- package/dist/src/coordinate-persistence.js.map +1 -0
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +19 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +127 -133
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1400 -4269
- package/dist/src/index.js.map +1 -1
- package/dist/src/instance-lifecycle.d.ts +117 -0
- package/dist/src/instance-lifecycle.d.ts.map +1 -0
- package/dist/src/instance-lifecycle.js +254 -0
- package/dist/src/instance-lifecycle.js.map +1 -0
- package/dist/src/join-warmup.d.ts +86 -0
- package/dist/src/join-warmup.d.ts.map +1 -0
- package/dist/src/join-warmup.js +351 -0
- package/dist/src/join-warmup.js.map +1 -0
- package/dist/src/native-write-through-block-store.d.ts +123 -0
- package/dist/src/native-write-through-block-store.d.ts.map +1 -0
- package/dist/src/native-write-through-block-store.js +989 -0
- package/dist/src/native-write-through-block-store.js.map +1 -0
- package/dist/src/peer-session.d.ts +117 -0
- package/dist/src/peer-session.d.ts.map +1 -0
- package/dist/src/peer-session.js +259 -0
- package/dist/src/peer-session.js.map +1 -0
- package/dist/src/replication-announcement.d.ts +116 -0
- package/dist/src/replication-announcement.d.ts.map +1 -0
- package/dist/src/replication-announcement.js +515 -0
- package/dist/src/replication-announcement.js.map +1 -0
- package/dist/src/replicator-liveness.d.ts +59 -0
- package/dist/src/replicator-liveness.d.ts.map +1 -0
- package/dist/src/replicator-liveness.js +304 -0
- package/dist/src/replicator-liveness.js.map +1 -0
- package/dist/src/sync/dispatch-lifecycle.d.ts +48 -0
- package/dist/src/sync/dispatch-lifecycle.d.ts.map +1 -0
- package/dist/src/sync/dispatch-lifecycle.js +141 -0
- package/dist/src/sync/dispatch-lifecycle.js.map +1 -0
- package/dist/src/sync/factory.d.ts +29 -0
- package/dist/src/sync/factory.d.ts.map +1 -0
- package/dist/src/sync/factory.js +87 -0
- package/dist/src/sync/factory.js.map +1 -0
- package/dist/src/sync/pending-sync-store.d.ts +105 -0
- package/dist/src/sync/pending-sync-store.d.ts.map +1 -0
- package/dist/src/sync/pending-sync-store.js +877 -0
- package/dist/src/sync/pending-sync-store.js.map +1 -0
- package/dist/src/sync/rateless-iblt.d.ts +4 -1
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.js +102 -106
- package/dist/src/sync/rateless-iblt.js.map +1 -1
- package/dist/src/sync/simple.d.ts +10 -41
- package/dist/src/sync/simple.d.ts.map +1 -1
- package/dist/src/sync/simple.js +259 -952
- package/dist/src/sync/simple.js.map +1 -1
- package/dist/src/sync/sync-peer-state.d.ts +16 -0
- package/dist/src/sync/sync-peer-state.d.ts.map +1 -0
- package/dist/src/sync/sync-peer-state.js +82 -0
- package/dist/src/sync/sync-peer-state.js.map +1 -0
- package/package.json +12 -12
- package/src/checked-prune.ts +35 -0
- package/src/coordinate-persistence.ts +1796 -0
- package/src/errors.ts +21 -0
- package/src/index.ts +4014 -7858
- package/src/instance-lifecycle.ts +351 -0
- package/src/join-warmup.ts +507 -0
- package/src/native-write-through-block-store.ts +1170 -0
- package/src/peer-session.ts +336 -0
- package/src/replication-announcement.ts +744 -0
- package/src/replicator-liveness.ts +439 -0
- package/src/sync/dispatch-lifecycle.ts +230 -0
- package/src/sync/factory.ts +154 -0
- package/src/sync/pending-sync-store.ts +1085 -0
- package/src/sync/rateless-iblt.ts +122 -132
- package/src/sync/simple.ts +334 -1144
- package/src/sync/sync-peer-state.ts +107 -0
package/dist/src/sync/simple.js
CHANGED
|
@@ -39,7 +39,10 @@ import { Entry, Log } from "@peerbit/log";
|
|
|
39
39
|
import { CONVERGENCE_MESSAGE_PRIORITY, SilentDelivery, } from "@peerbit/stream-interface";
|
|
40
40
|
import { EntryWithRefs, createExchangeHeadsMessages, createRawExchangeHeadsMessages, } from "../exchange-heads.js";
|
|
41
41
|
import { TransportMessage } from "../message.js";
|
|
42
|
+
import { DispatchLifecycleRegistry } from "./dispatch-lifecycle.js";
|
|
43
|
+
import { PendingSyncStore, QUEUED_SYNC_ALIAS_REFRESH_PENDING, } from "./pending-sync-store.js";
|
|
42
44
|
import { emitSyncProfileDuration, syncProfileStart } from "./profile.js";
|
|
45
|
+
import { SyncPeerSlotRegistry, } from "./sync-peer-state.js";
|
|
43
46
|
let RequestMaybeSync = (() => {
|
|
44
47
|
let _classDecorators = [variant([0, 1])];
|
|
45
48
|
let _classDescriptor;
|
|
@@ -429,14 +432,10 @@ export const MAX_PENDING_SIMPLE_SYNC_LOOKUPS_GLOBAL = 32;
|
|
|
429
432
|
// Retry scanning can touch persistent indexes. Bound each pass so a full
|
|
430
433
|
// adversarial queue cannot force 40,000 lookups in one event-loop turn.
|
|
431
434
|
export const MAX_SIMPLE_SYNC_RETRY_KEYS_PER_TICK = 4_096;
|
|
432
|
-
//
|
|
433
|
-
//
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
const QUEUED_SYNC_ALIAS_REFRESH_PENDING = Symbol("queued-sync-alias-refresh-pending");
|
|
437
|
-
// This is an absolute first-seen lifetime. Repeated claims and additional peers
|
|
438
|
-
// deliberately do not slide the deadline.
|
|
439
|
-
export const PENDING_SIMPLE_SYNC_KEY_TTL_MS = 60_000;
|
|
435
|
+
// The pending-claim TTL, alias-refresh bound and pending-refresh sentinel
|
|
436
|
+
// moved into ./pending-sync-store.ts with the record store (stage-4 sync
|
|
437
|
+
// unification). Re-exported here for compatibility.
|
|
438
|
+
export { PENDING_SIMPLE_SYNC_KEY_TTL_MS } from "./pending-sync-store.js";
|
|
440
439
|
// Bound retained request/response associations globally. Ten thousand hashes
|
|
441
440
|
// covers several full default-size request batches while keeping adversarial or
|
|
442
441
|
// abandoned requests to a small, predictable amount of heap.
|
|
@@ -455,38 +454,35 @@ const createDeferred = () => {
|
|
|
455
454
|
return { promise, resolve, reject };
|
|
456
455
|
};
|
|
457
456
|
export class SimpleSyncronizer {
|
|
458
|
-
//
|
|
459
|
-
syncInFlightQueue
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
pendingSyncExpiryHeap;
|
|
464
|
-
pendingSyncKeyExpiryNodes;
|
|
465
|
-
pendingSyncAdmissionExpiryNodes;
|
|
457
|
+
// The pending-sync record store owns the claim/admission state. The
|
|
458
|
+
// legacy public map instances (syncInFlightQueue, syncInFlightQueueInverted,
|
|
459
|
+
// syncInFlight, ...) live inside it and are exposed through the accessors
|
|
460
|
+
// below under their historical names.
|
|
461
|
+
pendingSync;
|
|
466
462
|
syncInFlightRetryIterator;
|
|
467
463
|
syncInFlightRetryRemaining = 0;
|
|
468
|
-
syncInFlightQueueClaimants;
|
|
469
|
-
syncInFlightQueueClaimantIndexes;
|
|
470
|
-
syncInFlightQueueRoundRobinCursor;
|
|
471
|
-
syncInFlightQueuedCoordinates;
|
|
472
|
-
syncInFlightQueuedHashByCoordinate;
|
|
473
|
-
syncInFlightQueuedCoordinatesByHash;
|
|
474
|
-
syncInFlightQueuedCoordinateRefreshIterator;
|
|
475
|
-
pendingSyncClaimCount;
|
|
476
|
-
pendingSyncAdmissionCount;
|
|
477
|
-
pendingSyncActiveAdmissionReservations;
|
|
478
|
-
pendingSyncAdmissionCountByPeer;
|
|
479
|
-
pendingSyncAdmissionIdentitiesByPeer;
|
|
480
|
-
pendingSyncAdmissionReservations;
|
|
481
|
-
pendingSyncAdmissionReservationsByPeer;
|
|
482
|
-
pendingSyncAdmissionReservationsByIdentity;
|
|
483
464
|
pendingCoordinateLookupCount;
|
|
484
465
|
pendingCoordinateLookupCountByPeer;
|
|
485
466
|
pendingCoordinateResponseCount;
|
|
486
467
|
pendingCoordinateResponseCountByPeer;
|
|
468
|
+
// Per-peer slot rows for coordinate lookups, coordinate responses and
|
|
469
|
+
// active maybe-sync responses (shared registry; see sync-peer-state.ts
|
|
470
|
+
// for the lifetime policy). Release closures capture the row object:
|
|
471
|
+
// once a disconnect detaches the row, the peer's slots return to the
|
|
472
|
+
// aggregate quota immediately, and a late release only settles the
|
|
473
|
+
// detached row — it never touches a reconnected successor's quota.
|
|
474
|
+
peerSlotRows;
|
|
475
|
+
// map of hash to public keys that we can ask for entries
|
|
476
|
+
get syncInFlightQueue() {
|
|
477
|
+
return this.pendingSync.syncInFlightQueue;
|
|
478
|
+
}
|
|
479
|
+
get syncInFlightQueueInverted() {
|
|
480
|
+
return this.pendingSync.syncInFlightQueueInverted;
|
|
481
|
+
}
|
|
487
482
|
// map of hash to public keys that we have asked for entries
|
|
488
|
-
syncInFlight
|
|
489
|
-
|
|
483
|
+
get syncInFlight() {
|
|
484
|
+
return this.pendingSync.syncInFlight;
|
|
485
|
+
}
|
|
490
486
|
rpc;
|
|
491
487
|
log;
|
|
492
488
|
entryIndex;
|
|
@@ -515,39 +511,22 @@ export class SimpleSyncronizer {
|
|
|
515
511
|
syncDispatchLifecycleController;
|
|
516
512
|
syncDispatchTargetEpochCounter;
|
|
517
513
|
syncDispatchTargetEpochs;
|
|
518
|
-
|
|
514
|
+
syncDispatchRegistry;
|
|
519
515
|
repairSessionCounter;
|
|
520
516
|
repairSessions;
|
|
521
517
|
// Syncing and dedeplucation work
|
|
522
518
|
syncMoreInterval;
|
|
523
519
|
closed;
|
|
524
520
|
constructor(properties) {
|
|
525
|
-
this.
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
this.pendingSyncKeyExpiryNodes = new Map();
|
|
530
|
-
this.pendingSyncAdmissionExpiryNodes = new Map();
|
|
531
|
-
this.syncInFlightQueueClaimants = new Map();
|
|
532
|
-
this.syncInFlightQueueClaimantIndexes = new Map();
|
|
533
|
-
this.syncInFlightQueueRoundRobinCursor = new Map();
|
|
534
|
-
this.syncInFlightQueuedCoordinates = new Set();
|
|
535
|
-
this.syncInFlightQueuedHashByCoordinate = new Map();
|
|
536
|
-
this.syncInFlightQueuedCoordinatesByHash = new Map();
|
|
537
|
-
this.pendingSyncClaimCount = 0;
|
|
538
|
-
this.pendingSyncAdmissionCount = 0;
|
|
539
|
-
this.pendingSyncActiveAdmissionReservations = 0;
|
|
540
|
-
this.pendingSyncAdmissionCountByPeer = new Map();
|
|
541
|
-
this.pendingSyncAdmissionIdentitiesByPeer = new Map();
|
|
542
|
-
this.pendingSyncAdmissionReservations = new Set();
|
|
543
|
-
this.pendingSyncAdmissionReservationsByPeer = new Map();
|
|
544
|
-
this.pendingSyncAdmissionReservationsByIdentity = new Map();
|
|
521
|
+
this.pendingSync = new PendingSyncStore({
|
|
522
|
+
coordinateToHash: properties.coordinateToHash,
|
|
523
|
+
isDispatchEpochCurrent: (peer, epoch) => this.syncDispatchTargetEpochs.get(peer) === epoch,
|
|
524
|
+
});
|
|
545
525
|
this.pendingCoordinateLookupCount = 0;
|
|
546
526
|
this.pendingCoordinateLookupCountByPeer = new Map();
|
|
547
527
|
this.pendingCoordinateResponseCount = 0;
|
|
548
528
|
this.pendingCoordinateResponseCountByPeer = new Map();
|
|
549
|
-
this.
|
|
550
|
-
this.syncInFlightTargetsByKey = new Map();
|
|
529
|
+
this.peerSlotRows = new SyncPeerSlotRegistry();
|
|
551
530
|
this.rpc = properties.rpc;
|
|
552
531
|
this.log = properties.log;
|
|
553
532
|
this.entryIndex = properties.entryIndex;
|
|
@@ -575,7 +554,19 @@ export class SimpleSyncronizer {
|
|
|
575
554
|
this.syncDispatchLifecycleController = new AbortController();
|
|
576
555
|
this.syncDispatchTargetEpochCounter = 0;
|
|
577
556
|
this.syncDispatchTargetEpochs = new Map();
|
|
578
|
-
this.
|
|
557
|
+
this.syncDispatchRegistry = new DispatchLifecycleRegistry({
|
|
558
|
+
isClosed: () => this.closed === true,
|
|
559
|
+
currentOwnershipLifecycleController: () => this.syncDispatchLifecycleController,
|
|
560
|
+
hasRetainedWork: (lifecycle) => lifecycle.retainedWork > 0,
|
|
561
|
+
// Target-activity strategy: dispatch-epoch identity.
|
|
562
|
+
isTargetCurrent: (targetLifecycle) => this.syncDispatchTargetEpochs.get(targetLifecycle.target) ===
|
|
563
|
+
targetLifecycle.epoch,
|
|
564
|
+
onTargetAborted: (targetLifecycle) => {
|
|
565
|
+
for (const batch of [...targetLifecycle.batches]) {
|
|
566
|
+
this.removePendingMaybeSyncResponseBatch(batch);
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
});
|
|
579
570
|
this.repairSessionCounter = 0;
|
|
580
571
|
this.repairSessions = new Map();
|
|
581
572
|
}
|
|
@@ -708,7 +699,7 @@ export class SimpleSyncronizer {
|
|
|
708
699
|
disposed: false,
|
|
709
700
|
abortAllOnTargetDisconnect: options?.abortAllOnTargetDisconnect === true,
|
|
710
701
|
};
|
|
711
|
-
|
|
702
|
+
this.syncDispatchRegistry.register(lifecycle, targets, (forLifecycle, target) => {
|
|
712
703
|
const expectedEpoch = options?.targetEpochs?.get(target);
|
|
713
704
|
const currentEpoch = this.syncDispatchTargetEpochs.get(target);
|
|
714
705
|
const epoch = expectedEpoch ??
|
|
@@ -717,10 +708,10 @@ export class SimpleSyncronizer {
|
|
|
717
708
|
? undefined
|
|
718
709
|
: this.getOrCreateSyncDispatchTargetEpoch(target));
|
|
719
710
|
if (!epoch) {
|
|
720
|
-
|
|
711
|
+
return undefined;
|
|
721
712
|
}
|
|
722
|
-
|
|
723
|
-
lifecycle,
|
|
713
|
+
return {
|
|
714
|
+
lifecycle: forLifecycle,
|
|
724
715
|
target,
|
|
725
716
|
epoch,
|
|
726
717
|
controller: new AbortController(),
|
|
@@ -728,94 +719,31 @@ export class SimpleSyncronizer {
|
|
|
728
719
|
responseLeases: 0,
|
|
729
720
|
activeWaiters: 0,
|
|
730
721
|
};
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
activeForTarget = new Set();
|
|
735
|
-
this.syncDispatchTargets.set(target, activeForTarget);
|
|
736
|
-
}
|
|
737
|
-
activeForTarget.add(targetLifecycle);
|
|
738
|
-
if (this.syncDispatchTargetEpochs.get(target) !== epoch) {
|
|
722
|
+
}, (targetLifecycle) => {
|
|
723
|
+
if (this.syncDispatchTargetEpochs.get(targetLifecycle.target) !==
|
|
724
|
+
targetLifecycle.epoch) {
|
|
739
725
|
this.abortSyncDispatchTarget(targetLifecycle, new Error("sync target lifecycle is stale"));
|
|
740
726
|
}
|
|
741
|
-
}
|
|
742
|
-
ownershipLifecycleController.signal.addEventListener("abort", lifecycle.onOwnerOrCallerAbort, { once: true });
|
|
743
|
-
if (callerSignal && callerSignal !== ownershipLifecycleController.signal) {
|
|
744
|
-
callerSignal.addEventListener("abort", lifecycle.onOwnerOrCallerAbort, {
|
|
745
|
-
once: true,
|
|
746
|
-
});
|
|
747
|
-
}
|
|
748
|
-
if (this.closed === true ||
|
|
749
|
-
ownershipLifecycleController !== this.syncDispatchLifecycleController ||
|
|
750
|
-
ownershipLifecycleController.signal.aborted ||
|
|
751
|
-
callerSignal?.aborted) {
|
|
752
|
-
lifecycle.onOwnerOrCallerAbort();
|
|
753
|
-
}
|
|
727
|
+
});
|
|
754
728
|
return lifecycle;
|
|
755
729
|
}
|
|
756
730
|
abortSyncDispatchTarget(targetLifecycle, reason) {
|
|
757
|
-
|
|
758
|
-
targetLifecycle.controller.abort(reason);
|
|
759
|
-
}
|
|
760
|
-
for (const batch of [...targetLifecycle.batches]) {
|
|
761
|
-
this.removePendingMaybeSyncResponseBatch(batch);
|
|
762
|
-
}
|
|
763
|
-
this.maybeDisposeSyncDispatchLifecycle(targetLifecycle.lifecycle);
|
|
731
|
+
this.syncDispatchRegistry.abortTarget(targetLifecycle, reason);
|
|
764
732
|
}
|
|
765
733
|
abortSyncDispatchLifecycle(lifecycle, reason) {
|
|
766
|
-
|
|
767
|
-
lifecycle.controller.abort(reason);
|
|
768
|
-
}
|
|
769
|
-
for (const targetLifecycle of lifecycle.targets.values()) {
|
|
770
|
-
this.abortSyncDispatchTarget(targetLifecycle, reason);
|
|
771
|
-
}
|
|
772
|
-
this.maybeDisposeSyncDispatchLifecycle(lifecycle);
|
|
734
|
+
this.syncDispatchRegistry.abortLifecycle(lifecycle, reason);
|
|
773
735
|
}
|
|
774
736
|
finishSyncDispatchLifecycle(lifecycle) {
|
|
775
|
-
lifecycle
|
|
776
|
-
this.maybeDisposeSyncDispatchLifecycle(lifecycle);
|
|
737
|
+
this.syncDispatchRegistry.finish(lifecycle);
|
|
777
738
|
}
|
|
778
739
|
maybeDisposeSyncDispatchLifecycle(lifecycle) {
|
|
779
|
-
|
|
780
|
-
!lifecycle.dispatchFinished ||
|
|
781
|
-
lifecycle.retainedWork > 0) {
|
|
782
|
-
return;
|
|
783
|
-
}
|
|
784
|
-
lifecycle.disposed = true;
|
|
785
|
-
lifecycle.ownershipLifecycleController.signal.removeEventListener("abort", lifecycle.onOwnerOrCallerAbort);
|
|
786
|
-
if (lifecycle.callerSignal &&
|
|
787
|
-
lifecycle.callerSignal !== lifecycle.ownershipLifecycleController.signal) {
|
|
788
|
-
lifecycle.callerSignal.removeEventListener("abort", lifecycle.onOwnerOrCallerAbort);
|
|
789
|
-
}
|
|
790
|
-
for (const targetLifecycle of lifecycle.targets.values()) {
|
|
791
|
-
const activeForTarget = this.syncDispatchTargets.get(targetLifecycle.target);
|
|
792
|
-
activeForTarget?.delete(targetLifecycle);
|
|
793
|
-
if (activeForTarget?.size === 0) {
|
|
794
|
-
this.syncDispatchTargets.delete(targetLifecycle.target);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
740
|
+
this.syncDispatchRegistry.maybeDispose(lifecycle);
|
|
797
741
|
}
|
|
798
742
|
isSyncDispatchLifecycleActive(lifecycle, target) {
|
|
799
|
-
|
|
800
|
-
lifecycle.disposed ||
|
|
801
|
-
lifecycle.ownershipLifecycleController !==
|
|
802
|
-
this.syncDispatchLifecycleController ||
|
|
803
|
-
lifecycle.ownershipLifecycleController.signal.aborted ||
|
|
804
|
-
lifecycle.callerSignal?.aborted ||
|
|
805
|
-
lifecycle.controller.signal.aborted) {
|
|
806
|
-
return false;
|
|
807
|
-
}
|
|
808
|
-
if (target === undefined) {
|
|
809
|
-
return true;
|
|
810
|
-
}
|
|
811
|
-
const targetLifecycle = lifecycle.targets.get(target);
|
|
812
|
-
return (targetLifecycle !== undefined &&
|
|
813
|
-
!targetLifecycle.controller.signal.aborted &&
|
|
814
|
-
this.syncDispatchTargetEpochs.get(target) === targetLifecycle.epoch);
|
|
743
|
+
return this.syncDispatchRegistry.isLifecycleActive(lifecycle, target);
|
|
815
744
|
}
|
|
816
745
|
getSyncDispatchSignal(lifecycle, target) {
|
|
817
|
-
return
|
|
818
|
-
lifecycle.controller.signal);
|
|
746
|
+
return this.syncDispatchRegistry.getSignal(lifecycle, target);
|
|
819
747
|
}
|
|
820
748
|
pendingMaybeSyncResponseWaiterBefore(left, right) {
|
|
821
749
|
return left.order < right.order;
|
|
@@ -1336,6 +1264,14 @@ export class SimpleSyncronizer {
|
|
|
1336
1264
|
beginDelivery: () => {
|
|
1337
1265
|
for (const authorization of addedAuthorizations) {
|
|
1338
1266
|
if (!authorization.settled) {
|
|
1267
|
+
if (authorization.deliveryInFlight !== true &&
|
|
1268
|
+
authorization.active === true &&
|
|
1269
|
+
authorization.slotRow?.attached === true) {
|
|
1270
|
+
// The response was accepted before the request send began:
|
|
1271
|
+
// custody of the hash-budget unit moves from the slot row to
|
|
1272
|
+
// finishDelivery for the duration of the send.
|
|
1273
|
+
authorization.slotRow.pendingResponseHashes -= 1;
|
|
1274
|
+
}
|
|
1339
1275
|
authorization.deliveryInFlight = true;
|
|
1340
1276
|
}
|
|
1341
1277
|
}
|
|
@@ -1353,6 +1289,13 @@ export class SimpleSyncronizer {
|
|
|
1353
1289
|
?.get(authorization.hash) !== authorization) {
|
|
1354
1290
|
releasedCount += 1;
|
|
1355
1291
|
}
|
|
1292
|
+
else if (authorization.active === true &&
|
|
1293
|
+
authorization.slotRow?.attached === true) {
|
|
1294
|
+
// The response was accepted while the request send was in
|
|
1295
|
+
// flight; custody of the hash-budget unit returns to the
|
|
1296
|
+
// active lease's slot row.
|
|
1297
|
+
authorization.slotRow.pendingResponseHashes += 1;
|
|
1298
|
+
}
|
|
1356
1299
|
}
|
|
1357
1300
|
if (releasedCount > 0) {
|
|
1358
1301
|
this.pendingMaybeSyncResponseCount -= releasedCount;
|
|
@@ -1609,35 +1552,65 @@ export class SimpleSyncronizer {
|
|
|
1609
1552
|
if (acceptedByLifecycle.size === 0) {
|
|
1610
1553
|
return [];
|
|
1611
1554
|
}
|
|
1555
|
+
const slotRow = this.getOrCreateSyncResponseSlotRow(fromHash);
|
|
1556
|
+
slotRow.active += 1;
|
|
1612
1557
|
this.activeMaybeSyncResponseCount += 1;
|
|
1613
1558
|
this.activeMaybeSyncResponseCountByPeer.set(fromHash, (this.activeMaybeSyncResponseCountByPeer.get(fromHash) ?? 0) + 1);
|
|
1614
|
-
|
|
1559
|
+
// Accepted authorizations leave their batch (above) but stay charged
|
|
1560
|
+
// against the global hash budget. Move that charge into row custody so a
|
|
1561
|
+
// disconnect can return it even when the response ship never settles;
|
|
1562
|
+
// units currently owned by finishDelivery (deliveryInFlight) stay out.
|
|
1563
|
+
for (const { authorizations } of acceptedByLifecycle.values()) {
|
|
1564
|
+
for (const authorization of authorizations) {
|
|
1565
|
+
authorization.slotRow = slotRow;
|
|
1566
|
+
if (authorization.deliveryInFlight !== true) {
|
|
1567
|
+
slotRow.pendingResponseHashes += 1;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
let remainingPhysicalLeases = acceptedByLifecycle.size;
|
|
1615
1572
|
return [...acceptedByLifecycle].map(([targetLifecycle, { hashes: acceptedHashes, authorizations }]) => {
|
|
1616
|
-
let
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
pendingForTarget.delete(authorization.hash);
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
if (pendingForTarget?.size === 0) {
|
|
1633
|
-
this.pendingMaybeSyncResponses.delete(fromHash);
|
|
1573
|
+
let logicalReleased = false;
|
|
1574
|
+
let physicalReleased = false;
|
|
1575
|
+
const releaseLogical = (options) => {
|
|
1576
|
+
if (logicalReleased) {
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
logicalReleased = true;
|
|
1580
|
+
slotRow.activeReleases.delete(releaseLogical);
|
|
1581
|
+
const pendingForTarget = this.pendingMaybeSyncResponses.get(fromHash);
|
|
1582
|
+
for (const authorization of authorizations) {
|
|
1583
|
+
this.settlePendingMaybeSyncResponseAuthorization(authorization, options?.fulfilled === true);
|
|
1584
|
+
if (pendingForTarget?.get(authorization.hash) === authorization) {
|
|
1585
|
+
pendingForTarget.delete(authorization.hash);
|
|
1634
1586
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1587
|
+
}
|
|
1588
|
+
if (pendingForTarget?.size === 0) {
|
|
1589
|
+
this.pendingMaybeSyncResponses.delete(fromHash);
|
|
1590
|
+
}
|
|
1591
|
+
const chargedToRow = authorizations.filter((authorization) => authorization.deliveryInFlight !== true).length;
|
|
1592
|
+
if (slotRow.attached) {
|
|
1593
|
+
this.pendingMaybeSyncResponseCount -= chargedToRow;
|
|
1594
|
+
slotRow.pendingResponseHashes -= chargedToRow;
|
|
1595
|
+
}
|
|
1596
|
+
// else: the hash budget already returned in aggregate when the row
|
|
1597
|
+
// detached; only the deliveryInFlight units (owned by
|
|
1598
|
+
// finishDelivery) remain charged.
|
|
1599
|
+
targetLifecycle.responseLeases -= 1;
|
|
1600
|
+
targetLifecycle.lifecycle.retainedWork -= 1;
|
|
1601
|
+
this.schedulePendingMaybeSyncResponseWaiter();
|
|
1602
|
+
this.maybeDisposeSyncDispatchLifecycle(targetLifecycle.lifecycle);
|
|
1603
|
+
};
|
|
1604
|
+
const releasePhysical = () => {
|
|
1605
|
+
if (physicalReleased) {
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
physicalReleased = true;
|
|
1609
|
+
remainingPhysicalLeases -= 1;
|
|
1610
|
+
if (remainingPhysicalLeases === 0) {
|
|
1611
|
+
slotRow.active -= 1;
|
|
1612
|
+
this.activeMaybeSyncResponseCount -= 1;
|
|
1613
|
+
if (slotRow.attached) {
|
|
1641
1614
|
const activeForPeer = (this.activeMaybeSyncResponseCountByPeer.get(fromHash) ?? 1) -
|
|
1642
1615
|
1;
|
|
1643
1616
|
if (activeForPeer === 0) {
|
|
@@ -1646,9 +1619,21 @@ export class SimpleSyncronizer {
|
|
|
1646
1619
|
else {
|
|
1647
1620
|
this.activeMaybeSyncResponseCountByPeer.set(fromHash, activeForPeer);
|
|
1648
1621
|
}
|
|
1622
|
+
this.maybeDropIdleSyncResponseSlotRow(slotRow);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
slotRow.activeReleases.add(releaseLogical);
|
|
1627
|
+
return {
|
|
1628
|
+
hashes: acceptedHashes,
|
|
1629
|
+
signal: targetLifecycle.controller.signal,
|
|
1630
|
+
release: (options) => {
|
|
1631
|
+
try {
|
|
1632
|
+
releaseLogical(options);
|
|
1633
|
+
}
|
|
1634
|
+
finally {
|
|
1635
|
+
releasePhysical();
|
|
1649
1636
|
}
|
|
1650
|
-
this.schedulePendingMaybeSyncResponseWaiter();
|
|
1651
|
-
this.maybeDisposeSyncDispatchLifecycle(targetLifecycle.lifecycle);
|
|
1652
1637
|
},
|
|
1653
1638
|
};
|
|
1654
1639
|
});
|
|
@@ -2164,11 +2149,26 @@ export class SimpleSyncronizer {
|
|
|
2164
2149
|
return true;
|
|
2165
2150
|
}
|
|
2166
2151
|
const target = from.hashcode();
|
|
2167
|
-
const
|
|
2168
|
-
if (!
|
|
2152
|
+
const lookupPermit = this.tryAcquireCoordinateLookup(target);
|
|
2153
|
+
if (!lookupPermit) {
|
|
2169
2154
|
return true;
|
|
2170
2155
|
}
|
|
2156
|
+
const { release: releaseLookup, row: slotRow } = lookupPermit;
|
|
2171
2157
|
const lifecycle = this.captureSyncDispatchLifecycle([target]);
|
|
2158
|
+
let lifecycleFinished = false;
|
|
2159
|
+
const finishLifecycle = () => {
|
|
2160
|
+
if (lifecycleFinished) {
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
lifecycleFinished = true;
|
|
2164
|
+
slotRow.activeReleases.delete(finishLifecycle);
|
|
2165
|
+
this.finishSyncDispatchLifecycle(lifecycle);
|
|
2166
|
+
this.maybeDropIdleSyncResponseSlotRow(slotRow);
|
|
2167
|
+
};
|
|
2168
|
+
// Dispatch completion is logical ownership. A disconnected peer must
|
|
2169
|
+
// not retain this lifecycle/listeners while a non-abortable lookup or
|
|
2170
|
+
// shipment keeps only its global physical permit alive.
|
|
2171
|
+
slotRow.activeReleases.add(finishLifecycle);
|
|
2172
2172
|
let lookupReleased = false;
|
|
2173
2173
|
const finishLookup = () => {
|
|
2174
2174
|
if (lookupReleased) {
|
|
@@ -2228,7 +2228,7 @@ export class SimpleSyncronizer {
|
|
|
2228
2228
|
}
|
|
2229
2229
|
finally {
|
|
2230
2230
|
finishLookup();
|
|
2231
|
-
|
|
2231
|
+
finishLifecycle();
|
|
2232
2232
|
}
|
|
2233
2233
|
}
|
|
2234
2234
|
else {
|
|
@@ -2246,284 +2246,86 @@ export class SimpleSyncronizer {
|
|
|
2246
2246
|
this.markRepairSessionResolvedHashes(properties.hashes);
|
|
2247
2247
|
}
|
|
2248
2248
|
getPendingSyncKeyIdentity(key) {
|
|
2249
|
-
|
|
2250
|
-
return key;
|
|
2251
|
-
}
|
|
2252
|
-
const hash = this.coordinateToHash.get(key);
|
|
2253
|
-
return hash ?? key;
|
|
2254
|
-
}
|
|
2255
|
-
removeQueuedSyncCoordinateAlias(key) {
|
|
2256
|
-
this.syncInFlightQueuedCoordinates.delete(key);
|
|
2257
|
-
if (this.syncInFlightQueuedCoordinates.size === 0) {
|
|
2258
|
-
this.syncInFlightQueuedCoordinateRefreshIterator = undefined;
|
|
2259
|
-
}
|
|
2260
|
-
const previousHash = this.syncInFlightQueuedHashByCoordinate.get(key);
|
|
2261
|
-
this.syncInFlightQueuedHashByCoordinate.delete(key);
|
|
2262
|
-
if (previousHash != null) {
|
|
2263
|
-
const coordinates = this.syncInFlightQueuedCoordinatesByHash.get(previousHash);
|
|
2264
|
-
coordinates?.delete(key);
|
|
2265
|
-
if (coordinates?.size === 0) {
|
|
2266
|
-
this.syncInFlightQueuedCoordinatesByHash.delete(previousHash);
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
}
|
|
2270
|
-
refreshQueuedSyncCoordinateAlias(key) {
|
|
2271
|
-
if (!this.syncInFlightQueue.has(key)) {
|
|
2272
|
-
this.removeQueuedSyncCoordinateAlias(key);
|
|
2273
|
-
return;
|
|
2274
|
-
}
|
|
2275
|
-
this.syncInFlightQueuedCoordinates.add(key);
|
|
2276
|
-
const hash = this.coordinateToHash.get(key) ?? undefined;
|
|
2277
|
-
const previousHash = this.syncInFlightQueuedHashByCoordinate.get(key);
|
|
2278
|
-
if (previousHash !== hash) {
|
|
2279
|
-
if (previousHash != null) {
|
|
2280
|
-
const coordinates = this.syncInFlightQueuedCoordinatesByHash.get(previousHash);
|
|
2281
|
-
coordinates?.delete(key);
|
|
2282
|
-
if (coordinates?.size === 0) {
|
|
2283
|
-
this.syncInFlightQueuedCoordinatesByHash.delete(previousHash);
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
if (hash == null) {
|
|
2287
|
-
this.syncInFlightQueuedHashByCoordinate.delete(key);
|
|
2288
|
-
}
|
|
2289
|
-
else {
|
|
2290
|
-
this.syncInFlightQueuedHashByCoordinate.set(key, hash);
|
|
2291
|
-
}
|
|
2292
|
-
}
|
|
2293
|
-
if (hash != null) {
|
|
2294
|
-
let coordinates = this.syncInFlightQueuedCoordinatesByHash.get(hash);
|
|
2295
|
-
if (!coordinates) {
|
|
2296
|
-
coordinates = new Set();
|
|
2297
|
-
this.syncInFlightQueuedCoordinatesByHash.set(hash, coordinates);
|
|
2298
|
-
}
|
|
2299
|
-
coordinates.add(key);
|
|
2300
|
-
this.reconcileQueuedSyncCoordinateAlias(key, hash);
|
|
2301
|
-
}
|
|
2302
|
-
}
|
|
2303
|
-
reconcileQueuedSyncCoordinateAlias(coordinate, hash) {
|
|
2304
|
-
const now = Date.now();
|
|
2305
|
-
const coordinateExpiresAt = this.syncInFlightQueueExpiresAt.get(coordinate);
|
|
2306
|
-
if (coordinateExpiresAt != null && coordinateExpiresAt <= now) {
|
|
2307
|
-
this.clearSyncProcessKey(coordinate);
|
|
2308
|
-
return;
|
|
2309
|
-
}
|
|
2310
|
-
const hashExpiresAt = this.syncInFlightQueueExpiresAt.get(hash);
|
|
2311
|
-
if (hashExpiresAt != null && hashExpiresAt <= now) {
|
|
2312
|
-
this.clearSyncProcessKey(hash);
|
|
2313
|
-
return;
|
|
2314
|
-
}
|
|
2315
|
-
const hashClaimants = this.syncInFlightQueue.get(hash);
|
|
2316
|
-
if (!hashClaimants || !this.syncInFlightQueue.has(coordinate)) {
|
|
2317
|
-
return;
|
|
2318
|
-
}
|
|
2319
|
-
const expiresAt = Math.min(this.syncInFlightQueueExpiresAt.get(coordinate) ?? Infinity, this.syncInFlightQueueExpiresAt.get(hash) ?? Infinity);
|
|
2320
|
-
for (const claimant of [...hashClaimants]) {
|
|
2321
|
-
this.addPendingSyncClaim(coordinate, claimant, expiresAt);
|
|
2322
|
-
}
|
|
2323
|
-
if (Number.isFinite(expiresAt)) {
|
|
2324
|
-
this.movePendingSyncKeyExpiryEarlier(coordinate, expiresAt);
|
|
2325
|
-
}
|
|
2326
|
-
for (const target of [...(this.syncInFlightTargetsByKey.get(hash) ?? [])]) {
|
|
2327
|
-
const state = this.syncInFlight.get(target)?.get(hash);
|
|
2328
|
-
if (state) {
|
|
2329
|
-
this.setSyncInFlightTargetKey(target, coordinate, state.timestamp);
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
this.clearSyncProcessKey(hash);
|
|
2249
|
+
return this.pendingSync.getPendingSyncKeyIdentity(key);
|
|
2333
2250
|
}
|
|
2334
2251
|
refreshQueuedSyncCoordinateAliases() {
|
|
2335
|
-
|
|
2336
|
-
this.syncInFlightQueueClaimants.size < this.syncInFlightQueue.size) {
|
|
2337
|
-
// Defensive compatibility for callers/tests that seed the public queue
|
|
2338
|
-
// directly. Keep hydration bounded; internal writes register coordinates
|
|
2339
|
-
// when the key is first admitted.
|
|
2340
|
-
let inspected = 0;
|
|
2341
|
-
for (const key of this.syncInFlightQueue.keys()) {
|
|
2342
|
-
if (typeof key === "bigint") {
|
|
2343
|
-
this.syncInFlightQueuedCoordinates.add(key);
|
|
2344
|
-
}
|
|
2345
|
-
inspected += 1;
|
|
2346
|
-
if (inspected >= MAX_PENDING_SIMPLE_SYNC_ALIAS_REFRESH_PER_MESSAGE) {
|
|
2347
|
-
break;
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
if (this.syncInFlightQueuedCoordinates.size === 0) {
|
|
2352
|
-
this.syncInFlightQueuedCoordinateRefreshIterator = undefined;
|
|
2353
|
-
return;
|
|
2354
|
-
}
|
|
2355
|
-
this.syncInFlightQueuedCoordinateRefreshIterator ??=
|
|
2356
|
-
this.syncInFlightQueuedCoordinates.values();
|
|
2357
|
-
for (let refreshed = 0; refreshed < MAX_PENDING_SIMPLE_SYNC_ALIAS_REFRESH_PER_MESSAGE; refreshed += 1) {
|
|
2358
|
-
const next = this.syncInFlightQueuedCoordinateRefreshIterator.next();
|
|
2359
|
-
if (next.done) {
|
|
2360
|
-
this.syncInFlightQueuedCoordinateRefreshIterator = undefined;
|
|
2361
|
-
break;
|
|
2362
|
-
}
|
|
2363
|
-
this.refreshQueuedSyncCoordinateAlias(next.value);
|
|
2364
|
-
}
|
|
2252
|
+
this.pendingSync.refreshQueuedSyncCoordinateAliases();
|
|
2365
2253
|
}
|
|
2366
2254
|
getQueuedSyncKeyForAdmission(key) {
|
|
2367
|
-
|
|
2368
|
-
if (!this.syncInFlightQueue.has(candidate)) {
|
|
2369
|
-
return undefined;
|
|
2370
|
-
}
|
|
2371
|
-
const expiresAt = this.syncInFlightQueueExpiresAt.get(candidate);
|
|
2372
|
-
if (expiresAt != null && expiresAt <= Date.now()) {
|
|
2373
|
-
this.clearSyncProcessKey(candidate);
|
|
2374
|
-
return undefined;
|
|
2375
|
-
}
|
|
2376
|
-
return candidate;
|
|
2377
|
-
};
|
|
2378
|
-
if (getValidQueuedKey(key) != null) {
|
|
2379
|
-
if (typeof key === "bigint") {
|
|
2380
|
-
this.refreshQueuedSyncCoordinateAlias(key);
|
|
2381
|
-
return getValidQueuedKey(key);
|
|
2382
|
-
}
|
|
2383
|
-
return key;
|
|
2384
|
-
}
|
|
2385
|
-
if (typeof key === "string") {
|
|
2386
|
-
const aliases = this.syncInFlightQueuedCoordinatesByHash.get(key);
|
|
2387
|
-
if (aliases) {
|
|
2388
|
-
let inspected = 0;
|
|
2389
|
-
for (const alias of aliases) {
|
|
2390
|
-
if (inspected >= MAX_PENDING_SIMPLE_SYNC_ALIAS_REFRESH_PER_MESSAGE) {
|
|
2391
|
-
return QUEUED_SYNC_ALIAS_REFRESH_PENDING;
|
|
2392
|
-
}
|
|
2393
|
-
inspected += 1;
|
|
2394
|
-
this.refreshQueuedSyncCoordinateAlias(alias);
|
|
2395
|
-
if (this.syncInFlightQueuedCoordinatesByHash.get(key)?.has(alias) ===
|
|
2396
|
-
true) {
|
|
2397
|
-
const validAlias = getValidQueuedKey(alias);
|
|
2398
|
-
if (validAlias != null) {
|
|
2399
|
-
return validAlias;
|
|
2400
|
-
}
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
if ((this.syncInFlightQueuedCoordinatesByHash.get(key)?.size ?? 0) > 0) {
|
|
2404
|
-
return QUEUED_SYNC_ALIAS_REFRESH_PENDING;
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
return undefined;
|
|
2408
|
-
}
|
|
2409
|
-
const hash = this.coordinateToHash.get(key);
|
|
2410
|
-
return hash != null ? getValidQueuedKey(hash) : undefined;
|
|
2255
|
+
return this.pendingSync.getQueuedSyncKeyForAdmission(key);
|
|
2411
2256
|
}
|
|
2412
2257
|
addPendingSyncClaim(key, from, expiresAt) {
|
|
2413
|
-
|
|
2414
|
-
let peers = this.syncInFlightQueue.get(key);
|
|
2415
|
-
let claimants = this.syncInFlightQueueClaimants.get(key);
|
|
2416
|
-
let claimantIndexes = this.syncInFlightQueueClaimantIndexes.get(key);
|
|
2417
|
-
if (!peers) {
|
|
2418
|
-
peers = [];
|
|
2419
|
-
this.syncInFlightQueue.set(key, peers);
|
|
2420
|
-
claimants = new Set();
|
|
2421
|
-
this.syncInFlightQueueClaimants.set(key, claimants);
|
|
2422
|
-
claimantIndexes = new Map();
|
|
2423
|
-
this.syncInFlightQueueClaimantIndexes.set(key, claimantIndexes);
|
|
2424
|
-
const deadline = expiresAt ?? Date.now() + PENDING_SIMPLE_SYNC_KEY_TTL_MS;
|
|
2425
|
-
this.syncInFlightQueueExpiresAt.set(key, deadline);
|
|
2426
|
-
const expiryNode = {
|
|
2427
|
-
kind: "key",
|
|
2428
|
-
key,
|
|
2429
|
-
expiresAt: deadline,
|
|
2430
|
-
heapIndex: -1,
|
|
2431
|
-
};
|
|
2432
|
-
this.pendingSyncKeyExpiryNodes.set(key, expiryNode);
|
|
2433
|
-
this.pushPendingSyncExpiry(expiryNode);
|
|
2434
|
-
if (typeof key === "bigint") {
|
|
2435
|
-
this.refreshQueuedSyncCoordinateAlias(key);
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
else if (!claimants) {
|
|
2439
|
-
// Defensive compatibility for callers/tests that seed the public queue
|
|
2440
|
-
// maps directly. Internally every retained key gets this set at creation.
|
|
2441
|
-
claimants = new Set(peers.map((peer) => peer.hashcode()));
|
|
2442
|
-
this.syncInFlightQueueClaimants.set(key, claimants);
|
|
2443
|
-
this.pendingSyncClaimCount += claimants.size;
|
|
2444
|
-
}
|
|
2445
|
-
if (!claimantIndexes) {
|
|
2446
|
-
claimantIndexes = new Map();
|
|
2447
|
-
for (let index = 0; index < peers.length; index += 1) {
|
|
2448
|
-
claimantIndexes.set(peers[index].hashcode(), index);
|
|
2449
|
-
}
|
|
2450
|
-
this.syncInFlightQueueClaimantIndexes.set(key, claimantIndexes);
|
|
2451
|
-
}
|
|
2452
|
-
if (claimants.has(fromHash)) {
|
|
2453
|
-
return false;
|
|
2454
|
-
}
|
|
2455
|
-
claimantIndexes.set(fromHash, peers.length);
|
|
2456
|
-
peers.push(from);
|
|
2457
|
-
claimants.add(fromHash);
|
|
2458
|
-
let inverted = this.syncInFlightQueueInverted.get(fromHash);
|
|
2459
|
-
if (!inverted) {
|
|
2460
|
-
inverted = new Set();
|
|
2461
|
-
this.syncInFlightQueueInverted.set(fromHash, inverted);
|
|
2462
|
-
}
|
|
2463
|
-
inverted.add(key);
|
|
2464
|
-
this.pendingSyncClaimCount += 1;
|
|
2465
|
-
this.schedulePendingSyncKeyExpiry();
|
|
2466
|
-
return true;
|
|
2258
|
+
return this.pendingSync.addPendingSyncClaim(key, from, expiresAt);
|
|
2467
2259
|
}
|
|
2468
2260
|
hasPendingSyncClaim(key, peer) {
|
|
2469
|
-
|
|
2470
|
-
if (claimants) {
|
|
2471
|
-
return claimants.has(peer);
|
|
2472
|
-
}
|
|
2473
|
-
const peers = this.syncInFlightQueue.get(key);
|
|
2474
|
-
if (!peers) {
|
|
2475
|
-
return false;
|
|
2476
|
-
}
|
|
2477
|
-
const hydrated = new Set(peers.map((candidate) => candidate.hashcode()));
|
|
2478
|
-
this.syncInFlightQueueClaimants.set(key, hydrated);
|
|
2479
|
-
const indexes = new Map();
|
|
2480
|
-
for (let index = 0; index < peers.length; index += 1) {
|
|
2481
|
-
indexes.set(peers[index].hashcode(), index);
|
|
2482
|
-
}
|
|
2483
|
-
this.syncInFlightQueueClaimantIndexes.set(key, indexes);
|
|
2484
|
-
this.pendingSyncClaimCount += hydrated.size;
|
|
2485
|
-
return hydrated.has(peer);
|
|
2261
|
+
return this.pendingSync.hasPendingSyncClaim(key, peer);
|
|
2486
2262
|
}
|
|
2487
2263
|
filterDispatchablePendingSyncClaims(keys, peer, epoch) {
|
|
2488
|
-
|
|
2489
|
-
return [];
|
|
2490
|
-
}
|
|
2491
|
-
const now = Date.now();
|
|
2492
|
-
const dispatchable = [];
|
|
2493
|
-
for (const key of keys) {
|
|
2494
|
-
const expiresAt = this.syncInFlightQueueExpiresAt.get(key);
|
|
2495
|
-
if (expiresAt != null && expiresAt <= now) {
|
|
2496
|
-
this.clearSyncProcessKey(key);
|
|
2497
|
-
continue;
|
|
2498
|
-
}
|
|
2499
|
-
if (this.syncInFlightQueue.has(key) &&
|
|
2500
|
-
this.hasPendingSyncClaim(key, peer)) {
|
|
2501
|
-
dispatchable.push(key);
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
return dispatchable;
|
|
2264
|
+
return this.pendingSync.filterDispatchablePendingSyncClaims(keys, peer, epoch);
|
|
2505
2265
|
}
|
|
2506
2266
|
canStartPendingSyncLookup(peer) {
|
|
2507
|
-
return (this.pendingSyncAdmissionReservations.size +
|
|
2267
|
+
return (this.pendingSync.pendingSyncAdmissionReservations.size +
|
|
2508
2268
|
this.pendingCoordinateLookupCount <
|
|
2509
2269
|
MAX_PENDING_SIMPLE_SYNC_LOOKUPS_GLOBAL &&
|
|
2510
|
-
(this.pendingSyncAdmissionReservationsByPeer.get(peer)
|
|
2270
|
+
(this.pendingSync.pendingSyncAdmissionReservationsByPeer.get(peer)
|
|
2271
|
+
?.size ?? 0) +
|
|
2511
2272
|
(this.pendingCoordinateLookupCountByPeer.get(peer) ?? 0) <
|
|
2512
2273
|
MAX_PENDING_SIMPLE_SYNC_LOOKUPS_PER_PEER);
|
|
2513
2274
|
}
|
|
2275
|
+
getOrCreateSyncResponseSlotRow(peer) {
|
|
2276
|
+
return this.peerSlotRows.ensure(peer);
|
|
2277
|
+
}
|
|
2278
|
+
detachSyncResponseSlotRow(peer) {
|
|
2279
|
+
const row = this.peerSlotRows.detach(peer);
|
|
2280
|
+
if (!row) {
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
// Disconnect returns only the current peer generation's quota. The
|
|
2284
|
+
// global counters represent physically live, potentially non-abortable
|
|
2285
|
+
// work and remain charged until each captured release actually settles.
|
|
2286
|
+
this.pendingCoordinateLookupCountByPeer.delete(peer);
|
|
2287
|
+
this.pendingCoordinateResponseCountByPeer.delete(peer);
|
|
2288
|
+
this.activeMaybeSyncResponseCountByPeer.delete(peer);
|
|
2289
|
+
if (row.pendingResponseHashes > 0) {
|
|
2290
|
+
// Active authorizations' hash budget returns in aggregate with the
|
|
2291
|
+
// row. The settled releases below (and any late ship-side release)
|
|
2292
|
+
// see the detached row and skip the global budget.
|
|
2293
|
+
this.pendingMaybeSyncResponseCount -= row.pendingResponseHashes;
|
|
2294
|
+
row.pendingResponseHashes = 0;
|
|
2295
|
+
this.schedulePendingMaybeSyncResponseWaiter();
|
|
2296
|
+
}
|
|
2297
|
+
// Settle only logical active-response ownership so authorizations leave
|
|
2298
|
+
// pendingMaybeSyncResponses and responseLeases/retainedWork drain. The
|
|
2299
|
+
// eventual ship-side release still returns its global physical permit.
|
|
2300
|
+
// Closures capture THIS row (identity, not peer hash), so a reconnected
|
|
2301
|
+
// successor's row is never touched.
|
|
2302
|
+
for (const release of [...row.activeReleases]) {
|
|
2303
|
+
release();
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
maybeDropIdleSyncResponseSlotRow(row) {
|
|
2307
|
+
this.peerSlotRows.maybeDropIdle(row);
|
|
2308
|
+
}
|
|
2514
2309
|
tryAcquireCoordinateLookup(peer) {
|
|
2515
2310
|
if (!this.canStartPendingSyncLookup(peer)) {
|
|
2516
2311
|
return undefined;
|
|
2517
2312
|
}
|
|
2313
|
+
const row = this.getOrCreateSyncResponseSlotRow(peer);
|
|
2314
|
+
row.lookups += 1;
|
|
2518
2315
|
this.pendingCoordinateLookupCount += 1;
|
|
2519
2316
|
this.pendingCoordinateLookupCountByPeer.set(peer, (this.pendingCoordinateLookupCountByPeer.get(peer) ?? 0) + 1);
|
|
2520
2317
|
let released = false;
|
|
2521
|
-
|
|
2318
|
+
const release = () => {
|
|
2522
2319
|
if (released) {
|
|
2523
2320
|
return;
|
|
2524
2321
|
}
|
|
2525
2322
|
released = true;
|
|
2323
|
+
row.lookups -= 1;
|
|
2526
2324
|
this.pendingCoordinateLookupCount -= 1;
|
|
2325
|
+
if (!row.attached) {
|
|
2326
|
+
// Per-peer quota returned when this generation detached.
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2527
2329
|
const remaining = (this.pendingCoordinateLookupCountByPeer.get(peer) ?? 1) - 1;
|
|
2528
2330
|
if (remaining === 0) {
|
|
2529
2331
|
this.pendingCoordinateLookupCountByPeer.delete(peer);
|
|
@@ -2531,7 +2333,9 @@ export class SimpleSyncronizer {
|
|
|
2531
2333
|
else {
|
|
2532
2334
|
this.pendingCoordinateLookupCountByPeer.set(peer, remaining);
|
|
2533
2335
|
}
|
|
2336
|
+
this.maybeDropIdleSyncResponseSlotRow(row);
|
|
2534
2337
|
};
|
|
2338
|
+
return { row, release };
|
|
2535
2339
|
}
|
|
2536
2340
|
tryAcquireCoordinateResponse(peer) {
|
|
2537
2341
|
if (this.pendingCoordinateResponseCount >=
|
|
@@ -2540,6 +2344,8 @@ export class SimpleSyncronizer {
|
|
|
2540
2344
|
MAX_PENDING_SIMPLE_COORDINATE_RESPONSES_PER_PEER) {
|
|
2541
2345
|
return undefined;
|
|
2542
2346
|
}
|
|
2347
|
+
const row = this.getOrCreateSyncResponseSlotRow(peer);
|
|
2348
|
+
row.responses += 1;
|
|
2543
2349
|
this.pendingCoordinateResponseCount += 1;
|
|
2544
2350
|
this.pendingCoordinateResponseCountByPeer.set(peer, (this.pendingCoordinateResponseCountByPeer.get(peer) ?? 0) + 1);
|
|
2545
2351
|
let released = false;
|
|
@@ -2548,7 +2354,12 @@ export class SimpleSyncronizer {
|
|
|
2548
2354
|
return;
|
|
2549
2355
|
}
|
|
2550
2356
|
released = true;
|
|
2357
|
+
row.responses -= 1;
|
|
2551
2358
|
this.pendingCoordinateResponseCount -= 1;
|
|
2359
|
+
if (!row.attached) {
|
|
2360
|
+
// Per-peer quota returned when this generation detached.
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2552
2363
|
const remaining = (this.pendingCoordinateResponseCountByPeer.get(peer) ?? 1) - 1;
|
|
2553
2364
|
if (remaining === 0) {
|
|
2554
2365
|
this.pendingCoordinateResponseCountByPeer.delete(peer);
|
|
@@ -2556,347 +2367,35 @@ export class SimpleSyncronizer {
|
|
|
2556
2367
|
else {
|
|
2557
2368
|
this.pendingCoordinateResponseCountByPeer.set(peer, remaining);
|
|
2558
2369
|
}
|
|
2370
|
+
this.maybeDropIdleSyncResponseSlotRow(row);
|
|
2559
2371
|
};
|
|
2560
2372
|
}
|
|
2561
2373
|
reservePendingSyncAdmission(peer, identities) {
|
|
2562
|
-
|
|
2563
|
-
if (count <= 0) {
|
|
2564
|
-
return undefined;
|
|
2565
|
-
}
|
|
2566
|
-
const reservation = {
|
|
2567
|
-
peer,
|
|
2568
|
-
remaining: count,
|
|
2569
|
-
active: true,
|
|
2570
|
-
released: false,
|
|
2571
|
-
expiresAt: Date.now() + PENDING_SIMPLE_SYNC_KEY_TTL_MS,
|
|
2572
|
-
identities: new Set(identities),
|
|
2573
|
-
retainedSettled: 0,
|
|
2574
|
-
};
|
|
2575
|
-
this.pendingSyncAdmissionReservations.add(reservation);
|
|
2576
|
-
let peerReservations = this.pendingSyncAdmissionReservationsByPeer.get(peer);
|
|
2577
|
-
if (!peerReservations) {
|
|
2578
|
-
peerReservations = new Set();
|
|
2579
|
-
this.pendingSyncAdmissionReservationsByPeer.set(peer, peerReservations);
|
|
2580
|
-
}
|
|
2581
|
-
peerReservations.add(reservation);
|
|
2582
|
-
this.pendingSyncActiveAdmissionReservations += 1;
|
|
2583
|
-
this.pendingSyncAdmissionCount += count;
|
|
2584
|
-
this.pendingSyncAdmissionCountByPeer.set(peer, (this.pendingSyncAdmissionCountByPeer.get(peer) ?? 0) + count);
|
|
2585
|
-
let reservedIdentities = this.pendingSyncAdmissionIdentitiesByPeer.get(peer);
|
|
2586
|
-
if (!reservedIdentities) {
|
|
2587
|
-
reservedIdentities = new Set();
|
|
2588
|
-
this.pendingSyncAdmissionIdentitiesByPeer.set(peer, reservedIdentities);
|
|
2589
|
-
}
|
|
2590
|
-
for (const identity of identities) {
|
|
2591
|
-
reservedIdentities.add(identity);
|
|
2592
|
-
let reservationsForIdentity = this.pendingSyncAdmissionReservationsByIdentity.get(identity);
|
|
2593
|
-
if (!reservationsForIdentity) {
|
|
2594
|
-
reservationsForIdentity = new Set();
|
|
2595
|
-
this.pendingSyncAdmissionReservationsByIdentity.set(identity, reservationsForIdentity);
|
|
2596
|
-
}
|
|
2597
|
-
reservationsForIdentity.add(reservation);
|
|
2598
|
-
}
|
|
2599
|
-
const expiryNode = {
|
|
2600
|
-
kind: "admission",
|
|
2601
|
-
reservation,
|
|
2602
|
-
expiresAt: reservation.expiresAt,
|
|
2603
|
-
heapIndex: -1,
|
|
2604
|
-
};
|
|
2605
|
-
this.pendingSyncAdmissionExpiryNodes.set(reservation, expiryNode);
|
|
2606
|
-
this.pushPendingSyncExpiry(expiryNode);
|
|
2607
|
-
this.schedulePendingSyncKeyExpiry();
|
|
2608
|
-
return reservation;
|
|
2609
|
-
}
|
|
2610
|
-
removePendingSyncAdmissionIdentity(reservation, identity, options) {
|
|
2611
|
-
if (!reservation.identities.delete(identity)) {
|
|
2612
|
-
return false;
|
|
2613
|
-
}
|
|
2614
|
-
if (options?.retainQuota === true) {
|
|
2615
|
-
reservation.retainedSettled += 1;
|
|
2616
|
-
}
|
|
2617
|
-
else {
|
|
2618
|
-
reservation.remaining -= 1;
|
|
2619
|
-
this.pendingSyncAdmissionCount -= 1;
|
|
2620
|
-
const peerCount = (this.pendingSyncAdmissionCountByPeer.get(reservation.peer) ?? 0) - 1;
|
|
2621
|
-
if (peerCount === 0) {
|
|
2622
|
-
this.pendingSyncAdmissionCountByPeer.delete(reservation.peer);
|
|
2623
|
-
}
|
|
2624
|
-
else {
|
|
2625
|
-
this.pendingSyncAdmissionCountByPeer.set(reservation.peer, peerCount);
|
|
2626
|
-
}
|
|
2627
|
-
}
|
|
2628
|
-
const reservedIdentities = this.pendingSyncAdmissionIdentitiesByPeer.get(reservation.peer);
|
|
2629
|
-
reservedIdentities?.delete(identity);
|
|
2630
|
-
if (reservedIdentities?.size === 0) {
|
|
2631
|
-
this.pendingSyncAdmissionIdentitiesByPeer.delete(reservation.peer);
|
|
2632
|
-
}
|
|
2633
|
-
const reservationsForIdentity = this.pendingSyncAdmissionReservationsByIdentity.get(identity);
|
|
2634
|
-
reservationsForIdentity?.delete(reservation);
|
|
2635
|
-
if (reservationsForIdentity?.size === 0) {
|
|
2636
|
-
this.pendingSyncAdmissionReservationsByIdentity.delete(identity);
|
|
2637
|
-
}
|
|
2638
|
-
return true;
|
|
2374
|
+
return this.pendingSync.reservePendingSyncAdmission(peer, identities);
|
|
2639
2375
|
}
|
|
2640
2376
|
clearPendingSyncAdmissionIdentity(identity) {
|
|
2641
|
-
|
|
2642
|
-
if (!reservations) {
|
|
2643
|
-
return;
|
|
2644
|
-
}
|
|
2645
|
-
for (const reservation of [...reservations]) {
|
|
2646
|
-
this.removePendingSyncAdmissionIdentity(reservation, identity, {
|
|
2647
|
-
retainQuota: true,
|
|
2648
|
-
});
|
|
2649
|
-
}
|
|
2377
|
+
this.pendingSync.clearPendingSyncAdmissionIdentity(identity);
|
|
2650
2378
|
}
|
|
2651
2379
|
consumePendingSyncAdmission(reservation, identity) {
|
|
2652
|
-
|
|
2653
|
-
if (reservation.expiresAt <= Date.now()) {
|
|
2654
|
-
this.invalidatePendingSyncAdmission(reservation);
|
|
2655
|
-
}
|
|
2656
|
-
return "invalid";
|
|
2657
|
-
}
|
|
2658
|
-
if (!reservation.identities.has(identity)) {
|
|
2659
|
-
return "settled";
|
|
2660
|
-
}
|
|
2661
|
-
this.removePendingSyncAdmissionIdentity(reservation, identity);
|
|
2662
|
-
if (reservation.remaining === 0) {
|
|
2663
|
-
this.removePendingSyncAdmissionExpiry(reservation);
|
|
2664
|
-
reservation.active = false;
|
|
2665
|
-
reservation.released = true;
|
|
2666
|
-
this.pendingSyncActiveAdmissionReservations -= 1;
|
|
2667
|
-
this.pendingSyncAdmissionReservations.delete(reservation);
|
|
2668
|
-
const peerReservations = this.pendingSyncAdmissionReservationsByPeer.get(reservation.peer);
|
|
2669
|
-
peerReservations?.delete(reservation);
|
|
2670
|
-
if (peerReservations?.size === 0) {
|
|
2671
|
-
this.pendingSyncAdmissionReservationsByPeer.delete(reservation.peer);
|
|
2672
|
-
}
|
|
2673
|
-
this.clearPendingSyncExpiryTimerIfIdle();
|
|
2674
|
-
}
|
|
2675
|
-
return "consumed";
|
|
2380
|
+
return this.pendingSync.consumePendingSyncAdmission(reservation, identity);
|
|
2676
2381
|
}
|
|
2677
2382
|
transferPendingSyncAdmissionIdentity(peer, identity) {
|
|
2678
|
-
|
|
2679
|
-
if (!reservations) {
|
|
2680
|
-
return undefined;
|
|
2681
|
-
}
|
|
2682
|
-
for (const reservation of reservations) {
|
|
2683
|
-
if (reservation.peer !== peer ||
|
|
2684
|
-
reservation.released ||
|
|
2685
|
-
reservation.expiresAt <= Date.now() ||
|
|
2686
|
-
!this.removePendingSyncAdmissionIdentity(reservation, identity, {
|
|
2687
|
-
retainQuota: true,
|
|
2688
|
-
})) {
|
|
2689
|
-
continue;
|
|
2690
|
-
}
|
|
2691
|
-
// The original resolver may be non-abortable and still retains its input
|
|
2692
|
-
// arrays. Keep that reservation charged until its queueSync finally
|
|
2693
|
-
// settles; the queued claim is counted separately and can disappear
|
|
2694
|
-
// without returning the resolver's quota early.
|
|
2695
|
-
return reservation.expiresAt;
|
|
2696
|
-
}
|
|
2697
|
-
return undefined;
|
|
2698
|
-
}
|
|
2699
|
-
invalidatePendingSyncAdmission(reservation) {
|
|
2700
|
-
if (!reservation || reservation.released || !reservation.active) {
|
|
2701
|
-
return;
|
|
2702
|
-
}
|
|
2703
|
-
// Expiry/disconnect invalidates late lookup results, but it must not return
|
|
2704
|
-
// the quota slot while the underlying storage/index work is still alive.
|
|
2705
|
-
// Those lookups are not generally abortable; only queueSync's finally block
|
|
2706
|
-
// may release their active-work accounting.
|
|
2707
|
-
this.removePendingSyncAdmissionExpiry(reservation);
|
|
2708
|
-
reservation.active = false;
|
|
2709
|
-
this.pendingSyncActiveAdmissionReservations -= 1;
|
|
2710
|
-
this.clearPendingSyncExpiryTimerIfIdle();
|
|
2383
|
+
return this.pendingSync.transferPendingSyncAdmissionIdentity(peer, identity);
|
|
2711
2384
|
}
|
|
2712
2385
|
releasePendingSyncAdmission(reservation) {
|
|
2713
|
-
|
|
2714
|
-
return;
|
|
2715
|
-
}
|
|
2716
|
-
this.removePendingSyncAdmissionExpiry(reservation);
|
|
2717
|
-
for (const identity of [...reservation.identities]) {
|
|
2718
|
-
this.removePendingSyncAdmissionIdentity(reservation, identity);
|
|
2719
|
-
}
|
|
2720
|
-
if (reservation.retainedSettled > 0) {
|
|
2721
|
-
const retainedSettled = reservation.retainedSettled;
|
|
2722
|
-
reservation.retainedSettled = 0;
|
|
2723
|
-
reservation.remaining -= retainedSettled;
|
|
2724
|
-
this.pendingSyncAdmissionCount -= retainedSettled;
|
|
2725
|
-
const peerCount = (this.pendingSyncAdmissionCountByPeer.get(reservation.peer) ?? 0) -
|
|
2726
|
-
retainedSettled;
|
|
2727
|
-
if (peerCount === 0) {
|
|
2728
|
-
this.pendingSyncAdmissionCountByPeer.delete(reservation.peer);
|
|
2729
|
-
}
|
|
2730
|
-
else {
|
|
2731
|
-
this.pendingSyncAdmissionCountByPeer.set(reservation.peer, peerCount);
|
|
2732
|
-
}
|
|
2733
|
-
}
|
|
2734
|
-
if (reservation.active) {
|
|
2735
|
-
this.pendingSyncActiveAdmissionReservations -= 1;
|
|
2736
|
-
}
|
|
2737
|
-
reservation.active = false;
|
|
2738
|
-
reservation.released = true;
|
|
2739
|
-
this.pendingSyncAdmissionReservations.delete(reservation);
|
|
2740
|
-
const peerReservations = this.pendingSyncAdmissionReservationsByPeer.get(reservation.peer);
|
|
2741
|
-
peerReservations?.delete(reservation);
|
|
2742
|
-
if (peerReservations?.size === 0) {
|
|
2743
|
-
this.pendingSyncAdmissionReservationsByPeer.delete(reservation.peer);
|
|
2744
|
-
}
|
|
2745
|
-
this.clearPendingSyncExpiryTimerIfIdle();
|
|
2386
|
+
this.pendingSync.releasePendingSyncAdmission(reservation);
|
|
2746
2387
|
}
|
|
2747
2388
|
clearPendingSyncAdmissions(peer) {
|
|
2748
|
-
|
|
2749
|
-
? this.pendingSyncAdmissionReservations
|
|
2750
|
-
: this.pendingSyncAdmissionReservationsByPeer.get(peer);
|
|
2751
|
-
if (!reservations) {
|
|
2752
|
-
return;
|
|
2753
|
-
}
|
|
2754
|
-
for (const reservation of [...reservations]) {
|
|
2755
|
-
this.invalidatePendingSyncAdmission(reservation);
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
swapPendingSyncExpiry(left, right) {
|
|
2759
|
-
const leftNode = this.pendingSyncExpiryHeap[left];
|
|
2760
|
-
const rightNode = this.pendingSyncExpiryHeap[right];
|
|
2761
|
-
this.pendingSyncExpiryHeap[left] = rightNode;
|
|
2762
|
-
this.pendingSyncExpiryHeap[right] = leftNode;
|
|
2763
|
-
rightNode.heapIndex = left;
|
|
2764
|
-
leftNode.heapIndex = right;
|
|
2765
|
-
}
|
|
2766
|
-
pushPendingSyncExpiry(node) {
|
|
2767
|
-
node.heapIndex = this.pendingSyncExpiryHeap.length;
|
|
2768
|
-
this.pendingSyncExpiryHeap.push(node);
|
|
2769
|
-
let index = node.heapIndex;
|
|
2770
|
-
while (index > 0) {
|
|
2771
|
-
const parent = Math.floor((index - 1) / 2);
|
|
2772
|
-
if (this.pendingSyncExpiryHeap[parent].expiresAt <=
|
|
2773
|
-
this.pendingSyncExpiryHeap[index].expiresAt) {
|
|
2774
|
-
break;
|
|
2775
|
-
}
|
|
2776
|
-
this.swapPendingSyncExpiry(parent, index);
|
|
2777
|
-
index = parent;
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
|
-
removePendingSyncExpiry(node) {
|
|
2781
|
-
const index = node.heapIndex;
|
|
2782
|
-
if (index < 0 ||
|
|
2783
|
-
index >= this.pendingSyncExpiryHeap.length ||
|
|
2784
|
-
this.pendingSyncExpiryHeap[index] !== node) {
|
|
2785
|
-
return;
|
|
2786
|
-
}
|
|
2787
|
-
const last = this.pendingSyncExpiryHeap.pop();
|
|
2788
|
-
node.heapIndex = -1;
|
|
2789
|
-
if (index >= this.pendingSyncExpiryHeap.length) {
|
|
2790
|
-
return;
|
|
2791
|
-
}
|
|
2792
|
-
this.pendingSyncExpiryHeap[index] = last;
|
|
2793
|
-
last.heapIndex = index;
|
|
2794
|
-
let current = index;
|
|
2795
|
-
while (current > 0) {
|
|
2796
|
-
const parent = Math.floor((current - 1) / 2);
|
|
2797
|
-
if (this.pendingSyncExpiryHeap[parent].expiresAt <=
|
|
2798
|
-
this.pendingSyncExpiryHeap[current].expiresAt) {
|
|
2799
|
-
break;
|
|
2800
|
-
}
|
|
2801
|
-
this.swapPendingSyncExpiry(parent, current);
|
|
2802
|
-
current = parent;
|
|
2803
|
-
}
|
|
2804
|
-
for (;;) {
|
|
2805
|
-
const left = current * 2 + 1;
|
|
2806
|
-
const right = left + 1;
|
|
2807
|
-
let smallest = current;
|
|
2808
|
-
if (left < this.pendingSyncExpiryHeap.length &&
|
|
2809
|
-
this.pendingSyncExpiryHeap[left].expiresAt <
|
|
2810
|
-
this.pendingSyncExpiryHeap[smallest].expiresAt) {
|
|
2811
|
-
smallest = left;
|
|
2812
|
-
}
|
|
2813
|
-
if (right < this.pendingSyncExpiryHeap.length &&
|
|
2814
|
-
this.pendingSyncExpiryHeap[right].expiresAt <
|
|
2815
|
-
this.pendingSyncExpiryHeap[smallest].expiresAt) {
|
|
2816
|
-
smallest = right;
|
|
2817
|
-
}
|
|
2818
|
-
if (smallest === current) {
|
|
2819
|
-
break;
|
|
2820
|
-
}
|
|
2821
|
-
this.swapPendingSyncExpiry(current, smallest);
|
|
2822
|
-
current = smallest;
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
2825
|
-
removePendingSyncKeyExpiry(key) {
|
|
2826
|
-
const node = this.pendingSyncKeyExpiryNodes.get(key);
|
|
2827
|
-
if (!node) {
|
|
2828
|
-
return;
|
|
2829
|
-
}
|
|
2830
|
-
this.pendingSyncKeyExpiryNodes.delete(key);
|
|
2831
|
-
this.removePendingSyncExpiry(node);
|
|
2389
|
+
this.pendingSync.clearPendingSyncAdmissions(peer);
|
|
2832
2390
|
}
|
|
2833
2391
|
movePendingSyncKeyExpiryEarlier(key, expiresAt) {
|
|
2834
|
-
|
|
2835
|
-
if (current == null || current <= expiresAt) {
|
|
2836
|
-
return;
|
|
2837
|
-
}
|
|
2838
|
-
this.removePendingSyncKeyExpiry(key);
|
|
2839
|
-
this.syncInFlightQueueExpiresAt.set(key, expiresAt);
|
|
2840
|
-
const node = {
|
|
2841
|
-
kind: "key",
|
|
2842
|
-
key,
|
|
2843
|
-
expiresAt,
|
|
2844
|
-
heapIndex: -1,
|
|
2845
|
-
};
|
|
2846
|
-
this.pendingSyncKeyExpiryNodes.set(key, node);
|
|
2847
|
-
this.pushPendingSyncExpiry(node);
|
|
2848
|
-
this.schedulePendingSyncKeyExpiry();
|
|
2849
|
-
}
|
|
2850
|
-
removePendingSyncAdmissionExpiry(reservation) {
|
|
2851
|
-
const node = this.pendingSyncAdmissionExpiryNodes.get(reservation);
|
|
2852
|
-
if (!node) {
|
|
2853
|
-
return;
|
|
2854
|
-
}
|
|
2855
|
-
this.pendingSyncAdmissionExpiryNodes.delete(reservation);
|
|
2856
|
-
this.removePendingSyncExpiry(node);
|
|
2392
|
+
this.pendingSync.movePendingSyncKeyExpiryEarlier(key, expiresAt);
|
|
2857
2393
|
}
|
|
2858
2394
|
expirePendingSyncKeys(now = Date.now()) {
|
|
2859
|
-
|
|
2860
|
-
const node = this.pendingSyncExpiryHeap[0];
|
|
2861
|
-
if (!node || node.expiresAt > now) {
|
|
2862
|
-
break;
|
|
2863
|
-
}
|
|
2864
|
-
this.removePendingSyncExpiry(node);
|
|
2865
|
-
if (node.kind === "key") {
|
|
2866
|
-
if (this.pendingSyncKeyExpiryNodes.get(node.key) !== node) {
|
|
2867
|
-
continue;
|
|
2868
|
-
}
|
|
2869
|
-
this.pendingSyncKeyExpiryNodes.delete(node.key);
|
|
2870
|
-
this.clearSyncProcessKey(node.key);
|
|
2871
|
-
}
|
|
2872
|
-
else {
|
|
2873
|
-
if (this.pendingSyncAdmissionExpiryNodes.get(node.reservation) !== node) {
|
|
2874
|
-
continue;
|
|
2875
|
-
}
|
|
2876
|
-
this.pendingSyncAdmissionExpiryNodes.delete(node.reservation);
|
|
2877
|
-
this.invalidatePendingSyncAdmission(node.reservation);
|
|
2878
|
-
}
|
|
2879
|
-
}
|
|
2395
|
+
this.pendingSync.expirePendingSyncKeys(now);
|
|
2880
2396
|
}
|
|
2881
2397
|
clearPendingSyncExpiryTimerIfIdle() {
|
|
2882
|
-
|
|
2883
|
-
this.syncInFlightQueueExpiryTimer != null) {
|
|
2884
|
-
clearTimeout(this.syncInFlightQueueExpiryTimer);
|
|
2885
|
-
this.syncInFlightQueueExpiryTimer = undefined;
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
schedulePendingSyncKeyExpiry() {
|
|
2889
|
-
if (this.syncInFlightQueueExpiryTimer != null ||
|
|
2890
|
-
this.pendingSyncExpiryHeap.length === 0) {
|
|
2891
|
-
return;
|
|
2892
|
-
}
|
|
2893
|
-
const expiresAt = this.pendingSyncExpiryHeap[0].expiresAt;
|
|
2894
|
-
this.syncInFlightQueueExpiryTimer = setTimeout(() => {
|
|
2895
|
-
this.syncInFlightQueueExpiryTimer = undefined;
|
|
2896
|
-
this.expirePendingSyncKeys();
|
|
2897
|
-
this.schedulePendingSyncKeyExpiry();
|
|
2898
|
-
}, Math.max(0, expiresAt - Date.now()));
|
|
2899
|
-
this.syncInFlightQueueExpiryTimer.unref?.();
|
|
2398
|
+
this.pendingSync.clearPendingSyncExpiryTimerIfIdle();
|
|
2900
2399
|
}
|
|
2901
2400
|
async queueSync(keys, from, options) {
|
|
2902
2401
|
if (this.closed === true || keys.length === 0) {
|
|
@@ -2911,9 +2410,9 @@ export class SimpleSyncronizer {
|
|
|
2911
2410
|
const peerClaimCount = this.syncInFlightQueueInverted.get(fromHash)?.size ?? 0;
|
|
2912
2411
|
let availableClaims = Math.max(0, Math.min(MAX_PENDING_SIMPLE_SYNC_KEYS_PER_PEER -
|
|
2913
2412
|
peerClaimCount -
|
|
2914
|
-
(this.pendingSyncAdmissionCountByPeer.get(fromHash) ?? 0), MAX_PENDING_SIMPLE_SYNC_KEYS_GLOBAL -
|
|
2915
|
-
this.pendingSyncClaimCount -
|
|
2916
|
-
this.pendingSyncAdmissionCount));
|
|
2413
|
+
(this.pendingSync.pendingSyncAdmissionCountByPeer.get(fromHash) ?? 0), MAX_PENDING_SIMPLE_SYNC_KEYS_GLOBAL -
|
|
2414
|
+
this.pendingSync.pendingSyncClaimCount -
|
|
2415
|
+
this.pendingSync.pendingSyncAdmissionCount));
|
|
2917
2416
|
const targetEpoch = this.getOrCreateSyncDispatchTargetEpoch(fromHash);
|
|
2918
2417
|
const ownershipLifecycleController = this.syncDispatchLifecycleController;
|
|
2919
2418
|
const isCapturedLifecycleActive = () => this.closed !== true &&
|
|
@@ -2934,7 +2433,7 @@ export class SimpleSyncronizer {
|
|
|
2934
2433
|
const keysToCheck = [];
|
|
2935
2434
|
const identitiesToCheck = [];
|
|
2936
2435
|
const seen = new Set();
|
|
2937
|
-
const pendingAdmissionIdentities = this.pendingSyncAdmissionIdentitiesByPeer.get(fromHash);
|
|
2436
|
+
const pendingAdmissionIdentities = this.pendingSync.pendingSyncAdmissionIdentitiesByPeer.get(fromHash);
|
|
2938
2437
|
// Default senders use 1,024-key messages. Capping examined input at the
|
|
2939
2438
|
// entire per-peer allowance prevents a duplicate-filled oversized vector
|
|
2940
2439
|
// from turning admission itself into unbounded work.
|
|
@@ -3351,7 +2850,7 @@ export class SimpleSyncronizer {
|
|
|
3351
2850
|
if (!isOpenLifecycleActive()) {
|
|
3352
2851
|
return;
|
|
3353
2852
|
}
|
|
3354
|
-
const expiresAt = this.syncInFlightQueueExpiresAt.get(key);
|
|
2853
|
+
const expiresAt = this.pendingSync.syncInFlightQueueExpiresAt.get(key);
|
|
3355
2854
|
if (expiresAt != null && expiresAt <= Date.now()) {
|
|
3356
2855
|
this.clearSyncProcessKey(key);
|
|
3357
2856
|
continue;
|
|
@@ -3364,7 +2863,7 @@ export class SimpleSyncronizer {
|
|
|
3364
2863
|
if (!value) {
|
|
3365
2864
|
continue;
|
|
3366
2865
|
}
|
|
3367
|
-
const currentExpiresAt = this.syncInFlightQueueExpiresAt.get(key);
|
|
2866
|
+
const currentExpiresAt = this.pendingSync.syncInFlightQueueExpiresAt.get(key);
|
|
3368
2867
|
if (currentExpiresAt != null && currentExpiresAt <= Date.now()) {
|
|
3369
2868
|
this.clearSyncProcessKey(key);
|
|
3370
2869
|
continue;
|
|
@@ -3374,8 +2873,7 @@ export class SimpleSyncronizer {
|
|
|
3374
2873
|
this.clearSyncProcessKey(key);
|
|
3375
2874
|
continue;
|
|
3376
2875
|
}
|
|
3377
|
-
const cursor =
|
|
3378
|
-
value.length;
|
|
2876
|
+
const cursor = this.pendingSync.getRoundRobinCursor(key) % value.length;
|
|
3379
2877
|
const candidate = value[cursor];
|
|
3380
2878
|
const publicKeyHash = candidate.hashcode();
|
|
3381
2879
|
const inflightTimestamp = this.syncInFlight
|
|
@@ -3394,7 +2892,7 @@ export class SimpleSyncronizer {
|
|
|
3394
2892
|
}
|
|
3395
2893
|
request.hashes.push(key);
|
|
3396
2894
|
if (value.length > 1) {
|
|
3397
|
-
this.
|
|
2895
|
+
this.pendingSync.setRoundRobinCursor(key, (cursor + 1) % value.length);
|
|
3398
2896
|
}
|
|
3399
2897
|
}
|
|
3400
2898
|
}
|
|
@@ -3463,28 +2961,9 @@ export class SimpleSyncronizer {
|
|
|
3463
2961
|
this.syncDispatchLifecycleController.abort();
|
|
3464
2962
|
this.syncDispatchTargetEpochs.clear();
|
|
3465
2963
|
this.clearPendingSyncAdmissions();
|
|
3466
|
-
this.syncInFlightQueue.clear();
|
|
3467
|
-
this.syncInFlightQueueInverted.clear();
|
|
3468
2964
|
this.syncInFlightRetryIterator = undefined;
|
|
3469
2965
|
this.syncInFlightRetryRemaining = 0;
|
|
3470
|
-
this.
|
|
3471
|
-
this.pendingSyncExpiryHeap.length = 0;
|
|
3472
|
-
this.pendingSyncKeyExpiryNodes.clear();
|
|
3473
|
-
this.pendingSyncAdmissionExpiryNodes.clear();
|
|
3474
|
-
this.syncInFlightQueueClaimants.clear();
|
|
3475
|
-
this.syncInFlightQueueClaimantIndexes.clear();
|
|
3476
|
-
this.syncInFlightQueueRoundRobinCursor.clear();
|
|
3477
|
-
this.syncInFlightQueuedCoordinates.clear();
|
|
3478
|
-
this.syncInFlightQueuedHashByCoordinate.clear();
|
|
3479
|
-
this.syncInFlightQueuedCoordinatesByHash.clear();
|
|
3480
|
-
this.syncInFlightQueuedCoordinateRefreshIterator = undefined;
|
|
3481
|
-
this.pendingSyncClaimCount = 0;
|
|
3482
|
-
if (this.syncInFlightQueueExpiryTimer != null) {
|
|
3483
|
-
clearTimeout(this.syncInFlightQueueExpiryTimer);
|
|
3484
|
-
this.syncInFlightQueueExpiryTimer = undefined;
|
|
3485
|
-
}
|
|
3486
|
-
this.syncInFlight.clear();
|
|
3487
|
-
this.syncInFlightTargetsByKey.clear();
|
|
2966
|
+
this.pendingSync.clearForClose();
|
|
3488
2967
|
this.recentlySentExchangeHeads.clear();
|
|
3489
2968
|
this.clearPendingMaybeSyncResponses();
|
|
3490
2969
|
for (const sessionId of [...this.repairSessions.keys()]) {
|
|
@@ -3529,202 +3008,31 @@ export class SimpleSyncronizer {
|
|
|
3529
3008
|
return this.hasSyncProcessState() || this.repairSessions.size > 0;
|
|
3530
3009
|
}
|
|
3531
3010
|
hasSyncProcessState() {
|
|
3532
|
-
return
|
|
3533
|
-
this.syncInFlightQueueInverted.size > 0 ||
|
|
3534
|
-
this.pendingSyncAdmissionCount > 0 ||
|
|
3535
|
-
this.syncInFlight.size > 0);
|
|
3011
|
+
return this.pendingSync.hasSyncProcessState();
|
|
3536
3012
|
}
|
|
3537
3013
|
clearSyncProcessKey(key) {
|
|
3538
|
-
|
|
3539
|
-
if (inflight) {
|
|
3540
|
-
for (const peer of inflight) {
|
|
3541
|
-
const map = this.syncInFlightQueueInverted.get(peer.hashcode());
|
|
3542
|
-
if (map) {
|
|
3543
|
-
map.delete(key);
|
|
3544
|
-
if (map.size === 0) {
|
|
3545
|
-
this.syncInFlightQueueInverted.delete(peer.hashcode());
|
|
3546
|
-
}
|
|
3547
|
-
}
|
|
3548
|
-
}
|
|
3549
|
-
const trackedClaimants = this.syncInFlightQueueClaimants.get(key);
|
|
3550
|
-
this.pendingSyncClaimCount = Math.max(0, this.pendingSyncClaimCount -
|
|
3551
|
-
(trackedClaimants?.size ?? inflight.length));
|
|
3552
|
-
this.syncInFlightQueue.delete(key);
|
|
3553
|
-
}
|
|
3554
|
-
this.syncInFlightQueueClaimants.delete(key);
|
|
3555
|
-
this.syncInFlightQueueClaimantIndexes.delete(key);
|
|
3556
|
-
this.syncInFlightQueueRoundRobinCursor.delete(key);
|
|
3557
|
-
if (typeof key === "bigint") {
|
|
3558
|
-
this.removeQueuedSyncCoordinateAlias(key);
|
|
3559
|
-
}
|
|
3560
|
-
this.removePendingSyncKeyExpiry(key);
|
|
3561
|
-
this.syncInFlightQueueExpiresAt.delete(key);
|
|
3562
|
-
this.clearPendingSyncExpiryTimerIfIdle();
|
|
3563
|
-
this.clearSyncInFlightKey(key);
|
|
3014
|
+
this.pendingSync.clearSyncProcessKey(key);
|
|
3564
3015
|
}
|
|
3565
3016
|
removePendingSyncClaim(key, peer) {
|
|
3566
|
-
|
|
3567
|
-
if (!inflight) {
|
|
3568
|
-
return;
|
|
3569
|
-
}
|
|
3570
|
-
let claimants = this.syncInFlightQueueClaimants.get(key);
|
|
3571
|
-
let claimantIndexes = this.syncInFlightQueueClaimantIndexes.get(key);
|
|
3572
|
-
if (!claimants || !claimantIndexes) {
|
|
3573
|
-
claimants = new Set();
|
|
3574
|
-
claimantIndexes = new Map();
|
|
3575
|
-
for (let index = 0; index < inflight.length; index += 1) {
|
|
3576
|
-
const claimant = inflight[index].hashcode();
|
|
3577
|
-
claimants.add(claimant);
|
|
3578
|
-
claimantIndexes.set(claimant, index);
|
|
3579
|
-
}
|
|
3580
|
-
if (!this.syncInFlightQueueClaimants.has(key)) {
|
|
3581
|
-
this.pendingSyncClaimCount += claimants.size;
|
|
3582
|
-
}
|
|
3583
|
-
this.syncInFlightQueueClaimants.set(key, claimants);
|
|
3584
|
-
this.syncInFlightQueueClaimantIndexes.set(key, claimantIndexes);
|
|
3585
|
-
}
|
|
3586
|
-
const index = claimantIndexes.get(peer);
|
|
3587
|
-
if (index == null) {
|
|
3588
|
-
return;
|
|
3589
|
-
}
|
|
3590
|
-
const lastIndex = inflight.length - 1;
|
|
3591
|
-
if (index !== lastIndex) {
|
|
3592
|
-
const lastClaimant = inflight[lastIndex];
|
|
3593
|
-
const lastClaimantHash = lastClaimant.hashcode();
|
|
3594
|
-
inflight[index] = lastClaimant;
|
|
3595
|
-
claimantIndexes.set(lastClaimantHash, index);
|
|
3596
|
-
}
|
|
3597
|
-
inflight.pop();
|
|
3598
|
-
claimantIndexes.delete(peer);
|
|
3599
|
-
claimants.delete(peer);
|
|
3600
|
-
this.pendingSyncClaimCount = Math.max(0, this.pendingSyncClaimCount - 1);
|
|
3601
|
-
const inverted = this.syncInFlightQueueInverted.get(peer);
|
|
3602
|
-
inverted?.delete(key);
|
|
3603
|
-
if (inverted?.size === 0) {
|
|
3604
|
-
this.syncInFlightQueueInverted.delete(peer);
|
|
3605
|
-
}
|
|
3606
|
-
if (inflight.length > 0) {
|
|
3607
|
-
const cursor = this.syncInFlightQueueRoundRobinCursor.get(key) ?? 0;
|
|
3608
|
-
this.syncInFlightQueueRoundRobinCursor.set(key, cursor === lastIndex
|
|
3609
|
-
? index % inflight.length
|
|
3610
|
-
: cursor % inflight.length);
|
|
3611
|
-
return;
|
|
3612
|
-
}
|
|
3613
|
-
this.syncInFlightQueue.delete(key);
|
|
3614
|
-
this.syncInFlightQueueClaimants.delete(key);
|
|
3615
|
-
this.syncInFlightQueueClaimantIndexes.delete(key);
|
|
3616
|
-
this.syncInFlightQueueRoundRobinCursor.delete(key);
|
|
3617
|
-
if (typeof key === "bigint") {
|
|
3618
|
-
this.removeQueuedSyncCoordinateAlias(key);
|
|
3619
|
-
}
|
|
3620
|
-
this.removePendingSyncKeyExpiry(key);
|
|
3621
|
-
this.syncInFlightQueueExpiresAt.delete(key);
|
|
3622
|
-
this.clearSyncInFlightKey(key);
|
|
3623
|
-
this.clearPendingSyncExpiryTimerIfIdle();
|
|
3017
|
+
this.pendingSync.removePendingSyncClaim(key, peer);
|
|
3624
3018
|
}
|
|
3625
3019
|
removeSyncInFlightTargetKey(peer, key) {
|
|
3626
|
-
|
|
3627
|
-
if (!map?.delete(key)) {
|
|
3628
|
-
return;
|
|
3629
|
-
}
|
|
3630
|
-
if (map.size === 0) {
|
|
3631
|
-
this.syncInFlight.delete(peer);
|
|
3632
|
-
}
|
|
3633
|
-
const targets = this.syncInFlightTargetsByKey.get(key);
|
|
3634
|
-
targets?.delete(peer);
|
|
3635
|
-
if (targets?.size === 0) {
|
|
3636
|
-
this.syncInFlightTargetsByKey.delete(key);
|
|
3637
|
-
}
|
|
3020
|
+
this.pendingSync.removeSyncInFlightTargetKey(peer, key);
|
|
3638
3021
|
}
|
|
3639
3022
|
setSyncInFlightTargetKey(peer, key, timestamp) {
|
|
3640
|
-
|
|
3641
|
-
if (!map) {
|
|
3642
|
-
map = new Map();
|
|
3643
|
-
this.syncInFlight.set(peer, map);
|
|
3644
|
-
}
|
|
3645
|
-
const existing = map.get(key);
|
|
3646
|
-
if (!existing || existing.timestamp < timestamp) {
|
|
3647
|
-
map.set(key, { timestamp });
|
|
3648
|
-
}
|
|
3649
|
-
let targets = this.syncInFlightTargetsByKey.get(key);
|
|
3650
|
-
if (!targets) {
|
|
3651
|
-
targets = new Set();
|
|
3652
|
-
this.syncInFlightTargetsByKey.set(key, targets);
|
|
3653
|
-
}
|
|
3654
|
-
targets.add(peer);
|
|
3023
|
+
this.pendingSync.setSyncInFlightTargetKey(peer, key, timestamp);
|
|
3655
3024
|
}
|
|
3656
3025
|
clearSyncInFlightTarget(peer) {
|
|
3657
|
-
|
|
3658
|
-
if (!map) {
|
|
3659
|
-
return;
|
|
3660
|
-
}
|
|
3661
|
-
for (const key of map.keys()) {
|
|
3662
|
-
const targets = this.syncInFlightTargetsByKey.get(key);
|
|
3663
|
-
targets?.delete(peer);
|
|
3664
|
-
if (targets?.size === 0) {
|
|
3665
|
-
this.syncInFlightTargetsByKey.delete(key);
|
|
3666
|
-
}
|
|
3667
|
-
}
|
|
3668
|
-
this.syncInFlight.delete(peer);
|
|
3669
|
-
}
|
|
3670
|
-
clearSyncInFlightKey(key) {
|
|
3671
|
-
const targets = this.syncInFlightTargetsByKey.get(key);
|
|
3672
|
-
if (!targets) {
|
|
3673
|
-
// Defensive compatibility for tests or integrations that seed the public
|
|
3674
|
-
// syncInFlight map directly. Internal writes always populate the index.
|
|
3675
|
-
for (const [peer, map] of this.syncInFlight) {
|
|
3676
|
-
if (map.has(key)) {
|
|
3677
|
-
this.removeSyncInFlightTargetKey(peer, key);
|
|
3678
|
-
}
|
|
3679
|
-
}
|
|
3680
|
-
return;
|
|
3681
|
-
}
|
|
3682
|
-
for (const peer of [...targets]) {
|
|
3683
|
-
this.removeSyncInFlightTargetKey(peer, key);
|
|
3684
|
-
}
|
|
3685
|
-
}
|
|
3686
|
-
forEachKnownAlias(hash, callback) {
|
|
3687
|
-
callback(hash);
|
|
3688
|
-
for (const coordinate of [
|
|
3689
|
-
...(this.syncInFlightQueuedCoordinatesByHash.get(hash) ?? []),
|
|
3690
|
-
]) {
|
|
3691
|
-
callback(coordinate);
|
|
3692
|
-
}
|
|
3693
|
-
}
|
|
3694
|
-
clearSyncInFlightForPeer(publicKeyHash, hash) {
|
|
3695
|
-
const map = this.syncInFlight.get(publicKeyHash);
|
|
3696
|
-
if (!map) {
|
|
3697
|
-
return;
|
|
3698
|
-
}
|
|
3699
|
-
this.refreshQueuedSyncCoordinateAliases();
|
|
3700
|
-
this.forEachKnownAlias(hash, (key) => this.removeSyncInFlightTargetKey(publicKeyHash, key));
|
|
3026
|
+
this.pendingSync.clearSyncInFlightTarget(peer);
|
|
3701
3027
|
}
|
|
3702
3028
|
clearSyncInFlightForPeerHashes(publicKeyHash, hashes) {
|
|
3703
|
-
|
|
3704
|
-
if (!map || hashes.length === 0) {
|
|
3705
|
-
return;
|
|
3706
|
-
}
|
|
3707
|
-
this.refreshQueuedSyncCoordinateAliases();
|
|
3708
|
-
for (const hash of hashes) {
|
|
3709
|
-
this.forEachKnownAlias(hash, (key) => this.removeSyncInFlightTargetKey(publicKeyHash, key));
|
|
3710
|
-
}
|
|
3029
|
+
this.pendingSync.clearSyncInFlightForPeerHashes(publicKeyHash, hashes);
|
|
3711
3030
|
}
|
|
3712
3031
|
clearSyncProcess(hash) {
|
|
3713
|
-
this.
|
|
3714
|
-
this.forEachKnownAlias(hash, (key) => this.clearSyncProcessKey(key));
|
|
3032
|
+
this.pendingSync.clearSyncProcess(hash);
|
|
3715
3033
|
}
|
|
3716
3034
|
clearSyncProcesses(hashes) {
|
|
3717
|
-
|
|
3718
|
-
return;
|
|
3719
|
-
}
|
|
3720
|
-
this.refreshQueuedSyncCoordinateAliases();
|
|
3721
|
-
const keys = new Set();
|
|
3722
|
-
for (const hash of hashes) {
|
|
3723
|
-
this.forEachKnownAlias(hash, (key) => keys.add(key));
|
|
3724
|
-
}
|
|
3725
|
-
for (const key of keys) {
|
|
3726
|
-
this.clearSyncProcessKey(key);
|
|
3727
|
-
}
|
|
3035
|
+
this.pendingSync.clearSyncProcesses(hashes);
|
|
3728
3036
|
}
|
|
3729
3037
|
onPeerDisconnected(key) {
|
|
3730
3038
|
const publicKeyHash = typeof key === "string" ? key : key.hashcode();
|
|
@@ -3734,7 +3042,7 @@ export class SimpleSyncronizer {
|
|
|
3734
3042
|
this.syncDispatchTargetEpochs.delete(publicKeyHash);
|
|
3735
3043
|
this.clearPendingSyncAdmissions(publicKeyHash);
|
|
3736
3044
|
for (const targetLifecycle of [
|
|
3737
|
-
...(this.
|
|
3045
|
+
...(this.syncDispatchRegistry.activeTargets.get(publicKeyHash) ?? []),
|
|
3738
3046
|
]) {
|
|
3739
3047
|
if (targetLifecycle.lifecycle.abortAllOnTargetDisconnect) {
|
|
3740
3048
|
this.abortSyncDispatchLifecycle(targetLifecycle.lifecycle, new Error("sync target disconnected"));
|
|
@@ -3746,13 +3054,12 @@ export class SimpleSyncronizer {
|
|
|
3746
3054
|
this.clearSyncInFlightTarget(publicKeyHash);
|
|
3747
3055
|
this.recentlySentExchangeHeads.delete(publicKeyHash);
|
|
3748
3056
|
this.clearPendingMaybeSyncResponses(publicKeyHash);
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
}
|
|
3057
|
+
this.pendingSync.removeClaimsForPeer(publicKeyHash);
|
|
3058
|
+
// Storage lookups and response ships for this peer may never settle
|
|
3059
|
+
// (the resolvers are not universally abortable). Detach the slot row so
|
|
3060
|
+
// the peer's quota returns immediately; the captured closures settle
|
|
3061
|
+
// against the detached row without touching a successor's quota.
|
|
3062
|
+
this.detachSyncResponseSlotRow(publicKeyHash);
|
|
3756
3063
|
}
|
|
3757
3064
|
get pending() {
|
|
3758
3065
|
return this.syncInFlightQueue.size;
|