@harperfast/rocksdb-js 2.10.0 → 2.11.0

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/index.mjs CHANGED
@@ -105,7 +105,17 @@ const NativeDatabase = binding.Database;
105
105
  const NativeIterator = binding.Iterator;
106
106
  const NativeTransaction = binding.Transaction;
107
107
  const TransactionLog = binding.TransactionLog;
108
- const registryStatus = binding.registryStatus;
108
+ /**
109
+ * Returns the status of every database currently held by the native registry.
110
+ * Walks every registered descriptor, so it samples live state rather than
111
+ * reading a stored property.
112
+ */
113
+ const getRegistryStatus = binding.registryStatus;
114
+ /**
115
+ * @deprecated Use {@link getRegistryStatus} instead. This alias will be removed
116
+ * in v3.0.0.
117
+ */
118
+ const registryStatus = getRegistryStatus;
109
119
  /**
110
120
  * Flushes every open database and waits for outstanding compactions, then
111
121
  * releases the registry.
@@ -169,6 +179,12 @@ const coolTransactionLogs = binding.coolTransactionLogs;
169
179
  */
170
180
  const transactionLogMapCount = binding.transactionLogMapCount;
171
181
  /**
182
+ * Number of coordinated-retry wake callbacks registered on verification-table
183
+ * locks across the process. Internal — used by tests to verify that a park
184
+ * ending by timeout, env teardown, or close leaves no registration behind.
185
+ */
186
+ const lockWakeCallbackCount = binding.lockWakeCallbackCount;
187
+ /**
172
188
  * Test-only: force the next `count` transaction commits to fail with TryAgain (rolled back, so
173
189
  * no data is committed), reproducing a stranded-snapshot conflict deterministically. Pass 0 to
174
190
  * disarm. Used by the ERR_TRY_AGAIN retry regression test.
@@ -1706,6 +1722,7 @@ var Store = class {
1706
1722
  /**
1707
1723
  * The target maximum size of a transaction log before rotation. Transactions
1708
1724
  * are never split across files, so one transaction may exceed this target.
1725
+ * Defaults to 16 MiB. `0` (formerly unlimited) is rejected at open.
1709
1726
  */
1710
1727
  transactionLogMaxSize;
1711
1728
  /**
@@ -1720,6 +1737,11 @@ var Store = class {
1720
1737
  */
1721
1738
  transactionLogsPath;
1722
1739
  /**
1740
+ * The transaction log whose batch keys this process originates; seeds the
1741
+ * monotonic timestamp floor at open. See `StoreOptions.timestampFloorLog`.
1742
+ */
1743
+ timestampFloorLog;
1744
+ /**
1723
1745
  * Whether this store's column family participates in the VerificationTable.
1724
1746
  */
1725
1747
  verificationTable;
@@ -1778,6 +1800,7 @@ var Store = class {
1778
1800
  this.transactionLogMaxSize = options?.transactionLogMaxSize;
1779
1801
  this.transactionLogRetention = options?.transactionLogRetention;
1780
1802
  this.transactionLogsPath = options?.transactionLogsPath;
1803
+ this.timestampFloorLog = options?.timestampFloorLog;
1781
1804
  this.verificationTable = options?.verificationTable;
1782
1805
  this.writeBufferSize = options?.writeBufferSize;
1783
1806
  this.writeKey = writeKey;
@@ -2142,6 +2165,7 @@ var Store = class {
2142
2165
  transactionLogMaxSize: this.transactionLogMaxSize,
2143
2166
  transactionLogRetentionMs: this.transactionLogRetention ? parseDuration(this.transactionLogRetention) : void 0,
2144
2167
  transactionLogsPath: this.transactionLogsPath,
2168
+ timestampFloorLog: this.timestampFloorLog,
2145
2169
  verificationTable: this.verificationTable,
2146
2170
  writeBufferSize: this.writeBufferSize
2147
2171
  });
@@ -2623,6 +2647,66 @@ var RocksDatabase = class RocksDatabase extends DBI {
2623
2647
  return this.store.db.getCompression();
2624
2648
  }
2625
2649
  /**
2650
+ * Dynamically changes the compression algorithm (and optional level) for
2651
+ * this database's column family on an already-open database — no close, no
2652
+ * reopen, and it does not conflict with another handle that already has
2653
+ * this column family open (unlike an explicit open-time `compression`
2654
+ * mismatch). That other handle's live options change too, silently,
2655
+ * without notice; a later explicit reopen of it is compared against the
2656
+ * new value, so a stale request it still holds can then conflict. Backed
2657
+ * by RocksDB's `DB::SetOptions()`, which documents both `compression` and
2658
+ * `blob_compression_type` as mutable column family options.
2659
+ *
2660
+ * This governs only *newly written* files (the next flush and any future
2661
+ * compaction output) going forward; SST and blob files already on disk
2662
+ * keep their existing compression until they are rewritten by a later
2663
+ * compaction. `compact()`'s default options can skip already-bottommost
2664
+ * files; use `compact({ bottommost: true })` to rewrite them under the new
2665
+ * codec. This is the live-mutation counterpart to the open-time
2666
+ * `compression` option — see the README's Compression section for when to
2667
+ * use each.
2668
+ *
2669
+ * @example
2670
+ * ```typescript
2671
+ * const db = RocksDatabase.open('/path/to/db', { compression: 'none' });
2672
+ * db.setCompression({ algorithm: 'zstd', level: 19 });
2673
+ * // new writes are now compressed with zstd; existing SSTs are untouched
2674
+ * // until compacted.
2675
+ * ```
2676
+ */
2677
+ setCompression(compression) {
2678
+ const { compression: algorithm, compressionLevel } = normalizeCompression(compression);
2679
+ if (algorithm === void 0) throw new TypeError("setCompression requires a compression algorithm");
2680
+ try {
2681
+ this.store.db.setCompression(algorithm, compressionLevel);
2682
+ } catch (error) {
2683
+ if (error?.appliedInMemoryOnly) this.syncStoreCompressionFromLive();
2684
+ throw error;
2685
+ }
2686
+ this.syncStoreCompressionFromLive();
2687
+ }
2688
+ /**
2689
+ * Resyncs the store's cached open-time compression option from the live
2690
+ * database state after `setCompression()`. Also clears
2691
+ * `compressionForAllColumnFamilies`: that flag means "apply this option to
2692
+ * every column family opened for this database", which no longer describes
2693
+ * a live change scoped to a single column family — leaving it set would
2694
+ * restamp every other family with this algorithm on the next
2695
+ * same-instance `close()` + `open()`.
2696
+ *
2697
+ * The flag clear is unconditional (a plain assignment, so it cannot itself
2698
+ * fail) and comes first: a concurrent close racing the live-state read
2699
+ * below throws "Database not open" from the getter, and that read failing
2700
+ * must not also skip the flag clear -- otherwise the same restamp this
2701
+ * method exists to prevent survives on the next open().
2702
+ */
2703
+ syncStoreCompressionFromLive() {
2704
+ this.store.compressionForAllColumnFamilies = false;
2705
+ try {
2706
+ this.store.compression = this.compression;
2707
+ } catch {}
2708
+ }
2709
+ /**
2626
2710
  * The informational-log settings currently in effect for this database,
2627
2711
  * read live from RocksDB, as `{ maxLogFileSize, infoLogLevel }`. These are
2628
2712
  * database-wide settings (not per-column-family): `maxLogFileSize` bounds
@@ -3626,18 +3710,11 @@ function findResyncPosition(dataView, from, dataEnd) {
3626
3710
  }
3627
3711
  }
3628
3712
  /**
3629
- * Builds the report for a broken frame, with the offset iteration should continue from. Resolves
3630
- * the written extent here rather than per frame: `getLogFileSize` crosses into native and takes
3631
- * the store mutex, which a healthy read must never pay.
3713
+ * Builds the report for a broken frame, with the offset iteration should continue from.
3632
3714
  */
3633
3715
  function corruptFrame(message, transactionLog, dataView, logBuffer, position, limit, size, readUncommitted) {
3634
3716
  let dataEnd = limit;
3635
- if (readUncommitted) try {
3636
- const writtenExtent = transactionLog.getLogFileSize(logBuffer.logId);
3637
- dataEnd = writtenExtent > 0 ? Math.min(logBuffer.length, writtenExtent) : 0;
3638
- } catch {
3639
- dataEnd = 0;
3640
- }
3717
+ if (readUncommitted) dataEnd = Math.min(logBuffer.length, logBuffer.size ?? readableExtent(logBuffer));
3641
3718
  const resyncPosition = findResyncPosition(dataView, position + 1, dataEnd);
3642
3719
  return {
3643
3720
  error: new CorruptFrameError(message, logBuffer.logId, position, resyncPosition),
@@ -3667,7 +3744,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3667
3744
  let logId = latestLogId;
3668
3745
  let position = 0;
3669
3746
  let dataView;
3670
- let logBuffer = this._currentLogBuffer;
3747
+ let logBuffer = this._currentLogBuffer?.deref();
3671
3748
  let foundExactStart = false;
3672
3749
  if (start === void 0 && !startFromLastFlushed) {
3673
3750
  position = size;
@@ -3685,7 +3762,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3685
3762
  }
3686
3763
  if (logBuffer === void 0 || logBuffer.logId !== logId) {
3687
3764
  logBuffer = getLogMemoryMap(this, logId);
3688
- if (logBuffer && latestLogId === logId && !readUncommitted) this._currentLogBuffer = logBuffer;
3765
+ if (logBuffer && latestLogId === logId && !readUncommitted && this._currentLogBuffer?.deref() !== logBuffer) this._currentLogBuffer = new WeakRef(logBuffer);
3689
3766
  if (logBuffer === void 0) {
3690
3767
  logBuffer = Buffer.alloc(0);
3691
3768
  logBuffer.logId = 0;
@@ -3695,9 +3772,11 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3695
3772
  }
3696
3773
  }
3697
3774
  dataView = logBuffer.dataView;
3698
- if (latestLogId !== logId) {
3699
- size = logBuffer.size;
3700
- if (size === void 0) size = logBuffer.size = this.getLogFileSize(logId);
3775
+ if (latestLogId < logId) size = 0;
3776
+ else if (latestLogId > logId) {
3777
+ const cachedSize = logBuffer.size;
3778
+ if (cachedSize === void 0) size = logBuffer.size = readableExtent(logBuffer);
3779
+ else size = cachedSize;
3701
3780
  }
3702
3781
  return {
3703
3782
  [Symbol.iterator]() {
@@ -3707,15 +3786,15 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3707
3786
  let timestamp;
3708
3787
  if (position >= size) {
3709
3788
  const { logId: latestLogId, size: latestSize } = loadLastPosition(transactionLog, !!readUncommitted);
3710
- size = latestSize;
3789
+ size = latestLogId < logBuffer.logId ? 0 : latestSize;
3711
3790
  if (latestLogId > logBuffer.logId) {
3712
- size = logBuffer.size ?? (logBuffer.size = transactionLog.getLogFileSize(logBuffer.logId));
3791
+ size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
3713
3792
  if (position >= size) {
3714
- const nextLogBuffer = getLogMemoryMap(transactionLog, logBuffer.logId + 1);
3793
+ const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
3715
3794
  if (nextLogBuffer) {
3716
3795
  dataView = nextLogBuffer.dataView;
3717
3796
  logBuffer = nextLogBuffer;
3718
- if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = transactionLog.getLogFileSize(logBuffer.logId));
3797
+ if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
3719
3798
  else size = latestSize;
3720
3799
  position = TRANSACTION_LOG_FILE_HEADER_SIZE;
3721
3800
  }
@@ -3766,19 +3845,25 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3766
3845
  };
3767
3846
  if (position >= size) {
3768
3847
  const { logId: latestLogId, size: latestSize } = loadLastPosition(transactionLog, !!readUncommitted);
3769
- size = latestSize;
3848
+ size = latestLogId < logBuffer.logId ? 0 : latestSize;
3770
3849
  if (latestLogId > logBuffer.logId) {
3771
- const nextLogBuffer = getLogMemoryMap(transactionLog, logBuffer.logId + 1);
3850
+ size = logBuffer.size ?? readableExtent(logBuffer);
3851
+ if (!readUncommitted) logBuffer.size = size;
3852
+ if (position < size) continue;
3853
+ const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
3772
3854
  if (!nextLogBuffer) return {
3773
3855
  done: true,
3774
3856
  value: void 0
3775
3857
  };
3776
3858
  logBuffer = nextLogBuffer;
3777
3859
  dataView = logBuffer.dataView;
3778
- size = logBuffer.size;
3779
- if (size == void 0) {
3780
- size = transactionLog.getLogFileSize(logBuffer.logId);
3781
- if (!readUncommitted) logBuffer.size = size;
3860
+ if (latestLogId === logBuffer.logId) size = latestSize;
3861
+ else {
3862
+ const cachedSize = logBuffer.size;
3863
+ if (cachedSize === void 0) {
3864
+ size = readableExtent(logBuffer);
3865
+ if (!readUncommitted) logBuffer.size = size;
3866
+ } else size = cachedSize;
3782
3867
  }
3783
3868
  position = TRANSACTION_LOG_FILE_HEADER_SIZE;
3784
3869
  }
@@ -3791,6 +3876,35 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3791
3876
  }
3792
3877
  };
3793
3878
  } });
3879
+ /**
3880
+ * Maps the next readable segment after `fromLogId`, skipping a run retention has deleted —
3881
+ * stopping at the hole wedges the iterator there for every later poll (invariant 30).
3882
+ *
3883
+ * The jump target is `_nextLogId()`, the successor over the store's registered segments — *not*
3884
+ * `_findPosition(0)`, which names the bottom of the contiguous run ending at the current segment
3885
+ * and so lands *past* a survivor sitting between two holes.
3886
+ */
3887
+ function nextReadableLogBuffer(transactionLog, fromLogId, latestLogId) {
3888
+ let candidateLogId = fromLogId + 1;
3889
+ while (candidateLogId <= latestLogId) {
3890
+ const logBuffer = getLogMemoryMap(transactionLog, candidateLogId);
3891
+ if (logBuffer) {
3892
+ if (candidateLogId === latestLogId || readableExtent(logBuffer) > TRANSACTION_LOG_FILE_HEADER_SIZE) return logBuffer;
3893
+ } else if (transactionLog.getLogFileSize(candidateLogId) > TRANSACTION_LOG_FILE_HEADER_SIZE) return;
3894
+ candidateLogId = transactionLog._nextLogId(candidateLogId);
3895
+ if (candidateLogId === 0) return;
3896
+ }
3897
+ }
3898
+ /**
3899
+ * The bound for reading `logBuffer`. Native advances this append-owned extent only after a
3900
+ * successful write. It remains attached to the mapping after purge forgets the file object, so
3901
+ * readers retain committed history without interpreting a partial failed append as another frame.
3902
+ */
3903
+ function readableExtent(logBuffer) {
3904
+ const extent = logBuffer.readableExtent;
3905
+ if (extent === void 0) throw new Error(`Transaction log buffer ${logBuffer.logId} has no readableExtent`);
3906
+ return Math.min(logBuffer.length, extent);
3907
+ }
3794
3908
  function getLogMemoryMap(transactionLog, logId) {
3795
3909
  if (logId <= 0) return;
3796
3910
  let logBuffer = transactionLog._logBuffers.get(logId)?.deref();
@@ -3836,9 +3950,9 @@ function loadLastPosition(transactionLog, readUncommitted) {
3836
3950
  //#region src/index.ts
3837
3951
  const versions = {
3838
3952
  rocksdb: version,
3839
- "rocksdb-js": "2.10.0"
3953
+ "rocksdb-js": "2.11.0"
3840
3954
  };
3841
3955
 
3842
3956
  //#endregion
3843
- export { BackgroundError, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, steadyClockNow, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
3957
+ export { BackgroundError, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getRegistryStatus, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, steadyClockNow, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
3844
3958
  //# sourceMappingURL=index.mjs.map