@harperfast/rocksdb-js 2.8.0 → 2.9.1

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
@@ -66,6 +66,26 @@ const binding = req(locateBinding());
66
66
  */
67
67
  const BackgroundError = binding.BackgroundError;
68
68
  const config = binding.config;
69
+ /**
70
+ * Reads the live state of the process-wide `WriteBufferManager`: its budget,
71
+ * charged memory, mutable share, stall state, and attached column-family
72
+ * inventory. The manager is shared by every database and worker thread in the
73
+ * process.
74
+ *
75
+ * A WriteBufferManager stall does not pass through RocksDB's `WriteController`,
76
+ * so it is not reflected by `rocksdb.stall.micros`, `db.isWriteStalled()`, or
77
+ * the `'writeStall'` event. Use `stallActive` to distinguish this condition from
78
+ * an idle database.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const wbm = getWriteBufferManagerStats();
83
+ * if (wbm.stallActive) {
84
+ * log.warn(`writes stalled for ${wbm.stallActiveMs}ms`);
85
+ * }
86
+ * ```
87
+ */
88
+ const getWriteBufferManagerStats = binding.getWriteBufferManagerStats;
69
89
  const FRESH_VERSION_FLAG$1 = binding.constants.FRESH_VERSION_FLAG;
70
90
  const addGlobalListener = binding.addListener;
71
91
  const removeGlobalListener = binding.removeListener;
@@ -114,6 +134,8 @@ const transactionLogMapCount = binding.transactionLogMapCount;
114
134
  * disarm. Used by the ERR_TRY_AGAIN retry regression test.
115
135
  */
116
136
  const forceTryAgainForTesting = binding.forceTryAgainForTesting;
137
+ /** Delays one selected watchdog join in the concurrent-shutdown regression test. */
138
+ const setWriteBufferManagerJoinDelayForTesting = binding.setWriteBufferManagerJoinDelayForTesting;
117
139
  /**
118
140
  * Creates a native file lock using the specified file path (`flock` on POSIX,
119
141
  * `LockFileEx` on Windows), creating the file and any missing parent
@@ -642,14 +664,12 @@ function parseDuration(duration) {
642
664
  num *= 1e3;
643
665
  break;
644
666
  case "m":
645
- num *= 1e3 * 60;
667
+ num *= 6e4;
646
668
  break;
647
669
  case "h":
648
- num *= 1e3 * 60 * 60;
649
- break;
650
- case "d":
651
- num *= 1e3 * 60 * 60 * 24;
670
+ num *= 36e5;
652
671
  break;
672
+ case "d": num *= 864e5;
653
673
  }
654
674
  result += num;
655
675
  }
@@ -1248,7 +1268,15 @@ var DBIterator = class {
1248
1268
  this.iterator.return?.();
1249
1269
  return DONE_RESULT;
1250
1270
  }
1251
- const result = this.iterator.next();
1271
+ let result;
1272
+ try {
1273
+ result = this.iterator.next();
1274
+ } catch (err) {
1275
+ try {
1276
+ this.iterator.return?.();
1277
+ } catch {}
1278
+ throw err;
1279
+ }
1252
1280
  if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
1253
1281
  this.#yielded++;
1254
1282
  const includeValues = this.#includeValues;
@@ -1352,10 +1380,10 @@ function createFixedBuffer(size) {
1352
1380
 
1353
1381
  //#endregion
1354
1382
  //#region src/store.ts
1355
- const { ONLY_IF_IN_MEMORY_CACHE_FLAG, NOT_IN_MEMORY_CACHE_FLAG, ALWAYS_CREATE_NEW_BUFFER_FLAG, FRESH_VERSION_FLAG, POPULATE_VERSION_FLAG, ITERATOR_REVERSE_FLAG, ITERATOR_INCLUSIVE_END_FLAG, ITERATOR_EXCLUSIVE_START_FLAG, ITERATOR_INCLUDE_VALUES_FLAG, ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG, ITERATOR_CONTEXT_IS_TRANSACTION_FLAG } = constants;
1383
+ const { ONLY_IF_IN_MEMORY_CACHE_FLAG, NOT_IN_MEMORY_CACHE_FLAG, ALWAYS_CREATE_NEW_BUFFER_FLAG, FRESH_VERSION_FLAG, POPULATE_VERSION_FLAG, ITERATOR_REVERSE_FLAG, ITERATOR_INCLUSIVE_END_FLAG, ITERATOR_EXCLUSIVE_START_FLAG, ITERATOR_INCLUDE_VALUES_FLAG, ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG, ITERATOR_CONTEXT_IS_TRANSACTION_FLAG, ITERATOR_HAS_TRANSACTION_ID_FLAG } = constants;
1356
1384
  const KEY_BUFFER_SIZE = 4096;
1357
1385
  const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
1358
- const VALUE_BUFFER = createFixedBuffer(64 * 1024);
1386
+ const VALUE_BUFFER = createFixedBuffer(65536);
1359
1387
  /**
1360
1388
  * Backing buffer for the shared iterator state. Layout (Uint32Array view):
1361
1389
  * [0] = key length written into KEY_BUFFER by the most recent iterator step
@@ -1363,7 +1391,7 @@ const VALUE_BUFFER = createFixedBuffer(64 * 1024);
1363
1391
  */
1364
1392
  const ITERATOR_STATE_BUFFER = Buffer.allocUnsafeSlow(8);
1365
1393
  const ITERATOR_STATE = new Uint32Array(ITERATOR_STATE_BUFFER.buffer, ITERATOR_STATE_BUFFER.byteOffset, 2);
1366
- const MAX_KEY_SIZE = 1024 * 1024;
1394
+ const MAX_KEY_SIZE = 1048576;
1367
1395
  const SAVE_BUFFER_SIZE = 8192;
1368
1396
  /**
1369
1397
  * Normalizes the public `compression` option into the primitive fields the
@@ -1398,6 +1426,15 @@ function normalizeCompression(option) {
1398
1426
  };
1399
1427
  }
1400
1428
  /**
1429
+ * Whether `codec` is a pre-constructed encoder/decoder instance that `open()`
1430
+ * would reuse as-is (rather than instantiate fresh from an `Encoder` factory).
1431
+ * Such an instance carries per-column-family mutable state (its `name`, msgpack
1432
+ * `structures`), so it cannot be shared across column families.
1433
+ */
1434
+ function sharesMutableCodec(codec) {
1435
+ return codec != null && typeof codec === "object" && typeof codec.Encoder !== "function" && (typeof codec.encode === "function" || typeof codec.decode === "function");
1436
+ }
1437
+ /**
1401
1438
  * A store wraps the `NativeDatabase` binding and database settings so that a
1402
1439
  * single database instance can be shared between the main `RocksDatabase`
1403
1440
  * instance and the `Transaction` instance.
@@ -1416,6 +1453,13 @@ var Store = class {
1416
1453
  */
1417
1454
  #claimed = false;
1418
1455
  /**
1456
+ * Shallow snapshot of the options this store was constructed with, used by
1457
+ * `createColumnFamilyStore()` to derive sibling column-family stores. Snapshot
1458
+ * (not the caller's object) so later mutations of the caller's options don't
1459
+ * leak into derived views.
1460
+ */
1461
+ #columnFamilyOptions;
1462
+ /**
1419
1463
  * The database instance.
1420
1464
  */
1421
1465
  db;
@@ -1510,10 +1554,16 @@ var Store = class {
1510
1554
  /**
1511
1555
  * The bytes of recent memtable history to retain in memory for transaction
1512
1556
  * conflict checking. `-1` derives the value from
1513
- * `maxWriteBufferNumber * writeBufferSize`, except under a stalling
1514
- * `writeBufferManager`, where it derives `0`: retained history is charged to
1515
- * that manager and is only trimmable down to this target, so a target the
1516
- * budget cannot hold stalls writes permanently. Set a number to size it
1557
+ * `maxWriteBufferNumber * writeBufferSize`, except when a
1558
+ * `writeBufferManager` is configured, where it derives `1`: retained history
1559
+ * is charged to that manager and is only trimmable down to this target, so a
1560
+ * target the budget cannot hold is memory the manager never reclaims, and
1561
+ * stalls writes permanently when `writeBufferManagerAllowStall` is on. Any
1562
+ * configured manager, not only a stalling one — that flag is mutable at
1563
+ * runtime while this value is fixed when the family is created. `1`, not `0`, because
1564
+ * RocksDB's transaction wrappers rewrite a `0` target to the derived value —
1565
+ * so `0` asks for the largest history rather than none, and an explicit `0`
1566
+ * is normalized to `1` for that reason. Set a positive number to size it
1517
1567
  * yourself against the budget and the column-family count.
1518
1568
  */
1519
1569
  maxWriteBufferSizeToMaintain;
@@ -1551,6 +1601,11 @@ var Store = class {
1551
1601
  */
1552
1602
  path;
1553
1603
  /**
1604
+ * The open database's resolved filesystem identity, read once from
1605
+ * `NativeDatabase.identityPath`. `undefined` until opened.
1606
+ */
1607
+ identityPath;
1608
+ /**
1554
1609
  * Whether to use pessimistic locking for transactions. When `true`,
1555
1610
  * transactions will fail as soon as a conflict is detected. When `false`,
1556
1611
  * transactions will only fail when `commit()` is called.
@@ -1564,6 +1619,13 @@ var Store = class {
1564
1619
  */
1565
1620
  readOnly;
1566
1621
  /**
1622
+ * The secondary instance's workspace directory when the database is opened
1623
+ * as a secondary (see `RocksDatabaseOptions.secondaryPath`); `undefined`
1624
+ * otherwise. A secondary is read-only, so `readOnly` is `true` whenever
1625
+ * this is set.
1626
+ */
1627
+ secondaryPath;
1628
+ /**
1567
1629
  * Encoder specific flag used to signal that the encoder should use a random
1568
1630
  * access structure.
1569
1631
  */
@@ -1649,7 +1711,10 @@ var Store = class {
1649
1711
  this.parallelismThreads = options?.parallelismThreads;
1650
1712
  this.path = path;
1651
1713
  this.pessimistic = options?.pessimistic ?? false;
1652
- this.readOnly = options?.readOnly ?? false;
1714
+ const secondaryPath = options?.secondaryPath ?? void 0;
1715
+ if (secondaryPath !== void 0 && options?.readOnly === false) throw new Error("A secondary open is read-only; secondaryPath cannot be combined with readOnly: false");
1716
+ this.readOnly = options?.readOnly ?? secondaryPath !== void 0;
1717
+ this.secondaryPath = secondaryPath;
1653
1718
  this.randomAccessStructure = options?.randomAccessStructure ?? false;
1654
1719
  this.readKey = readKey;
1655
1720
  this.sharedStructuresKey = options?.sharedStructuresKey;
@@ -1661,6 +1726,36 @@ var Store = class {
1661
1726
  this.verificationTable = options?.verificationTable;
1662
1727
  this.writeBufferSize = options?.writeBufferSize;
1663
1728
  this.writeKey = writeKey;
1729
+ this.#columnFamilyOptions = options ? { ...options } : void 0;
1730
+ }
1731
+ /**
1732
+ * Creates an independent `Store` for another column family of the same
1733
+ * database, used by {@link RocksDatabase#use}. The returned store shares the
1734
+ * underlying database but must have its own codec state: a mutable
1735
+ * encoder/decoder instance cannot be shared across column families (opening a
1736
+ * view mutates the encoder's `name`, and clearing a view resets its
1737
+ * `structures`), so a pre-constructed instance is rejected here — supply an
1738
+ * encoder factory (`{ Encoder }`) or a named `encoding` instead.
1739
+ *
1740
+ * Override this in a `Store` subclass whose constructor cannot be recreated
1741
+ * from `(path, options)` (e.g. it takes injected dependencies) or that needs
1742
+ * to control how a view's codec state is built.
1743
+ *
1744
+ * @param name - The column family name for the derived store.
1745
+ * @param options - Options overriding this store's, for the derived store.
1746
+ * @returns A new, independent `Store` bound to `name`.
1747
+ */
1748
+ createColumnFamilyStore(name, options) {
1749
+ const derived = {
1750
+ ...this.#columnFamilyOptions,
1751
+ ...options,
1752
+ name
1753
+ };
1754
+ if (sharesMutableCodec(derived.encoder) || sharesMutableCodec(derived.decoder)) throw new Error("Cannot derive a column family view from a Store configured with a pre-constructed encoder/decoder instance — its mutable state would be shared across column families. Provide an encoder factory (`{ Encoder }`) or a named `encoding`, or override `Store#createColumnFamilyStore`.");
1755
+ const StoreClass = this.constructor;
1756
+ const store = new StoreClass(this.path, derived);
1757
+ if (store.name !== name) throw new Error(`Store#createColumnFamilyStore produced a store bound to "${store.name}" instead of "${name}"; a custom Store must forward options to super() or override this method.`);
1758
+ return store;
1664
1759
  }
1665
1760
  /**
1666
1761
  * Closes the database.
@@ -1842,6 +1937,7 @@ var Store = class {
1842
1937
  let endUnencoded = options?.key ?? options?.end;
1843
1938
  const includeValues = options?.values ?? true;
1844
1939
  const reverse = options?.reverse ?? false;
1940
+ const txnId = this.getTxnId(options);
1845
1941
  let exclusiveStart = options?.exclusiveStart ?? false;
1846
1942
  let inclusiveEnd = options?.inclusiveEnd ?? false;
1847
1943
  if (options?.key !== void 0) inclusiveEnd = true;
@@ -1860,13 +1956,15 @@ var Store = class {
1860
1956
  startKeyEnd = this.writeKey(startUnencoded, keyBuffer, 0);
1861
1957
  if (startKeyEnd === 0) throw new Error("Zero length key is not allowed");
1862
1958
  }
1863
- if (endUnencoded !== void 0) if (endUnencoded === startUnencoded) {
1864
- endKeyStart = 0;
1865
- endKeyEnd = startKeyEnd;
1866
- } else {
1867
- endKeyStart = startKeyEnd;
1868
- endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
1869
- if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
1959
+ if (endUnencoded !== void 0) {
1960
+ if (endUnencoded === startUnencoded) {
1961
+ endKeyStart = 0;
1962
+ endKeyEnd = startKeyEnd;
1963
+ } else {
1964
+ endKeyStart = startKeyEnd;
1965
+ endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
1966
+ if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
1967
+ }
1870
1968
  }
1871
1969
  let flags = 0;
1872
1970
  if (reverse) flags |= ITERATOR_REVERSE_FLAG;
@@ -1875,8 +1973,9 @@ var Store = class {
1875
1973
  if (includeValues) flags |= ITERATOR_INCLUDE_VALUES_FLAG;
1876
1974
  if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
1877
1975
  if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
1976
+ if (txnId !== void 0) flags |= ITERATOR_HAS_TRANSACTION_ID_FLAG;
1878
1977
  const advancedOptions = options !== void 0 && (options.adaptiveReadahead !== void 0 || options.asyncIO !== void 0 || options.autoReadaheadSize !== void 0 || options.backgroundPurgeOnIteratorCleanup !== void 0 || options.fillCache !== void 0 || options.readaheadSize !== void 0 || options.tailing !== void 0) ? options : void 0;
1879
- return new ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues, options?.limit));
1978
+ return new ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId), this, includeValues, options?.limit));
1880
1979
  }
1881
1980
  getSync(context, key, alwaysCreateNewBuffer = false, options) {
1882
1981
  const keyParam = getKeyParam(this.encodeKey(key));
@@ -1897,9 +1996,12 @@ var Store = class {
1897
1996
  */
1898
1997
  getTxnId(options) {
1899
1998
  let txnId;
1900
- if (!this.readOnly && options?.transaction) {
1901
- txnId = options.transaction.id;
1999
+ const transaction = options?.transaction;
2000
+ if (!this.readOnly && transaction) {
2001
+ txnId = transaction.id;
1902
2002
  if (txnId === void 0) throw new TypeError("Invalid transaction");
2003
+ if (transaction.store === void 0) throw new TypeError("Invalid transaction");
2004
+ if (this.identityPath !== void 0 && transaction.store.identityPath !== this.identityPath) throw new TypeError("Transaction belongs to a different database");
1903
2005
  }
1904
2006
  return txnId;
1905
2007
  }
@@ -1957,7 +2059,10 @@ var Store = class {
1957
2059
  * are performed.
1958
2060
  */
1959
2061
  open() {
1960
- if (this.db.opened) return true;
2062
+ if (this.db.opened) {
2063
+ this.identityPath = this.db.identityPath;
2064
+ return true;
2065
+ }
1961
2066
  const { compression, compressionLevel } = normalizeCompression(this.compression);
1962
2067
  this.db.open(this.path, {
1963
2068
  compression,
@@ -1976,6 +2081,7 @@ var Store = class {
1976
2081
  noBlockCache: this.noBlockCache,
1977
2082
  parallelismThreads: this.parallelismThreads,
1978
2083
  readOnly: this.readOnly,
2084
+ secondaryPath: this.secondaryPath,
1979
2085
  statsLevel: this.statsLevel,
1980
2086
  transactionLogMaxAgeThreshold: this.transactionLogMaxAgeThreshold,
1981
2087
  transactionLogMaxSize: this.transactionLogMaxSize,
@@ -1984,6 +2090,7 @@ var Store = class {
1984
2090
  verificationTable: this.verificationTable,
1985
2091
  writeBufferSize: this.writeBufferSize
1986
2092
  });
2093
+ this.identityPath = this.db.identityPath;
1987
2094
  return false;
1988
2095
  }
1989
2096
  putSync(context, key, value, options) {
@@ -2244,10 +2351,12 @@ var Transaction = class extends DBI {
2244
2351
  return err;
2245
2352
  }
2246
2353
  /**
2247
- * Returns the transaction start timestamp in seconds. Defaults to the time at which
2248
- * the transaction was created.
2354
+ * Returns the transaction timestamp in milliseconds since the Unix epoch. It
2355
+ * defaults to a process-wide monotonic value assigned when the transaction
2356
+ * was created. A producer may use this value as its write identity; the
2357
+ * transaction log uses it as the batch key.
2249
2358
  *
2250
- * @returns The transaction start timestamp in seconds.
2359
+ * @returns The transaction timestamp in milliseconds.
2251
2360
  */
2252
2361
  getTimestamp() {
2253
2362
  return this.#txn.getTimestamp();
@@ -2259,9 +2368,14 @@ var Transaction = class extends DBI {
2259
2368
  return this.#txn.id;
2260
2369
  }
2261
2370
  /**
2262
- * Set the transaction start timestamp in seconds.
2371
+ * Overrides the transaction timestamp in milliseconds since the Unix epoch.
2372
+ * Call this before reading the timestamp or staging writes when adopting an
2373
+ * origin timestamp for replication or replay. Once a write or log entry is
2374
+ * staged, the timestamp is frozen, but reapplying the same timestamp remains
2375
+ * idempotent while the transaction is pending. The value must be finite,
2376
+ * positive, and below 8640000000000000.
2263
2377
  *
2264
- * @param timestamp - The timestamp to set in seconds.
2378
+ * @param timestamp - The timestamp to set in milliseconds.
2265
2379
  */
2266
2380
  setTimestamp(timestamp) {
2267
2381
  this.#txn.setTimestamp(timestamp);
@@ -2295,9 +2409,23 @@ function backgroundErrorSeverityName(severity) {
2295
2409
  */
2296
2410
  var RocksDatabase = class RocksDatabase extends DBI {
2297
2411
  /**
2298
- * The name of the database.
2412
+ * The name of the database's column family.
2299
2413
  */
2300
2414
  #name;
2415
+ /**
2416
+ * Weak cache of column-family views opened via `use()`, keyed by column
2417
+ * family name — mirroring `useLog`'s get-or-create semantics. The reference
2418
+ * is weak so a view is never pinned for this database's lifetime: an
2419
+ * unreferenced view can be collected (closing its own `DBHandle`), and the
2420
+ * next `use()` transparently recreates it. `#columnFamilyRegistry` deletes an
2421
+ * entry once its view is collected so a churn of distinct names (e.g.
2422
+ * `use(uuid())`) does not grow the map without bound.
2423
+ */
2424
+ #columnFamilies = /* @__PURE__ */ new Map();
2425
+ #columnFamilyRegistry = new FinalizationRegistry((name) => {
2426
+ const ref = this.#columnFamilies.get(name);
2427
+ if (ref !== void 0 && ref.deref() === void 0) this.#columnFamilies.delete(name);
2428
+ });
2301
2429
  constructor(pathOrStore, options) {
2302
2430
  let store;
2303
2431
  if (typeof pathOrStore === "string") store = new Store(pathOrStore, options);
@@ -2305,7 +2433,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
2305
2433
  else throw new TypeError("Invalid database path or store");
2306
2434
  store.claim();
2307
2435
  super(store);
2308
- this.#name = options?.name ?? "default";
2436
+ this.#name = this.store.name;
2309
2437
  }
2310
2438
  /**
2311
2439
  * Removes all data from the database asynchronously.
@@ -2524,6 +2652,40 @@ var RocksDatabase = class RocksDatabase extends DBI {
2524
2652
  static notify(event, ...args) {
2525
2653
  return globalNotify(event, args);
2526
2654
  }
2655
+ /**
2656
+ * Advances a secondary instance to the primary's current state by tailing
2657
+ * and replaying the primary's MANIFEST and WAL. A secondary does not see
2658
+ * the primary's writes until this (or {@link catchUpWithPrimarySync}) is
2659
+ * called. Concurrent reads through this handle remain safe while the
2660
+ * catch-up runs.
2661
+ *
2662
+ * Only valid on a database opened with the `secondaryPath` option; throws
2663
+ * with code `ERR_NOT_SECONDARY` otherwise.
2664
+ *
2665
+ * Advances the **database** view only. Against a cross-process primary,
2666
+ * transaction-log reads keep serving the stores discovered at open (a store
2667
+ * the primary creates later, and its appends, need a reopen); a primary in
2668
+ * the same process shares the store registry, so its stores and appends are
2669
+ * visible immediately. Either way a log entry can describe data this
2670
+ * handle's database view does not have yet — the log write completes before
2671
+ * the RocksDB commit for every writer, so consumers must tolerate the log
2672
+ * leading the database.
2673
+ *
2674
+ * Await one call before starting the next: the replay holds a libuv worker
2675
+ * for its whole duration and concurrent calls queue on a per-database mutex
2676
+ * while holding theirs, so overlapping catch-ups on a backlogged follower
2677
+ * can exhaust the default four-thread pool and stall unrelated I/O.
2678
+ */
2679
+ catchUpWithPrimary() {
2680
+ return new Promise((resolve, reject) => this.store.db.catchUpWithPrimary(resolve, reject));
2681
+ }
2682
+ /**
2683
+ * Synchronous version of {@link catchUpWithPrimary}. The replay runs on the
2684
+ * JS thread, so prefer the async form unless the caller is already blocking.
2685
+ */
2686
+ catchUpWithPrimarySync() {
2687
+ this.store.db.catchUpWithPrimarySync();
2688
+ }
2527
2689
  destroy() {
2528
2690
  this.store.db.destroy();
2529
2691
  }
@@ -2676,6 +2838,29 @@ var RocksDatabase = class RocksDatabase extends DBI {
2676
2838
  return this.store.db.getDBProperty(propertyName);
2677
2839
  }
2678
2840
  /**
2841
+ * Whether RocksDB is currently applying write backpressure to this database —
2842
+ * either delaying (rate-limiting) or fully stopping writes. This is the
2843
+ * authoritative, live pull counterpart to the `'writeStall'`
2844
+ * [event](../README.md#event-writestall): the event pushes per-column-family
2845
+ * transitions (and is rate-limited), while this reads the current state on
2846
+ * demand and can never be stale.
2847
+ *
2848
+ * The write controller is database-wide (shared across every column family),
2849
+ * so this reflects the database as a whole, not a single column family — a
2850
+ * stall triggered by any column family stops writes for all of them. Reads
2851
+ * `rocksdb.is-write-stopped` and `rocksdb.actual-delayed-write-rate`.
2852
+ *
2853
+ * @example
2854
+ * ```typescript
2855
+ * db.on('writeStall', (cf) => {
2856
+ * if (db.isWriteStalled()) log.warn(`writes throttled/blocked (triggered by ${cf})`);
2857
+ * });
2858
+ * ```
2859
+ */
2860
+ isWriteStalled() {
2861
+ return this.getDBIntProperty("rocksdb.is-write-stopped") === 1 || (this.getDBIntProperty("rocksdb.actual-delayed-write-rate") ?? 0) > 0;
2862
+ }
2863
+ /**
2679
2864
  * Retrieves the estimated number of keys in the database. This is an alias
2680
2865
  * for `db.estimateCount().count`; use `estimateCount()` for range support
2681
2866
  * and a confidence indicator.
@@ -2853,18 +3038,29 @@ var RocksDatabase = class RocksDatabase extends DBI {
2853
3038
  return this.store.listLogs();
2854
3039
  }
2855
3040
  /**
2856
- * The name of the database.
3041
+ * The name of the database's column family.
2857
3042
  */
2858
3043
  get name() {
2859
3044
  return this.#name;
2860
3045
  }
2861
3046
  /**
2862
- * Whether the database is open in readonly mode.
3047
+ * Whether the database is open in readonly mode. Also `true` for a
3048
+ * secondary instance (see {@link secondaryPath}), which is read-only by
3049
+ * construction.
2863
3050
  */
2864
3051
  get readOnly() {
2865
3052
  return this.store.readOnly;
2866
3053
  }
2867
3054
  /**
3055
+ * The secondary instance's workspace directory when the database was opened
3056
+ * as a secondary (a read-only follower of a live primary — see the
3057
+ * `secondaryPath` open option), or `undefined` for a regular or plain
3058
+ * read-only open.
3059
+ */
3060
+ get secondaryPath() {
3061
+ return this.store.secondaryPath;
3062
+ }
3063
+ /**
2868
3064
  * Sugar method for opening a database.
2869
3065
  *
2870
3066
  * @param pathOrStore - The filesystem path to the database or a custom store.
@@ -3137,6 +3333,57 @@ var RocksDatabase = class RocksDatabase extends DBI {
3137
3333
  return this.store.unlock(key);
3138
3334
  }
3139
3335
  /**
3336
+ * Returns a `RocksDatabase` bound to the `name` column family of this same
3337
+ * database, opening (and creating, if missing) the column family on first
3338
+ * use. This is a factory — create-and-open sugar — not a stateful switch: the
3339
+ * returned view is an independent instance whose own reads/writes target its
3340
+ * column family, while sharing the underlying database (so a transaction,
3341
+ * backup, or checkpoint still spans every column family). Because the view
3342
+ * opens its own handle, this database need not itself be open — a discarded
3343
+ * parent (`new RocksDatabase(path).use('events')`) still yields a valid,
3344
+ * opened view.
3345
+ *
3346
+ * Get-or-create, backed by a weak cache: `db.use('events') === db.use('events')`
3347
+ * while the view is still referenced **and open**, and calling with this
3348
+ * database's own column-family name returns `this`. A view that has been
3349
+ * closed (or garbage-collected — the cache does not pin it) is transparently
3350
+ * recreated on the next `use()`. Views are independent handles: closing this
3351
+ * database does not close them, and vice versa — the underlying database
3352
+ * stays open until every handle is closed or collected.
3353
+ *
3354
+ * The view's store is derived by {@link Store#createColumnFamilyStore}, which
3355
+ * builds an independent store of the same class with its own codec state (so
3356
+ * views never share a mutable encoder/decoder) and inherits the options this
3357
+ * store was constructed with; pass `options` to override them for this column
3358
+ * family. A database configured with a pre-constructed encoder/decoder
3359
+ * instance cannot derive views (its state would be shared) — use an encoder
3360
+ * factory (`{ Encoder }`) or a named `encoding`, or override
3361
+ * `createColumnFamilyStore` on a custom `Store`.
3362
+ *
3363
+ * @param name - The column family name.
3364
+ * @param options - Options for the column family, overriding inherited ones.
3365
+ * @returns A `RocksDatabase` bound to the column family.
3366
+ *
3367
+ * @example
3368
+ * ```typescript
3369
+ * const db = RocksDatabase.open('/path/to/database');
3370
+ * const events = db.use('events');
3371
+ * await events.put('e1', payload);
3372
+ * await db.put('k', 'v'); // default column family, unaffected
3373
+ * ```
3374
+ */
3375
+ use(name, options) {
3376
+ if (typeof name !== "string" || name === "") throw new TypeError("Column family name must be a non-empty string");
3377
+ if (name === this.#name) return this.open();
3378
+ const cached = this.#columnFamilies.get(name)?.deref();
3379
+ if (cached !== void 0 && cached.isOpen()) return cached;
3380
+ const columnFamily = new RocksDatabase(this.store.createColumnFamilyStore(name, options));
3381
+ columnFamily.open();
3382
+ this.#columnFamilies.set(name, new WeakRef(columnFamily));
3383
+ this.#columnFamilyRegistry.register(columnFamily, name);
3384
+ return columnFamily;
3385
+ }
3386
+ /**
3140
3387
  * Excecutes a function using a thread-safe lock to ensure mutual
3141
3388
  * exclusion.
3142
3389
  *
@@ -3443,11 +3690,12 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3443
3690
  position += TRANSACTION_LOG_ENTRY_HEADER_SIZE;
3444
3691
  let matchesRange;
3445
3692
  if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
3446
- else if (exactStart) if (timestamp === start) {
3447
- matchesRange = !exclusiveStart;
3448
- foundExactStart = true;
3449
- } else matchesRange = false;
3450
- else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
3693
+ else if (exactStart) {
3694
+ if (timestamp === start) {
3695
+ matchesRange = !exclusiveStart;
3696
+ foundExactStart = true;
3697
+ } else matchesRange = false;
3698
+ } else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
3451
3699
  const entryStart = position;
3452
3700
  position += length;
3453
3701
  if (matchesRange) return {
@@ -3530,9 +3778,9 @@ function loadLastPosition(transactionLog, readUncommitted) {
3530
3778
  //#region src/index.ts
3531
3779
  const versions = {
3532
3780
  rocksdb: version,
3533
- "rocksdb-js": "2.8.0"
3781
+ "rocksdb-js": "2.9.1"
3534
3782
  };
3535
3783
 
3536
3784
  //#endregion
3537
- export { BackgroundError, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, parseTransactionLog, registryStatus, shutdown, stats, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
3785
+ export { BackgroundError, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
3538
3786
  //# sourceMappingURL=index.mjs.map