@harperfast/rocksdb-js 2.9.1 → 2.11.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 +377 -56
- package/bin/rocksdb-js.mjs +49 -5
- package/dist/index.cjs +206 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +180 -7
- package/dist/index.d.mts +180 -7
- package/dist/index.mjs +205 -33
- package/dist/index.mjs.map +1 -1
- package/docs/backups.md +3 -2
- package/docs/performance/mutex-contention-2026-09-16.json +684 -0
- package/docs/performance/mutex-contention-admission-2026-09-17.json +730 -0
- package/docs/performance/mutex-contention.md +204 -0
- package/docs/stats.md +48 -46
- package/docs/steady-clock-design.md +121 -0
- package/docs/transaction-log.md +120 -1
- package/docs/transaction-timestamp-integrity-design.md +70 -8
- package/package.json +16 -16
package/dist/index.mjs
CHANGED
|
@@ -105,10 +105,60 @@ const NativeDatabase = binding.Database;
|
|
|
105
105
|
const NativeIterator = binding.Iterator;
|
|
106
106
|
const NativeTransaction = binding.Transaction;
|
|
107
107
|
const TransactionLog = binding.TransactionLog;
|
|
108
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Returns the status of every database currently held by the native registry.
|
|
110
|
+
* Walks every registered descriptor, so it samples live state rather than
|
|
111
|
+
* reading a stored property.
|
|
112
|
+
*/
|
|
113
|
+
const getRegistryStatus = binding.registryStatus;
|
|
114
|
+
/**
|
|
115
|
+
* @deprecated Use {@link getRegistryStatus} instead. This alias will be removed
|
|
116
|
+
* in v3.0.0.
|
|
117
|
+
*/
|
|
118
|
+
const registryStatus = getRegistryStatus;
|
|
119
|
+
/**
|
|
120
|
+
* Flushes every open database and waits for outstanding compactions, then
|
|
121
|
+
* releases the registry.
|
|
122
|
+
*
|
|
123
|
+
* **Throws** the first failure it encounters: a close whose native teardown
|
|
124
|
+
* failed (that descriptor stays quarantined — call `shutdown()` again to retry
|
|
125
|
+
* it), or a `lifecycleWaitSeconds` timeout waiting on a concurrent
|
|
126
|
+
* open/destroy/shutdown. A quarantining close also emits
|
|
127
|
+
* `database:closeFailed`; a timeout has no event, so the throw is its only
|
|
128
|
+
* signal.
|
|
129
|
+
*
|
|
130
|
+
* A `process.on('exit')` listener must therefore wrap it: an exception thrown
|
|
131
|
+
* from an `exit` listener skips every `exit` listener registered after it (and
|
|
132
|
+
* sets exit code 1 unless an `uncaughtException` handler is installed).
|
|
133
|
+
*
|
|
134
|
+
* ```typescript
|
|
135
|
+
* process.on('exit', () => {
|
|
136
|
+
* try {
|
|
137
|
+
* shutdown();
|
|
138
|
+
* } catch (error) {
|
|
139
|
+
* console.error('rocksdb-js shutdown failed', error);
|
|
140
|
+
* }
|
|
141
|
+
* });
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
109
144
|
const shutdown = binding.shutdown;
|
|
110
145
|
const currentThreadId = binding.currentThreadId;
|
|
111
146
|
/**
|
|
147
|
+
* Reads the process-wide steady clock: `std::chrono::steady_clock` as
|
|
148
|
+
* fractional milliseconds from an unspecified origin that is fixed for the life
|
|
149
|
+
* of the process. Every sample in the process — main thread and every
|
|
150
|
+
* `worker_threads` worker, whenever started or restarted, on Node, Bun and Deno
|
|
151
|
+
* — is in one domain, and the difference between two samples is real elapsed
|
|
152
|
+
* time unaffected by wall-clock steps. Non-decreasing but not unique (two
|
|
153
|
+
* samples may be equal; treat `==` as "not later"). Windows QPC samples from
|
|
154
|
+
* different threads within one native tick also have ambiguous ordering.
|
|
155
|
+
* Clock readings alone do not prove causality. Not comparable with
|
|
156
|
+
* `Date.now()`, `db.getMonotonicTimestamp()` or transaction timestamps, and not
|
|
157
|
+
* meaningful across processes or restarts. Time spent in host suspend is
|
|
158
|
+
* platform-defined. No open database is needed.
|
|
159
|
+
*/
|
|
160
|
+
const steadyClockNow = binding.steadyClockNow;
|
|
161
|
+
/**
|
|
112
162
|
* Advises the kernel that the file-backed pages of every mapped transaction log
|
|
113
163
|
* are cold (Linux MADV_COLD), so they are reclaimed first under memory pressure
|
|
114
164
|
* without being freed — useful during replication catch-up, where a full read of
|
|
@@ -129,11 +179,32 @@ const coolTransactionLogs = binding.coolTransactionLogs;
|
|
|
129
179
|
*/
|
|
130
180
|
const transactionLogMapCount = binding.transactionLogMapCount;
|
|
131
181
|
/**
|
|
182
|
+
* Number of coordinated-retry wake callbacks registered on verification-table
|
|
183
|
+
* locks across the process. Internal — used by tests to verify that a park
|
|
184
|
+
* ending by timeout, env teardown, or close leaves no registration behind.
|
|
185
|
+
*/
|
|
186
|
+
const lockWakeCallbackCount = binding.lockWakeCallbackCount;
|
|
187
|
+
/**
|
|
132
188
|
* Test-only: force the next `count` transaction commits to fail with TryAgain (rolled back, so
|
|
133
189
|
* no data is committed), reproducing a stranded-snapshot conflict deterministically. Pass 0 to
|
|
134
190
|
* disarm. Used by the ERR_TRY_AGAIN retry regression test.
|
|
135
191
|
*/
|
|
136
192
|
const forceTryAgainForTesting = binding.forceTryAgainForTesting;
|
|
193
|
+
/**
|
|
194
|
+
* Test-only seam: makes the physical column-family drop report failure.
|
|
195
|
+
* `0` = inert, `1` = fail without dropping (the retry performs the drop),
|
|
196
|
+
* `2` = drop for real and then report failure (the retry resolves as
|
|
197
|
+
* "already dropped"). Process-global, like `forceTryAgainForTesting`.
|
|
198
|
+
*/
|
|
199
|
+
const forceDropFailureForTesting = binding.forceDropFailureForTesting;
|
|
200
|
+
/** Parks one selected transaction write after its retirement precheck. */
|
|
201
|
+
const setTransactionStagingDelayForTesting = binding.setTransactionStagingDelayForTesting;
|
|
202
|
+
/** Reports whether the selected transaction write is parked. */
|
|
203
|
+
const isTransactionStagingDelayedForTesting = binding.isTransactionStagingDelayedForTesting;
|
|
204
|
+
/** Reports whether a transaction is parked at the native commit seam. */
|
|
205
|
+
const isTransactionCommitExecuteDelayedForTesting = binding.isTransactionCommitExecuteDelayedForTesting;
|
|
206
|
+
const setTransactionCommitAdmissionDelayForTesting = binding.setTransactionCommitAdmissionDelayForTesting;
|
|
207
|
+
const isTransactionCommitAdmissionDelayedForTesting = binding.isTransactionCommitAdmissionDelayedForTesting;
|
|
137
208
|
/** Delays one selected watchdog join in the concurrent-shutdown regression test. */
|
|
138
209
|
const setWriteBufferManagerJoinDelayForTesting = binding.setWriteBufferManagerJoinDelayForTesting;
|
|
139
210
|
/**
|
|
@@ -1651,6 +1722,7 @@ var Store = class {
|
|
|
1651
1722
|
/**
|
|
1652
1723
|
* The target maximum size of a transaction log before rotation. Transactions
|
|
1653
1724
|
* are never split across files, so one transaction may exceed this target.
|
|
1725
|
+
* Defaults to 16 MiB. `0` (formerly unlimited) is rejected at open.
|
|
1654
1726
|
*/
|
|
1655
1727
|
transactionLogMaxSize;
|
|
1656
1728
|
/**
|
|
@@ -1665,6 +1737,11 @@ var Store = class {
|
|
|
1665
1737
|
*/
|
|
1666
1738
|
transactionLogsPath;
|
|
1667
1739
|
/**
|
|
1740
|
+
* The transaction log whose batch keys this process originates; seeds the
|
|
1741
|
+
* monotonic timestamp floor at open. See `StoreOptions.timestampFloorLog`.
|
|
1742
|
+
*/
|
|
1743
|
+
timestampFloorLog;
|
|
1744
|
+
/**
|
|
1668
1745
|
* Whether this store's column family participates in the VerificationTable.
|
|
1669
1746
|
*/
|
|
1670
1747
|
verificationTable;
|
|
@@ -1723,6 +1800,7 @@ var Store = class {
|
|
|
1723
1800
|
this.transactionLogMaxSize = options?.transactionLogMaxSize;
|
|
1724
1801
|
this.transactionLogRetention = options?.transactionLogRetention;
|
|
1725
1802
|
this.transactionLogsPath = options?.transactionLogsPath;
|
|
1803
|
+
this.timestampFloorLog = options?.timestampFloorLog;
|
|
1726
1804
|
this.verificationTable = options?.verificationTable;
|
|
1727
1805
|
this.writeBufferSize = options?.writeBufferSize;
|
|
1728
1806
|
this.writeKey = writeKey;
|
|
@@ -2087,6 +2165,7 @@ var Store = class {
|
|
|
2087
2165
|
transactionLogMaxSize: this.transactionLogMaxSize,
|
|
2088
2166
|
transactionLogRetentionMs: this.transactionLogRetention ? parseDuration(this.transactionLogRetention) : void 0,
|
|
2089
2167
|
transactionLogsPath: this.transactionLogsPath,
|
|
2168
|
+
timestampFloorLog: this.timestampFloorLog,
|
|
2090
2169
|
verificationTable: this.verificationTable,
|
|
2091
2170
|
writeBufferSize: this.writeBufferSize
|
|
2092
2171
|
});
|
|
@@ -2094,12 +2173,12 @@ var Store = class {
|
|
|
2094
2173
|
return false;
|
|
2095
2174
|
}
|
|
2096
2175
|
putSync(context, key, value, options) {
|
|
2097
|
-
if (!this.db.opened) throw new Error("Database not open");
|
|
2176
|
+
if (!this.db.opened) throw new Error(this.db.closing ? "Database is closing" : "Database not open");
|
|
2098
2177
|
const valueBuffer = this.encodeValue(value);
|
|
2099
2178
|
context.putSync(this.encodeKey(key), valueBuffer, this.getTxnId(options));
|
|
2100
2179
|
}
|
|
2101
2180
|
removeSync(context, key, options) {
|
|
2102
|
-
if (!this.db.opened) throw new Error("Database not open");
|
|
2181
|
+
if (!this.db.opened) throw new Error(this.db.closing ? "Database is closing" : "Database not open");
|
|
2103
2182
|
context.removeSync(this.encodeKey(key), this.getTxnId(options));
|
|
2104
2183
|
}
|
|
2105
2184
|
/**
|
|
@@ -2568,6 +2647,66 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2568
2647
|
return this.store.db.getCompression();
|
|
2569
2648
|
}
|
|
2570
2649
|
/**
|
|
2650
|
+
* Dynamically changes the compression algorithm (and optional level) for
|
|
2651
|
+
* this database's column family on an already-open database — no close, no
|
|
2652
|
+
* reopen, and it does not conflict with another handle that already has
|
|
2653
|
+
* this column family open (unlike an explicit open-time `compression`
|
|
2654
|
+
* mismatch). That other handle's live options change too, silently,
|
|
2655
|
+
* without notice; a later explicit reopen of it is compared against the
|
|
2656
|
+
* new value, so a stale request it still holds can then conflict. Backed
|
|
2657
|
+
* by RocksDB's `DB::SetOptions()`, which documents both `compression` and
|
|
2658
|
+
* `blob_compression_type` as mutable column family options.
|
|
2659
|
+
*
|
|
2660
|
+
* This governs only *newly written* files (the next flush and any future
|
|
2661
|
+
* compaction output) going forward; SST and blob files already on disk
|
|
2662
|
+
* keep their existing compression until they are rewritten by a later
|
|
2663
|
+
* compaction. `compact()`'s default options can skip already-bottommost
|
|
2664
|
+
* files; use `compact({ bottommost: true })` to rewrite them under the new
|
|
2665
|
+
* codec. This is the live-mutation counterpart to the open-time
|
|
2666
|
+
* `compression` option — see the README's Compression section for when to
|
|
2667
|
+
* use each.
|
|
2668
|
+
*
|
|
2669
|
+
* @example
|
|
2670
|
+
* ```typescript
|
|
2671
|
+
* const db = RocksDatabase.open('/path/to/db', { compression: 'none' });
|
|
2672
|
+
* db.setCompression({ algorithm: 'zstd', level: 19 });
|
|
2673
|
+
* // new writes are now compressed with zstd; existing SSTs are untouched
|
|
2674
|
+
* // until compacted.
|
|
2675
|
+
* ```
|
|
2676
|
+
*/
|
|
2677
|
+
setCompression(compression) {
|
|
2678
|
+
const { compression: algorithm, compressionLevel } = normalizeCompression(compression);
|
|
2679
|
+
if (algorithm === void 0) throw new TypeError("setCompression requires a compression algorithm");
|
|
2680
|
+
try {
|
|
2681
|
+
this.store.db.setCompression(algorithm, compressionLevel);
|
|
2682
|
+
} catch (error) {
|
|
2683
|
+
if (error?.appliedInMemoryOnly) this.syncStoreCompressionFromLive();
|
|
2684
|
+
throw error;
|
|
2685
|
+
}
|
|
2686
|
+
this.syncStoreCompressionFromLive();
|
|
2687
|
+
}
|
|
2688
|
+
/**
|
|
2689
|
+
* Resyncs the store's cached open-time compression option from the live
|
|
2690
|
+
* database state after `setCompression()`. Also clears
|
|
2691
|
+
* `compressionForAllColumnFamilies`: that flag means "apply this option to
|
|
2692
|
+
* every column family opened for this database", which no longer describes
|
|
2693
|
+
* a live change scoped to a single column family — leaving it set would
|
|
2694
|
+
* restamp every other family with this algorithm on the next
|
|
2695
|
+
* same-instance `close()` + `open()`.
|
|
2696
|
+
*
|
|
2697
|
+
* The flag clear is unconditional (a plain assignment, so it cannot itself
|
|
2698
|
+
* fail) and comes first: a concurrent close racing the live-state read
|
|
2699
|
+
* below throws "Database not open" from the getter, and that read failing
|
|
2700
|
+
* must not also skip the flag clear -- otherwise the same restamp this
|
|
2701
|
+
* method exists to prevent survives on the next open().
|
|
2702
|
+
*/
|
|
2703
|
+
syncStoreCompressionFromLive() {
|
|
2704
|
+
this.store.compressionForAllColumnFamilies = false;
|
|
2705
|
+
try {
|
|
2706
|
+
this.store.compression = this.compression;
|
|
2707
|
+
} catch {}
|
|
2708
|
+
}
|
|
2709
|
+
/**
|
|
2571
2710
|
* The informational-log settings currently in effect for this database,
|
|
2572
2711
|
* read live from RocksDB, as `{ maxLogFileSize, infoLogLevel }`. These are
|
|
2573
2712
|
* database-wide settings (not per-column-family): `maxLogFileSize` bounds
|
|
@@ -2603,7 +2742,10 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2603
2742
|
* native binding use namespaced keys (e.g. `'transactionLog:warning'`).
|
|
2604
2743
|
*
|
|
2605
2744
|
* Listeners are not tied to any specific database — they fire for every
|
|
2606
|
-
* matching event emitted in this process.
|
|
2745
|
+
* matching event emitted in this process. Native lifecycle failures use
|
|
2746
|
+
* `'database:closeFailed'` with `(path, error)` string arguments. The event
|
|
2747
|
+
* reports both completed and incomplete teardowns; an incomplete close can
|
|
2748
|
+
* be retried by `shutdown()` or explicitly deleted by `destroy()`.
|
|
2607
2749
|
*
|
|
2608
2750
|
* @example
|
|
2609
2751
|
* ```typescript
|
|
@@ -2687,7 +2829,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2687
2829
|
this.store.db.catchUpWithPrimarySync();
|
|
2688
2830
|
}
|
|
2689
2831
|
destroy() {
|
|
2690
|
-
this.store.db.destroy();
|
|
2832
|
+
this.store.db.destroy(this.store.readOnly);
|
|
2691
2833
|
}
|
|
2692
2834
|
async drop() {
|
|
2693
2835
|
return new Promise((resolve, reject) => {
|
|
@@ -3553,8 +3695,8 @@ function frameFits(dataView, pos, dataEnd) {
|
|
|
3553
3695
|
* terminator, let a chain "end" anywhere in megabytes of padding. Against the written extent the
|
|
3554
3696
|
* end is a single offset, so a chain landing on it is ~1/2^32 to be coincidence.
|
|
3555
3697
|
*
|
|
3556
|
-
* The JS counterpart of `
|
|
3557
|
-
*
|
|
3698
|
+
* The JS counterpart of `findFramingResumeOffset()` in `transaction_log_recovery.cpp`, which the
|
|
3699
|
+
* open-time scan and the timestamp index use to keep walking past the same break.
|
|
3558
3700
|
*/
|
|
3559
3701
|
function findResyncPosition(dataView, from, dataEnd) {
|
|
3560
3702
|
const lastStart = dataEnd - TRANSACTION_LOG_ENTRY_HEADER_SIZE;
|
|
@@ -3568,18 +3710,11 @@ function findResyncPosition(dataView, from, dataEnd) {
|
|
|
3568
3710
|
}
|
|
3569
3711
|
}
|
|
3570
3712
|
/**
|
|
3571
|
-
* Builds the report for a broken frame, with the offset iteration should continue from.
|
|
3572
|
-
* the written extent here rather than per frame: `getLogFileSize` crosses into native and takes
|
|
3573
|
-
* the store mutex, which a healthy read must never pay.
|
|
3713
|
+
* Builds the report for a broken frame, with the offset iteration should continue from.
|
|
3574
3714
|
*/
|
|
3575
3715
|
function corruptFrame(message, transactionLog, dataView, logBuffer, position, limit, size, readUncommitted) {
|
|
3576
3716
|
let dataEnd = limit;
|
|
3577
|
-
if (readUncommitted)
|
|
3578
|
-
const writtenExtent = transactionLog.getLogFileSize(logBuffer.logId);
|
|
3579
|
-
dataEnd = writtenExtent > 0 ? Math.min(logBuffer.length, writtenExtent) : 0;
|
|
3580
|
-
} catch {
|
|
3581
|
-
dataEnd = 0;
|
|
3582
|
-
}
|
|
3717
|
+
if (readUncommitted) dataEnd = Math.min(logBuffer.length, logBuffer.size ?? readableExtent(logBuffer));
|
|
3583
3718
|
const resyncPosition = findResyncPosition(dataView, position + 1, dataEnd);
|
|
3584
3719
|
return {
|
|
3585
3720
|
error: new CorruptFrameError(message, logBuffer.logId, position, resyncPosition),
|
|
@@ -3609,7 +3744,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3609
3744
|
let logId = latestLogId;
|
|
3610
3745
|
let position = 0;
|
|
3611
3746
|
let dataView;
|
|
3612
|
-
let logBuffer = this._currentLogBuffer;
|
|
3747
|
+
let logBuffer = this._currentLogBuffer?.deref();
|
|
3613
3748
|
let foundExactStart = false;
|
|
3614
3749
|
if (start === void 0 && !startFromLastFlushed) {
|
|
3615
3750
|
position = size;
|
|
@@ -3627,7 +3762,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3627
3762
|
}
|
|
3628
3763
|
if (logBuffer === void 0 || logBuffer.logId !== logId) {
|
|
3629
3764
|
logBuffer = getLogMemoryMap(this, logId);
|
|
3630
|
-
if (logBuffer && latestLogId === logId && !readUncommitted) this._currentLogBuffer = logBuffer;
|
|
3765
|
+
if (logBuffer && latestLogId === logId && !readUncommitted && this._currentLogBuffer?.deref() !== logBuffer) this._currentLogBuffer = new WeakRef(logBuffer);
|
|
3631
3766
|
if (logBuffer === void 0) {
|
|
3632
3767
|
logBuffer = Buffer.alloc(0);
|
|
3633
3768
|
logBuffer.logId = 0;
|
|
@@ -3637,9 +3772,11 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3637
3772
|
}
|
|
3638
3773
|
}
|
|
3639
3774
|
dataView = logBuffer.dataView;
|
|
3640
|
-
if (latestLogId
|
|
3641
|
-
|
|
3642
|
-
|
|
3775
|
+
if (latestLogId < logId) size = 0;
|
|
3776
|
+
else if (latestLogId > logId) {
|
|
3777
|
+
const cachedSize = logBuffer.size;
|
|
3778
|
+
if (cachedSize === void 0) size = logBuffer.size = readableExtent(logBuffer);
|
|
3779
|
+
else size = cachedSize;
|
|
3643
3780
|
}
|
|
3644
3781
|
return {
|
|
3645
3782
|
[Symbol.iterator]() {
|
|
@@ -3649,15 +3786,15 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3649
3786
|
let timestamp;
|
|
3650
3787
|
if (position >= size) {
|
|
3651
3788
|
const { logId: latestLogId, size: latestSize } = loadLastPosition(transactionLog, !!readUncommitted);
|
|
3652
|
-
size = latestSize;
|
|
3789
|
+
size = latestLogId < logBuffer.logId ? 0 : latestSize;
|
|
3653
3790
|
if (latestLogId > logBuffer.logId) {
|
|
3654
|
-
size = logBuffer.size ?? (logBuffer.size =
|
|
3791
|
+
size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
3655
3792
|
if (position >= size) {
|
|
3656
|
-
const nextLogBuffer =
|
|
3793
|
+
const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
|
|
3657
3794
|
if (nextLogBuffer) {
|
|
3658
3795
|
dataView = nextLogBuffer.dataView;
|
|
3659
3796
|
logBuffer = nextLogBuffer;
|
|
3660
|
-
if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size =
|
|
3797
|
+
if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
3661
3798
|
else size = latestSize;
|
|
3662
3799
|
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
3663
3800
|
}
|
|
@@ -3708,19 +3845,25 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3708
3845
|
};
|
|
3709
3846
|
if (position >= size) {
|
|
3710
3847
|
const { logId: latestLogId, size: latestSize } = loadLastPosition(transactionLog, !!readUncommitted);
|
|
3711
|
-
size = latestSize;
|
|
3848
|
+
size = latestLogId < logBuffer.logId ? 0 : latestSize;
|
|
3712
3849
|
if (latestLogId > logBuffer.logId) {
|
|
3713
|
-
|
|
3850
|
+
size = logBuffer.size ?? readableExtent(logBuffer);
|
|
3851
|
+
if (!readUncommitted) logBuffer.size = size;
|
|
3852
|
+
if (position < size) continue;
|
|
3853
|
+
const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
|
|
3714
3854
|
if (!nextLogBuffer) return {
|
|
3715
3855
|
done: true,
|
|
3716
3856
|
value: void 0
|
|
3717
3857
|
};
|
|
3718
3858
|
logBuffer = nextLogBuffer;
|
|
3719
3859
|
dataView = logBuffer.dataView;
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
if (
|
|
3860
|
+
if (latestLogId === logBuffer.logId) size = latestSize;
|
|
3861
|
+
else {
|
|
3862
|
+
const cachedSize = logBuffer.size;
|
|
3863
|
+
if (cachedSize === void 0) {
|
|
3864
|
+
size = readableExtent(logBuffer);
|
|
3865
|
+
if (!readUncommitted) logBuffer.size = size;
|
|
3866
|
+
} else size = cachedSize;
|
|
3724
3867
|
}
|
|
3725
3868
|
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
3726
3869
|
}
|
|
@@ -3733,6 +3876,35 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3733
3876
|
}
|
|
3734
3877
|
};
|
|
3735
3878
|
} });
|
|
3879
|
+
/**
|
|
3880
|
+
* Maps the next readable segment after `fromLogId`, skipping a run retention has deleted —
|
|
3881
|
+
* stopping at the hole wedges the iterator there for every later poll (invariant 30).
|
|
3882
|
+
*
|
|
3883
|
+
* The jump target is `_nextLogId()`, the successor over the store's registered segments — *not*
|
|
3884
|
+
* `_findPosition(0)`, which names the bottom of the contiguous run ending at the current segment
|
|
3885
|
+
* and so lands *past* a survivor sitting between two holes.
|
|
3886
|
+
*/
|
|
3887
|
+
function nextReadableLogBuffer(transactionLog, fromLogId, latestLogId) {
|
|
3888
|
+
let candidateLogId = fromLogId + 1;
|
|
3889
|
+
while (candidateLogId <= latestLogId) {
|
|
3890
|
+
const logBuffer = getLogMemoryMap(transactionLog, candidateLogId);
|
|
3891
|
+
if (logBuffer) {
|
|
3892
|
+
if (candidateLogId === latestLogId || readableExtent(logBuffer) > TRANSACTION_LOG_FILE_HEADER_SIZE) return logBuffer;
|
|
3893
|
+
} else if (transactionLog.getLogFileSize(candidateLogId) > TRANSACTION_LOG_FILE_HEADER_SIZE) return;
|
|
3894
|
+
candidateLogId = transactionLog._nextLogId(candidateLogId);
|
|
3895
|
+
if (candidateLogId === 0) return;
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
/**
|
|
3899
|
+
* The bound for reading `logBuffer`. Native advances this append-owned extent only after a
|
|
3900
|
+
* successful write. It remains attached to the mapping after purge forgets the file object, so
|
|
3901
|
+
* readers retain committed history without interpreting a partial failed append as another frame.
|
|
3902
|
+
*/
|
|
3903
|
+
function readableExtent(logBuffer) {
|
|
3904
|
+
const extent = logBuffer.readableExtent;
|
|
3905
|
+
if (extent === void 0) throw new Error(`Transaction log buffer ${logBuffer.logId} has no readableExtent`);
|
|
3906
|
+
return Math.min(logBuffer.length, extent);
|
|
3907
|
+
}
|
|
3736
3908
|
function getLogMemoryMap(transactionLog, logId) {
|
|
3737
3909
|
if (logId <= 0) return;
|
|
3738
3910
|
let logBuffer = transactionLog._logBuffers.get(logId)?.deref();
|
|
@@ -3778,9 +3950,9 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
3778
3950
|
//#region src/index.ts
|
|
3779
3951
|
const versions = {
|
|
3780
3952
|
rocksdb: version,
|
|
3781
|
-
"rocksdb-js": "2.
|
|
3953
|
+
"rocksdb-js": "2.11.0"
|
|
3782
3954
|
};
|
|
3783
3955
|
|
|
3784
3956
|
//#endregion
|
|
3785
|
-
export { BackgroundError, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
|
|
3957
|
+
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 };
|
|
3786
3958
|
//# sourceMappingURL=index.mjs.map
|