@peerbit/shared-log 11.0.4 → 11.0.5-d8e5bfb
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 +10 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +104 -28
- package/dist/src/index.js.map +1 -1
- package/dist/src/ranges.d.ts +2 -0
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +16 -1
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/sync/rateless-iblt.js +1 -1
- package/dist/src/sync/rateless-iblt.js.map +1 -1
- package/package.json +70 -70
- package/src/index.ts +138 -35
- package/src/ranges.ts +20 -1
- package/src/sync/rateless-iblt.ts +1 -1
package/src/index.ts
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
} from "@peerbit/stream-interface";
|
|
49
49
|
import {
|
|
50
50
|
AbortError,
|
|
51
|
+
TimeoutError,
|
|
51
52
|
debounceAccumulator,
|
|
52
53
|
debounceFixedInterval,
|
|
53
54
|
waitFor,
|
|
@@ -1793,6 +1794,9 @@ export class SharedLog<
|
|
|
1793
1794
|
|
|
1794
1795
|
await this.rpc.subscribe();
|
|
1795
1796
|
|
|
1797
|
+
// mark all our replicaiton ranges as "new", this would allow other peers to understand that we recently reopend our database and might need some sync and warmup
|
|
1798
|
+
await this.updateTimestampOfOwnedReplicationRanges(); // TODO do we need to do this before subscribing?
|
|
1799
|
+
|
|
1796
1800
|
// if we had a previous session with replication info, and new replication info dictates that we unreplicate
|
|
1797
1801
|
// we should do that. Otherwise if options is a unreplication we dont need to do anything because
|
|
1798
1802
|
// we are already unreplicated (as we are just opening)
|
|
@@ -1820,6 +1824,38 @@ export class SharedLog<
|
|
|
1820
1824
|
}, RECALCULATE_PARTICIPATION_DEBOUNCE_INTERVAL);
|
|
1821
1825
|
}
|
|
1822
1826
|
|
|
1827
|
+
private async updateTimestampOfOwnedReplicationRanges(
|
|
1828
|
+
timestamp: number = +new Date(),
|
|
1829
|
+
) {
|
|
1830
|
+
const all = await this.replicationIndex
|
|
1831
|
+
.iterate({
|
|
1832
|
+
query: { hash: this.node.identity.publicKey.hashcode() },
|
|
1833
|
+
})
|
|
1834
|
+
.all();
|
|
1835
|
+
let bnTimestamp = BigInt(timestamp);
|
|
1836
|
+
for (const x of all) {
|
|
1837
|
+
x.value.timestamp = bnTimestamp;
|
|
1838
|
+
await this.replicationIndex.put(x.value);
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
if (all.length > 0) {
|
|
1842
|
+
// emit mature event
|
|
1843
|
+
const maturityTimeout = setTimeout(
|
|
1844
|
+
() => {
|
|
1845
|
+
this.events.dispatchEvent(
|
|
1846
|
+
new CustomEvent<ReplicationChangeEvent>("replicator:mature", {
|
|
1847
|
+
detail: { publicKey: this.node.identity.publicKey },
|
|
1848
|
+
}),
|
|
1849
|
+
);
|
|
1850
|
+
},
|
|
1851
|
+
await this.getDefaultMinRoleAge(),
|
|
1852
|
+
);
|
|
1853
|
+
this._closeController.signal.addEventListener("abort", () => {
|
|
1854
|
+
clearTimeout(maturityTimeout);
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1823
1859
|
async afterOpen(): Promise<void> {
|
|
1824
1860
|
await super.afterOpen();
|
|
1825
1861
|
|
|
@@ -2615,12 +2651,16 @@ export class SharedLog<
|
|
|
2615
2651
|
start?: NumberFromType<R>;
|
|
2616
2652
|
/** Optional: end of the content range (exclusive) */
|
|
2617
2653
|
end?: NumberFromType<R>;
|
|
2654
|
+
|
|
2655
|
+
/** Optional: roleAge (in ms) */
|
|
2656
|
+
roleAge?: number;
|
|
2618
2657
|
}) {
|
|
2619
2658
|
return calculateCoverage({
|
|
2620
2659
|
numbers: this.indexableDomain.numbers,
|
|
2621
2660
|
peers: this.replicationIndex,
|
|
2622
2661
|
end: properties?.end,
|
|
2623
2662
|
start: properties?.start,
|
|
2663
|
+
roleAge: properties?.roleAge,
|
|
2624
2664
|
});
|
|
2625
2665
|
}
|
|
2626
2666
|
|
|
@@ -2720,38 +2760,6 @@ export class SharedLog<
|
|
|
2720
2760
|
return set;
|
|
2721
2761
|
}
|
|
2722
2762
|
|
|
2723
|
-
async waitForReplicator(...keys: PublicSignKey[]) {
|
|
2724
|
-
const check = async () => {
|
|
2725
|
-
for (const k of keys) {
|
|
2726
|
-
const iterator = this.replicationIndex?.iterate(
|
|
2727
|
-
{ query: new StringMatch({ key: "hash", value: k.hashcode() }) },
|
|
2728
|
-
{ reference: true },
|
|
2729
|
-
);
|
|
2730
|
-
const rects = await iterator?.next(1);
|
|
2731
|
-
await iterator.close();
|
|
2732
|
-
const rect = rects[0]?.value;
|
|
2733
|
-
if (
|
|
2734
|
-
!rect ||
|
|
2735
|
-
!isMatured(rect, +new Date(), await this.getDefaultMinRoleAge())
|
|
2736
|
-
) {
|
|
2737
|
-
return false;
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
|
-
return true;
|
|
2741
|
-
};
|
|
2742
|
-
|
|
2743
|
-
// TODO do event based
|
|
2744
|
-
return waitFor(() => check(), {
|
|
2745
|
-
signal: this._closeController.signal,
|
|
2746
|
-
}).catch((e) => {
|
|
2747
|
-
if (e instanceof AbortError) {
|
|
2748
|
-
// ignore error
|
|
2749
|
-
return;
|
|
2750
|
-
}
|
|
2751
|
-
throw e;
|
|
2752
|
-
});
|
|
2753
|
-
}
|
|
2754
|
-
|
|
2755
2763
|
async join(
|
|
2756
2764
|
entries: (string | Entry<T> | ShallowEntry)[],
|
|
2757
2765
|
options?: {
|
|
@@ -2886,6 +2894,100 @@ export class SharedLog<
|
|
|
2886
2894
|
}
|
|
2887
2895
|
}
|
|
2888
2896
|
|
|
2897
|
+
async waitForReplicator(...keys: PublicSignKey[]) {
|
|
2898
|
+
const check = async () => {
|
|
2899
|
+
for (const k of keys) {
|
|
2900
|
+
const iterator = this.replicationIndex?.iterate(
|
|
2901
|
+
{ query: new StringMatch({ key: "hash", value: k.hashcode() }) },
|
|
2902
|
+
{ reference: true },
|
|
2903
|
+
);
|
|
2904
|
+
const rects = await iterator?.next(1);
|
|
2905
|
+
await iterator.close();
|
|
2906
|
+
const rect = rects[0]?.value;
|
|
2907
|
+
if (
|
|
2908
|
+
!rect ||
|
|
2909
|
+
!isMatured(rect, +new Date(), await this.getDefaultMinRoleAge())
|
|
2910
|
+
) {
|
|
2911
|
+
return false;
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
return true;
|
|
2915
|
+
};
|
|
2916
|
+
|
|
2917
|
+
// TODO do event based
|
|
2918
|
+
return waitFor(() => check(), {
|
|
2919
|
+
signal: this._closeController.signal,
|
|
2920
|
+
}).catch((e) => {
|
|
2921
|
+
if (e instanceof AbortError) {
|
|
2922
|
+
// ignore error
|
|
2923
|
+
return;
|
|
2924
|
+
}
|
|
2925
|
+
throw e;
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
async waitForReplicators(options?: {
|
|
2930
|
+
timeout?: number;
|
|
2931
|
+
roleAge?: number;
|
|
2932
|
+
signal?: AbortSignal;
|
|
2933
|
+
coverageThreshold?: number;
|
|
2934
|
+
}) {
|
|
2935
|
+
let coverageThreshold = options?.coverageThreshold ?? 0.99;
|
|
2936
|
+
let deferred = pDefer<void>();
|
|
2937
|
+
const roleAge = options?.roleAge ?? (await this.getDefaultMinRoleAge());
|
|
2938
|
+
const providedCustomRoleAge = options?.roleAge != null;
|
|
2939
|
+
|
|
2940
|
+
let checkCoverage = async () => {
|
|
2941
|
+
const coverage = await this.calculateCoverage({
|
|
2942
|
+
roleAge,
|
|
2943
|
+
});
|
|
2944
|
+
if (coverage > coverageThreshold) {
|
|
2945
|
+
deferred.resolve();
|
|
2946
|
+
return true;
|
|
2947
|
+
}
|
|
2948
|
+
return false;
|
|
2949
|
+
};
|
|
2950
|
+
this.events.addEventListener("replicator:mature", checkCoverage);
|
|
2951
|
+
this.events.addEventListener("replication:change", checkCoverage);
|
|
2952
|
+
await checkCoverage();
|
|
2953
|
+
|
|
2954
|
+
let interval = providedCustomRoleAge
|
|
2955
|
+
? setInterval(() => {
|
|
2956
|
+
checkCoverage();
|
|
2957
|
+
}, 100)
|
|
2958
|
+
: undefined;
|
|
2959
|
+
|
|
2960
|
+
let timeout = options?.timeout ?? this.waitForReplicatorTimeout;
|
|
2961
|
+
const timer = setTimeout(() => {
|
|
2962
|
+
clear();
|
|
2963
|
+
deferred.reject(
|
|
2964
|
+
new TimeoutError(`Timeout waiting for mature replicators`),
|
|
2965
|
+
);
|
|
2966
|
+
}, timeout);
|
|
2967
|
+
|
|
2968
|
+
const abortListener = () => {
|
|
2969
|
+
clear();
|
|
2970
|
+
deferred.reject(new AbortError());
|
|
2971
|
+
};
|
|
2972
|
+
|
|
2973
|
+
if (options?.signal) {
|
|
2974
|
+
options.signal.addEventListener("abort", abortListener);
|
|
2975
|
+
}
|
|
2976
|
+
const clear = () => {
|
|
2977
|
+
interval && clearInterval(interval);
|
|
2978
|
+
this.events.removeEventListener("join", checkCoverage);
|
|
2979
|
+
this.events.removeEventListener("leave", checkCoverage);
|
|
2980
|
+
clearTimeout(timer);
|
|
2981
|
+
if (options?.signal) {
|
|
2982
|
+
options.signal.removeEventListener("abort", abortListener);
|
|
2983
|
+
}
|
|
2984
|
+
};
|
|
2985
|
+
|
|
2986
|
+
return deferred.promise.finally(() => {
|
|
2987
|
+
return clear();
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2889
2991
|
private async _waitForReplicators(
|
|
2890
2992
|
cursors: NumberFromType<R>[],
|
|
2891
2993
|
entry: Entry<T> | EntryReplicated<R> | ShallowEntry,
|
|
@@ -3046,9 +3148,10 @@ export class SharedLog<
|
|
|
3046
3148
|
}
|
|
3047
3149
|
|
|
3048
3150
|
const now = +new Date();
|
|
3049
|
-
const subscribers =
|
|
3050
|
-
|
|
3051
|
-
|
|
3151
|
+
const subscribers = this.rpc.closed
|
|
3152
|
+
? 1
|
|
3153
|
+
: ((await this.node.services.pubsub.getSubscribers(this.rpc.topic))
|
|
3154
|
+
?.length ?? 1);
|
|
3052
3155
|
const diffToOldest =
|
|
3053
3156
|
subscribers > 1 ? now - this.oldestOpenTime - 1 : Number.MAX_SAFE_INTEGER;
|
|
3054
3157
|
|
package/src/ranges.ts
CHANGED
|
@@ -1372,6 +1372,9 @@ export const calculateCoverage = async <R extends "u32" | "u64">(properties: {
|
|
|
1372
1372
|
start?: NumberFromType<R>;
|
|
1373
1373
|
/** Optional: end of the content range (exclusive) */
|
|
1374
1374
|
end?: NumberFromType<R>;
|
|
1375
|
+
|
|
1376
|
+
/** Optional: role age limit in milliseconds */
|
|
1377
|
+
roleAge?: number;
|
|
1375
1378
|
}): Promise<number> => {
|
|
1376
1379
|
// Use the provided content range if given; otherwise use the default full range.
|
|
1377
1380
|
const contentStart = properties.start ?? properties.numbers.zero;
|
|
@@ -1385,12 +1388,14 @@ export const calculateCoverage = async <R extends "u32" | "u64">(properties: {
|
|
|
1385
1388
|
numbers: properties.numbers,
|
|
1386
1389
|
start: contentStart,
|
|
1387
1390
|
end: properties.numbers.maxValue,
|
|
1391
|
+
roleAge: properties.roleAge,
|
|
1388
1392
|
});
|
|
1389
1393
|
const coverage2 = await calculateCoverage({
|
|
1390
1394
|
peers: properties.peers,
|
|
1391
1395
|
numbers: properties.numbers,
|
|
1392
1396
|
start: properties.numbers.zero,
|
|
1393
1397
|
end: contentEnd,
|
|
1398
|
+
roleAge: properties.roleAge,
|
|
1394
1399
|
});
|
|
1395
1400
|
|
|
1396
1401
|
return Math.min(coverage1, coverage2);
|
|
@@ -1399,7 +1404,21 @@ export const calculateCoverage = async <R extends "u32" | "u64">(properties: {
|
|
|
1399
1404
|
const endpoints: { point: NumberFromType<R>; delta: -1 | 1 }[] = [];
|
|
1400
1405
|
|
|
1401
1406
|
// For each range, record its start and end as events.
|
|
1402
|
-
|
|
1407
|
+
const timeThresholdQuery =
|
|
1408
|
+
properties?.roleAge != null
|
|
1409
|
+
? [
|
|
1410
|
+
new IntegerCompare({
|
|
1411
|
+
key: "timestamp",
|
|
1412
|
+
compare: Compare.LessOrEqual,
|
|
1413
|
+
value: BigInt(Date.now() - properties.roleAge),
|
|
1414
|
+
}),
|
|
1415
|
+
]
|
|
1416
|
+
: undefined;
|
|
1417
|
+
for (const r of await properties.peers
|
|
1418
|
+
.iterate({
|
|
1419
|
+
query: timeThresholdQuery,
|
|
1420
|
+
})
|
|
1421
|
+
.all()) {
|
|
1403
1422
|
endpoints.push({ point: r.value.start1, delta: +1 });
|
|
1404
1423
|
endpoints.push({ point: r.value.end1, delta: -1 });
|
|
1405
1424
|
|
|
@@ -541,7 +541,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
541
541
|
if (outProcess === true) {
|
|
542
542
|
return true;
|
|
543
543
|
} else if (outProcess === undefined) {
|
|
544
|
-
return
|
|
544
|
+
return true; // we don't have enough information, or received information that is redundant
|
|
545
545
|
}
|
|
546
546
|
|
|
547
547
|
// we are not done
|