@peerbit/shared-log 12.2.0-369b236 → 12.2.0-62829ef
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/benchmark/rateless-iblt-startsync-cache.d.ts +2 -0
- package/dist/benchmark/rateless-iblt-startsync-cache.d.ts.map +1 -0
- package/dist/benchmark/rateless-iblt-startsync-cache.js +96 -0
- package/dist/benchmark/rateless-iblt-startsync-cache.js.map +1 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/sync/index.d.ts +14 -0
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.d.ts +14 -22
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.js +99 -7
- package/dist/src/sync/rateless-iblt.js.map +1 -1
- package/dist/src/sync/simple.d.ts +3 -1
- package/dist/src/sync/simple.d.ts.map +1 -1
- package/dist/src/sync/simple.js +23 -1
- package/dist/src/sync/simple.js.map +1 -1
- package/package.json +18 -18
- package/src/index.ts +9 -1
- package/src/sync/index.ts +19 -0
- package/src/sync/rateless-iblt.ts +153 -26
- package/src/sync/simple.ts +25 -2
|
@@ -12,14 +12,16 @@ import {
|
|
|
12
12
|
import type { RPC, RequestContext } from "@peerbit/rpc";
|
|
13
13
|
import { SilentDelivery } from "@peerbit/stream-interface";
|
|
14
14
|
import { type EntryWithRefs } from "../exchange-heads.js";
|
|
15
|
-
import { type Numbers } from "../integers.js";
|
|
16
15
|
import { TransportMessage } from "../message.js";
|
|
17
16
|
import {
|
|
18
17
|
type EntryReplicated,
|
|
19
|
-
type ReplicationRangeIndexable,
|
|
20
18
|
matchEntriesInRangeQuery,
|
|
21
19
|
} from "../ranges.js";
|
|
22
|
-
import type {
|
|
20
|
+
import type {
|
|
21
|
+
SyncableKey,
|
|
22
|
+
SynchronizerComponents,
|
|
23
|
+
Syncronizer,
|
|
24
|
+
} from "./index.js";
|
|
23
25
|
import { SimpleSyncronizer } from "./simple.js";
|
|
24
26
|
|
|
25
27
|
export const logger = loggerFn("peerbit:shared-log:rateless");
|
|
@@ -185,6 +187,13 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
185
187
|
simple: SimpleSyncronizer<D>;
|
|
186
188
|
|
|
187
189
|
startedOrCompletedSynchronizations: Cache<string>;
|
|
190
|
+
private localRangeEncoderCacheVersion = 0;
|
|
191
|
+
private localRangeEncoderCache: Map<
|
|
192
|
+
string,
|
|
193
|
+
{ encoder: EncoderWrapper; version: number; lastUsed: number }
|
|
194
|
+
> = new Map();
|
|
195
|
+
private localRangeEncoderCacheMax = 2;
|
|
196
|
+
|
|
188
197
|
ingoingSyncProcesses: Map<
|
|
189
198
|
string,
|
|
190
199
|
{
|
|
@@ -212,14 +221,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
212
221
|
>;
|
|
213
222
|
|
|
214
223
|
constructor(
|
|
215
|
-
readonly properties:
|
|
216
|
-
rpc: RPC<TransportMessage, TransportMessage>;
|
|
217
|
-
rangeIndex: Index<ReplicationRangeIndexable<D>, any>;
|
|
218
|
-
entryIndex: Index<EntryReplicated<D>, any>;
|
|
219
|
-
log: Log<any>;
|
|
220
|
-
coordinateToHash: Cache<string>;
|
|
221
|
-
numbers: Numbers<D>;
|
|
222
|
-
},
|
|
224
|
+
readonly properties: SynchronizerComponents<D>,
|
|
223
225
|
) {
|
|
224
226
|
this.simple = new SimpleSyncronizer(properties);
|
|
225
227
|
this.outgoingSyncProcesses = new Map();
|
|
@@ -227,6 +229,91 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
227
229
|
this.startedOrCompletedSynchronizations = new Cache({ max: 1e4 });
|
|
228
230
|
}
|
|
229
231
|
|
|
232
|
+
private clearLocalRangeEncoderCache() {
|
|
233
|
+
for (const [, cached] of this.localRangeEncoderCache) {
|
|
234
|
+
cached.encoder.free();
|
|
235
|
+
}
|
|
236
|
+
this.localRangeEncoderCache.clear();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private invalidateLocalRangeEncoderCache() {
|
|
240
|
+
this.localRangeEncoderCacheVersion += 1;
|
|
241
|
+
this.clearLocalRangeEncoderCache();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private localRangeEncoderCacheKey(ranges: {
|
|
245
|
+
start1: NumberOrBigint;
|
|
246
|
+
end1: NumberOrBigint;
|
|
247
|
+
start2: NumberOrBigint;
|
|
248
|
+
end2: NumberOrBigint;
|
|
249
|
+
}) {
|
|
250
|
+
return `${String(ranges.start1)}:${String(ranges.end1)}:${String(
|
|
251
|
+
ranges.start2,
|
|
252
|
+
)}:${String(ranges.end2)}`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private decoderFromCachedEncoder(encoder: EncoderWrapper): DecoderWrapper {
|
|
256
|
+
const clone = encoder.clone();
|
|
257
|
+
const decoder = clone.to_decoder();
|
|
258
|
+
clone.free();
|
|
259
|
+
return decoder;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private async getLocalDecoderForRange(ranges: {
|
|
263
|
+
start1: NumberOrBigint;
|
|
264
|
+
end1: NumberOrBigint;
|
|
265
|
+
start2: NumberOrBigint;
|
|
266
|
+
end2: NumberOrBigint;
|
|
267
|
+
}): Promise<DecoderWrapper | false> {
|
|
268
|
+
const key = this.localRangeEncoderCacheKey(ranges);
|
|
269
|
+
const cached = this.localRangeEncoderCache.get(key);
|
|
270
|
+
if (cached && cached.version === this.localRangeEncoderCacheVersion) {
|
|
271
|
+
cached.lastUsed = Date.now();
|
|
272
|
+
return this.decoderFromCachedEncoder(cached.encoder);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const encoder = (await buildEncoderOrDecoderFromRange(
|
|
276
|
+
ranges,
|
|
277
|
+
this.properties.entryIndex,
|
|
278
|
+
"encoder",
|
|
279
|
+
)) as EncoderWrapper | false;
|
|
280
|
+
if (!encoder) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const now = Date.now();
|
|
285
|
+
const existing = this.localRangeEncoderCache.get(key);
|
|
286
|
+
if (existing) {
|
|
287
|
+
existing.encoder.free();
|
|
288
|
+
}
|
|
289
|
+
this.localRangeEncoderCache.set(key, {
|
|
290
|
+
encoder,
|
|
291
|
+
version: this.localRangeEncoderCacheVersion,
|
|
292
|
+
lastUsed: now,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
while (this.localRangeEncoderCache.size > this.localRangeEncoderCacheMax) {
|
|
296
|
+
let oldestKey: string | undefined;
|
|
297
|
+
let oldestUsed = Number.POSITIVE_INFINITY;
|
|
298
|
+
for (const [candidateKey, value] of this.localRangeEncoderCache) {
|
|
299
|
+
if (value.lastUsed < oldestUsed) {
|
|
300
|
+
oldestUsed = value.lastUsed;
|
|
301
|
+
oldestKey = candidateKey;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (!oldestKey) {
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
const victim = this.localRangeEncoderCache.get(oldestKey);
|
|
308
|
+
if (victim) {
|
|
309
|
+
victim.encoder.free();
|
|
310
|
+
}
|
|
311
|
+
this.localRangeEncoderCache.delete(oldestKey);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return this.decoderFromCachedEncoder(encoder);
|
|
315
|
+
}
|
|
316
|
+
|
|
230
317
|
async onMaybeMissingEntries(properties: {
|
|
231
318
|
entries: Map<string, EntryReplicated<D>>;
|
|
232
319
|
targets: string[];
|
|
@@ -238,7 +325,6 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
238
325
|
// such as those assigned to range boundaries.
|
|
239
326
|
|
|
240
327
|
let entriesToSyncNaively: Map<string, EntryReplicated<D>> = new Map();
|
|
241
|
-
let allCoordinatesToSyncWithIblt: bigint[] = [];
|
|
242
328
|
let minSyncIbltSize = 333; // TODO: make configurable
|
|
243
329
|
let maxSyncWithSimpleMethod = 1e3;
|
|
244
330
|
|
|
@@ -251,17 +337,57 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
251
337
|
return;
|
|
252
338
|
}
|
|
253
339
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// Mixed strategy for larger batches
|
|
340
|
+
const nonBoundaryEntries: EntryReplicated<D>[] = [];
|
|
257
341
|
for (const entry of properties.entries.values()) {
|
|
258
342
|
if (entry.assignedToRangeBoundary) {
|
|
259
343
|
entriesToSyncNaively.set(entry.hash, entry);
|
|
260
344
|
} else {
|
|
261
|
-
|
|
345
|
+
nonBoundaryEntries.push(entry);
|
|
262
346
|
}
|
|
263
347
|
}
|
|
264
348
|
|
|
349
|
+
const priorityFn = this.properties.sync?.priority;
|
|
350
|
+
const maxSimpleEntries = this.properties.sync?.maxSimpleEntries;
|
|
351
|
+
const maxAdditionalNaive =
|
|
352
|
+
priorityFn &&
|
|
353
|
+
typeof maxSimpleEntries === "number" &&
|
|
354
|
+
Number.isFinite(maxSimpleEntries) &&
|
|
355
|
+
maxSimpleEntries > 0
|
|
356
|
+
? Math.max(
|
|
357
|
+
0,
|
|
358
|
+
Math.min(
|
|
359
|
+
Math.floor(maxSimpleEntries),
|
|
360
|
+
maxSyncWithSimpleMethod - entriesToSyncNaively.size,
|
|
361
|
+
),
|
|
362
|
+
)
|
|
363
|
+
: 0;
|
|
364
|
+
|
|
365
|
+
if (priorityFn && maxAdditionalNaive > 0 && nonBoundaryEntries.length > 0) {
|
|
366
|
+
let index = 0;
|
|
367
|
+
const scored: {
|
|
368
|
+
entry: EntryReplicated<D>;
|
|
369
|
+
index: number;
|
|
370
|
+
priority: number;
|
|
371
|
+
}[] = [];
|
|
372
|
+
for (const entry of nonBoundaryEntries) {
|
|
373
|
+
const priorityValue = priorityFn(entry);
|
|
374
|
+
scored.push({
|
|
375
|
+
entry,
|
|
376
|
+
index,
|
|
377
|
+
priority: Number.isFinite(priorityValue) ? priorityValue : 0,
|
|
378
|
+
});
|
|
379
|
+
index += 1;
|
|
380
|
+
}
|
|
381
|
+
scored.sort((a, b) => b.priority - a.priority || a.index - b.index);
|
|
382
|
+
for (const { entry } of scored.slice(0, maxAdditionalNaive)) {
|
|
383
|
+
entriesToSyncNaively.set(entry.hash, entry);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
let allCoordinatesToSyncWithIblt = nonBoundaryEntries
|
|
388
|
+
.filter((entry) => !entriesToSyncNaively.has(entry.hash))
|
|
389
|
+
.map((entry) => coerceBigInt(entry.hashNumber));
|
|
390
|
+
|
|
265
391
|
if (entriesToSyncNaively.size > 0) {
|
|
266
392
|
// If there are special-case entries, sync them simply in parallel
|
|
267
393
|
await this.simple.onMaybeMissingEntries({
|
|
@@ -284,6 +410,8 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
284
410
|
return;
|
|
285
411
|
}
|
|
286
412
|
|
|
413
|
+
await ribltReady;
|
|
414
|
+
|
|
287
415
|
const sortedEntries = allCoordinatesToSyncWithIblt.sort((a, b) => {
|
|
288
416
|
if (a > b) {
|
|
289
417
|
return 1;
|
|
@@ -406,16 +534,12 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
406
534
|
this.startedOrCompletedSynchronizations.add(syncId);
|
|
407
535
|
|
|
408
536
|
const wrapped = message.end < message.start;
|
|
409
|
-
const decoder = await
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
},
|
|
416
|
-
this.properties.entryIndex,
|
|
417
|
-
"decoder",
|
|
418
|
-
);
|
|
537
|
+
const decoder = await this.getLocalDecoderForRange({
|
|
538
|
+
start1: message.start,
|
|
539
|
+
end1: wrapped ? this.properties.numbers.maxValue : message.end,
|
|
540
|
+
start2: 0n,
|
|
541
|
+
end2: wrapped ? message.end : 0n,
|
|
542
|
+
});
|
|
419
543
|
|
|
420
544
|
if (!decoder) {
|
|
421
545
|
await this.simple.rpc.send(
|
|
@@ -620,10 +744,12 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
620
744
|
}
|
|
621
745
|
|
|
622
746
|
onEntryAdded(entry: Entry<any>): void {
|
|
747
|
+
this.invalidateLocalRangeEncoderCache();
|
|
623
748
|
return this.simple.onEntryAdded(entry);
|
|
624
749
|
}
|
|
625
750
|
|
|
626
751
|
onEntryRemoved(hash: string) {
|
|
752
|
+
this.invalidateLocalRangeEncoderCache();
|
|
627
753
|
return this.simple.onEntryRemoved(hash);
|
|
628
754
|
}
|
|
629
755
|
|
|
@@ -642,6 +768,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
642
768
|
for (const [, obj] of this.outgoingSyncProcesses) {
|
|
643
769
|
obj.free();
|
|
644
770
|
}
|
|
771
|
+
this.clearLocalRangeEncoderCache();
|
|
645
772
|
return this.simple.close();
|
|
646
773
|
}
|
|
647
774
|
|
package/src/sync/simple.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "../exchange-heads.js";
|
|
17
17
|
import { TransportMessage } from "../message.js";
|
|
18
18
|
import type { EntryReplicated } from "../ranges.js";
|
|
19
|
-
import type { SyncableKey, Syncronizer } from "./index.js";
|
|
19
|
+
import type { SyncableKey, SyncOptions, Syncronizer } from "./index.js";
|
|
20
20
|
|
|
21
21
|
@variant([0, 1])
|
|
22
22
|
export class RequestMaybeSync extends TransportMessage {
|
|
@@ -109,6 +109,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
109
109
|
log: Log<any>;
|
|
110
110
|
entryIndex: Index<EntryReplicated<R>, any>;
|
|
111
111
|
coordinateToHash: Cache<string>;
|
|
112
|
+
private syncOptions?: SyncOptions<R>;
|
|
112
113
|
|
|
113
114
|
// Syncing and dedeplucation work
|
|
114
115
|
syncMoreInterval?: ReturnType<typeof setTimeout>;
|
|
@@ -120,6 +121,7 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
120
121
|
entryIndex: Index<EntryReplicated<R>, any>;
|
|
121
122
|
log: Log<any>;
|
|
122
123
|
coordinateToHash: Cache<string>;
|
|
124
|
+
sync?: SyncOptions<R>;
|
|
123
125
|
}) {
|
|
124
126
|
this.syncInFlightQueue = new Map();
|
|
125
127
|
this.syncInFlightQueueInverted = new Map();
|
|
@@ -128,14 +130,35 @@ export class SimpleSyncronizer<R extends "u32" | "u64">
|
|
|
128
130
|
this.log = properties.log;
|
|
129
131
|
this.entryIndex = properties.entryIndex;
|
|
130
132
|
this.coordinateToHash = properties.coordinateToHash;
|
|
133
|
+
this.syncOptions = properties.sync;
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
onMaybeMissingEntries(properties: {
|
|
134
137
|
entries: Map<string, EntryReplicated<R>>;
|
|
135
138
|
targets: string[];
|
|
136
139
|
}): Promise<void> {
|
|
140
|
+
let hashes: string[];
|
|
141
|
+
const priorityFn = this.syncOptions?.priority;
|
|
142
|
+
if (priorityFn) {
|
|
143
|
+
let index = 0;
|
|
144
|
+
const scored: { hash: string; index: number; priority: number }[] = [];
|
|
145
|
+
for (const [hash, entry] of properties.entries) {
|
|
146
|
+
const priorityValue = priorityFn(entry);
|
|
147
|
+
scored.push({
|
|
148
|
+
hash,
|
|
149
|
+
index,
|
|
150
|
+
priority: Number.isFinite(priorityValue) ? priorityValue : 0,
|
|
151
|
+
});
|
|
152
|
+
index += 1;
|
|
153
|
+
}
|
|
154
|
+
scored.sort((a, b) => b.priority - a.priority || a.index - b.index);
|
|
155
|
+
hashes = scored.map((x) => x.hash);
|
|
156
|
+
} else {
|
|
157
|
+
hashes = [...properties.entries.keys()];
|
|
158
|
+
}
|
|
159
|
+
|
|
137
160
|
return this.rpc.send(
|
|
138
|
-
new RequestMaybeSync({ hashes
|
|
161
|
+
new RequestMaybeSync({ hashes }),
|
|
139
162
|
{
|
|
140
163
|
priority: 1,
|
|
141
164
|
mode: new SilentDelivery({ to: properties.targets, redundancy: 1 }),
|