@peerbit/shared-log 9.0.10 → 9.1.0-57b8640
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 +51 -31
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +435 -230
- 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 +22 -3
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +231 -336
- 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 +621 -312
- package/src/pid.ts +20 -19
- package/src/ranges.ts +328 -375
- 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/dist/src/ranges.js
CHANGED
|
@@ -1,116 +1,33 @@
|
|
|
1
|
-
import { equals } from "@peerbit/crypto";
|
|
2
|
-
import { And, Compare, IntegerCompare, Or, SearchRequest, Sort, SortDirection, StringMatch, iterate, iteratorInSeries, } from "@peerbit/indexer-interface";
|
|
3
|
-
import {} from "./replication.js";
|
|
4
|
-
import {
|
|
5
|
-
/*
|
|
6
|
-
export const containsPoint = (
|
|
7
|
-
rect: { offset: number; length: number },
|
|
8
|
-
point: number,
|
|
9
|
-
eps = 0.00001 // we do this to handle numerical errors
|
|
10
|
-
) => {
|
|
11
|
-
if (rect.factor === 0) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
const start = rect.offset;
|
|
15
|
-
const width = rect.factor + eps; // we do this to handle numerical errors. It is better to be more inclusive
|
|
16
|
-
const endUnwrapped = rect.offset + width;
|
|
17
|
-
let end = endUnwrapped;
|
|
18
|
-
let wrapped = false;
|
|
19
|
-
if (endUnwrapped > 1) {
|
|
20
|
-
end = endUnwrapped % 1;
|
|
21
|
-
wrapped = true;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const inFirstInterval = point >= start && point < Math.min(endUnwrapped, 1);
|
|
25
|
-
const inSecondInterval =
|
|
26
|
-
!inFirstInterval && wrapped && point >= 0 && point < end;
|
|
27
|
-
|
|
28
|
-
return inFirstInterval || inSecondInterval;
|
|
29
|
-
}; */
|
|
30
|
-
/* const resolveRectsThatContainPoint = async (
|
|
31
|
-
rects: Index<ReplicationRangeIndexable>,
|
|
32
|
-
point: number,
|
|
33
|
-
roleAgeLimit: number,
|
|
34
|
-
matured: boolean
|
|
35
|
-
): Promise<ReplicationRangeIndexable[]> => {
|
|
36
|
-
// 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
|
|
37
|
-
// so we need to query for all ranges that contain the point
|
|
38
|
-
const scaledPoint = Math.round(point * SEGMENT_COORDINATE_SCALE)
|
|
39
|
-
let queries = [
|
|
40
|
-
new IntegerCompare({ key: 'start', compare: Compare.LessOrEqual, value: scaledPoint }),
|
|
41
|
-
new IntegerCompare({ key: 'end', compare: Compare.Greater, value: scaledPoint }),
|
|
42
|
-
new IntegerCompare({ key: 'timestamp', compare: matured ? Compare.LessOrEqual : Compare.Greater, value: Date.now() - roleAgeLimit })
|
|
43
|
-
]
|
|
44
|
-
|
|
45
|
-
const results = await rects.query(new SearchRequest({
|
|
46
|
-
query: [
|
|
47
|
-
new Nested({
|
|
48
|
-
path: 'segments',
|
|
49
|
-
query: queries
|
|
50
|
-
})
|
|
51
|
-
]
|
|
52
|
-
}))
|
|
53
|
-
return results.results.map(x => x.value)
|
|
54
|
-
} */
|
|
55
|
-
/* const resolveRectsInRange = async (rects: Index<ReplicationRangeIndexable>,
|
|
56
|
-
start: number,
|
|
57
|
-
end: number,
|
|
58
|
-
roleAgeLimit: number,
|
|
59
|
-
matured: boolean
|
|
60
|
-
): Promise<ReplicationRangeIndexable[]> => {
|
|
61
|
-
// 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
|
|
62
|
-
// so we need to query for all ranges that contain the point
|
|
63
|
-
let endScaled = Math.round(end * SEGMENT_COORDINATE_SCALE);
|
|
64
|
-
let startScaled = Math.round(start * SEGMENT_COORDINATE_SCALE);
|
|
65
|
-
let queries = [
|
|
66
|
-
new Or([
|
|
67
|
-
new And([
|
|
68
|
-
new IntegerCompare({ key: 'start1', compare: Compare.Less, value: endScaled }),
|
|
69
|
-
new IntegerCompare({ key: 'end1', compare: Compare.GreaterOrEqual, value: startScaled }),
|
|
70
|
-
]),
|
|
71
|
-
new And([
|
|
72
|
-
new IntegerCompare({ key: 'start2', compare: Compare.Less, value: endScaled }),
|
|
73
|
-
new IntegerCompare({ key: 'end2', compare: Compare.GreaterOrEqual, value: startScaled }),
|
|
74
|
-
])
|
|
75
|
-
]),
|
|
76
|
-
new IntegerCompare({ key: 'timestamp', compare: matured ? Compare.LessOrEqual : Compare.Greater, value: BigInt(+new Date - roleAgeLimit) })
|
|
77
|
-
]
|
|
78
|
-
|
|
79
|
-
const results = await rects.query(new SearchRequest({
|
|
80
|
-
query: queries,
|
|
81
|
-
sort: [new Sort({ key: "start1" }), new Sort({ key: "start2" })],
|
|
82
|
-
fetch: 0xffffffff
|
|
83
|
-
}))
|
|
84
|
-
return results.results.map(x => x.value)
|
|
85
|
-
} */
|
|
1
|
+
import { PublicSignKey, equals } from "@peerbit/crypto";
|
|
2
|
+
import { And, ByteMatchQuery, Compare, IntegerCompare, Not, Or, SearchRequest, Sort, SortDirection, StringMatch, iterate, iteratorInSeries, } from "@peerbit/indexer-interface";
|
|
3
|
+
import { ReplicationIntent, } from "./replication.js";
|
|
4
|
+
import { MAX_U32, scaleToU32 } from "./role.js";
|
|
86
5
|
const containingPoint = (rects, point, roleAgeLimit, matured, now, options) => {
|
|
87
6
|
// 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
|
|
88
|
-
// so we need to query for all ranges that contain the point
|
|
89
|
-
let pointScaled = Math.round(point * (options?.scaled ? 1 : SEGMENT_COORDINATE_SCALE));
|
|
90
7
|
let queries = [
|
|
91
8
|
new Or([
|
|
92
9
|
new And([
|
|
93
10
|
new IntegerCompare({
|
|
94
11
|
key: "start1",
|
|
95
12
|
compare: Compare.LessOrEqual,
|
|
96
|
-
value:
|
|
13
|
+
value: point,
|
|
97
14
|
}),
|
|
98
15
|
new IntegerCompare({
|
|
99
16
|
key: "end1",
|
|
100
17
|
compare: Compare.Greater,
|
|
101
|
-
value:
|
|
18
|
+
value: point,
|
|
102
19
|
}),
|
|
103
20
|
]),
|
|
104
21
|
new And([
|
|
105
22
|
new IntegerCompare({
|
|
106
23
|
key: "start2",
|
|
107
24
|
compare: Compare.LessOrEqual,
|
|
108
|
-
value:
|
|
25
|
+
value: point,
|
|
109
26
|
}),
|
|
110
27
|
new IntegerCompare({
|
|
111
28
|
key: "end2",
|
|
112
29
|
compare: Compare.Greater,
|
|
113
|
-
value:
|
|
30
|
+
value: point,
|
|
114
31
|
}),
|
|
115
32
|
]),
|
|
116
33
|
]),
|
|
@@ -132,8 +49,7 @@ const containingPoint = (rects, point, roleAgeLimit, matured, now, options) => {
|
|
|
132
49
|
}))
|
|
133
50
|
return results.results.map(x => x.value) */
|
|
134
51
|
};
|
|
135
|
-
const getClosest = (direction, rects, point, roleAgeLimit, matured, now,
|
|
136
|
-
const scaledPoint = Math.round(point * (scaled ? 1 : SEGMENT_COORDINATE_SCALE));
|
|
52
|
+
const getClosest = (direction, rects, point, roleAgeLimit, matured, now, includeStrict) => {
|
|
137
53
|
const createQueries = (p, equality) => {
|
|
138
54
|
let queries;
|
|
139
55
|
if (direction === "below") {
|
|
@@ -165,23 +81,97 @@ const getClosest = (direction, rects, point, roleAgeLimit, matured, now, scaled
|
|
|
165
81
|
];
|
|
166
82
|
}
|
|
167
83
|
queries.push(new IntegerCompare({ key: "width", compare: Compare.Greater, value: 0 }));
|
|
84
|
+
if (!includeStrict) {
|
|
85
|
+
queries.push(new IntegerCompare({
|
|
86
|
+
key: "mode",
|
|
87
|
+
compare: Compare.Equal,
|
|
88
|
+
value: ReplicationIntent.NonStrict,
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
168
91
|
return queries;
|
|
169
92
|
};
|
|
170
93
|
const iterator = iterate(rects, new SearchRequest({
|
|
171
|
-
query: createQueries(
|
|
94
|
+
query: createQueries(point, false),
|
|
172
95
|
sort: direction === "below"
|
|
173
96
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
174
97
|
: new Sort({ key: ["start1"], direction: "asc" }),
|
|
175
98
|
}));
|
|
176
99
|
const iteratorWrapped = iterate(rects, new SearchRequest({
|
|
177
|
-
query: createQueries(direction === "below" ?
|
|
100
|
+
query: createQueries(direction === "below" ? MAX_U32 : 0, true),
|
|
178
101
|
sort: direction === "below"
|
|
179
102
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
180
103
|
: new Sort({ key: ["start1"], direction: "asc" }),
|
|
181
104
|
}));
|
|
182
|
-
return joinIterator([iterator, iteratorWrapped],
|
|
105
|
+
return joinIterator([iterator, iteratorWrapped], point, direction);
|
|
183
106
|
};
|
|
184
|
-
export const
|
|
107
|
+
export const hasCoveringRange = async (rects, range) => {
|
|
108
|
+
return ((await rects.count(new SearchRequest({
|
|
109
|
+
query: [
|
|
110
|
+
new Or([
|
|
111
|
+
new And([
|
|
112
|
+
new IntegerCompare({
|
|
113
|
+
key: "start1",
|
|
114
|
+
compare: Compare.LessOrEqual,
|
|
115
|
+
value: range.start1,
|
|
116
|
+
}),
|
|
117
|
+
new IntegerCompare({
|
|
118
|
+
key: "end1",
|
|
119
|
+
compare: Compare.GreaterOrEqual,
|
|
120
|
+
value: range.end1,
|
|
121
|
+
}),
|
|
122
|
+
]),
|
|
123
|
+
new And([
|
|
124
|
+
new IntegerCompare({
|
|
125
|
+
key: "start2",
|
|
126
|
+
compare: Compare.LessOrEqual,
|
|
127
|
+
value: range.start1,
|
|
128
|
+
}),
|
|
129
|
+
new IntegerCompare({
|
|
130
|
+
key: "end2",
|
|
131
|
+
compare: Compare.GreaterOrEqual,
|
|
132
|
+
value: range.end1,
|
|
133
|
+
}),
|
|
134
|
+
]),
|
|
135
|
+
]),
|
|
136
|
+
new Or([
|
|
137
|
+
new And([
|
|
138
|
+
new IntegerCompare({
|
|
139
|
+
key: "start1",
|
|
140
|
+
compare: Compare.LessOrEqual,
|
|
141
|
+
value: range.start2,
|
|
142
|
+
}),
|
|
143
|
+
new IntegerCompare({
|
|
144
|
+
key: "end1",
|
|
145
|
+
compare: Compare.GreaterOrEqual,
|
|
146
|
+
value: range.end2,
|
|
147
|
+
}),
|
|
148
|
+
]),
|
|
149
|
+
new And([
|
|
150
|
+
new IntegerCompare({
|
|
151
|
+
key: "start2",
|
|
152
|
+
compare: Compare.LessOrEqual,
|
|
153
|
+
value: range.start2,
|
|
154
|
+
}),
|
|
155
|
+
new IntegerCompare({
|
|
156
|
+
key: "end2",
|
|
157
|
+
compare: Compare.GreaterOrEqual,
|
|
158
|
+
value: range.end2,
|
|
159
|
+
}),
|
|
160
|
+
]),
|
|
161
|
+
]),
|
|
162
|
+
new StringMatch({
|
|
163
|
+
key: "hash",
|
|
164
|
+
value: range.hash,
|
|
165
|
+
}),
|
|
166
|
+
// assume that we are looking for other ranges, not want to update an existing one
|
|
167
|
+
new Not(new ByteMatchQuery({
|
|
168
|
+
key: "id",
|
|
169
|
+
value: range.id,
|
|
170
|
+
})),
|
|
171
|
+
],
|
|
172
|
+
}))) > 0);
|
|
173
|
+
};
|
|
174
|
+
export const getDistance = (from, to, direction, end = MAX_U32) => {
|
|
185
175
|
// if direction is 'above' only measure distance from 'from to 'to' from above.
|
|
186
176
|
// i.e if from < to, then from needs to wrap around 0 to 1 and then to to
|
|
187
177
|
// if direction is 'below' and from > to, then from needs to wrap around 1 to 0 and then to to
|
|
@@ -208,8 +198,7 @@ export const getDistance = (from, to, direction, end = SEGMENT_COORDINATE_SCALE)
|
|
|
208
198
|
}
|
|
209
199
|
throw new Error("Invalid direction");
|
|
210
200
|
};
|
|
211
|
-
const joinIterator = (iterators, point,
|
|
212
|
-
const scaledPoint = Math.round(point * (scaled ? 1 : SEGMENT_COORDINATE_SCALE));
|
|
201
|
+
const joinIterator = (iterators, point, direction) => {
|
|
213
202
|
let queues = [];
|
|
214
203
|
return {
|
|
215
204
|
next: async (count) => {
|
|
@@ -231,13 +220,13 @@ const joinIterator = (iterators, point, scaled, direction) => {
|
|
|
231
220
|
const closest = el.value;
|
|
232
221
|
let dist;
|
|
233
222
|
if (direction === "closest") {
|
|
234
|
-
dist = Math.min(getDistance(closest.start1,
|
|
223
|
+
dist = Math.min(getDistance(closest.start1, point, direction), getDistance(closest.end2, point, direction));
|
|
235
224
|
}
|
|
236
225
|
else if (direction === "above") {
|
|
237
|
-
dist = getDistance(closest.start1,
|
|
226
|
+
dist = getDistance(closest.start1, point, direction);
|
|
238
227
|
}
|
|
239
228
|
else if (direction === "below") {
|
|
240
|
-
dist = getDistance(closest.end2,
|
|
229
|
+
dist = getDistance(closest.end2, point, direction);
|
|
241
230
|
}
|
|
242
231
|
else {
|
|
243
232
|
throw new Error("Invalid direction");
|
|
@@ -289,18 +278,14 @@ const joinIterator = (iterators, point, scaled, direction) => {
|
|
|
289
278
|
},
|
|
290
279
|
};
|
|
291
280
|
};
|
|
292
|
-
const getClosestAround = (peers, point, roleAge, now,
|
|
293
|
-
const closestBelow = getClosest("below", peers, point, roleAge, true, now,
|
|
294
|
-
const closestAbove = getClosest("above", peers, point, roleAge, true, now,
|
|
295
|
-
const containing = containingPoint(peers, point, roleAge, true, now
|
|
296
|
-
|
|
297
|
-
});
|
|
298
|
-
return iteratorInSeries(containing, joinIterator([closestBelow, closestAbove], point, scaled, "closest"));
|
|
281
|
+
const getClosestAround = (peers, point, roleAge, now, includeStrictBelow, includeStrictAbove) => {
|
|
282
|
+
const closestBelow = getClosest("below", peers, point, roleAge, true, now, includeStrictBelow);
|
|
283
|
+
const closestAbove = getClosest("above", peers, point, roleAge, true, now, includeStrictAbove);
|
|
284
|
+
const containing = containingPoint(peers, point, roleAge, true, now);
|
|
285
|
+
return iteratorInSeries(containing, joinIterator([closestBelow, closestAbove], point, "closest"));
|
|
299
286
|
};
|
|
300
287
|
const collectNodesAroundPoint = async (roleAge, peers, collector, point, now, done = () => true) => {
|
|
301
|
-
const containing = containingPoint(peers, point, 0, true, now
|
|
302
|
-
scaled: false,
|
|
303
|
-
});
|
|
288
|
+
const containing = containingPoint(peers, point, 0, true, now);
|
|
304
289
|
const allContaining = await containing.next(0xffffffff);
|
|
305
290
|
for (const rect of allContaining.results) {
|
|
306
291
|
collector(rect.value, isMatured(rect.value, now, roleAge));
|
|
@@ -310,7 +295,7 @@ const collectNodesAroundPoint = async (roleAge, peers, collector, point, now, do
|
|
|
310
295
|
}
|
|
311
296
|
const closestBelow = getClosest("below", peers, point, 0, true, now, false);
|
|
312
297
|
const closestAbove = getClosest("above", peers, point, 0, true, now, false);
|
|
313
|
-
const aroundIterator = joinIterator([closestBelow, closestAbove], point,
|
|
298
|
+
const aroundIterator = joinIterator([closestBelow, closestAbove], point, "closest");
|
|
314
299
|
while (aroundIterator.done() === false && done() === false) {
|
|
315
300
|
const res = await aroundIterator.next(1);
|
|
316
301
|
for (const rect of res.results) {
|
|
@@ -326,7 +311,6 @@ export const isMatured = (segment, now, minAge) => {
|
|
|
326
311
|
};
|
|
327
312
|
export const getSamples = async (cursor, peers, amount, roleAge) => {
|
|
328
313
|
const leaders = new Set();
|
|
329
|
-
const width = 1;
|
|
330
314
|
if (!peers) {
|
|
331
315
|
return [];
|
|
332
316
|
}
|
|
@@ -339,10 +323,9 @@ export const getSamples = async (cursor, peers, amount, roleAge) => {
|
|
|
339
323
|
const maturedLeaders = new Set();
|
|
340
324
|
for (let i = 0; i < amount; i++) {
|
|
341
325
|
// evenly distributed
|
|
342
|
-
const point = (
|
|
326
|
+
const point = Math.round(cursor + (i * MAX_U32) / amount) % MAX_U32;
|
|
343
327
|
// aquire at least one unique node for each point
|
|
344
328
|
await collectNodesAroundPoint(roleAge, peers, (rect, m) => {
|
|
345
|
-
// console.log(m, rect.start1 / SEGMENT_COORDINATE_SCALE, rect.width / SEGMENT_COORDINATE_SCALE)
|
|
346
329
|
if (m) {
|
|
347
330
|
maturedLeaders.add(rect.hash);
|
|
348
331
|
}
|
|
@@ -361,61 +344,52 @@ const fetchOne = async (iterator) => {
|
|
|
361
344
|
await iterator.close();
|
|
362
345
|
return value.results[0]?.value;
|
|
363
346
|
};
|
|
364
|
-
export const
|
|
365
|
-
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
347
|
+
export const minimumWidthToCover = async (minReplicas /* , replicatorCount: number */) => {
|
|
348
|
+
/* minReplicas = Math.min(minReplicas, replicatorCount); */ // TODO do we need this?
|
|
349
|
+
// If min replicas = 2
|
|
350
|
+
// then we need to make sure we cover 0.5 of the total 'width' of the replication space
|
|
351
|
+
// to make sure we reach sufficient amount of nodes such that at least one one has
|
|
352
|
+
// the entry we are looking for
|
|
353
|
+
let widthToCoverScaled = Math.round(MAX_U32 / minReplicas);
|
|
354
|
+
return widthToCoverScaled;
|
|
355
|
+
};
|
|
356
|
+
export const getCoverSet = async (properties) => {
|
|
357
|
+
let intervalWidth = properties.intervalWidth ?? MAX_U32;
|
|
358
|
+
const { peers, start, widthToCoverScaled, roleAge } = properties;
|
|
359
|
+
const now = Date.now();
|
|
360
|
+
const { startNode, startLocation, endLocation } = await getStartAndEnd(peers, start, widthToCoverScaled, roleAge, now, intervalWidth);
|
|
361
|
+
let ret = new Set();
|
|
362
|
+
// if start node (assume is self) and not mature, ask all known remotes if limited
|
|
363
|
+
// TODO consider a more robust stragety here in a scenario where there are many nodes, lets say
|
|
364
|
+
// a social media app with 1m user, then it does not makes sense to query "all" just because we started
|
|
365
|
+
if (properties.eager) {
|
|
366
|
+
const eagerFetch = properties.eager === true
|
|
367
|
+
? 1000
|
|
368
|
+
: properties.eager.unmaturedFetchCoverSize;
|
|
369
|
+
// pull all umatured
|
|
370
|
+
const rects = await peers.query(new SearchRequest({
|
|
371
|
+
fetch: eagerFetch,
|
|
371
372
|
query: [
|
|
372
|
-
new
|
|
373
|
+
new IntegerCompare({
|
|
374
|
+
key: "timestamp",
|
|
375
|
+
compare: Compare.GreaterOrEqual,
|
|
376
|
+
value: BigInt(now - roleAge),
|
|
377
|
+
}),
|
|
373
378
|
],
|
|
374
|
-
fetch: 1,
|
|
375
379
|
}));
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (!isMatured(startNode, now, roleAge)) {
|
|
379
|
-
const matured = await fetchOne(getClosestAround(peers, startNode.start1, roleAge, now, true));
|
|
380
|
-
if (matured) {
|
|
381
|
-
startNode = matured;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
380
|
+
for (const rect of rects.results) {
|
|
381
|
+
ret.add(rect.value.hash);
|
|
384
382
|
}
|
|
385
383
|
}
|
|
386
|
-
let startLocation;
|
|
387
|
-
if (!startNode) {
|
|
388
|
-
startLocation = Math.random() * SEGMENT_COORDINATE_SCALE;
|
|
389
|
-
startNode = await fetchOne(getClosestAround(peers, startLocation, roleAge, now, true));
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
// TODO choose start location as the point with the longest range?
|
|
393
|
-
startLocation =
|
|
394
|
-
startNode.start1 ?? Math.random() * SEGMENT_COORDINATE_SCALE;
|
|
395
|
-
}
|
|
396
|
-
if (!startNode) {
|
|
397
|
-
return new Set();
|
|
398
|
-
}
|
|
399
|
-
let results = [];
|
|
400
|
-
let widthToCoverScaled = coveringWidth * SEGMENT_COORDINATE_SCALE;
|
|
401
|
-
const endLocation = (startLocation + widthToCoverScaled) % SEGMENT_COORDINATE_SCALE;
|
|
402
384
|
const endIsWrapped = endLocation <= startLocation;
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
return new Set();
|
|
385
|
+
if (!startNode) {
|
|
386
|
+
return ret;
|
|
406
387
|
}
|
|
407
|
-
let current =
|
|
408
|
-
/* (await getClosestAround(peers, startLocation, roleAge, 1, true))[0] */ startNode ||
|
|
409
|
-
(await fetchOne(getClosestAround(peers, startLocation, 0, now, true))); //(await getClosest('above', peers, startLocation, roleAge, true, 1, true))[0]
|
|
410
|
-
let coveredLength = current.width;
|
|
411
|
-
let nextLocation = current.end2;
|
|
388
|
+
let current = startNode;
|
|
412
389
|
// push edges
|
|
413
|
-
|
|
414
|
-
results.push(current);
|
|
415
|
-
/* const endIsSameAsStart = equals(endRect.id, current.id); */
|
|
390
|
+
ret.add(current.hash);
|
|
416
391
|
const resolveNextContaining = async (nextLocation, roleAge) => {
|
|
417
392
|
let next = await fetchOne(containingPoint(peers, nextLocation, roleAge, true, now, {
|
|
418
|
-
scaled: true,
|
|
419
393
|
sort: [new Sort({ key: "end2", direction: SortDirection.DESC })],
|
|
420
394
|
})); // get entersecting sort by largest end2
|
|
421
395
|
return next;
|
|
@@ -434,11 +408,23 @@ export const getCoverSet = async (coveringWidth, peers, roleAge, startNodeIdenti
|
|
|
434
408
|
};
|
|
435
409
|
// fill the middle
|
|
436
410
|
let wrappedOnce = current.end2 < current.end1;
|
|
437
|
-
let
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
411
|
+
let coveredLength = 0;
|
|
412
|
+
const addLength = (from) => {
|
|
413
|
+
if (current.end2 < from || current.wrapped) {
|
|
414
|
+
wrappedOnce = true;
|
|
415
|
+
coveredLength += MAX_U32 - from;
|
|
416
|
+
coveredLength += current.end2;
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
coveredLength += current.end1 - from;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
addLength(startLocation);
|
|
423
|
+
let maturedCoveredLength = coveredLength; /* TODO only increase matured length when startNode is matured? i.e. do isMatured(startNode, now, roleAge) ? coveredLength : 0; */
|
|
424
|
+
let nextLocation = current.end2;
|
|
425
|
+
while (maturedCoveredLength < widthToCoverScaled && // eslint-disable-line no-unmodified-loop-condition
|
|
426
|
+
coveredLength <= MAX_U32 // eslint-disable-line no-unmodified-loop-condition
|
|
427
|
+
) {
|
|
442
428
|
let nextCandidate = await resolveNext(nextLocation, roleAge);
|
|
443
429
|
/* let fromAbove = false; */
|
|
444
430
|
let matured = true;
|
|
@@ -450,195 +436,104 @@ export const getCoverSet = async (coveringWidth, peers, roleAge, startNodeIdenti
|
|
|
450
436
|
if (!nextCandidate[0]) {
|
|
451
437
|
break;
|
|
452
438
|
}
|
|
439
|
+
let nextIsCurrent = equals(nextCandidate[0].id, current.id);
|
|
440
|
+
if (nextIsCurrent) {
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
let last = current;
|
|
453
444
|
current = nextCandidate[0];
|
|
454
445
|
let distanceBefore = coveredLength;
|
|
455
|
-
|
|
456
|
-
wrappedOnce = true;
|
|
457
|
-
coveredLength += SEGMENT_COORDINATE_SCALE - nextLocation;
|
|
458
|
-
coveredLength += current.end2;
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
coveredLength += current.end1 - nextLocation;
|
|
462
|
-
}
|
|
446
|
+
addLength(nextLocation);
|
|
463
447
|
let isLast = distanceBefore < widthToCoverScaled &&
|
|
464
448
|
coveredLength >= widthToCoverScaled;
|
|
465
|
-
if (
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
/* if (lastMatured && lastMatured.distanceTo(endLocation) < current.distanceTo(endLocation)) {
|
|
471
|
-
breaks;
|
|
472
|
-
} */
|
|
473
|
-
break;
|
|
449
|
+
if (!isLast ||
|
|
450
|
+
nextCandidate[1] ||
|
|
451
|
+
Math.min(getDistance(last.start1, endLocation, "closest"), getDistance(last.end2, endLocation, "closest")) >
|
|
452
|
+
Math.min(getDistance(current.start1, endLocation, "closest"), getDistance(current.end2, endLocation, "closest"))) {
|
|
453
|
+
ret.add(current.hash);
|
|
474
454
|
}
|
|
475
|
-
|
|
455
|
+
if (isLast && !nextCandidate[1] /* || equals(endRect.id, current.id) */) {
|
|
476
456
|
break;
|
|
477
|
-
}
|
|
478
|
-
// this is a skip condition to not include too many rects
|
|
457
|
+
}
|
|
479
458
|
if (matured) {
|
|
480
459
|
maturedCoveredLength = coveredLength;
|
|
481
|
-
/* lastMatured = current; */
|
|
482
460
|
}
|
|
483
|
-
results.push(current);
|
|
484
|
-
/*
|
|
485
|
-
|
|
486
|
-
if (current.start1 > endLocation && (wrappedOnce || !endIsWrapped)) {
|
|
487
|
-
break;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
*/
|
|
491
461
|
nextLocation = endIsWrapped
|
|
492
462
|
? wrappedOnce
|
|
493
463
|
? Math.min(current.end2, endLocation)
|
|
494
464
|
: current.end2
|
|
495
465
|
: Math.min(current.end2, endLocation);
|
|
496
466
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
let
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
if (nextPoint > 1 || nextPoint < startPoint) {
|
|
513
|
-
wrappedOnce = true;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
nextPoint = nextPoint % 1;
|
|
517
|
-
let distanceStart: number;
|
|
518
|
-
|
|
519
|
-
if (wrappedOnce) {
|
|
520
|
-
distanceStart = (1 - startPoint + currentNode.segment.offset) % 1;
|
|
521
|
-
} else {
|
|
522
|
-
distanceStart = (currentNode.segment.offset - startPoint) % 1;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
const distanceEnd = distanceStart + currentNode.segment.factor;
|
|
526
|
-
|
|
527
|
-
return [nextPoint, distanceStart, distanceEnd, wrappedOnce];
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
const getNextMatured = async (from: ReplicatorRect) => {
|
|
531
|
-
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)!;
|
|
532
|
-
while (
|
|
533
|
-
next.hash !== from.hash &&
|
|
534
|
-
next.hash !== startNode.hash
|
|
535
|
-
) {
|
|
536
|
-
if (isMatured(next.segment, t, roleAge)) {
|
|
537
|
-
return next;
|
|
467
|
+
start instanceof PublicSignKey && ret.add(start.hashcode());
|
|
468
|
+
return ret;
|
|
469
|
+
};
|
|
470
|
+
export const fetchOneFromPublicKey = async (publicKey, index, roleAge, now) => {
|
|
471
|
+
let result = await index.query(new SearchRequest({
|
|
472
|
+
query: [new StringMatch({ key: "hash", value: publicKey.hashcode() })],
|
|
473
|
+
fetch: 1,
|
|
474
|
+
}));
|
|
475
|
+
let node = result.results[0]?.value;
|
|
476
|
+
if (node) {
|
|
477
|
+
if (!isMatured(node, now, roleAge)) {
|
|
478
|
+
const matured = await fetchOne(getClosestAround(index, node.start1, roleAge, now, false, false));
|
|
479
|
+
if (matured) {
|
|
480
|
+
node = matured;
|
|
538
481
|
}
|
|
539
|
-
next = (next.next || peers.head)!;
|
|
540
|
-
}
|
|
541
|
-
return undefined;
|
|
542
|
-
}; */
|
|
543
|
-
/**
|
|
544
|
-
* The purpose of this loop is to cover at least coveringWidth
|
|
545
|
-
* so that if we query all nodes in this range, we know we will
|
|
546
|
-
* "query" all data in that range
|
|
547
|
-
*/
|
|
548
|
-
/* let isPastThePoint = false;
|
|
549
|
-
outer: while (currentNode) {
|
|
550
|
-
if (set.has(currentNode.hash)) break;
|
|
551
|
-
|
|
552
|
-
const [nextPoint, distanceStart, distanceEnd, wrapped] = getNextPoint();
|
|
553
|
-
|
|
554
|
-
if (distanceStart <= coveringWidth) {
|
|
555
|
-
set.add(currentNode.hash);
|
|
556
482
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
(nextHasWrapped &&
|
|
575
|
-
(wrapped ? nextOffset > nextPoint : prevOffset < nextPoint)) ||
|
|
576
|
-
(!nextHasWrapped && prevOffset < nextPoint && nextPoint <= nextOffset)
|
|
577
|
-
) {
|
|
578
|
-
isPastThePoint = true;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
if (isPastThePoint) {
|
|
582
|
-
break; // include this next in the set;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
const overlapsRange = containsPoint(next.value.role, nextPoint);
|
|
586
|
-
|
|
587
|
-
if (overlapsRange) {
|
|
588
|
-
// Find out if there is a better choice ahead of us
|
|
589
|
-
const nextNext = await getNextMatured(next);
|
|
590
|
-
if (
|
|
591
|
-
nextNext &&
|
|
592
|
-
nextNext.hash === currentNode.hash &&
|
|
593
|
-
nextNext.segment.offset < nextPoint &&
|
|
594
|
-
nextNext.segment.offset + nextNext.segment.factor > nextPoint
|
|
595
|
-
) {
|
|
596
|
-
// nextNext is better (continue to iterate)
|
|
597
|
-
} else {
|
|
598
|
-
// done
|
|
599
|
-
break;
|
|
600
|
-
}
|
|
601
|
-
} else {
|
|
602
|
-
// (continue to iterate)
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
next = next.next || peers.head;
|
|
483
|
+
}
|
|
484
|
+
return node;
|
|
485
|
+
};
|
|
486
|
+
export const getStartAndEnd = async (peers, start, widthToCoverScaled, roleAge, now, intervalWidth) => {
|
|
487
|
+
// find a good starting point
|
|
488
|
+
let startNode = undefined;
|
|
489
|
+
let startLocation = undefined;
|
|
490
|
+
const nodeFromPoint = async (point = scaleToU32(Math.random())) => {
|
|
491
|
+
startLocation = point;
|
|
492
|
+
startNode = await fetchOneClosest(peers, startLocation, roleAge, now, false, true);
|
|
493
|
+
};
|
|
494
|
+
if (start instanceof PublicSignKey) {
|
|
495
|
+
// start at our node (local first)
|
|
496
|
+
startNode = await fetchOneFromPublicKey(start, peers, roleAge, now);
|
|
497
|
+
if (!startNode) {
|
|
498
|
+
// fetch randomly
|
|
499
|
+
await nodeFromPoint();
|
|
606
500
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
// collect 1 point around the boundary of the start and one at the end,
|
|
610
|
-
// preferrd matured and that we already have it
|
|
611
|
-
/* for (const point of [
|
|
612
|
-
startNode.segment.offset,
|
|
613
|
-
(startNode.segment.offset + coveringWidth) % 1
|
|
614
|
-
]) {
|
|
615
|
-
let done = false;
|
|
616
|
-
const unmatured: string[] = [];
|
|
617
|
-
collectNodesAroundPoint(
|
|
618
|
-
t,
|
|
619
|
-
roleAge,
|
|
620
|
-
peers,
|
|
621
|
-
isMatured(startNode.segment, t, roleAge) ? startNode : peers.head, // start at startNode is matured, else start at head (we only seek to find one matured node at the point)
|
|
622
|
-
(rect, matured) => {
|
|
623
|
-
if (matured) {
|
|
624
|
-
if (set.has(rect.hash)) {
|
|
625
|
-
// great!
|
|
626
|
-
} else {
|
|
627
|
-
set.add(rect.hash);
|
|
628
|
-
}
|
|
629
|
-
done = true;
|
|
630
|
-
} else {
|
|
631
|
-
unmatured.push(rect.hash);
|
|
632
|
-
}
|
|
633
|
-
},
|
|
634
|
-
point,
|
|
635
|
-
() => done
|
|
636
|
-
);
|
|
637
|
-
if (!done && unmatured.length > 0) {
|
|
638
|
-
set.add(unmatured[0]);
|
|
639
|
-
// TODO add more elements?
|
|
501
|
+
else {
|
|
502
|
+
startLocation = startNode.start1;
|
|
640
503
|
}
|
|
641
504
|
}
|
|
642
|
-
|
|
505
|
+
else if (typeof start === "number") {
|
|
506
|
+
await nodeFromPoint(start);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
await nodeFromPoint();
|
|
510
|
+
}
|
|
511
|
+
if (!startNode || startLocation == null) {
|
|
512
|
+
return { startNode: undefined, startLocation: 0, endLocation: 0 };
|
|
513
|
+
}
|
|
514
|
+
let endLocation = startLocation + widthToCoverScaled;
|
|
515
|
+
if (intervalWidth != null) {
|
|
516
|
+
endLocation = endLocation % intervalWidth;
|
|
517
|
+
}
|
|
518
|
+
// if start location is after endLocation and startNode is strict then return undefined because this is not a node we want to choose
|
|
519
|
+
let coveredDistanceToStart = 0;
|
|
520
|
+
if (startNode.start1 < startLocation) {
|
|
521
|
+
coveredDistanceToStart += intervalWidth - startLocation + startNode.start1;
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
coveredDistanceToStart += startNode.start1 - startLocation;
|
|
525
|
+
}
|
|
526
|
+
if (startNode.mode === ReplicationIntent.Strict &&
|
|
527
|
+
coveredDistanceToStart > widthToCoverScaled) {
|
|
528
|
+
return { startNode: undefined, startLocation: 0, endLocation: 0 };
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
startNode,
|
|
532
|
+
startLocation: Math.round(startLocation),
|
|
533
|
+
endLocation: Math.round(endLocation),
|
|
534
|
+
};
|
|
535
|
+
};
|
|
536
|
+
export const fetchOneClosest = (peers, point, roleAge, now, includeStrictBelow, includeStrictAbove) => {
|
|
537
|
+
return fetchOne(getClosestAround(peers, point, roleAge, now, includeStrictBelow, includeStrictAbove));
|
|
643
538
|
};
|
|
644
539
|
//# sourceMappingURL=ranges.js.map
|