@peerbit/shared-log 9.0.5 → 9.0.6-b57d808

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
+ compatiblity?: 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,28 @@ export class SharedLog<T = Uint8Array> extends Program<
278
282
  return this._replicationSettings;
279
283
  }
280
284
 
285
+ private get v8Behaviour() {
286
+ return (this._logProperties?.compatiblity ?? Number.MAX_SAFE_INTEGER) < 9;
287
+ }
288
+
289
+ // @deprecated
290
+ private getRole() {
291
+ let isFixedReplicationSettings =
292
+ (this._replicationSettings as FixedReplicationOptions).factor !==
293
+ undefined;
294
+ if (isFixedReplicationSettings) {
295
+ const fixedSettings = this
296
+ ._replicationSettings as FixedReplicationOptions;
297
+ return new Replicator({
298
+ factor: fixedSettings.factor,
299
+ offset: fixedSettings.offset ?? 0,
300
+ });
301
+ }
302
+
303
+ // TODO this is not accurate but might be good enough
304
+ return new Observer();
305
+ }
306
+
281
307
  async isReplicating() {
282
308
  if (!this._replicationSettings) {
283
309
  return false;
@@ -596,41 +622,15 @@ export class SharedLog<T = Uint8Array> extends Program<
596
622
  logger.warn("Not allowed to replicate by canReplicate");
597
623
  }
598
624
 
599
- added &&
600
- (await this.rpc.send(
625
+ if (added) {
626
+ await this.rpc.send(
601
627
  new StartedReplicating({ segments: [range.toReplicationRange()] }),
602
628
  {
603
629
  priority: 1,
604
630
  },
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);
631
+ );
630
632
  }
631
-
632
- return changed;
633
- } */
633
+ }
634
634
 
635
635
  async append(
636
636
  data: T,
@@ -1019,12 +1019,16 @@ export class SharedLog<T = Uint8Array> extends Program<
1019
1019
  async _onMessage(
1020
1020
  msg: TransportMessage,
1021
1021
  context: RequestContext,
1022
- ): Promise<TransportMessage | undefined> {
1022
+ ): Promise<void> {
1023
1023
  try {
1024
1024
  if (!context.from) {
1025
1025
  throw new Error("Missing from in update role message");
1026
1026
  }
1027
1027
 
1028
+ if (msg instanceof ResponseRoleMessage) {
1029
+ msg = msg.toReplicationInfoMessage(); // migration
1030
+ }
1031
+
1028
1032
  if (msg instanceof ExchangeHeadsMessage) {
1029
1033
  /**
1030
1034
  * I have received heads from someone else.
@@ -1132,7 +1136,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1132
1136
 
1133
1137
  logger.debug(
1134
1138
  `${this.node.identity.publicKey.hashcode()}: Dropping heads with gid: ${
1135
- entry.entry.gid
1139
+ entry.entry.meta.gid
1136
1140
  }. Because not leader`,
1137
1141
  );
1138
1142
  }
@@ -1300,6 +1304,8 @@ export class SharedLog<T = Uint8Array> extends Program<
1300
1304
  } else if (msg instanceof BlocksMessage) {
1301
1305
  await this.remoteBlocks.onMessage(msg.message);
1302
1306
  } else if (msg instanceof RequestReplicationInfoMessage) {
1307
+ // TODO this message type is never used, should we remove it?
1308
+
1303
1309
  if (context.from.equals(this.node.identity.publicKey)) {
1304
1310
  return;
1305
1311
  }
@@ -1313,6 +1319,28 @@ export class SharedLog<T = Uint8Array> extends Program<
1313
1319
  mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
1314
1320
  },
1315
1321
  );
1322
+
1323
+ // for backwards compatibility (v8) remove this when we are sure that all nodes are v9+
1324
+ if (this.v8Behaviour) {
1325
+ const role = this.getRole();
1326
+ if (role instanceof Replicator) {
1327
+ const fixedSettings = this
1328
+ ._replicationSettings as FixedReplicationOptions;
1329
+ if (fixedSettings.factor === 1) {
1330
+ await this.rpc.send(
1331
+ new ResponseRoleMessage({
1332
+ role,
1333
+ }),
1334
+ {
1335
+ mode: new SilentDelivery({
1336
+ to: [context.from],
1337
+ redundancy: 1,
1338
+ }),
1339
+ },
1340
+ );
1341
+ }
1342
+ }
1343
+ }
1316
1344
  } else if (
1317
1345
  msg instanceof ResponseReplicationInfoMessage ||
1318
1346
  msg instanceof StartedReplicating
@@ -1321,6 +1349,10 @@ export class SharedLog<T = Uint8Array> extends Program<
1321
1349
  return;
1322
1350
  }
1323
1351
 
1352
+ let replicationInfoMessage = msg as
1353
+ | ResponseReplicationInfoMessage
1354
+ | StartedReplicating;
1355
+
1324
1356
  // we have this statement because peers might have changed/announced their role,
1325
1357
  // but we don't know them as "subscribers" yet. i.e. they are not online
1326
1358
 
@@ -1343,7 +1375,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1343
1375
  await this.removeReplicator(context.from!);
1344
1376
  }
1345
1377
  let addedOnce = false;
1346
- for (const segment of msg.segments) {
1378
+ for (const segment of replicationInfoMessage.segments) {
1347
1379
  const added = await this.addReplicationRange(
1348
1380
  segment.toReplicationRangeIndexable(context.from!),
1349
1381
  context.from!,
@@ -1646,7 +1678,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1646
1678
  },
1647
1679
  ) {
1648
1680
  return this.isLeader(
1649
- entry.gid,
1681
+ entry.meta.gid,
1650
1682
  decodeReplicas(entry).getValue(this),
1651
1683
  options,
1652
1684
  );
@@ -1698,6 +1730,15 @@ export class SharedLog<T = Uint8Array> extends Program<
1698
1730
  },
1699
1731
  )
1700
1732
  .catch((e) => logger.error(e.toString()));
1733
+
1734
+ if (this.v8Behaviour) {
1735
+ // for backwards compatibility
1736
+ this.rpc
1737
+ .send(new ResponseRoleMessage({ role: this.getRole() }), {
1738
+ mode: new SilentDelivery({ redundancy: 1, to: [publicKey] }),
1739
+ })
1740
+ .catch((e) => logger.error(e.toString()));
1741
+ }
1701
1742
  }
1702
1743
  } else {
1703
1744
  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,81 @@ 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
 
74
+ get offset(): number {
75
+ return this.offsetNominator / SEGMENT_COORDINATE_SCALE;
76
+ }
64
77
 
65
78
  }
66
- */
67
-
68
- /* abstract class Capacity { }
69
79
 
70
80
  @variant(2)
71
81
  export class Replicator extends Role {
82
+ @field({ type: vec(RoleReplicationSegment) })
83
+ segments: RoleReplicationSegment[];
72
84
 
73
- @field({ type: vec(Capacity) })
74
- capacity: Capacity[];
75
-
76
- constructor(properties?: { capacity: Capacity[] }) {
85
+ constructor(properties: {
86
+ factor: number;
87
+ timestamp?: bigint;
88
+ offset: number;
89
+ }) {
77
90
  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
- }
91
+ const segment: RoleReplicationSegment = new RoleReplicationSegment(
92
+ properties,
93
+ );
94
+ this.segments = [segment];
95
+ }
95
96
 
96
- get offset(): number {
97
- return this.segments[0]!.offset;
98
- }
97
+ get factor(): number {
98
+ return this.segments[0]!.factor;
99
+ }
99
100
 
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
- /* } */
101
+ get offset(): number {
102
+ return this.segments[0]!.offset;
103
+ }
104
+
105
+ get timestamp(): bigint {
106
+ return this.segments[0]!.timestamp;
107
+ }
108
+
109
+ equals(other: Role) {
110
+ return (
111
+ other instanceof Replicator &&
112
+ other.factor === this.factor &&
113
+ other.offset === this.offset
114
+ );
115
+ }
116
+ }