@peerbit/shared-log 7.0.9 → 7.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/ranges.ts CHANGED
@@ -1,25 +1,46 @@
1
1
  import yallist from "yallist";
2
2
  import { ReplicatorRect } from "./replication.js";
3
- import { Replicator, containsPoint } from "./role.js";
3
+ import { Replicator } from "./role.js";
4
4
  import { PublicSignKey } from "@peerbit/crypto";
5
5
 
6
- export const collectNodesAroundPoint = (
6
+ export const containsPoint = (
7
+ rect: { offset: number; factor: 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
+
31
+ const collectNodesAroundPoint = (
7
32
  time: number,
8
33
  roleAge: number,
9
34
  peers: yallist<ReplicatorRect>,
10
35
  currentNode: yallist.Node<ReplicatorRect> | null,
11
- collector: Set<string>,
36
+ collector: (rect: ReplicatorRect, matured: boolean) => void,
12
37
  point: number,
13
- once: boolean = false
38
+ done: (postProcess: boolean) => boolean = () => true
14
39
  ) => {
15
- let matured = 0;
16
-
17
- const maybeIncrementMatured = (role: Replicator) => {
18
- if (isMatured(role, time, roleAge)) {
19
- matured++;
20
- return true;
21
- }
22
- return false;
40
+ /* let uniqueMatured = 0;
41
+ */ const maybeIncrementMatured = (rect: ReplicatorRect) => {
42
+ const isMature = isMatured(rect.role, time, roleAge);
43
+ collector(rect, isMature);
23
44
  };
24
45
 
25
46
  // Assume peers does not mutate during this loop
@@ -27,13 +48,11 @@ export const collectNodesAroundPoint = (
27
48
  const diffs: { diff: number; rect: ReplicatorRect }[] = [];
28
49
  while (currentNode) {
29
50
  if (containsPoint(currentNode.value.role, point)) {
30
- collector.add(currentNode.value.publicKey.hashcode());
31
- if (maybeIncrementMatured(currentNode.value.role)) {
32
- if (once) {
33
- return;
34
- }
51
+ maybeIncrementMatured(currentNode.value);
52
+ if (done(false)) {
53
+ return;
35
54
  }
36
- } else {
55
+ } /* if (matured === 0) */ else {
37
56
  const start = currentNode.value.role.offset;
38
57
  const end =
39
58
  (currentNode.value.role.offset + currentNode.value.role.factor) % 1;
@@ -58,28 +77,88 @@ export const collectNodesAroundPoint = (
58
77
  }
59
78
  }
60
79
 
61
- if (matured === 0) {
80
+ if (done(true) == false) {
62
81
  diffs.sort((x, y) => x.diff - y.diff);
63
82
  for (const node of diffs) {
64
- collector.add(node.rect.publicKey.hashcode());
65
- maybeIncrementMatured(node.rect.role);
66
- if (matured > 0) {
83
+ maybeIncrementMatured(node.rect);
84
+ if (done(true)) {
67
85
  break;
68
86
  }
69
87
  }
70
88
  }
71
89
  };
72
90
 
73
- const isMatured = (role: Replicator, now: number, minAge: number) => {
91
+ export const isMatured = (role: Replicator, now: number, minAge: number) => {
74
92
  return now - Number(role.timestamp) >= minAge;
75
93
  };
76
94
 
95
+ export const getSamples = (
96
+ cursor: number,
97
+ peers: yallist<ReplicatorRect>,
98
+ amount: number,
99
+ roleAge: number,
100
+ dbg?: string
101
+ ) => {
102
+ const leaders: Set<string> = new Set();
103
+ const width = 1;
104
+ if (!peers || peers?.length === 0) {
105
+ return [];
106
+ }
107
+ amount = Math.min(amount, peers.length);
108
+
109
+ const t = +new Date();
110
+
111
+ const matured = 0;
112
+
113
+ const maturedLeaders = new Set();
114
+ for (let i = 0; i < amount; i++) {
115
+ // evenly distributed
116
+ const point = ((cursor + i / amount) % 1) * width;
117
+ const currentNode = peers.head;
118
+
119
+ // aquire at least one unique node for each point
120
+ // but if previous point yielded more than one node
121
+ collectNodesAroundPoint(
122
+ t,
123
+ roleAge,
124
+ peers,
125
+ currentNode,
126
+ (rect, m) => {
127
+ if (m) {
128
+ maturedLeaders.add(rect.publicKey.hashcode());
129
+ }
130
+ leaders.add(rect.publicKey.hashcode());
131
+ },
132
+ point,
133
+ (postProcess) => {
134
+ if (postProcess) {
135
+ if (maturedLeaders.size > i) {
136
+ return true;
137
+ }
138
+ }
139
+ return false; // collect all intersecting points
140
+ }
141
+ );
142
+ }
143
+
144
+ return [...leaders];
145
+ };
146
+
77
147
  export const getCover = (
78
148
  coveringWidth: number,
79
149
  peers: yallist<ReplicatorRect>,
80
150
  roleAge: number,
81
151
  startNodeIdentity?: PublicSignKey
82
- ) => {
152
+ ): string[] => {
153
+ return [...getCoverSet(coveringWidth, peers, roleAge, startNodeIdentity)];
154
+ };
155
+ export const getCoverSet = (
156
+ coveringWidth: number,
157
+ peers: yallist<ReplicatorRect>,
158
+ roleAge: number,
159
+ startNodeIdentity?: PublicSignKey
160
+ ): Set<string> => {
161
+ // find a good starting point
83
162
  let walker = peers.head;
84
163
  if (startNodeIdentity) {
85
164
  // start at our node (local first)
@@ -104,38 +183,38 @@ export const getCover = (
104
183
 
105
184
  const startNode = walker;
106
185
  if (!startNode) {
107
- return [];
186
+ return new Set();
108
187
  }
188
+
189
+ //
109
190
  const set: Set<string> = new Set();
110
191
  let currentNode = startNode;
192
+ const t = +new Date();
111
193
 
112
- /**
113
- * The purpose of this loop is to cover at least coveringWidth
114
- * so that if we query all nodes in this range, we know we will
115
- * "query" all data in that range
116
- */
194
+ let wrappedOnce = false;
195
+ const startPoint = startNode.value.role.offset;
117
196
 
118
- let wrapped = false;
119
- const getNextPoint = (): [number, number, boolean] => {
197
+ const getNextPoint = (): [number, number, number, boolean] => {
120
198
  let nextPoint =
121
199
  currentNode.value.role.offset + currentNode.value.role.factor;
122
- if (nextPoint > 1) {
123
- wrapped = true;
124
- nextPoint = nextPoint % 1;
200
+
201
+ if (nextPoint > 1 || nextPoint < startPoint) {
202
+ wrappedOnce = true;
125
203
  }
126
- let distance: number;
127
- if (wrapped) {
128
- distance = 1 - startNode.value.role.offset + nextPoint;
204
+
205
+ nextPoint = nextPoint % 1;
206
+ let distanceStart: number;
207
+
208
+ if (wrappedOnce) {
209
+ distanceStart = (1 - startPoint + currentNode.value.role.offset) % 1;
129
210
  } else {
130
- distance =
131
- currentNode.value.role.offset -
132
- startNode.value.role.offset +
133
- currentNode.value.role.factor;
211
+ distanceStart = (currentNode.value.role.offset - startPoint) % 1;
134
212
  }
135
- return [nextPoint, distance, wrapped];
136
- };
137
213
 
138
- const t = +new Date();
214
+ const distanceEnd = distanceStart + currentNode.value.role.factor;
215
+
216
+ return [nextPoint, distanceStart, distanceEnd, wrappedOnce];
217
+ };
139
218
 
140
219
  const getNextMatured = (from: yallist.Node<ReplicatorRect>) => {
141
220
  let next = (from.next || peers.head)!;
@@ -151,14 +230,23 @@ export const getCover = (
151
230
  return undefined;
152
231
  };
153
232
 
233
+ /**
234
+ * The purpose of this loop is to cover at least coveringWidth
235
+ * so that if we query all nodes in this range, we know we will
236
+ * "query" all data in that range
237
+ */
238
+
239
+ let isPastThePoint = false;
154
240
  outer: while (currentNode) {
155
241
  if (set.has(currentNode.value.publicKey.hashcode())) break;
156
242
 
157
- set.add(currentNode.value.publicKey.hashcode());
243
+ const [nextPoint, distanceStart, distanceEnd, wrapped] = getNextPoint();
158
244
 
159
- const [nextPoint, distance, wrapped] = getNextPoint();
245
+ if (distanceStart <= coveringWidth) {
246
+ set.add(currentNode.value.publicKey.hashcode());
247
+ }
160
248
 
161
- if (distance >= coveringWidth) {
249
+ if (distanceEnd >= coveringWidth) {
162
250
  break;
163
251
  }
164
252
 
@@ -168,56 +256,80 @@ export const getCover = (
168
256
  break outer;
169
257
  }
170
258
 
259
+ const prevOffset = (next.prev || peers.tail)!.value.role.offset;
260
+ const nextOffset = next.value.role.offset;
261
+ const nextHasWrapped = nextOffset < prevOffset;
262
+
171
263
  if (
172
- next.value.role.offset < nextPoint &&
173
- (next.value.role.offset > currentNode.value.role.offset || wrapped)
264
+ (!wrapped && nextOffset > nextPoint) ||
265
+ (nextHasWrapped &&
266
+ (wrapped ? nextOffset > nextPoint : prevOffset < nextPoint)) ||
267
+ (!nextHasWrapped && prevOffset < nextPoint && nextPoint <= nextOffset)
174
268
  ) {
175
- if (next.value.role.offset + next.value.role.factor > nextPoint) {
176
- // Find out if there is a better choice ahead of us
177
- const nextNext = getNextMatured(next);
178
- if (
179
- nextNext &&
180
- !nextNext.value.publicKey.equals(currentNode.value.publicKey) &&
181
- nextNext.value.role.offset < nextPoint &&
182
- nextNext.value.role.offset + nextNext.value.role.factor > nextPoint
183
- ) {
184
- // nextNext is better (continue to iterate)
185
- } else {
186
- // done
187
- break;
188
- }
269
+ isPastThePoint = true;
270
+ }
271
+
272
+ if (isPastThePoint) {
273
+ break; // include this next in the set;
274
+ }
275
+
276
+ const overlapsRange = containsPoint(next.value.role, nextPoint);
277
+
278
+ if (overlapsRange) {
279
+ // Find out if there is a better choice ahead of us
280
+ const nextNext = getNextMatured(next);
281
+ if (
282
+ 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
286
+ ) {
287
+ // nextNext is better (continue to iterate)
189
288
  } else {
190
- // (continue to iterate)
289
+ // done
290
+ break;
191
291
  }
192
292
  } else {
193
- // we got the best choice
194
- break;
293
+ // (continue to iterate)
195
294
  }
196
- next = next.next;
295
+
296
+ next = next.next || peers.head;
197
297
  }
198
298
  currentNode = next!;
199
299
  }
200
300
 
201
- // collect around the boundary of the start
202
- collectNodesAroundPoint(
203
- t,
204
- roleAge,
205
- peers,
206
- 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)
207
- set,
301
+ // collect 1 point around the boundary of the start and one at the end,
302
+ // preferrd matured and that we already have it
303
+ for (const point of [
208
304
  startNode.value.role.offset,
209
- true
210
- );
211
-
212
- // collect around the boundary of the end
213
- collectNodesAroundPoint(
214
- t,
215
- roleAge,
216
- peers,
217
- startNode, // start somewhere close to the border
218
- set,
219
- (startNode.value.role.offset + coveringWidth) % 1,
220
- true
221
- );
222
- return [...set];
305
+ (startNode.value.role.offset + coveringWidth) % 1
306
+ ]) {
307
+ let done = false;
308
+ const unmatured: string[] = [];
309
+ collectNodesAroundPoint(
310
+ t,
311
+ roleAge,
312
+ 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)
314
+ (rect, matured) => {
315
+ if (matured) {
316
+ if (set.has(rect.publicKey.hashcode())) {
317
+ // great!
318
+ } else {
319
+ set.add(rect.publicKey.hashcode());
320
+ }
321
+ done = true;
322
+ } else {
323
+ unmatured.push(rect.publicKey.hashcode());
324
+ }
325
+ },
326
+ point,
327
+ () => done
328
+ );
329
+ if (!done && unmatured.length > 0) {
330
+ set.add(unmatured[0]);
331
+ // TODO add more elements?
332
+ }
333
+ }
334
+ return set;
223
335
  };
package/src/role.ts CHANGED
@@ -1,34 +1,12 @@
1
1
  import { field, variant, vec } from "@dao-xyz/borsh";
2
2
 
3
- export const containsPoint = (
4
- rect: { offset: number; factor: number },
5
- point: number
6
- ) => {
7
- if (rect.factor === 0) {
8
- return false;
9
- }
10
- const start = rect.offset;
11
- const endUnwrapped = rect.offset + rect.factor;
12
- let end = endUnwrapped;
13
- let wrapped = false;
14
- if (endUnwrapped > 1) {
15
- end = endUnwrapped % 1;
16
- wrapped = true;
17
- }
18
-
19
- const inFirstInterval = point >= start && point < Math.min(endUnwrapped, 1);
20
- const inSecondInterval =
21
- !inFirstInterval && wrapped && point >= 0 && point < end;
22
-
23
- return inFirstInterval || inSecondInterval;
24
- };
25
-
26
- const overlaps = (x1: number, x2: number, y1: number, y2: number) => {
3
+ export const overlaps = (x1: number, x2: number, y1: number, y2: number) => {
27
4
  if (x1 <= y2 && y1 <= x2) {
28
5
  return true;
29
6
  }
30
7
  return false;
31
8
  };
9
+
32
10
  export abstract class Role {
33
11
  abstract equals(other: Role);
34
12
  }