@peerbit/shared-log 10.0.6 → 10.1.0-7d319f1

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.js CHANGED
@@ -26,13 +26,13 @@ import { BlocksMessage } from "./blocks.js";
26
26
  import { CPUUsageIntervalLag } from "./cpu.js";
27
27
  import { debounceAccumulator, debounceFixedInterval, debouncedAccumulatorMap, } from "./debounce.js";
28
28
  import { EntryWithRefs, ExchangeHeadsMessage, RequestIPrune, ResponseIPrune, createExchangeHeadsMessages, } from "./exchange-heads.js";
29
- import { MAX_U32, bytesToNumber, createNumbers, denormalizer, } from "./integers.js";
29
+ import { MAX_U32, MAX_U64, bytesToNumber, createNumbers, denormalizer, } from "./integers.js";
30
30
  import { TransportMessage } from "./message.js";
31
31
  import { PIDReplicationController } from "./pid.js";
32
- import { EntryReplicatedU32, EntryReplicatedU64, ReplicationIntent, ReplicationRangeIndexableU32, ReplicationRangeIndexableU64, ReplicationRangeMessage, SyncStatus, appromixateCoverage, getCoverSet, getSamples, iHaveCoveringRange, isMatured, isReplicationRangeMessage, mergeRanges, minimumWidthToCover, shouldAssigneToRangeBoundary as shouldAssignToRangeBoundary, toRebalance, } from "./ranges.js";
32
+ import { EntryReplicatedU32, EntryReplicatedU64, ReplicationIntent, ReplicationRangeIndexableU32, ReplicationRangeIndexableU64, ReplicationRangeMessage, SyncStatus, appromixateCoverage, countCoveringRangesSameOwner, debounceAggregationChanges, getAllMergeCandiates, getCoverSet, getSamples, isMatured, isReplicationRangeMessage, mergeRanges, minimumWidthToCover, shouldAssigneToRangeBoundary as shouldAssignToRangeBoundary, toRebalance, } from "./ranges.js";
33
33
  import { createReplicationDomainHash, } from "./replication-domain-hash.js";
34
34
  import { createReplicationDomainTime, } from "./replication-domain-time.js";
35
- import { debounceAggregationChanges, mergeReplicationChanges, } from "./replication-domain.js";
35
+ import {} from "./replication-domain.js";
36
36
  import { AbsoluteReplicas, AddedReplicationSegmentMessage, AllReplicatingSegmentsMessage, MinReplicas, ReplicationError, RequestReplicationInfoMessage, ResponseRoleMessage, StoppedReplicating, decodeReplicas, encodeReplicas, maxReplicas, } from "./replication.js";
37
37
  import { Observer, Replicator } from "./role.js";
38
38
  import { RatelessIBLTSynchronizer } from "./sync/rateless-iblt.js";
@@ -42,6 +42,7 @@ export { createReplicationDomainHash, createReplicationDomainTime, };
42
42
  export { CPUUsageIntervalLag };
43
43
  export * from "./replication.js";
44
44
  export { EntryReplicatedU32, EntryReplicatedU64, };
45
+ export { MAX_U32, MAX_U64 };
45
46
  export const logger = loggerFn({ module: "shared-log" });
46
47
  const getLatestEntry = (entries) => {
47
48
  let latest = undefined;
@@ -77,6 +78,9 @@ const isReplicationOptionsDependentOnPreviousState = (options) => {
77
78
  if (options === true) {
78
79
  return true;
79
80
  }
81
+ if (options === "resume") {
82
+ return true;
83
+ }
80
84
  if (options == null) {
81
85
  // when not providing options, we assume previous behaviour
82
86
  return true;
@@ -135,6 +139,7 @@ let SharedLog = class SharedLog extends Program {
135
139
  _replicationRangeIndex;
136
140
  _entryCoordinatesIndex;
137
141
  coordinateToHash;
142
+ recentlyRebalanced;
138
143
  uniqueReplicators;
139
144
  /* private _totalParticipation!: number; */
140
145
  // gid -> coordinate -> publicKeyHash list (of owners)
@@ -230,13 +235,17 @@ let SharedLog = class SharedLog extends Program {
230
235
  ) */
231
236
  () => intervalTime);
232
237
  }
233
- async _replicate(options, { reset, checkDuplicates, syncStatus, announce, mergeSegments, } = {}) {
238
+ async _replicate(options, { reset, checkDuplicates, announce, mergeSegments, } = {}) {
234
239
  let offsetWasProvided = false;
235
240
  if (isUnreplicationOptions(options)) {
236
241
  await this.unreplicate();
237
242
  }
243
+ else if (options === "resume") {
244
+ // don't do anything
245
+ }
238
246
  else {
239
- let ranges = [];
247
+ let rangesToReplicate = [];
248
+ let rangesToUnreplicate = [];
240
249
  if (options == null) {
241
250
  options = {};
242
251
  }
@@ -254,11 +263,11 @@ let SharedLog = class SharedLog extends Program {
254
263
  // not allowed
255
264
  return;
256
265
  }
257
- ranges = [maybeRange];
266
+ rangesToReplicate = [maybeRange];
258
267
  offsetWasProvided = true;
259
268
  }
260
269
  else if (isReplicationRangeMessage(options)) {
261
- ranges = [
270
+ rangesToReplicate = [
262
271
  options.toReplicationRangeIndexable(this.node.identity.publicKey),
263
272
  ];
264
273
  offsetWasProvided = true;
@@ -302,23 +311,17 @@ let SharedLog = class SharedLog extends Program {
302
311
  let factorDenormalized = !normalized
303
312
  ? factor
304
313
  : this.indexableDomain.numbers.denormalize(factor);
305
- ranges.push(new this.indexableDomain.constructorRange({
314
+ rangesToReplicate.push(new this.indexableDomain.constructorRange({
306
315
  id: rangeArg.id,
307
316
  // @ts-ignore
308
317
  offset: offset,
309
318
  // @ts-ignore
310
- length: (factor === "all"
319
+ width: (factor === "all"
311
320
  ? fullWidth
312
321
  : factor === "right"
313
322
  ? // @ts-ignore
314
323
  fullWidth - offset
315
324
  : factorDenormalized),
316
- /* typeof factor === "number"
317
- ? factor
318
- : factor === "all"
319
- ? width
320
- // @ts-ignore
321
- : width - offset, */
322
325
  publicKeyHash: this.node.identity.publicKey.hashcode(),
323
326
  mode: rangeArg.strict
324
327
  ? ReplicationIntent.Strict
@@ -326,12 +329,30 @@ let SharedLog = class SharedLog extends Program {
326
329
  timestamp: timestamp ?? BigInt(+new Date()),
327
330
  }));
328
331
  }
329
- if (mergeSegments && ranges.length > 1) {
330
- const mergedSegment = mergeRanges(ranges, this.indexableDomain.numbers);
331
- ranges = [mergedSegment];
332
+ if (mergeSegments) {
333
+ let range = rangesToReplicate.length > 1
334
+ ? mergeRanges(rangesToReplicate, this.indexableDomain.numbers)
335
+ : rangesToReplicate[0];
336
+ // also merge segments that are already in the index
337
+ if (this.domain.canMerge) {
338
+ const mergable = await getAllMergeCandiates(this.replicationIndex, range, this.indexableDomain.numbers);
339
+ const mergeableFiltered = [range];
340
+ for (const mergeCandidate of mergable) {
341
+ if (this.domain.canMerge(mergeCandidate, range)) {
342
+ mergeableFiltered.push(mergeCandidate);
343
+ if (mergeCandidate.idString !== range.idString) {
344
+ rangesToUnreplicate.push(mergeCandidate);
345
+ }
346
+ }
347
+ }
348
+ if (mergeableFiltered.length > 1) {
349
+ range = mergeRanges(mergeableFiltered, this.indexableDomain.numbers);
350
+ }
351
+ }
352
+ rangesToReplicate = [range];
332
353
  }
333
354
  }
334
- for (const range of ranges) {
355
+ for (const range of rangesToReplicate) {
335
356
  this.oldestOpenTime = Math.min(Number(range.timestamp), this.oldestOpenTime);
336
357
  }
337
358
  let resetRanges = reset;
@@ -341,13 +362,22 @@ let SharedLog = class SharedLog extends Program {
341
362
  // but ({ replicate: 0.5, offset: 0.5 }) means that we want to add a range
342
363
  // TODO make behaviour more clear
343
364
  }
344
- await this.startAnnounceReplicating(ranges, {
365
+ if (rangesToUnreplicate.length > 0) {
366
+ await this.removeReplicationRanges(rangesToUnreplicate, this.node.identity.publicKey);
367
+ }
368
+ await this.startAnnounceReplicating(rangesToReplicate, {
345
369
  reset: resetRanges ?? false,
346
370
  checkDuplicates,
347
371
  announce,
348
- syncStatus,
349
372
  });
350
- return ranges;
373
+ if (rangesToUnreplicate.length > 0) {
374
+ await this.rpc.send(new StoppedReplicating({
375
+ segmentIds: rangesToUnreplicate.map((x) => x.id),
376
+ }), {
377
+ priority: 1,
378
+ });
379
+ }
380
+ return rangesToReplicate;
351
381
  }
352
382
  }
353
383
  setupDebouncedRebalancing(options) {
@@ -373,7 +403,6 @@ let SharedLog = class SharedLog extends Program {
373
403
  }
374
404
  async replicate(rangeOrEntry, options) {
375
405
  let range = undefined;
376
- let syncStatus = SyncStatus.Unsynced;
377
406
  if (rangeOrEntry instanceof ReplicationRangeMessage) {
378
407
  range = rangeOrEntry;
379
408
  }
@@ -383,7 +412,6 @@ let SharedLog = class SharedLog extends Program {
383
412
  offset: await this.domain.fromEntry(rangeOrEntry),
384
413
  normalized: false,
385
414
  };
386
- syncStatus = SyncStatus.Synced; /// we already have the entries
387
415
  }
388
416
  else if (Array.isArray(rangeOrEntry)) {
389
417
  let ranges = [];
@@ -393,8 +421,8 @@ let SharedLog = class SharedLog extends Program {
393
421
  factor: 1,
394
422
  offset: await this.domain.fromEntry(entry),
395
423
  normalized: false,
424
+ strict: true,
396
425
  });
397
- syncStatus = SyncStatus.Synced; /// we already have the entries
398
426
  }
399
427
  else {
400
428
  ranges.push(entry);
@@ -405,18 +433,34 @@ let SharedLog = class SharedLog extends Program {
405
433
  else {
406
434
  range = rangeOrEntry ?? true;
407
435
  }
408
- return this._replicate(range, { ...options, syncStatus });
436
+ return this._replicate(range, options);
409
437
  }
410
438
  async unreplicate(rangeOrEntry) {
411
- let range;
439
+ let segmentIds;
412
440
  if (rangeOrEntry instanceof Entry) {
413
- range = {
441
+ let range = {
414
442
  factor: 1,
415
443
  offset: await this.domain.fromEntry(rangeOrEntry),
416
444
  };
445
+ const indexed = this.replicationIndex.iterate({
446
+ query: {
447
+ width: 1,
448
+ start1: range.offset /* ,
449
+ hash: this.node.identity.publicKey.hashcode(), */,
450
+ },
451
+ });
452
+ segmentIds = (await indexed.all()).map((x) => x.id.key);
453
+ if (segmentIds.length === 0) {
454
+ logger.warn("No segment found to unreplicate");
455
+ return;
456
+ }
417
457
  }
418
- else if (rangeOrEntry instanceof ReplicationRangeMessage) {
419
- range = rangeOrEntry;
458
+ else if (Array.isArray(rangeOrEntry)) {
459
+ segmentIds = rangeOrEntry.map((x) => x.id);
460
+ if (segmentIds.length === 0) {
461
+ logger.warn("No segment found to unreplicate");
462
+ return;
463
+ }
420
464
  }
421
465
  else {
422
466
  this._isReplicating = false;
@@ -429,14 +473,8 @@ let SharedLog = class SharedLog extends Program {
429
473
  // TODO support this by never deleting the range with the segment id that is generated by the dynamic replication method
430
474
  throw new Error("Unsupported when adaptive replicating");
431
475
  }
432
- const indexed = this.replicationIndex.iterate({
433
- query: {
434
- width: 1,
435
- start1: range.offset,
436
- },
437
- });
438
- const segmentIds = (await indexed.all()).map((x) => x.id.key);
439
- await this.removeReplicationRange(segmentIds, this.node.identity.publicKey);
476
+ const rangesToRemove = await this.resolveReplicationRangesFromIdsAndKey(segmentIds, this.node.identity.publicKey);
477
+ await this.removeReplicationRanges(rangesToRemove, this.node.identity.publicKey);
440
478
  await this.rpc.send(new StoppedReplicating({ segmentIds }), {
441
479
  priority: 1,
442
480
  });
@@ -468,10 +506,12 @@ let SharedLog = class SharedLog extends Program {
468
506
  throw new Error("Key was not a PublicSignKey");
469
507
  }
470
508
  }
509
+ const timestamp = BigInt(+new Date());
471
510
  for (const x of deleted) {
472
511
  this.replicationChangeDebounceFn.add({
473
512
  range: x.value,
474
513
  type: "removed",
514
+ timestamp,
475
515
  });
476
516
  }
477
517
  const pendingMaturity = this.pendingMaturity.get(keyHash);
@@ -496,7 +536,7 @@ let SharedLog = class SharedLog extends Program {
496
536
  ? Number(oldestTimestampFromDB)
497
537
  : +new Date();
498
538
  }
499
- async removeReplicationRange(ids, from) {
539
+ async resolveReplicationRangesFromIdsAndKey(ids, from) {
500
540
  let idMatcher = new Or(ids.map((x) => new ByteMatchQuery({ key: "id", value: x })));
501
541
  // make sure we are not removing something that is owned by the replicator
502
542
  let identityMatcher = new StringMatch({
@@ -504,9 +544,12 @@ let SharedLog = class SharedLog extends Program {
504
544
  value: from.hashcode(),
505
545
  });
506
546
  let query = new And([idMatcher, identityMatcher]);
547
+ return (await this.replicationIndex.iterate({ query }).all()).map((x) => x.value);
548
+ }
549
+ async removeReplicationRanges(ranges, from) {
507
550
  const pendingMaturity = this.pendingMaturity.get(from.hashcode());
508
551
  if (pendingMaturity) {
509
- for (const id of ids) {
552
+ for (const id of ranges) {
510
553
  const info = pendingMaturity.get(id.toString());
511
554
  if (info) {
512
555
  clearTimeout(info.timeout);
@@ -517,7 +560,9 @@ let SharedLog = class SharedLog extends Program {
517
560
  this.pendingMaturity.delete(from.hashcode());
518
561
  }
519
562
  }
520
- await this.replicationIndex.del({ query });
563
+ await this.replicationIndex.del({
564
+ query: new Or(ranges.map((x) => new ByteMatchQuery({ key: "id", value: x.id }))),
565
+ });
521
566
  const otherSegmentsIterator = this.replicationIndex.iterate({ query: { hash: from.hashcode() } }, { shape: { id: true } });
522
567
  if ((await otherSegmentsIterator.next(1)).length === 0) {
523
568
  this.uniqueReplicators.delete(from.hashcode());
@@ -531,11 +576,12 @@ let SharedLog = class SharedLog extends Program {
531
576
  this.rebalanceParticipationDebounced?.();
532
577
  }
533
578
  }
534
- async addReplicationRange(ranges, from, { reset, checkDuplicates, } = {}) {
579
+ async addReplicationRange(ranges, from, { reset, checkDuplicates, timestamp: ts, } = {}) {
535
580
  if (this._isTrustedReplicator && !(await this._isTrustedReplicator(from))) {
536
581
  return undefined;
537
582
  }
538
583
  let isNewReplicator = false;
584
+ let timestamp = BigInt(ts ?? +new Date());
539
585
  let diffs;
540
586
  let deleted = undefined;
541
587
  if (reset) {
@@ -548,10 +594,10 @@ let SharedLog = class SharedLog extends Program {
548
594
  await this.replicationIndex.del({ query: { hash: from.hashcode() } });
549
595
  diffs = [
550
596
  ...deleted.map((x) => {
551
- return { range: x, type: "removed" };
597
+ return { range: x, type: "removed", timestamp };
552
598
  }),
553
599
  ...ranges.map((x) => {
554
- return { range: x, type: "added" };
600
+ return { range: x, type: "added", timestamp };
555
601
  }),
556
602
  ];
557
603
  isNewReplicator = prevCount === 0 && ranges.length > 0;
@@ -575,7 +621,7 @@ let SharedLog = class SharedLog extends Program {
575
621
  let deduplicated = [];
576
622
  // TODO also deduplicate/de-overlap among the ranges that ought to be inserted?
577
623
  for (const range of ranges) {
578
- if (!(await iHaveCoveringRange(this.replicationIndex, range))) {
624
+ if (!(await countCoveringRangesSameOwner(this.replicationIndex, range))) {
579
625
  deduplicated.push(range);
580
626
  }
581
627
  }
@@ -590,15 +636,27 @@ let SharedLog = class SharedLog extends Program {
590
636
  const prev = existingMap.get(x.idString);
591
637
  if (prev) {
592
638
  if (prev.equalRange(x)) {
593
- return undefined;
639
+ return [];
594
640
  }
595
- return { range: x, prev, type: "updated" };
641
+ return [
642
+ {
643
+ range: prev,
644
+ timestamp: x.timestamp - 1n,
645
+ prev,
646
+ type: "replaced",
647
+ },
648
+ {
649
+ range: x,
650
+ timestamp: x.timestamp,
651
+ type: "added",
652
+ },
653
+ ];
596
654
  }
597
655
  else {
598
- return { range: x, type: "added" };
656
+ return { range: x, timestamp: x.timestamp, type: "added" };
599
657
  }
600
658
  })
601
- .filter((x) => x != null);
659
+ .flat();
602
660
  diffs = changes;
603
661
  }
604
662
  this.uniqueReplicators.add(from.hashcode());
@@ -606,7 +664,7 @@ let SharedLog = class SharedLog extends Program {
606
664
  let minRoleAge = await this.getDefaultMinRoleAge();
607
665
  let isAllMature = true;
608
666
  for (const diff of diffs) {
609
- if (diff.type === "added" || diff.type === "updated") {
667
+ if (diff.type === "added") {
610
668
  /* if (this.closed) {
611
669
  return;
612
670
  } */
@@ -628,7 +686,7 @@ let SharedLog = class SharedLog extends Program {
628
686
  this.events.dispatchEvent(new CustomEvent("replicator:mature", {
629
687
  detail: { publicKey: from },
630
688
  }));
631
- this.replicationChangeDebounceFn.add(diff); // 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!
689
+ 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!
632
690
  pendingRanges.delete(diff.range.idString);
633
691
  if (pendingRanges.size === 0) {
634
692
  this.pendingMaturity.delete(diff.range.hash);
@@ -648,7 +706,7 @@ let SharedLog = class SharedLog extends Program {
648
706
  }
649
707
  }
650
708
  }
651
- else {
709
+ else if (diff.type === "removed") {
652
710
  const pendingFromPeer = this.pendingMaturity.get(diff.range.hash);
653
711
  if (pendingFromPeer) {
654
712
  const prev = pendingFromPeer.get(diff.range.idString);
@@ -661,6 +719,7 @@ let SharedLog = class SharedLog extends Program {
661
719
  }
662
720
  }
663
721
  }
722
+ // else replaced, do nothing
664
723
  }
665
724
  if (reset) {
666
725
  await this.updateOldestTimestampFromIndex();
@@ -850,14 +909,15 @@ let SharedLog = class SharedLog extends Program {
850
909
  this._logProperties = options;
851
910
  // TODO types
852
911
  this.domain = options?.domain
853
- ? options.domain
854
- : createReplicationDomainHash(options?.compatibility && options?.compatibility < 10 ? "u32" : "u64");
912
+ ? options.domain(this)
913
+ : createReplicationDomainHash(options?.compatibility && options?.compatibility < 10 ? "u32" : "u64")(this);
855
914
  this.indexableDomain = createIndexableDomainFromResolution(this.domain.resolution);
856
915
  this._respondToIHaveTimeout = options?.respondToIHaveTimeout ?? 2e4;
857
916
  this._pendingDeletes = new Map();
858
917
  this._pendingIHave = new Map();
859
918
  this.latestReplicationInfoMessage = new Map();
860
919
  this.coordinateToHash = new Cache({ max: 1e6, ttl: 1e4 });
920
+ this.recentlyRebalanced = new Cache({ max: 1e4, ttl: 1e5 });
861
921
  this.uniqueReplicators = new Set();
862
922
  this.openTime = +new Date();
863
923
  this.oldestOpenTime = this.openTime;
@@ -1151,12 +1211,13 @@ let SharedLog = class SharedLog extends Program {
1151
1211
  async getCover(args, options) {
1152
1212
  let roleAge = options?.roleAge ?? (await this.getDefaultMinRoleAge());
1153
1213
  let eager = options?.eager ?? false;
1154
- const range = await this.domain.fromArgs(args, this);
1214
+ const range = await this.domain.fromArgs(args);
1215
+ const width = range.length ??
1216
+ (await minimumWidthToCover(this.replicas.min.getValue(this), this.indexableDomain.numbers));
1155
1217
  const set = await getCoverSet({
1156
1218
  peers: this.replicationIndex,
1157
1219
  start: range.offset,
1158
- widthToCoverScaled: range.length ??
1159
- (await minimumWidthToCover(this.replicas.min.getValue(this), this.indexableDomain.numbers)),
1220
+ widthToCoverScaled: width,
1160
1221
  roleAge,
1161
1222
  eager,
1162
1223
  numbers: this.indexableDomain.numbers,
@@ -1177,6 +1238,7 @@ let SharedLog = class SharedLog extends Program {
1177
1238
  this.pendingMaturity.clear();
1178
1239
  this.distributeQueue?.clear();
1179
1240
  this.coordinateToHash.clear();
1241
+ this.recentlyRebalanced.clear();
1180
1242
  this.uniqueReplicators.clear();
1181
1243
  this._closeController.abort();
1182
1244
  clearInterval(this.interval);
@@ -1586,7 +1648,11 @@ let SharedLog = class SharedLog extends Program {
1586
1648
  if (this.closed) {
1587
1649
  return;
1588
1650
  }
1589
- await this.addReplicationRange(replicationInfoMessage.segments.map((x) => x.toReplicationRangeIndexable(context.from)), context.from, { reset, checkDuplicates: true });
1651
+ await this.addReplicationRange(replicationInfoMessage.segments.map((x) => x.toReplicationRangeIndexable(context.from)), context.from, {
1652
+ reset,
1653
+ checkDuplicates: true,
1654
+ timestamp: Number(context.timestamp),
1655
+ });
1590
1656
  /* await this._modifyReplicators(msg.role, context.from!); */
1591
1657
  })
1592
1658
  .catch((e) => {
@@ -1607,7 +1673,16 @@ let SharedLog = class SharedLog extends Program {
1607
1673
  if (context.from.equals(this.node.identity.publicKey)) {
1608
1674
  return;
1609
1675
  }
1610
- await this.removeReplicationRange(msg.segmentIds, context.from);
1676
+ const rangesToRemove = await this.resolveReplicationRangesFromIdsAndKey(msg.segmentIds, context.from);
1677
+ await this.removeReplicationRanges(rangesToRemove, context.from);
1678
+ const timestamp = BigInt(+new Date());
1679
+ for (const range of rangesToRemove) {
1680
+ this.replicationChangeDebounceFn.add({
1681
+ range,
1682
+ type: "removed",
1683
+ timestamp,
1684
+ });
1685
+ }
1611
1686
  }
1612
1687
  else {
1613
1688
  throw new Error("Unexpected message");
@@ -1767,7 +1842,13 @@ let SharedLog = class SharedLog extends Program {
1767
1842
  : undefined;
1768
1843
  const persistCoordinate = async (entry) => {
1769
1844
  const minReplicas = decodeReplicas(entry).getValue(this);
1770
- await this.findLeaders(await this.createCoordinates(entry, minReplicas), entry, { persist: {} });
1845
+ const leaders = await this.findLeaders(await this.createCoordinates(entry, minReplicas), entry, { persist: {} });
1846
+ if (options?.replicate &&
1847
+ typeof options.replicate !== "boolean" &&
1848
+ options.replicate.assumeSynced) {
1849
+ // make sure we dont start to initate syncing process outwards for this entry
1850
+ this.addPeersToGidPeerHistory(entry.meta.gid, leaders.keys());
1851
+ }
1771
1852
  };
1772
1853
  let entriesToPersist = [];
1773
1854
  let joinOptions = {
@@ -2376,8 +2457,9 @@ let SharedLog = class SharedLog extends Program {
2376
2457
  if (options?.clearCache) {
2377
2458
  this._gidPeersHistory.clear();
2378
2459
  }
2460
+ const timestamp = BigInt(+new Date());
2379
2461
  this.onReplicationChange((await this.getAllReplicationSegments()).map((x) => {
2380
- return { range: x, type: "added" };
2462
+ return { range: x, type: "added", timestamp };
2381
2463
  }));
2382
2464
  }
2383
2465
  async waitForPruned() {
@@ -2392,11 +2474,10 @@ let SharedLog = class SharedLog extends Program {
2392
2474
  return;
2393
2475
  }
2394
2476
  await this.log.trim();
2395
- const change = mergeReplicationChanges(changeOrChanges);
2396
2477
  const changed = false;
2397
2478
  try {
2398
2479
  const uncheckedDeliver = new Map();
2399
- for await (const entryReplicated of toRebalance(change, this.entryCoordinatesIndex)) {
2480
+ for await (const entryReplicated of toRebalance(changeOrChanges, this.entryCoordinatesIndex, this.recentlyRebalanced)) {
2400
2481
  if (this.closed) {
2401
2482
  break;
2402
2483
  }
@@ -2505,7 +2586,7 @@ let SharedLog = class SharedLog extends Program {
2505
2586
  // TODO can not reuse old range, since it will (potentially) affect the index because of sideeffects
2506
2587
  dynamicRange = new this.indexableDomain.constructorRange({
2507
2588
  offset: dynamicRange.start1,
2508
- length: this.indexableDomain.numbers.denormalize(newFactor),
2589
+ width: this.indexableDomain.numbers.denormalize(newFactor),
2509
2590
  publicKeyHash: dynamicRange.hash,
2510
2591
  id: dynamicRange.id,
2511
2592
  mode: dynamicRange.mode,
@@ -2560,7 +2641,7 @@ let SharedLog = class SharedLog extends Program {
2560
2641
  if (!range) {
2561
2642
  range = new this.indexableDomain.constructorRange({
2562
2643
  offset: this.getDynamicRangeOffset(),
2563
- length: this.indexableDomain.numbers.zero,
2644
+ width: this.indexableDomain.numbers.zero,
2564
2645
  publicKeyHash: this.node.identity.publicKey.hashcode(),
2565
2646
  mode: ReplicationIntent.NonStrict,
2566
2647
  timestamp: BigInt(+new Date()),