@harperfast/rocksdb-js 2.13.0 → 2.14.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 +246 -42
- package/dist/index.cjs +307 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -20
- package/dist/index.d.mts +109 -20
- package/dist/index.mjs +306 -125
- package/dist/index.mjs.map +1 -1
- package/docs/performance/transaction-state-2026-11-09.csv +28 -0
- package/docs/performance/transaction-state.md +67 -0
- package/docs/transaction-log.md +34 -3
- package/package.json +16 -16
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { execSync } from "node:child_process";
|
|
|
3
3
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { isMainThread } from "node:worker_threads";
|
|
6
7
|
import { access, cp, mkdir, readdir, rm } from "node:fs/promises";
|
|
7
8
|
import * as orderedBinary from "ordered-binary";
|
|
8
9
|
import { ExtendedIterable } from "@harperfast/extended-iterable";
|
|
@@ -54,6 +55,18 @@ function locateBinding() {
|
|
|
54
55
|
throw new Error("Unable to locate rocksdb-js native binding");
|
|
55
56
|
}
|
|
56
57
|
const binding = req(locateBinding());
|
|
58
|
+
if (isMainThread) {
|
|
59
|
+
let exitShutdownRan = false;
|
|
60
|
+
const shutdownOnExit = () => {
|
|
61
|
+
if (exitShutdownRan) return;
|
|
62
|
+
exitShutdownRan = true;
|
|
63
|
+
if (!binding.shutdownOnExit() && !process.exitCode) process.exitCode = 1;
|
|
64
|
+
};
|
|
65
|
+
process.on("exit", shutdownOnExit);
|
|
66
|
+
process.on("uncaughtExceptionMonitor", () => {
|
|
67
|
+
if (process._exiting) shutdownOnExit();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
57
70
|
/**
|
|
58
71
|
* The native `BackgroundError` constructor (a real `Error` subclass). Exported
|
|
59
72
|
* as both a value — for `err instanceof BackgroundError` — and, via declaration
|
|
@@ -116,7 +129,7 @@ const getRegistryStatus = binding.registryStatus;
|
|
|
116
129
|
*/
|
|
117
130
|
const registryStatus = getRegistryStatus;
|
|
118
131
|
/**
|
|
119
|
-
* Flushes every open database and
|
|
132
|
+
* Flushes every open database and cancels its outstanding compactions, then
|
|
120
133
|
* releases the registry.
|
|
121
134
|
*
|
|
122
135
|
* **Throws** the first failure it encounters: a close whose native teardown
|
|
@@ -126,19 +139,10 @@ const registryStatus = getRegistryStatus;
|
|
|
126
139
|
* `database:closeFailed`; a timeout has no event, so the throw is its only
|
|
127
140
|
* signal.
|
|
128
141
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* ```typescript
|
|
134
|
-
* process.on('exit', () => {
|
|
135
|
-
* try {
|
|
136
|
-
* shutdown();
|
|
137
|
-
* } catch (error) {
|
|
138
|
-
* console.error('rocksdb-js shutdown failed', error);
|
|
139
|
-
* }
|
|
140
|
-
* });
|
|
141
|
-
* ```
|
|
142
|
+
* You do not need to call it at exit: when rocksdb-js loads on the main
|
|
143
|
+
* thread it registers a `process.on('exit')` listener that does the same and
|
|
144
|
+
* reports failures to stderr and the exit code instead of throwing (see the
|
|
145
|
+
* `shutdownOnExit` config option). Calling it yourself as well is safe.
|
|
142
146
|
*/
|
|
143
147
|
const shutdown = binding.shutdown;
|
|
144
148
|
const currentThreadId = binding.currentThreadId;
|
|
@@ -158,6 +162,30 @@ const currentThreadId = binding.currentThreadId;
|
|
|
158
162
|
*/
|
|
159
163
|
const steadyClockNow = binding.steadyClockNow;
|
|
160
164
|
/**
|
|
165
|
+
* Raises the process-wide monotonic timestamp clock — the one behind
|
|
166
|
+
* `db.getMonotonicTimestamp()`, every transaction's initial timestamp and so
|
|
167
|
+
* every transaction-log batch key — to at least `timestamp` (Unix epoch
|
|
168
|
+
* milliseconds). Every timestamp the process issues after the call is strictly
|
|
169
|
+
* greater than `timestamp`, across every database and `worker_threads` worker.
|
|
170
|
+
* Timestamps already issued and timestamps adopted with `txn.setTimestamp()`
|
|
171
|
+
* are unaffected. No open database is needed.
|
|
172
|
+
*
|
|
173
|
+
* Use it to carry a floor the caller persisted across a restart, which a
|
|
174
|
+
* backward wall-clock step would otherwise let the new process reissue. The
|
|
175
|
+
* floor is in memory only and is not a fence: restore it before the process
|
|
176
|
+
* starts any transaction it originates, and pass only floors this node
|
|
177
|
+
* persisted itself, since the clock stays that far ahead until wall time
|
|
178
|
+
* catches up.
|
|
179
|
+
*
|
|
180
|
+
* @returns `true` if the clock was raised: `timestamp` was ahead of both the
|
|
181
|
+
* wall clock and every timestamp already issued. `false` if the clock was
|
|
182
|
+
* already at or above it.
|
|
183
|
+
* @throws {TypeError} If `timestamp` is not a number.
|
|
184
|
+
* @throws {RangeError} If `timestamp` is not finite, is not greater than 0, is
|
|
185
|
+
* not less than 8.64e15, or is more than ten years ahead of the wall clock.
|
|
186
|
+
*/
|
|
187
|
+
const raiseTimestampFloor = binding.raiseTimestampFloor;
|
|
188
|
+
/**
|
|
161
189
|
* Advises the kernel that the file-backed pages of every mapped transaction log
|
|
162
190
|
* are cold (Linux MADV_COLD), so they are reclaimed first under memory pressure
|
|
163
191
|
* without being freed — useful during replication catch-up, where a full read of
|
|
@@ -1796,6 +1824,10 @@ var Store = class {
|
|
|
1796
1824
|
*/
|
|
1797
1825
|
arenaBlockSize;
|
|
1798
1826
|
/**
|
|
1827
|
+
* Bits per key of the whole-key filter in each SST; `0` disables filters.
|
|
1828
|
+
*/
|
|
1829
|
+
bloomFilterBitsPerKey;
|
|
1830
|
+
/**
|
|
1799
1831
|
* The function used to encode keys using the shared `keyBuffer`.
|
|
1800
1832
|
*/
|
|
1801
1833
|
writeKey;
|
|
@@ -1849,6 +1881,7 @@ var Store = class {
|
|
|
1849
1881
|
this.verificationTable = options?.verificationTable;
|
|
1850
1882
|
this.writeBufferSize = options?.writeBufferSize;
|
|
1851
1883
|
this.arenaBlockSize = options?.arenaBlockSize;
|
|
1884
|
+
this.bloomFilterBitsPerKey = options?.bloomFilterBitsPerKey;
|
|
1852
1885
|
this.writeKey = writeKey;
|
|
1853
1886
|
this.#columnFamilyOptions = options ? { ...options } : void 0;
|
|
1854
1887
|
}
|
|
@@ -2008,7 +2041,7 @@ var Store = class {
|
|
|
2008
2041
|
const result = context.getSync(keyParam, flags | ONLY_IF_IN_MEMORY_CACHE_FLAG, txnId, expectedVersion);
|
|
2009
2042
|
if (typeof result === "number") {
|
|
2010
2043
|
if (result === NOT_IN_MEMORY_CACHE_FLAG) return new Promise((resolve, reject) => {
|
|
2011
|
-
context.get(keyParam, resolve, reject, txnId, expectedVersion);
|
|
2044
|
+
context.get(keyParam, resolve, reject, txnId, expectedVersion, flags);
|
|
2012
2045
|
});
|
|
2013
2046
|
if (result === FRESH_VERSION_FLAG) return result;
|
|
2014
2047
|
VALUE_BUFFER.end = result;
|
|
@@ -2216,7 +2249,8 @@ var Store = class {
|
|
|
2216
2249
|
timestampFloorLog: this.timestampFloorLog,
|
|
2217
2250
|
verificationTable: this.verificationTable,
|
|
2218
2251
|
writeBufferSize: this.writeBufferSize,
|
|
2219
|
-
arenaBlockSize: this.arenaBlockSize
|
|
2252
|
+
arenaBlockSize: this.arenaBlockSize,
|
|
2253
|
+
bloomFilterBitsPerKey: this.bloomFilterBitsPerKey
|
|
2220
2254
|
});
|
|
2221
2255
|
this.identityPath = this.db.identityPath;
|
|
2222
2256
|
return false;
|
|
@@ -3772,6 +3806,40 @@ function parseTransactionLog(path, options = {}) {
|
|
|
3772
3806
|
const FLOAT_TO_UINT32 = /* @__PURE__ */ new Float64Array(1);
|
|
3773
3807
|
const UINT32_FROM_FLOAT = new Uint32Array(FLOAT_TO_UINT32.buffer);
|
|
3774
3808
|
const EXACT_START_SPAN = /* @__PURE__ */ new Float64Array(3);
|
|
3809
|
+
const ITERATOR_TOKEN = Symbol("transactionLogIterator");
|
|
3810
|
+
const logEquals = TransactionLog.prototype.equals;
|
|
3811
|
+
var LogIterator = class {
|
|
3812
|
+
#sourceLog;
|
|
3813
|
+
#control;
|
|
3814
|
+
next;
|
|
3815
|
+
constructor(token, sourceLog, next, control) {
|
|
3816
|
+
if (token !== ITERATOR_TOKEN) throw new TypeError("Transaction log iterators require query()");
|
|
3817
|
+
this.#sourceLog = sourceLog;
|
|
3818
|
+
this.next = next;
|
|
3819
|
+
this.#control = control;
|
|
3820
|
+
}
|
|
3821
|
+
static cursor(value) {
|
|
3822
|
+
if (typeof value === "object" && value !== null && #control in value) return value;
|
|
3823
|
+
}
|
|
3824
|
+
static isFrom(value, sourceLog) {
|
|
3825
|
+
return logEquals.call(sourceLog, value.#sourceLog);
|
|
3826
|
+
}
|
|
3827
|
+
[Symbol.iterator]() {
|
|
3828
|
+
return this;
|
|
3829
|
+
}
|
|
3830
|
+
peek() {
|
|
3831
|
+
return this.#control("peek");
|
|
3832
|
+
}
|
|
3833
|
+
close() {
|
|
3834
|
+
this.#control("close");
|
|
3835
|
+
}
|
|
3836
|
+
static reached(value, logId, position, atSegmentEnd) {
|
|
3837
|
+
return value.#control("reached", logId, position, atSegmentEnd);
|
|
3838
|
+
}
|
|
3839
|
+
static scanEnd(value, logId, size) {
|
|
3840
|
+
return value.#control("scanEnd", logId, size);
|
|
3841
|
+
}
|
|
3842
|
+
};
|
|
3775
3843
|
const { TRANSACTION_LOG_FILE_HEADER_SIZE, TRANSACTION_LOG_ENTRY_HEADER_SIZE, TRANSACTION_LOG_ENTRY_LAST_FLAG, TRANSACTION_LOG_ENTRY_ABORTED_FLAG } = constants;
|
|
3776
3844
|
/**
|
|
3777
3845
|
* Number of consecutive well-formed frames that, on their own, signal that real log data has
|
|
@@ -3833,6 +3901,17 @@ var TransactionLogGapError = class extends Error {
|
|
|
3833
3901
|
this.unavailableLogCount = unavailableLogCount;
|
|
3834
3902
|
}
|
|
3835
3903
|
};
|
|
3904
|
+
/** Readable input ended before the iterator's physical `until` bound. Retry after input is available. */
|
|
3905
|
+
var TransactionLogUntilUnreachedError = class extends Error {
|
|
3906
|
+
logId;
|
|
3907
|
+
position;
|
|
3908
|
+
constructor(logId, position) {
|
|
3909
|
+
super(`Transaction log reader stopped at position ${position.toString(16)} of log ${logId}, short of the iterator it reads until`);
|
|
3910
|
+
this.name = "TransactionLogUntilUnreachedError";
|
|
3911
|
+
this.logId = logId;
|
|
3912
|
+
this.position = position;
|
|
3913
|
+
}
|
|
3914
|
+
};
|
|
3836
3915
|
/**
|
|
3837
3916
|
* Whether a complete, in-bounds frame begins at `pos`. The only sane bound on an entry's length is
|
|
3838
3917
|
* the written extent: a single entry can legitimately exceed the rotation threshold (the first
|
|
@@ -3895,7 +3974,9 @@ function corruptFrame(message, transactionLog, dataView, logBuffer, position, li
|
|
|
3895
3974
|
* exactly matches the start timestamp, and then return all subsequent transactions in the log
|
|
3896
3975
|
* regardless of whether their timestamp is before or after the start
|
|
3897
3976
|
*/
|
|
3898
|
-
Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, exactStart, startFromLastFlushed, readUncommitted, exclusiveStart } = {}) {
|
|
3977
|
+
Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, exactStart, startFromLastFlushed, readUncommitted, exclusiveStart, until } = {}) {
|
|
3978
|
+
let untilCursor = LogIterator.cursor(until);
|
|
3979
|
+
if (until !== void 0 && (!untilCursor || !LogIterator.isFrom(untilCursor, this))) throw new TypeError("until must be an iterator from query() on the same transaction log");
|
|
3899
3980
|
if (!this._lastCommittedPosition) {
|
|
3900
3981
|
const lastCommittedPosition = this._getLastCommittedPosition();
|
|
3901
3982
|
this._lastCommittedPosition = new Float64Array(lastCommittedPosition.buffer);
|
|
@@ -3942,7 +4023,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3942
4023
|
if (position === 0) position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
3943
4024
|
}
|
|
3944
4025
|
const positionedAt = position;
|
|
3945
|
-
if (stopLogId !== 0 && pastStop(logId, position, stopLogId, stopPosition)) {
|
|
4026
|
+
if (stopLogId !== 0 && pastStop(logId, position, stopLogId, stopPosition) && mayJumpTo(untilCursor, tailLogId, tailPosition)) {
|
|
3946
4027
|
stopLogId = 0;
|
|
3947
4028
|
if (skipToTail(this, logId, position, tailLogId, tailPosition, segmentRemovals)) {
|
|
3948
4029
|
logId = tailLogId;
|
|
@@ -3952,95 +4033,144 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3952
4033
|
tailLogBuffer = void 0;
|
|
3953
4034
|
}
|
|
3954
4035
|
let gapFloor = logId;
|
|
4036
|
+
let startUnmapped = false;
|
|
4037
|
+
let startMissing = false;
|
|
3955
4038
|
if (logBuffer === void 0 || logBuffer.logId !== logId) {
|
|
3956
4039
|
logBuffer = getLogMemoryMap(this, logId);
|
|
3957
4040
|
if (logBuffer && latestLogId === logId && !readUncommitted && this._currentLogBuffer?.deref() !== logBuffer) this._currentLogBuffer = new WeakRef(logBuffer);
|
|
3958
4041
|
if (logBuffer === void 0) {
|
|
3959
4042
|
logBuffer = Buffer.alloc(0);
|
|
3960
|
-
logBuffer.logId =
|
|
4043
|
+
logBuffer.logId = logId;
|
|
3961
4044
|
logBuffer.size = 0;
|
|
3962
4045
|
logBuffer.dataView = new DataView(logBuffer.buffer);
|
|
3963
4046
|
size = 0;
|
|
4047
|
+
startUnmapped = logId > 0;
|
|
3964
4048
|
if (positionedAt <= TRANSACTION_LOG_FILE_HEADER_SIZE || logId > 0 && this.getLogFileSize(logId) > 0) gapFloor = logId - 1;
|
|
3965
4049
|
}
|
|
3966
4050
|
}
|
|
3967
4051
|
dataView = logBuffer.dataView;
|
|
3968
4052
|
if (latestLogId < logId) size = 0;
|
|
3969
|
-
else if (latestLogId > logId) {
|
|
4053
|
+
else if (latestLogId > logId && !startUnmapped) {
|
|
3970
4054
|
const cachedSize = logBuffer.size;
|
|
3971
4055
|
if (cachedSize === void 0) size = logBuffer.size = readableExtent(logBuffer);
|
|
3972
4056
|
else size = cachedSize;
|
|
3973
4057
|
}
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
|
|
3988
|
-
if (nextLogBuffer) {
|
|
3989
|
-
const unavailable = unavailableLogCount;
|
|
3990
|
-
dataView = nextLogBuffer.dataView;
|
|
3991
|
-
logBuffer = nextLogBuffer;
|
|
3992
|
-
if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
3993
|
-
else size = latestSize;
|
|
3994
|
-
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
3995
|
-
previousEndedTransaction = true;
|
|
3996
|
-
if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
|
|
3997
|
-
}
|
|
3998
|
-
}
|
|
4058
|
+
let peeked;
|
|
4059
|
+
let peekedLogId = 0;
|
|
4060
|
+
let peekedPosition = 0;
|
|
4061
|
+
let closed = false;
|
|
4062
|
+
let needsCursorCheck = untilCursor !== void 0 || startUnmapped;
|
|
4063
|
+
const iterator = new LogIterator(ITERATOR_TOKEN, transactionLog, function next() {
|
|
4064
|
+
let scanEnd = size;
|
|
4065
|
+
if (needsCursorCheck) {
|
|
4066
|
+
if (peeked) {
|
|
4067
|
+
const result = peeked;
|
|
4068
|
+
if (!closed) {
|
|
4069
|
+
peeked = void 0;
|
|
4070
|
+
needsCursorCheck = untilCursor !== void 0;
|
|
3999
4071
|
}
|
|
4072
|
+
return result;
|
|
4000
4073
|
}
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4074
|
+
if (untilCursor) {
|
|
4075
|
+
const limit = LogIterator.scanEnd(untilCursor, logBuffer.logId, size);
|
|
4076
|
+
if (limit === void 0) {
|
|
4077
|
+
untilCursor = void 0;
|
|
4078
|
+
needsCursorCheck = startUnmapped;
|
|
4079
|
+
} else {
|
|
4080
|
+
if (position >= limit && LogIterator.reached(untilCursor, logBuffer.logId, position, position >= size && logBuffer.length > 0 && position >= readableExtent(logBuffer))) return {
|
|
4081
|
+
done: true,
|
|
4082
|
+
value: void 0
|
|
4083
|
+
};
|
|
4084
|
+
scanEnd = limit;
|
|
4007
4085
|
}
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
const length = dataView.getUint32(position + 8);
|
|
4020
|
-
if (length === 0 || position + TRANSACTION_LOG_ENTRY_HEADER_SIZE + length > limit) {
|
|
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);
|
|
4022
|
-
position = broken.nextPosition;
|
|
4023
|
-
previousEndedTransaction = true;
|
|
4024
|
-
throw broken.error;
|
|
4086
|
+
}
|
|
4087
|
+
if (startUnmapped) {
|
|
4088
|
+
const mapped = getLogMemoryMap(transactionLog, logBuffer.logId);
|
|
4089
|
+
if (mapped) {
|
|
4090
|
+
logBuffer = mapped;
|
|
4091
|
+
dataView = mapped.dataView;
|
|
4092
|
+
} else {
|
|
4093
|
+
const latestLogId = loadLastPosition(transactionLog, !!readUncommitted).logId;
|
|
4094
|
+
const fileSize = transactionLog.getLogFileSize(logBuffer.logId);
|
|
4095
|
+
if (logBuffer.logId >= latestLogId || fileSize > position) return endOfReadable(untilCursor, logBuffer, position);
|
|
4096
|
+
if (fileSize === 0) startMissing = true;
|
|
4025
4097
|
}
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4098
|
+
startUnmapped = false;
|
|
4099
|
+
needsCursorCheck = untilCursor !== void 0;
|
|
4100
|
+
size = 0;
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
let timestamp;
|
|
4104
|
+
if (position >= size) {
|
|
4105
|
+
const { logId: latestLogId, size: latestSize } = loadLastPosition(transactionLog, !!readUncommitted);
|
|
4106
|
+
size = latestLogId < logBuffer.logId ? 0 : latestSize;
|
|
4107
|
+
if (latestLogId > logBuffer.logId) {
|
|
4108
|
+
size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
4109
|
+
if (position >= size) {
|
|
4110
|
+
if (untilCursor && LogIterator.reached(untilCursor, logBuffer.logId, position, logBuffer.length > 0 && position >= readableExtent(logBuffer))) return {
|
|
4111
|
+
done: true,
|
|
4112
|
+
value: void 0
|
|
4113
|
+
};
|
|
4114
|
+
const fromLogId = logBuffer.logId - (startMissing ? 1 : 0);
|
|
4115
|
+
const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
|
|
4116
|
+
if (nextLogBuffer) {
|
|
4117
|
+
startMissing = false;
|
|
4118
|
+
const unavailable = unavailableLogCount;
|
|
4119
|
+
dataView = nextLogBuffer.dataView;
|
|
4120
|
+
logBuffer = nextLogBuffer;
|
|
4121
|
+
if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
4122
|
+
else size = latestSize;
|
|
4123
|
+
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
4124
|
+
previousEndedTransaction = true;
|
|
4125
|
+
if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
|
|
4041
4126
|
}
|
|
4042
|
-
}
|
|
4043
|
-
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
scanEnd = untilCursor ? LogIterator.scanEnd(untilCursor, logBuffer.logId, size) ?? size : size;
|
|
4130
|
+
}
|
|
4131
|
+
if (position >= scanEnd) return endOfReadable(untilCursor, logBuffer, position);
|
|
4132
|
+
let jumpRefused = false;
|
|
4133
|
+
let limit = Math.min(size, logBuffer.length);
|
|
4134
|
+
do {
|
|
4135
|
+
try {
|
|
4136
|
+
timestamp = dataView.getFloat64(position);
|
|
4137
|
+
} catch (error) {
|
|
4138
|
+
error.message += ` at position ${position.toString(16)} of log ${logBuffer.logId} (size=${size}, log buffer length=${logBuffer.length})`;
|
|
4139
|
+
throw error;
|
|
4140
|
+
}
|
|
4141
|
+
if (!timestamp) return endOfReadable(untilCursor, logBuffer, position);
|
|
4142
|
+
if (position + TRANSACTION_LOG_ENTRY_HEADER_SIZE > limit) {
|
|
4143
|
+
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);
|
|
4144
|
+
position = broken.nextPosition;
|
|
4145
|
+
previousEndedTransaction = true;
|
|
4146
|
+
throw broken.error;
|
|
4147
|
+
}
|
|
4148
|
+
const length = dataView.getUint32(position + 8);
|
|
4149
|
+
if (length === 0 || position + TRANSACTION_LOG_ENTRY_HEADER_SIZE + length > limit) {
|
|
4150
|
+
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);
|
|
4151
|
+
position = broken.nextPosition;
|
|
4152
|
+
previousEndedTransaction = true;
|
|
4153
|
+
throw broken.error;
|
|
4154
|
+
}
|
|
4155
|
+
position += TRANSACTION_LOG_ENTRY_HEADER_SIZE;
|
|
4156
|
+
const flags = logBuffer[position - 1];
|
|
4157
|
+
const aborted = (flags & TRANSACTION_LOG_ENTRY_ABORTED_FLAG) !== 0;
|
|
4158
|
+
if (previousEndedTransaction || timestamp !== previousTimestamp || aborted) skippingAborted = aborted;
|
|
4159
|
+
previousEndedTransaction = (flags & TRANSACTION_LOG_ENTRY_LAST_FLAG) !== 0;
|
|
4160
|
+
previousTimestamp = timestamp;
|
|
4161
|
+
let matchesRange;
|
|
4162
|
+
if (skippingAborted) matchesRange = false;
|
|
4163
|
+
else if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
|
|
4164
|
+
else if (exactStart) {
|
|
4165
|
+
matchesRange = false;
|
|
4166
|
+
if (timestamp === start) {
|
|
4167
|
+
matchesRange = !exclusiveStart;
|
|
4168
|
+
foundExactStart = true;
|
|
4169
|
+
stopLogId = 0;
|
|
4170
|
+
}
|
|
4171
|
+
} else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
|
|
4172
|
+
if (!matchesRange && stopLogId !== 0 && !jumpRefused && pastStop(logBuffer.logId, position + length, stopLogId, stopPosition)) {
|
|
4173
|
+
if (mayJumpTo(untilCursor, tailLogId, tailPosition)) {
|
|
4044
4174
|
stopLogId = 0;
|
|
4045
4175
|
position += length;
|
|
4046
4176
|
if (skipToTail(transactionLog, logBuffer.logId, position, tailLogId, tailPosition, segmentRemovals)) {
|
|
@@ -4055,57 +4185,108 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
4055
4185
|
tailLogBuffer = void 0;
|
|
4056
4186
|
return iterator.next();
|
|
4057
4187
|
}
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4188
|
+
jumpRefused = true;
|
|
4189
|
+
}
|
|
4190
|
+
const entryStart = position;
|
|
4191
|
+
position += length;
|
|
4192
|
+
if (matchesRange) return {
|
|
4193
|
+
done: false,
|
|
4194
|
+
value: {
|
|
4195
|
+
timestamp,
|
|
4196
|
+
endTxn: previousEndedTransaction,
|
|
4197
|
+
data: logBuffer.subarray(entryStart, position)
|
|
4198
|
+
}
|
|
4199
|
+
};
|
|
4200
|
+
if (position >= scanEnd) {
|
|
4201
|
+
if (untilCursor && LogIterator.reached(untilCursor, logBuffer.logId, position)) return {
|
|
4202
|
+
done: true,
|
|
4203
|
+
value: void 0
|
|
4067
4204
|
};
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4205
|
+
const { logId: latestLogId, size: latestSize } = loadLastPosition(transactionLog, !!readUncommitted);
|
|
4206
|
+
size = latestLogId < logBuffer.logId ? 0 : latestSize;
|
|
4207
|
+
if (latestLogId > logBuffer.logId) {
|
|
4208
|
+
size = logBuffer.size ?? readableExtent(logBuffer);
|
|
4209
|
+
if (!readUncommitted) logBuffer.size = size;
|
|
4210
|
+
if (position < size) {
|
|
4211
|
+
scanEnd = untilCursor ? LogIterator.scanEnd(untilCursor, logBuffer.logId, size) ?? size : size;
|
|
4212
|
+
limit = Math.min(size, logBuffer.length);
|
|
4213
|
+
continue;
|
|
4214
|
+
}
|
|
4215
|
+
if (untilCursor && LogIterator.reached(untilCursor, logBuffer.logId, position, logBuffer.length > 0 && position >= readableExtent(logBuffer))) return {
|
|
4216
|
+
done: true,
|
|
4217
|
+
value: void 0
|
|
4218
|
+
};
|
|
4219
|
+
const fromLogId = logBuffer.logId;
|
|
4220
|
+
const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
|
|
4221
|
+
if (!nextLogBuffer) return endOfReadable(untilCursor, logBuffer, position);
|
|
4222
|
+
const unavailable = unavailableLogCount;
|
|
4223
|
+
logBuffer = nextLogBuffer;
|
|
4224
|
+
dataView = logBuffer.dataView;
|
|
4225
|
+
if (latestLogId === logBuffer.logId) size = latestSize;
|
|
4226
|
+
else {
|
|
4227
|
+
const cachedSize = logBuffer.size;
|
|
4228
|
+
if (cachedSize === void 0) {
|
|
4229
|
+
size = readableExtent(logBuffer);
|
|
4230
|
+
if (!readUncommitted) logBuffer.size = size;
|
|
4231
|
+
} else size = cachedSize;
|
|
4095
4232
|
}
|
|
4233
|
+
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
4234
|
+
previousEndedTransaction = true;
|
|
4235
|
+
if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
|
|
4096
4236
|
}
|
|
4237
|
+
scanEnd = untilCursor ? LogIterator.scanEnd(untilCursor, logBuffer.logId, size) ?? size : size;
|
|
4238
|
+
limit = Math.min(size, logBuffer.length);
|
|
4239
|
+
}
|
|
4240
|
+
} while (position < scanEnd);
|
|
4241
|
+
return endOfReadable(untilCursor, logBuffer, position);
|
|
4242
|
+
}, function control(operation, fromLogId, fromPosition, atSegmentEnd) {
|
|
4243
|
+
if (operation === "peek") {
|
|
4244
|
+
if (peeked) return peeked;
|
|
4245
|
+
const result = iterator.next();
|
|
4246
|
+
if (!result.done) {
|
|
4247
|
+
peeked = result;
|
|
4248
|
+
needsCursorCheck = true;
|
|
4249
|
+
peekedLogId = logBuffer.logId;
|
|
4250
|
+
peekedPosition = position - result.value.data.length - TRANSACTION_LOG_ENTRY_HEADER_SIZE;
|
|
4097
4251
|
}
|
|
4098
|
-
return
|
|
4252
|
+
return result;
|
|
4253
|
+
}
|
|
4254
|
+
if (operation === "close") {
|
|
4255
|
+
closed = true;
|
|
4256
|
+
needsCursorCheck = true;
|
|
4257
|
+
peeked = {
|
|
4099
4258
|
done: true,
|
|
4100
4259
|
value: void 0
|
|
4101
4260
|
};
|
|
4261
|
+
logBuffer = void 0;
|
|
4262
|
+
dataView = void 0;
|
|
4263
|
+
untilCursor = void 0;
|
|
4264
|
+
return;
|
|
4102
4265
|
}
|
|
4103
|
-
|
|
4266
|
+
if (closed) return;
|
|
4267
|
+
const boundLogId = peeked ? peekedLogId : logBuffer.logId;
|
|
4268
|
+
const boundPosition = peeked ? peekedPosition : position;
|
|
4269
|
+
if (operation === "scanEnd") {
|
|
4270
|
+
if (boundLogId !== fromLogId) return boundLogId > fromLogId ? fromPosition : 0;
|
|
4271
|
+
return boundPosition < fromPosition ? boundPosition : fromPosition;
|
|
4272
|
+
}
|
|
4273
|
+
return pastStop(fromLogId, fromPosition, boundLogId, boundPosition) || !!atSegmentEnd && boundLogId === fromLogId + 1 && boundPosition === TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
4274
|
+
});
|
|
4104
4275
|
return iterator;
|
|
4105
4276
|
} });
|
|
4277
|
+
function endOfReadable(untilCursor, logBuffer, position) {
|
|
4278
|
+
if (untilCursor && LogIterator.reached(untilCursor, logBuffer.logId, position, logBuffer.length > 0 && position >= readableExtent(logBuffer)) === false) throw new TransactionLogUntilUnreachedError(logBuffer.logId, position);
|
|
4279
|
+
return {
|
|
4280
|
+
done: true,
|
|
4281
|
+
value: void 0
|
|
4282
|
+
};
|
|
4283
|
+
}
|
|
4106
4284
|
function pastStop(logId, position, stopLogId, stopPosition) {
|
|
4107
4285
|
return logId > stopLogId || logId === stopLogId && position >= stopPosition;
|
|
4108
4286
|
}
|
|
4287
|
+
function mayJumpTo(untilCursor, logId, position) {
|
|
4288
|
+
return !untilCursor || LogIterator.reached(untilCursor, logId, position - 1) !== true;
|
|
4289
|
+
}
|
|
4109
4290
|
let tailLogBuffer;
|
|
4110
4291
|
/**
|
|
4111
4292
|
* Whether an exact-start lookup at `position` of `fromLogId` may take its one-shot jump to the tail.
|
|
@@ -4204,8 +4385,8 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
4204
4385
|
// src/index.ts
|
|
4205
4386
|
const versions = {
|
|
4206
4387
|
rocksdb: version,
|
|
4207
|
-
"rocksdb-js": "2.
|
|
4388
|
+
"rocksdb-js": "2.14.0"
|
|
4208
4389
|
};
|
|
4209
4390
|
|
|
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 };
|
|
4391
|
+
export { BackgroundError, ConflictKind, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionAbortUnresolvedError, TransactionLog, TransactionLogGapError, TransactionLogUntilUnreachedError, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getRegistryStatus, getWriteBufferManagerStats, parseTransactionLog, raiseTimestampFloor, registryStatus, shutdown, stats, steadyClockNow, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
|
|
4211
4392
|
//# sourceMappingURL=index.mjs.map
|