@harperfast/rocksdb-js 2.9.0 → 2.10.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 +170 -18
- package/dist/index.cjs +85 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -6
- package/dist/index.d.mts +66 -6
- package/dist/index.mjs +85 -13
- package/dist/index.mjs.map +1 -1
- package/docs/backups.md +3 -2
- package/docs/performance/mutex-contention-2026-09-16.json +684 -0
- package/docs/performance/mutex-contention-admission-2026-09-17.json +730 -0
- package/docs/performance/mutex-contention.md +204 -0
- package/docs/stats.md +47 -46
- package/docs/steady-clock-design.md +121 -0
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -95,10 +95,20 @@ Creates a new database instance.
|
|
|
95
95
|
- `maxWriteBufferSizeToMaintain: number` The number of bytes of recent memtable history to keep in
|
|
96
96
|
memory for transaction conflict checking. `-1` (the default) derives the value from
|
|
97
97
|
`maxWriteBufferNumber * writeBufferSize` (the RocksDB-recommended default for optimistic
|
|
98
|
-
transactions) — except when a
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
transactions) — except when the database has a [`writeBufferManager`](#dbconfigoptions)
|
|
99
|
+
attached, in which case it resolves to `1` so the manager is not filled with history it will
|
|
100
|
+
never release. What decides this is the manager the database itself holds, not the current
|
|
101
|
+
`writeBufferManagerSize`: a column family created later on an already-open database is clamped
|
|
102
|
+
too, because its history is still charged to that manager. It also applies to any attached
|
|
103
|
+
manager, not only a stalling one — `writeBufferManagerAllowStall` can be changed at runtime
|
|
104
|
+
while this value is fixed when a column family is created, and history the budget cannot reclaim
|
|
105
|
+
is a problem either way. `1` is the smallest target a transactional database can be given: RocksDB's transaction wrappers rewrite a `0` target to
|
|
106
|
+
the derived value, so `0` requests the _largest_ history rather than none, and an explicitly
|
|
107
|
+
requested `0` is normalized to `1` for the same reason. A positive target still retains the most
|
|
108
|
+
recent flushed memtable per column family until that family's next write. An explicit positive
|
|
109
|
+
value is honored as-is; sizing it against the budget and the column-family count is then yours,
|
|
110
|
+
and a configuration whose known families already reach the budget is reported on the
|
|
111
|
+
[`'log.warn'`](#event-api) channel.
|
|
102
112
|
- `name: string` The column family name. Defaults to `"default"`.
|
|
103
113
|
- `noBlockCache: boolean` When `true`, disables the block cache. Block caching is enabled by
|
|
104
114
|
default and the cache is shared across all database instances.
|
|
@@ -175,7 +185,13 @@ Creates a new database instance.
|
|
|
175
185
|
### `db.close()`
|
|
176
186
|
|
|
177
187
|
Closes a database. This function can be called multiple times and will only close an opened
|
|
178
|
-
database. A database instance can be reopened once
|
|
188
|
+
database. A database instance can be reopened once it is closed. A flush failure leaves the native
|
|
189
|
+
database quarantined so `shutdown()` can retry without losing unflushed data; an explicit
|
|
190
|
+
`destroy()` can instead delete it. A failure while waiting for compaction to settle is reported
|
|
191
|
+
after native teardown completes; the optional `compactOnClose` pass itself is best-effort and its
|
|
192
|
+
errors do not fail the close, since a skipped compaction loses no data. All native close errors
|
|
193
|
+
emit `database:closeFailed`. The quarantine applies to both writable and read-only opens because
|
|
194
|
+
both modes share the physical path lifecycle.
|
|
179
195
|
|
|
180
196
|
```typescript
|
|
181
197
|
const db = RocksDatabase.open('foo');
|
|
@@ -217,6 +233,12 @@ Sets global database settings.
|
|
|
217
233
|
Defaults to 32MB. Set to `0` (zero) disables block cache for future opened databases. Existing
|
|
218
234
|
block cache for any opened databases is resized immediately. Negative values throw an error.
|
|
219
235
|
- `compactOnClose: boolean` When `true`, compacts the database on close. Defaults to `false`.
|
|
236
|
+
- `lifecycleWaitSeconds: number` How long a synchronous open, destroy, or shutdown waits for a
|
|
237
|
+
_conflicting_ lifecycle operation already in progress on the same path (e.g. another open or
|
|
238
|
+
close) before throwing a retryable timeout error. It does not bound the separate, intentionally
|
|
239
|
+
unbounded wait that `destroy()`/`shutdown()` make for in-flight backups, checkpoints, or other
|
|
240
|
+
async work still using the database — see [`db.destroy()`](#dbdestroy-void). Defaults to `30`
|
|
241
|
+
seconds and must be a positive integer.
|
|
220
242
|
- `verificationTableEntries: number` The number of slots in the process-global
|
|
221
243
|
[Verification Table](#verification-table). Each slot is 8 bytes, so the default of `131072`
|
|
222
244
|
(128K) slots is 1 MB. Set to `0` to disable the verification table. This must be configured
|
|
@@ -236,8 +258,10 @@ Sets global database settings.
|
|
|
236
258
|
back into the reclaimed space. Defaults to `false`.
|
|
237
259
|
- `writeBufferManagerSize: number` Total memtable memory limit (bytes) shared across every
|
|
238
260
|
database opened in this process. When set, RocksDB uses a single `WriteBufferManager` so write
|
|
239
|
-
buffers are bounded process-wide rather than per database.
|
|
240
|
-
|
|
261
|
+
buffers are bounded process-wide rather than per database. Defaults to `0`, which means no
|
|
262
|
+
manager. Setting `0` later stops _new_ opens from attaching one, but does not detach or resize
|
|
263
|
+
the manager a database already holds — `write_buffer_manager` is fixed for the life of an open
|
|
264
|
+
database, so its memtables stay charged against that budget.
|
|
241
265
|
|
|
242
266
|
```typescript
|
|
243
267
|
RocksDatabase.config({
|
|
@@ -562,7 +586,16 @@ db.compactSync({ bottommost: true });
|
|
|
562
586
|
### `db.destroy(): void`
|
|
563
587
|
|
|
564
588
|
Completely removes a database based on the `db` instance's path including all data, column families,
|
|
565
|
-
and files on disk.
|
|
589
|
+
and files on disk. Destruction owns the physical path for the process: it closes every writable and
|
|
590
|
+
read-only handle for that path, waits for registered backups and checkpoints to stop using the
|
|
591
|
+
native database, and prevents another handle from reopening the path until removal finishes. Those
|
|
592
|
+
waits are synchronous and can outlive `lifecycleWaitSeconds` once destruction has claimed the path,
|
|
593
|
+
because releasing the native database beneath an active copy would be unsafe.
|
|
594
|
+
|
|
595
|
+
A previously opened instance does not need to remain open, which allows an explicit `destroy()`
|
|
596
|
+
retry after failed physical cleanup. A never-opened or read-only instance cannot destroy the
|
|
597
|
+
database. `shutdown()` reports a pending cleanup tombstone but never retries deletion; only an
|
|
598
|
+
explicit `destroy()` can remove the path.
|
|
566
599
|
|
|
567
600
|
```typescript
|
|
568
601
|
db.destroy();
|
|
@@ -571,8 +604,8 @@ console.log(fs.existsSync(db.path)); // false
|
|
|
571
604
|
|
|
572
605
|
### `db.drop(): Promise<void>`
|
|
573
606
|
|
|
574
|
-
|
|
575
|
-
|
|
607
|
+
Drops the column family the database was opened with (`name`). For the default column family this
|
|
608
|
+
clears all entries instead.
|
|
576
609
|
|
|
577
610
|
```typescript
|
|
578
611
|
const db = RocksDatabase.open('path/to/db', { name: 'users' });
|
|
@@ -580,9 +613,56 @@ await db.drop();
|
|
|
580
613
|
db.close();
|
|
581
614
|
```
|
|
582
615
|
|
|
616
|
+
#### Dropping column families
|
|
617
|
+
|
|
618
|
+
A drop retires the column family **logically** before it returns: the name is gone from
|
|
619
|
+
`db.columns`, a later `open()` with the same name creates a fresh, empty column family, and any
|
|
620
|
+
transaction that then stages a write to a handle of the dropped family, or commits one it staged
|
|
621
|
+
earlier, is refused whole with `ERR_COLUMN_FAMILY_DROPPED` (`Column family "users" was dropped`).
|
|
622
|
+
That terminal refusal releases the transaction's verification-table intents and bars further
|
|
623
|
+
writes or commit attempts; retained reads continue until the caller aborts the transaction, and
|
|
624
|
+
they still serve that transaction's own staged writes — values no commit will ever produce. A
|
|
625
|
+
caller that catches the refusal instead of letting it propagate must not read a value back through
|
|
626
|
+
the transaction and carry it forward.
|
|
627
|
+
If a staging call fails while the generation is being retired, retirement takes precedence and the
|
|
628
|
+
transaction is refused whole even when the immediate RocksDB failure was a pessimistic lock timeout.
|
|
629
|
+
Handles other threads still hold keep **reading** the dropped data until they close; a
|
|
630
|
+
non-transactional `putSync`/`removeSync` through such a handle is discarded.
|
|
631
|
+
|
|
632
|
+
The **physical** RocksDB drop is deferred behind commits already admitted when the drop lands: a
|
|
633
|
+
commit claims every column family its batch names before it writes its transaction-log batch and
|
|
634
|
+
releases them after RocksDB has applied it, and the physical drop runs from whichever
|
|
635
|
+
side releases last (or, for a commit a mid-flight `close()` tore out of its pipeline, from the next
|
|
636
|
+
drop, open of that name, or close on the database). With no such commit (the common case) `drop()`/`dropSync()` perform the
|
|
637
|
+
physical drop before returning, exactly as before. This is what keeps a drop racing another
|
|
638
|
+
thread's commit from latching RocksDB's fatal `Invalid column family specified in write batch`
|
|
639
|
+
error on the whole database.
|
|
640
|
+
|
|
641
|
+
Consequences to know about:
|
|
642
|
+
|
|
643
|
+
- A commit admitted before the drop completes successfully into the retiring generation, then the
|
|
644
|
+
physical drop removes that generation. Its caller sees a successful commit, but those writes are
|
|
645
|
+
intentionally discarded with the rest of the dropped column family; a same-name reopen creates
|
|
646
|
+
a fresh, empty generation. If the transaction writes to a transaction log, its entries are still
|
|
647
|
+
published; consumers must order the schema drop after those entries.
|
|
648
|
+
- `open()` of a name whose previous generation is still held by an admitted commit waits for the
|
|
649
|
+
full admission-to-reclamation interval (bounded by `ROCKSDB_JS_CF_RECLAIM_WAIT_MS`, default
|
|
650
|
+
`30000`) before creating the fresh column family; if the previous generation's physical drop
|
|
651
|
+
failed, the open retries it once and throws with that error if it fails again.
|
|
652
|
+
- A physical drop that fails (an I/O error writing the MANIFEST) keeps the name retired, is
|
|
653
|
+
retried on the next drop on the database, the next `open()` of that name, or close, and is
|
|
654
|
+
reported through the global `log.warn` event and the `columnFamily.pendingReclaims` stat. When
|
|
655
|
+
the failing drop was the one `drop()` itself ran, the call rejects with that error as well.
|
|
656
|
+
|
|
657
|
+
What is **not** guaranteed: a process that exits while a physical drop is still pending, or
|
|
658
|
+
after one failed, leaves the column family on disk under its name, and the next open of the
|
|
659
|
+
database opens it as a live column family. A backup or checkpoint taken inside that window copies
|
|
660
|
+
it. Callers that need a drop to survive a crash record their own durable tombstone before
|
|
661
|
+
acknowledging it.
|
|
662
|
+
|
|
583
663
|
### `db.dropSync(): void`
|
|
584
664
|
|
|
585
|
-
Synchronous version of `db.drop()
|
|
665
|
+
Synchronous version of `db.drop()`, with the same deferral contract.
|
|
586
666
|
|
|
587
667
|
```typescript
|
|
588
668
|
const db = RocksDatabase.open('path/to/db');
|
|
@@ -791,6 +871,12 @@ const ts = db.getMonotonicTimestamp();
|
|
|
791
871
|
console.log(ts); // 1764307857213.739
|
|
792
872
|
```
|
|
793
873
|
|
|
874
|
+
It is a wall-clock (Unix epoch) value made strictly increasing: on a tie or a backward step of the
|
|
875
|
+
host clock it advances by one floating-point ulp per call until the wall clock catches up. That
|
|
876
|
+
keeps transaction timestamps ordered and durable, but it does not measure elapsed time — after a
|
|
877
|
+
backward step, differences between two calls understate real time. Use
|
|
878
|
+
[`steadyClockNow()`](#steadyclocknow-number) for elapsed durations and deadlines.
|
|
879
|
+
|
|
794
880
|
### `db.getOldestSnapshotTimestamp(): number`
|
|
795
881
|
|
|
796
882
|
Returns a number representing a unix timestamp of the oldest unreleased snapshot.
|
|
@@ -916,8 +1002,13 @@ Returns a new `ArrayBuffer` with two additional methods:
|
|
|
916
1002
|
- `cancel()` - Removes the callback; future `notify()` calls do nothing
|
|
917
1003
|
|
|
918
1004
|
Note: If a shared buffer already exists for the given `key`, the returned `ArrayBuffer` will
|
|
919
|
-
reference this existing shared buffer.
|
|
920
|
-
|
|
1005
|
+
reference this existing shared buffer. The buffer lives as long as its column family is open (a
|
|
1006
|
+
`drop()` discards it with the column family — except the default column family, which `drop()`
|
|
1007
|
+
only clears, so its buffers survive): it is process-wide state, so a thread dropping or garbage
|
|
1008
|
+
collecting its own view (or exiting) never resets it for the others. Because nothing is ever
|
|
1009
|
+
evicted, key the buffer on a small fixed set of names rather than on unbounded data such as
|
|
1010
|
+
record ids. The notify callback is removed when the `ArrayBuffer` it was registered with is
|
|
1011
|
+
garbage collected, or by `cancel()`.
|
|
921
1012
|
|
|
922
1013
|
```typescript
|
|
923
1014
|
const buffer = new Uint8Array(db.getUserSharedBuffer('isDone', new ArrayBuffer(1)));
|
|
@@ -1148,8 +1239,8 @@ uses this timestamp as the batch key; producers may also encode it into their ow
|
|
|
1148
1239
|
|
|
1149
1240
|
Type: `number`
|
|
1150
1241
|
|
|
1151
|
-
The transaction ID
|
|
1152
|
-
RocksDB database path, regardless the database name/column family.
|
|
1242
|
+
The transaction ID, a positive integer no greater than `Number.MAX_SAFE_INTEGER`. Transaction IDs
|
|
1243
|
+
are unique to the RocksDB database path, regardless the database name/column family.
|
|
1153
1244
|
|
|
1154
1245
|
#### `txn.setTimestamp(ts?: number): void`
|
|
1155
1246
|
|
|
@@ -2200,6 +2291,51 @@ Options:
|
|
|
2200
2291
|
Validating a store that is being actively appended to can spuriously report a torn tail for the
|
|
2201
2292
|
current log file — the tail of an in-flight append is indistinguishable from a crash artifact.
|
|
2202
2293
|
|
|
2294
|
+
### `steadyClockNow(): number`
|
|
2295
|
+
|
|
2296
|
+
Reads the process-wide steady clock. No database handle is needed.
|
|
2297
|
+
|
|
2298
|
+
```typescript
|
|
2299
|
+
import { steadyClockNow } from '@harperfast/rocksdb-js';
|
|
2300
|
+
|
|
2301
|
+
const start = steadyClockNow();
|
|
2302
|
+
// ... work, possibly on other worker threads ...
|
|
2303
|
+
const elapsedMs = steadyClockNow() - start;
|
|
2304
|
+
```
|
|
2305
|
+
|
|
2306
|
+
Contract:
|
|
2307
|
+
|
|
2308
|
+
- **Units / type**: milliseconds with a fractional part, as a `number`. Read from
|
|
2309
|
+
`std::chrono::steady_clock` (`CLOCK_MONOTONIC` on Linux, `CLOCK_MONOTONIC_RAW` on macOS,
|
|
2310
|
+
`QueryPerformanceCounter` on Windows).
|
|
2311
|
+
- **Origin and lifetime**: the origin is unspecified and fixed for the life of the process. Every
|
|
2312
|
+
sample taken in the process is in one domain — the main thread and every `worker_threads` worker,
|
|
2313
|
+
including workers started or restarted at any later time, under Node, Bun and Deno alike. Samples
|
|
2314
|
+
are not meaningful across processes or restarts, and are unrelated to the Unix epoch: never compare
|
|
2315
|
+
them with `Date.now()`, `db.getMonotonicTimestamp()` or transaction timestamps.
|
|
2316
|
+
- **Monotonic, not unique**: a sample taken after another (in real time, on any thread) is `>=` it.
|
|
2317
|
+
Two samples can be equal — in the same clock tick, or when two distinct readings round to the same
|
|
2318
|
+
double — so treat `==` as "not later" and pair the sample with a sequence number if strict ordering
|
|
2319
|
+
is required. On Windows, QPC samples from different threads within ±1 native counter tick also
|
|
2320
|
+
have ambiguous ordering; do not use near-equal clock samples alone to prove causality. No
|
|
2321
|
+
wall-clock ratchet is applied.
|
|
2322
|
+
- **Precision**: the double's spacing grows with distance from the origin: about 2 ns at 100 days,
|
|
2323
|
+
61 ns at 10 years, 0.49 µs at 100 years. Conversion error is below 0.51 µs per sample through
|
|
2324
|
+
100 years from the native origin. This is representation precision, not clock accuracy. Rounding is
|
|
2325
|
+
monotone, so distinct readings can only collapse to equality, never invert. The clock's own
|
|
2326
|
+
resolution is platform-defined (the C++ duration period is 1 ns on these platforms; actual clock
|
|
2327
|
+
resolution can be coarser). The native signed 64-bit nanosecond range is about 292 years in either
|
|
2328
|
+
direction from its origin; conversion to `number` does not narrow it.
|
|
2329
|
+
- **Independent of the wall clock**: `settimeofday`/NTP steps in either direction do not move it. NTP
|
|
2330
|
+
frequency slew is platform-defined (`CLOCK_MONOTONIC` is slewed; `CLOCK_MONOTONIC_RAW` and
|
|
2331
|
+
`QueryPerformanceCounter` are not).
|
|
2332
|
+
- **Suspend**: whether time the host spends suspended counts is platform-defined — `CLOCK_MONOTONIC`
|
|
2333
|
+
excludes it on Linux; current macOS `CLOCK_MONOTONIC_RAW` and Windows QPC include it. Do not
|
|
2334
|
+
assume runtime timers (`setTimeout`) use the same suspend policy. Portable suspend-inclusive
|
|
2335
|
+
deadlines are not provided by this API.
|
|
2336
|
+
- **Cost**: one clock read and a double conversion; no lock, no native allocation, no wall-clock read,
|
|
2337
|
+
no database or log I/O, no per-worker calibration. Safe from any thread. The runtime may allocate a boxed JS number.
|
|
2338
|
+
|
|
2203
2339
|
### `currentThreadId(): number`
|
|
2204
2340
|
|
|
2205
2341
|
Returns the current thread ID.
|
|
@@ -2214,9 +2350,13 @@ console.log(currentThreadId());
|
|
|
2214
2350
|
Returns an array containing that status of all active RocksDB instances.
|
|
2215
2351
|
|
|
2216
2352
|
- `path: string` The database path.
|
|
2353
|
+
- `closeError?: string` The native lifecycle error retaining this registry entry.
|
|
2354
|
+
- `destroyCleanupPending?: boolean` The native database is closed, but physical path cleanup must
|
|
2355
|
+
finish before the next open. Call `destroy()` to retry cleanup.
|
|
2217
2356
|
- `refCount: number` The number of JavaScript database instances plus the registry's reference.
|
|
2218
2357
|
- `columnFamiles: object` A map of column family names and their their info.
|
|
2219
|
-
- `userSharedBuffers: number` The count of
|
|
2358
|
+
- `userSharedBuffers: number` The count of user shared buffer keys created since the column family
|
|
2359
|
+
was opened; entries are never evicted, so this never decreases.
|
|
2220
2360
|
- `transactions: number` The count of active transactions.
|
|
2221
2361
|
- `closables: number` The count of active database, transactions, and iterators.
|
|
2222
2362
|
- `locks: number` The count of active locks.
|
|
@@ -2231,11 +2371,23 @@ console.log(registryStatus());
|
|
|
2231
2371
|
|
|
2232
2372
|
The `shutdown()` will flush all in-memory data to disk and wait for any outstanding compactions to
|
|
2233
2373
|
finish, for all open databases. It is highly recommended to call this in a `process` `exit` event
|
|
2234
|
-
listener (on the main thread), to ensure that all data is flushed to disk before the process exits
|
|
2374
|
+
listener (on the main thread), to ensure that all data is flushed to disk before the process exits.
|
|
2375
|
+
It throws the first close failure after attempting every claimed database; call it again to retry
|
|
2376
|
+
any descriptor whose native teardown did not complete. It reports pending destroy-cleanup
|
|
2377
|
+
tombstones without deleting their paths; retry those with an explicit `destroy()`.
|
|
2378
|
+
|
|
2379
|
+
Wrap the call: an exception thrown from an `exit` listener skips every `exit` listener registered
|
|
2380
|
+
after it, and sets the exit code to 1 unless an `uncaughtException` handler is installed.
|
|
2235
2381
|
|
|
2236
2382
|
```typescript
|
|
2237
2383
|
import { shutdown } from '@harperfast/rocksdb-js';
|
|
2238
|
-
process.on('exit',
|
|
2384
|
+
process.on('exit', () => {
|
|
2385
|
+
try {
|
|
2386
|
+
shutdown();
|
|
2387
|
+
} catch (error) {
|
|
2388
|
+
console.error('rocksdb-js shutdown failed', error);
|
|
2389
|
+
}
|
|
2390
|
+
});
|
|
2239
2391
|
```
|
|
2240
2392
|
|
|
2241
2393
|
### `versions: { 'rocksdb': string; 'rocksdb-js': string }`
|
package/dist/index.cjs
CHANGED
|
@@ -131,9 +131,49 @@ const NativeIterator = binding.Iterator;
|
|
|
131
131
|
const NativeTransaction = binding.Transaction;
|
|
132
132
|
const TransactionLog = binding.TransactionLog;
|
|
133
133
|
const registryStatus = binding.registryStatus;
|
|
134
|
+
/**
|
|
135
|
+
* Flushes every open database and waits for outstanding compactions, then
|
|
136
|
+
* releases the registry.
|
|
137
|
+
*
|
|
138
|
+
* **Throws** the first failure it encounters: a close whose native teardown
|
|
139
|
+
* failed (that descriptor stays quarantined — call `shutdown()` again to retry
|
|
140
|
+
* it), or a `lifecycleWaitSeconds` timeout waiting on a concurrent
|
|
141
|
+
* open/destroy/shutdown. A quarantining close also emits
|
|
142
|
+
* `database:closeFailed`; a timeout has no event, so the throw is its only
|
|
143
|
+
* signal.
|
|
144
|
+
*
|
|
145
|
+
* A `process.on('exit')` listener must therefore wrap it: an exception thrown
|
|
146
|
+
* from an `exit` listener skips every `exit` listener registered after it (and
|
|
147
|
+
* sets exit code 1 unless an `uncaughtException` handler is installed).
|
|
148
|
+
*
|
|
149
|
+
* ```typescript
|
|
150
|
+
* process.on('exit', () => {
|
|
151
|
+
* try {
|
|
152
|
+
* shutdown();
|
|
153
|
+
* } catch (error) {
|
|
154
|
+
* console.error('rocksdb-js shutdown failed', error);
|
|
155
|
+
* }
|
|
156
|
+
* });
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
134
159
|
const shutdown = binding.shutdown;
|
|
135
160
|
const currentThreadId = binding.currentThreadId;
|
|
136
161
|
/**
|
|
162
|
+
* Reads the process-wide steady clock: `std::chrono::steady_clock` as
|
|
163
|
+
* fractional milliseconds from an unspecified origin that is fixed for the life
|
|
164
|
+
* of the process. Every sample in the process — main thread and every
|
|
165
|
+
* `worker_threads` worker, whenever started or restarted, on Node, Bun and Deno
|
|
166
|
+
* — is in one domain, and the difference between two samples is real elapsed
|
|
167
|
+
* time unaffected by wall-clock steps. Non-decreasing but not unique (two
|
|
168
|
+
* samples may be equal; treat `==` as "not later"). Windows QPC samples from
|
|
169
|
+
* different threads within one native tick also have ambiguous ordering.
|
|
170
|
+
* Clock readings alone do not prove causality. Not comparable with
|
|
171
|
+
* `Date.now()`, `db.getMonotonicTimestamp()` or transaction timestamps, and not
|
|
172
|
+
* meaningful across processes or restarts. Time spent in host suspend is
|
|
173
|
+
* platform-defined. No open database is needed.
|
|
174
|
+
*/
|
|
175
|
+
const steadyClockNow = binding.steadyClockNow;
|
|
176
|
+
/**
|
|
137
177
|
* Advises the kernel that the file-backed pages of every mapped transaction log
|
|
138
178
|
* are cold (Linux MADV_COLD), so they are reclaimed first under memory pressure
|
|
139
179
|
* without being freed — useful during replication catch-up, where a full read of
|
|
@@ -159,6 +199,21 @@ const transactionLogMapCount = binding.transactionLogMapCount;
|
|
|
159
199
|
* disarm. Used by the ERR_TRY_AGAIN retry regression test.
|
|
160
200
|
*/
|
|
161
201
|
const forceTryAgainForTesting = binding.forceTryAgainForTesting;
|
|
202
|
+
/**
|
|
203
|
+
* Test-only seam: makes the physical column-family drop report failure.
|
|
204
|
+
* `0` = inert, `1` = fail without dropping (the retry performs the drop),
|
|
205
|
+
* `2` = drop for real and then report failure (the retry resolves as
|
|
206
|
+
* "already dropped"). Process-global, like `forceTryAgainForTesting`.
|
|
207
|
+
*/
|
|
208
|
+
const forceDropFailureForTesting = binding.forceDropFailureForTesting;
|
|
209
|
+
/** Parks one selected transaction write after its retirement precheck. */
|
|
210
|
+
const setTransactionStagingDelayForTesting = binding.setTransactionStagingDelayForTesting;
|
|
211
|
+
/** Reports whether the selected transaction write is parked. */
|
|
212
|
+
const isTransactionStagingDelayedForTesting = binding.isTransactionStagingDelayedForTesting;
|
|
213
|
+
/** Reports whether a transaction is parked at the native commit seam. */
|
|
214
|
+
const isTransactionCommitExecuteDelayedForTesting = binding.isTransactionCommitExecuteDelayedForTesting;
|
|
215
|
+
const setTransactionCommitAdmissionDelayForTesting = binding.setTransactionCommitAdmissionDelayForTesting;
|
|
216
|
+
const isTransactionCommitAdmissionDelayedForTesting = binding.isTransactionCommitAdmissionDelayedForTesting;
|
|
162
217
|
/** Delays one selected watchdog join in the concurrent-shutdown regression test. */
|
|
163
218
|
const setWriteBufferManagerJoinDelayForTesting = binding.setWriteBufferManagerJoinDelayForTesting;
|
|
164
219
|
/**
|
|
@@ -1293,7 +1348,15 @@ var DBIterator = class {
|
|
|
1293
1348
|
this.iterator.return?.();
|
|
1294
1349
|
return DONE_RESULT;
|
|
1295
1350
|
}
|
|
1296
|
-
|
|
1351
|
+
let result;
|
|
1352
|
+
try {
|
|
1353
|
+
result = this.iterator.next();
|
|
1354
|
+
} catch (err) {
|
|
1355
|
+
try {
|
|
1356
|
+
this.iterator.return?.();
|
|
1357
|
+
} catch {}
|
|
1358
|
+
throw err;
|
|
1359
|
+
}
|
|
1297
1360
|
if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
|
|
1298
1361
|
this.#yielded++;
|
|
1299
1362
|
const includeValues = this.#includeValues;
|
|
@@ -1571,10 +1634,16 @@ var Store = class {
|
|
|
1571
1634
|
/**
|
|
1572
1635
|
* The bytes of recent memtable history to retain in memory for transaction
|
|
1573
1636
|
* conflict checking. `-1` derives the value from
|
|
1574
|
-
* `maxWriteBufferNumber * writeBufferSize`, except
|
|
1575
|
-
* `writeBufferManager
|
|
1576
|
-
* that manager and is only trimmable down to this target, so a
|
|
1577
|
-
* budget cannot hold
|
|
1637
|
+
* `maxWriteBufferNumber * writeBufferSize`, except when a
|
|
1638
|
+
* `writeBufferManager` is configured, where it derives `1`: retained history
|
|
1639
|
+
* is charged to that manager and is only trimmable down to this target, so a
|
|
1640
|
+
* target the budget cannot hold is memory the manager never reclaims, and
|
|
1641
|
+
* stalls writes permanently when `writeBufferManagerAllowStall` is on. Any
|
|
1642
|
+
* configured manager, not only a stalling one — that flag is mutable at
|
|
1643
|
+
* runtime while this value is fixed when the family is created. `1`, not `0`, because
|
|
1644
|
+
* RocksDB's transaction wrappers rewrite a `0` target to the derived value —
|
|
1645
|
+
* so `0` asks for the largest history rather than none, and an explicit `0`
|
|
1646
|
+
* is normalized to `1` for that reason. Set a positive number to size it
|
|
1578
1647
|
* yourself against the budget and the column-family count.
|
|
1579
1648
|
*/
|
|
1580
1649
|
maxWriteBufferSizeToMaintain;
|
|
@@ -2105,12 +2174,12 @@ var Store = class {
|
|
|
2105
2174
|
return false;
|
|
2106
2175
|
}
|
|
2107
2176
|
putSync(context, key, value, options) {
|
|
2108
|
-
if (!this.db.opened) throw new Error("Database not open");
|
|
2177
|
+
if (!this.db.opened) throw new Error(this.db.closing ? "Database is closing" : "Database not open");
|
|
2109
2178
|
const valueBuffer = this.encodeValue(value);
|
|
2110
2179
|
context.putSync(this.encodeKey(key), valueBuffer, this.getTxnId(options));
|
|
2111
2180
|
}
|
|
2112
2181
|
removeSync(context, key, options) {
|
|
2113
|
-
if (!this.db.opened) throw new Error("Database not open");
|
|
2182
|
+
if (!this.db.opened) throw new Error(this.db.closing ? "Database is closing" : "Database not open");
|
|
2114
2183
|
context.removeSync(this.encodeKey(key), this.getTxnId(options));
|
|
2115
2184
|
}
|
|
2116
2185
|
/**
|
|
@@ -2614,7 +2683,10 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2614
2683
|
* native binding use namespaced keys (e.g. `'transactionLog:warning'`).
|
|
2615
2684
|
*
|
|
2616
2685
|
* Listeners are not tied to any specific database — they fire for every
|
|
2617
|
-
* matching event emitted in this process.
|
|
2686
|
+
* matching event emitted in this process. Native lifecycle failures use
|
|
2687
|
+
* `'database:closeFailed'` with `(path, error)` string arguments. The event
|
|
2688
|
+
* reports both completed and incomplete teardowns; an incomplete close can
|
|
2689
|
+
* be retried by `shutdown()` or explicitly deleted by `destroy()`.
|
|
2618
2690
|
*
|
|
2619
2691
|
* @example
|
|
2620
2692
|
* ```typescript
|
|
@@ -2698,7 +2770,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2698
2770
|
this.store.db.catchUpWithPrimarySync();
|
|
2699
2771
|
}
|
|
2700
2772
|
destroy() {
|
|
2701
|
-
this.store.db.destroy();
|
|
2773
|
+
this.store.db.destroy(this.store.readOnly);
|
|
2702
2774
|
}
|
|
2703
2775
|
async drop() {
|
|
2704
2776
|
return new Promise((resolve, reject) => {
|
|
@@ -3564,8 +3636,8 @@ function frameFits(dataView, pos, dataEnd) {
|
|
|
3564
3636
|
* terminator, let a chain "end" anywhere in megabytes of padding. Against the written extent the
|
|
3565
3637
|
* end is a single offset, so a chain landing on it is ~1/2^32 to be coincidence.
|
|
3566
3638
|
*
|
|
3567
|
-
* The JS counterpart of `
|
|
3568
|
-
*
|
|
3639
|
+
* The JS counterpart of `findFramingResumeOffset()` in `transaction_log_recovery.cpp`, which the
|
|
3640
|
+
* open-time scan and the timestamp index use to keep walking past the same break.
|
|
3569
3641
|
*/
|
|
3570
3642
|
function findResyncPosition(dataView, from, dataEnd) {
|
|
3571
3643
|
const lastStart = dataEnd - TRANSACTION_LOG_ENTRY_HEADER_SIZE;
|
|
@@ -3789,7 +3861,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
3789
3861
|
//#region src/index.ts
|
|
3790
3862
|
const versions = {
|
|
3791
3863
|
rocksdb: version,
|
|
3792
|
-
"rocksdb-js": "2.
|
|
3864
|
+
"rocksdb-js": "2.10.0"
|
|
3793
3865
|
};
|
|
3794
3866
|
|
|
3795
3867
|
//#endregion
|
|
@@ -3812,6 +3884,7 @@ exports.parseTransactionLog = parseTransactionLog;
|
|
|
3812
3884
|
exports.registryStatus = registryStatus;
|
|
3813
3885
|
exports.shutdown = shutdown;
|
|
3814
3886
|
exports.stats = stats;
|
|
3887
|
+
exports.steadyClockNow = steadyClockNow;
|
|
3815
3888
|
exports.supportedCompression = supportedCompression;
|
|
3816
3889
|
exports.tryFileLock = tryFileLock;
|
|
3817
3890
|
exports.validateTransactionLogStore = validateTransactionLogStore;
|