@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/lib/esm/index.js CHANGED
@@ -14,7 +14,7 @@ import { Program } from "@peerbit/program";
14
14
  import { BinaryWriter, BorshError, field, variant } from "@dao-xyz/borsh";
15
15
  import { AccessError, sha256, sha256Base64Sync } from "@peerbit/crypto";
16
16
  import { logger as loggerFn } from "@peerbit/logger";
17
- import { ExchangeHeadsMessage, RequestIHave, ResponseIHave, createExchangeHeadsMessage } from "./exchange-heads.js";
17
+ import { ExchangeHeadsMessage, RequestIPrune, ResponseIPrune, createExchangeHeadsMessage } from "./exchange-heads.js";
18
18
  import { AbortError, waitFor } from "@peerbit/time";
19
19
  import { Observer, Replicator, Role } from "./role.js";
20
20
  import { AbsoluteReplicas, ReplicationError, RequestRoleMessage, ResponseRoleMessage, decodeReplicas, encodeReplicas, hashToUniformNumber, maxReplicas } from "./replication.js";
@@ -22,12 +22,13 @@ import pDefer from "p-defer";
22
22
  import { Cache } from "@peerbit/cache";
23
23
  import { CustomEvent } from "@libp2p/interface";
24
24
  import yallist from "yallist";
25
- import { AcknowledgeDelivery, SilentDelivery } from "@peerbit/stream-interface";
25
+ import { AcknowledgeDelivery, SeekDelivery, SilentDelivery } from "@peerbit/stream-interface";
26
26
  import { AnyBlockStore, RemoteBlocks } from "@peerbit/blocks";
27
27
  import { BlocksMessage } from "./blocks.js";
28
28
  import debounce from "p-debounce";
29
29
  import { PIDReplicationController } from "./pid.js";
30
30
  export * from "./replication.js";
31
+ import PQueue from "p-queue";
31
32
  export { Observer, Replicator, Role };
32
33
  export const logger = loggerFn({ module: "shared-log" });
33
34
  const groupByGid = async (entries) => {
@@ -56,7 +57,7 @@ const isAdaptiveReplicatorOption = (options) => {
56
57
  export const DEFAULT_MIN_REPLICAS = 2;
57
58
  export const WAIT_FOR_REPLICATOR_TIMEOUT = 9000;
58
59
  export const WAIT_FOR_ROLE_MATURITY = 5000;
59
- const REBALANCE_DEBOUNCE_INTERAVAL = 50;
60
+ const REBALANCE_DEBOUNCE_INTERAVAL = 30;
60
61
  let SharedLog = class SharedLog extends Program {
61
62
  log;
62
63
  rpc;
@@ -92,6 +93,10 @@ let SharedLog = class SharedLog extends Program {
92
93
  get totalParticipation() {
93
94
  return this._totalParticipation;
94
95
  }
96
+ setupRebalanceDebounceFunction() {
97
+ this.rebalanceParticipationDebounced = debounce(() => this.rebalanceParticipation(), Math.max(REBALANCE_DEBOUNCE_INTERAVAL, (this.getReplicatorsSorted()?.length || 0) *
98
+ REBALANCE_DEBOUNCE_INTERAVAL));
99
+ }
95
100
  setupRole(options) {
96
101
  this.rebalanceParticipationDebounced = undefined;
97
102
  const setupDebouncedRebalancing = (options) => {
@@ -99,8 +104,7 @@ let SharedLog = class SharedLog extends Program {
99
104
  targetMemoryLimit: options?.limits?.memory,
100
105
  errorFunction: options?.error
101
106
  });
102
- this.rebalanceParticipationDebounced = debounce(() => this.rebalanceParticipation(), REBALANCE_DEBOUNCE_INTERAVAL // TODO make dynamic
103
- );
107
+ this.setupRebalanceDebounceFunction();
104
108
  };
105
109
  if (options instanceof Observer || options instanceof Replicator) {
106
110
  throw new Error("Unsupported role option type");
@@ -134,14 +138,20 @@ let SharedLog = class SharedLog extends Program {
134
138
  // setup the initial role
135
139
  if (this._roleOptions instanceof Replicator ||
136
140
  this._roleOptions instanceof Observer) {
137
- this._role = this._roleOptions; // Fixed
141
+ this._role = this._roleOptions;
138
142
  }
139
143
  else {
140
- this._role = new Replicator({
141
- // initial role in a dynamic setup
142
- factor: 1,
143
- timestamp: BigInt(+new Date())
144
- });
144
+ if (this._roleOptions.limits) {
145
+ this._role = new Replicator({
146
+ // initial role in a dynamic setup
147
+ factor: 1
148
+ });
149
+ }
150
+ else {
151
+ this._role = new Replicator({
152
+ factor: 1
153
+ });
154
+ }
145
155
  }
146
156
  return this._role;
147
157
  }
@@ -156,9 +166,13 @@ let SharedLog = class SharedLog extends Program {
156
166
  this._loadedOnce = true;
157
167
  }
158
168
  await this.rpc.subscribe();
159
- await this.rpc.send(new ResponseRoleMessage(role));
160
- if (onRoleChange && changed) {
161
- this.onRoleChange(undefined, this._role, this.node.identity.publicKey);
169
+ await this.rpc.send(new ResponseRoleMessage({ role: this._role }), {
170
+ mode: new SeekDelivery({
171
+ redundancy: 1
172
+ })
173
+ });
174
+ if (onRoleChange && changed !== "none") {
175
+ this.onRoleChange(this._role, this.node.identity.publicKey);
162
176
  }
163
177
  return changed;
164
178
  }
@@ -217,7 +231,9 @@ let SharedLog = class SharedLog extends Program {
217
231
  this.remoteBlocks = new RemoteBlocks({
218
232
  local: localBlocks,
219
233
  publish: (message, options) => this.rpc.send(new BlocksMessage(message), {
220
- to: options?.to
234
+ mode: options?.to
235
+ ? new SilentDelivery({ to: options.to, redundancy: 1 })
236
+ : undefined
221
237
  }),
222
238
  waitFor: this.rpc.waitFor.bind(this.rpc)
223
239
  });
@@ -370,6 +386,7 @@ let SharedLog = class SharedLog extends Program {
370
386
  let maybeDelete = undefined;
371
387
  const groupedByGid = await groupByGid(filteredHeads);
372
388
  const promises = [];
389
+ /// console.log("ADD CACHE", this.node.identity.publicKey.hashcode(), context.from!.hashcode(), groupedByGid.size)
373
390
  for (const [gid, entries] of groupedByGid) {
374
391
  const fn = async () => {
375
392
  const headsWithGid = this.log.headsIndex.gids.get(gid);
@@ -377,9 +394,20 @@ let SharedLog = class SharedLog extends Program {
377
394
  ? maxReplicas(this, [...headsWithGid.values()])
378
395
  : this.replicas.min.getValue(this);
379
396
  const maxReplicasFromNewEntries = maxReplicas(this, entries.map((x) => x.entry));
380
- const isLeader = await this.waitForIsLeader(gid, Math.max(maxReplicasFromHead, maxReplicasFromNewEntries));
381
- if (maxReplicasFromNewEntries < maxReplicasFromHead && isLeader) {
382
- (maybeDelete || (maybeDelete = [])).push(entries);
397
+ const leaders = await this.waitForIsLeader(gid, Math.max(maxReplicasFromHead, maxReplicasFromNewEntries));
398
+ const isLeader = !!leaders;
399
+ if (isLeader) {
400
+ if (leaders.find((x) => x === context.from.hashcode())) {
401
+ let peerSet = this._gidPeersHistory.get(gid);
402
+ if (!peerSet) {
403
+ peerSet = new Set();
404
+ this._gidPeersHistory.set(gid, peerSet);
405
+ }
406
+ peerSet.add(context.from.hashcode());
407
+ }
408
+ if (maxReplicasFromNewEntries < maxReplicasFromHead) {
409
+ (maybeDelete || (maybeDelete = [])).push(entries);
410
+ }
383
411
  }
384
412
  outer: for (const entry of entries) {
385
413
  if (isLeader) {
@@ -407,8 +435,8 @@ let SharedLog = class SharedLog extends Program {
407
435
  if (toMerge.length > 0) {
408
436
  await this.log.join(toMerge);
409
437
  toDelete &&
410
- this.prune(toDelete).catch((e) => {
411
- logger.error(e.toString());
438
+ Promise.all(this.prune(toDelete)).catch((e) => {
439
+ logger.info(e.toString());
412
440
  });
413
441
  this.rebalanceParticipationDebounced?.();
414
442
  }
@@ -419,8 +447,8 @@ let SharedLog = class SharedLog extends Program {
419
447
  const minReplicas = maxReplicas(this, headsWithGid.values());
420
448
  const isLeader = await this.isLeader(entries[0].entry.meta.gid, minReplicas);
421
449
  if (!isLeader) {
422
- this.prune(entries.map((x) => x.entry)).catch((e) => {
423
- logger.error(e.toString());
450
+ Promise.all(this.prune(entries.map((x) => x.entry))).catch((e) => {
451
+ logger.info(e.toString());
424
452
  });
425
453
  }
426
454
  }
@@ -428,12 +456,15 @@ let SharedLog = class SharedLog extends Program {
428
456
  }
429
457
  }
430
458
  }
431
- else if (msg instanceof RequestIHave) {
459
+ else if (msg instanceof RequestIPrune) {
432
460
  const hasAndIsLeader = [];
433
461
  for (const hash of msg.hashes) {
434
462
  const indexedEntry = this.log.entryIndex.getShallow(hash);
435
463
  if (indexedEntry &&
436
464
  (await this.isLeader(indexedEntry.meta.gid, decodeReplicas(indexedEntry).getValue(this)))) {
465
+ this._gidPeersHistory
466
+ .get(indexedEntry.meta.gid)
467
+ ?.delete(context.from.hashcode());
437
468
  hasAndIsLeader.push(hash);
438
469
  }
439
470
  else {
@@ -445,8 +476,14 @@ let SharedLog = class SharedLog extends Program {
445
476
  },
446
477
  callback: async (entry) => {
447
478
  if (await this.isLeader(entry.meta.gid, decodeReplicas(entry).getValue(this))) {
448
- this.rpc.send(new ResponseIHave({ hashes: [entry.hash] }), {
449
- to: [context.from]
479
+ this._gidPeersHistory
480
+ .get(entry.meta.gid)
481
+ ?.delete(context.from.hashcode());
482
+ this.rpc.send(new ResponseIPrune({ hashes: [entry.hash] }), {
483
+ mode: new SilentDelivery({
484
+ to: [context.from],
485
+ redundancy: 1
486
+ })
450
487
  });
451
488
  }
452
489
  prevPendingIHave && prevPendingIHave.callback(entry);
@@ -462,13 +499,13 @@ let SharedLog = class SharedLog extends Program {
462
499
  this._pendingIHave.set(hash, pendingIHave);
463
500
  }
464
501
  }
465
- await this.rpc.send(new ResponseIHave({ hashes: hasAndIsLeader }), {
466
- to: [context.from]
502
+ await this.rpc.send(new ResponseIPrune({ hashes: hasAndIsLeader }), {
503
+ mode: new SilentDelivery({ to: [context.from], redundancy: 1 })
467
504
  });
468
505
  }
469
- else if (msg instanceof ResponseIHave) {
506
+ else if (msg instanceof ResponseIPrune) {
470
507
  for (const hash of msg.hashes) {
471
- this._pendingDeletes.get(hash)?.callback(context.from.hashcode());
508
+ this._pendingDeletes.get(hash)?.resolve(context.from.hashcode());
472
509
  }
473
510
  }
474
511
  else if (msg instanceof BlocksMessage) {
@@ -482,7 +519,7 @@ let SharedLog = class SharedLog extends Program {
482
519
  return;
483
520
  }
484
521
  await this.rpc.send(new ResponseRoleMessage({ role: this.role }), {
485
- to: [context.from]
522
+ mode: new SilentDelivery({ to: [context.from], redundancy: 1 })
486
523
  });
487
524
  }
488
525
  else if (msg instanceof ResponseRoleMessage) {
@@ -566,11 +603,12 @@ let SharedLog = class SharedLog extends Program {
566
603
  removeListeners();
567
604
  res(false);
568
605
  }, timeout);
569
- const check = () => this.isLeader(slot, numberOfLeaders).then((isLeader) => {
606
+ const check = () => this.findLeaders(slot, numberOfLeaders).then((leaders) => {
607
+ const isLeader = leaders.find((l) => l === this.node.identity.publicKey.hashcode());
570
608
  if (isLeader) {
571
609
  removeListeners();
572
610
  clearTimeout(timer);
573
- res(isLeader);
611
+ res(leaders);
574
612
  }
575
613
  });
576
614
  const roleListener = () => {
@@ -593,6 +631,53 @@ let SharedLog = class SharedLog extends Program {
593
631
  const cursor = hashToUniformNumber(seed); // bounded between 0 and 1
594
632
  return this.findLeadersFromUniformNumber(cursor, numberOfLeaders, options);
595
633
  }
634
+ collectNodesAroundPoint(time, roleAge, peers, currentNode, width, collector, point, done = () => false, onMatured = () => { }) {
635
+ let matured = 0;
636
+ const maybeIncrementMatured = (role) => {
637
+ if (time - Number(role.timestamp) > roleAge) {
638
+ matured++;
639
+ return true;
640
+ }
641
+ return false;
642
+ };
643
+ // Assume peers does not mutate during this loop
644
+ const startNode = currentNode;
645
+ const diffs = [];
646
+ while (currentNode && !done()) {
647
+ const start = currentNode.value.offset % width;
648
+ const absDelta = Math.abs(start - point());
649
+ const diff = Math.min(absDelta, width - absDelta);
650
+ if (diff < currentNode.value.role.factor / 2 + 0.00001) {
651
+ collector.add(currentNode.value.publicKey.hashcode());
652
+ if (maybeIncrementMatured(currentNode.value.role)) {
653
+ onMatured(currentNode.value);
654
+ }
655
+ }
656
+ else {
657
+ diffs.push({
658
+ diff: currentNode.value.role.factor > 0
659
+ ? diff / currentNode.value.role.factor
660
+ : Number.MAX_SAFE_INTEGER,
661
+ rect: currentNode.value
662
+ });
663
+ }
664
+ currentNode = currentNode.next || peers.head;
665
+ if (currentNode?.value.publicKey &&
666
+ startNode?.value.publicKey.equals(currentNode?.value.publicKey)) {
667
+ break; // TODO throw error for failing to fetch ffull width
668
+ }
669
+ }
670
+ if (matured === 0) {
671
+ diffs.sort((x, y) => x.diff - y.diff);
672
+ for (const node of diffs) {
673
+ collector.add(node.rect.publicKey.hashcode());
674
+ maybeIncrementMatured(node.rect.role);
675
+ if (matured > 0) {
676
+ break;
677
+ }
678
+ }
679
+ }
680
+ }
596
681
  findLeadersFromUniformNumber(cursor, numberOfLeaders, options) {
597
682
  const leaders = new Set();
598
683
  const width = 1; // this.getParticipationSum(roleAge);
@@ -605,43 +690,9 @@ let SharedLog = class SharedLog extends Program {
605
690
  const roleAge = options?.roleAge ??
606
691
  Math.min(WAIT_FOR_ROLE_MATURITY, +new Date() - this.openTime);
607
692
  for (let i = 0; i < numberOfLeaders; i++) {
608
- let matured = 0;
609
- const maybeIncrementMatured = (role) => {
610
- if (t - Number(role.timestamp) > roleAge) {
611
- matured++;
612
- }
613
- };
614
- const x = ((cursor + i / numberOfLeaders) % 1) * width;
615
- let currentNode = peers.head;
616
- const diffs = [];
617
- while (currentNode) {
618
- const start = currentNode.value.offset % width;
619
- const absDelta = Math.abs(start - x);
620
- const diff = Math.min(absDelta, width - absDelta);
621
- if (diff < currentNode.value.role.factor / 2 + 0.00001) {
622
- leaders.add(currentNode.value.publicKey.hashcode());
623
- maybeIncrementMatured(currentNode.value.role);
624
- }
625
- else {
626
- diffs.push({
627
- diff: currentNode.value.role.factor > 0
628
- ? diff / currentNode.value.role.factor
629
- : Number.MAX_SAFE_INTEGER,
630
- rect: currentNode.value
631
- });
632
- }
633
- currentNode = currentNode.next;
634
- }
635
- if (matured === 0) {
636
- diffs.sort((x, y) => x.diff - y.diff);
637
- for (const node of diffs) {
638
- leaders.add(node.rect.publicKey.hashcode());
639
- maybeIncrementMatured(node.rect.role);
640
- if (matured > 0) {
641
- break;
642
- }
643
- }
644
- }
693
+ const point = ((cursor + i / numberOfLeaders) % 1) * width;
694
+ const currentNode = peers.head;
695
+ this.collectNodesAroundPoint(t, roleAge, peers, currentNode, width, leaders, () => point);
645
696
  }
646
697
  return [...leaders];
647
698
  }
@@ -655,6 +706,10 @@ let SharedLog = class SharedLog extends Program {
655
706
  // How much width you need to "query" to
656
707
  const peers = this.getReplicatorsSorted(); // TODO types
657
708
  const minReplicas = Math.min(peers.length, this.replicas.min.getValue(this));
709
+ // If min replicas = 2
710
+ // then we need to make sure we cover 0.5 of the total 'width' of the replication space
711
+ // to make sure we reach sufficient amount of nodes such that at least one one has
712
+ // the entry we are looking for
658
713
  const coveringWidth = width / minReplicas;
659
714
  let walker = peers.head;
660
715
  if (this.role instanceof Replicator) {
@@ -675,7 +730,7 @@ let SharedLog = class SharedLog extends Program {
675
730
  walker = walker.next;
676
731
  }
677
732
  }
678
- const set = [];
733
+ const set = new Set();
679
734
  let distance = 0;
680
735
  const startNode = walker;
681
736
  if (!startNode) {
@@ -683,30 +738,16 @@ let SharedLog = class SharedLog extends Program {
683
738
  }
684
739
  let nextPoint = startNode.value.offset;
685
740
  const t = +new Date();
686
- while (walker && distance < coveringWidth) {
687
- const absDelta = Math.abs(walker.value.offset - nextPoint);
688
- const diff = Math.min(absDelta, width - absDelta);
689
- if (diff < walker.value.role.factor / 2 + 0.00001) {
690
- set.push(walker.value.publicKey.hashcode());
691
- if (t - Number(walker.value.role.timestamp) >
692
- roleAge /* ||
693
- walker!.value.publicKey.equals(this.node.identity.publicKey)) */) {
694
- nextPoint = (nextPoint + walker.value.role.factor) % 1;
695
- distance += walker.value.role.factor;
696
- }
697
- }
698
- walker = walker.next || peers.head;
699
- if (walker?.value.publicKey &&
700
- startNode?.value.publicKey.equals(walker?.value.publicKey)) {
701
- break; // TODO throw error for failing to fetch ffull width
702
- }
703
- }
704
- return set;
741
+ this.collectNodesAroundPoint(t, roleAge, peers, walker, width, set, () => nextPoint, () => distance >= coveringWidth, (node) => {
742
+ distance += node.role.factor;
743
+ nextPoint = (nextPoint + walker.value.role.factor) % width;
744
+ });
745
+ return [...set];
705
746
  }
706
747
  async replicator(entry, options) {
707
748
  return this.isLeader(entry.gid, decodeReplicas(entry).getValue(this), options);
708
749
  }
709
- onRoleChange(prev, role, publicKey) {
750
+ onRoleChange(role, publicKey) {
710
751
  if (this.closed) {
711
752
  return;
712
753
  }
@@ -727,10 +768,23 @@ let SharedLog = class SharedLog extends Program {
727
768
  }));
728
769
  }
729
770
  async modifyReplicators(role, publicKey) {
730
- const { prev, changed } = await this._modifyReplicators(role, publicKey);
731
- if (changed) {
732
- await this.rebalanceParticipationDebounced?.(); // await this.rebalanceParticipation(false);
733
- this.onRoleChange(prev, role, publicKey);
771
+ const update = await this._modifyReplicators(role, publicKey);
772
+ if (update.changed !== "none") {
773
+ if (update.changed === "added" || update.changed === "removed") {
774
+ this.setupRebalanceDebounceFunction();
775
+ }
776
+ if (this.rebalanceParticipationDebounced) {
777
+ await this.rebalanceParticipationDebounced?.(); /* await this.rebalanceParticipation(false); */
778
+ }
779
+ if (update.changed === "added") {
780
+ await this.rpc.send(new ResponseRoleMessage({ role: this._role }), {
781
+ mode: new SeekDelivery({
782
+ to: [publicKey.hashcode()],
783
+ redundancy: 1
784
+ })
785
+ });
786
+ }
787
+ this.onRoleChange(role, publicKey);
734
788
  return true;
735
789
  }
736
790
  return false;
@@ -739,14 +793,14 @@ let SharedLog = class SharedLog extends Program {
739
793
  if (role instanceof Replicator &&
740
794
  this._canReplicate &&
741
795
  !(await this._canReplicate(publicKey, role))) {
742
- return { changed: false };
796
+ return { changed: "none" };
743
797
  }
744
798
  const sortedPeer = this._sortedPeersCache;
745
799
  if (!sortedPeer) {
746
800
  if (this.closed === false) {
747
801
  throw new Error("Unexpected, sortedPeersCache is undefined");
748
802
  }
749
- return { changed: false };
803
+ return { changed: "none" };
750
804
  }
751
805
  if (role instanceof Replicator && role.factor > 0) {
752
806
  // TODO use Set + list for fast lookup
@@ -767,7 +821,7 @@ let SharedLog = class SharedLog extends Program {
767
821
  if (!currentNode) {
768
822
  sortedPeer.push(rect);
769
823
  this._totalParticipation += rect.role.factor;
770
- return { changed: true };
824
+ return { changed: "added" };
771
825
  }
772
826
  else {
773
827
  while (currentNode) {
@@ -779,7 +833,7 @@ let SharedLog = class SharedLog extends Program {
779
833
  this._totalParticipation += rect.role.factor;
780
834
  this._totalParticipation -= prev.role.factor;
781
835
  // TODO change detection and only do change stuff if diff?
782
- return { prev: prev.role, changed: true };
836
+ return { prev: prev.role, changed: "updated" };
783
837
  }
784
838
  if (code > currentNode.value.offset) {
785
839
  const next = currentNode?.next;
@@ -804,11 +858,11 @@ let SharedLog = class SharedLog extends Program {
804
858
  else {
805
859
  throw new Error("Unexpected");
806
860
  }
807
- return { changed: true };
861
+ return { changed: "added" };
808
862
  }
809
863
  }
810
864
  else {
811
- return { changed: false };
865
+ return { changed: "none" };
812
866
  }
813
867
  }
814
868
  else {
@@ -817,11 +871,11 @@ let SharedLog = class SharedLog extends Program {
817
871
  if (currentNode.value.publicKey.equals(publicKey)) {
818
872
  sortedPeer.removeNode(currentNode);
819
873
  this._totalParticipation -= currentNode.value.role.factor;
820
- return { prev: currentNode.value.role, changed: true };
874
+ return { prev: currentNode.value.role, changed: "removed" };
821
875
  }
822
876
  currentNode = currentNode.next;
823
877
  }
824
- return { changed: false };
878
+ return { changed: "none" };
825
879
  }
826
880
  }
827
881
  async handleSubscriptionChange(publicKey, changes, subscribed) {
@@ -832,8 +886,8 @@ let SharedLog = class SharedLog extends Program {
832
886
  continue;
833
887
  }
834
888
  this.rpc
835
- .send(new ResponseRoleMessage(this.role), {
836
- mode: new AcknowledgeDelivery({ redundancy: 1, to: [publicKey] })
889
+ .send(new ResponseRoleMessage({ role: this._role }), {
890
+ mode: new SeekDelivery({ redundancy: 1, to: [publicKey] })
837
891
  })
838
892
  .catch((e) => logger.error(e.toString()));
839
893
  }
@@ -849,11 +903,14 @@ let SharedLog = class SharedLog extends Program {
849
903
  }
850
904
  }
851
905
  }
852
- async prune(entries, options) {
906
+ prune(entries, options) {
853
907
  if (options?.unchecked) {
854
- return Promise.all(entries.map((x) => this.log.remove(x, {
855
- recursively: true
856
- })));
908
+ return entries.map((x) => {
909
+ this._gidPeersHistory.delete(x.meta.gid);
910
+ return this.log.remove(x, {
911
+ recursively: true
912
+ });
913
+ });
857
914
  }
858
915
  // ask network if they have they entry,
859
916
  // so I can delete it
@@ -897,7 +954,8 @@ let SharedLog = class SharedLog extends Program {
897
954
  clear: () => {
898
955
  clear();
899
956
  },
900
- callback: async (publicKeyHash) => {
957
+ reject,
958
+ resolve: async (publicKeyHash) => {
901
959
  const minReplicasValue = minReplicas.getValue(this);
902
960
  const minMinReplicasValue = this.replicas.max
903
961
  ? Math.min(minReplicasValue, this.replicas.max.getValue(this))
@@ -910,6 +968,7 @@ let SharedLog = class SharedLog extends Program {
910
968
  if (leaders.find((x) => x === publicKeyHash)) {
911
969
  existCounter.add(publicKeyHash);
912
970
  if (minMinReplicasValue <= existCounter.size) {
971
+ this._gidPeersHistory.delete(entry.meta.gid);
913
972
  this.log
914
973
  .remove(entry, {
915
974
  recursively: true
@@ -927,21 +986,32 @@ let SharedLog = class SharedLog extends Program {
927
986
  promises.push(deferredPromise.promise);
928
987
  }
929
988
  if (filteredEntries.length == 0) {
930
- return;
989
+ return [];
931
990
  }
932
- this.rpc.send(new RequestIHave({ hashes: filteredEntries.map((x) => x.hash) }));
991
+ this.rpc.send(new RequestIPrune({ hashes: filteredEntries.map((x) => x.hash) }));
933
992
  const onNewPeer = async (e) => {
934
993
  if (e.detail.role instanceof Replicator) {
935
- await this.rpc.send(new RequestIHave({ hashes: filteredEntries.map((x) => x.hash) }), {
936
- to: [e.detail.publicKey.hashcode()]
994
+ await this.rpc.send(new RequestIPrune({ hashes: filteredEntries.map((x) => x.hash) }), {
995
+ mode: new SilentDelivery({
996
+ to: [e.detail.publicKey.hashcode()],
997
+ redundancy: 1
998
+ })
937
999
  });
938
1000
  }
939
1001
  };
940
1002
  // check joining peers
941
1003
  this.events.addEventListener("role", onNewPeer);
942
- return Promise.all(promises).finally(() => this.events.removeEventListener("role", onNewPeer));
1004
+ Promise.allSettled(promises).finally(() => this.events.removeEventListener("role", onNewPeer));
1005
+ return promises;
943
1006
  }
1007
+ _queue;
944
1008
  async distribute() {
1009
+ if (this._queue?.size > 0) {
1010
+ return;
1011
+ }
1012
+ (this._queue || (this._queue = new PQueue({ concurrency: 1 }))).add(() => this._distribute());
1013
+ }
1014
+ async _distribute() {
945
1015
  /**
946
1016
  * TODO use information of new joined/leaving peer to create a subset of heads
947
1017
  * that we potentially need to share with other peers
@@ -953,7 +1023,7 @@ let SharedLog = class SharedLog extends Program {
953
1023
  await this.log.trim();
954
1024
  const heads = await this.log.getHeads();
955
1025
  const groupedByGid = await groupByGid(heads);
956
- const toDeliver = new Map();
1026
+ const uncheckedDeliver = new Map();
957
1027
  const allEntriesToDelete = [];
958
1028
  for (const [gid, entries] of groupedByGid) {
959
1029
  if (this.closed) {
@@ -965,6 +1035,7 @@ let SharedLog = class SharedLog extends Program {
965
1035
  const oldPeersSet = this._gidPeersHistory.get(gid);
966
1036
  const currentPeers = await this.findLeaders(gid, maxReplicas(this, entries) // pick max replication policy of all entries, so all information is treated equally important as the most important
967
1037
  );
1038
+ const isLeader = currentPeers.find((x) => x === this.node.identity.publicKey.hashcode());
968
1039
  const currentPeersSet = new Set(currentPeers);
969
1040
  this._gidPeersHistory.set(gid, currentPeersSet);
970
1041
  for (const currentPeer of currentPeers) {
@@ -973,24 +1044,23 @@ let SharedLog = class SharedLog extends Program {
973
1044
  }
974
1045
  if (!oldPeersSet?.has(currentPeer)) {
975
1046
  // second condition means that if the new peer is us, we should not do anything, since we are expecting to receive heads, not send
976
- let arr = toDeliver.get(currentPeer);
1047
+ let arr = uncheckedDeliver.get(currentPeer);
977
1048
  if (!arr) {
978
1049
  arr = [];
979
- toDeliver.set(currentPeer, arr);
1050
+ uncheckedDeliver.set(currentPeer, arr);
980
1051
  }
981
1052
  for (const entry of entries) {
982
1053
  arr.push(entry);
983
1054
  }
984
1055
  }
985
1056
  }
986
- if (!currentPeers.find((x) => x === this.node.identity.publicKey.hashcode())) {
1057
+ if (!isLeader) {
987
1058
  if (currentPeers.length > 0) {
988
1059
  // If we are observer, never prune locally created entries, since we dont really know who can store them
989
1060
  // if we are replicator, we will always persist entries that we need to so filtering on createdLocally will not make a difference
990
1061
  const entriesToDelete = this._role instanceof Observer
991
1062
  ? entries.filter((e) => !e.createdLocally)
992
1063
  : entries;
993
- entriesToDelete.map((x) => this._gidPeersHistory.delete(x.meta.gid));
994
1064
  allEntriesToDelete.push(...entriesToDelete);
995
1065
  }
996
1066
  }
@@ -998,23 +1068,21 @@ let SharedLog = class SharedLog extends Program {
998
1068
  for (const entry of entries) {
999
1069
  this._pendingDeletes
1000
1070
  .get(entry.hash)
1001
- ?.promise.reject(new Error("Failed to delete, is leader: " +
1002
- this.role.constructor.name +
1003
- ". " +
1004
- this.node.identity.publicKey.hashcode()));
1071
+ ?.reject(new Error("Failed to delete, is leader again"));
1005
1072
  }
1006
1073
  }
1007
1074
  }
1008
- for (const [target, entries] of toDeliver) {
1009
- const message = await createExchangeHeadsMessage(this.log, entries, // TODO send to peers directly
1010
- this._gidParentCache);
1011
- // TODO perhaps send less messages to more receivers for performance reasons?
1012
- await this.rpc.send(message, {
1013
- to: [target]
1014
- });
1075
+ for (const [target, entries] of uncheckedDeliver) {
1076
+ for (let i = 0; i < entries.length; i += 100) {
1077
+ const message = await createExchangeHeadsMessage(this.log, entries.slice(i, i + 100), this._gidParentCache);
1078
+ // TODO perhaps send less messages to more receivers for performance reasons?
1079
+ await this.rpc.send(message, {
1080
+ mode: new SilentDelivery({ to: [target], redundancy: 1 })
1081
+ });
1082
+ }
1015
1083
  }
1016
1084
  if (allEntriesToDelete.length > 0) {
1017
- this.prune(allEntriesToDelete).catch((e) => {
1085
+ Promise.allSettled(this.prune(allEntriesToDelete)).catch((e) => {
1018
1086
  logger.error(e.toString());
1019
1087
  });
1020
1088
  }