@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.cjs CHANGED
@@ -20,7 +20,7 @@ var __copyProps = (to, from, except, desc) => {
20
20
  }
21
21
  return to;
22
22
  };
23
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
24
24
  value: mod,
25
25
  enumerable: true
26
26
  }) : target, mod));
@@ -91,6 +91,26 @@ const binding = req(locateBinding());
91
91
  */
92
92
  const BackgroundError = binding.BackgroundError;
93
93
  const config = binding.config;
94
+ /**
95
+ * Reads the live state of the process-wide `WriteBufferManager`: its budget,
96
+ * charged memory, mutable share, stall state, and attached column-family
97
+ * inventory. The manager is shared by every database and worker thread in the
98
+ * process.
99
+ *
100
+ * A WriteBufferManager stall does not pass through RocksDB's `WriteController`,
101
+ * so it is not reflected by `rocksdb.stall.micros`, `db.isWriteStalled()`, or
102
+ * the `'writeStall'` event. Use `stallActive` to distinguish this condition from
103
+ * an idle database.
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const wbm = getWriteBufferManagerStats();
108
+ * if (wbm.stallActive) {
109
+ * log.warn(`writes stalled for ${wbm.stallActiveMs}ms`);
110
+ * }
111
+ * ```
112
+ */
113
+ const getWriteBufferManagerStats = binding.getWriteBufferManagerStats;
94
114
  const FRESH_VERSION_FLAG$1 = binding.constants.FRESH_VERSION_FLAG;
95
115
  const addGlobalListener = binding.addListener;
96
116
  const removeGlobalListener = binding.removeListener;
@@ -139,6 +159,8 @@ const transactionLogMapCount = binding.transactionLogMapCount;
139
159
  * disarm. Used by the ERR_TRY_AGAIN retry regression test.
140
160
  */
141
161
  const forceTryAgainForTesting = binding.forceTryAgainForTesting;
162
+ /** Delays one selected watchdog join in the concurrent-shutdown regression test. */
163
+ const setWriteBufferManagerJoinDelayForTesting = binding.setWriteBufferManagerJoinDelayForTesting;
142
164
  /**
143
165
  * Creates a native file lock using the specified file path (`flock` on POSIX,
144
166
  * `LockFileEx` on Windows), creating the file and any missing parent
@@ -667,14 +689,12 @@ function parseDuration(duration) {
667
689
  num *= 1e3;
668
690
  break;
669
691
  case "m":
670
- num *= 1e3 * 60;
692
+ num *= 6e4;
671
693
  break;
672
694
  case "h":
673
- num *= 1e3 * 60 * 60;
674
- break;
675
- case "d":
676
- num *= 1e3 * 60 * 60 * 24;
695
+ num *= 36e5;
677
696
  break;
697
+ case "d": num *= 864e5;
678
698
  }
679
699
  result += num;
680
700
  }
@@ -1273,7 +1293,15 @@ var DBIterator = class {
1273
1293
  this.iterator.return?.();
1274
1294
  return DONE_RESULT;
1275
1295
  }
1276
- const result = this.iterator.next();
1296
+ let result;
1297
+ try {
1298
+ result = this.iterator.next();
1299
+ } catch (err) {
1300
+ try {
1301
+ this.iterator.return?.();
1302
+ } catch {}
1303
+ throw err;
1304
+ }
1277
1305
  if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
1278
1306
  this.#yielded++;
1279
1307
  const includeValues = this.#includeValues;
@@ -1377,10 +1405,10 @@ function createFixedBuffer(size) {
1377
1405
 
1378
1406
  //#endregion
1379
1407
  //#region src/store.ts
1380
- 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;
1408
+ 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;
1381
1409
  const KEY_BUFFER_SIZE = 4096;
1382
1410
  const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
1383
- const VALUE_BUFFER = createFixedBuffer(64 * 1024);
1411
+ const VALUE_BUFFER = createFixedBuffer(65536);
1384
1412
  /**
1385
1413
  * Backing buffer for the shared iterator state. Layout (Uint32Array view):
1386
1414
  * [0] = key length written into KEY_BUFFER by the most recent iterator step
@@ -1388,7 +1416,7 @@ const VALUE_BUFFER = createFixedBuffer(64 * 1024);
1388
1416
  */
1389
1417
  const ITERATOR_STATE_BUFFER = Buffer.allocUnsafeSlow(8);
1390
1418
  const ITERATOR_STATE = new Uint32Array(ITERATOR_STATE_BUFFER.buffer, ITERATOR_STATE_BUFFER.byteOffset, 2);
1391
- const MAX_KEY_SIZE = 1024 * 1024;
1419
+ const MAX_KEY_SIZE = 1048576;
1392
1420
  const SAVE_BUFFER_SIZE = 8192;
1393
1421
  /**
1394
1422
  * Normalizes the public `compression` option into the primitive fields the
@@ -1423,6 +1451,15 @@ function normalizeCompression(option) {
1423
1451
  };
1424
1452
  }
1425
1453
  /**
1454
+ * Whether `codec` is a pre-constructed encoder/decoder instance that `open()`
1455
+ * would reuse as-is (rather than instantiate fresh from an `Encoder` factory).
1456
+ * Such an instance carries per-column-family mutable state (its `name`, msgpack
1457
+ * `structures`), so it cannot be shared across column families.
1458
+ */
1459
+ function sharesMutableCodec(codec) {
1460
+ return codec != null && typeof codec === "object" && typeof codec.Encoder !== "function" && (typeof codec.encode === "function" || typeof codec.decode === "function");
1461
+ }
1462
+ /**
1426
1463
  * A store wraps the `NativeDatabase` binding and database settings so that a
1427
1464
  * single database instance can be shared between the main `RocksDatabase`
1428
1465
  * instance and the `Transaction` instance.
@@ -1441,6 +1478,13 @@ var Store = class {
1441
1478
  */
1442
1479
  #claimed = false;
1443
1480
  /**
1481
+ * Shallow snapshot of the options this store was constructed with, used by
1482
+ * `createColumnFamilyStore()` to derive sibling column-family stores. Snapshot
1483
+ * (not the caller's object) so later mutations of the caller's options don't
1484
+ * leak into derived views.
1485
+ */
1486
+ #columnFamilyOptions;
1487
+ /**
1444
1488
  * The database instance.
1445
1489
  */
1446
1490
  db;
@@ -1535,10 +1579,16 @@ var Store = class {
1535
1579
  /**
1536
1580
  * The bytes of recent memtable history to retain in memory for transaction
1537
1581
  * conflict checking. `-1` derives the value from
1538
- * `maxWriteBufferNumber * writeBufferSize`, except under a stalling
1539
- * `writeBufferManager`, where it derives `0`: retained history is charged to
1540
- * that manager and is only trimmable down to this target, so a target the
1541
- * budget cannot hold stalls writes permanently. Set a number to size it
1582
+ * `maxWriteBufferNumber * writeBufferSize`, except when a
1583
+ * `writeBufferManager` is configured, where it derives `1`: retained history
1584
+ * is charged to that manager and is only trimmable down to this target, so a
1585
+ * target the budget cannot hold is memory the manager never reclaims, and
1586
+ * stalls writes permanently when `writeBufferManagerAllowStall` is on. Any
1587
+ * configured manager, not only a stalling one — that flag is mutable at
1588
+ * runtime while this value is fixed when the family is created. `1`, not `0`, because
1589
+ * RocksDB's transaction wrappers rewrite a `0` target to the derived value —
1590
+ * so `0` asks for the largest history rather than none, and an explicit `0`
1591
+ * is normalized to `1` for that reason. Set a positive number to size it
1542
1592
  * yourself against the budget and the column-family count.
1543
1593
  */
1544
1594
  maxWriteBufferSizeToMaintain;
@@ -1576,6 +1626,11 @@ var Store = class {
1576
1626
  */
1577
1627
  path;
1578
1628
  /**
1629
+ * The open database's resolved filesystem identity, read once from
1630
+ * `NativeDatabase.identityPath`. `undefined` until opened.
1631
+ */
1632
+ identityPath;
1633
+ /**
1579
1634
  * Whether to use pessimistic locking for transactions. When `true`,
1580
1635
  * transactions will fail as soon as a conflict is detected. When `false`,
1581
1636
  * transactions will only fail when `commit()` is called.
@@ -1589,6 +1644,13 @@ var Store = class {
1589
1644
  */
1590
1645
  readOnly;
1591
1646
  /**
1647
+ * The secondary instance's workspace directory when the database is opened
1648
+ * as a secondary (see `RocksDatabaseOptions.secondaryPath`); `undefined`
1649
+ * otherwise. A secondary is read-only, so `readOnly` is `true` whenever
1650
+ * this is set.
1651
+ */
1652
+ secondaryPath;
1653
+ /**
1592
1654
  * Encoder specific flag used to signal that the encoder should use a random
1593
1655
  * access structure.
1594
1656
  */
@@ -1674,7 +1736,10 @@ var Store = class {
1674
1736
  this.parallelismThreads = options?.parallelismThreads;
1675
1737
  this.path = path;
1676
1738
  this.pessimistic = options?.pessimistic ?? false;
1677
- this.readOnly = options?.readOnly ?? false;
1739
+ const secondaryPath = options?.secondaryPath ?? void 0;
1740
+ if (secondaryPath !== void 0 && options?.readOnly === false) throw new Error("A secondary open is read-only; secondaryPath cannot be combined with readOnly: false");
1741
+ this.readOnly = options?.readOnly ?? secondaryPath !== void 0;
1742
+ this.secondaryPath = secondaryPath;
1678
1743
  this.randomAccessStructure = options?.randomAccessStructure ?? false;
1679
1744
  this.readKey = readKey;
1680
1745
  this.sharedStructuresKey = options?.sharedStructuresKey;
@@ -1686,6 +1751,36 @@ var Store = class {
1686
1751
  this.verificationTable = options?.verificationTable;
1687
1752
  this.writeBufferSize = options?.writeBufferSize;
1688
1753
  this.writeKey = writeKey;
1754
+ this.#columnFamilyOptions = options ? { ...options } : void 0;
1755
+ }
1756
+ /**
1757
+ * Creates an independent `Store` for another column family of the same
1758
+ * database, used by {@link RocksDatabase#use}. The returned store shares the
1759
+ * underlying database but must have its own codec state: a mutable
1760
+ * encoder/decoder instance cannot be shared across column families (opening a
1761
+ * view mutates the encoder's `name`, and clearing a view resets its
1762
+ * `structures`), so a pre-constructed instance is rejected here — supply an
1763
+ * encoder factory (`{ Encoder }`) or a named `encoding` instead.
1764
+ *
1765
+ * Override this in a `Store` subclass whose constructor cannot be recreated
1766
+ * from `(path, options)` (e.g. it takes injected dependencies) or that needs
1767
+ * to control how a view's codec state is built.
1768
+ *
1769
+ * @param name - The column family name for the derived store.
1770
+ * @param options - Options overriding this store's, for the derived store.
1771
+ * @returns A new, independent `Store` bound to `name`.
1772
+ */
1773
+ createColumnFamilyStore(name, options) {
1774
+ const derived = {
1775
+ ...this.#columnFamilyOptions,
1776
+ ...options,
1777
+ name
1778
+ };
1779
+ 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`.");
1780
+ const StoreClass = this.constructor;
1781
+ const store = new StoreClass(this.path, derived);
1782
+ 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.`);
1783
+ return store;
1689
1784
  }
1690
1785
  /**
1691
1786
  * Closes the database.
@@ -1867,6 +1962,7 @@ var Store = class {
1867
1962
  let endUnencoded = options?.key ?? options?.end;
1868
1963
  const includeValues = options?.values ?? true;
1869
1964
  const reverse = options?.reverse ?? false;
1965
+ const txnId = this.getTxnId(options);
1870
1966
  let exclusiveStart = options?.exclusiveStart ?? false;
1871
1967
  let inclusiveEnd = options?.inclusiveEnd ?? false;
1872
1968
  if (options?.key !== void 0) inclusiveEnd = true;
@@ -1885,13 +1981,15 @@ var Store = class {
1885
1981
  startKeyEnd = this.writeKey(startUnencoded, keyBuffer, 0);
1886
1982
  if (startKeyEnd === 0) throw new Error("Zero length key is not allowed");
1887
1983
  }
1888
- if (endUnencoded !== void 0) if (endUnencoded === startUnencoded) {
1889
- endKeyStart = 0;
1890
- endKeyEnd = startKeyEnd;
1891
- } else {
1892
- endKeyStart = startKeyEnd;
1893
- endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
1894
- if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
1984
+ if (endUnencoded !== void 0) {
1985
+ if (endUnencoded === startUnencoded) {
1986
+ endKeyStart = 0;
1987
+ endKeyEnd = startKeyEnd;
1988
+ } else {
1989
+ endKeyStart = startKeyEnd;
1990
+ endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
1991
+ if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
1992
+ }
1895
1993
  }
1896
1994
  let flags = 0;
1897
1995
  if (reverse) flags |= ITERATOR_REVERSE_FLAG;
@@ -1900,8 +1998,9 @@ var Store = class {
1900
1998
  if (includeValues) flags |= ITERATOR_INCLUDE_VALUES_FLAG;
1901
1999
  if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
1902
2000
  if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
2001
+ if (txnId !== void 0) flags |= ITERATOR_HAS_TRANSACTION_ID_FLAG;
1903
2002
  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;
1904
- return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues, options?.limit));
2003
+ return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId), this, includeValues, options?.limit));
1905
2004
  }
1906
2005
  getSync(context, key, alwaysCreateNewBuffer = false, options) {
1907
2006
  const keyParam = getKeyParam(this.encodeKey(key));
@@ -1922,9 +2021,12 @@ var Store = class {
1922
2021
  */
1923
2022
  getTxnId(options) {
1924
2023
  let txnId;
1925
- if (!this.readOnly && options?.transaction) {
1926
- txnId = options.transaction.id;
2024
+ const transaction = options?.transaction;
2025
+ if (!this.readOnly && transaction) {
2026
+ txnId = transaction.id;
1927
2027
  if (txnId === void 0) throw new TypeError("Invalid transaction");
2028
+ if (transaction.store === void 0) throw new TypeError("Invalid transaction");
2029
+ if (this.identityPath !== void 0 && transaction.store.identityPath !== this.identityPath) throw new TypeError("Transaction belongs to a different database");
1928
2030
  }
1929
2031
  return txnId;
1930
2032
  }
@@ -1982,7 +2084,10 @@ var Store = class {
1982
2084
  * are performed.
1983
2085
  */
1984
2086
  open() {
1985
- if (this.db.opened) return true;
2087
+ if (this.db.opened) {
2088
+ this.identityPath = this.db.identityPath;
2089
+ return true;
2090
+ }
1986
2091
  const { compression, compressionLevel } = normalizeCompression(this.compression);
1987
2092
  this.db.open(this.path, {
1988
2093
  compression,
@@ -2001,6 +2106,7 @@ var Store = class {
2001
2106
  noBlockCache: this.noBlockCache,
2002
2107
  parallelismThreads: this.parallelismThreads,
2003
2108
  readOnly: this.readOnly,
2109
+ secondaryPath: this.secondaryPath,
2004
2110
  statsLevel: this.statsLevel,
2005
2111
  transactionLogMaxAgeThreshold: this.transactionLogMaxAgeThreshold,
2006
2112
  transactionLogMaxSize: this.transactionLogMaxSize,
@@ -2009,6 +2115,7 @@ var Store = class {
2009
2115
  verificationTable: this.verificationTable,
2010
2116
  writeBufferSize: this.writeBufferSize
2011
2117
  });
2118
+ this.identityPath = this.db.identityPath;
2012
2119
  return false;
2013
2120
  }
2014
2121
  putSync(context, key, value, options) {
@@ -2269,10 +2376,12 @@ var Transaction = class extends DBI {
2269
2376
  return err;
2270
2377
  }
2271
2378
  /**
2272
- * Returns the transaction start timestamp in seconds. Defaults to the time at which
2273
- * the transaction was created.
2379
+ * Returns the transaction timestamp in milliseconds since the Unix epoch. It
2380
+ * defaults to a process-wide monotonic value assigned when the transaction
2381
+ * was created. A producer may use this value as its write identity; the
2382
+ * transaction log uses it as the batch key.
2274
2383
  *
2275
- * @returns The transaction start timestamp in seconds.
2384
+ * @returns The transaction timestamp in milliseconds.
2276
2385
  */
2277
2386
  getTimestamp() {
2278
2387
  return this.#txn.getTimestamp();
@@ -2284,9 +2393,14 @@ var Transaction = class extends DBI {
2284
2393
  return this.#txn.id;
2285
2394
  }
2286
2395
  /**
2287
- * Set the transaction start timestamp in seconds.
2396
+ * Overrides the transaction timestamp in milliseconds since the Unix epoch.
2397
+ * Call this before reading the timestamp or staging writes when adopting an
2398
+ * origin timestamp for replication or replay. Once a write or log entry is
2399
+ * staged, the timestamp is frozen, but reapplying the same timestamp remains
2400
+ * idempotent while the transaction is pending. The value must be finite,
2401
+ * positive, and below 8640000000000000.
2288
2402
  *
2289
- * @param timestamp - The timestamp to set in seconds.
2403
+ * @param timestamp - The timestamp to set in milliseconds.
2290
2404
  */
2291
2405
  setTimestamp(timestamp) {
2292
2406
  this.#txn.setTimestamp(timestamp);
@@ -2320,9 +2434,23 @@ function backgroundErrorSeverityName(severity) {
2320
2434
  */
2321
2435
  var RocksDatabase = class RocksDatabase extends DBI {
2322
2436
  /**
2323
- * The name of the database.
2437
+ * The name of the database's column family.
2324
2438
  */
2325
2439
  #name;
2440
+ /**
2441
+ * Weak cache of column-family views opened via `use()`, keyed by column
2442
+ * family name — mirroring `useLog`'s get-or-create semantics. The reference
2443
+ * is weak so a view is never pinned for this database's lifetime: an
2444
+ * unreferenced view can be collected (closing its own `DBHandle`), and the
2445
+ * next `use()` transparently recreates it. `#columnFamilyRegistry` deletes an
2446
+ * entry once its view is collected so a churn of distinct names (e.g.
2447
+ * `use(uuid())`) does not grow the map without bound.
2448
+ */
2449
+ #columnFamilies = /* @__PURE__ */ new Map();
2450
+ #columnFamilyRegistry = new FinalizationRegistry((name) => {
2451
+ const ref = this.#columnFamilies.get(name);
2452
+ if (ref !== void 0 && ref.deref() === void 0) this.#columnFamilies.delete(name);
2453
+ });
2326
2454
  constructor(pathOrStore, options) {
2327
2455
  let store;
2328
2456
  if (typeof pathOrStore === "string") store = new Store(pathOrStore, options);
@@ -2330,7 +2458,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
2330
2458
  else throw new TypeError("Invalid database path or store");
2331
2459
  store.claim();
2332
2460
  super(store);
2333
- this.#name = options?.name ?? "default";
2461
+ this.#name = this.store.name;
2334
2462
  }
2335
2463
  /**
2336
2464
  * Removes all data from the database asynchronously.
@@ -2549,6 +2677,40 @@ var RocksDatabase = class RocksDatabase extends DBI {
2549
2677
  static notify(event, ...args) {
2550
2678
  return globalNotify(event, args);
2551
2679
  }
2680
+ /**
2681
+ * Advances a secondary instance to the primary's current state by tailing
2682
+ * and replaying the primary's MANIFEST and WAL. A secondary does not see
2683
+ * the primary's writes until this (or {@link catchUpWithPrimarySync}) is
2684
+ * called. Concurrent reads through this handle remain safe while the
2685
+ * catch-up runs.
2686
+ *
2687
+ * Only valid on a database opened with the `secondaryPath` option; throws
2688
+ * with code `ERR_NOT_SECONDARY` otherwise.
2689
+ *
2690
+ * Advances the **database** view only. Against a cross-process primary,
2691
+ * transaction-log reads keep serving the stores discovered at open (a store
2692
+ * the primary creates later, and its appends, need a reopen); a primary in
2693
+ * the same process shares the store registry, so its stores and appends are
2694
+ * visible immediately. Either way a log entry can describe data this
2695
+ * handle's database view does not have yet — the log write completes before
2696
+ * the RocksDB commit for every writer, so consumers must tolerate the log
2697
+ * leading the database.
2698
+ *
2699
+ * Await one call before starting the next: the replay holds a libuv worker
2700
+ * for its whole duration and concurrent calls queue on a per-database mutex
2701
+ * while holding theirs, so overlapping catch-ups on a backlogged follower
2702
+ * can exhaust the default four-thread pool and stall unrelated I/O.
2703
+ */
2704
+ catchUpWithPrimary() {
2705
+ return new Promise((resolve, reject) => this.store.db.catchUpWithPrimary(resolve, reject));
2706
+ }
2707
+ /**
2708
+ * Synchronous version of {@link catchUpWithPrimary}. The replay runs on the
2709
+ * JS thread, so prefer the async form unless the caller is already blocking.
2710
+ */
2711
+ catchUpWithPrimarySync() {
2712
+ this.store.db.catchUpWithPrimarySync();
2713
+ }
2552
2714
  destroy() {
2553
2715
  this.store.db.destroy();
2554
2716
  }
@@ -2701,6 +2863,29 @@ var RocksDatabase = class RocksDatabase extends DBI {
2701
2863
  return this.store.db.getDBProperty(propertyName);
2702
2864
  }
2703
2865
  /**
2866
+ * Whether RocksDB is currently applying write backpressure to this database —
2867
+ * either delaying (rate-limiting) or fully stopping writes. This is the
2868
+ * authoritative, live pull counterpart to the `'writeStall'`
2869
+ * [event](../README.md#event-writestall): the event pushes per-column-family
2870
+ * transitions (and is rate-limited), while this reads the current state on
2871
+ * demand and can never be stale.
2872
+ *
2873
+ * The write controller is database-wide (shared across every column family),
2874
+ * so this reflects the database as a whole, not a single column family — a
2875
+ * stall triggered by any column family stops writes for all of them. Reads
2876
+ * `rocksdb.is-write-stopped` and `rocksdb.actual-delayed-write-rate`.
2877
+ *
2878
+ * @example
2879
+ * ```typescript
2880
+ * db.on('writeStall', (cf) => {
2881
+ * if (db.isWriteStalled()) log.warn(`writes throttled/blocked (triggered by ${cf})`);
2882
+ * });
2883
+ * ```
2884
+ */
2885
+ isWriteStalled() {
2886
+ return this.getDBIntProperty("rocksdb.is-write-stopped") === 1 || (this.getDBIntProperty("rocksdb.actual-delayed-write-rate") ?? 0) > 0;
2887
+ }
2888
+ /**
2704
2889
  * Retrieves the estimated number of keys in the database. This is an alias
2705
2890
  * for `db.estimateCount().count`; use `estimateCount()` for range support
2706
2891
  * and a confidence indicator.
@@ -2878,18 +3063,29 @@ var RocksDatabase = class RocksDatabase extends DBI {
2878
3063
  return this.store.listLogs();
2879
3064
  }
2880
3065
  /**
2881
- * The name of the database.
3066
+ * The name of the database's column family.
2882
3067
  */
2883
3068
  get name() {
2884
3069
  return this.#name;
2885
3070
  }
2886
3071
  /**
2887
- * Whether the database is open in readonly mode.
3072
+ * Whether the database is open in readonly mode. Also `true` for a
3073
+ * secondary instance (see {@link secondaryPath}), which is read-only by
3074
+ * construction.
2888
3075
  */
2889
3076
  get readOnly() {
2890
3077
  return this.store.readOnly;
2891
3078
  }
2892
3079
  /**
3080
+ * The secondary instance's workspace directory when the database was opened
3081
+ * as a secondary (a read-only follower of a live primary — see the
3082
+ * `secondaryPath` open option), or `undefined` for a regular or plain
3083
+ * read-only open.
3084
+ */
3085
+ get secondaryPath() {
3086
+ return this.store.secondaryPath;
3087
+ }
3088
+ /**
2893
3089
  * Sugar method for opening a database.
2894
3090
  *
2895
3091
  * @param pathOrStore - The filesystem path to the database or a custom store.
@@ -3162,6 +3358,57 @@ var RocksDatabase = class RocksDatabase extends DBI {
3162
3358
  return this.store.unlock(key);
3163
3359
  }
3164
3360
  /**
3361
+ * Returns a `RocksDatabase` bound to the `name` column family of this same
3362
+ * database, opening (and creating, if missing) the column family on first
3363
+ * use. This is a factory — create-and-open sugar — not a stateful switch: the
3364
+ * returned view is an independent instance whose own reads/writes target its
3365
+ * column family, while sharing the underlying database (so a transaction,
3366
+ * backup, or checkpoint still spans every column family). Because the view
3367
+ * opens its own handle, this database need not itself be open — a discarded
3368
+ * parent (`new RocksDatabase(path).use('events')`) still yields a valid,
3369
+ * opened view.
3370
+ *
3371
+ * Get-or-create, backed by a weak cache: `db.use('events') === db.use('events')`
3372
+ * while the view is still referenced **and open**, and calling with this
3373
+ * database's own column-family name returns `this`. A view that has been
3374
+ * closed (or garbage-collected — the cache does not pin it) is transparently
3375
+ * recreated on the next `use()`. Views are independent handles: closing this
3376
+ * database does not close them, and vice versa — the underlying database
3377
+ * stays open until every handle is closed or collected.
3378
+ *
3379
+ * The view's store is derived by {@link Store#createColumnFamilyStore}, which
3380
+ * builds an independent store of the same class with its own codec state (so
3381
+ * views never share a mutable encoder/decoder) and inherits the options this
3382
+ * store was constructed with; pass `options` to override them for this column
3383
+ * family. A database configured with a pre-constructed encoder/decoder
3384
+ * instance cannot derive views (its state would be shared) — use an encoder
3385
+ * factory (`{ Encoder }`) or a named `encoding`, or override
3386
+ * `createColumnFamilyStore` on a custom `Store`.
3387
+ *
3388
+ * @param name - The column family name.
3389
+ * @param options - Options for the column family, overriding inherited ones.
3390
+ * @returns A `RocksDatabase` bound to the column family.
3391
+ *
3392
+ * @example
3393
+ * ```typescript
3394
+ * const db = RocksDatabase.open('/path/to/database');
3395
+ * const events = db.use('events');
3396
+ * await events.put('e1', payload);
3397
+ * await db.put('k', 'v'); // default column family, unaffected
3398
+ * ```
3399
+ */
3400
+ use(name, options) {
3401
+ if (typeof name !== "string" || name === "") throw new TypeError("Column family name must be a non-empty string");
3402
+ if (name === this.#name) return this.open();
3403
+ const cached = this.#columnFamilies.get(name)?.deref();
3404
+ if (cached !== void 0 && cached.isOpen()) return cached;
3405
+ const columnFamily = new RocksDatabase(this.store.createColumnFamilyStore(name, options));
3406
+ columnFamily.open();
3407
+ this.#columnFamilies.set(name, new WeakRef(columnFamily));
3408
+ this.#columnFamilyRegistry.register(columnFamily, name);
3409
+ return columnFamily;
3410
+ }
3411
+ /**
3165
3412
  * Excecutes a function using a thread-safe lock to ensure mutual
3166
3413
  * exclusion.
3167
3414
  *
@@ -3468,11 +3715,12 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3468
3715
  position += TRANSACTION_LOG_ENTRY_HEADER_SIZE;
3469
3716
  let matchesRange;
3470
3717
  if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
3471
- else if (exactStart) if (timestamp === start) {
3472
- matchesRange = !exclusiveStart;
3473
- foundExactStart = true;
3474
- } else matchesRange = false;
3475
- else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
3718
+ else if (exactStart) {
3719
+ if (timestamp === start) {
3720
+ matchesRange = !exclusiveStart;
3721
+ foundExactStart = true;
3722
+ } else matchesRange = false;
3723
+ } else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
3476
3724
  const entryStart = position;
3477
3725
  position += length;
3478
3726
  if (matchesRange) return {
@@ -3555,7 +3803,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
3555
3803
  //#region src/index.ts
3556
3804
  const versions = {
3557
3805
  rocksdb: version,
3558
- "rocksdb-js": "2.8.0"
3806
+ "rocksdb-js": "2.9.1"
3559
3807
  };
3560
3808
 
3561
3809
  //#endregion
@@ -3573,6 +3821,7 @@ exports.constants = constants;
3573
3821
  exports.coolTransactionLogs = coolTransactionLogs;
3574
3822
  exports.currentThreadId = currentThreadId;
3575
3823
  exports.fileLockRelease = fileLockRelease;
3824
+ exports.getWriteBufferManagerStats = getWriteBufferManagerStats;
3576
3825
  exports.parseTransactionLog = parseTransactionLog;
3577
3826
  exports.registryStatus = registryStatus;
3578
3827
  exports.shutdown = shutdown;