@peerbit/shared-log 13.2.24 → 13.2.26

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.
Files changed (81) hide show
  1. package/dist/src/checked-prune.d.ts +12 -0
  2. package/dist/src/checked-prune.d.ts.map +1 -1
  3. package/dist/src/checked-prune.js +33 -0
  4. package/dist/src/checked-prune.js.map +1 -1
  5. package/dist/src/coordinate-persistence.d.ts +211 -0
  6. package/dist/src/coordinate-persistence.d.ts.map +1 -0
  7. package/dist/src/coordinate-persistence.js +1133 -0
  8. package/dist/src/coordinate-persistence.js.map +1 -0
  9. package/dist/src/errors.d.ts +1 -0
  10. package/dist/src/errors.d.ts.map +1 -1
  11. package/dist/src/errors.js +19 -0
  12. package/dist/src/errors.js.map +1 -1
  13. package/dist/src/index.d.ts +127 -133
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +1400 -4269
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/instance-lifecycle.d.ts +117 -0
  18. package/dist/src/instance-lifecycle.d.ts.map +1 -0
  19. package/dist/src/instance-lifecycle.js +254 -0
  20. package/dist/src/instance-lifecycle.js.map +1 -0
  21. package/dist/src/join-warmup.d.ts +86 -0
  22. package/dist/src/join-warmup.d.ts.map +1 -0
  23. package/dist/src/join-warmup.js +351 -0
  24. package/dist/src/join-warmup.js.map +1 -0
  25. package/dist/src/native-write-through-block-store.d.ts +123 -0
  26. package/dist/src/native-write-through-block-store.d.ts.map +1 -0
  27. package/dist/src/native-write-through-block-store.js +989 -0
  28. package/dist/src/native-write-through-block-store.js.map +1 -0
  29. package/dist/src/peer-session.d.ts +117 -0
  30. package/dist/src/peer-session.d.ts.map +1 -0
  31. package/dist/src/peer-session.js +259 -0
  32. package/dist/src/peer-session.js.map +1 -0
  33. package/dist/src/replication-announcement.d.ts +116 -0
  34. package/dist/src/replication-announcement.d.ts.map +1 -0
  35. package/dist/src/replication-announcement.js +515 -0
  36. package/dist/src/replication-announcement.js.map +1 -0
  37. package/dist/src/replicator-liveness.d.ts +59 -0
  38. package/dist/src/replicator-liveness.d.ts.map +1 -0
  39. package/dist/src/replicator-liveness.js +304 -0
  40. package/dist/src/replicator-liveness.js.map +1 -0
  41. package/dist/src/sync/dispatch-lifecycle.d.ts +48 -0
  42. package/dist/src/sync/dispatch-lifecycle.d.ts.map +1 -0
  43. package/dist/src/sync/dispatch-lifecycle.js +141 -0
  44. package/dist/src/sync/dispatch-lifecycle.js.map +1 -0
  45. package/dist/src/sync/factory.d.ts +29 -0
  46. package/dist/src/sync/factory.d.ts.map +1 -0
  47. package/dist/src/sync/factory.js +87 -0
  48. package/dist/src/sync/factory.js.map +1 -0
  49. package/dist/src/sync/pending-sync-store.d.ts +105 -0
  50. package/dist/src/sync/pending-sync-store.d.ts.map +1 -0
  51. package/dist/src/sync/pending-sync-store.js +877 -0
  52. package/dist/src/sync/pending-sync-store.js.map +1 -0
  53. package/dist/src/sync/rateless-iblt.d.ts +4 -1
  54. package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
  55. package/dist/src/sync/rateless-iblt.js +102 -106
  56. package/dist/src/sync/rateless-iblt.js.map +1 -1
  57. package/dist/src/sync/simple.d.ts +10 -41
  58. package/dist/src/sync/simple.d.ts.map +1 -1
  59. package/dist/src/sync/simple.js +259 -952
  60. package/dist/src/sync/simple.js.map +1 -1
  61. package/dist/src/sync/sync-peer-state.d.ts +16 -0
  62. package/dist/src/sync/sync-peer-state.d.ts.map +1 -0
  63. package/dist/src/sync/sync-peer-state.js +82 -0
  64. package/dist/src/sync/sync-peer-state.js.map +1 -0
  65. package/package.json +12 -12
  66. package/src/checked-prune.ts +35 -0
  67. package/src/coordinate-persistence.ts +1796 -0
  68. package/src/errors.ts +21 -0
  69. package/src/index.ts +4014 -7858
  70. package/src/instance-lifecycle.ts +351 -0
  71. package/src/join-warmup.ts +507 -0
  72. package/src/native-write-through-block-store.ts +1170 -0
  73. package/src/peer-session.ts +336 -0
  74. package/src/replication-announcement.ts +744 -0
  75. package/src/replicator-liveness.ts +439 -0
  76. package/src/sync/dispatch-lifecycle.ts +230 -0
  77. package/src/sync/factory.ts +154 -0
  78. package/src/sync/pending-sync-store.ts +1085 -0
  79. package/src/sync/rateless-iblt.ts +122 -132
  80. package/src/sync/simple.ts +334 -1144
  81. package/src/sync/sync-peer-state.ts +107 -0
@@ -25,6 +25,11 @@ import { SilentDelivery } from "@peerbit/stream-interface";
25
25
  import { type EntryWithRefs } from "../exchange-heads.js";
26
26
  import { TransportMessage } from "../message.js";
27
27
  import { type EntryReplicated } from "../ranges.js";
28
+ import {
29
+ DispatchLifecycleRegistry,
30
+ isOwnershipGenerationActive,
31
+ isTrackedSessionActive,
32
+ } from "./dispatch-lifecycle.js";
28
33
  import type {
29
34
  HashSymbolRangeResolver,
30
35
  RepairSession,
@@ -47,6 +52,10 @@ import {
47
52
  SYNC_MESSAGE_PRIORITY,
48
53
  SimpleSyncronizer,
49
54
  } from "./simple.js";
55
+ import {
56
+ SyncPeerSlotRegistry,
57
+ type SyncPeerSlotRow,
58
+ } from "./sync-peer-state.js";
50
59
 
51
60
  export const logger = loggerFn("peerbit:shared-log:rateless");
52
61
 
@@ -847,12 +856,19 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
847
856
  outgoingSyncProcesses: Map<string, OutgoingRatelessSyncProcess>;
848
857
  private outgoingSyncProcessByTarget: Map<string, OutgoingRatelessSyncProcess>;
849
858
  private ratelessDispatchLifecycleController: AbortController;
850
- private ratelessDispatchTargets: Map<
851
- string,
852
- Set<RatelessDispatchTargetLifecycle>
859
+ private readonly ratelessDispatchRegistry: DispatchLifecycleRegistry<
860
+ RatelessDispatchLifecycle,
861
+ RatelessDispatchTargetLifecycle
853
862
  >;
863
+
854
864
  private activeRatelessResponseCount: number;
855
865
  private activeRatelessResponseCountByPeer: Map<string, number>;
866
+ // Per-peer slot rows for active rateless responses (shared registry; see
867
+ // sync-peer-state.ts for the lifetime policy). Release closures capture
868
+ // the row: disconnect returns current-generation quota and logical
869
+ // ownership immediately, while the global physical permit remains charged
870
+ // until the shipment actually settles.
871
+ private readonly ratelessPeerSlotRows: SyncPeerSlotRegistry;
856
872
  private ratelessClosed: boolean;
857
873
 
858
874
  constructor(readonly properties: SynchronizerComponents<D>) {
@@ -862,9 +878,27 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
862
878
  this.outgoingSyncProcesses = new Map();
863
879
  this.outgoingSyncProcessByTarget = new Map();
864
880
  this.ratelessDispatchLifecycleController = new AbortController();
865
- this.ratelessDispatchTargets = new Map();
881
+ this.ratelessDispatchRegistry = new DispatchLifecycleRegistry<
882
+ RatelessDispatchLifecycle,
883
+ RatelessDispatchTargetLifecycle
884
+ >({
885
+ isClosed: () => this.ratelessClosed,
886
+ currentOwnershipLifecycleController: () =>
887
+ this.ratelessDispatchLifecycleController,
888
+ hasRetainedWork: (lifecycle) =>
889
+ [...lifecycle.targets.values()].some(
890
+ (target) => target.retainedByProcess || target.responseLeases > 0,
891
+ ),
892
+ // Target-activity strategy: set membership in the active-target
893
+ // registry (rateless target lifecycles carry no epochs).
894
+ isTargetCurrent: (targetLifecycle) =>
895
+ this.ratelessDispatchRegistry.activeTargets
896
+ .get(targetLifecycle.target)
897
+ ?.has(targetLifecycle) === true,
898
+ });
866
899
  this.activeRatelessResponseCount = 0;
867
900
  this.activeRatelessResponseCountByPeer = new Map();
901
+ this.ratelessPeerSlotRows = new SyncPeerSlotRegistry();
868
902
  this.ratelessClosed = false;
869
903
  this.ingoingSyncProcesses = new Map();
870
904
  this.incomingRatelessProcessAdmissions = new Set();
@@ -917,15 +951,16 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
917
951
  private isRatelessRepairSessionActive(
918
952
  session: RatelessRepairSessionLifecycle,
919
953
  ): boolean {
920
- return (
921
- !this.ratelessClosed &&
922
- !session.cancelled &&
923
- !session.controller.signal.aborted &&
924
- !session.ownershipLifecycleController.signal.aborted &&
925
- session.ownershipLifecycleController ===
926
- this.ratelessDispatchLifecycleController &&
927
- this.ratelessRepairSessions.get(session.id) === session
928
- );
954
+ return isTrackedSessionActive({
955
+ closed: this.ratelessClosed,
956
+ cancelled: session.cancelled,
957
+ sessionController: session.controller,
958
+ ownershipLifecycleController: session.ownershipLifecycleController,
959
+ currentOwnershipLifecycleController:
960
+ this.ratelessDispatchLifecycleController,
961
+ session,
962
+ registered: this.ratelessRepairSessions.get(session.id),
963
+ });
929
964
  }
930
965
 
931
966
  private cancelRatelessRepairSession(
@@ -1409,42 +1444,17 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
1409
1444
  disposed: false,
1410
1445
  } satisfies RatelessDispatchLifecycle;
1411
1446
 
1412
- for (const target of [...new Set(targets)]) {
1413
- const targetLifecycle: RatelessDispatchTargetLifecycle = {
1414
- lifecycle,
1447
+ this.ratelessDispatchRegistry.register(
1448
+ lifecycle,
1449
+ targets,
1450
+ (forLifecycle, target) => ({
1451
+ lifecycle: forLifecycle,
1415
1452
  target,
1416
1453
  controller: new AbortController(),
1417
1454
  retainedByProcess: false,
1418
1455
  responseLeases: 0,
1419
- };
1420
- lifecycle.targets.set(target, targetLifecycle);
1421
- let activeForTarget = this.ratelessDispatchTargets.get(target);
1422
- if (!activeForTarget) {
1423
- activeForTarget = new Set();
1424
- this.ratelessDispatchTargets.set(target, activeForTarget);
1425
- }
1426
- activeForTarget.add(targetLifecycle);
1427
- }
1428
-
1429
- ownershipLifecycleController.signal.addEventListener(
1430
- "abort",
1431
- lifecycle.onOwnerOrCallerAbort,
1432
- { once: true },
1456
+ }),
1433
1457
  );
1434
- if (callerSignal && callerSignal !== ownershipLifecycleController.signal) {
1435
- callerSignal.addEventListener("abort", lifecycle.onOwnerOrCallerAbort, {
1436
- once: true,
1437
- });
1438
- }
1439
- if (
1440
- this.ratelessClosed ||
1441
- ownershipLifecycleController !==
1442
- this.ratelessDispatchLifecycleController ||
1443
- ownershipLifecycleController.signal.aborted ||
1444
- callerSignal?.aborted
1445
- ) {
1446
- lifecycle.onOwnerOrCallerAbort();
1447
- }
1448
1458
  return lifecycle;
1449
1459
  }
1450
1460
 
@@ -1452,93 +1462,33 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
1452
1462
  targetLifecycle: RatelessDispatchTargetLifecycle,
1453
1463
  reason?: unknown,
1454
1464
  ): void {
1455
- if (!targetLifecycle.controller.signal.aborted) {
1456
- targetLifecycle.controller.abort(reason);
1457
- }
1458
- this.maybeDisposeRatelessDispatchLifecycle(targetLifecycle.lifecycle);
1465
+ this.ratelessDispatchRegistry.abortTarget(targetLifecycle, reason);
1459
1466
  }
1460
1467
 
1461
1468
  private abortRatelessDispatchLifecycle(
1462
1469
  lifecycle: RatelessDispatchLifecycle,
1463
1470
  reason?: unknown,
1464
1471
  ): void {
1465
- if (!lifecycle.controller.signal.aborted) {
1466
- lifecycle.controller.abort(reason);
1467
- }
1468
- for (const targetLifecycle of lifecycle.targets.values()) {
1469
- this.abortRatelessDispatchTarget(targetLifecycle, reason);
1470
- }
1471
- this.maybeDisposeRatelessDispatchLifecycle(lifecycle);
1472
+ this.ratelessDispatchRegistry.abortLifecycle(lifecycle, reason);
1472
1473
  }
1473
1474
 
1474
1475
  private finishRatelessDispatchLifecycle(
1475
1476
  lifecycle: RatelessDispatchLifecycle,
1476
1477
  ): void {
1477
- lifecycle.dispatchFinished = true;
1478
- this.maybeDisposeRatelessDispatchLifecycle(lifecycle);
1478
+ this.ratelessDispatchRegistry.finish(lifecycle);
1479
1479
  }
1480
1480
 
1481
1481
  private maybeDisposeRatelessDispatchLifecycle(
1482
1482
  lifecycle: RatelessDispatchLifecycle,
1483
1483
  ): void {
1484
- if (
1485
- lifecycle.disposed ||
1486
- !lifecycle.dispatchFinished ||
1487
- [...lifecycle.targets.values()].some(
1488
- (target) => target.retainedByProcess || target.responseLeases > 0,
1489
- )
1490
- ) {
1491
- return;
1492
- }
1493
- lifecycle.disposed = true;
1494
- lifecycle.ownershipLifecycleController.signal.removeEventListener(
1495
- "abort",
1496
- lifecycle.onOwnerOrCallerAbort,
1497
- );
1498
- if (
1499
- lifecycle.callerSignal &&
1500
- lifecycle.callerSignal !== lifecycle.ownershipLifecycleController.signal
1501
- ) {
1502
- lifecycle.callerSignal.removeEventListener(
1503
- "abort",
1504
- lifecycle.onOwnerOrCallerAbort,
1505
- );
1506
- }
1507
- for (const targetLifecycle of lifecycle.targets.values()) {
1508
- const activeForTarget = this.ratelessDispatchTargets.get(
1509
- targetLifecycle.target,
1510
- );
1511
- activeForTarget?.delete(targetLifecycle);
1512
- if (activeForTarget?.size === 0) {
1513
- this.ratelessDispatchTargets.delete(targetLifecycle.target);
1514
- }
1515
- }
1484
+ this.ratelessDispatchRegistry.maybeDispose(lifecycle);
1516
1485
  }
1517
1486
 
1518
1487
  private isRatelessDispatchLifecycleActive(
1519
1488
  lifecycle: RatelessDispatchLifecycle,
1520
1489
  target?: string,
1521
1490
  ): boolean {
1522
- if (
1523
- this.ratelessClosed ||
1524
- lifecycle.disposed ||
1525
- lifecycle.ownershipLifecycleController !==
1526
- this.ratelessDispatchLifecycleController ||
1527
- lifecycle.ownershipLifecycleController.signal.aborted ||
1528
- lifecycle.callerSignal?.aborted ||
1529
- lifecycle.controller.signal.aborted
1530
- ) {
1531
- return false;
1532
- }
1533
- if (target === undefined) {
1534
- return true;
1535
- }
1536
- const targetLifecycle = lifecycle.targets.get(target);
1537
- return (
1538
- targetLifecycle !== undefined &&
1539
- !targetLifecycle.controller.signal.aborted &&
1540
- this.ratelessDispatchTargets.get(target)?.has(targetLifecycle) === true
1541
- );
1491
+ return this.ratelessDispatchRegistry.isLifecycleActive(lifecycle, target);
1542
1492
  }
1543
1493
 
1544
1494
  private getIncomingSyncProcessKey(sender: string, syncId: string): string {
@@ -1548,12 +1498,12 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
1548
1498
  private isIncomingSyncGenerationActive(
1549
1499
  ownershipLifecycleController: AbortController,
1550
1500
  ): boolean {
1551
- return (
1552
- !this.ratelessClosed &&
1553
- ownershipLifecycleController ===
1554
- this.ratelessDispatchLifecycleController &&
1555
- !ownershipLifecycleController.signal.aborted
1556
- );
1501
+ return isOwnershipGenerationActive({
1502
+ closed: this.ratelessClosed,
1503
+ ownershipLifecycleController,
1504
+ currentOwnershipLifecycleController:
1505
+ this.ratelessDispatchLifecycleController,
1506
+ });
1557
1507
  }
1558
1508
 
1559
1509
  private isIncomingSyncProcessActive(
@@ -2309,41 +2259,77 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
2309
2259
 
2310
2260
  const targetLifecycle = process.targetLifecycle;
2311
2261
  targetLifecycle.responseLeases += 1;
2262
+ const slotRow = this.getOrCreateRatelessResponseSlotRow(target);
2263
+ slotRow.active += 1;
2312
2264
  this.activeRatelessResponseCount += 1;
2313
2265
  this.activeRatelessResponseCountByPeer.set(
2314
2266
  target,
2315
2267
  (this.activeRatelessResponseCountByPeer.get(target) ?? 0) + 1,
2316
2268
  );
2317
- let released = false;
2269
+ let logicalReleased = false;
2270
+ let physicalReleased = false;
2271
+ const releaseLogical = () => {
2272
+ if (logicalReleased) {
2273
+ return;
2274
+ }
2275
+ logicalReleased = true;
2276
+ slotRow.activeReleases.delete(releaseLogical);
2277
+ targetLifecycle.responseLeases -= 1;
2278
+ this.maybeDisposeRatelessDispatchLifecycle(targetLifecycle.lifecycle);
2279
+ };
2280
+ slotRow.activeReleases.add(releaseLogical);
2318
2281
  return {
2319
2282
  process,
2320
2283
  authorized,
2321
2284
  remaining,
2322
2285
  signal: targetLifecycle.controller.signal,
2323
2286
  release: (options) => {
2324
- if (released) {
2287
+ if (physicalReleased) {
2325
2288
  return;
2326
2289
  }
2327
- released = true;
2328
- if (options?.rollback) {
2329
- for (const hash of authorized) {
2330
- process.consumedResponseHashes.delete(hash);
2290
+ physicalReleased = true;
2291
+ try {
2292
+ releaseLogical();
2293
+ } finally {
2294
+ if (options?.rollback) {
2295
+ for (const hash of authorized) {
2296
+ process.consumedResponseHashes.delete(hash);
2297
+ }
2298
+ }
2299
+ this.activeRatelessResponseCount -= 1;
2300
+ slotRow.active -= 1;
2301
+ if (slotRow.attached) {
2302
+ const activeForPeer =
2303
+ (this.activeRatelessResponseCountByPeer.get(target) ?? 1) - 1;
2304
+ if (activeForPeer === 0) {
2305
+ this.activeRatelessResponseCountByPeer.delete(target);
2306
+ } else {
2307
+ this.activeRatelessResponseCountByPeer.set(target, activeForPeer);
2308
+ }
2309
+ this.ratelessPeerSlotRows.maybeDropIdle(slotRow);
2331
2310
  }
2332
2311
  }
2333
- targetLifecycle.responseLeases -= 1;
2334
- this.activeRatelessResponseCount -= 1;
2335
- const activeForPeer =
2336
- (this.activeRatelessResponseCountByPeer.get(target) ?? 1) - 1;
2337
- if (activeForPeer === 0) {
2338
- this.activeRatelessResponseCountByPeer.delete(target);
2339
- } else {
2340
- this.activeRatelessResponseCountByPeer.set(target, activeForPeer);
2341
- }
2342
- this.maybeDisposeRatelessDispatchLifecycle(targetLifecycle.lifecycle);
2343
2312
  },
2344
2313
  };
2345
2314
  }
2346
2315
 
2316
+ private getOrCreateRatelessResponseSlotRow(peer: string): SyncPeerSlotRow {
2317
+ return this.ratelessPeerSlotRows.ensure(peer);
2318
+ }
2319
+
2320
+ private detachRatelessResponseSlotRow(peer: string): void {
2321
+ const row = this.ratelessPeerSlotRows.detach(peer);
2322
+ if (!row) {
2323
+ return;
2324
+ }
2325
+ this.activeRatelessResponseCountByPeer.delete(peer);
2326
+ // Release logical lifecycle ownership immediately. Each underlying
2327
+ // shipment retains its global physical-work permit until its own finally.
2328
+ for (const releaseLogical of [...row.activeReleases]) {
2329
+ releaseLogical();
2330
+ }
2331
+ }
2332
+
2347
2333
  async onMessage(
2348
2334
  message: TransportMessage,
2349
2335
  context: RequestContext,
@@ -3240,13 +3226,17 @@ export class RatelessIBLTSynchronizer<D extends "u32" | "u64">
3240
3226
  }
3241
3227
  }
3242
3228
  for (const targetLifecycle of [
3243
- ...(this.ratelessDispatchTargets.get(target) ?? []),
3229
+ ...(this.ratelessDispatchRegistry.activeTargets.get(target) ?? []),
3244
3230
  ]) {
3245
3231
  this.abortRatelessDispatchTarget(
3246
3232
  targetLifecycle,
3247
3233
  new Error("rateless sync target disconnected"),
3248
3234
  );
3249
3235
  }
3236
+ // A response ship for this peer may never settle. Detach current-generation
3237
+ // quota and logical ownership, but keep its global physical permit charged
3238
+ // until settlement. open() likewise preserves physical accounting.
3239
+ this.detachRatelessResponseSlotRow(target);
3250
3240
  return this.simple.onPeerDisconnected(target);
3251
3241
  }
3252
3242