@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.
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 +51 -31
  6. package/dist/src/index.d.ts.map +1 -1
  7. package/dist/src/index.js +435 -230
  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 +22 -3
  13. package/dist/src/ranges.d.ts.map +1 -1
  14. package/dist/src/ranges.js +231 -336
  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 +70 -70
  37. package/src/index.ts +621 -312
  38. package/src/pid.ts +20 -19
  39. package/src/ranges.ts +328 -375
  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);
183
106
  };
184
- export const getDistance = (from, to, direction, end = SEGMENT_COORDINATE_SCALE) => {
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, 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,52 @@ 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({
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 StringMatch({ key: "hash", value: startNodeIdentity.hashcode() }),
373
+ new IntegerCompare({
374
+ key: "timestamp",
375
+ compare: Compare.GreaterOrEqual,
376
+ value: BigInt(now - roleAge),
377
+ }),
373
378
  ],
374
- fetch: 1,
375
379
  }));
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
- }
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
- 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) {
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
- results.push(endRect);
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 maturedCoveredLength = coveredLength;
438
- /* let lastMatured = isMatured(startNode, now, roleAge) ? startNode : undefined;
439
- */
440
- while (maturedCoveredLength < widthToCoverScaled &&
441
- coveredLength <= SEGMENT_COORDINATE_SCALE) {
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
- 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
- }
446
+ addLength(nextLocation);
463
447
  let isLast = distanceBefore < widthToCoverScaled &&
464
448
  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;
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
- /* if (fromAbove && next && next.start1 > endRect.start1 && (endIsWrapped === false || wrappedOnce)) {
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
- const res = new Set(results.map((x) => x.hash));
498
- startNodeIdentity && res.add(startNodeIdentity.hashcode());
499
- 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;
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
- 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;
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
- 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?
501
+ else {
502
+ startLocation = startNode.start1;
640
503
  }
641
504
  }
642
- return set; */
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