@peerbit/shared-log 9.0.5 → 9.0.6-7872365
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/src/index.d.ts +5 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +63 -35
- package/dist/src/index.js.map +1 -1
- package/dist/src/replication.d.ts +8 -0
- package/dist/src/replication.d.ts.map +1 -1
- package/dist/src/replication.js +36 -24
- package/dist/src/replication.js.map +1 -1
- package/dist/src/role.d.ts +37 -0
- package/dist/src/role.d.ts.map +1 -1
- package/dist/src/role.js +99 -92
- package/dist/src/role.js.map +1 -1
- package/package.json +70 -70
- package/src/index.ts +83 -38
- package/src/replication.ts +37 -22
- package/src/role.ts +68 -64
package/src/index.ts
CHANGED
|
@@ -73,6 +73,7 @@ import {
|
|
|
73
73
|
ReplicationRangeIndexable,
|
|
74
74
|
RequestReplicationInfoMessage,
|
|
75
75
|
ResponseReplicationInfoMessage,
|
|
76
|
+
ResponseRoleMessage,
|
|
76
77
|
StartedReplicating,
|
|
77
78
|
StoppedReplicating,
|
|
78
79
|
decodeReplicas,
|
|
@@ -80,7 +81,7 @@ import {
|
|
|
80
81
|
hashToUniformNumber,
|
|
81
82
|
maxReplicas,
|
|
82
83
|
} from "./replication.js";
|
|
83
|
-
import { SEGMENT_COORDINATE_SCALE } from "./role.js";
|
|
84
|
+
import { Observer, Replicator, SEGMENT_COORDINATE_SCALE } from "./role.js";
|
|
84
85
|
|
|
85
86
|
export * from "./replication.js";
|
|
86
87
|
|
|
@@ -96,10 +97,10 @@ const groupByGid = async <
|
|
|
96
97
|
const groupByGid: Map<string, T[]> = new Map();
|
|
97
98
|
for (const head of entries) {
|
|
98
99
|
const gid = await (head instanceof Entry
|
|
99
|
-
? head.
|
|
100
|
+
? (await head.getMeta()).gid
|
|
100
101
|
: head instanceof ShallowEntry
|
|
101
102
|
? head.meta.gid
|
|
102
|
-
: head.entry.
|
|
103
|
+
: (await head.entry.getMeta()).gid);
|
|
103
104
|
let value = groupByGid.get(gid);
|
|
104
105
|
if (!value) {
|
|
105
106
|
value = [];
|
|
@@ -156,6 +157,7 @@ export type SharedLogOptions<T> = {
|
|
|
156
157
|
timeUntilRoleMaturity?: number;
|
|
157
158
|
waitForReplicatorTimeout?: number;
|
|
158
159
|
distributionDebounceTime?: number;
|
|
160
|
+
compatibility?: number;
|
|
159
161
|
};
|
|
160
162
|
|
|
161
163
|
export const DEFAULT_MIN_REPLICAS = 2;
|
|
@@ -205,7 +207,9 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
205
207
|
publicKey: PublicSignKey,
|
|
206
208
|
) => Promise<boolean> | boolean;
|
|
207
209
|
|
|
208
|
-
private _logProperties?: LogProperties<T> &
|
|
210
|
+
private _logProperties?: LogProperties<T> &
|
|
211
|
+
LogEvents<T> &
|
|
212
|
+
SharedLogOptions<T>;
|
|
209
213
|
private _closeController!: AbortController;
|
|
210
214
|
private _gidParentCache!: Cache<Entry<any>[]>;
|
|
211
215
|
private _respondToIHaveTimeout!: any;
|
|
@@ -278,6 +282,32 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
278
282
|
return this._replicationSettings;
|
|
279
283
|
}
|
|
280
284
|
|
|
285
|
+
get compatibility(): number | undefined {
|
|
286
|
+
return this._logProperties?.compatibility;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private get v8Behaviour() {
|
|
290
|
+
return (this.compatibility ?? Number.MAX_VALUE) < 9;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// @deprecated
|
|
294
|
+
private getRole() {
|
|
295
|
+
let isFixedReplicationSettings =
|
|
296
|
+
(this._replicationSettings as FixedReplicationOptions).factor !==
|
|
297
|
+
undefined;
|
|
298
|
+
if (isFixedReplicationSettings) {
|
|
299
|
+
const fixedSettings = this
|
|
300
|
+
._replicationSettings as FixedReplicationOptions;
|
|
301
|
+
return new Replicator({
|
|
302
|
+
factor: fixedSettings.factor,
|
|
303
|
+
offset: fixedSettings.offset ?? 0,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// TODO this is not accurate but might be good enough
|
|
308
|
+
return new Observer();
|
|
309
|
+
}
|
|
310
|
+
|
|
281
311
|
async isReplicating() {
|
|
282
312
|
if (!this._replicationSettings) {
|
|
283
313
|
return false;
|
|
@@ -596,41 +626,15 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
596
626
|
logger.warn("Not allowed to replicate by canReplicate");
|
|
597
627
|
}
|
|
598
628
|
|
|
599
|
-
added
|
|
600
|
-
|
|
629
|
+
if (added) {
|
|
630
|
+
await this.rpc.send(
|
|
601
631
|
new StartedReplicating({ segments: [range.toReplicationRange()] }),
|
|
602
632
|
{
|
|
603
633
|
priority: 1,
|
|
604
634
|
},
|
|
605
|
-
)
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
/* async updateRole(role: InitialReplicationOptions, onRoleChange = true) {
|
|
609
|
-
await this.setupReplicationSettings(role)
|
|
610
|
-
return this._updateRole(, onRoleChange);
|
|
611
|
-
} */
|
|
612
|
-
|
|
613
|
-
/* private async _updateRole(
|
|
614
|
-
role: Observer | Replicator = this._role,
|
|
615
|
-
onRoleChange = true
|
|
616
|
-
) {
|
|
617
|
-
this._role = role;
|
|
618
|
-
const { changed } = await this._modifyReplicators(
|
|
619
|
-
this.role,
|
|
620
|
-
this.node.identity.publicKey
|
|
621
|
-
);
|
|
622
|
-
|
|
623
|
-
await this.rpc.subscribe();
|
|
624
|
-
await this.rpc.send(new ResponseReplicationInfoMessage({ segments: await }), {
|
|
625
|
-
priority: 1
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
if (onRoleChange && changed !== "none") {
|
|
629
|
-
await this.onRoleChange(this._role, this.node.identity.publicKey);
|
|
635
|
+
);
|
|
630
636
|
}
|
|
631
|
-
|
|
632
|
-
return changed;
|
|
633
|
-
} */
|
|
637
|
+
}
|
|
634
638
|
|
|
635
639
|
async append(
|
|
636
640
|
data: T,
|
|
@@ -1019,12 +1023,16 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
1019
1023
|
async _onMessage(
|
|
1020
1024
|
msg: TransportMessage,
|
|
1021
1025
|
context: RequestContext,
|
|
1022
|
-
): Promise<
|
|
1026
|
+
): Promise<void> {
|
|
1023
1027
|
try {
|
|
1024
1028
|
if (!context.from) {
|
|
1025
1029
|
throw new Error("Missing from in update role message");
|
|
1026
1030
|
}
|
|
1027
1031
|
|
|
1032
|
+
if (msg instanceof ResponseRoleMessage) {
|
|
1033
|
+
msg = msg.toReplicationInfoMessage(); // migration
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1028
1036
|
if (msg instanceof ExchangeHeadsMessage) {
|
|
1029
1037
|
/**
|
|
1030
1038
|
* I have received heads from someone else.
|
|
@@ -1132,7 +1140,7 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
1132
1140
|
|
|
1133
1141
|
logger.debug(
|
|
1134
1142
|
`${this.node.identity.publicKey.hashcode()}: Dropping heads with gid: ${
|
|
1135
|
-
entry.entry.gid
|
|
1143
|
+
entry.entry.meta.gid
|
|
1136
1144
|
}. Because not leader`,
|
|
1137
1145
|
);
|
|
1138
1146
|
}
|
|
@@ -1300,6 +1308,8 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
1300
1308
|
} else if (msg instanceof BlocksMessage) {
|
|
1301
1309
|
await this.remoteBlocks.onMessage(msg.message);
|
|
1302
1310
|
} else if (msg instanceof RequestReplicationInfoMessage) {
|
|
1311
|
+
// TODO this message type is never used, should we remove it?
|
|
1312
|
+
|
|
1303
1313
|
if (context.from.equals(this.node.identity.publicKey)) {
|
|
1304
1314
|
return;
|
|
1305
1315
|
}
|
|
@@ -1313,6 +1323,28 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
1313
1323
|
mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
|
|
1314
1324
|
},
|
|
1315
1325
|
);
|
|
1326
|
+
|
|
1327
|
+
// for backwards compatibility (v8) remove this when we are sure that all nodes are v9+
|
|
1328
|
+
if (this.v8Behaviour) {
|
|
1329
|
+
const role = this.getRole();
|
|
1330
|
+
if (role instanceof Replicator) {
|
|
1331
|
+
const fixedSettings = this
|
|
1332
|
+
._replicationSettings as FixedReplicationOptions;
|
|
1333
|
+
if (fixedSettings.factor === 1) {
|
|
1334
|
+
await this.rpc.send(
|
|
1335
|
+
new ResponseRoleMessage({
|
|
1336
|
+
role,
|
|
1337
|
+
}),
|
|
1338
|
+
{
|
|
1339
|
+
mode: new SilentDelivery({
|
|
1340
|
+
to: [context.from],
|
|
1341
|
+
redundancy: 1,
|
|
1342
|
+
}),
|
|
1343
|
+
},
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1316
1348
|
} else if (
|
|
1317
1349
|
msg instanceof ResponseReplicationInfoMessage ||
|
|
1318
1350
|
msg instanceof StartedReplicating
|
|
@@ -1321,6 +1353,10 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
1321
1353
|
return;
|
|
1322
1354
|
}
|
|
1323
1355
|
|
|
1356
|
+
let replicationInfoMessage = msg as
|
|
1357
|
+
| ResponseReplicationInfoMessage
|
|
1358
|
+
| StartedReplicating;
|
|
1359
|
+
|
|
1324
1360
|
// we have this statement because peers might have changed/announced their role,
|
|
1325
1361
|
// but we don't know them as "subscribers" yet. i.e. they are not online
|
|
1326
1362
|
|
|
@@ -1343,7 +1379,7 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
1343
1379
|
await this.removeReplicator(context.from!);
|
|
1344
1380
|
}
|
|
1345
1381
|
let addedOnce = false;
|
|
1346
|
-
for (const segment of
|
|
1382
|
+
for (const segment of replicationInfoMessage.segments) {
|
|
1347
1383
|
const added = await this.addReplicationRange(
|
|
1348
1384
|
segment.toReplicationRangeIndexable(context.from!),
|
|
1349
1385
|
context.from!,
|
|
@@ -1646,7 +1682,7 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
1646
1682
|
},
|
|
1647
1683
|
) {
|
|
1648
1684
|
return this.isLeader(
|
|
1649
|
-
entry.gid,
|
|
1685
|
+
entry.meta.gid,
|
|
1650
1686
|
decodeReplicas(entry).getValue(this),
|
|
1651
1687
|
options,
|
|
1652
1688
|
);
|
|
@@ -1698,6 +1734,15 @@ export class SharedLog<T = Uint8Array> extends Program<
|
|
|
1698
1734
|
},
|
|
1699
1735
|
)
|
|
1700
1736
|
.catch((e) => logger.error(e.toString()));
|
|
1737
|
+
|
|
1738
|
+
if (this.v8Behaviour) {
|
|
1739
|
+
// for backwards compatibility
|
|
1740
|
+
this.rpc
|
|
1741
|
+
.send(new ResponseRoleMessage({ role: this.getRole() }), {
|
|
1742
|
+
mode: new SilentDelivery({ redundancy: 1, to: [publicKey] }),
|
|
1743
|
+
})
|
|
1744
|
+
.catch((e) => logger.error(e.toString()));
|
|
1745
|
+
}
|
|
1701
1746
|
}
|
|
1702
1747
|
} else {
|
|
1703
1748
|
await this.removeReplicator(publicKey);
|
package/src/replication.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
BinaryReader,
|
|
3
3
|
deserialize,
|
|
4
4
|
field,
|
|
5
|
+
option,
|
|
5
6
|
serialize,
|
|
6
7
|
variant,
|
|
7
8
|
vec,
|
|
@@ -9,28 +10,14 @@ import {
|
|
|
9
10
|
import { PublicSignKey, equals, randomBytes } from "@peerbit/crypto";
|
|
10
11
|
import { type Index, id } from "@peerbit/indexer-interface";
|
|
11
12
|
import { TransportMessage } from "./message.js";
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
Observer,
|
|
15
|
+
Replicator,
|
|
16
|
+
Role,
|
|
17
|
+
SEGMENT_COORDINATE_SCALE,
|
|
18
|
+
} from "./role.js";
|
|
13
19
|
|
|
14
20
|
export type ReplicationLimits = { min: MinReplicas; max?: MinReplicas };
|
|
15
|
-
/*
|
|
16
|
-
export class ReplicatorRect {
|
|
17
|
-
|
|
18
|
-
@id({ type: Uint8Array })
|
|
19
|
-
id: Uint8Array;
|
|
20
|
-
|
|
21
|
-
@field({ type: 'string' })
|
|
22
|
-
hash: string;
|
|
23
|
-
|
|
24
|
-
@field({ type: vec(ReplicationSegment) })
|
|
25
|
-
segments: ReplicationSegment[];
|
|
26
|
-
|
|
27
|
-
constructor(properties: { hash: string; segments: ReplicationSegment[] }) {
|
|
28
|
-
this.id = randomBytes(32);
|
|
29
|
-
this.hash = properties.hash;
|
|
30
|
-
this.segments = properties.segments;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
*/
|
|
34
21
|
|
|
35
22
|
export enum ReplicationIntent {
|
|
36
23
|
Explicit = 0,
|
|
@@ -280,7 +267,35 @@ export class RequestReplicationInfoMessage extends TransportMessage {
|
|
|
280
267
|
}
|
|
281
268
|
}
|
|
282
269
|
|
|
270
|
+
// @deprecated remove when possible
|
|
283
271
|
@variant([1, 1])
|
|
272
|
+
export class ResponseRoleMessage extends TransportMessage {
|
|
273
|
+
@field({ type: option(Role) })
|
|
274
|
+
role: Observer | Replicator;
|
|
275
|
+
|
|
276
|
+
constructor(properties: { role: Observer | Replicator }) {
|
|
277
|
+
super();
|
|
278
|
+
this.role = properties.role;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
toReplicationInfoMessage(): ResponseReplicationInfoMessage {
|
|
282
|
+
return new ResponseReplicationInfoMessage({
|
|
283
|
+
segments:
|
|
284
|
+
this.role instanceof Replicator
|
|
285
|
+
? this.role.segments.map((x) => {
|
|
286
|
+
return new ReplicationRange({
|
|
287
|
+
id: randomBytes(32),
|
|
288
|
+
offset: x.offset,
|
|
289
|
+
factor: x.factor,
|
|
290
|
+
timestamp: x.timestamp,
|
|
291
|
+
});
|
|
292
|
+
})
|
|
293
|
+
: [],
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@variant([1, 2])
|
|
284
299
|
export class ResponseReplicationInfoMessage extends TransportMessage {
|
|
285
300
|
@field({ type: vec(ReplicationRange) })
|
|
286
301
|
segments: ReplicationRange[];
|
|
@@ -291,7 +306,7 @@ export class ResponseReplicationInfoMessage extends TransportMessage {
|
|
|
291
306
|
}
|
|
292
307
|
}
|
|
293
308
|
|
|
294
|
-
@variant([1,
|
|
309
|
+
@variant([1, 3])
|
|
295
310
|
export class StartedReplicating extends TransportMessage {
|
|
296
311
|
@field({ type: vec(ReplicationRange) })
|
|
297
312
|
segments: ReplicationRange[];
|
|
@@ -302,7 +317,7 @@ export class StartedReplicating extends TransportMessage {
|
|
|
302
317
|
}
|
|
303
318
|
}
|
|
304
319
|
|
|
305
|
-
@variant([1,
|
|
320
|
+
@variant([1, 4])
|
|
306
321
|
export class StoppedReplicating extends TransportMessage {
|
|
307
322
|
@field({ type: vec(Uint8Array) })
|
|
308
323
|
segmentIds: Uint8Array[];
|
package/src/role.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated
|
|
3
|
+
* Code below is deprecated and will be removed in the future.
|
|
4
|
+
* Roles have been replaces with just replication segments.
|
|
5
|
+
*/
|
|
6
|
+
import { field, variant, vec } from "@dao-xyz/borsh";
|
|
7
|
+
|
|
8
|
+
export const SEGMENT_COORDINATE_SCALE = 4294967295;
|
|
2
9
|
|
|
3
|
-
|
|
10
|
+
export const overlaps = (x1: number, x2: number, y1: number, y2: number) => {
|
|
4
11
|
if (x1 <= y2 && y1 <= x2) {
|
|
5
12
|
return true;
|
|
6
13
|
}
|
|
7
14
|
return false;
|
|
8
|
-
};
|
|
15
|
+
};
|
|
9
16
|
|
|
10
|
-
|
|
11
|
-
abstract equals(other: Role): boolean;
|
|
17
|
+
export abstract class Role {
|
|
18
|
+
abstract equals(other: Role): boolean;
|
|
12
19
|
}
|
|
13
|
-
|
|
20
|
+
|
|
14
21
|
export const NO_TYPE_VARIANT = new Uint8Array([0]);
|
|
15
22
|
|
|
16
23
|
@variant(0)
|
|
@@ -29,83 +36,80 @@ export class Observer extends Role {
|
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
export const REPLICATOR_TYPE_VARIANT = new Uint8Array([2])
|
|
33
|
-
|
|
34
|
-
export const SEGMENT_COORDINATE_SCALE = 4294967295;
|
|
35
|
-
/* export class ReplicationSegment {
|
|
39
|
+
export const REPLICATOR_TYPE_VARIANT = new Uint8Array([2]);
|
|
36
40
|
|
|
41
|
+
export class RoleReplicationSegment {
|
|
37
42
|
@field({ type: "u64" })
|
|
38
43
|
timestamp: bigint;
|
|
39
44
|
|
|
40
45
|
@field({ type: "u32" })
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@field({ type: 'u32' })
|
|
44
|
-
end: number;
|
|
45
|
-
|
|
46
|
+
private factorNominator: number;
|
|
46
47
|
|
|
48
|
+
@field({ type: "u32" })
|
|
49
|
+
private offsetNominator: number;
|
|
47
50
|
|
|
48
51
|
constructor(properties: {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
timestamp
|
|
52
|
+
factor: number;
|
|
53
|
+
offset: number;
|
|
54
|
+
timestamp?: bigint;
|
|
52
55
|
}) {
|
|
53
|
-
const {
|
|
56
|
+
const { factor, timestamp, offset } = properties;
|
|
57
|
+
if (factor > 1 || factor < 0) {
|
|
58
|
+
throw new Error("Expecting factor to be between 0 and 1, got: " + factor);
|
|
59
|
+
}
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
this.timestamp = timestamp ?? BigInt(+new Date());
|
|
62
|
+
this.factorNominator = Math.round(SEGMENT_COORDINATE_SCALE * factor);
|
|
63
|
+
|
|
64
|
+
if (offset > 1 || offset < 0) {
|
|
65
|
+
throw new Error("Expecting offset to be between 0 and 1, got: " + offset);
|
|
57
66
|
}
|
|
58
|
-
this.
|
|
59
|
-
this.end = Math.round(end * SEGMENT_COORDINATE_SCALE);
|
|
60
|
-
this.timestamp = timestamp
|
|
67
|
+
this.offsetNominator = Math.round(SEGMENT_COORDINATE_SCALE * offset);
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
get factor(): number {
|
|
71
|
+
return this.factorNominator / SEGMENT_COORDINATE_SCALE;
|
|
72
|
+
}
|
|
63
73
|
|
|
64
|
-
|
|
74
|
+
get offset(): number {
|
|
75
|
+
return this.offsetNominator / SEGMENT_COORDINATE_SCALE;
|
|
76
|
+
}
|
|
65
77
|
}
|
|
66
|
-
*/
|
|
67
|
-
|
|
68
|
-
/* abstract class Capacity { }
|
|
69
78
|
|
|
70
79
|
@variant(2)
|
|
71
80
|
export class Replicator extends Role {
|
|
81
|
+
@field({ type: vec(RoleReplicationSegment) })
|
|
82
|
+
segments: RoleReplicationSegment[];
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
constructor(properties: {
|
|
85
|
+
factor: number;
|
|
86
|
+
timestamp?: bigint;
|
|
87
|
+
offset: number;
|
|
88
|
+
}) {
|
|
77
89
|
super();
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
factor: number;
|
|
84
|
-
offset: number;
|
|
85
|
-
}) {
|
|
86
|
-
super();
|
|
87
|
-
let timestamp = properties.timestamp || BigInt(+new Date);
|
|
88
|
-
let ranges = getSegmentsFromOffsetAndRange(properties.offset, properties.factor);
|
|
89
|
-
this.segments = ranges.map(x => new ReplicationSegment({ start: x[0], end: x[1], timestamp }));
|
|
90
|
-
}
|
|
91
|
-
*/
|
|
92
|
-
/* get factor(): number {
|
|
93
|
-
return this.segments[0]!.factor;
|
|
94
|
-
}
|
|
90
|
+
const segment: RoleReplicationSegment = new RoleReplicationSegment(
|
|
91
|
+
properties,
|
|
92
|
+
);
|
|
93
|
+
this.segments = [segment];
|
|
94
|
+
}
|
|
95
95
|
|
|
96
|
-
get
|
|
97
|
-
|
|
98
|
-
}
|
|
96
|
+
get factor(): number {
|
|
97
|
+
return this.segments[0]!.factor;
|
|
98
|
+
}
|
|
99
99
|
|
|
100
|
-
get
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
100
|
+
get offset(): number {
|
|
101
|
+
return this.segments[0]!.offset;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get timestamp(): bigint {
|
|
105
|
+
return this.segments[0]!.timestamp;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
equals(other: Role) {
|
|
109
|
+
return (
|
|
110
|
+
other instanceof Replicator &&
|
|
111
|
+
other.factor === this.factor &&
|
|
112
|
+
other.offset === this.offset
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|