@harperfast/rocksdb-js 2.2.0 → 2.4.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 +122 -9
- package/dist/index.cjs +441 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1480 -1368
- package/dist/index.d.mts +1480 -1368
- package/dist/index.mjs +442 -33
- package/dist/index.mjs.map +1 -1
- package/docs/backups.md +238 -0
- package/package.json +20 -20
package/README.md
CHANGED
|
@@ -996,6 +996,40 @@ Note: If the `callback` throws an error, Node.js suppress the error. Node.js 18.
|
|
|
996
996
|
`--force-node-api-uncaught-exceptions-policy` flag which will cause errors to emit the
|
|
997
997
|
`'uncaughtException'` event. Future Node.js releases will enable this flag by default.
|
|
998
998
|
|
|
999
|
+
## Exclusive File Locking
|
|
1000
|
+
|
|
1001
|
+
`rocksdb-js` includes helper functions for creating lock files and releasing them using native APIs.
|
|
1002
|
+
This can be used to prevent multiple processes from concurrently accessing a resource. The lock is
|
|
1003
|
+
automatically released when the process exits.
|
|
1004
|
+
|
|
1005
|
+
### `tryFileLock(file: string): number`
|
|
1006
|
+
|
|
1007
|
+
Attempts to acquire an exclusive lock on the given file, creating it if it doesn't exist. Returns a
|
|
1008
|
+
non-zero token to pass to `fileLockRelease` if the lock was acquired, or `0` if another holder — in
|
|
1009
|
+
any process, container, or worker thread — currently has it. Throws if the file's parent directory
|
|
1010
|
+
is missing or on a hard error.
|
|
1011
|
+
|
|
1012
|
+
```typescript
|
|
1013
|
+
import { tryFileLock } from '@harperfast/rocksdb-js';
|
|
1014
|
+
|
|
1015
|
+
const token = tryFileLock('/path/to/lock');
|
|
1016
|
+
if (token) {
|
|
1017
|
+
console.log('lock acquired');
|
|
1018
|
+
} else {
|
|
1019
|
+
console.log('lock not available, another process is holding it');
|
|
1020
|
+
}
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
### `fileLockRelease(token: number): void`
|
|
1024
|
+
|
|
1025
|
+
Releases the file lock for the given token.
|
|
1026
|
+
|
|
1027
|
+
```typescript
|
|
1028
|
+
import { fileLockRelease } from '@harperfast/rocksdb-js';
|
|
1029
|
+
|
|
1030
|
+
fileLockRelease(token);
|
|
1031
|
+
```
|
|
1032
|
+
|
|
999
1033
|
## Verification Table
|
|
1000
1034
|
|
|
1001
1035
|
The verification table is a process-global, fixed-size structure that lets an application cheaply
|
|
@@ -1385,16 +1419,57 @@ import { versions } from '@harperfast/rocksdb-js';
|
|
|
1385
1419
|
console.log(versions); // { "rocksdb": "10.10.1", "rocksdb-js": "0.1.2" }
|
|
1386
1420
|
```
|
|
1387
1421
|
|
|
1422
|
+
## Checkpoints
|
|
1423
|
+
|
|
1424
|
+
### `db.createCheckpoint(targetPath: string): Promise<void>`
|
|
1425
|
+
|
|
1426
|
+
Creates a [checkpoint](https://github.com/facebook/rocksdb/wiki/Checkpoints) — a point-in-time,
|
|
1427
|
+
fully independent copy of the entire database (all column families) at `targetPath` — and resolves
|
|
1428
|
+
once written. Unlike a backup, a checkpoint is a normal, writable database: open it as a new
|
|
1429
|
+
`RocksDatabase` and it diverges independently from the source.
|
|
1430
|
+
|
|
1431
|
+
SST and blob files are **hard-linked** when `targetPath` is on the **same filesystem** as the
|
|
1432
|
+
database, and **copied** otherwise; other files (such as the `MANIFEST`) are always copied. As a
|
|
1433
|
+
result the operation is near-instant on the same filesystem and as costly as a full copy across
|
|
1434
|
+
filesystems. The memtable is flushed so the checkpoint includes the latest writes even when the
|
|
1435
|
+
WAL is disabled.
|
|
1436
|
+
|
|
1437
|
+
Parent directories are created as needed. `targetPath` itself must not already exist — RocksDB
|
|
1438
|
+
creates the checkpoint directory — and the call rejects with `Create checkpoint failed: target
|
|
1439
|
+
path exists` if it does (other failures, such as a full disk, surface the RocksDB status message).
|
|
1440
|
+
The caller is responsible for opening the checkpoint and for eventually deleting the directory.
|
|
1441
|
+
|
|
1442
|
+
```typescript
|
|
1443
|
+
const db = RocksDatabase.open('/path/to/database');
|
|
1444
|
+
await db.createCheckpoint('/path/to/checkpoint');
|
|
1445
|
+
|
|
1446
|
+
// The checkpoint is a normal, writable database.
|
|
1447
|
+
const branch = RocksDatabase.open('/path/to/checkpoint');
|
|
1448
|
+
```
|
|
1449
|
+
|
|
1388
1450
|
## Backups
|
|
1389
1451
|
|
|
1390
1452
|
Backups use RocksDB's `BackupEngine` to capture consistent, incremental, checksum-verified
|
|
1391
1453
|
snapshots of a database. A backup covers the **entire database** — every column family, the
|
|
1392
1454
|
manifest, and (by default) the write-ahead log — so it is not scoped to an individual `Store`.
|
|
1393
1455
|
|
|
1456
|
+
A backup can be written to a local **directory** (incremental, with a management API) or streamed to
|
|
1457
|
+
a **`WritableStream`** as a tar archive with no intermediate copy on disk. See
|
|
1458
|
+
[docs/backups.md](docs/backups.md) for a full guide covering both modes, restore, checkpoints, and
|
|
1459
|
+
caveats.
|
|
1460
|
+
|
|
1394
1461
|
Creating a backup is an instance method (`db.backup()`) because it needs a live database. The
|
|
1395
1462
|
remaining operations act on a backup directory and do not require an open database, so they are
|
|
1396
1463
|
grouped under the `backups` namespace export.
|
|
1397
1464
|
|
|
1465
|
+
> **Only one backup per directory may be in-flight at a time.** RocksDB has no cross-engine lock on
|
|
1466
|
+
> a backup directory, so the writing operations — `db.backup()`, `backups.delete()`, and
|
|
1467
|
+
> `backups.purge()` — take an on-disk lock (a `.backup.lock` file) for the directory. A second
|
|
1468
|
+
> writing operation on the same directory, whether from the same process, a `worker_thread`, or a
|
|
1469
|
+
> separate process, **rejects** with a "locked" error rather than corrupting the backup; retry once
|
|
1470
|
+
> the in-flight operation finishes. Operations on _different_ directories run in parallel, and the
|
|
1471
|
+
> read-only operations (`list`, `verify`, `restore`) are not locked.
|
|
1472
|
+
|
|
1398
1473
|
```typescript
|
|
1399
1474
|
import { RocksDatabase, backups } from '@harperfast/rocksdb-js';
|
|
1400
1475
|
|
|
@@ -1422,15 +1497,53 @@ const id = await db.backup('/path/to/backups', { metadata: 'nightly-2026-06-04'
|
|
|
1422
1497
|
|
|
1423
1498
|
`BackupOptions`:
|
|
1424
1499
|
|
|
1425
|
-
| Option | Type | Default | Description
|
|
1426
|
-
| ------------------------- | --------- | ---------------------- |
|
|
1427
|
-
| `
|
|
1428
|
-
| `
|
|
1429
|
-
| `
|
|
1430
|
-
| `
|
|
1431
|
-
| `
|
|
1432
|
-
| `
|
|
1433
|
-
| `
|
|
1500
|
+
| Option | Type | Default | Description |
|
|
1501
|
+
| ------------------------- | --------- | ---------------------- | ------------------------------------------------------------------------------------ |
|
|
1502
|
+
| `backupLogFiles` | `boolean` | `true` | Include write-ahead log files in the backup. |
|
|
1503
|
+
| `flushBeforeBackup` | `boolean` | `true` if WAL disabled | Flush the memtable before backing up. |
|
|
1504
|
+
| `maxBackgroundOperations` | `number` | `1` | Number of background threads used to copy files. |
|
|
1505
|
+
| `metadata` | `string` | `''` | Application metadata stored with the backup, returned by `list()`. |
|
|
1506
|
+
| `shareFilesWithChecksum` | `boolean` | `true` | Distinguish shared files by checksum to avoid cross-database clashes. |
|
|
1507
|
+
| `shareTableFiles` | `boolean` | `true` | Share files between backups to enable incremental backups. |
|
|
1508
|
+
| `sync` | `boolean` | `true` | `fsync` backup files (including the transaction log snapshot) for crash consistency. |
|
|
1509
|
+
| `transactionLogs` | `boolean` | `false` | Snapshot the transaction log store into `<backupDir>/transaction_logs/<backupId>/`. |
|
|
1510
|
+
|
|
1511
|
+
When `transactionLogs` is enabled, the log snapshot is staged and atomically renamed into
|
|
1512
|
+
`<backupDir>/transaction_logs/<backupId>/` only after every file has been copied (and fsynced, per
|
|
1513
|
+
`sync`), so a crash mid-backup can never leave a partial log snapshot for a listed backup id — a
|
|
1514
|
+
backup either has its complete snapshot or none. The snapshot is captured just after the RocksDB
|
|
1515
|
+
engine snapshot, so restored logs may run slightly ahead of the restored key-value data (never
|
|
1516
|
+
behind it), which is safe for redo-style logs replayed against the restored data.
|
|
1517
|
+
|
|
1518
|
+
### `db.backup(stream: WritableStream<Uint8Array>, options?: BackupStreamOptions): Promise<void>`
|
|
1519
|
+
|
|
1520
|
+
Streams a consistent snapshot of the entire database to `stream` as a tar archive, with **no
|
|
1521
|
+
intermediate copy written to disk**, and resolves once the stream has been fully written and closed.
|
|
1522
|
+
Backpressure is honored end to end, so a slow consumer (e.g. an upload) paces the backup rather than
|
|
1523
|
+
buffering it in memory. The archive unpacks with any tar tool into a directory that opens as a
|
|
1524
|
+
RocksDB database.
|
|
1525
|
+
|
|
1526
|
+
```typescript
|
|
1527
|
+
import { createWriteStream } from 'node:fs';
|
|
1528
|
+
import { Writable } from 'node:stream';
|
|
1529
|
+
|
|
1530
|
+
await db.backup(Writable.toWeb(createWriteStream('/path/to/backup.tar')));
|
|
1531
|
+
// Restore: `tar -xf backup.tar -C /restored`, then open '/restored'.
|
|
1532
|
+
|
|
1533
|
+
// Or gzip it (`tar -xzf backup.tar.gz` to restore):
|
|
1534
|
+
await db.backup(Writable.toWeb(createWriteStream('/path/to/backup.tar.gz')), { gzip: true });
|
|
1535
|
+
```
|
|
1536
|
+
|
|
1537
|
+
`BackupStreamOptions`:
|
|
1538
|
+
|
|
1539
|
+
| Option | Type | Default | Description |
|
|
1540
|
+
| ------------------- | --------- | ---------------------- | ------------------------------------------------------------------- |
|
|
1541
|
+
| `flushBeforeBackup` | `boolean` | `true` if WAL disabled | Flush the memtable before streaming. |
|
|
1542
|
+
| `gzip` | `boolean` | `false` | Gzip-compress the archive, producing a `.tar.gz` instead of `.tar`. |
|
|
1543
|
+
|
|
1544
|
+
Stream backups are always full snapshots (no incremental sharing), have no `backups.*` management
|
|
1545
|
+
API, and **cannot be resumed** — a failed transfer must be restarted from the beginning. See
|
|
1546
|
+
[docs/backups.md](docs/backups.md#stream-backups) for details.
|
|
1434
1547
|
|
|
1435
1548
|
### `backups.restore(backupDir: string, dbDir: string, options?: RestoreOptions): Promise<void>`
|
|
1436
1549
|
|