@peerbit/shared-log 12.1.3 → 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 +3 -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/like.d.ts +71 -0
- package/dist/src/like.d.ts.map +1 -0
- package/dist/src/like.js +2 -0
- package/dist/src/like.js.map +1 -0
- 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 +101 -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 +24 -2
- package/dist/src/sync/simple.js.map +1 -1
- package/package.json +20 -20
- package/src/index.ts +15 -1
- package/src/like.ts +84 -0
- package/src/sync/index.ts +19 -0
- package/src/sync/rateless-iblt.ts +159 -25
- package/src/sync/simple.ts +26 -3
package/src/index.ts
CHANGED
|
@@ -135,7 +135,11 @@ import {
|
|
|
135
135
|
maxReplicas,
|
|
136
136
|
} from "./replication.js";
|
|
137
137
|
import { Observer, Replicator } from "./role.js";
|
|
138
|
-
import type {
|
|
138
|
+
import type {
|
|
139
|
+
SyncOptions,
|
|
140
|
+
SynchronizerConstructor,
|
|
141
|
+
Syncronizer,
|
|
142
|
+
} from "./sync/index.js";
|
|
139
143
|
import { RatelessIBLTSynchronizer } from "./sync/rateless-iblt.js";
|
|
140
144
|
import { SimpleSyncronizer } from "./sync/simple.js";
|
|
141
145
|
import { groupByGid } from "./utils.js";
|
|
@@ -149,6 +153,12 @@ export {
|
|
|
149
153
|
};
|
|
150
154
|
export { type CPUUsage, CPUUsageIntervalLag };
|
|
151
155
|
export * from "./replication.js";
|
|
156
|
+
export type {
|
|
157
|
+
LogLike,
|
|
158
|
+
LogResultsIterator,
|
|
159
|
+
SharedLogLike,
|
|
160
|
+
SharedLogReplicationIndexLike,
|
|
161
|
+
} from "./like.js";
|
|
152
162
|
export {
|
|
153
163
|
type ReplicationRangeIndexable,
|
|
154
164
|
ReplicationRangeIndexableU32,
|
|
@@ -352,6 +362,7 @@ export type SharedLogOptions<
|
|
|
352
362
|
keep?: (
|
|
353
363
|
entry: ShallowOrFullEntry<T> | EntryReplicated<R>,
|
|
354
364
|
) => Promise<boolean> | boolean;
|
|
365
|
+
sync?: SyncOptions<R>;
|
|
355
366
|
syncronizer?: SynchronizerConstructor<R>;
|
|
356
367
|
timeUntilRoleMaturity?: number;
|
|
357
368
|
waitForReplicatorTimeout?: number;
|
|
@@ -2037,6 +2048,7 @@ export class SharedLog<
|
|
|
2037
2048
|
rangeIndex: this._replicationRangeIndex,
|
|
2038
2049
|
rpc: this.rpc,
|
|
2039
2050
|
coordinateToHash: this.coordinateToHash,
|
|
2051
|
+
sync: options?.sync,
|
|
2040
2052
|
});
|
|
2041
2053
|
} else {
|
|
2042
2054
|
if (
|
|
@@ -2048,6 +2060,7 @@ export class SharedLog<
|
|
|
2048
2060
|
rpc: this.rpc,
|
|
2049
2061
|
entryIndex: this.entryCoordinatesIndex,
|
|
2050
2062
|
coordinateToHash: this.coordinateToHash,
|
|
2063
|
+
sync: options?.sync,
|
|
2051
2064
|
});
|
|
2052
2065
|
} else {
|
|
2053
2066
|
if (this.domain.resolution === "u32") {
|
|
@@ -2063,6 +2076,7 @@ export class SharedLog<
|
|
|
2063
2076
|
rangeIndex: this._replicationRangeIndex,
|
|
2064
2077
|
rpc: this.rpc,
|
|
2065
2078
|
coordinateToHash: this.coordinateToHash,
|
|
2079
|
+
sync: options?.sync,
|
|
2066
2080
|
}) as Syncronizer<R>;
|
|
2067
2081
|
}
|
|
2068
2082
|
}
|
package/src/like.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { PublicSignKey } from "@peerbit/crypto";
|
|
2
|
+
import type {
|
|
3
|
+
CountOptions,
|
|
4
|
+
IndexIterator,
|
|
5
|
+
IterateOptions,
|
|
6
|
+
} from "@peerbit/indexer-interface";
|
|
7
|
+
import type { Entry } from "@peerbit/log";
|
|
8
|
+
import type { ShallowEntry } from "@peerbit/log";
|
|
9
|
+
import type { ReplicationOptions, ReplicationRangeIndexable } from "./index.js";
|
|
10
|
+
|
|
11
|
+
export type LogBlocksLike = {
|
|
12
|
+
has: (hash: string) => Promise<boolean> | boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type LogResultsIterator<T> = {
|
|
16
|
+
close: () => void | Promise<void>;
|
|
17
|
+
next: (amount: number) => T[] | Promise<T[]>;
|
|
18
|
+
done: () => boolean | undefined;
|
|
19
|
+
all: () => T[] | Promise<T[]>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type LogLike<T = any> = {
|
|
23
|
+
idString?: string;
|
|
24
|
+
length: number;
|
|
25
|
+
get: (
|
|
26
|
+
hash: string,
|
|
27
|
+
options?: any,
|
|
28
|
+
) => Promise<Entry<T> | undefined> | Entry<T> | undefined;
|
|
29
|
+
has: (hash: string) => Promise<boolean> | boolean;
|
|
30
|
+
getHeads: (resolve?: boolean) => LogResultsIterator<Entry<T> | ShallowEntry>;
|
|
31
|
+
toArray: () => Promise<Entry<T>[]>;
|
|
32
|
+
blocks?: LogBlocksLike;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type SharedLogReplicationIndexLike<R extends "u32" | "u64" = any> = {
|
|
36
|
+
iterate: (
|
|
37
|
+
request?: IterateOptions,
|
|
38
|
+
) => IndexIterator<ReplicationRangeIndexable<R>, undefined>;
|
|
39
|
+
count: (options?: CountOptions) => Promise<number> | number;
|
|
40
|
+
getSize?: () => Promise<number> | number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type SharedLogLike<T = any, R extends "u32" | "u64" = any> = {
|
|
44
|
+
closed?: boolean;
|
|
45
|
+
events: EventTarget;
|
|
46
|
+
log: LogLike<T>;
|
|
47
|
+
replicationIndex: SharedLogReplicationIndexLike<R>;
|
|
48
|
+
node?: { identity: { publicKey: PublicSignKey } };
|
|
49
|
+
getReplicators: () => Promise<Set<string>>;
|
|
50
|
+
waitForReplicator: (
|
|
51
|
+
publicKey: PublicSignKey,
|
|
52
|
+
options?: {
|
|
53
|
+
eager?: boolean;
|
|
54
|
+
roleAge?: number;
|
|
55
|
+
timeout?: number;
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
},
|
|
58
|
+
) => Promise<void>;
|
|
59
|
+
waitForReplicators: (options?: {
|
|
60
|
+
timeout?: number;
|
|
61
|
+
roleAge?: number;
|
|
62
|
+
coverageThreshold?: number;
|
|
63
|
+
waitForNewPeers?: boolean;
|
|
64
|
+
signal?: AbortSignal;
|
|
65
|
+
}) => Promise<void>;
|
|
66
|
+
replicate: (
|
|
67
|
+
rangeOrEntry?: ReplicationOptions<R> | any,
|
|
68
|
+
options?: {
|
|
69
|
+
reset?: boolean;
|
|
70
|
+
checkDuplicates?: boolean;
|
|
71
|
+
rebalance?: boolean;
|
|
72
|
+
mergeSegments?: boolean;
|
|
73
|
+
},
|
|
74
|
+
) => Promise<void | ReplicationRangeIndexable<R>[]>;
|
|
75
|
+
unreplicate: (rangeOrEntry?: { id: Uint8Array }[]) => Promise<void>;
|
|
76
|
+
calculateCoverage: (options?: {
|
|
77
|
+
start?: number | bigint;
|
|
78
|
+
end?: number | bigint;
|
|
79
|
+
roleAge?: number;
|
|
80
|
+
}) => Promise<number>;
|
|
81
|
+
getMyReplicationSegments: () => Promise<ReplicationRangeIndexable<R>[]>;
|
|
82
|
+
getAllReplicationSegments: () => Promise<ReplicationRangeIndexable<R>[]>;
|
|
83
|
+
close: () => Promise<void | boolean>;
|
|
84
|
+
};
|
package/src/sync/index.ts
CHANGED
|
@@ -8,6 +8,24 @@ import type { Numbers } from "../integers.js";
|
|
|
8
8
|
import type { TransportMessage } from "../message.js";
|
|
9
9
|
import type { EntryReplicated, ReplicationRangeIndexable } from "../ranges.js";
|
|
10
10
|
|
|
11
|
+
export type SyncPriorityFn<R extends "u32" | "u64"> = (
|
|
12
|
+
entry: EntryReplicated<R>,
|
|
13
|
+
) => number;
|
|
14
|
+
|
|
15
|
+
export type SyncOptions<R extends "u32" | "u64"> = {
|
|
16
|
+
/**
|
|
17
|
+
* Higher numbers are synced first.
|
|
18
|
+
* The callback should be fast and side-effect free.
|
|
19
|
+
*/
|
|
20
|
+
priority?: SyncPriorityFn<R>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* When using rateless IBLT sync, optionally pre-sync up to this many
|
|
24
|
+
* high-priority entries using the simple synchronizer.
|
|
25
|
+
*/
|
|
26
|
+
maxSimpleEntries?: number;
|
|
27
|
+
};
|
|
28
|
+
|
|
11
29
|
export type SynchronizerComponents<R extends "u32" | "u64"> = {
|
|
12
30
|
rpc: RPC<TransportMessage, TransportMessage>;
|
|
13
31
|
rangeIndex: Index<ReplicationRangeIndexable<R>, any>;
|
|
@@ -15,6 +33,7 @@ export type SynchronizerComponents<R extends "u32" | "u64"> = {
|
|
|
15
33
|
log: Log<any>;
|
|
16
34
|
coordinateToHash: Cache<string>;
|
|
17
35
|
numbers: Numbers<R>;
|
|
36
|
+
sync?: SyncOptions<R>;
|
|
18
37
|
};
|
|
19
38
|
export type SynchronizerConstructor<R extends "u32" | "u64"> = new (
|
|
20
39
|
properties: SynchronizerComponents<R>,
|
|
@@ -4,18 +4,24 @@ import { type PublicSignKey, randomBytes, toBase64 } from "@peerbit/crypto";
|
|
|
4
4
|
import { type Index } from "@peerbit/indexer-interface";
|
|
5
5
|
import type { Entry, Log } from "@peerbit/log";
|
|
6
6
|
import { logger as loggerFn } from "@peerbit/logger";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
DecoderWrapper,
|
|
9
|
+
EncoderWrapper,
|
|
10
|
+
ready as ribltReady,
|
|
11
|
+
} from "@peerbit/riblt";
|
|
8
12
|
import type { RPC, RequestContext } from "@peerbit/rpc";
|
|
9
13
|
import { SilentDelivery } from "@peerbit/stream-interface";
|
|
10
14
|
import { type EntryWithRefs } from "../exchange-heads.js";
|
|
11
|
-
import { type Numbers } from "../integers.js";
|
|
12
15
|
import { TransportMessage } from "../message.js";
|
|
13
16
|
import {
|
|
14
17
|
type EntryReplicated,
|
|
15
|
-
type ReplicationRangeIndexable,
|
|
16
18
|
matchEntriesInRangeQuery,
|
|
17
19
|
} from "../ranges.js";
|
|
18
|
-
import type {
|
|
20
|
+
import type {
|
|
21
|
+
SyncableKey,
|
|
22
|
+
SynchronizerComponents,
|
|
23
|
+
Syncronizer,
|
|
24
|
+
} from "./index.js";
|
|
19
25
|
import { SimpleSyncronizer } from "./simple.js";
|
|
20
26
|
|
|
21
27
|
export const logger = loggerFn("peerbit:shared-log:rateless");
|
|
@@ -142,6 +148,7 @@ const buildEncoderOrDecoderFromRange = async <
|
|
|
142
148
|
entryIndex: Index<EntryReplicated<D>>,
|
|
143
149
|
type: T,
|
|
144
150
|
): Promise<E | false> => {
|
|
151
|
+
await ribltReady;
|
|
145
152
|
const encoder =
|
|
146
153
|
type === "encoder" ? new EncoderWrapper() : new DecoderWrapper();
|
|
147
154
|
|
|
@@ -180,6 +187,13 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
180
187
|
simple: SimpleSyncronizer<D>;
|
|
181
188
|
|
|
182
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
|
+
|
|
183
197
|
ingoingSyncProcesses: Map<
|
|
184
198
|
string,
|
|
185
199
|
{
|
|
@@ -207,14 +221,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
207
221
|
>;
|
|
208
222
|
|
|
209
223
|
constructor(
|
|
210
|
-
readonly properties:
|
|
211
|
-
rpc: RPC<TransportMessage, TransportMessage>;
|
|
212
|
-
rangeIndex: Index<ReplicationRangeIndexable<D>, any>;
|
|
213
|
-
entryIndex: Index<EntryReplicated<D>, any>;
|
|
214
|
-
log: Log<any>;
|
|
215
|
-
coordinateToHash: Cache<string>;
|
|
216
|
-
numbers: Numbers<D>;
|
|
217
|
-
},
|
|
224
|
+
readonly properties: SynchronizerComponents<D>,
|
|
218
225
|
) {
|
|
219
226
|
this.simple = new SimpleSyncronizer(properties);
|
|
220
227
|
this.outgoingSyncProcesses = new Map();
|
|
@@ -222,6 +229,91 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
222
229
|
this.startedOrCompletedSynchronizations = new Cache({ max: 1e4 });
|
|
223
230
|
}
|
|
224
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
|
+
|
|
225
317
|
async onMaybeMissingEntries(properties: {
|
|
226
318
|
entries: Map<string, EntryReplicated<D>>;
|
|
227
319
|
targets: string[];
|
|
@@ -233,7 +325,6 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
233
325
|
// such as those assigned to range boundaries.
|
|
234
326
|
|
|
235
327
|
let entriesToSyncNaively: Map<string, EntryReplicated<D>> = new Map();
|
|
236
|
-
let allCoordinatesToSyncWithIblt: bigint[] = [];
|
|
237
328
|
let minSyncIbltSize = 333; // TODO: make configurable
|
|
238
329
|
let maxSyncWithSimpleMethod = 1e3;
|
|
239
330
|
|
|
@@ -246,15 +337,57 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
246
337
|
return;
|
|
247
338
|
}
|
|
248
339
|
|
|
249
|
-
|
|
340
|
+
const nonBoundaryEntries: EntryReplicated<D>[] = [];
|
|
250
341
|
for (const entry of properties.entries.values()) {
|
|
251
342
|
if (entry.assignedToRangeBoundary) {
|
|
252
343
|
entriesToSyncNaively.set(entry.hash, entry);
|
|
253
344
|
} else {
|
|
254
|
-
|
|
345
|
+
nonBoundaryEntries.push(entry);
|
|
255
346
|
}
|
|
256
347
|
}
|
|
257
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
|
+
|
|
258
391
|
if (entriesToSyncNaively.size > 0) {
|
|
259
392
|
// If there are special-case entries, sync them simply in parallel
|
|
260
393
|
await this.simple.onMaybeMissingEntries({
|
|
@@ -277,6 +410,8 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
277
410
|
return;
|
|
278
411
|
}
|
|
279
412
|
|
|
413
|
+
await ribltReady;
|
|
414
|
+
|
|
280
415
|
const sortedEntries = allCoordinatesToSyncWithIblt.sort((a, b) => {
|
|
281
416
|
if (a > b) {
|
|
282
417
|
return 1;
|
|
@@ -399,16 +534,12 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
399
534
|
this.startedOrCompletedSynchronizations.add(syncId);
|
|
400
535
|
|
|
401
536
|
const wrapped = message.end < message.start;
|
|
402
|
-
const decoder = await
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
},
|
|
409
|
-
this.properties.entryIndex,
|
|
410
|
-
"decoder",
|
|
411
|
-
);
|
|
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
|
+
});
|
|
412
543
|
|
|
413
544
|
if (!decoder) {
|
|
414
545
|
await this.simple.rpc.send(
|
|
@@ -613,10 +744,12 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
613
744
|
}
|
|
614
745
|
|
|
615
746
|
onEntryAdded(entry: Entry<any>): void {
|
|
747
|
+
this.invalidateLocalRangeEncoderCache();
|
|
616
748
|
return this.simple.onEntryAdded(entry);
|
|
617
749
|
}
|
|
618
750
|
|
|
619
751
|
onEntryRemoved(hash: string) {
|
|
752
|
+
this.invalidateLocalRangeEncoderCache();
|
|
620
753
|
return this.simple.onEntryRemoved(hash);
|
|
621
754
|
}
|
|
622
755
|
|
|
@@ -635,6 +768,7 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
|
|
|
635
768
|
for (const [, obj] of this.outgoingSyncProcesses) {
|
|
636
769
|
obj.free();
|
|
637
770
|
}
|
|
771
|
+
this.clearLocalRangeEncoderCache();
|
|
638
772
|
return this.simple.close();
|
|
639
773
|
}
|
|
640
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 {
|
|
@@ -57,7 +57,7 @@ const getHashesFromSymbols = async (
|
|
|
57
57
|
coordinateToHash: Cache<string>,
|
|
58
58
|
) => {
|
|
59
59
|
let queries: IntegerCompare[] = [];
|
|
60
|
-
let batchSize =
|
|
60
|
+
let batchSize = 128; // TODO arg
|
|
61
61
|
let results = new Set<string>();
|
|
62
62
|
const handleBatch = async (end = false) => {
|
|
63
63
|
if (queries.length >= batchSize || (end && queries.length > 0)) {
|
|
@@ -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 }),
|