@peerbit/shared-log 9.0.9 → 9.0.10-ccaf4f4
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/index.js +2 -2
- package/dist/benchmark/index.js.map +1 -1
- package/dist/benchmark/replication.js +3 -3
- package/dist/benchmark/replication.js.map +1 -1
- package/dist/src/index.d.ts +46 -32
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +432 -231
- package/dist/src/index.js.map +1 -1
- package/dist/src/pid.d.ts.map +1 -1
- package/dist/src/pid.js +20 -19
- package/dist/src/pid.js.map +1 -1
- package/dist/src/ranges.d.ts +13 -3
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +207 -335
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/replication-domain-hash.d.ts +5 -0
- package/dist/src/replication-domain-hash.d.ts.map +1 -0
- package/dist/src/replication-domain-hash.js +30 -0
- package/dist/src/replication-domain-hash.js.map +1 -0
- package/dist/src/replication-domain-time.d.ts +14 -0
- package/dist/src/replication-domain-time.d.ts.map +1 -0
- package/dist/src/replication-domain-time.js +59 -0
- package/dist/src/replication-domain-time.js.map +1 -0
- package/dist/src/replication-domain.d.ts +33 -0
- package/dist/src/replication-domain.d.ts.map +1 -0
- package/dist/src/replication-domain.js +6 -0
- package/dist/src/replication-domain.js.map +1 -0
- package/dist/src/replication.d.ts +10 -8
- package/dist/src/replication.d.ts.map +1 -1
- package/dist/src/replication.js +64 -46
- package/dist/src/replication.js.map +1 -1
- package/dist/src/role.d.ts +2 -1
- package/dist/src/role.d.ts.map +1 -1
- package/dist/src/role.js +6 -5
- package/dist/src/role.js.map +1 -1
- package/package.json +70 -70
- package/src/index.ts +609 -317
- package/src/pid.ts +20 -19
- package/src/ranges.ts +291 -371
- package/src/replication-domain-hash.ts +43 -0
- package/src/replication-domain-time.ts +85 -0
- package/src/replication-domain.ts +50 -0
- package/src/replication.ts +50 -46
- package/src/role.ts +6 -5
package/src/ranges.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PublicSignKey, equals } from "@peerbit/crypto";
|
|
2
2
|
import {
|
|
3
3
|
And,
|
|
4
|
+
ByteMatchQuery,
|
|
4
5
|
Compare,
|
|
5
6
|
type Index,
|
|
6
7
|
type IndexIterator,
|
|
7
8
|
type IndexedResult,
|
|
8
9
|
type IndexedResults,
|
|
9
10
|
IntegerCompare,
|
|
11
|
+
Not,
|
|
10
12
|
Or,
|
|
11
13
|
type Query,
|
|
12
14
|
SearchRequest,
|
|
@@ -16,95 +18,12 @@ import {
|
|
|
16
18
|
iterate,
|
|
17
19
|
iteratorInSeries,
|
|
18
20
|
} from "@peerbit/indexer-interface";
|
|
21
|
+
import type { u32 } from "./replication-domain.js";
|
|
19
22
|
import {
|
|
20
|
-
|
|
23
|
+
ReplicationIntent,
|
|
21
24
|
type ReplicationRangeIndexable,
|
|
22
25
|
} from "./replication.js";
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
/*
|
|
26
|
-
export const containsPoint = (
|
|
27
|
-
rect: { offset: number; length: number },
|
|
28
|
-
point: number,
|
|
29
|
-
eps = 0.00001 // we do this to handle numerical errors
|
|
30
|
-
) => {
|
|
31
|
-
if (rect.factor === 0) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
const start = rect.offset;
|
|
35
|
-
const width = rect.factor + eps; // we do this to handle numerical errors. It is better to be more inclusive
|
|
36
|
-
const endUnwrapped = rect.offset + width;
|
|
37
|
-
let end = endUnwrapped;
|
|
38
|
-
let wrapped = false;
|
|
39
|
-
if (endUnwrapped > 1) {
|
|
40
|
-
end = endUnwrapped % 1;
|
|
41
|
-
wrapped = true;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const inFirstInterval = point >= start && point < Math.min(endUnwrapped, 1);
|
|
45
|
-
const inSecondInterval =
|
|
46
|
-
!inFirstInterval && wrapped && point >= 0 && point < end;
|
|
47
|
-
|
|
48
|
-
return inFirstInterval || inSecondInterval;
|
|
49
|
-
}; */
|
|
50
|
-
|
|
51
|
-
/* const resolveRectsThatContainPoint = async (
|
|
52
|
-
rects: Index<ReplicationRangeIndexable>,
|
|
53
|
-
point: number,
|
|
54
|
-
roleAgeLimit: number,
|
|
55
|
-
matured: boolean
|
|
56
|
-
): Promise<ReplicationRangeIndexable[]> => {
|
|
57
|
-
// point is between 0 and 1, and the range can start at any offset between 0 and 1 and have length between 0 and 1
|
|
58
|
-
// so we need to query for all ranges that contain the point
|
|
59
|
-
const scaledPoint = Math.round(point * SEGMENT_COORDINATE_SCALE)
|
|
60
|
-
let queries = [
|
|
61
|
-
new IntegerCompare({ key: 'start', compare: Compare.LessOrEqual, value: scaledPoint }),
|
|
62
|
-
new IntegerCompare({ key: 'end', compare: Compare.Greater, value: scaledPoint }),
|
|
63
|
-
new IntegerCompare({ key: 'timestamp', compare: matured ? Compare.LessOrEqual : Compare.Greater, value: Date.now() - roleAgeLimit })
|
|
64
|
-
]
|
|
65
|
-
|
|
66
|
-
const results = await rects.query(new SearchRequest({
|
|
67
|
-
query: [
|
|
68
|
-
new Nested({
|
|
69
|
-
path: 'segments',
|
|
70
|
-
query: queries
|
|
71
|
-
})
|
|
72
|
-
]
|
|
73
|
-
}))
|
|
74
|
-
return results.results.map(x => x.value)
|
|
75
|
-
} */
|
|
76
|
-
|
|
77
|
-
/* const resolveRectsInRange = async (rects: Index<ReplicationRangeIndexable>,
|
|
78
|
-
start: number,
|
|
79
|
-
end: number,
|
|
80
|
-
roleAgeLimit: number,
|
|
81
|
-
matured: boolean
|
|
82
|
-
): Promise<ReplicationRangeIndexable[]> => {
|
|
83
|
-
// point is between 0 and 1, and the range can start at any offset between 0 and 1 and have length between 0 and 1
|
|
84
|
-
// so we need to query for all ranges that contain the point
|
|
85
|
-
let endScaled = Math.round(end * SEGMENT_COORDINATE_SCALE);
|
|
86
|
-
let startScaled = Math.round(start * SEGMENT_COORDINATE_SCALE);
|
|
87
|
-
let queries = [
|
|
88
|
-
new Or([
|
|
89
|
-
new And([
|
|
90
|
-
new IntegerCompare({ key: 'start1', compare: Compare.Less, value: endScaled }),
|
|
91
|
-
new IntegerCompare({ key: 'end1', compare: Compare.GreaterOrEqual, value: startScaled }),
|
|
92
|
-
]),
|
|
93
|
-
new And([
|
|
94
|
-
new IntegerCompare({ key: 'start2', compare: Compare.Less, value: endScaled }),
|
|
95
|
-
new IntegerCompare({ key: 'end2', compare: Compare.GreaterOrEqual, value: startScaled }),
|
|
96
|
-
])
|
|
97
|
-
]),
|
|
98
|
-
new IntegerCompare({ key: 'timestamp', compare: matured ? Compare.LessOrEqual : Compare.Greater, value: BigInt(+new Date - roleAgeLimit) })
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
const results = await rects.query(new SearchRequest({
|
|
102
|
-
query: queries,
|
|
103
|
-
sort: [new Sort({ key: "start1" }), new Sort({ key: "start2" })],
|
|
104
|
-
fetch: 0xffffffff
|
|
105
|
-
}))
|
|
106
|
-
return results.results.map(x => x.value)
|
|
107
|
-
} */
|
|
26
|
+
import { MAX_U32, scaleToU32 } from "./role.js";
|
|
108
27
|
|
|
109
28
|
const containingPoint = (
|
|
110
29
|
rects: Index<ReplicationRangeIndexable>,
|
|
@@ -114,38 +33,34 @@ const containingPoint = (
|
|
|
114
33
|
now: number,
|
|
115
34
|
options?: {
|
|
116
35
|
sort?: Sort[];
|
|
117
|
-
scaled?: boolean;
|
|
118
36
|
},
|
|
119
37
|
): IndexIterator<ReplicationRangeIndexable> => {
|
|
120
38
|
// point is between 0 and 1, and the range can start at any offset between 0 and 1 and have length between 0 and 1
|
|
121
|
-
|
|
122
|
-
let pointScaled = Math.round(
|
|
123
|
-
point * (options?.scaled ? 1 : SEGMENT_COORDINATE_SCALE),
|
|
124
|
-
);
|
|
39
|
+
|
|
125
40
|
let queries = [
|
|
126
41
|
new Or([
|
|
127
42
|
new And([
|
|
128
43
|
new IntegerCompare({
|
|
129
44
|
key: "start1",
|
|
130
45
|
compare: Compare.LessOrEqual,
|
|
131
|
-
value:
|
|
46
|
+
value: point,
|
|
132
47
|
}),
|
|
133
48
|
new IntegerCompare({
|
|
134
49
|
key: "end1",
|
|
135
50
|
compare: Compare.Greater,
|
|
136
|
-
value:
|
|
51
|
+
value: point,
|
|
137
52
|
}),
|
|
138
53
|
]),
|
|
139
54
|
new And([
|
|
140
55
|
new IntegerCompare({
|
|
141
56
|
key: "start2",
|
|
142
57
|
compare: Compare.LessOrEqual,
|
|
143
|
-
value:
|
|
58
|
+
value: point,
|
|
144
59
|
}),
|
|
145
60
|
new IntegerCompare({
|
|
146
61
|
key: "end2",
|
|
147
62
|
compare: Compare.Greater,
|
|
148
|
-
value:
|
|
63
|
+
value: point,
|
|
149
64
|
}),
|
|
150
65
|
]),
|
|
151
66
|
]),
|
|
@@ -178,11 +93,8 @@ const getClosest = (
|
|
|
178
93
|
roleAgeLimit: number,
|
|
179
94
|
matured: boolean,
|
|
180
95
|
now: number,
|
|
181
|
-
|
|
96
|
+
includeStrict: boolean,
|
|
182
97
|
): IndexIterator<ReplicationRangeIndexable> => {
|
|
183
|
-
const scaledPoint = Math.round(
|
|
184
|
-
point * (scaled ? 1 : SEGMENT_COORDINATE_SCALE),
|
|
185
|
-
);
|
|
186
98
|
const createQueries = (p: number, equality: boolean) => {
|
|
187
99
|
let queries: Query[];
|
|
188
100
|
if (direction === "below") {
|
|
@@ -215,13 +127,23 @@ const getClosest = (
|
|
|
215
127
|
queries.push(
|
|
216
128
|
new IntegerCompare({ key: "width", compare: Compare.Greater, value: 0 }),
|
|
217
129
|
);
|
|
130
|
+
|
|
131
|
+
if (!includeStrict) {
|
|
132
|
+
queries.push(
|
|
133
|
+
new IntegerCompare({
|
|
134
|
+
key: "mode",
|
|
135
|
+
compare: Compare.Equal,
|
|
136
|
+
value: ReplicationIntent.NonStrict,
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
218
140
|
return queries;
|
|
219
141
|
};
|
|
220
142
|
|
|
221
143
|
const iterator = iterate(
|
|
222
144
|
rects,
|
|
223
145
|
new SearchRequest({
|
|
224
|
-
query: createQueries(
|
|
146
|
+
query: createQueries(point, false),
|
|
225
147
|
sort:
|
|
226
148
|
direction === "below"
|
|
227
149
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
@@ -231,10 +153,7 @@ const getClosest = (
|
|
|
231
153
|
const iteratorWrapped = iterate(
|
|
232
154
|
rects,
|
|
233
155
|
new SearchRequest({
|
|
234
|
-
query: createQueries(
|
|
235
|
-
direction === "below" ? SEGMENT_COORDINATE_SCALE : 0,
|
|
236
|
-
true,
|
|
237
|
-
),
|
|
156
|
+
query: createQueries(direction === "below" ? MAX_U32 : 0, true),
|
|
238
157
|
sort:
|
|
239
158
|
direction === "below"
|
|
240
159
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
@@ -242,11 +161,83 @@ const getClosest = (
|
|
|
242
161
|
}),
|
|
243
162
|
);
|
|
244
163
|
|
|
245
|
-
return joinIterator(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
164
|
+
return joinIterator([iterator, iteratorWrapped], point, direction);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export const hasCoveringRange = async (
|
|
168
|
+
rects: Index<ReplicationRangeIndexable>,
|
|
169
|
+
range: ReplicationRangeIndexable,
|
|
170
|
+
) => {
|
|
171
|
+
return (
|
|
172
|
+
(await rects.count(
|
|
173
|
+
new SearchRequest({
|
|
174
|
+
query: [
|
|
175
|
+
new Or([
|
|
176
|
+
new And([
|
|
177
|
+
new IntegerCompare({
|
|
178
|
+
key: "start1",
|
|
179
|
+
compare: Compare.LessOrEqual,
|
|
180
|
+
value: range.start1,
|
|
181
|
+
}),
|
|
182
|
+
new IntegerCompare({
|
|
183
|
+
key: "end1",
|
|
184
|
+
compare: Compare.GreaterOrEqual,
|
|
185
|
+
value: range.end1,
|
|
186
|
+
}),
|
|
187
|
+
]),
|
|
188
|
+
new And([
|
|
189
|
+
new IntegerCompare({
|
|
190
|
+
key: "start2",
|
|
191
|
+
compare: Compare.LessOrEqual,
|
|
192
|
+
value: range.start1,
|
|
193
|
+
}),
|
|
194
|
+
new IntegerCompare({
|
|
195
|
+
key: "end2",
|
|
196
|
+
compare: Compare.GreaterOrEqual,
|
|
197
|
+
value: range.end1,
|
|
198
|
+
}),
|
|
199
|
+
]),
|
|
200
|
+
]),
|
|
201
|
+
new Or([
|
|
202
|
+
new And([
|
|
203
|
+
new IntegerCompare({
|
|
204
|
+
key: "start1",
|
|
205
|
+
compare: Compare.LessOrEqual,
|
|
206
|
+
value: range.start2,
|
|
207
|
+
}),
|
|
208
|
+
new IntegerCompare({
|
|
209
|
+
key: "end1",
|
|
210
|
+
compare: Compare.GreaterOrEqual,
|
|
211
|
+
value: range.end2,
|
|
212
|
+
}),
|
|
213
|
+
]),
|
|
214
|
+
new And([
|
|
215
|
+
new IntegerCompare({
|
|
216
|
+
key: "start2",
|
|
217
|
+
compare: Compare.LessOrEqual,
|
|
218
|
+
value: range.start2,
|
|
219
|
+
}),
|
|
220
|
+
new IntegerCompare({
|
|
221
|
+
key: "end2",
|
|
222
|
+
compare: Compare.GreaterOrEqual,
|
|
223
|
+
value: range.end2,
|
|
224
|
+
}),
|
|
225
|
+
]),
|
|
226
|
+
]),
|
|
227
|
+
new StringMatch({
|
|
228
|
+
key: "hash",
|
|
229
|
+
value: range.hash,
|
|
230
|
+
}),
|
|
231
|
+
// assume that we are looking for other ranges, not want to update an existing one
|
|
232
|
+
new Not(
|
|
233
|
+
new ByteMatchQuery({
|
|
234
|
+
key: "id",
|
|
235
|
+
value: range.id,
|
|
236
|
+
}),
|
|
237
|
+
),
|
|
238
|
+
],
|
|
239
|
+
}),
|
|
240
|
+
)) > 0
|
|
250
241
|
);
|
|
251
242
|
};
|
|
252
243
|
|
|
@@ -254,7 +245,7 @@ export const getDistance = (
|
|
|
254
245
|
from: number,
|
|
255
246
|
to: number,
|
|
256
247
|
direction: "above" | "below" | "closest",
|
|
257
|
-
end =
|
|
248
|
+
end = MAX_U32,
|
|
258
249
|
) => {
|
|
259
250
|
// if direction is 'above' only measure distance from 'from to 'to' from above.
|
|
260
251
|
// i.e if from < to, then from needs to wrap around 0 to 1 and then to to
|
|
@@ -292,12 +283,8 @@ export const getDistance = (
|
|
|
292
283
|
const joinIterator = (
|
|
293
284
|
iterators: IndexIterator<ReplicationRangeIndexable>[],
|
|
294
285
|
point: number,
|
|
295
|
-
scaled: boolean,
|
|
296
286
|
direction: "above" | "below" | "closest",
|
|
297
287
|
) => {
|
|
298
|
-
const scaledPoint = Math.round(
|
|
299
|
-
point * (scaled ? 1 : SEGMENT_COORDINATE_SCALE),
|
|
300
|
-
);
|
|
301
288
|
let queues: {
|
|
302
289
|
kept: number;
|
|
303
290
|
elements: {
|
|
@@ -331,13 +318,13 @@ const joinIterator = (
|
|
|
331
318
|
let dist: number;
|
|
332
319
|
if (direction === "closest") {
|
|
333
320
|
dist = Math.min(
|
|
334
|
-
getDistance(closest.start1,
|
|
335
|
-
getDistance(closest.end2,
|
|
321
|
+
getDistance(closest.start1, point, direction),
|
|
322
|
+
getDistance(closest.end2, point, direction),
|
|
336
323
|
);
|
|
337
324
|
} else if (direction === "above") {
|
|
338
|
-
dist = getDistance(closest.start1,
|
|
325
|
+
dist = getDistance(closest.start1, point, direction);
|
|
339
326
|
} else if (direction === "below") {
|
|
340
|
-
dist = getDistance(closest.end2,
|
|
327
|
+
dist = getDistance(closest.end2, point, direction);
|
|
341
328
|
} else {
|
|
342
329
|
throw new Error("Invalid direction");
|
|
343
330
|
}
|
|
@@ -401,7 +388,8 @@ const getClosestAround = (
|
|
|
401
388
|
point: number,
|
|
402
389
|
roleAge: number,
|
|
403
390
|
now: number,
|
|
404
|
-
|
|
391
|
+
includeStrictBelow: boolean,
|
|
392
|
+
includeStrictAbove: boolean,
|
|
405
393
|
) => {
|
|
406
394
|
const closestBelow = getClosest(
|
|
407
395
|
"below",
|
|
@@ -410,7 +398,7 @@ const getClosestAround = (
|
|
|
410
398
|
roleAge,
|
|
411
399
|
true,
|
|
412
400
|
now,
|
|
413
|
-
|
|
401
|
+
includeStrictBelow,
|
|
414
402
|
);
|
|
415
403
|
const closestAbove = getClosest(
|
|
416
404
|
"above",
|
|
@@ -419,15 +407,13 @@ const getClosestAround = (
|
|
|
419
407
|
roleAge,
|
|
420
408
|
true,
|
|
421
409
|
now,
|
|
422
|
-
|
|
410
|
+
includeStrictAbove,
|
|
423
411
|
);
|
|
424
|
-
const containing = containingPoint(peers, point, roleAge, true, now
|
|
425
|
-
scaled: scaled,
|
|
426
|
-
});
|
|
412
|
+
const containing = containingPoint(peers, point, roleAge, true, now);
|
|
427
413
|
|
|
428
414
|
return iteratorInSeries(
|
|
429
415
|
containing,
|
|
430
|
-
joinIterator([closestBelow, closestAbove], point,
|
|
416
|
+
joinIterator([closestBelow, closestAbove], point, "closest"),
|
|
431
417
|
);
|
|
432
418
|
};
|
|
433
419
|
|
|
@@ -435,13 +421,11 @@ const collectNodesAroundPoint = async (
|
|
|
435
421
|
roleAge: number,
|
|
436
422
|
peers: Index<ReplicationRangeIndexable>,
|
|
437
423
|
collector: (rect: ReplicationRangeIndexable, matured: boolean) => void,
|
|
438
|
-
point:
|
|
424
|
+
point: u32,
|
|
439
425
|
now: number,
|
|
440
426
|
done: () => boolean = () => true,
|
|
441
427
|
) => {
|
|
442
|
-
const containing = containingPoint(peers, point, 0, true, now
|
|
443
|
-
scaled: false,
|
|
444
|
-
});
|
|
428
|
+
const containing = containingPoint(peers, point, 0, true, now);
|
|
445
429
|
|
|
446
430
|
const allContaining = await containing.next(0xffffffff);
|
|
447
431
|
for (const rect of allContaining.results) {
|
|
@@ -457,7 +441,6 @@ const collectNodesAroundPoint = async (
|
|
|
457
441
|
const aroundIterator = joinIterator(
|
|
458
442
|
[closestBelow, closestAbove],
|
|
459
443
|
point,
|
|
460
|
-
false,
|
|
461
444
|
"closest",
|
|
462
445
|
);
|
|
463
446
|
while (aroundIterator.done() === false && done() === false) {
|
|
@@ -480,13 +463,12 @@ export const isMatured = (
|
|
|
480
463
|
};
|
|
481
464
|
|
|
482
465
|
export const getSamples = async (
|
|
483
|
-
cursor:
|
|
466
|
+
cursor: u32,
|
|
484
467
|
peers: Index<ReplicationRangeIndexable>,
|
|
485
468
|
amount: number,
|
|
486
469
|
roleAge: number,
|
|
487
470
|
) => {
|
|
488
471
|
const leaders: Set<string> = new Set();
|
|
489
|
-
const width = 1;
|
|
490
472
|
if (!peers) {
|
|
491
473
|
return [];
|
|
492
474
|
}
|
|
@@ -504,14 +486,13 @@ export const getSamples = async (
|
|
|
504
486
|
const maturedLeaders = new Set();
|
|
505
487
|
for (let i = 0; i < amount; i++) {
|
|
506
488
|
// evenly distributed
|
|
507
|
-
const point = (
|
|
489
|
+
const point = Math.round(cursor + (i * MAX_U32) / amount) % MAX_U32;
|
|
508
490
|
|
|
509
491
|
// aquire at least one unique node for each point
|
|
510
492
|
await collectNodesAroundPoint(
|
|
511
493
|
roleAge,
|
|
512
494
|
peers,
|
|
513
495
|
(rect, m) => {
|
|
514
|
-
// console.log(m, rect.start1 / SEGMENT_COORDINATE_SCALE, rect.width / SEGMENT_COORDINATE_SCALE)
|
|
515
496
|
if (m) {
|
|
516
497
|
maturedLeaders.add(rect.hash);
|
|
517
498
|
}
|
|
@@ -537,82 +518,50 @@ const fetchOne = async (iterator: IndexIterator<ReplicationRangeIndexable>) => {
|
|
|
537
518
|
return value.results[0]?.value;
|
|
538
519
|
};
|
|
539
520
|
|
|
521
|
+
export const minimumWidthToCover = async (
|
|
522
|
+
minReplicas: number /* , replicatorCount: number */,
|
|
523
|
+
) => {
|
|
524
|
+
/* minReplicas = Math.min(minReplicas, replicatorCount); */ // TODO do we need this?
|
|
525
|
+
|
|
526
|
+
// If min replicas = 2
|
|
527
|
+
// then we need to make sure we cover 0.5 of the total 'width' of the replication space
|
|
528
|
+
// to make sure we reach sufficient amount of nodes such that at least one one has
|
|
529
|
+
// the entry we are looking for
|
|
530
|
+
|
|
531
|
+
let widthToCoverScaled = Math.round(MAX_U32 / minReplicas);
|
|
532
|
+
return widthToCoverScaled;
|
|
533
|
+
};
|
|
534
|
+
|
|
540
535
|
export const getCoverSet = async (
|
|
541
|
-
coveringWidth: number,
|
|
542
536
|
peers: Index<ReplicationRangeIndexable>,
|
|
543
537
|
roleAge: number,
|
|
544
|
-
|
|
538
|
+
start: number | PublicSignKey | undefined,
|
|
539
|
+
widthToCoverScaled: number,
|
|
540
|
+
intervalWidth: number = MAX_U32,
|
|
545
541
|
): Promise<Set<string>> => {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
query: [
|
|
555
|
-
new StringMatch({ key: "hash", value: startNodeIdentity.hashcode() }),
|
|
556
|
-
],
|
|
557
|
-
fetch: 1,
|
|
558
|
-
}),
|
|
559
|
-
);
|
|
560
|
-
startNode = result.results[0]?.value;
|
|
561
|
-
|
|
562
|
-
if (startNode) {
|
|
563
|
-
if (!isMatured(startNode, now, roleAge)) {
|
|
564
|
-
const matured = await fetchOne(
|
|
565
|
-
getClosestAround(peers, startNode.start1, roleAge, now, true),
|
|
566
|
-
);
|
|
567
|
-
if (matured) {
|
|
568
|
-
startNode = matured;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
let startLocation: number;
|
|
574
|
-
|
|
575
|
-
if (!startNode) {
|
|
576
|
-
startLocation = Math.random() * SEGMENT_COORDINATE_SCALE;
|
|
577
|
-
startNode = await fetchOne(
|
|
578
|
-
getClosestAround(peers, startLocation, roleAge, now, true),
|
|
579
|
-
);
|
|
580
|
-
} else {
|
|
581
|
-
// TODO choose start location as the point with the longest range?
|
|
582
|
-
startLocation =
|
|
583
|
-
startNode.start1 ?? Math.random() * SEGMENT_COORDINATE_SCALE;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
if (!startNode) {
|
|
587
|
-
return new Set();
|
|
588
|
-
}
|
|
542
|
+
const { startNode, startLocation, endLocation } = await getStartAndEnd(
|
|
543
|
+
peers,
|
|
544
|
+
start,
|
|
545
|
+
widthToCoverScaled,
|
|
546
|
+
roleAge,
|
|
547
|
+
Date.now(),
|
|
548
|
+
intervalWidth,
|
|
549
|
+
);
|
|
589
550
|
|
|
590
551
|
let results: ReplicationRangeIndexable[] = [];
|
|
591
552
|
|
|
592
|
-
let
|
|
593
|
-
const endLocation =
|
|
594
|
-
(startLocation + widthToCoverScaled) % SEGMENT_COORDINATE_SCALE;
|
|
595
|
-
const endIsWrapped = endLocation <= startLocation;
|
|
553
|
+
let now = +new Date();
|
|
596
554
|
|
|
597
|
-
const
|
|
598
|
-
(await fetchOne(
|
|
599
|
-
getClosestAround(peers, endLocation, roleAge, now, true),
|
|
600
|
-
)) || (await fetchOne(getClosestAround(peers, endLocation, 0, now, true))); // (await getClosest('above', peers, nextLocation, roleAge, true, 1, true))[0]
|
|
555
|
+
const endIsWrapped = endLocation <= startLocation;
|
|
601
556
|
|
|
602
|
-
if (!
|
|
557
|
+
if (!startNode) {
|
|
603
558
|
return new Set();
|
|
604
559
|
}
|
|
605
560
|
|
|
606
|
-
let current =
|
|
607
|
-
/* (await getClosestAround(peers, startLocation, roleAge, 1, true))[0] */ startNode ||
|
|
608
|
-
(await fetchOne(getClosestAround(peers, startLocation, 0, now, true))); //(await getClosest('above', peers, startLocation, roleAge, true, 1, true))[0]
|
|
609
|
-
let coveredLength = current.width;
|
|
610
|
-
let nextLocation = current.end2;
|
|
561
|
+
let current = startNode;
|
|
611
562
|
|
|
612
563
|
// push edges
|
|
613
|
-
results.push(endRect);
|
|
614
564
|
results.push(current);
|
|
615
|
-
/* const endIsSameAsStart = equals(endRect.id, current.id); */
|
|
616
565
|
|
|
617
566
|
const resolveNextContaining = async (
|
|
618
567
|
nextLocation: number,
|
|
@@ -620,7 +569,6 @@ export const getCoverSet = async (
|
|
|
620
569
|
) => {
|
|
621
570
|
let next = await fetchOne(
|
|
622
571
|
containingPoint(peers, nextLocation, roleAge, true, now, {
|
|
623
|
-
scaled: true,
|
|
624
572
|
sort: [new Sort({ key: "end2", direction: SortDirection.DESC })],
|
|
625
573
|
}),
|
|
626
574
|
); // get entersecting sort by largest end2
|
|
@@ -649,13 +597,24 @@ export const getCoverSet = async (
|
|
|
649
597
|
// fill the middle
|
|
650
598
|
let wrappedOnce = current.end2 < current.end1;
|
|
651
599
|
|
|
600
|
+
let coveredLength = 0;
|
|
601
|
+
const addLength = (from: number) => {
|
|
602
|
+
if (current.end2 < from || current.wrapped) {
|
|
603
|
+
wrappedOnce = true;
|
|
604
|
+
coveredLength += MAX_U32 - from;
|
|
605
|
+
coveredLength += current.end2;
|
|
606
|
+
} else {
|
|
607
|
+
coveredLength += current.end1 - from;
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
addLength(startLocation);
|
|
611
|
+
|
|
652
612
|
let maturedCoveredLength = coveredLength;
|
|
653
|
-
|
|
654
|
-
*/
|
|
613
|
+
let nextLocation = current.end2;
|
|
655
614
|
|
|
656
615
|
while (
|
|
657
|
-
maturedCoveredLength < widthToCoverScaled &&
|
|
658
|
-
coveredLength <=
|
|
616
|
+
maturedCoveredLength < widthToCoverScaled && // eslint-disable-line no-unmodified-loop-condition
|
|
617
|
+
coveredLength <= MAX_U32 // eslint-disable-line no-unmodified-loop-condition
|
|
659
618
|
) {
|
|
660
619
|
let nextCandidate = await resolveNext(nextLocation, roleAge);
|
|
661
620
|
/* let fromAbove = false; */
|
|
@@ -671,53 +630,44 @@ export const getCoverSet = async (
|
|
|
671
630
|
break;
|
|
672
631
|
}
|
|
673
632
|
|
|
633
|
+
let nextIsCurrent = equals(nextCandidate[0].id, current.id);
|
|
634
|
+
if (nextIsCurrent) {
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
let last = current;
|
|
674
638
|
current = nextCandidate[0];
|
|
675
639
|
|
|
676
640
|
let distanceBefore = coveredLength;
|
|
677
641
|
|
|
678
|
-
|
|
679
|
-
wrappedOnce = true;
|
|
680
|
-
coveredLength += SEGMENT_COORDINATE_SCALE - nextLocation;
|
|
681
|
-
coveredLength += current.end2;
|
|
682
|
-
} else {
|
|
683
|
-
coveredLength += current.end1 - nextLocation;
|
|
684
|
-
}
|
|
642
|
+
addLength(nextLocation);
|
|
685
643
|
|
|
686
644
|
let isLast =
|
|
687
645
|
distanceBefore < widthToCoverScaled &&
|
|
688
646
|
coveredLength >= widthToCoverScaled;
|
|
647
|
+
|
|
689
648
|
if (
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
649
|
+
!isLast ||
|
|
650
|
+
nextCandidate[1] ||
|
|
651
|
+
Math.min(
|
|
652
|
+
getDistance(last.start1, endLocation, "closest"),
|
|
653
|
+
getDistance(last.end2, endLocation, "closest"),
|
|
654
|
+
) >
|
|
655
|
+
Math.min(
|
|
656
|
+
getDistance(current.start1, endLocation, "closest"),
|
|
657
|
+
getDistance(current.end2, endLocation, "closest"),
|
|
658
|
+
)
|
|
693
659
|
) {
|
|
694
|
-
|
|
695
|
-
// this is the end!
|
|
696
|
-
/* if (lastMatured && lastMatured.distanceTo(endLocation) < current.distanceTo(endLocation)) {
|
|
697
|
-
breaks;
|
|
698
|
-
} */
|
|
699
|
-
break;
|
|
660
|
+
results.push(current);
|
|
700
661
|
}
|
|
701
662
|
|
|
702
|
-
|
|
663
|
+
if (isLast && !nextCandidate[1] /* || equals(endRect.id, current.id) */) {
|
|
703
664
|
break;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// this is a skip condition to not include too many rects
|
|
665
|
+
}
|
|
707
666
|
|
|
708
667
|
if (matured) {
|
|
709
668
|
maturedCoveredLength = coveredLength;
|
|
710
|
-
/* lastMatured = current; */
|
|
711
669
|
}
|
|
712
670
|
|
|
713
|
-
results.push(current);
|
|
714
|
-
/*
|
|
715
|
-
|
|
716
|
-
if (current.start1 > endLocation && (wrappedOnce || !endIsWrapped)) {
|
|
717
|
-
break;
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
*/
|
|
721
671
|
nextLocation = endIsWrapped
|
|
722
672
|
? wrappedOnce
|
|
723
673
|
? Math.min(current.end2, endLocation)
|
|
@@ -726,153 +676,123 @@ export const getCoverSet = async (
|
|
|
726
676
|
}
|
|
727
677
|
|
|
728
678
|
const res = new Set(results.map((x) => x.hash));
|
|
729
|
-
|
|
679
|
+
|
|
680
|
+
start instanceof PublicSignKey && res.add(start.hashcode());
|
|
730
681
|
return res;
|
|
682
|
+
};
|
|
731
683
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
distanceStart = (1 - startPoint + currentNode.segment.offset) % 1;
|
|
753
|
-
} else {
|
|
754
|
-
distanceStart = (currentNode.segment.offset - startPoint) % 1;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
const distanceEnd = distanceStart + currentNode.segment.factor;
|
|
758
|
-
|
|
759
|
-
return [nextPoint, distanceStart, distanceEnd, wrappedOnce];
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
const getNextMatured = async (from: ReplicatorRect) => {
|
|
763
|
-
let next = (await peers.query(new SearchRequest({ query: [new IntegerCompare({ key: ['segment', 'offset'], compare: Compare.Greater, value: from.segment.offset })], fetch: 1 })))?.results[0]?.value // (from.next || peers.head)!;
|
|
764
|
-
while (
|
|
765
|
-
next.hash !== from.hash &&
|
|
766
|
-
next.hash !== startNode.hash
|
|
767
|
-
) {
|
|
768
|
-
if (isMatured(next.segment, t, roleAge)) {
|
|
769
|
-
return next;
|
|
770
|
-
}
|
|
771
|
-
next = (next.next || peers.head)!;
|
|
772
|
-
}
|
|
773
|
-
return undefined;
|
|
774
|
-
}; */
|
|
775
|
-
|
|
776
|
-
/**
|
|
777
|
-
* The purpose of this loop is to cover at least coveringWidth
|
|
778
|
-
* so that if we query all nodes in this range, we know we will
|
|
779
|
-
* "query" all data in that range
|
|
780
|
-
*/
|
|
781
|
-
|
|
782
|
-
/* let isPastThePoint = false;
|
|
783
|
-
outer: while (currentNode) {
|
|
784
|
-
if (set.has(currentNode.hash)) break;
|
|
785
|
-
|
|
786
|
-
const [nextPoint, distanceStart, distanceEnd, wrapped] = getNextPoint();
|
|
787
|
-
|
|
788
|
-
if (distanceStart <= coveringWidth) {
|
|
789
|
-
set.add(currentNode.hash);
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
if (distanceEnd >= coveringWidth) {
|
|
793
|
-
break;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
let next = currentNode.next || peers.head;
|
|
797
|
-
while (next) {
|
|
798
|
-
if (next.value.publicKey.equals(startNode.value.publicKey)) {
|
|
799
|
-
break outer;
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
const prevOffset = (next.prev || peers.tail)!.value.role.offset;
|
|
803
|
-
const nextOffset = next.value.role.offset;
|
|
804
|
-
const nextHasWrapped = nextOffset < prevOffset;
|
|
805
|
-
|
|
806
|
-
if (
|
|
807
|
-
(!wrapped && nextOffset > nextPoint) ||
|
|
808
|
-
(nextHasWrapped &&
|
|
809
|
-
(wrapped ? nextOffset > nextPoint : prevOffset < nextPoint)) ||
|
|
810
|
-
(!nextHasWrapped && prevOffset < nextPoint && nextPoint <= nextOffset)
|
|
811
|
-
) {
|
|
812
|
-
isPastThePoint = true;
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
if (isPastThePoint) {
|
|
816
|
-
break; // include this next in the set;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
const overlapsRange = containsPoint(next.value.role, nextPoint);
|
|
820
|
-
|
|
821
|
-
if (overlapsRange) {
|
|
822
|
-
// Find out if there is a better choice ahead of us
|
|
823
|
-
const nextNext = await getNextMatured(next);
|
|
824
|
-
if (
|
|
825
|
-
nextNext &&
|
|
826
|
-
nextNext.hash === currentNode.hash &&
|
|
827
|
-
nextNext.segment.offset < nextPoint &&
|
|
828
|
-
nextNext.segment.offset + nextNext.segment.factor > nextPoint
|
|
829
|
-
) {
|
|
830
|
-
// nextNext is better (continue to iterate)
|
|
831
|
-
} else {
|
|
832
|
-
// done
|
|
833
|
-
break;
|
|
834
|
-
}
|
|
835
|
-
} else {
|
|
836
|
-
// (continue to iterate)
|
|
684
|
+
export const fetchOneFromPublicKey = async (
|
|
685
|
+
publicKey: PublicSignKey,
|
|
686
|
+
index: Index<ReplicationRangeIndexable>,
|
|
687
|
+
roleAge: number,
|
|
688
|
+
now: number,
|
|
689
|
+
) => {
|
|
690
|
+
let result = await index.query(
|
|
691
|
+
new SearchRequest({
|
|
692
|
+
query: [new StringMatch({ key: "hash", value: publicKey.hashcode() })],
|
|
693
|
+
fetch: 1,
|
|
694
|
+
}),
|
|
695
|
+
);
|
|
696
|
+
let node = result.results[0]?.value;
|
|
697
|
+
if (node) {
|
|
698
|
+
if (!isMatured(node, now, roleAge)) {
|
|
699
|
+
const matured = await fetchOne(
|
|
700
|
+
getClosestAround(index, node.start1, roleAge, now, false, false),
|
|
701
|
+
);
|
|
702
|
+
if (matured) {
|
|
703
|
+
node = matured;
|
|
837
704
|
}
|
|
838
|
-
|
|
839
|
-
next = next.next || peers.head;
|
|
840
705
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
706
|
+
}
|
|
707
|
+
return node;
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
export const getStartAndEnd = async (
|
|
711
|
+
peers: Index<ReplicationRangeIndexable>,
|
|
712
|
+
start: number | PublicSignKey | undefined | undefined,
|
|
713
|
+
widthToCoverScaled: number,
|
|
714
|
+
roleAge: number,
|
|
715
|
+
now: number,
|
|
716
|
+
intervalWidth: number,
|
|
717
|
+
) => {
|
|
718
|
+
// find a good starting point
|
|
719
|
+
let startNode: ReplicationRangeIndexable | undefined = undefined;
|
|
720
|
+
let startLocation: number | undefined = undefined;
|
|
721
|
+
|
|
722
|
+
const nodeFromPoint = async (point = scaleToU32(Math.random())) => {
|
|
723
|
+
startLocation = point;
|
|
724
|
+
startNode = await fetchOneClosest(
|
|
855
725
|
peers,
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
} else {
|
|
862
|
-
set.add(rect.hash);
|
|
863
|
-
}
|
|
864
|
-
done = true;
|
|
865
|
-
} else {
|
|
866
|
-
unmatured.push(rect.hash);
|
|
867
|
-
}
|
|
868
|
-
},
|
|
869
|
-
point,
|
|
870
|
-
() => done
|
|
726
|
+
startLocation,
|
|
727
|
+
roleAge,
|
|
728
|
+
now,
|
|
729
|
+
false,
|
|
730
|
+
true,
|
|
871
731
|
);
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
if (start instanceof PublicSignKey) {
|
|
735
|
+
// start at our node (local first)
|
|
736
|
+
startNode = await fetchOneFromPublicKey(start, peers, roleAge, now);
|
|
737
|
+
if (!startNode) {
|
|
738
|
+
// fetch randomly
|
|
739
|
+
await nodeFromPoint();
|
|
740
|
+
} else {
|
|
741
|
+
startLocation = startNode.start1;
|
|
875
742
|
}
|
|
743
|
+
} else if (typeof start === "number") {
|
|
744
|
+
await nodeFromPoint(start);
|
|
745
|
+
} else {
|
|
746
|
+
await nodeFromPoint();
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (!startNode || startLocation == null) {
|
|
750
|
+
return { startNode: undefined, startLocation: 0, endLocation: 0 };
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
let endLocation = startLocation + widthToCoverScaled;
|
|
754
|
+
if (intervalWidth != null) {
|
|
755
|
+
endLocation = endLocation % intervalWidth;
|
|
876
756
|
}
|
|
877
|
-
|
|
757
|
+
|
|
758
|
+
// if start location is after endLocation and startNode is strict then return undefined because this is not a node we want to choose
|
|
759
|
+
let coveredDistanceToStart = 0;
|
|
760
|
+
if (startNode.start1 < startLocation) {
|
|
761
|
+
coveredDistanceToStart += intervalWidth - startLocation + startNode.start1;
|
|
762
|
+
} else {
|
|
763
|
+
coveredDistanceToStart += startNode.start1 - startLocation;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (
|
|
767
|
+
startNode.mode === ReplicationIntent.Strict &&
|
|
768
|
+
coveredDistanceToStart > widthToCoverScaled
|
|
769
|
+
) {
|
|
770
|
+
return { startNode: undefined, startLocation: 0, endLocation: 0 };
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return {
|
|
774
|
+
startNode,
|
|
775
|
+
startLocation: Math.round(startLocation),
|
|
776
|
+
endLocation: Math.round(endLocation),
|
|
777
|
+
};
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
export const fetchOneClosest = (
|
|
781
|
+
peers: Index<ReplicationRangeIndexable>,
|
|
782
|
+
point: number,
|
|
783
|
+
roleAge: number,
|
|
784
|
+
now: number,
|
|
785
|
+
includeStrictBelow: boolean,
|
|
786
|
+
includeStrictAbove: boolean,
|
|
787
|
+
) => {
|
|
788
|
+
return fetchOne(
|
|
789
|
+
getClosestAround(
|
|
790
|
+
peers,
|
|
791
|
+
point,
|
|
792
|
+
roleAge,
|
|
793
|
+
now,
|
|
794
|
+
includeStrictBelow,
|
|
795
|
+
includeStrictAbove,
|
|
796
|
+
),
|
|
797
|
+
);
|
|
878
798
|
};
|