@peerbit/shared-log 9.1.2 → 9.2.0
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/dist/benchmark/get-samples.js +2 -3
- package/dist/benchmark/get-samples.js.map +1 -1
- package/dist/benchmark/index.js +4 -6
- package/dist/benchmark/index.js.map +1 -1
- package/dist/benchmark/memory/child.d.ts +2 -0
- package/dist/benchmark/memory/child.d.ts.map +1 -0
- package/dist/benchmark/memory/child.js +149 -0
- package/dist/benchmark/memory/child.js.map +1 -0
- package/dist/benchmark/memory/index.d.ts +2 -0
- package/dist/benchmark/memory/index.d.ts.map +1 -0
- package/dist/benchmark/memory/index.js +81 -0
- package/dist/benchmark/memory/index.js.map +1 -0
- package/dist/benchmark/memory/utils.d.ts +13 -0
- package/dist/benchmark/memory/utils.d.ts.map +1 -0
- package/dist/benchmark/memory/utils.js +2 -0
- package/dist/benchmark/memory/utils.js.map +1 -0
- package/dist/benchmark/replication-prune.js +27 -25
- package/dist/benchmark/replication-prune.js.map +1 -1
- package/dist/benchmark/replication.js +15 -16
- package/dist/benchmark/replication.js.map +1 -1
- package/dist/src/debounce.d.ts +25 -0
- package/dist/src/debounce.d.ts.map +1 -0
- package/dist/src/debounce.js +130 -0
- package/dist/src/debounce.js.map +1 -0
- package/dist/src/index.d.ts +55 -21
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +867 -390
- package/dist/src/index.js.map +1 -1
- package/dist/src/pid.d.ts.map +1 -1
- package/dist/src/pid.js +23 -21
- package/dist/src/pid.js.map +1 -1
- package/dist/src/ranges.d.ts +104 -8
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +518 -76
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/replication-domain-hash.d.ts.map +1 -1
- package/dist/src/replication-domain-hash.js.map +1 -1
- package/dist/src/replication-domain-time.d.ts.map +1 -1
- package/dist/src/replication-domain-time.js.map +1 -1
- package/dist/src/replication-domain.d.ts +22 -2
- package/dist/src/replication-domain.d.ts.map +1 -1
- package/dist/src/replication-domain.js +33 -0
- package/dist/src/replication-domain.js.map +1 -1
- package/dist/src/replication.d.ts +1 -55
- package/dist/src/replication.d.ts.map +1 -1
- package/dist/src/replication.js +5 -215
- package/dist/src/replication.js.map +1 -1
- package/dist/src/role.d.ts +1 -0
- package/dist/src/role.d.ts.map +1 -1
- package/dist/src/role.js +1 -0
- package/dist/src/role.js.map +1 -1
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +39 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +5 -5
- package/src/debounce.ts +172 -0
- package/src/index.ts +1282 -562
- package/src/pid.ts +27 -25
- package/src/ranges.ts +794 -181
- package/src/replication-domain-hash.ts +3 -1
- package/src/replication-domain-time.ts +2 -1
- package/src/replication-domain.ts +68 -5
- package/src/replication.ts +9 -235
- package/src/role.ts +1 -0
- package/src/utils.ts +49 -0
package/src/ranges.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { deserialize, field, serialize, variant } from "@dao-xyz/borsh";
|
|
2
|
+
import { PublicSignKey, equals, randomBytes, toBase64 } from "@peerbit/crypto";
|
|
2
3
|
import {
|
|
3
4
|
And,
|
|
5
|
+
BoolQuery,
|
|
4
6
|
ByteMatchQuery,
|
|
5
7
|
Compare,
|
|
6
8
|
type Index,
|
|
@@ -11,30 +13,416 @@ import {
|
|
|
11
13
|
Not,
|
|
12
14
|
Or,
|
|
13
15
|
type Query,
|
|
14
|
-
|
|
16
|
+
type ReturnTypeFromShape,
|
|
17
|
+
type Shape,
|
|
15
18
|
Sort,
|
|
16
19
|
SortDirection,
|
|
17
20
|
StringMatch,
|
|
18
|
-
iterate,
|
|
19
21
|
iteratorInSeries,
|
|
20
22
|
} from "@peerbit/indexer-interface";
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
type ReplicationRangeIndexable,
|
|
25
|
-
} from "./replication.js";
|
|
23
|
+
import { id } from "@peerbit/indexer-interface";
|
|
24
|
+
import { Meta, ShallowMeta } from "@peerbit/log";
|
|
25
|
+
import { type ReplicationChanges, type u32 } from "./replication-domain.js";
|
|
26
26
|
import { MAX_U32, scaleToU32 } from "./role.js";
|
|
27
|
+
import { groupByGidSync } from "./utils.js";
|
|
28
|
+
|
|
29
|
+
export enum ReplicationIntent {
|
|
30
|
+
NonStrict = 0, // indicates that the segment will be replicated and nearby data might be replicated as well
|
|
31
|
+
Strict = 1, // only replicate data in the segment to the specified replicator, not any other data
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const getSegmentsFromOffsetAndRange = (
|
|
35
|
+
offset: number,
|
|
36
|
+
factor: number,
|
|
37
|
+
): [[number, number], [number, number]] => {
|
|
38
|
+
let start1 = offset;
|
|
39
|
+
let end1Unscaled = offset + factor; // only add factor if it is not 1 to prevent numerical issues (like (0.9 + 1) % 1 => 0.8999999)
|
|
40
|
+
let end1 = Math.min(end1Unscaled, MAX_U32);
|
|
41
|
+
return [
|
|
42
|
+
[start1, end1],
|
|
43
|
+
end1Unscaled > MAX_U32
|
|
44
|
+
? [0, (factor !== MAX_U32 ? offset + factor : offset) % MAX_U32]
|
|
45
|
+
: [start1, end1],
|
|
46
|
+
];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const shouldAssigneToRangeBoundary = (
|
|
50
|
+
leaders:
|
|
51
|
+
| Map<
|
|
52
|
+
string,
|
|
53
|
+
{
|
|
54
|
+
intersecting: boolean;
|
|
55
|
+
}
|
|
56
|
+
>
|
|
57
|
+
| false,
|
|
58
|
+
) => {
|
|
59
|
+
let assignedToRangeBoundary = leaders === false || leaders.size === 0;
|
|
60
|
+
if (!assignedToRangeBoundary && leaders) {
|
|
61
|
+
for (const [_, { intersecting }] of leaders) {
|
|
62
|
+
if (!intersecting) {
|
|
63
|
+
assignedToRangeBoundary = true;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return assignedToRangeBoundary;
|
|
69
|
+
};
|
|
70
|
+
export class EntryReplicated {
|
|
71
|
+
@id({ type: "string" })
|
|
72
|
+
id: string; // hash + coordinate
|
|
73
|
+
|
|
74
|
+
@field({ type: "string" })
|
|
75
|
+
hash: string;
|
|
76
|
+
|
|
77
|
+
@field({ type: "string" })
|
|
78
|
+
gid: string;
|
|
79
|
+
|
|
80
|
+
@field({ type: "u32" })
|
|
81
|
+
coordinate: number;
|
|
82
|
+
|
|
83
|
+
@field({ type: "u64" })
|
|
84
|
+
wallTime: bigint;
|
|
85
|
+
|
|
86
|
+
@field({ type: "bool" })
|
|
87
|
+
assignedToRangeBoundary: boolean;
|
|
88
|
+
|
|
89
|
+
@field({ type: Uint8Array })
|
|
90
|
+
private _meta: Uint8Array;
|
|
91
|
+
|
|
92
|
+
private _metaResolved: ShallowMeta;
|
|
93
|
+
|
|
94
|
+
constructor(properties: {
|
|
95
|
+
coordinate: number;
|
|
96
|
+
hash: string;
|
|
97
|
+
meta: Meta;
|
|
98
|
+
assignedToRangeBoundary: boolean;
|
|
99
|
+
}) {
|
|
100
|
+
this.coordinate = properties.coordinate;
|
|
101
|
+
this.hash = properties.hash;
|
|
102
|
+
this.gid = properties.meta.gid;
|
|
103
|
+
this.id = this.hash + "-" + this.coordinate;
|
|
104
|
+
this.wallTime = properties.meta.clock.timestamp.wallTime;
|
|
105
|
+
const shallow =
|
|
106
|
+
properties.meta instanceof Meta
|
|
107
|
+
? new ShallowMeta(properties.meta)
|
|
108
|
+
: properties.meta;
|
|
109
|
+
this._meta = serialize(shallow);
|
|
110
|
+
this._metaResolved = deserialize(this._meta, ShallowMeta);
|
|
111
|
+
this._metaResolved = properties.meta;
|
|
112
|
+
this.assignedToRangeBoundary = properties.assignedToRangeBoundary;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get meta(): ShallowMeta {
|
|
116
|
+
if (!this._metaResolved) {
|
|
117
|
+
this._metaResolved = deserialize(this._meta, ShallowMeta);
|
|
118
|
+
}
|
|
119
|
+
return this._metaResolved;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@variant(0)
|
|
124
|
+
export class ReplicationRange {
|
|
125
|
+
@field({ type: Uint8Array })
|
|
126
|
+
id: Uint8Array;
|
|
127
|
+
|
|
128
|
+
@field({ type: "u64" })
|
|
129
|
+
timestamp: bigint;
|
|
130
|
+
|
|
131
|
+
@field({ type: "u32" })
|
|
132
|
+
private _offset: number;
|
|
133
|
+
|
|
134
|
+
@field({ type: "u32" })
|
|
135
|
+
private _factor: number;
|
|
136
|
+
|
|
137
|
+
@field({ type: "u8" })
|
|
138
|
+
mode: ReplicationIntent;
|
|
139
|
+
|
|
140
|
+
constructor(properties: {
|
|
141
|
+
id: Uint8Array;
|
|
142
|
+
offset: number;
|
|
143
|
+
factor: number;
|
|
144
|
+
timestamp: bigint;
|
|
145
|
+
mode: ReplicationIntent;
|
|
146
|
+
}) {
|
|
147
|
+
const { id, offset, factor, timestamp, mode } = properties;
|
|
148
|
+
this.id = id;
|
|
149
|
+
this._offset = offset;
|
|
150
|
+
this._factor = factor;
|
|
151
|
+
this.timestamp = timestamp;
|
|
152
|
+
this.mode = mode;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get factor(): number {
|
|
156
|
+
return this._factor;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
get offset(): number {
|
|
160
|
+
return this._offset;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
toReplicationRangeIndexable(key: PublicSignKey): ReplicationRangeIndexable {
|
|
164
|
+
return new ReplicationRangeIndexable({
|
|
165
|
+
id: this.id,
|
|
166
|
+
publicKeyHash: key.hashcode(),
|
|
167
|
+
offset: this.offset,
|
|
168
|
+
length: this.factor,
|
|
169
|
+
timestamp: this.timestamp,
|
|
170
|
+
mode: this.mode,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export class ReplicationRangeIndexable {
|
|
176
|
+
@id({ type: Uint8Array })
|
|
177
|
+
id: Uint8Array;
|
|
178
|
+
|
|
179
|
+
@field({ type: "string" })
|
|
180
|
+
hash: string;
|
|
181
|
+
|
|
182
|
+
@field({ type: "u64" })
|
|
183
|
+
timestamp: bigint;
|
|
184
|
+
|
|
185
|
+
@field({ type: "u32" })
|
|
186
|
+
start1!: number;
|
|
187
|
+
|
|
188
|
+
@field({ type: "u32" })
|
|
189
|
+
end1!: number;
|
|
190
|
+
|
|
191
|
+
@field({ type: "u32" })
|
|
192
|
+
start2!: number;
|
|
193
|
+
|
|
194
|
+
@field({ type: "u32" })
|
|
195
|
+
end2!: number;
|
|
196
|
+
|
|
197
|
+
@field({ type: "u32" })
|
|
198
|
+
width!: number;
|
|
199
|
+
|
|
200
|
+
@field({ type: "u8" })
|
|
201
|
+
mode: ReplicationIntent;
|
|
27
202
|
|
|
28
|
-
|
|
203
|
+
constructor(
|
|
204
|
+
properties: {
|
|
205
|
+
id?: Uint8Array;
|
|
206
|
+
normalized?: boolean;
|
|
207
|
+
offset: number;
|
|
208
|
+
length: number;
|
|
209
|
+
mode?: ReplicationIntent;
|
|
210
|
+
timestamp?: bigint;
|
|
211
|
+
} & ({ publicKeyHash: string } | { publicKey: PublicSignKey }),
|
|
212
|
+
) {
|
|
213
|
+
this.id = properties.id ?? randomBytes(32);
|
|
214
|
+
this.hash =
|
|
215
|
+
(properties as { publicKeyHash: string }).publicKeyHash ||
|
|
216
|
+
(properties as { publicKey: PublicSignKey }).publicKey.hashcode();
|
|
217
|
+
if (!properties.normalized) {
|
|
218
|
+
this.transform({ length: properties.length, offset: properties.offset });
|
|
219
|
+
} else {
|
|
220
|
+
this.transform({
|
|
221
|
+
length: scaleToU32(properties.length),
|
|
222
|
+
offset: scaleToU32(properties.offset),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.mode = properties.mode ?? ReplicationIntent.NonStrict;
|
|
227
|
+
this.timestamp = properties.timestamp || BigInt(0);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private transform(properties: { offset: number; length: number }) {
|
|
231
|
+
const ranges = getSegmentsFromOffsetAndRange(
|
|
232
|
+
properties.offset,
|
|
233
|
+
properties.length,
|
|
234
|
+
);
|
|
235
|
+
this.start1 = Math.round(ranges[0][0]);
|
|
236
|
+
this.end1 = Math.round(ranges[0][1]);
|
|
237
|
+
this.start2 = Math.round(ranges[1][0]);
|
|
238
|
+
this.end2 = Math.round(ranges[1][1]);
|
|
239
|
+
|
|
240
|
+
this.width =
|
|
241
|
+
this.end1 -
|
|
242
|
+
this.start1 +
|
|
243
|
+
(this.end2 < this.end1 ? this.end2 - this.start2 : 0);
|
|
244
|
+
|
|
245
|
+
if (
|
|
246
|
+
this.start1 > 0xffffffff ||
|
|
247
|
+
this.end1 > 0xffffffff ||
|
|
248
|
+
this.start2 > 0xffffffff ||
|
|
249
|
+
this.end2 > 0xffffffff ||
|
|
250
|
+
this.width > 0xffffffff ||
|
|
251
|
+
this.width < 0
|
|
252
|
+
) {
|
|
253
|
+
throw new Error("Segment coordinate out of bounds");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
get idString() {
|
|
258
|
+
return toBase64(this.id);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
contains(point: number) {
|
|
262
|
+
return (
|
|
263
|
+
(point >= this.start1 && point < this.end1) ||
|
|
264
|
+
(point >= this.start2 && point < this.end2)
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
overlaps(other: ReplicationRangeIndexable, checkOther = true): boolean {
|
|
269
|
+
if (
|
|
270
|
+
this.contains(other.start1) ||
|
|
271
|
+
this.contains(other.start2) ||
|
|
272
|
+
this.contains(other.end1 - 1) ||
|
|
273
|
+
this.contains(other.end2 - 1)
|
|
274
|
+
) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (checkOther) {
|
|
279
|
+
return other.overlaps(this, false);
|
|
280
|
+
}
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
toReplicationRange() {
|
|
284
|
+
return new ReplicationRange({
|
|
285
|
+
id: this.id,
|
|
286
|
+
offset: this.start1,
|
|
287
|
+
factor: this.width,
|
|
288
|
+
timestamp: this.timestamp,
|
|
289
|
+
mode: this.mode,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
distanceTo(point: number) {
|
|
294
|
+
let wrappedPoint = MAX_U32 - point;
|
|
295
|
+
return Math.min(
|
|
296
|
+
Math.abs(this.start1 - point),
|
|
297
|
+
Math.abs(this.end2 - point),
|
|
298
|
+
Math.abs(this.start1 - wrappedPoint),
|
|
299
|
+
Math.abs(this.end2 - wrappedPoint),
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
get wrapped() {
|
|
303
|
+
return this.end2 < this.end1;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
get widthNormalized() {
|
|
307
|
+
return this.width / MAX_U32;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
equals(other: ReplicationRangeIndexable) {
|
|
311
|
+
if (
|
|
312
|
+
equals(this.id, other.id) &&
|
|
313
|
+
this.hash === other.hash &&
|
|
314
|
+
this.timestamp === other.timestamp &&
|
|
315
|
+
this.mode === other.mode &&
|
|
316
|
+
this.start1 === other.start1 &&
|
|
317
|
+
this.end1 === other.end1 &&
|
|
318
|
+
this.start2 === other.start2 &&
|
|
319
|
+
this.end2 === other.end2 &&
|
|
320
|
+
this.width === other.width
|
|
321
|
+
) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
equalRange(other: ReplicationRangeIndexable) {
|
|
329
|
+
return (
|
|
330
|
+
this.start1 === other.start1 &&
|
|
331
|
+
this.end1 === other.end1 &&
|
|
332
|
+
this.start2 === other.start2 &&
|
|
333
|
+
this.end2 === other.end2
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
toString() {
|
|
338
|
+
let roundToTwoDecimals = (num: number) => Math.round(num * 100) / 100;
|
|
339
|
+
|
|
340
|
+
if (Math.abs(this.start1 - this.start2) < 0.0001) {
|
|
341
|
+
return `([${roundToTwoDecimals(this.start1 / MAX_U32)}, ${roundToTwoDecimals(this.end1 / MAX_U32)}])`;
|
|
342
|
+
}
|
|
343
|
+
return `([${roundToTwoDecimals(this.start1 / MAX_U32)}, ${roundToTwoDecimals(this.end1 / MAX_U32)}] [${roundToTwoDecimals(this.start2 / MAX_U32)}, ${roundToTwoDecimals(this.end2 / MAX_U32)}])`;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
toStringDetailed() {
|
|
347
|
+
return `(hash ${this.hash} range: ${this.toString()})`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* removeRange(other: ReplicationRangeIndexable): ReplicationRangeIndexable | ReplicationRangeIndexable[] {
|
|
351
|
+
if (!this.overlaps(other)) {
|
|
352
|
+
return this
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (this.equalRange(other)) {
|
|
356
|
+
return []
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
let diff: ReplicationRangeIndexable[] = [];
|
|
360
|
+
let start1 = this.start1;
|
|
361
|
+
if (other.start1 > start1) {
|
|
362
|
+
diff.push(new ReplicationRangeIndexable({
|
|
363
|
+
id: this.id,
|
|
364
|
+
offset: this.start1,
|
|
365
|
+
length: other.start1 - this.start1,
|
|
366
|
+
mode: this.mode,
|
|
367
|
+
publicKeyHash: this.hash,
|
|
368
|
+
timestamp: this.timestamp,
|
|
369
|
+
normalized: false
|
|
370
|
+
}));
|
|
371
|
+
|
|
372
|
+
start1 = other.end2
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (other.end1 < this.end1) {
|
|
376
|
+
diff.push(new ReplicationRangeIndexable({
|
|
377
|
+
id: this.id,
|
|
378
|
+
offset: other.end1,
|
|
379
|
+
length: this.end1 - other.end1,
|
|
380
|
+
mode: this.mode,
|
|
381
|
+
publicKeyHash: this.hash,
|
|
382
|
+
timestamp: this.timestamp,
|
|
383
|
+
normalized: false
|
|
384
|
+
}));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (other.start2 > this.start2) {
|
|
388
|
+
diff.push(new ReplicationRangeIndexable({
|
|
389
|
+
id: this.id,
|
|
390
|
+
offset: this.start2,
|
|
391
|
+
length: other.start2 - this.start2,
|
|
392
|
+
mode: this.mode,
|
|
393
|
+
publicKeyHash: this.hash,
|
|
394
|
+
timestamp: this.timestamp,
|
|
395
|
+
normalized: false
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (other.end2 < this.end2) {
|
|
400
|
+
diff.push(new ReplicationRangeIndexable({
|
|
401
|
+
id: this.id,
|
|
402
|
+
offset: other.end2,
|
|
403
|
+
length: this.end2 - other.end2,
|
|
404
|
+
mode: this.mode,
|
|
405
|
+
publicKeyHash: this.hash,
|
|
406
|
+
timestamp: this.timestamp,
|
|
407
|
+
normalized: false
|
|
408
|
+
}));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return diff;
|
|
412
|
+
} */
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const containingPoint = <S extends Shape | undefined = undefined>(
|
|
29
416
|
rects: Index<ReplicationRangeIndexable>,
|
|
30
417
|
point: number,
|
|
31
418
|
roleAgeLimit: number,
|
|
32
419
|
matured: boolean,
|
|
33
420
|
now: number,
|
|
34
421
|
options?: {
|
|
422
|
+
shape?: S;
|
|
35
423
|
sort?: Sort[];
|
|
36
424
|
},
|
|
37
|
-
): IndexIterator<ReplicationRangeIndexable> => {
|
|
425
|
+
): IndexIterator<ReplicationRangeIndexable, S> => {
|
|
38
426
|
// 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
|
|
39
427
|
|
|
40
428
|
let queries = [
|
|
@@ -70,13 +458,12 @@ const containingPoint = (
|
|
|
70
458
|
value: BigInt(now - roleAgeLimit),
|
|
71
459
|
}),
|
|
72
460
|
];
|
|
73
|
-
return iterate(
|
|
74
|
-
|
|
75
|
-
new SearchRequest({
|
|
461
|
+
return rects.iterate(
|
|
462
|
+
{
|
|
76
463
|
query: queries,
|
|
77
464
|
sort: options?.sort,
|
|
78
|
-
|
|
79
|
-
|
|
465
|
+
},
|
|
466
|
+
options,
|
|
80
467
|
);
|
|
81
468
|
/* const results = await rects.query(new SearchRequest({
|
|
82
469
|
query: queries,
|
|
@@ -86,7 +473,7 @@ const containingPoint = (
|
|
|
86
473
|
return results.results.map(x => x.value) */
|
|
87
474
|
};
|
|
88
475
|
|
|
89
|
-
const getClosest = (
|
|
476
|
+
const getClosest = <S extends Shape | undefined = undefined>(
|
|
90
477
|
direction: "above" | "below",
|
|
91
478
|
rects: Index<ReplicationRangeIndexable>,
|
|
92
479
|
point: number,
|
|
@@ -94,7 +481,8 @@ const getClosest = (
|
|
|
94
481
|
matured: boolean,
|
|
95
482
|
now: number,
|
|
96
483
|
includeStrict: boolean,
|
|
97
|
-
|
|
484
|
+
options?: { shape?: S },
|
|
485
|
+
): IndexIterator<ReplicationRangeIndexable, S> => {
|
|
98
486
|
const createQueries = (p: number, equality: boolean) => {
|
|
99
487
|
let queries: Query[];
|
|
100
488
|
if (direction === "below") {
|
|
@@ -140,28 +528,38 @@ const getClosest = (
|
|
|
140
528
|
return queries;
|
|
141
529
|
};
|
|
142
530
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
531
|
+
const sortByOldest = new Sort({ key: "timestamp", direction: "asc" });
|
|
532
|
+
const sortByHash = new Sort({ key: "hash", direction: "asc" }); // when breaking even
|
|
533
|
+
|
|
534
|
+
const iterator = rects.iterate(
|
|
535
|
+
{
|
|
146
536
|
query: createQueries(point, false),
|
|
147
|
-
sort:
|
|
537
|
+
sort: [
|
|
148
538
|
direction === "below"
|
|
149
539
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
150
540
|
: new Sort({ key: ["start1"], direction: "asc" }),
|
|
151
|
-
|
|
541
|
+
sortByOldest,
|
|
542
|
+
sortByHash,
|
|
543
|
+
],
|
|
544
|
+
},
|
|
545
|
+
options,
|
|
152
546
|
);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
547
|
+
|
|
548
|
+
const iteratorWrapped = rects.iterate(
|
|
549
|
+
{
|
|
156
550
|
query: createQueries(direction === "below" ? MAX_U32 : 0, true),
|
|
157
|
-
sort:
|
|
551
|
+
sort: [
|
|
158
552
|
direction === "below"
|
|
159
553
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
160
554
|
: new Sort({ key: ["start1"], direction: "asc" }),
|
|
161
|
-
|
|
555
|
+
sortByOldest,
|
|
556
|
+
sortByHash,
|
|
557
|
+
],
|
|
558
|
+
},
|
|
559
|
+
options,
|
|
162
560
|
);
|
|
163
561
|
|
|
164
|
-
return joinIterator([iterator, iteratorWrapped], point, direction);
|
|
562
|
+
return joinIterator<S>([iterator, iteratorWrapped], point, direction);
|
|
165
563
|
};
|
|
166
564
|
|
|
167
565
|
export const hasCoveringRange = async (
|
|
@@ -169,75 +567,73 @@ export const hasCoveringRange = async (
|
|
|
169
567
|
range: ReplicationRangeIndexable,
|
|
170
568
|
) => {
|
|
171
569
|
return (
|
|
172
|
-
(await rects.count(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
new
|
|
176
|
-
new
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}),
|
|
187
|
-
]),
|
|
188
|
-
new And([
|
|
189
|
-
new IntegerCompare({
|
|
190
|
-
key: "start2",
|
|
191
|
-
compare: Compare.LessOrEqual,
|
|
192
|
-
value: range.start1,
|
|
193
|
-
}),
|
|
194
|
-
new IntegerCompare({
|
|
195
|
-
key: "end2",
|
|
196
|
-
compare: Compare.GreaterOrEqual,
|
|
197
|
-
value: range.end1,
|
|
198
|
-
}),
|
|
199
|
-
]),
|
|
570
|
+
(await rects.count({
|
|
571
|
+
query: [
|
|
572
|
+
new Or([
|
|
573
|
+
new And([
|
|
574
|
+
new IntegerCompare({
|
|
575
|
+
key: "start1",
|
|
576
|
+
compare: Compare.LessOrEqual,
|
|
577
|
+
value: range.start1,
|
|
578
|
+
}),
|
|
579
|
+
new IntegerCompare({
|
|
580
|
+
key: "end1",
|
|
581
|
+
compare: Compare.GreaterOrEqual,
|
|
582
|
+
value: range.end1,
|
|
583
|
+
}),
|
|
200
584
|
]),
|
|
201
|
-
new
|
|
202
|
-
new
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}),
|
|
213
|
-
]),
|
|
214
|
-
new And([
|
|
215
|
-
new IntegerCompare({
|
|
216
|
-
key: "start2",
|
|
217
|
-
compare: Compare.LessOrEqual,
|
|
218
|
-
value: range.start2,
|
|
219
|
-
}),
|
|
220
|
-
new IntegerCompare({
|
|
221
|
-
key: "end2",
|
|
222
|
-
compare: Compare.GreaterOrEqual,
|
|
223
|
-
value: range.end2,
|
|
224
|
-
}),
|
|
225
|
-
]),
|
|
585
|
+
new And([
|
|
586
|
+
new IntegerCompare({
|
|
587
|
+
key: "start2",
|
|
588
|
+
compare: Compare.LessOrEqual,
|
|
589
|
+
value: range.start1,
|
|
590
|
+
}),
|
|
591
|
+
new IntegerCompare({
|
|
592
|
+
key: "end2",
|
|
593
|
+
compare: Compare.GreaterOrEqual,
|
|
594
|
+
value: range.end1,
|
|
595
|
+
}),
|
|
226
596
|
]),
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
key: "id",
|
|
235
|
-
value: range.id,
|
|
597
|
+
]),
|
|
598
|
+
new Or([
|
|
599
|
+
new And([
|
|
600
|
+
new IntegerCompare({
|
|
601
|
+
key: "start1",
|
|
602
|
+
compare: Compare.LessOrEqual,
|
|
603
|
+
value: range.start2,
|
|
236
604
|
}),
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
605
|
+
new IntegerCompare({
|
|
606
|
+
key: "end1",
|
|
607
|
+
compare: Compare.GreaterOrEqual,
|
|
608
|
+
value: range.end2,
|
|
609
|
+
}),
|
|
610
|
+
]),
|
|
611
|
+
new And([
|
|
612
|
+
new IntegerCompare({
|
|
613
|
+
key: "start2",
|
|
614
|
+
compare: Compare.LessOrEqual,
|
|
615
|
+
value: range.start2,
|
|
616
|
+
}),
|
|
617
|
+
new IntegerCompare({
|
|
618
|
+
key: "end2",
|
|
619
|
+
compare: Compare.GreaterOrEqual,
|
|
620
|
+
value: range.end2,
|
|
621
|
+
}),
|
|
622
|
+
]),
|
|
623
|
+
]),
|
|
624
|
+
new StringMatch({
|
|
625
|
+
key: "hash",
|
|
626
|
+
value: range.hash,
|
|
627
|
+
}),
|
|
628
|
+
// assume that we are looking for other ranges, not want to update an existing one
|
|
629
|
+
new Not(
|
|
630
|
+
new ByteMatchQuery({
|
|
631
|
+
key: "id",
|
|
632
|
+
value: range.id,
|
|
633
|
+
}),
|
|
634
|
+
),
|
|
635
|
+
],
|
|
636
|
+
})) > 0
|
|
241
637
|
);
|
|
242
638
|
};
|
|
243
639
|
|
|
@@ -280,15 +676,14 @@ export const getDistance = (
|
|
|
280
676
|
throw new Error("Invalid direction");
|
|
281
677
|
};
|
|
282
678
|
|
|
283
|
-
const joinIterator = (
|
|
284
|
-
iterators: IndexIterator<ReplicationRangeIndexable>[],
|
|
679
|
+
const joinIterator = <S extends Shape | undefined = undefined>(
|
|
680
|
+
iterators: IndexIterator<ReplicationRangeIndexable, S>[],
|
|
285
681
|
point: number,
|
|
286
682
|
direction: "above" | "below" | "closest",
|
|
287
|
-
) => {
|
|
683
|
+
): IndexIterator<ReplicationRangeIndexable, S> => {
|
|
288
684
|
let queues: {
|
|
289
|
-
kept: number;
|
|
290
685
|
elements: {
|
|
291
|
-
result: IndexedResult<ReplicationRangeIndexable
|
|
686
|
+
result: IndexedResult<ReturnTypeFromShape<ReplicationRangeIndexable, S>>;
|
|
292
687
|
dist: number;
|
|
293
688
|
}[];
|
|
294
689
|
}[] = [];
|
|
@@ -296,23 +691,23 @@ const joinIterator = (
|
|
|
296
691
|
return {
|
|
297
692
|
next: async (
|
|
298
693
|
count: number,
|
|
299
|
-
): Promise<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
694
|
+
): Promise<
|
|
695
|
+
IndexedResults<ReturnTypeFromShape<ReplicationRangeIndexable, S>>
|
|
696
|
+
> => {
|
|
697
|
+
let results: IndexedResults<
|
|
698
|
+
ReturnTypeFromShape<ReplicationRangeIndexable, S>
|
|
699
|
+
> = [];
|
|
304
700
|
for (let i = 0; i < iterators.length; i++) {
|
|
305
701
|
let queue = queues[i];
|
|
306
702
|
if (!queue) {
|
|
307
|
-
queue = { elements: []
|
|
703
|
+
queue = { elements: [] };
|
|
308
704
|
queues[i] = queue;
|
|
309
705
|
}
|
|
310
706
|
let iterator = iterators[i];
|
|
311
|
-
if (queue.elements.length < count && iterator.done()
|
|
707
|
+
if (queue.elements.length < count && iterator.done() !== true) {
|
|
312
708
|
let res = await iterator.next(count);
|
|
313
|
-
queue.kept = res.kept;
|
|
314
709
|
|
|
315
|
-
for (const el of res
|
|
710
|
+
for (const el of res) {
|
|
316
711
|
const closest = el.value;
|
|
317
712
|
|
|
318
713
|
let dist: number;
|
|
@@ -356,24 +751,25 @@ const joinIterator = (
|
|
|
356
751
|
|
|
357
752
|
let closest = queues[closestQueue]?.elements.shift();
|
|
358
753
|
if (closest) {
|
|
359
|
-
results.
|
|
754
|
+
results.push(closest.result);
|
|
360
755
|
}
|
|
361
756
|
}
|
|
362
|
-
|
|
363
|
-
for (let i = 0; i < queues.length; i++) {
|
|
364
|
-
results.kept += queues[i].elements.length + queues[i].kept;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
757
|
return results;
|
|
368
758
|
},
|
|
369
|
-
|
|
759
|
+
pending: async () => {
|
|
760
|
+
let allPending = await Promise.all(iterators.map((x) => x.pending()));
|
|
761
|
+
return allPending.reduce((acc, x) => acc + x, 0);
|
|
762
|
+
},
|
|
763
|
+
done: () => iterators.every((x) => x.done() === true),
|
|
370
764
|
close: async () => {
|
|
371
765
|
for (const iterator of iterators) {
|
|
372
766
|
await iterator.close();
|
|
373
767
|
}
|
|
374
768
|
},
|
|
375
769
|
all: async () => {
|
|
376
|
-
let results: IndexedResult<
|
|
770
|
+
let results: IndexedResult<
|
|
771
|
+
ReturnTypeFromShape<ReplicationRangeIndexable, S>
|
|
772
|
+
>[] = [];
|
|
377
773
|
for (const iterator of iterators) {
|
|
378
774
|
let res = await iterator.all();
|
|
379
775
|
results.push(...res);
|
|
@@ -383,15 +779,18 @@ const joinIterator = (
|
|
|
383
779
|
};
|
|
384
780
|
};
|
|
385
781
|
|
|
386
|
-
const getClosestAround =
|
|
782
|
+
const getClosestAround = <
|
|
783
|
+
S extends (Shape & { timestamp: true }) | undefined = undefined,
|
|
784
|
+
>(
|
|
387
785
|
peers: Index<ReplicationRangeIndexable>,
|
|
388
786
|
point: number,
|
|
389
787
|
roleAge: number,
|
|
390
788
|
now: number,
|
|
391
789
|
includeStrictBelow: boolean,
|
|
392
790
|
includeStrictAbove: boolean,
|
|
791
|
+
options?: { shape?: S },
|
|
393
792
|
) => {
|
|
394
|
-
const closestBelow = getClosest(
|
|
793
|
+
const closestBelow = getClosest<S>(
|
|
395
794
|
"below",
|
|
396
795
|
peers,
|
|
397
796
|
point,
|
|
@@ -399,8 +798,9 @@ const getClosestAround = (
|
|
|
399
798
|
true,
|
|
400
799
|
now,
|
|
401
800
|
includeStrictBelow,
|
|
801
|
+
options,
|
|
402
802
|
);
|
|
403
|
-
const closestAbove = getClosest(
|
|
803
|
+
const closestAbove = getClosest<S>(
|
|
404
804
|
"above",
|
|
405
805
|
peers,
|
|
406
806
|
point,
|
|
@@ -408,45 +808,79 @@ const getClosestAround = (
|
|
|
408
808
|
true,
|
|
409
809
|
now,
|
|
410
810
|
includeStrictAbove,
|
|
811
|
+
options,
|
|
812
|
+
);
|
|
813
|
+
const containing = containingPoint<S>(
|
|
814
|
+
peers,
|
|
815
|
+
point,
|
|
816
|
+
roleAge,
|
|
817
|
+
true,
|
|
818
|
+
now,
|
|
819
|
+
options,
|
|
411
820
|
);
|
|
412
|
-
const containing = containingPoint(peers, point, roleAge, true, now);
|
|
413
821
|
|
|
414
822
|
return iteratorInSeries(
|
|
415
823
|
containing,
|
|
416
|
-
joinIterator([closestBelow, closestAbove], point, "closest"),
|
|
824
|
+
joinIterator<S>([closestBelow, closestAbove], point, "closest"),
|
|
417
825
|
);
|
|
418
826
|
};
|
|
419
827
|
|
|
420
828
|
const collectNodesAroundPoint = async (
|
|
421
829
|
roleAge: number,
|
|
422
830
|
peers: Index<ReplicationRangeIndexable>,
|
|
423
|
-
collector: (
|
|
831
|
+
collector: (
|
|
832
|
+
rect: { hash: string },
|
|
833
|
+
matured: boolean,
|
|
834
|
+
interescting: boolean,
|
|
835
|
+
) => void,
|
|
424
836
|
point: u32,
|
|
425
837
|
now: number,
|
|
426
838
|
done: () => boolean = () => true,
|
|
427
839
|
) => {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
840
|
+
/* let shape = { timestamp: true, hash: true } as const */
|
|
841
|
+
const containing = containingPoint(
|
|
842
|
+
peers,
|
|
843
|
+
point,
|
|
844
|
+
0,
|
|
845
|
+
true,
|
|
846
|
+
now /* , { shape } */,
|
|
847
|
+
);
|
|
848
|
+
const allContaining = await containing.all();
|
|
849
|
+
for (const rect of allContaining) {
|
|
850
|
+
collector(rect.value, isMatured(rect.value, now, roleAge), true);
|
|
433
851
|
}
|
|
434
852
|
|
|
435
853
|
if (done()) {
|
|
436
854
|
return;
|
|
437
855
|
}
|
|
438
856
|
|
|
439
|
-
const closestBelow = getClosest(
|
|
440
|
-
|
|
857
|
+
const closestBelow = getClosest(
|
|
858
|
+
"below",
|
|
859
|
+
peers,
|
|
860
|
+
point,
|
|
861
|
+
0,
|
|
862
|
+
true,
|
|
863
|
+
now,
|
|
864
|
+
false /* , { shape } */,
|
|
865
|
+
);
|
|
866
|
+
const closestAbove = getClosest(
|
|
867
|
+
"above",
|
|
868
|
+
peers,
|
|
869
|
+
point,
|
|
870
|
+
0,
|
|
871
|
+
true,
|
|
872
|
+
now,
|
|
873
|
+
false /* , { shape } */,
|
|
874
|
+
);
|
|
441
875
|
const aroundIterator = joinIterator(
|
|
442
876
|
[closestBelow, closestAbove],
|
|
443
877
|
point,
|
|
444
878
|
"closest",
|
|
445
879
|
);
|
|
446
|
-
while (aroundIterator.done()
|
|
880
|
+
while (aroundIterator.done() !== true && done() !== true) {
|
|
447
881
|
const res = await aroundIterator.next(1);
|
|
448
|
-
for (const rect of res
|
|
449
|
-
collector(rect.value, isMatured(rect.value, now, roleAge));
|
|
882
|
+
for (const rect of res) {
|
|
883
|
+
collector(rect.value, isMatured(rect.value, now, roleAge), false);
|
|
450
884
|
if (done()) {
|
|
451
885
|
return;
|
|
452
886
|
}
|
|
@@ -454,6 +888,14 @@ const collectNodesAroundPoint = async (
|
|
|
454
888
|
}
|
|
455
889
|
};
|
|
456
890
|
|
|
891
|
+
export const getEvenlySpacedU32 = (from: number, count: number) => {
|
|
892
|
+
let ret: number[] = new Array(count);
|
|
893
|
+
for (let i = 0; i < count; i++) {
|
|
894
|
+
ret[i] = Math.round(from + (i * MAX_U32) / count) % MAX_U32;
|
|
895
|
+
}
|
|
896
|
+
return ret;
|
|
897
|
+
};
|
|
898
|
+
|
|
457
899
|
export const isMatured = (
|
|
458
900
|
segment: { timestamp: bigint },
|
|
459
901
|
now: number,
|
|
@@ -461,44 +903,42 @@ export const isMatured = (
|
|
|
461
903
|
) => {
|
|
462
904
|
return now - Number(segment.timestamp) >= minAge;
|
|
463
905
|
};
|
|
464
|
-
|
|
906
|
+
// get peer sample that are responsible for the cursor point
|
|
907
|
+
// will return a list of peers that want to replicate the data,
|
|
908
|
+
// but also if necessary a list of peers that are responsible for the data
|
|
909
|
+
// but have not explicitly replicating a range that cover the cursor point
|
|
465
910
|
export const getSamples = async (
|
|
466
|
-
cursor: u32,
|
|
911
|
+
cursor: u32[],
|
|
467
912
|
peers: Index<ReplicationRangeIndexable>,
|
|
468
|
-
amount: number,
|
|
469
913
|
roleAge: number,
|
|
470
|
-
) => {
|
|
471
|
-
const leaders:
|
|
914
|
+
): Promise<Map<string, { intersecting: boolean }>> => {
|
|
915
|
+
const leaders: Map<string, { intersecting: boolean }> = new Map();
|
|
472
916
|
if (!peers) {
|
|
473
|
-
return
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const size = await peers.getSize();
|
|
477
|
-
|
|
478
|
-
amount = Math.min(amount, size);
|
|
479
|
-
|
|
480
|
-
if (amount === 0) {
|
|
481
|
-
return [];
|
|
917
|
+
return new Map();
|
|
482
918
|
}
|
|
483
919
|
|
|
484
920
|
const now = +new Date();
|
|
485
921
|
|
|
486
922
|
const maturedLeaders = new Set();
|
|
487
|
-
for (let i = 0; i <
|
|
923
|
+
for (let i = 0; i < cursor.length; i++) {
|
|
488
924
|
// evenly distributed
|
|
489
|
-
const point = Math.round(cursor + (i * MAX_U32) / amount) % MAX_U32;
|
|
490
925
|
|
|
491
926
|
// aquire at least one unique node for each point
|
|
492
927
|
await collectNodesAroundPoint(
|
|
493
928
|
roleAge,
|
|
494
929
|
peers,
|
|
495
|
-
(rect, m) => {
|
|
930
|
+
(rect, m, intersecting) => {
|
|
496
931
|
if (m) {
|
|
497
932
|
maturedLeaders.add(rect.hash);
|
|
498
933
|
}
|
|
499
|
-
|
|
934
|
+
|
|
935
|
+
const prev = leaders.get(rect.hash);
|
|
936
|
+
|
|
937
|
+
if (!prev || (intersecting && !prev.intersecting)) {
|
|
938
|
+
leaders.set(rect.hash, { intersecting });
|
|
939
|
+
}
|
|
500
940
|
},
|
|
501
|
-
|
|
941
|
+
cursor[i],
|
|
502
942
|
now,
|
|
503
943
|
() => {
|
|
504
944
|
if (maturedLeaders.size > i) {
|
|
@@ -509,13 +949,15 @@ export const getSamples = async (
|
|
|
509
949
|
);
|
|
510
950
|
}
|
|
511
951
|
|
|
512
|
-
return
|
|
952
|
+
return leaders;
|
|
513
953
|
};
|
|
514
954
|
|
|
515
|
-
const fetchOne = async
|
|
955
|
+
const fetchOne = async <S extends Shape | undefined>(
|
|
956
|
+
iterator: IndexIterator<ReplicationRangeIndexable, S>,
|
|
957
|
+
) => {
|
|
516
958
|
const value = await iterator.next(1);
|
|
517
959
|
await iterator.close();
|
|
518
|
-
return value
|
|
960
|
+
return value[0]?.value;
|
|
519
961
|
};
|
|
520
962
|
|
|
521
963
|
export const minimumWidthToCover = async (
|
|
@@ -566,22 +1008,21 @@ export const getCoverSet = async (properties: {
|
|
|
566
1008
|
const eagerFetch =
|
|
567
1009
|
properties.eager === true
|
|
568
1010
|
? 1000
|
|
569
|
-
: properties.eager.unmaturedFetchCoverSize;
|
|
1011
|
+
: (properties.eager.unmaturedFetchCoverSize ?? 1000);
|
|
570
1012
|
|
|
571
1013
|
// pull all umatured
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
)
|
|
584
|
-
for (const rect of rects.results) {
|
|
1014
|
+
const iterator = peers.iterate({
|
|
1015
|
+
query: [
|
|
1016
|
+
new IntegerCompare({
|
|
1017
|
+
key: "timestamp",
|
|
1018
|
+
compare: Compare.GreaterOrEqual,
|
|
1019
|
+
value: BigInt(now - roleAge),
|
|
1020
|
+
}),
|
|
1021
|
+
],
|
|
1022
|
+
});
|
|
1023
|
+
const rects = await iterator.next(eagerFetch);
|
|
1024
|
+
await iterator.close();
|
|
1025
|
+
for (const rect of rects) {
|
|
585
1026
|
ret.add(rect.value.hash);
|
|
586
1027
|
}
|
|
587
1028
|
}
|
|
@@ -713,24 +1154,177 @@ export const getCoverSet = async (properties: {
|
|
|
713
1154
|
start instanceof PublicSignKey && ret.add(start.hashcode());
|
|
714
1155
|
return ret;
|
|
715
1156
|
};
|
|
1157
|
+
/* export const getReplicationDiff = (changes: ReplicationChange) => {
|
|
1158
|
+
// reduce the change set to only regions that are changed for each peer
|
|
1159
|
+
// i.e. subtract removed regions from added regions, and vice versa
|
|
1160
|
+
const result = new Map<string, { range: ReplicationRangeIndexable, added: boolean }[]>();
|
|
1161
|
+
|
|
1162
|
+
for (const addedChange of changes.added ?? []) {
|
|
1163
|
+
let prev = result.get(addedChange.hash) ?? [];
|
|
1164
|
+
for (const [_hash, ranges] of result.entries()) {
|
|
1165
|
+
for (const r of ranges) {
|
|
1166
|
+
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
*/
|
|
1172
|
+
|
|
1173
|
+
const matchRangeQuery = (range: ReplicationRangeIndexable) => {
|
|
1174
|
+
let ors = [];
|
|
1175
|
+
ors.push(
|
|
1176
|
+
new And([
|
|
1177
|
+
new IntegerCompare({
|
|
1178
|
+
key: "coordinate",
|
|
1179
|
+
compare: "gte",
|
|
1180
|
+
value: range.start1,
|
|
1181
|
+
}),
|
|
1182
|
+
new IntegerCompare({
|
|
1183
|
+
key: "coordinate",
|
|
1184
|
+
compare: "lt",
|
|
1185
|
+
value: range.end1,
|
|
1186
|
+
}),
|
|
1187
|
+
]),
|
|
1188
|
+
);
|
|
716
1189
|
|
|
717
|
-
|
|
1190
|
+
ors.push(
|
|
1191
|
+
new And([
|
|
1192
|
+
new IntegerCompare({
|
|
1193
|
+
key: "coordinate",
|
|
1194
|
+
compare: "gte",
|
|
1195
|
+
value: range.start2,
|
|
1196
|
+
}),
|
|
1197
|
+
new IntegerCompare({
|
|
1198
|
+
key: "coordinate",
|
|
1199
|
+
compare: "lt",
|
|
1200
|
+
value: range.end2,
|
|
1201
|
+
}),
|
|
1202
|
+
]),
|
|
1203
|
+
);
|
|
1204
|
+
|
|
1205
|
+
return new Or(ors);
|
|
1206
|
+
};
|
|
1207
|
+
export const toRebalance = (
|
|
1208
|
+
changes: ReplicationChanges,
|
|
1209
|
+
index: Index<EntryReplicated>,
|
|
1210
|
+
): AsyncIterable<{ gid: string; entries: EntryReplicated[] }> => {
|
|
1211
|
+
const assignedRangesQuery = (changes: ReplicationChanges) => {
|
|
1212
|
+
let ors: Query[] = [];
|
|
1213
|
+
for (const change of changes) {
|
|
1214
|
+
const matchRange = matchRangeQuery(change.range);
|
|
1215
|
+
if (change.type === "updated") {
|
|
1216
|
+
// assuming a range is to be removed, is this entry still enoughly replicated
|
|
1217
|
+
const prevMatchRange = matchRangeQuery(change.prev);
|
|
1218
|
+
ors.push(prevMatchRange);
|
|
1219
|
+
ors.push(matchRange);
|
|
1220
|
+
|
|
1221
|
+
/* ors.push(
|
|
1222
|
+
new And([
|
|
1223
|
+
// not sufficiently replicated
|
|
1224
|
+
new IntegerCompare({
|
|
1225
|
+
key: "replicatorsMissingShifted",
|
|
1226
|
+
compare: Compare.Greater,
|
|
1227
|
+
value: HALF_MAX_U32 - 1, // + 1 since we are going to remove a replicator for this entry
|
|
1228
|
+
}),
|
|
1229
|
+
|
|
1230
|
+
// is not the current range
|
|
1231
|
+
new Not(matchRange),
|
|
1232
|
+
|
|
1233
|
+
// but was in the the previous range
|
|
1234
|
+
prevMatchRange,
|
|
1235
|
+
]),
|
|
1236
|
+
); */
|
|
1237
|
+
} else {
|
|
1238
|
+
ors.push(matchRange);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// entry is assigned to a range boundary, meaning it is due to be inspected
|
|
1243
|
+
ors.push(
|
|
1244
|
+
new BoolQuery({
|
|
1245
|
+
key: "assignedToRangeBoundary",
|
|
1246
|
+
value: true,
|
|
1247
|
+
}),
|
|
1248
|
+
);
|
|
1249
|
+
|
|
1250
|
+
// entry is not sufficiently replicated, and we are to still keep it
|
|
1251
|
+
return new Or(ors);
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
return {
|
|
1255
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1256
|
+
const iterator = index.iterate({
|
|
1257
|
+
query: assignedRangesQuery(changes),
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
/* const iteratorFitlered = index.iterate({
|
|
1261
|
+
query: assignedRangesQuery(changes),
|
|
1262
|
+
}); */
|
|
1263
|
+
|
|
1264
|
+
while (iterator.done() !== true) {
|
|
1265
|
+
const entries = await iterator.next(0xffffffff); // TODO batch sizes
|
|
1266
|
+
/* const entriesFiltered = await iteratorFitlered.next(0xffffffff); // TODO batch sizes
|
|
1267
|
+
|
|
1268
|
+
console.log(
|
|
1269
|
+
"SIZE",
|
|
1270
|
+
await index.getSize(),
|
|
1271
|
+
entries.results.length,
|
|
1272
|
+
entriesFiltered.results.length,
|
|
1273
|
+
Math.min(
|
|
1274
|
+
...entries.results
|
|
1275
|
+
.filter((x) => x.value.assignedToRangeBoundary === false)
|
|
1276
|
+
.map((x) => x.value.coordinate / 0xffffffff),
|
|
1277
|
+
),
|
|
1278
|
+
Math.max(
|
|
1279
|
+
...entries.results
|
|
1280
|
+
.filter((x) => x.value.assignedToRangeBoundary === false)
|
|
1281
|
+
.map((x) => x.value.coordinate / 0xffffffff),
|
|
1282
|
+
),
|
|
1283
|
+
); */
|
|
1284
|
+
|
|
1285
|
+
// TODO do we need this
|
|
1286
|
+
const grouped = await groupByGidSync(entries.map((x) => x.value));
|
|
1287
|
+
|
|
1288
|
+
for (const [gid, entries] of grouped.entries()) {
|
|
1289
|
+
yield { gid, entries };
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
},
|
|
1293
|
+
};
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1296
|
+
export const fetchOneFromPublicKey = async <
|
|
1297
|
+
S extends (Shape & { timestamp: true }) | undefined = undefined,
|
|
1298
|
+
>(
|
|
718
1299
|
publicKey: PublicSignKey,
|
|
719
1300
|
index: Index<ReplicationRangeIndexable>,
|
|
720
1301
|
roleAge: number,
|
|
721
1302
|
now: number,
|
|
1303
|
+
options?: {
|
|
1304
|
+
shape: S;
|
|
1305
|
+
},
|
|
722
1306
|
) => {
|
|
723
|
-
let
|
|
724
|
-
|
|
1307
|
+
let iterator = index.iterate<S>(
|
|
1308
|
+
{
|
|
725
1309
|
query: [new StringMatch({ key: "hash", value: publicKey.hashcode() })],
|
|
726
|
-
|
|
727
|
-
|
|
1310
|
+
},
|
|
1311
|
+
options,
|
|
728
1312
|
);
|
|
729
|
-
let
|
|
1313
|
+
let result = await iterator.next(1);
|
|
1314
|
+
await iterator.close();
|
|
1315
|
+
let node = result[0]?.value;
|
|
730
1316
|
if (node) {
|
|
731
1317
|
if (!isMatured(node, now, roleAge)) {
|
|
732
1318
|
const matured = await fetchOne(
|
|
733
|
-
getClosestAround(
|
|
1319
|
+
getClosestAround<S>(
|
|
1320
|
+
index,
|
|
1321
|
+
node.start1,
|
|
1322
|
+
roleAge,
|
|
1323
|
+
now,
|
|
1324
|
+
false,
|
|
1325
|
+
false,
|
|
1326
|
+
options,
|
|
1327
|
+
),
|
|
734
1328
|
);
|
|
735
1329
|
if (matured) {
|
|
736
1330
|
node = matured;
|
|
@@ -740,16 +1334,24 @@ export const fetchOneFromPublicKey = async (
|
|
|
740
1334
|
return node;
|
|
741
1335
|
};
|
|
742
1336
|
|
|
743
|
-
export const getStartAndEnd = async
|
|
1337
|
+
export const getStartAndEnd = async <
|
|
1338
|
+
S extends (Shape & { timestamp: true }) | undefined,
|
|
1339
|
+
>(
|
|
744
1340
|
peers: Index<ReplicationRangeIndexable>,
|
|
745
1341
|
start: number | PublicSignKey | undefined | undefined,
|
|
746
1342
|
widthToCoverScaled: number,
|
|
747
1343
|
roleAge: number,
|
|
748
1344
|
now: number,
|
|
749
1345
|
intervalWidth: number,
|
|
750
|
-
|
|
1346
|
+
options?: { shape: S },
|
|
1347
|
+
): Promise<{
|
|
1348
|
+
startNode: ReturnTypeFromShape<ReplicationRangeIndexable, S> | undefined;
|
|
1349
|
+
startLocation: number;
|
|
1350
|
+
endLocation: number;
|
|
1351
|
+
}> => {
|
|
751
1352
|
// find a good starting point
|
|
752
|
-
let startNode: ReplicationRangeIndexable | undefined =
|
|
1353
|
+
let startNode: ReturnTypeFromShape<ReplicationRangeIndexable, S> | undefined =
|
|
1354
|
+
undefined;
|
|
753
1355
|
let startLocation: number | undefined = undefined;
|
|
754
1356
|
|
|
755
1357
|
const nodeFromPoint = async (point = scaleToU32(Math.random())) => {
|
|
@@ -761,12 +1363,19 @@ export const getStartAndEnd = async (
|
|
|
761
1363
|
now,
|
|
762
1364
|
false,
|
|
763
1365
|
true,
|
|
1366
|
+
options,
|
|
764
1367
|
);
|
|
765
1368
|
};
|
|
766
1369
|
|
|
767
1370
|
if (start instanceof PublicSignKey) {
|
|
768
1371
|
// start at our node (local first)
|
|
769
|
-
startNode = await fetchOneFromPublicKey(
|
|
1372
|
+
startNode = await fetchOneFromPublicKey(
|
|
1373
|
+
start,
|
|
1374
|
+
peers,
|
|
1375
|
+
roleAge,
|
|
1376
|
+
now,
|
|
1377
|
+
options,
|
|
1378
|
+
);
|
|
770
1379
|
if (!startNode) {
|
|
771
1380
|
// fetch randomly
|
|
772
1381
|
await nodeFromPoint();
|
|
@@ -810,22 +1419,26 @@ export const getStartAndEnd = async (
|
|
|
810
1419
|
};
|
|
811
1420
|
};
|
|
812
1421
|
|
|
813
|
-
export const fetchOneClosest =
|
|
1422
|
+
export const fetchOneClosest = <
|
|
1423
|
+
S extends (Shape & { timestamp: true }) | undefined = undefined,
|
|
1424
|
+
>(
|
|
814
1425
|
peers: Index<ReplicationRangeIndexable>,
|
|
815
1426
|
point: number,
|
|
816
1427
|
roleAge: number,
|
|
817
1428
|
now: number,
|
|
818
1429
|
includeStrictBelow: boolean,
|
|
819
1430
|
includeStrictAbove: boolean,
|
|
1431
|
+
options?: { shape?: S },
|
|
820
1432
|
) => {
|
|
821
1433
|
return fetchOne(
|
|
822
|
-
getClosestAround(
|
|
1434
|
+
getClosestAround<S>(
|
|
823
1435
|
peers,
|
|
824
1436
|
point,
|
|
825
1437
|
roleAge,
|
|
826
1438
|
now,
|
|
827
1439
|
includeStrictBelow,
|
|
828
1440
|
includeStrictAbove,
|
|
1441
|
+
options,
|
|
829
1442
|
),
|
|
830
1443
|
);
|
|
831
1444
|
};
|