@peerbit/shared-log 9.0.10 → 9.1.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/index.js +2 -2
- package/dist/benchmark/index.js.map +1 -1
- package/dist/benchmark/replication.js +3 -3
- package/dist/benchmark/replication.js.map +1 -1
- package/dist/src/index.d.ts +46 -31
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +426 -229
- package/dist/src/index.js.map +1 -1
- package/dist/src/pid.d.ts.map +1 -1
- package/dist/src/pid.js +20 -19
- package/dist/src/pid.js.map +1 -1
- package/dist/src/ranges.d.ts +13 -3
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +207 -335
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/replication-domain-hash.d.ts +5 -0
- package/dist/src/replication-domain-hash.d.ts.map +1 -0
- package/dist/src/replication-domain-hash.js +30 -0
- package/dist/src/replication-domain-hash.js.map +1 -0
- package/dist/src/replication-domain-time.d.ts +14 -0
- package/dist/src/replication-domain-time.d.ts.map +1 -0
- package/dist/src/replication-domain-time.js +59 -0
- package/dist/src/replication-domain-time.js.map +1 -0
- package/dist/src/replication-domain.d.ts +33 -0
- package/dist/src/replication-domain.d.ts.map +1 -0
- package/dist/src/replication-domain.js +6 -0
- package/dist/src/replication-domain.js.map +1 -0
- package/dist/src/replication.d.ts +10 -8
- package/dist/src/replication.d.ts.map +1 -1
- package/dist/src/replication.js +64 -46
- package/dist/src/replication.js.map +1 -1
- package/dist/src/role.d.ts +2 -1
- package/dist/src/role.d.ts.map +1 -1
- package/dist/src/role.js +6 -5
- package/dist/src/role.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +604 -310
- package/src/pid.ts +20 -19
- package/src/ranges.ts +291 -371
- package/src/replication-domain-hash.ts +43 -0
- package/src/replication-domain-time.ts +85 -0
- package/src/replication-domain.ts +50 -0
- package/src/replication.ts +50 -46
- package/src/role.ts +6 -5
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BinaryReader, BinaryWriter } from "@dao-xyz/borsh";
|
|
2
|
+
import { sha256 } from "@peerbit/crypto";
|
|
3
|
+
import type { ShallowOrFullEntry } from "@peerbit/log";
|
|
4
|
+
import {
|
|
5
|
+
type Log,
|
|
6
|
+
type ReplicationDomain,
|
|
7
|
+
type ReplicationDomainMapper,
|
|
8
|
+
} from "./replication-domain.js";
|
|
9
|
+
|
|
10
|
+
export const hashToU32 = (hash: Uint8Array) => {
|
|
11
|
+
const seedNumber = new BinaryReader(
|
|
12
|
+
hash.subarray(hash.length - 4, hash.length),
|
|
13
|
+
).u32();
|
|
14
|
+
return seedNumber;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const hashTransformer: ReplicationDomainMapper<any> = async (
|
|
18
|
+
entry: ShallowOrFullEntry<any>,
|
|
19
|
+
) => {
|
|
20
|
+
// For a fixed set or members, the choosen leaders will always be the same (address invariant)
|
|
21
|
+
// This allows for that same content is always chosen to be distributed to same peers, to remove unecessary copies
|
|
22
|
+
|
|
23
|
+
// Convert this thing we wan't to distribute to 8 bytes so we get can convert it into a u64
|
|
24
|
+
// modulus into an index
|
|
25
|
+
const utf8writer = new BinaryWriter();
|
|
26
|
+
utf8writer.string(entry.meta.gid);
|
|
27
|
+
const seed = await sha256(utf8writer.finalize());
|
|
28
|
+
|
|
29
|
+
// convert hash of slot to a number
|
|
30
|
+
return hashToU32(seed);
|
|
31
|
+
};
|
|
32
|
+
export type ReplicationDomainHash = ReplicationDomain<undefined, any>;
|
|
33
|
+
export const createReplicationDomainHash: () => ReplicationDomainHash = () => {
|
|
34
|
+
return {
|
|
35
|
+
type: "hash",
|
|
36
|
+
fromEntry: hashTransformer,
|
|
37
|
+
fromArgs: async (args: undefined, log: Log) => {
|
|
38
|
+
return {
|
|
39
|
+
offset: log.node.identity.publicKey,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { ShallowOrFullEntry } from "@peerbit/log";
|
|
2
|
+
import {
|
|
3
|
+
type ReplicationDomain,
|
|
4
|
+
type ReplicationDomainMapper,
|
|
5
|
+
type u32,
|
|
6
|
+
} from "./replication-domain.js";
|
|
7
|
+
|
|
8
|
+
type TimeUnit = "seconds" | "milliseconds" | "microseconds" | "nanoseconds";
|
|
9
|
+
|
|
10
|
+
const scalarNanoToUnit = {
|
|
11
|
+
seconds: BigInt(1e9),
|
|
12
|
+
milliseconds: BigInt(1e6),
|
|
13
|
+
microseconds: BigInt(1e3),
|
|
14
|
+
nanoseconds: BigInt(1),
|
|
15
|
+
};
|
|
16
|
+
const scalarMilliToUnit = {
|
|
17
|
+
seconds: 1e3,
|
|
18
|
+
milliseconds: 1,
|
|
19
|
+
microseconds: 1e-3,
|
|
20
|
+
nanoseconds: 1e-6,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const fromEntry = (
|
|
24
|
+
origin: Date,
|
|
25
|
+
unit: TimeUnit = "milliseconds",
|
|
26
|
+
): ReplicationDomainMapper<any> => {
|
|
27
|
+
const scalar = scalarNanoToUnit[unit];
|
|
28
|
+
const originTime = +origin / scalarMilliToUnit[unit];
|
|
29
|
+
|
|
30
|
+
const fn = (entry: ShallowOrFullEntry<any>) => {
|
|
31
|
+
const cursor = entry.meta.clock.timestamp.wallTime / scalar;
|
|
32
|
+
return Math.round(Number(cursor) - originTime);
|
|
33
|
+
};
|
|
34
|
+
return fn;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type TimeRange = { from: number; to: number };
|
|
38
|
+
|
|
39
|
+
export type ReplicationDomainTime = ReplicationDomain<TimeRange, any> & {
|
|
40
|
+
fromTime: (time: number | Date) => u32;
|
|
41
|
+
fromDuration: (duration: number) => u32;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const createReplicationDomainTime = (
|
|
45
|
+
origin: Date,
|
|
46
|
+
unit: TimeUnit = "milliseconds",
|
|
47
|
+
): ReplicationDomainTime => {
|
|
48
|
+
const originScaled = +origin * scalarMilliToUnit[unit];
|
|
49
|
+
const fromMilliToUnit = scalarMilliToUnit[unit];
|
|
50
|
+
const fromTime = (time: number | Date): u32 => {
|
|
51
|
+
return (
|
|
52
|
+
(typeof time === "number" ? time : +time * fromMilliToUnit) - originScaled
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const fromDuration = (duration: number): u32 => {
|
|
57
|
+
return duration;
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
type: "time",
|
|
61
|
+
fromTime,
|
|
62
|
+
fromDuration,
|
|
63
|
+
fromEntry: fromEntry(origin, unit),
|
|
64
|
+
fromArgs: async (args: TimeRange | undefined, log) => {
|
|
65
|
+
if (!args) {
|
|
66
|
+
return {
|
|
67
|
+
offset: log.node.identity.publicKey,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
offset: fromTime(args.from),
|
|
72
|
+
length: fromDuration(args.to - args.from),
|
|
73
|
+
};
|
|
74
|
+
/* roleAge = roleAge ?? (await log.getDefaultMinRoleAge());
|
|
75
|
+
const ranges = await getCoverSet(
|
|
76
|
+
log.replicationIndex,
|
|
77
|
+
roleAge,
|
|
78
|
+
fromTime(args.from),
|
|
79
|
+
fromDuration(args.to - args.from),
|
|
80
|
+
undefined,
|
|
81
|
+
);
|
|
82
|
+
return [...ranges]; */
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { PublicSignKey } from "@peerbit/crypto";
|
|
2
|
+
import { type Index } from "@peerbit/indexer-interface";
|
|
3
|
+
import type { Entry, ShallowEntry } from "@peerbit/log";
|
|
4
|
+
import type {
|
|
5
|
+
ReplicationLimits,
|
|
6
|
+
ReplicationRangeIndexable,
|
|
7
|
+
} from "./replication.js";
|
|
8
|
+
import { MAX_U32 } from "./role.js";
|
|
9
|
+
|
|
10
|
+
export type u32 = number;
|
|
11
|
+
export type ReplicationDomainMapper<T> = (
|
|
12
|
+
entry: Entry<T> | ShallowEntry,
|
|
13
|
+
) => Promise<u32> | u32;
|
|
14
|
+
|
|
15
|
+
export type Log = {
|
|
16
|
+
replicas: ReplicationLimits;
|
|
17
|
+
node: {
|
|
18
|
+
identity: {
|
|
19
|
+
publicKey: PublicSignKey;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
syncInFlight: Map<string, Map<string, { timestamp: number }>>;
|
|
23
|
+
replicationIndex: Index<ReplicationRangeIndexable>;
|
|
24
|
+
getDefaultMinRoleAge: () => Promise<number>;
|
|
25
|
+
};
|
|
26
|
+
export type ReplicationDomainCoverSet<Args> = (
|
|
27
|
+
log: Log,
|
|
28
|
+
roleAge: number | undefined,
|
|
29
|
+
args: Args,
|
|
30
|
+
) => Promise<string[]> | string[]; // minimum set of peers that covers all the data
|
|
31
|
+
|
|
32
|
+
type CoverRange = {
|
|
33
|
+
offset: number | PublicSignKey;
|
|
34
|
+
length?: number;
|
|
35
|
+
};
|
|
36
|
+
export type ReplicationDomain<Args, T> = {
|
|
37
|
+
type: string;
|
|
38
|
+
fromEntry: ReplicationDomainMapper<T>;
|
|
39
|
+
fromArgs: (
|
|
40
|
+
args: Args | undefined,
|
|
41
|
+
log: Log,
|
|
42
|
+
) => Promise<CoverRange> | CoverRange;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const uniformToU32 = (cursor: number) => {
|
|
46
|
+
return cursor * MAX_U32;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type ExtractDomainArgs<T> =
|
|
50
|
+
T extends ReplicationDomain<infer Args, any> ? Args : never;
|
package/src/replication.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
BinaryReader,
|
|
3
2
|
deserialize,
|
|
4
3
|
field,
|
|
5
4
|
option,
|
|
@@ -10,18 +9,13 @@ import {
|
|
|
10
9
|
import { PublicSignKey, equals, randomBytes } from "@peerbit/crypto";
|
|
11
10
|
import { type Index, id } from "@peerbit/indexer-interface";
|
|
12
11
|
import { TransportMessage } from "./message.js";
|
|
13
|
-
import {
|
|
14
|
-
Observer,
|
|
15
|
-
Replicator,
|
|
16
|
-
Role,
|
|
17
|
-
SEGMENT_COORDINATE_SCALE,
|
|
18
|
-
} from "./role.js";
|
|
12
|
+
import { MAX_U32, Observer, Replicator, Role, scaleToU32 } from "./role.js";
|
|
19
13
|
|
|
20
14
|
export type ReplicationLimits = { min: MinReplicas; max?: MinReplicas };
|
|
21
15
|
|
|
22
16
|
export enum ReplicationIntent {
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
NonStrict = 0, // indicates that the segment will be replicated and nearby data might be replicated as well
|
|
18
|
+
Strict = 1, // only replicate data in the segment to the specified replicator, not any other data
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
export const getSegmentsFromOffsetAndRange = (
|
|
@@ -30,15 +24,16 @@ export const getSegmentsFromOffsetAndRange = (
|
|
|
30
24
|
): [[number, number], [number, number]] => {
|
|
31
25
|
let start1 = offset;
|
|
32
26
|
let end1Unscaled = offset + factor; // only add factor if it is not 1 to prevent numerical issues (like (0.9 + 1) % 1 => 0.8999999)
|
|
33
|
-
let end1 = Math.min(end1Unscaled,
|
|
27
|
+
let end1 = Math.min(end1Unscaled, MAX_U32);
|
|
34
28
|
return [
|
|
35
29
|
[start1, end1],
|
|
36
|
-
end1Unscaled >
|
|
37
|
-
? [0, (factor !==
|
|
30
|
+
end1Unscaled > MAX_U32
|
|
31
|
+
? [0, (factor !== MAX_U32 ? offset + factor : offset) % MAX_U32]
|
|
38
32
|
: [start1, end1],
|
|
39
33
|
];
|
|
40
34
|
};
|
|
41
35
|
|
|
36
|
+
@variant(0)
|
|
42
37
|
export class ReplicationRange {
|
|
43
38
|
@field({ type: Uint8Array })
|
|
44
39
|
private id: Uint8Array;
|
|
@@ -52,25 +47,30 @@ export class ReplicationRange {
|
|
|
52
47
|
@field({ type: "u32" })
|
|
53
48
|
private _factor: number;
|
|
54
49
|
|
|
50
|
+
@field({ type: "u8" })
|
|
51
|
+
mode: ReplicationIntent;
|
|
52
|
+
|
|
55
53
|
constructor(properties: {
|
|
56
54
|
id: Uint8Array;
|
|
57
55
|
offset: number;
|
|
58
56
|
factor: number;
|
|
59
57
|
timestamp: bigint;
|
|
58
|
+
mode: ReplicationIntent;
|
|
60
59
|
}) {
|
|
61
|
-
const { id, offset, factor, timestamp } = properties;
|
|
60
|
+
const { id, offset, factor, timestamp, mode } = properties;
|
|
62
61
|
this.id = id;
|
|
63
|
-
this._offset =
|
|
64
|
-
this._factor =
|
|
62
|
+
this._offset = offset;
|
|
63
|
+
this._factor = factor;
|
|
65
64
|
this.timestamp = timestamp;
|
|
65
|
+
this.mode = mode;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
get factor(): number {
|
|
69
|
-
return this._factor
|
|
69
|
+
return this._factor;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
get offset(): number {
|
|
73
|
-
return this._offset
|
|
73
|
+
return this._offset;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
toReplicationRangeIndexable(key: PublicSignKey): ReplicationRangeIndexable {
|
|
@@ -80,6 +80,7 @@ export class ReplicationRange {
|
|
|
80
80
|
offset: this.offset,
|
|
81
81
|
length: this.factor,
|
|
82
82
|
timestamp: this.timestamp,
|
|
83
|
+
mode: this.mode,
|
|
83
84
|
});
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -110,15 +111,15 @@ export class ReplicationRangeIndexable {
|
|
|
110
111
|
width: number;
|
|
111
112
|
|
|
112
113
|
@field({ type: "u8" })
|
|
113
|
-
|
|
114
|
+
mode: ReplicationIntent;
|
|
114
115
|
|
|
115
116
|
constructor(
|
|
116
117
|
properties: {
|
|
117
118
|
id?: Uint8Array;
|
|
118
|
-
|
|
119
|
+
normalized?: boolean;
|
|
119
120
|
offset: number;
|
|
120
121
|
length: number;
|
|
121
|
-
|
|
122
|
+
mode?: ReplicationIntent;
|
|
122
123
|
timestamp?: bigint;
|
|
123
124
|
} & ({ publicKeyHash: string } | { publicKey: PublicSignKey }),
|
|
124
125
|
) {
|
|
@@ -126,9 +127,16 @@ export class ReplicationRangeIndexable {
|
|
|
126
127
|
this.hash =
|
|
127
128
|
(properties as { publicKeyHash: string }).publicKeyHash ||
|
|
128
129
|
(properties as { publicKey: PublicSignKey }).publicKey.hashcode();
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
if (!properties.normalized) {
|
|
131
|
+
this.transform({ length: properties.length, offset: properties.offset });
|
|
132
|
+
} else {
|
|
133
|
+
this.transform({
|
|
134
|
+
length: scaleToU32(properties.length),
|
|
135
|
+
offset: scaleToU32(properties.offset),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.mode = properties.mode ?? ReplicationIntent.NonStrict;
|
|
132
140
|
this.timestamp = properties.timestamp || BigInt(0);
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -137,10 +145,10 @@ export class ReplicationRangeIndexable {
|
|
|
137
145
|
properties.offset,
|
|
138
146
|
properties.length,
|
|
139
147
|
);
|
|
140
|
-
this.start1 = Math.round(ranges[0][0]
|
|
141
|
-
this.end1 = Math.round(ranges[0][1]
|
|
142
|
-
this.start2 = Math.round(ranges[1][0]
|
|
143
|
-
this.end2 = Math.round(ranges[1][1]
|
|
148
|
+
this.start1 = Math.round(ranges[0][0]);
|
|
149
|
+
this.end1 = Math.round(ranges[0][1]);
|
|
150
|
+
this.start2 = Math.round(ranges[1][0]);
|
|
151
|
+
this.end2 = Math.round(ranges[1][1]);
|
|
144
152
|
|
|
145
153
|
this.width =
|
|
146
154
|
this.end1 -
|
|
@@ -152,7 +160,8 @@ export class ReplicationRangeIndexable {
|
|
|
152
160
|
this.end1 > 0xffffffff ||
|
|
153
161
|
this.start2 > 0xffffffff ||
|
|
154
162
|
this.end2 > 0xffffffff ||
|
|
155
|
-
this.width > 0xffffffff
|
|
163
|
+
this.width > 0xffffffff ||
|
|
164
|
+
this.width < 0
|
|
156
165
|
) {
|
|
157
166
|
throw new Error("Segment coordinate out of bounds");
|
|
158
167
|
}
|
|
@@ -183,14 +192,15 @@ export class ReplicationRangeIndexable {
|
|
|
183
192
|
toReplicationRange() {
|
|
184
193
|
return new ReplicationRange({
|
|
185
194
|
id: this.id,
|
|
186
|
-
offset: this.start1
|
|
187
|
-
factor: this.width
|
|
195
|
+
offset: this.start1,
|
|
196
|
+
factor: this.width,
|
|
188
197
|
timestamp: this.timestamp,
|
|
198
|
+
mode: this.mode,
|
|
189
199
|
});
|
|
190
200
|
}
|
|
191
201
|
|
|
192
202
|
distanceTo(point: number) {
|
|
193
|
-
let wrappedPoint =
|
|
203
|
+
let wrappedPoint = MAX_U32 - point;
|
|
194
204
|
return Math.min(
|
|
195
205
|
Math.abs(this.start1 - point),
|
|
196
206
|
Math.abs(this.end2 - point),
|
|
@@ -203,7 +213,7 @@ export class ReplicationRangeIndexable {
|
|
|
203
213
|
}
|
|
204
214
|
|
|
205
215
|
get widthNormalized() {
|
|
206
|
-
return this.width /
|
|
216
|
+
return this.width / MAX_U32;
|
|
207
217
|
}
|
|
208
218
|
|
|
209
219
|
equals(other: ReplicationRangeIndexable) {
|
|
@@ -211,7 +221,7 @@ export class ReplicationRangeIndexable {
|
|
|
211
221
|
equals(this.id, other.id) &&
|
|
212
222
|
this.hash === other.hash &&
|
|
213
223
|
this.timestamp === other.timestamp &&
|
|
214
|
-
this.
|
|
224
|
+
this.mode === other.mode &&
|
|
215
225
|
this.start1 === other.start1 &&
|
|
216
226
|
this.end1 === other.end1 &&
|
|
217
227
|
this.start2 === other.start2 &&
|
|
@@ -228,9 +238,9 @@ export class ReplicationRangeIndexable {
|
|
|
228
238
|
let roundToTwoDecimals = (num: number) => Math.round(num * 100) / 100;
|
|
229
239
|
|
|
230
240
|
if (Math.abs(this.start1 - this.start2) < 0.0001) {
|
|
231
|
-
return `([${roundToTwoDecimals(this.start1 /
|
|
241
|
+
return `([${roundToTwoDecimals(this.start1 / MAX_U32)}, ${roundToTwoDecimals(this.end1 / MAX_U32)}])`;
|
|
232
242
|
}
|
|
233
|
-
return `([${roundToTwoDecimals(this.start1 /
|
|
243
|
+
return `([${roundToTwoDecimals(this.start1 / MAX_U32)}, ${roundToTwoDecimals(this.end1 / MAX_U32)}] [${roundToTwoDecimals(this.start2 / MAX_U32)}, ${roundToTwoDecimals(this.end2 / MAX_U32)}])`;
|
|
234
244
|
}
|
|
235
245
|
}
|
|
236
246
|
|
|
@@ -278,8 +288,8 @@ export class ResponseRoleMessage extends TransportMessage {
|
|
|
278
288
|
this.role = properties.role;
|
|
279
289
|
}
|
|
280
290
|
|
|
281
|
-
toReplicationInfoMessage():
|
|
282
|
-
return new
|
|
291
|
+
toReplicationInfoMessage(): AllReplicatingSegmentsMessage {
|
|
292
|
+
return new AllReplicatingSegmentsMessage({
|
|
283
293
|
segments:
|
|
284
294
|
this.role instanceof Replicator
|
|
285
295
|
? this.role.segments.map((x) => {
|
|
@@ -288,6 +298,7 @@ export class ResponseRoleMessage extends TransportMessage {
|
|
|
288
298
|
offset: x.offset,
|
|
289
299
|
factor: x.factor,
|
|
290
300
|
timestamp: x.timestamp,
|
|
301
|
+
mode: ReplicationIntent.NonStrict,
|
|
291
302
|
});
|
|
292
303
|
})
|
|
293
304
|
: [],
|
|
@@ -296,7 +307,7 @@ export class ResponseRoleMessage extends TransportMessage {
|
|
|
296
307
|
}
|
|
297
308
|
|
|
298
309
|
@variant([1, 2])
|
|
299
|
-
export class
|
|
310
|
+
export class AllReplicatingSegmentsMessage extends TransportMessage {
|
|
300
311
|
@field({ type: vec(ReplicationRange) })
|
|
301
312
|
segments: ReplicationRange[];
|
|
302
313
|
|
|
@@ -307,7 +318,7 @@ export class ResponseReplicationInfoMessage extends TransportMessage {
|
|
|
307
318
|
}
|
|
308
319
|
|
|
309
320
|
@variant([1, 3])
|
|
310
|
-
export class
|
|
321
|
+
export class AddedReplicationSegmentMessage extends TransportMessage {
|
|
311
322
|
@field({ type: vec(ReplicationRange) })
|
|
312
323
|
segments: ReplicationRange[];
|
|
313
324
|
|
|
@@ -376,10 +387,3 @@ export const maxReplicas = (
|
|
|
376
387
|
const numberOfLeaders = Math.max(Math.min(higher, max), lower);
|
|
377
388
|
return numberOfLeaders;
|
|
378
389
|
};
|
|
379
|
-
|
|
380
|
-
export const hashToUniformNumber = (hash: Uint8Array) => {
|
|
381
|
-
const seedNumber = new BinaryReader(
|
|
382
|
-
hash.subarray(hash.length - 4, hash.length),
|
|
383
|
-
).u32();
|
|
384
|
-
return seedNumber / 0xffffffff; // bounded between 0 and 1
|
|
385
|
-
};
|
package/src/role.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { field, variant, vec } from "@dao-xyz/borsh";
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const MAX_U32 = 4294967295;
|
|
9
|
+
export const scaleToU32 = (value: number) => Math.round(MAX_U32 * value);
|
|
9
10
|
|
|
10
11
|
export const overlaps = (x1: number, x2: number, y1: number, y2: number) => {
|
|
11
12
|
if (x1 <= y2 && y1 <= x2) {
|
|
@@ -59,20 +60,20 @@ export class RoleReplicationSegment {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
this.timestamp = timestamp ?? BigInt(+new Date());
|
|
62
|
-
this.factorNominator = Math.round(
|
|
63
|
+
this.factorNominator = Math.round(MAX_U32 * factor);
|
|
63
64
|
|
|
64
65
|
if (offset > 1 || offset < 0) {
|
|
65
66
|
throw new Error("Expecting offset to be between 0 and 1, got: " + offset);
|
|
66
67
|
}
|
|
67
|
-
this.offsetNominator = Math.round(
|
|
68
|
+
this.offsetNominator = Math.round(MAX_U32 * offset);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
get factor(): number {
|
|
71
|
-
return this.factorNominator /
|
|
72
|
+
return this.factorNominator / MAX_U32;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
get offset(): number {
|
|
75
|
-
return this.offsetNominator /
|
|
76
|
+
return this.offsetNominator / MAX_U32;
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|