@peerbit/shared-log 9.0.10 → 9.1.0

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.
Files changed (44) hide show
  1. package/dist/benchmark/index.js +2 -2
  2. package/dist/benchmark/index.js.map +1 -1
  3. package/dist/benchmark/replication.js +3 -3
  4. package/dist/benchmark/replication.js.map +1 -1
  5. package/dist/src/index.d.ts +46 -31
  6. package/dist/src/index.d.ts.map +1 -1
  7. package/dist/src/index.js +426 -229
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/pid.d.ts.map +1 -1
  10. package/dist/src/pid.js +20 -19
  11. package/dist/src/pid.js.map +1 -1
  12. package/dist/src/ranges.d.ts +13 -3
  13. package/dist/src/ranges.d.ts.map +1 -1
  14. package/dist/src/ranges.js +207 -335
  15. package/dist/src/ranges.js.map +1 -1
  16. package/dist/src/replication-domain-hash.d.ts +5 -0
  17. package/dist/src/replication-domain-hash.d.ts.map +1 -0
  18. package/dist/src/replication-domain-hash.js +30 -0
  19. package/dist/src/replication-domain-hash.js.map +1 -0
  20. package/dist/src/replication-domain-time.d.ts +14 -0
  21. package/dist/src/replication-domain-time.d.ts.map +1 -0
  22. package/dist/src/replication-domain-time.js +59 -0
  23. package/dist/src/replication-domain-time.js.map +1 -0
  24. package/dist/src/replication-domain.d.ts +33 -0
  25. package/dist/src/replication-domain.d.ts.map +1 -0
  26. package/dist/src/replication-domain.js +6 -0
  27. package/dist/src/replication-domain.js.map +1 -0
  28. package/dist/src/replication.d.ts +10 -8
  29. package/dist/src/replication.d.ts.map +1 -1
  30. package/dist/src/replication.js +64 -46
  31. package/dist/src/replication.js.map +1 -1
  32. package/dist/src/role.d.ts +2 -1
  33. package/dist/src/role.d.ts.map +1 -1
  34. package/dist/src/role.js +6 -5
  35. package/dist/src/role.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/index.ts +604 -310
  38. package/src/pid.ts +20 -19
  39. package/src/ranges.ts +291 -371
  40. package/src/replication-domain-hash.ts +43 -0
  41. package/src/replication-domain-time.ts +85 -0
  42. package/src/replication-domain.ts +50 -0
  43. package/src/replication.ts +50 -46
  44. package/src/role.ts +6 -5
@@ -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 { SEGMENT_COORDINATE_SCALE } from "./role.js";
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: pointScaled,
13
+ value: point,
97
14
  }),
98
15
  new IntegerCompare({
99
16
  key: "end1",
100
17
  compare: Compare.Greater,
101
- value: pointScaled,
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: pointScaled,
25
+ value: point,
109
26
  }),
110
27
  new IntegerCompare({
111
28
  key: "end2",
112
29
  compare: Compare.Greater,
113
- value: pointScaled,
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, scaled = false) => {
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(scaledPoint, false),
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" ? SEGMENT_COORDINATE_SCALE : 0, true),
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], scaledPoint, true, direction);
105
+ return joinIterator([iterator, iteratorWrapped], point, direction);
106
+ };
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);
183
173
  };
184
- export const getDistance = (from, to, direction, end = SEGMENT_COORDINATE_SCALE) => {
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, scaled, direction) => {
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, scaledPoint, direction), getDistance(closest.end2, scaledPoint, direction));
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, scaledPoint, direction);
226
+ dist = getDistance(closest.start1, point, direction);
238
227
  }
239
228
  else if (direction === "below") {
240
- dist = getDistance(closest.end2, scaledPoint, direction);
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, scaled) => {
293
- const closestBelow = getClosest("below", peers, point, roleAge, true, now, scaled);
294
- const closestAbove = getClosest("above", peers, point, roleAge, true, now, scaled);
295
- const containing = containingPoint(peers, point, roleAge, true, now, {
296
- scaled: scaled,
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, false, "closest");
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 = ((cursor + i / amount) % 1) * width;
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,28 @@ const fetchOne = async (iterator) => {
361
344
  await iterator.close();
362
345
  return value.results[0]?.value;
363
346
  };
364
- export const getCoverSet = async (coveringWidth, peers, roleAge, startNodeIdentity) => {
365
- let now = +new Date();
366
- // find a good starting point
367
- let startNode = undefined;
368
- if (startNodeIdentity) {
369
- // start at our node (local first)
370
- let result = await peers.query(new SearchRequest({
371
- query: [
372
- new StringMatch({ key: "hash", value: startNodeIdentity.hashcode() }),
373
- ],
374
- fetch: 1,
375
- }));
376
- startNode = result.results[0]?.value;
377
- if (startNode) {
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
- }
384
- }
385
- }
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
- }
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 (peers, roleAge, start, widthToCoverScaled, intervalWidth = MAX_U32) => {
357
+ const { startNode, startLocation, endLocation } = await getStartAndEnd(peers, start, widthToCoverScaled, roleAge, Date.now(), intervalWidth);
399
358
  let results = [];
400
- let widthToCoverScaled = coveringWidth * SEGMENT_COORDINATE_SCALE;
401
- const endLocation = (startLocation + widthToCoverScaled) % SEGMENT_COORDINATE_SCALE;
359
+ let now = +new Date();
402
360
  const endIsWrapped = endLocation <= startLocation;
403
- const endRect = (await fetchOne(getClosestAround(peers, endLocation, roleAge, now, true))) || (await fetchOne(getClosestAround(peers, endLocation, 0, now, true))); // (await getClosest('above', peers, nextLocation, roleAge, true, 1, true))[0]
404
- if (!endRect) {
361
+ if (!startNode) {
405
362
  return new Set();
406
363
  }
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;
364
+ let current = startNode;
412
365
  // push edges
413
- results.push(endRect);
414
366
  results.push(current);
415
- /* const endIsSameAsStart = equals(endRect.id, current.id); */
416
367
  const resolveNextContaining = async (nextLocation, roleAge) => {
417
368
  let next = await fetchOne(containingPoint(peers, nextLocation, roleAge, true, now, {
418
- scaled: true,
419
369
  sort: [new Sort({ key: "end2", direction: SortDirection.DESC })],
420
370
  })); // get entersecting sort by largest end2
421
371
  return next;
@@ -434,11 +384,23 @@ export const getCoverSet = async (coveringWidth, peers, roleAge, startNodeIdenti
434
384
  };
435
385
  // fill the middle
436
386
  let wrappedOnce = current.end2 < current.end1;
387
+ let coveredLength = 0;
388
+ const addLength = (from) => {
389
+ if (current.end2 < from || current.wrapped) {
390
+ wrappedOnce = true;
391
+ coveredLength += MAX_U32 - from;
392
+ coveredLength += current.end2;
393
+ }
394
+ else {
395
+ coveredLength += current.end1 - from;
396
+ }
397
+ };
398
+ addLength(startLocation);
437
399
  let maturedCoveredLength = coveredLength;
438
- /* let lastMatured = isMatured(startNode, now, roleAge) ? startNode : undefined;
439
- */
440
- while (maturedCoveredLength < widthToCoverScaled &&
441
- coveredLength <= SEGMENT_COORDINATE_SCALE) {
400
+ let nextLocation = current.end2;
401
+ while (maturedCoveredLength < widthToCoverScaled && // eslint-disable-line no-unmodified-loop-condition
402
+ coveredLength <= MAX_U32 // eslint-disable-line no-unmodified-loop-condition
403
+ ) {
442
404
  let nextCandidate = await resolveNext(nextLocation, roleAge);
443
405
  /* let fromAbove = false; */
444
406
  let matured = true;
@@ -450,44 +412,28 @@ export const getCoverSet = async (coveringWidth, peers, roleAge, startNodeIdenti
450
412
  if (!nextCandidate[0]) {
451
413
  break;
452
414
  }
415
+ let nextIsCurrent = equals(nextCandidate[0].id, current.id);
416
+ if (nextIsCurrent) {
417
+ break;
418
+ }
419
+ let last = current;
453
420
  current = nextCandidate[0];
454
421
  let distanceBefore = coveredLength;
455
- if (current.end2 < nextLocation) {
456
- wrappedOnce = true;
457
- coveredLength += SEGMENT_COORDINATE_SCALE - nextLocation;
458
- coveredLength += current.end2;
459
- }
460
- else {
461
- coveredLength += current.end1 - nextLocation;
462
- }
422
+ addLength(nextLocation);
463
423
  let isLast = distanceBefore < widthToCoverScaled &&
464
424
  coveredLength >= widthToCoverScaled;
465
- if ((isLast &&
466
- !nextCandidate[1]) /* || Math.min(current.start1, current.start2) > Math.min(endRect.start1, endRect.start2) */ /* (Math.min(current.start1, current.start2) > endLocation) */ ||
467
- equals(endRect.id, current.id)) {
468
- /* if ((isLast && ((endIsWrapped && wrappedOnce) || (!endIsWrapped))) && (current.start1 > endLocation || equals(current.id, endRect.id))) { */
469
- // this is the end!
470
- /* if (lastMatured && lastMatured.distanceTo(endLocation) < current.distanceTo(endLocation)) {
471
- breaks;
472
- } */
473
- break;
425
+ if (!isLast ||
426
+ nextCandidate[1] ||
427
+ Math.min(getDistance(last.start1, endLocation, "closest"), getDistance(last.end2, endLocation, "closest")) >
428
+ Math.min(getDistance(current.start1, endLocation, "closest"), getDistance(current.end2, endLocation, "closest"))) {
429
+ results.push(current);
474
430
  }
475
- /* if (fromAbove && next && next.start1 > endRect.start1 && (endIsWrapped === false || wrappedOnce)) {
431
+ if (isLast && !nextCandidate[1] /* || equals(endRect.id, current.id) */) {
476
432
  break;
477
- } */
478
- // this is a skip condition to not include too many rects
433
+ }
479
434
  if (matured) {
480
435
  maturedCoveredLength = coveredLength;
481
- /* lastMatured = current; */
482
436
  }
483
- results.push(current);
484
- /*
485
-
486
- if (current.start1 > endLocation && (wrappedOnce || !endIsWrapped)) {
487
- break;
488
- }
489
-
490
- */
491
437
  nextLocation = endIsWrapped
492
438
  ? wrappedOnce
493
439
  ? Math.min(current.end2, endLocation)
@@ -495,150 +441,76 @@ export const getCoverSet = async (coveringWidth, peers, roleAge, startNodeIdenti
495
441
  : Math.min(current.end2, endLocation);
496
442
  }
497
443
  const res = new Set(results.map((x) => x.hash));
498
- startNodeIdentity && res.add(startNodeIdentity.hashcode());
444
+ start instanceof PublicSignKey && res.add(start.hashcode());
499
445
  return res;
500
- //
501
- /* const set: Set<string> = new Set();
502
- let currentNode = startNode;
503
- const t = +new Date();
504
-
505
- let wrappedOnce = false;
506
- const startPoint = startNode.segment.offset;
507
-
508
- const getNextPoint = (): [number, number, number, boolean] => {
509
- let nextPoint =
510
- currentNode.segment.offset + currentNode.segment.factor;
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;
446
+ };
447
+ export const fetchOneFromPublicKey = async (publicKey, index, roleAge, now) => {
448
+ let result = await index.query(new SearchRequest({
449
+ query: [new StringMatch({ key: "hash", value: publicKey.hashcode() })],
450
+ fetch: 1,
451
+ }));
452
+ let node = result.results[0]?.value;
453
+ if (node) {
454
+ if (!isMatured(node, now, roleAge)) {
455
+ const matured = await fetchOne(getClosestAround(index, node.start1, roleAge, now, false, false));
456
+ if (matured) {
457
+ node = matured;
538
458
  }
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
459
  }
557
-
558
- if (distanceEnd >= coveringWidth) {
559
- break;
560
- }
561
-
562
- let next = currentNode.next || peers.head;
563
- while (next) {
564
- if (next.value.publicKey.equals(startNode.value.publicKey)) {
565
- break outer;
566
- }
567
-
568
- const prevOffset = (next.prev || peers.tail)!.value.role.offset;
569
- const nextOffset = next.value.role.offset;
570
- const nextHasWrapped = nextOffset < prevOffset;
571
-
572
- if (
573
- (!wrapped && nextOffset > nextPoint) ||
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;
460
+ }
461
+ return node;
462
+ };
463
+ export const getStartAndEnd = async (peers, start, widthToCoverScaled, roleAge, now, intervalWidth) => {
464
+ // find a good starting point
465
+ let startNode = undefined;
466
+ let startLocation = undefined;
467
+ const nodeFromPoint = async (point = scaleToU32(Math.random())) => {
468
+ startLocation = point;
469
+ startNode = await fetchOneClosest(peers, startLocation, roleAge, now, false, true);
470
+ };
471
+ if (start instanceof PublicSignKey) {
472
+ // start at our node (local first)
473
+ startNode = await fetchOneFromPublicKey(start, peers, roleAge, now);
474
+ if (!startNode) {
475
+ // fetch randomly
476
+ await nodeFromPoint();
606
477
  }
607
- currentNode = next!;
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?
478
+ else {
479
+ startLocation = startNode.start1;
640
480
  }
641
481
  }
642
- return set; */
482
+ else if (typeof start === "number") {
483
+ await nodeFromPoint(start);
484
+ }
485
+ else {
486
+ await nodeFromPoint();
487
+ }
488
+ if (!startNode || startLocation == null) {
489
+ return { startNode: undefined, startLocation: 0, endLocation: 0 };
490
+ }
491
+ let endLocation = startLocation + widthToCoverScaled;
492
+ if (intervalWidth != null) {
493
+ endLocation = endLocation % intervalWidth;
494
+ }
495
+ // if start location is after endLocation and startNode is strict then return undefined because this is not a node we want to choose
496
+ let coveredDistanceToStart = 0;
497
+ if (startNode.start1 < startLocation) {
498
+ coveredDistanceToStart += intervalWidth - startLocation + startNode.start1;
499
+ }
500
+ else {
501
+ coveredDistanceToStart += startNode.start1 - startLocation;
502
+ }
503
+ if (startNode.mode === ReplicationIntent.Strict &&
504
+ coveredDistanceToStart > widthToCoverScaled) {
505
+ return { startNode: undefined, startLocation: 0, endLocation: 0 };
506
+ }
507
+ return {
508
+ startNode,
509
+ startLocation: Math.round(startLocation),
510
+ endLocation: Math.round(endLocation),
511
+ };
512
+ };
513
+ export const fetchOneClosest = (peers, point, roleAge, now, includeStrictBelow, includeStrictAbove) => {
514
+ return fetchOne(getClosestAround(peers, point, roleAge, now, includeStrictBelow, includeStrictAbove));
643
515
  };
644
516
  //# sourceMappingURL=ranges.js.map