@peerbit/shared-log 4.0.6 → 4.0.7

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
@@ -5,7 +5,8 @@ import {
5
5
  Entry,
6
6
  Log,
7
7
  LogEvents,
8
- LogProperties
8
+ LogProperties,
9
+ ShallowEntry
9
10
  } from "@peerbit/log";
10
11
  import { Program, ProgramEvents } from "@peerbit/program";
11
12
  import { BinaryWriter, BorshError, field, variant } from "@dao-xyz/borsh";
@@ -19,15 +20,15 @@ import { logger as loggerFn } from "@peerbit/logger";
19
20
  import {
20
21
  EntryWithRefs,
21
22
  ExchangeHeadsMessage,
22
- RequestIHave,
23
- ResponseIHave,
23
+ RequestIPrune,
24
+ ResponseIPrune,
24
25
  createExchangeHeadsMessage
25
26
  } from "./exchange-heads.js";
26
27
  import {
27
28
  SubscriptionEvent,
28
29
  UnsubcriptionEvent
29
30
  } from "@peerbit/pubsub-interface";
30
- import { AbortError, delay, TimeoutError, waitFor } from "@peerbit/time";
31
+ import { AbortError, waitFor } from "@peerbit/time";
31
32
  import { Observer, Replicator, Role } from "./role.js";
32
33
  import {
33
34
  AbsoluteReplicas,
@@ -45,14 +46,19 @@ import pDefer, { DeferredPromise } from "p-defer";
45
46
  import { Cache } from "@peerbit/cache";
46
47
  import { CustomEvent } from "@libp2p/interface";
47
48
  import yallist from "yallist";
48
- import { AcknowledgeDelivery, SilentDelivery } from "@peerbit/stream-interface";
49
+ import {
50
+ AcknowledgeDelivery,
51
+ AnyWhere,
52
+ SeekDelivery,
53
+ SilentDelivery
54
+ } from "@peerbit/stream-interface";
49
55
  import { AnyBlockStore, RemoteBlocks } from "@peerbit/blocks";
50
56
  import { BlocksMessage } from "./blocks.js";
51
57
  import debounce from "p-debounce";
52
58
  import { PIDReplicationController, ReplicationErrorFunction } from "./pid.js";
53
59
  export type { ReplicationErrorFunction };
54
60
  export * from "./replication.js";
55
-
61
+ import PQueue from "p-queue";
56
62
  export { Observer, Replicator, Role };
57
63
 
58
64
  export const logger = loggerFn({ module: "shared-log" });
@@ -125,7 +131,7 @@ export type SharedLogOptions = {
125
131
  export const DEFAULT_MIN_REPLICAS = 2;
126
132
  export const WAIT_FOR_REPLICATOR_TIMEOUT = 9000;
127
133
  export const WAIT_FOR_ROLE_MATURITY = 5000;
128
- const REBALANCE_DEBOUNCE_INTERAVAL = 50;
134
+ const REBALANCE_DEBOUNCE_INTERAVAL = 30;
129
135
 
130
136
  export type Args<T> = LogProperties<T> & LogEvents<T> & SharedLogOptions;
131
137
 
@@ -152,7 +158,6 @@ export class SharedLog<T = Uint8Array> extends Program<
152
158
  // options
153
159
  private _role: Observer | Replicator;
154
160
  private _roleOptions: AdaptiveReplicatorOptions | Observer | Replicator;
155
-
156
161
  private _sortedPeersCache: yallist<ReplicatorRect> | undefined;
157
162
  private _totalParticipation: number;
158
163
  private _gidPeersHistory: Map<string, Set<string>>;
@@ -175,7 +180,8 @@ export class SharedLog<T = Uint8Array> extends Program<
175
180
  {
176
181
  promise: DeferredPromise<void>;
177
182
  clear: () => void;
178
- callback: (publicKeyHash: string) => Promise<void> | void;
183
+ resolve: (publicKeyHash: string) => Promise<void> | void;
184
+ reject(reason: any): Promise<void> | void;
179
185
  }
180
186
  >;
181
187
 
@@ -210,6 +216,16 @@ export class SharedLog<T = Uint8Array> extends Program<
210
216
  return this._totalParticipation;
211
217
  }
212
218
 
219
+ private setupRebalanceDebounceFunction() {
220
+ this.rebalanceParticipationDebounced = debounce(
221
+ () => this.rebalanceParticipation(),
222
+ Math.max(
223
+ REBALANCE_DEBOUNCE_INTERAVAL,
224
+ (this.getReplicatorsSorted()?.length || 0) *
225
+ REBALANCE_DEBOUNCE_INTERAVAL
226
+ )
227
+ );
228
+ }
213
229
  private setupRole(options?: RoleOptions) {
214
230
  this.rebalanceParticipationDebounced = undefined;
215
231
 
@@ -219,10 +235,7 @@ export class SharedLog<T = Uint8Array> extends Program<
219
235
  errorFunction: options?.error
220
236
  });
221
237
 
222
- this.rebalanceParticipationDebounced = debounce(
223
- () => this.rebalanceParticipation(),
224
- REBALANCE_DEBOUNCE_INTERAVAL // TODO make dynamic
225
- );
238
+ this.setupRebalanceDebounceFunction();
226
239
  };
227
240
 
228
241
  if (options instanceof Observer || options instanceof Replicator) {
@@ -250,17 +263,23 @@ export class SharedLog<T = Uint8Array> extends Program<
250
263
  }
251
264
 
252
265
  // setup the initial role
266
+
253
267
  if (
254
268
  this._roleOptions instanceof Replicator ||
255
269
  this._roleOptions instanceof Observer
256
270
  ) {
257
- this._role = this._roleOptions; // Fixed
271
+ this._role = this._roleOptions as Replicator | Observer;
258
272
  } else {
259
- this._role = new Replicator({
260
- // initial role in a dynamic setup
261
- factor: 1,
262
- timestamp: BigInt(+new Date())
263
- });
273
+ if (this._roleOptions.limits) {
274
+ this._role = new Replicator({
275
+ // initial role in a dynamic setup
276
+ factor: 1
277
+ });
278
+ } else {
279
+ this._role = new Replicator({
280
+ factor: 1
281
+ });
282
+ }
264
283
  }
265
284
 
266
285
  return this._role;
@@ -286,10 +305,14 @@ export class SharedLog<T = Uint8Array> extends Program<
286
305
  }
287
306
  await this.rpc.subscribe();
288
307
 
289
- await this.rpc.send(new ResponseRoleMessage(role));
308
+ await this.rpc.send(new ResponseRoleMessage({ role: this._role }), {
309
+ mode: new SeekDelivery({
310
+ redundancy: 1
311
+ })
312
+ });
290
313
 
291
- if (onRoleChange && changed) {
292
- this.onRoleChange(undefined, this._role, this.node.identity.publicKey);
314
+ if (onRoleChange && changed !== "none") {
315
+ this.onRoleChange(this._role, this.node.identity.publicKey);
293
316
  }
294
317
 
295
318
  return changed;
@@ -382,7 +405,9 @@ export class SharedLog<T = Uint8Array> extends Program<
382
405
  local: localBlocks,
383
406
  publish: (message, options) =>
384
407
  this.rpc.send(new BlocksMessage(message), {
385
- to: options?.to
408
+ mode: options?.to
409
+ ? new SilentDelivery({ to: options.to, redundancy: 1 })
410
+ : undefined
386
411
  }),
387
412
  waitFor: this.rpc.waitFor.bind(this.rpc)
388
413
  });
@@ -587,6 +612,8 @@ export class SharedLog<T = Uint8Array> extends Program<
587
612
  const groupedByGid = await groupByGid(filteredHeads);
588
613
  const promises: Promise<void>[] = [];
589
614
 
615
+ /// console.log("ADD CACHE", this.node.identity.publicKey.hashcode(), context.from!.hashcode(), groupedByGid.size)
616
+
590
617
  for (const [gid, entries] of groupedByGid) {
591
618
  const fn = async () => {
592
619
  const headsWithGid = this.log.headsIndex.gids.get(gid);
@@ -601,13 +628,24 @@ export class SharedLog<T = Uint8Array> extends Program<
601
628
  entries.map((x) => x.entry)
602
629
  );
603
630
 
604
- const isLeader = await this.waitForIsLeader(
631
+ const leaders = await this.waitForIsLeader(
605
632
  gid,
606
633
  Math.max(maxReplicasFromHead, maxReplicasFromNewEntries)
607
634
  );
635
+ const isLeader = !!leaders;
636
+ if (isLeader) {
637
+ if (leaders.find((x) => x === context.from!.hashcode())) {
638
+ let peerSet = this._gidPeersHistory.get(gid);
639
+ if (!peerSet) {
640
+ peerSet = new Set();
641
+ this._gidPeersHistory.set(gid, peerSet);
642
+ }
643
+ peerSet.add(context.from!.hashcode());
644
+ }
608
645
 
609
- if (maxReplicasFromNewEntries < maxReplicasFromHead && isLeader) {
610
- (maybeDelete || (maybeDelete = [])).push(entries);
646
+ if (maxReplicasFromNewEntries < maxReplicasFromHead) {
647
+ (maybeDelete || (maybeDelete = [])).push(entries);
648
+ }
611
649
  }
612
650
 
613
651
  outer: for (const entry of entries) {
@@ -644,8 +682,8 @@ export class SharedLog<T = Uint8Array> extends Program<
644
682
  if (toMerge.length > 0) {
645
683
  await this.log.join(toMerge);
646
684
  toDelete &&
647
- this.prune(toDelete).catch((e) => {
648
- logger.error(e.toString());
685
+ Promise.all(this.prune(toDelete)).catch((e) => {
686
+ logger.info(e.toString());
649
687
  });
650
688
  this.rebalanceParticipationDebounced?.();
651
689
  }
@@ -664,15 +702,17 @@ export class SharedLog<T = Uint8Array> extends Program<
664
702
  );
665
703
 
666
704
  if (!isLeader) {
667
- this.prune(entries.map((x) => x.entry)).catch((e) => {
668
- logger.error(e.toString());
669
- });
705
+ Promise.all(this.prune(entries.map((x) => x.entry))).catch(
706
+ (e) => {
707
+ logger.info(e.toString());
708
+ }
709
+ );
670
710
  }
671
711
  }
672
712
  }
673
713
  }
674
714
  }
675
- } else if (msg instanceof RequestIHave) {
715
+ } else if (msg instanceof RequestIPrune) {
676
716
  const hasAndIsLeader: string[] = [];
677
717
 
678
718
  for (const hash of msg.hashes) {
@@ -684,6 +724,9 @@ export class SharedLog<T = Uint8Array> extends Program<
684
724
  decodeReplicas(indexedEntry).getValue(this)
685
725
  ))
686
726
  ) {
727
+ this._gidPeersHistory
728
+ .get(indexedEntry.meta.gid)
729
+ ?.delete(context.from!.hashcode());
687
730
  hasAndIsLeader.push(hash);
688
731
  } else {
689
732
  const prevPendingIHave = this._pendingIHave.get(hash);
@@ -699,8 +742,14 @@ export class SharedLog<T = Uint8Array> extends Program<
699
742
  decodeReplicas(entry).getValue(this)
700
743
  )
701
744
  ) {
702
- this.rpc.send(new ResponseIHave({ hashes: [entry.hash] }), {
703
- to: [context.from!]
745
+ this._gidPeersHistory
746
+ .get(entry.meta.gid)
747
+ ?.delete(context.from!.hashcode());
748
+ this.rpc.send(new ResponseIPrune({ hashes: [entry.hash] }), {
749
+ mode: new SilentDelivery({
750
+ to: [context.from!],
751
+ redundancy: 1
752
+ })
704
753
  });
705
754
  }
706
755
 
@@ -719,12 +768,13 @@ export class SharedLog<T = Uint8Array> extends Program<
719
768
  this._pendingIHave.set(hash, pendingIHave);
720
769
  }
721
770
  }
722
- await this.rpc.send(new ResponseIHave({ hashes: hasAndIsLeader }), {
723
- to: [context.from!]
771
+
772
+ await this.rpc.send(new ResponseIPrune({ hashes: hasAndIsLeader }), {
773
+ mode: new SilentDelivery({ to: [context.from!], redundancy: 1 })
724
774
  });
725
- } else if (msg instanceof ResponseIHave) {
775
+ } else if (msg instanceof ResponseIPrune) {
726
776
  for (const hash of msg.hashes) {
727
- this._pendingDeletes.get(hash)?.callback(context.from!.hashcode());
777
+ this._pendingDeletes.get(hash)?.resolve(context.from!.hashcode());
728
778
  }
729
779
  } else if (msg instanceof BlocksMessage) {
730
780
  await this.remoteBlocks.onMessage(msg.message);
@@ -738,7 +788,7 @@ export class SharedLog<T = Uint8Array> extends Program<
738
788
  }
739
789
 
740
790
  await this.rpc.send(new ResponseRoleMessage({ role: this.role }), {
741
- to: [context.from!]
791
+ mode: new SilentDelivery({ to: [context.from!], redundancy: 1 })
742
792
  });
743
793
  } else if (msg instanceof ResponseRoleMessage) {
744
794
  if (!context.from) {
@@ -771,7 +821,6 @@ export class SharedLog<T = Uint8Array> extends Program<
771
821
  if (e instanceof AbortError) {
772
822
  return;
773
823
  }
774
-
775
824
  logger.error(
776
825
  "Failed to find peer who updated their role: " + e?.message
777
826
  );
@@ -843,7 +892,7 @@ export class SharedLog<T = Uint8Array> extends Program<
843
892
  slot: { toString(): string },
844
893
  numberOfLeaders: number,
845
894
  timeout = WAIT_FOR_REPLICATOR_TIMEOUT
846
- ): Promise<boolean> {
895
+ ): Promise<string[] | false> {
847
896
  return new Promise((res, rej) => {
848
897
  const removeListeners = () => {
849
898
  this.events.removeEventListener("role", roleListener);
@@ -861,11 +910,14 @@ export class SharedLog<T = Uint8Array> extends Program<
861
910
  }, timeout);
862
911
 
863
912
  const check = () =>
864
- this.isLeader(slot, numberOfLeaders).then((isLeader) => {
913
+ this.findLeaders(slot, numberOfLeaders).then((leaders) => {
914
+ const isLeader = leaders.find(
915
+ (l) => l === this.node.identity.publicKey.hashcode()
916
+ );
865
917
  if (isLeader) {
866
918
  removeListeners();
867
919
  clearTimeout(timer);
868
- res(isLeader);
920
+ res(leaders);
869
921
  }
870
922
  });
871
923
 
@@ -900,6 +952,73 @@ export class SharedLog<T = Uint8Array> extends Program<
900
952
  return this.findLeadersFromUniformNumber(cursor, numberOfLeaders, options);
901
953
  }
902
954
 
955
+ private collectNodesAroundPoint(
956
+ time: number,
957
+ roleAge: number,
958
+ peers: yallist<ReplicatorRect>,
959
+ currentNode: yallist.Node<ReplicatorRect> | null,
960
+ width: number,
961
+ collector: Set<string>,
962
+ point: () => number,
963
+ done = () => false,
964
+ onMatured: (node: ReplicatorRect) => void = () => {}
965
+ ) {
966
+ let matured = 0;
967
+
968
+ const maybeIncrementMatured = (role: Replicator) => {
969
+ if (time - Number(role.timestamp) > roleAge) {
970
+ matured++;
971
+ return true;
972
+ }
973
+
974
+ return false;
975
+ };
976
+
977
+ // Assume peers does not mutate during this loop
978
+ const startNode = currentNode;
979
+ const diffs: { diff: number; rect: ReplicatorRect }[] = [];
980
+ while (currentNode && !done()) {
981
+ const start = currentNode.value.offset % width;
982
+ const absDelta = Math.abs(start - point());
983
+ const diff = Math.min(absDelta, width - absDelta);
984
+
985
+ if (diff < currentNode.value.role.factor / 2 + 0.00001) {
986
+ collector.add(currentNode.value.publicKey.hashcode());
987
+ if (maybeIncrementMatured(currentNode.value.role)) {
988
+ onMatured(currentNode.value);
989
+ }
990
+ } else {
991
+ diffs.push({
992
+ diff:
993
+ currentNode.value.role.factor > 0
994
+ ? diff / currentNode.value.role.factor
995
+ : Number.MAX_SAFE_INTEGER,
996
+ rect: currentNode.value
997
+ });
998
+ }
999
+
1000
+ currentNode = currentNode.next || peers.head;
1001
+
1002
+ if (
1003
+ currentNode?.value.publicKey &&
1004
+ startNode?.value.publicKey.equals(currentNode?.value.publicKey)
1005
+ ) {
1006
+ break; // TODO throw error for failing to fetch ffull width
1007
+ }
1008
+ }
1009
+
1010
+ if (matured === 0) {
1011
+ diffs.sort((x, y) => x.diff - y.diff);
1012
+ for (const node of diffs) {
1013
+ collector.add(node.rect.publicKey.hashcode());
1014
+ maybeIncrementMatured(node.rect.role);
1015
+ if (matured > 0) {
1016
+ break;
1017
+ }
1018
+ }
1019
+ }
1020
+ }
1021
+
903
1022
  private findLeadersFromUniformNumber(
904
1023
  cursor: number,
905
1024
  numberOfLeaders: number,
@@ -921,47 +1040,17 @@ export class SharedLog<T = Uint8Array> extends Program<
921
1040
  Math.min(WAIT_FOR_ROLE_MATURITY, +new Date() - this.openTime);
922
1041
 
923
1042
  for (let i = 0; i < numberOfLeaders; i++) {
924
- let matured = 0;
925
- const maybeIncrementMatured = (role: Replicator) => {
926
- if (t - Number(role.timestamp) > roleAge) {
927
- matured++;
928
- }
929
- };
930
-
931
- const x = ((cursor + i / numberOfLeaders) % 1) * width;
932
- let currentNode = peers.head;
933
- const diffs: { diff: number; rect: ReplicatorRect }[] = [];
934
- while (currentNode) {
935
- const start = currentNode.value.offset % width;
936
- const absDelta = Math.abs(start - x);
937
- const diff = Math.min(absDelta, width - absDelta);
938
-
939
- if (diff < currentNode.value.role.factor / 2 + 0.00001) {
940
- leaders.add(currentNode.value.publicKey.hashcode());
941
- maybeIncrementMatured(currentNode.value.role);
942
- } else {
943
- diffs.push({
944
- diff:
945
- currentNode.value.role.factor > 0
946
- ? diff / currentNode.value.role.factor
947
- : Number.MAX_SAFE_INTEGER,
948
- rect: currentNode.value
949
- });
950
- }
951
-
952
- currentNode = currentNode.next;
953
- }
954
-
955
- if (matured === 0) {
956
- diffs.sort((x, y) => x.diff - y.diff);
957
- for (const node of diffs) {
958
- leaders.add(node.rect.publicKey.hashcode());
959
- maybeIncrementMatured(node.rect.role);
960
- if (matured > 0) {
961
- break;
962
- }
963
- }
964
- }
1043
+ const point = ((cursor + i / numberOfLeaders) % 1) * width;
1044
+ const currentNode = peers.head;
1045
+ this.collectNodesAroundPoint(
1046
+ t,
1047
+ roleAge,
1048
+ peers,
1049
+ currentNode,
1050
+ width,
1051
+ leaders,
1052
+ () => point
1053
+ );
965
1054
  }
966
1055
 
967
1056
  return [...leaders];
@@ -982,6 +1071,11 @@ export class SharedLog<T = Uint8Array> extends Program<
982
1071
  peers.length,
983
1072
  this.replicas.min.getValue(this)
984
1073
  );
1074
+
1075
+ // If min replicas = 2
1076
+ // then we need to make sure we cover 0.5 of the total 'width' of the replication space
1077
+ // to make sure we reach sufficient amount of nodes such that at least one one has
1078
+ // the entry we are looking for
985
1079
  const coveringWidth = width / minReplicas;
986
1080
 
987
1081
  let walker = peers.head;
@@ -1003,7 +1097,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1003
1097
  }
1004
1098
  }
1005
1099
 
1006
- const set: string[] = [];
1100
+ const set: Set<string> = new Set();
1007
1101
  let distance = 0;
1008
1102
  const startNode = walker;
1009
1103
  if (!startNode) {
@@ -1012,33 +1106,22 @@ export class SharedLog<T = Uint8Array> extends Program<
1012
1106
 
1013
1107
  let nextPoint = startNode.value.offset;
1014
1108
  const t = +new Date();
1015
- while (walker && distance < coveringWidth) {
1016
- const absDelta = Math.abs(walker!.value.offset - nextPoint);
1017
- const diff = Math.min(absDelta, width - absDelta);
1018
-
1019
- if (diff < walker!.value.role.factor / 2 + 0.00001) {
1020
- set.push(walker!.value.publicKey.hashcode());
1021
- if (
1022
- t - Number(walker!.value.role.timestamp) >
1023
- roleAge /* ||
1024
- walker!.value.publicKey.equals(this.node.identity.publicKey)) */
1025
- ) {
1026
- nextPoint = (nextPoint + walker!.value.role.factor) % 1;
1027
- distance += walker!.value.role.factor;
1028
- }
1029
- }
1030
-
1031
- walker = walker.next || peers.head;
1032
-
1033
- if (
1034
- walker?.value.publicKey &&
1035
- startNode?.value.publicKey.equals(walker?.value.publicKey)
1036
- ) {
1037
- break; // TODO throw error for failing to fetch ffull width
1109
+ this.collectNodesAroundPoint(
1110
+ t,
1111
+ roleAge,
1112
+ peers,
1113
+ walker,
1114
+ width,
1115
+ set,
1116
+ () => nextPoint,
1117
+ () => distance >= coveringWidth,
1118
+ (node) => {
1119
+ distance += node.role.factor;
1120
+ nextPoint = (nextPoint + walker!.value.role.factor) % width;
1038
1121
  }
1039
- }
1122
+ );
1040
1123
 
1041
- return set;
1124
+ return [...set];
1042
1125
  }
1043
1126
 
1044
1127
  async replicator(
@@ -1055,11 +1138,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1055
1138
  );
1056
1139
  }
1057
1140
 
1058
- private onRoleChange(
1059
- prev: Observer | Replicator | undefined,
1060
- role: Observer | Replicator,
1061
- publicKey: PublicSignKey
1062
- ) {
1141
+ private onRoleChange(role: Observer | Replicator, publicKey: PublicSignKey) {
1063
1142
  if (this.closed) {
1064
1143
  return;
1065
1144
  }
@@ -1091,10 +1170,24 @@ export class SharedLog<T = Uint8Array> extends Program<
1091
1170
  role: Observer | Replicator,
1092
1171
  publicKey: PublicSignKey
1093
1172
  ) {
1094
- const { prev, changed } = await this._modifyReplicators(role, publicKey);
1095
- if (changed) {
1096
- await this.rebalanceParticipationDebounced?.(); // await this.rebalanceParticipation(false);
1097
- this.onRoleChange(prev, role, publicKey);
1173
+ const update = await this._modifyReplicators(role, publicKey);
1174
+ if (update.changed !== "none") {
1175
+ if (update.changed === "added" || update.changed === "removed") {
1176
+ this.setupRebalanceDebounceFunction();
1177
+ }
1178
+
1179
+ if (this.rebalanceParticipationDebounced) {
1180
+ await this.rebalanceParticipationDebounced?.(); /* await this.rebalanceParticipation(false); */
1181
+ }
1182
+ if (update.changed === "added") {
1183
+ await this.rpc.send(new ResponseRoleMessage({ role: this._role }), {
1184
+ mode: new SeekDelivery({
1185
+ to: [publicKey.hashcode()],
1186
+ redundancy: 1
1187
+ })
1188
+ });
1189
+ }
1190
+ this.onRoleChange(role, publicKey);
1098
1191
  return true;
1099
1192
  }
1100
1193
  return false;
@@ -1103,13 +1196,16 @@ export class SharedLog<T = Uint8Array> extends Program<
1103
1196
  private async _modifyReplicators(
1104
1197
  role: Observer | Replicator,
1105
1198
  publicKey: PublicSignKey
1106
- ): Promise<{ prev?: Replicator; changed: boolean }> {
1199
+ ): Promise<
1200
+ | { changed: "added" | "none" }
1201
+ | { prev: Replicator; changed: "updated" | "removed" }
1202
+ > {
1107
1203
  if (
1108
1204
  role instanceof Replicator &&
1109
1205
  this._canReplicate &&
1110
1206
  !(await this._canReplicate(publicKey, role))
1111
1207
  ) {
1112
- return { changed: false };
1208
+ return { changed: "none" };
1113
1209
  }
1114
1210
 
1115
1211
  const sortedPeer = this._sortedPeersCache;
@@ -1117,7 +1213,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1117
1213
  if (this.closed === false) {
1118
1214
  throw new Error("Unexpected, sortedPeersCache is undefined");
1119
1215
  }
1120
- return { changed: false };
1216
+ return { changed: "none" };
1121
1217
  }
1122
1218
 
1123
1219
  if (role instanceof Replicator && role.factor > 0) {
@@ -1143,7 +1239,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1143
1239
  if (!currentNode) {
1144
1240
  sortedPeer.push(rect);
1145
1241
  this._totalParticipation += rect.role.factor;
1146
- return { changed: true };
1242
+ return { changed: "added" };
1147
1243
  } else {
1148
1244
  while (currentNode) {
1149
1245
  if (currentNode.value.publicKey.equals(publicKey)) {
@@ -1154,7 +1250,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1154
1250
  this._totalParticipation += rect.role.factor;
1155
1251
  this._totalParticipation -= prev.role.factor;
1156
1252
  // TODO change detection and only do change stuff if diff?
1157
- return { prev: prev.role, changed: true };
1253
+ return { prev: prev.role, changed: "updated" };
1158
1254
  }
1159
1255
 
1160
1256
  if (code > currentNode.value.offset) {
@@ -1178,10 +1274,10 @@ export class SharedLog<T = Uint8Array> extends Program<
1178
1274
  } else {
1179
1275
  throw new Error("Unexpected");
1180
1276
  }
1181
- return { changed: true };
1277
+ return { changed: "added" };
1182
1278
  }
1183
1279
  } else {
1184
- return { changed: false };
1280
+ return { changed: "none" };
1185
1281
  }
1186
1282
  } else {
1187
1283
  let currentNode = sortedPeer.head;
@@ -1189,11 +1285,11 @@ export class SharedLog<T = Uint8Array> extends Program<
1189
1285
  if (currentNode.value.publicKey.equals(publicKey)) {
1190
1286
  sortedPeer.removeNode(currentNode);
1191
1287
  this._totalParticipation -= currentNode.value.role.factor;
1192
- return { prev: currentNode.value.role, changed: true };
1288
+ return { prev: currentNode.value.role, changed: "removed" };
1193
1289
  }
1194
1290
  currentNode = currentNode.next;
1195
1291
  }
1196
- return { changed: false };
1292
+ return { changed: "none" };
1197
1293
  }
1198
1294
  }
1199
1295
 
@@ -1209,8 +1305,8 @@ export class SharedLog<T = Uint8Array> extends Program<
1209
1305
  continue;
1210
1306
  }
1211
1307
  this.rpc
1212
- .send(new ResponseRoleMessage(this.role), {
1213
- mode: new AcknowledgeDelivery({ redundancy: 1, to: [publicKey] })
1308
+ .send(new ResponseRoleMessage({ role: this._role }), {
1309
+ mode: new SeekDelivery({ redundancy: 1, to: [publicKey] })
1214
1310
  })
1215
1311
  .catch((e) => logger.error(e.toString()));
1216
1312
  }
@@ -1228,18 +1324,17 @@ export class SharedLog<T = Uint8Array> extends Program<
1228
1324
  }
1229
1325
  }
1230
1326
 
1231
- async prune(
1327
+ prune(
1232
1328
  entries: Entry<any>[],
1233
1329
  options?: { timeout?: number; unchecked?: boolean }
1234
- ): Promise<any> {
1330
+ ): Promise<any>[] {
1235
1331
  if (options?.unchecked) {
1236
- return Promise.all(
1237
- entries.map((x) =>
1238
- this.log.remove(x, {
1239
- recursively: true
1240
- })
1241
- )
1242
- );
1332
+ return entries.map((x) => {
1333
+ this._gidPeersHistory.delete(x.meta.gid);
1334
+ return this.log.remove(x, {
1335
+ recursively: true
1336
+ });
1337
+ });
1243
1338
  }
1244
1339
  // ask network if they have they entry,
1245
1340
  // so I can delete it
@@ -1293,7 +1388,8 @@ export class SharedLog<T = Uint8Array> extends Program<
1293
1388
  clear: () => {
1294
1389
  clear();
1295
1390
  },
1296
- callback: async (publicKeyHash: string) => {
1391
+ reject,
1392
+ resolve: async (publicKeyHash: string) => {
1297
1393
  const minReplicasValue = minReplicas.getValue(this);
1298
1394
  const minMinReplicasValue = this.replicas.max
1299
1395
  ? Math.min(minReplicasValue, this.replicas.max.getValue(this))
@@ -1314,6 +1410,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1314
1410
  if (leaders.find((x) => x === publicKeyHash)) {
1315
1411
  existCounter.add(publicKeyHash);
1316
1412
  if (minMinReplicasValue <= existCounter.size) {
1413
+ this._gidPeersHistory.delete(entry.meta.gid);
1317
1414
  this.log
1318
1415
  .remove(entry, {
1319
1416
  recursively: true
@@ -1330,32 +1427,48 @@ export class SharedLog<T = Uint8Array> extends Program<
1330
1427
  });
1331
1428
  promises.push(deferredPromise.promise);
1332
1429
  }
1430
+
1333
1431
  if (filteredEntries.length == 0) {
1334
- return;
1432
+ return [];
1335
1433
  }
1336
1434
 
1337
1435
  this.rpc.send(
1338
- new RequestIHave({ hashes: filteredEntries.map((x) => x.hash) })
1436
+ new RequestIPrune({ hashes: filteredEntries.map((x) => x.hash) })
1339
1437
  );
1340
1438
 
1341
1439
  const onNewPeer = async (e: CustomEvent<UpdateRoleEvent>) => {
1342
1440
  if (e.detail.role instanceof Replicator) {
1343
1441
  await this.rpc.send(
1344
- new RequestIHave({ hashes: filteredEntries.map((x) => x.hash) }),
1442
+ new RequestIPrune({ hashes: filteredEntries.map((x) => x.hash) }),
1345
1443
  {
1346
- to: [e.detail.publicKey.hashcode()]
1444
+ mode: new SilentDelivery({
1445
+ to: [e.detail.publicKey.hashcode()],
1446
+ redundancy: 1
1447
+ })
1347
1448
  }
1348
1449
  );
1349
1450
  }
1350
1451
  };
1452
+
1351
1453
  // check joining peers
1352
1454
  this.events.addEventListener("role", onNewPeer);
1353
- return Promise.all(promises).finally(() =>
1455
+ Promise.allSettled(promises).finally(() =>
1354
1456
  this.events.removeEventListener("role", onNewPeer)
1355
1457
  );
1458
+ return promises;
1356
1459
  }
1357
1460
 
1461
+ _queue: PQueue;
1358
1462
  async distribute() {
1463
+ if (this._queue?.size > 0) {
1464
+ return;
1465
+ }
1466
+ (this._queue || (this._queue = new PQueue({ concurrency: 1 }))).add(() =>
1467
+ this._distribute()
1468
+ );
1469
+ }
1470
+
1471
+ async _distribute() {
1359
1472
  /**
1360
1473
  * TODO use information of new joined/leaving peer to create a subset of heads
1361
1474
  * that we potentially need to share with other peers
@@ -1369,7 +1482,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1369
1482
  await this.log.trim();
1370
1483
  const heads = await this.log.getHeads();
1371
1484
  const groupedByGid = await groupByGid(heads);
1372
- const toDeliver: Map<string, Entry<any>[]> = new Map();
1485
+ const uncheckedDeliver: Map<string, Entry<any>[]> = new Map();
1373
1486
  const allEntriesToDelete: Entry<any>[] = [];
1374
1487
 
1375
1488
  for (const [gid, entries] of groupedByGid) {
@@ -1386,6 +1499,9 @@ export class SharedLog<T = Uint8Array> extends Program<
1386
1499
  gid,
1387
1500
  maxReplicas(this, entries) // pick max replication policy of all entries, so all information is treated equally important as the most important
1388
1501
  );
1502
+ const isLeader = currentPeers.find(
1503
+ (x) => x === this.node.identity.publicKey.hashcode()
1504
+ );
1389
1505
  const currentPeersSet = new Set(currentPeers);
1390
1506
  this._gidPeersHistory.set(gid, currentPeersSet);
1391
1507
 
@@ -1396,10 +1512,10 @@ export class SharedLog<T = Uint8Array> extends Program<
1396
1512
 
1397
1513
  if (!oldPeersSet?.has(currentPeer)) {
1398
1514
  // second condition means that if the new peer is us, we should not do anything, since we are expecting to receive heads, not send
1399
- let arr = toDeliver.get(currentPeer);
1515
+ let arr = uncheckedDeliver.get(currentPeer);
1400
1516
  if (!arr) {
1401
1517
  arr = [];
1402
- toDeliver.set(currentPeer, arr);
1518
+ uncheckedDeliver.set(currentPeer, arr);
1403
1519
  }
1404
1520
 
1405
1521
  for (const entry of entries) {
@@ -1408,9 +1524,7 @@ export class SharedLog<T = Uint8Array> extends Program<
1408
1524
  }
1409
1525
  }
1410
1526
 
1411
- if (
1412
- !currentPeers.find((x) => x === this.node.identity.publicKey.hashcode())
1413
- ) {
1527
+ if (!isLeader) {
1414
1528
  if (currentPeers.length > 0) {
1415
1529
  // If we are observer, never prune locally created entries, since we dont really know who can store them
1416
1530
  // if we are replicator, we will always persist entries that we need to so filtering on createdLocally will not make a difference
@@ -1418,43 +1532,36 @@ export class SharedLog<T = Uint8Array> extends Program<
1418
1532
  this._role instanceof Observer
1419
1533
  ? entries.filter((e) => !e.createdLocally)
1420
1534
  : entries;
1421
- entriesToDelete.map((x) => this._gidPeersHistory.delete(x.meta.gid));
1422
1535
  allEntriesToDelete.push(...entriesToDelete);
1423
1536
  }
1424
1537
  } else {
1425
1538
  for (const entry of entries) {
1426
1539
  this._pendingDeletes
1427
1540
  .get(entry.hash)
1428
- ?.promise.reject(
1429
- new Error(
1430
- "Failed to delete, is leader: " +
1431
- this.role.constructor.name +
1432
- ". " +
1433
- this.node.identity.publicKey.hashcode()
1434
- )
1435
- );
1541
+ ?.reject(new Error("Failed to delete, is leader again"));
1436
1542
  }
1437
1543
  }
1438
1544
  }
1439
1545
 
1440
- for (const [target, entries] of toDeliver) {
1441
- const message = await createExchangeHeadsMessage(
1442
- this.log,
1443
- entries, // TODO send to peers directly
1444
- this._gidParentCache
1445
- );
1446
- // TODO perhaps send less messages to more receivers for performance reasons?
1447
- await this.rpc.send(message, {
1448
- to: [target]
1449
- });
1546
+ for (const [target, entries] of uncheckedDeliver) {
1547
+ for (let i = 0; i < entries.length; i += 100) {
1548
+ const message = await createExchangeHeadsMessage(
1549
+ this.log,
1550
+ entries.slice(i, i + 100),
1551
+ this._gidParentCache
1552
+ );
1553
+ // TODO perhaps send less messages to more receivers for performance reasons?
1554
+ await this.rpc.send(message, {
1555
+ mode: new SilentDelivery({ to: [target], redundancy: 1 })
1556
+ });
1557
+ }
1450
1558
  }
1451
1559
 
1452
1560
  if (allEntriesToDelete.length > 0) {
1453
- this.prune(allEntriesToDelete).catch((e) => {
1561
+ Promise.allSettled(this.prune(allEntriesToDelete)).catch((e) => {
1454
1562
  logger.error(e.toString());
1455
1563
  });
1456
1564
  }
1457
-
1458
1565
  return changed;
1459
1566
  }
1460
1567