@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.cjs
CHANGED
|
@@ -31,6 +31,7 @@ let node_fs = require("node:fs");
|
|
|
31
31
|
let node_module = require("node:module");
|
|
32
32
|
let node_path = require("node:path");
|
|
33
33
|
let node_url = require("node:url");
|
|
34
|
+
let node_fs_promises = require("node:fs/promises");
|
|
34
35
|
let ordered_binary = require("ordered-binary");
|
|
35
36
|
ordered_binary = __toESM(ordered_binary, 1);
|
|
36
37
|
let _harperfast_extended_iterable = require("@harperfast/extended-iterable");
|
|
@@ -80,6 +81,7 @@ function locateBinding() {
|
|
|
80
81
|
}
|
|
81
82
|
const binding = req(locateBinding());
|
|
82
83
|
const config = binding.config;
|
|
84
|
+
const FRESH_VERSION_FLAG$1 = binding.constants.FRESH_VERSION_FLAG;
|
|
83
85
|
const addGlobalListener = binding.addListener;
|
|
84
86
|
const removeGlobalListener = binding.removeListener;
|
|
85
87
|
const globalListenerCount = binding.listenerCount;
|
|
@@ -92,9 +94,95 @@ const TransactionLog = binding.TransactionLog;
|
|
|
92
94
|
const registryStatus = binding.registryStatus;
|
|
93
95
|
const shutdown = binding.shutdown;
|
|
94
96
|
const currentThreadId = binding.currentThreadId;
|
|
97
|
+
/**
|
|
98
|
+
* Advises the kernel that the file-backed pages of every mapped transaction log
|
|
99
|
+
* are cold (Linux MADV_COLD), so they are reclaimed first under memory pressure
|
|
100
|
+
* without being freed — useful during replication catch-up, where a full read of
|
|
101
|
+
* the logs would otherwise inflate the container's reclaimable cache toward its
|
|
102
|
+
* cgroup limit. No-op on kernels < 5.4, macOS, and Windows.
|
|
103
|
+
*
|
|
104
|
+
* The transaction log registry is a process-global singleton shared across all
|
|
105
|
+
* worker threads, so a single call cools every worker's maps. Call it on an
|
|
106
|
+
* interval from one thread (e.g. an `unref()`ed timer on the main thread).
|
|
107
|
+
*
|
|
108
|
+
* @returns the number of maps cooled and total file-backed bytes advised.
|
|
109
|
+
*/
|
|
110
|
+
const coolTransactionLogs = binding.coolTransactionLogs;
|
|
111
|
+
/**
|
|
112
|
+
* Number of live transaction-log memory maps across the process. Internal —
|
|
113
|
+
* used by tests to verify that releasing a frozen log's external buffer unmaps
|
|
114
|
+
* the underlying mapping rather than leaving it retained.
|
|
115
|
+
*/
|
|
116
|
+
const transactionLogMapCount = binding.transactionLogMapCount;
|
|
117
|
+
const nativeBackupRestore = binding.backupRestore;
|
|
118
|
+
const nativeBackupList = binding.backupList;
|
|
119
|
+
const nativeBackupDelete = binding.backupDelete;
|
|
120
|
+
const nativeBackupPurge = binding.backupPurge;
|
|
121
|
+
const nativeBackupVerify = binding.backupVerify;
|
|
95
122
|
const stats = binding.stats;
|
|
96
123
|
const version = binding.version;
|
|
97
124
|
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/backup.ts
|
|
127
|
+
/**
|
|
128
|
+
* Backup management operations that act on a backup directory and do not
|
|
129
|
+
* require an open database. To create a backup, use the `db.backup()` instance
|
|
130
|
+
* method instead (it needs a live database for a consistent snapshot).
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* import { backups } from '@harperfast/rocksdb-js';
|
|
135
|
+
*
|
|
136
|
+
* const id = await db.backup('/path/to/backups');
|
|
137
|
+
* db.close();
|
|
138
|
+
* await backups.restore('/path/to/backups', '/path/to/restored-db');
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
const backups = {
|
|
142
|
+
/**
|
|
143
|
+
* Restores a database from a backup directory into a (closed) database
|
|
144
|
+
* directory. The default mode purges the destination directory, so it must
|
|
145
|
+
* not point at a live database.
|
|
146
|
+
*/
|
|
147
|
+
async restore(backupDir, dbDir, options) {
|
|
148
|
+
if ((0, node_path.resolve)(backupDir) === (0, node_path.resolve)(dbDir)) throw new Error("Backup directory and database directory must be different");
|
|
149
|
+
const walDir = options?.walDir ?? dbDir;
|
|
150
|
+
await (0, node_fs_promises.mkdir)(dbDir, { recursive: true });
|
|
151
|
+
if (walDir !== dbDir) await (0, node_fs_promises.mkdir)(walDir, { recursive: true });
|
|
152
|
+
return new Promise((resolve, reject) => nativeBackupRestore(resolve, reject, backupDir, dbDir, walDir, {
|
|
153
|
+
backupId: options?.backupId,
|
|
154
|
+
keepLogFiles: options?.keepLogFiles,
|
|
155
|
+
mode: options?.mode
|
|
156
|
+
}));
|
|
157
|
+
},
|
|
158
|
+
/**
|
|
159
|
+
* Lists the non-corrupt backups in a backup directory, ordered by id.
|
|
160
|
+
*/
|
|
161
|
+
list(backupDir) {
|
|
162
|
+
return new Promise((resolve, reject) => nativeBackupList(resolve, reject, backupDir));
|
|
163
|
+
},
|
|
164
|
+
/**
|
|
165
|
+
* Deletes a specific backup. Shared files are reference-counted and only
|
|
166
|
+
* removed once no remaining backup references them.
|
|
167
|
+
*/
|
|
168
|
+
delete(backupDir, backupId) {
|
|
169
|
+
return new Promise((resolve, reject) => nativeBackupDelete(resolve, reject, backupDir, backupId));
|
|
170
|
+
},
|
|
171
|
+
/**
|
|
172
|
+
* Deletes all but the newest `keepCount` backups.
|
|
173
|
+
*/
|
|
174
|
+
purge(backupDir, keepCount) {
|
|
175
|
+
return new Promise((resolve, reject) => nativeBackupPurge(resolve, reject, backupDir, keepCount));
|
|
176
|
+
},
|
|
177
|
+
/**
|
|
178
|
+
* Verifies a backup's file sizes, and optionally their checksums (which
|
|
179
|
+
* requires reading all backed-up data).
|
|
180
|
+
*/
|
|
181
|
+
verify(backupDir, backupId, options) {
|
|
182
|
+
return new Promise((resolve, reject) => nativeBackupVerify(resolve, reject, backupDir, backupId, options?.verifyWithChecksum ?? false));
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
98
186
|
//#endregion
|
|
99
187
|
//#region src/util.ts
|
|
100
188
|
/**
|
|
@@ -213,11 +301,11 @@ var DBI = class DBI {
|
|
|
213
301
|
*/
|
|
214
302
|
get(key, options) {
|
|
215
303
|
if (this.store.decoderCopies) return when(() => this.getBinaryFast(key, options), (result) => {
|
|
216
|
-
if (result === void 0) return;
|
|
304
|
+
if (result === void 0 || result === FRESH_VERSION_FLAG$1) return result;
|
|
217
305
|
if (options?.skipDecode) return result;
|
|
218
306
|
return this.store.decodeValue(result);
|
|
219
307
|
});
|
|
220
|
-
return when(() => this.getBinary(key, options), (result) => result === void 0 ?
|
|
308
|
+
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));
|
|
221
309
|
}
|
|
222
310
|
/**
|
|
223
311
|
* Retrieves the binary data for the given key. This is just like `get()`,
|
|
@@ -227,7 +315,7 @@ var DBI = class DBI {
|
|
|
227
315
|
*/
|
|
228
316
|
getBinary(key, options) {
|
|
229
317
|
if (!this.store.isOpen()) return Promise.reject(/* @__PURE__ */ new Error("Database not open"));
|
|
230
|
-
return this.store.get(this._context, key, true,
|
|
318
|
+
return this.store.get(this._context, key, true, options);
|
|
231
319
|
}
|
|
232
320
|
/**
|
|
233
321
|
* Synchronously retrieves the binary data for the given key.
|
|
@@ -248,7 +336,7 @@ var DBI = class DBI {
|
|
|
248
336
|
*/
|
|
249
337
|
getBinaryFast(key, options) {
|
|
250
338
|
if (!this.store.isOpen()) return Promise.reject(/* @__PURE__ */ new Error("Database not open"));
|
|
251
|
-
return this.store.get(this._context, key, false,
|
|
339
|
+
return this.store.get(this._context, key, false, options);
|
|
252
340
|
}
|
|
253
341
|
/**
|
|
254
342
|
* Synchronously retrieves the binary data for the given key using a
|
|
@@ -307,12 +395,12 @@ var DBI = class DBI {
|
|
|
307
395
|
getSync(key, options) {
|
|
308
396
|
if (this.store.decoderCopies) {
|
|
309
397
|
const bytes = this.getBinaryFastSync(key, options);
|
|
310
|
-
return bytes === void 0 ?
|
|
398
|
+
return bytes === void 0 || bytes === FRESH_VERSION_FLAG$1 ? bytes : this.store.decodeValue(bytes);
|
|
311
399
|
}
|
|
312
400
|
if (this.store.encoding === "binary") return this.getBinarySync(key, options);
|
|
313
401
|
if (this.store.decoder) {
|
|
314
402
|
const result = this.getBinarySync(key, options);
|
|
315
|
-
return result ? this.store.decodeValue(result)
|
|
403
|
+
return !result || result === FRESH_VERSION_FLAG$1 ? result : this.store.decodeValue(result);
|
|
316
404
|
}
|
|
317
405
|
if (!this.store.isOpen()) throw new Error("Database not open");
|
|
318
406
|
return this.store.decodeValue(this.store.getSync(this._context, key, true, options));
|
|
@@ -475,17 +563,25 @@ var DBIterator = class {
|
|
|
475
563
|
iterator;
|
|
476
564
|
store;
|
|
477
565
|
#includeValues;
|
|
478
|
-
|
|
566
|
+
#limit;
|
|
567
|
+
#yielded = 0;
|
|
568
|
+
constructor(iterator, store, includeValues, limit) {
|
|
479
569
|
this.iterator = iterator;
|
|
480
570
|
this.store = store;
|
|
481
571
|
this.#includeValues = includeValues;
|
|
572
|
+
this.#limit = limit ?? void 0;
|
|
482
573
|
}
|
|
483
574
|
[Symbol.iterator]() {
|
|
484
575
|
return this;
|
|
485
576
|
}
|
|
486
577
|
next() {
|
|
578
|
+
if (this.#limit !== void 0 && this.#yielded >= this.#limit) {
|
|
579
|
+
this.iterator.return?.();
|
|
580
|
+
return DONE_RESULT;
|
|
581
|
+
}
|
|
487
582
|
const result = this.iterator.next();
|
|
488
583
|
if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
|
|
584
|
+
this.#yielded++;
|
|
489
585
|
const includeValues = this.#includeValues;
|
|
490
586
|
const value = {};
|
|
491
587
|
if (result === ITERATOR_RESULT_FAST) {
|
|
@@ -587,7 +683,7 @@ function createFixedBuffer(size) {
|
|
|
587
683
|
|
|
588
684
|
//#endregion
|
|
589
685
|
//#region src/store.ts
|
|
590
|
-
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;
|
|
686
|
+
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;
|
|
591
687
|
const KEY_BUFFER_SIZE = 4096;
|
|
592
688
|
const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
|
|
593
689
|
const VALUE_BUFFER = createFixedBuffer(64 * 1024);
|
|
@@ -746,6 +842,10 @@ var Store = class {
|
|
|
746
842
|
*/
|
|
747
843
|
transactionLogsPath;
|
|
748
844
|
/**
|
|
845
|
+
* Whether this store's column family participates in the VerificationTable.
|
|
846
|
+
*/
|
|
847
|
+
verificationTable;
|
|
848
|
+
/**
|
|
749
849
|
* The per-column-family memtable size in bytes at which the memtable is
|
|
750
850
|
* sealed and flushed.
|
|
751
851
|
*/
|
|
@@ -792,6 +892,7 @@ var Store = class {
|
|
|
792
892
|
this.transactionLogMaxSize = options?.transactionLogMaxSize;
|
|
793
893
|
this.transactionLogRetention = options?.transactionLogRetention;
|
|
794
894
|
this.transactionLogsPath = options?.transactionLogsPath;
|
|
895
|
+
this.verificationTable = options?.verificationTable;
|
|
795
896
|
this.writeBufferSize = options?.writeBufferSize;
|
|
796
897
|
this.writeKey = writeKey;
|
|
797
898
|
}
|
|
@@ -825,6 +926,20 @@ var Store = class {
|
|
|
825
926
|
return new Promise((resolve, reject) => this.db.compact(resolve, reject, startBuffer, endBuffer));
|
|
826
927
|
}
|
|
827
928
|
/**
|
|
929
|
+
* Creates a backup of the entire database (all column families) into the
|
|
930
|
+
* given directory and resolves with the new backup id. Parent directories
|
|
931
|
+
* are created as needed. See `backups` for restore and management.
|
|
932
|
+
*
|
|
933
|
+
* @example
|
|
934
|
+
* ```typescript
|
|
935
|
+
* const id = await db.backup('/path/to/backups');
|
|
936
|
+
* ```
|
|
937
|
+
*/
|
|
938
|
+
async backup(backupDir, options) {
|
|
939
|
+
await (0, node_fs_promises.mkdir)(backupDir, { recursive: true });
|
|
940
|
+
return new Promise((resolve, reject) => this.db.backup(resolve, reject, backupDir, options));
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
828
943
|
* Compacts the entire key range of the database synchronously.
|
|
829
944
|
* This triggers manual compaction which removes tombstones and reclaims space.
|
|
830
945
|
*
|
|
@@ -897,16 +1012,19 @@ var Store = class {
|
|
|
897
1012
|
if (value instanceof Uint8Array) return value;
|
|
898
1013
|
throw new Error(`Invalid value put in database (${typeof value}), consider using an encoder`);
|
|
899
1014
|
}
|
|
900
|
-
get(context, key, alwaysCreateNewBuffer = false,
|
|
1015
|
+
get(context, key, alwaysCreateNewBuffer = false, options) {
|
|
901
1016
|
const keyParam = getKeyParam(this.encodeKey(key));
|
|
902
1017
|
let flags = 0;
|
|
903
1018
|
if (alwaysCreateNewBuffer) flags |= ALWAYS_CREATE_NEW_BUFFER_FLAG;
|
|
904
|
-
if (
|
|
905
|
-
const
|
|
1019
|
+
if (options?.populateVersion) flags |= POPULATE_VERSION_FLAG;
|
|
1020
|
+
const txnId = this.getTxnId(options);
|
|
1021
|
+
const expectedVersion = options?.expectedVersion;
|
|
1022
|
+
const result = context.getSync(keyParam, flags | ONLY_IF_IN_MEMORY_CACHE_FLAG, txnId, expectedVersion);
|
|
906
1023
|
if (typeof result === "number") {
|
|
907
1024
|
if (result === NOT_IN_MEMORY_CACHE_FLAG) return new Promise((resolve, reject) => {
|
|
908
|
-
context.get(keyParam, resolve, reject, txnId);
|
|
1025
|
+
context.get(keyParam, resolve, reject, txnId, expectedVersion);
|
|
909
1026
|
});
|
|
1027
|
+
if (result === FRESH_VERSION_FLAG) return result;
|
|
910
1028
|
VALUE_BUFFER.end = result;
|
|
911
1029
|
return VALUE_BUFFER;
|
|
912
1030
|
}
|
|
@@ -973,14 +1091,16 @@ var Store = class {
|
|
|
973
1091
|
if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
|
|
974
1092
|
if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
|
|
975
1093
|
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;
|
|
976
|
-
return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues));
|
|
1094
|
+
return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues, options?.limit));
|
|
977
1095
|
}
|
|
978
1096
|
getSync(context, key, alwaysCreateNewBuffer = false, options) {
|
|
979
1097
|
const keyParam = getKeyParam(this.encodeKey(key));
|
|
980
1098
|
let flags = 0;
|
|
981
1099
|
if (alwaysCreateNewBuffer) flags |= ALWAYS_CREATE_NEW_BUFFER_FLAG;
|
|
982
|
-
|
|
1100
|
+
if (options?.populateVersion) flags |= POPULATE_VERSION_FLAG;
|
|
1101
|
+
const result = context.getSync(keyParam, flags, this.getTxnId(options), options?.expectedVersion);
|
|
983
1102
|
if (typeof result === "number") {
|
|
1103
|
+
if (result === FRESH_VERSION_FLAG) return result;
|
|
984
1104
|
VALUE_BUFFER.end = result;
|
|
985
1105
|
return VALUE_BUFFER;
|
|
986
1106
|
}
|
|
@@ -1069,6 +1189,7 @@ var Store = class {
|
|
|
1069
1189
|
transactionLogMaxSize: this.transactionLogMaxSize,
|
|
1070
1190
|
transactionLogRetentionMs: this.transactionLogRetention ? parseDuration(this.transactionLogRetention) : void 0,
|
|
1071
1191
|
transactionLogsPath: this.transactionLogsPath,
|
|
1192
|
+
verificationTable: this.verificationTable,
|
|
1072
1193
|
writeBufferSize: this.writeBufferSize
|
|
1073
1194
|
});
|
|
1074
1195
|
return false;
|
|
@@ -1118,6 +1239,25 @@ var Store = class {
|
|
|
1118
1239
|
return context.useLog(String(name));
|
|
1119
1240
|
}
|
|
1120
1241
|
/**
|
|
1242
|
+
* Checks the process-global verification table for a fresh version match
|
|
1243
|
+
* on `key`. Returns `true` when the table currently records `version` for
|
|
1244
|
+
* this database+column-family. Provides a fast cache-freshness check
|
|
1245
|
+
* before falling back to a full read.
|
|
1246
|
+
*/
|
|
1247
|
+
verifyVersion(key, version) {
|
|
1248
|
+
const keyParam = getKeyParam(this.encodeKey(key));
|
|
1249
|
+
return this.db.verifyVersion(keyParam, version);
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Seeds the verification-table slot for `key` with `version`. Has no
|
|
1253
|
+
* effect if the slot is currently lock-tagged or the table is disabled.
|
|
1254
|
+
* Useful after a full read where the caller already knows the version.
|
|
1255
|
+
*/
|
|
1256
|
+
populateVersion(key, version) {
|
|
1257
|
+
const keyParam = getKeyParam(this.encodeKey(key));
|
|
1258
|
+
this.db.populateVersion(keyParam, version);
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1121
1261
|
* Acquires a lock on the given key and calls the callback.
|
|
1122
1262
|
*
|
|
1123
1263
|
* @param key - The key to lock.
|
|
@@ -1145,6 +1285,13 @@ function getKeyParam(keyBuffer) {
|
|
|
1145
1285
|
|
|
1146
1286
|
//#endregion
|
|
1147
1287
|
//#region src/transaction.ts
|
|
1288
|
+
/**
|
|
1289
|
+
* Sentinel value returned by `commit()` when `coordinatedRetry: true` and the
|
|
1290
|
+
* transaction encountered an IsBusy conflict. The native layer parks on VT
|
|
1291
|
+
* slots and resolves only after the conflicting transaction releases its write
|
|
1292
|
+
* intent, so callers should retry immediately without any backoff delay.
|
|
1293
|
+
*/
|
|
1294
|
+
const RETRY_NOW = constants.RETRY_NOW_VALUE;
|
|
1148
1295
|
var TransactionAlreadyAbortedError = class extends Error {
|
|
1149
1296
|
code = "ERR_ALREADY_ABORTED";
|
|
1150
1297
|
};
|
|
@@ -1205,13 +1352,16 @@ var Transaction = class extends DBI {
|
|
|
1205
1352
|
}
|
|
1206
1353
|
/**
|
|
1207
1354
|
* Commit the transaction.
|
|
1355
|
+
*
|
|
1356
|
+
* Returns `RETRY_NOW` when `coordinatedRetry: true` and an IsBusy conflict
|
|
1357
|
+
* was detected. The caller should retry the transaction body immediately.
|
|
1208
1358
|
*/
|
|
1209
1359
|
async commit() {
|
|
1210
1360
|
try {
|
|
1211
|
-
await new Promise((resolve, reject) => {
|
|
1361
|
+
if (await new Promise((resolve, reject) => {
|
|
1212
1362
|
this.notify("beforecommit");
|
|
1213
1363
|
this.#txn.commit(resolve, reject);
|
|
1214
|
-
});
|
|
1364
|
+
}) === RETRY_NOW) return RETRY_NOW;
|
|
1215
1365
|
} catch (err) {
|
|
1216
1366
|
throw this.#handleCommitError(err);
|
|
1217
1367
|
} finally {
|
|
@@ -1357,6 +1507,26 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1357
1507
|
return this.store.compact(options);
|
|
1358
1508
|
}
|
|
1359
1509
|
/**
|
|
1510
|
+
* Creates a backup of the entire database (all column families) into the
|
|
1511
|
+
* given directory, creating parent directories as needed, and resolves with
|
|
1512
|
+
* the new backup id. Use the `backups` namespace to restore, list, delete,
|
|
1513
|
+
* purge, or verify backups.
|
|
1514
|
+
*
|
|
1515
|
+
* When the database was opened with `disableWAL`, the memtable is flushed
|
|
1516
|
+
* before the backup by default so unflushed data is not lost.
|
|
1517
|
+
*
|
|
1518
|
+
* @example
|
|
1519
|
+
* ```typescript
|
|
1520
|
+
* import { backups } from '@harperfast/rocksdb-js';
|
|
1521
|
+
*
|
|
1522
|
+
* const db = RocksDatabase.open('/path/to/database');
|
|
1523
|
+
* const id = await db.backup('/path/to/backups');
|
|
1524
|
+
* ```
|
|
1525
|
+
*/
|
|
1526
|
+
backup(backupDir, options) {
|
|
1527
|
+
return this.store.backup(backupDir, options);
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1360
1530
|
* Compacts the entire key range of the database synchronously.
|
|
1361
1531
|
* This triggers manual compaction which removes tombstones and reclaims space.
|
|
1362
1532
|
*
|
|
@@ -1544,17 +1714,8 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1544
1714
|
getStat(statName) {
|
|
1545
1715
|
return this.store.db.getStat(statName);
|
|
1546
1716
|
}
|
|
1547
|
-
/**
|
|
1548
|
-
* Gets the RocksDB statistics. Requires statistics to be enabled.
|
|
1549
|
-
*
|
|
1550
|
-
* @example
|
|
1551
|
-
* ```typescript
|
|
1552
|
-
* const db = RocksDatabase.open('/path/to/database');
|
|
1553
|
-
* const stats = db.getStats();
|
|
1554
|
-
* ```
|
|
1555
|
-
*/
|
|
1556
1717
|
getStats(all = false) {
|
|
1557
|
-
return this.store.db.getStats(
|
|
1718
|
+
return all ? this.store.db.getStats(true) : this.store.db.getStats(false);
|
|
1558
1719
|
}
|
|
1559
1720
|
/**
|
|
1560
1721
|
* Gets or creates a buffer that can be shared across worker threads.
|
|
@@ -1597,6 +1758,30 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1597
1758
|
}
|
|
1598
1759
|
async ifNoExists(_key) {}
|
|
1599
1760
|
/**
|
|
1761
|
+
* Checks the process-global verification table for a fresh version match
|
|
1762
|
+
* on `key`. Returns `true` when the table currently records `version`. Use
|
|
1763
|
+
* this as a fast cache-freshness check before falling back to a full read:
|
|
1764
|
+
*
|
|
1765
|
+
* ```typescript
|
|
1766
|
+
* if (db.verifyVersion(key, cachedEntry.version)) {
|
|
1767
|
+
* return cachedEntry.value;
|
|
1768
|
+
* }
|
|
1769
|
+
* const value = db.getSync(key);
|
|
1770
|
+
* db.populateVersion(key, extractVersion(value));
|
|
1771
|
+
* ```
|
|
1772
|
+
*/
|
|
1773
|
+
verifyVersion(key, version) {
|
|
1774
|
+
return this.store.verifyVersion(key, version);
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Seeds the verification-table slot for `key` with `version`. Has no
|
|
1778
|
+
* effect if the slot is currently lock-tagged or if the verification
|
|
1779
|
+
* table is disabled.
|
|
1780
|
+
*/
|
|
1781
|
+
populateVersion(key, version) {
|
|
1782
|
+
this.store.populateVersion(key, version);
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1600
1785
|
* Returns whether the database is open.
|
|
1601
1786
|
*
|
|
1602
1787
|
* @returns `true` if the database is open, `false` otherwise.
|
|
@@ -1736,9 +1921,6 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1736
1921
|
get path() {
|
|
1737
1922
|
return this.store.path;
|
|
1738
1923
|
}
|
|
1739
|
-
/**
|
|
1740
|
-
* Purges transaction logs.
|
|
1741
|
-
*/
|
|
1742
1924
|
purgeLogs(options) {
|
|
1743
1925
|
return this.store.db.purgeLogs(options);
|
|
1744
1926
|
}
|
|
@@ -1774,13 +1956,19 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1774
1956
|
} catch (callbackErr) {
|
|
1775
1957
|
return this.#abortTransaction(txn, callbackErr);
|
|
1776
1958
|
}
|
|
1959
|
+
let commitResult;
|
|
1777
1960
|
try {
|
|
1778
|
-
await txn.commit();
|
|
1779
|
-
return result;
|
|
1961
|
+
commitResult = await txn.commit();
|
|
1780
1962
|
} catch (commitErr) {
|
|
1781
1963
|
if (commitErr instanceof TransactionAlreadyAbortedError) return;
|
|
1782
|
-
if (commitErr instanceof TransactionIsBusyError && (options?.retryOnBusy ?? commitErr.hasLog) && attempt
|
|
1964
|
+
if (commitErr instanceof TransactionIsBusyError && (options?.retryOnBusy ?? commitErr.hasLog) && attempt < maxRetries) continue;
|
|
1783
1965
|
this.#abandonTransaction(txn, commitErr);
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
if (commitResult !== RETRY_NOW) return result;
|
|
1969
|
+
if (attempt >= maxRetries) {
|
|
1970
|
+
this.#abandonTransaction(txn, /* @__PURE__ */ new Error(`Transaction did not commit after ${maxRetries} coordinated retries`));
|
|
1971
|
+
return;
|
|
1784
1972
|
}
|
|
1785
1973
|
}
|
|
1786
1974
|
}
|
|
@@ -2093,7 +2281,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
2093
2281
|
done: true,
|
|
2094
2282
|
value: void 0
|
|
2095
2283
|
};
|
|
2096
|
-
const limit = readUncommitted ? logBuffer.length : size;
|
|
2284
|
+
const limit = readUncommitted ? logBuffer.length : Math.min(size, logBuffer.length);
|
|
2097
2285
|
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})`);
|
|
2098
2286
|
const length = dataView.getUint32(position + 8);
|
|
2099
2287
|
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})`);
|
|
@@ -2187,7 +2375,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
2187
2375
|
//#region src/index.ts
|
|
2188
2376
|
const versions = {
|
|
2189
2377
|
rocksdb: version,
|
|
2190
|
-
"rocksdb-js": "2.
|
|
2378
|
+
"rocksdb-js": "2.2.0"
|
|
2191
2379
|
};
|
|
2192
2380
|
|
|
2193
2381
|
//#endregion
|
|
@@ -2197,7 +2385,9 @@ exports.RocksDatabase = RocksDatabase;
|
|
|
2197
2385
|
exports.Store = Store;
|
|
2198
2386
|
exports.Transaction = Transaction;
|
|
2199
2387
|
exports.TransactionLog = TransactionLog;
|
|
2388
|
+
exports.backups = backups;
|
|
2200
2389
|
exports.constants = constants;
|
|
2390
|
+
exports.coolTransactionLogs = coolTransactionLogs;
|
|
2201
2391
|
exports.currentThreadId = currentThreadId;
|
|
2202
2392
|
exports.parseTransactionLog = parseTransactionLog;
|
|
2203
2393
|
exports.registryStatus = registryStatus;
|