@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/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.getGid()
100
+ ? (await head.getMeta()).gid
100
101
  : head instanceof ShallowEntry
101
102
  ? head.meta.gid
102
- : head.entry.getGid());
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> & LogEvents<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
- (await this.rpc.send(
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<TransportMessage | undefined> {
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 msg.segments) {
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);
@@ -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 { SEGMENT_COORDINATE_SCALE } from "./role.js";
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, 2])
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, 3])
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
- /* import { field, variant, vec } from "@dao-xyz/borsh"; */
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
- /* export const overlaps = (x1: number, x2: number, y1: number, y2: number) => {
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
- /* export abstract class Role {
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
- start: number;
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
- start: number;
50
- end: number;
51
- timestamp: bigint;
52
+ factor: number;
53
+ offset: number;
54
+ timestamp?: bigint;
52
55
  }) {
53
- const { start, end, timestamp } = properties;
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
- if (start > end) {
56
- throw new Error("Range 'start' needs to be lower or equal to 'end'")
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.start = Math.round(start * SEGMENT_COORDINATE_SCALE);
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
- @field({ type: vec(Capacity) })
74
- capacity: Capacity[];
75
-
76
- constructor(properties?: { capacity: Capacity[] }) {
84
+ constructor(properties: {
85
+ factor: number;
86
+ timestamp?: bigint;
87
+ offset: number;
88
+ }) {
77
89
  super();
78
- this.capacity = properties?.capacity || [];
79
- } */
80
-
81
- /* constructor(properties: {
82
- timestamp?: bigint;
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 offset(): number {
97
- return this.segments[0]!.offset;
98
- }
96
+ get factor(): number {
97
+ return this.segments[0]!.factor;
98
+ }
99
99
 
100
- get timestamp(): bigint {
101
- return this.segments[0]!.timestamp;
102
- } */
103
-
104
- /* equals(other: Role) {
105
- return (
106
- other instanceof Replicator &&
107
- other.factor === this.factor &&
108
- other.offset === this.offset
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
+ }