@harperfast/rocksdb-js 2.10.0 → 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 +227 -46
- package/bin/rocksdb-js.mjs +49 -5
- package/dist/index.cjs +141 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -5
- package/dist/index.d.mts +124 -5
- package/dist/index.mjs +141 -27
- package/dist/index.mjs.map +1 -1
- package/docs/stats.md +48 -47
- 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,7 +105,17 @@ 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;
|
|
109
119
|
/**
|
|
110
120
|
* Flushes every open database and waits for outstanding compactions, then
|
|
111
121
|
* releases the registry.
|
|
@@ -169,6 +179,12 @@ const coolTransactionLogs = binding.coolTransactionLogs;
|
|
|
169
179
|
*/
|
|
170
180
|
const transactionLogMapCount = binding.transactionLogMapCount;
|
|
171
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
|
+
/**
|
|
172
188
|
* Test-only: force the next `count` transaction commits to fail with TryAgain (rolled back, so
|
|
173
189
|
* no data is committed), reproducing a stranded-snapshot conflict deterministically. Pass 0 to
|
|
174
190
|
* disarm. Used by the ERR_TRY_AGAIN retry regression test.
|
|
@@ -1706,6 +1722,7 @@ var Store = class {
|
|
|
1706
1722
|
/**
|
|
1707
1723
|
* The target maximum size of a transaction log before rotation. Transactions
|
|
1708
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.
|
|
1709
1726
|
*/
|
|
1710
1727
|
transactionLogMaxSize;
|
|
1711
1728
|
/**
|
|
@@ -1720,6 +1737,11 @@ var Store = class {
|
|
|
1720
1737
|
*/
|
|
1721
1738
|
transactionLogsPath;
|
|
1722
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
|
+
/**
|
|
1723
1745
|
* Whether this store's column family participates in the VerificationTable.
|
|
1724
1746
|
*/
|
|
1725
1747
|
verificationTable;
|
|
@@ -1778,6 +1800,7 @@ var Store = class {
|
|
|
1778
1800
|
this.transactionLogMaxSize = options?.transactionLogMaxSize;
|
|
1779
1801
|
this.transactionLogRetention = options?.transactionLogRetention;
|
|
1780
1802
|
this.transactionLogsPath = options?.transactionLogsPath;
|
|
1803
|
+
this.timestampFloorLog = options?.timestampFloorLog;
|
|
1781
1804
|
this.verificationTable = options?.verificationTable;
|
|
1782
1805
|
this.writeBufferSize = options?.writeBufferSize;
|
|
1783
1806
|
this.writeKey = writeKey;
|
|
@@ -2142,6 +2165,7 @@ var Store = class {
|
|
|
2142
2165
|
transactionLogMaxSize: this.transactionLogMaxSize,
|
|
2143
2166
|
transactionLogRetentionMs: this.transactionLogRetention ? parseDuration(this.transactionLogRetention) : void 0,
|
|
2144
2167
|
transactionLogsPath: this.transactionLogsPath,
|
|
2168
|
+
timestampFloorLog: this.timestampFloorLog,
|
|
2145
2169
|
verificationTable: this.verificationTable,
|
|
2146
2170
|
writeBufferSize: this.writeBufferSize
|
|
2147
2171
|
});
|
|
@@ -2623,6 +2647,66 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
2623
2647
|
return this.store.db.getCompression();
|
|
2624
2648
|
}
|
|
2625
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
|
+
/**
|
|
2626
2710
|
* The informational-log settings currently in effect for this database,
|
|
2627
2711
|
* read live from RocksDB, as `{ maxLogFileSize, infoLogLevel }`. These are
|
|
2628
2712
|
* database-wide settings (not per-column-family): `maxLogFileSize` bounds
|
|
@@ -3626,18 +3710,11 @@ function findResyncPosition(dataView, from, dataEnd) {
|
|
|
3626
3710
|
}
|
|
3627
3711
|
}
|
|
3628
3712
|
/**
|
|
3629
|
-
* Builds the report for a broken frame, with the offset iteration should continue from.
|
|
3630
|
-
* the written extent here rather than per frame: `getLogFileSize` crosses into native and takes
|
|
3631
|
-
* 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.
|
|
3632
3714
|
*/
|
|
3633
3715
|
function corruptFrame(message, transactionLog, dataView, logBuffer, position, limit, size, readUncommitted) {
|
|
3634
3716
|
let dataEnd = limit;
|
|
3635
|
-
if (readUncommitted)
|
|
3636
|
-
const writtenExtent = transactionLog.getLogFileSize(logBuffer.logId);
|
|
3637
|
-
dataEnd = writtenExtent > 0 ? Math.min(logBuffer.length, writtenExtent) : 0;
|
|
3638
|
-
} catch {
|
|
3639
|
-
dataEnd = 0;
|
|
3640
|
-
}
|
|
3717
|
+
if (readUncommitted) dataEnd = Math.min(logBuffer.length, logBuffer.size ?? readableExtent(logBuffer));
|
|
3641
3718
|
const resyncPosition = findResyncPosition(dataView, position + 1, dataEnd);
|
|
3642
3719
|
return {
|
|
3643
3720
|
error: new CorruptFrameError(message, logBuffer.logId, position, resyncPosition),
|
|
@@ -3667,7 +3744,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3667
3744
|
let logId = latestLogId;
|
|
3668
3745
|
let position = 0;
|
|
3669
3746
|
let dataView;
|
|
3670
|
-
let logBuffer = this._currentLogBuffer;
|
|
3747
|
+
let logBuffer = this._currentLogBuffer?.deref();
|
|
3671
3748
|
let foundExactStart = false;
|
|
3672
3749
|
if (start === void 0 && !startFromLastFlushed) {
|
|
3673
3750
|
position = size;
|
|
@@ -3685,7 +3762,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3685
3762
|
}
|
|
3686
3763
|
if (logBuffer === void 0 || logBuffer.logId !== logId) {
|
|
3687
3764
|
logBuffer = getLogMemoryMap(this, logId);
|
|
3688
|
-
if (logBuffer && latestLogId === logId && !readUncommitted) this._currentLogBuffer = logBuffer;
|
|
3765
|
+
if (logBuffer && latestLogId === logId && !readUncommitted && this._currentLogBuffer?.deref() !== logBuffer) this._currentLogBuffer = new WeakRef(logBuffer);
|
|
3689
3766
|
if (logBuffer === void 0) {
|
|
3690
3767
|
logBuffer = Buffer.alloc(0);
|
|
3691
3768
|
logBuffer.logId = 0;
|
|
@@ -3695,9 +3772,11 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3695
3772
|
}
|
|
3696
3773
|
}
|
|
3697
3774
|
dataView = logBuffer.dataView;
|
|
3698
|
-
if (latestLogId
|
|
3699
|
-
|
|
3700
|
-
|
|
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;
|
|
3701
3780
|
}
|
|
3702
3781
|
return {
|
|
3703
3782
|
[Symbol.iterator]() {
|
|
@@ -3707,15 +3786,15 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3707
3786
|
let timestamp;
|
|
3708
3787
|
if (position >= size) {
|
|
3709
3788
|
const { logId: latestLogId, size: latestSize } = loadLastPosition(transactionLog, !!readUncommitted);
|
|
3710
|
-
size = latestSize;
|
|
3789
|
+
size = latestLogId < logBuffer.logId ? 0 : latestSize;
|
|
3711
3790
|
if (latestLogId > logBuffer.logId) {
|
|
3712
|
-
size = logBuffer.size ?? (logBuffer.size =
|
|
3791
|
+
size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
3713
3792
|
if (position >= size) {
|
|
3714
|
-
const nextLogBuffer =
|
|
3793
|
+
const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
|
|
3715
3794
|
if (nextLogBuffer) {
|
|
3716
3795
|
dataView = nextLogBuffer.dataView;
|
|
3717
3796
|
logBuffer = nextLogBuffer;
|
|
3718
|
-
if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size =
|
|
3797
|
+
if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
|
|
3719
3798
|
else size = latestSize;
|
|
3720
3799
|
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
3721
3800
|
}
|
|
@@ -3766,19 +3845,25 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3766
3845
|
};
|
|
3767
3846
|
if (position >= size) {
|
|
3768
3847
|
const { logId: latestLogId, size: latestSize } = loadLastPosition(transactionLog, !!readUncommitted);
|
|
3769
|
-
size = latestSize;
|
|
3848
|
+
size = latestLogId < logBuffer.logId ? 0 : latestSize;
|
|
3770
3849
|
if (latestLogId > logBuffer.logId) {
|
|
3771
|
-
|
|
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);
|
|
3772
3854
|
if (!nextLogBuffer) return {
|
|
3773
3855
|
done: true,
|
|
3774
3856
|
value: void 0
|
|
3775
3857
|
};
|
|
3776
3858
|
logBuffer = nextLogBuffer;
|
|
3777
3859
|
dataView = logBuffer.dataView;
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
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;
|
|
3782
3867
|
}
|
|
3783
3868
|
position = TRANSACTION_LOG_FILE_HEADER_SIZE;
|
|
3784
3869
|
}
|
|
@@ -3791,6 +3876,35 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
|
|
|
3791
3876
|
}
|
|
3792
3877
|
};
|
|
3793
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
|
+
}
|
|
3794
3908
|
function getLogMemoryMap(transactionLog, logId) {
|
|
3795
3909
|
if (logId <= 0) return;
|
|
3796
3910
|
let logBuffer = transactionLog._logBuffers.get(logId)?.deref();
|
|
@@ -3836,9 +3950,9 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
3836
3950
|
//#region src/index.ts
|
|
3837
3951
|
const versions = {
|
|
3838
3952
|
rocksdb: version,
|
|
3839
|
-
"rocksdb-js": "2.
|
|
3953
|
+
"rocksdb-js": "2.11.0"
|
|
3840
3954
|
};
|
|
3841
3955
|
|
|
3842
3956
|
//#endregion
|
|
3843
|
-
export { BackgroundError, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, steadyClockNow, 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 };
|
|
3844
3958
|
//# sourceMappingURL=index.mjs.map
|