@harperfast/rocksdb-js 2.7.1 → 2.8.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 +287 -26
- package/dist/index.cjs +460 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +440 -12
- package/dist/index.d.mts +440 -12
- package/dist/index.mjs +458 -13
- package/dist/index.mjs.map +1 -1
- package/docs/stats.md +309 -268
- package/package.json +19 -20
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@ A Node.js binding for the RocksDB library.
|
|
|
11
11
|
- Custom stores provide ability to override default database interactions
|
|
12
12
|
- Efficient binary key and value encoding
|
|
13
13
|
- Configurable block/blob compression (LZ4, Zstd, Zlib, and more)
|
|
14
|
+
- Observable background errors via the `'error'` event or `db.getLastError()`, with in-process
|
|
15
|
+
recovery (`db.resume()`)
|
|
14
16
|
- Access to internal RocksDB statistics
|
|
15
17
|
- Designed for Node.js and Bun on Linux, macOS, and Windows
|
|
16
18
|
|
|
@@ -53,6 +55,16 @@ Creates a new database instance.
|
|
|
53
55
|
when linked, else no compression). See [Compression](#compression). Throws if the algorithm is
|
|
54
56
|
not compiled into the native build — check [`supportedCompression`](#supportedcompression) for
|
|
55
57
|
the available list.
|
|
58
|
+
- `compressionForAllColumnFamilies: boolean` When `true`, applies `compression` to every column
|
|
59
|
+
family opened for the database rather than only the column family specified by `name`. Requires an explicit
|
|
60
|
+
`compression`. Defaults to `false`. See [Compression](#compression).
|
|
61
|
+
- `dbWriteBufferSize: number` The total memtable memory budget in bytes shared across all of the
|
|
62
|
+
database's column families. When the combined size of all memtables reaches this value, RocksDB
|
|
63
|
+
flushes the largest one. `0` (the default) disables this global trigger, so per-column-family
|
|
64
|
+
`writeBufferSize` alone drives flushing. This is distinct from the process-wide
|
|
65
|
+
[`writeBufferManagerSize`](#dbconfigoptions) config option. Database-wide, so it binds when the
|
|
66
|
+
path is first opened in this process: a later open of the same path — including from another
|
|
67
|
+
worker thread — keeps the first opener's value rather than overriding or rejecting it.
|
|
56
68
|
- `disableWAL: boolean` Whether to disable the RocksDB write ahead log. Defaults to `false`.
|
|
57
69
|
- `enableStats: boolean` When `true` and the database is open, RocksDB will captures stats that
|
|
58
70
|
are retrieved by calling `db.getStats()`. Enabling statistics imposes 5-10% in overhead.
|
|
@@ -76,6 +88,17 @@ Creates a new database instance.
|
|
|
76
88
|
compaction falls behind under sustained ingest); a positive `int32` is an explicit cap. Reads
|
|
77
89
|
only pay a reopen cost when the number of live table files exceeds the budget, so raise the
|
|
78
90
|
process fd limit (and with it the derived budget) for very large databases.
|
|
91
|
+
- `maxWriteBufferNumber: number` The maximum number of memtables that can be queued per column
|
|
92
|
+
family before writes stall. Higher values absorb write bursts while flushes catch up, at the
|
|
93
|
+
cost of memory (roughly `maxWriteBufferNumber * writeBufferSize` per column family). Defaults to
|
|
94
|
+
`16`.
|
|
95
|
+
- `maxWriteBufferSizeToMaintain: number` The number of bytes of recent memtable history to keep in
|
|
96
|
+
memory for transaction conflict checking. `-1` (the default) derives the value from
|
|
97
|
+
`maxWriteBufferNumber * writeBufferSize` (the RocksDB-recommended default for optimistic
|
|
98
|
+
transactions) — except when a stalling [`writeBufferManager`](#dbconfigoptions) is configured
|
|
99
|
+
(`writeBufferManagerSize > 0` with `writeBufferManagerAllowStall`), in which case it resolves to
|
|
100
|
+
`0` to avoid retaining memtable history the manager will never release. An explicit non-negative
|
|
101
|
+
value is always honored as-is.
|
|
79
102
|
- `name: string` The column family name. Defaults to `"default"`.
|
|
80
103
|
- `noBlockCache: boolean` When `true`, disables the block cache. Block caching is enabled by
|
|
81
104
|
default and the cache is shared across all database instances.
|
|
@@ -85,7 +108,10 @@ Creates a new database instance.
|
|
|
85
108
|
until commit. Defaults to `false`.
|
|
86
109
|
- `readOnly: boolean` When `true`, the database is opened in read-only mode. Read operations are
|
|
87
110
|
permitted. Write operations will throw an error with code `ERR_DATABASE_READONLY`. Transactions
|
|
88
|
-
are a no-op in read-only mode.
|
|
111
|
+
are a no-op in read-only mode. [`flush()`](#dbflushoptions-promisevoid),
|
|
112
|
+
[`flushSync()`](#dbflushsyncoptions-void), [`compact()`](#dbcompactoptions-promisevoid) and
|
|
113
|
+
[`compactSync()`](#dbcompactsyncoptions-void) are the exception: they have nothing to do when
|
|
114
|
+
there are no writes, so they succeed as no-ops rather than throwing.
|
|
89
115
|
- `statsLevel: StatsLevel` Controls which type of statistics to skip and reduce statistic
|
|
90
116
|
overhead. Defaults to `StatsLevel.ExceptDetailedTimers`.
|
|
91
117
|
- `store: Store` A custom store that handles all interaction between the `RocksDatabase` or
|
|
@@ -96,10 +122,9 @@ Creates a new database instance.
|
|
|
96
122
|
modified time to be older than the retention period before it is rotated to the next sequence
|
|
97
123
|
number. Value must be between `0.0` and `1.0`. A threshold of `0.0` means ignore age check.
|
|
98
124
|
Defaults to `0.75`.
|
|
99
|
-
- `transactionLogMaxSize: number` The maximum size of a transaction log file.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
rotated to the next sequence number. Defaults to 16 MB.
|
|
125
|
+
- `transactionLogMaxSize: number` The target maximum size of a transaction log file. Transactions
|
|
126
|
+
are never split across files: if the complete transaction does not fit, the log rotates before
|
|
127
|
+
writing it. A transaction written to an empty file may exceed the target. Defaults to 16 MB.
|
|
103
128
|
- `transactionLogRetention: string | number` The number of minutes to retain transaction logs
|
|
104
129
|
before purging. Defaults to `'3d'` (3 days).
|
|
105
130
|
- `transactionLogsPath: string` The path to store transaction logs. Defaults to
|
|
@@ -109,6 +134,9 @@ Creates a new database instance.
|
|
|
109
134
|
the verification slot for each written key. Enable this only for column families whose records
|
|
110
135
|
are cached (e.g. the primary column family of a table). Defaults to `false`. Requires
|
|
111
136
|
`verificationTableEntries` to be configured before the first database is opened.
|
|
137
|
+
- `writeBufferSize: number` The per-column-family memtable size in bytes at which the memtable is
|
|
138
|
+
sealed and flushed to an SST file. Smaller values produce more frequent, faster flushes; larger
|
|
139
|
+
values batch more writes per SST file at the cost of memory. Defaults to `16777216` (16 MB).
|
|
112
140
|
|
|
113
141
|
### `db.close()`
|
|
114
142
|
|
|
@@ -146,18 +174,6 @@ const db = RocksDatabase.open('path/to/db', {
|
|
|
146
174
|
console.log(db.compression); // { algorithm: 'zstd', level: 3 }
|
|
147
175
|
```
|
|
148
176
|
|
|
149
|
-
### `db.logOptions: { maxLogFileSize: number, infoLogLevel: number }`
|
|
150
|
-
|
|
151
|
-
Returns the informational-log settings currently in effect for this database, read live from
|
|
152
|
-
RocksDB. These are database-wide settings (not per-column-family). `maxLogFileSize` is the
|
|
153
|
-
per-file size cap for informational log files (`LOG` / `LOG.old.*`); `infoLogLevel` is the logging
|
|
154
|
-
verbosity. The database must be open.
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
const db = RocksDatabase.open('path/to/db', { maxLogFileSize: 4 * 1024 * 1024 });
|
|
158
|
-
console.log(db.logOptions); // { maxLogFileSize: 4194304, infoLogLevel: 1 }
|
|
159
|
-
```
|
|
160
|
-
|
|
161
177
|
### `db.config(options)`
|
|
162
178
|
|
|
163
179
|
Sets global database settings.
|
|
@@ -203,6 +219,18 @@ Returns `true` if the database is open, otherwise false.
|
|
|
203
219
|
console.log(db.isOpen()); // true or false
|
|
204
220
|
```
|
|
205
221
|
|
|
222
|
+
### `db.logOptions: { maxLogFileSize: number, infoLogLevel: number }`
|
|
223
|
+
|
|
224
|
+
Returns the informational-log settings currently in effect for this database, read live from
|
|
225
|
+
RocksDB. These are database-wide settings (not per-column-family). `maxLogFileSize` is the
|
|
226
|
+
per-file size cap for informational log files (`LOG` / `LOG.old.*`); `infoLogLevel` is the logging
|
|
227
|
+
verbosity. The database must be open.
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
const db = RocksDatabase.open('path/to/db', { maxLogFileSize: 4 * 1024 * 1024 });
|
|
231
|
+
console.log(db.logOptions); // { maxLogFileSize: 4194304, infoLogLevel: 1 }
|
|
232
|
+
```
|
|
233
|
+
|
|
206
234
|
### `db.name: string`
|
|
207
235
|
|
|
208
236
|
Returns the database column family's name.
|
|
@@ -302,9 +330,13 @@ await db.compact({ start: 'a', end: 'z' });
|
|
|
302
330
|
await db.compact({ bottommost: true });
|
|
303
331
|
```
|
|
304
332
|
|
|
333
|
+
On a [read-only](#new-rocksdatabasepath-options) database this is a no-op: arguments are still
|
|
334
|
+
validated, but the returned promise resolves without compacting rather than rejecting.
|
|
335
|
+
|
|
305
336
|
### `db.compactSync(options?): void`
|
|
306
337
|
|
|
307
|
-
Synchronous version of `compact()`.
|
|
338
|
+
Synchronous version of `compact()`. On a [read-only](#new-rocksdatabasepath-options) database it
|
|
339
|
+
validates its arguments and then returns without compacting, rather than throwing.
|
|
308
340
|
|
|
309
341
|
```typescript
|
|
310
342
|
db.compactSync();
|
|
@@ -345,23 +377,63 @@ db.dropSync();
|
|
|
345
377
|
db.close();
|
|
346
378
|
```
|
|
347
379
|
|
|
348
|
-
### `db.flush(): Promise<void>`
|
|
380
|
+
### `db.flush(options?): Promise<void>`
|
|
349
381
|
|
|
350
382
|
Flushes all in-memory data to disk asynchronously.
|
|
351
383
|
|
|
384
|
+
- `options: object`
|
|
385
|
+
- `allowWriteStall?: boolean` Whether the flush may proceed even though it will stall writes for
|
|
386
|
+
its duration. Defaults to `false`, which — despite how that reads — means the flush **waits**
|
|
387
|
+
until it can run without causing a stall, with no timeout: on a database stuck in a stall
|
|
388
|
+
condition (immutable-memtable backlog, L0 stop trigger, an exhausted `WriteBufferManager`
|
|
389
|
+
budget), the returned promise never settles, and since `flush()` runs on the libuv threadpool
|
|
390
|
+
(default size 4), a handful of concurrently stalled flushes can exhaust the pool and stall
|
|
391
|
+
every unrelated `fs`/`dns`/`crypto` call and cold-cache `get()` in the process, not only this
|
|
392
|
+
database's. Pass `true` up front, before issuing a flush you expect might stall, when it's a
|
|
393
|
+
durability gate you'd rather have stall writers than wait indefinitely — there's no way to
|
|
394
|
+
cancel an in-flight `flush()`, so this isn't a rescue for one already hung, and forcing the
|
|
395
|
+
memtable switch can itself prolong an existing L0 stop-trigger condition rather than clear it.
|
|
396
|
+
That cost is database-wide, covering every column family on the (process-global,
|
|
397
|
+
`worker_threads`-shared) database handle, not just the caller's — and it relocates the hang
|
|
398
|
+
rather than removing it: a stalled write blocks the database's single commit thread, which
|
|
399
|
+
dispatches every `Transaction.commit()` in order, so every commit behind it queues up too,
|
|
400
|
+
including ones from callers that never touched flush. This is a different knob from
|
|
401
|
+
`writeBufferManagerAllowStall` (see [`new RocksDatabase()`](#new-rocksdatabasepath-options)
|
|
402
|
+
options), with nearly opposite polarity: that one governs whether the `WriteBufferManager` may
|
|
403
|
+
stall writers at all, this one governs whether one manual flush is willing to cause a stall
|
|
404
|
+
rather than wait one out.
|
|
405
|
+
|
|
352
406
|
```typescript
|
|
353
407
|
await db.flush();
|
|
408
|
+
|
|
409
|
+
// Chosen up front, as a durability gate willing to pay the stall cost
|
|
410
|
+
await db.flush({ allowWriteStall: true });
|
|
354
411
|
```
|
|
355
412
|
|
|
356
|
-
|
|
413
|
+
On a [read-only](#new-rocksdatabasepath-options) database this is a no-op: the options bag is still
|
|
414
|
+
validated, but the returned promise resolves without flushing rather than rejecting.
|
|
415
|
+
|
|
416
|
+
### `db.flushSync(options?): void`
|
|
357
417
|
|
|
358
418
|
Flushes all in-memory data to disk synchronously. Note that this can be an expensive operation, so
|
|
359
419
|
it is recommended to use `flush()` if you want to keep the event loop free.
|
|
360
420
|
|
|
421
|
+
- `options: object` Same as [`flush()`](#dbflushoptions-promisevoid). The default
|
|
422
|
+
`allowWriteStall: false` wait is taken on the calling thread, which here is the JS thread — so
|
|
423
|
+
the hazard described there is strictly larger on this entry point, not smaller: instead of
|
|
424
|
+
parking one libuv pool worker it freezes the event loop outright, and it holds the in-flight
|
|
425
|
+
operation claim that `close()` waits on, so the database cannot be closed out from under it
|
|
426
|
+
either. Prefer `flush()` if there is any chance the database is in a stall condition.
|
|
427
|
+
|
|
361
428
|
```typescript
|
|
362
429
|
db.flushSync();
|
|
430
|
+
|
|
431
|
+
db.flushSync({ allowWriteStall: true });
|
|
363
432
|
```
|
|
364
433
|
|
|
434
|
+
On a [read-only](#new-rocksdatabasepath-options) database it validates its options and then returns
|
|
435
|
+
without flushing, rather than throwing.
|
|
436
|
+
|
|
365
437
|
### `db.get(key: Key, options?: GetOptions): MaybePromise<any>`
|
|
366
438
|
|
|
367
439
|
Retreives the value for a given key. If the key does not exist, it will resolve `undefined`.
|
|
@@ -408,13 +480,75 @@ the `expectedVersion` option is used.
|
|
|
408
480
|
### `db.getEstimatedKeyCount(): number`
|
|
409
481
|
|
|
410
482
|
Retrieves the estimated number of keys in the database. This is an alias for
|
|
411
|
-
`db.getDBIntProperty('rocksdb.estimate-num-keys')
|
|
483
|
+
`db.getDBIntProperty('rocksdb.estimate-num-keys')`; use `estimateCount()` for range support and a
|
|
484
|
+
confidence indicator.
|
|
412
485
|
|
|
413
486
|
```typescript
|
|
414
487
|
const estimated = db.getEstimatedKeyCount();
|
|
415
488
|
console.log(estimated);
|
|
416
489
|
```
|
|
417
490
|
|
|
491
|
+
### `db.estimateCount(options?: CountEstimateOptions): CountEstimate`
|
|
492
|
+
|
|
493
|
+
Estimates the number of keys in the database, or within a key range, returning
|
|
494
|
+
`{ count, confidence }`. Unlike `getKeysCount()`, this never iterates: the estimate is derived
|
|
495
|
+
from RocksDB statistics (memtable stats plus approximate SST sizes converted through the entry
|
|
496
|
+
density of the SSTs overlapping the range), so its cost scales with the number of SSTs overlapping
|
|
497
|
+
the range rather than the number of keys. Reading cold table properties can do I/O through the
|
|
498
|
+
table cache, so narrow ranges are preferable. A start-only range is computed as the
|
|
499
|
+
whole-database estimate minus the complement, so it does the work of the range _below_ `start`.
|
|
500
|
+
Accuracy improves with range size. Resolution is bounded by SST data-block granularity, so a range
|
|
501
|
+
narrower than a block is unreliable in either direction: it may over-report or report 0 for present
|
|
502
|
+
keys, and its low `confidence` is the signal. Recently deleted or overwritten entries may be counted
|
|
503
|
+
until compaction.
|
|
504
|
+
Estimates always reflect committed state; writes pending in a transaction are not included. Set
|
|
505
|
+
`reverse: true` to use `getRange()`'s reverse convention (`start` is the upper bound and `end` is
|
|
506
|
+
the lower bound). An inverted range (`start` ≥ `end`) returns
|
|
507
|
+
`{ count: 0, confidence: 1 }`.
|
|
508
|
+
|
|
509
|
+
`confidence` is a heuristic 0–1 indicator of how trustworthy `count` is — exactly 1 only when the
|
|
510
|
+
count is exact. It is derived from the estimate's resolution (data-block/memtable-sampling
|
|
511
|
+
granularity relative to the count), the tombstone fraction of the overlapping SSTs, and — for
|
|
512
|
+
start-only ranges — the error compounded by complement subtraction. Treat it as an ordering
|
|
513
|
+
signal (e.g. when to trust an estimate for query planning vs fall back to a heuristic), not a
|
|
514
|
+
statistical bound.
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
const { count, confidence } = db.estimateCount({ start: 'a', end: 'z' });
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### `db.createCountEstimator(options?: CountEstimatorOptions): CountEstimator`
|
|
521
|
+
|
|
522
|
+
Creates an estimator that progressively refines a range count estimate while the range is being
|
|
523
|
+
iterated — useful for reporting a total alongside a page of results without scanning the full
|
|
524
|
+
range. Before any traversal, `estimate()` returns the pure statistical estimate (same as
|
|
525
|
+
`estimateCount(range)`). As the caller reports progress with `advance(lastKey, count)` (e.g. once
|
|
526
|
+
per page), `estimate()` returns the exact traversed count plus a statistical estimate of the
|
|
527
|
+
remainder, calibrated by the observed ratio of actual-to-estimated entries over the portion
|
|
528
|
+
already traversed — so the count converges toward the exact total. `confidence` is the
|
|
529
|
+
exactness-weighted blend of the traversed portion and the remainder's confidence, so it approaches 1
|
|
530
|
+
as the exact portion grows, although a checkpoint may decrease when calibration makes a large
|
|
531
|
+
correction.
|
|
532
|
+
Each checkpoint reads committed state, so a traversal performed against a transaction snapshot may
|
|
533
|
+
be calibrated against data committed after that snapshot.
|
|
534
|
+
When traversal completes, call `finish()` and `estimate()` returns the exact count with
|
|
535
|
+
confidence 1. Reverse ranges follow `getRange`: set `start` to the upper bound and `end` to the
|
|
536
|
+
lower bound, then set `reverse: true`. The caller owns the progress contract: cursors must move
|
|
537
|
+
monotonically through the range and each entry must be reported exactly once.
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
const range = { start: 'a', end: 'z' };
|
|
541
|
+
const estimator = db.createCountEstimator(range);
|
|
542
|
+
let lastKey;
|
|
543
|
+
let pageSize = 0;
|
|
544
|
+
for (const { key } of db.getRange({ ...range, limit: 25 })) {
|
|
545
|
+
lastKey = key;
|
|
546
|
+
pageSize++;
|
|
547
|
+
}
|
|
548
|
+
estimator.advance(lastKey, pageSize);
|
|
549
|
+
const { count, confidence } = estimator.estimate();
|
|
550
|
+
```
|
|
551
|
+
|
|
418
552
|
### `db.getKeys(options?: IteratorOptions): ExtendedIterable`
|
|
419
553
|
|
|
420
554
|
Retrieves all keys within a range.
|
|
@@ -692,6 +826,15 @@ not committed after the configured number of coordinated retries, the transactio
|
|
|
692
826
|
an `ERR_TRANSACTION_ABANDONED` error. Coordinated retry requires the column family to be opened with
|
|
693
827
|
`verificationTable: true`.
|
|
694
828
|
|
|
829
|
+
That wait is bounded so a conflicting transaction that is never committed or aborted cannot block
|
|
830
|
+
the commit forever: if the write intent has not been released after `ROCKSDB_JS_PARK_TIMEOUT_MS`
|
|
831
|
+
(default `5000`), the commit resolves anyway and consumes a retry attempt exactly as a real release
|
|
832
|
+
would. A conflicting transaction held for longer than roughly `maxRetries` times that timeout
|
|
833
|
+
therefore ends in `ERR_TRANSACTION_ABANDONED` rather than waiting indefinitely. Deployments where
|
|
834
|
+
waiting is preferable to failing should raise the timeout; there is no way to disable the bound.
|
|
835
|
+
In particular `0` does not disable it: `0`, negative, and unparseable values all fall back to the
|
|
836
|
+
`5000` default, and a value between `1` and `49` is clamped up to `50`.
|
|
837
|
+
|
|
695
838
|
### Class: `Transaction`
|
|
696
839
|
|
|
697
840
|
The transaction callback is passed in a `Transaction` instance which contains all of the same data
|
|
@@ -768,6 +911,73 @@ await db.transaction(async (txn) => {
|
|
|
768
911
|
});
|
|
769
912
|
```
|
|
770
913
|
|
|
914
|
+
## Error Handling
|
|
915
|
+
|
|
916
|
+
### `db.getLastError(): BackgroundError | null`
|
|
917
|
+
|
|
918
|
+
Returns the most recent [`BackgroundError`](#event-error) observed on this database, or `null` when
|
|
919
|
+
none has occurred. This is the **pull** counterpart to the `'error'` [event](#event-error): use it
|
|
920
|
+
for an on-demand check (e.g. a health probe) or to catch an error that fired before a listener was
|
|
921
|
+
attached. The value is historical — it is **not** cleared by a successful
|
|
922
|
+
[`db.resume()`](#dbresume-void); reset it explicitly with
|
|
923
|
+
[`db.setLastError(null)`](#dbsetlasterrorerror-void). When the returned error's `writesDisabled` is
|
|
924
|
+
`true`, writes are stopped until recovery.
|
|
925
|
+
|
|
926
|
+
```typescript
|
|
927
|
+
const err = db.getLastError();
|
|
928
|
+
if (err?.writesDisabled) {
|
|
929
|
+
// ...free disk space, then...
|
|
930
|
+
db.resume();
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### `db.setLastError(error?): void`
|
|
935
|
+
|
|
936
|
+
Sets or clears the last background error, mirroring the Win32 `SetLastError` / `GetLastError` pair.
|
|
937
|
+
|
|
938
|
+
- **Clear** — pass `null` (or no argument). [`db.getLastError()`](#dbgetlasterror-backgrounderror--null)
|
|
939
|
+
then returns `null` and no event fires. This is how you reset the error after handling or
|
|
940
|
+
recovering it (e.g. after [`db.resume()`](#dbresume-void)) — the equivalent of
|
|
941
|
+
`SetLastError(ERROR_SUCCESS)`.
|
|
942
|
+
- **Set** — pass an object (`{ message, severity?, severityName?, writesDisabled?, reason?, reasonName? }`;
|
|
943
|
+
`type` defaults to `'background'`). It is stored and the `'error'` [event](#event-error) fires with
|
|
944
|
+
the reconstructed [`BackgroundError`](#event-error). Useful for surfacing an application-level
|
|
945
|
+
"this database is unusable" state, and for deterministically exercising the error path in tests.
|
|
946
|
+
|
|
947
|
+
```typescript
|
|
948
|
+
// reset after recovery
|
|
949
|
+
db.resume();
|
|
950
|
+
db.setLastError(null);
|
|
951
|
+
|
|
952
|
+
// inject (e.g. in a test)
|
|
953
|
+
db.on('error', (err) => console.error(err.message));
|
|
954
|
+
db.setLastError({
|
|
955
|
+
message: 'disk quota exceeded',
|
|
956
|
+
severity: 2,
|
|
957
|
+
severityName: 'hard',
|
|
958
|
+
writesDisabled: true,
|
|
959
|
+
});
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
### `db.resume(): void`
|
|
963
|
+
|
|
964
|
+
Attempts to recover the database from a background error (see the `'error'` [event](#event-error))
|
|
965
|
+
by calling RocksDB's `DB::Resume()`. Call this after the underlying condition has cleared — e.g.
|
|
966
|
+
once disk space has been freed. On success writes are accepted again (and RocksDB can resume the
|
|
967
|
+
obsolete-file cleanup it was blocking); on failure — the condition has not actually cleared — it
|
|
968
|
+
throws with the RocksDB error and the database stays read-only. A no-op on a healthy database.
|
|
969
|
+
|
|
970
|
+
Runs synchronously and may briefly block, since recovery can re-flush memtables.
|
|
971
|
+
|
|
972
|
+
```typescript
|
|
973
|
+
db.on('error', (err) => {
|
|
974
|
+
if (err.writesDisabled) {
|
|
975
|
+
// ...free disk space, then...
|
|
976
|
+
db.resume();
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
```
|
|
980
|
+
|
|
771
981
|
## Events
|
|
772
982
|
|
|
773
983
|
### Event: `'aftercommit'`
|
|
@@ -794,6 +1004,57 @@ The `'committed'` event is emitted after the transaction has been written. When
|
|
|
794
1004
|
emitted, the transaction is still cleaning up. If you need to know when the transaction is fully
|
|
795
1005
|
complete, use the `'aftercommit'` event.
|
|
796
1006
|
|
|
1007
|
+
### Event: `'error'`
|
|
1008
|
+
|
|
1009
|
+
When a write fails at the filesystem level — a full disk, an exhausted quota — RocksDB records a
|
|
1010
|
+
background error and, for a hard-or-worse severity, stops accepting writes. The database emits an
|
|
1011
|
+
`'error'` event carrying a `BackgroundError` so a consumer can observe it and recover in-process
|
|
1012
|
+
instead of restarting. When `writesDisabled` is `true`, call
|
|
1013
|
+
[`db.resume()`](#dbresume-void) after the underlying condition clears. Register the listener with
|
|
1014
|
+
`db.on('error', ...)` before the database is put under load — or use the pull-based
|
|
1015
|
+
[`db.getLastError()`](#dbgetlasterror-backgrounderror--null) to catch an error that fired earlier.
|
|
1016
|
+
|
|
1017
|
+
`BackgroundError` is a real `Error` subclass, exported from the package, so both
|
|
1018
|
+
`err instanceof Error` and `err instanceof BackgroundError` hold. It extends `Error` with:
|
|
1019
|
+
|
|
1020
|
+
- `message: string` — the RocksDB error string.
|
|
1021
|
+
- `name: string` — always `'BackgroundError'`.
|
|
1022
|
+
- `type: string` — the error-class discriminator; always `'background'`.
|
|
1023
|
+
- `severity: number` — the RocksDB `Status::Severity`: `1` soft, `2` hard, `3` fatal,
|
|
1024
|
+
`4` unrecoverable.
|
|
1025
|
+
- `severityName: string` — `'soft'`, `'hard'`, `'fatal'`, or `'unrecoverable'`.
|
|
1026
|
+
- `writesDisabled: boolean` — whether RocksDB has disabled writes on the database (`severity >= 2`).
|
|
1027
|
+
Only then is `resume()` warranted; a soft error is auto-recoverable and leaves writes enabled.
|
|
1028
|
+
Distinct from opening the database in read-only mode.
|
|
1029
|
+
- `reason?: number` / `reasonName?: string` — the originating `BackgroundErrorReason`
|
|
1030
|
+
(e.g. `'flush'`, `'compaction'`), present when the error came from a reason-bearing callback.
|
|
1031
|
+
|
|
1032
|
+
```typescript
|
|
1033
|
+
import { BackgroundError } from '@harperfast/rocksdb-js';
|
|
1034
|
+
|
|
1035
|
+
db.on('error', (err) => {
|
|
1036
|
+
if (err instanceof BackgroundError && err.writesDisabled) {
|
|
1037
|
+
console.error(`writes disabled (${err.severityName}): ${err.message}`);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
```typescript
|
|
1043
|
+
db.on('error', (err) => {
|
|
1044
|
+
if (err.writesDisabled) {
|
|
1045
|
+
// Hard-or-worse: RocksDB has disabled writes on this database. They stay
|
|
1046
|
+
// disabled until you clear the underlying condition and call db.resume().
|
|
1047
|
+
console.error(`database writes disabled (${err.severityName}): ${err.message}`);
|
|
1048
|
+
} else {
|
|
1049
|
+
// Soft error: writes are NOT disabled and RocksDB will auto-recover. This
|
|
1050
|
+
// is typically a transient background hiccup (e.g. a retryable I/O error
|
|
1051
|
+
// during flush or compaction), surfaced for visibility only — no action
|
|
1052
|
+
// is required.
|
|
1053
|
+
console.warn(`recoverable background error (${err.severityName}): ${err.message}`);
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
```
|
|
1057
|
+
|
|
797
1058
|
## Event API
|
|
798
1059
|
|
|
799
1060
|
`rocksdb-js` provides a EventEmitter-like API that lets you asynchronously notify events to one or
|
|
@@ -1705,7 +1966,7 @@ snapshots of a database. A backup covers the **entire database** — every colum
|
|
|
1705
1966
|
manifest, and (by default) the write-ahead log — so it is not scoped to an individual `Store`.
|
|
1706
1967
|
|
|
1707
1968
|
A backup can be written to a local **directory** (incremental, with a management API) or streamed to
|
|
1708
|
-
a
|
|
1969
|
+
a `WritableStream` as a tar archive with no intermediate copy on disk. See
|
|
1709
1970
|
[docs/backups.md](docs/backups.md) for a full guide covering both modes, restore, checkpoints, and
|
|
1710
1971
|
caveats.
|
|
1711
1972
|
|
|
@@ -1781,10 +2042,10 @@ required bytes — before any files are copied, so a full destination fails fast
|
|
|
1781
2042
|
through (a mid-copy failure can leave zero usable backups). The required size is estimated
|
|
1782
2043
|
conservatively as a full copy of the database's live files, plus the transaction-log snapshot when
|
|
1783
2044
|
`transactionLogs` is set; since a backup only ever copies files, this can never under-estimate.
|
|
1784
|
-
Incremental backups to a right-sized volume may therefore be over-rejected — set
|
|
1785
|
-
false` to skip the check. It also self-disables when free space can't be determined
|
|
1786
|
-
network filesystems report `0`), so it never blocks a backup on an untrustworthy number.
|
|
1787
|
-
directory-target backups are checked; the streaming backup path is unaffected.
|
|
2045
|
+
Incremental backups to a right-sized volume may therefore be over-rejected — set
|
|
2046
|
+
`checkDiskSpace: false` to skip the check. It also self-disables when free space can't be determined
|
|
2047
|
+
(e.g. some network filesystems report `0`), so it never blocks a backup on an untrustworthy number.
|
|
2048
|
+
Only directory-target backups are checked; the streaming backup path is unaffected.
|
|
1788
2049
|
|
|
1789
2050
|
### `db.backup(stream: WritableStream<Uint8Array>, options?: BackupStreamOptions): Promise<void>`
|
|
1790
2051
|
|