@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/lib/esm/index.d.ts +5 -0
- package/lib/esm/index.js +106 -45
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/ranges.d.ts +8 -1
- package/lib/esm/ranges.js +145 -67
- package/lib/esm/ranges.js.map +1 -1
- package/lib/esm/role.d.ts +1 -4
- package/lib/esm/role.js +1 -17
- package/lib/esm/role.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +134 -59
- package/src/ranges.ts +201 -89
- package/src/role.ts +2 -24
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
|
|
3
|
+
import { Replicator } from "./role.js";
|
|
4
4
|
import { PublicSignKey } from "@peerbit/crypto";
|
|
5
5
|
|
|
6
|
-
export const
|
|
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:
|
|
36
|
+
collector: (rect: ReplicatorRect, matured: boolean) => void,
|
|
12
37
|
point: number,
|
|
13
|
-
|
|
38
|
+
done: (postProcess: boolean) => boolean = () => true
|
|
14
39
|
) => {
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
31
|
-
if (
|
|
32
|
-
|
|
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 (
|
|
80
|
+
if (done(true) == false) {
|
|
62
81
|
diffs.sort((x, y) => x.diff - y.diff);
|
|
63
82
|
for (const node of diffs) {
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
200
|
+
|
|
201
|
+
if (nextPoint > 1 || nextPoint < startPoint) {
|
|
202
|
+
wrappedOnce = true;
|
|
125
203
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
243
|
+
const [nextPoint, distanceStart, distanceEnd, wrapped] = getNextPoint();
|
|
158
244
|
|
|
159
|
-
|
|
245
|
+
if (distanceStart <= coveringWidth) {
|
|
246
|
+
set.add(currentNode.value.publicKey.hashcode());
|
|
247
|
+
}
|
|
160
248
|
|
|
161
|
-
if (
|
|
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
|
-
|
|
173
|
-
(
|
|
264
|
+
(!wrapped && nextOffset > nextPoint) ||
|
|
265
|
+
(nextHasWrapped &&
|
|
266
|
+
(wrapped ? nextOffset > nextPoint : prevOffset < nextPoint)) ||
|
|
267
|
+
(!nextHasWrapped && prevOffset < nextPoint && nextPoint <= nextOffset)
|
|
174
268
|
) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
//
|
|
289
|
+
// done
|
|
290
|
+
break;
|
|
191
291
|
}
|
|
192
292
|
} else {
|
|
193
|
-
//
|
|
194
|
-
break;
|
|
293
|
+
// (continue to iterate)
|
|
195
294
|
}
|
|
196
|
-
|
|
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
|
-
|
|
203
|
-
|
|
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
|
-
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
|
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
|
}
|