Summary:
Fixes an off-by-one bug in how sequence numbers are handled when splitting range tombstones across output levels in per-key-placement (tiered) compaction. The bug was either introduced or propagated in https://github.com/facebook/rocksdb/issues/13256.
Point entries move to proximal output only when their sequence number is strictly greater than `proximal_after_seqno_`, but range tombstones were previously split using an inclusive lower bound at that same seqno. That allowed a range tombstone at the boundary to be emitted to the proximal level while point keys at the same seqno stayed in the last level, which could create overlapping files in the proximal level (caught by `force_consistency_checks` as `L<n> has overlapping ranges`).
This matters when `proximal_output_range_type_` is `kNonLastRange`: the compaction only owns the selected proximal-level input range, so existing last-level data at the split boundary must stay in the last level. In `kFullRange`, the compaction owns the relevant proximal-level range, so newer last-level data can be safely emitted to proximal output.
The fix splits range tombstones at `proximal_after_seqno_ + 1` (saturating at `kMaxSequenceNumber`), so the half-open `[lower, upper)` tombstone filter lands on the same boundary as the strict `seqno > proximal_after_seqno_` rule used for point keys.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14795
Test Plan:
New regression test `PrecludeLastLevelTestBase.RangeDelAtProximalSeqnoBoundaryStaysInLastLevel` uses a targeted manual compaction to exercise the `kNonLastRange` case directly, verifying a boundary range tombstone stays in the last level instead of widening the proximal output range.
Worked example:
```text
Initial state:
L6 (last level): Put(Key 2)s1, Put(Key 12)s2, RangeDel[Key 2, Key 12)s3
L5 (proximal): file A [Key 0 .. Key 4]s4-s5 file B [Key 5 .. Key 9]s6-s7
preclude_last_level_min_seqno is forced to 0 via sync point.
Manual CompactFiles selects only L5 file B + the L6 file (output level 6).
Only part of the proximal level is selected, so this is the kNonLastRange case:
max_last_level_seqno = 3
proximal_after_seqno_ = max(0, 3) = 3
Point keys Key 5 (s6) and Key 9 (s7): both seqno > 3 -> proximal output (L5) OK
Range tombstone s3:
Old (buggy): proximal keep range [3, MAX) includes s3, so the tombstone is
emitted to the proximal output. The output is built from L5 input
file B [Key 5 .. Key 9], but the tombstone covers [Key 2, Key 12),
so the proximal output file starts at Key 2 and spills past Key 5
into the existing, untouched L5 file A [Key 0 .. Key 4].
Two overlapping files in L5 -> Corruption.
New (fixed): split at proximal_after_seqno_ + 1 = 4.
proximal keep [4, MAX) excludes s3; last-level keep [0, 4) includes
s3 -> tombstone stays in the last level (L6). OK
```
Verification (debug build, `make -j64 tiered_compaction_test`):
- Without the fix, the test fails at the `CompactFiles` call:
```
tiered_compaction_test.cc:2940: Failure
Corruption: force_consistency_checks(DEBUG): VersionBuilder: L5 has overlapping ranges:
file https://github.com/facebook/rocksdb/issues/11 largest key: Key(4) seq:5, type:1 (Put) vs.
file https://github.com/facebook/rocksdb/issues/17 smallest key: Key(2) seq:3, type:15 (range deletion)
```
- With the fix, the test passes (range deletions absent from L5, still present in L6).
Reviewed By: pdillinger
Differential Revision: D106528471
Pulled By: joshkang97
fbshipit-source-id: a5f99b426d3a7a6253bc1972cf8cb60d1cb85089
Summary:
- Use `IndexValue::first_internal_key` from `kBinarySearchWithFirstKey` index entries to decide whether the final bounded MultiScan candidate block starts at or beyond the scan limit.
- Skip that block when its first user key compares greater than or equal to the range limit with `CompareWithoutTimestamp`.
- Preserve existing conservative behavior for unbounded ranges, index entries without first-key metadata, and normal `kBinarySearch` indexes.
- Add parameterized coverage for boundary limits, in-block limits, bytewise and reverse comparators, and stripped/persisted user-defined timestamp modes.
## Testing
CI
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14784
Reviewed By: pdillinger
Differential Revision: D106302205
Pulled By: xingbowang
fbshipit-source-id: 1acebdf48bf7c18d35a781ca41c7bfd5c4ab8f47
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14799
When `CompactionService::Wait()` returns `kSuccess` but `CompactionServiceResult::Read()` fails before the primary renames any remote output file from `CompactionServiceResult::output_path` into the DB directory, fall back to local compaction for the same job and notify the service with `OnInstallation(..., kUseLocal)`.
At that point the remote SSTs are still in the service-managed output directory recorded in `CompactionServiceResult::output_path`, and the primary has not installed any of them into the DB yet.
Update `CompactionServiceTest.InvalidResultFallsBackToLocal` to verify the fallback completes successfully, preserves the data, and invokes `OnInstallation()` exactly once with `kUseLocal`.
Reviewed By: jaykorean
Differential Revision: D106321319
fbshipit-source-id: 39d9206f0e3f62612a52c03462bd1bee69020b80
Summary:
- parse folly getdeps manifests with bare package entries so fallback prefetching actually runs
- validate and remove bad cached/downloaded archives before trying fallback mirrors
- download through temporary files and include libiberty in the GNU toolchain fallback set
Context:
Nightly test failed with dependency download failure in folly.
```
Assessing autoconf...
Download with https://ftpmirror.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz -> /tmp/fbcode_builder_getdeps-Z__wZrocksdbZrocksdbZthird-partyZfollyZbuildZfbcode_builder-root/downloads/autoconf-autoconf-2.69.tar.gz ...
[Complete in 136.616022 seconds]
raise Exception(
Exception: https://ftpmirror.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz: expected sha256 954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969 but got e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
make: *** [folly.mk:152: build_folly] Error 1
##[error]Process completed with exit code 2.
```
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14763
Test Plan:
1. Connection failure:
- Forced first mirror to http://127.0.0.1:1/...
- It logged connection refused.
- It then tried https://mirrors.kernel.org/gnu/...
- Download succeeded, size 1927468, SHA matched 954bd69b...
2. Empty file / bad hash:
- Ran a local HTTP server returning a zero-byte autoconf-2.69.tar.gz
- Script logged mismatch with actual=e3b0c442... size=0
- It removed the bad download and fell back to mirrors.kernel.org
- Download succeeded with the expected SHA.
3. Existing zero-byte cache:
- Seeded cache with an empty tarball.
- Script removed invalid cache and downloaded a verified copy.
Reviewed By: mszeszko-meta
Differential Revision: D105859558
Pulled By: xingbowang
fbshipit-source-id: ff1f20f87debad561610271ce99b8b8de2d4264f
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14749
Add `--num_dbs` flag to run N independent DB instances in parallel. Each `StressTest` instance has its own DB with isolated fault injection (from D104959945). `db_crashtest.py` defaults to `num_dbs=1`.
For `num_dbs=1`: `--db` and `--expected_values_dir` are paths used as-is.
For `num_dbs>1`: they are parent directories; C++ creates `db_0/`, `db_1/`, ... subdirs underneath.
Path ownership: C++ owns DB and secondary dir creation (supports remote env). Python owns EV dir creation (always local). C++ also creates EV dirs as fallback for direct CLI usage. `DestroyAllDbs` cleans up subdirs and the parent dir.
Per-DB: `threads`, `max_key`, `ops_per_thread`, `reopen`, `column_families`, and all DB options.
Shared: background env threads (compaction, flush pool), `block_cache`, `write_buffer_manager`, `compressed_secondary_cache`, `rate_limiter`, `compaction_thread_pool_adjust_interval`.
Reviewed By: anand1976
Differential Revision: D104959942
fbshipit-source-id: 3d0d60101e7f2e600306e5a9c4018686bf649658
Summary:
- Restore prepared transactions to `PREPARED` state when writing the commit marker fails, so callers can still roll them back.
- Preserve `PREPARED` state when rollback of a prepared transaction hits a retryable write error, allowing rollback to be retried after `DB::Resume()`.
- Update `db_stress` to clean up prepared transactions after failed commits and report detailed rollback cleanup failure diagnostics.
- Add a WritePrepared regression test covering retryable commit write failure, retryable rollback write failure, successful rollback retry, and DB reopen.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14778
Test Plan: CI
Reviewed By: pdillinger
Differential Revision: D106202437
Pulled By: xingbowang
fbshipit-source-id: b0b52e1d14f39b023b9692dd8fc44060fa35c446
Summary:
- When `reuse_manifest_on_open` reuses the current MANIFEST, `DB::Open` recovery can flush WAL data into a new L0 SST and append the corresponding `VersionEdit` to that already-current MANIFEST.
- If open later fails and the process crashes, the MANIFEST edit can be durable while the recovered SST directory entry is not, leaving the DB pointing at a missing SST.
- Fsync the recovered SST's data directory before adding the file to the recovery edit when appending to a reused MANIFEST.
- Add a regression test that injects failure after MANIFEST sync, simulates crash cleanup of files created after the last directory sync, and verifies the recovered key remains readable.
## Task
- T272584339
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14780
Test Plan: CI
Reviewed By: anand1976
Differential Revision: D106201774
Pulled By: xingbowang
fbshipit-source-id: a44a7d1263d5bc1d82b995c90eef1a825eab4182
Summary:
- Fix secondary catch-up WAL discovery to retain existing WAL readers until MANIFEST replay advances min_log_number_to_keep past them.
- Do not treat a higher-number WAL appearing in the directory as proof that a lower-number current WAL is obsolete; async WAL precreation can expose that shape while the lower-number WAL is still growing.
- Continue scanning from the smallest retained reader so later appends to the current WAL are replayed, and add a regression test that precreates an empty future WAL before secondary catch-up.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14781
Test Plan:
- make -j128 db_secondary_test
- ./db_secondary_test --gtest_filter=DBSecondaryTest.CatchUpTailsCurrentWalWhenFutureWalExists
- ./db_secondary_test
- make check-sources
Reviewed By: hx235
Differential Revision: D106296411
Pulled By: xingbowang
fbshipit-source-id: acc850a177c02968372981d1407721540bc164f5
Summary:
`AdvancedColumnFamilyOptions::memtable_batch_lookup_optimization` ([advanced_options.h](https://github.com/facebook/rocksdb/blob/main/include/rocksdb/advanced_options.h)) gates the skip-list memtable's batch-lookup optimization for `MultiGet`. When enabled, the search path is cached between consecutive keys, reducing per-key cost from `O(log N)` to `O(log d)` where `d` is the distance between consecutive keys.
The C++ field exists; the C API setter does not. This PR adds the missing pair, mirroring the existing `rocksdb_options_{set,get}_memtable_huge_page_size` shape exactly — the closest sibling on both axes:
- C API: adjacent memtable knob, same `rocksdb_options_t*` receiver.
- C++: same `AdvancedColumnFamilyOptions` parent struct, same immutability semantics.
## Motivation
Without this setter, C API consumers and downstream bindings cannot opt into the batch-lookup optimization. Non-skip-list memtable implementations fall back to per-key lookups, so the flag is a no-op for them.
The field is immutable on the C++ side, so calling the setter on options that are already in use by an open DB has no effect on that DB — same constraint as the underlying C++ field. This matches the behavior of every other immutable-options setter in the C API.
No change to the C++ API.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14776
Reviewed By: joshkang97
Differential Revision: D106364224
Pulled By: xingbowang
fbshipit-source-id: 90946af498fba51581a1e7d493c9e5c9b98472a2
Summary:
The block-based table format gained an "auto" index-block search mode ([table.h](https://github.com/facebook/rocksdb/blob/main/include/rocksdb/table.h)) that selects binary vs interpolation search per index block based on key uniformity. The C++ surface exposes this as two coupled knobs:
- `BlockSearchType::kAuto = 0x02` selects the per-block adaptive search at read time.
- `BlockBasedTableOptions::uniform_cv_threshold` (default `-1`, i.e. disabled) is the coefficient-of-variation threshold checked on the write path to set the per-block `is_uniform` footer bit that `kAuto` reads.
This PR adds the missing C API coverage for both:
1. **`rocksdb_block_based_table_index_block_search_type_auto = 2`** enum constant. The existing setter `rocksdb_block_based_options_set_index_block_search_type` already does `static_cast<BlockSearchType>(v)`, so `kAuto = 2` was reachable today by passing the raw int — only the named constant was missing.
2. **`rocksdb_block_based_options_set_uniform_cv_threshold(...)` setter**. The field had no C wrapper, so C/binding users could select `kAuto` but the `is_uniform` bit was never set on the write path, making `kAuto` degenerate to `kBinary`.
No getter is added: the surrounding `rocksdb_block_based_options_set_*` functions in `c.h` do not expose getters either, so adding one only here would be inconsistent with the local style.
## Motivation
Without both pieces, `kAuto` is effectively unreachable from C. This matters for binding consumers (Rust, Go, Java-via-JNI shim, etc.) who want to opt index-block search into the per-block adaptive mode.
The setter mirrors the existing `rocksdb_block_based_options_set_data_block_hash_ratio` shape exactly (same struct, same `double` payload, same naming pattern), so review surface is minimal.
No change to the C++ API.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14775
Reviewed By: joshkang97
Differential Revision: D106364288
Pulled By: xingbowang
fbshipit-source-id: bb532eac6d4c04d032a7235f25ac29ab74f636f2
Summary:
- Suppress retryable `io_uring_wait_cqe()` errors in `PosixFileSystem::Poll()` and `AbortIO()` before they reach stderr during crash-test SIGTERM timeout handling.
- Keep terminal `wait_cqe` failures logged and fatal.
- Extend the `db_crashtest.py` SIGTERM stderr filter only for retryable `Poll`/`AbortIO` wait_cqe errors and add regression coverage.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14779
Test Plan:
CI
## Related
- T272682963
Reviewed By: hx235
Differential Revision: D106201579
Pulled By: xingbowang
fbshipit-source-id: 2331d5cdca064ad901f6af7341b4e8f15b418663
Summary:
The async MultiGet support introduced two `ReadOptions` flags: `async_io` and `optimize_multiget_for_io`. The setter/getter for `async_io` was exposed in the C API in [ff04fb154](https://github.com/facebook/rocksdb/commit/ff04fb154bd74fd0681baa83b478095207e2719d); the corresponding pair for `optimize_multiget_for_io` was not.
This PR adds `rocksdb_readoptions_set_optimize_multiget_for_io` and `rocksdb_readoptions_get_optimize_multiget_for_io`, mirroring the existing `async_io` pattern exactly.
## Motivation
The flag is consulted in `db/version_set.cc` only inside the `#if USE_COROUTINES` guard, so this setter has no behavioral effect in non-coroutine builds. It matters for:
1. **API parity** with the C++ surface and with the existing `async_io` C API. The two flags were introduced together as part of the async MultiGet feature; only exposing one is an oversight.
2. **CPU/latency tuning in `USE_COROUTINES` builds.** Per the [Asynchronous IO in RocksDB blog post](https://rocksdb.org/blog/2022/10/07/asynchronous-io-in-rocksdb.html), `async_io=true` with `optimize_multiget_for_io=false` (single-level parallel reads) ran 775 μs/op vs 508 μs/op with `optimize_multiget_for_io=true` (multi-level), with the latter incurring additional CPU overhead from coroutine scheduling. Without this setter, coroutine-enabled builds cannot reach the single-level configuration from C.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14752
Reviewed By: mszeszko-meta
Differential Revision: D106080144
Pulled By: xingbowang
fbshipit-source-id: 28f12f07f29660392ba6ef7840b22804dab3567b
Summary:
* Fix Makefile default target (was ordered after a folly target)
* Improved the speed of `make clean` by using just one `find` and by pruning "hidden" .* and third-party directories that should not be modified anyway.
* Reduce excessive output from `make clean`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14767
Test Plan: manual
Reviewed By: mszeszko-meta
Differential Revision: D105972958
Pulled By: pdillinger
fbshipit-source-id: 2c0f6097c74c3129b815450f23c19ef07bfbe656
Summary:
Fix flaky test failures in EnvPosixTestWithParam.AllocateTest and DBWALTest.TruncateLastLogAfterRecoverWithFlush that occur when running on filesystems where preallocated space is not reliably reflected in st_blocks.
The tests use stat() to check st_blocks and verify that fallocate with FALLOC_FL_KEEP_SIZE actually preallocates disk space. However, on certain filesystems, this check is unreliable:
1. btrfs and zfs: Copy-on-write filesystems where preallocated extents are not reliably reflected in st_blocks, especially under load.
2. tmpfs: Memory filesystem that may not report preallocated blocks in st_blocks.
3. overlayfs: Union filesystem common in containers that may not pass through fallocate properly or report preallocated space.
4. Any filesystem where FALLOC_FL_KEEP_SIZE is not supported: The preallocation will fail silently (error ignored with PermitUncheckedError), leaving only the written data in st_blocks.
Changes made:
env/env_test.cc:
- Added filesystem magic number definitions for TMPFS_MAGIC, OVERLAYFS_SUPER_MAGIC, and ZFS_SUPER_MAGIC
- Extended the AllocateTest to skip block count checks on zfs, tmpfs, and overlayfs in addition to btrfs
- Added runtime fallback: if st_blocks is less than expected, print a warning and skip the check instead of failing. This handles unknown filesystems or configurations where preallocation isn't supported.
db/db_wal_test.cc:
- Added includes and filesystem magic number definitions
- Added ShouldSkipAllocationCheck() helper function to detect problematic filesystems
- Modified TruncateLastLogAfterRecoverWithoutFlush, TruncateLastLogAfterRecoverWithFlush, TruncateLastLogAfterRecoverWALEmpty, and ReadOnlyRecoveryNoTruncate tests to skip allocation checks on problematic filesystems
- Added runtime fallback checks similar to env_test.cc
These changes make the tests robust against filesystem differences while still validating preallocation behavior on filesystems where it works correctly (ext4, xfs).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14744
Test Plan: Many local 'make -j100 check' runs that would previously fail with good probability.
Reviewed By: hx235
Differential Revision: D105331256
Pulled By: pdillinger
fbshipit-source-id: 862a1512da1466cb037af15342404939b677c02a
Summary:
### Notify listeners before DB shutdown begins
db_stress listener bookkeeping correlates compaction callbacks with file deletion callbacks. DBImpl intentionally skips some compaction listener callbacks once shutdown starts, but file deletion callbacks can still be delivered. That mismatch is normally tolerable for production listeners, but db_stress uses listener-local tracking to detect callback consistency and can report a false positive when shutdown interrupts a compaction callback sequence.
The failed-open case is the important gap. DB::Open() can create a DBImpl, recover or flush files, schedule background compaction, and then fail during late open work such as persisting OPTIONS or waiting for open-time compaction under fault injection. At that point DB::Open() tears down the internal DBImpl before returning an error to StressTest::Open(). If OnCompactionBegin already recorded an input file or job in DbStressListener, DBImpl shutdown can suppress the later OnCompactionPreCommit/OnCompactionCompleted callbacks that would normally clear that state. Since control has not returned to the stress harness yet, StressTest::CleanUp()/Reopen() cannot notify the listener in time. A later shutdown-time callback, or the next open retry reusing the same listener, can then observe stale tracking and abort even though RocksDB did not compact the same SST concurrently.
Add EventListener::OnDBShutdownBegin and fire it once from DBImpl::CancelAllBackgroundWork() before publishing shutting_down_. The callback also covers cleanup of a failed DB::Open() attempt, where the DB pointer refers to the internal DBImpl that was never returned to the caller. Track shutdown_notification_sent_ separately from shutting_down_ because listeners are invoked with mutex_ released, and a concurrent or reentrant cancellation must not deliver the callback twice.
Update DbStressListener to consume the DBImpl-driven shutdown notification instead of relying on StressTest::CleanUp()/Reopen() to manually notify it. This lets db_stress mark itself as shutting down before DBImpl starts skipping shutdown-sensitive compaction notifications, including during failed-open cleanup.
### Also keep listener state scoped to one db_stress open attempt.
Initialize listeners at the top of each non-transactional open retry before enabling open fault injection, so retry attempts get fresh listener state without widening open fault injection to listener construction. Factor the open fault setup into a small helper to keep the retry loop readable. The transaction open path still initializes listeners once because it does not use this open-fault retry loop.
### Also fix multi-ops transaction listener checks during shutdown
A TSAN race was reported where MultiOpsTxnsStressListener::OnCompactionCompleted called VerifyPkSkFast on a background compaction thread while the main thread was destroying the transaction DB wrapper in StressTest::CleanUp(). The reported read was a virtual call through the DB object and the write was the WritePreparedTxnDB/WriteUnpreparedTxnDB destructor updating the vptr.
The vulnerable ordering is that DBImpl can already be inside NotifyOnCompactionCompleted before shutdown is requested. It unlocks db mutex while iterating listeners; another listener such as DbStressListener can spend time in its callback, giving the main thread time to enter CleanUp()/Close(). The DB object is still shutting down, but MultiOpsTxnsStressListener may be invoked later in the same callback iteration and call VerifyPkSkFast through stress_test_->db_aptr_, racing with DB wrapper destruction.
With DBImpl-owned EventListener::OnDBShutdownBegin callback, we have MultiOpsTxnsStressListener consume that callback directly. Once DBImpl begins shutdown, the listener skips both flush-completed and compaction-completed verification callbacks, avoiding DB access during teardown. This also covers failed-open cleanup without name-based downcasts in StressTest.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14769
Test Plan: CI
Reviewed By: pdillinger
Differential Revision: D106011452
Pulled By: xingbowang
fbshipit-source-id: 768838ddcd9910de5d1b5204c990a4d88dbc850c
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14757
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14750
Prerequisite for multi-DB stress test support where each StressTest instance owns one DB. Moves fault injection from a single global to per-StressTest instance so each DB gets isolated fault injection; errors injected into one DB do not leak to others.
FS/Env architecture change:
Before (upstream):
raw_fs → FaultInjectionTestFS (global) → DbStressFSWrapper → db_stress_env → DB::Open
After (this diff):
Global: raw_env (no wrappers)
Used outside StressTest: non-FS ops (threads, time, sleep), test framework FS setup (dirs), DB destruction
Used inside StressTest: cleanup ops that must succeed (external file delete)
Per StressTest:
raw_fs → DbStressFSWrapper (db_stress_fs_, always)
→ FaultInjectionTestFS (db_fault_injection_fs_, optional)
→ CompositeEnvWrapper (db_env_, always) → DB::Open
Expected values state: Env::Default() (always local PosixEnv even when raw_env is remote)
FS layer order swapped (DbStressFSWrapper now innermost). Safe because:
- Error injection returns early; inner wrapper never executes (same behavior)
- DbStressFSWrapper assertions do not modify data (checksum validation, IOActivity checks)
- MANIFEST rename tracking slightly better for crash simulation in new order
Stored members (per StressTest):
- db_stress_fs_: DbStressFSWrapper. Always active regardless of fault injection flags.
- db_fault_injection_fs_: FaultInjectionTestFS. Only when fault injection flags set. Direct access for Enable/Disable/SetThreadLocal.
- db_env_: CompositeEnvWrapper. Always present. THE env for all DB I/O (options_.env).
Eliminated globals: db_stress_env → renamed to raw_env (no wrappers, DbStressFSWrapper moved to per-StressTest); db_stress_listener_env, db_stress_raw_fs removed; fault_fs_guard, fault_env_guard moved to per-StressTest.
Other changes:
- CleanupOutputDirectory simplified (uses raw_env, no disable/enable needed)
- SstFileManager recreated with per-StressTest env in Open()
- Remote compaction override env uses options.env directly (fixes pre-existing silent bug)
- Comments added: Env::Default() always local, DbStressDestroyDb MANIFEST explanation, fault injection log path in TEST_TMPDIR
- TestFSWritableFile::Close() now mirrors the production FSWritableFile close boundary. After the first Close() attempt, later explicit or destructor Close() calls are wrapper-level no-ops, while FaultInjectionTestFS still records the first close attempt for crash/recovery simulation.
- Added targeted fault_injection_fs_test coverage for injected metadata-close failures to ensure FaultInjectionTestFS does not retry the inner Close() path.
Reviewed By: anand1976
Differential Revision: D104959945
fbshipit-source-id: 7cf9bb494dec2b372528d5f119c023b6d392ffca
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14754
**Problem**
When db_bench shuts down (fatal IO error, unknown benchmark name, etc.), ErrorExit() or ~Benchmark() destroys db_/multi_dbs_ while worker threads may still be inside benchmark methods holding raw DBWithColumnFamilies pointers returned by SelectDBWithCfh(). In single-DB mode, db_.db becomes nullptr and multi_dbs_ is empty, so workers evaluate rand_int % 0 and SIGFPE. The cascade across all workers masks whatever originally triggered the shutdown. Observed in the reliable_volumes crash-fault test at ~5 SIGFPE events per hour.
**Approach**
Guard DB lifetime at the worker-thread boundary with a reader-writer lock (port::RWMutex): workers acquire a read lock for the duration of their benchmark method; mutators acquire the write lock (which waits for active readers to drain) before destroying DBs. This ensures raw pointers returned by SelectDBWithCfh() remain valid for their entire usage scope.
**Fix**
- Workers hold a DbUseGuard (RAII wrapper over a read lock on db_lifecycle_rwlock_) for the entire benchmark method in ThreadBody. Cost: one rdlock/unlock pair per worker lifetime, zero hot-path overhead.
- Every DB mutation path (ErrorExit, ~Benchmark, fresh-DB reopen) takes DbStateMutationGuard, which acquires the write lock and then stops the secondary update thread before mutation may proceed.
- ErrorExit() routes to std::_Exit(1) when called from any thread that cannot safely run the mutation cleanup path: a DbUseGuard holder (would self-wait on the write lock) or the secondary update thread (would self-join via StopSecondaryUpdateThread). Tracked via two thread-locals: holds_db_use_guard_ and is_secondary_update_thread_. Main thread takes DbStateMutationGuard, cleans up, dispatches through db_bench_exit() / ToolHooks::Exit.
- StopSecondaryUpdateThread() resets secondary_update_stopped_ to 0 after joining, so a replacement thread created by a subsequent Open() is not immediately killed.
- Protocol is mechanically enforced in debug builds:
* SelectDBWithCfh asserts holds_db_use_guard_ (read-side ownership)
* DeleteDBs asserts holds_db_state_mutation_guard_ (write-side ownership)
* DbUseGuard and DbStateMutationGuard ctor/dtor assert correct imbalance (no nested or stray release).
* DbStateMutationGuard ctor additionally asserts !holds_db_use_guard_ and !is_secondary_update_thread_, catching the new deadlock modes the single-lock design makes reachable (WriteLock while holding ReadLock; secondary thread self-joining via StopSecondaryUpdateThread).
These convert "trust me" invariants into "the assert fires if you break it." Direct field accesses (e.g. db_.db->NewIterator inside a benchmark method, multi_dbs_.clear() in the reopen branch) are protected by the enclosing guard scope but are not per-site asserted.
**Caveat**
port::RWMutex is pthread_rwlock_t with default attrs -> reader-preferred on Linux/glibc. The current call graph has no concurrent worker spawn during mutator wait (mutator paths run only from the main thread, not concurrently with RunBenchmark spawning workers), so writer starvation is not reachable. Documented as a constraint; revisit if that invariant changes. The port layer doesn't expose pthread_rwlockattr_setkind_np cross-platform.
**Alternatives considered**
- std::_Exit(1) on every shutdown path, skip cleanup entirely. Loses ToolHooks::Exit dispatch, flushed traces, and end-of-run stats -- those matter for the RV crash-fault test image which consumes db_bench output. Rejected.
- shared_ptr<DBWithColumnFamilies> from SelectDBWithCfh, let DB lifetime extend naturally to the last reader. Adds a per-call atomic refcount bump in the hot path; the RWMutex approach is per-method-call instead of per-op, making the hot-path cost zero. Rejected for hot-path neutrality.
Reviewed By: xingbowang
Differential Revision: D104974784
fbshipit-source-id: 3a04d9e1c0b5042436d690f573cf369de6b4c9df
Summary:
- Rebuild db_stress event listeners through a shared `InitializeListenersForOpen()` helper.
- Reinitialize listeners before retrying `DB::Open()` after injected open or open-compaction failures.
## Context
- A stress test failed with "Concurrent compaction of SST file detected".
- Root cause: StressTest::Open() built DbStressListener once before its DB::Open() retry loop. With open fault injection, a DB::Open() attempt can create a DBImpl, schedule background compaction, and then fail during late open work such as persisting OPTIONS. During teardown of that failed DBImpl, DBImpl's shutdown flag can suppress later compaction callbacks, leaving listener-local compaction bookkeeping stale.
- Diagnosis: Sandcastle DB LOGs showed file 16821 flushed, then a failed open attempt with an injected read error and a background compaction picking 16821. The crash was in DbStressListener::OnCompactionBegin, so this was stale db_stress listener state across open attempts rather than DBImpl allowing a real concurrent compaction of the same SST.
- Fix: factor listener construction into InitializeListenersForOpen() and call it before each DB::Open() attempt, including the retry path after open/open-compaction failure. Each DBImpl open attempt now gets fresh listener state.
- Verification: make clean; make db_stress -j192; make check-sources; git diff --check.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14765
Test Plan:
- `make clean`
- `make db_stress -j192`
- `make check-sources`
- `git diff --check`
Reviewed By: pdillinger
Differential Revision: D105969381
Pulled By: xingbowang
fbshipit-source-id: 759e2be7e1215a498ed449ab36f13e8c7975f4a4
Summary:
- Clean DBTestBase's fixture-owned alternate WAL and db_log_dir paths during setup and teardown.
- Prevent kDBLogDir option tests from leaving dbname_/db_log_dir behind and polluting later DBTest cases in the same gtest shard.
- Preserve production DestroyDB() behavior while making the test fixture cleanup complete.
Context:
- The ARM nightly failure was exposed by the 32-shard db_test layout running DBTest.GetPicksCorrectFile before DBTest.PurgeInfoLogs in the same process.
- GetPicksCorrectFile can use kDBLogDir, which creates logs under dbname_/db_log_dir. DestroyDB() intentionally ignores DeleteDir() failures when unknown children remain, so the next fixture could observe dbname_ still existing.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14764
Test Plan:
- make -j14 db_test
- TEST_TMPDIR=/tmp/rocksdb_arm_fix_test ./db_test --gtest_filter=DBTest.GetPicksCorrectFile:DBTest.PurgeInfoLogs
- TEST_TMPDIR=/tmp/rocksdb_arm_fix_test_shard GTEST_TOTAL_SHARDS=32 GTEST_SHARD_INDEX=18 ./db_test
Reviewed By: mszeszko-meta
Differential Revision: D105860476
Pulled By: xingbowang
fbshipit-source-id: b2685063ca6c4eedec589697b439fe4aee4eda1a
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14756
Pure mechanical refactor: replace all direct FLAGS_db / FLAGS_expected_values_dir / FLAGS_secondaries_base reads with accessor methods on StressTest. No new flags, no parameters, no behavior change. Prepares for multi-DB stress test where each StressTest instance has its own DB.
Changes:
- GetDbPath(), GetExpectedValuesDir(), GetSecondariesBase() accessors return the corresponding FLAGS values directly
- Replace ~15 FLAGS_db references with GetDbPath() in db_stress_test_base.cc
- Move SharedState constructor from .h to .cc (needs full StressTest type for GetExpectedValuesDir())
- Move DbStressListener constructor from .h to .cc (same reason)
- Replace FLAGS_db / FLAGS_expected_values_dir in db_stress_driver.cc with accessor calls
- NO changes to db_crashtest.py or db_stress_gflags.cc
Reviewed By: anand1976
Differential Revision: D104959943
fbshipit-source-id: d7ef6a39d4c2ed467b2960417629c09f3988faf5
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14742
This change enables testing of the two new MANIFEST optimization options introduced in D103568447:
1. optimize_manifest_for_recovery - Skips unnecessary MANIFEST edits during recovery
2. reuse_manifest_on_open - Reuses existing MANIFEST file on DB open
Changes:
- tools/db_crashtest.py: Add both options with 20% probability to default_params
- db_stress_tool/db_stress_test_base.h: Add ManifestVerifyMode enum and member variables for tracking MANIFEST state
- db_stress_tool/db_stress_test_base.cc: Implement RecordManifestStateBeforeReopen() and VerifyManifestNotRewritten() methods to validate MANIFEST reuse on DB reopen
The verification logic handles 4 combinations:
- Both disabled: No verification (baseline)
- Only optimize enabled: No verification (hard to measure without sync points)
- Only reuse enabled: Verify MANIFEST file is reused
- Both enabled: Verify MANIFEST reused AND CURRENT unchanged
Verification is warning-only (not fatal) to account for legitimate fallback cases like corruption or size limits.
Reviewed By: hx235
Differential Revision: D105224068
fbshipit-source-id: 5baa65680fdd639674d87ff1e9187b743e691bc1
Summary:
seen several times in the build-linux-mini-crashtest job. Raise ulimit in container spec.
Task: T271298423
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14755
Test Plan: look at reported ulimits, watch CI
Reviewed By: hx235
Differential Revision: D105664569
Pulled By: pdillinger
fbshipit-source-id: 6f7f3976bd73ecc86509ac955d5e190316e98ba3
Summary:
NotifyOnCompactionPreCommit initially omitted the shutting_down_ guard to avoid a false positive abort in the db_stress OnTableFileDeleted check, where stale compacting_files_ entries from skipped notifications would be mistaken for a bug. The better fix is to add the shutting_down_ guard for consistency with Begin and Completed, and instead make OnTableFileDeleted tolerate stale tracking during shutdown by checking a new atomic bool in the listener intended to track DBImpl's shutting_down_.
Also fix lint: release mutex before RandomSleep() in PreCommit; use char literal for find_last_of; avoid unnecessary string copy.
Document WART in listener.h: all three compaction callbacks are skipped during DB shutdown, so a committed compaction may go unobserved.
Bonus: update CLAUDE.md with instructions on avoiding non-ASCII characters
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14753
Test Plan: manually trigger many crash test runs
Reviewed By: hx235
Differential Revision: D105591913
Pulled By: pdillinger
fbshipit-source-id: 32029fea4c2571d88f645eb325db2e25a94e0d26
Summary:
BackgroundJobPressure test was flaky because Phase 3 used TEST_WaitForCompact() which waits for bg_compaction_scheduled_ but NOT bg_pressure_callback_in_progress_. The final "healthy" pressure callback could still be in-flight when the test checked snapshots.back(), causing compaction_scheduled=1 instead of 0.
Fix: replace TEST_WaitForCompact() with TEST_WaitForBackgroundWork() which explicitly checks bg_pressure_callback_in_progress_ (db_impl.cc:478-484). Also add TEST_WaitForBackgroundWork() before Phase 1 and Phase 2 snapshot checks for consistency, ensuring all pressure callbacks are delivered before assertions.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14708
Reviewed By: jaykorean
Differential Revision: D103784101
fbshipit-source-id: 275802b5bb70094af62486bde26b599a292e71fa
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14751
`regression_test.sh` already cleans the current run's `TEST_PATH` on normal success and on the early-exit path when a recent `db_bench` is still running. But a hard benchmark failure goes through `exit_on_error`, which exits before the end-of-`main()` cleanup runs.
This change adds an `EXIT` trap and a single-shot finalizer so the current invocation still cleans its own `TEST_PATH` on hard failure. It does not reintroduce any sibling-directory cleanup, and it preserves the existing success-path cleanup, early-exit cleanup, and debug preservation behavior.
| Scenario | Previous code | New code |
| --- | --- | --- |
| Normal success | Cleans current `TEST_PATH` at end of `main()` | Still cleans current `TEST_PATH` |
| Hard benchmark failure via `exit_on_error` | Can leave current `TEST_PATH` behind | `EXIT` trap cleans current `TEST_PATH` |
| Early exit because recent `db_bench` exists | Cleans current `TEST_PATH`, exits `2` | Same behavior |
| Debug mode / `DELETE_TEST_PATH=0` | Preserves artifacts | Same behavior |
Reviewed By: jaykorean
Differential Revision: D105411220
fbshipit-source-id: 37a335b87faaeee86d44ef2e24bebf1b7b9626d6
Summary:
Reduce wall-clock time of parallel 'make check' by improving the scheduling and granularity of slow test binaries.
Three Makefile changes:
1) Refresh slow_test_regexp with current observed bottlenecks. Adds
binaries (point_lock_manager_stress_test, compaction_service_test,
corruption_test, comparator_db_test, external_sst_file_basic_test,
rate_limiter_test, db_compaction_test, db_merge_operator_test,
db_dynamic_level_test, db_bloom_filter_test, error_handler_fs_test,
merge_helper_test, db_kv_checksum_test, inlineskiplist_test) whose
shards take >=15s but were not being front-loaded for early
queueing. Also drops stale FIXME comments that no longer apply
and adds tier annotations + a maintenance recipe.
2) Add SHARD_SIZE_OVERRIDES, a per-binary override of GTEST_SHARD_SIZE,
so binaries with slow individual tests (e.g.
point_lock_manager_stress_test where each test is ~10s) can be
chopped into more, smaller shards. The default of 10 stays for
everything else. Each shard's effective size is reported in the
'Generating ... shards for ...' line.
3) Add 'make suggest-slow-tests' to print a per-binary aggregation of
the most recent LOG (max single-shard time, total time, shard
count) for any binary worth attention. Used to maintain the regex
and override list above.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14745
Test Plan:
Two runs each of 'make -j166 check', before and after this change (all compilation already finished):
Before: 197s and 198s
After: 123s and 125s
Reduction: 37%
Reviewed By: xingbowang
Differential Revision: D105332444
Pulled By: pdillinger
fbshipit-source-id: 1d1c2f89a32647e6651e2ffeb72da9d51bcc004f
Summary:
In PR https://github.com/facebook/rocksdb/issues/13531, we added a `saved_key_.SetUserKey(ikey.user_key)` call in `FindValueForCurrentKeyUsingSeek` to fix unprepared-value reverse iteration. The default `copy`=`true` parameter unconditionally copies the key into the internal buffer, breaking is-key-pinned when `pin_data`=`true`. This path triggers only when a key has more versions than `max_sequential_skip_in_iterations` (default 8 ), making the bug rare and hard to repro deterministically.
**Fix:** pass `!pin_thru_lifetime_ || !iter_.iter()->IsKeyPinned()` as the copy parameter, matching every other `SetUserKey` call site in `DBIter`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14746
Test Plan: New test `SeekForPrevKeyPinnedWithManyVersions`: writes 20 versions of the same key, confirms `FindValueForCurrentKeyUsingSeek` is taken via `NUMBER_OF_RESEEKS_IN_ITERATION`, asserts is-key-pinned == "1" after `SeekForPrev`. Fails without fix, passes with.
Reviewed By: xingbowang
Differential Revision: D105397906
Pulled By: mszeszko-meta
fbshipit-source-id: 5581105e9d929bb4c582c52dd6a7ae3e8dd9da72
Summary:
Adds a new `EventListener::OnCompactionPreCommit` callback that fires after a compaction job's output files are written but *before* the manifest write commits the new Version. At that point input files still have `FileMetaData::being_compacted == true`, so listeners that maintain bookkeeping of "files currently being compacted" can clean up that state without racing the compaction picker.
A check implemented by a Meta-internal RocksDB user crashes when the same file appears as input to two concurrent compactions. Tracking that set in `OnCompactionBegin` / `OnCompactionCompleted` produces false positives because `Compaction::ReleaseCompactionFiles()` flips `being_compacted` back to false before `OnCompactionCompleted` fires, so another thread can pick the same file and trigger `OnCompactionBegin` before the previous compaction's `Completed` callback runs. Doing the cleanup in `OnCompactionPreCommit` closes that race. Default implementation is a no-op, so no API break.
A trivial refactoring to split PerformTrivialMove ensures data is populated for the new callback, while calling back before the trivial move compaction is committed.
Bonus: `CLAUDE.md` update for when to call `make clean` as I've recently had it get thoroughly confused TWICE mixing build modes.
Task: T269479969
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14740
Test Plan:
- New unit test in `db/listener_test.cc` checks that `OnCompactionPreCommit` fires strictly between `OnCompactionBegin` and `OnCompactionCompleted` for the same compaction, and that input files still have `being_compacted == true` at the time it fires.
- Crash test: adds a concurrent-compaction sanity check to `db_stress`'s listener resembling the Meta-internal intended usage: tracks input file numbers from `OnCompactionBegin` to `OnCompactionPreCommit`, aborting if the same file appears as input to two concurrent compactions. Also checks other ordering constraints and checks for "leaks" from possible failure to call OnCompactionPreCommit(). Exercises all compaction styles and trivial-move/FIFO paths under load.
Reviewed By: hx235
Differential Revision: D105065326
Pulled By: pdillinger
fbshipit-source-id: 8f606a7c0fac899574e7340c600b71fed902394a
Summary:
- Add experimental immutable `DBOptions::async_wal_precreate` to reserve and open one future WAL on a background HIGH-priority task, with sanitization that disables the optimization when WAL recycling is configured.
- Split WAL creation into open/preallocate and start phases so `SwitchMemtable()` can consume a prepared WAL after writing normal WAL metadata, wait for in-flight precreation, fall back to synchronous creation, and delete an unstarted prepared WAL on start failure.
- Keep WAL numbering, close, recovery, and read-only open safe for empty future WAL files left by async precreation; `error_if_wal_file_exists=true` now rejects non-empty WALs while tolerating empty WALs.
- Add public option plumbing for the C API, options parsing/stringification, random option testing, `db_bench`, `db_stress`, and crash-test configuration.
- Add WAL precreate statistics counters plus Java `TickerType`/JNI mappings, and update C++, C, and Java read-only-open documentation for the empty-WAL behavior.
- Add focused WAL/option/C/Java tests for async precreate ready/wait/failure/recovery paths, read-only WAL detection, option sanitization, and API plumbing, plus write-flow docs and unreleased history entries for the new feature and behavior change.
PR https://github.com/facebook/rocksdb/pull/14738
Reviewed By: pdillinger
Differential Revision: D105020559
fbshipit-source-id: 5059b424702e021abb8de65ceeb6d3b975280ffc
Summary:
**Summary:**
Adds a --listener_uri flag to db_stress that creates an EventListener via ObjectLibrary from the given URI and attaches it to the DB options.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14741
Test Plan:
- Compilation
- e2e test will be done next internally with a customized listener
Reviewed By: anand1976
Differential Revision: D104750476
Pulled By: hx235
fbshipit-source-id: cdc00191de6b7434e4b373db30769ff34d99b80d
Summary:
last_compacted_manifest_file_size_ drives TuneMaxManifestFileSize() to compute the manifest rotation threshold, but it started at 0 on every DB::Open and was only populated after the first manifest rotation. This is really only a problem with reuse_manifest_on_open, because no fresh manifest is created on open.
Add a new forward-compatible (safe-to-ignore) MANIFEST tag kLastCompactedManifestFileSize that records the approximate compacted manifest size at the end of WriteCurrentStateToManifest. During recovery, the value is loaded and used to immediately tune the rotation threshold.
The record includes a rough estimate of its own overhead (~15 bytes) and must be the last record written by WriteCurrentStateToManifest for accurate estimation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14725
Test Plan:
Extended AutoTuneManifestSize in db_etc3_test to close and reopen with reuse_manifest_on_open after establishing a known auto-tuning state. Verifies that the manifest file number is preserved (no spurious rotation) and that subsequent CF additions don't trigger rotation -- proving the persisted compacted size keeps the tuned threshold correct. Verified the test fails when the recovery loading is disabled.
Relax a fragile Java test that was dependent on the exact size of the manifest file.
SHORT_TEST=1 ./tools/check_format_compatible.sh
Reviewed By: anand1976
Differential Revision: D104464522
Pulled By: pdillinger
fbshipit-source-id: 4f5d22d2e149bd40a523ee11780e5e3344803c19
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14726
Add SstFileReader::ParseTableIteratorKey() so callers of NewTableIterator() have a public way to decode raw table keys without duplicating RocksDB internal-key layout. The implementation delegates to the existing public ParsedEntryInfo parser using the reader comparator.
Reviewed By: pdillinger
Differential Revision: D104584393
fbshipit-source-id: 98e21c4d6676fbba69e533376b3da67539dd8fad
Summary:
Previously, calling `DB::CreateColumnFamily(opts, "", &handle)` returned `Status::OK()` with a usable handle, but the column family was not persisted in the manifest. Any data written to the empty-named CF was silently lost on DB reopen, and `ListColumnFamilies` would not show it.
The empty string is also reserved as a sentinel meaning "no/unknown column family" in various RocksDB APIs and serialization formats (e.g. `TablePropertiesCollectorFactory::Context::kUnknownColumnFamily` and table properties), so allowing it as a real CF name is ambiguous in addition to being broken.
This change rejects an empty CF name with `Status::InvalidArgument` at the top of `DBImpl::CreateColumnFamilyImpl`, which covers the single-CF `CreateColumnFamily` API as well as both `CreateColumnFamilies` overloads (by-names and by-descriptors).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14732
Test Plan: * Added `ColumnFamilyTest.EmptyNameRejected` covering all three Create entry points; verifies `IsInvalidArgument()` and that no spurious handles are returned.
Reviewed By: hx235
Differential Revision: D104753911
Pulled By: pdillinger
fbshipit-source-id: e52d792830b965484a618f4e55981eee4eb6f515
Summary:
- propagate lower-level read and merge failures through `GetContext` via `read_status`, so `Get` and `GetEntity` preserve the original error instead of synthesizing `Corruption` when blob-backed reads or merge resolution fail
- teach `GetMergeOperands` to resolve blob-backed default columns from wide-column entities, covering both the direct base-value path and the merge-plus-base path
- add regression coverage for blob-read IO errors during `Get`/`GetEntity` merge resolution and for `GetMergeOperands` on blob-backed wide-column entities
- fix the `DBFlushTest.MemPurgeCorrectLogNumberAndSSTFileCreation` test race by waiting for flush callbacks and cleaning up sync points
## Testing
- `make db_blob_basic_test -j14`
- `/usr/bin/perl -e 'alarm shift; exec ARGV' 60 ./db_blob_basic_test --gtest_filter='DBBlobBasicTest/DBBlobBasicIOErrorTest.GetBlob_IOError/*:DBBlobBasicTest/DBBlobBasicIOErrorTest.GetEntityMergeWithBlobBaseIOError/*'`
## Task
T265824017, T265415808
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14640
Reviewed By: anand1976
Differential Revision: D101690700
Pulled By: xingbowang
fbshipit-source-id: 2b6fc357b37a01efa72a2d54dcff55be8992f42a
Summary:
### Motivation
Claude's auto review workflow classifies the PR as complex and sets `MAX_THINKING_TOKENS`=**32000** in `.github/workflows/ai-review-analysis.yml:534`. The Claude action then sends a request where `thinking.budget_tokens` is **32000**, but the request `max_tokens` is not greater than that, so Anthropic rejects it before the review starts ([example](https://github.com/facebook/rocksdb/actions/runs/25691112276/job/75427435133)).
### Fix
Reduce the "complex" thinking budget from **32000** to **24000** tokens and clamps manual overrides to the same ceiling, preventing the thinking budget from exceeding or equalling the action's effective `max_tokens`. 24k is still generous — enough for deep reasoning on complex PRs while guaranteeing the model can emit the formatted review.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14731
Reviewed By: xingbowang
Differential Revision: D104749426
Pulled By: mszeszko-meta
fbshipit-source-id: bf94d0f50e7c2c9bc9e4d4cdffd61087d739018a
Summary:
The non-ASCII character check in check-sources.sh used git grep -P (Perl regex), which requires git compiled with PCRE support. On systems without it, the command fails with exit code 128, which is != 1 (no match), so the check always reported a violation -- effectively dead.
Even in CI where git has PCRE2 support, the check was silently broken: git grep -P uses PCRE2 in UTF mode by default, which interprets [\x80-\xFF] as a Unicode codepoint range (U+0080 to U+00FF). Characters like em-dash (U+2014), arrows (U+2192), and math symbols (U+2248, etc.) fall outside that range and were not detected. Only Latin-1 Supplement characters (U+0080-U+00FF) would have been caught.
Replace with LC_ALL=C git grep using bash $'[\x80-\xff]' literal byte range, which works with basic regex in the C locale, and replace all non-ASCII characters in non-excluded source files:
- em-dash to --
- arrow to ->
- math symbols to ASCII equivalents (~=, <=, >=)
- box-drawing characters to ASCII art
Also exclude .github/ from the check, as scripts there can use non-ascii without disrupting RocksDB builds on non-UTF-8 systems.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14729
Test Plan: manual / CI (make check-sources passes clean)
Reviewed By: hx235
Differential Revision: D104692574
Pulled By: pdillinger
fbshipit-source-id: 1d884c21056dcd83558b825a04b867f1c08e3f45
Summary:
Previously, `Classify PR complexity (Codex)` ran under `bash -e`, so any `codex exec` failure aborted the entire Codex review before the real review step could run. The classifier only selects the review budget, so on failure we now log the classifier output tail, default to the `complex` review budget, and continue. This keeps actual Codex review failures visible through the existing review exit-code/log handling while preventing the auxiliary classifier from blocking review generation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14721
Reviewed By: xingbowang
Differential Revision: D104326081
Pulled By: mszeszko-meta
fbshipit-source-id: c17388bbf576f71ff85ded320e0740f89072f8c1
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14723
### Context
`GetCreationTimeOfOldestFile()` assumed `max_open_files` = -1 meant every live SST had a pinned table reader. That is not true with `open_files_async`: recovery intentionally skips loading table files, `DB::Open()` returns, and `BGWorkAsyncFileOpen()` pins readers later. Any caller — e.g. fb_rocksdb's daily report at `FbRocksDb.cpp:1025` — invoking the API in the window between `DB::Open` returning and the background opener completing trips a debug assert. Reported in https://fb.workplace.com/groups/rocksdb/permalink/31668956482726231/.
Removing the assert alone is insufficient. For legacy DBs whose manifest does not carry `file_creation_time`, `FileMetaData::TryGetFileCreationTime()` falls back to the pinned reader; with no reader, it returns `kUnknownFileCreationTime` and the function silently returns 0 (the "info unavailable" sentinel). The caller cannot distinguish "no info" from "raced with async open."
### Changes
- Remove the invalid debug assert in `Version::GetCreationTimeOfOldestFile`.
- Add a private helper `DBImpl::WaitForAsyncFileOpen()` that blocks on `bg_cv_` while `bg_async_file_open_state_ == kScheduled`. The synchronization machinery (`bg_async_file_open_state_` + `bg_cv_`) already exists — the destructor wait loop in `db_impl.cc:674-687` uses the same pattern. The helper is a no-op when `open_files_async = false`, and bails on `shutting_down_` so `DB::Close()` is not blocked by an in-flight caller.
- Call `WaitForAsyncFileOpen()` at the top of `DBImpl::GetCreationTimeOfOldestFile()` (inside the `max_open_files == -1` branch).
- Document the blocking behavior in `include/rocksdb/db.h`.
- Replace the regression test with one that uses a `DBImpl::WaitForAsyncFileOpen::BeforeWait` sync point: spawn a thread that calls `GetCreationTimeOfOldestFile`, deterministically confirm it blocks inside the wait, release async open, confirm the caller wakes with the real value.
### Potential Followups (not included here)
- Apply the same wait to `GetLiveFilesMetaData` and `GetColumnFamilyMetaData` — both zero out `oldest_ancester_time` / `file_creation_time` in the SST metadata they return during the async-open window (`db/version_set.cc:7877-7878`, `2090-2091`, `2172-2173`).
- Address compaction-picker effects: TTL/periodic file selection (`db/version_set.cc:4039`, `4092-4094`), bottommost over-marking (`db/version_set.cc:4700-4701`), FIFO TTL/temperature pickers (`db/compaction/compaction_picker_fifo.cc:105-107`, `167-170`, `401-411`), and tiered-compaction output time inheritance (`db/compaction/compaction.cc:981`, `1000`).
- Harden `FbRocksDb.cpp:1025` to check `status.ok()` instead of `status.code() != kNotSupported`.
Reviewed By: mszeszko-meta
Differential Revision: D104285992
fbshipit-source-id: ea46375ea1b3ba77fe6b548071aee1101ac0da77
Summary:
- Use the liburing TSAN suppressions in `tools/tsan_suppressions.txt` instead of defining the process-wide `__tsan_default_suppressions()` hook, avoiding conflicts with downstream applications.
- Wire RocksDB TSAN make and crash-test flows to use that suppressions file by default without overriding caller-provided `TSAN_OPTIONS`.
- Cover direct `db_crashtest.py` launches by passing the default suppressions to `db_stress` subprocesses.
- Fix the GCC 16 unity-build warning in `CacheItemHelper` by directly initializing the no-secondary-cache helper fields instead of delegating with `this`.
Imported from D101303486.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14710
Test Plan:
- `COMPILE_WITH_TSAN=1 make -j128 env_test`
- `timeout 60s ./env_test --gtest_filter=EnvPosixTest.IOUringAddressReuseNoTsanFalsePositive`
- `python3 tools/db_crashtest_test.py`
- `python3 -m py_compile tools/db_crashtest.py tools/db_crashtest_test.py`
- CI: `build-linux-unity-and-headers` passed after the `CacheItemHelper` fix
Reviewed By: hx235
Differential Revision: D104103800
Pulled By: xingbowang
fbshipit-source-id: 6066d9abe02a3c44d75f9ce449889468c927ce56
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14704
Add an immutable DBOption `reuse_manifest_on_open` (default false). When enabled, `DB::Open` can keep using the recovered MANIFEST for the first post-open metadata update instead of rebuilding a fresh MANIFEST, which can reduce warm-open latency for DBs whose MANIFEST is expensive to regenerate.
Reuse is still best-effort. If RocksDB cannot safely resume appending to the recovered MANIFEST, it falls back to the existing fresh-MANIFEST path. The option is also disabled under `best_efforts_recovery`.
This diff also teaches the reopened MANIFEST writer to adopt the existing file size before appending, documents the small-`max_manifest_file_size` caveat for the reused path, and keeps the full warm-reopen composition working with `optimize_manifest_for_recovery`.
Reviewed By: hx235, pdillinger
Differential Revision: D103568447
fbshipit-source-id: f4f5c35ea3ef0b80a0d52d94be40c6bd11505999
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14703
Extend `optimize_manifest_for_recovery` so a clean `DB::Close` can persist up-to-date WAL recovery markers when that can be done safely. Combined with the recovery-side optimization in the previous diff, a clean close/reopen can avoid recovery-time MANIFEST appends.
This remains best-effort: if the close-time write is disabled, skipped, or fails, RocksDB falls back to the standard recovery path on the next open. The option stays mutable so it can be turned off before close to suppress the optimization without restarting the DB.
The close-time path respects the existing recovery constraints for 2PC, non-empty column families, dropped column families, and WAL tracking, and preserves the existing file-number invariants.
Reviewed By: pdillinger, hx235
Differential Revision: D103568449
fbshipit-source-id: ae62867507a8a87640a2c140bea852b7c608cb66
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14702
Add a mutable DBOption `optimize_manifest_for_recovery` (default false) as a temporary rollout / kill switch for warm-reopen MANIFEST optimizations.
In this diff, enabling the option lets recovery skip MANIFEST updates during `DB::Open` when the recovered state is already reflected on disk, which reduces metadata appends after a clean shutdown and can lower warm-reopen latency on storage where MANIFEST appends are expensive.
If the option is disabled, RocksDB follows the existing recovery path unchanged. The optimization is disabled under `best_efforts_recovery`, where recovery intentionally rewrites metadata as part of salvage, and the option is mutable so later diffs in this stack can share the same rollout knob.
Reviewed By: pdillinger, hx235
Differential Revision: D103568448
fbshipit-source-id: 9ec930343e434f1bee6130bcdbd7738dddd92b6d
Summary:
This fixes a GCC 16 unity-build failure in `Cache::CacheItemHelper`. The no-secondary-cache constructor delegated to the full constructor while passing this as `without_secondary_compat`. GCC 16 reports that pattern as `-Wmaybe-uninitialized` because the delegated constructor receives a pointer to the object under construction and its debug assertions dereference it. Since RocksDB builds with `-Werror`, the warning is promoted to a build error. We see it now because the unity job uses `gcc:latest`, which now has `GCC 16.1.0`. The code pattern itself dates back to PR https://github.com/facebook/rocksdb/issues/11299 / ccaa3225b from 2023.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14713
Reviewed By: xingbowang
Differential Revision: D104162382
Pulled By: mszeszko-meta
fbshipit-source-id: d6cb579e37bed0d833c6427c3b0541c857425d42
Summary:
Add MLIR-compatible PrefixVarint32/64 helpers in util/prefix_varint.h. Document the format, split decode API, and hot paths, and cover the codec in coding_test with round-trip, disk-read, overflow, and truncation cases.
I'm intending to use this in the new blog file format because you only need to read the first byte to know how many varint bytes to read total, and it might be more CPU efficient in other uses as well (to be determined in follow-up work).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14692
Test Plan: Unit tests included
Reviewed By: joshkang97
Differential Revision: D103245079
Pulled By: pdillinger
fbshipit-source-id: dca435beccb6e666d864a31d8683ad19e2121c34
Summary:
Reduces per-step heap allocations in the external table iterator's hot path
by replacing the `InternalKey` member of `ExternalTableIteratorAdapter` with
`IterKey`. `InternalKey` stores its bytes in a `std::string`, which
heap-allocates whenever a key exceeds the small-string threshold. `IterKey`
keeps a 39-byte inline buffer and only spills to the heap for larger keys.
`UpdateKey` runs on every `Next` / `Prev` / `Seek*`, so this swap removes a
recurring per-step allocation for typical key sizes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14695
Test Plan: CI
Reviewed By: anand1976
Differential Revision: D103440780
Pulled By: joshkang97
fbshipit-source-id: deb0a8ea04110e3dff0a1bcad767e7136192fdc6
Summary:
See SC crash test failure 2729181374202803470.
Flush/compaction retention was protecting only explicit snapshots, while readers without an explicit snapshot were still bounded by GetLastPublishedSequence(). That mismatch becomes observable in WRITE_COMMITTED when commit_bypass_memtable runs: a newer version can already be installed into WBWI-backed immutable memtables before SetLastSequence() publishes it. This can happen with two-write-queue enabled OR disabled.
Example setup:
- K@P = "old_value" is the current published version of key K.
- K@U = "new_value" is written later with U > P.
- commit_bypass_memtable ingests K@U into immutable memtables, but publication is still at P.
- flush starts in that window and builds its snapshot context.
Before this change, if there was no explicit snapshot at P, flush/compaction was free to collapse K@P under K@U. A reader at the published boundary P then saw neither version: K@P had been discarded, while K@U was still too new, so Get(K) returned NotFound.
A simple solution is to add a "fake" snapshot boundary for pessimistic write committed txns.
NOTE: A behavioral side effect is that this will prevent a merge operand at snapshot seqno from being filtered via compaction filter because of the new managed snapshot. But this keeps it aligned with the behavior that write prepared and write unprepared have.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14681
Test Plan: Regression test that fails without fix, specifically K@P gets deleted and a snapshot read returns NotFound, when it should exist.
Reviewed By: hx235
Differential Revision: D102856943
Pulled By: joshkang97
fbshipit-source-id: 46cb3936e9d4e771966d39fd0d15436335bf6ccf
Summary:
This commit fixes several potential issues introduced by cc8d9ea04 (CI: AI review workflow improvements) that broke the agent review jobs in GitHub CI.
1. Codex workflows fail when OPENAI_API_KEY is not configured:
- Added Codex review workflows fail with exit code 1 if the API key secret is missing
- Added script-level checks to skip Codex steps gracefully when OPENAI_API_KEY is unset, avoiding CI noise
2. Missing pull-requests: read permission in manual-review job:
- The manual-review job calls github.rest.pulls.get() but lacked the required permission
- Added 'pull-requests: read' to the manual-review job permissions
3. Potential crash when headSha is undefined:
- parse-claude-review.js and parse-codex-review.js accessed meta.headSha.substring() without null check
- Added defensive checks to handle undefined headSha gracefully
4. Claude model claude-opus-4-7 incompatible with thinking API:
- The commit upgraded default model to claude-opus-4-7, but this model doesn't support the 'thinking.type.enabled' API used by the Claude Code action
- Error: 'thinking.type.enabled' is not supported for this model. Use 'thinking.type.adaptive' and 'output_config.effort' to control thinking behavior
- Reverted default model to claude-opus-4-6 and removed claude-opus-4-7 from options
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14696
Test Plan: Created a draft PR with this change and draft production code changes, including a branch on facebook/rocksdb. Go to https://github.com/facebook/rocksdb/actions/workflows/claude-review.yml and choose "Run workflow" with that branch and PR. (Changes in the PR do are not picked up for the agent code review workflows; the only way to test what is outside of main is with a branch on facebook/rocksdb)
Reviewed By: joshkang97
Differential Revision: D103453631
Pulled By: pdillinger
fbshipit-source-id: 15a82257b5762e9e0b1b393dc45c4b343c866f7a
Summary:
Two CPU-side optimizations to `DBIter`'s hot iteration path.
1. `PrepareValueInternal` always re-parses the internal key after `iter_.PrepareValue()`, even though most inner iterators report their value was already prepared and cannot have moved `iter_.key()`.
2. `ResetValueAndColumns` and `ResetBlobData` unconditionally clear wide-column / blob state at the top of every `Next()`, even on plain-value scans where neither has been populated since the last reset.
Both wins are pure CPU on hot, fully-cached workloads. The dirty-flag bookkeeping introduced for (2) is extracted into a small reusable `DirtyTracked<T>` utility so the same pattern can be reused for any T whose `Reset()` is non-trivial.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14684
Test Plan:
**Benchmark — `db_bench seekrandom`.** Each op = 1 Seek + 100 Next on a fully cached 10M-key DB (16 B key, 100 B value, no compression).
| | ops/sec |
|---|---:|
| BEFORE | 42,131 |
| AFTER | 45,269 |
| **Δ** | **+3,138 (+7.4%)** |
Reviewed By: xingbowang
Differential Revision: D103070805
Pulled By: joshkang97
fbshipit-source-id: 6b184a1aa559649c575d95be28ab43bbee9ffe64
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14685
D100235293 and D102823090 migrated the generated RocksDB BUCK file from
`//folly/experimental/coro:*` to `//folly/coro:*`, but did not update
the buckifier script that generates it in internal_repo_rocksdb. The next release would revert the change.
Update buckify_rocksdb.py to match, so the generated BUCK file stays consistent with the folly coro migration.
Reviewed By: nmk70
Differential Revision: D103096688
fbshipit-source-id: 9055769ed5e9893397c7504ada22e21980f59dd2
Summary:
- Upgrade the Claude review workflow models and add complexity-based thinking budget selection for the main review run.
- Keep AI review comments tied to the reviewed commit and restructure long reviews so high-severity findings stay visible while details live in a collapsible section.
- Add early auto-trigger gating plus Codex review/comment workflows, including shared comment-building and parsing helpers.
## Testing
- Not run. The branch refresh was a no-op rebase onto current `upstream/main`, and no new code changes were made during this task.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14659
Reviewed By: pdillinger
Differential Revision: D102224743
Pulled By: xingbowang
fbshipit-source-id: 39c25494062cbcd52d3e85f8a859b5a3907c758e
Summary: Migrate coro references across the repo to the non shim version.
Differential Revision: D102823090
fbshipit-source-id: 3bec59aed1f2645139a1ad9c18363e6af462e61f
Summary:
The previous pre-push hook auto-formatted, committed, and re-pushed on behalf of the user. This was fragile: it's not clear whether the format fix should amend the current commit or create a new one. Adding a commit breaks populating the summary for creating a new PR. Also, the hook's internal re-push would drop flags like --set-upstream from the original command. Replace with a simple check-only approach that blocks the push and tells the user what to fix.
Also add a new check for untracked source files (.cc, .h, .py, etc.) in tracked directories (excluding third-party/). These typically indicate files that were forgotten in the commit, which would cause the pushed code to fail to build.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14680
Test Plan: manual
Reviewed By: xingbowang
Differential Revision: D102855227
Pulled By: pdillinger
fbshipit-source-id: 7dc2c4e7a2b2c392bf8da74d7ea43883c8c075a9
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14676
When fast_sst_open is disabled, RocksDB was still passing previously-persisted
file_open_metadata from the MANIFEST to NewRandomAccessFile. This could cause
failures when the metadata becomes stale (e.g. expired filesystem credentials).
This change gates the consumption of file_open_metadata in
TableCache::GetTableReader on the fast_sst_open option. When fast_sst_open is
false, previously persisted metadata is ignored and not passed to the filesystem
via FileOptions::file_metadata.
The fast_sst_open flag is threaded from MutableDBOptions through VersionSet ->
ColumnFamilySet -> ColumnFamilyData -> TableCache at construction time, ensuring
the gate is active before any table readers are opened during recovery. Dynamic
changes via SetDBOptions are also propagated to all existing TableCache instances.
Reviewed By: mszeszko-meta, xingbowang
Differential Revision: D102735581
fbshipit-source-id: 9a2c4dc0644a2f65c36b2468605df57779e127cd
Summary:
Fix false internal warnings about skipped tests (seen on D102718613).
ROCKSDB_GTEST_SKIP signals a gap in test coverage due to the build/execution
environment, while ROCKSDB_GTEST_BYPASS is for intentionally and permanently
omitting certain parameterizations. These six tests skip based on the test
parameterization (forward vs reverse, primary vs secondary mode), not due to
any environment limitation, so BYPASS is the correct macro.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14679
Test Plan: these are tests, look at internal signals
Reviewed By: joshkang97
Differential Revision: D102833346
Pulled By: pdillinger
fbshipit-source-id: e57bd1e217ca63aaf418b68c788563e67bbb07e3
Summary:
- External Table is only PUTs with no seqno, so we can route it to the simpler more efficient `GetContext::SaveValue`.
- `SstFileReader::Get` was allocating a string to append key footer, instead use LookUpKey to allocate on stack for short keys.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14673
Test Plan: CI passes
Reviewed By: anand1976
Differential Revision: D102659533
Pulled By: joshkang97
fbshipit-source-id: e55127548c9e4b7bdeb63c46acfdb8eb5883db14
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14672
This fixes the forward path introduced in the original pull request: https://www.internalfb.com/diff/D102409405
That change tried to avoid using user-owned `iterate_upper_bound_` memory by reading `iter_.key()` when the internal iterator was still valid and using it as the exclusive end key for the synthetic range tombstone. That is not safe: after the upper-bound break, `iter_.key()` is only the current merged child position, not a key the bounded scan proved safe to read or use as a range endpoint.
Example: run a prefix-bounded scan over prefix `b` with tombstones `ba`, `bb`, `bc`, `iterate_upper_bound = "bd"`, live key `be` in an SST, and later same-prefix key `bz` in the memtable. The SST child can drop out when it notices `be >= bd`, while the memtable child is not upper-bound aware and can still surface `bz`. If cleanup uses `iter_.key() == "bz"`, it inserts `[ba, bz)` and covers the live `be`, even though that key was never safe for the bounded scan to reason about.
The fix is to always end the forward range at `saved_key_`, which is the last tombstone user key the iterator actually proved deleted. That is conservative but safe. The regression test builds the mixed SST/memtable example above and asserts that the live `be` remains visible. The diff also re-enables randomized `min_tombstones_for_range_conversion` in `db_crashtest.py` and forces it back to `0` when `use_multiscan == 1`, since multiscan still does not support read-path range conversion.
Reviewed By: xingbowang
Differential Revision: D102493855
fbshipit-source-id: 45bdb86464268e2c4fa2ea735dbb059a5e054f28
Summary:
Replace raw owning pointers with std::unique_ptr for StreamingCompress/Uncompress in Create functions and log::Writer/Reader, eliminating manual delete calls in destructors. Also update the StreamingCompressionTest to use unique_ptr for its local StreamingCompress, StreamingUncompress, and MemoryAllocator objects.
Bonus: improve error message for missing/broken ruby (new build requirement)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14665
Test Plan: ./log_test - all 211 tests pass.
Reviewed By: xingbowang
Differential Revision: D102381893
Pulled By: pdillinger
fbshipit-source-id: 02806b46c8371474a969d6fa3012fff5eb7886c3
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14662
When a file becomes obsolete, `ReleaseObsolete()` calls `ReleaseAndEraseIfLastRef()` to remove it from the table cache. However, if concurrent readers hold references to the cache entry, this is not the last ref, so the entry stays. When those readers later call plain `Release()`, the entry becomes unreferenced but remains in the cache — there is no mechanism to trigger cleanup.
The fix is to call `cache->Erase()` in `ReleaseObsolete()` instead of relying solely on `ReleaseAndEraseIfLastRef()`. `Erase()` marks the entry as Invisible in the cache. The cache's `Release()` implementation already checks `IsInvisible()` and erases Invisible entries when the last reference is released. This guarantees that obsolete file entries are cleaned up regardless of concurrent reader timing.
Also reverts the D102158618 workaround that reordered `EraseUnRefEntries()` before `TEST_VerifyNoObsoleteFilesCached` in `CloseHelper`. With the root cause fixed, the assertion correctly passes in its original position.
Reviewed By: pdillinger
Differential Revision: D102158618
fbshipit-source-id: 5796abee916dfbd99554a36d3bbf579842d2fb8b
Summary:
Seems like crashtests are failing more often now, disabling again to investigate.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14670
Reviewed By: archang19
Differential Revision: D102479287
Pulled By: joshkang97
fbshipit-source-id: 3f147f58b0cf2901ffe3b99e8681ed5a6085e7c8
Summary:
`blob_compression_opts` is dynamically changeable through `SetOptions()`, but
blob direct write cached its per-thread compressor by `CompressionType` only.
After updating `blob_compression_opts`, new blob records on an existing writer
thread could keep using stale compression settings.
This change threads `CompressionOptions` through the direct-write settings and
rebuilds the cached per-thread compressor whenever the published options for
that compression type change. When the options stay the same, the steady-state
cache behavior is unchanged.
It also adds a regression test that toggles the ZSTD checksum flag through
`SetOptions()` and inspects the raw blob records to confirm each direct-write
record used the latest compression options.
## Testing
- `timeout 60s ./db_blob_direct_write_test --gtest_filter='DBBlobDirectWriteTest.DirectWriteCompressionOptionsUpdateRebuildsCachedCompressor' --gtest_repeat=5`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14669
Reviewed By: jainraj91
Differential Revision: D102423376
Pulled By: xingbowang
fbshipit-source-id: 504243d72cf22e62097f7e88ac1cc36c5bf29eee
Summary:
kNoChecksum was not being tested in db_crashtest.py despite being a supported checksum type. Add it to the random selection to ensure crash test coverage of the no-checksum code path.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14667
Test Plan: Ran multiple blackbox and whitebox crash test validation runs with kNoChecksum forced and with random selection including kNoChecksum. All passed crash-recovery verification.
Reviewed By: hx235
Differential Revision: D102404837
Pulled By: pdillinger
fbshipit-source-id: 0120f1269c917dd6e347764f2bdc0095d46ff71a
Summary:
`DBIter::FindNextUserEntryInternal` previously used `iterate_upper_bound_` (user-controlled memory) as the exclusive end-key when flushing an accumulated tombstone run. That pointer is dangerous as it points to user memory space, which can be modified at any moment.
This PR removes that dependency: the end-key is always derived from RocksDB-owned data — either the live `iter_` key that broke the run, or `saved_key_` when `iter_` is exhausted.
The tradeoff here is that there is potentially 1 less tombstone to get covered in this path, which is reasonable.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14664
Test Plan: Updated unit tests, CI passes
Reviewed By: xingbowang
Differential Revision: D102409405
Pulled By: joshkang97
fbshipit-source-id: b2beed73e5fe5b955b2d782fc467eda637c4fcf2
Summary:
CONTEXT: The manifest validation on close feature (verify_manifest_content_on_close) detects corruption but does not increment any statistics counter, making it harder to monitor in production.
WHAT: Add a new ticker MANIFEST_VALIDATION_FAILURE_COUNT that is incremented each time content validation detects manifest corruption during DB::Close(). The counter fires per corruption detection, so it can increment up to 2 times per close (once on initial check, once after rewrite attempt). Updated all existing manifest validation tests to verify the counter value.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14657
Test Plan:
- All 7 manifest validation tests pass with new stat assertions
- 5x repeat with COERCE_CONTEXT_SWITCH=1 shows no flakiness
- Full version_set_test suite (212 tests) passes
Reviewed By: anand1976
Differential Revision: D102404260
Pulled By: dannyhchen
fbshipit-source-id: 21a0aa1ad8de12a935caf5642e41ccf2a47b46d9
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14658
Follow up to D101463511. Add a hook and a manager-aware overload of so custom CompressionManagers can provide human-readable names for custom compression types while preserving the existing generic fallback when no compatible manager is available.
Reviewed By: pdillinger
Differential Revision: D102201365
fbshipit-source-id: 0c7456bb9db2e54927a4349d12c035fc8b5ad562
Summary:
There is a separate way to handle incompatibility. Revert this.
- revert the temporary workaround from https://github.com/facebook/rocksdb/issues/14656 that serialized `InternalStats::CompactionStats::counts` with the pre-11.2 compaction-reason count
- restore remote compaction stats serialization to use the full `CompactionReason::kNumOfReasons` array size again
## Context
https://github.com/facebook/rocksdb/issues/14656 temporarily shrank the serialized `counts` array to keep remote compaction metadata readable across the 11.1/11.2 boundary. This follow-up removes that special case because the compatibility issue is being handled separately.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14663
Test Plan: - Not run in this metadata-prep task
Reviewed By: jaykorean
Differential Revision: D102376258
Pulled By: xingbowang
fbshipit-source-id: 55693ea25a97b70de9d53b0d6eebcbd88a325b1c
Summary:
Fixes a correctness bug in read-path range-tombstone synthesis when it races with `IngestExternalFile`. The synthesis path could insert a tombstone into the active memtable at a snapshot's sequence number, while ingestion installed an L0 SST at `LastSequence + 1` — a higher seqno than the synthesized tombstone. This breaks the main assumption of range tombstone reads that all lower levels have lower seqno.
The fix introduces a per-CF `port::RWMutex` (`ColumnFamilyData::ingest_sst_lock_`) plus a per-memtable `ingest_seqno_barrier_`. Ingestion takes the read lock and range tombstone synthesis **tries** to take a write lock.
If iterator lock is successful, then we have a new updated barrier seqno that we can validate the iterator seqno against. An added benefit is we no longer need to gate against empty memtable. This was originally added as an easy fix to prevent memtables from being inserted into while ingestion was happening.
## The bug, by example
`ReadPathRangeTombstoneTest.NewerPointInOlderFileStillVisible` (`db/db_iterator_test.cc:6929`):
1. L0 file `b@1, c@2, d@3`, then L0 file `Delete(b)4, Delete(c)5`.
2. Active memtable: `Put(z)6`. Snapshot taken at seq 6.
3. `IngestExternalFile({c → "vc_live"})` → installed at L0 with seq 7.
4. Iterator at snap 6 walks the deletion run and synthesizes `[b, d) @ seq 6` into the active memtable via `MemTable::AddLogicallyRedundantRangeTombstone`.
5. `Get("c")` at the latest snapshot: memtable returns covering tombstone (seq 6), `Version::Get` short-circuits, **never reads `c@7`** — returns `NotFound` instead of `"vc_live"`.
The invariant `Version::Get` relies on (memtable seqs ≥ any L0 seq for the same key) is broken because synthesis writes at the *snapshot's* seq while ingestion writes at `LastSequence + 1`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14654
Test Plan:
- Updated regression test
- Re-enable crashtests and manually run
| Flavor | Jobs |
|---|---:|
| `fbcode_blackbox_crash_test` | 200 |
| `fbcode_whitebox_crash_test` | 30 |
| `fbcode_asan_blackbox_crash_test` | 30 |
| `fbcode_tsan_blackbox_crash_test` | 30 |
| `fbcode_crash_test_with_atomic_flush` | 30 |
| `fbcode_crash_test_with_wc_txn` | 30 |
| `fbcode_crash_test_with_ts` | 30 |
| **Total** | **380** |
Reviewed By: xingbowang
Differential Revision: D102044512
Pulled By: joshkang97
fbshipit-source-id: 0c69187595edc5a5fa80be24bffdba710a92e56e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14637
Add public API ParseCompressionNameForDisplay() to render TableProperties::compression_name in human-readable form for both legacy and format_version >= 7 SST metadata, including BuiltinV2/compression-manager encodings and filtered no-compression markers for display.
Reviewed By: xingbowang
Differential Revision: D101463511
fbshipit-source-id: 51c44850183cb9757644a323376844adffc7a8c2
Summary:
- Keep remote compaction stats serialization compatible with RocksDB 11.1.
- Serialize `InternalStats::CompactionStats::counts` using the pre-11.2 compaction-reason count so remote compaction metadata remains readable across the version boundary.
- Preserve the existing formatting-only follow-up commit on the branch.
## Notes
- Needs a better mechanism to make OptionTypeInfo::Array ser/des friendly. Will follow up after 11.2 release.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14656
Reviewed By: pdillinger
Differential Revision: D102071507
Pulled By: xingbowang
fbshipit-source-id: 6eeb87d86494f528f0d1ce46ce4c3e8e7dd2da53
Summary:
This reverts commit `bad2d5b0af7e160de5ca58869f40941af0f9da95`.
The reverted change switched fbcode Buck dependencies from `//folly/experimental/coro:*` to `//folly/coro:*` in:
- `BUCK`
- `buckifier/buckify_rocksdb.py`
That change broke the internal build, so this PR restores the previous dependency paths.
## Testing
- Not run locally; this task only fetched, rebased, and verified the existing revert branch.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14652
Reviewed By: archang19
Differential Revision: D101980062
Pulled By: xingbowang
fbshipit-source-id: 9a6edf14c226caf87e2c63c7f2806d409dc809ef
Summary:
**Summary:**
Assert the DB::Open status before asserting on the DB pointer in FaultInjectionTest::OpenDB(). This avoids aborting the test process when reopen fails and preserves the underlying RocksDB error for ASSERT_OK(OpenDB()). This is to deal with recent failures on a host with nearby tests failing with space issue already.
```
[ RUN ] ExternalSSTFileBasicTest.LargeSizeSstFileWriter
db/external_sst_file_basic_test.cc:3303: Failure
sst_file_writer.Finish()
IO error: No space left on device: While appending to file: /dev/shm/rocksdb_testt/run-external_sst_file_basic_test-shard-3/external_sst_file_basic_test_578578_8746831521190564136/large_key.sst: No space left on device
[ FAILED ] ExternalSSTFileBasicTest.LargeSizeSstFileWriter (8551 ms)
[ RUN ] FaultTest/FaultInjectionTestSplitted.FaultTest/2
fault_injection_test: db/fault_injection_test.cc:269: rocksdb::Status rocksdb::FaultInjectionTest::OpenDB(): Assertion `db_ != nullptr' failed.
Received signal 6 (Aborted)
```
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14646
Test Plan: Test changes only
Reviewed By: anand1976
Differential Revision: D101742874
Pulled By: hx235
fbshipit-source-id: de3b9349d9d067dd8f538b44ac08d108924faed3
Summary:
**Summary:**
When remote compaction is enabled with skip_stats_update_on_db_open=true, the remote worker's secondary DB skips UpdateAccumulatedStats(), leaving FileMetaData fields (num_entries, num_range_deletions) at 0. This breaks standalone range deletion file filtering in
FilterInputsForCompactionIterator() (https://github.com/facebook/rocksdb/blob/b374ba5968bdd6b16ff58918c561781c8bdab442/db/compaction/compaction.cc#L1085 + https://github.com/facebook/rocksdb/blob/b374ba5968bdd6b16ff58918c561781c8bdab442/db/version_edit.h#L462) , causing the primary and remote worker to disagree on input key count and triggering a "Compaction number of input keys does not match number of keys processed" corruption error.
Repro test (should be landed with the fix in the future): https://github.com/hx235/rocksdb/commit/aa1225c69cd6b6e2d2af735e1d1d9b147edfd6ae
```
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from CompactionServiceTest
[ RUN ] CompactionServiceTest.StandaloneRangeDeletionWithSkipStatsUpdateOnOpen
db/compaction/compaction_service_test.cc:2970: Failure
s
Corruption: Compaction number of input keys does not match number of keys processed. Expected 0 but processed 4. Compaction summary: Base version 15 Base level 5, inputs: [20(1280B)], [18(1270B filtered:true) 19(1270B filtered:true)]
[ FAILED ] CompactionServiceTest.StandaloneRangeDeletionWithSkipStatsUpdateOnOpen (1257 ms)
[----------] 1 test from CompactionServiceTest (1257 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1257 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] CompactionServiceTest.StandaloneRangeDeletionWithSkipStatsUpdateOnOpen
1 FAILED TEST
```
This can happen even when standalone range deletion ingestion is disabled in the current run, because such files may have been ingested in a previous crash test run with different randomized options. So we disable skip_stats_update_on_db_open unconditionally when remote compaction is active until the real fix lands .
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14647
Test Plan:
1. Verify skip_stats_update_on_db_open is forced to 0 with remote compaction
2. Verify skip_stats_update_on_db_open can still be 1 without remote compaction:
Reviewed By: anand1976
Differential Revision: D101744943
Pulled By: hx235
fbshipit-source-id: c1dcd8943433e8a9dfb7385c998be2c339e3b859
Summary:
**Summary:**
Temporarily disables inplace_update_support in db_crashtest.py. See the db_crashtest.py new comments for mow.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14645
Test Plan: - Run db_crashtest.py without passing inplace_update_support option, with short interval, and verify each run shows inplace_update_support is false in the db_stress command line.
Reviewed By: joshkang97
Differential Revision: D101739997
Pulled By: hx235
fbshipit-source-id: 39936b8282e392e688afea5e9bd074b60138f210
Summary:
Restore the iterator contract for blob-backed wide-column entities by eagerly materializing all blob-backed columns before a `DBIter` entry is exposed as valid. Also capture similar error inside compaction filter V4, so that the error is captured a surfaced up like older compaction filter V3.
## Problem
Lazy wide-column blob resolution let `DBIter` report `Valid() == true` before all columns were actually prepared. Callers could read `value()` successfully, then hit a blob-read failure in `columns()`, which flipped the iterator to invalid after the entry had already been observed as valid.
## Solution
- Restore back to old behavior.
- Resolve and materialize all blob-backed wide columns during iterator positioning.
- Keep `columns()` as a pure accessor once `Valid()` is true.
- Add coverage for eager blob resolution on iterator scans.
- Add coverage for blob resolution failures invalidating the iterator before the entry is exposed.
- Capture the same error in compaction filter and set db to bg error status if the error is not retriable.
## Next step
If we want lazy iterator resolution in the future, we will need an API that can surface lazy resolution failures explicitly instead of hiding I/O and status transitions inside value() or columns().
## Testing
- `make -j14 db_wide_blob_direct_write_test`
- `timeout 60s ./db_wide_blob_direct_write_test --gtest_filter='*DirectWriteIteratorValueScanEagerlyResolvesBlobColumns:*DirectWriteIteratorBlobResolutionErrorInvalidatesEntry'`
## Task
T265294130
# Bonus fix
## Non-compaction blob filtering fixes
### 1. Flush-time wide-entity lazy blob resolution now works correctly
Blob direct-write can leave wide-entity blob references in memtables before flush. Flush already runs through `CompactionIterator`, but outside a real compaction it did not have blob read support. As a result, `FilterV4` lazy resolution on flush could fail immediately with
`NotSupported("Blob fetcher not available")` instead of either:
- resolving the blob successfully, or
- surfacing the real blob read error and failing the flush
This stack fixes that by plumbing blob read support into non-compaction table-file creation. `BuildTable()` now gives `CompactionIterator` enough context to construct a `BlobFetcher` for flush/recovery table building, including write-path fallback for direct-write blob files
that are not yet manifest-visible.
With that in place:
- flush-time `FilterV4` can lazily resolve blob-backed wide columns
- if resolution fails, the failure is latched and flush fails after `FilterV4()` returns
- `bg_error` is set instead of silently preserving the entry
### 2. Plain blob fallback outside compaction now uses the resolved user value
For plain blob-backed values, `FilterBlobByKey()` is allowed to return `kUndetermined`, which is supposed to fall back to the normal value-based filter path. That fallback worked in compaction, but the non-compaction `BuildTable()` path still treated plain blob indexes as
unsupported/corrupt because it could not read the blob.
This stack fixes that behavior for flush/recovery table-file creation:
- when `FilterBlobByKey()` returns `kUndetermined`, RocksDB now eagerly resolves the plain blob value
- `FilterV2`/`FilterV3`/`FilterV4` then see the actual user value as `ValueType::kValue`, not the `BlobIndex` encoding
- legacy filters therefore behave the same way outside compaction as they already do inside compaction
Wide-column entities still use the `FilterV4` lazy resolver path. The plain-blob fix is specifically about restoring the documented fallback behavior for blob-backed `kValue` records outside compaction.
## Why this matters
Without these last two commits, flush-time filtering still had two inconsistent behaviors:
- wide-entity lazy resolution could not actually read blob-backed columns in the direct-write case
- plain blob-backed values could not fall back to normal value-based filtering outside compaction
So even after fixing iterator validity and compaction error propagation, non-compaction table-file creation still had remaining contract holes. These commits make flush/recovery behave consistently with compaction.
## Coverage added
The tests added in these commits cover:
- flush-time plain-blob `FilterV3` fallback using the resolved user value
- flush-time plain-blob `FilterV4` fallback using the resolved user value
- flush-time direct-write wide-entity lazy resolver success
- flush-time direct-write wide-entity lazy resolver failure on missing blob file, including flush failure and `bg_error` latching
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14632
Reviewed By: anand1976
Differential Revision: D101425233
Pulled By: xingbowang
fbshipit-source-id: 695b2df5189033d30309b35849815e31fd965664
Summary:
Fix nightly build regressions introduced by recent CI/toolchain changes.
This PR includes two parts:
- CMake/link fixes for the Folly and Folly Lite nightly jobs
- a targeted nightly workflow fix for the clang-21 ASAN/UBSAN + Folly job
Changes:
- link gflags explicitly for USE_FOLLY CMake tool and benchmark targets
- resolve USE_FOLLY_LITE glog via its installed library path instead of bare -lglog
- in the clang-21 nightly job, build Folly/getdeps with gcc/g++ instead of inheriting clang-21 for third-party dependency builds
- disable ccache only for that standalone Folly build step so getdeps/CMake does not inject the broken ccache compiler launcher for ASM
Root cause:
- recent CI/container changes exposed missing explicit link dependencies in the CMake Folly paths
- the clang-21 nightly job exported CC/CXX at job scope, so Folly getdeps inherited clang-21 into libiberty/binutils build logic that expects GCC-style driver behavior such as -print-multi-os-directory
- the same standalone Folly build path also misbehaved when ccache was auto-detected and used as a compiler launcher for assembler-with-cpp inputs
Verification:
- make format-auto
- git diff --check
- local CMake configure sanity check for default config
- upstream nightly reruns used to confirm failure signatures before and after the first-round fixes
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14609
Reviewed By: anand1976
Differential Revision: D100695693
Pulled By: xingbowang
fbshipit-source-id: 703546ad3ddc518ab80c936442709d65fe2d22af
Summary:
The prior fix (https://github.com/facebook/rocksdb/issues/14586) disabled read-path range tombstone synthesis at the `DBIter` level when `table_filter` was set. However, synthesized tombstones persist in the memtable beyond the lifetime of the iterator that created them. A prior unfiltered iterator can synthesize a range tombstone that then silently affects a subsequent filtered iterator's results — the filtered scan sees the tombstone but not the SSTs it was derived from, allowing hidden SST state to corrupt the filtered view. See failed crash test in T264151327.
This PR takes the stronger approach of rejecting iterator creation outright (`InvalidArgument`) when `ReadOptions::table_filter` is used on a column family with `min_tombstones_for_range_conversion > 0`. This eliminates the entire class of interaction bugs between the two features rather than trying to suppress conversion in individual code paths.
## Example
- `min_tombstones_for_range_conversion = 2`
- L1 SST_keep: `Put(a), Put(b), Put(c)` (`c` is the live boundary)
- L0 SST_dels: `Delete(a), Delete(b)` (2 contiguous tombstones, newer)
### Step 1 — Iterator A (no filter)
A walks `Del(a) Del(b)` (2 contig) then `c` (live).
Synthesizes range tombstone `[a, c)` into the active memtable.
### Step 2 — Iterator B (`table_filter` skips SST_dels)
B's filter excludes SST_dels, so `a, b` from SST_keep should appear live.
But the memtable now holds range tombstone `[a, c)` from step 1, which
applies to B regardless of `table_filter`. B sees only `c` —
**a, b are silently hidden**.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14618
Test Plan:
- `TableFilterNotAllowed` test validates the rejection path end-to-end: unfiltered scan synthesizes the tombstone, filtered iterator is rejected with `InvalidArgument`, and the full-DB view remains correct via `VerifyIteration`.
- Crash test sanitization ensures `db_stress` won't hit the incompatible configuration.
- Run crash test sanitization unit test: `python3 tools/db_crashtest_test.py`
- Run iterator tests: `make db_iterator_test && ./db_iterator_test --gtest_filter="*TableFilterNotAllowed*"`
Reviewed By: xingbowang
Differential Revision: D100873156
Pulled By: joshkang97
fbshipit-source-id: dd0d123d94b1bfda2a4a5040681fcc7cf14f760e
Summary:
There is an assumption in the code that within a given prefix all keys can be seen after a seek to that prefix. The problem occurs when there is a Next() followed by a Prev() back into the same prefix. The merging iterator actually uses SeekForPrev during Prev for non-child iterators, and as a result changes the prefix being used. Now that we are back to our original prefix, the view is incomplete.
The fix is to simply disable range tombstone conversion in this legacy prefix mode.
### Example
Use a 1-byte prefix extractor, so the prefix is just the first character.
Think of the merged iterator as combining two children:
- Child A: `b8`
- Child B: `b7, c1`
Global sorted order is:
```text
b7, b8, c1
```
So:
- predecessor of `c1` should be `b8`
- predecessor of `b8` should be `b7`
## Sequence
Start in legacy prefix mode:
- `total_order_seek = false`
- `prefix_same_as_start = false`
Now do:
1. `Seek("b8")`
2. `Next()`
3. `Prev()`
## What should happen
```text
Seek("b8") -> b8
Next() -> c1
Prev() -> b8
```
Because in the global order, `b8` is immediately before `c1`.
## What the buggy iterator did
```text
Seek("b8") -> b8
Next() -> c1
Prev() -> b7
```
So it skipped `b8`.
## Why it happens
When `Prev()` switches direction, the merging iterator tells all non-current
children to `SeekForPrev(current_key)`.
At that moment:
- current key is `c1`
- so the other children get `SeekForPrev("c1")`
But in legacy prefix mode, those child seeks are allowed to use prefix
filtering. So a child that only contains `b*` keys can effectively say:
```text
"I was asked for prefix c; I do not have prefix c."
```
That child drops out instead of returning its real global predecessor `b8`.
Then the current child simply moves back from `c1` to `b7`, and the merged
result becomes `b7`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14625
Test Plan: Update unit tests to disable feature.
Reviewed By: xingbowang
Differential Revision: D101259748
Pulled By: joshkang97
fbshipit-source-id: 23e19c470f931406f96a34a7baebca89e91c7e39
Summary:
- meter blob garbage during `BuildTable()` when flush input can contain direct-write blob references, so overwrite elision and flush-time compaction filters register garbage in the manifest
- plumb `BlobFileGarbage` through flush and recovery manifest edits even when the flush produces no SST output
- add wide-column direct-write tests covering TTL-filtered flushes with both mixed live/expired records and all-expired input
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14623
Reviewed By: anand1976
Differential Revision: D101212841
Pulled By: xingbowang
fbshipit-source-id: e5803ddaf17b5dfd92312e42191b76e311257f3b
Summary:
This fixes the Claude review workflow YAML parse error and adds a CI validation step for GitHub Actions workflow YAML via `make check-
workflow-yaml`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14624
Reviewed By: archang19
Differential Revision: D101225058
Pulled By: xingbowang
fbshipit-source-id: 0f371dcbd70ece2c25220c96e43305d9665554b1
Summary:
Detect out-of-space failures from combined db_stress stdout/stderr in the Python wrapper so both OpenAndCompact stdout failures and verification stderr failures trigger the same diagnostics.
When a match is found, print filesystem usage for /dev/shm and the db roots, then summarize per-directory and per-extension usage to make it clear which files consumed space. Add unit coverage for the failure matcher and suffix accounting.
This help triage any regression in additional file usage in stress test.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14619
Reviewed By: archang19
Differential Revision: D100984310
Pulled By: xingbowang
fbshipit-source-id: 23765ff405f0e64382e601b0da173ab6b37dba6d
Summary:
`RangeTreeLockManager::CompareDbtEndpoints()` called `Comparator::Compare()` on range lock endpoint keys that never contain user-defined timestamps. With a timestamp-aware comparator (`timestamp_size() > 0`), this caused assertion failures in debug builds and silent endpoint misordering in release builds.
Replace `Compare()` with the 4-argument `CompareWithoutTimestamp()` using `a_has_ts=false, b_has_ts=false`, which is the correct contract for serialized range lock endpoints (format: `[1-byte suffix][key bytes]`, no timestamp).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14611
Test Plan:
- New test `RangeLockWithTimestampComparator` reopens with `BytewiseComparatorWithU64Ts` and exercises range lock acquisition, conflict detection, and non-overlapping success with short keys.
- Verified RED (assertion failure) before fix, GREEN after.
- Full `range_locking_test` suite passes (16/16).
- Stress tested with `COERCE_CONTEXT_SWITCH=1 --gtest_repeat=5`.
Reviewed By: xingbowang
Differential Revision: D100775201
Pulled By: laurynas-biveinis
fbshipit-source-id: 58e3846e62e9b3cc5bdc69557458b245f90b3967
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14596
When DBOptions::fast_sst_open is enabled, RocksDB retrieves opaque file system
metadata for SST files after flush, compaction, and external file ingestion via
FSRandomAccessFile::GetFileOpenMetadata(). This metadata is persisted in the
MANIFEST using a new forward-compatible NewFileCustomTag (kFileOpenMetadata = 17),
and passed back to the file system via FileOptions::file_metadata on subsequent
file opens. This accelerates DB open time on remote storage systems by allowing
the file system to skip expensive metadata RPCs.
The feature is gated by DBOptions::fast_sst_open (default false). Everything
works seamlessly regardless of the option setting, file metadata support, or
presence/absence of the metadata in the MANIFEST. The MANIFEST change is backward
compatible - older RocksDB versions safely ignore the new tag.
Reviewed By: xingbowang
Differential Revision: D100220973
fbshipit-source-id: f52de9dd853a50653b3297ab4a37a868fe41cc04
Summary:
Temporarily disable the feature until all fixes land and crash tests are stabilized.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14617
Reviewed By: xingbowang
Differential Revision: D100853151
Pulled By: joshkang97
fbshipit-source-id: f2c9018f0b55891f6c0623902c3b961bc7f623aa
Summary:
Blackbox crash tests intentionally terminate `db_stress` with `SIGTERM` on timeout. When `io_uring` is enabled, that shutdown can emit the expected
`PosixRandomAccessFile::MultiRead: io_uring_submit_and_wait returned terminal error: -9.`
message on `stderr`, which currently makes the timeout path look like a real failure.
This change filters only that known post-`SIGTERM` stderr after a timeout when the process output confirms the `SIGTERM` handler ran. Any other stderr is still surfaced and fails the test, while the ignored lines are appended to `stdout` so the signal remains visible in logs.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14613
Test Plan:
- Added unit tests covering fully ignored post-`SIGTERM` stderr
- Added unit tests covering mixed stderr where unrelated lines must still fail
- Added unit tests covering guard conditions when the run did not time out or did not print the `SIGTERM` marker
- Not run (not requested)
Reviewed By: archang19
Differential Revision: D100806204
Pulled By: xingbowang
fbshipit-source-id: 01248a371afae91bb43df55f88400baf155373f5
Summary:
- add a `docs/components/` landing page and a stress-test docs index
- document the `db_stress` expected-state trace/replay lifecycle, file invariants, and prefix-recovery contract in `expected_state_trace.md`
- align `db_stress` comments with the restore semantics: missing trace entries are fatal, while extra tail trace entries are tolerated
- keep the new docs tree trackable and point repo instructions at the new component-docs entrypoint
## Testing
- Not run (documentation and comment updates only)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14612
Reviewed By: joshkang97
Differential Revision: D100797173
Pulled By: xingbowang
fbshipit-source-id: 25be8c6239b9fdd84580818efe7520c371f9a46b
Summary:
BlockBuilder was somewhat inconsistent in its treatment of Slices whose size exceeds 4GB, which in a random corruption case could lead to (for example) serializing only the bottom 32 bits of a value size (uncorrupted) but appending the full multi-GB (corrupted) size. Because this is inner loop code, we don't want to pay CPU for extra conversions, data movements, or Status plumbing (already ruled out by Xingbo).
In this change we make BlockBuilder more internally consistent and lift the 32-bit size requirement to callers (of a few functions specifically, for now). To ensure that's satisfied, I've added additional checks near the perimeter of RocksDB to ensure keys and values do not exceed 4GB, plus an extra random corruption or backstop check in BlockBasedTableBuilder. In detail,
BlockBuilder (block_builder.cc/h):
* Add API comments documenting the < 4GB assumption on Add/AddWithLastKey
* Add debug assertions verifying input slice sizes < 4GB
* Simplify AddWithLastKeyImpl to use uint32_t locals, reducing static_cast
* Use only bottom 32 bits when appending derivative slices for consistency
* Update MaybeStripTimestampFromKey to also truncate to 32-bit size
* Add FIXME comments where buffer_/values_buffer_ sizes are truncated
BlockBasedTableBuilder (block_based_table_builder.cc):
* Add value size check (> uint32_t max) as a safety net against random corruption, since we have seen such corruptions in production
WriteBatch (write_batch.cc):
* Tighten existing key size checks to account for kNumInternalBytes (8-byte internal key suffix), using new kMaxWriteBatchKeySize constant
* Add missing size checks to Delete, SingleDelete, and DeleteRange (both Slice and SliceParts variants)
SstFileWriter (sst_file_writer.cc):
* Add key and value size checks in AddImpl and DeleteRangeImpl, which bypass WriteBatch and go directly to the table builder
MergeHelper (merge_helper.cc):
* Add merge result size check (> uint32_t max) in both overloads of TimedFullMergeImpl, returning Corruption if exceeded
* Add PartialMergeMulti result size check in MergeUntil
CompactionIterator (compaction_iterator.cc):
* Add size check on compaction filter output values (kChangeValue and kChangeWideColumnEntity), returning Corruption if > 4GB
meta_blocks.cc:
* Use Slice with uint32_t-truncated sizes in PropertyBlockBuilder::Finish to handle potentially oversized user property collector output
Needed follow-up:
* Check for blocks that are mis-encoded due to overflowing 32 bits for restart point offsets (and similar). See FIXME comments in block_builder.cc for why this is tricky.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14461
Test Plan:
New extreme-size unit tests, along with some testing infrastructure improvements:
Added test::HasBigMem() (in test_util/testharness.h) which returns true when the system has ≥128GB RAM (via sysconf(_SC_PHYS_PAGES) on Linux/macOS) or when ROCKSDB_BIGMEM_TESTS is set. All extreme-size tests use HasBigMem() to skip gracefully on smaller machines rather than being permanently disabled.
Where possible, tests use MemMapping::AllocateLazyZeroed() to supply large keys/values as Slices backed by anonymous mmap (cleaner with new MemMapping::AsSlice()). On Linux, read-only access to these pages maps to the shared kernel zero page, so the source data consumes no physical RAM — only the destination copy (e.g., WriteBatch::rep_) materializes, cutting peak memory roughly in half vs. std::string.
Tests (all enabled, skip via HasBigMem()):
./write_batch_test --gtest_filter='*LargeKeyValueSizeLimit*'
./external_sst_file_basic_test --gtest_filter='*LargeSizeSstFileWriter*'
./merge_test --gtest_filter='*LargeMergeResultRejected*'
./merge_helper_test --gtest_filter='*LargePartialMergeResultRejected*'
./db_compaction_test --gtest_filter='*CompactionFilterLargeValueRejected*'
All 5 pass (verified on a 128GB+ machine). On smaller machines, all 5 bypass cleanly with "insufficient memory for reliable continuous testing".
Reviewed By: xingbowang
Differential Revision: D96521899
Pulled By: pdillinger
fbshipit-source-id: 70f4b5e6a23ab074d60e653fbb7ddc5edbe162ab
Summary:
Reverts the two commits https://github.com/facebook/rocksdb/pull/14578https://github.com/facebook/rocksdb/pull/14575 that changed tracer behavior. The correct behavior is that trace should be written to before WAL.
Although a trace file may not be fully consumed, it is recycles after each crash, so there is no need to worry about applying an uncomited write. The original crash test was a false positive for a different error.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14600
Reviewed By: xingbowang
Differential Revision: D100397989
Pulled By: joshkang97
fbshipit-source-id: 3da6fb80f682ac6f9529c1a76eaf169e38cf2477
Summary:
T263957043 reproduces in whitebox crash testing when DBIter converts a run of point deletes into a range tombstone while reading with an older user-defined timestamp. The scan can observe an older delete for the boundary keys but miss newer live versions of the same keys and interior keys, so the synthesized tombstone is based on partial visibility and later hides valid max-timestamp reads.
## Problem
Read-path range tombstone conversion (`min_tombstones_for_range_conversion`) assumes the scan observes all interior live keys between the start and end of a contiguous delete run. With user-defined timestamps, a read at an older timestamp can see deletes but miss newer Puts for the same keys. The synthesized range tombstone then incorrectly covers those newer versions, causing data loss on subsequent max-timestamp reads.
## Fix
Gate read-path range conversion on full timestamp visibility. The optimization is now only enabled when:
1. There is no `table_filter` (existing guard — SSTs hidden by filter can break the contiguity assumption)
2. There is no `iter_start_ts` (time-travel scans see a subset of versions)
3. Either no read timestamp is set, or the read timestamp is the max timestamp (all `0xff` bytes)
This is done via a new helper `HasFullTimestampVisibility()` checked at `DBIter` construction time.
## Test Changes
- Updated all existing UDT test variants in `ReadPathRangeTombstoneTest` to read at max timestamp so the optimization remains covered where it is valid.
- Added `UDTOlderTimestampDisablesInsertion`: a regression test that writes at ts=1, deletes at ts=2, writes again at ts=3, then reads at ts=2. Verifies no range tombstone is synthesized and a subsequent max-timestamp read still sees all live values.
## Validation
- `make -j192 db_iterator_test`
- `./db_iterator_test --gtest_filter='*ReadPathRangeTombstoneTest*'`
- `./db_iterator_test --gtest_filter='*UDTOlderTimestampDisablesInsertion*' --gtest_repeat=5`
- Reran the original whitebox crash-test seed from T263957043 against the patched tree; crash-recovery verification passed
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14595
Reviewed By: joshkang97
Differential Revision: D100235999
Pulled By: xingbowang
fbshipit-source-id: 7a287f3c7fdc47428fda0ab89eedd5d43f663985
Summary:
TsanAnnotateMappedMemory() always builds a TsanMappedMemoryInfo payload
for TEST_SYNC_POINT_CALLBACK(), but in builds where sync points compile to a
no-op the local variable becomes unused and Clang fails with
-Werror,-Wunused-variable.
Move the explicit (void)info cast so it applies regardless of whether
__SANITIZE_THREAD__ is enabled. This keeps the helper warning-free in:
- non-TSAN builds
- builds where TEST_SYNC_POINT_CALLBACK expands to nothing
- TSAN builds that still want the SyncPoint payload for tests
This was missed by CI because the warning only appears in the
COMPILE_WITH_TSAN=1 + NDEBUG/release configuration. Our CI matrix covers
release builds without TSAN and TSAN builds in debug mode, but not the
TSAN+NDEBUG combination.
No behavior change is intended. The helper still:
- exposes the mapping metadata to SyncPoint-based tests
- calls AnnotateNewMemory() in TSAN builds
- remains a no-op with respect to runtime behavior in non-TSAN builds
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14597
Differential Revision: D100333855
Pulled By: xingbowang
fbshipit-source-id: 3ff0b0c8a51b4959b7a36984e40ac31e49da82eb
Summary:
TSAN was reporting false data-race warnings after the kernel reused a virtual address for a new mapping while TSAN still associated that address range with the old mapping.
This showed up in two RocksDB paths:
- `io_uring` setup on helper threads vs later `io_uring` `MultiRead` access
- `io_uring` setup vs file mmap reads when `use_mmap_reads` is enabled
## Changes
Introduce `TsanAnnotateMappedMemory()`, which calls `AnnotateNewMemory()` as soon as a fresh mapping exists so TSAN drops any stale shadow state for the recycled address range.
Apply the helper to:
- io_uring SQ/CQ/SQE mappings created by `CreateIOUring()`
- `PosixFileSystem` mmap-read mappings
- `PosixFileSystem` raw memory-mapped file buffers
- `PosixMmapFile` writable mmap region growth
Also add deterministic regression tests that force virtual-address reuse with `MAP_FIXED` for both the io_uring and mmap-read cases. The tests pass mapping metadata over a pipe so teardown/remap ordering is deterministic without introducing TSAN-visible synchronization that would mask the problem.
## Testing
- `COMPILE_WITH_TSAN=1 make -j192 env_test`
- `env_test --gtest_filter='EnvPosixTest.SupportedOpsNoAsyncIOOnIOUringInitFailure:EnvPosixTest.IOUringAddressReuseNoTsanFalsePositive:EnvPosixTest.MmapReadAddressReuseNoTsanFalsePositive'`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14594
Reviewed By: archang19
Differential Revision: D100235746
Pulled By: xingbowang
fbshipit-source-id: 8ef8d85c08a7646540b9c11c3741978898a5af3d
Summary:
### Summary
Fix flaky Claude Code Review workflow failures caused by stale workflow_run SHAs.
Problem:
- The auto-review workflow is triggered from pr-jobs via workflow_run.
- In some cases, by the time the review job starts, the PR branch head has already advanced.
- Then Get PR info cannot find an open PR whose head SHA matches the older workflow_run.head_sha, so it fails with:
- Could not find PR for SHA ... after retries
- This creates unnecessary CI noise even though a newer review job will be triggered for the updated commit.
Solution:
- Treat “PR not found for this head SHA” as a stale run, not an error.
- In Get PR info, replace workflow failure with:
- a warning
- skip=true
- skip_reason=stale_sha:<sha>
- Gate all later auto-review steps on steps.pr_info.outputs.skip != 'true'
- Add a small logging step to report the skip reason
Result:
- stale auto-review runs exit successfully instead of failing
- CI becomes less noisy
- the newest commit still gets reviewed by the later workflow run
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14591
Reviewed By: mszeszko-meta
Differential Revision: D100190375
Pulled By: xingbowang
fbshipit-source-id: fa4a0479a10e953e52b3d99c2b483dae01d3e7fb
Summary:
Flush(), Sync(), and Fsync() on WritableFile and FSWritableFile were essentially undocumented. Added comprehensive API contracts covering durability guarantees (process crash vs. power failure), data readability after Flush(), the implication relationship (Sync implies Flush), and thread-safety (referencing IsSyncThreadSafe()). The contracts are written to be implementation-agnostic, avoiding assumptions about OS-level mechanisms, so they apply equally to local, remote, and virtual/wrapper filesystem implementations. Several such implementations have been audited for adherence.
Note that WritableFileWriter currently calls FSWritableFile::Flush() UNNECESSARILY for cases like writing SST files. This should be optimized away in follow-up assuming this interpretation of the contract is agreed upon.
Also removed the dead FileSystem::WriteLifeTimeHint enum, which was never referenced anywhere. The actively used enum is Env::WriteLifeTimeHint.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14590
Test Plan: Documentation-only change to public headers, plus removal of an unused enum with no references. No functional changes. Verified no compilation errors with existing tests.
Reviewed By: xingbowang
Differential Revision: D100183288
Pulled By: pdillinger
fbshipit-source-id: 7f1982c41e09fe39896dbc6cc328316be559ec4a
Summary:
Add Compressor::GetRecommendedParallelThreads() virtual method so that the compression subsystem can influence the parallel compression thread count used when building SST files. The base Compressor returns 0 (no opinion), while built-in compressors (via CompressorBase) return the parallel_threads value from their CompressionOptions. This gives CompressionManager a clean mechanism to override parallel_threads by customizing the CompressionOptions passed to GetCompressor() in its GetCompressorForSST() implementation.
The table builder now reads the thread count from the compressor after creation, rather than only from CompressionOptions directly. Hard structural constraints (partition_filters without decoupled mode, user_defined_index_factory) still force single-threaded compression regardless of the compressor's recommendation.
Also adds a sync point in MaybeStartParallelCompression for test observability, and compression_test coverage for the new functionality.
Bonus: extends CompressionManagerCustomCompression test to cover re-opening a DB at format_version=7 with a non-default compression manager that uses the built-in CompatibilityName ("BuiltinV2"). Verifies that data is readable after re-opening without the original manager, and that neither GetId() nor CompatibilityName() resolves to the custom manager via CreateFromString.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14580
Test Plan:
New unit tests in compression_test:
- GetRecommendedParallelThreads: verifies built-in compressors return the parallel_threads from their CompressionOptions for all supported compression types.
- CompressionManagerOverridesParallelThreads: end-to-end test with a custom CompressionManager that overrides parallel_threads from 1 to 4 in GetCompressorForSST, verified via sync point that parallel compression actually activates with the overridden thread count, plus data readback verification.
Existing parallel compression tests (DBCompressionTestMaybeParallel) continue to pass.
Reviewed By: xingbowang
Differential Revision: D99896343
Pulled By: pdillinger
fbshipit-source-id: 6b3a30856a78641714d33ec7ba2099f33533d3af
Summary:
Add `use_udi_as_primary_index` option to `BlockBasedTableOptions`. When
enabled, the UDI becomes the primary index — all reads (including
internal operations like compaction and VerifyChecksum) automatically
route through the UDI without needing `ReadOptions::table_index_factory`.
Both the standard binary search index and the UDI are always fully
built. The standard index serves as a safety fallback (e.g., for
backup/restore or rollback to a non-UDI configuration). A future
refactor will extract the index abstraction to allow skipping the
standard index build when the UDI is primary (see discussion below).
## Write path
- `UserDefinedIndexBuilderWrapper` always forwards `AddIndexEntry` and
`OnKeyAdded` to both the internal standard builder and the UDI builder
- New `udi_is_primary_index` table property marks primary-mode SSTs
- Validates incompatible options at `DB::Open` and builder creation:
partitioned index, partitioned filters, missing
`user_defined_index_factory`
## Read path
- `UserDefinedIndexReaderWrapper` defaults to UDI when `udi_is_primary_`,
even when `ReadOptions::table_index_factory` is null — this handles
the 15+ internal call sites that don't set `table_index_factory`
- `use_udi_as_primary_index` automatically enforces `fail_if_no_udi_on_open`
to prevent silent data loss if SSTs are opened without UDI support
## Rollback
Since the standard index is always fully populated, rollback from
primary mode is straightforward: set `use_udi_as_primary_index=false`.
No compaction required — SSTs written in primary mode are immediately
readable through the standard index.
## Public API
- `BlockBasedTableOptions::use_udi_as_primary_index` (default: false)
- `UserDefinedIndexBuilder::EstimatedSize()` — pure virtual, O(1) via
running counter in the trie implementation
## Bug fixes (issues https://github.com/facebook/rocksdb/issues/14560, https://github.com/facebook/rocksdb/issues/14561, https://github.com/facebook/rocksdb/issues/14562)
Fixed trie index correctness bugs that caused crash test failures:
- **Always-on seqno encoding**: `must_use_separator_with_seq_` is now
unconditionally true. Non-boundary separators store tag=0 (sentinel),
same-user-key boundaries store the real tag, and the last block stores
its real last-key tag. This fixes the `NonBoundaryTag` bug where
non-boundary separators with `kMaxSequenceNumber` caused the post-seek
correction to incorrectly advance past the correct block.
- **Standard index always built in primary mode**: An empty (stub)
standard index block caused behavioral divergence in
`BlockBasedTableIterator` under concurrent flush/compaction, leading to
`test_batches_snapshots` prefix scan inconsistencies.
## Stress test
- `use_udi_as_primary_index` flag randomized by `db_crashtest.py`
- Both primary and secondary UDI modes exercised in crash tests
- Trie index probability halved (~6%) per reviewer request
- Re-enables trie crash tests (disabled by https://github.com/facebook/rocksdb/issues/14559)
## Tests
- Parameterized `TrieIndexDBTest` on UDI mode (secondary vs primary)
- New factory-level tests: non-boundary separator seek, Prev within
overflow, SeekToLast with overflow, empty trie, overflow exhaustion,
all-scans-exhausted
- New DB-level tests: multi-CF coalescing iterator, GetEntity with
explicit snapshot, reverse iteration across same-user-key blocks,
non-boundary separator seek correctness, rollback from primary
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14547
Reviewed By: anand1976
Differential Revision: D99494181
Pulled By: xingbowang
fbshipit-source-id: ca52a0d5c0e523770c80e1fe2b9b5d50406b67bc
Summary:
Wide-column blob separation: lazy resolution through read, compaction, and write paths
Extend blob direct write to support wide-column entities (PutEntity), and add
lazy blob resolution for wide-column values across all read and compaction paths.
**Write path -- PutEntity blob separation:**
- BlobWriteBatchTransformer::PutEntityCF now extracts large column values
(>= min_blob_size) to blob files and serializes V2 entities with BlobIndex
references, matching the existing Put behavior.
- Add MaybePreprocessWideColumns() static helper to share blob extraction
logic between the WriteBatch transformer and the new PutEntity fast path.
- Add PutEntityFastPath() in DBImpl that preprocesses columns (sort, blob
extract, serialize) before calling WriteImpl, skipping the redundant
WriteBatch transformation pass. Trace batch preserves the original columns.
**Read path -- blob resolution for Get/MultiGet/Iterator:**
- GetContext::SaveValue resolves V2 entity blob columns eagerly: for
value (Get), resolves the default column's blob reference; for columns
(GetEntity), resolves all blob columns and re-serializes as V1.
- DBIter::SetValueAndColumnsFromEntity detects V2 entities, deserializes
with DeserializeV2, and eagerly resolves all blob columns via a new
ReadPathBlobResolver. Resolved values are cached in the resolver and
wide_columns_ Slices point into the cache, avoiding copies.
- Add ReadPathBlobResolver (new file) -- on-demand blob fetcher for the
read path with per-column caching, used by both DBIter and GetContext.
- BlobFetcher gains allow_write_path_fallback to read from in-flight
direct-write blob files not yet visible through Version (pre-flush reads).
- Memtable lookups for Get(key) on V2 entities with a blob default column
now return the blob index with is_blob_index=true, triggering the
existing BDW resolution in MaybeResolveWritePathValue.
- MaybeResolveWritePathValue (renamed from MaybeResolveDirectWriteBlobIndex)
now also resolves V2 entity blob columns for GetEntity/MultiGetEntity,
re-serializing as V1 after resolution.
**Compaction path -- filter, GC, and extraction:**
- CompactionIterator::InvokeFilterIfNeeded handles V2 entities: FilterV3
gets eagerly-resolved column values for backward compatibility; FilterV4
gets a CompactionBlobResolver for lazy on-demand resolution.
- Add CompactionFilter::FilterV4 with WideColumnBlobResolver* parameter
and SupportsFilterV4() opt-in. Default delegates to FilterV3.
- CompactionBlobResolver (new class) implements WideColumnBlobResolver
for the compaction path with stats tracking.
- ExtractLargeColumnValuesIfNeeded extracts inline columns to blob files
during compaction (entities without existing blob columns only).
- GarbageCollectEntityBlobsIfNeeded relocates blob values from old blob
files to new ones during compaction GC, with helpers FetchBlobsNeedingGC,
RelocateBlobValues, and SerializeEntityAfterGC.
- PrepareOutput unified entity deserialization: single DeserializeV2 call
reused by both filter and GC/extraction paths via entity_deserialized_
flag, avoiding redundant parsing.
**Merge path -- V2 entity base value resolution:**
- MergeHelper::MergeUntil, GetContext::MergeWithWideColumnBaseValue, and
DBIter::MergeWithWideColumnBaseValue resolve V2 blob columns before
calling TimedFullMerge, using ResolveEntityForMerge.
**Blob garbage accounting:**
- BlobGarbageMeter tracks blob file in/out flow for V2 entity blob
columns via ForEachBlobFileNumber, used for accurate GC decisions.
- FileMetaData::UpdateBoundaries tracks oldest_blob_file_number for
V2 entities, ensuring blob files referenced by entities are not
prematurely deleted.
**Serialization improvements:**
- WideColumnSerialization::SerializeV2Impl allocates serialized_blob_indices
only for actual blob columns (not all columns) and uses autovector for
name/value sizes.
- Add ForEachBlobFileNumber for lightweight blob file number extraction
without full deserialization.
- Add ResolveEntityForMerge helper for merge-path resolution.
- Add section-size validation in DeserializeV2Impl.
- Add empty blob index and column type validation.
- blob_column_resolver_util.h -- shared helpers (FindBlobColumn, FindInCache,
CacheInlinedBlob) used by both ReadPathBlobResolver and CompactionBlobResolver.
**Testing:**
- db_blob_direct_write_test: end-to-end PutEntity with BDW before/after flush,
verifying Get, GetEntity, MultiGetEntity, and Iterator.
- db_blob_index_test: ~1550 lines covering V2 entity blob resolution through
Get, GetEntity, MultiGet, Iterator, compaction filter (V3 compat and V4 lazy),
merge with blob base, and compaction GC/extraction.
- compaction_iterator_test: ~950 lines testing entity blob GC, extraction,
filter interaction, and combined GC+filter scenarios.
- db_wide_basic_test: ~1200 lines for wide-column lazy blob resolution through
all read paths plus compaction round-trips.
- db_open_with_config_test: ~450 lines for BDW entity config validation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14386
Reviewed By: anand1976
Differential Revision: D99739701
Pulled By: xingbowang
fbshipit-source-id: 6badd89b577f3054802eaaa654738468efb9dbdb
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14581
In `MultiScanIndexIterator::Seek()` Case 3, when re-entering a scan range
after all ranges were exhausted, `block_idx = std::max(cur_scan_start_idx,
cur_idx_)` could produce an out-of-bounds value because `cur_idx_` was left
at `block_handles_.size()` from previous exhaustion. `SeekToBlockIdx()`
unconditionally set `valid_ = true` without checking bounds, causing the
subsequent `value()` call to hit the assertion
`cur_idx_ < block_handles_.size()`.
Added bounds check before `SeekToBlockIdx()` in Case 3 to correctly report
exhaustion instead of crashing.
Reviewed By: joshkang97
Differential Revision: D99604049
fbshipit-source-id: 9d5d91afde7c0984a7b4c2f62604f27f19b07922
Summary:
**Root cause:** DBIter's read-path range tombstone conversion assumes the iterator can observe *every* interior live key between point tombstones. When `ReadOptions.table_filter` is active, whole SSTs can be hidden from the scan. DBIter may then mistake non-contiguous deletes for a contiguous run and synthesize a range tombstone that covers real, still-live keys — silently corrupting the read path.
This is not UDT-specific; UDT just changes the failure surface. The stress test failures that triggered investigation were from `db_stress_tool/no_batched_ops_stress.cc` applying an SQFC-backed `table_filter` for range queries even when `total_order_seek` is forced.
## Fix
`db/db_iter.cc`: When initializing `min_tombstones_for_range_conversion_`, set it to 0 (disabling range conversion) whenever `read_options.table_filter` is non-null. This is the conservative and correct behavior — filtered scans don't provide the full key visibility the optimization requires.
## Test
`db/db_iterator_test.cc` — new test `ReadPathRangeTombstoneTest.TableFilterHiddenInteriorKey`:
- Constructs five flushed SSTs so that the live interior key `"b"` lives in a 2-entry SST that the `table_filter` hides.
- Keeps the active memtable non-empty (key `"zz"`) so range conversion has a valid insertion point — without this the test is a false negative.
- Asserts `inserted_ranges_.size() == 0` (no synthesis occurred).
- Verifies `"b"` is still readable via a normal `Get`.
- Runs both with and without UDT (user-defined timestamps) via `SCOPED_TRACE`.
On unpatched HEAD this test fails with `inserted_ranges_.size() == 1`; with the fix it passes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14586
Reviewed By: joshkang97
Differential Revision: D100023487
Pulled By: xingbowang
fbshipit-source-id: 5ef885c89a6cfd7a97b814e38bdcc48d3a1ab349
Summary:
Crash test T263619547 exposed a flush input accounting bug in
`FragmentedRangeTombstoneList`.
When the constructor sees that the incoming range tombstones are not sorted, it
falls back to rereading all tombstones into `keys`/`values` and sorting them via
`VectorIterator` before fragmentation. However, `num_unfragmented_tombstones_`
was left with the partial count from the aborted first pass. In timestamp
stripping flushes, this stale count could make flush verification report
`Expected X entries in memtables, but read Y` even though all tombstones were
still processed.
Fix the slow path by resetting `num_unfragmented_tombstones_` from
`keys.size()` after the second pass. Add a regression test that feeds unsorted
range tombstones into the fragmenter and verifies the full tombstone count is
preserved.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14579
Test Plan: - New unit test that fails without the fix.
Reviewed By: xingbowang
Differential Revision: D99872374
Pulled By: joshkang97
fbshipit-source-id: 38e2bb73c68dc1c677870b8973c93b933ded9038
Summary:
https://github.com/facebook/rocksdb/pull/14575 addresses an existing issue where trace record before WAL lead to inconsistent expected state.
However, placing the trace record after the WAL is still problematic because. The trace may not have all the records in the WAL, resulting in `Error restoring historical expected values -> Trace ended before replaying all expected write ops` on recovery.
This change minimizes the gap to reduce stress test failures, but it is still not a full solution. It is however still better than previously writing the trace before the WAL because the error is much more obvious when writing the trace after WAL.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14578
Test Plan: Existing CI passes.
Reviewed By: xingbowang
Differential Revision: D99870545
Pulled By: joshkang97
fbshipit-source-id: 024ee3b15e258afd2dd8ff98db7cbe6a01906be7
Summary:
Handle the case where a transaction commit with `commit_bypass_memtable` succeeds in writing the commit marker to WAL, but then `IngestWBWIAsMemtable` fails (e.g., WAL creation failure during `SwitchMemtable` or invalid column family). Previously this failure path was not distinguished from a direct WBWI ingest, so the DB could continue operating with committed data durable in WAL but not published to memtables.
This resulted in a higher seqno in the WAL that what the DB's latest seqno was.
Now `IngestWBWIAsMemtable` takes an `ingest_wbwi_for_commit` parameter. When true and the ingest fails (without a prior memtable update), the DB sets a fatal background error, stopping all writes until the DB is closed and reopened for WAL recovery. A close and reopen is required because the auto-recovery mechanism works by flushing memtables to disk, but here the committed data was never published to memtables in the first place — it only exists as prepare + commit records in the WAL. Only a full WAL replay during `DB::Open` can reconstruct the committed transaction data and insert it into memtables.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14567
Test Plan:
- Added `CommitBypassMemtableTest.SwitchMemtableFailureStopsDBUntilReopen`:
- Injects an IO error during `SwitchMemtable` WAL creation via `TEST_SYNC_POINT_CALLBACK`
- Verifies commit returns `Corruption` status
- Verifies DB enters fatal background error state and rejects further writes
- Verifies that after close and reopen, committed data is recovered from WAL and the DB resumes normal operation
Reviewed By: xingbowang
Differential Revision: D98638705
Pulled By: joshkang97
fbshipit-source-id: f116b1f257b19984b0413f6aeba0ba4a7c0a5b25
Summary:
When BuildTable succeeds during flush, it caches the output SST file in the table cache for subsequent user reads (builder.cc:460). If the flush then fails to install — e.g., LogAndApply MANIFEST I/O error, CF dropped, or shutdown — the cache entry was never evicted. BuildTable only cleans up its own failures (builder.cc:511), not failures in the install path.
The FindObsoleteFiles full-scan backstop would normally catch this by finding the orphan file on disk and evicting the cache entry. However, under crash test metadata read fault injection
(open_metadata_read_fault_one_in), GetChildren fails and the orphan is never found, causing the TEST_VerifyNoObsoleteFilesCached assertion to fire during Close().
This is the same class of bug previously fixed in the compaction path by https://github.com/facebook/rocksdb/issues/14469 (Run failure) and https://github.com/facebook/rocksdb/issues/14549 (Install failure), but in the flush path which had no analogous cleanup.
Fix: call TableCache::ReleaseObsolete in FlushJob::Run() when the flush fails after BuildTable succeeded (meta_.fd.GetFileSize() > 0).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14577
Test Plan:
New unit test DBFlushTest.LeakedTableCacheEntryOnFlushInstallFailure:
- Injects failure after BuildTable via sync point, deactivates filesystem to prevent backstop cleanup
- Without fix: assertion fires ("Leaked table cache entry")
- With fix: passes
```
COMPILE_WITH_ASAN=1 make -j db_flush_test
./db_flush_test --gtest_filter="DBFlushTest.LeakedTableCacheEntry*"
[ PASSED ] 1 test.
```
Existing compaction leak tests still pass:
```
COMPILE_WITH_ASAN=1 make -j db_compaction_test
./db_compaction_test --gtest_filter="*LeakedTableCacheEntry*"
[ PASSED ] 2 tests.
```
Reviewed By: xingbowang
Differential Revision: D99692601
Pulled By: pdillinger
fbshipit-source-id: ff5aa1ad165b3abec915844f97f6af9e59d85774
Summary:
Upgrade the Ubuntu 24 CI Docker image to include clang-21 (from clang-18), and bump all workflow references to the new image tags. Also adds ccache to both images and renames all clang-18 job references to clang-21.
This is mainly for upgrading the clang version later used for generating C API automatically. See PR https://github.com/facebook/rocksdb/issues/14572
### Changes
**`build_tools/ubuntu24_image/Dockerfile`**
- Add clang-21 installation from LLVM snapshot repo (`apt.llvm.org/noble/llvm-toolchain-noble-21`)
- Add ccache
- Add comment pointing Meta employees to internal devvm build guide
**`build_tools/ubuntu22_image/Dockerfile`**
- Add ccache
- Add comment pointing Meta employees to internal devvm build guide
**`.github/workflows/pr-jobs.yml`**
- Rename `build-linux-clang-18-no_test_run` → `build-linux-clang-21-no_test_run`
- Rename `build-linux-clang18-asan-ubsan` → `build-linux-clang21-asan-ubsan`
- Rename `build-linux-clang18-mini-tsan` → `build-linux-clang21-mini-tsan`
- Update all `clang-18`/`clang++-18` references to `clang-21`/`clang++-21`
- Update ccache key prefixes: `clang18-asan-ubsan` → `clang21-asan-ubsan`, `clang18-tsan` → `clang21-tsan`
- Bump `rocksdb_ubuntu:24.0` → `rocksdb_ubuntu:24.1`
**`.github/workflows/nightly.yml`**
- Rename `build-linux-clang-18-asan-ubsan-with-folly` → `build-linux-clang-21-asan-ubsan-with-folly`
- Update clang-18 → clang-21 compiler references
- Bump `rocksdb_ubuntu:24.0` → `rocksdb_ubuntu:24.1`
**`.github/workflows/clang-tidy.yml`**
- Update clang-18 → clang-21
- Bump `rocksdb_ubuntu:24.0` → `rocksdb_ubuntu:24.1`
### New Docker Images
Both images have been built and tested locally. They will be pushed to `ghcr.io/facebook/rocksdb_ubuntu` before this PR is merged.
- `ghcr.io/facebook/rocksdb_ubuntu:22.2` — adds ccache, adds devvm build note
- `ghcr.io/facebook/rocksdb_ubuntu:24.1` — adds clang-21 from LLVM snapshot repo, adds ccache
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14576
Test Plan: Built both images locally and verified CI job names/compiler flags are consistent.
Reviewed By: joshkang97
Differential Revision: D99694293
Pulled By: xingbowang
fbshipit-source-id: c23c27f5cf870fb2c8b4e3d1cba281d0ce63f9d6
Summary:
Fix trace ordering for `preserve_write_order` mode to only record writes that actually succeeded.
Previously, when `preserve_write_order = true`, trace records were emitted **before** confirming the write succeeded — at a point where the write could still fail (e.g., WAL I/O error). This meant the trace could contain records for writes that never actually committed to the DB. When `db_stress` crash testing replays the trace to reconstruct expected state, these phantom records cause the expected state to diverge from reality, leading to false verification failures.
This fix moves the `preserve_write_order` tracing to happen **after** the write is confirmed successful (after memtable insert, right before `SetLastSequence`), in all three write paths: `WriteImpl`, `PipelinedWriteImpl`, and `WriteImplWALOnly`.
default and pipelined write modes. Replays the trace into a fresh DB and confirms only the successful write is present.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14575
Test Plan:
- New unit test: `TracePreserveWriteOrderSkipsFailedWrite` in `db/db_test2.cc`
- Tests both `enable_pipelined_write = false` and `true`
- Uses `FaultInjectionTestEnv` to force a write failure
- Verifies the failed write is absent from both the original DB and the replayed trace
Reviewed By: xingbowang
Differential Revision: D99624672
Pulled By: joshkang97
fbshipit-source-id: 5793727c91cb01a12fbb4b2cd59615802ae3d394
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14569
ReadSet::ReadIndex() moves block values out of pinned_blocks_ via std::move,
but never releases the associated prefetch memory accounting. This causes
ReleaseBlock() and the destructor to skip ReleaseMemory() since they check
pinned_blocks_.GetValue() which returns null after the move. Over time, the
memory budget is exhausted and no further prefetches can be dispatched when
max_prefetch_memory_bytes is set. The bug was introduced in https://github.com/facebook/rocksdb/pull/14401.
The fix releases memory accounting in ReadIndex() when moving values out
(both for Case 1: block already available, and Case 2: after async IO
polling), and zeros block_sizes_ to prevent double-release.
Also adds multiscan_max_prefetch_memory_bytes option to db_stress/crashtest
for stress testing this code path.
Reviewed By: hx235
Differential Revision: D99488961
fbshipit-source-id: 5ddd1f50e2f6ebb357f86e013d781a790e7e558a
Summary:
btrfs accepts fallocate without error but uses copy-on-write, so preallocated extents are not reflected in st_blocks. This caused AllocateTest to fail spuriously on btrfs filesystems. Detect btrfs via statfs and skip only the block-count assertions, keeping the rest of the test (write, flush, close, size checks) intact.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14553
Test Plan: env_test AllocateTest passes on btrfs (skips with message) and would still enforce preallocation checks on ext4.
Reviewed By: joshkang97
Differential Revision: D99307962
Pulled By: pdillinger
fbshipit-source-id: 8f40c0109aa397e9ca7d9ee4496fa0614e971970
Summary:
When MarkAsTrash fails (e.g., rename error due to filesystem conditions), AddFileToDeletionQueue falls back to deleting the file directly via fs_->DeleteFile(). This bypasses DeleteFileImmediately() and its TEST_SYNC_POINT("DeleteScheduler::DeleteFile") callback, causing tests that count deletions via sync points to undercount. This explains flaky failures in DBSSTTest.DeleteSchedulerMultipleDBPaths where bg_delete_file is 4 instead of the expected 8.
Fix by calling DeleteFileImmediately() in the error path instead of fs_->DeleteFile() directly. DeleteFileImmediately already handles the sync point, OnDeleteFile tracking, and FILES_DELETED_IMMEDIATELY stat, so the manual bookkeeping is also removed to avoid duplication.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14556
Test Plan:
- Existing tests: db_sst_test (DeleteSchedulerMultipleDBPaths, DestroyDBWithRateLimitedDelete) and delete_scheduler_test all pass.
- Ran DeleteSchedulerMultipleDBPaths 100x with COERCE_CONTEXT_SWITCH=1.
Reviewed By: joshkang97
Differential Revision: D99308177
Pulled By: pdillinger
fbshipit-source-id: 3918c4800c2420ff1ae667beb321da3f27406420
Summary:
Fix two bugs in read-path range tombstone conversion (`min_tombstones_for_range_conversion`):
1. **SeekToLast stale saved_key_**: `SeekToLast()` did not clear `saved_key_` before calling `PrevInternal()`. A stale key from a prior `Seek()` would be swapped into `range_tomb_end_key_`, corrupting the tombstone tracking bounds and triggering an assertion failure when `range_tomb_first_key_ > end_key`.
2. **Tombstone inserted at wrong sequence**: The current behavior uses the "max" seqno of the tombstones as the insertion seqno. This is not correct because it does not take into account the seqno range deletions seen throughout the iteration. Unfortunately, range deletions are not visible to the db_iter as the are filtered at the merging iterator level. A future refactor may try to expose this, but currently the simplest solution is to just use the iterator seqno.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14573
Test Plan: Unit tests for both bugs that fail without the fix.
Reviewed By: xingbowang
Differential Revision: D99569668
Pulled By: joshkang97
fbshipit-source-id: bb1b154beccced7438831cd5cf2780ad1e11a4cc
Summary:
Add a read-path optimization that converts contiguous point tombstones into range tombstones during forward/reverse iteration. When a configurable threshold of consecutive point deletions (kTypeDeletion, kTypeDeletionWithTimestamp, kTypeSingleDeletion — with no live keys between them) is detected, a range tombstone covering `[first_tombstone_key, next_live_key)` is inserted into the active mutable memtable. This benefits future iterators by enabling efficient skipping via range tombstone fragmentation.
If there is a memtable switch during the read iteration, then the range deletion entry is discarded.
The inserted range tombstones are logically redundant (they don't delete anything that isn't already deleted by point tombstones), skip WAL (they're a derived optimization regenerated by future reads on crash), and use the max tombstone sequence number so they don't interfere with newer writes.
## Key changes
- **New option `min_tombstones_for_range_conversion`** (`AdvancedColumnFamilyOptions`): Threshold of contiguous point tombstones before converting to a range tombstone. Default 0 (disabled). Dynamically changeable via `SetOptions()`.
- **`DBIter` tracking logic** (`db/db_iter.cc`): Tracks contiguous tombstones during `FindNextUserEntryInternal()` (forward) and `PrevInternal()` (reverse). When a live key terminates a run that meets the threshold, `MaybeInsertRangeTombstone()` inserts `[first_tombstone, live_key)` into the active memtable.
- **`FindValueForCurrentKey` `found_visible` output** (`db/db_iter.cc`): Distinguishes "key deleted at this snapshot" from "no visible entries" so reverse tracking doesn't treat post-snapshot keys as tombstones.
- **`IterKey::Swap()`** (`db/dbformat.h`): Efficiently tracks reverse tombstone run end keys without extra allocations.
- **`MemTable::AddLogicallyRedundantRangeTombstone()`** (`db/memtable.cc`): Concurrent-safe range tombstone insertion into the active memtable. Range tombstone skiplist always uses concurrent inserts.
- **`ConstructFragmentedRangeTombstones` race fix** (`db/db_impl/db_impl_write.cc`): Moved after `MarkImmutable()` to prevent lost entries.
- **`MarkImmutable` ordering fix** (`db/memtable_list.cc`): Called before `current_->Add()` to close a race window.
- **Prefix filter awareness** (`db/db_iter.cc`): Tombstone tracking scoped to the seek prefix when prefix filtering is active. See dedicated section below.
- **Transaction awareness** (`db/db_iter.cc`): Tombstones with `seq > snapshot` excluded from tracking. `min_uncommitted` guard uses `insert_seq` (which may be bumped to `earliest_seq`) instead of `range_tomb_max_seq_`. See dedicated section below.
- **Duplicate range check**: Skips insertion if the memtable already covers `[start, end)`.
- **New statistics**: `READ_PATH_RANGE_TOMBSTONES_INSERTED` and `READ_PATH_RANGE_TOMBSTONES_DISCARDED`.
- **Memtable MultiGet batch lookup** (`memtable/inlineskiplist.h`, `db/memtable.cc`): `InlineSkipList::MultiGet()` with cached search path ("finger") for sorted key lookups.
- **New option `memtable_batch_lookup_optimization`** (`AdvancedColumnFamilyOptions`): Enables batch lookup for memtable MultiGet. Default false. Immutable.
## Deciding Range Tombstone Seqno
- The range tombstone is inserted with `insert_seq = max(range_tomb_max_seq_, earliest_seq)` where `range_tomb_max_seq_` is the maximum sequence number across all point tombstones in the contiguous run, and `earliest_seq` is the memtable's earliest sequence number. This preserves the memtable's `earliest_seqno_` invariant.
- If the iterator's snapshot sequence (`sequence_`) predates the memtable's `earliest_seq`, insertion is skipped entirely to avoid unintentionally covering entries between `sequence_` and `earliest_seq`.
## ConstructFragmentedRangeTombstones Race Fix
- `MarkImmutable()` and `ConstructFragmentedRangeTombstones()` are now called before `mutex_.Lock()` in `SwitchMemtable`, keeping this work outside the DB mutex. `MarkImmutable()` blocks concurrent `AddLogicallyRedundantRangeTombstone()` calls via `immutable_mutex_`, ensuring no range tombstones are inserted after the fragmented list is built. `MarkImmutable()` is idempotent, so `MemTableList::Add()` calling it again inside the mutex is harmless.
## Prefix Filter Safety
- When prefix filtering is active, the BBTI bloom filter may reject SST files outside the seek prefix, but the memtable (no bloom filter) returns keys across prefix boundaries. Tombstone tracking is scoped to the seek prefix so that converted range tombstones cannot cover live keys hidden in filtered files.
- `total_order_seek=true` disables prefix filtering — all files are visible, so tombstones safely span prefix boundaries.
- **Behavior change**: Seeking to an out-of-domain key with `total_order_seek=false` now treats it as total-order (prefix_ not set). When `prefix_same_as_start=true`, iterating past an out-of-domain key cleanly invalidates the iterator instead of calling `Transform()` on it (which was UB in release builds with `FixedPrefixTransform`). This is a requirement because an incorrect iterator scan could lead to a range tombstone covering a live key.
## Transaction Support
- Tombstones written by the transaction's own uncommitted writes (sequence > snapshot) are now excluded from contiguous tombstone tracking entirely at the tracking site in `FindNextUserEntryInternal()` and `PrevInternal()`. Previously, tracking relied on a `min_uncommitted` check at insertion time, but this was insufficient — a transaction's own Delete with `seq > snapshot` could extend a run of committed tombstones, and the resulting range tombstone would cover data visible to other snapshots.
- The fix skips any tombstone with `ikey_.sequence > sequence_` during tracking. If a transaction-owned tombstone appears mid-run, it flushes the accumulated committed run first, then resets tracking. This ensures only tombstones visible to the current snapshot are ever converted.
- Both WritePrepared and WriteUnprepared transactions are supported with dedicated test coverage:
- **WritePrepared**: When tombstones are committed before `Prepare()`, their seqnos are below `min_uncommitted` and insertion proceeds safely. When `Prepare()` happens first, tombstone seqnos exceed `min_uncommitted` and insertion is blocked.
- **WriteUnprepared**: Multiple unprepared batches with different seqno ranges are handled correctly. Own transaction Deletes that extend a committed tombstone run block insertion of the entire run. After rollback, data correctness is verified.
## UDT support
- When user-defined timestamps (UDT) are enabled, keys include an 8-byte timestamp suffix. The comparator, Put/Delete APIs, and ReadOptions all require timestamps.
- Forward exhaustion with UDT: `iterate_upper_bound_` is a plain user key without a timestamp suffix. It is padded with min timestamp via `AppendKeyWithMinTimestamp()` so it sorts after all entries with this user key, preserving the exclusive bound semantics.
- Reverse exhaustion with UDT: The end key comes from either the previous live key (which already has a proper timestamp suffix) or the seek target set by `SetSavedKeyToSeekForPrevTarget()` (which appends a timestamp via `SetInternalKey(..., timestamp_ub_)` and `UpdateInternalKey(..., ts)`). In both cases, the end key is already properly timestamped, so no additional padding is needed unlike forward exhaustion.
- Contiguous tombstone detection works correctly with UDT because the underlying `kTypeDeletionWithTimestamp` entries are tracked the same way as `kTypeDeletion`.
## Concurrent Iterators
- Should concurrent iterators happen to read the range at the same time, both will produce the same range and seqno entry. Only one will be accepted by the skip list and the others will be rejected. Future iterators will read the range and not even attempt to insert the range.
- There is nothing preventing similar ranges from being inserted however. Two iterators can produce overlapping ranges, but this protection would be complicated to implement and there is no evidence that it is a likely scenario yet.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14448
Test Plan:
- **Unit tests** (`db/db_iterator_test.cc`): `ReadPathRangeTombstoneTest` parameterized by forward/reverse with cases for basic insertion, non-contiguous (below threshold), memtable switch, exhausted iterator with/without bounds, direction change, mixed Delete/SingleDelete, single-delete-only runs, snapshot predating memtable, block cache tier incomplete, skip when covered by existing range, UDT basic scan, UDT exhaustion, prefix filter cross-prefix scan (`PrefixFilterCrossPrefixScanCoversLiveKey` with default/total_order_seek/prefix_same_as_start variants), stale ikey from forward-then-reverse scan (`StaleIkeyFromForwardThenReverse`), and reseek stale ikey (`ReseekStaleIkey`).
- **Concurrency test** (`db/db_test2.cc`): `DBTestConcurrentRangeTombstoneConversions` parameterized by `(allow_concurrent_memtable_write, min_tombstones_for_range_conversion)` with mixed writers, deleters, range deleters, and concurrent forward/reverse readers.
- **Transaction tests** (`utilities/transactions/write_prepared_transaction_test.cc`, `write_unprepared_transaction_test.cc`): Tests for WritePrepared (insertion allowed when tombstones committed before prepare, blocked when after; seqno bump shadowing prepared writes `RangeTombstoneSeqnoBumpShadowsPreparedWrite`) and WriteUnprepared (multiple batches, extended visibility with CalcMaxVisibleSeq, own deletions with rollback).
- **IterKey::Swap tests** (`db/dbformat_test.cc`): `IterKeySwapTest` parameterized over `(key_len, copy, use_secondary)` × 2 covering all inline/heap/pinned/secondary combinations.
- **InlineSkipList MultiGet tests** (`memtable/inlineskiplist_test.cc`): Basic, exact matches, empty, single key, randomized validation against `std::set::lower_bound`, duplicate keys with callback walk, and concurrent MultiGet with read-after-write consistency.
- **Memtable MultiGet tests** (`db/db_basic_test.cc`): Batch lookup, overwrite, flush, merge, disabled by default, paranoid checks, and snapshot tests.
- **Stress test coverage**: `min_tombstones_for_range_conversion` and `memtable_batch_lookup_optimization` options added to `db_crashtest.py` and `db_stress` flags.
- `make check` passes all tests.
### Benchmark results
Tombstones scattered randomly in clusters via `seek_nexts_to_delete` for realistic workloads.
**DB Setup A (scattered deletes, 100 per seek)**:
```
# Step 1: Create and compact
./db_bench --benchmarks=fillseq,compact --seed=1 --compression_type=none --num=1000000 --db=<DB>
# Step 2: Scatter tombstones (5000 seeks × 100 deletes ≈ 500k tombstones)
./db_bench --benchmarks=seekrandom,flush --seed=1 --compression_type=none --num=2000 \
--seek_nexts=0 --seek_nexts_to_delete=100 --use_existing_db=1 --threads=1 --db=<DB>
```
**DB Setup A2 (scattered deletes, 8 per seek)**:
```
# Step 1: Create and compact
./db_bench --benchmarks=fillseq,compact --seed=1 --compression_type=none --num=1000000 --db=<DB>
# Step 2: Scatter tombstones (5000 seeks × 8 deletes ≈ 40k tombstones)
./db_bench --benchmarks=seekrandom,flush --seed=1 --compression_type=none --num=2000 \
--seek_nexts=0 --seek_nexts_to_delete=8 --use_existing_db=1 --threads=1 --db=<DB>
```
**DB Setup B (no deletes)**:
```
./db_bench --benchmarks=fillseq,compact --seed=1 --compression_type=none --num=1000000 \
[--key_size=100] --db=<DB>
```
**Read workload** (same for all):
```
./db_bench --benchmarks=seekrandom --seek_nexts=100 --threads=8 \
--reverse_iterator={true,false} --seed=1 --use_existing_db=1 \
--compression_type=none --num=1000000 --duration=10 \
--disable_auto_compactions \
[--key_size=100] [--min_tombstones_for_range_conversion=X] --db=<DB_COPY>
```
Each workload averaged over 3 runs.
**Table 1: seekrandom forward, scattered deletes (2000 seeks × 100 deletes/seek)**
| Variant | avg ops/s | % vs main |
|---------|-----------|-----------|
| main | 2,895 | - |
| threshold=0 | 2,869 | -0.9% |
| threshold=8 | 287,334 | +9,824% |
**Table 2: seekrandom reverse, scattered deletes (2000 seeks × 100 deletes/seek)**
| Variant | avg ops/s | % vs main |
|---------|-----------|-----------|
| main | 544 | - |
| threshold=0 | 548 | +0.7% |
| threshold=8 | 206,491 | +37,860% |
**Table 3: seekrandom forward, scattered deletes (2000 seeks × 8 deletes/seek)**
| Variant | avg ops/s | % vs main |
|---------|-----------|-----------|
| main | 194,049 | - |
| threshold=0 | 195,703 | +0.9% |
| threshold=8 | 310,740 | +60.1% |
**Table 4: seekrandom reverse, scattered deletes (2000 seeks × 8 deletes/seek)**
| Variant | avg ops/s | % vs main |
|---------|-----------|-----------|
| main | 63,854 | - |
| threshold=0 | 69,266 | +8.5% |
| threshold=8 | 218,101 | +241.6% |
**Table 5: seekrandom forward, no deletes (regression check)**
| Variant | key=16B avg ops/s | % vs main | key=100B avg ops/s | % vs main |
|---------|-------------------|-----------|---------------------|-----------|
| main | 330,901 | - | 236,048 | - |
| threshold=0 | 328,398 | -0.8% | 238,055 | +0.9% |
| threshold=8 | 332,539 | +0.5% | 233,776 | -1.0% |
**Table 6: seekrandom reverse, no deletes (regression check)**
| Variant | key=16B avg ops/s | % vs main | key=100B avg ops/s | % vs main |
|---------|-------------------|-----------|---------------------|-----------|
| main | 261,445 | - | 192,177 | - |
| threshold=0 | 265,020 | +1.4% | 191,616 | -0.3% |
| threshold=8 | 250,881 | -4.0% | 189,239 | -1.5% |
Reviewed By: xingbowang
Differential Revision: D96203950
Pulled By: joshkang97
fbshipit-source-id: 06ba66ebde3c355f04671d1e681f1b1586e8751d
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14557
RocksDB's PerfContext tracks block_decompress_time but has no counter for the
number of block decompressions. The global Statistics ticker
NUMBER_BLOCK_DECOMPRESSED exists but is not accessible through PerfContext,
which is the thread-local, per-operation metric system used by benchmarks.
Add block_decompress_count as a new PerfContext counter (gated at
kEnableCount level) incremented in DecompressBlockData alongside the existing
NUMBER_BLOCK_DECOMPRESSED ticker. This enables benchmarks and applications to
observe how many blocks required decompression per operation, complementing
the existing block_read_count.
Reviewed By: anand1976
Differential Revision: D99233514
fbshipit-source-id: 40a68c1d9321f560cebdb7c30a544a0c62ae64f0
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14551
Add two new per-SST TableProperties fields that track how many data blocks
were stored uncompressed:
- `num_data_blocks_compression_rejected`: blocks where compression was
attempted but the compressed output exceeded the ratio limit set by
`CompressionOptions::max_compressed_bytes_per_kb`.
- `num_data_blocks_compression_bypassed`: blocks where compression was
never attempted (e.g., kNoCompression type, no compressor available).
Together with `num_data_blocks`, these give a full decomposition:
num_data_blocks = compressed + rejected + bypassed
Previously this information was only available via global Statistics tickers
(NUMBER_BLOCK_COMPRESSION_REJECTED / BYPASSED) which aggregate across all
SSTs. Now it is persisted per-SST in the properties block and visible via
sst_dump --show_properties.
Reviewed By: anand1976
Differential Revision: D99229339
fbshipit-source-id: 8333f51acff30a759d725e5e94bd80f4fcb18b57
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14568
Port of D98326754 to internal_repo_rocksdb (GitHub-synced repo).
BlobDB blob file compression currently uses hardcoded default
CompressionOptions{} in BlobFileBuilder, ignoring any user-specified
compression options like level, window_bits, or strategy. This means
ZSTD always uses level 3 (doubleFast strategy with two hash tables),
even when a lower level would significantly reduce CPU usage.
This diff adds a new `blob_compression_opts` field to
AdvancedColumnFamilyOptions (and MutableCFOptions) and plumbs it into
BlobFileBuilder::GetCompressor(). The option is registered as mutable
and dynamically changeable via SetOptions().
For example, setting level=1 with ZSTD switches from "doubleFast"
(two hash tables per thread) to "fast" (single hash table), reducing
L3 cache pressure on flush-heavy workloads.
There was an existing TODO in blob_file_builder.cc requesting exactly
this feature — this diff fulfills it.
This change is safe as the default value is always dFast. So this will not impact the current code.
Reviewed By: xingbowang
Differential Revision: D99478562
fbshipit-source-id: e79b847a854f60dea552442b18fd3f1384efac70
Summary:
This PR adds a `BlobFilePartitionStrategy` interface that allows users to plug in custom partition selection logic for blob direct writes. The default behavior (round-robin across partitions) is preserved as the built-in `RoundRobinBlobFilePartitionStrategy`.
### Motivation
The existing blob direct write implementation distributes writes across partitions using a fixed round-robin strategy (atomic counter mod num_partitions). While this works well for uniform workloads, some use cases benefit from custom routing:
- **Key-based routing**: co-locate related keys in the same blob partition for locality-aware reads.
- **CF-based routing**: direct writes for different column families to dedicated partitions.
- **Value-size routing**: send large vs. small values to different partitions.
- **Application-defined affinity**: any domain-specific grouping that the default round-robin cannot express.
### Design
A new pure-virtual interface `BlobFilePartitionStrategy` is added to `include/rocksdb/advanced_options.h`:
```cpp
class BlobFilePartitionStrategy {
public:
virtual ~BlobFilePartitionStrategy() = default;
// Select a partition for the given blob direct write.
// The return value can be any uint32_t; the caller applies
// modulo num_partitions internally.
// Implementations must be thread-safe.
virtual uint32_t SelectPartition(uint32_t num_partitions,
uint32_t column_family_id,
const Slice& key,
const Slice& value) = 0;
};
```
The strategy receives:
- `num_partitions`: the configured partition count (for implementations that want to be partition-count-aware).
- `column_family_id`: useful for CF-based routing.
- `key` / `value`: the key and value being written.
The return value is taken modulo `num_partitions` internally, so implementations can return any `uint32_t` without worrying about bounds.
A new column family option `blob_direct_write_partition_strategy` (type `std::shared_ptr<BlobFilePartitionStrategy>`, default `nullptr`) wires the strategy into `BlobFilePartitionManager`. When `nullptr`, the built-in `RoundRobinBlobFilePartitionStrategy` is used, preserving existing behavior exactly.
### Changes
| File | Change |
|---|---|
| `include/rocksdb/advanced_options.h` | New `BlobFilePartitionStrategy` interface; new `blob_direct_write_partition_strategy` option |
| `db/blob/blob_file_partition_manager.h/.cc` | Accept strategy in constructor; replace atomic counter with strategy call; move `RoundRobinBlobFilePartitionStrategy` here as default |
| `options/cf_options.h/.cc` | Thread strategy through `ImmutableCFOptions` |
| `options/options_helper.cc` | Propagate strategy in `UpdateColumnFamilyOptions` |
| `options/options_settable_test.cc` | Register new option field in options settability test |
| `db/db_impl/db_impl.cc` | Pass strategy when constructing `BlobFilePartitionManager` |
| `db/blob/db_blob_direct_write_test.cc` | New test with `FixedBlobDirectWritePartitionStrategy` |
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14565
Test Plan:
New test `DirectWriteCustomPartitionStrategyRoutesWritesToOneBlobFile` verifies that a `FixedBlobDirectWritePartitionStrategy` that always returns partition 3 correctly routes all writes to a single blob file across 4 configured partitions.
```
make -j128 db_blob_direct_write_test && ./db_blob_direct_write_test --gtest_filter='*CustomPartition*'
make -j128 db_blob_direct_write_test && ./db_blob_direct_write_test
```
Reviewed By: joshkang97
Differential Revision: D99458813
Pulled By: xingbowang
fbshipit-source-id: 54cfbb0f75e24b58a8db61ad8755204d0db6ece7
Summary:
When BatchedOpsStressTest runs on a TransactionDB, db_->Write() creates internal transactions using default_lock_timeout (1 second), which is too short under heavy contention with many threads. This causes sporadic "Timeout waiting to lock key" errors printed to stderr, which the crash test framework treats as fatal failures.
Increase default_lock_timeout to 600000ms (10 minutes) to match the lock_timeout already used for explicit transactions in NewTxn().
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14540
Test Plan: Manual `make crash_test_with_wc_txn` as a sanity check
Reviewed By: mszeszko-meta
Differential Revision: D98944474
Pulled By: pdillinger
fbshipit-source-id: 13b9e13fddc6332da010b01f7c41a51748f75624
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14566
**What**: Adds an explicit `file->Close()` call in `WriteStringToFile()` before
the function returns, instead of relying on the `unique_ptr` destructor to close
the file.
**How**: In `file_system.cc`, after the optional `Sync()` and before the
error-cleanup path, calls `file->Close()`. If `Close()` fails, the file is
deleted just like any other write failure. Adds two unit tests in `env_test.cc`:
- `WriteStringToFileClosesFile`: uses `CountedFileSystem` to verify `Close()` is
called and content is written correctly.
- `WriteStringToFileCloseFailureDeletesFile`: uses a custom `CloseFailFS`
wrapper to inject a `Close()` failure and verify the file is deleted on error.
**Why**: The previous implementation never called `Close()` explicitly — it
relied on the `unique_ptr` destructor which may silently swallow write errors
that occur during close (e.g., flushing buffered data). Explicit `Close()`
ensures such errors are detected and propagated to the caller.
Reviewed By: archang19
Differential Revision: D99462173
fbshipit-source-id: 525b5489bd0dbfc3159b007a13f9474f84d3c84e
Summary:
**Summary:**
Disable `use_trie_index` in `db_crashtest.py` to avoid trie UDI stress test failures.
The default param was previously set to randomly enable trie index ~12.5% of the time (`random.choice([0, 0, 0, 0, 0, 0, 0, 1])`). Setting it to `0` until the underlying issues are resolved.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14559
Test Plan: Stress test runs without trie UDI failures.
Reviewed By: hx235
Differential Revision: D99343747
Pulled By: xingbowang
fbshipit-source-id: 6f86b5dd5ddb2869d797e93ddbda521981614bae
Summary:
AnonExpectedState::Open() never initialized the base class
persisted_seqno_ pointer, leaving it as nullptr. Any call to
SetPersistedSeqno() or GetPersistedSeqno() on an AnonExpectedState
(used when expected_values_dir is empty) would dereference a null
pointer. Fix by allocating and assigning it in Open(), matching
FileExpectedState's pattern.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14523
Reviewed By: mszeszko-meta
Differential Revision: D98556119
Pulled By: hx235
fbshipit-source-id: c4c3103272735d36c1d05b96f7d009b6b750bf53
Summary:
Add a git pre-push hook that automatically runs `make format-auto` before every push. If formatting issues are found, the hook fixes them, creates a new commit, and retries the push — all in one shot.
The hook is auto-configured on the first `make` build via `core.hooksPath`, so there is no manual install step needed.
## Changes
**`githooks/pre-push`** — pre-push hook that:
1. Runs `make check-format` to detect formatting issues
2. If issues found, runs `make format-auto` to fix them
3. Creates a new "format" commit with the fixes
4. Retries the push automatically with the format commit included
5. Stashes/restores any uncommitted work so it is not affected
6. Skips in non-interactive environments (CI) unless `ROCKSDB_FORMAT_HOOK` is set
7. Can be skipped with `git push --no-verify`
**`Makefile`**:
- `setup-hooks` target: auto-sets `core.hooksPath=githooks` in the local repo config. Runs as a dependency of `all`, so any `make` build activates the hook with zero manual setup.
- `install-hooks` / `uninstall-hooks` targets: manual copy-based alternative for those who prefer it
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14558
Reviewed By: archang19
Differential Revision: D99324330
Pulled By: xingbowang
fbshipit-source-id: b824e56d572ad423fab7de7e15ead5fa8fd8e847
Summary:
When CompactionJob::Run() succeeds but Install() fails (e.g., LogAndApply MANIFEST I/O error), compact_->status was never updated with the install failure. CleanupCompaction() passed the stale OK status to SubcompactionState::Cleanup(), which skipped ReleaseObsolete -- leaking table cache entries for output files that were cached by VerifyOutputFiles but never installed into any Version.
This is the same class of bug fixed in https://github.com/facebook/rocksdb/issues/14469 (where Run() failed after VerifyOutputFiles), but in the Install() failure path. The FindObsoleteFiles full-scan backstop would normally catch this, but fails under crash test metadata read fault injection
(--open_metadata_read_fault_one_in), causing the
TEST_VerifyNoObsoleteFilesCached assertion to fire during Close().
Fix: propagate Install()'s local status back to compact_->status before CleanupCompaction(), so Cleanup() sees the failure and calls ReleaseObsolete on the output files.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14549
Test Plan:
New unit test DBCompactionTest.LeakedTableCacheEntryOnInstallFailure:
- Without fix (ASAN): assertion fires -- "File 12 is not live nor quarantined"
- With fix (ASAN): passes -- ReleaseObsolete properly cleans up the entry
```
COMPILE_WITH_ASAN=1 make -j db_compaction_test
./db_compaction_test --gtest_filter="DBCompactionTest.LeakedTableCacheEntry*"
[ PASSED ] 2 tests.
```
Reviewed By: joshkang97
Differential Revision: D99155908
Pulled By: pdillinger
fbshipit-source-id: ed5374a38d7903866a38a0fe0f5539e12321bc84
Summary:
This file was accidentally included in https://github.com/facebook/rocksdb/issues/14535. It contains local Claude Code permission settings that are user-specific and should not be in the repo.
Also adds it to `.gitignore` to prevent future accidental commits.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14554
Reviewed By: pdillinger
Differential Revision: D99296922
Pulled By: xingbowang
fbshipit-source-id: e229dd85179ae25bb15df1633bba7197dd2fa1f9
Summary:
This PR introduces **blob direct write v1**, a reduced-scope write-path optimization where large values (>= `min_blob_size`) are written directly to blob files during `Put()` and replaced in the memtable with compact `BlobIndex` references. This avoids holding full values in memory until flush time.
### Motivation
In the existing BlobDB architecture, values are written to the WAL and memtable in their full form and separated into blob files only at flush time. This means:
- Large values are held in memory twice (raw in memtable + blob file at flush)
- Blob I/O is serialized through a single flush thread per column family
Blob direct write addresses both: values leave the write path as small `BlobIndex` references, and multiple **partitions** (configurable via `blob_direct_write_partitions`) allow concurrent blob writes with independent locks.
### Design (v1 — single-writer, WAL-disabled, reduced scope)
The v1 design intentionally keeps scope narrow for correctness and reviewability:
- **Single writer thread assumption**: no concurrent writes to the same partition file. One logical writer serializes the batch.
- **WAL-disabled**: direct-write blob files are only registered in MANIFEST at flush time. WAL replay cannot recover unregistered blob references, so WAL is disabled for this v1.
- **Flush-on-write**: each `AddRecord` call flushes to the OS immediately.
- **FIFO generation batching**: each memtable switch creates one generation batch. Direct-write files for that memtable are sealed and registered atomically when the batch is flushed to MANIFEST.
- **Round-robin partitions**: blob writes are distributed across `blob_direct_write_partitions` files using an atomic counter.
### New components
| Component | Description |
|---|---|
| `BlobFilePartitionManager` | Owns N partition files per CF. Manages open/seal/register lifecycle tied to memtable generations. |
| `BlobWriteBatchTransformer` | A `WriteBatch::Handler` that rewrites qualifying `Put` values as `BlobIndex` entries before the batch enters the write group. |
### Write path integration
1. `DBImpl::WriteImpl` calls `BlobWriteBatchTransformer::TransformBatch` before entering the writer group (for default write path), or before joining the batch group (for pipelined/unordered write).
2. Values >= `min_blob_size` are written to a partition file; the key is stored with a `BlobIndex` in the transformed batch. A rollback guard marks blob bytes as initial garbage if the write fails.
3. On `SwitchMemtable`, `RotateCurrentGeneration` moves active partitions into the next immutable batch.
4. `FlushMemTableToOutputFile` / `AtomicFlushMemTablesToOutputFiles` call `PrepareFlushAdditions` to seal partition files and collect `BlobFileAddition` + `BlobFileGarbage` entries registered to MANIFEST alongside the flush.
5. Shutdown paths (`CancelAllBackgroundWork`, `WaitForCompact` with `close_db=true`) force-flush all CFs with active direct-write managers to ensure blob files are registered before close.
### Read path
- **Get/MultiGet**: `MaybeResolveBlobForWritePath` resolves `BlobIndex` references found in memtable or immutable memtable via `BlobFilePartitionManager::ResolveBlobDirectWriteIndex`, which first checks manifest-visible state and falls back to direct blob-file reads via `BlobFileCache`.
- **Iterator**: `DBIter::BlobReader` is extended with a `BlobFilePartitionManager*` to resolve direct-write blob indexes during iteration. The unified `ResolveBlobDirectWriteIndex` path handles both manifest-visible and not-yet-flushed files.
### New options
| Option | Default | Description |
|---|---|---|
| `enable_blob_direct_write` | `false` | Enable write-path blob separation for this CF. Requires `enable_blob_files = true`. Not dynamically changeable. |
| `blob_direct_write_partitions` | `1` | Number of parallel partition files per CF. Not dynamically changeable. |
### Feature incompatibilities (reduced v1 scope)
The following features are *not supported* when `enable_blob_direct_write = true`, and are enforced both in `db_stress_tool` validation and `db_crashtest.py` sanitization:
**Write model constraints:**
- `threads` must be 1 (single writer assumption)
- `allow_concurrent_memtable_write` = 0
- `enable_pipelined_write` = 0 (transformation done before batch group, but pipelined path supported with pre-transform)
- `two_write_queues` = 0
- `unordered_write` = 0 (transformation done before batch group, but unordered path supported with pre-transform)
**WAL and recovery:**
- `disable_wal` = 1 (required — WAL replay of unregistered blob files is out of v1 scope)
- `best_efforts_recovery` = 0
- `reopen` = 0 (no crash-restart with WAL replay)
- All WAL-related stress features disabled: `manual_wal_flush_one_in`, `sync_wal_one_in`, `lock_wal_one_in`, `get_sorted_wal_files_one_in`, `get_current_wal_file_one_in`, `track_and_verify_wals`, `rate_limit_auto_wal_flush`, `recycle_log_file_num`
**Blob GC and dynamic options:**
- `use_blob_db` = 0 (stacked BlobDB not supported)
- `allow_setting_blob_options_dynamically` = 0
- `enable_blob_garbage_collection` = 0
- `blob_compaction_readahead_size` = 0
- `blob_file_starting_level` = 0
**Unsupported value types and APIs:**
- Merge (`use_merge`, `use_full_merge_v1`) — merge values pass through untransformed
- Entity APIs (`use_put_entity_one_in`, `use_get_entity`, `use_multi_get_entity`, `use_attribute_group`)
- `use_timed_put_one_in`
- User-defined timestamps (`user_timestamp_size`, `persist_user_defined_timestamps`, `create_timestamped_snapshot_one_in`)
- Transactions (`use_txn`, `use_optimistic_txn`, `test_multi_ops_txns`, `commit_bypass_memtable_one_in`) — though `WriteCommittedTxn::CommitInternal` falls back from bypass-memtable to normal path when BDW is active
- `IngestWriteBatchWithIndex` returns `NotSupported`
- `inplace_update_support` = 0
**Fault injection:**
- All write/read/metadata fault injection disabled (`sync_fault_injection`, `write_fault_one_in`, `metadata_write_fault_one_in`, `read_fault_one_in`, `metadata_read_fault_one_in`, `open_*_fault_one_in`)
**Infrastructure/snapshot APIs:**
- `remote_compaction_worker_threads` = 0
- `test_secondary` = 0
- `backup_one_in` = 0
- `checkpoint_one_in` = 0
- `get_live_files_apis_one_in` = 0
- `ingest_external_file_one_in` = 0
- `ingest_wbwi_one_in` = 0
### Tests
- `db/blob/db_blob_basic_test.cc`: ~660 lines of new direct-write unit tests covering basic put/get, multi-partition, flush/compaction, recovery, and error injection.
- `db/blob/blob_file_cache_test.cc`: ~96 lines of new tests for direct-write blob file cache behavior.
- `db/write_batch_test.cc`: ~96 lines of tests for WriteBatch with blob index entries.
- `utilities/transactions/transaction_test.cc`: verifies transaction commit path falls back correctly with direct write enabled.
- `db_stress_tool/`: full stress test support with `--enable_blob_direct_write` and `--blob_direct_write_partitions` flags, integrated into `db_crashtest.py` with 10% random selection alongside regular blob params.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14535
Test Plan:
```
make -j128 db_blob_basic_test && ./db_blob_basic_test
make -j128 blob_file_cache_test && ./blob_file_cache_test
make -j128 write_batch_test && ./write_batch_test
make -j128 transaction_test && ./transaction_test
make -j128 check
```
Stress test:
```
python3 tools/db_crashtest.py blackbox --enable_blob_direct_write=1 \
--enable_blob_files=1 --blob_direct_write_partitions=4 \
--disable_wal=1 --threads=1
```
Reviewed By: pdillinger
Differential Revision: D98766843
Pulled By: xingbowang
fbshipit-source-id: 1577653826913a59d05680a87bce5534ac5a5e69
Summary:
Transactions in rockdb supported adding log data using the `put_log_data` api, but this was not exported for other language bindings. Exported this binding allows other languages like rust, go, etc add log data on an transaction
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14543
Reviewed By: joshkang97
Differential Revision: D99171663
Pulled By: xingbowang
fbshipit-source-id: 24c94d71668a41cc7b2913972427255ab67d0863
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14521
ReadAsync calls io_uring_get_sqe() without checking for nullptr. When the
io_uring submission queue is full (outstanding completions not yet reaped),
io_uring_get_sqe returns NULL and the subsequent io_uring_prep_readv
dereferences it, causing a segfault.
MultiRead already handles this correctly by using io_uring_sq_space_left()
to cap submissions. ReadAsync submits exactly one SQE per call so a simple
null check with error return is sufficient.
On null sqe, clean up the already-allocated Posix_IOHandle and return
IOStatus::Busy so the caller can retry after reaping completions.
The io_uring queue depth is kIoUringDepth (256), and each thread gets its
own io_uring instance via thread-local storage. In practice the SQ rarely
fills because ReadAsync calls io_uring_submit() after each io_uring_get_sqe(),
immediately flushing the SQE to the kernel. The null SQE would only occur
under unusual kernel backpressure where the kernel cannot consume from the
SQ ring fast enough.
IOStatus::Busy was chosen (over IOError) because this is a transient
condition. The caller has two options:
1. Call Poll() to reap outstanding completions from the CQ, then retry
ReadAsync. This mirrors how MultiRead handles queue pressure internally
by capping submissions and reaping between batches.
2. Fall back to synchronous Read(). Existing callers (FilePrefetchBuffer,
IODispatcher) already have synchronous fallback paths for non-OK
ReadAsync status, so IOStatus::Busy naturally triggers that fallback
without additional code changes. Given the rarity of this condition,
the synchronous fallback is pragmatic and avoids adding retry complexity.
Also adds a TEST_SYNC_POINT_CALLBACK on io_uring_get_sqe to enable test
injection, and a new ReadAsyncQueueFull unit test that uses SyncPoint to
force a null SQE and verifies the Busy return, handle cleanup, and no crash.
Reviewed By: xingbowang
Differential Revision: D98533853
fbshipit-source-id: f6d181e5c0d5154b570ff6da39e2f52e2a6aea84
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14514
SupportedOps previously checked only that the ThreadLocalPtr container existed (non-null), which was set during construction based on a one-time probe on the main thread. However, CreateIOUring() can fail on other threads due to kernel resource limits or flag incompatibilities, causing ReadAsync to hit "failed to init io_uring" at runtime.
Now SupportedOps eagerly initializes the thread-local io_uring instance via Get() + CreateIOUring() and only advertises kAsyncIO if it succeeds. Also logs to stderr (with thread id) when io_uring init fails, and adds a TEST_SYNC_POINT_CALLBACK for testing simulated CreateIOUring failures.
Reviewed By: mszeszko-meta, xingbowang
Differential Revision: D98409140
fbshipit-source-id: efa92d9ac920860e95a46710c4a87e36bacbb466
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14522
Add two verification steps to RocksDB CLAUDE.md:
1. **Unit Test flakiness testing**: After writing a test, stress-test for
flakiness with `COERCE_CONTEXT_SWITCH=1 make {test_binary}` followed by
`--gtest_repeat=5`. This catches race conditions and context-switch-dependent
failures early.
2. **Status object verification**: Run `ASSERT_STATUS_CHECKED=1 make check`
to catch missing error handling that can lead to silent data corruption.
These are compile-time checks enforced via a special build mode.
These are existing RocksDB best practices that both the stress test agent
and human developers should follow when writing or fixing code.
Reviewed By: xingbowang
Differential Revision: D98249994
fbshipit-source-id: 25ca6a628a82ecf968d5ed86aaa5c2f4b1060471
Summary:
This PR fixes several correctness and stability issues in the stress test and crash test infrastructure, plus adds regression coverage for trie UDI iterators.
### db_stress: constrain batched prefix scans
`BatchedOpsStressTest::TestPrefixScan` opens 10 iterators with different prefixes and compares results in lockstep. Without `prefix_same_as_start`, unconstrained iterators could return keys beyond their seek prefix, producing more entries than bounded iterators and causing spurious assertion failures. Fix: set `prefix_same_as_start = true` on all iterators so every iterator stops at the seek prefix boundary.
### db_stress: skip invalid cross-prefix iterator checks
When prefix iteration is enabled, `ReadOptions` requires that the seek key and `iterate_lower_bound` share the same prefix. Previously, stress test verification could run against configurations where they don't (e.g. when `lower_bound` spans a prefix boundary), producing false positives. Fix: detect this invalid configuration and mark the check as diverged (skip verification) rather than asserting.
### db_crashtest: disable BlobDB in best-efforts recovery
BlobDB is not compatible with best-efforts recovery mode. Fix: explicitly disable blob file options when `best_efforts_recovery=True` in db_crashtest.py to avoid spurious failures.
### db_crashtest: preserve expected-state dirs across restarts
The expected-state directory was being wiped on certain restart paths, causing verification failures on the next run. Fix: preserve expected-state dirs across db_crashtest restarts. Adds a new `db_crashtest_test.py` test to verify the behavior, runnable via `make db_crashtest_test`.
### Add trie UDI iterator regression coverage
Adds regression tests for trie-based UserDefinedIndex (UDI) iterators covering snapshot-based reads, lower/upper bound iteration, `auto_refresh_iterator_with_snapshot`, and multi-version key handling. Also fixes an MSVC C4244 warning (int→char implicit narrowing) in the test.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14512
Reviewed By: hx235
Differential Revision: D98302018
Pulled By: xingbowang
fbshipit-source-id: 84f5878665e5aeb61338ec2b6bfb2d2b077bb9f2
Summary:
CONTEXT: MyRocks and other third-party users currently need to understand deep RocksDB internals to sort unsorted data into SST files. The existing pattern requires: opening a full DB instance, configuring VectorRepFactory with universal compaction, disabling auto-compaction, writing unsorted data, manually triggering CompactRange with kForceOptimized, collecting output via GetColumnFamilyMetaData, and finally ingesting via IngestExternalFile with allow_db_generated_files. This is error-prone and requires ~30 lines of RocksDB configuration knowledge.
WHAT: This adds a new utility class, SortedRunBuilder, that wraps all of the above into a simple Create/Add/Finish API. Callers feed in unsorted key-value pairs (in any order, from any number of threads) and receive sorted SST files with seqno=0 ready for ingestion -- or can iterate sorted output directly.
The name SortedRunBuilder was chosen over alternatives like UnsortedSstFileWriter because this utility targets a broad audience: anyone wanting to use RocksDB as an external sort engine, not just users migrating from SstFileWriter. That said, this explicitly removes the sorted-input requirement that SstFileWriter enforces -- callers no longer need to pre-sort their data before writing SST files.
KEY DESIGN DECISIONS:
- Zero changes to core RocksDB internals. This is a pure utility layer that exclusively calls existing public APIs:
* DB::Open(), DB::Put(), DB::Write(), DB::Flush(), DB::CompactRange(), DB::GetColumnFamilyMetaData(), DB::NewIterator(), DestroyDB()
* VectorRepFactory (sort-on-flush memtable)
* BottommostLevelCompaction::kForceOptimized (zero seqnos)
- No modifications to: compaction logic, memtable implementation, SST file format, ingestion logic, or DB open/close paths
- All new code lives in utilities/sorted_run_builder/ and the public header include/rocksdb/utilities/sorted_run_builder.h
- Build file changes are limited to registering the new source/test files
API SURFACE:
SortedRunBuilderOptions opts; opts.temp_dir = "/tmp/sort_work"; std::unique_ptr<SortedRunBuilder> builder; SortedRunBuilder::Create(opts, &builder);
builder->Add(key, value); // any order, thread-safe
builder->AddBatch(&batch); // WriteBatch for throughput
builder->Finish(); // flush + compact + collect
builder->GetOutputFiles(); // sorted SSTs for ingestion
builder->NewIterator(ro); // iterate sorted output
FILES CHANGED:
New: include/rocksdb/utilities/sorted_run_builder.h (public header) New: utilities/sorted_run_builder/sorted_run_builder.cc (implementation) New: utilities/sorted_run_builder/sorted_run_builder_test.cc (12 tests) New: docs/plans/sorted_run_builder_plan.md (design plan) New: docs/plans/sorted_run_builder_usage_guide.md (usage guide) Modified: src.mk, CMakeLists.txt, Makefile (register new files only)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14499
Test Plan:
$ make clean && make -j$(nproc) sorted_run_builder_test $ ./sorted_run_builder_test [==========] Running 12 tests from 1 test case. [ PASSED ] 12 tests. (688 ms total)
Tests cover: basic sort correctness, empty builder, WriteBatch path, concurrent multi-threaded writes, ingestion into a target DB, large random dataset (10K keys), entry/size counters, error cases (Add after Finish, iterator before Finish, empty temp_dir), cleanup verification, and duplicate key handling.
Reviewed By: xingbowang
Differential Revision: D98935972
Pulled By: dannyhchen
fbshipit-source-id: e3bb7f1ea5f6004aab697e4da667fa21292ca250
Summary:
`teardown-ccache` runs with if: always(), but in folly jobs `setup-folly` precedes `setup-ccache`. When `setup-folly` fails, `setup-ccache` is skipped, so `CCACHE_DIR` is unset and ccache is not on `PATH`. This is exactly what happened [here](https://github.com/facebook/rocksdb/actions/runs/23660261947/job/68928337162). Fix guards `teardown-ccache` to exit gracefully when `CCACHE_DIR` is unset, and tolerate missing ccache binary for stats.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14542
Reviewed By: joshkang97
Differential Revision: D98964757
Pulled By: mszeszko-meta
fbshipit-source-id: 8e83c1b62ef66130ba479142f002897d0a97f6c0
Summary:
The warm_storage_crash_test was failing because UniqueIdVerifier stored its .unique_ids bookkeeping file in the DB directory, which lives on warm storage. After a crash, warm storage's weaker durability guarantees could cause flushed-but-not-synced data to be lost, making the file appear shorter on a second open within the same constructor. This caused CopyFile to return Corruption and trigger assert(false).
Move the file to expected_values_dir (which is always on local filesystem) and always use Env::Default() for file operations, so that POSIX flush semantics are sufficient for read consistency without needing Sync.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14539
Test Plan: Full local run of `make blackbox_crash_test`
Reviewed By: mszeszko-meta
Differential Revision: D98934451
Pulled By: pdillinger
fbshipit-source-id: 5497f23c2382cb5ac2c3d7f238c6c4ca1dd29f5b
Summary:
Adds regression tests and failure-diagnostic tooling for the trie UDI post-seek seqno correction bug (T258590238). The core fix itself already landed in https://github.com/facebook/rocksdb/issues/14466.
## What's in this PR
### Regression tests
- `TrieIndexFactoryTest.ZeroSeqMustNotSkipLeafForSmallerUserKey` — minimal unit test proving the original bug; exercises the seqno-based block advancement with a smaller user key that should NOT trigger advancement
- `TrieIndexDBTest.AutoRefreshSnapshotNextAcrossSameUserKeyBoundaries` — DB-level test for snapshot-refresh iteration across same-user-key block boundaries
- `TrieIndexDBTest.AutoRefreshSnapshotNextAfterCompactionAcrossSameUserKeyBoundaries` — same scenario after compaction reshapes SST layout
- `TrieIndexDBTest.AutoRefreshSnapshotStressLikeSingleCfCoalescingIterator` — stress-like test exercising snapshot-refresh + coalescing-iterator interaction
### Diagnostic logging in db_stress
Extracts the iterator verification failure dump into a `DumpIteratorVerificationFailure()` helper in `NonBatchedOpsStressTest`. On verification failure, logs:
- Expected-state window (pre/post read values, raw state, pending flags)
- Iterator config (UDI, trie, snapshot, multi-CF)
- Replay comparison: creates fresh iterators with standard vs trie index, direct vs coalescing, seek-to-failure-key vs replay-from-mid — making it possible to identify which index/iterator combination diverges
This logging was instrumental in triaging the original bug and will help with future trie UDI stress failures.
## Tests
All existing trie index tests pass. New tests listed above all pass.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14524
Reviewed By: hx235
Differential Revision: D98583342
Pulled By: xingbowang
fbshipit-source-id: 57e099055d6beae9b5f03f4e6605f0af6e65b94a
Summary:
The following performance optimizations are included in this PR:
- Inline NextSetBit/PrevSetBit into header (called on every trie level)
- Unroll Rank1 popcount loop (the single hottest function)
- Advance/Retreat: replace path stack top in-place instead of pop+push
- std::swap for prev_key_scratch_ instead of O(n) string copy
- assign() instead of ToString() to reuse buffer capacity
- Cache IndexValue in wrapper to avoid repeated virtual dispatch
- Mark TrieIndexIterator/TrieIndexBuilder as final for devirtualization
Part of https://github.com/facebook/rocksdb/issues/12396
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14466
Reviewed By: anand1976
Differential Revision: D97979699
Pulled By: xingbowang
fbshipit-source-id: 7ec56ecc8b6d68548a336dd1aaccfb215189eff0
Summary:
Add tracking of uniform index blocks as a table property (`num_uniform_blocks`). When `uniform_cv_threshold` is set, the block builder detects uniformly distributed keys via coefficient of variation of key gaps. This information is now surfaced end-to-end: from block building through index builders to SST table properties.
## Key changes
- **BlockBuilder**: Persist the uniformity result from `ScanForUniformity()` as a member (`is_uniform_`) exposed via `IsUniform()`, rather than a local variable discarded after `Finish()`.
- **Index builders**: All three index builder types (`ShortenedIndexBuilder`, `HashIndexBuilder`, `PartitionedIndexBuilder`) implement `NumUniformIndexBlocks()`. For partitioned indexes, the count accumulates across partition `Finish()` calls.
- **Table property serialization**: Added `TablePropertiesNames::kNumUniformBlocks` (`"rocksdb.num.uniform.blocks"`) with full serialization/deserialization support. Without this, the property was computed in memory but never persisted to SST files.
- **Table property aggregation**: `num_uniform_blocks` included in `Add()` and `GetAggregatablePropertiesAsMap()` for correct cross-SST aggregation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14513
Test Plan:
- **Unit tests**: `block_test` (7504 passed), `table_test` (6917 passed), `table_properties_collector_test` (8 passed).
- **End-to-end verification via db_bench + sst_dump**:
- Positive: `./db_bench --benchmarks=fillseq,compact --num=10000 --uniform_cv_threshold=0.2 --index_shortening_mode=0 --compression_type=none` produces SST with `# uniform blocks: 1`.
- Negative: Same with `--uniform_cv_threshold=-1` (disabled) produces `# uniform blocks: 0`.
Reviewed By: xingbowang
Differential Revision: D98343170
Pulled By: joshkang97
fbshipit-source-id: ed08e83b2dcfb70f074bfb0f7bb5c31d75dc6da9
Summary:
## Problems
**(1) Claude review times out on large PRs**
The `claude-code-base-action` defaults to `timeout_minutes=10`. Large PRs (e.g. https://github.com/facebook/rocksdb/issues/14499) get killed with exit code 124 after 600 seconds mid-review.
**(2) Exported PRs never get reviewed**
PRs exported from Meta's internal pipeline (e.g. https://github.com/facebook/rocksdb/issues/14515) trigger CI within milliseconds of PR creation. The `workflow_run` payload has `pull_requests=[]`, and the SHA-based fallback also misses because GitHub hasn't registered the PR yet — so Claude review never fires.
## Fixes
- Set `timeout_minutes: "60"` on both auto-review and manual-review `Run Claude` steps
- Retry the SHA→PR lookup up to 5 times with a 10s delay, giving GitHub up to ~50s to register the PR. Also bumped `per_page` from 30 to 100.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14526
Reviewed By: pdillinger
Differential Revision: D98636217
Pulled By: xingbowang
fbshipit-source-id: bba19095ab2ddf468c5c19cf5c77d19536f498b0
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14520
CreateIOUring() silently returns nullptr on failure, discarding the errno
from io_uring_queue_init. This makes it impossible to diagnose why
io_uring initialization fails on specific threads (e.g. ENOMEM from
memlock limits, EINVAL from unsupported flags, EMFILE from fd exhaustion).
Add a fprintf(stderr, ...) that logs strerror, errno, and pthread thread
ID when io_uring_queue_init fails, so failures are diagnosable from logs
without needing to reproduce.
Reviewed By: xingbowang
Differential Revision: D98526792
fbshipit-source-id: 1eb5042c8b62663c4d24c09f29ef7c55b90032f0
Summary:
Add read-triggered compaction, a new feature that reduces read amplification by compacting SST files that receive high read traffic. When an SST file's read frequency (`num_reads_sampled / file_size`) exceeds a configurable threshold, it is marked for compaction to a lower level.
The feature introduces two new options: a CF option `read_triggered_compaction_threshold` (default 0, disabled) and a DB option `max_periodic_compaction_trigger_seconds` (default 43200s) that controls how often the background thread re-evaluates compaction scores on quiet databases. Both options are dynamically changeable.
Lowering `max_periodic_compaction_trigger_seconds` does add some overhead, but generally is minimal, so running this every couple of minutes in a production environment seems fairly reasonable.
## Key changes
- **New CF option `read_triggered_compaction_threshold`** (`advanced_options.h`): When positive, files with `reads_per_byte > threshold` are marked for compaction. Files at the last non-empty level are skipped (bottommost compaction handles those separately). Marked files are sorted by hotness (reads_per_byte descending).
- **New DB option `max_periodic_compaction_trigger_seconds`** (`options.h`): Replaces the hardcoded 12-hour ceiling in `ComputeTriggerCompactionPeriod()`. Essential for read-triggered compaction on quiet DBs since there are no writes to trigger score re-evaluation.
- **Leveled compaction picker** (`compaction_picker_level.cc`): Adds read-triggered as the lowest-priority compaction reason in `SetupInitialFiles()`, using the existing `PickFileToCompact` helper.
- **Universal compaction picker** (`compaction_picker_universal.cc`): Adds `PickReadTriggeredCompaction` as lowest priority. Refactors shared "find output level + compute overlapping inputs + create Compaction" logic from both `PickDeleteTriggeredCompaction` and `PickReadTriggeredCompaction` into `BuildCompactionToNextLevel`, handling both single-level and multi-level universal cases.
- **Periodic trigger integration** (`db_impl.cc`): `TriggerPeriodicCompaction` now also fires for CFs with `read_triggered_compaction_threshold > 0`, even without time-based compaction configured.
- **Stress test & db_bench support**: Both `db_stress` and `db_bench` support the new options. `db_crashtest.py` randomly enables read-triggered compaction and sets a short periodic trigger interval when enabled.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14426
Test Plan:
**Unit tests**:
- `compaction_picker_test` — 7 new tests: `ReadTriggeredCompactionDisabled`, `ReadTriggeredCompactionBelowThreshold`, `ReadTriggeredCompactionAboveThreshold`, `NeedsCompactionReadTriggered`, `ReadTriggeredPicksFile`, `UniversalReadTriggeredCompaction`, `ReadTriggeredSkipsLastLevel`, `UniversalReadTriggeredNoPickWhenNotMarked`
- `db_compaction_test` — `ReadTriggeredCompaction` integration test verifying end-to-end behavior with sync points
- Stress test coverage
**Stress test**:
```
make V=1 -j "CRASH_TEST_EXT_ARGS=--duration=600 --max_key=2500000 --max_compaction_trigger_wakeup_seconds=10
--read_triggered_compaction_threshold=0.0001 --interval=600" blackbox_crash_test
```
- confirmed read triggered compactions from LOGS
**Benchmark** (`db_bench`):
Setup: 5M keys (100B values, 16B keys), leveled compaction, 5 levels, 4MB target file size. DB fully compacted, then 2M overlapping keys written without compaction to create L0/L1 overlap (82 files, ~294MB).
LSM shape change during readrandom with read-triggered compaction:
```
BEFORE: L0=9 files (15MB), L1=4 (16MB), L2=20 (69MB), L3=49 (194MB) — 82 files, 294MB
AFTER: L3=66 files (223MB)
```
| Benchmark | Config | avg ops/s | % change |
|-----------|--------|-----------|----------|
| readrandom (8 threads, 5M reads) | baseline (threshold=0) | 1,086,965 | — |
| readrandom (8 threads, 5M reads) | threshold=0.000001, trigger=5s | 1,453,697 | **+33.7%** |
Reviewed By: xingbowang
Differential Revision: D97838716
Pulled By: joshkang97
fbshipit-source-id: a21fcb270c7fadd4f78d98b9c821982f220dd3f0
Summary:
When `min_blob_size=0`, the existing guard condition:
```cpp
if (value.size() < min_blob_size_) {
return Status::OK(); // skip blob creation
}
```
is **false** for empty values (`0 < 0`), so empty values proceed to blob creation. This writes a 0-byte blob to disk and creates a `BlobContents` object with an empty `Slice`.
When that `BlobContents` is later evicted from the primary blob cache to `CompressedSecondaryCache`, the eviction handler calls `SaveTo(value, 0, 0, out)`, which hits:
```cpp
// typed_cache.h:230
assert(from_offset < slice.size()); // 0 < 0 → CRASH
```
This crash was found by the stress test with `--min_blob_size=0` and `--blob_cache` + secondary cache enabled (T261142690).
## Fix
Add an explicit `value.empty()` check before the blob path:
```cpp
if (value.empty() || value.size() < min_blob_size_) {
return Status::OK();
}
```
Empty values are now always stored inline in the SST, regardless of `min_blob_size`. This is also correct on principle: a `BlobIndex` reference is larger than an empty value, so storing an empty value as a blob is pure overhead with no benefit.
## Root Cause Chain
1. User writes `Put("key", "")` with `min_blob_size=0`
2. `BlobFileBuilder::Add()` — `0 < 0` is false, empty value proceeds to blob creation
3. 0-byte blob written to blob file; `BlobContents` created with 0-size slice
4. `BlobContents` inserted into primary blob cache (LRU)
5. Cache eviction triggers `CacheWithSecondaryAdapter::EvictionHandler()`
6. `CompressedSecondaryCache::InsertInternal()` → `SaveTo(value, 0, 0, out)`
7. `assert(from_offset < slice.size())` → `assert(0 < 0)` → **** assertion failure
## Test
Added `DBBlobBasicTest.EmptyValueNotStoredAsBlob` which:
- Writes an empty value and a non-empty value with `min_blob_size=0`
- Verifies both are readable
- Confirms the empty value is stored **inline** (readable from `kBlockCacheTier` without blob I/O)
- Confirms the non-empty value is stored as a **blob** (returns `IsIncomplete()` from `kBlockCacheTier`)
## Related
- Task: T261142690
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14517
Reviewed By: hx235
Differential Revision: D98499174
Pulled By: xingbowang
fbshipit-source-id: 5713923daec83db6491d00ce58acdf8231fabeba
Summary:
**Summary:**
Add a new boolean flag `include_blob_files` (default: `false`) to `SizeApproximationOptions` and a corresponding `INCLUDE_BLOB_FILES` enum value to `SizeApproximationFlags`. When set to `true`, the returned size includes an approximation of blob file data in the queried key range.
**Algorithm:**
The blob file size contribution is prorated using the SST size ratio:
```
blob_size_in_range ≈ total_blob_size * (sst_size_in_range / total_sst_size)
```
The blob-to-SST ratio (`total_blob_size / total_sst_size`) is computed once before the per-range loop, so iterating levels and blob files only happens once per `GetApproximateSizes` call regardless of how many ranges are queried. The per-range SST size (`ApproximateSize`) is computed once and shared between `include_files` and `include_blob_files`.
**Limitations:**
- Assumes blob data is distributed proportionally to SST data across the key space. May be inaccurate if blob value sizes vary significantly across different key ranges (e.g., one range has large blobs while another has small ones).
- If there are no SST files (all data in memtables), the blob size contribution will be 0 even if blob files exist on disk.
**Changes:**
- `include/rocksdb/options.h`: New `include_blob_files` field in `SizeApproximationOptions`; updated doc comments for `include_memtables`/`include_files`
- `include/rocksdb/db.h`: New `INCLUDE_BLOB_FILES` in `SizeApproximationFlags` enum, updated flags-to-options mapping
- `include/rocksdb/c.h`: New `rocksdb_size_approximation_flags_include_blob_files` C API enum value
- `java/`: Added `INCLUDE_BLOB_FILES` to `SizeApproximationFlag.java` and JNI flag mapping in `rocksjni.cc`
- `db/db_impl/db_impl.cc`: Blob-to-SST ratio computed once before loop, SST range size computed once per range and shared
- `db_stress_tool/db_stress_test_base.cc`: Randomized `include_blob_files` in stress test
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14501
Test Plan:
- New `DBBlobBasicTest.GetApproximateSizesIncludingBlobFiles` — verifies:
- Size with blobs > without (full range)
- Non-overlapping range returns 0
- Partial range returns proportionally less than full range
- `SizeApproximationFlags` API works
- Multi-range query: two sub-ranges sum approximately to the full-range result
- Stress test now exercises the new option randomly
Reviewed By: hx235
Differential Revision: D97984211
Pulled By: xingbowang
fbshipit-source-id: e9127eac3308687fd4f0b17a771fd61fba6a8380
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14518
When RocksDB creates an io_uring instance via `CreateIOUring()`, it calls
`io_uring_queue_init()` under the hood. This function asks the Linux kernel
to set up a submission queue and completion queue for async I/O — the kernel
allocates file descriptors and memory-mapped ring buffers to make this work.
To properly release those kernel resources, you must call
`io_uring_queue_exit()` before freeing the `io_uring` struct. This function
tells the kernel "I'm done with this io_uring" — it unmaps the shared
memory regions and closes the kernel file descriptors. Without it, those
resources leak every time an io_uring instance is destroyed.
`DeleteIOUring()` (the ThreadLocalPtr destructor callback) was only doing
`delete iu` — freeing the C++ struct's heap memory but never telling the
kernel to clean up. This meant every thread exit leaked kernel resources.
The same bug also existed in the `PosixFileSystem` constructor, which
creates a temporary test io_uring to check kernel support and then did
`delete new_io_uring` without cleanup.
Fix:
1. Add `io_uring_queue_exit(iu)` before `delete iu` in `DeleteIOUring()`.
2. Replace bare `delete new_io_uring` in fs_posix.cc with
`DeleteIOUring(new_io_uring)` to reuse the now-correct helper.
Note: the error-recovery path in io_posix.cc (~line 907) already correctly
calls `io_uring_queue_exit()` before `delete`, confirming this was the
intended pattern that was missed in these two spots.
Reviewed By: anand1976
Differential Revision: D98500559
fbshipit-source-id: bd6c10c19d9fd67cf537dd7f100ef9dc49bfe77e
Summary:
Fix build failures, flaky tests, and Windows ccache issues exposed by PR https://github.com/facebook/rocksdb/issues/14478.
### 1. Release build (NDEBUG) compile error
**Job**: `build-linux-release-with-folly`
The SyncPoint cleanup listener in testharness.cc referenced `SyncPoint::GetInstance()` unconditionally, but SyncPoint is only declared in debug builds. Wrapped with `#ifndef NDEBUG`.
### 2. Folly-lite link error
**Job**: `build-linux-cmake-with-folly-lite`
The `USE_FOLLY_LITE` cmake path unconditionally linked `-lglog`, which fails with lld (added in https://github.com/facebook/rocksdb/issues/14478) when glog isn't installed. Use `find_library()` to link only when available.
### 3. Flaky tests — leaked `Env::Default()` thread pool state
Sharded execution runs multiple tests per process. Several tests in `db_compaction_test.cc` modified the global `Env::Default()` BOTTOM thread pool without resetting. With a leaked BOTTOM pool, subsequent tests' last-level compactions get forwarded to the bottom pool, freeing the LOW thread to pick additional compactions unexpectedly.
Fix: add `TearDown()` override to `DBCompactionTest` that captures default thread pool sizes in the constructor and restores them after every test. This is more robust than per-test cleanup because:
- It runs even when a test fails (gtest calls TearDown after assertion failures)
- It catches all current and future leakers without per-test maintenance
### 4. Windows ccache — 0.26% hit rate → should be ~99%
The Windows nightly build takes 57 minutes because ccache has near-zero hit rate. Two issues:
- **Cache key**: The `hendrikmuhs/ccache-action` used a timestamp-based key, so each nightly run created a unique key and never found the previous run's cache (`No cache found.`). Fixed by using a stable key `ccache-windows-<workflow>` with prefix-based restore.
- **Compiler check**: Missing `compiler_check=content` setting, so MSVC path/version changes between runners invalidated all cache entries. Added `compiler_check=content` (same fix as macOS in https://github.com/facebook/rocksdb/issues/14478).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14508
Test Plan:
- Release and debug builds compile cleanly
- 70 consecutive shuffled runs of db_compaction_test (367 tests each) pass — 50 on full cores + 20 on 4 cores
- Format check passes
- Windows ccache fix requires CI run to verify (first run populates cache, second run should see high hit rate)
Reviewed By: anand1976
Differential Revision: D98380757
Pulled By: xingbowang
fbshipit-source-id: d74079b75786ba3299e335b145a6c5fdc81fd5c1
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14510
When a filesystem does not return kAsyncIO in SupportedOps(), MultiScan
still sends ReadAsync/Poll/AbortIO requests. Regular iterators check via
CheckFSFeatureSupport in ArenaWrappedDBIter::Init and ForwardIterator,
but MultiScan blindly trusted the caller-provided
MultiScanArgs::use_async_io flag.
Fix: In the MultiScan constructor, after scan_opts_ is initialized,
check CheckFSFeatureSupport and disable use_async_io if the FS doesn't
support it. Also pass the (potentially modified) scan_opts_ to Prepare()
instead of the original scan_opts parameter.
Reviewed By: mszeszko-meta
Differential Revision: D97995735
fbshipit-source-id: 331639d950fd3cb9f491feed996d5820294beb4e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14511
Add `atomic_flush` option to `CreateBackupOptions` that plumbs through
`CheckpointImpl::CreateCustomCheckpoint` to `LiveFilesStorageInfoOptions`.
When `flush_before_backup=true` and `atomic_flush=true`, the backup engine
atomically flushes all column families before creating the backup. This
ensures cross-CF consistency without needing WAL files. Combined with
`BackupEngineOptions::backup_log_files=false`, this allows safely skipping
WAL backup for multi-CF databases, reducing backup size and complexity.
Changes:
- `backup_engine.h`: Add `bool atomic_flush = false` to `CreateBackupOptions`
- `checkpoint_impl.h/.cc`: Add `bool atomic_flush` parameter to
`CreateCustomCheckpoint`, plumb to `LiveFilesStorageInfoOptions::atomic_flush`
- `backup_engine.cc`: Pass `options.atomic_flush` through to
`CreateCustomCheckpoint`
- `checkpoint_test.cc`: Add `BackupWithAtomicFlushSkipsWAL` test verifying
backup with atomic flush + no WAL creates a valid restorable multi-CF backup
Reviewed By: xingbowang
Differential Revision: D98208696
fbshipit-source-id: e818ba8669ac52a206e30e9dd56f1d7573cc0175
Summary:
Improves the Claude Code review CI workflow to produce deeper, more reliable reviews.
**Motivation:** The previous 30-turn limit caused reviews to silently produce "no output" on complex PRs (e.g. https://github.com/facebook/rocksdb/issues/14477). The review prompt was also too shallow — single-pass with no codebase context phase.
**Changes:**
**1. Comprehensive multi-agent review prompt** (`claude_md/ci_review_prompt.md`)
- 9 specialized review agents: design, correctness, cross-component, invariant-adversary, caller-audit, performance, API, serialization, test coverage
- Deep codebase context phase before agents spawn: caller-chain analysis (3-5 levels up), callee side-effect tracing, cross-component data consumer analysis, execution context verification, assumption stress-testing
- Inter-agent debate with round-robin critique assignments
- Final report quality rules: disproven findings removed, no stream-of-consciousness
**2. Incremental findings + recovery flow**
- `Write` tool added so Claude saves findings to `review-findings.md` after each phase
- If the review hits the turn limit, a recovery step launches a Sonnet session to format partial findings into the standard output
- Recovery file existence check prevents crash if recovery step fails
- `getLastAssistantText` fallback truncated to 50KB to avoid enormous PR comments
**3. Prompts extracted to files** (`claude_md/ci_*.md`)
- `ci_review_prompt.md` — full review methodology
- `ci_query_prompt.md` — `/claude-query` system prompt
- `ci_recovery_prompt.md` — recovery formatting prompt
- Secure: checkout is from base branch (main), not PR head
**4. `max_turns` increased from 30 to 300**
- Orchestrator budget for multi-agent workflow; sub-agents get their own turns
- Recovery flow ensures partial results if limit is still hit
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14507
Reviewed By: archang19
Differential Revision: D98170111
Pulled By: xingbowang
fbshipit-source-id: 390626a53e7a7f91c2d3e91ed4403494532425ed
Summary:
Add `SstFileReader::Get` (single-key) and `SstFileReader::MultiGet` (PinnableSlice) overloads to enable zero-copy point lookups directly from SST files. The existing `MultiGet(std::string*)` is refactored to delegate to the new `MultiGet(PinnableSlice*)`, which writes results directly into caller-provided `PinnableSlice` values instead of copying through an intermediate buffer. The single-key `Get` uses `TableReader::Get` with a `GetContext` for efficient single-key lookups without the overhead of MultiGet's sorting and batching machinery.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14500
Test Plan: - New unit tests
Reviewed By: xingbowang
Differential Revision: D97825648
Pulled By: joshkang97
fbshipit-source-id: 17f3edd59bbf4747d17309c44ef12f0d952ea4eb
Summary:
Add `force_atomic_flush` to `FlushOptions` so any flush caller (Checkpoint, `DB::Flush()`, etc.) can force atomic flush of all column families even when `DBOptions::atomic_flush` is disabled.
## Changes
### Public API
- Added `bool force_atomic_flush = false` to `FlushOptions`
- Added `bool atomic_flush = false` to `LiveFilesStorageInfoOptions`
### Flush dispatch
- `FlushAllColumnFamilies()` — uses `immutable_db_options_.atomic_flush || flush_options.force_atomic_flush`
- `DB::Flush()` (single-CF and multi-CF) — respects `force_atomic_flush`
- `FlushForGetLiveFiles()` — constructs `FlushOptions` with `force_atomic_flush` from `LiveFilesStorageInfoOptions::atomic_flush`
### Per-request atomic flush in pipeline
- `FlushRequest.atomic_flush` and `BGFlushArg.atomic_flush_` carry the per-request flag
- `GenerateFlushRequest`, `EnqueuePendingFlush`, `PopFirstFromFlushQueue`, `BackgroundFlush`, `FlushMemTablesToOutputFiles` all use the per-request flag
### Stress test
- Added `--checkpoint_atomic_flush` flag to db_stress, randomly enabled in db_crashtest.py
### Unit tests (6 new)
- End-to-end checkpoint with atomic flush override
- Negative test (default = no atomic flush)
- Single-CF edge case
- Interaction with `DBOptions::atomic_flush=true`
- Mixed non-atomic then atomic flushes in queue
- Mixed atomic then non-atomic flushes in queue
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14477
Reviewed By: joshkang97
Differential Revision: D97639233
Pulled By: xingbowang
fbshipit-source-id: b924c044d6179e68b38c6604eb46c9140516d42a
Summary:
FIFO compaction drops old SST files when total size exceeds `max_table_files_size`
or `max_data_files_size`. The stress test's expected state can't track these drops,
causing false "GetEntity returns NotFound" failures. Confirmed by
`rocksdb.fifo.max.size.compactions COUNT: 7` in an asan_crash_test whitebox run
(`compaction_style=2`, `fifo_compaction_max_data_files_size_mb=100`).
This diff adds a `fifo_compaction_max_table_files_size_mb` flag and sets it to
100GB in db_crashtest.py. Since `max_table_files_size` is always active (default
1GB), it must always be set very high. `fifo_compaction_max_data_files_size_mb`
is randomized between 0 (disabled, defers to `max_table_files_size`) and 100GB
(overrides `max_table_files_size`) to exercise both code paths while preventing
drops in either case. FIFO intra-L0 compaction (`fifo_allow_compaction`) is
still exercised.
Long-term TODO: handle FIFO drops in expected state via `OnCompactionBegin`
listener calling `SetPendingDel()` on affected key ranges, treating drops as
concurrent deletes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14503
Reviewed By: joshkang97
Differential Revision: D97601382
Pulled By: hx235
fbshipit-source-id: 1a3badfa4de47efd73f965807982a966ebe135ef
Summary:
Fix thread-safety issues in memtable stats tracking and `MaybeUpdateNewestUDT` to prepare for PR https://github.com/facebook/rocksdb/issues/14448, which introduces concurrent range tombstone insertion from the read path into the mutable memtable. Currently the non-concurrent write path updates memtable counters (`num_entries_`, `num_deletes_`, `num_range_deletes_`, `data_size_`) using non-atomic load/store pairs. While this is safe today PR https://github.com/facebook/rocksdb/issues/14448 will make range_tombstone memtable concurrent, but the main write path can still be non-concurrent. This fix ensures stats are tracked correctly.
Similarly, `MaybeUpdateNewestUDT` was not thread-safe and was not called in the concurrent write path at all. This PR also fixes the `post_process_info` delete counter which missed `kTypeSingleDeletion` and `kTypeDeletionWithTimestamp`.
## Key changes
- Switch non-concurrent counter updates from `LoadRelaxed`/`StoreRelaxed` pairs to `FetchAddRelaxed` to avoid races with concurrent range tombstone inserts (e.g. `AddLogicallyRedundantRangeTombstone` calling `BatchPostProcess`).
- Make `MaybeUpdateNewestUDT` thread-safe by replacing `Slice newest_udt_` with `RelaxedAtomic<const char*> newest_udt_data_` and using a CAS loop. The pointed-to memory lives in the arena and remains valid for the memtable's lifetime.
- Call `MaybeUpdateNewestUDT` in the concurrent write path (was previously skipped with a TODO).
- Fix `post_process_info->num_deletes++` to also count `kTypeSingleDeletion` and `kTypeDeletionWithTimestamp`, matching the non-concurrent path.
- Change `GetNewestUDT()` return type from `const Slice&` to `Slice` (returning by value) to avoid dangling reference issues with the new atomic pointer storage. Updated across `MemTable`, `ReadOnlyMemTable`, `MemTableListVersion`, and `WBWIMemTable`.
- Remove unused `Slice newest_udt_` member from `WBWIMemTable`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14506
Test Plan:
- Added `ConcurrentWriteMemTableProperties` test in `db_properties_test.cc`: 4 threads writing puts, deletes, and single deletes concurrently, then verifying `rocksdb.num-entries-active-mem-table` and `rocksdb.num-deletes-active-mem-table` properties are correct. Flush with `flush_verify_memtable_count=true` validates integrity.
- Added `ConcurrentGetTableNewestUDT` test in `memtable_list_test.cc`: 4 threads concurrently inserting entries with UDTs via `allow_concurrent=true`, verifying the newest UDT is correctly tracked.
- Extended `DuplicateSeq` and `ConcurrentMergeWrite` tests in `db_memtable_test.cc` to verify stat counters after `BatchPostProcess`.
- Benchmark results show no performance regression (3 runs each, averaged):
**Workload 1: fillrandom** (pure Put workload)
```
./db_bench -benchmarks=fillrandom -seed=1 -compression_type=none -threads=8 -db=<DB>
```
**Workload 2: readrandomwriterandom** (mixed read/write/delete)
```
# Setup: fillrandom,compact to populate DB (single-threaded)
./db_bench -benchmarks=readrandomwriterandom -seed=1 -compression_type=none \
-use_existing_db=1 -readwritepercent=50 -deletepercent=20 -threads=8 -db=<DB>
```
**Workload 3: fillrandom with range tombstones** (puts interleaved with range deletes)
```
./db_bench -benchmarks=fillrandom -seed=1 -compression_type=none \
-writes_per_range_tombstone=100 -range_tombstone_width=10 \
-max_num_range_tombstones=1000 -threads=8 -db=<DB>
```
```
| Benchmark | avg ops/s (main) | avg ops/s (feature) | % change |
|------------------------------------|-----------------|--------------------:|----------|
| fillrandom | 531,986 | 532,099 | +0.02% |
| readrandomwriterandom (50r/30w/20d)| 786,400 | 815,143 | +3.65% |
| fillrandom (range tombstones) | 541,656 | 531,008 | -1.97% |
```
Reviewed By: xingbowang
Differential Revision: D97974456
Pulled By: joshkang97
fbshipit-source-id: ea95f23953fe37771a599aed61ece1a504b12c2e
Summary:
Reduce `make check` time from ~9.4 minutes to ~3.7 minutes (2.6x) on a 192-core machine, speed up CI by 1.8x across all jobs, and fix ccache hit rates (macOS cmake: 0.51% → 99.80%).
## Changes
### 1. Auto-detect lld linker (`build_detect_platform`)
- Probe for `lld` at configure time; fall back to default `ld.bfd` if unavailable
- Linux-only guard (`TARGET_OS = Linux`)
- **Measured: 12x faster linking** (7.4s → 0.6s per test binary)
- Add `-L/usr/local/lib` for lld library resolution on CI
- Install lld in CI pre-steps action
- Opt out with `ROCKSDB_NO_FAST_LINKER=1`
### 2. Sharded test execution (`Makefile`)
**Before:** `make check` enumerated every individual gtest case via `--gtest_list_tests`, then created a separate shell script for each case with `--gtest_filter=TestName`. For example, `block_based_table_reader_test` has 9,788 parameterized test cases — each spawned its own process, loading a ~50MB binary, initializing gtest, registering all ~10K tests, running exactly ONE, then tearing down. The per-process overhead was ~1.5s, so 9,788 × 1.5s ≈ 15,000s wasted on overhead alone. Across all 302 test binaries this produced 39,467 individual processes.
**After:** Use gtest's built-in sharding (`GTEST_TOTAL_SHARDS`/`GTEST_SHARD_INDEX`) to group ~10 test cases per process. Each shard loads the binary once and runs multiple tests sequentially — eliminating the per-process overhead. The 9,788 cases in `block_based_table_reader_test` become ~980 shards. Total process count drops from 39,467 to ~4,036. Same tests, same coverage, just fewer process spawns.
The shard count adapts to machine size: `min(ceil(test_count / GTEST_SHARD_SIZE), NCORES * 8)`. This ensures large machines (192 cores) get many small shards for parallelism, while small CI runners (4 cores) get fewer larger shards to avoid excessive overhead. Both `GTEST_SHARD_SIZE` (default 10) and `NCORES` can be overridden.
CI sharding uses round-robin distribution across 3 shards to balance heavy tests (db_test, db_compaction_test, etc.) instead of contiguous alphabetical ranges.
Note: gtest continues running all tests after a failure (`GTEST_THROW_ON_FAILURE=0`), so grouping tests does not mask failures — all failures within a shard are reported.
**Measured: 3.6x CPU reduction** (76,121s → 21,033s)
### 3. Fix SyncPoint leaks for test isolation (5 files)
Sharded execution exposed 43 test files that set SyncPoint callbacks but never clean them up. Stale callbacks with captured local variables cause segfaults or data corruption when subsequent tests in the same process trigger them.
**Systematic fix:** Global gtest `TestEventListener` in `testharness.cc` that calls `SyncPoint::DisableProcessing()` + `ClearAllCallBacks()` + `ClearTrace()` + `LoadDependency({})` after every test case. This cleans up all SyncPoint state: callbacks, dependency maps, cleared points, and the point filter. Registered via static initialization — no changes needed to individual test `main()` functions.
**SyncPoint infrastructure fixes:**
- `DisableProcessing()` now calls `cv_.notify_all()` to wake threads blocked in `Process()`. Previously, threads waiting for predecessor sync points that would never fire (because the test ended) would hang forever.
- `Process()` now rechecks `enabled_` after waking from `cv_.wait()`, so threads exit promptly when processing is disabled instead of looping forever.
**Specific fixes (defense-in-depth):**
- `SstFileReaderTest::VerifyNumEntriesCorruption` — leaked `PropertyBlockBuilder::AddTableProperty:Start` callback that corrupted SST entry counts
- `WritePreparedTransactionTest::CommitAndSnapshotDuringCompaction` — leaked `CompactionIterator:AfterInit` callback causing segfault via dangling pointers
- `TransactionTestBase` destructor — cleanup for 26 SyncPoint uses across transaction tests
- `RetriableLogTest` destructor — cleanup for log reader SyncPoint callbacks
### 4. Fix ccache for reliable cross-run caching (`setup-ccache`, `CMakeLists.txt`)
- **`CCACHE_COMPILERCHECK=content`**: Default `mtime` compared compiler binary modification time, which differs on every fresh CI runner → 0% hit rate. `content` hashes compiler output instead — stable across runner instances
- **`CCACHE_SLOPPINESS`**: CMake generates varying `-MF` paths and timestamps. Without sloppiness settings, ccache treated these as different compilations
- **`CMAKE_C/CXX_COMPILER_LAUNCHER`** instead of `RULE_LAUNCH_COMPILE`: Avoids double-wrapping ccache when also injected via PATH. Removes `RULE_LAUNCH_LINK` since ccache cannot cache link operations
- **macOS cmake hit rate: 0.51% → 99.80%**
### 5. Align GTEST_THROW_ON_FAILURE (`Makefile`)
- Set `GTEST_THROW_ON_FAILURE=0` in Makefile default to match CI's `pre-steps/action.yml`
- `=1` caused `std::terminate` in multi-threaded stress tests (e.g., `point_lock_manager_stress_test`, `rate_limiter_test`) when assertions fail — the gtest exception propagates across thread boundaries, triggering undefined behavior and heap-use-after-free under ASAN
### 6. Portability fixes
- `[[maybe_unused]]` instead of `__attribute__((unused))` for MSVC compatibility
- Remove blanket `-fPIC` from `COMMON_FLAGS` — only apply via `PLATFORM_SHARED_CFLAGS` for shared builds, preserving optimal codegen for release/static builds
- `make -s list_all_tests` to suppress make noise in test enumeration
## Results
### Local (192-core devvm, clean build)
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| Build time (clean) | 2m43s | ~2m | lld linker |
| Test wall clock | 400s | 200s | 2.0x |
| Test CPU total | 76,121s | 21,033s | 3.6x |
| Test failures | 0 | 0 | — |
| **Total make check** | **~9.4min** | **3m41s** | **2.6x** |
### CI (GitHub Actions)
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| Aggregate CI time | 15,220s | 8,425s | 1.8x |
| build-linux-arm | 763s | 221s | 3.5x |
| build-linux-release | 616s | 244s | 2.5x |
| build-windows-vs2022 | 2,868s | 1,205s | 2.4x |
| build-linux-java-static | 858s | 335s | 2.6x |
### ccache hit rates (all jobs)
| Job | Before | After |
|-----|--------|-------|
| macOS cmake (0-3) | 0.51% | **99.80%** |
| build-linux | 99.68% | **99.84%** |
| build-linux-clang18-asan-ubsan | 98.58% | **99.84%** |
| build-linux-clang18-mini-tsan | 98.74% | **99.84%** |
| build-windows-vs2022 | 99.22% | **99.83%** |
| All 27 ccache jobs | — | **>94%** |
## Remaining bottleneck
The critical path is now dominated by genuinely slow integration tests:
- `external_sst_file_test` shards: 97–113s each
- `db_test` shards: 109–124s each
- `db_bloom_filter_test`: 103s (non-parallel)
Further improvement would require test optimization or a `make check-fast` target.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14478
Test Plan:
- `make check -j192 SKIP_FORMAT_BUCK_CHECKS=1` — all 4,045 shards pass (ASAN+UBSAN), zero failures
- Verified SyncPoint fixes: each crash/corruption reproduces before fix, passes after
- Verified SyncPoint::DisableProcessing wakes blocked threads (previously caused indefinite hangs)
- Verified gtest does NOT stop after failure with `GTEST_THROW_ON_FAILURE=0` — all tests in a shard run and all failures are reported
- CI: all 34 checks pass across two consecutive runs
- ccache: validated hit rates >94% on second run after cache seeding
Reviewed By: hx235
Differential Revision: D97623305
Pulled By: xingbowang
fbshipit-source-id: b299e6d43c8713a9ef2b5659e8bbd74958afe155
Summary:
The ExternalTableReader `Get` API has been modified to use PinnableSlice instead of std::string, this will allow implementations to utilize zero-copy Gets (e.g. reading from mmap or a cache). This is not a compatible change, but the API is marked as experimental, so should be allowed.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14497
Test Plan: - New unit tests
Reviewed By: xingbowang
Differential Revision: D97782011
Pulled By: joshkang97
fbshipit-source-id: 8a9e8c5bc5ff5e8dee6c0f2ee745521f09042cef
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14502
The asan_crash_test_with_ts randomly selected use_trie_index=1 alongside
user_timestamp_size=8. TrieIndexFactory requires plain BytewiseComparator,
but user-defined timestamps wrap it as BytewiseComparator.u64ts, causing
Status::NotSupported errors during flush/compaction.
Fix:
1. Add use_trie_index=0 to ts_params in db_crashtest.py to prevent the
incompatible combination (matches existing pattern for other features).
2. Add early validation in db_stress_tool.cc to reject this combination
with a clear error message (defense in depth).
Reviewed By: xingbowang
Differential Revision: D97592648
fbshipit-source-id: 2259f82d95ea8410c4278b52f60983a4b5e84ec2
Summary:
Add GitHub Actions workflows for AI-powered code review on PRs using Claude (Anthropic). Follows the clang-tidy security pattern with two separate workflows for privilege separation.
## Trigger Modes
**1. Auto** — Runs after `pr-jobs` workflow completes successfully via `workflow_run`. Safe for fork PRs (runs from default branch, never executes PR code).
**2. Manual** — Maintainers comment `/claude-review [focus area]` or `/claude-query <question>` on any PR. Restricted to 15 authorized team members.
**3. workflow_dispatch** — For manual testing.
## Security Model (Two-Workflow Separation)
Same pattern as clang-tidy:
**`claude-review.yml`** (analysis):
- Runs Claude with `ANTHROPIC_API_KEY`
- Has ONLY `contents: read` — no PR write, no issue write
- Saves review markdown + metadata as artifact
**`claude-review-comment.yml`** (posting):
- Triggers on `workflow_run` completion
- Downloads artifact and posts/updates PR comment
- Has `pull-requests: write` but never runs AI
This separation prevents a crafted PR from tricking Claude into exfiltrating write tokens.
## Review Methodology
Review prompt in `claude_md/code_review.md` (shared with local Claude Code reviews). Five perspectives:
- Call-chain analysis (3-5 levels up/down)
- Correctness & edge cases
- Cross-component & adversarial (10 execution contexts)
- Performance
- API compatibility & test coverage
## Shared Scripts
- `.github/scripts/post-pr-comment.js` — Create-or-update PR comment with marker-based dedup. Now used by both clang-tidy and Claude review.
- `.github/scripts/parse-claude-review.js` — Parses `claude-code-base-action` execution log into markdown.
## Files Changed
| File | Description |
|------|-------------|
| `.github/workflows/claude-review.yml` | Analysis workflow (476 lines) |
| `.github/workflows/claude-review-comment.yml` | Comment posting workflow (146 lines) |
| `.github/scripts/post-pr-comment.js` | Shared PR comment utility (57 lines) |
| `.github/scripts/parse-claude-review.js` | Execution log parser (78 lines) |
| `.github/workflows/clang-tidy-comment.yml` | Updated to use shared script |
| `claude_md/code_review.md` | Review methodology (104 lines) |
## Setup Required
Add `ANTHROPIC_API_KEY` secret to the repo settings.
## Testing
Tested end-to-end on `xingbowang/rocksdb` fork — both auto and manual triggers, artifact upload/download, comment posting, and duplicate detection all verified working.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14480
Reviewed By: omkarhgawde
Differential Revision: D97832666
Pulled By: xingbowang
fbshipit-source-id: f80c7d8683ac980614dc4ca66c1e545deb3be504
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14475
GetLiveFiles was previously blocked on secondary instances with
Status::NotSupported, even though the operation is safe to perform.
The only reason GetLiveFiles was originally blocked is that it defaults
to flushing the memtable (flush_memtable=true), which is a write
operation. However, the actual implementation in DBImpl::GetLiveFiles
(db_filesnapshot.cc) does two things:
1. Optionally flush the memtable — which we skip by passing
flush_memtable=false. The secondary already overrides
FlushForGetLiveFiles() as a no-op, so even if true were passed
it would not actually flush.
2. Read live file state from versions_ under the mutex — this is
purely read-only. It iterates the ColumnFamilySet to collect live
table and blob file numbers, builds relative file paths for SST
files, blob files, CURRENT, MANIFEST, and OPTIONS, and reads the
manifest file size.
The secondary maintains its own VersionSet which it keeps up to date
via MANIFEST replay in TryCatchUpWithPrimary(). So all of this state
is valid and accurate — it reflects exactly which files the secondary
considers live at its current replay point.
This is the same approach used by DBImplReadOnly and CompactedDBImpl,
which both delegate to DBImpl::GetLiveFiles with flush_memtable=false.
Reviewed By: xingbowang
Differential Revision: D97563143
fbshipit-source-id: 8d5b52e26a478ef190eba598819de6527817bcfc
Summary:
Fix leaked table cache entries that cause `TEST_VerifyNoObsoleteFilesCached` assertion failure during `DB::Close()` in ASAN crash test builds (T258745630):
```
File 126519 is not live nor quarantined
Assertion `cached_file_is_live_or_quar' failed.
```
When a compaction fails *after* `VerifyOutputFiles` succeeds (e.g., at `VerifyCompactionRecordCounts`), the overall `compact_->status` is set to error but each subcompaction's individual `status` remains OK. `SubcompactionState::Cleanup` only checked the individual subcompaction status, so it skipped calling `ReleaseObsolete` on the output files' table cache entries — leaking them.
Normally, `Close()`'s backstop (`FindObsoleteFiles(force=true)` + `PurgeObsoleteFiles`) would catch this by finding the orphan file on disk, evicting the cache entry, and deleting the file. However, `FindObsoleteFiles` calls `FaultInjectionTestFS::GetChildren` which can fail under metadata read fault injection (`--open_metadata_read_fault_one_in=8`). The error is silently ignored (`s.PermitUncheckedError()` in db_impl_files.cc:199), so the orphan file is never found and the leaked cache entry is never evicted.
The `Cleanup` bug is latent and predates recent changes. PR https://github.com/facebook/rocksdb/issues/14433 added `verify_output_flags` randomization to the crash test, and PR https://github.com/facebook/rocksdb/issues/14456 fixed false-positive corruptions that https://github.com/facebook/rocksdb/issues/14433 caused. Before https://github.com/facebook/rocksdb/issues/14456, `VerifyOutputFiles` would produce false corruption errors that *accidentally prevented the leak* by setting the subcompaction status to non-OK.
### How it triggers
**Step 1 — VerifyOutputFiles adds cache entries for compaction output files:**
```
CompactionJob::Run()
RunSubcompactions() // all subcompactions succeed
// output file 12 written to disk
// each sub_compact.status = OK
SyncOutputDirectories() // status = OK
VerifyOutputFiles()
for each output_file:
table_cache()->NewIterator(output_file.meta)
FindTable()
cache->Insert(file_number=12) // <<< ENTRY ADDED TO TABLE CACHE
return iterator holding handle
delete iter // releases handle, entry stays in LRU
// status = OK
```
**Step 2 — A post-verification step fails, overall status set but NOT subcompaction status:**
```
CompactionJob::Run()
VerifyCompactionRecordCounts() // returns Status::Corruption(...)
FinalizeCompactionRun(status=Corruption)
compact_->status = Corruption // <<< OVERALL status = error
// each sub_compact.status is still OK!
```
**Step 3 — Install skips InstallCompactionResults (file 12 never enters a Version):**
```
CompactionJob::Install()
status = compact_->status // Corruption
if (status.ok()) // FALSE
InstallCompactionResults() // <<< SKIPPED — file 12 not in any version
```
**Step 4 — CleanupCompaction skips ReleaseObsolete (THE BUG):**
```
CompactionJob::CleanupCompaction()
for each sub_compact:
sub_compact.Cleanup(table_cache)
if (!status.ok()) // checks sub_compact.status = OK
// <<< FALSE — individual status is OK!
ReleaseObsolete(...) // <<< NEVER CALLED — cache entry leaked!
```
**Step 5 — Metadata read fault prevents backstop from finding the orphan:**
`FindObsoleteFiles(force=true)` calls `GetChildren()` to scan the DB directory.
`FaultInjectionTestFS::GetChildren` injects a metadata read error
(`--open_metadata_read_fault_one_in=8`). The error is silently ignored
(`s.PermitUncheckedError()`), so the directory listing is empty and the orphan
file is never found. This happens both in the post-compaction cleanup
(`BackgroundCallCompaction`) and in `Close()`'s backstop:
```
FindObsoleteFiles(force=true)
fs->GetChildren(path, ...) // returns IOError (fault injected)
s.PermitUncheckedError(); // error silently ignored
// files vector is empty — orphan file 12 not found
PurgeObsoleteFiles() // nothing to do — no Evict called
```
**Step 6 — Assertion fires during Close():**
```
CloseHelper()
FindObsoleteFiles(force=true) // GetChildren fails again → orphan missed
PurgeObsoleteFiles() // nothing to evict
TEST_VerifyNoObsoleteFilesCached()
for each cache entry:
file_number = 12
live_and_quar_files.find(12) == end() // NOT in any version!
>>> assert(cached_file_is_live_or_quar) FAILS <<<
```
**Crash test call stack:**
```
frame https://github.com/facebook/rocksdb/issues/9: __assert_fail_base("cached_file_is_live_or_quar", "db_impl_debug.cc", 389)
frame https://github.com/facebook/rocksdb/issues/11: DBImpl::TEST_VerifyNoObsoleteFilesCached()::lambda // finds leaked entry
frame https://github.com/facebook/rocksdb/issues/18: LRUCacheShard::ApplyToSomeEntries(...) // iterating cache shard
frame https://github.com/facebook/rocksdb/issues/19: ShardedCache::ApplyToAllEntries(...) // iterating all shards
frame https://github.com/facebook/rocksdb/issues/20: DBImpl::TEST_VerifyNoObsoleteFilesCached() // the verification
frame https://github.com/facebook/rocksdb/issues/21: DBImpl::CloseHelper() // during Close
frame https://github.com/facebook/rocksdb/issues/22: DBImpl::CloseImpl()
frame https://github.com/facebook/rocksdb/issues/23: DBImpl::Close()
frame https://github.com/facebook/rocksdb/issues/24: StressTest::Reopen() // crash test reopen
frame https://github.com/facebook/rocksdb/issues/25: StressTest::OperateDb() // worker thread
```
### Fix
Pass the overall `compact_->status` to `SubcompactionState::Cleanup` and call
`ReleaseObsolete` when *either* the subcompaction status or the overall status
is non-OK:
```cpp
// Before:
if (!status.ok()) { ReleaseObsolete(...); }
// After:
if (!status.ok() || !overall_status.ok()) { ReleaseObsolete(...); }
```
## Key changes
- **`SubcompactionState::Cleanup`**: Now takes an `overall_status` parameter and calls `ReleaseObsolete` when *either* the subcompaction status or the overall compaction status is non-OK.
- **`CompactionJob::CleanupCompaction`**: Passes `compact_->status` (the overall status) to each subcompaction's `Cleanup`.
- **Sync point**: Added `CompactionJob::Run():AfterVerifyOutputFiles` for error injection in tests.
- **Unit test**: `DBCompactionTest.LeakedTableCacheEntryOnCompactionFailure` uses `FaultInjectionTestFS` to reproduce the crash test scenario — injects error after `VerifyOutputFiles` and deactivates the filesystem so `GetChildren` fails in `FindObsoleteFiles`, preventing the backstop from evicting the leaked cache entry.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14469
Test Plan:
- `DBCompactionTest.LeakedTableCacheEntryOnCompactionFailure`:
- **Without fix (ASAN)**: assertion fires during `Close()` — `File 12 is not live nor quarantined`
- **With fix (ASAN)**: passes — `ReleaseObsolete` properly cleans up the cache entry
- **With fix (non-ASAN)**: passes
```
$ COMPILE_WITH_ASAN=1 make -j db_compaction_test
$ ./db_compaction_test --gtest_filter="DBCompactionTest.LeakedTableCacheEntryOnCompactionFailure"
[ PASSED ] 1 test.
```
Reviewed By: xingbowang
Differential Revision: D97190944
Pulled By: joshkang97
fbshipit-source-id: fdfd481cc1e192803cfb7d64052ccb9162c21b94
Summary:
**Context/Summary:**
Remove assertions on flush_running/flush_scheduled from the BackgroundJobPressure listener test. These checks are inherently racy because Flush() can return before the background thread fires the pressure callback.
The race: when BackgroundCallFlush re-acquires the DB mutex after cleanup (line 3661), COERCE_CONTEXT_SWITCH=1 or others may call bg_cv_->SignalAll() and sleep before actually acquiring the lock. This spurious signal wakes WaitForFlushMemTables(), which sees the flush is already installed and returns — so Flush() "completes" seen as foreground in test. The test then starts the next Put()+Flush(), scheduling new flush work. When the previous background thread finally wakes and captures the pressure snapshot, it sees the newly scheduled flush (flush_scheduled > 0). If the race happens on the last flush, its callback may not have fired at all by the time the test reads GetSnapshots(), so snapshots.back() can also show stale data.
The remaining assertions (compaction counts, speedup, write stall proximity) are not affected: compaction is blocked by sleeping_task in Phase 1-2, and Phase 3 uses TEST_WaitForCompact() whose wait condition (bg_*_scheduled_ counts) is only satisfied after the mutex is re-acquired and callbacks have fired.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14476
Test Plan:
COERCE_CONTEXT_SWITCH=1 make -j56 listener_test
Before: ~22/50 failures at line 1715 (flush_running != 0)
After: 50/50 pass
Reviewed By: xingbowang
Differential Revision: D97572545
Pulled By: hx235
fbshipit-source-id: e8a57a7a00e41d17bf47ef8e04cea977225a5909
Summary:
**Summary:**
Add a new EventListener callback `OnBackgroundJobPressureChanged` that fires after every flush or compaction background job completes. The callback delivers a `BackgroundJobPressure` snapshot containing:
- Compaction scheduling counters (scheduled/running, combined and per-priority LOW/BOTTOM breakdown)
- Flush scheduling counters (scheduled/running)
- Write stall proximity percentage (0=healthy, 100=at stall threshold, can exceed 100 when stalling)
- Whether compaction speedup is active
`CaptureBackgroundJobPressure()` reads scheduling counters and computes write stall proximity from L0 sorted run count and pending compaction bytes (same inputs as `RecalculateWriteStallConditions()`). TODO: add memory-related write stall triggers later.
Introduces `num_running_bottom_compactions_` counter to track BOTTOM- priority compactions separately from LOW, enabling per-pool breakdown in the pressure snapshot.
The callback fires on the background thread after counter decrements and `MaybeScheduleFlushOrCompaction()`, so the snapshot reflects post- completion state. Uses the same mutex unlock/lock pattern as `NotifyOnFlushCompleted`. A `bg_pressure_callback_in_progress_` counter ensures destructor safety since the callback fires after `bg_flush_scheduled_`/`bg_compaction_scheduled_` are decremented.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14474
Test Plan:
- listener_test BackgroundJobPressure: 3-phase test verifying no pressure, pressure build-up (speedup, scheduling, proximity), and pressure relief after compaction completes
- db_compaction_test CompactRangeBottomPri: verifies num_running_bottom_compactions_ via sync point
- db_stress_tool exercises the callback with RandomSleep()
Reviewed By: xingbowang
Differential Revision: D97423623
Pulled By: hx235
fbshipit-source-id: 07003c8de226ec29d32b8a88e2d86e5de85cd2cc
Summary:
Skip WAL recovery when opening a secondary DB instance in OpenAndCompact() for remote compaction. WAL replay is unnecessary in this flow since only LSM state from MANIFEST is needed.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14462
Test Plan:
- make -j db_secondary_test && ./db_secondary_test — 35/35 passed
- make -j compaction_service_test && ./compaction_service_test — 43/43 passed (includes new SkipWALRecoveryInOpenAndCompact test)
- make -j options_settable_test && ./options_settable_test --gtest_filter="*DBOptionsAllFieldsSettable*" — 1/1 passed
- Removed temporary hack in stress test that disables WAL
Reviewed By: hx235
Differential Revision: D96788211
Pulled By: jaykorean
fbshipit-source-id: f91a2f861f2450ebc83423ed4c6f5b70da7d9e8b
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14473
Add separate PerfContext byte counters for data, index, filter, compression-dictionary, and metadata block reads while preserving block_read_byte as the aggregate. Wire the new counters through the block fetch and multi-read data block paths, expose them via the C perfcontext API, and extend table tests to verify byte attribution and that the classified counters sum back to block_read_byte.
Reviewed By: xingbowang
Differential Revision: D97333746
fbshipit-source-id: 3411844d7fa9c76c9ff28af477b3a72a5d6e5d9b
Summary:
Add a new mutable DB option `verify_manifest_content_on_close` (default: false).
When enabled, on DB close the MANIFEST file is read back and all records are
validated (CRC checksums via log::Reader and logical content via
VersionEdit::DecodeFrom). If corruption is detected, a fresh MANIFEST is written
from in-memory state using the existing LogAndApply recovery path.
This complements the existing size validation in VersionSet::Close() with content
validation, reusing the same manifest reading pattern as VersionSet::Recover().
Implementation plan:
## Part 1: New DB Option — verify_manifest_content_on_close
- A new mutable bool DB option (default: false) that can be dynamically toggled
via SetDBOptions() at runtime, following the pattern of other mutable manifest
options like max_manifest_file_size.
- Propagation: SetDBOptions() -> DBImpl::mutable_db_options_ ->
versions_->UpdatedMutableDbOptions() -> VersionSet::verify_manifest_content_on_close_
## Part 2: Core Implementation — Content Validation in VersionSet::Close()
- Inserted after existing size check, before closed_ = true
- Opens manifest as SequentialFileReader, creates log::Reader with checksum=true
- Loops ReadRecord with WALRecoveryMode::kAbsoluteConsistency, decodes each
record as VersionEdit
- On corruption: fires OnIOError listeners, logs error, calls LogAndApply with
empty edit to trigger manifest rewrite from in-memory state
- If manifest can't be opened for reading: logs warning, doesn't fail close
## Part 3: Unit Tests (in version_set_test.cc)
- ManifestContentValidationOnClose_Clean: enable option, normal close, verify
no manifest rotation
- ManifestContentValidationOnClose_CorruptRecord: enable option, corrupt manifest
via SyncPoint, verify rotation occurs and DB reopens cleanly
- ManifestContentValidationOnClose_Disabled: default off, verify content
validation does not run
- ManifestContentValidationOnClose_SizeCheckFails: truncate manifest so size
check fails first, verify recovery via size-check path
## What Happens If a Corruption is Detected
If corruption was detected, four things happen:
1. **Notify listeners** — Fires `OnIOError` on all registered event listeners
(from db_options_->listeners) so monitoring/alerting systems can observe
the corruption event. Uses `FileOperationType::kVerify` to categorize it.
2. **Permit unchecked errors** — `PermitUncheckedError()` silences RocksDB's
debug-mode assertion that every `IOStatus` must be inspected. These statuses
are informational-only here; the real recovery is via `LogAndApply`.
3. **Log the error** — Writes a `ROCKS_LOG_ERROR` message with the filename
for operational visibility (grep-able in production logs).
4. **Rewrite the manifest via `LogAndApply`** — This is the actual recovery.
`LogAndApply` is called with an empty `VersionEdit` (no changes). Internally,
`LogAndApply` detects that the current `descriptor_log_` is null (it was
reset at line 5551, or by the previous `LogAndApply` in the size-check
path) and creates a brand-new MANIFEST file. It serializes the entire
current in-memory LSM state — all column families, all levels, all file
metadata, sequence numbers, etc. — into this new file. It then atomically
updates the `CURRENT` file pointer to reference the new MANIFEST.
This works because the in-memory state was built from the original manifest
during `DB::Open()` and has been kept fully up to date through all
subsequent operations (flushes, compactions, etc.) during the DB's lifetime.
The on-disk manifest is essentially a journal of changes; `LogAndApply`
with an empty edit produces a fresh, compacted snapshot of that state.
## Flow Diagram of Manifest Content Validation
VersionSet::Close()
│
├─ Close descriptor_log_ and check size
│ └─ Size mismatch? → LogAndApply (rewrite manifest)
│
├─ Content validation (if s.ok() && option enabled)
│ ├─ Open manifest for sequential reading
│ │ └─ Can't open? → WARN log, continue
│ │
│ ├─ For each record:
│ │ ├─ ReadRecord (CRC32 check, kAbsoluteConsistency)
│ │ └─ DecodeFrom (VersionEdit logical check)
│ │
│ └─ Corruption detected?
│ ├─ Notify OnIOError listeners
│ ├─ LOG_ERROR
│ └─ LogAndApply (rewrite manifest from in-memory state)
│
└─ closed_ = true; return s;
## How This Relates to the Existing Size Check
The existing size check (lines 5556-5582) and the new content validation are
complementary:
| Check | What it catches | How it checks |
|----------------|-----------------------------------------|----------------------------|
| Size check | Truncation, partial writes, extra bytes | Compare expected vs actual file size |
| Content check | Bit-rot, silent corruption, bad records | CRC32 + VersionEdit decode |
The size check catches gross corruption (file too short or too long). The
content check catches subtle corruption where the file is the right size but
individual bytes have been flipped (e.g., storage media bit-rot, buggy
filesystem, incomplete block write).
Both recovery paths use the same mechanism: `LogAndApply` with an empty
`VersionEdit` to rewrite the manifest from in-memory state.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14451
Reviewed By: xingbowang
Differential Revision: D96004906
Pulled By: dannyhchen
fbshipit-source-id: 0b0ecdada3a74e97d2cadbba2091b8b577f1d684
Summary:
InjectedErrorLog is a lock-free circular ring buffer designed to be safe to call from signal handlers (which cannot use locks). It has an intentional benign data race between Record() (called by worker threads) and PrintAll() (called by the main thread from a signal/termination handler). The code documents this trade-off in comments, but was missing TSAN suppression annotations.
Simply adding TSAN_SUPPRESSION (__attribute__((no_sanitize("thread")))) is insufficient because TSAN still intercepts libc functions like vsnprintf/snprintf -- accesses through these interceptors are still tracked even when the calling function is annotated.
The fix:
1. Add TSAN_SUPPRESSION to both Record() and PrintAll() to suppress direct field reads/writes in the function body.
2. Restructure both functions to use local stack buffers for vsnprintf/snprintf operations instead of operating directly on shared entry data. This avoids passing shared memory through TSAN-intercepted libc functions.
Also adds fault_injection_fs_test with a ConcurrentRecordAndPrintAll test that exercises the concurrent Record() + PrintAll() pattern and verifies no TSAN race is reported.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14467
Test Plan:
- fault_injection_fs_test passes under TSAN (buck2 test fbcode//mode/dbg-tsan)
- Reverted fix, re-ran: Fatal (6 TSAN warnings) -- round-trip confirmed
- fault_injection_fs_test passes under debug mode (no regression)
Reviewed By: mszeszko-meta
Differential Revision: D96948483
Pulled By: xingbowang
fbshipit-source-id: efdd5eafa12a5a82f973e40aa327901cc5f95033
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14464
SetBackupInfoFromBackupMeta passes file_ptr->filename (which may contain
directory components like "private/1/000008.log") directly to
ParseFileName. ParseFileName expects a bare filename, so it fails and
file_type stays at the default kTempFile for all files in file_details.
Fix by extracting the basename before calling ParseFileName.
Reviewed By: mszeszko-meta
Differential Revision: D96793040
fbshipit-source-id: 7bb6b633eb07bb7ebda06edbc039a96b9b77b410
Summary:
The callback loop in InlineSkipList::MultiGet updated finger.prev_[0] as it walked forward through entries (e.g., merge operands). When the MultiGet batch contained duplicate user keys, the next lookup for the same key would find finger.prev_[0] pointing to an entry that sorts AFTER the lookup key in internal key order (because the lookup key has a high sequence number which sorts first), violating the FindSpliceForLevel precondition: before == head_ || KeyIsAfterNode(key, before).
Fix: stop updating finger.prev_[0] in the callback loop. Only finger.next_[0] needs advancing to track the walk-forward position. The prev_[0] from FindGreaterOrEqualWithFinger is always a valid lower bound for any subsequent key, whether it uses kMaxSequenceNumber or a snapshot sequence number.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14465
Reviewed By: xingbowang
Differential Revision: D96882549
Pulled By: anand1976
fbshipit-source-id: a733fa9d4f23f8b55a027c257a114c4cf35abe2b
Summary:
write_prepared_transaction_test_seqno keeps showing up in git changes
test binaries need to end with _test so they are ignored by .gitignore
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14453
Reviewed By: xingbowang
Differential Revision: D96202671
Pulled By: joshkang97
fbshipit-source-id: 91642226bedde37ca72c6af03f300990dca8ff3a
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14458
In `CheckpointImpl::ExportColumnFamily()`, the assignment
`*metadata = result_metadata` is inside the `for` loop over
`db_metadata.levels`. If the column family has no levels, the loop body
never executes, so the `new ExportImportFilesMetaData()` is leaked and
the caller receives a nullptr despite a success status.
Fix: Move `*metadata = result_metadata` outside the loop.
Reviewed By: anand1976
Differential Revision: D95303457
fbshipit-source-id: 6a9be47bcca257803969eb3daac7b91e95143ebf
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14443
In `PosixDirectory::FsyncWithDirOptions()`, when handling btrfs file rename
syncing, if `open()` fails and `fd` is -1, the code unconditionally calls
`close(fd)`. Calling `close(-1)` is undefined behavior per POSIX (returns
EBADF on Linux), and overwrites the original meaningful open error with a
misleading "While closing file after fsync" error message.
Fix: Guard the `close()` call with `fd >= 0`.
Reviewed By: xingbowang
Differential Revision: D95303407
fbshipit-source-id: 9b64b45a09e6ba41d87d164a1c094b4d22f7c186
Summary:
Fix a bug where `VerifyOutputFiles()` produces false positive "Key-value checksum of compaction output doesn't match what was computed when written" errors when `verify_output_flags` includes `kVerifyIteration` but `paranoid_file_checks` is false.
The root cause is a hash enable flag mismatch between writing and verification:
- During compaction writing (`OpenCompactionOutputFile`), the `OutputValidator` hash computation was gated solely by `paranoid_file_checks_`. When false, `enable_hash=false` and the hash stays at 0.
- During verification (`VerifyOutputFiles`), a new `OutputValidator` is always created with `enable_hash=true`, computing a non-zero hash.
- `CompareValidator()` then compares 0 vs non-zero, producing a false positive corruption.
This was exposed by the crash test randomization of `verify_output_flags` added in https://github.com/facebook/rocksdb/issues/14433. Before that change, `verify_output_flags` was always 0, so `kVerifyIteration` was only exercised via `paranoid_file_checks=true` (which correctly enabled the hash during writing).
The fix ensures hash computation is enabled during writing whenever either `paranoid_file_checks_` is true OR `verify_output_flags` includes `kVerifyIteration`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14456
Test Plan:
- Added `DBCompactionTest.VerifyIterationWithoutParanoidFileChecks`
- Added `DBCompactionTest.VerifyAllOutputFlagsWithoutParanoidFileChecks`
- Round-trip verified: tests FAIL without fix, PASS with fix
Reviewed By: jaykorean
Differential Revision: D96371769
Pulled By: xingbowang
fbshipit-source-id: 2f7406327496c7b541e4fa2668894df89eb813e8
Summary:
One of the follow ups from https://github.com/facebook/rocksdb/pull/14103. Users will have the option to verify file checksums for all compaction output files before they are installed. This feature helps prevent corrupted SST files from being added to the LSM tree.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14433
Test Plan: Unit test added
Reviewed By: archang19
Differential Revision: D95648000
Pulled By: jaykorean
fbshipit-source-id: 512f3c1a7449b96a660865531f3537624c89a9cc
Summary:
Add a circular ring buffer (InjectedErrorLog) to FaultInjectionTestFS that records the last 1000 injected errors. Each entry captures the timestamp, thread ID, FS API name with all arguments (file path, offset, buffer size, first 8 bytes of data in hex), and the injected error status. For example:
Append("/path/035354.sst", size=4096, head=[1a 2b 3c ...]) -> IOError (Retryable): injected write error
RenameFile("/path/tmp.sst", "/path/035355.sst") -> IOError: injected metadata write error
Read(offset=16384, size=4096) -> IOError (Retryable): injected read error
The ring buffer is printed to a file automatically:
- On any fatal signal (SIGABRT, SIGSEGV, SIGTERM, SIGINT, SIGHUP, SIGFPE, SIGBUS, SIGILL, SIGQUIT, SIGXCPU, SIGXFSZ, SIGSYS) via a registered crash callback
- At the end of db_stress main(), for diagnostic visibility even when the test completes normally
This addresses a key debugging gap: when write fault injection causes secondary failures (e.g., the builder error propagation issue in T257612259), the injected errors were previously completely silent with no logging trail. The ring buffer provides the missing diagnostic context to correlate fault injection with downstream failures.
Changes:
- port/stack_trace.h/.cc: Add RegisterCrashCallback() API; extend InstallStackTraceHandler() to catch all catchable termination signals
- utilities/fault_injection_fs.h: Add InjectedErrorLog class with printf-style Record(), HexHead() for data bytes, and signal-safe PrintAll()
- utilities/fault_injection_fs.cc: Record full API arguments and error status at all 31 fault injection call sites
- db_stress_tool/db_stress_tool.cc: Register crash callback and print ring buffer at end of main()
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14431
Reviewed By: hx235
Differential Revision: D95435430
Pulled By: xingbowang
fbshipit-source-id: 6c18e1b072044575d6c8c3f198070127b0f80608
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14447
`rocksdb_create_column_family()` and
`rocksdb_transactiondb_create_column_family()` allocate a
`rocksdb_column_family_handle_t` but always return it even when
`CreateColumnFamily()` fails. This leaks the handle and returns an object
with an indeterminate `rep` pointer. Other similar functions like
`rocksdb_create_column_family_with_import()` correctly delete the handle
and return nullptr on error.
Fix: Initialize `handle->rep = nullptr`, check `SaveError()` return value,
and on error delete the handle and return nullptr.
Reviewed By: anand1976
Differential Revision: D95303444
fbshipit-source-id: 5fde7a3ed588794d78429d5cb3d9f621f0fb6388
Summary:
When RocksDB operates with tiered or remote storage (e.g., Warm Storage, HDFS, S3), reading recently compacted data incurs high-latency remote reads because compaction output files are not present in the block cache. The existing `prepopulate_block_cache = kFlushOnly` avoids this for flush output but leaves compaction output cold until first access.
Add a new `PrepopulateBlockCache::kFlushAndCompaction` enum value that warms all block types (data, index, filter, compression dict) into the block cache during both flush and compaction. Flush-warmed blocks use `LOW` priority (unchanged from kFlushOnly behavior), while compaction-warmed blocks use `BOTTOM` priority — compaction data is less temporally local than freshly flushed data, so it should be the first to be evicted when the cache is full. This gives the remote-read avoidance benefit without risking cache thrashing.
The enum uses `kFlushAndCompaction` rather than separate `kCompactionOnly` + `kFlushAndCompaction` values because there is no practical use case for warming compaction output without also warming flush output. Flush output is by definition the hottest data (just written by the user), so if a workload benefits from warming the colder compaction output, it would always benefit from warming flush output too.
The implementation reuses the existing `InsertBlockInCacheHelper` / `WarmInCache` infrastructure in `BlockBasedTableBuilder`. The only internal change is adding a `warm_cache_priority` field to `Rep` alongside the existing `warm_cache` bool, and plumbing it through to the `WarmInCache` call instead of the previously hardcoded `Cache::Priority::LOW`.
### Key changes
- New `PrepopulateBlockCache::kFlushAndCompaction` enum value in table.h
- `Rep::warm_cache_priority` field in BlockBasedTableBuilder for per-reason priority control
- Serialization support ("kFlushAndCompaction" in string map)
- db_bench support (--prepopulate_block_cache=2)
- Crash test coverage (random choice includes new value)
**NOTE:** Unlike flush output (which is inherently hot — just written by the user), it is hard to distinguish hot from cold blocks in compaction output. Warming all compaction output therefore risks polluting the block cache and evicting genuinely hot entries. The kFlushAndCompaction mode is recommended only for use cases where most or all of the database is expected to reside in cache (e.g., the working set fits in cache). For workloads where only a fraction of the data is hot, kFlushOnly remains the safer choice.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14445
Test Plan:
- New `WarmCacheWithDataBlocksDuringCompaction` test: verifies data blocks from compaction output are present in the block cache and served without misses
- Extended `DynamicOptions` test: verifies dynamic switching through kDisable -> kFlushAndCompaction -> kFlushOnly -> kDisable via SetOptions
- Existing `WarmCacheWithDataBlocksDuringFlush` and parameterized `WarmCacheWithBlocksDuringFlush` tests continue to pass (kFlushOnly behavior unchanged)
- db_block_cache_test: 81/81 passed
- options_test: 74/74 passed
- table_test: 6910/6910 passed
Reviewed By: xingbowang
Differential Revision: D95997952
Pulled By: mszeszko-meta
fbshipit-source-id: 4ad568264992532053947df298e63e343821ddb5
Summary:
The trie UDI sanitization in db_crashtest.py disables use_txn because TransactionDB ROLLBACK writes DELETE entries that violate UDI's Put-only restriction. However, it was not clearing use_optimistic_txn or test_multi_ops_txns, which both require use_txn to be true.
Since use_trie_index is randomly enabled with 1/8 probability, the optimistic_txn and multiops_txn crash tests would intermittently fail with:
- "You cannot set use_optimistic_txn true while use_txn is false"
- "-use_txn must be true if -test_multi_ops_txns"
Fix by also setting use_optimistic_txn=0 and test_multi_ops_txns=0 in the trie UDI sanitization block alongside the existing use_txn=0.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14446
Test Plan: Watch crash test CI results
Reviewed By: xingbowang
Differential Revision: D95986370
Pulled By: pdillinger
fbshipit-source-id: 0751dcb4e99425ba9eb9aa34c2a99efa8eb3a194
Summary:
The function handles more than just lock timeouts — it also covers TryAgain from optimistic transactions. Rename it and update the comment to reflect its broader scope.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14441
Reviewed By: hx235
Differential Revision: D95877799
Pulled By: xingbowang
fbshipit-source-id: 0651ffd39ac9d3a7979750be6dfe5b293eab7a64
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14442
In `BackgroundEmptyTrash()`, the bucket counter uses post-decrement
(`iter->second--`), which assigns the pre-decrement value to
`pending_files_in_bucket`. When a bucket transitions from 1 to 0 files,
`pending_files_in_bucket` receives the value 1 (not 0), so the check
`pending_files_in_bucket == 0` fails. This means `WaitForEmptyTrashBucket()`
is never woken up when a specific bucket empties, unless all global pending
files are also zero.
Fix: Use pre-decrement (`--iter->second`) so the decremented value correctly
triggers the condition variable signal.
Reviewed By: xingbowang
Differential Revision: D95303385
fbshipit-source-id: 3c5f0978ff33600acaf406b3f839cf13d9983055
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14427
In `MultiGetBlob()`, the `adjustments` autovector is populated only for blob
requests that pass validation. Requests that fail validation are skipped via
`continue`, so `adjustments` has fewer entries than `blob_reqs`. When consuming
results, `adjustments[i]` uses the `blob_reqs` index instead of the
`read_reqs`/`adjustments` index, causing an out-of-bounds read when any request
fails validation.
Fix: Replace `adjustments[i]` with `adjustments[j - 1]`, since `j` tracks the
position in `read_reqs`/`adjustments` and has already been post-incremented.
Reviewed By: hx235
Differential Revision: D95303356
fbshipit-source-id: a264ae6481f74ce33f64e40624441d666135bcd0
Summary:
Add per-file sampling of "collapsible" entry reads (single deletions, merges, and kNotFound results) that may later be used to help inform read-triggered compactions. This is a better metric than `num_reads_sampled` as it is more targeted towards reads that could be avoided via compaction.
The existing behavior of `num_reads_sampled` is that reads only gets sampled on iterator creation for a file. It is problematic because next/prev() calls are not sampled, nor are additional seeks().
This PR moves sampling to per-seek/next granularity within `LevelIterator` and adds a new `num_collapsible_entry_reads_sampled` counter that tracks how often a file serves entries that could be eliminated by compaction.
Note only L1+ files have iterator seeks/nexts/prevs sampled. Introducing this at L0 would require wrapping table reader iterators, introducing a performance cost.
## Key changes
- **New counter `num_collapsible_entry_reads_sampled`** in `FileSampledStats` tracks sampled reads that encounter deletions, single deletions, merges, or kNotFound results in both Get and Iterator paths.
- **Moved sampling from file-open to per-operation** in `LevelIterator`: sampling now happens in `SampleRead()` called from `Seek()`, `SeekForPrev()`, `SeekToFirst()`, `SeekToLast()`, `Next()`, `NextAndGetResult()`, and `Prev()`. The `should_sample` parameter was removed from `LevelIterator`'s constructor.
- **Differentiated sampling rate for Next() vs Seek()**: `should_sample_file_read_next()` uses a 64x lower sampling rate (`kFileReadSampleRate * 64`) since Next() is cheaper than Seek() and called more frequently.
- **Collapsible tracking in Get path**: `Version::Get()` now increments the collapsible counter when `GetContext::State()` is `kNotFound`, `kMerge`, or `kDeleted`.
- **Collapsible tracking in MultiGet path**: `MultiGetFromSST` also increments the collapsible counter for the same states.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14434
Test Plan:
- Added new DB tests for both num_reads_sampled and num_collapsible_entry_reads_sampled
### Benchmark results (readrandom, readseq)
Setup: 1M keys, 16-byte keys, 100-byte values, no compression, fillrandom+compact
| Benchmark | Params | ops/s (main) | ops/s (feature) | % change |
|------------|--------------------|-------------|--------------------------|----------|
| readrandom | seed=1, threads=1 | 387,194 | 389,449 | +0.6% |
| readseq | seed=1, threads=1 | 5,598,371 | 5,572,975 | -0.5% |
No meaningful performance regression observed — differences are within run-to-run noise.
Reviewed By: xingbowang
Differential Revision: D95613793
Pulled By: joshkang97
fbshipit-source-id: 9dd09c9b7527b148424bde5686f4157c7a9e1214
Summary:
### Context/Summary:
**_See below for an example of the bug:_**
L1 has 5 SST files at indices [0, 1, 2, 3, 4] where files at indices 1/2/3
share user key boundaries (e.g., file[1].largest and file[2].smallest have
the same user key).
Round-robin picks file[1] at start_index=1.
Before fix:
1. ExpandInputsToCleanCut expands {file[1]} → {file[1], file[2], file[3]}
2. Loop starts at i=2 (start_index+1), adds file[2] → duplicate!
3. start_level_inputs_ = {file[1], file[2], file[3], file[2]}
4. GetRange uses back()=file[2], returns [file[1].smallest, file[2].largest]
5. ExpandInputsToCleanCut converges on {file[1], file[2]}, missing file[3]
6. AssertCleanCut crashes (debug) or data corruption (release)
After fix:
1. ExpandInputsToCleanCut expands {file[1]} → {file[1], file[2], file[3]}
2. Find last file = file[3] at index 3, loop starts at i=4
3. start_level_inputs_ = {file[1], file[2], file[3], file[4]} (no duplicates)
4. GetRange correctly returns [file[1].smallest, file[4].largest]
_**More details:**_
When round-robin compaction picks a file, PickFileToCompact calls ExpandInputsToCleanCut which may expand start_level_inputs_ from 1 file to N files (when adjacent files share user key boundaries). Then SetupOtherFilesWithRoundRobinExpansion loops from start_index + 1 to add more files, not knowing the expansion already happened. This re-adds files already in start_level_inputs_, creating duplicates.
The duplicates corrupt GetRange, which trusts inputs.back()->largest for non-L0 levels. With a duplicate earlier file at back(), GetRange returns a truncated range. ExpandInputsToCleanCut then converges on an incomplete file set, violating the clean-cut invariant.
In debug builds this crashes at AssertCleanCut. In release builds the compaction proceeds with a non-clean-cut input, causing data corruption (newer data in a later level while older data remains in an earlier level).
The fix finds the position of the last file already in start_level_inputs_ and starts the loop after it, avoiding duplicates. When start_level_inputs_ has only 1 file (no expansion happened), the behavior is unchanged.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14436
Test Plan:
New test RoundRobinCleanCutWithSharedBoundary:
- Without fix: crashes at AssertCleanCut in SetupOtherFilesWithRoundRobinExpansion
```
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from DBCompactionTest
[ RUN ] DBCompactionTest.RoundRobinCleanCutWithSharedBoundary
db_compaction_test: db/compaction/compaction_picker.cc:81: void rocksdb::AssertCleanCut(const rocksdb::InternalKeyComparator*, rocksdb::VersionStorageInfo*, rocksdb::CompactionInputFiles*, int, rocksdb::Logger*): Assertion `false' failed.
Received signal 6 (Aborted)
Invoking GDB for stack trace...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/local/fbcode/platform010/lib/libthread_db.so.1".
0x00007fd9ba8f0e13 in __GI___wait4 (pid=1004850, stat_loc=0x7fd9b8bfb2ac, options=0, usage=0x0) at ../sysdeps/unix/sysv/linux/wait4.c:30
30 ../sysdeps/unix/sysv/linux/wait4.c: No such file or directory.
https://github.com/facebook/rocksdb/issues/4 __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:45
45 pthread_kill.c: No such file or directory.
https://github.com/facebook/rocksdb/issues/5 __GI___pthread_kill (threadid=<optimized out>, signo=6) at pthread_kill.c:62
62 in pthread_kill.c
https://github.com/facebook/rocksdb/issues/6 0x00007fd9ba8444ad in __GI_raise (sig=6) at ../sysdeps/posix/raise.c:26
26 ../sysdeps/posix/raise.c: No such file or directory.
https://github.com/facebook/rocksdb/issues/7 0x00007fd9ba82c433 in __GI_abort () at abort.c:79
79 abort.c: No such file or directory.
https://github.com/facebook/rocksdb/issues/8 0x00007fd9ba83bc28 in __assert_fail_base (fmt=0x7fd9ba9e11d8 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0x7fd9bcc04819 "false", file=0x7fd9bcc04700 "db/compaction/compaction_picker.cc", line=81, function=<optimized out>) at assert.c:92
92 assert.c: No such file or directory.
https://github.com/facebook/rocksdb/issues/9 0x00007fd9ba83bc93 in __GI___assert_fail (assertion=0x7fd9bcc04819 "false", file=0x7fd9bcc04700 "db/compaction/compaction_picker.cc", line=81, function=0x7fd9bcc04780 "void rocksdb::AssertCleanCut(const rocksdb::InternalKeyComparator*, rocksdb::VersionStorageInfo*, rocksdb::CompactionInputFiles*, int, rocksdb::Logger*)") at assert.c:101
101 in assert.c
https://github.com/facebook/rocksdb/issues/10 0x00007fd9bc2f8181 in rocksdb::AssertCleanCut (icmp=0x7fd9b9a7a840, vstorage=0x7fd9b9b6d040, inputs=0x7fd9b8bfc690, level=1, logger=0x7fd9b9aa2790) at db/compaction/compaction_picker.cc:81
81 assert(false);
https://github.com/facebook/rocksdb/issues/11 0x00007fd9bc2f8fa6 in rocksdb::CompactionPicker::ExpandInputsToCleanCut (this=0x7fd9b9b32900, vstorage=0x7fd9b9b6d040, inputs=0x7fd9b8bfc690, next_smallest=0x0) at db/compaction/compaction_picker.cc:310
310 AssertCleanCut(icmp_, vstorage, inputs, level, ioptions_.logger);
https://github.com/facebook/rocksdb/issues/12 0x00007fd9bc30cbfb in rocksdb::(anonymous namespace)::LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion (this=0x7fd9b8bfc910) at db/compaction/compaction_picker_level.cc:423
```
- With fix: compaction completes, data correctness verified
Reviewed By: joshkang97
Differential Revision: D95718659
Pulled By: hx235
fbshipit-source-id: 6ef125455ef5ae8a07c323835ff25588dbbb3634
Summary:
The whitebox_crash_test crashes with Assertion `checking_set_.count(cfd) == 0` failed in `FlushScheduler::ScheduleWork()` during a read-only DB open (e.g., StressTest::TestBackupRestore).
## Root cause:
Commit 3aa706c2b ("Enforce WriteBufferManager during WAL recovery") added logic to schedule flushes when `WriteBufferManager::ShouldFlush()` is true during WAL recovery. However, the drain logic in `MaybeWriteLevel0TableForRecovery()` was gated by !read_only, so in read-only mode the flush
scheduler queue was never cleared. On subsequent WAL records, `ScheduleWork()` is called again for the same CFD still in the queue, triggering the duplicate assertion.
## Fix:
Add a `read_only` parameter to `InsertLogRecordToMemtable()` and skip WBM flush scheduling entirely in read-only mode. Flushes cannot be performed during read-only recovery, so scheduling them is pointless and causes the assertion failure when the scheduler is never drained.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14440
Test Plan:
- flush_job_test — all 15 tests pass
- db_basic_test --gtest_filter="*ReadOnly*:*Recovery*:*WAL*" — all 9 tests pass
- db_write_buffer_manager_test — all 18 tests pass
- `python3 tools/db_crashtest.py whitebox --simple --duration=600 --interval=30`
Reviewed By: xingbowang
Differential Revision: D95829493
Pulled By: jaykorean
fbshipit-source-id: bdfef9ce66d507a1169381064344afd612a4b318
Summary:
Add automatic per-block interpolation search selection (`kAuto` mode) for index blocks. During SST construction, each index block's key distribution is analyzed using the coefficient of variation (CV) of gaps between restart-point keys. Blocks with uniformly distributed keys are flagged via a new bit in the data block footer, and at read time, `kAuto` resolves to interpolation search for uniform blocks and binary search otherwise.
## Key changes
- **New `BlockSearchType::kAuto` enum value**: Resolves per-block at read time to either `kInterpolation` or `kBinary` based on the block's uniformity flag. Falls back to `kBinary` on older versions that don't recognize it.
- **Write-path uniformity analysis**: `BlockBuilder::ScanForUniformity()` uses Welford's online algorithm to incrementally compute the CV of key gaps at restart points. The result is stored in a new bit (bit 30) of the data block footer's packed restart count.
- **New table option `uniform_cv_threshold`** (default: -1 `disabled`): Controls how strict the uniformity check is. Set to negative to disable. Exposed in C++, Java (JNI), and `db_bench`.
- **Code reorganization**: Block entry decode helpers (`DecodeEntry`, `DecodeKey`, `DecodeKeyV4`, `ReadBe64FromKey`) moved from `block.cc` to a new shared header `block_util.h` so they can be reused by `BlockBuilder` on the write path.
- **New histogram `BLOCK_KEY_DISTRIBUTION_CV`**: Records the CV (scaled by 10000) of each index block's key distribution for observability.
- **Java bindings**: `IndexSearchType.kAuto`, `uniformCvThreshold` getter/setter, JNI portal constructor signature updated, and `HistogramType.BLOCK_KEY_DISTRIBUTION_CV` added.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14383
Test Plan:
- `IndexBlockTest.IndexValueEncodingTest` parameterized to include `kAuto` search type alongside `kBinary` and `kInterpolation`, verifying correct seek/iteration behavior across all combinations of key distributions, restart intervals, and key lengths.
- Uniformity detection validated: blocks with uniform key distribution correctly set `is_uniform = true`, blocks with clustered/non-uniform keys set `is_uniform = false`.
- Stress test coverage
- Updated check_format_compatible to also include a "uniform" dataset. By default using uniform_cv_threshold=-1 does not result in an incompatibility issues. When manually changing the threshold (e.g. `uniform_cv_threshold=1000`), I see `bad block contents`, which is expected
## Benchmark
readrandom with `fillrandom,compact -seed=1 --statistics`:
| Benchmark | Branch | Params | avg ops/s | % change vs main | CV P50 |
|-----------|--------|--------|-----------|------------------|--------|
| readrandom | main | `binary_search, shortening=1` | 335,791 | baseline | N/A |
| readrandom | feature | `binary_search, shortening=1` (default) | 335,749 | -0.0% | 1,500 |
| readrandom | feature | `auto_search, shortening=1` (kAuto) | 366,832 | **+9.2%** | 1,500 |
| readrandom | feature | `interpolation_search, shortening=1` | 366,598 | **+9.2%** | 1,500 |
| readrandom | feature | `auto_search, shortening=2` (kAuto) | 344,631 | **+2.6%** | 1,030,000 |
| readrandom | feature | `interpolation_search, shortening=2` | 201,178 | **-40.1%** | 1,030,000 |
As seen with shortening=2, a non-uniform distribution produces a high CV, which does not use interpolation search.
## Write benchmark
There is a write overhead which scans each restart entry for a block upon Finish. In practice this is very low because currently it is only applied to index blocks.
See cpu profile (https://fburl.com/strobelight/io5hwj9h) here of `-benchmarks=fillseq,compact -compression_type=none -disable_wal=1`. Only 0.08% attributed to `ScanForUniformity`.
Reviewed By: pdillinger
Differential Revision: D94738890
Pulled By: joshkang97
fbshipit-source-id: 9661ac593c5fef89d49f3a8a027f1338a0c96766
Summary:
Address 2 compatibility issue of UDI:
A:
Trie UDI (UserDefinedIndex) does not support SeekToFirst/SeekToLast. The crash test already disabled prefix scanning (prefixpercent=0) when use_trie_index=1, but iteration (iterpercent) was still enabled.
During iteration, LevelIterator::SkipEmptyFileForward() internally calls file_iter_.SeekToFirst() when Next() crosses SST file boundaries within a level. This propagates to UserDefinedIndexIteratorWrapper::SeekToFirst() which returns NotSupported, causing "Iterator diverged from control iterator" / "VerifyIterator failed" errors across many crash test variants.
B:
BlobDB is incompatible with trie UDI (user-defined index). When BlobDB is enabled (`enable_blob_files=1`), the flush job stores large values in separate blob files and writes `kTypeBlobIndex` entries in the SST instead of `kTypeValue`. The UDI builder (`UserDefinedIndexBuilderWrapper::OnKeyAdded()`) rejects any entry whose type is not `kTypeValue`, causing the flush to fail with `"user_defined_index_factory only supported with Puts"`.
Once the flush fails, the DB enters background error state, and all subsequent `Write()` calls fail with the same error -- printed as the repeated `"multiput error: ..."` messages from
`BatchedOpsStressTest::TestPut`. Eventually, `ProcessStatus` encounters this error and triggers `assert(false)`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14432
Reviewed By: pdillinger
Differential Revision: D95457462
Pulled By: xingbowang
fbshipit-source-id: bf2bc47bd1ed75926765b2e3ff068f99f89a7793
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14424
## Context
`MemPurge` releases db_mutex_ to do its merge work and re-acquires it before adding the output memtable to the immutable list. During this window, concurrent writers can fill the active memtable and trigger a switch, adding a new immutable memtable with a higher ID. `MemPurge` then assigns its output memtable the stale ID from mems_.back() (the newest memtable in the original flush batch), which is now lower than the front of the immutable list, violating the ordering assertion in `MemTableListVersion::AddMemTable `.
## Fix
After re-acquiring db_mutex_, use `std::max(mems_.back()->GetID(), imm()->GetLatestMemTableID())` so the output memtable's ID is never lower than any existing immutable memtable. Two `TEST_SYNC_POINT`s are added to the `MemPurge` success path to enable deterministic repro.
Reviewed By: pdillinger
Differential Revision: D94756638
fbshipit-source-id: 2e9e15b4285dc6b996c8744795228180dbd73ed3
Summary:
Add a new immutable DB option `enforce_write_buffer_manager_during_recovery` to control whether WriteBufferManager buffer_size is enforced during WAL recovery. When multiple RocksDB instances share a WriteBufferManager, a recovering instance could exceed the global memory limit by replaying large amounts of WAL data into memtables. This can cause OOM, especially when other instances are actively using the shared memory budget. When this option is enabled (and a WriteBufferManager is configured), RocksDB will check `WriteBufferManager::ShouldFlush()` after each batch insertion during WAL recovery and schedule flushes when needed to keep memory bounded.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14305
Test Plan:
Two unit tests added - `WriteBufferManagerLimitDuringWALRecoverySingleDB` and `WriteBufferManagerLimitDuringWALRecoveryMultipleDBs`
```
./db_write_buffer_manager_test --gtest_filter="*WriteBufferManagerLimitDuringWALRecovery*"
```
Basic stress test added with options toggled
## To follow up
Multiple DB scenario in stress test
Reviewed By: hx235
Differential Revision: D92533792
Pulled By: jaykorean
fbshipit-source-id: 35ca70e2300a8bfd6b81a6646a65ef284fb90a9a
Summary:
Propagate builder error when flush produces empty output
When write fault injection causes the table builder to enter an error
state during flush, all subsequent Add() calls return early (ok() is
false), leaving the builder empty (IsEmpty() == true). Previously,
BuildTable() would call builder->Abandon() but not propagate the
builder's error status to 's', leaving it OK. This caused the downstream
key count validation in flush_job.cc to fire a misleading Corruption
error ('Number of keys in flush output SST files does not match...'),
which the stress test harness couldn't identify as a retryable injected
fault error, leading to SafeTerminate().
This started failing recently because ('Separate keys and
values in data blocks', ) introduced a new SST
block format (separate_key_value_in_data_block) that stores keys and
values in separate sections within data blocks. This format requires
additional write operations during Flush() inside the table builder,
increasing the probability that write fault injection
(--write_fault_one_in=128) hits a data block write and puts the builder
into an error state before any entries are committed. The bug in
BuildTable() existed before, but was rarely triggered because the old
interleaved block format had fewer write points susceptible to fault
injection during the critical Add() path.
Fix: After builder->Abandon(), propagate the builder's error status to
's' when the builder is empty due to an internal error. This ensures the
actual IOError from write fault injection is reported, which the stress
test can properly handle via IsErrorInjectedAndRetryable().
The analysis was based on stack trace. However, it would be great
if we could get direct evidence from fault injection.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14418
Test Plan: Unit test
Reviewed By: hx235
Differential Revision: D95121070
Pulled By: xingbowang
fbshipit-source-id: cfb513bd744ac34ac90cda11c1cbe49a9d0a7c6c
Summary:
This fixes a longstanding bug in which `ldb dump` swallows iterator errors. This can affect check_format_compatible.sh test results; if lucky, it will misleadingly look like a data mismatch instead of an outright failure. If unlucky, it could cause a test false negative.
However the compatibility test uses old versions of ldb, so the best way to improve the test (for the foreseeable future) is to replace `ldb dump` with `ldb scan`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14422
Test Plan: manual
Reviewed By: joshkang97
Differential Revision: D95332577
Pulled By: pdillinger
fbshipit-source-id: bef1b427dd8aaa2cabbd23b7ad9f3cad1f67a349
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14401
Unify the MultiScan and regular iterator codepaths in BlockBasedTableIterator by introducing a MultiScanIndexIterator that implements InternalIteratorBase<IndexValue>. During Prepare(), the original index iterator is swapped out for a MultiScanIndexIterator that wraps the prefetched block handles and scan range metadata. This allows SeekImpl() and FindBlockForward() to use the same code flow for both regular and MultiScan operations, eliminating the need for separate MultiScan-specific methods (SeekMultiScan, FindBlockForwardInMultiScan, MultiScanSeekTargetFromBlock, MultiScanUnexpectedSeekTarget, MultiScanLoadDataBlock, MarkPreparedRangeExhausted).
Key changes:
- New MultiScanIndexIterator class that manages scan range tracking, block handle iteration, forward-only seek enforcement, and wasted block counting
- InitDataBlock() loads blocks from ReadSet when MultiScan is active
- FindBlockForward() detects scan range boundaries via IsScanRangeExhausted() after index_iter_->Next()
- Disabled reseek optimization for MultiScan so MultiScanIndexIterator::Seek() is always called to update scan range tracking state
- Removed MultiScanState struct and all MultiScan-specific methods from BlockBasedTableIterator
- No changes to CheckDataBlockWithinUpperBound or CheckOutOfBound — they work as-is through iterate_upper_bound
- multi_scan_status_ intentionally not checked in Valid() hot path to avoid performance regression; when status is non-OK, block_iter_points_to_real_block_ is already false
- Fixed pre-existing bug in ReadSet::SyncRead() that used the base decompressor without compression dictionary, causing ZSTD data corruption when blocks with dictionary compression needed synchronous fallback reads
Reviewed By: xingbowang
Differential Revision: D93300655
fbshipit-source-id: 231059208e0cc512bc2ec43ff7055fcb2a2dc72d
Summary:
When the same user key spans adjacent data blocks with different sequence
numbers, the trie index cannot distinguish them because it only stores
user keys. This adds a side-table that stores per-leaf sequence numbers
and overflow block metadata, enabling correct post-seek correction using
seqno comparison.
Design:
- Trie always stores user-key-only separators (unchanged)
- Duplicate separators from same-key boundaries are de-duplicated
- Side-table stores leaf_seqnos[], leaf_block_counts[], and overflow
arrays (offsets, sizes, seqnos) serialized after the trie data
- Post-seek correction: after trie Seek lands on a leaf, compare
target_seq vs leaf_seqno to decide whether to advance through
overflow blocks or to the next trie leaf
- Zero overhead when no same-user-key boundaries exist (no side-table
serialized, no seqno checks at seek time)
Public API changes (user_defined_index.h):
- AddIndexEntry: single pure virtual with IndexEntryContext parameter
(replaces old 4-arg version). Context carries last_key_seq and
first_key_seq for block boundaries.
- SeekAndGetResult: single pure virtual with SeekContext parameter
(replaces old 2-arg version). Context carries target_seq.
- Fix trailing semicolons on NewBuilder/NewReader default overrides.
Wrapper layer (user_defined_index_wrapper.h):
- Extract sequence numbers from parsed internal keys and pass via
context structs to UDI builder/iterator.
- Fix CurrentIndexSizeEstimate() to delegate to internal builder
(was returning 0).
- Fix ApproximateMemoryUsage() to include UDI reader memory.
- Fix typos: lof_err_key -> log_err_key, COuld -> Could,
"Bad index name" -> "Bad index name: ".
Trie builder (trie_index_factory.cc):
- Buffer separator entries during building; at Finish(), detect whether
any same-user-key boundary was seen (sticky flag, same strategy as
ShortenedIndexBuilder::must_use_separator_with_seq_).
- When seqno encoding is needed, re-encode all separators with seqno
side-table metadata in the trie.
- Default null comparator to BytewiseComparator() to prevent crash.
Trie iterator (trie_index_factory.cc):
- Post-seek correction: compare target_seq vs leaf_seqno to advance
through overflow runs.
- Overflow run state tracking: overflow_run_index_, overflow_run_size_,
overflow_base_idx_ for O(1) access to overflow block handles.
- NextAndGetResult advances within overflow runs before moving to next
trie leaf.
- Return kOutOfBound instead of kUnknown when Seek/Next finds no blocks.
LOUDS trie (louds_trie.h, louds_trie.cc):
- Seqno side-table: builder AddKeyWithSeqno/AddOverflowBlock methods,
BFS reordering of seqno/block_count arrays, serialization/deserialization
of per-leaf seqnos, block counts, overflow handles/seqnos, and
overflow_base_ prefix sum for O(1) access.
- AppendKeySlot() helper with debug bounds assert on all key-append sites.
Dead code removal:
- LoudsTrieBuilder::NumKeys() (never called externally)
- sparse_leaf_count_ (serialized but never read by the reader)
- DenseChildNodeNum(pos), DenseLeafIndex(pos), DenseNodeNum(pos)
(superseded by FromRank variants that avoid redundant Rank1 calls)
Deserialization hardening (all in InitFromData, cold path only):
- Move num_keys_ validation before any dependent arithmetic.
- Validate dense bitvector sizes match dense_node_count_ (d_labels
must be node_count*256, d_has_child must equal d_labels.NumOnes(),
d_is_prefix_key must equal node_count).
- Validate sparse bitvector sizes match s_labels_size_ (s_has_child
and s_louds must have the same number of bits as the label array).
- Validate child position table values within s_labels_size_ bounds.
- Validate chain suffix offset+length within suffix data blob.
- Validate chain end child indices < num_internal or == UINT32_MAX.
- EliasFano: add count_ upper-bound check (<=2^30) to prevent
count_*low_bits_ integer overflow.
- Bitvector: validate select hint values < num_rank_samples_ to
prevent OOB in FindNthOneBit/FindNthZeroBit.
- EliasFano: custom move ctor/assignment to re-seat low_words_
after owned_low_data_ move (fixes dangling pointer for SSO strings).
https://github.com/facebook/rocksdb/issues/14406
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14412
Reviewed By: anand1976
Differential Revision: D95160933
Pulled By: xingbowang
fbshipit-source-id: f2c3681c1059c03d540ce1cb9cc14cc79cd9730c
Summary:
It contains 3 commits.
Commit 1: Fix /dev/shm exhaustion during make check
Fix /dev/shm exhaustion during make check
1. Add space-heavy tests to NON_PARALLEL_TEST: perf_context_test (1GB
write_buffer_size), obsolete_files_test (~1GB), backup_engine_test
(1GB), prefetch_test (1GB), and db_io_failure_test (256MB).
2. Add per-test cleanup on success: each parallel test script now
removes its test directory after passing. Failed test directories
are preserved for debugging.
3. Add age-based stale directory cleanup: at the start of make check,
remove /dev/shm/rocksdb.* directories older than 3 hours from
previous failed runs, using age-based filtering to avoid disturbing
concurrent runs.
Commit 2: Fix SIGSEGV in prefetch_test due to stale SyncPoint callbacks
Both FilePrefetchBufferTest and FSBufferPrefetchTest fixtures set up
SyncPoint callbacks capturing local variables by reference and enable
processing, but neither fixture's TearDown() clears them. When a
subsequent test runs, the stale callbacks fire with dangling
references, causing memory corruption and SIGSEGV.
Fixed by adding DisableProcessing() and ClearAllCallBacks() to both
fixtures' TearDown() methods.
Commit 3: Fix OpenFilesAsyncTest using excessive disk space
Fix OpenFilesAsyncTest using excessive disk space in /dev/shm
OpenFilesAsyncTest::SetupData() set write_buffer_size to SIZE_MAX to
prevent automatic flushes. This caused each of its ~20 parallel
instances to consume 6-43 GB in /dev/shm (validated: a single
Shutdown/3 instance used 43 GB), totaling ~233 GB and filling the
disk. This was the primary cause of "No space left on device" errors
in make check. The sst flushed is small, but it causes issue in WAL
space reservation, which bloated the disk usage.
The SIZE_MAX is unnecessary — the test writes only 4 tiny key-value
pairs and does explicit Flush() after each. The default 64 MB
write_buffer_size will never auto-flush for such small writes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14425
Test Plan: Unit Test
Reviewed By: joshkang97
Differential Revision: D95253518
Pulled By: xingbowang
fbshipit-source-id: 03907df59b3d89d90413a0e33996ec205944d48b
Summary:
wal_in_db_path_ was declared without an in-class initializer in DBImpl. While DB::Open, OpenAsSecondary, and OpenAsFollower all explicitly set this field, OpenForReadOnly and CompactedDBImpl::Open did not.
This was harmless until recently because CloseHelper() only calls PurgeObsoleteFiles when opened_successfully_ is true, and read-only DBs previously did not set opened_successfully_. After https://github.com/facebook/rocksdb/issues/14322 added opened_successfully_ = true to the read-only path, CloseHelper now executes PurgeObsoleteFiles -> DeleteObsoleteFileImpl, which reads wal_in_db_path_ to decide whether WAL files should be deleted in the foreground. Reading an uninitialized bool is undefined behavior, caught by UBSan as "invalid-bool-load" in the secondary_cache_crash_test.
Fixed by adding an in-class initializer (= false) to wal_in_db_path_. The default of false means WAL deletions use foreground deletion, which is safe for read-only DBs that don't write WALs. The existing DB::Open path continues to set the correct value explicitly.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14419
Test Plan:
- Added regression test ReadOnlyDBWalInDbPathInitialized in db_test2
- Ran test 5 times without failure
- Ran all OpenForReadOnly tests (3 tests pass)
Task:
T257988006
Reviewed By: joshkang97
Differential Revision: D95122268
Pulled By: xingbowang
fbshipit-source-id: 7f65fe1f09e7e0b42ba68f44f246615abc0757d4
Summary:
The trie-based User Defined Index (UDI) has a bug in its iterator implementation that causes "Not implemented: SeekToFirst not supported" errors during prefix scanning. This causes assertion failures in BatchedOpsStressTest::TestPrefixScan and may cause silent wrong results in other prefix scan tests.
Until the trie index is fixed, disable prefix scanning when use_trie_index is enabled by setting prefixpercent=0 and redistributing the percentage to reads.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14421
Test Plan:
- Reproduced: db_stress with --use_trie_index=1 --prefixpercent=5 crashes with assertion failure. Instrumentation revealed iterator status "Not implemented: SeekToFirst not supported" on prefix 0x35.
- Verified: db_stress with --use_trie_index=1 --prefixpercent=0 completes cleanly.
Reviewed By: anand1976
Differential Revision: D95135250
Pulled By: xingbowang
fbshipit-source-id: 81540b2426aa1a855a58e6ca9e68a035d53aa2d8
Summary:
The kv ratio compaction option sanitization is too strict. Sometimes, it blocks engine start up. Relax the validation and convert it to runtime check.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14397
Test Plan: Unit test
Reviewed By: pdillinger
Differential Revision: D94709471
Pulled By: xingbowang
fbshipit-source-id: cc076c397b3acfa426112063224771a196684798
Summary:
When using optimistic transactions (--use_optimistic_txn=1), the transaction commit can fail with Status::TryAgain() if the memtable history is insufficient for conflict detection (controlled by max_write_buffer_size_to_maintain). ExecuteTransaction retries up to 10 times, and if all retries fail, it returns TryAgain.
Previously, this TryAgain status was not handled in TestPut, TestDelete, and TestSingleDelete, causing the stress test to call SafeTerminate() and crash with "put or merge error" / "delete error".
This is an expected condition with optimistic transactions and should be handled gracefully by rolling back the pending expected value.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14410
Test Plan:
- make -j db_stress && run crash_test_with_optimistic_txn
- Stress test with --use_optimistic_txn=1 --use_txn=1 with small max_write_buffer_size_to_maintain to verify no crash on TryAgain.
Reviewed By: hx235
Differential Revision: D95077227
Pulled By: xingbowang
fbshipit-source-id: 15c343cc1c5f888fb79e95a87cfe46145f98b3a1
Summary:
The trie-based UDI (LoudsTrie/Bitvector) stores zero-copy reinterpret_cast pointers into the serialized UDI block data. When mmap_read is enabled, the block data resides in memory-mapped file pages. If the SST file's mmap mapping is later invalidated (e.g. through table cache eviction or DB reopen), the trie's internal pointers become dangling, causing SIGSEGV.
For now, disable mmap_read when use_trie_index is enabled:
- db_crashtest.py: force mmap_read=0 when use_trie_index=1
- db_stress_tool.cc: reject the combination with an error message
The proper fix, involve loading index then fix the pointer address.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14409
Reviewed By: pdillinger
Differential Revision: D94971965
Pulled By: xingbowang
fbshipit-source-id: 6abbd4bfbfd26e7ba8725a93a6c68beb26f6393e
Summary:
In StressTest::OperateDb(), read_opts.table_index_factory is set to udi_factory_.get() (a raw pointer) once at function entry. When Reopen() is called during the stress test loop, Open() recreates udi_factory_ via std::make_shared<TrieIndexFactory>(), destroying the old factory. But read_opts.table_index_factory still holds the dangling pointer.
When MultiGet subsequently calls UserDefinedIndexReaderWrapper::NewIterator() and accesses read_options.table_index_factory->Name(), it dereferences a dangling pointer, causing a segmentation fault.
Fix: After each reopen, update read_opts.table_index_factory to point to the newly created udi_factory_ instance.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14411
Test Plan: - Ran db_stress with --use_trie_index=1 --reopen=20 --use_multiget=1 4 times without failure (previously crashed with SIGSEGV)
Reviewed By: pdillinger
Differential Revision: D95064144
Pulled By: xingbowang
fbshipit-source-id: 7139baacda659d8a3cb84fdcc4822a428542bf0c
Summary:
Add `open_files_async` option for faster DB startup. When enabled, SST file opening and validation is deferred to a background thread after `DB::Open` returns, reducing startup latency for databases with many SST files. WAL recovery remains synchronous.
To support this, `FindTable` is extended with a pinning mechanism that stores the cache handle directly on `FileMetaData` via a new `PinnedTableReader` class, and sets the table reader atomically so subsequent reads skip cache lookups. `FileDescriptor::table_reader` is replaced with `PinnedTableReader pinned_reader` which wraps a `std::atomic<TableReader*>` with acquire/release ordering to safely handle concurrent access between the background opener and read threads.
Should validations fail, the background opener sets a `kAsyncFileOpen` background error. Future read requests will look up the table reader again via the cache, and if any validations fail there it will get propagated to the user (existing behavior when `max_open_files > 0`).
This feature is most useful when `max_open_files=-1`, because otherwise file opening is already capped at 16 files and DB open should be fast.
## Restrictions
- This feature also is incompatible with fifo compaction because fifo compaction requires reading table properties under DB mutex. When table reader is unpinned, this may cause a DB hang.
- This feature is also incompatible with `skip_stats_update_on_db_open=false` because it will result in even longer DB open
## Key changes
- New `open_files_async` DB option with C, Java, and `db_bench` bindings
- `BGWorkAsyncFileOpen` background worker that opens all SST files post-`DB::Open`, with shutdown awareness via `shutting_down_` flag
- New `PinnedTableReader` class in `version_edit.h` — thread-safe wrapper holding `std::atomic<TableReader*>` and `Cache::Handle*` with proper acquire/release ordering. Replaces the old `FileDescriptor::table_reader` raw pointer and `FileMetaData::table_reader_handle`
- Extract `LoadTableHandlersHelper` into `db/version_util.cc` — shared between `VersionBuilder::LoadTableHandlers` (for version edits during recovery) and `BGWorkAsyncFileOpen` (for base storage post-open)
- `FindTable` extended with `pin_table_handle` and `out_table_reader` params — when pinning is enabled, the table reader is stored on `FileMetaData` so Get/MultiGet/Iterator skip redundant cache lookups. `FindTable` now performs the pinned-reader fast-path check internally instead of requiring callers to check `fd.table_reader` beforehand
- Note: pinning is explicit (not default) because some callers create temporary `FileMetaData`s that would need to properly clean up table handles
- `CompactedDBImpl` updated to use `FindTable` + pinning instead of raw `fd.table_reader` access for Get/MultiGet
- New `kAsyncFileOpen` background error reason in `listener.h` and `error_handler.cc`
- Add a check in ~DBImpl to ensure async file open task has not been forgotten to be scheduled in (future) subclasses of DBImpl. Certain subclasses that never use it will need to explicitly mark it.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14322
Test Plan:
- `OpenFilesAsyncTest` parameterized over `num_flushes` (1, 20), `ReadType` (Get, MultiGet, Iterator), `max_open_files` (-1, 10), and `read_only` (true, false)
- **ConcurrentFileAccess**: concurrent reads and compactions race with async opener
- **AfterRead**: reads happen before async opener, verifying lazy open and that the opener sees already-pinned readers
- **BeforeRead**: async opener completes first, verifying reads use pre-loaded table readers
- **Shutdown**: DB closes before async opener starts, verifying clean cancellation with 0 file opens
- **Error**: corrupted SST files, verifying `kAsyncFileOpen` background error is set and reads return corruption
- **DropColumnFamily**: CF dropped before async opener runs, verifying the opener gracefully skips dropped CFs
- Added to crash test
### Benchmark
To simulate a high-latency remote filesystem, I set up a virtual filesystem with dm-delay using 10ms reads, 0 ms writes.
```
# Generate a DB with many L0 files
TEST_TMPDIR=/data/users/jkangs/dm-delay-test/mnt ./db_bench -benchmarks=fillseq -disable_auto_compactions=true -write_buffer_size=1000 -num=1000000
```
```
./db_bench -use_existing_db=true -db=/data/users/jkangs/dm-delay-test/mnt/dbbench -benchmarks=readrandom -reads=1 -report_open_timing=true -open_files_async=true -use_direct_reads -file_opening_threads=1 -skip_stats_update_on_db_open
OpenDb: 25.1419 milliseconds
```
```
./db_bench -use_existing_db=true -db=/data/users/jkangs/dm-delay-test/mnt/dbbench -benchmarks=readrandom -reads=1 -report_open_timing=true -open_files_async=false -use_direct_reads -file_opening_threads=1 -skip_stats_update_on_db_open
OpenDb: 23109.4 milliseconds
```
### No read regressions
On main branch
```
./db_bench -use_existing_db=true -db=/dev/shm/dbbench -benchmarks=readrandom -seed=1 -threads=8 -duration=30
readrandom : 4.827 micros/op 1657100 ops/sec 30.005 seconds 49720992 operations; 183.3 MB/s (6198999 of 6198999 found)
```
On this branch
```
./db_bench -use_existing_db=true -db=/dev/shm/dbbench -benchmarks=readrandom -seed=1 -threads=8 -duration=30
readrandom : 4.863 micros/op 1644808 ops/sec 30.007 seconds 49354992 operations; 182.0 MB/s (6099999 of 6099999 found)
./db_bench -use_existing_db=true -db=/dev/shm/dbbench -benchmarks=readrandom -seed=1 -threads=8 -duration=30 -open_files_async=true
readrandom : 4.803 micros/op 1665392 ops/sec 30.004 seconds 49968992 operations; 184.2 MB/s (6222999 of 6222999 found)
```
Reviewed By: pdillinger, xingbowang
Differential Revision: D93538033
Pulled By: joshkang97
fbshipit-source-id: 32ac70c112cd733b7c1e1c1e2e7ce6422318a5ae
Summary:
Fix stress test to check UDI usage for unsupported iterator ops
UserDefinedIndexIterator only supports Seek(target) + Next() - it requires
a target key for all seeks. The following operations are not supported:
- SeekToFirst() - no target key
- SeekToLast() - no target key
- SeekForPrev() - not in UDI interface
- Prev() - not in UDI interface
This fix checks for UDI usage at both levels:
1. ReadOptions level: ro.table_index_factory != nullptr
2. CF/table level: udi_factory_ != nullptr (BlockBasedTableOptions)
This is future-proof for any UDI implementation, not just trie index.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14407
Reviewed By: joshkang97
Differential Revision: D94925001
Pulled By: xingbowang
fbshipit-source-id: bf2ceacc7acebbc2347be445e2f208820a97b817
Summary:
As a follow-up to https://github.com/facebook/rocksdb/issues/13736, allow a "quiet" DB to react much sooner to time-based compaction triggers. For details see DBImpl::ComputeTriggerCompactionPeriod() implementation.
Also based on review feedback, fixing a bug where only column families setting periodic compaction would be triggered, rather than any time-based compaction.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14396
Test Plan: extended+added unit tests to cover much of the logic
Reviewed By: xingbowang
Differential Revision: D94626166
Pulled By: pdillinger
fbshipit-source-id: de9ca19e46bdba5d9715474efbc61805354d730d
Summary:
When the UDI wrapper's OnKeyAdded() encountered a non-Put key type (e.g. Delete or Merge), it set its internal status_ to non-OK and stopped forwarding OnKeyAdded() to the wrapped internal index builder. However, AddIndexEntry() was always forwarded unconditionally. This asymmetry left the internal ShortenedIndexBuilder's current_block_first_internal_key_ empty, triggering an assertion failure in GetFirstInternalKey() during the buffered-block replay in MaybeEnterUnbuffered().
The crash required three conditions to co-occur:
1. UDI enabled (use_trie_index=1)
2. Compression dictionary enabled (triggering kBuffered mode)
3. Non-Put entries in the data (Delete, Merge, etc.)
Fix: move the internal_index_builder_->OnKeyAdded() call before the status_ guard so the internal builder always receives every key, matching the unconditional forwarding in AddIndexEntry().
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14404
Reviewed By: archang19
Differential Revision: D94791493
Pulled By: xingbowang
fbshipit-source-id: 882e409f61ae9aca084e9511794ca32ba4ff090f
Summary:
Implement a Fast Succinct Trie (FST) index based on LOUDS encoding as a User Defined Index plugin for RocksDB's block-based tables. First step toward trie-based indexing per https://github.com/facebook/rocksdb/issues/12396.
The trie uses hybrid LOUDS-Dense (upper levels, 256-bit bitmaps) + LOUDS-Sparse (lower levels, label arrays) encoding inspired by the [SuRF paper](https://www.pdl.cmu.edu/PDL-FTP/Storage/surf_sigmod18.pdf) (Zhang et al., SIGMOD 2018). The boundary between dense and sparse levels is automatically chosen to minimize total space.
Key Components
- **Bitvector** with O(1) rank and O(log n) select using a rank LUT sampled every 256 bits with popcount intrinsics. Uses uint32_t rank LUT entries (halving memory vs uint64_t). Includes word-level `AppendWord()` for efficient dense bitmap construction and `AppendMultiple()` optimized at word granularity for bulk bit fills.
- **Streaming trie builder** using flat per-level arrays with deferred internal marking, handle migration for prefix keys, and lazy node creation. Infers trie structure directly from sorted keys via LCP analysis in a single pass (no intermediate tree).
- **LoudsTrie** for immutable querying with BFS-ordered handle reordering built into single-pass level-by-level serialization. Move-only semantics with correct pointer re-seating after `std::string` move.
- **LoudsTrieIterator** with rank-based traversal, key reconstruction from trie path, and stack-based backtracking for `Next()`. Uses packed 8-byte `LevelPos` (is_dense flag in bit 63) and `autovector<LevelPos, 24>` to avoid heap allocation. Key reconstruction uses a raw char buffer allocated once to `MaxDepth()+1` bytes.
- **TrieIndexFactory/Builder/Reader/Iterator** implementing the `UserDefinedIndexFactory` interface.
- **Zero-copy block handle loading** using two fixed-width uint64_t arrays (offsets + sizes) with 8-byte alignment, enabling O(1) initialization via direct pointer assignment.
Seek Hot Path Optimizations
- **Fanout-1 sparse fast path**: Most sparse nodes in tries built from zero-padded numeric keys have exactly one child. Detected via `start_pos + 1 == end_pos` and inlined as a single byte comparison, avoiding the full `SparseSeekLabel` call.
- **Linear scan for small sparse nodes**: `SparseSeekLabel` uses sequential scan for nodes with ≤16 labels instead of binary search. Faster for common 10-child digit nodes where branch misprediction cost outweighs linear scan cost.
- **Rank reuse**: `DenseLeafIndexFromRankAndHasChildRank` and `SparseLeafIndexFromHasChildRank` overloads accept pre-computed `has_child_rank` from the Seek descent, avoiding redundant `Rank1` calls.
General Performance Optimizations
- **Select-free sparse traversal**: Precomputed child position lookup tables (`s_child_start_pos_`/`s_child_end_pos_`) eliminate `Select1` calls during Seek. Sparse traversal tracks `(start_pos, end_pos)` directly, using only `Rank1` (O(1)) + array lookup (O(1)) for child descent.
- **Cached label_rank pattern**: Eliminates redundant `Rank1` calls in hot paths (Seek, Next, Advance all cache and reuse the label_rank computed for has_child checking).
- **Leaf index fast path**: When no prefix keys exist (common case), `SparseLeafIndex` and `DenseLeafIndexFromRank` skip the prefix-key `Rank1` calls entirely, reducing from 3 to 1 `Rank1` call.
- **Popcount-based Select64** via 6-step binary search within 64-bit words.
- MSVC portability using RocksDB's `BitsSetToOne`/`CountTrailingZeroBits`.
Benchmark Results
Trie Seek at 32K keys (16-byte keys, 5M lookups, median of 5 runs):
| Configuration | ns/op |
|---|---:|
| Trie (optimized) | **118** |
| Binary search (native) | 134 |
Trie Seek is **~12% faster** than native binary search index at 32K keys per block.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14310
Reviewed By: anand1976
Differential Revision: D93921511
Pulled By: xingbowang
fbshipit-source-id: ba18604bc6bd4f575311a1ae3047a541274869b6
Summary:
**Summary:**
This is to sync an internal change of passing `Statistics*` to the GetContext constructor and collecting more stats as well as fix a bug this change created.
SstFileReader::MultiGet was passing nullptr for both `SystemClock*` and `Statistics*` to the GetContext constructor. After `Statistics*` was passed to the GetContext constructor (the internal change), this caused a segfault when a merge operation was triggered with statistics enabled, because the merge helper's StopWatchNano attempted to dereference the null clock pointer. Fix by passing `r->ioptions.clock` from the reader's options.
Additionally, add `assert(clock_)` guards to `StopWatchNano::Start()` and `ElapsedNanos()` to catch null clock bugs in debug builds. Can't do so in release build because it's on hot path.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14393
Test Plan:
- `./sst_file_reader_test --gtest_filter='SstFileReaderTableMultiGetTest.Basic'` exercises merge with statistics enabled, previously segfaulted without the fix with the clock, now passes.
- `./sst_file_reader_test` - all tests pass.
Reviewed By: xingbowang
Differential Revision: D94599343
Pulled By: hx235
fbshipit-source-id: 0a748bb00ee27bb202d01d410b52657101c05de0
Summary:
The nightly build had a flag for portable build on folly, but rocksdb does not. This causes link error. Fix this by removing portable build flag in folly build in nightly run. Nightly run will always build without cache using native flag. Only PR jobs uses cache.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14391
Test Plan: nightly run
Reviewed By: joshkang97
Differential Revision: D94520182
Pulled By: xingbowang
fbshipit-source-id: 7c7d3b089744c7e2e8ea073158f1b5b80db420d4
Summary:
-march=native in cmake builds causes SIGILL after cache hits
Issue: PORTABLE=1 env var only works for Makefile builds. cmake ignores it and injects -march=native via its own CPU detection. Since ccache hashes the flag string literally, a cache compiled on an AVX-512 runner and restored on a non-AVX-512 runner produces a SIGILL crash.
Fix: Added -DPORTABLE=ON to build-linux-cmake-with-benchmark-no-thread-status and build-linux-cmake-with-folly-coroutines cmake commands.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14388
Test Plan: Github CI
Reviewed By: joshkang97
Differential Revision: D94367557
Pulled By: xingbowang
fbshipit-source-id: 5aa39cc36fc004d0ee636cd3fa197880d6cb773a
Summary:
**Context/Summary:**
It's very easy to make mistake in removing deprecated option such as deleting the corresponding entry in type info that breaks backward compatibility or missing tests to test backward compatibility. Example: https://github.com/facebook/rocksdb/pull/14350#discussion_r2834554287. Added a claude md file for that.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14360
Test Plan: In claude code, prompted for deprecated option removal with this claude md file in a repo separated from my previous deprecation efforts as much as possible and received correct change at one try.
Reviewed By: pdillinger
Differential Revision: D93920127
Pulled By: hx235
fbshipit-source-id: 1977ce7404b0188f84258552b4843e70394a5aea
Summary:
PR https://github.com/facebook/rocksdb/issues/14266 ("Remove compression support") removed compression-related functionality from blob_dump_tool and ldb commands. While this was valid for the legacy stacked BlobDB (which no longer supports compression), the integrated blob storage (`enable_blob_files`) uses the same blob file format and fully supports compression via `blob_compression_type`.
This partial revert restores the ability to view uncompressed blob data from compressed blob files, which is essential for debugging and analysis of integrated blob storage.
Restored functionality:
- `blob_dump --show_uncompressed_blob` option
- `ldb dump --dump_uncompressed_blobs` option
- `ldb dump_live_files --dump_uncompressed_blobs` option
- Related ldb_test.py test coverage
The decompression implementation in utilities/blob_db/blob_dump_tool.cc has been updated to use the modern
`GetBuiltinV2CompressionManager()->GetDecompressorOptimizeFor()` API.
Suggested follow-up:
* Tests for blob_dump (or integrate it into sst_dump/ldb?)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14382
Test Plan:
Restored ldb_test.py
Some manual testing
Reviewed By: mszeszko-meta
Differential Revision: D94257588
Pulled By: pdillinger
fbshipit-source-id: c6d8a556a51ec9422df208ae161806ccc1f20b36
Summary:
Accelerate CI: ccache integration, replace scan-build, upgrade runners
1. ccache integration via reusable composite action (setup-ccache)
- Created .github/actions/setup-ccache/action.yml
- Applied to 13+ Linux compilation jobs in pr-jobs.yml
- PORTABLE=1 by default to avoid illegal instruction errors on
heterogeneous runners; disabled for jobs linking pre-built Folly
- On cache hit, reducing compilation from 20+ min to ~2-5 min per job
- ccache placed before Folly build so Folly compilation also
benefits from cache on Folly cache misses
2. Replace scan-build with clang-tidy (30+ min -> seconds)
- Removed build-linux-clang18-clang-analyze job from pr-jobs.yml
- Expanded .clang-tidy to enable all clang-analyzer-* checks,
matching scan-build's full coverage
- Existing clang-tidy-comment.yml workflow now handles both
clang-tidy and clang-analyzer checks on changed files only
3. Upgrade Folly job runners for faster builds and tests
- build-linux-make-with-folly: 16-core -> 32-core, -j32 -> -j64
- build-linux-cmake-with-folly-coroutines: 16-core -> 32-core,
-j20 -> -j64 (both make and ctest)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14368
Reviewed By: joshkang97
Differential Revision: D94166095
Pulled By: xingbowang
fbshipit-source-id: 3dfbd71aace3ba9cb2e790873bdbedba20215657
Summary:
Add proper synchronization in mempurge unit test to fix flaky test
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14377
Test Plan: Unit test
Reviewed By: nmk70
Differential Revision: D94153290
Pulled By: xingbowang
fbshipit-source-id: 2a2cdcc6f8b5500bb5e68d2d223255cd1a5660b3
Summary:
Sanitize crash test arg for interpolation search when UDTs are used. Currently its not supported and returns invalid arg on DB open.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14374
Test Plan: CI
Reviewed By: archang19
Differential Revision: D94123220
Pulled By: joshkang97
fbshipit-source-id: 89b63f14a721bc692465620298161af23eec2446
Summary:
Summary
- Fix a race condition in three WritePreparedTransactionSeqnoTest tests (SeqnoGoesBackwardsDuringErrorRecovery, SeqnoDiscrepancyDuringErrorRecovery, ConcurrentWritesDuringErrorRecovery) that could cause permanent hangs.
- The tests inject a filesystem error during flush via a WriteManifest sync point callback, then wait for background error recovery to complete. The bug was in the ordering of operations after recovery starts: SetFilesystemActive(true) was called before ClearCallBack, allowing a window where recovery's ResumeImpl could trigger the callback and re-disable the filesystem. This left the filesystem permanently disabled, causing all recovery retries to fail and exit without firing the RecoverSuccess sync point, leaving the test thread blocked forever.
- The fix swaps the order so ClearCallBack is called before SetFilesystemActive(true), ensuring the filesystem cannot be re-disabled by a late callback firing.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14361
Test Plan:
- Stress tested with gtest_parallel (500 iterations, 32 workers, 60s timeout) with no hangs observed.
- Previously reproduced the hang at ~7% rate under stress with 15s timeout before the fix.
Reviewed By: pdillinger
Differential Revision: D93929251
Pulled By: anand1976
fbshipit-source-id: 9cb6844ed20146c754091575156b08d5551b3034
Summary:
Introduce a new V2 serialization format for wide column entities that supports storing individual column values in blob files. The V2 format adds a column type section that marks each column as either inline or blob-index, enabling per-column blob storage for large values.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14314
Reviewed By: pdillinger
Differential Revision: D92832066
Pulled By: xingbowang
fbshipit-source-id: 13c24347e1f481a059d67eef987d2d2b184b4a51
Summary:
RocksDB has been using clang-tidy for a long time inside Meta. However, it is not efficient for external contributor, as the result from clang-tidy has to be ferried back through internal contributor. This PR added support to run clang-tidy on external github CI. It added .clang-tidy file based on internal version. It run clang-tidy in a separate pr job and a workflow step would post the pr job result to the PR itself. See example below.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14347
Test Plan: Github CI
Reviewed By: archang19
Differential Revision: D93862467
Pulled By: xingbowang
fbshipit-source-id: bb4330241036894deb619470efd73a7041a8b62f
Summary:
**Context/Summary:**
Remove deprecated, unused APIs and options:
- ReadOptions::managed: This option was not used anymore. The functionality it controlled has been removed long ago.
- ColumnFamilyOptions::snap_refresh_nanos: Deprecated and unused option.
Corresponding C API (rocksdb_readoptions_set_managed) and Java API (ReadOptions.managed/setManaged) are also removed. All related checks an db_impl and db_impl_secondary iterators are cleaned up.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14350
Test Plan: make check
Reviewed By: pdillinger
Differential Revision: D93812438
Pulled By: hx235
fbshipit-source-id: e4a9d21c65f83294b6d0878286ba14024f049bac
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14355
SupportedOps advertised kAsyncIO based only on the IsIOUringEnabled() weak symbol check, without verifying that the constructor's io_uring probe actually succeeded. Add a thread_local_async_read_io_urings_ null check so kAsyncIO is only reported when the probe passed. Also update the constructor to probe with the same IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN flags that ReadAsync and MultiRead use at runtime.
Reviewed By: anand1976
Differential Revision: D93780065
fbshipit-source-id: 6f51f544b267cb39d09b49949a9485f55eeae12e
Summary:
**Context/Summary:**
Remove `SstFileWriter::Add()` (deprecated in favor of `Put()`) and the `skip_filters` parameter from `SstFileWriter` constructors (deprecated in favor of setting `BlockBasedTableOptions::filter_policy` to `nullptr`).
Both APIs have zero active callers. The `skip_filters` field is also removed from `TableBuilderOptions` (write-side only; the read-side `TableReaderOptions::skip_filters` is unchanged).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14352
Test Plan: make check
Reviewed By: xingbowang
Differential Revision: D93812389
Pulled By: hx235
fbshipit-source-id: 236b36a6e664758ab5ad90e606bc195d0a6de70f
Summary:
a couple recent failures in this test. Waiting for purge and disabling sync points before Close should resolve the issues.
Also fixing EventListenerTest.BlobDBOnFlushCompleted because it showed up as flaky in CI for this PR
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14349
Test Plan: watch CI
Reviewed By: mszeszko-meta
Differential Revision: D93619322
Pulled By: pdillinger
fbshipit-source-id: bb9fc7d3c0ecaaeaffe4305e1ad403cbcd597484
Summary:
In my last version bump, I forgot that the next release would be a major release. We can fix that now ahead of release cut.
I'm also updating folly now because I have experience resolving folly issues. Folly commit e04860553 changed libevent to build as static-only so required a change in our build.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14357
Test Plan: CI
Reviewed By: xingbowang
Differential Revision: D93797666
Pulled By: pdillinger
fbshipit-source-id: 22179da900f9dc6c5544163071079a4701c7c663
Summary:
**Summary/Context:**
Remove the `InRange()` virtual method from `SliceTransform` and all its overrides. This method was marked DEPRECATED, never called by RocksDB, and existed only for backward compatibility.
Also removes the `in_range` callback parameter from `rocksdb_slicetransform_create()` in the C API, which is a breaking change appropriate for a major version release.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14353
Test Plan: Make check
Reviewed By: xingbowang
Differential Revision: D93795070
Pulled By: hx235
fbshipit-source-id: 5eba23f1d038b19c494997a55e5d8ca379fbedcb
Summary:
There was an edge case missed in the implementation of interpolation search for target keys that had a length smaller than the shared prefix.
E.g. first_key = "aaaaaa", last_key = "aaaaaz", target_key = "aaz". In the existing setup, we will seek to position 0, but in reality is should be seeked to the end.
#### The fix
The solution here was to also do a bounds check on the first search iteration. We utilize memcmp on the target key with the shared_prefix to determine if the target key is outside the bounds. An edge case here is if the target key itself a prefix of the shared prefix (e.g. target = "aaaa"), in this case memcmp return return 0, but the target key is actually smaller.
### Minor optimizations
- cache left,right values so we don't need to re-compute it when left/right boundaries don't change
- In ReadBe64FromKey, utilize memcpy + swap for fast path
- since we have already computed a shared_prefix, every other comparison only needs to compare the non-shared suffixes
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14343
Test Plan:
Added new unit tests to test this case
### Benchmarks
No significant regressions due to additional memcmp.
#### Configuration
- **CPU:** 192 * AMD EPYC-Genoa Processor
- **RocksDB Version:** 10.12.0
- **Compression:** Snappy
- **Entries:** 1,000,000
- **Value Size:** 100 bytes
- **Index Search Type:** interpolation_search
- **Index Shortening Mode:** 1
#### Results
| Benchmark | Params | ops/s (main) | ops/s (feature) | % change |
|-----------|--------|-------------|-----------------|----------|
| readrandom | 16B keys, no prefix | 367,264 | 369,163 | +0.52% |
| readrandom | 100B keys, prefix_size=50 | 376,066 | 371,193 | -1.29% |
Reviewed By: pdillinger
Differential Revision: D93535267
Pulled By: joshkang97
fbshipit-source-id: beda182efce1e914ff587e697b927347cfa42656
Summary:
Add clang-tidy-comment workflow. This workflow allows pr clang tidy pr job to post the clang-tidy finding directly on the PR page.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14348
Test Plan: Will be tested with next clang-tidy PR
Reviewed By: joshkang97
Differential Revision: D93670150
Pulled By: xingbowang
fbshipit-source-id: 8245f9d5bde8cf800d88034c4339de9f387c5692
Summary:
Extending https://github.com/facebook/rocksdb/issues/14323 by testing scenarios for compaction after downgrade. Detail: we shouldn't need to test loading options with compaction, as options file inclusion is mostly a sanity check for "can you open the DB with options file?"
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14344
Test Plan: manual run of SHORT_TEST=1 J=140 tools/check
Reviewed By: xingbowang
Differential Revision: D93553897
Pulled By: pdillinger
fbshipit-source-id: ec08ae2a3d49971e24a215e38df9506fe1133096
Summary:
and remove deprecated DB::MaxMemCompactionLevel(). In the process of pushing through a relatively clean refactoring of uses of the old functions, some other minor public APIs are also migrated from raw DB pointers to unique_ptr.
Claude did pretty much all the work, but requiring dozens of prompts to actually push through relatively clean phase out of raw DB pointers from what needed to be touched, and leaving that code in better shape. (Hundreds of `DB*` still remain all over the place even outside C and Java bindings.)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14335
Test Plan: existing tests; no functional changes intended
Reviewed By: xingbowang, mszeszko-meta
Differential Revision: D93523820
Pulled By: pdillinger
fbshipit-source-id: e4ca22ad81cd2cfe91122d7507d7ca34fe03d043
Summary:
After PR https://github.com/facebook/rocksdb/issues/14315 dropped support for block-based table format_version < 2, several code paths became obsolete. This change removes them.
Investigation findings:
1. Table properties are now a hard requirement for block-based SST files:
- format_version >= 2 guarantees a properties block exists
- Removed defensive conditionals like `if (rep_->table_properties)`
- Missing properties block now returns Status::Corruption instead of just logging an error. This is important because some properties affect the semantic interpretation of the file.
2. Index type property (kIndexType) is now required:
- kIndexType was introduced in Feb 2014 (commit 74939a9e1), ~11 months BEFORE format_version was introduced in Jan 2015
- BlockBasedTablePropertiesCollector::Finish() has always written kIndexType unconditionally for all block-based tables
- Therefore all format_version >= 2 files have this property
- Now returns Status::Corruption if missing instead of silently defaulting to kBinarySearch
3. Removed SetOldTableOptions() from sst_file_dumper:
- This fallback handled files without a properties block
- Dead code since format_version >= 2 guarantees properties exist
4. Removed kPropertiesBlockOldName ("rocksdb.stats") fallback:
- The properties block was renamed from "rocksdb.stats" to "rocksdb.properties" in RocksDB 2.7 (April 2014)
- format_version 2 was introduced in RocksDB 3.10 (Oct 2015)
- All table formats (block-based, plain, cuckoo) were created after the rename, so they all use "rocksdb.properties"
- The backward compatibility fallback in FindOptionalMetaBlock() was dead code for all supported table formats
5. Removed obsolete assertion about format_version 0 checksum in BlockBasedTableBuilder::WriteFooter()
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14327
Test Plan: some tests updated for updated requirements. Mostly, CI including format compatible test
Reviewed By: mszeszko-meta
Differential Revision: D93124820
Pulled By: pdillinger
fbshipit-source-id: eb12cbdca0e69f34a08051d5160c282384128a4a
Summary:
I'm implementing this intending it to be used for https://github.com/facebook/rocksdb/issues/14287
Refactor the data block footer encoding/decoding to use a struct-based Encode/Decode API (DataBlockFooter), reserving the top 4 bits of the footer for metadata:
- Bit 31: Hash index present (kDataBlockBinaryAndHash) - existing use
- Bits 28-30: Reserved for future features
Comments have some detail for why it is safe to assume no practical existing SST files would use these newly reserved bits. And for forward compatibility, existing versions detect (non-zero) use of these new bits as impossibly large num_restarts and report "bad block contents". Not perfect, but not bad.
Key changes:
- Replace PackIndexTypeAndNumRestarts/UnPackIndexTypeAndNumRestarts with DataBlockFooter::EncodeTo/DecodeFrom methods
- DecodeFrom returns a detailed error when reserved bits are set, enabling graceful failure on newer format versions
- Reduce kMaxNumRestarts from 2^31-1 to 2^28-1 (268M), which is adequate for the maximum possible restarts in a 4GiB block
- Add GetCorruptionStatus() to Block for detailed error messages (Note that we are sensitive to the size of Block objects, so have to avoid adding unnecessary new members.)
- Remove obsolete kMaxBlockSizeSupportedByHashIndex size checks
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14332
Test Plan:
- Existing unit tests and format compatibility test
- Add test for reserved bit detection (ReservedBitInDataBlockFooter)
Reviewed By: joshkang97
Differential Revision: D93293152
Pulled By: pdillinger
fbshipit-source-id: b65a83e96bb09a98fb9b8b2dd9f754653ca7ed4d
Summary:
This PR fixes a bug in the interaction between WritePrepared/WriteUnprepared TransactionDB (with two_write_queues=true) and background error recovery. This bug caused crash tests to fail with a "sequence number going backwards" error during DB open.
Root Cause
------------
When two_write_queues=true, sequence numbers are allocated via FetchAddLastAllocatedSequence() before a write completes, but are only published via SetLastSequence() after the write succeeds. If a background error occurs (e.g., a MANIFEST write failure during flush), the error recovery path in DBImpl::ResumeImpl creates new memtables and WAL files. The new WAL's starting sequence number is based on LastSequence() (the published value), which can be lower than already-allocated sequence numbers that were written to the old WAL. On subsequent recovery, RocksDB detects that sequence numbers in the new WAL are lower than those in the old WAL and reports a "sequence number going backwards" corruption error, causing the DB to fail to open.
Fix
---
The fix adds a call to a new VersionSet::SyncLastSequenceWithAllocated() method at the beginning of DBImpl::ResumeImpl, before any new memtables or WALs are created. This method advances last_sequence_ to match last_allocated_sequence_ if the latter is higher, ensuring the new WAL starts with a sequence number that is at least as high as any previously allocated one.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14313
Test Plan:
---------
Add new unit tests in write_prepared_transaction_test_seqno
Reviewed By: pdillinger
Differential Revision: D92746944
Pulled By: anand1976
fbshipit-source-id: 34385fc13fd74435dd1c3283637eb118f45d887e
Summary:
Fixes blog author display issues on rocksdb.org/blog by:
* Adding missing authors to authors.yml: pdillinger, alanpaxton, akankshamahajan15, anand1976, poojam23
* Standardizing on GitHub usernames: renamed sdong → siying
* Fixing typo in 2016-02-25-rocksdb-ama.markdown: yhchiang → yhciang
* A short note in CLAUDE.md
Authors were not showing on the blog because they were referenced in post frontmatter but not defined in the _data/authors.yml lookup file.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14342
Test Plan: push & see ;)
Reviewed By: mszeszko-meta
Differential Revision: D93523972
Pulled By: pdillinger
fbshipit-source-id: 757c33e80f3c1d99ff4134a37321f40634d6e294
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14321
Add file_checksum and file_checksum_func_name fields to FileOptions so that downstream FileSystem implementations can access per-file checksum metadata when SST files are opened. The fields are populated from FileMetaData at all call sites where SST files are opened via NewRandomAccessFile: TableCache::GetTableReader, Version::GetTableProperties, and CompactionJob::ReadTablePropertiesDirectly. Also fixes the fallback path in TableCache::GetTableReader to use the local fopts (with temperature and checksum) instead of the original file_options.
Added a kNoFileChecksumFuncName which is distinct from kUnknownFileChecksumFuncName:
- kUnknownFileChecksumFuncName ("Unknown"): We have FileMetaData for this file, and the metadata says no checksum was computed (no factory was configured when the file was written). This is a property of the file itself.
- kNoFileChecksumFuncName ("Unavailable"): We don't even have FileMetaData — we're opening this file in a context where there's no checksum metadata to propagate at all (e.g., SstFileDumper, SstFileReader, checksum generation). It's a property of the call site, not the file.
So the assertion file_checksum.empty() is correct for both, but for different reasons — one says "the file has no checksum," the other says "we have no idea about this file's checksum."
Reviewed By: pdillinger
Differential Revision: D92728944
fbshipit-source-id: 8fd34ea22ca87090b26d0a55c921f354f97f1ffc
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14337
## Context:
D91624185 changed `FilePrefetchBuffer::PollIfNeeded` from `void` to returning `Status`, correctly propagating `Poll` errors instead of silently swallowing them. A side effect is that when io_uring fails to initialize at runtime (e.g., sandcastle seccomp restrictions), `ReadAsync` returns `NotSupported` which now propagates through `PrefetchRemBuffers` and `HandleOverlappingAsyncData`, causing `PrefetchInternal` to return early before executing the synchronous `Read` that the caller actually depends on. This leaves iterators invalid with no recovery path — both in `db_stress` crash tests and in production. The filesystem advertises async IO support (`CheckFSFeatureSupport` passes), so the failure only surfaces at runtime when io_uring initialization fails. The prior behavior silently degraded to sync reads because `PollIfNeeded` swallowed the error.
## Changes
Add a sync fallback in `FilePrefetchBuffer::ReadAsync` — the single chokepoint for all async reads. When `reader->ReadAsync()` returns `NotSupported`, fall back to `reader->Read()` synchronously, populate the buffer inline, and return OK. Since `async_read_in_progress_` stays false, `PollIfNeeded` becomes a no-op (nothing to poll, data is already there). All callers — `PrefetchRemBuffers`, `HandleOverlappingAsyncData`, `PrefetchAsync` — work transparently without any per-site changes.
Reviewed By: archang19
Differential Revision: D93432284
fbshipit-source-id: daef185fc3535e347d182e75dd443ae921eeb495
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14340
`ReadBe64FromKey` pads its result with `val <<= (8 - len) * 8` to right-align partial reads. When the seek target's user key is shorter than shared_prefix_len, len is 0 and this becomes a shift by 64, which is undefined behavior for uint64_t. On x86 this happens to produce 0 (the correct result), but UBSan rightfully flags it. Guard the shift with `len > 0 && len < 8`.
Reviewed By: joshkang97
Differential Revision: D93435715
fbshipit-source-id: bab128e9a65ea18d401670268cbac77d45e11340
Summary:
FIFO crash tests fail on DB open when `fifo_compaction_max_data_files_size_mb` is randomly set to 100 or 500 MB, because `max_table_files_size` defaults to 1GB and the validation requires `max_data_files_size` >= `max_table_files_size` when non-zero. Cap `max_table_files_size` to `max_data_files_size` in db_stress when the latter is set. `max_table_files_size` is ignored at runtime when `max_data_files_size` is non-zero, so this only satisfies the validation constraint.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14341
Reviewed By: pdillinger
Differential Revision: D93503113
Pulled By: mszeszko-meta
fbshipit-source-id: 5c3e7c9b568661244c71c548cb0fe5e55472c0ca
Summary:
Add a new kv ratio based compaction picking algorithm in fifo compaction
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14326
Test Plan: Unit test
Reviewed By: pdillinger
Differential Revision: D93257941
Pulled By: xingbowang
fbshipit-source-id: fd2d0e1356c7b54682a1197475a1bd26cb45c9d4
Summary:
Interpolation search is an alternative algorithm to binary search, which performs better on uniformly distributed keys. Instead of binary search always computing the mid point of the left and right boundaries, interpolation search "interpolates" the mid point based on the distance to the target. Fortunately, we can re-use existing block format to support interpolation search.
For a given block, we compute the shared_prefix length of the first and last key. Interpolation search is usually done with numerical target values, so for a variable binary length key, we calculate the "value" as the first 8 non-shared bytes. This also means interpolation search would only really be effective for bytewise comparator (guarded via options validations).
#### Fallback to binary search
- if the the val(left_key) == val(right_key) then we fallback to classic binary search (to avoid divide by 0)
- interpolation search is significantly more computationally expensive than binary search, so when the search distance is small, we also fallback to binary search.
- if interpolation search does not make significant progress (i.e. reduces search space by more than half each iteration), we can assume data is non-uniform and fallback.
Interpolation search also performs best when there is minimal shortening, especially shortening of the last block, as it can heavily skew the distribution of the actual keys.
Note that each search algorithm is guaranteed to make progress because at each iteration the search space is guaranteed to be reduce by at least 1.
For now this change only applies to index block seeks, as data block seeks and other blocks do not have as many entries and would not require significant number of search rounds, but it could be easily extended to include that support.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14247
Test Plan:
Updated unit tests and crash test with new search option
### Benchmark
The default benchmark sets up keys in generally uniform distribution, so it was a good way to test performance improvements.
Setup: `./db_bench -benchmarks=fillseq,compact -index_shortening_mode=1`
#### Before this change
```
./db_bench -use_existing_db=true -benchmarks=readrandom -seed=1
readrandom : 2.899 micros/op 344973 ops/sec 2.899 seconds 1000000 operations; 38.2 MB/s (1000000 of 1000000 found)
```
#### After this change
Notice how key comparison counts are the same between the two.
```
./db_bench -use_existing_db=true -benchmarks=readrandom -seed=1 -index_search_type=binary_search
readrandom : 2.881 micros/op 347128 ops/sec 2.881 seconds 1000000 operations; 38.4 MB/s (1000000 of 1000000 found)
```
```
./db_bench -use_existing_db=true -benchmarks=readrandom -seed=1 -index_search_type=interpolation_search
readrandom : 2.609 micros/op 383209 ops/sec 2.610 seconds 1000000 operations; 42.4 MB/s (1000000 of 1000000 found)
```
With a non-uniform distribution, `i.e. index_shortening_mode=2`
```
./db_bench -use_existing_db=true -benchmarks=readrandom -seed=1 -index_search_type=binary_search
readrandom : 2.958 micros/op 338075 ops/sec 2.958 seconds 1000000 operations; 37.4 MB/s (1000000 of 1000000 found)
```
```
./db_bench -use_existing_db=true -benchmarks=readrandom -seed=1 -index_search_type=interpolation_search
readrandom : 5.502 micros/op 181750 ops/sec 5.502 seconds 1000000 operations; 20.1 MB/s (1000000 of 1000000 found)
```
Reviewed By: pdillinger
Differential Revision: D91063163
Pulled By: joshkang97
fbshipit-source-id: 151d6aa76f8713740b714de6e406aff40d28ccbc
Summary:
In follow-up to https://github.com/facebook/rocksdb/issues/14315
Remove obsolete code replaced by new Compressor/Decompressor interface:
* OLD_CompressData and OLD_UncompressData
* Individual compression/decompression functions (Snappy_*, Zlib_*, BZip2_*, LZ4_*, LZ4HC_*, XPRESS_*, ZSTD_Compress, ZSTD_Uncompress)
* CompressionInfo and UncompressionInfo classes
* UncompressionDict class
* compression::PutDecompressedSizeInfo and GetDecompressedSizeInfo
The only small refactoring in this change that is not pure code removal or movement is in blob_file_builder_test.cc.
Move some function implementations etc. from compression.h to compression.cc:
* CompressionTypeToString, CompressionTypeFromString, CompressionOptionsToString
* ZSTD_TrainDictionary (both overloads), ZSTD_FinalizeDictionary
* DecompressorDict::Populate
* Most compression library includes
Also cleaned up other includes of compression.h, which caused some other files to need new includes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14325
Test Plan: existing tests
Reviewed By: hx235
Differential Revision: D93120580
Pulled By: pdillinger
fbshipit-source-id: ab5c50db7379c0387a8c0e379642c9ea2799eae5
Summary:
**Context/Summary:**
Internal adoption has demonstrated stability and measurable improvements of this feature without much cost so we can turn it on by default. Eventually we'd like to remove this configuration and make this an expected behavior.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14329
Test Plan: Existing unit test
Reviewed By: mszeszko-meta
Differential Revision: D93210059
Pulled By: hx235
fbshipit-source-id: 04f77954e6624c8e60a2db030eb19eb341dd0fcf
Summary:
See https://github.com/facebook/rocksdb/issues/14240 which brought this to my attention. Here I've added range deletions and compactions to the format compatible test, and fixed or worked-around compatibility issues (likely longstanding).
The first fix was in Version::MaybeInitializeFileMetaData for an assertion failure simply from adding range deletions from some 5.x version.
The second fix is a broader work-around for older SST files with unreliable num_entries/num_range_deletions/num_deletions statistics in their table properties. We depend on them only for some paranoid checks for compaction, so in my assessment the best way to deal with those files is to exclude the paranoid checks when dealing with the files with unrelaible data. (Details in code comments.) The important part is that compacting old files is exceptionally rare, so we aren't really interefering with the paranoid checks doing thier job on an ongoing basis.
This depends on https://github.com/facebook/rocksdb/issues/14315 (just landed) because there is a remaining undiagnosed problem with some very early releases, but I'm not fixing that because its support is being dropped.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14323
Test Plan: test extended (ran locally excluding some releases)
Reviewed By: xingbowang
Differential Revision: D93032653
Pulled By: pdillinger
fbshipit-source-id: f90b32f30ba4764692e68d23705f42c778e0dc1d
Summary:
**Context/Summary**:
compaction_job_test does low level assertions on what keys were saved and to resume in https://github.com/facebook/rocksdb/commit/1e5fa69c99ac8765783f5ce8a3a065b08f5b08a7 before the integration of the feature is done in a separate PR https://github.com/facebook/rocksdb/commit/f7e4009de1d16421a254dd7e799dd91c522d832c. Such low-level test makes it difficult to assert data correctness, is hard to understand by being tied to implementation details.
Therefore they are now replaced with db-level tests with data correctness check, which is what we ultimately care out of those details. I also expand the test to cover wide column and TimedPut() which associates a key with write time.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14191
Test Plan:
- Only test change; I also manually traced every test to ensure correct resumption point; also removing
```
if (c_iter->IsCurrentKeyAlreadyScanned()) {
return false;
}
```
correctly leads to the expected error with resumption at merge, single delete and deletion at bottom
Reviewed By: jaykorean
Differential Revision: D89492846
Pulled By: hx235
fbshipit-source-id: 6c6ab3cbd643ca1b15d049a062da2c76165ef9db
Summary:
... and remove some old code and tech debt in the process.
This is arguably a great milestone and precendent in RocksDB history as for the first time we are explicitly dropping support for the ability to read source-of-truth data in old formats. (We previously dropped support for reading some old bloom filters, but those are performance optimizers not source-of-truth. https://github.com/facebook/rocksdb/issues/10184) However, DBs written with default settings since release 4.6.0, which is very nearly 10 years ago, can still be read. And by using compaction with intermediate versions, there's an upgrade path going back to (AFAIK) early releases of LevelDB (from which RocksDB was forked).
Some detail:
* The magic number for LevelDB SST files (0xdb4775248b80fb57, most recently called kLegacyBlockBasedTableMagicNumber) now only exists in the code to provide a good error message and to test that good error message.
* There is some notable refactoring and renaming around format_version handling. This is a bit of a messy area of code because the footer code being shared between different table formats (block-based, plain, cuckoo) means format_version in the footer is in ways tied to all of them, but in other ways is just tied to block-based table where we have been making updates. Hopefully code comments keep this clear.
* Now that there are old format_versions we can't read (and can't write authoritatively in tests), I've needed to split out kMinSupportedFormatVersion into a constant for reads and for writes, currently the same at format_version=2. Comments describe how to update these in the future.
* The idea of versioning the compression format is basically going away, though we're keeping BuiltinV2 in places just because it's already there. There's lots of room in the BuiltinV2 schema to expand to new built-in compression types, or new ways of handling existing compression algorithms. CompressionManager with CompatibilityName gives users the power to customize compression without the need for versions tied to format_version.
Immediate follow-up:
* Clean up compression loose ends like OLD_Compress, OLD_Uncompress
Suggested follow-up:
* Update plain table builder to migrate to new footer version so that we can drop support for legacy footer. We have to be careful that the (likely untested) forward compatibility path I put in place a while back works (or fix it and wait a while) before dropping support for plain table with legacy footer.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14315
Test Plan:
* Some tests updated / added
* A couple tests are obsolete: removed
* Also updated format compatible test, which now doesn't need to dig as far back into history building RocksDB.
Reviewed By: hx235
Differential Revision: D92577766
Pulled By: pdillinger
fbshipit-source-id: a23be846189d901ce087af4ca9a99cef18445cb7
Summary:
This could is triggering `-Wstring-conversion`, which presents as:
```
warning: implicit conversion turns string literal into bool: A to B
```
This is often a bug and what was intended. The most frequent cause is the code was:
```
void foo(bool) { ... }
void foo(std::string) { ... }
foo("this gets interpreted as a bool");
```
It is also possible the issue is innocuous as part of an assert:
```
assert(false && "this string is true, so the assertion is false");
EXPECT_FALSE("this string is true, so the expect fails");
```
in these cases the use is to "cute", so we modify the code to make it more obvious.
```
assert(false && "the compiler recognizes and doesn't complain about this pattern");
FAIL() << "much more obvious";
```
Differential Revision: D92886041
fbshipit-source-id: 6adfaa102f12e293491cc579ec92b48834d1d0a8
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14319
The test asserted h1->is_finished and h2->is_finished immediately after AbortIO({H0}), before calling Poll. This is invalid because AbortIO only guarantees that handles in its abort set are finalized. Non-aborted handles' CQEs may or may not be consumed during AbortIO depending on io_uring completion ordering. If H0's two CQEs (original read + cancel) arrive before H1/H2's CQEs, AbortIO breaks out of its wait loop without processing them. Move the H1/H2 is_finished assertions to after Poll, which correctly handles either case. Also remove the racy req_count checks for non-aborted handles since Poll does not increment req_count.
Reviewed By: jaykorean
Differential Revision: D92848827
fbshipit-source-id: 0c09b44ceada99877e8311cff799fa94f1056545
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14312
This could is triggering `-Wstring-conversion`, which presents as:
```
warning: implicit conversion turns string literal into bool: A to B
```
This is often a bug and what was intended. The most frequent cause is the code was:
```
void foo(bool) { ... }
void foo(std::string) { ... }
foo("this gets interpreted as a bool");
```
It is also possible the issue is innocuous as part of an assert:
```
assert(!"this string is true, so the assertion is false");
EXPECT_FALSE("this string is true, so the expect fails");
```
in these cases the use is to "cute", so we modify the code to make it more obvious.
```
assert(false && "the compiler recognizes and doesn't complain about this pattern");
FAIL() << "much more obvious";
```
Reviewed By: dmm-fb
Differential Revision: D92528316
fbshipit-source-id: 93fbb624e8731c4cdb559746b44c1aa71d786304
Summary:
To make CI consistent with internal meta clang version.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14311
Test Plan:
CI shows correct version
```
Successfully installed clang-format-21.1.2
clang-format version 21.1.2
```
Reviewed By: xingbowang
Differential Revision: D92535441
Pulled By: joshkang97
fbshipit-source-id: ea21ea97b13a35b286f0c2ce18b3f01ffbf49afd
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14301
When AbortIO was called with a subset of outstanding async read handles,
it would consume io_uring completions for handles NOT in the abort set
but fail to finalize them. This caused subsequent Poll calls on those
handles to hang forever waiting for completions that had already been
consumed.
The fix adds an `is_being_aborted` flag to Posix_IOHandle that is set
when submitting the cancel request. When processing completions in
AbortIO, handles with this flag wait for req_count==2 (original + cancel),
while handles without the flag are finalized immediately at req_count==1.
Also refactored the completion finalization logic into a shared
FinalizeAsyncRead() helper function used by both Poll and AbortIO.
Reviewed By: mszeszko-meta, archang19
Differential Revision: D92230883
fbshipit-source-id: e6d11e009a4930e5608459771990f6cf7d46d827
Summary:
This change adds the ability to open and operate on databases as TransactionDB in the ldb command-line tool.
New Command-Line Options
- --use_txn - Opens the database as a TransactionDB instead of a regular DB
- --txn_write_policy=<0|1|2> - Sets the transaction write policy:
- 0 = WRITE_COMMITTED (default)
- 1 = WRITE_PREPARED
- 2 = WRITE_UNPREPARED
Use Case
This is needed to inspect or modify databases that were created with WritePrepared or WriteUnprepared transactions, which require opening via TransactionDB::Open() rather than the regular DB::Open().
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14304
Test Plan:
Tests (tools/ldb_test.py): Adds testTxnPutGet() covering:
- Basic put/get/delete with TransactionDB
- All three write policies
- Validation that --use_txn and --ttl are mutually exclusive
Reviewed By: pdillinger
Differential Revision: D92323195
Pulled By: anand1976
fbshipit-source-id: 0a62b8ea4e2985feed977fad72595d6fff75db09
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14306
GetLiveFilesStorageInfo crashes when called on a read-only RocksDB because
it calls FlushWAL(), which accesses logs_.back() on an empty deque.
Root cause: DBImplReadOnly overrides SyncWAL() to return NotSupported, but
does NOT override FlushWAL(). Read-only DBs have an empty logs_ deque
because they don't create WAL writers during recovery - there's nothing to
write, so no WAL infrastructure is initialized.
The reason SyncWAL was originally marked NotSupported is that these WAL
operations (SyncWAL syncs buffer to disk, FlushWAL flushes to OS buffer)
require an active WAL writer at logs_.back().writer. Since read-only DBs:
1. Cannot perform writes
2. Don't create WAL files for writing
3. Have an empty logs_ deque
...there's no WAL writer to sync or flush. The operations are semantically
meaningless, not just "forbidden write operations."
The fix adds a FlushWAL override matching the SyncWAL pattern. The caller
in db_filesnapshot.cc:403-405 already handles IsNotSupported() gracefully:
if (s.IsNotSupported()) { s = Status::OK(); }
Reviewed By: pdillinger
Differential Revision: D92419557
fbshipit-source-id: 7079071209b3c7be41a2c98c9b691e68bc031595
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14296
This could is triggering `-Wstring-conversion`, which presents as:
```
warning: implicit conversion turns string literal into bool: A to B
```
This is often a bug and what was intended. The most frequent cause is the code was:
```
void foo(bool) { ... }
void foo(std::string) { ... }
foo("this gets interpreted as a bool");
```
It is also possible the issue is innocuous as part of an assert:
```
assert(!"this string is true, so the assertion is false");
EXPECT_FALSE("this string is true, so the expect fails");
```
in these cases the use is to "cute", so we modify the code to make it more obvious.
```
assert(false && "the compiler recognizes and doesn't complain about this pattern");
FAIL() << "much more obvious";
```
Reviewed By: anand1976
Differential Revision: D92013593
fbshipit-source-id: 0b4e00339bef3f76fc5b9ad35e2383c5e4f828f9
Summary:
I don't think this option was ever useful. There was no compressed secondary cache compatibility issue that needed to accommodate compression format version 1. It was needlessly imported from legacy SST file formats. Version 1 is simply an inefficient format because it requires guessing the uncompressed size on decompression.
And as far as I know, we don't have any plans to make compressed secondary cache entries persistable across RocksDB versions. I.e. if persisting, we would simply tag the persistence layer with the version (perhaps major and minor) and throw out the cache whenever that changes. Then we don't have to deal with explicit schema versioning in persistenct caches. This is a workable approach because unlike SSTs, caches are not source-of-truth that need to survive version rollback.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14302
Test Plan: existing tests
Reviewed By: anand1976
Differential Revision: D92315003
Pulled By: pdillinger
fbshipit-source-id: 0b82cfdbd92bcd2b8fbddd6586824f53c88069c4
The internal and external repositories are out of sync. This Pull Request attempts to brings them back in sync by patching the GitHub repository. Please carefully review this patch. You must disable ShipIt for your project in order to merge this pull request. DO NOT IMPORT this pull request. Instead, merge it directly on GitHub using the MERGE BUTTON. Re-enable ShipIt after merging.
fbshipit-source-id: 08b287a3f343f6ac5872c2a059d91d1bed9ff0a8
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14286
Add the db_c leanup target which can be used by CI test scripts to delete the db on failure. The db_crashtest.py doesn't automatically delete on error.
Reviewed By: jaykorean
Differential Revision: D91912877
fbshipit-source-id: d36ec0896fba64faaafe055d8673e437e85d0c3a
2026-02-03 12:22:09 -08:00
617 changed files with 83057 additions and 9718 deletions
// The workflow runs a recovery session when this happens, so this
// branch is typically only hit if recovery wasn't attempted (e.g.,
// no findings file was written). Extract what we can.
constpartial=getLastAssistantText(executionLog);
if(partial){
responseBody=
`⚠️ **Review incomplete — Claude hit the turn limit.**\n\nBelow is the last partial output. You can request a fresh review with \`/claude-review\`.\n\n---\n\n${
partial}`;
}else{
responseBody=
'⚠️ **Review incomplete — Claude hit the turn limit before producing output.** You can request a fresh review with `/claude-review`.';
This document provides guidance for generating and reviewing code in the RocksDB project, derived from analysis of code review feedback across hundreds of complex merged Pull Requests. Use this as a reference when writing code with AI assistants or conducting code reviews.
---
## General Best Practices
### Code Quality and Maintainability
**Clarity and Readability:** Write clear, self-documenting code. Use meaningful variable names, add comments for complex logic, and structure code to minimize cognitive load. Avoid clever tricks that sacrifice readability for marginal performance gains unless absolutely necessary.
**Consistent Style:** Follow existing code style conventions. RocksDB uses `.clang-format` for formatting, specific naming conventions, and structural patterns. Deviations from these patterns are frequently flagged in reviews.
**Error Handling:** Ensure robust error handling throughout the codebase. Use RocksDB's `Status` type consistently, propagate errors appropriately, and avoid silently ignoring failures. Reviewers pay close attention to edge cases and failure modes.
### Testing Philosophy
**Comprehensive Coverage:** Every change should include appropriate test coverage. This includes unit tests for isolated functionality, integration tests for component interactions, and stress tests for concurrency and performance validation. Reviewers will ask for additional tests if coverage is insufficient.
**Edge Cases and Failure Modes:** Tests should explicitly cover edge cases, boundary conditions, and potential failure scenarios. This is especially important for changes affecting core database operations, compaction, or recovery logic.
**Platform-Specific Testing:** RocksDB supports multiple platforms (Linux, Windows, macOS) and compilers (GCC, Clang, MSVC). Changes should be tested across relevant platforms, particularly when touching platform-specific code or using compiler-specific features.
### Performance Considerations
**⚠️ PERFORMANCE IS CRITICAL:** RocksDB is a high-performance storage engine where every CPU cycle and memory access matters. When writing code, always evaluate from a performance perspective. This is not optional—performance-aware coding is a fundamental requirement for all contributions.
**Benchmarking and Profiling:** Performance claims should be backed by empirical evidence. Use RocksDB's benchmarking tools (e.g., `db_bench`) to validate improvements. Reviewers will request benchmark results for changes that could impact performance.
**Memory Allocation:** Minimize dynamic memory allocations, especially in hot paths. Prefer stack allocation over heap allocation. Reuse buffers when possible. Consider using arena allocators or memory pools for frequent small allocations. Every `new`, `malloc`, or container resize has a cost.
**Memory Copy:** Avoid unnecessary memory copies. Use move semantics, `std::string_view`, `Slice`, and pass-by-reference where appropriate. Be aware of implicit copies in STL containers and function returns. Prefer in-place operations over copy-and-modify patterns.
**CPU Cache Efficiency:** Design data structures and access patterns to be cache-friendly. Keep frequently accessed data together (data locality). Prefer sequential memory access over random access. Be mindful of cache line sizes (typically 64 bytes) and avoid false sharing in concurrent code. Consider struct packing and field ordering to improve cache utilization.
**Loop Optimization:** Look for opportunities to collapse nested loops, reduce loop overhead, and minimize branch mispredictions. Hoist invariant computations out of loops. Consider loop unrolling for tight inner loops. Batch operations when possible to amortize per-operation overhead.
**SIMD and Vectorization:** Leverage SIMD instructions (SSE, AVX) for data-parallel operations when appropriate. Structure data to enable auto-vectorization by the compiler. Consider explicit SIMD intrinsics for critical hot paths like checksum computation, encoding/decoding, and bulk data processing.
**Branch Prediction:** Minimize unpredictable branches in hot paths. Use `LIKELY`/`UNLIKELY` macros to hint branch prediction. Consider branchless alternatives for simple conditionals. Order switch cases and if-else chains by frequency.
**Memory and Resource Management:** Be mindful of memory allocations, especially in hot paths. Use RAII patterns, smart pointers, and RocksDB's memory management utilities appropriately.
**Hot Path Analysis:** When deciding how aggressively to optimize code, consider whether it's on a hot path:
- **Hot path** (executed thousands+ times, e.g., data access, iteration, compaction loops): Performance is paramount. Apply all optimization techniques—loop collapsing, SIMD, cache optimization, pre-allocation, etc. The cost of each operation is multiplied by execution frequency.
- **Cold path** (executed rarely, e.g., DB open, configuration parsing, error handling): Maintainability and clarity are more important. Prefer readable code over micro-optimizations. Complex optimizations here add maintenance burden with negligible performance benefit.
- **Warm path** (moderate frequency): Balance both concerns. Use profiling data to guide optimization decisions.
**Avoid Premature Optimization:** While performance is critical, focus on correctness first, then optimize based on profiling data. However, be performance-aware from the start—choosing the right algorithm and data structure upfront is not premature optimization. Use the hot path analysis above to decide how much optimization effort is warranted.
### API Design and Compatibility
**Backwards Compatibility:** RocksDB maintains strong backwards compatibility guarantees. Breaking changes are rare and require extensive justification. When deprecating features, follow the project's deprecation policy (typically spanning multiple releases).
**API Consistency:** New APIs should be consistent with existing patterns. Use similar naming conventions, parameter ordering, and return types. Reviewers will suggest changes to improve consistency with the broader codebase.
**Documentation:** Public APIs must be thoroughly documented. Include usage examples, parameter descriptions, and notes on thread safety, performance characteristics, and compatibility considerations.
---
## Component-Specific Guidance
### Database Core (`db`)
The database core handles write-ahead logging (WAL), memtables, compaction, and recovery. This component receives the most scrutiny in code reviews.
**Concurrency and Thread Safety:** Database operations are highly concurrent. Reviewers carefully examine locking strategies, atomic operations, and memory ordering. Document synchronization assumptions clearly. Use appropriate memory ordering semantics (`acquire`/`release` vs. `seq_cst`).
**Compaction Logic:** Changes to compaction are complex and high-risk. Ensure that compaction logic respects configured parameters, handles edge cases (empty databases, single-file compactions), and maintains correctness under concurrent operations.
**Error Propagation:** Database operations can fail in many ways (I/O errors, corruption, resource exhaustion). Ensure that errors are properly propagated, logged, and handled. Avoid assertions in production code paths.
**Testing:** Database core changes require extensive testing, including unit tests, integration tests, and stress tests. Test with various configurations, compaction styles, and concurrent workloads.
### Public Headers (`include`)
Public headers define RocksDB's API surface. Changes here have the highest compatibility impact.
**API Design:** New APIs should be intuitive, consistent with existing patterns, and well-documented. Consider how the API will be used in practice and avoid adding unnecessary complexity.
**Backwards Compatibility:** Breaking changes to public APIs require extensive justification and a deprecation plan. Maintain ABI compatibility for bug fixes and patch releases.
**Documentation:** Every public API must be thoroughly documented with usage examples, parameter descriptions, and notes on thread safety and performance characteristics.
**Deprecation:** When deprecating APIs, follow the project's policy. Mark deprecated APIs clearly, provide migration guidance, and maintain support for at least one major release.
### Internal Utilities (`util`)
Internal utilities provide common functionality used throughout the codebase.
**Code Reuse:** Utilities should be general-purpose and reusable. Avoid duplicating functionality that already exists elsewhere in the codebase.
**Error Handling:** Utility functions should handle errors robustly and propagate them appropriately. Consider edge cases like overflow, underflow, and invalid inputs.
**Testing:** Utility functions should have comprehensive test coverage, including edge cases and failure modes. Consider adding death tests for assertions.
**Performance:** Utilities are often used in hot paths. Ensure that implementations are efficient and avoid unnecessary allocations or copies.
### Table Management (`table`)
Table management handles SST file format, block-based tables, and table readers/writers.
**Block Format and Checksums:** Changes to block format require extreme care. Ensure that checksums are computed and verified correctly. Test with various compression algorithms and block sizes.
**Iterator Correctness:** Table iterators are used throughout the codebase. Ensure that iterator semantics (Seek, Next, Prev) are correct, especially at boundaries and with deletions.
**Caching and Prefetching:** Table readers interact with the block cache and prefetching logic. Ensure that cache keys are unique and that prefetching respects configured limits.
**Performance:** Table operations are performance-critical. Benchmark changes that could impact read or write performance.
### Utilities (`utilities`)
Utilities include optional features like transactions, backup engine, and checkpoint.
**Feature Isolation:** Utilities should be self-contained and not introduce unnecessary dependencies on core database internals.
**Deprecation and Cleanup:** Legacy features are being phased out. When removing deprecated code, ensure that migration paths are documented and that users have sufficient warning.
**Cross-Platform Compatibility:** Utilities often interact with OS-specific APIs. Ensure that code works on all supported platforms.
### Options and Configuration (`options`)
Options define RocksDB's configuration system.
**Type Safety:** Use appropriate types for options (e.g., `uint32_t` for flags, scoped enums for enumerated values).
**Deprecation Policy:** When deprecating options, follow the project's policy. Document the deprecation, provide migration guidance, and maintain support for at least one major release.
**Dynamic Configuration:** Some options can be changed dynamically. Ensure that dynamic changes are thread-safe and take effect correctly.
**Validation:** Validate option values and provide clear error messages for invalid configurations.
### Cache (`cache`)
Cache management is critical for RocksDB's performance.
**Concurrency:** Cache operations are highly concurrent. Ensure that implementations are thread-safe and use appropriate synchronization primitives.
**Performance:** Cache operations are in the hot path. Optimize for low latency and high throughput. Benchmark changes carefully.
**Memory Management:** Cache implementations must manage memory carefully to avoid leaks and excessive allocations.
**Eviction Policies:** Changes to eviction policies should be well-tested and benchmarked to ensure they improve overall performance.
---
## Code Review Checklist
When reviewing RocksDB code (or preparing code for review), use this checklist:
### Correctness
- [ ] Does the change preserve database semantics (e.g., snapshot isolation, key ordering)?
- [ ] Are all error cases handled appropriately?
- [ ] Is the change thread-safe? Are synchronization primitives used correctly?
- [ ] Are there any potential data races or deadlocks?
### Testing
- [ ] Does the change include appropriate test coverage?
- [ ] Are edge cases and failure modes tested?
- [ ] Have the tests been run on all supported platforms?
- [ ] Are stress tests passing?
### Performance
- [ ] Are there benchmark results for performance-sensitive changes?
- [ ] Does the change avoid unnecessary allocations or copies?
- [ ] Are hot paths optimized appropriately?
### API and Compatibility
- [ ] Is the change backwards compatible?
- [ ] Are new APIs consistent with existing patterns?
- [ ] Is the public API documented?
- [ ] Are deprecated features handled according to policy?
### Code Quality
- [ ] Does the code follow RocksDB's style conventions?
- [ ] Is the code clear and maintainable?
- [ ] Are comments and documentation sufficient?
- [ ] Are there any code smells or anti-patterns?
---
## Common Review Feedback Patterns
The following patterns emerged as frequent sources of review feedback:
1.**Test Coverage:** Reviewers frequently request additional tests for edge cases, platform-specific behavior, and failure modes. Complex changes require comprehensive test coverage including unit tests, integration tests, and stress tests.
2.**Error Handling:** Ensure proper error propagation using RocksDB's `Status` type. Avoid silent failures and provide clear error messages that include context about what failed and why.
3.**API Design:** New APIs should be consistent with existing patterns. Use descriptive names that follow established conventions. Avoid breaking changes without strong justification and a clear deprecation plan.
4.**Documentation:** Public APIs must be documented with usage examples and notes on thread safety, performance characteristics, and compatibility considerations. Complex internal logic should also be well-commented.
5.**Performance:** Performance-sensitive changes require benchmark results to validate improvements. Use `db_bench` and other profiling tools to measure impact. Avoid premature optimization that adds complexity without measurable benefit.
6.**Concurrency:** Thread safety is critical in RocksDB. Document synchronization assumptions clearly. Use appropriate memory ordering semantics. Consider potential race conditions and deadlocks.
7.**Code Style:** Follow existing conventions for naming, formatting, and structure. Use `.clang-format` for consistent formatting. Prefer scoped enums (`enum class`) over unscoped enums.
8.**Backwards Compatibility:** RocksDB maintains strong compatibility guarantees. Breaking changes require extensive justification. When deprecating features, provide migration guidance and maintain support across multiple releases.
9.**Refactoring:** Reviewers appreciate refactoring that improves code readability and maintainability. Look for opportunities to deduplicate code and simplify complex logic.
10.**Platform Compatibility:** Ensure changes work correctly on all supported platforms (Linux, Windows, macOS) and with all supported compilers (GCC, Clang, MSVC).
---
## Important tips
### Build system
* There are 3 build system. Make, CMake, BUCK(meta internal).
* When a new .cc file is added, update Makefile, CMakeLists.txt, src.mk, BUCK.
* Don't manually edit BUCK file, after updating src.mk, run
/usr/local/bin/python3 buckifier/buckify_rocksdb.py to update it
* Use make to build and run the test. CMake and BUCK are not used locally.
* Use `make dbg` command to build all of the unit test in debug mode.
* For -j in make command, use the number of CPU cores to decide it.
### When to run `make clean` (avoid mixing build modes)
The Makefile does **not** track build mode, so object files from a prior
build are silently reused even when compiled with different flags, leading
to confusing linker errors, sanitizer false negatives, ODR violations, or
"phantom" bugs.
Run `make clean` before switching any of these:
***`ASSERT_STATUS_CHECKED=1` ↔ unset** — changes the `Status` class layout (ABI break).
***Sanitizer builds** — toggling any of `COMPILE_WITH_ASAN=1`,
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 11.3.0 (05/15/2026)
### New Features
* Add experimental DB option `async_wal_precreate` to precreate the next WAL file in a background thread and reduce foreground WAL rotation latency. The option is sanitized to false when WAL recycling is enabled.
* Added a new `EventListener::OnCompactionPreCommit` callback that fires after a compaction job finishes but before its input files are released (i.e. while `FileMetaData::being_compacted` is still true). Listeners that maintain bookkeeping of which files are currently being compacted can clean up such state in this new callback to avoid races with concurrent compaction picking, where another thread might pick up the same files for a new compaction immediately after `being_compacted` is flipped back to false but before `OnCompactionCompleted` fires. The default implementation is a no-op so this is not a breaking change.
* Add mutable DBOption `optimize_manifest_for_recovery` (default false). When enabled, RocksDB can reduce recovery work after a clean shutdown, which may lower DB::Open latency on warm reopens.
* Added public utility APIs `ParseCompressionNameForDisplay()` to convert `TableProperties::compression_name` into a human-readable compression name for both legacy and format_version 7+ SST metadata, including custom `CompressionManager`-provided display names for custom compression types.
* Add `reuse_manifest_on_open` DBOption (default false). When enabled, DB::Open reuses the existing MANIFEST file for append instead of creating a fresh one, avoiding the cost of serializing the entire database state into a new MANIFEST on the first post-open write. To prevent this feature from interfering with manifest file size auto-tuning, an extra forward-compatible field is now always added to the MANIFEST (to track the last "compacted" size).
### Behavior Changes
* Read-only open with `error_if_wal_file_exists=true` now tolerates empty WAL files so empty precreated WALs do not prevent inspection.
* WriteCommitted TransactionDB now matches WritePrepared and WriteUnprepared compaction filtering in both single- and two-write-queue modes: a compaction filter's FilterMergeOperand will not be invoked on merge operands at or below the latest published sequence number.
### Bug Fixes
* Fixed blob-backed wide-column merge reads to preserve correct status
propagation and resolution across memtable, read-only, and secondary DB
paths.
* Fixed a bug where `DB::GetCreationTimeOfOldestFile()` could return inaccurate results instead of the real creation time when called shortly after opening a legacy DB (one whose manifest lacks `file_creation_time`) with `open_files_async = true`. The API now waits for background SST file loading to complete only when needed; modern DBs are unaffected.
* Fixed merge reads against wide-column/blob-backed base values to preserve precise failure statuses, including `GetMergeOperands()` and direct-write memtable reads.
* Fix bug in range tombstone synthesis that covers live keys added during an IngestExternalFile
* Reject the empty string as a column family name in `DB::CreateColumnFamily` / `DB::CreateColumnFamilies`. Previously such calls returned OK and a usable handle, but the column family was not persisted in the manifest, so any data written to it was silently lost on DB reopen.
* Fixed a bug where a WriteCommitted TransactionDB using commit-bypass WBWI ingestion could drop an entry that is still visible at the published sequence boundary.
## 11.2.0 (04/18/2026)
### New Features
* Added experimental `DBOptions::fast_sst_open` option. When enabled, RocksDB retrieves opaque file system metadata for SST files after flush, compaction, and external file ingestion, persists it in the MANIFEST, and passes it back to the file system on subsequent file opens to accelerate DB open time.
* Added new option `min_tombstones_for_range_conversion` in `AdvancedColumnFamilyOptions`. When set to a non-zero value N, forward or reverse iteration will convert N or more contiguous point tombstones into a range tombstone in the mutable memtable. Future read operations will then be able to benefit from range tombstone optimizations. There are some limitations when it comes to table_filters, prefix_filters, and UDTs. See header comments for more details.
* Added read-triggered compaction: a new column family option `read_triggered_compaction_threshold` (default 0, disabled) that marks SST files for compaction when their read frequency (`num_collapsible_entry_reads_sampled / file_size`) exceeds the threshold. This helps reduce read amplification for frequently-read ("hot") keys. A new DB option `max_compaction_trigger_wakeup_seconds` (default 43200s / 12 hours) controls the maximum interval for periodic compaction score re-evaluation, which is necessary for this feature to work on quiet (no-write) databases.
### Public API Changes
* Added new `WideColumnBlobResolver` interface and `CompactionFilter::FilterV4()` method, including `ResolveColumn()` / `ResolveColumns()` helpers for lazy loading blob column values in wide-column entities during compaction. This allows compaction filters to resolve blob values on-demand, avoiding unnecessary I/O for blob columns they don't need to access.
* Changed experimental feature `ExternalTableReader::Get` and `ExternalTableReader::MultiGet` to use `PinnableSlice` instead of `std::string` for output values, enabling zero-copy pinning. This will break existing implementations.
* Added `SstFileReader::Get` and `SstFileReader::MultiGet` overloads that accept `PinnableSlice`/`std::vector<PinnableSlice>*`, enabling zero-copy reads when the underlying `TableReader` supports pinning.
### Behavior Changes
* Prefix filter changes - when seeking to a key that is out of domain, and total_order_seek is false, total_order_seek is treated as if it were true. When prefix_same_as_start = true, now iterating past a key that is out of domain invalidates the iterator. The existing behavior does not check for InDomain in the DBIter, so Transform() can produce undefined behavior (e.g. key of size 3 on FixedPrefixTransform(4)).
* Wide-column entities with blob-backed columns now use a new V2 on-disk encoding; older RocksDB versions that do not support wide-column blob separation will reject DBs or SSTs containing those entities.
### Bug Fixes
* Fix blob garbage accounting for blob direct-write flushes so flush-time filtering and overwrite elision correctly register obsolete blob bytes in blob metadata.
* Fix a memory accounting leak in IODispatcher where ReadIndex() moved block values out of ReadSet without releasing the associated prefetch memory, causing subsequent prefetches to be blocked when max_prefetch_memory_bytes was set.
* Fix MultiScan to fall back to synchronous coalesced reads when async I/O is unsupported at runtime.
## 11.1.0 (03/23/2026)
### New Features
* Add a new option `open_files_async`. The existing behavior is on DB open, we open all sst files and do basic validations. For very large DBs on remote filesystems with many ssts, this may take very long. This option performs these validations instead in the background. Open errors found by this async background task are surfaced as a new background error kAsyncFileOpen.
* Added `BlockBasedTableOptions::kAuto` index block search type that automatically selects between binary and interpolation search on a per-index-block basis. During SST construction, each index block's key distribution uniformity is analyzed using the coefficient of variation of key gaps, and index blocks with uniform keys use interpolation search while others fall back to binary search. The uniformity threshold is configurable via `BlockBasedTableOptions::uniform_cv_threshold` (default: 0.2).
* Introduced enforce_write_buffer_manager_during_recovery option to allow WriteBufferManager to be enforced during WAL recovery. (Default: true)
* Add `memtable_batch_lookup_optimization` option to use batch lookup optimization for memtable MultiGet. For skip list memtables, after each key lookup, the search path is cached and reused for the next key, reducing per-key cost from O(log N) to O(log d) where d is the distance between consecutive keys. Benchmarks show ~7% improvement in memtable-resident MultiGet throughput.
* Added `BlockBasedTableOptions::PrepopulateBlockCache::kFlushAndCompaction` to prepopulate the block cache during both flush and compaction. Compaction-warmed blocks are inserted at `BOTTOM` priority (vs `LOW` for flush) so they are evicted first under cache pressure. Recommended only for use cases where most or all of the database is expected to reside in cache (e.g., tiered or remote storage where the working set fits in cache).
* Added new mutable DB option `verify_manifest_content_on_close` (default: false). When enabled, on DB close the MANIFEST file is read back and all records are validated (CRC checksums and logical content). If corruption is detected, a fresh MANIFEST is written from in-memory state.
### Behavior Changes
* num_reads_sampled now factors in re-seeks and next/prev() on file iterators for files in L1+. next/prev() is discounted by 64x compared to seek due to being a much cheaper call.
* Remote compaction workers now skip WAL recovery when opening the secondary DB instance, since only the LSM state from MANIFEST is needed for compaction. This reduces I/O and speeds up remote compaction startup.
### Bug Fixes
* Fix a bug in round-robin compaction that missed selecting input files that are needed to guarantee data correctness and cause crashing in debug builds or silent data corruption in release builds for Get().
## 11.0.0 (02/23/2026)
### New Features
* Added support for storing wide-column entity column values in blob files. When `min_blob_size` is configured, large column values in wide-column entities will be stored in blob files, reducing SST file size and improving read performance.
* Added `CompactionOptionsFIFO::max_data_files_size` to support FIFO compaction trimming based on combined SST and blob file sizes. Added `CompactionOptionsFIFO::use_kv_ratio_compaction` to enable a capacity-derived intra-L0 compaction strategy optimized for BlobDB workloads, producing uniform-sized compacted files for predictable FIFO trimming.
* Include interpolation search as an alternative to binary search, which typically performs better when keys are uniformly distributed. This is exposed as a new table option `index_block_search_type`. The default is `binary_search`.
### Public API Changes
* Added new virtual methods `AbortAllCompactions()` and `ResumeAllCompactions()` to the `DB` class. Added new `Status::SubCode::kCompactionAborted` to indicate a compaction was aborted. Added `Status::IsCompactionAborted()` helper method to check if a status represents an aborted compaction.
* Drop support for reading (and writing) SST files using `BlockBasedTableOptions.format_version` < 2, which hasn't been the default format for about 10 years. An upgrade path is still possible with full compaction using a RocksDB version >= 4.6.0 and < 11.0.0 and then using the newer version.
* Remove deprecated raw `DB*` variants of `DB::Open` and related functions. Some other minor public APIs were updated as a result
* Remove deprecated DB option `skip_checking_sst_file_sizes_on_db_open`. The option was deprecated in 10.5.0 and has been a no-op since then. File size validation is now always performed in parallel during DB open.
* Remove deprecated `SliceTransform::InRange()` virtual method and the `in_range` callback parameter from `rocksdb_slicetransform_create()` in the C API. `InRange()` was never called by RocksDB and existed only for backward compatibility.
* Remove deprecated, unused APIs and options: `ReadOptions::managed` and `ColumnFamilyOptions::snap_refresh_nanos`. Corresponding C and Java APIs are also removed.
* Remove deprecated `SstFileWriter::Add()` method (use `Put()` instead) and the deprecated `skip_filters` parameter from `SstFileWriter` constructors (use `BlockBasedTableOptions::filter_policy` set to `nullptr` to skip filter generation instead).
### Behavior Changes
* Change the default value of `CompactionOptionsUniversal::reduce_file_locking` from `false` to `true` to improve write stall and reduce read regression
### Bug Fixes
* Fix longstanding failures that can arise from reading and/or compacting old DB dirs with range deletions (likely from version < 5.19.0) in many newer versions.
* Fix a bug where WritePrepared/WriteUnprepared TransactionDB with two_write_queues=true could experience "sequence number going backwards" corruption during recovery from a background error, due to allocated-but-not-published sequence numbers not being synced before creating new WAL files.
### Performance Improvements
* Add a new table option `separate_key_value_in_data_block`. When set to true keys and values will be stored separately in the data block, which can result in higher cpu cache hit rate and better compression. Works best with data blocks with sufficient restart intervals and large values. Previous versions of RocksDB will reject files written using this option.
## 10.11.0 (01/23/2026)
### Public API Changes
* New SetOptions API that allows setting options for multiple CFs, avoiding the need to reserialize OPTIONS file for each CF
- [ ] **KEEP** entry in type_info (`options/cf_options.cc` or `options/db_options.cc`) with `OptionVerificationType::kDeprecated` for loading old option files
- [ ] **KEEP** or add test in `options/options_test.cc``OptionsOldApiTest::GetOptionsFromMapTest` for loading old option files
### Documentation:
- [ ] Add release note to `HISTORY.md` and `unreleased_history/`
ROCKS_LOG_BUFFER(log_buffer,"[%s] FIFO compaction: no compaction picked",
cf_name.c_str());
}
RegisterCompaction(c);
returnc;
}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.