@peerbit/shared-log 16.0.6 → 16.0.8
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 +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -9
- package/dist/src/index.js.map +1 -1
- package/dist/src/ranges.d.ts +50 -1
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +273 -112
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/sync/factory.d.ts.map +1 -1
- package/dist/src/sync/factory.js +8 -2
- package/dist/src/sync/factory.js.map +1 -1
- package/dist/src/sync/index.d.ts +12 -0
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.d.ts +1 -0
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.js +67 -14
- package/dist/src/sync/rateless-iblt.js.map +1 -1
- package/dist/src/sync/simple.d.ts.map +1 -1
- package/dist/src/sync/simple.js +36 -10
- package/dist/src/sync/simple.js.map +1 -1
- package/package.json +15 -14
- package/src/index.ts +34 -9
- package/src/ranges.ts +436 -165
- package/src/sync/factory.ts +11 -2
- package/src/sync/index.ts +14 -4
- package/src/sync/rateless-iblt.ts +81 -24
- package/src/sync/simple.ts +45 -14
package/src/sync/factory.ts
CHANGED
|
@@ -87,11 +87,20 @@ export function createSyncronizer<R extends "u32" | "u64">(
|
|
|
87
87
|
end1: bigint | number;
|
|
88
88
|
start2: bigint | number;
|
|
89
89
|
end2: bigint | number;
|
|
90
|
+
limit?: number;
|
|
90
91
|
}) => {
|
|
91
92
|
const nativeState = props.getNativeState();
|
|
93
|
+
const limit = range.limit;
|
|
94
|
+
if (limit == null) {
|
|
95
|
+
return (
|
|
96
|
+
nativeState?.getEntryHashNumbersInRangeU64?.(range) ??
|
|
97
|
+
nativeState?.getEntryHashNumbersInRange(range)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
const limitedRange = { ...range, limit };
|
|
92
101
|
return (
|
|
93
|
-
nativeState?.
|
|
94
|
-
nativeState?.
|
|
102
|
+
nativeState?.getEntryHashNumbersInRangeU64Limited?.(limitedRange) ??
|
|
103
|
+
nativeState?.getEntryHashNumbersInRangeLimited?.(limitedRange)
|
|
95
104
|
);
|
|
96
105
|
};
|
|
97
106
|
|
package/src/sync/index.ts
CHANGED
|
@@ -34,6 +34,14 @@ export type SyncOptions<R extends "u32" | "u64"> = {
|
|
|
34
34
|
*/
|
|
35
35
|
maxSimpleEntries?: number;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Maximum number of local entries materialized into the range encoder for
|
|
39
|
+
* one incoming rateless sync. Ranges above this limit fall back to bounded
|
|
40
|
+
* simple sync. Defaults to 16,384. This is a per-process entry cap, not an
|
|
41
|
+
* aggregate byte budget across concurrent syncs.
|
|
42
|
+
*/
|
|
43
|
+
maxRatelessReceiveRangeEntries?: number;
|
|
44
|
+
|
|
37
45
|
/**
|
|
38
46
|
* Maximum number of hash strings in one simple sync message.
|
|
39
47
|
*/
|
|
@@ -141,16 +149,18 @@ export type HashSymbolResolver = (
|
|
|
141
149
|
|
|
142
150
|
export type HashSymbolHashListResolver = (
|
|
143
151
|
symbols: HashSymbolInput,
|
|
144
|
-
) =>
|
|
145
|
-
| Iterable<string>
|
|
146
|
-
| undefined
|
|
147
|
-
| Promise<Iterable<string> | undefined>;
|
|
152
|
+
) => Iterable<string> | undefined | Promise<Iterable<string> | undefined>;
|
|
148
153
|
|
|
149
154
|
export type HashSymbolRangeResolver = (range: {
|
|
150
155
|
start1: bigint | number;
|
|
151
156
|
end1: bigint | number;
|
|
152
157
|
start2: bigint | number;
|
|
153
158
|
end2: bigint | number;
|
|
159
|
+
/**
|
|
160
|
+
* Maximum values to return, including any overflow sentinel. Omitted only by
|
|
161
|
+
* legacy custom synchronizers; built-in receive paths always provide it.
|
|
162
|
+
*/
|
|
163
|
+
limit?: number;
|
|
154
164
|
}) =>
|
|
155
165
|
| Iterable<bigint | number>
|
|
156
166
|
| undefined
|
|
@@ -91,6 +91,11 @@ const CODED_SYMBOL_WORDS = 3;
|
|
|
91
91
|
const CODED_SYMBOL_WORD_BYTES = 8;
|
|
92
92
|
const CODED_SYMBOL_BYTES = CODED_SYMBOL_WORDS * CODED_SYMBOL_WORD_BYTES;
|
|
93
93
|
const MAX_CODED_SYMBOL_BATCH_SIZE = 1_024;
|
|
94
|
+
const DEFAULT_MAX_RATELESS_RECEIVE_RANGE_ENTRIES = 16_384;
|
|
95
|
+
const RATELESS_RANGE_QUERY_PAGE_SIZE = 1_024;
|
|
96
|
+
// Native range resolvers accept a u32 limit. Keep one value available as the
|
|
97
|
+
// overflow sentinel used to distinguish an exact fit from a larger range.
|
|
98
|
+
const MAX_RATELESS_RANGE_ENTRIES = 0xffff_fffe;
|
|
94
99
|
const BIG_UINT64_ARRAY_IS_LITTLE_ENDIAN =
|
|
95
100
|
typeof BigUint64Array !== "undefined" &&
|
|
96
101
|
new Uint8Array(new BigUint64Array([1n]).buffer)[0] === 1;
|
|
@@ -656,6 +661,7 @@ const buildEncoderOrDecoderFromRange = async <
|
|
|
656
661
|
},
|
|
657
662
|
entryIndex: Index<EntryReplicated<D>>,
|
|
658
663
|
type: T,
|
|
664
|
+
maxEntries: number,
|
|
659
665
|
profile?: SyncProfileFn,
|
|
660
666
|
resolveHashNumbersInRange?: HashSymbolRangeResolver,
|
|
661
667
|
): Promise<E | false> => {
|
|
@@ -665,6 +671,7 @@ const buildEncoderOrDecoderFromRange = async <
|
|
|
665
671
|
let transferred = false;
|
|
666
672
|
try {
|
|
667
673
|
const rangeQueryStartedAt = syncProfileStart(profile);
|
|
674
|
+
const queryLimit = maxEntries + 1;
|
|
668
675
|
let hashNumbers: Array<bigint | number> | BigUint64Array | undefined;
|
|
669
676
|
let source = "index";
|
|
670
677
|
const resolved = await resolveHashNumbersInRange?.({
|
|
@@ -672,45 +679,87 @@ const buildEncoderOrDecoderFromRange = async <
|
|
|
672
679
|
start1: ranges.start1,
|
|
673
680
|
end2: ranges.end2,
|
|
674
681
|
start2: ranges.start2,
|
|
682
|
+
limit: queryLimit,
|
|
675
683
|
});
|
|
676
684
|
if (resolved) {
|
|
677
685
|
source = "native";
|
|
678
|
-
|
|
686
|
+
if (
|
|
679
687
|
typeof BigUint64Array !== "undefined" &&
|
|
680
688
|
resolved instanceof BigUint64Array
|
|
681
|
-
|
|
682
|
-
|
|
689
|
+
) {
|
|
690
|
+
hashNumbers = resolved;
|
|
691
|
+
} else {
|
|
692
|
+
hashNumbers = [];
|
|
693
|
+
const iterator = resolved[Symbol.iterator]();
|
|
694
|
+
let exhausted = false;
|
|
695
|
+
try {
|
|
696
|
+
while (hashNumbers.length < queryLimit) {
|
|
697
|
+
const next = iterator.next();
|
|
698
|
+
if (next.done) {
|
|
699
|
+
exhausted = true;
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
hashNumbers.push(next.value);
|
|
703
|
+
}
|
|
704
|
+
} finally {
|
|
705
|
+
if (!exhausted) {
|
|
706
|
+
iterator.return?.();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
683
710
|
} else {
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
},
|
|
711
|
+
const iterator = entryIndex.iterate(
|
|
712
|
+
{
|
|
713
|
+
consistency: "weak",
|
|
714
|
+
// Range sync for IBLT is done in hashNumber space.
|
|
715
|
+
query: matchEntriesByHashNumberInRangeQuery({
|
|
716
|
+
end1: ranges.end1,
|
|
717
|
+
start1: ranges.start1,
|
|
718
|
+
end2: ranges.end2,
|
|
719
|
+
start2: ranges.start2,
|
|
720
|
+
}),
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
shape: {
|
|
724
|
+
hash: true,
|
|
725
|
+
hashNumber: true,
|
|
700
726
|
},
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
hashNumbers =
|
|
727
|
+
},
|
|
728
|
+
);
|
|
729
|
+
hashNumbers = [];
|
|
730
|
+
try {
|
|
731
|
+
while (hashNumbers.length < queryLimit) {
|
|
732
|
+
const remaining = queryLimit - hashNumbers.length;
|
|
733
|
+
const entries = await iterator.next(
|
|
734
|
+
Math.min(RATELESS_RANGE_QUERY_PAGE_SIZE, remaining),
|
|
735
|
+
);
|
|
736
|
+
if (entries.length === 0) {
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
for (const entry of entries) {
|
|
740
|
+
if (hashNumbers.length >= queryLimit) {
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
hashNumbers.push(entry.value.hashNumber);
|
|
744
|
+
}
|
|
745
|
+
if (iterator.done()) {
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} finally {
|
|
750
|
+
await iterator.close();
|
|
751
|
+
}
|
|
704
752
|
}
|
|
753
|
+
const overflow = hashNumbers.length > maxEntries;
|
|
705
754
|
if (profile) {
|
|
706
755
|
emitSyncProfileDuration(profile, rangeQueryStartedAt, {
|
|
707
756
|
name: "rateless.rangeQuery",
|
|
708
757
|
entries: hashNumbers.length,
|
|
709
|
-
details: { type, source },
|
|
758
|
+
details: { type, source, overflow, maxEntries },
|
|
710
759
|
});
|
|
711
760
|
}
|
|
712
761
|
|
|
713
|
-
if (hashNumbers.length === 0) {
|
|
762
|
+
if (hashNumbers.length === 0 || overflow) {
|
|
714
763
|
return false;
|
|
715
764
|
}
|
|
716
765
|
|
|
@@ -912,6 +961,13 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
912
961
|
: DEFAULT_MAX_CONVERGENT_TRACKED_HASHES;
|
|
913
962
|
}
|
|
914
963
|
|
|
964
|
+
private get maxRatelessReceiveRangeEntries() {
|
|
965
|
+
const value = this.properties.sync?.maxRatelessReceiveRangeEntries;
|
|
966
|
+
return value && Number.isFinite(value) && value > 0
|
|
967
|
+
? Math.min(MAX_RATELESS_RANGE_ENTRIES, Math.max(1, Math.floor(value)))
|
|
968
|
+
: DEFAULT_MAX_RATELESS_RECEIVE_RANGE_ENTRIES;
|
|
969
|
+
}
|
|
970
|
+
|
|
915
971
|
private normalizeRetryIntervals(retryIntervalsMs?: number[]): number[] {
|
|
916
972
|
if (!retryIntervalsMs || retryIntervalsMs.length === 0) {
|
|
917
973
|
return [...DEFAULT_CONVERGENT_RETRY_INTERVALS_MS];
|
|
@@ -1342,6 +1398,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
1342
1398
|
ranges,
|
|
1343
1399
|
this.properties.entryIndex,
|
|
1344
1400
|
"encoder",
|
|
1401
|
+
this.maxRatelessReceiveRangeEntries,
|
|
1345
1402
|
profile,
|
|
1346
1403
|
this.properties.resolveHashNumbersInRange,
|
|
1347
1404
|
)) as EncoderWrapper | false;
|
package/src/sync/simple.ts
CHANGED
|
@@ -139,6 +139,8 @@ type KnownSyncKeys = {
|
|
|
139
139
|
checkedHashes: boolean;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
+
const SIMPLE_COORDINATE_INDEX_PAGE_SIZE = 1_024;
|
|
143
|
+
|
|
142
144
|
const getHashesFromSymbols = async (
|
|
143
145
|
symbols: bigint[],
|
|
144
146
|
entryIndex: Index<EntryReplicated<any>, any>,
|
|
@@ -151,6 +153,7 @@ const getHashesFromSymbols = async (
|
|
|
151
153
|
let batchSize = 128; // TODO arg
|
|
152
154
|
let results = new Set<string>();
|
|
153
155
|
let missingSymbols: bigint[] = [];
|
|
156
|
+
let remainingIndexCandidates = maxHashes;
|
|
154
157
|
const addHash = (hash: string) => {
|
|
155
158
|
if (results.has(hash)) {
|
|
156
159
|
return true;
|
|
@@ -171,22 +174,50 @@ const getHashesFromSymbols = async (
|
|
|
171
174
|
};
|
|
172
175
|
const handleBatch = async (end = false) => {
|
|
173
176
|
if (queries.length >= batchSize || (end && queries.length > 0)) {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
const remainingHashes = maxHashes - results.size;
|
|
178
|
+
if (remainingHashes <= 0 || remainingIndexCandidates <= 0) {
|
|
179
|
+
queries = [];
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const iterator = entryIndex.iterate(
|
|
183
|
+
{
|
|
184
|
+
consistency: "weak",
|
|
185
|
+
query: queries.length > 1 ? new Or(queries) : queries,
|
|
186
|
+
},
|
|
187
|
+
{ shape: { hash: true, hashNumber: true } },
|
|
188
|
+
);
|
|
180
189
|
queries = [];
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
190
|
+
try {
|
|
191
|
+
while (results.size < maxHashes && remainingIndexCandidates > 0) {
|
|
192
|
+
const entries = await iterator.next(
|
|
193
|
+
Math.min(
|
|
194
|
+
SIMPLE_COORDINATE_INDEX_PAGE_SIZE,
|
|
195
|
+
maxHashes - results.size,
|
|
196
|
+
remainingIndexCandidates,
|
|
197
|
+
),
|
|
198
|
+
);
|
|
199
|
+
if (entries.length === 0) {
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
for (const entry of entries) {
|
|
203
|
+
if (remainingIndexCandidates <= 0) {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
remainingIndexCandidates -= 1;
|
|
207
|
+
if (!addHash(entry.value.hash)) {
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
coordinateToHash.add(entry.value.hashNumber, entry.value.hash);
|
|
211
|
+
if (results.size >= maxHashes) {
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (iterator.done()) {
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
189
218
|
}
|
|
219
|
+
} finally {
|
|
220
|
+
await iterator.close();
|
|
190
221
|
}
|
|
191
222
|
}
|
|
192
223
|
};
|