@peerbit/shared-log 9.1.2 → 9.2.0-0b8baa8
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 +497 -82
- 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 +70 -70
- package/src/debounce.ts +172 -0
- package/src/index.ts +1282 -562
- package/src/pid.ts +27 -25
- package/src/ranges.ts +772 -187
- 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;
|
|
202
|
+
|
|
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
|
+
}
|
|
27
358
|
|
|
28
|
-
|
|
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,23 +458,16 @@ 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
|
-
/* const results = await rects.query(new SearchRequest({
|
|
82
|
-
query: queries,
|
|
83
|
-
sort: options?.sort,
|
|
84
|
-
fetch: 0xffffffff
|
|
85
|
-
}))
|
|
86
|
-
return results.results.map(x => x.value) */
|
|
87
468
|
};
|
|
88
469
|
|
|
89
|
-
const getClosest = (
|
|
470
|
+
const getClosest = <S extends Shape | undefined = undefined>(
|
|
90
471
|
direction: "above" | "below",
|
|
91
472
|
rects: Index<ReplicationRangeIndexable>,
|
|
92
473
|
point: number,
|
|
@@ -94,7 +475,8 @@ const getClosest = (
|
|
|
94
475
|
matured: boolean,
|
|
95
476
|
now: number,
|
|
96
477
|
includeStrict: boolean,
|
|
97
|
-
|
|
478
|
+
options?: { shape?: S },
|
|
479
|
+
): IndexIterator<ReplicationRangeIndexable, S> => {
|
|
98
480
|
const createQueries = (p: number, equality: boolean) => {
|
|
99
481
|
let queries: Query[];
|
|
100
482
|
if (direction === "below") {
|
|
@@ -140,28 +522,38 @@ const getClosest = (
|
|
|
140
522
|
return queries;
|
|
141
523
|
};
|
|
142
524
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
525
|
+
const sortByOldest = new Sort({ key: "timestamp", direction: "asc" });
|
|
526
|
+
const sortByHash = new Sort({ key: "hash", direction: "asc" }); // when breaking even
|
|
527
|
+
|
|
528
|
+
const iterator = rects.iterate(
|
|
529
|
+
{
|
|
146
530
|
query: createQueries(point, false),
|
|
147
|
-
sort:
|
|
531
|
+
sort: [
|
|
148
532
|
direction === "below"
|
|
149
533
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
150
534
|
: new Sort({ key: ["start1"], direction: "asc" }),
|
|
151
|
-
|
|
535
|
+
sortByOldest,
|
|
536
|
+
sortByHash,
|
|
537
|
+
],
|
|
538
|
+
},
|
|
539
|
+
options,
|
|
152
540
|
);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
541
|
+
|
|
542
|
+
const iteratorWrapped = rects.iterate(
|
|
543
|
+
{
|
|
156
544
|
query: createQueries(direction === "below" ? MAX_U32 : 0, true),
|
|
157
|
-
sort:
|
|
545
|
+
sort: [
|
|
158
546
|
direction === "below"
|
|
159
547
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
160
548
|
: new Sort({ key: ["start1"], direction: "asc" }),
|
|
161
|
-
|
|
549
|
+
sortByOldest,
|
|
550
|
+
sortByHash,
|
|
551
|
+
],
|
|
552
|
+
},
|
|
553
|
+
options,
|
|
162
554
|
);
|
|
163
555
|
|
|
164
|
-
return joinIterator([iterator, iteratorWrapped], point, direction);
|
|
556
|
+
return joinIterator<S>([iterator, iteratorWrapped], point, direction);
|
|
165
557
|
};
|
|
166
558
|
|
|
167
559
|
export const hasCoveringRange = async (
|
|
@@ -169,75 +561,73 @@ export const hasCoveringRange = async (
|
|
|
169
561
|
range: ReplicationRangeIndexable,
|
|
170
562
|
) => {
|
|
171
563
|
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
|
-
]),
|
|
564
|
+
(await rects.count({
|
|
565
|
+
query: [
|
|
566
|
+
new Or([
|
|
567
|
+
new And([
|
|
568
|
+
new IntegerCompare({
|
|
569
|
+
key: "start1",
|
|
570
|
+
compare: Compare.LessOrEqual,
|
|
571
|
+
value: range.start1,
|
|
572
|
+
}),
|
|
573
|
+
new IntegerCompare({
|
|
574
|
+
key: "end1",
|
|
575
|
+
compare: Compare.GreaterOrEqual,
|
|
576
|
+
value: range.end1,
|
|
577
|
+
}),
|
|
200
578
|
]),
|
|
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
|
-
]),
|
|
579
|
+
new And([
|
|
580
|
+
new IntegerCompare({
|
|
581
|
+
key: "start2",
|
|
582
|
+
compare: Compare.LessOrEqual,
|
|
583
|
+
value: range.start1,
|
|
584
|
+
}),
|
|
585
|
+
new IntegerCompare({
|
|
586
|
+
key: "end2",
|
|
587
|
+
compare: Compare.GreaterOrEqual,
|
|
588
|
+
value: range.end1,
|
|
589
|
+
}),
|
|
226
590
|
]),
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
key: "id",
|
|
235
|
-
value: range.id,
|
|
591
|
+
]),
|
|
592
|
+
new Or([
|
|
593
|
+
new And([
|
|
594
|
+
new IntegerCompare({
|
|
595
|
+
key: "start1",
|
|
596
|
+
compare: Compare.LessOrEqual,
|
|
597
|
+
value: range.start2,
|
|
236
598
|
}),
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
599
|
+
new IntegerCompare({
|
|
600
|
+
key: "end1",
|
|
601
|
+
compare: Compare.GreaterOrEqual,
|
|
602
|
+
value: range.end2,
|
|
603
|
+
}),
|
|
604
|
+
]),
|
|
605
|
+
new And([
|
|
606
|
+
new IntegerCompare({
|
|
607
|
+
key: "start2",
|
|
608
|
+
compare: Compare.LessOrEqual,
|
|
609
|
+
value: range.start2,
|
|
610
|
+
}),
|
|
611
|
+
new IntegerCompare({
|
|
612
|
+
key: "end2",
|
|
613
|
+
compare: Compare.GreaterOrEqual,
|
|
614
|
+
value: range.end2,
|
|
615
|
+
}),
|
|
616
|
+
]),
|
|
617
|
+
]),
|
|
618
|
+
new StringMatch({
|
|
619
|
+
key: "hash",
|
|
620
|
+
value: range.hash,
|
|
621
|
+
}),
|
|
622
|
+
// assume that we are looking for other ranges, not want to update an existing one
|
|
623
|
+
new Not(
|
|
624
|
+
new ByteMatchQuery({
|
|
625
|
+
key: "id",
|
|
626
|
+
value: range.id,
|
|
627
|
+
}),
|
|
628
|
+
),
|
|
629
|
+
],
|
|
630
|
+
})) > 0
|
|
241
631
|
);
|
|
242
632
|
};
|
|
243
633
|
|
|
@@ -280,15 +670,14 @@ export const getDistance = (
|
|
|
280
670
|
throw new Error("Invalid direction");
|
|
281
671
|
};
|
|
282
672
|
|
|
283
|
-
const joinIterator = (
|
|
284
|
-
iterators: IndexIterator<ReplicationRangeIndexable>[],
|
|
673
|
+
const joinIterator = <S extends Shape | undefined = undefined>(
|
|
674
|
+
iterators: IndexIterator<ReplicationRangeIndexable, S>[],
|
|
285
675
|
point: number,
|
|
286
676
|
direction: "above" | "below" | "closest",
|
|
287
|
-
) => {
|
|
677
|
+
): IndexIterator<ReplicationRangeIndexable, S> => {
|
|
288
678
|
let queues: {
|
|
289
|
-
kept: number;
|
|
290
679
|
elements: {
|
|
291
|
-
result: IndexedResult<ReplicationRangeIndexable
|
|
680
|
+
result: IndexedResult<ReturnTypeFromShape<ReplicationRangeIndexable, S>>;
|
|
292
681
|
dist: number;
|
|
293
682
|
}[];
|
|
294
683
|
}[] = [];
|
|
@@ -296,23 +685,23 @@ const joinIterator = (
|
|
|
296
685
|
return {
|
|
297
686
|
next: async (
|
|
298
687
|
count: number,
|
|
299
|
-
): Promise<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
688
|
+
): Promise<
|
|
689
|
+
IndexedResults<ReturnTypeFromShape<ReplicationRangeIndexable, S>>
|
|
690
|
+
> => {
|
|
691
|
+
let results: IndexedResults<
|
|
692
|
+
ReturnTypeFromShape<ReplicationRangeIndexable, S>
|
|
693
|
+
> = [];
|
|
304
694
|
for (let i = 0; i < iterators.length; i++) {
|
|
305
695
|
let queue = queues[i];
|
|
306
696
|
if (!queue) {
|
|
307
|
-
queue = { elements: []
|
|
697
|
+
queue = { elements: [] };
|
|
308
698
|
queues[i] = queue;
|
|
309
699
|
}
|
|
310
700
|
let iterator = iterators[i];
|
|
311
|
-
if (queue.elements.length < count && iterator.done()
|
|
701
|
+
if (queue.elements.length < count && iterator.done() !== true) {
|
|
312
702
|
let res = await iterator.next(count);
|
|
313
|
-
queue.kept = res.kept;
|
|
314
703
|
|
|
315
|
-
for (const el of res
|
|
704
|
+
for (const el of res) {
|
|
316
705
|
const closest = el.value;
|
|
317
706
|
|
|
318
707
|
let dist: number;
|
|
@@ -356,24 +745,25 @@ const joinIterator = (
|
|
|
356
745
|
|
|
357
746
|
let closest = queues[closestQueue]?.elements.shift();
|
|
358
747
|
if (closest) {
|
|
359
|
-
results.
|
|
748
|
+
results.push(closest.result);
|
|
360
749
|
}
|
|
361
750
|
}
|
|
362
|
-
|
|
363
|
-
for (let i = 0; i < queues.length; i++) {
|
|
364
|
-
results.kept += queues[i].elements.length + queues[i].kept;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
751
|
return results;
|
|
368
752
|
},
|
|
369
|
-
|
|
753
|
+
pending: async () => {
|
|
754
|
+
let allPending = await Promise.all(iterators.map((x) => x.pending()));
|
|
755
|
+
return allPending.reduce((acc, x) => acc + x, 0);
|
|
756
|
+
},
|
|
757
|
+
done: () => iterators.every((x) => x.done() === true),
|
|
370
758
|
close: async () => {
|
|
371
759
|
for (const iterator of iterators) {
|
|
372
760
|
await iterator.close();
|
|
373
761
|
}
|
|
374
762
|
},
|
|
375
763
|
all: async () => {
|
|
376
|
-
let results: IndexedResult<
|
|
764
|
+
let results: IndexedResult<
|
|
765
|
+
ReturnTypeFromShape<ReplicationRangeIndexable, S>
|
|
766
|
+
>[] = [];
|
|
377
767
|
for (const iterator of iterators) {
|
|
378
768
|
let res = await iterator.all();
|
|
379
769
|
results.push(...res);
|
|
@@ -383,15 +773,18 @@ const joinIterator = (
|
|
|
383
773
|
};
|
|
384
774
|
};
|
|
385
775
|
|
|
386
|
-
const getClosestAround =
|
|
776
|
+
const getClosestAround = <
|
|
777
|
+
S extends (Shape & { timestamp: true }) | undefined = undefined,
|
|
778
|
+
>(
|
|
387
779
|
peers: Index<ReplicationRangeIndexable>,
|
|
388
780
|
point: number,
|
|
389
781
|
roleAge: number,
|
|
390
782
|
now: number,
|
|
391
783
|
includeStrictBelow: boolean,
|
|
392
784
|
includeStrictAbove: boolean,
|
|
785
|
+
options?: { shape?: S },
|
|
393
786
|
) => {
|
|
394
|
-
const closestBelow = getClosest(
|
|
787
|
+
const closestBelow = getClosest<S>(
|
|
395
788
|
"below",
|
|
396
789
|
peers,
|
|
397
790
|
point,
|
|
@@ -399,8 +792,9 @@ const getClosestAround = (
|
|
|
399
792
|
true,
|
|
400
793
|
now,
|
|
401
794
|
includeStrictBelow,
|
|
795
|
+
options,
|
|
402
796
|
);
|
|
403
|
-
const closestAbove = getClosest(
|
|
797
|
+
const closestAbove = getClosest<S>(
|
|
404
798
|
"above",
|
|
405
799
|
peers,
|
|
406
800
|
point,
|
|
@@ -408,45 +802,79 @@ const getClosestAround = (
|
|
|
408
802
|
true,
|
|
409
803
|
now,
|
|
410
804
|
includeStrictAbove,
|
|
805
|
+
options,
|
|
806
|
+
);
|
|
807
|
+
const containing = containingPoint<S>(
|
|
808
|
+
peers,
|
|
809
|
+
point,
|
|
810
|
+
roleAge,
|
|
811
|
+
true,
|
|
812
|
+
now,
|
|
813
|
+
options,
|
|
411
814
|
);
|
|
412
|
-
const containing = containingPoint(peers, point, roleAge, true, now);
|
|
413
815
|
|
|
414
816
|
return iteratorInSeries(
|
|
415
817
|
containing,
|
|
416
|
-
joinIterator([closestBelow, closestAbove], point, "closest"),
|
|
818
|
+
joinIterator<S>([closestBelow, closestAbove], point, "closest"),
|
|
417
819
|
);
|
|
418
820
|
};
|
|
419
821
|
|
|
420
822
|
const collectNodesAroundPoint = async (
|
|
421
823
|
roleAge: number,
|
|
422
824
|
peers: Index<ReplicationRangeIndexable>,
|
|
423
|
-
collector: (
|
|
825
|
+
collector: (
|
|
826
|
+
rect: { hash: string },
|
|
827
|
+
matured: boolean,
|
|
828
|
+
interescting: boolean,
|
|
829
|
+
) => void,
|
|
424
830
|
point: u32,
|
|
425
831
|
now: number,
|
|
426
832
|
done: () => boolean = () => true,
|
|
427
833
|
) => {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
834
|
+
/* let shape = { timestamp: true, hash: true } as const */
|
|
835
|
+
const containing = containingPoint(
|
|
836
|
+
peers,
|
|
837
|
+
point,
|
|
838
|
+
0,
|
|
839
|
+
true,
|
|
840
|
+
now /* , { shape } */,
|
|
841
|
+
);
|
|
842
|
+
const allContaining = await containing.all();
|
|
843
|
+
for (const rect of allContaining) {
|
|
844
|
+
collector(rect.value, isMatured(rect.value, now, roleAge), true);
|
|
433
845
|
}
|
|
434
846
|
|
|
435
847
|
if (done()) {
|
|
436
848
|
return;
|
|
437
849
|
}
|
|
438
850
|
|
|
439
|
-
const closestBelow = getClosest(
|
|
440
|
-
|
|
851
|
+
const closestBelow = getClosest(
|
|
852
|
+
"below",
|
|
853
|
+
peers,
|
|
854
|
+
point,
|
|
855
|
+
0,
|
|
856
|
+
true,
|
|
857
|
+
now,
|
|
858
|
+
false /* , { shape } */,
|
|
859
|
+
);
|
|
860
|
+
const closestAbove = getClosest(
|
|
861
|
+
"above",
|
|
862
|
+
peers,
|
|
863
|
+
point,
|
|
864
|
+
0,
|
|
865
|
+
true,
|
|
866
|
+
now,
|
|
867
|
+
false /* , { shape } */,
|
|
868
|
+
);
|
|
441
869
|
const aroundIterator = joinIterator(
|
|
442
870
|
[closestBelow, closestAbove],
|
|
443
871
|
point,
|
|
444
872
|
"closest",
|
|
445
873
|
);
|
|
446
|
-
while (aroundIterator.done()
|
|
874
|
+
while (aroundIterator.done() !== true && done() !== true) {
|
|
447
875
|
const res = await aroundIterator.next(1);
|
|
448
|
-
for (const rect of res
|
|
449
|
-
collector(rect.value, isMatured(rect.value, now, roleAge));
|
|
876
|
+
for (const rect of res) {
|
|
877
|
+
collector(rect.value, isMatured(rect.value, now, roleAge), false);
|
|
450
878
|
if (done()) {
|
|
451
879
|
return;
|
|
452
880
|
}
|
|
@@ -454,6 +882,14 @@ const collectNodesAroundPoint = async (
|
|
|
454
882
|
}
|
|
455
883
|
};
|
|
456
884
|
|
|
885
|
+
export const getEvenlySpacedU32 = (from: number, count: number) => {
|
|
886
|
+
let ret: number[] = new Array(count);
|
|
887
|
+
for (let i = 0; i < count; i++) {
|
|
888
|
+
ret[i] = Math.round(from + (i * MAX_U32) / count) % MAX_U32;
|
|
889
|
+
}
|
|
890
|
+
return ret;
|
|
891
|
+
};
|
|
892
|
+
|
|
457
893
|
export const isMatured = (
|
|
458
894
|
segment: { timestamp: bigint },
|
|
459
895
|
now: number,
|
|
@@ -461,44 +897,42 @@ export const isMatured = (
|
|
|
461
897
|
) => {
|
|
462
898
|
return now - Number(segment.timestamp) >= minAge;
|
|
463
899
|
};
|
|
464
|
-
|
|
900
|
+
// get peer sample that are responsible for the cursor point
|
|
901
|
+
// will return a list of peers that want to replicate the data,
|
|
902
|
+
// but also if necessary a list of peers that are responsible for the data
|
|
903
|
+
// but have not explicitly replicating a range that cover the cursor point
|
|
465
904
|
export const getSamples = async (
|
|
466
|
-
cursor: u32,
|
|
905
|
+
cursor: u32[],
|
|
467
906
|
peers: Index<ReplicationRangeIndexable>,
|
|
468
|
-
amount: number,
|
|
469
907
|
roleAge: number,
|
|
470
|
-
) => {
|
|
471
|
-
const leaders:
|
|
908
|
+
): Promise<Map<string, { intersecting: boolean }>> => {
|
|
909
|
+
const leaders: Map<string, { intersecting: boolean }> = new Map();
|
|
472
910
|
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 [];
|
|
911
|
+
return new Map();
|
|
482
912
|
}
|
|
483
913
|
|
|
484
914
|
const now = +new Date();
|
|
485
915
|
|
|
486
916
|
const maturedLeaders = new Set();
|
|
487
|
-
for (let i = 0; i <
|
|
917
|
+
for (let i = 0; i < cursor.length; i++) {
|
|
488
918
|
// evenly distributed
|
|
489
|
-
const point = Math.round(cursor + (i * MAX_U32) / amount) % MAX_U32;
|
|
490
919
|
|
|
491
920
|
// aquire at least one unique node for each point
|
|
492
921
|
await collectNodesAroundPoint(
|
|
493
922
|
roleAge,
|
|
494
923
|
peers,
|
|
495
|
-
(rect, m) => {
|
|
924
|
+
(rect, m, intersecting) => {
|
|
496
925
|
if (m) {
|
|
497
926
|
maturedLeaders.add(rect.hash);
|
|
498
927
|
}
|
|
499
|
-
|
|
928
|
+
|
|
929
|
+
const prev = leaders.get(rect.hash);
|
|
930
|
+
|
|
931
|
+
if (!prev || (intersecting && !prev.intersecting)) {
|
|
932
|
+
leaders.set(rect.hash, { intersecting });
|
|
933
|
+
}
|
|
500
934
|
},
|
|
501
|
-
|
|
935
|
+
cursor[i],
|
|
502
936
|
now,
|
|
503
937
|
() => {
|
|
504
938
|
if (maturedLeaders.size > i) {
|
|
@@ -509,13 +943,15 @@ export const getSamples = async (
|
|
|
509
943
|
);
|
|
510
944
|
}
|
|
511
945
|
|
|
512
|
-
return
|
|
946
|
+
return leaders;
|
|
513
947
|
};
|
|
514
948
|
|
|
515
|
-
const fetchOne = async
|
|
949
|
+
const fetchOne = async <S extends Shape | undefined>(
|
|
950
|
+
iterator: IndexIterator<ReplicationRangeIndexable, S>,
|
|
951
|
+
) => {
|
|
516
952
|
const value = await iterator.next(1);
|
|
517
953
|
await iterator.close();
|
|
518
|
-
return value
|
|
954
|
+
return value[0]?.value;
|
|
519
955
|
};
|
|
520
956
|
|
|
521
957
|
export const minimumWidthToCover = async (
|
|
@@ -566,22 +1002,21 @@ export const getCoverSet = async (properties: {
|
|
|
566
1002
|
const eagerFetch =
|
|
567
1003
|
properties.eager === true
|
|
568
1004
|
? 1000
|
|
569
|
-
: properties.eager.unmaturedFetchCoverSize;
|
|
1005
|
+
: (properties.eager.unmaturedFetchCoverSize ?? 1000);
|
|
570
1006
|
|
|
571
1007
|
// pull all umatured
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
)
|
|
584
|
-
for (const rect of rects.results) {
|
|
1008
|
+
const iterator = peers.iterate({
|
|
1009
|
+
query: [
|
|
1010
|
+
new IntegerCompare({
|
|
1011
|
+
key: "timestamp",
|
|
1012
|
+
compare: Compare.GreaterOrEqual,
|
|
1013
|
+
value: BigInt(now - roleAge),
|
|
1014
|
+
}),
|
|
1015
|
+
],
|
|
1016
|
+
});
|
|
1017
|
+
const rects = await iterator.next(eagerFetch);
|
|
1018
|
+
await iterator.close();
|
|
1019
|
+
for (const rect of rects) {
|
|
585
1020
|
ret.add(rect.value.hash);
|
|
586
1021
|
}
|
|
587
1022
|
}
|
|
@@ -713,24 +1148,155 @@ export const getCoverSet = async (properties: {
|
|
|
713
1148
|
start instanceof PublicSignKey && ret.add(start.hashcode());
|
|
714
1149
|
return ret;
|
|
715
1150
|
};
|
|
1151
|
+
/* export const getReplicationDiff = (changes: ReplicationChange) => {
|
|
1152
|
+
// reduce the change set to only regions that are changed for each peer
|
|
1153
|
+
// i.e. subtract removed regions from added regions, and vice versa
|
|
1154
|
+
const result = new Map<string, { range: ReplicationRangeIndexable, added: boolean }[]>();
|
|
1155
|
+
|
|
1156
|
+
for (const addedChange of changes.added ?? []) {
|
|
1157
|
+
let prev = result.get(addedChange.hash) ?? [];
|
|
1158
|
+
for (const [_hash, ranges] of result.entries()) {
|
|
1159
|
+
for (const r of ranges) {
|
|
1160
|
+
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
*/
|
|
1166
|
+
|
|
1167
|
+
const matchRangeQuery = (range: ReplicationRangeIndexable) => {
|
|
1168
|
+
let ors = [];
|
|
1169
|
+
ors.push(
|
|
1170
|
+
new And([
|
|
1171
|
+
new IntegerCompare({
|
|
1172
|
+
key: "coordinate",
|
|
1173
|
+
compare: "gte",
|
|
1174
|
+
value: range.start1,
|
|
1175
|
+
}),
|
|
1176
|
+
new IntegerCompare({
|
|
1177
|
+
key: "coordinate",
|
|
1178
|
+
compare: "lt",
|
|
1179
|
+
value: range.end1,
|
|
1180
|
+
}),
|
|
1181
|
+
]),
|
|
1182
|
+
);
|
|
716
1183
|
|
|
717
|
-
|
|
1184
|
+
ors.push(
|
|
1185
|
+
new And([
|
|
1186
|
+
new IntegerCompare({
|
|
1187
|
+
key: "coordinate",
|
|
1188
|
+
compare: "gte",
|
|
1189
|
+
value: range.start2,
|
|
1190
|
+
}),
|
|
1191
|
+
new IntegerCompare({
|
|
1192
|
+
key: "coordinate",
|
|
1193
|
+
compare: "lt",
|
|
1194
|
+
value: range.end2,
|
|
1195
|
+
}),
|
|
1196
|
+
]),
|
|
1197
|
+
);
|
|
1198
|
+
|
|
1199
|
+
return new Or(ors);
|
|
1200
|
+
};
|
|
1201
|
+
export const toRebalance = (
|
|
1202
|
+
changes: ReplicationChanges,
|
|
1203
|
+
index: Index<EntryReplicated>,
|
|
1204
|
+
): AsyncIterable<{ gid: string; entries: EntryReplicated[] }> => {
|
|
1205
|
+
const assignedRangesQuery = (changes: ReplicationChanges) => {
|
|
1206
|
+
let ors: Query[] = [];
|
|
1207
|
+
for (const change of changes) {
|
|
1208
|
+
const matchRange = matchRangeQuery(change.range);
|
|
1209
|
+
if (change.type === "updated") {
|
|
1210
|
+
// assuming a range is to be removed, is this entry still enoughly replicated
|
|
1211
|
+
const prevMatchRange = matchRangeQuery(change.prev);
|
|
1212
|
+
ors.push(prevMatchRange);
|
|
1213
|
+
ors.push(matchRange);
|
|
1214
|
+
|
|
1215
|
+
/* ors.push(
|
|
1216
|
+
new And([
|
|
1217
|
+
// not sufficiently replicated
|
|
1218
|
+
new IntegerCompare({
|
|
1219
|
+
key: "replicatorsMissingShifted",
|
|
1220
|
+
compare: Compare.Greater,
|
|
1221
|
+
value: HALF_MAX_U32 - 1, // + 1 since we are going to remove a replicator for this entry
|
|
1222
|
+
}),
|
|
1223
|
+
|
|
1224
|
+
// is not the current range
|
|
1225
|
+
new Not(matchRange),
|
|
1226
|
+
|
|
1227
|
+
// but was in the the previous range
|
|
1228
|
+
prevMatchRange,
|
|
1229
|
+
]),
|
|
1230
|
+
); */
|
|
1231
|
+
} else {
|
|
1232
|
+
ors.push(matchRange);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// entry is assigned to a range boundary, meaning it is due to be inspected
|
|
1237
|
+
ors.push(
|
|
1238
|
+
new BoolQuery({
|
|
1239
|
+
key: "assignedToRangeBoundary",
|
|
1240
|
+
value: true,
|
|
1241
|
+
}),
|
|
1242
|
+
);
|
|
1243
|
+
|
|
1244
|
+
// entry is not sufficiently replicated, and we are to still keep it
|
|
1245
|
+
return new Or(ors);
|
|
1246
|
+
};
|
|
1247
|
+
|
|
1248
|
+
return {
|
|
1249
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1250
|
+
const iterator = index.iterate({
|
|
1251
|
+
query: assignedRangesQuery(changes),
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
while (iterator.done() !== true) {
|
|
1255
|
+
const entries = await iterator.next(1000); // TODO choose right batch sizes here for optimal memory usage / speed
|
|
1256
|
+
|
|
1257
|
+
// TODO do we need this
|
|
1258
|
+
const grouped = await groupByGidSync(entries.map((x) => x.value));
|
|
1259
|
+
|
|
1260
|
+
for (const [gid, entries] of grouped.entries()) {
|
|
1261
|
+
yield { gid, entries };
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
},
|
|
1265
|
+
};
|
|
1266
|
+
};
|
|
1267
|
+
|
|
1268
|
+
export const fetchOneFromPublicKey = async <
|
|
1269
|
+
S extends (Shape & { timestamp: true }) | undefined = undefined,
|
|
1270
|
+
>(
|
|
718
1271
|
publicKey: PublicSignKey,
|
|
719
1272
|
index: Index<ReplicationRangeIndexable>,
|
|
720
1273
|
roleAge: number,
|
|
721
1274
|
now: number,
|
|
1275
|
+
options?: {
|
|
1276
|
+
shape: S;
|
|
1277
|
+
},
|
|
722
1278
|
) => {
|
|
723
|
-
let
|
|
724
|
-
|
|
1279
|
+
let iterator = index.iterate<S>(
|
|
1280
|
+
{
|
|
725
1281
|
query: [new StringMatch({ key: "hash", value: publicKey.hashcode() })],
|
|
726
|
-
|
|
727
|
-
|
|
1282
|
+
},
|
|
1283
|
+
options,
|
|
728
1284
|
);
|
|
729
|
-
let
|
|
1285
|
+
let result = await iterator.next(1);
|
|
1286
|
+
await iterator.close();
|
|
1287
|
+
let node = result[0]?.value;
|
|
730
1288
|
if (node) {
|
|
731
1289
|
if (!isMatured(node, now, roleAge)) {
|
|
732
1290
|
const matured = await fetchOne(
|
|
733
|
-
getClosestAround(
|
|
1291
|
+
getClosestAround<S>(
|
|
1292
|
+
index,
|
|
1293
|
+
node.start1,
|
|
1294
|
+
roleAge,
|
|
1295
|
+
now,
|
|
1296
|
+
false,
|
|
1297
|
+
false,
|
|
1298
|
+
options,
|
|
1299
|
+
),
|
|
734
1300
|
);
|
|
735
1301
|
if (matured) {
|
|
736
1302
|
node = matured;
|
|
@@ -740,16 +1306,24 @@ export const fetchOneFromPublicKey = async (
|
|
|
740
1306
|
return node;
|
|
741
1307
|
};
|
|
742
1308
|
|
|
743
|
-
export const getStartAndEnd = async
|
|
1309
|
+
export const getStartAndEnd = async <
|
|
1310
|
+
S extends (Shape & { timestamp: true }) | undefined,
|
|
1311
|
+
>(
|
|
744
1312
|
peers: Index<ReplicationRangeIndexable>,
|
|
745
1313
|
start: number | PublicSignKey | undefined | undefined,
|
|
746
1314
|
widthToCoverScaled: number,
|
|
747
1315
|
roleAge: number,
|
|
748
1316
|
now: number,
|
|
749
1317
|
intervalWidth: number,
|
|
750
|
-
|
|
1318
|
+
options?: { shape: S },
|
|
1319
|
+
): Promise<{
|
|
1320
|
+
startNode: ReturnTypeFromShape<ReplicationRangeIndexable, S> | undefined;
|
|
1321
|
+
startLocation: number;
|
|
1322
|
+
endLocation: number;
|
|
1323
|
+
}> => {
|
|
751
1324
|
// find a good starting point
|
|
752
|
-
let startNode: ReplicationRangeIndexable | undefined =
|
|
1325
|
+
let startNode: ReturnTypeFromShape<ReplicationRangeIndexable, S> | undefined =
|
|
1326
|
+
undefined;
|
|
753
1327
|
let startLocation: number | undefined = undefined;
|
|
754
1328
|
|
|
755
1329
|
const nodeFromPoint = async (point = scaleToU32(Math.random())) => {
|
|
@@ -761,12 +1335,19 @@ export const getStartAndEnd = async (
|
|
|
761
1335
|
now,
|
|
762
1336
|
false,
|
|
763
1337
|
true,
|
|
1338
|
+
options,
|
|
764
1339
|
);
|
|
765
1340
|
};
|
|
766
1341
|
|
|
767
1342
|
if (start instanceof PublicSignKey) {
|
|
768
1343
|
// start at our node (local first)
|
|
769
|
-
startNode = await fetchOneFromPublicKey(
|
|
1344
|
+
startNode = await fetchOneFromPublicKey(
|
|
1345
|
+
start,
|
|
1346
|
+
peers,
|
|
1347
|
+
roleAge,
|
|
1348
|
+
now,
|
|
1349
|
+
options,
|
|
1350
|
+
);
|
|
770
1351
|
if (!startNode) {
|
|
771
1352
|
// fetch randomly
|
|
772
1353
|
await nodeFromPoint();
|
|
@@ -810,22 +1391,26 @@ export const getStartAndEnd = async (
|
|
|
810
1391
|
};
|
|
811
1392
|
};
|
|
812
1393
|
|
|
813
|
-
export const fetchOneClosest =
|
|
1394
|
+
export const fetchOneClosest = <
|
|
1395
|
+
S extends (Shape & { timestamp: true }) | undefined = undefined,
|
|
1396
|
+
>(
|
|
814
1397
|
peers: Index<ReplicationRangeIndexable>,
|
|
815
1398
|
point: number,
|
|
816
1399
|
roleAge: number,
|
|
817
1400
|
now: number,
|
|
818
1401
|
includeStrictBelow: boolean,
|
|
819
1402
|
includeStrictAbove: boolean,
|
|
1403
|
+
options?: { shape?: S },
|
|
820
1404
|
) => {
|
|
821
1405
|
return fetchOne(
|
|
822
|
-
getClosestAround(
|
|
1406
|
+
getClosestAround<S>(
|
|
823
1407
|
peers,
|
|
824
1408
|
point,
|
|
825
1409
|
roleAge,
|
|
826
1410
|
now,
|
|
827
1411
|
includeStrictBelow,
|
|
828
1412
|
includeStrictAbove,
|
|
1413
|
+
options,
|
|
829
1414
|
),
|
|
830
1415
|
);
|
|
831
1416
|
};
|