@peerbit/shared-log 11.0.8 → 11.1.0-2f92713
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/dist/src/index.d.ts +7 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +158 -144
- package/dist/src/index.js.map +1 -1
- package/package.json +70 -70
- package/src/index.ts +213 -181
package/src/index.ts
CHANGED
|
@@ -201,13 +201,21 @@ export type FixedReplicationOptions = {
|
|
|
201
201
|
offset?: number | bigint;
|
|
202
202
|
};
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
type NewReplicationOptions<R extends "u32" | "u64" = any> =
|
|
205
205
|
| DynamicReplicationOptions<R>
|
|
206
206
|
| FixedReplicationOptions
|
|
207
207
|
| FixedReplicationOptions[]
|
|
208
208
|
| number
|
|
209
|
-
| boolean
|
|
210
|
-
|
|
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
|
|
239
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
638
|
+
return [];
|
|
639
|
+
}
|
|
640
|
+
if ((options as ExistingReplicationOptions).type === "resume") {
|
|
641
|
+
options = (options as ExistingReplicationOptions)
|
|
642
|
+
.default as ReplicationOptions<R>;
|
|
643
|
+
}
|
|
626
644
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
} else if (options === true) {
|
|
630
|
-
options = {};
|
|
631
|
-
}
|
|
645
|
+
let rangesToReplicate: ReplicationRangeIndexable<R>[] = [];
|
|
646
|
+
let rangesToUnreplicate: ReplicationRangeIndexable<R>[] = [];
|
|
632
647
|
|
|
633
|
-
|
|
648
|
+
if (options == null) {
|
|
649
|
+
options = {};
|
|
650
|
+
} else if (options === true) {
|
|
651
|
+
options = {};
|
|
652
|
+
}
|
|
634
653
|
|
|
635
|
-
|
|
636
|
-
this._isAdaptiveReplicating = true;
|
|
637
|
-
this.setupDebouncedRebalancing(options);
|
|
654
|
+
this._isReplicating = true;
|
|
638
655
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
// not allowed
|
|
643
|
-
return [];
|
|
644
|
-
}
|
|
645
|
-
rangesToReplicate = [maybeRange];
|
|
656
|
+
if (isAdaptiveReplicatorOption(options!)) {
|
|
657
|
+
this._isAdaptiveReplicating = true;
|
|
658
|
+
this.setupDebouncedRebalancing(options);
|
|
646
659
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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
|
-
|
|
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
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
689
|
+
if (rangeArgs.length === 0) {
|
|
690
|
+
// nothing to do
|
|
691
|
+
return [];
|
|
692
|
+
}
|
|
672
693
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
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
|
-
|
|
746
|
-
if (
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
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
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
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
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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
|
-
|
|
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>) {
|
|
@@ -879,6 +897,11 @@ export class SharedLog<
|
|
|
879
897
|
}
|
|
880
898
|
}
|
|
881
899
|
range = ranges;
|
|
900
|
+
} else if (
|
|
901
|
+
rangeOrEntry &&
|
|
902
|
+
(rangeOrEntry as ExistingReplicationOptions<R>).type === "resume"
|
|
903
|
+
) {
|
|
904
|
+
range = (rangeOrEntry as ExistingReplicationOptions<R>).default;
|
|
882
905
|
} else {
|
|
883
906
|
range = rangeOrEntry ?? true;
|
|
884
907
|
}
|
|
@@ -1195,7 +1218,11 @@ export class SharedLog<
|
|
|
1195
1218
|
},
|
|
1196
1219
|
];
|
|
1197
1220
|
} else {
|
|
1198
|
-
return {
|
|
1221
|
+
return {
|
|
1222
|
+
range: x,
|
|
1223
|
+
timestamp: x.timestamp,
|
|
1224
|
+
type: "added" as const,
|
|
1225
|
+
};
|
|
1199
1226
|
}
|
|
1200
1227
|
})
|
|
1201
1228
|
.flat() as ReplicationChanges<ReplicationRangeIndexable<R>>;
|
|
@@ -1602,7 +1629,7 @@ export class SharedLog<
|
|
|
1602
1629
|
),
|
|
1603
1630
|
waitFor: this.rpc.waitFor.bind(this.rpc),
|
|
1604
1631
|
publicKey: this.node.identity.publicKey,
|
|
1605
|
-
|
|
1632
|
+
eagerBlocks: options?.eagerBlocks ?? true,
|
|
1606
1633
|
});
|
|
1607
1634
|
|
|
1608
1635
|
await this.remoteBlocks.start();
|
|
@@ -1804,12 +1831,17 @@ export class SharedLog<
|
|
|
1804
1831
|
let isUnreplicationOptionsDefined = isUnreplicationOptions(
|
|
1805
1832
|
options?.replicate,
|
|
1806
1833
|
);
|
|
1834
|
+
|
|
1835
|
+
const canResumeReplication =
|
|
1836
|
+
(await isReplicationOptionsDependentOnPreviousState(
|
|
1837
|
+
options?.replicate,
|
|
1838
|
+
this.replicationIndex,
|
|
1839
|
+
this.node.identity.publicKey,
|
|
1840
|
+
)) && hasIndexedReplicationInfo;
|
|
1841
|
+
|
|
1807
1842
|
if (hasIndexedReplicationInfo && isUnreplicationOptionsDefined) {
|
|
1808
1843
|
await this.replicate(options?.replicate, { checkDuplicates: true });
|
|
1809
|
-
} else if (
|
|
1810
|
-
isReplicationOptionsDependentOnPreviousState(options?.replicate) &&
|
|
1811
|
-
hasIndexedReplicationInfo
|
|
1812
|
-
) {
|
|
1844
|
+
} else if (canResumeReplication) {
|
|
1813
1845
|
// dont do anthing since we are alread replicating stuff
|
|
1814
1846
|
} else {
|
|
1815
1847
|
await this.replicate(options?.replicate, {
|
|
@@ -2540,13 +2572,13 @@ export class SharedLog<
|
|
|
2540
2572
|
context.from!.hashcode(),
|
|
2541
2573
|
);
|
|
2542
2574
|
|
|
2543
|
-
if (prev && prev > context.timestamp) {
|
|
2575
|
+
if (prev && prev > context.message.header.timestamp) {
|
|
2544
2576
|
return;
|
|
2545
2577
|
}
|
|
2546
2578
|
|
|
2547
2579
|
this.latestReplicationInfoMessage.set(
|
|
2548
2580
|
context.from!.hashcode(),
|
|
2549
|
-
context.timestamp,
|
|
2581
|
+
context.message.header.timestamp,
|
|
2550
2582
|
);
|
|
2551
2583
|
|
|
2552
2584
|
let reset = msg instanceof AllReplicatingSegmentsMessage;
|
|
@@ -2563,7 +2595,7 @@ export class SharedLog<
|
|
|
2563
2595
|
{
|
|
2564
2596
|
reset,
|
|
2565
2597
|
checkDuplicates: true,
|
|
2566
|
-
timestamp: Number(context.timestamp),
|
|
2598
|
+
timestamp: Number(context.message.header.timestamp),
|
|
2567
2599
|
},
|
|
2568
2600
|
);
|
|
2569
2601
|
|