@peerbit/shared-log 13.0.23 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +238 -95
- package/dist/src/index.js.map +1 -1
- package/package.json +12 -12
- package/src/index.ts +330 -148
package/src/index.ts
CHANGED
|
@@ -495,6 +495,7 @@ const RECALCULATE_PARTICIPATION_MIN_RELATIVE_CHANGE = 0.01;
|
|
|
495
495
|
const RECALCULATE_PARTICIPATION_MIN_RELATIVE_CHANGE_WITH_CPU_LIMIT = 0.005;
|
|
496
496
|
const RECALCULATE_PARTICIPATION_MIN_RELATIVE_CHANGE_WITH_MEMORY_LIMIT = 0.001;
|
|
497
497
|
const RECALCULATE_PARTICIPATION_RELATIVE_DENOMINATOR_FLOOR = 1e-3;
|
|
498
|
+
const TOPIC_SUBSCRIBERS_CACHE_TTL_MS = 250;
|
|
498
499
|
const ADAPTIVE_REBALANCE_IDLE_INTERVAL_MULTIPLIER = 5;
|
|
499
500
|
const ADAPTIVE_REBALANCE_MIN_IDLE_AFTER_LOCAL_APPEND_MS = 10_000;
|
|
500
501
|
|
|
@@ -752,6 +753,10 @@ export class SharedLog<
|
|
|
752
753
|
private _repairSweepRunning!: boolean;
|
|
753
754
|
private _repairSweepForceFreshPending!: boolean;
|
|
754
755
|
private _repairSweepAddedPeersPending!: Set<string>;
|
|
756
|
+
private _topicSubscribersCache!: Map<
|
|
757
|
+
string,
|
|
758
|
+
{ expiresAt: number; keys: PublicSignKey[] }
|
|
759
|
+
>;
|
|
755
760
|
|
|
756
761
|
// regular distribution checks
|
|
757
762
|
private distributeQueue?: PQueue;
|
|
@@ -1364,14 +1369,26 @@ export class SharedLog<
|
|
|
1364
1369
|
private async _getTopicSubscribers(
|
|
1365
1370
|
topic: string,
|
|
1366
1371
|
): Promise<PublicSignKey[] | undefined> {
|
|
1372
|
+
const cached = this._topicSubscribersCache.get(topic);
|
|
1373
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
1374
|
+
return cached.keys.slice();
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1367
1377
|
const maxPeers = 64;
|
|
1378
|
+
const cache = (keys: PublicSignKey[]) => {
|
|
1379
|
+
this._topicSubscribersCache.set(topic, {
|
|
1380
|
+
expiresAt: Date.now() + TOPIC_SUBSCRIBERS_CACHE_TTL_MS,
|
|
1381
|
+
keys,
|
|
1382
|
+
});
|
|
1383
|
+
return keys.slice();
|
|
1384
|
+
};
|
|
1368
1385
|
|
|
1369
1386
|
// Prefer the bounded peer set we already know from the fanout overlay.
|
|
1370
1387
|
if (this._fanoutChannel && (topic === this.topic || topic === this.rpc.topic)) {
|
|
1371
1388
|
const hashes = this._fanoutChannel
|
|
1372
1389
|
.getPeerHashes({ includeSelf: false })
|
|
1373
1390
|
.slice(0, maxPeers);
|
|
1374
|
-
if (hashes.length === 0) return [];
|
|
1391
|
+
if (hashes.length === 0) return cache([]);
|
|
1375
1392
|
|
|
1376
1393
|
const keys = await Promise.all(
|
|
1377
1394
|
hashes.map((hash) => this._resolvePublicKeyFromHash(hash)),
|
|
@@ -1387,7 +1404,7 @@ export class SharedLog<
|
|
|
1387
1404
|
seen.add(hash);
|
|
1388
1405
|
uniqueKeys.push(key);
|
|
1389
1406
|
}
|
|
1390
|
-
return uniqueKeys;
|
|
1407
|
+
return cache(uniqueKeys);
|
|
1391
1408
|
}
|
|
1392
1409
|
|
|
1393
1410
|
const selfHash = this.node.identity.publicKey.hashcode();
|
|
@@ -1444,7 +1461,7 @@ export class SharedLog<
|
|
|
1444
1461
|
}
|
|
1445
1462
|
}
|
|
1446
1463
|
|
|
1447
|
-
if (hashes.length === 0) return [];
|
|
1464
|
+
if (hashes.length === 0) return cache([]);
|
|
1448
1465
|
|
|
1449
1466
|
const uniqueHashes: string[] = [];
|
|
1450
1467
|
const seen = new Set<string>();
|
|
@@ -1465,7 +1482,18 @@ export class SharedLog<
|
|
|
1465
1482
|
if (hash === selfHash) continue;
|
|
1466
1483
|
uniqueKeys.push(key);
|
|
1467
1484
|
}
|
|
1468
|
-
return uniqueKeys;
|
|
1485
|
+
return cache(uniqueKeys);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
private invalidateTopicSubscribersCache(...topics: (string | undefined)[]) {
|
|
1489
|
+
for (const topic of topics) {
|
|
1490
|
+
if (!topic) continue;
|
|
1491
|
+
this._topicSubscribersCache.delete(topic);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
private invalidateSharedLogTopicSubscribersCache() {
|
|
1496
|
+
this.invalidateTopicSubscribersCache(this.topic, this.rpc.topic);
|
|
1469
1497
|
}
|
|
1470
1498
|
|
|
1471
1499
|
// @deprecated
|
|
@@ -2639,15 +2667,16 @@ export class SharedLog<
|
|
|
2639
2667
|
|
|
2640
2668
|
const iterator = this.entryCoordinatesIndex.iterate({});
|
|
2641
2669
|
try {
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2670
|
+
while (!this.closed && !iterator.done()) {
|
|
2671
|
+
const entries = await iterator.next(REPAIR_SWEEP_ENTRY_BATCH_SIZE);
|
|
2672
|
+
for (const entry of entries) {
|
|
2673
|
+
const entryReplicated = entry.value;
|
|
2674
|
+
const knownPeers = this._gidPeersHistory.get(entryReplicated.gid);
|
|
2675
|
+
const currentPeers = await this.findLeaders(
|
|
2676
|
+
entryReplicated.coordinates,
|
|
2677
|
+
entryReplicated,
|
|
2678
|
+
{ roleAge: 0 },
|
|
2679
|
+
);
|
|
2651
2680
|
if (forceFreshDelivery) {
|
|
2652
2681
|
for (const [currentPeer] of currentPeers) {
|
|
2653
2682
|
if (currentPeer === this.node.identity.publicKey.hashcode()) {
|
|
@@ -2656,14 +2685,14 @@ export class SharedLog<
|
|
|
2656
2685
|
queueEntryForTarget(currentPeer, entryReplicated);
|
|
2657
2686
|
}
|
|
2658
2687
|
}
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2688
|
+
if (addedPeers.size > 0) {
|
|
2689
|
+
for (const peer of addedPeers) {
|
|
2690
|
+
if (currentPeers.has(peer) && !knownPeers?.has(peer)) {
|
|
2691
|
+
queueEntryForTarget(peer, entryReplicated);
|
|
2692
|
+
}
|
|
2663
2693
|
}
|
|
2664
2694
|
}
|
|
2665
2695
|
}
|
|
2666
|
-
}
|
|
2667
2696
|
}
|
|
2668
2697
|
} finally {
|
|
2669
2698
|
await iterator.close();
|
|
@@ -2934,6 +2963,7 @@ export class SharedLog<
|
|
|
2934
2963
|
this._repairSweepRunning = false;
|
|
2935
2964
|
this._repairSweepForceFreshPending = false;
|
|
2936
2965
|
this._repairSweepAddedPeersPending = new Set();
|
|
2966
|
+
this._topicSubscribersCache = new Map();
|
|
2937
2967
|
this.coordinateToHash = new Cache<string>({ max: 1e6, ttl: 1e4 });
|
|
2938
2968
|
this.recentlyRebalanced = new Cache<string>({ max: 1e4, ttl: 1e5 });
|
|
2939
2969
|
|
|
@@ -3049,6 +3079,18 @@ export class SharedLog<
|
|
|
3049
3079
|
})) ?? []
|
|
3050
3080
|
);
|
|
3051
3081
|
},
|
|
3082
|
+
watchProviders: fanoutService
|
|
3083
|
+
? (cid, opts) =>
|
|
3084
|
+
fanoutService.watchProviders(blockProviderNamespace(cid), {
|
|
3085
|
+
signal: opts.signal,
|
|
3086
|
+
want: 8,
|
|
3087
|
+
ttlMs: 10_000,
|
|
3088
|
+
renewIntervalMs: 5_000,
|
|
3089
|
+
bootstrapMaxPeers: 2,
|
|
3090
|
+
onProviders: (providers) =>
|
|
3091
|
+
opts.onProviders(providers.map((provider) => provider.hash)),
|
|
3092
|
+
})
|
|
3093
|
+
: undefined,
|
|
3052
3094
|
onPut: async (cid) => {
|
|
3053
3095
|
// Best-effort directory announce for "get without remote.from" workflows.
|
|
3054
3096
|
try {
|
|
@@ -3958,39 +4000,40 @@ export class SharedLog<
|
|
|
3958
4000
|
this.coordinateToHash.clear();
|
|
3959
4001
|
this.recentlyRebalanced.clear();
|
|
3960
4002
|
this.uniqueReplicators.clear();
|
|
3961
|
-
|
|
4003
|
+
this._topicSubscribersCache.clear();
|
|
4004
|
+
this._closeController.abort();
|
|
3962
4005
|
|
|
3963
|
-
|
|
3964
|
-
|
|
4006
|
+
clearInterval(this.interval);
|
|
4007
|
+
this.stopReplicatorLivenessSweep();
|
|
3965
4008
|
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
4009
|
+
this.node.services.pubsub.removeEventListener(
|
|
4010
|
+
"subscribe",
|
|
4011
|
+
this._onSubscriptionFn,
|
|
3969
4012
|
);
|
|
3970
4013
|
|
|
3971
4014
|
this.node.services.pubsub.removeEventListener(
|
|
3972
4015
|
"unsubscribe",
|
|
3973
4016
|
this._onUnsubscriptionFn,
|
|
3974
4017
|
);
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
4018
|
+
for (const timer of this._repairRetryTimers) {
|
|
4019
|
+
clearTimeout(timer);
|
|
4020
|
+
}
|
|
4021
|
+
this._repairRetryTimers.clear();
|
|
4022
|
+
this._recentRepairDispatch.clear();
|
|
4023
|
+
this._repairSweepRunning = false;
|
|
4024
|
+
this._repairSweepForceFreshPending = false;
|
|
4025
|
+
this._repairSweepAddedPeersPending.clear();
|
|
3983
4026
|
|
|
3984
4027
|
for (const [_k, v] of this._pendingDeletes) {
|
|
3985
4028
|
v.clear();
|
|
3986
4029
|
v.promise.resolve(); // TODO or reject?
|
|
3987
4030
|
}
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4031
|
+
for (const [_k, v] of this._pendingIHave) {
|
|
4032
|
+
v.clear();
|
|
4033
|
+
}
|
|
4034
|
+
for (const [_k, v] of this._checkedPruneRetries) {
|
|
4035
|
+
if (v.timer) clearTimeout(v.timer);
|
|
4036
|
+
}
|
|
3994
4037
|
|
|
3995
4038
|
await this.remoteBlocks.stop();
|
|
3996
4039
|
this._pendingDeletes.clear();
|
|
@@ -4846,6 +4889,23 @@ export class SharedLog<
|
|
|
4846
4889
|
options?.replicate &&
|
|
4847
4890
|
typeof options.replicate !== "boolean" &&
|
|
4848
4891
|
options.replicate.assumeSynced;
|
|
4892
|
+
const seedAssumeSyncedPeerHistory = async (entry: Entry<T>) => {
|
|
4893
|
+
if (!assumeSynced) {
|
|
4894
|
+
return;
|
|
4895
|
+
}
|
|
4896
|
+
|
|
4897
|
+
const minReplicas = decodeReplicas(entry).getValue(this);
|
|
4898
|
+
const leaders = await this.findLeaders(
|
|
4899
|
+
await this.createCoordinates(entry, minReplicas),
|
|
4900
|
+
entry,
|
|
4901
|
+
{
|
|
4902
|
+
roleAge: 0,
|
|
4903
|
+
persist: false,
|
|
4904
|
+
},
|
|
4905
|
+
);
|
|
4906
|
+
|
|
4907
|
+
this.addPeersToGidPeerHistory(entry.meta.gid, leaders.keys());
|
|
4908
|
+
};
|
|
4849
4909
|
const persistCoordinate = async (entry: Entry<T>) => {
|
|
4850
4910
|
const minReplicas = decodeReplicas(entry).getValue(this);
|
|
4851
4911
|
const leaders = await this.findLeaders(
|
|
@@ -4887,6 +4947,12 @@ export class SharedLog<
|
|
|
4887
4947
|
if (options?.replicate) {
|
|
4888
4948
|
let messageToSend: AddedReplicationSegmentMessage | undefined = undefined;
|
|
4889
4949
|
|
|
4950
|
+
if (assumeSynced) {
|
|
4951
|
+
for (const entry of entriesToReplicate) {
|
|
4952
|
+
await seedAssumeSyncedPeerHistory(entry);
|
|
4953
|
+
}
|
|
4954
|
+
}
|
|
4955
|
+
|
|
4890
4956
|
await this.replicate(entriesToReplicate, {
|
|
4891
4957
|
rebalance: assumeSynced ? false : true,
|
|
4892
4958
|
checkDuplicates: true,
|
|
@@ -5390,6 +5456,7 @@ export class SharedLog<
|
|
|
5390
5456
|
entry: Entry<T> | EntryReplicated<R> | ShallowEntry,
|
|
5391
5457
|
options?: {
|
|
5392
5458
|
roleAge?: number;
|
|
5459
|
+
candidates?: Iterable<string>;
|
|
5393
5460
|
onLeader?: (key: string) => void;
|
|
5394
5461
|
// persist even if not leader
|
|
5395
5462
|
persist?:
|
|
@@ -5433,6 +5500,7 @@ export class SharedLog<
|
|
|
5433
5500
|
},
|
|
5434
5501
|
options?: {
|
|
5435
5502
|
roleAge?: number;
|
|
5503
|
+
candidates?: Iterable<string>;
|
|
5436
5504
|
onLeader?: (key: string) => void;
|
|
5437
5505
|
// persist even if not leader
|
|
5438
5506
|
persist?:
|
|
@@ -5458,6 +5526,7 @@ export class SharedLog<
|
|
|
5458
5526
|
cursors: NumberFromType<R>[],
|
|
5459
5527
|
options?: {
|
|
5460
5528
|
roleAge?: number;
|
|
5529
|
+
candidates?: Iterable<string>;
|
|
5461
5530
|
},
|
|
5462
5531
|
): Promise<Map<string, { intersecting: boolean }>> {
|
|
5463
5532
|
const roleAge = options?.roleAge ?? (await this.getDefaultMinRoleAge()); // TODO -500 as is added so that i f someone else is just as new as us, then we treat them as mature as us. without -500 we might be slower syncing if two nodes starts almost at the same time
|
|
@@ -5467,44 +5536,48 @@ export class SharedLog<
|
|
|
5467
5536
|
// If it is still warming up (for example, only contains self), supplement with
|
|
5468
5537
|
// current subscribers until we have enough candidates for this decision.
|
|
5469
5538
|
let peerFilter: Set<string> | undefined = undefined;
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5539
|
+
if (options?.candidates) {
|
|
5540
|
+
peerFilter = new Set(options.candidates);
|
|
5541
|
+
} else {
|
|
5542
|
+
const selfReplicating = await this.isReplicating();
|
|
5543
|
+
if (this.uniqueReplicators.size > 0) {
|
|
5544
|
+
peerFilter = new Set(this.uniqueReplicators);
|
|
5545
|
+
if (selfReplicating) {
|
|
5546
|
+
peerFilter.add(selfHash);
|
|
5547
|
+
} else {
|
|
5548
|
+
peerFilter.delete(selfHash);
|
|
5549
|
+
}
|
|
5478
5550
|
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5551
|
+
try {
|
|
5552
|
+
const subscribers = await this._getTopicSubscribers(this.topic);
|
|
5553
|
+
if (subscribers && subscribers.length > 0) {
|
|
5554
|
+
for (const subscriber of subscribers) {
|
|
5555
|
+
peerFilter.add(subscriber.hashcode());
|
|
5556
|
+
}
|
|
5557
|
+
if (selfReplicating) {
|
|
5558
|
+
peerFilter.add(selfHash);
|
|
5559
|
+
} else {
|
|
5560
|
+
peerFilter.delete(selfHash);
|
|
5561
|
+
}
|
|
5489
5562
|
}
|
|
5563
|
+
} catch {
|
|
5564
|
+
// Best-effort only; keep current peerFilter.
|
|
5490
5565
|
}
|
|
5491
|
-
}
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
} else {
|
|
5503
|
-
peerFilter.delete(selfHash);
|
|
5566
|
+
} else {
|
|
5567
|
+
try {
|
|
5568
|
+
const subscribers =
|
|
5569
|
+
(await this._getTopicSubscribers(this.topic)) ?? undefined;
|
|
5570
|
+
if (subscribers && subscribers.length > 0) {
|
|
5571
|
+
peerFilter = new Set(subscribers.map((key) => key.hashcode()));
|
|
5572
|
+
if (selfReplicating) {
|
|
5573
|
+
peerFilter.add(selfHash);
|
|
5574
|
+
} else {
|
|
5575
|
+
peerFilter.delete(selfHash);
|
|
5576
|
+
}
|
|
5504
5577
|
}
|
|
5578
|
+
} catch {
|
|
5579
|
+
// Best-effort only; if pubsub isn't ready, do a full scan.
|
|
5505
5580
|
}
|
|
5506
|
-
} catch {
|
|
5507
|
-
// Best-effort only; if pubsub isn't ready, do a full scan.
|
|
5508
5581
|
}
|
|
5509
5582
|
}
|
|
5510
5583
|
return getSamples<R>(
|
|
@@ -6156,30 +6229,48 @@ export class SharedLog<
|
|
|
6156
6229
|
}
|
|
6157
6230
|
}
|
|
6158
6231
|
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6232
|
+
const changed = false;
|
|
6233
|
+
const addedPeers = new Set<string>();
|
|
6234
|
+
const warmupPeers = new Set<string>();
|
|
6235
|
+
const hasSelfWarmupChange = changes.some(
|
|
6236
|
+
(change) =>
|
|
6237
|
+
change.range.hash === selfHash &&
|
|
6238
|
+
(change.type === "added" || change.type === "replaced"),
|
|
6239
|
+
);
|
|
6240
|
+
for (const change of changes) {
|
|
6241
|
+
if (change.type === "added" || change.type === "replaced") {
|
|
6242
|
+
const hash = change.range.hash;
|
|
6243
|
+
if (hash !== selfHash) {
|
|
6244
|
+
// Range updates can reassign entries to an existing peer shortly after it
|
|
6245
|
+
// already received a subset. Avoid suppressing legitimate follow-up repair.
|
|
6246
|
+
this._recentRepairDispatch.delete(hash);
|
|
6247
|
+
}
|
|
6174
6248
|
}
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6249
|
+
if (change.type === "added") {
|
|
6250
|
+
const hash = change.range.hash;
|
|
6251
|
+
if (hash !== selfHash) {
|
|
6252
|
+
addedPeers.add(hash);
|
|
6253
|
+
warmupPeers.add(hash);
|
|
6254
|
+
}
|
|
6180
6255
|
}
|
|
6181
6256
|
}
|
|
6182
|
-
|
|
6257
|
+
const hasAdaptiveStorageLimit =
|
|
6258
|
+
this._isAdaptiveReplicating &&
|
|
6259
|
+
this.replicationController?.maxMemoryLimit != null;
|
|
6260
|
+
const useJoinWarmupFastPath =
|
|
6261
|
+
!forceFreshDelivery &&
|
|
6262
|
+
warmupPeers.size > 0 &&
|
|
6263
|
+
!hasSelfWarmupChange &&
|
|
6264
|
+
!hasAdaptiveStorageLimit;
|
|
6265
|
+
const immediateRebalanceChanges = useJoinWarmupFastPath
|
|
6266
|
+
? changes.filter(
|
|
6267
|
+
(change) =>
|
|
6268
|
+
!(
|
|
6269
|
+
change.range.hash === selfHash &&
|
|
6270
|
+
(change.type === "added" || change.type === "replaced")
|
|
6271
|
+
),
|
|
6272
|
+
)
|
|
6273
|
+
: changes;
|
|
6183
6274
|
|
|
6184
6275
|
try {
|
|
6185
6276
|
const uncheckedDeliver: Map<
|
|
@@ -6191,15 +6282,15 @@ export class SharedLog<
|
|
|
6191
6282
|
if (!entries || entries.size === 0) {
|
|
6192
6283
|
return;
|
|
6193
6284
|
}
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6285
|
+
const isWarmupTarget = warmupPeers.has(target);
|
|
6286
|
+
const bypassRecentDedupe = isWarmupTarget || forceFreshDelivery;
|
|
6287
|
+
this.dispatchMaybeMissingEntries(target, entries, {
|
|
6288
|
+
bypassRecentDedupe,
|
|
6289
|
+
retryScheduleMs: isWarmupTarget
|
|
6290
|
+
? JOIN_WARMUP_RETRY_SCHEDULE_MS
|
|
6291
|
+
: undefined,
|
|
6292
|
+
forceFreshDelivery,
|
|
6293
|
+
});
|
|
6203
6294
|
uncheckedDeliver.delete(target);
|
|
6204
6295
|
};
|
|
6205
6296
|
const queueUncheckedDeliver = (
|
|
@@ -6220,18 +6311,85 @@ export class SharedLog<
|
|
|
6220
6311
|
}
|
|
6221
6312
|
};
|
|
6222
6313
|
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6314
|
+
if (immediateRebalanceChanges.length > 0) {
|
|
6315
|
+
for await (const entryReplicated of toRebalance<R>(
|
|
6316
|
+
immediateRebalanceChanges,
|
|
6317
|
+
this.entryCoordinatesIndex,
|
|
6318
|
+
this.recentlyRebalanced,
|
|
6319
|
+
{
|
|
6320
|
+
forceFresh: forceFreshDelivery || useJoinWarmupFastPath,
|
|
6321
|
+
},
|
|
6322
|
+
)) {
|
|
6229
6323
|
if (this.closed) {
|
|
6230
6324
|
break;
|
|
6231
6325
|
}
|
|
6232
6326
|
|
|
6233
|
-
|
|
6234
|
-
|
|
6327
|
+
if (useJoinWarmupFastPath) {
|
|
6328
|
+
let oldPeersSet: Set<string> | undefined;
|
|
6329
|
+
const gid = entryReplicated.gid;
|
|
6330
|
+
oldPeersSet = gidPeersHistorySnapshot.get(gid);
|
|
6331
|
+
if (!gidPeersHistorySnapshot.has(gid)) {
|
|
6332
|
+
const existing = this._gidPeersHistory.get(gid);
|
|
6333
|
+
oldPeersSet = existing ? new Set(existing) : undefined;
|
|
6334
|
+
gidPeersHistorySnapshot.set(gid, oldPeersSet);
|
|
6335
|
+
}
|
|
6336
|
+
|
|
6337
|
+
for (const target of warmupPeers) {
|
|
6338
|
+
queueUncheckedDeliver(target, entryReplicated);
|
|
6339
|
+
}
|
|
6340
|
+
|
|
6341
|
+
const candidatePeers = new Set<string>([selfHash]);
|
|
6342
|
+
for (const target of warmupPeers) {
|
|
6343
|
+
candidatePeers.add(target);
|
|
6344
|
+
}
|
|
6345
|
+
if (oldPeersSet) {
|
|
6346
|
+
for (const oldPeer of oldPeersSet) {
|
|
6347
|
+
candidatePeers.add(oldPeer);
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6350
|
+
|
|
6351
|
+
const currentPeers = await this.findLeaders(
|
|
6352
|
+
entryReplicated.coordinates,
|
|
6353
|
+
entryReplicated,
|
|
6354
|
+
{
|
|
6355
|
+
roleAge: 0,
|
|
6356
|
+
candidates: candidatePeers,
|
|
6357
|
+
persist: false,
|
|
6358
|
+
},
|
|
6359
|
+
);
|
|
6360
|
+
|
|
6361
|
+
if (oldPeersSet) {
|
|
6362
|
+
for (const oldPeer of oldPeersSet) {
|
|
6363
|
+
if (!currentPeers.has(oldPeer)) {
|
|
6364
|
+
this.removePruneRequestSent(entryReplicated.hash);
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
}
|
|
6368
|
+
|
|
6369
|
+
this.addPeersToGidPeerHistory(
|
|
6370
|
+
entryReplicated.gid,
|
|
6371
|
+
currentPeers.keys(),
|
|
6372
|
+
true,
|
|
6373
|
+
);
|
|
6374
|
+
|
|
6375
|
+
if (!currentPeers.has(selfHash)) {
|
|
6376
|
+
this.pruneDebouncedFnAddIfNotKeeping({
|
|
6377
|
+
key: entryReplicated.hash,
|
|
6378
|
+
value: { entry: entryReplicated, leaders: currentPeers },
|
|
6379
|
+
});
|
|
6380
|
+
|
|
6381
|
+
this.responseToPruneDebouncedFn.delete(entryReplicated.hash);
|
|
6382
|
+
} else {
|
|
6383
|
+
this.pruneDebouncedFn.delete(entryReplicated.hash);
|
|
6384
|
+
await this._pendingDeletes
|
|
6385
|
+
.get(entryReplicated.hash)
|
|
6386
|
+
?.reject(new Error("Failed to delete, is leader again"));
|
|
6387
|
+
this.removePruneRequestSent(entryReplicated.hash);
|
|
6388
|
+
}
|
|
6389
|
+
continue;
|
|
6390
|
+
}
|
|
6391
|
+
|
|
6392
|
+
let oldPeersSet: Set<string> | undefined;
|
|
6235
6393
|
const gid = entryReplicated.gid;
|
|
6236
6394
|
oldPeersSet = gidPeersHistorySnapshot.get(gid);
|
|
6237
6395
|
if (!gidPeersHistorySnapshot.has(gid)) {
|
|
@@ -6239,18 +6397,18 @@ export class SharedLog<
|
|
|
6239
6397
|
oldPeersSet = existing ? new Set(existing) : undefined;
|
|
6240
6398
|
gidPeersHistorySnapshot.set(gid, oldPeersSet);
|
|
6241
6399
|
}
|
|
6242
|
-
}
|
|
6243
|
-
let isLeader = false;
|
|
6244
6400
|
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6401
|
+
let isLeader = false;
|
|
6402
|
+
const currentPeers = await this.findLeaders(
|
|
6403
|
+
entryReplicated.coordinates,
|
|
6404
|
+
entryReplicated,
|
|
6405
|
+
{
|
|
6406
|
+
// We do this to make sure new replicators get data even though
|
|
6407
|
+
// they are not mature so they can figure out if they want to
|
|
6408
|
+
// replicate more or less.
|
|
6409
|
+
roleAge: 0,
|
|
6410
|
+
},
|
|
6411
|
+
);
|
|
6254
6412
|
|
|
6255
6413
|
for (const [currentPeer] of currentPeers) {
|
|
6256
6414
|
if (currentPeer === this.node.identity.publicKey.hashcode()) {
|
|
@@ -6263,41 +6421,63 @@ export class SharedLog<
|
|
|
6263
6421
|
}
|
|
6264
6422
|
}
|
|
6265
6423
|
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6424
|
+
if (oldPeersSet) {
|
|
6425
|
+
for (const oldPeer of oldPeersSet) {
|
|
6426
|
+
if (!currentPeers.has(oldPeer)) {
|
|
6427
|
+
this.removePruneRequestSent(entryReplicated.hash);
|
|
6428
|
+
}
|
|
6270
6429
|
}
|
|
6271
6430
|
}
|
|
6272
|
-
}
|
|
6273
6431
|
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6432
|
+
this.addPeersToGidPeerHistory(
|
|
6433
|
+
entryReplicated.gid,
|
|
6434
|
+
currentPeers.keys(),
|
|
6435
|
+
true,
|
|
6436
|
+
);
|
|
6279
6437
|
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6438
|
+
if (!isLeader) {
|
|
6439
|
+
this.pruneDebouncedFnAddIfNotKeeping({
|
|
6440
|
+
key: entryReplicated.hash,
|
|
6441
|
+
value: { entry: entryReplicated, leaders: currentPeers },
|
|
6442
|
+
});
|
|
6285
6443
|
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6444
|
+
this.responseToPruneDebouncedFn.delete(entryReplicated.hash); // don't allow others to prune because of expecting me to replicating this entry
|
|
6445
|
+
} else {
|
|
6446
|
+
this.pruneDebouncedFn.delete(entryReplicated.hash);
|
|
6447
|
+
await this._pendingDeletes
|
|
6448
|
+
.get(entryReplicated.hash)
|
|
6449
|
+
?.reject(new Error("Failed to delete, is leader again"));
|
|
6450
|
+
this.removePruneRequestSent(entryReplicated.hash);
|
|
6451
|
+
}
|
|
6452
|
+
}
|
|
6293
6453
|
}
|
|
6294
|
-
}
|
|
6295
6454
|
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6455
|
+
if (forceFreshDelivery) {
|
|
6456
|
+
// Removed/shrunk ranges still need the authoritative background pass.
|
|
6457
|
+
this.scheduleRepairSweep({ forceFreshDelivery, addedPeers });
|
|
6458
|
+
} else if (useJoinWarmupFastPath) {
|
|
6459
|
+
// Pure join warmup uses the cheap immediate maybe-missing dispatch above,
|
|
6460
|
+
// then defers the authoritative sweep so it does not compete with the
|
|
6461
|
+
// write burst itself.
|
|
6462
|
+
const peers = new Set(addedPeers);
|
|
6463
|
+
const timer = setTimeout(() => {
|
|
6464
|
+
this._repairRetryTimers.delete(timer);
|
|
6465
|
+
if (this.closed) {
|
|
6466
|
+
return;
|
|
6467
|
+
}
|
|
6468
|
+
this.scheduleRepairSweep({
|
|
6469
|
+
forceFreshDelivery: false,
|
|
6470
|
+
addedPeers: peers,
|
|
6471
|
+
});
|
|
6472
|
+
}, 250);
|
|
6473
|
+
timer.unref?.();
|
|
6474
|
+
this._repairRetryTimers.add(timer);
|
|
6475
|
+
} else if (addedPeers.size > 0) {
|
|
6476
|
+
this.scheduleRepairSweep({
|
|
6477
|
+
forceFreshDelivery: false,
|
|
6478
|
+
addedPeers,
|
|
6479
|
+
});
|
|
6480
|
+
}
|
|
6301
6481
|
|
|
6302
6482
|
for (const target of [...uncheckedDeliver.keys()]) {
|
|
6303
6483
|
flushUncheckedDeliverTarget(target);
|
|
@@ -6336,6 +6516,7 @@ export class SharedLog<
|
|
|
6336
6516
|
if (!prev || prev < now) {
|
|
6337
6517
|
this.latestReplicationInfoMessage.set(fromHash, now);
|
|
6338
6518
|
}
|
|
6519
|
+
this.invalidateSharedLogTopicSubscribersCache();
|
|
6339
6520
|
|
|
6340
6521
|
return this.handleSubscriptionChange(
|
|
6341
6522
|
evt.detail.from,
|
|
@@ -6356,6 +6537,7 @@ export class SharedLog<
|
|
|
6356
6537
|
|
|
6357
6538
|
this.remoteBlocks.onReachable(evt.detail.from);
|
|
6358
6539
|
this._replicationInfoBlockedPeers.delete(evt.detail.from.hashcode());
|
|
6540
|
+
this.invalidateSharedLogTopicSubscribersCache();
|
|
6359
6541
|
|
|
6360
6542
|
await this.handleSubscriptionChange(
|
|
6361
6543
|
evt.detail.from,
|