@peerbit/shared-log 11.0.8 → 11.1.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.
package/src/index.ts CHANGED
@@ -201,13 +201,21 @@ export type FixedReplicationOptions = {
201
201
  offset?: number | bigint;
202
202
  };
203
203
 
204
- export type ReplicationOptions<R extends "u32" | "u64" = any> =
204
+ type NewReplicationOptions<R extends "u32" | "u64" = any> =
205
205
  | DynamicReplicationOptions<R>
206
206
  | FixedReplicationOptions
207
207
  | FixedReplicationOptions[]
208
208
  | number
209
- | boolean
210
- | "resume";
209
+ | boolean;
210
+
211
+ type ExistingReplicationOptions<R extends "u32" | "u64" = any> = {
212
+ type: "resume";
213
+ default: NewReplicationOptions<R>;
214
+ };
215
+ export type ReplicationOptions<R extends "u32" | "u64" = any> =
216
+ | NewReplicationOptions<R>
217
+ | ExistingReplicationOptions<R>;
218
+
211
219
  export { BlocksMessage };
212
220
 
213
221
  const isAdaptiveReplicatorOption = (
@@ -234,15 +242,24 @@ const isUnreplicationOptions = (options?: ReplicationOptions<any>): boolean =>
234
242
  ((options as FixedReplicationOptions)?.offset === undefined &&
235
243
  (options as FixedReplicationOptions)?.factor === 0);
236
244
 
237
- const isReplicationOptionsDependentOnPreviousState = (
238
- options?: ReplicationOptions<any>,
239
- ): boolean => {
245
+ const isReplicationOptionsDependentOnPreviousState = async (
246
+ options: ReplicationOptions<any> | undefined,
247
+ index: Index<ReplicationRangeIndexable<any>>,
248
+ me: PublicSignKey,
249
+ ): Promise<boolean> => {
240
250
  if (options === true) {
241
251
  return true;
242
252
  }
243
253
 
244
- if (options === "resume") {
245
- return true;
254
+ if ((options as ExistingReplicationOptions<any>)?.type === "resume") {
255
+ // check if there is actually previous replication info
256
+ let countSegments = await index.count({
257
+ query: new StringMatch({
258
+ key: "hash",
259
+ value: me.hashcode(),
260
+ }),
261
+ });
262
+ return countSegments > 0;
246
263
  }
247
264
 
248
265
  if (options == null) {
@@ -338,7 +355,7 @@ export type SharedLogOptions<
338
355
  distributionDebounceTime?: number;
339
356
  compatibility?: number;
340
357
  domain?: ReplicationDomainConstructor<D>;
341
- earlyBlocks?: boolean | { cacheSize?: number };
358
+ eagerBlocks?: boolean | { cacheSize?: number };
342
359
  };
343
360
 
344
361
  export const DEFAULT_MIN_REPLICAS = 2;
@@ -618,194 +635,195 @@ export class SharedLog<
618
635
  let offsetWasProvided = false;
619
636
  if (isUnreplicationOptions(options)) {
620
637
  await this.unreplicate();
621
- } else if (options === "resume") {
622
- // don't do anything
623
- } else {
624
- let rangesToReplicate: ReplicationRangeIndexable<R>[] = [];
625
- let rangesToUnreplicate: ReplicationRangeIndexable<R>[] = [];
638
+ return [];
639
+ }
640
+ if ((options as ExistingReplicationOptions).type === "resume") {
641
+ options = (options as ExistingReplicationOptions)
642
+ .default as ReplicationOptions<R>;
643
+ }
626
644
 
627
- if (options == null) {
628
- options = {};
629
- } else if (options === true) {
630
- options = {};
631
- }
645
+ let rangesToReplicate: ReplicationRangeIndexable<R>[] = [];
646
+ let rangesToUnreplicate: ReplicationRangeIndexable<R>[] = [];
632
647
 
633
- this._isReplicating = true;
648
+ if (options == null) {
649
+ options = {};
650
+ } else if (options === true) {
651
+ options = {};
652
+ }
634
653
 
635
- if (isAdaptiveReplicatorOption(options!)) {
636
- this._isAdaptiveReplicating = true;
637
- this.setupDebouncedRebalancing(options);
654
+ this._isReplicating = true;
638
655
 
639
- // initial role in a dynamic setup
640
- const maybeRange = await this.getDynamicRange();
641
- if (!maybeRange) {
642
- // not allowed
643
- return [];
644
- }
645
- rangesToReplicate = [maybeRange];
656
+ if (isAdaptiveReplicatorOption(options!)) {
657
+ this._isAdaptiveReplicating = true;
658
+ this.setupDebouncedRebalancing(options);
646
659
 
647
- offsetWasProvided = true;
648
- } else if (isReplicationRangeMessage(options)) {
649
- rangesToReplicate = [
650
- options.toReplicationRangeIndexable(this.node.identity.publicKey),
651
- ];
660
+ // initial role in a dynamic setup
661
+ const maybeRange = await this.getDynamicRange();
662
+ if (!maybeRange) {
663
+ // not allowed
664
+ return [];
665
+ }
666
+ rangesToReplicate = [maybeRange];
667
+
668
+ offsetWasProvided = true;
669
+ } else if (isReplicationRangeMessage(options)) {
670
+ rangesToReplicate = [
671
+ options.toReplicationRangeIndexable(this.node.identity.publicKey),
672
+ ];
652
673
 
653
- offsetWasProvided = true;
674
+ offsetWasProvided = true;
675
+ } else {
676
+ let rangeArgs: FixedReplicationOptions[];
677
+ if (typeof options === "number") {
678
+ rangeArgs = [
679
+ {
680
+ factor: options,
681
+ } as FixedReplicationOptions,
682
+ ];
654
683
  } else {
655
- let rangeArgs: FixedReplicationOptions[];
656
- if (typeof options === "number") {
657
- rangeArgs = [
658
- {
659
- factor: options,
660
- } as FixedReplicationOptions,
661
- ];
662
- } else {
663
- rangeArgs = (
664
- Array.isArray(options) ? options : [{ ...options }]
665
- ) as FixedReplicationOptions[];
666
- }
684
+ rangeArgs = (
685
+ Array.isArray(options) ? options : [{ ...options }]
686
+ ) as FixedReplicationOptions[];
687
+ }
667
688
 
668
- if (rangeArgs.length === 0) {
669
- // nothing to do
670
- return [];
671
- }
689
+ if (rangeArgs.length === 0) {
690
+ // nothing to do
691
+ return [];
692
+ }
672
693
 
673
- for (const rangeArg of rangeArgs) {
674
- let timestamp: bigint | undefined = undefined;
675
- if (rangeArg.id != null) {
676
- // fetch the previous timestamp if it exists
677
- const indexed = await this.replicationIndex.get(toId(rangeArg.id), {
678
- shape: { id: true, timestamp: true },
679
- });
680
- if (indexed) {
681
- timestamp = indexed.value.timestamp;
682
- }
694
+ for (const rangeArg of rangeArgs) {
695
+ let timestamp: bigint | undefined = undefined;
696
+ if (rangeArg.id != null) {
697
+ // fetch the previous timestamp if it exists
698
+ const indexed = await this.replicationIndex.get(toId(rangeArg.id), {
699
+ shape: { id: true, timestamp: true },
700
+ });
701
+ if (indexed) {
702
+ timestamp = indexed.value.timestamp;
683
703
  }
684
- const normalized = rangeArg.normalized ?? true;
685
- offsetWasProvided = rangeArg.offset != null;
686
- const offset =
687
- rangeArg.offset != null
688
- ? normalized
689
- ? this.indexableDomain.numbers.denormalize(
690
- rangeArg.offset as number,
691
- )
692
- : rangeArg.offset
693
- : this.indexableDomain.numbers.random();
694
- let factor = rangeArg.factor;
695
- let fullWidth = this.indexableDomain.numbers.maxValue;
696
-
697
- let factorDenormalized = !normalized
698
- ? factor
699
- : this.indexableDomain.numbers.denormalize(factor as number);
700
- rangesToReplicate.push(
701
- new this.indexableDomain.constructorRange({
702
- id: rangeArg.id,
703
- // @ts-ignore
704
- offset: offset,
705
- // @ts-ignore
706
- width: (factor === "all"
707
- ? fullWidth
708
- : factor === "right"
709
- ? // @ts-ignore
710
- fullWidth - offset
711
- : factorDenormalized) as NumberFromType<R>,
712
- publicKeyHash: this.node.identity.publicKey.hashcode(),
713
- mode: rangeArg.strict
714
- ? ReplicationIntent.Strict
715
- : ReplicationIntent.NonStrict, // automatic means that this range might be reused later for dynamic replication behaviour
716
- timestamp: timestamp ?? BigInt(+new Date()),
717
- }),
718
- );
719
704
  }
705
+ const normalized = rangeArg.normalized ?? true;
706
+ offsetWasProvided = rangeArg.offset != null;
707
+ const offset =
708
+ rangeArg.offset != null
709
+ ? normalized
710
+ ? this.indexableDomain.numbers.denormalize(
711
+ rangeArg.offset as number,
712
+ )
713
+ : rangeArg.offset
714
+ : this.indexableDomain.numbers.random();
715
+ let factor = rangeArg.factor;
716
+ let fullWidth = this.indexableDomain.numbers.maxValue;
717
+
718
+ let factorDenormalized = !normalized
719
+ ? factor
720
+ : this.indexableDomain.numbers.denormalize(factor as number);
721
+ rangesToReplicate.push(
722
+ new this.indexableDomain.constructorRange({
723
+ id: rangeArg.id,
724
+ // @ts-ignore
725
+ offset: offset,
726
+ // @ts-ignore
727
+ width: (factor === "all"
728
+ ? fullWidth
729
+ : factor === "right"
730
+ ? // @ts-ignore
731
+ fullWidth - offset
732
+ : factorDenormalized) as NumberFromType<R>,
733
+ publicKeyHash: this.node.identity.publicKey.hashcode(),
734
+ mode: rangeArg.strict
735
+ ? ReplicationIntent.Strict
736
+ : ReplicationIntent.NonStrict, // automatic means that this range might be reused later for dynamic replication behaviour
737
+ timestamp: timestamp ?? BigInt(+new Date()),
738
+ }),
739
+ );
740
+ }
720
741
 
721
- if (mergeSegments) {
722
- let range =
723
- rangesToReplicate.length > 1
724
- ? mergeRanges(rangesToReplicate, this.indexableDomain.numbers)
725
- : rangesToReplicate[0];
726
-
727
- // also merge segments that are already in the index
728
- if (this.domain.canMerge) {
729
- const mergeRangesThatAlreadyExist = await getAllMergeCandiates(
730
- this.replicationIndex,
731
- range,
732
- this.indexableDomain.numbers,
733
- );
734
- const mergeableFiltered: ReplicationRangeIndexable<R>[] = [];
735
- const toKeep: Set<string> = new Set();
742
+ if (mergeSegments) {
743
+ let range =
744
+ rangesToReplicate.length > 1
745
+ ? mergeRanges(rangesToReplicate, this.indexableDomain.numbers)
746
+ : rangesToReplicate[0];
736
747
 
737
- for (const [_key, mergeCandidate] of mergeRangesThatAlreadyExist) {
738
- if (this.domain.canMerge(mergeCandidate, range)) {
739
- mergeableFiltered.push(mergeCandidate);
740
- } else {
741
- toKeep.add(mergeCandidate.idString);
742
- }
743
- }
748
+ // also merge segments that are already in the index
749
+ if (this.domain.canMerge) {
750
+ const mergeRangesThatAlreadyExist = await getAllMergeCandiates(
751
+ this.replicationIndex,
752
+ range,
753
+ this.indexableDomain.numbers,
754
+ );
755
+ const mergeableFiltered: ReplicationRangeIndexable<R>[] = [];
756
+ const toKeep: Set<string> = new Set();
744
757
 
745
- mergeableFiltered.push(range); // * we push this last, because mergeRanges will reuse ids of the first elements
746
- if (mergeableFiltered.length > 1) {
747
- // ** this is important here as we want to reuse ids of what we already persist, not the new ranges, so we dont get a delet add op, but just a update op
748
- range = mergeRanges(
749
- mergeableFiltered,
750
- this.indexableDomain.numbers,
751
- );
758
+ for (const [_key, mergeCandidate] of mergeRangesThatAlreadyExist) {
759
+ if (this.domain.canMerge(mergeCandidate, range)) {
760
+ mergeableFiltered.push(mergeCandidate);
761
+ } else {
762
+ toKeep.add(mergeCandidate.idString);
752
763
  }
753
- for (const [_key, mergeCandidate] of mergeRangesThatAlreadyExist) {
754
- if (
755
- mergeCandidate.idString !== range.idString &&
756
- !toKeep.has(mergeCandidate.idString)
757
- ) {
758
- rangesToUnreplicate.push(mergeCandidate);
759
- }
764
+ }
765
+
766
+ mergeableFiltered.push(range); // * we push this last, because mergeRanges will reuse ids of the first elements
767
+ if (mergeableFiltered.length > 1) {
768
+ // ** this is important here as we want to reuse ids of what we already persist, not the new ranges, so we dont get a delet add op, but just a update op
769
+ range = mergeRanges(
770
+ mergeableFiltered,
771
+ this.indexableDomain.numbers,
772
+ );
773
+ }
774
+ for (const [_key, mergeCandidate] of mergeRangesThatAlreadyExist) {
775
+ if (
776
+ mergeCandidate.idString !== range.idString &&
777
+ !toKeep.has(mergeCandidate.idString)
778
+ ) {
779
+ rangesToUnreplicate.push(mergeCandidate);
760
780
  }
761
781
  }
762
- rangesToReplicate = [range];
763
782
  }
783
+ rangesToReplicate = [range];
764
784
  }
785
+ }
765
786
 
766
- for (const range of rangesToReplicate) {
767
- this.oldestOpenTime = Math.min(
768
- Number(range.timestamp),
769
- this.oldestOpenTime,
770
- );
771
- }
772
-
773
- let resetRanges = reset;
774
- if (!resetRanges && !offsetWasProvided) {
775
- resetRanges = true;
776
- // because if we do something like replicate ({ factor: 0.5 }) it means that we want to replicate 50%
777
- // but ({ replicate: 0.5, offset: 0.5 }) means that we want to add a range
778
- // TODO make behaviour more clear
779
- }
780
- if (rangesToUnreplicate.length > 0) {
781
- await this.removeReplicationRanges(
782
- rangesToUnreplicate,
783
- this.node.identity.publicKey,
784
- );
785
- }
787
+ for (const range of rangesToReplicate) {
788
+ this.oldestOpenTime = Math.min(
789
+ Number(range.timestamp),
790
+ this.oldestOpenTime,
791
+ );
792
+ }
786
793
 
787
- await this.startAnnounceReplicating(rangesToReplicate, {
788
- reset: resetRanges ?? false,
789
- checkDuplicates,
790
- announce,
791
- rebalance,
792
- });
794
+ let resetRanges = reset;
795
+ if (!resetRanges && !offsetWasProvided) {
796
+ resetRanges = true;
797
+ // because if we do something like replicate ({ factor: 0.5 }) it means that we want to replicate 50%
798
+ // but ({ replicate: 0.5, offset: 0.5 }) means that we want to add a range
799
+ // TODO make behaviour more clear
800
+ }
801
+ if (rangesToUnreplicate.length > 0) {
802
+ await this.removeReplicationRanges(
803
+ rangesToUnreplicate,
804
+ this.node.identity.publicKey,
805
+ );
806
+ }
793
807
 
794
- if (rangesToUnreplicate.length > 0) {
795
- await this.rpc.send(
796
- new StoppedReplicating({
797
- segmentIds: rangesToUnreplicate.map((x) => x.id),
798
- }),
799
- {
800
- priority: 1,
801
- },
802
- );
803
- }
808
+ await this.startAnnounceReplicating(rangesToReplicate, {
809
+ reset: resetRanges ?? false,
810
+ checkDuplicates,
811
+ announce,
812
+ rebalance,
813
+ });
804
814
 
805
- return rangesToReplicate;
815
+ if (rangesToUnreplicate.length > 0) {
816
+ await this.rpc.send(
817
+ new StoppedReplicating({
818
+ segmentIds: rangesToUnreplicate.map((x) => x.id),
819
+ }),
820
+ {
821
+ priority: 1,
822
+ },
823
+ );
806
824
  }
807
825
 
808
- return [];
826
+ return rangesToReplicate;
809
827
  }
810
828
 
811
829
  setupDebouncedRebalancing(options?: DynamicReplicationOptions<R>) {
@@ -1195,7 +1213,11 @@ export class SharedLog<
1195
1213
  },
1196
1214
  ];
1197
1215
  } else {
1198
- return { range: x, timestamp: x.timestamp, type: "added" as const };
1216
+ return {
1217
+ range: x,
1218
+ timestamp: x.timestamp,
1219
+ type: "added" as const,
1220
+ };
1199
1221
  }
1200
1222
  })
1201
1223
  .flat() as ReplicationChanges<ReplicationRangeIndexable<R>>;
@@ -1602,7 +1624,7 @@ export class SharedLog<
1602
1624
  ),
1603
1625
  waitFor: this.rpc.waitFor.bind(this.rpc),
1604
1626
  publicKey: this.node.identity.publicKey,
1605
- earlyBlocks: options?.earlyBlocks ?? true,
1627
+ eagerBlocks: options?.eagerBlocks ?? true,
1606
1628
  });
1607
1629
 
1608
1630
  await this.remoteBlocks.start();
@@ -1804,12 +1826,17 @@ export class SharedLog<
1804
1826
  let isUnreplicationOptionsDefined = isUnreplicationOptions(
1805
1827
  options?.replicate,
1806
1828
  );
1829
+
1830
+ const canResumeReplication =
1831
+ (await isReplicationOptionsDependentOnPreviousState(
1832
+ options?.replicate,
1833
+ this.replicationIndex,
1834
+ this.node.identity.publicKey,
1835
+ )) && hasIndexedReplicationInfo;
1836
+
1807
1837
  if (hasIndexedReplicationInfo && isUnreplicationOptionsDefined) {
1808
1838
  await this.replicate(options?.replicate, { checkDuplicates: true });
1809
- } else if (
1810
- isReplicationOptionsDependentOnPreviousState(options?.replicate) &&
1811
- hasIndexedReplicationInfo
1812
- ) {
1839
+ } else if (canResumeReplication) {
1813
1840
  // dont do anthing since we are alread replicating stuff
1814
1841
  } else {
1815
1842
  await this.replicate(options?.replicate, {
@@ -2540,13 +2567,13 @@ export class SharedLog<
2540
2567
  context.from!.hashcode(),
2541
2568
  );
2542
2569
 
2543
- if (prev && prev > context.timestamp) {
2570
+ if (prev && prev > context.message.header.timestamp) {
2544
2571
  return;
2545
2572
  }
2546
2573
 
2547
2574
  this.latestReplicationInfoMessage.set(
2548
2575
  context.from!.hashcode(),
2549
- context.timestamp,
2576
+ context.message.header.timestamp,
2550
2577
  );
2551
2578
 
2552
2579
  let reset = msg instanceof AllReplicatingSegmentsMessage;
@@ -2563,7 +2590,7 @@ export class SharedLog<
2563
2590
  {
2564
2591
  reset,
2565
2592
  checkDuplicates: true,
2566
- timestamp: Number(context.timestamp),
2593
+ timestamp: Number(context.message.header.timestamp),
2567
2594
  },
2568
2595
  );
2569
2596