@peerbit/shared-log 10.3.0 → 10.3.1-c0de42e

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
@@ -85,7 +85,6 @@ import {
85
85
  ReplicationRangeIndexableU32,
86
86
  ReplicationRangeIndexableU64,
87
87
  ReplicationRangeMessage,
88
- SyncStatus,
89
88
  appromixateCoverage,
90
89
  countCoveringRangesSameOwner,
91
90
  debounceAggregationChanges,
@@ -572,15 +571,17 @@ export class SharedLog<
572
571
  checkDuplicates,
573
572
  announce,
574
573
  mergeSegments,
574
+ rebalance,
575
575
  }: {
576
576
  reset?: boolean;
577
577
  checkDuplicates?: boolean;
578
578
  mergeSegments?: boolean;
579
+ rebalance?: boolean;
579
580
  announce?: (
580
581
  msg: AddedReplicationSegmentMessage | AllReplicatingSegmentsMessage,
581
582
  ) => void;
582
583
  } = {},
583
- ) {
584
+ ): Promise<ReplicationRangeIndexable<R>[]> {
584
585
  let offsetWasProvided = false;
585
586
  if (isUnreplicationOptions(options)) {
586
587
  await this.unreplicate();
@@ -607,7 +608,7 @@ export class SharedLog<
607
608
  const maybeRange = await this.getDynamicRange();
608
609
  if (!maybeRange) {
609
610
  // not allowed
610
- return;
611
+ return [];
611
612
  }
612
613
  rangesToReplicate = [maybeRange];
613
614
 
@@ -634,7 +635,7 @@ export class SharedLog<
634
635
 
635
636
  if (rangeArgs.length === 0) {
636
637
  // nothing to do
637
- return;
638
+ return [];
638
639
  }
639
640
 
640
641
  for (const rangeArg of rangeArgs) {
@@ -744,6 +745,7 @@ export class SharedLog<
744
745
  reset: resetRanges ?? false,
745
746
  checkDuplicates,
746
747
  announce,
748
+ rebalance,
747
749
  });
748
750
 
749
751
  if (rangesToUnreplicate.length > 0) {
@@ -759,6 +761,8 @@ export class SharedLog<
759
761
 
760
762
  return rangesToReplicate;
761
763
  }
764
+
765
+ return [];
762
766
  }
763
767
 
764
768
  setupDebouncedRebalancing(options?: DynamicReplicationOptions<R>) {
@@ -796,6 +800,7 @@ export class SharedLog<
796
800
  options?: {
797
801
  reset?: boolean;
798
802
  checkDuplicates?: boolean;
803
+ rebalance?: boolean;
799
804
  mergeSegments?: boolean;
800
805
  announce?: (
801
806
  msg: AllReplicatingSegmentsMessage | AddedReplicationSegmentMessage,
@@ -1036,13 +1041,20 @@ export class SharedLog<
1036
1041
  reset,
1037
1042
  checkDuplicates,
1038
1043
  timestamp: ts,
1039
- }: { reset?: boolean; checkDuplicates?: boolean; timestamp?: number } = {},
1044
+ rebalance,
1045
+ }: {
1046
+ reset?: boolean;
1047
+ rebalance?: boolean;
1048
+ checkDuplicates?: boolean;
1049
+ timestamp?: number;
1050
+ } = {},
1040
1051
  ) {
1041
1052
  if (this._isTrustedReplicator && !(await this._isTrustedReplicator(from))) {
1042
1053
  return undefined;
1043
1054
  }
1044
1055
  let isNewReplicator = false;
1045
1056
  let timestamp = BigInt(ts ?? +new Date());
1057
+ rebalance = rebalance == null ? true : rebalance;
1046
1058
 
1047
1059
  let diffs: ReplicationChanges<ReplicationRangeIndexable<R>>;
1048
1060
  let deleted: ReplicationRangeIndexable<R>[] | undefined = undefined;
@@ -1070,16 +1082,25 @@ export class SharedLog<
1070
1082
 
1071
1083
  isNewReplicator = prevCount === 0 && ranges.length > 0;
1072
1084
  } else {
1073
- let existing = await this.replicationIndex
1074
- .iterate(
1075
- {
1076
- query: ranges.map(
1077
- (x) => new ByteMatchQuery({ key: "id", value: x.id }),
1078
- ),
1079
- },
1080
- { reference: true },
1081
- )
1082
- .all();
1085
+ let batchSize = 100;
1086
+ let existing: ReplicationRangeIndexable<R>[] = [];
1087
+ for (let i = 0; i < ranges.length; i += batchSize) {
1088
+ const results = await this.replicationIndex
1089
+ .iterate(
1090
+ {
1091
+ query: (ranges.length <= batchSize
1092
+ ? ranges
1093
+ : ranges.slice(i, i + batchSize)
1094
+ ).map((x) => new ByteMatchQuery({ key: "id", value: x.id })),
1095
+ },
1096
+ { reference: true },
1097
+ )
1098
+ .all();
1099
+ for (const result of results) {
1100
+ existing.push(result.value);
1101
+ }
1102
+ }
1103
+
1083
1104
  if (existing.length === 0) {
1084
1105
  let prevCount = await this.replicationIndex.count({
1085
1106
  query: new StringMatch({ key: "hash", value: from.hashcode() }),
@@ -1104,7 +1125,7 @@ export class SharedLog<
1104
1125
  }
1105
1126
  let existingMap = new Map<string, ReplicationRangeIndexable<any>>();
1106
1127
  for (const result of existing) {
1107
- existingMap.set(result.value.idString, result.value);
1128
+ existingMap.set(result.idString, result);
1108
1129
  }
1109
1130
 
1110
1131
  let changes: ReplicationChanges<ReplicationRangeIndexable<R>> = ranges
@@ -1181,7 +1202,13 @@ export class SharedLog<
1181
1202
  }),
1182
1203
  );
1183
1204
 
1184
- this.replicationChangeDebounceFn.add({ ...diff, matured: true }); // we need to call this here because the outcom of findLeaders will be different when some ranges become mature, i.e. some of data we own might be prunable!
1205
+ if (rebalance && diff.range.mode !== ReplicationIntent.Strict) {
1206
+ // TODO this statement (might) cause issues with triggering pruning if the segment is strict and maturity timings will affect the outcome of rebalancing
1207
+ this.replicationChangeDebounceFn.add({
1208
+ ...diff,
1209
+ matured: true,
1210
+ }); // we need to call this here because the outcom of findLeaders will be different when some ranges become mature, i.e. some of data we own might be prunable!
1211
+ }
1185
1212
  pendingRanges.delete(diff.range.idString);
1186
1213
  if (pendingRanges.size === 0) {
1187
1214
  this.pendingMaturity.delete(diff.range.hash);
@@ -1242,7 +1269,7 @@ export class SharedLog<
1242
1269
  }
1243
1270
  }
1244
1271
 
1245
- if (diffs.length > 0) {
1272
+ if (rebalance && diffs.length > 0) {
1246
1273
  for (const diff of diffs) {
1247
1274
  this.replicationChangeDebounceFn.add(diff);
1248
1275
  }
@@ -1258,9 +1285,9 @@ export class SharedLog<
1258
1285
  async startAnnounceReplicating(
1259
1286
  range: ReplicationRangeIndexable<R>[],
1260
1287
  options: {
1261
- syncStatus?: SyncStatus;
1262
1288
  reset?: boolean;
1263
1289
  checkDuplicates?: boolean;
1290
+ rebalance?: boolean;
1264
1291
  announce?: (
1265
1292
  msg: AllReplicatingSegmentsMessage | AddedReplicationSegmentMessage,
1266
1293
  ) => void;
@@ -1315,7 +1342,7 @@ export class SharedLog<
1315
1342
  }
1316
1343
  }
1317
1344
 
1318
- private addPeersToGidPeerHistory(
1345
+ addPeersToGidPeerHistory(
1319
1346
  gid: string,
1320
1347
  publicKeys: Iterable<string>,
1321
1348
  reset?: boolean,
@@ -1922,7 +1949,6 @@ export class SharedLog<
1922
1949
  this.coordinateToHash.clear();
1923
1950
  this.recentlyRebalanced.clear();
1924
1951
  this.uniqueReplicators.clear();
1925
-
1926
1952
  this._closeController.abort();
1927
1953
 
1928
1954
  clearInterval(this.interval);
@@ -2667,6 +2693,10 @@ export class SharedLog<
2667
2693
  }
2668
2694
  : undefined;
2669
2695
 
2696
+ let assumeSynced =
2697
+ options?.replicate &&
2698
+ typeof options.replicate !== "boolean" &&
2699
+ options.replicate.assumeSynced;
2670
2700
  const persistCoordinate = async (entry: Entry<T>) => {
2671
2701
  const minReplicas = decodeReplicas(entry).getValue(this);
2672
2702
  const leaders = await this.findLeaders(
@@ -2675,11 +2705,7 @@ export class SharedLog<
2675
2705
  { persist: {} },
2676
2706
  );
2677
2707
 
2678
- if (
2679
- options?.replicate &&
2680
- typeof options.replicate !== "boolean" &&
2681
- options.replicate.assumeSynced
2682
- ) {
2708
+ if (assumeSynced) {
2683
2709
  // make sure we dont start to initate syncing process outwards for this entry
2684
2710
  this.addPeersToGidPeerHistory(entry.meta.gid, leaders.keys());
2685
2711
  }
@@ -2711,7 +2737,9 @@ export class SharedLog<
2711
2737
 
2712
2738
  if (options?.replicate) {
2713
2739
  let messageToSend: AddedReplicationSegmentMessage | undefined = undefined;
2740
+
2714
2741
  await this.replicate(entriesToReplicate, {
2742
+ rebalance: assumeSynced ? false : true,
2715
2743
  checkDuplicates: true,
2716
2744
  mergeSegments:
2717
2745
  typeof options.replicate !== "boolean" && options.replicate
@@ -2736,6 +2764,7 @@ export class SharedLog<
2736
2764
  },
2737
2765
  });
2738
2766
 
2767
+ // it is importat that we call persistCoordinate after this.replicate(entries) as else there might be a prune job deleting the entry before replication duties has been assigned to self
2739
2768
  for (const entry of entriesToPersist) {
2740
2769
  await persistCoordinate(entry);
2741
2770
  }
@@ -2747,39 +2776,6 @@ export class SharedLog<
2747
2776
  }
2748
2777
  }
2749
2778
  }
2750
- /*
2751
- private async updateLeaders(
2752
- cursor: NumberFromType<R>,
2753
- prev: EntryReplicated<R>,
2754
- options?: {
2755
- roleAge?: number;
2756
- },
2757
- ): Promise<{
2758
- isLeader: boolean;
2759
- leaders: Map<string, { intersecting: boolean }>;
2760
- }> {
2761
- // we consume a list of coordinates in this method since if we are leader of one coordinate we want to persist all of them
2762
- const leaders = await this._findLeaders(cursor, options);
2763
- const isLeader = leaders.has(this.node.identity.publicKey.hashcode());
2764
- const isAtRangeBoundary = shouldAssignToRangeBoundary(leaders, 1);
2765
-
2766
- // dont do anthing if nothing has changed
2767
- if (prev.assignedToRangeBoundary !== isAtRangeBoundary) {
2768
- return { isLeader, leaders };
2769
- }
2770
-
2771
- await this.entryCoordinatesIndex.put(
2772
- new this.indexableDomain.constructorEntry({
2773
- assignedToRangeBoundary: isAtRangeBoundary,
2774
- coordinate: cursor,
2775
- meta: prev.meta,
2776
- hash: prev.hash,
2777
- }),
2778
- );
2779
-
2780
- return { isLeader, leaders };
2781
- }
2782
- */
2783
2779
 
2784
2780
  private async _waitForReplicators(
2785
2781
  cursors: NumberFromType<R>[],
@@ -3411,7 +3407,6 @@ export class SharedLog<
3411
3407
  const minReplicasValue = minReplicasObj.getValue(this);
3412
3408
 
3413
3409
  // TODO is this check necessary
3414
-
3415
3410
  if (
3416
3411
  !(await this._waitForReplicators(
3417
3412
  cursor ??
@@ -3542,7 +3537,7 @@ export class SharedLog<
3542
3537
  }
3543
3538
 
3544
3539
  const timestamp = BigInt(+new Date());
3545
- this.onReplicationChange(
3540
+ return this.onReplicationChange(
3546
3541
  (await this.getAllReplicationSegments()).map((x) => {
3547
3542
  return { range: x, type: "added", timestamp };
3548
3543
  }),
@@ -3577,7 +3572,6 @@ export class SharedLog<
3577
3572
  Map<string, EntryReplicated<any>>
3578
3573
  > = new Map();
3579
3574
 
3580
- let c = 0;
3581
3575
  for await (const entryReplicated of toRebalance<R>(
3582
3576
  changeOrChanges,
3583
3577
  this.entryCoordinatesIndex,
@@ -3586,7 +3580,6 @@ export class SharedLog<
3586
3580
  if (this.closed) {
3587
3581
  break;
3588
3582
  }
3589
- c++;
3590
3583
 
3591
3584
  let oldPeersSet = this._gidPeersHistory.get(entryReplicated.gid);
3592
3585
  let isLeader = false;
package/src/ranges.ts CHANGED
@@ -43,11 +43,6 @@ export enum ReplicationIntent {
43
43
  Strict = 1, // only replicate data in the segment to the specified replicator, not any other data
44
44
  }
45
45
 
46
- export enum SyncStatus {
47
- Unsynced = 0,
48
- Synced = 1,
49
- }
50
-
51
46
  const min = (a: number | bigint, b: number | bigint) => (a < b ? a : b);
52
47
 
53
48
  const getSegmentsFromOffsetAndRange = <T extends number | bigint>(
@@ -1897,7 +1892,6 @@ export const getSamples = async <R extends "u32" | "u64">(
1897
1892
  const now = +new Date();
1898
1893
  let matured = 0;
1899
1894
 
1900
- /* let missingForCursors: NumberFromType<R>[] = [] */
1901
1895
  let uniqueVisited = new Set<string>();
1902
1896
  for (let i = 0; i < cursor.length; i++) {
1903
1897
  let point = cursor[i];
@@ -1962,6 +1956,7 @@ export const getSamples = async <R extends "u32" | "u64">(
1962
1956
  /* if (leaders.size < cursor.length) {
1963
1957
  throw new Error("Missing leaders got: " + leaders.size + " -- expected -- " + cursor.length + " role age " + roleAge + " missing " + missingForCursors.length + " replication index size: " + (await peers.count()));
1964
1958
  } */
1959
+
1965
1960
  return leaders;
1966
1961
  };
1967
1962
 
@@ -2277,6 +2272,9 @@ export const debounceAggregationChanges = <
2277
2272
  ) => {
2278
2273
  return debounceAccumulator(
2279
2274
  (result) => {
2275
+ if (result.size === 0) {
2276
+ return;
2277
+ }
2280
2278
  return fn([...result.values()]);
2281
2279
  },
2282
2280
  () => {
@@ -2450,12 +2448,6 @@ export const toRebalance = <R extends "u32" | "u64">(
2450
2448
 
2451
2449
  while (iterator.done() !== true) {
2452
2450
  const entries = await iterator.all(); // TODO choose right batch sizes here for optimal memory usage / speed
2453
-
2454
- /* const grouped = await groupByGidSync(entries.map((x) => x.value));
2455
- for (const [gid, entries] of grouped.entries()) {
2456
- yield { gid, entries };
2457
- } */
2458
-
2459
2451
  for (const entry of entries) {
2460
2452
  yield entry.value;
2461
2453
  }
@@ -3,6 +3,7 @@ import { Cache } from "@peerbit/cache";
3
3
  import { type PublicSignKey, randomBytes, toBase64 } from "@peerbit/crypto";
4
4
  import { type Index } from "@peerbit/indexer-interface";
5
5
  import type { Entry, Log } from "@peerbit/log";
6
+ import { logger as loggerFn } from "@peerbit/logger";
6
7
  import { DecoderWrapper, EncoderWrapper } from "@peerbit/riblt";
7
8
  import type { RPC, RequestContext } from "@peerbit/rpc";
8
9
  import { SilentDelivery } from "@peerbit/stream-interface";
@@ -17,6 +18,8 @@ import {
17
18
  } from "../ranges.js";
18
19
  import { SimpleSyncronizer } from "./simple.js";
19
20
 
21
+ export const logger = loggerFn({ module: "shared-log" });
22
+
20
23
  type NumberOrBigint = number | bigint;
21
24
 
22
25
  const coerceBigInt = (value: NumberOrBigint): bigint =>
@@ -470,7 +473,16 @@ export class RatelessIBLTSynchronizer<
470
473
  for (const symbol of message.symbols) {
471
474
  decoder.add_coded_symbol(symbol);
472
475
  }
473
- decoder.try_decode();
476
+ try {
477
+ decoder.try_decode();
478
+ } catch (error: any) {
479
+ if (error?.message === "Invalid degree") {
480
+ logger.error(error?.message);
481
+ return false;
482
+ } else {
483
+ throw error;
484
+ }
485
+ }
474
486
  count += message.symbols.length;
475
487
 
476
488
  if (decoder.decoded()) {