@peerbit/shared-log 16.0.2 → 16.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peerbit/shared-log",
3
- "version": "16.0.2",
3
+ "version": "16.0.3",
4
4
  "description": "Shared log",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -62,23 +62,23 @@
62
62
  "pidusage": "^4.0.1",
63
63
  "pino": "^9.4.0",
64
64
  "uint8arrays": "^5.1.0",
65
+ "@peerbit/any-store": "2.2.14",
65
66
  "@peerbit/blocks": "4.2.10",
66
- "@peerbit/cache": "3.1.1",
67
67
  "@peerbit/blocks-interface": "2.1.5",
68
+ "@peerbit/cache": "3.1.1",
68
69
  "@peerbit/crypto": "3.1.5",
69
- "@peerbit/any-store": "2.2.14",
70
70
  "@peerbit/indexer-interface": "3.0.9",
71
+ "@peerbit/logger": "2.0.1",
71
72
  "@peerbit/indexer-sqlite3": "3.0.14",
72
- "@peerbit/log": "6.2.17",
73
73
  "@peerbit/diagnostics": "0.0.1",
74
- "@peerbit/logger": "2.0.1",
74
+ "@peerbit/log": "6.2.17",
75
75
  "@peerbit/pubsub": "5.4.1",
76
76
  "@peerbit/program": "6.0.49",
77
+ "@peerbit/pubsub-interface": "5.2.1",
77
78
  "@peerbit/riblt": "1.2.0",
78
79
  "@peerbit/stream-interface": "6.0.15",
79
80
  "@peerbit/rpc": "6.1.17",
80
- "@peerbit/time": "3.0.1",
81
- "@peerbit/pubsub-interface": "5.2.1"
81
+ "@peerbit/time": "3.0.1"
82
82
  },
83
83
  "optionalDependencies": {
84
84
  "@peerbit/native-backbone": "0.2.5",
@@ -88,9 +88,9 @@
88
88
  "@types/libsodium-wrappers": "^0.7.14",
89
89
  "@types/pidusage": "^2.0.5",
90
90
  "uuid": "^11.1.1",
91
+ "@peerbit/any-store-rust": "0.1.4",
91
92
  "@peerbit/indexer-rust": "1.0.7",
92
93
  "peerbit": "5.3.23",
93
- "@peerbit/any-store-rust": "0.1.4",
94
94
  "@peerbit/test-utils": "3.1.23"
95
95
  },
96
96
  "repository": {
@@ -207,7 +207,20 @@ export class CoordinatePersistenceCoordinator<R extends "u32" | "u64"> {
207
207
  // roll back only while that generation is still current, so a rollback
208
208
  // superseded by a newer mutation is a strict no-op. The map deliberately
209
209
  // survives open/close cycles of the same instance.
210
- _nativeCoordinateMutationGenerations?: Map<string, number>;
210
+ //
211
+ // Each row is hold-counted: `snapshotResidentCoordinateEntries` takes one
212
+ // hold per hash per token, and `settleResidentCoordinateSnapshot` releases
213
+ // it once the token can no longer be rolled back, deleting the row at zero
214
+ // holds. That bounds the map by the outstanding rollback tokens instead of
215
+ // by lifetime throughput. Deleting a row that still has a live token would
216
+ // make its rollback fail open (or, worse, ABA-clobber newer state), so
217
+ // eviction is refcount-driven only — never TTL, LRU or a size cap. A
218
+ // MISSED settle merely retains a row, which is the pre-refcount behavior
219
+ // and therefore never a regression.
220
+ _nativeCoordinateMutationGenerations?: Map<
221
+ string,
222
+ { generation: number; holds: number }
223
+ >;
211
224
 
212
225
  /**
213
226
  * Wall-clock watermark of the last settled native coordinate journal
@@ -567,6 +580,11 @@ export class CoordinatePersistenceCoordinator<R extends "u32" | "u64"> {
567
580
  } catch (error) {
568
581
  this.rollbackBackboneOnlyReceiveCoordinateBatch(batch);
569
582
  throw error;
583
+ } finally {
584
+ // The token cannot escape this function: both outcomes are
585
+ // observed here, and the catch arm has already consumed it by the
586
+ // time this runs.
587
+ this.settleResidentCoordinateSnapshot(batch.rollbackCoordinateEntries);
570
588
  }
571
589
  }
572
590
 
@@ -1012,8 +1030,14 @@ export class CoordinatePersistenceCoordinator<R extends "u32" | "u64"> {
1012
1030
  const mutationGenerations = (this._nativeCoordinateMutationGenerations ??=
1013
1031
  new Map());
1014
1032
  for (const hash of uniqueHashes) {
1015
- const generation = (mutationGenerations.get(hash) ?? 0) + 1;
1016
- mutationGenerations.set(hash, generation);
1033
+ // `uniqueHashes` is a Set, so this is exactly one hold per token
1034
+ // per hash; `settleResidentCoordinateSnapshot` releases it.
1035
+ const row = mutationGenerations.get(hash);
1036
+ const generation = (row?.generation ?? 0) + 1;
1037
+ mutationGenerations.set(hash, {
1038
+ generation,
1039
+ holds: (row?.holds ?? 0) + 1,
1040
+ });
1017
1041
  generations.set(hash, generation);
1018
1042
  const entry = this._residentEntryCoordinatesByHash?.get(hash);
1019
1043
  if (entry) {
@@ -1023,6 +1047,43 @@ export class CoordinatePersistenceCoordinator<R extends "u32" | "u64"> {
1023
1047
  return { hashes: uniqueHashes, entries, generations };
1024
1048
  }
1025
1049
 
1050
+ /**
1051
+ * Release the holds a rollback token took on the mutation-generation map.
1052
+ *
1053
+ * Call this only where the token is TERMINAL — after the last point at
1054
+ * which any code path could still roll it back. A premature settle deletes
1055
+ * a row that a live token still needs, which silently turns that token's
1056
+ * rollback into a no-op (phantom coordinates) or, if the hash cycles back
1057
+ * to the same generation, lets it clobber newer state. A missed settle
1058
+ * only retains the row, which is the pre-refcount behavior.
1059
+ *
1060
+ * Idempotent by design: `settled` is set FIRST so a second settle of the
1061
+ * same token cannot consume another token's hold on a shared hash.
1062
+ */
1063
+ settleResidentCoordinateSnapshot(
1064
+ rollback?: NativeBackboneCoordinateRollback<R>,
1065
+ ): void {
1066
+ if (!rollback || rollback.settled) {
1067
+ return;
1068
+ }
1069
+ rollback.settled = true;
1070
+ const mutationGenerations = this._nativeCoordinateMutationGenerations;
1071
+ if (!mutationGenerations) {
1072
+ return;
1073
+ }
1074
+ for (const hash of rollback.hashes) {
1075
+ const row = mutationGenerations.get(hash);
1076
+ if (!row) {
1077
+ continue;
1078
+ }
1079
+ if (row.holds <= 1) {
1080
+ mutationGenerations.delete(hash);
1081
+ } else {
1082
+ row.holds -= 1;
1083
+ }
1084
+ }
1085
+ }
1086
+
1026
1087
  rollbackNativeBackboneCoordinateAppend(
1027
1088
  appendHash: string,
1028
1089
  rollback?: NativeBackboneCoordinateRollback<R>,
@@ -1038,7 +1099,7 @@ export class CoordinatePersistenceCoordinator<R extends "u32" | "u64"> {
1038
1099
  const expectedGeneration = rollback?.generations.get(hash);
1039
1100
  if (
1040
1101
  expectedGeneration !== undefined &&
1041
- mutationGenerations.get(hash) !== expectedGeneration
1102
+ mutationGenerations.get(hash)?.generation !== expectedGeneration
1042
1103
  ) {
1043
1104
  continue;
1044
1105
  }
@@ -1096,7 +1157,7 @@ export class CoordinatePersistenceCoordinator<R extends "u32" | "u64"> {
1096
1157
  const expectedGeneration = rollback?.generations.get(hash);
1097
1158
  if (
1098
1159
  expectedGeneration !== undefined &&
1099
- mutationGenerations.get(hash) !== expectedGeneration
1160
+ mutationGenerations.get(hash)?.generation !== expectedGeneration
1100
1161
  ) {
1101
1162
  continue;
1102
1163
  }