@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
|
@@ -16,6 +16,26 @@
|
|
|
16
16
|
// caller-specific fields such as epochs, batches and retained-work
|
|
17
17
|
// counters); the registry owns the per-target active sets, the listener
|
|
18
18
|
// add/remove pairing and the dispose gating.
|
|
19
|
+
import { AbortError } from "@peerbit/time";
|
|
20
|
+
|
|
21
|
+
/** Local receive drain cancellation; never a transport or persistence failure. */
|
|
22
|
+
export class SyncReceiveAbortError extends AbortError {}
|
|
23
|
+
|
|
24
|
+
export const isSyncDispatchCancellation = (
|
|
25
|
+
error: unknown,
|
|
26
|
+
signal: AbortSignal | undefined,
|
|
27
|
+
inactive = signal?.aborted === true,
|
|
28
|
+
): boolean => {
|
|
29
|
+
if (!inactive) return false;
|
|
30
|
+
// Keep the existing owner/disconnect error policy. Only the new receive
|
|
31
|
+
// cancellation must distinguish unrelated failures that race its abort.
|
|
32
|
+
if (!(signal?.reason instanceof SyncReceiveAbortError)) return true;
|
|
33
|
+
return (
|
|
34
|
+
error === signal.reason ||
|
|
35
|
+
error instanceof AbortError ||
|
|
36
|
+
(error instanceof Error && error.name === "AbortError")
|
|
37
|
+
);
|
|
38
|
+
};
|
|
19
39
|
|
|
20
40
|
export interface DispatchTargetLifecycleBase<LC> {
|
|
21
41
|
lifecycle: LC;
|
package/src/sync/index.ts
CHANGED
|
@@ -231,6 +231,9 @@ export interface Syncronizer<R extends "u32" | "u64"> {
|
|
|
231
231
|
onMessage(
|
|
232
232
|
message: TransportMessage,
|
|
233
233
|
context: RequestContext,
|
|
234
|
+
// Exact admitted receive lifetime, not a peer-hash-wide disconnect.
|
|
235
|
+
// Implementations must still settle physical work before returning.
|
|
236
|
+
options?: { signal?: AbortSignal },
|
|
234
237
|
): Promise<boolean> | boolean;
|
|
235
238
|
|
|
236
239
|
onReceivedEntries(properties: {
|
|
@@ -27,7 +27,9 @@ import { TransportMessage } from "../message.js";
|
|
|
27
27
|
import { type EntryReplicated } from "../ranges.js";
|
|
28
28
|
import {
|
|
29
29
|
DispatchLifecycleRegistry,
|
|
30
|
+
SyncReceiveAbortError,
|
|
30
31
|
isOwnershipGenerationActive,
|
|
32
|
+
isSyncDispatchCancellation,
|
|
31
33
|
isTrackedSessionActive,
|
|
32
34
|
} from "./dispatch-lifecycle.js";
|
|
33
35
|
import type {
|
|
@@ -827,6 +829,7 @@ type OutgoingRatelessSyncProcess = {
|
|
|
827
829
|
lastSeqNo: bigint;
|
|
828
830
|
}) => { symbols: CodedSymbolBatch; exhaustedAfterSend: boolean } | undefined;
|
|
829
831
|
startSimpleFallback: () => Promise<void>;
|
|
832
|
+
cancelSimpleFallback: (reason: unknown) => void;
|
|
830
833
|
simpleFallbackStarted: boolean;
|
|
831
834
|
free: (reason?: unknown) => void;
|
|
832
835
|
processController: AbortController;
|
|
@@ -863,6 +866,7 @@ type IncomingRatelessSyncProcess = {
|
|
|
863
866
|
symbols: CodedSymbolInput;
|
|
864
867
|
}) => Promise<IncomingRatelessProcessResult>;
|
|
865
868
|
requestAll: () => Promise<void>;
|
|
869
|
+
drain: () => Promise<void>;
|
|
866
870
|
fallbackToSimple: (reason?: unknown) => Promise<void>;
|
|
867
871
|
free: (reason?: unknown) => void;
|
|
868
872
|
complete: () => void;
|
|
@@ -1571,6 +1575,35 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
1571
1575
|
);
|
|
1572
1576
|
}
|
|
1573
1577
|
|
|
1578
|
+
private async settleIncomingReceive(
|
|
1579
|
+
process: IncomingRatelessSyncProcess,
|
|
1580
|
+
signal: AbortSignal,
|
|
1581
|
+
): Promise<void> {
|
|
1582
|
+
const errors: unknown[] = [];
|
|
1583
|
+
try {
|
|
1584
|
+
await process.drain();
|
|
1585
|
+
} catch (error) {
|
|
1586
|
+
errors.push(error);
|
|
1587
|
+
}
|
|
1588
|
+
if (signal.aborted) {
|
|
1589
|
+
try {
|
|
1590
|
+
process.free(signal.reason);
|
|
1591
|
+
} catch (error) {
|
|
1592
|
+
errors.push(error);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
if (errors.length === 1) throw errors[0];
|
|
1596
|
+
if (errors.length > 1) {
|
|
1597
|
+
throw new AggregateError(
|
|
1598
|
+
errors,
|
|
1599
|
+
"incoming receive drain and cleanup failed",
|
|
1600
|
+
{
|
|
1601
|
+
cause: errors[0],
|
|
1602
|
+
},
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1574
1607
|
async onMaybeMissingEntries(properties: {
|
|
1575
1608
|
entries: Map<string, SyncEntryCoordinates<D>>;
|
|
1576
1609
|
targets: string[];
|
|
@@ -2008,6 +2041,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2008
2041
|
let symbolsProduced = startSyncSymbols.length;
|
|
2009
2042
|
let symbolBudgetExhausted = false;
|
|
2010
2043
|
let simpleFallbackPromise: Promise<void> | undefined;
|
|
2044
|
+
const simpleFallbackController = new AbortController();
|
|
2011
2045
|
const symbolBudget = getOutgoingRatelessSymbolBudget(
|
|
2012
2046
|
properties.coordinates.length,
|
|
2013
2047
|
startSyncSymbols.length,
|
|
@@ -2055,7 +2089,14 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2055
2089
|
this.simple.onMaybeMissingHashes({
|
|
2056
2090
|
hashes: properties.outgoingHashes,
|
|
2057
2091
|
targets: [target],
|
|
2058
|
-
|
|
2092
|
+
// Encoder retirement must not cancel an admitted fallback.
|
|
2093
|
+
// A receive awaiting that exact memoized operation can do so.
|
|
2094
|
+
signal: lifecycle.callerSignal
|
|
2095
|
+
? AbortSignal.any([
|
|
2096
|
+
lifecycle.callerSignal,
|
|
2097
|
+
simpleFallbackController.signal,
|
|
2098
|
+
])
|
|
2099
|
+
: simpleFallbackController.signal,
|
|
2059
2100
|
}),
|
|
2060
2101
|
);
|
|
2061
2102
|
}
|
|
@@ -2150,6 +2191,8 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2150
2191
|
return { symbols, exhaustedAfterSend };
|
|
2151
2192
|
},
|
|
2152
2193
|
startSimpleFallback,
|
|
2194
|
+
cancelSimpleFallback: (reason) =>
|
|
2195
|
+
simpleFallbackController.abort(reason),
|
|
2153
2196
|
simpleFallbackStarted: false,
|
|
2154
2197
|
free: clear,
|
|
2155
2198
|
outgoingHashes: properties.outgoingHashes,
|
|
@@ -2386,7 +2429,74 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2386
2429
|
async onMessage(
|
|
2387
2430
|
message: TransportMessage,
|
|
2388
2431
|
context: RequestContext,
|
|
2432
|
+
options?: { signal?: AbortSignal },
|
|
2389
2433
|
): Promise<boolean> {
|
|
2434
|
+
const signal = options?.signal;
|
|
2435
|
+
if (!signal) {
|
|
2436
|
+
return this.onMessageForReceive(message, context);
|
|
2437
|
+
}
|
|
2438
|
+
let cancel: (() => void) | undefined;
|
|
2439
|
+
let settle: (() => Promise<void>) | undefined;
|
|
2440
|
+
const onAbort = () => cancel?.();
|
|
2441
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2442
|
+
const errors: unknown[] = [];
|
|
2443
|
+
let handled = false;
|
|
2444
|
+
try {
|
|
2445
|
+
handled = await this.onMessageForReceive(message, context, {
|
|
2446
|
+
signal,
|
|
2447
|
+
bind: (cancelWork, settleWork) => {
|
|
2448
|
+
cancel = cancelWork;
|
|
2449
|
+
settle = settleWork;
|
|
2450
|
+
if (signal.aborted) {
|
|
2451
|
+
onAbort();
|
|
2452
|
+
}
|
|
2453
|
+
},
|
|
2454
|
+
});
|
|
2455
|
+
} catch (error) {
|
|
2456
|
+
errors.push(error);
|
|
2457
|
+
} finally {
|
|
2458
|
+
// Keep cancellation connected while a bounded fallback's logical
|
|
2459
|
+
// completion still leaves its lower send physically draining.
|
|
2460
|
+
try {
|
|
2461
|
+
await settle?.();
|
|
2462
|
+
} catch (error) {
|
|
2463
|
+
if (!errors.includes(error)) {
|
|
2464
|
+
errors.push(error);
|
|
2465
|
+
}
|
|
2466
|
+
} finally {
|
|
2467
|
+
signal.removeEventListener("abort", onAbort);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
if (errors.length === 1) throw errors[0];
|
|
2471
|
+
if (errors.length > 1) {
|
|
2472
|
+
throw new AggregateError(
|
|
2473
|
+
errors,
|
|
2474
|
+
"sync receive and physical cleanup failed",
|
|
2475
|
+
{
|
|
2476
|
+
cause: errors[0],
|
|
2477
|
+
},
|
|
2478
|
+
);
|
|
2479
|
+
}
|
|
2480
|
+
return handled;
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
private async onMessageForReceive(
|
|
2484
|
+
message: TransportMessage,
|
|
2485
|
+
context: RequestContext,
|
|
2486
|
+
options?: {
|
|
2487
|
+
signal: AbortSignal;
|
|
2488
|
+
bind: (cancel: () => void, settle?: () => Promise<void>) => void;
|
|
2489
|
+
},
|
|
2490
|
+
): Promise<boolean> {
|
|
2491
|
+
if (options?.signal.aborted) {
|
|
2492
|
+
return (
|
|
2493
|
+
message instanceof StartSync ||
|
|
2494
|
+
message instanceof MoreSymbols ||
|
|
2495
|
+
message instanceof RequestMoreSymbols ||
|
|
2496
|
+
message instanceof RequestAll ||
|
|
2497
|
+
(await this.simple.onMessage(message, context, options))
|
|
2498
|
+
);
|
|
2499
|
+
}
|
|
2390
2500
|
const profile = this.properties.sync?.profile;
|
|
2391
2501
|
if (message instanceof StartSync) {
|
|
2392
2502
|
const from = context.from;
|
|
@@ -2511,6 +2621,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2511
2621
|
symbolBudget: getIncomingRatelessSymbolBudget(message.symbols.length),
|
|
2512
2622
|
process: async () => undefined,
|
|
2513
2623
|
requestAll: async () => {},
|
|
2624
|
+
drain: async () => {},
|
|
2514
2625
|
fallbackToSimple: async () => {},
|
|
2515
2626
|
free,
|
|
2516
2627
|
complete,
|
|
@@ -2522,6 +2633,10 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2522
2633
|
onOwnershipAbort,
|
|
2523
2634
|
{ once: true },
|
|
2524
2635
|
);
|
|
2636
|
+
options?.bind(
|
|
2637
|
+
() => controller.abort(options.signal.reason),
|
|
2638
|
+
() => this.settleIncomingReceive(obj, options.signal),
|
|
2639
|
+
);
|
|
2525
2640
|
obj.deadlineTimeout = setTimeout(() => {
|
|
2526
2641
|
void obj.fallbackToSimple(
|
|
2527
2642
|
new Error("incoming rateless process deadline exceeded"),
|
|
@@ -2534,6 +2649,9 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2534
2649
|
}
|
|
2535
2650
|
|
|
2536
2651
|
let requestAllPromise: Promise<void> | undefined;
|
|
2652
|
+
obj.drain = async () => {
|
|
2653
|
+
await requestAllPromise;
|
|
2654
|
+
};
|
|
2537
2655
|
obj.requestAll = () => {
|
|
2538
2656
|
if (requestAllPromise) {
|
|
2539
2657
|
return requestAllPromise;
|
|
@@ -2566,6 +2684,12 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2566
2684
|
syncId,
|
|
2567
2685
|
});
|
|
2568
2686
|
}
|
|
2687
|
+
} catch (error) {
|
|
2688
|
+
// Classify before complete() aborts the process as normal cleanup;
|
|
2689
|
+
// that cleanup must not disguise a genuine lower-send failure.
|
|
2690
|
+
if (!isSyncDispatchCancellation(error, controller.signal)) {
|
|
2691
|
+
throw error;
|
|
2692
|
+
}
|
|
2569
2693
|
} finally {
|
|
2570
2694
|
fallbackSendPending = false;
|
|
2571
2695
|
if (this.isIncomingSyncProcessActive(obj)) {
|
|
@@ -2643,7 +2767,8 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2643
2767
|
const processAborted = controller.signal.aborted;
|
|
2644
2768
|
free(error);
|
|
2645
2769
|
if (
|
|
2646
|
-
processAborted
|
|
2770
|
+
(processAborted &&
|
|
2771
|
+
isSyncDispatchCancellation(error, controller.signal)) ||
|
|
2647
2772
|
!this.isIncomingSyncGenerationActive(ownershipLifecycleController)
|
|
2648
2773
|
) {
|
|
2649
2774
|
return true;
|
|
@@ -2924,7 +3049,13 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2924
3049
|
},
|
|
2925
3050
|
);
|
|
2926
3051
|
} catch (error) {
|
|
2927
|
-
if (
|
|
3052
|
+
if (
|
|
3053
|
+
isSyncDispatchCancellation(
|
|
3054
|
+
error,
|
|
3055
|
+
controller.signal,
|
|
3056
|
+
!this.isIncomingSyncProcessActive(obj),
|
|
3057
|
+
)
|
|
3058
|
+
) {
|
|
2928
3059
|
return true;
|
|
2929
3060
|
}
|
|
2930
3061
|
free(error);
|
|
@@ -2959,6 +3090,10 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
2959
3090
|
) {
|
|
2960
3091
|
return true;
|
|
2961
3092
|
}
|
|
3093
|
+
options?.bind(
|
|
3094
|
+
() => obj.controller.abort(options.signal.reason),
|
|
3095
|
+
() => this.settleIncomingReceive(obj, options.signal),
|
|
3096
|
+
);
|
|
2962
3097
|
let outProcess: IncomingRatelessProcessResult;
|
|
2963
3098
|
try {
|
|
2964
3099
|
outProcess = await obj.process(message);
|
|
@@ -3005,7 +3140,13 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
3005
3140
|
signal: obj.controller.signal,
|
|
3006
3141
|
},
|
|
3007
3142
|
);
|
|
3008
|
-
} catch {
|
|
3143
|
+
} catch (error) {
|
|
3144
|
+
if (
|
|
3145
|
+
obj.controller.signal.reason instanceof SyncReceiveAbortError &&
|
|
3146
|
+
!isSyncDispatchCancellation(error, obj.controller.signal)
|
|
3147
|
+
) {
|
|
3148
|
+
throw error;
|
|
3149
|
+
}
|
|
3009
3150
|
if (profile) {
|
|
3010
3151
|
emitSyncProfileDuration(profile, sendStartedAt, {
|
|
3011
3152
|
name: "rateless.sendRequestMoreSymbols",
|
|
@@ -3039,6 +3180,14 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
3039
3180
|
if (context.from?.hashcode() !== obj.target) {
|
|
3040
3181
|
return true;
|
|
3041
3182
|
}
|
|
3183
|
+
options?.bind(
|
|
3184
|
+
() => obj.processController.abort(options.signal.reason),
|
|
3185
|
+
async () => {
|
|
3186
|
+
if (options.signal.aborted) {
|
|
3187
|
+
obj.free(options.signal.reason);
|
|
3188
|
+
}
|
|
3189
|
+
},
|
|
3190
|
+
);
|
|
3042
3191
|
const signal = obj.signal;
|
|
3043
3192
|
if (signal.aborted) {
|
|
3044
3193
|
return true;
|
|
@@ -3066,7 +3215,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
3066
3215
|
},
|
|
3067
3216
|
);
|
|
3068
3217
|
} catch (error) {
|
|
3069
|
-
if (signal
|
|
3218
|
+
if (isSyncDispatchCancellation(error, signal)) {
|
|
3070
3219
|
return true;
|
|
3071
3220
|
}
|
|
3072
3221
|
throw error;
|
|
@@ -3101,7 +3250,14 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
3101
3250
|
}
|
|
3102
3251
|
// RequestAll ends only this target's rateless attempt. Other target
|
|
3103
3252
|
// encoders and response authorizations remain independently owned.
|
|
3104
|
-
|
|
3253
|
+
let fallback: Promise<void> | undefined;
|
|
3254
|
+
options?.bind(
|
|
3255
|
+
() => p.cancelSimpleFallback(options.signal.reason),
|
|
3256
|
+
async () => {
|
|
3257
|
+
await fallback;
|
|
3258
|
+
},
|
|
3259
|
+
);
|
|
3260
|
+
fallback = p.startSimpleFallback();
|
|
3105
3261
|
p.free();
|
|
3106
3262
|
await fallback;
|
|
3107
3263
|
return true;
|
|
@@ -3174,13 +3330,17 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
3174
3330
|
entries: 0,
|
|
3175
3331
|
};
|
|
3176
3332
|
try {
|
|
3333
|
+
const signal = options?.signal
|
|
3334
|
+
? AbortSignal.any([response.signal, options.signal])
|
|
3335
|
+
: response.signal;
|
|
3177
3336
|
responseShipment =
|
|
3178
3337
|
await this.simple.shipAuthorizedMaybeSyncResponse({
|
|
3179
3338
|
hashes: response.authorized,
|
|
3180
3339
|
from,
|
|
3181
3340
|
response: message,
|
|
3182
|
-
signal
|
|
3341
|
+
signal,
|
|
3183
3342
|
});
|
|
3343
|
+
rollbackRatelessAuthorization = signal.aborted;
|
|
3184
3344
|
} catch (error) {
|
|
3185
3345
|
firstError = error;
|
|
3186
3346
|
rollbackRatelessAuthorization = true;
|
|
@@ -3209,6 +3369,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
3209
3369
|
leases: simpleLeases,
|
|
3210
3370
|
from,
|
|
3211
3371
|
response: simpleMessage,
|
|
3372
|
+
signal: options?.signal,
|
|
3212
3373
|
});
|
|
3213
3374
|
} catch (error) {
|
|
3214
3375
|
firstError ??= error;
|
|
@@ -3229,7 +3390,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
3229
3390
|
}
|
|
3230
3391
|
}
|
|
3231
3392
|
}
|
|
3232
|
-
return this.simple.onMessage(message, context);
|
|
3393
|
+
return this.simple.onMessage(message, context, options);
|
|
3233
3394
|
}
|
|
3234
3395
|
|
|
3235
3396
|
onReceivedEntries(properties: {
|