@peerbit/shared-log 8.0.7 → 9.0.0-55cebfe

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