@peerbit/shared-log 13.1.17 → 13.2.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/benchmark/index.js +91 -29
- package/dist/benchmark/index.js.map +1 -1
- package/dist/benchmark/native-graph.js +120 -10
- package/dist/benchmark/native-graph.js.map +1 -1
- package/dist/benchmark/network-preset-e2e.d.ts +2 -0
- package/dist/benchmark/network-preset-e2e.d.ts.map +1 -0
- package/dist/benchmark/network-preset-e2e.js +497 -0
- package/dist/benchmark/network-preset-e2e.js.map +1 -0
- package/dist/benchmark/receive-prune.d.ts +2 -0
- package/dist/benchmark/receive-prune.d.ts.map +1 -0
- package/dist/benchmark/receive-prune.js +816 -0
- package/dist/benchmark/receive-prune.js.map +1 -0
- package/dist/benchmark/sync-catchup.d.ts +1 -2
- package/dist/benchmark/sync-catchup.d.ts.map +1 -1
- package/dist/benchmark/sync-catchup.js +175 -7
- package/dist/benchmark/sync-catchup.js.map +1 -1
- package/dist/src/checked-prune.d.ts +3 -0
- package/dist/src/checked-prune.d.ts.map +1 -1
- package/dist/src/checked-prune.js +24 -0
- package/dist/src/checked-prune.js.map +1 -1
- package/dist/src/exchange-heads.d.ts +238 -1
- package/dist/src/exchange-heads.d.ts.map +1 -1
- package/dist/src/exchange-heads.js +1512 -36
- package/dist/src/exchange-heads.js.map +1 -1
- package/dist/src/index.d.ts +276 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7358 -666
- package/dist/src/index.js.map +1 -1
- package/dist/src/pid.d.ts.map +1 -1
- package/dist/src/pid.js +25 -9
- package/dist/src/pid.js.map +1 -1
- package/dist/src/ranges.d.ts +11 -2
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +74 -30
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/sync/index.d.ts +77 -2
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.d.ts +11 -5
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.js +123 -60
- package/dist/src/sync/rateless-iblt.js.map +1 -1
- package/dist/src/sync/simple.d.ts +56 -4
- package/dist/src/sync/simple.d.ts.map +1 -1
- package/dist/src/sync/simple.js +495 -63
- package/dist/src/sync/simple.js.map +1 -1
- package/dist/src/utils.d.ts +2 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +49 -2
- package/dist/src/utils.js.map +1 -1
- package/package.json +27 -17
- package/src/checked-prune.ts +27 -0
- package/src/exchange-heads.ts +2018 -41
- package/src/index.ts +13114 -3167
- package/src/pid.ts +24 -13
- package/src/ranges.ts +90 -40
- package/src/sync/index.ts +115 -2
- package/src/sync/rateless-iblt.ts +154 -75
- package/src/sync/simple.ts +590 -95
- package/src/utils.ts +73 -3
package/dist/src/sync/simple.js
CHANGED
|
@@ -37,7 +37,7 @@ import { Cache } from "@peerbit/cache";
|
|
|
37
37
|
import { Compare, IntegerCompare, Or, } from "@peerbit/indexer-interface";
|
|
38
38
|
import { Entry, Log } from "@peerbit/log";
|
|
39
39
|
import { CONVERGENCE_MESSAGE_PRIORITY, SilentDelivery, } from "@peerbit/stream-interface";
|
|
40
|
-
import { EntryWithRefs, createExchangeHeadsMessages, } from "../exchange-heads.js";
|
|
40
|
+
import { EntryWithRefs, createExchangeHeadsMessages, createRawExchangeHeadsMessages, } from "../exchange-heads.js";
|
|
41
41
|
import { TransportMessage } from "../message.js";
|
|
42
42
|
import { emitSyncProfileDuration, syncProfileStart } from "./profile.js";
|
|
43
43
|
let RequestMaybeSync = (() => {
|
|
@@ -160,10 +160,99 @@ let ConfirmEntriesMessage = (() => {
|
|
|
160
160
|
return ConfirmEntriesMessage = _classThis;
|
|
161
161
|
})();
|
|
162
162
|
export { ConfirmEntriesMessage };
|
|
163
|
-
const
|
|
163
|
+
export const SIMPLE_SYNC_RAW_EXCHANGE_HEADS_CAPABILITY = 1;
|
|
164
|
+
let ResponseMaybeSyncCapabilities = (() => {
|
|
165
|
+
let _classDecorators = [variant([0, 8])];
|
|
166
|
+
let _classDescriptor;
|
|
167
|
+
let _classExtraInitializers = [];
|
|
168
|
+
let _classThis;
|
|
169
|
+
let _classSuper = TransportMessage;
|
|
170
|
+
let _hashes_decorators;
|
|
171
|
+
let _hashes_initializers = [];
|
|
172
|
+
let _hashes_extraInitializers = [];
|
|
173
|
+
let _capabilities_decorators;
|
|
174
|
+
let _capabilities_initializers = [];
|
|
175
|
+
let _capabilities_extraInitializers = [];
|
|
176
|
+
var ResponseMaybeSyncCapabilities = class extends _classSuper {
|
|
177
|
+
static { _classThis = this; }
|
|
178
|
+
static {
|
|
179
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
180
|
+
_hashes_decorators = [field({ type: vec("string") })];
|
|
181
|
+
_capabilities_decorators = [field({ type: "u32" })];
|
|
182
|
+
__esDecorate(null, null, _hashes_decorators, { kind: "field", name: "hashes", static: false, private: false, access: { has: obj => "hashes" in obj, get: obj => obj.hashes, set: (obj, value) => { obj.hashes = value; } }, metadata: _metadata }, _hashes_initializers, _hashes_extraInitializers);
|
|
183
|
+
__esDecorate(null, null, _capabilities_decorators, { kind: "field", name: "capabilities", static: false, private: false, access: { has: obj => "capabilities" in obj, get: obj => obj.capabilities, set: (obj, value) => { obj.capabilities = value; } }, metadata: _metadata }, _capabilities_initializers, _capabilities_extraInitializers);
|
|
184
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
185
|
+
ResponseMaybeSyncCapabilities = _classThis = _classDescriptor.value;
|
|
186
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
187
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
188
|
+
}
|
|
189
|
+
hashes = __runInitializers(this, _hashes_initializers, void 0);
|
|
190
|
+
capabilities = (__runInitializers(this, _hashes_extraInitializers), __runInitializers(this, _capabilities_initializers, void 0));
|
|
191
|
+
constructor(props) {
|
|
192
|
+
super();
|
|
193
|
+
__runInitializers(this, _capabilities_extraInitializers);
|
|
194
|
+
this.hashes = props.hashes;
|
|
195
|
+
this.capabilities =
|
|
196
|
+
props.capabilities ?? SIMPLE_SYNC_RAW_EXCHANGE_HEADS_CAPABILITY;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
return ResponseMaybeSyncCapabilities = _classThis;
|
|
200
|
+
})();
|
|
201
|
+
export { ResponseMaybeSyncCapabilities };
|
|
202
|
+
let RequestMaybeSyncCoordinateCapabilities = (() => {
|
|
203
|
+
let _classDecorators = [variant([0, 9])];
|
|
204
|
+
let _classDescriptor;
|
|
205
|
+
let _classExtraInitializers = [];
|
|
206
|
+
let _classThis;
|
|
207
|
+
let _classSuper = TransportMessage;
|
|
208
|
+
let _hashNumbers_decorators;
|
|
209
|
+
let _hashNumbers_initializers = [];
|
|
210
|
+
let _hashNumbers_extraInitializers = [];
|
|
211
|
+
let _capabilities_decorators;
|
|
212
|
+
let _capabilities_initializers = [];
|
|
213
|
+
let _capabilities_extraInitializers = [];
|
|
214
|
+
var RequestMaybeSyncCoordinateCapabilities = class extends _classSuper {
|
|
215
|
+
static { _classThis = this; }
|
|
216
|
+
static {
|
|
217
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
218
|
+
_hashNumbers_decorators = [field({ type: vec("u64") })];
|
|
219
|
+
_capabilities_decorators = [field({ type: "u32" })];
|
|
220
|
+
__esDecorate(null, null, _hashNumbers_decorators, { kind: "field", name: "hashNumbers", static: false, private: false, access: { has: obj => "hashNumbers" in obj, get: obj => obj.hashNumbers, set: (obj, value) => { obj.hashNumbers = value; } }, metadata: _metadata }, _hashNumbers_initializers, _hashNumbers_extraInitializers);
|
|
221
|
+
__esDecorate(null, null, _capabilities_decorators, { kind: "field", name: "capabilities", static: false, private: false, access: { has: obj => "capabilities" in obj, get: obj => obj.capabilities, set: (obj, value) => { obj.capabilities = value; } }, metadata: _metadata }, _capabilities_initializers, _capabilities_extraInitializers);
|
|
222
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
223
|
+
RequestMaybeSyncCoordinateCapabilities = _classThis = _classDescriptor.value;
|
|
224
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
225
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
226
|
+
}
|
|
227
|
+
hashNumbers = __runInitializers(this, _hashNumbers_initializers, void 0);
|
|
228
|
+
capabilities = (__runInitializers(this, _hashNumbers_extraInitializers), __runInitializers(this, _capabilities_initializers, void 0));
|
|
229
|
+
constructor(props) {
|
|
230
|
+
super();
|
|
231
|
+
__runInitializers(this, _capabilities_extraInitializers);
|
|
232
|
+
this.hashNumbers = props.hashNumbers;
|
|
233
|
+
this.capabilities =
|
|
234
|
+
props.capabilities ?? SIMPLE_SYNC_RAW_EXCHANGE_HEADS_CAPABILITY;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
return RequestMaybeSyncCoordinateCapabilities = _classThis;
|
|
238
|
+
})();
|
|
239
|
+
export { RequestMaybeSyncCoordinateCapabilities };
|
|
240
|
+
const canReceiveRawExchangeHeads = (message) => (message instanceof ResponseMaybeSyncCapabilities ||
|
|
241
|
+
message instanceof RequestMaybeSyncCoordinateCapabilities) &&
|
|
242
|
+
(message.capabilities & SIMPLE_SYNC_RAW_EXCHANGE_HEADS_CAPABILITY) !== 0;
|
|
243
|
+
const getHashesFromSymbols = async (symbols, entryIndex, coordinateToHash, resolveHashesForSymbols, resolveHashListForSymbols) => {
|
|
164
244
|
let queries = [];
|
|
165
245
|
let batchSize = 128; // TODO arg
|
|
166
246
|
let results = new Set();
|
|
247
|
+
let missingSymbols = [];
|
|
248
|
+
const addMissingUnlessCached = (symbol) => {
|
|
249
|
+
const fromCache = coordinateToHash.get(symbol);
|
|
250
|
+
if (fromCache) {
|
|
251
|
+
results.add(fromCache);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
missingSymbols.push(symbol);
|
|
255
|
+
};
|
|
167
256
|
const handleBatch = async (end = false) => {
|
|
168
257
|
if (queries.length >= batchSize || (end && queries.length > 0)) {
|
|
169
258
|
const entries = await entryIndex
|
|
@@ -176,16 +265,63 @@ const getHashesFromSymbols = async (symbols, entryIndex, coordinateToHash) => {
|
|
|
176
265
|
}
|
|
177
266
|
}
|
|
178
267
|
};
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
268
|
+
if (resolveHashListForSymbols) {
|
|
269
|
+
const resolvedHashes = await resolveHashListForSymbols(symbols);
|
|
270
|
+
if (resolvedHashes) {
|
|
271
|
+
const resolvedHashList = Array.isArray(resolvedHashes)
|
|
272
|
+
? resolvedHashes
|
|
273
|
+
: [...resolvedHashes];
|
|
274
|
+
let mergedHashes;
|
|
275
|
+
for (const symbol of symbols) {
|
|
276
|
+
const fromCache = coordinateToHash.get(symbol);
|
|
277
|
+
if (fromCache) {
|
|
278
|
+
mergedHashes ??= new Set(resolvedHashList);
|
|
279
|
+
mergedHashes.add(fromCache);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return mergedHashes ?? resolvedHashList;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (resolveHashesForSymbols) {
|
|
286
|
+
const resolved = await resolveHashesForSymbols(symbols);
|
|
287
|
+
if (resolved) {
|
|
288
|
+
for (const symbol of symbols) {
|
|
289
|
+
const hashes = resolved.get(symbol);
|
|
290
|
+
if (!hashes) {
|
|
291
|
+
addMissingUnlessCached(symbol);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
let singleHash;
|
|
295
|
+
let count = 0;
|
|
296
|
+
for (const hash of hashes) {
|
|
297
|
+
results.add(hash);
|
|
298
|
+
singleHash = hash;
|
|
299
|
+
count += 1;
|
|
300
|
+
}
|
|
301
|
+
if (count === 0) {
|
|
302
|
+
addMissingUnlessCached(symbol);
|
|
303
|
+
}
|
|
304
|
+
else if (count === 1) {
|
|
305
|
+
coordinateToHash.add(symbol, singleHash);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
for (const symbol of symbols) {
|
|
311
|
+
addMissingUnlessCached(symbol);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
for (const symbol of symbols) {
|
|
317
|
+
addMissingUnlessCached(symbol);
|
|
184
318
|
}
|
|
319
|
+
}
|
|
320
|
+
for (const symbol of missingSymbols) {
|
|
185
321
|
const matchQuery = new IntegerCompare({
|
|
186
322
|
key: "hashNumber",
|
|
187
323
|
compare: Compare.Equal,
|
|
188
|
-
value:
|
|
324
|
+
value: symbol,
|
|
189
325
|
});
|
|
190
326
|
queries.push(matchQuery);
|
|
191
327
|
await handleBatch();
|
|
@@ -193,6 +329,7 @@ const getHashesFromSymbols = async (symbols, entryIndex, coordinateToHash) => {
|
|
|
193
329
|
await handleBatch(true);
|
|
194
330
|
return results;
|
|
195
331
|
};
|
|
332
|
+
const hashLookupResultSize = (hashes) => Array.isArray(hashes) ? hashes.length : hashes.size;
|
|
196
333
|
const DEFAULT_CONVERGENT_REPAIR_TIMEOUT_MS = 30_000;
|
|
197
334
|
const DEFAULT_CONVERGENT_RETRY_INTERVALS_MS = [0, 1_000, 3_000, 7_000];
|
|
198
335
|
const DEFAULT_BEST_EFFORT_RETRY_INTERVALS_MS = [0];
|
|
@@ -207,6 +344,8 @@ export const SYNC_MESSAGE_PRIORITY = CONVERGENCE_MESSAGE_PRIORITY;
|
|
|
207
344
|
// pubsub stream warmup). Keep it coarse-grained so we do not hammer the network under
|
|
208
345
|
// large historical backfills.
|
|
209
346
|
const SIMPLE_SYNC_RETRY_AFTER_MS = 10_000;
|
|
347
|
+
const EXCHANGE_HEAD_RESPONSE_DEDUPE_TTL_MS = SIMPLE_SYNC_RETRY_AFTER_MS - 1_000;
|
|
348
|
+
const RECENT_KNOWN_EXCHANGE_HEAD_SUPPRESSION_MS = 30_000;
|
|
210
349
|
const createDeferred = () => {
|
|
211
350
|
let resolve;
|
|
212
351
|
let reject;
|
|
@@ -226,7 +365,12 @@ export class SimpleSyncronizer {
|
|
|
226
365
|
log;
|
|
227
366
|
entryIndex;
|
|
228
367
|
coordinateToHash;
|
|
368
|
+
resolveHashesForSymbols;
|
|
369
|
+
resolveHashListForSymbols;
|
|
229
370
|
syncOptions;
|
|
371
|
+
isEntryRecentlyKnownByPeer;
|
|
372
|
+
sendRawExchangeHeads;
|
|
373
|
+
recentlySentExchangeHeads;
|
|
230
374
|
repairSessionCounter;
|
|
231
375
|
repairSessions;
|
|
232
376
|
// Syncing and dedeplucation work
|
|
@@ -240,7 +384,12 @@ export class SimpleSyncronizer {
|
|
|
240
384
|
this.log = properties.log;
|
|
241
385
|
this.entryIndex = properties.entryIndex;
|
|
242
386
|
this.coordinateToHash = properties.coordinateToHash;
|
|
387
|
+
this.resolveHashesForSymbols = properties.resolveHashesForSymbols;
|
|
388
|
+
this.resolveHashListForSymbols = properties.resolveHashListForSymbols;
|
|
243
389
|
this.syncOptions = properties.sync;
|
|
390
|
+
this.isEntryRecentlyKnownByPeer = properties.isEntryRecentlyKnownByPeer;
|
|
391
|
+
this.sendRawExchangeHeads = properties.sendRawExchangeHeads;
|
|
392
|
+
this.recentlySentExchangeHeads = new Map();
|
|
244
393
|
this.repairSessionCounter = 0;
|
|
245
394
|
this.repairSessions = new Map();
|
|
246
395
|
}
|
|
@@ -303,6 +452,37 @@ export class SimpleSyncronizer {
|
|
|
303
452
|
}
|
|
304
453
|
return out;
|
|
305
454
|
}
|
|
455
|
+
filterRecentlySentExchangeHeads(hashes, peer) {
|
|
456
|
+
const peerHash = peer.hashcode();
|
|
457
|
+
const now = Date.now();
|
|
458
|
+
let recentlySent = this.recentlySentExchangeHeads.get(peerHash);
|
|
459
|
+
if (!recentlySent) {
|
|
460
|
+
recentlySent = new Map();
|
|
461
|
+
this.recentlySentExchangeHeads.set(peerHash, recentlySent);
|
|
462
|
+
}
|
|
463
|
+
for (const [hash, timestamp] of recentlySent) {
|
|
464
|
+
if (now - timestamp > EXCHANGE_HEAD_RESPONSE_DEDUPE_TTL_MS) {
|
|
465
|
+
recentlySent.delete(hash);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const out = [];
|
|
469
|
+
const seen = new Set();
|
|
470
|
+
for (const hash of hashes) {
|
|
471
|
+
if (seen.has(hash)) {
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
seen.add(hash);
|
|
475
|
+
if (recentlySent.has(hash)) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (this.isEntryRecentlyKnownByPeer?.(hash, peerHash, RECENT_KNOWN_EXCHANGE_HEAD_SUPPRESSION_MS)) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
recentlySent.set(hash, now);
|
|
482
|
+
out.push(hash);
|
|
483
|
+
}
|
|
484
|
+
return out;
|
|
485
|
+
}
|
|
306
486
|
isRepairSessionComplete(session) {
|
|
307
487
|
for (const state of session.targets.values()) {
|
|
308
488
|
if (state.unresolved.size > 0) {
|
|
@@ -348,12 +528,22 @@ export class SimpleSyncronizer {
|
|
|
348
528
|
return;
|
|
349
529
|
}
|
|
350
530
|
for (const state of session.targets.values()) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
531
|
+
const resolved = typeof this.log.hasMany === "function"
|
|
532
|
+
? await this.log.hasMany(state.unresolved)
|
|
533
|
+
: await this.getExistingRepairHashes(state.unresolved);
|
|
534
|
+
for (const hash of resolved) {
|
|
535
|
+
state.unresolved.delete(hash);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async getExistingRepairHashes(hashes) {
|
|
540
|
+
const resolved = new Set();
|
|
541
|
+
for (const hash of hashes) {
|
|
542
|
+
if (await this.log.has(hash)) {
|
|
543
|
+
resolved.add(hash);
|
|
355
544
|
}
|
|
356
545
|
}
|
|
546
|
+
return resolved;
|
|
357
547
|
}
|
|
358
548
|
markRepairSessionResolvedHashes(hashes) {
|
|
359
549
|
if (hashes.length === 0 || this.repairSessions.size === 0) {
|
|
@@ -370,6 +560,19 @@ export class SimpleSyncronizer {
|
|
|
370
560
|
}
|
|
371
561
|
}
|
|
372
562
|
}
|
|
563
|
+
markRepairSessionResolvedHash(hash) {
|
|
564
|
+
if (this.repairSessions.size === 0) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
for (const [sessionId, session] of this.repairSessions) {
|
|
568
|
+
for (const state of session.targets.values()) {
|
|
569
|
+
state.unresolved.delete(hash);
|
|
570
|
+
}
|
|
571
|
+
if (this.isRepairSessionComplete(session)) {
|
|
572
|
+
this.finalizeRepairSession(sessionId, true);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
373
576
|
async runRepairSession(sessionId) {
|
|
374
577
|
const session = this.repairSessions.get(sessionId);
|
|
375
578
|
if (!session) {
|
|
@@ -535,9 +738,15 @@ export class SimpleSyncronizer {
|
|
|
535
738
|
};
|
|
536
739
|
}
|
|
537
740
|
async onMaybeMissingEntries(properties) {
|
|
741
|
+
await this.onMaybeMissingHashes({
|
|
742
|
+
hashes: this.getPrioritizedHashes(properties.entries),
|
|
743
|
+
targets: properties.targets,
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
async onMaybeMissingHashes(properties) {
|
|
538
747
|
const profile = this.syncOptions?.profile;
|
|
539
748
|
const startedAt = syncProfileStart(profile);
|
|
540
|
-
const hashes =
|
|
749
|
+
const hashes = [...properties.hashes];
|
|
541
750
|
const chunks = this.chunk(hashes, this.maxHashesPerMessage);
|
|
542
751
|
try {
|
|
543
752
|
await chunks.reduce((promise, chunk) => promise.then(() => this.rpc.send(new RequestMaybeSync({ hashes: chunk }), {
|
|
@@ -559,69 +768,92 @@ export class SimpleSyncronizer {
|
|
|
559
768
|
}
|
|
560
769
|
}
|
|
561
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* Ship exchange heads to one peer: the fused native raw path when the
|
|
773
|
+
* peer advertised raw capability and the shared log provided a fused
|
|
774
|
+
* sender, otherwise the TS message path (raw or plain by capability).
|
|
775
|
+
* Returns the number of messages sent and whether the fused path ran.
|
|
776
|
+
*/
|
|
777
|
+
async shipExchangeHeads(hashes, to, canReceiveRaw) {
|
|
778
|
+
if (canReceiveRaw && this.sendRawExchangeHeads) {
|
|
779
|
+
const sentMessages = await this.sendRawExchangeHeads(hashes, [
|
|
780
|
+
to.hashcode(),
|
|
781
|
+
]);
|
|
782
|
+
if (sentMessages !== undefined) {
|
|
783
|
+
return { messages: sentMessages, fused: true };
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
let messages = 0;
|
|
787
|
+
const messageGenerator = canReceiveRaw
|
|
788
|
+
? createRawExchangeHeadsMessages(this.log, hashes, this.syncOptions?.profile)
|
|
789
|
+
: createExchangeHeadsMessages(this.log, hashes);
|
|
790
|
+
for await (const message of messageGenerator) {
|
|
791
|
+
messages += 1;
|
|
792
|
+
await this.rpc.send(message, {
|
|
793
|
+
mode: new SilentDelivery({ to: [to], redundancy: 1 }),
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
return { messages, fused: false };
|
|
797
|
+
}
|
|
562
798
|
async onMessage(msg, context) {
|
|
563
799
|
const from = context.from;
|
|
564
800
|
if (msg instanceof RequestMaybeSync) {
|
|
565
801
|
await this.queueSync(msg.hashes, from);
|
|
566
802
|
return true;
|
|
567
803
|
}
|
|
568
|
-
else if (msg instanceof ResponseMaybeSync
|
|
804
|
+
else if (msg instanceof ResponseMaybeSync ||
|
|
805
|
+
msg instanceof ResponseMaybeSyncCapabilities) {
|
|
569
806
|
// TODO perhaps send less messages to more receivers for performance reasons?
|
|
570
807
|
// TODO wait for previous send to target before trying to send more?
|
|
571
808
|
const profile = this.syncOptions?.profile;
|
|
572
809
|
const startedAt = syncProfileStart(profile);
|
|
810
|
+
const hashes = this.filterRecentlySentExchangeHeads(msg.hashes, from);
|
|
573
811
|
let messages = 0;
|
|
812
|
+
let fused = false;
|
|
574
813
|
try {
|
|
575
|
-
|
|
576
|
-
messages += 1;
|
|
577
|
-
await this.rpc.send(message, {
|
|
578
|
-
mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
|
|
579
|
-
});
|
|
580
|
-
}
|
|
814
|
+
({ messages, fused } = await this.shipExchangeHeads(hashes, context.from, canReceiveRawExchangeHeads(msg)));
|
|
581
815
|
}
|
|
582
816
|
finally {
|
|
583
817
|
if (profile) {
|
|
584
818
|
emitSyncProfileDuration(profile, startedAt, {
|
|
585
819
|
name: "simple.exchangeHeads",
|
|
586
|
-
entries:
|
|
820
|
+
entries: hashes.length,
|
|
587
821
|
messages,
|
|
588
822
|
targets: 1,
|
|
589
|
-
details: { source: "responseMaybeSync" },
|
|
823
|
+
details: { source: "responseMaybeSync", fused },
|
|
590
824
|
});
|
|
591
825
|
}
|
|
592
826
|
}
|
|
593
827
|
return true;
|
|
594
828
|
}
|
|
595
|
-
else if (msg instanceof RequestMaybeSyncCoordinate
|
|
829
|
+
else if (msg instanceof RequestMaybeSyncCoordinate ||
|
|
830
|
+
msg instanceof RequestMaybeSyncCoordinateCapabilities) {
|
|
596
831
|
const profile = this.syncOptions?.profile;
|
|
597
832
|
const lookupStartedAt = syncProfileStart(profile);
|
|
598
|
-
const hashes = await getHashesFromSymbols(msg.hashNumbers, this.entryIndex, this.coordinateToHash);
|
|
833
|
+
const hashes = await getHashesFromSymbols(msg.hashNumbers, this.entryIndex, this.coordinateToHash, this.resolveHashesForSymbols, this.resolveHashListForSymbols);
|
|
599
834
|
if (profile) {
|
|
600
835
|
emitSyncProfileDuration(profile, lookupStartedAt, {
|
|
601
836
|
name: "simple.coordinateLookup",
|
|
602
|
-
entries: hashes
|
|
837
|
+
entries: hashLookupResultSize(hashes),
|
|
603
838
|
symbols: msg.hashNumbers.length,
|
|
604
839
|
});
|
|
605
840
|
}
|
|
606
841
|
const exchangeStartedAt = syncProfileStart(profile);
|
|
842
|
+
const hashesToSend = this.filterRecentlySentExchangeHeads(hashes, from);
|
|
607
843
|
let messages = 0;
|
|
844
|
+
let fused = false;
|
|
608
845
|
try {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
await this.rpc.send(message, {
|
|
612
|
-
mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
|
|
613
|
-
// dont set priority 1 here because this will block other messages that should higher priority
|
|
614
|
-
});
|
|
615
|
-
}
|
|
846
|
+
// dont set priority 1 here because this will block other messages that should higher priority
|
|
847
|
+
({ messages, fused } = await this.shipExchangeHeads(hashesToSend, context.from, canReceiveRawExchangeHeads(msg)));
|
|
616
848
|
}
|
|
617
849
|
finally {
|
|
618
850
|
if (profile) {
|
|
619
851
|
emitSyncProfileDuration(profile, exchangeStartedAt, {
|
|
620
852
|
name: "simple.exchangeHeads",
|
|
621
|
-
entries:
|
|
853
|
+
entries: hashesToSend.length,
|
|
622
854
|
messages,
|
|
623
855
|
targets: 1,
|
|
624
|
-
details: { source: "requestMaybeSyncCoordinate" },
|
|
856
|
+
details: { source: "requestMaybeSyncCoordinate", fused },
|
|
625
857
|
});
|
|
626
858
|
}
|
|
627
859
|
}
|
|
@@ -632,47 +864,105 @@ export class SimpleSyncronizer {
|
|
|
632
864
|
}
|
|
633
865
|
}
|
|
634
866
|
onReceivedEntries(properties) {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
867
|
+
return this.onReceivedEntryHashes({
|
|
868
|
+
hashes: properties.entries.map((entry) => entry.entry.hash),
|
|
869
|
+
from: properties.from,
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
onReceivedEntryHashes(properties) {
|
|
873
|
+
this.clearSyncInFlightForPeerHashes(properties.from.hashcode(), properties.hashes);
|
|
874
|
+
this.markRepairSessionResolvedHashes(properties.hashes);
|
|
641
875
|
}
|
|
642
876
|
async queueSync(keys, from, options) {
|
|
643
877
|
const requestHashes = [];
|
|
644
878
|
const profile = this.syncOptions?.profile;
|
|
645
879
|
const startedAt = syncProfileStart(profile);
|
|
880
|
+
const resolveKnownStartedAt = syncProfileStart(profile);
|
|
881
|
+
const knownKeys = options?.skipCheck === true
|
|
882
|
+
? undefined
|
|
883
|
+
: await this.resolveKnownSyncKeys(keys);
|
|
884
|
+
if (profile) {
|
|
885
|
+
emitSyncProfileDuration(profile, resolveKnownStartedAt, {
|
|
886
|
+
name: "simple.queueSync.resolveKnown",
|
|
887
|
+
entries: keys.length,
|
|
888
|
+
count: knownKeys?.keys.size ?? 0,
|
|
889
|
+
details: {
|
|
890
|
+
checkedCoordinates: knownKeys?.checkedCoordinates === true,
|
|
891
|
+
checkedHashes: knownKeys?.checkedHashes === true,
|
|
892
|
+
skipCheck: options?.skipCheck === true,
|
|
893
|
+
},
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
const fromHash = from.hashcode();
|
|
897
|
+
let queuedHashAliases;
|
|
898
|
+
const getQueuedSyncKeyForBatch = (key) => {
|
|
899
|
+
if (this.syncInFlightQueue.has(key)) {
|
|
900
|
+
return key;
|
|
901
|
+
}
|
|
902
|
+
if (typeof key === "string") {
|
|
903
|
+
if (!queuedHashAliases) {
|
|
904
|
+
queuedHashAliases = new Map();
|
|
905
|
+
for (const queuedKey of this.syncInFlightQueue.keys()) {
|
|
906
|
+
if (typeof queuedKey !== "bigint") {
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
const hash = this.coordinateToHash.get(queuedKey);
|
|
910
|
+
if (hash) {
|
|
911
|
+
queuedHashAliases.set(hash, queuedKey);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return queuedHashAliases.get(key);
|
|
916
|
+
}
|
|
917
|
+
const hash = this.coordinateToHash.get(key);
|
|
918
|
+
return hash && this.syncInFlightQueue.has(hash) ? hash : undefined;
|
|
919
|
+
};
|
|
646
920
|
try {
|
|
921
|
+
const loopStartedAt = syncProfileStart(profile);
|
|
647
922
|
for (const key of keys) {
|
|
648
|
-
const coordinateOrHash =
|
|
923
|
+
const coordinateOrHash = getQueuedSyncKeyForBatch(key) ?? key;
|
|
649
924
|
const inFlight = this.syncInFlightQueue.get(coordinateOrHash);
|
|
650
925
|
if (inFlight) {
|
|
651
|
-
if (!inFlight.find((x) => x.hashcode() ===
|
|
926
|
+
if (!inFlight.find((x) => x.hashcode() === fromHash)) {
|
|
652
927
|
inFlight.push(from);
|
|
653
|
-
let inverted = this.syncInFlightQueueInverted.get(
|
|
928
|
+
let inverted = this.syncInFlightQueueInverted.get(fromHash);
|
|
654
929
|
if (!inverted) {
|
|
655
930
|
inverted = new Set();
|
|
656
|
-
this.syncInFlightQueueInverted.set(
|
|
931
|
+
this.syncInFlightQueueInverted.set(fromHash, inverted);
|
|
657
932
|
}
|
|
658
933
|
inverted.add(coordinateOrHash);
|
|
659
934
|
}
|
|
660
935
|
}
|
|
661
936
|
else if (options?.skipCheck ||
|
|
662
|
-
!(await this.checkHasCoordinateOrHash(coordinateOrHash))) {
|
|
937
|
+
!(await this.checkHasCoordinateOrHash(coordinateOrHash, knownKeys))) {
|
|
663
938
|
// Track the initial sender so we can retry if the first request is lost.
|
|
664
939
|
this.syncInFlightQueue.set(coordinateOrHash, [from]);
|
|
665
|
-
let inverted = this.syncInFlightQueueInverted.get(
|
|
940
|
+
let inverted = this.syncInFlightQueueInverted.get(fromHash);
|
|
666
941
|
if (!inverted) {
|
|
667
942
|
inverted = new Set();
|
|
668
|
-
this.syncInFlightQueueInverted.set(
|
|
943
|
+
this.syncInFlightQueueInverted.set(fromHash, inverted);
|
|
669
944
|
}
|
|
670
945
|
inverted.add(coordinateOrHash);
|
|
671
946
|
requestHashes.push(coordinateOrHash); // request immediately (first time we have seen this hash)
|
|
947
|
+
if (queuedHashAliases &&
|
|
948
|
+
typeof coordinateOrHash === "bigint") {
|
|
949
|
+
const hash = this.coordinateToHash.get(coordinateOrHash);
|
|
950
|
+
if (hash) {
|
|
951
|
+
queuedHashAliases.set(hash, coordinateOrHash);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
672
954
|
}
|
|
673
955
|
}
|
|
956
|
+
if (profile) {
|
|
957
|
+
emitSyncProfileDuration(profile, loopStartedAt, {
|
|
958
|
+
name: "simple.queueSync.plan",
|
|
959
|
+
entries: keys.length,
|
|
960
|
+
count: requestHashes.length,
|
|
961
|
+
targets: 1,
|
|
962
|
+
});
|
|
963
|
+
}
|
|
674
964
|
requestHashes.length > 0 &&
|
|
675
|
-
(await this.requestSync(requestHashes, [
|
|
965
|
+
(await this.requestSync(requestHashes, [fromHash]));
|
|
676
966
|
}
|
|
677
967
|
finally {
|
|
678
968
|
if (profile) {
|
|
@@ -726,7 +1016,11 @@ export class SimpleSyncronizer {
|
|
|
726
1016
|
const chunks = this.chunk(coordinateHashes, this.maxCoordinatesPerMessage);
|
|
727
1017
|
coordinateMessages = chunks.length;
|
|
728
1018
|
for (const chunk of chunks) {
|
|
729
|
-
await this.rpc.send(
|
|
1019
|
+
await this.rpc.send(this.syncOptions?.rawExchangeHeads
|
|
1020
|
+
? new RequestMaybeSyncCoordinateCapabilities({
|
|
1021
|
+
hashNumbers: chunk,
|
|
1022
|
+
})
|
|
1023
|
+
: new RequestMaybeSyncCoordinate({ hashNumbers: chunk }), {
|
|
730
1024
|
mode: new SilentDelivery({ to, redundancy: 1 }),
|
|
731
1025
|
priority: SYNC_MESSAGE_PRIORITY,
|
|
732
1026
|
});
|
|
@@ -736,7 +1030,9 @@ export class SimpleSyncronizer {
|
|
|
736
1030
|
const chunks = this.chunk(stringHashes, this.maxHashesPerMessage);
|
|
737
1031
|
stringMessages = chunks.length;
|
|
738
1032
|
for (const chunk of chunks) {
|
|
739
|
-
await this.rpc.send(
|
|
1033
|
+
await this.rpc.send(this.syncOptions?.rawExchangeHeads
|
|
1034
|
+
? new ResponseMaybeSyncCapabilities({ hashes: chunk })
|
|
1035
|
+
: new ResponseMaybeSync({ hashes: chunk }), {
|
|
740
1036
|
mode: new SilentDelivery({ to, redundancy: 1 }),
|
|
741
1037
|
priority: SYNC_MESSAGE_PRIORITY,
|
|
742
1038
|
});
|
|
@@ -758,7 +1054,58 @@ export class SimpleSyncronizer {
|
|
|
758
1054
|
}
|
|
759
1055
|
}
|
|
760
1056
|
}
|
|
761
|
-
async
|
|
1057
|
+
async resolveKnownSyncKeys(keys) {
|
|
1058
|
+
const hashes = [];
|
|
1059
|
+
const coordinates = [];
|
|
1060
|
+
for (const key of keys) {
|
|
1061
|
+
if (typeof key === "bigint") {
|
|
1062
|
+
coordinates.push(key);
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
hashes.push(key);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
const known = {
|
|
1069
|
+
keys: new Set(),
|
|
1070
|
+
checkedCoordinates: false,
|
|
1071
|
+
checkedHashes: false,
|
|
1072
|
+
};
|
|
1073
|
+
if (hashes.length > 0) {
|
|
1074
|
+
for (const hash of await this.log.hasMany(hashes)) {
|
|
1075
|
+
known.keys.add(hash);
|
|
1076
|
+
}
|
|
1077
|
+
known.checkedHashes = true;
|
|
1078
|
+
}
|
|
1079
|
+
if (coordinates.length > 0 && this.resolveHashesForSymbols) {
|
|
1080
|
+
const resolved = await this.resolveHashesForSymbols(coordinates);
|
|
1081
|
+
if (resolved) {
|
|
1082
|
+
for (const coordinate of coordinates) {
|
|
1083
|
+
const hashes = resolved.get(coordinate);
|
|
1084
|
+
if (!hashes) {
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
for (const _hash of hashes) {
|
|
1088
|
+
known.keys.add(coordinate);
|
|
1089
|
+
break;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
known.checkedCoordinates = true;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
return known.checkedCoordinates || known.checkedHashes ? known : undefined;
|
|
1096
|
+
}
|
|
1097
|
+
async checkHasCoordinateOrHash(key, knownKeys) {
|
|
1098
|
+
if (knownKeys) {
|
|
1099
|
+
if (knownKeys.keys.has(key)) {
|
|
1100
|
+
return true;
|
|
1101
|
+
}
|
|
1102
|
+
if (typeof key === "bigint" && knownKeys.checkedCoordinates) {
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
if (typeof key === "string" && knownKeys.checkedHashes) {
|
|
1106
|
+
return false;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
762
1109
|
return typeof key === "bigint"
|
|
763
1110
|
? (await this.entryIndex.count({ query: { hashNumber: key } })) > 0
|
|
764
1111
|
: this.log.has(key);
|
|
@@ -834,18 +1181,49 @@ export class SimpleSyncronizer {
|
|
|
834
1181
|
this.syncInFlightQueue.clear();
|
|
835
1182
|
this.syncInFlightQueueInverted.clear();
|
|
836
1183
|
this.syncInFlight.clear();
|
|
1184
|
+
this.recentlySentExchangeHeads.clear();
|
|
837
1185
|
for (const sessionId of [...this.repairSessions.keys()]) {
|
|
838
1186
|
this.finalizeRepairSession(sessionId, false);
|
|
839
1187
|
}
|
|
840
1188
|
clearTimeout(this.syncMoreInterval);
|
|
841
1189
|
}
|
|
842
1190
|
onEntryAdded(entry) {
|
|
843
|
-
this.
|
|
844
|
-
|
|
1191
|
+
this.onEntryAddedHash(entry.hash);
|
|
1192
|
+
}
|
|
1193
|
+
onEntryAddedHashes(hashes) {
|
|
1194
|
+
if (hashes.length === 0 || !this.hasEntryAddedState()) {
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
this.clearSyncProcesses(hashes);
|
|
1198
|
+
this.markRepairSessionResolvedHashes(hashes);
|
|
1199
|
+
}
|
|
1200
|
+
onEntryAddedHash(hash) {
|
|
1201
|
+
if (!this.hasEntryAddedState()) {
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
this.clearSyncProcess(hash);
|
|
1205
|
+
this.markRepairSessionResolvedHash(hash);
|
|
845
1206
|
}
|
|
846
1207
|
onEntryRemoved(hash) {
|
|
1208
|
+
if (!this.hasSyncProcessState()) {
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
847
1211
|
return this.clearSyncProcess(hash);
|
|
848
1212
|
}
|
|
1213
|
+
onEntryRemovedHashes(hashes) {
|
|
1214
|
+
if (hashes.length === 0 || !this.hasSyncProcessState()) {
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
return this.clearSyncProcesses(hashes);
|
|
1218
|
+
}
|
|
1219
|
+
hasEntryAddedState() {
|
|
1220
|
+
return this.hasSyncProcessState() || this.repairSessions.size > 0;
|
|
1221
|
+
}
|
|
1222
|
+
hasSyncProcessState() {
|
|
1223
|
+
return (this.syncInFlightQueue.size > 0 ||
|
|
1224
|
+
this.syncInFlightQueueInverted.size > 0 ||
|
|
1225
|
+
this.syncInFlight.size > 0);
|
|
1226
|
+
}
|
|
849
1227
|
clearSyncProcessKey(key) {
|
|
850
1228
|
const inflight = this.syncInFlightQueue.get(key);
|
|
851
1229
|
if (inflight) {
|
|
@@ -870,24 +1248,52 @@ export class SimpleSyncronizer {
|
|
|
870
1248
|
}
|
|
871
1249
|
}
|
|
872
1250
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1251
|
+
forEachKnownAlias(hash, callback) {
|
|
1252
|
+
callback(hash);
|
|
1253
|
+
if (this.syncInFlightQueue.size === 0 && this.syncInFlight.size === 0) {
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
for (const key of this.syncInFlightQueue.keys()) {
|
|
879
1257
|
if (typeof key === "bigint" && this.coordinateToHash.get(key) === hash) {
|
|
880
|
-
|
|
1258
|
+
callback(key);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
for (const map of this.syncInFlight.values()) {
|
|
1262
|
+
for (const key of map.keys()) {
|
|
1263
|
+
if (typeof key === "bigint" &&
|
|
1264
|
+
this.coordinateToHash.get(key) === hash) {
|
|
1265
|
+
callback(key);
|
|
1266
|
+
}
|
|
881
1267
|
}
|
|
882
1268
|
}
|
|
883
|
-
return [...aliases];
|
|
884
1269
|
}
|
|
885
1270
|
clearSyncInFlightForPeer(publicKeyHash, hash) {
|
|
886
1271
|
const map = this.syncInFlight.get(publicKeyHash);
|
|
887
1272
|
if (!map) {
|
|
888
1273
|
return;
|
|
889
1274
|
}
|
|
890
|
-
|
|
1275
|
+
this.forEachKnownAlias(hash, (key) => map.delete(key));
|
|
1276
|
+
if (map.size === 0) {
|
|
1277
|
+
this.syncInFlight.delete(publicKeyHash);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
clearSyncInFlightForPeerHashes(publicKeyHash, hashes) {
|
|
1281
|
+
const map = this.syncInFlight.get(publicKeyHash);
|
|
1282
|
+
if (!map || hashes.length === 0) {
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
const keys = new Set(hashes);
|
|
1286
|
+
const hashSet = new Set(hashes);
|
|
1287
|
+
for (const key of map.keys()) {
|
|
1288
|
+
if (typeof key !== "bigint") {
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
const hash = this.coordinateToHash.get(key);
|
|
1292
|
+
if (hash != null && hashSet.has(hash)) {
|
|
1293
|
+
keys.add(key);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
for (const key of keys) {
|
|
891
1297
|
map.delete(key);
|
|
892
1298
|
}
|
|
893
1299
|
if (map.size === 0) {
|
|
@@ -895,7 +1301,32 @@ export class SimpleSyncronizer {
|
|
|
895
1301
|
}
|
|
896
1302
|
}
|
|
897
1303
|
clearSyncProcess(hash) {
|
|
898
|
-
|
|
1304
|
+
this.forEachKnownAlias(hash, (key) => this.clearSyncProcessKey(key));
|
|
1305
|
+
}
|
|
1306
|
+
clearSyncProcesses(hashes) {
|
|
1307
|
+
if (hashes.length === 0) {
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
const keys = new Set(hashes);
|
|
1311
|
+
const hashSet = new Set(hashes);
|
|
1312
|
+
const maybeAddAlias = (key) => {
|
|
1313
|
+
if (typeof key !== "bigint") {
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
const hash = this.coordinateToHash.get(key);
|
|
1317
|
+
if (hash != null && hashSet.has(hash)) {
|
|
1318
|
+
keys.add(key);
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
for (const key of this.syncInFlightQueue.keys()) {
|
|
1322
|
+
maybeAddAlias(key);
|
|
1323
|
+
}
|
|
1324
|
+
for (const map of this.syncInFlight.values()) {
|
|
1325
|
+
for (const key of map.keys()) {
|
|
1326
|
+
maybeAddAlias(key);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
for (const key of keys) {
|
|
899
1330
|
this.clearSyncProcessKey(key);
|
|
900
1331
|
}
|
|
901
1332
|
}
|
|
@@ -905,6 +1336,7 @@ export class SimpleSyncronizer {
|
|
|
905
1336
|
}
|
|
906
1337
|
clearSyncProcessPublicKeyHash(publicKeyHash) {
|
|
907
1338
|
this.syncInFlight.delete(publicKeyHash);
|
|
1339
|
+
this.recentlySentExchangeHeads.delete(publicKeyHash);
|
|
908
1340
|
const map = this.syncInFlightQueueInverted.get(publicKeyHash);
|
|
909
1341
|
if (map) {
|
|
910
1342
|
for (const hash of map) {
|