@peerbit/shared-log 16.0.31 → 16.0.33
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/README.md +26 -0
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +4 -2
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -5
- 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 +16 -16
- package/src/errors.ts +4 -2
- package/src/index.ts +27 -7
- 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/src/sync/simple.ts
CHANGED
|
@@ -20,7 +20,10 @@ import {
|
|
|
20
20
|
} from "../exchange-heads.js";
|
|
21
21
|
import { TransportMessage } from "../message.js";
|
|
22
22
|
import type { EntryReplicated } from "../ranges.js";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
DispatchLifecycleRegistry,
|
|
25
|
+
isSyncDispatchCancellation,
|
|
26
|
+
} from "./dispatch-lifecycle.js";
|
|
24
27
|
import type {
|
|
25
28
|
HashSymbolHashListResolver,
|
|
26
29
|
HashSymbolResolver,
|
|
@@ -586,7 +589,10 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
586
589
|
) => boolean;
|
|
587
590
|
private peerSupportsRawExchangeHeads?: (peer: string) => boolean;
|
|
588
591
|
private sendRawExchangeHeads?: RawExchangeHeadsSender;
|
|
589
|
-
private recentlySentExchangeHeads: Map<
|
|
592
|
+
private recentlySentExchangeHeads: Map<
|
|
593
|
+
string,
|
|
594
|
+
Map<string, { timestamp: number }>
|
|
595
|
+
>;
|
|
590
596
|
private pendingMaybeSyncResponses: Map<
|
|
591
597
|
string,
|
|
592
598
|
Map<string, PendingMaybeSyncResponseAuthorization>
|
|
@@ -774,16 +780,17 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
774
780
|
private filterRecentlySentExchangeHeads(
|
|
775
781
|
hashes: Iterable<string>,
|
|
776
782
|
peer: PublicSignKey,
|
|
777
|
-
): string[] {
|
|
783
|
+
): { hashes: string[]; rollback: () => void } {
|
|
778
784
|
const peerHash = peer.hashcode();
|
|
779
785
|
const now = Date.now();
|
|
786
|
+
const stamp = { timestamp: now };
|
|
780
787
|
let recentlySent = this.recentlySentExchangeHeads.get(peerHash);
|
|
781
788
|
if (!recentlySent) {
|
|
782
789
|
recentlySent = new Map();
|
|
783
790
|
this.recentlySentExchangeHeads.set(peerHash, recentlySent);
|
|
784
791
|
}
|
|
785
|
-
for (const [hash,
|
|
786
|
-
if (now - timestamp > EXCHANGE_HEAD_RESPONSE_DEDUPE_TTL_MS) {
|
|
792
|
+
for (const [hash, previous] of recentlySent) {
|
|
793
|
+
if (now - previous.timestamp > EXCHANGE_HEAD_RESPONSE_DEDUPE_TTL_MS) {
|
|
787
794
|
recentlySent.delete(hash);
|
|
788
795
|
}
|
|
789
796
|
}
|
|
@@ -806,26 +813,27 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
806
813
|
) {
|
|
807
814
|
continue;
|
|
808
815
|
}
|
|
809
|
-
recentlySent.set(hash,
|
|
816
|
+
recentlySent.set(hash, stamp);
|
|
810
817
|
out.push(hash);
|
|
811
818
|
}
|
|
812
|
-
return
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
819
|
+
return {
|
|
820
|
+
hashes: out,
|
|
821
|
+
rollback: () => {
|
|
822
|
+
// A cancelled predecessor may settle after a fresh receive shipped
|
|
823
|
+
// the same hash. Only undo this exact attempt's dedupe ownership.
|
|
824
|
+
for (const hash of out) {
|
|
825
|
+
if (recentlySent.get(hash) === stamp) {
|
|
826
|
+
recentlySent.delete(hash);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (
|
|
830
|
+
recentlySent.size === 0 &&
|
|
831
|
+
this.recentlySentExchangeHeads.get(peerHash) === recentlySent
|
|
832
|
+
) {
|
|
833
|
+
this.recentlySentExchangeHeads.delete(peerHash);
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
};
|
|
829
837
|
}
|
|
830
838
|
|
|
831
839
|
private getOrCreateSyncDispatchTargetEpoch(
|
|
@@ -881,6 +889,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
881
889
|
currentEpoch ??
|
|
882
890
|
(options?.createTargetEpochs === false ||
|
|
883
891
|
this.closed === true ||
|
|
892
|
+
callerSignal?.aborted === true ||
|
|
884
893
|
ownershipLifecycleController.signal.aborted
|
|
885
894
|
? undefined
|
|
886
895
|
: this.getOrCreateSyncDispatchTargetEpoch(target));
|
|
@@ -2530,8 +2539,12 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2530
2539
|
} catch (error) {
|
|
2531
2540
|
reservation.release();
|
|
2532
2541
|
if (
|
|
2533
|
-
|
|
2534
|
-
|
|
2542
|
+
isSyncDispatchCancellation(
|
|
2543
|
+
error,
|
|
2544
|
+
this.getSyncDispatchSignal(lifecycle, target),
|
|
2545
|
+
!this.isSyncDispatchLifecycleActive(lifecycle) ||
|
|
2546
|
+
!this.isSyncDispatchLifecycleActive(lifecycle, target),
|
|
2547
|
+
)
|
|
2535
2548
|
) {
|
|
2536
2549
|
break;
|
|
2537
2550
|
}
|
|
@@ -2593,7 +2606,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2593
2606
|
{ signal },
|
|
2594
2607
|
);
|
|
2595
2608
|
} catch (error) {
|
|
2596
|
-
if (signal
|
|
2609
|
+
if (isSyncDispatchCancellation(error, signal)) {
|
|
2597
2610
|
return { messages: 0, fused: true };
|
|
2598
2611
|
}
|
|
2599
2612
|
throw error;
|
|
@@ -2621,7 +2634,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2621
2634
|
});
|
|
2622
2635
|
messages += 1;
|
|
2623
2636
|
} catch (error) {
|
|
2624
|
-
if (signal
|
|
2637
|
+
if (isSyncDispatchCancellation(error, signal)) {
|
|
2625
2638
|
break;
|
|
2626
2639
|
}
|
|
2627
2640
|
throw error;
|
|
@@ -2644,7 +2657,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2644
2657
|
response: ResponseMaybeSync | ResponseMaybeSyncCapabilities;
|
|
2645
2658
|
signal: AbortSignal;
|
|
2646
2659
|
}): Promise<{ messages: number; fused: boolean; entries: number }> {
|
|
2647
|
-
const hashes = this.filterRecentlySentExchangeHeads(
|
|
2660
|
+
const { hashes, rollback } = this.filterRecentlySentExchangeHeads(
|
|
2648
2661
|
properties.hashes,
|
|
2649
2662
|
properties.from,
|
|
2650
2663
|
);
|
|
@@ -2660,11 +2673,11 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2660
2673
|
entries: hashes.length,
|
|
2661
2674
|
};
|
|
2662
2675
|
} catch (error) {
|
|
2663
|
-
|
|
2676
|
+
rollback();
|
|
2664
2677
|
throw error;
|
|
2665
2678
|
} finally {
|
|
2666
2679
|
if (properties.signal.aborted) {
|
|
2667
|
-
|
|
2680
|
+
rollback();
|
|
2668
2681
|
}
|
|
2669
2682
|
}
|
|
2670
2683
|
}
|
|
@@ -2673,6 +2686,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2673
2686
|
leases: AuthorizedMaybeSyncResponseLease[];
|
|
2674
2687
|
from: PublicSignKey;
|
|
2675
2688
|
response: ResponseMaybeSync | ResponseMaybeSyncCapabilities;
|
|
2689
|
+
signal?: AbortSignal;
|
|
2676
2690
|
source?: string;
|
|
2677
2691
|
}): Promise<{ messages: number; fused: boolean; entries: number }> {
|
|
2678
2692
|
if (properties.leases.length === 0) {
|
|
@@ -2685,18 +2699,21 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2685
2699
|
let entries = 0;
|
|
2686
2700
|
let firstError: unknown;
|
|
2687
2701
|
for (const lease of properties.leases) {
|
|
2702
|
+
const signal = properties.signal
|
|
2703
|
+
? AbortSignal.any([lease.signal, properties.signal])
|
|
2704
|
+
: lease.signal;
|
|
2688
2705
|
let fulfilled = false;
|
|
2689
2706
|
try {
|
|
2690
2707
|
const shipped = await this.shipAuthorizedMaybeSyncResponse({
|
|
2691
2708
|
hashes: lease.hashes,
|
|
2692
2709
|
from: properties.from,
|
|
2693
2710
|
response: properties.response,
|
|
2694
|
-
signal
|
|
2711
|
+
signal,
|
|
2695
2712
|
});
|
|
2696
2713
|
messages += shipped.messages;
|
|
2697
2714
|
fused ||= shipped.fused;
|
|
2698
2715
|
entries += shipped.entries;
|
|
2699
|
-
fulfilled = !
|
|
2716
|
+
fulfilled = !signal.aborted;
|
|
2700
2717
|
} catch (error) {
|
|
2701
2718
|
firstError ??= error;
|
|
2702
2719
|
} finally {
|
|
@@ -2724,10 +2741,20 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2724
2741
|
async onMessage(
|
|
2725
2742
|
msg: TransportMessage,
|
|
2726
2743
|
context: RequestContext,
|
|
2744
|
+
options?: { signal?: AbortSignal },
|
|
2727
2745
|
): Promise<boolean> {
|
|
2746
|
+
if (options?.signal?.aborted) {
|
|
2747
|
+
return (
|
|
2748
|
+
msg instanceof RequestMaybeSync ||
|
|
2749
|
+
msg instanceof ResponseMaybeSync ||
|
|
2750
|
+
msg instanceof ResponseMaybeSyncCapabilities ||
|
|
2751
|
+
msg instanceof RequestMaybeSyncCoordinate ||
|
|
2752
|
+
msg instanceof RequestMaybeSyncCoordinateCapabilities
|
|
2753
|
+
);
|
|
2754
|
+
}
|
|
2728
2755
|
const from = context.from!;
|
|
2729
2756
|
if (msg instanceof RequestMaybeSync) {
|
|
2730
|
-
await this.queueSync(msg.hashes, from);
|
|
2757
|
+
await this.queueSync(msg.hashes, from, options);
|
|
2731
2758
|
return true;
|
|
2732
2759
|
} else if (
|
|
2733
2760
|
msg instanceof ResponseMaybeSync ||
|
|
@@ -2744,6 +2771,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2744
2771
|
leases: pending,
|
|
2745
2772
|
from,
|
|
2746
2773
|
response: msg,
|
|
2774
|
+
signal: options?.signal,
|
|
2747
2775
|
});
|
|
2748
2776
|
return true;
|
|
2749
2777
|
} else if (
|
|
@@ -2762,7 +2790,10 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2762
2790
|
return true;
|
|
2763
2791
|
}
|
|
2764
2792
|
const { release: releaseLookup, row: slotRow } = lookupPermit;
|
|
2765
|
-
const lifecycle = this.captureSyncDispatchLifecycle(
|
|
2793
|
+
const lifecycle = this.captureSyncDispatchLifecycle(
|
|
2794
|
+
[target],
|
|
2795
|
+
options?.signal,
|
|
2796
|
+
);
|
|
2766
2797
|
let lifecycleFinished = false;
|
|
2767
2798
|
const finishLifecycle = () => {
|
|
2768
2799
|
if (lifecycleFinished) {
|
|
@@ -2820,16 +2851,25 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2820
2851
|
let hashesToSend: string[] = [];
|
|
2821
2852
|
let messages = 0;
|
|
2822
2853
|
let fused = false;
|
|
2854
|
+
let rollback: (() => void) | undefined;
|
|
2855
|
+
const signal = this.getSyncDispatchSignal(lifecycle, target);
|
|
2823
2856
|
try {
|
|
2824
|
-
|
|
2857
|
+
({ hashes: hashesToSend, rollback } =
|
|
2858
|
+
this.filterRecentlySentExchangeHeads(hashes, from));
|
|
2825
2859
|
// dont set priority 1 here because this will block other messages that should higher priority
|
|
2826
2860
|
({ messages, fused } = await this.shipExchangeHeads(
|
|
2827
2861
|
hashesToSend,
|
|
2828
2862
|
context.from!,
|
|
2829
2863
|
canReceiveRawExchangeHeads(msg),
|
|
2830
|
-
|
|
2864
|
+
signal,
|
|
2831
2865
|
));
|
|
2866
|
+
} catch (error) {
|
|
2867
|
+
rollback?.();
|
|
2868
|
+
throw error;
|
|
2832
2869
|
} finally {
|
|
2870
|
+
if (signal.aborted) {
|
|
2871
|
+
rollback?.();
|
|
2872
|
+
}
|
|
2833
2873
|
releaseResponse();
|
|
2834
2874
|
if (profile) {
|
|
2835
2875
|
emitSyncProfileDuration(profile, exchangeStartedAt, {
|
|
@@ -3095,9 +3135,9 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3095
3135
|
async queueSync(
|
|
3096
3136
|
keys: SyncableKey[],
|
|
3097
3137
|
from: PublicSignKey,
|
|
3098
|
-
options?: { skipCheck?: boolean },
|
|
3138
|
+
options?: { skipCheck?: boolean; signal?: AbortSignal },
|
|
3099
3139
|
) {
|
|
3100
|
-
if (this.closed === true || keys.length === 0) {
|
|
3140
|
+
if (this.closed === true || options?.signal?.aborted || keys.length === 0) {
|
|
3101
3141
|
return;
|
|
3102
3142
|
}
|
|
3103
3143
|
// A delayed timer must not let expired claims or admission reservations
|
|
@@ -3124,6 +3164,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3124
3164
|
const ownershipLifecycleController = this.syncDispatchLifecycleController;
|
|
3125
3165
|
const isCapturedLifecycleActive = () =>
|
|
3126
3166
|
this.closed !== true &&
|
|
3167
|
+
options?.signal?.aborted !== true &&
|
|
3127
3168
|
this.syncDispatchLifecycleController === ownershipLifecycleController &&
|
|
3128
3169
|
!ownershipLifecycleController.signal.aborted &&
|
|
3129
3170
|
this.syncDispatchTargetEpochs.get(fromHash) === targetEpoch;
|
|
@@ -3219,6 +3260,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3219
3260
|
const existingRequest =
|
|
3220
3261
|
dispatchableExistingRequestHashes.length > 0
|
|
3221
3262
|
? this.requestSync(dispatchableExistingRequestHashes, [fromHash], {
|
|
3263
|
+
signal: options?.signal,
|
|
3222
3264
|
ownershipLifecycleController,
|
|
3223
3265
|
targetEpochs: new Map([[fromHash, targetEpoch]]),
|
|
3224
3266
|
createTargetEpochs: false,
|
|
@@ -3229,54 +3271,42 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3229
3271
|
// when a later lifecycle check returns before joining it.
|
|
3230
3272
|
void existingRequest?.catch(() => {});
|
|
3231
3273
|
const resolveKnownStartedAt = syncProfileStart(profile);
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
return;
|
|
3239
|
-
}
|
|
3240
|
-
if (profile) {
|
|
3241
|
-
emitSyncProfileDuration(profile, resolveKnownStartedAt, {
|
|
3242
|
-
name: "simple.queueSync.resolveKnown",
|
|
3243
|
-
entries: keysToCheck.length,
|
|
3244
|
-
count: knownKeys?.keys.size ?? 0,
|
|
3245
|
-
details: {
|
|
3246
|
-
checkedCoordinates: knownKeys?.checkedCoordinates === true,
|
|
3247
|
-
checkedHashes: knownKeys?.checkedHashes === true,
|
|
3248
|
-
skipCheck: options?.skipCheck === true,
|
|
3249
|
-
},
|
|
3250
|
-
});
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
|
-
if (keysToCheck.length > 0) {
|
|
3254
|
-
// A resolver/index lookup may have populated coordinateToHash while
|
|
3255
|
-
// it yielded. Refresh another fixed-size slice, never the full queue.
|
|
3256
|
-
this.refreshQueuedSyncCoordinateAliases();
|
|
3257
|
-
}
|
|
3258
|
-
const loopStartedAt = syncProfileStart(profile);
|
|
3259
|
-
for (let index = 0; index < keysToCheck.length; index += 1) {
|
|
3260
|
-
const key = keysToCheck[index]!;
|
|
3261
|
-
const identity = identitiesToCheck[index]!;
|
|
3274
|
+
const planning = (async () => {
|
|
3275
|
+
try {
|
|
3276
|
+
const knownKeys =
|
|
3277
|
+
options?.skipCheck === true || keysToCheck.length === 0
|
|
3278
|
+
? undefined
|
|
3279
|
+
: await this.resolveKnownSyncKeys(keysToCheck);
|
|
3262
3280
|
if (!isCapturedLifecycleActive()) {
|
|
3263
3281
|
return;
|
|
3264
3282
|
}
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3283
|
+
if (profile) {
|
|
3284
|
+
emitSyncProfileDuration(profile, resolveKnownStartedAt, {
|
|
3285
|
+
name: "simple.queueSync.resolveKnown",
|
|
3286
|
+
entries: keysToCheck.length,
|
|
3287
|
+
count: knownKeys?.keys.size ?? 0,
|
|
3288
|
+
details: {
|
|
3289
|
+
checkedCoordinates: knownKeys?.checkedCoordinates === true,
|
|
3290
|
+
checkedHashes: knownKeys?.checkedHashes === true,
|
|
3291
|
+
skipCheck: options?.skipCheck === true,
|
|
3292
|
+
},
|
|
3293
|
+
});
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
if (keysToCheck.length > 0) {
|
|
3297
|
+
// A resolver/index lookup may have populated coordinateToHash while
|
|
3298
|
+
// it yielded. Refresh another fixed-size slice, never the full queue.
|
|
3299
|
+
this.refreshQueuedSyncCoordinateAliases();
|
|
3300
|
+
}
|
|
3301
|
+
const loopStartedAt = syncProfileStart(profile);
|
|
3302
|
+
for (let index = 0; index < keysToCheck.length; index += 1) {
|
|
3303
|
+
const key = keysToCheck[index]!;
|
|
3304
|
+
const identity = identitiesToCheck[index]!;
|
|
3305
|
+
if (!isCapturedLifecycleActive()) {
|
|
3272
3306
|
return;
|
|
3273
3307
|
}
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
const coordinateOrHash = queuedKeyResult ?? key;
|
|
3277
|
-
const inFlight = this.syncInFlightQueue.get(coordinateOrHash);
|
|
3278
|
-
if (inFlight) {
|
|
3279
|
-
if (!this.hasPendingSyncClaim(coordinateOrHash, fromHash)) {
|
|
3308
|
+
const queuedKeyResult = this.getQueuedSyncKeyForAdmission(key);
|
|
3309
|
+
if (queuedKeyResult === QUEUED_SYNC_ALIAS_REFRESH_PENDING) {
|
|
3280
3310
|
const consumption = this.consumePendingSyncAdmission(
|
|
3281
3311
|
admission!,
|
|
3282
3312
|
identity,
|
|
@@ -3284,79 +3314,104 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3284
3314
|
if (consumption === "invalid") {
|
|
3285
3315
|
return;
|
|
3286
3316
|
}
|
|
3287
|
-
|
|
3317
|
+
continue;
|
|
3318
|
+
}
|
|
3319
|
+
const coordinateOrHash = queuedKeyResult ?? key;
|
|
3320
|
+
const inFlight = this.syncInFlightQueue.get(coordinateOrHash);
|
|
3321
|
+
if (inFlight) {
|
|
3322
|
+
if (!this.hasPendingSyncClaim(coordinateOrHash, fromHash)) {
|
|
3323
|
+
const consumption = this.consumePendingSyncAdmission(
|
|
3324
|
+
admission!,
|
|
3325
|
+
identity,
|
|
3326
|
+
);
|
|
3327
|
+
if (consumption === "invalid") {
|
|
3328
|
+
return;
|
|
3329
|
+
}
|
|
3330
|
+
if (consumption === "settled") {
|
|
3331
|
+
continue;
|
|
3332
|
+
}
|
|
3333
|
+
this.movePendingSyncKeyExpiryEarlier(
|
|
3334
|
+
coordinateOrHash,
|
|
3335
|
+
admission!.expiresAt,
|
|
3336
|
+
);
|
|
3337
|
+
const added = this.addPendingSyncClaim(
|
|
3338
|
+
coordinateOrHash,
|
|
3339
|
+
from,
|
|
3340
|
+
admission!.expiresAt,
|
|
3341
|
+
);
|
|
3342
|
+
if (added) {
|
|
3343
|
+
requestHashes.push(coordinateOrHash);
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
} else {
|
|
3347
|
+
const has =
|
|
3348
|
+
options?.skipCheck !== true &&
|
|
3349
|
+
(await this.checkHasCoordinateOrHash(
|
|
3350
|
+
coordinateOrHash,
|
|
3351
|
+
knownKeys,
|
|
3352
|
+
));
|
|
3353
|
+
if (!isCapturedLifecycleActive()) {
|
|
3354
|
+
return;
|
|
3355
|
+
}
|
|
3356
|
+
if (has) {
|
|
3357
|
+
this.clearPendingSyncAdmissionIdentity(identity);
|
|
3288
3358
|
continue;
|
|
3289
3359
|
}
|
|
3290
|
-
this.
|
|
3291
|
-
|
|
3292
|
-
|
|
3360
|
+
const consumption = this.consumePendingSyncAdmission(
|
|
3361
|
+
admission!,
|
|
3362
|
+
identity,
|
|
3293
3363
|
);
|
|
3294
|
-
|
|
3364
|
+
if (consumption === "invalid") {
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3367
|
+
if (consumption === "settled") {
|
|
3368
|
+
continue;
|
|
3369
|
+
}
|
|
3370
|
+
// Track the initial sender so we can retry if the first request is lost.
|
|
3371
|
+
this.addPendingSyncClaim(
|
|
3295
3372
|
coordinateOrHash,
|
|
3296
3373
|
from,
|
|
3297
3374
|
admission!.expiresAt,
|
|
3298
3375
|
);
|
|
3299
|
-
|
|
3300
|
-
requestHashes.push(coordinateOrHash);
|
|
3301
|
-
}
|
|
3302
|
-
}
|
|
3303
|
-
} else {
|
|
3304
|
-
const has =
|
|
3305
|
-
options?.skipCheck !== true &&
|
|
3306
|
-
(await this.checkHasCoordinateOrHash(coordinateOrHash, knownKeys));
|
|
3307
|
-
if (!isCapturedLifecycleActive()) {
|
|
3308
|
-
return;
|
|
3309
|
-
}
|
|
3310
|
-
if (has) {
|
|
3311
|
-
this.clearPendingSyncAdmissionIdentity(identity);
|
|
3312
|
-
continue;
|
|
3313
|
-
}
|
|
3314
|
-
const consumption = this.consumePendingSyncAdmission(
|
|
3315
|
-
admission!,
|
|
3316
|
-
identity,
|
|
3317
|
-
);
|
|
3318
|
-
if (consumption === "invalid") {
|
|
3319
|
-
return;
|
|
3320
|
-
}
|
|
3321
|
-
if (consumption === "settled") {
|
|
3322
|
-
continue;
|
|
3376
|
+
requestHashes.push(coordinateOrHash); // request immediately (first time we have seen this hash)
|
|
3323
3377
|
}
|
|
3324
|
-
// Track the initial sender so we can retry if the first request is lost.
|
|
3325
|
-
this.addPendingSyncClaim(
|
|
3326
|
-
coordinateOrHash,
|
|
3327
|
-
from,
|
|
3328
|
-
admission!.expiresAt,
|
|
3329
|
-
);
|
|
3330
|
-
requestHashes.push(coordinateOrHash); // request immediately (first time we have seen this hash)
|
|
3331
3378
|
}
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
}
|
|
3340
|
-
}
|
|
3379
|
+
if (profile) {
|
|
3380
|
+
emitSyncProfileDuration(profile, loopStartedAt, {
|
|
3381
|
+
name: "simple.queueSync.plan",
|
|
3382
|
+
entries: keysToCheck.length,
|
|
3383
|
+
count: requestHashes.length,
|
|
3384
|
+
targets: 1,
|
|
3385
|
+
});
|
|
3386
|
+
}
|
|
3341
3387
|
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3388
|
+
// Persistent admission work is complete. Do not let an unrelated
|
|
3389
|
+
// blocked transport send retain unused quota.
|
|
3390
|
+
this.releasePendingSyncAdmission(admission);
|
|
3391
|
+
const dispatchableRequestHashes =
|
|
3392
|
+
this.filterDispatchablePendingSyncClaims(
|
|
3393
|
+
requestHashes,
|
|
3394
|
+
fromHash,
|
|
3395
|
+
targetEpoch,
|
|
3396
|
+
);
|
|
3397
|
+
dispatchableRequestHashes.length > 0 &&
|
|
3398
|
+
(await this.requestSync(dispatchableRequestHashes, [fromHash], {
|
|
3399
|
+
signal: options?.signal,
|
|
3400
|
+
ownershipLifecycleController,
|
|
3401
|
+
targetEpochs: new Map([[fromHash, targetEpoch]]),
|
|
3402
|
+
createTargetEpochs: false,
|
|
3403
|
+
}));
|
|
3404
|
+
} finally {
|
|
3405
|
+
this.releasePendingSyncAdmission(admission);
|
|
3406
|
+
}
|
|
3407
|
+
})();
|
|
3408
|
+
// Admission quota covers the finished lookup only. The eager dispatch
|
|
3409
|
+
// retains its own physical lifecycle, and every receive exit joins it.
|
|
3410
|
+
const results = await Promise.allSettled([planning, existingRequest]);
|
|
3411
|
+
const errors = results.flatMap((result) =>
|
|
3412
|
+
result.status === "rejected" ? [result.reason] : [],
|
|
3413
|
+
);
|
|
3414
|
+
try {
|
|
3360
3415
|
if (profile) {
|
|
3361
3416
|
emitSyncProfileDuration(profile, startedAt, {
|
|
3362
3417
|
name: "simple.queueSync",
|
|
@@ -3369,6 +3424,18 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3369
3424
|
},
|
|
3370
3425
|
});
|
|
3371
3426
|
}
|
|
3427
|
+
} catch (error) {
|
|
3428
|
+
errors.push(error);
|
|
3429
|
+
}
|
|
3430
|
+
if (errors.length === 1) throw errors[0];
|
|
3431
|
+
if (errors.length > 1) {
|
|
3432
|
+
throw new AggregateError(
|
|
3433
|
+
errors,
|
|
3434
|
+
"sync lookup and eager response failed",
|
|
3435
|
+
{
|
|
3436
|
+
cause: errors[0],
|
|
3437
|
+
},
|
|
3438
|
+
);
|
|
3372
3439
|
}
|
|
3373
3440
|
}
|
|
3374
3441
|
|
|
@@ -3376,6 +3443,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3376
3443
|
hashes: SyncableKey[],
|
|
3377
3444
|
to: Set<string> | string[],
|
|
3378
3445
|
options?: {
|
|
3446
|
+
signal?: AbortSignal;
|
|
3379
3447
|
ownershipLifecycleController?: AbortController;
|
|
3380
3448
|
targetEpochs?: Map<string, SyncDispatchTargetEpoch>;
|
|
3381
3449
|
createTargetEpochs?: boolean;
|
|
@@ -3385,11 +3453,15 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3385
3453
|
if (hashes.length === 0 || targets.length === 0) {
|
|
3386
3454
|
return;
|
|
3387
3455
|
}
|
|
3388
|
-
const lifecycle = this.captureSyncDispatchLifecycle(
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3456
|
+
const lifecycle = this.captureSyncDispatchLifecycle(
|
|
3457
|
+
targets,
|
|
3458
|
+
options?.signal,
|
|
3459
|
+
{
|
|
3460
|
+
ownershipLifecycleController: options?.ownershipLifecycleController,
|
|
3461
|
+
targetEpochs: options?.targetEpochs,
|
|
3462
|
+
createTargetEpochs: options?.createTargetEpochs,
|
|
3463
|
+
},
|
|
3464
|
+
);
|
|
3393
3465
|
const profile = this.syncOptions?.profile;
|
|
3394
3466
|
const startedAt = syncProfileStart(profile);
|
|
3395
3467
|
let coordinateMessages = 0;
|
|
@@ -3453,7 +3525,13 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3453
3525
|
);
|
|
3454
3526
|
coordinateMessages += 1;
|
|
3455
3527
|
} catch (error) {
|
|
3456
|
-
if (
|
|
3528
|
+
if (
|
|
3529
|
+
isSyncDispatchCancellation(
|
|
3530
|
+
error,
|
|
3531
|
+
this.getSyncDispatchSignal(lifecycle, target),
|
|
3532
|
+
!this.isSyncDispatchLifecycleActive(lifecycle, target),
|
|
3533
|
+
)
|
|
3534
|
+
) {
|
|
3457
3535
|
break;
|
|
3458
3536
|
}
|
|
3459
3537
|
throw error;
|
|
@@ -3489,7 +3567,13 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3489
3567
|
);
|
|
3490
3568
|
stringMessages += 1;
|
|
3491
3569
|
} catch (error) {
|
|
3492
|
-
if (
|
|
3570
|
+
if (
|
|
3571
|
+
isSyncDispatchCancellation(
|
|
3572
|
+
error,
|
|
3573
|
+
this.getSyncDispatchSignal(lifecycle, target),
|
|
3574
|
+
!this.isSyncDispatchLifecycleActive(lifecycle, target),
|
|
3575
|
+
)
|
|
3576
|
+
) {
|
|
3493
3577
|
break;
|
|
3494
3578
|
}
|
|
3495
3579
|
throw error;
|