@peerbit/shared-log 9.2.13 → 10.0.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 +190 -64
- package/dist/benchmark/get-samples.js.map +1 -1
- package/dist/benchmark/index.js +16 -38
- package/dist/benchmark/index.js.map +1 -1
- package/dist/benchmark/memory/child.js.map +1 -1
- package/dist/benchmark/partial-sync.d.ts +3 -0
- package/dist/benchmark/partial-sync.d.ts.map +1 -0
- package/dist/benchmark/partial-sync.js +121 -0
- package/dist/benchmark/partial-sync.js.map +1 -0
- package/dist/benchmark/replication-prune.js.map +1 -1
- package/dist/benchmark/replication.js.map +1 -1
- package/dist/benchmark/to-rebalance.d.ts +2 -0
- package/dist/benchmark/to-rebalance.d.ts.map +1 -0
- package/dist/benchmark/to-rebalance.js +117 -0
- package/dist/benchmark/to-rebalance.js.map +1 -0
- package/dist/benchmark/utils.d.ts +24 -0
- package/dist/benchmark/utils.d.ts.map +1 -0
- package/dist/benchmark/utils.js +47 -0
- package/dist/benchmark/utils.js.map +1 -0
- package/dist/src/debounce.d.ts +2 -2
- package/dist/src/debounce.d.ts.map +1 -1
- package/dist/src/debounce.js +17 -47
- package/dist/src/debounce.js.map +1 -1
- package/dist/src/exchange-heads.d.ts +1 -13
- package/dist/src/exchange-heads.d.ts.map +1 -1
- package/dist/src/exchange-heads.js +0 -32
- package/dist/src/exchange-heads.js.map +1 -1
- package/dist/src/index.d.ts +119 -60
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1116 -762
- package/dist/src/index.js.map +1 -1
- package/dist/src/integers.d.ts +22 -0
- package/dist/src/integers.d.ts.map +1 -0
- package/dist/src/integers.js +76 -0
- package/dist/src/integers.js.map +1 -0
- package/dist/src/pid.d.ts.map +1 -1
- package/dist/src/pid.js +22 -22
- package/dist/src/pid.js.map +1 -1
- package/dist/src/ranges.d.ts +168 -38
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +869 -272
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/replication-domain-hash.d.ts +2 -3
- package/dist/src/replication-domain-hash.d.ts.map +1 -1
- package/dist/src/replication-domain-hash.js +40 -15
- package/dist/src/replication-domain-hash.js.map +1 -1
- package/dist/src/replication-domain-time.d.ts +5 -5
- package/dist/src/replication-domain-time.d.ts.map +1 -1
- package/dist/src/replication-domain-time.js +2 -0
- package/dist/src/replication-domain-time.js.map +1 -1
- package/dist/src/replication-domain.d.ts +17 -19
- package/dist/src/replication-domain.d.ts.map +1 -1
- package/dist/src/replication-domain.js +2 -6
- package/dist/src/replication-domain.js.map +1 -1
- package/dist/src/replication.d.ts +6 -6
- package/dist/src/replication.d.ts.map +1 -1
- package/dist/src/replication.js +4 -4
- package/dist/src/replication.js.map +1 -1
- package/dist/src/role.d.ts +3 -6
- package/dist/src/role.d.ts.map +1 -1
- package/dist/src/role.js +4 -5
- package/dist/src/role.js.map +1 -1
- package/dist/src/sync/index.d.ts +40 -0
- package/dist/src/sync/index.d.ts.map +1 -0
- package/dist/src/sync/index.js +2 -0
- package/dist/src/sync/index.js.map +1 -0
- package/dist/src/sync/rateless-iblt.d.ts +124 -0
- package/dist/src/sync/rateless-iblt.d.ts.map +1 -0
- package/dist/src/sync/rateless-iblt.js +495 -0
- package/dist/src/sync/rateless-iblt.js.map +1 -0
- package/dist/src/sync/simple.d.ts +69 -0
- package/dist/src/sync/simple.d.ts.map +1 -0
- package/dist/src/sync/simple.js +338 -0
- package/dist/src/sync/simple.js.map +1 -0
- package/dist/src/sync/wasm-init.browser.d.ts +1 -0
- package/dist/src/sync/wasm-init.browser.d.ts.map +1 -0
- package/dist/src/sync/wasm-init.browser.js +3 -0
- package/dist/src/sync/wasm-init.browser.js.map +1 -0
- package/dist/src/sync/wasm-init.d.ts +2 -0
- package/dist/src/sync/wasm-init.d.ts.map +1 -0
- package/dist/src/sync/wasm-init.js +13 -0
- package/dist/src/sync/wasm-init.js.map +1 -0
- package/dist/src/utils.d.ts +3 -3
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +2 -2
- package/dist/src/utils.js.map +1 -1
- package/package.json +10 -6
- package/src/debounce.ts +16 -51
- package/src/exchange-heads.ts +1 -23
- package/src/index.ts +1532 -1038
- package/src/integers.ts +102 -0
- package/src/pid.ts +23 -22
- package/src/ranges.ts +1204 -413
- package/src/replication-domain-hash.ts +43 -18
- package/src/replication-domain-time.ts +9 -9
- package/src/replication-domain.ts +21 -31
- package/src/replication.ts +10 -9
- package/src/role.ts +4 -6
- package/src/sync/index.ts +51 -0
- package/src/sync/rateless-iblt.ts +617 -0
- package/src/sync/simple.ts +403 -0
- package/src/sync/wasm-init.browser.ts +1 -0
- package/src/sync/wasm-init.ts +14 -0
- package/src/utils.ts +10 -4
package/src/ranges.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { deserialize, field, serialize, variant } from "@dao-xyz/borsh";
|
|
2
|
-
import {
|
|
1
|
+
import { deserialize, field, serialize, variant, vec } from "@dao-xyz/borsh";
|
|
2
|
+
import {
|
|
3
|
+
PublicSignKey,
|
|
4
|
+
equals,
|
|
5
|
+
randomBytes,
|
|
6
|
+
sha256Base64Sync,
|
|
7
|
+
toBase64,
|
|
8
|
+
} from "@peerbit/crypto";
|
|
3
9
|
import {
|
|
4
10
|
And,
|
|
5
11
|
BoolQuery,
|
|
@@ -18,30 +24,48 @@ import {
|
|
|
18
24
|
Sort,
|
|
19
25
|
SortDirection,
|
|
20
26
|
StringMatch,
|
|
21
|
-
iteratorInSeries,
|
|
27
|
+
/* iteratorInSeries, */
|
|
22
28
|
} from "@peerbit/indexer-interface";
|
|
23
29
|
import { id } from "@peerbit/indexer-interface";
|
|
24
30
|
import { Meta, ShallowMeta } from "@peerbit/log";
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
import {
|
|
32
|
+
MAX_U32,
|
|
33
|
+
MAX_U64,
|
|
34
|
+
type NumberFromType,
|
|
35
|
+
type Numbers,
|
|
36
|
+
} from "./integers.js";
|
|
37
|
+
import { type ReplicationChanges } from "./replication-domain.js";
|
|
28
38
|
|
|
29
39
|
export enum ReplicationIntent {
|
|
30
40
|
NonStrict = 0, // indicates that the segment will be replicated and nearby data might be replicated as well
|
|
31
41
|
Strict = 1, // only replicate data in the segment to the specified replicator, not any other data
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
export enum SyncStatus {
|
|
45
|
+
Unsynced = 0,
|
|
46
|
+
Synced = 1,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const min = (a: number | bigint, b: number | bigint) => (a < b ? a : b);
|
|
50
|
+
|
|
51
|
+
const getSegmentsFromOffsetAndRange = <T extends number | bigint>(
|
|
52
|
+
offset: T,
|
|
53
|
+
factor: T,
|
|
54
|
+
zero: T,
|
|
55
|
+
max: T,
|
|
56
|
+
): [[T, T], [T, T]] => {
|
|
38
57
|
let start1 = offset;
|
|
58
|
+
// @ts-ignore
|
|
39
59
|
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 =
|
|
60
|
+
let end1: T = min(end1Unscaled, max) as T;
|
|
41
61
|
return [
|
|
42
62
|
[start1, end1],
|
|
43
|
-
|
|
44
|
-
|
|
63
|
+
/* eslint-disable no-irregular-whitespace */
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
end1Unscaled > max
|
|
66
|
+
? /* eslint-disable no-irregular-whitespace */
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
[zero, (factor !== max ? offset + factor : offset) % max]
|
|
45
69
|
: [start1, end1],
|
|
46
70
|
];
|
|
47
71
|
};
|
|
@@ -59,8 +83,8 @@ export const shouldAssigneToRangeBoundary = (
|
|
|
59
83
|
) => {
|
|
60
84
|
let assignedToRangeBoundary = leaders === false || leaders.size < minReplicas;
|
|
61
85
|
if (!assignedToRangeBoundary && leaders) {
|
|
62
|
-
for (const [_,
|
|
63
|
-
if (!intersecting) {
|
|
86
|
+
for (const [_, value] of leaders) {
|
|
87
|
+
if (!value.intersecting) {
|
|
64
88
|
assignedToRangeBoundary = true;
|
|
65
89
|
break;
|
|
66
90
|
}
|
|
@@ -68,18 +92,77 @@ export const shouldAssigneToRangeBoundary = (
|
|
|
68
92
|
}
|
|
69
93
|
return assignedToRangeBoundary;
|
|
70
94
|
};
|
|
71
|
-
export
|
|
95
|
+
export interface EntryReplicated<R extends "u32" | "u64"> {
|
|
96
|
+
hash: string; // id of the entry
|
|
97
|
+
gid: string;
|
|
98
|
+
coordinates: NumberFromType<R>[];
|
|
99
|
+
wallTime: bigint;
|
|
100
|
+
assignedToRangeBoundary: boolean;
|
|
101
|
+
get meta(): ShallowMeta;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const isEntryReplicated = (x: any): x is EntryReplicated<any> => {
|
|
105
|
+
return x instanceof EntryReplicatedU32 || x instanceof EntryReplicatedU64;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export class EntryReplicatedU32 implements EntryReplicated<"u32"> {
|
|
72
109
|
@id({ type: "string" })
|
|
73
|
-
|
|
110
|
+
hash: string;
|
|
74
111
|
|
|
75
112
|
@field({ type: "string" })
|
|
113
|
+
gid: string;
|
|
114
|
+
|
|
115
|
+
@field({ type: vec("u32") })
|
|
116
|
+
coordinates: number[];
|
|
117
|
+
|
|
118
|
+
@field({ type: "u64" })
|
|
119
|
+
wallTime: bigint;
|
|
120
|
+
|
|
121
|
+
@field({ type: "bool" })
|
|
122
|
+
assignedToRangeBoundary: boolean;
|
|
123
|
+
|
|
124
|
+
@field({ type: Uint8Array })
|
|
125
|
+
private _meta: Uint8Array;
|
|
126
|
+
|
|
127
|
+
private _metaResolved: ShallowMeta;
|
|
128
|
+
|
|
129
|
+
constructor(properties: {
|
|
130
|
+
coordinates: number[];
|
|
131
|
+
hash: string;
|
|
132
|
+
meta: Meta;
|
|
133
|
+
assignedToRangeBoundary: boolean;
|
|
134
|
+
}) {
|
|
135
|
+
this.coordinates = properties.coordinates;
|
|
136
|
+
this.hash = properties.hash;
|
|
137
|
+
this.gid = properties.meta.gid;
|
|
138
|
+
this.wallTime = properties.meta.clock.timestamp.wallTime;
|
|
139
|
+
const shallow =
|
|
140
|
+
properties.meta instanceof Meta
|
|
141
|
+
? new ShallowMeta(properties.meta)
|
|
142
|
+
: properties.meta;
|
|
143
|
+
this._meta = serialize(shallow);
|
|
144
|
+
this._metaResolved = deserialize(this._meta, ShallowMeta);
|
|
145
|
+
this._metaResolved = properties.meta;
|
|
146
|
+
this.assignedToRangeBoundary = properties.assignedToRangeBoundary;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
get meta(): ShallowMeta {
|
|
150
|
+
if (!this._metaResolved) {
|
|
151
|
+
this._metaResolved = deserialize(this._meta, ShallowMeta);
|
|
152
|
+
}
|
|
153
|
+
return this._metaResolved;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export class EntryReplicatedU64 implements EntryReplicated<"u64"> {
|
|
158
|
+
@id({ type: "string" })
|
|
76
159
|
hash: string;
|
|
77
160
|
|
|
78
161
|
@field({ type: "string" })
|
|
79
162
|
gid: string;
|
|
80
163
|
|
|
81
|
-
@field({ type: "
|
|
82
|
-
|
|
164
|
+
@field({ type: vec("u64") })
|
|
165
|
+
coordinates: bigint[];
|
|
83
166
|
|
|
84
167
|
@field({ type: "u64" })
|
|
85
168
|
wallTime: bigint;
|
|
@@ -93,15 +176,14 @@ export class EntryReplicated {
|
|
|
93
176
|
private _metaResolved: ShallowMeta;
|
|
94
177
|
|
|
95
178
|
constructor(properties: {
|
|
96
|
-
|
|
179
|
+
coordinates: bigint[];
|
|
97
180
|
hash: string;
|
|
98
181
|
meta: Meta;
|
|
99
182
|
assignedToRangeBoundary: boolean;
|
|
100
183
|
}) {
|
|
101
|
-
this.
|
|
184
|
+
this.coordinates = properties.coordinates;
|
|
102
185
|
this.hash = properties.hash;
|
|
103
186
|
this.gid = properties.meta.gid;
|
|
104
|
-
this.id = this.hash + "-" + this.coordinate;
|
|
105
187
|
this.wallTime = properties.meta.clock.timestamp.wallTime;
|
|
106
188
|
const shallow =
|
|
107
189
|
properties.meta instanceof Meta
|
|
@@ -121,8 +203,25 @@ export class EntryReplicated {
|
|
|
121
203
|
}
|
|
122
204
|
}
|
|
123
205
|
|
|
206
|
+
export interface ReplicationRangeMessage<R extends "u32" | "u64"> {
|
|
207
|
+
id: Uint8Array;
|
|
208
|
+
timestamp: bigint;
|
|
209
|
+
get offset(): NumberFromType<R>;
|
|
210
|
+
get factor(): NumberFromType<R>;
|
|
211
|
+
mode: ReplicationIntent;
|
|
212
|
+
toReplicationRangeIndexable(key: PublicSignKey): ReplicationRangeIndexable<R>;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const isReplicationRangeMessage = (
|
|
216
|
+
x: any,
|
|
217
|
+
): x is ReplicationRangeMessage<any> => {
|
|
218
|
+
return x instanceof ReplicationRangeMessage;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export abstract class ReplicationRangeMessage<R extends "u32" | "u64"> {}
|
|
222
|
+
|
|
124
223
|
@variant(0)
|
|
125
|
-
export class
|
|
224
|
+
export class ReplicationRangeMessageU32 extends ReplicationRangeMessage<"u32"> {
|
|
126
225
|
@field({ type: Uint8Array })
|
|
127
226
|
id: Uint8Array;
|
|
128
227
|
|
|
@@ -145,6 +244,7 @@ export class ReplicationRange {
|
|
|
145
244
|
timestamp: bigint;
|
|
146
245
|
mode: ReplicationIntent;
|
|
147
246
|
}) {
|
|
247
|
+
super();
|
|
148
248
|
const { id, offset, factor, timestamp, mode } = properties;
|
|
149
249
|
this.id = id;
|
|
150
250
|
this._offset = offset;
|
|
@@ -161,8 +261,65 @@ export class ReplicationRange {
|
|
|
161
261
|
return this._offset;
|
|
162
262
|
}
|
|
163
263
|
|
|
164
|
-
toReplicationRangeIndexable(
|
|
165
|
-
|
|
264
|
+
toReplicationRangeIndexable(
|
|
265
|
+
key: PublicSignKey,
|
|
266
|
+
): ReplicationRangeIndexableU32 {
|
|
267
|
+
return new ReplicationRangeIndexableU32({
|
|
268
|
+
id: this.id,
|
|
269
|
+
publicKeyHash: key.hashcode(),
|
|
270
|
+
offset: this.offset,
|
|
271
|
+
length: this.factor,
|
|
272
|
+
timestamp: this.timestamp,
|
|
273
|
+
mode: this.mode,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@variant(1)
|
|
279
|
+
export class ReplicationRangeMessageU64 extends ReplicationRangeMessage<"u64"> {
|
|
280
|
+
@field({ type: Uint8Array })
|
|
281
|
+
id: Uint8Array;
|
|
282
|
+
|
|
283
|
+
@field({ type: "u64" })
|
|
284
|
+
timestamp: bigint;
|
|
285
|
+
|
|
286
|
+
@field({ type: "u64" })
|
|
287
|
+
private _offset: bigint;
|
|
288
|
+
|
|
289
|
+
@field({ type: "u64" })
|
|
290
|
+
private _factor: bigint;
|
|
291
|
+
|
|
292
|
+
@field({ type: "u8" })
|
|
293
|
+
mode: ReplicationIntent;
|
|
294
|
+
|
|
295
|
+
constructor(properties: {
|
|
296
|
+
id: Uint8Array;
|
|
297
|
+
offset: bigint;
|
|
298
|
+
factor: bigint;
|
|
299
|
+
timestamp: bigint;
|
|
300
|
+
mode: ReplicationIntent;
|
|
301
|
+
}) {
|
|
302
|
+
super();
|
|
303
|
+
const { id, offset, factor, timestamp, mode } = properties;
|
|
304
|
+
this.id = id;
|
|
305
|
+
this._offset = offset;
|
|
306
|
+
this._factor = factor;
|
|
307
|
+
this.timestamp = timestamp;
|
|
308
|
+
this.mode = mode;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
get factor(): bigint {
|
|
312
|
+
return this._factor;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
get offset(): bigint {
|
|
316
|
+
return this._offset;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
toReplicationRangeIndexable(
|
|
320
|
+
key: PublicSignKey,
|
|
321
|
+
): ReplicationRangeIndexableU64 {
|
|
322
|
+
return new ReplicationRangeIndexableU64({
|
|
166
323
|
id: this.id,
|
|
167
324
|
publicKeyHash: key.hashcode(),
|
|
168
325
|
offset: this.offset,
|
|
@@ -173,7 +330,91 @@ export class ReplicationRange {
|
|
|
173
330
|
}
|
|
174
331
|
}
|
|
175
332
|
|
|
176
|
-
|
|
333
|
+
class HashableSegmentU32 {
|
|
334
|
+
@field({ type: "u32" })
|
|
335
|
+
start1!: number;
|
|
336
|
+
|
|
337
|
+
@field({ type: "u32" })
|
|
338
|
+
end1!: number;
|
|
339
|
+
|
|
340
|
+
@field({ type: "u32" })
|
|
341
|
+
start2!: number;
|
|
342
|
+
|
|
343
|
+
@field({ type: "u32" })
|
|
344
|
+
end2!: number;
|
|
345
|
+
|
|
346
|
+
@field({ type: "u8" })
|
|
347
|
+
mode: ReplicationIntent;
|
|
348
|
+
|
|
349
|
+
constructor(properties: {
|
|
350
|
+
start1: number;
|
|
351
|
+
start2: number;
|
|
352
|
+
end1: number;
|
|
353
|
+
end2: number;
|
|
354
|
+
mode: ReplicationIntent;
|
|
355
|
+
}) {
|
|
356
|
+
this.start1 = properties.start1;
|
|
357
|
+
this.end1 = properties.end1;
|
|
358
|
+
this.start2 = properties.start2;
|
|
359
|
+
this.end2 = properties.end2;
|
|
360
|
+
this.mode = properties.mode;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
class HashableSegmentU64 {
|
|
365
|
+
@field({ type: "u64" })
|
|
366
|
+
start1!: bigint;
|
|
367
|
+
|
|
368
|
+
@field({ type: "u64" })
|
|
369
|
+
end1!: bigint;
|
|
370
|
+
|
|
371
|
+
@field({ type: "u64" })
|
|
372
|
+
start2!: bigint;
|
|
373
|
+
|
|
374
|
+
@field({ type: "u64" })
|
|
375
|
+
end2!: bigint;
|
|
376
|
+
|
|
377
|
+
@field({ type: "u8" })
|
|
378
|
+
mode: ReplicationIntent;
|
|
379
|
+
|
|
380
|
+
constructor(properties: {
|
|
381
|
+
start1: bigint;
|
|
382
|
+
start2: bigint;
|
|
383
|
+
end1: bigint;
|
|
384
|
+
end2: bigint;
|
|
385
|
+
mode: ReplicationIntent;
|
|
386
|
+
}) {
|
|
387
|
+
this.start1 = properties.start1;
|
|
388
|
+
this.end1 = properties.end1;
|
|
389
|
+
this.start2 = properties.start2;
|
|
390
|
+
this.end2 = properties.end2;
|
|
391
|
+
this.mode = properties.mode;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export interface ReplicationRangeIndexable<R extends "u32" | "u64"> {
|
|
396
|
+
id: Uint8Array;
|
|
397
|
+
idString: string;
|
|
398
|
+
hash: string;
|
|
399
|
+
timestamp: bigint;
|
|
400
|
+
start1: NumberFromType<R>;
|
|
401
|
+
end1: NumberFromType<R>;
|
|
402
|
+
start2: NumberFromType<R>;
|
|
403
|
+
end2: NumberFromType<R>;
|
|
404
|
+
width: NumberFromType<R>;
|
|
405
|
+
widthNormalized: number;
|
|
406
|
+
mode: ReplicationIntent;
|
|
407
|
+
wrapped: boolean;
|
|
408
|
+
toUniqueSegmentId(): string;
|
|
409
|
+
toReplicationRange(): ReplicationRangeMessage<R>;
|
|
410
|
+
contains(point: NumberFromType<R>): boolean;
|
|
411
|
+
equalRange(other: ReplicationRangeIndexable<R>): boolean;
|
|
412
|
+
overlaps(other: ReplicationRangeIndexable<R>): boolean;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export class ReplicationRangeIndexableU32
|
|
416
|
+
implements ReplicationRangeIndexable<"u32">
|
|
417
|
+
{
|
|
177
418
|
@id({ type: Uint8Array })
|
|
178
419
|
id: Uint8Array;
|
|
179
420
|
|
|
@@ -204,7 +445,6 @@ export class ReplicationRangeIndexable {
|
|
|
204
445
|
constructor(
|
|
205
446
|
properties: {
|
|
206
447
|
id?: Uint8Array;
|
|
207
|
-
normalized?: boolean;
|
|
208
448
|
offset: number;
|
|
209
449
|
length: number;
|
|
210
450
|
mode?: ReplicationIntent;
|
|
@@ -215,14 +455,7 @@ export class ReplicationRangeIndexable {
|
|
|
215
455
|
this.hash =
|
|
216
456
|
(properties as { publicKeyHash: string }).publicKeyHash ||
|
|
217
457
|
(properties as { publicKey: PublicSignKey }).publicKey.hashcode();
|
|
218
|
-
|
|
219
|
-
this.transform({ length: properties.length, offset: properties.offset });
|
|
220
|
-
} else {
|
|
221
|
-
this.transform({
|
|
222
|
-
length: scaleToU32(properties.length),
|
|
223
|
-
offset: scaleToU32(properties.offset),
|
|
224
|
-
});
|
|
225
|
-
}
|
|
458
|
+
this.transform({ length: properties.length, offset: properties.offset });
|
|
226
459
|
|
|
227
460
|
this.mode = properties.mode ?? ReplicationIntent.NonStrict;
|
|
228
461
|
this.timestamp = properties.timestamp || BigInt(0);
|
|
@@ -232,6 +465,8 @@ export class ReplicationRangeIndexable {
|
|
|
232
465
|
const ranges = getSegmentsFromOffsetAndRange(
|
|
233
466
|
properties.offset,
|
|
234
467
|
properties.length,
|
|
468
|
+
0,
|
|
469
|
+
MAX_U32,
|
|
235
470
|
);
|
|
236
471
|
this.start1 = Math.round(ranges[0][0]);
|
|
237
472
|
this.end1 = Math.round(ranges[0][1]);
|
|
@@ -244,11 +479,11 @@ export class ReplicationRangeIndexable {
|
|
|
244
479
|
(this.end2 < this.end1 ? this.end2 - this.start2 : 0);
|
|
245
480
|
|
|
246
481
|
if (
|
|
247
|
-
this.start1 >
|
|
248
|
-
this.end1 >
|
|
249
|
-
this.start2 >
|
|
250
|
-
this.end2 >
|
|
251
|
-
this.width >
|
|
482
|
+
this.start1 > MAX_U32 ||
|
|
483
|
+
this.end1 > MAX_U32 ||
|
|
484
|
+
this.start2 > MAX_U32 ||
|
|
485
|
+
this.end2 > MAX_U32 ||
|
|
486
|
+
this.width > MAX_U32 ||
|
|
252
487
|
this.width < 0
|
|
253
488
|
) {
|
|
254
489
|
throw new Error("Segment coordinate out of bounds");
|
|
@@ -266,7 +501,7 @@ export class ReplicationRangeIndexable {
|
|
|
266
501
|
);
|
|
267
502
|
}
|
|
268
503
|
|
|
269
|
-
overlaps(other:
|
|
504
|
+
overlaps(other: ReplicationRangeIndexableU32, checkOther = true): boolean {
|
|
270
505
|
if (
|
|
271
506
|
this.contains(other.start1) ||
|
|
272
507
|
this.contains(other.start2) ||
|
|
@@ -282,7 +517,7 @@ export class ReplicationRangeIndexable {
|
|
|
282
517
|
return false;
|
|
283
518
|
}
|
|
284
519
|
toReplicationRange() {
|
|
285
|
-
return new
|
|
520
|
+
return new ReplicationRangeMessageU32({
|
|
286
521
|
id: this.id,
|
|
287
522
|
offset: this.start1,
|
|
288
523
|
factor: this.width,
|
|
@@ -291,15 +526,6 @@ export class ReplicationRangeIndexable {
|
|
|
291
526
|
});
|
|
292
527
|
}
|
|
293
528
|
|
|
294
|
-
distanceTo(point: number) {
|
|
295
|
-
let wrappedPoint = MAX_U32 - point;
|
|
296
|
-
return Math.min(
|
|
297
|
-
Math.abs(this.start1 - point),
|
|
298
|
-
Math.abs(this.end2 - point),
|
|
299
|
-
Math.abs(this.start1 - wrappedPoint),
|
|
300
|
-
Math.abs(this.end2 - wrappedPoint),
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
529
|
get wrapped() {
|
|
304
530
|
return this.end2 < this.end1;
|
|
305
531
|
}
|
|
@@ -308,7 +534,7 @@ export class ReplicationRangeIndexable {
|
|
|
308
534
|
return this.width / MAX_U32;
|
|
309
535
|
}
|
|
310
536
|
|
|
311
|
-
equals(other:
|
|
537
|
+
equals(other: ReplicationRangeIndexableU32) {
|
|
312
538
|
if (
|
|
313
539
|
equals(this.id, other.id) &&
|
|
314
540
|
this.hash === other.hash &&
|
|
@@ -326,7 +552,7 @@ export class ReplicationRangeIndexable {
|
|
|
326
552
|
return false;
|
|
327
553
|
}
|
|
328
554
|
|
|
329
|
-
equalRange(other:
|
|
555
|
+
equalRange(other: ReplicationRangeIndexableU32) {
|
|
330
556
|
return (
|
|
331
557
|
this.start1 === other.start1 &&
|
|
332
558
|
this.end1 === other.end1 &&
|
|
@@ -348,137 +574,494 @@ export class ReplicationRangeIndexable {
|
|
|
348
574
|
return `(hash ${this.hash} range: ${this.toString()})`;
|
|
349
575
|
}
|
|
350
576
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
577
|
+
toUniqueSegmentId() {
|
|
578
|
+
// return a unique id as a function of the segments location and the replication intent
|
|
579
|
+
const hashable = new HashableSegmentU32(this);
|
|
580
|
+
return sha256Base64Sync(serialize(hashable));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export class ReplicationRangeIndexableU64
|
|
585
|
+
implements ReplicationRangeIndexable<"u64">
|
|
586
|
+
{
|
|
587
|
+
@id({ type: Uint8Array })
|
|
588
|
+
id: Uint8Array;
|
|
589
|
+
|
|
590
|
+
@field({ type: "string" })
|
|
591
|
+
hash: string;
|
|
592
|
+
|
|
593
|
+
@field({ type: "u64" })
|
|
594
|
+
timestamp: bigint;
|
|
595
|
+
|
|
596
|
+
@field({ type: "u64" })
|
|
597
|
+
start1!: bigint;
|
|
598
|
+
|
|
599
|
+
@field({ type: "u64" })
|
|
600
|
+
end1!: bigint;
|
|
601
|
+
|
|
602
|
+
@field({ type: "u64" })
|
|
603
|
+
start2!: bigint;
|
|
604
|
+
|
|
605
|
+
@field({ type: "u64" })
|
|
606
|
+
end2!: bigint;
|
|
607
|
+
|
|
608
|
+
@field({ type: "u64" })
|
|
609
|
+
width!: bigint;
|
|
610
|
+
|
|
611
|
+
@field({ type: "u8" })
|
|
612
|
+
mode: ReplicationIntent;
|
|
613
|
+
|
|
614
|
+
constructor(
|
|
615
|
+
properties: {
|
|
616
|
+
id?: Uint8Array;
|
|
617
|
+
offset: bigint | number;
|
|
618
|
+
length: bigint | number;
|
|
619
|
+
mode?: ReplicationIntent;
|
|
620
|
+
timestamp?: bigint;
|
|
621
|
+
} & ({ publicKeyHash: string } | { publicKey: PublicSignKey }),
|
|
622
|
+
) {
|
|
623
|
+
this.id = properties.id ?? randomBytes(32);
|
|
624
|
+
this.hash =
|
|
625
|
+
(properties as { publicKeyHash: string }).publicKeyHash ||
|
|
626
|
+
(properties as { publicKey: PublicSignKey }).publicKey.hashcode();
|
|
627
|
+
this.transform({ length: properties.length, offset: properties.offset });
|
|
628
|
+
|
|
629
|
+
this.mode = properties.mode ?? ReplicationIntent.NonStrict;
|
|
630
|
+
this.timestamp = properties.timestamp || BigInt(0);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
private transform(properties: {
|
|
634
|
+
offset: bigint | number;
|
|
635
|
+
length: bigint | number;
|
|
636
|
+
}) {
|
|
637
|
+
const ranges = getSegmentsFromOffsetAndRange(
|
|
638
|
+
BigInt(properties.offset),
|
|
639
|
+
BigInt(properties.length),
|
|
640
|
+
0n,
|
|
641
|
+
MAX_U64,
|
|
642
|
+
);
|
|
643
|
+
this.start1 = ranges[0][0];
|
|
644
|
+
this.end1 = ranges[0][1];
|
|
645
|
+
this.start2 = ranges[1][0];
|
|
646
|
+
this.end2 = ranges[1][1];
|
|
647
|
+
|
|
648
|
+
this.width =
|
|
649
|
+
this.end1 -
|
|
650
|
+
this.start1 +
|
|
651
|
+
(this.end2 < this.end1 ? this.end2 - this.start2 : 0n);
|
|
652
|
+
|
|
653
|
+
if (
|
|
654
|
+
this.start1 > MAX_U64 ||
|
|
655
|
+
this.end1 > MAX_U64 ||
|
|
656
|
+
this.start2 > MAX_U64 ||
|
|
657
|
+
this.end2 > MAX_U64 ||
|
|
658
|
+
this.width > MAX_U64 ||
|
|
659
|
+
this.width < 0n
|
|
660
|
+
) {
|
|
661
|
+
throw new Error("Segment coordinate out of bounds");
|
|
354
662
|
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
get idString() {
|
|
666
|
+
return toBase64(this.id);
|
|
667
|
+
}
|
|
355
668
|
|
|
356
|
-
|
|
357
|
-
|
|
669
|
+
contains(point: bigint) {
|
|
670
|
+
return (
|
|
671
|
+
(point >= this.start1 && point < this.end1) ||
|
|
672
|
+
(point >= this.start2 && point < this.end2)
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
overlaps(other: ReplicationRangeIndexableU64, checkOther = true): boolean {
|
|
677
|
+
if (
|
|
678
|
+
this.contains(other.start1) ||
|
|
679
|
+
this.contains(other.start2) ||
|
|
680
|
+
this.contains(other.end1 - 1n) ||
|
|
681
|
+
this.contains(other.end2 - 1n)
|
|
682
|
+
) {
|
|
683
|
+
return true;
|
|
358
684
|
}
|
|
359
685
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if (other.start1 > start1) {
|
|
363
|
-
diff.push(new ReplicationRangeIndexable({
|
|
364
|
-
id: this.id,
|
|
365
|
-
offset: this.start1,
|
|
366
|
-
length: other.start1 - this.start1,
|
|
367
|
-
mode: this.mode,
|
|
368
|
-
publicKeyHash: this.hash,
|
|
369
|
-
timestamp: this.timestamp,
|
|
370
|
-
normalized: false
|
|
371
|
-
}));
|
|
372
|
-
|
|
373
|
-
start1 = other.end2
|
|
686
|
+
if (checkOther) {
|
|
687
|
+
return other.overlaps(this, false);
|
|
374
688
|
}
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
toReplicationRange() {
|
|
692
|
+
return new ReplicationRangeMessageU64({
|
|
693
|
+
id: this.id,
|
|
694
|
+
offset: this.start1,
|
|
695
|
+
factor: this.width,
|
|
696
|
+
timestamp: this.timestamp,
|
|
697
|
+
mode: this.mode,
|
|
698
|
+
});
|
|
699
|
+
}
|
|
375
700
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
701
|
+
get wrapped() {
|
|
702
|
+
return this.end2 < this.end1;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
get widthNormalized() {
|
|
706
|
+
return Number(this.width) / Number(MAX_U64);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
equals(other: ReplicationRangeIndexableU64) {
|
|
710
|
+
if (
|
|
711
|
+
equals(this.id, other.id) &&
|
|
712
|
+
this.hash === other.hash &&
|
|
713
|
+
this.timestamp === other.timestamp &&
|
|
714
|
+
this.mode === other.mode &&
|
|
715
|
+
this.start1 === other.start1 &&
|
|
716
|
+
this.end1 === other.end1 &&
|
|
717
|
+
this.start2 === other.start2 &&
|
|
718
|
+
this.end2 === other.end2 &&
|
|
719
|
+
this.width === other.width
|
|
720
|
+
) {
|
|
721
|
+
return true;
|
|
386
722
|
}
|
|
387
723
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
equalRange(other: ReplicationRangeIndexableU64) {
|
|
728
|
+
return (
|
|
729
|
+
this.start1 === other.start1 &&
|
|
730
|
+
this.end1 === other.end1 &&
|
|
731
|
+
this.start2 === other.start2 &&
|
|
732
|
+
this.end2 === other.end2
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
toString() {
|
|
737
|
+
let roundToTwoDecimals = (num: number) => Math.round(num * 100) / 100;
|
|
738
|
+
|
|
739
|
+
if (Math.abs(Number(this.start1 - this.start2)) < 0.0001) {
|
|
740
|
+
return `([${roundToTwoDecimals(Number(this.start1) / Number(MAX_U64))}, ${roundToTwoDecimals(Number(this.start1) / Number(MAX_U64))}])`;
|
|
398
741
|
}
|
|
742
|
+
return `([${roundToTwoDecimals(Number(this.start1) / Number(MAX_U64))}, ${roundToTwoDecimals(Number(this.start1) / Number(MAX_U64))}] [${roundToTwoDecimals(Number(this.start2) / Number(MAX_U64))}, ${roundToTwoDecimals(Number(this.end2) / Number(MAX_U64))}])`;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
toStringDetailed() {
|
|
746
|
+
return `(hash ${this.hash} range: ${this.toString()})`;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
toUniqueSegmentId() {
|
|
750
|
+
// return a unique id as a function of the segments location and the replication intent
|
|
751
|
+
const hashable = new HashableSegmentU64(this);
|
|
752
|
+
return sha256Base64Sync(serialize(hashable));
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export const mergeRanges = <R extends "u32" | "u64">(
|
|
757
|
+
segments: ReplicationRangeIndexable<R>[],
|
|
758
|
+
numbers: { zero: NumberFromType<R>; maxValue: NumberFromType<R> },
|
|
759
|
+
) => {
|
|
760
|
+
if (segments.length === 0) {
|
|
761
|
+
throw new Error("No segments to merge");
|
|
762
|
+
}
|
|
763
|
+
if (segments.length === 1) {
|
|
764
|
+
return segments[0];
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// only allow merging from same publicKeyHash
|
|
768
|
+
const sameHash = segments.every((x) => x.hash === segments[0].hash);
|
|
769
|
+
if (!sameHash) {
|
|
770
|
+
throw new Error("Segments have different publicKeyHash");
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// only allow merging segments with length 1 (trivial)
|
|
774
|
+
const sameLength = segments.every((x) => x.width === 1 || x.width === 1n);
|
|
775
|
+
if (!sameLength) {
|
|
776
|
+
throw new Error(
|
|
777
|
+
"Segments have different length, only merging of segments length 1 is supported",
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const sorted = segments.sort((a, b) => Number(a.start1 - b.start1));
|
|
782
|
+
|
|
783
|
+
let calculateLargeGap = (): [NumberFromType<R>, number] => {
|
|
784
|
+
let last = sorted[sorted.length - 1];
|
|
785
|
+
let largestArc = numbers.zero;
|
|
786
|
+
let largestArcIndex = -1;
|
|
787
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
788
|
+
const current = sorted[i];
|
|
789
|
+
if (current.start1 !== last.start1) {
|
|
790
|
+
let arc = numbers.zero;
|
|
791
|
+
if (current.start1 < last.end2) {
|
|
792
|
+
arc += ((numbers.maxValue as any) - last.end2) as any;
|
|
793
|
+
|
|
794
|
+
arc += (current.start1 - numbers.zero) as any;
|
|
795
|
+
} else {
|
|
796
|
+
arc += (current.start1 - last.end2) as any;
|
|
797
|
+
}
|
|
399
798
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
publicKeyHash: this.hash,
|
|
407
|
-
timestamp: this.timestamp,
|
|
408
|
-
normalized: false
|
|
409
|
-
}));
|
|
799
|
+
if (arc > largestArc) {
|
|
800
|
+
largestArc = arc;
|
|
801
|
+
largestArcIndex = i;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
last = current;
|
|
410
805
|
}
|
|
411
806
|
|
|
412
|
-
return
|
|
413
|
-
}
|
|
414
|
-
|
|
807
|
+
return [largestArc, largestArcIndex];
|
|
808
|
+
};
|
|
809
|
+
const [largestArc, largestArcIndex] = calculateLargeGap();
|
|
810
|
+
|
|
811
|
+
let totalLengthFinal: number = numbers.maxValue - largestArc;
|
|
812
|
+
|
|
813
|
+
if (largestArcIndex === -1) {
|
|
814
|
+
return segments[0]; // all ranges are the same
|
|
815
|
+
}
|
|
816
|
+
// use segments[0] constructor to create a new object
|
|
817
|
+
|
|
818
|
+
const proto = segments[0].constructor;
|
|
819
|
+
return new (proto as any)({
|
|
820
|
+
length: totalLengthFinal,
|
|
821
|
+
offset: segments[largestArcIndex].start1,
|
|
822
|
+
publicKeyHash: segments[0].hash,
|
|
823
|
+
});
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
const createContainingPointQuery = <R extends "u32" | "u64">(
|
|
827
|
+
points: NumberFromType<R>[] | NumberFromType<R>,
|
|
828
|
+
options?: {
|
|
829
|
+
time?: {
|
|
830
|
+
roleAgeLimit: number;
|
|
831
|
+
matured: boolean;
|
|
832
|
+
now: number;
|
|
833
|
+
};
|
|
834
|
+
},
|
|
835
|
+
) => {
|
|
836
|
+
const or: Query[] = [];
|
|
837
|
+
for (const point of Array.isArray(points) ? points : [points]) {
|
|
838
|
+
or.push(
|
|
839
|
+
new And([
|
|
840
|
+
new IntegerCompare({
|
|
841
|
+
key: "start1",
|
|
842
|
+
compare: Compare.LessOrEqual,
|
|
843
|
+
value: point,
|
|
844
|
+
}),
|
|
845
|
+
new IntegerCompare({
|
|
846
|
+
key: "end1",
|
|
847
|
+
compare: Compare.Greater,
|
|
848
|
+
value: point,
|
|
849
|
+
}),
|
|
850
|
+
]),
|
|
851
|
+
);
|
|
852
|
+
or.push(
|
|
853
|
+
new And([
|
|
854
|
+
new IntegerCompare({
|
|
855
|
+
key: "start2",
|
|
856
|
+
compare: Compare.LessOrEqual,
|
|
857
|
+
value: point,
|
|
858
|
+
}),
|
|
859
|
+
new IntegerCompare({
|
|
860
|
+
key: "end2",
|
|
861
|
+
compare: Compare.Greater,
|
|
862
|
+
value: point,
|
|
863
|
+
}),
|
|
864
|
+
]),
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
if (options?.time) {
|
|
868
|
+
let queries = [
|
|
869
|
+
new Or(or),
|
|
870
|
+
new IntegerCompare({
|
|
871
|
+
key: "timestamp",
|
|
872
|
+
compare: options.time.matured ? Compare.LessOrEqual : Compare.Greater,
|
|
873
|
+
value: BigInt(options.time.now - options.time.roleAgeLimit),
|
|
874
|
+
}),
|
|
875
|
+
];
|
|
876
|
+
return queries;
|
|
877
|
+
} else {
|
|
878
|
+
return new Or(or);
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
const createContainingPartialPointQuery = <R extends "u32" | "u64">(
|
|
883
|
+
point: NumberFromType<R>,
|
|
884
|
+
first: boolean,
|
|
885
|
+
options?: {
|
|
886
|
+
time?: {
|
|
887
|
+
roleAgeLimit: number;
|
|
888
|
+
matured: boolean;
|
|
889
|
+
now: number;
|
|
890
|
+
};
|
|
891
|
+
},
|
|
892
|
+
) => {
|
|
893
|
+
let query: Query[];
|
|
894
|
+
if (first) {
|
|
895
|
+
query = [
|
|
896
|
+
new IntegerCompare({
|
|
897
|
+
key: "start1",
|
|
898
|
+
compare: Compare.LessOrEqual,
|
|
899
|
+
value: point,
|
|
900
|
+
}),
|
|
901
|
+
new IntegerCompare({
|
|
902
|
+
key: "end1",
|
|
903
|
+
compare: Compare.Greater,
|
|
904
|
+
value: point,
|
|
905
|
+
}),
|
|
906
|
+
];
|
|
907
|
+
} else {
|
|
908
|
+
query = [
|
|
909
|
+
new IntegerCompare({
|
|
910
|
+
key: "start2",
|
|
911
|
+
compare: Compare.LessOrEqual,
|
|
912
|
+
value: point,
|
|
913
|
+
}),
|
|
914
|
+
new IntegerCompare({
|
|
915
|
+
key: "end2",
|
|
916
|
+
compare: Compare.Greater,
|
|
917
|
+
value: point,
|
|
918
|
+
}),
|
|
919
|
+
];
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (options?.time) {
|
|
923
|
+
query.push(
|
|
924
|
+
new IntegerCompare({
|
|
925
|
+
key: "timestamp",
|
|
926
|
+
compare: options.time.matured ? Compare.LessOrEqual : Compare.Greater,
|
|
927
|
+
value: BigInt(options.time.now - options.time.roleAgeLimit),
|
|
928
|
+
}),
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
return query;
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
const iterateRangesContainingPoint = <
|
|
936
|
+
S extends Shape | undefined,
|
|
937
|
+
R extends "u32" | "u64",
|
|
938
|
+
>(
|
|
939
|
+
rects: Index<ReplicationRangeIndexable<R>>,
|
|
940
|
+
points: NumberFromType<R>[] | NumberFromType<R>,
|
|
415
941
|
|
|
416
|
-
const containingPoint = <S extends Shape | undefined = undefined>(
|
|
417
|
-
rects: Index<ReplicationRangeIndexable>,
|
|
418
|
-
point: number,
|
|
419
|
-
roleAgeLimit: number,
|
|
420
|
-
matured: boolean,
|
|
421
|
-
now: number,
|
|
422
942
|
options?: {
|
|
423
943
|
shape?: S;
|
|
424
944
|
sort?: Sort[];
|
|
945
|
+
time?: {
|
|
946
|
+
roleAgeLimit: number;
|
|
947
|
+
matured: boolean;
|
|
948
|
+
now: number;
|
|
949
|
+
};
|
|
425
950
|
},
|
|
426
|
-
): IndexIterator<ReplicationRangeIndexable
|
|
951
|
+
): IndexIterator<ReplicationRangeIndexable<R>, S> => {
|
|
427
952
|
// 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
|
|
428
953
|
|
|
429
|
-
let queries = [
|
|
430
|
-
new Or([
|
|
431
|
-
new And([
|
|
432
|
-
new IntegerCompare({
|
|
433
|
-
key: "start1",
|
|
434
|
-
compare: Compare.LessOrEqual,
|
|
435
|
-
value: point,
|
|
436
|
-
}),
|
|
437
|
-
new IntegerCompare({
|
|
438
|
-
key: "end1",
|
|
439
|
-
compare: Compare.Greater,
|
|
440
|
-
value: point,
|
|
441
|
-
}),
|
|
442
|
-
]),
|
|
443
|
-
new And([
|
|
444
|
-
new IntegerCompare({
|
|
445
|
-
key: "start2",
|
|
446
|
-
compare: Compare.LessOrEqual,
|
|
447
|
-
value: point,
|
|
448
|
-
}),
|
|
449
|
-
new IntegerCompare({
|
|
450
|
-
key: "end2",
|
|
451
|
-
compare: Compare.Greater,
|
|
452
|
-
value: point,
|
|
453
|
-
}),
|
|
454
|
-
]),
|
|
455
|
-
]),
|
|
456
|
-
new IntegerCompare({
|
|
457
|
-
key: "timestamp",
|
|
458
|
-
compare: matured ? Compare.LessOrEqual : Compare.Greater,
|
|
459
|
-
value: BigInt(now - roleAgeLimit),
|
|
460
|
-
}),
|
|
461
|
-
];
|
|
462
954
|
return rects.iterate(
|
|
463
955
|
{
|
|
464
|
-
query:
|
|
956
|
+
query: createContainingPointQuery(points, {
|
|
957
|
+
time: options?.time,
|
|
958
|
+
}), // new Or(points.map(point => new And(createContainingPointQuery(point, roleAgeLimit, matured, now)))
|
|
465
959
|
sort: options?.sort,
|
|
466
960
|
},
|
|
467
961
|
options,
|
|
468
962
|
);
|
|
469
963
|
};
|
|
470
964
|
|
|
471
|
-
const
|
|
965
|
+
const allRangesContainingPoint = async <
|
|
966
|
+
S extends Shape | undefined,
|
|
967
|
+
R extends "u32" | "u64",
|
|
968
|
+
>(
|
|
969
|
+
rects: Index<ReplicationRangeIndexable<R>>,
|
|
970
|
+
points: NumberFromType<R>[] | NumberFromType<R>,
|
|
971
|
+
options?: {
|
|
972
|
+
shape?: S;
|
|
973
|
+
sort?: Sort[];
|
|
974
|
+
time?: {
|
|
975
|
+
roleAgeLimit: number;
|
|
976
|
+
matured: boolean;
|
|
977
|
+
now: number;
|
|
978
|
+
};
|
|
979
|
+
},
|
|
980
|
+
) => {
|
|
981
|
+
// 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
|
|
982
|
+
|
|
983
|
+
let allResults: IndexedResult<
|
|
984
|
+
ReturnTypeFromShape<ReplicationRangeIndexable<R>, S>
|
|
985
|
+
>[] = [];
|
|
986
|
+
for (const point of Array.isArray(points) ? points : [points]) {
|
|
987
|
+
const firstIterator = rects.iterate(
|
|
988
|
+
{
|
|
989
|
+
query: createContainingPartialPointQuery(point, false, options),
|
|
990
|
+
sort: options?.sort,
|
|
991
|
+
},
|
|
992
|
+
options,
|
|
993
|
+
);
|
|
994
|
+
|
|
995
|
+
const secondIterator = rects.iterate(
|
|
996
|
+
{
|
|
997
|
+
query: createContainingPartialPointQuery(point, true, options),
|
|
998
|
+
sort: options?.sort,
|
|
999
|
+
},
|
|
1000
|
+
options,
|
|
1001
|
+
);
|
|
1002
|
+
|
|
1003
|
+
[...(await firstIterator.all()), ...(await secondIterator.all())].forEach(
|
|
1004
|
+
(x) => allResults.push(x),
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
return allResults;
|
|
1008
|
+
/* return [...await iterateRangesContainingPoint(rects, points, options).all()]; */
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
const countRangesContainingPoint = async <R extends "u32" | "u64">(
|
|
1012
|
+
rects: Index<ReplicationRangeIndexable<R>>,
|
|
1013
|
+
point: NumberFromType<R>,
|
|
1014
|
+
options?: {
|
|
1015
|
+
time?: {
|
|
1016
|
+
roleAgeLimit: number;
|
|
1017
|
+
matured: boolean;
|
|
1018
|
+
now: number;
|
|
1019
|
+
};
|
|
1020
|
+
},
|
|
1021
|
+
) => {
|
|
1022
|
+
return rects.count({
|
|
1023
|
+
query: createContainingPointQuery(point, options),
|
|
1024
|
+
});
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
export const appromixateCoverage = async <R extends "u32" | "u64">(properties: {
|
|
1028
|
+
peers: Index<ReplicationRangeIndexable<R>>;
|
|
1029
|
+
samples: number;
|
|
1030
|
+
numbers: Numbers<R>;
|
|
1031
|
+
roleAge?: number;
|
|
1032
|
+
normalized?: boolean; // if true, we dont care about the actual number of ranges, only if there is a range, hence the output will be between 0 and 1
|
|
1033
|
+
}) => {
|
|
1034
|
+
const grid = properties.numbers.getGrid(
|
|
1035
|
+
properties.numbers.zero,
|
|
1036
|
+
properties.samples,
|
|
1037
|
+
);
|
|
1038
|
+
/* const now = +new Date(); */
|
|
1039
|
+
let hits = 0;
|
|
1040
|
+
for (const point of grid) {
|
|
1041
|
+
const count = await countRangesContainingPoint(
|
|
1042
|
+
properties.peers,
|
|
1043
|
+
point,
|
|
1044
|
+
/* properties?.roleAge ?? 0,
|
|
1045
|
+
true,
|
|
1046
|
+
now, */
|
|
1047
|
+
);
|
|
1048
|
+
hits += properties.normalized ? (count > 0 ? 1 : 0) : count;
|
|
1049
|
+
}
|
|
1050
|
+
return hits / properties.samples;
|
|
1051
|
+
};
|
|
1052
|
+
|
|
1053
|
+
const getClosest = <S extends Shape | undefined, R extends "u32" | "u64">(
|
|
472
1054
|
direction: "above" | "below",
|
|
473
|
-
rects: Index<ReplicationRangeIndexable
|
|
474
|
-
point:
|
|
1055
|
+
rects: Index<ReplicationRangeIndexable<R>>,
|
|
1056
|
+
point: NumberFromType<R>,
|
|
475
1057
|
roleAgeLimit: number,
|
|
476
1058
|
matured: boolean,
|
|
477
1059
|
now: number,
|
|
478
1060
|
includeStrict: boolean,
|
|
1061
|
+
numbers: Numbers<R>,
|
|
479
1062
|
options?: { shape?: S },
|
|
480
|
-
): IndexIterator<ReplicationRangeIndexable
|
|
481
|
-
const createQueries = (p:
|
|
1063
|
+
): IndexIterator<ReplicationRangeIndexable<R>, S> => {
|
|
1064
|
+
const createQueries = (p: NumberFromType<R>, equality: boolean) => {
|
|
482
1065
|
let queries: Query[];
|
|
483
1066
|
if (direction === "below") {
|
|
484
1067
|
queries = [
|
|
@@ -507,6 +1090,7 @@ const getClosest = <S extends Shape | undefined = undefined>(
|
|
|
507
1090
|
}),
|
|
508
1091
|
];
|
|
509
1092
|
}
|
|
1093
|
+
|
|
510
1094
|
queries.push(
|
|
511
1095
|
new IntegerCompare({ key: "width", compare: Compare.Greater, value: 0 }),
|
|
512
1096
|
);
|
|
@@ -542,7 +1126,10 @@ const getClosest = <S extends Shape | undefined = undefined>(
|
|
|
542
1126
|
|
|
543
1127
|
const iteratorWrapped = rects.iterate(
|
|
544
1128
|
{
|
|
545
|
-
query: createQueries(
|
|
1129
|
+
query: createQueries(
|
|
1130
|
+
direction === "below" ? numbers.maxValue : numbers.zero,
|
|
1131
|
+
true,
|
|
1132
|
+
),
|
|
546
1133
|
sort: [
|
|
547
1134
|
direction === "below"
|
|
548
1135
|
? new Sort({ key: ["end2"], direction: "desc" })
|
|
@@ -554,68 +1141,83 @@ const getClosest = <S extends Shape | undefined = undefined>(
|
|
|
554
1141
|
options,
|
|
555
1142
|
);
|
|
556
1143
|
|
|
557
|
-
return joinIterator<S>(
|
|
1144
|
+
return joinIterator<S, R>(
|
|
1145
|
+
[iterator, iteratorWrapped],
|
|
1146
|
+
point,
|
|
1147
|
+
direction,
|
|
1148
|
+
numbers,
|
|
1149
|
+
);
|
|
558
1150
|
};
|
|
559
1151
|
|
|
560
|
-
export const
|
|
561
|
-
|
|
562
|
-
|
|
1152
|
+
export const getCoveringRangeQuery = (range: {
|
|
1153
|
+
start1: number | bigint;
|
|
1154
|
+
end1: number | bigint;
|
|
1155
|
+
start2: number | bigint;
|
|
1156
|
+
end2: number | bigint;
|
|
1157
|
+
}) => {
|
|
1158
|
+
return [
|
|
1159
|
+
new Or([
|
|
1160
|
+
new And([
|
|
1161
|
+
new IntegerCompare({
|
|
1162
|
+
key: "start1",
|
|
1163
|
+
compare: Compare.LessOrEqual,
|
|
1164
|
+
value: range.start1,
|
|
1165
|
+
}),
|
|
1166
|
+
new IntegerCompare({
|
|
1167
|
+
key: "end1",
|
|
1168
|
+
compare: Compare.GreaterOrEqual,
|
|
1169
|
+
value: range.end1,
|
|
1170
|
+
}),
|
|
1171
|
+
]),
|
|
1172
|
+
new And([
|
|
1173
|
+
new IntegerCompare({
|
|
1174
|
+
key: "start2",
|
|
1175
|
+
compare: Compare.LessOrEqual,
|
|
1176
|
+
value: range.start1,
|
|
1177
|
+
}),
|
|
1178
|
+
new IntegerCompare({
|
|
1179
|
+
key: "end2",
|
|
1180
|
+
compare: Compare.GreaterOrEqual,
|
|
1181
|
+
value: range.end1,
|
|
1182
|
+
}),
|
|
1183
|
+
]),
|
|
1184
|
+
]),
|
|
1185
|
+
new Or([
|
|
1186
|
+
new And([
|
|
1187
|
+
new IntegerCompare({
|
|
1188
|
+
key: "start1",
|
|
1189
|
+
compare: Compare.LessOrEqual,
|
|
1190
|
+
value: range.start2,
|
|
1191
|
+
}),
|
|
1192
|
+
new IntegerCompare({
|
|
1193
|
+
key: "end1",
|
|
1194
|
+
compare: Compare.GreaterOrEqual,
|
|
1195
|
+
value: range.end2,
|
|
1196
|
+
}),
|
|
1197
|
+
]),
|
|
1198
|
+
new And([
|
|
1199
|
+
new IntegerCompare({
|
|
1200
|
+
key: "start2",
|
|
1201
|
+
compare: Compare.LessOrEqual,
|
|
1202
|
+
value: range.start2,
|
|
1203
|
+
}),
|
|
1204
|
+
new IntegerCompare({
|
|
1205
|
+
key: "end2",
|
|
1206
|
+
compare: Compare.GreaterOrEqual,
|
|
1207
|
+
value: range.end2,
|
|
1208
|
+
}),
|
|
1209
|
+
]),
|
|
1210
|
+
]),
|
|
1211
|
+
];
|
|
1212
|
+
};
|
|
1213
|
+
export const iHaveCoveringRange = async <R extends "u32" | "u64">(
|
|
1214
|
+
rects: Index<ReplicationRangeIndexable<R>>,
|
|
1215
|
+
range: ReplicationRangeIndexable<R>,
|
|
563
1216
|
) => {
|
|
564
1217
|
return (
|
|
565
1218
|
(await rects.count({
|
|
566
1219
|
query: [
|
|
567
|
-
|
|
568
|
-
new And([
|
|
569
|
-
new IntegerCompare({
|
|
570
|
-
key: "start1",
|
|
571
|
-
compare: Compare.LessOrEqual,
|
|
572
|
-
value: range.start1,
|
|
573
|
-
}),
|
|
574
|
-
new IntegerCompare({
|
|
575
|
-
key: "end1",
|
|
576
|
-
compare: Compare.GreaterOrEqual,
|
|
577
|
-
value: range.end1,
|
|
578
|
-
}),
|
|
579
|
-
]),
|
|
580
|
-
new And([
|
|
581
|
-
new IntegerCompare({
|
|
582
|
-
key: "start2",
|
|
583
|
-
compare: Compare.LessOrEqual,
|
|
584
|
-
value: range.start1,
|
|
585
|
-
}),
|
|
586
|
-
new IntegerCompare({
|
|
587
|
-
key: "end2",
|
|
588
|
-
compare: Compare.GreaterOrEqual,
|
|
589
|
-
value: range.end1,
|
|
590
|
-
}),
|
|
591
|
-
]),
|
|
592
|
-
]),
|
|
593
|
-
new Or([
|
|
594
|
-
new And([
|
|
595
|
-
new IntegerCompare({
|
|
596
|
-
key: "start1",
|
|
597
|
-
compare: Compare.LessOrEqual,
|
|
598
|
-
value: range.start2,
|
|
599
|
-
}),
|
|
600
|
-
new IntegerCompare({
|
|
601
|
-
key: "end1",
|
|
602
|
-
compare: Compare.GreaterOrEqual,
|
|
603
|
-
value: range.end2,
|
|
604
|
-
}),
|
|
605
|
-
]),
|
|
606
|
-
new And([
|
|
607
|
-
new IntegerCompare({
|
|
608
|
-
key: "start2",
|
|
609
|
-
compare: Compare.LessOrEqual,
|
|
610
|
-
value: range.start2,
|
|
611
|
-
}),
|
|
612
|
-
new IntegerCompare({
|
|
613
|
-
key: "end2",
|
|
614
|
-
compare: Compare.GreaterOrEqual,
|
|
615
|
-
value: range.end2,
|
|
616
|
-
}),
|
|
617
|
-
]),
|
|
618
|
-
]),
|
|
1220
|
+
...getCoveringRangeQuery(range),
|
|
619
1221
|
new StringMatch({
|
|
620
1222
|
key: "hash",
|
|
621
1223
|
value: range.hash,
|
|
@@ -632,54 +1234,55 @@ export const hasCoveringRange = async (
|
|
|
632
1234
|
);
|
|
633
1235
|
};
|
|
634
1236
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
1237
|
+
// TODO
|
|
1238
|
+
export function getDistance(
|
|
1239
|
+
from: any,
|
|
1240
|
+
to: any,
|
|
638
1241
|
direction: "above" | "below" | "closest",
|
|
639
|
-
end
|
|
640
|
-
)
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
// if direction is 'closest' then the shortest distance is the distance
|
|
645
|
-
|
|
646
|
-
// also from is 0.1 and to is 0.9, then distance should be 0.2 not 0.8
|
|
647
|
-
// same as for if from is 0.9 and to is 0.1, then distance should be 0.2 not 0.8
|
|
1242
|
+
end: any,
|
|
1243
|
+
): any {
|
|
1244
|
+
const abs = (value: number | bigint): number | bigint =>
|
|
1245
|
+
value < 0 ? -value : value;
|
|
1246
|
+
const diff = <T extends number | bigint>(a: T, b: T): T => abs(a - b) as T;
|
|
648
1247
|
|
|
649
1248
|
if (direction === "closest") {
|
|
650
1249
|
if (from === to) {
|
|
651
|
-
return 0;
|
|
1250
|
+
return typeof from === "number" ? 0 : 0n; // returns 0 of the correct type
|
|
652
1251
|
}
|
|
653
|
-
|
|
654
|
-
|
|
1252
|
+
return diff(from, to) < diff(end, diff(from, to))
|
|
1253
|
+
? diff(from, to)
|
|
1254
|
+
: diff(end, diff(from, to));
|
|
655
1255
|
}
|
|
656
1256
|
|
|
657
1257
|
if (direction === "above") {
|
|
658
1258
|
if (from <= to) {
|
|
659
|
-
return
|
|
1259
|
+
return end - to + from;
|
|
660
1260
|
}
|
|
661
1261
|
return from - to;
|
|
662
1262
|
}
|
|
663
1263
|
|
|
664
1264
|
if (direction === "below") {
|
|
665
1265
|
if (from >= to) {
|
|
666
|
-
return
|
|
1266
|
+
return end - from + to;
|
|
667
1267
|
}
|
|
668
1268
|
return to - from;
|
|
669
1269
|
}
|
|
670
1270
|
|
|
671
1271
|
throw new Error("Invalid direction");
|
|
672
|
-
}
|
|
1272
|
+
}
|
|
673
1273
|
|
|
674
|
-
const joinIterator = <S extends Shape | undefined
|
|
675
|
-
iterators: IndexIterator<ReplicationRangeIndexable
|
|
676
|
-
point:
|
|
1274
|
+
const joinIterator = <S extends Shape | undefined, R extends "u32" | "u64">(
|
|
1275
|
+
iterators: IndexIterator<ReplicationRangeIndexable<R>, S>[],
|
|
1276
|
+
point: NumberFromType<R>,
|
|
677
1277
|
direction: "above" | "below" | "closest",
|
|
678
|
-
|
|
1278
|
+
numbers: Numbers<R>,
|
|
1279
|
+
): IndexIterator<ReplicationRangeIndexable<R>, S> => {
|
|
679
1280
|
let queues: {
|
|
680
1281
|
elements: {
|
|
681
|
-
result: IndexedResult<
|
|
682
|
-
|
|
1282
|
+
result: IndexedResult<
|
|
1283
|
+
ReturnTypeFromShape<ReplicationRangeIndexable<R>, S>
|
|
1284
|
+
>;
|
|
1285
|
+
dist: NumberFromType<R>;
|
|
683
1286
|
}[];
|
|
684
1287
|
}[] = [];
|
|
685
1288
|
|
|
@@ -687,10 +1290,10 @@ const joinIterator = <S extends Shape | undefined = undefined>(
|
|
|
687
1290
|
next: async (
|
|
688
1291
|
count: number,
|
|
689
1292
|
): Promise<
|
|
690
|
-
IndexedResults<ReturnTypeFromShape<ReplicationRangeIndexable
|
|
1293
|
+
IndexedResults<ReturnTypeFromShape<ReplicationRangeIndexable<R>, S>>
|
|
691
1294
|
> => {
|
|
692
1295
|
let results: IndexedResults<
|
|
693
|
-
ReturnTypeFromShape<ReplicationRangeIndexable
|
|
1296
|
+
ReturnTypeFromShape<ReplicationRangeIndexable<R>, S>
|
|
694
1297
|
> = [];
|
|
695
1298
|
for (let i = 0; i < iterators.length; i++) {
|
|
696
1299
|
let queue = queues[i];
|
|
@@ -705,16 +1308,36 @@ const joinIterator = <S extends Shape | undefined = undefined>(
|
|
|
705
1308
|
for (const el of res) {
|
|
706
1309
|
const closest = el.value;
|
|
707
1310
|
|
|
708
|
-
let dist:
|
|
1311
|
+
let dist: NumberFromType<R>;
|
|
709
1312
|
if (direction === "closest") {
|
|
710
|
-
dist =
|
|
711
|
-
getDistance(
|
|
712
|
-
|
|
1313
|
+
dist = numbers.min(
|
|
1314
|
+
getDistance(
|
|
1315
|
+
closest.start1,
|
|
1316
|
+
point as any,
|
|
1317
|
+
direction,
|
|
1318
|
+
numbers.maxValue as any,
|
|
1319
|
+
) as NumberFromType<R>,
|
|
1320
|
+
getDistance(
|
|
1321
|
+
closest.end2,
|
|
1322
|
+
point as any,
|
|
1323
|
+
direction,
|
|
1324
|
+
numbers.maxValue as any,
|
|
1325
|
+
) as NumberFromType<R>,
|
|
713
1326
|
);
|
|
714
1327
|
} else if (direction === "above") {
|
|
715
|
-
dist = getDistance(
|
|
1328
|
+
dist = getDistance(
|
|
1329
|
+
closest.start1,
|
|
1330
|
+
point as any,
|
|
1331
|
+
direction,
|
|
1332
|
+
numbers.maxValue as any,
|
|
1333
|
+
) as NumberFromType<R>;
|
|
716
1334
|
} else if (direction === "below") {
|
|
717
|
-
dist = getDistance(
|
|
1335
|
+
dist = getDistance(
|
|
1336
|
+
closest.end2,
|
|
1337
|
+
point as any,
|
|
1338
|
+
direction,
|
|
1339
|
+
numbers.maxValue as any,
|
|
1340
|
+
) as NumberFromType<R>;
|
|
718
1341
|
} else {
|
|
719
1342
|
throw new Error("Invalid direction");
|
|
720
1343
|
}
|
|
@@ -728,7 +1351,7 @@ const joinIterator = <S extends Shape | undefined = undefined>(
|
|
|
728
1351
|
|
|
729
1352
|
for (let i = 0; i < count; i++) {
|
|
730
1353
|
let closestQueue = -1;
|
|
731
|
-
let closestDist = Number.
|
|
1354
|
+
let closestDist: bigint | number = Number.MAX_VALUE;
|
|
732
1355
|
for (let j = 0; j < queues.length; j++) {
|
|
733
1356
|
let queue = queues[j];
|
|
734
1357
|
if (queue && queue.elements.length > 0) {
|
|
@@ -763,7 +1386,7 @@ const joinIterator = <S extends Shape | undefined = undefined>(
|
|
|
763
1386
|
},
|
|
764
1387
|
all: async () => {
|
|
765
1388
|
let results: IndexedResult<
|
|
766
|
-
ReturnTypeFromShape<ReplicationRangeIndexable
|
|
1389
|
+
ReturnTypeFromShape<ReplicationRangeIndexable<R>, S>
|
|
767
1390
|
>[] = [];
|
|
768
1391
|
for (const iterator of iterators) {
|
|
769
1392
|
let res = await iterator.all();
|
|
@@ -775,17 +1398,19 @@ const joinIterator = <S extends Shape | undefined = undefined>(
|
|
|
775
1398
|
};
|
|
776
1399
|
|
|
777
1400
|
const getClosestAround = <
|
|
778
|
-
S extends (Shape & { timestamp: true }) | undefined
|
|
1401
|
+
S extends (Shape & { timestamp: true }) | undefined,
|
|
1402
|
+
R extends "u32" | "u64",
|
|
779
1403
|
>(
|
|
780
|
-
peers: Index<ReplicationRangeIndexable
|
|
781
|
-
point:
|
|
1404
|
+
peers: Index<ReplicationRangeIndexable<R>>,
|
|
1405
|
+
point: NumberFromType<R>,
|
|
782
1406
|
roleAge: number,
|
|
783
1407
|
now: number,
|
|
784
1408
|
includeStrictBelow: boolean,
|
|
785
1409
|
includeStrictAbove: boolean,
|
|
1410
|
+
numbers: Numbers<R>,
|
|
786
1411
|
options?: { shape?: S },
|
|
787
1412
|
) => {
|
|
788
|
-
const closestBelow = getClosest<S>(
|
|
1413
|
+
const closestBelow = getClosest<S, R>(
|
|
789
1414
|
"below",
|
|
790
1415
|
peers,
|
|
791
1416
|
point,
|
|
@@ -793,9 +1418,10 @@ const getClosestAround = <
|
|
|
793
1418
|
true,
|
|
794
1419
|
now,
|
|
795
1420
|
includeStrictBelow,
|
|
1421
|
+
numbers,
|
|
796
1422
|
options,
|
|
797
1423
|
);
|
|
798
|
-
const closestAbove = getClosest<S>(
|
|
1424
|
+
const closestAbove = getClosest<S, R>(
|
|
799
1425
|
"above",
|
|
800
1426
|
peers,
|
|
801
1427
|
point,
|
|
@@ -803,43 +1429,59 @@ const getClosestAround = <
|
|
|
803
1429
|
true,
|
|
804
1430
|
now,
|
|
805
1431
|
includeStrictAbove,
|
|
1432
|
+
numbers,
|
|
806
1433
|
options,
|
|
807
1434
|
);
|
|
808
|
-
const containing =
|
|
1435
|
+
/* const containing = iterateRangesContainingPoint<S, R>(
|
|
809
1436
|
peers,
|
|
810
1437
|
point,
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1438
|
+
{
|
|
1439
|
+
time: {
|
|
1440
|
+
roleAgeLimit: roleAge,
|
|
1441
|
+
matured: true,
|
|
1442
|
+
now,
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
815
1445
|
);
|
|
816
1446
|
|
|
817
1447
|
return iteratorInSeries(
|
|
818
1448
|
containing,
|
|
819
|
-
joinIterator<S>([closestBelow, closestAbove], point, "closest"),
|
|
1449
|
+
joinIterator<S, R>([closestBelow, closestAbove], point, "closest", numbers),
|
|
1450
|
+
); */
|
|
1451
|
+
return joinIterator<S, R>(
|
|
1452
|
+
[closestBelow, closestAbove],
|
|
1453
|
+
point,
|
|
1454
|
+
"closest",
|
|
1455
|
+
numbers,
|
|
820
1456
|
);
|
|
821
1457
|
};
|
|
822
1458
|
|
|
823
|
-
const
|
|
1459
|
+
export const isMatured = (
|
|
1460
|
+
segment: { timestamp: bigint },
|
|
1461
|
+
now: number,
|
|
1462
|
+
minAge: number,
|
|
1463
|
+
) => {
|
|
1464
|
+
return now - Number(segment.timestamp) >= minAge;
|
|
1465
|
+
};
|
|
1466
|
+
/*
|
|
1467
|
+
|
|
1468
|
+
const collectNodesAroundPoint = async <R extends "u32" | "u64">(
|
|
824
1469
|
roleAge: number,
|
|
825
|
-
peers: Index<ReplicationRangeIndexable
|
|
1470
|
+
peers: Index<ReplicationRangeIndexable<R>>,
|
|
826
1471
|
collector: (
|
|
827
1472
|
rect: { hash: string },
|
|
828
1473
|
matured: boolean,
|
|
829
|
-
|
|
1474
|
+
intersecting: boolean,
|
|
830
1475
|
) => void,
|
|
831
|
-
point:
|
|
1476
|
+
point: NumberFromType<R>,
|
|
832
1477
|
now: number,
|
|
1478
|
+
numbers: Numbers<R>,
|
|
833
1479
|
done: () => boolean = () => true,
|
|
834
1480
|
) => {
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
0,
|
|
840
|
-
true,
|
|
841
|
-
now /* , { shape } */,
|
|
842
|
-
);
|
|
1481
|
+
const containing = iterateRangesContainingPoint<
|
|
1482
|
+
{ timestamp: true, hash: true },
|
|
1483
|
+
R
|
|
1484
|
+
>(peers, point, 0, true, now, { shape: { timestamp: true, hash: true } as const });
|
|
843
1485
|
const allContaining = await containing.all();
|
|
844
1486
|
for (const rect of allContaining) {
|
|
845
1487
|
collector(rect.value, isMatured(rect.value, now, roleAge), true);
|
|
@@ -849,28 +1491,31 @@ const collectNodesAroundPoint = async (
|
|
|
849
1491
|
return;
|
|
850
1492
|
}
|
|
851
1493
|
|
|
852
|
-
const closestBelow = getClosest(
|
|
1494
|
+
const closestBelow = getClosest<undefined, R>(
|
|
853
1495
|
"below",
|
|
854
1496
|
peers,
|
|
855
1497
|
point,
|
|
856
1498
|
0,
|
|
857
1499
|
true,
|
|
858
1500
|
now,
|
|
859
|
-
false
|
|
1501
|
+
false,
|
|
1502
|
+
numbers
|
|
860
1503
|
);
|
|
861
|
-
const closestAbove = getClosest(
|
|
1504
|
+
const closestAbove = getClosest<undefined, R>(
|
|
862
1505
|
"above",
|
|
863
1506
|
peers,
|
|
864
1507
|
point,
|
|
865
1508
|
0,
|
|
866
1509
|
true,
|
|
867
1510
|
now,
|
|
868
|
-
false
|
|
1511
|
+
false,
|
|
1512
|
+
numbers
|
|
869
1513
|
);
|
|
870
|
-
const aroundIterator = joinIterator(
|
|
1514
|
+
const aroundIterator = joinIterator<undefined, R>(
|
|
871
1515
|
[closestBelow, closestAbove],
|
|
872
1516
|
point,
|
|
873
1517
|
"closest",
|
|
1518
|
+
numbers,
|
|
874
1519
|
);
|
|
875
1520
|
while (aroundIterator.done() !== true && done() !== true) {
|
|
876
1521
|
const res = await aroundIterator.next(1);
|
|
@@ -882,30 +1527,75 @@ const collectNodesAroundPoint = async (
|
|
|
882
1527
|
}
|
|
883
1528
|
}
|
|
884
1529
|
};
|
|
1530
|
+
*/
|
|
885
1531
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
return ret;
|
|
892
|
-
};
|
|
893
|
-
|
|
894
|
-
export const isMatured = (
|
|
895
|
-
segment: { timestamp: bigint },
|
|
1532
|
+
const collectClosestAround = async <R extends "u32" | "u64">(
|
|
1533
|
+
roleAge: number,
|
|
1534
|
+
peers: Index<ReplicationRangeIndexable<R>>,
|
|
1535
|
+
collector: (rect: { hash: string }, matured: boolean) => void,
|
|
1536
|
+
point: NumberFromType<R>,
|
|
896
1537
|
now: number,
|
|
897
|
-
|
|
1538
|
+
numbers: Numbers<R>,
|
|
1539
|
+
done: () => boolean = () => true,
|
|
898
1540
|
) => {
|
|
899
|
-
|
|
1541
|
+
const closestBelow = getClosest<undefined, R>(
|
|
1542
|
+
"below",
|
|
1543
|
+
peers,
|
|
1544
|
+
point,
|
|
1545
|
+
0,
|
|
1546
|
+
true,
|
|
1547
|
+
now,
|
|
1548
|
+
false,
|
|
1549
|
+
numbers,
|
|
1550
|
+
);
|
|
1551
|
+
const closestAbove = getClosest<undefined, R>(
|
|
1552
|
+
"above",
|
|
1553
|
+
peers,
|
|
1554
|
+
point,
|
|
1555
|
+
0,
|
|
1556
|
+
true,
|
|
1557
|
+
now,
|
|
1558
|
+
false,
|
|
1559
|
+
numbers,
|
|
1560
|
+
);
|
|
1561
|
+
/* const containingIterator = iterateRangesContainingPoint<undefined, R>(
|
|
1562
|
+
peers,
|
|
1563
|
+
point,
|
|
1564
|
+
);
|
|
1565
|
+
*/
|
|
1566
|
+
const aroundIterator = joinIterator<undefined, R>(
|
|
1567
|
+
[/* containingIterator, */ closestBelow, closestAbove],
|
|
1568
|
+
point,
|
|
1569
|
+
"closest",
|
|
1570
|
+
numbers,
|
|
1571
|
+
);
|
|
1572
|
+
|
|
1573
|
+
let visited = new Set<string>();
|
|
1574
|
+
while (aroundIterator.done() !== true && done() !== true) {
|
|
1575
|
+
const res = await aroundIterator.next(100);
|
|
1576
|
+
for (const rect of res) {
|
|
1577
|
+
visited.add(rect.value.idString);
|
|
1578
|
+
collector(rect.value, isMatured(rect.value, now, roleAge));
|
|
1579
|
+
if (done()) {
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
900
1584
|
};
|
|
1585
|
+
|
|
901
1586
|
// get peer sample that are responsible for the cursor point
|
|
902
1587
|
// will return a list of peers that want to replicate the data,
|
|
903
1588
|
// but also if necessary a list of peers that are responsible for the data
|
|
904
1589
|
// but have not explicitly replicating a range that cover the cursor point
|
|
905
|
-
export const getSamples = async (
|
|
906
|
-
cursor:
|
|
907
|
-
peers: Index<ReplicationRangeIndexable
|
|
1590
|
+
export const getSamples = async <R extends "u32" | "u64">(
|
|
1591
|
+
cursor: NumberFromType<R>[],
|
|
1592
|
+
peers: Index<ReplicationRangeIndexable<R>>,
|
|
908
1593
|
roleAge: number,
|
|
1594
|
+
numbers: Numbers<R>,
|
|
1595
|
+
options?: {
|
|
1596
|
+
onlyIntersecting?: boolean;
|
|
1597
|
+
uniqueReplicators?: Set<string>;
|
|
1598
|
+
},
|
|
909
1599
|
): Promise<Map<string, { intersecting: boolean }>> => {
|
|
910
1600
|
const leaders: Map<string, { intersecting: boolean }> = new Map();
|
|
911
1601
|
if (!peers) {
|
|
@@ -913,50 +1603,87 @@ export const getSamples = async (
|
|
|
913
1603
|
}
|
|
914
1604
|
|
|
915
1605
|
const now = +new Date();
|
|
1606
|
+
let matured = 0;
|
|
916
1607
|
|
|
917
|
-
|
|
1608
|
+
/* let missingForCursors: NumberFromType<R>[] = [] */
|
|
1609
|
+
let uniqueVisited = new Set<string>();
|
|
918
1610
|
for (let i = 0; i < cursor.length; i++) {
|
|
919
|
-
|
|
1611
|
+
let point = cursor[i];
|
|
920
1612
|
|
|
921
|
-
|
|
922
|
-
await collectNodesAroundPoint(
|
|
923
|
-
roleAge,
|
|
1613
|
+
const allContaining = await allRangesContainingPoint<undefined, R>(
|
|
924
1614
|
peers,
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1615
|
+
point,
|
|
1616
|
+
);
|
|
1617
|
+
|
|
1618
|
+
for (const rect of allContaining) {
|
|
1619
|
+
uniqueVisited.add(rect.value.hash);
|
|
1620
|
+
let prev = leaders.get(rect.value.hash);
|
|
1621
|
+
if (!prev) {
|
|
1622
|
+
if (isMatured(rect.value, now, roleAge)) {
|
|
1623
|
+
matured++;
|
|
928
1624
|
}
|
|
1625
|
+
leaders.set(rect.value.hash, { intersecting: true });
|
|
1626
|
+
} else {
|
|
1627
|
+
prev.intersecting = true;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
929
1630
|
|
|
930
|
-
|
|
1631
|
+
if (options?.uniqueReplicators) {
|
|
1632
|
+
if (
|
|
1633
|
+
options.uniqueReplicators.size === leaders.size ||
|
|
1634
|
+
options.uniqueReplicators.size === uniqueVisited.size
|
|
1635
|
+
) {
|
|
1636
|
+
break; // nothing more to find
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
931
1639
|
|
|
932
|
-
|
|
933
|
-
|
|
1640
|
+
if (options?.onlyIntersecting || matured > i) {
|
|
1641
|
+
continue;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
let foundOneUniqueMatured = false;
|
|
1645
|
+
await collectClosestAround(
|
|
1646
|
+
roleAge,
|
|
1647
|
+
peers,
|
|
1648
|
+
(rect, m) => {
|
|
1649
|
+
uniqueVisited.add(rect.hash);
|
|
1650
|
+
const prev = leaders.get(rect.hash);
|
|
1651
|
+
if (m) {
|
|
1652
|
+
if (!prev) {
|
|
1653
|
+
matured++;
|
|
1654
|
+
leaders.set(rect.hash, { intersecting: false });
|
|
1655
|
+
}
|
|
1656
|
+
if (matured > i) {
|
|
1657
|
+
foundOneUniqueMatured = true;
|
|
1658
|
+
}
|
|
934
1659
|
}
|
|
935
1660
|
},
|
|
936
|
-
|
|
1661
|
+
point,
|
|
937
1662
|
now,
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
return true;
|
|
941
|
-
}
|
|
942
|
-
return false;
|
|
943
|
-
},
|
|
1663
|
+
numbers,
|
|
1664
|
+
() => foundOneUniqueMatured,
|
|
944
1665
|
);
|
|
1666
|
+
/* if (!foundOneUniqueMatured) {
|
|
1667
|
+
missingForCursors.push(point);
|
|
1668
|
+
} */
|
|
945
1669
|
}
|
|
946
|
-
|
|
1670
|
+
/* if (leaders.size < cursor.length) {
|
|
1671
|
+
throw new Error("Missing leaders got: " + leaders.size + " -- expected -- " + cursor.length + " role age " + roleAge + " missing " + missingForCursors.length + " replication index size: " + (await peers.count()));
|
|
1672
|
+
} */
|
|
947
1673
|
return leaders;
|
|
948
1674
|
};
|
|
949
1675
|
|
|
950
|
-
const fetchOne = async <S extends Shape | undefined>(
|
|
951
|
-
iterator: IndexIterator<ReplicationRangeIndexable
|
|
1676
|
+
const fetchOne = async <S extends Shape | undefined, R extends "u32" | "u64">(
|
|
1677
|
+
iterator: IndexIterator<ReplicationRangeIndexable<R>, S>,
|
|
952
1678
|
) => {
|
|
953
1679
|
const value = await iterator.next(1);
|
|
954
1680
|
await iterator.close();
|
|
955
1681
|
return value[0]?.value;
|
|
956
1682
|
};
|
|
957
1683
|
|
|
958
|
-
export const minimumWidthToCover = async (
|
|
1684
|
+
export const minimumWidthToCover = async <R extends "u32" | "u64">(
|
|
959
1685
|
minReplicas: number /* , replicatorCount: number */,
|
|
1686
|
+
numbers: Numbers<R>,
|
|
960
1687
|
) => {
|
|
961
1688
|
/* minReplicas = Math.min(minReplicas, replicatorCount); */ // TODO do we need this?
|
|
962
1689
|
|
|
@@ -965,34 +1692,29 @@ export const minimumWidthToCover = async (
|
|
|
965
1692
|
// to make sure we reach sufficient amount of nodes such that at least one one has
|
|
966
1693
|
// the entry we are looking for
|
|
967
1694
|
|
|
968
|
-
let widthToCoverScaled =
|
|
1695
|
+
let widthToCoverScaled = numbers.divRound(numbers.maxValue, minReplicas);
|
|
969
1696
|
return widthToCoverScaled;
|
|
970
1697
|
};
|
|
971
1698
|
|
|
972
|
-
export const getCoverSet = async (properties: {
|
|
973
|
-
peers: Index<ReplicationRangeIndexable
|
|
974
|
-
start:
|
|
975
|
-
widthToCoverScaled:
|
|
1699
|
+
export const getCoverSet = async <R extends "u32" | "u64">(properties: {
|
|
1700
|
+
peers: Index<ReplicationRangeIndexable<R>>;
|
|
1701
|
+
start: NumberFromType<R> | PublicSignKey | undefined;
|
|
1702
|
+
widthToCoverScaled: NumberFromType<R>;
|
|
976
1703
|
roleAge: number;
|
|
977
|
-
|
|
1704
|
+
numbers: Numbers<R>;
|
|
978
1705
|
eager?:
|
|
979
1706
|
| {
|
|
980
1707
|
unmaturedFetchCoverSize?: number;
|
|
981
1708
|
}
|
|
982
1709
|
| boolean;
|
|
983
1710
|
}): Promise<Set<string>> => {
|
|
984
|
-
let intervalWidth: number = properties.intervalWidth ?? MAX_U32;
|
|
985
1711
|
const { peers, start, widthToCoverScaled, roleAge } = properties;
|
|
986
1712
|
|
|
987
1713
|
const now = Date.now();
|
|
988
|
-
const { startNode, startLocation, endLocation } = await getStartAndEnd
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
roleAge,
|
|
993
|
-
now,
|
|
994
|
-
intervalWidth,
|
|
995
|
-
);
|
|
1714
|
+
const { startNode, startLocation, endLocation } = await getStartAndEnd<
|
|
1715
|
+
undefined,
|
|
1716
|
+
R
|
|
1717
|
+
>(peers, start, widthToCoverScaled, roleAge, now, properties.numbers);
|
|
996
1718
|
|
|
997
1719
|
let ret = new Set<string>();
|
|
998
1720
|
|
|
@@ -1034,29 +1756,46 @@ export const getCoverSet = async (properties: {
|
|
|
1034
1756
|
ret.add(current.hash);
|
|
1035
1757
|
|
|
1036
1758
|
const resolveNextContaining = async (
|
|
1037
|
-
nextLocation:
|
|
1759
|
+
nextLocation: NumberFromType<R>,
|
|
1038
1760
|
roleAge: number,
|
|
1039
1761
|
) => {
|
|
1040
1762
|
let next = await fetchOne(
|
|
1041
|
-
|
|
1763
|
+
iterateRangesContainingPoint<undefined, R>(peers, nextLocation, {
|
|
1042
1764
|
sort: [new Sort({ key: "end2", direction: SortDirection.DESC })],
|
|
1765
|
+
time: {
|
|
1766
|
+
matured: true,
|
|
1767
|
+
roleAgeLimit: roleAge,
|
|
1768
|
+
now,
|
|
1769
|
+
},
|
|
1043
1770
|
}),
|
|
1044
1771
|
); // get entersecting sort by largest end2
|
|
1045
1772
|
return next;
|
|
1046
1773
|
};
|
|
1047
1774
|
|
|
1048
|
-
const resolveNextAbove = async (
|
|
1775
|
+
const resolveNextAbove = async (
|
|
1776
|
+
nextLocation: NumberFromType<R>,
|
|
1777
|
+
roleAge: number,
|
|
1778
|
+
) => {
|
|
1049
1779
|
// if not get closest from above
|
|
1050
|
-
let next = await fetchOne(
|
|
1051
|
-
getClosest(
|
|
1780
|
+
let next = await fetchOne<undefined, R>(
|
|
1781
|
+
getClosest(
|
|
1782
|
+
"above",
|
|
1783
|
+
peers,
|
|
1784
|
+
nextLocation,
|
|
1785
|
+
roleAge,
|
|
1786
|
+
true,
|
|
1787
|
+
now,
|
|
1788
|
+
true,
|
|
1789
|
+
properties.numbers,
|
|
1790
|
+
),
|
|
1052
1791
|
);
|
|
1053
1792
|
return next;
|
|
1054
1793
|
};
|
|
1055
1794
|
|
|
1056
1795
|
const resolveNext = async (
|
|
1057
|
-
nextLocation:
|
|
1796
|
+
nextLocation: NumberFromType<R>,
|
|
1058
1797
|
roleAge: number,
|
|
1059
|
-
): Promise<[ReplicationRangeIndexable
|
|
1798
|
+
): Promise<[ReplicationRangeIndexable<R>, boolean]> => {
|
|
1060
1799
|
const containing = await resolveNextContaining(nextLocation, roleAge);
|
|
1061
1800
|
if (containing) {
|
|
1062
1801
|
return [containing, true];
|
|
@@ -1067,13 +1806,16 @@ export const getCoverSet = async (properties: {
|
|
|
1067
1806
|
// fill the middle
|
|
1068
1807
|
let wrappedOnce = current.end2 < current.end1;
|
|
1069
1808
|
|
|
1070
|
-
let coveredLength =
|
|
1071
|
-
const addLength = (from:
|
|
1809
|
+
let coveredLength = properties.numbers.zero;
|
|
1810
|
+
const addLength = (from: NumberFromType<R>) => {
|
|
1072
1811
|
if (current.end2 < from || current.wrapped) {
|
|
1073
1812
|
wrappedOnce = true;
|
|
1074
|
-
|
|
1813
|
+
// @ts-ignore
|
|
1814
|
+
coveredLength += properties.numbers.maxValue - from;
|
|
1815
|
+
// @ts-ignore
|
|
1075
1816
|
coveredLength += current.end2;
|
|
1076
1817
|
} else {
|
|
1818
|
+
// @ts-ignore
|
|
1077
1819
|
coveredLength += current.end1 - from;
|
|
1078
1820
|
}
|
|
1079
1821
|
};
|
|
@@ -1085,7 +1827,7 @@ export const getCoverSet = async (properties: {
|
|
|
1085
1827
|
|
|
1086
1828
|
while (
|
|
1087
1829
|
maturedCoveredLength < widthToCoverScaled && // eslint-disable-line no-unmodified-loop-condition
|
|
1088
|
-
coveredLength <=
|
|
1830
|
+
coveredLength <= properties.numbers.maxValue // eslint-disable-line no-unmodified-loop-condition
|
|
1089
1831
|
) {
|
|
1090
1832
|
let nextCandidate = await resolveNext(nextLocation, roleAge);
|
|
1091
1833
|
/* let fromAbove = false; */
|
|
@@ -1119,13 +1861,33 @@ export const getCoverSet = async (properties: {
|
|
|
1119
1861
|
if (
|
|
1120
1862
|
!isLast ||
|
|
1121
1863
|
nextCandidate[1] ||
|
|
1122
|
-
|
|
1123
|
-
getDistance(
|
|
1124
|
-
|
|
1864
|
+
properties.numbers.min(
|
|
1865
|
+
getDistance(
|
|
1866
|
+
last.start1,
|
|
1867
|
+
endLocation,
|
|
1868
|
+
"closest",
|
|
1869
|
+
properties.numbers.maxValue,
|
|
1870
|
+
),
|
|
1871
|
+
getDistance(
|
|
1872
|
+
last.end2,
|
|
1873
|
+
endLocation,
|
|
1874
|
+
"closest",
|
|
1875
|
+
properties.numbers.maxValue,
|
|
1876
|
+
),
|
|
1125
1877
|
) >
|
|
1126
|
-
|
|
1127
|
-
getDistance(
|
|
1128
|
-
|
|
1878
|
+
properties.numbers.min(
|
|
1879
|
+
getDistance(
|
|
1880
|
+
current.start1,
|
|
1881
|
+
endLocation,
|
|
1882
|
+
"closest",
|
|
1883
|
+
properties.numbers.maxValue,
|
|
1884
|
+
),
|
|
1885
|
+
getDistance(
|
|
1886
|
+
current.end2,
|
|
1887
|
+
endLocation,
|
|
1888
|
+
"closest",
|
|
1889
|
+
properties.numbers.maxValue,
|
|
1890
|
+
),
|
|
1129
1891
|
)
|
|
1130
1892
|
) {
|
|
1131
1893
|
ret.add(current.hash);
|
|
@@ -1141,9 +1903,9 @@ export const getCoverSet = async (properties: {
|
|
|
1141
1903
|
|
|
1142
1904
|
nextLocation = endIsWrapped
|
|
1143
1905
|
? wrappedOnce
|
|
1144
|
-
?
|
|
1906
|
+
? properties.numbers.min(current.end2, endLocation)
|
|
1145
1907
|
: current.end2
|
|
1146
|
-
:
|
|
1908
|
+
: properties.numbers.min(current.end2, endLocation);
|
|
1147
1909
|
}
|
|
1148
1910
|
|
|
1149
1911
|
start instanceof PublicSignKey && ret.add(start.hashcode());
|
|
@@ -1153,63 +1915,69 @@ export const getCoverSet = async (properties: {
|
|
|
1153
1915
|
// reduce the change set to only regions that are changed for each peer
|
|
1154
1916
|
// i.e. subtract removed regions from added regions, and vice versa
|
|
1155
1917
|
const result = new Map<string, { range: ReplicationRangeIndexable, added: boolean }[]>();
|
|
1156
|
-
|
|
1918
|
+
|
|
1157
1919
|
for (const addedChange of changes.added ?? []) {
|
|
1158
1920
|
let prev = result.get(addedChange.hash) ?? [];
|
|
1159
1921
|
for (const [_hash, ranges] of result.entries()) {
|
|
1160
1922
|
for (const r of ranges) {
|
|
1161
|
-
|
|
1923
|
+
|
|
1162
1924
|
}
|
|
1163
1925
|
}
|
|
1164
1926
|
}
|
|
1165
1927
|
}
|
|
1166
1928
|
*/
|
|
1167
1929
|
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1930
|
+
export const matchEntriesInRangeQuery = (range: {
|
|
1931
|
+
start1: number | bigint;
|
|
1932
|
+
end1: number | bigint;
|
|
1933
|
+
start2: number | bigint;
|
|
1934
|
+
end2: number | bigint;
|
|
1935
|
+
}) => {
|
|
1936
|
+
const c1 = new And([
|
|
1937
|
+
new IntegerCompare({
|
|
1938
|
+
key: "coordinates",
|
|
1939
|
+
compare: "gte",
|
|
1940
|
+
value: range.start1,
|
|
1941
|
+
}),
|
|
1942
|
+
new IntegerCompare({
|
|
1943
|
+
key: "coordinates",
|
|
1944
|
+
compare: "lt",
|
|
1945
|
+
value: range.end1,
|
|
1946
|
+
}),
|
|
1947
|
+
]);
|
|
1948
|
+
|
|
1949
|
+
if (range.start2 === range.end2) {
|
|
1950
|
+
return c1;
|
|
1951
|
+
}
|
|
1184
1952
|
|
|
1185
|
-
ors
|
|
1953
|
+
let ors = [
|
|
1954
|
+
c1,
|
|
1186
1955
|
new And([
|
|
1187
1956
|
new IntegerCompare({
|
|
1188
|
-
key: "
|
|
1957
|
+
key: "coordinates",
|
|
1189
1958
|
compare: "gte",
|
|
1190
1959
|
value: range.start2,
|
|
1191
1960
|
}),
|
|
1192
1961
|
new IntegerCompare({
|
|
1193
|
-
key: "
|
|
1962
|
+
key: "coordinates",
|
|
1194
1963
|
compare: "lt",
|
|
1195
1964
|
value: range.end2,
|
|
1196
1965
|
}),
|
|
1197
1966
|
]),
|
|
1198
|
-
|
|
1199
|
-
|
|
1967
|
+
];
|
|
1200
1968
|
return new Or(ors);
|
|
1201
1969
|
};
|
|
1202
|
-
export const toRebalance = (
|
|
1970
|
+
export const toRebalance = <R extends "u32" | "u64">(
|
|
1203
1971
|
changes: ReplicationChanges,
|
|
1204
|
-
index: Index<EntryReplicated
|
|
1205
|
-
): AsyncIterable<
|
|
1972
|
+
index: Index<EntryReplicated<R>>,
|
|
1973
|
+
): AsyncIterable<EntryReplicated<R>> => {
|
|
1206
1974
|
const assignedRangesQuery = (changes: ReplicationChanges) => {
|
|
1207
1975
|
let ors: Query[] = [];
|
|
1208
1976
|
for (const change of changes) {
|
|
1209
|
-
const matchRange =
|
|
1977
|
+
const matchRange = matchEntriesInRangeQuery(change.range);
|
|
1210
1978
|
if (change.type === "updated") {
|
|
1211
1979
|
// assuming a range is to be removed, is this entry still enoughly replicated
|
|
1212
|
-
const prevMatchRange =
|
|
1980
|
+
const prevMatchRange = matchEntriesInRangeQuery(change.prev);
|
|
1213
1981
|
ors.push(prevMatchRange);
|
|
1214
1982
|
ors.push(matchRange);
|
|
1215
1983
|
} else {
|
|
@@ -1235,13 +2003,15 @@ export const toRebalance = (
|
|
|
1235
2003
|
});
|
|
1236
2004
|
|
|
1237
2005
|
while (iterator.done() !== true) {
|
|
1238
|
-
const entries = await iterator.
|
|
1239
|
-
|
|
1240
|
-
// TODO do we need this
|
|
1241
|
-
const grouped = await groupByGidSync(entries.map((x) => x.value));
|
|
2006
|
+
const entries = await iterator.all(); // TODO choose right batch sizes here for optimal memory usage / speed
|
|
1242
2007
|
|
|
2008
|
+
/* const grouped = await groupByGidSync(entries.map((x) => x.value));
|
|
1243
2009
|
for (const [gid, entries] of grouped.entries()) {
|
|
1244
2010
|
yield { gid, entries };
|
|
2011
|
+
} */
|
|
2012
|
+
|
|
2013
|
+
for (const entry of entries) {
|
|
2014
|
+
yield entry.value;
|
|
1245
2015
|
}
|
|
1246
2016
|
}
|
|
1247
2017
|
},
|
|
@@ -1249,12 +2019,14 @@ export const toRebalance = (
|
|
|
1249
2019
|
};
|
|
1250
2020
|
|
|
1251
2021
|
export const fetchOneFromPublicKey = async <
|
|
1252
|
-
S extends (Shape & { timestamp: true }) | undefined
|
|
2022
|
+
S extends (Shape & { timestamp: true }) | undefined,
|
|
2023
|
+
R extends "u32" | "u64",
|
|
1253
2024
|
>(
|
|
1254
2025
|
publicKey: PublicSignKey,
|
|
1255
|
-
index: Index<ReplicationRangeIndexable
|
|
2026
|
+
index: Index<ReplicationRangeIndexable<R>>,
|
|
1256
2027
|
roleAge: number,
|
|
1257
2028
|
now: number,
|
|
2029
|
+
numbers: Numbers<R>,
|
|
1258
2030
|
options?: {
|
|
1259
2031
|
shape: S;
|
|
1260
2032
|
},
|
|
@@ -1271,13 +2043,14 @@ export const fetchOneFromPublicKey = async <
|
|
|
1271
2043
|
if (node) {
|
|
1272
2044
|
if (!isMatured(node, now, roleAge)) {
|
|
1273
2045
|
const matured = await fetchOne(
|
|
1274
|
-
getClosestAround<S>(
|
|
2046
|
+
getClosestAround<S, R>(
|
|
1275
2047
|
index,
|
|
1276
2048
|
node.start1,
|
|
1277
2049
|
roleAge,
|
|
1278
2050
|
now,
|
|
1279
2051
|
false,
|
|
1280
2052
|
false,
|
|
2053
|
+
numbers,
|
|
1281
2054
|
options,
|
|
1282
2055
|
),
|
|
1283
2056
|
);
|
|
@@ -1291,33 +2064,36 @@ export const fetchOneFromPublicKey = async <
|
|
|
1291
2064
|
|
|
1292
2065
|
export const getStartAndEnd = async <
|
|
1293
2066
|
S extends (Shape & { timestamp: true }) | undefined,
|
|
2067
|
+
R extends "u32" | "u64",
|
|
1294
2068
|
>(
|
|
1295
|
-
peers: Index<ReplicationRangeIndexable
|
|
1296
|
-
start:
|
|
1297
|
-
widthToCoverScaled:
|
|
2069
|
+
peers: Index<ReplicationRangeIndexable<R>>,
|
|
2070
|
+
start: NumberFromType<R> | PublicSignKey | undefined | undefined,
|
|
2071
|
+
widthToCoverScaled: NumberFromType<R>,
|
|
1298
2072
|
roleAge: number,
|
|
1299
2073
|
now: number,
|
|
1300
|
-
|
|
2074
|
+
numbers: Numbers<R>,
|
|
1301
2075
|
options?: { shape: S },
|
|
1302
2076
|
): Promise<{
|
|
1303
|
-
startNode: ReturnTypeFromShape<ReplicationRangeIndexable
|
|
1304
|
-
startLocation:
|
|
1305
|
-
endLocation:
|
|
2077
|
+
startNode: ReturnTypeFromShape<ReplicationRangeIndexable<R>, S> | undefined;
|
|
2078
|
+
startLocation: NumberFromType<R>;
|
|
2079
|
+
endLocation: NumberFromType<R>;
|
|
1306
2080
|
}> => {
|
|
1307
2081
|
// find a good starting point
|
|
1308
|
-
let startNode:
|
|
1309
|
-
|
|
1310
|
-
|
|
2082
|
+
let startNode:
|
|
2083
|
+
| ReturnTypeFromShape<ReplicationRangeIndexable<R>, S>
|
|
2084
|
+
| undefined = undefined;
|
|
2085
|
+
let startLocation: NumberFromType<R> | undefined = undefined;
|
|
1311
2086
|
|
|
1312
|
-
const nodeFromPoint = async (point =
|
|
2087
|
+
const nodeFromPoint = async (point = numbers.random()) => {
|
|
1313
2088
|
startLocation = point;
|
|
1314
|
-
startNode = await fetchOneClosest(
|
|
2089
|
+
startNode = await fetchOneClosest<S, R>(
|
|
1315
2090
|
peers,
|
|
1316
2091
|
startLocation,
|
|
1317
2092
|
roleAge,
|
|
1318
2093
|
now,
|
|
1319
2094
|
false,
|
|
1320
2095
|
true,
|
|
2096
|
+
numbers,
|
|
1321
2097
|
options,
|
|
1322
2098
|
);
|
|
1323
2099
|
};
|
|
@@ -1329,6 +2105,7 @@ export const getStartAndEnd = async <
|
|
|
1329
2105
|
peers,
|
|
1330
2106
|
roleAge,
|
|
1331
2107
|
now,
|
|
2108
|
+
numbers,
|
|
1332
2109
|
options,
|
|
1333
2110
|
);
|
|
1334
2111
|
if (!startNode) {
|
|
@@ -1337,62 +2114,76 @@ export const getStartAndEnd = async <
|
|
|
1337
2114
|
} else {
|
|
1338
2115
|
startLocation = startNode.start1;
|
|
1339
2116
|
}
|
|
1340
|
-
} else if (typeof start === "number") {
|
|
2117
|
+
} else if (typeof start === "number" || typeof start === "bigint") {
|
|
1341
2118
|
await nodeFromPoint(start);
|
|
1342
2119
|
} else {
|
|
1343
2120
|
await nodeFromPoint();
|
|
1344
2121
|
}
|
|
1345
2122
|
|
|
1346
2123
|
if (!startNode || startLocation == null) {
|
|
1347
|
-
return {
|
|
2124
|
+
return {
|
|
2125
|
+
startNode: undefined,
|
|
2126
|
+
startLocation: numbers.zero,
|
|
2127
|
+
endLocation: numbers.zero,
|
|
2128
|
+
};
|
|
1348
2129
|
}
|
|
1349
2130
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
endLocation = endLocation % intervalWidth;
|
|
1353
|
-
}
|
|
2131
|
+
// @ts-ignore
|
|
2132
|
+
let endLocation: T = (startLocation + widthToCoverScaled) % numbers.maxValue;
|
|
1354
2133
|
|
|
1355
|
-
// if start
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
2134
|
+
// if the start node range is not containing the start point, then figure out if the startNode is ideal
|
|
2135
|
+
if (!startNode.contains(startLocation)) {
|
|
2136
|
+
let coveredDistanceToStart = numbers.zero;
|
|
2137
|
+
if (startNode.start1 < startLocation) {
|
|
2138
|
+
coveredDistanceToStart +=
|
|
2139
|
+
numbers.maxValue - startLocation + startNode.start1;
|
|
2140
|
+
} else {
|
|
2141
|
+
coveredDistanceToStart += ((startNode.start1 as any) -
|
|
2142
|
+
startLocation) as any;
|
|
2143
|
+
}
|
|
1362
2144
|
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
2145
|
+
// in this case, the gap to the start point is larger than the width we want to cover. Assume there are no good points
|
|
2146
|
+
if (
|
|
2147
|
+
startNode.mode === ReplicationIntent.Strict &&
|
|
2148
|
+
coveredDistanceToStart > widthToCoverScaled
|
|
2149
|
+
) {
|
|
2150
|
+
return {
|
|
2151
|
+
startNode: undefined,
|
|
2152
|
+
startLocation: numbers.zero,
|
|
2153
|
+
endLocation: numbers.zero,
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
1368
2156
|
}
|
|
1369
2157
|
|
|
1370
2158
|
return {
|
|
1371
2159
|
startNode,
|
|
1372
|
-
startLocation
|
|
1373
|
-
endLocation
|
|
2160
|
+
startLocation,
|
|
2161
|
+
endLocation,
|
|
1374
2162
|
};
|
|
1375
2163
|
};
|
|
1376
2164
|
|
|
1377
2165
|
export const fetchOneClosest = <
|
|
1378
|
-
S extends (Shape & { timestamp: true }) | undefined
|
|
2166
|
+
S extends (Shape & { timestamp: true }) | undefined,
|
|
2167
|
+
R extends "u32" | "u64",
|
|
1379
2168
|
>(
|
|
1380
|
-
peers: Index<ReplicationRangeIndexable
|
|
1381
|
-
point:
|
|
2169
|
+
peers: Index<ReplicationRangeIndexable<R>>,
|
|
2170
|
+
point: NumberFromType<R>,
|
|
1382
2171
|
roleAge: number,
|
|
1383
2172
|
now: number,
|
|
1384
2173
|
includeStrictBelow: boolean,
|
|
1385
2174
|
includeStrictAbove: boolean,
|
|
2175
|
+
numbers: Numbers<R>,
|
|
1386
2176
|
options?: { shape?: S },
|
|
1387
2177
|
) => {
|
|
1388
|
-
return fetchOne(
|
|
1389
|
-
getClosestAround<S>(
|
|
2178
|
+
return fetchOne<S, R>(
|
|
2179
|
+
getClosestAround<S, R>(
|
|
1390
2180
|
peers,
|
|
1391
2181
|
point,
|
|
1392
2182
|
roleAge,
|
|
1393
2183
|
now,
|
|
1394
2184
|
includeStrictBelow,
|
|
1395
2185
|
includeStrictAbove,
|
|
2186
|
+
numbers,
|
|
1396
2187
|
options,
|
|
1397
2188
|
),
|
|
1398
2189
|
);
|