@peerbit/shared-log 13.1.16 → 13.1.17
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/native-graph.d.ts +2 -0
- package/dist/benchmark/native-graph.d.ts.map +1 -0
- package/dist/benchmark/native-graph.js +249 -0
- package/dist/benchmark/native-graph.js.map +1 -0
- package/dist/benchmark/sync-batch-sweep.js +72 -24
- package/dist/benchmark/sync-batch-sweep.js.map +1 -1
- package/dist/benchmark/sync-phase-profile.d.ts +2 -0
- package/dist/benchmark/sync-phase-profile.d.ts.map +1 -0
- package/dist/benchmark/sync-phase-profile.js +303 -0
- package/dist/benchmark/sync-phase-profile.js.map +1 -0
- package/dist/src/checked-prune.d.ts +55 -0
- package/dist/src/checked-prune.d.ts.map +1 -0
- package/dist/src/checked-prune.js +244 -0
- package/dist/src/checked-prune.js.map +1 -0
- package/dist/src/index.d.ts +10 -9
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +265 -164
- package/dist/src/index.js.map +1 -1
- package/dist/src/sync/index.d.ts +9 -1
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/profile.d.ts +3 -0
- package/dist/src/sync/profile.d.ts.map +1 -0
- package/dist/src/sync/profile.js +2 -0
- package/dist/src/sync/profile.js.map +1 -0
- package/dist/src/sync/rateless-iblt.d.ts +25 -11
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
- package/dist/src/sync/rateless-iblt.js +597 -106
- package/dist/src/sync/rateless-iblt.js.map +1 -1
- package/dist/src/sync/simple.d.ts +5 -0
- package/dist/src/sync/simple.d.ts.map +1 -1
- package/dist/src/sync/simple.js +224 -74
- package/dist/src/sync/simple.js.map +1 -1
- package/package.json +17 -13
- package/src/checked-prune.ts +331 -0
- package/src/index.ts +429 -282
- package/src/sync/index.ts +11 -1
- package/src/sync/profile.ts +9 -0
- package/src/sync/rateless-iblt.ts +768 -128
- package/src/sync/simple.ts +256 -94
|
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
32
|
}
|
|
33
33
|
return useValue ? value : void 0;
|
|
34
34
|
};
|
|
35
|
-
import { field, variant,
|
|
35
|
+
import { field, variant, } from "@dao-xyz/borsh";
|
|
36
36
|
import { Cache } from "@peerbit/cache";
|
|
37
37
|
import { randomBytes, toBase64 } from "@peerbit/crypto";
|
|
38
38
|
import { And, IntegerCompare, Or, } from "@peerbit/indexer-interface";
|
|
@@ -42,7 +42,8 @@ import { SilentDelivery } from "@peerbit/stream-interface";
|
|
|
42
42
|
import {} from "../exchange-heads.js";
|
|
43
43
|
import { TransportMessage } from "../message.js";
|
|
44
44
|
import {} from "../ranges.js";
|
|
45
|
-
import {
|
|
45
|
+
import { emitSyncProfileDuration, emitSyncProfileEvent, syncProfileStart, } from "./profile.js";
|
|
46
|
+
import { SYNC_MESSAGE_PRIORITY, SimpleSyncronizer, } from "./simple.js";
|
|
46
47
|
export const logger = loggerFn("peerbit:shared-log:rateless");
|
|
47
48
|
const coerceBigInt = (value) => typeof value === "bigint" ? value : BigInt(value);
|
|
48
49
|
let SymbolSerialized = (() => {
|
|
@@ -77,12 +78,285 @@ let SymbolSerialized = (() => {
|
|
|
77
78
|
}
|
|
78
79
|
};
|
|
79
80
|
})();
|
|
81
|
+
const CODED_SYMBOL_WORDS = 3;
|
|
82
|
+
const CODED_SYMBOL_WORD_BYTES = 8;
|
|
83
|
+
const CODED_SYMBOL_BYTES = CODED_SYMBOL_WORDS * CODED_SYMBOL_WORD_BYTES;
|
|
84
|
+
const BIG_UINT64_ARRAY_IS_LITTLE_ENDIAN = typeof BigUint64Array !== "undefined" &&
|
|
85
|
+
new Uint8Array(new BigUint64Array([1n]).buffer)[0] === 1;
|
|
86
|
+
const assertValidFlatCodedSymbols = (flat) => {
|
|
87
|
+
if (flat.length % CODED_SYMBOL_WORDS !== 0) {
|
|
88
|
+
throw new Error("Invalid RIBLT coded symbol batch");
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
export class CodedSymbolBatch {
|
|
92
|
+
flat;
|
|
93
|
+
symbols;
|
|
94
|
+
constructor(properties) {
|
|
95
|
+
this.flat = properties.flat;
|
|
96
|
+
this.symbols = properties.symbols;
|
|
97
|
+
}
|
|
98
|
+
static from(symbols) {
|
|
99
|
+
if (symbols instanceof CodedSymbolBatch) {
|
|
100
|
+
return symbols;
|
|
101
|
+
}
|
|
102
|
+
if (typeof BigUint64Array !== "undefined" &&
|
|
103
|
+
symbols instanceof BigUint64Array) {
|
|
104
|
+
return CodedSymbolBatch.fromFlat(symbols);
|
|
105
|
+
}
|
|
106
|
+
return CodedSymbolBatch.fromSymbols(symbols);
|
|
107
|
+
}
|
|
108
|
+
static fromFlat(flat) {
|
|
109
|
+
assertValidFlatCodedSymbols(flat);
|
|
110
|
+
return new CodedSymbolBatch({ flat });
|
|
111
|
+
}
|
|
112
|
+
static fromSymbols(symbols) {
|
|
113
|
+
return new CodedSymbolBatch({ symbols });
|
|
114
|
+
}
|
|
115
|
+
get length() {
|
|
116
|
+
return this.flat
|
|
117
|
+
? this.flat.length / CODED_SYMBOL_WORDS
|
|
118
|
+
: (this.symbols?.length ?? 0);
|
|
119
|
+
}
|
|
120
|
+
toFlat() {
|
|
121
|
+
if (this.flat) {
|
|
122
|
+
return this.flat;
|
|
123
|
+
}
|
|
124
|
+
const symbols = this.symbols ?? [];
|
|
125
|
+
const flat = new BigUint64Array(symbols.length * CODED_SYMBOL_WORDS);
|
|
126
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
127
|
+
const offset = i * CODED_SYMBOL_WORDS;
|
|
128
|
+
const symbol = symbols[i];
|
|
129
|
+
flat[offset] = coerceBigInt(symbol.count);
|
|
130
|
+
flat[offset + 1] = coerceBigInt(symbol.hash);
|
|
131
|
+
flat[offset + 2] = coerceBigInt(symbol.symbol);
|
|
132
|
+
}
|
|
133
|
+
this.flat = flat;
|
|
134
|
+
return flat;
|
|
135
|
+
}
|
|
136
|
+
toSymbols() {
|
|
137
|
+
const symbols = [];
|
|
138
|
+
for (const symbol of this) {
|
|
139
|
+
symbols.push(symbol instanceof SymbolSerialized
|
|
140
|
+
? symbol
|
|
141
|
+
: new SymbolSerialized({
|
|
142
|
+
count: symbol.count,
|
|
143
|
+
hash: symbol.hash,
|
|
144
|
+
symbol: symbol.symbol,
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
return symbols;
|
|
148
|
+
}
|
|
149
|
+
*[Symbol.iterator]() {
|
|
150
|
+
if (this.symbols) {
|
|
151
|
+
yield* this.symbols;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const flat = this.flat;
|
|
155
|
+
if (!flat) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
for (let i = 0; i < flat.length; i += CODED_SYMBOL_WORDS) {
|
|
159
|
+
yield {
|
|
160
|
+
count: flat[i],
|
|
161
|
+
hash: flat[i + 1],
|
|
162
|
+
symbol: flat[i + 2],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const codedSymbolBatchField = {
|
|
168
|
+
serialize: (symbols, writer) => {
|
|
169
|
+
const batch = CodedSymbolBatch.from(symbols);
|
|
170
|
+
const length = batch.length;
|
|
171
|
+
writer.u32(length);
|
|
172
|
+
if (length === 0) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (typeof BigUint64Array !== "undefined") {
|
|
176
|
+
const flat = batch.toFlat();
|
|
177
|
+
if (BIG_UINT64_ARRAY_IS_LITTLE_ENDIAN) {
|
|
178
|
+
writer.set(new Uint8Array(flat.buffer, flat.byteOffset, flat.byteLength));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
for (let i = 0; i < flat.length; i++) {
|
|
182
|
+
writer.u64(flat[i]);
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (const symbol of batch) {
|
|
187
|
+
writer.u64(symbol.count);
|
|
188
|
+
writer.u64(symbol.hash);
|
|
189
|
+
writer.u64(symbol.symbol);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
deserialize: (reader) => {
|
|
193
|
+
const length = reader.u32();
|
|
194
|
+
const wordLength = length * CODED_SYMBOL_WORDS;
|
|
195
|
+
const byteLength = length * CODED_SYMBOL_BYTES;
|
|
196
|
+
if (typeof BigUint64Array !== "undefined" &&
|
|
197
|
+
BIG_UINT64_ARRAY_IS_LITTLE_ENDIAN) {
|
|
198
|
+
if (reader._offset + byteLength > reader._buf.length) {
|
|
199
|
+
throw new Error("Invalid RIBLT coded symbol batch length");
|
|
200
|
+
}
|
|
201
|
+
const bytes = reader.buffer(byteLength);
|
|
202
|
+
const flat = new BigUint64Array(wordLength);
|
|
203
|
+
new Uint8Array(flat.buffer).set(bytes);
|
|
204
|
+
return CodedSymbolBatch.fromFlat(flat);
|
|
205
|
+
}
|
|
206
|
+
const symbols = [];
|
|
207
|
+
for (let i = 0; i < length; i++) {
|
|
208
|
+
symbols.push(new SymbolSerialized({
|
|
209
|
+
count: reader.u64(),
|
|
210
|
+
hash: reader.u64(),
|
|
211
|
+
symbol: reader.u64(),
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
return CodedSymbolBatch.fromSymbols(symbols);
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
const addSymbolsToRiblt = (target, symbols) => {
|
|
218
|
+
if (typeof BigUint64Array !== "undefined" &&
|
|
219
|
+
typeof target.add_symbols === "function") {
|
|
220
|
+
target.add_symbols(symbols instanceof BigUint64Array
|
|
221
|
+
? symbols
|
|
222
|
+
: BigUint64Array.from(symbols, coerceBigInt));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
for (const symbol of symbols) {
|
|
226
|
+
target.add_symbol(coerceBigInt(symbol));
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const produceNextCodedSymbols = (encoder, count) => {
|
|
230
|
+
const produceBatch = encoder
|
|
231
|
+
.produce_next_coded_symbols;
|
|
232
|
+
if (typeof BigUint64Array !== "undefined" && produceBatch) {
|
|
233
|
+
return CodedSymbolBatch.fromFlat(produceBatch.call(encoder, count));
|
|
234
|
+
}
|
|
235
|
+
const symbols = [];
|
|
236
|
+
for (let i = 0; i < count; i++) {
|
|
237
|
+
symbols.push(new SymbolSerialized(encoder.produce_next_coded_symbol()));
|
|
238
|
+
}
|
|
239
|
+
return CodedSymbolBatch.fromSymbols(symbols);
|
|
240
|
+
};
|
|
241
|
+
const flatFromCodedSymbols = (symbols) => {
|
|
242
|
+
if (typeof BigUint64Array !== "undefined" &&
|
|
243
|
+
symbols instanceof BigUint64Array) {
|
|
244
|
+
assertValidFlatCodedSymbols(symbols);
|
|
245
|
+
return symbols;
|
|
246
|
+
}
|
|
247
|
+
return CodedSymbolBatch.from(symbols).toFlat();
|
|
248
|
+
};
|
|
249
|
+
const getRemoteSymbolValues = (decoder) => {
|
|
250
|
+
const getBatch = decoder.get_remote_symbol_values;
|
|
251
|
+
if (typeof BigUint64Array !== "undefined" && getBatch) {
|
|
252
|
+
return Array.from(getBatch.call(decoder));
|
|
253
|
+
}
|
|
254
|
+
const symbols = [];
|
|
255
|
+
for (const missingSymbol of decoder.get_remote_symbols()) {
|
|
256
|
+
symbols.push(coerceBigInt(missingSymbol));
|
|
257
|
+
}
|
|
258
|
+
return symbols;
|
|
259
|
+
};
|
|
260
|
+
const prepareStartSyncEncoder = (coordinates, maxValue, initialSymbolCount) => {
|
|
261
|
+
const encoder = new EncoderWrapper();
|
|
262
|
+
let complete = false;
|
|
263
|
+
try {
|
|
264
|
+
const prepareAndProduceNative = encoder
|
|
265
|
+
.add_symbols_sorted_find_range_and_produce;
|
|
266
|
+
if (typeof BigUint64Array !== "undefined" && prepareAndProduceNative) {
|
|
267
|
+
const prepared = prepareAndProduceNative.call(encoder, BigUint64Array.from(coordinates), coerceBigInt(maxValue), initialSymbolCount);
|
|
268
|
+
if (prepared.length < 2 ||
|
|
269
|
+
(prepared.length - 2) % CODED_SYMBOL_WORDS !== 0) {
|
|
270
|
+
throw new Error("Invalid RIBLT prepared encoder result");
|
|
271
|
+
}
|
|
272
|
+
complete = true;
|
|
273
|
+
return {
|
|
274
|
+
encoder,
|
|
275
|
+
start: prepared[0],
|
|
276
|
+
end: prepared[1],
|
|
277
|
+
initialSymbols: CodedSymbolBatch.fromFlat(prepared.subarray(2)),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const prepareNative = encoder
|
|
281
|
+
.add_symbols_sorted_and_find_range;
|
|
282
|
+
if (typeof BigUint64Array !== "undefined" && prepareNative) {
|
|
283
|
+
const range = prepareNative.call(encoder, BigUint64Array.from(coordinates), coerceBigInt(maxValue));
|
|
284
|
+
if (range.length !== 2) {
|
|
285
|
+
throw new Error("Invalid RIBLT range result");
|
|
286
|
+
}
|
|
287
|
+
complete = true;
|
|
288
|
+
return { encoder, start: range[0], end: range[1] };
|
|
289
|
+
}
|
|
290
|
+
let sortedEntries;
|
|
291
|
+
if (typeof BigUint64Array !== "undefined") {
|
|
292
|
+
const typed = new BigUint64Array(coordinates.length);
|
|
293
|
+
for (let i = 0; i < coordinates.length; i++) {
|
|
294
|
+
typed[i] = coordinates[i];
|
|
295
|
+
}
|
|
296
|
+
typed.sort();
|
|
297
|
+
sortedEntries = typed;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
sortedEntries = [...coordinates].sort((a, b) => {
|
|
301
|
+
if (a > b) {
|
|
302
|
+
return 1;
|
|
303
|
+
}
|
|
304
|
+
else if (a < b) {
|
|
305
|
+
return -1;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
return 0;
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
// assume sorted, and find the largest gap
|
|
313
|
+
let largestGap = 0n;
|
|
314
|
+
let largestGapIndex = 0;
|
|
315
|
+
for (let i = 0; i < sortedEntries.length; i++) {
|
|
316
|
+
const current = sortedEntries[i];
|
|
317
|
+
const next = sortedEntries[(i + 1) % sortedEntries.length];
|
|
318
|
+
const gap = next >= current
|
|
319
|
+
? next - current
|
|
320
|
+
: coerceBigInt(maxValue) - current + next;
|
|
321
|
+
if (gap > largestGap) {
|
|
322
|
+
largestGap = gap;
|
|
323
|
+
largestGapIndex = i;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const smallestRangeStartIndex = (largestGapIndex + 1) % sortedEntries.length;
|
|
327
|
+
const smallestRangeEndIndex = largestGapIndex; /// === (smallRangeStartIndex + 1) % sortedEntries.length
|
|
328
|
+
let smallestRangeStart = sortedEntries[smallestRangeStartIndex];
|
|
329
|
+
let smallestRangeEnd = sortedEntries[smallestRangeEndIndex];
|
|
330
|
+
let start, end;
|
|
331
|
+
if (smallestRangeEnd === smallestRangeStart) {
|
|
332
|
+
start = smallestRangeEnd;
|
|
333
|
+
end = smallestRangeEnd + 1n;
|
|
334
|
+
if (end > maxValue) {
|
|
335
|
+
end = 0n;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
start = smallestRangeStart;
|
|
340
|
+
end = smallestRangeEnd;
|
|
341
|
+
}
|
|
342
|
+
addSymbolsToRiblt(encoder, sortedEntries);
|
|
343
|
+
complete = true;
|
|
344
|
+
return { encoder, start, end };
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
if (!complete) {
|
|
348
|
+
encoder.free();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
80
352
|
const getSyncIdString = (message) => {
|
|
81
353
|
return toBase64(message.syncId);
|
|
82
354
|
};
|
|
83
355
|
const DEFAULT_CONVERGENT_REPAIR_TIMEOUT_MS = 30_000;
|
|
84
356
|
const DEFAULT_CONVERGENT_RETRY_INTERVALS_MS = [0, 1_000, 3_000, 7_000];
|
|
85
357
|
const DEFAULT_MAX_CONVERGENT_TRACKED_HASHES = 4_096;
|
|
358
|
+
const MIN_MORE_SYMBOLS_BATCH_SIZE = 64;
|
|
359
|
+
const MAX_MORE_SYMBOLS_BATCH_SIZE = 1_024;
|
|
86
360
|
let StartSync = (() => {
|
|
87
361
|
let _classDecorators = [variant([3, 0])];
|
|
88
362
|
let _classDescriptor;
|
|
@@ -108,7 +382,7 @@ let StartSync = (() => {
|
|
|
108
382
|
_syncId_decorators = [field({ type: Uint8Array })];
|
|
109
383
|
_start_decorators = [field({ type: "u64" })];
|
|
110
384
|
_end_decorators = [field({ type: "u64" })];
|
|
111
|
-
_symbols_decorators = [field({ type:
|
|
385
|
+
_symbols_decorators = [field({ type: codedSymbolBatchField })];
|
|
112
386
|
__esDecorate(null, null, _syncId_decorators, { kind: "field", name: "syncId", static: false, private: false, access: { has: obj => "syncId" in obj, get: obj => obj.syncId, set: (obj, value) => { obj.syncId = value; } }, metadata: _metadata }, _syncId_initializers, _syncId_extraInitializers);
|
|
113
387
|
__esDecorate(null, null, _start_decorators, { kind: "field", name: "start", static: false, private: false, access: { has: obj => "start" in obj, get: obj => obj.start, set: (obj, value) => { obj.start = value; } }, metadata: _metadata }, _start_initializers, _start_extraInitializers);
|
|
114
388
|
__esDecorate(null, null, _end_decorators, { kind: "field", name: "end", static: false, private: false, access: { has: obj => "end" in obj, get: obj => obj.end, set: (obj, value) => { obj.end = value; } }, metadata: _metadata }, _end_initializers, _end_extraInitializers);
|
|
@@ -128,7 +402,7 @@ let StartSync = (() => {
|
|
|
128
402
|
this.syncId = randomBytes(32);
|
|
129
403
|
this.start = coerceBigInt(props.from);
|
|
130
404
|
this.end = coerceBigInt(props.to);
|
|
131
|
-
this.symbols = props.symbols;
|
|
405
|
+
this.symbols = CodedSymbolBatch.from(props.symbols);
|
|
132
406
|
}
|
|
133
407
|
};
|
|
134
408
|
return StartSync = _classThis;
|
|
@@ -155,7 +429,7 @@ let MoreSymbols = (() => {
|
|
|
155
429
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
156
430
|
_syncId_decorators = [field({ type: Uint8Array })];
|
|
157
431
|
_seqNo_decorators = [field({ type: "u64" })];
|
|
158
|
-
_symbols_decorators = [field({ type:
|
|
432
|
+
_symbols_decorators = [field({ type: codedSymbolBatchField })];
|
|
159
433
|
__esDecorate(null, null, _syncId_decorators, { kind: "field", name: "syncId", static: false, private: false, access: { has: obj => "syncId" in obj, get: obj => obj.syncId, set: (obj, value) => { obj.syncId = value; } }, metadata: _metadata }, _syncId_initializers, _syncId_extraInitializers);
|
|
160
434
|
__esDecorate(null, null, _seqNo_decorators, { kind: "field", name: "seqNo", static: false, private: false, access: { has: obj => "seqNo" in obj, get: obj => obj.seqNo, set: (obj, value) => { obj.seqNo = value; } }, metadata: _metadata }, _seqNo_initializers, _seqNo_extraInitializers);
|
|
161
435
|
__esDecorate(null, null, _symbols_decorators, { kind: "field", name: "symbols", static: false, private: false, access: { has: obj => "symbols" in obj, get: obj => obj.symbols, set: (obj, value) => { obj.symbols = value; } }, metadata: _metadata }, _symbols_initializers, _symbols_extraInitializers);
|
|
@@ -172,7 +446,7 @@ let MoreSymbols = (() => {
|
|
|
172
446
|
__runInitializers(this, _symbols_extraInitializers);
|
|
173
447
|
this.syncId = props.syncId;
|
|
174
448
|
this.seqNo = props.lastSeqNo + 1n;
|
|
175
|
-
this.symbols = props.symbols;
|
|
449
|
+
this.symbols = CodedSymbolBatch.from(props.symbols);
|
|
176
450
|
}
|
|
177
451
|
};
|
|
178
452
|
return MoreSymbols = _classThis;
|
|
@@ -279,9 +553,10 @@ const matchEntriesByHashNumberInRangeQuery = (range) => {
|
|
|
279
553
|
]),
|
|
280
554
|
]);
|
|
281
555
|
};
|
|
282
|
-
const buildEncoderOrDecoderFromRange = async (ranges, entryIndex, type) => {
|
|
556
|
+
const buildEncoderOrDecoderFromRange = async (ranges, entryIndex, type, profile) => {
|
|
283
557
|
await ribltReady;
|
|
284
558
|
const encoder = type === "encoder" ? new EncoderWrapper() : new DecoderWrapper();
|
|
559
|
+
const rangeQueryStartedAt = syncProfileStart(profile);
|
|
285
560
|
const entries = await entryIndex
|
|
286
561
|
.iterate({
|
|
287
562
|
// Range sync for IBLT is done in hashNumber space.
|
|
@@ -298,11 +573,37 @@ const buildEncoderOrDecoderFromRange = async (ranges, entryIndex, type) => {
|
|
|
298
573
|
},
|
|
299
574
|
})
|
|
300
575
|
.all();
|
|
576
|
+
if (profile) {
|
|
577
|
+
emitSyncProfileDuration(profile, rangeQueryStartedAt, {
|
|
578
|
+
name: "rateless.rangeQuery",
|
|
579
|
+
entries: entries.length,
|
|
580
|
+
details: { type },
|
|
581
|
+
});
|
|
582
|
+
}
|
|
301
583
|
if (entries.length === 0) {
|
|
302
584
|
return false;
|
|
303
585
|
}
|
|
304
|
-
|
|
305
|
-
|
|
586
|
+
const addSymbolsStartedAt = syncProfileStart(profile);
|
|
587
|
+
if (typeof BigUint64Array !== "undefined" &&
|
|
588
|
+
typeof encoder.add_symbols === "function") {
|
|
589
|
+
const symbols = new BigUint64Array(entries.length);
|
|
590
|
+
for (let i = 0; i < entries.length; i++) {
|
|
591
|
+
symbols[i] = coerceBigInt(entries[i].value.hashNumber);
|
|
592
|
+
}
|
|
593
|
+
addSymbolsToRiblt(encoder, symbols);
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
for (const entry of entries) {
|
|
597
|
+
encoder.add_symbol(coerceBigInt(entry.value.hashNumber));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (profile) {
|
|
601
|
+
emitSyncProfileDuration(profile, addSymbolsStartedAt, {
|
|
602
|
+
name: "rateless.rangeAddSymbols",
|
|
603
|
+
entries: entries.length,
|
|
604
|
+
symbols: entries.length,
|
|
605
|
+
details: { type },
|
|
606
|
+
});
|
|
306
607
|
}
|
|
307
608
|
return encoder;
|
|
308
609
|
};
|
|
@@ -489,14 +790,34 @@ export class RatelessIBLTSynchronizer {
|
|
|
489
790
|
return decoder;
|
|
490
791
|
}
|
|
491
792
|
async getLocalDecoderForRange(ranges) {
|
|
793
|
+
const profile = this.properties.sync?.profile;
|
|
492
794
|
const key = this.localRangeEncoderCacheKey(ranges);
|
|
493
795
|
const cached = this.localRangeEncoderCache.get(key);
|
|
494
796
|
if (cached && cached.version === this.localRangeEncoderCacheVersion) {
|
|
797
|
+
const startedAt = syncProfileStart(profile);
|
|
495
798
|
cached.lastUsed = Date.now();
|
|
496
|
-
|
|
799
|
+
try {
|
|
800
|
+
return this.decoderFromCachedEncoder(cached.encoder);
|
|
801
|
+
}
|
|
802
|
+
finally {
|
|
803
|
+
if (profile) {
|
|
804
|
+
emitSyncProfileDuration(profile, startedAt, {
|
|
805
|
+
name: "rateless.localDecoder",
|
|
806
|
+
cacheHit: true,
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
}
|
|
497
810
|
}
|
|
498
|
-
const
|
|
811
|
+
const startedAt = syncProfileStart(profile);
|
|
812
|
+
const encoder = (await buildEncoderOrDecoderFromRange(ranges, this.properties.entryIndex, "encoder", profile));
|
|
499
813
|
if (!encoder) {
|
|
814
|
+
if (profile) {
|
|
815
|
+
emitSyncProfileDuration(profile, startedAt, {
|
|
816
|
+
name: "rateless.localDecoder",
|
|
817
|
+
cacheHit: false,
|
|
818
|
+
entries: 0,
|
|
819
|
+
});
|
|
820
|
+
}
|
|
500
821
|
return false;
|
|
501
822
|
}
|
|
502
823
|
const now = Date.now();
|
|
@@ -527,9 +848,21 @@ export class RatelessIBLTSynchronizer {
|
|
|
527
848
|
}
|
|
528
849
|
this.localRangeEncoderCache.delete(oldestKey);
|
|
529
850
|
}
|
|
530
|
-
|
|
851
|
+
try {
|
|
852
|
+
return this.decoderFromCachedEncoder(encoder);
|
|
853
|
+
}
|
|
854
|
+
finally {
|
|
855
|
+
if (profile) {
|
|
856
|
+
emitSyncProfileDuration(profile, startedAt, {
|
|
857
|
+
name: "rateless.localDecoder",
|
|
858
|
+
cacheHit: false,
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
}
|
|
531
862
|
}
|
|
532
863
|
async onMaybeMissingEntries(properties) {
|
|
864
|
+
const profile = this.properties.sync?.profile;
|
|
865
|
+
const startedAt = syncProfileStart(profile);
|
|
533
866
|
// NOTE: this method is best-effort dispatch, not a per-hash convergence API.
|
|
534
867
|
// It may require follow-up repair rounds under churn/loss to fully close all gaps.
|
|
535
868
|
// Strategy:
|
|
@@ -542,12 +875,33 @@ export class RatelessIBLTSynchronizer {
|
|
|
542
875
|
let maxSyncWithSimpleMethod = 1e3;
|
|
543
876
|
// Small batch => use simple synchronizer entirely
|
|
544
877
|
if (properties.entries.size <= minSyncIbltSize) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
878
|
+
if (profile) {
|
|
879
|
+
emitSyncProfileEvent(profile, {
|
|
880
|
+
name: "rateless.dispatchMode",
|
|
881
|
+
entries: properties.entries.size,
|
|
882
|
+
targets: properties.targets.length,
|
|
883
|
+
details: { mode: "simple-small" },
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
try {
|
|
887
|
+
await this.simple.onMaybeMissingEntries({
|
|
888
|
+
entries: properties.entries,
|
|
889
|
+
targets: properties.targets,
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
finally {
|
|
893
|
+
if (profile) {
|
|
894
|
+
emitSyncProfileDuration(profile, startedAt, {
|
|
895
|
+
name: "rateless.onMaybeMissingEntries",
|
|
896
|
+
entries: properties.entries.size,
|
|
897
|
+
targets: properties.targets.length,
|
|
898
|
+
details: { mode: "simple-small" },
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
}
|
|
549
902
|
return;
|
|
550
903
|
}
|
|
904
|
+
const selectStartedAt = syncProfileStart(profile);
|
|
551
905
|
const nonBoundaryEntries = [];
|
|
552
906
|
for (const entry of properties.entries.values()) {
|
|
553
907
|
if (entry.assignedToRangeBoundary) {
|
|
@@ -604,91 +958,84 @@ export class RatelessIBLTSynchronizer {
|
|
|
604
958
|
allCoordinatesToSyncWithIblt.push(coerceBigInt(entry.hashNumber));
|
|
605
959
|
}
|
|
606
960
|
}
|
|
607
|
-
if (
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
typed.sort();
|
|
618
|
-
sortedEntries = typed;
|
|
619
|
-
}
|
|
620
|
-
else {
|
|
621
|
-
sortedEntries = allCoordinatesToSyncWithIblt.sort((a, b) => {
|
|
622
|
-
if (a > b) {
|
|
623
|
-
return 1;
|
|
624
|
-
}
|
|
625
|
-
else if (a < b) {
|
|
626
|
-
return -1;
|
|
627
|
-
}
|
|
628
|
-
else {
|
|
629
|
-
return 0;
|
|
630
|
-
}
|
|
961
|
+
if (profile) {
|
|
962
|
+
emitSyncProfileDuration(profile, selectStartedAt, {
|
|
963
|
+
name: "rateless.selectEntries",
|
|
964
|
+
entries: properties.entries.size,
|
|
965
|
+
symbols: allCoordinatesToSyncWithIblt.length,
|
|
966
|
+
targets: properties.targets.length,
|
|
967
|
+
details: {
|
|
968
|
+
naiveEntries: entriesToSyncNaively.size,
|
|
969
|
+
priority: priorityFn != null,
|
|
970
|
+
},
|
|
631
971
|
});
|
|
632
972
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const smallestRangeStartIndex = (largestGapIndex + 1) % sortedEntries.length;
|
|
648
|
-
const smallestRangeEndIndex = largestGapIndex; /// === (smallRangeStartIndex + 1) % sortedEntries.length
|
|
649
|
-
let smallestRangeStart = sortedEntries[smallestRangeStartIndex];
|
|
650
|
-
let smallestRangeEnd = sortedEntries[smallestRangeEndIndex];
|
|
651
|
-
let start, end;
|
|
652
|
-
if (smallestRangeEnd === smallestRangeStart) {
|
|
653
|
-
start = smallestRangeEnd;
|
|
654
|
-
end = smallestRangeEnd + 1n;
|
|
655
|
-
if (end > this.properties.numbers.maxValue) {
|
|
656
|
-
end = 0n;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
else {
|
|
660
|
-
start = smallestRangeStart;
|
|
661
|
-
end = smallestRangeEnd;
|
|
662
|
-
}
|
|
663
|
-
const startSync = new StartSync({ from: start, to: end, symbols: [] });
|
|
664
|
-
const encoder = new EncoderWrapper();
|
|
665
|
-
if (typeof BigUint64Array !== "undefined" &&
|
|
666
|
-
sortedEntries instanceof BigUint64Array) {
|
|
667
|
-
encoder.add_symbols(sortedEntries);
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
for (const entry of sortedEntries) {
|
|
671
|
-
encoder.add_symbol(coerceBigInt(entry));
|
|
973
|
+
if (allCoordinatesToSyncWithIblt.length === 0) {
|
|
974
|
+
if (profile) {
|
|
975
|
+
emitSyncProfileEvent(profile, {
|
|
976
|
+
name: "rateless.dispatchMode",
|
|
977
|
+
entries: properties.entries.size,
|
|
978
|
+
targets: properties.targets.length,
|
|
979
|
+
details: { mode: "simple-only" },
|
|
980
|
+
});
|
|
981
|
+
emitSyncProfileDuration(profile, startedAt, {
|
|
982
|
+
name: "rateless.onMaybeMissingEntries",
|
|
983
|
+
entries: properties.entries.size,
|
|
984
|
+
targets: properties.targets.length,
|
|
985
|
+
details: { mode: "simple-only" },
|
|
986
|
+
});
|
|
672
987
|
}
|
|
988
|
+
return;
|
|
673
989
|
}
|
|
990
|
+
await ribltReady;
|
|
674
991
|
// For smaller sets, the original `sqrt(n)` heuristic can occasionally under-provision
|
|
675
992
|
// low-degree symbols early, causing an unnecessary `MoreSymbols` round-trip. Use a
|
|
676
993
|
// small floor to make small-delta syncs more reliable without affecting large-n behavior.
|
|
677
|
-
let
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
994
|
+
let initialSymbolCount = Math.round(Math.sqrt(allCoordinatesToSyncWithIblt.length)); // TODO choose better
|
|
995
|
+
initialSymbolCount = Math.max(64, initialSymbolCount);
|
|
996
|
+
const prepareStartedAt = syncProfileStart(profile);
|
|
997
|
+
const { encoder, start, end, initialSymbols } = prepareStartSyncEncoder(allCoordinatesToSyncWithIblt, this.properties.numbers.maxValue, initialSymbolCount);
|
|
998
|
+
if (profile) {
|
|
999
|
+
emitSyncProfileDuration(profile, prepareStartedAt, {
|
|
1000
|
+
name: "rateless.prepareStartSyncEncoder",
|
|
1001
|
+
entries: allCoordinatesToSyncWithIblt.length,
|
|
1002
|
+
symbols: initialSymbols?.length,
|
|
1003
|
+
details: {
|
|
1004
|
+
initialSymbolCount,
|
|
1005
|
+
includesInitialSymbols: initialSymbols != null,
|
|
1006
|
+
},
|
|
1007
|
+
});
|
|
681
1008
|
}
|
|
1009
|
+
let startSyncSymbols = initialSymbols;
|
|
1010
|
+
if (!startSyncSymbols) {
|
|
1011
|
+
const produceStartedAt = syncProfileStart(profile);
|
|
1012
|
+
startSyncSymbols = produceNextCodedSymbols(encoder, initialSymbolCount);
|
|
1013
|
+
if (profile) {
|
|
1014
|
+
emitSyncProfileDuration(profile, produceStartedAt, {
|
|
1015
|
+
name: "rateless.produceStartSyncSymbols",
|
|
1016
|
+
symbols: startSyncSymbols.length,
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
const startSync = new StartSync({
|
|
1021
|
+
from: start,
|
|
1022
|
+
to: end,
|
|
1023
|
+
symbols: startSyncSymbols,
|
|
1024
|
+
});
|
|
1025
|
+
const syncId = getSyncIdString(startSync);
|
|
682
1026
|
const clear = () => {
|
|
683
1027
|
encoder.free();
|
|
684
|
-
clearTimeout(this.outgoingSyncProcesses.get(
|
|
685
|
-
this.outgoingSyncProcesses.delete(
|
|
1028
|
+
clearTimeout(this.outgoingSyncProcesses.get(syncId)?.timeout);
|
|
1029
|
+
this.outgoingSyncProcesses.delete(syncId);
|
|
686
1030
|
};
|
|
687
1031
|
const createTimeout = () => {
|
|
688
1032
|
return setTimeout(clear, 1e4); // TODO arg
|
|
689
1033
|
};
|
|
690
1034
|
let lastSeqNo = -1n;
|
|
691
|
-
|
|
1035
|
+
// Keep follow-up symbol payloads bounded. Each symbol is serialized as an
|
|
1036
|
+
// object with three bigint fields, so very large batches can dominate heap under
|
|
1037
|
+
// concurrent churn even though the native RIBLT encoder itself is compact.
|
|
1038
|
+
const nextBatch = Math.max(MIN_MORE_SYMBOLS_BATCH_SIZE, Math.min(MAX_MORE_SYMBOLS_BATCH_SIZE, Math.ceil(allCoordinatesToSyncWithIblt.length / 4)));
|
|
692
1039
|
const obj = {
|
|
693
1040
|
encoder,
|
|
694
1041
|
timeout: createTimeout(),
|
|
@@ -701,26 +1048,73 @@ export class RatelessIBLTSynchronizer {
|
|
|
701
1048
|
},
|
|
702
1049
|
next: (properties) => {
|
|
703
1050
|
if (properties.lastSeqNo <= lastSeqNo) {
|
|
704
|
-
return [];
|
|
1051
|
+
return CodedSymbolBatch.fromSymbols([]);
|
|
705
1052
|
}
|
|
706
1053
|
lastSeqNo++;
|
|
707
1054
|
obj.refresh(); // TODO use timestamp instead and collective pruning/refresh
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
1055
|
+
const produceStartedAt = syncProfileStart(profile);
|
|
1056
|
+
const symbols = produceNextCodedSymbols(encoder, nextBatch);
|
|
1057
|
+
if (profile) {
|
|
1058
|
+
emitSyncProfileDuration(profile, produceStartedAt, {
|
|
1059
|
+
name: "rateless.produceMoreSymbols",
|
|
1060
|
+
syncId,
|
|
1061
|
+
symbols: symbols.length,
|
|
1062
|
+
});
|
|
711
1063
|
}
|
|
712
|
-
return
|
|
1064
|
+
return symbols;
|
|
713
1065
|
},
|
|
714
1066
|
free: clear,
|
|
715
1067
|
outgoing: properties.entries,
|
|
716
1068
|
};
|
|
717
|
-
this.outgoingSyncProcesses.set(
|
|
718
|
-
|
|
1069
|
+
this.outgoingSyncProcesses.set(syncId, obj);
|
|
1070
|
+
if (profile) {
|
|
1071
|
+
emitSyncProfileEvent(profile, {
|
|
1072
|
+
name: "rateless.dispatchMode",
|
|
1073
|
+
entries: properties.entries.size,
|
|
1074
|
+
symbols: startSyncSymbols.length,
|
|
1075
|
+
targets: properties.targets.length,
|
|
1076
|
+
syncId,
|
|
1077
|
+
details: { mode: "rateless" },
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
const sendStartedAt = syncProfileStart(profile);
|
|
1081
|
+
const sendResult = this.simple.rpc.send(startSync, {
|
|
719
1082
|
mode: new SilentDelivery({ to: properties.targets, redundancy: 1 }),
|
|
720
|
-
priority:
|
|
1083
|
+
priority: SYNC_MESSAGE_PRIORITY,
|
|
721
1084
|
});
|
|
1085
|
+
if (profile) {
|
|
1086
|
+
void Promise.resolve(sendResult).then(() => emitSyncProfileDuration(profile, sendStartedAt, {
|
|
1087
|
+
name: "rateless.sendStartSync",
|
|
1088
|
+
messages: 1,
|
|
1089
|
+
symbols: startSyncSymbols.length,
|
|
1090
|
+
targets: properties.targets.length,
|
|
1091
|
+
syncId,
|
|
1092
|
+
}), () => emitSyncProfileDuration(profile, sendStartedAt, {
|
|
1093
|
+
name: "rateless.sendStartSync",
|
|
1094
|
+
messages: 1,
|
|
1095
|
+
symbols: startSyncSymbols.length,
|
|
1096
|
+
targets: properties.targets.length,
|
|
1097
|
+
syncId,
|
|
1098
|
+
details: { rejected: true },
|
|
1099
|
+
}));
|
|
1100
|
+
}
|
|
1101
|
+
if (profile) {
|
|
1102
|
+
emitSyncProfileDuration(profile, startedAt, {
|
|
1103
|
+
name: "rateless.onMaybeMissingEntries",
|
|
1104
|
+
entries: properties.entries.size,
|
|
1105
|
+
messages: 1,
|
|
1106
|
+
symbols: startSyncSymbols.length,
|
|
1107
|
+
targets: properties.targets.length,
|
|
1108
|
+
details: {
|
|
1109
|
+
mode: "rateless",
|
|
1110
|
+
ibltEntries: allCoordinatesToSyncWithIblt.length,
|
|
1111
|
+
naiveEntries: entriesToSyncNaively.size,
|
|
1112
|
+
},
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
722
1115
|
}
|
|
723
1116
|
async onMessage(message, context) {
|
|
1117
|
+
const profile = this.properties.sync?.profile;
|
|
724
1118
|
if (message instanceof StartSync) {
|
|
725
1119
|
const syncId = getSyncIdString(message);
|
|
726
1120
|
if (this.ingoingSyncProcesses.has(syncId)) {
|
|
@@ -731,19 +1125,36 @@ export class RatelessIBLTSynchronizer {
|
|
|
731
1125
|
}
|
|
732
1126
|
this.startedOrCompletedSynchronizations.add(syncId);
|
|
733
1127
|
const wrapped = message.end < message.start;
|
|
1128
|
+
const decoderStartedAt = syncProfileStart(profile);
|
|
734
1129
|
const decoder = await this.getLocalDecoderForRange({
|
|
735
1130
|
start1: message.start,
|
|
736
1131
|
end1: wrapped ? this.properties.numbers.maxValue : message.end,
|
|
737
1132
|
start2: 0n,
|
|
738
1133
|
end2: wrapped ? message.end : 0n,
|
|
739
1134
|
});
|
|
1135
|
+
if (profile) {
|
|
1136
|
+
emitSyncProfileDuration(profile, decoderStartedAt, {
|
|
1137
|
+
name: "rateless.getLocalDecoderForRange",
|
|
1138
|
+
syncId,
|
|
1139
|
+
details: { wrapped, found: decoder !== false },
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
740
1142
|
if (!decoder) {
|
|
1143
|
+
const sendStartedAt = syncProfileStart(profile);
|
|
741
1144
|
await this.simple.rpc.send(new RequestAll({
|
|
742
1145
|
syncId: message.syncId,
|
|
743
1146
|
}), {
|
|
744
1147
|
mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
|
|
745
|
-
priority:
|
|
1148
|
+
priority: SYNC_MESSAGE_PRIORITY,
|
|
746
1149
|
});
|
|
1150
|
+
if (profile) {
|
|
1151
|
+
emitSyncProfileDuration(profile, sendStartedAt, {
|
|
1152
|
+
name: "rateless.sendRequestAll",
|
|
1153
|
+
messages: 1,
|
|
1154
|
+
targets: 1,
|
|
1155
|
+
syncId,
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
747
1158
|
return true;
|
|
748
1159
|
}
|
|
749
1160
|
const createTimeout = () => {
|
|
@@ -778,9 +1189,15 @@ export class RatelessIBLTSynchronizer {
|
|
|
778
1189
|
if (!decoder.decoded()) {
|
|
779
1190
|
return false;
|
|
780
1191
|
}
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
1192
|
+
const remoteStartedAt = syncProfileStart(profile);
|
|
1193
|
+
const allMissingSymbolsInRemote = getRemoteSymbolValues(decoder);
|
|
1194
|
+
if (profile) {
|
|
1195
|
+
emitSyncProfileDuration(profile, remoteStartedAt, {
|
|
1196
|
+
name: "rateless.remoteSymbols",
|
|
1197
|
+
entries: allMissingSymbolsInRemote.length,
|
|
1198
|
+
symbols: allMissingSymbolsInRemote.length,
|
|
1199
|
+
syncId,
|
|
1200
|
+
});
|
|
784
1201
|
}
|
|
785
1202
|
// The IBLT decoder is based on a local snapshot. Entries can arrive via
|
|
786
1203
|
// overlapping repair before we issue the follow-up simple request, so
|
|
@@ -796,7 +1213,37 @@ export class RatelessIBLTSynchronizer {
|
|
|
796
1213
|
break;
|
|
797
1214
|
}
|
|
798
1215
|
lastSeqNo = symbolMessage.seqNo;
|
|
799
|
-
|
|
1216
|
+
const addBatchAndDecode = decoder
|
|
1217
|
+
.add_coded_symbols_and_try_decode;
|
|
1218
|
+
if (typeof BigUint64Array !== "undefined" && addBatchAndDecode) {
|
|
1219
|
+
const flatStartedAt = syncProfileStart(profile);
|
|
1220
|
+
const flatSymbols = flatFromCodedSymbols(symbolMessage.symbols);
|
|
1221
|
+
if (profile) {
|
|
1222
|
+
emitSyncProfileDuration(profile, flatStartedAt, {
|
|
1223
|
+
name: "rateless.symbolBatchToFlat",
|
|
1224
|
+
symbols: flatSymbols.length / CODED_SYMBOL_WORDS,
|
|
1225
|
+
syncId,
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
const decodeStartedAt = syncProfileStart(profile);
|
|
1229
|
+
const decoded = addBatchAndDecode.call(decoder, flatSymbols);
|
|
1230
|
+
if (profile) {
|
|
1231
|
+
emitSyncProfileDuration(profile, decodeStartedAt, {
|
|
1232
|
+
name: "rateless.decodeBatch",
|
|
1233
|
+
symbols: flatSymbols.length / CODED_SYMBOL_WORDS,
|
|
1234
|
+
syncId,
|
|
1235
|
+
details: { decoded },
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
if (decoded && finalizeIfDecoded()) {
|
|
1239
|
+
return true;
|
|
1240
|
+
}
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
const decodeLoopStartedAt = syncProfileStart(profile);
|
|
1244
|
+
let symbolsProcessed = 0;
|
|
1245
|
+
for (const symbol of CodedSymbolBatch.from(symbolMessage.symbols)) {
|
|
1246
|
+
symbolsProcessed += 1;
|
|
800
1247
|
const normalizedSymbol = symbol instanceof SymbolSerialized
|
|
801
1248
|
? symbol
|
|
802
1249
|
: new SymbolSerialized({
|
|
@@ -820,6 +1267,13 @@ export class RatelessIBLTSynchronizer {
|
|
|
820
1267
|
throw error;
|
|
821
1268
|
}
|
|
822
1269
|
}
|
|
1270
|
+
if (profile) {
|
|
1271
|
+
emitSyncProfileDuration(profile, decodeLoopStartedAt, {
|
|
1272
|
+
name: "rateless.decodeSymbolLoop",
|
|
1273
|
+
symbols: symbolsProcessed,
|
|
1274
|
+
syncId,
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
823
1277
|
}
|
|
824
1278
|
return false;
|
|
825
1279
|
},
|
|
@@ -834,17 +1288,27 @@ export class RatelessIBLTSynchronizer {
|
|
|
834
1288
|
return true;
|
|
835
1289
|
}
|
|
836
1290
|
// not done, request more symbols
|
|
1291
|
+
const sendStartedAt = syncProfileStart(profile);
|
|
837
1292
|
await this.simple.rpc.send(new RequestMoreSymbols({
|
|
838
1293
|
lastSeqNo: 0n,
|
|
839
1294
|
syncId: message.syncId,
|
|
840
1295
|
}), {
|
|
841
1296
|
mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
|
|
842
|
-
priority:
|
|
1297
|
+
priority: SYNC_MESSAGE_PRIORITY,
|
|
843
1298
|
});
|
|
1299
|
+
if (profile) {
|
|
1300
|
+
emitSyncProfileDuration(profile, sendStartedAt, {
|
|
1301
|
+
name: "rateless.sendRequestMoreSymbols",
|
|
1302
|
+
messages: 1,
|
|
1303
|
+
targets: 1,
|
|
1304
|
+
syncId,
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
844
1307
|
return true;
|
|
845
1308
|
}
|
|
846
1309
|
else if (message instanceof MoreSymbols) {
|
|
847
|
-
const
|
|
1310
|
+
const syncId = getSyncIdString(message);
|
|
1311
|
+
const obj = this.ingoingSyncProcesses.get(syncId);
|
|
848
1312
|
if (!obj) {
|
|
849
1313
|
return true;
|
|
850
1314
|
}
|
|
@@ -856,28 +1320,55 @@ export class RatelessIBLTSynchronizer {
|
|
|
856
1320
|
return true; // we don't have enough information, or received information that is redundant
|
|
857
1321
|
}
|
|
858
1322
|
// we are not done
|
|
859
|
-
|
|
1323
|
+
const sendStartedAt = syncProfileStart(profile);
|
|
1324
|
+
const sendResult = this.simple.rpc.send(new RequestMoreSymbols({
|
|
860
1325
|
lastSeqNo: message.seqNo,
|
|
861
1326
|
syncId: message.syncId,
|
|
862
1327
|
}), {
|
|
863
1328
|
mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
|
|
864
|
-
priority:
|
|
1329
|
+
priority: SYNC_MESSAGE_PRIORITY,
|
|
865
1330
|
});
|
|
1331
|
+
if (profile) {
|
|
1332
|
+
void Promise.resolve(sendResult).then(() => emitSyncProfileDuration(profile, sendStartedAt, {
|
|
1333
|
+
name: "rateless.sendRequestMoreSymbols",
|
|
1334
|
+
messages: 1,
|
|
1335
|
+
targets: 1,
|
|
1336
|
+
syncId,
|
|
1337
|
+
}), () => emitSyncProfileDuration(profile, sendStartedAt, {
|
|
1338
|
+
name: "rateless.sendRequestMoreSymbols",
|
|
1339
|
+
messages: 1,
|
|
1340
|
+
targets: 1,
|
|
1341
|
+
syncId,
|
|
1342
|
+
details: { rejected: true },
|
|
1343
|
+
}));
|
|
1344
|
+
}
|
|
866
1345
|
return true;
|
|
867
1346
|
}
|
|
868
1347
|
else if (message instanceof RequestMoreSymbols) {
|
|
869
|
-
const
|
|
1348
|
+
const syncId = getSyncIdString(message);
|
|
1349
|
+
const obj = this.outgoingSyncProcesses.get(syncId);
|
|
870
1350
|
if (!obj) {
|
|
871
1351
|
return true;
|
|
872
1352
|
}
|
|
1353
|
+
const symbols = obj.next(message);
|
|
1354
|
+
const sendStartedAt = syncProfileStart(profile);
|
|
873
1355
|
await this.properties.rpc.send(new MoreSymbols({
|
|
874
1356
|
lastSeqNo: message.lastSeqNo,
|
|
875
1357
|
syncId: message.syncId,
|
|
876
|
-
symbols
|
|
1358
|
+
symbols,
|
|
877
1359
|
}), {
|
|
878
1360
|
mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
|
|
879
|
-
priority:
|
|
1361
|
+
priority: SYNC_MESSAGE_PRIORITY,
|
|
880
1362
|
});
|
|
1363
|
+
if (profile) {
|
|
1364
|
+
emitSyncProfileDuration(profile, sendStartedAt, {
|
|
1365
|
+
name: "rateless.sendMoreSymbols",
|
|
1366
|
+
messages: 1,
|
|
1367
|
+
symbols: symbols.length,
|
|
1368
|
+
targets: 1,
|
|
1369
|
+
syncId,
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
881
1372
|
return true;
|
|
882
1373
|
}
|
|
883
1374
|
else if (message instanceof RequestAll) {
|