@harperfast/rocksdb-js 2.12.0 → 2.13.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 +133 -11
- package/dist/index.cjs +311 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +168 -39
- package/dist/index.d.mts +168 -39
- package/dist/index.mjs +309 -66
- package/dist/index.mjs.map +1 -1
- package/docs/performance/mutex-contention.md +4 -3
- package/docs/stats.md +4 -0
- package/docs/steady-clock-design.md +1 -1
- package/docs/transaction-log.md +5 -4
- package/package.json +16 -16
package/dist/index.mjs
CHANGED
|
@@ -8,11 +8,10 @@ import * as orderedBinary from "ordered-binary";
|
|
|
8
8
|
import { ExtendedIterable } from "@harperfast/extended-iterable";
|
|
9
9
|
import { Encoder } from "msgpackr";
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// \0rolldown/runtime.js
|
|
12
12
|
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
//#region src/load-binding.ts
|
|
14
|
+
// src/load-binding.ts
|
|
16
15
|
const nativeExtRE = /\.node$/;
|
|
17
16
|
const req = createRequire(import.meta.url);
|
|
18
17
|
/**
|
|
@@ -147,7 +146,7 @@ const currentThreadId = binding.currentThreadId;
|
|
|
147
146
|
* Reads the process-wide steady clock: `std::chrono::steady_clock` as
|
|
148
147
|
* fractional milliseconds from an unspecified origin that is fixed for the life
|
|
149
148
|
* of the process. Every sample in the process — main thread and every
|
|
150
|
-
* `worker_threads` worker, whenever started or restarted, on Node
|
|
149
|
+
* `worker_threads` worker, whenever started or restarted, on Node and Bun
|
|
151
150
|
* — is in one domain, and the difference between two samples is real elapsed
|
|
152
151
|
* time unaffected by wall-clock steps. Non-decreasing but not unique (two
|
|
153
152
|
* samples may be equal; treat `==` as "not later"). Windows QPC samples from
|
|
@@ -185,12 +184,30 @@ const transactionLogMapCount = binding.transactionLogMapCount;
|
|
|
185
184
|
*/
|
|
186
185
|
const lockWakeCallbackCount = binding.lockWakeCallbackCount;
|
|
187
186
|
/**
|
|
187
|
+
* The calling thread's counters for the per-transaction iterator pool: pooled
|
|
188
|
+
* iterators created, and idle ones reused by a later range. Internal — used by
|
|
189
|
+
* tests to prove a range was served by a reused iterator.
|
|
190
|
+
*/
|
|
191
|
+
const iteratorPoolStats = binding.iteratorPoolStats;
|
|
192
|
+
/**
|
|
193
|
+
* Whether transaction `getRange()` reuses pooled native iterators (off with
|
|
194
|
+
* `ROCKSDB_JS_ITERATOR_POOL=0`). Internal.
|
|
195
|
+
*/
|
|
196
|
+
const iteratorPoolEnabled = binding.iteratorPoolEnabled;
|
|
197
|
+
/**
|
|
188
198
|
* Test-only: force the next `count` transaction commits to fail with TryAgain (rolled back, so
|
|
189
199
|
* no data is committed), reproducing a stranded-snapshot conflict deterministically. Pass 0 to
|
|
190
200
|
* disarm. Used by the ERR_TRY_AGAIN retry regression test.
|
|
191
201
|
*/
|
|
192
202
|
const forceTryAgainForTesting = binding.forceTryAgainForTesting;
|
|
193
203
|
/**
|
|
204
|
+
* Test-only: turn the RocksDB conflict check before the transaction-log write off (`true`) or back
|
|
205
|
+
* on, so a test can make a logged batch conflict at commit from a stale snapshot.
|
|
206
|
+
*/
|
|
207
|
+
const disablePrelogConflictCheckForTesting = binding.disablePrelogConflictCheckForTesting;
|
|
208
|
+
/** Test-only: run the pre-log conflict check on retries alone (`true`, the default) or on every attempt. */
|
|
209
|
+
const setPrelogConflictCheckRetriesOnlyForTesting = binding.setPrelogConflictCheckRetriesOnlyForTesting;
|
|
210
|
+
/**
|
|
194
211
|
* Test-only seam: makes the physical column-family drop report failure.
|
|
195
212
|
* `0` = inert, `1` = fail without dropping (the retry performs the drop),
|
|
196
213
|
* `2` = drop for real and then report failure (the retry resolves as
|
|
@@ -233,8 +250,7 @@ const nativeValidateTransactionLog = binding.validateTransactionLog;
|
|
|
233
250
|
const stats = binding.stats;
|
|
234
251
|
const version = binding.version;
|
|
235
252
|
|
|
236
|
-
|
|
237
|
-
//#region src/validate-transaction-log.ts
|
|
253
|
+
// src/validate-transaction-log.ts
|
|
238
254
|
/**
|
|
239
255
|
* Validates a transaction log store directory: every `<sequence>.txnlog` file
|
|
240
256
|
* is checked for a valid header (token, version) and intact v1 entry framing
|
|
@@ -274,8 +290,7 @@ function validateTransactionLogStore(path, options) {
|
|
|
274
290
|
return new Promise((resolve, reject) => nativeValidateTransactionLog(resolve, reject, path, options?.strict ?? false));
|
|
275
291
|
}
|
|
276
292
|
|
|
277
|
-
|
|
278
|
-
//#region src/backup.ts
|
|
293
|
+
// src/backup.ts
|
|
279
294
|
/** Subdirectory (under a backup directory) holding per-backup transaction log snapshots. */
|
|
280
295
|
const TRANSACTION_LOGS_DIRNAME = "transaction_logs";
|
|
281
296
|
/** Non-blocking existence check (`fs.existsSync` would block the event loop). */
|
|
@@ -559,8 +574,7 @@ const backups = {
|
|
|
559
574
|
}
|
|
560
575
|
};
|
|
561
576
|
|
|
562
|
-
|
|
563
|
-
//#region src/count-estimator.ts
|
|
577
|
+
// src/count-estimator.ts
|
|
564
578
|
/**
|
|
565
579
|
* Below this many traversed entries the observed density is too noisy to
|
|
566
580
|
* calibrate with, so `estimate()` returns the raw statistical estimate.
|
|
@@ -703,8 +717,7 @@ var CountEstimator = class {
|
|
|
703
717
|
}
|
|
704
718
|
};
|
|
705
719
|
|
|
706
|
-
|
|
707
|
-
//#region src/util.ts
|
|
720
|
+
// src/util.ts
|
|
708
721
|
/**
|
|
709
722
|
* Parses a duration string into milliseconds.
|
|
710
723
|
*
|
|
@@ -775,8 +788,7 @@ function when(subject, callback, errback) {
|
|
|
775
788
|
}
|
|
776
789
|
}
|
|
777
790
|
|
|
778
|
-
|
|
779
|
-
//#region src/dbi.ts
|
|
791
|
+
// src/dbi.ts
|
|
780
792
|
/**
|
|
781
793
|
* The base class for all database operations. This base class is shared by
|
|
782
794
|
* `RocksDatabase` and `Transaction`.
|
|
@@ -1061,8 +1073,7 @@ var DBI = class DBI {
|
|
|
1061
1073
|
}
|
|
1062
1074
|
};
|
|
1063
1075
|
|
|
1064
|
-
|
|
1065
|
-
//#region src/tar.ts
|
|
1076
|
+
// src/tar.ts
|
|
1066
1077
|
/**
|
|
1067
1078
|
* A minimal, dependency-free streaming USTAR (POSIX tar) encoder.
|
|
1068
1079
|
*
|
|
@@ -1268,8 +1279,7 @@ var TarEncoder = class {
|
|
|
1268
1279
|
}
|
|
1269
1280
|
};
|
|
1270
1281
|
|
|
1271
|
-
|
|
1272
|
-
//#region src/backup-stream.ts
|
|
1282
|
+
// src/backup-stream.ts
|
|
1273
1283
|
/** Native event discriminator: a new file header (vs. a payload chunk). */
|
|
1274
1284
|
const EVENT_FILE = 0;
|
|
1275
1285
|
/**
|
|
@@ -1313,9 +1323,22 @@ async function backupToStream(db, stream, options) {
|
|
|
1313
1323
|
}
|
|
1314
1324
|
}
|
|
1315
1325
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
const
|
|
1326
|
+
// src/dbi-iterator.ts
|
|
1327
|
+
const { ITERATOR_RESULT_DONE, ITERATOR_RESULT_FAST, ITERATOR_RESULT_RELEASED } = constants;
|
|
1328
|
+
const transactionsToTrim = /* @__PURE__ */ new Set();
|
|
1329
|
+
function trimIdleIterators() {
|
|
1330
|
+
try {
|
|
1331
|
+
for (const transaction of transactionsToTrim) transaction.trimIterators();
|
|
1332
|
+
} finally {
|
|
1333
|
+
transactionsToTrim.clear();
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
function scheduleTrim(transaction) {
|
|
1337
|
+
if (transaction !== void 0) {
|
|
1338
|
+
if (transactionsToTrim.size === 0) setImmediate(trimIdleIterators);
|
|
1339
|
+
transactionsToTrim.add(transaction);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1319
1342
|
const DONE_RESULT = Object.freeze({
|
|
1320
1343
|
done: true,
|
|
1321
1344
|
value: void 0
|
|
@@ -1335,10 +1358,12 @@ var DBIterator = class {
|
|
|
1335
1358
|
#includeValues;
|
|
1336
1359
|
#limit;
|
|
1337
1360
|
#yielded = 0;
|
|
1338
|
-
|
|
1361
|
+
#transaction;
|
|
1362
|
+
constructor(iterator, store, includeValues, limit, transaction) {
|
|
1339
1363
|
this.iterator = iterator;
|
|
1340
1364
|
this.store = store;
|
|
1341
1365
|
this.#includeValues = includeValues;
|
|
1366
|
+
this.#transaction = transaction;
|
|
1342
1367
|
this.#limit = limit ?? void 0;
|
|
1343
1368
|
}
|
|
1344
1369
|
[Symbol.iterator]() {
|
|
@@ -1346,7 +1371,7 @@ var DBIterator = class {
|
|
|
1346
1371
|
}
|
|
1347
1372
|
next() {
|
|
1348
1373
|
if (this.#limit !== void 0 && this.#yielded >= this.#limit) {
|
|
1349
|
-
this.iterator.return
|
|
1374
|
+
this.#release(this.iterator.return());
|
|
1350
1375
|
return DONE_RESULT;
|
|
1351
1376
|
}
|
|
1352
1377
|
let result;
|
|
@@ -1354,11 +1379,15 @@ var DBIterator = class {
|
|
|
1354
1379
|
result = this.iterator.next();
|
|
1355
1380
|
} catch (err) {
|
|
1356
1381
|
try {
|
|
1357
|
-
this.iterator.return
|
|
1382
|
+
this.#release(this.iterator.return());
|
|
1358
1383
|
} catch {}
|
|
1359
1384
|
throw err;
|
|
1360
1385
|
}
|
|
1361
1386
|
if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
|
|
1387
|
+
if (result === ITERATOR_RESULT_RELEASED) {
|
|
1388
|
+
scheduleTrim(this.#transaction);
|
|
1389
|
+
return DONE_RESULT;
|
|
1390
|
+
}
|
|
1362
1391
|
this.#yielded++;
|
|
1363
1392
|
const includeValues = this.#includeValues;
|
|
1364
1393
|
const value = {};
|
|
@@ -1379,20 +1408,23 @@ var DBIterator = class {
|
|
|
1379
1408
|
};
|
|
1380
1409
|
}
|
|
1381
1410
|
return(value) {
|
|
1382
|
-
this.iterator.return
|
|
1411
|
+
this.#release(this.iterator.return());
|
|
1383
1412
|
return {
|
|
1384
1413
|
done: true,
|
|
1385
1414
|
value
|
|
1386
1415
|
};
|
|
1387
1416
|
}
|
|
1388
1417
|
throw(err) {
|
|
1389
|
-
this.iterator.throw
|
|
1418
|
+
this.#release(this.iterator.throw(err));
|
|
1390
1419
|
throw err;
|
|
1391
1420
|
}
|
|
1421
|
+
#release(pooled) {
|
|
1422
|
+
if (pooled) scheduleTrim(this.#transaction);
|
|
1423
|
+
this.#transaction = void 0;
|
|
1424
|
+
}
|
|
1392
1425
|
};
|
|
1393
1426
|
|
|
1394
|
-
|
|
1395
|
-
//#region src/encoding.ts
|
|
1427
|
+
// src/encoding.ts
|
|
1396
1428
|
/**
|
|
1397
1429
|
* Initializes the key encoder functions.
|
|
1398
1430
|
*
|
|
@@ -1459,8 +1491,7 @@ function createFixedBuffer(size) {
|
|
|
1459
1491
|
return buffer;
|
|
1460
1492
|
}
|
|
1461
1493
|
|
|
1462
|
-
|
|
1463
|
-
//#region src/store.ts
|
|
1494
|
+
// src/store.ts
|
|
1464
1495
|
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, ITERATOR_HAS_TRANSACTION_ID_FLAG } = constants;
|
|
1465
1496
|
const KEY_BUFFER_SIZE = 4096;
|
|
1466
1497
|
const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
|
|
@@ -1761,6 +1792,10 @@ var Store = class {
|
|
|
1761
1792
|
*/
|
|
1762
1793
|
writeBufferSize;
|
|
1763
1794
|
/**
|
|
1795
|
+
* The memtable arena block size in bytes; `0` or unset is automatic.
|
|
1796
|
+
*/
|
|
1797
|
+
arenaBlockSize;
|
|
1798
|
+
/**
|
|
1764
1799
|
* The function used to encode keys using the shared `keyBuffer`.
|
|
1765
1800
|
*/
|
|
1766
1801
|
writeKey;
|
|
@@ -1813,6 +1848,7 @@ var Store = class {
|
|
|
1813
1848
|
this.timestampFloorLog = options?.timestampFloorLog;
|
|
1814
1849
|
this.verificationTable = options?.verificationTable;
|
|
1815
1850
|
this.writeBufferSize = options?.writeBufferSize;
|
|
1851
|
+
this.arenaBlockSize = options?.arenaBlockSize;
|
|
1816
1852
|
this.writeKey = writeKey;
|
|
1817
1853
|
this.#columnFamilyOptions = options ? { ...options } : void 0;
|
|
1818
1854
|
}
|
|
@@ -2063,7 +2099,9 @@ var Store = class {
|
|
|
2063
2099
|
if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
|
|
2064
2100
|
if (txnId !== void 0) flags |= ITERATOR_HAS_TRANSACTION_ID_FLAG;
|
|
2065
2101
|
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;
|
|
2066
|
-
|
|
2102
|
+
const nativeIterator = new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId);
|
|
2103
|
+
const transaction = context !== this.db ? context : options?.transaction?._context;
|
|
2104
|
+
return new ExtendedIterable(new DBIterator(nativeIterator, this, includeValues, options?.limit, transaction));
|
|
2067
2105
|
}
|
|
2068
2106
|
getSync(context, key, alwaysCreateNewBuffer = false, options) {
|
|
2069
2107
|
const keyParam = getKeyParam(this.encodeKey(key));
|
|
@@ -2177,7 +2215,8 @@ var Store = class {
|
|
|
2177
2215
|
transactionLogsPath: this.transactionLogsPath,
|
|
2178
2216
|
timestampFloorLog: this.timestampFloorLog,
|
|
2179
2217
|
verificationTable: this.verificationTable,
|
|
2180
|
-
writeBufferSize: this.writeBufferSize
|
|
2218
|
+
writeBufferSize: this.writeBufferSize,
|
|
2219
|
+
arenaBlockSize: this.arenaBlockSize
|
|
2181
2220
|
});
|
|
2182
2221
|
this.identityPath = this.db.identityPath;
|
|
2183
2222
|
return false;
|
|
@@ -2283,8 +2322,7 @@ function getKeyParam(keyBuffer) {
|
|
|
2283
2322
|
return keyBuffer.length;
|
|
2284
2323
|
}
|
|
2285
2324
|
|
|
2286
|
-
|
|
2287
|
-
//#region src/transaction.ts
|
|
2325
|
+
// src/transaction.ts
|
|
2288
2326
|
/**
|
|
2289
2327
|
* Sentinel value returned by `commit()` when `coordinatedRetry: true` and the
|
|
2290
2328
|
* transaction encountered an IsBusy conflict. The native layer parks on VT
|
|
@@ -2308,10 +2346,18 @@ var TransactionAlreadyAbortedError = class extends Error {
|
|
|
2308
2346
|
*/
|
|
2309
2347
|
var TransactionRetryableError = class extends Error {
|
|
2310
2348
|
hasLog;
|
|
2349
|
+
/**
|
|
2350
|
+
* The attempt was refused before writing its transaction-log batch (`transactionLogAbortMarkers`):
|
|
2351
|
+
* an older transaction held a write intent on one of its keys (`transactionLogWriteIntents`), or, on a retry, RocksDB's conflict
|
|
2352
|
+
* check found a write that already committed (see `lastConflictKind()`). Nothing of it is in the
|
|
2353
|
+
* log, so a retry may stage its batch afresh.
|
|
2354
|
+
*/
|
|
2355
|
+
refusedBeforeLog;
|
|
2311
2356
|
txn;
|
|
2312
2357
|
constructor(error, txn) {
|
|
2313
2358
|
super(error.message);
|
|
2314
2359
|
this.hasLog = error.hasLog ?? false;
|
|
2360
|
+
this.refusedBeforeLog = error.refusedBeforeLog ?? false;
|
|
2315
2361
|
this.txn = txn;
|
|
2316
2362
|
}
|
|
2317
2363
|
};
|
|
@@ -2341,6 +2387,38 @@ var TransactionAbandonedError = class extends Error {
|
|
|
2341
2387
|
}
|
|
2342
2388
|
};
|
|
2343
2389
|
/**
|
|
2390
|
+
* `abort()` of a transaction whose transaction-log batch could not be marked aborted
|
|
2391
|
+
* (`transactionLogAbortMarkers`). The batch stays unresolved, so committed readers do not pass it and
|
|
2392
|
+
* the log store refuses appends, until a retry on the next append, database flush or store close
|
|
2393
|
+
* marks it.
|
|
2394
|
+
*/
|
|
2395
|
+
var TransactionAbortUnresolvedError = class extends Error {
|
|
2396
|
+
code = "ERR_TRANSACTION_ABORT_UNRESOLVED";
|
|
2397
|
+
txn;
|
|
2398
|
+
constructor(error, txn) {
|
|
2399
|
+
super(error.message);
|
|
2400
|
+
this.txn = txn;
|
|
2401
|
+
}
|
|
2402
|
+
};
|
|
2403
|
+
/**
|
|
2404
|
+
* Why a transaction's most recent attempt was refused or retried, from {@link Transaction.lastConflictKind}.
|
|
2405
|
+
*/
|
|
2406
|
+
const ConflictKind = {
|
|
2407
|
+
none: 0,
|
|
2408
|
+
/** Refused on an older transaction's write intent before writing its log batch. */
|
|
2409
|
+
intentBeforeLog: 1,
|
|
2410
|
+
/** Refused on an older transaction's write intent after its log batch was written. */
|
|
2411
|
+
intentAfterLog: 2,
|
|
2412
|
+
/** RocksDB found a conflict at commit. */
|
|
2413
|
+
atCommit: 3,
|
|
2414
|
+
/**
|
|
2415
|
+
* RocksDB's conflict check, run before the log write, found a write that already committed, or,
|
|
2416
|
+
* as at commit, could not validate because the memtable history it needs was flushed (needs a
|
|
2417
|
+
* RocksDB build that exports the check).
|
|
2418
|
+
*/
|
|
2419
|
+
checkBeforeLog: 4
|
|
2420
|
+
};
|
|
2421
|
+
/**
|
|
2344
2422
|
* Provides transaction level operations to a transaction callback.
|
|
2345
2423
|
*/
|
|
2346
2424
|
var Transaction = class extends DBI {
|
|
@@ -2355,9 +2433,10 @@ var Transaction = class extends DBI {
|
|
|
2355
2433
|
if (store.readOnly) {
|
|
2356
2434
|
super(store);
|
|
2357
2435
|
this.#txn = { id: 0 };
|
|
2358
|
-
this.abandonWrites = this.abort = this.commitSync = this.setTimestamp = () => {};
|
|
2436
|
+
this.abandonWrites = this.abort = this.commitSync = this.setTimestamp = this.setParkDeadline = () => {};
|
|
2359
2437
|
this.commit = async () => {};
|
|
2360
|
-
this.getTimestamp = () => 0;
|
|
2438
|
+
this.getTimestamp = this.lastConflictKind = () => 0;
|
|
2439
|
+
this.logWritten = () => false;
|
|
2361
2440
|
} else {
|
|
2362
2441
|
const txn = new NativeTransaction(store.db, options);
|
|
2363
2442
|
super(store, txn);
|
|
@@ -2383,6 +2462,7 @@ var Transaction = class extends DBI {
|
|
|
2383
2462
|
this.#txn.abort();
|
|
2384
2463
|
} catch (err) {
|
|
2385
2464
|
if (err instanceof Error && "code" in err && err.code === "ERR_TRANSACTION_ABANDONED") throw new TransactionAbandonedError(err, this);
|
|
2465
|
+
if (err instanceof Error && "code" in err && err.code === "ERR_TRANSACTION_ABORT_UNRESOLVED") throw new TransactionAbortUnresolvedError(err, this);
|
|
2386
2466
|
throw err;
|
|
2387
2467
|
}
|
|
2388
2468
|
}
|
|
@@ -2451,6 +2531,28 @@ var Transaction = class extends DBI {
|
|
|
2451
2531
|
return this.#txn.getTimestamp();
|
|
2452
2532
|
}
|
|
2453
2533
|
/**
|
|
2534
|
+
* Whether this transaction's batch is in the transaction log. A commit conflict after the batch
|
|
2535
|
+
* was written keeps it for the retry, which must not stage it again; an attempt refused before
|
|
2536
|
+
* writing it (`refusedBeforeLog`) leaves nothing.
|
|
2537
|
+
*/
|
|
2538
|
+
logWritten() {
|
|
2539
|
+
return this.#txn.logWritten();
|
|
2540
|
+
}
|
|
2541
|
+
/**
|
|
2542
|
+
* Why the most recent attempt was refused or retried; one of {@link ConflictKind}.
|
|
2543
|
+
*/
|
|
2544
|
+
lastConflictKind() {
|
|
2545
|
+
return this.#txn.lastConflictKind();
|
|
2546
|
+
}
|
|
2547
|
+
/**
|
|
2548
|
+
* Bounds coordinated-retry parks: past `deadline` (epoch milliseconds) a parked commit resolves
|
|
2549
|
+
* `RETRY_NOW` at once, so a caller enforcing its own deadline can abort instead of waiting out
|
|
2550
|
+
* the default park bound. 0 clears it.
|
|
2551
|
+
*/
|
|
2552
|
+
setParkDeadline(deadline) {
|
|
2553
|
+
this.#txn.setParkDeadline(deadline);
|
|
2554
|
+
}
|
|
2555
|
+
/**
|
|
2454
2556
|
* Get the transaction id.
|
|
2455
2557
|
*/
|
|
2456
2558
|
get id() {
|
|
@@ -2471,8 +2573,8 @@ var Transaction = class extends DBI {
|
|
|
2471
2573
|
}
|
|
2472
2574
|
};
|
|
2473
2575
|
|
|
2474
|
-
|
|
2475
|
-
|
|
2576
|
+
// src/database.ts
|
|
2577
|
+
const backoffCell = new Int32Array(new SharedArrayBuffer(4));
|
|
2476
2578
|
const BACKGROUND_ERROR_SEVERITY_NAMES = [
|
|
2477
2579
|
"none",
|
|
2478
2580
|
"soft",
|
|
@@ -2882,6 +2984,19 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2882
2984
|
return this.store.db.flushSync(options);
|
|
2883
2985
|
}
|
|
2884
2986
|
/**
|
|
2987
|
+
* Syncs the write-ahead log to stable storage (`DB::SyncWAL`), so every WAL-backed write
|
|
2988
|
+
* committed before the call survives a power loss afterwards. Unlike {@link flush} it writes
|
|
2989
|
+
* no SST file and never stalls writers. Not covered: writes that skipped the WAL
|
|
2990
|
+
* (`disableWAL`), and transaction-log files, which keep their own durability. Runs on the
|
|
2991
|
+
* libuv threadpool; RocksDB serializes concurrent syncs of one WAL, so keep one in flight
|
|
2992
|
+
* per database rather than parking pool workers on a queue of them.
|
|
2993
|
+
*
|
|
2994
|
+
* On a read-only database the returned promise resolves without syncing.
|
|
2995
|
+
*/
|
|
2996
|
+
syncWal() {
|
|
2997
|
+
return new Promise((resolve, reject) => this.store.db.syncWal(resolve, reject));
|
|
2998
|
+
}
|
|
2999
|
+
/**
|
|
2885
3000
|
* Returns the most recent {@link BackgroundError} observed on this database, or
|
|
2886
3001
|
* `null` when none has occurred. The `'error'` event is the push counterpart;
|
|
2887
3002
|
* this is the pull equivalent for on-demand checks (e.g. a health probe) and
|
|
@@ -3350,7 +3465,10 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
3350
3465
|
async transaction(callback, options) {
|
|
3351
3466
|
if (typeof callback !== "function") throw new TypeError("Callback must be a function");
|
|
3352
3467
|
const maxRetries = options?.maxRetries ?? 3;
|
|
3353
|
-
const txn = new Transaction(this.store, options
|
|
3468
|
+
const txn = new Transaction(this.store, options?.retryOnBusy === false && options.coordinatedRetry ? {
|
|
3469
|
+
...options,
|
|
3470
|
+
coordinatedRetry: false
|
|
3471
|
+
} : options);
|
|
3354
3472
|
let result;
|
|
3355
3473
|
this.notify("begin-transaction");
|
|
3356
3474
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
@@ -3364,7 +3482,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
3364
3482
|
commitResult = await txn.commit();
|
|
3365
3483
|
} catch (commitErr) {
|
|
3366
3484
|
if (commitErr instanceof TransactionAlreadyAbortedError) return;
|
|
3367
|
-
if (commitErr instanceof TransactionRetryableError && (options?.retryOnBusy ?? commitErr.hasLog) && attempt < maxRetries) continue;
|
|
3485
|
+
if (commitErr instanceof TransactionRetryableError && (options?.retryOnBusy ?? (commitErr.hasLog || commitErr.refusedBeforeLog)) && attempt < maxRetries) continue;
|
|
3368
3486
|
this.#abandonTransaction(txn, commitErr);
|
|
3369
3487
|
return;
|
|
3370
3488
|
}
|
|
@@ -3396,10 +3514,13 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
3396
3514
|
if (typeof callback !== "function") throw new TypeError("Callback must be a function");
|
|
3397
3515
|
const maxRetries = options?.maxRetries ?? 3;
|
|
3398
3516
|
const isRetryable = (err, attempt) => {
|
|
3399
|
-
|
|
3517
|
+
if (!(err instanceof TransactionRetryableError && (options?.retryOnBusy ?? (err.hasLog || err.refusedBeforeLog)) && attempt <= maxRetries)) return false;
|
|
3518
|
+
const kind = txn.lastConflictKind();
|
|
3519
|
+
if (kind === ConflictKind.intentBeforeLog || kind === ConflictKind.intentAfterLog) Atomics.wait(backoffCell, 0, 0, Math.min(2 ** attempt, 50));
|
|
3520
|
+
return true;
|
|
3400
3521
|
};
|
|
3522
|
+
const txn = new Transaction(this.store, options);
|
|
3401
3523
|
const runAttempt = (attempt) => {
|
|
3402
|
-
const txn = new Transaction(this.store, options);
|
|
3403
3524
|
let result;
|
|
3404
3525
|
try {
|
|
3405
3526
|
result = callback(txn, attempt);
|
|
@@ -3415,7 +3536,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
3415
3536
|
if (isRetryable(commitErr, attempt)) return runAttempt(attempt + 1);
|
|
3416
3537
|
this.#abandonTransaction(txn, commitErr);
|
|
3417
3538
|
}
|
|
3418
|
-
});
|
|
3539
|
+
}, (callbackErr) => this.#abortTransaction(txn, callbackErr));
|
|
3419
3540
|
try {
|
|
3420
3541
|
txn.commitSync();
|
|
3421
3542
|
return result;
|
|
@@ -3440,7 +3561,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
3440
3561
|
try {
|
|
3441
3562
|
txn.abort();
|
|
3442
3563
|
} catch (abortErr) {
|
|
3443
|
-
if (abortErr instanceof TransactionAbandonedError) throw abortErr;
|
|
3564
|
+
if (abortErr instanceof TransactionAbandonedError || abortErr instanceof TransactionAbortUnresolvedError) throw abortErr;
|
|
3444
3565
|
}
|
|
3445
3566
|
throw commitErr;
|
|
3446
3567
|
}
|
|
@@ -3555,10 +3676,10 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
3555
3676
|
}
|
|
3556
3677
|
};
|
|
3557
3678
|
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
const { TRANSACTION_LOG_TOKEN, TRANSACTION_LOG_ENTRY_HEADER_SIZE: TRANSACTION_LOG_ENTRY_HEADER_SIZE$1 } = constants;
|
|
3679
|
+
// src/parse-transaction-log.ts
|
|
3680
|
+
const { TRANSACTION_LOG_TOKEN, TRANSACTION_LOG_ENTRY_HEADER_SIZE: TRANSACTION_LOG_ENTRY_HEADER_SIZE$1, TRANSACTION_LOG_ENTRY_LAST_FLAG: TRANSACTION_LOG_ENTRY_LAST_FLAG$1, TRANSACTION_LOG_ENTRY_ABORTED_FLAG: TRANSACTION_LOG_ENTRY_ABORTED_FLAG$1 } = constants;
|
|
3561
3681
|
const MIN_VALID_TIMESTAMP = Date.UTC(2026, 0, 27);
|
|
3682
|
+
const VALID_FLAGS_MASK = TRANSACTION_LOG_ENTRY_LAST_FLAG$1 | TRANSACTION_LOG_ENTRY_ABORTED_FLAG$1;
|
|
3562
3683
|
/**
|
|
3563
3684
|
* Loads an entire transaction log file into memory.
|
|
3564
3685
|
* @param path - The path to the transaction log file.
|
|
@@ -3596,6 +3717,8 @@ function parseTransactionLog(path, options = {}) {
|
|
|
3596
3717
|
if (!Number.isFinite(timestamp) || timestamp < MIN_VALID_TIMESTAMP) anomalies.push(`Header timestamp ${timestamp} predates 2026-01-27 (possible corruption)`);
|
|
3597
3718
|
const entries = [];
|
|
3598
3719
|
let entryAnomalyCount = 0;
|
|
3720
|
+
let previousEndedTransaction = true;
|
|
3721
|
+
let previousTimestamp = NaN;
|
|
3599
3722
|
while (fileOffset < size) {
|
|
3600
3723
|
const timestamp = read(8).readDoubleBE(0);
|
|
3601
3724
|
if (timestamp === 0) {
|
|
@@ -3612,7 +3735,11 @@ function parseTransactionLog(path, options = {}) {
|
|
|
3612
3735
|
const data = read(length);
|
|
3613
3736
|
const entryAnomalies = [];
|
|
3614
3737
|
if (!Number.isFinite(timestamp) || timestamp < MIN_VALID_TIMESTAMP) entryAnomalies.push(`timestamp ${timestamp} predates 2026-01-27 (possible corruption)`);
|
|
3615
|
-
if ((flags &
|
|
3738
|
+
if ((flags & ~VALID_FLAGS_MASK) !== 0) entryAnomalies.push(`flags 0x${flags.toString(16).padStart(2, "0")} contains undefined bits (expected 0x00-0x03)`);
|
|
3739
|
+
const startsTransaction = previousEndedTransaction || timestamp !== previousTimestamp;
|
|
3740
|
+
if ((flags & TRANSACTION_LOG_ENTRY_ABORTED_FLAG$1) !== 0 && !startsTransaction) entryAnomalies.push("abort flag on an entry that does not start a transaction");
|
|
3741
|
+
previousEndedTransaction = (flags & TRANSACTION_LOG_ENTRY_LAST_FLAG$1) !== 0;
|
|
3742
|
+
previousTimestamp = timestamp;
|
|
3616
3743
|
const entry = {
|
|
3617
3744
|
timestamp,
|
|
3618
3745
|
length,
|
|
@@ -3641,11 +3768,11 @@ function parseTransactionLog(path, options = {}) {
|
|
|
3641
3768
|
}
|
|
3642
3769
|
}
|
|
3643
3770
|
|
|
3644
|
-
|
|
3645
|
-
//#region src/transaction-log-reader.ts
|
|
3771
|
+
// src/transaction-log-reader.ts
|
|
3646
3772
|
const FLOAT_TO_UINT32 = /* @__PURE__ */ new Float64Array(1);
|
|
3647
3773
|
const UINT32_FROM_FLOAT = new Uint32Array(FLOAT_TO_UINT32.buffer);
|
|
3648
|
-
const
|
|
3774
|
+
const EXACT_START_SPAN = /* @__PURE__ */ new Float64Array(3);
|
|
3775
|
+
const { TRANSACTION_LOG_FILE_HEADER_SIZE, TRANSACTION_LOG_ENTRY_HEADER_SIZE, TRANSACTION_LOG_ENTRY_LAST_FLAG, TRANSACTION_LOG_ENTRY_ABORTED_FLAG } = constants;
|
|
3649
3776
|
/**
|
|
3650
3777
|
* Number of consecutive well-formed frames that, on their own, signal that real log data has
|
|
3651
3778
|
* resumed after a framing break. Kept in step with `RESYNC_MIN_FRAMES` in
|
|
@@ -3681,6 +3808,32 @@ var CorruptFrameError = class extends RangeError {
|
|
|
3681
3808
|
}
|
|
3682
3809
|
};
|
|
3683
3810
|
/**
|
|
3811
|
+
* Thrown when an iterator advances past segments it never read and that can no longer be read —
|
|
3812
|
+
* typically ones retention purged while the reader lagged. The entries they held cannot be
|
|
3813
|
+
* recovered from this log, so a consumer replicating it must not treat the stream as contiguous.
|
|
3814
|
+
* Missing sequence numbers prove only that continuity is unknown, not that each held entries.
|
|
3815
|
+
*
|
|
3816
|
+
* The iterator is left positioned at the start of `resumeLogId`, so a caller that accepts the
|
|
3817
|
+
* discontinuity calls `next()` again to continue from there.
|
|
3818
|
+
*
|
|
3819
|
+
* Not a `RangeError`: this is not broken framing, and consumers classify those by class.
|
|
3820
|
+
*/
|
|
3821
|
+
var TransactionLogGapError = class extends Error {
|
|
3822
|
+
/** Sequence number of the last segment this iterator read up to. */
|
|
3823
|
+
logId;
|
|
3824
|
+
/** Sequence number of the segment iteration resumes in. */
|
|
3825
|
+
resumeLogId;
|
|
3826
|
+
/** Segments between `logId` and `resumeLogId` that could not be read. Empty segments are not counted. */
|
|
3827
|
+
unavailableLogCount;
|
|
3828
|
+
constructor(logId, resumeLogId, unavailableLogCount) {
|
|
3829
|
+
super(`Transaction log iterator skipped ${unavailableLogCount} unavailable segment(s) between log ${logId} and log ${resumeLogId}; entries in them were not read`);
|
|
3830
|
+
this.name = "TransactionLogGapError";
|
|
3831
|
+
this.logId = logId;
|
|
3832
|
+
this.resumeLogId = resumeLogId;
|
|
3833
|
+
this.unavailableLogCount = unavailableLogCount;
|
|
3834
|
+
}
|
|
3835
|
+
};
|
|
3836
|
+
/**
|
|
3684
3837
|
* Whether a complete, in-bounds frame begins at `pos`. The only sane bound on an entry's length is
|
|
3685
3838
|
* the written extent: a single entry can legitimately exceed the rotation threshold (the first
|
|
3686
3839
|
* entry written to a fresh file is always written in full). A zero timestamp is the
|
|
@@ -3756,6 +3909,14 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3756
3909
|
let dataView;
|
|
3757
3910
|
let logBuffer = this._currentLogBuffer?.deref();
|
|
3758
3911
|
let foundExactStart = false;
|
|
3912
|
+
let stopLogId = 0;
|
|
3913
|
+
let stopPosition = 0;
|
|
3914
|
+
let tailLogId = 0;
|
|
3915
|
+
let tailPosition = 0;
|
|
3916
|
+
let segmentRemovals = 0;
|
|
3917
|
+
let previousEndedTransaction = true;
|
|
3918
|
+
let previousTimestamp = NaN;
|
|
3919
|
+
let skippingAborted = false;
|
|
3759
3920
|
if (start === void 0 && !startFromLastFlushed) {
|
|
3760
3921
|
position = size;
|
|
3761
3922
|
if (position === 0) position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
@@ -3765,11 +3926,32 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3765
3926
|
FLOAT_TO_UINT32[0] = this._getLastFlushed();
|
|
3766
3927
|
if (FLOAT_TO_UINT32[0] === 0) FLOAT_TO_UINT32[0] = this._findPosition(0);
|
|
3767
3928
|
start ??= 0;
|
|
3929
|
+
} else if (exactStart) {
|
|
3930
|
+
const startPosition = this._findExactStart(start, EXACT_START_SPAN);
|
|
3931
|
+
FLOAT_TO_UINT32[0] = EXACT_START_SPAN[0];
|
|
3932
|
+
stopLogId = UINT32_FROM_FLOAT[1];
|
|
3933
|
+
stopPosition = UINT32_FROM_FLOAT[0];
|
|
3934
|
+
FLOAT_TO_UINT32[0] = EXACT_START_SPAN[1];
|
|
3935
|
+
tailLogId = UINT32_FROM_FLOAT[1];
|
|
3936
|
+
tailPosition = UINT32_FROM_FLOAT[0];
|
|
3937
|
+
segmentRemovals = EXACT_START_SPAN[2];
|
|
3938
|
+
FLOAT_TO_UINT32[0] = startPosition;
|
|
3768
3939
|
} else FLOAT_TO_UINT32[0] = this._findPosition(start);
|
|
3769
3940
|
logId = UINT32_FROM_FLOAT[1];
|
|
3770
3941
|
position = UINT32_FROM_FLOAT[0];
|
|
3771
3942
|
if (position === 0) position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
3772
3943
|
}
|
|
3944
|
+
const positionedAt = position;
|
|
3945
|
+
if (stopLogId !== 0 && pastStop(logId, position, stopLogId, stopPosition)) {
|
|
3946
|
+
stopLogId = 0;
|
|
3947
|
+
if (skipToTail(this, logId, position, tailLogId, tailPosition, segmentRemovals)) {
|
|
3948
|
+
logId = tailLogId;
|
|
3949
|
+
position = tailPosition;
|
|
3950
|
+
logBuffer = tailLogBuffer ?? logBuffer;
|
|
3951
|
+
}
|
|
3952
|
+
tailLogBuffer = void 0;
|
|
3953
|
+
}
|
|
3954
|
+
let gapFloor = logId;
|
|
3773
3955
|
if (logBuffer === void 0 || logBuffer.logId !== logId) {
|
|
3774
3956
|
logBuffer = getLogMemoryMap(this, logId);
|
|
3775
3957
|
if (logBuffer && latestLogId === logId && !readUncommitted && this._currentLogBuffer?.deref() !== logBuffer) this._currentLogBuffer = new WeakRef(logBuffer);
|
|
@@ -3779,6 +3961,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3779
3961
|
logBuffer.size = 0;
|
|
3780
3962
|
logBuffer.dataView = new DataView(logBuffer.buffer);
|
|
3781
3963
|
size = 0;
|
|
3964
|
+
if (positionedAt <= TRANSACTION_LOG_FILE_HEADER_SIZE || logId > 0 && this.getLogFileSize(logId) > 0) gapFloor = logId - 1;
|
|
3782
3965
|
}
|
|
3783
3966
|
}
|
|
3784
3967
|
dataView = logBuffer.dataView;
|
|
@@ -3788,7 +3971,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3788
3971
|
if (cachedSize === void 0) size = logBuffer.size = readableExtent(logBuffer);
|
|
3789
3972
|
else size = cachedSize;
|
|
3790
3973
|
}
|
|
3791
|
-
|
|
3974
|
+
const iterator = {
|
|
3792
3975
|
[Symbol.iterator]() {
|
|
3793
3976
|
return this;
|
|
3794
3977
|
},
|
|
@@ -3800,13 +3983,17 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3800
3983
|
if (latestLogId > logBuffer.logId) {
|
|
3801
3984
|
size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
3802
3985
|
if (position >= size) {
|
|
3803
|
-
const
|
|
3986
|
+
const fromLogId = logBuffer.logId;
|
|
3987
|
+
const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
|
|
3804
3988
|
if (nextLogBuffer) {
|
|
3989
|
+
const unavailable = unavailableLogCount;
|
|
3805
3990
|
dataView = nextLogBuffer.dataView;
|
|
3806
3991
|
logBuffer = nextLogBuffer;
|
|
3807
3992
|
if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
3808
3993
|
else size = latestSize;
|
|
3809
3994
|
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
3995
|
+
previousEndedTransaction = true;
|
|
3996
|
+
if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
|
|
3810
3997
|
}
|
|
3811
3998
|
}
|
|
3812
3999
|
}
|
|
@@ -3826,30 +4013,55 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3826
4013
|
if (position + TRANSACTION_LOG_ENTRY_HEADER_SIZE > limit) {
|
|
3827
4014
|
const broken = corruptFrame(`Corrupt transaction log: truncated entry header at position ${position.toString(16)} of log ${logBuffer.logId} (available=${limit - position})`, transactionLog, dataView, logBuffer, position, limit, size, !!readUncommitted);
|
|
3828
4015
|
position = broken.nextPosition;
|
|
4016
|
+
previousEndedTransaction = true;
|
|
3829
4017
|
throw broken.error;
|
|
3830
4018
|
}
|
|
3831
4019
|
const length = dataView.getUint32(position + 8);
|
|
3832
4020
|
if (length === 0 || position + TRANSACTION_LOG_ENTRY_HEADER_SIZE + length > limit) {
|
|
3833
4021
|
const broken = corruptFrame(length === 0 ? `Corrupt transaction log entry at position ${position.toString(16)} of log ${logBuffer.logId}: zero-length entry` : `Corrupt transaction log entry at position ${position.toString(16)} of log ${logBuffer.logId}: declared length ${length} overruns the log (limit=${limit})`, transactionLog, dataView, logBuffer, position, limit, size, !!readUncommitted);
|
|
3834
4022
|
position = broken.nextPosition;
|
|
4023
|
+
previousEndedTransaction = true;
|
|
3835
4024
|
throw broken.error;
|
|
3836
4025
|
}
|
|
3837
4026
|
position += TRANSACTION_LOG_ENTRY_HEADER_SIZE;
|
|
4027
|
+
const flags = logBuffer[position - 1];
|
|
4028
|
+
const aborted = (flags & TRANSACTION_LOG_ENTRY_ABORTED_FLAG) !== 0;
|
|
4029
|
+
if (previousEndedTransaction || timestamp !== previousTimestamp || aborted) skippingAborted = aborted;
|
|
4030
|
+
previousEndedTransaction = (flags & TRANSACTION_LOG_ENTRY_LAST_FLAG) !== 0;
|
|
4031
|
+
previousTimestamp = timestamp;
|
|
3838
4032
|
let matchesRange;
|
|
3839
|
-
if (
|
|
4033
|
+
if (skippingAborted) matchesRange = false;
|
|
4034
|
+
else if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
|
|
3840
4035
|
else if (exactStart) {
|
|
4036
|
+
matchesRange = false;
|
|
3841
4037
|
if (timestamp === start) {
|
|
3842
4038
|
matchesRange = !exclusiveStart;
|
|
3843
4039
|
foundExactStart = true;
|
|
3844
|
-
|
|
4040
|
+
stopLogId = 0;
|
|
4041
|
+
}
|
|
3845
4042
|
} else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
|
|
4043
|
+
if (!matchesRange && stopLogId !== 0 && pastStop(logBuffer.logId, position + length, stopLogId, stopPosition)) {
|
|
4044
|
+
stopLogId = 0;
|
|
4045
|
+
position += length;
|
|
4046
|
+
if (skipToTail(transactionLog, logBuffer.logId, position, tailLogId, tailPosition, segmentRemovals)) {
|
|
4047
|
+
if (tailLogBuffer) {
|
|
4048
|
+
logBuffer = tailLogBuffer;
|
|
4049
|
+
dataView = tailLogBuffer.dataView;
|
|
4050
|
+
}
|
|
4051
|
+
position = tailPosition;
|
|
4052
|
+
previousEndedTransaction = true;
|
|
4053
|
+
size = 0;
|
|
4054
|
+
}
|
|
4055
|
+
tailLogBuffer = void 0;
|
|
4056
|
+
return iterator.next();
|
|
4057
|
+
}
|
|
3846
4058
|
const entryStart = position;
|
|
3847
4059
|
position += length;
|
|
3848
4060
|
if (matchesRange) return {
|
|
3849
4061
|
done: false,
|
|
3850
4062
|
value: {
|
|
3851
4063
|
timestamp,
|
|
3852
|
-
endTxn:
|
|
4064
|
+
endTxn: previousEndedTransaction,
|
|
3853
4065
|
data: logBuffer.subarray(entryStart, position)
|
|
3854
4066
|
}
|
|
3855
4067
|
};
|
|
@@ -3860,11 +4072,13 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3860
4072
|
size = logBuffer.size ?? readableExtent(logBuffer);
|
|
3861
4073
|
if (!readUncommitted) logBuffer.size = size;
|
|
3862
4074
|
if (position < size) continue;
|
|
3863
|
-
const
|
|
4075
|
+
const fromLogId = logBuffer.logId;
|
|
4076
|
+
const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
|
|
3864
4077
|
if (!nextLogBuffer) return {
|
|
3865
4078
|
done: true,
|
|
3866
4079
|
value: void 0
|
|
3867
4080
|
};
|
|
4081
|
+
const unavailable = unavailableLogCount;
|
|
3868
4082
|
logBuffer = nextLogBuffer;
|
|
3869
4083
|
dataView = logBuffer.dataView;
|
|
3870
4084
|
if (latestLogId === logBuffer.logId) size = latestSize;
|
|
@@ -3876,6 +4090,8 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3876
4090
|
} else size = cachedSize;
|
|
3877
4091
|
}
|
|
3878
4092
|
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
4093
|
+
previousEndedTransaction = true;
|
|
4094
|
+
if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
|
|
3879
4095
|
}
|
|
3880
4096
|
}
|
|
3881
4097
|
}
|
|
@@ -3885,7 +4101,29 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3885
4101
|
};
|
|
3886
4102
|
}
|
|
3887
4103
|
};
|
|
4104
|
+
return iterator;
|
|
3888
4105
|
} });
|
|
4106
|
+
function pastStop(logId, position, stopLogId, stopPosition) {
|
|
4107
|
+
return logId > stopLogId || logId === stopLogId && position >= stopPosition;
|
|
4108
|
+
}
|
|
4109
|
+
let tailLogBuffer;
|
|
4110
|
+
/**
|
|
4111
|
+
* Whether an exact-start lookup at `position` of `fromLogId` may take its one-shot jump to the tail.
|
|
4112
|
+
* next() calls it on a frame boundary, so a mapping error leaves a resumable cursor; from query()
|
|
4113
|
+
* the error escapes before any iterator exists. The removal
|
|
4114
|
+
* check follows the mapping and holds the store's lock, so a purge either declines the jump (the
|
|
4115
|
+
* scan then reports the gap) or follows it like a purge of segments already read.
|
|
4116
|
+
*/
|
|
4117
|
+
function skipToTail(transactionLog, fromLogId, position, tailLogId, tailPosition, segmentRemovals) {
|
|
4118
|
+
tailLogBuffer = void 0;
|
|
4119
|
+
if (tailLogId === fromLogId) return tailPosition > position;
|
|
4120
|
+
if (tailLogId < fromLogId) return false;
|
|
4121
|
+
const mapped = getLogMemoryMap(transactionLog, tailLogId);
|
|
4122
|
+
if (mapped === void 0 || transactionLog._segmentRemovals() !== segmentRemovals) return false;
|
|
4123
|
+
tailLogBuffer = mapped;
|
|
4124
|
+
return true;
|
|
4125
|
+
}
|
|
4126
|
+
let unavailableLogCount = 0;
|
|
3889
4127
|
/**
|
|
3890
4128
|
* Maps the next readable segment after `fromLogId`, skipping a run retention has deleted —
|
|
3891
4129
|
* stopping at the hole wedges the iterator there for every later poll (invariant 30).
|
|
@@ -3894,15 +4132,22 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3894
4132
|
* `_findPosition(0)`, which names the bottom of the contiguous run ending at the current segment
|
|
3895
4133
|
* and so lands *past* a survivor sitting between two holes.
|
|
3896
4134
|
*/
|
|
3897
|
-
function nextReadableLogBuffer(transactionLog, fromLogId, latestLogId) {
|
|
4135
|
+
function nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor) {
|
|
4136
|
+
unavailableLogCount = 0;
|
|
3898
4137
|
let candidateLogId = fromLogId + 1;
|
|
3899
4138
|
while (candidateLogId <= latestLogId) {
|
|
3900
4139
|
const logBuffer = getLogMemoryMap(transactionLog, candidateLogId);
|
|
3901
4140
|
if (logBuffer) {
|
|
3902
4141
|
if (candidateLogId === latestLogId || readableExtent(logBuffer) > TRANSACTION_LOG_FILE_HEADER_SIZE) return logBuffer;
|
|
3903
|
-
} else
|
|
3904
|
-
|
|
3905
|
-
|
|
4142
|
+
} else {
|
|
4143
|
+
const fileSize = transactionLog.getLogFileSize(candidateLogId);
|
|
4144
|
+
if (fileSize > TRANSACTION_LOG_FILE_HEADER_SIZE) return;
|
|
4145
|
+
if (fileSize === 0 && candidateLogId > gapFloor) unavailableLogCount++;
|
|
4146
|
+
}
|
|
4147
|
+
const nextLogId = transactionLog._nextLogId(candidateLogId);
|
|
4148
|
+
if (nextLogId === 0) return;
|
|
4149
|
+
unavailableLogCount += Math.max(0, nextLogId - 1 - Math.max(candidateLogId, gapFloor));
|
|
4150
|
+
candidateLogId = nextLogId;
|
|
3906
4151
|
}
|
|
3907
4152
|
}
|
|
3908
4153
|
/**
|
|
@@ -3956,13 +4201,11 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
3956
4201
|
};
|
|
3957
4202
|
}
|
|
3958
4203
|
|
|
3959
|
-
|
|
3960
|
-
//#region src/index.ts
|
|
4204
|
+
// src/index.ts
|
|
3961
4205
|
const versions = {
|
|
3962
4206
|
rocksdb: version,
|
|
3963
|
-
"rocksdb-js": "2.
|
|
4207
|
+
"rocksdb-js": "2.13.0"
|
|
3964
4208
|
};
|
|
3965
4209
|
|
|
3966
|
-
|
|
3967
|
-
export { BackgroundError, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getRegistryStatus, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, steadyClockNow, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
|
|
4210
|
+
export { BackgroundError, ConflictKind, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionAbortUnresolvedError, TransactionLog, TransactionLogGapError, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getRegistryStatus, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, steadyClockNow, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
|
|
3968
4211
|
//# sourceMappingURL=index.mjs.map
|