@peerbit/shared-log 8.0.7-a9206a8 → 8.0.7-cccc078

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 (49) 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.js +15 -16
  6. package/dist/benchmark/index.js.map +1 -1
  7. package/dist/benchmark/replication-prune.d.ts +2 -0
  8. package/dist/benchmark/replication-prune.d.ts.map +1 -0
  9. package/dist/benchmark/replication-prune.js +103 -0
  10. package/dist/benchmark/replication-prune.js.map +1 -0
  11. package/dist/benchmark/replication.d.ts +2 -0
  12. package/dist/benchmark/replication.d.ts.map +1 -0
  13. package/dist/benchmark/replication.js +91 -0
  14. package/dist/benchmark/replication.js.map +1 -0
  15. package/dist/src/blocks.js +1 -1
  16. package/dist/src/blocks.js.map +1 -1
  17. package/dist/src/cpu.js.map +1 -1
  18. package/dist/src/exchange-heads.d.ts +1 -1
  19. package/dist/src/exchange-heads.d.ts.map +1 -1
  20. package/dist/src/exchange-heads.js +8 -8
  21. package/dist/src/exchange-heads.js.map +1 -1
  22. package/dist/src/index.d.ts +53 -47
  23. package/dist/src/index.d.ts.map +1 -1
  24. package/dist/src/index.js +513 -365
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/pid.d.ts.map +1 -1
  27. package/dist/src/pid.js +20 -20
  28. package/dist/src/pid.js.map +1 -1
  29. package/dist/src/ranges.d.ts +9 -12
  30. package/dist/src/ranges.d.ts.map +1 -1
  31. package/dist/src/ranges.js +528 -133
  32. package/dist/src/ranges.js.map +1 -1
  33. package/dist/src/replication.d.ts +70 -12
  34. package/dist/src/replication.d.ts.map +1 -1
  35. package/dist/src/replication.js +258 -17
  36. package/dist/src/replication.js.map +1 -1
  37. package/dist/src/role.d.ts +1 -38
  38. package/dist/src/role.d.ts.map +1 -1
  39. package/dist/src/role.js +92 -116
  40. package/dist/src/role.js.map +1 -1
  41. package/package.json +11 -10
  42. package/src/blocks.ts +1 -1
  43. package/src/cpu.ts +4 -4
  44. package/src/exchange-heads.ts +18 -18
  45. package/src/index.ts +797 -550
  46. package/src/pid.ts +23 -22
  47. package/src/ranges.ts +693 -147
  48. package/src/replication.ts +271 -19
  49. package/src/role.ts +63 -83
package/src/ranges.ts CHANGED
@@ -1,10 +1,30 @@
1
- import yallist from "yallist";
2
- import { type 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,207 +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;
280
+ }
281
+
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;
78
453
  }
79
454
 
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;
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
- roleAge: number
486
+ roleAge: number,
100
487
  ) => {
101
488
  const leaders: Set<string> = new Set();
102
489
  const width = 1;
103
- if (!peers || peers?.length === 0) {
490
+ if (!peers) {
104
491
  return [];
105
492
  }
106
- amount = Math.min(amount, peers.length);
107
493
 
108
- const t = +new Date();
494
+ const size = await peers.getSize();
495
+
496
+ amount = Math.min(amount, size);
109
497
 
498
+ if (amount === 0) {
499
+ return [];
500
+ }
501
+
502
+ const now = +new Date();
110
503
 
111
504
  const maturedLeaders = new Set();
112
505
  for (let i = 0; i < amount; i++) {
113
506
  // evenly distributed
114
507
  const point = ((cursor + i / amount) % 1) * width;
115
- const currentNode = peers.head;
116
508
 
117
509
  // aquire at least one unique node for each point
118
- // but if previous point yielded more than one node
119
- collectNodesAroundPoint(
120
- t,
510
+ await collectNodesAroundPoint(
121
511
  roleAge,
122
512
  peers,
123
- currentNode,
124
513
  (rect, m) => {
514
+ // console.log(m, rect.start1 / SEGMENT_COORDINATE_SCALE, rect.width / SEGMENT_COORDINATE_SCALE)
125
515
  if (m) {
126
- maturedLeaders.add(rect.publicKey.hashcode());
516
+ maturedLeaders.add(rect.hash);
127
517
  }
128
- leaders.add(rect.publicKey.hashcode());
518
+ leaders.add(rect.hash);
129
519
  },
130
520
  point,
131
- (postProcess) => {
132
- if (postProcess) {
133
- if (maturedLeaders.size > i) {
134
- return true;
135
- }
521
+ now,
522
+ () => {
523
+ if (maturedLeaders.size > i) {
524
+ return true;
136
525
  }
137
- return false; // collect all intersecting points
138
- }
526
+ return false;
527
+ },
139
528
  );
140
529
  }
141
530
 
142
531
  return [...leaders];
143
532
  };
144
533
 
145
- export const getCover = (
146
- coveringWidth: number,
147
- peers: yallist<ReplicatorRect>,
148
- roleAge: number,
149
- startNodeIdentity?: PublicSignKey
150
- ): string[] => {
151
- 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;
152
538
  };
153
- export const getCoverSet = (
539
+
540
+ export const getCoverSet = async (
154
541
  coveringWidth: number,
155
- peers: yallist<ReplicatorRect>,
542
+ peers: Index<ReplicationRangeIndexable>,
156
543
  roleAge: number,
157
- startNodeIdentity?: PublicSignKey
158
- ): Set<string> => {
544
+ startNodeIdentity?: PublicSignKey,
545
+ ): Promise<Set<string>> => {
546
+ let now = +new Date();
547
+
159
548
  // find a good starting point
160
- let walker = peers.head;
549
+ let startNode: ReplicationRangeIndexable | undefined = undefined;
161
550
  if (startNodeIdentity) {
162
551
  // start at our node (local first)
163
- while (walker) {
164
- if (walker.value.publicKey.equals(startNodeIdentity)) {
165
- 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
+ }
166
570
  }
167
- walker = walker.next;
168
- }
169
- if (!walker) {
170
- walker = peers.head;
171
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
+ );
172
580
  } else {
173
- const seed = Math.round(peers.length * Math.random()); // start at a random point
174
- for (let i = 0; i < seed - 1; i++) {
175
- if (walker?.next == null) {
176
- break;
177
- }
178
- walker = walker.next;
179
- }
581
+ // TODO choose start location as the point with the longest range?
582
+ startLocation =
583
+ startNode.start1 ?? Math.random() * SEGMENT_COORDINATE_SCALE;
180
584
  }
181
585
 
182
- const startNode = walker;
586
+ /* const startNode = walker; */
183
587
  if (!startNode) {
184
588
  return new Set();
185
589
  }
186
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
+
187
733
  //
188
- const set: Set<string> = new Set();
734
+ /* const set: Set<string> = new Set();
189
735
  let currentNode = startNode;
190
736
  const t = +new Date();
191
-
737
+
192
738
  let wrappedOnce = false;
193
- const startPoint = startNode.value.role.offset;
194
-
739
+ const startPoint = startNode.segment.offset;
740
+
195
741
  const getNextPoint = (): [number, number, number, boolean] => {
196
742
  let nextPoint =
197
- currentNode.value.role.offset + currentNode.value.role.factor;
198
-
743
+ currentNode.segment.offset + currentNode.segment.factor;
744
+
199
745
  if (nextPoint > 1 || nextPoint < startPoint) {
200
746
  wrappedOnce = true;
201
747
  }
202
-
748
+
203
749
  nextPoint = nextPoint % 1;
204
750
  let distanceStart: number;
205
-
751
+
206
752
  if (wrappedOnce) {
207
- distanceStart = (1 - startPoint + currentNode.value.role.offset) % 1;
753
+ distanceStart = (1 - startPoint + currentNode.segment.offset) % 1;
208
754
  } else {
209
- distanceStart = (currentNode.value.role.offset - startPoint) % 1;
755
+ distanceStart = (currentNode.segment.offset - startPoint) % 1;
210
756
  }
211
-
212
- const distanceEnd = distanceStart + currentNode.value.role.factor;
213
-
757
+
758
+ const distanceEnd = distanceStart + currentNode.segment.factor;
759
+
214
760
  return [nextPoint, distanceStart, distanceEnd, wrappedOnce];
215
761
  };
216
-
217
- const getNextMatured = (from: yallist.Node<ReplicatorRect>) => {
218
- 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)!;
219
765
  while (
220
- !next.value.publicKey.equals(from.value.publicKey) &&
221
- !next.value.publicKey.equals(startNode.value.publicKey)
766
+ next.hash !== from.hash &&
767
+ next.hash !== startNode.hash
222
768
  ) {
223
- if (isMatured(next.value.role, t, roleAge)) {
769
+ if (isMatured(next.segment, t, roleAge)) {
224
770
  return next;
225
771
  }
226
772
  next = (next.next || peers.head)!;
227
773
  }
228
774
  return undefined;
229
- };
775
+ }; */
230
776
 
231
777
  /**
232
778
  * The purpose of this loop is to cover at least coveringWidth
@@ -234,30 +780,30 @@ export const getCoverSet = (
234
780
  * "query" all data in that range
235
781
  */
236
782
 
237
- let isPastThePoint = false;
783
+ /* let isPastThePoint = false;
238
784
  outer: while (currentNode) {
239
- if (set.has(currentNode.value.publicKey.hashcode())) break;
240
-
785
+ if (set.has(currentNode.hash)) break;
786
+
241
787
  const [nextPoint, distanceStart, distanceEnd, wrapped] = getNextPoint();
242
-
788
+
243
789
  if (distanceStart <= coveringWidth) {
244
- set.add(currentNode.value.publicKey.hashcode());
790
+ set.add(currentNode.hash);
245
791
  }
246
-
792
+
247
793
  if (distanceEnd >= coveringWidth) {
248
794
  break;
249
795
  }
250
-
796
+
251
797
  let next = currentNode.next || peers.head;
252
798
  while (next) {
253
799
  if (next.value.publicKey.equals(startNode.value.publicKey)) {
254
800
  break outer;
255
801
  }
256
-
802
+
257
803
  const prevOffset = (next.prev || peers.tail)!.value.role.offset;
258
804
  const nextOffset = next.value.role.offset;
259
805
  const nextHasWrapped = nextOffset < prevOffset;
260
-
806
+
261
807
  if (
262
808
  (!wrapped && nextOffset > nextPoint) ||
263
809
  (nextHasWrapped &&
@@ -266,21 +812,21 @@ export const getCoverSet = (
266
812
  ) {
267
813
  isPastThePoint = true;
268
814
  }
269
-
815
+
270
816
  if (isPastThePoint) {
271
817
  break; // include this next in the set;
272
818
  }
273
-
819
+
274
820
  const overlapsRange = containsPoint(next.value.role, nextPoint);
275
-
821
+
276
822
  if (overlapsRange) {
277
823
  // Find out if there is a better choice ahead of us
278
- const nextNext = getNextMatured(next);
824
+ const nextNext = await getNextMatured(next);
279
825
  if (
280
826
  nextNext &&
281
- !nextNext.value.publicKey.equals(currentNode.value.publicKey) &&
282
- nextNext.value.role.offset < nextPoint &&
283
- 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
284
830
  ) {
285
831
  // nextNext is better (continue to iterate)
286
832
  } else {
@@ -290,17 +836,17 @@ export const getCoverSet = (
290
836
  } else {
291
837
  // (continue to iterate)
292
838
  }
293
-
839
+
294
840
  next = next.next || peers.head;
295
841
  }
296
842
  currentNode = next!;
297
- }
843
+ } */
298
844
 
299
845
  // collect 1 point around the boundary of the start and one at the end,
300
846
  // preferrd matured and that we already have it
301
- for (const point of [
302
- startNode.value.role.offset,
303
- (startNode.value.role.offset + coveringWidth) % 1
847
+ /* for (const point of [
848
+ startNode.segment.offset,
849
+ (startNode.segment.offset + coveringWidth) % 1
304
850
  ]) {
305
851
  let done = false;
306
852
  const unmatured: string[] = [];
@@ -308,17 +854,17 @@ export const getCoverSet = (
308
854
  t,
309
855
  roleAge,
310
856
  peers,
311
- 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)
312
858
  (rect, matured) => {
313
859
  if (matured) {
314
- if (set.has(rect.publicKey.hashcode())) {
860
+ if (set.has(rect.hash)) {
315
861
  // great!
316
862
  } else {
317
- set.add(rect.publicKey.hashcode());
863
+ set.add(rect.hash);
318
864
  }
319
865
  done = true;
320
866
  } else {
321
- unmatured.push(rect.publicKey.hashcode());
867
+ unmatured.push(rect.hash);
322
868
  }
323
869
  },
324
870
  point,
@@ -329,5 +875,5 @@ export const getCoverSet = (
329
875
  // TODO add more elements?
330
876
  }
331
877
  }
332
- return set;
878
+ return set; */
333
879
  };