@peerbit/shared-log 16.0.30 → 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/README.md +23 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +101 -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 +10 -1
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.d.ts +8 -1
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.js +140 -13
- package/dist/src/sync/rateless-iblt.js.map +1 -1
- package/dist/src/sync/simple.d.ts +6 -2
- package/dist/src/sync/simple.d.ts.map +1 -1
- package/dist/src/sync/simple.js +181 -122
- package/dist/src/sync/simple.js.map +1 -1
- package/package.json +12 -12
- package/src/index.ts +128 -6
- package/src/sync/dispatch-lifecycle.ts +20 -0
- package/src/sync/index.ts +10 -0
- package/src/sync/rateless-iblt.ts +178 -11
- package/src/sync/simple.ts +252 -154
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(
|
|
@@ -879,7 +887,10 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
879
887
|
const epoch =
|
|
880
888
|
expectedEpoch ??
|
|
881
889
|
currentEpoch ??
|
|
882
|
-
(options?.createTargetEpochs === false
|
|
890
|
+
(options?.createTargetEpochs === false ||
|
|
891
|
+
this.closed === true ||
|
|
892
|
+
callerSignal?.aborted === true ||
|
|
893
|
+
ownershipLifecycleController.signal.aborted
|
|
883
894
|
? undefined
|
|
884
895
|
: this.getOrCreateSyncDispatchTargetEpoch(target));
|
|
885
896
|
if (!epoch) {
|
|
@@ -2362,7 +2373,11 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2362
2373
|
requestedCount: trackedHashes.length,
|
|
2363
2374
|
requestedTotalCount: allHashes.length,
|
|
2364
2375
|
attempts: 0,
|
|
2365
|
-
|
|
2376
|
+
// Rejected late repairs only need result metadata. Do not create a
|
|
2377
|
+
// retained dispatch epoch after the terminal admission fence.
|
|
2378
|
+
targetEpoch: this.closed
|
|
2379
|
+
? { id: 0 }
|
|
2380
|
+
: this.getOrCreateSyncDispatchTargetEpoch(target),
|
|
2366
2381
|
});
|
|
2367
2382
|
}
|
|
2368
2383
|
|
|
@@ -2377,6 +2392,10 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2377
2392
|
deferred,
|
|
2378
2393
|
cancelled: false,
|
|
2379
2394
|
};
|
|
2395
|
+
if (this.closed) {
|
|
2396
|
+
deferred.resolve(this.buildRepairSessionResult(session, false));
|
|
2397
|
+
return { id, done: deferred.promise, cancel: () => {} };
|
|
2398
|
+
}
|
|
2380
2399
|
|
|
2381
2400
|
if (allHashes.length === 0 || targets.length === 0) {
|
|
2382
2401
|
deferred.resolve(this.buildRepairSessionResult(session, true));
|
|
@@ -2520,8 +2539,12 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2520
2539
|
} catch (error) {
|
|
2521
2540
|
reservation.release();
|
|
2522
2541
|
if (
|
|
2523
|
-
|
|
2524
|
-
|
|
2542
|
+
isSyncDispatchCancellation(
|
|
2543
|
+
error,
|
|
2544
|
+
this.getSyncDispatchSignal(lifecycle, target),
|
|
2545
|
+
!this.isSyncDispatchLifecycleActive(lifecycle) ||
|
|
2546
|
+
!this.isSyncDispatchLifecycleActive(lifecycle, target),
|
|
2547
|
+
)
|
|
2525
2548
|
) {
|
|
2526
2549
|
break;
|
|
2527
2550
|
}
|
|
@@ -2583,7 +2606,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2583
2606
|
{ signal },
|
|
2584
2607
|
);
|
|
2585
2608
|
} catch (error) {
|
|
2586
|
-
if (signal
|
|
2609
|
+
if (isSyncDispatchCancellation(error, signal)) {
|
|
2587
2610
|
return { messages: 0, fused: true };
|
|
2588
2611
|
}
|
|
2589
2612
|
throw error;
|
|
@@ -2611,7 +2634,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2611
2634
|
});
|
|
2612
2635
|
messages += 1;
|
|
2613
2636
|
} catch (error) {
|
|
2614
|
-
if (signal
|
|
2637
|
+
if (isSyncDispatchCancellation(error, signal)) {
|
|
2615
2638
|
break;
|
|
2616
2639
|
}
|
|
2617
2640
|
throw error;
|
|
@@ -2634,7 +2657,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2634
2657
|
response: ResponseMaybeSync | ResponseMaybeSyncCapabilities;
|
|
2635
2658
|
signal: AbortSignal;
|
|
2636
2659
|
}): Promise<{ messages: number; fused: boolean; entries: number }> {
|
|
2637
|
-
const hashes = this.filterRecentlySentExchangeHeads(
|
|
2660
|
+
const { hashes, rollback } = this.filterRecentlySentExchangeHeads(
|
|
2638
2661
|
properties.hashes,
|
|
2639
2662
|
properties.from,
|
|
2640
2663
|
);
|
|
@@ -2650,11 +2673,11 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2650
2673
|
entries: hashes.length,
|
|
2651
2674
|
};
|
|
2652
2675
|
} catch (error) {
|
|
2653
|
-
|
|
2676
|
+
rollback();
|
|
2654
2677
|
throw error;
|
|
2655
2678
|
} finally {
|
|
2656
2679
|
if (properties.signal.aborted) {
|
|
2657
|
-
|
|
2680
|
+
rollback();
|
|
2658
2681
|
}
|
|
2659
2682
|
}
|
|
2660
2683
|
}
|
|
@@ -2663,6 +2686,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2663
2686
|
leases: AuthorizedMaybeSyncResponseLease[];
|
|
2664
2687
|
from: PublicSignKey;
|
|
2665
2688
|
response: ResponseMaybeSync | ResponseMaybeSyncCapabilities;
|
|
2689
|
+
signal?: AbortSignal;
|
|
2666
2690
|
source?: string;
|
|
2667
2691
|
}): Promise<{ messages: number; fused: boolean; entries: number }> {
|
|
2668
2692
|
if (properties.leases.length === 0) {
|
|
@@ -2675,18 +2699,21 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2675
2699
|
let entries = 0;
|
|
2676
2700
|
let firstError: unknown;
|
|
2677
2701
|
for (const lease of properties.leases) {
|
|
2702
|
+
const signal = properties.signal
|
|
2703
|
+
? AbortSignal.any([lease.signal, properties.signal])
|
|
2704
|
+
: lease.signal;
|
|
2678
2705
|
let fulfilled = false;
|
|
2679
2706
|
try {
|
|
2680
2707
|
const shipped = await this.shipAuthorizedMaybeSyncResponse({
|
|
2681
2708
|
hashes: lease.hashes,
|
|
2682
2709
|
from: properties.from,
|
|
2683
2710
|
response: properties.response,
|
|
2684
|
-
signal
|
|
2711
|
+
signal,
|
|
2685
2712
|
});
|
|
2686
2713
|
messages += shipped.messages;
|
|
2687
2714
|
fused ||= shipped.fused;
|
|
2688
2715
|
entries += shipped.entries;
|
|
2689
|
-
fulfilled = !
|
|
2716
|
+
fulfilled = !signal.aborted;
|
|
2690
2717
|
} catch (error) {
|
|
2691
2718
|
firstError ??= error;
|
|
2692
2719
|
} finally {
|
|
@@ -2714,10 +2741,20 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2714
2741
|
async onMessage(
|
|
2715
2742
|
msg: TransportMessage,
|
|
2716
2743
|
context: RequestContext,
|
|
2744
|
+
options?: { signal?: AbortSignal },
|
|
2717
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
|
+
}
|
|
2718
2755
|
const from = context.from!;
|
|
2719
2756
|
if (msg instanceof RequestMaybeSync) {
|
|
2720
|
-
await this.queueSync(msg.hashes, from);
|
|
2757
|
+
await this.queueSync(msg.hashes, from, options);
|
|
2721
2758
|
return true;
|
|
2722
2759
|
} else if (
|
|
2723
2760
|
msg instanceof ResponseMaybeSync ||
|
|
@@ -2734,6 +2771,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2734
2771
|
leases: pending,
|
|
2735
2772
|
from,
|
|
2736
2773
|
response: msg,
|
|
2774
|
+
signal: options?.signal,
|
|
2737
2775
|
});
|
|
2738
2776
|
return true;
|
|
2739
2777
|
} else if (
|
|
@@ -2752,7 +2790,10 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2752
2790
|
return true;
|
|
2753
2791
|
}
|
|
2754
2792
|
const { release: releaseLookup, row: slotRow } = lookupPermit;
|
|
2755
|
-
const lifecycle = this.captureSyncDispatchLifecycle(
|
|
2793
|
+
const lifecycle = this.captureSyncDispatchLifecycle(
|
|
2794
|
+
[target],
|
|
2795
|
+
options?.signal,
|
|
2796
|
+
);
|
|
2756
2797
|
let lifecycleFinished = false;
|
|
2757
2798
|
const finishLifecycle = () => {
|
|
2758
2799
|
if (lifecycleFinished) {
|
|
@@ -2810,16 +2851,25 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
2810
2851
|
let hashesToSend: string[] = [];
|
|
2811
2852
|
let messages = 0;
|
|
2812
2853
|
let fused = false;
|
|
2854
|
+
let rollback: (() => void) | undefined;
|
|
2855
|
+
const signal = this.getSyncDispatchSignal(lifecycle, target);
|
|
2813
2856
|
try {
|
|
2814
|
-
|
|
2857
|
+
({ hashes: hashesToSend, rollback } =
|
|
2858
|
+
this.filterRecentlySentExchangeHeads(hashes, from));
|
|
2815
2859
|
// dont set priority 1 here because this will block other messages that should higher priority
|
|
2816
2860
|
({ messages, fused } = await this.shipExchangeHeads(
|
|
2817
2861
|
hashesToSend,
|
|
2818
2862
|
context.from!,
|
|
2819
2863
|
canReceiveRawExchangeHeads(msg),
|
|
2820
|
-
|
|
2864
|
+
signal,
|
|
2821
2865
|
));
|
|
2866
|
+
} catch (error) {
|
|
2867
|
+
rollback?.();
|
|
2868
|
+
throw error;
|
|
2822
2869
|
} finally {
|
|
2870
|
+
if (signal.aborted) {
|
|
2871
|
+
rollback?.();
|
|
2872
|
+
}
|
|
2823
2873
|
releaseResponse();
|
|
2824
2874
|
if (profile) {
|
|
2825
2875
|
emitSyncProfileDuration(profile, exchangeStartedAt, {
|
|
@@ -3085,9 +3135,9 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3085
3135
|
async queueSync(
|
|
3086
3136
|
keys: SyncableKey[],
|
|
3087
3137
|
from: PublicSignKey,
|
|
3088
|
-
options?: { skipCheck?: boolean },
|
|
3138
|
+
options?: { skipCheck?: boolean; signal?: AbortSignal },
|
|
3089
3139
|
) {
|
|
3090
|
-
if (this.closed === true || keys.length === 0) {
|
|
3140
|
+
if (this.closed === true || options?.signal?.aborted || keys.length === 0) {
|
|
3091
3141
|
return;
|
|
3092
3142
|
}
|
|
3093
3143
|
// A delayed timer must not let expired claims or admission reservations
|
|
@@ -3114,6 +3164,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3114
3164
|
const ownershipLifecycleController = this.syncDispatchLifecycleController;
|
|
3115
3165
|
const isCapturedLifecycleActive = () =>
|
|
3116
3166
|
this.closed !== true &&
|
|
3167
|
+
options?.signal?.aborted !== true &&
|
|
3117
3168
|
this.syncDispatchLifecycleController === ownershipLifecycleController &&
|
|
3118
3169
|
!ownershipLifecycleController.signal.aborted &&
|
|
3119
3170
|
this.syncDispatchTargetEpochs.get(fromHash) === targetEpoch;
|
|
@@ -3209,6 +3260,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3209
3260
|
const existingRequest =
|
|
3210
3261
|
dispatchableExistingRequestHashes.length > 0
|
|
3211
3262
|
? this.requestSync(dispatchableExistingRequestHashes, [fromHash], {
|
|
3263
|
+
signal: options?.signal,
|
|
3212
3264
|
ownershipLifecycleController,
|
|
3213
3265
|
targetEpochs: new Map([[fromHash, targetEpoch]]),
|
|
3214
3266
|
createTargetEpochs: false,
|
|
@@ -3219,54 +3271,42 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3219
3271
|
// when a later lifecycle check returns before joining it.
|
|
3220
3272
|
void existingRequest?.catch(() => {});
|
|
3221
3273
|
const resolveKnownStartedAt = syncProfileStart(profile);
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
return;
|
|
3229
|
-
}
|
|
3230
|
-
if (profile) {
|
|
3231
|
-
emitSyncProfileDuration(profile, resolveKnownStartedAt, {
|
|
3232
|
-
name: "simple.queueSync.resolveKnown",
|
|
3233
|
-
entries: keysToCheck.length,
|
|
3234
|
-
count: knownKeys?.keys.size ?? 0,
|
|
3235
|
-
details: {
|
|
3236
|
-
checkedCoordinates: knownKeys?.checkedCoordinates === true,
|
|
3237
|
-
checkedHashes: knownKeys?.checkedHashes === true,
|
|
3238
|
-
skipCheck: options?.skipCheck === true,
|
|
3239
|
-
},
|
|
3240
|
-
});
|
|
3241
|
-
}
|
|
3242
|
-
|
|
3243
|
-
if (keysToCheck.length > 0) {
|
|
3244
|
-
// A resolver/index lookup may have populated coordinateToHash while
|
|
3245
|
-
// it yielded. Refresh another fixed-size slice, never the full queue.
|
|
3246
|
-
this.refreshQueuedSyncCoordinateAliases();
|
|
3247
|
-
}
|
|
3248
|
-
const loopStartedAt = syncProfileStart(profile);
|
|
3249
|
-
for (let index = 0; index < keysToCheck.length; index += 1) {
|
|
3250
|
-
const key = keysToCheck[index]!;
|
|
3251
|
-
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);
|
|
3252
3280
|
if (!isCapturedLifecycleActive()) {
|
|
3253
3281
|
return;
|
|
3254
3282
|
}
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
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()) {
|
|
3262
3306
|
return;
|
|
3263
3307
|
}
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
const coordinateOrHash = queuedKeyResult ?? key;
|
|
3267
|
-
const inFlight = this.syncInFlightQueue.get(coordinateOrHash);
|
|
3268
|
-
if (inFlight) {
|
|
3269
|
-
if (!this.hasPendingSyncClaim(coordinateOrHash, fromHash)) {
|
|
3308
|
+
const queuedKeyResult = this.getQueuedSyncKeyForAdmission(key);
|
|
3309
|
+
if (queuedKeyResult === QUEUED_SYNC_ALIAS_REFRESH_PENDING) {
|
|
3270
3310
|
const consumption = this.consumePendingSyncAdmission(
|
|
3271
3311
|
admission!,
|
|
3272
3312
|
identity,
|
|
@@ -3274,79 +3314,104 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3274
3314
|
if (consumption === "invalid") {
|
|
3275
3315
|
return;
|
|
3276
3316
|
}
|
|
3277
|
-
|
|
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);
|
|
3278
3358
|
continue;
|
|
3279
3359
|
}
|
|
3280
|
-
this.
|
|
3281
|
-
|
|
3282
|
-
|
|
3360
|
+
const consumption = this.consumePendingSyncAdmission(
|
|
3361
|
+
admission!,
|
|
3362
|
+
identity,
|
|
3283
3363
|
);
|
|
3284
|
-
|
|
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(
|
|
3285
3372
|
coordinateOrHash,
|
|
3286
3373
|
from,
|
|
3287
3374
|
admission!.expiresAt,
|
|
3288
3375
|
);
|
|
3289
|
-
|
|
3290
|
-
requestHashes.push(coordinateOrHash);
|
|
3291
|
-
}
|
|
3376
|
+
requestHashes.push(coordinateOrHash); // request immediately (first time we have seen this hash)
|
|
3292
3377
|
}
|
|
3293
|
-
} else {
|
|
3294
|
-
const has =
|
|
3295
|
-
options?.skipCheck !== true &&
|
|
3296
|
-
(await this.checkHasCoordinateOrHash(coordinateOrHash, knownKeys));
|
|
3297
|
-
if (!isCapturedLifecycleActive()) {
|
|
3298
|
-
return;
|
|
3299
|
-
}
|
|
3300
|
-
if (has) {
|
|
3301
|
-
this.clearPendingSyncAdmissionIdentity(identity);
|
|
3302
|
-
continue;
|
|
3303
|
-
}
|
|
3304
|
-
const consumption = this.consumePendingSyncAdmission(
|
|
3305
|
-
admission!,
|
|
3306
|
-
identity,
|
|
3307
|
-
);
|
|
3308
|
-
if (consumption === "invalid") {
|
|
3309
|
-
return;
|
|
3310
|
-
}
|
|
3311
|
-
if (consumption === "settled") {
|
|
3312
|
-
continue;
|
|
3313
|
-
}
|
|
3314
|
-
// Track the initial sender so we can retry if the first request is lost.
|
|
3315
|
-
this.addPendingSyncClaim(
|
|
3316
|
-
coordinateOrHash,
|
|
3317
|
-
from,
|
|
3318
|
-
admission!.expiresAt,
|
|
3319
|
-
);
|
|
3320
|
-
requestHashes.push(coordinateOrHash); // request immediately (first time we have seen this hash)
|
|
3321
3378
|
}
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
}
|
|
3330
|
-
}
|
|
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
|
+
}
|
|
3331
3387
|
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
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 {
|
|
3350
3415
|
if (profile) {
|
|
3351
3416
|
emitSyncProfileDuration(profile, startedAt, {
|
|
3352
3417
|
name: "simple.queueSync",
|
|
@@ -3359,6 +3424,18 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3359
3424
|
},
|
|
3360
3425
|
});
|
|
3361
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
|
+
);
|
|
3362
3439
|
}
|
|
3363
3440
|
}
|
|
3364
3441
|
|
|
@@ -3366,6 +3443,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3366
3443
|
hashes: SyncableKey[],
|
|
3367
3444
|
to: Set<string> | string[],
|
|
3368
3445
|
options?: {
|
|
3446
|
+
signal?: AbortSignal;
|
|
3369
3447
|
ownershipLifecycleController?: AbortController;
|
|
3370
3448
|
targetEpochs?: Map<string, SyncDispatchTargetEpoch>;
|
|
3371
3449
|
createTargetEpochs?: boolean;
|
|
@@ -3375,11 +3453,15 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3375
3453
|
if (hashes.length === 0 || targets.length === 0) {
|
|
3376
3454
|
return;
|
|
3377
3455
|
}
|
|
3378
|
-
const lifecycle = this.captureSyncDispatchLifecycle(
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
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
|
+
);
|
|
3383
3465
|
const profile = this.syncOptions?.profile;
|
|
3384
3466
|
const startedAt = syncProfileStart(profile);
|
|
3385
3467
|
let coordinateMessages = 0;
|
|
@@ -3443,7 +3525,13 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3443
3525
|
);
|
|
3444
3526
|
coordinateMessages += 1;
|
|
3445
3527
|
} catch (error) {
|
|
3446
|
-
if (
|
|
3528
|
+
if (
|
|
3529
|
+
isSyncDispatchCancellation(
|
|
3530
|
+
error,
|
|
3531
|
+
this.getSyncDispatchSignal(lifecycle, target),
|
|
3532
|
+
!this.isSyncDispatchLifecycleActive(lifecycle, target),
|
|
3533
|
+
)
|
|
3534
|
+
) {
|
|
3447
3535
|
break;
|
|
3448
3536
|
}
|
|
3449
3537
|
throw error;
|
|
@@ -3479,7 +3567,13 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3479
3567
|
);
|
|
3480
3568
|
stringMessages += 1;
|
|
3481
3569
|
} catch (error) {
|
|
3482
|
-
if (
|
|
3570
|
+
if (
|
|
3571
|
+
isSyncDispatchCancellation(
|
|
3572
|
+
error,
|
|
3573
|
+
this.getSyncDispatchSignal(lifecycle, target),
|
|
3574
|
+
!this.isSyncDispatchLifecycleActive(lifecycle, target),
|
|
3575
|
+
)
|
|
3576
|
+
) {
|
|
3483
3577
|
break;
|
|
3484
3578
|
}
|
|
3485
3579
|
throw error;
|
|
@@ -3755,9 +3849,13 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
3755
3849
|
});
|
|
3756
3850
|
}
|
|
3757
3851
|
|
|
3758
|
-
|
|
3852
|
+
beginClose(): void {
|
|
3759
3853
|
this.closed = true;
|
|
3760
3854
|
this.syncDispatchLifecycleController.abort();
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
async close() {
|
|
3858
|
+
this.beginClose();
|
|
3761
3859
|
this.syncDispatchTargetEpochs.clear();
|
|
3762
3860
|
this.clearPendingSyncAdmissions();
|
|
3763
3861
|
this.syncInFlightRetryIterator = undefined;
|