@harperfast/rocksdb-js 2.0.0 → 2.2.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 +344 -31
- package/bin/rocksdb-js.mjs +290 -2
- package/dist/index.cjs +224 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +770 -31
- package/dist/index.d.mts +770 -31
- package/dist/index.mjs +223 -35
- package/dist/index.mjs.map +1 -1
- package/docs/stats.md +459 -0
- package/docs/transaction-log.md +279 -0
- package/package.json +20 -19
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { execSync } from "node:child_process";
|
|
|
3
3
|
import { closeSync, openSync, readFileSync, readSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { mkdir } from "node:fs/promises";
|
|
6
7
|
import * as orderedBinary from "ordered-binary";
|
|
7
8
|
import { ExtendedIterable } from "@harperfast/extended-iterable";
|
|
8
9
|
import { Encoder } from "msgpackr";
|
|
@@ -55,6 +56,7 @@ function locateBinding() {
|
|
|
55
56
|
}
|
|
56
57
|
const binding = req(locateBinding());
|
|
57
58
|
const config = binding.config;
|
|
59
|
+
const FRESH_VERSION_FLAG$1 = binding.constants.FRESH_VERSION_FLAG;
|
|
58
60
|
const addGlobalListener = binding.addListener;
|
|
59
61
|
const removeGlobalListener = binding.removeListener;
|
|
60
62
|
const globalListenerCount = binding.listenerCount;
|
|
@@ -67,9 +69,95 @@ const TransactionLog = binding.TransactionLog;
|
|
|
67
69
|
const registryStatus = binding.registryStatus;
|
|
68
70
|
const shutdown = binding.shutdown;
|
|
69
71
|
const currentThreadId = binding.currentThreadId;
|
|
72
|
+
/**
|
|
73
|
+
* Advises the kernel that the file-backed pages of every mapped transaction log
|
|
74
|
+
* are cold (Linux MADV_COLD), so they are reclaimed first under memory pressure
|
|
75
|
+
* without being freed — useful during replication catch-up, where a full read of
|
|
76
|
+
* the logs would otherwise inflate the container's reclaimable cache toward its
|
|
77
|
+
* cgroup limit. No-op on kernels < 5.4, macOS, and Windows.
|
|
78
|
+
*
|
|
79
|
+
* The transaction log registry is a process-global singleton shared across all
|
|
80
|
+
* worker threads, so a single call cools every worker's maps. Call it on an
|
|
81
|
+
* interval from one thread (e.g. an `unref()`ed timer on the main thread).
|
|
82
|
+
*
|
|
83
|
+
* @returns the number of maps cooled and total file-backed bytes advised.
|
|
84
|
+
*/
|
|
85
|
+
const coolTransactionLogs = binding.coolTransactionLogs;
|
|
86
|
+
/**
|
|
87
|
+
* Number of live transaction-log memory maps across the process. Internal —
|
|
88
|
+
* used by tests to verify that releasing a frozen log's external buffer unmaps
|
|
89
|
+
* the underlying mapping rather than leaving it retained.
|
|
90
|
+
*/
|
|
91
|
+
const transactionLogMapCount = binding.transactionLogMapCount;
|
|
92
|
+
const nativeBackupRestore = binding.backupRestore;
|
|
93
|
+
const nativeBackupList = binding.backupList;
|
|
94
|
+
const nativeBackupDelete = binding.backupDelete;
|
|
95
|
+
const nativeBackupPurge = binding.backupPurge;
|
|
96
|
+
const nativeBackupVerify = binding.backupVerify;
|
|
70
97
|
const stats = binding.stats;
|
|
71
98
|
const version = binding.version;
|
|
72
99
|
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/backup.ts
|
|
102
|
+
/**
|
|
103
|
+
* Backup management operations that act on a backup directory and do not
|
|
104
|
+
* require an open database. To create a backup, use the `db.backup()` instance
|
|
105
|
+
* method instead (it needs a live database for a consistent snapshot).
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* import { backups } from '@harperfast/rocksdb-js';
|
|
110
|
+
*
|
|
111
|
+
* const id = await db.backup('/path/to/backups');
|
|
112
|
+
* db.close();
|
|
113
|
+
* await backups.restore('/path/to/backups', '/path/to/restored-db');
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
const backups = {
|
|
117
|
+
/**
|
|
118
|
+
* Restores a database from a backup directory into a (closed) database
|
|
119
|
+
* directory. The default mode purges the destination directory, so it must
|
|
120
|
+
* not point at a live database.
|
|
121
|
+
*/
|
|
122
|
+
async restore(backupDir, dbDir, options) {
|
|
123
|
+
if (resolve(backupDir) === resolve(dbDir)) throw new Error("Backup directory and database directory must be different");
|
|
124
|
+
const walDir = options?.walDir ?? dbDir;
|
|
125
|
+
await mkdir(dbDir, { recursive: true });
|
|
126
|
+
if (walDir !== dbDir) await mkdir(walDir, { recursive: true });
|
|
127
|
+
return new Promise((resolve, reject) => nativeBackupRestore(resolve, reject, backupDir, dbDir, walDir, {
|
|
128
|
+
backupId: options?.backupId,
|
|
129
|
+
keepLogFiles: options?.keepLogFiles,
|
|
130
|
+
mode: options?.mode
|
|
131
|
+
}));
|
|
132
|
+
},
|
|
133
|
+
/**
|
|
134
|
+
* Lists the non-corrupt backups in a backup directory, ordered by id.
|
|
135
|
+
*/
|
|
136
|
+
list(backupDir) {
|
|
137
|
+
return new Promise((resolve, reject) => nativeBackupList(resolve, reject, backupDir));
|
|
138
|
+
},
|
|
139
|
+
/**
|
|
140
|
+
* Deletes a specific backup. Shared files are reference-counted and only
|
|
141
|
+
* removed once no remaining backup references them.
|
|
142
|
+
*/
|
|
143
|
+
delete(backupDir, backupId) {
|
|
144
|
+
return new Promise((resolve, reject) => nativeBackupDelete(resolve, reject, backupDir, backupId));
|
|
145
|
+
},
|
|
146
|
+
/**
|
|
147
|
+
* Deletes all but the newest `keepCount` backups.
|
|
148
|
+
*/
|
|
149
|
+
purge(backupDir, keepCount) {
|
|
150
|
+
return new Promise((resolve, reject) => nativeBackupPurge(resolve, reject, backupDir, keepCount));
|
|
151
|
+
},
|
|
152
|
+
/**
|
|
153
|
+
* Verifies a backup's file sizes, and optionally their checksums (which
|
|
154
|
+
* requires reading all backed-up data).
|
|
155
|
+
*/
|
|
156
|
+
verify(backupDir, backupId, options) {
|
|
157
|
+
return new Promise((resolve, reject) => nativeBackupVerify(resolve, reject, backupDir, backupId, options?.verifyWithChecksum ?? false));
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
73
161
|
//#endregion
|
|
74
162
|
//#region src/util.ts
|
|
75
163
|
/**
|
|
@@ -188,11 +276,11 @@ var DBI = class DBI {
|
|
|
188
276
|
*/
|
|
189
277
|
get(key, options) {
|
|
190
278
|
if (this.store.decoderCopies) return when(() => this.getBinaryFast(key, options), (result) => {
|
|
191
|
-
if (result === void 0) return;
|
|
279
|
+
if (result === void 0 || result === FRESH_VERSION_FLAG$1) return result;
|
|
192
280
|
if (options?.skipDecode) return result;
|
|
193
281
|
return this.store.decodeValue(result);
|
|
194
282
|
});
|
|
195
|
-
return when(() => this.getBinary(key, options), (result) => result === void 0 ?
|
|
283
|
+
return when(() => this.getBinary(key, options), (result) => result === void 0 || result === FRESH_VERSION_FLAG$1 ? result : this.store.encoding === "binary" || !this.store.decoder || options?.skipDecode ? result : this.store.decodeValue(result));
|
|
196
284
|
}
|
|
197
285
|
/**
|
|
198
286
|
* Retrieves the binary data for the given key. This is just like `get()`,
|
|
@@ -202,7 +290,7 @@ var DBI = class DBI {
|
|
|
202
290
|
*/
|
|
203
291
|
getBinary(key, options) {
|
|
204
292
|
if (!this.store.isOpen()) return Promise.reject(/* @__PURE__ */ new Error("Database not open"));
|
|
205
|
-
return this.store.get(this._context, key, true,
|
|
293
|
+
return this.store.get(this._context, key, true, options);
|
|
206
294
|
}
|
|
207
295
|
/**
|
|
208
296
|
* Synchronously retrieves the binary data for the given key.
|
|
@@ -223,7 +311,7 @@ var DBI = class DBI {
|
|
|
223
311
|
*/
|
|
224
312
|
getBinaryFast(key, options) {
|
|
225
313
|
if (!this.store.isOpen()) return Promise.reject(/* @__PURE__ */ new Error("Database not open"));
|
|
226
|
-
return this.store.get(this._context, key, false,
|
|
314
|
+
return this.store.get(this._context, key, false, options);
|
|
227
315
|
}
|
|
228
316
|
/**
|
|
229
317
|
* Synchronously retrieves the binary data for the given key using a
|
|
@@ -282,12 +370,12 @@ var DBI = class DBI {
|
|
|
282
370
|
getSync(key, options) {
|
|
283
371
|
if (this.store.decoderCopies) {
|
|
284
372
|
const bytes = this.getBinaryFastSync(key, options);
|
|
285
|
-
return bytes === void 0 ?
|
|
373
|
+
return bytes === void 0 || bytes === FRESH_VERSION_FLAG$1 ? bytes : this.store.decodeValue(bytes);
|
|
286
374
|
}
|
|
287
375
|
if (this.store.encoding === "binary") return this.getBinarySync(key, options);
|
|
288
376
|
if (this.store.decoder) {
|
|
289
377
|
const result = this.getBinarySync(key, options);
|
|
290
|
-
return result ? this.store.decodeValue(result)
|
|
378
|
+
return !result || result === FRESH_VERSION_FLAG$1 ? result : this.store.decodeValue(result);
|
|
291
379
|
}
|
|
292
380
|
if (!this.store.isOpen()) throw new Error("Database not open");
|
|
293
381
|
return this.store.decodeValue(this.store.getSync(this._context, key, true, options));
|
|
@@ -450,17 +538,25 @@ var DBIterator = class {
|
|
|
450
538
|
iterator;
|
|
451
539
|
store;
|
|
452
540
|
#includeValues;
|
|
453
|
-
|
|
541
|
+
#limit;
|
|
542
|
+
#yielded = 0;
|
|
543
|
+
constructor(iterator, store, includeValues, limit) {
|
|
454
544
|
this.iterator = iterator;
|
|
455
545
|
this.store = store;
|
|
456
546
|
this.#includeValues = includeValues;
|
|
547
|
+
this.#limit = limit ?? void 0;
|
|
457
548
|
}
|
|
458
549
|
[Symbol.iterator]() {
|
|
459
550
|
return this;
|
|
460
551
|
}
|
|
461
552
|
next() {
|
|
553
|
+
if (this.#limit !== void 0 && this.#yielded >= this.#limit) {
|
|
554
|
+
this.iterator.return?.();
|
|
555
|
+
return DONE_RESULT;
|
|
556
|
+
}
|
|
462
557
|
const result = this.iterator.next();
|
|
463
558
|
if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
|
|
559
|
+
this.#yielded++;
|
|
464
560
|
const includeValues = this.#includeValues;
|
|
465
561
|
const value = {};
|
|
466
562
|
if (result === ITERATOR_RESULT_FAST) {
|
|
@@ -562,7 +658,7 @@ function createFixedBuffer(size) {
|
|
|
562
658
|
|
|
563
659
|
//#endregion
|
|
564
660
|
//#region src/store.ts
|
|
565
|
-
const { ONLY_IF_IN_MEMORY_CACHE_FLAG, NOT_IN_MEMORY_CACHE_FLAG, ALWAYS_CREATE_NEW_BUFFER_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;
|
|
661
|
+
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;
|
|
566
662
|
const KEY_BUFFER_SIZE = 4096;
|
|
567
663
|
const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
|
|
568
664
|
const VALUE_BUFFER = createFixedBuffer(64 * 1024);
|
|
@@ -721,6 +817,10 @@ var Store = class {
|
|
|
721
817
|
*/
|
|
722
818
|
transactionLogsPath;
|
|
723
819
|
/**
|
|
820
|
+
* Whether this store's column family participates in the VerificationTable.
|
|
821
|
+
*/
|
|
822
|
+
verificationTable;
|
|
823
|
+
/**
|
|
724
824
|
* The per-column-family memtable size in bytes at which the memtable is
|
|
725
825
|
* sealed and flushed.
|
|
726
826
|
*/
|
|
@@ -767,6 +867,7 @@ var Store = class {
|
|
|
767
867
|
this.transactionLogMaxSize = options?.transactionLogMaxSize;
|
|
768
868
|
this.transactionLogRetention = options?.transactionLogRetention;
|
|
769
869
|
this.transactionLogsPath = options?.transactionLogsPath;
|
|
870
|
+
this.verificationTable = options?.verificationTable;
|
|
770
871
|
this.writeBufferSize = options?.writeBufferSize;
|
|
771
872
|
this.writeKey = writeKey;
|
|
772
873
|
}
|
|
@@ -800,6 +901,20 @@ var Store = class {
|
|
|
800
901
|
return new Promise((resolve, reject) => this.db.compact(resolve, reject, startBuffer, endBuffer));
|
|
801
902
|
}
|
|
802
903
|
/**
|
|
904
|
+
* Creates a backup of the entire database (all column families) into the
|
|
905
|
+
* given directory and resolves with the new backup id. Parent directories
|
|
906
|
+
* are created as needed. See `backups` for restore and management.
|
|
907
|
+
*
|
|
908
|
+
* @example
|
|
909
|
+
* ```typescript
|
|
910
|
+
* const id = await db.backup('/path/to/backups');
|
|
911
|
+
* ```
|
|
912
|
+
*/
|
|
913
|
+
async backup(backupDir, options) {
|
|
914
|
+
await mkdir(backupDir, { recursive: true });
|
|
915
|
+
return new Promise((resolve, reject) => this.db.backup(resolve, reject, backupDir, options));
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
803
918
|
* Compacts the entire key range of the database synchronously.
|
|
804
919
|
* This triggers manual compaction which removes tombstones and reclaims space.
|
|
805
920
|
*
|
|
@@ -872,16 +987,19 @@ var Store = class {
|
|
|
872
987
|
if (value instanceof Uint8Array) return value;
|
|
873
988
|
throw new Error(`Invalid value put in database (${typeof value}), consider using an encoder`);
|
|
874
989
|
}
|
|
875
|
-
get(context, key, alwaysCreateNewBuffer = false,
|
|
990
|
+
get(context, key, alwaysCreateNewBuffer = false, options) {
|
|
876
991
|
const keyParam = getKeyParam(this.encodeKey(key));
|
|
877
992
|
let flags = 0;
|
|
878
993
|
if (alwaysCreateNewBuffer) flags |= ALWAYS_CREATE_NEW_BUFFER_FLAG;
|
|
879
|
-
if (
|
|
880
|
-
const
|
|
994
|
+
if (options?.populateVersion) flags |= POPULATE_VERSION_FLAG;
|
|
995
|
+
const txnId = this.getTxnId(options);
|
|
996
|
+
const expectedVersion = options?.expectedVersion;
|
|
997
|
+
const result = context.getSync(keyParam, flags | ONLY_IF_IN_MEMORY_CACHE_FLAG, txnId, expectedVersion);
|
|
881
998
|
if (typeof result === "number") {
|
|
882
999
|
if (result === NOT_IN_MEMORY_CACHE_FLAG) return new Promise((resolve, reject) => {
|
|
883
|
-
context.get(keyParam, resolve, reject, txnId);
|
|
1000
|
+
context.get(keyParam, resolve, reject, txnId, expectedVersion);
|
|
884
1001
|
});
|
|
1002
|
+
if (result === FRESH_VERSION_FLAG) return result;
|
|
885
1003
|
VALUE_BUFFER.end = result;
|
|
886
1004
|
return VALUE_BUFFER;
|
|
887
1005
|
}
|
|
@@ -948,14 +1066,16 @@ var Store = class {
|
|
|
948
1066
|
if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
|
|
949
1067
|
if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
|
|
950
1068
|
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;
|
|
951
|
-
return new ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues));
|
|
1069
|
+
return new ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues, options?.limit));
|
|
952
1070
|
}
|
|
953
1071
|
getSync(context, key, alwaysCreateNewBuffer = false, options) {
|
|
954
1072
|
const keyParam = getKeyParam(this.encodeKey(key));
|
|
955
1073
|
let flags = 0;
|
|
956
1074
|
if (alwaysCreateNewBuffer) flags |= ALWAYS_CREATE_NEW_BUFFER_FLAG;
|
|
957
|
-
|
|
1075
|
+
if (options?.populateVersion) flags |= POPULATE_VERSION_FLAG;
|
|
1076
|
+
const result = context.getSync(keyParam, flags, this.getTxnId(options), options?.expectedVersion);
|
|
958
1077
|
if (typeof result === "number") {
|
|
1078
|
+
if (result === FRESH_VERSION_FLAG) return result;
|
|
959
1079
|
VALUE_BUFFER.end = result;
|
|
960
1080
|
return VALUE_BUFFER;
|
|
961
1081
|
}
|
|
@@ -1044,6 +1164,7 @@ var Store = class {
|
|
|
1044
1164
|
transactionLogMaxSize: this.transactionLogMaxSize,
|
|
1045
1165
|
transactionLogRetentionMs: this.transactionLogRetention ? parseDuration(this.transactionLogRetention) : void 0,
|
|
1046
1166
|
transactionLogsPath: this.transactionLogsPath,
|
|
1167
|
+
verificationTable: this.verificationTable,
|
|
1047
1168
|
writeBufferSize: this.writeBufferSize
|
|
1048
1169
|
});
|
|
1049
1170
|
return false;
|
|
@@ -1093,6 +1214,25 @@ var Store = class {
|
|
|
1093
1214
|
return context.useLog(String(name));
|
|
1094
1215
|
}
|
|
1095
1216
|
/**
|
|
1217
|
+
* Checks the process-global verification table for a fresh version match
|
|
1218
|
+
* on `key`. Returns `true` when the table currently records `version` for
|
|
1219
|
+
* this database+column-family. Provides a fast cache-freshness check
|
|
1220
|
+
* before falling back to a full read.
|
|
1221
|
+
*/
|
|
1222
|
+
verifyVersion(key, version) {
|
|
1223
|
+
const keyParam = getKeyParam(this.encodeKey(key));
|
|
1224
|
+
return this.db.verifyVersion(keyParam, version);
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Seeds the verification-table slot for `key` with `version`. Has no
|
|
1228
|
+
* effect if the slot is currently lock-tagged or the table is disabled.
|
|
1229
|
+
* Useful after a full read where the caller already knows the version.
|
|
1230
|
+
*/
|
|
1231
|
+
populateVersion(key, version) {
|
|
1232
|
+
const keyParam = getKeyParam(this.encodeKey(key));
|
|
1233
|
+
this.db.populateVersion(keyParam, version);
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1096
1236
|
* Acquires a lock on the given key and calls the callback.
|
|
1097
1237
|
*
|
|
1098
1238
|
* @param key - The key to lock.
|
|
@@ -1120,6 +1260,13 @@ function getKeyParam(keyBuffer) {
|
|
|
1120
1260
|
|
|
1121
1261
|
//#endregion
|
|
1122
1262
|
//#region src/transaction.ts
|
|
1263
|
+
/**
|
|
1264
|
+
* Sentinel value returned by `commit()` when `coordinatedRetry: true` and the
|
|
1265
|
+
* transaction encountered an IsBusy conflict. The native layer parks on VT
|
|
1266
|
+
* slots and resolves only after the conflicting transaction releases its write
|
|
1267
|
+
* intent, so callers should retry immediately without any backoff delay.
|
|
1268
|
+
*/
|
|
1269
|
+
const RETRY_NOW = constants.RETRY_NOW_VALUE;
|
|
1123
1270
|
var TransactionAlreadyAbortedError = class extends Error {
|
|
1124
1271
|
code = "ERR_ALREADY_ABORTED";
|
|
1125
1272
|
};
|
|
@@ -1180,13 +1327,16 @@ var Transaction = class extends DBI {
|
|
|
1180
1327
|
}
|
|
1181
1328
|
/**
|
|
1182
1329
|
* Commit the transaction.
|
|
1330
|
+
*
|
|
1331
|
+
* Returns `RETRY_NOW` when `coordinatedRetry: true` and an IsBusy conflict
|
|
1332
|
+
* was detected. The caller should retry the transaction body immediately.
|
|
1183
1333
|
*/
|
|
1184
1334
|
async commit() {
|
|
1185
1335
|
try {
|
|
1186
|
-
await new Promise((resolve, reject) => {
|
|
1336
|
+
if (await new Promise((resolve, reject) => {
|
|
1187
1337
|
this.notify("beforecommit");
|
|
1188
1338
|
this.#txn.commit(resolve, reject);
|
|
1189
|
-
});
|
|
1339
|
+
}) === RETRY_NOW) return RETRY_NOW;
|
|
1190
1340
|
} catch (err) {
|
|
1191
1341
|
throw this.#handleCommitError(err);
|
|
1192
1342
|
} finally {
|
|
@@ -1332,6 +1482,26 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1332
1482
|
return this.store.compact(options);
|
|
1333
1483
|
}
|
|
1334
1484
|
/**
|
|
1485
|
+
* Creates a backup of the entire database (all column families) into the
|
|
1486
|
+
* given directory, creating parent directories as needed, and resolves with
|
|
1487
|
+
* the new backup id. Use the `backups` namespace to restore, list, delete,
|
|
1488
|
+
* purge, or verify backups.
|
|
1489
|
+
*
|
|
1490
|
+
* When the database was opened with `disableWAL`, the memtable is flushed
|
|
1491
|
+
* before the backup by default so unflushed data is not lost.
|
|
1492
|
+
*
|
|
1493
|
+
* @example
|
|
1494
|
+
* ```typescript
|
|
1495
|
+
* import { backups } from '@harperfast/rocksdb-js';
|
|
1496
|
+
*
|
|
1497
|
+
* const db = RocksDatabase.open('/path/to/database');
|
|
1498
|
+
* const id = await db.backup('/path/to/backups');
|
|
1499
|
+
* ```
|
|
1500
|
+
*/
|
|
1501
|
+
backup(backupDir, options) {
|
|
1502
|
+
return this.store.backup(backupDir, options);
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1335
1505
|
* Compacts the entire key range of the database synchronously.
|
|
1336
1506
|
* This triggers manual compaction which removes tombstones and reclaims space.
|
|
1337
1507
|
*
|
|
@@ -1519,17 +1689,8 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1519
1689
|
getStat(statName) {
|
|
1520
1690
|
return this.store.db.getStat(statName);
|
|
1521
1691
|
}
|
|
1522
|
-
/**
|
|
1523
|
-
* Gets the RocksDB statistics. Requires statistics to be enabled.
|
|
1524
|
-
*
|
|
1525
|
-
* @example
|
|
1526
|
-
* ```typescript
|
|
1527
|
-
* const db = RocksDatabase.open('/path/to/database');
|
|
1528
|
-
* const stats = db.getStats();
|
|
1529
|
-
* ```
|
|
1530
|
-
*/
|
|
1531
1692
|
getStats(all = false) {
|
|
1532
|
-
return this.store.db.getStats(
|
|
1693
|
+
return all ? this.store.db.getStats(true) : this.store.db.getStats(false);
|
|
1533
1694
|
}
|
|
1534
1695
|
/**
|
|
1535
1696
|
* Gets or creates a buffer that can be shared across worker threads.
|
|
@@ -1572,6 +1733,30 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1572
1733
|
}
|
|
1573
1734
|
async ifNoExists(_key) {}
|
|
1574
1735
|
/**
|
|
1736
|
+
* Checks the process-global verification table for a fresh version match
|
|
1737
|
+
* on `key`. Returns `true` when the table currently records `version`. Use
|
|
1738
|
+
* this as a fast cache-freshness check before falling back to a full read:
|
|
1739
|
+
*
|
|
1740
|
+
* ```typescript
|
|
1741
|
+
* if (db.verifyVersion(key, cachedEntry.version)) {
|
|
1742
|
+
* return cachedEntry.value;
|
|
1743
|
+
* }
|
|
1744
|
+
* const value = db.getSync(key);
|
|
1745
|
+
* db.populateVersion(key, extractVersion(value));
|
|
1746
|
+
* ```
|
|
1747
|
+
*/
|
|
1748
|
+
verifyVersion(key, version) {
|
|
1749
|
+
return this.store.verifyVersion(key, version);
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Seeds the verification-table slot for `key` with `version`. Has no
|
|
1753
|
+
* effect if the slot is currently lock-tagged or if the verification
|
|
1754
|
+
* table is disabled.
|
|
1755
|
+
*/
|
|
1756
|
+
populateVersion(key, version) {
|
|
1757
|
+
this.store.populateVersion(key, version);
|
|
1758
|
+
}
|
|
1759
|
+
/**
|
|
1575
1760
|
* Returns whether the database is open.
|
|
1576
1761
|
*
|
|
1577
1762
|
* @returns `true` if the database is open, `false` otherwise.
|
|
@@ -1711,9 +1896,6 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1711
1896
|
get path() {
|
|
1712
1897
|
return this.store.path;
|
|
1713
1898
|
}
|
|
1714
|
-
/**
|
|
1715
|
-
* Purges transaction logs.
|
|
1716
|
-
*/
|
|
1717
1899
|
purgeLogs(options) {
|
|
1718
1900
|
return this.store.db.purgeLogs(options);
|
|
1719
1901
|
}
|
|
@@ -1749,13 +1931,19 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1749
1931
|
} catch (callbackErr) {
|
|
1750
1932
|
return this.#abortTransaction(txn, callbackErr);
|
|
1751
1933
|
}
|
|
1934
|
+
let commitResult;
|
|
1752
1935
|
try {
|
|
1753
|
-
await txn.commit();
|
|
1754
|
-
return result;
|
|
1936
|
+
commitResult = await txn.commit();
|
|
1755
1937
|
} catch (commitErr) {
|
|
1756
1938
|
if (commitErr instanceof TransactionAlreadyAbortedError) return;
|
|
1757
|
-
if (commitErr instanceof TransactionIsBusyError && (options?.retryOnBusy ?? commitErr.hasLog) && attempt
|
|
1939
|
+
if (commitErr instanceof TransactionIsBusyError && (options?.retryOnBusy ?? commitErr.hasLog) && attempt < maxRetries) continue;
|
|
1758
1940
|
this.#abandonTransaction(txn, commitErr);
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
if (commitResult !== RETRY_NOW) return result;
|
|
1944
|
+
if (attempt >= maxRetries) {
|
|
1945
|
+
this.#abandonTransaction(txn, /* @__PURE__ */ new Error(`Transaction did not commit after ${maxRetries} coordinated retries`));
|
|
1946
|
+
return;
|
|
1759
1947
|
}
|
|
1760
1948
|
}
|
|
1761
1949
|
}
|
|
@@ -2068,7 +2256,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
2068
2256
|
done: true,
|
|
2069
2257
|
value: void 0
|
|
2070
2258
|
};
|
|
2071
|
-
const limit = readUncommitted ? logBuffer.length : size;
|
|
2259
|
+
const limit = readUncommitted ? logBuffer.length : Math.min(size, logBuffer.length);
|
|
2072
2260
|
if (position + TRANSACTION_LOG_ENTRY_HEADER_SIZE > limit) throw new RangeError(`Corrupt transaction log: truncated entry header at position ${position.toString(16)} of log ${logBuffer.logId} (available=${limit - position})`);
|
|
2073
2261
|
const length = dataView.getUint32(position + 8);
|
|
2074
2262
|
if (position + TRANSACTION_LOG_ENTRY_HEADER_SIZE + length > limit) throw new RangeError(`Corrupt transaction log entry at position ${position.toString(16)} of log ${logBuffer.logId}: declared length ${length} overruns the log (limit=${limit})`);
|
|
@@ -2162,9 +2350,9 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
2162
2350
|
//#region src/index.ts
|
|
2163
2351
|
const versions = {
|
|
2164
2352
|
rocksdb: version,
|
|
2165
|
-
"rocksdb-js": "2.
|
|
2353
|
+
"rocksdb-js": "2.2.0"
|
|
2166
2354
|
};
|
|
2167
2355
|
|
|
2168
2356
|
//#endregion
|
|
2169
|
-
export { DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, constants, currentThreadId, parseTransactionLog, registryStatus, shutdown, stats, versions };
|
|
2357
|
+
export { DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, parseTransactionLog, registryStatus, shutdown, stats, versions };
|
|
2170
2358
|
//# sourceMappingURL=index.mjs.map
|