@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/benchmark/get-samples.js +1 -1
- package/dist/benchmark/get-samples.js.map +1 -1
- package/dist/benchmark/utils.js +1 -1
- package/dist/benchmark/utils.js.map +1 -1
- package/dist/src/index.d.ts +16 -11
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +146 -65
- package/dist/src/index.js.map +1 -1
- package/dist/src/ranges.d.ts +95 -11
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +440 -163
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/replication-domain-hash.d.ts +2 -2
- package/dist/src/replication-domain-hash.d.ts.map +1 -1
- package/dist/src/replication-domain-hash.js +2 -17
- package/dist/src/replication-domain-hash.js.map +1 -1
- package/dist/src/replication-domain-time.d.ts +7 -2
- package/dist/src/replication-domain-time.d.ts.map +1 -1
- package/dist/src/replication-domain-time.js +7 -12
- package/dist/src/replication-domain-time.js.map +1 -1
- package/dist/src/replication-domain.d.ts +3 -20
- package/dist/src/replication-domain.d.ts.map +1 -1
- package/dist/src/replication-domain.js +0 -33
- package/dist/src/replication-domain.js.map +1 -1
- package/package.json +70 -70
- package/src/index.ts +215 -108
- package/src/ranges.ts +652 -202
- package/src/replication-domain-hash.ts +16 -29
- package/src/replication-domain-time.ts +46 -40
- package/src/replication-domain.ts +7 -59
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,
|
|
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 {
|
|
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,
|
|
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
|
|
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
|
-
|
|
266
|
+
rangesToReplicate = [maybeRange];
|
|
258
267
|
offsetWasProvided = true;
|
|
259
268
|
}
|
|
260
269
|
else if (isReplicationRangeMessage(options)) {
|
|
261
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
330
|
-
|
|
331
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
436
|
+
return this._replicate(range, options);
|
|
409
437
|
}
|
|
410
438
|
async unreplicate(rangeOrEntry) {
|
|
411
|
-
let
|
|
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
|
|
419
|
-
|
|
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
|
|
433
|
-
|
|
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
|
|
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
|
|
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({
|
|
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
|
|
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
|
|
639
|
+
return [];
|
|
594
640
|
}
|
|
595
|
-
return
|
|
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
|
-
.
|
|
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"
|
|
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
|
|
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:
|
|
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, {
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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()),
|