@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.
Files changed (104) hide show
  1. package/dist/benchmark/get-samples.js +190 -64
  2. package/dist/benchmark/get-samples.js.map +1 -1
  3. package/dist/benchmark/index.js +16 -38
  4. package/dist/benchmark/index.js.map +1 -1
  5. package/dist/benchmark/memory/child.js.map +1 -1
  6. package/dist/benchmark/partial-sync.d.ts +3 -0
  7. package/dist/benchmark/partial-sync.d.ts.map +1 -0
  8. package/dist/benchmark/partial-sync.js +121 -0
  9. package/dist/benchmark/partial-sync.js.map +1 -0
  10. package/dist/benchmark/replication-prune.js.map +1 -1
  11. package/dist/benchmark/replication.js.map +1 -1
  12. package/dist/benchmark/to-rebalance.d.ts +2 -0
  13. package/dist/benchmark/to-rebalance.d.ts.map +1 -0
  14. package/dist/benchmark/to-rebalance.js +117 -0
  15. package/dist/benchmark/to-rebalance.js.map +1 -0
  16. package/dist/benchmark/utils.d.ts +24 -0
  17. package/dist/benchmark/utils.d.ts.map +1 -0
  18. package/dist/benchmark/utils.js +47 -0
  19. package/dist/benchmark/utils.js.map +1 -0
  20. package/dist/src/debounce.d.ts +2 -2
  21. package/dist/src/debounce.d.ts.map +1 -1
  22. package/dist/src/debounce.js +17 -47
  23. package/dist/src/debounce.js.map +1 -1
  24. package/dist/src/exchange-heads.d.ts +1 -13
  25. package/dist/src/exchange-heads.d.ts.map +1 -1
  26. package/dist/src/exchange-heads.js +0 -32
  27. package/dist/src/exchange-heads.js.map +1 -1
  28. package/dist/src/index.d.ts +119 -60
  29. package/dist/src/index.d.ts.map +1 -1
  30. package/dist/src/index.js +1116 -762
  31. package/dist/src/index.js.map +1 -1
  32. package/dist/src/integers.d.ts +22 -0
  33. package/dist/src/integers.d.ts.map +1 -0
  34. package/dist/src/integers.js +76 -0
  35. package/dist/src/integers.js.map +1 -0
  36. package/dist/src/pid.d.ts.map +1 -1
  37. package/dist/src/pid.js +22 -22
  38. package/dist/src/pid.js.map +1 -1
  39. package/dist/src/ranges.d.ts +168 -38
  40. package/dist/src/ranges.d.ts.map +1 -1
  41. package/dist/src/ranges.js +869 -272
  42. package/dist/src/ranges.js.map +1 -1
  43. package/dist/src/replication-domain-hash.d.ts +2 -3
  44. package/dist/src/replication-domain-hash.d.ts.map +1 -1
  45. package/dist/src/replication-domain-hash.js +40 -15
  46. package/dist/src/replication-domain-hash.js.map +1 -1
  47. package/dist/src/replication-domain-time.d.ts +5 -5
  48. package/dist/src/replication-domain-time.d.ts.map +1 -1
  49. package/dist/src/replication-domain-time.js +2 -0
  50. package/dist/src/replication-domain-time.js.map +1 -1
  51. package/dist/src/replication-domain.d.ts +17 -19
  52. package/dist/src/replication-domain.d.ts.map +1 -1
  53. package/dist/src/replication-domain.js +2 -6
  54. package/dist/src/replication-domain.js.map +1 -1
  55. package/dist/src/replication.d.ts +6 -6
  56. package/dist/src/replication.d.ts.map +1 -1
  57. package/dist/src/replication.js +4 -4
  58. package/dist/src/replication.js.map +1 -1
  59. package/dist/src/role.d.ts +3 -6
  60. package/dist/src/role.d.ts.map +1 -1
  61. package/dist/src/role.js +4 -5
  62. package/dist/src/role.js.map +1 -1
  63. package/dist/src/sync/index.d.ts +40 -0
  64. package/dist/src/sync/index.d.ts.map +1 -0
  65. package/dist/src/sync/index.js +2 -0
  66. package/dist/src/sync/index.js.map +1 -0
  67. package/dist/src/sync/rateless-iblt.d.ts +124 -0
  68. package/dist/src/sync/rateless-iblt.d.ts.map +1 -0
  69. package/dist/src/sync/rateless-iblt.js +495 -0
  70. package/dist/src/sync/rateless-iblt.js.map +1 -0
  71. package/dist/src/sync/simple.d.ts +69 -0
  72. package/dist/src/sync/simple.d.ts.map +1 -0
  73. package/dist/src/sync/simple.js +338 -0
  74. package/dist/src/sync/simple.js.map +1 -0
  75. package/dist/src/sync/wasm-init.browser.d.ts +1 -0
  76. package/dist/src/sync/wasm-init.browser.d.ts.map +1 -0
  77. package/dist/src/sync/wasm-init.browser.js +3 -0
  78. package/dist/src/sync/wasm-init.browser.js.map +1 -0
  79. package/dist/src/sync/wasm-init.d.ts +2 -0
  80. package/dist/src/sync/wasm-init.d.ts.map +1 -0
  81. package/dist/src/sync/wasm-init.js +13 -0
  82. package/dist/src/sync/wasm-init.js.map +1 -0
  83. package/dist/src/utils.d.ts +3 -3
  84. package/dist/src/utils.d.ts.map +1 -1
  85. package/dist/src/utils.js +2 -2
  86. package/dist/src/utils.js.map +1 -1
  87. package/package.json +10 -6
  88. package/src/debounce.ts +16 -51
  89. package/src/exchange-heads.ts +1 -23
  90. package/src/index.ts +1532 -1038
  91. package/src/integers.ts +102 -0
  92. package/src/pid.ts +23 -22
  93. package/src/ranges.ts +1204 -413
  94. package/src/replication-domain-hash.ts +43 -18
  95. package/src/replication-domain-time.ts +9 -9
  96. package/src/replication-domain.ts +21 -31
  97. package/src/replication.ts +10 -9
  98. package/src/role.ts +4 -6
  99. package/src/sync/index.ts +51 -0
  100. package/src/sync/rateless-iblt.ts +617 -0
  101. package/src/sync/simple.ts +403 -0
  102. package/src/sync/wasm-init.browser.ts +1 -0
  103. package/src/sync/wasm-init.ts +14 -0
  104. package/src/utils.ts +10 -4
@@ -1,41 +1,66 @@
1
- import { BinaryReader, BinaryWriter } from "@dao-xyz/borsh";
1
+ import { BinaryWriter } from "@dao-xyz/borsh";
2
2
  import { sha256 } from "@peerbit/crypto";
3
3
  import type { ShallowOrFullEntry } from "@peerbit/log";
4
- import type { EntryReplicated } from "./ranges.js";
4
+ import { bytesToNumber } from "./integers.js";
5
+ import { type EntryReplicated } from "./ranges.js";
5
6
  import {
6
7
  type Log,
7
8
  type ReplicationDomain,
8
9
  type ReplicationDomainMapper,
9
10
  } from "./replication-domain.js";
10
11
 
11
- export const hashToU32 = (hash: Uint8Array) => {
12
+ /* const hashToU32 = (hash: Uint8Array) => {
12
13
  const seedNumber = new BinaryReader(
13
14
  hash.subarray(hash.length - 4, hash.length),
14
15
  ).u32();
15
16
  return seedNumber;
16
17
  };
17
18
 
18
- const hashTransformer: ReplicationDomainMapper<any> = async (
19
- entry: ShallowOrFullEntry<any> | EntryReplicated,
20
- ) => {
21
- // For a fixed set or members, the choosen leaders will always be the same (address invariant)
22
- // This allows for that same content is always chosen to be distributed to same peers, to remove unecessary copies
23
19
 
24
- // Convert this thing we wan't to distribute to 8 bytes so we get can convert it into a u64
25
- // modulus into an index
26
- const utf8writer = new BinaryWriter();
27
- utf8writer.string(entry.meta.gid);
28
- const seed = await sha256(utf8writer.finalize());
20
+ const hashToU64 = (hash: Uint8Array): bigint => {
21
+ const seedNumber = new BinaryReader(
22
+ hash.subarray(hash.length - 4, hash.length), //
23
+ ).u64();
24
+ return seedNumber;
25
+ };
26
+ */
29
27
 
30
- // convert hash of slot to a number
31
- return hashToU32(seed);
28
+ const hashTransformer = <R extends "u32" | "u64">(
29
+ resolution: R,
30
+ ): ReplicationDomainMapper<any, R> => {
31
+ const numberConverter = bytesToNumber(resolution);
32
+ if (resolution === "u32") {
33
+ return (async (entry: ShallowOrFullEntry<any> | EntryReplicated<R>) => {
34
+ const utf8writer = new BinaryWriter();
35
+ utf8writer.string(entry.meta.gid);
36
+ const seed = await sha256(utf8writer.finalize());
37
+ return numberConverter(seed);
38
+ }) as ReplicationDomainMapper<any, R>;
39
+ } else if (resolution === "u64") {
40
+ return (async (entry: ShallowOrFullEntry<any> | EntryReplicated<R>) => {
41
+ const utf8writer = new BinaryWriter();
42
+ utf8writer.string(entry.meta.gid);
43
+ const seed = await sha256(utf8writer.finalize());
44
+ return numberConverter(seed);
45
+ }) as ReplicationDomainMapper<any, R>;
46
+ } else {
47
+ throw new Error("Unsupported resolution");
48
+ }
32
49
  };
33
50
 
34
- export type ReplicationDomainHash = ReplicationDomain<undefined, any>;
35
- export const createReplicationDomainHash: () => ReplicationDomainHash = () => {
51
+ export type ReplicationDomainHash<R extends "u32" | "u64"> = ReplicationDomain<
52
+ undefined,
53
+ any,
54
+ R
55
+ >;
56
+
57
+ export const createReplicationDomainHash = <R extends "u32" | "u64">(
58
+ resolution: R,
59
+ ): ReplicationDomainHash<R> => {
36
60
  return {
61
+ resolution,
37
62
  type: "hash",
38
- fromEntry: hashTransformer,
63
+ fromEntry: hashTransformer<R>(resolution),
39
64
  fromArgs: async (args: undefined, log: Log) => {
40
65
  return {
41
66
  offset: log.node.identity.publicKey,
@@ -1,9 +1,8 @@
1
1
  import type { ShallowOrFullEntry } from "@peerbit/log";
2
- import type { EntryReplicated } from "./ranges.js";
2
+ import { type EntryReplicated } from "./ranges.js";
3
3
  import {
4
4
  type ReplicationDomain,
5
5
  type ReplicationDomainMapper,
6
- type u32,
7
6
  } from "./replication-domain.js";
8
7
 
9
8
  type TimeUnit = "seconds" | "milliseconds" | "microseconds" | "nanoseconds";
@@ -24,11 +23,11 @@ const scalarMilliToUnit = {
24
23
  export const fromEntry = (
25
24
  origin: Date,
26
25
  unit: TimeUnit = "milliseconds",
27
- ): ReplicationDomainMapper<any> => {
26
+ ): ReplicationDomainMapper<any, "u32"> => {
28
27
  const scalar = scalarNanoToUnit[unit];
29
28
  const originTime = +origin / scalarMilliToUnit[unit];
30
29
 
31
- const fn = (entry: ShallowOrFullEntry<any> | EntryReplicated) => {
30
+ const fn = (entry: ShallowOrFullEntry<any> | EntryReplicated<"u32">) => {
32
31
  const cursor = entry.meta.clock.timestamp.wallTime / scalar;
33
32
  return Math.round(Number(cursor) - originTime);
34
33
  };
@@ -37,9 +36,9 @@ export const fromEntry = (
37
36
 
38
37
  type TimeRange = { from: number; to: number };
39
38
 
40
- export type ReplicationDomainTime = ReplicationDomain<TimeRange, any> & {
41
- fromTime: (time: number | Date) => u32;
42
- fromDuration: (duration: number) => u32;
39
+ export type ReplicationDomainTime = ReplicationDomain<TimeRange, any, "u32"> & {
40
+ fromTime: (time: number | Date) => number;
41
+ fromDuration: (duration: number) => number;
43
42
  };
44
43
 
45
44
  export const createReplicationDomainTime = (
@@ -48,16 +47,17 @@ export const createReplicationDomainTime = (
48
47
  ): ReplicationDomainTime => {
49
48
  const originScaled = +origin * scalarMilliToUnit[unit];
50
49
  const fromMilliToUnit = scalarMilliToUnit[unit];
51
- const fromTime = (time: number | Date): u32 => {
50
+ const fromTime = (time: number | Date): number => {
52
51
  return (
53
52
  (typeof time === "number" ? time : +time * fromMilliToUnit) - originScaled
54
53
  );
55
54
  };
56
55
 
57
- const fromDuration = (duration: number): u32 => {
56
+ const fromDuration = (duration: number): number => {
58
57
  return duration;
59
58
  };
60
59
  return {
60
+ resolution: "u32",
61
61
  type: "time",
62
62
  fromTime,
63
63
  fromDuration,
@@ -1,15 +1,15 @@
1
1
  import type { PublicSignKey } from "@peerbit/crypto";
2
2
  import { type Index } from "@peerbit/indexer-interface";
3
3
  import type { Entry, ShallowEntry } from "@peerbit/log";
4
- import { debounceAcculmulator } from "./debounce.js";
5
- import type { EntryReplicated, ReplicationRangeIndexable } from "./ranges.js";
4
+ import { debounceAccumulator } from "./debounce.js";
5
+ import type { ReplicationRangeIndexable } from "./index.js";
6
+ import type { NumberFromType } from "./integers.js";
7
+ import type { EntryReplicated } from "./ranges.js";
6
8
  import type { ReplicationLimits } from "./replication.js";
7
- import { MAX_U32 } from "./role.js";
8
9
 
9
- export type u32 = number;
10
- export type ReplicationDomainMapper<T> = (
11
- entry: Entry<T> | ShallowEntry | EntryReplicated,
12
- ) => Promise<u32> | u32;
10
+ export type ReplicationDomainMapper<T, R extends "u32" | "u64"> = (
11
+ entry: Entry<T> | ShallowEntry | EntryReplicated<R>,
12
+ ) => Promise<NumberFromType<R>> | NumberFromType<R>;
13
13
 
14
14
  export type Log = {
15
15
  replicas: ReplicationLimits;
@@ -18,8 +18,7 @@ export type Log = {
18
18
  publicKey: PublicSignKey;
19
19
  };
20
20
  };
21
- syncInFlight: Map<string, Map<string, { timestamp: number }>>;
22
- replicationIndex: Index<ReplicationRangeIndexable>;
21
+ replicationIndex: Index<ReplicationRangeIndexable<any>>;
23
22
  getDefaultMinRoleAge: () => Promise<number>;
24
23
  };
25
24
  export type ReplicationDomainCoverSet<Args> = (
@@ -28,24 +27,24 @@ export type ReplicationDomainCoverSet<Args> = (
28
27
  args: Args,
29
28
  ) => Promise<string[]> | string[]; // minimum set of peers that covers all the data
30
29
 
31
- type CoverRange = {
32
- offset: number | PublicSignKey;
33
- length?: number;
30
+ type CoverRange<T extends number | bigint> = {
31
+ offset: T | PublicSignKey;
32
+ length?: T;
34
33
  };
35
34
  export type ReplicationChanges = ReplicationChange[];
36
35
  export type ReplicationChange =
37
36
  | {
38
37
  type: "added";
39
- range: ReplicationRangeIndexable;
38
+ range: ReplicationRangeIndexable<any>;
40
39
  }
41
40
  | {
42
41
  type: "removed";
43
- range: ReplicationRangeIndexable;
42
+ range: ReplicationRangeIndexable<any>;
44
43
  }
45
44
  | {
46
45
  type: "updated";
47
- range: ReplicationRangeIndexable;
48
- prev: ReplicationRangeIndexable;
46
+ range: ReplicationRangeIndexable<any>;
47
+ prev: ReplicationRangeIndexable<any>;
49
48
  };
50
49
 
51
50
  export const mergeReplicationChanges = (
@@ -62,7 +61,7 @@ export const debounceAggregationChanges = (
62
61
  fn: (changeOrChanges: ReplicationChange[]) => void,
63
62
  delay: number,
64
63
  ) => {
65
- return debounceAcculmulator(
64
+ return debounceAccumulator(
66
65
  (result) => {
67
66
  return fn([...result.values()]);
68
67
  },
@@ -90,24 +89,15 @@ export const debounceAggregationChanges = (
90
89
  );
91
90
  };
92
91
 
93
- export type ReplicationDomain<Args, T> = {
92
+ export type ReplicationDomain<Args, T, R extends "u32" | "u64"> = {
93
+ resolution: R;
94
94
  type: string;
95
- fromEntry: ReplicationDomainMapper<T>;
95
+ fromEntry: ReplicationDomainMapper<T, R>;
96
96
  fromArgs: (
97
97
  args: Args | undefined,
98
98
  log: Log,
99
- ) => Promise<CoverRange> | CoverRange;
100
-
101
- // to rebalance will return an async iterator of objects that will be added to the log
102
- /* toRebalance(
103
- change: ReplicationChange,
104
- index: Index<EntryWithCoordinate>
105
- ): AsyncIterable<{ gid: string, entries: { coordinate: number, hash: string }[] }> | Promise<AsyncIterable<{ gid: string, entries: EntryWithCoordinate[] }>>; */
106
- };
107
-
108
- export const uniformToU32 = (cursor: number) => {
109
- return cursor * MAX_U32;
99
+ ) => Promise<CoverRange<NumberFromType<R>>> | CoverRange<NumberFromType<R>>;
110
100
  };
111
101
 
112
102
  export type ExtractDomainArgs<T> =
113
- T extends ReplicationDomain<infer Args, any> ? Args : never;
103
+ T extends ReplicationDomain<infer Args, any, any> ? Args : never;
@@ -11,8 +11,9 @@ import { type Index } from "@peerbit/indexer-interface";
11
11
  import { TransportMessage } from "./message.js";
12
12
  import {
13
13
  ReplicationIntent,
14
- ReplicationRange,
15
14
  type ReplicationRangeIndexable,
15
+ ReplicationRangeMessage,
16
+ ReplicationRangeMessageU32,
16
17
  } from "./ranges.js";
17
18
  import { Observer, Replicator, Role } from "./role.js";
18
19
 
@@ -20,7 +21,7 @@ export type ReplicationLimits = { min: MinReplicas; max?: MinReplicas };
20
21
 
21
22
  interface SharedLog {
22
23
  replicas: Partial<ReplicationLimits>;
23
- replicationIndex: Index<ReplicationRangeIndexable> | undefined;
24
+ replicationIndex: Index<ReplicationRangeIndexable<any>> | undefined;
24
25
  }
25
26
 
26
27
  export class MinReplicas {
@@ -67,7 +68,7 @@ export class ResponseRoleMessage extends TransportMessage {
67
68
  segments:
68
69
  this.role instanceof Replicator
69
70
  ? this.role.segments.map((x) => {
70
- return new ReplicationRange({
71
+ return new ReplicationRangeMessageU32({
71
72
  id: randomBytes(32),
72
73
  offset: x.offset,
73
74
  factor: x.factor,
@@ -82,10 +83,10 @@ export class ResponseRoleMessage extends TransportMessage {
82
83
 
83
84
  @variant([1, 2])
84
85
  export class AllReplicatingSegmentsMessage extends TransportMessage {
85
- @field({ type: vec(ReplicationRange) })
86
- segments: ReplicationRange[];
86
+ @field({ type: vec(ReplicationRangeMessage) })
87
+ segments: ReplicationRangeMessage<any>[];
87
88
 
88
- constructor(properties: { segments: ReplicationRange[] }) {
89
+ constructor(properties: { segments: ReplicationRangeMessage<any>[] }) {
89
90
  super();
90
91
  this.segments = properties.segments;
91
92
  }
@@ -93,10 +94,10 @@ export class AllReplicatingSegmentsMessage extends TransportMessage {
93
94
 
94
95
  @variant([1, 3])
95
96
  export class AddedReplicationSegmentMessage extends TransportMessage {
96
- @field({ type: vec(ReplicationRange) })
97
- segments: ReplicationRange[];
97
+ @field({ type: vec(ReplicationRangeMessage) })
98
+ segments: ReplicationRangeMessage<any>[];
98
99
 
99
- constructor(properties: { segments: ReplicationRange[] }) {
100
+ constructor(properties: { segments: ReplicationRangeMessage<any>[] }) {
100
101
  super();
101
102
  this.segments = properties.segments;
102
103
  }
package/src/role.ts CHANGED
@@ -4,10 +4,7 @@
4
4
  * Roles have been replaces with just replication segments.
5
5
  */
6
6
  import { field, variant, vec } from "@dao-xyz/borsh";
7
-
8
- export const MAX_U32 = 4294967295;
9
- export const HALF_MAX_U32 = 2147483647; // rounded down
10
- export const scaleToU32 = (value: number) => Math.round(MAX_U32 * value);
7
+ import { MAX_U32, denormalizer } from "./integers.js";
11
8
 
12
9
  export const overlaps = (x1: number, x2: number, y1: number, y2: number) => {
13
10
  if (x1 <= y2 && y1 <= x2) {
@@ -40,6 +37,7 @@ export class Observer extends Role {
40
37
 
41
38
  export const REPLICATOR_TYPE_VARIANT = new Uint8Array([2]);
42
39
 
40
+ const denormalizeru32 = denormalizer("u32");
43
41
  export class RoleReplicationSegment {
44
42
  @field({ type: "u64" })
45
43
  timestamp: bigint;
@@ -61,12 +59,12 @@ export class RoleReplicationSegment {
61
59
  }
62
60
 
63
61
  this.timestamp = timestamp ?? BigInt(+new Date());
64
- this.factorNominator = Math.round(MAX_U32 * factor);
62
+ this.factorNominator = denormalizeru32(factor);
65
63
 
66
64
  if (offset > 1 || offset < 0) {
67
65
  throw new Error("Expecting offset to be between 0 and 1, got: " + offset);
68
66
  }
69
- this.offsetNominator = Math.round(MAX_U32 * offset);
67
+ this.offsetNominator = denormalizeru32(factor);
70
68
  }
71
69
 
72
70
  get factor(): number {
@@ -0,0 +1,51 @@
1
+ import type { Cache } from "@peerbit/cache";
2
+ import type { PublicSignKey } from "@peerbit/crypto";
3
+ import type { Index } from "@peerbit/indexer-interface";
4
+ import type { Entry, Log } from "@peerbit/log";
5
+ import type { RPC, RequestContext } from "@peerbit/rpc";
6
+ import type { EntryWithRefs } from "../exchange-heads.js";
7
+ import type { Numbers } from "../integers.js";
8
+ import type { TransportMessage } from "../message.js";
9
+ import type { EntryReplicated, ReplicationRangeIndexable } from "../ranges.js";
10
+
11
+ export type SynchronizerComponents<R extends "u32" | "u64"> = {
12
+ rpc: RPC<TransportMessage, TransportMessage>;
13
+ rangeIndex: Index<ReplicationRangeIndexable<R>, any>;
14
+ entryIndex: Index<EntryReplicated<R>, any>;
15
+ log: Log<any>;
16
+ coordinateToHash: Cache<string>;
17
+ numbers: Numbers<R>;
18
+ };
19
+ export type SynchronizerConstructor<R extends "u32" | "u64"> = new (
20
+ properties: SynchronizerComponents<R>,
21
+ ) => Syncronizer<R>;
22
+
23
+ export type SyncableKey = string | bigint; // hash or coordinate
24
+
25
+ export interface Syncronizer<R extends "u32" | "u64"> {
26
+ onMaybeMissingEntries(properties: {
27
+ entries: Map<string, EntryReplicated<R>>;
28
+ targets: string[];
29
+ }): Promise<void> | void;
30
+
31
+ onMessage(
32
+ message: TransportMessage,
33
+ context: RequestContext,
34
+ ): Promise<boolean> | boolean;
35
+
36
+ onReceivedEntries(properties: {
37
+ entries: EntryWithRefs<any>[];
38
+ from: PublicSignKey;
39
+ }): Promise<void> | void;
40
+
41
+ onEntryAdded(entry: Entry<any>): void;
42
+ onEntryRemoved(hash: string): void;
43
+ onPeerDisconnected(key: PublicSignKey): void;
44
+
45
+ open(): Promise<void> | void;
46
+ close(): Promise<void> | void;
47
+
48
+ get pending(): number;
49
+
50
+ get syncInFlight(): Map<string, Map<SyncableKey, { timestamp: number }>>;
51
+ }