@peerbit/shared-log 10.3.2 → 10.3.3-2ec6eb5
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.map +1 -1
- package/dist/src/index.js +39 -26
- package/dist/src/index.js.map +1 -1
- package/dist/src/ranges.d.ts +1 -1
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +82 -45
- package/dist/src/ranges.js.map +1 -1
- package/package.json +70 -70
- package/src/index.ts +43 -27
- package/src/ranges.ts +100 -56
package/src/index.ts
CHANGED
|
@@ -694,27 +694,38 @@ export class SharedLog<
|
|
|
694
694
|
|
|
695
695
|
// also merge segments that are already in the index
|
|
696
696
|
if (this.domain.canMerge) {
|
|
697
|
-
const
|
|
697
|
+
const mergeRangesThatAlreadyExist = await getAllMergeCandiates(
|
|
698
698
|
this.replicationIndex,
|
|
699
699
|
range,
|
|
700
700
|
this.indexableDomain.numbers,
|
|
701
701
|
);
|
|
702
|
-
const mergeableFiltered: ReplicationRangeIndexable<R>[] = [
|
|
702
|
+
const mergeableFiltered: ReplicationRangeIndexable<R>[] = [];
|
|
703
|
+
const toKeep: Set<string> = new Set();
|
|
703
704
|
|
|
704
|
-
for (const mergeCandidate of
|
|
705
|
+
for (const [_key, mergeCandidate] of mergeRangesThatAlreadyExist) {
|
|
705
706
|
if (this.domain.canMerge(mergeCandidate, range)) {
|
|
706
707
|
mergeableFiltered.push(mergeCandidate);
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
}
|
|
708
|
+
} else {
|
|
709
|
+
toKeep.add(mergeCandidate.idString);
|
|
710
710
|
}
|
|
711
711
|
}
|
|
712
|
+
|
|
713
|
+
mergeableFiltered.push(range); // * we push this last, because mergeRanges will reuse ids of the first elements
|
|
712
714
|
if (mergeableFiltered.length > 1) {
|
|
715
|
+
// ** 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
|
|
713
716
|
range = mergeRanges(
|
|
714
717
|
mergeableFiltered,
|
|
715
718
|
this.indexableDomain.numbers,
|
|
716
719
|
);
|
|
717
720
|
}
|
|
721
|
+
for (const [_key, mergeCandidate] of mergeRangesThatAlreadyExist) {
|
|
722
|
+
if (
|
|
723
|
+
mergeCandidate.idString !== range.idString &&
|
|
724
|
+
!toKeep.has(mergeCandidate.idString)
|
|
725
|
+
) {
|
|
726
|
+
rangesToUnreplicate.push(mergeCandidate);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
718
729
|
}
|
|
719
730
|
rangesToReplicate = [range];
|
|
720
731
|
}
|
|
@@ -1005,6 +1016,9 @@ export class SharedLog<
|
|
|
1005
1016
|
this.pendingMaturity.delete(from.hashcode());
|
|
1006
1017
|
}
|
|
1007
1018
|
}
|
|
1019
|
+
if (ranges.length === 0) {
|
|
1020
|
+
throw new Error("???");
|
|
1021
|
+
}
|
|
1008
1022
|
|
|
1009
1023
|
await this.replicationIndex.del({
|
|
1010
1024
|
query: new Or(
|
|
@@ -1303,24 +1317,29 @@ export class SharedLog<
|
|
|
1303
1317
|
logger.warn("Not allowed to replicate by canReplicate");
|
|
1304
1318
|
}
|
|
1305
1319
|
|
|
1306
|
-
let message: AllReplicatingSegmentsMessage | AddedReplicationSegmentMessage;
|
|
1307
|
-
|
|
1308
1320
|
if (change) {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
}
|
|
1321
|
+
let addedOrReplaced = change.filter((x) => x.type !== "removed");
|
|
1322
|
+
if (addedOrReplaced.length > 0) {
|
|
1323
|
+
let message:
|
|
1324
|
+
| AllReplicatingSegmentsMessage
|
|
1325
|
+
| AddedReplicationSegmentMessage
|
|
1326
|
+
| undefined = undefined;
|
|
1327
|
+
if (options.reset) {
|
|
1328
|
+
message = new AllReplicatingSegmentsMessage({
|
|
1329
|
+
segments: addedOrReplaced.map((x) => x.range.toReplicationRange()),
|
|
1330
|
+
});
|
|
1331
|
+
} else {
|
|
1332
|
+
message = new AddedReplicationSegmentMessage({
|
|
1333
|
+
segments: addedOrReplaced.map((x) => x.range.toReplicationRange()),
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
if (options.announce) {
|
|
1337
|
+
return options.announce(message);
|
|
1338
|
+
} else {
|
|
1339
|
+
await this.rpc.send(message, {
|
|
1340
|
+
priority: 1,
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1324
1343
|
}
|
|
1325
1344
|
}
|
|
1326
1345
|
}
|
|
@@ -2284,10 +2303,7 @@ export class SharedLog<
|
|
|
2284
2303
|
}
|
|
2285
2304
|
|
|
2286
2305
|
if (isLeader) {
|
|
2287
|
-
//console.log("IS LEADER", this.node.identity.publicKey.hashcode(), hash);
|
|
2288
|
-
|
|
2289
2306
|
hasAndIsLeader.push(hash);
|
|
2290
|
-
|
|
2291
2307
|
hasAndIsLeader.length > 0 &&
|
|
2292
2308
|
this.responseToPruneDebouncedFn.add({
|
|
2293
2309
|
hashes: hasAndIsLeader,
|
|
@@ -2484,6 +2500,7 @@ export class SharedLog<
|
|
|
2484
2500
|
msg.segmentIds,
|
|
2485
2501
|
context.from,
|
|
2486
2502
|
);
|
|
2503
|
+
|
|
2487
2504
|
await this.removeReplicationRanges(rangesToRemove, context.from);
|
|
2488
2505
|
const timestamp = BigInt(+new Date());
|
|
2489
2506
|
for (const range of rangesToRemove) {
|
|
@@ -3635,7 +3652,6 @@ export class SharedLog<
|
|
|
3635
3652
|
});
|
|
3636
3653
|
}
|
|
3637
3654
|
|
|
3638
|
-
// console.log("DELETE RESPONSE AS LEADER", this.node.identity.publicKey.hashcode(), entryReplicated.hash)
|
|
3639
3655
|
this.responseToPruneDebouncedFn.delete(entryReplicated.hash); // don't allow others to prune because of expecting me to replicating this entry
|
|
3640
3656
|
} else {
|
|
3641
3657
|
this.pruneDebouncedFn.delete(entryReplicated.hash);
|
package/src/ranges.ts
CHANGED
|
@@ -1019,68 +1019,112 @@ export const mergeRanges = <R extends "u32" | "u64">(
|
|
|
1019
1019
|
throw new Error("Segments have different publicKeyHash");
|
|
1020
1020
|
}
|
|
1021
1021
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
let last = sorted[sorted.length - 1];
|
|
1030
|
-
let largestArc = numbers.zero;
|
|
1031
|
-
let largestArcIndex = -1;
|
|
1032
|
-
let mode = ReplicationIntent.NonStrict;
|
|
1033
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
1034
|
-
const current = sorted[i];
|
|
1035
|
-
|
|
1036
|
-
if (current.mode === ReplicationIntent.Strict) {
|
|
1037
|
-
mode = ReplicationIntent.Strict;
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
if (current.start1 !== last.start1) {
|
|
1041
|
-
let arc = numbers.zero;
|
|
1042
|
-
if (current.start1 < last.end2) {
|
|
1043
|
-
arc += ((numbers.maxValue as any) - last.end2) as any;
|
|
1044
|
-
|
|
1045
|
-
arc += (current.start1 - numbers.zero) as any;
|
|
1046
|
-
} else {
|
|
1047
|
-
arc += (current.start1 - last.end2) as any;
|
|
1048
|
-
}
|
|
1022
|
+
// 1) Sort by start offset (avoid subtracting bigints).
|
|
1023
|
+
// We do slice() to avoid mutating the original 'segments'.
|
|
1024
|
+
const sorted = segments.slice().sort((a, b) => {
|
|
1025
|
+
if (a.start1 < b.start1) return -1;
|
|
1026
|
+
if (a.start1 > b.start1) return 1;
|
|
1027
|
+
return 0;
|
|
1028
|
+
});
|
|
1049
1029
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1030
|
+
// 2) Merge overlapping arcs in a purely functional way
|
|
1031
|
+
// so we don’t mutate any intermediate objects.
|
|
1032
|
+
const merged = sorted.reduce<ReplicationRangeIndexable<R>[]>(
|
|
1033
|
+
(acc, current) => {
|
|
1034
|
+
if (acc.length === 0) {
|
|
1035
|
+
return [current];
|
|
1054
1036
|
}
|
|
1055
|
-
last = current;
|
|
1056
|
-
}
|
|
1057
1037
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1038
|
+
const last = acc[acc.length - 1];
|
|
1039
|
+
|
|
1040
|
+
// Check overlap: next arc starts before (or exactly at) last arc's end => overlap
|
|
1041
|
+
if (current.start1 <= last.end2) {
|
|
1042
|
+
// Merge them:
|
|
1043
|
+
// - end2 is the max of last.end2 and current.end2
|
|
1044
|
+
// - width is adjusted so total covers both
|
|
1045
|
+
// - mode is strict if either arc is strict
|
|
1046
|
+
const newEnd2 = last.end2 > current.end2 ? last.end2 : current.end2;
|
|
1047
|
+
const extendedWidth = Number(newEnd2 - last.start1); // safe if smaller arcs
|
|
1048
|
+
|
|
1049
|
+
// If you need to handle big widths carefully, you might do BigInt logic here.
|
|
1050
|
+
const newMode =
|
|
1051
|
+
last.mode === ReplicationIntent.Strict ||
|
|
1052
|
+
current.mode === ReplicationIntent.Strict
|
|
1053
|
+
? ReplicationIntent.Strict
|
|
1054
|
+
: ReplicationIntent.NonStrict;
|
|
1055
|
+
|
|
1056
|
+
// Create a new merged arc object (no mutation of last)
|
|
1057
|
+
const proto = segments[0].constructor as any;
|
|
1058
|
+
const mergedArc = new proto({
|
|
1059
|
+
width: extendedWidth,
|
|
1060
|
+
offset: last.start1,
|
|
1061
|
+
publicKeyHash: last.hash,
|
|
1062
|
+
mode: newMode,
|
|
1063
|
+
id: last.id, // re-use id
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
// Return a new array with the last item replaced by mergedArc
|
|
1067
|
+
return [...acc.slice(0, -1), mergedArc];
|
|
1068
|
+
} else {
|
|
1069
|
+
// No overlap => just append current
|
|
1070
|
+
return [...acc, current];
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
[],
|
|
1074
|
+
);
|
|
1061
1075
|
|
|
1062
|
-
|
|
1076
|
+
// After the merge pass:
|
|
1077
|
+
if (merged.length === 1) {
|
|
1078
|
+
// Everything merged into one arc already
|
|
1079
|
+
return merged[0];
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// 3) OPTIONAL: If your existing logic always wants to produce a single ring arc
|
|
1083
|
+
// that covers "everything except the largest gap," do it here:
|
|
1084
|
+
|
|
1085
|
+
// Determine if any arc ended up Strict
|
|
1086
|
+
const finalMode = merged.some((m) => m.mode === ReplicationIntent.Strict)
|
|
1087
|
+
? ReplicationIntent.Strict
|
|
1088
|
+
: ReplicationIntent.NonStrict;
|
|
1089
|
+
|
|
1090
|
+
// Find the largest gap on a ring among these disjoint arcs
|
|
1091
|
+
const { largestGap, largestGapIndex } = merged.reduce<{
|
|
1092
|
+
largestGap: NumberFromType<R>;
|
|
1093
|
+
largestGapIndex: number;
|
|
1094
|
+
}>(
|
|
1095
|
+
(acc, arc, i, arr) => {
|
|
1096
|
+
// next arc in a ring
|
|
1097
|
+
const nextArc = arr[(i + 1) % arr.length];
|
|
1098
|
+
|
|
1099
|
+
// measure gap from arc.end2 -> nextArc.start1
|
|
1100
|
+
let gap: NumberFromType<R>;
|
|
1101
|
+
if (nextArc.start1 < arc.end2) {
|
|
1102
|
+
// wrap-around scenario
|
|
1103
|
+
gap = (numbers.maxValue -
|
|
1104
|
+
arc.end2 +
|
|
1105
|
+
(nextArc.start1 - numbers.zero)) as NumberFromType<R>;
|
|
1106
|
+
} else {
|
|
1107
|
+
gap = (nextArc.start1 - arc.end2) as NumberFromType<R>;
|
|
1108
|
+
}
|
|
1063
1109
|
|
|
1064
|
-
|
|
1110
|
+
if (gap > acc.largestGap) {
|
|
1111
|
+
return { largestGap: gap, largestGapIndex: (i + 1) % arr.length };
|
|
1112
|
+
}
|
|
1113
|
+
return acc;
|
|
1114
|
+
},
|
|
1115
|
+
{ largestGap: numbers.zero, largestGapIndex: -1 },
|
|
1116
|
+
);
|
|
1065
1117
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
width: segments[0].width,
|
|
1070
|
-
offset: segments[0].start1,
|
|
1071
|
-
publicKeyHash: segments[0].hash,
|
|
1072
|
-
mode,
|
|
1073
|
-
});
|
|
1074
|
-
}
|
|
1075
|
-
return segments[0]; // all ranges are the same
|
|
1076
|
-
}
|
|
1077
|
-
// use segments[0] constructor to create a new object
|
|
1118
|
+
// Single arc coverage = "the ring minus largestGap"
|
|
1119
|
+
const totalCoverage = (numbers.maxValue - largestGap) as number;
|
|
1120
|
+
const offset = merged[largestGapIndex].start1;
|
|
1078
1121
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1122
|
+
const proto = segments[0].constructor as any;
|
|
1123
|
+
return new proto({
|
|
1124
|
+
width: totalCoverage,
|
|
1125
|
+
offset,
|
|
1082
1126
|
publicKeyHash: segments[0].hash,
|
|
1083
|
-
mode,
|
|
1127
|
+
mode: finalMode,
|
|
1084
1128
|
});
|
|
1085
1129
|
};
|
|
1086
1130
|
|
|
@@ -1802,7 +1846,7 @@ export const getAllMergeCandiates = async <R extends "u32" | "u64">(
|
|
|
1802
1846
|
id: Uint8Array;
|
|
1803
1847
|
},
|
|
1804
1848
|
numbers: Numbers<R>,
|
|
1805
|
-
): Promise<
|
|
1849
|
+
): Promise<Map<string, ReplicationRangeIndexable<R>>> => {
|
|
1806
1850
|
const adjacent = await getAdjecentSameOwner(peers, range, numbers);
|
|
1807
1851
|
const covering = await getCoveringRangesSameOwner(peers, range).all();
|
|
1808
1852
|
|
|
@@ -1816,7 +1860,7 @@ export const getAllMergeCandiates = async <R extends "u32" | "u64">(
|
|
|
1816
1860
|
for (const range of covering) {
|
|
1817
1861
|
ret.set(range.value.idString, range.value);
|
|
1818
1862
|
}
|
|
1819
|
-
return ret
|
|
1863
|
+
return ret;
|
|
1820
1864
|
};
|
|
1821
1865
|
|
|
1822
1866
|
export const isMatured = (
|