@harperfast/rocksdb-js 2.0.0 → 2.1.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/README.md CHANGED
@@ -804,35 +804,28 @@ same column family-level properties, but includes all internal tickers and histo
804
804
  Column family and ticker stat values are 64-bit unsigned integers and histogram values are
805
805
  `StatsHistogramData` objects.
806
806
 
807
+ The result also always includes a summarized, aggregate set of `txnlog.*` keys covering all of
808
+ the database's transaction logs. These are present regardless of whether statistics are enabled
809
+ and can be fetched individually with `db.getStat('txnlog.…')`. For detailed, per-log statistics —
810
+ including memory-map usage — use [`log.getStats()`](#loggetstats-transactionlogstats). All stat
811
+ names are documented in [docs/stats.md](docs/stats.md).
812
+
807
813
  ```typescript
808
814
  // get essential stats
809
815
  console.log(db.getStats());
810
816
 
811
817
  // get all stats
812
818
  console.log(db.getStats(true));
813
- ```
814
-
815
- ### `stats`
816
-
817
- An object containing stat specific constants including ticker and histogram names.
818
-
819
- #### `stats.histograms`
820
819
 
821
- An array of internal histogram stat names.
822
-
823
- ```typescript
824
- import { stats } from '@harperfast/rocksdb-js';
825
- console.log(stats.histograms);
820
+ // transaction log bytes across all logs
821
+ console.log(db.getStats()['txnlog.totalSizeBytes']);
826
822
  ```
827
823
 
828
- #### `stats.tickers`
824
+ ### `stats`
829
825
 
830
- An array of internal ticker stat names.
831
-
832
- ```typescript
833
- import { stats } from '@harperfast/rocksdb-js';
834
- console.log(stats.tickers);
835
- ```
826
+ An object containing stat-specific constants. The full catalog of available stat names (RocksDB
827
+ tickers, histograms, internal properties, and transaction log stats) is documented in
828
+ [docs/stats.md](docs/stats.md).
836
829
 
837
830
  #### `stats.StatsLevel`
838
831
 
@@ -968,6 +961,46 @@ log gets, puts, and deletes, but also arbitrary entries.
968
961
  Transaction logs are isolated by the database path allowing different column families in the same
969
962
  database to share the transaction log store, but not other databases.
970
963
 
964
+ ### Memory-Map Handling
965
+
966
+ Transaction log files are read through read-only memory maps. Understanding how these maps interact
967
+ with system memory helps when interpreting [`log.getStats()`](#loggetstats-transactionlogstats)
968
+ figures and process memory usage:
969
+
970
+ - **Maps are created lazily and live until the file is closed.** Writing log entries does not map
971
+ anything; a log file is mapped the first time a `log.query()` reads from it. The native layer
972
+ holds each file's map for the life of the file — it is released when the log file is purged or
973
+ the database is closed, not by garbage collection. JS `Buffer` views over the map (including
974
+ `entry.data`) hold an additional reference, so the underlying memory is never unmapped while a
975
+ view is still reachable. `stats.memory.activeMaps` counts the maps currently held by the native
976
+ layer.
977
+ - **Mapped bytes are virtual, not resident.** Creating a map reserves address space only. A page
978
+ consumes physical RAM (RSS) when it is first read (demand paging). Querying a multi-gigabyte log
979
+ can show `memory.mappedBytes` in the gigabytes while actual memory usage barely moves.
980
+ - **Queries are zero-copy.** The iterator returned by `log.query()` reads only each entry's small
981
+ header; `entry.data` is a `Buffer` view directly into the map (no copy). Payload pages are
982
+ faulted into memory only if and when the entry data is actually read.
983
+ - **Resident pages are reclaimable.** Because the maps are read-only and file-backed, every
984
+ resident page is "clean" — the kernel can evict it at any time under memory pressure and re-read
985
+ it from disk on the next access. Mapped log data therefore lives in the page cache and cannot
986
+ exhaust memory the way heap allocations can; a full scan of a log larger than RAM will simply
987
+ cycle pages through the cache.
988
+
989
+ OS-specific differences:
990
+
991
+ - **POSIX (Linux and macOS):** The active write file is mapped at the full configured
992
+ `transactionLogMaxSize` (an anonymous reservation with the file's contents overlaid on top), so
993
+ `memory.mappedBytes` over-reports the active file; `memory.overlayBytes` is the file-backed
994
+ portion and is the closer proxy for real consumption.
995
+ - **macOS:** Activity Monitor's "Memory" column reports the physical footprint, which excludes
996
+ clean file-backed pages — mapped log data is essentially invisible there even when resident. Use
997
+ process RSS (e.g. `process.memoryUsage().rss`, `ps`, or `vmmap <pid>`) to observe it.
998
+ - **Linux:** Resident map pages are visible in process RSS, and `/proc/<pid>/smaps` reports exact
999
+ per-file `Rss`/`Pss` for each mapped `.txnlog` file.
1000
+ - **Windows:** Maps are created with `CreateFileMapping`/`MapViewOfFile` at the file's current
1001
+ size. There is no overlay mechanism, so `memory.overlayBytes` is always `0`, and the active
1002
+ write file's map is not cached because it is not growable.
1003
+
971
1004
  ### `db.listLogs(): string[]`
972
1005
 
973
1006
  Returns an array of log store names.
@@ -1116,6 +1149,34 @@ for (const entry of rangeIter) {
1116
1149
  Returns the size of the given transaction log sequence file in bytes. Omit the sequence number to
1117
1150
  get the total size of all the transaction log sequence files for this log.
1118
1151
 
1152
+ #### `log.getStats(): TransactionLogStats`
1153
+
1154
+ Returns a detailed statistics snapshot for this transaction log, including file/transaction
1155
+ gauges, memory-map usage, recovery positions, purge/retention gauges, and lifetime counters. All
1156
+ sizes are in bytes; timestamps are milliseconds since the Unix epoch.
1157
+
1158
+ ```typescript
1159
+ const log = db.useLog('replication');
1160
+ const stats = log.getStats();
1161
+
1162
+ stats.fileCount; // number of sequence files on disk
1163
+ stats.totalSizeBytes; // total bytes across all sequence files
1164
+ stats.memory.mappedBytes; // bytes mapped into memory (virtual address space)
1165
+ stats.memory.overlayBytes; // POSIX file-backed overlay portion (0 on Windows)
1166
+ stats.replayGapBytes; // bytes between the last flushed position and the write head
1167
+ stats.purge.retainedUnflushedFiles; // files past retention but kept (not yet flushed to RocksDB)
1168
+ stats.totals.transactionsWritten; // lifetime count of transactions written
1169
+ ```
1170
+
1171
+ > **Memory note:** `memory.mappedBytes` is virtual address space — the active write file is mapped
1172
+ > at the full configured `transactionLogMaxSize` on POSIX, so it does not reflect resident memory.
1173
+ > `memory.overlayBytes` (POSIX only) is the file-backed portion and is the closer proxy for real
1174
+ > consumption.
1175
+
1176
+ The `purge.retainedUnflushedFiles` gauge is useful for diagnosing why logs are not being cleaned
1177
+ up: a file can be older than the retention period but still retained because its transactions have
1178
+ not yet been flushed to RocksDB (purging it would be unsafe for crash recovery).
1179
+
1119
1180
  ### Transaction Log Initialization
1120
1181
 
1121
1182
  When a database is opened, `rocksdb-js` will automatically discover the transaction log files. If
package/dist/index.cjs CHANGED
@@ -1544,17 +1544,8 @@ var RocksDatabase = class RocksDatabase extends DBI {
1544
1544
  getStat(statName) {
1545
1545
  return this.store.db.getStat(statName);
1546
1546
  }
1547
- /**
1548
- * Gets the RocksDB statistics. Requires statistics to be enabled.
1549
- *
1550
- * @example
1551
- * ```typescript
1552
- * const db = RocksDatabase.open('/path/to/database');
1553
- * const stats = db.getStats();
1554
- * ```
1555
- */
1556
1547
  getStats(all = false) {
1557
- return this.store.db.getStats(all);
1548
+ return all ? this.store.db.getStats(true) : this.store.db.getStats(false);
1558
1549
  }
1559
1550
  /**
1560
1551
  * Gets or creates a buffer that can be shared across worker threads.
@@ -2187,7 +2178,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
2187
2178
  //#region src/index.ts
2188
2179
  const versions = {
2189
2180
  rocksdb: version,
2190
- "rocksdb-js": "2.0.0"
2181
+ "rocksdb-js": "2.1.0"
2191
2182
  };
2192
2183
 
2193
2184
  //#endregion