@harperfast/rocksdb-js 2.11.0 → 2.12.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 +25 -16
- package/dist/index.cjs +12 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.mjs +12 -2
- package/dist/index.mjs.map +1 -1
- package/docs/backups.md +27 -0
- package/docs/stats.md +7 -4
- package/docs/transaction-log.md +22 -3
- package/package.json +17 -16
package/docs/backups.md
CHANGED
|
@@ -80,6 +80,33 @@ verification). Pass `verifyTransactionLogs: false` to check only the RocksDB fil
|
|
|
80
80
|
stores (e.g. the `transaction_logs` directory of a closed database) can be validated directly with
|
|
81
81
|
`validateTransactionLogStore()`.
|
|
82
82
|
|
|
83
|
+
Pass `verifyTables: true` to check that **this build** can read the backup, not only that its files
|
|
84
|
+
are intact. Every table file is opened in turn (one at a time), so verification rejects, naming the
|
|
85
|
+
file, when a table uses a `format_version` or a compression algorithm this build does not support.
|
|
86
|
+
That is the case after a downgrade, or when restoring a backup taken by a build that linked a
|
|
87
|
+
compressor this one lacks. Blob files are not checked.
|
|
88
|
+
|
|
89
|
+
Check before a destructive restore. The default `purgeAllFiles` mode deletes the destination before
|
|
90
|
+
copying, so an unreadable backup would otherwise destroy the database it was meant to replace:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { backups, fileLockRelease, tryFileLock } from '@harperfast/rocksdb-js';
|
|
94
|
+
import { join } from 'node:path';
|
|
95
|
+
|
|
96
|
+
// A shared lock keeps a concurrent delete/purge from changing the backup between the two calls.
|
|
97
|
+
// Shared locks coexist, so restore can still take its own.
|
|
98
|
+
const token = tryFileLock(join(backupDir, '.backup.lock'), true);
|
|
99
|
+
if (!token) {
|
|
100
|
+
throw new Error('A backup, delete, or purge is running on this directory; retry later');
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
await backups.verify(backupDir, backupId, { verifyWithChecksum: true, verifyTables: true });
|
|
104
|
+
await backups.restore(backupDir, dbPath, { backupId });
|
|
105
|
+
} finally {
|
|
106
|
+
fileLockRelease(token);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
83
110
|
### Restoring
|
|
84
111
|
|
|
85
112
|
```typescript
|
package/docs/stats.md
CHANGED
|
@@ -493,7 +493,9 @@ const stats: TransactionLogStats = log.getStats();
|
|
|
493
493
|
|
|
494
494
|
- `name: string` The name of the transaction log store.
|
|
495
495
|
- `path: string` Filesystem path to the log store's directory.
|
|
496
|
-
- `fileCount: number` Number of sequence log files on disk for this log.
|
|
496
|
+
- `fileCount: number` Number of sequence log files on disk for this log. `0` does not mean the log
|
|
497
|
+
was never written: retention can purge every file of an idle log. A log that has never been written
|
|
498
|
+
reports `currentSequenceNumber` 1; one emptied by retention reports a higher one.
|
|
497
499
|
- `currentSequenceNumber: number` Sequence number of the active write file.
|
|
498
500
|
- `oldestSequenceNumber: number` Sequence number of the oldest file still on disk.
|
|
499
501
|
- `totalSizeBytes: number` Total on-disk size of all of this log's files.
|
|
@@ -522,9 +524,10 @@ const stats: TransactionLogStats = log.getStats();
|
|
|
522
524
|
- `offset: number` The byte offset within that file.
|
|
523
525
|
- `purge: object`
|
|
524
526
|
- `oldestFileAgeMs: number` Age in milliseconds of the oldest file on disk.
|
|
525
|
-
- `purgeableFiles: number` Number of files
|
|
526
|
-
|
|
527
|
-
|
|
527
|
+
- `purgeableFiles: number` Number of files past the retention threshold that lie entirely at or
|
|
528
|
+
before the flushed position, including the current file, so the next purge can delete them.
|
|
529
|
+
- `retentionPins: number` Backups currently copying this store's files. Ordinary purges of the
|
|
530
|
+
store are skipped while it is nonzero, so a value that stays nonzero points at a stalled backup.
|
|
528
531
|
- `retainedUnflushedFiles: number` Number of files past the retention threshold but retained
|
|
529
532
|
because they are unflushed.
|
|
530
533
|
- `lastPurgeMs: number` Timestamp of the last purge scan.
|
package/docs/transaction-log.md
CHANGED
|
@@ -198,9 +198,23 @@ await db.transaction((txn) => {
|
|
|
198
198
|
- Log files are automatically rotated when either the index or data file reaches their configured
|
|
199
199
|
maximum sizes
|
|
200
200
|
- Rotation happens on the next write after the size limit is exceeded
|
|
201
|
-
- Old log files can be automatically purged based on retention policy.
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
- Old log files can be automatically purged based on retention policy. A file is purgeable once it
|
|
202
|
+
is past the cutoff and lies entirely at or before the flushed position recorded in `txn.state`.
|
|
203
|
+
That includes the file `txn.state` names and the file currently being appended to, so an idle
|
|
204
|
+
store whose entries are all flushed ends up with no log files at all. The next write starts a new,
|
|
205
|
+
higher sequence; `txn.state` stays behind as the record of the highest sequence used, and a
|
|
206
|
+
restart appends past it. Before deleting a store's last file, retention syncs `txn.state` and its
|
|
207
|
+
directory; on a filesystem that rejects a directory fsync it keeps that file and warns once.
|
|
208
|
+
- `txn.state` is rewritten in place (8 bytes at offset 0, not synced on each flush). Retention relies
|
|
209
|
+
on storage writing that range atomically: a power loss that tears it into a mix of old and new
|
|
210
|
+
bytes can name a later position than was flushed, and retention would then treat unflushed
|
|
211
|
+
entries below it as flushed.
|
|
212
|
+
- A reader that starts from the last flushed position, or one already inside a purged file,
|
|
213
|
+
continues at the lowest retained sequence (or at the next write when nothing is retained).
|
|
214
|
+
- Downgrading to a release without this rule after a store's last file was purged is unsafe: older
|
|
215
|
+
releases restart an empty store at sequence 1, below the flushed position, so a replay from
|
|
216
|
+
`txn.state` skips the new writes and retention treats them as flushed. Remove the store's
|
|
217
|
+
`txn.state` before downgrading.
|
|
204
218
|
|
|
205
219
|
### Error Handling
|
|
206
220
|
|
|
@@ -251,6 +265,11 @@ handle is returned, so no transaction can be constructed below it. Keys are not
|
|
|
251
265
|
across segments, and a segment header records the store's latest timestamp only as of that
|
|
252
266
|
segment's creation, so there is no shortcut: every segment is read.
|
|
253
267
|
|
|
268
|
+
Retention can purge every segment of the named log. The walk then finds no keys and leaves the clock
|
|
269
|
+
alone, so a backward step after restart can reissue a key that was only in purged segments. With
|
|
270
|
+
age-based retention alone that needs a backward step at least as large as the retention period; a
|
|
271
|
+
`purgeLogs({ before })` cutoff closer to now shrinks it to the time since that cutoff.
|
|
272
|
+
|
|
254
273
|
Name only a log this process originates. A log a replication receiver writes under an adopted origin
|
|
255
274
|
timestamp is keyed by another node's clock, and seeding from it would ratchet this process's clock
|
|
256
275
|
to the fastest of those nodes at each restart. Native code cannot tell the two kinds of log apart,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harperfast/rocksdb-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "RocksDB binding for Node.js",
|
|
5
5
|
"homepage": "https://github.com/HarperFast/rocksdb-js",
|
|
6
6
|
"bugs": [
|
|
@@ -38,32 +38,32 @@
|
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@rollup/plugin-replace": "6.0.3",
|
|
41
|
-
"@types/node": "26.6.
|
|
41
|
+
"@types/node": "26.6.5",
|
|
42
42
|
"@vitest/coverage-v8": "4.1.11",
|
|
43
43
|
"cross-env": "10.1.0",
|
|
44
|
-
"dotenv": "18.0.
|
|
45
|
-
"lefthook": "2.1
|
|
44
|
+
"dotenv": "18.0.7",
|
|
45
|
+
"lefthook": "2.2.1",
|
|
46
46
|
"lmdb": "3.5.7",
|
|
47
47
|
"node-gyp": "13.1.0",
|
|
48
|
-
"oxfmt": "0.
|
|
49
|
-
"oxlint": "1.
|
|
48
|
+
"oxfmt": "0.72.0",
|
|
49
|
+
"oxlint": "1.87.0",
|
|
50
50
|
"prebuildify": "6.0.1",
|
|
51
51
|
"semver": "7.8.5",
|
|
52
52
|
"tar": "7.5.22",
|
|
53
|
-
"tsdown": "0.23.
|
|
53
|
+
"tsdown": "0.23.1",
|
|
54
54
|
"tslib": "2.8.1",
|
|
55
55
|
"typescript": "7.0.2",
|
|
56
56
|
"vitest": "4.1.11"
|
|
57
57
|
},
|
|
58
58
|
"optionalDependencies": {
|
|
59
|
-
"@harperfast/rocksdb-js-darwin-arm64": "2.
|
|
60
|
-
"@harperfast/rocksdb-js-darwin-x64": "2.
|
|
61
|
-
"@harperfast/rocksdb-js-linux-arm64-glibc": "2.
|
|
62
|
-
"@harperfast/rocksdb-js-linux-arm64-musl": "2.
|
|
63
|
-
"@harperfast/rocksdb-js-linux-x64-glibc": "2.
|
|
64
|
-
"@harperfast/rocksdb-js-linux-x64-musl": "2.
|
|
65
|
-
"@harperfast/rocksdb-js-win32-arm64": "2.
|
|
66
|
-
"@harperfast/rocksdb-js-win32-x64": "2.
|
|
59
|
+
"@harperfast/rocksdb-js-darwin-arm64": "2.12.0",
|
|
60
|
+
"@harperfast/rocksdb-js-darwin-x64": "2.12.0",
|
|
61
|
+
"@harperfast/rocksdb-js-linux-arm64-glibc": "2.12.0",
|
|
62
|
+
"@harperfast/rocksdb-js-linux-arm64-musl": "2.12.0",
|
|
63
|
+
"@harperfast/rocksdb-js-linux-x64-glibc": "2.12.0",
|
|
64
|
+
"@harperfast/rocksdb-js-linux-x64-musl": "2.12.0",
|
|
65
|
+
"@harperfast/rocksdb-js-win32-arm64": "2.12.0",
|
|
66
|
+
"@harperfast/rocksdb-js-win32-x64": "2.12.0"
|
|
67
67
|
},
|
|
68
68
|
"engines": {
|
|
69
69
|
"node": "^22.18.0 || >=24.0.0"
|
|
@@ -81,7 +81,8 @@
|
|
|
81
81
|
"build:binding:debug": "node-gyp build --coverage --debug --verbose",
|
|
82
82
|
"build:bundle": "node scripts/clean-dist.mjs && tsdown -c tsdown.config.ts",
|
|
83
83
|
"build:bundle:minify": "cross-env MINIFY=1 pnpm build:bundle",
|
|
84
|
-
"check": "pnpm
|
|
84
|
+
"check": "pnpm check:code && pnpm fmt:check",
|
|
85
|
+
"check:code": "pnpm type-check && pnpm lint",
|
|
85
86
|
"clean": "node-gyp clean",
|
|
86
87
|
"coverage": "node scripts/coverage/main.ts",
|
|
87
88
|
"coverage:native": "node scripts/native-test/coverage.mjs",
|