Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15048
Adds a validated db_stress flag for selecting the WriteBufferManager flush policy and randomizes it in db_crashtest. Crash-test generation covers all three policies; kFlushLargestAcrossDBs automatically uses two DBs sharing one manager so the cross-DB path is exercised.
Reviewed By: pdillinger
Differential Revision: D114638570
fbshipit-source-id: 00f664445614e083a4c59d4e2bb0896731d4bea7
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15343
Problem
The cross-DB WriteBufferManager coordinator dispatches ranked flush candidates asynchronously. A candidate can reject its handoff immediately—for example because its DB mutex is busy, writes are stopped, or equivalent WBM work is already scheduled—and finish before the sorter reaches its pressure wait.
Before this change, completion called RequestRefresh(), but the sorter’s active-pressure branch did not consume that already-recorded notification. Condition-variable notifications are not retained, so if completion won the race, the sorter still slept for the full 10 ms pressure interval before trying the next ranked DB. With many quickly rejected candidates, that delay accumulated once per candidate; 10,000 rejected DBs ahead of a healthy DB could theoretically delay useful relief by roughly 100 seconds while shared memory remained above its limit.
Solution
Snapshot the existing refresh-request generation immediately before ProcessFlushHandoffRequest(). After processing, if the handoff is already idle, the current ranked candidate cycle remains active, and the generation advanced, the sorter knows that asynchronous work completed during the dispatch window. It consumes that persistent notification and immediately continues to the next candidate instead of entering the pressure backoff.
The generation check is deliberately narrower than checking the refresh Boolean alone: it reacts only to notifications that occurred during this handoff attempt and avoids treating an older unrelated refresh as completion of the current candidate. The existing 10 ms backoff remains after a complete no-progress candidate cycle, and queued or executing handoffs retain their existing lease/wait behavior.
Testing
A deterministic SyncPoint test pauses the sorter only after a handoff has actually been submitted, lets a rejecting executor callback finish first, and verifies that the healthy next candidate is reached without entering the pressure-wait point. The synchronization wait is bounded so a broken ordering fails the test rather than hanging CI.
Reviewed By: xingbowang
Differential Revision: D123926506
fbshipit-source-id: b057e5b9a41ee0abd415f72ef2067b0d3f718384
Summary:
Finish OpenZL, the internal RocksDB source tree, and the released RocksDB copy, removing all three parent AutoHeaders opt-ins. Declare inferred headers while preserving aliases, globs, selectors, and visibility. RocksDB's generator lists library headers and passes test source headers through its template and shared wrapper. Header lists account for the source files compiled by each release, so the older released tree does not gain headers from newer internal test helpers.
Add `release_to_fbcode.py internal_repo_rocksdb --buck-only -y` to refresh the four Buck generator files and regenerate BUCK against the existing release's source lists. The command restores its five outputs if copying or generation fails, applies the existing generated-file warning, and rejects release-commit options. It does not copy runtime sources, change the release version, clone, commit, or deploy. Both RocksDB BUCK files are produced by the maintained generators.
Graft the OpenZL dev declarations into prod. Align the predictor's Folly include paths and dependencies with prod so promotion is reproducible. Add the IPNext controller transition header explicitly.
Authored with Codex.
Reviewed By: IanChilds
Differential Revision: D123482073
fbshipit-source-id: 05ab47e592d710834da56bbea57f5f93a0aea565
Summary:
The workflow_dispatch-only nightly-candidate.yml shared the exact `name: facebook/rocksdb/nightly` with the scheduled nightly.yml. The name collision confuses GitHub's "list workflow runs by file name" REST query, so release tooling querying actions/workflows/nightly.yml/runs got a stale/partial set missing recent scheduled runs. Rename it to facebook/rocksdb/nightly-candidate, matching the pr-jobs-candidate convention, so every workflow name is unique.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15345
Test Plan: rety the integration after fix
Reviewed By: xingbowang
Differential Revision: D123973936
Pulled By: pdillinger
fbshipit-source-id: cde1c238e6a91444f7112848081e161f54f5bf24
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15331
## Motivation
The parent diff already implements cross-DB flushable-memory accounting and a background sorter that ranks DBs outside the write path. This change does not alter how the best candidate is selected.
The remaining liveness problem occurs after the sorter selects a DB and hands it off for execution. Previously, the coordinator tracked one remote handoff as pending and waited for that DB to report completion before advancing. A selected DB could therefore monopolize coordinator progress if its job waited in Env::LOW, blocked on the DB mutex, waited behind an existing write leader or write stall, or otherwise failed to complete promptly. Other ranked DBs could be ready to reclaim memory but would not be attempted while the first handoff remained pending.
## Solution
Make cross-DB flush handoff execution bounded and bypassable.
Cross-DB attempts now run on a small four-worker executor owned by the WriteBufferManager instead of consuming Env::LOW compaction capacity. The executor is created only for kFlushLargestAcrossDBs and its workers are retired when that policy is disabled.
The coordinator distinguishes queued and executing handoffs. A candidate lease begins when an executor worker starts the callback, rather than while the callback is merely waiting in the executor queue. If an executing handoff exceeds its lease, the coordinator releases ownership of that attempt and advances to the next ranked candidate. The original callback is not cancelled and may still complete, so the executor and outstanding-handoff count provide a strict bound on concurrent stuck work.
Each registration has a per-DB outstanding-job reservation, preventing the sorter from dispatching the same DB again while an earlier callback remains alive. The coordinator also records the identity of the active candidate, so late completion from an expired attempt cannot clear or corrupt a newer candidate lease.
DB mutex acquisition, write-thread joining, memtable switching, and flush dispatch continue to happen outside registry and coordinator locks. The executor invokes the existing BackgroundCallWBMFlush path directly; actual immutable-memtable flushing still uses RocksDB normal flush infrastructure.
## Compatibility
This behavior is limited to kFlushLargestAcrossDBs. Existing policies retain their previous local flush behavior, and no WBM executor threads are started for them.
## Validation
Unit coverage exercises lease expiry, advancement to the next candidate, late completion, duplicate suppression, bounded worker capacity, policy changes with blocked callbacks, shutdown, and reranking after progress.
The end-to-end stack was also compared against the original local policy in one matched memory-pressure run with 1,000 DBs and 16 writers. The complete largest-across-DBs policy delivered 61.0% higher throughput, 55.5% fewer WBM flushes, 37.9% lower mean latency, 47.9% lower P99 latency, 5.17x larger average flushes, and 62.8% lower peak RSS. These measurements validate the full policy stack rather than isolating the performance contribution of this liveness follow-up.
Reviewed By: xingbowang
Differential Revision: D123461329
fbshipit-source-id: 4ad86102c2a43beb469f7672d722674dfbda9871
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15342
Add an ExternalTableBuilderBase::TryAdd() contract so external table builders can distinguish accepted rows, real builder errors, and clean requests to cut the current output table and retry the same row.
Add external-table-facing result, reason, and context types. Existing external table builders inherit a default TryAdd() implementation that forwards to Add(), so current implementations keep their historical behavior unless they explicitly opt into retryable file cuts.
Reviewed By: xingbowang
Differential Revision: D123930815
fbshipit-source-id: b5701c704938759db68b7c0bf0ebeba19d176945
Summary:
Motivation
A WriteBufferManager can be shared by many RocksDB instances and column families. Under the existing policy, the DB whose writer first observes memory pressure flushes its own oldest mutable memtable. That DB might own only a small fraction of the reclaimable memory, so workloads with many DBs or CFs can generate many small flushes while the largest memory consumer remains untouched. Scanning every registered DB while holding process-wide or DB mutexes would improve selection accuracy but would serialize unrelated write paths and is not viable for deployments with thousands of DBs.
Previous behavior
kFlushOldest was the only behavior: the write-group leader that noticed WBM pressure synchronously selected the oldest eligible mutable memtable in its own DB. The WBM had no per-DB view of reclaimable memory and could not direct reclamation to another DB sharing the same budget.
New behavior
This change adds configurable WriteBufferFlushPolicy modes:
- kFlushOldest preserves the existing behavior and remains the default.
- kFlushLargest selects the largest eligible flushable column family in the writing DB.
- kFlushLargestAcrossDBs selects the DB advertising the most reclaimable memory across all DBs sharing the WBM, then flushes its largest eligible CF. Atomic-flush DBs advertise and flush the aggregate eligible memory across their CFs.
High-level implementation
Each writable DB registers a lightweight FlushInitiator with the shared WBM. Per-DB accounting publishes two approximate values using atomics: total mutable memory and the largest flushable CF contribution, including eligible immutable memtables waiting for flush. Allocator reporting is batched, so the allocation hot path avoids registry scans and DB mutexes.
A dedicated WBM coordinator thread maintains an approximate ranked snapshot of registered DBs. Refreshes are driven by accounting changes, threshold crossings, and periodic pressure checks. Ranking and O(number of DBs) work happen on that coordinator thread; writers only update counters and signal the coordinator. Registration lifetime is protected independently so DB close can safely drain an in-flight handoff.
Threading and flush handoff
The coordinator reserves and pins one ranked candidate at a time, without holding a registry lock while invoking the DB. The target DB uses TryLock on its DB mutex and rejects the handoff if it is closing, paused, stopped, stalled, already handling a WBM job, or no longer has flushable memory. Rejected candidates do not block consideration of the next DB.
An accepted handoff queues one LOW-priority WBM job. The LOW worker joins the target DB write thread and revalidates eligibility before switching the memtable. The resulting ordinary flush is then scheduled through RocksDB's existing flush machinery on the HIGH-priority flush pool. Keeping the write-thread join off the requesting writer avoids making one DB's write path wait on another DB's mutex or write queue.
Only one manager-wide remote handoff is active at a time. Completion or cancellation clears the handoff and requests a rerank. If memory reaches the hard limit while remote coordination is delayed, a rate-limited local fallback lets a writer seal a local memtable to preserve forward progress. Write stalls, shutdown, WaitForCompact, policy changes, and DB deregistration explicitly drain or reject outstanding handoffs.
Compatibility
The legacy constructor and kFlushOldest behavior remain unchanged unless a new policy is explicitly selected. There are no on-disk format changes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15047
Reviewed By: xingbowang
Differential Revision: D112396106
fbshipit-source-id: 3049adc7034dd4e00d74d7d86e1d19004292d299
Summary:
Follow-up to https://github.com/facebook/rocksdb/issues/15304, which renamed `TableProperties::creation_time` to `oldest_ancestor_time` but left the long-standing "ancester" misspelling everywhere else, so the same concept was spelled two ways (e.g. `TableProperties::oldest_ancestor_time` vs. `FileMetaData::oldest_ancester_time` and `SstFileMetaData::oldest_ancester_time`). This change makes the spelling consistent and addresses remaining review feedback on https://github.com/facebook/rocksdb/issues/15304.
Change:
* All internal identifiers and comments now use "ancestor": `FileMetaData::oldest_ancestor_time`, `CompactionServiceOutputFile::oldest_ancestor_time`, `kUnknownOldestAncestorTime`, `TryGetOldestAncestorTime()`, `Compaction::MinInputFileOldestAncestorTime()`, the `kOldestAncestorTime` manifest tag enum, and related locals and test sync point names.
* Public API: `SstFileMetaData::oldest_ancestor_time` is the new name; `oldest_ancester_time` remains as a deprecated alias sharing storage via an anonymous union (same approach as `TableProperties::creation_time`, so size, layout, and copy semantics are unchanged).
* Public API: `TablePropertiesNames::kOldestAncestorTime` is the new name; `kCreationTime` remains as a deprecated `static const std::string&` alias. A static reference member has no per-object cost and binds at constant initialization, so it adds no static-init-order exposure.
* `TablePropertiesNames` as a whole is marked DEPRECATED for public use. An fbsource-wide BigGrep found no references outside RocksDB and its vendored copies, and it is only meaningful to table format internals; users should read the corresponding `TableProperties` fields.
Fix:
* The `table_file_creation` event log entry had the `oldest_key_time` and `newest_key_time` values swapped since https://github.com/facebook/rocksdb/issues/13083 (v10.0). Now each label is paired with its own value.
Also: tools/c_api_gen/auto_simple_bindings.py resets the pending anonymous union field list after use and skips unnamed members inside the union (review suggestion); generated C API output is unchanged.
Compatibility: names that are persisted or externally parsed intentionally keep the old spelling: the on-disk property name `rocksdb.creation.time`, the `"oldest_ancester_time"` key in remote compaction serialization, and the `oldest_ancester_time` / `OldestAncesterTime` labels in `VersionEdit` DebugString/DebugJSON (ldb manifest_dump output). The manifest tag value is unchanged. HISTORY.md is left as historical record.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15335
Test Plan: DBTablePropertiesTest.CreationTimeAliasesOldestAncestorTime now also verifies `TablePropertiesNames::kCreationTime` and `kOldestAncestorTime` refer to the same object, and new DBTablePropertiesTest.SstFileMetaDataOldestAncesterTimeAlias verifies the two `SstFileMetaData` names share an address and that writes through the old name are visible through the new one. Remote compaction serialization through the unchanged `"oldest_ancester_time"` key is covered by compaction_service_test; manifest encode/decode through the renamed field by version_edit_test. `make check-c-api-gen` confirms the generated C API is up to date and backward compatible.
Reviewed By: jaykorean
Differential Revision: D123559299
Pulled By: pdillinger
fbshipit-source-id: 480106e8d539e654f31db210ef6e1de02325d604
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15334
External table formats cannot currently persist range tombstones, so SST ingestion and full-mode DB flushes or compactions fail when they contain `DeleteRange` entries. This change adds an opt-in contract for storing RocksDB’s canonical range-deletion block. RocksDB retains responsibility for sequence-number handling, fragmentation, and visibility through its native range-tombstone iterators.
Existing factories remain unsupported by default, and existing files without range tombstones are unaffected. Missing or invalid advertised tombstone blocks are treated as corruption. RocksDB also supplies logical entry counts and sizes when a format does not persist the properties block, while preserving format-specific properties.
Reviewed By: xingbowang
Differential Revision: D123475827
fbshipit-source-id: de8fec401bdc311282f5d4a48b248875c682f7c9
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15321
`MultiGet` with `read_tier = kBlockCacheTier` now returns `Incomplete` for data blocks that are not in the block cache. Before this fix, it read them from disk.
**Why it matters:** Callers use `kBlockCacheTier` to prevent disk I/O. `Get` obeyed it, but `MultiGet` did not.
**Problem**
- Without mmap reads, `MultiGet` sends cache misses to `RetrieveMultipleBlocks`.
- That path called `MultiRead` with no `kBlockCacheTier` check.
- `RetrieveBlock` (used by `Get`) has the check.
**Fix**
- `RetrieveMultipleBlocks`: if `read_tier == kBlockCacheTier`, set `Incomplete("no blocking io")` for each block and do no I/O.
- `MultiGet`: when a block is `Incomplete`, return that status for the key, the same as `Get`. Before, the key status stayed `OK` with no value.
- Release note in `unreleased_history/bug_fixes`.
**Tests**
- New `DBTest2.MultiGetBlockCacheTierSkipsColdSstData`: `Get` and `MultiGet` both return `Incomplete` for a key whose data block is not in the cache.
- `DBBlobBasicTest.MultiGetBlobsFromCache` relied on the bug. Its inline key passed only because `MultiGet` read the data block from disk. The test now loads the block cache first, so it still checks the blob cache.
Reviewed By: joshkang97
Differential Revision: D122431823
fbshipit-source-id: 262913930cb4b570ea0a4d8f4f92364de08a27f5
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15320
Why the change is required:
`DB::ClipColumnFamily(cf, begin_key, end_key)` keeps `[begin_key, end_key)`. It first drops whole non-L0 files outside the range with `DeleteFilesInRanges` on `(-inf, begin_key)` and `[end_key, +inf)`, but called it with the default `include_end = true`. The first range was therefore treated as `(-inf, begin_key]`, so a non-L0 file whose largest key is `begin_key` was deleted even though that key is inside the kept range. Clipping `[b, d)` on L1 files `[a, b]` and `[c, d]` lost `b`.
This showed up while smoke-testing `SplitDB` (D120757697): single-key files trivially moved to the bottom level made the split destination miss its first key.
New behavior:
- Pass `include_end = false`, so `DeleteFilesInRanges` skips files whose largest key equals `begin_key`. The second range has no end, so it is unaffected. The remaining keys below `begin_key` are still removed by the existing `DeleteRange` and compaction steps.
Reviewed By: xingbowang
Differential Revision: D123306376
fbshipit-source-id: 65cb36ae730a55750ea558a6cdcd48d8fc17f0e9
Summary:
The `creation_time` table property has long stored the file's oldest ancestor time (the oldest key time among the file's ancestors, falling back to flush/compaction time), not the file's creation time, which is `file_creation_time`. A TODO in table_properties.h acknowledged the misleading name. This change starts the API migration: `TableProperties::oldest_ancestor_time` is the new name, and `creation_time` remains as a deprecated alias sharing storage via an anonymous union, so existing external reads and writes continue to compile and behave identically.
An anonymous union is used rather than a `uint64_t&` alias member. A reference member would delete TableProperties copy/move assignment, make the implicit copy constructor bind the copy's alias to the source object (dangling), permit writes through a const TableProperties, add 8 bytes, and break the uint64_t-range walk in TEST_SetRandomTableProperties. The union leaves size, layout, copying, constness, and offsetof unchanged. Reading the other union member is formally UB in ISO C++, but both members are uint64_t and this is supported in practice by GCC, Clang, and MSVC.
All in-repo uses of the old name now use `oldest_ancestor_time`. Names that are externally observable or persisted are intentionally unchanged:
* On-disk property name `rocksdb.creation.time` (`TablePropertiesNames::kCreationTime`)
* The `"creation_time"` key in TableProperties serialization (used by remote compaction)
* The event-log JSON key and `TableProperties::ToString()` label
* Java `TableProperties.getCreationTime()` and C `rocksdb_table_properties_creation_time()`
C API: the auto-binding generator (tools/c_api_gen/auto_simple_bindings.py) only considered direct FieldDecls and failed on the anonymous union's implicit unnamed field. It now treats anonymous union members as direct fields. Regenerating adds a new C getter
`rocksdb_table_properties_oldest_ancestor_time()` alongside the existing `rocksdb_table_properties_creation_time()`.
Follow-up: remove the deprecated `creation_time` alias in a future release.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15304
Test Plan: New unit test DBTablePropertiesTest.CreationTimeAliasesOldestAncestorTime verifies the two names share an address and that a write through the old name is visible through the new one. options_settable_test and compaction_service_test cover TableProperties serialization round trips through the unchanged "creation_time" key, now mapped to the renamed field.
Reviewed By: jaykorean
Differential Revision: D123079083
Pulled By: pdillinger
fbshipit-source-id: 18b07d91f303a8f6de58da0c730f80d8c371505c
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15287
`IngestExternalFileOptions::write_global_seqno` has been deprecated since RocksDB 8.0 and will soon be removed from the API. It requires ingestion to mutate SST files, sync them, regenerate checksums, and expose file-offset plumbing solely for that mutation.
Remove the option and its C, Java, and `ldb` APIs. Assigned sequence numbers now live only in the MANIFEST, leaving newly ingested SST bytes and whole-file checksums unchanged.
## Backward Compatibility
New readers continue to support existing SSTs containing a nonzero global sequence number written by legacy ingestion. Because that old write did not update the properties-block checksum, checksum verification retries against a temporary copy with only the global-seqno value restored to its original zero placeholder. The parsed sequence number and file contents remain unchanged.
This fallback only accepts mismatches explained by the legacy rewrite. Other checksum failures continue through normal corruption handling, and `verify_checksums=false` continues to bypass verification.
Reviewed By: xingbowang
Differential Revision: D122405856
fbshipit-source-id: 934e9bc755d58ea8d84e1c085e9769015bd8dfd7
Summary:
CONTEXT: The AI review workflow had generic concurrency and performance checks, but it did not require reviewers to determine whether changed code or transitive callees execute while DBImpl::mutex_ is held. Reviews could therefore miss added contention, blocking work, lifetime errors, or snapshot and lock-ordering violations.
PREVIOUS: Review agents could examine DB-mutex changes through general correctness and performance lenses, but there was no explicit call-path audit, before/after mutex-held work comparison, or evidence requirement for regressions.
NEW: Every non-trivial review must state whether the DB mutex is affected. When it is affected, reviewers must inspect lock boundaries and correctness invariants, compare CPU and I/O before and after the change, keep unavoidable locked work highly optimized, and require measurements for material regressions.
IMPLEMENTATION: Add a DB Mutex Review Perspective covering locking contracts, object lifetime, snapshot boundaries, lock ordering, hidden callee costs, blocking work, validation, and performance evidence. Assign its checks to the correctness and performance reviewers and add required context and review checklist entries.
SAFETY: This changes only the AI code-review instructions. It does not change RocksDB runtime behavior, APIs, file formats, or compatibility. Changes that cannot execute under the DB mutex only need to state that the mutex is not affected.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15324
Test Plan:
- `git show --check HEAD`
- `git diff --check upstream/main...2026_10_05_main`
- Documentation-only change; no runtime tests were run.
Reviewed By: joshkang97
Differential Revision: D123415306
Pulled By: xingbowang
fbshipit-source-id: 0f78e0b1d677edf003cd5dc6ed5ff8123aff4f98
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15285
Why the change is required:
MyRocks bulk-load v2 creates a temporary RocksDB and calls DestroyDB when the load is committed, rolled back, abandoned, or recovered. Successfully ingested SSTs are moved into the main DB, so leftover SST cleanup is primarily an error, abort, or recovery case; WALs, obsolete files, and compaction-created trash can still remain. All physical deletion performed by DestroyDB should honor the configured SstFileManager rate, including rocksdb_sst_mgr_rate_bytes_per_sec in MyRocks.
Previous behavior:
DestroyDB sent recognized table, blob, and WAL files through SstFileManager, but ignored filenames ending in .trash because ParseFileName rejects that suffix. The final best-effort DeleteDir then silently left the directory behind. DB::Open handled the same files through CleanupDirectory, forcing MyRocks cleanup code to discover and register trash itself. A global WaitForEmptyTrash workaround would also drain unrelated compaction, DROP TABLE, and DROP INDEX deletion work from every DB sharing the manager.
New behavior:
DestroyDB waits only for pending deletion work associated with each DB, data, WAL, and archive directory. It then schedules any remaining .trash file through the existing rate-limited scheduler and waits only for the private bucket created for this DestroyDB call. Directory keys are normalized for bookkeeping, so equivalent path spellings identify the same pending work while filesystem operations retain the original path.
Accounting and failure handling:
A failed accounted trash deletion retains its original file path and remaining trash-byte charge. If DB open or DestroyDB later discovers the file, the retry preserves the accounted classification, avoids charging the trash bytes twice, and clears SstFileManager tracked-size and trash-size accounting after successful deletion.
Scope and safety:
There is no new sysvar, public RocksDB API, or persisted-format change. Existing rate settings, hard-link handling, partial deletion, and separate-WAL foreground deletion remain unchanged. The bookkeeping is confined to file enqueue/completion and DestroyDB cleanup. A shared FIFO can still delay this DB behind unrelated work already ahead of its files, but unrelated work queued behind this DB no longer gates DestroyDB.
Reviewed By: xingbowang
Differential Revision: D122221503
fbshipit-source-id: ec106f57efa70e24827234b4173366e5dbcbda5c
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15293
Why the change is required:
Full background-error recovery flushes all column families because WAL durability can be uncertain. A later recovery-flush failure or process crash must not persist different WAL replay boundaries for different column families. Otherwise reopen can recover only part of a write batch spanning those column families.
The risk includes non-file-scoped WAL write or sync errors. A failed write can have reached the WAL without reaching the memtables. An earlier unsynced write can instead remain in the memtables while its WAL bytes are not durable; retrying the fsync after resetting the writer error does not by itself prove those bytes survived. Sequential recovery can make either case visible in only some column families after reopen.
Previous behavior:
File-scoped WAL-error recovery already set FlushOptions::force_atomic_flush. Other full kErrorRecovery paths flushed column families sequentially when DBOptions::atomic_flush was false. Each successful per-column-family flush independently advanced its MANIFEST log number, so a later flush failure or process crash could leave mismatched replay boundaries.
New behavior:
Every full kErrorRecovery atomically commits the flush results and WAL replay boundaries for all participating column families. A successful recovery commits every column family; a failed recovery commits none. If an atomic recovery attempt fails, all of its flush output is discarded and recreated by a later attempt instead of retaining output from column families that finished first.
Implementation:
Set FlushOptions::force_atomic_flush for every full error-recovery FlushAllColumnFamilies call. Remove the now-unreachable recovery-only behavior from FlushMemTable and assert that recovery does not enter the non-atomic path. Keep the two-write-queue sequence fence at the atomic memtable-switch point.
Add an end-to-end fault-injection test for a retryable, non-file-scoped WAL sync error after a multi-column-family record is appended. It covers successful and failed recovery flushes, including a column family with no other memtable data, and verifies all-or-none behavior after reopen. Update the manual_wal_flush comment and add a release note.
Safety, compatibility, and limitations:
Only full kErrorRecovery changes. Normal, manual, shutdown, and kErrorRecoveryRetryFlush behavior is unchanged. File-scoped WAL-error recovery was already atomic. There are no API, ABI, option-default, or file-format changes.
The additional work is limited to the cold recovery path. A failed atomic recovery can redo more flush work than the previous sequential path, but this avoids partial multi-column-family recovery. Automatic recovery with manual_wal_flush remains disabled as fatal pending separate analysis and testing; this change does not remove that workaround. Validation covers local Linux builds and focused recovery tests; platform CI for this version is pending.
Reviewed By: pdillinger, rban1
Differential Revision: D122469648
fbshipit-source-id: f72845903d9b6bff70f5d881e02a4cb2e920ed72
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15294
Why the change is required:
`open_files_async` reduces startup latency by moving SST validation and table-reader opening to background work, but `DB::OpenAsSecondary()` still performed that work synchronously.
Previous behavior:
A secondary using `open_files_async=true` replayed its MANIFEST through `ReactiveVersionSet`, synchronously verified and opened every current SST, and only then returned. The option therefore provided no startup benefit for secondary instances.
New behavior:
`DB::OpenAsSecondary()` can now return after installing its initial read view and start initial SST validation and table-reader pinning through the existing asynchronous opener. With `max_open_files=-1`, the opener processes all current SSTs in the background; a finite table cache can stop the eager pass at capacity and validate remaining files on demand. Later `TryCatchUpWithPrimary()` rounds remain synchronous. Async-open failures notify listeners and are latched according to the existing severity and `paranoid_checks` policy. If an error is not latched, a later read retries the file access and reports a persistent failure. A latched error requires fixing the underlying problem and reopening the secondary. Read-only instances do not start write-oriented recovery.
Implementation:
Initial reactive MANIFEST replay can defer SST verification and handler loading. The deferral is immutable constructor state on `ReactiveVersionSet`, is cleared before catch-up mode, and is restricted to normal secondary recovery. `VersionBuilder::GetAndClearIntermediateFiles()` now moves out and clears its own historical add-then-delete bookkeeping so callers cannot accidentally retain stale state. Secondary open schedules the background opener after publishing the read view. Its context pins the captured Versions and column families, including retained secondary handles whose column family was subsequently dropped by the primary. Existing table-reader opening performs SST size and unique-ID validation, avoiding a separate serial metadata pass. Closing the DB or canceling background work safely cancels a queued opener or waits for a running opener. Read-only async-open errors use an ErrorHandler path that notifies listeners without automatic recovery. Secondary async open explicitly rejects FIFO compaction.
Safety, compatibility, and limitations:
The default `open_files_async=false` path and existing primary async-open behavior are unchanged. WAL and blob recovery, as well as later secondary catch-up, remain synchronous. Follower DB and `OpenAndCompact()` do not use this deferral. There are no file-format, ABI, or public API signature changes. Secondary async open requires `skip_stats_update_on_db_open=true` and does not support FIFO compaction. Applications must keep files accessible by coordinating deletion with the primary or by using an Env/FileSystem that delays physical deletion until every instance releases a file. With asynchronous opening, `max_open_files=-1` cannot guarantee every SST is pinned before `OpenAsSecondary()` returns; the table-files-only `max_open_files=-1` workaround applies only when asynchronous opening is disabled. No completion API is added.
Validation covers initial deferral and pinning, synchronous catch-up, table-reader detection of physical-size mismatch, FIFO rejection, listener-adjusted errors and synchronous read retry, retained handles after primary column-family deletion, close and explicit cancellation with no HIGH-priority worker, stopping new table opens during shutdown, primary async open, follower isolation, and remote compaction. Linux Buck builds and focused tests passed. Cross-platform CI is pending, and `make check-sources` cannot run successfully in this Sapling checkout because its script requires Git.
Reviewed By: pdillinger
Differential Revision: D122417547
fbshipit-source-id: 91d408469c35c4ea21165c5e7db75068e65c2389
Summary:
The persistent injected-error log writes `InjectedErrorLog::Entry` objects using their native in-memory layout, while the log format's consumer expects little-endian multi-byte fields. The existing `static_assert(port::kLittleEndian)` protected that format by rejecting every big-endian build that included the fault-injection filesystem, even though the log is an optional diagnostic.
This change keeps RocksDB and the fault-injection filesystem available on supported big-endian platforms while disabling only the incompatible persistent log. Log-file creation now returns an inactive descriptor when `port::kLittleEndian` is false, which makes `Record()` and `Flush()` remain safe no-ops. The fixed 256-byte entry-size assertion remains in place, and format-dependent tests are bypassed on big-endian targets.
The change also adds a release note and documents big-endian support plus the preferred capability-check pattern for optional architecture-specific features.
## Compatibility and limitations
- Little-endian behavior and the on-disk log layout are unchanged.
- On big-endian platforms, configuring an injected-error log path does not create or populate a log file; the rest of fault injection remains available.
- Windows already leaves this POSIX-backed diagnostic disabled.
- This does not introduce a portable log encoding. Supporting persistent injected-error logs on big-endian platforms would require a format change.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15297
Test Plan:
- `build_tools/rockstest.sh fault_injection_fs_test '--gtest_filter=InjectedErrorLogTest.*'` (little-endian macOS): all 5 tests passed.
- `make check-sources`
- `git diff --check upstream/main...2026_10_01_endian_fix`
Native big-endian build and test execution were not available locally; that path relies on the compile-time `port::kLittleEndian` capability and should be covered by big-endian CI.
Reviewed By: jaykorean
Differential Revision: D122880760
Pulled By: xingbowang
fbshipit-source-id: 59dae9661a47608aa12f41078ad1b3ef0fa7231e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15269
Why the change is required:
Transaction entity reads must enforce the same commit visibility and read-your-own-write rules as value-only reads. Without the transaction-specific read callback, GetEntity-family APIs can return a value that is not visible to the caller.
Previous behavior:
TransactionBaseImpl handled GetEntity and MultiGetEntity through non-virtual helpers that called WriteBatchWithIndex without a ReadCallback. WritePrepared transactions could expose a prepared but uncommitted update. WriteUnprepared transactions could miss their own write after it had been flushed as an unprepared batch, although that write remained hidden from other transactions.
New behavior:
GetEntity, MultiGetEntity, and GetEntityForUpdate now follow the same snapshot visibility and read-your-own-write semantics as Get and MultiGet for WritePrepared and WriteUnprepared transactions.
Implementation:
Make the internal entity-read helper hooks virtual. Override them in WritePreparedTxn and WriteUnpreparedTxn to construct the existing policy-specific ReadCallback and pass it through WriteBatchWithIndex. The WritePrepared batched path validates the callback refreshed read sequence so reads without an explicit snapshot do not bypass eviction validation. Invalid callback or snapshot state returns TryAgain. Add shared transaction entity-read assertions and parameterized regression coverage for prepared visibility and unprepared read-your-own-write.
Safety boundaries and unsupported cases:
There is no public API or persisted-format change. Other transaction policies keep the existing base implementation. The change adds only the callback and validation already used by value-only reads. Validation covers synchronous snapshot and non-snapshot entity reads, prepared visibility, and unprepared read-your-own-write in normal and checked-Status builds. Coroutine-specific and named wide-column payload cases were not separately tested, and the full repository suite was not run.
Reviewed By: rban1
Differential Revision: D121793013
fbshipit-source-id: e300642ed360b640814056e4d95cced5f5c180d8
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15274
Expose `SerializePreparedFileInfo()` and `DeserializePreparedFileInfo()` so opaque metadata produced by SST writers or DBs can be persisted and transferred across process boundaries. The representation uses RocksDB `OptionTypeInfo` adapters for `InternalKey` and `TableProperties` and includes a CRC32C integrity check.
Deserialization now rejects malformed nested string-map entries, including missing key/value separators and invalid hex, with `InvalidArgument` instead of asserting in debug builds or accepting malformed input in release builds. Valid serialized data and the wire format are unchanged. The `PreparedFileInfo` type metadata is also initialized on first use to avoid throwable namespace-scope initialization.
Reviewed By: xingbowang
Differential Revision: D121897852
fbshipit-source-id: 59a3d221bfd20e6c257bd60477823a12d05a35c5
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15296
**Why:** trace replay (`DB::NewDefaultReplayer()`, db_bench `replay`) cannot replay a trace of a DB with only the default column family, and multi-threaded replay reports success when records fail. Together these make db_bench replay look successful while replaying no reads.
**Previous behavior:**
- `TraceExecutionHandler` resolves a record's column family ID only against the `handles` passed to `NewDefaultReplayer()`. Records on a column family missing from `handles`, including the default one, fail with `Corruption: Invalid Column Family ID.`. An empty `handles` also trips `assert(!handles.empty())` in debug builds.
- db_bench fills its handle list (`db_.cfh`) only when `--num_column_families` > 1, so with the default of 1 it passes an empty list. Every Get, MultiGet and iterator record fails. Write records are not affected, because `DB::Write()` resolves the column family from the batch itself.
- In multi-threaded `Replay()` (`num_threads` > 1), `BackgroundWork()` sends only decode errors to the error callback. Execution errors are dropped, so `Replay()` returns OK. Single-threaded replay stops and returns the error, including a NotSupported from execution (for example `DB::Write()` of a DeleteRange with `row_cache` set). It skips only records whose trace type it cannot decode.
- Result in db_bench on a single column family DB: `--trace_replay_threads=1` prints `Replay failed. Error: Corruption: Invalid Column Family ID.`. `--trace_replay_threads=4` prints `Replay completed` with `rocksdb.number.keys.read` = 0.
**New behavior:**
- The replay handler always includes the DB's default column family, so `handles` may be empty or leave it out. A handle the caller passes for a column family is still the one used.
- Multi-threaded `Replay()` reports execution errors through the same callback as decode errors. It stops scheduling new records and returns the error of the failed record with the earliest trace timestamp, as the existing code already does for decode errors. Get records that find no key still count as success, since the handler treats NotFound as OK.
- As in single-threaded replay, NotSupported is skipped only when decoding returns it (a trace type the replayer does not support). NotSupported from executing a record stops the replay.
- The scheduling loop no longer reads the shared status `bg_s` while worker threads write it. It already did so for decode errors; with execution errors reported, TSAN flags it in the new test (see Test Plan).
**Implementation:**
- `TraceExecutionHandler` constructor: drop the non-empty assert; after the caller's handles, insert `db->DefaultColumnFamily()`. `unordered_map::insert` does not overwrite, so a caller's handle wins.
- `TraceExecutionHandler` constructor: skip the default column family if `DefaultColumnFamily()` returns nullptr (some test `DB` mocks do), so such DBs behave as before.
- `ReplayerImpl::BackgroundWork()`: call `error_cb` when `Accept()` returns an error. On a decode error, skip `error_cb` for NotSupported, matching single-threaded replay.
- `ReplayerImpl::Replay()`: `error_cb` no longer filters out NotSupported (the decode path now does that); it still keeps the error of the earliest record.
- `ReplayerImpl::Replay()`: `error_cb` also sets an `std::atomic<bool> bg_failed` while holding `mtx`, and the scheduling loop checks that flag instead of `bg_s.ok()`. `bg_s` is read only after `WaitForJobsAndJoinAllThreads()`.
- Document both contracts in `db.h` (`NewDefaultReplayer`), `trace_record.h` (`NewExecutionHandler`) and `replayer.h` (`Replay`).
- Release notes in `unreleased_history/bug_fixes/`.
**Safety, compatibility, limitations:**
- No API or ABI change.
- Callers that already pass the default column family handle see no change from the first fix.
- Behavior change: multi-threaded replay callers that got OK while records failed now get the error, including NotSupported from execution. That matches single-threaded replay and the new `Replay()` comment.
- A record read by the scheduling loop while a failing worker is setting `bg_failed` may still be scheduled. This is allowed by the `Replay()` comment (records already scheduled still run) and does not change the returned status, which is that of the earliest failed record.
- Cost: one extra map entry per replayer, and one branch per record in threaded replay. The loop's `bg_s.ok()` read becomes a relaxed atomic load.
- Read records on column families that are neither passed nor the default still fail with Corruption, as before. Write records never used `handles` and are unchanged.
Reviewed By: xingbowang
Differential Revision: D122626982
fbshipit-source-id: 9a0973448710bac25233feedbaf264696ac85035
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15291
Add experimental coroutine interfaces for implementing asynchronous basic and full `ExternalTableReader`s. Implementations opt in through `CoroExternalTableReaderBase<Mode>` by providing `GetCoroutine()` and may override `MultiGetCoroutine()`; the default implementation runs the individual Get operations concurrently. The external-table adapter uses these APIs when supported and otherwise retains the existing synchronous behavior.
As a smaller supporting fix, Single Get coroutine reads using non-mmap, non-direct I/O on a kFSBuffer-capable `FileSystem` now use `FSRandomAccessFile::SubmitReadAsync()` instead of falling back to synchronous `Read()`.
Reviewed By: xingbowang
Differential Revision: D122187491
fbshipit-source-id: 217e2953756938bbd3f30358a997a7ca3ed5543e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15268
Why this is required: remote file systems need an immutable blob-file checksum and checksum-function name when RocksDB opens a finalized blob file. This lets the file system distinguish file generations and safely validate or key cached extents.
Previous behavior: integrated BlobDB opened normal read-path blob files with the column family's ordinary FileOptions. Although finalized checksum metadata was stored in BlobFileMetaData and BlobFileAddition, it was not propagated through point reads, range reads, MultiGet, retry opens, or recovery verification. Delayed reads of direct-write files could also cache a finalized file reader opened without metadata.
New behavior: every finalized integrated BlobDB file open receives its checksum value and checksum-function name through FileOptions. Active direct-write files continue to use empty checksum fields until finalization. Delayed reads pinned to a pre-flush SuperVersion use the finalized metadata once the file has been sealed.
Implementation: add internal BlobFileOpenInfo transport across Version, BlobSource, and BlobFileCache. Populate it from version metadata and recovery additions. At the actual cache miss/open, copy the base FileOptions and set both checksum fields. Direct-write sealing registers owned checksum metadata with BlobFileCache, atomically evicts footer-less readers, and prevents a racing pre-seal refresh from repopulating the cache without metadata. Metadata is released after write-path ownership and old-memtable protection end.
Safety boundaries: this does not change public APIs, persisted formats, or blob cache keys. Cache hits do not copy checksum strings. Files with no generated checksum, including active direct-write files, retain empty checksum fields. Existing footer-skip, corruption, and reader-refresh behavior remains in place. Integration in downstream file systems is outside this change.
Tests and known limitations: filesystem-capture tests cover regular blob reads and delayed direct-write reads. After rebasing onto the latest master, blob_file_cache_test, blob_source_test, db_blob_basic_test, and db_blob_direct_write_test passed both normally and with ASSERT_STATUS_CHECKED=1 (95 tests in each configuration). Fast arc lint passed. make format-auto and make check-sources could not operate because their scripts assume a Git checkout while this checkout uses Sapling; all files in the patch were formatted directly with clang-format.
Reviewed By: chaitanyadatye, anand1976
Differential Revision: D121630172
fbshipit-source-id: 601d1097e4a3caa60f0d9ba1794e78e81855980c
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14924
Widen the serialized "counts" array in CompactionServiceResult from kNumOfReasons - 1 back to the full kNumOfReasons, so the count for the newest CompactionReason is included in the remote compaction result.
The width was previously reduced by one to match readers that expected the narrower array; restoring it changes what is written. This is safe only because deserialization of "counts" is now size-tolerant (a reader expecting fewer elements drops the extra, one expecting more zero-fills the missing). **It must not land until that size-tolerant read D106321320 has been deployed to every primary, including rollback targets, so should be >= 2 releases ahead; otherwise a primary can still or fallback to running the strict reader would reject the wider result and fail deserialization of this field.**
== Test plan ==
**Legend**
CompactionReason is an enum whose last entry is kNumOfReasons (currently 21). CompactionStats::counts is an int[kNumOfReasons] holding one count per reason; it is serialized into the remote compaction result and read back by the primary.
- "narrow" = kNumOfReasons - 1 counts (omits the newest reason); what current code writes.
- "wide" = kNumOfReasons counts (includes the newest reason).
- strict reader: accepts only its exact expected element count; any other count fails deserialization (the plain OptionTypeInfo::Array behavior).
- tolerant reader: accepts any count -- more elements than expected are dropped, fewer are zero-filled; never fails on a count mismatch (this change, SizeTolerantDeserializeArray).
**Unit test**
CompactionServiceTest.CompatCheckCountsWidthTolerance -- verifies that deserializing "counts" tolerates a serialized element count equal to, one greater than, or one less than the reader's expected width (extras dropped, missing zero-filled), and that the binary serializes exactly the expected number of elements.
**Manual cross-version verification (ldb remote_compaction)**
Test 1: expect-narrow tolerant-read primary reads wide counts a worker write (extra reason dropped)
```
$ tolerant_wide remote_compaction_worker --db=$DB --job_dir=$JOB &
$ tolerant_narrow remote_compaction_primary --db=$DB --job_dir=$JOB
remote_compaction_worker: OK (4915 bytes result)
remote_compaction_primary: OK
$ grep 'remote compaction result' $DB/LOG
[default] [JOB 3] Received remote compaction result, output path: /tmp/cs_run/output, files: 000022.sst
```
=> PASS: the primary parsed and installed the remote result.
Test 2: expect-wide tolerant-read primary reads narrow counts a worker writes (missing reason zero-filled)
```
$ strict_narrow remote_compaction_worker --db=$DB --job_dir=$JOB &
$ tolerant_wide remote_compaction_primary --db=$DB --job_dir=$JOB
remote_compaction_worker: OK (4911 bytes result)
remote_compaction_primary: OK
$ grep 'remote compaction result' $DB/LOG
[default] [JOB 3] Received remote compaction result, output path: /tmp/cs_run/output, files: 000022.sst
```
=> PASS: the primary parsed and installed the remote result.
Test 3: expect-wide tolerant-read primary reads wide counts a worker writes
```
$ tolerant_wide remote_compaction_worker --db=$DB --job_dir=$JOB &
$ tolerant_wide remote_compaction_primary --db=$DB --job_dir=$JOB
remote_compaction_worker: OK (4917 bytes result)
remote_compaction_primary: OK
$ grep 'remote compaction result' $DB/LOG
[default] [JOB 3] Received remote compaction result, output path: /tmp/cs_run/output, files: 000022.sst
```
=> PASS: the primary parsed and installed the remote result.
Reviewed By: hx235
Differential Revision: D111068809
fbshipit-source-id: d78dd4b7501cb74e60bcb27dccd869b73052ac6d
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15193
Keep `DB::Write`'s existing immediate `IOError` contract while allowing RocksDB to recover safely when a remote file system reports that a WAL handle is permanently unusable. The public API documentation makes the persisted-state boundary, reopen behavior, and safe post-resumption retry point explicit. Normal operation remains unchanged; the additional protocol is entered only after a supported file-scoped WAL error.
### Motivation
A WAL write crosses an acknowledgement boundary: storage can accept the complete record and then fail before the caller receives confirmation. The resulting `IOError` therefore cannot tell RocksDB or the application whether the record is absent, partial, or complete. At the same time, the remote file handle can become permanently poisoned, so retrying that same handle can never succeed.
The previous recovery path retried the poisoned handle. Simply replacing it is not sufficient: RocksDB must also prevent reuse of an ambiguous sequence range, keep every column family on one durable boundary, and prevent WAL tailers from publishing a physically complete record that successful live resumption deliberately abandoned. The new protocol quarantines the affected WALs, reserves the ambiguous sequence range, creates a replacement WAL, atomically persists acknowledged state, retires the quarantined handles, and only then resumes writes.
### Behavior changes
#### 1. Recovering from a poisoned remote WAL handle
- **Why required:** A remote file system can permanently poison a WAL handle after an I/O failure. Retrying that handle cannot succeed and leaves the database write-stopped.
- **Previous behavior:** RocksDB retried the same WAL handle during recovery.
- **New behavior:** The remote file system marks the failure as file-scoped. RocksDB creates a replacement WAL, atomically persists all state that could depend on the failed WAL, and then removes the quarantined WAL handles from active use. Physical file deletion remains part of normal cleanup.
#### 2. Preserving an indeterminate WAL write outcome
- **Why required:** A WAL record can reach storage before the append returns `IOError`; neither RocksDB nor the application can know the outcome from that error alone.
- **Previous behavior:** Recovery could reuse a file-scoped failed write group's sequence numbers, allowing a later acknowledged write to overlap a record that might already be in the WAL. The public write API did not explain the acknowledgement ambiguity or distinguish live resumption from close/reopen recovery.
- **New behavior:** `DB::Write` still returns the original `IOError` immediately and does not retry the logical batch. Normal DB reads can continue, but the failed write is not visible in that DB session. The persisted DB remains indeterminate with respect to the failed write until supported file-scoped live resumption succeeds without closing or reopening the DB. On reopen, or for any other WAL error without the durable-abandonment guarantee, applications must resolve consistency within RocksDB's documented guarantees, including `WALRecoveryMode`, through idempotency, deduplication, reconciliation, or an equivalent mechanism. After successful supported file-scoped live resumption, RocksDB has durably abandoned the failed batch and reserved its sequence range; retrying the same logical write is then recommended to preserve one linear logical history.
#### 3. Keeping TransactionLogIterator consistent with live resumption
- **Why required:** A physically complete error-returned record can remain readable from its WAL even though supported file-scoped recovery excludes that batch from the database. Returning it from `GetUpdatesSince()` would make replication or change-data-capture consumers diverge from the recovered DB.
- **Previous behavior:** An iterator opened before recovery could return the abandoned batch. A new iterator could also return it while the physical WAL remained visible.
- **New behavior:** For file-scoped failures, RocksDB records the exact sequence range and WAL number of each indeterminate write. For the lifetime of that open DB, existing and new WAL iterators stop with `Status::NotFound` before the range, whether the physical record is complete or absent. A caller can begin a new iterator after the reserved range. Iterator creation snapshots the recovery-gap generation together with its WAL-file view so a failure on a later, unseen WAL returns `TryAgain` for the old iterator instead of skipping its valid prefix.
#### 4. Keeping all column families on one recovery boundary
- **Why required:** A write batch can update multiple column families. Retiring its WAL after independently persisting only some column families can expose a partially applied batch after reopen.
- **Previous behavior:** With `atomic_flush=false`, recovery could persist column families independently before abandoning the WAL.
- **New behavior:** File-scoped WAL recovery force-flushes every live column family atomically and verifies that each one advanced beyond the failed WAL before writers are retired or writes resume.
#### 5. Merging concurrent WAL failures
- **Why required:** Another WAL write can fail while an earlier recovery attempt temporarily releases the DB mutex.
- **Previous behavior:** A later failed WAL cutoff or ambiguous sequence could be overwritten or cleared by the earlier recovery attempt.
- **New behavior:** Each error episode monotonically merges the largest failed WAL cutoff and the sequence range of the latest ambiguous write group. Recovery clears the quarantine cutoff only if no concurrent failure extended it; otherwise the newer failure remains write-stopping for the next attempt. Iterator fencing tracks the actual ambiguous write range separately, so an older failed `SyncWAL()` and a newer ambiguous write are not incorrectly paired.
#### 6. Retaining file scope through `NoSpace` recovery
- **Why required:** `NoSpace` can still identify one permanently unusable WAL handle.
- **Previous behavior:** The `SstFileManager` recovery path entered generic recovery without carrying the failed WAL context.
- **New behavior:** File-scoped `NoSpace` carries the same cutoff and sequence evidence through the `SstFileManager` path as other file-scoped WAL errors.
#### 7. Scoping recovery context to one error episode
- **Why required:** A completed recovery must not affect a later unrelated background error.
- **Previous behavior:** WAL recovery context could remain latched after `ClearBGError` succeeded.
- **New behavior:** Successful background-error clearing resets the context. A failed recovery retains it so a later retry has the same safety information. The separate iterator gap record remains for the lifetime of the open DB because later WAL readers must continue to reject the abandoned physical record.
#### 8. Preserving successful `SyncWAL` metadata
- **Why required:** A healthy WAL can finish syncing while a separate recovery error already exists.
- **Previous behavior:** `SyncWAL` could return the existing background error before applying the successful WAL's `VersionEdit`, dropping its MANIFEST update.
- **New behavior:** `SyncWAL` skips quarantined writers, applies metadata for successfully synced healthy WALs, and only then returns any existing recovery error.
#### 9. Classifying only the intended remote-file-system failure
- **Why required:** File-scoped recovery is valid only for an actual WAL `IOError` from a handle whose ownership is still valid.
- **Previous behavior:** Poison state could be queried before ownership fencing was resolved, and file scope could be stamped onto a non-I/O status.
- **New behavior:** Ownership fencing is resolved first, poison state is queried afterward, and only an actual `IOError` can be classified as file-scoped. `IOFenced` remains fatal.
#### 10. Preserving `paranoid_checks=false` retry behavior
- **Why required:** Non-paranoid mode historically cleared a writer's sticky error so a later operation could retry after storage recovered.
- **Previous behavior:** Avoiding reclassification of a cached synthetic writer error also bypassed `WALIOStatusCheck`, so later `SyncWAL` calls kept returning the stale error.
- **New behavior:** A cached error still fails the current `SyncWAL` call, but the non-paranoid path resets the writer for a later retry without setting a background error or entering file-scoped recovery.
#### 11. Preserving async WAL precreation across recovery
- **Why required:** A precreated WAL is an unpublished replacement and is not the active WAL. Recovery can also consume the prepared slot while background work is stopped.
- **Previous behavior:** A file-scoped failure while creating or starting the unpublished replacement was attributed to the active WAL, unnecessarily quarantining it. After successful recovery consumed a prepared WAL, the next slot was not replenished, so the following rotation created its WAL synchronously.
- **New behavior:** Only an active WAL `WriteBuffer()` failure attaches the active-WAL cutoff. A failed unpublished candidate is discarded while the current WAL remains active, and successful recovery schedules the next async precreation after `ClearBGError()` re-enables background work.
### Implementation
- Carry `DBRecoverContext` through generic, `NoSpace`, and `SstFileManager` recovery, merging WAL cutoffs and failed write-group sequence ranges independently of error severity. Generic errors retain the range only so a later retry that establishes file scope can activate the stronger protocol; they do not persist a reservation or install an iterator gap on their own.
- Document the WAL acknowledgement ambiguity, persisted-state boundary, reopen behavior, application consistency requirements, and post-resumption retry guidance on the public `DB::Write` API.
- Publish a lock-free quarantine cutoff used by write and sync paths; replace even an empty failed current WAL; persist file-scoped ambiguous sequence reservations; force and verify an atomic all-column-family flush; and detach quarantined writers only after that durable boundary.
- Record each file-scoped ambiguous write group's exact WAL number and sequence range in a DB-lifetime tracker shared by `WalManager` and all `WalIteratorImpl` instances. Stop an iterator before a batch that contains or crosses the range, and use a generation snapshot to preserve the existing `TryAgain` behavior when an iterator's fixed WAL-file view predates the failure.
- Keep successful healthy-WAL tracking edits in `SyncWAL`, reset completed recovery context, and preserve legacy non-paranoid writer reset behavior.
- Distinguish failures on an unpublished replacement WAL from failures flushing the active WAL. Log the failed replacement identity, keep the active WAL out of quarantine, and replenish the async-precreation slot after successful recovery.
- Classify poisoned remote-file-system handles after ownership fencing and only for `IOError`.
- Add lifecycle logging and a state-machine comment covering quarantine, sequence reservation, iterator fencing, replacement, atomic persistence, retirement, and concurrent-error handoff.
- Add an opt-in `db_stress` mode that injects file-scoped faults only into WAL writes, disables WAL tracking in that mode, validates unsupported direct invocations, and sanitizes crash-test parameters to the supported recovery envelope. A small fraction of ordinary non-specialized crash-test runs select this mode automatically; existing stress behavior is unchanged when the flag is off.
- Make the concurrent-error regression test hold the initial background flush until `SyncWAL` records the intended older-WAL error, preventing scheduler-dependent retirement of the test WAL before fault injection.
### Safety boundaries and unsupported cases
- Automatic file-scoped WAL recovery fails closed with `NotSupported` for manual WAL flush, 2PC/transactions, two write queues, pipelined or unordered writes, WAL recycling, WAL TTL or size retention, either WAL-tracking mode, or blob direct writes.
- Unsupported recovery leaves the DB write-stopped. `DB::Resume()` returns `NotSupported`; after correcting the I/O condition, operational recovery requires close and reopen.
- The failed logical batch is never retried. Only supported file-scoped recovery durably reserves its sequence range and fences `GetUpdatesSince()`. Generic WAL errors retain the existing indeterminate outcome and can replay after close/reopen; applications must use idempotency or reconciliation unless successful file-scoped live resumption has provided the stronger guarantee.
- Failed recovery attempts retain their context. Only a successful `ClearBGError` resets it.
- Physical WAL deletion is not part of the recovery correctness boundary; normal obsolete-file cleanup removes files later.
- The TransactionLogIterator ambiguity fence is kept for the lifetime of the DB instance that observed the file-scoped error and is not persisted as new MANIFEST metadata. After a later close/reopen, normal WAL recovery and obsolete-WAL retention/deletion semantics apply.
- The in-memory iterator-gap tracker can grow with the number of recovered file-scoped failures during one DB lifetime. Safe pruning must account for retained WALs and existing iterator snapshots; it is left for follow-up.
- Async WAL precreation remains disabled with WAL recycling. A prepared WAL is unpublished until its start records succeed; failure before installation does not make the active WAL file-scoped.
- The new stress mode is test-only and defaults off, so normal `db_stress` and database operation without an error are unchanged.
- Automatic stress-profile selection is limited to fully parsed, supported configurations. Explicit targeted runs reject options files and other unsupported combinations.
### Tests and known limitations
- The affected recovery, WAL-manager, WAL-iterator, fault-injection, async-WAL-precreation, and secondary-close binaries build successfully.
- Checked-status suites pass for all 70 `error_handler_fs_test` cases, all 7 `wal_manager_test` cases, all 11 `db_log_iter_test` cases, all 8 `fault_injection_fs_test` cases, and all 5 `DBWALTest.AsyncWalPrecreate*` cases. `DBSecondaryTest.SecondaryCloseFiles` passes 20 consecutive repetitions.
- Focused regressions cover file-scoped gap fencing after an iterator snapshot, generic errors not installing a file-scoped sequence gap, and later recovery discovering that a generic error is file-scoped.
- Earlier validation on this diff also passed crash-test unit coverage, remote-file-system adapter tests, repeated concurrency tests, two bounded multi-column-family stress runs, and bounded black-box crash/reopen dry runs.
- The exact internal RocksDB format check and `arc f --take-formatters --enforce-lint-clean` report no formatting or lint issues.
- The full crash/stress matrix and TSAN were not run.
Reviewed By: pdillinger
Differential Revision: D119324616
fbshipit-source-id: 05779d2bff7018f0ae69c4cbbe5b6455a45675d6
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15292
D120637416 introduced a range-for whose `const std::string&` loop variable binds to temporary strings created from a string-literal initializer list. GCC diagnoses this with `-Wrange-loop-construct`, breaking warnings-as-errors builds. Iterate by value to make the intended string construction explicit without changing test behavior.
___
Differential Revision: D122474818
fbshipit-source-id: c7f30a374dc7569ceb1a4ecc6f7f8c3c3637be00
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15271
## Summary
Extend the experimental external-table API with a full mode that preserves RocksDB internal keys, sequence numbers, and entry types. Unlike the existing ingestion-oriented mode, full external tables can represent normal flush and compaction output and participate in live databases with puts, merges, deletions, snapshots, `Get`, `MultiGet`, iteration, multiple LSM levels, and external-file ingestion.
The existing `ExternalTable*` interfaces remain aliases for the original sequence-zero, Put-only mode, preserving compatibility for current implementations.
## Implementation details
- Introduce `ExternalTableMode` and mode-specific reader and factory interfaces. Full-mode builders and iterators receive complete internal keys, while the original mode continues to receive user keys.
- Adapt full-mode point lookups to RocksDB’s `GetContext`, allowing readers to return multiple versions of a user key in internal-key order so RocksDB can resolve snapshots, merges, and deletions correctly.
- Add a batched `MultiGet` context that preserves per-request lookup state and maps external-reader results back to RocksDB’s original request range.
- Translate iterator seek boundaries and returned keys while ensuring file-wide global sequence numbers are applied exactly once.
- Track sequence-number bounds, deletion and merge counts, user-collected properties, file size, and unique IDs when building tables. Unsupported entries fail and abandon the builder.
- Forward checksum verification to the external reader while retaining compatibility with implementations that do not override it.
- Add a standalone test implementation shared by basic and full modes. It uses a simple length-prefixed file format with a RocksDB properties block and checksum, avoiding dependencies on native table implementations.
## Crash testing
Add `use_full_external_table` support to `db_stress` and a dedicated blackbox `db_crashtest.py` profile. The profile exercises randomized reads, writes, merges, ingestion, flushes, compactions, snapshots, checksum verification, and crash recovery.
The profile disables unsupported combinations, including range deletions, user timestamps, sequence-number time tracking, backward scans, multiple column families, backup reconstruction, remote compaction, dynamic table-factory replacement, randomized aggregate/all-table property probes, and global-sequence-number rewriting during ingestion.
Because the simple reader materializes each open table in memory, the profile limits open files and generated file sizes to bound total database memory usage.
Reviewed By: xingbowang
Differential Revision: D120637416
fbshipit-source-id: aa2cbd06999e4325a234210d9c45987addd37550
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15288
Follow-up to D122242630 (https://github.com/facebook/rocksdb/pull/15286), addressing its review.
Previous behavior:
Before D122242630, when `VersionSet::ProcessManifestWrites` failed to build a batch's new `Version`, it returned without removing the batch from `manifest_writers_` (the two `FIXME? manifest_writers_ still has requested updates` comments). The writers are destroyed when their `LogAndApply` call returns, so later MANIFEST updates, including flushes and compactions, could hang behind the dangling entries or corrupt memory. It needs a fatal LSM consistency-check failure, or a `MemoryLimit` from charging `CacheEntryRole::kFileMetadata` (off by default) to a full block cache with `strict_capacity_limit`. It was found by reading the code.
D122242630 fixed that, but found the end of a failed batch with a second scan that copied the grouping loop's stop conditions, which can drift apart, and added no release note.
New behavior:
A failed batch completes the same writers as with D122242630, but only the grouping loop decides where it ends: `ProcessManifestWrites` records the first `LogAndApplyHelper` failure in `batch_apply_status`, later iterations skip version construction but still run the loop's end-of-batch checks, and the batch then completes through `finish_batch`. This also labels the inner-loop `break`s and adds a release note.
Safety, compatibility, and limitations:
No API, option, or format change. The success path and the `SaveTo` failure path are unchanged.
Reviewed By: pdillinger
Differential Revision: D122417768
fbshipit-source-id: c6d474ace32446d6b413b1a1b3cf2fa49a02447d
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15289
Why the change is required:
Secondary catch-up must release reader-free retired read views before scanning
for obsolete files. T290043099 and duplicate T290043100 exposed a race where
that ordering was lost, leaving two obsolete table files open and failing
DBSecondaryTest.SecondaryCloseFiles. The assertion failure also exposed unsafe
test cleanup because the secondary DB could outlive the test-local Env.
Previous behavior:
Retiring a read view always woke the background reclaimer. The worker could
remove a reader-free view from the retirement queue and pause before destroying
its SuperVersions. Synchronous cleanup then saw no queued work, ignored the
in-flight reclamation, and continued to obsolete-file discovery while the old
files were still referenced. The test also used a static lambda that captured
the first fixture across repeated runs and closed the secondary only on the
success path.
New behavior:
Catch-up synchronously reclaims reader-free views, or waits when a competing
background worker has already claimed them. Views still held by active readers
remain non-blocking and are reclaimed asynchronously after the last reader
releases them. The test now closes the secondary before its local Env on every
exit path and creates its verification lambda per test invocation.
Implementation:
Publishing a replacement view no longer wakes the background reclaimer; the
immediately following synchronous cleanup owns that work. Reader release still
wakes the worker. Cleanup waits on the existing in-flight counter when a worker
won an earlier race. The test uses a Defer guard for lifetime ordering and
removes the repeat-unsafe static lambda.
Safety, compatibility, and limitations:
This changes no public API, ABI, file format, recovery behavior, or read
consistency. Cleanup does not wait for views retained by active readers, so the
non-blocking multi-column-family iterator behavior is preserved. It waits only
for reclamation already in progress. Validation covered the internal Linux
ASAN/UBSAN target; the external GitHub Actions matrix was not run. Repository
make-based format/source checks require a Git worktree and were unavailable in
this Sapling checkout.
Reviewed By: jaykorean
Differential Revision: D121816425
fbshipit-source-id: a86cfff43a0cedd10b90e526a56e862f18d2a58d
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15286
**What**: `LogAndApply` completes every MANIFEST writer in a batch when version construction fails before persistence.
**How**: `VersionSet::ProcessManifestWrites` centralizes queue completion in `finish_batch`, invokes it for `VersionBuilder::Apply` and `SaveTo` failures, and extends assembly failures through the normal group boundary. Completion propagates the failure through callbacks, signals writers owned by other callers, and wakes the next leader.
**Why**: Stack-backed writers otherwise remain queued after their caller returns, leaving dangling pointers, blocked writes, and flush or compaction state that never receives its failure callback.
Reviewed By: xingbowang
Differential Revision: D122242630
fbshipit-source-id: bab4ac8f665269ba57dd86579299c1d5935fa907
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15273
**What**: `ClipColumnFamily` keeps boundary keys valid across unlocked writes, and atomic-flush waits pair every column family with the correct memtable bound.
**How**: `DBImpl::ClipColumnFamily` copies boundary keys from the current `Version` while holding the DB mutex, with a sync point that lets the regression test replace that version before deletion. `DBImpl::AtomicFlushMemTables` and `DBImpl::RetryFlushesForErrorRecovery` construct wait bounds in `cfds` order and supply no-bound fallbacks when a column family is absent from the flush-request map. Both wait sites gain a debug invariant asserting each id matches the map entry for the cfd at the same index.
**Why**: Background version replacement could leave `ClipColumnFamily` reading freed metadata, while unordered or missing atomic-flush map entries could cause early completion or out-of-bounds access for idle or duplicate column families.
Reviewed By: xingbowang
Differential Revision: D121840843
fbshipit-source-id: 8e49b5c953a1aa29a0244fc0aae2ca3ab84be74f
Summary:
GitHub Actions container jobs previously used a fixed 16 GB `/dev/shm` allocation regardless of runner size, while the clang-tidy container did not set one explicitly. This over-allocated shared memory on smaller runners and gave larger, more highly parallel runners no additional shared-memory capacity.
Scale container shared memory to 2 GB per runner core across the PR, nightly, nightly-candidate, weekly, and clang-tidy workflows:
- 4-core jobs use 8 GB.
- 16-core jobs use 32 GB.
- 32-core jobs use 64 GB.
- The nightly-candidate ARM job uses 8 GB, matching its four-way build.
The clang-tidy job now explicitly receives 8 GB. Existing unrelated container options, including the mini crash test's `nofile` limit, remain unchanged.
## Safety and compatibility
This only changes container resource configuration for GitHub Actions jobs. It does not change RocksDB code, APIs, persistent formats, or non-containerized jobs.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15284
Test Plan:
- `make check-workflow-yaml`
- `git diff --check upstream/main...2026_09_28_shm`
Both checks pass. The CI workloads themselves were not executed locally, so the new shared-memory allocations will be exercised by PR and scheduled workflow runs.
Reviewed By: omkarhgawde
Differential Revision: D122218094
Pulled By: xingbowang
fbshipit-source-id: aa577c37683022ba01c4a073a011dbe0032abbf6
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15283
**What**: `DBSecondaryTest.SecondaryCloseFiles` no longer calls into a destroyed `Env` when the test body returns early, fixing a ThreadSanitizer heap-use-after-free at secondary DB close.
**How**: The test opens `db_secondary_` (a fixture member) on a stack-local `traced_env`. A `Defer` now closes the secondary before `traced_env` is destroyed on every exit path, so the later `~DBSecondaryTestBase` close never reaches `DBImpl::CloseHelper`'s `env_->UnSchedule` on a freed Env. This mirrors the sibling `DBSecondaryCatchUpFaultTest` fixture, which already closes the secondary in its destructor before its Env member for the same reason.
**Why**: On any early `ASSERT_*` return before the explicit close, `traced_env` (a body local) was freed first while `db_secondary_` (a fixture member) was closed later in the base destructor, which made a virtual call on the freed Env. ThreadSanitizer flagged this as a land-blocking heap-use-after-free in the `build-linux-clang21-mini-tsan` CI job.
Failed CI run: https://fburl.com/jo6gtllf
___
Differential Revision: D122195601
fbshipit-source-id: 3d8b427ba50759c40f15d2b512379594b6057234
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15265
This change adds column family name reflection to an external table reader and allows the user to extract an external table factory from a top level table factory, if it exists
Motivation
Adding column family metadata to the reader allows the reader to handle CF specific metadata, also add external table reflection
Previous/New Behavior
No change in behavior. Added a defaulted parameter threaded through many constructors to preserve existing API contract while introducing new capability. Added a function which allows unwrapping of an external table factory
Nothing much to test
Reviewed By: xingbowang
Differential Revision: D121335729
fbshipit-source-id: 74f9b1be6a8f301bdbe4540de0656b83e26f88ab
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15270
D121318547 added SecondaryDB batch-iterator coverage to CfConsistencyStressTest::ContinuouslyVerifyDb(), but the automated CF-consistency crash-test profile never selected test_secondary. Automated runs therefore exercised only the primary DB path while the targeted manual validation used test_secondary=true.
Randomize test_secondary between 0 and 1 in cf_consistency_params. Each generated run can now exercise either the existing primary-only path or the SecondaryDB path, preserving both configurations in the continuous matrix. The regression test deterministically verifies both random outcomes. Existing sanitization may still disable SecondaryDB when another selected option is incompatible, such as remote compaction or primary UDI mode.
Reviewed By: xingbowang
Differential Revision: D121813002
fbshipit-source-id: 4080e2131c1f063d590d24cc0bcee9b9096de63a
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15199
## Why
PrefixScan and MultiScan verification failures are difficult to reconstruct from the terminal assertion alone. The iterator options, bounds, seeks, preparation state, and immediately preceding movements are often the evidence needed to distinguish a database defect from an invalid test assumption.
## Why the existing recovery trace is not enough
The expected-state trace exists to reconstruct the expected database state after recovery. It preserves ordered write batches and deliberately filters out reads and iterator seeks, then recovery consumes a sequence-number-selected prefix. Consequently it contains none of the scan-specific evidence needed here: read options, bounds, seek targets, iterator validity, lazy-value preparation, movements, or the exact mismatch state. Scan witnesses capture that bounded human-readable context only when a verification failure needs it; they are best effort, are never replayed, and do not alter the recovery trace or its failure semantics.
## Previous behavior
The scan paths emitted generic failure output but did not retain a focused bounded witness of iterator setup and state transitions. Adding instrumentation naively would also format diagnostic strings on the normal path even when disabled and could create one file per failing operation.
## New behavior
An opt-in bounded ScanWitnessLog records scan configuration and recent iterator events for PrefixScan and MultiScan variants. On a qualifying failure it writes the latest complete witness for that DB, PID, thread, and scan type. The operation ordinal remains in the file contents. Repeated failures atomically replace the fixed witness rather than creating unbounded files.
## Implementation
The witness captures sanitized read options, input ranges, bounds, seeks, movement, PrepareValue results, iterator status, keys, value size and hash, and wide-column state. MultiScan tracks whether the current primary iterator value was actually prepared so failure snapshots report accurate state. Disabled witnesses avoid scan-label ownership, range formatting, expected-count formatting in release builds, and numeric iterator-label conversion. Witness writes share the per-thread diagnostic I/O-disable latch with breadcrumbs, write to a fixed temporary path, close, and rename over the final file. Entry counts are capped at 65,536. A formatting-only follow-up normalized all touched C++ lines with the repository clang-format rules.
## Safety and unsupported cases
The feature is stress-tool-only and raw db_stress keeps it disabled by default. It observes public iterator state and does not change database contents, expected-state tracing, recovery replay, iterator decisions, or fault-injection outcomes. Diagnostic I/O remains best effort and cannot fail the workload. Files are bounded per retained PID, thread, and scan type. The witness covers the instrumented PrefixScan and MultiScan implementations only and is not a general RocksDB trace. Files are not fsynced and therefore are not a power-loss durability mechanism.
## Tests and limitations
Validated with 47 tools/db_crashtest_test.py tests, exact-candidate debug and release db_stress builds, arc lint over the complete stack, oversized-entry rejection, and clean independent correctness reviews of the final integrated stack. make check-sources is unavailable because it invokes Git in this Sapling checkout; equivalent added-line hygiene checks pass. Only the newest complete witness per DB, PID, thread, and scan type is retained, so later failures intentionally replace earlier witnesses.
## Rebase update
Rebased onto fbsource master dc59f986b964. The logical diff is unchanged; the rebased stack passes debug and release db_stress builds, all 47 db_crashtest Python tests, and arc lint.
Reviewed By: jaykorean
Differential Revision: D119160911
fbshipit-source-id: 0e9b040c3bbb482e0a3b1b11070a7c5cc519f060
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15241
Native `TableFactory` implementations receive the RocksDB `WritableFileWriter` in `NewTableBuilder()` and `RandomAccessFileReader` in `NewTableReader()`. These wrappers provide buffering, checksums, rate limiting, statistics, listeners, tracing, and direct-I/O handling. External table implementations previously received a limited writable-file adapter and reopened readers from a path, bypassing parts of this wrapper logic.
This change makes `WritableFileWriter` and `RandomAccessFileReader` implement `FSWritableFile` and `FSRandomAccessFile`, respectively, so the same RocksDB wrappers can be passed to external tables. `ExternalTableFactory::NewTableReader()` now receives the already-opened reader and file size. The filesystem interfaces expose standard append, flush, sync, fsync, file metadata, synchronous reads, asynchronous reads, and batched reads. `RandomAccessFileReader::SubmitReadAsync()` schedules its wrapped coroutine read on the configured filesystem read executor, and the DB table-cache and `SstFileReader` construction paths pass that filesystem through. `SstFileReader` also supplies its configured statistics, listeners, rate limiter, and file temperature, removing the need for a separate external-table metrics filesystem wrapper.
For direct I/O, `MultiRead()` checks whether requests are already sorted. When alignment and merging are needed, it constructs a sorted index only for unsorted input, preserving caller request order and result mapping. Fully aligned requests using aligned caller buffers bypass this path and may be supplied in any order.
Reviewed By: xingbowang
Differential Revision: D121035539
fbshipit-source-id: 27cb897726c31cb1a4dc811c65c8d79e690d82b1
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15264
Why the change is required:
LargeSizeSstFileWriter creates approximately 12 GiB of SST output in the shared /dev/shm used by build-linux. With 32 concurrent test jobs in a 16 GiB tmpfs, this can exhaust the filesystem and make unrelated fault-injection and repair shards fail with ENOSPC.
Previous behavior:
The test wrote an approximately 8 GiB large-key SST and an approximately 4 GiB large-value SST even though it never read or ingested either file. It validated the size limits and successful Finish calls, but the physical output competed with every other test process.
New behavior:
The test still runs the real SstFileWriter and table builder for over-limit rejection and maximum-size acceptance, including Finish, sync, and close. The generated byte stream is counted and discarded instead of materialized in the shared tmpfs.
Implementation:
Add a test-local FileSystem wrapper whose writable file counts appended bytes and records sync and close calls. Inject it only into LargeSizeSstFileWriter and verify that the writer-reported logical size matches the observed append stream, the lifecycle completes, and no physical SST is created.
Safety, compatibility, and limitations:
Production code, shared filesystem implementations, serialized formats, and other tests are unchanged. The fake does not validate SST contents, but the previous test did not read the generated files either. Table construction still consumes substantial memory, so the existing HasBigMem guard remains; this change addresses tmpfs exhaustion only.
Reviewed By: rban1
Differential Revision: D121483663
fbshipit-source-id: 055372bc07e3a1f7289e34ebc35694bc3c4bfad9
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15263
Why: keep internal RocksDB main ready for 11.12.0 development while the 11.11 release is cut.
Previous behavior: Conveyor opened a GitHub pull request for this update, which could be missed and was not stacked with the internal release diff.
New behavior: Conveyor owns an internal Phabricator diff that the release-import diff stacks on top of. The release manager exports this diff manually with ExportIt.
Implementation:
- Bump version.h from 11.11.0 to 11.12.0
- Add `11.11.fb` to `check_format_compatible.sh`
- Sync HISTORY.md `## 11.11.` section from `11.11.fb`
- Delete 19 consumed `unreleased_history/` note file(s) from main
Safety and unsupported cases:
- Only version.h, check_format_compatible.sh, HISTORY.md, and consumed release-note files are allowed.
- Open legacy GitHub update-main PRs block this flow; this automation never modifies, exports, or lands them.
- Concurrent direct CLI invocations are unsupported; Conveyor serializes the Release Cut node to one active run.
- The tool refuses unrelated commits, mismatched release metadata, and non-canonical GitHub repositories.
Known limitations:
- ExportIt remains manual. Bot create permission for internal_repo_rocksdb/repo is a rollout prerequisite.
Reviewed By: xingbowang
Differential Revision: D121434642
fbshipit-source-id: 3c6c90f425e043ff9c13318ecc671aedbdef0347
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15259
Publish immutable SecondaryDB read views after open and successful catch-up so `NewIterators()` can acquire one cross-CF sequence and SuperVersion set without waiting for `DBImpl::mutex_`. Keep failed catch-ups on the previous view, reject refresh for batch iterators, and exercise concurrent catch-up plus batch scans in the CF-consistency stress profile.
Reviewed By: xingbowang
Differential Revision: D121318547
fbshipit-source-id: 3198f4766c90ab0bb39400963df3c329b4ecf6bf
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15254
**What**: `IODispatcher` verifies prefetched data blocks before serving or caching them and safely falls back when block construction fails.
**How**: `CreateAndPinBlockFromBuffer` validates every returned span and, when `ReadOptions::verify_checksums` is enabled, verifies its checksum and records the standard counters. `PollAndProcessAsyncIO` handles coalesced blocks independently and retires shared request state before failed entries fall back through `SyncRead`; `ReleaseFailedSyncPrefetchMemory` returns limiter capacity for unmaterialized synchronous blocks.
**Why**: Unverified prefetches could serve or cache bit rot, while newly detected failures could otherwise leave async state referencing released provider buffers or permanently consume bounded prefetch capacity.
Reviewed By: xingbowang
Differential Revision: D121277687
fbshipit-source-id: 65cc1d5b9dcc53bdc64f8ee5d672bfe2cda7b40b
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15261
Why the change is required:
Internal Phabricator diffs and external GitHub pull requests need the same substantive explanation of a RocksDB change. Existing local instructions focus on code generation and review but do not define what a summary must tell reviewers, so summaries can omit prior behavior, safety boundaries, compatibility, or validation evidence and can drift between the two review surfaces.
Previous behavior:
There was no CLAUDE.md at the internal_repo_rocksdb wrapper root, the RocksDB CLAUDE.md did not point authors to summary guidance, and there was no shared document covering both internal diff and external pull request summaries.
New behavior:
The wrapper-level CLAUDE.md requires authors to consult a shared summary guideline before writing or updating an internal diff summary. The repository CLAUDE.md applies the same guideline to external pull request summaries. The guideline requires motivation, previous and new behavior, implementation, safety and compatibility boundaries, and test evidence, with RocksDB-specific advice for correctness, persisted formats, concurrency, and performance changes. Examples are separated by access boundary: the internal wrapper contains Phabricator diff examples, while the repository guideline contains publicly accessible GitHub pull request examples.
Implementation:
Add a concise wrapper-level CLAUDE.md, add claude_md/pull_request_summary_guideline.md as the shared source of truth, and link it from repo/CLAUDE.md. Keep annotated internal diff references in the wrapper-level file and corresponding public pull request references in the repository guideline, including a recent change from Peter Dillinger.
Safety boundaries and unsupported cases:
This is documentation-only and changes no runtime, API, build, test, or CI behavior. The repository guideline contains no internal links, so external readers do not need Meta access. The guidance is manually followed by authors and agents; it does not mechanically validate summaries, replace reviewer judgment, or require a rigid section layout for small changes.
___
Differential Revision: D121388476
fbshipit-source-id: bb09ab7ba8ef6dc2b2830a42735322890536622f
Summary:
`BlockBasedTableIterator::SeekImpl()` sets
`seek_key_prefix_for_readahead_trimming_` only when given a seek key,
and `ResetBlockCacheLookupVar()` does not clear it. After a `Seek()`, a
`SeekToFirst()` on the same iterator trims readahead against the
previous seek key's prefix. `DBIter::SeekToFirst()` under
`prefix_same_as_start` takes its prefix from the first key found, so the
old prefix does not describe the keys being scanned.
With the stale prefix, `IsNextBlockOutOfReadaheadBound()` is true at
every block instead of only at the prefix boundary, and each prefetch is
clamped to the current block. The same predicate sets
`is_index_out_of_bound_`, which `FindBlockForward()` converts to
`is_out_of_bound_` and ends the scan. Under `ReadOptions::async_io` this
drops keys.
The bug needs `auto_readahead_size` and a preceding `Seek()` that reads
no data block. A `Seek()` that lands on a key leaves the prefetch buffer
anchored above the scan, and the trimming callback is not called. The
bug does not occur under `allow_mmap_reads`, where `FilePrefetchBuffer`
is constructed disabled.
The fix clears the prefix in `SeekToFirst()`, so prefix-based trimming
is disabled for that positioning. The `auto_readahead_size`
documentation in `include/rocksdb/options.h` already says prefix-based
trimming requires `Seek(key)`.
On buffered reads with `prefix_same_as_start`, `auto_readahead_size` and
`readahead_size=8192`, a cold-cache `SeekToFirst()` scan of 2M keys in a
single 222 MB file, preceded on the same iterator by a `Seek()` past the
last key, took 391 ms before and 308 ms after. The same scan with no
preceding `Seek()` takes 307 ms. Under direct IO on a local disk it went
from 33.1 s to 11.8 s. Data blocks read are unchanged; file reads drop
by 3x.
Also fixes a pre-existing test bug: `SeekParallelizationTestWithPosix`
asserts on `number_async_seek` without resetting that cumulative
thread-local, unlike its three siblings. The new tests move
`prefetch_test` from 11 shards to 13, so different tests share a
process, and the assertion fails at the parent commit under that
grouping.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15216
Test Plan:
New regression test in `PrefetchTrimReadaheadTestParam`, in sync and
`async_io` variants over the fixture's three `index_shortening` modes.
It runs two scans of the same keys from an equally cold block cache,
differing only in a preceding `Seek()`. Reverting the `clear()` fails 3
of 6 sync parameterizations on `READAHEAD_TRIMMED` (6 vs 40) and 3 of 6
`async_io` parameterizations on key count (40 vs 1).
`make check` and `ASSERT_STATUS_CHECKED=1 make check` show no new
failures. `prefetch_test` 122/122, `db_iterator_test` 249/249,
`table_test` 7629/7629, `block_based_table_reader_test` 5347/5347,
`db_basic_test` 300/300. New cases ran 1200 times under
`COERCE_CONTEXT_SWITCH=1` without failure.
Added db_stress coverage for `SeekToFirst()` after `Seek()` under
`prefix_same_as_start`. With the fix reverted and io_uring available,
the configuration in the second commit fails 8 of 8 seeds with
"Iterator diverged from control iterator"; with the fix, the same seeds
pass.
Reviewed By: rban1
Differential Revision: D120228466
Pulled By: xingbowang
fbshipit-source-id: a4935f03a0080cc8dd4c7a8c1438404e9b8f5c3a
Summary:
Make `IndexFactory` the public interface for custom indexes. Keep the old `UserDefinedIndex` names, header, and `NewBuilder()` / `NewReader(Slice&)` overloads so existing implementations can still compile.
UDI and options tests pass on the full stack, both normally and with `ASSERT_STATUS_CHECKED=1` and `COERCE_CONTEXT_SWITCH=1`. The C API test and generation/compatibility checks pass. Full `make check` runs had test timeouts and hit the 30-minute local limit. These test results are from before the rebase.
Part 5 of 9. Previous: https://github.com/facebook/rocksdb/issues/14961 (landed). Next: https://github.com/facebook/rocksdb/issues/14963.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14962
Reviewed By: jaykorean
Differential Revision: D119960591
Pulled By: xingbowang
fbshipit-source-id: 9703950bd48723c2c524da89cbea63bf00254cc1
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15218
Why the change is required:
Public and serialized RocksDB inputs can change observable performance or resource behavior without a signature or default change. These changes require deliberate human judgment, clear documentation, and research into downstream production impact. The automated CI review also needs an explicit perspective for identifying gaps in that analysis.
Previous behavior:
CLAUDE.md described source and ABI compatibility but did not explicitly guide authors or reviewers through behavioral changes to already accepted inputs. The CI review prompt had only a brief API-compatibility checklist item and no shared instructions for evaluating intentionality, release notes, downstream usage, or regression coverage.
New behavior:
CLAUDE.md states that behavior changes are human judgment calls that must be intentional, documented, and researched. It gives concrete guidance for existing accepted inputs while asking agents and reviewers to call out gaps or ambiguity. The CI review prompt applies the same perspective to every review agent and makes clear that a behavior change is not automatically a defect.
Implementation:
Revise the Behavioral Compatibility policy in CLAUDE.md, reduce its workflow entry to a concise pointer, add a dedicated Behavioral Compatibility Review Perspective to ci_review_prompt.md, and point the API and compatibility reviewer to that shared section.
Safety boundaries and unsupported cases:
This is documentation-only and changes no runtime, API, build, or CI execution behavior. The guidance supports human judgment; it does not automatically decide whether a behavior change is acceptable, guarantee exhaustive discovery of downstream consumers, or replace release-note, testing, and production-impact review.
Reviewed By: pdillinger
Differential Revision: D120344151
fbshipit-source-id: aa5bbb00fbbe167ef21b91b3551868c4d1b4046d
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15237
Route `DB::MultiGet` batches through `ExternalTableReader::MultiGet` instead of `TableReader`'s per-key fallback, preserving per-key status and pinned-value handling.
Allow basic external tables to honor DB-assigned global sequence numbers across overlapping levels, snapshots, `Get`, `MultiGet`, and iteration. Preserve ingestion-only behavior by rejecting compaction output, and support both `allow_global_seqno` and deprecated `write_global_seqno` files.
Reviewed By: xingbowang
Differential Revision: D120629061
fbshipit-source-id: 366f48660e97a300afb59627b9a1a41fe3805590
Summary:
These changes improve the block-based SST table properties available for compression benchmarking and optimization.
- Fix: `uncompressed_data_size` was computed while building a file but was never written to the properties block, so it always read back as 0. It is now persisted (written in AddTableProperty and read back in ParsePropertiesBlock).
- Add: a new packed `lsm_info_at_creation` property records the LSM level and bottommost status at the file's creation. A new `LsmInfoAtCreation` struct owns the (extensible) uint64 encoding via symmetric Encode()/DecodeFrom(), keeping TableProperties a bare data struct. It distinguishes three states: "unknown" (nothing recorded, e.g. older files), "not applicable" (no LSM position, e.g. SstFileWriter/misc output), and "applicable" (level + bottommost, with an unknown-level sentinel for the DB repairer). The level is stored as its natural value.
- Change: the `_compression_manager` detail in the `compression_options` property now records the manager's full serialized configuration (via ToString) instead of only its GetId().
New properties are additive and backward compatible: older files without them read back as "unknown"/0.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15231
Test Plan:
Added coverage in db/db_table_properties_test.cc: LsmInfoAtCreation encode/decode round-trips (natural levels, unknown level, not-applicable vs unknown); end-to-end persistence of uncompressed_data_size and lsm_info_at_creation after flush (L0, not bottommost) and a forced bottommost compaction; not-applicable recorded for an SstFileWriter file; and full compression manager configuration present in compression_options. Updated options/options_settable_test.cc for the new field.
Also made table_test's BlockBasedTableTest.CompressionRatioThreshold resilient to per-file overhead changes (such as these added table properties): it now verifies the compression decision via the persisted data_size vs uncompressed_data_size rather than modeling total file size against a fixed overhead constant, which had become too tight. Full table_test passes.
Reviewed By: rban1
Differential Revision: D120553038
Pulled By: pdillinger
fbshipit-source-id: 07ede62dbcbe31c040fd9b037c7636a49558c4d9
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15194
## Why
Intermittent db_stress and db_crashtest failures often report only the terminal assertion, after the operations that established the failing state have scrolled out of logs. The diagnostics must retain the latest operations even when the stress process is killed before it can run a failure handler.
## Why the existing recovery trace is not enough
The existing expected-state trace is a correctness oracle for recovery: after an expected-state checkpoint it records ordered write batches, and recovery replays the prefix implied by the database sequence number. Reads and iterator seeks are deliberately filtered out, trace-write failures are fatal, and the trace is machine-consumed rather than intended as a concise failure narrative. It therefore cannot show which read, scan, maintenance operation, or operation phase immediately preceded an assertion. Breadcrumbs fill that observability gap with a bounded, human-readable, per-thread history. They are best effort, never replayed, and remain independent of the recovery-validation contract.
## Previous behavior
db_stress did not retain a bounded per-thread operation history. db_crashtest did not automatically provision a local directory for these artifacts. The first version of this diff kept the ring in process-local memory and periodically wrote a complete temp-file snapshot, which could lose every operation since the last snapshot when the process crashed.
## New behavior
An opt-in per-thread memory-mapped breadcrumb ring records recent operation begin, context, and end events directly in a local file. Committed records therefore remain in the kernel page cache when only the db_stress process exits or is killed; no periodic flush is required. db_crashtest enables breadcrumbs by default, assigns a local diagnostics directory, retains the newest eight process groups after failed child runs, and removes managed artifacts after success.
## Implementation
Each ThreadState lazily creates a uniquely named, preallocated file and maps it through Env::NewMemoryMappedFileBuffer(). The file contains a fixed header and 512-byte text records. A record slot is marked I before its body is replaced and V only after the complete body is published; E denotes an unused slot. Each valid record carries a monotonically increasing sequence number so wrapped records can be ordered. Oversized context is bounded and ends with details_truncated=1. Lazy initialization avoids empty files for auxiliary ThreadState objects, and a process-wide file ID prevents collisions between ThreadState objects that share a logical thread ID.
The periodic-flush flag, temp-file/rename snapshot path, and verification-failure fallback flush are removed. Enabled operation-context formatting continues to reuse a per-thread string and uses std::to_chars for integers; disabled call sites avoid formatting. Diagnostic directory setup and mapping failures are reported but do not change the workload result. Auto-directory pruning and cleanup accept only generated filename schemas, reject symlink roots, preserve unrelated files and explicit user directories, and retain at most eight PID groups.
## Safety and unsupported cases
This is stress-tool-only diagnostic behavior. Raw db_stress keeps breadcrumbs disabled by default. The expected-state recovery trace and scan-witness contracts are unchanged. The mapping deliberately does not use msync or fsync: it is intended to survive a process crash while the kernel remains running, not power loss, host reset, or kernel failure. Filesystems or Env implementations without shared writable mappings disable breadcrumbs non-fatally. Replacing or truncating a live mapped file is unsupported. Records are fixed width, so unusually large operation context is explicitly truncated. Concurrent live readers are not promised a consistent snapshot; post-termination readers must ignore I and E records and order V records by sequence number. Nested operation scopes remain unsupported.
## Tests and limitations
Validated with all 47 tools/db_crashtest_test.py tests, exact integrated debug and release db_stress builds, complete-stack arc lint, lower/upper/combined clang-format checks, normal ring wrap, distinct files for four worker threads, an oversized 256-column-family context, and process-crash tests. A release process killed with SIGKILL retained all 16 committed ring records without an explicit flush. A debugger-forced SIGKILL after the I store but before V publication left the interrupted slot marked I. In five CPU-pinned alternating release pairs of 50,000 operations, enabled and disabled medians were both 2.15 seconds. This remains a bounded latest history rather than a complete log and provides no host-crash or power-loss durability.
## Rebase update
Rebased onto fbsource master dc59f986b964. The logical diff is unchanged; the rebased stack passes debug and release db_stress builds, all 47 db_crashtest Python tests, and arc lint.
## Review follow-up
The mapped-slot invalidation now uses an acquire-release atomic exchange so later body stores cannot become visible before the I marker on weakly ordered architectures.
Reviewed By: anand1976
Differential Revision: D119160910
fbshipit-source-id: ffe172af9ebc75c617196592806dc9641a539a25
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15229
Add `db_session_id` to obsolete file deletion logs and structured table/blob deletion event logs. Also include `db_session_id` when logging new MANIFEST creation so split-brain DBImpl sessions can be correlated without relying on process-local pointers.
Reviewed By: joshkang97
Differential Revision: D120413139
fbshipit-source-id: c175b70d9b1f93e3242ee9e3191c274dc51ef3bf
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15232
**What**: Make `SstFileWriter::Open()` return `InvalidArgument` without replacing the active writer when a file is already open.
**How**: Check `rep_->builder` before constructing new output resources, matching `OpenWithEmbeddedBlobs()` and leaving the original file finishable.
**Why**: A second open otherwise destroys an unfinished `TableBuilder`, violating its lifecycle contract and triggering an assertion.
Reviewed By: pdillinger
Differential Revision: D120469611
fbshipit-source-id: 8daa4607725eea574fe80012f9df98132160ce3e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15230
Add a raw C++ `DB::NewIterators()` overload accepting parallel vectors of `ReadOptions` and column family handles. Each iterator receives its own options while all iterators share one consistent sequence snapshot. Require identical snapshot pointers and tailing modes across the options.
Implement the overload for primary, secondary, read-only, stackable, and write-prepared DBs. Building on D120048299, both secondary overloads share one helper that captures all SuperVersions and the sequence number under the DB mutex.
Preserve read-only `table_filter` support when range-tombstone conversion is configured, and reject that unsafe combination in write-prepared read-write iterators before allocating any iterator.
Add validation, per-CF prefix-seek coverage, a secondary catch-up regression test, table-filter contract tests, and a public API release note.
Reviewed By: xingbowang
Differential Revision: D120296578
fbshipit-source-id: 3775884537ea29a0d32bb3fe10330cf62ac5a406
Summary:
Follow-up (Phase 3) to the experimental lazy wide-column read API (DB::GetEntityLazy / DB::MultiGetEntityLazy). A MultiGetEntityLazy batch now resolves blob references across keys with coalesced I/O instead of key-by-key.
- MultiGetEntityLazy acquires one SuperVersion and one consistent (implicit) sequence number for the whole batch via MultiCFSnapshot (as multi-CF iterators do) and transfers a single shared per-column-family pin into the LazyWideColumnsBatch, replacing the former per-key SuperVersion + explicit-snapshot loop. Single column family for now; a cross-CF ColumnFamily** overload remains a follow-up (the batch already models a per-CF pin map).
- LazyWideColumnsBatch::MultiResolve groups the batch's reads by (Version, blob file) for separate-file references and by SST for embedded references, and issues one coalesced read per group -- for whole-column and byte-range reads alike -- caching whole values in each entity's resolver and pinning partial reads directly.
- Adds the coalesced blob-read primitives this needs (none existed before): range-aware BlobFileReader::MultiGetBlobRange / BlobSource::MultiGetBlobRange / Version::MultiGetBlobLazy for separate-file references, and an embedded (SimpleGen2) batch read path (blob_gen2_format batch readers + BlobSource::MultiGetSimpleGen2Blob[Range] + BlockBasedTable::MultiGetSameFileBlob). Whole-value separate-file coalescing reuses the existing MultiGetBlob. The batch readers sort their requests by file offset before issuing the underlying MultiRead (which requires ascending offsets), and gen2 checksum verification is factored into one helper shared by the scalar and batch readers.
- The hot non-lazy Get / MultiGet / iterator / compaction paths are untouched: the new multis are lazy-only, and the GetImpl change is gated on a new opt-in GetImplOptions field (an injected shared SuperVersion + sequence number) used only by the batched lazy read.
- Blob read failures on these paths (short reads, invalid offsets, out-of-range requests, compression mismatches) now carry file/offset/size and expected-vs-actual context, mirroring the block-checksum diagnostics.
- db_bench gains a multireadrandomentitylazy benchmark for measuring the coalesced batch path.
No public API change, so this is a performance follow-up to the still-recent lazy API rather than a new feature. EXCEPT that `LazyColumnReadRequest::status == nullptr is now explicitly disallowed`.
Left for later follow-up:
- When a whole fetch is planned for an (entity, column), bind ordinary range requests for that column to the same fetch and slice them after adoption instead of issuing separate range I/O.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15178
Test Plan:
New unit tests in db/wide/db_lazy_entity_test.cc assert cross-key coalescing via a blob-file / SST MultiRead counter added to the test FileSystem wrapper:
- N whole-column separate-file reads across keys in one blob file collapse to a single coalesced MultiRead (and the fetched values are cached, so a repeat read does no further blob I/O);
- N byte-range separate-file reads collapse to one MultiRead, save the un-read bytes (rocksdb.blobdb.lazy.partial.bytes.saved), and never fill the blob cache;
- embedded (same-file) reads across keys in one SST collapse to one MultiRead over that SST;
- an out-of-order embedded batch (reads issued in descending record offset) still resolves correctly in one coalesced read, covering the batch readers' internal offset sort;
- a mixed batch (inline + whole + byte-range across keys) returns the correct bytes for each read;
- a batched MultiGetEntityLazy + one MultiResolve matches resolving each key via a separate GetEntityLazy. The existing db_stress lazy-vs-eager differential (MaybeTestMultiGetEntityLazy) exercises the new batched read and cross-key MultiResolve unchanged (same public API).
Reviewed By: xingbowang
Differential Revision: D118660682
Pulled By: pdillinger
fbshipit-source-id: 6a832175e730917eddeedab8e0b9cb2b8d6ae7b4
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15211
Capture the secondary DB sequence number and all requested column-family SuperVersions while holding the DB mutex, then construct the iterators after releasing it. This keeps every child on one state even when `TryCatchUpWithPrimary()` concurrently installs file-backed SuperVersions.
Add a file-only catch-up regression test and release note.
Reviewed By: xingbowang
Differential Revision: D120048299
fbshipit-source-id: 89f4de533c4fbc1c6e86656845bda4bbb9da4d2c
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15215
**What**: Stop `SstFileDumper` from dereferencing a null file handle on two error paths, both of which crash `sst_dump` today.
**How**: Both sites create a file and then use the handle without checking whether creation succeeded. `Env::NewWritableFile()` and `FileSystem::NewRandomAccessFile()` each reset the caller's handle before they can fail, so on failure the handle is null.
- `DumpTable()` fell through to a shared error path that closed the handle. It now returns as soon as `NewWritableFile()` fails.
- `GetTableReader()` re-opens plain and cuckoo tables with mmap enabled and discarded the returned status entirely, then wrapped the null handle in a `RandomAccessFileReader` that the next property read dereferenced. It now propagates the status.
**Why**: Both are reachable from the `sst_dump` tool. The output file cannot be created on an unwritable directory, a missing parent, or a full filesystem; the mmap re-open fails when mmap is unavailable or the file changed between the two opens. In each case the tool crashed instead of reporting the error. The plain and cuckoo branch had no test coverage at all, which is why its unchecked status never tripped an `ASSERT_STATUS_CHECKED` build.
Reviewed By: xingbowang
Differential Revision: D120182509
fbshipit-source-id: dbe9b7f84140ed11dea395f4192b895e0a7bc70d
Summary:
Extends `sst_dump --command=recompress` from a basic evaluation tool to a serious compression benchmarking tool, also able to evaluate interactions with table format options affecting uncompressed sizes and compressibility.
For each input file it now emits a benchmark "baseline" entry for the file as stored, followed by one entry per compression type / strategy / level, reporting compressed and uncompressed data-block sizes, index sizes, compression ratio, and both wall-clock and CPU time for the write and read tests. All data/index sizes are block payload excluding block trailers, so compressed vs uncompressed figures are on a consistent basis. The input's uncompressed data size is inferred from decompression statistics, so it is available even for files that don't persist it. That inference is exact when the input's index blocks are stored uncompressed (the default); with enable_index_compression it can be slightly overstated because index-block decompression is also counted (see the CAVEAT in GetInputBenchmarkMeasurement).
New capabilities:
* `--json` for machine-readable output. It is self-contained: each measurement carries the compression manager id and the full CompressionOptions, and every baseline/measurement entry is tagged with its input file path so multiple input files produce one valid document.
* `--compression_strategy=` to sweep a list of CompressionOptions strategy values (multiplied with levels), useful for side-channel configuration of custom compressors.
* `--show_properties` additionally dumps the input and last recompressed output TableProperties.
To keep measurements meaningful, a quiet throw-away priming build runs whenever the compression type changes (warming up the compressor), CPU time is measured process-wide so parallel compression worker threads (compression_parallel_threads > 1) are included rather than undercounted, and the timed windows are kept tight: the write build is fed from an in-memory copy of the input so its timing excludes reading and decompressing the input (and is independent of block-cache state), and the read timing covers only the block reads. The timings for compression and decompression do include BlockBasedTable overheads but this is considered reasonable--if not preferable--to the intended application of finding the overall most efficient settings related to compression.
Also fixes a latent bug exposed by measuring several compression types in one run: the recompressed table was read back through the shared block cache under a common (unknown) cache-key identity, so a later type could get a cache hit on a stale block from an earlier, differently-compressed table and fail with "Corruption: bad entry in block". The read-back now bypasses the block cache.
Also fixes a typo in CLAUDE.md.
Intended follow-up: persist TableProperties::uncompressed_data_size. It is already computed in memory at build time but is never written to the properties block (PropertyBlockBuilder::AddTableProperty has no entry for it), so it reads back as 0 for on-disk files. Persisting it (backward compatible; old files read 0) would fix `data uncompressed size: 0` for all property readers and let this tool read the value directly, removing the decompression-ticker inference above and its index-compression caveat for all but pre-existing files.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15195
Test Plan:
Added sst_dump_test cases exercising recompress with --show_properties and with --json.
Ad hoc manual testing against real SST files: verified the text benchmark table, and piped `--json` output through `json_pp` to confirm well-formed JSON, for a single file, a directory of multiple SST files, and combined with --show_properties, --output_hex, --compression_strategy, and --block_based_table_options. Confirmed process CPU time exceeds wall-clock write time with --compression_parallel_threads=4 (and matches at =1).
Reviewed By: joshkang97
Differential Revision: D119536444
Pulled By: pdillinger
fbshipit-source-id: 30dbabc14872b9c6f7d8ca11227e762674dcef02
Summary:
Compressor::GetRecommendedParallelThreads() was a narrow hook for the one CompressionOptions field (parallel_threads) that the SST builder, rather than the Compressor, is responsible for applying. Other such options exist (max_compressed_bytes_per_kb, auto_skip, auto_skip_min_sample_every), and more may be added. This replaces the narrow hook with an omnibus MaybeOverrideCompressionOptions(CompressionOptions*) that a Compressor can override to steer any of them. GetRecommendedParallelThreads() is now deprecated; the default implementation of the new method still honors an overridden GetRecommendedParallelThreads(), so existing custom Compressors keep working. CompressorWrapper forwards the new method; built-in compressors are unchanged (picked up via the default bridge).
The block-based table builder now consults the data-block compressor (falling back to the base compressor before the data-block compressor exists, e.g. during dictionary sampling, where parallel compression does not start anyway) and applies the possibly-overridden options. While here, the builder records the effective (post-override) options in the rocksdb.compression_options table property instead of the pre-override configured options, and the two affected Rep members (compression_parallel_threads, max_compressed_bytes_per_kb) are set exactly once from the effective options -- with poisoned defaults so any use before they are computed surfaces -- rather than initialized in the member list and overwritten later.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15177
Test Plan:
Added DBCompressionTest.CompressorOverridesCompressionOptions, which installs a Compressor that overrides MaybeOverrideCompressionOptions() to force parallel_threads and verifies via the builder's sync point that parallel compression starts with the overridden thread count, then reads the data back. The existing GetRecommendedParallelThreads and
ConfiguredCompressionTypeRecordedInProperties tests continue to cover the deprecated bridge and the table-property recording respectively.
Reviewed By: joshkang97
Differential Revision: D118656406
Pulled By: pdillinger
fbshipit-source-id: 8a5d7cde90ca38913525a7ac11650607fa0bce58
Summary:
Let the built-in binary, hash, and partitioned indexes be created through `IndexFactory`. The wrappers preserve the existing internal-key handling and multi-block finish path.
Pass the common-prefix flag to every builder, including both levels of a partitioned index. The regression test checks the prefix encoding and decoded keys with the flag on and off.
The factory tests pass on the original PR plus the fix. Focused tests on the full stack also pass with `ASSERT_STATUS_CHECKED=1` and `COERCE_CONTEXT_SWITCH=1`. Full `make check` runs had test timeouts and hit the 30-minute local limit.
Part 4 of 9. Previous: https://github.com/facebook/rocksdb/issues/14960. Next: https://github.com/facebook/rocksdb/issues/14962. The first three parts have landed.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14961
Reviewed By: jaykorean
Differential Revision: D119493650
Pulled By: xingbowang
fbshipit-source-id: 7d43cc456de86e8bd35e4b0689fb3d256608cf2f
Summary:
Why this change is required:
Block iterators parse persisted SST bytes. Truncated headers, malformed restart arrays, and invalid key or value lengths are runtime corruption, not private control-flow failures. They must be rejected safely in release builds before any out-of-range read, pointer arithmetic, or Slice construction.
Previous behavior:
DecodeEntry relied on an assertion before reading its three-byte fast-path header. Strict entry-span validation was enabled only for selected meta-block paths. Restart offsets were not all validated up front, and several separated-key/value bounds assumptions were asserted only after pointers or slices had already been constructed. Because assertions disappear under NDEBUG, malformed blocks could trigger out-of-bounds access or incorrect iteration in release builds.
New behavior:
All block iterators now reject truncated entry headers, keys or inline values extending past the key section, invalid separated-value offsets or lengths, and malformed restart arrays with Status::Corruption. Block construction validates every restart offset for section bounds and strict ordering before an iterator can use it.
Implementation:
Remove the StrictCheck template distinction and apply the span checks to every BlockIter. Validate restart keys before constructing their Slice, validate separated values before pointer arithmetic, and scan the restart array once during Block construction. Mark corruption-only branches UNLIKELY and combine the inline key/value span into one overflow-safe uint64_t comparison so valid data remains on the compiler hot path.
Safety boundaries and unsupported cases:
These checks are unconditional because they are required for safe parsing of persisted bytes; they are not gated by paranoid_checks or checksum verification. Existing assertions covering private iterator state and post-validation relationships remain. The change does not alter the SST format or repair corrupted blocks; it detects malformed blocks and reports corruption.
Performance:
Both revisions were explicitly cleaned and rebuilt with DEBUG_LEVEL=0, then measured on one pinned AMD EPYC Genoa CPU using 2 million 16-byte keys, 32-byte values, 4 KiB blocks, restart interval 16, and no compression. Hardware cycle counts are candidate versus parent, with lower being better:
- Cached random reads: 47.68B versus 47.69B cycles (-0.03%).
- Random seeks plus forward/reverse scans and batched MultiGet: 70.26B versus 69.96B cycles (+0.43%).
- mmap random reads without block cache: 16.92B versus 17.19B cycles (-1.55%).
- Separated-KV random plus sequential reads: 30.44B versus 31.20B cycles (-2.43%).
No tested workload regressed. These are targeted single-thread CPU microbenchmarks rather than a guarantee for every production workload.
Tests performed and known limitations:
The corruption tests cover truncated headers, entries extending past key or value sections, invalid separated-value offsets, and out-of-range or non-increasing restart offsets in both inline and separated-KV layouts. They verify detection rather than recovery or repair of a corrupted block.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15200
Test Plan:
Built block_test and ran the 14 BlockEntryCorruption and separated-KV corruption tests under ASSERT_STATUS_CHECKED=1 and for 100 repetitions with COERCE_CONTEXT_SWITCH=1. Ran make check-sources.
Ran make clean and built db_bench with DEBUG_LEVEL=0 for both this commit and its parent. Compared cached random reads, random seeks, forward and reverse scans, batched MultiGet, mmap reads without block cache, and separated-KV variants using the configuration documented above.
Reviewed By: pdillinger
Differential Revision: D119668019
Pulled By: xingbowang
fbshipit-source-id: 1594379acefa047f9598df341e675340831066ee
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15188
In the pointer branch of `lossless_cast`, the destination type check tested
`std::is_enum_v<To>` instead of `std::is_enum_v<ToDeref>`. Since `To` is a
pointer type in that branch, `std::is_enum_v<To>` is always false, so the
check collapsed to `std::is_integral_v<ToDeref>` and rejected casting to a
pointer-to-enum, e.g. `lossless_cast<MyEnum8*>(&some_uint8)`.
Reviewed By: xingbowang
Differential Revision: D119422904
fbshipit-source-id: b1f4e205d536ba3d10563fa62adb04867fae02ac
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15086
Why: An application built on top of RocksDB could use cache population reads through explicit snapshots and needs to know when the returned value has already been superseded, without a second point read.
Previous behavior: Get and MultiGet returned the snapshot-visible value and optional timestamp but could not report a newer point update or covering range deletion.
New behavior: experimental C++, C, and Java metadata-read APIs report newer committed puts, deletes, merges, and covering range tombstones; ordinary primary-DB writes completed before the read are guaranteed to be reported, racing writes remain best-effort, and true has no false positives.
Implementation: metadata reads widen lookup behind read-callback hooks while keeping metadata-only state off passive common object layouts; per-key state stays in KeyContext/GetContext, ordinary MultiGet row-cache replay semantics are preserved, BlobDB/TTL use shared output helpers, dead single-CF plumbing and an invalid cross-key stress assertion are removed, and the range-tombstone ordering invariant is documented without changing behavior.
Safety and unsupported cases: tracking is opt-in and requires an explicit snapshot; persisted-tier reads, custom transactional visibility callbacks, secondary DBs, and unsupported subclasses return NotSupported, while read-only/compacted DBs return false because their in-process views are static.
Tests and limitations: 1,265 Buck tests plus focused metadata, 100x coerced-context-switch row-cache, write-committed/write-prepared crash tests, builds, and lint passed. Current-parent release db_bench found no material passive regression: readrandom +0.65%, MultiGet batch 4 -1.40%, and batch 32 +0.48%.
Reviewed By: pdillinger
Differential Revision: D104182397
fbshipit-source-id: 36f7be1cd50fe2383689adaae31a6bb7d7e462dc
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15197
**What**: Compare the excluded-file lookup iterator against the engine it was obtained from, in `BackupEngineImpl::RestoreDBFromBackup()`.
**How**: The loop walks `locked_restore_from_dirs` calling `find()` on each engine's `backuped_file_infos_`, but tested the result against `this` engine's `end()`. Test against the same engine's `end()`.
**Why**: Comparing iterators obtained from different containers is undefined. It is inert here rather than a live bug: on libstdc++ and libc++ an `unordered_map` end iterator is a null sentinel that is not container-specific, so a miss compares equal and a hit does not. On an implementation with a container-specific end iterator - MSVC, a required CI axis - a miss would read as found and dereference the sentinel node. No test can pin this; see the test plan.
Reviewed By: xingbowang
Differential Revision: D119585045
fbshipit-source-id: 9ff0e770b426f6a2c4a0fdad261910a927d1bfa7
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15189
Add `CoroDB::CoGet` and `CoMultiGet` overloads for standalone `SstFileReader` instances. Reuse a shared sync/coroutine implementation on the private reader `Rep`, configure and validate the read executor at `Open`, and retain synchronous fallback when unavailable.
Reviewed By: xingbowang
Differential Revision: D119431444
fbshipit-source-id: 1ac1926b6a2ffc11cd3e91a438a8c62cff5d57ec
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15196
**What**: Fix heap corruption and out-of-bounds reads in `SstFileReader::MultiGet()` when the request exceeds `MultiGetContext::MAX_BATCH_SIZE` keys.
**How**:
- Batch the lookup in `MAX_BATCH_SIZE` slices over the sorted keys, with a fresh `MultiGetContext` and `TableReader::MultiGet()` call per slice. Caller ordering survives because each `KeyContext` already points into the caller's status and value vectors.
- `reserve()` the `key_context`, `get_ctx` and `merge_ctx` autovectors, as `DBImpl::MultiGetCommon` does: the setup loop takes addresses of their elements, and past its inline capacity an `autovector` spills into a `std::vector` that reallocates as later keys are appended.
- Only the slicing is borrowed from `DBImpl::MultiGetImpl`. Its cross-batch value-size accumulation and per-batch deadline check are deliberately not replicated: value size is accumulated only under `db/` and never by a `TableReader`, and the deadline already reaches every read through the table reader's I/O options.
**Why**: `MultiGetContext` caps at `MAX_BATCH_SIZE` keys but only asserts it, so release builds hit a bad `delete[]` from its mismatched `LookupKey` construct/destroy bounds, an out-of-bounds read of its fixed-size `sorted_keys_` array, and an out-of-range shift once the key count exceeds the width of its range mask; `include/rocksdb/sst_file_reader.h` documents no key-count limit, so any caller of this public API could reach it.
Reviewed By: xingbowang
Differential Revision: D119401930
fbshipit-source-id: 3bbafd72badaa944dbb94f4ab6ecb7f14a11f6d0
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15101
This diff contains all changes under fbcode/internal_repo_rocksdb/repo for the RocksDB regression benchmark observability work.
It adds opt-in per-interval latency percentile reporting to db_bench. When --report_interval_percentiles is enabled with histogram and a positive stats interval, each worker records into pre-created per-operation interval histograms and counters. A background reporter periodically merges and resets those thread-owned stats, skips excluded background-writer stats, performs a final partial-interval flush, and emits IntervalPercentiles lines with sample count, operation count, and P50/P75/P99/P99.9/P99.99 latency. Worker updates and reporter snapshots use a double-buffer handoff, so the reporter never reads or clears a histogram still being updated. The legacy histogram timing path remains unchanged when interval reporting is disabled.
The interval implementation uses RocksDB UnorderedMap aliases for the hot lookup maps and UnownedPtr for non-owning reporter references.
It also aggregates each worker thread PerfContext and IOStatsContext into the final merged benchmark Stats and emits final PERF_CONTEXT and IOSTATS_CONTEXT blocks, so multi-thread benchmark metrics represent all workers rather than one thread.
For options-file runs, explicit benchmark-critical CLI overrides are applied after loading the file. This includes --disable_auto_compactions, fixing an existing precedence bug where disable_auto_compactions=false in the options file silently overrode an explicit CLI true value. The regression harness relies on that CLI setting, so the bug allowed automatic compactions during origin construction and write-heavy benchmarks. The regression test persists false in an options file, passes --disable_auto_compactions=true, reloads the opened DB options, and verifies the effective value is true.
One shared WriteBufferManager is created after option loading and reused by all DBs, enforcing the intended aggregate memory cap for multi-DB benchmarks.
The compactall benchmark now accepts --compact_all_parallelism. It compacts independent DBs through a bounded worker set, applies --subcompactions to each DB, emits per-DB progress and elapsed time, and propagates the first CompactRange failure after joining all workers. Its default parallelism remains 1 for backward compatibility.
The dependent harness diff enables these features, parses their output, and publishes the resulting interval and final metrics.
Reviewed By: pdillinger
Differential Revision: D116025321
fbshipit-source-id: d44185ba2110aacbe9fadc65a59799bf8c1c54f0
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15179
A remote compaction worker (`DB::OpenAndCompact()`) opens the *live* primary's DB directory read-only, and that open reads `CURRENT`. The primary publishes each MANIFEST rotation by writing a new `CURRENT` under a temp name and renaming it over the old `CURRENT`. POSIX keeps an already-opened `CURRENT` readable after that replacement, but file systems without read-after-unlink semantics (remote or object stores) do not: the replaced object is reported as gone, surfacing in RocksDB as `IO error: No such file or directory` (`Status::PathNotFound`). The secondary open was not retried, so this transient race failed the whole compaction. There is an equivalent, already-recognized race one step later for the MANIFEST file (`ReactiveVersionSet::MaybeSwitchManifest()` converts it into `Status::TryAgain`), which was fatal here for the same reason.
`DB::OpenAndCompact()` now retries the secondary open (up to 3 attempts) when it fails with `TryAgain` / `PathNotFound` / `NotFound` -- exactly the statuses a concurrent `CURRENT`/MANIFEST replacement produces. The retry limit is configurable via `OpenAndCompactOptions::max_secondary_open_retries`. Genuine failures are still returned after the bounded retries, and no safety check is weakened or skipped.
Found by a `db_stress` crash test failure. Two non-default options in that run make this reachable and fatal:
* `remote_compaction_worker_threads=8` with `remote_compaction_failure_fall_back_to_local=0` -- the latter was only recently randomized to 0; before that a remote compaction failure was always silently retried locally. Now `DbStressCompactionService::Wait()` maps the failure to `kFailure`, which becomes a sticky background error on the primary, so every subsequent `Flush()` returns it and `StressTest::ProcessStatus()` aborts with `assert(false)`.
* `max_manifest_file_size=8192` -- the primary rotates MANIFEST (and therefore replaces `CURRENT`) constantly, which is what makes the race window hit in practice.
Full chain: primary renames a new `CURRENT` over the old one -> the file system reports the replaced object as gone for the reader that already resolved it -> `GetCurrentManifestPath()` inside the worker's secondary open fails with `IO error: No such file or directory` -> `DB::OpenAndCompact()` returns that error with no retry -> `kFailure` (no local fallback) -> primary background error -> `Flush failed: IO error: No such file or directory: ... /CURRENT ...` -> assertion failure in `db_stress`.
Reviewed By: joshkang97
Differential Revision: D118480192
fbshipit-source-id: a2618ee24e582e2e713af04464ee1e4038925ccb
Summary:
**What**: Fix an out-of-bounds read and a forged end timestamp in the timestamped `SstFileWriter::DeleteRange()` overload.
**How**:
- `Rep::DeleteRange()` now builds each boundary through a new `Rep::AppendTimestamp()` helper, which tests one key's adjacency to `timestamp` and either aliases the two or materializes `key + timestamp` into a caller-owned buffer.
- Drops the fast path's `assert`. It was never a guard: it performed the same out-of-bounds read it was meant to police, and `DeleteRangeImpl()` already asserts the two boundary timestamps match.
- Behavior change for callers whose two keys each carry their own trailing timestamp: they now take one extra copy. Aliasing is still used for whichever key is genuinely adjacent.
**Why**: A single `timestamp` buffer can be byte-adjacent to at most one of two distinct keys, but the old branch tested `end_key` using `begin_key`'s length and then widened both slices whenever either test passed - so an asymmetric caller, one key contiguous with the timestamp and the other bare, read past an allocation and persisted a range tombstone carrying foreign heap bytes as its end timestamp. Nothing forbids that layout: the public contract requires only that the keys carry no timestamp.
Reviewed By: xingbowang
Differential Revision: D119401932
fbshipit-source-id: 4c842d4afbe7bd7c4eabc68c6aee0543cdee2c9c
Summary:
`ShardedCache::ApplyToAllEntries()` clamped `average_entries_per_lock` with `std::min(aepl, 1)`, forcing every positive value (including the default 256) down to 1. The intent was a floor of 1 (`std::max`); `ApplyToSomeEntries` even asserts the value is > 0. As a result a full cache traversal advanced a single hash-table slot per iteration step instead of batching ~256, so the documented 256 default never actually took effect. The bug dates to the original API (https://github.com/facebook/rocksdb/issues/8225) and was carried through the ShardedCache refactor (https://github.com/facebook/rocksdb/issues/10801) unchanged, so the intended default was never exercised: in hindsight the "gather stats" latency in the https://github.com/facebook/rocksdb/issues/8225 test plan is a batch-size-1 measurement.
Despite its name, `average_entries_per_lock` is really a batch size for iterating the cache, not something specific to locking: it bounds the work done per uninterrupted step. For a lock-based cache (LRUCache) a shard mutex is held for each batch, so single-entry batches mean many extra lock acquire/release cycles; but lock-free implementations (HyperClockCache) also pay fixed per-batch overhead and benefit from the fix as well. This is a control-path cost (e.g. periodic block cache stats via DumpStats -> CollectCacheEntryStats), not hot-path read/write, so the practical impact is usually minor. The option's doc comment is updated to describe it as a batch size; the field is not renamed given how obscure the option is.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15186
Test Plan:
No unit test: asserting on batch/lock-acquisition counts would be intrusive/awkward, and LRUCache is disfavored (though not deprecated).
Measured with release-mode cache_bench, comparing the binary built before (`std::min`) vs after (`std::max`); 4 GiB cache, 1 KiB values, ~3.7M entries, default gather_stats_entries_per_lock=256. Average full ApplyToAllEntries traversal latency:
| cache_type | before | after | speedup |
|-------------------------|---------|---------|---------|
| lru_cache | 2.69 s | 0.64 s | ~4.2x |
| fixed_hyper_clock_cache | 0.441 s | 0.138 s | ~3.2x |
| auto_hyper_clock_cache | 0.264 s | 0.136 s | ~1.9x |
All implementations improve; LRUCache most, due to per-batch mutex acquire/release. HCC traversals are already ~6-10x cheaper than LRU in absolute terms, and entries visited are identical before and after.
Reviewed By: rban1
Differential Revision: D119256456
Pulled By: pdillinger
fbshipit-source-id: e58f7f97d47d8970ebc8c3ab786199132ce6340d
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15181
A crash test failed when `use_async_db_api=1` created read-executor workers on a host unable to allocate io_uring rings. Folly logged the setup failure and threw; RocksDB caught it and successfully fell back to synchronous reads, but the harness failed because stderr was non-empty.
Representative stderr:
```
WARNING: Logging before InitGoogleLogging() is written to STDERR
I0903 14:05:39.470695 286445 IoUringBackend.cpp:527] io_uring_queue_init_params(512,1024) failed errno = 0:"Success" 0x7d2e06295280 retrying with capacity = 512
E0903 14:05:39.470759 286445 IoUringBackend.cpp:537] io_uring_queue_init_params(512,512) failed ret = -12:"Unknown error -12" 0x7d2e06295280
```
Initialize glog in the Folly-enabled OSS `db_stress` entry point and centralize known stderr sanitization. Only `IoUringBackend` ring-initialization failures are moved to stdout when the async DB API is enabled; existing expected post-SIGTERM errors use the same sanitizer. All unrelated stderr remains fatal.
___
Differential Revision: D118836521
fbshipit-source-id: c9ce05896f417c7723c5cd73d293fbb1cd14ee1e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15142
Coroutine statistics use TLS, but coroutine reads can interleave on the same executor thread. Previously, when a stats-enabled request suspended, it saved its counters without disabling the executor's TLS configuration. A stats-disabled request running next could inherit those enabled settings and collect statistics unexpectedly. Stats setup also lived in individual DB implementations, so wrapper early returns and future stackable DB implementations could bypass it.
Move statistics ownership to the public `CoroDB` and callback-based async read boundaries, before virtual dispatch. Each operation captures its caller's configuration, enabled requests preserve their counters across suspensions, and every suspension or completion leaves executor TLS disabled. Because the scope wraps stackable DB dispatch, current and future `CoroStackableDB` implementations automatically inherit the correct behavior without adding their own stats reset or scope.
For consistency between coroutine and callback-based reads, the experimental callback API now uses TLS for both configuring and consuming statistics. `AsyncCallback::OnComplete()` no longer receives context arguments; RocksDB publishes the completed per-operation counters to TLS before invoking it.
```
Before
time -------------------------------------------------------------->
stats-on A: onSet(enable A) -- work -- onUnset(save A) ...... onSet(A)
executor TLS: [A enabled] ------------> [still enabled] ------> [A enabled]
stats-off B: run
^ inherits A's enabled TLS
After
time -------------------------------------------------------------->
stats-on A: onSet(enable A) -- work -- onUnset(save A, disable) ... onSet(A)
executor TLS: [A enabled] ------------> [disabled] ----------------> [A enabled]
stats-off B: run
^ remains stats-disabled
```
Reviewed By: xingbowang
Differential Revision: D117017089
fbshipit-source-id: 8a85f7ba43e3b73225995f5d0b79da3892baafb9
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15170
`warm_storage_tsan_liveness_crash_test` fails with
```
error : Get() returns IO error: Failed to read 1032 bytes from file
ws://ws.zdb.ldc0zdbtest1/.../000093.sst from offset 5461, message
[0 code=DEADLINE_EXCEEDED isRetryable=1 ... error=WSE_BLOCK_READ_TIMEOUT
classification=TRANSIENT_ERROR ...] for key: 0000...78 (30280).
Verification failed :(
```
The Warm Storage error is explicitly `classification=TRANSIENT_ERROR isRetryable=1`, and the DB artifact confirms there is no RocksDB problem: `000093.sst` is intact (37760 bytes, downloaded successfully after the run) and the DB `LOG` reaches `Shutdown complete` with zero IO-error or corruption entries. This is exactly the class of failure `--tolerate_non_injected_io_errors_for_remote_dbs` was added for (D112136438), but that mechanism never actually took effect. Two independent defects:
**1. The triggering non-default option: `tolerate_non_injected_io_errors_for_remote_dbs=0` on a remote DB.**
`db_crashtest.py` hard-coded the parameter to `0` in `default_params` and only ever *force-disabled* it (for local DBs), leaving each remote job to opt in on its own command line. `rocks/sandcastle/test-driver.sh`'s `warm_storage_*crash*` branch never passed it, so every Warm Storage crash-test job ran with the flag off and a single transient read timeout failed the run. Fix: the parameter now defaults to `1` whenever `--env_uri` / `--fs_uri` names a remote backend, so no remote job has to remember to opt in. A caller can still pass `0` explicitly, and `finalize_and_sanitize()` still force-disables it for local DBs so genuine local IO errors (e.g. io_uring failures) are never masked.
**2. `injected_error_count == 0` made the flag a no-op in `no_batched_ops_stress.cc`.**
All five read-path guards were
`injected_error_count == 0 || !IsErrorInjectedAndRetryable(s)`.
`IsErrorInjectedAndRetryable()` covers *non-injected* remote IO errors, which by definition have no injected fault to correlate with — and the `liveness` profile forces every `*_fault_one_in` to `0`, pinning `injected_error_count` to `0` for the whole run. So the left operand short-circuited to true and the error was reported as a verification failure regardless of the flag. Fix: extract `StressTest::IsTolerableNonInjectedRemoteIOError()` and add `StressTest::IsRetryableOperationError(injected_error_count, s)`, which gates the injected-error branch on `injected_error_count > 0` but lets a tolerated non-injected remote IO error through unconditionally. The five call sites now use the helper.
With the tolerate flag off (all local runs), the new guard reduces algebraically to the old expression, so local behaviour is unchanged. The genuine consistency-check gate at `no_batched_ops_stress.cc:1146` (`do_consistency_check && injected_error_count == 0`) is intentionally left alone.
Fixes T286575055.
[Session trajectory link](https://www.internalfb.com/intern/devai/devmate/inspector/?id=9646c050-6791-4ac3-9abb-3ee110dcb54e)
Reviewed By: pdillinger
Differential Revision: D117886823
fbshipit-source-id: d7591472db8ee6e03c9d9f018480409b6d802838
Summary:
Remote compaction writes output SSTs into caller-selected job directories. If the primary crashes before installation or falls back to local compaction, those directories can outlive the DB session and accumulate under the DB directory.
This change defines `kSessionTmpDirName` as `session_tmp`, the canonical name for a DB-local temporary directory whose contents have the lifetime of a DB session. The immutable boolean option `use_session_tmp_dir_for_remote_compaction` opts remote compaction into this directory. When enabled, an `OpenAndCompact()` directory name is resolved as `<dbname>/session_tmp/<job>`.
During DB open, RocksDB creates the session temporary directory, completes recovery, and then synchronously deletes contents left by the previous DB session before scheduling compactions. `DestroyDB()` removes it as part of database deletion, and normal completion, failure, and cancellation paths clean the resolved per-job directory.
The option defaults to false. Disabled callers retain the existing `<dbname>/<job>` layout and no session-directory cleanup. For rollout compatibility, legacy callers that already pass a DB-rooted output path are accepted unchanged only while the option is false. Directory creation and invalid-path errors are delegated to the configured filesystem rather than prevalidated by RocksDB.
After D118301531 landed, per-job directory creation and resumption cleanup are kept together in `InitializeCompactionWorkspace()`. `OpenAndCompact()` resolves the client-provided name and expects DB open to have initialized the session temporary directory when the feature is enabled.
The boolean is wired through C++ and generated C APIs, option serialization and reconstruction, `OpenAndCompact()`, ldb, db_bench, and db_stress. Tests cover path resolution, cross-session deletion, ownership boundaries, disabled behavior, `DestroyDB()`, configured compaction output, resumption fallback, and option round trips.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15169
Reviewed By: pdillinger
Differential Revision: D118227112
fbshipit-source-id: 01aa834b0b3f75638102130a86d95d537a2797bd
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15173
Add an opaque, non-owning `ReadOptions::custom_context` pointer that is passed to table implementations so external table readers can consume application-defined read options. Document its lifetime and synchronization contract, and keep the unsupported pointer out of generated C bindings.
Reviewed By: xingbowang
Differential Revision: D118341098
fbshipit-source-id: e51185f58b5ce80d8fd07efab02a05dbc8c7bd12
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15143
## Summary
Adds a RocksDB blog post describing the native async/coroutine read APIs introduced in RocksDB 11.10.0. It covers the callback and `folly::coro::Task` interfaces, differences from `ReadOptions::async_io`, executor and I/O-completion handoffs, filesystem integration, request-local statistics across suspension, and API lifetime constraints.
The post includes three explanatory diagrams and benchmark results comparing synchronous and coroutine point reads across task counts and `MultiGet` batch sizes.
https://www.internalfb.com/intern/px/p/cDGqv/
Reviewed By: xingbowang
Differential Revision: D117159178
fbshipit-source-id: 5452e6b07e29abb5594505021995e9b5bac9d198
Summary:
Follow-up to the "UPDATE" note on D116738823 (PR https://github.com/facebook/rocksdb/issues/15133, "Faster and safer MANIFEST recovery for remote compaction (OpenAndCompact)"). That change observed that the crash test does not really cover remote compaction because `remote_compaction_failure_fall_back_to_local` is pinned to 1 in db_crashtest.py, so any remote compaction failure is silently retried locally instead of failing the test -- masking existing failures. Addressing that was called out as a high-priority follow-up (tracked under T232588967, "Rocksdb Crash Test failed: Remote Compaction").
This does the cited enablement and fixes the first masked failure it surfaces:
* Crash test: randomize `remote_compaction_failure_fall_back_to_local` between 0 and 1 (the TODO(jaykorean)), so remote compaction failures actually surface instead of always being hidden by a local fallback.
* Fix a real OpenAndCompact bug the enablement immediately exposed. When a compaction is scheduled with `allow_resumption=true` but resumption is internally disabled because it is incompatible with output-hash verification (`paranoid_file_checks=true`, or `verify_output_flags` requesting iteration verification), the worker started a fresh compaction but did NOT clean the output directory. The caller, having requested resumption, does not empty the directory either, so a compaction that was canceled after writing some output and then retried reused the same output file numbers and collided with the leftover files -- surfacing under the crash test's fault-injection FS as `NewWritableFile violates no-reopen-for-write contract`, which became a background error and failed subsequent writes. The fix honors the documented `OpenAndCompactOptions::allow_resumption=true` fallback contract: when resumption is requested but cannot be fulfilled, clean any leftover progress and output files before starting fresh (reusing the existing Scan/Cleanup helpers), so the fresh compaction always begins from a clean directory. When `allow_resumption=false`, the caller still owns emptying the directory (unchanged).
No known production exposure: this bug can only bite a caller that requests `allow_resumption=true` on a column family where output-hash verification is enabled (`paranoid_file_checks=true`, or `verify_output_flags` including `kVerifyIteration`), because only that combination disables resumption while the (uncleaned) output directory is left to the caller. Auditing how the option is used across internal deployments found nothing in that state: the deployments that enable resumption for remote compaction do not enable those iteration/paranoid verification modes, and where remote-compaction output verification is enabled at all it is block-checksum-only, which keeps resumption active and never hits the buggy path. So this is effectively a latent-bug fix -- it prevents a failure that would appear if iteration/paranoid output verification were later turned on for a resumption-enabled remote-compaction fleet.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15168
Test Plan:
Added compaction_service_test.cc unit test
`CleansOutputDirWhenResumptionDisabled`, which drives OpenAndCompact with `allow_resumption=true` and `paranoid_file_checks=true` after seeding a leftover output SST, and asserts the leftover is cleaned and the compaction succeeds. It was confirmed to FAIL without the fix (leftover retained) and pass with it.
Local crash test runs (blackbox, `remote_compaction_worker_threads=8`, `remote_compaction_failure_fall_back_to_local=0`):
- Without the fix, the collision-trigger config (`allow_resumption_one_in=1 --paranoid_file_checks=1`) fails within the first ~60s iteration on the no-reopen-for-write contract violation.
- With the fix, that same config ran ~10 min clean, and a full-random config (everything else randomized) ran ~15 min clean, both with 10-15 kill/reopen cycles plus a final crash-recovery verification. More local crash test runs are planned to shake out any remaining, rarer masked failures now that the fallback no longer hides them.
Reviewed By: jaykorean
Differential Revision: D118301531
Pulled By: pdillinger
fbshipit-source-id: ceb9683ae06ce930ea78bafa3585ad616012243f
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15163
Reintroduces the `optimize_manifest_for_recovery` optimization for `OPTIONS-*` and temp files without letting `VersionSet::NewFileNumber()` collide with files already present in the DB directory.
Recovery now distinguishes between files that require a persisted `SetNextFile` edit and files that only need the in-memory allocator reserved. If only `OPTIONS-*` or temp files are above the recovered `next_file_number_`, recovery advances `next_file_number_` in memory and still skips the MANIFEST edit.
Reviewed By: jaykorean
Differential Revision: D117887276
fbshipit-source-id: f211fa90303904befdf331242ba0d97553f84169
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15155
SST block writing now hands checksum coverage across the table-builder to file-writer boundary when the block checksum algorithm is CRC32C.
The block builder passes the raw CRC32C of the block contents to `WritableFileWriter::Append()`, which validates the copied destination-buffer contents and accumulates that checksum into the filesystem handoff checksum instead of recomputing it after the copy. The trailer is still appended separately; table building computes and passes the trailer handoff checksum independently, while preserving the existing persisted block-checksum encoding. Non-CRC32C block checksum algorithms continue to use the existing file-writer checksum path.
Destination-buffer validation failures return a stage-specific corruption `Status` that includes the file path. Flush and compaction propagate that status through their existing background error handling, so the same message is logged without adding local duplicate log sites.
The SST tests record the builder append stream through sync-point callbacks while writing to a null sink, then replay those exact append calls into a file-backed writer to cover successful handoff, block-content corruption, compression-type corruption, trailer-checksum corruption, non-CRC32C fallback behavior, and a small randomized boundary-injection loop.
Reviewed By: xingbowang
Differential Revision: D116969191
fbshipit-source-id: f75dcb37936106b7f6f14cf1ed999c81a0879d7b
Summary:
Instrument partitioned Bloom-filter block retrievals with read_filter_block_nanos so PerfContext includes time spent looking up these blocks in the block cache or reading them from SST files.
Root cause:
The top-level filter-partition index is read through ReadFilterBlock, which already owns the timing guard. Individual filter partitions bypass that helper and call RetrieveBlock directly. Once the top-level index is cached, workloads can therefore report filter block reads and cache hits while read_filter_block_nanos remains zero.
Implementation:
Place the timer at the partition retrieval boundary, after the pinned filter-map fast path and immediately around RetrieveBlock. This keeps the metric aligned with actual cache or SST retrieval work without charging accesses to already-pinned partition objects.
Testing:
Extend the partitioned MultiGet test to enable time-level PerfContext collection and verify that partition reads populate read_filter_block_nanos across whole-key and prefix-filter configurations and table format versions.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15164
Test Plan:
- `make check-sources`
- `build_tools/rocksptest.sh db_bloom_filter_test`
- `ASSERT_STATUS_CHECKED=1 build_tools/rockstest.sh db_bloom_filter_test --gtest_filter=*PartitionedMultiGet*`
Reviewed By: rban1
Differential Revision: D118052826
Pulled By: xingbowang
fbshipit-source-id: e4467b107ba13e5cfe4826a8a93834b319a131a5
Summary:
Remote compaction passes snapshot sequence numbers through serialized `CompactionServiceInput`. Downstream compaction logic assumes the sequence numbers are strictly increasing when selecting the earliest snapshot and using `std::lower_bound()` for snapshot visibility. Previously, malformed input could bypass the debug-only ordering assertion in release builds and select an incorrect visibility boundary.
- Validate decoded snapshot sequence numbers before remote compaction setup and return `InvalidArgument` for duplicate or descending values.
- Document the strict ordering contract while retaining the `CompactionIterator` assertion as defense in depth for internally constructed inputs.
- Canonicalize randomized serialization-test snapshots and add coverage for malformed snapshot lists through `DB::OpenAndCompact()`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15160
Test Plan:
- Built `compaction_job_test` and ran `CompactionJobTest.OpenAndCompactRejectsUnorderedSnapshots` in the normal configuration.
- Ran the focused test with `ASSERT_STATUS_CHECKED=1`.
- Ran the focused test for 100 repetitions.
Reviewed By: rban1
Differential Revision: D117888826
Pulled By: xingbowang
fbshipit-source-id: 2312205acfe17271c2083808d372c93a5914114c
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15136
`WritePreparedTxnDB` overrides `Get` and `MultiGet` to install a
`WritePreparedTxnReadCallback` that performs visibility checking for
prepared-but-uncommitted data. However, `GetEntity` (both the single-CF
`PinnableWideColumns` variant and the multi-CF `PinnableAttributeGroups`
variant) was never overridden, causing it to fall through to
`StackableDB::GetEntity` which delegates to the inner `DBImpl` without
any WritePrepared visibility callback.
This means `GetEntity` could read prepared-but-uncommitted data, violating
snapshot isolation. The bug surfaces in the crash test with the combination:
`--use_txn=1 --txn_write_policy=1 --use_attribute_group=1 --use_get_entity=1
--unordered_write=1 --two_write_queues=1 --test_batches_snapshots=1`
The fix adds `GetEntity` overrides to `WritePreparedTxnDB` that create a
`WritePreparedTxnReadCallback` and pass it via `GetImplOptions::callback`,
matching the pattern used by the existing `Get` override.
Reviewed By: xingbowang
Differential Revision: D116324596
fbshipit-source-id: faf636cfbf093f47a645c1a5d5037baa02ff594a
Summary:
Failures in GitHub Actions CI workflows attempting to download assets from releases.pagure.org occur because the Pagure platform has been decommissioned. Maintainers must update their configurations to retrieve packages from active mirrors, alternative repositories.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15159
Reviewed By: rban1
Differential Revision: D117863179
Pulled By: xingbowang
fbshipit-source-id: e54e80aa6f1e2e0503dbd7ec16e67b86c4bad7ad
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15158
Reverts `https://github.com/facebook/rocksdb/pull/15106`, which ignored `OPTIONS-*` and temp files when deciding whether recovery must advance `next_file_number_`.
Crash-test failures show backup/checkpoint failing to copy the current `OPTIONS-*` file after options cleanup, because `DeleteObsoleteOptionsFiles()` ignores `next_file_number_` and `options_file_number_` when choosing which files to keep. The failing commands use `optimize_manifest_for_recovery=1`, so restore the previous conservative file-number reservation behavior while we investigate a narrower fix.
Reviewed By: xingbowang
Differential Revision: D117807052
fbshipit-source-id: 89f8656f045be4ac36d2ec851980573ed83370fe
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15154
**What**: Let `IngestExternalFileArg::atomic_replace_range` replace a half-open `[start, limit)` range that only partially overlaps existing SSTs, by committing a generated range tombstone alongside the replacement files.
**How**:
- Tombstone: `NumFilesToPrepare` reserves one extra file number and `PrepareAtomicReplaceRangeTombstone` writes the SST only when the pinned `Version` holds a straddling file (`HasPartialOverlap`), so the no-straddle case pays no extra write; run rescans and returns `TryAgain` if a straddler appeared since. On activation it is rotated to the front of `files_to_ingest_` so one `VersionEdit` lands `old < tombstone < data` seqnos, straddlers are retained, contained files metadata-deleted, and `generated_for_ingestion` keeps `move_files` from unlinking it.
- Half-open plumbing: `RangesOverlapWithMemtables`, `RangeOverlapWithCompaction`, `OverlapProximalLevelOutputRange`, and `IsRangeOverlapped` take a required limit-exclusivity argument, and `largest_internal_key` now comes from `RangeTombstone::SerializeEndKey()`. `OverlapWithLevelIterator` stays inclusive: over-counting can only push the tombstone one level shallower, which is safe.
- Eligibility: `SupportsAtomicReplaceRangeTombstone` requires universal compaction, a delete-range-capable table factory, `allow_global_seqno`, `!allow_db_generated_files`, `!snapshot_consistency`, and no UDT. `fail_if_not_bottommost_level=true` rejects partial overlap because the retained file forces the tombstone and replacements above Lmax. The `atomic_replace_range` doc comment and a new `unreleased_history/public_api_changes/` note state the half-open contract.
- `ResetTableReader` passes `kNoFileChecksumFuncName` in `FileOptions` for external files, making "no checksum metadata" explicit rather than empty.
- Stress: new `ingest_external_file_atomic_replace_one_in` gflag. `PickAtomicReplaceRange` picks a run of overwriteable keys strictly inside an existing SST so every replacement straddles, writes even-indexed keys and marks the interleaved odd-indexed keys deleted so the tombstone must clear the holes, and rolls back expected values on `TryAgain`. `db_stress_tool.cc` and `db_crashtest.py` gate the flag on the same eligibility set and force `acquire_snapshot_one_in=0`.
**Why**: Atomic replacement previously required range bounds to align with SST bounds, and the public doc admitted the upper bound "may be interpreted as inclusive or exclusive". The tombstone overlay removes the alignment restriction and pins the contract to half-open without slowing the Nimble path or the no-straddle case.
Reviewed By: pdillinger
Differential Revision: D117155307
fbshipit-source-id: 65f45463e8082a7db7a9d44c8dc50debe95e36e7
Summary:
`periodic_compaction_seconds` softly guarantees every SST is rewritten at least
once per interval. Across a fleet of similarly-configured DBs, many events line
those intervals up in lockstep -- a shared enable, a coordinated restart (e.g.
DC power-outage recovery), or just similar write history -- so the periodic
compactions all come due together, a recurring "thundering herd" that is
especially painful with remote compaction (SEV S698635).
The obvious fixes trade one problem for another. Spreading the herd out means
some files get compacted off their natural schedule, which is wasted work; and a
clumsy spread just relocates the herd or spawns a new one. So the real goal is
narrow: **de-herd while keeping "badness" -- extra and/or late compaction --
small, and without inducing fresh herding at any step.**
This change adds an EXPERIMENTAL, opt-in mechanism that gives each `(DB, column
family)` a stable, well-spread *preferred phase* within the interval and steers
periodic compactions toward it. It is randomized and *uncoordinated* (DBs never
talk to each other), which both keeps it simple and, as it turns out, de-herds
far more cheaply than adding jitter to the interval. It is controlled by a single
mutable DBOption and is **off by default**:
- `periodic_compaction_phase_recovery_percent` (default `0` = disabled / kill
switch; recommended `33`; clamped to `[0, 100]`) -- how aggressively each
trigger converges toward the phase, as a percent of the remaining gap, only
ever pulling the trigger *earlier*. `0` reproduces the classic scheduler
exactly; `100` jumps to the phase in one cycle; lower values approach gradually
with much less extra work.
The preferred phase itself needs no tuning: it is derived from the DB ID (stable
across re-open and physical cloning/migration, but distinct across DBs) and each
column family is offset from the DB's base phase, so the CFs of one DB don't fire
together. (An earlier draft exposed a `compaction_schedule_seed` string option
for choosing the phase source; it was over-engineered for the one behavior anyone
needed, and as a mutable string it also broke OPTIONS-file round-trip
verification in crash testing. It has been removed in favor of always using the
DB ID.)
## The two kinds of badness
"Badness" here is the fraction of compaction work that is ideally unnecessary:
over-compaction (rewriting a file earlier than the interval required) plus a
penalty for lateness (going overdue past the interval). Steady state is 0; both
"never compact" and "compact twice as often as needed" approach 1. De-herding
inherently spends a little badness to spread load out; every design choice below
exists to bound one of those two costs *without* re-clustering the fleet.
The scenario names cited in the design bullets below (`upgrade_dynamic_r33`,
`naive_upgrade_r25`, `burst_r*`, `turndown_r33`, `turnup_r33`, ...) all come from
the out-of-tree simulator; see its results page (the "Simulator and
visualization" section below, with an interactive plot per scenario) for the
evidence behind each claim.
## Design
- **A stable per-(DB, CF) preferred phase = one fixed attractor per DB.** Each DB
gets a base phase from its DB ID; each CF is offset from it. Because the target
is fixed (not re-rolled each cycle) and unique per DB, the fleet converges to a
uniform spread and then compacts *on time* -- badness decays toward ~0. This is
the crucial contrast with naive per-cycle jitter, which de-herds but never stops
jittering and so pays a permanent over-compaction cost (see
`naive_upgrade_r25`: flat ~12-14%, only diffusive spreading) vs.
`upgrade_dynamic_r33`, whose badness decays to ~0 for the same anti-herding.
- **Converge gradually, and only ever earlier.** Each trigger closes a
`recovery_percent` *fraction* of the gap to the phase, reaching it over a few
cycles rather than in one burst, and never later than
`periodic_compaction_seconds` in steady state. A lower rate does less extra
work, which matters when the phase keeps moving (write-driven / bursty
workloads), where a high rate wastefully "chases" it -- the burst sweep shows
`burst_r25 < burst_r33 < burst_r50 < burst_r100` in sustained badness. `33` is
the default balance: quick spreading, low extra work.
- **Fractional recovery (not "snap") is latency-tolerant and doesn't re-herd.**
Closing a fraction of the gap gives a single stable convergence point, so
real-world scheduling/queueing latency -- the delay between a file becoming due
and its compaction actually running -- merely shifts the whole fleet uniformly
instead of re-clustering it. A tried "snap onto the phase when in range" variant
converged a bit faster with no latency but re-herded around the snap
threshold under latency; it was rejected. Graceful degradation beat peak speed.
- **Spread a suddenly-due cohort instead of bursting it (bounded lateness).**
Enabling the feature, turning `periodic_compaction_seconds` down, or catching up
after downtime can leave a whole cohort past due at the same instant. Firing it
all at once would just be the herd again, so such a cohort is spread over the
first quarter-interval (~N/4) after the phasing anchor on an *absolute*,
epoch-aligned per-(DB, CF) grid. This trades a bounded lateness (<= ~N/4 past
the deadline, so the interval is a *soft* upper bound during these transients)
for anti-herding, and the absolute grid keeps a rapidly restarting DB from being
perpetually re-deferred and starved. `turndown_r33` shows the past-due cohort
smeared over N/4 rather than bursting; `turnup_r33` confirms a turn-up creates
no past-due cohort (deadlines only grow) and so no burst. For immediate
enforcement at such a transition, trigger a manual compaction.
- **Off by default, with a kill switch.** Inert unless
`periodic_compaction_phase_recovery_percent > 0`; at `0` behavior is identical
to the classic scheduler.
## Implementation notes
- Phase math is integer-only (`Hash64` + `FastRange64`); floating point is avoided
for its pitfalls. The per-CF offset is a golden-ratio (Weyl/Kronecker) additive
sequence -- low-discrepancy for any number of CFs.
- Logic lives in `db/periodic_compaction_phaser.{h,cc}`
(`PeriodicCompactionPhaser`), which owns the recovery percent and the phasing
anchor and exposes the stateless trigger-time math; `VersionSet` holds one and
caches the per-CF result on each `Version`. The anchor is (re)set to now on DB
open, on a recovery-percent change, and on any `periodic_compaction_seconds`
change -- the three events that can create a past-due cohort.
- Plumbed through the C API, `db_bench`, `db_stress`, and `db_crashtest`.
## Simulator and visualization (out-of-tree, not part of this change)
A standalone simulator drives 40 real RocksDB instances through a real
periodic-task scheduler, records every compaction (periodic vs write-driven), and
scores the badness metric above across ~17 scenarios: dynamic/restart enable,
interval turn-down/turn-up via `SetOptions`, on-cadence writes, random write
bursts with a recovery-rate sweep, and the naive-jitter baseline. It is the basis
for the numbers cited above. Interactive HTML (**NOTE** fb-internal URL sanitization might require you to copy-paste the URL yourself):
https://htmlpreview.github.io/?https://github.com/facebook/rocksdb/blob/5759c6a62d9ff36cecf030b80453cc819b2f66ae/pc_phase_sim.html
Reviewed By: jaykorean
Differential Revision: D117487278
fbshipit-source-id: a62439548b7dd6fe7dfdbbe21b69531d8b57a1e8
Summary:
Leveled compaction combined with preserve_internal_time_seconds or preclude_last_level_data_seconds is a known-broken combination, but it is easy to enable by accident -- for example a fleet-wide config rollout that turns on the preserve time for CFs that happen to be leveled. Rather than degrading gracefully, it triggers an infinite kBottommostFiles compaction loop: a bottommost file gets marked, rewritten with no progress, and immediately re-marked, continuously rewriting the file and growing the MANIFEST so the file set never quiesces. This change makes such a misconfiguration a harmless no-op instead.
Before this change, the marking logic
(ComputeBottommostFilesMarkedForCompaction) marked a bottommost file whenever its largest seqno was below the oldest snapshot, without checking whether a compaction could actually zero that seqno. Recently written seqnos stay within the seqno->time "preserve" window and cannot be zeroed, so the rewrite never made progress. The same futile marking also occurred with user-defined timestamps when full_history_ts_low is unset. The underlying issue is that the "can this bottommost seqno be zeroed?" question was answered independently by the marking logic and by the compaction output path (CompactionIterator::PrepareOutput), and the two had drifted apart.
With this change, both paths answer that question through one shared predicate (BottommostSeqnoCanBeZeroed), and the preserve-window boundary that marking needs is plumbed into VersionStorageInfo from DBImpl, mirroring the existing oldest-snapshot plumbing. Snapshot visibility stays a per-site check. On CFs without preserve/preclude the predicate is a no-op. Confirmed that caller invariants ensure that relocating the `ikey_.sequence <= preserve_seqno_after_` check in CompactionIterator::PrepareOutput() will not trip the ROCKSDB_LOG_FATAL.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15146
Test Plan:
- compaction_picker_test: new CompactionPickerTest BottommostFileNotMarkedWithinPreserveWindow / BottommostFileMarkedBelowPreserveWindow cover the preserve-window boundary; new CompactionPickerU64TsTest BottommostNotMarkedWithEmptyFullHistoryTsLowAndUnknownMaxTs covers the UDT edge case.
- tiered_compaction_test: new PrecludeBottommostLoopTest LeveledPreserveTimeNoBottommostLoop is an end-to-end regression for the loop and the DBImpl plumbing: a bottommost file with a nonzero largest seqno inside the preserve window, made a marking candidate via a snapshot release, must not be marked. It reproduces the loop (hangs) when the plumbing is neutralized and passes with the fix.
Reviewed By: xingbowang
Differential Revision: D117299979
Pulled By: pdillinger
fbshipit-source-id: 51e8e2d4e2116984d16d3c7ef54a9635d5aded74
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15145
## Summary
RocksDB coroutine statistics are stored in `RequestContext`, but their `RequestData` is thread-affine. A filesystem implementation may propagate the context while completing `SubmitReadAsync` on another thread. Installing that context invokes `CoroutineStatsRequestData::onSet()`, whose `AssertSameThread()` detects the thread hop and aborts.
```
Before:
Thread A Thread B
RequestContext
├── caller tracing/attribution data
└── RocksDB coroutine stats ── propagated ──> setContext()
│
▼
CoroutineStatsRequestData::onSet()
│
▼
AssertSameThread(A != B)
│
▼
abort
```
Keep propagating the complete `RequestContext`, including the coroutine-stats token, so filesystem callbacks retain caller tracing and attribution. `CoroutineStatsRequestData` now records its owner using `Env::GetThreadID()` and ignores `onSet()` and `onUnset()` on foreign threads. When `ViaIfAsync` schedules the continuation back onto the RocksDB read EventBase, it restores the saved context on the owner thread before the coroutine continues, so the stats are safely loaded back into RocksDB TLS.
```
After:
Thread A Thread B
caller RequestContext
├── caller data ───────── propagated ───────> caller data
└── RocksDB coroutine stats ─ propagated ───> onSet()/onUnset()
│
▼
owner thread != current thread
│
▼
no-op
continuation queued back onto Thread A
│
▼
saved RequestContext restored on owner thread
│
▼
RocksDB coroutine stats reinstalled in TLS
```
Reviewed By: xingbowang
Differential Revision: D117274942
fbshipit-source-id: 9995cc3053a0a1d3a63d0cb2f4dd9320ab553005
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15123
Subset checkpoint post-processing appends column-family drop records to a copied MANIFEST. Recovery can tolerate an incomplete atomic group at the tail while retaining the reader high-water size, so appending at that size can produce a checkpoint that fails recovery.
Track the valid logical append boundary together with its MANIFEST file number, refresh it after recovery and successful writes, reset it across reactive MANIFEST switches, and trim only subset-checkpoint copies before appending drop records. Whole-DB checkpoints and the source MANIFEST remain unchanged.
Reviewed By: anand1976
Differential Revision: D116449394
fbshipit-source-id: a162dd6a2558f0f9ec40d878ee83956a89df698b
Summary:
Previously, during WAL replay, a secondary stored each 2PC prepare in
`recovered_transactions_`. Replaying the matching commit or rollback marker
deletes it. If the primary purges that marker's WAL before the secondary reads
it, the prepared `WriteBatch` and its tracker entry remain until the secondary
closes. This retention case was flagged by xingbowang in https://github.com/facebook/rocksdb/issues/15066. Even when
the marker is replayed, the secondary records completion but previously did not
prune the completed prefix from `logs_with_prep_tracker_`.
This change deletes recovered transactions whose prepare WALs are all below
`min_log_number_to_keep` after opening every discovered WAL reader but before
replaying records. The primary keeps that watermark at or below every
unresolved prepare and every prepare with unflushed committed data, so an entry
below it is committed and flushed, or rolled back. Deleting before replay lets
a reused transaction name start a new generation instead of merging into stale
state. If a reader cannot be opened, no recovered transaction is deleted.
After a round successfully replays every discovered WAL, the secondary also
prunes the completed prefix from `logs_with_prep_tracker_`. This covers
completions from normal commit or rollback replay and from the watermark-based
cleanup. The oldest outstanding prepare stops the pruning scan.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15121
Test Plan:
Added coverage for missed-marker cleanup, transaction-name reuse before replay,
the exact watermark boundary, a catch-up round that reports success after
replaying no WALs, and tracker pruning after normal commit replay. The
missed-marker case verifies cleanup of both retained structures.
Reviewed By: pdillinger
Differential Revision: D116620460
Pulled By: xingbowang
fbshipit-source-id: baca7faf53f414507957f2afdcc4ca67133c5669
Summary:
Building on D116105558, this change finishes and publishes `format_version = 8` as
production-ready.
The headline feature is a tweaked block encoding that stores the common user-key
prefix shared by all keys in a block **once**, in the block's leading bytes
`[0, restarts[0])`, instead of repeating it at every restart point. This shrinks
prefix-heavy blocks (and their block-cache footprint) and, for bytewise-ordered
keys, speeds up `Seek` by comparing prefix-stripped suffixes. It is applied to
**data blocks** and **every index tier** (leaf/partition index, the
partitioned-index top-level, and the partitioned-filter top-level).
Motivation: MyRocks/ZippyDB/timeseries-style keys carry a long shared prefix
(table/index id, partition, etc.). Repeating it at each restart wastes space and
cache, and every Seek re-compares those bytes. Profiles show index-block seek CPU
is comparable to data-block seek CPU (partitioned index does two index seeks per
data seek), so index blocks are a first-class target, not just data blocks.
### Format (self-describing, no new footer bit)
- The common prefix occupies the block's leading bytes; a non-zero `restarts[0]`
(the offset of the first entry, i.e. the prefix length) self-signals its
presence. `restarts[0]` is 0 in every block that does not use the feature (all
versions, all block types), so a single fv8 reader transparently handles any
mix of stripped and unstripped blocks -- no footer bit is consumed.
- Previous versions would misinterpret this encoding by quietly ignoring the
common prefix, which (along with the lack of extensibility in data-block
metadata) is why it is gated behind format_version 8.
- Only restart-point entries change on disk; non-restart entries are byte
identical to fv7, so the writer produces the exact same bytes regardless of
implementation.
### Public option
New `BlockBasedTableOptions::optimize_key_common_prefix`
(`enum class OptimizeKeyCommonPrefix`), default `kIfFastSeek`:
- `kDisabled` -- never use it.
- `kIfFastSeek` (default) -- use it only where it also enables the Seek speedup:
format_version >= 8 with a built-in (reverse-)bytewise comparator.
- `kEnabled` -- use it for all comparators at fv8; (reverse-)bytewise get space +
fast seek, other comparators get the space savings only (the reader
reconstructs full keys and compares with the real comparator).
Expressed as a criteria so it need not be toggled per CF based on the comparator
(which can vary by CF). It additionally requires `use_delta_encoding` (the
feature builds on delta encoding) and `ts_sz == 0`; when `use_delta_encoding` is
false -- the setting that also preserves zero-copy key pinning -- it is a no-op.
Default format_version is unchanged (still 7); the feature is opt-in via fv8.
### Scope: every index tier
The reader handles all index-like blocks uniformly (a non-zero `restarts[0]`
self-signals a stripped block regardless of block type), so the writer strips the
common prefix from every tier:
- **Data blocks** and **leaf / partition index blocks**: enabled.
- **Partitioned-index top-level block**: enabled. Under the default
`index_shortening = kShortenSeparatorsAndSuccessor` the file's last separator
would otherwise be a short successor sharing no leading bytes, collapsing the
single-block top-level's common prefix to 0. When common-prefix is on the
builder suppresses that one successor (keeps the full last key -- i.e.
`kShortenSeparators` behavior for that one entry), so the top-level strips
uniformly under every shortening mode; the full last key is also a tighter file
upper bound, so it is not worse.
- **Partitioned-filter top-level index**: enabled (same uniform treatment). Its
space/CPU effect is small at default partitioning (the top-level filter index
is tiny) but grows with many small partitions -- a filter miss short-circuits
at, and is dominated by, the top-level filter-index seek (see Performance).
Included for a uniform on-disk surface across tiers.
Bloom filter *contents* still hash full user keys, so prefix/whole-key filtering
is unaffected.
### Implementation
- Writer (`block_builder.{h,cc}`): keys are encoded incrementally in the fv7
layout while the running common prefix is tracked cheaply (reusing the delta
`shared`); at `Finish()` only the restart-point entries are rewritten in place
with the prefix stripped, and the prefix is written once at the block start.
The rewrite is generalized to the value-delta (fv4) index layout used by index
blocks (no `value_size` header; bulk-copy the inline BlockHandle and
non-restart entries verbatim).
- Reader (`block.{h,cc}`, `dbformat.h`): `Block` decodes the prefix from
`restarts[0]`; `BlockIter::ParseNextKey` prepends it on a restart point
(`shared == 0`). The suffix-seek helpers (strip target once, compare shortened
suffixes) live in the templated `BlockIter` base and are shared by data and
index iterators (binary search, templated on `DecodeKey`/`DecodeKeyV4`); for
non-bytewise comparators (data) and the hash `PrefixSeek` refine step, the
reader reconstructs the probed restart key and compares full keys.
- Gating (`block_based_table_builder.cc`, `index_builder.{h,cc}`,
`partitioned_filter_block.{h,cc}`): the criteria is threaded to the data-block
builder and to every index tier -- leaf/partition index, the partitioned-index
top-level (`use_common_prefix_top`), and the partitioned-filter top-level --
together with the last-separator successor suppression
(`ShortenedIndexBuilder::GetSeparatorWithSeq`) that lets the top-level strip
under the default shortening mode. The index base gate also requires
`super_block_alignment_size == 0` (block alignment can trigger
`skip_delta_encoding`, producing non-restart `shared == 0` entries the reader
could not distinguish from stripped restart keys).
- Memory: the per-block/per-iterator prefix is stored as a `uint32_t` length
(the prefix is always at offset 0) rather than a full `Slice`, and `Block` /
`BlockBuilder` members were repacked so the feature adds little or no struct
size.
- Java: a new `OptimizeKeyCommonPrefix` enum and a `BlockBasedTableConfig`
getter/setter, plumbed through JNI in both directions (the
`newTableFactoryHandle` writer and the options-reconstruction constructor).
### Whole-file (table-level) prefix: investigated and rejected
A natural extension is to store the whole-file common prefix once per *file*
(when an `SstPartitioner` guarantees every key in the file shares a fixed prefix)
instead of once per *block* -- e.g. the MyRocks index/table id, or a future
128-bit global id. It was prototyped end-to-end (partitioner-provided prefix
captured at file open, stored in a `user_key_common_prefix` table property, and
stripped from every data/index block; the reader re-prepends it) and then
dropped. The `user_key_common_prefix` table property reserved in the
previous diff is dropped in this one. The rationale, in case it is revisited:
- Marginal benefit over per-block. Per-block already strips the full shared
prefix from every key; the only thing whole-file removes on top is the single
per-block copy of that prefix, i.e. `~= prefix_len / block_size` of the
uncompressed (block-cache) footprint, with no meaningful read-CPU change
(per-block already delivers strip-once + suffix compares). Measured at
prefix_len=12, 4 KB blocks this was ~-0.3% incremental space; at the 8-16 KB
data blocks typical of the space-constrained target (MyRocks/UDB) it is
~0.1-0.2% even with a 16-byte global id -- a fraction of a percent, seen only
by partitioner users. Small block-size workloads are becoming more
niche, and even where they exist, they are also CPU-sensitive (see next
paragraph).
- Real, if small, CPU tax on the *per-block* path. Reconstructing a key from two
non-contiguous parts (file prefix + block residual) cost ~0.5-1.0% additional
retired instructions on the per-block read path (deterministic across gcc and
clang), wall-clock-neutral in cache-resident tests but present even when no
whole-file prefix is configured. Attempts to remove it did not pan out: a
compile-time specialization of the reconstruction *increased* instructions
(+2.9%) by defeating inlining; a per-block combined-prefix buffer trades the
CPU cost for a memory cost on every (default) block; and a per-iterator buffer
was the original larger regression. Only invasive iterator-level
monomorphization would truly zero it, which is not justified on this hot path.
- Decision. A fraction-of-a-percent, partitioner-only space win does not justify
a permanent per-block-read-path CPU tax plus additional on-disk format surface
(a table property that changes whole-file interpretation). Dropped. The generic
`SstPartitioner::ShouldPartitionByPrefix` hook is kept (cheap, and useful
independently), and the reserved `user_key_common_prefix` table property is
removed since no writer will emit it.
### Performance (db_bench, release/native, cache-warm, best-of-3, pinned)
Three arms: (A) fv7 on the base/main branch, (B) fv7 with this change (should be
neutral / byte-identical writing), (C) fv8 with this change and
`optimize_key_common_prefix=enabled` (the optimization is active). Partitioned
index + filters; `compression_type=none`, so SST bytes are the uncompressed
block-cache footprint. `int` = db_bench default keys (variation-early, near worst
case); `pfxN` = an N-byte shared prefix + varying suffix (MyRocks-like).
Setup: `num=3,000,000`, `reads=1,500,000`, single thread,
`metadata_block_size=4096`, cache holds the working set, pinned to one core.
| shape (key/val) | arm | SST bytes | readrandom us/op | seekrandom us/op |
|---|---|---:|---:|---:|
| int (32/8) | A fv7 main | 138,983,658 | 3.484 | 4.995 |
| | B fv7 ours | 138,983,146 | 3.518 | 5.024 |
| | C fv8 ours | 137,681,420 | 3.490 | 4.986 |
| pfx24 (32/8) | A fv7 main | 71,697,531 | 3.417 | 5.561 |
| | B fv7 ours | 71,698,043 | 3.448 | 5.579 |
| | C fv8 ours | 65,578,236 | 3.284 | 5.386 |
| pfx56 (64/16) | A fv7 main | 102,942,266 | 3.624 | 5.830 |
| | B fv7 ours | 102,942,266 | 3.678 | 5.835 |
| | C fv8 ours | 90,554,642 | 3.444 | 5.626 |
fv8 vs fv7-main (C vs A) -- space / readrandom / seekrandom:
- int (32/8): **-0.9%** / ~neutral / ~neutral
- pfx24 (32/8): **-8.5%** / **-3.9%** / **-3.1%**
- pfx56 (64/16): **-12.0%** / **-5.0%** / **-3.5%**
fv7-ours vs fv7-main (B vs A): SST bytes byte-identical (within 512 B; the fv8
paths are gated off), read/seek us/op within run-to-run noise (~1%, best-of-3 on
a shared host). Net: fv8 is a space **and** read/seek-CPU win on prefix-heavy
data and neutral on variation-early (`int`) data, so `kIfFastSeek` is safe on by
default at fv8.
### Space by tier (compression=none, num=3M, mdb=4096; fv7 -> fv8)
Where the win comes from -- the index tiers strip the most:
| shape | data | leaf/partition index | top-level index | filter |
|---|---:|---:|---:|---:|
| int | -0.8% | -29% | -45% | -0.2% |
| pfx24 | -8.3% | -69% | -89% | -0.8% |
| pfx56 | -11.1% | -83% | -96% | -1.6% |
Data bytes dominate the absolute total, but every index tier is stripped:
leaf/partition index -29% to -83% and the partitioned-index top-level -45% to
-96%. Filter *blocks* barely move (-0.2% to -1.6%) because filter bits hash full
user keys; only the small top-level filter index is stripped.
### Point-miss lookups (readmissing)
A partitioned-filter miss short-circuits at the filter (no data-block read), so
it is dominated by the top-level filter-index seek plus the partition bloom. At a
typical `metadata_block_size=4096` (few partitions, tiny top-level filter index)
fv8 is neutral on misses (int +0.9%, pfx24 -1.0%, pfx56 -0.7% us/op -- all
noise). The top-level-filter stripping pays only with many small partitions: at
`metadata_block_size=256` it is pfx24 1.166->1.138 (**-2.4%**) and pfx56
1.273->1.188 (**-6.7%**) us/op.
### Backward / forward compatibility
- Default `format_version` remains 7; the feature is inert unless a CF opts into
fv8. Older RocksDB rejects fv8 files at footer decode (fail-safe).
- Within fv8, the stripped-block set a writer can emit is fixed and every fv8
reader can read it (the `restarts[0]` signal plus uniform reader handling), so
the on-disk surface is self-consistent across builds/downgrade.
### Follow-ups (not in this change)
- Suffix-aware interpolation / hash index seek (currently correct via the
reconstruction fallback, not perf-tuned).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15117
Test Plan:
- New focused unit tests (`table_test`), run at the full `format_version` matrix
including the now-supported fv8:
`GeneralTableTest.OptimizeKeyCommonPrefix` (data-block space+read+Seek
correctness: existing and missing keys straddling the prefix, before/after-all,
SeekForPrev), `OptimizeKeyCommonPrefixNonBytewise` (reconstruction path), and
`IndexBlockCommonPrefixEquivalence` across `{kBinarySearch,
kBinarySearchWithFirstKey, kHashSearch, kTwoLevelIndexSearch}` x
`{partitioned filters}` x `{bytewise, reverse-bytewise}` (so both top-level
tiers -- partitioned-index and partitioned-filter -- are exercised), asserting
identical reads and a shrunken fv8 index.
- The randomized `ParameterizedHarnessTest` now runs at fv8 (reverse-bytewise,
restart intervals `{1,16,1024}`, special/empty keys fuzzed against the feature).
- `db_crashtest.py` randomizes `format_version` including 8 and
`optimize_key_common_prefix` in {0,1,2}.
- Removed the reserved-property carve-out: dropped the `ReservedUserKeyCommonPrefix`
reader test and the C API getter for the property; C API regenerated via
`tools/c_api_gen/` (`make regen-c-api`), and the intentional public-C-API
removal is recorded in `tools/c_api_gen/api_compatibility_allowlist.json`.
- Java: `BlockBasedTableConfigTest.optimizeKeyCommonPrefix` (default
`kIfFastSeek`, plus set/get of every value).
- `table_test` `FooterTests` updated for the now-published fv8 footer, whose
metaindex-gap encoding stores the metaindex block size in 16 bits (the test
caps that size for fv >= 8).
- Manual verification: the db_bench runs in Performance above (three-arm
space + read/seek CPU, per-tier byte breakdown via `sst_dump`, and
`readmissing`); and `readrandom`/`seekrandom` with
`--block_protection_bytes_per_key=8` find 100% of keys at both fv7 and fv8
(reconstructed keys pass per-key checksum).
Reviewed By: xingbowang
Differential Revision: D116365016
Pulled By: pdillinger
fbshipit-source-id: 66f4b5c60ea4fe0ff48d34c98d191ba8ebdf0665
Summary:
Faster and safer MANIFEST recovery for remote compaction (OpenAndCompact)
`DB::OpenAndCompact` (remote/distributed compaction) opens the target DB as a
secondary and reconstructs the LSM state from the MANIFEST before running the
requested compaction. Previously, point-in-time recovery would stat/open the
SST (and blob) files referenced by the MANIFEST to classify them found/missing
and materialize candidate versions, and eagerly load their table handlers.
That is (a) a lot of unnecessary file operations and open latency, especially
on remote/distributed storage, and (b) fragile: when a file referenced by the
MANIFEST but not an input to the compaction was momentarily unavailable to the
worker -- e.g. an obsolete file the primary had already deleted -- recovery
could fail the job (surfacing as an incomplete "no valid result") or roll back
to an earlier version, even though the compaction only needs its own input
files, which the primary keeps referenced for the job's duration. This was seen
as a flaky failure of CompactionServiceTest.BasicCompactions on aarch64, and
reproduces on x86 under COERCE_CONTEXT_SWITCH.
Efficiency + reliability: recovery for the OpenAndCompact path now trusts the
MANIFEST. It reconstructs the current version from FileMetaData without
stat-ing/opening files to classify them and without eagerly opening
candidate-version table handlers, so it does far fewer file operations and
never fails or rolls back over a file the compaction does not need. The
compaction's input files are opened (and unique-id verified) on demand as it
reads them.
Correctness hardening: trusting the MANIFEST means the worker's own (possibly
eventually-consistent) view of CURRENT/MANIFEST drives the reconstructed LSM
shape, which feeds correctness-critical decisions (Compaction::IsBottommostLevel
/ KeyNotExistsBeyondOutputLevel). A worker that read a stale CURRENT (older
MANIFEST), a truncated MANIFEST, or a MANIFEST with a corrupt/torn tail could
reconstruct against an older LSM shape and silently drop keys that should be
kept. To prevent this, the primary records the MANIFEST position (file number
and size) it scheduled the compaction from and passes it in
CompactionServiceInput as a floor. After recovery the worker compares the size
of the maximal *valid* prefix it reconstructed -- which stops at any corrupt or
torn record and at a partial atomic group at EOF, so tolerated tail garbage
cannot inflate it -- against that floor, and refuses to compact against an older
view (an older file number, or the same file number whose valid prefix is
shorter), returning Status::Incomplete so the CompactionService can fall back to
a local compaction. Accept-equal-or-later is safe by monotonicity: while the
inputs stay locked (being_compacted), a later view can only have moved data
further down, never resurrecting a shadowed value, so only an older view is
unsafe.
Single kill switch: both behaviors are bundled behind one signal and one
option. Presence of the floor on the request (min_manifest_file_number != 0)
couples on both trust-manifest recovery and the floor check in the worker;
absence reverts the worker to the original point-in-time recovery with no floor
check. A single mutable DBOption, remote_compaction_manifest_floor (default
on), gates whether the primary populates the floor, so it is the one escape
hatch that disables the entire new recovery path on a running DB via
SetDBOptions if it ever misbehaves. The new CompactionServiceInput fields are
wire-compatible in both directions (0 = not provided), so an old worker or an
old primary simply behaves as if the switch were off.
Follow-up: recovery is not yet strictly "open only the compaction's inputs". A
limited number of non-input files are still opened by the bounded file-property
sampling in Version::PrepareAppend -> UpdateAccumulatedStats (tolerating any
that are missing), which today populates input FileMetaData::num_entries for
compaction input-record-count verification. A natural follow-up is to record
the needed per-file info (e.g. num_entries) in the MANIFEST so the worker can
skip these auxiliary opens entirely; alternatively, source input num_entries
from the worker's own read of its input table properties. Relatedly, the floor
comparand is the recovered "maximal valid prefix" size, which today equals the
MANIFEST file size but is defined to stay correct if a future MANIFEST format
writes trailing padding/footer past the last record.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15133
Test Plan:
Added tests in compaction_service_test.cc:
- RemoteCompactionMissingLowerFileTrustsManifest: a non-input file referenced
by the MANIFEST is unavailable during a remote compaction; the job completes
with correct results (fails without trust-manifest recovery). The underlying
race also reproduces under COERCE_CONTEXT_SWITCH.
- RemoteCompactionManifestFloorRejectsStaleView: inflate the floor so the
worker's recovered MANIFEST is below it; assert the worker rejects, the job
falls back to local, and a below-output deleted key stays deleted (no silent
resurrection).
- RemoteCompactionManifestFloorSendsPosition: a non-zero floor is attached and
the worker accepts its equal-or-later view.
- RemoteCompactionManifestFloorKillSwitchOff: with the option off, no floor is
sent, the worker uses the original recovery, and a transiently-missing
non-input file makes the job fail/roll back (the escape hatch reverts
behavior).
- RemoteCompactionManifestFloorMutable: SetDBOptions flips the option; the
floor stops being attached and the worker skips the check.
options_settable_test covers the new mutable DBOption.
Crash test: remote compaction is already exercised (db_crashtest randomly
enables remote_compaction_worker_threads, driving DB::OpenAndCompact), so with
remote_compaction_manifest_floor defaulting on, the new recovery path is
covered by the crash test by default -- no new crash-test plumbing is needed.
The kill switch is intentionally left un-randomized (always on): turning it off
re-enables the original, occasionally-broken recovery, which would only add
noisy failures for no benefit.
UPDATE: crash test is not well covered because of `"remote_compaction_failure_fall_back_to_local": 1` which is masking other existing failures. Addressing this is a high-priority follow-up item. (Not practical to fix in this diff.)
Reviewed By: jaykorean
Differential Revision: D116738823
Pulled By: pdillinger
fbshipit-source-id: c02e0a437d62fc526dbbfcf70edf43434dfc7c1f
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15129
When `persist_user_defined_timestamps=false` and `long_running_snapshots=1`, `AssertSame` in `MaybeReleaseSnapshots` reuses the timestamp saved at snapshot acquisition time. That timestamp can become stale because concurrent flushes advance `full_history_ts_low`, and long-running snapshots can be held much longer than the 10-second headroom added by `GetReadTimestamp()`.
The snapshot validation is intended to check that a held snapshot still exposes the same sequence-number view. It should not require the acquisition-time read timestamp to remain valid after history below `full_history_ts_low` has been collapsed.
The fix keeps the timestamped read API contract intact. Column families with user-defined timestamps still require `ReadOptions.timestamp`, even when timestamps are memtable-only. For `persist_user_defined_timestamps=false`, `AssertSame` now uses a fresh validation timestamp so the read remains above `full_history_ts_low` while the snapshot sequence number continues to pin data visibility. For persisted timestamps, it continues reusing the original timestamp to validate the exact timestamped snapshot view.
Reviewed By: joshkang97
Differential Revision: D116110124
fbshipit-source-id: 9cf6cae6e7123f8453b7065565e9e2e0eb34321a
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15130
## Summary
Fix a corruption error in remote compaction where `FilterInputsForCompactionIterator` produces different results on the primary host vs. the remote worker.
**Root Cause:**
When a standalone range tombstone file completely shadows another input file, `FilterInputsForCompactionIterator()` removes the shadowed file from `input_levels_`. This filtering relies on `FileIsStandAloneRangeTombstone()`, which checks `num_entries == num_range_deletions == 1`.
On the primary host, these `FileMetaData` fields are always populated (set during flush/compaction when the file was created). On the remote worker, however, these fields are NOT persisted in the MANIFEST and are only lazily loaded from table properties via `UpdateAccumulatedStats()` during DB open, which is capped at 20 files (`kMaxInitCount`).
If the standalone range tombstone file is not among those first 20 initialized files, its `num_entries` remains 0 on the remote worker. This causes `FileIsStandAloneRangeTombstone()` to return false, so the remote worker does NOT filter the shadowed file and processes all its keys. Meanwhile the primary's expected record count is computed from its (filtered) view, leading to:
```
Corruption: Compaction number of input keys does not match number of keys
processed. Expected 0 but processed 12.
```
**Fix:**
Before constructing the `Compaction` object on the remote worker, explicitly initialize `FileMetaData` stats from table properties for all input files. This ensures `FilterInputsForCompactionIterator` produces consistent filtering results on both sides.
**Introduced by:** D83375779 (2025-09-29), which enabled `FilterInputsForCompactionIterator` on the remote worker by passing `earliest_snapshot` to `PickCompactionForCompactFiles`.
Reviewed By: xingbowang
Differential Revision: D116252973
fbshipit-source-id: 2cb2d61dae1f3982edeaf41f9a0f39d5aef1e789
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15132
This fixes a liveness bug in background-error recovery. Retryable soft errors allow foreground writes to continue, but recovery must wait for every scheduled background flush worker to exit before it can submit the recovery flush.
While recovery was active, normal flush requests could still enter `flush_queue_`. Atomic flush requests bypass per-column-family deduplication, so writes could accumulate a backlog of normal requests.
For each request, the background worker:
1. Pops and drops the normal request because BG work is stopped
2. Sleeps for the one-second background-error backoff
3. Decrements `bg_flush_scheduled_`: 1 -> 0
4. Calls `MaybeScheduleFlushOrCompaction()`
5. Schedules the next queued request: 0 -> 1
6. Only then signals the recovery thread
The rescheduling happens under the DB mutex before recovery is signaled:
Queued requests: R1, R2, R3, ...
Flush worker Recovery thread
------------ ---------------
drop R1
sleep 1 second waiting for scheduled == 0
scheduled: 1 -> 0
schedule R2: 0 -> 1
signal -------------------------> wake, observe scheduled == 1
wait again
drop R2
sleep 1 second
scheduled: 1 -> 0
schedule R3: 0 -> 1
signal -------------------------> wake, observe scheduled == 1
wait again
The dropped request is not requeued. Instead, the worker exit path guarantees that the next queued request is scheduled while `unscheduled_flushes_ > 0`.
A finite backlog therefore delays recovery by roughly one second per request. If writes keep producing requests as quickly as workers discard them, the queue may never drain and recovery is starved indefinitely. Recovery can complete after writes become quiescent, so this is a liveness failure rather than a lock deadlock or data-corruption bug.
There is also a queue-accounting edge. Scheduling consumes `unscheduled_flushes_`, but a worker does not own its `FlushRequest` until it pops `flush_queue_`. A worker can exit on the background error before popping, leaving:
flush_queue_.size() > 0
unscheduled_flushes_ == 0
Clearing only unscheduled requests therefore misses scheduled-but-unclaimed requests.
The fix:
- Reject new non-recovery requests while background work is stopped.
- Allow error-recovery flush reasons to bypass that guard.
- Clear the entire flush queue before recovery waits for scheduled workers.
- Retain the worker-side reason check for requests queued before the error.
- Rebuild recovery flushes and schedule a catch-up flush for writes made during recovery.
Reviewed By: pdillinger
Differential Revision: D116708546
fbshipit-source-id: 6e29fd3653ef72d047c64ca89d311a7bee51ab84
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15131
ASAN and other sanitizer runtimes can disable coredumps or terminate without a core after the test driver sets its shell core limit. This leaves sanitizer stress-test failures with an LLDB stack but no core for the DB artifact.
Configure ASAN_OPTIONS, TSAN_OPTIONS, and UBSAN_OPTIONS during sanitizer crash-test steps so reports terminate with a core-generating abort and core dumping remains enabled. Preserve caller-provided options and the existing Make fallback semantics. Keep the behavior scoped to test steps and crash targets.
Enabling sanitizer cores raises a concern that the generated core-dump artifacts can be large because they include the process working set. Reduce RocksDB's cache and memtable contribution to the core size without replacing the normal randomized test configuration. Add a db_crashtest cache/write-buffer size multiplier that is applied after each iteration chooses its values, and pass 0.5 from ASAN, TSAN, and UBSAN crash lanes. For the simple profile, cache_size remains randomized with 8/32 MiB becoming 4/16 MiB, while the 32 MiB write buffer becomes 16 MiB. Preserve RocksDB's 64 KiB minimum write-buffer size and leave non-sanitizer lanes unchanged.
Reviewed By: rban1
Differential Revision: D116666171
fbshipit-source-id: aa157cfa255bb637d4a68e54ea2039db779fdc53
Summary:
When a column family configures an SstPartitioner, compaction paid a per-key cost for every key-value written to an output SST: a full user-key copy (into `last_key_for_partitioner_`) plus a virtual `SstPartitioner::ShouldPartition()` call (which also builds a PartitionerRequest and truncates/compares two Slices).
This is motivated by an internal user whose custom SstPartitioner parses a variable-length prefix (a prefix code) out of each key to decide partition boundaries; today that prefix parsing runs on every key written during compaction, on top of the copy and virtual call above.
This wires up the `SstPartitioner::ShouldPartitionByPrefix()` API (added, but unused, alongside the format_version=8 groundwork) so compaction can skip that per-key work. When a partitioner can express its next boundary as a key prefix, `CompactionOutputs` caches that prefix once per output file (re-derived from the file's first key) and replaces the per-key copy + virtual call with an inlined `Slice::starts_with()` check. That also lets a partitioner like the one above do its prefix parsing once per output file instead of once per key. When the partitioner returns no prefix (ambiguous, e.g. `SstPartitionerFixedPrefix` for keys shorter than its length), compaction falls back to the previous per-key `ShouldPartition()` path. Output file boundaries are byte-for-byte identical to before. `SstPartitionerFixedPrefix` implements `ShouldPartitionByPrefix()`; the base-class default returns "no prefix", so existing custom partitioners are unaffected until they opt in.
Also:
- db_bench: new `--sst_partitioner_fixed_prefix_len` to configure a fixed-prefix partitioner (enables benchmarking; no default behavior change).
- `ShouldPartitionByPrefix()` API doc updated now that it is used and recommended for compaction CPU efficiency (any prefix code works, not just fixed-size).
No new option and no behavior or format change.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15118
Test Plan:
Added db_compaction_test cases covering the new fast path and its fallbacks: many keys sharing a prefix (one output file per prefix, no spurious cuts), keys shorter than the fixed prefix length (fallback to ShouldPartition), the zero-length-prefix "never partition" case, and short<->long key transitions across output-file boundaries. Existing SstPartitioner tests and the range tombstone + partitioner tests (db_range_del_test) provide regression coverage that file boundaries are unchanged, including the fast path with range tombstones. The crash test already exercises the partitioner via `enable_sst_partitioner_factory`.
Benchmark (db_bench `compact` of a pre-built 50M-key DB; 16B keys / 8B values, 8-byte shared key prefixes with FixedPrefix length 8, no compression, single subcompaction). A/B compares this change against the same binary with only the compaction hot-path change reverted. Means of 6 trials, optimized faster on every trial and every metric; both variants produced identical output (50 files, i.e. identical partition boundaries):
| Metric | Baseline | Optimized | Delta |
|---------------------------------|----------|-----------|--------|
| Instructions (whole process) | 92.46 B | 90.09 B | -2.6% |
| CPU cycles | 46.20 B | 45.28 B | -2.0% |
| CPU time (task-clock) | 18.74 s | 18.39 s | -1.9% |
| `compact` wall time | 20.58 s | 20.08 s | -2.4% |
DB-internal compaction stats corroborate: CompMergeCPU 15.84s -> 15.42s (-2.6%), compaction write throughput 38.9 -> 39.9 MB/s. The relative gain shrinks with larger values or compression enabled, where the partitioner is a smaller fraction of total compaction work; it is a pure win whenever a prefix partitioner is configured.
Reviewed By: rban1
Differential Revision: D116372363
Pulled By: pdillinger
fbshipit-source-id: 63de190329112aa2ce5b48da9fc77ef386494558
Summary:
PR https://github.com/facebook/rocksdb/issues/15066 fixed the bug that has caused this combination fails in the past. Re-enable this combination in the stress test.
db_crashtest.py currently disables continuous_verification_interval whenever test_secondary=1. This change removes that quarantine and enables continuous verification for secondary DB stress tests.
The disable predates auto_refresh_iterator_with_snapshot. When auto-refresh was later added, its stress-test integration mechanically added snapshot setup to several iterator paths, including the secondary-
only NonBatchedOpsStressTest::ContinuouslyVerifyDb() path:
snapshot = std::make_unique<ManagedSnapshot>(db_);
read_opts.snapshot = snapshot->snapshot();
This creates a snapshot from the primary db_ and passes it to an iterator created from secondary_db_. That was never supported: a ReadOptions snapshot must belong to the DB being read, and secondary DB
iterators explicitly reject non-null snapshots.
The incompatibility went unnoticed because the existing sanitizer made this combination unreachable in recurring crash tests. It was therefore a stress-test integration bug, not evidence that snapshot-
preserving auto-refresh was supported by secondary DBs.
This change:
- Stops forcing continuous_verification_interval=0 for secondary tests.
- Avoids attaching a primary-owned snapshot to secondary iterators.
- Continues exercising auto_refresh_iterator_with_snapshot through primary iterator paths.
Secondary iterators still support ordinary Iterator::Refresh(), which advances them to the latest caught-up secondary state. Snapshot-preserving automatic refresh remains unsupported.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15114
Test Plan:
- python3 tools/db_crashtest_test.py
- make check-sources
- AUTO_CLEAN=1 make -j192 db_stress
- Targeted blackbox crash test with:
- test_secondary=1
- continuous_verification_interval=1000
- auto_refresh_iterator_with_snapshot=1
Reviewed By: jaykorean
Differential Revision: D116381630
Pulled By: xingbowang
fbshipit-source-id: a162ab357a537c2e5047c1eda85d0c2138b56ed1
Summary:
`ExpectedState::Restore()` always attempted to open and replay the post-`saved_seqno_` trace. In the crash-test failure, recovery had already returned the DB exactly to `saved_seqno_`, so `replay_write_ops == 0` and the trace was legitimately empty. `FileTraceReader` reported `Status::Incomplete()` on that empty trace, which incorrectly failed restore.
Skip the trace replay work when `replay_write_ops == 0`, but still run the normal state promotion, cleanup, and `saved_seqno_` reset path so later saves continue to work.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14840
Reviewed By: xingbowang
Differential Revision: D107968797
fbshipit-source-id: b4024e1ff329aa3bd47f0307e67d9a4f224b66d4
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15106
When `optimize_manifest_for_recovery` is enabled, `MaybeUpdateNextFileNumber` uses on-disk files to decide whether recovery must append a `SetNextFile` edit. `OPTIONS-*` and temp options files are written as sidecar option snapshots during open, after recovery, so their presence alone should not force MANIFEST growth on a later clean reopen.
Exclude `kOptionsFile` and `kTempFile` from the trigger that requires a MANIFEST file-number advance. If any real file requires an advance, the code still advances past the largest observed file number, including OPTIONS files.
Also tighten db_stress strict MANIFEST verification so fatal zero-growth checks only run when all fault injection is disabled. Open write/sync fault injection can legitimately make `reuse_manifest_on_open` fall back to a fresh MANIFEST.
Reviewed By: xingbowang
Differential Revision: D116092705
fbshipit-source-id: 65b57b0b7f57b2cef42efc4df353fb2218688d34
Summary:
Add an opaque external_table_config option to the external table adapter. RocksDB persists the string in OPTIONS files and passes it to ExternalTableFactory::Configure during bootstrap, allowing implementations such as Nimble to parse implementation-specific settings without adding RocksDB-specific fields. The option is immutable after DB open, and attempted runtime updates fail safely for non-clonable table factories. A reopen test verifies that callers can supply a different valid configuration between DB opens. Initialize the adapter option metadata lazily as const to avoid namespace-scope dynamic initialization and mutable-global lint issues.
PR https://github.com/facebook/rocksdb/pull/15119
Reviewed By: anand1976
Differential Revision: D116362444
fbshipit-source-id: 225a4aedc455f320b5c90ac2f0862917da477ccb
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15102
Add a dedicated Skycastle signal that invokes liveness through the existing remote-storage test-driver path.
For remote DB runs, double default wrapper/no-progress timeouts while preserving explicit overrides. Default remote liveness `expected_values_dir` to local storage so `UniqueIdVerifier` never treats a remote DB path as a local path; local liveness behavior remains unchanged.
Normalize both equals and spaced `--env_uri`/`--fs_uri` forms once so remote detection, workload execution, and cleanup share the same arguments. Track the crash harness and internal release scripts as CI source triggers, scoped so release-script changes select only the remote-storage signal. Add command-level regression coverage and failure-safe expected-values cleanup. Use generic remote DB/storage terminology throughout public RocksDB code and comments.
Reviewed By: pdillinger
Differential Revision: D115936164
fbshipit-source-id: e4997bf19eec18e4914626d479690b78c6cd3ad2
Summary:
Follow-ups to the lazy wide-column read API (DB::GetEntityLazy / MultiGetEntityLazy). Three related, low-risk changes under one theme; no public API change.
1. Internal read-API cleanup (retires TODO(lazy-blob-resolution-cleanup)). The lazy byte-range work had left a whole/range/force-verify method fan-out on the blob read path: VersionBlobFetcherBase::FetchBlobRange plus a separate FetchBlobForceVerify, and SameFileBlobReader::GetSameFileBlob paired with GetSameFileBlobRange, with the (verify_checksums, force_verify) decision duplicated across branches. This folds them into a single 3-valued BlobVerifyPolicy (kVerifyIfPresent / kVerifyIfNoAmplification / kSkip, added to db/blob/blob_constants.h) derived once at the resolver boundary and threaded down. FetchBlobRange and GetSameFileBlob now take a range descriptor (range_length == kWholeBlobLength means the whole value) plus the policy; FetchBlobForceVerify and the *Range twins are removed. The public LazyColumnReadRequest::force_verify bool is unchanged. The widely-called lower-layer whole-value primitives (BlobFileReader / BlobSource / Version GetBlob) are intentionally left as delegated primitives to bound blast radius; folding those is a later cleanup.
2. Embedded block-cache-tier quirk fix (retires TODO(lazy-blob-resolution-quirks)). ValidateEmbeddedBlobIndex rejected read_tier == kBlockCacheTier before the blob-cache probe, so a same-file (embedded) column already in the blob cache returned Incomplete under kBlockCacheTier -- unlike the separate-file path, which probes the cache first. The up-front rejection moves to the two no-cache paths (ResolveEmbeddedBlob / ResolveEmbeddedBlobPinned); the cache-probing paths now serve a cached record under kBlockCacheTier, matching separate-file behavior. This is a shared lower-layer change: the non-lazy Get / GetEntity and iterator embedded read paths (get_context / MaybeResolveEmbeddedValue -> ResolveEmbeddedBlobCached) get the same corrected behavior, not just the lazy resolver.
3. Coverage. db_bench gains a readrandomentitylazy benchmark (GetEntityLazy + MultiResolve of --lazy_entity_read_length bytes per column) for measuring byte-range (partial) blob read savings; it requires open_files == -1. The crash test now also fuzzes LazyColumnReadRequest::force_verify in the lazy-vs-eager differential (force_verify affects only verification, not the returned bytes, so the comparison is unchanged).
This also sets up the next planned step -- cross-key coalescing for MultiGetEntityLazy / LazyWideColumnsBatch::MultiResolve. The internal BlobReadRequest already carries a {offset, len} range descriptor, and with the verify decision now a single BlobVerifyPolicy rather than a bool plus a separate force-verify method, a batched resolve can group a vector of range+policy requests per (CF, Version, blob file) and coalesce them onto the existing MultiGetBlob machinery -- without re-introducing whole/range/force-verify method twins in the multi-read path.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15115
Test Plan:
- New unit test DBLazyEntityTest.EmbeddedBlockCacheTierServedFromCachedValue: a cached embedded column serves whole- and sub-range reads under kBlockCacheTier instead of returning Incomplete. Existing force_verify / BlockCacheTier tests are unchanged (public behavior preserved).
- New unit test DBBlobIndexTest.EmbeddedBlobWideColumnGetEntityBlockCacheTierServesCached: covers the non-lazy GetEntity path -- a cached embedded column is served under kBlockCacheTier; the existing ...GetEntityBlockCacheTier test still asserts a genuine miss yields Incomplete.
- Crash test: extended the existing lazy differential to fuzz force_verify (reuses --lazy_entity_read_one_in).
- db_bench: readrandomentitylazy over 2000 entities x 2 uncompressed embedded blob columns (4000 B each), reading 100 B per column:
| metric | value |
|---|---|
| rocksdb.blobdb.lazy.partial.read.count | 4000 |
| rocksdb.blobdb.lazy.read.bytes | 400000 |
| rocksdb.blobdb.lazy.partial.bytes.saved | 15600000 |
i.e. 100 B read per column instead of the 4000 B whole-column value.
Reviewed By: xingbowang
Differential Revision: D116334969
Pulled By: pdillinger
fbshipit-source-id: cebe90456b23be0f5be60cdc4bff85c8a37b5442
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15103
The crash artifact showed `MANIFEST-000005` was physically well-formed but contained an incomplete atomic group that had been tolerated at EOF. `reuse_manifest_on_open` then appended ordinary records after that tail, turning the tolerated crash tail into mid-file `corrupted atomic group` corruption seen by OpenAndCompact and backup restore.
Record whether MANIFEST recovery ended with a pending atomic group. When that marker is set, skip `ReopenManifestForAppend()` so the next manifest write creates a fresh MANIFEST instead of appending after the partial group. The existing physical-size guard remains as a separate torn-tail defense.
This replaces the earlier keep-size preallocation hypothesis with the artifact-backed root cause.
Reviewed By: xingbowang
Differential Revision: D116062618
fbshipit-source-id: fb55ce2891a6fd50fa2c670dc720e91bad27e94d
Summary:
The point_lock_manager_stress_test (via PointLockValidationTestRunner) was seen flaky under USE_CLANG=1 COMPILE_WITH_ASAN=1 COMPILE_WITH_UBSAN=1, manifesting as a heap-use-after-free plus "terminate called without an active exception" / SIGABRT. There were two distinct problems.
Cleanup on failure: run() uses gtest ASSERT_* macros, which return early from run() on failure. That early return skipped the shutdown flag set and the join of the worker threads, so the worker threads kept running and accessed members of the (now destroyed) test runner object (heap-use-after-free), and destroying the still-joinable threads_ vector called std::terminate. Fixed by adding a destructor plus a StopAndJoinThreads() helper that always signals shutdown and joins the worker threads before any member is destroyed, no matter how run() exits. Copy/move are deleted since the object owns worker threads.
Underlying flaky failure: the per-second "No locks were acquired in the last 1 second" liveness assertion was too strict. Under sanitizer builds combined with very short lock timeout/expiration and post-acquisition sleeps, all threads can occasionally fail to acquire a single lock within a given one-second window without the lock manager actually being stuck. The per-thread variant of this check was already disabled for this reason; the aggregate one is now relaxed to tolerate a few consecutive no-progress seconds, which still reliably detects a genuinely stuck (dead/livelocked) lock manager. Also fixed the end-of-run check from `>= 0` (always true) to `> 0` so it actually catches a fully stuck manager.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15116
Test Plan:
Verified the cleanup path by temporarily forcing the liveness assertion to fail while worker threads were active: the test now reports a normal gtest failure with clean teardown, with no heap-use-after-free and no std::terminate/SIGABRT.
I don't have a consistent reproducer for the failure but ran
* 3x `USE_CLANG=1 COMPILE_WITH_ASAN=1 COMPILE_WITH_UBSAN=1 make check`
* `USE_CLANG=1 COMPILE_WITH_ASAN=1 COMPILE_WITH_UBSAN=1 rocksptest point_lock_manager_stress_test -r100`
Reviewed By: xingbowang
Differential Revision: D116349420
Pulled By: pdillinger
fbshipit-source-id: 104784c5c59770414224ac4751eabf69f65e0958
Summary:
Fix https://github.com/facebook/rocksdb/issues/15051: a secondary serves stale reads because entries replayed from a WAL the primary has since flushed are never evicted from the active memtable. During TryCatchUpWithPrimary(), seal the active memtable once the installed Version provably covers everything it holds, so the following RemoveOldMemTables() call drops it. Every memtable-dropping decision takes that same watermark, so a file set the secondary cannot read never turns the stale read into a vanished key.
## Why and How
A secondary replays the primary's WALs into its active memtable and stops replaying a WAL once the primary flushes it. Entries already replayed stayed behind, and since reads consult the memtable first, they shadowed the newer flushed values indefinitely. Iterators go stale too once bottommost compaction rewrites the flushed entry's sequence number to 0; until then Get() and an iterator disagree.
Seal the active memtable during catch-up, gated on the installed Version: each point-in-time Version now carries the column family's log number it reflects, exposed via the new
ReactiveVersionSet::GetInstalledVersionLogNumber(). Sealing requires cf_id_to_current_log_[cfd->GetID()] < that number, which guarantees the primary flushed everything the memtable holds (unflushed WAL data is kept) and that the flushed data is readable through the installed Version. The gate asks the Version directly because proxies fail: the log number alone advances even when no Version was installed, and "no missing files" misses atomic_flush groups parked by a sibling's missing file and corrupt-but-present files with
verify_sst_unique_id_in_manifest=false. In those cases the memtable holds the only readable copy, so dropping it would turn a stale read into a vanished key; the gate fails closed and keeps it.
Reconcile every initialized column family, not just cfds_changed: a round that failed after advancing the log number leaves a stale memtable that later rounds see no new MANIFEST record for. It can also leave a collectible immutable memtable, so the loop consults the new O(1) MemTableList::HasOldMemTablesToRemove() before skipping a column family. The predicate keeps the skip precise: reconciling any column family with a non-empty immutable list would be correct but wasteful, installing a fresh SuperVersion every catch-up call for memtables legitimately waiting on a primary flush and costing readers a mutex acquisition each time. With it, no extra super versions are installed in the steady state. That predicate and the RemoveOldMemTables() cutoff take the installed Version's log number as well, not cfd->GetLogNumber(): that advances as soon as the flush record is read, so an immutable memtable sealed on a WAL switch could otherwise be freed while the flushed file superseding it is unreadable. A sealed memtable is stamped with cf_id_to_current_log_[cfd->GetID()] + 1, the log number following its contents.
cf_id_to_current_log_ is keyed by column family id rather than ColumnFamilyData*, because ids are never reused while a dropped column family's pointer can be recycled, and it is recorded before WriteBatchInternal::InsertInto() rather than after: a batch stops at its first failure with earlier entries already inserted, so recording afterwards could leave the map naming an older WAL than the memtable holds. While the watermark lags and uncollectible memtables accumulate, the secondary warns, since it has no write path to enforce max_write_buffer_number.
The seal sequence, shared with RecoverLogFiles() and the follower's TryCatchUpWithLeader(), is extracted into
DBImplSecondary::SealActiveMemtable(). The follower is refactored onto it with no behaviour change: it keeps cfd->GetLogNumber() as its cutoff because that catch-up path never replays WALs, so its memtables hold nothing that only a not-yet-installed Version could make readable.
## Testing
New tests in db_secondary_test cover: the stale read via both Get() and an iterator, before and after bottommost compaction; unflushed WAL-only data surviving sealing, including two column families sharing a WAL; no data loss with a missing, atomic_flush-sibling-missing, or corrupt flushed file; an immutable memtable sealed on a WAL switch surviving an unreadable flushed file; a batch rejected partway through replay leaving the recorded WAL no older than the memtable's contents; and reconciliation after a failed round (injected via FaultInjectionTestFS at a sync point). Each mechanism was individually neutralized and confirmed to fail its test.
Verified with build_tools/rocksptest.sh over db_secondary_test, db_follower_test, memtable_list_test, version_set_test and version_edit_test (all pass, also under ASSERT_STATUS_CHECKED=1), plus db_basic_test, corruption_test, repair_test and db_flush_test; the secondary tests also ran 20x under COERCE_CONTEXT_SWITCH=1 with no flakes. make format-auto and make check-sources are clean.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15066
Reviewed By: jaykorean, anand1976
Differential Revision: D115317523
Pulled By: xingbowang
fbshipit-source-id: a1d4615eb951d75e69ed242731336485c0dfa137
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15098
Compute the base block checksum in `CompressAndVerifyBlock()` before `verify_compression` decompresses the compressed output. Parallel compression stores that base checksum on the block rep so worker threads can keep the CPU-heavy checksum and verification work off the ordered write path; the writer only applies the offset context modifier before writing the trailer.
Update the regression test to cover both serial and parallel compression paths.
Reviewed By: pdillinger
Differential Revision: D115950785
fbshipit-source-id: b5e47f7d7c41680e7e7907443fc805bc0d043d6e
Summary:
format_version background: SST files are shared across RocksDB versions. format_version is a critical tool in ensuring prior (and later) versioned readers do not misinterpret a file created by some version. Opt-in features can sometimes rely on previously-rejected corners of the schema space, but not always.
This change: lands much of the format_version=8 (fv8) format surface as an internal-only draft -- readable and testable but not user-writable -- so upcoming features can build on a stable base. fv8 is NOT published: kLatestBbtFormatVersion stays 7, and reading or writing fv8 is gated behind the existing TEST-only TEST_AllowUnsupportedFormatVersion() hook. Publishing fv8 (raising the ceiling) is deferred to a later change.
The pieces of fv8 added here, and what each unlocks:
- Index value-delta escape. Existing behavior: index blocks (format_version >= 4) store each block handle as a delta against the previous entry's handle, and signal "this value is a full handle instead" by reusing the key sentinel `shared == 0`. Because the two are coupled, an index entry whose handle is not contiguous with the previous one must be forced to a restart, costing a full key as well as a full value. fv8 decouples them with an in-value escape codepoint: varint 0 means "a full BlockHandle follows," and genuine size deltas are remapped to zigzag(delta)+1 so that 0 stays free. A non-contiguous entry can then remain an ordinary key-delta-encoded, on-cadence entry. The escape applies to every index-shaped block, which includes the top-level index of index partitions and the index of filter partitions, not just the index over data blocks. Unlocks: the upcoming optimize_key_common_prefix index feature (removes its need to special-case super_block_alignment) and value-delta encoding for embedded-blob SSTs (previously disabled outright because interleaved blob records break handle contiguity). Guarded strictly on fv8; format_version <= 7 encoding and decoding are byte-for-byte unchanged. Note that restart points must not delta-encode, so they still have full values without a sentinel.
- Data block footer bit 30 is reserved as an "extended metadata present" escape. Decode rejects a set bit as unsupported/corrupt at all versions. Unlocks: adding block-level metadata in the future through the one shared footer decoder, without another format_version bump or plumbing format_version into every block parser. (Older reader versions relied on this bit being zero for some integrity/consistency checking; format_version bump now makes it safe to use.)
- Footer metaindex gap. For fv8, the top 16 bits of the footer's 4-byte metaindex-size field record the gap (in bytes) between the end of the metaindex block and the start of the footer; the low 16 bits hold the metaindex block size. Unlocks: a space very near the end of the file to save full file checksum state, for a "files carry their own checksums" feature that doesn't require SST parsing to extract the checksum states (to run them to completion on the trivial file tail).
- Reserved "user_key_common_prefix" table property. The reader returns NotSupported when it is present and non-empty. Unlocks: a future whole-file optimization that stores the common prefix of all user keys once instead of in every restart key; until implemented, fv8 readers refuse such a file rather than mis-read it.
- Because of the problem of inferring such a prefix at the start of constructing an SST file, we expect it will rely on SstPartitioner. Here we've added SstPartitioner::ShouldPartitionByPrefix() (not yet used) that will enable this optimization, and avoid the overhead of a virtual callback to SstPartitioner on every key written to an SST file.
Supporting cleanups: the Footer's "unset" format_version sentinel is named (kInvalidFormatVersion) and the FormatVersionUses*() predicates assert against it, as each is a `version >= N` test that an unpopulated Footer would silently satisfy. Index block reader entry points (Block::NewIndexIterator() and friends) drop their defaulted trailing arguments so each caller states which codec it expects, and BlockBuilder::Add() takes delta_value by const reference rather than a pointer whose null case no caller used.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15104
Test Plan:
New unit tests, all exercising fv8 through TEST_AllowUnsupportedFormatVersion():
- block_test: value-delta escape codec round-trip (delta 0, negative, large, and the escape; asserts format_version <= 7 bytes match the legacy encoding and a legacy 0x00 still decodes as delta 0), and rejection of footer bit 30 at all versions.
- db_flush_test: fv7-vs-fv8 read equivalence for super_block_alignment, including the separate_key_value_in_data_block intersection, confirming padding events (hence escape entries) occur and fv8 index values are delta encoded.
- sst_file_reader_test: fv7-vs-fv8 read equivalence for embedded-blob SSTs, confirming fv8 (unlike fv7) delta-encodes their index values and actually emits escape entries (counted through a sync point).
- block_based_table_reader_test: a builder+reader test that injects a footer metaindex gap via a sync point (verifies the gap bytes are written and the reader locates the metaindex via the recorded gap, and that too large a gap surfaces as a builder error), and that a non-empty reserved user_key_common_prefix property makes the reader return NotSupported.
- table_test: two-level index with partitioned filters and index_block_restart_interval > 1, the configuration in which both top-level indexes delta-encode their values; verifies scan and point lookup round trips at each tested format_version including fv8.
- (Not strictly fv8) Basic SstPartitionerFixedPrefix tests in slice_test (ShouldPartition, ShouldPartitionByPrefix, their mutual consistency, and CanDoTrivialMove), which had no non-DB coverage before.
fv8 is also added to the table_test harness (GenerateArgList) and the shared footer version set (test::kFooterFormatVersionsToTest), so the existing table-level and DB-level property/checksum suites now also run at fv8. And added to crash test (minor revisions to db_stress).
## Performance details
Release db_bench (PORTABLE=0, -march=native) A/B/C on the read/write fast path,
comparing (a) parent, (b) this commit @ fv7, (c) this commit @ fv8. All DBs on
tmpfs, 20M keys, block_size=2048, compression=none. Note index_block_restart_
interval=16 (not the default 1) is required to actually exercise the fv7<->fv8
index value-delta codec -- with restart interval 1 every index entry is a full-
handle restart and the changed path never runs (confirmed delta-value=1 via
sst_dump). Reads single-thread + core-pinned, warm cache, interleaved/rotated
rounds, paired deltas.
Reads (readrandom + seekrandom, non-partitioned single ~1.9MB index block and
partitioned index+filters): fv7->fv8 in the *same* binary is within noise on all
four workloads (largest -1.0%, CIs cross zero). before->after (fv7 path) is also
within noise; perf shows a deterministic +0.4-0.6% instructions/op from the added
branches (the format_version>=8 value_delta_escape guard + refactored data-block
footer decode), which is below the throughput noise floor.
Writes (fillseq + compact total CPU): fv7->fv8 null. compact (which rewrites all
index blocks) is null before->after. fillseq showed +3.5% CPU before->after, but
perf attributes it entirely to cycles/IPC with *identical* instructions/op
(+0.01-0.04%) -- a code-layout effect on the untouched memtable-insert loop, not
this change.
Sanity: any single throughput/CPU delta <~1% is within the build-to-build noise
floor, measured directly by comparing two byte-identical binaries (swings up to
~0.9% reads; the pre-native/PORTABLE=1 build showed a spurious -1.85% that
vanished under -march=native).
Net: no measurable regression from fv8 draft; fv7 encoding/decoding byte-for-byte
unchanged, only the noted sub-1% instruction bump on the shared read path.
Reviewed By: xingbowang
Differential Revision: D116105558
Pulled By: pdillinger
fbshipit-source-id: ead776cdd396d497d47872c0956ea8c651c0d13f
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15111
T284514290 exposed an online-recovery race between the automatic recovery coordinator and a foreground synchronous write. A retryable no-WAL flush error selected lightweight retry-flush recovery. While that recovery attempt waited for the failed background flush, `Delete(sync=true)` appended and synced sequence N to the active WAL, then failed while persisting WAL-tracking metadata to the MANIFEST. Because memtable insertion and sequence publication happen after that MANIFEST step, the record was durable but logically unpublished.
OLD: the concurrent error triggered another attempt, but the attempt reused a stale recovery plan. That preserved both ingredients needed for a duplicate: the first `Delete(key, N)` remained in the active WAL, while the published sequence remained `N-1`.
```
Recovery coordinator Foreground sync-write thread
------------------------------------ --------------------------------
[db_mutex held]
plan = RetryFlush
(copied once before the retry loop)
attempt 1: ResumeImpl(plan)
WaitForBackgroundWork()
bg_cv_.Wait()
[atomically releases db_mutex] ---------> assign sequence N
append + sync Delete(key, N)
[db_mutex held]
MANIFEST WAL edit fails
no memtable insert
no LastSequence publication
write returns I/O error
recovery_error_ = Hard
(requests another attempt)
bg_error_ = Hard
recover_context_ eventually
becomes FullRecovery
[db_mutex released]
[db_mutex reacquired] <---------
ClearBGError() sees recovery_error_
attempt 1 cannot clear the error
attempt 2:
recovery_error_ = OK
ResumeImpl(plan)
^
+-- still the cached RetryFlush plan
RetryFlushesForErrorRecovery()
retries the failed immutable flush only
does not call SwitchMemtable()
does not switch the active WAL
ClearBGError() succeeds
caller retries Delete(key)
LastSequence is still N-1
=> assigns sequence N again
appends Delete(key, N) to same WAL
memtable insert succeeds
publishes LastSequence = N
Reopen / WAL replay:
first (key, N, deletion) -> memtable insert succeeds
second (key, N, deletion) -> TryAgain("key+seq exists")
normal WAL recovery has seq_per_batch_ == false
assert(seq_per_batch_) fails
```
The cached argument to `ResumeImpl()` is therefore the direct bridge to the assertion: lightweight retry-flush recovery can report success without retiring the WAL that contains the unpublished record. Since it also does not publish sequence N, the successful caller retry writes the identical internal key into that same WAL.
NEW: the same recovery coordinator consumes the concurrent error through a mutex-ordered handoff.
```
Recovery coordinator Foreground sync-write thread
------------------------------------ --------------------------------
[db_mutex held]
attempt 1:
plan = recover_context_ // RetryFlush
ResumeImpl(plan)
WaitForBackgroundWork()
bg_cv_.Wait()
[atomically releases db_mutex] ---------> append + sync Delete(key, seq=N)
[db_mutex held]
MANIFEST WAL edit fails
CheckAndSet...(Hard, FullRecovery):
recovery_error_ = Hard
bg_error_ = Hard
recover_context_ = FullRecovery
[db_mutex released]
[db_mutex reacquired] <---------
ClearBGError() sees recovery_error_
current attempt cannot clear the error
next retry, with db_mutex held:
recovery_error_ = OK
plan = recover_context_ // FullRecovery
ResumeImpl(plan)
SwitchMemtable()
switch to a new WAL
clear the background error
```
The foreground unlock of `db_mutex_` happens before the recovery coordinator reacquires it and snapshots the next plan. Publishing `bg_error_` and `recover_context_` together under that mutex prevents the next attempt from observing a new severity with an old recovery requirement. `recovery_error_` is the per-attempt latch: a concurrent I/O error cannot be cleared as part of the in-flight attempt; it either stops recovery or forces another attempt to consume the updated context. If another error arrives while `ResumeImpl()` releases the mutex again, the same handoff repeats. No second recovery coordinator is needed.
Manual `Resume()` now uses the retained recovery context instead of reconstructing one from the background-work scheduling flag, and the soft-error clear-only shortcut is allowed only when that flag shows there is no pending recovery work. The write ordering and `assert(seq_per_batch_)` detector are unchanged.
Reviewed By: anand1976, xingbowang
Differential Revision: D116106177
fbshipit-source-id: a78450c0924a326b5302ae6709e367f07bd3d5a6
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15100
Fix the TSAN race in T284274705 where `CoroutineStatsRequestData::onSet()` can access its borrowed `Env*` after `AsyncCallback::OnComplete()` allows the caller to destroy that environment.
When `co_await baton` suspends, the Task's implicit `ViaIfAsync` saves the read's statistics `RequestContext` (`S`) as the context needed by the real RocksDB continuation. The old explicit `executor->add()` also ran the Baton bridge under `S`. Consequently, `baton.post()` resumed the Via trampoline under `S`, and the trampoline's own `executor_->add()` caused EventBase to capture `S` a second time as the queued continuation's outer context. After the read removed its stats scope and called `OnComplete()`, the Via guard restored that outer `S`, triggering a late `onSet()` that dereferenced the potentially destroyed `Env*`.
Remove the redundant outer executor hop and call `baton.post()` directly under an explicitly null `RequestContext`. If the read is suspended, `post()` resumes only the Via trampoline; the trampoline still queues the real continuation on the bound read executor. The Via promise retains its saved `S`, so `executeContinuation()` installs `S` while RocksDB executes, but EventBase now captures null as the outer context restored afterward. If filesystem completion happens before `co_await baton`, `post()` only marks the Baton ready and the read continues inline under its existing context.
Thread Race:
`E` = temporary backup `Env`
`S` = statistics `RequestContext` containing `CoroutineStatsRequestData{env_ = E}`
`P` = parent context without RocksDB statistics
`B` = old explicit Baton bridge
`C` = `ViaIfAsync` callback that resumes the real read
```
Caller / stress thread RocksDB read EventBase thread
---------------------- -----------------------------
BackupInfo owns shared_ptr<Env> E
Call MultiGetAsync()
Wait for OnComplete() Start the RocksDB read coroutine
Install S:
P -> S
S.onSet()
Call SubmitReadAsync()
No io_uring is available, so:
Read() executes synchronously
filesystem callback runs inline
callback is still under S
Old code calls executor->add(B)
EventBase records:
B.outer_context = S
Reach co_await baton
B has not executed, so Baton is not ready
ViaIfAsync:
saves execution_context = S
gives trampoline V to Baton
Suspend the real read
Leave S:
S -> P
S.onUnset()
Later, EventBase runs B
Install B.outer_context:
P -> S
S.onSet()
B calls baton.post()
-> V resumes inline
-> V queues callback C
-> EventBase records:
C.outer_context = S
Later, EventBase runs C
Install C.outer_context:
P -> S
S.onSet()
Via executeContinuation():
remembers previous context = S
installs saved execution context S
resumes the real RocksDB read
Read finishes
Statistics scope removes S:
S -> P
S.onUnset()
Call OnComplete()
Wait returns <--------------- OnComplete signals caller
================ PUBLIC READ IS COMPLETE ================
Destroy restored DB C is still unwinding
Release BackupInfo Via guard restores previous context:
Last shared_ptr<Env> disappears P -> S
Begin Env E destruction S.onSet()
-> E->GetThreadID()
Env::~Env() <--------------- TSAN race ---------------> S.onSet()
```
The same context was captured in two roles:
```
Via execution_context = S required while the real read executes
C.outer_context = S accidentally restored after the read completes
```
The fix removes bridge `B` and posts under null. For an outstanding asynchronous read, the Via trampoline therefore queues `C` with `outer_context = null`, while retaining `execution_context = S`. The real read still executes under `S`, but after `OnComplete()` the Via guard restores null instead of reactivating `S`.
Reviewed By: anand1976
Differential Revision: D115967513
fbshipit-source-id: 87d744ecdbad3421c4d2a1a7be6423a3f0373573
Summary:
The parallel-compression auto-skip feature (https://github.com/facebook/rocksdb/issues/15079) introduced a data race that TSAN reports in block_based_table_reader_test. In StateTransition(), the auto-skip loop reads
ring_buffer[cslot].auto_skip_bypassed speculatively, from a not-yet-committed atomic_state snapshot, before the CAS that would validate that snapshot. Unlike every other BlockRep field (touched only after a successful CAS grants exclusive slot ownership), a stale snapshot can point that read at a physical ring-buffer slot the emit thread is concurrently recycling for a new block in EmitBlockForParallel, racing the plain write there. The speculative result is discarded when the CAS fails, so there is no logical corruption, but the plain read/write is a data race (UB) and is flagged by TSAN.
Make auto_skip_bypassed a RelaxedAtomic<bool> so the speculative read and the emit-thread write are atomic. Relaxed ordering suffices: correctness of the auto-skip decision is still gated by the CAS, and when the CAS succeeds the value was published by the emit thread through the existing acquire/release ordering on atomic_state and is not modified elsewhere.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15094
Test Plan: Covered by existing block_based_table_reader_test parameterizations that build with parallel compression (parallel_threads > 1) under TSAN, which is where the race was originally reported.
Reviewed By: joshkang97
Differential Revision: D115777206
Pulled By: pdillinger
fbshipit-source-id: 7ee969ec2d95890255d2e8b76eceaa5876b82170
Summary:
Custom filter and compression strategies decide what to build per SST from a FilterBuildingContext (shared by FilterPolicy::GetBuilderWithContext and CompressionManager::GetCompressorForSST). This adds more context so those strategies can adapt to where and how a file is being produced.
FilterBuildingContext gains two fields:
* is_remote_compaction: true only when the file is produced by an offloaded CompactionService worker. Detected with a new virtual CompactionJob::IsRemoteCompaction() (default false) overridden by CompactionServiceCompactionJob, mirroring the existing GetTableFileName hook. Local/fallback compactions and flushes are false.
* db_name: a non-owning Slice naming the DB, populated for flush, compaction, recovery, and repair, and empty only where there is no DB, as with SstFileWriter. Under remote compaction it is the DB as opened by the worker, which can differ from the primary's.
Both are threaded through TableBuilderOptions and populated in BlockBasedTableBuilder::Rep, whose "is this file in the LSM" test becomes a switch with no default case, so that a future TableFileCreationReason has to be classified there.
Separately, TableFileCreationReason is now backed by uint8_t and gains a kSstFileWriter value, which SstFileWriter reports instead of the catch-all kMisc. It is appended after kMisc so existing enumerator values are unchanged; those values are public API, surfaced to EventListener and mirrored by the Java binding (updated here). Note that SstFileWriter neither notifies listeners nor consults CompactionFilterFactory, so for now the new value is only observable through FilterBuildingContext.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15085
Test Plan:
* New CompactionServiceTest.IsRemoteCompactionInCompressorContext installs a custom CompressionManager and asserts GetCompressorForSST sees is_remote_compaction == true for the offloaded worker's output files and false for the primary's flushes in the same run, so the flag is shown to be per-file rather than global. IsRemoteCompactionFalseForLocalCompaction covers the same manager with no compaction_service configured.
* DBBloomFilterTest.ContextCustomFilterPolicy now reports db_name as a token (expected / none / UNEXPECTED:<value>), so the value rather than just its presence is checked for flush and compaction, and expects kSstFileWriter instead of kMisc for the external SstFileWriter file.
* bloom_test's "Like SST file writer" case uses kSstFileWriter so that it keeps modelling SstFileWriter.
* Java EventListenerTest references TableFileCreationReason.MISC.getValue() instead of a hardcoded enum byte, so it stays correct across enum changes.
Reviewed By: jaykorean
Differential Revision: D115603616
Pulled By: pdillinger
fbshipit-source-id: ad73fbc391a5377ace406725238ae6eede46c8d9
Summary:
Add a db_crashtest liveness mode that runs db_stress with a watchdog for
foreground progress. The profile disables fault injection and uses longer runs
so failures are more likely to represent real forward-progress bugs instead of
expected injected I/O errors. The watchdog tracks completed operations, active
operation type/start time, and per-operation completion counts to distinguish
stuck writes from long admin operations.
Bug: The new mode exposed a latent flush scheduling bug. A flush request can be queued
with an effective max memtable id, then a newer immutable memtable can be added
before the background flush picks work. PickMemtablesToFlush() could pick only
the older memtables and still clear flush_requested_. A later queued flush for
the newer memtable would then be skipped because the column family no longer
looked flush-pending, leaving the immutable memtable permanently unflushed. In
stress this surfaced as IngestExternalFile() waiting in WaitForFlushMemTables(),
with writes eventually blocked behind the same unflushed memtable.
Fix: Keep flush_requested_ set until all unstarted immutable memtables have been
picked. Add a direct MemTableList regression for the partial-pick case and a
DB-level regression covering the ingest/get-live-files ordering that reproduced
the lost request.
## Testing
- CI
- Local liveness mode 32 full 20-minute runs passed, 0 failures
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14830
Reviewed By: joshkang97
Differential Revision: D107895052
Pulled By: xingbowang
fbshipit-source-id: ca67c5f9376899eefdc9af7bad4af06200b52446
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15061
Adds mutable ColumnFamilyOptions field `blob_file_writable_file_max_buffer_size` to cap blob-file WritableFileWriter buffers independently from DBOptions::writable_file_max_buffer_size.
The default value is 0, which preserves existing behavior by inheriting the DB-wide writer buffer cap. Non-zero values apply to both flush/compaction blob writers and direct-write blob writers. Direct-write updates are propagated through DBImpl::SetOptions to newly opened blob files without requiring a DB restart.
Also wires option parsing, dumping, db_bench, db_stress, crashtest, and release-note coverage.
Reviewed By: xingbowang
Differential Revision: D114979265
fbshipit-source-id: 3f764b797628eee0ce379110c1a1f8a16beb3fa2
Summary:
The Claude/Codex CI reviewer was posting PR comments like "the review has been written to review-findings.md" instead of the review itself, and the comment footer cited claude_md/code_review.md as the review methodology even though CI stopped feeding that file to the model in https://github.com/facebook/rocksdb/issues/14507 (it switched to claude_md/ci_review_prompt.md). code_review.md had been orphaned from CI for months while still advertising itself as the CI methodology, so edits to it (e.g. the "post directly in your response" note) had no effect on CI. Separately, an over-long review exceeded GitHub's per-comment character limit and was rejected outright, losing the whole review.
Changes:
- ci_review_prompt.md: make the final response the review -- it becomes the PR comment verbatim -- and forbid ending the turn with a meta-summary such as "written to review-findings.md". review-findings.md is demoted to a recovery backstop; the per-phase incremental writes and the turn-limit recovery path are unchanged, so both the normal and recovery comment paths still produce a real review.
- Repoint the "Review methodology" footer in parse-claude-review.js and parse-codex-review.js to claude_md/ci_review_prompt.md (the file CI actually uses), so the citation no longer points at a deleted file.
- build-ai-review-comment.js: cap the assembled comment at GitHub's 65536-char limit, trimming only the model response (the sole unbounded input) and appending a pointer to the execution-log artifact, so an oversized review is truncated cleanly instead of rejected by GitHub. The header, footer, and <details> scaffolding are always preserved.
- Delete claude_md/code_review.md (CI-dead since https://github.com/facebook/rocksdb/issues/14507; superseded locally).
- Add claude_md/manual_code_review.md: a lightweight, single-reviewer local wrapper around ci_review_prompt.md (no multi-agent team/debate, no scratch files, inline output) for running a modest review by hand. It sources its methodology from ci_review_prompt.md so it cannot drift.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15087
Test Plan:
- The workflow scripts have no CI unit coverage; verified both parse-*-review.js still parse, and that no reference to code_review.md remains anywhere.
- Dry-ran manual_code_review.md by reviewing the current commit locally; it produced a right-sized single-reviewer report without spawning agents or writing files, and helped find+fix the comment limit issue.
- Exercised the end-to-end GitHub CI path (per https://github.com/facebook/rocksdb/issues/14696, review-workflow changes not yet on main can only be tested from a branch on facebook/rocksdb): pushed these changes to the fix_hidden_review_details_test branch and ran the Claude Code Review workflow (claude-review.yml) via "Run workflow" against PR https://github.com/facebook/rocksdb/issues/15079, confirming the posted comment carries the full review inline (not a review-findings.md summary) with the ci_review_prompt.md footer. https://github.com/facebook/rocksdb/actions/runs/31537781229https://github.com/facebook/rocksdb/pull/15079#issuecomment-5259127037
Reviewed By: xingbowang
Differential Revision: D115635558
Pulled By: pdillinger
fbshipit-source-id: d3ca7188162a086cd42d7b8bd8e60f3fc3df3bad
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15071
__Why__: Give operators visibility into currently running bottom-priority compactions. Low-priority running compactions can be derived from `rocksdb.num-running-compactions - rocksdb.num-running-bottom-compactions`.
__What__: Expose `rocksdb.num-running-bottom-compactions` as a new statistics property.
__How__: We already record `num_running_bottom_compactions_`, so this feeds that value through the DB property path.
Reviewed By: xingbowang
Differential Revision: D114989532
fbshipit-source-id: efa19ef3a69eaecf4cbbbc9e4fc9cd55fce09ca5
Summary:
Auto-skip compression (stop attempting compression on data that isn't compressing well, to save CPU) was previously an experimental CompressionManager wrapper (CreateAutoSkipCompressionManager) whose predictor lived in each per-thread working area. Besides being fragmented across parallel workers and reset on a tumbling window, it decided based on how OFTEN blocks failed to compress, ignoring how MUCH they compress -- so on mixed data it could give up substantial compression to save a little CPU (see performance results).
This makes auto-skip a first-class, optional, ratio-aware feature of the block-based table builder, controlled by two new CompressionOptions fields:
- auto_skip (default false): the on/off switch. There is no separate threshold knob; the decision reuses max_compressed_bytes_per_kb (the existing per-block "minimum compression worth keeping" bar) as the bar for whether attempting compression on the stream is worthwhile.
- auto_skip_min_sample_every: sampling interval that bounds the local impact around a transition back to compressible data. 0 selects an internal default (currently 64 skipped data blocks between forced samples).
The emit thread makes the skip/attempt decision once per data block (serial and parallel paths alike; data blocks only), so a skipped block is never dispatched to a compression worker. It is driven by a continuously updated, shared estimate q_hat of the aggregate stored/uncompressed ratio the builder would achieve by attempting -- each block counted at its achieved ratio, or 1.0 when stored raw: attempt while q_hat is at or below the bar, otherwise skip.
Because the estimate is payoff-weighted, a stream whose blocks each save little -- or where only a few blocks compress well -- does not clear the bar and is skipped, while a stream that compresses substantially is attempted. The compression given up per KB is bounded by (1024 - max_compressed_bytes_per_kb).
q_hat uses an asymmetric exponentially weighted moving average (EWMA), slightly faster toward better compression than toward worse; the fast side is deliberately gentle so a single marginally-compressible sample does not flip a whole file back to attempting. It is kept on its own cache line to avoid false sharing. Under parallel compression q_hat is updated with relaxed atomics, and the emit thread can decide from a value that lags the learning it drives by the in-flight depth -- tolerable concessions of accuracy and responsiveness for a coarse heuristic.
The first data block of each file is always attempted (the sample countdown starts at 0), and while skipping the builder keeps sampling (one block after a randomized gap) so a return to compressible data is detected. q_hat is carried across the files a flush/compaction thread emits via thread_local state keyed by preferred compression type, so a sustained region does not re-learn on every file.
The old CompressionManager-based AutoSkip is removed (CostAware is retained); db_stress, db_bench, and db_crashtest are migrated to the new option. db_bench is extended so it can generate the relevant regimes: --compressible_data_fraction and --compressible_data_run_length produce a mix of compressible/incompressible blocks -- i.i.d. per value (run length 1) or in alternating regions (large run length). The default (fraction 1.0) reproduces the historical single-pool behavior.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15079
Test Plan:
New/updated unit tests in `util/compression_test.cc` (`DBAutoSkipTest`):
- skips sustained incompressible data;
- never skips compressible data;
- disabled by default never bypasses a block;
- 4-thread parallel compression round-trips all data across alternating compressible/incompressible regions.
**Performance** (`db_bench fillseq`, release, zstd level 3; 100k values of 16 KiB each, roughly one value per data block; WAL and auto-compaction off; median of 5 runs on a quiet host). Auto-skip trades one cost for another, so each cell reports two costs, both lower-is-better: CPU cost as process CPU time (user+sys) per GB of uncompressed input (s/GB; ~1.64 GB uncompressed here), and storage cost as the resulting DB size. Auto-skip does not appear to change parallelizability, so wall-clock s/GB tracks CPU s/GB (the two agree here); CPU s/GB is reported. Percentages are relative to the no-auto-skip baseline: negative CPU = cheaper, positive size = more storage (shown only where storage grows).
| Workload | baseline (off) | (a) old wrapper | (b) draft | (c) this version |
|---|---|---|---|---|
| Incompressible | 2.33 s/GB, 1568 MB | 0.96 (-59%), 1568 MB | 0.82 (-65%), 1568 MB | 0.82 (-65%), 1568 MB |
| Compressible | 3.55 s/GB, 710 MB | 3.54 (~0%), 710 MB | 3.56 (~0%), 710 MB | 3.55 (~0%), 710 MB |
| Marginal-payoff mix | 3.80 s/GB, 1376 MB | 1.54 (-59%), 1523 MB (+11%) | 3.81 (~0%), 1376 MB | 1.43 (-62%), 1531 MB (+11%) |
| High-payoff mix | 2.20 s/GB, 929 MB | 1.14 (-48%), 1417 MB (+53%) | 2.16 (~0%), 929 MB | 2.17 (~0%), 929 MB |
A strategy wins by cutting CPU cost without paying much storage cost. On incompressible data all three cut CPU ~60% at no storage cost; on compressible data none skip (within run-to-run noise). The two mixes (50% incompressible, 50% compressible per block) separate the strategies:
- Marginal-payoff mix: the compressible half barely clears the per-block bar, so the stream is not worth compressing -- skipping is right. (c) and (a) cut CPU ~60% for ~11% more storage. That extra storage is compression (c) intentionally declines because its aggregate benefit is below the configured worthwhile threshold (max_compressed_bytes_per_kb, ~12.5% per KB); it stays within the (1024 - max_compressed_bytes_per_kb) bound. The ratio-unaware draft (b) cannot see the low payoff, keeps compressing, and saves no CPU.
- High-payoff mix: the compressible half compresses hard, so compressing is right. (c) and (b) keep full compression at baseline storage; the old wrapper (a) cuts CPU 48% but inflates storage +53% -- far beyond that bound -- giving up worthwhile compression.
Only (c) makes the right call on both mixes: it never gives up worthwhile compression (unlike (a)) and does not waste CPU on low-payoff data (unlike (b)).
Reviewed By: xingbowang
Differential Revision: D115444567
Pulled By: pdillinger
fbshipit-source-id: 05e122d25de95ddcd0edeb0dd07c9d25bead7293
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15084
MaybeOpenReadOnlyOnPrimary is intended to exercise opening and closing a read-only DB on the primary live directory, then verify that read-only close does not delete files still live in the primary.
D115278311 diagnosed T283967366 as a race between GetEntityLazy on that read-only handle and the primary deleting SST files. After checking the call path, the ENOENT is from GetEntityLazy's initial point lookup opening an SST through TableCache, not from deferred lazy column resolution. Other read APIs are not exercised on this read-only-on-primary handle, so adding GetEntityLazy there made this old open/close harness test also perform a live-directory point read.
Remove the read-only-handle GetEntityLazy exercise and keep GetEntityLazy coverage in the normal stress read paths, consistent with the other read operations.
Reviewed By: pdillinger
Differential Revision: D115569968
fbshipit-source-id: fcef30d09903525c2a7897b9c52bc9ef951bed6a
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15074
Why: We want to be able to monitor how many remote compaction jobs are running on a given server. Currently we know the total number of running compactions but not the local vs remote breakdown.
What: Add a new `rocksdb.num-running-remote-compaction-jobs` statistic. This measures in-progress remote compaction jobs.
Note that remote compaction work is scheduled per subcompaction, so one RocksDB compaction may contribute more than one running remote compaction job.
Reviewed By: jaykorean
Differential Revision: D115324149
fbshipit-source-id: 9397e39acd696c88ce7499ecb0da68de215bf1f3
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15076
Fix the `io_activity == Env::IOActivity::kUnknown || io_activity == options.io_activity`
assertion failure in the crash test. The crash occurs because `TestMultiGetEntity` in
`no_batched_ops_stress.cc` changes the thread operation to `OP_GETENTITY` during its
consistency-check phase (the `check_results` lambda calls `SetThreadOperation(OP_GETENTITY)`
before calling `GetEntity` for comparison). When `MaybeTestMultiGetEntityLazy` is called
afterward, it invokes `MultiGetEntityLazy` which correctly sets
`read_options.io_activity = kMultiGetEntity`, but the thread operation is still stale at
`OP_GETENTITY`. The debug assertion in `DbStressRandomAccessFileWrapper::Read` then sees
`TEST_GetExpectedIOActivity(OP_GETENTITY) = kGetEntity != kMultiGetEntity` and aborts.
The fix restores `SetThreadOperation(OP_MULTIGETENTITY)` before each call to
`MaybeTestMultiGetEntityLazy` in `TestMultiGetEntity`.
Triggered by commit cb73fc845fe0 which added the `MaybeTestMultiGetEntityLazy` calls.
The `--lazy_entity_read_one_in=100` flag (non-default) enables this code path.
Reviewed By: pdillinger
Differential Revision: D115021723
fbshipit-source-id: e3eed600db4986cab27d7b1924282b1dfb8b0a7a
Summary:
## Summary
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15080
Introduce reusable `CoroStackableDBBase` forwarding and public `CoroDB::CoGet()` / `CoMultiGet()` entry points matching the `DB::Get()` / `MultiGet()` overloads. These helpers schedule native coroutine reads on the read executor and use the synchronous DB API when coroutine support or a read executor is unavailable.
Use the stackable coroutine hooks for TTL and WritePrepared transaction reads. TTL preserves expiration decoding and timestamp behavior, while WritePrepared preserves transaction visibility.
TTL keeps a single `DB` inheritance path while adding `CoroDB`:
```
DBWithTTLImpl
`-- CoroStackableDBBase<DBWithTTL>
|-- DBWithTTL -> StackableDB -> DB
`-- CoroDB
```
`CoroDB::CoGet()` dispatches through the stackable coroutine hook and schedules the operation on the read EventBase. `CoMultiGet()` follows the same path:
```
CoroDB::CoGet(db, ...)
|-- db->GetCoroDB() -> DBWithTTLImpl
|-- no CoroDB or read executor -> db->Get()
`-- schedule on read EventBase
`-- virtual CoroDB::GetCoroutine()
`-- DBWithTTLImpl::GetCoroutine()
|-- CoroStackableDBBase<DBWithTTL>::GetCoroutine()
| `-- wrapped CoroDB::GetCoroutine()
`-- strip TTL timestamp
```
Reviewed By: xingbowang
Differential Revision: D115232510
fbshipit-source-id: 59e5090db28d15bcff1a8b9b8c0b30ca7eeb742d
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15078
The legacy regression test runner attempted to discover multi-DB origin directories by running find through run_remote, then assigned dirs=$?, which captured only find's exit code. For NUM_MULTI_DB > 1 this meant the checkpoint loop operated on the value 0 instead of the discovered sub-DB directories.
For NUM_MULTI_DB > 1, build checkpoints for the deterministic numeric sub-DB directories 0..NUM_MULTI_DB-1 and explicitly fail if any expected origin DB is missing. This avoids rediscovering DBs through find output, so checkpoint creation no longer depends on pipeline exit status or whitespace-sensitive word splitting.
Reviewed By: joshkang97
Differential Revision: D115380183
fbshipit-source-id: 24adfa37f872dd7b8ee4ee986cf4a7a4e58562d0
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15077
__Why__: Allow offload compaction workers to configure `max_open_files` via `options_map`.
__Problem__: `OpenAndCompact` hard-codes `max_open_files` to -1. This overwrites any value set via the `options_map` and prevents configuration in the remote compaction workers.
__Fix__: Remove the hard-coding assignment.
The value now comes from:
1. OPTIONS file via `LoadOptionsFromFile`
2. `override_options.options_map` via `GetDBOptionsFromMap`
The default value of `max_open_files` was already -1, so existing behavior is preserved.
Reviewed By: jaykorean
Differential Revision: D115373592
fbshipit-source-id: fa742397246092544ab6f9004678be7ae938d051
Summary:
## Summary
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15073
The callback-based async read APIs make coroutine-native implementations and StackableDB composition awkward because wrappers must bridge callbacks back into tasks. This change introduces `CoroDB` as a side capability exposing lazy `GetCoroutine` and `MultiGetCoroutine` tasks. `DBImpl` and `CompactedDBImpl` implement this capability directly, while `DB::GetAsync` and `DB::MultiGetAsync` remain available as compatibility entry points with synchronous fallback.
The native interface should also be more efficient because callers directly await the DB coroutine. It avoids the callback/Baton adapter, callback context handoff, and explicit rescheduling required for each request. Native tasks must run on the DB filesystem read executor's EventBase; debug builds verify this affinity.
Public API documentation, a release note, and native-coroutine support in `db_bench` and `db_stress` are included.
## Dispatch
DB::GetAsync
-> GetCoroDB() on the concrete DB
-> virtual CoroDB::GetCoroutine(...)
-> DBImpl::GetCoroutine or CompactedDBImpl::GetCoroutine
-> AsyncCallback::OnComplete(...)
Virtual dispatch selects the coroutine implementation belonging to the concrete DB while preserving the existing callback API.
## Statistics
Coroutine reads preserve RocksDB's TLS statistics behavior while keeping concurrent requests isolated. Each top-level task copies only the caller's statistics configuration—`PerfLevel`, per-level perf context enablement, and IO statistics enablement—into fresh request-local `PerfContext` and `IOStatsContext` objects. Existing counters are not inherited. If both perf and IO statistics are disabled, no request statistics context is installed.
Folly `RequestContext` preserves these contexts across suspension and resumption on the read executor's EventBase. At completion, RocksDB publishes the request's collected values to TLS, allowing the awaiting caller to use `get_perf_context()` and `get_iostats_context()` as after a synchronous read. Callers must consume or copy them before leaving that EventBase. Statistics scopes are installed only by top-level coroutine reads and cannot be nested.
## Benchmarks
Compared the previous `GetAsync` callback bridge with native `GetCoroutine`. Both variants used optimized builds, the same compacted DB, a fixed seed, eight executor threads, ten jobs per thread, and 2.56M reads per invocation. Results are the mean of three independent invocations using the default RocksDB block cache. A sequential read warmed the OS page cache before the non-direct-read measurements.
| Workload | `GetAsync` bridge | Native `GetCoroutine` | Change |
| Direct I/O | 237,710 ops/s | 243,588 ops/s | +2.47% |
| Warm OS page cache | 783,859 ops/s | 891,531 ops/s | +13.74% |
The direct-I/O difference is within observed run-to-run variation. When reads are served from the OS page cache and API overhead is a larger portion of latency, the native interface improves throughput by 13.74%. Every invocation completed all requested reads.
```bash
db_bench \
--benchmarks=readrandomcoroutine \
--db=$CORO_BENCH_DB \
--use_existing_db=true \
--num=2000000 \
--value_size=1024 \
--compression_type=none \
--threads=8 \
--coro_jobs_per_thread=10 \
--reads=320000 \
--use_direct_reads=<true|false> \
--perf_level=1 \
--statistics=true \
--seed=20260807
```
Reviewed By: xingbowang
Differential Revision: D115232511
fbshipit-source-id: 30b48da2a798094d9080178f5430aae26e6c5954
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15060
Expose the `rocksdb.num-unscheduled-compactions` DB statistic to help troubleshoot high L0 counts.
The metric captures the raw `unscheduled_compactions_` count. It measures compaction work waiting in the DB internal compaction queue, but not yet assigned to a background job.
This complements `rocksdb.compaction-pending`, which tells us "some compaction work is needed," and `rocksdb.num-running-compactions`, which tells us how many background compactions are executing.
`rocksdb.compaction-pending` is just a boolean value so we can't infer too much from it. `rocksdb.num-unscheduled-compactions` can be used to indicate that we may need to increase the max number of background jobs.
Reviewed By: xingbowang
Differential Revision: D114539448
fbshipit-source-id: 3657951bd9d8a4f4a7bc884513f9a06a87892b50
Summary:
Builds on the lazy wide-column read API (DB::GetEntityLazy / DB::MultiGetEntityLazy) to serve true byte-range reads: resolving a sub-range of an uncompressed blob column now reads only the requested bytes from storage instead of fetching and slicing the whole column. This covers blobs in separate blob files and embedded (same-file) blobs in the SST, so a caller that knows a large value's layout can pull just the parts it needs.
A partial read skips whole-record checksum verification (a strict sub-range cannot cover the checksum) and does not populate the blob cache (the cache entry holds the whole record). A compressed column, a whole-column read, a cache hit, or a read with LazyColumnReadRequest::force_verify still resolves (and verifies) the whole column; force_verify forces verification even when ReadOptions::verify_checksums is off.
Mechanics: new range primitives mirror the whole-value read paths -- BlobFileReader::GetBlobRange, BlobSource::GetBlobRange, Version::GetBlobRange, and VersionBlobFetcherBase::FetchBlobRange for separate-file blobs; ReadSimpleGen2BlobRange, BlobSource::GetSimpleGen2BlobRange, and SameFileBlobReader::GetSameFileBlobRange (implemented by BlockBasedTable) for embedded blobs. ReadPathBlobResolver::ResolveColumnRange owns the partial-vs-whole decision and routes same-file references through the current SST's reader; the whole-column path also gained
VersionBlobFetcherBase::FetchBlobForceVerify to honor force_verify.
Observability: deferred resolve reads are attributed to a new Env::IOActivity::kLazyResolve (distinct from the kGetEntity / kMultiGetEntity of the initial entity read), and lazy resolution's storage reads are counted by new statistics rocksdb.blobdb.lazy.read.count / .read.bytes (all lazy reads) and rocksdb.blobdb.lazy.partial.read.count / .partial.bytes.saved (the byte-range subset and the bytes it avoided). The whole-column read sites gate recording on kLazyResolve so non-lazy reads are excluded.
Also fixes a latent bug in LazyWideColumnsBatch::MultiResolve where an empty/default-constructed batch (null rep_) would accept a foreign standalone column (null owning_batch_rep_) through a nullptr == nullptr comparison instead of returning InvalidArgument.
Follow-ups (in a local plan): unifying the blob-read API surface -- folding the whole/range method twins and the verify-policy flags (verify_checksums x force_verify) into one internal BlobReadRequest primitive (a range descriptor plus a 3-valued verify policy), which retires FetchBlobForceVerify and the whole/range method pairs; this is deferred for feature velocity and is not public-API affecting (the public force_verify bool stays). Marked in code with TODO(lazy-blob-resolution-cleanup) in db/blob/blob_fetcher.h and db/wide/read_path_blob_resolver.cc. Also cross-key coalescing (Phase 3) and async execution (Phase 4).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15067
Test Plan:
Extended db/wide/db_lazy_entity_test.cc: separate-file and embedded partial reads return the exact sub-span and read only the requested bytes; a partial read does not fill the blob cache and a cache hit slices with no I/O; a compressed column resolves whole and slices; force_verify does a full verified read and is detected via a tampered record even with verify_checksums off; the lazy statistics distinguish whole vs partial reads and are not touched by non-lazy reads; deferred reads carry kLazyResolve (verified with a FileSystem wrapper); and a regression test for the empty-batch ownership check. Added an IOActivityToString case in env/env_test.cc and registered the new tickers in the statistics name map.
Crash test: extended the db_stress lazy-vs-eager differential (db_stress_tool/db_stress_test_base.cc) to fuzz per-column byte ranges. Each resolved column now randomly picks a whole-column read or a partial (offset, length) sub-range -- occasionally with an offset past the end or a length beyond the remainder to exercise clamping -- for both GetEntityLazy and MultiGetEntityLazy; when an eager reference is available the resolved bytes are compared against the same clamped sub-range of it. No new flags (reuses --lazy_entity_read_one_in); force_verify fuzzing remains a follow-up.
Reviewed By: xingbowang
Differential Revision: D115130388
Pulled By: pdillinger
fbshipit-source-id: 454368dea8fae069d9de649c2c51611469d3c809
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15059
**What**:
1. Extends the parallel `CheckpointEngine` so it can also checkpoint a subset of column families.
2. Adds `db_stress` and `db_crashtest.py` coverage for subset checkpoints, over both the serial and the parallel API.
**How**:
- **Engine API.** `CreateCheckpointOptions` gains a `std::vector<ColumnFamilyHandle*> column_families` field (empty, the default, still means all column families). This goes on the existing per-operation options
truct rather than adding a second pure virtual to `CheckpointEngineBase`: it avoids overload-resolution hazards with the defaulted `sequence_number_ptr`, and it is the pattern the engine API already uses.
`CreateCheckpointOptions` has no users outside the engine API, so adding a field breaks nothing.
- **Shared validation.** The handle checks that were inline in `CheckpointImpl::CreateCheckpoint` -- reject null handles, handles belonging to another DB, and handles for already-dropped families; coalesce ids;
always add the default CF -- move to `static CheckpointImpl::ResolveIncludeColumnFamilyIds()`, now called by both APIs. Rejecting a foreign handle matters because `handle->GetID()` alone would silently coerce a
foreign id onto whichever family in this DB happens to share it. Validation runs before any staging directory is created, so a rejected request leaves nothing behind.
- **Almost no new plumbing.** `CheckpointImpl::CreateCheckpointImpl()` already accepted `include_cf_ids` and is shared by the serial and parallel movers, and the MANIFEST `kColumnFamilyDrop` rewrite runs after
`mover->Finish()`, so it was already mover-agnostic. The engine change is just resolving the ids and forwarding them.
- **db_stress.** New `subset_cf_checkpoint_one_in` knob (mirrors `parallel_checkpoint_one_in`): each checkpoint operation covers a random proper subset with probability 1/N. It is a no-op when the DB has only the
default column family, and it is independent of `parallel_checkpoint_one_in`, so subset checkpoints get exercised through both the serial API and the engine. Subset selection includes each non-default family
with probability 1/2 and then force-excludes one, so every subset checkpoint exercises the MANIFEST rewrite instead of degenerating into a whole-DB checkpoint; the default handle is passed in only half the time,
covering the "default is always included whether or not you pass it" contract on both sides.
- **db_stress validation.** `DB::ListColumnFamilies()` on the checkpoint must return exactly the requested set -- the direct check on the MANIFEST post-processing, catching both an excluded family that was not
recorded as dropped and a requested family that went missing. The checkpoint is then opened read-only with exactly those descriptors, proving the reduced MANIFEST and reduced file set are self-consistent under
the default `paranoid_checks`. The pre-existing per-key comparison against `SharedState::Exists()` still runs, restricted to the families actually present.
- **Incompatibility.** `clear_column_family_one_in > 0` is rejected together with the new knob, the same way D113103899 did for `abort_and_resume_cf_compactions_one_in`: a subset checkpoint hands live handles
from `column_families_` to the API and validates against `column_family_names_`, and neither is stable while families are dropped and recreated underneath. `db_crashtest.py` already pins
`clear_column_family_one_in` to 0.
- **Test cleanup.** The subset tests shared a lot of setup and verification, so `PopulateFourCfsForSubsetCheckpoint()`, `VerifyFourCfsIntact()` and `VerifyDefaultAndTwoSubsetCheckpoint()` were extracted into the
test file's anonymous namespace and are reused by the pre-existing `CheckpointSubsetOfCF` as well as the four new engine tests.
This extends `StressTest::TestCheckpoint` with a mode rather than adding a separate operation, which is how `parallel_checkpoint_one_in` was integrated.
Reviewed By: rban1
Differential Revision: D114832267
fbshipit-source-id: f3f843819614477497cc1466910547922cafc51d
Summary:
- make the ARM nightly crash-test swap setup skip `/swapfile` creation when it is already active
- keep the existing 4 GiB swap creation path for hosts without active `/swapfile`
- print the active swap devices after setup for easier workflow diagnostics
## Testing
- `git diff --check upstream/main...2026_08_06_arm_failure`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15065
Reviewed By: archang19
Differential Revision: D115045941
Pulled By: xingbowang
fbshipit-source-id: ff2248b8501c82f5f9e7561bd77937d4823bc67f
Summary:
Adds an experimental **lazy wide-column read API** that lets callers enumerate a
wide-column entity and read back only the columns (and, in a later phase, the
byte ranges) they actually need, instead of eagerly materializing every
blob-backed column like `GetEntity()` / `MultiGetEntity()`.
New public entry points (`include/rocksdb/db.h`):
- `DB::GetEntityLazy(read_options, cf, key, LazyWideColumns* result)`
- `DB::MultiGetEntityLazy(read_options, cf, num_keys, keys, LazyWideColumnsBatch* result, statuses, sorted_input)`
Both leave a wide-column entity's **inline** columns materialized up front but
its **blob-backed** columns as *unresolved references*, so blobs for columns
that are never pulled are never read.
### Result types (`include/rocksdb/lazy_wide_columns.h`)
- **`LazyWideColumn`** — the lazy analogue of `WideColumn`: `name()`, `index()`,
`inline_value()` (an `OptSlice`; empty ⇒ unresolved reference),
`is_reference()`, and `logical_size()` (`std::optional<uint64_t>`; known for
inline and uncompressed references, empty for compressed ones). All no-I/O.
- **`LazyWideColumns`** — a move-only result that is **indexable and iterable
like a vector** of `LazyWideColumn` (`size()`, `operator[]`, range `begin/end`).
Unlike `PinnableWideColumns`, it may be used after the producing call returns:
it **pins the SuperVersion** (like an iterator) so referenced blob files / SST
readers stay valid for deferred reads.
- **`LazyWideColumnsBatch`** — the `MultiGetEntityLazy` result; vector-like over
its per-key `LazyWideColumns`.
### Resolution
- **`LazyColumnReadRequest { const LazyWideColumn* column; uint64_t offset;
size_t length; bool force_verify; PinnableSlice* result; Status* status; }`** —
a read names a **typed column pointer**, not an untyped index. Because a
column identifies its owning result (and, for a batch, its owning entity),
`MultiResolve` routes each read correctly and **rejects a null or foreign
column** with `InvalidArgument` on that read's per-read status — cross-result
misuse can't be silently mis-indexed.
- **`LazyWideColumns::MultiResolve` / `LazyWideColumnsBatch::MultiResolve`** take
the same request type (pointer+count and `std::vector` sugar overloads); the
batch form resolves reads spanning any entities of the batch in one call.
- **`ResolveColumn(column, result)` / `ResolveColumnRange(column, offset, length,
result)`** — per-column sugar over a one-entry `MultiResolve`.
- A column's blob is fetched **at most once** over the result's lifetime (bytes
are cached in the result), so repeated reads of the same column reuse that
fetch. Resolution honors the `ReadOptions` captured at `GetEntityLazy()` time
(e.g. `read_tier`, `verify_checksums`); the resolve methods intentionally take
no per-resolve `ReadOptions`.
### Where it works
- Primary, **read-only**, and **secondary** DB instances (each `GetImpl`
override forwards the lazy signal and transfers a SuperVersion pin via the
shared `DBImpl::TransferSuperVersionPin` helper).
- Forwarded through `StackableDB` wrappers (e.g. `TransactionDB`) rather than
falling through to the `NotSupported` base default.
- Requires `max_open_files == -1` (immortal table readers) so same-file
("embedded") references stay resolvable lazily; otherwise the call returns
`InvalidArgument`.
## Implementation notes
- Read path: `GetContext` gains a lazy mode that leaves blob references
unresolved and captures the SST's `SameFileBlobReader`; `Version::Get` forwards
the new (defaulted) `lazy_columns_same_file_reader` out-param.
- `ReadPathBlobResolver` performs on-demand, cached blob resolution bound to the
pinned `Version` + decoded blob references.
- Internal identity is by heap-stable `Rep` pointers, so column/result/batch
pointers stay valid across moves with no move-time fix-ups.
## Scope / current limitations
- Resolves **whole columns**; byte ranges are served by slicing the whole column
for now (so `length`/`offset`/`force_verify` don't yet save I/O or skip
checksums). Single column family. Synchronous. `kBlockCacheTier` on the
originating call yields `Incomplete` on a blob-cache miss.
- User-defined timestamps: entities aren't writable under UDT, so `GetEntityLazy`
is at parity with `GetEntity` (same read-timestamp validation; plain values
return a single default column).
- Planned follow-ups: partial (byte-range) blob reads that skip checksum/cache-
fill, cross-key coalescing, and asynchronous execution.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15012
Test Plan:
- **`db/wide/db_lazy_entity_test.cc` (15 tests, all passing):** enumeration with
no blob I/O; single range and whole-column resolution; `MultiResolve` of many
blobs and mixed ranges/columns (incl. the `std::vector` overload); cross-key
batch resolution; result-outlives-call / pin survives move; unpulled columns
never read; `max_open_files == -1` requirement; `kBlockCacheTier` ⇒
`Incomplete`; foreign/null column ⇒ `InvalidArgument` (single-entity and
batch); UDT parity with `GetEntity`; `StackableDB` forwarding; and read-only
and secondary instances.
- **db_stress:** `GetEntityLazy` / `MultiGetEntityLazy` run as a differential
against eager `GetEntity` / `MultiGetEntity` (gated by `--lazy_entity_read_one_in`,
which only takes effect under `--open_files=-1`), including coverage on
read-only (`--open_read_only_one_in`) and secondary (`--test_secondary`)
instances. Smoke-run (`EXIT=0`, "Verification successful") with integrated blob
files + `--use_put_entity_one_in`.
- `make format-auto` clean; builds `ASSERT_STATUS_CHECKED`-clean.
Reviewed By: xingbowang
Differential Revision: D113792365
Pulled By: pdillinger
fbshipit-source-id: cb73fc845fe0e20ed352d697e552e7b47b2eb9cf
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15054
`db_stress` often terminates on raw `assert(status.ok())` checks, which discard the underlying error and make crash-test failures difficult to diagnose.
Log the expression and full status before terminating. A message variant preserves operation-specific context and variadic formatting. In debug builds, assert after logging to retain assertion diagnostics and stack traces. In optimized builds, where assertions are disabled, exit with status 1.
Reviewed By: jaykorean
Differential Revision: D114756034
fbshipit-source-id: 5a0d4409bac69820b6560c8200cc401b7bfc65d6
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15052
When `persist_user_defined_timestamps=false`, `full_history_ts_low` is automatically advanced by flushes to just above the newest timestamp in the flushed memtable. This creates the following race:
```
Reader Concurrent writer / flush
------ -------------------------
T0: Sample read timestamp:
892779959679761
T1: Write with a newer timestamp:
892779963289806
T2: Flush the memtable.
T3: Advance full_history_ts_low:
892779963289807
T4: Validate the read:
892779959679761
< 892779963289807
Result: InvalidArgument
```
This is expected RocksDB behavior because history below `full_history_ts_low` is no longer guaranteed to be complete. However, the stress test intends these operations to read the latest value.
This change adds ten seconds of headroom to read timestamps when user-defined timestamps are not persisted. This makes it substantially less likely that a concurrent flush overtakes a read timestamp. Unlike tolerating every `InvalidArgument`, this preserves reporting for genuine timestamp errors. Persisted read timestamps and write timestamps remain unchanged.
The triggering non-default options are `persist_user_defined_timestamps=false` and `user_timestamp_size=8`.
Reviewed By: xingbowang
Differential Revision: D114565978
fbshipit-source-id: 6939df9163fd97fefec2fa185bc2ca72231ab04f
Summary:
`TransactionLogIterator` iterates the write-ahead log. It does not return transactions, does not require `TransactionDB`, and in fact returns `Status::NotSupported()` for two of the three `TransactionDB` write policies (WritePrepared and WriteUnprepared). The name predates `TransactionDB` and comes from "transaction log" being an old synonym for WAL. Everything else in this header was already renamed away from that terminology -- `LogFile` -> `WalFile`, `VectorLogPtr` -> `VectorWalPtr` -- and the C API has long used `rocksdb_wal_iterator_t` and `rocksdb_wal_iter_*`. This finishes the job.
The public class is now `WalIterator` in `include/rocksdb/wal_iterator.h`. `include/rocksdb/transaction_log.h` remains as a forwarding header and `TransactionLogIterator` remains as an alias, both marked deprecated by comment only (not `[[deprecated]]`) so existing users compiling with -Werror are unaffected, matching how `LogFile` and `VectorLogPtr` were retired. `DB::GetUpdatesSince()` keeps its name.
More importantly, the documentation was incomplete in ways that could cost a user data, so this states plainly what the API has always done:
- It only sees the WAL. Writes made with `WriteOptions::disableWAL`, and sequence numbers consumed by `IngestExternalFile()`, advance the DB sequence number without producing a WAL record. They leave permanent holes that end iteration. This was not documented anywhere, so it was unreasonable to expect a user to know those features silently break WAL tailing.
- The three iterator states, in particular that `!Valid()` with an OK `status()` means caught up rather than finished, and that calling `Next()` again is the intended way to tail a DB. The old comment on `Next()` said "REQUIRES: Valid() to be true", which forbids the primary use case.
- `Status::TryAgain`, which a caller must handle but which appeared nowhere in the public headers.
- That `GetUpdatesSince(seq)` silently starts later when `seq` is unavailable. Combined with the above this is the sharp edge: recovering from a spent iterator means calling `GetUpdatesSince()` again, so the recovery step is exactly where data can be skipped unnoticed. A consumer applying these batches to a copy of the DB diverges permanently and undetectably if it misses one, so the header now says such consumers must verify continuity themselves using `BatchResult::sequence` and `WriteBatch::Count()`.
Almost all of this diff is mechanical: renaming the type and its files, updating `#include` paths, rewriting comments, and updating build lists (`src.mk`, `CMakeLists.txt`, `BUCK`, `java/Makefile`, `java/CMakeLists.txt`). `db/c.cc` is regenerated from `tools/c_api_gen/c_base.cc`. Reviewers should focus on the four places that are not:
1. `WalIteratorImpl::UpdateCurrentWriteBatch()` in `db/wal_iterator_impl.cc` (renamed from `db/transaction_log_impl.cc`) drops dead code. On a sequence discontinuity it set `current_status_` to `NotFound` and then called `SeekToStartSequence()`, which returns immediately when `current_status_` is not OK. That guard and the early return in `Next()` were both added by fe63899d1 ("Add checks to GetUpdatesSince", https://github.com/facebook/rocksdb/issues/9459), which made the reseek unreachable without mentioning it. The surviving behavior -- a discontinuity ends the run with `NotFound` -- is what the class has always documented, so this deletes the unreachable reseek and the comment claiming it can restore an OK status rather than resurrecting it. There is no observable change: `Valid()` is `started_ && is_valid_`, `is_valid_` is false either way, and a non-OK `current_status_` already makes `Next()` a no-op.
2. `WalIteratorImpl::IsBatchExpected()` in the same file drops "Log iterator will reseek the correct batch." from its info log message, since it no longer does.
3. `org.rocksdb.WalIterator` now holds the implementation and the nested `BatchResult`, and `org.rocksdb.TransactionLogIterator` becomes a subclass of it. Existing Java code keeps compiling, `RocksDB.getUpdatesSince()` keeps its return type, and `TransactionLogIterator.BatchResult` still resolves through inheritance. Because the native methods are now declared on `WalIterator`, the JNI entry points in `java/rocksjni/wal_iterator.cc` (renamed from `transaction_log.cc`) become `Java_org_rocksdb_WalIterator_*`.
4. `BatchResultJni::getJClass()` in `java/rocksjni/portal.h` looks up `org/rocksdb/WalIterator$BatchResult` to match.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15056
Test Plan:
Adds three tests to `db_log_iter_test.cc` that pin the semantics now being documented, none of which had coverage:
- `CaughtUpThenResumes` -- an iterator that has run out of records is `!Valid()` with an OK `status()`, and a later write to the same WAL is picked up by calling `Next()` again. This is the tailing pattern the old `Next()` comment prohibited.
- `GapFromDisableWAL` -- a `disableWAL` write between two normal writes ends the run with `NotFound` instead of skipping to the later sequence number, and the iterator stays spent across further `Next()` calls.
- `SilentlySkipsUnavailableStart` -- `GetUpdatesSince(1)` where sequence 1 never reached the WAL returns OK and positions at sequence 2, documenting the silent skip.
`IngestExternalFile()` is documented as the other source of holes but is not covered by a test; it needs `SstFileWriter` setup and is worth a follow-up.
The Java rename is verified by `WalIteratorTest`, which declares `WalIterator` while `RocksDB.getUpdatesSince()` still returns `TransactionLogIterator`, so the compatibility subclass is exercised on every use. It is otherwise an identifier-only rename of the previous test.
Renames the C++ test fixture to `DBWalIteratorTest`, drops the now-redundant `TransactionLogIterator` prefix from its test names, and updates the comments in `wal_manager.cc`, `sync_point.h` and `fs_posix.cc` that refer to those tests by name. Those comments already pointed at the wrong fixture and file (`DBTest` in `db_test.cc`) before this change.
Reviewed By: xingbowang
Differential Revision: D114809792
Pulled By: pdillinger
fbshipit-source-id: d3cfd23867b4738b62a096db1dbc895bada069b7
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14982
**What**: Add reference-counted `DB::AbortCompactions(cf)` / `DB::ResumeCompactions(cf)` to abort and resume compactions for a single column family without stopping unrelated column families. They compose with the DB-wide `AbortAllCompactions()` / `ResumeAllCompactions()` and are no-ops once DB shutdown has begun.
**How**:
- **Per-CF abort flag.** `ColumnFamilyData` gains `compaction_aborted_`, wrapped in `CacheAlignedWrapper` so the lock-free reads done by compaction workers do not false-share with the DB-mutex-written fields packed around it. A new `IsCompactionAborted(cfd)` helper checks it alongside the DB-wide counter at every entry point (`CompactRange()`, `RunManualCompaction()`, `CompactFilesImpl()`), and `CompactionJob::ProcessKeyValue()` checks both on entry and in its periodic cron check.
- **Parking.** Automatic work for an aborted CF is preserved rather than dropped. `PickCompactionFromQueue()` now returns `std::variant<CompactionQueuePicked, CompactionQueueThrottled, CompactionQueueParked>` and moves an aborted CF into `parked_compaction_cfds_` only once it reaches the front of the queue, so CFs behind it keep their place and the scheduler credit is not restored (which would cause a hot re-pick loop). `EnqueuePendingCompaction()` parks newly needed work for an aborted CF and now early-returns for dropped CFs. `RestoreParkedCompaction()` re-enqueues on the final resume; a CF dropped while parked is handled in `DropColumnFamily()`, and one dropped before its aborted queue entry is reached is handled by a fast path in `PickCompactionFromQueue()`.
- **Knowing when it is safe to return.** `ColumnFamilyData::num_in_flight_compactions_` counts actually-picked work, and `Defer` balances `BeginInFlightCompaction()` / `EndInFlightCompaction()` on every `BackgroundCompaction()` and `CompactFiles()` exit path. That covers the `NotifyOnCompactionCompleted()` window that `CompactionPicker::compactions_in_progress_` does not, since the picker set is cleared during `Install`'s manifest write. `MustWaitForCompaction()` combines the counter with the picker set (filtered to skip `kExternalSstIngestion` / `kRefitLevel` reservations, which are not real jobs) because only the picker set covers the LOW-to-BOTTOM priority handoff, where the counter briefly dips to 0.
- **Waking the waiter.** `AbortCompactions()` cancels matching `manual_compaction_dequeue_` entries and then waits on `bg_cv_`; `per_cf_compaction_abort_waiters_` forces the compaction completion paths to signal, because the picker's `UnregisterCompaction()` does not. `CloseHelper()` drains `parked_compaction_cfds_` and releases their references.
- **API surface.** The two methods are new pure virtuals on `DB`, so `StackableDB` forwards them and the `db_test.cc` DB wrapper stubs them. `db.h` documents how the per-CF API composes with DB-wide abort, `DisableManualCompaction()`, `PauseBackgroundWork()`, shutdown, ingestion reservations, level refit and remote compaction, plus the parked-work caveat on `WaitForCompact()`. Comments in `listener.h`, `statistics.h` and `status.h` now name both abort APIs.
- **Coverage.** 10 new cases in `db_compaction_abort_test.cc` (CF scoping, counter composition, parked-work restore, CF drop both while parked and before parking, trivial-move rejection, waiting for the completion callback, post-close no-op). `db_stress` gains `abort_and_resume_cf_compactions_one_in` and `TestAbortAndResumeCfCompactions()`, rejected in combination with `clear_column_family_one_in` because that would drop the column family out from under an active abort. Also a `db_crashtest.py` knob and one `new_features` release note.
- **Known gap.** A `TODO` records the pre-existing post-pick stale compaction-score problem: aborting after `PickCompaction()` releases the inputs without recomputing the score, so the resume can observe a stale sub-trigger score and re-enqueue nothing. `ResumeAllCompactions()` has the same gap and carries a pointer to the same TODO; fixing it is left to a separate change covering both APIs.
**Why**: Atomic-replace bulk loads only conflict with their target column family, but the DB-wide abort suppresses compaction for every column family colocated in the same RocksDB instance. Scoping the abort keeps unrelated column families compacting for the duration of a bulk load.
Reviewed By: pdillinger
Differential Revision: D113103899
fbshipit-source-id: 9bef924132676ed118a37aee86da6ee8bcefef09
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15046
`BlobFetcher` is the policy boundary for single-blob reads, but
`Version::Get()` and primary direct-write postprocessing still called
lower-level backends directly. This duplicated backend selection and could
bypass future fetcher policy or decorators.
Route those paths through their existing `VersionBlobFetcher` and remove the
redundant Slice-based `Version::GetBlob()` overload. This is behavior-preserving:
each fetcher delegates to the same Version or direct-write backend with the same
`ReadOptions`, cache, and lifetime context.
Reviewed By: xingbowang
Differential Revision: D114616978
fbshipit-source-id: 0d94cc389138f27791de636e2cca585e51cb142a
Summary:
Adds db_stress / crash-test coverage for the parallel `CheckpointEngine` so it
is exercised alongside existing features (atomic flush, sync fault injection,
cf_consistency, backups).
- Opens one shared `CheckpointEngine` per db_stress process, configured by
`--checkpoint_engine_max_background_operations` and
`--checkpoint_engine_use_link_file_when_available`.
- `--parallel_checkpoint_one_in=N`: each checkpoint independently uses the
parallel engine with probability 1/N, otherwise the legacy serial
`Checkpoint` API, so serial and parallel paths run concurrently across threads.
- `db_crashtest.py` randomizes all three per run.
This coverage surfaced the missing destination fsync in the parallel copy path
fixed by the parent diff.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15037
Reviewed By: xingbowang
Differential Revision: D114414578
Pulled By: rban1
fbshipit-source-id: 48e7920a62b9b663a1c6054fc60b5765667bcbe5
Summary:
Parallel checkpoint copies were never fsynced. `CopyEngine::CopyOrCreateFile`
gated the destination sync on `use_fsync` (wired to `db_options.use_fsync`) --
but `use_fsync` means "fsync vs fdatasync", not "whether to sync". With the
default `use_fsync=false`, the parallel path skipped the sync, so a copied
MANIFEST/SST stayed in the unsynced buffer and was dropped by
`TestFSWritableFile::Close` under `sync_fault_injection`, leaving a 0-byte file
and checkpoint corruption:
Corruption: no log_file_number, next_file_number, last_sequence entry in MANIFEST
Corruption: Sst file size mismatch between expected N and file system 0
Serial `CopyFile` always syncs (using `use_fsync` only to pick the flavor), so
it never hit this.
Fix: split `WorkItem` / `CopyOrCreateFile` into `sync` (whether to sync) and
`use_fsync` (fsync vs fdatasync). CheckpointEngine now passes `sync=true,
use_fsync=db_options.use_fsync`, matching serial. BackupEngine is unchanged --
`sync=options_.sync` with the new `use_fsync=false` default reproduces the prior
behavior.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15038
Reviewed By: xingbowang
Differential Revision: D114414620
Pulled By: rban1
fbshipit-source-id: e0f1f3dbadcd48c5a5c2a11111a266dbed623f86
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15034
`db_stress` startup validation had accumulated multiple error-reporting paths, including direct `fprintf`/`exit(1)` pairs. This made invalid configurations inconsistent and bypassed normal unwinding.
Centralize validation failures through `ReturnValidationError`, including diagnostics with runtime values, while leaving operational I/O failures on their existing paths. The variadic stream interface keeps the existing messages without `printf` casts.
Reviewed By: jaykorean
Differential Revision: D114384751
fbshipit-source-id: f396f8e7d099ef87c72aaf19455b777f2125f4df
Summary:
The C API compatibility job always fetched `main` as its baseline. On release branches, that can report false API removals for symbols that were added to `main` after the release branch was created.
Select the compatibility baseline from the triggering event instead:
- Pull requests use the merge target commit.
- Pushes use the branch's pre-push commit.
- Newly created branches or tags use `HEAD`, since they have no prior ref state to preserve.
Fetch the selected commit by SHA, fail explicitly for unsupported events or missing event data, and pass the resolved baseline to `check-c-api-gen`. Also update the Makefile documentation to describe the PR and push behavior.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15039
Test Plan: CI
Reviewed By: pdillinger
Differential Revision: D114437175
Pulled By: xingbowang
fbshipit-source-id: ee66f54bcabba238477d07a69ec7dcb42219c088
Summary:
- add `rocksdb_batched_multi_get_pinned_cf` for batched reads using `rocksdb_slice_t` keys
- return one `rocksdb_pinnable_multi_get_t` owner for all values and per-key errors
- preserve input ordering, duplicates, misses, and sorted-input behavior
- expose indexed borrowed results plus count and destroy functions
## Why
`rocksdb_batched_multi_get_cf_slice` returns one separately allocated `rocksdb_pinnableslice_t` wrapper for every found key. Language bindings that want to retain a full batch have to manage and destroy each wrapper independently.
The new API keeps successful `PinnableSlice` values in one owner. It stores only compact value and error payloads, while a result index maps them back to the original key order. Found values and error messages remain borrowed until the batch is destroyed.
The batch must be destroyed before closing the DB that created it because pinned values can retain DB-owned cache state.
## Compatibility
This is additive. Existing MultiGet APIs and ownership rules are unchanged.
## Testing
`c_test` covers:
- found and missing keys
- duplicate keys and input ordering
- empty values
- per-key merge errors
- sorted and unsorted input
- empty batches
- out-of-bounds access
- borrowed value and error lifetimes
Validation run locally:
- `make -j8 check-c-api-c_test`
- `CXX=clang++ make check-c-api-gen CHECK_C_API_GEN_STRICT=1 CLANG_FORMAT_BINARY=clang-format`
- C API link-completeness and backward-compatibility checks
This API is intended to replace a temporary compatibility extension in `rust-rocksdb` once it is available in a RocksDB release.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14988
Reviewed By: jaykorean
Differential Revision: D114357453
Pulled By: xingbowang
fbshipit-source-id: a35785f7bb4d097b391932e2b9f96fc177d1ba1c
Summary:
CI/build downloaded pinned third-party binaries (compression source tarballs, Java test jars, and clang-format-diff.py) from an S3 bucket. Move the mirror to assets on an unlisted "third-party-deps" GitHub release, so the files live alongside the code with no separate bucket to maintain and no per-repo bandwidth quota (unlike Git LFS). Recent releases on the RocksDB release page are not polluted (https://github.com/facebook/rocksdb/releases/, far down the list probably from tagging a very old release).
The compression download variables are refactored from *_DOWNLOAD_BASE (a directory URL) to *_DOWNLOAD_PREFIX (directory + "<project>-", or "...archive/v"), so each release asset can carry a project-name prefix in the otherwise flat release namespace while the upstream defaults still resolve to byte-identical URLs. The Java DEPS_URL defaults (Makefile and CMake) and the clang-format-diff.py download are repointed at the release. A comment at each dependency site links to the release edit page for adding/updating assets.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15041
Test Plan:
CI and...
Downloaded all 11 assets from the release and confirmed their SHA256 sums match the files previously served from S3 (tarball/jar sums are also enforced by the existing Makefile checksum checks). Verified the refactored Makefile upstream defaults expand to the same URLs as before, and that the CI *_DOWNLOAD_PREFIX overrides plus DEPS_URL resolve to the uploaded asset names. Covered by the normal PR/nightly/weekly jobs that build compression from source and run the Java tests (Make and CMake).
Reviewed By: xingbowang
Differential Revision: D114443886
Pulled By: pdillinger
fbshipit-source-id: 8c59435e7064f0d9931cba103c8120fe113b0d6d
Summary:
ReadOptions is copied frequently on read paths, and its only non-trivially-copyable member, table_filter, is a std::function that is almost always empty yet still gets deep-copied on every ReadOptions copy (and can heap-allocate). This changes table_filter to a pointer to a caller-owned std::function instead of storing one by value: when non-nullptr, the caller is responsible for keeping the pointed-to function alive for the duration of the read (like the other pointer fields such as iterate_upper_bound). With this change every ReadOptions member is trivially copyable, so copying ReadOptions no longer touches a std::function at all. That trivial-copyability is locked in with a static_assert in options_test.cc (kept out of the public header).
This is inspired by D113109635, which targeted the same ReadOptions-copy hotspot by relocating table_filter to the end of the struct so the trivial members memcpy in one contiguous block. This is intended to be a superior solution: rather than only defragmenting the copy, it removes the std::function copy entirely.
This is a breaking API change, tolerated because table_filter is a relatively obscure option. A nullptr, or a pointer to an empty std::function, means no filtering (preserving the previous empty-function behavior). A shared internal helper HasTableFilter(const ReadOptions&) in options_helper.h centralizes the nullptr + empty-function check used on the read paths. The C API and Java bindings now own the std::function and hand ReadOptions a pointer to it; their public signatures are unchanged, and the C API rocksdb_readoptions_t clears the owned function when the callback is null.
Making ReadOptions trivially copyable also let
-Werror=unused-but-set-variable fire on a pre-existing dead local in db_bench's MultiScan: a ReadOptions that set async_io/readahead_size but was never passed to NewMultiScan (the warning had been suppressed while ReadOptions had a non-trivial member). That local was in fact inert for multiscan -- BlockBasedTableIterator::Prepare overrides ReadOptions::async_io with MultiScanArgs::use_async_io for the coalesced prefetch, so multiscan async I/O is driven by --multiscan_use_async_io, not ReadOptions::async_io. Removed the dead local; MultiScan now uses read_options_ directly, consistent with MultiScanRandom.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15005
Test Plan:
Existing table_filter coverage was adapted to the new pointer API and still exercises the behavior end to end: db_iterator_test.TableFilter (callback invoked once per table; returning false hides a table) and its range-conversion rejection case; db_bloom_filter_test SstQueryFilter/FixedWidthSegments range queries; and trie_index_db_test's range-query iterator (which returns iterators outliving the filter, exercising the caller-owned lifetime). db/c_test covers the C API set/clear/has_table_filter path. options_test asserts std::is_trivially_copyable_v<ReadOptions>.
Manually verified the db_bench MultiScan async behavior with a ~200MB DB and --use_direct_reads: with ReadOptions::async_io held true, async reads tracked only --multiscan_use_async_io (rocksdb.read.async.micros count 329511 with use_async_io=true vs 0 with false), confirming ReadOptions::async_io does not drive multiscan async and the removed local had no effect.
Reviewed By: anand1976
Differential Revision: D113613525
Pulled By: pdillinger
fbshipit-source-id: 38d73b607500a5c7ca2b89dd8f4187ef43940aa4
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15019
RocksDB's Checkpoint API currently copies all column families.
For use cases that only need a subset, this is wasteful.
This diff adds a new `CreateCheckpoint(dir, column_families, ...)` overload that:
- Validates input handles (rejects null/foreign handles, coalesces duplicates, always includes default CF).
- Filters GetLiveFilesStorageInfo to capture only the requested CFs' table/blob files.
- Post-processes the copied MANIFEST by appending kColumnFamilyDrop records for excluded CFs, so the checkpoint opens without referencing missing files.
- An empty column_families vector falls through to the existing whole-DB path.
Reviewed By: xingbowang, pdillinger
Differential Revision: D113963616
fbshipit-source-id: d1becbbb9528dd13c2dc314165880b76123757f0
Summary:
`make check` ran a series of extra checks serially, one after another, after the GNU-parallel unit tests: the Python syntax check, ldb/db_crashtest/ rocksdb_dump tests, and the format/buck/sources/workflow/c-api-gen source checks. These had accumulated into a non-trivial single-threaded tail on every `make check`.
Each extra check is now a make target collected in a single CHECK_EXTRA_TARGETS list, and gen_check_scripts emits a `t/run-<target>` wrapper per enabled check. check_0 already runs every `t/run-*` as a gnu_parallel job, so the checks now run alongside the unit tests, hiding their cost behind the (dominant) test phase. The slow ones (check-c-api-gen, check-format) are front-loaded via slow_test_regexp, and under CI sharding the checks are distributed round-robin so each runs on exactly one shard. The J=1 / no-gnu-parallel fallback runs them serially. New check-all-python and rocksdb_dump_test targets wrap the two steps that were previously inlined; the guards match the old serial tail exactly.
Each wrapper frames its result with a gtest-style
`[ RUN ]` / `[ OK ]` / `[ FAILED ] >>> make <check> <<<` line so a non-unit-test failure is easy to distinguish and re-run.
The wrappers spawn sub-makes concurrently, and each make invocation otherwise re-runs build_detect_platform, which rewrites the shared make_config.mk non-atomically -- concurrently that corrupts it ("missing separator"). The wrappers pass a new ROCKSDB_RECURSIVE_MAKE_SAME_CONFIG=1, which reuses the parent's already-generated make_config.mk (guarded on it existing, so normal builds still detect) and also silences the parse-time banners for clean output.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15028
Test Plan: `make check` runs the same set of auxiliary checks as before, now as parallel jobs. Verified that gen_check_scripts emits one wrapper per enabled check, that the SKIP_FORMAT_BUCK_CHECKS / ASSERT_STATUS_CHECKED / AIX guards still gate the set, that CI_TOTAL_SHARDS distributes the checks one-per-shard (union across shards is the full set), and that many concurrent wrappers no longer corrupt make_config.mk. A seeded check failure surfaces framed as `[ FAILED ] >>> make <check> <<<`.
Reviewed By: xingbowang
Differential Revision: D114245857
Pulled By: pdillinger
fbshipit-source-id: 519f78ccdcfbfdd88509980f78944e527a572331
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15027
`BackgroundPurgeCFDropTest` blocks `DBImpl::BGWorkPurge:start` to verify dropped-CF files remain until background purge is released. Runtime OPTIONS-file cleanup can also schedule a purge on the HIGH pool when `avoid_unnecessary_blocking_io` is enabled, so installing the sync-point dependency before test setup can park that OPTIONS purge before the foreground `Flush()` has a chance to run.
This does not need a broader product change. Runtime OPTIONS-file writes are rare, and the hard deadlock is a test artifact caused by the sync-point dependency. Outside the test, the observable impact would be limited to a foreground `Flush()` API call waiting longer if its flush is queued behind the OPTIONS-file purge.
Scope the sync-point dependency to the behavior under test by installing it only for the background-purge case, after the setup flush has produced the SST and just before `DropColumnFamily()`. This keeps setup-side OPTIONS-file purge work outside the blocked window while still verifying that dropped-CF file deletion waits for background purge.
Reviewed By: joshkang97
Differential Revision: D114241457
fbshipit-source-id: 21c5f4242a50590e161b3720077a9dfe5e77aa08
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15020
Each read executor worker creates its own `IoUringBackend`, and Folly's I/O thread pool creates its worker threads and EventBases lazily. A successful calling-thread probe or `DB::Open()` therefore does not validate every worker. A later read can start a new worker; previously, an `IoUringBackend` initialization exception on that worker could escape startup and terminate the process. The previous 512-submit setup also allowed Folly's capacity retry to reduce the completion queue to 512 while retaining a 1024-entry submission queue, which the kernel rejects with `EINVAL`.
Use a 256-submit limit so the 512-entry retry remains valid. Configure the shared read executor during `DB::Open()` through `DBOptions::read_io_executor_threads`, defaulting to one thread. The backend factory catches per-worker io_uring initialization failures and uses Folly's default backend. Each worker's thread-local availability marker starts false and is set true only after `IoUringBackend` construction succeeds. When construction fails, the marker remains false and `SubmitReadAsync()` performs the read synchronously. The executor's maximum thread capacity can be raised or lowered; `db_stress` requests eight threads for async DB APIs.
To expose this degradation, `FSRandomAccessFile::SubmitReadAsync()` now reports whether the asynchronous path was used. `RandomAccessFileReader` increments the `FILE_SUBMIT_ASYNC_READ_FALLBACK` ticker for every submission that falls back to synchronous I/O, making the fallback visible through the DB's `Statistics`.
Reviewed By: xingbowang
Differential Revision: D114031018
fbshipit-source-id: 1b7f6c1cea693e5dd743ea1ada0fca1cf757b3f9
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15021
The warm storage crash failure in T282626155 exposed a RocksDB bookkeeping bug, not a transient WS error. The regression was introduced by D91480994 / upstream RocksDB PR https://github.com/facebook/rocksdb/pull/14227 (“Support abort background compaction jobs”). That change routed `BlobFileBuilder` output tracking through `CompactionOutputs::output_file_paths_`, which contains both blob and SST paths, while blob completion still assumed `.back()` identified the current blob.
Failure timeline:
1. Compaction job 225 opens `000455.blob`, producing `[000455.blob]`.
2. It opens `000456.sst`, producing `[000455.blob, 000456.sst]`.
3. Blob 455 finishes, but `OnBlobFileCompleted()` receives `.back()`, which is `000456.sst`.
4. `SstFileManager` stats the still-open SST. WS correctly returns non-retryable `NOT_FOUND` because the output is not pathname-visible until close.
5. Compaction fails before installing its outputs in the MANIFEST. Cleanup then deletes `000455.blob` and `000456.sst`; those deletions are consequences of the failure, not its cause.
This change derives callback paths from the blob file number instead of the shared cleanup vector, renames the vector accordingly, and adds a deterministic regression test verifying that `SstFileManager` tracks every blob created during compaction.
Reviewed By: pdillinger
Differential Revision: D114002145
fbshipit-source-id: 472b563f3fb731753f286a40feece7428928bc20
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15004
This change reduces DB open latency on remote storage by avoiding foreground deletion of obsolete `OPTIONS-*` files during open when `avoid_unnecessary_blocking_io` is enabled. DB open now avoids foreground deletion of obsolete `OPTIONS-*` files on the pre-open `WriteOptionsFile()` path. `RenameTempFileToOptionsFile()` records the new OPTIONS file and chooses an explicit cleanup mode: skip while open is still in progress, schedule deletion when background work is available, or delete immediately when blocking IO is allowed or background jobs are rejected.
`DBImpl::Open()` makes the deferred cleanup decision once final open status is known. A successful open schedules obsolete OPTIONS files on the purge queue, while a failed open deletes them synchronously before tearing down the partially opened DB. The default synchronous behavior remains unchanged when `avoid_unnecessary_blocking_io` is disabled, and the latest-two OPTIONS retention policy is preserved.
Reviewed By: pdillinger
Differential Revision: D113593680
fbshipit-source-id: 8aee99b3f38a729647d0bcb644d212181fe86895
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15017
## Summary
RocksDB async `GetAsync` and `MultiGetAsync` callbacks now default to a completion-only path. Callbacks can override `EnableStats()` when they need request-specific `PerfContext` or `IOStatsContext`; otherwise RocksDB avoids creating a coroutine stats context and returns null stats pointers. Callbacks must not initiate another async read from `OnComplete()`.
RocksDB stats normally use thread-local storage, which does not compose with interleaved coroutine reads. A stats-enabled request captures only the caller's configuration: perf level, per-level perf enablement, and whether io stats are disabled. It then starts with empty request contexts. `onSet()` loads that request's contexts into TLS, while `onUnset()` saves the accumulated contexts back into request data. On resume, the same contexts are loaded again. This isolates request counters without preserving or restoring pre-existing executor TLS state. The synchronous fallback similarly resets TLS before executing a stats-enabled read.
`PerfContext` and `IOStatsContext` gain `Merge()` support for caller-side aggregation. `PerfContext` move construction and assignment now transfer ownership of per-level state rather than deep-copying it, avoiding allocations during coroutine context handoffs. A fbcode-wide audit found no caller that reads a moved-from `PerfContext`.
`db_bench` opts in only for coroutine jobs that print per-job perf context and merges every completed request's callback stats into the job aggregate.
## Example: Two Async Gets on One Thread
Both callbacks enable stats. Each request starts with empty counters and carries its contexts across suspensions.
```text
Shared executor thread
|
| onSet(A): load A's empty contexts into TLS
| A records 3 reads
| onUnset(A): save TLS contexts back into A
|
| onSet(B): load B's empty contexts into TLS
| B records 4 reads
| onUnset(B): save TLS contexts back into B
|
| onSet(A): load A's contexts containing 3 reads
| A records 2 more reads
| callback A receives block_read_count = 5
|
| onSet(B): load B's contexts containing 4 reads
| callback B receives block_read_count = 4
```
Isolation comes from replacing TLS with the active request's saved contexts on every `onSet()`.
### Timers
Existing wall-clock timers remain in coroutine frames, so timers spanning `co_await` include suspended time and measure end-to-end latency.
Request-level `get_cpu_nanos` is suspension-aware: it starts in `onSet()` and stops in `onUnset()` or `Finalize()`, charging only time while the request is active.
Nested CPU timers remain disabled because normal RAII CPU timers would also charge executor work performed while their coroutine is suspended. `MultiGetAsync` fanout additionally requires per-child timer state to avoid double counting.
## Benchmarks
Benchmarked to confirm no performance regressions when perf is disabled, and also to see the overhead of enabling perf.
```bash
db_bench --benchmarks=<readrandomcoroutine|multireadrandomcoroutine> --db=<db_path> --coro_jobs_per_thread=10 --use_existing_db=true --num=2000000 --value_size=1024 --compression_type=none --threads=8 --batch_size=16 --multiread_batched=true --use_direct_reads=true --cache_size=1428076626 --reads=320000 --perf_level=<1|3> --seed=<seed>
```
| Workload | Before change | Perf level 1 | Before -> 1 | Perf level 3 | 1 -> 3 |
| -------- | ------------- | ------------ | ----------- | ------------ | ------ |
| Async Get | 475.6K ops/s | 476.7K ops/s | +0.2% | 470.5K ops/s | -1.3% |
| Async MultiGet | 567.3K ops/s | 567.4K ops/s | +0.0% | 552.2K ops/s | -2.7% |
Reviewed By: xingbowang
Differential Revision: D112634874
fbshipit-source-id: 94e240aeb1496fa12dac995b5aa5a9c3543cc5fc
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14997
`CheckpointEngine`'s shared `CopyEngine` recorded copied bytes under the
`BACKUP_READ_BYTES` / `BACKUP_WRITE_BYTES` tickers (the `CopyEngineOptions`
defaults), conflating checkpoint I/O with backup I/O in statistics. This adds
dedicated `CHECKPOINT_READ_BYTES` / `CHECKPOINT_WRITE_BYTES` tickers and points
`CheckpointEngineImpl`'s `CopyEngineOptions` at them, so checkpoint copies report
under their own metric names. `BackupEngine` is unchanged (still uses the
backup-ticker defaults).
Addresses review feedback that the shared `CopyEngine` should not hardcode
backup metric names for non-backup callers.
The new tickers are intentionally not mirrored in the Java bindings
(`TickerType.java` / `portal.h`): the signed-`jbyte` id space there is exhausted,
so they remain C++-only, documented inline in `statistics.h`.
Reviewed By: xingbowang
Differential Revision: D113469742
fbshipit-source-id: 2a6a7ef04c939a1240f1c48c7bcaba8b2e7a1c10
Summary:
- Gate the `MultiScanPrepare` test's async I/O mode on the filesystem's reported `kAsyncIO` support.
- Use the same gated async-I/O flag for `MultiScanArgs::use_async_io` and read-count expectations.
- Match production DB-level MultiScan behavior so the test falls back consistently after io_uring initialization fails.
## Background
- Recent Sandcastle failures in T265255062 all hit the MultiScanPrepare read-count assertion after PosixFileSystem::SupportedOps() logged "CreateIOUring failed: Cannot allocate memory (errno=12)" and returned without kAsyncIO support.
- The test already probed SupportedOps(), but it only used that result when computing expected reads. It still copied the raw use_async_io test parameter into MultiScanArgs. That let the lower-level table-reader path call ReadAsync() and retry CreateIOUring() after the support probe had failed. If the retry succeeded, Prepare queued async reads and recorded zero sync reads while the assertion expected the sync fallback count.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15016
Test Plan:
- `make format-auto`
- `AUTO_CLEAN=1 make -j$(nproc) block_based_table_reader_test`
- `timeout 60s ./block_based_table_reader_test --gtest_filter="*MultiScanPrepare*"`
- `make check-sources`
Reviewed By: archang19
Differential Revision: D113830233
Pulled By: xingbowang
fbshipit-source-id: 38460f24fd44b33eb139a8f59bd87c637983e503
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15014
Implements coroutine-backed execution behind the callback-based `DB::GetAsync` and `DB::MultiGetAsync` APIs that issue SST block reads asynchronously, so a single application thread can keep many point-lookup reads in flight at once instead of paying one blocking read per thread. The motivation is point-lookup throughput on disk-bound workloads (working set larger than the block cache), where the win comes from queue depth rather than per-read latency.
The async capability rests on a small, generic FileSystem API that a `FileSystem` must implement to participate. Every method is virtual with a working synchronous default, so existing and custom `FileSystem` implementations compile and run unchanged (they simply stay synchronous):
- `FSRandomAccessFile::SubmitReadAsync(req, opts, cb, dbg)` -- a fire-and-forget read: it starts the read and returns without blocking, then invokes `cb` once `req.result`/`req.status` are filled in (the callback may run on a different thread). The default implementation is synchronous -- a blocking `Read` followed immediately by `cb`.
- `FileSystem::GetReadExecutor()` -- returns the `folly::Executor*` that async queries and read completions run on. The default returns `nullptr`, which the query layer treats as "async reads unavailable" and falls back to the synchronous path. It is exposed to users, so they may add additional application logic to run on the same thread pool, thus avoiding expensive context switches.
- `FileSystem::SetReadIOExecutorThreads(int)` -- sizes that executor. The default is a no-op.
`RandomAccessFileReader::Read`/`MultiRead` are `DEFINE_SYNC_AND_ASYNC` coroutines (new `file/random_access_file_reader_sync_and_async.h`), and `DBImpl::GetAsync`/`MultiGetAsync` bind the query to `GetReadExecutor()` with `co_withExecutor`. For a MultiRead, `SubmitMultiReadAsync` issues all of the block reads through `SubmitReadAsync` back-to-back and then `co_await`s a `folly::coro::Baton`: awaiting the baton suspends the query and releases the worker thread while the reads are in flight, and the callback for the last outstanding read posts the baton to resume it. A MultiGet's per-file `MultiGetFromSST` reads additionally fan out across the current eventbase so overlapping-level lookups run in parallel. This layer works against any executor and any `SubmitReadAsync` implementation.
The Posix `FileSystem` is the concrete async implementation, and is where io_uring enters. `GetReadExecutor()` is a `folly::IOThreadPoolExecutor` whose workers are EventBases that each drive an io_uring ring (sized by `SetReadIOExecutorThreads`). `PosixRandomAccessFile::SubmitReadAsync` -- which must be invoked on one of those EventBase workers -- places the read on that worker's submission queue via `IoUringBackend::queueRead` and returns immediately: no blocking `pread`, no parked thread. When the kernel posts the completion the backend runs the callback on that same worker, and a short read re-queues its remainder from inside the completion callback, so even multi-syscall reads stay on the non-blocking path. The net effect is that one worker thread keeps many reads outstanding at once, bounded by io_uring queue depth rather than thread count.
## Thread-local state handling
A coroutine suspends at every `co_await` and can resume on a different read-executor worker, and another coroutine can run on that worker in between. Any thread-local state held across a suspension point is therefore unsafe -- it can be observed or mutated by an interleaved coroutine, restored onto the wrong thread, or trip an in-use assertion. The read path handles each thread-local it touches explicitly, and introduces no new thread-local storage of its own:
- SuperVersion cache: the thread-local SuperVersion slot is marked `kSVInUse` for the duration of a read, so a second coroutine resuming on the same worker would trip that assertion. The coroutine path instead takes an independent heap reference that releases the slot before any suspension -- `cfd->GetReferencedSuperVersion()` for single Get, and `kExtraSvRef = true` threaded through `MultiCFSnapshot` for MultiGet -- and releases it with `CleanupSuperVersion` (via a `Defer` for single Get). The synchronous path is unchanged and keeps using the thread-local slot (`GetAndRefSuperVersion` / `ReturnAndCleanupSuperVersion`).
- perf_context / iostats_context / perf level: the timing guards that save-and-restore or accumulate into these thread-locals across an IO -- `GetPerfLevel`/`SetPerfLevel`, `IOSTATS_TIMER_GUARD(read_nanos)`, `IOSTATS_CPU_TIMER_GUARD(cpu_read_nanos)` -- are compiled out on the coroutine path (`#if defined(WITHOUT_COROUTINES)`), so none of that thread-local timing state spans a `co_await`. The tradeoff is that these perf/IO counters are not populated for coroutine reads (a known limitation of this path).
- TLS-free helpers are left as-is: `StopWatch`, for instance, holds only clock/statistics pointers and a start time, so it is safe across a suspension (it can over-count wall time under oversubscription, which is a metrics-accuracy issue, not corruption).
## Benchmarks
Benchmarked using 8 threads for both multiget (with/without async_io enabled), and single get on both direct-io enabled and disabled on a ~11 GB DB. Non direct-io benchmarks are not a good representation because my dev machine is capable of paging the entire DB into memory. But, it is still useful to compare against async_io=true, since that feature only benefits non direct-io.
Jobs-per-thread (jpt) is the multiplier for the number of tasks being submitted to the thread pool. E.g. a threadpool of 8 threads with jpt = 4 has 32 running tasks on the thread pool. It is important to oversubscribe so that when a task suspends, another one can resume on the thread pool.
MultiGet (batch 16):
| IO | cache hit % | sync | async_io | coro jpt=1 | coro jpt=2 | coro jpt=5 | coro jpt=10 |
| -------- | --- | ---- | -------- | ---------- | ---------- | ---------- | ----------- |
| direct | 10% | 65K | 64K | 145K | 151K | 161K | 166K |
| direct | 50% | 88K | 80K | 239K | 287K | 327K | 340K |
| direct | 90% | 435K | 399K | 732K | 991K | 1,093K | 1,119K |
| buffered | 10% | 449K | 561K | 555K | 593K | 572K | 566K |
| buffered | 50% | 495K | 682K | 656K | 724K | 700K | 701K |
| buffered | 90% | 600K | 878K | 828K | 1,021K | 1,048K | 1,060K |
single Get:
| IO | cache hit % | sync | coro jpt=1 | coro jpt=2 | coro jpt=5 | coro jpt=10 |
| -------- | --- | ---- | ---------- | ---------- | ---------- | ----------- |
| direct | 10% | 41K | 37K | 69K | 125K | 144K |
| direct | 50% | 74K | 66K | 127K | 238K | 281K |
| direct | 90% | 390K | 330K | 540K | 767K | 849K |
| buffered | 10% | 403K | 291K | 369K | 447K | 447K |
| buffered | 50% | 441K | 337K | 443K | 554K | 527K |
| buffered | 90% | 544K | 433K | 606K | 753K | 810K |
Example command (MultiGet coroutine, ~50% hit):
```
db_bench -benchmarks=multireadrandomcoroutine -coro_jobs_per_thread=10 -use_existing_db=true -num=2000000 -value_size=1024 -compression_type=none -threads=8 -batch_size=16 -use_direct_reads=true -cache_size=402653184 -reads=320000 -statistics
```
### Coroutine vs context switch overhead
Coroutines are not zero overhead, so we also compare it against the context switching of using many sync threads. Both variants were pinned to the same 8 CPUs and ran 80 concurrent MultiGet operations.
| variant | throughput |
| ---------------------------------------- | ------------ |
| sync, 80 threads | 330.6K ops/s |
| coroutine, 8 executor threads / 80 jobs | 446.0K ops/s |
Reviewed By: xingbowang
Differential Revision: D111116635
fbshipit-source-id: bd0ac052efe79261a552e9e66137e5e70f7cd8a1
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15013
Add `--use_async_db_api` to db_stress and route direct `DB::Get` and value `DB::MultiGet` reads through the callback async APIs (`DB::GetAsync` / `DB::MultiGetAsync`) when coroutine support is available. The db_stress helpers block on callback completion and notify waiters while holding the callback lock, so existing stress-test control flow and callback lifetime are preserved. Builds without coroutine support silently keep the existing synchronous path.
Centralize the dispatch in db_stress helpers and use them for workload, verification, secondary, and consistency/debug reads while leaving transaction and entity APIs on their existing paths.
Randomize `use_async_db_api` in `tools/db_crashtest.py` one run in six.
Reviewed By: jaykorean
Differential Revision: D111132477
fbshipit-source-id: 2aeac267867d8501131161c9fdbaf1da2b0c51d5
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15011
Root cause: `open_read_only_one_in` can create a read-only DB instance on the same live directory as the primary DB. When the stress run also enables `mmap_read=1`, that read-only instance can hold mmap-backed slices into SST files while the primary continues compacting and deleting obsolete files. If `SstFileManager` uses chunked truncation (`sst_file_manager_bytes_per_truncate > 0`), Linux can raise SIGBUS when the read-only path later touches a page whose backing SST was truncated or removed. The range tombstone fragmenter/comparator is where the invalid mmap-backed key slice was touched, not necessarily the root cause.
Evidence from T281873950 / Sandcastle job `63050395314610170`: the crashing command had `--mmap_read=1`, `--open_read_only_one_in=16`, and `--sst_file_manager_bytes_per_truncate=1048576`. The crash stack was `BlockBasedTable::ReadRangeDelBlock()` -> `FragmentedRangeTombstoneList::FragmentTombstones()` -> `std::set<ParsedInternalKey>::emplace()` -> comparator -> `memcmp`, ending in signal 7/SIGBUS. The crash DB LOG shows `allow_mmap_reads: 1`; table `104662.sst` was created cleanly at size `6257069` with range tombstones and was later deleted by the primary. The artifact does not show persistent SST corruption or checksum failure, and injected read faults would normally surface as RocksDB statuses rather than an OS SIGBUS.
Why this started surfacing: D111995911 / `rFBS0f7a2ef93e63cfa6677b6fa32c1bd34eb818c978` added randomized read-only-on-primary coverage via `open_read_only_one_in` on 2026-07-20. Before that change, blackbox crash test did not randomly keep a separate read-only DB open on the primary live directory, so the existing `mmap_read + SstFileManager truncation` hazard lacked the second reader needed to trigger SIGBUS. The failure is therefore a probabilistic exposure of the new stress mode when it combines with mmap reads and chunked SST truncation.
Fix: disable `open_read_only_one_in` in the existing `mmap_read=1` sanitizer path. This preserves read-only-on-primary stress coverage for non-mmap runs while avoiding an OS-level mmap/truncation hazard that RocksDB cannot convert into a normal `Status`.
Reviewed By: jaykorean
Differential Revision: D113774892
fbshipit-source-id: f82ec2924c12f4512aeeb7a913659dc9a72a4c2f
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15006
Block::GetCorruptionStatus() uses restart_offset_ in error state to decide whether it can re-decode the data block footer for a more specific corruption status: nonzero means the footer decode failed and restart_offset_ stores the original block size; zero means the corruption was detected after the footer decoded.
For separated-KV data blocks, Block::Block() first sets restart_offset_ to the restart-array offset, then validates footer.values_section_offset. If that offset is past the restart array, the block is marked corrupt with size = 0, but restart_offset_ was left nonzero. NewDataIterator() then called GetCorruptionStatus(), which treated the stale restart-array offset as an original block size, re-decoded a syntactically valid footer, and hit DEBUG_FAIL("ok status on presumed bad block contents").
Reset restart_offset_ to 0 on this semantic-corruption path so GetCorruptionStatus() reports generic block corruption instead of trying to re-decode a footer that already decoded successfully.
The regression test builds a separated-KV hash-index data block, corrupts only the encoded values_section_offset to point past the block, and verifies NewDataIterator() returns "Corruption: bad block contents". Removing the fix line makes the test reproduce the original assertion.
This update also applies the clang-format layout required by check-format for the new regression test.
Reviewed By: joshkang97
Differential Revision: D113561303
fbshipit-source-id: 8d989a38761063c62694837976aad98f1868f884
Summary:
Follow-up cleanup to the embedded (same-file) blob wide-column work (PR https://github.com/facebook/rocksdb/issues/14990): the "ReadOptions usage cleanup/optimization" item.
Split the Version-backed blob fetcher into two variants sharing a common VersionBlobFetcherBase (which implements FetchBlob via a virtual read_options()):
- VersionBlobFetcher now holds a `const ReadOptions&` for transient/stack fetchers (the common case: point Get/MultiGet, DBIter, version_set), so the growing ReadOptions is no longer copied per fetcher on the hot path. A deleted rvalue-ref constructor rejects binding to a temporary.
- OwningVersionBlobFetcher takes `ReadOptions&&` and owns a copy, for fetchers whose lifetime is independent of the caller (the read-path lazy resolver and compaction), making ownership transfer explicit.
DBIter::BlobReader now holds a single blob fetcher instead of decomposed scalar fields (read_tier/verify_checksums/fill_cache/io_activity). This removes the decompose-then-recompose that the old CreateBlobFetcher and RetrieveAndSetBlobValue performed, and closes the field-loss gap: all ReadOptions fields now flow through to blob reads rather than just those four. CreateBlobFetcher is gone; the merge path uses the reader's owned fetcher directly. A CanResolve() predicate on the fetcher preserves the prior "unexpected blob index" Corruption instead of dereferencing a null Version, and the now-redundant allow_blob_write_path_fallback_ member/ parameter (equal to the fetcher's own flag) was dropped.
ReadPathBlobResolver keeps owning its fetcher (now OwningVersionBlobFetcher) so it stays self-contained for the planned lazy entity read path, where the result outlives the read call. A TODO in db_iter.h records the remaining duplication (BlobReader and ReadPathBlobResolver each own a fetcher with the same {version, read_options, blob_file_cache, allow_write_path_fallback}), deferred to that work.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15003
Test Plan:
Pure refactoring with no intended behavior change; Version::GetBlob's Slice overload just decodes and calls the BlobIndex overload, so routing whole-value blob reads through the fetcher is equivalent, and the direct-write fallback flag/read options are unchanged. Covered by existing tests:
- db/blob/db_blob_index_test.cc: embedded/entity blob GetEntity/MultiGetEntity zero-copy, plain Get of an embedded default column, merge over an embedded-blob entity, read_tier == kBlockCacheTier Incomplete, corrupt record Corruption.
- db/blob/db_blob_basic_test.cc and db/wide/db_wide_blob_direct_write_test.cc: separate-file and direct-write blob resolution (the fallback branch).
- db/db_iterator_test.cc (blob) and db/compaction/compaction_iterator_test.cc: DBIter blob/merge paths and the compaction (owning) fetcher.
Reviewed By: xingbowang
Differential Revision: D113582247
Pulled By: pdillinger
fbshipit-source-id: 231c9364e266b826d12a8479cbb7f026a0d26bf6
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14998
T280925646 reported an ASAN double-free in db_stress' batched prefix scan. The visible free was in VerifyWideColumns(), but WideColumn values are non-owning Slice views, so that destructor should only free the vector element storage. The stack points to prior ownership corruption rather than an intentional verifier free.
The concrete bug is in PinnableSlice's move constructor: it delegates to move assignment without first initializing the target object. When moving from a pinned slice, move assignment does not set buf_ because the target is still pinned. If that moved-to slice is later Reset() and reused with PinSelf(), it writes through an uninitialized buf_ pointer.
This became reachable after D112212266 changed PinnableWideColumns to keep backing buffers in std::forward_list<PinnableSlice> and emplace moved PinnableSlice values into backing nodes.
Fix the move constructor by delegating to the default constructor first, so buf_ is initialized before move assignment. Add a regression that poisons placement-new storage, move-constructs from a pinned slice, resets it, and reuses it with PinSelf(); this crashed before the fix.
Reviewed By: pdillinger
Differential Revision: D113480016
fbshipit-source-id: 38ce9030f392ff9f0438d38d53d19e356e2d2fbf
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15001
Add IODispatcher-specific histograms for async read observed completion latency, `FileSystem::Poll` wait time, and submit-to-consumer-poll prefetch lead time. Record submit timestamps on async read state, report completion latency from the callback, and report poll wait/prefetch lead when the block consumer polls the handle.
Keep Java histogram byte mappings stable by assigning the new histogram IDs after the existing range. Add a controlled async filesystem test that verifies one deferred async read records all three histograms.
Address Infer feedback by iterating `async_state->block_indices` directly when removing async IO map entries. Keep `PollAndProcessAsyncIO()` holding an owning `shared_ptr<AsyncIOState>` while erasing those entries so the coalesced request state cannot be destroyed mid-loop.
Format `util/io_dispatcher_imp.cc` and `util/io_dispatcher_test.cc` with clang-format 21.1.2 to fix the format signal.
Reviewed By: pdillinger, anand1976
Differential Revision: D113549401
fbshipit-source-id: dafc272c9252e1baccf404f681d33f76890ce0b2
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14993
A corrupted RocksDB SST data block whose footer sets the hash-index bit
(`kDataBlockBinaryAndHash`) could drive an out-of-bounds heap read while the
block is being parsed. `DataBlockHashIndex::Initialize` guarded its
`num_buckets` / `map_offset` arithmetic only with `assert()`, which is compiled
out in release and ASAN builds. When the encoded `num_buckets` exceeds the
block, the `uint16_t` `map_offset` computation underflows; that value then
flows into `Block`'s ctor at `input.remove_suffix(input.size() - map_offset)`,
inflating `restart_offset_` far past the real allocation. If `size` is later
zeroed as an error marker (e.g. a corrupted separated-KV footer),
`Block::GetCorruptionStatus` builds a `Slice(data(), restart_offset_)` and
`DataBlockFooter::DecodeFrom` reads `data() + restart_offset_ - 4`, over-reading
the block's heap buffer (reported by agentic fuzzing, T279933950).
Fix:
- `DataBlockHashIndex::Initialize` now returns `bool` and rejects a
`num_buckets` of 0 or one whose bucket array plus the `NUM_BUCKETS` field does
not fit within `size`, instead of relying on debug-only asserts.
- `Block`'s ctor checks that return value and also rejects blocks too large for
the 64KiB `uint16_t`-offset hash-index format, marking the block corrupt
(`size = 0`) rather than under-flowing `map_offset` and over-reading.
Reviewed By: pdillinger
Differential Revision: D113429299
fbshipit-source-id: 3167106e793252c09fd6a558e3ac96de7a2169c0
Summary:
DB::Flush() with wait=true only waits until the flush result is committed (memtables removed from the immutable list). The EventListener::OnFlushCompleted callbacks run afterward, on the background flush thread, with the DB mutex released, and the waiter is only woken by a shared condition variable that many unrelated events also signal. As a result a Flush(wait=true) caller can return before -- or in the middle of -- its OnFlushCompleted callbacks. This surfaced as a flaky failure in the c_test table_filter_listener_metadata case, which asserts listener_state.flush_count == 3 immediately after Flush() returns (https://github.com/facebook/rocksdb/actions/runs/29976431449). It is also long-standing behavior that other tests have worked around with TEST_WaitForBackgroundWork().
This adds an opt-in FlushOptions::listener_wait (default false). When set together with wait=true, Flush() does not return until the OnFlushCompleted callbacks for the flushed memtables have finished. Default behavior is unchanged.
Implementation: MemTableList gains a per-column-family num_pending_flush_notifications_ counter (guarded by the DB mutex). The background flush thread reserves a pending notification at flush-commit time -- before the mutex is released to run the callbacks -- and releases it after they return, in both the single-CF (FlushMemTableToOutputFile) and atomic (AtomicFlushMemTablesToOutputFiles) paths. WaitForFlushMemTables treats a CF as finished only when its normal predicate holds and the counter is zero. Because the memtables are removed and the counter incremented under one continuous mutex hold (and, for atomic flush, all CFs are reserved before the per-CF notify loop starts releasing the mutex), a listener_wait waiter can never observe the flush as finished before the callbacks complete.
The counter is per-CF rather than per-flush, so a listener_wait waiter can wait somewhat longer than strictly necessary: under rapid flushing it may block until an unrelated, later flush's OnFlushCompleted on the same CF also drains (e.g. a waiter satisfied by GetEarliestMemTableID() advancing still waits for the whole CF's pending notifications). This can only over-wait, never return early, and is tolerated in favor of implementation simplicity; a per-memtable-id watermark could tighten it if it ever matters.
To keep the counter balanced against future edits, the release uses an RAII Defer (runs on every exit path) and ~MemTableList asserts the counter has drained to zero; SubPendingFlushNotifications asserts against underflow. A reservation is taken only when a callback will actually fire (committed flush info non-empty, not a mempurge, CF not dropped, listeners registered).
Also adds the C API rocksdb_flushoptions_set/get_listener_wait (regenerated via tools/c_api_gen) and a DBImpl::TEST_FlushMemTableWithListenerWait() helper.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14992
Test Plan:
New db_flush_test cases ListenerWaitWaitsForOnFlushCompleted and ListenerWaitAtomicFlushWaitsForAllOnFlushCompleted use a listener that blocks inside OnFlushCompleted plus spurious bg_cv_ wakeups to deterministically verify that Flush(wait=true, listener_wait=true) does not return until the callback (all CFs, for atomic flush) has finished; the atomic case guards the per-CF mutex-release gap in the notify loop.
Converted existing racy/worked-around cases to the new option: c_test table_filter_listener_metadata, listener_test OnSingleDBFlushTest and MultiDBMultiListeners, and db_flush_test FireOnFlushCompletedAfterCommittedResult (the last previously relied on TEST_WaitForBackgroundWork()). The auto-generated C API roundtrip test covers the new flushoptions field.
db_stress StressTest::TestFlush now sets listener_wait on a hard-coded 1 in 4 of its manual flushes (both the atomic and non-atomic branches), so the crash test exercises the new path with the DbStressListener registered.
Reviewed By: jaykorean
Differential Revision: D113425407
Pulled By: pdillinger
fbshipit-source-id: 6fd0f5bc93fabaccd8010c2fead1d93c4c44ccfb
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14948
Adds a reusable `CheckpointEngine` (opened once, works for any DB) that creates checkpoints via the shared `CopyEngine` pool, copying and hard-linking data files across `max_background_operations` threads. `CheckpointImpl::CreateCheckpoint` is refactored into a shared `CreateCheckpointImpl`, with the serial/parallel split behind a `CheckpointFileMover`; files the FileSystem cannot hard-link fall back to copies. The legacy `Checkpoint` API stays serial and unchanged.
Reviewed By: xingbowang
Differential Revision: D111387993
fbshipit-source-id: 036d375da9c9b8f051cfc3da0551b035e29d80c4
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14947
Teaches the shared `CopyEngine` to hard-link files through its background pool: adds `WorkItemType::Link` and `CopyEngine::LinkFile(src, dst, dst_env)`, which returns the FileSystem's `NotSupported` when links are unavailable so callers can fall back to copying. This is what CheckpointEngine (next diff) needs for warm storage, where linking is supported but benefits from parallelism.
No existing caller links through the engine yet (BackupEngine never hard-links), so no behavior change; the path is exercised by a new `copy_engine_test.cc` (parallel copy, parallel link, single-thread copy, per-item error reporting).
Reviewed By: xingbowang
Differential Revision: D111387972
fbshipit-source-id: ec0b0e9b80749a3f51e62066ceef2df21a1811d2
Summary:
Extracts BackupEngine's parallel file-copy/checksum thread pool into a standalone `CopyEngine` (`utilities/copy_engine/copy_engine.{h,cc}`) so `CheckpointEngine` (later in the stack) can reuse it. Behavior-preserving; all `TEST_SYNC_POINT` names are unchanged.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14946
Reviewed By: xingbowang
Differential Revision: D111387950
Pulled By: rban1
fbshipit-source-id: b5046adb98735a606fd1ba471731fd316ce6f036
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14952
`DBImpl::ResumeImpl()` constructed its local `ReadOptions`/`WriteOptions`
with defaults and carried a `// TODO: plumb Env::IOActivity, Env::IOPriority`
comment. Those options flow into `VersionSet::LogAndApply()` (the MANIFEST
write during error recovery), so the rate limiter could not attribute or
prioritize that I/O correctly.
This resolves the TODO for `ResumeImpl` (1 of 91 identical TODOs across the
codebase) by accepting an `Env::IOActivity` parameter and constructing the
options with it, following the pattern already established in `Get()`,
`MultiGet()`, and `NewIterator()`, and in the `kDBOpen` paths in
`db_impl_open.cc`.
Error recovery is driven by flush operations (as the existing comment in
`ErrorHandler::RecoverFromRetryableBGIOError` notes), so both production
callers and the `TEST_ResumeImpl` hook pass `Env::IOActivity::kFlush`. Setting
`io_activity` on these options is safe: `LogAndApply` does not assert
`io_activity == kUnknown`.
Reviewed By: pdillinger
Differential Revision: D112537885
fbshipit-source-id: 59045fd2f0a6e5bbacef6e0381e03ff95cd28c51
Summary:
This is a follow-up to D112212266 /
https://github.com/facebook/rocksdb/pull/14943 (multi-buffer PinnableWideColumns), which added the zero-copy read path for separate-blob-file references and explicitly left extending it to SST embedded-blob references as a follow-up.
Point GetEntity/MultiGetEntity of a wide-column entity whose columns are stored as embedded (same-file) blob records previously deserialized the entity, copied every column name/value, fetched each embedded blob (copying it), and re-serialized the whole entity before GetContext ever saw it (BlockBasedTable::MaybeResolveEmbeddedValue's wide-column branch). This extends the existing multi-buffer PinnableWideColumns zero-copy machinery (previously only for separate blob files / direct-write) to embedded blob columns.
Changes:
- New SameFileBlobReader interface (db/blob/same_file_blob_reader.h), a narrow seam for resolving a same-file BlobIndex against the current SST. BlockBasedTable implements it (GetSameFileBlob), delegating to the existing blob-cache-backed / pinned embedded reads.
- BlobFetcher is now an abstract interface; the Version-backed implementation moved to VersionBlobFetcher. Added an EmbeddedAwareBlobFetcher decorator that routes same-file references to a SameFileBlobReader and delegates all others to a base fetcher (EffectiveFetcher() selects the decorator only when a same-file reader is present).
- The point-Get and MultiGet loops now hand the raw entity to GetContext with the table as SameFileBlobReader, instead of pre-resolving/re-serializing. This is gated on not building a row-cache replay log, so a row-cache entry still captures a fully resolved entity (a later row-cache hit never reopens the SST to resolve same-file references).
- GetContext composes the decorator and resolves same-file and separate-file blob columns zero-copy across the columns / default-value / merge consumers.
- The iterator (DBIter) path is unchanged (still re-serializes via MaybeResolveEmbeddedValue).
- Also fixes latent method hiding in the test-only MockBlobFetcher.
Intended follow-ups:
- Lazy/deferred embedded resolution: return a LazyWideColumns result whose blob columns stay unresolved and are resolved on demand (whole or by byte range). The SameFileBlobReader seam and the polymorphic BlobFetcher introduced here are the mechanism for embedded support there.
- ReadOptions usage cleanup/optimization: have DBIter/BlobReader hold a single ReadOptions instead of decomposed scalar fields (removing the decompose-then-recompose in CreateBlobFetcher and closing the field-loss gap), and split VersionBlobFetcher into a ReadOptions-referencing variant for transient/stack fetchers plus an owning variant for lifetime-independent ones (the lazy resolver and compaction), to avoid copying the (growing) ReadOptions on the hot path.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14990
Test Plan:
Added embedded wide-column tests to db/blob/db_blob_index_test.cc:
- GetEntity/MultiGetEntity of an embedded-blob wide-column entity return the correct columns and resolve them zero-copy (blob columns served from the blob cache: identical data() across reads and in separate backing buffers; pointer-stable across a PinnableWideColumns move).
- Plain Get of an entity whose default column is an embedded blob.
- Merge base and GetMergeOperands over an embedded-blob entity.
- read_tier == kBlockCacheTier yields Incomplete for an unresolved same-file reference.
- A corrupt embedded record surfaces Corruption, not a raw BlobIndex.
# Performance validation with db_bench
Measured with db_bench benchmarks added in https://github.com/facebook/rocksdb/issues/14995 / D113455538, temporarily stacked
on this diff for A/B testing.
## Setup
- **Baseline**: this diff's parent + db_bench benchmark commit.
- **Treatment**: this diff, incl. its test/lint fixups + the same
db_bench benchmark commit.
- Both are release builds (`DEBUG_LEVEL=0 make db_bench`). The diff touches only
the read path, so both binaries produce byte-identical SSTs; the db_bench commit
is additive (`tools/db_bench_tool.cc` only) and identical in both.
- Host: AMD EPYC-Genoa, 166 vCPU, 224 GiB RAM. Single reader thread.
## Method
- Datasets are created **once** and read by **both** binaries via
`--use_existing_db`, so the A/B compares identical on-disk data.
- Each read runs twice in-process (warmup + measured); the blob cache (1 GiB) and
block cache (512 MiB) hold the whole ~400 MiB dataset, so the reported (2nd) run
is fully warm — isolating the column-resolution cost the diff changes rather than
I/O.
- 100k keys, `value_size=1024`, `embedded_blob_min_size=256`,
`compression_type=none`, `format_version=7`, `--duration=10` per measured run.
- Datasets:
- **A** — wide-column entity, `num_short_wide_columns=1` + `num_wide_columns=4`
(small inline default column + 4 embedded-blob columns). Read via
`readrandomentity` (GetEntity) and `multireadentity` (MultiGetEntity, batch 32).
- **B** — wide-column entity, `num_short_wide_columns=0` + `num_wide_columns=4`
(the default column itself is an embedded blob). Read via plain `readrandom`
(Get returns the resolved default column).
- **C** — whole-value embedded blobs (`fillembeddedblob`). Read via plain
`readrandom`. This path is **not** changed by the diff — a regression check.
Example commands:
```
# create (once)
db_bench --benchmarks=fillembeddedentity --db=/tmp/perfA --use_existing_db=0 \
--num=100000 --value_size=1024 --embedded_blob_min_size=256 --format_version=7 \
--num_wide_columns=4 --num_short_wide_columns=1 \
--use_blob_cache=true --blob_cache_size=1073741824 --cache_size=536870912 \
--compression_type=none
# read (each binary; warmup + measured)
db_bench --benchmarks=readrandomentity,readrandomentity --db=/tmp/perfA \
--use_existing_db=1 --threads=1 --duration=10 <same options>
```
## Results (warm, 2 runs each)
| Benchmark (read path) | Baseline µs/op | Treatment µs/op | Latency | Baseline Kops/s | Treatment Kops/s | Throughput |
|---|---|---|---|---|---|---|
| `readrandomentity` — GetEntity, inline default + 4 embedded cols | 7.16 | 5.00 | **-30%** | 140 | 200 | **1.43×** |
| `multireadentity` — MultiGetEntity (batch 32) | 6.46 | 4.54 | **-30%** | 155 | 220 | **1.42×** |
| `readrandom` — Get of embedded default column (dataset B) | 6.55 | 2.12 | **-68%** | 153 | 471 | **3.09×** |
| `readrandom` — whole-value embedded blob (dataset C, unchanged path) | 1.92 | 1.91 | ~0% | 521 | 524 | neutral |
Both runs were within ~1–3% of each other (raw numbers below).
## Interpretation
- **GetEntity / MultiGetEntity of embedded-blob wide-column entities improve ~30%
in latency (~1.4× throughput)** — the expected win from resolving embedded blob
columns zero-copy into the `PinnableWideColumns` backing buffers instead of the
old deserialize + per-column copy + re-serialize round trip.
- **Plain `Get` of an entity whose default column is an embedded blob improves
~3×** — the largest win, because the old path re-serialized the entire entity
even when only the default column is returned, whereas the new path resolves just
that column.
- **The whole-value embedded blob `Get` path is unchanged (~0% delta), confirming
no regression** on the pre-existing zero-copy path.
No unexpected regressions observed; all deltas match the diff's intended scope.
## Raw measured numbers (both runs)
```
readrandomentity BASELINE 7.183 / 7.137 µs/op 139225 / 140118 ops/s
readrandomentity TREATMENT 4.992 / 5.008 µs/op 200333 / 199681 ops/s
multireadentity BASELINE 6.458 / 6.458 µs/op 154837 / 154843 ops/s
multireadentity TREATMENT 4.509 / 4.577 µs/op 221798 / 218463 ops/s
readrandom (Bdef) BASELINE 6.497 / 6.609 µs/op 153920 / 151307 ops/s
readrandom (Bdef) TREATMENT 2.126 / 2.120 µs/op 470317 / 471805 ops/s
readrandom (Cblob) BASELINE 1.891 / 1.947 µs/op 528760 / 513554 ops/s
readrandom (Cblob) TREATMENT 1.895 / 1.920 µs/op 527767 / 520807 ops/s
```
Reviewed By: xingbowang
Differential Revision: D113347622
Pulled By: pdillinger
fbshipit-source-id: c51bc187085695d559411d9c0555bb52c8e0a61a
Summary:
db_bench had no way to exercise SST embedded (same-file) blob reads or wide-column entity reads (GetEntity/MultiGetEntity), which are needed to measure the zero-copy embedded-blob wide-column entity read path.
This adds:
- Two setup benchmarks, fillembeddedentity and fillembeddedblob, that build an SST via SstFileWriter::OpenWithEmbeddedBlobs and ingest it (embedded blobs can only be produced this way, not by fillrandom/flush/compaction). Entity mode writes wide-column entities whose large columns are embedded blobs; blob mode writes whole-value embedded blobs. Both require format_version >= 7.
- readrandomentity (GetEntity) and multireadentity (MultiGetEntity) read benchmarks; plain readrandom is reused for the whole-value embedded blob baseline and for entity default-column Get.
- Flags embedded_blob_min_size, num_wide_columns, and num_short_wide_columns to control the embedded threshold and the inline/embedded column mix per entity.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14995
Test Plan: Manual db_bench runs of the new benchmarks, used to validate D113347622.
Reviewed By: xingbowang
Differential Revision: D113455538
Pulled By: pdillinger
fbshipit-source-id: c6b62f5a2d6a61ae0b7cc32ac1b445b566ea8842
Summary:
`USE_CLANG=1 make check-c-api-gen` failed with "clang++ not found" because the staleness check and the Python generator detected clang inconsistently: the Makefile inspected the `$(CXX)` make variable (the fbcode clang path) while the generator read the `CXX` environment variable, which make never exports. More broadly, clang++ was detected in several ad hoc places with fragile heuristics (grepping the compiler path, duplicated fallback lists in shell and Python).
Centralize clang++ detection in build_detect_platform as a single `CLANG_CXX`:
- `CLANG_CXX` honors `$CXX` when it is itself clang (so CI's `CXX=clang++-21` is used), else takes the first clang++ on PATH (bare, versioned, or the Meta-internal clang++.par). `CXX` (when building with clang) and `CLANG_ANALYZER` now derive from `CLANG_CXX` instead of re-detecting.
- Infer `USE_CLANG=1` when the compiler is clang, detected via the compiler's own `__clang__` predefined macro rather than matching the binary path (which false-positives, e.g. a home dir containing "clang"), and emit it so clang-specific Makefile logic is consistent.
- Emit `CLANG_CXX` to make_config.mk and pass it explicitly to the codegen, so `make check-c-api-gen` works without requiring `USE_CLANG=1` and the generator never guesses.
Add a first-class `make regen-c-api` target -- now the preferred regenerate command referenced by the generated banners and user-facing messages. The Python generators no longer detect clang themselves; `--clang` defaults to `clang++` for manual runs and is overridden by the Makefile with `CLANG_CXX`. Regeneration verifies the clang runs before writing any file, so a missing/broken toolchain aborts cleanly instead of leaving a partially regenerated tree.
Make field discovery clang-version-independent: older clang (e.g. 15) spells field types fully qualified (`rocksdb::ReadTier`,
`rocksdb::BlockBasedTableOptions::IndexType`) while the fbcode/CI clang (21) uses the bare `ReadTier` / `IndexType` the family tables expect. Strip the ROCKSDB namespace and enclosing-struct qualifiers so both agree; this is a no-op on newer clang, so checked-in output and CI are unaffected.
Also escape the literal "generated" in the generator .py sources (they emit the marker but are not themselves generated) so tooling stops treating them as read-only generated files.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14994
Test Plan:
Manual verification (not covered by CI):
- `make check-c-api-gen` now runs the staleness sub-check (instead of skipping) and passes in a default gcc build as well as with `USE_CLANG=1` on an fbcode host, since `CLANG_CXX` is always populated.
- `ROCKSDB_NO_FBCODE=1 make regen-c-api` using a system clang 15 (clang++.par) produces output byte-identical to the checked-in files (generated with clang 21), and `check-c-api-gen` reports "up to date" -- confirming the namespace/struct stripping is version-robust and a no-op on clang 21.
- `regen_all.py --clang /nonexistent` exits non-zero with a clear message and leaves the generated files untouched.
- `make regen-c-api` is idempotent (no diff on a clean tree).
- Confirmed the escaped `\x40generated` still emits `// generated` byte-for-byte in the regenerated c.h/c.cc/.inc.
- Reviewed .github/workflows/pr-jobs.yml: the build-linux-clang-21-no_test_run job's `CXX=clang++-21 make check-c-api-gen` resolves `CLANG_CXX` to clang++-21 (honoring `$CXX`) rather than the container's stray bare clang++.
Reviewed By: xingbowang
Differential Revision: D113447462
Pulled By: pdillinger
fbshipit-source-id: 91ca92d21a71a8b92f9ff57500a8cec91a884078
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14945
First diff in a stack that lets `Checkpoint` and `BackupEngine` share one parallel copy/link infrastructure. Pure options refactor, no behavior change.
Adds two shared option bases in `rocksdb/utilities/checkpoint.h`: `CheckpointOrBackupEngineOptions` (engine-level config) and `CreateCheckpointOrBackupOptions` (per-operation options). `CheckpointEngineOptions` / `CreateCheckpointOptions` derive from them for checkpoint; `BackupEngineOptions` / `CreateBackupOptions` derive from them for backup and keep their backup-only fields.
Reviewed By: xingbowang
Differential Revision: D111387916
fbshipit-source-id: d7efa528ac91fb00090130c9d2a671b13e934a82
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14991
D91705195 ("Replace Prefetch Logic in BlockBasedTableIterator with IODispatcher") moved MultiScan block prefetching out of BlockBasedTableIterator and into the IODispatcher. The IO-level RecordTick/RecordInHistogram calls lived in the deleted prefetch code, and the IODispatcher was not given equivalent instrumentation, so these tickers were still defined but never populated (they published as absent):
- MULTISCAN_BLOCKS_PREFETCHED
- MULTISCAN_BLOCKS_FROM_CACHE
- MULTISCAN_PREFETCH_BYTES
- MULTISCAN_IO_REQUESTS
- MULTISCAN_IO_COALESCED_NONADJACENT
- MULTISCAN_BLOCKS_PER_PREPARE (histogram)
The PREPARE_*, SEEK_ERRORS, and PREFETCH_BLOCKS_WASTED tickers were unaffected (recorded on the prepare/teardown path, which was not replaced).
Fix:
- ReadSet now exposes generic, caller-agnostic prefetch counters (GetNumBlocksPrefetched, GetNumIORequests, GetPrefetchBytes, GetNumCoalescedNonadjacent), incremented once per block in DispatchPrefetch -- the single choke point for both immediate and memory-deferred dispatch. Counting at dispatch time (not completion) keeps async and deferred prefetches accounted for without double counting the async->sync fallback.
- PrepareIORequests now outputs prefetch_bytes (sum of request len) and num_coalesced_nonadjacent (within-threshold gap count) while building coalesced requests, per review feedback to avoid the extra pass over read_reqs/coalesced_block_indices at former line 836. DispatchPrefetch now gets stats directly from PrepareIORequests.
- The MULTISCAN_* ticker names stay at the caller: MultiScanIndexIterator's destructor records the aggregate counters at end-of-scan (alongside the existing PREFETCH_BLOCKS_WASTED), so async/deferred reads that complete during iteration are included. MULTISCAN_BLOCKS_PER_PREPARE is recorded in BlockBasedTableIterator::Prepare. This keeps the IODispatcher a generic mechanism rather than leaking a caller's metric names into it.
- Also plumb the same metrics into PerfContext for per-operation visibility.
Reviewed By: xingbowang
Differential Revision: D113356436
fbshipit-source-id: 660e9755d6b1b9aecccd112df94eb6838061b298
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14985
The Open Source Automated Checkup (T280493780) flagged the 27 generated
`c_api_gen/c_generated_*.inc` fragments for missing the required copyright
header ("Meta Platforms, Inc. and affiliates"). The fragments are emitted by
the C API code generators, none of which wrote a header, so every
regeneration reproduced the violation.
Root-cause fix in the generators so regenerated output is compliant:
- `tools/c_api_gen/generate_c_api.py` (`HEADER_BANNER`)
- `tools/c_api_gen/auto_simple_bindings.py` (`auto_header_banner`)
- `tools/c_api_gen/gen_roundtrip_tests.py`
Each now emits the standard Meta dual-license block (matching `c_base.h` /
`include/rocksdb/c.h`) ahead of the `// generated` marker, and the 27
checked-in `.inc` fragments are regenerated accordingly.
The assembled `include/rocksdb/c.h` and `db/c.cc` already carry a top-level
header, so `regen_all.py`'s inliner now strips each fragment's license block
when inlining to avoid repeating it once per fragment; those two files are
therefore unchanged.
Also adds the header to `tools/gtest_parallel_repro.py`, the one non-`.inc`
source file flagged by the checkup.
Reviewed By: pdillinger
Differential Revision: D113242908
fbshipit-source-id: 4d81d1355c8d8a35cbc4377dd9b21df901ad3cb7
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14934
First diff in the async read stack: pure plumbing, no behavior change. Both the point `Get` path and the batched `MultiGet` path gain synchronous/coroutine twins generated by `DEFINE_SYNC_AND_ASYNC`; `DB::GetAsync` and `DB::MultiGetAsync` become the public callback-based async entry points with synchronous default implementations. For `Get`, the existing `DBImpl::Get` / `GetImpl` bodies are relocated into the sync/async wrapper. For `MultiGet`, the same wiring continues through the DBImpl, version, and table-cache layers. The generated coroutine variants still run the existing synchronous logic.
This keeps the refactor mechanical while preserving dispatch semantics: the coroutine side remains virtual at the same `GetImpl` boundary as the synchronous path, read-only and secondary DBs use generated sync/coroutine overrides instead of falling back to base `DBImpl`, and `StackableDB` leaves async `Get` / `MultiGet` to the base wrappers so wrapper-specific `Get` / `MultiGet` behavior is preserved.
For ABI compatibility and avoiding MACRO blocks in public headers, the asynchronous APIs are callback based instead of coroutine based. Internally it will be developed using coroutines, and applications can also develop their own coroutine wrappers.
This is meant to make the later changes to actually enable coroutine-backed async queries much easier to review.
Benchmarks:
Compared `db_bench` with and without this diff using:
`db_bench --benchmarks=fillseq,readrandom --num=1000000 --reads=5000000 --key_size=16 --value_size=100 --seed=12345 --db=<tmp>`
| Benchmark | Without diff avg | With diff avg | Delta |
| `fillseq` | 4.076 us/op | 4.165 us/op | +2.2% |
| `readrandom` | 3.939 us/op | 3.947 us/op | +0.2% |
No meaningful local `db_bench` regression was observed.
Reviewed By: xingbowang
Differential Revision: D109346179
fbshipit-source-id: 0dee1239f49099b67c11c63426204466e963f903
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14987
The `fast_sst_open` optimization speeds up DB open times by persisting file metadata information in the MANIFEST.
When MANIFESTs are rotated however, this metadata is currently getting dropped, since it is not fed through `VersionEdit::AddFile`. There were also a few additional missing `AddFile` call sites.
Fix: Add an optional `file_open_metadata` to `AddFile`. Feed this through wherever we call `AddFile`.
Reviewed By: anand1976
Differential Revision: D113265655
fbshipit-source-id: c8371d66f6555bb9c372eea095bfa30a5f56a88a
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14986
Why: We want to measure the DB open time during offloaded compactions.
What: Add a histogram for the DB open time in `DB::OpenAndCompact()`.
Reviewed By: anand1976
Differential Revision: D113136652
fbshipit-source-id: f8b2d74bcda3506f7b9ac80fa3e50a172deafc5e
Summary:
An audit of the wide-column blob-reference read paths (from the preceding "multiple backing buffers" change, D112212266) surfaced several inconsistencies in how Status is used: control-flow-via-Status, a status classification that diverged from the rest of the codebase, the same "no blob fetcher" Corruption repeated at many call sites, a swallowed non-OK status (bug), and read statistics advanced on failed reads. This change cleans those up. It is internal only -- no public API or on-disk format change -- and stacks on that diff.
Concretely, this resolves two of the intended follow-ups called out in D112212266:
* "Resolve FIXME about pre-existing swallowed non-OK status in db_impl.cc"
* the error-status/API cleanup (classify an unrecognized wide-column version as Corruption, not NotSupported, at all entry points; replace the NotSupported sentinel from GetValueOfDefaultColumn with an is_blob_reference out-param, removing GetValueOfDefaultColumnResolvingBlobs; and centralize the "blob-backed value without a blob fetcher" Corruption into the resolvers, resolving the ResolveEntityForMerge FIXME, rather than repeating it at each caller). The other two follow-ups from that diff -- the value_size_soft_limit TODO in db_impl.cc and extending the zero-copy optimization to SST embedded-blob references -- are deferred to separate diffs.
Unrecognized version -> Corruption. A wide-column version newer than kVersion2 now returns Corruption instead of NotSupported, consistent with how the rest of the codebase classifies an unrecognized serialized version/type (BlobIndex, the blob log header, the table footer). The guard is applied at every entry point (Deserialize, GetValueOfDefaultColumn, HasBlobColumns, ForEachBlobFileNumber); the latter two previously lacked it and would misparse a future/corrupt version.
Reject trailing data after an empty entity. Deserialize() previously returned OK early for a zero-column entity, silently ignoring any trailing bytes; it now dispatches zero-column entities to the V1/V2 parser like the non-empty case, so the "exactly one serialized entity" trailing-data check the preceding diff added for non-empty entities now covers the empty case too.
Default-column extraction no longer signals via Status. now returns (value, is_blob_reference): for a blob-referenced default column it returns OK with the raw serialized BlobIndex bytes and the flag set, instead of a NotSupported sentinel that every caller had to detect and pair with a second, redundant full deserialization. GetValueOfDefaultColumnResolvingBlobs and the memtable GetDefaultColumnBlobIndexSlice helper are removed; a single ResolveDefaultColumnBlobReference() resolves the raw BlobIndex (handling an inlined index with no fetch). This also fixes a case where a non-default column 0 that happened to be a blob reference was misreported as a blob-backed default. The one remaining NotSupported is the honest, explicit base-DB case: reading a blob-index default column when is_blob_index was not requested.
Null-blob-fetcher handling centralized. "A blob-backed value with no fetcher to read it" (reachable via a reader without blob support such as SstFileReader, or corrupt/legacy data) was checked -- and reported as Corruption -- at ~6 call sites with three slightly different messages, forced by the resolvers asserting the fetcher was non-null. The asserts are gone; each resolver now owns the check in one commented place: ResolveEntityBlobColumnsMultiBuffer (whole entity), ResolveDefaultColumnBlobReference (default column), and the MaybeResolveMemtableBlobValue plain-value path (which open-codes its fetch). Callers just propagate the status. A side benefit: an entity/column whose only blob references are inlined now resolves without a fetcher instead of being wrongly rejected.
Return Status from the memtable resolve helpers, fixing a swallowed error. MaybeResolveMemtableBlobValue (was bool + Status*) and PostprocessMemtableValue- Read (was void + Status*) now return Status, matching codebase convention; MaybeResolveMemtableBlobValue reports "there was a reference to resolve" via a bool* did_resolve out-param (true means it has finalized value/columns). This resolves a pre-existing bug: on the blob-resolve path a non-OK status was returned early without the output cleanup the plain path performed, leaving a half-populated result behind an error. PostprocessMemtableValueRead now has a single success/error finalization block reached by every path (self-pin on success, clear outputs on any error), and returns the finalized status. Also switched the memtable "no blob fetcher" case from NotSupported to Corruption (an internal helper whose sole caller treats all non-OK statuses identically).
Read-only/secondary read stats mirror the primary path on error. DBImpl::GetImpl gates byte/throughput accounting (BYTES_READ, BYTES_PER_READ, get_read_bytes) and merge-operand output on s.ok(); the read-only and secondary Get paths did so unconditionally, logging spurious samples (e.g. a 0-byte BYTES_PER_READ sample) on NotFound/error. They now gate on s.ok() too, while keeping NUMBER_KEYS_READ, MEMTABLE_HIT, and super-version cleanup unconditional.
Header docs updated to match (Deserialize/DeserializeSimple return Corruption for an unexpected blob reference; unknown per-column value types are rejected as Corruption).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14980
Test Plan:
Extended db/wide/wide_column_serialization_test.cc: DeserializeUnsupportedVersion now expects Corruption; new FutureVersionRejectedConsistently verifies that Deserialize, GetValueOfDefaultColumn, HasBlobColumns, and ForEachBlobFileNumber all reject a future version as Corruption; V2GetValueOfDefaultColumnBlobRef now checks the is_blob_reference flag and raw-BlobIndex bytes and that resolving without a fetcher is Corruption; DeserializeRejectsTrailingData now also covers empty (zero-column) V1 and V2 entities.
db/blob/db_blob_index_test.cc: MaybeResolveMemtableBlobValueWithoutFetcherFails- Closed updated to the new signature (asserts did_resolve + Corruption); new PostprocessMemtableValueReadClearsOutputsOnError pins the swallowed-status fix, covering both an already-non-OK incoming status and (the actual FIXME scenario) an OK incoming status whose blob-resolve path fails -- both clear outputs and return the error. The StackableDB-style DBBlobIndexTest.Get contract is unchanged (its NotSupported comes from the base-DB blob-index read path, not these helpers).
Reviewed By: xingbowang
Differential Revision: D113099114
Pulled By: pdillinger
fbshipit-source-id: 4bc374dd74f8ba83bdf49bd8753c1a8d88529cde
Summary:
Resolves the value_size_soft_limit TODO left in db_impl.cc by the PinnableWideColumns multi-buffer change (D112212266 / PR https://github.com/facebook/rocksdb/issues/14943).
ReadOptions::value_size_soft_limit is meant to bound how much a MultiGet buffers, but it was enforced in the batch loop on pre-resolution sizes. Pre-flush blob direct write references (a plain value encoded as a BlobIndex, or a wide-column entity with unresolved blob-valued columns) are only resolved later, in MultiGetImpl's post-processing loop, so their (possibly large) blob values were fetched with no limit applied -- read unbounded. Separate-file blob columns do not have this gap because they are resolved eagerly in GetContext and thus already counted.
Now the post-processing loop enforces the limit on direct-write resolution using the "always make progress" soft-limit semantics: a key is aborted only once the bytes already returned by this MultiGet exceed the limit, so at least one key is always read even if its value alone exceeds the limit. Once the returned total is over the limit, subsequent keys that need fetching get Status::Aborted. This bounds the reads while ensuring a caller retrying the aborted keys cannot loop forever on a single oversized value.
Pieces that benefited from human input and refinement:
* Enforcement semantics: settled on "always make progress" -- checking the bytes already returned (rather than a projected pre-fetch size, which in foreseeable user integrations could starve a lone key that by itself exceeds the limit) -- matching the existing separate-file/batch soft-limit behavior.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14983
Test Plan:
Added to db/wide/db_wide_blob_direct_write_test.cc (memtable / pre-flush direct-write reads, using the ResolveDirectWriteWideColumns:BlobFetched sync point to confirm which blobs are fetched):
* MultiGetEntitySoftLimitBoundsDirectWriteBlobResolution -- two over-limit entities: first read (progress), second aborted; one blob fetch.
* MultiGetEntitySoftLimitAlwaysReadsOversizedKey -- a single entity whose value alone far exceeds the limit is returned OK (no starvation).
* MultiGetSoftLimitBoundsDirectWritePlainValue -- same for plain values stored as direct-write blob references: first read, second aborted.
Reviewed By: xingbowang
Differential Revision: D113159249
Pulled By: pdillinger
fbshipit-source-id: 5af443b86bdfc3ecd0112d3c5af541a77f0d3d67
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14979
`MaybeOpenReadOnlyOnPrimary` (added in D111995911) opens a read-only DB on the primary's live directory while the primary keeps mutating it. Two separate stress-test false positives were observed there; this diff fixes both and consolidates the earlier one-off fixes into a single diff.
1) TSAN deadlock-detector crash in the CF-name snapshot (`fbcode_tsan_blackbox_crash_test`):
ThreadSanitizer: CHECK failed: sanitizer_deadlock_detector.h:67
"((n_all_locks_)) < (...)" (0x80, 0x80)
`n_all_locks_` is the number of mutexes a thread holds *simultaneously*; TSAN caps it at 128 (0x80). The snapshot loop called `LockColumnFamily(cf)` to read `column_family_names_[cf]`, and that helper `.Lock()`s ALL per-key mutexes for the CF at once (`ceil(max_key / 2^log2_keys_per_lock)` -- e.g. 25,000 in blackbox with max_key=100000, log2_keys_per_lock=2) without releasing any, so a single call blows past 128. The writer (`MaybeClearOneColumnFamily`) holds all key locks while renaming a CF, so a single key lock is enough to synchronize the read. Fix: take one `MutexLock` on key-lock index 0 per CF instead of `LockColumnFamily`, keeping the simultaneously-held count at 1.
(`LockColumnFamily` is a latent hazard for any caller; the pre-existing caller `MaybeClearOneColumnFamily` never runs in the crash-test config -- `clear_column_family_one_in=0` -- so only this new, ungated caller exposed it.)
2) False-positive read-only Get (folded in from D112987468). After flushing the primary, the test performed a `Get(Key(0))` on each CF of the read-only instance. That Get races against concurrent primary compaction, which can delete SST files the read-only's frozen MANIFEST still references, surfacing as IOError/PathNotFound/Corruption ("truncated block read"). The Get adds no unique coverage -- the scenario under test (the read-only close wrongly deleting primary live files) is detected on the primary's own verification paths -- so remove the Get block entirely instead of widening a tolerated-status list.
Note on D112936376: that diff tried to fix crash (1) by setting `table_cache_numshardbits=0` on the read-only instance, on the theory that the primary's and reader's table caches contribute "128 unique lock objects." That is a misdiagnosis: the failing CHECK counts locks held *simultaneously*, and sharded-cache shard mutexes are held transiently (one at a time; read-path nesting is a constant depth of 2), so shard count does not affect `n_all_locks_`. The real cause is `LockColumnFamily`, fixed in (1), so `table_cache_numshardbits=0` is unnecessary and is not included here.
This diff subsumes and replaces D112936376 and D112987468.
Reviewed By: joshkang97
Differential Revision: D112980148
fbshipit-source-id: 012ff852109223f3d19d8dbbc065fde9340f9c66
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14971
Follow-up to https://github.com/facebook/rocksdb/pull/14942. That PR made db_bench read ops end the run with an INVALID result (`SetFatal` -> exit 1) on a tolerable remote IO error and left write ops on unconditional `SetFatal`. In practice that still failed the remote benchmark TW jobs (exit 1) on transient WS infra errors, and did not cover write ops or `DB::Open`.
Change the toleration to skip-and-continue so a transient remote IO error no longer fails the run:
- `HandleBenchmarkIOError` now just logs and returns on a tolerable error (was: `SetFatal`); the caller skips the failed op and keeps going. Non-tolerable, data-loss, and local-DB errors still `abort()` (core dump for debugging).
- Applied uniformly to all worker op error paths: `Get` / `MultiGet`, put / merge, delete / delete-range, `DoDelete`, `RandomWithVerify` put/delete-many, and the `BGScan` iterator (stops the scan and continues).
- `DB::Open`: on a tolerable remote IO error, exit cleanly (0) -- the benchmark cannot run, but it is not a db_bench / RocksDB failure.
Gated by `--tolerate_io_errors_for_remote_dbs` plus a remote `--env_uri` / `--fs_uri` backend, so local runs and non-IO / data-loss errors are unaffected. The result is best-effort (skipped ops are not counted); the goal is to keep remote benchmark jobs green through transient WS infrastructure errors instead of failing or crashing.
db_stress is intentionally NOT changed: it is a correctness (crash-recovery) test and must keep failing on real errors.
Differential Revision: D112749712
fbshipit-source-id: a8ccd46ef66e6c3c97a1baffd35d27830879d428
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14940
A few improvements to the CPU corruption injector:
- Seed handling: `--seed` is now validated against db_stress's accepted range and no longer silently wraps, so an out-of-range seed fails fast instead of producing misleading NO_INJECTION runs. Omit `--seed` for a fresh random base seed.
- Per-run reproducibility: each run's full db_stress command is recorded at the top of its `gdb.log`, so any run can be reproduced by copy-pasting one line.
- README: clarified the `--seed` semantics and examples.
**Test plan:**
All db_stress runs use a debug build: `make DEBUG_LEVEL=0 EXTRA_CXXFLAGS="-g -fno-inline" db_stress`.
## Seed handling: out-of-range seeds fail fast
Command:
```
python3 tools/cpu_corruption_injector/runner.py --op write --runs 2 --stress_cmd ./db_stress --report_dir /tmp/tp_badseed --seed 4294967295; echo "exit=$?"
python3 tools/cpu_corruption_injector/runner.py --op write --runs 1 --stress_cmd ./db_stress --report_dir /tmp/tp_badseed0 --seed 0; echo "exit=$?"
```
Output:
```
2026-07-15 01:26:14,408 ERROR --seed range [4294967295, 4294967296] is outside db_stress's accepted [1, 4294967295]; pass a smaller --seed
exit=2
2026-07-15 01:26:14,558 ERROR --seed range [0, 0] is outside db_stress's accepted [1, 4294967295]; pass a smaller --seed
exit=2
```
Both are rejected before any run starts (previously the seed silently wrapped and every run came back a misleading NO_INJECTION).
## Per-run command logged (full db_stress command in gdb.log)
Command:
```
python3 tools/cpu_corruption_injector/runner.py --op write --runs 2 --stress_cmd ./db_stress --report_dir /tmp/tp_seed --seed 424242
head -6 /tmp/tp_seed/run_00000/gdb.log
```
Output (the first line is the full command; the run's gdb/db_stress output follows it):
```
# /usr/bin/gdb --batch --nx -iex 'py import sys; sys.argv=["injector.py", "--op", "write", "--op_index", "259", "--entry_fn", "rocksdb::MemTable::Add", "--target_fn", "rocksdb::MemTable::Add", "--seed", "424242", "--dir", "/tmp/tp_seed/run_00000"]' -x /data/users/huixiao/fbsource/fbcode/internal_repo_rocksdb/repo/tools/cpu_corruption_injector/injector.py --args /data/users/huixiao/fbsource/fbcode/internal_repo_rocksdb/repo/db_stress --db=/tmp/tp_seed/run_00000/db --expected_values_dir=/tmp/tp_seed/run_00000/exp --seed=424242 --verify_cpu_corruption_dir=/tmp/tp_seed/run_00000 --ops_per_thread=1000 --max_key=1000 --value_size_mult=8 --column_families=1 --nooverwritepercent=0 --writepercent=50 --delpercent=40 --delrangepercent=10 --readpercent=0 --iterpercent=0 --prefixpercent=0 --flush_one_in=20 --compact_range_one_in=10 --compact_files_one_in=10 --target_file_size_base=65536 --reopen=0 --destroy_db_initially=1 --clear_column_family_one_in=0 --disable_wal=0 --threads=1 --num_dbs=1 --disable_auto_compactions=1 --write_buffer_size=1073741824 --remote_compaction_worker_threads=0 --subcompactions=1 --batch_protection_bytes_per_key=8 --memtable_protection_bytes_per_key=8 --block_protection_bytes_per_key=8 --paranoid_file_checks=1 --paranoid_memory_checks=1 --memtable_verify_per_key_checksum_on_seek=1 --detect_filter_construct_corruption=true --verify_checksum=1 --compression_checksum=1 --file_checksum_impl=crc32c --verify_checksum_one_in=1 --verify_file_checksums_one_in=1000 --read_fault_one_in=0 --write_fault_one_in=0 --metadata_read_fault_one_in=0 --metadata_write_fault_one_in=0 --open_read_fault_one_in=0 --open_write_fault_one_in=0 --open_metadata_read_fault_one_in=0 --open_metadata_write_fault_one_in=0 --secondary_cache_fault_one_in=0 --sync_fault_injection=0
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.centos.org/>
Debuginfod has been disabled.
```
The first line of `gdb.log` is the complete `gdb` + `db_stress` command, including `--seed=424242` and `--max_key=1000`; the run's own output follows below it.
## The logged command reflects real flag values
Temporarily set `max_key` to `12345` in `runner_build.py`, then:
Command:
```
python3 tools/cpu_corruption_injector/runner.py --op write --runs 1 --stress_cmd ./db_stress --report_dir /tmp/tp_maxkey --seed 424242
head -1 /tmp/tp_maxkey/run_00000/gdb.log | tr ' ' '
' | grep -- --max_key
```
Output:
```
--max_key=12345
```
The logged command tracked the changed preset value. Reverted the change afterward.
Reviewed By: archang19
Differential Revision: D112075800
fbshipit-source-id: 0d2be1741ede806a84a1a0f9ca6b2a32147c4e9c
Summary:
Wide-column read paths that materialize blob-referenced columns used to re-serialize the entire entity into a fresh contiguous buffer, copying the (potentially large) resolved column values byte-for-byte. This was forced by PinnableWideColumns holding a single backing buffer with all column Slices pointing into it.
PinnableWideColumns now holds its columns as zero-copy Slices into one or more stable backing buffers, kept in a std::forward_list<PinnableSlice> whose nodes never relocate on move, move-assignment, or splice. Resolved blob columns can be spliced in as additional backing buffers instead of re-serializing the entity.
Using address-stable nodes also cleans up the old Slice-invalidation handling. The previous single-buffer representation had to cope with a self-pinned PinnableSlice whose small (SSO) std::string relocates when the PinnableSlice is moved: on every move it detected the relocated backing pointer and re-derived the column Slices by re-parsing. With forward_list nodes that never move, the column Slices stay valid across move / move-assignment / splice, so that handling is gone and the move operations are now the defaulted member-wise moves.
The multi-buffer plumbing is internal only: a new PinnableWideColumnsHelper (a friend) performs the splice and the little bit of private-member access the read paths need, so there is no new public API surface and the previous `friend class DBImpl` is gone. The public view is unchanged: columns() still returns a single, in-order set.
Read paths converted to resolve without re-serializing:
- GetContext::SaveWideColumnEntityToColumns (eager GetEntity SST reads, separate-blob-file references)
- DBImpl::ResolveDirectWriteWideColumns (direct-write blob references)
- DBImpl::MaybeResolveMemtableBlobValue (blob-backed memtable reads)
Merge output still serializes (the MergeOperator API consumes/produces a serialized entity). Same-file ("embedded") blob columns are resolved -- and still re-serialized -- earlier in the block-based table reader (MaybeResolveEmbeddedValue / ResolveEmbeddedValueForGet) before the entity reaches GetContext, so the GetEntity path above only avoids re-serialization for separate-blob-file references. Compaction's separate entity machinery is unchanged.
API/accounting: deprecated PinnableWideColumns::serialized_size() (comment-only, per repo convention) in favor of the new payload_size() (sum of column name + value sizes; equals the value size for a plain value). Read statistics for entities (BYTES_READ, BYTES_PER_READ, per-key GetEntity/MultiGetEntity byte counts) and the ReadOptions::value_size_soft_limit threshold now measure entities by payload_size() instead of the serialized size. This drops framing bytes (entity measurements are slightly smaller), thus making plain-value and wide-column accounting consistent and reducing unnecessary exposure to implementation details.
Serialization cleanup enabled by the above:
- Consolidated Deserialize/DeserializeV2 into a single Deserialize(const Slice&, WideColumns&, blob_columns*) plus a DeserializeSimple alias for the no-blob-references case; both take const Slice&, require the input to be exactly one serialized entity, and reject any trailing data uniformly (V1 previously ignored trailing bytes). An unexpected blob reference where a fully-resolved entity is required now returns Corruption.
- ResolveEntityBlobColumns (serialized output, used only by merge) is now a thin wrapper over the new multi-buffer resolver plus Serialize, removing the duplicated fetch loop and the SerializeResolvedEntity helper.
- Consistently use `const Slice& input` rather than `Slice& input` for wide column functions expecting an input slice with no trailing data.
Design/polish from human review:
* The initial plan called for more complex handling around a vector<PinnableSlice> backing store (unique_ptr indirection and re-deriving the column Slices on move to cope with reallocation and SSO relocation); switching the backing store to a std::forward_list of address-stable nodes was a human suggestion that removed that complexity and the move-time Slice re-derivation entirely.
* Keep resolve plumbing off the public API via PinnableWideColumnsHelper and removing the DBImpl friend
* Various pieces of cleanup and polish (more left to do)
* The eager-GetEntity optimization
* The memtable inline-copy NOTE/TODO.
Intended follow-up:
* Resolve FIXME about pre-existing swallowed non-OK status in db_impl.cc
* Resolve TODO about value_size_soft_limit in db_impl.cc
* Extend this optimization to SST embedded-blob references
* Error-status/API cleanup: classify an unrecognized wide-column version as Corruption (not NotSupported) at all entry points; replace the NotSupported sentinel from GetValueOfDefaultColumn with an explicit is_blob_reference out-param (removing GetValueOfDefaultColumnResolvingBlobs); and centralize the "blob-backed value without a blob fetcher" Corruption into the resolvers (resolving the ResolveEntityForMerge FIXME) rather than repeating it at each caller
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14943
Test Plan:
Extended db/wide/wide_column_serialization_test.cc with PinnableWideColumns unit tests: set/payload_size for plain, V1, and resolved entities; move and move-assign of a multi-buffer result preserve column data pointers (the node-stability guarantee); Reset; ResolveColumns splices blob buffers in zero-copy (resolved values' data() point into the spliced-in buffers, not a re-serialized entity); serialized_size() == SerializedSizeV1(columns()); and Deserialize rejects trailing data after a serialized entity for both V1 and V2 layouts.
Built and ran (debug): wide_column_serialization_test, db_wide_basic_test, db_wide_blob_direct_write_test, db_blob_basic_test, compaction_iterator_test, db_merge_operator_test, db_iterator_test, udt_util_test, write_batch_with_index_test -- all pass. librocksdb and db_stress build. make format-auto and build_tools/check-sources.sh are clean.
Reviewed By: xingbowang
Differential Revision: D112212266
Pulled By: pdillinger
fbshipit-source-id: 13b89e2bb7d8371f4c1ff18b234a549bea9c9e77
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14938
Adds db_stress coverage for the topology where a read-only DB (`DB::OpenForReadOnly`) is opened on the same directory as a live read-write primary. This interaction was previously untested: `FLAGS_read_only` runs a DB by itself in read-only mode (no live writer), and `FLAGS_test_secondary` co-locates a secondary that tails the MANIFEST and never owns/deletes files, so neither exercises a read-only instance holding a stale live-file snapshot while the primary keeps creating files. That gap allowed a bug where a read-only DB's close ran obsolete-file cleanup against its stale snapshot and deleted the primary's live SST files.
New flag `open_read_only_one_in=N`: on thread 0, with probability 1/N per operation, open a read-only instance on the primary's live directory, flush the primary (so new SSTs exist that are absent from the reader's frozen snapshot), do a few reads, then close the reader. If the read-only close wrongly purges obsolete files, the primary's live files are deleted and surface through db_stress's existing missing-file/corruption detection (deterministically on the next reopen).
The read-only reader is opened with the primary's options (so it inherits the table factory, e.g. UDI) but with `compaction_service` and `listeners` cleared, since a read-only reader never compacts and must not share the primary's background-coordination services -- otherwise the reader's open/close would abort the primary's in-flight remote compactions.
`db_crashtest.py` enables the mode randomly and excludes configurations it does not support (transactions, BlobDB, TTL, and the blob-direct-write profile). Startup validation in `db_stress_tool.cc` rejects those same combinations when the flag is set directly.
Reviewed By: joshkang97
Differential Revision: D111995911
fbshipit-source-id: 0f7a2ef93e63cfa6677b6fa32c1bd34eb818c978
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14942
Remote storage reached over `--env_uri` / `--fs_uri` (e.g. Warm Storage) can surface transient infrastructure IO errors that are unrelated to any RocksDB bug. Previously such an error would take down the test/benchmark process, making remote runs unusable. This diff lets both `db_stress` and `db_bench` tolerate non-data-loss IO errors, behind default-off flags that only take effect against a remote backend, so local behavior is unchanged and local IO errors are never masked.
**db_stress:** Add a default-off flag `--tolerate_non_injected_io_errors_for_remote_dbs` that treats non-injected, non-data-loss IO errors as retryable, so remote infrastructure errors do not fail the stress harness through `SharedState::SafeTerminate()`. `IsErrorInjectedAndRetryable` enforces at the binary level that this only applies when a remote backend (`--env_uri` / `--fs_uri`) is in use; `db_crashtest.py` additionally force-disables it for local DBs. Remote crash-test jobs opt in by passing the flag on the command line (see the companion Tupperware config change).
**db_bench:** Add a default-off flag `--tolerate_io_errors_for_remote_dbs`. When set and a remote backend (`--env_uri` / `--fs_uri`) is in use, a non-data-loss IO error surfaced by a benchmark operation ends the run gracefully with an INVALID result through the existing cooperative shutdown path (`SharedState::SetFatal`) instead of calling `abort()` (which previously produced a `Received signal 6 (Aborted)` process crash). All read `Get` / `MultiGet` abort sites and the `BGScan` iterator-error site are routed through a shared `HandleBenchmarkIOError` helper; IO errors on local DBs and data-loss errors are never tolerated. With the flag off (default), the original `abort()` behavior is preserved so local runs still produce a core dump for debugging.
Reviewed By: anand1976
Differential Revision: D112136438
fbshipit-source-id: 510dbc644da78c4315a476ec766e26b2711dc060
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14930
Fix a TSAN-detected data race on the vptr between `MultiOpsTxnsStressTest::VerifyPkSkFast` making virtual calls through `db_` and `WritePreparedTxnDB::~WritePreparedTxnDB` updating the vptr during destruction.
The race occurs because `VerifyPkSkFast` (called from a compaction notification callback on a background thread) accesses `db_` which is a non-atomic raw pointer. Meanwhile, the main thread destroys the TransactionDB wrapper via `db_owner_.reset()` in `StressTest::CleanUp()`.
The previous fix (commit 768838ddcd99, `OnDBShutdownBegin`) reduced the race window by adding a `shutting_down_` check in the listener, but did not fully eliminate it because a background thread can pass the check before shutdown begins and then proceed to use `db_` while the destructor runs.
This fix addresses the root cause in two ways:
1. In `VerifyPkSkFast`: Use the atomically loaded `db` local variable (from `db_aptr_.load(memory_order_acquire)`) for ALL virtual calls instead of the non-atomic `db_` member. This ensures the pointer used for virtual dispatch was safely acquired before any shutdown signaling.
2. In `StressTest::CleanUp()`: Store nullptr to `db_aptr_` before calling `Close()`. This prevents any NEW entries into `VerifyPkSkFast` from background threads that have not yet loaded the pointer. `Close()` then waits for all in-flight background work (including any `VerifyPkSkFast` that already loaded a non-null pointer) to complete before proceeding to destroy the DB.
Together, these changes ensure that no background thread performs a virtual call through the TransactionDB pointer while the destructor is modifying the vptr.
Reviewed By: xingbowang
Differential Revision: D111480308
fbshipit-source-id: 0c998888d07712236c28eee60d04c6f169949a80
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14951
The `build-macos-cmake` CI job intermittently but persistently fails during `make` with:
GoogleTestAddTests.cmake:... (parse_tests_from_json)
* Line 1, Column 1
Syntax error: value, object or array expected.
make: *** [all] Error 2
This is `gtest_discover_tests` running in its default POST_BUILD mode: it executes each freshly-built test binary during the parallel `-j` build to enumerate test cases as JSON. On the resource-constrained arm64 macOS runners, that discovery run competes with the in-flight compile jobs and can produce empty output, which CMake's JSON parser then rejects, aborting the whole build. The failing targets (e.g. `cache_reservation_manager_test`, `lru_cache_test`) are unrelated to whatever change is under test -- other test binaries in the same run discover fine -- confirming it is a build-phase discovery race, not a code defect.
Switch discovery to `DISCOVERY_MODE PRE_TEST` so enumeration happens at ctest time (serially, after the build) instead of during the parallel build. This keeps the build green while preserving full test discovery. `PRE_TEST` requires CMake >= 3.18, so it is guarded by a version check; on older CMake (the project's declared minimum is 3.12) behavior is unchanged.
Reviewed By: xingbowang
Differential Revision: D112478854
fbshipit-source-id: 0d5edea416610f9cc1e9ae806d7d521165abe656
Summary:
Add RangeLockManagerHandle::SetIsKilledCallback, a std::function<bool()> predicate mirroring SetEscalationBarrierFunc. When installed, RangeTreeLockManager wakes the toku lock_request::wait loop every 100ms to poll it; if it fires, the wait completes with a new DB_LOCK_INTERRUPTED result mapped to Status::Aborted(), so a blocked GetRangeLock() returns promptly instead of waiting out the lock timeout. Range-lock users that install no callback keep the original wait-until-grant-or-timeout behavior (no extra wakeups).
Also remove the unused toku kill_waiter abort path (lock_request and locktree_manager), which had no caller.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14927
Test Plan:
`build_tools/rocksptest.sh range_locking_test` passes, including the two new tests. KilledCallbackAbortsWait completes in ~90ms despite a 100s lock timeout, confirming the kill callback aborts the wait promptly; KilledCallbackFalseStillTimesOut confirms an installed-but-false callback still times out.
Tested end-to-end together with the MyRocks side in D110890436: with rocksdb_use_range_locking=ON, a KILL QUERY parked on a conflicting range-lock wait is interrupted promptly (rocksdb.range_locking MTR suite) instead of waiting out rocksdb_lock_wait_timeout.
Reviewed By: pdillinger
Differential Revision: D111663405
Pulled By: laurynas-biveinis
fbshipit-source-id: 4819e09bde66f4e932aef74f6adfd14789bb1425
Summary:
Point-in-time MANIFEST replay -- best-efforts recovery (VersionSet::TryRecoverFromOneManifest) and secondary/follower catch-up (ManifestTailer) -- was quadratic in the number of MANIFEST records.
d458331ee (D61171320, "Move file tracking in VersionEditHandlerPointInTime to VersionBuilder") took a clean approach: it consolidated applying a VersionEdit and checking Version completeness into a single VersionBuilder implementation, so those checks were no longer duplicated between the handler and the builder. To let VersionEditHandlerPointInTime still build a Version reflecting the state before a valid->invalid edit, it added a save point -- but that save point proved inefficient: it was implemented by deep-copying the entire VersionBuilder::Rep before every edit (all of levels_[].added_files with a ref bump per file, table_file_levels_, found_files_, mutable_blob_file_metas_, ...). With N edits accumulating O(F) live files that is O(N*F) time and memory, and in the common healthy case the copy is pure waste since no Version is built during replay.
D110842287 (66bccb467, "Store BySmallestKey by value to avoid shared_ptr atomic copy in Rep") addressed a CPU-overhead symptom that had been misattributed to the shared_ptr atomic copies in that per-edit Rep copy. The real expense was copying Rep's containers -- notably table_file_levels_ -- on every edit. This change keeps d458331ee's clean single-implementation design but makes the save point cheap, removing the copy entirely:
- Replace the whole-Rep save point with a single-edit undo. Apply() records a compact, self-inverse undo of just the current edit (the pre-edit value of each touched key), and the PIT handler builds the pre-edit Version by rolling that one edit back, then redoing it to continue replay. This is O(edit) per edit, single-copy memory, and adds no extra file-verification I/O except on the rare valid->invalid negative edge. Also removes the Rep copy constructor.
- Store the undo registries in UnorderedMap (hash map) where ordering is not needed, which reduces the remaining per-edit CPU further. added_files and deleted_files keep an ordered map keyed by (level, file number), which trivial moves (same file number at two levels in one edit) require.
- Group the scalar/flag state mutated during Apply into a MutableScalars base so the undo can snapshot/restore it with a single copy/swap without duplicating the field list. Add util/cast_util.h up_cast<Base> for the explicit, compile-time-checked upcasts this uses instead of raw static_cast.
- Generalize D110842287: also store the stateless L0 comparators level_zero_cmp_by_epochno_ and level_zero_cmp_by_seqno_ by value rather than shared_ptr, dropping their make_shared allocation and atomic refcounting.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14929
Test Plan: Debug build (asserts + force_consistency_checks on). The single-edit rollback path is exercised by the existing recovery coverage, all passing: version_builder_test, version_set_test, db_secondary_test (ManifestTailer), db_follower_test (PartialVersionRecovery = the negative-edge rebuild), and the db_basic_test best-efforts suite -- including BestEffortsRecoverIncompleteVersion with blob-file variants (blob addition rolled back/redone) and BestEffortRecoveryFailureWithTableCacheUseAfterFree (FileMetaData/table-cache lifecycle) -- plus corruption_test.
Reviewed By: joshkang97
Differential Revision: D111753669
Pulled By: pdillinger
fbshipit-source-id: 7e6b5238f55cd906b1d4ff0044aeb0a02ec1cdcd
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14918
When `optimize_manifest_for_recovery=true` and `track_and_verify_wals_in_manifest=true`, the close-time code (`MaybeWriteWalMarkersToManifestOnClose`) already writes `DeleteWalsBefore(max_wal+1)` to the MANIFEST. On the subsequent clean reopen, recovery was unconditionally emitting the same `DeleteWalsBefore` edit, causing 43 bytes of unnecessary MANIFEST growth. This violated the zero-growth guarantee that the stress test STRICT mode verifies, triggering `exit(1)` which then caused a secondary `property_info != nullptr` assertion in `DumpStats()` (from the Timer thread running during static destructor teardown).
The fix adds the same monotonicity guard (`new_min_log > GetMinWalNumberToKeep()`) that is already used at all four other `DeleteWalsBefore` call sites (db_impl.cc:748, db_impl_files.cc:906, db_impl_write.cc:3331, memtable_list.cc:1015). This skips the redundant write when the WalSet min was already advanced at close time, while still correctly emitting it after a crash where the close-time markers were never written.
Reviewed By: xingbowang
Differential Revision: D110713771
fbshipit-source-id: 3592d11b20a9e660f1fea58a7a72565b2030c6d8
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14931
The failing crash-test scenario combines `disable_wal=1`, `LockWAL()`, and backup/checkpoint live-file-copy verification. A WAL-disabled write can leave a key only in the memtable. The old live-file path skipped `FlushForGetLiveFiles()` whenever `LockWAL()` was active, so the copied SST/WAL file set could omit that memtable-only key even though the original DB still served it. Restore verification could then report the key as present in the original DB but missing from the restored backup/checkpoint.
Fix live-file capture to flush unpersisted WAL-disabled data even while `LockWAL()` is active. If all data is already persisted, the path still skips the flush while WAL is locked. If unpersisted data exists, backup/checkpoint can now block behind `LockWAL()` until another thread calls `UnlockWAL()` and the flush can safely proceed.
To avoid self-deadlock, track `LockWAL()` ownership by thread with recursive counts. `UnlockWAL()` now has to be called by the same thread that successfully called `LockWAL()`, and wait=true flushes called by that same owner return `Aborted("Likely deadlock as the same thread called LockWAL()")` instead of waiting forever. Cross-thread flushes remain allowed and wait for all `LockWAL()` owners to unlock.
Also audited MyRocks and can confirm they already conform to the Lock/Unlock on same thread semantics.
Reviewed By: pdillinger
Differential Revision: D111793089
fbshipit-source-id: 1bbb03b25d00d47440126cc6bb4ce7410302e25b
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14920
When a WriteUnprepared transaction's commit write fails while two_write_queues
is enabled (e.g. due to an I/O error), CommitInternal() unconditionally cleared
unprep_seqs_ and the flushed/unflushed save points before returning the error.
That left the transaction with no record of its prepared sequence numbers, so a
subsequent Rollback() -- the natural way for a caller to recover from a failed
commit -- constructed a WriteUnpreparedCommitEntryPreReleaseCallback over an
empty unprep_seqs_ and tripped assert(unprep_seqs.size() > 0) in its
constructor. In release builds this would instead corrupt commit-cache
accounting rather than aborting.
Fix: only clear unprep_seqs_ and the save points on the commit success path. On
the two-write-queues commit-write failure path we still remove the prepared
entry that AddPreparedCallback may have added for commit_batch_seq (that seq is
never stored in unprep_seqs_, so a later Rollback cannot clean it up), but
unprep_seqs_ is otherwise preserved so the transaction can still be rolled back
once the caller resumes the DB from the write error.
Originally authored by Devmate trigger
Reviewed By: xingbowang
Differential Revision: D110271443
fbshipit-source-id: 7d991e01ddc74ccd170cbc25f6af160c5f3d5663
Summary:
- Add a RocksDB blog post explaining Blob Direct Write and partitioned blob files.
- Cover the write-path transformation to `BlobIndex`, partition selection, blob-file lifecycle, read fallback for in-flight direct-write files, and the current v1 scope.
- Describe policy-driven grouping use cases such as TTL bucketing with Universal Compaction and wide-column metadata.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14873
Test Plan: {F1991646296}
Reviewed By: joshkang97
Differential Revision: D109564817
Pulled By: xingbowang
fbshipit-source-id: 712fc0b6a8ffe68e2068af756dceb421c65a9af8
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14933
Codex reviews currently have some issues with the API Key. Will disable them from reviews to prevent noise until it is properly fixed.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14932
Differential Revision: D111798073
Pulled By: joshkang97
fbshipit-source-id: 9340b89371185979e34465325c20d02a494990e5
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14926
This fixes a gap in the MultiScan prefetch waste accounting introduced by https://github.com/facebook/rocksdb/pull/14876. That change added reverse MultiScan support and introduced `unreleased_prefetch_blocks_` to track prefetched blocks still owned by the `ReadSet`, because reverse scans can prefetch a suffix of the collected block list and release blocks in non-prefix order. However, those unreleased prefetched blocks were only released during `MultiScanIndexIterator` destruction; they were not included in `MULTISCAN_PREFETCH_BLOCKS_WASTED`. As a result, scans that stopped before consuming all prefetched blocks underreported wasted prefetch blocks.
This change counts unreleased prefetched blocks as wasted before destructor cleanup, while avoiding counting the currently positioned block as wasted when it has been reached.
Reviewed By: xingbowang
Differential Revision: D111531409
fbshipit-source-id: 5e605ce686037256bf6847ab7ca363c3f7701401
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14807
The "counts" array in CompactionServiceResult (one compaction count per CompactionReason) is non-critical information. During cross-process remote compaction the primary and the remote worker can run different RocksDB versions and therefore disagree on kNumOfReasons, so the serialized counts array can have a different length than the reader expects. The plain OptionTypeInfo::Array parser rejects any element-count mismatch, which fails deserialization of the entire result over this non-critical field.
This makes deserialization of "counts" size-tolerant via SizeTolerantDeserializeArray, which overrides only the parse callback: extra trailing elements are dropped and missing trailing elements are zero-filled, so a length mismatch no longer fails deserialization. Serialization and comparison are inherited from Array<T, kSize> unchanged.
The serialized width is intentionally kept at kNumOfReasons - 1 here, so this change does not alter what is written -- only the read path becomes tolerant. Widening the serialized width to the full kNumOfReasons is done as a separate follow-up change, which must not land until this tolerant read has been deployed to every primary (including rollback targets), so that no reader still rejects a wider result.
== Test plan ==
**Legend**
CompactionReason is an enum whose last entry is kNumOfReasons (currently 21). CompactionStats::counts is an int[kNumOfReasons] holding one count per reason; it is serialized into the remote compaction result and read back by the primary.
- "narrow" = kNumOfReasons - 1 counts (omits the newest reason); what current code writes.
- "wide" = kNumOfReasons counts (includes the newest reason).
- strict reader: accepts only its exact expected element count; any other count fails deserialization (the plain OptionTypeInfo::Array behavior).
- tolerant reader: accepts any count -- more elements than expected are dropped, fewer are zero-filled; never fails on a count mismatch (this change, SizeTolerantDeserializeArray).
**Unit test**
CompactionServiceTest.CompatCheckCountsWidthTolerance -- verifies that deserializing "counts" tolerates a serialized element count equal to, one greater than, or one less than the reader's expected width (extras dropped, missing zero-filled), and that the binary serializes exactly the expected number of elements.
**Manual cross-version verification (ldb remote_compaction)**
Test 1: baseline (unchanged behavior) -- expect-narrow strict-read primary reads narrow counts written by a worker
```
$ strict_narrow remote_compaction_worker --db=$DB --job_dir=$JOB &
$ strict_narrow remote_compaction_primary --db=$DB --job_dir=$JOB
remote_compaction_worker: OK (4913 bytes result)
remote_compaction_primary: OK
$ grep 'remote compaction result' $DB/LOG
[default] [JOB 3] Received remote compaction result, output path: /tmp/cs_run/output, files: 000022.sst
```
=> PASS: the primary parsed and installed the remote result.
Test 2: expect-narrow tolerant-read primary reads the narrow counts a worker writes
```
$ strict_narrow remote_compaction_worker --db=$DB --job_dir=$JOB &
$ tolerant_narrow remote_compaction_primary --db=$DB --job_dir=$JOB
remote_compaction_worker: OK (4912 bytes result)
remote_compaction_primary: OK
$ grep 'remote compaction result' $DB/LOG
[default] [JOB 3] Received remote compaction result, output path: /tmp/cs_run/output, files: 000022.sst
```
=> PASS: the primary parsed and installed the remote result.
Test 3: expect-narrow tolerant-read primary reads the wide counts a worker writes (extra reason dropped)
```
$ tolerant_wide remote_compaction_worker --db=$DB --job_dir=$JOB &
$ tolerant_narrow remote_compaction_primary --db=$DB --job_dir=$JOB
remote_compaction_worker: OK (4915 bytes result)
remote_compaction_primary: OK
$ grep 'remote compaction result' $DB/LOG
[default] [JOB 3] Received remote compaction result, output path: /tmp/cs_run/output, files: 000022.sst
```
=> PASS: the primary parsed and installed the remote result.
Test 4: expect-wide, tolerant primary reads the narrow counts a worker writes (missing reason zero-filled)
```
$ strict_narrow remote_compaction_worker --db=$DB --job_dir=$JOB &
$ tolerant_wide remote_compaction_primary --db=$DB --job_dir=$JOB
remote_compaction_worker: OK (4911 bytes result)
remote_compaction_primary: OK
$ grep 'remote compaction result' $DB/LOG
[default] [JOB 3] Received remote compaction result, output path: /tmp/cs_run/output, files: 000022.sst
```
=> PASS: the primary parsed and installed the remote result.
Test 5: expect-wide, tolerant primary reads a wide counts a worker write
```
$ tolerant_wide remote_compaction_worker --db=$DB --job_dir=$JOB &
$ tolerant_wide remote_compaction_primary --db=$DB --job_dir=$JOB
remote_compaction_worker: OK (4917 bytes result)
remote_compaction_primary: OK
$ grep 'remote compaction result' $DB/LOG
[default] [JOB 3] Received remote compaction result, output path: /tmp/cs_run/output, files: 000022.sst
```
=> PASS: the primary parsed and installed the remote result.
Reviewed By: jaykorean
Differential Revision: D106321320
fbshipit-source-id: fc1bd66a4d11ac6bf38dff6aa214ebafab75f3d0
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14923
Fix the GitHub Actions Codex review job by running generated review prompts through generic `codex exec` instead of the specialized `codex exec review` subcommand. The workflow already embeds PR metadata, reviewer instructions, and the diff in `/tmp/prompt.txt`, while `codex exec review --base` rejects a positional/stdin prompt. This removes the invalid `--base`/stdin combination from both automatic and manual Codex review paths while leaving `/codex-query` behavior unchanged.
Reviewed By: xingbowang
Differential Revision: D111135454
fbshipit-source-id: 16e86adda41d3e784c59466658cb769278c909c7
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14922
- Preserve source compatibility for `Scan::Reset` by restoring the default `reverse` argument.
- Make `HasMoreScanRanges()` defensive for reverse prepared scans instead of asserting.
- Restore broader forward coverage in `MultiScanAsyncIOTest` while keeping reverse coverage on the representative async IO matrix.
- Let `--multiscan_reverse` drive reverse MultiScan stress coverage independently of `--test_backward_scan`.
- Add the reverse MultiScan public API release note.
Reviewed By: xingbowang
Differential Revision: D111127403
fbshipit-source-id: 1a17d0a36aaf66e8cbc51f445d0f85e145fd653c
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14876
Add reverse scan support to `MultiScanArgs`/`MultiScan` so callers can iterate bounded scan ranges in descending order. Plumb reverse scan state through `DBIter`, `BlockBasedTableIterator`, and `MultiScanIndexIterator`, including reverse prefetch windowing and async I/O support. Update `db_bench` `multiscanrandom` to honor `--reverse_iterator` and report rows scanned so reverse MultiScan and iterator baselines can be compared on row throughput.
Add `db_stress --multiscan_reverse` coverage so the prepared MultiScan path can be stressed with `SeekForPrev()`/`Prev()` against a control iterator.
Also fix the table child iterator path to accept reverse `SeekForPrev()` targets above a file's prepared ranges. `MergingIterator` and `LevelIterator` can legally seek every child to the current range limit; a child file may only have lower prepared ranges, so treating that as invalid caused intermittent `SeekForPrev target is outside prepared ranges` exceptions in the benchmark.
Reviewed By: xingbowang
Differential Revision: D109357730
fbshipit-source-id: eccece5ca7d8f5d6760d51ebe76801a332a49061
Summary:
- Update `folly.mk` FOLLY_COMMIT_HASH to 2a68075b77d9
This is folly's `main` at release-cut time. Review and merge to take the bump, or close to skip folly for this release.
Part of 11.6 release workflow.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14912
Reviewed By: jaykorean
Differential Revision: D110524765
Pulled By: pdillinger
fbshipit-source-id: 3361fdc6277b5f02828f23942b9301237b1ed272
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14914
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14720
### Context
`VersionSet::LogAndApply` loads table handlers before the `MANIFEST` update is durable. If that `MANIFEST` update later fails, newly added files can be discarded by `VersionBuilder` without ever being installed in a `Version`. `VersionBuilder::UnrefFile` released the `FileMetaData` `pinned_reader` handle, but releasing that handle only dropped the reference; it did not erase the cache key, so an orphaned table-cache entry could survive for a file that is neither live nor quarantined. When metadata read fault injection prevents the obsolete-file scan from cleaning up the orphan, DB close can trip `TEST_VerifyNoObsoleteFilesCached` with File N is not live nor quarantined.
### Fix
After releasing a pinned_reader for a `FileMetaData` whose refcount reaches zero, explicitly evict that file number from the table cache. The new `VersionBuilderDBTest.FailedLogAndApplyEvictsTableCacheEntry` regression test exercises the realistic production path: it creates a real SST, calls `LogAndApply` through the DB's own `VersionSet`, injects a MANIFEST sync failure via the `AfterSyncManifest` sync point, and asserts that the table cache no longer contains the file after `ProcessManifestWrites` destroys the `VersionBuilder`. With the eviction removed, the test fails deterministically with `leaked_cache_entry=true`.
Reviewed By: hx235
Differential Revision: D99759696
fbshipit-source-id: 0ebb32b1544ad1982d23a6c06569984be97a0a88
Summary:
Fixes three unrelated nightly/CI failures.
Makefile: 'make build_folly' was tripping the build-signature change check, causing the folly-flavored jobs (e.g. nightly clang-21 ASAN+UBSAN+folly, which runs 'make build_folly' then 'USE_FOLLY=1 ... make check') to abort with 'Build parameters changed since the last build'. build_folly only builds the external folly library under third-party/folly and never compiles RocksDB objects into OBJ_DIR, so it should not record or check a build signature; add it to BUILD_SIG_NONBUILD_GOALS alongside checkout_folly.
util/rate_limiter_test.cc: RateLimiterTest.Rate's minimum-rate assertion is timing-sensitive and flakes on the small/loaded ARM CI runners, which (unlike x86_64) can't reliably sustain the target rate. Extend the existing CI exemption with an ARM-only GITHUB_ACTIONS branch, preserving the check on x86_64.
tools/ldb_test.py: the list_live_files_metadata output includes the full DB path, and the unescaped-dot regex '\d+.sst' could match a <digit><char>sst run inside the random tempfile.mkdtemp suffix (drawn from [a-z0-9_]) that precedes the real filename, collapsing testMap to a single bogus key such as '8asst'. Require a literal '.sst'; temp-dir characters can never contain a dot, so only the real filename matches.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14910
Test Plan:
- build_folly: confirmed 'make build_folly' no longer writes .build_signature and that BUILD_SIG_DO_BUILD resolves empty for that goal.
- rate_limiter_test: confirmed via the C preprocessor that x86_64 keeps the SANDCASTLE-only exemption while aarch64 selects the new GITHUB_ACTIONS branch.
- ldb_test: reproduced the collapse with a poisoned temp suffix (produces {'8asst': ['0', 'mycol2']}) and confirmed the escaped-dot regex yields the correct 4-entry map.
Reviewed By: mszeszko-meta
Differential Revision: D110405164
Pulled By: pdillinger
fbshipit-source-id: a9a73b7a717e726fdf79d7795f085f3dbede940e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14907
`RandomAccessFileReader::ReadAsync` took its "already aligned" fast path when the request offset and length were sector-aligned, forwarding the caller's `req` (including its `scratch` pointer) straight to the FileSystem. A null `scratch` trivially satisfied the alignment check (`uintptr_t(nullptr) & (alignment - 1) == 0`), so when a caller submitted an aligned request with `scratch == nullptr` and an `aligned_buf` out-parameter for the reader to allocate into, the null buffer was submitted to the async read. For direct IO with io_uring this becomes a null `iovec` base, which the kernel rejects with `EFAULT` (surfaced as `Req failed: Unknown error -14`).
This is exactly how `IODispatcher` submits MultiScan async reads for plain direct IO (`scratch == nullptr`, `aligned_buf` provided), so the failure manifested intermittently as `db_stress` iterator divergence ("Iterator diverged from control iterator") in crash tests with `use_direct_reads=1 --use_multiscan=1 --multiscan_use_async_io=1`. It is intermittent because it only triggers when the coalesced read's offset and length both happen to be sector-aligned. Task T267030385.
The fix excludes the aligned fast path when the caller provided no `scratch` but did provide an `aligned_buf`, forcing the allocating path so a valid backing buffer is always handed to the async read. This matches the existing behavior of the synchronous `Read`, which already guards its aligned fast path with `scratch != nullptr`.
Reviewed By: xingbowang
Differential Revision: D110361301
fbshipit-source-id: 3884f7ddbc746cf51b2566d74918e2fb97233dcb
Summary:
Experimental embedded ("same-file") blob SST support (https://github.com/facebook/rocksdb/issues/14851) overloads blob file number 0 as both kInvalidBlobFileNumber and the same-file sentinel kCurrentFileBlobIndexFileNumber. Same-file references must be resolved by EmbeddedBlobResolvingIterator before they reach any generic file-metadata or integrated-BlobDB path, all of which reject file number 0 as invalid.
When resolving a same-file blob hit an error (e.g. a db_stress-injected blob-region read fault), EmbeddedBlobResolvingIterator::value() fell back to returning the RAW, unresolved same-file value, surfacing the error only via status()/Valid(). Compaction consumed the raw value before consulting status, leaking an unresolved same-file reference into the output stream. Depending on the value type this produced two different crash-test failures, both from this one root cause:
* T277566778 -- wide-column entity variant. The raw same-file entity (kTypeWideColumnEntity) reached FileMetaData::UpdateBoundaries, whose blob-ref scan (correctly) rejected file number 0: Flush failed: Corruption: Invalid blob file number (also observed from a background compaction). The tripwire fired on the leak, so the real injected read error was masked behind a misleading corruption status.
* T277310719 -- whole-value BlobIndex variant, and more dangerous because it escapes that tripwire. ResolveKeyType() rewrites the key type kTypeBlobIndex -> kTypeValue with no I/O (so it always succeeds); on the masked error the emitted entry is therefore {kTypeValue, raw BlobIndex bytes}. UpdateBoundaries only scans kTypeBlobIndex/entity types, so it does NOT reject this record: the corruption is silently written to the compaction output and persists. A later point lookup reads those raw BlobIndex bytes back as the value, which db_stress detects as db_stress: expected_value.cc:102: Assertion `ExpectedValue::IsValueBaseValid(value_base)' failed from TestGet. In the captured crash the aborting db_stress process had injected no faults itself -- it read corruption persisted by an earlier faults-on run (blackbox reuses the DB directory), confirming the persistence route.
Fix: EmbeddedBlobResolvingIterator now resolves eagerly for callers that do not opt into unprepared values (allow_unprepared_value=false, e.g. compaction), via a new EagerEmbeddedBlobResolvingIterator (the caller is selected in BlockBasedTable::NewIterator). Eager callers resolve the value during positioning, so a resolution error (blob-region I/O or corruption) is observable through status()/Valid() BEFORE value() is consumed, and value() never exposes an unresolved same-file BlobIndex. Lazy callers (allow_unprepared_value=true, user iteration) keep value laziness but must honor PrepareValue()'s result. This keeps the "callers never see an unresolved same-file blob" invariant structural, even on the error path. Using a template for EmbeddedBlobResolvingIterator minimizes unnecessary overheads.
Also hardens and documents the integrity tripwires that caught the leak, so they are not "fixed" by weakening them -- doing so would mask real corruption and could persist unresolvable same-file references into ordinary (non-embedded) SSTs, turning a transient error into permanent data loss: FileMetaData::UpdateBoundaries (write/output path), Version::GetBlob and Version::MultiGetBlob (integrated-BlobDB read path), plus the contract note in blob_constants.h.
Adds a test-only sync point
"BlockBasedTable::MaybeResolveEmbeddedValue:InjectError" (compiled out under NDEBUG) to simulate a blob-region resolution fault deterministically.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14906
Test Plan:
New unit tests in db_blob_index_test, both of which fail without the fix and pass with it:
* DBBlobIndexTest.EmbeddedBlobResolveErrorDuringCompactionNotMasked -- entity variant. Without the fix CompactRange fails with the masked "Invalid blob file number"; with the fix it fails with the injected IOError.
* DBBlobIndexTest.EmbeddedBlobResolveErrorWholeValueDuringCompactionNotMasked -- whole-value variant. Without the fix CompactRange SUCCEEDS (silently masking the error and persisting {kTypeValue, raw BlobIndex}); with the fix it fails with the injected IOError.
Confirmed the tests exercise the real root cause by temporarily neutralizing the eager resolution (MaybeEagerlyMaterialize): both tests then fail in their respective pre-fix modes (entity -> "Invalid blob file number"; whole-value -> compaction succeeds), then pass again once restored.
End-to-end: the db_stress reproducer for T277566778 (blackbox crash test with embedded-blob ingestion and fault injection) reproduced the "Invalid blob file number" crash before the fix and no longer reproduces with it.
Reviewed By: anand1976
Differential Revision: D110361228
Pulled By: pdillinger
fbshipit-source-id: 1faaa9a9ead726f17b611ff150418fa59ed75fd4
Summary:
This PR adds flush observability for debugging write stalls and flush bottlenecks.
Changes include:
- Add flush reason tickers for physical flush jobs:
- rocksdb.flush.reason.write_buffer_full
- rocksdb.flush.reason.write_buffer_manager
- rocksdb.flush.reason.memtable_max_range_deletions
- Add flush memtable size histograms:
- rocksdb.flush.memtable.memory.bytes
- rocksdb.flush.memtable.total.data.size.bytes
- rocksdb.flush.write_buffer_full.memtable.memory.bytes
- rocksdb.flush.write_buffer_manager.memtable.memory.bytes
- Add FlushReason::kMemtableMaxRangeDeletions and propagate it from memtables when memtable_max_range_deletions triggers a flush.
- Preserve atomic flush as a single request-level reason. For atomic flush, the reason is derived only from CFs that scheduled the flush, not every CF selected into the atomic flush cut.
- Add atomic flush request-level counters:
- rocksdb.atomic_flush.request.reason.write_buffer_full
- rocksdb.atomic_flush.request.reason.write_buffer_manager
- rocksdb.atomic_flush.request.reason.memtable_max_range_deletions
- rocksdb.atomic_flush.request.reason.other
- Keep existing per-CF flush job reason counters unchanged. Under atomic flush, rocksdb.flush.reason.* still counts physical CF flush jobs, while rocksdb.atomic_flush.request.reason.* counts one logical atomic flush request.
- Wire the new flush reason, ticker, and histogram types through Java and JNI bindings.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14898
Test Plan: CI
Reviewed By: anand1976
Differential Revision: D110214337
Pulled By: xingbowang
fbshipit-source-id: 4a445c4175d2e09ff5edbe66fde81fa00bb9e8da
Summary:
D110140135/https://github.com/facebook/rocksdb/issues/14896 worked around a db_stress failure by disabling test_backward_scan whenever embedded-blob SSTs are ingested. The real limitation was in EmbeddedBlobResolvingIterator: DBIter requires the underlying iterator's value to be pinned for backward iteration (Prev/SeekForPrev), and rebuilt wide-column entity values (with same-file blob columns) were stored in an iterator-owned std::string that was cleared on every reposition, so IsValuePinned() returned false and DBIter returned NotSupported (or tripped the IsValuePinned() assertion in FindValueForCurrentKeyUsingSeek). Whole-value same-file blobs were already pinned and worked.
This change makes the rebuilt wide-column value pinnable like a whole-value blob: it is moved into a heap buffer and pinned into resolved_pinned_value_, whose cleanup ResetState() hands to the PinnedIteratorsManager across repositioning so the value survives backward iteration. The now-redundant resolved_value_ member is removed and value()/IsValuePinned() simplified. The Get/MultiGet resolution path (MaybeResolveEmbeddedValue) is unchanged. The db_stress test_backward_scan workaround is removed now that backward iteration over embedded blobs (including wide-column entities) is supported.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14902
Test Plan:
Added DBBlobIndexTest.EmbeddedBlobBackwardIteration: ingests an embedded-blob SST with whole-value blobs plus a mixed wide-column entity (inline default column + embedded same-file blob column), then iterates backward via SeekToLast/Prev and SeekForPrev, verifying values and columns. Verified it fails before the fix (NotSupported at the entity) and passes after.
Full db_blob_index_test and sst_file_reader_test suites pass. make format-auto clean. Direct db_stress run with
ingest_external_file_with_embedded_blobs=1, use_put_entity_one_in=3, test_backward_scan=1, iterpercent>0 ran with no divergence/NotSupported/ assert.
Reviewed By: anand1976
Differential Revision: D110271453
Pulled By: pdillinger
fbshipit-source-id: 501a50d3c97bac9a217b604c43c8748abbb563c9
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14867
Adds an opt-in --randomize_stress_flags mode to the CPU corruption injection runner.
The fixed DB_STRESS_PRESET keeps an injected corruption isolated and observable, but every run then exercises the same stress test flags (default values for non DB_STRESS_PRESET flags). To widen what a runner covers, we want every other db_stress knob to vary run to run.
Therefore this PR makes each run's db_stress flags are a fresh db_crashtest.py roll (its full blackbox parameter space) with DB_STRESS_PRESET pinned on top. A small CLOSURE gate set stops db_crashtest's finalize_and_sanitize() from un-pinning preset flags, and an assertion fails the run fast if any preset flag is changed. This is considered as a work-around until we can refactor the flag generation of db_crashtest.py into a isolated function to take a set of flags to honor. This PR also adds some randomization into `max_keys` and local vs remote compaction as they are subjected to some special constraint unique to the cpu corruption injection framework.
The per-run roll is seeded by the run's seed, so randomized runs' flags are reproducible with --seed (though the actual db stress test behavior may still have some difference under the same flags).
**Test plan:**
Functional test
- build_runs(..., randomize=True) produces a full randomized flag set (~286 flags vs ~49 for the fixed preset) and the preset survives the db_crashtest roll (the _assert_preset_survived check never fires).
- randomized runs are reproducible: same base_seed -> same flags; run i seeded base_seed+i.
Integration test
```
for op in write flush compaction; do
python3 tools/cpu_corruption_injector/runner.py --op $op --runs 30 --randomize_stress_flags --parallel 8 --stress_cmd "$BIN" --report_dir /tmp/icc-rand/$op
done
```
- every run still carries the pinned DB_STRESS_PRESET values (preset-survived assertion never fires across the campaign).
- the workload genuinely varies run to run (e.g. max_keys, different compaction_style / memtablerep / *_one_in values across runs), confirming randomization is active.
- SDC still appears across write / flush / compaction -- randomization widens coverage without losing the SDC signal; NO_INJECTION and ERROR stay ~0.
**Runs' Outcomes (`summary.json`):**
| SDC | CORRUPTION | CRASH | NO_EFFECT | NO_INJECTION | ERROR |
| --- | --- | --- | --- | --- | --- |
| 2 | 7 | 7 | 43 | 1 | 0 |
Local vs remote compaction. With `--randomize_stress_flags` a compaction run may flip to the remote compaction service (`remote_compaction_worker_threads=1`), so the injector breaks on `CompactionServiceCompactionJob::Run` instead of the local `CompactionJob::Run`. Splitting the runs by injection entry_fn:
| where | runs | SDC | CORRUPTION | CRASH | NO_EFFECT | NO_INJECTION |
| --- | --- | --- | --- | --- | --- | --- |
| local | 25 | 1 | 1 | 3 | 20 | 0 |
| remote | 35 | 1 | 6 | 4 | 23 | 1 |
Reviewed By: pdillinger
Differential Revision: D108551605
fbshipit-source-id: d3dc5687e70ab57779ad6e4f87cac7ed0086da34
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14866
Orchestration layer of the CPU corruption injector -- the glue between detection (https://github.com/facebook/rocksdb/pull/14852) and injection (https://github.com/facebook/rocksdb/pull/14858
).
One CPU-corruption injection says little on its own. What matters is the OUTCOME DISTRIBUTION over many injections -- how often a corruption is silently absorbed (NO_EFFECT), crashes the process (CRASH), is caught by an integrity check (CORRUPTION), or more importantly slips through as a silent data corruption (SDC) and the paths frequently leading to those outcomes. A trustworthy distribution needs a (somewhat) repeatable and reproducible harness as well as a db_stress configuration in which an injected corruption is both reachable and attributable to the chosen stress test op (i.e, write, foreground compaction or flush).
Therefore this PR implements a runner that can launch N independent runs for a chosen op type (i.e, write, foreground compaction or flush). Each run picks where to inject, runs db_stress under gdb via the `injector,py` (https://github.com/facebook/rocksdb/pull/14858), and is classified into one outcome bucket (https://github.com/facebook/rocksdb/pull/14852).
The runner has DB_STRESS_PRESET -- the pinned db_stress config that isolates a single injected corruption (single-threaded, integrity checks on, other fault injection off, auto-compaction off). The runner also does gdb preflight that fails fast when the build or gdb cannot support injection because, for example, the hard-coded `target_fn` has changed its name, provides parallel launching of many runs and one summary.json per campaign (the outcome distribution plus each run's record). The whole run set is reproducible from one logged base_seed (run i uses base_seed + i).
**Test plan:**
Build: `make DEBUG_LEVEL=0 EXTRA_CXXFLAGS="-g -fno-inline" db_stress`
# 1. Preflight (`verify_injection_site`) catches a build that can't support injection
Before doing any work, the runner has gdb confirm — on this exact binary — that every injection-site function resolves and gdb can read its source line. A good build logs:
```
INFO gdb check OK for op=compaction: rocksdb::CompactionJob::Run, rocksdb::CompactionIterator::NextFromInput, rocksdb::BlockBuilder::Add
```
A build that cannot support injection (functions renamed, fully inlined, or absent) fails fast with exit 2 before any run — forced here by pointing `--stress_cmd` at a non-db_stress binary:
```
$ python3 tools/cpu_corruption_injector/runner.py --op compaction --runs 1 --stress_cmd /bin/ls --report_dir /tmp/icc/preflight_demo
ERROR gdb could not set a breakpoint on these functions in ls (renamed, fully inlined, or not in this build?): rocksdb::CompactionJob::Run, rocksdb::CompactionIterator::NextFromInput, rocksdb::BlockBuilder::Add
Function "rocksdb::CompactionJob::Run" not defined.
Function "rocksdb::CompactionIterator::NextFromInput" not defined.
# exit code 2
```
So a broken/inlined build is rejected up front instead of silently producing `NO_INJECTION` runs.
# 2. Compaction op -- 100 runs
```
$ python3 tools/cpu_corruption_injector/runner.py --op compaction --runs 100 --stress_cmd /data/users/huixiao/rocksdb/db_stress --report_dir /tmp/icc/preflight_demo
```
**Runs' outcomes (`summary.json`):**
| SDC | CORRUPTION | CRASH | NO_EFFECT | NO_INJECTION | ERROR |
| --- | --- | --- | --- | --- | --- |
| 9 | 5 | 6 | 79 | 1 | 0 |
**Spread:**
- target_fn x outcome: `NextFromInput` {SDC 9, CORRUPTION 3, CRASH 2, NO_EFFECT 34, NO_INJECTION 1}; `BlockBuilder::Add` {CORRUPTION 2, CRASH 4, NO_EFFECT 45}.
- corruption_type x outcome: `bit_flip` {SDC 8, CORRUPTION 3, CRASH 6, NO_EFFECT 32}; `flag_flip` {SDC 1, CORRUPTION 2, NO_EFFECT 42}; `lane_bit_flip` {NO_EFFECT 5}.
**Analysis:** all 9 SDCs land on the read/iterate path (`NextFromInput`); corrupting the output writer (`BlockBuilder::Add`) never produced an SDC (its blocks are checksummed -- corruption there is caught or inert). The 5 detected `CORRUPTION`s are compaction's key-order and record-count cross-checks firing (both CompactRange and CompactFiles origins appear), correctly bucketed by the fix.
### A representative compaction SDC: `run_00000`
What we corrupted (`inject.json`):
```json
{"op":"compaction","op_index":17,"entry_fn":"rocksdb::CompactionJob::Run","target_fn":"rocksdb::CompactionIterator::NextFromInput","injection_result":"injected","db_stress_crash_signal":null,
"corruptions":[{"instruction":"mov %rdx,0x160(%r12)","register":"rdx","corruption_type":"bit_flip","before":"0x10","after":"0x18",
"details":{"source":"rocksdb::CompactionIterator::NextFromInput @ db/compaction/compaction_iterator.cc:719"}}]}
```
The recorded silent corruption (`data_corruption.<tid>.json`):
```json
{"kind":"wrong-value","cf":0,"key":70,"value_from_db":"010000000504070609080B0A0D0C0F0E070F105E78787878","value_from_expected":"010000000504070609080B0A0D0C0F0E","op_status":"Get: OK"}
```
**Walkthrough:** a single bit flip on `rdx` (`0x10 -> 0x18`, 16 -> 24) at `CompactionIterator::NextFromInput` (`compaction_iterator.cc:719`) -- `rdx` holds the value LENGTH stored into the iterator's record (offset `0x160`). The length reads 24 instead of 16, so compaction copies a value 8 bytes too long into the output SST, absorbing adjacent bytes. The internal key is untouched (`ParseInternalKey` passes); the over-long value is the single Slice fed to both the paranoid validator and the SST builder, so the file is self-consistent and every checksum agrees. On read-back `Get(key=70)` returns OK with the wrong bytes -- `value_from_db` is the expected value (`...0F0E`) **plus 8 trailing bytes** (`070F105E78787878`). Silent: read OK, all checks pass, the value visibly grew. `classify()` routes `kind=wrong-value` to the SDC bucket.
### A representative compaction CORRUPTION (detected): `run_00007`
What we corrupted (`inject.json`):
```json
{"op":"compaction","op_index":41,"entry_fn":"rocksdb::CompactionJob::Run","target_fn":"rocksdb::CompactionIterator::NextFromInput","injection_result":"injected","db_stress_crash_signal":"SIGABRT",
"corruptions":[{"instruction":"mov (%rbx),%rdi","register":"rdi","corruption_type":"bit_flip","before":"0x7fffeee8c1c0","after":"0x7fffeee8c1c2",
"details":{"source":"rocksdb::IterKey::SetKeyImpl @ ./db/dbformat.h:941","call_chain":["rocksdb::IterKey::SetKeyImpl @ ./db/dbformat.h:941","rocksdb::CompactionIterator::NextFromInput @ db/compaction/compaction_iterator.cc:781"]}}]}
```
The recorded detection (`data_corruption.<tid>.json`):
```json
{"kind":"detected-corruption","cf":-1,"key":-1,"value_from_db":"","value_from_expected":"","op_status":"compactfiles: Corruption: Compaction sees out-of-order keys."}
```
**Walkthrough:** a bit flip on `rdi` (`...c1c0 -> ...c1c2`, a key pointer) at `IterKey::SetKeyImpl` (`dbformat.h:941`), reached from `NextFromInput`, mis-sets the iterator's key so the next emitted key is out of order. Compaction's key-order check catches it and returns `compactfiles: Corruption: Compaction sees out-of-order keys`. The op then takes `SIGABRT`, but `classify()` reads the recorded `data_corruption` result before the crash signal, so the run is correctly bucketed `CORRUPTION` (the bucketization fix; pre-fix this surfaced as `CRASH`). `classify()` routes `kind=detected-corruption` to the `CORRUPTION` bucket.
# 3. Flush op -- 100 runs
```
$ python3 tools/cpu_corruption_injector/runner.py --op flush --runs 100 --stress_cmd /data/users/huixiao/rocksdb/db_stress --report_dir /tmp/icc/preflight_demo
```
**Runs' outcomes (`summary.json`):**
| SDC | CORRUPTION | CRASH | NO_EFFECT | NO_INJECTION | ERROR |
| --- | --- | --- | --- | --- | --- |
| 2 | 12 | 11 | 74 | 1 | 0 |
**Spread:**
- target_fn x outcome: `BlockBuilder::Add` {SDC 1, CORRUPTION 5, CRASH 5, NO_EFFECT 49}; `NextFromInput` {SDC 1, CORRUPTION 7, CRASH 6, NO_EFFECT 25, NO_INJECTION 1}.
- corruption_type x outcome: `bit_flip` {SDC 2, CORRUPTION 9, CRASH 9, NO_EFFECT 32}; `flag_flip` {CORRUPTION 3, CRASH 2, NO_EFFECT 42}.
**Analysis:** flush mirrors compaction's mechanisms (shared iterator/builder). The 2 SDCs are a value/key-pointer corruption that slips past the checksums; the 12 corruptions are caught by the flush-time key-order / key-size integrity checks.
### A representative flush SDC: `run_00027`
What we corrupted (`inject.json`):
```json
{"op":"flush","op_index":16,"entry_fn":"rocksdb::FlushJob::Run","target_fn":"rocksdb::BlockBuilder::Add","injection_result":"injected","db_stress_crash_signal":null,
"corruptions":[{"instruction":"mov (%rdi),%rax","register":"rax","corruption_type":"bit_flip","before":"0x7fffef059400","after":"0x7fffef059440",
"details":{"source":"rocksdb::Slice::data @ ./include/rocksdb/slice.h:58","call_chain":["rocksdb::Slice::data @ ./include/rocksdb/slice.h:58","rocksdb::BlockBuilder::AddWithLastKeyImpl @ table/block_based/block_builder.cc:351"]}}]}
```
The recorded silent corruption (`data_corruption.<tid>.json`):
```json
{"kind":"lost","cf":0,"key":763,"value_from_db":"","value_from_expected":"010000000504070609080B0A0D0C0F0E","op_status":"Get: NotFound"}
```
**Walkthrough:** a bit flip on `rax` (a key/value data pointer, `...9400 -> ...9440`) at `Slice::data` (`slice.h:58`), reached from `BlockBuilder::AddWithLastKeyImpl` while the flush builds the output block, makes the builder read key bytes from the wrong address, so key 763's entry is written wrong and the key is dropped from the flushed SST. On read-back `Get(key=763)` returns `NotFound` for a committed key -- silent. `classify()` routes `kind=lost` to the SDC bucket.
### A representative flush CORRUPTION (detected): `run_00047`
What we corrupted (`inject.json`):
```json
{"op":"flush","op_index":7,"entry_fn":"rocksdb::FlushJob::Run","target_fn":"rocksdb::CompactionIterator::NextFromInput","injection_result":"injected","db_stress_crash_signal":"SIGABRT",
"corruptions":[{"instruction":"cmp $0x7,%rax","register":"eflags","corruption_type":"flag_flip","before":"0x216","after":"0x256",
"details":{"source":"rocksdb::ParseInternalKey @ ./db/dbformat.h:523","call_chain":["rocksdb::ParseInternalKey @ ./db/dbformat.h:523","rocksdb::CompactionIterator::NextFromInput @ db/compaction/compaction_iterator.cc:731"]}}]}
```
The recorded detection (`data_corruption.<tid>.json`):
```json
{"kind":"detected-corruption","cf":-1,"key":-1,"value_from_db":"","value_from_expected":"","op_status":"flush: Corruption: Corrupted Key: Internal Key too small. Size=16. "}
```
**Walkthrough:** a flag flip (`eflags 0x216 -> 0x256`) on a `cmp $0x7,%rax` branch in `ParseInternalKey` (`dbformat.h:523`), reached from `NextFromInput`, makes the parser mis-judge the internal-key size, so the flush emits a malformed key and the key-size integrity check returns `flush: Corruption: Corrupted Key: Internal Key too small`. The op takes `SIGABRT`; `classify()` reads the recorded `data_corruption` before the signal and buckets `CORRUPTION` (bucketization fix). `classify()` routes `kind=detected-corruption` to the `CORRUPTION` bucket.
# 4. Write op (`MemTable::Add`) -- two key spaces
```
$ python3 tools/cpu_corruption_injector/runner.py --op write --runs 100 --stress_cmd /data/users/huixiao/rocksdb/db_stress --report_dir /tmp/icc/preflight_demo
```
A write injection corrupts a single `MemTable::Add` (a Put/Delete/DeleteRange). The corruption is reachable and attributable, but whether it surfaces as a *silent* write SDC depends heavily on the key space. A silent write SDC needs the affected/mispositioned key to have other live versions to fall through to -- which only happens in a dense, multi-version memtable. We therefore run two write campaigns: the default `max_key=1000`, then a small `max_key=8`. The contrast is what motivates randomizing `max_key` (see PR https://github.com/facebook/rocksdb/pull/14867 for `--randomize_stress_flags`).
### 4a. Default `max_key=1000` -- 100 runs (no silent write SDC)
**Runs' outcomes (`summary.json`):**
| SDC | CORRUPTION | CRASH | NO_EFFECT | NO_INJECTION | ERROR |
| --- | --- | --- | --- | --- | --- |
| 0 | 31 | 13 | 56 | 0 | 0 |
With a 1000-key space almost every write touches a distinct key, so a corrupted entry has no older live version to mask it: a value/key byte flip is caught at write by the per-key checksum (`VerifyEncodedEntry` -> `CORRUPTION`), and a structural flip tends to crash (`CRASH`) rather than silently mis-read. `ERROR=0`, `NO_INJECTION=0`. No write op silently corrupted data -- every reachable corruption was caught or crashed.
### 4b. Small `max_key=8` -- 100 runs (surfaces 2 silent write SDCs)
**Runs' outcomes (`summary.json`):**
| SDC | CORRUPTION | CRASH | NO_EFFECT | NO_INJECTION | ERROR |
| --- | --- | --- | --- | --- | --- |
| 2 | 33 | 8 | 57 | 0 | 0 |
Shrinking the key space makes each key hold ~125 versions (`ops_per_thread` / `max_key`), so a misplaced entry can fall through to an older version of the *same* key and be returned silently -- the per-key checksum (bytes intact) and on-seek verify cannot see a pure link-position error.
### A representative write SDC: `run_00028` (skiplist misposition -> silent stale read, flush catches)
What we corrupted (`inject.json`):
```json
{"op":"write","op_index":317,"entry_fn":"rocksdb::MemTable::Add","target_fn":"rocksdb::MemTable::Add","injection_result":"injected","db_stress_crash_signal":null,
"corruptions":[{"instruction":"cmp %rbx,-0xb8(%rbp)","register":"eflags","corruption_type":"flag_flip","before":"0x216","after":"0x217",
"details":{"source":"rocksdb::MemTable::Add @ db/memtable.cc:1319",
"call_chain":["rocksdb::MemTable::Add @ db/memtable.cc:1319"]}}]}
```
The recorded silent corruption (`data_corruption.<tid>.json`):
```json
{"kind":"resurrected","cf":0,"key":1,"value_from_db":"110000001514171619181B1A1D1C1F1E0100030205040706","value_from_expected":"","op_status":"Get: OK"}
```
**Walkthrough:** a flag flip (CF, `eflags 0x216 -> 0x217`) on the `cmp` that produces `KeyIsAfterNode` inside `InlineSkipList::Insert` (`inlineskiplist.h:1253`; the `@ memtable.cc:1319` in the record is inlining line-drift) inverts the key comparison, so the Delete tombstone for key 1 is linked at the wrong position. The stored bytes and per-key checksum are intact, so neither the checksum nor on-seek verify sees anything wrong -- on read-back `Get(key=1)` returns OK with key 1's live value for a key that was Deleted (`kind=resurrected`, silent). A follow-up `Flush()` in unit test repro *does* catch it: the full-scan order check returns `Corruption: Out-of-order keys found in skiplist` -- caught only after the silent read, not during it.
### A representative write CORRUPTION (detected) `max_key=1000 or 8` : `run_00018`
Where `run_00028`'s pure link-position error is invisible to the per-key checksum, this run shows a byte-level corruption that the checksum *catches* at write time. What we corrupted (`inject.json`):
```json
{"op":"write","op_index":106,"entry_fn":"rocksdb::MemTable::Add","target_fn":"rocksdb::MemTable::Add","injection_result":"injected","db_stress_crash_signal":null,
"corruptions":[{"instruction":"mov %rsi,(%rdi)","register":"rsi","corruption_type":"bit_flip","before":"0x7fffeec2a21c","after":"0x7fffeec2a25c",
"details":{"source":"rocksdb::Slice::Slice @ ./include/rocksdb/slice.h:39",
"call_chain":["rocksdb::Slice::Slice @ ./include/rocksdb/slice.h:39","rocksdb::GetVarint32 @ ./util/coding.h:280","rocksdb::MemTable::VerifyEncodedEntry @ db/memtable.cc:1102","rocksdb::MemTable::Add @ db/memtable.cc:1189"]}}]}
```
The recorded detection (`data_corruption.<tid>.json`):
```json
{"kind":"detected-corruption","cf":-1,"key":-1,"value_from_db":"","value_from_expected":"","op_status":"put: Corruption: ProtectionInfo mismatch"}
```
**Walkthrough:** a bit flip on `rsi` (`0x7fffeec2a21c -> 0x7fffeec2a25c`) at `Slice::Slice` (`slice.h:39`) while `MemTable::Add` re-parses the just-encoded entry through `VerifyEncodedEntry` (`memtable.cc:1102`) corrupts the Slice the verifier reads, so the recomputed per-key protection info no longer matches and the put returns `Corruption: ProtectionInfo mismatch`.
Reviewed By: pdillinger
Differential Revision: D108367345
fbshipit-source-id: 250b069c8e9d63a2dc257f6de86f1d9040284dd6
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14858
This PR is the injection layer of the CPU corruption injector, runs inside gdb and randomly corrupts a register by bit flip in exactly one db_stress op (i.e, write, foreground compaction and flush) per stress test run. Detection layer is at db_stress (https://github.com/facebook/rocksdb/pull/14852); orchestration layer is coming up.
__How one run works__
- The orchestration layer, coming up, randomly picks which stress test `op` instance (so corruption can land at different points in the LSM shape journey) and which `target_fn` of that `op` (so to cap instructions to step under a reasonable limit; `injector.py` in this PR randomly picks which instruction within the `target_fn` to inject (so corruption can land at different points of a `target_fn`).
- Attach: gdb starts with injector.py's parameters passed via -iex and the db_stress command after --args, so db_stress runs unmodified. Example:
```
gdb --batch --nx \
-iex "py import sys; sys.argv=['injector.py','--op','write','--op_index','42','--entry_fn','rocksdb::MemTable::Add','--target_fn','rocksdb::MemTable::Add','--corruptions_per_op','1','--seed','7','--dir','<rundir>']" \
-x tools/cpu_corruption_injector/injector.py \
--args <db_stress> --threads=1 --verify_cpu_corruption_dir=<rundir> ...
```
- Navigate: The orchestration layer will pick op_index. `entry_fn` is called exactly once per stress test run's op so the op_index-th op is its op_index-th call. `injector_navigate.py` breaks on `entry_fn` and set a gdb ignore-count of op_index-1 to fast-forward to op_index-th one. It also breaks at the first `target_fn` within that `entry_fn`.
- Warm up: `injector_critical_instruction.py` will choose "critical instruction" (those that move key/value bytes with general-purpose or vector registers or set a branch flag) uniformly within the chosen `target_fn` by the orchestration layer. In order to do that, it needs to approximate how many such instructions within the `target_fn`. Hence we have this warm-up phase. It single-steps the instruction within the first encoutering of `target_fn` to count and draw the critical instruction index, then corrupt that index at a later call.
- Corrupt: on a later call of `target_fn`, `injector_critical_instruction.py` single-step to the m-th critical instruction and bit-flip the register through `injector_register_corruption.py`. The way to corrupt register depends on what instruction it is. If the current call of `target_fn`'s m-th instruction is not a critical instruction, we will try next `target_fn` till running out of `target_fn`.
- Record: `injector_telemetry.py` provides telemetry to capture the corruption for later analysis.
**Test plan:**
1. Isolated tests (real gdb-captured x/i fixtures): test_inject_critical_instruction
2. E2E test on navigation, inject, telemetry will be done in the later orchestration PR. Below is inject.json from such run
```
{
"injection_result": "injected",
"db_stress_crash_signal": null,
"op": "write",
"op_index": 279,
"entry_fn": "rocksdb::MemTable::Add",
"target_fn": "rocksdb::MemTable::Add",
"critical_instruction_index": 37,
"corruptions": [
{
"instruction": "mov %rsi,0x8c8(%rbx)",
"register": "rsi",
"corruption_type": "bit_flip",
"before": "0x7fffee4c64d8",
"after": "0x7fffee4c64c8",
"details": {
"source": "rocksdb::Arena::AllocateAligned @ ./fbcode/internal_repo_rocksdb/repo/memory/arena.cc:135",
"call_chain": [
"rocksdb::Arena::AllocateAligned @ ./fbcode/internal_repo_rocksdb/repo/memory/arena.cc:135",
"rocksdb::ConcurrentArena::AllocateAligned(unsigned long, unsigned long, rocksdb::Logger*)::{lambda()#1}::operator()() const @ fbcode/internal_repo_rocksdb/repo/memory/concurrent_arena.h:65",
"rocksdb::ConcurrentArena::AllocateImpl<rocksdb::ConcurrentArena::AllocateAligned(unsigned long, unsigned long, rocksdb::Logger*)::{lambda()#1}>(unsigned long, bool, rocksdb::ConcurrentArena::AllocateAligned(unsigned long, unsigned long, rocksdb::Logger*)::{lambda()#1} const&) @ fbcode/internal_repo_rocksdb/repo/memory/concurrent_arena.h:145",
"rocksdb::ConcurrentArena::AllocateAligned @ fbcode/internal_repo_rocksdb/repo/memory/concurrent_arena.h:63",
"rocksdb::InlineSkipList<rocksdb::MemTableRep::KeyComparator const&>::AllocateNode @ fbcode/internal_repo_rocksdb/repo/memtable/inlineskiplist.h:868",
"rocksdb::InlineSkipList<rocksdb::MemTableRep::KeyComparator const&>::AllocateKey @ fbcode/internal_repo_rocksdb/repo/memtable/inlineskiplist.h:855",
"rocksdb::(anonymous namespace)::SkipListRep::Allocate @ ./fbcode/internal_repo_rocksdb/repo/memtable/skiplistrep.cc:36",
"rocksdb::MemTable::Add @ ./fbcode/internal_repo_rocksdb/repo/db/memtable.cc:1157"
]
}
}
],
"ops_seen": 279,
"critical_instructions_seen": 38
}
```
Reviewed By: pdillinger
Differential Revision: D107999835
fbshipit-source-id: 0863715e5d596d69abff8b8e4d9a252fa8c24b6c
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14852
Detection layer of the CPU corruption injector (https://github.com/facebook/rocksdb/pull/14858). With `--verify_cpu_corruption_dir=<dir>`, db_stress reads back the full keyspace after every write/manual flush/manual compaction op and compares it to the expected-values model, classifying any mismatch by `kind`: `lost` / `resurrected` / `wrong-value` (silent data corruption) or `detected-corruption` (a status/checksum-caught error). Each finding is written to `<dir>/data_corruption.<tid>.json` ({kind, cf, key, value_from_db, value_from_expected, op_status}) and routed through db_stress's standard `VerificationAbort` for a clean exit-1. A startup guard requires `--threads=1` and all fault injection off so the read-back is single-writer and the only corruption present is the injected one
Bonus: a minor refactoring into the surrounding error handling code in these ops
**Test plan:**
1.Startup guard rejects misconfiguration:
```
--threads=2 -> exit 1: "--verify_cpu_corruption_dir requires --threads=1"
--read_fault_one_in=5 -> exit 1: "requires all fault injection off"
```
2.No false positive (clean CORE preset run, no injection):
```
$ db_stress --verify_cpu_corruption_dir=<dir> --threads=1 (full protections, all *_fault_one_in=0) ...
exit 0; no data_corruption.<tid>.json produced; "Verification successful"
```
3.Write-path cpu corruption injection (coming up, e.g, gdb flips a register inside MemTable::Add), then the immediate post-op read-back catches it. Real `<dir>/data_corruption.<tid>.json`:
silent data corruption -- write returned OK but the key is gone on read-back:
```
{"kind":"lost","cf":0,"key":9814,"value_from_db":"","value_from_expected":"010000000504070609080B0A0D0C0F0E","op_status":"Get: NotFound"}
```
detected corruption -- read-back Get returns Corruption via the memtable per-key checksum:
```
{"kind":"detected-corruption","cf":0,"key":139,"value_from_db":"","value_from_expected":"","op_status":"Get: Corruption: Corrupted memtable entry, per key-value checksum verification failed."
```
4.See PR https://github.com/facebook/rocksdb/pull/14866 test plan's spread in the outcome for verification of detection
Reviewed By: pdillinger
Differential Revision: D107999834
fbshipit-source-id: 18decbf51dc56eec62b735136e2dfb4e1175b773
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14896
Replace the earlier mitigation that disabled test_backward_scan when
separate_key_value_in_data_block is set (D109837793). That gate was incorrect:
separate_key_value_in_data_block does not affect value pinning -- it was only
coincidentally correlated with the failing crash-test runs (the crash test
biases it on ~2/3 of the time).
Root cause: embedded blob SSTs are read through EmbeddedBlobResolvingIterator,
which resolves same-file blob references (and wide-column entities with same-file
blob columns) into an iterator-owned buffer. Those resolved values are not
pinnable, but DBIter requires pinned values for backward iteration
(Prev/SeekForPrev), returning NotSupported (surfaced as "Iterator diverged from
control iterator") or hitting the IsValuePinned() assertion in
FindValueForCurrentKeyUsingSeek (db_iter.cc).
Gate test_backward_scan on ingest_external_file_with_embedded_blobs instead.
This is a stress-test mitigation only; the underlying limitation (backward
iteration over unpinnable resolved values) should be fixed separately by making
the resolved value pinnable via the PinnedIteratorsManager.
___
Differential Revision: D110140135
fbshipit-source-id: 5288dab67ab753cbba22806e09fb8fec1e6f5a01
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14889
When `separate_key_value_in_data_block=true`, the data block iterator stores keys and values in separate sections. Under certain conditions during backward iteration (Prev/SeekForPrev), `DBIter::FindValueForCurrentKey` encounters entries whose values cannot be pinned, causing a `NotSupported` error: "Backward iteration not supported if underlying iterator's value cannot be pinned." This manifests in the crash test as `TestIterateAgainstExpected` failures when the random flag combination includes `separate_key_value_in_data_block=1` (added in 47cfeb656ff3) with `test_backward_scan=1` (the default). The fix automatically disables `test_backward_scan` when `separate_key_value_in_data_block` is enabled, matching the pattern used for other option incompatibilities in the stress tool.
Reviewed By: xingbowang
Differential Revision: D109837793
fbshipit-source-id: 04230e6c0af42e98fb82d0ddcc3d857419e879e3
Summary:
Add rate limiter statistics that make it easier to diagnose whether RocksDB background IO is being throttled by the GenericRateLimiter.
New ticker counters:
- rocksdb.rate.limiter.bytes.read/write: bytes granted by the limiter, split by operation type.
- rocksdb.rate.limiter.requests.read/write: requests granted by the limiter, split by operation type.
- rocksdb.rate.limiter.delayed.requests.read/write: requests that actually had to wait for a future refill because available tokens were exhausted.
- rocksdb.rate.limiter.total.wait.micros.read/write: cumulative wait time for delayed requests.
New histograms:
- rocksdb.rate.limiter.wait.micros.read/write: per-request wait latency for delayed requests, enabling percentile debugging.
GenericRateLimiter now overrides the OpType-aware Request() path so these stats preserve the configured limiter mode. The legacy three-argument Request() path is treated as write IO for compatibility. Existing NUMBER_RATE_LIMITER_DRAINS is left intact, but the new delayed request counters are the clearer signal for out-of-token throttling because one delayed request can observe multiple drain intervals.
Also update the Java ticker and histogram mappings so Java consumers can access the new statistics.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14884
Test Plan:
- make format-auto
- make -j16 rate_limiter_test statistics_test
- ./rate_limiter_test
- ./statistics_test
- make check-sources
Reviewed By: anand1976
Differential Revision: D109694030
Pulled By: xingbowang
fbshipit-source-id: e92756788ba629f25655958c7beb91099184cda4
Summary:
Local Make builds silently reuse object files even when build parameters (DEBUG_LEVEL, sanitizers, ASSERT_STATUS_CHECKED, RTTI, etc.) change, since all object files share the same paths. This leads to confusing linker errors, sanitizer false negatives, ODR violations, and "phantom" bugs that are easy to miss -- a pitfall for humans and especially for AI agents driving builds non-interactively.
This change improves the Make experience on three fronts:
1. Build-parameter change detection in the Makefile. After flags are fully resolved, we hash the effective compile/link inputs (CC|CXX|CFLAGS|CXXFLAGS|LDFLAGS|EXEC_LDFLAGS) into a per-OBJ_DIR .build_signature stamp and compare against the previous build:
- Default: stop with a clear error when parameters changed. This is optimized for the expert who might not intend a full rebuild, and for avoiding CI inefficiency.
- AUTO_CLEAN=1: automatically run clean-rocks and proceed.
- ALLOW_BUILD_PARAMETER_CHANGE=1: skip the check (e.g. intentionally mixing DEBUG_LEVEL=1 and DEBUG_LEVEL=2 objects).
The check is skipped for dry runs (-n) and for non-building / self-cleaning targets (clean*, format, check-*, tags, gen-pc, check-progress, watch-log, release, coverage, the asan_*/ubsan_* variants, etc.). make_config.mk is removed only by the top-level `clean` target, not by clean-rocks, so the auto-clean does not delete the make_config.mk already included by the running make. The signature is removed last in clean-rocks so an interrupted clean keeps change detection meaningful.
3. New build_tools/rockstest.sh and build_tools/rocksptest.sh helpers that build and run unit tests in one step. They set AUTO_CLEAN=1 and build with -j<NCORES> (computed like the Makefile). rocksptest.sh runs one or more binaries under the checked-in gtest-parallel (sharing one parallel worker pool); rockstest.sh runs a single binary directly for a small number of cases. Both reject a leading-option first argument; `rockstest.sh install` writes ~/bin/rockstest and ~/bin/rocksptest shims that defer to the in-tree scripts (with a friendly message when run outside a source root). This is a big win for AI-agent workflows: one command instead of a separate `make` then test-run invocation, removing the overhead of monitoring two long commands, while avoiding serial builds and serial test execution.
4. CLAUDE.md guidance updated to recommend AUTO_CLEAN=1 for manual make invocations and the rocks(p)test.sh helpers for running tests
Although there might be drive to consolidate around the BUCK build for internal use, I'll note that last I checked it was around ~20 minutes to run all the unit tests under buck and ~2 minutes under `make check`.
Bonus: the first revision of this had copyright/license mistakes, and this change corrects similar errors elsewhere, with CLAUDE.md advice updated.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14883
Test Plan:
Manual verification of the Makefile change detection:
- Confirmed the resolved-flags hash is deterministic across runs for the same goal, and differs between configs (e.g. default/shared vs static_lib, and with/without ASSERT_STATUS_CHECKED).
- Unchanged parameters: rebuild does not error.
- Changed parameters: errors by default; AUTO_CLEAN=1 runs clean-rocks and proceeds; ALLOW_BUILD_PARAMETER_CHANGE=1 bypasses the check.
- Dry runs (make -n) and exempt targets (e.g. `make check-progress`, list_all_tests, gen-pc) do not trip the check and leave the stamp untouched.
- Reproduced the ASSERT_STATUS_CHECKED=1 auto-clean path end to end and confirmed it no longer fails with "No rule to make target 'make_config.mk'"; verified `make clean` removes make_config.mk while `clean-rocks` does not, and that the signature is deleted as the final clean-rocks step.
Manual verification of the helper scripts:
- shellcheck-clean and `bash -n` pass for both scripts.
- Usage/guard behavior: missing argument and leading-option first argument are rejected with a clear message.
- `rockstest.sh install` (validated against a temp HOME) generates both shims; running a shim outside a source root prints "(Not in a rocksdb source root directory?)" and exits non-zero.
- rocksptest.sh argument splitting verified for single binary, multiple binaries, and binaries followed by gtest-parallel/test args.
- .gitignore patterns verified with `git check-ignore` for .build_signature (root and jl/jls) and build_tools/__pycache__/.
Reviewed By: xingbowang
Differential Revision: D109713512
Pulled By: pdillinger
fbshipit-source-id: a801d29afa91f53a2dce717db9fe5e5d485cc154
Summary:
### Embedded Blob Resolving Iterator Fix
#### Use-After-Free Bug Fix
This diff fixes a use-after-free bug in the `EmbeddedBlobResolvingIterator` class. The bug occurred when the `key()` method was called before the `value()` method, causing the `resolved_internal_key_` buffer to be freed prematurely.
#### Changes
The fix involves modifying the `EmbeddedBlobResolvingIterator` class to ensure that the `resolved_key` is not moved into `resolved_internal_key_` if it has already been resolved (`key_resolved_` flag is set to `true`). This prevents the `resolved_internal_key_` buffer from being overwritten and freed when `MaterializeValue()` is called.
Reviewed By: xingbowang
Differential Revision: D109796428
fbshipit-source-id: d89fb36428ba62e8d0be53c2acfa3cdb9bda76f4
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14887
This reverts two changes to the range-lock endpoint comparator and restores the prior, direction-agnostic behavior:
- https://github.com/facebook/rocksdb/pull/14831 ("Fix range lock manager crash with reverse comparator"), which made RangeTreeLockManager::CompareDbtEndpoints reverse-aware, and
- https://github.com/facebook/rocksdb/pull/14880 (a follow-up that only corrected the equal-length / point-range branch).
Why: #14831 taught the endpoint comparator about reverse-ordered column families by flipping the suffix tie-break based on comparator direction. However, callers that use range locking on a reverse-ordered CF already flip the start/end endpoints themselves before calling lock_range() -- for example, MyRocks does this in ha_rocksdb::set_range_lock() (see its "RangeFlagsShouldBeFlippedForRevCF" comment). The two corrections stack and produce left > right again, which:
(a) trips paranoid_invariant(compare(left, right) <= 0) in toku::locktree::try_acquire_lock() and aborts on equality/point locks, and
(b) for range scans, mis-orders the endpoints and produces an incorrect lock range, leading to silent correctness issues under concurrency.
#14880 only addressed (a) (the equal-length / point-range branch), not (b), so it was an incomplete fix at the wrong layer.
Decision: keep CompareDbtEndpoints direction-agnostic and make it an explicit caller requirement to pass a flipped (swapped start/end, with negated infimum/supremum flags) range for range locking to work on a reverse-ordered column family. A function-level comment documenting this contract is added so reverse-awareness is not reintroduced here.
Removes the RangeLockWithReverseComparator unit test added by #14831 (it asserts the reverted direction-flipped ordering). Keeps a regression test that a point range lock [infimum(K), supremum(K)] works on a reverse-comparator column family.
Reviewed By: xingbowang
Differential Revision: D109713989
fbshipit-source-id: 4ea0999c92645e1adc9cd1114d924cad142f2001
Summary:
Refresh the fbcode platform010 toolchain and library pins to current third-party2 versions, and remove the now-unused Intel TBB dependency.
Toolchain / deps:
- Fix update_dependencies.sh: the gcc and binutils trees moved from centos8-native to centos9-native, so the old paths resolved to nothing and a re-run emitted empty GCC_BASE/BINUTILS_BASE. Point them at centos9-native.
- Bump clang 15 -> 21; binutils 2.37 -> 2.43; libunwind 1.4 -> 1.8; valgrind 3.19 -> 3.22; plus hash-only refreshes for the LATEST-tracked libs. Regenerate dependencies_platform010.sh.
- Keep GCC pinned at 11.x. The only newer GCC in third-party2 (13.x) is built for glibc >= 2.35 (its libgcc_s needs _dl_find_object@GLIBC_2.35), but platform010 ships glibc 2.34, so GCC 13 will not link/run here.
- zlib stays 1.2.8 (the only version with an x86_64 platform010 build).
- Document why GCC/libgcc/glibc/zlib remain pinned in update_dependencies.sh.
Remove TBB (no longer used) from the build system: the get_lib_base entry, both fbcode_config*.sh, build_detect_platform detection, the CMake WITH_TBB option / find_dependency, and cmake/modules/FindTBB.cmake. Dockerfiles and the HISTORY.md changelog are left untouched. (TBB was used by the old clock cache, long ago removed.)
Although this change was originally motivated by upgrading gcc for its libasan not to hit process lifetime thread limits, upgrading gcc proved impractical under platform010.
Bonus: fix USBAN+gcc build by making GetParam() valid by the time it is called in several test class constructors.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14882
Test Plan:
a variety of local builds (gcc, clang; various sanitizers) using fbcode tooling
No production code changes
Reviewed By: xingbowang
Differential Revision: D109631787
Pulled By: pdillinger
fbshipit-source-id: 9c466c59b039c2a67ee0318c0ccbac02e19f537b
Summary:
Add EXPERIMENTAL embedded blob SST support for SstFileWriter through
OpenWithEmbeddedBlobs(). Eligible large values are written as same-file blob
records inline in a block-based SST as values are added (interleaved with data
blocks), while table entries store same-file BlobIndex references that readers
resolve for Get, MultiGet, and iteration, including mixed embedded and
non-embedded wide-column values.
Embedded-blob handling is folded directly into BlockBasedTableBuilder rather than
living in SstFileWriter or a separate table-builder wrapper: SstFileWriter only
selects the mode (via TableBuilderOptions::embedded_blob_options), and the
builder writes blob records inline using its own file writer and running offset.
This is enabled by disabling index-value delta encoding for these SSTs — delta
encoding reconstructs a data block's offset from the previous block and so
requires byte-contiguous data blocks, which interleaved blob records would break.
With full (non-delta) index values, blob records can sit between data blocks, so
no entry buffering/replay is needed. To keep inline blob appends correctly
ordered with data-block writes, these SSTs use single-threaded (non-parallel)
compression. The mode is the only entry point today but the placement keeps it
open to generalization beyond SstFileWriter; regardless, this experimental
feature is expected only to have niche applications.
(Previous revisions of this change allowed delta-encoded index blocks by
putting all blobs at the beginning of the file, but that was a more awkward
and memory-hungry implementation due to buffering all the data blocks before
writing.)
The on-disk record format (SimpleGen2Blob: payload bytes followed by a 5-byte
trailer of a compression marker plus a builtin checksum that is context-modified
by the record's absolute file offset) lives in db/blob/blob_gen2_format.{h,cc},
which now owns both the read (ReadAndVerifySimpleGen2BlobRecord) and write
(WriteSimpleGen2BlobRecord) sides so the format is defined in one place. This is
expected to be reused for upcoming "blog file" support.
Readers need no record-layout metadata: same-file blob resolution is purely
absolute-offset keyed, and the per-record offset-modified checksum (plus a cheap
"record fits within the file" bound) is the corruption guard. The reader's only
embedded-blob metadata is presence: a best-effort auxiliary table property
(blob count and payload-byte totals, for diagnostics) whose mere presence signals
that the SST contains embedded blobs.
Reads route through the column family's BlobSource when a DB is attached, so
embedded payloads are served from / inserted into the blob value cache and
recorded in BLOB_DB_* statistics; the cache key is derived from the
SimpleGen2Blob offset scheme (the same scheme block-based SST blocks use), so
embedded blob records stay collision-free with data blocks even when the blob
cache and block cache are the same cache. Non-DB openers (SstFileReader,
sst_dump, repair, ingestion prevalidation) have no BlobSource and fall back to a
direct, uncached read.
Same-file BlobIndex references use blob file number 0 as the marker. That value
also serves as the invalid blob-file-number sentinel in broader metadata code,
but the meanings do not conflict when used carefully: only the embedded-SST
reader/writer path interprets 0 as same-file, while generic file-metadata paths
continue to reject it as invalid. Using 1 would be worse because legacy
"stackable" BlobDB can use low blob file numbers, including 1, so reserving it
would collide with real blob files.
Compression options remain in the public API as placeholders, but embedded blob
compression support is deferred. Integrating compression with
BlockBasedTableBuilder while avoiding copied CompressAndVerifyBlock-style logic
is tricky enough to deserve a separate, focused PR.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14851
Test Plan:
- Added basic feature coverage to the crash test.
- Added BlobIndexTest.SameFileBlobIndex and BlobGarbageMeterTest.SameFileBlobIndex
coverage for same-file BlobIndex encoding, display, recognition, and ignoring
same-file references in blob-garbage accounting.
- Extended FileMetaDataTest.UpdateBoundariesBlobIndex to preserve the generic
zero-file-number corruption check while keeping same-file embedded blob
semantics at the table-reader/writer layers.
- SstFileReader embedded blob coverage: round-trip Get, MultiGet, and iterator
reads; format_version gating; ignored placeholder compression options; the
2048-byte default min_blob_size; wide-column mixed embedded/non-embedded
values; and early append-error surfacing.
- Added an interleaved-layout test (small block_size with alternating
small/large values) asserting the SST property index_value_is_delta_encoded==0,
more than one data block, and that blob records are interspersed with data
blocks (not a strict front prefix), with all values read back correctly via
Get and iteration; replaces the old "ignored bytes before/after the blob record
prefix" test.
- Added an embedded-record corruption test: flipping a byte inside a blob
record's payload yields Corruption on read with verify_checksums (the
offset-keyed record checksum is the guard now that the range pre-check is gone).
- Exercised the cached read path through BlobSource, including blob-cache
hit/miss behavior and the shared blob_cache == block_cache configuration, in
db_blob_index_test.
- Normal-path CPU regression check: release (DEBUG_LEVEL=0) db_bench on a
non-embedded DB comparing this change vs upstream main, 3 interleaved reps of
fillseq, fillrandom, readrandom, and readseq (5M keys, value_size=100,
compression none, DB on /dev/shm). All deltas were within run-to-run noise
(~1%), i.e. no measurable regression from adding the embedded-mode branch to
the builder hot path.
Reviewed By: xingbowang
Differential Revision: D108564468
Pulled By: pdillinger
fbshipit-source-id: 5f01ffb1d40c6fd5b82d2451ec3342abb5040ca6
Summary:
- Current 3rd party jdk8 installation fails due to trust issue in homebrew.
- Detect and reuse an existing Jdk installation on macOS runners before installing a new cask.
- Still target on jdk 8 binary build.
- Stop using 3rd party jdk installation,
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14885
Test Plan: - CI
Reviewed By: jaykorean
Differential Revision: D109696828
Pulled By: xingbowang
fbshipit-source-id: cc71958a05bd58acaac5f05fe3349ffd3e57880f
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14881
Read-only DB instances can share a directory with a live writer, so their view of live files is only a snapshot from the time they opened. After read-only DB open began setting `opened_successfully_` for its intended open-success semantics, that state had an unintended side effect: the common close path treated a successful read-only open like a read-write open and ran obsolete-file cleanup. If the writer created or made files live after the read-only handle opened, that cleanup could use the stale read-only live set and delete files still needed by the writer.
This change records whether a `DBImpl` was opened read-only and skips close-time obsolete-file cleanup for read-only DBs instead of simply keeping `opened_successfully_=false`, which could be confusing and easily mistaken in the future again.
For completeness I also updated other callsites of `opened_successfully_`, but those should not be real bugs as ReadOnly DBs do not run flushes/compactions or write to WALs.
Reviewed By: xingbowang
Differential Revision: D109622445
fbshipit-source-id: d7be4b20fce86ccb218a63ac6f5b707b316aac79
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14880
The RocksDB 11.5.0 reverse-comparator range-lock fix (the HISTORY.md entry "Fixed a bug where the range lock manager would crash with an assertion failure when using a reverse comparator column family") made `RangeTreeLockManager::CompareDbtEndpoints` reverse-aware by wrapping the endpoint-suffix tie-break results in an `is_reverse` flip. The length-disparity (different user key) branches need that flip, but the equal-length / equal-user-key branch was flipped too, and that is incorrect.
Range-lock endpoints are encoded as `[suffix_byte][user_key]` where the suffix is `SUFFIX_INFIMUM` (0x0) or `SUFFIX_SUPREMUM` (0x1). When two endpoints share the same user key and differ only in the suffix byte, the suffix is a positional boundary marker for that key, not key content, so the infimum endpoint must sort before the supremum endpoint in any total order, regardless of the column family sort direction. Flipping it for a reverse comparator makes `m_cmp(infimum(K), supremum(K)) > 0`.
MyRocks issues an equality lock (e.g. `UPDATE t1 SET a=123 WHERE a=35`) as the range `[infimum(K), supremum(K)]` over the same user key K. On a reverse (`rev:`) column family — which uses the stock `ReverseBytewiseComparator()`, so `is_reverse` is true — the flipped comparator reports `left > right`, tripping `paranoid_invariant(m_cmp(left_key, right_key) <= 0)` in `toku::locktree::try_acquire_lock` and aborting the server with SIGABRT. This broke many MyRocks `---range_locking` MTR tests (partial_index*, bypass_select_range_sk*, alter_table_online_debug, allow_no_primary_key_with_sk) after RocksDB was bumped to 11.5.0 in D108936924.
Fix: in the equal-length / equal-user-key branch of `CompareDbtEndpoints`, compare the suffix bytes without the `is_reverse` flip so infimum always precedes supremum. The length-disparity branches keep the reverse-aware flip (still covered by `RangeLockWithReverseComparator`).
Note: this lands in `internal_repo_rocksdb/repo`, the RocksDB development tree; it reaches `fbcode/rocksdb/src` (what MyRocks/ZippyDB link) through the RocksDB release sync. Tracked in T276980728.
Reviewed By: xingbowang
Differential Revision: D109591908
fbshipit-source-id: 078b16e6e51f4f365b4a8d539ac5b968a7461729
Summary:
This continues and finishes **https://github.com/facebook/rocksdb/issues/14572** ("Add semi-automated code generation for RocksDB C API bindings") by xingbowang. The original author is unavailable to finish it, so I've taken it over. **All 13 of the original commits are preserved** (this branch was created from the PR head and builds on top of it — `git log` shows the original `Xingbo Wang` authorship intact); my follow-up work is in the commits prefixed `C API codegen:`.
The underlying design is unchanged and is the original author's: hand-written source templates (`tools/c_api_gen/c_base.h` / `c_base.cc`) plus two generators (auto-discovery from the C++ headers + a spec-driven generator) are inlined into a single, self-contained, `generated` `include/rocksdb/c.h` and `db/c.cc`. This grows the public C API by **668 functions** while keeping `c.h` a single includable header with no `-I` requirement (so `bindgen` and other FFI tools keep working unchanged).
This branch reconciles the PR with ~4 months of `main` and addresses the outstanding review feedback (clang-tidy bot, the automated code review, the `c.h` self-containedness discussion, and pdillinger's points about `include/rocksdb` hygiene and `generated` marking).
## What changed on top of the original PR
### Reconciled with current `main`
- Merged current `main` (conflicts were confined to the generated/test files) and regenerated. Reconciled the 14 C API functions `main` added since the merge-base (e.g. `rocksdb_set_db_options`, the backup-engine rate limiters, `memtable_batch_lookup_optimization`, `optimize_multiget_for_io`, …) and restored 5 enum constants that upstream had added by hand (`rocksdb_txndb_write_policy_*`, `..._index_block_search_type_auto`, `rocksdb_blob_cache_read_byte`).
### Maintainer feedback (pdillinger)
- **No non-user-includable files in `include/rocksdb`.** Moved the hand-written templates out of `include/rocksdb/` and `db/` to `tools/c_api_gen/c_base.{h,cc}`. They were `#include`-ing generated fragments, which broke `make check-headers` and was shipped by `make install`. `include/rocksdb/` now contains only the user-facing, self-contained, `generated` `c.h`.
- `c.h` / `c.cc` carry the `// generated` marker.
### Backward compatibility (zero ABI break)
- The generator derived each wrapper's C type purely from the C++ field, which had silently changed **5 already-shipped signatures** (e.g. `rocksdb_writeoptions_disable_WAL` `int` → `unsigned char`). Added an ABI type-pinning layer (`tools/c_api_gen/abi_type_overrides.json`) so already-shipped functions keep their historical C signature (the body still casts to the real field type). A repo-wide diff against the merge-base now reports **0 ABI drift**.
- New `check_api_compatibility.py` gate (wired into CI + `make`) fails on any removed/changed public function **or** removed enum/typedef symbol, vs a reference revision. Intentional changes go in an allowlist with a reason.
### Correctness (from the automated review)
- Restored 5 option setters that were declared in `c.h` but **defined nowhere** (link failure for downstream bindings such as `rust-rocksdb`). Added `check_api_completeness.py` (dependency-free; runs in CI + `make`) asserting every declared function has exactly one definition — this is the gate that would have caught it.
- `CopyStringVector` now null-checks `malloc`; the WAL filter `std::move`s the `WriteBatch`; the backup exclude-files callback captures by value instead of the wrapper pointer.
### Build / CI robustness
- Removed the dead `C_API_CODEGEN_STAMP` Makefile prerequisite (it was a silent no-op).
- The `make check` staleness check is now opt-out-able (behind `SKIP_FORMAT_BUCK_CHECKS`) and skips gracefully when `clang++` is unavailable, so `make check` works without the codegen toolchain. CI remains the authoritative gate.
- Pinned `clang-format` consistently through `regen_all.py` / `verify_generated_up_to_date.py` (CI uses clang-format-21) so regeneration is byte-reproducible across environments.
- Cleared all 20 `clang-tidy` warnings the bot reported on `db/c.cc` changed lines (fixed in the `c_base.cc` template, not the generated output).
- Updated the internal Buck `c_test_bin` wrapper to expose generated `c_api_gen/*.inc` fragments as headers, so sandboxed Buck builds can compile `db/c_test.c` after the generated round-trip tests are included.
### Test coverage
- Added `gen_roundtrip_tests.py`, which derives **462 set→get→assert round-trip checks across 25 option objects** from the same generated fragments and wires them into `db/c_test.c`. Coverage now tracks the generated surface automatically.
### Docs
- Added the `unreleased_history/public_api_changes` note and fixed `claude_md/add_public_api.md`, which still told contributors to hand-edit the now-`generated` `c.h`/`c.cc`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14868
Test Plan:
- `make c_test && ./c_test` — **passes**, including the 462 generated round-trip assertions (a successful link also confirms the API is complete).
- `python3 tools/c_api_gen/check_api_completeness.py` — all 1737 declared functions defined exactly once.
- `python3 tools/c_api_gen/check_api_compatibility.py --ref <release>` — 1070 reference functions + 229 enum/typedef symbols preserved, 0 removed/changed.
- `python3 tools/c_api_gen/verify_generated_up_to_date.py` — generated output is stable.
- `include/rocksdb/c.h` confirmed self-contained (only `<stdbool.h>`, `<stddef.h>`, `<stdint.h>`).
cc xingbowang
- `buck2 build --flagfile fbcode//mode/dev fbcode//internal_repo_rocksdb/repo:c_test_bin` — passes.
- `buck2 build --flagfile fbcode//mode/dev --config fbcode.arch=aarch64 fbcode//internal_repo_rocksdb/repo:c_test_bin` — passes.
Reviewed By: pdillinger
Differential Revision: D109149150
Pulled By: xingbowang
fbshipit-source-id: 3417375345f360a4c78bdfe27e9850b89d0a226a
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14877
The rollback-after-failed-commit retry loop in `db_stress_test_base.cc` (added in b0b52e1d14f3) counted every loop iteration — including iterations where `Resume()` returned Busy — against a single 100-count limit. With 32 concurrent threads, when write fault injection causes all prepared transaction commits and rollbacks to fail simultaneously, `Resume()` returns Busy (recovery in progress from another thread) for the entire 100-iteration budget (100 × 10ms = 1 second). Recovery itself keeps failing due to ongoing fault injection causing repeated background flush errors, so no thread ever gets a chance to perform an actual rollback.
The fix separates two budgets:
- `kMaxRollbackRetries` (100): counts only actual rollback attempts after `Resume()` returns OK
- `kMaxResumeBusyWaits` (3000): allows up to 30 seconds of busy-waiting for recovery to complete on another thread
Key triggering options: `write_fault_one_in=1000` + `txn_write_policy=1` (write-prepared) + `two_write_queues=1` + 32 threads + `error_recovery_with_no_fault_injection=1`.
Reviewed By: joshkang97
Differential Revision: D108561314
fbshipit-source-id: 03f4578ea33289685ad982208451f0584bf0535f
Summary:
- Add a blog post explaining FIFO KV-ratio compaction for BlobDB-backed TTL workloads.
- Describe why the existing FIFO intra-L0 picker can repeatedly compact small BlobDB SST files and how the new KV-ratio target plus tiered merging strategy works.
- Add diagrams for the old picker behavior and the KV-ratio tiering flow, along with the required blog author metadata.
## Testing
- `git diff --check upstream/main...HEAD`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14871
Reviewed By: joshkang97
Differential Revision: D109243987
Pulled By: xingbowang
fbshipit-source-id: 7ad07953f38c55c77ffb6510824ba8500db78c0e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14874
### Problem
RocksDB [alert](https://www.internalfb.com/monitoring/alert/?alert_id=twshared38314.04.ldc2%40%23%24host_health.crashlooping_key.tw_agent.tw_agent.task_failure.app.count.60%40%23%24uhc%3A%20tupperware%20task%20crashlooping) fired for `tsp_ldc/RocksDB/rocks_on_ws_stress_test` after the job started crashlooping with `ModuleNotFoundError: No module named fault_injection_log_parser`.
### RCA
D101973626 added `import fault_injection_log_parser` to `db_crashtest.py`, but `rocksdb_db_stress_internal_repo` only packaged `crashtest.py` and `rocks_db_stress`, so the `release_test` fbpkg rolled to TW without the new helper module.
### Fix
Export `tools/fault_injection_log_parser.py` and include it in the `rocksdb_db_stress_internal_repo` fbpkg `path_actions` so it is installed next to `crashtest.py`; also add the BUCKLINT-required `network_access = network_access_utils.none()` on the existing `db_stress_auto_tuner_test` target.
Reviewed By: xingbowang
Differential Revision: D109346224
fbshipit-source-id: 3f770a7ad306d246f279641bf0f19142d05560c4
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14862
Add blog post for range tombstone conversion.
Reviewed By: xingbowang
Differential Revision: D108946953
fbshipit-source-id: f2ded24a5452d2f56a12928b979a8f44930dde03
Summary:
Update Windows CI to stop hardcoding Visual Studio 2022. The Windows build action now detects the installed Visual Studio C++ toolchain with vswhere, derives the matching CMake generator, and passes the corresponding version range to microsoft/setup-msbuild@v2.
Also update the Windows build steps to use the preinstalled JDK instead of installing Liberica JDK8 through Chocolatey, and build Snappy/RocksDB through cmake --build, instead of assuming fixed .sln names. Rename Windows CI jobs from VS2022-specific names to MSVC-neutral names, and move the nightly AVX2 Windows job to windows-8-core so PR and nightly CI use the same runner/toolchain path.
## Testing
- CI
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14869
Reviewed By: jaykorean
Differential Revision: D109176291
Pulled By: xingbowang
fbshipit-source-id: 848c5964922cf6e09b727b1ed96cbf475b325142
Summary:
# Summary
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14864
With `two_write_queues`=`true`, `WRITE_UNPREPARED` can allocate sequence numbers through `FetchAddLastAllocatedSequence()` before those numbers are published through `SetLastSequence()`. Error recovery can then create new memtable/WAL boundaries from a stale `LastSequence()`, which can later surface as "sequence number going backwards" corruption. Fix this by syncing `LastSequence()` to `LastAllocatedSequence()` at the recovery entry point after draining both write queues, and again at the recovery flush fence after `FlushAllColumnFamilies()` has released/reacquired the DB mutex and before `SwitchMemtable()` consumes `LastSequence()`. Apply the same fence to atomic recovery flushes.
# Perf comparative results
TLDR; no visible regression.
I ran a deterministic local recovery benchmark to check whether the new two-write-queue recovery fence adds measurable recovery latency.
## Workload (P2385173320)
- Optimized build.
- `TransactionDB` with `WRITE_UNPREPARED`.
- `two_write_queues=true`.
- Each iteration dirties the memtable with WUP transactions, forces a retryable flush IO error through `FaultInjectionTestFS`, re-enables the filesystem, then measures `DB::Resume()`.
- 50 warmup recoveries + 500 measured recoveries per run.
## Why this is relevant
This targets the changed recovery path directly rather than measuring generic write throughput. The change only runs during recovery, so the useful signal is recovery latency around `ResumeImpl()` and the recovery flush fence.
## Code path exercised
- `DB::Resume()`
- `ErrorHandler::RecoverFromBGError(true)`
- `DBImpl::ResumeImpl()`
- new `two_write_queues_` recovery queue fence
- non-atomic recovery `FlushMemTable()` fence before `SwitchMemtable()`
This does not cover atomic flush recovery or a writer-backlog stress case.
Comparative results:
| Revision | Attempt | resume_p50_us | resume_p95_us | Retryable BG errors |
| Parent | | 1555.75 | 2092.22 | 550 |
| Fix | 1 | 1537.68 | 1905.65 | 550 |
| Fix | 2 | 1475.10 | 1883.26 | 550 |
| Fix | 3 | 1617.52 | 1993.97 | 550 |
Reviewed By: xingbowang, anand1976
Differential Revision: D108946310
fbshipit-source-id: 75fcb4bf0c9a932b5fbed7e934c7f14d981a1bf2
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14872
### Motivation
The `build-windows-vs2022` job in `pr-jobs.yml` was running on the `windows-8-core` runner, whose image no longer provides a Visual Studio 2022 installation. The job sets `CMAKE_GENERATOR: Visual Studio 17 2022`, so CMake's generator probes for an installed VS 2022 instance and aborts the Snappy dependency configure with:
```
CMake Error: Generator Visual Studio 17 2022 could not find any instance of Visual Studio.
```
The failure surfaces during the thirdparty Snappy build, before any RocksDB code is compiled, so it blocks the entire Windows PR signal.
### Changes
Switch the `build-windows-vs2022` job from `runs-on: windows-8-core` to `runs-on: windows-2022`. The GitHub-hosted `windows-2022` image ships VS 2022 with the Desktop C++ workload preinstalled, so the `Visual Studio 17 2022` generator resolves out of the box and the `CMAKE_GENERATOR` value stays consistent with the runner's installed toolchain. This also brings `pr-jobs.yml` in line with `nightly.yml`, whose `build-windows-vs2022-avx2` job already runs on `windows-2022`.
NOTE: `windows-2022` is the standard 2-core hosted runner (vs. the larger `windows-8-core` pool), so Windows builds will be somewhat slower; ccache keeps the compile cost mostly in cache hits.
Reviewed By: pdillinger
Differential Revision: D109229258
fbshipit-source-id: b9f6d9ef8b4080b1b1129ed0ae6a9b1da2b7b14c
Summary:
- switch fault injection error recording from an in-memory ring buffer to per-run fixed-record binary logs under `TEST_TMPDIR/fault_injection_logs` (or `/tmp/fault_injection_logs`) so crash paths survive DB reopen cleanup
- keep the raw and decoded fault logs for external artifact collection/cleanup, and make `db_crashtest` print consistent blackbox/whitebox summaries after decoding
- make expected-state tracing fail fast on trace write failures and document offline trace inspection via `trace_analyzer`
- add coverage for binary log persistence/decoding/truncated-tail handling and keep info logs excluded from fault injection
Reviewed By: hx235
Differential Revision: D101973626
fbshipit-source-id: fdcb5b6370cf92a046e09b8d3391e80eecb66c23
Summary:
db_bench worker operations historically responded to a fatal error (failed Get/Put/Merge/Delete, checksum failure, etc.) by calling db_bench_exit() -> exit() in place. When that happened on a worker thread while sibling workers and background (compaction/flush) threads were still running, exit() began static/global destruction underneath those live threads. A thread mid-call into a user callback (e.g. a custom Env/ FileSystem) could then touch a global/singleton that had already been destroyed -> cross-thread use-after-free. This is the same class of bug fixed for db_stress in https://github.com/facebook/rocksdb/issues/14850, but it also matters for db_bench's secondary role as an *integration validation* tool: we want production-like processes to shut down cleanly after an error (so a custom FileSystem can log the adverse condition and run its teardown) rather than crash or _exit at the first sign of trouble.
This change makes fatal errors on benchmark worker threads unwind instead of exiting in place, and performs an ordered shutdown on the main thread:
* SharedState gains a cooperative fatal flag + first-error Status/message and SetFatal(). A worker that hits a fatal error records it and returns out of its operation (releasing its DbUseGuard) instead of exiting.
* Op loops stop promptly via Duration: the loop-termination helper now consults the run's fatal flag at its existing throttled check boundary (~every FLAGS_ops_between_duration_checks ops), so peers wind down with no added per-key cost.
* After RunBenchmark joins all workers, the (guard-free) main thread runs the ordered shutdown -- DeleteDBs() closes all DBs, joining background threads and running user Env/FileSystem teardown -- and only then exits nonzero. No static destructors run while RocksDB threads or user callbacks are still live.
* The worker fatal sites that previously called db_bench_exit() (BGWriter, DoDelete, RandomWithVerify, UpdateRandom, XORUpdateRandom, MergeRandom, ReadRandomMergeRandom, VerifyChecksum, VerifyFileChecksums, RandomReplaceKeys, Replay, DoDeterministicCompact) now SetFatal + return. Flush (main-thread) routes to ErrorExit() which closes DBs first.
Supporting changes:
* Duration is now a nested member type of SharedState, constructible only via SharedState::MakeDuration() (movable, non-copyable). This binds every Duration to its run's fatal flag by construction and removes the previous mutable global (g_cooperative_abort_flag) and its set/clear lifecycle. A `using Duration = SharedState::Duration;` alias keeps call sites tidy.
* New test-only flag --fault_injection_read_after_reads: wraps the FileSystem so random-access reads start returning a non-retryable IOError after N reads, to exercise the fatal/shutdown path on demand.
* New flag --fatal_shutdown_watchdog_seconds (default 180, 0 disables): arms a one-shot detached watchdog on the first fatal error (and before ErrorExit's cleanup) that forces port::ImmediateExit() if the careful shutdown itself hangs (e.g. a background thread or misbehaving Env that never returns). This is a backstop, not the path under test.
* Added an "Error handling background" comment documenting the rationale and the machinery, and an invariant comment at db_bench_exit covering pre-Open vs worker vs main-thread exit rules.
Existing _Exit()/abort() sites are intentionally left unchanged.
Future work:
* Worker ops that still abort() on error (ReadRandom, ReadRandomFast, ReadToRowCache, MultiReadRandom, MixGraph, AppendRandom, UpdateRandom's read path, BGScan, RandomTransaction, CreateNewCf) are not the use-after-free class (abort() skips static destruction) but are not graceful either; converting them would extend clean-shutdown coverage. RandomTransaction needs a rollback decision; MultiReadRandom needs a structured break-out.
* Post-Open main-thread db_bench_exit() sites in Open()/Run()/ VerifyDBFromDB() are the harder, https://github.com/facebook/rocksdb/issues/14850-class case: Open() is reachable both under and outside a DbStateMutationGuard, so it cannot unconditionally call ErrorExit() without deadlock. Needs guard-aware handling or restructuring Open() to return Status.
* Pre-Open setup exits (option/flag parsing, cache setup) correctly stay plain exit() -- no live threads -- and are out of scope.
* Fault injection currently covers read faults only; verifying graceful shutdown for write/open/transaction error paths would need write-fault, open-fault, and conflict injection.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14855
Test Plan:
Built debug db_bench (make db_bench) and validated manually:
* Correctness of loop termination (unchanged by the Duration refactor):
- Count-bounded is exact: `db_bench --benchmarks=fillrandom --num=300` reports "300 operations".
- Time-bounded honors duration and is not capped by max_ops: `db_bench --benchmarks=readrandom --use_existing_db --duration=5 --reads=3000` ran 5.002 s / ~2.0-2.7M operations.
* Graceful shutdown on a fatal error (multi-threaded, peers live): `db_bench --benchmarks=xorupdaterandom --use_existing_db --threads=8 --duration=30 --fault_injection_read_after_reads=5000` -> exit code 1, ZERO "Received signal", prints 'Fatal error during "xorupdaterandom"' after the ordered shutdown.
* Confirmed this is a real fix, not just masking: rebuilt pristine main + only the fault-injection flag (graceful-shutdown changes reverted) and ran the same command -> SIGSEGV (rc=139), crash stack in __run_exit_handlers -> jemalloc free, triggered from XORUpdateRandom -> db_bench_exit() -> exit() with sibling threads still live. With the fix in place the same workload is graceful (above).
* Watchdog:
- Default (180 s): graceful runs above complete with no watchdog message (no spurious firing).
- --fatal_shutdown_watchdog_seconds=0 disables it (still graceful).
- Positive: temporarily inserted a 60 s sleep before DeleteDBs in the fatal path, ran with --fatal_shutdown_watchdog_seconds=3 -> process exited at ~3 s (not 60 s) with the watchdog message and rc=1; reverted the temporary sleep and rebuilt clean.
* No benchmark performance impact (normal, error-free runs):
- Structural: the cooperative-abort check adds no per-key work. It is evaluated only at Duration's pre-existing throttled boundary (~every FLAGS_ops_between_duration_checks ops, same site as the existing time check); there is no atomic load on the per-op hot path. Fatal recording (SetFatal) lives only in cold error branches. A Duration is constructed once per benchmark via the factory (returned by value, elided), not per op.
- Observed: error-free fillrandom / fillseq / readrandom throughput (e.g. fillrandom ~300K ops/s, fillseq ~800K-870K ops/s) was in line with pre-change runs; no regression observed.
* make format-auto applied; debug build is clean.
Reviewed By: anand1976
Differential Revision: D108690137
Pulled By: pdillinger
fbshipit-source-id: 7ffdf4abc665b4c2172b718670c5c2fa1da58500
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14854
External SST ingestion can already skip the open-and-scan metadata path when ingesting files produced by `SstFileWriter`. This extends that fast path to live SST files produced by a DB, so applications that move DB-generated files between DBs can request prepared metadata directly from the source DB and pass it through ingestion.
The API is intentionally limited to live table files whose metadata is already available from the DB and cached table reader. Callers remain responsible for keeping the source file alive and unchanged until ingestion completes.
Reviewed By: xingbowang
Differential Revision: D108440584
fbshipit-source-id: e732278ea194aac71f046b54234238b35c6fe0e5
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14853
`IngestExternalFileOptions` now exposes `file_opening_threads` so external file ingestion can open table readers for newly committed SSTs in parallel. The requested thread count is carried from each ingestion job into commit and then into manifest `LogAndApply`, where `VersionBuilder::LoadTableHandlers()` uses it while installing the ingested files.
This is intended for workloads that ingest many external SSTs in one call, where serial table-reader opens can dominate commit latency. The ingestexternalfile benchmark now has a flag for this option, and `db_stress` now creates multiple data SSTs per ingest operation so stress runs cover multi-file ingestion, file-info ingestion, and the parallel file-opening path together.
Benchmarks
Ran `db_bench --benchmarks=ingestexternalfile --use_existing_db=0 --num=2200000 --compression_type=none --statistics --use_direct_reads=true --ingest_external_file_batch_size=200 --ingest_external_file_num_batches=1 --ingest_external_file_use_file_info=true --ingest_external_file_fill_cache=false` while varying `--ingest_external_file_file_opening_threads`. The generated SST files were about 256 MiB each.
| `file_opening_threads` | `rocksdb.ingest.external.file.prepare.micros` | `rocksdb.ingest.external.file.run.micros` | File opens |
| 1 | 16,150 us | 327,136 us | 200 |
| 32 | 16,665 us | 38,349 us | 200 |
At the same ~256 MiB SST size, 2,000 files is roughly 500 GiB of SST data. A straight 10x extrapolation of the 200-file ingest histograms puts prepare+run at about 3.43s with `file_opening_threads=1` and 0.55s with `file_opening_threads=32`; run alone is about 3.27s vs. 0.38s.
Reviewed By: pdillinger
Differential Revision: D108347952
fbshipit-source-id: 49efa48524df64cace010b881030f63f771cbb95
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14837
External file ingestion (`DB::IngestExternalFile[s]`) re-opens every SST file and scans it -- footer, properties, index, filter, and the first/last data blocks -- to recompute the boundary keys, sequence-number bounds, and table properties before committing. For cold, I/O-bound files this scan dominates ingest latency, even when the file is moved/linked rather than copied.
This change lets a caller skip that work when it already has the file's metadata. `SstFileWriter::Finish()` now returns a `PreparedFileInfo` with the file size, table properties, and prepared internal `smallest`/`largest` bounds, and a new `IngestExternalFileArg::file_infos` field carries one `PreparedFileInfo` per file into `IngestExternalFiles()`. When set, ingestion reuses that metadata instead of re-opening and scanning each file. The file is still copied/linked, and the checksum is still verified when `verify_checksums_before_ingest` is set (the fast path opens the file only for that). Point-key and range-deletion bounds are folded into the same prepared bound pair, and user-defined-timestamp files (including the "UDT in Memtables only" format, whose boundary keys carry no timestamp) are handled.
Internally, ingestion-job metadata acquisition was split into `GetIngestedFileInfoFromFileInfo` (reuse caller metadata) and `GetIngestedFileInfoFromFile` (open + scan). Prepared boundary updates use RocksDB comparators, and the timestamp-stripping path pads prepared bounds back to the internal timestamp shape before installing the file.
The `ingestexternalfile` `db_bench` benchmark now also exposes `--ingest_external_file_fill_cache` so ingestion-read block-cache effects can be controlled independently while comparing the file-info fast path against the normal open-and-scan path.
A future PR will support creating `PreparedFileInfo` from a existing DB-generated file, so this optimization is not just limited to SstFileWriter.
## Benchmark Results
Existing `db_bench ingestexternalfile` benchmark, release build, using an XFS flash-backed filesystem. Files are linked (`move_files`) so the measurement isolates the ingest path rather than file-copy throughput. These numbers used direct reads and `fill_cache=false` to avoid warming the block cache while comparing the file-info fast path against the normal open-and-scan path.
Each run used 1M keys/SST, one ingest call, and `db_bench` reported a 62.9 MB estimated file size. The comparison varied the number of files in that ingest call across 10, 30, and 50 files.
| Files/batch | Config | Prepare P50 | Run P50 | Run P50/file | Total ingestion P50 | Total P50 drop |
| --- | --- | --- | --- | --- | --- | --- |
| 10 | Baseline | 10.909 ms | 7.593 ms | 0.759 ms/file | 18.502 ms | -- |
| 10 | `file_info=true` | 1.127 ms | 7.541 ms | 0.754 ms/file | 8.668 ms | 53.2% |
| 30 | Baseline | 29.049 ms | 23.161 ms | 0.772 ms/file | 52.210 ms | -- |
| 30 | `file_info=true` | 2.734 ms | 23.384 ms | 0.779 ms/file | 26.118 ms | 50.0% |
| 50 | Baseline | 49.036 ms | 39.224 ms | 0.784 ms/file | 88.260 ms | -- |
| 50 | `file_info=true` | 4.133 ms | 39.417 ms | 0.788 ms/file | 43.550 ms | 50.7% |
With `fill_cache=false` and direct reads, the metadata fast path cuts `Prepare()` by roughly 90-92%. `Run()` is roughly 0.75-0.79 ms per file across these runs. End-to-end `db_bench` micros/op is almost unchanged because this benchmark includes SST generation and compaction work; use `rocksdb.ingest.external.file.prepare.micros` and `rocksdb.ingest.external.file.run.micros` to isolate ingestion.
Benchmark args: `--benchmarks=ingestexternalfile --use_existing_db=0 --num=1000000 --compression_type=none --statistics --use_direct_reads=true --ingest_external_file_batch_size=<10|30|50> --ingest_external_file_num_batches=1 --ingest_external_file_use_file_info=<false|true> --ingest_external_file_fill_cache=false`.
Reviewed By: xingbowang
Differential Revision: D107721261
fbshipit-source-id: b06ecb7b35f260ec02acf837e7986057bc23cdbf
Summary:
# Context
The C API lacks a SetDBOptions wrapper, so callers using the C bindings cannot dynamically reconfigure DB-level options at runtime without dropping down to the C++ API.
# Changes
- Add `rocksdb_set_db_options` to the C API as a wrapper around `DB::SetDBOptions`
- Adds new tests in `c_test.c` covering both valid and invalid option updates
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14615
Reviewed By: joshkang97
Differential Revision: D101010593
Pulled By: xingbowang
fbshipit-source-id: 3feac94bd0e3b7d6a4913ce9bc014e1233292309
Summary:
We have seen multiple coding error with the usage of WideColumn API. Hence, improving the documentation to reduce the chance of this happening again.
Clarify that `WideColumn` and `WideColumns` are non-owning views over caller-managed memory.
Update the public `PutEntity()` API documentation to state that column name and value storage must remain valid until the call returns.
Add safe and unsafe usage examples in `wide_columns.h` to make lifetime requirements explicit.
## Testing
Not run. Documentation-only change.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14626
Reviewed By: archang19
Differential Revision: D101280295
Pulled By: xingbowang
fbshipit-source-id: 076103321f1d54103dd1c2772aa9e84affdfd2ad
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14849
This change splits ingestion into two public calls: `PrepareFileIngestion()` performs all of the off-mutex work and returns an opaque `FileIngestionHandle`, and `CommitFileIngestionHandle()` makes the prepared files visible under the mutex.
Even though the "Prepare" phase was off the DB mutex, the application layer may still have application logic that depends on the completion of `IngestExternalFile`.
`IngestExternalFiles(args)` is also just `PrepareFileIngestion(args)` followed by `CommitFileIngestionHandle()`, so its behavior is unchanged.
`CommitFileIngestionHandles()` also supports committing multiple handles atomically. However, it is possible that a CF may be present in multiple handles. In this case, we merge the ingestion jobs together. This allows applications to prepare file ingestions at different times, but still provide a single atomic commit.
Reviewed By: xingbowang
Differential Revision: D108225105
fbshipit-source-id: 082e149f13983a4b14a8fd711d2113728485421f
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14839
Wrap the filesystem passed to external table readers when statistics or file IO listeners are configured so reads performed by implementations such as Nimble update the same RocksDB metrics as regular SST reads. The wrapper records `SST_READ_MICROS`, activity-specific `FILE_READ_*` histograms, last/non-last-level and temperature read tickers, and forwards `OnFileReadFinish`/`OnIOError` to configured listeners so ZippyDB file-read counters observe external-table IO. `MultiRead` records the elapsed read histogram once and applies byte/listener accounting to each completed request.
Add a table test that makes a dummy external table reader read through `ExternalTableOptions::fs` and verifies both `Statistics` counters/histograms and file-read listener updates. Add an unreleased-history entry for the new external table read metrics coverage. This revision also applies the RocksDB `make check-format` clang-format output for `external_table.cc`.
Reviewed By: pdillinger
Differential Revision: D108012449
fbshipit-source-id: 6b6c993c0aa99479e6e043171124bd48f0416bec
Summary:
- Add `tools/gtest_parallel_repro.py` to reproduce gtest flaky tests that depend on process-level CPU contention and scheduler delays.
- Run many fresh gtest processes concurrently, isolate each process with its own `TEST_TMPDIR`, and optionally pin the workload to a smaller CPU set with `taskset`.
- Record per-run logs, write `failures.jsonl`, summarize failure keys, support stopping on first failed batch, and optionally rebuild with `COERCE_CONTEXT_SWITCH=1`.
- Document when to use the runner in `CLAUDE.md` for CI-style flaky tests that single-process `--gtest_repeat` or coerce mode alone does not reproduce.
- Using the new runner, we found and fixed one flaky test and improved c_test harness
- Flaky test: EnvPosixTest.ReadAsyncQueueFull simulated a full io_uring submission queue by overwriting the SQE pointer after io_uring_get_sqe() had already consumed a submission slot. That left stale state in the thread-local ring, so later AbortIO tests could process an unexpected completion and crash. Add a skip_io_uring_get_sqe syncpoint before the io_uring_get_sqe() call and use it from the test so the Busy path is exercised without mutating the ring.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14827
Test Plan: - CI
Reviewed By: pdillinger
Differential Revision: D107758097
Pulled By: xingbowang
fbshipit-source-id: 3256612ae1ae98b66901593371743b3de7e5bb53
Summary:
When `FinishInitDb()` or `Open()` calls `exit(1)` after the DB has been opened, background compaction/flush threads are still running. `exit()` triggers static object destruction (including the `KillPoint` singleton and its `rocksdb_kill_exclude_prefixes` vector) while those threads are still accessing them via `TestKillRandom()`, causing a heap-use-after-free detected by ASAN.
This became more likely to trigger after the multi-DB support commit (3d0d60101e7f) which runs `RunStressTestImpl` on worker threads, making the race window larger when one DB fails initialization while other DBs background threads are active.
The fix replaces `exit(1)` in all error paths that fire after the DB has been opened with a wrapper around `_exit(1)`. `_exit()` terminates immediately without running atexit handlers or destroying static objects, avoiding the race with background threads.
Also updated CLAUDE.md to help get cross-platform compatibility right the first time.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14850
Reviewed By: hx235
Differential Revision: D108298839
Pulled By: pdillinger
fbshipit-source-id: 8e87fcb259c273e2be5eb26b4eaf6009f5b998f1
Summary:
kLZ4Compression and kLZ4HCCompression share the same on-disk format and decompressor, but historically kLZ4Compression only honored negative (acceleration) levels while kLZ4HCCompression only honored positive levels. This unifies them so `compression_opts.level` alone selects the variant: level <= 0 uses LZ4 fast (acceleration = -level) and level >= 1 uses LZ4HC (1..12), regardless of which of the two types is configured.
The configured type now only determines the default compression level (LZ4: acceleration 1, equivalent to level -1; LZ4HC: level 9).
For code simplicity, the recorded per-block type comes from the compression type derived from the level, which could differ from the configured type. To preserve the originally configured choice for debugging/tracking, it is recorded as a `_type=<decimal>` pseudo-option in the rocksdb.compression_options SST table property.
Out-of-range non-default levels are clamped to the nearest effective value (LZ4 acceleration capped at 65537, which also avoids signed overflow negating INT_MIN; LZ4HC level capped at 12). The cost-aware (auto-tune) compressor's LZ4 level grid is changed to negative accelerations so it actually exercises fast LZ4 (positive levels now route to LZ4HC).
Related inclusion: The ZSTD library has a discontinuity at level=0, which maps to level 3, which is more aggressive than levels 1 and 2, which are more aggressive than levels -1, -2, etc. For better friendliness to auto-tuning (etc.), we now map level 0 to be the same as level -1, so that increasing compression level numbers have non-decreasing aggressiveness.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14819
Test Plan:
New unit tests in compression_test.cc:
- UnifiedLZ4LZ4HCLevels: for a representative set of non-default levels, both configured types produce identical output and the same recorded type (selected by the level), levels that clamp to the same effective parameter compress identically, and each round-trips; plus per-type default-level behavior.
- ConfiguredCompressionTypeRecordedInProperties: the `_type=` pseudo- option appears in the SST table property for each configured type.
- ZSTDLevelZeroMapsToMinusOne: level 0 behaves like -1, not like 3.
Reviewed By: joshkang97
Differential Revision: D107536580
Pulled By: pdillinger
fbshipit-source-id: a1281956f70a75d0620cb73d0bfb9ad76c52cca3
Summary:
Fixed a bug where the range lock manager would crash with an assertion failure when using a reverse comparator column family. The CompareDbtEndpoints function used bytewise ordering for length-disparity comparisons, ignoring the user comparator direction.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14831
Test Plan:
- Added unit test RangeLockWithReverseComparator
- Ran make check (4552 tests, 0 failures)
- Verified range_locking_test passes (9/9)
Reviewed By: xingbowang
Differential Revision: D107880089
Pulled By: laurynas-biveinis
fbshipit-source-id: 77f2f2f58197ab5104d4e42f005b0aab1132f1bb
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14772
Updated the iterator creation scheme to happen lazily (on request) as oppsed to eagerly. this allows us to prune the iterator tree structure at the time of requesting iterator preparation as opposed to creation, and allows pruning to become an implementation detail. Version now skips non-overlapping SST levels and files before adding children to the iterator tree, returns direct table iterators when a level has a single matching file, and uses pruned LevelIterator instances when multiple files in one non-L0 level match. The overload no longer prepares iterators during creation; callers that need prepared multiscan execution still call Prepare explicitly after construction, and MultiScan does that itself.
Benchmark: ran `db_bench` in opt mode for the base revision and this diff, with `fillseq,compact,levelstats,multiscanrandom`, `--num=1000000`, `--reads=10000000`, single thread, fixed seeds, `--multiscan_use_async_io=false`, and `--use_multiscan=true`. Both A and B had exactly one SST file and no memtable/L0 data (`L0: 0 files`, `L1: 1 file, 61 MB`). `multiscanrandom` creates `MultiScanArgs` and calls `NewMultiScan(...)`, which reaches the new `NewIterator(..., scan_opts)` pruning path in this diff.
```
seed base A pruning B delta
424242 21824.333 17693.333 -18.9%
424243 24042.014 19424.056 -19.2%
424244 22424.974 17636.910 -21.4%
424245 22404.213 18612.840 -16.9%
```
Average: base `22673.9 us/op`, pruning `18341.8 us/op`, about `19.1%` faster.
Reviewed By: xingbowang
Differential Revision: D104904298
fbshipit-source-id: a742106a1d5813fb795a39eeeb35f8cddc02e886
Summary:
- Add a controlled async filesystem regression test for released direct I/O async reads, verifying released handles are aborted before a later `Poll()` can complete them.
- Add a MultiScan-style regression test for skipping a pending range remainder before seeking into a later async read.
- Extend db_stress/crashtest coverage so MultiScan can stop early after `--num_iterations` results and randomize `--multiscan_use_async_io` again.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14844
Test Plan: CI
Reviewed By: archang19, anand1976
Differential Revision: D108191370
Pulled By: xingbowang
fbshipit-source-id: 5b56099644cf918ea11071521875d0d059f3a69e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14842
### Problem
A process using RocksDB can hit an ASAN `heap-use-after-free` in `rocksdb::InstrumentedMutex::Lock()` when a `DBImpl` is closed while obsolete-file purge work is still in flight. This can happen during close storms with `avoid_unnecessary_blocking_io=true` on a shared Env, where a queued or late handoff purge can run after `~DBImpl` has destroyed `mutex_`.
### Root Cause
`CloseHelper()` has several mutex-unlocked windows late in shutdown. A dropped column-family handle or SuperVersion cleanup can run in one of those windows and start obsolete-file purge work. `FindObsoleteFiles()` marks that work by incrementing `pending_purge_obsolete_files_`, but `PurgeObsoleteFiles(..., true)` can then spend time outside the DB mutex before transferring the work to `bg_purge_scheduled_` via `SchedulePurge()`. During that handoff, `bg_purge_scheduled_` is still zero even though purge work is pending.
### Fix
Make the final `CloseHelper()` drain wait for both `pending_purge_obsolete_files_` and `bg_purge_scheduled_` before destroying `versions_` / `DBImpl`. This covers both the pending handoff and the scheduled `BGWorkPurge` state. Also move `TEST_VerifyNoObsoleteFilesCached()` and `table_cache_->EraseUnRefEntries()` after the final drain, so debug/ASAN table-cache verification runs only after close-time purge work has settled. The regression test parks a dropped-CF cleanup in the pending handoff window and verifies `CloseHelper()` blocks in the final drain until that handoff is released.
Reviewed By: xingbowang
Differential Revision: D107920881
fbshipit-source-id: f73cd79afe50fc30e0f5d14889dd4285bf350a58
Summary:
Use Compressor::GetRecommendedParallelThreads() overrides to disable parallel compression for Snappy, LZ4 (accelerated, not LZ4HC), and for accelerated levels of ZSTD (level < 0). These do not generally benefit from parallel compression.
Also add --verify_compression option to sst_dump for some basic "recompress" benchmarking with that option.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14841
Test Plan:
unit test updated.
In planning the scope of this change, I manually tested some production SST files with release build sst_dump --command=recompress and various settings for --compression_parallel_threads, --compression_types, and --compression_level, while I had the CPUs mostly busy using cache_bench in the background. Here's an example, before the change to override parallel_threads:
```
$ for PT in 1 8; do /usr/bin/time ./sst_dump --command=recompress --compression_parallel_threads=$PT --block_size=16384 --compression_types=kLZ4Compression --verify_compression=1 test.sst; done
...
Compression: kLZ4Compression Block Size: 16384 Threads: 1
Cx level: 32767 Cx size: 168501634 Uncx size: 791721664 Ratio: 4.698599 Write usec: 2345894 Read usec: 429022 Cx count: 48661 (100.0%) Not cx for ratio: 0 ( 0.0%) Not cx otherwise: 0 ( 0.0%)
2.54user 0.29system 0:02.84elapsed 99%CPU (0avgtext+0avgdata 460816maxresident)k
...
Compression: kLZ4Compression Block Size: 16384 Threads: 8
Cx level: 32767 Cx size: 168501634 Uncx size: 791721664 Ratio: 4.698599 Write usec: 2476459 Read usec: 439464 Cx count: 48661 (100.0%) Not cx for ratio: 0 ( 0.0%) Not cx otherwise: 0 ( 0.0%)
3.95user 0.33system 0:02.98elapsed 143%CPU (0avgtext+0avgdata 455504maxresident)k
```
Here as in many of the cases I'm changing, it actually takes longer to compress with parallel, despite the added parallel opportunity of verify_compression. And overall CPU is much higher from 2.54 `CPU*s` to 3.95 `CPU*s`.
The difference disappears with the change, because both use single-threaded SST construction.
Reviewed By: joshkang97
Differential Revision: D108075938
Pulled By: pdillinger
fbshipit-source-id: 5d1fc77ddbccf9f3b24a4f0b20b2b3c43074e89d
Summary:
Adds a new `DBOption use_direct_io_for_compaction_reads` (default false). When on, compaction-input SST files are opened with `O_DIRECT` so the sequential read-once data from compaction doesn't pollute the OS page cache and evict the hot user-read working set. User reads keep going through the buffered fast path. This protects user-read tail latency on write-heavy workloads without forcing user reads onto the existing global `use_direct_reads` knob (which pays in throughput and P50 — see the bench below).
The interesting bit is that just flipping the FileOptions returned by `FileSystem::OptimizeForCompactionTableRead` doesn't actually trigger `O_DIRECT` at the kernel level. The TableCache (and `FileMetaData::pinned_reader`) is already holding buffered handles opened at flush time or at `DB::Open` via `LoadTableHandlers`. When compaction asks for an iterator, it gets back the cached buffered handle and the kernel never sees the `O_DIRECT` flag.
So this PR also adds a small bypass path:
- `TableCache::FindTable` / `NewIterator` learn a `open_ephemeral_table_reader` mode. When set, the pinned-reader fast path and the shared cache are skipped, `GetTableReader` is called directly with the caller's FileOptions, and ownership of the freshly opened TableReader is handed back via a `unique_ptr`. The iterator takes ownership via `RegisterCleanup` and frees the reader on destruction.
- `VersionSet::MakeInputIterator` and `LevelIterator` plumb the flag through both L0 and L1+ compaction-input paths.
- `CompactionJob::ProcessKeyValueCompaction` turns the bypass on when `use_direct_io_for_compaction_reads` is set, the global `use_direct_reads` is off, and `OptimizeForCompactionTableRead` produced `use_direct_reads=true` in the compaction-read FileOptions.
The option is opt-in: when off, nothing changes for existing users. When on, only the compaction-input opens take the bypass path; user reads keep hitting the TableCache and the buffered fast path normally.
There's also a small db_bench helper in the same PR: a new `--bgwriter_num` flag that lets the writer thread in `readwhilewriting` (and the other "while writing" variants) spread its puts across `[0, bgwriter_num)` instead of `[0, num)`. Without this the readers and writer share a key range and you can't have both a hot read subset and meaningful compaction work — this lets you have both.
### Benchmark
Setup: Ubuntu 24.04 (kernel 7.0.5, OrbStack Linux VM on Apple Silicon), 14 vCPUs, virtio-blk disk, btrfs. MGLRU disabled (`echo 0 > /sys/kernel/mm/lru_gen/enabled`) so the kernel uses the classic active/inactive LRU. 14 GB DB (3.5M keys × 4 KB values), no compression. Each measurement run is pinned to a 1 GB cgroup via `systemd-run --scope -p MemoryMax=1G -p MemorySwapMax=0`. Page cache is dropped between configs. db_bench is Release build.
Workload: `readwhilewriting` for 120s. 4 reader threads doing random reads over a hot key subset, plus 1 writer thread spreading overwrites across the full 3.5M-key keyspace (via `--bgwriter_num=3500000`) throttled at 200 MB/s, so there's continuous compaction running while the readers go.
The size of the hot reader subset relative to available page cache controls how visible the optimization is. The Cassandra blog ([Lightfoot 2026](https://lightfoot.dev/direct-i-o-for-cassandra-compaction-cutting-p99-read-latency-by-5x/)) documented the same thing: biggest wins when the hot set is big enough to actually compete for cache, smaller wins when the hot set trivially fits, neutral when the hot set is way bigger than cache. So I ran two hot-set sizes.
#### Small hot set: ~30 MB (~3% of the 1 GB cgroup) — N=5 iterations, mean (CV)
`--num=7500`. The hot set is small enough that the page cache holds it without much trouble even under compaction, so the wins here are real but on the modest side.
| Config | Throughput (ops/s) | Read P50 (µs) | Read P99 (µs) | Read P99.9 (µs) | Read P99.99 (µs) |
|---|---|---|---|---|---|
| buffered (default) | 233,477 (8.2%) | 16.09 | 82.24 | 721.0 | 2,102.5 |
| direct_compaction_writes_only (existing knob alone) | 287,405 (2.8%) — **+23.1%** | 13.00 (−19.2%) | **66.77 (−18.8%)** | 553.9 (−23.2%) | 1,787.6 (−15.0%) |
| direct_compaction_read_only (new knob alone) | 250,669 (2.4%) — +7.4% | 14.16 (−12.0%) | 102.99 (+25.2%) | 689.8 (−4.3%) | 1,801.3 (−14.3%) |
| direct_compaction_read_write (new + existing, recommended) | 277,920 (3.3%) — **+19.0%** | **12.99 (−19.3%)** | 84.23 (+2.4%) | 613.4 (−14.9%) | **1,738.2 (−17.3%)** |
| use_direct_reads=true (existing global) + write-side | 249,014 (2.5%) — +6.7% | 15.95 (−0.9%) | 68.78 (−16.4%) | **450.8 (−37.5%)** | 1,814.5 (−13.7%) |
CV is 2.4–3.3% on the optimized configs (8.2% on buffered), so the deltas are real. With a hot set this small, the existing `use_direct_io_for_flush_and_compaction` knob is already doing most of the work — the new flag's main extra contribution here is P99.99 (combined wins it by ~2 points vs writes-only-alone). Worth noting: the new flag *alone* (without the existing write-side flag) improves P99.99 but regresses P99 by 25% on this small-hot-set workload, because direct compaction reads lose kernel readahead and compaction-output writes are still hitting the page cache. That regression goes away once you combine with the existing write-side flag, or once the hot set is bigger (see next table). So if you're using just one knob, use the existing one. If you're using this PR's flag, pair it with `use_direct_io_for_flush_and_compaction=true`.
#### Larger hot set: ~400 MB (~40% of cache) — N=5 iterations, mean (CV)
`--num=100000`. This is the case the Cassandra blog calls out — hot set big enough to actually fight compaction for cache. Their analogous setup (1M hot partitions, ~33% hot/cache) reported 1.93× p99 improvement. Numbers here are the headline:
| Config | Throughput (ops/s) | Read P50 (µs) | Read P99 (µs) | Read P99.9 (µs) | Read P99.99 (µs) |
|---|---|---|---|---|---|
| buffered (default) | 68,959 (7.7%) | 44.81 | 541.22 | 2,225.2 | 11,334.5 |
| direct_compaction_writes_only (existing knob alone) | 73,973 (10.3%) — +7.3% | 42.22 (−5.8%) | 456.27 (−15.7%) | 2,016.9 (−9.4%) | 9,190.0 (−18.9%) |
| direct_compaction_read_only (new knob alone) | 84,337 (2.3%) — +22.3% | 38.66 (−13.7%) | 386.97 (−28.5%) | 1,644.8 (−26.1%) | 4,837.9 (−57.3%, 2.34×) |
| direct_compaction_read_write (new + existing, recommended) | **104,923 (8.4%) — +52.2%** | **34.26 (−23.5%)** | **290.97 (−46.2%)** | **1,143.4 (−48.6%)** | **3,080.3 (−72.8%, 3.68×)** |
| use_direct_reads=true (existing global) + write-side | 71,598 (9.1%) — +3.8% | 51.33 (+14.5%) | 297.91 (−45.0%) | 1,663.6 (−25.2%) | 6,530.0 (−42.4%) |
Combined config gets a 3.68× p99.99 win, 1.86× p99, p50 down 23%, throughput up 52%. Same shape as the Cassandra blog's 1.93× p99 result — the improvement just lands at deeper percentiles for us because RocksDB's baseline data path is roughly 40× faster than Cassandra's (their buffered p99 was 35 ms, ours is 0.54 ms), so the cache-miss tail is further out.
A few things worth calling out from this table:
- The new flag is doing real work on top of the existing write-side flag here, not just shifting things around. Combined throughput is +42% over `direct_compaction_writes_only` alone, and combined p99.99 is 3× better. The existing knob alone gives a fairly modest +7% throughput / -19% p99.99 in this case — there's a clear gap that the new flag fills.
- The new flag *alone* (no existing write-side flag) is also a real improvement here: +22% throughput, p99.99 down 57%. The P99 regression we saw in the small-hot-set case is gone, because the cache-protection effect now dominates the lost-readahead cost.
- `use_direct_reads=true` (the existing global flag) actually regresses P50 by 14.5% in this workload — taking user reads off the page cache hurts you when the hot data could have been cached. It also gets the worst throughput of any direct config. It's not an equivalent way to get these gains.
### `compaction_readahead_size` matters when this flag is on
Direct I/O bypasses kernel readahead, so RocksDB's own `DBOptions::compaction_readahead_size` becomes the only prefetch the iterator has. The default of 2 MB is enough and real users will get it automatically. **But `db_bench`'s `--compaction_readahead_size` CLI default is 0**, which defeats prefetch and makes direct compaction look slower than it actually is. If you're reproducing the numbers above, pass `--compaction_readahead_size=2097152` (or larger).
- Recommended production config is `use_direct_io_for_compaction_reads=true` + `use_direct_io_for_flush_and_compaction=true`. Strongest configuration at every percentile and throughput in both benches.
- The new flag is the read-side counterpart to `use_direct_io_for_flush_and_compaction`, which handles compaction-write cache pollution. They address different sources of pollution and compose. The gap between "combined" and "writes-only-alone" is 17 percentage points on p99.99 in the small-hot-set bench and 54 points in the larger one, so the new flag is contributing real value, especially as the hot set grows.
- The new flag alone is also a real improvement when the hot set is big enough to compete with cache (+22% throughput, 2.34× p99.99 in the larger-hot-set bench). On a very small hot set it improves p99.99 but regresses p99, so pairing with the existing write-side flag is safer.
- The benefit is workload-dependent. Small hot sets get modest tail-latency wins. Hot sets sized to actually compete for cache get the big multi-percentile wins shown above. Hot sets bigger than cache (not benched here but covered in the Cassandra blog) see no change either way — every read misses regardless.
### Reproducing
Any Linux host (or a Linux VM on macOS via OrbStack / Multipass / lima):
```bash
sudo apt-get install -y build-essential clang cmake git pkg-config \
libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
cmake -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DWITH_GFLAGS=1 -DWITH_TESTS=0 ..
make -j db_bench
echo 0 | sudo tee /sys/kernel/mm/lru_gen/enabled
```
Build the source DB once, unrestricted memory:
```bash
./db_bench --benchmarks=fillrandom,compact,waitforcompaction,stats \
--db=/path/to/source_db --num=3500000 --key_size=16 --value_size=4096 \
--write_buffer_size=16777216 --target_file_size_base=16777216 \
--max_background_jobs=4 --compression_type=none --cache_size=4194304 \
--max_bytes_for_level_base=67108864 --disable_wal=1 --sync=0
```
For each config, copy `source_db -> scratch_db`, run `sync && echo 3 > /proc/sys/vm/drop_caches`, then:
```bash
sudo systemd-run --scope -p MemoryMax=1G -p MemorySwapMax=0 \
./db_bench --use_existing_db=1 \
--benchmarks=readwhilewriting,stats --db=/path/to/scratch_db \
--threads=5 --duration=120 --statistics=true --histogram=1 \
--num=7500 --bgwriter_num=3500000 \
--key_size=16 --value_size=4096 \
--write_buffer_size=16777216 --target_file_size_base=16777216 \
--max_background_jobs=4 --compression_type=none \
--cache_size=4194304 --open_files=200 \
--skip_stats_update_on_db_open=true \
--max_bytes_for_level_base=67108864 \
--benchmark_write_rate_limit=209715200 \
--compaction_readahead_size=2097152 \
--rate_limiter_bytes_per_sec=0 \
--use_direct_reads={true|false} \
--use_direct_io_for_compaction_reads={true|false} \
--use_direct_io_for_flush_and_compaction={true|false}
```
For the larger hot-set table, change `--num=7500` to `--num=100000`.
The five configs in the tables:
- `buffered`: all three flags false.
- `direct_compaction_writes_only`: `use_direct_io_for_flush_and_compaction=true`, the other two false. This is what users have today without this PR.
- `direct_compaction_read_only`: `use_direct_io_for_compaction_reads=true`, the other two false.
- `direct_compaction_read_write`: `use_direct_io_for_compaction_reads=true`, `use_direct_io_for_flush_and_compaction=true`, `use_direct_reads=false`. **Recommended.**
- `direct_all`: `use_direct_reads=true`, `use_direct_io_for_flush_and_compaction=true`, `use_direct_io_for_compaction_reads=false`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14743
Reviewed By: pdillinger
Differential Revision: D108017601
Pulled By: xingbowang
fbshipit-source-id: 4039d490d7e77b476db7a477a2f3d24738db6336
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14838
Remote compaction can pre-filter whole input files when universal compaction sees standalone range tombstones. In that case the worker's iterator count no longer describes the full original input set, so returning `has_accurate_num_input_records=true` can trigger a false-positive input-record verification on the primary. Mark the remote count inaccurate whenever the reconstructed compaction filtered input files before iteration, and add a regression test covering that path.
Reviewed By: jaykorean
Differential Revision: D107961493
fbshipit-source-id: 0c302081964ab1d894b34bddd2c55f5a4f06752f
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14836
Adds a public `Statistics` histogram, `INGEST_EXTERNAL_FILE_TIME` (`"rocksdb.ingest.external.file.micros"`), recording the end-to-end latency in microseconds of each `IngestExternalFile(s)` call. Ingestion timing was previously only available through per-thread `perf_context` counters, which require setting a `PerfLevel` and are not aggregated, so there was no process-wide latency distribution (p50/p99/max) for dashboards.
It is recorded with an RAII `StopWatch` at the top of `DBImpl::IngestExternalFiles` -- one sample per call (not per column family), covering all return paths. It is null-safe and self-gating on the stats level, so there is no cost when statistics are off, and ingestion is not a hot path. Java bindings are kept in sync per the `statistics.h` requirement; the C API needs no change.
Reviewed By: xingbowang
Differential Revision: D107721260
fbshipit-source-id: 0705f9e0f7392329a7bcbdbd9f3afd34594d20bb
Summary:
Add read-scoped block buffers for scan reads. Introduce an experimental read-scoped block buffer provider API, configured through ReadOptions::read_scoped_block_buffer_provider, so supported block-based table iterator scans and MultiScan data-block reads can use caller-provided read-scoped storage for final data-block contents.
When configured, supported provider-backed scan data-block reads bypass the data-block cache while preserving normal index/filter block-cache behavior. Known-uncompressed reads can attach provider cleanup to provider-backed read buffers without copying. Compressed reads decompress directly into provider-backed output, while maybe-compressed reads that turn out to be uncompressed copy once into provider-backed final contents. mmap reads ignore the provider.
Extend AlignedBuffer and RandomAccessFileReader direct-I/O paths to support external aligned allocations, then use that support for read-scoped iterator, async I/O, and MultiRead scratch buffers. Centralize read-scoped I/O policy, keep coalesced async reads safe when blocks are released before completion, and validate provider lease contracts.
Add focused coverage for read-scoped ownership, compressed and uncompressed blocks, direct I/O, data-block cache bypass behavior, invalid provider leases, async release handling, and stress-test provider invariants. Add public API release notes for read-scoped block buffers.
Bonus change: Fixed a flaky test in ReserveThread
## Testing
- CI
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14806
Reviewed By: anand1976
Differential Revision: D106999951
Pulled By: xingbowang
fbshipit-source-id: b1f23d4bab6318b6373ba2ca99a5c4d6a842dc5a
Summary:
Extends RocksDB's C API with two `BackupEngine` capabilities needed by
language bindings (e.g. Rust via librocksdb-sys) that consume the C API:
- **StopBackup**: Add `rocksdb_backup_engine_stop_backup()` to allow cancelling
an in-progress backup.
- **Rate limiters**: Add
`rocksdb_backup_engine_options_set_backup_rate_limiter()` and
`rocksdb_backup_engine_options_set_restore_rate_limiter()` to expose the
`shared_ptr<RateLimiter>` fields on `BackupEngineOptions`. The existing
`uint64_t` setters only throttle writes; these expose the richer `RateLimiter`
object that supports read+write throttling (e.g. `kAllIo` mode).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14722
Test Plan:
- [x] New tests in `db/c_test.c` cover `StopBackup` and rate limiter
setter/getter roundtrips, plus opening a real backup engine with rate
limiters set and running a backup end-to-end
- [x] `make check` passes with no regressions
Reviewed By: pdillinger
Differential Revision: D107654882
Pulled By: xingbowang
fbshipit-source-id: f50c3989779e6a099113fec203231d47b9480cb9
Summary:
`TransactionDB` supports different write policies (e.g. `WritePrepared`), but this functionality is not currently accessible via the C API. Creating a C shim to expose the functionality for setting the write policy.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14810
Reviewed By: pdillinger
Differential Revision: D107654915
Pulled By: xingbowang
fbshipit-source-id: b0d915a3420057de5236fe9f6cb47d291294788c
Summary:
PR https://github.com/facebook/rocksdb/issues/14585 added FileOpenContract enforcement to FaultInjectionTestFS. The no-reopen-for-write check recorded contracts by path but did not clear a stale contract after the file was deleted. A later SST create that reused the same path could be rejected as a forbidden reopen, causing DBWALTest.WALWithChecksumHandoff to fail with "NewWritableFile violates no-reopen-for-write contract". If the ASSERT exited the test early, the local Env stack object could also be destroyed before DB fixture teardown closed the DB, producing the follow-on TSAN heap-use-after-free.
When opening a file for write, drop stale no-reopen tracking if the target file no longer exists, while still rejecting writes through reopened handles. Also close the DB before the WAL test's local Env unwinds on assertion failure.
A separate TSAN report in DBBlobBasicIOErrorTest.GetEntityMergeWithBlobBaseIOError exposed unsynchronized access to FaultInjectionTestEnv::error_: SetFilesystemActive() updated the stored error under mutex_, while background purge work read it through GetError() without that mutex. Copy injected errors under the same mutex and read the no-space state in GetFreeSpace() while protected. Apply the same synchronization to FaultInjectionTestFS.
Bonus fix: Fix another TSAN data race in FaultInjectionTestEnv GetError not synchronized under mutex_
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14822
Test Plan: CI
Reviewed By: mszeszko-meta
Differential Revision: D107689662
Pulled By: xingbowang
fbshipit-source-id: 6d4f8fdc8b898d3ddcad7816e385f3b7f20c4727
Summary:
- Rename the misspelled `memtable_veirfy_per_key_checksum_on_seek` option and related flags/config keys to `memtable_verify_per_key_checksum_on_seek`.
- Update memtable option plumbing, options serialization/logging, db_bench/db_stress/crash-test flags, tests, and the option-addition guide to use the corrected name.
- Keep the checksum-on-seek behavior unchanged.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14811
Test Plan: CI
Reviewed By: pdillinger
Differential Revision: D107382276
Pulled By: xingbowang
fbshipit-source-id: 7621dc718d61503b982a7e3f65cc9293a1ad085b
Summary:
- Add a contract-boundaries section to the RocksDB code review checklist in `CLAUDE.md`.
- Document common review prompts for keeping caller-specific policy out of reusable lower layers.
- Add "Contract Boundary Leaks" to the common review feedback patterns.
## Testing
CI
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14821
Reviewed By: pdillinger
Differential Revision: D107652555
Pulled By: xingbowang
fbshipit-source-id: 383bd78fec6e3ecc251c752d30f9de3ea44a10de
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14585
Adds typed file-open contracts and open-file size handling needed for blob direct-write files on remote or SHM-backed file systems.
This diff:
- Adds typed `FileOpenContract` semantics to RocksDB `FileOptions`, including `kNoReopenForWrite`, `kNoReadersWhileOpenForWrite`, bitwise helpers, and constructor propagation from `EnvOptions` to `FileOptions`.
- Marks SST/table output creation paths with both `kNoReopenForWrite` and `kNoReadersWhileOpenForWrite`, including builder output, compaction output, and external `SstFileWriter` output.
- Marks blob write paths with the appropriate contracts: regular blob file builders use both `kNoReopenForWrite` and `kNoReadersWhileOpenForWrite`, while blob direct-write partition output uses `kNoReopenForWrite` so active readers can still observe SHM-backed data through the read path.
- Adds `GetFileSizeFromOpenFileOrPath` so readers can prefer an already-open `FSRandomAccessFile::GetFileSize` result and fall back to path-level `FileSystem::GetFileSize` according to the caller's fallback policy.
- Updates blob file reader and table footer reader size checks to use the new open-file-or-path size helper, preserving malformed-file validation while supporting file systems where path-level size is not the only valid source.
- Extends `FaultInjectionTestFS` to track file-open contracts, reject readers while a no-readers writer is open, reject reopened data writes for `kNoReopenForWrite`, allow `SyncFile` to use a reopened handle, and preserve contract state across create, reopen, reuse, rename, link, reset, and delete paths.
- Keeps `IOStatus` return paths simple by returning status locals directly in the file-size helper and fault-injection contract validation paths.
- Adds clang-format-compliant tests covering file-open contract enforcement, `SyncFile` being allowed while reopened data writes are rejected, blob direct-write behavior, blob reader behavior, and the new file-size fallback helper behavior.
- Leaves WAL and MANIFEST contract assignment for follow-up work because live WAL/MANIFEST readers and `reuse_manifest_on_open` need separate handling before RocksDB can safely claim stronger file-open contracts for those file classes.
Reviewed By: anand1976, pdillinger
Differential Revision: D100002686
fbshipit-source-id: 26b4021bc13333ede1077f0ff8cbd71335c5f294
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14739
Adds public path-level file sync APIs to RocksDB so callers can ask `Env` or `FileSystem` to sync a named file without hand-rolled reopen logic.
This diff:
- Adds public `Env::SyncFile` and `FileSystem::SyncFile` APIs for syncing or fsyncing a file by name without requiring callers to reopen it directly.
- Documents the `SyncFile` contract: filesystems may override it as a no-op when flush/close already provides durability, and RocksDB callers should use it instead of hand-rolled `ReopenWritableFile()+Sync()/Fsync()` so filesystems can reject post-close data-write reopen paths.
- Implements the default `Env` and `FileSystem` behavior by reopening the file as writable, calling `Sync` or `Fsync`, closing the handle, returning the sync/fsync error ahead of a close error when both fail, and explicitly consuming the close status on the sync-failure path.
- Wires `SyncFile` through the Env/FileSystem bridge and wrapper layers, including `EnvWrapper`, `FileSystemWrapper`, `CompositeEnvWrapper`, `EncryptedFileSystem`, `RemapFileSystem`, read-only file systems, mock file systems, and fault-injection wrappers.
- Keeps fault-injection `SyncFile` overrides on the base default implementation so the operation still exercises the wrapper's `ReopenWritableFile`, wrapped-file `Sync`/`Fsync`, and `Close` hooks instead of bypassing them through target forwarding.
- Updates external SST ingestion to call `FileSystem::SyncFile` instead of manually reopening an ingested file as writable, while preserving the `NotSupported` skip behavior and failure logging.
- Adds release-note coverage for the new public API and unit coverage for default `Env`/`FileSystem` success, reopen failure, close failure, sync-versus-close failure precedence, and external SST sync behavior.
There was an earlier version of this API which got reverted (PR #13987) due to internal change broken. Re-apply the change. The internal issue will be resolved in next release.
Reviewed By: pdillinger
Differential Revision: D104918547
fbshipit-source-id: 3f8d2d127fc962b68b423bbb90de551fe6706224
Summary:
The historical default block compression `kSnappyCompression` dates to when Snappy was the obvious fast/cheap choice. On modern server CPUs LZ4 matches or beats Snappy on compression ratio while decompressing substantially cheaper, so it is a better default. This changes the default `ColumnFamilyOptions::compression` to `kLZ4Compression`, with a runtime fallback of LZ4 -> Snappy -> none depending on what is compiled into the binary (new `GetDefaultCompressionType()` in util/compression.h). Only column families that do not explicitly set `compression` are affected (including compaction output when
`CompactionOptions::compression == kDisableCompressionOption`), and only newly written SST files; existing data is read as before. Doc comments, the Java bindings, and the sorted_run_builder example are updated to describe the new default.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14818
Test Plan:
Adjusted two unit tests that implicitly depended on the old Snappy default to pin `compression = kSnappyCompression`: db_iterator_test's ReadAhead (readahead byte thresholds assume Snappy-sized files) and compaction_service_test's CustomFileChecksum (LSM shape after auto-compaction determined whether a manual CompactRange had work to do).
This change is primarily validated by performance testing. Added a `compressreject` db_bench benchmark (output buffer sized just below the predicted compressed size) to measure the cost of attempting then declining compression, alongside `compress`/`uncompress`. Both db_bench and sst_dump compress/decompress a modest block at a time, as in a real workload.
sst_dump on SST files from production workloads (4 files, 16KB blocks, single thread) on recent server-class AMD, Intel, and ARM CPUs, LZ4 vs Snappy:
- Compression ratio: comparable; LZ4 is slightly smaller on the more compressible files (up to ~8%) and within ~2% on the rest.
- Compression (write) CPU: a wash, within ~2% either direction.
- Decompression (read) CPU: the clear win -- Snappy costs ~1.2x-1.5x as much as LZ4, i.e. LZ4 saves ~25-30% read CPU, consistently across AMD, Intel, and ARM.
db_bench synthetic workload (100-byte values), at 1 / 12 / 160 threads, LZ4 vs Snappy:
- Compression throughput: LZ4 ~10-30% higher.
- Decompression throughput: LZ4 much higher, and the advantage grows with core count -- from ~+12% at 12 threads to ~+45-50% at 160 threads, i.e. better multi-core scaling.
- Rejection (insufficient ratio) path: comparable; LZ4 ~15-18% faster on compressible-but-rejected blocks and Snappy within ~10% on barely-compressible blocks. No meaningful regression, confirming incompressible data is still efficiently detected and stored raw.
Reviewed By: xingbowang
Differential Revision: D107536490
Pulled By: pdillinger
fbshipit-source-id: f8abaee630d782674778338148e36ed0c84e3661
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14798
Add cross-version format compatibility testing for remote compaction to `check_format_compatible.sh` as primary and worker RocksDB instances can run different versions.
Two new ldb commands coordinate via local files:
- `remote_compaction_primary`: opens an existing DB and runs `CompactRange()` through `LocalFileCompactionService`, which writes `input.bin` via `Schedule()` and polls for `result.bin` via `Wait()`.
- `remote_compaction_worker`: polls for `input.bin`, calls `OpenAndCompact()`, writes `result.bin`.
The test script creates a DB using `generate_random_db.sh` with the primary's ldb binary (new optional 3rd argument) so the OPTIONS file matches the primary's version. An overlap key is written to ensure `CompactRange` triggers a real compaction (not a trivial move). For each old ref in `db_forward_with_options_refs`, the script tests both directions -- current primary + old worker and old primary + current worker -- to catch wire-format incompatibilities in `CompactionServiceInput`/`CompactionServiceResult`. Old refs lacking the commands are skipped gracefully.
Reviewed By: pdillinger
Differential Revision: D106321150
fbshipit-source-id: e0341b57c1b12e1fa5296609f0463f77484c1a6e
Summary:
When searching/grepping through unit tests, it's convenient to use test.cc suffix to match all unit tests, or to exclude test.cc when excluding unit tests from a code search. (I've even seen my AI assistant grep through `*test.cc`.) So I'm renaming db_test2.cc (as planned in https://github.com/facebook/rocksdb/issues/14076)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14218
Test Plan: existing tests + CI
Reviewed By: xingbowang
Differential Revision: D90140624
Pulled By: pdillinger
fbshipit-source-id: 78aa099290670bbb4093b2c7c02bc47ab3bf5f8e
Summary:
Fix a false-positive compaction corruption error in the remote compaction path. Remote workers can mark `CompactionJobStats::num_input_records` as unreliable when input iteration uses seek/skip behavior, but the remote result serialization was dropping `has_accurate_num_input_records`. The primary then deserialized the flag as its default `true`, trusted an unreliable zero input-record count, and raised `Compaction number of input keys does not match number of keys processed`.
This change serializes the accuracy flag with the rest of `CompactionJobStats` so the primary preserves the remote worker's "do not verify this count" signal. It also adds a targeted regression test and a release note for the bug fix.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14808
Test Plan: New regression test that triggers corruption error without the fix by modifying `has_accurate_num_input_records=false`
Reviewed By: xingbowang
Differential Revision: D107128357
Pulled By: joshkang97
fbshipit-source-id: 3243c9c2ab18534652737f7410fe3c51724db549
Summary:
Async WAL precreation in https://github.com/facebook/rocksdb/pull/14738 / D105020559 was motivated by slow file creation time on remote storage. MANIFEST does not need the same precreation treatment as WAL because most MANIFEST writes come from background flush and compaction work, but user-facing metadata operations can still pay MANIFEST rotation file creation latency inline. File ingestion performance is a particular concern to some Meta users.
Relax the effective MANIFEST rotation limit by 25% for MANIFEST write batches containing any foreground VersionEdit, while keeping background-only flush/compaction batches on the configured or auto-tuned limit. This covers column family manipulation, external file ingestion and import, and DeleteFilesInRange(s). SetOptions remains expected to avoid MANIFEST writes; the test keeps a regression guard for that behavior.
The relaxation is intentionally bounded. It reduces the chance that foreground metadata operations create a new MANIFEST inline, while still allowing foreground operations to rotate once the current MANIFEST is beyond the relaxed threshold. Heavier blocking operations like manual Flush or CompactRange already trigger additional file creation and do not get this treatment here, though that could be reconsidered later.
This should reduce a potential latency hazard of manifest file size auto-tuning: more frequent MANIFEST rotations. With this change, rotation latency is shifted toward background-only MANIFEST batches when possible.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14797
Test Plan:
Expanded DBEtc3Test.AutoTuneManifestSize to cover the foreground threshold behavior and the original auto-tuning behavior in separate phases:
- verifies foreground-only CreateColumnFamily writes get only bounded 25% headroom by asserting the first four large-CF additions do not rotate and the fifth does;
- verifies auto-tuned background thresholds still prevent excessive rotation;
- verifies foreground operations stay below the relaxed threshold for CreateColumnFamily, IngestExternalFile, CreateColumnFamilyWithImport, and DeleteFilesInRanges;
- verifies SetOptions still does not write to MANIFEST;
- verifies a following background flush still rotates at the normal threshold;
- preserves the persisted compacted manifest size close/reopen coverage.
Reviewed By: xingbowang
Differential Revision: D106578771
Pulled By: pdillinger
fbshipit-source-id: f8e274032cd9e7f50e95b685c949242f95351498
Summary:
StringToMap previously stripped the outer braces from nested values,
making single-entry round-trip via `key=value;` (e.g. SetOptions)
silently corrupt values that contain ';'. For example, a filter_policy
with sub-options serialized as
filter_policy={id=ribbonfilter:10:-1;bloom_before_level=-1;}
parsed to map[filter_policy] = "id=ribbonfilter:10:-1;bloom_before_level=-1;",
and embedding that map entry directly into another `key=value;` string
re-exposed the inner ';' as a top-level delimiter.
This change is a step toward the "essential view": the outer `{...}` of
a nested value is part of the value's identity. StringToMap preserves it, so
each map entry is in self-contained form (a simple value or a single
balanced `{...}` block) and can be embedded directly without further
escaping by the caller. However some permissiveness is kept for
compatibility: list-style typed parsers (ParseVector, ParseArray,
kStringMap, the listener parser) and scalar dispatch in
OptionTypeInfo::Parse accept either the bare or the wrapped form via a
new OptionTypeInfo::StripOuterBraces helper that peels at most one
outer-pair-matched layer. So braced scalar input like `key={42};` and
`key={true};` still parses, and `key={};` denotes an empty list,
distinct from `key={{}};` which denotes a one-element list with an
empty element.
A new public MapToString is added to convenience.h as the symmetric
inverse of StringToMap: a trivial `key=value;` joiner that relies on
each value already being self-contained (which StringToMap guarantees).
Also fixed and refactored our trim() function because it would do the wrong
thing for a single space. This problem was detected by expanding the
StringToMapRandomTest based on code review feedback, and should only
improve existing callers to trim().
The OPTIONS-file serializer code is untouched, so the on-disk format
is byte-for-byte identical and format-compatibility is preserved.
Bonus: fixes / clarifications to CLAUDE.md's build-system note.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14805
Test Plan:
db_bloom_filter_test extends MutableFilterPolicy with the OPTIONS-file
format for filter_policy to confirm that form round-trips through
SetOptions.
options_test updates StringToMapTest expectations for the new
brace-preserving behavior, and adds:
- MapToStringTest covering the new joiner.
- StringToMapMapToStringRoundTripTest including a single-entry
pull-and-embed scenario that previously corrupted on round-trip.
- FullOptionsStringToMapRoundTripTest that drives a populated
DBOptions and CFOptions through GetStringFromXxx -> StringToMap ->
per-entry single round-trip -> MapToString -> GetXxxFromString ->
VerifyXxxOptions, catching any custom serializer that emits a
value with ';' without enclosing it in '{}'.
- EmptyBracedVectorAndBracedScalarTest covering `key={};` (empty
list) and braced scalar input like `key={42};`, `key={true};`.
- Expanded StringToMapRandomTest with round-tripping.
Some explicit trim() tests added to string_util_test.cc
Format compatibility verified with
SHORT_TEST=1 tools/check_format_compatible.sh
Other existing tests in options_test, customizable_test, options_settable_test,
listener_test, options_file_test, options_util_test, db_options_test,
and compaction_service_test have extensive coverage of serialization /
deserialization logic.
Reviewed By: hx235, xingbowang
Differential Revision: D106855466
Pulled By: pdillinger
fbshipit-source-id: 872aac8d819c7b90c807b92bab85569b48fa2aa0
Summary:
- AbortAllCompactions can race with an already-scheduled automatic compaction before the background worker calls PickCompactionFromQueue(). MaybeScheduleFlushOrCompaction() has already consumed one unscheduled_compactions_ credit when it scheduled the worker, but the CF is still present in compaction_queue_ with queued_for_compaction=true. If the worker returns kCompactionAborted before popping the CF, ResumeAllCompactions() can see unscheduled_compactions_ == 0 and fail to schedule the still-queued work, leaving compaction permanently stalled until DB restart.
- Restore the unscheduled compaction credit in the non-prepicked abort path, matching the existing BG-work-stopped handling for the same scheduled-before-pick state. Prepicked/manual compactions are not adjusted because they do not represent an unpopped automatic compaction_queue_ entry whose scheduling credit was consumed.
- Add AbortScheduledAutomaticCompactionBeforePick to deterministically reproduce the lost-credit race with a sync point at BackgroundCallCompaction:0. The test verifies that ResumeAllCompactions() schedules the still-queued automatic compaction by checking COMPACT_WRITE_BYTES advances and L0 file count drops.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14800
Test Plan: - Without the implementation fix, `DBCompactionAbortTest.AbortScheduledAutomaticCompactionBeforePick` fails because `COMPACT_WRITE_BYTES` remains unchanged after `ResumeAllCompactions()`.
Reviewed By: pdillinger
Differential Revision: D106684155
Pulled By: xingbowang
fbshipit-source-id: 6dacea473ef481eb3122eb15e296e5b69635413f
Summary:
- Propagate validated, sorted MultiScan range state from `DBIter::Prepare()` through `MultiScanArgs`.
- Mark block-based table IO jobs as already sorted when the public MultiScan ranges have been validated.
- Keep `IODispatcher::SubmitJob()` as the normalization boundary for unsorted callers, while allowing sorted callers to skip the defensive block-handle sort.
- Update private dispatcher coalescing helpers to consume sorted block indices and add debug assertions for that precondition.
## Testing
CI, new unit test
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14783
Reviewed By: anand1976
Differential Revision: D106301516
Pulled By: xingbowang
fbshipit-source-id: 99b7ffcaecbf27cb79f15feb4af8680ff1e422d9
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
Summary:
* Add CLAUDE.md This CLAUDE.md is generated through the analysis of 9,012 commits from the RocksDB repository since 2016. It aggregate the commits into 8 major components based on the component it is changing. It selected top 100 most complex PR based on line of changes to collect the code review feedbacks. For each PR, we collected:
• PR title and description to understand the change context
• Files changed to identify affected components and measure complexity
• Inline code review comments from reviewers
• General review summaries and approval/change request feedback
The feedback was then categorized by RocksDB component and analyzed for recurring themes, patterns, and best practices.
This CLAUDE.md file could be used for guiding code generation and code review.
* Optimize tooling for claude code Add make check-progress and format-auto targets for automation
Add machine-parseable progress reporting for `make check` to support automated monitoring tools like Claude Code:
- Add `build_tools/check_progress.sh` script that outputs JSON progress
- Add `make check-progress` target to poll build/test progress
- Detects phases: compiling -> linking -> generating -> testing
- Reports failed tests with exit codes, signals, and log output
- Limits to 10 failures with last 50 lines of output each
Also add non-interactive formatting support:
- Add `-y` flag to format-diff.sh for auto-apply without prompts
- Add `make format-auto` target for CI/automation use
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14293
Test Plan: Local build
Reviewed By: pdillinger
Differential Revision: D92085763
Pulled By: xingbowang
fbshipit-source-id: ba122a4ff51087aec5c06bab804edfee34e13880
Summary:
Change adds `log_data_` function callback for when iterating over a `WriteBatch`. Previously only the `Put`, `Delete`, `Merge` operations were called into when iterating over an `WriteBatch` (and their `*_cf` equivalent through a different `WriteBatch::Handler` implementation).
To maintain backwards compatibility, previously exported function definitions remain the same, but new functions are exported for different languages to use the `LogData` callback on an iteration.
### Background
Hi - we use the [`rust-rocksdb`](https://github.com/rust-rocksdb/rust-rocksdb) bindings to work with `rocksdb` at Stripe. We are starting to make small contributions https://github.com/facebook/rocksdb/pull/14183 & https://github.com/facebook/rocksdb/pull/14136 and this adds on top of it. I saw that the `PutLogData` method is already exported for a `WriteBatch`, but there's no way to consume that. This change allows us to consume that information (with a follow up change on the [`rust-rocksdb`](https://github.com/rust-rocksdb/rust-rocksdb) repo.).
Thanks for your time looking into this. Previously we had trouble with meta's internal linters - I am happy to make appropriate change if something like that pops up again.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14245
Reviewed By: archang19
Differential Revision: D92069503
Pulled By: jaykorean
fbshipit-source-id: a4a3c885462f641c8df9e3401a0e4c1d38871c6f
Summary:
This diff introduces the IODispatcher into the BlockBasedTableIterator. This replaces much of the prefetch logic with the logic found in IODispatcher.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14255
Test Plan:
I ran the following benchmark, %change is within noise tolerance. There shouldn't be any large performance improvement with this change, more so there should also not be any performance degradation.
MultiScan Benchmark: Current Branch vs Main
Configuration:
- Threads: 4
- Ranges per scan: 10
- Stride: 5000
- Seek nexts: 100
- Cache: Cold (dropped before each run)
- Runs: 3
Results:
│ Mode │ Main (ops/sec) │ Current (ops/sec) │ Change │
│ Sync │ 8,901 │ 9,032 │ +1.5% │
│ Async │ 11,297 │ 11,947 │ +5.8% │
I further run db_stress test
```
make -j32 -f crash_test.mk J=32 blackbox_crash_test
```
Against my local machine for 60 minutes, on local flash, with async-io for multiscans always on.
Reviewed By: anand1976
Differential Revision: D91705195
Pulled By: krhancoc
fbshipit-source-id: acf2f944e8b715e99384c8cee79f8d241eadf5b8
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14282
Previously, `PollIfNeeded` returned `void` and silently ignored errors from
`fs_->Poll()` by calling `PermitUncheckedError()`. This could lead to silent
data corruption or unexpected behavior when Poll operations fail.
This diff changes `PollIfNeeded` to return `Status` and properly propagate
Poll errors to callers. When Poll fails:
1. The IO handle is cleaned up via `DestroyAndClearIOHandle`
2. The error status is returned to the caller
3. Callers (`HandleOverlappingAsyncData` and `PrefetchInternal`) now check
and propagate this error
Also adds a `TEST_SYNC_POINT_CALLBACK` to allow tests to inject Poll errors.
Reviewed By: anand1976
Differential Revision: D91624185
fbshipit-source-id: 8dd0ee6588ed1ce4bf080bcf857b778c5140ccf5
Summary:
This adds a new public API to allow applications to abort all running compactions and prevent new ones from starting. Unlike DisableManualCompaction() which only pauses manual compactions and waits for them to finish naturally, AbortAllCompactions() actively signals running compactions (both automatic and manual) to terminate early and waits for them to complete before returning.
The abort signal is checked periodically during compaction (every 100 keys), so ongoing compactions abort quickly. Any output files from aborted compactions are automatically cleaned up to prevent partial results from being installed.
This is useful for scenarios where applications need to quickly stop all compaction activity, such as during graceful shutdown or when performing maintenance operations.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14227
Test Plan:
- Unit tests in db_compaction_abort_test.cc cover various abort scenarios including: abort before/during compaction, abort with multiple subcompactions, nested abort/resume calls, abort with CompactFiles API, abort across multiple column families, and timing guarantees
- Updated compaction_job_test.cc to include the new parameter
Reviewed By: anand1976
Differential Revision: D91480994
Pulled By: xingbowang
fbshipit-source-id: 36837971d8a540cd34d3ec28a78bc94b582625b0
Summary:
When building a Release on Windows RTTI is not available, so asserts that use dynamic_cast need to be disabled
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14280
Reviewed By: nmk70
Differential Revision: D91807791
Pulled By: mszeszko-meta
fbshipit-source-id: e29c19c757bcd076a1f09ed40b306bb50ba9e882
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14278
Remove dead code from `BlobDBImpl`:
- `debug_level_ `member and associated unreachable debug logging
- `CopyBlobFiles()` - private method that was never called
- `FileDeleteOk_SnapshotCheckLocked()` - declared but never implemented
- `RemoveTimerQ()` - declared but never implemented
Remove unused includes:
- rocksdb/wal_filter.h from blob_db_impl.h
- rocksdb/utilities/transaction.h from blob_db_impl.cc
- table/meta_blocks.h from blob_db_impl.cc
- util/random.h from blob_db_impl.cc
Remove from BlobFile:
- `GetColumnFamilyId()` - declared/implemented but never called
Reviewed By: xingbowang
Differential Revision: D91089144
fbshipit-source-id: d9bce24122b3bb790644fe4e51ce4403c77a1abf
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14277
Remove `GetBlobDBOptions()` and `SyncBlobFiles()` from the public `BlobDB` interface. These methods were only used internally or in tests and are not needed by any production code. `GetBlobDBOptions()` is now replaced by storing bdb_options_ as a member in the test class. `SyncBlobFiles()` is moved to private in `BlobDBImpl` since it's only called internally. Also remove unused `kDeleteCheckPeriodMillisecs` constant.
Reviewed By: xingbowang
Differential Revision: D91089111
fbshipit-source-id: 9c92b6d9563cf241c69d8880b418e8bcb7acb6c5
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14276
No production user of Legacy overrides the default value of `bytes_per_sync`. Replace the option with a constant `kBytesPerSync` to further reduce legacy blob db customizability / configuration surface.
Reviewed By: xingbowang
Differential Revision: D91089096
fbshipit-source-id: 162df65646a4f3a3fab3586cf6ff223e1917d86e
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14275
No one ever sets `blob_dir` to a non-default value. Replace the configurable `blob_dir` option with a constant `kBlobDirName`. This simplifies the code and further reduces the configurability surface.
Reviewed By: xingbowang
Differential Revision: D91089039
fbshipit-source-id: 7d82e86415cc4bc89a7fe1399c29d4cc3058d1de
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14273
The `path_relative` option in `BlobDBOptions` was never used in practice - all
production deployments use the default value of `true` (relative path). The
absolute path mode (`path_relative = false`) was essentially unsupported:
- `GetLiveFiles()` returned `NotSupported` for absolute paths
- `GetLiveFilesMetaData()` had an assertion that would crash for absolute paths
This change removes the option and simplifies the code to always use relative
paths for the blob directory.
Changes:
- Remove `path_relative` field from `BlobDBOptions`
- Simplify `blob_dir_` construction in `BlobDBImpl` constructor
- Simplify path construction in `DestroyBlobDB()`
- Remove `NotSupported` check in `GetLiveFiles()`
- Remove assertion in `GetLiveFilesMetaData()`
- Remove logging of `path_relative` in `Dump()`
- Remove redundant `path_relative = true` in tests
Reviewed By: xingbowang
Differential Revision: D91089016
fbshipit-source-id: 947b129e405a315b94ac73bc48b23103ba12d73b
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14272
All production deployments have the hardcoded (non-configurable) default for`garbage_collection_cutoff = 0.25`. This change removes the configurable option and replaces it with a fixed constant `kGarbageCollectionCutoff = 0.25`, simplifying the configuration surface.
Changes:
- Remove `garbage_collection_cutoff` from `BlobDBOptions`
- Add `kGarbageCollectionCutoff` constant (0.25) in blob_db_impl.cc
- Remove `--blob_db_gc_cutoff` flag from db_bench tool and db_stress
- Update tests to work with the fixed cutoff value
Reviewed By: xingbowang
Differential Revision: D91088998
fbshipit-source-id: 820fc7f1ad4c3fe8a15f22a92cd53fb96c56c6e1
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14270
Legacy BlobDB's inline values feature (storing small values directly in the LSM tree via `min_blob_size` threshold) is unused in production - all
deployments use `min_blob_size = 0`. This removes the functionality entirely.
Changes:
- Remove `min_blob_size` from `BlobDBOptions`
- Remove `IsInlined()` check from compaction filter (dead code path)
- Remove inline-related statistics (`BLOB_DB_WRITE_INLINED*`)
- Remove `InlineSmallValues` test
- Update stale comments referencing inlined data
Reviewed By: xingbowang
Differential Revision: D91088985
fbshipit-source-id: ec67848ece1a7dc071ca8e8a17faebb435394733
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14268
FIFO eviction is unused in production, making this dead code that adds complexity.
Core changes:
- Remove `is_fifo` from `BlobDBOptions`
- Remove `CheckSizeAndEvictBlobFiles` and related methods
- Remove `fifo_eviction_seq` and `evict_expiration_up_to` from `BlobCompactionContext`
CLI tool cleanup:
- Remove `--blob_db_is_fifo` flag from db_bench
- Remove stale FIFO eviction comments
Tests:
- Remove FIFO-related tests (`FIFOEviction_*`, `FilterForFIFOEviction`)
Note: TTL-based expiration (`EvictExpiredFiles`) is preserved as it handles blob file cleanup based on TTL, which is separate from FIFO eviction.
Reviewed By: xingbowang
Differential Revision: D91088968
fbshipit-source-id: 123df98d1132095cef15473b76011de030c5df34
Summary:
Work around a warning/linter false positive related to the use of string::insert. The code in question is legal C++, but GCC 12's libstdc++ implementation of string::insert internally uses memcpy, which can trigger undefined behavior warnings when the source and destination overlap.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14265
Reviewed By: pdillinger
Differential Revision: D91594561
Pulled By: mszeszko-meta
fbshipit-source-id: faa1487aba11a6581bf9ac8eb89442b6e4120427
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14257
Removes the 'unused' `PutUntil` API and updates `Put/PutWithTTL` to inline the previous implementation. Test helpers are updated to use `PutWithTTL` with computed TTL values instead.
Reviewed By: xingbowang
Differential Revision: D90900841
fbshipit-source-id: c6ab89fe32773f426b0bedc706bf5a2683ec31cf
Summary:
When running make check on aarch64, hash_test reports an integer underflows:
util/math.h:44:46: runtime error: signed integer overflow:
-2147483648 - 1 cannot be represented in type 'int'
util/math.h:44:46: runtime error: signed integer overflow:
-9223372036854775808 - 1 cannot be represented in type 'long long'
util/math.h:44:46: runtime error: signed integer overflow:
-9223372036854775808 - 1 cannot be represented in type 'long'
The issue is when BottomNBits(int32 value, 31) does not use BMI2, it executes the following:
return static_cast<T>(v & ((T{1} << nbits) - 1));
For int32_t, (1 << 31) is the minimum value, and -1 is an integer underflow. The fix is to cast T to an unsigned type and use that for the bit manipulation.
I used Compiler Explorer to verify that this still compiles to the BZHI instruction mentioned in the comment with -march=x86-64-v3: https://godbolt.org/z/8bcTE8xbf
To reproduce these errors on x86-64, disable the BMI code path:
```
USE_CLANG=1 PORTABLE=x86-64-v2 LDFLAGS=-fsanitize=undefined CXXFLAGS=-fsanitize=undefined make -j20 hash_test
```
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14231
Reviewed By: mszeszko-meta
Differential Revision: D91353147
Pulled By: pdillinger
fbshipit-source-id: 64cc191ccb9ecba20c260fab759e8881e30d2352
Summary:
Update HISTORY, version number, format compatible test, and folly version
folly build now depends on libaio
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14259
Test Plan: CI
Reviewed By: anand1976
Differential Revision: D91356493
Pulled By: pdillinger
fbshipit-source-id: 9d85960c647758d5cb33e3910e714e2f7785fd06
Summary:
Seeing many errors like this
```
Iterator diverged from control iterator which has value ...
iterator is not valid with status: IO error: Req failed: Unknown error -14
VerifyIterator failed. Control CF default
```
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14263
Test Plan: CI
Reviewed By: archang19
Differential Revision: D91478886
Pulled By: pdillinger
fbshipit-source-id: 94b955b6ecdb7a3cab39dac8e7b0d1047d49a0bb
Summary:
... in addition to those derived from samples. This could be useful when trade-offs favor an offline trained dictionary that's good for the whole work load, which can involve heavy-weight training, vs. on-the-fly training on samples for each file, which has limitations.
This involves some breaking changes to some deeper parts of the new compression API. I'm not concerned about performance because this doesn't touch the per-block parts of the API, just the per-file parts.
Bonus: change to
CompressionManagerWrapper::FindCompatibleCompressionManager to implement what is likely the preferred behavior.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14253
Test Plan: unit test included
Reviewed By: hx235
Differential Revision: D91082208
Pulled By: pdillinger
fbshipit-source-id: 1442db65e15c9435437204c19787c96f7a40a207
Summary:
Clearing DB dir for crash test is currently a hodgepodge of
1. Caller of db_crashtest.py maybe tries to clear the dir
2. db_crashtest.py tries to clear the dir in get_dbname() (but ignoring failure)
3. db_crashtest.py passes --destroy_db_initially to some db_stress calls as needed
4. db_crashtest.py tries to clear the dir between some db_stress calls
5. db_crashtest.py tries to clear the dir after everything is done and successful (no artifacts to investigate or save) (but ignoring failure)
6. Try to add more uniqueness to the directory from https://github.com/facebook/rocksdb/issues/14249
This change reverts or replaces 2, 4, 5, and 6 by doubling-down on (expanding) 3 and a small variant of it:
* crash_test.mk passes --destroy_db_initially=1 so that the first run of db_stress clears the db dir.
* After each db_stress invocation, db_crashtest.py resets destroy_db_initially=0 so that the next invocation reuses the same DB, except in cases where there is an incompatibility that requires a fresh DB (from cases 3 and 4 above).
* On success, uses new `db_stress --destroy_db_and_exit` option to clean up the DB dir without needing a custom cleanup_cmd (now ignored)
Note that although case 1 is likely obsolete, it is out of control of an open source PR.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14254
Test Plan: some manual runs
Reviewed By: xingbowang
Differential Revision: D91164731
Pulled By: pdillinger
fbshipit-source-id: 0a66c8c0e130c9eeacc55af411a18a09bc9debdf
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14248
### Overview
This diff introduces the addition of multi-scan statistics to RocksDB, enhancing the database's ability to monitor and analyze performance during multi-scan operations.
### Key Changes
#### Implemented Multi-Scan Statistics
The following statistics were implemented to provide deeper insights into multi-scan operations:
- **MULTISCAN_PREPARE_MICROS**: Measures the time (in microseconds) spent preparing for multi-scan operations.
- **MULTISCAN_BLOCKS_PER_PREPARE**: Tracks the number of blocks processed per multi-scan prepare operation.
- **Wasted Prefetch Blocks Count**: Counts the number of prefetched blocks that were not used (i.e., wasted) if the iterator is abandoned before accessing them.
- **MULTISCAN_TOTAL_BLOCKS_SCANNED**: Tracks the total number of blocks scanned during all multi-scan operations.
- **MULTISCAN_TOTAL_KEYS_SCANNED**: Measures the total number of keys scanned across all multi-scan operations.
- **MULTISCAN_TOTAL_MICROS**: Captures the total time (in microseconds) spent in multi-scan operations.
- **MULTISCAN_PREFETCHED_BLOCKS**: Counts the number of blocks that were prefetched during multi-scan operations.
- **MULTISCAN_USED_PREFETCH_BLOCKS**: Tracks the number of prefetched blocks that were actually used during multi-scan operations.
### Impact
This diff provides more fine-grained statistics for multi-scan operations, allowing developers and users to better understand and optimize the performance of their RocksDB instances.
Reviewed By: krhancoc
Differential Revision: D91053297
fbshipit-source-id: 7158741b9f026c0b5ce8ba1264dbd137e7fe985d
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14252
Fixed a bug in PosixFileSystem::AbortIO that could cause an infinite hang
when aborting multiple concurrent async IO handles.
The bug occurred in the completion processing loop: when an io_uring
completion arrived for a handle other than the one currently being waited
for (io_handles[i]), the code would increment that handle's req_count but
only mark it as finished if it also matched io_handles[i]. This meant
completions for other handles were consumed but those handles were never
marked as finished.
Later, when iterating to those handles, the code would enter
io_uring_wait_cqe expecting more completions, but they had already been
consumed - causing an infinite hang.
The fix aligns AbortIO's completion handling with what Poll() already does:
mark handles as finished whenever their completions arrive, regardless of
which handle we're currently waiting for in the outer loop. Only the break
statement remains conditional on matching io_handles[i].
Reviewed By: anand1976
Differential Revision: D91070044
fbshipit-source-id: 47faf5f0df3e26a2aa83444bbac623f43f560933
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14251
The AbortIO API documentation incorrectly stated that the callback
should NOT be called for aborted io_handles. However, the actual
implementation in fs_posix.cc does invoke the callback with
IOStatus::Aborted() status after cancelling requests:
```
// fs_posix.cc:1252-1260
if (posix_handle->req_count == 2 &&
static_cast<Posix_IOHandle*>(io_handles[i]) == posix_handle) {
posix_handle->is_finished = true;
FSReadRequest req;
req.status = IOStatus::Aborted();
posix_handle->cb(req, posix_handle->cb_arg);
break;
}
```
This change corrects the documentation to match the actual behavior
in RocksDB.
Reviewed By: anand1976
Differential Revision: D91073466
fbshipit-source-id: 47ae14a09e9386cc68049ca272d6b712f5a9bed7
Summary:
Since it's been > 6 months and we have production uses, migrate to fv=7 by default. One unit test needed an update for the change to table properties with fv=7.
On making this change, PresetCompressionDictTest tests detected extra memory usage by decompressing LZ4 with dictionary compression. This turned out to be a bug in `std::find` usage that led to using the ZSTD-optimized decompressor (with digested dictionary usage) in cases where it is not needed. I've fixed the bug and improved the unit tests that found the bug.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14239
Test Plan: existing tests, including format compatible CI job (updated, and run locally with SHORT_TEST=1)
Reviewed By: hx235
Differential Revision: D90728697
Pulled By: pdillinger
fbshipit-source-id: 8f1a0e9ca59a88c18eaa4cdfdea00309175ce30a
Summary:
Some of the stress tests script run tests multiple times with TEST_TMPDIR set. When TEST_TMPDIR is set, the db directory is a fixed string. This caused the same DB directory was reused across db_crashtest.py script run. Typically, the DB folder is cleaned up after db_crashtest.py complete. But sometimes, the clean up command could fail. This caused the DB folder to be reused across different db_crashtest.py runs. Meantime, each db_crashtest.py run would randomize some of the parameters. This caused different parameters to be used with same DB directory, violating some of the assumption such as use_put_entity_one_in parameter to be not changed between runs. This change added a suffix to DB directory, so that each db_crashtest.py script run would generate a unique DB directory, which prevents the clean up failure issue causing test flaky.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14249
Test Plan:
Stress test local run
```
TEST_TMPDIR=/tmp/aaa /usr/local/bin/python3 -u tools/db_crashtest.py --stress_cmd=./db_stress --cleanup_cmd='' --simple blackbox --duration 15 --interval 10
>>> Running db_stress with pid=113810: ./db_stress ... --db=/tmp/aaa/rocksdb_crashtest_blackbox_6967584463401575611 ...
```
Reviewed By: hx235
Differential Revision: D91069655
Pulled By: xingbowang
fbshipit-source-id: 327fc3cd0d8e3ef4b49e182e21bcd91a10647710
Summary:
Problem
The TEST_WaitForCompact in
TimestampCompatibleCompactionTest.UdtTombstoneCollapsingTest would sometimes
run forever, indicating an infinite compaction loop.
Issue https://github.com/facebook/rocksdb/issues/14223
Root Cause
In ComputeBottommostFilesMarkedForCompaction(), files were marked for
bottommost compaction based only on the condition largest_seqno <
oldest_snapshot_seqnum. However, for User-Defined Timestamps (UDT) columns,
compaction can only zero sequence numbers when the file's maximum timestamp is
below full_history_ts_low.
When timestamps were above this threshold:
1. File gets marked for compaction (seqno condition met)
2. Compaction runs but cannot zero seqno (timestamp condition not met)
3. Output file immediately gets re-marked for compaction
4. Infinite loop
Solution
Added timestamp range tracking to FileMetaData and updated the marking logic to
check timestamps before marking files.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14228
Test Plan: Unit test
Reviewed By: pdillinger
Differential Revision: D90586045
Pulled By: xingbowang
fbshipit-source-id: addfa4f988db8c87fb513a1bf58ee54623a6c210
Summary:
Surface async read errors instead of asserting on them. This makes it easier to debug stress test failures. Async reads can fail for legitimate reasons, such as fs errors.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14244
Reviewed By: hx235
Differential Revision: D90878515
Pulled By: anand1976
fbshipit-source-id: 6335d4b06ddf250b26842ce94e3f5263356b2695
Summary:
These will be useful for qualifying non-tiered workloads for tiered storage.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14243
Test Plan:
unit test included
I'm not concerned about performance because this fits pretty nicely into some existing code and only adds overhead when (expensive) IOs are done.
Reviewed By: jaykorean
Differential Revision: D90870348
Pulled By: pdillinger
fbshipit-source-id: 984411123bcd54c249a949da813ff04fedacc6a4
Summary:
To move away from OLD_CompressData / OLD_UncompressData. Also improved some error/warning messages.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14241
Test Plan: manual tests showing similar performance, runs with ASAN/UBSAN to check for issues
Reviewed By: hx235
Differential Revision: D90793708
Pulled By: pdillinger
fbshipit-source-id: e0655f7bed8d85e5ea110167dca73c6664f7465b
Summary:
Trying to get rid of uses of OLD_CompressData / OLD_UncompressData. Some performance optimizations and corrections for better accounting also.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14230
Test Plan:
* exanded unit test to be more complete / rigorous
* manual before-and-after db_bench runs with the option, seeing table properties as expected
Reviewed By: hx235
Differential Revision: D90545476
Pulled By: pdillinger
fbshipit-source-id: 2f7c577574bcc4b2acafa002761ec1cad7fdb093
Summary:
as part of the effort to get rid of OLD_CompressData and OLD_UncompressData and the old implementations in compression.h.
It's unfortunate the the existing blob file schema doesn't allow storing blobs uncompressed when the compressed version is larger, so we have to work around that.
Note that use of GrowableBuffer in place of std::string is intended to avoid the potential performance overhead of zeroing out memory before overwriting it.
Also includes some cleanup of includes
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14234
Test Plan:
some unit test updates as needed. Crash test covers integrated blob support.
I'm not too concerned about performance, as until a future schema change, this code is committing the grave performance error of storing compressed data larger than uncompressed.
Reviewed By: mszeszko-meta, hx235
Differential Revision: D90544049
Pulled By: pdillinger
fbshipit-source-id: 2f2ed16de63990b797cc06c8dad36b5869dac302
Summary:
Deadlock or timeout is possible in TestPut, when TestMultiGet was executed at the same time, because it executes MaybeAddKeyToTxnForRYW, which writes to the same key space but does not acquire stress test level mutex. Therefore, RocksDB could return deadlock error.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14235
Test Plan: Stress test
Reviewed By: hx235
Differential Revision: D90621772
Pulled By: xingbowang
fbshipit-source-id: eb808193ded06b69a8161320f88d5ba4e20b4901
Summary:
Folly download dependencies directly from external source. Sometimes, this could fail due to external website instability. To solve this, we added github cache to cache the dependencies. We also added a python script to try different sources during download to reduce the chance of failure.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14226
Test Plan: github CI
Reviewed By: krhancoc, archang19
Differential Revision: D90343051
Pulled By: xingbowang
fbshipit-source-id: 3faad6aaa6c1bfd361b9e405c298856cd64bf457
Summary:
Currently to set options for multiple CFs, the caller must repeatedly call SetOptions() for each CF. This in turn serializes the entire options file each time. This PR exposes a new API that allows SetOptions to be called on multiple CFs at once, thus only paying the OPTIONS file serialization once.
Also added a new unit test for SetOptions.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14201
Reviewed By: pdillinger
Differential Revision: D89735181
Pulled By: joshkang97
fbshipit-source-id: 9b7a721b7e8769b653243b1581678ffd05d038e8
Summary:
This diff introduces the IO Dispatcher, which will be used to simplify the code path for MultiScan, while further providing a centralized place to enact policy on how MultiScan is done (i.e., limit memory usage and pinned buffers for example). Right now this diff only encapsulates the functionality done during the Prepare of MultiScan.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14135
Reviewed By: anand1976
Differential Revision: D87837261
Pulled By: krhancoc
fbshipit-source-id: 2698910ade02bc3d182413ae07ce69fe7abb7ec5
Summary:
As compaction scheduling is not deterministic, the existing check is too strict sometimes, causing test to be flaky.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14220
Test Plan: Unit test
Reviewed By: pdillinger
Differential Revision: D90143556
Pulled By: xingbowang
fbshipit-source-id: 6780423c63324a4b20fc8b8ccac2051a094c9f4a
Summary:
My PR https://github.com/facebook/rocksdb/issues/14195 regressed a case in which db_crashtest.py calling db_stress with --destroy_db_initially=1 could lead to dbname directory being nonexistant for subsequent calls to gen_cmd -> finalize_and_sanitize -> is_direct_io_supported which would fail in creating a temporary file. Fix this (and clean up existing related code) using os.makedirs.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14219
Test Plan: I don't have a good reproducer for the error but some manual testing indicates this change is at least safe
Reviewed By: virajthakur
Differential Revision: D90138248
Pulled By: pdillinger
fbshipit-source-id: 0ed6524cd50f8632346a8583f26bf1f4941817ce
Summary:
* Some existing commentary and motivation around my atomic wrappers in atomic.h was based on a misreading of documentation. seq_cst *is* a safe substitute for acq_rel in all cases. I still like having a distinct type for RelaxedAtomic (as folly does) and a wrapper also for other cases to avoid readability traps like implicit conversion and implicit memory order. This PR is only comment changes and renaming.
* Create a blog post about bit fields API to help with lock-free (and low-lock) programming.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14213
Test Plan: esiting tests
Reviewed By: xingbowang
Differential Revision: D89971581
Pulled By: pdillinger
fbshipit-source-id: 9bd1181d692258d668189c2da8bd0e5d98fd6230
Summary:
This change builds on https://github.com/facebook/rocksdb/issues/14027 and https://github.com/facebook/rocksdb/issues/13965 to complete migration
of the HyperClockCache implementation to using the hygienic BitFields API.
No semantic change in the implementation details is intended, just
greatly improving readability and safety of the code while maintaining
the same performance.
In more detail,
* Refactor the main metadata atomic for each slot in an HCC table into
SlotMeta using BitFields.
* Extended BitFields APIs with some additional features, and renamed
BlahTransform classes to BlahTransformer to resolve potential naming
conflicts with member functions to create them.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14154
Test Plan:
for correctness, mostly existing tests. but also added tests
for new BitFields features. I especially ran local TSAN whitebox crash
test extensively which caught a couple of refactoring errors.
For performance, I verified with release builds of cache_bench, using
default options, that there was no noticeable/consistent difference
after all these HCC migrations vs. backing them out. That test was with
GCC 11 and -O2, which is a reasonable baseline for expected compiler
optimizations.
Reviewed By: xingbowang
Differential Revision: D87960540
Pulled By: pdillinger
fbshipit-source-id: e0257b7fea8a5c7709daef18911959201ce4e0f3
Summary:
This bug caused seqno to be incorrectly zeroed when UDT is enabled. This is one of the contributing factor that caused tombstones to be accumulated at bottommost level, causing high space amp.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14207
Test Plan: Unit test
Reviewed By: pdillinger
Differential Revision: D89826564
Pulled By: xingbowang
fbshipit-source-id: 62ab1e37c36ae1ed95f26213c97a591a17e962a6
Summary:
## Context
1. OpenAndCompact required CompactionServiceOptionsOverride
2. Currently there are no C APIs to create CompactionServiceOptionsOverride
## Changes
1. Create C API for compactionServiceOptionsOverride
2. Create helper function to create compactionServiceOptionsOverride from Options. This was added in because The C API lacks getter methods for non-serializable options (comparator, table_factory, etc.). Without this, users would need to maintain separate references to all these options just to pass them to the override. If the user need to create a new comparator or table factory then C API for compactionServiceOptionsOverride already as the setters for the same.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14183
Reviewed By: hx235
Differential Revision: D89690005
Pulled By: jaykorean
fbshipit-source-id: efe8211feec9d144b32be0f5e66c8cf8bde8dac0
Summary:
Let db_crashtest.py work with TEST_TMPDIR on remote filesystem, by infering whether it's remote from the env_uri argument. Note that some other paths passed to db_stress are local paths and we can't reuse TEST_TMPDIR for those cases when it's remote.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14195
Test Plan: public and private CI
Reviewed By: archang19
Differential Revision: D89590246
Pulled By: pdillinger
fbshipit-source-id: db6eb9c16d4e76617183780747353c798cc9bef6
Summary:
Causing failures and not yet supported. Also putting a note in db.h about the combination being unsupported.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14189
Test Plan: started up blackbox_crash_test_with_ts many times and checked command line to be confident it's excluded.
Reviewed By: hx235
Differential Revision: D89297971
Pulled By: pdillinger
fbshipit-source-id: c5134351d9ecb37879c7e3319c17dd9228d7f12a
Summary:
`PosixRandomAccessFile::MultiRead` was introduced in Dec 2019 in https://github.com/facebook/rocksdb/pull/5881. Subsequently, 2 years after, we introduced the `PosixRandomAccessFile::ReadAsync` API in https://github.com/facebook/rocksdb/pull/9578, which was reusing the same `PosixFileSystem` IO ring as `MultiRead` API, consequently writing to the very same ring's submission queue (without waiting!). This 'shared ring' design is problematic, since sequentially interleaving `ReadAsync` and `MultiRead` API calls on the very same thread might result in reading 'unknown' events in `MultiRead` leading to `Bad cqe data` errors (and therefore falsely perceived as a corruption) - which, for some services (running on local flash), in itself is a hard blocker for adopting RocksDB async prefetching ('async IO') that heavily relies on the `ReadAsync` API. This change aims to solve this problem by maintaining separate thread local IO rings for `async reads` and `multi reads` assuring correct execution. In addition, we're adding more robust error handling in form of retries for kernel interrupts and draining the queue when process is experiencing terse memory condition. Separately, we're enhancing the performance aspect by explicitly marking the rings to be written to / read from by a single thread (`IORING_SETUP_SINGLE_ISSUER` [if available]) and defer the task just before the application intends to process completions (`IORING_SETUP_DEFER_TASKRUN` [if available]). See https://man7.org/linux/man-pages/man2/io_uring_setup.2.html for reference.
## Benchmark
**TLDR**
There's no evident advantage of using `io_uring_submit` (relative to proposed `io_uring_submit_and_wait`) across batches of size 10, 250 and 1000 simulating significantly-less, close-to and 4x-above `kIoUringDepth` batch size. `io_uring_submit` might be more appealing if (at least) one of the IOs is slow (which was NOT the case during the benchmark). More notably, with this PR switching from `io_uring_submit_and_wait` -> `io_uring_submit` can be done with a single line change due to implemented guardrails (we can followup with adding optional config for true ring semantics [if needed]).
**Compilation**
```
DEBUG_LEVEL=0 make db_bench
```
**Create DB**
```
./db_bench \
--db=/db/testdb_2.5m_k100_v6144_16kB_LZ4 \
--benchmarks=fillseq \
--num=2500000 \
--key_size=100 \
--value_size=6144 \
--compression_type=LZ4 \
--block_size=16384 \
--seed=1723056275
```
**LSM**
* L0: 2 files, L1: 5, L2: 49, L3: 79
* Each file is roughly ~35M in size
### MultiReadRandom (with caching disabled)
Each run was preceded by OS page cache cleanup with `echo 1 | sudo tee /proc/sys/vm/drop_caches`.
```
./db_bench \
--use_existing_db=true \
--db=/db/testdb_2.5m_k100_v6144_16kB_LZ4 \
--compression_type=LZ4 \
--benchmarks=multireadrandom \
--num= **<N>** \
--batch_size= **<B>** \
--io_uring_enabled=true \
--async_io=false \
--optimize_multiget_for_io=false \
--threads=4 \
--cache_size=0 \
--use_direct_reads=true \
--use_direct_io_for_flush_and_compaction=true \
--cache_index_and_filter_blocks=false \
--pin_l0_filter_and_index_blocks_in_cache=false \
--pin_top_level_index_and_filter=false \
--prepopulate_block_cache=0 \
--row_cache_size=0 \
--use_blob_cache=false \
--use_compressed_secondary_cache=false
```
| B=10; N=100,000 | B = 250; N=80,000 | B = 1,000; N=20,000
-- | -- | -- | --
baseline | 31.5 (± 0.4) us/op | 17.5 (± 0.5) us/op | 13.5 (± 0.4) us/op
io_uring_submit_and_wait | 31.5 (± 0.6) us/op | 17.7 (± 0.4) us/op | 13.6 (± 0.4) us/op
io_uring_submit | 31.5 (± 0.6) us/op | 17.5 (± 0.5) us/op | 13.4 (± 0.45) us/op
### Specs
| Property | Value
-- | --
RocksDB | version 10.9.0
Date | Tue Dec 9 15:57:03 2025
CPU | 56 * Intel Sapphire Rapids (T10 SPR)
Kernel version | 6.9.0-0_fbk12_0_g28f2d09ad102
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14158
Reviewed By: anand1976
Differential Revision: D88172809
Pulled By: mszeszko-meta
fbshipit-source-id: 5198de3d2f18f76fee661a2ec5f447e79ba06fbd
Summary:
**Context/Summary:**
Truncated range deletion in input files can be output by CompactionIterator with type kMaxValid instead of kTypeRangeDeletion, to satisfy ordering requirement between the truncated range deletion start key and a file's point keys. There was a plan to skip such key in https://github.com/facebook/rocksdb/pull/14122 but blockers remain to fulfill the plan.
Resumable compaction is not able to handle resumption from range deletion well at this point and should consider kMaxValid type same as kTypeRangeDeletion for resumption. Previously, it didn't and mistakenly allow resumption from a delete range. That led to an assertion failure, complaining about lacking information to update file boundaries in the presence of range deletion needed during cutting an output file, after the compaction resumes from that delete range and happens to cut the output file shortly after without any point keys in between.
```
frame https://github.com/facebook/rocksdb/issues/9: 0x00007f4f4743bc93 libc.so.6`__GI___assert_fail(assertion="meta.smallest.size() > 0", file="db/compaction/compaction_outputs.cc", line=530, function="rocksdb::Status rocksdb::CompactionOutputs::AddRangeDels(rocksdb::CompactionRangeDelAggregator&, const rocksdb::Slice*, const rocksdb::Slice*, rocksdb::CompactionIterationStats&, bool, const rocksdb::InternalKeyComparator&, rocksdb::SequenceNumber, std::pair<long unsigned int, long unsigned int>, const rocksdb::Slice&, const string&)") at assert.c:101:3
frame https://github.com/facebook/rocksdb/issues/10: 0x00007f4f4808c68c librocksdb.so.10.9`rocksdb::CompactionOutputs::AddRangeDels(this=0x00007f4f0c27e1a0, range_del_agg=0x00007f4f0c21ecc0, comp_start_user_key=0x0000000000000000, comp_end_user_key=0x0000000000000000, range_del_out_stats=0x00007f4f0dffa140, bottommost_level=false, icmp=0x00007f4ef4c93040, earliest_snapshot=13108729, keep_seqno_range=<unavailable>, next_table_min_key=0x00007f4ef4c8f540, full_history_ts_low="") at compaction_outputs.cc:530:7
frame https://github.com/facebook/rocksdb/issues/11: 0x00007f4f480480dd librocksdb.so.10.9`rocksdb::CompactionJob::FinishCompactionOutputFile(this=0x00007f4f0dffb890, input_status=<unavailable>, prev_table_last_internal_key=0x00007f4f0dffa650, next_table_min_key=0x00007f4ef4c8f540, comp_start_user_key=0x0000000000000000, comp_end_user_key=0x0000000000000000, c_iter=0x00007f4ef4c8f400, sub_compact=0x00007f4f0c27e000, outputs=0x00007f4f0c27e1a0) at compaction_job.cc:1917:31
```
This PR simply prevents MaxValid from being a resumption point like regular range deletion - see commit 842d66eb18ea67e965d6acb1fce12c18eeb778d2
Besides that, the PR also improves the testing, variable naming, logging in resumable compaction codes that were needed to debug this assertion failure - see commit https://github.com/facebook/rocksdb/pull/14184/commits/aecd4e7f971f6dd4df672d9e5f1409fe4747c561. These improvements are covered by existing tests.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14184
Test Plan:
- The stress initially surfaced the error. Using the exact same LSM shapes and files that were used in stress test but in a unit test, I'm able to get a deterministic repro and confirmed the fix resolves the error. This is the repro test https://github.com/hx235/rocksdb/commit/1075936e693c68c960761855900c53f5b894f57a
```
./compaction_service_test --gtest_filter=ResumableCompactionServiceTest.CompactSpecificFilesFromExistingDBWithCancelAndResume
# Before fix
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from ResumableCompactionServiceTest
[ RUN ] ResumableCompactionServiceTest.CompactSpecificFilesFromExistingDBWithCancelAndResume
compaction_service_test: db/compaction/compaction_outputs.cc:530: rocksdb::Status rocksdb::CompactionOutputs::AddRangeDels(rocksdb::CompactionRangeDelAggregator&, const rocksdb::Slice*, const rocksdb::Slice*, rocksdb::CompactionIterationStats&, bool, const rocksdb::InternalKeyComparator&, rocksdb::SequenceNumber, std::pair<long unsigned int, long unsigned int>, const rocksdb::Slice&, const string&): Assertion `meta.smallest.size() > 0' failed.
Received signal 6 (Aborted)
Invoking GDB for stack trace...
[New LWP 2621610]
[New LWP 2621611]
[New LWP 2621612]
[New LWP 2621613]
[New LWP 2621614]
[New LWP 2621630]
[New LWP 2621631]
# After fix
Note: Google Test filter = ResumableCompactionServiceTest.CompactSpecificFilesFromExistingDBWithCancelAndResume
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from ResumableCompactionServiceTest
[ RUN ] ResumableCompactionServiceTest.CompactSpecificFilesFromExistingDBWithCancelAndResume
[ OK ] ResumableCompactionServiceTest.CompactSpecificFilesFromExistingDBWithCancelAndResume (4722 ms)
[----------] 1 test from ResumableCompactionServiceTest (4722 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (4722 ms total)
[ PASSED ] 1 test.
```
- Follow-up: I tried a couple time to coerce the truncated range delete from scratch in the unit test but failed doing so. Considering kMaxValid may not be outputted by compaction iterator anymore after https://github.com/facebook/rocksdb/pull/14122/files gets landed again (and obsolete the bug) ADN the simple nature of this fix 842d66eb18ea67e965d6acb1fce12c18eeb778d2 AND the worst case of such fix going wrong is just less resumption, I decided to leave writing a unit test to coerce truncated ranged deletion from scratch a follow-up. Maybe I will draw inspiration from https://github.com/facebook/rocksdb/pull/14122/files.
Reviewed By: jaykorean
Differential Revision: D88912663
Pulled By: hx235
fbshipit-source-id: 80a01135684c8fea659650faaa00c2dc452c482a
Summary:
**Context/Summary:**
Stress test flag printed by db_crashtest.py like `./db_stres ....-secondary_cache_uri=compressed_secondary_cache://capacity=8388608;enable_custom_split_merge=true --otherflags=xxxx` is not copy-paste-run friendly. Directly running this command will cause parsing hiccups due to special characters like // or ;. This PR made the db_crashtest.py print a single-quoted value so at least the copy-paste-run works for unix-like shell (the most common case).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14180
Test Plan:
`python3 tools/db_crashtest.py --simple blackbox ...` display the following
Before fix, no single-quoted
```
Use random seed for iteration 9698536012932546857
Running db_stress with pid=1280640:./db_stress --secondary_cache_uri=compressed_secondary_cache://capacity=8388608;enable_custom_split_merge=true ...
// Directly copy, paste and run the ./db_stress command will encounter
Error: Read(-readpercent=0)+Prefix(-prefixpercent=0)+Write(-writepercent=45)+Delete(-delpercent=0)+DeleteRange(-delrangepercent=30)+Iterate(-iterpercent=40)+CustomOps(-customopspercent=0) percents != 100!
bash: --set_options_one_in=0: command not found
```
After fix, has single-quoted
```
se random seed for iteration 6017815530972723112
Running db_stress with pid=1234632: ./db_stress --secondary_cache_uri='compressed_secondary_cache://capacity=8388608;enable_custom_split_merge=true' ....
// Directly copy, paste and run the ./db_stress command is fine
```
Reviewed By: archang19
Differential Revision: D88688584
Pulled By: hx235
fbshipit-source-id: 88b8b2de7c2c5619b6e19900f4144dcd8e032f7b
Summary:
r? cbi42
Exposes RocksDB's remote compaction functionality through the C API, enabling C/FFI clients (Go, Rust, Python, etc.) to offload compaction work to remote workers.
## API Components
### Compaction Service
Create service with schedule, wait, cancel, and on_installation callbacks
Ownership transfers to options object (auto-destroyed, no manual cleanup)
### Job Info (13 getters)
DB/CF metadata and compaction details (priority, reason, levels, flags)
### Schedule Response
Create with job ID and status (validated with errptr)
Status: success, failure, aborted, use_local
### OpenAndCompact (for remote workers)
Execute compaction on worker node with environment/comparator overrides
Cancellation support via atomic flags
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14136
Reviewed By: hx235
Differential Revision: D88316558
Pulled By: jaykorean
fbshipit-source-id: 60a0fee69ff1e650dd785d96ec656649263214f8
Summary:
Crash tests have been failing of late with this assertion failure - db_stress: `./table/block_based/block_based_table_iterator.h:656: void rocksdb::BlockBasedTableIterator::PrepareReadAsyncCallBack(rocksdb::FSReadRequest &, void *): Assertion `async_state->status.IsAborted()' failed.` Instead of asserting, surface the failure status so we can troubleshoot.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14171
Reviewed By: xingbowang
Differential Revision: D88396654
Pulled By: anand1976
fbshipit-source-id: 8d59d7ace0c522c17b7af17c50e16af876911bad
Summary:
To help find potential issues not showing up in ARM unit tests. I'm running it with and without TransactionDB (write-committed) for better coverage. The job expands the size of /dev/shm for adequate space on maximum performance storage, and adds swap space to reduce risk of OOM in case we fill that up.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14172
Test Plan: earlier drafts of this PR added the job to PR jobs, and the last before putting in "nightly" can be seen here: https://github.com/facebook/rocksdb/actions/runs/19945493840/job/57193797390?pr=14172
Reviewed By: archang19
Differential Revision: D88429479
Pulled By: pdillinger
fbshipit-source-id: bd4d9cda9256950c3c6c126c299a44dbbbc30c7e
Summary:
Fix missing const for arg of OptionChangeMigration
We switched from std::string to std::string & for API OptionChangeMigration, which caused const qualifier to be lost at call site, which causes compilation failure.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14173
Test Plan: Unit test
Reviewed By: pdillinger
Differential Revision: D88431457
Pulled By: xingbowang
fbshipit-source-id: a705f3b80cc5ff56dab73aa6a31c940798d8df45
Summary:
Revert "Fix a bug where compaction with range deletion can persist kTypeMaxValid in file metadata (https://github.com/facebook/rocksdb/issues/14122)"
Add a new unit test to capture the situation found by stress test
This reverts commit 8c7c8b8dab.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14170
Test Plan: Unit Test
Reviewed By: anand1976
Differential Revision: D88395956
Pulled By: xingbowang
fbshipit-source-id: 226649dc79a86010ad326ffb2eae35109dc96bc4
Summary:
Continuing work from https://github.com/facebook/rocksdb/issues/13965. Here I'm migrating the "next with shift" kind of bit field and for that I've added an API for atomic additive transformations that can be combined into a single atomic update for multiple fields. (I implemented more features than needed, just in case they are needed someday and to demonstrate what is possible.)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14027
Test Plan: BitFields unit test updated/added, existing HCC tests
Reviewed By: xingbowang
Differential Revision: D83895094
Pulled By: pdillinger
fbshipit-source-id: e4487f34f5607b20f94b85a645ca654e6401e35d
Summary:
I want to reduce the time from when we call `StopBackup` to `CreateNewBackup` returning `BackupStopped`. We already check for the `stop_backup_` inside `CopyOrCreateFile` and `ReadFileAndComputeChecksum`, but we should add a check at the top of these methods to abort immediately. This could help save some latency from the file system metadata operations, like creating the sequential file and writable file.
We also want to update the API documentation for `StopBackup` which currently does not indicate that once it is called, all subsequent requests to create backups will fail.
In a follow up PR, we should also add coverage of `StopBackup` to the crash tests.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14129
Test Plan:
We were missing unit test coverage for `StopBackup`. I added test cases which cancel backups at different points in time.
Once this change is rolled out to production, we can monitor the DB close latencies, which depend on first cancelling ongoing backups
Reviewed By: pdillinger
Differential Revision: D87356536
Pulled By: archang19
fbshipit-source-id: 687094a41f096f6a156be65b2cce0b5054fb26f2
Summary:
Support ccache in make file
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14123
Test Plan: local build
Reviewed By: cbi42
Differential Revision: D87332892
Pulled By: xingbowang
fbshipit-source-id: 2088bd19bdab1bd7070734c886200be80f1a65af
Summary:
... from https://github.com/facebook/rocksdb/issues/14140. The assertion in the default implementation of CompressorWrapper::MaybeCloneSpecialized() could fail because this wrapper wasn't overriding it when it should. (See the NOTE on that implementation.)
Because this release already has a breaking modification to the Compressor API (adding Clone()), I took this opportunity to add 'const' to MaybeCloneSpecialized(). Also marked some compression classes as 'final' that could be marked as such.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14150
Test Plan: unit test expanded to cover this case (verified failing before). Audited the rest of our CompressorWrappers.
Reviewed By: archang19
Differential Revision: D87793987
Pulled By: pdillinger
fbshipit-source-id: 61c4469b84e4a47451a9942df09277faeeccfe63
Summary:
This change enables a custom CompressionManager / Compressor to adopt custom handling for data and index blocks. In particular, index blocks for format_version >= 4 use a distinct variant of the block format. Thus, a potentially format-aware compression algorithm such as OpenZL should be told which kind of block we are compressing. (And previously I avoided passing block type in CompressBlock for efficient handling of things like dictionaries but also avoiding checks on every CompressBlock call.)
Most of the change is in BlockBasedTableBuilder to call MaybeCloneSpecialized for both kDataBlock and for kIndexBlock. But I also needed some small tweaks/additions to the public API also:
* Require a Clone() function from Compressors, to support proper implementations of MaybeCloneSpecialized() in wrapper Compressors.
* Assert that the default implementation of CompressorWrapper::MaybeCloneSpecialized() is only used in allowable cases.
* Convenience function Compressor::CloneMaybeSpecialized()
This also fixes a serious bug/oversight in ManagedPtr for (ManagedWorkingArea) that somehow wasn't showing up before. It probably doesn't need a release note because CompressionManager stuff is still considered experimental.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14140
Test Plan: Greatly expanded DBCompressionTest.CompressionManagerWrapper to make sure the distinction between data blocks and index blocks is properly communicated to a custom CompressionManager/Compressor. The test includes processing the expected structure of data and index blocks, to serve as a tested example for structure-aware compressors.
Reviewed By: hx235
Differential Revision: D87600019
Pulled By: pdillinger
fbshipit-source-id: 252ef78910073a0e45f2c81dd45ac87ff8a41fc6
Summary:
Range deletion start keys are considered during compaction for cutting output files. Due to some ordering requirement (see comment above InsertNextValidRangeTombstoneAtLevel()) between truncated range deletion start key and a file's point keys, there was logic in https://github.com/facebook/rocksdb/blob/f6c9c3bf1cf05096e8ff8c03ded60c1e199edbb7/db/range_del_aggregator.cc#L39 that changes the value type to be kTypeMaxValid. However, kTypeMaxValid is not supposed to be persisted per https://github.com/facebook/rocksdb/blob/f6c9c3bf1cf05096e8ff8c03ded60c1e199edbb7/db/dbformat.h#L75-L76. This can cause forward compatibility issues reported in https://github.com/facebook/rocksdb/issues/14101. This PR fixes this issue by removing the logic that sets kTypeMaxValid and always skip truncated range deletion start key in CompactionMergingIterator.
For existing SST files, we want to avoid using this kTypeMaxValid, so this PR also introduces a new placeholder value type. This allows us to re-strengthen the relevant value type checks (IsExtendedValueType()) that was loosen for kTypeMaxValid.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14122
Test Plan:
- a unit test that persists kTypeMaxValid before this fix
- crash test with frequent range deletion: `python3 ./tools/db_crashtest.py blackbox --delrangepercent=11 --readpercent=35`
- Generate SST files with 0x1A as value type (kTypeMaxValid before this change) in file metadata. Run ldb with the strengthened check in IsExtendedValueType() to dump the MANIFEST. It failed to parse MANIFEST as expected before this PR and succeeds after this PR.
```
Error in processing file /tmp/rocksdbtest-543376/db_range_del_test_2549357_6547198162080866792/MANIFEST-000005 Corruption: VersionEdit: new-file4 entry The file /tmp/rocksdbtest-543376/db_range_del_test_2549357_6547198162080866792/MANIFEST-000005 may be corrupted.
```
Reviewed By: pdillinger
Differential Revision: D87016541
Pulled By: cbi42
fbshipit-source-id: 9957a095db2cd9947463b403f352bd9a1fd70a76
Summary:
Fixing internal validator failure
```
Every project specific source file must contain a doc block with an appropriate copyright header. Unrelated files must be listed as exceptions in the Copyright Headers Exceptions page in the repo dashboard.
A copyright header clearly indicates that the code is owned by Meta. Every open source file must start with a comment containing "Meta Platforms, Inc. and affiliates"
https://github.com/facebook/rocksdb/blob/main/buckifier/targets_cfg.py:
The first 16 lines of 'buckifier/targets_cfg.py' do not contain the patterns:
(Meta Platforms, Inc. and affiliates)|(Facebook, Inc(\.|,)? and its affiliates)|([0-9]{4}-present(\.|,)? Facebook)|([0-9]{4}(\.|,)? Facebook)
```
While fixing the text to pass the linter, I took the opportunity to modify `format-diff.sh` script to add the copyright header automatically if missing in new files.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14143
Test Plan:
```
$> make format
```
**new python file**
```
build_tools/format-diff.sh
Checking format of uncommitted changes...
Checking for copyright headers in new files...
Added copyright header to build_tools/test.py
Copyright headers were added to new files.
Nothing needs to be reformatted!
```
**new header file**
```
build_tools/format-diff.sh
Checking format of uncommitted changes...
Checking for copyright headers in new files...
Added copyright header to db/db_impl/db_impl_jewoongh.h
Copyright headers were added to new files.
Nothing needs to be reformatted!
```
Reviewed By: hx235
Differential Revision: D87653124
Pulled By: jaykorean
fbshipit-source-id: 164322cfcd2c162bb3b41bb8f3bafefa3f20b695
Summary:
**Context/Summary:**
.. because verify_output_flags contains information of usage of paranoid_file_check that is currently not yet compatible with resumable remote compaction
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14139
Test Plan: Existing tests
Reviewed By: jaykorean
Differential Revision: D87582635
Pulled By: hx235
fbshipit-source-id: ef21223da53a0696fa3ca9b1617c2c1ee2e19878
Summary:
**Context/Summary:**
Due to double's 53-bit mantissa limitation, large uint64_t values lose precision when converted to double. Value equals to or smaller than UINT64_MAX (but greater than 2^64 - 1024) round up to 2^64 since rounding up results in less error than rounding down, which exceeds UINT64_MAX. `std::numeric_limits<uint64_t>::max() / op1 < op2` won't catch those cases. Casting such out-of-range doubles back to uint64_t causes undefined behavior. T
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14132
UndefinedBehaviorSanitizer: undefined-behavior options/cf_options.cc:1087:32 in
```
before the fix but not after.
Test Plan:
```
COMPILE_WITH_ASAN=1 COMPILE_WITH_UBSAN=1 CC=clang-18 CXX=clang++-18 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j55 db_stress
python3 tools/db_crashtest.py --simple blackbox --compact_range_one_in=5 --target_file_size_base=9223372036854775807 // Half of std::numeric_limits<uint64_t>::max()
```
It fails with
```
stderr:
options/cf_options.cc:1087:32: runtime error: 1.84467e+19 is outside the range of representable values of type 'unsigned long'
Reviewed By: pdillinger
Differential Revision: D87434936
Pulled By: hx235
fbshipit-source-id: 65563edf9faf732410bdba8b9e4b7fd61b958169
Summary:
I have been using sst_dump --command=recompress for some ad hoc automation for compression engineering and these new options help with that.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14133
Test Plan: manual
Reviewed By: hx235
Differential Revision: D87453635
Pulled By: pdillinger
fbshipit-source-id: 2ae54e13a9221ec27c6637fea16623465a9163ae
Summary:
Saw a mysterious failure of assertion
`assert(rep_->props.num_data_blocks == 0)` in
DBCompressionTest/CompressionFailuresTest.CompressionFailures/45. This seems to be caused by a parallel compression failure arriving after the emit thread has started Finish() but before the Flush() at the start of Finish(). We can fix this by relaxing the assertion to allow for the !ok() case. Testing revealed more ok() assertions that needed to be relaxed/moved.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14130
Test Plan: Added a sync point to inject a failure status in the right place and added to unit test to be sure the case is essentially covered. It would arguably be a more realistic test to force a particular thread interleaving but I believe simple is good here.
Reviewed By: hx235
Differential Revision: D87377709
Pulled By: pdillinger
fbshipit-source-id: 4bd465673b084afcc235688503d1c2f464eed32d
Summary:
**Context/Summary:**
This PR adds multi-cf support to option migration. The original implementation sets options, opens db, compacts files and reopens the db in almost all the three branches below. Such design makes expanding to multi-cf difficult as it needs to change all these places within each of the branch causing code redundancy.
```
Status OptionChangeMigration(std::string dbname, const Options& old_opts,
const Options& new_opts) {
if (old_opts.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
// LSM generated by FIFO compaction can be opened by any compaction.
return Status::OK();
} else if (new_opts.compaction_style ==
CompactionStyle::kCompactionStyleUniversal) {
return MigrateToUniversal(dbname, old_opts, new_opts);
} else if (new_opts.compaction_style ==
CompactionStyle::kCompactionStyleLevel) {
return MigrateToLevelBase(dbname, old_opts, new_opts);
} else if (new_opts.compaction_style ==
CompactionStyle::kCompactionStyleFIFO) {
return CompactToLevel(old_opts, dbname, 0, 0 /* l0_file_size */, true);
} else {
return Status::NotSupported(
"Do not how to migrate to this compaction style");
}
}
```
Therefore this PR
- Refactor the option migration implementation by moving the common parts into the high-level `OptionChangeMigration()` through `PrepareNoCompactionCFDescriptors()` and `OpenDBWithCFs()` so `MigrateAllCFs()` can focus on compaction only.
- Treat the original OptionChangeMigration() API as a special case of the multi-cf version option migration
- Add multiple-cf support
A few notes:
- CompactToLevel() originally modifies the compaction-related options conditionally before doing compaction. This is moved into earlier steps through `ApplySpecialSingleLevelSettings()` in `PrepareNoCompactionCFDescriptors()`
- MigrateToUniversal() originally opens the db twice with essentially the same option. This PR reduces that to one open
- Option migration does not always use the old option to compact the db and reopen the db after migration, see ` return CompactToLevel(new_opts, dbname, new_opts.num_levels - 1,/*l0_file_size=*/0, false);`. `PrepareNoCompactionCFDescriptors()` is where we handle those decisions.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14059
Test Plan:
- Existing UTs
- New UTs
Reviewed By: cbi42
Differential Revision: D84852970
Pulled By: hx235
fbshipit-source-id: 936b456cf9fb4c3ccb687e5d1387f2d67a1448be
Summary:
This diff introduces the async prepare of all iterators within a MultiScan. The current state has each iterator be prepared as its needed, and with this diff, we prepare all iterators during the prepare phase of the Level Iterator, this will allow more time for each IO to be dispatched and serviced, increasing the odds that a block is ready as the scan seeks to it.
Benchmark is prefilled using
```
KEYSIZE=64
VALUESIZE=512
NUMKEYS=5000000
SCAN_SIZE=100
DISTANCE=25000
NUM_SCANS=15
THREADS=1
./db_bench --db=$DB \
--benchmarks="fillseq" \
--write_buffer_size=5242880 \
--max_write_buffer_number=4 \
--target_file_size_base=5242880 \
--disable_wal=1 --key_size=$KEYSIZE \
--value_size=$VALUESIZE --num=$NUMKEYS --threads=32
}
```
And benchmark ran is
```
run() {
echo 1 | sudo tee /proc/sys/vm/drop_caches
./db_bench --db=$DB --use_existing_db=1 \
--benchmarks=multiscan \
--disable_auto_compactions=1 --seek_nexts=$SCAN_SIZE \
--multiscan-use-async-io=1 \
--multiscan-size=$NUM_SCANS --multiscan-stride=$DISTANCE \
--key_size=$KEYSIZE --value_size=$VALUESIZE \
--num=$NUMKEYS --threads=$THREADS --duration=60 --statistics
}
```
The benchmark uses large stride sides to ensure that two scans would touch separate files. We reduce the size of the block cache to increase likelyhood of reads (and simulate larger data sets)
**Branch:**
```
Integrated BlobDB: blob cache disabled
RocksDB: version 10.8.0
Date: Tue Nov 11 13:26:29 2025
CPU: 166 * AMD EPYC-Milan Processor
CPUCache: 512 KB
Keys: 64 bytes each (+ 0 bytes user-defined timestamp)
Values: 512 bytes each (256 bytes after compression)
Entries: 5000000
Prefix: 0 bytes
Keys per prefix: 0
RawSize: 2746.6 MB (estimated)
FileSize: 1525.9 MB (estimated)
Write rate: 0 bytes/second
Read rate: 0 ops/second
Compression: Snappy
Compression sampling rate: 0
Memtablerep: SkipListFactory
Perf Level: 1
------------------------------------------------
multiscan_stride = 25000
multiscan_size = 15
seek_nexts = 100
DB path: [/data/rocksdb/mydb]
multiscan : 837.941 micros/op 1193 ops/sec 60.001 seconds 71605 operations; (multscans:71605)
```
**Baseline:**
```
Set seed to 1762898809121995 because --seed was 0
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
RocksDB: version 10.9.0
Date: Tue Nov 11 14:06:49 2025
CPU: 166 * AMD EPYC-Milan Processor
CPUCache: 512 KB
Keys: 64 bytes each (+ 0 bytes user-defined timestamp)
Values: 512 bytes each (256 bytes after compression)
Entries: 5000000
Prefix: 0 bytes
Keys per prefix: 0
RawSize: 2746.6 MB (estimated)
FileSize: 1525.9 MB (estimated)
Write rate: 0 bytes/second
Read rate: 0 ops/second
Compression: Snappy
Compression sampling rate: 0
Memtablerep: SkipListFactory
Perf Level: 1
------------------------------------------------
multiscan_stride = 25000
multiscan_size = 15
seek_nexts = 100
DB path: [/data/rocksdb/mydb]
multiscan : 1129.916 micros/op 885 ops/sec 60.001 seconds 53102 operations; (multscans:53102)
```
Repeated for confirmation.
This introduces a ~20% improvement in latency and op/s.
Note: Benchmarks are single threaded as, when increasing thread count, we start seeing large amounts of overhead being induced by block cache contention, finally resulting in both baseline and branch becoming equal.
Further on network attached storage with high latency, the level iterator, preparing all iterators so a 20% improvement even at high thread counts.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14100
Reviewed By: anand1976
Differential Revision: D86913584
Pulled By: krhancoc
fbshipit-source-id: da9d0c890e25e392a33389ce6b80f9bfb84d3f85
Summary:
Oversight in https://github.com/facebook/rocksdb/issues/13964. More detail:
* Applies to cache_bench and db_bench (db_stress already using it)
* Make sure those along with db_stress treat "hyper_clock_cache" as "auto_hyper_clock_cache" because this is now the blessed implementation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14120
Test Plan: manual runs of the tools
Reviewed By: krhancoc
Differential Revision: D86913202
Pulled By: pdillinger
fbshipit-source-id: 07b425d3522103417f4b034735376b9d759af5fb
Summary:
Right now, in Java's Get() calls, the way Get() is treated is inefficient. Status.NotFound is turned into an exception in the JNI layer, and is caught in the same function to turn into not found return. This causes significant overhead in the scenario where most of the queries ending up with not found. For example, in Spark's deduplication query, this exception creation overhead is higher than Get() itself. With the proposed change, if return status is NotFound, we directly return, rather than going through the exception path
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14095
Test Plan: Existing tests should cover all Get() cases, and they are passing.
Reviewed By: jaykorean
Differential Revision: D86797594
Pulled By: cbi42
fbshipit-source-id: 1202d24e46a2358976bb7c8ff38a2fd4783d0f99
Summary:
There are instances where an application might be interested in knowing the distribution in SST files for a key range in a particular level.
This implementation creates an overloaded GetColumnFamilyMetaData api where (startKey, EndKey) can be passed along with level information to filter the necessary sst files along with the keyranges for each sst file
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14009
Reviewed By: anand1976
Differential Revision: D83389707
fbshipit-source-id: 6df1dc1f9233efe9000b03cc1831b3c618cbcef3
Summary:
Support trivial move in CompactFiles API, which is not supported previously.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14112
Test Plan: Unit test
Reviewed By: cbi42
Differential Revision: D86546150
Pulled By: xingbowang
fbshipit-source-id: 08a3ae9a055f3d3d41711403b1695f44977e6ea8
Summary:
**Summary:**
Merge the BuiltinFilterBitsBuilder into FilterBitsBuilder. This enables using
CalculateSpace() for accurate filter size estimation instead of hardcoded
bits-per-key which could result in incorrect estimations for different filter types.
The previous hardcoded estimate of 15 bits per key was in the filter block builders UpdateFilterSizeEstimate().
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14111
Test Plan: - Existing filter tests pass (bloom_test, full_filter_block_test, filter_bench, db_bloom_filter_test)
Reviewed By: pdillinger
Differential Revision: D86473287
Pulled By: nmk70
fbshipit-source-id: cd4a47351e67444e944d5b1b375b3b13274dd6e3
Summary:
For all compactions, RocksDB performs a lightweight sanity check on output SST files before installation (in `CompactionJob::VerifyOutputFiles()`). However, this lightweight check may not catch corruption that is small enough to allow the SST files to still be opened.
There is an existing feature, `paranoid_file_check`, which opens the SST file, iterates through all keys, and checks the hash of each key. While this provides the ultimate level of data integrity checking, it comes at a high computational cost.
In this PR, we introduce a new mutable CF option, `verify_output_flags`. The `verify_output_flags` is a bitmask enum that allows users to select various verification types, including block checksum verification, full key iteration, and file checksum verification (to be added in subsequent PRs). Note that the existing `paranoid_file_check` option is equivalent to a full key iteration check. Block-level checksum verification is much lighter than the full key iteration check.
Please note that the previously deprecated `verify_checksums_in_compaction` option (removed in version 5.3.0) was for verifying the checksum of **input SST files**. RocksDB continues to perform this verification for both local and remote compactions, and this behavior remains unchanged. In contrast, this PR focuses on verifying the **output SST files**.
## To follow up
- File-level Checksum verification for output SST files
- Deprecate `paranoid_file_checks` option in favor of the new option
- Add to stress test / db_bench
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14103
Test Plan:
New Unit Test added. The corruption is both detected by `paranoid_file_check` and various types of verification set by this new option, `verify_output_flags`
```
./compaction_service_test --gtest_filter="*CompactionServiceTest.CorruptedOutput*"
```
Reviewed By: pdillinger
Differential Revision: D86357924
Pulled By: jaykorean
fbshipit-source-id: a9e04798f249c7e977231e179622a0830d6675fe
Summary:
MultiScanUnexpectedSeekTarget() currently uses user key comparison to decide on the next data block for multiscan. This can cause a multiscan to move backward in the following scenario:
data block 1: ..., k@7, k@6
data block 2: k@5, ...
DB iter scan through k@7, k@6 and k@5 and decides to seek to k@0 due to option [`max_sequential_skip_in_iterations`](https://github.com/facebook/rocksdb/blob/d56da8c112b4e6968fd79ce2bf15e6435df40656/include/rocksdb/advanced_options.h#L621-L629). Multiscan was on data block 2, but moves to data block 1 after the seek.
This can cause assertion failure in debug mode and seg fault in prod since older data blocks are unpinned and freed as we advanced a multiscan. This PR fixes the issue by forcing a multiscan to never go backward.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14106
Test Plan: - added a new unit test that reproduces the scenario: `./db_iterator_test --gtest_filter="*ReseekAcrossBlocksSameUserKey*"`
Reviewed By: xingbowang
Differential Revision: D86428845
Pulled By: cbi42
fbshipit-source-id: ab623f93e73298a60857fb2ff268366f289092a0
Summary:
This test is now taking > 6 hours, timing out, and has low signal, so creating a weekly job for it, with an explicit timeout of 12 hours.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14110
Test Plan: watch CI
Reviewed By: virajthakur
Differential Revision: D86428262
Pulled By: pdillinger
fbshipit-source-id: 44103518064ca378f3fd2ff8d21967ede698c8ea
Summary:
Adds auto-tuning of manifest file size to avoid the need to scale `max_manifest_file_size` in proportion to things like number of SST files to properly balance (a) manifest file write amp and new file creation, vs. (b) manifest file space amp and replay time, including non-incremental space usage in backups. (Manifest file write amp comes from re-writing a "live" record when the manifest file is re-created, or "compacted"; space amp is usage beyond what would be used by a compacted manifest file.) In more detail,
* Add new option `max_manifest_space_amp_pct` with default value of 500, which defaults to 0.2 write amp and up to roughly 5.0 space amp, except `max_manifest_file_size` is treated as the "minimum" size before re-creating ("compacting") the manifest file.
* `max_manifest_file_size` in a way means the same thing, with the same default of 1GB, but in a way has taken on a new role. What is the same is that we do not re-create the manifest file before reaching this size (except for DB re-open), and so users are very unlikely to see a change in default behavior (auto-tuning only kicking in if auto-tuning would exceed 1GB for effective max size for the current manifest file). The new role is as a file size lower bound before auto-tuning kicks in, to minimize churn in files considered "negligibly small." We recommend a new setting of around 1MB or even smaller like 64KB, and expect something like this to become the default soon.
* These two options along with `manifest_preallocation_size` are now mutable with SetDBOptions. The effect is nearly immediate, affecting the next write to the current manifest file.
Also in this PR:
* Refactoring of VersionSet to allow it to get (more) settings from MutableDBOptions. This touches a number of files in not very interesting ways, but notably we have to be careful about thread-safe access to MutableDBOptions fields, and even fields within VersionSet. I have decided to save copies of relevant fields from MutableDBOptions to simplify testing, etc. by not saving a reference to MutableDBOptions but getting notified of updates.
* Updated some logging in VersionSet to provide some basic data about final and compacted manifest sizes (effects of auto-tuning), making sure to avoid I/O while holding DB mutex.
* Added db_etc3_test.cc which is intended as a successor to db_test and db_test2, but having "test.cc" in its name for easier exclusion of test files when using `git grep`. Intended follow-up: rename db_test2 to db_etc2_test
* Moved+updated `ManifestRollOver` test to the new file to be closer to other manifest file rollover testing.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14076
Test Plan:
As for correctness, new unit test AutoTuneManifestSize is pretty thorough. Some other unit tests updated appropriately. Manual tests in the performance section were also audited for expected behavior based on the new logging in the DB LOG. Example LOG data with -max_manifest_file_size=2048 -max_manifest_space_amp_pct=500:
```
2025/10/24-11:12:48.979472 2150678 [/version_set.cc:5927] Created manifest 5, compacted+appended from 52 to 116
2025/10/24-11:12:49.626441 2150682 [/version_set.cc:5927] Created manifest 24, compacted+appended from 2169 to 1801
2025/10/24-11:12:52.194592 2150682 [/version_set.cc:5927] Created manifest 91, compacted+appended from 10913 to 8707
2025/10/24-11:13:02.969944 2150682 [/version_set.cc:5927] Created manifest 362, compacted+appended from 52259 to 13321
2025/10/24-11:13:18.815120 2150681 [/version_set.cc:5927] Created manifest 765, compacted+appended from 80064 to 13304
2025/10/24-11:13:35.590905 2150681 [/version_set.cc:5927] Created manifest 1167, compacted+appended from 79863 to 13304
```
As you can see, it only took a few iterations of ramp-up to settle on the auto-tuned max manifest size for tracking ~122 live SST files, around 80KB and compacting down to about 13KB. (13KB * (500 + 100) / 100 = 78KB). With the default large setting for max_manifest_file_size, we end up with a 232KB manifest, which is more than 90% wasted space. (A long-running DB would be much worse.)
As for performance, we don't expect a difference, even with TransactionDB because actual writing of the manifest is done without holding the DB mutex. I was not able to see a performance regression using db_bench with FIFO compaction and >1000 ~10MB SST files, including settings of -max_manifest_file_size=2048 -max_manifest_space_amp_pct={500,10,0}. No "hiccups" visible with -histogram either.
I also tried seeding a 1 second delay in writing new manifest files (other than the first). This had no significant effect at -max_manifest_space_amp_pct=500 but at 100 started causing write stalls in my test. In many ways this is kind of a worst case scenario and out-of-proportion test, but gives me more confidence that a higher number like 500 is probably the best balance in general.
Reviewed By: xingbowang
Differential Revision: D85445178
Pulled By: pdillinger
fbshipit-source-id: 1e6e07e89c586762dd65c65bb7cb2b8b719513f9
Summary:
**Summary:**
This change introduces tail size estimation during SST construction to improve compaction file cutting accuracy to prevent oversized files. The BlockBasedTableBuilder now estimates the SST tail size (index and filter blocks) and uses this estimate, in addition to the data size, to determine when to cut files during compaction.
**Problem:**
Currently, file cutting logic only considers data size when determining where to cut a file, failing to reserve space for index and filter blocks that are added when the file is finalized. This often leads to SST files that exceed target file size limits.
**Behavior Change:**
Implement size estimation methods for index and filter builders, and integrate these estimates into BlockBasedTableBuilder via a new EstimatedTailSize() method. This method aggregates estimates from all tail components and is used for file cutting decisions during compaction.
**Performance Considerations:**
To minimize CPU overhead, size estimates are updated when data blocks are finalized rather than on every key add. For index builders, estimates are updated when index entries are added (one per data block). For filter builders, the OnDataBlockFinalized() hook triggers estimate updates when data blocks are cut/finalized.
This approach provides:
* Minimal impact to compaction hot path (key additions)
* Near real-time estimates for file cutting decisions
* Meaningful estimate changes only when data blocks are finalized
**Usage:**
* Set true mutable cf option `compaction_use_tail_size_estimation`
to use tail size estimation for compaction file cutting decisions.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14051
Test Plan:
* Assert tail size estimate is an overestimate in BlockBasedTableBuilder::Finish
* Add new test to verify compaction output file is below target file size
**Next steps:**
* Enable tail size estimation for compaction file cutting by default (and other improvements)
Reviewed By: pdillinger, cbi42
Differential Revision: D84852285
Pulled By: nmk70
fbshipit-source-id: c43cf5dbd2cb2f623a0622591ef24eee30ce0c87
Summary:
* Fix nightly build-linux-cmake-with-folly-lite-no-test for real this time
with correct include directory. (CMakeLists.txt)
* Add test runs to that build (and rename)
* Improve folly build caching with a folly.mk file with most of the relevant
parts of Makefile that contribute to the checkout_folly and
build_folly builds. This reduces the risk of false passing of CI job with
cache folly build. This caching is still only for folly debug builds, (which
is probably OK with just a single nightly build relying on release folly
build, which also serves as a rough canary against false passing
because of caching).
* Use `make VERBOSE=1` after cmake calls for detailed output
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14099
Test Plan:
temporary CI change to put the relevant parts in pr-jobs,
then back to homes including in nightly
Reviewed By: mszeszko-meta
Differential Revision: D86243363
Pulled By: pdillinger
fbshipit-source-id: f7975fa190ef45195c6d0b74417f7886e551516a
Summary:
... caused by public headers depending on build parameters (macro definitions). This change also adds a check under 'make check-headers' (already in CI) looking for potential future violations.
I've audited the uses of '#if' in public headers and either
* Eliminated them
* Systematically excluded them because they are intentional or similar (details in comments in check-public-header.sh
* Manually excluded them as being ODR-SAFE
In the case of ROCKSDB_USING_THREAD_STATUS, there was no good reason for this to appear in public headers so I've replaced it with a static bool ThreadStatus::kEnabled. I considered getting rid of the ability to disable this code but some relatively recent PRs have been submitted for fixing that case. I've added a release note and updated one of the CI jobs to use this build configuration. (I didn't want to combine with some jobs like no_compression and status_checked because the interaction might limit what is checked.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14096
Test Plan: manual 'make check-headers' + manual cmake as in new CI config + CI
Reviewed By: jaykorean
Differential Revision: D86241864
Pulled By: pdillinger
fbshipit-source-id: d16addc9e3480706b174a006720a4def0740bf2e
Summary:
Following up on https://github.com/facebook/rocksdb/pull/14071, updating folly to
https://github.com/facebook/folly/commit/8a9fc1e80a18cafadbec85e33d5042ce13a7c634 or beyond was failing an F14Table assertion for a very subtle reason: ODR violation between the folly build and RocksDB build because folly build was release mode and RocksDB build was debug mode. What was happening was that folly change introduced a dependence on kDebug (whether build is debug) in a hashing implementation in a .h file, and the inconsistency between the inlined implementation during RocksDB build and the linked-to implementation from the folly build was leading to inconsistencies in the data structure.
The primary fix is to ensure we build folly in debug mode for debug mode RocksDB builds. Also,
* Needed to use the `patchelf` tool in `build_folly` to ensure the glog dependency shared library can always find its own gflags dependency. I explored many options for working around this, and this is what would work without reworking folly's own build.
* Updated folly to latest commit.
* Thrown in an ad hoc folly patch to use ftp.gnu.org mirrors (the canonical is super slow)
* Moved the placement of GETDEPS_USE_WGET=1 to apply to local builds also, to avoid the issue of a large download almost reaching completion and then stalling indefinitely.
* Fix failing nightly build-linux-cmake-with-folly-lite-no-test with fmt includes in cmake build (as was done with make build)
* Add a release mode folly+RocksDB to nightly CI, including both cmake and make. This also serves as a non-cached folly build to detect potential problems with PR jobs working from cached folly build.
* Move build-linux-cmake-with-folly to nightly because it's mostly covered by build-linux-cmake-with-folly-coroutines
Intended follow-up:
* folly-lite build with tests
* Make the folly build caching more friendly+accurate by hashing the relevant Makefile parts and tagging whether debug or release. Not in this PR because then you wouldn't be able to see what changed in the folly build steps themselves.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14094
Test Plan: manual + CI
Reviewed By: mszeszko-meta
Differential Revision: D85864871
Pulled By: pdillinger
fbshipit-source-id: 50009b33422d5781074fcbbdf18089be9e36800d
Summary:
Resolving this folly upgrade required fixing the FOLLY_LITE build with header include from the 'fmt' library.
I was close to timing out on fixing USE_FOLLY_LITE and removing it altogether - it could be considered obsolete and/or not worth the maintenance cost.
Follow-up: make the folly build caching more friendly by hashing the relevant makefile parts. Not in this PR because then you wouldn't be able to see what changed in the folly build steps themselves.
UPDATE/NOTE: I wasn't able to fully update to latest due to a failure seen in F14, using the next folly commit or later. The source of the bug is likely outside of F14 but investigation is in progress.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14071
Test Plan: CI
Reviewed By: jaykorean
Differential Revision: D85268833
Pulled By: pdillinger
fbshipit-source-id: 1d0a2d61f095524a20e6ec796ef46c02d0696f4e
Summary:
Change PosixWritableFile's Truncate to the new end offset. This ensures that future appends are written with no holes or overwrites. RocksDB doesn't guarantee this in the FileSystem contract, and its left up to the specific implementation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14088
Reviewed By: cbi42
Differential Revision: D85786398
Pulled By: anand1976
fbshipit-source-id: 3520d9d6336362f5128a17bbf396297d821a5da3
Summary:
Comprehensive performance optimizations for the RocksDB C API that eliminate unnecessary memory allocations and copies.
## Key Changes
### 1. PinnableSlice for Get Operations (50% reduction in copies)
- Changed all `rocksdb_get*` functions to use `PinnableSlice` internally instead of `std::string`
- **Before:** RocksDB → std::string → malloc'd buffer (2 copies)
- **After:** RocksDB → malloc'd buffer (1 copy)
- Affects: Get, Transaction Get, TransactionDB Get, WriteBatch Get variants
### 2. Array-Based MultiGet with PinnableSlice (30% allocation reduction)
- Switched MultiGet operations to use optimized array-based RocksDB API with `PinnableSlice`
- Eliminates vector overhead and string allocations
- Affects: MultiGet, Transaction MultiGet, TransactionDB MultiGet variants
### New Zero-Copy APIs
Added high-performance zero-copy functions for applications that can use them:
- `rocksdb_iter_key_slice()` / `value_slice()` / `timestamp_slice()` - Return slices by value (eliminates output param overhead)
- `rocksdb_batched_multi_get_cf_slice()` - Batched get with slice array input
- `rocksdb_slice_t` - ABI-compatible slice type
Note that this pr builds on top of https://github.com/facebook/rocksdb/pull/13911
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14036
Reviewed By: pdillinger
Differential Revision: D85604919
Pulled By: jaykorean
fbshipit-source-id: 7f04b935eea79af1d45b3125a79b90e4706666f6
Summary:
Stress test can fail with assertion inside MultiScan in some reseek scenario. E.g., data block 1 ends with k@9, data block 2 starts with k@8, when a DB iter seeks to k@0 (see option `max_sequential_skip_in_iterations`), MultiScan will land in data block 1 due to https://github.com/facebook/rocksdb/blob/fd0b4e0cf08315f6a644d54d585fe70ca958d4ba/table/block_based/block_based_table_iterator.cc#L1258-L1263.
We can't just use internal key as separator since index block might not use it. I plan to follow up with a fix that never moves `cur_data_block_idx` backward within a MultiScan.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14087
Test Plan: CI and internal crash tests
Reviewed By: anand1976
Differential Revision: D85701668
Pulled By: cbi42
fbshipit-source-id: d3f1aaff40a12be4e3d1b4b7160bf2547f43b849
Summary:
All remote compaction test failures had `mmap_read=1` in common. Unfortunately, the failure hasn't been very reproducible. Try disabling `mmap_read` to see if that shed some light.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14083
Test Plan: CI
Reviewed By: hx235
Differential Revision: D85622229
Pulled By: jaykorean
fbshipit-source-id: bbe9e08efc369813f0fec388c910446089e43650
Summary:
As titled, this fixes some internal crash test failures when UDT is enabled.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14085
Test Plan: monitor crash tests.
Reviewed By: anand1976
Differential Revision: D85617949
Pulled By: cbi42
fbshipit-source-id: da6fb21c0ca5803ea24e8daf7de8558321babcf4
Summary:
Due to some internal requirements, what's being used for`$SSH` and `$SCP` has changed and it broke the regression test. (e.g. tarball streaming to remote host no longer works)
Minor behavior changes to the script to make the internal workflow work.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14079
Test Plan:
```
./tools/regression_test.sh
```
Meta Internal automation
Reviewed By: pdillinger
Differential Revision: D85502798
Pulled By: jaykorean
fbshipit-source-id: d294c2ee47661fbe368ccc318062e891f3ac7c81
Summary:
The TTL-based WAL archive cleanup logic could incorrectly delete an archived WAL if the system clock moved backwards between the last write to that WAL and `WALManager::PurgeObsoleteWALFiles()`. This happened due to unsigned underflow in subtraction of two wall clock based timestamps: `now_seconds - file_m_time`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14016
Test Plan: unit test repro
Reviewed By: pdillinger
Differential Revision: D83879806
Pulled By: hx235
fbshipit-source-id: 643e7f623c6b5c31711565854314cfd6cbbcf3a7
Summary:
Fixed a missing CV signal when `FindObsoleteFiles()` decides there is nothing to purge and then decrements `pending_purge_obsolete_files_` to zero. This bug could cause `DB::GetSortedWalFiles()` to hang, at least.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14069
Test Plan: unit test repro
Reviewed By: hx235
Differential Revision: D85453534
Pulled By: cbi42
fbshipit-source-id: cf5cfe7f5087459ca1f1f28ce81ea6afc84178f0
Summary:
* Address feedback from https://github.com/facebook/rocksdb/issues/14040
* Add additional test for MultiScan
* Fix a bug when del range and data are in same file for multi-scan
* Rewrite the cases need to be handled in SeekMultiScan
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14055
Test Plan: Unit test
Reviewed By: cbi42, anand1976
Differential Revision: D84851788
Pulled By: xingbowang
fbshipit-source-id: 0f69632733afb99685f6341badbf239681010c38
Summary:
Linter complains like this
```
void foo(Arg parameter_name) {}
void bar() {
Arg a;
foo(/*some_other_name=*/ a); // Wrong! Comment/parameter name mismatch
foo(/*parameter_name=*/ a); // This is OK; the names match.
}
```
```
Argument name in comment (`read_only`) does not match parameter name (`unchanging`).
```
This used to be warning, but now treated as an error :(
Fixing a few other linter warnings before they become errors in the future.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14074
Test Plan: CI
Reviewed By: archang19
Differential Revision: D85370353
Pulled By: jaykorean
fbshipit-source-id: 20e96aad740d516a29c0424282674e655f99c0a2
Summary:
When a standalone range deletion file is ingested in L0, currently it is compacted with any overlapping L0 files. This is not desirable when we ingest new data on top of the range deletion file. This PR fixes the compaction picking logic to only consider L0 files older than the standalone range deletion file.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14061
Test Plan: added a new unit test and updated an existing one.
Reviewed By: xingbowang
Differential Revision: D84930780
Pulled By: cbi42
fbshipit-source-id: 65f4403ccb40ba964b9e65b09e2f7f7efebe81df
Summary:
**Context/Summary:**
- Add resumable compaction to stress test with adaptive progress cancellation
- Add fault injection to remote compaction
- Fix a real minor bug in a couple testing framework bugs with remote compaction
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14041
Test Plan: - Rehearsal stress test, finding bugs for https://github.com/facebook/rocksdb/pull/13984 effectively and did not create new failures.
Reviewed By: jaykorean
Differential Revision: D84524194
Pulled By: hx235
fbshipit-source-id: 42b4264e428c6739631ed9aa5eb02723367510bc
Summary:
With cache hit and compiler option optimization, the compilation time build time is reduced from 40 min to 2 min. Overall build time is reduced from 60 min to less 20 minutes on cache hit on majority of the source file. On 100% cache miss, it would be around 40 minutes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14064
Test Plan: Github CI
Reviewed By: mszeszko-meta
Differential Revision: D85023882
Pulled By: xingbowang
fbshipit-source-id: 98551880c98f14d36133ff43e6af8c3be94ab465
Summary:
Fixing a nullptr access in multiscan, under following situation.
```
Block Based Table: blk1:[k1,k2], blk2:[k3, k8], blk3:[k9]
Scan ranges: [k1, k4), [k5,k6), [k7, k10)
Prepared block ranges: [0,2], [2,2], [1,3]
```
1. Seek key k1 on the first range, read key k1, k2.
2. Seek key k4 on the 2nd range, blocks 0,1 would be unpinned.
3. Seek key k9, block 1 would be accessed, but it is unpinned, which trigger assert failure in debug mode and nullptr access on release build.
This fix changes how blocks are unpinned. It is now only unpinning the block, when the cur_data_block_idx has passed it.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14062
Test Plan:
Unit Test
rand_seed 304010984 on UserDefinedIndexStressTest
Reviewed By: cbi42
Differential Revision: D84976410
Pulled By: xingbowang
fbshipit-source-id: 6b99bf85fc9d4108c5267ae77be77ccfe08923cd
Summary:
**Problem:** RocksDB was making unnecessary prefetch system calls on file systems that don't support prefetch operations, potentially leading to wasted CPU cycles.
**Fix:** Add kFSPrefetch to FSSupportedOps enum to allow file systems to indicate prefetch support capability. File systems can now opt out of prefetch calls by not setting this field.
**Backwards compatibility:** File systems that don't override SupportedOps() continue to receive prefetch calls exactly as before. Only file systems that explicitly opt out by not setting kFSPrefetch will avoid the calls.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13917
Test Plan:
- Added a new test in block_based_table_reader.
- Run existing tests: ```make prefetch_test && ./prefetch_test```
Reviewed By: anand1976
Differential Revision: D81607145
Pulled By: nmk70
fbshipit-source-id: 3bbefa05919034e8776ea4e4540cdc695cdc6d3f
Summary:
Currently we return `File is too large for PlainTableReader!` when the file size exceeds our pre-defined constant. There was a request to have the file size information logged when this error is returned.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14056
Reviewed By: nmk70
Differential Revision: D84834869
Pulled By: archang19
fbshipit-source-id: 8f332b6a31d51f320c7e2db06ad49f50798ff70e
Summary:
* Reduce build time of folly from 45m~1hr down to 25m. This is achieved by caching folly build artifact from previous build.
* Reduce windows build time of folly from 1hr 15m down to 50m. This is done by increase windows build machine size.
* Fix build on macos on other macos target.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14057
Test Plan: github CI
Reviewed By: archang19, nmk70
Differential Revision: D84848041
Pulled By: xingbowang
fbshipit-source-id: 00306750737070e7e446ee436d607ed6ecae79ae
Summary:
We simulate remote compaction in our stress test by running a separate set of worker threads to run compactions. In reality, these remote compactions run on a different host or (at least in a different process) where we cannot share the TableFactory and BlockCache with the main DB process.
To make this simulated remote compaction closer to reality, create a new TableFactory for each remote compaction in stress test.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14050
Test Plan:
```
python3 -u tools/db_crashtest.py --cleanup_cmd='' --simple blackbox --remote_compaction_worker_threads=8 --interval=10
```
Reviewed By: hx235
Differential Revision: D84775656
Pulled By: jaykorean
fbshipit-source-id: d6203fcbe0eca3539e008a19fd47b742553537ed
Summary:
We are adding more and more tests, so we need to increase the number of shards in macos build to reduce overall CI time.
macos-15-xlarge image is ARM, which has 5 vCPU cores, but is still 50% faster than the intel x86 12 vCPU.
Test time reduced from 1h 37m to 14m.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14048
Reviewed By: archang19
Differential Revision: D84741917
Pulled By: xingbowang
fbshipit-source-id: 9ba9bd696d3b2152f11dec2fb4280572b98233d5
Summary:
Currently in BlockBasedTableIterator's Prepare(), the index lookup for a MultiScan range is expected to return atleast 1 data block (unless UDI is in use). This is because there's an implicit assumption that only ranges intersecting with the keys in the file will be prepared. This assumption, however, doesn't hold if there are range deletions and the smallest and/or largest keys in the file extend beyond the keys in the file. The LevelIterator prunes the MultiScan ranges based on the smallest/largest key, so its possible for a range to only overlap the range deletion portion of the file and not overlap any of the data blocks. Furthermore, the BlockBasedTableIterator is now much more forgiving of Seek to targets outside of prepared ranges after https://github.com/facebook/rocksdb/issues/14040 .
Keeping the above in mind, this PR removes the check in BlockBasedTableIterator for non-empty index result. It adds assertions in LevelIterator to verify that ranges are being properly pruned. Another side effect is we can no longer rely solely on a scan range having 0 data blocks (i.e cur_scan_start_idx >= cur_scan_end_idx) to decide if the iterator is out of bound. We can only do so for all but the last range prepared range.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14046
Test Plan:
1. Add unit test in db_iterator_test
2. Run crash test
Reviewed By: xingbowang
Differential Revision: D84623871
Pulled By: anand1976
fbshipit-source-id: 2418e629f92b1c46c555ddea3761140f700819e4
Summary:
The current seek key validation is too strict. This change relaxes it at block iterator level, and add additional check at DB iterator level. The new contract is that when MultiScan is used, after prepared is called, each following seek must seek the start key of the prepared scan range in order. Otherwise, the iterator is set with error status.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14040
Test Plan: Unit test
Reviewed By: anand1976
Differential Revision: D84292297
Pulled By: xingbowang
fbshipit-source-id: 7b31f727e67e7c0bfc53c2f9a6552e0c3d324869
Summary:
Multi scan crash/stress tests are failing when skip_stats_update_on_db_open is true, because LevelIterator::Prepare relies on these stats in FileMetaData to make decisions. Disable it in crash tests until the proper fix is ready.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14039
Reviewed By: archang19
Differential Revision: D84280059
Pulled By: anand1976
fbshipit-source-id: f9f58b94c24d1f455432b05f3bf97f25c7233e3c
Summary:
**Context/Summary:**
There is no way to tag or rate-limit write IO occurs during FlushWAL() with priority. Under `Options::manual_wal_flush=true`, it is the major source of write IO during user writes so we decide to add that support. A new option struct `FlushWALOptions` is introduced to avoid making the API ugly for future new fields.
Also, we can't use the WriteOptions (https://github.com/facebook/rocksdb/blob/main/include/rocksdb/options.h#L2293-L2302 i) since is associated with that particular Put/Merge/.. associated with that option but FlushWAL() can happen after that write. There is no way to carry that write option over in RocksDB. I also avoided using the WriteOptions since it's mostly for live write.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14037
Test Plan: New UTs `TEST_P(DBRateLimiterOnManualWALFlushTest, ManualWALFlush)`
Reviewed By: archang19
Differential Revision: D84193522
Pulled By: hx235
fbshipit-source-id: 18feb5235672010d19a101ce52c8abdcc4a789f2
Summary:
- Include Status in RemoteCompactionResultMap in SharedState so that we can directly check the status of the remote compaction in `DbStressCompactionService::Wait()`
- If result is empty, populate the result with the status that was returned from `GetRemoteCompactionResult()` so that the status can be bubbled up to the primary (main db thread)
- Get rid of Timeout in `Wait()`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14022
Test Plan:
With fall-back
```
python3 -u tools/db_crashtest.py blackbox --remote_compaction_worker_threads=8 --remote_compaction_failure_fall_back_to_local=1
```
Without fall-back
```
python3 -u tools/db_crashtest.py blackbox --remote_compaction_worker_threads=8 --remote_compaction_failure_fall_back_to_local=0
```
Reviewed By: hx235
Differential Revision: D83789172
Pulled By: jaykorean
fbshipit-source-id: 08f710c4ece5fcc1d4b95b3f9c353831882851b7
Summary:
Fix the binutils truncated download issue by switching to wget in the folly build scripts for downloading dependencies.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14030
Test Plan: make build_folly
Reviewed By: jaykorean
Differential Revision: D84033126
Pulled By: anand1976
fbshipit-source-id: bc6706d7e57c97d6edff149a965aa12c7959825f
Summary:
MultiScan currently doesn't handle delete range properly. In this specific case, a file with only delete range will have an empty index resulting in BlockBasedTableIterator wrongly thinking that a scan doesn't intersect the file due to empty result.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14026
Test Plan: Run crash test
Reviewed By: xingbowang
Differential Revision: D83881266
Pulled By: anand1976
fbshipit-source-id: dc1faa494ea23f36391b700dd1ee0430a1f20ac5
Summary:
When there is an ingested SST file that only contains delete range operations, MultiScan may return error "Scan does not intersect with file". This is due to file selection during Prepare uses the file smallest and largest key without considering whether there is any key in the file. This is only a temporary fix.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14028
Test Plan: Unit test
Reviewed By: anand1976
Differential Revision: D83986964
Pulled By: xingbowang
fbshipit-source-id: e0961ca854e2062c2457be4324817ba073ae785d
Summary:
Implicit reseek in the middle of an iteration is not supported with MultiScan. Avoid this for now in crash tests by setting max_sequential_skip_in_iterations to an absurdly high value.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14015
Reviewed By: xingbowang
Differential Revision: D83761612
Pulled By: anand1976
fbshipit-source-id: 16f4e856374b79170c0a79c11c275cbb0fc83a70
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14024
Fix some typo found along the codebase
Reviewed By: pdillinger
Differential Revision: D83789182
fbshipit-source-id: feb24d7d47a6faaf735fcfd50dd3ecce4a6c8cd5
Summary:
When inplace_update_support and memtable_veirfy_per_key_checksum_on_seek are enabled at the same time, it would cause data race in memtable.
inplace_update_support allows key/value pair in place update in memtable.
memtable_veirfy_per_key_checksum_on_seek performs key checksum verification during seek. It is possible that one thread is updating the key/value pair in place, while another thread is reading the key/value pair for checksum verification during seek.
Therefore, there these 2 configurations could not be enabled at the same time
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14023
Test Plan: local stress test run stops reporting race condition
Reviewed By: anand1976
Differential Revision: D83812322
Pulled By: xingbowang
fbshipit-source-id: 6cb9f0f3faa8deba97305bfe87266f2fe78e0501
Summary:
In RocksDB 10.6 with https://github.com/facebook/rocksdb/issues/13805, due to inaccurate testing of an async system, it went undetected at the time that LZ4 compression was using more CPU despite making a change to reuse stream objects which dramatically improved LZ4HC compression efficiency.
This change switches to using a basic LZ4 compress API which appears to be faster than all of these:
* Legacy behavior of creating LZ4_stream_t for each compression
* 10.6-10.7 behavior of re-using streams between compressions for the same file (with stream-as-WorkingArea)
* using LZ4's extState APIs without streams (with extState-as-WorkingArea) (data not shown in below results)
Also in this PR: more improvements to sst_dump --recompress, which is arguably the best SST construction benchmark right now since db_bench seems to be so noisy due to backgroun flush+compaction, even with no compaction (FIFO). Streamlined some output and added a SST read time test, mostly for decompression performance.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14017
Test Plan:
Performance test using sst_dump --recompress with newer sst_dump back-ported to 10.5:
```
./sst_dump --command=recompress --compression_types=kLZ4Compression
test5.sst --compression_level_from=-6 --compression_level_to=-1
```
and with default compression level.
10.5:
```
Cx level: -6 Cx size: 61608137 Write usec: 880404
Cx level: -5 Cx size: 60793749 Write usec: 840903
Cx level: -4 Cx size: 58134030 Write usec: 836365
Cx level: -3 Cx size: 55193773 Write usec: 857113
Cx level: -2 Cx size: 54013891 Write usec: 855642
Cx level: -1 Cx size: 50400393 Write usec: 865194
Cx level: 32767 Cx size: 50400393 Write usec: 886310
```
Before this change (showing the regression, more time, from 10.6:
```
Cx level: -6 Cx size: 61608137 Write usec: 933448
Cx level: -5 Cx size: 60793749 Write usec: 893826
Cx level: -4 Cx size: 58134030 Write usec: 891138
Cx level: -3 Cx size: 55193773 Write usec: 898461
Cx level: -2 Cx size: 54013891 Write usec: 897485
Cx level: -1 Cx size: 50400393 Write usec: 936970
Cx level: 32767 Cx size: 50400393 Write usec: 958764
```
After this change (faster than both the above):
```
Cx level: -6 Cx size: 63641883 Write usec: 874190
Cx level: -5 Cx size: 58860032 Write usec: 834662
Cx level: -4 Cx size: 57150188 Write usec: 832707
Cx level: -3 Cx size: 58791894 Write usec: 850305
Cx level: -2 Cx size: 53145885 Write usec: 839574
Cx level: -1 Cx size: 49809139 Write usec: 845639
Cx level: 32767 Cx size: 49809139 Write usec: 875199
```
Similar tests with dictionary compression show essentially no difference (need to use stream APIs and reuse doesn't seem to matter). LZ4HC also unaffected (still improved vs. 10.5)
Reviewed By: hx235
Differential Revision: D83722880
Pulled By: pdillinger
fbshipit-source-id: 30149dd187686d5dd98321e6aa7d74bd7653a905
Summary:
Pad block based table based on super block alignment
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13909
Test Plan:
Unit Test
No impact on perf observed due to change in the inner loop of flush.
upstream/main branch 202.15 MB/s
```
for i in `seq 1 10`; do ./db_bench --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 >> /tmp/x1 2>&1; grep fillseq /tmp/x1 | grep -Po "\d+\.\d+ MB/s" | grep -Po "\d+\.\d+" | awk '{sum+=$1} END {print sum/NR}'
```
After the change without super block alignment 203.44 MB/s
```
for i in `seq 1 10`; do ./db_bench --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 >> /tmp/x1 2>&1
```
After the change with super block alignment 204.47 MB/s
```
for i in `seq 1 10`; do ./db_bench --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 --super_block_alignment_size=131072 --super_block_alignment_max_padding_size=4096 >> /tmp/x1 2>&1;
```
Reviewed By: pdillinger
Differential Revision: D83068913
Pulled By: xingbowang
fbshipit-source-id: eecd65088ab3e9dbc7902aab8c2580f1bc8575df
Summary:
### Context/Summary:
Flow of resuming: DB::OpenAndCompact() -> Compaction progress file -> SubcompactionProgress -> CompactionJob
Flow of persistence: CompactionJob -> SubcompactionProgress -> Compaction progress file -> DB that is called with OpenAndCompact()
This PR focuses on SubcompactionProgress -> CompactionJob and CompactionJob -> SubcompactionProgress -> Compaction progress file. For now only single subcompaction is supported as OpenAndCompact() does not partition compaction anyway.
The actual triggering of progress persistence and resuming (i.e, integration) is through DB::OpenAndCompact() in the upcoming PR.
**Resume Flow**
1. input_iter->Seek(next_internal_key_to_compact) // Position iterator
2. ReadTableProperties() // Validate existing outputs
3. RestoreCompactionOutputs() in CompactionOutputs // Rebuild output file metadata
4. Restore critical statistics about processed input and output records count for verification later
5. AdvanceFileNumbers() // Prevent file number conflicts
6. Continue normal compaction from positioned iterator or fallback to not resuming compaction in limited case or fail the compaction entirely
**Persistence Strategy**
1. When: At each SST file completion (FinishCompactionOutputFile()). This is the simplest but most expensive frequency. See below for benchmarking and potential follow-up items
2. What: Serialize, write and sync the in-memory SubcompactionProgress to a dedicated manifest-like file
3. For simplicity: Only persist at "clean" boundaries (no overlapping user keys, no range deletions, no timestamp for now)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13983
Test Plan:
- New unit test in CompactionJob level to cover basic compaction progress resumption
- Existing UTs and stress/crash test to test no correctness regression to existing compaction code
- Run benchmark to ensure no performance regression to existing compaction code
```
./db_bench --benchmarks=fillseq[-X10] --db=$db --disable_auto_compactions=true --num=100000 --value_size=25000 --compression_type=none --target_file_size_base=268435456 --write_buffer_size=268435456
```
Pre-PR:
fillseq [AVG 10 runs] : 45127 (± 799) ops/sec; 1076.6 (± 19.1) MB/sec
fillseq [MEDIAN 10 runs] : 45375 ops/sec; 1082.5 MB/sec
Post-PR (regressed 0.057%, ignorable)
fillseq [AVG 10 runs] : 45101 (± 920) ops/sec; 1076.0 (± 22.0) MB/sec
fillseq [MEDIAN 10 runs] : 45385 ops/sec; 1082.8 MB/sec
Reviewed By: jaykorean
Differential Revision: D82889188
Pulled By: hx235
fbshipit-source-id: 8553fd478f134969d331af2c5a125b94bd747268
Summary:
This method will be used to improve the compaction logic by accounting for the tail size, in addition to the data size, when determining when to cut a file.
Problem: Currently the file cutting logic only considers data size when determining where to cut a file, failing to reserve space for index and filter blocks that are added when the file is finalized.
Key changes:
- Add EstimateCurrentIndexSize() to IndexBuilder interface
- Implement in ShortenedIndexBuilder with buffer that accounts for the next index entry. The buffer addresses under-estimation where the current index size doesn't account for the next index entry associated with the data block currently being built. The 2x multiplier bounds the estimate in the right direction and handles outlier cases with large keys.
- Add num_index_entries_ member to track added index entries (== data blocks emitted). This is thread-safe since it's updated/read in the serialized emit step.
Next steps:
- Partitioned index size estimation implementation
- Update compaction file cutting logic to consider index size estimation
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14010
Test Plan: Added a new test class with unit tests for new builder size estimation across all IndexBuilder implementations.
Reviewed By: pdillinger
Differential Revision: D83501741
Pulled By: nmk70
fbshipit-source-id: d58fc2a9e92e12a162f6244d4abd707a9c9e1885
Summary:
This PR fixes a bug in how MultiScan handled a scan range limit falling in the key range between files. The bug was in LevelIterator, where Prepare() relied on FindFile to determine the lower bound file for the range limit. FindFile returns the smallest file index with `range.limit < file.largest_key`. However, that doesn't guarantee that the range overlaps the file, as the `range.limit` could be smaller than `file.smallest_key`.
This also fixes a bug in BlockBasedTableIterator of Valid() returning true even if status() returned error. This was exposed by the previous bug.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14011
Test Plan: Add unit tests in db_iterator_test and table_test
Reviewed By: cbi42
Differential Revision: D83496439
Pulled By: anand1976
fbshipit-source-id: a9d2d138d69d0c816d9f4160a984b273d00d683f
Summary:
Pretty self-explanatory from the changes, including re-arranging the "COOL" entries for easier tracking of which values are used.
I'm not touching the TICKER_ENUM_MAX issue because IIRC we've gotten in trouble in the past for changing any Java ticker values.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14012
Test Plan: CI, sufficient prompts to get AI to discover the known issues relayed by hx235, to help ensure we found any other outstanding issues.
Reviewed By: hx235
Differential Revision: D83497503
Pulled By: pdillinger
fbshipit-source-id: ec0bd7e28188e0430fb03fc5bd79c2ed7b28f3ad
Summary:
Pass the comparator to UDI interface for both reader and builder.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14001
Test Plan: Unit test
Reviewed By: anand1976
Differential Revision: D83339943
Pulled By: xingbowang
fbshipit-source-id: 7f6541776b0995260e28224329f0cca37f13b3d4
Summary:
currently BlockBasedTableIterator::Prepare() fails the iterator with non-ok status if an out-of-range scan option is detected. This is due to the interaction between LevelIterator and BlockBasedTableIterator, see added comment above BlockBasedTableIterator::Prepare(). This can fail stress test for L0 files since it doesn't use LevelIterator and scan options are not pruned. This PR fixes this by adding an internal option to MultiScanArgs that enables this check.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13995
Test Plan:
- new unit test
- stress test that fails before this pr: `python3 -u ./tools/db_crashtest.py whitebox --iterpercent=60 --prefix_size=-1 --prefixpercent=0 --readpercent=0 --test_batches_snapshots=0 --use_multiscan=1 --read_fault_one_in=0 --kill_random_test=88888 --interval=60 --multiscan_use_async_io=0 --mmap_read=0 --level0_file_num_compaction_trigger=20`
Reviewed By: anand1976
Differential Revision: D83166088
Pulled By: cbi42
fbshipit-source-id: 241a7d43c8c00d9a98eea0cabb03d2174d51aae5
Summary:
There can be concurrent reads/writes to fields in `IODebugContext`. One example we have seen is for the `cost_info` field which is of type `std::any`. In fact, in RocksDB's async MultiRead implementation, the same `IODebugContext` is re-used across separate async read requests.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13993
Test Plan: Update code which reads/writes to `cost_data` to first acquire shared/exclusive lock on the `mutex` field. There should not be any race conditions when async MultiRead is used.
Reviewed By: pdillinger
Differential Revision: D83091423
Pulled By: archang19
fbshipit-source-id: 4db86d33cf162ed39114b1cd115fcd8964c8ff9b
Summary:
Remove the restriction of only using BytewiseComparator(). In a follow on PR, the UDI interface will be updated to take the Comparator as a parameter.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13999
Test Plan: Add a unit test in table_test.cc
Reviewed By: cbi42
Differential Revision: D83179747
Pulled By: anand1976
fbshipit-source-id: 60222533c71022aa0701ac61c39268d36ca86338
Summary:
In https://github.com/facebook/rocksdb/issues/13964 I changed an expensive DEBUG check in ~AutoHyperClockTable to only run in ASAN builds. It's still expensive so I'm modifying it to scan only about one page beyond what we expect to have written to the anonymous mmap, rather than scanning the whole thing.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13998
Test Plan: manually checked that lru_cache_test running time went from 5.0s to 4.0s after the change. Verified that existing unit test ClockCacheTest.Limits uses the full anonymous mmap to be sure it is sized as expected, by temporarily breaking AutoHyperClockTable::Grow() to allow slightly exceeding the anonymous mmap size.
Reviewed By: cbi42
Differential Revision: D83178493
Pulled By: pdillinger
fbshipit-source-id: a2bf093e98bf68b540c073800be7e193021f2692
Summary:
This combination causes MultiScan iteration to fail due to internal reseek by the iterator.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13992
Reviewed By: cbi42
Differential Revision: D83094631
Pulled By: anand1976
fbshipit-source-id: 96410747d88de391e6d65857d39063d4fb113d65
Summary:
Fix the bug in Improve random seed override support in stress test.
The Bug:
`parser.parse_known_args()` is used to parse command line argument. When it is called without any argument, it uses sys.argv as input parameter. In sys.argv, the first argument is the command itself, so parser.parse_known_args skip the first argument. Meantime, the return value `remain_argv` of `parser.parse_known_args()` does not contain the command itself. When `remain_arg` replaces `sys.argv`, the first argument is treated as the command itself, which is skipped by `parser.parse_known_args()`. In the internal stress test tool, the first argument is `--stress_cmd`, therefore, it is skipped. Instead, the default value `./stress_db` is used. This is why `./stress_db` showed up in the error message. This is also why it works in local, as stress_db is located in the local folder.
The Fix:
When `parser.parse_known_args()` is called first time, the remain_argv is saved as a global variable. It is used in the second call of the `parser.parse_known_args(remain_argv)`. When argument is passed to `parser.parse_known_args` directly, the first argument will not be skipped.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13991
Test Plan:
The the value of first argument `--stress_cmd` is parsed correctly, and shown up in the error message.
```
/usr/local/bin/python3 -u tools/db_crashtest.py --stress_cmd=/data/sandcastle/boxes/trunk-hg-full-fbsource/buck-out/v2/gen/fbcode/d7db8b24dd42e2db/internal_repo_rocksdb/repo/__db_stress__/db_stress --cleanup_cmd='' --simple blackbox --print_stderr_separately
Start with random seed 11107847853133580500
Running blackbox-crash-test with
interval_between_crash=120
total-duration=6000
Use random seed for iteration 8577470137673434540
Traceback (most recent call last):
File "/home/xbw/workspace/ws1/rocksdb/tools/db_crashtest.py", line 1650, in <module>
main()
File "/home/xbw/workspace/ws1/rocksdb/tools/db_crashtest.py", line 1639, in main
blackbox_crash_main(args, unknown_args)
File "/home/xbw/workspace/ws1/rocksdb/tools/db_crashtest.py", line 1358, in blackbox_crash_main
hit_timeout, retcode, outs, errs = execute_cmd(cmd, cmd_params["interval"])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/xbw/workspace/ws1/rocksdb/tools/db_crashtest.py", line 1294, in execute_cmd
child = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/fbcode/platform010/lib/python3.12/subprocess.py", line 1028, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/local/fbcode/platform010/lib/python3.12/subprocess.py", line 1957, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/data/sandcastle/boxes/trunk-hg-full-fbsource/buck-out/v2/gen/fbcode/d7db8b24dd42e2db/internal_repo_rocksdb/repo/__db_stress__/db_stress'
```
Reviewed By: hx235
Differential Revision: D83068960
Pulled By: xingbowang
fbshipit-source-id: 28334d38a444c6f8525444e15f460ec6b257ef38
Summary:
Return a failure status for multi scan if Prepare fails, or if the scan options are unsupported, instead of falling back on a regular scan. This PR also fixes a bug in LevelIterator that caused max_prefetch_size to be ignored.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13974
Test Plan: Add new test in db_iterator_test and table_test
Reviewed By: xingbowang
Differential Revision: D82843944
Pulled By: anand1976
fbshipit-source-id: f12756c40ebd38d8d4e4425e97438b6e766a4663
Summary:
**Context/Summary**
This reverts commit 73432a3f36. This is due to it mysteriously fails our internal CI running with this change to db_crashtest.py. The root-cause is unknown but the error only reproed with this commit frequently but not the one before it. The error message appears to be the command parsing leading to the db_stress binary can't be found
```
Traceback (most recent call last):
File "/data/sandcastle/boxes/trunk-hg-full-fbsource/fbcode/internal_repo_rocksdb/repo/tools/db_crashtest.py", line 1638, in <module>
main()
File "/data/sandcastle/boxes/trunk-hg-full-fbsource/fbcode/internal_repo_rocksdb/repo/tools/db_crashtest.py", line 1627, in main
blackbox_crash_main(args, unknown_args)
File "/data/sandcastle/boxes/trunk-hg-full-fbsource/fbcode/internal_repo_rocksdb/repo/tools/db_crashtest.py", line 1347, in blackbox_crash_main
hit_timeout, retcode, outs, errs = execute_cmd(cmd, cmd_params["interval"])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/sandcastle/boxes/trunk-hg-full-fbsource/fbcode/internal_repo_rocksdb/repo/tools/db_crashtest.py", line 1283, in execute_cmd
child = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/fbcode/platform010/lib/python3.12/subprocess.py", line 1028, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/local/fbcode/platform010/lib/python3.12/subprocess.py", line 1957, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: './db_stress'
```
**Test plan**
- Rehearsal crash test
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13989
Reviewed By: xingbowang
Differential Revision: D83010751
Pulled By: hx235
fbshipit-source-id: d8cfc70564074065b6bb8a3986d6c1011064dd5e
Summary:
This is causing some internal failure, we decide to revert this for now until we have a proper fix.
This reverts commit 961880b458.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13987
Reviewed By: anand1976
Differential Revision: D82990294
Pulled By: cbi42
fbshipit-source-id: 5f5b4d18d0afe47599738d27e11e3eb2d08d88a0
Summary:
**Context**
Resuming compaction is designed to periodically record the progress of an ongoing compaction and can resume from that saved progress after interruptions such as cancellation, database shutdown, or crashes.
This PR introduces the data structures needed to store subcompaction progress in memory, along with serialization and deserialization support to persist and parse this progress to/from "a manifest-like compaction progress file" (the actual creation of such file is in upcoming PRs).
Flow of resuming: DB::OpenAndCompact() -> Compaction progress file -> SubcompactionProgress -> CompactionJob
Flow of persistence: CompactionJob -> SubcompactionProgress -> Compaction progress file -> DB that is called with OpenAndCompact()
**Summary**
Progress represented by `SubcompactionProgress` will be tracked at the scope of a subcompaction, which is the smallest independent unit of compaction work.
The frequency of recording this progress is once every N compaction output files (to be detailed in future PRs).
When recording, all fields, except for the output files metadata in `SubcompactionProgress`, will directly overwrite the corresponding fields from the last saved progress (See `SubcompactionProgress` and `SubcompactionProgressBuilder` for more).
As a bonus, this PR refactors the file metadata encoding and decoding utilities into two static helper functions, EncodeToNewFile4() and DecodeNewFile4From(), to support subcompaction progress usage.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13928
Test Plan:
- Added various `SubcompactionProgressTest` unit tests in version_edit_test.cc to verify basic serialization/deserialization and forward compatibility handling
- Existing UTs and stress/crash test
**Follow up:**
- Move output entry number and file verification to after each file creation so we can remove kNumProcessedOutputRecords persistence support and make resuming compaction work with `paranoid_file_checks=true` (by default false). Output verification will be done before persistence of progress. As long as this follow-up is done before the landing of the integration PR to create the progress file, we can change the manifest-like compaction progress file format freely.
Reviewed By: jaykorean
Differential Revision: D81986583
Pulled By: hx235
fbshipit-source-id: b42766da7d9c2e2f596c892d050c753238d1039f
Summary:
for MultiScan and UDI we start to use bound check from index iterator, so removing this assert here.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13988
Test Plan: existing test
Reviewed By: hx235
Differential Revision: D82993180
Pulled By: cbi42
fbshipit-source-id: 442b2e83cb3aef96fc1a825bf733af9ce59c21c1
Summary:
It is useful to be able to specify output temperatures in the CompactFiles API. For example it may be useful to store small L0 files produced by flushes locally, while larger intra-L0 compactions can store the compacted L0 file remotely.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13955
Test Plan: New unit tests
Reviewed By: jaykorean
Differential Revision: D82492503
Pulled By: joshkang97
fbshipit-source-id: e1225fe572a15d7c5c30a265762b048a4a9e7f0b
Summary:
- updated release note
- updated version to 10.8 in version.h
- added 10.7 to check_format_compatible.sh
- did not updated folly commit hash due to some build failure.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13980
Reviewed By: xingbowang
Differential Revision: D82882035
Pulled By: cbi42
fbshipit-source-id: b5e0e78570fdd492d592ee77bd3901e4b39c25fb
Summary:
the test did not consider the ingestion_option settings that can result in different error message. This PR fixes the relevant check and ensure we have enough randomness in this test.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13979
Test Plan: `gtest-parallel --repeat=20 --workers=20 ./external_sst_file_test --gtest_filter="*VaryingOptions/IngestDBGeneratedFileTest2.NonZeroSeqno/*"`
Reviewed By: hx235
Differential Revision: D82873439
Pulled By: cbi42
fbshipit-source-id: b0d74bf26a502ca3db59b4a0ea9717bf7d027400
Summary:
Start the process of migrating the HCC implementation over to my new system of "bit field atomics" to clean up the code. Here I took on the simplest of the three "bit field atomic" formats in HCC, but ended up moving some things around to end up with less plumbing of definitions and values overall.
In the process, updated BitFields to use the CRTP pattern to simplify some things (see updated example, etc.)
https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13965
Test Plan: existing tests. ClockCacheTest.ClockEvictionEffortCapTest caught a regression during my development, and the crash test has a history of finding subtle HCC bugs.
Reviewed By: xingbowang
Differential Revision: D82669582
Pulled By: pdillinger
fbshipit-source-id: b73dd47361cbe9fbd334413dd4ce01b3c667159e
Summary:
longtime wanted e.g. for easy tab-completion, now implemented
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13978
Test Plan: pretty good unit test updates, manual testing
Reviewed By: cbi42
Differential Revision: D82857671
Pulled By: pdillinger
fbshipit-source-id: d2b63b7d15e61ebf22c58a6ecd3003311e2d03cb
Summary:
* There was a bug where the compression manager would actually not be used for recompress because the options passed to SstFileDumper were not respected. That is now fixed by respecting the Options.
* Refactored SstFileDumper not to take explicit options that could naturally be embedded in Options.
* Report compressed and uncompressed data block sizes (and ratio) instead of total file size (without a useful ratio). Needed to add a new table property to support that.
* Allow --block_size instead of --set_block_size to be consistent with other tools
* Allow --compression_level as shorthand for both _from and _to options, for simplicity and consistency with other tools
* Support --compression_parallel_threads option
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13977
Test Plan:
* sst_dump manual testing
* TableProperties unit tests updated
* Made it much easier to detect when a functional change requires an update to ParseTablePropertiesString() (rather than causing cryptic downstream failures)
Reviewed By: cbi42
Differential Revision: D82841412
Pulled By: pdillinger
fbshipit-source-id: 8d3421be4d2a3e25b7590cd59d204a3779c2a928
Summary:
Currently in MultiScan we only unpins a block after we scan through it. This PR adds unpinning during Seek to release all blocks pinned by the previous scan range. This is useful when users do not scan through the entire scan range. I plan to follow up with support for aborting async IOs from the previous scan.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13972
Test Plan: new test MultiScanUnpinPreviousBlocks validates unpinning behavior
Reviewed By: xingbowang
Differential Revision: D82779504
Pulled By: cbi42
fbshipit-source-id: 17ba7d1e5a6d8ff09ceea57b79c18febfba75584
Summary:
This change adds FFI support for exporting column family checkpoints, basic access to the export/import files metadata, and creating column families by import.
I've been able to successfully use this to [add checkpoint export and import support to `rust-rocksdb`](https://github.com/pcholakov/rust-rocksdb/pull/2), a forked version of which has been successfully used in production for some time.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13874
Reviewed By: hx235
Differential Revision: D82343565
Pulled By: jaykorean
fbshipit-source-id: fb4182bdfd5cce10743c021a1ac636fd6ac48df3
Summary:
If there's a static initialization of Options() this could now instantiate an AutoHyperClockTable before kPageSize is initialized. Break the dependency because it's a very minor optimization.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13973
Test Plan: internal CI (not able to reproduce locally)
Reviewed By: hx235
Differential Revision: D82789849
Pulled By: pdillinger
fbshipit-source-id: 3f32b5779a4f56d2071be5aadacda2bf0f4b895d
Summary:
Add a new CF immutable option `paranoid_memory_check_key_checksum_on_seek` that allows additional data integrity validations during seek on SkipList Memtable. When this option is enabled and memtable_protection_bytes_per_key is non zero, skiplist-based memtable will validate the checksum of each key visited during seek operation. The option is opt-in due to performance overhead. This is an enhancement on top of paranoid_memory_checks option.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13902
Test Plan:
* new unit test added for paranoid_memory_check_key_checksum_on_seek=true.
* existing unit test for paranoid_memory_check_key_checksum_on_seek=false.
* enable in stress test.
Performance Benchmark: we check for performance regression in read path where data is in memtable only. For each benchmark, the script was run at the same time for main and this PR:
### Memtable-only randomread ops/sec:
* Value size = 100 Bytes
```
for B in 0 1 2 4 8; do (for I in $(seq 1 50);do ./db_bench --benchmarks=fillseq,readrandom --write_buffer_size=268435456 --writes=250000 --value_size=100 --num=250000 --reads=500000 --seed=1723056275 --paranoid_memory_check_key_checksum_on_seek=true --memtable_protection_bytes_per_key=$B 2>&1 | grep "readrandom"; done;) | awk '{ t += $5; c++; print } END { print 1.0 * t / c }'; done;
```
1. Main: 928999
2. PR with paranoid_memory_check_key_checksum_on_seek=false: 930993 (+0.2%)
3. PR with paranoid_memory_check_key_checksum_on_seek=true:
3.1 memtable_protection_bytes_per_key=1: 464577 (-50%)
3.2 memtable_protection_bytes_per_key=2: 470319 (-49%)
3.3 memtable_protection_bytes_per_key=4: 468457 (-50%)
3.4 memtable_protection_bytes_per_key=8: 465061 (-50%)
* Value size = 1000 Bytes
```
for B in 0 1 2 4 8; do (for I in $(seq 1 50);do ./db_bench --benchmarks=fillseq,readrandom --write_buffer_size=268435456 --writes=250000 --value_size=1000 --num=250000 --reads=500000 --seed=1723056275 --paranoid_memory_check_key_checksum_on_seek=true --memtable_protection_bytes_per_key=$B 2>&1 | grep "readrandom"; done;) | awk '{ t += $5; c++; print } END { print 1.0 * t / c }'; done;
```
1. Main: 601321
2. PR with paranoid_memory_check_key_checksum_on_seek=false: 607885 (+1.1%)
3. PR with paranoid_memory_check_key_checksum_on_seek=true:
3.1 memtable_protection_bytes_per_key=1: 185742 (-69%)
3.2 memtable_protection_bytes_per_key=2: 177167 (-71%)
3.3 memtable_protection_bytes_per_key=4: 185908 (-69%)
3.4 memtable_protection_bytes_per_key=8: 183639 (-69%)
Reviewed By: pdillinger
Differential Revision: D81199245
Pulled By: xingbowang
fbshipit-source-id: e3c29552ab92f2c5f360361366a293fa26934913
Summary:
Force caller of MultiScanArgs to pass comparator. Pass comparator from CF handle to MultiScanArgs in NewMultiScan.
Expand MultiScanArgs unit test with different comparator.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13970
Test Plan: unit test
Reviewed By: cbi42
Differential Revision: D82739270
Pulled By: xingbowang
fbshipit-source-id: e709f4a333ad547c0ba6d24d8fb2b22e50e8a12f
Summary:
**Context/Summary:**
`Status::state` can be nullptr when created with no specific error message. std::strstr on nullptr caused some segfault in our stress test.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13968
Test Plan: Monitor stress test
Reviewed By: jaykorean
Differential Revision: D82695541
Pulled By: hx235
fbshipit-source-id: cf08f70163a9ee6c911cdc3a3d79acd3429f0d15
Summary:
After seeing more people hit issues with thrashing small LRUCache shards and AutoHCC running fully in production for a while on a very large service, here I make these updates:
* In the public API, mark the case of `estimated_entry_charge = 0` (which is how you select AutoHCC) as production-ready and generally preferred. That means devoting a lot less space to how to tune FixedHCC (`estimated_entry_charge > 0`) because it is not generally recommended anymore even though in theory it is the fastest (conditional on a fragile configuration).
* In the public API, add more detail about potential problems with LRUCache and explicitly endorse HCC.
* When a default block cache is created, use AutoHCC instead of LRUCache. It's still a 32MB cache but that's just one cache shard for AutoHCC so the risk of issues with small cache shards is dramatically reduced. And a single AutoHCC shard is still essentially wait-free.
* Improve the handling of the hypothetical scenario of a failed anonymous mmap. This is hardly a concern for 64-bit Linux and likely most other OSes. It would in theory be possible to fall back on LRUCache in that case but the code structure makes that annoying/challenging. Instead we crash with an appropriate message.
* Cleaned up some includes
* Fixed some previously unreported leaks (better assertions on HCC perhaps, some subtle behavior changes)
* Added a new mode to cache_bench (detailed below)
* Avoid a particularly costly sanity check in `~AutoHyperClockTable()` even in debug builds so that unit testing, etc., isn't bogged down, except keep it in ASAN build.
Planned follow-up:
* Update HCC implementation to use my new "bit field atomics" API introduced in https://github.com/facebook/rocksdb/issues/13910 to make it easier to read and maintain
Possible follow-up:
* Re-engineer table cache to use AutoHCC also, instead of LRUCache and a single mutex to ensure no duplication across threads. (a) Pad table cache key to 128 bits for AutoHCC. (b) Stripe/shard the no-duplication mutex. (HCC's consistency model is too weak for concurrent threads to use its API to agree on a winner, even if entries could be inserted in an "open in progress" state.)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13964
Test Plan:
existing tests. ClockCacheTest.ClockEvictionEffortCapTest caught a regression during my development, and the crash test has a history of finding subtle HCC bugs.
## Performance
Although we've validated AutoHCC performance under high load, etc., before we haven't really considered whether there will be unacceptable overheads for small DBs and CFs, e.g. in unit tests. For this, I have added a new mode to cache_bench: with the -stress_cache_instances=n parameter, it will create and destroy n empty cache instances several times. In the debug build, this found that a particular check in `~AutoHyperClockTable()` was extremely costly for short-lived caches (fixed). Beyond that, we can answer the question of whether it is feasible for a single process to host 1000 DBs each with 1000 CFs with default block cache instances, after moving LRUCache -> AutoHCC, for example:
```
/usr/bin/time ./cache_bench -stress_
cache_instances=1000000 -cache_type=auto_hyper_clock_cache -cache_size=33554432
```
Release build:
Average 9.8 us per 32MB LRUCache creation, 2.9 us per destruction, 24.6GB max RSS (~25KB each)
->
Average 4.3 us per 32MB AutoHCC creation, 4.9 us per destruction, 4.8GB max RSS (~5KB each)
Debug build:
Average 10.9 us per 32MB LRUCache creation, 3.5 us per destruction, 28.7GB max RSS (~29KB each)
->
Average 4.5 us per 32MB AutoHCC creation, 4.9 us per destruction, 4.7GB max RSS (~5KB each)
Despite the anonymous mmaps, it's apparently more efficient for default/small/empty structures. This is likely due to the dramatically low number of cache shards at this size. If we switch to `-stress_cache_instances=10000 -cache_size=1073741824`:
Release build:
Average 10.6 us per 1GB LRUCache, 2.8 us per destruction, 2.3 GB max RSS (~230KB each)
->
Average 130 us per 1GB AutoHCC creation, 153 us per destruction, 1.5 GB max RSS (~150KB each)
Debug build:
Average 11.2 us per 1GB LRUCache, 3.6 us per destruction, 2.4 GB max RSS (~240KB each)
->
Average 130 us per 1GB AutoHCC creation, 150 us per destruction, 1.6 GB max RSS (~160KB each)
Here it's clear that we are paying a price in time for setting up all those mmaps for the good number of cache shards and potential table growth, even though the RSS is well under control. However, I am not concerned about this at all, as it's unlikely to slow down anything notably such as unit tests. Before and after full testsuite runs confirm:
3327.73user 5188.71system 3:38.88elapsed -> 3312.07user 5704.77system 3:41.61elapsed
There is increased kernel time but acceptable. With ASAN+UBSAN:
11618.70user 15671.30system 5:54.68elapsed -> 12595.81user 16159.67system 6:32.77elapsed
Acceptable given that our ASAN+UBSAN builds are not the slowest in CI
Reviewed By: hx235
Differential Revision: D82661067
Pulled By: pdillinger
fbshipit-source-id: ab25c766ca70f2b8664849c2a838b9e1b4e72d3b
Summary:
when ingesting DB generated file with non-zero sequence number, we need smallest seqno of each file for file meta data. To avoid full table scan, we record this information in table property and use it during file ingestion.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13942
Test Plan: new unit test and updated existing unit test.
Reviewed By: hx235
Differential Revision: D82331802
Pulled By: cbi42
fbshipit-source-id: 3009a6801ca7092cd0fde33692db1a13567068a9
Summary:
This PR fixes a bug in BlockBasedTableIterator::Prepare in conjunction with a user defined index (UDI). If the UDI determines a scan range to be empty and thus returns the kOutOfBound iteration result during Seek, the iteration result is not propagated up and Prepare() assumes end of file and aborts the remaining scans. This results in incorrect behavior and unpredictable multi scan results.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13960
Test Plan: Add unit test to table_test.cc
Reviewed By: xingbowang
Differential Revision: D82590892
Pulled By: anand1976
fbshipit-source-id: 8cfaaae2bb1a9509ddf8ec967cb8a8801748413d
Summary:
* Fix compaction/flush CPU usage stats to include CPU usage by parallel compression workers. (Validated with manual db_bench testing.)
* Disable the parallel compression framework when compression is disabled. See new code comment for details, because in theory it could be useful to hide SST write latency, but manual testing with db_bench and -rate_limiter_bytes_per_sec or -simulate_hdd options shows no useful increase in throughput, just more CPU usage.
* Fix some minor clean-up items in the implementation
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13959
Test Plan: Also ran some tests like in https://github.com/facebook/rocksdb/issues/13910 to ensure the new CPU usage tracking did not regress performance, all good.
Reviewed By: xingbowang
Differential Revision: D82556686
Pulled By: pdillinger
fbshipit-source-id: 77c522159a7e6ab0ab6f7fb1d662070a46661557
Summary:
The stress test runs concurrent transactions through many threads at the same time on a shared key space. It is possible that a dead lock or a timeout is detected from the transactiondb layer. When this happens, simply return from the function and continue the test, instead of fail the test.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13950
Test Plan: Stress test pass locally with the same random seed from stress test 14723229280871643749.
Reviewed By: hx235
Differential Revision: D82373959
Pulled By: xingbowang
fbshipit-source-id: 5d72e89998171c5844fb22f13d8f061f81014c7d
Summary:
... reporting false positive double-lock on some of the new parallel compression code. Switching from std::condition_variable to condition_variable_any simply changes the FP from double-lock to lock inversion. In addition, leaking ParallelCompressionRep instances to avoid memory location reuse fails to fix the FP reports. Thus, I've decided to disable the watchdog with GCC+TSAN.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13958
Test Plan: local crash test runs could reproduce, now don't reproduce. CLANG TSAN doesn't seem to be reporting the same supposed issues
Reviewed By: xingbowang
Differential Revision: D82555968
Pulled By: pdillinger
fbshipit-source-id: 537fbc3a787f917915a6faf0bdedd1449a7f378a
Summary:
Complete redo of parallel compression in block_based_table_builder.cc to greatly reduce cross-thread hand-off and blocking. A ring buffer of blocks-in-progress is used to essentially bound working memory while enabling high throughput. Unlike before, all threads can participate in compression work, for a kind of work-stealing algorithm that reduces the need for threads to block. This builds on improvements in https://github.com/facebook/rocksdb/pull/13850
Previously, there was either
* parallel_threads==1, the *emit thread* (caller from flush/compaction) doing all the work
* parallel_threads > 1, the emit thread generates uncompressed blocks, `parallel_threads` worker threads compress blocks, and a writer thread writes to the SST file. Total of `parallel_threads + 2` threads participating. (Other bookkeeping in emit and write steps omitted from description for simplicity.)
Now we have either
* parallel_threads==1 (same), the emit thread doing all the work
* parallel_threads > 1, the emit thread generates uncompressed blocks and can take up compression work when the ring buffer is full; `parallel_threads` worker threads have as their top priority to write compressed blocks to the SST file but also take up compression work in priority order of next-to-write. Total of `parallel_threads + 1` threads participating. In some cases, this could result in less throughput than before, but arguably the previous implementation was using more threads than explicitly allowed.
## Future/alternate considerations
Although we could likely have used some framework for micro-work sharing across threads, that could be difficult with the asymmetry of work loads and thread affinity. Specifically, (a) it would be quite challenging to allow emit work in other threads, because it happens in the caller of BlockBasedTableBuilder, (b) async programming is unlikely to pay off until we have an async interface for writing SST files, and (c) this implementation will nevertheless serve as a benchmark for what we lose or gain in such a framework vs. a hand-tuned system.
This implementation still creates and destroys threads for each SST file created. We hope in the future to have more governance and/or pooling of worker threads across various flushes and compactions, but that is not available currently and would require significant design and implementation work.
## More details
* This implementation makes use of semaphores for idling and re-waking threads. `std::counting_semaphore` and `binary_semaphore` offer the best performance (see benchmark results below) but some implementations are known to have correctness bugs. Also, my attempt at upgrading CI for C++20 support (required for these) in https://github.com/facebook/rocksdb/pull/13904 is actually incomplete. Therefore, using these structures is opt-in with `-DROCKSDB_USE_STD_SEMAPHORES` at compile time, and a naive semaphore implementation based on mutex and condvar is used by default. A folly alternative (folly::fibers::Semaphore) was dropped in during development and found to be less efficient than the naive implementation. One CI job is upgraded to test with the new opt-in.
* One of the biggest concerns about correctness/reliability for this implementation is the possibility of hitting a deadlock, in part because that is not well checked in the DB crash test (a challenging problem!). Note also that with the parallel compression improvements in this release, I am calling the feature production-ready, so there is an extra level of confidence needed in the reliability of the feature. Thus, for DEBUG builds including crash test, I have added a watchdog thread to each parallel SST construction that heuristically checks for the most likely kinds of deadlock that could happen, including for the case of buggy semaphore implementations. It periodically verifies that some thread is outside of its "idle" state, and if the watchdog wakes up repeatedly to see all live threads stuck in their idle state (even if wake-up was attempted) then it declares a deadlock. This feature was manually verified for several seeded deadlock bugs. (More details in code comments.)
* For CPU efficiency, this implementation greatly simplifies the logic to estimate the outstanding or "inflight" size not yet written to the SST file. I expect this size to generally be insignificant relative to the full SST file size so is not worth careful engineering. And based on Meta's current needs, landing under-size for an SST file is better than over-size. See comments on `estimated_inflight_size` for details.
* Some other existing atomics in block_based_table_builder.cc modified to use safe atomic wrappers.
* Status handling in BlockBasedTableBuilder was streamlined to get rid of essentially redundant `status`+`io_status` fields and associated code. Made small optimizations to reduce unnecessary IOStatus copies (with StatusOk()) and mark status conditional branches as LIKELY or UNLIKELY.
* Prefer inline field initialization to initialization in constructor.
* Minimize references to the `parallel_threads` configuration parameter for better separation of concerns / sanitization / etc. For example, use non-nullity of `pc_rep` to indicate that parallel compression is enabled (and active).
* Some other refactoring to aid the new implementation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13910
Test Plan:
## Correctness
Already integrated into unit tests and crash test. CI updated for opt-in semaphore implementation. Basic semaphore unit tests added/updated.
As for the tremendous simplification of logic relating to hitting target SST file size, as expected, the new behavior could under-shoot the single-threaded behavior by a small number of blocks, which will typically affect the file size by ~1/1000th or less. I think that's a good trade-off for cutting out unnecessarily complex code with non-trivial CPU cost (FileSizeEstimator).
```
./db_bench -db=/dev/shm/dbbench_filesize_after8 -benchmarks=fillseq,compact -num=10000000 -compression_type=zstd -compression_level=8 -compression_parallel_threads=8
```
Before, PT=8 & PT=1, and After PT=1 the same or very similar
```
-rw-r--r-- 1 peterd users 67474097 Sep 12 15:32 000052.sst
-rw-r--r-- 1 peterd users 67474214 Sep 12 15:32 000053.sst
-rw-r--r-- 1 peterd users 67473834 Sep 12 15:32 000054.sst
-rw-r--r-- 1 peterd users 67473437 Sep 12 15:32 000055.sst
-rw-r--r-- 1 peterd users 67473835 Sep 12 15:32 000056.sst
-rw-r--r-- 1 peterd users 67473204 Sep 12 15:33 000057.sst
-rw-r--r-- 1 peterd users 67473294 Sep 12 15:33 000058.sst
-rw-r--r-- 1 peterd users 67473839 Sep 12 15:33 000059.sst
```
After, PT=8 (worst case here ~0.05% smaller)
```
-rw-r--r-- 1 peterd users 67463189 Sep 12 14:55 000052.sst
-rw-r--r-- 1 peterd users 67465233 Sep 12 14:55 000053.sst
-rw-r--r-- 1 peterd users 67466822 Sep 12 14:55 000054.sst
-rw-r--r-- 1 peterd users 67466221 Sep 12 14:55 000055.sst
-rw-r--r-- 1 peterd users 67441675 Sep 12 14:55 000056.sst
-rw-r--r-- 1 peterd users 67467855 Sep 12 14:55 000057.sst
-rw-r--r-- 1 peterd users 67455132 Sep 12 14:55 000058.sst
-rw-r--r-- 1 peterd users 67458334 Sep 12 14:55 000059.sst
```
## Performance, modest load
We are primarily interested in balancing throughput in building SST files and CPU usage in doing so. (For example, we could maximize throughput by having worker threads only spin waiting for work, but that would likely be extra CPU usage we want to avoid to allow other productive CPU work to be scheduled.) No read path code has been touched.
A benchmark script running "before" and "after" configurations at the same time to minimize random machine load effects:
```
$ SUFFIX=`tty | sed 's|/|_|g'`; for CT in none lz4 zstd; do for PT in 1 2 3 4 6 8; do echo -n "$CT pt=$PT -> "; (for I in `seq 1 10`; do BIN=/tmp/dbbench${SUFFIX}.bin; rm -f $BIN; cp db_bench $BIN; /usr/bin/time $BIN -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 -compression_type=$CT -compression_parallel_threads=$PT 2>&1; done) | awk '/micros.op/ {n++; sum += $5;} /system / { cpu += $1 + $2; } END { print "ops/s: " int(sum/n) " cpu*s: " cpu; }'; done; done
```
Before this change:
```
none pt=1 -> ops/s: 1999603 cpu*s: 72.08
none pt=2 -> ops/s: 1871094 cpu*s: 148.3
none pt=3 -> ops/s: 1882907 cpu*s: 147.7
lz4 pt=1 -> ops/s: 1987858 cpu*s: 94.74
lz4 pt=2 -> ops/s: 1590192 cpu*s: 182.65
lz4 pt=3 -> ops/s: 1896294 cpu*s: 174.7
lz4 pt=4 -> ops/s: 1949174 cpu*s: 172.26
lz4 pt=6 -> ops/s: 1912517 cpu*s: 175.91
lz4 pt=8 -> ops/s: 1930585 cpu*s: 176.71
zstd pt=1 -> ops/s: 1239379 cpu*s: 129.85
zstd pt=2 -> ops/s: 1171742 cpu*s: 226.12
zstd pt=3 -> ops/s: 1832574 cpu*s: 214.21
zstd pt=4 -> ops/s: 1887124 cpu*s: 212.51
zstd pt=6 -> ops/s: 1920936 cpu*s: 211.7
zstd pt=8 -> ops/s: 1885544 cpu*s: 214.87
```
After this change:
```
none pt=1 -> ops/s: 1964361 cpu*s: 72.66
none pt=2 -> ops/s: 1914033 cpu*s: 104.95
none pt=3 -> ops/s: 1978567 cpu*s: 100.24
lz4 pt=1 -> ops/s: 2041703 cpu*s: 92.88
lz4 pt=2 -> ops/s: 1903210 cpu*s: 121.64
lz4 pt=3 -> ops/s: 1973906 cpu*s: 122.22
lz4 pt=4 -> ops/s: 1952605 cpu*s: 123.05
lz4 pt=6 -> ops/s: 1957524 cpu*s: 124.31
lz4 pt=8 -> ops/s: 1986274 cpu*s: 129.06
zstd pt=1 -> ops/s: 1233748 cpu*s: 130.43
zstd pt=2 -> ops/s: 1675226 cpu*s: 158.41
zstd pt=3 -> ops/s: 1929878 cpu*s: 159.77
zstd pt=4 -> ops/s: 1916403 cpu*s: 160.99
zstd pt=6 -> ops/s: 1942526 cpu*s: 166.21
zstd pt=8 -> ops/s: 1966704 cpu*s: 171.56
```
For parallel_threads=1, results are very similar, as expected.
For parallel_threads>1, throughput is usually improved a bit, but cpu consumption is dramatically reduced. For zstd, maximum throughput is essentially achieved with pt=3 rather than the previous roughly pt=4 to 6. And the old used about 30% more CPU.
We can also compare with more expensive compression by raising the compression level.
```
SUFFIX=`tty | sed 's|/|_|g'`; CT=zstd; for CL in 4 6 8; do for PT in 1 4 8; do echo -n "$CT@$CL pt=$PT -> "; (for I in `seq 1 10`; do BIN=/tmp/dbbench${SUFFIX}.bin; rm -f $BIN; cp db_bench $BIN; /usr/bin/time $BIN -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 -compression_type=$CT -compression_parallel_threads=$PT -compression_level=$CL 2>&1; done) | awk '/micros.op/ {n++; sum += $5;} /system / { cpu += $1 + $2; } END { print "ops/s: " int(sum/n) " cpu*s: " cpu; }'; done; done
```
Before:
```
zstd@4 pt=1 -> ops/s: 883630 cpu*s: 161.12
zstd@4 pt=4 -> ops/s: 1878206 cpu*s: 243.25
zstd@4 pt=8 -> ops/s: 1885002 cpu*s: 245.89
zstd@6 pt=1 -> ops/s: 710767 cpu*s: 189.44
zstd@6 pt=4 -> ops/s: 1706377 cpu*s: 277.29
zstd@6 pt=8 -> ops/s: 1866736 cpu*s: 275.07
zstd@8 pt=1 -> ops/s: 529047 cpu*s: 237.87
zstd@8 pt=4 -> ops/s: 1401379 cpu*s: 330.61
zstd@8 pt=8 -> ops/s: 1895601 cpu*s: 321.59
```
After:
```
zstd@4 pt=1 -> ops/s: 889905 cpu*s: 161.03
zstd@4 pt=4 -> ops/s: 1942240 cpu*s: 193.18
zstd@4 pt=8 -> ops/s: 1922367 cpu*s: 205.21
zstd@6 pt=1 -> ops/s: 713870 cpu*s: 188.91
zstd@6 pt=4 -> ops/s: 1832314 cpu*s: 219.66
zstd@6 pt=8 -> ops/s: 1949631 cpu*s: 229.34
zstd@8 pt=1 -> ops/s: 530324 cpu*s: 238.02
zstd@8 pt=4 -> ops/s: 1479767 cpu*s: 271.65
zstd@8 pt=8 -> ops/s: 1949631 cpu*s: 275.6
```
And we can also look at the cumulative effect of this change and https://github.com/facebook/rocksdb/pull/13850 that will combine for the parallel compression improvements in the upcoming 10.7 release:
Before both:
```
lz4 pt=1 -> ops/s: 1954445 cpu*s: 95.14
lz4 pt=3 -> ops/s: 1687043 cpu*s: 186.62
lz4 pt=5 -> ops/s: 1708196 cpu*s: 188.33
zstd pt=1 -> ops/s: 1220649 cpu*s: 131.2
zstd pt=3 -> ops/s: 1658100 cpu*s: 227.08
zstd pt=5 -> ops/s: 1685074 cpu*s: 226.08
```
After:
```
lz4 pt=1 -> ops/s: 2048214 cpu*s: 93.24
lz4 pt=3 -> ops/s: 1922049 cpu*s: 122.9
lz4 pt=5 -> ops/s: 1980165 cpu*s: 122.49
zstd pt=1 -> ops/s: 1245165 cpu*s: 128.84
zstd pt=3 -> ops/s: 1956961 cpu*s: 158.73
zstd pt=5 -> ops/s: 1970458 cpu*s: 161.02
```
In summary, before with zstd default level, you could see only
* about 38% increase in throughput for about 73% increase in CPU usage
Now you can get
* about 58% increase in throughput for about 25% increase in CPU usage
## Performance, high load
To validate this for usage on remote compaction workers, we also need to test whether it falls over at high load or anything concerning like that. For this I did a lot of testing with concurrent db_bench and zstd compression_level=8 and parallel_thread (PT) in {1,8} trying to observe "bad" behaviors such as stalls due to preempted threads and such. On a 166 core machine where a "job" is a db_bench process running a fillseq benchmark similar to above in parallel with others, I could summarize the results like this:
10 jobs PT=8 vs. PT=1 -> 12% more CPU usage, 75% reduction in wall time, 1.9 jobs/sec (vs. 0.5)
50 jobs PT=8 vs. PT=1 -> 89% more CPU usage, 27% reduction in wall time, 3.1 jobs/sec (vs. 2.3)
100 jobs PT=8 vs. PT=1 -> 24% more CPU usage, 5% reduction in wall time, 3.25 jobs/sec (vs. 3.1)
150 jobs PT=8 vs. PT=1 -> 4% more CPU usage, 2% increase in wall time, 3.3 jobs/sec (vs. 3.4)
500 jobs PT=8 vs. PT=1 -> 1% more CPU usage, insignificant difference in wall time, 3.3 jobs/sec
Even when there are 4000 threads potentially competing for 166 cores, the throughput (3.3 jobs / sec) is still very close to maximum (3.4). Enabling parallel compression didn't result in notably less throughput (based on wall clock time for all jobs to complete) in any case tested above, and much higher throughput for many cases. If parallel compression causes us to tip from comfortably under-saturating to over-saturating the cores (as in the 50 jobs case), the overall CPU usage can be much higher, presumably due to lower CPU cache hit rates and maybe clock throttling, but parallel compression still has the throughput advantage in those cases.
In other words, what would we stand to gain from being able to intelligently share worker threads between compaction jobs? It doesn't seem that much.
Reviewed By: xingbowang
Differential Revision: D81365623
Pulled By: pdillinger
fbshipit-source-id: 5db5151a959b5d25b84dbe185bc208bd188f2d1c
Summary:
we saw some crash test failure at https://github.com/facebook/rocksdb/blob/f46242cef631351a5c8f4a7b0fb0935ec7fa61c8/table/block_based/block_based_table_iterator.cc#L964-L965. This is likely due to timestamp not being considered properly in some places in MultiScan code paths. This PR fixes the issue.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13938
Test Plan: crash test with timestamp and multiscan: `python3 -u ./tools/db_crashtest.py whitebox --enable_ts --iterpercent=60 --prefix_size=-1 --prefixpercent=0 --readpercent=0 --test_batches_snapshots=0 --use_multiscan=1 --read_fault_one_in=0 --kill_random_test=88888 --interval=60`
Reviewed By: anand1976
Differential Revision: D82175263
Pulled By: cbi42
fbshipit-source-id: 5d40ede1aec15f8faeaa7fd041b939e68611ff73
Summary:
This PR enables Stress Test to fall back to local compaction when a remote compaction fails, allowing the compaction to be retried on the main thread.
If the local compaction succeeds, the stress test will continue without failing. The main thread will log that the remote compaction failed and was retried locally, while detailed failure logs from the remote compaction attempt will still be printed by the worker thread for further investigation.
This approach allows us to keep collecting useful logs for diagnosing remote compaction failures in Stress Test, while ensuring the test continues to run with remote compaction enabled.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13945
Test Plan:
```
python3 -u tools/db_crashtest.py --cleanup_cmd='' --simple blackbox --remote_compaction_worker_threads=8 --interval=10
```
# Internal Only
https://www.internalfb.com/sandcastle/workflow/1315051091202224133https://www.internalfb.com/sandcastle/workflow/3382203320165521367https://www.internalfb.com/sandcastle/workflow/2616591383512372892https://www.internalfb.com/sandcastle/workflow/4607182418810099066
Reviewed By: hx235
Differential Revision: D82279337
Pulled By: jaykorean
fbshipit-source-id: 6f663ec2eeb642fd4ad885a90efb344432a32f89
Summary:
We should add error logging to be able to pinpoint why RocksDB is returning status `NotSupported` for `ReadAsync`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13936
Test Plan: Look at logs (and client logs of error status)
Reviewed By: anand1976
Differential Revision: D82141529
Pulled By: archang19
fbshipit-source-id: c71b70967457be35ef5168321d449f96b2b9441d
Summary:
Fix uninitialized value complaint in valgrind due to gtest print padded struct.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13934
Test Plan: CI. Verified that valgrind no longer complains about it.
Reviewed By: pdillinger
Differential Revision: D82124983
Pulled By: xingbowang
fbshipit-source-id: 99eb7bab99726c45affe0a231777e5951844d73b
Summary:
... and associated statistics, etc. Someone needs it, so here it is.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13927
Test Plan: Updated / extended / added some unit tests
Reviewed By: cbi42
Differential Revision: D81981469
Pulled By: pdillinger
fbshipit-source-id: 52558c08741890b781310906acbc18d9eb479363
Summary:
There are some internal use cases that do not map cleanly onto the existing `IOActivity` enums. This PR creates new custom IOActivity types that internal users can use as they see fit.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13924
Test Plan: Wrote a simple unit test
Reviewed By: pdillinger
Differential Revision: D82029992
Pulled By: archang19
fbshipit-source-id: a3e23c360baa96cd2e9adf570e71c6e43947bfc8
Summary:
PointLockManager manages point lock per key. The old implementation partition the per key lock into 16 stripes. Each stripe handles the point lock for a subset of keys. Each stripe have only one conditional variable. This conditional variable is used by all the transactions that are waiting for its turn to acquire a lock of a key that belongs to this stripe.
In production, we notified that when there are multiple transactions trying to write to the same key, all of them will wait on the same conditional variables. When the previous lock holder released the key, all of the transactions are woken up, but only one of them could proceed, and the rest goes back to sleep. This wasted a lot of CPU cycles. In addition, when there are other keys being locked/unlocked on the same lock stripe, the problem becomes even worse.
In order to solve this issue, we implemented a new PerKeyPointLockManager that keeps a transaction waiter queue at per key level. When a transaction could not acquire a lock immediately, it joins the waiter queue of the key and waits on a dedicated conditional variable. When previous lock holder released the lock, it wakes up the next set of transactions that are eligible to acquire the lock from the waiting queue. The queue respect FIFO order, except it prioritizes lock upgrade/downgrade operation.
However, this waiter queue change increases the deadlock detection cost, because the transaction waiting in the queue also needs to be considered during deadlock detection. To resolve this issue, a new deadlock_timeout_us (microseconds) configuration is introduced in transaction option. Essentially, when a transaction is waiting on a lock, it will join the wait queue and wait for the duration configured by deadlock_timeout_us without perform deadlock detection. If the transaction didn't get the lock after the deadlock_timeout_us timeout is reached, it will then perform deadlock detection and wait until lock_timeout is reached. This optimization takes the heuristic where majority of the transaction would be able to get the lock without perform deadlock detection.
The deadlock_timeout_us configuration needs to be tuned for different workload, if the likelihood of deadlock is very low, the deadlock_timeout_us could be configured close to a big higher than the average transaction execution time, so that majority of the transaction would be able to acquire the lock without performing deadlock detection. If the likelihood of deadlock is high, deadlock_timeout_us could be configured with lower value, so that deadlock would get detected faster.
The new PerKeyPointLockManager is disabled by default. It can be enabled by TransactionDBOptions.use_per_key_point_lock_mgr. The deadlock_timeout_us is only effective when PerKeyPointLockManager is used. When deadlock_timeout_us is set to 0, transaction will perform deadlock detection immediately before wait.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13731
Test Plan:
Unit test.
Stress unit test that validates deadlock detection and exclusive, shared lock guarantee.
A new point_lock_bench binary is created to help perform performance test.
Reviewed By: pdillinger
Differential Revision: D77353607
Pulled By: xingbowang
fbshipit-source-id: 21cf93354f9a367a78c8666596ed14013ac7240b
Summary:
A follow-up to https://github.com/facebook/rocksdb/issues/13904 which was incomplete in updating CI jobs to support C++20 because the C++20 usage was only in tests. Here we add subtle C++20 usage in the public API ("using enum" feature in db.h) to force the issue.
A lot of the work for this PR was in updating the Ubuntu22 docker image, for earlier compiler/runtime versions supporting C++20, and generating a new Ubuntu24 docker image, for later compiler/runtime versions. The Ubuntu22 image needed to be updated because there are incompatibilities with clang-13 + c++20 + libstdc++ for gcc 11, seen on these examples
```
#include <chrono>
int main(int argc, char *argv[]) {
std::chrono::microseconds d = {}; return 0;
}
```
and
```
#include <coroutine>
int main() { return 0; }
```
The second was causing recurring failures in build-linux-clang-13-asan-ubsan-with-folly, now fixed.
So we have to install clang's libc++ to compile with clang-13. I haven't been able to get this to work with some of the libraries like benchmark, glog, and/or gflags, but I'm able to compile core RocksDB with clang-13. On this docker image, an extra compiler parameter is needed to compile with gcc and glog because it's built from source perhaps not perfectly, because the ubuntu package transitively conflicts with libc++.
The Ubuntu24 image seems to be low-drama and generally work for testing out newer compiler versions. The mingw build uses Ubuntu24 because the mingw package on Ubuntu22 uses a gcc version that is too old.
And the mass of other code changes are trying to work around new warnings, mostly from clang-analyze, which I upgraded to clang-18 in CI.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13915
Test Plan: CI, including temporarily including the nightly jobs in the PR jobs in earlier revisions to test and stabilize
Reviewed By: archang19
Differential Revision: D81933067
Pulled By: pdillinger
fbshipit-source-id: 7e33823006a79d5f3cf5bc1d625f0a3c08a7d74c
Summary:
After running stress test over a week, we've identified more failures to fix. While we work on the fix, disable the remote compaction temporarily to reduce noise and avoid these failures hiding other failures.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13925
Test Plan: CI
Reviewed By: anand1976
Differential Revision: D81934248
Pulled By: jaykorean
fbshipit-source-id: 9ac11926429eebe1aebf7b520a548dc5987b7d76
Summary:
This diff adds logging in various places in the external file ingestion code where we check for non-OK status codes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13905
Test Plan: Debugging external file ingestion should be easier with additional logging.
Differential Revision: D81814033
Pulled By: archang19
fbshipit-source-id: 77f8b342cbad892acedc4603c02865c38886f2f4
Summary:
If user_defined_index_factory in BlockBasedTableOptions is configured and we try to open an SST file without the corresponding UDI (either during DB open or file ingestion), ignore a failure to load the UDI by default. If fail_if_no_udi_on_open in BlockBasedTableOptions is true, then treat it as a fatal error.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13921
Test Plan: Update unit tests
Reviewed By: xingbowang
Differential Revision: D81826054
Pulled By: anand1976
fbshipit-source-id: f4fe0b13ccb02b9448622af487680131e349c52b
Summary:
Add a new option `MultiScanArgs::max_prefetch_size` that limits the memory usage of per file pinning of prefetched blocks. Note that this only accounts for compressed block size. This is intended to be a stopgap until we implement some kind of global prefetch manager that limits the global multiscan memory usage.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13920
Test Plan: new unit test `./block_based_table_reader_test --gtest_filter="*MultiScanPrefetchSizeLimit/*"`
Reviewed By: xingbowang
Differential Revision: D81630629
Pulled By: cbi42
fbshipit-source-id: 9f66678915242fe1220620531a4b9fd22747cdea
Summary:
# Summary
Until we get WAL + Remote Compaction in Stress Test working, temporarily disable this
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13919
Test Plan: Meta Internal CI run
Reviewed By: anand1976
Differential Revision: D81605621
Pulled By: jaykorean
fbshipit-source-id: 6e1f9a0a7a0f27e7465512689b51364b63ef3e2b
Summary:
Re-enabling Remote Compaction Stress Test with some changes to stress test feature combo sanitization changes
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13913
Test Plan:
Ran Meta Internal Tests for a few days
# Follow up
- Skip recovering from WAL in remote worker and re-enable WAL
- Investigate and fix races with Integrated BlobDB
Reviewed By: hx235
Differential Revision: D81509225
Pulled By: jaykorean
fbshipit-source-id: 949762c48ece0a25e3d0281e3510f1e7d3fe3667
Summary:
**Context/Summary:**
A small change as titled.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13891
Test Plan: - Existing UT and rehearsal stress test
Reviewed By: jaykorean
Differential Revision: D80588011
Pulled By: hx235
fbshipit-source-id: 6987e08a4855782305ad742eef6c0196da0d67ca
Summary:
I am wanting to use std::counting_semaphore for something and the timing seems good to require C++20 support. The internets suggest:
* GCC >= 10 is adequate, >= 11 preferred
* Clang >= 10 is needed
* Visual Studio >= 2019 is adquate
And popular linux distributions look like this:
* CentOS Stream 9 -> GCC 11.2 (CentOS 8 is EOL)
* Ubuntu 22.04 LTS -> GCC 11.x (Ubuntu 20 just ended standard support)
* Debian 12 (oldstable) -> GCC 12.2
* (Debian 11 has ended security updates, uses GCC 10.2)
This required generating a new docker image based on Ubuntu 22 for CI using gcc. The existing Ubuntu 20 image works for covering appropriate clang versions (though we should maybe add a much later version as well, in the next increment of our Ubuntu 22 image; however the minimum available clang build from apt.llvm.org for Ubuntu 22 is clang 13).
Update to SetDumpFilter is to quiet a mysterious gcc-13 warning-as-error.
Removed --compile-no-warning-as-error from a cmake command line because cmake in the new docker image is too old for this option.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13904
Test Plan: CI, one minor unit test added to verify std::counting_semaphor works
Reviewed By: xingbowang
Differential Revision: D81266435
Pulled By: pdillinger
fbshipit-source-id: 26040eeccca7004416e29a6ff4f6ea93f2052684
Summary:
**Context/Summary:**
`ProcessKeyValueCompaction()` has grown too long to resonate or add any logic to resume from some key and save progress for resumable compaction. This PR breaks this function into smaller functions. Almost all of them are cosmetic changes, except for one thing pointed out in below PR conversation.
Specially, this PR did the following:
- Added `SubcompactionInternalIterators`, `SubcompactionKeyBoundaries` and `BlobFileResources` to manage the lifetime of the local variables of the original functions to be used across smaller functions
- Moved AutoThreadOperationStageUpdater, some IO stats measurement to a different place that makes more sense
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13879
Test Plan: Existing UT
Reviewed By: jaykorean
Differential Revision: D80216092
Pulled By: hx235
fbshipit-source-id: 515615906e5e5fd5ec191bcdd4126f17d282cac2
Summary:
The implementation of parallel compression has historically scaled rather poorly, or perhaps modestly with heavy compression, topping out around 3x throughput vs. serial and incurring big overheads in CPU consumption relative to the throughput.
This change addresses one source of that extra CPU consumption: stashing all the keys of a block for later processing into building index and filter blocks. Historically with parallel compression, the index and filter block updates were handled in the last stage of processing along with writing each data block to the file writer. This was because the index blocks needed to know the BlockHandle of the new data block, which could only be known after every preceeding data block was compressed, to know the starting location for the BlockHandle. And because index and filter partitions were historically coupled (see decouple_partitioned_filters), filter updates had to happen at the same time.
Here we get rid of stashing the keys for later processing and the extra CPU associated with it, by
* Creating a two stage process of adding to index blocks ("prepare" and "finish" each entry; one entry per data block). The two stages must be executable in parallel for separate index entries. NOTE: not yet supported by UserDefinedIndex
* Requiring decouple_partitioned_filters=true for parallel compression, because we now add to filters in the first stage of processing when each key is readily available and we cannot couple that with finalizing index entries in the last stage of processing.
It might seem like adding to filters is something that is expensive (hashing etc.) and should be kept out of the bottle-neck first stage of processing (which includes walking the compaction iterator) but it's probably similar cost to simply stashing the keys away for later processing. (We might be able to reduce a bottle-neck by stashing hashes, but we're not to a point where that is worth the effort.)
And it makes sense to make two more simple public API updates in conjunction with this:
* Set decouple_partitioned_filters=true by default. No signs of problems in production.
* Mark parallel compression as production-ready. It's being thoroughly tested in the crash test, successfully, and in limited production uses.
Follow-up:
* Improve the threading/sychronization model of parallel compression for the next major efficiency improvement
* Consider supporting the parallel-compatible index building APIs with UserDefinedIndex, unless it's considered too dangerous to expect users to safely handle the multi-threading.
* (In a subsequent release) remove all the code associated with coupling filter and index partitions and mark the option as ignored.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13850
Test Plan:
for correctness, existing tests
## Performance Data
The "before" data here includes revert of https://github.com/facebook/rocksdb/issues/13828 for combined performance measurement of this change and that one.
```
SUFFIX=`tty | sed 's|/|_|g'`; for CT in lz4 zstd lz4; do for PT in 1 2 3 4 6 8; do echo "$CT pt=$PT"; (for I in `seq 1 1`; do BIN=/dev/shm/dbbench${SUFFIX}.bin; rm -f $BIN; cp db_bench $BIN; /usr/bin/time $BIN -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=30000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 -compression_type=$CT -compression_parallel_threads=$PT 2>&1 | tail -n 3 | head -n 2; done); done; done
```
To get a sense of the overall performance relative to number of parallel threads, we vary that with popular fast compression and popular heavier weight compression (some noise in this data, don't interpret each data point too strongly)
lz4 pt=1
2107431 -> 2112941 ops/sec (+0.3% - improvement)
(26.51 + 0.75) = 27.26 CPU sec -> (26.63 + 0.79) = 27.42 CPU sec (+0.6% - regression)
lz4 pt=2
1606660 -> 1580333 ops/sec (-1.6% - regression)
(47.10 + 8.37) = 55.47 CPU sec -> (45.05 + 9.23) = 54.28 CPU sec (-2.2% - improvement)
lz4 pt=3
1701353 -> 1889283 ops/sec (+11.1% - improvement)
(47.23 + 8.29) = 55.52 CPU sec -> (43.89 + 8.33) = 52.22 CPU sec (-6.0% - improvement)
lz4 pt=4
1651504 -> 1817890 ops/sec (+10.1% - improvement)
(48.07 + 8.31) = 56.38 CPU sec -> (44.77 + 8.45) = 53.22 CPU sec (-5.6% - improvement)
lz4 pt=6
1716099 -> 1888523 ops/sec (+10.1% - improvement)
(47.50 + 8.45) = 55.95 CPU sec -> (44.25 + 8.73) = 52.98 CPU sec (-5.3% - improvement)
lz4 pt=8
1696840 -> 1797256 ops/sec (+5.9% - improvement)
(48.09 + 8.61) = 56.70 CPU sec -> (45.90 + 8.68) = 54.58 CPU sec (-3.8% - improvement)
Clearly parallel threads do not help with fast compression like LZ4, but it's not as bad as it was before.
zstd pt=1
1214258 -> 1202863 ops/sec (-0.9% - regression)
(38.26 + 0.66) = 38.92 CPU sec -> (39.37 + 0.69) = 40.06 CPU sec (+2.9% - regression)
zstd pt=2
1194673 -> 1152746 ops/sec (-3.5% - regression)
(61.01 + 9.85) = 70.86 CPU sec -> (58.28 + 9.99) = 68.27 CPU sec (-3.7% - improvement)
zstd pt=3
1653661 -> 1825618 ops/sec (+10.4% - improvement)
(60.07 + 8.45) = 68.52 CPU sec -> (56.03 + 8.43) = 64.46 CPU sec (-5.9% - improvement)
zstd pt=4
1691723 -> 1890976 ops/sec (+11.8% - improvement)
(59.72 + 8.46) = 68.18 CPU sec -> (55.96 + 8.27) = 64.23 CPU sec (-5.7% - improvement)
zstd pt=6
1684982 -> 1900002 ops/sec (+12.8% - improvement)
(58.89 + 8.26) = 67.15 CPU sec -> (55.98 + 8.48) = 64.46 CPU sec (-4.0% - improvement)
zstd pt=8
1648282 -> 1892531 ops/sec (+14.8% - improvement)
(59.43 + 8.63) = 68.06 CPU sec -> (56.49 + 8.32) = 64.81 CPU sec (-4.8% - improvement)
The throughput is now able to increase by *more than half* with lots of parallelism, rather than only *about a third*.
Scalability is a bit better with higher compression level, and we still see a benefit from this change. (We've also enabled partitioned indexes and filters here, which sees essentially the same benefits):
zstd pt=1 compression_level=7
595720 -> 597359 ops/sec (+0.3% - improvement)
(63.45 + 0.73) = 64.18 CPU sec -> (63.25 + 0.71) = 63.96 CPU sec (-0.3% - improvement)
zstd pt=4 compression_level=7
1527116 -> 1501779 ops/sec (-1.7% - regression)
(85.00 + 8.14) = 93.14 CPU sec -> (81.85 + 9.02) = 90.87 CPU sec (-2.5% - improvement)
zstd pt=6 compression_level=7
1678239 -> 1956070 ops/sec (+16.5% - improvement)
(83.77 + 8.11) = 91.88 CPU sec -> (79.87 + 7.78) = 87.65 CPU sec (-4.6% - improvement)
zstd pt=8 compression_level=7
1696132 -> 1953041 ops/sec (+15.1% - improvement)
(83.97 + 8.14) = 92.11 CPU sec -> (80.61 + 7.78) = 88.39 CPU sec (-4.1% - improvement)
With more tests, not really seeing any consistent differences with no parallelism (despite some micro-optimizations thrown in)
Reviewed By: hx235
Differential Revision: D79853111
Pulled By: pdillinger
fbshipit-source-id: 7a34fd7811217fb74fa6d3efaea7ffcce72beec7
Summary:
**Context/Summary:**
RocksDB stress test verifies IOActivity is set correctly through reusing the pass-in Read/Write options through assertion. This is too strict for API that does not take or do not need to take Read/WriteOptions yet hence assertion failure.
```
stderr:
db_stress: ... db_stress_tool/db_stress_env_wrapper.h:24: void rocksdb::(anonymous namespace)::CheckIOActivity(const IOOptions &): Assertion `io_activity == Env::IOActivity::kUnknown || io_activity == options.io_activity' failed.
Received signal 6 (Aborted)
```
An example is ManagedSnapshot snapshot_guard(db_); in TestMultiScan().
This PR ignores such check.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13898
Test Plan: The same command repro-ed this assertion failure passes after this fix
Reviewed By: archang19
Differential Revision: D80983214
Pulled By: hx235
fbshipit-source-id: d8b660f8c8771198bc7fa0e805c3e86d2584f03e
Summary:
**Context/Summary:**
Clear statistics reference from options_ to intentionally shorten the statistics object lifetime to be same as the db object (which is the common case in practice) and detect if RocksDB access the statistics beyond its lifetime.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13899
Test Plan: - [Ongoing] Stress test rehearsal
Reviewed By: pdillinger
Differential Revision: D80985435
Pulled By: hx235
fbshipit-source-id: ab238231cd81f47fa451aea12a0c85fa11d9ac81
Summary:
`IngestExternalFileOptions::allow_db_generated_files` requires SST files to have zero sequence number. This PR opens it up for any DB generated SST files. Currently we don't do global sequence number assignment when `allow_db_generated_files` is true, so we require that files do not overlap with any key in the CF. One behavior difference is that now we allow ingesting overlapping files when `allow_db_generated_files` is true. Users need to ensure that files are ordered such that later files have more recent updates.
Intended follow ups:
- Record smallest seqno in table property, so that we don't need to scan the file for it.
- Cover allow_db_generated_files in crash test. We may create a new DB and ingest all files from a CF for verification.
- Add APIs that uses allow_db_generated_files. For example, an API for ingesting SST files from a source CF, so that we take care of ingestion file ordering for user. If we are already getting metadata from the source CF, we may be use it as a hint for level placement instead of dividing input files into batches again (`ExternalSstFileIngestionJob::DivideInputFilesIntoBatches`).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13878
Test Plan: two new unit tests.
Reviewed By: hx235, xingbowang
Differential Revision: D80233727
Pulled By: cbi42
fbshipit-source-id: 74209386d8426c434bff2d9a734f06db537eb50c
Summary:
RocksDB currently aborts whenever `io_uring_wait_cqe` returns an error code. It also does not log what error code was returned.
While experimenting with `IO_URING`, my application crashed because of this.
I asked the Linux Kernel user group the best way to handle unsuccessful `io_uring_wait_cqe`.
It was recommended to retry on `EINTR`, `EAGAIN`, and `ETIME`. `ETIME` only happens when waiting with a timeout, so I am not handling it.
I also write to `stderr` so that we have some debugging information if we abort.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13890
Test Plan: Unfortunately this is hard to cover through unit/stress tests. We have to see what sort of errors get encountered in production.
Reviewed By: anand1976
Differential Revision: D80639955
Pulled By: archang19
fbshipit-source-id: e3a230bd37552ec0f36be34e6a4e53cfd2a254f1
Summary:
When fill_cache is ReadOptions is false, multi scan Prepare crashes with the following assertion failure. In this case, CreateAndPibBlockInCache needs to directly create a block with full ownership.
https://github.com/facebook/rocksdb/issues/9 0x00007f2fc003bc93 in __GI___assert_fail (assertion=0x7f2fc2147361 "pinned_data_blocks_guard[block_idx].GetValue()", file=0x7f2fc2146e08 "table/block_based/block_based_table_iterator.cc", line=1178, function=0x7f2fc2147262 "virtual void rocksdb::BlockBasedTableIterator::Prepare(const rocksdb::MultiScanArgs *)") at assert.c:101
101 in assert.c
https://github.com/facebook/rocksdb/issues/10 0x00007f2fc1d73088 in rocksdb::BlockBasedTableIterator::Prepare(rocksdb::MultiScanArgs const*) () from /data/users/anand76/rocksdb_anand76/librocksdb.so.10.6
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13889
Test Plan: Parameterize the DBMultiScanIteratorTest tests with fill_cache
Reviewed By: cbi42
Differential Revision: D80552069
Pulled By: anand1976
fbshipit-source-id: 1a0b64af1e14c63d826add1f994a832ebff12757
Summary:
I ran multiple runs of crash test jobs internally, so far I've seen one iterator mismatch and one assertion failure. I've added relevant logging improvements to help debugging them. use_multiscan will be stable within a crash test run to make it easier to triage.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13888
Test Plan: `python3 tools/db_crashtest.py whitebox --prefix_size=-1 --test_batches_snapshots=0 --use_multiscan=1 --read_fault_one_in=0 --kill_random_test=88888`
Reviewed By: anand1976
Differential Revision: D80627399
Pulled By: cbi42
fbshipit-source-id: 2fa3f77e730f5bc7d1d200dc122cf84e3558c588
Summary:
The assert occasionally throws off the stress test runs. We already have sufficient logging in place to collect the signal about secondary cache capacity exceeding primary cache reservation for further investigation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13885
Reviewed By: anand1976
Differential Revision: D80355513
Pulled By: mszeszko-meta
fbshipit-source-id: b36926f0493a3aca19818a1980ef79277db9fe7e
Summary:
Add the --list_meta_blocks option to sst_dump. This PR also refactors some of the test code in sst_dump_test.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13838
Reviewed By: cbi42
Differential Revision: D80320812
Pulled By: anand1976
fbshipit-source-id: 921b6560fbd756f5f8b364893700d240d3b7ad00
Summary:
Two instances of change that are not just cosmetic:
* InlineSkipList<>::Node::CASNext() was implicitly using memory_order_seq_cst to access `next_` while it's intended to be accessed with acquire/release. This is probably not a correctness issue for compare_exchange_strong but potentially a previously missed optimization.
* Similar for `max_height_` in Insert which is otherwise accessed with relaxed memory order.
* One non-relaxed access to `is_range_del_table_empty_` in a function only used in assertions. Access to this atomic is otherwise relaxed (and should be - comment added)
Didn't do all of memtable.h because some of them are more complicated changes and I should probably add FetchMin and FetchMax functions to simplify and take advantage of C++27 functions where available (intended follow-up).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13844
Test Plan: existing tests
Reviewed By: xingbowang
Differential Revision: D79742552
Pulled By: pdillinger
fbshipit-source-id: d97ce72ba9af6c105694b7d40622db9e994720cd
Summary:
This is an important feature for avoiding (reducing) unfair block cache treatment for a lot of blocks. It should also unlock some parallel optimizations (https://github.com/facebook/rocksdb/issues/13850) and code simplification.
Consider for follow-up:
* Feature to avoid majorly under0sized data blocks and filter and index partition blocks
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13881
Test Plan: existing tests, been looking good in production
Reviewed By: hx235
Differential Revision: D80288192
Pulled By: pdillinger
fbshipit-source-id: 5e274ffffb044713278d2a286db6bceaab2dadec
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13882
The `expect_valid_internal_key` parameter was always passed as true, with false only used in one unit test. This change removes the parameter and always fail compaction when encountering corrupted internal keys, which is the expected production behavior.
Reviewed By: mszeszko-meta
Differential Revision: D80287672
fbshipit-source-id: e30a282ac30d7fded677504cec11173de8d15167
Summary:
Allow a user defined index to be configured from a string
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13880
Test Plan: Add a unit test in table_test.cc
Reviewed By: bikash-c
Differential Revision: D80237701
Pulled By: anand1976
fbshipit-source-id: 8b3d0bcdfbb4bb76803916ea1b1f940a4d985dfd
Summary:
The original intention of the User Defined Index interface was to use the user key. However, the implementation mixed user and internal key usage. This PR makes it consistent. It also clarifies the UDI contract.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13865
Test Plan: Update tests in table_test.cc
Reviewed By: pdillinger
Differential Revision: D80050344
Pulled By: anand1976
fbshipit-source-id: ace47737d21684ec19709640a09e198cee2d98bd
Summary:
... as we see some issues that rehearsal stress test didn't surface.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13869
Reviewed By: cbi42
Differential Revision: D80103341
Pulled By: hx235
fbshipit-source-id: 8b2c1d76d4c3099727ba3a69de44de67afd64369
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13846
This diff addresses few issues that was identified during testing of the user defined index.
1. During the finishing of the index blocks, we run into an infinite loop because the user defined index wrapper returns
early on incomplete status. This happens because the wrapper blindly returns the status if it not OK. But, the status
could legitimately be `Incomplete()` for some indices like Partitioned Index (serving as the internal index for the UDI
wrapper). Fix is to exclude `Incomplete()` check from the status check early in the UDI wrapper's finish.
2. Once we fixed (1), we noticed that the meta blocks for the UDI-based index writer were not written out to the final
SST file. This is because the UDI's meta blocks are created after the internal index's meta blocks and the block-based
index builder didn't account for this. The fix is to finish the UDI wrapper first which will create the necessary meta blocks
and then finish the internal index. If the internal index is incomplete, the block-based index builder should still continue
to write out the meta blocks.
3. OnKeyAdded when delegating to the user-defined index should only pass the user key. The UDI builder doesn't
understand RocksDB's internal key format and while that poses interesting challenges when the UDI is used for non
last level SST files, our plan is to restrict the usage of the UDI to last level files only (for now).
Reviewed By: pdillinger
Differential Revision: D79781453
fbshipit-source-id: 2239c8fc016da55df5c24be6aacc8f6357cab029
Summary:
fix the following error showing up in continuous tests:
```
Makefile:186: Warning: Compiling in debug mode. Don't use the resulting binary in production
port/mmap.cc:46:15: error: first argument in call to 'memcpy' is a pointer to non-trivially copyable type 'rocksdb::MemMapping' [-Werror,-Wnontrivial-memcall]
46 | std::memcpy(this, &other, sizeof(*this));
| ^
port/mmap.cc:46:15: note: explicitly cast the pointer to silence this warning
46 | std::memcpy(this, &other, sizeof(*this));
| ^
| (void*)
1 error generated.
make: *** [Makefile:2580: port/mmap.o] Error 1
make: *** Waiting for unfinished jobs....
```
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13864
Test Plan: `make USE_CLANG=1 j=150 check` with https://github.com/facebook/rocksdb/blob/13f054febb26100184eeefaac11877d735d45ac2/build_tools/build_detect_platform#L61-L70 commented out.
Reviewed By: mszeszko-meta
Differential Revision: D80033441
Pulled By: cbi42
fbshipit-source-id: b2330eea71fe28243236b75128ec6f3f1e971873
Summary:
while debugging stress test failure, I noticed that sst_dump and ldb do not work if custom db_stress compression manager is used. This PR adds support for it.
```
./sst_dump --command=raw --show_properties --file=/tmp/rocksdb_crashtest_whitebox4ny5mass/000589.sst
options.env is 0x7f2b1f4b9000
Process /tmp/rocksdb_crashtest_whitebox4ny5mass/000589.sst
Sst file format: block-based
/tmp/rocksdb_crashtest_whitebox4ny5mass/000589.sst: Not implemented: Could not load CompressionManager: DbStressCustom1
/tmp/rocksdb_crashtest_whitebox4ny5mass/000589.sst is not a valid SST file
./ldb idump --db=/tmp/rocksdb_crashtest_whiteboxy_emah11 --ignore_unknown_options --hex >> /tmp/i_dump
Failed: Not implemented: Could not load CompressionManager: DbStressCustom1
```
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13827
Test Plan: manually tested that ldb and sst_dump work with DbStressCustomCompressionManager after this PR
Reviewed By: pdillinger
Differential Revision: D79461175
Pulled By: cbi42
fbshipit-source-id: c8c092b10b4fde3a295b00751057749e8f0cf095
Summary:
To better support future options, and changes, we need to convert the std::vector<ScanOptions> to something more malleable.
This diff introduces the MultiScanOptions structure and pipes it through the various points in the code in the Prepare path.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13837
Test Plan:
Ensure all associated tests pass
```
make check all
```
Reviewed By: cbi42
Differential Revision: D79655229
Pulled By: krhancoc
fbshipit-source-id: 3a90fb7420e9655021de85ed0158b866f8bfba05
Summary:
**Context/Summary:**
This update, which should have been part of a previous refactoring [PR](https://github.com/facebook/rocksdb/commit/d2ac955881e856fc69d5b15427d742fc635aaead), involves simple renaming for clarity and ensures output table properties are only set when compaction succeeds. Output properties are not meaningful if compaction fails, so this change prevents their population in such cases. Additionally, subsequent statistics updates already do not rely on output file table properties, maintaining correctness regardless of compaction success.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13851
Test Plan: Existing unit tests
Reviewed By: jaykorean
Differential Revision: D79862244
Pulled By: hx235
fbshipit-source-id: 1db16b8dc7b820fab3ec1d5c8a4b757466590e2c
Summary:
**Context/Summary:**
The `CompactionJob::Run()` method has grown too large and complex, making it difficult to implement moderate changes or reason about the code flow (e.g., determining where to save compaction progress for resuming). This PR refactors the method into smaller, more focused functions to improve readability and maintainability.
The refactoring consists mostly of cosmetic changes that extract logical sections into separate methods, with two notable functional improvements:
1. **Relocated output processing logic**: Moved code under `RemoveEmptyOutputs()` and `HasNewBlobFiles()` to where it's actually needed, rather than piggy-backing on the subcompaction state loop. While this introduces 2 additional loops over subcompactions, the performance impact should be negligible given the improved code clarity.
2. **Repositioned statistics updates**: Moved `UpdateCompactionJobInputStats()` and `UpdateCompactionJobOutputStats()` from the record verification section to the end `FinalizeCompactionRun()` methods. This change is safe since record verification is a read-only operation that doesn't modify any statistics.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13849
Test Plan: Existing unit tests
Reviewed By: jaykorean
Differential Revision: D79824429
Pulled By: hx235
fbshipit-source-id: 6b73136f32ecc6842a04a77502b7dbb0bbf507f7
Summary:
We temporarily disabled WAL when Remote Compaction is enabled in Stress Test (https://github.com/facebook/rocksdb/pull/13843). There are few others to incompatible features when WAL is disabled. Due to the sanitization order, WAL was disabled at the end of the sanitization and these incompatible features weren't set properly. Stress Test failed with an error like the following.
e.g. `reopen` stress test is not compatible with `disable_wal` - `Error: Db cannot reopen safely with disable_wal set!`
This PR changes the order of sanitization so that `disable_wal` is set earlier when `remote_compaction_worker_threads > 0`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13845
Test Plan:
```
python3 -u tools/db_crashtest.py blackbox --remote_compaction_worker_threads=8 --interval=5 --duration=6000 --continuous_verification_interval=10 --disable_wal=1 --use_txn=1 --txn_write_policy=2 --enable_pipelined_write=0 --checkpoint_one_in=0 --use_timed_put_one_in=0
```
Reviewed By: cbi42
Differential Revision: D79758670
Pulled By: jaykorean
fbshipit-source-id: aa6f4a74cc86c23f442928c301187b06e8137f53
Summary:
https://github.com/facebook/rocksdb/issues/13676 unfortunately treated some IOErrors as corruption, which is not appropriate when remote storage is involved. To help enforce this, our crash test injects errors that are expected to be propagated back to the user rather than causing some other failure.
Saw crash test failures like this:
```
TestMultiGetEntity (AttributeGroup) error: Corruption: Failed to get file size: Not implemented: GetFileSize Not Supported for file ...
```
So fixing this handling by not injecting a false Corruption failure and allowing smooth fallback from FSRandomAccessFile::GetFileSize to FileSystem::GetFileSize
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13842
Test Plan: unit test added
Reviewed By: xingbowang
Differential Revision: D79728861
Pulled By: pdillinger
fbshipit-source-id: 33f7dfc85d86d88cb4ab24a8defd26618c95c954
Summary:
To reduce the noise, disable the incompatible ones for now when `remote_compaction_worker_threads > 0`. We will investigate each, fix as needed and re-enable them as follow up.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13843
Test Plan:
```
python3 -u tools/db_crashtest.py blackbox --remote_compaction_worker_threads=8 --interval=5 --duration=6000 --continuous_verification_interval=10 --disable_wal=1 --use_txn=1 --enable_pipelined_write=0 --checkpoint_one_in=0 --use_timed_put_one_in=0
```
Reviewed By: cbi42
Differential Revision: D79735166
Pulled By: jaykorean
fbshipit-source-id: ae3be38a21073fd3282d6e8cd7d71f0363df3590
Summary:
**Summary:**
This test verifies that compaction respects the min_file_size parameter when triggered by deletions, preventing the compaction of files with deletions smaller than the threshold. The test logic includes two scenarios:
1. Verify that a large L0 file with deletions exceeding the minimum file size threshold triggers deletion-triggered compaction (DTC) and compacts to L1.
2. Verify that a small L0 file with deletions, but below the minimum file size threshold, does not trigger DTC and remains at L0.
Added the DeletionTriggeredCompactionWithMinFileSizeTestListener, which verifies that files selected for compaction based on deletion triggers meet the minimum file size threshold. The listener validates in OnCompactionBegin that all input files have sizes greater than or equal to the configured min_file_size parameter.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13825
Test Plan:
Tested this feature on our devserver using the following commands:
```
DEBUG_LEVEL=2 make -j64 db_compaction_test && KEEP_DB=1 ./db_compaction_test --gtest_filter="*DBCompactionTest.CompactionWith*"
```
Test output confirms the expected behavior:
```
2025/07/31-11:24:49.473181 1431671 [/compaction/compaction_job.cc:2291] [default] [JOB 6] Compacting 2@0 files to L1, score 0.04
2025/07/31-11:24:49.473240 1431671 [/compaction/compaction_job.cc:2297] [default]: Compaction start summary: Base version 6 Base level 0, inputs: [15(52KB) 9(103KB)]
2025/07/31-11:24:49.473304 1431671 EVENT_LOG_v1 {"time_micros": 1753986289473273, "job": 6, "event": "compaction_started", "cf_name": "default", "compaction_reason": "FilesMarkedForCompaction", "files_L0": [15, 9], "score": 0.04, "input_data_size": 159848, "oldest_snapshot_seqno": -1}
```
**Tasks:**
T228156639
Reviewed By: cbi42
Differential Revision: D79395851
Pulled By: nmk70
fbshipit-source-id: 4c2a80a95521b40543981dd81b347f3984cd2a8b
Summary:
Remote Compaction in the stress test previously failed with the following error, so we temporarily disabled it in PR https://github.com/facebook/rocksdb/issues/13815 :
```
reference std::vector<rocksdb::ThreadState *>::operator[](size_type) [_Tp = rocksdb::ThreadState *, _Alloc = std::allocator<rocksdb::ThreadState *>]: Assertion '__n < this->size()' failed.
```
The error was from accessing `remote_compaction_worker_threads[i]` when `i < remote_compaction_worker_threads.size()` which leads to an undefined behavior. This PR fixes the issue by properly setting the worker thread pointers in `remote_compaction_worker_threads`.
Note: We are still encountering errors when both BlobDB and Remote Compaction are enabled. It appears to be a race condition. For now, BlobDB is temporarily disabled if remote compaction is enabled. We will fix the race condition and re-enable BlobDB as a follow-up.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13835
Test Plan:
```
python3 -u tools/db_crashtest.py blackbox --remote_compaction_worker_threads=16 --interval=2 --duration=180
```
Reviewed By: hx235
Differential Revision: D79684447
Pulled By: jaykorean
fbshipit-source-id: 65f5809f651865c3df76c2cf3b9e7b8d654bb90a
Summary:
this option has the same functionality as DBOptions::allow_ingest_behind but allows the feature at per CF level. `DBOptions::allow_ingest_behind` is deprecated after this PR and users should use `cf_allow_ingest_behind` instead.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13810
Test Plan: updated some existing tests to use the new option.
Reviewed By: xingbowang
Differential Revision: D79191969
Pulled By: cbi42
fbshipit-source-id: 0da45f6be472ace6754ad15df93d45ac86313837
Summary:
**Context/Summary:**
The `RoundRobinSubcompactionsAgainstResources` test, specifically the `SubcompactionsUsingResources` case, is now disabled. This decision was made because the test's reliability depends on the absence of any concurrent compactions other than the round-robin compaction. Addressing this issue while maintaining the test's focus on resource reservation requires a deeper investigation, which is currently beyond my available bandwidth. Given the increased frequency of test failures, it has been temporarily disabled to prevent further disruptions.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13839
Test Plan: - Should be no test failure from RoundRobinSubcompactionsAgainstResources.SubcompactionsUsingResources anymore.
Reviewed By: cbi42
Differential Revision: D79686366
Pulled By: hx235
fbshipit-source-id: 3a226cfd2b67cabc6c585ea567e2b0c25aa5f345
Summary:
#Summary
Quick follow-up from https://github.com/facebook/rocksdb/pull/13816: `CompactFiles()` and `CompactRange()` in CompactionPickers do not run compaction as their names might suggest. What they actually do is create the Compaction object that will be passed to `CompactionJob` to run the compaction.
Renaming these two functions to better represent their purposes.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13831
Test Plan: No functional change. Existing CI should be sufficient.
Reviewed By: hx235
Differential Revision: D79660196
Pulled By: jaykorean
fbshipit-source-id: ca831dbef5120e7115b52fd07b0059ca16c8f1e8
Summary:
... by ensuring that files in dropped column family are not returned to the caller upon successful, offline MANIFEST iteration.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13832
Test Plan: `DBTest2, GetFileChecksumsFromCurrentManifest_CRC32`
Reviewed By: pdillinger
Differential Revision: D79607298
Pulled By: mszeszko-meta
fbshipit-source-id: e7948e086ba6e6fb953a3959fdcc81300613d73e
Summary:
Introduce `CompactionJob::VerifyOutputRecordCount()` and make it align with `VerifyInputRecordCount()`.
Functionality-wise, it should be the same except when `db_options_.compaction_verify_record_count` is false. RocksDB will only print WARN message upon verification failure and not return `Status::Corruption()`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13830
Test Plan:
Existing tests cover both
```
./compaction_service_test --gtest_filter="*CompactionServiceTest.VerifyInputRecordCount*"
```
```
./compaction_service_test --gtest_filter="*CompactionServiceTest.CorruptedOutput*"
```
Reviewed By: hx235
Differential Revision: D79584795
Pulled By: jaykorean
fbshipit-source-id: 5851328999005601b28504085b688b80880bca7c
Summary:
In anticipation of an enhancement related to parallel compression
* Rename confusing state variables `seperator_is_key_plus_seq_` -> `must_use_separator_with_seq_`
* Eliminate copy-paste code in `PartitionedIndexBuilder::AddIndexEntry`
* Optimize/simplify `PartitionedIndexBuilder::flush_policy_` by allowing a single policy to be re-targetted to different block builders. Added some additional internal APIs to make this work, and it only works because the FlushBlockBySizePolicy is otherwise stateless (after creation).
* Improve some comments, including another proposed optimization especially for the common case of no live snapshots affecting a large compaction
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13828
Test Plan:
existing tests are pretty exhaustive, especially with crash test
Planning to validate performance in combination with next change. (This change is saving some extra allocate/deallocate with partitioned index.)
Reviewed By: cbi42
Differential Revision: D79570576
Pulled By: pdillinger
fbshipit-source-id: f7a16f0e6e6ad2023a3d1a2ebaa3cc22aac717af
Summary:
This diff introduces the IntervalSet data structure, which will be used to help create sets of non overlapping sets of intervals for MultiScan scan options. Specifically, we add specializations for Slices to assist in this.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13787
Test Plan: Added test to catch various cases within adding intervals.
Reviewed By: anand1976
Differential Revision: D78624970
Pulled By: krhancoc
fbshipit-source-id: 9a3e4a28738ab8428788467540fc05ab5c1a1b67
Summary:
One of the parameters for constructing a Compaction object is `earliest_snapshot`, which is required for Standalone Range Deletion Optimization (introduced in [https://github.com/facebook/rocksdb/pull/13078](https://github.com/facebook/rocksdb/pull/13078)). Remote Compaction has been using the `CompactionPicker::CompactFiles()` API to create the Compaction object, but this API never sets the `earliest_snapshot` parameter. To address this, update `CompactionPicker::CompactFiles()` to optionally accept `earliest_snapshot` and pass it during the call in `DBImplSecondary::CompactWithoutInstallation()`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13816
Test Plan:
```
./compaction_service_test --gtest_filter="*CompactionServiceTest.StandaloneDeleteRangeTombstoneOptimization*"
```
\+ Tested in Meta's internal offload infra.
Reviewed By: hx235
Differential Revision: D79284769
Pulled By: jaykorean
fbshipit-source-id: 164834ef6972d5e0ddfc2970bb9234ef166d6e52
Summary:
Fix a bug in MultiScan where BlockBasedTableIterator should not return out-of-bound when the all blocks of the last scan are exhausted. This prevented LevelIterator from entering the next file so iterator is returning less keys than expected.
Also fixed stress testing to specify iterate_upper_bound correctly.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13822
Test Plan:
- the following fails quickly before this PR and finishes after this PR
```python3 tools/db_crashtest.py whitebox --iterpercent=60 --prefix_size=-1 --prefixpercent=0 --readpercent=0 --test_batches_snapshots=0 --use_multiscan=1 --seed=1 --fill_cache=1 --read_fault_one_in=0 --column_families=1 --allow_unprepared_value=0 --kill_random_test=88888```
- new unit test that fails before this PR
Reviewed By: krhancoc
Differential Revision: D79308957
Pulled By: cbi42
fbshipit-source-id: c9eafd1c8750b959b0185d7c63199b503493cbd2
Summary:
The main motivation for this change is to more flexibly and efficiently support compressing data without extra copies when we do not want to support saving compressed data that is LARGER than the uncompressed. We believe pretty strongly that for the various workloads served by RocksDB, it is well worth a single byte compression marker so that we have the flexibility to save compressed or uncompressed data when compression is attempted. Why? Compression algorithms can add tens of bytes in fixed overheads and percents of bytes in relative overheads. It is also an advantage for the reader when they can bypass decompression, including at least a buffer copy in most cases, after reading just one byte.
The block-based table format in RocksDB follows this model with a single-byte compression marker, and at least after https://github.com/facebook/rocksdb/pull/13797 so does CompressedSecondaryCache. (Notably, the blob file format DOES NOT. This is left to follow-up work.)
In particular, Compressor::CompressBlock now takes in a fixed size buffer for output rather than a `std::string*`. CompressBlock itself rejects the compression if the output would not fit in the provided buffer. This also works well with `max_compressed_bytes_per_kb` option to reject compression even sooner if its ratio is insufficient (implemented in this change). In the future we might use this functionality to reduce a buffer copy (in many cases) into the WritableFileWriter buffer of the block based table builder.
This is a large change because we needed to (or were compelled to)
* Update all the existing callers of CompressBlock, sometimes with substantial changes. This includes introducing GrowableBuffer to reuse between calls rather than std::string, which (at least in C++17) requires zeroing out data when allocating/growing a buffer.
* Re-implement built-in Compressors (V2; V1 is obsolete) to efficiently implement the new version of the API, no longer wrapping the `OLD_CompressData()` function. The new compressors appropriately leverage the CompressBlock virtual call required for the customization interface and no rely on `switch` on compression type for each block. The implementations are largely adaptations of the old implementations, except
* LZ4 and LZ4HC are notably upgraded to take advantage of WorkingArea (see performance tests). And for simplicity in the new implementation, we are dropping support for some super old versions of the library.
* Getting snappy to work with limited-size output buffer required using the Sink/Source interfaces, which appear to be well supported for a long time and efficient (see performance tests).
* Replace awkward old CompressionManager::GetDecompressorForCompressor with Compressor::GetOptimizedDecompressor (which is optional to implement)
* Small behavior change where we treat lack of support for compression closer to not configuring compression, such as incompatibility with block_align. This is motivated by giving CompressionManager the freedom of determining when compression can be excluded for an entire file despite the configured "compression" type, and thus only surfacing actual incompatibilities not hypothetical ones that might be irrelevant to the CompressionManager (or build configuration). Unit tests in `table_test` and `compact_files_test` required update.
* Some lingering clean up of CompressedSecondaryCache and a re-optimization made possible by compressing into an existing buffer.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13805
Test Plan:
for correctness, existing tests
## Performance Test
As I generally only modified compression paths, I'm using a db_bench write benchmark, with before & after configurations running at the same time. vc=1 means verify_compression=1
```
USE_CLANG=1 DEBUG_LEVEL=0 LIB_MODE=static make -j100 db_bench
SUFFIX=`tty | sed 's|/|_|g'`; for CT in zlib bzip2 none snappy zstd lz4 lz4hc none snappy zstd lz4 bzip2; do for VC in 0 1; do echo "$CT vc=$VC"; (for I in `seq 1 20`; do BIN=/dev/shm/dbbench${SUFFIX}.bin; rm -f $BIN; cp db_bench $BIN; $BIN -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 -compression_type=$CT -verify_compression=$VC 2>&1 | grep micros/op; done) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done; done
```
zlib vc=0 524198 -> 524904 (+0.1%)
zlib vc=1 430521 -> 430699 (+0.0%)
bzip2 vc=0 61841 -> 60835 (-1.6%)
bzip2 vc=1 49232 -> 48734 (-1.0%)
none vc=0 1802375 -> 1906227 (+5.8%)
none vc=1 1837181 -> 1950308 (+6.2%)
snappy vc=0 1783266 -> 1901461 (+6.6%)
snappy vc=1 1799703 -> 1879660 (+4.4%)
zstd vc=0 1216779 -> 1230507 (+1.1%)
zstd vc=1 996370 -> 1015415 (+1.9%)
lz4 vc=0 1801473 -> 1943095 (+7.9%)
lz4 vc=1 1799155 -> 1935242 (+7.6%)
lz4hc vc=0 349719 -> 1126909 (+222.2%)
lz4hc vc=1 348099 -> 1108933 (+218.6%)
(Repeating the most important ones)
none vc=0 1816878 -> 1952221 (+7.4%)
none vc=1 1813736 -> 1904622 (+5.0%)
snappy vc=0 1794816 -> 1875062 (+4.5%)
snappy vc=1 1789363 -> 1873771 (+4.7%)
zstd vc=0 1202592 -> 1225164 (+1.9%)
zstd vc=1 994322 -> 1016688 (+2.2%)
lz4 vc=0 1786959 -> 1971518 (+10.3%)
lz4 vc=1 1829483 -> 1935871 (+5.8%)
I confirmed manually that the new WorkingArea for LZ4HC makes the huge difference on that one, but not as much difference for LZ4, presumably because LZ4HC uses much larger buffers/structures/whatever for better compression ratios.
Reviewed By: hx235
Differential Revision: D79111736
Pulled By: pdillinger
fbshipit-source-id: 1ce1b14af9f15365f1b6da49906b5073a8cecc14
Summary:
Unit Test for a repro for the fix that was reported by https://github.com/facebook/rocksdb/pull/13743
There's potential dataloss when Remote Compaction entries are all removed due to various reasons (CompactionFilter, DeleteRange covering all keys of the SST file, etc)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13812
Test Plan:
```
./compaction_service_test --gtest_filter="*CompactionServiceTest.EmptyResult*"
```
Failed before merging https://github.com/facebook/rocksdb/pull/13743, now passing
Reviewed By: cbi42
Differential Revision: D79192829
Pulled By: jaykorean
fbshipit-source-id: e200300c4a7993de21c63cd92bda65b692921b89
Summary:
We were seeing some internal builds apparently failing the `-d /mnt/gvfs/third-party` check. Although third-party2 is likely a better check (see dependencies_platform010.sh), that would create a big headache with check_format_compatible.sh which has to work across codebase versions.
* Report a WARNING when we detect on a Meta machine but the `-d /mnt/gvfs/third-party` check fails
* Let USE_CLANG influence default compiler choice so that things might still work in that case (e.g. `USE_CLANG=1 make -j24 check`)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13820
Test Plan: manual, CI
Reviewed By: jaykorean
Differential Revision: D79277197
Pulled By: pdillinger
fbshipit-source-id: 19b2d45ed794f64bbf838f4414568d77ae9ca6f1
Summary:
Preserve tombstone when allow_ingest_behind` is enabled so that they can be applied to ingested files. This can be useful when users use ingest_behind to buffer updates where Deletion needs to be preserved. This fixes https://github.com/facebook/rocksdb/issues/13571.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13807
Test Plan: updated a unit test to verify that tombstones are not dropped during compaction.
Reviewed By: hx235
Differential Revision: D79016109
Pulled By: cbi42
fbshipit-source-id: c4d31ef32c88468ababcc1ea5af5db6de42a3b0d
Summary:
As title. We will re-enable it once fixed
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13815
Test Plan: N/A - Disabling the test.
Reviewed By: archang19
Differential Revision: D79172697
Pulled By: jaykorean
fbshipit-source-id: 936de3743816049cda811bde48b3b2207ed256ee
Summary:
**Issue**:
When running remote compaction, if all entries in the input files are expired, RocksDB incorrectly deletes an active file from the primary DB, leading to data loss and corruption.
**Root Cause**:
The current logic mistakenly mixed up the input and output file paths during the cleanup phase when no keys survive the compaction (all expired). This results in deleting the input files (which belong to the primary DB) instead of the output files (which belong to the SecondaryDB).
**Fix**:
Use `GetTableFileName` (virtual function) instead of `TableFileName`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13743
Reviewed By: hx235
Differential Revision: D79108650
Pulled By: jaykorean
fbshipit-source-id: 1c9ba971a0e9a62c15ebc014436cb8fc961af95c
Summary:
Building db_bench with clang and DEBUG_LEVEL=0 was failing with unused variable. This was not caught by CI so I have added this to the build-linux-clang-13-no_test_run job.
Also, while I was touching CI:
* Fold build-linux-release-rtti into build-linux-release by reducing the number of combinations tested between static/dynamic lib and rtti/not. I don't expect these to interact meaningfully with an extremely mature compiler.
* Combine build-linux-clang10-asan and build-linux-clang10-ubsan because clang is extremely reliable running both together
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13813
Test Plan: manual builds, CI
Reviewed By: krhancoc
Differential Revision: D79112643
Pulled By: pdillinger
fbshipit-source-id: 4ffc672718c05fa4597d637aacbc5a179ad8a0cf
Summary:
• Guard on __cpp_lib_atomic_shared_ptr to use std::atomic<std::shared_ptr<T>>::load()/store()
• Fallback to std::atomic_load_explicit()/store_explicit() under C++17
When attempting to build with CXX 20 using clang in a Linux environment, the build fails due to deprecation of atomic_load_explicit.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13744
Reviewed By: xingbowang
Differential Revision: D78997919
Pulled By: cbi42
fbshipit-source-id: f829c282cba878f072d4b0ad44192a87f73b8a90
Summary:
Simulate Remote Compaction in Stress Test by running a separate set of threads that runs remote compaction.
Queue and ResultMap for the remote compactions are stored in memory as part of the `SharedState`. They are shared across main worker threads and remote compaction worker threads.
`enable_remote_compaction` is replaced by `remote_compaction_worker_threads`.
If `remote_compaction_worker_threads` is set to 0, remote compaction is not enabled in Stress Test.
**To Follow up**
This PR covers happy path only. Failure injection in the remote worker thread will be added as a follow up.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13800
Test Plan:
```
./db_stress --remote_compaction_worker_threads=4 --flush_one_in=1000 --writepercent=40 --readpercent=40 --iterpercent=10 --prefixpercent=0 --delpercent=10 --destroy_db_initially=0 --clear_column_family_one_in=0 --reopen=0
```
```
python3 -u tools/db_crashtest.py blackbox --remote_compaction_worker_threads=8
```
Reviewed By: hx235
Differential Revision: D78862084
Pulled By: jaykorean
fbshipit-source-id: b262058c92d7fecc5e014cef5df9cca4a209921b
Summary:
To be compatible with some upcoming compression change/refactoring where we supply a fixed size buffer to CompressBlock, we need to support CompressedSecondaryCache storing uncompressed values when the compression ratio is not suitable. It seems crazy that CompressedSecondaryCache currently stores compressed values that are *larger* than the uncompressed value, and even explicitly exercises that case (almost exclusively) in the existing unit tests. But it's true.
This change fixes that with some other nearby refactoring/improvement:
* Update the in-memory representation of these cache entries to support uncompressed entries even when compression is enabled. AFAIK this also allows us to safely get rid of "don't support custom split/merge for the tiered case".
* Use more efficient in-memory representation for non-split entries
* For CompressionType and CacheTier, which are defined as single-byte data types, use a single byte instead of varint32. (I don't know if varint32 was an attempt at future-proofing for a memory-only schema or what.) Now using lossless_cast will raise a compiler error if either of these types is made too large for a single byte.
* Don't wrap entries in a CacheAllocationPtr object; it's not necessary. We can rely on the same allocator being provided at delete time.
* Restructure serialization/deserialization logic, hopefully simpler or easier to read/understand.
* Use a RelaxedAtomic for disable_cache_ to avoid race.
Suggested follow-up on CompressedSecondaryCache:
* Refine the exact strategy for rejecting compressions
* Still have a lot of buffer copies; try to reduce
* Revisit the split-merge logic and try to make it more efficient overall, more unified with non-split case
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13797
Test Plan:
Unit tests updated to use actually compressible strings in many places and more testing around non-compressible string.
## Performance Test
There was some pre-existing issue causing decompression failures in compressed secondary cache with cache_bench that is somehow fixed in this change. This decompression failures were present before the new compression API, but since then cause assertion failures rather than being quietly ignored. For the "before" test here, they are back to quietly ignored. And the cache_bench changes here were back-ported to the "before" configuration.
### No compressed secondary (setting expectations)
```
./cache_bench --cache_type=auto_hyper_clock_cache -cache_size=8000000000 -populate_cache
```
Max key : 3906250
Before:
Complete in 12.784 s; Rough parallel ops/sec = 2503123
Thread ops/sec = 160329; Lookup hit ratio: 0.686771
After:
Complete in 12.745 s; Rough parallel ops/sec = 2510717 (in the noise)
Thread ops/sec = 159498; Lookup hit ratio: 0.68686
### Compressed secondary, no split/merge
Same max key and approximate total memory size
```
/usr/bin/time ./cache_bench --cache_type=auto_hyper_clock_cache -cache_size=4000000000 -populate_cache -resident_ratio=0.125 -compressible_to_ratio=0.4 --secondary_cache_uri=compressed_secondary_cache://capacity=4000000000
```
Before:
Complete in 18.690 s; Rough parallel ops/sec = 1712144
Thread ops/sec = 108683; Lookup hit ratio: 0.776683
Latency: P50: 4205.19 P75: 15281.76 P99: 43810.98 P99.9: 71487.41 P99.99: 165453.32
max RSS (according to /usr/bin/time): 9341856
After:
Complete in 17.878 s; Rough parallel ops/sec = 1789951 (+4.5%)
Thread ops/sec = 114957; Lookup hit ratio: 0.792998 (+0.016)
Latency: P50: 4012.70 P75: 14477.63 P99: 40039.70 P99.9: 62521.04 P99.99: 167049.18
max RSS (according to /usr/bin/time): 9235688
The improved hit ratio is probably from fixing the failed decompressions (somehow). And my modifications could have improved CPU efficiency, or it could be the small penalty the benchmark naturally imposes on most misses (generate another value and insert it).
### Compressed secondary, with split/merge
```
/usr/bin/time ./cache_bench --cache_type=auto_hyper_clock_cache -cache_size=4000000000 -populate_cache -resident_ratio=0.125 -compressible_to_ratio=0.4 --secondary_cache_uri='compressed_secondary_cache://capacity=4000000000;enable_custom_split_merge=true'
```
Before:
Complete in 20.062 s; Rough parallel ops/sec = 1595075
Thread ops/sec = 101759; Lookup hit ratio: 0.787129
Latency: P50: 5338.53 P75: 16073.46 P99: 46752.65 P99.9: 73459.11 P99.99: 201318.75
max RSS (according to /usr/bin/time): 9049852
After:
Complete in 18.564 s; Rough parallel ops/sec = 1723771 (+8.1%)
Thread ops/sec = 110724; Lookup hit ratio: 0.813414 (+0.026)
Latency: P50: 5234.75 P75: 14590.43 P99: 41401.03 P99.9: 65606.50 P99.99: 157248.04
max RSS (according to /usr/bin/time): 8917592
Looks like an improvement
Reviewed By: anand1976
Differential Revision: D78842120
Pulled By: pdillinger
fbshipit-source-id: 5f754b160c37ebee789279178ebb5e862071bdb2
Summary:
Create a new API FileSystem::SyncFile for file sync, so that we could use file sync directly in places where we need to sync file content to file system without any modification. This is mostly used combined with link file. In some file system link file does not guarantee the file content is synced to file system.
https://github.com/facebook/rocksdb/issues/13741
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13762
Test Plan:
Unit test
T229418750
Reviewed By: pdillinger
Differential Revision: D78121137
Pulled By: xingbowang
fbshipit-source-id: 0ea8a5a3b486e0b61636700400613fed6bbd3faa
Summary:
The yield is actually of not much use because waitFor should already be doing that.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13796
Reviewed By: pdillinger
Differential Revision: D78823656
Pulled By: jainpr
fbshipit-source-id: 040eaf596938ce8db535bc810ad77a9e50b2d551
Summary:
This diff fixes up a miss in which the property_bag was not pushed down to the BlockBasedIterator.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13795
Reviewed By: anand1976
Differential Revision: D78762294
Pulled By: krhancoc
fbshipit-source-id: 8970b0a87e35d07d5a0dd16f360ec96859f66550
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13794
LLVM has a warning `-Wdeprecated-redundant-constexpr-static-def` which raises the warning:
> warning: out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated
Since we are now on C++20, we can remove the out-of-line definition of constexpr static data members. This diff does so.
- If you approve of this diff, please use the "Accept & Ship" button :-)
Reviewed By: meyering
Differential Revision: D78635037
fbshipit-source-id: a90c68469947705c65f36588b2d575237689dbe8
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13793
LLVM has a warning `-Wdeprecated-redundant-constexpr-static-def` which raises the warning:
> warning: out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated
Since we are now on C++20, we can remove the out-of-line definition of constexpr static data members. This diff does so.
- If you approve of this diff, please use the "Accept & Ship" button :-)
Reviewed By: meyering
Differential Revision: D78635005
fbshipit-source-id: bd7cbfff0580b9579e78237ec4371615d3609536
Summary:
This patch reverted "NewRandomRWFile" back to "ReopenWritableFile" in external sst file ingestion job when file is linked instead of copied. The reason is that some of the file systems do not support "NewRandomRWFile". A long term fix is being worked in progress.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13791
Test Plan: Unit test
Reviewed By: pdillinger
Differential Revision: D78697825
Pulled By: xingbowang
fbshipit-source-id: d3651223ab1f2369aac34b772bba8049c6c2c628
Summary:
This diff introduces the ScanOption Pruning, previously the intent was to do prefetching for each sub-iterator of the level iterator, however since BlockBasedIterator does not prefetch asynchronously, this optimization does not make sense just yet.
For now we will prune the ScanOptions to the overlapping ranges and make sure they are properly piped to the underlying layers (during Prepare, and Seek).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13780
Reviewed By: cbi42
Differential Revision: D78436869
Pulled By: krhancoc
fbshipit-source-id: 681fe7f7f88b04b5c2d60cb3a5de01e03f6f8431
Summary:
So that we can use --command=recompress with a custom CompressionManager. (It's not required for reading files using a custom CompressionManager because those can already use ObjectLibrary for dependency injection.)
Suggested follow-up:
* These tests should not be using C arrays, snprintf, manual delete, etc. except for thin compatibility with argc/argv.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13783
Test Plan: unit test added, some manual testing
Reviewed By: archang19
Differential Revision: D78574434
Pulled By: pdillinger
fbshipit-source-id: 609e6c6439090e6b7e9b63fbd4c2d3f04b104fcf
Summary:
initial support for Prepare() to optimize the performance of MultiScan when using block-based tables. In Prepare(), we do the following:
1. Load all data blocks that will be read in multiscan to block cache
2. Pin the data blocks during the scan
3. if I/O is needed, coalesce I/Os when they are adjacent.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13778
Test Plan:
Added a new unit test.
Benchmark:
1. Set up the DB, I use FIFO here so that files will be in L0 and iterator will use BlockBasedTableIterator directly instead of LevelIterator, where Prepare() call is not implemented yet.
```
./db_bench --benchmarks="fillseq,compact" --disable_wal=1 --threads=1 --num_levels=1 --compaction_style=2 --fifo_compaction_max_table_files_size_mb=1000 --write_buffer_size=268435456
```
2. Multi-scan: based on https://github.com/facebook/rocksdb/issues/13765
```
./db_bench --db="/tmp/rocksdbtest-543376/dbbench" --use_existing_db=1 --benchmarks=multiscan --disable_auto_compactions=1 --seek_nexts=100 --threads=32 --duration=10 --statistics=1
multiscan_stride = 100
multiscan_size = 10
seek_nexts = 100
Main:
multiscan : 449.386 micros/op 70562 ops/sec 10.359 seconds 730968 operations; (multscans:22999)
multiscan : 453.606 micros/op 69433 ops/sec 10.369 seconds 719968 operations; (multscans:22999)
rocksdb.non.last.level.read.bytes COUNT : 47763519421
rocksdb.non.last.level.read.count COUNT : 21573878
Branch:
multiscan : 332.670 micros/op 94698 ops/sec 10.285 seconds 973968 operations; (multscans:29999)
rocksdb.non.last.level.read.bytes COUNT : 111791308336
rocksdb.non.last.level.read.count COUNT : 1062942
With direct-IO:
./db_bench --db="/tmp/rocksdbtest-543376/dbbench" --use_existing_db=1 --benchmarks=multiscan --disable_auto_compactions=1 --seek_nexts=100 --threads=32 --duration=10 --statistics=1 --use_direct_reads=1
Main:
multiscan : 586.045 micros/op 53825 ops/sec 10.366 seconds 557968 operations; (multscans:14999)
rocksdb.non.last.level.read.bytes COUNT : 69107458693
rocksdb.non.last.level.read.count COUNT : 6724651
Branch:
multiscan : 386.679 micros/op 81282 ops/sec 10.359 seconds 841968 operations; (multscans:25999)
rocksdb.non.last.level.read.bytes COUNT : 96605800558
rocksdb.non.last.level.read.count COUNT : 918973
```
Throughput is 36% higher with non-direct IO and 50% higher with direct IO. The improvement is likely from doing less number of I/Os due to I/O coalescing during Prepare(), as shown in `rocksdb.non.last.level.read.count`. The total bytes read is more with this PR for the same reason.
3. Regular iterator:
```
./db_bench --use_existing_db=1 --db="/tmp/rocksdbtest-543376/dbbench" --benchmarks=seekrandom --disable_auto_compactions=1 --seek_nexts=10 --threads=32 --duration=10
Main:
seekrandom : 13.014 micros/op 2456735 ops/sec 10.014 seconds 24602968 operations; 2717.8 MB/s (773999 of 773999 found)
Branch:
seekrandom : 13.048 micros/op 2450554 ops/sec 10.013 seconds 24537968 operations; 2710.9 MB/s (772999 of 772999 found)
```
The result fluctuates but without noticeable regression.
Reviewed By: anand1976
Differential Revision: D78440807
Pulled By: cbi42
fbshipit-source-id: 80ac6fd222696fa65ac0b4b5441748be5ee0b979
Summary:
When max_table_files_size was accidentally configured with 0 value, engine could crash on divide by 0 operation. Although RocksDB do configuration validation during bootstrap, it typically does not do this for runtime dynamic parameter validation. Therefore, there is a chance where max_table_files_size could be set to 0. This PR only focuses on fixing a code path where max_table_files_size ack as divisor.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13767
Test Plan: Unit test.
Reviewed By: cbi42
Differential Revision: D78420516
Pulled By: xingbowang
fbshipit-source-id: 6fdcc85b28a2c6319066665262b981e513719703
Summary:
This diff introduces the ability to override behavior of exits, allow for users to catch exits in a try catch for example as opposed to fully exiting the process.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13772
Reviewed By: hx235
Differential Revision: D78244499
Pulled By: krhancoc
fbshipit-source-id: b403327ed5b494a22b6beeaad4083945a1def0c7
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13728
The Prepare interface allows the user defined index iterator to prefetch index entries, as well as take custom scan termination criteria specified in the property_bag into account.
Reviewed By: pdillinger
Differential Revision: D76165546
fbshipit-source-id: 83d628598924aa7a60dff7ed62a16ae575b2c8ec
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13727
Add UserDefinedIndexReader and UserDefinedIndexIterator. The BlockBasedTable reads the user defined index meta block during open, verifies the checksum, pins in cache or heap depending on configuration, and allocates a UserDefinedIndexReader object with the contents. Similar to the builder, an IndexReader wrapper is allocated. The wrapper forwards the calls to the native reader and/or user defined index reader as appropriate.
A new option, table_index_name, in ReadOptions specifies the index to use when creating a new Iterator.
Reviewed By: pdillinger
Differential Revision: D76165694
fbshipit-source-id: c30bde4c5ce91ea3dc9ad302e73fe4963c1ed457
Summary:
In SstFileWriter::Finish, the call to DeleteFile to delete the output file in case of an error may fail. The current behavior is to ignore the error. In stress tests, there may be expected failures due to error injection. Not acting on the return status will cause the ASSERT_STATUS_CHECKED test to fail, so silence it.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13776
Reviewed By: mszeszko-meta
Differential Revision: D78307124
Pulled By: anand1976
fbshipit-source-id: d27d9397c15cac5cb33b27094c9123a3fde7fa24
Summary:
* A number of comments clarifying contracts, etc.
* Make ReleaseWorkingArea public instead of protected because there are some limited cases where a wrapper implementation might want to call it directly
* Check non-empty dictionary precondition on MaybeCloneForDict
* Expand testing of wrapped WorkingAreas
* Random documentation improvement in block_builder.cc
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13775
Test Plan: existing and expanded tests and assertions
Reviewed By: hx235
Differential Revision: D78304550
Pulled By: pdillinger
fbshipit-source-id: e5f064e8405a5a49be123ee13145cb3626bbbfbf
Summary:
The Stress test was broken due to a change in switching from ReopenWritableFile to FSRandomRWFile for sync linked file in external Sst ingestion job. The Stress test is using FaultInjectionFs, which tracks the opening of ReopenWritableFile properly, but does not track FSRandomRWFile properly. This change fixes the tracking of FSRandomRWFile in FaultInjectionFs.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13771
Test Plan: unit test, stress test
Reviewed By: mszeszko-meta
Differential Revision: D78282719
Pulled By: xingbowang
fbshipit-source-id: f8f2ed8a5b28a76836f75effbdfa2c3bb172dc51
Summary:
in log_reader.cc.
* `for (;;)` (with no matching break inside) should be more structurally recognizable to compilers as unreachable after compared to `while (true)` which compilers can treat as conditional for warning/error purposes because `true` might have come from a macro, etc.
* Comment the `break` statements to indicate they are for the `switch` (not the `for`)
* No code or annotation is apparently needed for the unreachable end of the non-void function, so just a comment
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13764
Test Plan: CI
Reviewed By: archang19
Differential Revision: D78135493
Pulled By: pdillinger
fbshipit-source-id: e313435a846a6e15346acf40404f755be98ab09a
Summary:
This diff reverts D77424529
Unland reason: This diff broke our Windows 2022 build for Open Source CI (T230460952).
Depends on D77424529
Reviewed By: pdillinger
Differential Revision: D78107313
fbshipit-source-id: 6177448e1015c239abcebb0e68470dfd841b6fa0
Summary:
I was going to use this in some code I was working on but ended up not needing it. But it's useful nonetheless and I'm using it in a few places to replace reinterpret_cast.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13648
Test Plan: existing tests, manually see compilation fail when pointed-to types are not same size integral types
Reviewed By: cbi42
Differential Revision: D75576195
Pulled By: pdillinger
fbshipit-source-id: e10c7a4959340f6f2b536de8088072a90e871fcf
Summary:
LLVM has a warning `-Wdeprecated-redundant-constexpr-static-def` which raises the warning:
> warning: out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated
Since we are now on C++20, we can remove the out-of-line definition of constexpr static data members. This diff does so.
- If you approve of this diff, please use the "Accept & Ship" button :-)
Differential Revision: D77423205
fbshipit-source-id: 4ee4a390431a5d25e7733311f3fa40395dfd4bc0
Summary:
LLVM has a warning `-Wunreachable-code-return` which identifies return statements that cannot be reached.
In innocuous situations such statements are often present:
* to satisfy a compiler warning that existed before `[[noreturn]]` was introduced. Now that we have `[[noreturn]]`, this use is not necessary.
* to specify a return type. But there are clearer ways to do this.
* in place of the more legible `__builtin_unreachable()` (which will soon become `std::unreachable()`). In this case, we should use the more legible alternative.
* because the programmer was afraid of the function unexpectedly returning. But we check for this condition with `-Wreturn-type`.
In dangerous situations such statements can obscure the intended execution of the program or even hide an erroneous early return.
In this diff, we remove one or more unreachable returns.
- If you approve of this diff, please use the "Accept & Ship" button :-)
Differential Revision: D77424529
fbshipit-source-id: fe41b5a640264d0a299d5ad330c645f94b147323
Summary:
Add file size validation in ReadFooterFromFile function.
Deprecate skip_checking_sst_file_sizes_on_db_open option.
This change is used to address this issue
https://github.com/facebook/rocksdb/issues/13619
It supports file size validation in ReadFooterFromFile. In favor of this
change, CheckConsistency function and
skip_checking_sst_file_sizes_on_db_open flag are deprecated.
The CheckConsistency function checks each file size matches what was
recorded in manifest during DB open. Meantime, ReadFooterFromFile was
called for each file in LoadTables function. Since ReadFooterFromFile
always validates file size, the CheckConsistency is redundant.
In addtion, CheckConsistency is executed in a single thread. This could
slow down DB open when a network file system is used. Therefore, the
flag skip_checking_sst_file_sizes_on_db_open was added to skip this
check. After this change, ReadFooterFromFile was executed in parallel
through multiple threads. Therefore, the concern of DB open slowness is
eliminated, and the flag could be deprecated.
When paranoid check flag is set to true, corrupted file will fail to open the DB.
When paranoid check flag is set to false, DB will still be able to open, the
healthy ones can be accessed, while the corrupted ones not.
There is 2 slight concerns of this change.
*If max_open_files is set with smaller value, engine will not open all
the files during DB open. This means if there is a corruption on file
size, it will not be detected during DB open, but rather at a later
time. Since the default is -1, which means open all the files, and it is
rarely overridden and a lot of new features rely on it to be -1, the
risk is very low.
*If FIFO compaction is used, engine could fail to open DB unnecessarily
on the corrupted files that would never be used again. However, this is
a very rare case as well. The error could still be ignored by setting
paranoid_checks operationally. The risk is very low.
To remain backward compatibility. The public facing flag was kept and
marked as no-op internally. Another change is required to fully remove
the flag.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13676
Test Plan:
make check
A new unit test was added to validate file size check API works as
expected.
Reviewed By: pdillinger
Differential Revision: D76168033
Pulled By: xingbowang
fbshipit-source-id: 8ceacf39bcfe02ff7aa289868c341366ee9f3a8e
Summary:
`SstFileManager` is supposed to be thread-safe for all of its public methods, but `SetStatisticsPtr` leads to a race condition because the access to `stat_` is not synchronized. We don't use `stat_` internally so we can get rid of it.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13757
Test Plan: Existing unit tests.
Reviewed By: mszeszko-meta
Differential Revision: D77962592
Pulled By: archang19
fbshipit-source-id: e8e56194dda034935ddef44e479243770a73d065
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13726
Add UserDefinedIndexFactory and UserDefinedIndexBuilder interfaces to allow users to plugin custom index implementation into block based table. The factory is specified in BlockBasedTableOptions. If non-null, BlockBasedTableBuilder allocates a wrapper index builder encapsulating the native index and the custom index. The custom index is exposed to BlockBasedTableBuilder as a meta_block of type kUserDefinedIndex. This block type is not compressed.
The IndexBuilder OnKeyAdded interface is enhanced to accept the value in addition to the key. Only full values are supported, and parallel compression is not supported since we cannot obtain the value when calling OnKeyAdded.
Reviewed By: pdillinger
Differential Revision: D76165614
fbshipit-source-id: dfad9cbd6d0359987b7f4abe64cae58c472836f9
Summary:
While a transaction is waiting on a lock, we can use GetWaitingTxns() to determine the transactionID of the blocking transaction and the contended key. However, this gets cleared when the lock times out, so if a client has widespread timeout errors, you need to catch a transaction 'in the act' before they actually hit the timeout in order to understand the contention pattern. This diff adds a new TransactionOptions variable enable_get_waiting_txn_after_timeout, which persists the lock contention information after timeout so it can be accessed by the client after they have received the timeout error.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13754
Test Plan:
- updated TransactionTest.WaitingTxn to test the changed behavior
- ran production shadow tests on traffic with frequent timeouts
Reviewed By: cbi42
Differential Revision: D77703598
Pulled By: akabcenell
fbshipit-source-id: b4448ca1b6a3694d51bfe1ce801b09eb376ff3e9
Summary:
See `Makefile` for actual changes:
* ZLIB remains the same
* BZIP2 remains the same
* SNAPPY is a minor update
* LZ4 is a significant update with multithreaded/multicore compression https://github.com/lz4/lz4/releases/tag/v1.10.0
* ZSTD is a significant update RocksDB is called out as benefiting in particular from the performance improvements herein https://github.com/facebook/zstd/releases/tag/v1.5.7
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13609
Reviewed By: archang19
Differential Revision: D77877295
Pulled By: mszeszko-meta
fbshipit-source-id: bf9a257e8f68dec3d02743b339aa2df65df4ab2c
Summary:
Was reading sec_cache_res_ratio_ outside of mutex and using the result for computation that needs to be synchronized
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13747
Test Plan: existing tests. Has been showing up in crash test, and there's no interesting concurrency here that would warrant a regression test based on sync points.
Reviewed By: cbi42
Differential Revision: D77607660
Pulled By: pdillinger
fbshipit-source-id: 12a71936b3558c7528d229a11c7d2e43982ad06b
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13752
... to github repo. This include changes from D77323287, D77473923 and the release note change in patch release: D77611483.
Reviewed By: archang19
Differential Revision: D77670619
fbshipit-source-id: 37d877f3317c71de190128fa4da6b18f6dfcf3c5
Summary:
address an existing limitation on compaction triggering mechanism that relies on events like flush/compaction/SetOptions. This is important for periodic compactions where files can become eligible without any of these events. The periodic task now runs every 12 hours and check CFs that enables `periodic_compaction_second` (TBD if we want to expand to all CFs) for eligible compactions.
Some of the periodic tasks probably don't need to run immediately after Register(). I'm keeping the existing behavior for now for patch release and to makes tests happy.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13736
Test Plan:
- new unit test that fails before this change.
- ran crash test for hours with the periodic task running every 5 seconds: `python3 ./tools/db_crashtest.py blackbox --test_batches_snapshot=0 --periodic_compaction_seconds=10`
Reviewed By: pdillinger
Differential Revision: D77460715
Pulled By: cbi42
fbshipit-source-id: 00f61502753185e76830c9ed44c5ccc4f4f16bfa
Summary:
CostAwareCompressor simply ignores the preferred compression type as compression manager setting takes precedence over the compression type setting. Thus, I am removing the assert statement as it itself is unnecessary for this case.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13739
Test Plan:
Run nightly build test
```bash
make V=1 J=4 -j4 check
```
Reviewed By: hx235
Differential Revision: D77470932
Pulled By: shubhajeet
fbshipit-source-id: ebb69367d2ffb9bd72432fd04b0cd12ce2d6240a
Summary:
improve assertions, one apparently a previous typo in https://github.com/facebook/rocksdb/issues/13606 and one a suspected possible area of logic error
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13737
Test Plan: watch crash test
Reviewed By: anand1976
Differential Revision: D77453102
Pulled By: pdillinger
fbshipit-source-id: d4196910a9e8d59ef814130a52ff4ebf188a976d
Summary:
I am seeing crashes during backups. The stack trace points back to `WritableFileWriter` creation inside `BackupEngineImpl::CopyOrCreateFile`. I believe the issue is that we are calling `writable_file_->GetRequiredBufferAlignment()` with a `null` `writable_file`.
https://github.com/facebook/rocksdb/blob/v10.2.1/utilities/backup/backup_engine.cc#L2396-L2397https://github.com/facebook/rocksdb/blob/v10.2.1/file/writable_file_writer.h#L210
Here's how I think the flow is:
```cpp
io_s = dst_env->GetFileSystem()->NewWritableFile(dst, dst_file_options,
&dst_file, nullptr);
// say there was some issue and dst_file is nullptr
// evaluates to false
if (io_s.ok() && !src.empty()) {
// we don't go down this branch
auto src_file_options = FileOptions(src_env_options);
src_file_options.temperature = *src_temperature;
io_s = src_env->GetFileSystem()->NewSequentialFile(src, src_file_options,
&src_file, nullptr);
}
// say this evaluates to true
if (io_s.IsPathNotFound() && *src_temperature != Temperature::kUnknown) {
// Retry without temperature hint in case the FileSystem is strict with
// non-kUnknown temperature option
io_s = src_env->GetFileSystem()->NewSequentialFile(
src, FileOptions(src_env_options), &src_file, nullptr);
}
// this is now from the NewSequentialFile call, not NewWritableFile
if (!io_s.ok()) {
return io_s;
}
// dst_file is still nullptr
```
If the first `NewWritableFile` fails and `IsPathNotFound
Tests: existing unit tests
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13734
Reviewed By: pdillinger
Differential Revision: D77390694
Pulled By: archang19
fbshipit-source-id: 865a3a646079ae2349a3b6f25e53ae85df8e4985
Summary:
`data_` and `size_` fields are duplicated in `Block` class, as `contents_` field already have a `data` member variable, which contains `data` and `size` already. This reduces memory consumption by 16 bytes per block.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13733
Test Plan: Unit test
Reviewed By: pdillinger
Differential Revision: D77389791
Pulled By: xingbowang
fbshipit-source-id: 50a56bc5fae494ed5bc39bdfde7303ca06ce87c6
Summary:
Corrected misspelling of "Compression". Changed "Compresssion" to "Compression".
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13735
Test Plan:
All the test case for compression is still working properly.
```bash
./compression_test
```
Reviewed By: hx235
Differential Revision: D77390273
Pulled By: shubhajeet
fbshipit-source-id: f5310e393e23f5d6c8310154cb929db4b6c60a77
Summary:
Respect the scan upper bound/limit, if specified, in `MultiScan`. This applies to block based table and other native RocksDB SSTs. In order to properly support it, the `MultiScan` object caches the `ReadOptions` passed by the user and sets the `iterate_upper_bound` as appropriate. We optimize for the case of either all scans specifying the upper bound, or none of them. In case of mixed scans, we reallocate the DB iterator anytime `ReadOptions` has to be updated.
Tests:
New unit tests in `db_iterator_test`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13723
Reviewed By: cbi42
Differential Revision: D77385049
Pulled By: anand1976
fbshipit-source-id: 9c02d125770cbedbe6e8c10767ba537e7f7540e1
Summary:
Large txn commit optimization requires all updates are added to a transaction's WriteBatchWithIndex. However, some usage of transactions may add updates directly to the WBWI's underlying write batch. In these cases, we should not attempt to ingest the WBWI since it will drop these updates. This PR adds sanity checking for this.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13722
Test Plan:
- added checks in unit test and stress test
- manually check LOG files for the new unit test
Reviewed By: hx235
Differential Revision: D77247688
Pulled By: cbi42
fbshipit-source-id: 3d1c0c6e64d6d7dfd5578bc4d77abe44cac1e419
Summary:
The nightly build was failing because we were using the AutoSkipCompressionManager with kNoCompression. The test cases should not be running with NoCompression.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13716
Test Plan:
Run the test code being run on the nightly build.
```bash
make V=1 J=4 -j4 check
```
Reviewed By: hx235
Differential Revision: D77042874
Pulled By: shubhajeet
fbshipit-source-id: 821643b30ca53b1855fc24e3bc0a319e4fec2876
Summary:
Port changes made directly in fbcode in order to facilitate the 10.4 release.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13714
Test Plan: Existing tests
Reviewed By: mszeszko-meta
Differential Revision: D77038668
Pulled By: anand1976
fbshipit-source-id: 6b9b16d62bccf75923b525c1c24597a59920a948
Summary:
* Make new format_version=7 a supported setting.
* Fix a bug in compressed_secondary_cache.cc that is newly exercised by custom compression types and showing up in crash test with tiered secondary cache
* Small change to handling of disabled compression in fv=7: use empty compression manager compatibility name.
* Get rid of GetDefaultBuiltinCompressionManager() in public API because it could cause unexpected+unsafe schema change on a user's CompressionManager if built upon the default built-in manager and we add a new built-in schema. Now must be referenced by explicit compression schema version in the public API. (That notion was already exposed in compressed secondary cache API, for better or worse.)
* Improve some error messages for compression misconfiguration
* Improve testing with ObjectLibrary and CompressionManagers
* Improve testing of compression_name table property in BlockBasedTableTest.BlockBasedTableProperties2
* Improve some comments
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13713
Test Plan: existing and updated tests. Notably, the crash test has already been running with (unpublished) format_version=7
Reviewed By: mszeszko-meta, hx235
Differential Revision: D77035482
Pulled By: pdillinger
fbshipit-source-id: 95278de8734a79706a22361bff2184b1edb230ca
Summary:
Auto skip compression manager code is currently running only in context of test / db bench. Disable failing test to unblock monthly minor release.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13715
Test Plan: Disable test.
Reviewed By: hx235
Differential Revision: D77039218
Pulled By: mszeszko-meta
fbshipit-source-id: f9eeec8d5ca4efeaf1f490c5f091b3aff7861a4a
Summary:
Some pieces of follow-up to https://github.com/facebook/rocksdb/issues/13659.
_Recommend hiding whitespace for review_
* Add support for instantiating CompressionManagers through CreateFromString/ObjectLibrary.
* Pull CompressorCustomAlg and DecompressorCustomAlg out of db_test2, refactor/improvement them a bit, and put them in testutil.h for sharing with db_stress. Switched it from being built on snappy to being built on lz4 so that it can properly test dictionary compression.
* Add a custom compression manager for db_stress that uses these, and add to crash test. This depends on the ObjectLibrary stuff because some invocations of db_stress will not be configured with the custom compression manager but will need to access it to read some existing SST files.
* Remove some pieces where the concern of setting compression=kZSTD for compatibility purposes had leaked into configuring some tests and compression managers. After https://github.com/facebook/rocksdb/issues/13659 this compatibility concern is contained in the SST building code.
* Fix BuiltinDecompressorV2SnappyOnly hiding the (ignored) compression dictionary. SST read logic expects the serialized dictionary to be returned by the decompressor even if it's effectively ignored. Updated DBBlockCacheTest.CacheCompressionDict to cover this case.
For follow-up:
* Combine custom compression and mixed compression types in a file (not clean/easy without duplicating or majorly refactoring the mixed/random compressor)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13710
Test Plan: unit tests updated
Reviewed By: hx235
Differential Revision: D76928974
Pulled By: pdillinger
fbshipit-source-id: 772cf9cb048d737699b0e2887c624fb64a68aa8c
Summary:
add the `min_file_size` parameter to CompactOnDeletionCollector. A file must be at least this size for it to qualify for DTC. This is useful when a user wants to specific a min file size requirement that is larger than the size constraint imposed by the sliding window's `deletion_trigger` requirement.
Added some comment explaining that the file_size provided to table property collector only includes data blocks and may not be up-to-date. This PR also updates DTC to consider SingleDelete and DeletionWithTimestamp as tombstones.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13707
Test Plan:
- new unit test for when min_file_size is specified.
- existing unit test for when min_file_size is not specified.
Reviewed By: hx235, pdillinger
Differential Revision: D76837231
Pulled By: cbi42
fbshipit-source-id: 0782144e75aef9961bf03da2a2c4b3c613ce5db3
Summary:
Some usage of vector memtable is bottlenecked in the memtable insertion path when using multiple writers. This PR adds support for concurrent writes for the vector memtable. The updates from each concurrent writer are buffered in a thread local vector. When a writer is done, MemTable::BatchPostProcess() is called to flush the thread local updates to the main vector. TSAN test and function comment suggest that ApproximateMemoryUsage() needs to be thread-safe, so its implementation is updated to provide thread-safe access.
Together with unordered_write, benchmark shows much improved insertion throughput.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13675
Test Plan:
- new unit test
- enabled some coverage of vector memtable in stress test
- Performance benchmark: benchmarked memtable insertion performance with by running fillrandom 20 times
- Compare branch and main performance with one thread and write batch size 100:
- main: 4896888.950 ops/sec
- branch: 4923366.350 ops/sec
- Benchmark this branch by configuring different threads, allow_concurrent_memtable_write, and unordered_write. Performance ratio is computed as current ops/sec divided by ops/sec at 1 thread with the same options.
allow_concurrent | unordered_write | Threads | ops/sec | Performance Ratio
-- | -- | -- | -- | --
0 | 0 | 1 | 4923367 | 1.0
0 | 0 | 2 | 5215640 | 1.1
0 | 0 | 4 | 5588510 | 1.1
0 | 0 | 8 | 6077525 | 1.2
1 | 0 | 1 | 4919060 | 1.0
1 | 0 | 2 | 5821922 | 1.2
1 | 0 | 4 | 7850395 | 1.6
1 | 0 | 8 | 10516600 | 2.1
1 | 1 | 1 | 5050004 | 1.0
1 | 1 | 2 | 8489834 | 1.7
1 | 1 | 4 | 14439513 | 2.9
1 | 1 | 8 | 21538098 | 4.3
```
mkdir -p /tmp/bench_$1
export TEST_TMPDIR=/tmp/bench_$1
memtablerep_value=${6:-vector}
(for I in $(seq 1 $2)
do
/data/users/changyubi/vscode-root/rocksdb/$1 --benchmarks=fillrandom --seed=1722808058 --write_buffer_size=67108864 --min_write_buffer_number_to_merge=1000 --max_write_buffer_number=1000 --enable_pipelined_write=0 --memtablerep=$memtablerep_value --disable_auto_compactions=1 --disable_wal=1 --avoid_flush_during_shutdown=1 --allow_concurrent_memtable_write=${5:-0} --unordered_write=$4 --batch_size=1 --threads=$3 2>&1 | grep "fillrandom"
done;) | awk '{ t += $5; c++; print } END { printf ("%9.3f\n", 1.0 * t / c) }';
```
Reviewed By: pdillinger
Differential Revision: D76641755
Pulled By: cbi42
fbshipit-source-id: c107ba42749855ad4fd1f52491eb93900757542e
Summary:
* Improve debugability with better error messages (including the returned status, not just log messages)
* Tolerate user providing file checksums recognized by the factory but not the same function as currently, generally provided by the factory. This makes it practical to transition from one type of checksum to another without major hiccups in ingestion workflows.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13708
Test Plan: updated unit test, manually inspect LOG file from the unit test
Reviewed By: cbi42
Differential Revision: D76837804
Pulled By: pdillinger
fbshipit-source-id: 45b744829b3a125e9d0ee6874bd37ce534c2e13c
Summary:
**Summary:**
We need to move the Predictor to WorkingArea so that it is local to each thread and thus is thread safe.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13706
Test Plan: It should pass the test case written in ./compression_test.
Reviewed By: pdillinger
Differential Revision: D76836846
Pulled By: shubhajeet
fbshipit-source-id: 0d0170baf65f4bb95ba107fec77151e66b8a4449
Summary:
**Summary**:
Clang tidy was throwing error that the gtest assertion EXPECT_EQ was being carried out variables that was not initialized.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13703
Test Plan:
Ran the clang-tidy operations to make sure the same error does not appear.
```bash
CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze
```
Reviewed By: hx235, pdillinger
Differential Revision: D76777988
Pulled By: shubhajeet
fbshipit-source-id: b9bfe26a2264d4c21224ab53a0b0307596d7f49d
Summary:
The Object registry requires object to be allocated as std::unique_ptr. Hence we provide a new API for external table plugins to allocate and return a unique_ptr ExternalTableFactory wrapper.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13694
Reviewed By: jaykorean
Differential Revision: D76767974
Pulled By: anand1976
fbshipit-source-id: ac59c523a11679ca7c9f0b280325c7873c6b4c07
Summary:
This change builds on https://github.com/facebook/rocksdb/issues/13540 and https://github.com/facebook/rocksdb/issues/13626 in allowing a CompressionManager / Compressor / Decompressor to use a custom compression algorithm, with a distinct CompressionType. For background, review the API comments on CompressionManager and its CompatibilityName() function.
Highlights:
* Reserve and name 127 new CompressionTypes that can be used for custom compression algorithms / schemas. In many or most cases I expect the enumerators such as `kCustomCompression8F` to be used in user code rather than casting between integers and CompressionTypes, as I expect the supported custom compression algorithms to be identifiable / enumerable at compile time.
* When using these custom compression types, a CompressionManager must use a CompatibilityName() other than the built-in one AND new format_version=7 (see below).
* When building new SST files, track the full set of CompressionTypes actually used (usually just one aside from kNoCompression), using our efficient bitset SmallEnumSet, which supports fast iteration over the bits set to 1. Ideally, to support mixed or non-mixed compression algorithms in a file as efficiently as possible, we would know the set of CompressionTypes as SST file open time.
* New schema for `TableProperties::compression_name` in format_version=7 to represent the CompressionManager's CompatibilityName(), the set of CompressionTypes used, and potentially more in the future, while keeping the data relatively human-readable.
* It would be possible to do this without a new format_version, but then the only way to ensure incompatible versions fail is with an unsupported CompressionType tag, not with a compression_name property. Therefore, (a) I prefer not to put something misleading in the `compression_name` property (a built-in compression name) when there is nuance because of a CompressionManager, and (b) I prefer better, more consistent error messages that refer to either format_version or the CompressionManager's CompatibilityName(), rather than an unrecognized custom CompressionType value (which could have come from various CompressionManagers).
* The current configured CompressionManager is passed in to TableReaders so that it (or one it knows about) can be used if it matches the CompatibilityName() used for compression in the SST file. Until the connection with ObjectRegistry is implemented, the only way to read files generated with a particular CompressionManager using custom compression algorithms is to configure it (or a known relative; see FindCompatibleCompressionManager()) in the ColumnFamilyOptions.
* Optimized snappy compression with BuiltinDecompressorV2SnappyOnly, to offset some small added overheads with the new tracking. This is essentially an early part of the planned refactoring that will get rid of the old internal compression APIs.
* Another small optimization in eliminating an unnecessary key copy in flush (builder.cc).
* Fix some handling of named CompressionManagers in CompressionManager::CreateFromString() (problem seen in https://github.com/facebook/rocksdb/issues/13647)
Smaller things:
* Adds Name() and GetId() functions to Compressor for debugging/logging purposes. (Compressor and Decompressor are not expected to be Customizable because they are only instantiated by a CompressionManager.)
* When using an explicit compression_manager, the GetId() of the CompressionManager and the Compressor used to build the file are stored as bonus entries in the compression_options table property. This table property is not parsed anywhere, so it is currently for human reading, but still could be parsed with the new underscore-prefixed bonus entries. IMHO, this is preferable to additional table properties, which would increase memory fragmentation in the TableProperties objects and likely take slightly more CPU on SST open and slightly more storage.
* ReleaseWorkingArea() function from protected to public to make wrappers work, because of a quirk in C++ (vs. Java) in which you cannot access protected members of another instance of the same class (sigh)
* Added `CompressionManager:: SupportsCompressionType()` for early options sanity checking.
Follow-up before release:
* Make format_version=7 official / supported
* Stress test coverage
Sooner than later:
* Update tests for RoundRobinManager and SimpleMixedCompressionManager to take advantage of e.g. set of compression types in compression_name property
* ObjectRegistry stuff
* Refactor away old internal compression APIs
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13659
Test Plan:
Basic unit test added.
## Performance
### SST write performance
```
SUFFIX=`tty | sed 's|/|_|g'`; for ARGS in "-compression_type=none" "-compression_type=snappy" "-compression_type=zstd" "-compression_type=snappy -verify_compression=1" "-compression_type=zstd -verify_compression=1" "-compression_type=zstd -compression_max_dict_bytes=8180"; do echo $ARGS; (for I in `seq 1 20`; do BIN=/dev/shm/dbbench${SUFFIX}.bin; rm -f $BIN; cp db_bench $BIN; $BIN -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -format_version=7 $ARGS 2>&1 | grep micros/op; done) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done
```
Ops/sec, Before -> After, both fv=6:
-compression_type=none
1894386 -> 1858403 (-2.0%)
-compression_type=snappy
1859131 -> 1807469 (-2.8%)
-compression_type=zstd
1191428 -> 1214374 (+1.9%)
-compression_type=snappy -verify_compression=1
1861819 -> 1858342 (+0.2%)
-compression_type=zstd -verify_compression=1
979435 -> 995870 (+1.6%)
-compression_type=zstd -compression_max_dict_bytes=8180
905349 -> 940563 (+3.9%)
Ops/sec, Before fv=6 -> After fv=7:
-compression_type=none
1879365 -> 1836159 (-2.3%)
-compression_type=snappy
1865460 -> 1830916 (-1.9%)
-compression_type=zstd
1191428 -> 1210260 (+1.6%)
-compression_type=snappy -verify_compression=1
1866756 -> 1818989 (-2.6%)
-compression_type=zstd -verify_compression=1
982640 -> 997129 (+1.5%)
-compression_type=zstd -compression_max_dict_bytes=8180
912608 -> 937248 (+2.7%)
### SST read performance
Create DBs
```
for COMP in none snappy zstd; do echo $ARGS; ./db_bench -db=/dev/shm/dbbench-7-$COMP --benchmarks=fillseq,flush -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -compression_type=$COMP -format_version=7; done
```
And test
```
for COMP in none
snappy zstd none; do echo $COMP; (for I in `seq 1 8`; do ./db_bench -readonly -db=/dev/shm/dbbench
-7-$COMP --benchmarks=readrandom -num=10000000 -duration=20 -threads=8 2>&1 | grep micros/op; done
) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done
```
Ops/sec, Before -> After (both fv=6)
none
1491732 -> 1500209 (+0.6%)
snappy
1157216 -> 1169202 (+1.0%)
zstd
695414 -> 703719 (+1.2%)
none (again)
1491787 -> 1528789 (+2.4%)
Ops/sec, Before fv=6 -> After fv=7:
none
1492278 -> 1508668 (+1.1%)
snappy
1140769 -> 1152613 (+1.0%)
zstd
696437 -> 696511 (+0.0%)
none (again)
1500585 -> 1512037 (+0.7%)
Overall, I think we can take the read CPU improvement in exchange for the hit (in some cases) on background write CPU
Reviewed By: hx235
Differential Revision: D76520739
Pulled By: pdillinger
fbshipit-source-id: e73bd72502ff85c8779cba313f26f7d1fd50be3a
Summary:
Add an atomic bool to CompactionOptions to cancel an ongoing CompactFiles() operation, in the same fashion we do for CompactRange().
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13687
Test Plan: ./db_test2 --gtest_filter=DBTest2.TestCancelCompactFiles
Reviewed By: jaykorean
Differential Revision: D76538529
Pulled By: virajthakur
fbshipit-source-id: 77db5b4fb4cbd5280584834df28e51a72b084dab
Summary:
**Summary:**
This pull request configures RocksDB to optionally utilize this customized compressor (RandomCompressor) in the db stress test. It randomly selects the compression algorithm among the blocks.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13691
Test Plan: Testing was performed by verifying the stdout output from both RandomCompressor.
Reviewed By: hx235
Differential Revision: D76624220
Pulled By: shubhajeet
fbshipit-source-id: d9c458eeee930b25e8a87a77dc29f0647836310e
Summary:
**Context:**
RocksDB's current compression approach rejects blocks if the compressed size exceeds a predefined threshold. To optimize performance, we aim to develop an algorithm that dynamically stops and resumes block compression attempts based on past rejection data.
**Summary:**
The goal of this milestone is to design, implement, and evaluate an algorithm that intelligently skips and resumes block compression attempts in RocksDB. The algorithm tracks whether randomly selected blocks was rejected, compressed or bypassed and using data of window size to determine the current rejection rate. The calculate rejection rate is used to decide whether to pause and resume compression attempts. We measure the effectiveness of skipping and resuming compression using DB bench and identify any concerning regressions in correctness and performance.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13674
Test Plan:
1. Test case to see if it can automatically start compression on compression friendly workload and see if it can automatically stop compression on non-compression friendly workload (auto_skip_compresor_test.cc)
3. Regression analysis to prove that no significant performance attempt
```bash
SUFFIX=`tty | sed 's|/|_|g'`; for ARGS in "-compression_parallel_threads=1 -compression_type=zstd -compression_manager=none" "-compression_parallel_threads=4 -compression_type=zstd -compression_manager=none" "-compression_parallel_threads=1 -compression_type=zstd -compression_manager=autoskip" "-compression_parallel_threads=4 -compression_type=zstd -compression_manager=autoskip" ; do echo $ARGS; (for I in `seq 1 20`; do ./db_bench -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 $ARGS 2>&1 | grep micros/op; done) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done
```
Measurement experiment | throughput (% change from main branch) |
|---------------|--------------------------------|
compression manager = none (main branch) | 1106890.35 ops/s
compression manager = none (auto skip) | 1097574.55 ops/s (-0.84%)
compression manager = auto skip (auto skip branch) | 1133432.9 ops/s (+2.4%)
Reviewed By: hx235
Differential Revision: D76220795
Pulled By: shubhajeet
fbshipit-source-id: 0f46ab34da1b451f8907306afba221503e6e22a5
Summary:
Since `request_id` is a raw pointer to a string, copying `IODebugContext` becomes a little bit more complicated. We need to ensure that `request_id` gets its memory freed, but by we don't have ownership of the memory by default. The `request_id` inside `IODebugContext` is meant to point to a string allocated outside of the RocksDB read request. To get around this issue without refactoring `request_id`'s type entirely, we can store a private member variable and have `request_id` point to it, so the memory deallocation happens automatically for us.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13690
Test Plan:
I updated the `RequestIdPlumbingTest` unit test from https://github.com/facebook/rocksdb/issues/13616
```
./db_test --gtest_filter=DBTest.RequestIdPlumbingTest
```
Reviewed By: anand1976
Differential Revision: D76613051
Pulled By: archang19
fbshipit-source-id: 053a5b9c4cde20606ec7854ada29904bdf11d40c
Summary:
This field will be used internally to feed Warm Storage cost information back through the Sally IO stack. This is needed for cost accounting / reporting.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13666
Test Plan: I made the additional changes needed to set/record the new cost info field, and confirmed that this information could be fed through.
Reviewed By: anand1976
Differential Revision: D76070434
Pulled By: archang19
fbshipit-source-id: 2fab975f14fd8f7c20b5d0d85c31686ccf682068
Summary:
**Context:**
RocksDB currently selects files for long-running compaction outputs to the bottommost level, preventing these selected files files from being selected, but does not execute the compaction immediately like other compactions. Instead, this compaction is forwarded to another Env::Priority::bottom thread pool, where it waits (potentially for a long time) until its thread is ready to execute. This extended L0 lock time in universal compaction caused our users write stall and read performance regression.
**Summary:**
This PR is to eliminate L0 lock time during bottom priority compaction waiting to execute by the following
- Create and forward an intended compaction only consists of last input file (or sorted run if non-L0) instead of all the input files. This eliminate the locking for non-bottommost level input files while waiting for bottom priority thread is up to run.
- Re-pick compaction that outputs to max output level when bottom priority thread is up to run
- Refactor universal compaction picking logic to make it cleaner and easier to force picking compaction with max output level when bottom priority thread is up to run
- Guard feature behind a temporary option as requested
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13633
Test Plan:
- New unit test to cover the case that's not covered by existing tests - bottom priority thread re-picks compaction ends up picking nothing due to LSM shape changes
- Adapted existing unit tests to verify various bottom priority compaction behavior with this new option
- Stress test `python3 tools/db_crashtest.py --simple blackbox --compaction_style=1 --target_file_size_base=1000 --write_buffer_size=1000 --compact_range_one_in=10000 --compact_files_one_in=10000 `
Reviewed By: cbi42
Differential Revision: D76005505
Pulled By: hx235
fbshipit-source-id: 9688f22d4a84f619452820f12f15b765c17301fd
Summary:
**Context/Summary:**
Since LDB manifest dump including printing the LSM shape does not open the db and manifest itself does not have info about Options.num_levels, LDB tool (the only caller of `DumpManifestHandler` has to set a "hopefully-large-enough" level number (i.e,64) to print info of every level for the LSM shape in the manifest. This can mislead whoever that's reading the manifest to believe there are actually 64 levels configured with the CF. This PR clarifies that.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13681
Test Plan:
Manual test
`./ldb manifest_dump --hex --verbose --json --path=<some manifest file path>`
```
--------------- Column family "9" (ID 9) --------------
log number: 115873
comparator: leveldb.BytewiseComparator
--- level 0 --- version# 19 ---
--- level 1 --- version# 19 --- compact_cursor: '000000000000000900000000000000DF78787878787878' seq:3418519, type:2 ---
--- level 2 --- version# 19 --- compact_cursor: '000000000000000900000000000000D8' seq:3446619, type:2 ---
--- level 3 --- version# 19 --- compact_cursor: '000000000000000900000000000000DF78787878787878' seq:3418519, type:2 ---
--- level 4 --- version# 19 --- compact_cursor: '000000000000000900000000000000DF78787878787878' seq:3418519, type:2 ---
--- level 5 --- version# 19 --- compact_cursor: '0000000000000009000000000000012B0000000000000065' seq:3447830, type:2 ---
--- level 6 --- version# 19 ---
115931:376281[0 .. 0]['0000000000000000' seq:0, type:1 .. '00000000000003E7000000000000012B00000000000002B1' seq:0, type:1]
--- level 7 --- version# 19 ---
--- level 8 --- version# 19 ---
--- level 9 --- version# 19 ---
--- level 10 --- version# 19 ---
--- level 11 --- version# 19 ---
....
--- level 61 --- version# 19 ---
--- level 62 --- version# 19 ---
--- level 63 --- version# 19 ---
By default, manifest file dump prints LSM trees as if 64 levels were configured, which is not necessarily true for the column family (CF) this manifest is associated with. Please consult other DB files, such as the OPTIONS file, to confirm.
```
Reviewed By: jaykorean
Differential Revision: D76391064
Pulled By: hx235
fbshipit-source-id: 3e1c58e0eeb39a5fa020040201b07b181f8977a6
Summary:
In the function WritableFileWriter::WriteBufferedWithChecksum, since the alignment parameter passed to RequestToken defaults to 4096, when data_size is less than 4096, subtracting a larger value from data_size (which is of type unsigned long) will cause an underflow. This results in an infinite loop. Since WriteBuffered does not require alignment, it is sufficient to pass alignment == 0.
issue:https://github.com/facebook/rocksdb/issues/13640
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13641
Reviewed By: jaykorean
Differential Revision: D76341973
Pulled By: hx235
fbshipit-source-id: 8912f2b6598bb5a48b6b813c53146d9ecfd31d30
Summary:
**Context/Summary:**
`RoundRobinSubcompactionsAgainstResources.SubcompactionsUsingResources` has been flaky and difficult to de-flake. One of the reasons is the complicated usage of sync points and unnecessarily strict verification.
- The sync points don't seem necessary to verify the number of extra reserved threads for sub-compactions so are removed.
- The full reservation after compaction to verify extra reserved threads were release is indirect and hard to get right. So it's replaced with simpler sync-point callback check.
- Since we already have tests (see https://github.com/facebook/rocksdb/blob/7d80ea45442e84c25669db61cb7376ba0cd10ba5/env/env_test.cc#L841 and )for testing pure functionality of reserve/release does reserve/release the threads, verifying the relevant code paths are called should be enough to verify extra reserved threads were released after compaction
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13672
Test Plan: Monitor future flakiness.
Reviewed By: cbi42
Differential Revision: D76108242
Pulled By: hx235
fbshipit-source-id: 30113f16455688f113f296bda0098a66a7a198a3
Summary:
**Summary**
This pull request fixes the issue of having a single file simple_mixed_compressor.h containing both implementation and declaration. To improve code organization and follow best practices, I have separated the implementation into a new file simple_mixed_compressor.cc and updated the original file to only contain the necessary declarations.
**Testing**
Testing was performed by verifying the stdout output from both RoundRobinCompressor and BuiltInCompressorV2.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13665
Reviewed By: pdillinger
Differential Revision: D76060831
Pulled By: shubhajeet
fbshipit-source-id: c034868be51ea7b89c1a8dd12082b0159f49f588
Summary:
Iterator seek returns "SeekAndValidate() not implemented" error if the disallow_memtable_writes CF option is set along with paranoid_memory_checks. The fix is to sanitize the paranoid_memory_checks option to false, which should be safe since the memtable is guaranteed to be empty.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13663
Test Plan: Update unit test in db_basic_test.cc
Reviewed By: pdillinger
Differential Revision: D75973515
Pulled By: anand1976
fbshipit-source-id: 3f381f19dcda72e3b78ee375f755fb4809c6b99c
Summary:
Some tests were failing due to apparent missing include of iomanip. I suspect this was from a gtest upgrade, because in open source, the include iomanip comes from gtest.h. To ensure we maintain compatibility with older gtest as well as the newer one, I pulled the include iomanip out of the in-repo gtest.h. Note that other places in gtest code only instantiate floating-point related templates with `float` and `double` types.
Also, to avoid `make format` being insanely slow on gtest.h, I've excluded third-party from the formatting check.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13661
Test Plan: make check, internal CI, manually ensure formatting check works outside of third-party/
Reviewed By: jaykorean
Differential Revision: D75963897
Pulled By: pdillinger
fbshipit-source-id: ed5737dd456e74068185f1ac5d57046d7509df7a
Summary:
**Summary**
This pull request introduces a mixed compressor, RoundRobinManager and RoundRobinCompressor, which selects algorithms in a loop. This implementation replaces the current hacky approach to round-robin compression in BuiltInCompressorV2. Additionally, it configures RocksDB to optionally utilize this customized compressor in the db stress test.
**Testing**
Testing was performed by verifying the stdout output from both RoundRobinCompressor and BuiltInCompressorV2.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13647
Reviewed By: pdillinger
Differential Revision: D75921997
Pulled By: shubhajeet
fbshipit-source-id: 8f42ac46f08ba982b2cd70241bd7dc13ff5a1225
Summary:
... to support SmallEnumSet over CompressionType with allowed custom compression types using most of the available byte. This is accomplished using an std::array<uint64_t> in place of just uint64_t. Also adds an std::bitset-like count() operation.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13657
Test Plan: unit tests included
Reviewed By: hx235
Differential Revision: D75827601
Pulled By: pdillinger
fbshipit-source-id: 519ae97ac671fd9885d6485976abbd969d1392d3
Summary:
**Summary**
This pull request aims to populate num_input_files and total_input_bytes in the CompactionJobStats object, which is accessible through EventListener::OnCompactionBegin(DB*, const CompactionJobInfo&). This change will enable RocksDB users to access accurate compaction input information.
**Context/Goals**
Provide accurate compaction input statistics to RocksDB users
Populate num_input_files and total_input_bytes in CompactionJobStats
Ensure correct population of these fields before EventListener::OnCompactionBegin() is called
**Test Plan**
Added test code to capture num_input_file and total_num_bytes when EventHandler is triggered
Asserted that these values are populated correctly
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13637
Reviewed By: cbi42
Differential Revision: D75690774
Pulled By: shubhajeet
fbshipit-source-id: 8236546f8ce7743f46048b302b376b7ef6429887
Summary:
Somehow this was previously not being tested in our Windows CI jobs so was accidentally broken in https://github.com/facebook/rocksdb/pull/13540 This fix will need to be backported to 10.3.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13649
Test Plan: CI
Reviewed By: hx235
Differential Revision: D75655418
Pulled By: pdillinger
fbshipit-source-id: a56bb213270904a1b7a13b905c2cc1919116df1c
Summary:
Also revamping test
GeneralTableTest::ApproximateOffsetOfCompressed so that it's not sensitive to adding new metadata to SST files
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13646
Test Plan: manually inspect new table property, which is not parsed anywhere, just for information to human reader
Reviewed By: hx235
Differential Revision: D75561241
Pulled By: pdillinger
fbshipit-source-id: c076c01a8b540bc4cb771964d48fa919c4c48ae4
Summary:
to make it easier to use 0 for disabled. And deprecate the use of txn db option `txn_commit_bypass_memtable_threshold`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13636
Test Plan: updated unit tests.
Reviewed By: jowlyzhang
Differential Revision: D75262136
Pulled By: cbi42
fbshipit-source-id: 9040e5a9c918c1d0906a2db4600cc012d2436b22
Summary:
Larger key/values can cause memtable write to take longer time. Add new option `TransactionOptions::large_txn_commit_optimize_byte_threshold` that enables the optimization by transaction write batch size.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13634
Test Plan:
- new unit test
- added option to stress test and ran stress test for some time: `python3 ./tools/db_crashtest.py --txn blackbox --txn_write_policy=0 --commit_bypass_memtable_one_in=50 --test_batches_snapshots=0`
Reviewed By: jowlyzhang
Differential Revision: D75248126
Pulled By: cbi42
fbshipit-source-id: 9522db93457729ba60e4176f7d47f7c2c7778567
Summary:
This exposes CompressionManager and related classes to the public API and adds `ColumnFamilyOptions::compression_manager` for tying a custom compression strategy to a column family. At the moment, this does not support custom/pluggable compression algorithms, just custom strategies around the built-in algorithms, e.g. which compression to use when and where.
A large part of the change is moving code from internal compression.h to a new public header advanced_compression.h, with some minor changes:
* `Decompressor::ExtractUncompressedSize()` is out-of-lined
* CompressionManager inherits Customizable and some related changes to members of CompressionManager are made. (Core functionality of CompressionManager is unchanged.)
This depends on a smart pointer I'm calling `ManagedPtr` which I'm adding to data_structure.h.
Additionally, advanced_compression.h gets CompressorWrapper and CompressionManagerWrapper as building blocks for overriding aspects of compression strategy while leveraging existing compression algorithms / schemas.
Some pieces needed to support the `compression_manager` option and rudimentary Customizable implementation are included. More work will be needed to make this general and well-behaved (see e.g. https://github.com/facebook/rocksdb/issues/8641; I still hit inscrutible problems every time I touch Customizable).
I'll add a release note for the experimental feature once pluggable compression algorithms and more of the Customizable things are working.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13626
Test Plan:
Added a unit test demonstrating how a custom compressor can "bypass" or "reject" compressions.
Expected next follow-up (probably someone else): use a custom CompressionManager/Compressor to replace the internal hack for testing mixed compressions.
Reviewed By: hx235
Differential Revision: D75028850
Pulled By: pdillinger
fbshipit-source-id: 8565bb8ba4b5fa923b1e29e76b4f7bb4faa42381
Summary:
Some specific old versions around RocksDB 2.5 would compress the metaindex and properties blocks. This hasn't been done since, probably because it interferes with the properties block indicating how to set up for decompression (so the reader can read those blocks before doing any decompression).
To fix backward compatibility, we establish a decompressor early if format_version indicates the file could come from a sufficiently old version.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13628
Test Plan: local and CI runs of tools/check_format_compatible.sh. (I don't believe we need special code to set up a unit test for this case.)
Reviewed By: jowlyzhang
Differential Revision: D75107623
Pulled By: pdillinger
fbshipit-source-id: 97132b8c5e0602e8e27254a11386d866b23cb4f5
Summary:
This PR introduces a new CF option, `memtable_avg_op_scan_flush_trigger`, to support triggering a memtable flush when an iterator skips too many invisible keys from the active memtable. This is a follow up to https://github.com/facebook/rocksdb/pull/13523#discussion_r2038261975, which introduced the option `memtable_op_scan_flush_trigger` for a single expensive iterator step. This PR focus on an expensive stretch of iterator steps, between Seeks and until iterator destruction. To avoid triggering a memtable flush for a stretch that is too small, this option only takes effect when the total number of entries skipped from the active memtable in a stretch of iterator steps exceeds `memtable_op_scan_flush_trigger`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13593
Test Plan:
* New unit tests covering the new option
* Add the option to the crash test.
Reviewed By: hx235
Differential Revision: D74434263
Pulled By: cbi42
fbshipit-source-id: 64f1101efb79c7498e2038eff630713ead8f6f41
Summary:
Instead of using FileSystem::GetFileSize() for each CompactionOutputFile, use the file size that is being tracked internally as part of the output file's metadata. FileSize is now part of `CompactionServiceOutputFile` and serialized in the `CompactionServiceResult`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13620
Test Plan:
Tested with logging Meta's internal offload Infra
```
./compaction_job_test
```
Reviewed By: jowlyzhang
Differential Revision: D75006961
Pulled By: jaykorean
fbshipit-source-id: 008f9dc22bd672746ac180380ada4188713a6b85
Summary:
Usual release steps
* Release notes from 10.3 branch
* Update version.h
* Add 10.3.fb to check_format_compatible.sh
* Update folly commit hash. Added a few hacks to fix build errors.
Bonus:
* Add a check_format_compatible.sh sanity check to the per-PR GitHub actions jobs. It should be quick enough and catch typos in release diffs as we've seen in the past.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13622
Test Plan: CI
Reviewed By: jowlyzhang
Differential Revision: D74943843
Pulled By: pdillinger
fbshipit-source-id: 4ff1db9a635e111f8830cadff2d3ee51cf2de512
Summary:
showing up in the crash test after https://github.com/facebook/rocksdb/issues/13540
* For an assertion `dict_samples.sample_data.size() <= opts_.max_dict_bytes` we needed to ensure that `zstd_max_train_bytes` only takes effect with kZSTD compression.
* For an assertion with `r->table_options.verify_compression == (verify_decomp != nullptr)` we needed to ensure that `data_block_verify_decompressor` is set even when dictionary compression is attempted but not used.
* Noticed along the way: finish an optimization in `CompressAndVerifyBlock` that was incomplete.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13621
Test Plan:
Both failures were reproducible with hard-coding of some crash test params, and now not getting a failure.
```
--compression_type=zstd --compression_max_dict_bytes=16384 --compression_zstd_max_train_bytes=65536 --compression_max_dict_buffer_bytes=131071 --compression_use_zstd_dict_trainer=1
```
Write performance test like in https://github.com/facebook/rocksdb/issues/13540 shows essentially no change, maybe slightly faster (+0.4%) with verify_compression.
Reviewed By: virajthakur
Differential Revision: D74939103
Pulled By: pdillinger
fbshipit-source-id: 8bac8891bc08e1356eff52cc524e5bb409b0f86f
Summary:
[internal use] Allow the application to pass a request_id per read request to RocksDB and pass it down to the FileSystem (via IODebugContext)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13616
Test Plan:
./db_test --gtest_filter=DBTest.RequestIdPlumbingTest
Validates that RocksDB Api calls with request_id set result in request_id being passed to the filesystem through IODebugContext
Reviewed By: pdillinger
Differential Revision: D74912824
Pulled By: virajthakur
fbshipit-source-id: 4f15fef3ff7b5d700563f993f9b211c991020fb6
Summary:
Remove the dependency on `allow_db_generated_files` option in `IngestExternalFile` to be set for ingesting external tables. The files are created by SstFileWriter, and we should be able to ingest them. We could make it work by having the external table implementation provide the version and global sequence number related properties, but its safer to have RocksDB generate the table properties block and store it as is in the file.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13608
Test Plan: Add unit test to test basic ingestion and ingestion with atomic_replace_range
Reviewed By: pdillinger
Differential Revision: D74830707
Pulled By: anand1976
fbshipit-source-id: 4a9bea4a4f38f7c24c584262095c5c98cd771ddc
Summary:
Add stats to monitor the large transaction optimization. A stat is added for how many times wbwi ingestion is used. A histogram is added to track transaction size. We could also just track write batch size for all writes but I don't want to add the overhead to all writes yet.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13611
Test Plan:
ran `python3 ./tools/db_crashtest.py --txn blackbox --txn_write_policy=0 --commit_bypass_memtable_one_in=50 --test_batches_snapshots=0 --stats_dump_period_sec=2 --dump_malloc_stats=0 --statistics=1` and manually check LOG files
```
rocksdb.number.wbwi.ingest COUNT : 57
...
rocksdb.num.op.per.transaction P50 : 1.000000 P95 : 1.000000 P99 : 1.000000 P100 : 1.000000 COUNT : 2265 SUM : 2265
```
Reviewed By: jowlyzhang
Differential Revision: D74829087
Pulled By: cbi42
fbshipit-source-id: 5a9c3ab2d4cb6071cedfc47201ce2cf65a77d3c6
Summary:
Similar to https://github.com/facebook/rocksdb/pull/13555, add more info, ColumnFamily Id and name, to `CompactionServiceJobInfo`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13615
Test Plan:
Updated Unit Test
```
./compaction_service_test
```
Reviewed By: archang19
Differential Revision: D74845661
Pulled By: jaykorean
fbshipit-source-id: e2fc61006092b9febec1c6637b92cb00fb6cb73e
Summary:
Adds new classes etc. in internal compression.h that are intended to become public APIs for supporting custom/pluggable compression. Some steps remain to allow for pluggable compression and to remove a lot of legacy code (e.g. now called `OLD_CompressData` and `OLD_UncompressData`), but this change refactors the key integration points of SST building and reading and compressed secondary cache over to the new APIs.
Compared with the proposed https://github.com/facebook/rocksdb/issues/7650, this fixes a number of issues including
* Making a clean divide between public and internal APIs (currently just indicated with comments)
* Enough generality that built-in compressions generally fit into the framework rather than needing special treatment
* Avoid exposing obnoxious idioms like `compress_format_version` to the user.
* Enough generality that a compressor mixing algorithms/strategies from other compressors is pretty well supported without an extra schema layer
* Explicit thread-safety contracts (carefully considered)
* Contract details around schema compatibility and extension with code changes (more detail in next PR)
* Customizable "working areas" (e.g. for ZSTD "context")
* Decompression into an arbitrary memory location (rather than involving the decompressor in memory allocation; should facilitate reducing number of objects in block cache)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13540
Test Plan:
This is currently an internal refactor. More testing will come when the new API is migrated to the public API. A test in db_block_cache_test is updated to meaningfully cover a case (cache warming compression dictionary block) that was previously only covered in the crash test.
SST write performance test, like https://github.com/facebook/rocksdb/issues/13583. Compile with CLANG, run before & after simultaneously:
```
SUFFIX=`tty | sed 's|/|_|g'`; for ARGS in "-compression_parallel_threads=1 -compression_type=none" "-compression_parallel_threads=1 -compression_type=snappy" "-compression_parallel_threads=1 -compression_type=zstd" "-compression_parallel_threads=1 -compression_type=zstd -verify_compression=1" "-compression_parallel_threads=1 -compression_type=zstd -compression_max_dict_bytes=8180" "-compression_parallel_threads=4 -compression_type=snappy"; do echo $ARGS; (for I in `seq 1 20`; do ./db_bench -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 $ARGS 2>&1 | grep micros/op; done) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done
```
Before (this PR and with https://github.com/facebook/rocksdb/issues/13583 reverted):
-compression_parallel_threads=1 -compression_type=none
1908372
-compression_parallel_threads=1 -compression_type=snappy
1926093
-compression_parallel_threads=1 -compression_type=zstd
1208259
-compression_parallel_threads=1 -compression_type=zstd -verify_compression=1
997583
-compression_parallel_threads=1 -compression_type=zstd -compression_max_dict_bytes=8180
934246
-compression_parallel_threads=4 -compression_type=snappy
1644849
After:
-compression_parallel_threads=1 -compression_type=none
1956054 (+2.5%)
-compression_parallel_threads=1 -compression_type=snappy
1911433 (-0.8%)
-compression_parallel_threads=1 -compression_type=zstd
1205668 (-0.3%)
-compression_parallel_threads=1 -compression_type=zstd -verify_compression=1
999263 (+0.2%)
-compression_parallel_threads=1 -compression_type=zstd -compression_max_dict_bytes=8180
934322 (+0.0%)
-compression_parallel_threads=4 -compression_type=snappy
1642519 (-0.2%)
Pretty neutral change(s) overall.
SST read performance test (related to https://github.com/facebook/rocksdb/issues/13583). Set up:
```
for COMP in none snappy zstd; do echo $ARGS; ./db_bench -db=/dev/shm/dbbench-$COMP --benchmarks=fillseq,flush -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 -compression_type=$COMP; done
```
Test (compile with CLANG, run before & after simultaneously):
```
for COMP in none snappy zstd; do echo $COMP; (for I in `seq 1 5`; do ./db_bench -readonly -db=/dev/shm/dbbench-$COMP --benchmarks=readrandom -num=10000000 -duration=20 -threads=8 2>&1 | grep micros/op; done) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done
```
Before (this PR and with https://github.com/facebook/rocksdb/issues/13583 reverted):
none
1495646
snappy
1172443
zstd
706036
zstd (after constructing with -compression_max_dict_bytes=8180)
656182
After:
none
1494981 (-0.0%)
snappy
1171846 (-0.1%)
zstd
696363 (-1.4%)
zstd (after constructing with -compression_max_dict_bytes=8180)
667585 (+1.7%)
Pretty neutral.
Reviewed By: hx235
Differential Revision: D74626863
Pulled By: pdillinger
fbshipit-source-id: dc8ff3178da9b4eaa7c16aa1bb910c872afaf14a
Summary:
Addresses https://github.com/facebook/rocksdb/issues/13587.
This PR exposes the optimized implementation of batched reads through a `Transaction` object to Java clients.
The latency improvement of transactional multiget on production workload achieved by switching the implementation is roughly:
```
quantile=0.2: 21%
quantile=0.5: 28%
quantile=0.8: 46%
quantile=1.0: 239%
```
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13589
Reviewed By: jaykorean
Differential Revision: D74660169
Pulled By: cbi42
fbshipit-source-id: d01780173e0500c96e5e431ff6645008cbf6e8b5
Summary:
Expose pinned WriteBatchWithIndex::GetFromBatchAndDB through C bindings so that one can read data from the `WriteBatchWithIndex` and db w/o copying the data.
This fixes https://github.com/facebook/rocksdb/issues/12969.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12970
Reviewed By: cbi42
Differential Revision: D74586418
Pulled By: jaykorean
fbshipit-source-id: a5a4d2e8ce3ddf4c2371fdfdb4e9c3309966a05d
Summary:
As titled.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13605
Test Plan: This is removing a test
Reviewed By: mszeszko-meta
Differential Revision: D74660230
Pulled By: jowlyzhang
fbshipit-source-id: 9c1d46b56d2f9ee43eba645563d4f954645d1ace
Summary:
We saw some crash test failure for secondary db. It happens during crash recovery verification. This PR logs the manifest number when such failure happens. This PR also includes a small fix in `TryCatchUpWithPrimary()` that could incorrectly check WAL not found case.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13594
Test Plan: monitor further secondary DB crash test failure.
Reviewed By: archang19
Differential Revision: D74488769
Pulled By: cbi42
fbshipit-source-id: 226e55b2f99a739e93abda3ee91c05b80f59bf6a
Summary:
This PR fixes a bug where the file checksum for an external table file was not being calculated by SstFileWriter. The checksum is calculated in WritableFileWriter, so we need to pass that the the external table builder rather than the FSWritableFile pointer directly. However, WritableFileWriter is private to RocksDB, so wrap it in an FSWritableFile and pass it.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13591
Test Plan: Add a new test in table_test.cc
Reviewed By: jaykorean
Differential Revision: D74410563
Pulled By: anand1976
fbshipit-source-id: c7fa8142e20da8836589dee5fa50919951cf4046
Summary:
Prior to this PR, for FIFO kChangeTemperature compaction was done by iterating and reading thru the input sst and generate the output sst. This was wasteful since for FIFO we could apply the "trivial" move by copying the input sst to the out sst without need decompress/compress and reading thru the input sst content at all. This PR added "allow_trivial_copy_when_change_temperature" to the CompactionOptionsFIFO.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13562
Reviewed By: cbi42
Differential Revision: D73295404
Pulled By: mikechuangmeta
fbshipit-source-id: 02241c7389797730ecd4a3b636837cb5f912b424
Summary:
Allow specifying ReadOptions for WBWI iterator when creating it through the C bindings. This allows to specify upper and lower bounds for the created iterator.
This fixes https://github.com/facebook/rocksdb/issues/12963.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12968
Reviewed By: pdillinger
Differential Revision: D74188049
Pulled By: jaykorean
fbshipit-source-id: 970d9910472dfedaa29a800c6d52bec14c656f3c
Summary:
clarify in comments and fix one implementation under NewIterator where option `memtable_op_scan_flush_trigger` does not work correctly with tailing iterator yet. This is because tailing iterator can rebuild iterator internally which reads from a newer memtable, and DBIter's reference to active memtable needs to be refreshed. This PR clarifies that `memtable_op_scan_flush_trigger` will have no effect on tailing iterator. We can add the support in the future if needed.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13586
Test Plan: existing tests.
Reviewed By: jaykorean
Differential Revision: D74108099
Pulled By: cbi42
fbshipit-source-id: 7c6608485d57755abc44f3be0b3c5d82a7bc5ca9
Summary:
While working on some compression refactoring, I noticed that `NotifyCollectTableCollectorsOnBlockAdd()` was being called from multiple threads (with `parallel_threads` > 1), meaning we were violating the promise that TablePropertiesCollectors need not be thread safe (and typically will not be, for efficiency).
Fixing this is a bit awkward or intrusive. Even though it seems weird to expose `block_compressed_bytes_fast` and `block_compressed_bytes_fast` in the public `BlockAdd()` function, and NOT the actual compressed block size used, there are some Meta-internal uses that would at least require negotiation / coordination to deprecate and remove. So it's probably easiest to just keep the awkward functionality and do the necessary modifications to call from a single thread.
The simplest solution that preserves the functionality with `parallel_threads` > 1 (provide the sampling data, expected ordering between `BlockAdd()` and `AddUserKey()`, no races) is to do the compression sampling in the thread building uncompressed blocks. Specifically, moving `NotifyCollectTableCollectorsOnBlockAdd()` and the compression sampling from `CompressAndVerifyBlock()`, which is called in parallel, to table builder `Flush()`, which is only called serially (per file). Even though this adds some compression to that single thread when sampling is enabled, that should be tolerable without complicating the code or regressing performance. Some related or nearby optimizations are included to ensure this.
* Got rid of a lot of unnecessary indirection and unnecessary fields in BlockRep, which should be a step in improving parallel compression performance (still bad IMHO).
* Restructured some `if`s etc. to streamline some logic
This satisfies my original refactoring need to moving the sampling code higher up the stack from `CompressBlock()`, to set up some other upcoming refactorings. The other caller of `CompressBlock()` (legacy BlobDB) doesn't need it, and in fact is better off calling `CompressData()` directly because it does not appear to be dealing with the various "no compression" outcomes introduced by `CompressBlock()`.
Eventual follow-up:
* Performance data below shows how the overhead of parallel compression can make it slower, with available CPUs, compared to serial compression. This infrastructure should be re-designed/re-engineered to reduce thread creation, context switches, etc. Also, more of the processing such as checksumming could be parallelized. (Things dependent on the block location in the file, such as ChecksumModifierForContext and cache warming, cannot be parallelized.)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13583
ThreadSanitizer: data race /data/users/peterd/rocksdb/./db_stress_tool/db_stress_table_properties_collector.h:36:5 in rocksdb::DbStressTablePropertiesCollector::BlockAdd(unsigned long, unsigned long, unsigned long)
```
Performance:
```
SUFFIX=`tty | sed 's|/|_|g'`; for ARGS in "-compression_parallel_threads=1 -compression_type=none" "-compression_parallel_threads=1 -compression_type=snappy" "-compression_parallel_threads=4 -compression_type=snappy"; do echo $ARGS; (for I in `seq 1 100`; do ./db_bench -db=/dev/shm/dbbench$SUFFIX --benchmarks=fillseq -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -disable_wal -write_buffer_size=12000000 $ARGS 2>&1 | grep micros/op; done) | awk '{n++; sum += $5;} END { print int(sum / n); }'; done
```
Average ops/s of 100 runs, running before & after at the same time, using clang DEBUG_LEVEL=0:
-compression_parallel_threads=1 -compression_type=none
Before: 1976319
After: 1983840 (+0.3%)
-compression_parallel_threads=1 -compression_type=snappy
Before: 1945576
After: 1953473 (+0.4%)
-compression_parallel_threads=4 -compression_type=snappy
Before: 1573190
After: 1611881 (+2.4%)
-compression_parallel_threads=4 -sample_for_compression=100 (pretty high sample rate)
Before: 1577167
After: 1589704 (+0.8%)
-compression_parallel_threads=4 -sample_for_compression=10 (crazy high sample rate)
Before: 1581276
After: 1393453 (-11.9%)
As seen, you need a very very high compression sample rate to see a regression. I would expect a setting like 1000 to be more typical.
Test Plan:
Along with existing unit tests + CI, expanded crash test to make its TablePropertiesCollector non-trivial, to exercise the bug (and other potential bugs), which was confirmed with local run of whitebox_crash_test with TSAN:
```
Reviewed By: hx235
Differential Revision: D73944593
Pulled By: pdillinger
fbshipit-source-id: f1dcba4ebdc01e735251037395003945c9b34e62
Summary:
I added `TransactionDBOptions::txn_commit_bypass_memtable_threshold` previously but per DB option is not dynamically changeable. Adding it as a per transaction option to make it easier to use. The option naming is updated to make it easier for customer to understand `large_txn_commit_optimize_threshold`. The transaction DB option `TransactionDBOptions::txn_commit_bypass_memtable_threshold` is marked as deprecated.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13582
Test Plan:
- new unit test
- updated stress test to use this new transaction option
Reviewed By: jowlyzhang
Differential Revision: D73960981
Pulled By: cbi42
fbshipit-source-id: 406f6e0f5f4eb6b336976f9a93b0bc08e61a9662
Summary:
AcquireLocked() returns transaction ids that currently hold the lock for deadlock detection purpose. We should not include the id of the transaction that is trying to acquire the lock, since this would lead to a false-positive deadlock detection where the deadlock is a self-loop. Note that since `wait_ids` is never cleared, there is another bug where if AcquireLocked() fails with kLockLimit, we could do deadlock detection based on `wait_ids` from a previous lock acquire attempt. This PR fixes both bugs.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13575
Test Plan: added a unit test repro that shows deadlock status can be incorrectly returned.
Reviewed By: jaykorean
Differential Revision: D73617887
Pulled By: cbi42
fbshipit-source-id: a6388b3ec53db13e2c502d60199378ea95885841
Summary:
**Context/Summary:**
Similar to https://github.com/facebook/rocksdb/commit/0a43d8a261b9c633c0a4e369b1ef33aa5ee32810, this is to verify flush output file contains the exact number of keys (represented by its `TableProperties::num_entries`) as added to table builder for block-based and plain table format. The implementation reuses a temporary compaction stats to record output record and existing input record (with some refactoring)
**Bonus:**
following https://github.com/facebook/rocksdb/commit/0a43d8a261b9c633c0a4e369b1ef33aa5ee32810#r154313564, limit compaction output record count check within block based table and plain table format as well as removing extra test setting; fix some typo
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13556
Test Plan: New test
Reviewed By: jaykorean
Differential Revision: D73229644
Pulled By: hx235
fbshipit-source-id: 2a7796450048b3bcb2d5c38f2b5fc6b53e4aae37
Summary:
we want to limit the maximum disk space used by RocksDB in one of our Go services, as it runs on a highly disk-constrained network switch.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13404
Reviewed By: cbi42
Differential Revision: D73517940
Pulled By: jaykorean
fbshipit-source-id: ae91fc7a4992399e20f06cc67dad8130cf19049e
Summary:
This test was failing sporadically for me, like
```
db/db_properties_test.cc:247: Failure
Expected: (static_cast<double>(dbl_a - dbl_b) / (dbl_a + dbl_b)) <
(bias), actual: 0.113964 vs 0.1
```
I tried waiting for compaction in the test, but that made it fail consistently. Based on inspection of the test and the related test AggregatedTablePropertiesAtLevel already using `disable_auto_compactions = true`, I'm applying that to this test.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13568
Test Plan: Parallel runs of the unit test, before and after
Reviewed By: jaykorean
Differential Revision: D73463685
Pulled By: pdillinger
fbshipit-source-id: 84df7cc9bdcd1caa108a7be254ffbebbe9a77de7
Summary:
* Mostly, remove `sample_for_compression` from CompressionInfo because it's not used by the core function it serves, `CompressData()`. Confusing (and inefficient), especially in db_bench where it appears to use `FLAGS_sample_for_compression` in places where it is actually ignored.
* Various clarifying comments, clean-ups, and tiny optimizations
* Prepare some structures like `CompressionDict` for more usage
* Some TODOs and FIXMEs about some things I've noticed are amiss, confusing, or excessive
* A notable optimization opportunity that might become a "pay as you go" improvement for the potential indirection costs of customizable compression: use C++23's resize_and_overwrite() in compress functions to avoid zeroing the string buffer contents before populating it.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13539
Test Plan: existing tests / CI
Reviewed By: hx235
Differential Revision: D73451273
Pulled By: pdillinger
fbshipit-source-id: 0373627466d695043d21146ce34d52f189ae9432
Summary:
Updated version, HISTORY and compatibility script for 10.3 release (no folly hash update in this release).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13566
Test Plan: CI
Reviewed By: anand1976
Differential Revision: D73391839
Pulled By: jaykorean
fbshipit-source-id: 075bb1f9f25caf96c4fcca7f4a315666acd5a288
Summary:
Adding an arbitrary options map so that any additional overridable options can be added without RocksDB change. Unknown options will be ignored
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13552
Test Plan:
Unit Test added
```
./db_secondary_test -- --gtest_filter="*OptionsOverrideTest*"
```
Reviewed By: hx235
Differential Revision: D73203789
Pulled By: jaykorean
fbshipit-source-id: 176bd9849d2bc60e78657c119e10a1a2a0988cd1
Summary:
This PR adds a DB::GetNewestUserDefinedTimestamp API to get the newest timestamp of the column family. This is only for when the column family enables user defined timestamp.
It checks the mutable memtable, the immutable memtable and the SST files, and returns the first newest user defined timestamp found. When user defined timestamp is not persisted in SST files, there is metadata in MANIFEST tracking upperbound of flushed timestamps, so the newest timestamp in SST files can be found. If user defined timestamps are
persisted in SST files, currently no timestamp metadata info is persisted. A NotSupported status will be returned if SST files need to be checked in that case.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13547
Test Plan: Added tests
Reviewed By: cbi42
Differential Revision: D73123575
Pulled By: jowlyzhang
fbshipit-source-id: 460ac4f9c96926d3c8fcf7944edab8dc0feae1dd
Summary:
add support for ingesting a WriteBatchWithIndex into the DB with the new API `IngestWriteBatchWithIndex()`. This ingestion works similarly as `TransactionOptions::commit_bypass_memtable` where the WBWI will be ingested as an immutable memtable. Since this skips memtable writes, it improves the write performance when writing a large write batch into the DB. Currently this API only supports `disableWAL=true`. Support for WAL write will be in a follow up if needed.
For a WBWI to be ingestable, we needed to call `SetTrackPerCFStat()` at WBWI creation. This PR removes this step for simpler usage and per CF stats will always be tracked in WBWI. `WBWIIteratorImpl::TestOutOfBound()` is optimized to offset the performance impact.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13550
Test Plan:
- new unit test
- stress test option ingest_wbwi_one_in and ran a few runs of `python3 ./tools/db_crashtest.py blackbox --enable_pipelined_write=0 --use_timed_put_one_in=0 --use_put_entity_one_in=0 --ingest_wbwi_one_in=10 --test_batches_snapshots=0 --enable_blob_files=0 --preserve_unverified_changes=1 --avoid_flush_during_recovery=1 --disable_wal=1 --inplace_update_support=0 --interval=40`
Reviewed By: jowlyzhang
Differential Revision: D73152223
Pulled By: cbi42
fbshipit-source-id: 339f8ed26ac5a798238870df3ba857ba1add759b
Summary:
A reopened writable file's size is not correctly tracked in the `WritableFile`'s internal state. This PR adds a querying to the file system to get the initial file size in the reopen case and use it to populate posix `WritableFile`'s internal state.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13534
Reviewed By: anand1976
Differential Revision: D72756628
Pulled By: jowlyzhang
fbshipit-source-id: 6f02b5c5da069fe49055d7b75bec9e7e47d5cd71
Summary:
Add a test to cover an internal user's expected behavior of using atomic_replace_range feature to atomically ingest a version key and a data file.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13549
Test Plan: This is a test
Reviewed By: cbi42
Differential Revision: D73142626
Pulled By: jowlyzhang
fbshipit-source-id: a5bdc24b762cbe91dd4d94242b9e1539c9feaf61
Summary:
add support for atomic_flush when using WBWI ingestion [feature](https://github.com/facebook/rocksdb/blob/29c6610617ddc1b486f12b99c16e7c9851e80430/include/rocksdb/utilities/transaction_db.h#L387). Transaction DB usually uses WAL so atomic_flush is not as helpful. This is to prepare for a follow up PR that enables ingesting WBWI without using transaction DB.
This PR also removes a redundant parameter `prep_log` for the WBWI ingestion feature.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13545
Test Plan:
- unti test added
- stress test will be added as we add support to ingest WBWI without using transaction DB.
Reviewed By: jowlyzhang
Differential Revision: D73062342
Pulled By: cbi42
fbshipit-source-id: e05da55dfabb8241a042214b9d50b1b49d42613e
Summary:
**Context/Summary:**
This PR adds new stats to measure compaction readahead size for rocksdb managed prefetching (not FS prefetching). It can be used to verify compaction read-ahead is doing what's configured. This PR also excludes compaction readahead stats from user scan readahead stats measured in existing stats so there is a cleaner separating between these two.
Bonus: this PR also included some typo fixing about "io activities"
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13520
Test Plan: Modified existing test to verify stats
Reviewed By: archang19
Differential Revision: D72892850
Pulled By: hx235
fbshipit-source-id: 1a73182061baa044c9c9193a2b0fd967ffe75c4a
Summary:
* Clarify in API comments which `log_` options in DBOptions relate to WALs, info log, and/or manifest files.
* Rename a bunch of "log" things to "wal" for clarity, especially in DBImpl. (More to go, especially some more challenging cases like `DBImpl::logs_`, but a step in the right direction IMHO)
* Simplify DBImpl ctor by moving constant initializers to field definitions.
* Use RelaxedAtomic for (renamed) `wals_total_size_`
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13490
Test Plan: existing tests
Reviewed By: cbi42
Differential Revision: D71939382
Pulled By: pdillinger
fbshipit-source-id: 852f4737eca83e6ad653010cc197ad1b6e6bae13
Summary:
Introduce a mutable CF option `memtable_op_scan_flush_trigger`. When a DB iterator scans this number of hidden entries (tombstones, overwritten puts) from the active memtable in a Seek() or Next() operation, it marks the memtable to be eligible for flush. Subsequent write operations will schedule the marked memtable for flush.
The main change is small and is in db_iter.cc. Some refactoring is done to consolidate and simplify creation of `ArenaWrappedDBIter` and `DBIter`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13523
Test Plan:
- new unit tests added.
- added `memtable_op_scan_flush_trigger` in crash test
- benchmark:
The following benchmark was done with a previous version of the PR where the option was `memtable_tombstone_scan_limit` and it concerns tombstone only. The results should still be applicable for the case when there's no overwritten puts.
Tests that when memtable has many tombstones, the option helps to improve scan performance:
```
TEST_TMPDIR=/dev/shm ./db_bench --benchmarks=seekrandomwhilewriting --expand_range_tombstones=true --writes_per_range_tombstone=1 --max_num_range_tombstones=10000000 --perf_level=2 --range_tombstone_width=100 --memtable_tombstone_scan_limit=
memtable_tombstone_scan_limit = 10000
seekrandomwhilewriting : 18.527 micros/op 53973 ops/sec 18.527 seconds 1000000 operations; (7348 of 1000000 found)
next_on_memtable_count = 122305248
grep "flush_started" /dev/shm/dbbench/LOG | wc
8 200 2417
memtable_tombstone_scan_limit=200
seekrandomwhilewriting : 4.918 micros/op 203315 ops/sec 4.918 seconds 1000000 operations; (4510 of 1000000 found)
next_on_memtable_count = 1853167
grep "flush_started" /dev/shm/dbbench/LOG | wc
184 4600 54121
When memtable_tombstone_scan_limit=200, more flush is trigged to drop tombstones sooner and improve scan performance.
```
Tests that the new option does not introduce noticeable regression:
```
TEST_TMPDIR=/dev/shm ./db_bench --benchmarks=seekrandomwhilewriting[-X5] --expand_range_tombstones=true --writes_per_range_tombstone=1 --max_num_range_tombstones=10000000 --perf_level=2 --range_tombstone_width=100 --seed=123
Main:
seekrandomwhilewriting [AVG 5 runs] : 46049 (± 4512) ops/sec
PR:
seekrandomwhilewriting [AVG 5 runs] : 46100 (± 4470) ops/sec
The results are noisy with this PR performing better and worse in different runs, with no noticeable regression.
```
Reviewed By: pdillinger
Differential Revision: D72596434
Pulled By: cbi42
fbshipit-source-id: 2d51a0221dc20dac844aeba2ad3999d075a4cf91
Summary:
When ReadOptions.allow_unprepared_value is true, a `Iterator::PrepareValue()` call is needed to prepare the value after an entry is pinpointed, to only load the blob when it's actually needed. And it uses the `saved_key_.GetUserKey()` to prepare value.
https://github.com/facebook/rocksdb/blob/6d802639f7dc35bf765dbe1ed6b3942e4d76375d/db/db_iter.cc#L319
In the reverse iteration case, when the `FindValueForCurrentKeyUsingSeek()` path is used, `saved_key_` is only updated when `ReadOptions.iter_start_ts` is specified. This PR fixes it by updating `saved_key_` for the other case too.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13531
Test Plan: The FIXME test that reproduce the bug is updated
Reviewed By: pdillinger
Differential Revision: D72681397
Pulled By: jowlyzhang
fbshipit-source-id: 6c239da53c9beed1560d30013474f2ba542b245c
Summary:
Fix a reported data race, accessing `manifest_reader_` without locking `mutex_` could race with another `DBImpl::Secondary::TryCatchUpWithPrimary` thread that is updating to a new manifest in `ReactiveVersionSet::MaybeSwitchManifest`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13529
Test Plan: Existing tests
Reviewed By: hx235
Differential Revision: D72655645
Pulled By: jowlyzhang
fbshipit-source-id: 08599862346bb39a6872c3adfd7f0097fc633849
Summary:
As titled. This option has been marked deprecated since introduction of a better option `max_write_buffer_size_to_maintain` and acts as its fallback since RocksDB 6.5.0 The internal user we know these options were created for migrated to `max_write_buffer_size_to_maintain` for a long time too.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13491
Test Plan: existing tests
Reviewed By: cbi42
Differential Revision: D71984601
Pulled By: jowlyzhang
fbshipit-source-id: c264d4809e311f60fdbad817ebfade256db549b6
Summary:
**Context/Summary:**
Rebased on https://github.com/facebook/rocksdb/pull/13522/files, this is to use the refactored function to calculate tail size from table property "tail_start_offset"
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13524
Test Plan: CI
Reviewed By: anand1976
Differential Revision: D72576262
Pulled By: hx235
fbshipit-source-id: 78c126bc64024c2341d183d6871e06d55fd27501
Summary:
**Context/Summary:**
This is to fix a bug that tail size of remote compaction output SST file is not persisted to manifest in primary instance. This prevent us from using direct tail prefetch optimization each time opening this SST file.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13522
Test Plan: Modify existing UT that failed before the fix
Reviewed By: anand1976
Differential Revision: D72479612
Pulled By: hx235
fbshipit-source-id: 1ba8aa66fac71b9196589f60076229c29a103706
Summary:
Public APIs like `DB::GetFullHistoryTsLow` and `DB::IncreaseFullHistoryTsLow` have such safeguarding, allowing them to only be invoked when user defined timestamp is enabled. This PR adds safeguarding into related internal APIs in `ColumnFamilyData` to properly handle the case when the UDT feature are toggled.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13521
Test Plan: ./db_with_timestamp_basic_test --gtest_filter="*EnableDisableUDT*"
Reviewed By: cbi42
Differential Revision: D72475234
Pulled By: jowlyzhang
fbshipit-source-id: 194c07287e3100da95450b04c76552c9d4a86c2d
Summary:
A multi scan API for users to pass a set of scan ranges and have the table readers determine the optimal strategy for performing the scans. This might include coalescing of IOs across scans, for example. The requested scans should be in increasing key order. The scan start keys and other info is passed to NewMultiScanIterator, which in turn uses the newly added Prepare() interface in Iterator to update the iterator. The Prepare() takes a vector of ScanOptions, which contain the start keys and optional upper bounds, as well as user defined parameters in the property_bag taht are passed through as is to external table readers.
The initial implementation plumbs this through to the ExternalTableReader. This PR also fixes an issue of premature destruction of the external table iterator after the first scan of the multi-scan. The `LevelIterator` treats an invalid iterator as a potential end of file and destroys the table iterator in order to move to the next file. To prevent that, this PR defines the `NextAndGetResult` interface that the external table iterator must implement. The result returned by `NextAndGetResult` differentiates between iterator invalidation due to out of bound vs end of file.
Eventually, I envision the `MultiScanIterator` to be built on top of a producer-consumer queue like container, with RocksDB (producer) enqueueing keys and values into the container and the application (consumer) dequeueing them. Unlike a traditional producer consumer queue, there is no concurrency here. The results will be buffered in the container, and when the buffer is empty a new batch will be read from the child iterators. This will allow the virtual function call overhead to be amortized over many entries.
TODO (in future PRs):
1. Update the internal implementation of Prepare to trim the ScanOptions range based on the intersection with the table key range, taking into consideration unbounded scans and opaque user defined bounds.
2. Long term, take advantage of Prepare in BlockBasedTableIterator, atleast for the upper bound case.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13473
Reviewed By: pdillinger
Differential Revision: D71447559
Pulled By: anand1976
fbshipit-source-id: 31668abb0c529aa1ac1738ae46c36cbddf9148f1
Summary:
**Context/Summary:**
- This is an attempt to fix our [build-window-vs2022 failure](https://github.com/facebook/rocksdb/actions/runs/14215681026/job/39831770554?fbclid=IwZXh0bgNhZW0CMTAAAR2BQLjp8kC1u1yyvN1_S5qwmrHEZOfzxJdcbj2vq7mvwwq83n1cbkmiBCA_aem_ygYxQA5EUmxh2y4EjMlTfg) below. snappy-1.1.8's cmake_minimum_required being less than 3.5 seems to trigger the complaint. Hopefully downloading the 1.2.2 which is the [first version starting to use higher cmake_minimum_required version](https://github.com/google/snappy/releases/tag/1.2.2) solves the failure.
```
Directory: D:\a\rocksdb\rocksdb\thirdparty\snappy-1.1.8
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 4/2/2025 9:02 AM build
CMake Error at CMakeLists.txt:29 (cmake_minimum_required):
Compatibility with CMake < 3.5 has been removed from CMake.
Update the VERSION argument <min> value. Or, use the <min>...<max> syntax
to tell CMake that the project requires at least <min> but has been updated
to work with policies introduced by <max> or earlier.
Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3.5 to try configuring anyway.
```
- The downloaded snappy do not include the content under nested repos Google Test and Google Benchmark. But snappy cmake by default will attempt to build them. Since we don't change snappy, we don't need building such development suit. This PR also disabled snappy cmake's attempt to build them.
- By running above changes, the same build [complained](https://github.com/facebook/rocksdb/actions/runs/14228883966/job/39874927730?pr=13514) about java cmakelists requiring too low cmake_minimum_required as well. So this PR also upgraded its cmake_minimum_required to be 3.11 aligning with its warning message
```
if(${CMAKE_VERSION} VERSION_LESS "3.11.4")
message("Please consider switching to CMake 3.11.4 or newer")
endif()
```
**Test plan**
Monitor build-window-vs2022 for this PR
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13514
Reviewed By: pdillinger
Differential Revision: D72333581
Pulled By: hx235
fbshipit-source-id: 1a9096738d39c8b1d270fe17fbd78c1ea4c4c45e
Summary:
Essentially fix https://github.com/facebook/rocksdb/issues/13429 by
* Avoiding publishing to readers a partial write batch written to memtable. Also clarify in DB::Write that WriteBatch is applied atomically, and improve some logging.
* When we know we have written a bad write batch to WAL due to memtable insert failure, make a good effort to roll it back to make the DB recoverable. (Not compatible with all options.)
Fixes https://github.com/facebook/rocksdb/issues/13429
Follow-up items:
* More rigorously test and fix the code paths and option combinations where these features could be useful.
* Allow default CF with disallow_memtable_writes (with caveat that violation stops writes on your open DB)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13489
Test Plan: Updated existing test, manually verified the DB went into a "stopped" state at least in this example.
Reviewed By: jaykorean
Differential Revision: D71917670
Pulled By: pdillinger
fbshipit-source-id: c9b9dfc102817fc4c160a6c7170c04011c228aaf
Summary:
We are seeing some occasional failures with WRITE_(UN)PREPARED crash test runs, and it's alarming when these are grouped in with WRITE_COMMITTED, which AFAIK is the only one considered mature and mission-critical at this point.
* Mark WRITE_(UN)PREPARED as EXPERIMENTAL in the public APIs
* Separate out the `_with_txn` crash test jobs by write policy, now `_with_wc_txn`, `_with_wp_txn` and `_with_wup_txn` so that the major functional and maturity differences are better grouped.
* Add `_with_multiops_wup_txn` which was apparently missing
* Clean up db_crashtest.py for better consistency
* Get rid of awkard "write_policy" parameter that could conflict with authoritative "txn_write_policy" parameter.
* Similarly, move some multiops logic from different parameter sets to finalize_and_sanitize logic.
Immediate internal follow-up:
* Migrate from `_with_txn` which are now deprecated aliases of `_with_wc_txn` to more jobs with the new variants. And likely also add new multiops job.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13499
Test Plan: manual runs of modified jobs, at least long enough to spot check things like txn_write_policy
Reviewed By: hx235
Differential Revision: D72015307
Pulled By: pdillinger
fbshipit-source-id: 06b99b2d1f15ac76fe7b8e22c93a51aaa2a42ecf
Summary:
**Context/Summary:**
MaxMemCompactionLevel() developed 10 years ago simply returns the level a memtable flushed to, which has historically been L0 and have no plan to change to something different for future. It is also not used in test or internally.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13503
Test Plan: CI + fake release
Reviewed By: cbi42
Differential Revision: D72066092
Pulled By: hx235
fbshipit-source-id: 5ff5b16a6664ef3efabd3a6fbd8a2d0529b62460
Summary:
based on the option comment, `ignore_range_deletions` was added due to the overhead of range deletions in read path when a DB does not use DeleteRange(). The current implementation should not have a noticeable performance difference in this case.
`experimental::PromoteL0()` can be replaced by doing a manual compaction with proper CompactRangeOptions.
There are some internal use of these option and API so we will remove them later after the usages are updated.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13500
Test Plan:
comment change only.
Performance: benchmark the performance difference with `ignore_range_deletions` and without (borrowed flag `universal_incremental` for this purpose), ran at the same time on the same machine.
- random point get:
- ignore_range_deletions=false: 343078 ops/sec
- ignore_range_deletions=true: 340219 ops/sec (0.8% slower)
```
(for I in $(seq 1 1); do TEST_TMPDIR=/dev/shm/t1 /data/users/changyubi/vscode-root/rocksdb/db_bench --benchmarks=fillseq,waitforcompaction,readrandom --write_buffer_size=67108864 --writes=1000000 --num=2000000 --reads=1000000 --seed=1723056275 --universal_incremental=false 2>&1 | grep "readrandom"; done;) | awk '{ t += $5; c++; print } END { print 1.0 * t / c }';
```
- sequential scan:
- ignore_range_deletions=false: 5378104 ops/sec
- ignore_range_deletions=true: 5393809 ops/sec (0.3% faster)
```
(for I in $(seq 1 10); do TEST_TMPDIR=/dev/shm/t1 /data/users/changyubi/vscode-root/rocksdb/db_bench --benchmarks=fillseq,waitforcompaction,readseq[-X10] --write_buffer_size=67108864 --writes=1000000 --num=2000000 --universal_incremental=true --seed=1723056275 2>1 | grep "\[AVG 10 runs\]"; done;) | awk '{ t += $6; c++; print; } END { printf "%.0f\n", 1.0 * t / c }';
```
The difference in ops/sec for the two benchmarks is likely noise.
Reviewed By: hx235
Differential Revision: D72069223
Pulled By: cbi42
fbshipit-source-id: ad82a051aa4682790d2178cd4fb2d1467397fbb5
Summary:
Acquiring a lock here can take a long time and cause a user mode scheduler to hold up, as it relies on explicit yielding. Hence, forcing a check here but ignoring any abort requests. Would rely on upstream to take action on aborts.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13498
Reviewed By: pdillinger
Differential Revision: D71987173
Pulled By: jainpr
fbshipit-source-id: 4aec40bdf0bc657e29f72c306c576b3117f97a25
2025-03-27 15:10:55 -07:00
1085 changed files with 266627 additions and 33191 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.
**Refactoring traps:** At least in production code, avoid constructs that could allow existing code to unexpectedly, quietly change meaning.
Specifically:
* Avoid new defaulted parameters. This is the #1 trap on refactoring!
* Avoid static_cast, reinterpret_cast, and C-style casts; static_cast_with_check, up_cast, and lossless_cast from cast_util.h are preferred.
* Avoid declared type `auto` (`auto&`, `auto*` OK).
### 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 (CACHE_LINE_SIZE, typically 64 bytes) and avoid false sharing in concurrent code (see CacheAlignedWrapper). Especially when adding new data members, be mindful of ordering within structs and classes to reduce unnecessary padding (special care with bool members) and avoid larger-than-necessary types (e.g. prefer OptSlice to std::optional<Slice>).
**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, e.g. for error cases and other rare or otherwise costly cases, but NOT for predicting popular configurations. 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:** Performance is known critical in places like inner loops of compaction, read path and write path. Elsewhere, try to find a "holy trinity" solution of *correct, simple, and reasonably efficient*. More complex solutions are only appropriate when profiling/benchmarking data shows a measurable payoff.
### 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).
**Behavioral Compatibility:** Behavior changes are human judgment calls that must be intentional, well-documented (including release notes), and well researched for potential downstream production impact. Agents and reviewers must call out potential gaps, ambiguity, or adverse impacts in intentionality, documentation, and production impact. For an existing accepted input—including an undocumented, discouraged, or historically ignored value—the analysis should compare old and new observable behavior, including performance and resource impact, identify known direct callers and stored configurations, and add or update regression coverage as appropriate.
**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, without unnecessary embelishment nor dwelling on implementation details nor project planning. When non-obvious, include usage examples, parameter descriptions, known bugs or limitations, notes on thread safety, performance characteristics, and compatibility considerations. Re-read comments for ambiguous terminology and phrasing, such as ambiguously re-purposed programming jargon.
---
## 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 / "Public API" (`include/rocksdb/`)
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:
### Contract Boundaries
- [ ] Is each behavior owned by the right layer? High-level policy (for example,
"compaction wants this I/O mode") should live at the caller/policy layer, while
lower layers should expose generic mechanisms (for example, "open a fresh
reader", "skip shared cache insertion", or "use these FileOptions").
- [ ] Do comments and names describe local contracts rather than leaking a
specific caller's rationale into reusable APIs? Generic code should not need to
know about one current use case unless the API itself is intentionally
use-case-specific.
- [ ] Does each flag or parameter control one coherent behavior? If one boolean
starts implying ownership, cache policy, I/O mode, prefetching, and caller
identity, split it into explicit flags or an options struct.
- [ ] Could a future caller use this lower-level API without accidentally
inheriting assumptions from compaction, backup, user reads, or a particular
table format? If not, tighten the contract with assertions, clearer names, or
a narrower API.
- [ ] Are implementation details not being used as policy signals? Prefer an
explicit contract over inferring behavior from incidental fields such as file
options, cache handles, or current table-reader state.
### 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).
11.**Contract Boundary Leaks:** When a change plumbs a new option or use-case
specific behavior through multiple subsystems, review the call chain for
contract leaks. Caller-specific rationale belongs at the call site or public API
documentation; reusable layers should expose precise, layer-local capabilities.
Watch especially for comments mentioning one caller in generic code, booleans
that silently bundle several behaviors, and downstream code inferring policy
from an implementation detail instead of an explicit option.
---
## Important tips
### Pull request and diff summaries
* Before writing or updating an external RocksDB pull request summary or its
corresponding internal Phabricator diff summary, read and follow
`claude_md/pull_request_summary_guideline.md`.
### Build system
* There are 3 build system. Make for git clones, BUCK (meta internal) for hg
clones, and CMake for some special cases.
* 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
* For -j in make command, use the number of CPU cores to decide it.
* When searching for references to something (a symbol, library, etc.), do not
restrict or truncate your search based on presumed relevance or scope. It is
important and time-saving to keep the repo reasonably consistent across
different build systems, programming languages, and even between
documentation and implementation.
### Avoiding mixed build modes with Make (use `AUTO_CLEAN=1`)
Object files are written to the same paths regardless of build flags, so
reusing objects from a prior build with different flags causes confusing
linker errors, etc. This problem is essentially avoidable by ALWAYS using
`AUTO_CLEAN=1 make -j<n> <something>` for manual make invocations. This
will automatically clean object files if the build parameters/flavor have
changed. The `build_tools/rockstest.sh` / `rocksptest.sh` helpers described
below set `AUTO_CLEAN=1` for you.
`AUTO_CLEAN=1` does not fix Make failures associated with stale .d files
referring to removed files. Resolve that manually or with `make clean`
without complaining to the user.
### Source checks
* Run `make check-sources` before committing. This catches non-ASCII
characters in source files and other source-level issues that CI will
reject. In particular, **do not use Unicode characters** (em dashes,
smart quotes, etc.) in comments or strings -- use ASCII equivalents
(`--` instead of em dash, `'` instead of smart quote, etc.).
### License headers
* Every new source file needs a license header. For a file that does **not**
carry an outside/third-party copyright, use the standard Meta dual-licensed
header (the dual-license designation is required -- a bare
"All Rights Reserved" copyright is not an acceptable open-source header):
```
// Copyright (c) Meta Platforms, Inc. and affiliates.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
```
Use a `#` comment prefix instead of `//` for shell, Python, and Makefile
fragments.
* Files derived from an external source (e.g. LevelDB) keep their original
upstream copyright line in addition to the header above.
### RTTI and dynamic_cast
* Production code and `db_stress` must build in **release mode
(`-fno-rtti`)**. Do not use `dynamic_cast` anywhere except unit tests.
Use `static_cast_with_check` from `util/cast_util.h` (validates with
`dynamic_cast` in debug builds, plain `static_cast` in release).
* Unit tests (`*_test.cc`) are built in debug mode with RTTI enabled.
### Cross-platform / portability
Local `make` only exercises Linux with GCC/Clang, but CI
(`.github/workflows/pr-jobs.yml` and `nightly.yml`) gates on a much wider
matrix, so portability breaks are invisible locally until CI fails. Code must
build (and where noted, run tests) across:
| Axis | Must support |
|------|--------------|
| OS | Linux (x86_64 + ARM), macOS, Windows |
| Architecture | Little-endian and big-endian targets, including Linux on s390x |
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 11.12.0 (10/07/2026)
### New Features
* Added `FullExternalTableFactory` and related full-mode external table APIs that preserve RocksDB internal keys, enabling external tables to support nonzero sequence numbers, point and single deletions, merge operands, blob indexes, wide-column entities, snapshots, live DB writes, and multi-level compactions.
### Public API Changes
* Added experimental coroutine capability interfaces for basic and full `ExternalTableReader` implementations. Coroutine-capable external readers can initiate file reads through `FSRandomAccessFile::SubmitReadAsync()`.
* `ExternalTableFactory::NewTableReader()` now receives an already-opened `FSRandomAccessFile` and its file size instead of requiring implementations to reopen `file_path`. The supplied file preserves RocksDB's direct-I/O alignment handling, rate limiting, read statistics and listener notifications, prefetching, and synchronous, asynchronous, and batched reads.
* Added `PutRangeDeletionBlock()`, `GetRangeDeletionBlock()`, and `IsDeleteRangeSupported()` to the external table interfaces so formats can opt in to range deletions using RocksDB's raw range-deletion metadata block.
* Opaque `PreparedFileInfo` metadata can now be serialized to a checksummed representation with `SerializePreparedFileInfo()` and restored with `DeserializePreparedFileInfo()` for reuse across processes.
* Removed the deprecated `IngestExternalFileOptions::write_global_seqno` option and its C, Java, and `ldb` APIs, `TableProperties::external_sst_file_global_seqno_offset`, and the `file_offset` output from `ExternalTableReader::GetPropertiesBlock()`. Existing external SST files whose global sequence number was rewritten without updating the properties-block checksum remain readable.
* `TablePropertiesNames` has no apparent use in the public API so has been deprecated there.
* `TableProperties::creation_time` is renamed to `oldest_ancestor_time`, which better describes its meaning, along with `TablePropertiesNames::kCreationTime` to `kOldestAncestorTime`. The misspelled `SstFileMetaData::oldest_ancester_time` is renamed to `oldest_ancestor_time`. The old names remain available as deprecated aliases and will be removed in a future release.
* Added `WriteBufferFlushPolicy` and `WriteBufferManager` policy selection for flushing the oldest or largest column family, including configurable batched flushing of the largest column families across DBs sharing a manager, with C and Java bindings.
### Behavior Changes
* When `ExternalTableBuilderBase::PutPropertiesBlock()` returns `NotSupported`, RocksDB now overrides `num_entries`, `raw_key_size`, and `raw_value_size` from `ExternalTableBuilderBase::GetTableProperties()` with the logical totals tracked by the external-table adapter. Format-specific properties such as `data_size` remain unchanged.
* The `FSWritableFile` passed to `ExternalTableFactory::NewTableBuilder()` now reports logical file size including RocksDB-buffered data and exposes the underlying file's direct-I/O mode and required buffer alignment.
* `Iterator::Refresh()` now returns `NotSupported` on iterators from a secondary DB's `DB::NewIterators()` only, because those iterators are pinned to one cross-column-family read view. Iterators from `DB::NewIterator()` are unaffected; to advance a batch, call `DB::NewIterators()` again after `TryCatchUpWithPrimary()`.
* `SstFileReader` now reports standard SST read statistics and file I/O listener notifications, and honors the configured read rate limiter.
### Bug Fixes
* Fixed full recovery from retryable background errors to atomically advance WAL replay boundaries for all column families, preventing partial recovery of multi-column-family write batches after a failed recovery flush.
* Fixed RocksDB builds on big-endian architectures such as s390x by disabling the optional persistent fault-injection log, whose binary format is little-endian-only.
* Fixed `DB::ClipColumnFamily()` deleting a non-L0 SST file whose largest key equals `begin_key`, which lost keys at the start of the kept range.
* Fixed swapped `oldest_key_time` and `newest_key_time` values in the `table_file_creation` event log entry.
* Fixed `ExternalTableReader::VerifyChecksum()` results not being honored by the external table adapter.
* Fixed a bug in manifest handling that could lead to a hang or memory corruption in the case of LSM sanity checks being fatally violated (exceptional; see `force_consistency_checks`).
* Fixed `MultiGet()` with `ReadOptions::read_tier == kBlockCacheTier` reading data blocks from disk when `allow_mmap_reads` is false. Keys whose data block is not in the block cache now return `Status::Incomplete()`, the same as `Get()`.
* Fixed trace replay (`DB::NewDefaultReplayer()`, db_bench `replay`) failing every read record (Get, MultiGet, iterator seek) on the default column family with `Corruption: Invalid Column Family ID` when the default column family handle was not passed, which is always the case in db_bench with `--num_column_families=1`.
* Fixed multi-threaded `Replayer::Replay()` ignoring errors from executing trace records and returning OK. It now stops and returns the error, including `Status::NotSupported()` from executing a record, as single-threaded replay does.
* Fixed secondary DB `NewIterators()` returning iterators from different database states or blocking for `TryCatchUpWithPrimary()` by serving the last completed catch-up view.
### Performance Improvements
* Single Get coroutine reads using non-mmap, non-direct I/O on a `kFSBuffer`-capable `FileSystem` now use `FSRandomAccessFile::SubmitReadAsync()` instead of falling back to synchronous `Read()`. When rate-limited, these reads acquire their full token budget before submission to preserve a single filesystem-owned buffer, which can make physical I/O burstier than reads using caller-provided buffers.
## 11.11.0 (09/21/2026)
### New Features
* External tables now support DB-assigned global sequence numbers when `IngestExternalFileOptions::allow_global_seqno` is enabled, allowing overlapping external tables to be ingested across multiple levels when compactions are disabled. This includes the deprecated `write_global_seqno` path when the reader supplies the properties block's file offset.
* Added checksum handoff support for SST file writes when `BlockBasedTableOptions::checksum` is `kCRC32c`, allowing block and trailer CRC32C checksums to be passed from the table builder to the file writer for buffered-data validation and filesystem in-flight write protection.
* Enhanced `sst_dump --command=recompress` to be more powerful for benchmarking, including through automation with a new `--json` option for machine-readable output.
### Public API Changes
* Changed the experimental async read statistics API by removing `DB::AsyncCallback::EnableStats()` and changing `OnComplete(const PerfContext*, const IOStatsContext*)` to `OnComplete()`. Callers must configure TLS statistics before each async or coroutine read. `DB::GetAsync()` and `DB::MultiGetAsync()` callbacks must copy per-operation statistics from TLS during `OnComplete()`; coroutine users must copy them from inside a `CoroStackableDB` hook before suspending again. Each operation leaves TLS statistics collection disabled.
* Added `Compressor::MaybeOverrideCompressionOptions()` (EXPERIMENTAL), an omnibus hook letting a custom compressor adjust the subset of `CompressionOptions` that the SST builder applies rather than the compressor itself: `parallel_threads`, `max_compressed_bytes_per_kb`, `auto_skip`, and `auto_skip_min_sample_every` (and potentially more in the future). This deprecates `Compressor::GetRecommendedParallelThreads()`, which it subsumes; the default implementation still honors an overridden `GetRecommendedParallelThreads()` for compatibility.
* Added experimental `CoroDB::CoGet()` and `CoroDB::CoMultiGet()` overloads for point reads from an `SstFileReader`.
* Added experimental `DB::GetWithMetadata()` and `DB::MultiGetWithMetadata()` APIs with opt-in output metadata for key timestamps and explicit-snapshot newer-version detection.
* Added a `DB::NewIterators()` overload that accepts parallel vectors of `ReadOptions` and column family handles, allowing each returned iterator to use independent read settings while sharing a consistent database state. The vectors must have the same size, all snapshots must be null or identical, and all `tailing` settings must match.
* Added `ReadOptions::custom_context`, an opaque, non-owning pointer passed through table reads so external table implementations can consume application-defined read options.
### Behavior Changes
* `ExternalTableReader::MultiGet()` is now used by RocksDB's `DB::MultiGet()` path. Previously, the external table adapter used `TableReader`'s fallback, which called `ExternalTableReader::Get()` separately for each key.
* `DB::OpenAndCompact()` now requires `output_directory` to be a simple name for a new subdirectory when the new `use_session_tmp_dir_for_remote_compaction` DB option is enabled. The option places that subdirectory under the canonical `<dbname>/session_tmp/` root, and `DB::Open()` deletes stale contents under the root after recovery. The disabled default preserves the existing `<dbname>/<output_directory>` layout without automatic cleanup. Previously, remote-compaction output directories were created directly under `<dbname>`, which made it challenging to safely identify and delete obsolete files.
* Several block-based SST table properties now better support compression benchmarking and optimization: `uncompressed_data_size` is now persisted in the file (previously it was computed only at write time and read back as 0); a new `lsm_info_at_creation` property records the LSM level and "bottommost" status at the file's creation, distinguishing "not applicable" (e.g. SstFileWriter files) from "unknown" (older files); and the `compression_options` property now records the full compression manager configuration instead of only its name.
### Bug Fixes
* Fixed a usually-minor performance bug where `Cache::ApplyToAllEntries()` used single-entry batches instead of the intended 256-entry default for `ApplyToAllEntriesOptions::average_entries_per_lock`. All of the Cache implementations (LRU and HCC) benefit from the fix, but the disfavored LRU cache was especially impacted by repeated mutex acquisition and release.
* Fixed stats-disabled coroutine and asynchronous reads inheriting enabled TLS statistics state when interleaved with stats-enabled reads on the same executor. Each read now uses its captured per-call configuration, while enabled request counters remain isolated across suspensions.
* Fixed a bug where a remote compaction (`CompactionService` / `DB::OpenAndCompact`) requested with `OpenAndCompactOptions::allow_resumption=true` but for which resumption was internally disabled (incompatible with `paranoid_file_checks` or output-iteration verification) could fail a retried attempt, because the output directory was not cleaned before starting fresh and output files left by a previously interrupted attempt collided with the reused file numbers.
* Fixed a bug where a remote compaction (`CompactionService` / `DB::OpenAndCompact`) could fail when the primary rotated its MANIFEST while the worker was opening the primary's directory read-only. The open reads `CURRENT`, and the primary publishes a new MANIFEST by renaming a freshly written `CURRENT` over the old one; on file systems without POSIX read-after-unlink semantics (remote/object stores) the replaced object is then reported as gone (`PathNotFound`/`NotFound`, or `TryAgain` for the MANIFEST equivalent) instead of remaining readable. `DB::OpenAndCompact()` now retries the secondary open a bounded, configurable number of times for those transient statuses, so a concurrent rotation no longer fails the compaction (which, for a `CompactionService` that does not fall back to a local compaction, became a background error on the primary).
* Fixed secondary DB `NewIterators()` returning iterators from different database states when `TryCatchUpWithPrimary()` runs concurrently.
* Fixed a bug where a `SeekToFirst()` on an iterator previously used for a `Seek()` could end the scan early and silently omit keys with `ReadOptions::async_io`, or otherwise run with readahead effectively disabled. Affects scans using `ReadOptions::prefix_same_as_start` together with `ReadOptions::auto_readahead_size` (enabled by default); memory-mapped reads are unaffected.
### Performance Improvements
* `DB::MultiGetEntityLazy()` now reads all keys of a batch under a single shared per-column-family SuperVersion pin, and a single `LazyWideColumnsBatch::MultiResolve()` coalesces the resulting blob reads across keys: one read per blob file (and one per SST for embedded blobs), for whole-column and byte-range reads alike, instead of reading each key's blobs separately.
## 11.10.0 (08/28/2026)
### New Features
* Added `BlockBasedTableOptions::optimize_key_common_prefix` as part of new `format_version=8`. When enabled, each data block and index block stores the common user-key prefix of its keys once instead of at every restart point, reducing block size and block-cache footprint for prefix-heavy data and speeding up Seek for bytewise-ordered keys.
* A new experimental feature spreads out periodic (time-based) compaction trigger times across a fleet of similarly-configured DBs to avoid recurring "thundering herd" triggers, especially with remote compaction. See the new experimental DB option `periodic_compaction_phase_recovery_percent`.
### Public API Changes
* `IngestExternalFileArg::atomic_replace_range` now supports a range that only partially overlaps existing files, instead of failing. Requires universal compaction and a table factory that supports range deletion; other configurations still fail on partial overlap. The range is also now specified as half-open (`start` inclusive, `limit` exclusive), resolving a previously documented ambiguity in the upper bound.
* Added an immutable `external_table_config` option that is persisted in OPTIONS files and passed to external table factories during bootstrap.
### Bug Fixes
* Fixed a liveness bug where RocksDB could remain indefinitely in recovery from a soft background error when concurrent writes continuously generated normal flush requests.
* Fixed a race in online error recovery where a concurrent higher-severity I/O error could leave a stale retry-flush plan active, prevent the required WAL switch, and create duplicate internal WAL history detected on DB reopen.
* Fix "bottommost files" infinite compaction loops (`kBottommostFiles`). This happens with the unsupported combination of leveled compaction and `preserve_internal_time_seconds` / `preclude_last_level_data_seconds`, and in an edge case with user-defined timestamps when `full_history_ts_low` is unset. The underlying logic is now more unified to reduce risk of a similar divergence in logic in the future.
* Fixed `reuse_manifest_on_open` to create a fresh MANIFEST instead of appending after recovery sees an incomplete atomic group at the MANIFEST tail.
* Starting remote compaction (`CompactionService` / `DB::OpenAndCompact`) is now more efficient and more reliable, by avoiding unnecessary file operations (latency) while using more accurate consistency / precondition checks, both avoiding some spurious failures and rejecting more legitimate inconsistencies. New DBOption `remote_compaction_manifest_floor` can be disabled in case of unexpected problems with the change.
* Fixed unbounded retention of recovered transaction state by a long-lived secondary opened with `allow_2pc`.
* Fixed stale reads on secondary instances, where entries replayed from a WAL that the primary had already flushed could shadow the newer values in the flushed files indefinitely. Also fixed a case where a secondary could stop returning such a key altogether while the primary's flushed file holding it was unreadable.
* Fixed subset checkpoints to discard tolerated MANIFEST tail corruption before appending column-family drop records.
### Performance Improvements
* Improved compaction CPU efficiency for column families using an `SstPartitioner` (such as the built-in `SstPartitionerFixedPrefix`): when the partitioner can express its next boundary as a key prefix via the new `SstPartitioner::ShouldPartitionByPrefix()` API, compaction now checks that cached prefix inline instead of copying every user key and making a virtual `ShouldPartition()` call for each key-value written to an SST.
## 11.9.0 (08/14/2026)
### New Features
* Added an integrated and improved compression auto-skip feature (new CompressionOptions, still experimental) that saves CPU by skipping compression when and where the overall ratio is inadequate. This improves on the previous auto-skip compression manager by (a) taking achieved compression ratios into account, (b) using a continuously evolving payoff estimate, and (c) combining natively with parallel compression.
* Added the `blob_file_writable_file_max_buffer_size` mutable column family option. When set to a non-zero value, blob-file writers use it as their `WritableFileWriter` max buffer size instead of inheriting `DBOptions::writable_file_max_buffer_size`.
* Added an experimental lazy wide-column read API, `DB::GetEntityLazy()` / `DB::MultiGetEntityLazy()` (see `include/rocksdb/lazy_wide_columns.h`). The returned `LazyWideColumns` leaves blob-backed columns as unresolved references and, like an iterator, stays valid after the call returns, so the caller resolves only the columns -- and only the byte ranges -- it needs; a byte-range read of an uncompressed blob (separate-file or embedded) reads just those bytes from storage. Adds `Env::IOActivity::kLazyResolve` and `rocksdb.blobdb.lazy.*` statistics. Requires `max_open_files == -1`.
* Added DB property `rocksdb.num-running-remote-compactions`, reporting the number of compaction service jobs currently waiting in `CompactionService::Wait()`.
* Added DB property `rocksdb.num-unscheduled-compactions`, reporting compactions waiting in the DB's internal compaction queue but not yet assigned to a background job.
* Added `DB::AbortCompactions()` and `DB::ResumeCompactions()` for per-column-family compaction abort and resume, with the same active-abort semantics as the DB-wide `AbortAllCompactions()`/`ResumeAllCompactions()` but scoped to a single column family. Calls are reference-counted independently per column family and compose with the DB-wide abort.
### Public API Changes
* Add IndexFactory compatibility names for the existing user-defined index API.
* Added the experimental `CoroDB` capability, exposed by `DB::GetCoroDB()`, with lazy native coroutine variants of `Get` and `MultiGet`. The existing `DB::GetAsync()` and `DB::MultiGetAsync()` APIs dispatch through it.
* Added `rocksdb_batched_multi_get_pinned_cf()` to return batch-owned pinned MultiGet values and per-key errors without allocating one wrapper per found value.
* Add `DBOptions::read_io_executor_threads` to increase the maximum thread count of the shared filesystem read I/O executor used for asynchronous reads when opening a DB. Opening a DB never reduces the shared executor thread count. Change the experimental `FSRandomAccessFile::SubmitReadAsync()` API to return whether the asynchronous path was used, and add the `FILE_SUBMIT_ASYNC_READ_FALLBACK` ticker to count calls that fall back to synchronous reads.
* `ReadOptions::table_filter` is now a *pointer* to a caller-owned `std::function` instead of a `std::function` value, to improve efficiency in copying and destroying `ReadOptions` objects (which RocksDB does internally). This is a breaking change for users of this relatively obscure option, who will now need to declare a `std::function` that outlives the read. As a side effect of `ReadOptions` being trivially copyable, new but legitimate compiler warnings may surface.
* Removed the experimental `CreateAutoSkipCompressionManager()`. Auto-skip compression is now a first-class feature of the block-based table builder, controlled by the new experimental `CompressionOptions::auto_skip`.
* Added `CoroStackableDB` for implementing wrappers over native coroutine reads and used it to add coroutine `Get` and `MultiGet` support to `DBWithTTL` and WritePrepared `TransactionDB`. A stackable wrapper advertises coroutine support only when its wrapped DB supports it; TTL value decoding and transaction visibility semantics are preserved.
* Added a `kSstFileWriter` value to `TableFileCreationReason`, which `SstFileWriter` now reports instead of the catch-all `kMisc`. This, along with new `db_name` and `is_remote_compaction` fields in `FilterBuildingContext`, improves the contextual information available to custom filter and compression strategies.
* Renamed `TransactionLogIterator` to `WalIterator` (and `include/rocksdb/transaction_log.h` to `include/rocksdb/wal_iterator.h`), because it iterates the write-ahead log and is unrelated to `TransactionDB`. The old names still work. `DB::GetUpdatesSince()` is unchanged. In Java, `WalIterator` is the new name and `TransactionLogIterator` remains as a subclass. Also documented existing behavior that was previously unstated: only writes that reached the WAL are visible (so `WriteOptions::disableWAL` and `IngestExternalFile()` leave holes that end iteration), what each iterator state means, and that `GetUpdatesSince()` silently starts later when the requested sequence number is unavailable.
### Bug Fixes
* Fixed a bug where `BlobFileBuilder` passed an SST path to `OnBlobFileCompleted()` instead of the completed blob path during BlobDB garbage-collection compactions, causing incorrect `SstFileManager` accounting and compaction failures on filesystems where the SST was not yet pathname-visible.
### Performance Improvements
* When `DBOptions::avoid_unnecessary_blocking_io` is true, obsolete `OPTIONS-*` files found during DB open are deleted by background purge instead of synchronously on the opening thread.
## 11.8.0 (07/28/2026)
### Public API Changes
* Add callback-based asynchronous read APIs, `DB::GetAsync()` and `DB::MultiGetAsync()`. The idea is that when IO is required, RocksDB can suspend its internal coroutine read path, allowing the read executor thread to do other work. When the IO is complete, RocksDB invokes the user callback. This requires filesystem support for full performance benefits. A new filesystem API `FSRandomAccessFile::SubmitReadAsync()` is introduced for this. Unlike `ReadAsync`, the filesystem is responsible for eventually calling the callback. `FileSystem::GetReadExecutor()` returns the IO executor whose EventBases run coroutine read processing. In the Posix filesystem, an IO-uring based event loop is used to complete the IO.
* Added `FlushOptions::listener_wait` (default `false`). When set together with `FlushOptions::wait == true`, `DB::Flush()` will not return until the registered `EventListener::OnFlushCompleted` callbacks for the flushed memtables have finished running. By default (`false`), `Flush(wait=true)` may return as soon as the flush result is committed, which can be before (or while) the `OnFlushCompleted` callbacks execute on the background flush thread. Also added the corresponding C API `rocksdb_flushoptions_set_listener_wait()` / `rocksdb_flushoptions_get_listener_wait()`.
* Deprecated `PinnableWideColumns::serialized_size()` in favor of the new `PinnableWideColumns::payload_size()`, which returns the total size of the columns' names and values (for a plain value this equals the value size). Relatedly, wide-column read accounting changed subtly: read statistics for entities (`BYTES_READ`, `BYTES_PER_READ`, and the per-key byte counts for `GetEntity`/`MultiGetEntity`) and the `ReadOptions::value_size_soft_limit` threshold now measure entities by this payload size instead of the serialized entity size. This drops serialization framing bytes from those measurements (so reported/limited sizes for entity reads are slightly smaller than before) and makes plain-value and wide-column accounting consistent.
### Behavior Changes
* `ReadOptions::value_size_soft_limit` now also bounds the blob values resolved from pre-flush blob direct write references during `MultiGet`/`MultiGetEntity` (previously these were resolved after the limit check and read without bound). Enforcement is "always make progress": at least one key is read even if its value alone exceeds the limit, and only once the returned size exceeds the limit do subsequent keys get `Status::Aborted` -- so a caller retrying the aborted keys cannot loop forever on a single value that by itself exceeds the limit.
### Performance Improvements
* Reduced copying when reading wide-column entities that have blob-referenced columns, whether stored in separate blob files or embedded (same-file) as blob records in the same SST. On the point-lookup (`GetEntity`/`MultiGetEntity`) and blob-backed memtable/direct-write read paths, resolving these columns no longer re-serializes the whole entity into a fresh buffer: inline columns are referenced in place in the pinned entity and each resolved blob value is referenced in place from its fetched buffer. This is enabled by an internal change to `PinnableWideColumns` that lets its columns reference multiple backing buffers.
## 11.7.0 (07/16/2026)
### Public API Changes
* Added `RangeLockManagerHandle::SetIsKilledCallback()`, a `std::function<bool()>` predicate polled during range-lock waits. When it returns true, a blocked `GetRangeLock()` wait returns promptly with `Status::Aborted()` instead of waiting for the lock timeout. Range-lock users that install no callback keep the original wait-until-grant-or-timeout behavior.
* Added `MultiScanArgs::reverse` to support reverse MultiScan reads over bounded scan ranges.
### Behavior Changes
* When using `LockWAL()`, live-file capture APIs such as checkpoint and backup now flush WAL-disabled unpersisted data; they can block until `UnlockWAL()` or return `Aborted` when called from the same thread that holds `LockWAL()` to avoid deadlock. `UnlockWAL()` must now also be called from the same thread that called `LockWAL()`.
### Performance Improvements
* Fixed slow DB open with `best_efforts_recovery`, and slow catch-up of secondary and follower (read-only) instances, in which time and memory grew quadratically with the number of files in the DB (so the slowdown was worst for large DBs with long history).
## 11.6.0 (07/02/2026)
### New Features
* Added EXPERIMENTAL embedded blob SST support through `SstFileWriter::OpenWithEmbeddedBlobs()`, storing eligible large values as same-file blob records in block-based SST files and resolving them transparently for reads. This niche feature currently supports uncompressed embedded blobs only; compression options are placeholders and compression support is deferred to follow-up work.
### Public API Changes
* Expanded the C API (`include/rocksdb/c.h`) with a large set of new `rocksdb_*` functions, mostly option getters/setters plus table-properties, job/event-listener, and metadata accessors, a WAL filter, a ReadOptions table filter, and a backup exclude-files callback. Many are now produced by a new semi-automated generator (`tools/c_api_gen/`) from the C++ headers; `include/rocksdb/c.h` remains a single self-contained header and the signatures of pre-existing functions are unchanged.
### Bug Fixes
* Reverted PR14831 that made range_lock_manager aware of reverse-order CF
* Fixed a bug in `RandomAccessFileReader::ReadAsync` where an already-aligned direct-IO read request with a null `scratch` and a caller-provided `aligned_buf` would take the "already aligned" fast path and submit the null buffer to the underlying async read (e.g. a null iovec base to io_uring, failing with EFAULT). This could surface as spurious iterator failures during async prefetch (MultiScan with async IO) on direct-IO databases. The async path now allocates a backing buffer in this case, matching the synchronous `Read` path.
* Fixed a bug where closing a read-only DB instance could delete live SST files created by a concurrent read-write DB sharing the same directory.
## 11.5.0 (06/16/2026)
### New Features
* External table readers that open files through `ExternalTableOptions::fs` now update RocksDB SST/file-read statistics and file IO listener callbacks, making external file IO activity visible in existing read metrics.
* Added `DB::PrepareFileIngestion()`, a two-phase form of `IngestExternalFile`/`IngestExternalFiles`. `PrepareFileIngestion()` performs all of the work that does not require the DB mutex (validating the arguments, reading each external file's metadata, and linking/copying the files into the DB) and returns an opaque `FileIngestionHandle`. `DB::CommitFileIngestionHandle()` (or `DB::CommitFileIngestionHandles()` for several handles at once) then makes the prepared files visible; multiple handles are committed atomically in a single MANIFEST write, or none are. `FileIngestionHandle::Abort()` (or simply destroying the handle) cancels a prepared ingestion and rolls it back. This gives flexibility to the application to Prepare a file separately from when its committed, and may shorten an applications critical path on `IngestExternalFile`.
* Added two stats histograms reporting the per-call latency (in microseconds) of each successful `IngestExternalFile`/`IngestExternalFiles` call, split by phase: `rocksdb.ingest.external.file.prepare.micros` (argument validation and reading/validating/linking the external files, which does not block live writes) and `rocksdb.ingest.external.file.run.micros` (the ingestion performed under the DB mutex while live writes are blocked). Failed ingestions are not recorded.
* Added `DBOptions::use_direct_io_for_compaction_reads` (default false). When enabled, compaction-input SST reads use `O_DIRECT` while user reads remain buffered, avoiding page-cache eviction of hot user-read data by sequential compaction scans. Pair with `use_direct_io_for_flush_and_compaction = true` on write-heavy workloads for direct I/O on both compaction inputs and outputs. Rejected at Open when combined with `allow_mmap_reads = true`. No-op when `use_direct_reads = true` is already set (since all reads are already direct).
### Public API Changes
* `DB::GetPreparedFileInfoForExternalSstIngestion()` can now prepare metadata for a live DB-generated SST file so it can be passed to `IngestExternalFileArg::file_infos`. The input path must exactly match a live table file owned by the source DB, and the returned metadata handle must outlive the ingestion.
* `ExternalSstFileInfo` gained a `prepared_file_info` member produced by `SstFileWriter::Finish`, and `IngestExternalFileArg` gained a `file_infos` field: a vector of borrowed `const PreparedFileInfo*` pointers, parallel to `external_files`. The owning `ExternalSstFileInfo::prepared_file_info` handle must outlive the ingestion. When set, `IngestExternalFiles()` reuses the supplied per-file metadata instead of re-opening and scanning each file to recompute it, avoiding that extra I/O.
* Corrected the public C++ option spelling from `memtable_veirfy_per_key_checksum_on_seek` to `memtable_verify_per_key_checksum_on_seek`. RocksDB continues to accept the old misspelled OPTIONS-file key for compatibility, while newly serialized OPTIONS files use the corrected key.
* Added experimental read-scoped block buffer provider API, configured through `ReadOptions::read_scoped_block_buffer_provider`, for supported block-based table iterator scans and MultiScan reads to use caller-provided read-scoped storage for final data-block contents. When configured, supported provider-backed scan data-block reads bypass the data-block cache. The provider is ignored when mmap reads are enabled. RocksDB may still use ordinary temporary scratch for serialized block bytes, such as when a block may be compressed. Get and MultiGet do not currently provide API guarantees for this provider.
* Added `FileSystem::SyncFile()` and `Env::SyncFile()` public APIs for syncing or fsyncing a file by name without requiring callers to reopen it as writable.
### Behavior Changes
* The default `compression` for column families is now `kLZ4Compression` rather than `kSnappyCompression`. This affects only column families that do not explicitly set `compression`, and only newly-written SST files. The change is fully compatible: existing data remains readable with no migration needed, as RocksDB selects the decompressor per block. LZ4 offers slightly better compression ratios and decompression CPU efficiency vs. Snappy and similar compression CPU, tested across various server CPUs. When support is not compiled in, the fallback path for default compression is LZ4 -> Snappy -> NoCompression.
* Disable parallel compression (ignore `CompressionOptions::parallel_threads`) for fast built-in compressors: Snappy, LZ4 (accelerated, not LZ4HC), and accelerated levels of ZSTD (level < 0). These do not generally benefit from parallel compression. This behavior can be overridden with a custom Compressor from a custom CompressionManager.
* `PosixFileSystem::OptimizeForCompactionTableRead` now delegates to the base `FileSystem::OptimizeForCompactionTableRead` implementation before applying its Linux-only compaction-readahead clamp (added for #12038). The returned `FileOptions::use_direct_reads` therefore reflects `DBOptions::use_direct_reads`, consistent with the base class and with other `FileSystem` implementations. Previously the override ignored the base implementation and keyed both the returned options and the Linux readahead clamp off the incoming `FileOptions` rather than `DBOptions`. At the in-tree call site the incoming `FileOptions::use_direct_reads` already matches `DBOptions::use_direct_reads`, so this is effectively a no-op for existing configurations; it removes a latent inconsistency where a caller passing `FileOptions` whose `use_direct_reads` disagreed with `DBOptions` would have had the global flag silently ignored for compaction-input reads on Linux. (Note: the `#12038` readahead clamp still keys off the global `use_direct_reads`; it is not skipped for the compaction-only `use_direct_io_for_compaction_reads` path, since that flag enables O_DIRECT after this hook runs.)
* Brought more unity to `kLZ4Compression` and `kLZ4HCCompression` by giving each access to the other's compression levels. Negative (fast) values previously only available to LZ4 and positive (slow) values previously only available to LZ4HC are now available to both, so configuring a non-default `compression_opts.level` now selects the LZ4 compressor variant. This is a behavior change for previously tolerated but dubious configurations such as positive compression level with `kLZ4Compression`. `kLZ4Compression` and `kLZ4HCCompression` keep their effective default levels, equivalent to -1 and 9 respectively.
* Removed a discontinuity in `kZSTD` compression levels at `compression_opts.level == 0` by mapping that setting to `level = -1` rather than to `level = 3` as the ZSTD library does internally. This improves the compression auto-tuning landscape.
### Bug Fixes
* Fixed a bug where the range lock manager would crash with an assertion failure when using a reverse comparator column family.
### Performance Improvements
* Reduced commit latency when ingesting many external files by allowing `IngestExternalFileOptions::file_opening_threads` to open table readers for committed ingested files using multiple threads.
* Reduced commit latency for large external file ingestions into the last level by adding `IngestExternalFileOptions::prefetch_lmax_index_and_filter_blocks`, which can skip commit-time index and filter block prefetching for cache-backed table metadata.
## 11.4.0 (06/02/2026)
### Public API Changes
* Added `rocksdb_options_set_memtable_batch_lookup_optimization()` and `rocksdb_options_get_memtable_batch_lookup_optimization()` to the C API, exposing the existing `AdvancedColumnFamilyOptions::memtable_batch_lookup_optimization` field. This allows C API users (and downstream language bindings) to enable the skip-list memtable's batch-lookup optimization for `MultiGet`, which caches the search path between consecutive keys and reduces per-key cost from O(log N) to O(log d) where d is the distance between consecutive keys.
* Added `rocksdb_readoptions_set_optimize_multiget_for_io()` and `rocksdb_readoptions_get_optimize_multiget_for_io()` to the C API, exposing the existing `ReadOptions::optimize_multiget_for_io` field. This allows C API users (and downstream language bindings) to opt out of the multi-level parallel MultiGet path, which only takes effect when the library is built with `USE_COROUTINES`.
* Added `rocksdb_block_based_table_index_block_search_type_auto` enum constant and the `rocksdb_block_based_options_set_uniform_cv_threshold()` setter to the C API. The constant exposes `BlockSearchType::kAuto`, and the setter exposes `BlockBasedTableOptions::uniform_cv_threshold`. Both are required for `kAuto` index-block search to take effect from the C API: without setting `uniform_cv_threshold >= 0`, the per-block `is_uniform` footer bit is never written, and `kAuto` falls back to binary search at read time.
* Added `EventListener::OnDBShutdownBegin`, a callback that fires once when a DB begins shutdown, including when RocksDB cleans up a failed `DB::Open()` attempt.
* Added a new PerfContext counter `blob_cache_read_byte` for blob cache bytes read.
### Behavior Changes
* Remote compaction now falls back to local compaction when the primary cannot deserialize a successful `CompactionServiceResult` before installing any remote output files.
* StringToMap (rocksdb/convenience.h) now preserves the outer braces of nested values so each map entry is in self-contained form and can be embedded directly into another `key=value;` string (e.g. SetOptions) without losing inner ';' delimiters. A new symmetric MapToString utility is provided.
### Bug Fixes
* Fixed a bug where `AbortAllCompactions()` could leave automatic compaction work unscheduled when aborting before the background worker picked it, causing compaction and write stalls until DB restart.
* Fix a bug where db had a false positive compaction corruption error due to remote compaction service result with `has_accurate_num_input_records=false` not being serialized.
* Fixed WritePrepared TransactionDB cleanup after retryable commit or rollback write failures so prepared transactions remain rollbackable instead of leaving unresolved prepared state.
* Fix a rare corruption bug for tiered compaction that incorrectly moved last level range tombstones into proximal level. This corruption error is surfaced when force_consistency_checks is enabled.
### Performance Improvements
* Reduce the likelihood of user-facing metadata operations blocking on MANIFEST rotation when file creation is slow, such as on remote storage. MANIFEST write batches containing foreground edits from `CreateColumnFamily()`, `DropColumnFamily()`, `CreateColumnFamilyWithImport()`, `IngestExternalFile()`, and `DeleteFilesInRanges()` now get a relaxed file size threshold for triggering MANIFEST rotation; background-only MANIFEST write batches, such as compaction and flush, continue to use the normal threshold. This change should make auto-tuning manifest file size more attractive (see `max_manifest_file_size` and `max_manifest_space_amp_pct` options).
## 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
* Remove remaining pieces of Lua integration
### Behavior Changes
* The new default for `BlockBasedTableOptions::format_version` is 7, which has been supported since RocksDB 10.4.0 and is required in order to use CompressionManagers supporting custom compression types.
### Bug Fixes
* Fixed a small performance bug with `format_version=7` when decompressing formats other than Snappy and ZSTD.
* Fixed an infinite compaction loop bug with User-Defined Timestamps (UDT) where bottommost files were repeatedly marked for compaction even though their timestamp could not be collapsed.
* Bugfix for persisted UDT record sequence number zeroing logic.
## 10.10.0 (12/16/2025)
### Bug Fixes
* Fixed a bug in best-efforts recovery that causes use-after-free crashes when accessing SST files that were cached during the recovery.
* Fix resumable compaction incorrectly allowing resumption from a truncated range deletion that is not well handled currently.
* Fixed a bug in `PosixRandomFileAccess` IO uring submission queue ownership & management. Fix eliminates the false positive 'Bad cqe data' IO errors in `PosixRandomFileAccess::MultiRead` when interleaved with `PosixRandomFileAccess::ReadAsync` on the same thread.
## 10.9.0 (11/21/2025)
### New Features
* Added an auto-tuning feature for DB manifest file size that also (by default) improves the safety of existing configurations in case `max_manifest_file_size` is repeatedly exceeded. The new recommendation is to set `max_manifest_file_size` to something small like 1MB and tune `max_manifest_space_amp_pct` as needed to balance write amp and space amp in the manifest. Refer to comments on those options in `DBOptions` for details. Both options are (now) mutable.
* Added a new API to support option migration for multiple column families
* Added new option target_file_size_is_upper_bound that makes most compaction output SST files come close to the target file size without exceeding it, rather than commonly exceeding it by some fraction (current behavior). For now the new behavior is off by default, but we expect to enable it by default in the future.
* Add a new option allow_trivial_move in CompactionOptions to allow CompactFiles to perform trivial move if possible. By default the flag of allow_trivial_move is false, so it preserve the original behavior.
### Public API Changes
* To reduce risk of ODR violations or similar, `ROCKSDB_USING_THREAD_STATUS` has been removed from public headers and replaced with static `const bool ThreadStatus::kEnabled`. Some other uses of conditional compilation have been removed from public API headers to reduce risk of ODR violations or other issues.
### Behavior Changes
* PosixWritableFile now repositions the seek pointer to the new end of file after a call to Truncate.
* Updated standalone range deletion L0 file compaction behavior to avoid compacting with any newer L0 files (which is expensive and not useful).
### Bug Fixes
* Fix a bug where compaction with range deletion can persist kTypeMaxValid in MANIFEST as file metadata. kTypeMaxValid is not supposed to be persisted and can change as new value types are introduced. This can cause a forward compatibility issue where older versions of RocksDB don't recognize kTypeMaxValid from newer versions. A new placeholder value type kTypeTruncatedRangeDeletionSentinel is also introduced to replace kTypeMaxValid when reading existing SST files' metadata from MANIFEST. This allows us to strengthen some checks to avoid using kTypeMaxValid in the future.
* Fixed a bug where `DB::GetSortedWalFiles()` could hang when waiting for a purge operation that found nothing to do (potentially triggered by iterator release, flush, compaction, etc.).
* Fixed a bug in MultiScan where `max_sequential_skip_in_iterations` could cause the iterator to seek backward to already-unpinned blocks when the same user key spans multiple data blocks, leading to assertion failures or seg fault.
* Fixed a bug for `WAL_ttl_seconds > 0` use cases where the newest archived WAL files could be incorrectly deleted when the system clock moved backwards.
### Performance Improvements
* Added optimization that allowed for the asynchronous prefetching of all data outlined in a multiscan iterator. This optimization was applied to the level iterator, which prefetches all data through each of the block-based iterators.
## 10.8.0 (10/21/2025)
### New Features
* Add kFSPrefetch to FSSupportedOps enum to allow file systems to indicate prefetch support capability, avoiding unnecessary prefetch system calls on file systems that don't support them.
* Added experimental support `OpenAndCompactOptions::allow_resumption` for resumable compaction that persists progress during `OpenAndCompact()`, allowing interrupted compactions to resume from the last progress persitence. The default behavior is to not persist progress.
### Public API Changes
* Allow specifying output temperature in CompactionOptions
* Added `DB::FlushWAL(const FlushWALOptions&)` as an alternative to `DB::FlushWAL(bool sync)`, where `FlushWALOptions` includes a new `rate_limiter_priority` field (default `Env::IO_TOTAL`) that allows rate limiting and priority passing of manual WAL flush's IO operations.
* The MultiScan API contract is updated. After a multi scan range got prepared with Prepare API call, the following seeks must seek the start of each prepared scan range in order. In addition, when limit is set, upper bound must be set to the same value of limit before each seek
### Behavior Changes
* `kChangeTemperature` FIFO compaction will now honor `compaction_target_temp` to all levels regardless of `cf_options::last_level_temperature`
* Allow UDIs with a non BytewiseComparator
### Bug Fixes
* Fix incorrect MultiScan seek error status due to bugs in handling range limit falling between adjacent SST files key range.
* Fix a bug in Page unpinning in MultiScan
### Performance Improvements
* Fixed a performance regression in LZ4 compression that started in version 10.6.0
## 10.7.0 (09/19/2025)
### New Features
* Add the fail_if_no_udi_on_open flag in BlockBasedTableOption to control whether a missing user defined index block in a SST is a hard error or not.
* A new flag memtable_verify_per_key_checksum_on_seek is added to AdvancedColumnFamilyOptions. When it is enabled, it will validate key checksum along the binary search path on skiplist based memtable during seek operation.
* Introduce option MultiScanArgs::use_async_io to enable asynchronous I/O during MultiScan, instead of waiting for I/O to be done in Prepare().
* Add new option `MultiScanArgs::max_prefetch_size` that limits the memory usage of per file pinning of prefetched blocks.
* Improved `sst_dump` by allowing standalone file and directory arguments without `--file=`. Also added new options and better output for `sst_dump --command=recompress`. See `sst_dump --help`
### Public API Changes
* HyperClockCache with no `estimated_entry_charge` is now production-ready and is the preferred block cache implementation vs. LRUCache. Please consider updating your code to minimize the risk of hitting performance bottlenecks or anomalies from LRUCache. See cache.h for more detail.
* RocksDB now requires a C++20 compatible compiler (GCC >= 11, Clang >= 10, Visual Studio >= 2019), including for any code using RocksDB headers.
* MultiScanArgs used to have a default constructor with default parameter of BytewiseComparator. Now it always requires Comparator in its constructor.
### Behavior Changes
* The default provided block cache implementation is now HyperClockCache instead of LRUCache, when `block_cache` is nullptr (default) and `no_block_cache==false` (default). We recommend explicitly creating a HyperClockCache block cache based on memory budget and sharing it across all column families and even DB instances. This change could expose previously hidden memory or resource leaks.
### Bug Fixes
* Reported numbers for compaction and flush CPU usage now include time spent by parallel compression worker threads. This now means compaction/flush CPU usage could exceed the wall clock time.
* Fix a race condition in FIFO size-based compaction where concurrent threads could select the same non-L0 file, causing assertion failures in debug builds or "Cannot delete table file from LSM tree" errors in release builds.
* Fix a bug in RocksDB MultiScan with UDI when one of the scan ranges is determined to be empty by the UDI, which causes incorrect results.
### Performance Improvements
* Add a new table property "rocksdb.key.smallest.seqno" which records the smallest sequence number of all keys in file. It makes ingesting DB generated files faster by
avoiding scanning the whole file to find the smallest sequence number.
* Add a new experimental PerKeyPointLockManager to improve efficiency under high lock contention. PointLockManager was not efficient when there is high write contention on same key, as it uses a single conditional variable per lock stripe. PerKeyPointLockManager uses per thread conditional variable supporting fifo order. Although this is an experimental feature. By default, it is disabled. A new boolean flag TransactionDBOptions::use_per_key_point_lock_mgr is added to optionally enable it. Search the flag in code for more info.
Together, a new configuration TransactionOptions::deadlock_timeout_us is added, which allows the transaction to wait for a short period before perform deadlock detection. When the workload has low lock contention, the deadlock_timeout_us can be configured to be slightly higher than average transaction execution time, so that transaction would likely be able to take the lock before deadlock detection is performed when it is waiting for a lock. This allows transaction to reduce CPU cost on performing deadlock detection, which could be expensive in CPU time. When the workload has high lock contention, the deadlock_timeout_us can be configured to 0, so that transaction would perform deadlock detection immediately. By default the value is 0 to keep the behavior same as before.
* Majorly improved CPU efficiency and scalability of parallel compression (`CompressionOptions::parallel_threads` > 1), though this efficiency improvement makes parallel compression currently incompatible with UserDefinedIndex and with old setting of `decouple_partitioned_filters=false`. Parallel compression is now considered a production-ready feature. Maximum performance is available with `-DROCKSDB_USE_STD_SEMAPHORES` at compile time, but this is not currently recommended because of reported bugs in implementations of `std::counting_semaphore`/`binary_semaphore`.
## 10.6.0 (08/22/2025)
### New Features
* Introduce column family option `cf_allow_ingest_behind`. This option aims to replace `DBOptions::allow_ingest_behind` to enable ingest behind at the per-CF level. `DBOptions::allow_ingest_behind` is deprecated.
* Introduce `MultiScanArgs::io_coalesce_threshold` to allow a configurable IO coalescing threshold.
### Public API Changes
* `IngestExternalFileOptions::allow_db_generated_files` now allows files ingestion of any DB generated SST file, instead of only the ones with all keys having sequence number 0.
* `decouple_partitioned_filters = true` is now the default in BlockBasedTableOptions.
* GetTtl() API is now available in TTL DB
* Minimum supported version of LZ4 library is now 1.7.0 (r129 from 2015)
* Some changes to experimental Compressor and CompressionManager APIs
* A new Filesystem::SyncFile function is added for syncing a file that was already written, such as on file ingestion. The default implementation matches previous RocksDB behavior: re-open the file for read-write, sync it, and close it. We recommend overriding for FileSystems that do not require syncing for crash recovery or do not handle (well) re-opening for writes.
### Behavior Changes
* When `allow_ingest_behind` is enabled, compaction will no longer drop tombstones based on the absence of underlying data. Tombstones will be preserved to apply to ingested files.
### Bug Fixes
* Files in dropped column family won't be returned to the caller upon successful, offline MANIFEST iteration in `GetFileChecksumsFromCurrentManifest`.
* Fix a bug in MultiScan that causes it to fall back to a normal scan when dictionary compression is enabled.
* Fix a crash in iterator Prepare() when fill_cache=false
* Fix a bug in MultiScan where incorrect results can be returned when a Scan's range is across multiple files.
* Fixed a bug in remote compaction that may mistakenly delete live SST file(s) during the cleanup phase when no keys survive the compaction (all expired)
* Allow a user defined index to be configured from a string.
* Make the User Defined Index interface consistently use the user key format, fixing the previous mixed usage of internal and user key.
### Performance Improvements
* Small improvement to CPU efficiency of compression using built-in algorithms, and a dramatic efficiency improvement for LZ4HC, based on reusing data structures between invocations.
## 10.5.0 (07/18/2025)
### Public API Changes
* DB option skip_checking_sst_file_sizes_on_db_open is deprecated, in favor of validating file size in parallel in a thread pool, when db is opened. When DB is opened, with paranoid check enabled, a file with the wrong size would fail the DB open. With paranoid check disabled, the DB open would succeed, the column family with the corrupted file would not be read or write, while the other healthy column families could be read and write normally. When max_open_files option is not set to -1, only a subset of the files will be opened and checked. The rest of the files will be opened and checked when they are accessed.
### Behavior Changes
* PessimisticTransaction::GetWaitingTxns now returns waiting transaction information even if the current transaction has timed out. This allows the information to be surfaced to users for debugging purposes once it is known that the timeout has occurred.
* A new API GetFileSize is added to FSRandomAccessFile interface class. It uses fstat vs stat on the posix implementation which is more efficient. Caller could use it to get file size faster. This function might be required in the future for FileSystem implementation outside of the RocksDB code base.
* RocksDB now triggers eligible compactions every 12 hours when periodic compaction is configured. This solves a limitation of the compaction trigger mechanism, which would only trigger compaction after specific events like flush, compaction, or SetOptions.
### Bug Fixes
* Fix a bug in BackupEngine that can crash backup due to a null FSWritableFile passed to WritableFileWriter.
* Fix DB::NewMultiScan iterator to respect the scan upper bound specified in ScanOptions
### Performance Improvements
* Optimized MultiScan using BlockBasedTable to coalesce I/Os and prefetch all data blocks.
## 10.4.0 (06/20/2025)
### New Features
* Add a new CF option `memtable_avg_op_scan_flush_trigger` that supports triggering memtable flush when an iterator scans through an expensive range of keys, with the average number of skipped keys from the active memtable exceeding the threshold.
* Vector based memtable now supports concurrent writers (DBOptions::allow_concurrent_memtable_write) #13675.
* Add new experimental `TransactionOptions::large_txn_commit_optimize_byte_threshold` to enable optimizations for large transaction commit by transaction batch data size.
* Add a new option `CompactionOptionsUniversal::reduce_file_locking` and if it's true, auto universal compaction picking will adjust to minimize locking of input files when bottom priority compactions are waiting to run. This can increase the likelihood of existing L0s being selected for compaction, thereby improving write stall and reducing read regression.
* Add new `format_version=7` to aid experimental support of custom compression algorithms with CompressionManager and block-based table. This format version includes changing the format of `TableProperties::compression_name`.
### Public API Changes
* Change NewExternalTableFactory to return a unique_ptr instead of shared_ptr.
* Add an optional min file size requirement for deletion triggered compaction. It can be specified when creating `CompactOnDeletionCollectorFactory`.
### Behavior Changes
* `TransactionOptions::large_txn_commit_optimize_threshold` now has default value 0 for disabled. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` now has no effect on transactions.
### Bug Fixes
* Fix a bug where CreateColumnFamilyWithImport() could miss the SST file for the memtable flush it triggered. The exported CF then may not contain the updates in the memtable when CreateColumnFamilyWithImport() is called.
* Fix iterator operations returning NotImplemented status if disallow_memtable_writes and paranoid_memory_checks CF options are both set.
* Fixed handling of file checksums in IngestExternalFile() to allow providing checksums using recognized but not necessarily the DB's preferred checksum function, to ease migration between checksum functions.
## 10.3.0 (05/17/2025)
### New Features
* Add new experimental `CompactionOptionsFIFO::allow_trivial_copy_when_change_temperature` along with `CompactionOptionsFIFO::trivial_copy_buffer_size` to allow optimizing FIFO compactions with tiering when kChangeTemperature to move files from source tier FileSystem to another tier FileSystem via trivial and direct copying raw sst file instead of reading thru the content of the SST file then rebuilding the table files.
* Add a new field to Compaction Stats in LOG files for the pre-compression size written to each level.
* Add new experimental `TransactionOptions::large_txn_commit_optimize_threshold` to enable optimizations for large transaction commit with per transaction threshold. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` is deprecated in favor of this transaction option.
* [internal team use only] Allow an application-defined `request_id` to be passed to RocksDB and propagated to the filesystem via IODebugContext
### Bug Fixes
* Fix a bug where transaction lock upgrade can incorrectly fail with a Deadlock status. This happens when a transaction has a non-zero timeout and tries to upgrade a shared lock that is also held by another transaction.
* Pass wrapped WritableFileWriter pointer to ExternalTableBuilder so that the file checksum can be correctly calculated and returned by SstFileWriter for external table files.
* Fix an infinite-loop bug in transaction locking. This can happen if a transaction reaches lock limit and its time out expires before it attempts to wait for it.
* Fixed a potential data race with `CompressionOptions::parallel_threads > 1` and a `TablePropertiesCollector` overriding `BlockAdd()`.
## 10.2.0 (04/21/2025)
### New Features
* Provide histogram stats `COMPACTION_PREFETCH_BYTES` to measure number of bytes for RocksDB's prefetching (as opposed to file
system's prefetch) on SST file during compaction read
* A new API DB::GetNewestUserDefinedTimestamp is added to return the newest user defined timestamp seen in a column family
* Introduce API `IngestWriteBatchWithIndex()` for ingesting updates into DB while bypassing memtable writes. This improves performance when writing a large write batch to the DB.
* Add a new CF option `memtable_op_scan_flush_trigger` that triggers a flush of the memtable if an iterator's Seek()/Next() scans over a certain number of invisible entries from the memtable.
### Public API Changes
* AdvancedColumnFamilyOptions.max_write_buffer_number_to_maintain is deleted. It's deprecated since introduction of a better option max_write_buffer_size_to_maintain since RocksDB 6.5.0.
* Added arbitrary string map for additional options to be overridden for remote compactions
* The fail_if_options_file_error option in DBOptions has been removed. The behavior now is to always return failure in any API that fails to persist the OPTIONS file.
### Behavior Changes
* Make stats `PREFETCH_BYTES_USEFUL`, `PREFETCH_HITS`, `PREFETCH_BYTES` only account for prefetching during user initiated scan
### Bug Fixes
* Fix a bug in Posix file system that the FSWritableFile created via `FileSystem::ReopenWritableFile` internally does not track the correct file size.
* Fix a bug where tail size of remote compaction output is not persisted in primary db's manifest
## 10.1.0 (03/24/2025)
### New Features
* Added a new `DBOptions.calculate_sst_write_lifetime_hint_set` setting that allows to customize which compaction styles SST write lifetime hint calculation is allowed on. Today RocksDB supports only two modes `kCompactionStyleLevel` and `kCompactionStyleUniversal`.
@@ -141,7 +617,7 @@
* In FIFO compaction, compactions for changing file temperature (configured by option `file_temperature_age_thresholds`) will compact one file at a time, instead of merging multiple eligible file together (#13018).
* Support ingesting db generated files using hard link, i.e. IngestExternalFileOptions::move_files/link_files and IngestExternalFileOptions::allow_db_generated_files.
* Add a new file ingestion option `IngestExternalFileOptions::link_files` to hard link input files and preserve original files links after ingestion.
* DB::Close now untracks files in SstFileManager, making avaialble any space used
* DB::Close now untracks files in SstFileManager, making available any space used
by them. Prior to this change they would be orphaned until the DB is re-opened.
* Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
* `sst_dump --command=check` now compares the number of records in a table with `num_entries` in table property, and reports corruption if there is a mismatch. API `SstFileDumper::ReadSequential()` is updated to optionally do this verification. (#12322)
* `rocksdb.blobdb.blob.file.write.micros` expands to also measure time writing the header and footer. Therefore the COUNT may be higher and values may be smaller than before. For stacked BlobDB, it no longer measures the time of explictly flushing blob file.
* `rocksdb.blobdb.blob.file.write.micros` expands to also measure time writing the header and footer. Therefore the COUNT may be higher and values may be smaller than before. For stacked BlobDB, it no longer measures the time of explicitly flushing blob file.
* Files will be compacted to the next level if the data age exceeds periodic_compaction_seconds except for the last level.
* Reduced the compaction debt ratio trigger for scheduling parallel compactions
* For leveled compaction with default compaction pri (kMinOverlappingRatio), files marked for compaction will be prioritized over files not marked when picking a file from a level for compaction.
@@ -429,7 +905,7 @@ want to continue to use force enabling, they need to explicitly pass a `true` to
### Behavior Changes
* During off-peak hours defined by `daily_offpeak_time_utc`, the compaction picker will select a larger number of files for periodic compaction. This selection will include files that are projected to expire by the next off-peak start time, ensuring that these files are not chosen for periodic compaction outside of off-peak hours.
* If an error occurs when writing to a trace file after `DB::StartTrace()`, the subsequent trace writes are skipped to avoid writing to a file that has previously seen error. In this case, `DB::EndTrace()` will also return a non-ok status with info about the error occured previously in its status message.
* If an error occurs when writing to a trace file after `DB::StartTrace()`, the subsequent trace writes are skipped to avoid writing to a file that has previously seen error. In this case, `DB::EndTrace()` will also return a non-ok status with info about the error occurred previously in its status message.
* Deleting stale files upon recovery are delegated to SstFileManger if available so they can be rate limited.
* Make RocksDB only call `TablePropertiesCollector::Finish()` once.
* When `WAL_ttl_seconds > 0`, we now process archived WALs for deletion at least every `WAL_ttl_seconds / 2` seconds. Previously it could be less frequent in case of small `WAL_ttl_seconds` values when size-based expiration (`WAL_size_limit_MB > 0 `) was simultaneously enabled.
@@ -1217,7 +1693,7 @@ Note: The next release will be major release 7.0. See https://github.com/faceboo
### Public API change
* Extend WriteBatch::AssignTimestamp and AssignTimestamps API so that both functions can accept an optional `checker` argument that performs additional checking on timestamp sizes.
* Introduce a new EventListener callback that will be called upon the end of automatic error recovery.
* Add IncreaseFullHistoryTsLow API so users can advance each column family's full_history_ts_low seperately.
* Add IncreaseFullHistoryTsLow API so users can advance each column family's full_history_ts_low separately.
* Add GetFullHistoryTsLow API so users can query current full_history_low value of specified column family.
export CC CXX AR AS CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
export CC CXX AR AS CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_CXX CLANG_ANALYZER CLANG_SCAN_BUILD
Loaded 100 of 1085 files, more 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.