@harperfast/rocksdb-js 2.8.0 → 2.9.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 +337 -19
- package/dist/index.cjs +271 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +317 -34
- package/dist/index.d.mts +317 -34
- package/dist/index.mjs +270 -36
- package/dist/index.mjs.map +1 -1
- package/docs/stats.md +46 -1
- package/docs/transaction-log.md +18 -5
- package/docs/transaction-timestamp-integrity-design.md +123 -0
- package/package.json +19 -19
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 *=
|
|
667
|
+
num *= 6e4;
|
|
646
668
|
break;
|
|
647
669
|
case "h":
|
|
648
|
-
num *=
|
|
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
|
}
|
|
@@ -1352,10 +1372,10 @@ function createFixedBuffer(size) {
|
|
|
1352
1372
|
|
|
1353
1373
|
//#endregion
|
|
1354
1374
|
//#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;
|
|
1375
|
+
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
1376
|
const KEY_BUFFER_SIZE = 4096;
|
|
1357
1377
|
const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
|
|
1358
|
-
const VALUE_BUFFER = createFixedBuffer(
|
|
1378
|
+
const VALUE_BUFFER = createFixedBuffer(65536);
|
|
1359
1379
|
/**
|
|
1360
1380
|
* Backing buffer for the shared iterator state. Layout (Uint32Array view):
|
|
1361
1381
|
* [0] = key length written into KEY_BUFFER by the most recent iterator step
|
|
@@ -1363,7 +1383,7 @@ const VALUE_BUFFER = createFixedBuffer(64 * 1024);
|
|
|
1363
1383
|
*/
|
|
1364
1384
|
const ITERATOR_STATE_BUFFER = Buffer.allocUnsafeSlow(8);
|
|
1365
1385
|
const ITERATOR_STATE = new Uint32Array(ITERATOR_STATE_BUFFER.buffer, ITERATOR_STATE_BUFFER.byteOffset, 2);
|
|
1366
|
-
const MAX_KEY_SIZE =
|
|
1386
|
+
const MAX_KEY_SIZE = 1048576;
|
|
1367
1387
|
const SAVE_BUFFER_SIZE = 8192;
|
|
1368
1388
|
/**
|
|
1369
1389
|
* Normalizes the public `compression` option into the primitive fields the
|
|
@@ -1398,6 +1418,15 @@ function normalizeCompression(option) {
|
|
|
1398
1418
|
};
|
|
1399
1419
|
}
|
|
1400
1420
|
/**
|
|
1421
|
+
* Whether `codec` is a pre-constructed encoder/decoder instance that `open()`
|
|
1422
|
+
* would reuse as-is (rather than instantiate fresh from an `Encoder` factory).
|
|
1423
|
+
* Such an instance carries per-column-family mutable state (its `name`, msgpack
|
|
1424
|
+
* `structures`), so it cannot be shared across column families.
|
|
1425
|
+
*/
|
|
1426
|
+
function sharesMutableCodec(codec) {
|
|
1427
|
+
return codec != null && typeof codec === "object" && typeof codec.Encoder !== "function" && (typeof codec.encode === "function" || typeof codec.decode === "function");
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1401
1430
|
* A store wraps the `NativeDatabase` binding and database settings so that a
|
|
1402
1431
|
* single database instance can be shared between the main `RocksDatabase`
|
|
1403
1432
|
* instance and the `Transaction` instance.
|
|
@@ -1416,6 +1445,13 @@ var Store = class {
|
|
|
1416
1445
|
*/
|
|
1417
1446
|
#claimed = false;
|
|
1418
1447
|
/**
|
|
1448
|
+
* Shallow snapshot of the options this store was constructed with, used by
|
|
1449
|
+
* `createColumnFamilyStore()` to derive sibling column-family stores. Snapshot
|
|
1450
|
+
* (not the caller's object) so later mutations of the caller's options don't
|
|
1451
|
+
* leak into derived views.
|
|
1452
|
+
*/
|
|
1453
|
+
#columnFamilyOptions;
|
|
1454
|
+
/**
|
|
1419
1455
|
* The database instance.
|
|
1420
1456
|
*/
|
|
1421
1457
|
db;
|
|
@@ -1551,6 +1587,11 @@ var Store = class {
|
|
|
1551
1587
|
*/
|
|
1552
1588
|
path;
|
|
1553
1589
|
/**
|
|
1590
|
+
* The open database's resolved filesystem identity, read once from
|
|
1591
|
+
* `NativeDatabase.identityPath`. `undefined` until opened.
|
|
1592
|
+
*/
|
|
1593
|
+
identityPath;
|
|
1594
|
+
/**
|
|
1554
1595
|
* Whether to use pessimistic locking for transactions. When `true`,
|
|
1555
1596
|
* transactions will fail as soon as a conflict is detected. When `false`,
|
|
1556
1597
|
* transactions will only fail when `commit()` is called.
|
|
@@ -1564,6 +1605,13 @@ var Store = class {
|
|
|
1564
1605
|
*/
|
|
1565
1606
|
readOnly;
|
|
1566
1607
|
/**
|
|
1608
|
+
* The secondary instance's workspace directory when the database is opened
|
|
1609
|
+
* as a secondary (see `RocksDatabaseOptions.secondaryPath`); `undefined`
|
|
1610
|
+
* otherwise. A secondary is read-only, so `readOnly` is `true` whenever
|
|
1611
|
+
* this is set.
|
|
1612
|
+
*/
|
|
1613
|
+
secondaryPath;
|
|
1614
|
+
/**
|
|
1567
1615
|
* Encoder specific flag used to signal that the encoder should use a random
|
|
1568
1616
|
* access structure.
|
|
1569
1617
|
*/
|
|
@@ -1649,7 +1697,10 @@ var Store = class {
|
|
|
1649
1697
|
this.parallelismThreads = options?.parallelismThreads;
|
|
1650
1698
|
this.path = path;
|
|
1651
1699
|
this.pessimistic = options?.pessimistic ?? false;
|
|
1652
|
-
|
|
1700
|
+
const secondaryPath = options?.secondaryPath ?? void 0;
|
|
1701
|
+
if (secondaryPath !== void 0 && options?.readOnly === false) throw new Error("A secondary open is read-only; secondaryPath cannot be combined with readOnly: false");
|
|
1702
|
+
this.readOnly = options?.readOnly ?? secondaryPath !== void 0;
|
|
1703
|
+
this.secondaryPath = secondaryPath;
|
|
1653
1704
|
this.randomAccessStructure = options?.randomAccessStructure ?? false;
|
|
1654
1705
|
this.readKey = readKey;
|
|
1655
1706
|
this.sharedStructuresKey = options?.sharedStructuresKey;
|
|
@@ -1661,6 +1712,36 @@ var Store = class {
|
|
|
1661
1712
|
this.verificationTable = options?.verificationTable;
|
|
1662
1713
|
this.writeBufferSize = options?.writeBufferSize;
|
|
1663
1714
|
this.writeKey = writeKey;
|
|
1715
|
+
this.#columnFamilyOptions = options ? { ...options } : void 0;
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Creates an independent `Store` for another column family of the same
|
|
1719
|
+
* database, used by {@link RocksDatabase#use}. The returned store shares the
|
|
1720
|
+
* underlying database but must have its own codec state: a mutable
|
|
1721
|
+
* encoder/decoder instance cannot be shared across column families (opening a
|
|
1722
|
+
* view mutates the encoder's `name`, and clearing a view resets its
|
|
1723
|
+
* `structures`), so a pre-constructed instance is rejected here — supply an
|
|
1724
|
+
* encoder factory (`{ Encoder }`) or a named `encoding` instead.
|
|
1725
|
+
*
|
|
1726
|
+
* Override this in a `Store` subclass whose constructor cannot be recreated
|
|
1727
|
+
* from `(path, options)` (e.g. it takes injected dependencies) or that needs
|
|
1728
|
+
* to control how a view's codec state is built.
|
|
1729
|
+
*
|
|
1730
|
+
* @param name - The column family name for the derived store.
|
|
1731
|
+
* @param options - Options overriding this store's, for the derived store.
|
|
1732
|
+
* @returns A new, independent `Store` bound to `name`.
|
|
1733
|
+
*/
|
|
1734
|
+
createColumnFamilyStore(name, options) {
|
|
1735
|
+
const derived = {
|
|
1736
|
+
...this.#columnFamilyOptions,
|
|
1737
|
+
...options,
|
|
1738
|
+
name
|
|
1739
|
+
};
|
|
1740
|
+
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`.");
|
|
1741
|
+
const StoreClass = this.constructor;
|
|
1742
|
+
const store = new StoreClass(this.path, derived);
|
|
1743
|
+
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.`);
|
|
1744
|
+
return store;
|
|
1664
1745
|
}
|
|
1665
1746
|
/**
|
|
1666
1747
|
* Closes the database.
|
|
@@ -1842,6 +1923,7 @@ var Store = class {
|
|
|
1842
1923
|
let endUnencoded = options?.key ?? options?.end;
|
|
1843
1924
|
const includeValues = options?.values ?? true;
|
|
1844
1925
|
const reverse = options?.reverse ?? false;
|
|
1926
|
+
const txnId = this.getTxnId(options);
|
|
1845
1927
|
let exclusiveStart = options?.exclusiveStart ?? false;
|
|
1846
1928
|
let inclusiveEnd = options?.inclusiveEnd ?? false;
|
|
1847
1929
|
if (options?.key !== void 0) inclusiveEnd = true;
|
|
@@ -1860,13 +1942,15 @@ var Store = class {
|
|
|
1860
1942
|
startKeyEnd = this.writeKey(startUnencoded, keyBuffer, 0);
|
|
1861
1943
|
if (startKeyEnd === 0) throw new Error("Zero length key is not allowed");
|
|
1862
1944
|
}
|
|
1863
|
-
if (endUnencoded !== void 0)
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1945
|
+
if (endUnencoded !== void 0) {
|
|
1946
|
+
if (endUnencoded === startUnencoded) {
|
|
1947
|
+
endKeyStart = 0;
|
|
1948
|
+
endKeyEnd = startKeyEnd;
|
|
1949
|
+
} else {
|
|
1950
|
+
endKeyStart = startKeyEnd;
|
|
1951
|
+
endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
|
|
1952
|
+
if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
|
|
1953
|
+
}
|
|
1870
1954
|
}
|
|
1871
1955
|
let flags = 0;
|
|
1872
1956
|
if (reverse) flags |= ITERATOR_REVERSE_FLAG;
|
|
@@ -1875,8 +1959,9 @@ var Store = class {
|
|
|
1875
1959
|
if (includeValues) flags |= ITERATOR_INCLUDE_VALUES_FLAG;
|
|
1876
1960
|
if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
|
|
1877
1961
|
if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
|
|
1962
|
+
if (txnId !== void 0) flags |= ITERATOR_HAS_TRANSACTION_ID_FLAG;
|
|
1878
1963
|
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));
|
|
1964
|
+
return new ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId), this, includeValues, options?.limit));
|
|
1880
1965
|
}
|
|
1881
1966
|
getSync(context, key, alwaysCreateNewBuffer = false, options) {
|
|
1882
1967
|
const keyParam = getKeyParam(this.encodeKey(key));
|
|
@@ -1897,9 +1982,12 @@ var Store = class {
|
|
|
1897
1982
|
*/
|
|
1898
1983
|
getTxnId(options) {
|
|
1899
1984
|
let txnId;
|
|
1900
|
-
|
|
1901
|
-
|
|
1985
|
+
const transaction = options?.transaction;
|
|
1986
|
+
if (!this.readOnly && transaction) {
|
|
1987
|
+
txnId = transaction.id;
|
|
1902
1988
|
if (txnId === void 0) throw new TypeError("Invalid transaction");
|
|
1989
|
+
if (transaction.store === void 0) throw new TypeError("Invalid transaction");
|
|
1990
|
+
if (this.identityPath !== void 0 && transaction.store.identityPath !== this.identityPath) throw new TypeError("Transaction belongs to a different database");
|
|
1903
1991
|
}
|
|
1904
1992
|
return txnId;
|
|
1905
1993
|
}
|
|
@@ -1957,7 +2045,10 @@ var Store = class {
|
|
|
1957
2045
|
* are performed.
|
|
1958
2046
|
*/
|
|
1959
2047
|
open() {
|
|
1960
|
-
if (this.db.opened)
|
|
2048
|
+
if (this.db.opened) {
|
|
2049
|
+
this.identityPath = this.db.identityPath;
|
|
2050
|
+
return true;
|
|
2051
|
+
}
|
|
1961
2052
|
const { compression, compressionLevel } = normalizeCompression(this.compression);
|
|
1962
2053
|
this.db.open(this.path, {
|
|
1963
2054
|
compression,
|
|
@@ -1976,6 +2067,7 @@ var Store = class {
|
|
|
1976
2067
|
noBlockCache: this.noBlockCache,
|
|
1977
2068
|
parallelismThreads: this.parallelismThreads,
|
|
1978
2069
|
readOnly: this.readOnly,
|
|
2070
|
+
secondaryPath: this.secondaryPath,
|
|
1979
2071
|
statsLevel: this.statsLevel,
|
|
1980
2072
|
transactionLogMaxAgeThreshold: this.transactionLogMaxAgeThreshold,
|
|
1981
2073
|
transactionLogMaxSize: this.transactionLogMaxSize,
|
|
@@ -1984,6 +2076,7 @@ var Store = class {
|
|
|
1984
2076
|
verificationTable: this.verificationTable,
|
|
1985
2077
|
writeBufferSize: this.writeBufferSize
|
|
1986
2078
|
});
|
|
2079
|
+
this.identityPath = this.db.identityPath;
|
|
1987
2080
|
return false;
|
|
1988
2081
|
}
|
|
1989
2082
|
putSync(context, key, value, options) {
|
|
@@ -2244,10 +2337,12 @@ var Transaction = class extends DBI {
|
|
|
2244
2337
|
return err;
|
|
2245
2338
|
}
|
|
2246
2339
|
/**
|
|
2247
|
-
* Returns the transaction
|
|
2248
|
-
* the transaction
|
|
2340
|
+
* Returns the transaction timestamp in milliseconds since the Unix epoch. It
|
|
2341
|
+
* defaults to a process-wide monotonic value assigned when the transaction
|
|
2342
|
+
* was created. A producer may use this value as its write identity; the
|
|
2343
|
+
* transaction log uses it as the batch key.
|
|
2249
2344
|
*
|
|
2250
|
-
* @returns The transaction
|
|
2345
|
+
* @returns The transaction timestamp in milliseconds.
|
|
2251
2346
|
*/
|
|
2252
2347
|
getTimestamp() {
|
|
2253
2348
|
return this.#txn.getTimestamp();
|
|
@@ -2259,9 +2354,14 @@ var Transaction = class extends DBI {
|
|
|
2259
2354
|
return this.#txn.id;
|
|
2260
2355
|
}
|
|
2261
2356
|
/**
|
|
2262
|
-
*
|
|
2357
|
+
* Overrides the transaction timestamp in milliseconds since the Unix epoch.
|
|
2358
|
+
* Call this before reading the timestamp or staging writes when adopting an
|
|
2359
|
+
* origin timestamp for replication or replay. Once a write or log entry is
|
|
2360
|
+
* staged, the timestamp is frozen, but reapplying the same timestamp remains
|
|
2361
|
+
* idempotent while the transaction is pending. The value must be finite,
|
|
2362
|
+
* positive, and below 8640000000000000.
|
|
2263
2363
|
*
|
|
2264
|
-
* @param timestamp - The timestamp to set in
|
|
2364
|
+
* @param timestamp - The timestamp to set in milliseconds.
|
|
2265
2365
|
*/
|
|
2266
2366
|
setTimestamp(timestamp) {
|
|
2267
2367
|
this.#txn.setTimestamp(timestamp);
|
|
@@ -2295,9 +2395,23 @@ function backgroundErrorSeverityName(severity) {
|
|
|
2295
2395
|
*/
|
|
2296
2396
|
var RocksDatabase = class RocksDatabase extends DBI {
|
|
2297
2397
|
/**
|
|
2298
|
-
* The name of the database.
|
|
2398
|
+
* The name of the database's column family.
|
|
2299
2399
|
*/
|
|
2300
2400
|
#name;
|
|
2401
|
+
/**
|
|
2402
|
+
* Weak cache of column-family views opened via `use()`, keyed by column
|
|
2403
|
+
* family name — mirroring `useLog`'s get-or-create semantics. The reference
|
|
2404
|
+
* is weak so a view is never pinned for this database's lifetime: an
|
|
2405
|
+
* unreferenced view can be collected (closing its own `DBHandle`), and the
|
|
2406
|
+
* next `use()` transparently recreates it. `#columnFamilyRegistry` deletes an
|
|
2407
|
+
* entry once its view is collected so a churn of distinct names (e.g.
|
|
2408
|
+
* `use(uuid())`) does not grow the map without bound.
|
|
2409
|
+
*/
|
|
2410
|
+
#columnFamilies = /* @__PURE__ */ new Map();
|
|
2411
|
+
#columnFamilyRegistry = new FinalizationRegistry((name) => {
|
|
2412
|
+
const ref = this.#columnFamilies.get(name);
|
|
2413
|
+
if (ref !== void 0 && ref.deref() === void 0) this.#columnFamilies.delete(name);
|
|
2414
|
+
});
|
|
2301
2415
|
constructor(pathOrStore, options) {
|
|
2302
2416
|
let store;
|
|
2303
2417
|
if (typeof pathOrStore === "string") store = new Store(pathOrStore, options);
|
|
@@ -2305,7 +2419,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2305
2419
|
else throw new TypeError("Invalid database path or store");
|
|
2306
2420
|
store.claim();
|
|
2307
2421
|
super(store);
|
|
2308
|
-
this.#name =
|
|
2422
|
+
this.#name = this.store.name;
|
|
2309
2423
|
}
|
|
2310
2424
|
/**
|
|
2311
2425
|
* Removes all data from the database asynchronously.
|
|
@@ -2524,6 +2638,40 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2524
2638
|
static notify(event, ...args) {
|
|
2525
2639
|
return globalNotify(event, args);
|
|
2526
2640
|
}
|
|
2641
|
+
/**
|
|
2642
|
+
* Advances a secondary instance to the primary's current state by tailing
|
|
2643
|
+
* and replaying the primary's MANIFEST and WAL. A secondary does not see
|
|
2644
|
+
* the primary's writes until this (or {@link catchUpWithPrimarySync}) is
|
|
2645
|
+
* called. Concurrent reads through this handle remain safe while the
|
|
2646
|
+
* catch-up runs.
|
|
2647
|
+
*
|
|
2648
|
+
* Only valid on a database opened with the `secondaryPath` option; throws
|
|
2649
|
+
* with code `ERR_NOT_SECONDARY` otherwise.
|
|
2650
|
+
*
|
|
2651
|
+
* Advances the **database** view only. Against a cross-process primary,
|
|
2652
|
+
* transaction-log reads keep serving the stores discovered at open (a store
|
|
2653
|
+
* the primary creates later, and its appends, need a reopen); a primary in
|
|
2654
|
+
* the same process shares the store registry, so its stores and appends are
|
|
2655
|
+
* visible immediately. Either way a log entry can describe data this
|
|
2656
|
+
* handle's database view does not have yet — the log write completes before
|
|
2657
|
+
* the RocksDB commit for every writer, so consumers must tolerate the log
|
|
2658
|
+
* leading the database.
|
|
2659
|
+
*
|
|
2660
|
+
* Await one call before starting the next: the replay holds a libuv worker
|
|
2661
|
+
* for its whole duration and concurrent calls queue on a per-database mutex
|
|
2662
|
+
* while holding theirs, so overlapping catch-ups on a backlogged follower
|
|
2663
|
+
* can exhaust the default four-thread pool and stall unrelated I/O.
|
|
2664
|
+
*/
|
|
2665
|
+
catchUpWithPrimary() {
|
|
2666
|
+
return new Promise((resolve, reject) => this.store.db.catchUpWithPrimary(resolve, reject));
|
|
2667
|
+
}
|
|
2668
|
+
/**
|
|
2669
|
+
* Synchronous version of {@link catchUpWithPrimary}. The replay runs on the
|
|
2670
|
+
* JS thread, so prefer the async form unless the caller is already blocking.
|
|
2671
|
+
*/
|
|
2672
|
+
catchUpWithPrimarySync() {
|
|
2673
|
+
this.store.db.catchUpWithPrimarySync();
|
|
2674
|
+
}
|
|
2527
2675
|
destroy() {
|
|
2528
2676
|
this.store.db.destroy();
|
|
2529
2677
|
}
|
|
@@ -2676,6 +2824,29 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2676
2824
|
return this.store.db.getDBProperty(propertyName);
|
|
2677
2825
|
}
|
|
2678
2826
|
/**
|
|
2827
|
+
* Whether RocksDB is currently applying write backpressure to this database —
|
|
2828
|
+
* either delaying (rate-limiting) or fully stopping writes. This is the
|
|
2829
|
+
* authoritative, live pull counterpart to the `'writeStall'`
|
|
2830
|
+
* [event](../README.md#event-writestall): the event pushes per-column-family
|
|
2831
|
+
* transitions (and is rate-limited), while this reads the current state on
|
|
2832
|
+
* demand and can never be stale.
|
|
2833
|
+
*
|
|
2834
|
+
* The write controller is database-wide (shared across every column family),
|
|
2835
|
+
* so this reflects the database as a whole, not a single column family — a
|
|
2836
|
+
* stall triggered by any column family stops writes for all of them. Reads
|
|
2837
|
+
* `rocksdb.is-write-stopped` and `rocksdb.actual-delayed-write-rate`.
|
|
2838
|
+
*
|
|
2839
|
+
* @example
|
|
2840
|
+
* ```typescript
|
|
2841
|
+
* db.on('writeStall', (cf) => {
|
|
2842
|
+
* if (db.isWriteStalled()) log.warn(`writes throttled/blocked (triggered by ${cf})`);
|
|
2843
|
+
* });
|
|
2844
|
+
* ```
|
|
2845
|
+
*/
|
|
2846
|
+
isWriteStalled() {
|
|
2847
|
+
return this.getDBIntProperty("rocksdb.is-write-stopped") === 1 || (this.getDBIntProperty("rocksdb.actual-delayed-write-rate") ?? 0) > 0;
|
|
2848
|
+
}
|
|
2849
|
+
/**
|
|
2679
2850
|
* Retrieves the estimated number of keys in the database. This is an alias
|
|
2680
2851
|
* for `db.estimateCount().count`; use `estimateCount()` for range support
|
|
2681
2852
|
* and a confidence indicator.
|
|
@@ -2853,18 +3024,29 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2853
3024
|
return this.store.listLogs();
|
|
2854
3025
|
}
|
|
2855
3026
|
/**
|
|
2856
|
-
* The name of the database.
|
|
3027
|
+
* The name of the database's column family.
|
|
2857
3028
|
*/
|
|
2858
3029
|
get name() {
|
|
2859
3030
|
return this.#name;
|
|
2860
3031
|
}
|
|
2861
3032
|
/**
|
|
2862
|
-
* Whether the database is open in readonly mode.
|
|
3033
|
+
* Whether the database is open in readonly mode. Also `true` for a
|
|
3034
|
+
* secondary instance (see {@link secondaryPath}), which is read-only by
|
|
3035
|
+
* construction.
|
|
2863
3036
|
*/
|
|
2864
3037
|
get readOnly() {
|
|
2865
3038
|
return this.store.readOnly;
|
|
2866
3039
|
}
|
|
2867
3040
|
/**
|
|
3041
|
+
* The secondary instance's workspace directory when the database was opened
|
|
3042
|
+
* as a secondary (a read-only follower of a live primary — see the
|
|
3043
|
+
* `secondaryPath` open option), or `undefined` for a regular or plain
|
|
3044
|
+
* read-only open.
|
|
3045
|
+
*/
|
|
3046
|
+
get secondaryPath() {
|
|
3047
|
+
return this.store.secondaryPath;
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
2868
3050
|
* Sugar method for opening a database.
|
|
2869
3051
|
*
|
|
2870
3052
|
* @param pathOrStore - The filesystem path to the database or a custom store.
|
|
@@ -3137,6 +3319,57 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
3137
3319
|
return this.store.unlock(key);
|
|
3138
3320
|
}
|
|
3139
3321
|
/**
|
|
3322
|
+
* Returns a `RocksDatabase` bound to the `name` column family of this same
|
|
3323
|
+
* database, opening (and creating, if missing) the column family on first
|
|
3324
|
+
* use. This is a factory — create-and-open sugar — not a stateful switch: the
|
|
3325
|
+
* returned view is an independent instance whose own reads/writes target its
|
|
3326
|
+
* column family, while sharing the underlying database (so a transaction,
|
|
3327
|
+
* backup, or checkpoint still spans every column family). Because the view
|
|
3328
|
+
* opens its own handle, this database need not itself be open — a discarded
|
|
3329
|
+
* parent (`new RocksDatabase(path).use('events')`) still yields a valid,
|
|
3330
|
+
* opened view.
|
|
3331
|
+
*
|
|
3332
|
+
* Get-or-create, backed by a weak cache: `db.use('events') === db.use('events')`
|
|
3333
|
+
* while the view is still referenced **and open**, and calling with this
|
|
3334
|
+
* database's own column-family name returns `this`. A view that has been
|
|
3335
|
+
* closed (or garbage-collected — the cache does not pin it) is transparently
|
|
3336
|
+
* recreated on the next `use()`. Views are independent handles: closing this
|
|
3337
|
+
* database does not close them, and vice versa — the underlying database
|
|
3338
|
+
* stays open until every handle is closed or collected.
|
|
3339
|
+
*
|
|
3340
|
+
* The view's store is derived by {@link Store#createColumnFamilyStore}, which
|
|
3341
|
+
* builds an independent store of the same class with its own codec state (so
|
|
3342
|
+
* views never share a mutable encoder/decoder) and inherits the options this
|
|
3343
|
+
* store was constructed with; pass `options` to override them for this column
|
|
3344
|
+
* family. A database configured with a pre-constructed encoder/decoder
|
|
3345
|
+
* instance cannot derive views (its state would be shared) — use an encoder
|
|
3346
|
+
* factory (`{ Encoder }`) or a named `encoding`, or override
|
|
3347
|
+
* `createColumnFamilyStore` on a custom `Store`.
|
|
3348
|
+
*
|
|
3349
|
+
* @param name - The column family name.
|
|
3350
|
+
* @param options - Options for the column family, overriding inherited ones.
|
|
3351
|
+
* @returns A `RocksDatabase` bound to the column family.
|
|
3352
|
+
*
|
|
3353
|
+
* @example
|
|
3354
|
+
* ```typescript
|
|
3355
|
+
* const db = RocksDatabase.open('/path/to/database');
|
|
3356
|
+
* const events = db.use('events');
|
|
3357
|
+
* await events.put('e1', payload);
|
|
3358
|
+
* await db.put('k', 'v'); // default column family, unaffected
|
|
3359
|
+
* ```
|
|
3360
|
+
*/
|
|
3361
|
+
use(name, options) {
|
|
3362
|
+
if (typeof name !== "string" || name === "") throw new TypeError("Column family name must be a non-empty string");
|
|
3363
|
+
if (name === this.#name) return this.open();
|
|
3364
|
+
const cached = this.#columnFamilies.get(name)?.deref();
|
|
3365
|
+
if (cached !== void 0 && cached.isOpen()) return cached;
|
|
3366
|
+
const columnFamily = new RocksDatabase(this.store.createColumnFamilyStore(name, options));
|
|
3367
|
+
columnFamily.open();
|
|
3368
|
+
this.#columnFamilies.set(name, new WeakRef(columnFamily));
|
|
3369
|
+
this.#columnFamilyRegistry.register(columnFamily, name);
|
|
3370
|
+
return columnFamily;
|
|
3371
|
+
}
|
|
3372
|
+
/**
|
|
3140
3373
|
* Excecutes a function using a thread-safe lock to ensure mutual
|
|
3141
3374
|
* exclusion.
|
|
3142
3375
|
*
|
|
@@ -3443,11 +3676,12 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3443
3676
|
position += TRANSACTION_LOG_ENTRY_HEADER_SIZE;
|
|
3444
3677
|
let matchesRange;
|
|
3445
3678
|
if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
|
|
3446
|
-
else if (exactStart)
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3679
|
+
else if (exactStart) {
|
|
3680
|
+
if (timestamp === start) {
|
|
3681
|
+
matchesRange = !exclusiveStart;
|
|
3682
|
+
foundExactStart = true;
|
|
3683
|
+
} else matchesRange = false;
|
|
3684
|
+
} else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
|
|
3451
3685
|
const entryStart = position;
|
|
3452
3686
|
position += length;
|
|
3453
3687
|
if (matchesRange) return {
|
|
@@ -3530,9 +3764,9 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
3530
3764
|
//#region src/index.ts
|
|
3531
3765
|
const versions = {
|
|
3532
3766
|
rocksdb: version,
|
|
3533
|
-
"rocksdb-js": "2.
|
|
3767
|
+
"rocksdb-js": "2.9.0"
|
|
3534
3768
|
};
|
|
3535
3769
|
|
|
3536
3770
|
//#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 };
|
|
3771
|
+
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
3772
|
//# sourceMappingURL=index.mjs.map
|