@peerbit/shared-log 16.0.31 → 16.0.32
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.map +1 -1
- package/dist/src/index.js +18 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/sync/dispatch-lifecycle.d.ts +5 -0
- package/dist/src/sync/dispatch-lifecycle.d.ts.map +1 -1
- package/dist/src/sync/dispatch-lifecycle.js +15 -0
- package/dist/src/sync/dispatch-lifecycle.js.map +1 -1
- package/dist/src/sync/index.d.ts +3 -1
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.d.ts +7 -1
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.js +132 -10
- package/dist/src/sync/rateless-iblt.js.map +1 -1
- package/dist/src/sync/simple.d.ts +5 -2
- package/dist/src/sync/simple.d.ts.map +1 -1
- package/dist/src/sync/simple.js +165 -119
- package/dist/src/sync/simple.js.map +1 -1
- package/package.json +12 -12
- package/src/index.ts +26 -6
- package/src/sync/dispatch-lifecycle.ts +20 -0
- package/src/sync/index.ts +3 -0
- package/src/sync/rateless-iblt.ts +169 -8
- package/src/sync/simple.ts +235 -151
package/dist/src/sync/simple.js
CHANGED
|
@@ -39,7 +39,7 @@ 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";
|
|
42
|
+
import { DispatchLifecycleRegistry, isSyncDispatchCancellation, } from "./dispatch-lifecycle.js";
|
|
43
43
|
import { PendingSyncStore, QUEUED_SYNC_ALIAS_REFRESH_PENDING, } from "./pending-sync-store.js";
|
|
44
44
|
import { emitSyncProfileDuration, syncProfileStart } from "./profile.js";
|
|
45
45
|
import { SyncPeerSlotRegistry, } from "./sync-peer-state.js";
|
|
@@ -702,13 +702,14 @@ export class SimpleSyncronizer {
|
|
|
702
702
|
filterRecentlySentExchangeHeads(hashes, peer) {
|
|
703
703
|
const peerHash = peer.hashcode();
|
|
704
704
|
const now = Date.now();
|
|
705
|
+
const stamp = { timestamp: now };
|
|
705
706
|
let recentlySent = this.recentlySentExchangeHeads.get(peerHash);
|
|
706
707
|
if (!recentlySent) {
|
|
707
708
|
recentlySent = new Map();
|
|
708
709
|
this.recentlySentExchangeHeads.set(peerHash, recentlySent);
|
|
709
710
|
}
|
|
710
|
-
for (const [hash,
|
|
711
|
-
if (now - timestamp > EXCHANGE_HEAD_RESPONSE_DEDUPE_TTL_MS) {
|
|
711
|
+
for (const [hash, previous] of recentlySent) {
|
|
712
|
+
if (now - previous.timestamp > EXCHANGE_HEAD_RESPONSE_DEDUPE_TTL_MS) {
|
|
712
713
|
recentlySent.delete(hash);
|
|
713
714
|
}
|
|
714
715
|
}
|
|
@@ -725,22 +726,25 @@ export class SimpleSyncronizer {
|
|
|
725
726
|
if (this.isEntryRecentlyKnownByPeer?.(hash, peerHash, RECENT_KNOWN_EXCHANGE_HEAD_SUPPRESSION_MS)) {
|
|
726
727
|
continue;
|
|
727
728
|
}
|
|
728
|
-
recentlySent.set(hash,
|
|
729
|
+
recentlySent.set(hash, stamp);
|
|
729
730
|
out.push(hash);
|
|
730
731
|
}
|
|
731
|
-
return
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
732
|
+
return {
|
|
733
|
+
hashes: out,
|
|
734
|
+
rollback: () => {
|
|
735
|
+
// A cancelled predecessor may settle after a fresh receive shipped
|
|
736
|
+
// the same hash. Only undo this exact attempt's dedupe ownership.
|
|
737
|
+
for (const hash of out) {
|
|
738
|
+
if (recentlySent.get(hash) === stamp) {
|
|
739
|
+
recentlySent.delete(hash);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (recentlySent.size === 0 &&
|
|
743
|
+
this.recentlySentExchangeHeads.get(peerHash) === recentlySent) {
|
|
744
|
+
this.recentlySentExchangeHeads.delete(peerHash);
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
};
|
|
744
748
|
}
|
|
745
749
|
getOrCreateSyncDispatchTargetEpoch(target) {
|
|
746
750
|
let epoch = this.syncDispatchTargetEpochs.get(target);
|
|
@@ -776,6 +780,7 @@ export class SimpleSyncronizer {
|
|
|
776
780
|
currentEpoch ??
|
|
777
781
|
(options?.createTargetEpochs === false ||
|
|
778
782
|
this.closed === true ||
|
|
783
|
+
callerSignal?.aborted === true ||
|
|
779
784
|
ownershipLifecycleController.signal.aborted
|
|
780
785
|
? undefined
|
|
781
786
|
: this.getOrCreateSyncDispatchTargetEpoch(target));
|
|
@@ -2043,8 +2048,8 @@ export class SimpleSyncronizer {
|
|
|
2043
2048
|
}
|
|
2044
2049
|
catch (error) {
|
|
2045
2050
|
reservation.release();
|
|
2046
|
-
if (!this.isSyncDispatchLifecycleActive(lifecycle) ||
|
|
2047
|
-
!this.isSyncDispatchLifecycleActive(lifecycle, target)) {
|
|
2051
|
+
if (isSyncDispatchCancellation(error, this.getSyncDispatchSignal(lifecycle, target), !this.isSyncDispatchLifecycleActive(lifecycle) ||
|
|
2052
|
+
!this.isSyncDispatchLifecycleActive(lifecycle, target))) {
|
|
2048
2053
|
break;
|
|
2049
2054
|
}
|
|
2050
2055
|
throw error;
|
|
@@ -2093,7 +2098,7 @@ export class SimpleSyncronizer {
|
|
|
2093
2098
|
sentMessages = await this.sendRawExchangeHeads(hashes, [to.hashcode()], { signal });
|
|
2094
2099
|
}
|
|
2095
2100
|
catch (error) {
|
|
2096
|
-
if (signal
|
|
2101
|
+
if (isSyncDispatchCancellation(error, signal)) {
|
|
2097
2102
|
return { messages: 0, fused: true };
|
|
2098
2103
|
}
|
|
2099
2104
|
throw error;
|
|
@@ -2118,7 +2123,7 @@ export class SimpleSyncronizer {
|
|
|
2118
2123
|
messages += 1;
|
|
2119
2124
|
}
|
|
2120
2125
|
catch (error) {
|
|
2121
|
-
if (signal
|
|
2126
|
+
if (isSyncDispatchCancellation(error, signal)) {
|
|
2122
2127
|
break;
|
|
2123
2128
|
}
|
|
2124
2129
|
throw error;
|
|
@@ -2135,7 +2140,7 @@ export class SimpleSyncronizer {
|
|
|
2135
2140
|
* exact advertised hash set.
|
|
2136
2141
|
*/
|
|
2137
2142
|
async shipAuthorizedMaybeSyncResponse(properties) {
|
|
2138
|
-
const hashes = this.filterRecentlySentExchangeHeads(properties.hashes, properties.from);
|
|
2143
|
+
const { hashes, rollback } = this.filterRecentlySentExchangeHeads(properties.hashes, properties.from);
|
|
2139
2144
|
try {
|
|
2140
2145
|
const shipped = await this.shipExchangeHeads(hashes, properties.from, canReceiveRawExchangeHeads(properties.response), properties.signal);
|
|
2141
2146
|
return {
|
|
@@ -2144,12 +2149,12 @@ export class SimpleSyncronizer {
|
|
|
2144
2149
|
};
|
|
2145
2150
|
}
|
|
2146
2151
|
catch (error) {
|
|
2147
|
-
|
|
2152
|
+
rollback();
|
|
2148
2153
|
throw error;
|
|
2149
2154
|
}
|
|
2150
2155
|
finally {
|
|
2151
2156
|
if (properties.signal.aborted) {
|
|
2152
|
-
|
|
2157
|
+
rollback();
|
|
2153
2158
|
}
|
|
2154
2159
|
}
|
|
2155
2160
|
}
|
|
@@ -2164,18 +2169,21 @@ export class SimpleSyncronizer {
|
|
|
2164
2169
|
let entries = 0;
|
|
2165
2170
|
let firstError;
|
|
2166
2171
|
for (const lease of properties.leases) {
|
|
2172
|
+
const signal = properties.signal
|
|
2173
|
+
? AbortSignal.any([lease.signal, properties.signal])
|
|
2174
|
+
: lease.signal;
|
|
2167
2175
|
let fulfilled = false;
|
|
2168
2176
|
try {
|
|
2169
2177
|
const shipped = await this.shipAuthorizedMaybeSyncResponse({
|
|
2170
2178
|
hashes: lease.hashes,
|
|
2171
2179
|
from: properties.from,
|
|
2172
2180
|
response: properties.response,
|
|
2173
|
-
signal
|
|
2181
|
+
signal,
|
|
2174
2182
|
});
|
|
2175
2183
|
messages += shipped.messages;
|
|
2176
2184
|
fused ||= shipped.fused;
|
|
2177
2185
|
entries += shipped.entries;
|
|
2178
|
-
fulfilled = !
|
|
2186
|
+
fulfilled = !signal.aborted;
|
|
2179
2187
|
}
|
|
2180
2188
|
catch (error) {
|
|
2181
2189
|
firstError ??= error;
|
|
@@ -2201,10 +2209,17 @@ export class SimpleSyncronizer {
|
|
|
2201
2209
|
}
|
|
2202
2210
|
return { messages, fused, entries };
|
|
2203
2211
|
}
|
|
2204
|
-
async onMessage(msg, context) {
|
|
2212
|
+
async onMessage(msg, context, options) {
|
|
2213
|
+
if (options?.signal?.aborted) {
|
|
2214
|
+
return (msg instanceof RequestMaybeSync ||
|
|
2215
|
+
msg instanceof ResponseMaybeSync ||
|
|
2216
|
+
msg instanceof ResponseMaybeSyncCapabilities ||
|
|
2217
|
+
msg instanceof RequestMaybeSyncCoordinate ||
|
|
2218
|
+
msg instanceof RequestMaybeSyncCoordinateCapabilities);
|
|
2219
|
+
}
|
|
2205
2220
|
const from = context.from;
|
|
2206
2221
|
if (msg instanceof RequestMaybeSync) {
|
|
2207
|
-
await this.queueSync(msg.hashes, from);
|
|
2222
|
+
await this.queueSync(msg.hashes, from, options);
|
|
2208
2223
|
return true;
|
|
2209
2224
|
}
|
|
2210
2225
|
else if (msg instanceof ResponseMaybeSync ||
|
|
@@ -2219,6 +2234,7 @@ export class SimpleSyncronizer {
|
|
|
2219
2234
|
leases: pending,
|
|
2220
2235
|
from,
|
|
2221
2236
|
response: msg,
|
|
2237
|
+
signal: options?.signal,
|
|
2222
2238
|
});
|
|
2223
2239
|
return true;
|
|
2224
2240
|
}
|
|
@@ -2234,7 +2250,7 @@ export class SimpleSyncronizer {
|
|
|
2234
2250
|
return true;
|
|
2235
2251
|
}
|
|
2236
2252
|
const { release: releaseLookup, row: slotRow } = lookupPermit;
|
|
2237
|
-
const lifecycle = this.captureSyncDispatchLifecycle([target]);
|
|
2253
|
+
const lifecycle = this.captureSyncDispatchLifecycle([target], options?.signal);
|
|
2238
2254
|
let lifecycleFinished = false;
|
|
2239
2255
|
const finishLifecycle = () => {
|
|
2240
2256
|
if (lifecycleFinished) {
|
|
@@ -2284,12 +2300,22 @@ export class SimpleSyncronizer {
|
|
|
2284
2300
|
let hashesToSend = [];
|
|
2285
2301
|
let messages = 0;
|
|
2286
2302
|
let fused = false;
|
|
2303
|
+
let rollback;
|
|
2304
|
+
const signal = this.getSyncDispatchSignal(lifecycle, target);
|
|
2287
2305
|
try {
|
|
2288
|
-
|
|
2306
|
+
({ hashes: hashesToSend, rollback } =
|
|
2307
|
+
this.filterRecentlySentExchangeHeads(hashes, from));
|
|
2289
2308
|
// dont set priority 1 here because this will block other messages that should higher priority
|
|
2290
|
-
({ messages, fused } = await this.shipExchangeHeads(hashesToSend, context.from, canReceiveRawExchangeHeads(msg),
|
|
2309
|
+
({ messages, fused } = await this.shipExchangeHeads(hashesToSend, context.from, canReceiveRawExchangeHeads(msg), signal));
|
|
2310
|
+
}
|
|
2311
|
+
catch (error) {
|
|
2312
|
+
rollback?.();
|
|
2313
|
+
throw error;
|
|
2291
2314
|
}
|
|
2292
2315
|
finally {
|
|
2316
|
+
if (signal.aborted) {
|
|
2317
|
+
rollback?.();
|
|
2318
|
+
}
|
|
2293
2319
|
releaseResponse();
|
|
2294
2320
|
if (profile) {
|
|
2295
2321
|
emitSyncProfileDuration(profile, exchangeStartedAt, {
|
|
@@ -2478,7 +2504,7 @@ export class SimpleSyncronizer {
|
|
|
2478
2504
|
this.pendingSync.clearPendingSyncExpiryTimerIfIdle();
|
|
2479
2505
|
}
|
|
2480
2506
|
async queueSync(keys, from, options) {
|
|
2481
|
-
if (this.closed === true || keys.length === 0) {
|
|
2507
|
+
if (this.closed === true || options?.signal?.aborted || keys.length === 0) {
|
|
2482
2508
|
return;
|
|
2483
2509
|
}
|
|
2484
2510
|
// A delayed timer must not let expired claims or admission reservations
|
|
@@ -2496,6 +2522,7 @@ export class SimpleSyncronizer {
|
|
|
2496
2522
|
const targetEpoch = this.getOrCreateSyncDispatchTargetEpoch(fromHash);
|
|
2497
2523
|
const ownershipLifecycleController = this.syncDispatchLifecycleController;
|
|
2498
2524
|
const isCapturedLifecycleActive = () => this.closed !== true &&
|
|
2525
|
+
options?.signal?.aborted !== true &&
|
|
2499
2526
|
this.syncDispatchLifecycleController === ownershipLifecycleController &&
|
|
2500
2527
|
!ownershipLifecycleController.signal.aborted &&
|
|
2501
2528
|
this.syncDispatchTargetEpochs.get(fromHash) === targetEpoch;
|
|
@@ -2565,6 +2592,7 @@ export class SimpleSyncronizer {
|
|
|
2565
2592
|
const dispatchableExistingRequestHashes = this.filterDispatchablePendingSyncClaims(existingRequestHashes, fromHash, targetEpoch);
|
|
2566
2593
|
const existingRequest = dispatchableExistingRequestHashes.length > 0
|
|
2567
2594
|
? this.requestSync(dispatchableExistingRequestHashes, [fromHash], {
|
|
2595
|
+
signal: options?.signal,
|
|
2568
2596
|
ownershipLifecycleController,
|
|
2569
2597
|
targetEpochs: new Map([[fromHash, targetEpoch]]),
|
|
2570
2598
|
createTargetEpochs: false,
|
|
@@ -2575,107 +2603,115 @@ export class SimpleSyncronizer {
|
|
|
2575
2603
|
// when a later lifecycle check returns before joining it.
|
|
2576
2604
|
void existingRequest?.catch(() => { });
|
|
2577
2605
|
const resolveKnownStartedAt = syncProfileStart(profile);
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
return;
|
|
2584
|
-
}
|
|
2585
|
-
if (profile) {
|
|
2586
|
-
emitSyncProfileDuration(profile, resolveKnownStartedAt, {
|
|
2587
|
-
name: "simple.queueSync.resolveKnown",
|
|
2588
|
-
entries: keysToCheck.length,
|
|
2589
|
-
count: knownKeys?.keys.size ?? 0,
|
|
2590
|
-
details: {
|
|
2591
|
-
checkedCoordinates: knownKeys?.checkedCoordinates === true,
|
|
2592
|
-
checkedHashes: knownKeys?.checkedHashes === true,
|
|
2593
|
-
skipCheck: options?.skipCheck === true,
|
|
2594
|
-
},
|
|
2595
|
-
});
|
|
2596
|
-
}
|
|
2597
|
-
if (keysToCheck.length > 0) {
|
|
2598
|
-
// A resolver/index lookup may have populated coordinateToHash while
|
|
2599
|
-
// it yielded. Refresh another fixed-size slice, never the full queue.
|
|
2600
|
-
this.refreshQueuedSyncCoordinateAliases();
|
|
2601
|
-
}
|
|
2602
|
-
const loopStartedAt = syncProfileStart(profile);
|
|
2603
|
-
for (let index = 0; index < keysToCheck.length; index += 1) {
|
|
2604
|
-
const key = keysToCheck[index];
|
|
2605
|
-
const identity = identitiesToCheck[index];
|
|
2606
|
+
const planning = (async () => {
|
|
2607
|
+
try {
|
|
2608
|
+
const knownKeys = options?.skipCheck === true || keysToCheck.length === 0
|
|
2609
|
+
? undefined
|
|
2610
|
+
: await this.resolveKnownSyncKeys(keysToCheck);
|
|
2606
2611
|
if (!isCapturedLifecycleActive()) {
|
|
2607
2612
|
return;
|
|
2608
2613
|
}
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2614
|
+
if (profile) {
|
|
2615
|
+
emitSyncProfileDuration(profile, resolveKnownStartedAt, {
|
|
2616
|
+
name: "simple.queueSync.resolveKnown",
|
|
2617
|
+
entries: keysToCheck.length,
|
|
2618
|
+
count: knownKeys?.keys.size ?? 0,
|
|
2619
|
+
details: {
|
|
2620
|
+
checkedCoordinates: knownKeys?.checkedCoordinates === true,
|
|
2621
|
+
checkedHashes: knownKeys?.checkedHashes === true,
|
|
2622
|
+
skipCheck: options?.skipCheck === true,
|
|
2623
|
+
},
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
if (keysToCheck.length > 0) {
|
|
2627
|
+
// A resolver/index lookup may have populated coordinateToHash while
|
|
2628
|
+
// it yielded. Refresh another fixed-size slice, never the full queue.
|
|
2629
|
+
this.refreshQueuedSyncCoordinateAliases();
|
|
2630
|
+
}
|
|
2631
|
+
const loopStartedAt = syncProfileStart(profile);
|
|
2632
|
+
for (let index = 0; index < keysToCheck.length; index += 1) {
|
|
2633
|
+
const key = keysToCheck[index];
|
|
2634
|
+
const identity = identitiesToCheck[index];
|
|
2635
|
+
if (!isCapturedLifecycleActive()) {
|
|
2613
2636
|
return;
|
|
2614
2637
|
}
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
const coordinateOrHash = queuedKeyResult ?? key;
|
|
2618
|
-
const inFlight = this.syncInFlightQueue.get(coordinateOrHash);
|
|
2619
|
-
if (inFlight) {
|
|
2620
|
-
if (!this.hasPendingSyncClaim(coordinateOrHash, fromHash)) {
|
|
2638
|
+
const queuedKeyResult = this.getQueuedSyncKeyForAdmission(key);
|
|
2639
|
+
if (queuedKeyResult === QUEUED_SYNC_ALIAS_REFRESH_PENDING) {
|
|
2621
2640
|
const consumption = this.consumePendingSyncAdmission(admission, identity);
|
|
2622
2641
|
if (consumption === "invalid") {
|
|
2623
2642
|
return;
|
|
2624
2643
|
}
|
|
2625
|
-
|
|
2644
|
+
continue;
|
|
2645
|
+
}
|
|
2646
|
+
const coordinateOrHash = queuedKeyResult ?? key;
|
|
2647
|
+
const inFlight = this.syncInFlightQueue.get(coordinateOrHash);
|
|
2648
|
+
if (inFlight) {
|
|
2649
|
+
if (!this.hasPendingSyncClaim(coordinateOrHash, fromHash)) {
|
|
2650
|
+
const consumption = this.consumePendingSyncAdmission(admission, identity);
|
|
2651
|
+
if (consumption === "invalid") {
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
if (consumption === "settled") {
|
|
2655
|
+
continue;
|
|
2656
|
+
}
|
|
2657
|
+
this.movePendingSyncKeyExpiryEarlier(coordinateOrHash, admission.expiresAt);
|
|
2658
|
+
const added = this.addPendingSyncClaim(coordinateOrHash, from, admission.expiresAt);
|
|
2659
|
+
if (added) {
|
|
2660
|
+
requestHashes.push(coordinateOrHash);
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
else {
|
|
2665
|
+
const has = options?.skipCheck !== true &&
|
|
2666
|
+
(await this.checkHasCoordinateOrHash(coordinateOrHash, knownKeys));
|
|
2667
|
+
if (!isCapturedLifecycleActive()) {
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2670
|
+
if (has) {
|
|
2671
|
+
this.clearPendingSyncAdmissionIdentity(identity);
|
|
2626
2672
|
continue;
|
|
2627
2673
|
}
|
|
2628
|
-
this.
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2674
|
+
const consumption = this.consumePendingSyncAdmission(admission, identity);
|
|
2675
|
+
if (consumption === "invalid") {
|
|
2676
|
+
return;
|
|
2677
|
+
}
|
|
2678
|
+
if (consumption === "settled") {
|
|
2679
|
+
continue;
|
|
2632
2680
|
}
|
|
2681
|
+
// Track the initial sender so we can retry if the first request is lost.
|
|
2682
|
+
this.addPendingSyncClaim(coordinateOrHash, from, admission.expiresAt);
|
|
2683
|
+
requestHashes.push(coordinateOrHash); // request immediately (first time we have seen this hash)
|
|
2633
2684
|
}
|
|
2634
2685
|
}
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
this.clearPendingSyncAdmissionIdentity(identity);
|
|
2643
|
-
continue;
|
|
2644
|
-
}
|
|
2645
|
-
const consumption = this.consumePendingSyncAdmission(admission, identity);
|
|
2646
|
-
if (consumption === "invalid") {
|
|
2647
|
-
return;
|
|
2648
|
-
}
|
|
2649
|
-
if (consumption === "settled") {
|
|
2650
|
-
continue;
|
|
2651
|
-
}
|
|
2652
|
-
// Track the initial sender so we can retry if the first request is lost.
|
|
2653
|
-
this.addPendingSyncClaim(coordinateOrHash, from, admission.expiresAt);
|
|
2654
|
-
requestHashes.push(coordinateOrHash); // request immediately (first time we have seen this hash)
|
|
2686
|
+
if (profile) {
|
|
2687
|
+
emitSyncProfileDuration(profile, loopStartedAt, {
|
|
2688
|
+
name: "simple.queueSync.plan",
|
|
2689
|
+
entries: keysToCheck.length,
|
|
2690
|
+
count: requestHashes.length,
|
|
2691
|
+
targets: 1,
|
|
2692
|
+
});
|
|
2655
2693
|
}
|
|
2694
|
+
// Persistent admission work is complete. Do not let an unrelated
|
|
2695
|
+
// blocked transport send retain unused quota.
|
|
2696
|
+
this.releasePendingSyncAdmission(admission);
|
|
2697
|
+
const dispatchableRequestHashes = this.filterDispatchablePendingSyncClaims(requestHashes, fromHash, targetEpoch);
|
|
2698
|
+
dispatchableRequestHashes.length > 0 &&
|
|
2699
|
+
(await this.requestSync(dispatchableRequestHashes, [fromHash], {
|
|
2700
|
+
signal: options?.signal,
|
|
2701
|
+
ownershipLifecycleController,
|
|
2702
|
+
targetEpochs: new Map([[fromHash, targetEpoch]]),
|
|
2703
|
+
createTargetEpochs: false,
|
|
2704
|
+
}));
|
|
2656
2705
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
name: "simple.queueSync.plan",
|
|
2660
|
-
entries: keysToCheck.length,
|
|
2661
|
-
count: requestHashes.length,
|
|
2662
|
-
targets: 1,
|
|
2663
|
-
});
|
|
2706
|
+
finally {
|
|
2707
|
+
this.releasePendingSyncAdmission(admission);
|
|
2664
2708
|
}
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
ownershipLifecycleController,
|
|
2672
|
-
targetEpochs: new Map([[fromHash, targetEpoch]]),
|
|
2673
|
-
createTargetEpochs: false,
|
|
2674
|
-
}));
|
|
2675
|
-
await existingRequest;
|
|
2676
|
-
}
|
|
2677
|
-
finally {
|
|
2678
|
-
this.releasePendingSyncAdmission(admission);
|
|
2709
|
+
})();
|
|
2710
|
+
// Admission quota covers the finished lookup only. The eager dispatch
|
|
2711
|
+
// retains its own physical lifecycle, and every receive exit joins it.
|
|
2712
|
+
const results = await Promise.allSettled([planning, existingRequest]);
|
|
2713
|
+
const errors = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
2714
|
+
try {
|
|
2679
2715
|
if (profile) {
|
|
2680
2716
|
emitSyncProfileDuration(profile, startedAt, {
|
|
2681
2717
|
name: "simple.queueSync",
|
|
@@ -2689,13 +2725,23 @@ export class SimpleSyncronizer {
|
|
|
2689
2725
|
});
|
|
2690
2726
|
}
|
|
2691
2727
|
}
|
|
2728
|
+
catch (error) {
|
|
2729
|
+
errors.push(error);
|
|
2730
|
+
}
|
|
2731
|
+
if (errors.length === 1)
|
|
2732
|
+
throw errors[0];
|
|
2733
|
+
if (errors.length > 1) {
|
|
2734
|
+
throw new AggregateError(errors, "sync lookup and eager response failed", {
|
|
2735
|
+
cause: errors[0],
|
|
2736
|
+
});
|
|
2737
|
+
}
|
|
2692
2738
|
}
|
|
2693
2739
|
async requestSync(hashes, to, options) {
|
|
2694
2740
|
const targets = [...new Set(to)];
|
|
2695
2741
|
if (hashes.length === 0 || targets.length === 0) {
|
|
2696
2742
|
return;
|
|
2697
2743
|
}
|
|
2698
|
-
const lifecycle = this.captureSyncDispatchLifecycle(targets,
|
|
2744
|
+
const lifecycle = this.captureSyncDispatchLifecycle(targets, options?.signal, {
|
|
2699
2745
|
ownershipLifecycleController: options?.ownershipLifecycleController,
|
|
2700
2746
|
targetEpochs: options?.targetEpochs,
|
|
2701
2747
|
createTargetEpochs: options?.createTargetEpochs,
|
|
@@ -2755,7 +2801,7 @@ export class SimpleSyncronizer {
|
|
|
2755
2801
|
coordinateMessages += 1;
|
|
2756
2802
|
}
|
|
2757
2803
|
catch (error) {
|
|
2758
|
-
if (!this.isSyncDispatchLifecycleActive(lifecycle, target)) {
|
|
2804
|
+
if (isSyncDispatchCancellation(error, this.getSyncDispatchSignal(lifecycle, target), !this.isSyncDispatchLifecycleActive(lifecycle, target))) {
|
|
2759
2805
|
break;
|
|
2760
2806
|
}
|
|
2761
2807
|
throw error;
|
|
@@ -2788,7 +2834,7 @@ export class SimpleSyncronizer {
|
|
|
2788
2834
|
stringMessages += 1;
|
|
2789
2835
|
}
|
|
2790
2836
|
catch (error) {
|
|
2791
|
-
if (!this.isSyncDispatchLifecycleActive(lifecycle, target)) {
|
|
2837
|
+
if (isSyncDispatchCancellation(error, this.getSyncDispatchSignal(lifecycle, target), !this.isSyncDispatchLifecycleActive(lifecycle, target))) {
|
|
2792
2838
|
break;
|
|
2793
2839
|
}
|
|
2794
2840
|
throw error;
|