@peerbit/shared-log 9.2.13 → 10.0.0-05f4bef
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/get-samples.js +190 -64
- package/dist/benchmark/get-samples.js.map +1 -1
- package/dist/benchmark/index.js +16 -38
- package/dist/benchmark/index.js.map +1 -1
- package/dist/benchmark/memory/child.js.map +1 -1
- package/dist/benchmark/partial-sync.d.ts +3 -0
- package/dist/benchmark/partial-sync.d.ts.map +1 -0
- package/dist/benchmark/partial-sync.js +121 -0
- package/dist/benchmark/partial-sync.js.map +1 -0
- package/dist/benchmark/replication-prune.js.map +1 -1
- package/dist/benchmark/replication.js.map +1 -1
- package/dist/benchmark/to-rebalance.d.ts +2 -0
- package/dist/benchmark/to-rebalance.d.ts.map +1 -0
- package/dist/benchmark/to-rebalance.js +117 -0
- package/dist/benchmark/to-rebalance.js.map +1 -0
- package/dist/benchmark/utils.d.ts +24 -0
- package/dist/benchmark/utils.d.ts.map +1 -0
- package/dist/benchmark/utils.js +47 -0
- package/dist/benchmark/utils.js.map +1 -0
- package/dist/src/debounce.d.ts +2 -2
- package/dist/src/debounce.d.ts.map +1 -1
- package/dist/src/debounce.js +17 -47
- package/dist/src/debounce.js.map +1 -1
- package/dist/src/exchange-heads.d.ts +1 -13
- package/dist/src/exchange-heads.d.ts.map +1 -1
- package/dist/src/exchange-heads.js +0 -32
- package/dist/src/exchange-heads.js.map +1 -1
- package/dist/src/index.d.ts +119 -60
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1116 -762
- package/dist/src/index.js.map +1 -1
- package/dist/src/integers.d.ts +22 -0
- package/dist/src/integers.d.ts.map +1 -0
- package/dist/src/integers.js +76 -0
- package/dist/src/integers.js.map +1 -0
- package/dist/src/pid.d.ts.map +1 -1
- package/dist/src/pid.js +22 -22
- package/dist/src/pid.js.map +1 -1
- package/dist/src/ranges.d.ts +168 -38
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +869 -272
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/replication-domain-hash.d.ts +2 -3
- package/dist/src/replication-domain-hash.d.ts.map +1 -1
- package/dist/src/replication-domain-hash.js +40 -15
- package/dist/src/replication-domain-hash.js.map +1 -1
- package/dist/src/replication-domain-time.d.ts +5 -5
- package/dist/src/replication-domain-time.d.ts.map +1 -1
- package/dist/src/replication-domain-time.js +2 -0
- package/dist/src/replication-domain-time.js.map +1 -1
- package/dist/src/replication-domain.d.ts +17 -19
- package/dist/src/replication-domain.d.ts.map +1 -1
- package/dist/src/replication-domain.js +2 -6
- package/dist/src/replication-domain.js.map +1 -1
- package/dist/src/replication.d.ts +6 -6
- package/dist/src/replication.d.ts.map +1 -1
- package/dist/src/replication.js +4 -4
- package/dist/src/replication.js.map +1 -1
- package/dist/src/role.d.ts +3 -6
- package/dist/src/role.d.ts.map +1 -1
- package/dist/src/role.js +4 -5
- package/dist/src/role.js.map +1 -1
- package/dist/src/sync/index.d.ts +40 -0
- package/dist/src/sync/index.d.ts.map +1 -0
- package/dist/src/sync/index.js +2 -0
- package/dist/src/sync/index.js.map +1 -0
- package/dist/src/sync/rateless-iblt.d.ts +124 -0
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -0
- package/dist/src/sync/rateless-iblt.js +495 -0
- package/dist/src/sync/rateless-iblt.js.map +1 -0
- package/dist/src/sync/simple.d.ts +69 -0
- package/dist/src/sync/simple.d.ts.map +1 -0
- package/dist/src/sync/simple.js +338 -0
- package/dist/src/sync/simple.js.map +1 -0
- package/dist/src/sync/wasm-init.browser.d.ts +1 -0
- package/dist/src/sync/wasm-init.browser.d.ts.map +1 -0
- package/dist/src/sync/wasm-init.browser.js +3 -0
- package/dist/src/sync/wasm-init.browser.js.map +1 -0
- package/dist/src/sync/wasm-init.d.ts +2 -0
- package/dist/src/sync/wasm-init.d.ts.map +1 -0
- package/dist/src/sync/wasm-init.js +13 -0
- package/dist/src/sync/wasm-init.js.map +1 -0
- package/dist/src/utils.d.ts +3 -3
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +2 -2
- package/dist/src/utils.js.map +1 -1
- package/package.json +73 -69
- package/src/debounce.ts +16 -51
- package/src/exchange-heads.ts +1 -23
- package/src/index.ts +1532 -1038
- package/src/integers.ts +102 -0
- package/src/pid.ts +23 -22
- package/src/ranges.ts +1204 -413
- package/src/replication-domain-hash.ts +43 -18
- package/src/replication-domain-time.ts +9 -9
- package/src/replication-domain.ts +21 -31
- package/src/replication.ts +10 -9
- package/src/role.ts +4 -6
- package/src/sync/index.ts +51 -0
- package/src/sync/rateless-iblt.ts +617 -0
- package/src/sync/simple.ts +403 -0
- package/src/sync/wasm-init.browser.ts +1 -0
- package/src/sync/wasm-init.ts +14 -0
- package/src/utils.ts +10 -4
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
import { field, variant, vec } from "@dao-xyz/borsh";
|
|
2
|
+
import { Cache } from "@peerbit/cache";
|
|
3
|
+
import { type PublicSignKey, randomBytes, toBase64 } from "@peerbit/crypto";
|
|
4
|
+
import { type Index } from "@peerbit/indexer-interface";
|
|
5
|
+
import type { Entry, Log } from "@peerbit/log";
|
|
6
|
+
import init, { DecoderWrapper, EncoderWrapper } from "@peerbit/riblt";
|
|
7
|
+
import type { RPC, RequestContext } from "@peerbit/rpc";
|
|
8
|
+
import { SilentDelivery } from "@peerbit/stream-interface";
|
|
9
|
+
import type { SyncableKey, Syncronizer } from ".";
|
|
10
|
+
import { type EntryWithRefs } from "../exchange-heads.js";
|
|
11
|
+
import { type NumberFromType, type Numbers } from "../integers.js";
|
|
12
|
+
import { TransportMessage } from "../message.js";
|
|
13
|
+
import {
|
|
14
|
+
type EntryReplicated,
|
|
15
|
+
type ReplicationRangeIndexable,
|
|
16
|
+
matchEntriesInRangeQuery,
|
|
17
|
+
} from "../ranges.js";
|
|
18
|
+
import { SimpleSyncronizer } from "./simple.js";
|
|
19
|
+
import "./wasm-init.js";
|
|
20
|
+
|
|
21
|
+
await init();
|
|
22
|
+
|
|
23
|
+
type NumberOrBigint = number | bigint;
|
|
24
|
+
|
|
25
|
+
const coerceBigInt = (value: NumberOrBigint): bigint =>
|
|
26
|
+
typeof value === "bigint" ? value : BigInt(value);
|
|
27
|
+
|
|
28
|
+
class SymbolSerialized implements SSymbol {
|
|
29
|
+
@field({ type: "u64" })
|
|
30
|
+
count: bigint;
|
|
31
|
+
|
|
32
|
+
@field({ type: "u64" })
|
|
33
|
+
hash: bigint;
|
|
34
|
+
|
|
35
|
+
@field({ type: "u64" })
|
|
36
|
+
symbol: bigint;
|
|
37
|
+
|
|
38
|
+
constructor(props: { count: bigint; hash: bigint; symbol: bigint }) {
|
|
39
|
+
this.count = props.count;
|
|
40
|
+
this.hash = props.hash;
|
|
41
|
+
this.symbol = props.symbol;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const getSyncIdString = (message: { syncId: Uint8Array }) => {
|
|
46
|
+
return toBase64(message.syncId);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
@variant([3, 0])
|
|
50
|
+
export class StartSync extends TransportMessage {
|
|
51
|
+
@field({ type: Uint8Array })
|
|
52
|
+
syncId: Uint8Array;
|
|
53
|
+
|
|
54
|
+
@field({ type: "u64" })
|
|
55
|
+
start: bigint;
|
|
56
|
+
|
|
57
|
+
@field({ type: "u64" })
|
|
58
|
+
end: bigint;
|
|
59
|
+
|
|
60
|
+
@field({ type: vec(SymbolSerialized) })
|
|
61
|
+
symbols: SymbolSerialized[];
|
|
62
|
+
|
|
63
|
+
constructor(props: {
|
|
64
|
+
from: NumberOrBigint;
|
|
65
|
+
to: NumberOrBigint;
|
|
66
|
+
symbols: SymbolSerialized[];
|
|
67
|
+
}) {
|
|
68
|
+
super();
|
|
69
|
+
this.syncId = randomBytes(32);
|
|
70
|
+
this.start = coerceBigInt(props.from);
|
|
71
|
+
this.end = coerceBigInt(props.to);
|
|
72
|
+
this.symbols = props.symbols;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@variant([3, 1])
|
|
77
|
+
export class MoreSymbols extends TransportMessage {
|
|
78
|
+
@field({ type: Uint8Array })
|
|
79
|
+
syncId: Uint8Array;
|
|
80
|
+
|
|
81
|
+
@field({ type: "u64" })
|
|
82
|
+
seqNo: bigint;
|
|
83
|
+
|
|
84
|
+
@field({ type: vec(SymbolSerialized) })
|
|
85
|
+
symbols: SymbolSerialized[];
|
|
86
|
+
|
|
87
|
+
constructor(props: {
|
|
88
|
+
syncId: Uint8Array;
|
|
89
|
+
lastSeqNo: bigint;
|
|
90
|
+
symbols: SymbolSerialized[];
|
|
91
|
+
}) {
|
|
92
|
+
super();
|
|
93
|
+
this.syncId = props.syncId;
|
|
94
|
+
this.seqNo = props.lastSeqNo + 1n;
|
|
95
|
+
this.symbols = props.symbols;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@variant([3, 2])
|
|
100
|
+
export class RequestMoreSymbols extends TransportMessage {
|
|
101
|
+
@field({ type: Uint8Array })
|
|
102
|
+
syncId: Uint8Array;
|
|
103
|
+
|
|
104
|
+
@field({ type: "u64" })
|
|
105
|
+
lastSeqNo: bigint;
|
|
106
|
+
|
|
107
|
+
constructor(props: { syncId: Uint8Array; lastSeqNo: bigint }) {
|
|
108
|
+
super();
|
|
109
|
+
this.syncId = props.syncId;
|
|
110
|
+
this.lastSeqNo = props.lastSeqNo;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@variant([3, 3])
|
|
115
|
+
export class RequestAll extends TransportMessage {
|
|
116
|
+
@field({ type: Uint8Array })
|
|
117
|
+
syncId: Uint8Array;
|
|
118
|
+
|
|
119
|
+
constructor(props: { syncId: Uint8Array }) {
|
|
120
|
+
super();
|
|
121
|
+
this.syncId = props.syncId;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface SSymbol {
|
|
126
|
+
count: bigint;
|
|
127
|
+
hash: bigint;
|
|
128
|
+
symbol: bigint;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const buildEncoderOrDecoderFromRange = async <
|
|
132
|
+
T extends "encoder" | "decoder",
|
|
133
|
+
E = T extends "encoder" ? EncoderWrapper : DecoderWrapper,
|
|
134
|
+
D extends "u32" | "u64" = "u64",
|
|
135
|
+
>(
|
|
136
|
+
ranges: {
|
|
137
|
+
start1: NumberOrBigint;
|
|
138
|
+
end1: NumberOrBigint;
|
|
139
|
+
start2: NumberOrBigint;
|
|
140
|
+
end2: NumberOrBigint;
|
|
141
|
+
},
|
|
142
|
+
entryIndex: Index<EntryReplicated<D>>,
|
|
143
|
+
type: T,
|
|
144
|
+
): Promise<E | false> => {
|
|
145
|
+
const encoder =
|
|
146
|
+
type === "encoder" ? new EncoderWrapper() : new DecoderWrapper();
|
|
147
|
+
|
|
148
|
+
/* const buildDecoderStart = +new Date(); */
|
|
149
|
+
let symbolCount = 0;
|
|
150
|
+
|
|
151
|
+
const entries = await entryIndex
|
|
152
|
+
.iterate(
|
|
153
|
+
{
|
|
154
|
+
query: matchEntriesInRangeQuery({
|
|
155
|
+
end1: ranges.end1,
|
|
156
|
+
start1: ranges.start1,
|
|
157
|
+
end2: ranges.end2,
|
|
158
|
+
start2: ranges.start2,
|
|
159
|
+
}),
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
shape: {
|
|
163
|
+
coordinates: true,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
.all();
|
|
168
|
+
|
|
169
|
+
if (entries.length === 0) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (const entry of entries) {
|
|
174
|
+
symbolCount += entry.value.coordinates.length;
|
|
175
|
+
|
|
176
|
+
for (const coordinate of entry.value.coordinates) {
|
|
177
|
+
encoder.add_symbol(coerceBigInt(coordinate));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return encoder as E;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export class RatelessIBLTSynchronizer<
|
|
185
|
+
D extends "u32" | "u64",
|
|
186
|
+
N = NumberFromType<D>,
|
|
187
|
+
> implements Syncronizer<D>
|
|
188
|
+
{
|
|
189
|
+
simple: SimpleSyncronizer<D>;
|
|
190
|
+
|
|
191
|
+
startedOrCompletedSynchronizations: Cache<string>;
|
|
192
|
+
ingoingSyncProcesses: Map<
|
|
193
|
+
string,
|
|
194
|
+
{
|
|
195
|
+
decoder: DecoderWrapper;
|
|
196
|
+
timeout: ReturnType<typeof setTimeout>;
|
|
197
|
+
refresh: () => void;
|
|
198
|
+
process: (message: {
|
|
199
|
+
seqNo: bigint;
|
|
200
|
+
symbols: SSymbol[];
|
|
201
|
+
}) => Promise<boolean | undefined>;
|
|
202
|
+
free: () => void;
|
|
203
|
+
}
|
|
204
|
+
>;
|
|
205
|
+
|
|
206
|
+
outgoingSyncProcesses: Map<
|
|
207
|
+
string,
|
|
208
|
+
{
|
|
209
|
+
outgoing: Map<string, EntryReplicated<D>>;
|
|
210
|
+
encoder: EncoderWrapper;
|
|
211
|
+
timeout: ReturnType<typeof setTimeout>;
|
|
212
|
+
refresh: () => void;
|
|
213
|
+
next: (message: { lastSeqNo: bigint }) => SSymbol[];
|
|
214
|
+
free: () => void;
|
|
215
|
+
}
|
|
216
|
+
>;
|
|
217
|
+
|
|
218
|
+
constructor(
|
|
219
|
+
readonly properties: {
|
|
220
|
+
rpc: RPC<TransportMessage, TransportMessage>;
|
|
221
|
+
rangeIndex: Index<ReplicationRangeIndexable<D>, any>;
|
|
222
|
+
entryIndex: Index<EntryReplicated<D>, any>;
|
|
223
|
+
log: Log<any>;
|
|
224
|
+
coordinateToHash: Cache<string>;
|
|
225
|
+
numbers: Numbers<D>;
|
|
226
|
+
},
|
|
227
|
+
) {
|
|
228
|
+
this.simple = new SimpleSyncronizer(properties);
|
|
229
|
+
this.outgoingSyncProcesses = new Map();
|
|
230
|
+
this.ingoingSyncProcesses = new Map();
|
|
231
|
+
this.startedOrCompletedSynchronizations = new Cache({ max: 1e4 });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async onMaybeMissingEntries(properties: {
|
|
235
|
+
entries: Map<string, EntryReplicated<D>>;
|
|
236
|
+
targets: string[];
|
|
237
|
+
}): Promise<void> {
|
|
238
|
+
// calculate the smallest range that covers all the entries
|
|
239
|
+
// calculate the largest gap and the smallest range will be the one that starts at the end of it
|
|
240
|
+
|
|
241
|
+
// assume sorted, and find the largest gap
|
|
242
|
+
let largestGap = 0n;
|
|
243
|
+
let largestGapIndex = 0;
|
|
244
|
+
|
|
245
|
+
let entriesToSyncNaively: Map<string, EntryReplicated<D>> = new Map();
|
|
246
|
+
|
|
247
|
+
let allCoordinatesToSyncWithIblt: bigint[] = [];
|
|
248
|
+
let minSyncIbltSize = 333; // TODO arg
|
|
249
|
+
let maxSyncWithSimpleMethod = 1e3;
|
|
250
|
+
|
|
251
|
+
for (const entry of properties.entries.values()) {
|
|
252
|
+
if (
|
|
253
|
+
entry.assignedToRangeBoundary ||
|
|
254
|
+
properties.entries.size < minSyncIbltSize
|
|
255
|
+
) {
|
|
256
|
+
entriesToSyncNaively.set(entry.hash, entry);
|
|
257
|
+
} else {
|
|
258
|
+
for (const coordinate of entry.coordinates) {
|
|
259
|
+
allCoordinatesToSyncWithIblt.push(coerceBigInt(coordinate));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (
|
|
265
|
+
allCoordinatesToSyncWithIblt.length === 0 ||
|
|
266
|
+
entriesToSyncNaively.size > maxSyncWithSimpleMethod
|
|
267
|
+
) {
|
|
268
|
+
// add all coordinates to sync with iblt
|
|
269
|
+
allCoordinatesToSyncWithIblt = Array.from(
|
|
270
|
+
properties.entries.values(),
|
|
271
|
+
).flatMap((entry) => entry.coordinates.map((y) => coerceBigInt(y)));
|
|
272
|
+
} else {
|
|
273
|
+
// TODO what happens if too many
|
|
274
|
+
await this.simple.onMaybeMissingEntries({
|
|
275
|
+
entries: entriesToSyncNaively,
|
|
276
|
+
targets: properties.targets,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (allCoordinatesToSyncWithIblt.length === 0) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const sortedEntries = allCoordinatesToSyncWithIblt.sort((a, b) => {
|
|
285
|
+
if (a > b) {
|
|
286
|
+
return 1;
|
|
287
|
+
} else if (a < b) {
|
|
288
|
+
return -1;
|
|
289
|
+
} else {
|
|
290
|
+
return 0;
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
for (let i = 0; i < sortedEntries.length - 1; i++) {
|
|
295
|
+
const current = sortedEntries[i];
|
|
296
|
+
const next = sortedEntries[i + 1];
|
|
297
|
+
const gap =
|
|
298
|
+
next >= current
|
|
299
|
+
? next - current
|
|
300
|
+
: coerceBigInt(this.properties.numbers.maxValue) - current + next;
|
|
301
|
+
if (gap > largestGap) {
|
|
302
|
+
largestGap = gap;
|
|
303
|
+
largestGapIndex = i;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const smallestRangeStartIndex =
|
|
308
|
+
(largestGapIndex + 1) % sortedEntries.length;
|
|
309
|
+
const smallestRangeEndIndex = largestGapIndex; /// === (smallRangeStartIndex + 1) % sortedEntries.length
|
|
310
|
+
let smallestRangeStart = sortedEntries[smallestRangeStartIndex];
|
|
311
|
+
let smallestRangeEnd = sortedEntries[smallestRangeEndIndex];
|
|
312
|
+
let start: bigint, end: bigint;
|
|
313
|
+
if (smallestRangeEnd === smallestRangeStart) {
|
|
314
|
+
start = smallestRangeEnd;
|
|
315
|
+
end = smallestRangeEnd + 1n;
|
|
316
|
+
if (end > this.properties.numbers.maxValue) {
|
|
317
|
+
end = 0n;
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
start = smallestRangeStart;
|
|
321
|
+
end = smallestRangeEnd;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const startSync = new StartSync({ from: start, to: end, symbols: [] });
|
|
325
|
+
const encoder = new EncoderWrapper();
|
|
326
|
+
for (const entry of sortedEntries) {
|
|
327
|
+
encoder.add_symbol(BigInt(entry));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let initialSymbols = Math.round(
|
|
331
|
+
Math.sqrt(allCoordinatesToSyncWithIblt.length),
|
|
332
|
+
); // TODO choose better
|
|
333
|
+
for (let i = 0; i < initialSymbols; i++) {
|
|
334
|
+
startSync.symbols.push(
|
|
335
|
+
new SymbolSerialized(encoder.produce_next_coded_symbol()),
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const clear = () => {
|
|
340
|
+
// encoder.free(); TODO?
|
|
341
|
+
clearTimeout(
|
|
342
|
+
this.outgoingSyncProcesses.get(getSyncIdString(startSync))?.timeout,
|
|
343
|
+
);
|
|
344
|
+
this.outgoingSyncProcesses.delete(getSyncIdString(startSync));
|
|
345
|
+
};
|
|
346
|
+
const createTimeout = () => {
|
|
347
|
+
return setTimeout(clear, 1e4); // TODO arg
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
let lastSeqNo = -1n;
|
|
351
|
+
let nextBatch = 1e4;
|
|
352
|
+
const obj = {
|
|
353
|
+
encoder,
|
|
354
|
+
timeout: createTimeout(),
|
|
355
|
+
refresh: () => {
|
|
356
|
+
let prevTimeout = obj.timeout;
|
|
357
|
+
if (prevTimeout) {
|
|
358
|
+
clearTimeout(prevTimeout);
|
|
359
|
+
}
|
|
360
|
+
obj.timeout = createTimeout();
|
|
361
|
+
},
|
|
362
|
+
next: (properties: { lastSeqNo: bigint }): SSymbol[] => {
|
|
363
|
+
if (properties.lastSeqNo <= lastSeqNo) {
|
|
364
|
+
return [];
|
|
365
|
+
}
|
|
366
|
+
lastSeqNo++;
|
|
367
|
+
obj.refresh(); // TODO use timestamp instead and collective pruning/refresh
|
|
368
|
+
|
|
369
|
+
let result: SSymbol[] = [];
|
|
370
|
+
for (let i = 0; i < nextBatch; i++) {
|
|
371
|
+
result.push(encoder.produce_next_coded_symbol());
|
|
372
|
+
}
|
|
373
|
+
return result;
|
|
374
|
+
},
|
|
375
|
+
free: clear,
|
|
376
|
+
outgoing: properties.entries,
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
this.outgoingSyncProcesses.set(getSyncIdString(startSync), obj);
|
|
380
|
+
this.simple.rpc.send(startSync, {
|
|
381
|
+
mode: new SilentDelivery({ to: properties.targets, redundancy: 1 }),
|
|
382
|
+
priority: 1,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async onMessage(
|
|
387
|
+
message: TransportMessage,
|
|
388
|
+
context: RequestContext,
|
|
389
|
+
): Promise<boolean> {
|
|
390
|
+
if (message instanceof StartSync) {
|
|
391
|
+
const syncId = getSyncIdString(message);
|
|
392
|
+
if (this.ingoingSyncProcesses.has(syncId)) {
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (this.startedOrCompletedSynchronizations.has(syncId)) {
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
this.startedOrCompletedSynchronizations.add(syncId);
|
|
401
|
+
|
|
402
|
+
const wrapped = message.end < message.start;
|
|
403
|
+
const decoder = await buildEncoderOrDecoderFromRange(
|
|
404
|
+
{
|
|
405
|
+
start1: message.start,
|
|
406
|
+
end1: wrapped ? this.properties.numbers.maxValue : message.end,
|
|
407
|
+
start2: 0n,
|
|
408
|
+
end2: wrapped ? message.end : 0n,
|
|
409
|
+
},
|
|
410
|
+
this.properties.entryIndex,
|
|
411
|
+
"decoder",
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
if (!decoder) {
|
|
415
|
+
await this.simple.rpc.send(
|
|
416
|
+
new RequestAll({
|
|
417
|
+
syncId: message.syncId,
|
|
418
|
+
}),
|
|
419
|
+
{
|
|
420
|
+
mode: new SilentDelivery({ to: [context.from!], redundancy: 1 }),
|
|
421
|
+
priority: 1,
|
|
422
|
+
},
|
|
423
|
+
);
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const createTimeout = () => {
|
|
428
|
+
return setTimeout(() => {
|
|
429
|
+
// decoder.free(); TODO?
|
|
430
|
+
this.ingoingSyncProcesses.delete(syncId);
|
|
431
|
+
}, 2e4); // TODO arg
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
let count = 0;
|
|
435
|
+
/* let t0 = +new Date(); */
|
|
436
|
+
let messageQueue: {
|
|
437
|
+
seqNo: bigint;
|
|
438
|
+
symbols: (SSymbol | SymbolSerialized)[];
|
|
439
|
+
}[] = [];
|
|
440
|
+
let lastSeqNo = -1n;
|
|
441
|
+
const obj = {
|
|
442
|
+
decoder,
|
|
443
|
+
timeout: createTimeout(),
|
|
444
|
+
refresh: () => {
|
|
445
|
+
let prevTimeout = obj.timeout;
|
|
446
|
+
if (prevTimeout) {
|
|
447
|
+
clearTimeout(prevTimeout);
|
|
448
|
+
}
|
|
449
|
+
obj.timeout = createTimeout();
|
|
450
|
+
},
|
|
451
|
+
process: async (newMessage: {
|
|
452
|
+
seqNo: bigint;
|
|
453
|
+
symbols: (SSymbol | SymbolSerialized)[];
|
|
454
|
+
}): Promise<boolean | undefined> => {
|
|
455
|
+
obj.refresh(); // TODO use timestamp instead and collective pruning/refresh
|
|
456
|
+
|
|
457
|
+
if (newMessage.seqNo <= lastSeqNo) {
|
|
458
|
+
return undefined;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
messageQueue.push(newMessage);
|
|
462
|
+
messageQueue.sort((a, b) => Number(a.seqNo - b.seqNo));
|
|
463
|
+
if (messageQueue[0].seqNo !== lastSeqNo + 1n) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
lastSeqNo++;
|
|
467
|
+
|
|
468
|
+
const message = messageQueue.shift();
|
|
469
|
+
if (!message) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
for (const symbol of message.symbols) {
|
|
474
|
+
decoder.add_coded_symbol(symbol);
|
|
475
|
+
}
|
|
476
|
+
decoder.try_decode();
|
|
477
|
+
count += message.symbols.length;
|
|
478
|
+
|
|
479
|
+
if (decoder.decoded()) {
|
|
480
|
+
let allMissingSymbolsInRemote: bigint[] = [];
|
|
481
|
+
for (const missingSymbol of decoder.get_remote_symbols()) {
|
|
482
|
+
allMissingSymbolsInRemote.push(missingSymbol);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
this.simple.queueSync(allMissingSymbolsInRemote, context.from!, {
|
|
486
|
+
skipCheck: true,
|
|
487
|
+
});
|
|
488
|
+
obj.free();
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
},
|
|
493
|
+
free: () => {
|
|
494
|
+
// decoder.free(); TODO?
|
|
495
|
+
clearTimeout(this.ingoingSyncProcesses.get(syncId)?.timeout);
|
|
496
|
+
this.ingoingSyncProcesses.delete(syncId);
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
this.ingoingSyncProcesses.set(syncId, obj);
|
|
501
|
+
|
|
502
|
+
if (await obj.process({ seqNo: 0n, symbols: message.symbols })) {
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// not done, request more symbols
|
|
507
|
+
await this.simple.rpc.send(
|
|
508
|
+
new RequestMoreSymbols({
|
|
509
|
+
lastSeqNo: 0n,
|
|
510
|
+
syncId: message.syncId,
|
|
511
|
+
}),
|
|
512
|
+
{
|
|
513
|
+
mode: new SilentDelivery({ to: [context.from!], redundancy: 1 }),
|
|
514
|
+
priority: 1,
|
|
515
|
+
},
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
return true;
|
|
519
|
+
} else if (message instanceof MoreSymbols) {
|
|
520
|
+
const obj = this.ingoingSyncProcesses.get(getSyncIdString(message));
|
|
521
|
+
if (!obj) {
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
const outProcess = await obj.process(message);
|
|
525
|
+
|
|
526
|
+
if (outProcess === true) {
|
|
527
|
+
return true;
|
|
528
|
+
} else if (outProcess === undefined) {
|
|
529
|
+
return false; // we don't have enough information, or received information that is redundant
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// we are not done
|
|
533
|
+
|
|
534
|
+
this.simple.rpc.send(
|
|
535
|
+
new RequestMoreSymbols({
|
|
536
|
+
lastSeqNo: message.seqNo,
|
|
537
|
+
syncId: message.syncId,
|
|
538
|
+
}),
|
|
539
|
+
{
|
|
540
|
+
mode: new SilentDelivery({ to: [context.from!], redundancy: 1 }),
|
|
541
|
+
priority: 1,
|
|
542
|
+
},
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
return true;
|
|
546
|
+
} else if (message instanceof RequestMoreSymbols) {
|
|
547
|
+
const obj = this.outgoingSyncProcesses.get(getSyncIdString(message));
|
|
548
|
+
if (!obj) {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
await this.properties.rpc.send(
|
|
552
|
+
new MoreSymbols({
|
|
553
|
+
lastSeqNo: message.lastSeqNo,
|
|
554
|
+
syncId: message.syncId,
|
|
555
|
+
symbols: obj.next(message).map((x) => new SymbolSerialized(x)),
|
|
556
|
+
}),
|
|
557
|
+
{
|
|
558
|
+
mode: new SilentDelivery({ to: [context.from!], redundancy: 1 }),
|
|
559
|
+
priority: 1,
|
|
560
|
+
},
|
|
561
|
+
);
|
|
562
|
+
return true;
|
|
563
|
+
} else if (message instanceof RequestAll) {
|
|
564
|
+
const p = this.outgoingSyncProcesses.get(getSyncIdString(message));
|
|
565
|
+
if (!p) {
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
await this.simple.onMaybeMissingEntries({
|
|
569
|
+
entries: p.outgoing,
|
|
570
|
+
targets: [context.from!.hashcode()],
|
|
571
|
+
});
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
return this.simple.onMessage(message, context);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
onReceivedEntries(properties: {
|
|
578
|
+
entries: EntryWithRefs<any>[];
|
|
579
|
+
from: PublicSignKey;
|
|
580
|
+
}): Promise<void> | void {
|
|
581
|
+
return this.simple.onReceivedEntries(properties);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
onEntryAdded(entry: Entry<any>): void {
|
|
585
|
+
return this.simple.onEntryAdded(entry);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
onEntryRemoved(hash: string) {
|
|
589
|
+
return this.simple.onEntryRemoved(hash);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
onPeerDisconnected(key: PublicSignKey) {
|
|
593
|
+
return this.simple.onPeerDisconnected(key);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
open(): Promise<void> | void {
|
|
597
|
+
return this.simple.open();
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
close(): Promise<void> | void {
|
|
601
|
+
for (const [, obj] of this.ingoingSyncProcesses) {
|
|
602
|
+
obj.free();
|
|
603
|
+
}
|
|
604
|
+
for (const [, obj] of this.outgoingSyncProcesses) {
|
|
605
|
+
obj.free();
|
|
606
|
+
}
|
|
607
|
+
return this.simple.close();
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
get syncInFlight(): Map<string, Map<SyncableKey, { timestamp: number }>> {
|
|
611
|
+
return this.simple.syncInFlight;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
get pending(): number {
|
|
615
|
+
return this.simple.pending;
|
|
616
|
+
}
|
|
617
|
+
}
|