Compare commits

...
363 Commits
Author SHA1 Message Date
Xingbo Wang 0e768876e5 Don't let error recovery wait forever behind a writer stalled on the failed flush (#15605)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15605

**Problem:** automatic error recovery can hang forever, with all writes stopped, when a writer is waiting on a write stop (for example the `max_write_buffer_number` memtable limit) as the flush that would end it fails. Only a restart clears it.

**Why:** the stalled writer leads the write queue. It waits in `DelayWrite()` for the stop to end, and keeps waiting while recovery is in progress, so any recovery step that enters the write queue waits behind it. Two such steps exist:
1. A retryable SST write error is a soft error, and recovery retries the flush of the immutable memtables, which does not need the write queue. But with `two_write_queues = true`, `ResumeImpl()` first enters both write queues to sync `LastSequence()` with the allocated sequence numbers.
2. A hard error with auto recovery, for example a retryable error syncing the closed WALs during the flush of a DB with two column families, is recovered by an atomic flush of all column families. Its memtable switch enters the write queue in every configuration. New writes already fail with the background error at this point (`PreprocessWrite()` checks `IsDBStopped()`); only a writer that was already waiting keeps waiting.

**Fix:**
- The sequence sync at recovery start no longer waits for a write queue whose leader is in a write stall. That leader stalls before it allocates sequence numbers, and it cannot leave the stall while recovery holds the DB mutex, so the sync stays exact. If the stall ends while recovery waits for the other queue, the sync retries. Queues without a stall are still drained first, and the recovery flushes that switch memtables keep their own full fence.
- `DelayWrite()` stops waiting on a write stop once the DB is stopped, and returns the background error, like a new write does.

**Tests:**
- In `error_handler_fs_test`:
  - `RetryableFlushErrorWithWriterStalledOnFlush` (with and without `two_write_queues`): the first flush fails with a retryable SST write error after the writer stops on its memtables; the writer finishes after recovery.
  - `FlushWALSyncErrorWithWriterStalledOnFlush`: with two column families, the closed WAL sync fails after the writer stops; the writer gets the hard error, recovery completes, and the next write succeeds.
- In `write_prepared_transaction_seqno_test`:
  - `RecoverySeqnoSyncRetriesWhenStallEndsDuringNonmemJoin`: the main queue stall ends while recovery joins the nonmem queue, and the writer allocates before recovery syncs; recovery retries and syncs only after the writer publishes.
  - `RecoverySeqnoSyncSkipsStalledWalOnlyCommit`: a WAL-only commit is stalled on the nonmem queue; recovery syncs and finishes while the commit still waits.

Reviewed By: archang19

Differential Revision: D127129804

fbshipit-source-id: 88ed4938be39d7222ceb7c28437187989885b5d0
2026-11-05 17:25:23 -08:00
Xingbo Wang 9cbdbcfce8 Update RocksDB main for 11.14 release (#15604)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15604

Why: keep internal RocksDB main ready for 11.15.0 development while the 11.14 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.14.0 to 11.15.0
- Add `11.14.fb` to `check_format_compatible.sh`
- Sync HISTORY.md `## 11.14.` section from `11.14.fb`
- Delete 53 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: omkarhgawde, archang19

Differential Revision: D129984171

fbshipit-source-id: 2e9612c1c3b68f69c6252f191c24975a6ab26210
2026-11-05 13:34:42 -08:00
cm-sally-mate a4e298049a FileIngestionHandle::GetPreparedFiles: metadata of the files a prepared ingestion makes visible (#15593)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15593

**What:** `FileIngestionHandle` gets `GetPreparedFiles()`. It returns an `SstFileMetaData` for each file that the prepared ingestion makes visible on commit. Set fields: `directory`, `relative_filename`, `name`, `db_path`, `file_number`, `size`, `temperature`, `file_checksum`, `file_checksum_func_name`, `smallestkey`, `largestkey`, `num_entries` and `num_deletions`. With `allow_db_generated_files`, `smallest_seqno` and `largest_seqno` too, since commit keeps the file's own seqnos; otherwise commit assigns a global seqno and they stay 0. Commit also decides `epoch_number`, and `file_creation_time`/`oldest_ancestor_time` are the commit-time clock, so those keep their defaults. The list is empty after the handle is committed or aborted.

**Why:** a caller can read or verify the prepared files before commit makes them visible. Commit keeps each file's path, number, size and checksum: `MergeForSameColumnFamily` moves each `IngestedFileInfo` unchanged, and commit only edits the MANIFEST.

**How:** one new pure virtual on `FileIngestionHandle`. `FileIngestionHandleImpl` builds the entries from `files_to_ingest()` of each job, using the existing public `SstFileMetaData` struct. The directory comes from the column family's `cf_paths` and the file name from `MakeTableFileName`, the same way `TableFileName` builds the path.

**Not listed:** the range-tombstone SST that an `atomic_replace_range` prepare writes on a universal-compaction column family. Commit makes it live only on a partial overlap and deletes it otherwise, so a listed entry could name a file that never goes live.

**Risk:** low. Additive API; no behavior change for existing callers.

Reviewed By: archang19, joshkang97

Differential Revision: D129771271

fbshipit-source-id: be6e89d14bb710f787ad0514a839883f23731d3d
2026-11-05 13:02:47 -08:00
cm-sally-mate a9bcab5e75 Fix flaky ExternalTableTest.FactoryWithoutTableBuilderIsNeverCompacted (#15594)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15594

**Why:** `ExternalTableTest.FactoryWithoutTableBuilderIsNeverCompacted` fails intermittently, mostly in TSAN builds: `table_test.cc` reports `ingested[cf].size()` is 0, expected 1.

**Old behavior:** the test ingests one file into a FIFO column family whose `max_table_files_size` is 1, so any ingested file exceeds the limit. Ingestion schedules that column family's compaction right away. The test then reads the column family's live files to check that the ingestion added one. If the background FIFO compaction runs first, it has already deleted the file, and the check fails. This is a race in the test only; RocksDB behaves as intended.

**New behavior:** the test calls `PauseBackgroundWork()` before the two ingestions and `ContinueBackgroundWork()` after it checks them. Then it waits for compaction as before. The writable column family still loses its file to FIFO compaction, and the column family without a table builder still keeps its file. The test checks the same things, in a fixed order.

**Safety:** test-only change. The memtables are empty, so ingestion does not need a flush while background work is paused.

Reviewed By: joshkang97, archang19

Differential Revision: D129959521

fbshipit-source-id: 4f781efe2122f4bd40aef4da82854c5ca28f5499
2026-11-05 10:42:38 -08:00
Robert Pan a24f8f8785 Honor SstPartitioner before a bottommost range tombstone (#15590)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15590

Why the change is required:
`IngestExternalFiles()` with `fail_if_not_bottommost_level=true` can fail with `TryAgain` indefinitely when a range tombstone stretches an existing bottommost file across an `SstPartitioner` boundary. Recompacting the range does not help, because the same input produces the same bracketing file. MyRocks hits this in its tmp-rdb copy ALTER, which compacts the destination range with a partitioner that cuts at the new index boundaries and then ingests into Lmax. The ALTER fails with error 525 when another DDL creates and drops an index in the same column family while the ALTER runs, and a snapshot older than the drop is open. This can happen on any server that runs DDL concurrently, a primary or a replica whose applier runs DDL in parallel; it was first seen on replicas with `replica_parallel_type=DATABASE`.

Previous behavior:
At the bottommost level, `CompactionOutputs::AddToOutput()` returned early for a range-del sentinel key, before `ShouldStopBefore()`. As a result, the partitioner was never consulted for that key. At the bottommost level a range tombstone is kept only while a snapshot older than it exists. When it is kept, `AddRangeDels()` adds it to the current output file and extends that file's largest key to the tombstone's end key, even if the partitioner would have required a cut between the last point key and the tombstone start.

Triggering conditions, all of which are required:
- A partitioner returns `kRequired` at a boundary B.
- A bottommost compaction outputs point keys below B, followed by a range tombstone starting at or above B, with no point key in between.
- A snapshot keeps the tombstone alive.

The resulting file spans B, so no file whose smallest key is at or after B and below the tombstone end fits in the bottommost level.

In MyRocks, the tombstone is the `[id, id+1)` DeleteRange written by the drop-index thread for a dropped index whose id is above the ALTER's newly reserved index ids.

New behavior:
A bottommost range-del sentinel cuts the output file when the partitioner returns `kRequired` for the sentinel's start key, unless the open file's largest key has the same user key. The tombstone then starts its own file, and the file holding the preceding point keys ends below B.

Implementation:
- `AddToOutput()` keeps returning early for a bottommost sentinel unless a file is open, its largest user key differs from the sentinel's, and the partitioner requires a cut. Each branch now decides `cut_before` explicitly. On a cut, it closes the current file exactly as the non-bottommost sentinel path does, including setting `range_tombstone_lower_bound_` from the sentinel key.
- The partitioner check is factored out of `ShouldStopBefore()` into `PartitionerRequiresCut()`, so both paths use the same fast prefix path and per-key callback. It is only meaningful while an output file is open and asserts `HasBuilder()`. `ShouldStopBefore()` behavior is unchanged.
- The same-user-key guard is needed because a bottommost point key can have its seqno zeroed (`k@0`), which sorts after a sentinel `k@s` for an older tombstone starting at the same user key. Cutting there would close the file with a next-file key below its own largest key: debug builds assert in `AddRangeDels()`, and with a newer snapshot-pinned tombstone in another input run, release builds install two overlapping bottommost files and fail with Corruption. No partition boundary can fall inside one user key, so skipping the cut loses nothing. Prefix partitioners and the MyRocks index-boundary partitioner never cut between equal keys; a partitioner that cuts at every key, or by file size, can.

Safety, compatibility, and limitations:
- Only bottommost compactions with a configured `SstPartitioner` are affected. Without a partitioner, `PartitionerRequiresCut()` returns false and output is unchanged. Grandparent-overlap and file-size cuts still never trigger on a bottommost sentinel.
- Such compactions can now produce one more, smaller output file per cut. The cut is decided before `AddRangeDels()` decides whether to drop the tombstone, so it also happens when the tombstone is about to be dropped (the common no-snapshot case). The file opened for it then usually ends up empty: it is written, synced and closed, then deleted by the existing empty-output cleanup in `FinishCompactionOutputFile()` before the compaction installs its outputs, so it never enters the LSM. Listeners see `OnTableFileCreated` with `Aborted("Empty SST file not kept")`, and the output-file count includes it. Predicting tombstone survival in `CompactionOutputs` would avoid this but add complexity, so the cost is accepted. In a microbenchmark with a range delete over every other one of 2000 prefixes, it added 999 such files and raised `CompactRange` from 797 ms to 1155 ms. In MyRocks, the index-boundary partitioner only cuts at index ids registered for an in-flight bulk load or tmp-rdb ALTER, so the extra file appears only in compactions within such a window.
- The same applies to the proximal-level output of per-key placement (tiered storage), which shares the bottommost compaction. A snapshot-pinned tombstone there no longer shares a file with the preceding hot key when the partitioner requires a cut; `TieredCompactionTest.LevelOutofBoundaryRangeDelete` and `UniversalRangeDelete` asserted the old layout (one proximal file holding a data key and the tombstone) and now expect the tombstone in a file of its own.
- No API, option, or on-disk format change.
- Not addressed: a partitioner cut inside a single tombstone's range. A tombstone still cannot be split across files at the bottommost level, as before.

Reviewed By: pdillinger

Differential Revision: D129661896

fbshipit-source-id: 1c38b00b1fd80c679a094b124db6f097b122b5af
2026-11-05 08:40:29 -08:00
Peter Dillinger 9d3733d9d8 Accurate SST file times, plus DB ownership metadata for SST files (#15583)
Summary:
Ideally, each age-based feature acts on the time that matches its purpose, which requires tracking three different times for each SST file:
* How old its data is (when its keys were written, as bounded by the oldest ancestor time and the `newest_key_time` table property), so that TTL expires data based on its actual age.
* When the file's contents were built, i.e. when its data last went through a flush or compaction (`file_creation_time`). Periodic compaction (commonly used so that all data regularly goes through a compaction filter) and `GetCreationTimeOfOldestFile()` should act on this.
* When this DB began holding the file. `bottommost_file_compaction_delay` should act on this: it limits how soon a recently written bottommost file is rewritten alone, after snapshot releases, to zero its sequence numbers or drop data no longer protected, so that a series of snapshot releases does not rewrite the same data repeatedly. External storage accounting (how long each file occupies storage) also needs this time.

Knowing how the DB got each file (built it, or ingested or imported it) is also useful, e.g. for storage accounting.

Previous behavior:
A file's age for periodic compaction comes from its MANIFEST `file_creation_time`, then (only if the table reader is pinned) the `file_creation_time` table property, then the oldest ancestor time, then the file's mtime (unchanged by this commit). However:
* Files written by `SstFileWriter` had table property `file_creation_time` = 0 (unknown).
* Files flushed during WAL recovery had `file_creation_time` = 0 in both the table properties and the MANIFEST.
* `RepairDB` restored each file's oldest ancestor time from its table properties but not its `file_creation_time`, so every repaired file lost its MANIFEST creation time.
* `IngestExternalFile()` and `CreateColumnFamilyWithImport()` set both `file_creation_time` and the oldest ancestor time to the ingest/import time, ignoring times carried by the file's table properties or the export metadata. Data written or last compacted long before ingestion was therefore not due for periodic compaction (or TTL) until a full period after ingestion. Also, `Checkpoint::ExportColumnFamily()` left the time fields of its export metadata unset.
* A FIFO temperature change done as a trivial copy (with `allow_trivial_copy_when_change_temperature`) recorded the copy time as the new file's MANIFEST `file_creation_time`, although its contents and table properties are the original's.
* FIFO TTL compaction picks expired files oldest first, judging each by the `newest_key_time` table property or else the oldest ancestor time table property. `SstFileWriter` files have neither, so an ingested `SstFileWriter` file stopped the scan: neither it nor any newer file was ever deleted by TTL.
* Nothing recorded when the DB began holding a file, or whether it was ingested, imported, or built by this DB. (For ingested and imported files, `file_creation_time` happened to be the time they were added, which `bottommost_file_compaction_delay` used.)

New behavior, for when contents were built:
* The block-based table builder now records the `file_creation_time` property itself, reading the clock when it finishes the file, so that the time is no earlier than when any entry was added. So every block-based SST file carries one (unless reading the clock fails), including `SstFileWriter` files, whose entries can be added over a long time. (For flush and compaction outputs, the MANIFEST value is still read when the output file is started, so it can be slightly earlier than the property.) The WAL recovery flush now also sets the MANIFEST value, and `RepairDB` restores it from the table property.
* Ingestion and import keep the creation and oldest ancestor times carried by the file when known: for ingestion, from its table properties; for import, from the export metadata, or else the file's table properties. A carried time later than the current time suggests an unreliable clock, so it is logged as a WARNING and replaced with the current time; an unknown time becomes the current time. When no oldest ancestor time is carried (true of all `SstFileWriter` files), it falls back to the resolved creation time, an upper bound on when the data was written, so TTL never fires early. `ExportColumnFamily()` now fills in both time fields.
* A FIFO temperature-change copy keeps the original's `file_creation_time`.
* FIFO TTL compaction falls back to `file_creation_time` when a file has neither of the table properties above. Every key in a file was written no later than the file's contents were built, so this never deletes early.
* New option `reject_too_old_file` (default false) in `IngestExternalFileOptions` and `ImportColumnFamilyOptions` fails the whole ingestion/import with `InvalidArgument`, before any file is linked or copied, if any file is already too old for the column family to keep without recompaction or deletion: for leveled and universal compaction, if its known creation time is older than the effective `periodic_compaction_seconds`; for FIFO compaction, if its newest data (the `newest_key_time` table property when known, else its creation time) is older than `ttl`. Such data should already have been recompacted (e.g. through a compaction filter) or deleted. Leveled `ttl` is not considered because it does not limit how long data can be kept, only how long it stays above the last level. Files without the needed time are not rejected. C and Java bindings included.

New behavior, for when the DB began holding a file:
* New `SstFileMetaData::db_ownership_start_time` (also in `LiveFileMetaData` and export metadata), backed by a new MANIFEST field: the time this DB began holding the physical file (file number). That is when the DB started writing it (flush or compaction output, local or remote), when ingestion linked or copied it into the DB (in the prepare phase, for two-phase ingestion), when import did, or when a FIFO temperature-change copy wrote it. Moves between levels preserve it. A long compaction's outputs start when they are created, not when the compaction finishes.
* New `SstFileMetaData::db_ownership_info`, decoded by the new public struct `DbOwnershipInfo`, backed by another MANIFEST field: how the DB got the contents (flush, compaction, ingestion, or import) plus sticky flags for things that have happened to the file without its contents being rewritten (moved between levels; copied to a new temperature; temperature updated from the file system by `UpdateManifestForFilesState()`; recorded by `RepairDB`).
* `bottommost_file_compaction_delay` is now measured from `db_ownership_start_time` (falling back to `file_creation_time` when unknown). For files built by the DB that is the same as before; ingested files again get the delay from when they were ingested, rather than skipping it because of old contents (which only matters for ingested DB-generated files, since normally ingested `SstFileWriter` files have sequence number 0 and are never marked).

Neither new field is a table property: they describe one DB's relationship to a file, and the same file contents can be added to several DBs. Times not recorded, for lack of a consumer: when the DB first held the contents across temperature copies, when a file became visible (MANIFEST commit time), and when a file arrived at its current level.

Implementation notes:
* `TableBuilderOptions::file_creation_time` (and its positional constructor parameter) is removed, since the builder now reads the clock itself. All callers passing arguments past that position were updated.
* `ResolveExternalFileTimes()` and `CheckExternalFileNotTooOld()` are shared by ingestion and import.
* The new MANIFEST fields use new safe-to-ignore `NewFile` tags, written only when known. Their packed bit layout is defined next to the tags; unknown bits are ignored on read and preserved when a file's record is carried over.
* New `VersionEdit::AddExistingFile()` re-adds a file under the same file number with all of its persisted metadata. It replaces field-by-field copies into the positional `AddFile()` at the five places that move files (trivial move, `CompactRange()` with `change_level`, `PromoteL0()`, the level migration at open, and `UpdateManifestForFilesState()`) and in the MANIFEST snapshot writer, so MANIFEST-only fields, including future ones, carry over. The WAL recovery flush, `RepairDB`, and import now use the `FileMetaData` overload of `AddFile()`; as a side effect, recovery flush outputs now also record min/max timestamps and file open metadata in the MANIFEST, as regular flush outputs do.

Compatibility and limitations:
* Behavior change: ingesting or importing old data can make it due for periodic compaction or TTL soon afterward rather than a full period later, so bulk-ingesting old SSTs can cause a burst of compactions. Users who must not publish such data can set `reject_too_old_file`.
* Files already in a DB keep their MANIFEST entries; the new fields are unknown (0) for them. For files ingested, imported, or temperature-copied by older versions, `file_creation_time` was the ingestion, import, or copy time, so the `bottommost_file_compaction_delay` fallback is accurate for them. Files from older `SstFileWriter` versions carry no creation time and still get the ingestion time.
* Older RocksDB versions ignore the new MANIFEST fields and drop them when rewriting the MANIFEST. A DB opened from a checkpoint or backup keeps the values recorded by the DB it was copied from. `RepairDB` loses them for files that existed before the repair.
* Plain and cuckoo table builders still record no time properties.
* The times recorded for remote compaction outputs come from the remote worker's clock.
* Possible follow-ups: letting `SstFileWriter` callers supply an authentic oldest ancestor time; and avoiding bottommost-files compactions that rewrite an ingested `SstFileWriter` file only to zero a global sequence number that exists only in the MANIFEST (found by code reading, not yet confirmed by a test).

Pull Request resolved: https://github.com/meta/rocksdb/pull/15583

Test Plan:
New tests, using mock clocks where time matters:
* `DBTablePropertiesTest.FileCreationTimeAlwaysSet`: `SstFileWriter` file plus ingestion, WAL recovery flush, and `RepairDB` all record a creation time, checked in both table properties and the raw MANIFEST `FileMetaData` (`GetLiveFilesMetaData()` would mask a missing MANIFEST value by falling back on pinned table properties).
* `ExternalSSTFileTimeTest.*` (external_sst_file_test): `KeepRecordedTimes` covers recorded, unknown, and future creation times; `KeepDBGeneratedFileTimes` checks that `allow_db_generated_files` keeps both times from the source DB; `PeriodicCompactionFromWriteTime` checks that periodic compaction triggers relative to when an `SstFileWriter` file was written, not when it was ingested; `RejectTooOldFile` covers leveled, universal, and FIFO: a batch containing a too-old file is rejected with the DB unchanged, a file just under the limit and a file with unknown creation time are accepted, and the option off accepts the too-old file; `RejectFIFOFileWithOldNewestKey` checks that for FIFO, a recently built file whose newest key time is older than `ttl` is rejected; `CreationTimeIsWhenFileIsFinished` checks that an `SstFileWriter` file's creation time is when it was finished, not opened; `FIFOTtlDeletesIngestedFile` checks that an ingested `SstFileWriter` file and a newer flushed file expire by TTL; `RecordsDbOwnership` checks the ownership start time and origin for one-phase ingestion, two-phase ingestion (from the prepare phase, not the commit), and ingestion of a DB-generated file; `BottommostCompactionDelayFromIngestion` checks that an ingested DB-generated file with old contents is not compacted as a bottommost file until the delay has passed since ingestion.
* `ImportColumnFamilyTest.ImportKeepsExportedFileTimes` and `ImportFileTimesAndRejectTooOldFile`: same coverage for export/import, plus falling back on the file's table properties when the import metadata has no time, and ownership metadata in the export and after import.
* `DBOwnershipTest.*` (db_etc3_test): ownership start time and origin of flush, WAL recovery flush, and compaction outputs (including a compaction delayed after creating its output), and in public metadata; preservation, with the "moved" flag, through `PromoteL0()`, trivial move, `CompactRange()` with `change_level`, the level migration at open, and reopens (MANIFEST replay and snapshot); FIFO temperature-change copy (same creation time, new ownership start time, "temperature copied" flag); `UpdateManifestForFilesState()`; and `RepairDB` (unknown ownership for pre-existing tables, flush origin for tables built from WALs).
* `CompactionServiceTest.BasicCompactions`: remote compaction outputs record ownership like local ones.
* `VersionEditTest.*`: MANIFEST encoding of the new fields (round trip, preserved reserved bits, written only when known, safe to ignore, debug output), the persisted `DbOwnershipInfo` bit layout, `AddExistingFile()`, and the fallback for an unknown ownership start time.
* Java getter/setter tests for the new option. The JNI code was compiled against `javac`-generated headers, but the Java tests were not run locally.

`FIFOTtlDeletesIngestedFile` was verified to fail before the FIFO TTL fix. `BottommostCompactionDelayFromIngestion`, `FIFOTemperatureChangeCopy`, `MovesPreserveOwnership`, `TemperatureUpdatedFromFileSystem`, and `AddExistingFile` were verified to fail with, respectively, the delay measured from `file_creation_time`, the copy time used as creation time, and `AddExistingFile()` dropping the ownership start time. `CreationTimeIsWhenFileIsFinished`, `RejectFIFOFileWithOldNewestKey`, and `ImportFileTimesAndRejectTooOldFile` were verified to fail with, respectively, the creation time read when the builder is created, FIFO rejection based only on creation time, and import ignoring the file's table properties. The creation-time tests were verified to fail with the new logic stubbed out to the old behavior before rebasing onto current main, and `ImportKeepsExportedFileTimes` also fails with only the `ExportColumnFamily()` fix reverted.

Adjusted `ExternalSSTFileBasicTest.AtomicReplaceRangeAvoidsRedundantTimestampScans`: it models legacy files with no recorded creation time and relied on `SstFileWriter` files having a zero `file_creation_time` property (pinned readers fall back on it). It now writes those files with a writer clock that reads 0.

Reviewed By: xingbowang

Differential Revision: D129668538

Pulled By: pdillinger

fbshipit-source-id: 1e8ad8e9738d7b0427c408d693bc22c46deca913
2026-11-04 16:31:52 -08:00
cm-sally-mate 8437f701ce Fix MultiGet with empty prefix filter partitions (#15589)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15589

Why the change is required:
With partitioned filters and `whole_key_filtering=false`, filter partitions hold only prefixes. Keys outside the prefix extractor's domain (for example keys shorter than N with `fixed:N`) are never added. When filter partitions are cut together with index partitions (`decouple_partitioned_filters=false`, which is also how files from older versions are laid out), a partition that covers only such keys is empty, and its handle has size 0. `MultiGet` dropped every key mapped to such a partition, so it returned NotFound for keys that exist.

Previous behavior:
`PartitionedFilterBlockReader::MayMatch(MultiGetRange*)` looks up the filter partition of every key in the batch before any per-key domain check. For a size-0 handle it called `SkipKey()`, after `assert(false)` with the comment "Not reachable". So:
- Debug builds: `MultiGet` fails the assertion.
- Release builds: `MultiGet` returns NotFound for existing out-of-domain keys. In a release-build repro, `get a` returned the value while `multi_get` of the same 10 short keys reported 9 of them as not found.
`Get` is not affected: it checks `InDomain()` before it consults a prefix filter.

New behavior:
A key mapped to an empty filter partition is ruled out only when the filter could have contained it: in whole-key mode, or when the key is in the prefix extractor's domain. Out-of-domain keys stay in the batch, as with `Get`.

Implementation:
The size-0 handle branch in `PartitionedFilterBlockReader::MayMatch(MultiGetRange*)` now checks `prefix_extractor->InDomain()` before `SkipKey()`, and the unreachable-path assertion is removed.

Safety, compatibility, and limitations:
Read-side only, no format change. Existing files with empty filter partitions read correctly after this change. Decoupled filter partitions (the current default) are cut by filter size, so new files rarely have empty partitions.

Reviewed By: pdillinger

Differential Revision: D126811019

fbshipit-source-id: d154acc42ded8d1fe9637e03e9d52bbd9ec3177e
2026-11-04 16:14:22 -08:00
cm-rockee c9fcb564f8 Distinguish captured compaction pool metadata from the DB option (#15587)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15587

`use_separate_remote_compaction_pool` is a mutable DB setting, while the same name on job info structs records the pool selected for an already scheduled job. Using an option-style name for that snapshot makes callback metadata look like another configuration knob.

Rename the captured field to `separate_remote_compaction_pool_selected` in `CompactionJobInfo`, `SubcompactionJobInfo`, and `CompactionServiceJobInfo`, together with the backing `Compaction` field and its accessors. The selection is still captured at the same points, stays stable across later DB option updates, and remains selected when execution falls back to local workers. The DB option, runtime option string, defaults, serialization policy, and scheduling behavior are unchanged.

Regenerate the C bindings to expose getters using the metadata name. Retain the previous C getter names as compatibility aliases, preserving existing exported signatures.

Requested by: Ranjan Banerjee (rbanerjee2)

Reviewed By: pdillinger

Differential Revision: D129730425

fbshipit-source-id: 48cc35f35b6479a37a0b94aeef9f09b87d158644
2026-11-04 15:14:48 -08:00
Chaitanya Datye 78eb4900c8 Let table factories declare that they cannot build tables (#15586)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15586

This adds `TableFactory::IsTableBuilderSupported()` and `ExternalTableFactory::IsTableBuilderSupported()`, so a table factory that only reads files produced elsewhere can say so, and makes RocksDB keep every compaction away from a column family that uses one.

Some external table factories read a format that RocksDB cannot write: their files are produced by an outside pipeline and only ever ingested, and `NewTableBuilder()` returns a builder that fails. Today nothing tells RocksDB this, so any flush or compaction that reaches such a factory fails in the background, and the resulting background error stops writes to the whole DB, not just to that column family. Flushes can already be ruled out with `disallow_memtable_writes`, but a manual compaction cannot: with a single ingested file on the last level, a default `CompactRange()` is a no-op, while `CompactRange()` with `BottommostLevelCompaction::kForce` rewrites the file, fails with the builder's error, and leaves the next `Put()` to the default column family failing with the same status. Automatic compactions have the same problem unless the user remembers `disable_auto_compactions`.

With this change, a factory that returns false gets four guarantees. Opening or creating a column family with it fails with `InvalidArgument` unless `disallow_memtable_writes` is set, so nothing can reach a flush. `ColumnFamilyData::NeedsCompaction()` returns false, so automatic compactions are never scheduled, whatever the compaction style and `disable_auto_compactions`. `CompactRange()` and `CompactFiles()` return `NotSupported` before doing any work, so a caller gets a foreground error and the DB stays writable. And its L0 files and pending compaction bytes never stall or stop writes, the same as for a column family with `disable_auto_compactions`: since nothing can compact it, counting that backlog would let one such column family stop writes to the whole DB for good, for example after an overlapping ingestion lands in L0 of a column family whose L0 stop trigger it reaches. This follows `IsDeleteRangeSupported()`: the column family caches the capability when it is created, and the external table adapter caches the wrapped factory's answer so that RocksDB never calls external code while holding the DB mutex.

The default is true, so existing table factories behave as before. Blocking automatic compactions also blocks compactions that need no builder, such as FIFO deletions and trivial moves; a factory that cannot build tables should not rely on them. `OpenAndTrimHistory()` calls `CompactRangeInternal()` directly, but only for column families with user-defined timestamps, which external tables reject.

Changes:
1. `TableFactory::IsTableBuilderSupported()` and `ExternalTableFactoryBase::IsTableBuilderSupported()` default to true; `ExternalTableFactoryAdapter` caches and forwards the wrapped factory's value, including after `PrepareOptions()`.
2. `ColumnFamilyData` caches the capability, `NeedsCompaction()` returns false without it, and `ColumnFamilyData::ValidateOptions()` requires `disallow_memtable_writes` without it.
3. `GetWriteStallConditionAndCause()` takes the capability and, like `disable_auto_compactions`, ignores L0 file counts and pending compaction bytes without it; its three callers pass the column family's cached value.
4. `DBImpl::CompactRange()` and `DBImpl::CompactFiles()` return `NotSupported` for such a column family.
5. `ExternalTableTest.FactoryWithoutTableBuilderIsNeverCompacted` compares two FIFO column families holding the same ingested file, one with a factory that can build tables and one without, and checks creation-time validation, that only the first is compacted automatically, that `CompactRange()` with and without `kForce` and `CompactFiles()` return `NotSupported` for the second, and that the DB stays writable and the file readable.
6. `ExternalTableTest.FactoryWithoutTableBuilderDoesNotStallWrites` ingests two overlapping files into a two-level column family without a builder, so one lands in L0 and reaches every L0 trigger, and checks that writes to the default column family are neither stopped nor delayed.

Reviewed By: xingbowang

Differential Revision: D128259405

fbshipit-source-id: f52221321a1f3ae20725d41632a3c61bde0738f5
2026-11-04 13:05:08 -08:00
Jay Huh 15951afbb5 Experimental live tuning: measure, recommend, apply, watch, keep or revert option changes (#15563)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15563

Why:
Tuning RocksDB options for a workload is manual. LLMs can now reason about RocksDB's stats and workload well enough to propose option changes, and RocksDB already has the signals (DB properties, statistics, the query tracer) and can change mutable options online. This adds the loop that connects them: an on-demand tuning cycle inside RocksDB that measures the DB, asks an advisor for changes, applies them, watches the result and keeps or reverts them, with every step pluggable and logged.

What (EXPERIMENTAL; nothing runs unless the application calls `StartLiveTuneCycle()`):
- One cycle: baseline metrics from the DB's own properties -> a short query trace (`DB::StartTrace()`) and its analysis -> a recommendation -> checks (allowed options, bounds, `max_changes`, `max_step_ratio`) -> `SetOptions()` / `SetDBOptions()` (`skip_apply` stops before this) -> each following window compared with the baseline and decided on -> kept or reverted.
- API (`include/rocksdb/live_tune.h`): `StartLiveTuneCycle()`, `GetLiveTuneStatus()`, `StopLiveTuneCycle()`, `GetLiveTuneAppliedOptions()`, and `LiveTuneOptions`: the intent (a goal in plain words, allowed options with bounds, optional constraints), window and limit settings, `skip_apply`, `allow_compaction`.
  - `GetLiveTuneAppliedOptions()` returns the options live tuning has changed and not reverted, with their values: the running cycle's changes (from just before they are applied until they are reverted) and the changes kept by cycles since the DB was opened. An application that also sets options, e.g. by re-applying its own configuration periodically, uses it to leave those options alone instead of undoing the cycle's change.
- Changes that take effect only on SST files written after them (e.g. `block_size`): the advisor says so with `LiveTuneRecommendation::requires_compaction`. With `allow_compaction` the cycle compacts the column family (`CompactRange()`) after applying them and before watching; without it, such a recommendation is rejected. Table-property metrics (`avg_data_block_bytes`, `index_bytes`, `filter_bytes`) show their effect.
- With a `Statistics` object, the metrics also come from the DB's tickers: `db_keys_read` (Get and MultiGet keys), `db_iterator_seeks`, `db_iterator_steps`, `db_block_cache_data_hits` and `db_block_cache_data_misses`, with derived `sst_file_reads_per_key` (the column family's SST file reads per key read, sought or stepped) and `db_block_cache_data_hit_ratio`. Read efficiency can then be judged per key rather than on raw counts that follow traffic, with no help from the application. Tickers cover every column family, and every DB sharing the `Statistics` object, so their metrics carry a `db_` prefix; the public header lists which metrics are per column family and which are for the whole DB.
- Pluggable:
  - `LiveTuneAdvisor` (required): recommends changes, e.g. by asking an LLM, with the objectives and constraints to judge them by.
  - `LiveTuneTraceAnalyzer`: summarizes the trace; the built-in one uses RocksDB's `TraceAnalyzer` (parent change).
  - `LiveTuneVerdict`: `GetWindowDecision()` after every watch window: keep watching, keep the changes or revert them. By default a broken constraint reverts, and a change is kept once a one-sided 95% Wilson lower bound of winning windows reaches `min_confidence`. An override can call the default and add its own checks, e.g. revert a change the default would keep.
  - `LiveTuneMetricsProvider`: the application's own metrics.
  - `LiveTuneEventListener`: every step as a JSON event, and `OnCycleDone()` with the final result, e.g. to roll a kept change out to other DBs.
- Every event also goes to the info LOG as `LIVE_TUNE {json}`.

Safety and limits:
- Only allowed options within bounds change, through `SetOptions()` / `SetDBOptions()`; they are reverted on a broken constraint, a revert decision, `StopLiveTuneCycle()` or DB close. A change that mixes column family and DB options is checked before anything is set, and if `SetDBOptions()` still fails after `SetOptions()` succeeded, the column family options are restored, so nothing stays half applied. Because `SetOptions()` and `SetDBOptions()` can fail with the new values already in effect (for example when the OPTIONS file write fails), the values are read back after an error to decide what is in effect. A revert restores the values read right before apply, after the advisor call, so it doesn't undo an option the application changed during the trace or the advisor call; if a change is still in effect after a revert, the outcome ends with "; revert failed: <status>" and `GetLiveTuneAppliedOptions()` still lists it. One cycle per DB at a time. Any DB opened read-only (read-only, compacted, secondary or follower) returns NotSupported.
- Lifecycle: an event listener may stop the cycle from its callbacks (the stop is only requested), and starting the next cycle from a callback returns Busy; start it from another thread, e.g. once the "done" event is seen. Starting a new cycle joins the previous cycle's thread without holding the live tune mutex. The advisor's `CancelRecommendation()` is called only while its `GetRecommendation()` is in progress.
- Option values are read and written with RocksDB's own options serde: single options by name through `CFOptionsAsConfigurable()` / `DBOptionsAsConfigurable()` and `Configurable::GetOption()`, including dotted fields of a struct or of the table factory (`compaction_options_universal.size_ratio`, `block_based_table_factory.block_size`), which `SetOptions()` also takes directly, keeping the block cache and filter. Recommended values are rewritten the way RocksDB writes them (e.g. `32K` as `32768`) by parsing them as their option, so checks and read-back comparisons are type-aware. They are parsed into a copy of the table factory and its filter policy, because the table factory parser sets a mutable field under it (a ribbon filter's `filter_policy.bloom_before_level`) on the existing object, so checking a recommendation never changes the DB; a value the option doesn't accept is rejected ("rejected: invalid value: ...") before anything changes, and like any rejected recommendation it is logged with its changes and recorded in the history the next cycle's advisor gets. Numbers are parsed with `strtod`, like RocksDB's own parsing of double options.
- `max_step_ratio` bounds every numeric change; a move to or from 0 or a negative value (often "disabled" or "no limit" in RocksDB) is accepted only within explicit intent bounds.
- If the current values of the allowed options cannot be read (an option fails to serialize), the cycle ends as "rejected: cannot read the current option values: <status>" before anything changes, rather than recommending from incomplete values. If applying fails, the previous values are restored the same way as a revert, read back, and the outcome adds "; revert failed: <status>" only for a change still in effect. An allowed option that neither the column family nor the DB options have makes `StartLiveTuneCycle()` return InvalidArgument, before any baseline.
- Objectives and constraints must be on measured metrics: a recommendation with changes that names an unmeasured one is rejected (an answer with no changes is "no change" whatever objectives it lists), and an enforced constraint (the intent's, or `default_constraints` when the intent has none) on an unmeasured metric ends the cycle right after the baseline ("rejected: intent constraint metric <metric> is not measured", or "default constraint metric"), before any trace or advisor call, instead of never being checked.
- Each window is compared with a baseline taken before the change (no control group). If the application already has a trace running (`DB::StartTrace()`), the cycle leaves it alone and goes on without a trace (the "traced" event has status Busy); if the application starts one while the cycle traces, the cycle's trace ends and the application's keeps running. This uses two internal `DBImpl` methods, `StartTraceIfNone()` and `EndTraceIfCurrent()`; `DB::StartTrace()` itself still replaces a running trace. A trace file left by a cycle that ended with the process (e.g. a crash) is deleted when the next cycle on the DB starts. SST read metrics come from the file read histograms read without a lock, so they are approximate. Table-property metrics come from `GetPropertiesOfAllTables()`, which reads the files' properties without holding the DB mutex. Not yet supported in db_bench, db_stress, Java or the C API.

Reviewed By: xingbowang

Differential Revision: D128710591

fbshipit-source-id: 06b7af65841593870e9c2325c0f305bca94c7787
2026-11-03 17:37:21 -08:00
Michael C Huang a51c5e21ec Repair missing blob references during manual compaction (#15538)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15538

What changed?

- Adds `CompactRangeOptions::skip_missing_blob_files`, default `false`, for manual repair when referenced blob files are absent from the compaction's input Version.

https://pxl.cl/j8V47

- Rewrites selected SSTs. Lost scalar values become deletions. Wide entities keep surviving columns.
- Manual `CompactRange` only. Runs locally; compaction services are bypassed. FIFO is unsupported.

Why?

Best efforts recovery can leave dangling blob references in SSTs. These can cause later compactions to fail. Offline repair prepares the DB for normal reopening.

Reviewed By: xingbowang

Differential Revision: D128836898

fbshipit-source-id: 3acdf3b2deb3193dd181e748522d85e8efd64d97
2026-11-03 16:15:46 -08:00
Steph Pontikes 8fa3ee8fdc Fan out prefetched block decompression on a bounded executor (#15409)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15409

X-link: https://github.com/facebook/rocksdb/pull/15409

**WHAT**

- Add an optional fixed-size worker pool to the scan I/O dispatcher.
- Poll asynchronous reads and materialize their blocks, including decompression, off the foreground iterator thread.
- Fan out blocks from each coalesced read across bounded workers and join them with an atomic counter.
- Publish status and blocks through an acquire/release completion state, parking only when the sole `ReadSet` consumer arrives early.
- Preserve caller-thread processing when the configured worker count is zero

This change is purely additive; if a decompression thread pool is not specified via read options, the behavior is the same as previously with decompression happening on the calling thread.

**WHY**

Decompression can often become the bottleneck during async-io iteration. async io with rolling prefetching is meant to pipeline the application level iteration with io happening underneath. however, if the application level iteration (which involves decompressing fetched blocks) is far slower than the IO, the pipeline stalls, and throughput gets worse.

to this end, we can fork decompression tasks onto an executor, which can speed up the application layer part of the pipeline. for full table scans, this can see ~2x throughput improvements

Reviewed By: xingbowang

Differential Revision: D125249244

fbshipit-source-id: 99b80d7fb4f38ad6297d455ef0340f203d05aeb6
2026-11-03 15:59:08 -08:00
Steph Pontikes d0e6a4ca27 Add a dedicated READ Env thread pool (#15576)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15576

Add `Env::Priority::READ` as a schedulable pool and keep `USER` as a metrics-only priority category. Propagate the new pool through POSIX, Windows, thread-status, and Java/JNI APIs.

Reviewed By: xingbowang

Differential Revision: D129424000

fbshipit-source-id: 9dcf7c25ac6be6ec8931072dba2ad0bab8146300
2026-11-03 15:59:08 -08:00
Jay Huh 3993edbf34 Keep a secondary's view when catch-up switches to a MANIFEST that lists deleted files (#15571)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15571

## Problem

`TryCatchUpWithPrimary()` on a secondary (and follower catch-up) can install a column family with no files and still return OK. It happens when catch-up switches to a MANIFEST the primary just rolled, and the primary deletes a file that MANIFEST lists before catch-up checks it, recording the deletion in a newer MANIFEST. The secondary then serves the column family as empty until a later catch-up installs a new state. NeoSQL reproduced it deterministically with `branch_gc_catch_up_test` in D129307821 (T296537643).

## Cause

On a MANIFEST switch, `ManifestTailer::Initialize()` (default column family) and `ManifestTailer::OnColumnFamilyAdd()` (other column families) reset each builder to the column family's retained empty initial `Version`, so that the new MANIFEST's snapshot can add every live file again. The builder treats that empty base as a valid point in time, and the log number, next file number and last sequence are already known from the previous MANIFEST. When the snapshot lists a file that is gone, applying it is a valid-to-invalid edge, so `VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit()` builds a `Version` from the state before the snapshot, which is empty, and `CheckIterationResult()` installs it.

## Fix

`ManifestTailer` tracks the column families whose builder was reset for a new MANIFEST and that have not yet applied the record listing their files. `VersionEditHandlerPointInTime` now asks `BuilderHoldsPointInTime()` before creating a `Version`, and `ManifestTailer` answers false for those column families, so no `Version` is built from the empty base, neither on a negative edge nor on a forced create. A column family whose snapshot is incomplete keeps its previously installed `Version`. It gets a new one when a later record in the same MANIFEST completes the state, or from the next MANIFEST switch.

`VersionSet::WriteCurrentStateToManifest()` writes each column family's files in one record that also sets the column family's log number, and the records before it (DB id, comparator) set none. So the first record for the column family that sets a log number, other than a column family add, ends the wait.

A column family can also be left waiting when catch-up stops partway through a MANIFEST, after the record adding the column family and before the one listing its files. If the primary then drops it, no later MANIFEST lists it, and while it waits it holds back every atomic group. So a column family still waiting when catch-up switches to a new MANIFEST, and that the new snapshot does not add again, stops waiting at the first record after that snapshot. That is the first record setting the next file number: `LogAndApply()` sets it on every edit, and `WriteCurrentStateToManifest()` never does.

Because no `Version` is installed, `GetInstalledVersionLogNumber()` does not advance, and the secondary keeps memtables against the previous view, consistent with it.

Other paths are unchanged: the hook returns true by default, and only catch-up resets fill the set. Opening a secondary on such a MANIFEST is a separate case and also unchanged: initial recovery builds no `Version` from the snapshot, so the column family keeps its empty initial `Version` (see D127928276).

Reviewed By: xingbowang

Differential Revision: D129395958

fbshipit-source-id: 765ec6c60f842618fb2e3a691dcef61bbed02b51
2026-11-03 14:48:35 -08:00
Richard Barnes a303d02bb9 Use folly::coro::coroutine_handle in AsyncFileReader (#15580)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15580

Why: `AsyncFileReader::ReadAwaiter` names `folly::coro::impl::coroutine_handle`. `folly::coro::impl` is an internal alias that let folly switch between `std` and `std::experimental` coroutines. folly now requires C++20 and is dropping the alias, which would break RocksDB builds with `USE_COROUTINES`.

Change: use `folly::coro::coroutine_handle`, the public name folly exports for the same type. This works with both current and older folly releases, so no folly version check is needed. Behavior is unchanged.

Scope: only affects builds with `USE_COROUTINES=1` (folly coroutine support).

___

landed-with-radar-review

Differential Revision: D129467132

fbshipit-source-id: 81e87588a61ce7e73426336a2e91ec80f7ad1ba6
2026-11-03 14:44:18 -08:00
cm-rockee e15742470e Use ImmediateExit for db_stress MANIFEST verification failures (#15575)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15575

**Why:** when db_stress's MANIFEST-avoidance check fails on reopen, the crash test can report a misleading `Assertion 'property_info != nullptr' failed` in `DBImpl::DumpStats()` instead of the real failure. T287969622 collected four such hits (Sep 9 to Nov 1). Each started with a `FATAL: MANIFEST file grew by N bytes ...` or `FATAL: MANIFEST file was recreated ...` line from `StressTest::VerifyManifestNotRewritten()`. The task title, and the signature the automation groups by, came from the secondary assert. When the race does not happen, the same failure is filed under its own title instead (for example T288683405).

**Previous behavior:** `StressTest::Reopen()` calls `VerifyManifestNotRewritten()` right after `Open()`, so the DB is open with live background threads, including the stats-dump `Timer`. The three STRICT-mode failure paths in that function (MANIFEST recreated, MANIFEST grew, CURRENT updated) called `exit(1)`. `exit()` runs static destructors. If the stats timer fires during that teardown, `DBImpl::DumpStats()` looks up a DB property in an already-destroyed static map and asserts, so the process aborts with signal 6. The Sep 9 analysis on T287969622 identified this race.

**New behavior:** those three paths call `port::ImmediateExit(1)`, which wraps `_exit()`. The process prints the same FATAL message on stderr and exits with code 1 without running static destructors, so the stats timer can no longer turn it into a `DumpStats` assert. The crash test now reports the real failure.

**Implementation:** replace `exit(1)` with `port::ImmediateExit(1)` at the three STRICT-mode failure sites in `VerifyManifestNotRewritten()`. D108298839 added `port::ImmediateExit()` for this kind of failure: an exit after DB open with background threads running. Other post-open db_stress exits already use it (`FinishInitDb()`, `TrackExpectedState()` and the open-error paths in `Open()`).

**Safety and limitations:** db_stress only; no engine, API or format change. The remaining `exit(1)` calls in `db_stress_test_base.cc` are option-validation paths that run before the DB is opened, so they are left alone. `_exit()` skips flushing stdio buffers, so any buffered stdout from that run can be lost. The FATAL text goes to unbuffered stderr, as with the existing `ImmediateExit` sites. This does not fix the MANIFEST-avoidance failures themselves. T287969622 tracks those: one is fixed by D127204400, one (leftover `async_wal_precreate` WAL) still looks open, and the "MANIFEST recreated" variant is unexplained.

Requested by: Peter Dillinger (peterd)

Reviewed By: pdillinger

Differential Revision: D129421184

fbshipit-source-id: b3ab9f60200fd785b3ecfbf9bac35093ff7f4212
2026-11-03 11:37:19 -08:00
Peter Dillinger 2a2e19f51b Remove Codex review support from GitHub CI (#15570)
Summary:
Before this change, public GitHub CI supported both Claude and Codex AI code reviews through shared reusable workflows. Codex auto-review on PRs was already disabled (https://github.com/meta/rocksdb/issues/14933), but manual Codex reviews were still available via `/codex-review` and `/codex-query` PR comments.

We don't expect to need Codex as an additional review layer, largely because internal reviews are more convenient, and Claude reviews are sufficient for public CI. This change removes the Codex wrapper workflows, the Codex comment parser script, and all Codex-specific steps from the shared review workflows. The shared workflows otherwise keep their provider-parameterized structure.

If Codex review support is ever resurrected, it will need a security audit.

Repo admins can delete the OPENAI_API_KEY secret after this lands.

Pull Request resolved: https://github.com/meta/rocksdb/pull/15570

Test Plan: These workflows run from the base/default branch, so this PR's own CI does not exercise them; the first Claude review after merge will.

Reviewed By: archang19

Differential Revision: D129398405

Pulled By: pdillinger

fbshipit-source-id: 0628d20a61f6e9c5466d59443e422b71f00cfaf2
2026-11-03 10:01:53 -08:00
Omkar Gawde 6bc72c508b Fix the empty-level assert and wrong level labels in compaction resume (#15568)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15568

Why the change is required:
When a resumable remote compaction (`OpenAndCompactOptions::allow_resumption`) resumes, `MaybeResumeSubcompactionProgressOnInputIterator()` restores the saved output files of the output level and, with per-key placement, of the proximal output level. It reads their table properties with `CompactionJob::ReadOutputFilesTableProperties()`. With per-key placement, a debug build aborted on resume when one of the two levels had no output files, and a failure on a proximal level file was logged as an output level failure.

Fix: allow a level with no output files. A level can be empty when the progress is saved, for example when every key so far goes to the last level. `ReadOutputFilesTableProperties()` then failed `assert(!output_files.empty())`. Release builds compile the assert out, and the rest of the restore path already handles an empty list: there are no properties to read, no files or output records to restore, and no file numbers to skip. The assert is removed, with a comment that the list can be empty.

Fix: label the level correctly. `level_type` was a function-level `static`, so the first call in the process fixed the label for every later call. The only caller also did not pass `is_proximal_level`, so it always took its default of `false`. The caller's own log printed "output levelfiles", with a missing space. `level_type` is now a normal local variable, the caller passes `is_proximal_level`, and the parameter default is removed so that a later caller cannot omit it.

Safety and limitations:
Release behavior does not change, except for the log and status text. The empty-level case needs per-key placement (the compaction outputs to the last level and `preclude_last_level_data_seconds` is set) and saved progress with files at only one of the two levels. Progress is not saved while per-key placement writes blob files. A related case, where the other level still has an open file when the progress is saved, is fixed separately in D128238210.

Reviewed By: jaykorean

Differential Revision: D128228840

fbshipit-source-id: 59981a38e2d8a0964bfa659cc3f8bfa1a7c3954a
2026-11-03 09:41:27 -08:00
Chaitanya Datye 75e965d2a6 Validate table factory options when creating a column family (#15514)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15514

This makes `CreateColumnFamily()` and `CreateColumnFamilies()` run the same table factory option validation that `DB::Open()` runs, and makes the external table adapter forward that validation to the factory it wraps.

`DB::Open()` validates each column family twice: `ColumnFamilyData::ValidateOptions()` checks the plain column family options, and `ValidateOptions()` in `options_helper` walks the configurable objects, which is where `TableFactory::ValidateOptions()` runs. `CreateColumnFamiliesImpl()` only ran the first. A column family whose options the table factory rejects, for example a block-based table with a hash index and no prefix extractor, was therefore created and persisted, and the next `DB::Open()` failed on it. Validating at creation turns that into an `InvalidArgument` from `CreateColumnFamily()`.

The external table adapter returned by `NewExternalTableFactory()` did not override `ValidateOptions()`, and `Configurable::ValidateOptions()` only checks registered options, not `Inner()`, so an external table factory had no way to reject a column family it cannot serve. This matters for ingestion-only factories whose builders fail every write: in a column family without `disallow_memtable_writes`, one stray `Put` makes the next flush fail with a background error that stops writes to the whole DB. Such a factory can now override `ValidateOptions()` to require `disallow_memtable_writes`, and with the first change the check covers column families created at runtime as well as those opened with the DB.

This is a behavior change for callers that create column families with options the table factory rejects. Those calls used to succeed and leave a DB that failed to reopen; they now fail up front. A release note is included.

Changes:
1. `CreateColumnFamiliesImpl()` calls `ValidateOptions(db_options, cf_options)` after `ColumnFamilyData::ValidateOptions()`.
2. `ExternalTableFactoryAdapter::ValidateOptions()` validates its own options and then calls the wrapped factory's `ValidateOptions()`.
3. `ColumnFamilyTest.CreateRejectsOptionsThatOpenRejects` checks that a block-based hash index without a prefix extractor is rejected at creation and that the DB still reopens.
4. RocksJava's `OptionsUtilTest` created a column family whose block-based options `DB::Open()` rejects (`cache_index_and_filter_blocks` and `pin_l0_filter_and_index_blocks_in_cache` with `no_block_cache`, and `block_align` with compression and a 35 KiB block size); it only round-trips the options file, so it never noticed. It now uses a block cache, a 32 KiB block size and no compression for that column family, keeping every other non-default table option it verifies.
5. `ExternalTableTest.WrappedFactoryValidatesColumnFamilyOptions` checks that an external factory's `ValidateOptions()` is reached directly, through `CreateColumnFamily()` and through `DB::Open()`.

Reviewed By: xingbowang

Differential Revision: D128151632

fbshipit-source-id: 348e7820aa3995ddfa798b02bb38dddb8528e76c
2026-11-02 23:11:33 -08:00
Peter Dillinger e189b2724b Disable test flaky in GitHub Actions (#15567)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15567

EnvTest.GenerateRawUniqueIdTrackRandomDeviceOnly, to revisit in a few
months.

Differential Revision: D129271441

fbshipit-source-id: 3b9d7ec54f186bd8433249387f6ede81d83810c9
2026-11-02 21:20:49 -08:00
cm-rockee cba23b972b Fix remote compaction pool test flakes from 10 s wait timeouts (#15566)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15566

**Why:** The tests added with the separate remote compaction pool (D124139026) failed in the RocksDB contrun `check` job twice on Oct 30 (T265642089). The same code passed when the job retried at 14:07.

The failed tests:
- `DedicatedCompactionSubmissionExceptionTest.DrainsAcceptedSubcompactionsBeforeReturning/3,4,5`
- `RemoteCompactionPoolGateTest.RuntimeSwitchPreservesScheduledJobs/0,1`
- `DedicatedCompactionFallbackTest.CompactFilesFromLowWorker/0,2`

**Cause:** These tests wait for background progress, such as a remote compaction reaching the service's `Wait()`, a local subcompaction starting, or a manual compaction returning. Each wait is on a condition variable that the background work signals through sync point callbacks, the test compaction service or a listener, and it gave up after 10 s, failing the test. In the failing jobs each test took 15-125 s instead of about 1 s. The job runs 253 test processes at once with `TEST_TMPDIR` on `/tmp`, because `/dev/shm` is only about 100 GB on these hosts, so every flush and remote compaction output fsyncs a busy shared disk.

**Fix:**
- All 25 of these waits, including three `WaitForCompactOptions::timeout` values, now use a 10 minute `kProgressTimeout` instead of 10 s. A wait still returns as soon as its event happens, so a passing run takes as long as before; the timeout only makes a hang fail the test instead of blocking it. The one intentionally short timeout, a 10 ms `WaitForCompact()` that is expected to time out while remote compactions are held, is unchanged.
- `DedicatedCompactionPoolTest`, the fixture of all these tests, sets `SpecialEnv::skip_fsync_`, as `column_family_test` does. These tests check scheduling, routing and draining, not durability, and skipping fsync keeps them fast on a busy disk.

The waits are not converted to sync point dependencies: they already wait for events, and several wait for counts (such as N remote `Wait()` calls in progress), which a sync point dependency cannot express.

Test only.

Requested by: Peter Dillinger (peterd)

Reviewed By: pdillinger

Differential Revision: D128766650

fbshipit-source-id: 2f065678fe072cb6d9b64d47bcdeb76e6bf978ff
2026-11-02 20:31:45 -08:00
cm-rockee 47570c7418 Fix FlushLargestAcrossDBsPicksAtomicFlushDB flake on loaded hosts (#15565)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15565

**Why:** `DBWriteBufferManagerTest.FlushLargestAcrossDBsPicksAtomicFlushDB/0` failed in the RocksDB contrun `clang_check` job on Oct 25 (T265642089). It expected that only `atomic_db` gets flushed, but `db_` was flushed too.

**Cause:** with `kFlushLargestAcrossDBs`, the coordinator gives the DB it picks a 20 ms lease (`kFlushWorkCycleMicros`), starting when the handoff worker runs. If the lease expires while the WBM still needs to flush, the coordinator moves on to the next largest DB. That is intentional: it is how it bypasses stalled candidates. On a heavily loaded host, the handoff worker can take longer than 20 ms to get `atomic_db` to switch its memtables, so the coordinator also flushes `db_`.

I reproduced this with 150 concurrent copies of the test next to 200 CPU-burning processes on a 160-core host, plus logging in the WBM:
- the coordinator submitted `atomic_db` (626 KB flushable);
- the lease expired 20 ms later, with mutable memory still 989 KB;
- it then submitted `db_` (362 KB), and `db_` flushed first;
- `atomic_db`'s handoff ran 8 ms after that, and its flush followed.

**Fix:** a new `WriteBufferManager::ExpireFlushHandoffLease:Expired` sync point. The test asserts that `db_` was not flushed only when no lease expired. `atomic_db` must still flush both of its column families in either case.

This keeps the test's check of the ranking for the normal case. If the lease is not supposed to start before the chosen DB has switched its memtables, the fix belongs in the WBM instead; this diff does not change WBM behavior.

Requested by: Peter Dillinger (peterd)

Reviewed By: pdillinger

Differential Revision: D128766672

fbshipit-source-id: 746ebf858e5e31657bfe3d4ea4176252919b9af9
2026-11-02 20:26:10 -08:00
Jay Huh b07a47dd5b Check every Status in the trace analyzer's statistics passes (#15564)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15564

`TraceAnalyzerTest.LibraryResultWithoutOutputFiles` aborts with "Failed to check Status" in ASSERT_STATUS_CHECKED builds. `MakeStatistics()`, `MakeStatisticQPS()` and `ReProcessing()` start with a default `Status s` that is assigned only when an output file is set, so a library run without output files destroys it unchecked. `Analyze()` also leaves the `EndProcessing()` status unchecked when an earlier step failed.

Each status is now declared where it is assigned, and `Analyze()` marks the `EndProcessing()` status checked when it returns the earlier error.

___

landed-with-radar-review

Differential Revision: D129241122

fbshipit-source-id: 7e8c1d0f5a7042f1eee7263d3d2af63bf669606c
2026-11-02 18:29:21 -08:00
cm-wang 49a8f7d762 Handle a failed blob direct write seal: roll back picked memtables and keep the unsealed blob file (#15482)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15482

X-link: https://github.com/facebook/rocksdb/pull/15482

With blob direct write, `FlushMemTableToOutputFile()` and `AtomicFlushMemTablesToOutputFiles()` pick the memtables and then seal the write-path blob files with `PrepareFlushAdditions()` before running the flush job. A seal can fail with ENOSPC or an I/O error while writing or syncing the footer. Two bugs follow.

**Stranded memtables.** The flush job is cancelled without running. `FlushJob::Cancel()` only releases the base version, so the picked memtables stay marked as flush in progress. No later flush picks them up (`rocksdb.mem-table-flush-pending` reads 0), and error recovery, which flushes before it clears the error, waits for them forever. The comment in that path already expects a later flush to retry and publish the same blob file additions.

**Dropped blob file.** When the seal itself fails, `PrepareFlushAdditions()` has already popped the file from its generation, and `SealDeferredFile()` stops tracking it. `WriteBlob()` does the same when sealing a full active file fails. The memtable still points into that file. Once the memtables are flushable again, a retried flush finds no file to add and installs a table file whose blob file is not in the Version or the MANIFEST. The file is also untracked, so the next full obsolete-file scan, or the next open, deletes it, and the values are lost. In debug builds, `Close()` asserts on the cached, untracked file.

Fix:
- Roll back the picked memtables of a flush job that did not run with `RollbackMemtableFlush()`, in both the single column family and the atomic flush paths. Such a job never got an output file number, so this is the plain rollback.
- Keep a blob file whose seal failed tracked and in its generation, with the seal status. The writer cannot be finalized again, so every later flush of that generation fails with that status instead of publishing table files without the file. A failed active file joins the next generation, and its partition opens a new file for later writes.

Reviewed By: xingbowang

Differential Revision: D126837498

fbshipit-source-id: f80bcefbd6a4435f6ca7451c2a14071c537da9af
2026-11-02 13:58:31 -08:00
cm-wang 4500871744 Keep a sealed blob file when the space limit is reached, and report it outside the blob manager mutex (#15483)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15483

X-link: https://github.com/facebook/rocksdb/pull/15483

With blob direct write and an `SstFileManager` space limit, sealing a write-path blob file reports it through `BlobFileCompletionCallback::OnBlobFileCompleted()`. Once the limit is reached, that returns `Status::SpaceLimit` and sets the background error under the DB mutex. Two bugs follow.

**Data loss.** `FinalizeBlobFile()` returned that status as a sealing failure, although the footer was already written and the file was complete. `PrepareFlushAdditions()` (flush) and `WriteBlob()` (active file full) then dropped the sealed file: it was no longer tracked by the partition manager and never reached the MANIFEST, so the obsolete-file purge deleted it while the memtable still held blob indexes pointing into it. Reads of those keys returned `IO error: No such file or directory`, and the values were gone for good, including after error recovery flushed the memtable.

**Deadlock.** `OnBlobFileCompleted()` took the DB mutex while its caller held the partition manager's `mutex_`. `SwitchMemtable()` holds the DB mutex and calls `RotateCurrentGeneration()`, which takes `mutex_`. A memtable switch racing with the space-limit report deadlocked both threads, and everything else that needs the DB mutex (writes, `DumpStats()`) hung behind them.

Fix: blob direct write handles the space limit the way an SST flush does. The blob bytes are already on disk when a file is sealed, so failing the seal saves no space.
- A seal that reaches the limit still succeeds. The partition manager completes the file through a new `OnKeptBlobFileCompleted()`, which registers it with the `SstFileManager` at the size its `BlobLogWriter` wrote (so the registration cannot fail), reports its creation to listeners as successful, and only returns whether the limit is reached. It never takes the DB mutex.
- A write that rolls over a full file succeeds, then sets `SpaceLimit` as a `kFlush` background error after `mutex_` is released. A flush that seals a file commits it normally, and the flush's existing post-commit `SstFileManager` check sets the same error; setting it before the commit would make the flush roll back. Either way writes stop, reads keep working, and `SstFileManager` recovery resumes the DB once space is freed.
- So the space limit is soft for blob direct write. A direct-write blob file is counted only once it is sealed, and after the error the memtables waiting for a flush are not flushed, so usage can exceed the limit by up to about `blob_file_size` × `blob_direct_write_partitions` for each memtable not flushed yet (up to `max_write_buffer_number`). This is documented on `enable_blob_direct_write`. Blob files written by `BlobFileBuilder` (flush and compaction) still fail their job and are deleted.
- `MaybeTransformBatchForBlobDirectWrite()` keeps a failed batch's partial rollback records, as `TransformBatch()` promises. Blobs that a failed batch already wrote are counted as garbage, so their sealed file can still be dropped.

Reviewed By: archang19, xingbowang

Differential Revision: D126837497

fbshipit-source-id: c06241899f108fbbf5ee0125d191214bbfee8e0f
2026-11-02 13:58:31 -08:00
Jay Huh b4d0865a93 Make the trace analyzer a library API (#15561)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15561

Why the change is required:
RocksDB's query trace analyzer (`tools/trace_analyzer_tool.cc`, described in the "RocksDB Trace, Replay, Analyzer, and Workload Generation" wiki) can only be run as the `trace_analyzer` command line tool. Code inside RocksDB, such as experimental live tuning, cannot use it: it is built only into the tools and test libraries, it reads its configuration from global gflags, it writes its results to files, and on bad input it prints and calls `exit(1)`. Live tuning therefore carried its own small trace summarizer that computes much less (no QPS, no size statistics, no per-type key statistics, no top keys).

Previous behavior:
`TraceAnalyzer` lived in `tools/trace_analyzer_tool.{h,cc}` (ANALYZER_LIB_SOURCES), under `#ifdef GFLAGS`, configured by 29 `FLAGS_*` globals. Results went to stdout and to files under `--output_dir`; with no `--output_dir`, the files were opened under "/". `ta_[kPutEntity]` was never initialized, so whether PutEntity statistics were collected was undefined.

New behavior:
- `TraceAnalyzer` is in the RocksDB library (`trace_replay/trace_analyzer.{h,cc}`, in LIB_SOURCES), with no gflags. It takes a `TraceAnalyzerOptions` whose fields are the tool's flags, and either a trace path or an open `TraceReader`.
- `Analyze()` runs the whole analysis; `GetResult()` returns the statistics in memory (per operation type and column family: count, unique keys, key and value size sums, peak and average QPS, access count distribution, top keys; plus the trace's totals) without draining anything; `PrintStatistics()` prints them as the tool does.
- With an empty `output_dir`, no output files are written and the statistics are only kept in memory. The overall QPS peak and average are computed even without the QPS file.
- Errors are returned as `Status` instead of `exit(1)`: a bad `print_correlation`, a `value_interval` of 0 or less (which used to divide by zero), a file that cannot be created, a bad key in a whole key space file.
- New `analyze_put_entity` option and `--analyze_put_entity` flag (off by default), which also fixes the uninitialized PutEntity setting.
- `tools/trace_analyzer_tool.cc` is now a thin gflags front end that fills `TraceAnalyzerOptions` and runs `TraceAnalyzer`; its flags and output are unchanged.

Implementation:
- `tools/trace_analyzer_tool.{h,cc}` are moved (with history) to `trace_replay/trace_analyzer.{h,cc}`; `FLAGS_x` became `options_.x`; the `LDBCommand` hex helpers (tools-only) are replaced by local ones with the same output; the option-name maps are file-local and const.
- File writes are skipped when their file is not open, so statistics that were computed only on the way to a file (e.g. prefix top-k) are still computed without files.
- `EndProcessing()` closes the output files (including the QPS files) and no longer prints; the tool prints before it, unless `--no_print`.

Safety, compatibility, and limitations:
- The `trace_analyzer` tool's flags, defaults and output files are unchanged; `trace_analyzer_test`, which drives the tool through argv, passes unchanged.
- `TraceAnalyzer` is an internal API (not under include/), for code inside RocksDB.
- The analyzer keeps every accessed key in memory per type and column family; callers that analyze long traces should sample (`sample_ratio`, or the tracer's sampling).
- Diagnostic `fprintf`s on errors that also return a Status are kept as they were.

Reviewed By: xingbowang

Differential Revision: D129139885

fbshipit-source-id: a3eec6ce76b8199352d7bf52c3f109cf2f37d02b
2026-11-02 12:30:38 -08:00
Omkar Gawde 5cabe9439d Fix uninitialized and out-of-bounds reads from a damaged remote compaction result (#15562)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15562

Why the change is required:
With a damaged or hand-made remote compaction result, the primary could use a status built partly from uninitialized bytes as the compaction outcome, install an output SST in the wrong level under per-key placement, or read out of bounds in a release build when it logs the remote status. Results that RocksDB writes are not affected.

Previous behavior:
`CompactionServiceResult::Read()` parses with `OptionTypeInfo`, which sets only the fields present in the string.
- `StatusSerializationAdapter` did not initialize `code`, `subcode`, or `severity`. A missing field gave the status an uninitialized byte, and on the success path the primary used that status.
- `CompactionServiceOutputFile` did not initialize `marked_for_compaction` or `is_proximal_level_output`, and `ParseVector` default-initializes each element. A missing flag was indeterminate when the primary checked and installed the file.
- Parsed status values were not range-checked. With a non-OK code, a subcode at or above `kMaxSubCode` makes `Status::ToString()` read past its message table in release builds. Debug builds assert.
- A failed status parse, such as bad hex in the message, still replaced the caller's `status` with a half-parsed one. The primary does not use the result after a failed `Read()`.

New behavior:
A missing status field takes its `Status::OK()` value, and a missing output-file flag reads as false. A code, subcode, or severity outside its enum makes `Read()` return `InvalidArgument`, which the primary handles like any other parse error. A failed status parse leaves the caller's `status` unchanged. Complete results with in-range values parse as before.

Implementation:
The five members get initializers. `StatusSerializationAdapter::IsValid()` checks the enum ranges, and the status parse lambda returns on any error before it assigns the status.

Safety, compatibility, and limitations:
The serialized format does not change. A result from a newer worker with a status value that this primary does not know now fails to parse: after a `kSuccess` wait the primary falls back to local compaction, and after a `kFailure` wait it reports `Incomplete`. Before, it built an invalid `Status`. `Read()` still parses in place, so when a later field fails, the fields parsed before it stay written.

Reviewed By: jaykorean

Differential Revision: D128231672

fbshipit-source-id: 8335decb90ae452ade71ce2cd011bfed200d3945
2026-11-02 12:28:04 -08:00
Xingbo Wang 24282f7418 Support merge-before-filter compaction (#15517)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15517

## Why

Some useful compaction filters only make sense on merged results. For example, a filter that removes counters below a threshold must see the counter's total, not each `+N` operand. With the current order, the compaction filter runs before the merge operator, so it sees (and can remove) individual operands and never sees the merge result.

Important limit: compaction can only merge what is in its own inputs. If a key's base value or older operands are in files outside the compaction, the filter sees a partial merge result (or single operands), not the logical value of the key.

## Behavior

- **Default (unchanged):** `CompactionFilter::MergeOrder::kFilterBeforeMerge`. Operands are filtered one by one before merging.
- **New opt-in:** a filter or factory returns `kMergeBeforeFilter` from the new `GetMergeOrder()`. During compaction the merge operator runs first, then the filter sees:
  - the full merge result as `kValue` / `kWideColumnEntity`, if the inputs include the base (Put or tombstone), or the compaction is bottommost with no older entries;
  - otherwise the operands as `kMergeOperand`: one partial result from `PartialMergeMulti`, or each operand by itself if partial merge declines.
- A filter result is not merged again in the same compaction. Filters that only implement FilterV2/V3 also work.
- A merge result is offered only if no snapshot can see any of its operands. Plain values are filtered as before, regardless of snapshots.
- `kPurge` on a value is treated as `kRemove` (ordinary Delete), because SingleDelete does not mix with Merge history. `kPurge` and `kChangeWideColumnEntity` on an operand fail the compaction with NotSupported.
- `kRemoveAndSkipUntil` on any operand of an uncombined batch drops the whole batch, the same result as the default order.
- `COMPACTION_KEY_DROP_USER` keeps the same meaning in both orders (skipped ranges are not counted).
- The new order applies only to compaction. Flush, recovery, MemPurge, and repair keep the default order.

### Example

Counter merge operator (add) with a filter that removes counters below 10. The compaction input for one key is `Merge(+3)`, `Merge(+5)`, `Put(4)`. Before: the filter sees the raw operands `+3` and `+5`, removes both, and the output is `Put(4)`; it never sees the merged value. After: the merge runs first (4 + 5 + 3 = 12), the filter sees `kValue` "12", and keeps it. If the base is not in the compaction, partial merge gives `+8` and the filter sees it as `kMergeOperand`.

## Implementation

- `MergeHelper` carries the merge order and records each merge outcome (full, partial, uncombined) and whether it can be filtered under snapshots.
- `CompactionIterator` reads the order from `MergeHelper` and filters after the merge. A full result changed to Delete goes through the normal `ProcessDeletion()` path. Uncombined operand batches are filtered as a whole before any of them is output, so a skip decision can drop the full batch.
- Order checks (`GetCompactionFilterMergeOrder()`) run outside the DB mutex. A factory's non-null filters must declare the same order; otherwise the compaction fails with InvalidArgument. A factory that returns a null filter means no filtering, which is valid with either order.
- **Remote compaction:** `CompactionService::SupportsMergeBeforeFilter()` (default false) lets a service receive new-order jobs; otherwise they run locally. The request carries `merge_before_filter`, and the worker acknowledges the order it actually used. A missing or wrong acknowledgment (for example, from an older worker) falls back to local compaction before any output is imported. Real compaction errors stay errors. A worker with no filter accepts either order.
- **Early rejection:** DB open and CF creation reject a new-order declaration with user-defined timestamps, WritePrepared/WriteUnprepared transactions, stacked BlobDB, or a factory that selects non-compaction table file creation. This avoids a background error that would stop writes at the first compaction.
- `ShouldFilterTableFileCreation()` checks use an exhaustive switch over `TableFileCreationReason`, so a new reason must be classified.
- **Bug fix included:** `MemPurge()` returned with the DB mutex unlocked when the flush filter failed a check (for example, `IgnoreSnapshots() == false`). The new order check adds another early return on the same path, so the re-lock is fixed here, with a `bug_fixes` release note.
- **Stress test:** with `merge_before_compaction_filter`, the db_stress filter now makes real decisions that never change what a reader sees: `kChangeValue` with the same bytes for about 1 in N records (`merge_before_filter_change_one_in`); with `merge_before_filter_remove_deleted`, `kRemove` for keys deleted in the expected state, and `kRemoveAndSkipUntil` over just that key in full compactions (where every version of the key is an input). It also aborts if a value it sees is not a well-formed stress value for its key, or if the new-order factory is asked to filter a non-compaction file. It prints per-type decision counts at the end of a run. The crash test picks these modes randomly and uses merges in about 3/4 of new-order runs; the remove/skip mode uses the same no-snapshot settings as the default-order filter.
- `Repairer` only marks `BuildTable()`'s `IOStatus` as checked, because `BuildTable()` can now return early before reading it. Repair behavior does not change.

## Safety and limitations

- The default order and existing filters behave as before. There is no SST format change, no new CF option, and no C/Java binding.
- Not supported in the new order: user-defined timestamps, WritePrepared/WriteUnprepared transactions (SnapshotChecker), and stacked BlobDB. Integrated BlobDB is supported.
- Do not use SingleDelete on keys handled by a new-order filter, even after switching back to the default order.
- How operands are grouped into partial results can differ between compactions, and SST files keep no record of earlier groupings. Filter decisions must be correct for any grouping.
- **Stress test limits:** the stress merge operators keep only the newest operand, and every stress filter decision is chosen so that reads do not change. So both orders give the same visible result, and the oracle checks robustness of the new paths (wrong output key or sequence, lost or returning data, buffer and pinning errors, crash recovery, remote fallback), not whether the filter saw the right input. A same-bytes rewrite also cannot detect a change that RocksDB ignores. Skips only run in full compactions, which are rare in leveled runs. The decision counters print only when db_stress exits normally (not in blackbox kills).
- **Unit tests cover the rest:** which input the filter sees (full, partial, uncombined), the snapshot rule, changed values in the output (`MergeBeforeFilterFullReplacementAndFailure`, `MergeBeforeFilterPartialAndDecline`, remote full/partial tests), skip and removal results, kPurge handling, unsupported decisions, early rejection, remote routing and fallback, and BlobDB.
- Validation: the full suites of the touched test binaries pass, and local db_stress and crash-test runs with the new modes pass. See the Test Plan for details and the benchmark results.

Reviewed By: pdillinger, jaykorean

Differential Revision: D128313536

fbshipit-source-id: 996dd31a78636f7968005938365ce94d11cbe8b3
2026-11-02 11:05:34 -08:00
Peter Dillinger ea5db0c142 Fix format_version=8 common prefix extending past a user key (#15556)
Summary:
## Background / Motivation
With format_version=8 (not yet the default) and optimize_key_common_prefix, each data and index block stores one common prefix. The design and code intend that prefix to be a prefix of every user key in the block. Seek and SeekForPrev for bytewise and reverse bytewise comparators rely on that: they strip the prefix from the target and from the block's keys, then compare what remains (the "prefix-optimized seek"). That preserves ordering only if the stripped bytes are user key bytes.

Before this change, BlockBuilder limited the prefix to the user key of the first key and of each restart key. For other keys, it limited the prefix only to the internal key bytes shared with the previous key. ReverseBytewiseComparator orders a user key after longer user keys that extend it, so those shared bytes could reach past a user key into its internal key footer. For example, in a block with `ab\x01c` followed by a Put of `ab`, whose internal key begins `ab\x01` because the first footer byte is the value type, the stored prefix was `ab\x01`. Because internal keys are not bytewise comparable, the prefix-stripped comparisons then gave wrong results:
* Get, MultiGet and seeks could miss such a key. A missed deletion would expose an older value.
* Iteration could skip such a key or fail a debug assertion, because iterators seek internally after skipping many versions of a key and when changing direction.
* A compaction with subcompactions seeks to the start key of each subcompaction, and could skip versions of such a key there. The check of the number of input records (compaction_verify_record_count, on by default) then fails the compaction with Corruption; without that check, the skipped versions would likely be dropped from the compaction output.

Index blocks of internal keys with index_block_restart_interval > 1 could be affected the same way, theoretically including filter partition indexes. RocksDB 11.10 through 11.13 wrote such blocks.

Other comparators were not buggy:
* With BytewiseComparator, the prefix cannot extend past a user key, because a user key sorts before every longer user key that extends it.
* With other comparators (optimize_key_common_prefix=kEnabled), a prefix extending past a user key is apparently benign, at least for now, because their reads reconstruct full keys before comparing.

This bug was found by an agent in D126805261, but this solution is less deeply intrusive in the most performance-critical areas and generally improves performance with a bundled optimization.

## This Change
This change fixes the writer, makes the reader handle "bad" blocks that have already been written, and bundles an optimization of the prefix-optimized seek that offsets the cost of the reader fix.

### Write Path
BlockBuilder now also limits the prefix to the user key of each non-restart key, so new blocks store only a common user key prefix, with any comparator.

There is no format change. Blocks written with this change meet the invariant that all format_version=8 readers assume, so older versions read them correctly.

### Read Path
When a data or index block (including a filter partition index block) of a table opened with ReverseBytewiseComparator is loaded, the reader checks whether the stored prefix extends past a user key. If it does, seeks within that block use the existing full-key search (as for other comparators) instead of the prefix-optimized seek. BlockBasedTable::Open() enables the check; blocks inserted into the block cache while building a table (prepopulate_block_cache) come from the fixed writer and are not checked.

Checking only the block's last entry is enough. A user key shorter than the stored prefix is extended by every other user key in the block, so in reverse bytewise order its versions are the block's last entries. None of them can be a restart key, since the prefix was limited to each restart key's user key, so they all follow the last restart point. The check therefore costs:
* for a data block, decoding the entry headers of the last restart interval;
* for an index block of internal keys, reading the last entry with an iterator (a single entry with the default index_block_restart_interval of 1);
* for an index block of user keys, nothing: its prefix was computed from user key bytes.

The result is a new Block flag. Block's flags are now bit-fields so that sizeof(Block), allocated for every cached block, stays 96 bytes (one more bool would have made it 104).

Two other approaches to reading existing blocks were considered:
* Handling affected blocks within the prefix-stripped comparisons, as D126805261 did, adds work to every (reverse-)bytewise seek: 63 to 98 more instructions per block seek, and up to 12x slower seeks for reverse bytewise targets that sort outside a block's prefix.
* Rejecting affected blocks as corruption would keep the seek code unchanged. But reads that work today, such as lookups of other keys and most scans and compactions, would then fail. That includes the compactions by a fixed version that would rewrite the affected files without the problem (compaction by an unfixed version can write the same layout again).

### Bundled optimization: comparing without virtual calls
The reader fix needs a per-iterator decision of whether to use the prefix-optimized seek, which already requires knowing that the comparator is BytewiseComparator or ReverseBytewiseComparator. Each block iterator now records that decision, made once when it is initialized, as a PrefixOptimizedSeek value (none, bytewise, or reverse bytewise). Previously, every seek compared the comparator with BytewiseComparator() and ReverseBytewiseComparator() (out-of-line calls), and then compared keys through InternalKeyComparator and the virtual Comparator::Compare(), although the order was known to be bytewise or reverse bytewise.

Because the same value tells which of the two orders applies, the prefix-optimized seek now compares keys directly (BlockIter::CompareKeyForPrefixOptimizedSeek(): a byte comparison of the user key parts, negated for reverse bytewise, then the internal key footer, with the file's global sequence number applied as in CompareKey()). It still counts user_key_comparison_count.

This is bundled, rather than a separate change, so that the fix "pays as it goes": the reader fix alone added a small cost to seeks (and grew Block until the bit-field change), while this optimization reuses the state the fix introduces and more than offsets that cost in most measurements (below).

Pull Request resolved: https://github.com/meta/rocksdb/pull/15556

Test Plan:
## Unit Tests
New tests in db_etc3_test, all using ReverseBytewiseComparator and format_version=8. A test sync point in BlockBuilder restores the old prefix computation so that tests can write affected blocks. A sync point at detection lets tests confirm that affected blocks were detected.
* CommonKeyPrefixWithShorterKeyAfterLongerKey: the fixed writer with Put and Delete forms of the problematic key order; no block is detected as affected.
* CommonKeyPrefixReadsBlocksWithShorterKey: Get, MultiGet, Seek and SeekForPrev on a data block whose stored prefix extends past a user key, before and after a compaction with two subcompactions.
* CommonKeyPrefixIteratesBlocksWithShorterKey: forward and reverse scans, with and without a snapshot, that seek internally into such a block.
* CommonKeyPrefixIndexBlockWithShorterKey: an affected index block of internal keys.
* CommonKeyPrefixRandomizedReverse: random DBs whose user keys often extend each other, written with the old or new prefix computation and with varied block sizes, restart intervals, index types (including partitioned, with partitioned Bloom filters), separated KV storage, data block hash index, block_protection_bytes_per_key, cache_index_and_filter_blocks, prepopulate_block_cache (with the new writer), and snapshots, sometimes including an ingested file with a global sequence number, checked against a model before and after compaction. With the new writer, it also checks that no block is flagged for the full-key search.

I confirmed that the last four tests fail with the BlockBuilder change but without the reader changes, with missing or wrong results or debug assertion failures. The randomized test also fails without the filter partition index check (debug assertion in the comparison) and if blocks are checked while building a table (index blocks of user keys flagged). The direct comparisons are also covered by existing tests of format_version=8 tables and ingested files (e.g. table_test, block_based_table_reader_test, external_sst_file_test).

## Performance
Release builds from GCC 11 and clang 21, each with the default (-march=haswell) and with PORTABLE=0 (-march=native, here znver3 or newer); single thread pinned to one CPU; interleaved runs; median of paired time ratios vs. base (lower is better); user-space instructions per operation.

Block seeks (4KB blocks, 14-byte common prefix, 92 keys per data block, 128 entries per index block; ranges are over the two builds per compiler). "Re-initialized" means the block iterator is initialized before each seek, as for each block that a point lookup visits.

| Block seek | GCC: instructions, time | clang: instructions, time |
|---|---|---|
| Index seek | +49 to +50, 0.989 to 1.003 | -83, 0.974 |
| Index seek, re-initialized | +67 to +68, 0.997 to 1.009 | -38, 0.978 to 1.006 |
| Data seek | -241 to -259, 0.936 to 0.940 | -250, 0.913 to 0.926 |
| Data seek, re-initialized | -221 to -241, 0.946 to 0.956 | -206, 0.933 to 0.952 |
| Data SeekForPrev | -344 to -369, 0.925 to 0.941 | -290, 0.934 to 0.944 |
| Reverse bytewise index seek, re-initialized | +58 to +59, 1.003 to 1.009 | -45, 0.998 to 1.002 |
| Reverse bytewise data seek, re-initialized | -227 to -247, 0.944 to 0.969 | -216, 0.936 to 0.952 |

With GCC, the direct comparison is inlined into data block seeks but called out of line (three call sites) from IndexBlockIter::SeekImpl(), hence the small instruction increase there, with no measurable time change.

db_bench (10M entries, 16-byte keys, 100-byte values, fully cached), time:

| db_bench | GCC | clang |
|---|---|---|
| readrandom, bytewise | 0.975 to 1.013 | 1.001 |
| readrandom, reverse bytewise | 0.985 to 0.986 | 0.995 to 1.006 |
| seekrandom, bytewise | 1.007 to 1.018 | 0.980 to 0.983 |
| seekrandom, reverse bytewise | 1.005 to 1.023 | 0.971 to 0.973 |

Per operation, readrandom ran 47 to 81 fewer instructions with GCC and 261 to 275 fewer with clang; seekrandom ran 12 to 81 fewer with GCC and 188 to 315 fewer with clang.

With GCC, seekrandom (a new DB iterator per operation) was 0.3% to 3.4% slower than base across repeated runs (median about 1.8%), while running fewer instructions; with clang it was 2-3% faster. GCC seekrandom is front-end bound, with about 1300 L1 instruction cache misses per operation, and with GCC this change (and earlier versions of it, without the bundled optimization) adds 5-6% to those misses. Shifting code by 16 bytes to 3 KB left each build's miss count unchanged, and this change's hot functions are no larger in total than base's. So the GCC seekrandom difference is not from added work. It looks like an effect of GCC code generation and layout: a change with no behavior change, adding an unused field to BlockIter, moved base's misses by +3% (and its seekrandom time by -0.8%), though this change's effect is somewhat larger.

Parsing a 4KB data block on load (excluding I/O, checksum verification, decompression, and cache insertion), GCC default build: +8 instructions with bytewise (no check); with reverse bytewise, +415 instructions, time 1.194 (about +37 ns) at block_restart_interval=16 and +145 instructions, time 1.049 at block_restart_interval=4.

Reviewed By: xingbowang

Differential Revision: D129007823

Pulled By: pdillinger

fbshipit-source-id: d9ffa16ed31384dbd8fd5f883b5080b986f7539e
2026-11-02 10:55:17 -08:00
Omkar Gawde d375bbbbd0 Skip SetOptions when no option value changes (#15552)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15552

Why the change is required:
`DB::SetOptions()` does the full update on every call, also when the request changes no value. It installs a new Version and SuperVersion, writes a new OPTIONS file, and deletes obsolete ones. Callers that apply their full configuration on each refresh pay this cost every time. `SetDBOptions()` already skips a request that changes nothing.

New behavior:
`SetOptions()` returns OK and does nothing when each column family in the request is not dropped and the request parses, validates, and changes no value. The full update still runs for:
- A request that names an object option (`table_factory` or one of its fields, `prefix_extractor`, `compression_manager`). For example, `table_factory.filter_policy.bloom_before_level` changes the shared filter policy in place, and a `MutableCFOptions` comparison cannot see that.
- A request after an earlier update left the OPTIONS file or the seqno-to-time worker behind: an OPTIONS file write or a worker update failed, a multi-CF call failed after it changed some column families, or, with `track_options_file_number_in_manifest`, a column family create or drop batch stopped part way. While the OPTIONS file is behind, an unchanged `SetDBOptions()` request also writes it.

Implementation:
- `MutableCFOptionsUnchangedByMap()` parses the request over the current options and compares the result with `MutableCFOptions::operator==`, so doubles compare exactly. It returns false for a request that names an object option.
- `ColumnFamilyData::options_install_pending_` marks a column family that a failed multi-CF call changed but did not install.
- `DBImpl::options_file_stale_` is set by a failed `WriteOptionsFile()` and cleared by a successful one. `DBImpl::seqno_time_worker_stale_` does the same for `RegisterRecordSeqnoTimeWorker()`. A multi-CF call that fails after it changes some column families sets both. A call that fails before it changes anything sets neither, so a request with a bad option does not turn off the skip for later requests. Both flags are protected by `options_mutex_`.
- With `track_options_file_number_in_manifest`, column family create and drop do not call `WriteOptionsFile()`. Instead, the first create or the last drop of a batch commits an OPTIONS snapshot through the MANIFEST. So `CreateColumnFamiliesImpl()` sets `options_file_stale_` unless every create of the batch succeeds. `DropColumnFamilyWithOptionsFile()` sets it after a drop that commits no snapshot and clears it after a drop that does. The drop records the flag right after its MANIFEST commit, before a later seqno-to-time worker update can fail. A snapshot holds the options in effect, so it also clears a flag that an earlier failed OPTIONS file write set.

Safety and compatibility:
- Every failed `WriteOptionsFile()` sets the flag, also in `SetDBOptions()`, `CreateColumnFamily()`, and `DropColumnFamily()`. So the next `SetOptions()` or `SetDBOptions()` call writes the OPTIONS file again. Before this change, the next `SetOptions()` call did this because it always wrote the file.
- A tracked snapshot that lists a dropped or never-created column family was not a correctness problem, because `LoadLatestOptions()` filters a tracked snapshot by MANIFEST membership. Before this change, the next `SetOptions()` call replaced such a snapshot, and the new flags keep that result.
- A skipped request does not re-anchor periodic-compaction phasing or schedule flushes and compactions, and it cannot fail on an OPTIONS file write. For example, `EnableAutoCompaction()` on a column family with auto compaction already on now does nothing.
- `MergeTest.MergeWithCompactionAndFlush` sent an unchanged `SetOptions()` request to reach `LogAndApply()`, so it hung with this change. It now sets `report_bg_io_stats`, which changes a value.
- A release note is in `unreleased_history/behavior_changes/`.

Reviewed By: xingbowang

Differential Revision: D128887760

fbshipit-source-id: 2a095713e67f79600d9f5ef5588180f03c0e2f8e
2026-11-02 10:30:14 -08:00
Jay Huh 9bfe67777a Make JSONWriter escape strings and write non-finite numbers as null (#15559)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15559

`JSONWriter` (`logging/event_logger.h`) wrote keys and string values between quotes without escaping, and wrote floating-point values through `std::ostream`, which prints `nan` and `inf`. A string containing a quote, backslash, or control character, or a non-finite double, produced invalid JSON in `EVENT_LOG_v1` lines and in `VersionEdit::DebugJSON()` output (for example, a non-hex user key with binary bytes).

`JSONWriter` now escapes `"`, `\`, and control characters (using `\b`, `\f`, `\n`, `\r`, `\t`, or `\u00XX`) in keys and string values, and writes non-finite floating-point values as `null`. Output that was already valid JSON is unchanged.

This lets other code that builds JSON log lines use `JSONWriter` directly instead of carrying its own escaping helpers.

Reviewed By: xingbowang

Differential Revision: D129106883

fbshipit-source-id: 8097d91ae1a2bc88fbbc93fd7cbdc7f00f7aa504
2026-11-02 09:58:04 -08:00
zaidoon 8fd33c2fac Stress and benchmark all IndexFactory modes; add docs and release notes (#14968)
Summary:
**Why:** The `IndexFactory` series (https://github.com/meta/rocksdb/issues/14963, https://github.com/meta/rocksdb/issues/14965, https://github.com/meta/rocksdb/issues/14966) added five index modes and a per-read selector, but the stress and benchmark tools could reach only part of that surface. Its public API also shipped with no release note, and the `CompressionOptions::parallel_threads` comment still said a custom index forces single-threaded compression.

**Previous behavior:**

- `db_stress` had only `--use_udi_as_primary_index`, which selects `kStandardDefault` or `kCustomDefault`. `kCustomOnly`, `kStandardRequired`, and the `kBuiltin`/`kPreferCustom` read selectors were never stress tested. Reads chose the trie through the legacy `ReadOptions::table_index_factory` pointer, and the two read paths disagreed: after a reopen, the pointer was also set in primary mode.
- `db_bench --use_trie_index` could only build the trie as the secondary index, so the trie could not be benchmarked as the default read index or as the only index.

**New behavior:**

- `db_stress` and `db_bench` take `--index_mode` (0-4, defaulting to the library default `kStandardDefault`) and `--read_index` (-1 = auto, or `kDefault`/`kBuiltin`/`kPreferCustom`). Auto keeps today's behavior of reading through the trie when it is the secondary index. Both tools reject combinations that cannot work: modes 2-4 or a non-default selector without `--use_trie_index`, and `kBuiltin` with `kCustomOnly`. `--use_udi_as_primary_index` is removed from `db_stress`, and `--index_mode=2` replaces it.
- The crashtest draws `index_mode` once per DB together with `use_trie_index`, so every SST in a DB has matching table options, and draws `read_index` on every invocation. It disables only what cannot work in a mode:
  - `kCustomDefault`/`kCustomOnly`: partitioned index and filters (rejected by option validation); backup, because the restored DB rebuilds options from strings that do not carry the factory; and the secondary instance, which main already disables for primary mode.
  - `kCustomOnly`: `format_version` raised to 6, and `kBuiltin` reads dropped.
  - After all other sanitizers, custom modes and selectors are reset when something (for example user-defined timestamps) has turned the trie off.
- New `docs/components/index_factory/` component doc, and two release notes: one for the `IndexFactory` API, one for parallel compression with a custom index.

## Changes since the previous revision

This branch now contains only https://github.com/meta/rocksdb/issues/14968's own work on top of main, which includes https://github.com/meta/rocksdb/issues/14966. I re-checked each piece for value and redundancy:

- **Dropped `--index_mode=-1` ("auto")** from both tools. With no factory, the default `kStandardDefault` already builds only the standard index, so auto behaved identically to the default and needed a helper in each tool.
- **Kept remote compaction on in the custom modes.** The previous revision disabled it because it was "not exercised yet", but main already runs primary mode with remote compaction, and remote workers rebuild the trie factory from its registered name. The crashtest runs below cover it in `kCustomOnly` and `kCustomDefault`.
- **Dropped disabling write faults for `cf_consistency`** in the custom modes. Main runs that combination in primary mode today, and the targeted `cf_consistency` + `kCustomOnly` + write-fault run below passed.
- **Kept backup and secondary on for `kStandardRequired`.** It has a full standard index, so a restored or secondary DB can always read it.
- **Release notes cut from eight to two.** Dropped one that duplicated main's existing user-defined-timestamp note, and one that described `kPreferCustom`'s fallback as a behavior change. Before https://github.com/meta/rocksdb/issues/14963, `table_index_factory` was only consulted for SSTs that had a custom index, so SSTs without one already used the standard index.
- Replaced eleven per-case crashtest tests with four table-driven ones, and dropped an unrelated `(void)argc` edit in `db_bench_tool_test.cc`.

## Safety and compatibility

- Tool defaults are unchanged: without `--use_trie_index`, both tools build only the standard index. With it, `db_bench` and `db_stress` still read through the trie as before.
- Removing `--use_udi_as_primary_index` from `db_stress` breaks any external command line that passes it. The in-tree crashtest is updated.
- The header and docs changes are comments only.
- The component doc notes known gaps rather than fixing them: `sst_dump`, and `ldb` without the DB's OPTIONS file, cannot open `kCustomOnly` files, and there is no Java API.

Pull Request resolved: https://github.com/meta/rocksdb/pull/14968

Test Plan:
All on this branch rebased onto main at a7ae6cd0a (macOS ARM64, Apple Clang, debug):

- `tools/db_crashtest_test.py`: 76 tests pass. Each of the four new tests fails when its sanitizer rule is removed: the `format_version` bump, the `kBuiltin` drop for `kCustomOnly`, the reset when the trie is off, and keeping remote compaction on (verified by re-adding the old disable).
- Flag validation: every invalid combination listed above exits with status 1 and a clear message in both tools.
- `db_bench` smoke runs: fillseq, compact, readrandom, and seekrandom with `--use_trie_index=1` in all 19 valid mode and selector combinations, with every key found.
- Blackbox crashtests (`db_crashtest.py blackbox`, 300 s, killed every 30 s, `db_stress` built with gflags). Runs A, B, and D predate the last `read_index` fix, so they always used auto. Each run completed 10 kill/restart cycles, and its final crash-recovery verification passed:

| Run | Index mode | What it adds | Result |
|---|---|---|---|
| A | `kCustomOnly` | Remote compaction in 4 of 11 runs, `format_version` 6-8, 1-16 compression threads | Passed |
| A2 | `kCustomOnly` | Write faults (1 in 128) in all 11 runs. Remote compaction in 7 runs: 289 remote jobs, none fell back to local. `read_index` auto, `kDefault`, `kPreferCustom` | Passed |
| D | `kCustomOnly` | `--cf_consistency` | Passed |
| D2 | `kCustomOnly` | `--cf_consistency` with write faults in all 11 runs. Remote compaction in 6 runs: 1,710 remote jobs; 18 fell back to local after the remote job failed (`db_stress` falls back only for injected faults or when configured to, and fails the run on any other remote error) | Passed |
| B | `kStandardRequired` | Remote compaction and backup in every run, write faults in 4 runs | Passed |
| B2 | `kStandardRequired` | Secondary instance in every run, 2 DBs, all four `read_index` values including `kBuiltin` | Passed |
| E | `kCustomDefault` | Remote compaction in 9 runs: 1,472 remote jobs, none fell back to local. All four `read_index` values | Passed |

- `make format-auto` and `make check-sources` pass.

Not run locally: `db_bench_tool_test` (this Mac's gflags setup pulls in a conflicting gtest; CI runs it), full `make check`, Linux, Windows, sanitizers, and a release build.

## Series

Builds on https://github.com/meta/rocksdb/issues/14963, https://github.com/meta/rocksdb/issues/14965, and https://github.com/meta/rocksdb/issues/14966, all on main. This is the last PR in the series.

Reviewed By: jaykorean

Differential Revision: D128807954

Pulled By: xingbowang

fbshipit-source-id: b4faa1e94959f63ea56fd8d69f64b1763c8aacce
2026-11-02 09:37:26 -08:00
Yedidya Feldblum ee13e2a640 Use owning EventBase acquisition in coroutine benchmarks (#15527)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15527

Prefer owning EventBase acquisition in both coroutine benchmark families without breaking RocksDB's public build against pinned Folly 7ba92f0e29d24b453cee8ab27d117b1c7cc19589, which lacks `IOExecutor::getEventBaseKeepAlive()`. A private function template uses a dependent C++20 `requires` expression and `if constexpr`: use the owning getter when available, otherwise retain the previous raw selection plus token acquisition. Exceptions propagate without switching to the fallback.

Acquisition remains at each job's existing launch point; executors, parameters, futures, teardown, non-coroutine builds, API/ABI and stored formats are unchanged. The legacy branch preserves existing behavior rather than providing the newer API's selection-window safety. The Folly pin is not advanced here; a separate cleanup will remove this temporary fallback once the pin includes the owning API.

Validation: native owner builds and both 64-record coroutine benchmarks passed; five C++20 compile/selection/exception checks cover both API shapes. Formatting, default lint and 3/3 applicable sanity checks passed; 72 whole-file clang-tidy warnings match the exact unmodified baseline. Full Make and status-checked Make runs were attempted but are not green: Git-only checks fail in this Sapling checkout and unchanged I/O-dispatcher tests fail/abort outside the modified benchmark library. The standalone indexed source snapshot passed `make check-sources`. The actual pinned-Folly public build and other platforms remain unverified locally.

Reviewed By: r-barnes

Differential Revision: D128119434

fbshipit-source-id: d5b6bfbb0b782d334423b876717d20d0c829cc22
2026-11-01 17:05:25 -08:00
Omkar Gawde a87e2a0e43 Report OnSubcompactionCompleted when a resumed subcompaction fails to restore its progress (#15530)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15530

`CompactionJob::ProcessKeyValueCompaction()` calls `NotifyOnSubcompactionBegin()` before `MaybeResumeSubcompactionProgressOnInputIterator()`. When the resume failed, the function set `sub_compact->status` and returned without a call to `NotifyOnSubcompactionCompleted()`. The resume fails when a saved output file cannot be read, when the key to resume from is outside the input, when the input iterator fails at that key, or when the saved blob progress cannot be restored. A listener then got `OnSubcompactionBegin()` with no matching `OnSubcompactionCompleted()`, so it could not close the event or see the failure status.

Only `DB::OpenAndCompact()` with `OpenAndCompactOptions::allow_resumption` loads saved progress, so only the listeners of such a worker are affected. When there is no progress to resume, the function returns `NotFound`, and the subcompaction starts from the first key as before.

The early return now calls `NotifyOnSubcompactionCompleted()`, and the event carries the resume error in `SubcompactionJobInfo::status`. `NotifyOnSubcompactionCompleted()` keeps its checks: it sends nothing when `NotifyOnSubcompactionBegin()` sent no event, or when the DB is shutting down. The resume error and what follows it do not change: the worker's compaction fails with that status, and the `CompactionService` implementation decides what the primary does.

The diff also adds a release note under `unreleased_history/bug_fixes/`.

Reviewed By: jaykorean

Differential Revision: D128236290

fbshipit-source-id: 2b2a0e97b65556c1cd52f7c494636ae923cd639e
2026-11-01 12:38:57 -08:00
Andrew Chang 02815ee01c Don't recycle a WAL until its old writer is closed (#15458)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15458

X-link: https://github.com/facebook/rocksdb/pull/15458

**Bug:** with WAL recycling on (`recycle_log_file_num > 0`), RocksDB can lose writes that were already acknowledged, including `sync` writes.

**Why:** `FindObsoleteFiles()` puts an old WAL A on the recycle list and only closes A's writer later, after releasing the mutexes. If a memtable switch happens in that window, A is renamed and reused as the new WAL C, so A's still-open writer and C point at the same file. When the old writer finally closes, `PosixWritableFile::Close()` truncates the file to A's old size and wipes out what was written to C. The next reopen recovers from the truncated WAL, and those writes are gone. In the test, C went from 65,589 bytes to 32.

With `background_close_inactive_wals = true`, two more paths (`FindObsoleteFiles()` and `MarkLogsSynced()`) take A out of `logs_` before its writer is closed, so the same reuse can happen.

**Fix:** never reuse a WAL whose writer may still be open.
- `SwitchMemtable()` skips recycling a WAL that is still in `logs_` (`IsWalInLogs()`) and creates a fresh WAL that time.
- With recycling on, `FindObsoleteFiles()` and `MarkLogsSynced()` close a WAL's writer before removing it from `logs_`.

Reviewed By: xingbowang

Differential Revision: D126882379

fbshipit-source-id: ecaf3af9d89947d4236a07097bbd9cddaa962b71
2026-10-31 14:27:50 -07:00
Vitalii Kalinkin 563d4b14e0 Skip column families dropped during a whole-DB atomic flush (#15529)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15529

With `atomic_flush=true`, `DB::CompactRange()`, `WaitForCompact(flush=true)` and DB close flush all live column families through `AtomicFlushMemTables()` with internally generated candidates. If an unrelated column family is dropped while that flush waits, the flush returns `ColumnFamilyDropped`:
- `WaitUntilFlushWouldNotStallWrites()` returns it for a candidate dropped while waiting for write-stall conditions to clear, before any memtable is switched. `CompactRange()` then fails although its target is live, and `FlushAllColumnFamilies()` maps the error to OK without flushing the remaining column families.
- `WaitForFlushMemTables()` returns it when the only selected column family is dropped while its flush is pending, for example on `CompactRange(cf, nullptr, nullptr)` with an empty target and one other column family with data. This happens with `allow_write_stall=true` as well.

A dropped column family has nothing left to flush, so for generated candidates `AtomicFlushMemTablesImpl()` now skips it in the stall wait and treats `ColumnFamilyDropped` from `WaitForFlushMemTables()` as OK. Callers that pass explicit column families, such as `DB::Flush(handles)`, still get the error. `CompactRange()` returns `ColumnFamilyDropped` when its own target was dropped, and `FlushAllColumnFamilies()` no longer masks the status. There are no API, option or file format changes.

Reviewed By: xingbowang

Differential Revision: D128611460

fbshipit-source-id: 2e4d6c9204ca108eb7757bb8d39c3596348dee4d
2026-10-31 06:46:58 -07:00
Ranjan Banerjee 7e61be9ecc Preserve compaction context across worker handoffs (#15450)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15450

X-link: https://github.com/facebook/rocksdb/pull/15450

The separate remote compaction pool moves coordinators, sibling subcompactions, and local fallback work between threads. Consumers need the execution mode selected for each job so listener and compaction-service callbacks can preserve the correct context even when DB options change at runtime.

Capture `use_separate_remote_compaction_pool` on `Compaction` for background/manual compaction and `CompactFiles`. Expose the captured value in `CompactionJobInfo`, `SubcompactionJobInfo`, and `CompactionServiceJobInfo`, and populate it in parent/subcompaction callbacks and compaction-service scheduling requests. Default the metadata fields to false and keep each accepted job's value independent of later option updates.

Include the parent/subcompaction C API getters and their generated definitions. Add regressions for gate changes during a job and for metadata propagation through `CompactFiles` with multiple subcompactions.

Keep these library changes in `internal_repo_rocksdb/repo` so the normal RocksDB release carries all consumer prerequisites into fbcode. ZippyDB then only needs its gated context handling and tests; copying released RocksDB sources remains the responsibility of the separate release diff.

Reviewed By: xingbowang

Differential Revision: D126716904

fbshipit-source-id: fc6d377cd139fb2a81684f9f47daabc26d3095d5
2026-10-30 16:08:44 -07:00
Josh Kang a7ae6cd0a8 Remove dead code (#15407)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15407

X-link: https://github.com/facebook/rocksdb/pull/15407

## Summary

A full clang-tidy scan of RocksDB reports 65 dead-code findings in
existing code:
- `clang-analyzer-deadcode.DeadStores` (20)
- `-Wunused-member-function` (24)
- `-Wunreachable-code-return` / `-Wunreachable-code-loop-increment` (6)
- `-Wmissing-noreturn` (9)
- `-Wextra-semi` (6)

Every PR that touches nearby lines gets flagged for them, and some hide
real mistakes (an unchecked result, a loop that can only run once). This
diff fixes 57 of them.

What changed:
- Unused private and file-local member functions are removed, along with
  members that only fed them and local classes that are never
  instantiated. Examples: `EmptyIterator` in the hash memtable reps,
  `PersistentCacheFromCache` in `db_block_cache_test.cc`, and
  `LegacyFileSystemWrapper::target()`. Each one was confirmed unused in
  every build configuration and is file-local or private.
- Unreachable code after `return`, after a `throw`, or after an infinite
  loop is removed, but only where every compiler still sees every path
  returning. A loop that always returned on its first pass is now an
  `if`. A trailing return after `#ifdef` branches is now an
  `#else`/`#endif`.
- Dead stores are removed. Google Benchmark loop variables are marked
  `[[maybe_unused]]` (`for ([[maybe_unused]] auto _ : state)`).
- `[[noreturn]]` is added to functions that never return, such as
  `ErrorExit` in `db_bench`.
- Stray `;` after member function bodies are removed.

Not changed: `[[noreturn]]` was not added to `NoopDelete` helpers whose
body is `assert(false)`, because they return in release builds. It was
also not added to `MemTableRep::InsertConcurrently` (public virtual) or to
`LDBTool::Run` (public, deprecated), and lambdas have no portable C++20
syntax for it.

Reviewed By: xingbowang

Differential Revision: D123186311

fbshipit-source-id: 378348c02a494aef6786827e3d18b44cbd280a04
2026-10-30 15:45:12 -07:00
zaidoon 5f39d7b8ea Support parallel AddIndexEntry in trie index (#14966)
Summary:
**Why:** With `CompressionOptions::parallel_threads > 1`, the block-based table builder runs parallel compression only if every index builder implements the two-phase `PrepareAddEntry` / `FinishAddEntry` protocol. The trie builder declined it, so any SST built with the trie `IndexFactory` fell back to single-threaded compression (with a LOG warning), in every index mode.

**New behavior:** The trie builder opts into the protocol. `PrepareAddEntry` runs on the emit thread: it computes the separator and updates the size estimate. `FinishAddEntry` runs on the writer thread in block order: it attaches the block handle, resolves same-user-key boundaries against the previously committed entry, and buffers the entry. The serialized trie is byte-identical to the serial `AddIndexEntry` output.

**Implementation:**

- **Split state by thread.** The estimate (`estimated_trie_edges_`, `estimated_num_entries_`, and an emit-owned copy of the previous separator for shared-prefix accounting) is written only on the emit thread. `buffered_entries_` is owned by the writer. `EstimatedSize()` is called on the emit thread, from compaction's `ShouldStopBefore()` via `EstimatedTailSize()`, concurrently with `FinishAddEntry()`, so it never reads writer-owned state.
- **Mirror the serial path.** `FinishAddEntry` follows the serial branches and comparison primitives, including the unshortened-separator collision with the previous entry and the last-block check.
- **Fail on a protocol violation.** Finishing an entry that was never prepared, or finishing one twice, makes `Finish()` return `InvalidArgument`. Previously it was skipped silently, so the build succeeded with a trie that omitted that data block and seeks for its keys landed on the next block.
- **Crashtest.** `db_crashtest.py` no longer forces `compression_parallel_threads=1` when `use_trie_index=1`, so crashtest randomizes the thread count with the trie in both modes.
- **Renames (no behavior change).** The trie code and tests move from `UserDefinedIndex*` to the `IndexFactory*` names and from `ReadOptions::table_index_factory` to `read_index`, and tests set `index_mode` explicitly. The always-true `must_use_separator_with_seq_` flag becomes `!buffered_entries_.empty()`. The old public names remain as aliases on main.

## Safety and compatibility

- The table builder decides on parallel compression once, when it is constructed, so serial and parallel callbacks never mix within one SST. `Finish()` runs only after the worker threads join.
- The serialized trie format and readers are unchanged. Index builders that do not support the protocol still force serial compression for that SST.
- The table builder only finishes entries it prepared (an existing table test asserts equal prepare and finish counts), so the new `InvalidArgument` path cannot fire in a supported build.
- Users who configure the trie with `parallel_threads > 1` now get multi-threaded compression for those SSTs, and therefore more CPU threads per build. No throughput gain is claimed; this was not benchmarked.
- The release note for parallel compression with a custom index is in https://github.com/meta/rocksdb/issues/14968.
- The existing 32-bit block offset/size limit of the trie encoding is unchanged.

Pull Request resolved: https://github.com/meta/rocksdb/pull/14966

Test Plan:
Unit (`trie_index_test`):

- `ParallelMatchesSerialOutput`: serial and parallel builds produce byte-identical output, including a four-block run of one user key and the last block.
- `ParallelPipelineOverlapsPrepareAndFinish`: separate emit and writer threads with a bounded handoff queue, output identical to a serial build.
- `ParallelEstimatedSizeStaysMonotonic`: the estimate grows on `PrepareAddEntry` and is unchanged by `FinishAddEntry`. Verified to fail if `EstimatedSize()` is changed back to read writer-owned `buffered_entries_`.
- `ParallelFinishWithoutPrepareFailsBuild`: covers a never-prepared entry and an entry finished twice. Verified to fail without the fix: `Finish()` returned OK and a seek for a key in the skipped block returned the next block's offset.

DB (`trie_index_db_test`):

- `ParallelCompressionWithTrieIndex` (all three modes), `ParallelCompressionWithHashStandardIndexAndTrieUdi`, and `SharedPrefixWithParallelCompression` now also require the parallel pipeline to have started (via the `MaybeStartParallelCompression:Started` sync point). Verified: with the trie builder forced to decline the protocol, all 7 previously passed and now fail.
- Under TSAN, `SharedPrefixWithParallelCompression` reports a data race between `EstimatedSize()` (compaction thread) and `FinishAddEntry()` (writer thread) if `EstimatedSize()` reads `buffered_entries_` again, and the process exits non-zero. It is clean with this PR.

Crashtest (`tools/db_crashtest_test.py`):

- `test_finalize_keeps_parallel_compression_with_trie_index`: with `use_trie_index=1` and `compression_parallel_threads=4`, sanitizing keeps 4 threads in both modes. Verified to fail before the change (it returned 1).

Runs on this branch rebased onto main at 160a3171f (macOS ARM64, Apple Clang):

- Debug: all of `trie_index_test` (156) and `trie_index_db_test` (260) pass.
- `ASSERT_STATUS_CHECKED=1 COERCE_CONTEXT_SWITCH=1`: both suites pass, and the 12 parallel tests pass 100 times each (1,200 runs).
- TSAN (`COMPILE_WITH_TSAN=1`): both suites pass with no reports.
- No compression libraries (`ROCKSDB_DISABLE_{SNAPPY,ZLIB,BZIP,LZ4,ZSTD}=1`, as in `build-linux-encrypted_env-no_compression`): both suites pass, and the 7 Zlib-dependent parallel tests skip.
- `db_stress` (debug) with `--use_trie_index=1 --compression_type=zlib --compression_parallel_threads=4`, 512-byte blocks, 256 KiB memtables and target files, 8 threads x 200,000 ops, and 10 reopens, in both `--use_udi_as_primary_index=0` and `=1`: final verification passed with 0 errors, and the LOG has no parallel-compression fallback warning.
- Blackbox crashtest (`db_crashtest.py blackbox`, 300 s, killed every 30 s) with `--use_trie_index=1 --compression_parallel_threads=4` and all other parameters randomized by the script, once per index mode. Each run completed 10 kill/restart cycles. In secondary mode, the script's final crash-recovery verification passed. In primary mode, I reran the final verification on a copy of the DB with `--read_fault_one_in=0`, and it passed. The script's own run with read faults on spent its time retrying compactions inside `WaitForCompact`, because `db_stress` injects read faults into every subcompaction. That injection doesn't depend on the index.
- Source and format checks pass.

Not run locally: full `make check`, Linux, Windows, ASAN, UBSAN, or a release (`-fno-rtti`) build. Hosted CI covers those.

## Series

Builds on https://github.com/meta/rocksdb/issues/14963 and https://github.com/meta/rocksdb/issues/14965, both already on main. This branch contains only this PR's commits. Next: https://github.com/meta/rocksdb/issues/14968.

Reviewed By: jaykorean

Differential Revision: D128658113

Pulled By: xingbowang

fbshipit-source-id: 3e3090565f92401274e27c112edeef3cac1bb398
2026-10-30 15:22:06 -07:00
Ranjan Banerjee fcb42ecdb2 Synchronize manual compaction cancellation during close (#15528)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15528

Closing a DB can race with a manual-compaction caller finishing canceled work. `CloseHelper()` reads `manual_compaction_dequeue_` through `HasPendingManualCompaction()` without holding `mutex_`, while the caller removes its entry under that mutex. The concurrent-close regression `CloseQueuedManualBottomSubcompactions` exposed this as a TSAN data race.

Acquire the DB mutex before checking for pending manual work during close. If work is pending, release the lock before calling `DisableManualCompaction()`, then reacquire it before teardown continues. An empty queue skips cancellation, preserving the existing `manual_compaction_paused_` count. The public helper retains its contract of pausing future manual requests even when its queue is empty.

Require the mutex in both `HasPendingManualCompaction()` overloads with `AssertHeld()`. Add regressions for idle manual disable, pause-count preservation when closing without pending work, and repeated close.

Reviewed By: xingbowang

Differential Revision: D128672217

fbshipit-source-id: 4169c44ff568d5750f33af37b3f5f03cec120867
2026-10-30 14:51:20 -07:00
Josh Kang bc11adb4ba Add buffer-based internal key encoding (#15495)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15495

X-link: https://github.com/facebook/rocksdb/pull/15495

## Summary

`EncodeEntry()` currently requires a `std::string`, so callers using preallocated storage must stage encoded keys in a string or reproduce the encoding themselves. Add `GetEncodedEntrySize()` and a `char*` overload that writes directly into caller-owned storage without allocating on success.

Callers provide at least `GetEncodedEntrySize(entry)` writable bytes. Input slices must not overlap the written region; debug builds assert this. The overload uses the existing internal key format and validates sequence and type before writing, leaving the buffer unchanged on error. The string overload retains its append implementation.

Reviewed By: xingbowang

Differential Revision: D127866846

fbshipit-source-id: efcd489aeb487a2ce17f2d396fe50dc68e1161c4
2026-10-30 14:50:31 -07:00
Josh Kang feb6fb8377 Fix atomic replace ingestion racing with compaction on replaced files (#15392)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15392

X-link: https://github.com/facebook/rocksdb/pull/15392

## Summary

Fix atomic range replacement racing with a background compaction that selects an SST the replacement is deleting. During `LogAndApply()`'s unlocked MANIFEST write, the old SST remained in the current LSM version and appeared available to the picker. If ingestion committed first, compaction later tried to delete the same SST and raised a `VersionBuilder` corruption, leaving the DB in a background error state that fails subsequent writes. Registration of the new SST output ranges did not protect the old deletion inputs; universal compaction with `num_levels=1` permits overlapping L0 outputs.

Reserve the actual old SSTs with `being_compacted`, checking eligibility and registering under one DB mutex hold. Cleanup releases only reservations acquired by the job, on success or failure. Full-column-family and bounded replacement reject already-reserved inputs, including `DeleteFilesInRanges()` reservations that have no registered compaction. Atomic range replacement now returns `NotSupported` with FIFO compaction, before preparing or reserving files. Existing partial-overlap handling and snapshot restrictions are unchanged, and there are no persisted format changes.

Replacement now selects files from a pinned `cfd->current()`, and ingestion level assignment probes `cfd->current()` as well, matching the Version it already uses for level fit. The published SuperVersion can lag after a grouped MANIFEST write in which a follower compaction released its inputs before installing its own SuperVersion. Selecting from it could delete an SST that is no longer in the LSM tree, and probing it could place a replacement range tombstone below the compaction output it must cover. Reserving files filters cached compaction candidates; successful commits use the normal Version refresh. Failed commits recompute compaction eligibility before rescheduling.

### Race outcomes

| Ordering under the DB mutex | Winner | Expected behavior |
| Compaction selects the SST before ingestion reserves it. | Compaction | Ingestion returns `InvalidArgument` for the pending-compaction conflict without committing the replacement. Compaction continues; the caller can retry ingestion. |
| Ingestion checks and reserves the SST before compaction selects it. | Ingestion | The picker skips the reserved SST and may use other eligible inputs or find no work. The reservation covers MANIFEST I/O; successful replacement removes the SST before cleanup clears its flag. |
| DeleteFilesInRanges reserves the SST before ingestion checks it. | File deletion | Full-column-family replacement returns `InvalidArgument` without committing or clearing deletion's reservation. Deletion completes; the caller can retry ingestion. |
| Ingestion reserves the SST before DeleteFilesInRanges checks it. | Ingestion | `DeleteFilesInRanges()` skips the reserved SST and can return success without deleting it. Successful replacement removes the old SST before releasing its reservation. |

Reviewed By: xingbowang

Differential Revision: D124614960

fbshipit-source-id: b85030cf7609e97df2ea0e34e30db2bd6789ce16
2026-10-30 14:12:54 -07:00
Erin Gao d2d4c9b44c Expose batched_multi_get_cf on pessimistic TransactionDB (#15520)
Summary:
PR adds two TransactionDB C APIs:
- `rocksdb_transactiondb_batched_multi_get_cf`
- `rocksdb_transactiondb_batched_multi_get_cf_slice`

They mirror the existing base-DB wrappers, return pinned values, preserve `sorted_input`, and call TransactionDB directly to retain transaction visibility rules.

## Motivation
We are using the pessimistic DB and would not like to unwrap to the base db, which is what the current API does. The other option which uses TransactionDB doesn't return the DBPinnableSlice or have the ability to specify whether inputs are sorted.

Pull Request resolved: https://github.com/meta/rocksdb/pull/15520

Reviewed By: anand1976

Differential Revision: D128633853

Pulled By: xingbowang

fbshipit-source-id: f84ac90bcf5a1e13d6516db90127c280879839a7
2026-10-30 14:10:24 -07:00
Josh Kang 800b5741f2 Limit prepared ingestion SST protection to reserved files (#15490)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15490

X-link: https://github.com/facebook/rocksdb/pull/15490

## Summary

Prepared external ingestions held a `pending_outputs_` watermark until commit or abort, preventing reclamation of unrelated SSTs created later and compacted away. Prepared ingestions now protect only their reserved SST numbers through `pending_ingestion_outputs_` (`first_file_number` -> `num_files`), copied into full-purge `sst_live` snapshots. This preserves staged inputs and generated tombstones while allowing unrelated obsolete SSTs to be deleted.

Successful manifest completion releases reservations under the DB mutex after all Versions are installed, including grouped commits performed by another writer. Waiting for the ingestion caller to resume could otherwise let purge claim a compacted input and retain it through the stale reservation, leaving its grabbed marker behind. Failure and abort retain protection through cleanup. Public APIs and persisted formats are unchanged, existing background-output protection remains in place, and handles still retain other resources until consumed.

## Example

Keep a prepared handle open for SST `100`, containing key `z`. Meanwhile, normal DB writes update another key, `a`. The SST numbers are illustrative.

```text
Prepare ingestion:
  SST 100: key z (staged, awaiting commit)

Unrelated DB activity:
  flush a=old --> SST 101 --+
                            +--> compaction --> SST 103 (live)
  flush a=new --> SST 102 --+
                  101 and 102 become obsolete

Files remaining after full purge, before commit/abort:
  SST   State      Before fix   After fix
  100   staged     kept         kept
  101   obsolete   kept         deleted
  102   obsolete   kept         deleted
  103   live       kept         kept
```

`100` is not yet visible to reads or compaction. After compaction releases its input references, the old watermark still protects obsolete `101` and `102`. The new reservation protects only staged `100`; `103` remains because it is live.

Reviewed By: xingbowang

Differential Revision: D127694250

fbshipit-source-id: c89ecc94c96ff0a2043a2673cc2484c68323f275
2026-10-30 13:57:20 -07:00
Ranjan Banerjee 160a3171f2 Exercise the remote compaction pool in stress tools
Summary:
The stress and benchmark tools need a way to select the separate remote compaction pool when exercising a compaction service.

Expose `use_separate_remote_compaction_pool` as a default-false flag in `db_stress` and `db_bench`, and pass it into their DB options. Randomize the flag in `db_crashtest.py` to cover both scheduling modes. The option takes effect when a compaction service is configured.

X-link: https://github.com/facebook/rocksdb/pull/15406

Reviewed By: xingbowang

Differential Revision: D125484658

fbshipit-source-id: 1fb3f0b92ff156f86d035f11b14e2c5316c60d19
2026-10-30 12:47:10 -07:00
Ranjan Banerjee e6d1ec007f Remote Compaction to not take local slots but run in a separate remote pool
Summary:
Remote compaction coordinators currently occupy LOW/BOTTOM workers and local compaction credits while scheduling and waiting for remote execution. Slow remote work can delay local compactions, including jobs with multiple subcompactions.

Add the default-off, runtime mutable `use_separate_remote_compaction_pool` DB option. When enabled with a `CompactionService`, coordinators and sibling subcompactions use one process-wide elastic pool, including DBs with different Env wrappers. Sharing the pool avoids multiplying its baseline across shard DBs. The baseline is the largest combined LOW/BOTTOM thread count among participating DBs, and global outstanding work determines growth. After 30 seconds of inactivity, the pool shrinks to the enabled clients' baseline, or zero if none remain. Name its workers `rocksdb:remote`.

Local fallback acquires a DB credit when needed and executes in LOW/BOTTOM. Caller-driven `CompactFiles` retains inline local execution. Each DB drains its own accepted tasks before closing; only the last pool reference joins the shared workers. Preserve completion and submission exception handling and each coordinator's captured gate across runtime updates.

Support parsing, runtime updates, and the generated C API, while omitting the option from serialized OPTIONS so older remote workers can parse requests. Gate-only mutable updates remain effective. Expose the process-wide worker count through `DB::Properties::kNumRemoteCompactionPoolThreads`; collectors must sample it once rather than sum it across DBs.

X-link: https://github.com/facebook/rocksdb/pull/15361

Reviewed By: xingbowang

Differential Revision: D124139026

fbshipit-source-id: a46de1740db5aa2068525bb6b4fd18f808cdf864
2026-10-30 12:19:34 -07:00
Xingbo Wang e6961ad337 Fix resumable remote compaction losing restored outputs on repeated resume (#15526)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15526

CONTEXT:
Resumable remote compaction can lose output data when the same job is resumed more than once.

`DBImplSecondary::FinalizeCompactionProgressWriter()` sets up the progress file for a resumed job. It wrote the initial progress record (the list of restored output SSTs and the record count) into a temporary progress file, renamed that file to the final name, and then reopened the final file with `CreateCompactionProgressWriter()`. The reopen uses `FileSystem::NewWritableFile()`, which truncates the file, so the initial record was erased.

Before 7790768fe8a2 ("Support BlobDB in remote compaction") this was hidden: `last_persisted_output_files_count_` stayed 0 after the initial persist, so the first checkpoint of the resumed job wrote all restored output files again together with the new ones. 7790768fe8a2 made `PersistInitialCompactionProgress()` call `UpdateLastPersistedOutputFilesCount()` (and the blob-addition equivalent). Now the first checkpoint only writes the files added after resumption, together with the cumulative `num_processed_output_records`, so the restored files are in no progress record on disk.

Previous behavior: on the next resume of the same job (third attempt), the worker loads a progress whose file list lacks the restored files but whose record count still includes them. `CleanupPhysicalCompactionOutputFiles()` deletes those SSTs as untracked, and the worker returns `num_output_records` larger than the entries in the returned files. The primary's `VerifyOutputRecordCount()` then fails with "Number of keys in compaction output SST files does not match number of keys added" (count > entries). With `compaction_verify_record_count=false`, the data is silently dropped.

The stress test hits this with `allow_resumption_one_in=1` and `remote_compaction_worker_threads=8`: the first attempt is always canceled and later attempts are canceled 1 in 10, so some jobs resume more than once. Seen in T295182746 (JOB 1086 and JOB 1069: each final result has one restored file followed by a file-number jump) and T295084500.

WHAT:
New behavior: the initial progress record is written and synced through the final writer, after the final progress file is opened. The record stays in the same file that later checkpoints append to, so the progress file always lists all restored SSTs and the delta encoding from 7790768fe8a2 stays correct. Repeated resumes keep all restored outputs, and the output record count matches the output files.

Implementation:
- `FinalizeCompactionProgressWriter()`: move the `PersistInitialCompactionProgress()` call from the temporary writer to the final writer, after `CreateCompactionProgressWriter(final_file_path)`. If it fails, clean up the final file (the temporary file is already renamed). Also fix the "copmaction" typo in the log message.
- Test: add `ResumableCompactionService::SetSecondCancelAtKey()`, which runs an extra resume-then-cancel phase between the first canceled phase and the final phase, and add `ResumableCompactionServiceTest.RepeatedCancelKeepsOutputRecordCountConsistent`.

Safety boundaries and unsupported cases:
- No change to the progress record format, public API, or DB mutex use. Fresh (not resumed) attempts write nothing extra.
- A crash after the final file is created and before the initial record is synced leaves an empty latest progress file. As before, the next attempt then starts fresh. This window is shorter than before, when the file stayed empty until the first checkpoint.
- Progress files already written by binaries that have 7790768fe8a2 but not this fix are not repaired. Resuming such a job again can still fail record count verification.
- The restored progress is now durable. If a resumed attempt fails every time while restoring outputs (for example, a tracked SST cannot be read), later resumes return the same error instead of accidentally starting fresh. This matches the documented `allow_resumption` contract.

Validation: the new unit test cancels, resumes and cancels again, then resumes to completion with record count verification on primary and worker. It fails without the fix and passes with it; the full `compaction_service_test` passes. Exact commands and results are in the Test Plan.

Reviewed By: pdillinger

Differential Revision: D128126725

fbshipit-source-id: 6321a1a69c03f925d820a617ed25883be97ea74a
2026-10-30 11:40:18 -07:00
Andrei Preda 7fb0d8e94a Fix MultiScan Prepare crashes with L0 files (#15502)
Summary:
Problem: L0 table iterators receive Iterator::Prepare() directly from
the MergingIterator, bypassing LevelIterator, which exposes two bugs
in BlockBasedTableIterator::Prepare():

1. A single scan range without a limit reaches it as Prepare(nullptr),
   which CollectBlockHandles() dereferences (SIGSEGV).

2. On an already-positioned iterator (Seek, SeekToFirst, SeekToLast,
   SeekForPrev, Next), Prepare() marks the data block unloaded without
   invalidating it, so the next Seek() re-initializes a live block
   iterator (assertion in BlockIter::InitializeBase() in debug builds).

Memtable-only DBs, files in L1+, and Prepare() as the first call on an
iterator are unaffected.

Fix:
1. Prepare(nullptr) returns early, as in LevelIterator.
   InternalIterator::Prepare() now documents the nullptr case.

2. Prepare() resets the data iterator before repositioning the index.
   Since that releases the block key() and value() point into,
   Prepare() also invalidates the iterator until it is positioned
   again; it was already documented to be followed by Seek().

Pull Request resolved: https://github.com/meta/rocksdb/pull/15502

Test Plan:
New PrepareWithMultiScanAfterPositioning covers each positioning call,
a reverse MultiScan, pin_data and first-key indexes, and checks that
MultiScan ran, Valid() is false after Prepare(), and the scanned keys
and values. PrepareWithMultiScanAllowsSingleUnboundedRange now
includes an L0 file. Reverting each change makes a test fail: SIGSEGV
in CollectBlockHandles(), the InitializeBase() assertion, or Valid()
after Prepare(). make check passes (2264 shards).

Reviewed By: anand1976

Differential Revision: D128181531

Pulled By: xingbowang

fbshipit-source-id: 4b662046a2b5790362a7c832b0b7c3eec6237f41
2026-10-30 10:15:38 -07:00
zaidoon 4ef117052f Account UDI blocks as index blocks
Summary:
Custom-index blocks previously lacked entries in the cache-helper tables and were omitted from the index-specific block-read counters. Register their cache helpers and count their reads and bytes as index-block activity, making custom-index work visible in the same statistics as built-in indexes.

Preserve the ownership contract established in https://github.com/facebook/rocksdb/issues/14963: charge the serialized block once, either to the table reader or to the block cache. A parsed `IndexFactoryReader` reports only its own allocations, excluding the borrowed serialized bytes.

## Safety and compatibility

This changes cache helper registration and statistics, without changing the SST format or index selection. The existing cache enablement and lifetime rules still apply. The accounting regression allows small allocator-size-class differences but rejects a missing or duplicate raw-block charge.

X-link: https://github.com/facebook/rocksdb/pull/14965

Reviewed By: jaykorean

Differential Revision: D126949386

Pulled By: xingbowang

fbshipit-source-id: d7b8d6cbaf939c20adbbbd24a4ff6f26d155cb5f
2026-10-30 10:14:42 -07:00
Jay Huh ce00781b64 Trace decoder ignores query record fields from newer versions
Summary:
X-link: https://github.com/facebook/rocksdb/pull/15486

Why: query records (Write, Get, MultiGet, Seek/SeekForPrev) list their fields in a 64-bit `payload_map`, and a newer RocksDB can add fields under new bits, as https://github.com/facebook/rocksdb/pull/15460 did with `TraceOptions::include_read_write_options` (bits 11-14). `TracerHelper::DecodeTraceRecord()` hit `assert(false)` on any bit it did not know, so a debug build could not read a trace from a newer version; release builds already went on and decoded the known fields. A review of https://github.com/facebook/rocksdb/pull/15453 raised this for traces recorded with the new flag.

Previous behavior: on an unknown bit, debug builds abort. Release builds skip the bit without consuming its bytes; because fields are encoded in bit order, every known field was already decoded by then and the record is used without the new field.

New behavior: at the first unknown bit, the decoder stops reading the record's fields, in debug and release builds alike, and returns the record with the fields it knows. A later field it would know is never placed after an unknown one, because encoders write fields in bit order and new fields get higher bits.

Compatibility: no format change. Release-build behavior is unchanged; traces this version writes decode as before. Versions without this change still assert in debug builds on traces written with `include_read_write_options` (documented on that option).

Reviewed By: xingbowang

Differential Revision: D127652082

fbshipit-source-id: 01a34d705e57a4b1429ad74d8602502357153dc6
2026-10-30 09:50:05 -07:00
cm-sally-mate 87d442749a Let async_io MultiGet read direct-IO files asynchronously (#15493)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15493

X-link: https://github.com/facebook/rocksdb/pull/15493

**What:** with `ReadOptions::async_io`, MultiGet now reads direct-IO files asynchronously too. Before, `RetrieveMultipleBlocks` sent them to the synchronous `MultiRead`, so a MultiGet over direct-IO files got no read overlap at all. Async reads are now also charged to the rate limiter, like sync reads.

**How:**
- `AsyncFileReader::MultiReadAsyncImpl`: for a direct-IO request without a scratch buffer, `RandomAccessFileReader::ReadAsync` allocates the aligned buffer and hands it back in the request's `fs_scratch`, with `result` pointing into it. That uses the existing `aligned_buf` support in `ReadAsync`, so the file system only sees aligned requests. If `ReadAsync` reports NotSupported, the synchronous fallback reads through an `AlignedBufferAllocationContext` and moves that buffer into `fs_scratch` the same way.
- `RandomAccessFileReader::ReadAsync` charges the read rate limiter at `rate_limiter_priority` for every read it submits: the aligned length for direct IO, the request length otherwise. It charges once the file system accepts the read, because every caller falls back to `Read` on NotSupported and `Read` charges for itself. `MultiRead` and `ReadAsync` share the charging loop (`ChargeRateLimiterForRead`).
- `RetrieveMultipleBlocks`: direct-IO files now take `MultiReadAsync` like buffered files. The requests (and so their `fs_scratch`) live until the blocks are parsed.

**Behavior changes:**
- Direct IO: each block request is aligned and read on its own; requests aren't merged. Adjacent blocks of one file can read an overlapping aligned page twice.
- Async reads (async_io MultiGet and iterator prefetching), buffered or direct, are now charged to `DBOptions::rate_limiter`. Before, `ReadAsync` charged nothing.

Reviewed By: archang19, anand1976

Differential Revision: D127688099

fbshipit-source-id: cfab475b6a27cb2a78b610144f9f8436c3b8a807
2026-10-29 17:28:27 -07:00
Daniel Bradleigh Crockett f216185228 Emit fbsource//xplat/folly deps from the RocksDB buckifier (#15521)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15521

The fbcode copy of folly is a dirsync mirror of xplat/folly that is being removed; once it is gone, `fbcode//folly` targets will no longer resolve. Hand-written BUCK files are migrated by `migrate_fbcode_folly.py`; generated BUCK files have to be fixed in their generator.

Rewrites the 16 `rocksdb_lib` folly deps in `internal_repo_rocksdb/repo/buckifier/buckify_rocksdb.py` to `fbsource//xplat/folly...` and regenerates `internal_repo_rocksdb/repo/BUCK` with `python3 buckifier/buckify_rocksdb.py`; only the 16 labels change.

`fbcode/rocksdb/src/BUCK` is a release snapshot updated by the periodic RocksDB release process and is not changed here.

___

landed-with-radar-review

Differential Revision: D128219553

fbshipit-source-id: 259fd54bf827a75e389c7d9be8275145f55515a8
2026-10-29 17:27:19 -07:00
Josh Kang f14b3508d4 Allow the current Version as a secondary replay base (#15499)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15499

X-link: https://github.com/facebook/rocksdb/pull/15499

## Summary

A secondary can successfully open without installing a new `Version` when the SSTs referenced by its initial MANIFEST snapshot disappear during primary compaction. On a later MANIFEST switch, catch-up selects the original empty `Version` as its replay base. That `Version` is still `current()`, so an overly strict assertion in `BaseReferencedVersionBuilder` aborts assertion-enabled builds.

Allow that valid base with one explicit-base `BaseReferencedVersionBuilder` constructor that takes and releases its own reference. Ordinary updates pass `current()`; MANIFEST replay passes the retained empty `Version`. This replaces the overload-specific inequality assertion and moves reference acquisition into the constructor, before the old builder is released. Replay still constructs a separate candidate `Version`; reference counts, release-build replay behavior, public APIs, and persisted formats are unchanged.

## Example

In this example, `V0` is the secondary’s initial empty `Version`, and `A`/`B` are SST files. A MANIFEST begins with a live-file snapshot, followed by later edits. The log names and SST layout below are simplified.

```text
Primary                                      Secondary
MANIFEST-1 snapshot: AddFile(A)               opens MANIFEST-1;
                                             current = V0 (empty)
roll to MANIFEST-2:
  snapshot: AddFile(A)
compact A -> B:
  append ONE edit to MANIFEST-2:
    DeleteFile(A), AddFile(B)
  purge A                                    replay MANIFEST-1 snapshot:
                                               A is missing
                                               no candidate installed
                                               current still V0

Catch-up sees CURRENT -> MANIFEST-2:
  replay base = V0 (empty); current() = V0
  old constructor: assert(version_ != cfd->current()) -> abort

With the unified constructor:
  Ref(V0), then replay MANIFEST-2 from the beginning:
    snapshot AddFile(A)                   -> A missing
    edit {DeleteFile(A), AddFile(B)}      -> live file set {B}
    SaveTo() a separate V1, then install  -> current() = V1 {B}
```

The new MANIFEST is rebuilt from the retained empty `V0`. Here `V0` is also current because initial recovery installed no candidate. The builder keeps `V0` referenced while replay saves the reconstructed file set into a separate `V1`, then installs `V1`.

## CI test fix

Also reorder `SyncPointAbortHelper::CleanupAndWait()` to call `WaitForAbortCompletion()` before `CleanupSyncPoints()`, fixing a pre-existing race exposed by CI. This keeps the abort dependency active until the worker has observed the abort and exited.

Reviewed By: pdillinger

Differential Revision: D127928276

fbshipit-source-id: 3e437328ed5afc8c9bf53bb97cbce0dfe2560097
2026-10-29 16:45:54 -07:00
Peter Dillinger 92c99cb6aa Some Status testing advice in CLAUDE.md and status.h
Summary:
as title, after observing bad agent behavior

X-link: https://github.com/facebook/rocksdb/pull/15476

Reviewed By: xingbowang

Differential Revision: D127492952

Pulled By: pdillinger

fbshipit-source-id: 94cfe2766433a7e59c05a79e189d53b46facc292
2026-10-29 14:53:15 -07:00
Omkar Gawde 42ed9b6a09 Reject malformed hex in encoded string fields (#15512)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15512

`OptionTypeInfo` parses an `OptionType::kEncodedString` field by hex-decoding its value, but `ParseOptionHelper()` ignored the result of `Slice::DecodeHex()` and always reported success. A value with an odd number of digits left the field unchanged, which is empty in a freshly constructed struct. A value with a non-hex digit kept only the bytes decoded before that digit. In both cases `Parse()` returned OK.

Only serialized formats that RocksDB itself writes as hex use `kEncodedString`: the remote compaction input and result (`CompactionServiceInput::Read()` and `CompactionServiceResult::Read()`), and `TableProperties::Parse()`, which the compaction result and the `PreparedFileInfo` deserialization use. So a corrupted or truncated serialized string was accepted with an empty or shortened value in fields such as the column family name, an input file name, a compaction range key, an output file key, or a file checksum, with no sign that the input was bad. The custom blob-metadata parser in `compaction_service_job.cc` already rejects bad hex with `InvalidArgument`.

Fix: decode into a local string and return false when `DecodeHex()` fails, so `OptionTypeInfo::Parse()` returns `InvalidArgument("Error parsing:", <field>)`. The field stays unchanged on failure, as with the `StringMap` parser.

Compatibility: `Serialize()` always writes valid hex, and valid hex in either letter case parses as before. Only malformed input changes behavior: it now fails instead of yielding an empty or partial value. `CompactionServiceInput::Read()` and `CompactionServiceResult::Read()` still ignore unknown fields.

Reviewed By: jaykorean

Differential Revision: D128131523

fbshipit-source-id: 49e2840758b96e24c910355b0d969092e760a222
2026-10-29 13:35:20 -07:00
Rajiv Harlalka 609269e0ae Fix GetCharge on uncharged standalone LRU entries
Summary:
X-link: https://github.com/facebook/rocksdb/pull/15501

Why the change is required:
`Cache::CreateStandalone` documents that with `strict_capacity_limit` and no
room, `allow_uncharged=true` creates the entry anyway with `GetCharge() == 0`.
The secondary cache adapter relies on this whenever it promotes an entry into
a full strict primary cache. Under `kFullChargeCacheMetadata` (the default),
such entries violated the public API contract.

Previous behavior:
`LRUCacheShard::CreateStandalone` sets `total_charge = 0` for an uncharged
entry, but `LRUHandle::GetCharge` unconditionally subtracted the metadata
charge. Debug builds aborted on `assert(total_charge >= meta_charge)`;
release builds reported enormous per-handle charges from unsigned underflow,
corrupting block-cache byte statistics through `GetUsage(handle)` in
`UpdateCacheHitMetrics`. The trigger requires an LRU cache with
`strict_capacity_limit`, full metadata charging, pinned entries preventing
sufficient eviction, and `allow_uncharged=true`. `kDontChargeCacheMetadata`
and HyperClockCache are unaffected.

New behavior:
Uncharged standalone handles report `GetCharge() == 0`, preventing the debug
assertion failure and incorrect release-build byte statistics.
`GetUsage(handle)` and `ApplyToHandle` also receive the correct charge. Total
cache usage accounting is unchanged: uncharged handles add no usage on
creation and subtract none on release.

Implementation:
`LRUHandle::GetCharge` returns zero for standalone handles with the named
zero-charge sentinel, retaining the existing assertion for other handles.
Added a release note and simplified the direct API regression test.
`FullCapacityTest` now exercises both metadata charge policies and verifies
zero per-handle usage when strict capacity forces secondary-cache promotion
to use an uncharged standalone handle. Its 700-byte values leave room for
metadata while ensuring two entries still exceed the 1024-byte capacity.

Safety, compatibility, and limitations:
Only the previously broken standalone, uncharged case changes its returned
charge. There are no API signature, ABI, or serialized-format changes.
The added check runs on every LRU `GetCharge` call, for every handle,
including block-cache hits through `UpdateCacheHitMetrics` and
`GetUsage(handle)`. Expected overhead is negligible because `im_flags` is
stored near `total_charge` and the branch is predictable; this is an
assessment of the code, with no performance benchmark performed.

Reviewed By: xingbowang

Differential Revision: D126972213

fbshipit-source-id: 595d8771b0e57f20c12314e00c2aa2d7d2965a34
2026-10-29 12:47:48 -07:00
Peter Dillinger 4750c1cce3 Move macOS CMake tests to nightly; remove unused post-steps action (#15518)
Summary:
CI cost reduction and cleanup in the GitHub Actions configuration:

* Move `build-macos-cmake` (four test shards on `macos-15-xlarge`) from pr-jobs to nightly. These jobs are among the most expensive per run and rarely catch anything not caught elsewhere. PRs still run `build-macos` (a build without tests) and the macOS Java jobs. The job is unchanged except for using nightly's owner check (it can no longer be selected through pr-jobs' `ONLY_JOB`; use workflow_dispatch on nightly instead).

* Remove the `post-steps` composite action and its 36 call sites. It was invoked without an `if:` condition, so GitHub skipped it whenever an earlier step had failed, meaning it only ever ran after successful jobs. Its failure-oriented steps (copying `t/` test logs, uploading core dumps) therefore never did anything. On success it uploaded the gnu_parallel job log (misleadingly named "db-log-file") and gtest XML results, neither of which is consumed by anything, and the macOS Java jobs that call it produce neither. Failing test output is already printed in the job console by `make check`. The `GTEST_OUTPUT` setting in pre-steps existed only to produce the uploaded XML, so it is removed too. Rather than fixing post-steps to run on failure, this change drops it because what it would collect is redundant with the console output.

* Bonus: the pre-steps diagnostics step now logs OS and CPU details alongside the existing ulimits, to help diagnose failures that depend on the runner environment.

Pull Request resolved: https://github.com/meta/rocksdb/pull/15518

Test Plan: CI on this PR validates the workflow YAML (`make check-workflow-yaml` in check-format-and-targets) and exercises pre-steps and the remaining jobs without post-steps. The relocated `build-macos-cmake` won't run before merge; parsing the workflows confirmed the nightly job is identical to the removed pr-jobs job apart from its owner-check condition. A manual workflow_dispatch of nightly after merge can confirm it runs.

Reviewed By: xingbowang

Differential Revision: D128353844

Pulled By: pdillinger

fbshipit-source-id: d066e6f60bf433ab6dc70aea7e1cd22d7207c8d1
2026-10-29 11:42:28 -07:00
Alex Zhavnerchik a12aafaf69 Track newer snapshot versions in entity metadata reads (#15398)
Summary:
Pull Request resolved: https://github.com/meta/rocksdb/pull/15398

X-link: https://github.com/facebook/rocksdb/pull/15398

Why: Applications that populate a cache from explicit-snapshot wide-column reads (`GetEntity` / `MultiGetEntity`) need to know whether a returned entity has already been superseded by a newer committed write, without issuing a second lookup. `D104182397` added `DB::GetWithMetadata()` and `DB::MultiGetWithMetadata()` for plain values, but left entity reads without a metadata counterpart.

Previous behavior: `DB::GetEntity()` and `DB::MultiGetEntity()` returned the snapshot-visible `PinnableWideColumns` or `PinnableAttributeGroups`, but always passed `newer_version_present = nullptr` into `DBImpl::GetImpl` and `DBImpl::MultiGetCommon` and could not report a newer point write or covering range deletion.

New behavior: experimental C++ `DB::GetEntityWithMetadata()` (virtual column-family overload plus default-CF helper) and `DB::MultiGetEntityWithMetadata()` (multi-CF `PinnableWideColumns*` virtual, single-CF helper, and `PinnableAttributeGroups*` attribute-group virtual) report newer committed `PutEntity`, `Put`, `Delete`, `Merge`, `SingleDelete`, and covering `DeleteRange` operations after an explicit snapshot. As with `GetWithMetadata()` / `MultiGetWithMetadata()`, ordinary primary-DB writes completed before the read starts are guaranteed to be reported, racing writes remain best-effort, and `true` has no false positives. For the attribute-group overload, `MultiGetOutputMetadata` vectors are sized to the total number of attribute groups across `results[0..num_keys-1]` in `(key, attribute_group)` order.

Implementation: newer-version tracking in `DBImpl::GetImpl` and `DBImpl::MultiGetCommon` is already result-type agnostic (gated on the read-callback metadata bounds inside memtable, SST, range-tombstone, and row-cache lookups rather than `value` vs `columns`). `DBImpl::GetEntityWithMetadata` and `DBImpl::MultiGetEntityWithMetadata` validate inputs, initialize `OutputMetadata` / `MultiGetOutputMetadata` via shared helpers in `db/db_impl/db_impl_metadata.h`, and pass the requested `newer_version_present` pointer or vector alongside `columns` into `GetImpl` and `MultiGetCommon` while keeping metadata-only state off passive `PinnableWideColumns` and `PinnableAttributeGroup` layouts. Existing `GetEntity()` and `MultiGetEntity()` paths are unchanged.

Safety and unsupported cases: tracking is opt-in (`WantNewerVersionPresent()`) and requires an explicit `read_options.snapshot`; when `snapshot == nullptr` or tracking is not requested, reads behave like `GetEntity()` / `MultiGetEntity()` and any engaged `newer_version_present` output stays `false`. Entity reads do not return user-defined timestamps, so `WantTimestamp()` / `WantTimestamps()` returns `Status::NotSupported` on all DB types. With an explicit snapshot and tracking requested, `ReadOptions::read_tier == kPersistedTier`, `DBImplSecondary`, `WritePreparedTxnDB` / `WriteUnpreparedTxnDB` (custom visibility callbacks), and `DB` subclasses that do not override the methods return `Status::NotSupported`, whereas `DBImplReadOnly` and `CompactedDBImpl` return `false` because their in-process views are static. `StackableDB` forwards to the wrapped `DB`.

Tests and limitations: 1,328 Buck tests across `db_wide_basic_test`, `db_basic_test`, `db_secondary_test`, and `write_prepared_transaction_test` passed, all 6 per-guard counterfactual mutations fail at least one targeted test, and `db_stress`, `db_bench`, and `db_test` build cleanly.

Reviewed By: xingbowang

Differential Revision: D125108470

fbshipit-source-id: 1ef367d5696ee6f8217391f64698229f48675d68
2026-10-29 11:29:17 -07:00
Peter Dillinger 7e0c4d8e11 Spread periodic compaction catch-up evenly after a phasing re-anchor (#15505)
Summary:
`periodic_compaction_phase_recovery_percent` (experimental, since 11.10) de-herds periodic compactions across a fleet of DBs sharing their `periodic_compaction_seconds` settings and writing slowly enough, on average, to actually reach it: instead of letting every (DB, column family) come due together, it steers each one toward a stable "preferred phase" within that period. Phasing (re)anchors at DB open and whenever that option or `periodic_compaction_seconds` changes. The "anchor" is the earliest time phasing may pull a compaction to so that enabling phasing cannot stampede a backlog of already-old files all at once.

**The problem.** Enabling the option across a production use case showed that the catch-up right after an anchor was still too bunched up. In one deployment (7-day `periodic_compaction_seconds`, recovery percent 33), enabling phasing in a single region pushed the remote periodic compaction rate to about 5.7x normal in the first hour and kept it 3-4x elevated for about 6 hours. (The intended long-term de-herding did work.)

**Root cause.** Before this change, a file whose preferred-phase target landed before the anchor but whose natural deadline landed after it was given a stable per-(DB, CF) trigger time picked uniformly in [anchor, natural deadline]. Any one such file is spread out fine, but every file's window *starts* at the anchor, so the windows stack up there. Summed over a fleet whose file ages are spread across the period, the trigger density at time t after the anchor grows like ln(n/t) -- about 3.8x the steady rate in the first 1% of a period, and elevated for a large fraction of a period. Within a single DB that is minor, but a fleet-wide option change (e.g. a dynamic SetDBOptions from a config push) lines up every DB's anchor and so turns each DB's small front-load into one fleet-wide burst. The original simulator missed this because no scenario enabled phasing on a fleet whose files already had ages spread across the period: the upgrade scenarios start from a lockstep herd, and the staggered scenario enables phasing at DB open, before any file exists.

**The fix.** Phasing fires a file early by some `early_pull`, at `natural deadline - early_pull`. The old code computed it two ways: a normal file used

    early_pull = q * offset          (q = recovery_percent / 100)

while a near-anchor file (preferred phase before the anchor, deadline after) ignored that and was instead dropped at a uniform point in [anchor, natural deadline] -- the spreading branch that caused the spike. This change deletes the spreading branch and computes every file the same way:

    early_pull = offset * min(q, k * r / n)

with `offset` the gap from the natural deadline back to the preferred phase, `n = periodic_compaction_seconds`, `r = natural deadline - anchor`, and `k = 1/2` (`TriggerTime` carries the full derivation). The `min` is the crux: for a file due well after the anchor (large r) it selects `q`, reproducing the old steady-state pull exactly; for a file due soon after the anchor (small r) it scales the pull down toward 0, so near-anchor files stay close to their own deadlines instead of all jumping toward the anchor. For example, with k = 1/2 a file due only 0.1n after the anchor is pulled by at most 0.05 of its offset (it barely moves), whereas a file due a full period or more out gets the full q pull (for the recommended q <= 1/2). Resulting behavior:

- **Steady state is unchanged.** The full q pull applies whenever r >= q*n/k, which for q <= 1/2 is every file written at or after the anchor.
- **Never before the anchor, never after the natural deadline.**
- **Catch-up density is bounded** at about -ln(1-k)/k = 1.39x the steady rate right after an anchor on a uniform-age fleet, for any q.
- **Herds stay spread.** DBs sharing one deadline get pulls proportional to their own (differing) offsets, so they don't re-collapse. Capping the pull at q*r instead would hand a herd identical pulls and clump much of it onto one instant.

Files already past due at the anchor are unaffected and keep the existing n/4 grid.

`k` is fixed at 1/2 rather than tracking `q`. k = max(q, 1/2) is identical for q <= 1/2 (including the recommended 33), but at q = 100 it gives k = 1, which loses the density bound entirely; k = q was rejected because it noticeably slows the first de-herding cycle of a lockstep fleet. The price of fixing k = 1/2 is that for q > 1/2 the first cycle after an anchor (including DB open) spreads only as fast as q = 1/2.

Phasing now applies only to `periodic_compaction_seconds` up to 2^31 (~68 years), which keeps the scaled-pull arithmetic within 64 bits (asserted statically); longer periods use the classic deadline.

No effect when phasing is disabled (the default). No API or file-format changes.

Pull Request resolved: https://github.com/meta/rocksdb/pull/15505

Test Plan:
New/changed unit tests in db_compaction_test.cc, both of which fail against the previous implementation:
- PeriodicCompactionPhaseTriggerTime: replaced the subcase that asserted the old spread-to-deadline result with hand-computed results near the anchor for recovery percent 10 to 100, including the threshold where the full pull resumes, plus long-period cases: exact results up to the 2^31 limit (including the largest intermediate product) and the classic deadline beyond it. The geometric-convergence subcase is unchanged.
- PeriodicCompactionPhaseTriggerTimeRandomized (new, time-seeded): over random (n, recovery percent, phase, file time, anchor) it checks anchor < trigger <= natural deadline and trigger >= the steady-state trigger, with exactly the steady-state result for files written after the anchor (a full period after, for recovery percent > 50). With many files sharing one anchor and uniform ages, no 1%-of-period bin after the anchor exceeds 1.6x its share (expected 1.39x) for recovery percent 10, 33, 50, 67, 100. A herd sharing one deadline 0.62n after the anchor stays spread over >= 0.3n with no clump.

Simulation (out-of-tree harness, not part of this change): 1000 real RocksDB instances per scenario, `periodic_compaction_seconds` N = 100s, a real periodic-task scheduler, every compaction recorded; the previous code, k = max(q, 1/2), and k = 1/2 run concurrently with identical per-DB phases, plus a replicate of the previous code as a noise reference. New `midlife_dynamic_r*` scenarios model the production case: file ages uniformly spread, phasing enabled via SetDBOptions at 2.4N. Full interactive HTML (plots per scenario plus a side-by-side table; **fb-internal URL sanitization may require copy-pasting the URL manually**):
https://htmlpreview.github.io/?https://github.com/pdillinger/rocksdb/blob/38ba095d48e12b85c9a0ccc397c68a486c07a63a/pc_phase_switchover.html

Periodic compaction load relative to an even rate, in `midlife_dynamic_r*`:

| recovery % | old, first 0.05N | old, 0.1N-0.2N | new, 0.05N-0.4N | |---|---|---|---|
| 33 | 2.8x | 1.2x | ~1.4x |
| 50 | 2.7x | 1.8x | ~1.4x |
| 100 | 3.2x | 2.2x | ~1.4x |

Peak load there (max periodic compactions per DB per N, in N/40 bins) dropped from 3.5 / 3.3 / 3.7 to 2.0 / 1.7 / 1.8 for recovery percent 33 / 50 / 100.

The tradeoffs are all in first-cycle de-herding of a lockstep fleet: peak load rose from 3.3 to 3.8 (`upgrade_restart_r33`), 2.6 to 3.8 (`upgrade_dynamic_r50`), and 3.0 to 3.8 (`upgrade_dynamic_r100`), comparable to the 3.6 to 3.8 of `upgrade_dynamic_r33`; convergence at 50 and 100 finishes somewhat later. Cumulative badness was unchanged within noise, or better, in every scenario. Scenarios the change should not affect (`staggered_phased_r33`, `turnup_r33`, `natural_*`, `burst_r0` through `burst_r50`) matched the old code within run-to-run noise.

Reviewed By: jaykorean

Differential Revision: D128151011

Pulled By: pdillinger

fbshipit-source-id: b1b34d532647cd5e2280b9c613e659efb79da20b
2026-10-29 11:09:22 -07:00
Andrew Chang 898ace2b02 Make remote compaction honor preclude_last_level_data_seconds and preserve_internal_time_seconds
Summary:
X-link: https://github.com/facebook/rocksdb/pull/15457

**Problem:** a remote compaction (`CompactionService` / `DB::OpenAndCompact`) ignores `preclude_last_level_data_seconds` and `preserve_internal_time_seconds`.
- With tiered storage, every remote compaction into the last level writes recent (hot-window) data to the last level, which is the cold tier. A local compaction keeps that data in the proximal level.
- With `preserve_internal_time_seconds`, a remote compaction zeroes sequence numbers that a local compaction keeps, so their write-time information is lost.
- Remote outputs also get no `seqno_to_time_mapping` table property. Later compactions, local ones included, take write times only from their input files, so that data is treated as recent unless other inputs cover it. Files written by remote compactions before this fix stay that way after upgrading.

**Root cause:** the worker opens the primary's DB as a secondary. `SanitizeCfOptions()` treats the secondary as read-only and sets both options to 0 ("ignored in read-only DB"). `DBImplSecondary::CompactWithoutInstallation()` builds the compaction from these sanitized options, so `Compaction::EvaluateProximalLevel()` turns off per-key placement and `CompactionJob::Prepare()` computes no preserve window. The existing `CompactionServiceTest.PrecludeLastLevel` forces per-key placement and the cutoff through sync points, so it did not catch this.

**Fix:**
- `CompactionServiceInput` carries the primary's `preserve_internal_time_seconds` and `preclude_last_level_data_seconds`. The worker sets them on its copy of the CF options before it picks the compaction.
- Restoring the preserve window alone would bring back the kBottommostFiles infinite compaction loop for remote compactions. The worker does not know the compaction reason or the primary's DB-wide seqno->time mapping, so it cannot apply the version-carried preserve boundary that a local bottommost-files compaction applies. `CompactionServiceInput` therefore also carries that boundary (`preserve_time_min_seqno_floor`, set only for kBottommostFiles). The worker takes the max with it, the same as a local compaction does.
- The options are sent in `CompactionServiceInput` rather than re-read from the OPTIONS file (which the worker already loads before sanitizing) so that they always arrive together with the boundary. With the OPTIONS-file approach, a new worker paired with an old primary would get a preserve window but no boundary, which brings back the loop.
- With per-key placement on, the primary and the worker each decide it, and the worker's MANIFEST view can be newer than the primary's input version. The primary now also sends its proximal level, proximal output range type and `keep_in_last_level_through_seqno`. The worker returns `Status::Incomplete` on any mismatch, so the `CompactionService` can fall back to local compaction (the same pattern as the MANIFEST floor check). As a backstop, the primary falls back to local compaction if a result has proximal-level outputs while its own compaction has no proximal level.
- Compatibility: the new fields default to 0, and readers ignore unknown fields. So an old primary or an old worker keeps today's behavior. The fix takes effect when both are upgraded.

Reviewed By: pdillinger

Differential Revision: D126826333

fbshipit-source-id: cb59f0f75601e1cbb1233cb52c173d05dc127b92
2026-10-29 09:00:12 -07:00
Steph Pontikes b6582c84bc Add rolling MultiScan prefetch windows
Summary:
X-link: https://github.com/facebook/rocksdb/pull/15408

Add bounded rolling MultiScan prefetch windows to `IODispatcher`.

Invariants:

- Rolling prefetch is effective only for forward async scans with verified file-offset-ordered handles, a nonzero window that fits every uncached block, and no global dispatcher memory limit; otherwise it disables itself and preserves ordinary iterator and prefetch error semantics.
- Each SST partitions uncached data blocks into ordered, variable windows whose physical I/O span, including coalesced gaps, is at most `max_prefetch_size`; a gap beyond `io_coalesce_threshold` ends the current window early.
- `Prepare` submits the first window, first use of a block in the current window submits the next window, and reaching a queued window waits for that whole window instead of issuing an individual block read while rolling remains enabled.
- Rolling consumption is monotonic: forward jumps discard obsolete queued windows, successful portions of partial dispatches are removed from pending bookkeeping, benign absent or drained pending groups are treated as already handled, and failed speculation falls back without duplicate I/O or a rolling-specific status.

Reviewed By: anand1976

Differential Revision: D121817105

fbshipit-source-id: 1af83d23634b20cc2938a4c5ad6137fa700a0c7b
2026-10-28 19:09:55 -07:00
Steph Pontikes d27327384d Extract I/O prefetch scheduling policy
Summary:
X-link: https://github.com/facebook/rocksdb/pull/15448

- Move the existing global prefetch-memory policy and pending queue into a dedicated scheduler module.
- Make every returned `ReadSet` own a configured scheduler.
- Keep I/O submission, buffers, completion, and block construction in `IODispatcher`.
- Preserve the existing default scheduling and coalescing behavior.

Reviewed By: anand1976, xingbowang

Differential Revision: D126191480

fbshipit-source-id: 515bbe66dafc26908d26e0e7c7c772d952e56f3f
2026-10-28 19:09:55 -07:00
Peter Dillinger 0adcea5ea4 Update for GitHub repository move to meta/rocksdb (#15508)
Summary:
The RocksDB GitHub repository moved from github.com/facebook/rocksdb to github.com/meta/rocksdb. Nearly all GitHub Actions jobs are gated on `github.repository_owner == 'facebook'` (to keep them from running on forks), so after the move those jobs were skipped on new PRs and pushes, and in the nightly and weekly schedules.

This change updates the repository for the new location:
* Fix: All `repository_owner` checks in the workflows now compare against 'meta', so the PR, nightly, weekly, and clang-tidy jobs run again.
* Change: CI container images now come from ghcr.io/meta/rocksdb_ubuntu, and the Dockerfile build/push instructions match.
* Change: Workflow names facebook/rocksdb/* are now meta/rocksdb/*. The Claude review workflow triggers on the pr-jobs workflow by name, so its `workflow_run` trigger is updated to match. (Required status checks are keyed by job name, not workflow name.)
* Change: Every github.com/facebook/rocksdb URL in the repo now uses github.com/meta/rocksdb. This covers URLs used by CI and build scripts (third-party-deps release downloads, including DEPS_URL for Java builds; git remotes in setup-upstream and check_format_compatible.sh), the docs site config (`ghrepo`) and navigation, Maven SCM metadata, and links in code comments, docs, HISTORY.md, and blog posts. Historical links are included because GitHub's redirect from the old URL is not dependable for every link: some old issue URLs return 404 while the same issue under meta/rocksdb works.
* Change: build_tools/format-diff.sh finds the upstream remote by matching either meta/rocksdb.git or facebook/rocksdb.git, because existing clones still use the old remote URL (which GitHub redirects for git operations).
* Remove: The stale CircleCI status badge from README.md. CircleCI is no longer used.
* Add: A final step in nightly's build-format-compatible job that fails if https://github.com/facebook/rocksdb stops redirecting to https://github.com/meta/rocksdb (or the old name is taken by another repo). Existing releases (e.g. DEPS_URL in java/Makefile as of v11.12), existing clones, and external links still depend on that redirect, so this alerts the RocksDB team if it breaks. The step runs even when the compatibility test fails.

Not addressed here: runner labels (16-core-ubuntu, etc.) and secrets are configured per GitHub org, outside the repo. Any that are missing in the meta org need to be set up there.

Pull Request resolved: https://github.com/meta/rocksdb/pull/15508

Test Plan:
CI on this PR exercises the main fix: jobs should run instead of being skipped.

Manual checks from an unrestricted network:
* https://github.com/facebook/rocksdb returns a single 301 to https://github.com/meta/rocksdb, then 200, and `git ls-remote` via the old URL works. Old pull, wiki, blob, and release-download URLs redirect, but old issue URLs for https://github.com/meta/rocksdb/issues/9275, https://github.com/meta/rocksdb/issues/10080, and https://github.com/meta/rocksdb/issues/13293 returned 404 (https://github.com/meta/rocksdb/issues/12038 redirected) even though all four exist under meta/rocksdb.
* A CI log showed ghcr.io/meta/rocksdb_ubuntu:22.1 pulling successfully.

The new nightly step won't run before merge. The exact curl command it uses printed the expected `200 https://github.com/meta/rocksdb` when run manually. Separately, its script was extracted from the parsed workflow YAML and run against a stub curl: it passes for a redirect to meta, fails for a 404 and for a 200 without redirect, and reports a network failure distinctly. After merge, a manual workflow_dispatch of the nightly workflow can confirm it in CI.

Reviewed By: archang19

Differential Revision: D128195050

Pulled By: pdillinger

fbshipit-source-id: 3d42aa271b706eec2ef6c1586643056084fc866f
2026-10-28 17:23:50 -07:00
Andrei Preda 9457606301 Stop pinning iterators over immortal mmap blocks
Summary:
With `ReadOptions::pin_data`, `LevelIterator` pins every table iterator
it leaves and `BlockBasedTableIterator::ResetDataIter()` delegates every
data block it leaves to the `PinnedIteratorsManager`, until the `DBIter`
is destroyed. For an immortal table (read-only with `max_open_files=-1`)
whose uncompressed blocks are read through mmap, the returned slices
point into the mapping, which the reader keeps alive for the iterator's
Version. The pinned iterators and `Block` objects keep nothing alive and
only grow memory with every seek.

Add `InternalIterator::IsSafeToDropWhilePinned()` (default false), true
for a `BlockBasedTableIterator` whose returned slices all point into an
immortal source. `LevelIterator` now deletes such an iterator instead
of pinning it, and `ResetDataIter()` frees such a block instead of
delegating it. Owned and cached blocks are delegated as before.

X-link: https://github.com/facebook/rocksdb/pull/15459

Reviewed By: anand1976

Differential Revision: D127012316

Pulled By: xingbowang

fbshipit-source-id: e29957fb615915abd98a40f98815205bad14c696
2026-10-28 16:27:13 -07:00
Omkar Gawde 302eaa8b1f Share GetMergeOperands finalization across DB modes and reset only filled output slots
Summary:
X-link: https://github.com/facebook/rocksdb/pull/15503

Why the change is required:
After D123382954, the primary, read-only, and secondary `GetMergeOperands` paths still each have their own copy of output setup and post-processing, so they can drift apart again. Read-only and secondary also copy every operand, even when the result is large enough for the primary's zero-copy bundle (the TODO for PR #10458). Separately, xbw pointed out in review that the up-front output reset scales with the caller's capacity rather than the result.

Previous behavior:
- Each mode set up `MergeContext` and published operands with its own code. Only the primary used the shared cleanup bundle, which pins a SuperVersion and the iterator pins for large results. Read-only and secondary always called `PinSelf` on each operand.
- All three modes called `Reset()` on all `expected_max_number_of_operands` output slots before the lookup, even when the caller had already reset them or only a few operands existed. A caller passing a capacity of 1000 to read 3 operands touched 1000 `PinnableSlice` objects on every call.

New behavior:
- All three modes publish operands the same way. Read-only and secondary now return large results pinned through the shared bundle, just as the primary does.
- `GetMergeOperands` resets only the slots it fills, each one just before writing it, so the cost scales with the result instead of the capacity. Slots after `*number_of_operands`, and the whole array on `Incomplete`, `NotFound`, or an error, are left as the caller passed them. Previously they were cleared.

Implementation:
- `DBImpl::PrepareGetMergeOperands` now only forwards `GetMergeOperandsOptions` into `MergeContext`.
- `DBImpl::FinalizeGetMergeOperands` reports the count, checks capacity before writing anything, then resets and fills each slot. Large results move into the existing `GetMergeOperandsState` bundle, with a new SuperVersion reference and the moved `PinnedIteratorsManager`; small results are copied. The per-slot `Reset()` is still required because a reused slot can hold an earlier pin, and `PinSlice`/`PinSelf` assert the slot is unpinned.
- All three `GetImpl` variants call the shared pair. Callers must invoke finalization while their SuperVersion reference and pins are still held; secondary releases its reference afterwards, as in D123382954. The primary keeps the `OkMergeOperandThresholdExceeded` status from `merge_operand_count_threshold` unless finalization fails.

Safety, compatibility, and limitations:
The reset change is visible through the public API in every mode, including the primary. It still satisfies the documented contract: `db.h` says `merge_operands` is not populated on `Incomplete` and that callers own `Reset()` of the entries. A caller that inspected slots after a failed call, or past `*number_of_operands`, will now see its own earlier contents instead of empty slices. A pin left in a slot this call doesn't touch stays alive until the caller resets it. This diff adds a behavior-change release note. Pinned results from read-only and secondary carry the same requirement as the primary's: the DB must outlive them. There is no ABI or file-format change, and snapshot acquisition and the memtable/SST lookups are unchanged.

Reviewed By: jaykorean

Differential Revision: D123475366

fbshipit-source-id: 615e09ce0658c2ef86235810ff59dbfc3b54c289
2026-10-28 11:47:00 -07:00
Omkar Gawde a8a626ba8b Enforce GetMergeOperands contract and operand lifetime in secondary modes (#15332)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15332

Why the change is required:
Read-only and secondary DBs use their own `GetImpl` paths, which had drifted from the primary `GetMergeOperands` contract. A caller passing an output array sized by `expected_max_number_of_operands` could get writes past the end of that array. Separately, both paths could copy operands out of memory they had already released.

Previous behavior:
- Both modes pinned every accumulated operand without checking `expected_max_number_of_operands`. More operands than capacity meant writes beyond the caller's array instead of `Incomplete`.
- Output slots were not cleared first, so a reused array could expose stale values.
- `MergeContext` never received `GetMergeOperandsOptions`, so `continue_cb` was ignored.
- Both modes destroyed the SST lookup's `PinnedIteratorsManager` before copying operands into the caller's slices. SST operands borrow block-cache memory that is freed once unpinned under cache pressure, so the copy in `PinSelf` could read freed memory.
- Secondary only: the SuperVersion reference was also released before the copy. Memtable operands are borrowed slices into the memtable, and a `TryCatchUpWithPrimary` that completes in between can retire that memtable, with the same heap use-after-free.

New behavior:
Both modes now follow the primary contract. They clear every advertised output slot, report the required operand count, and return `Incomplete` with `KMergeOperandsInsufficientCapacity` before writing anything to an undersized array. `continue_cb` stops collection and returns the collected newest suffix. Both modes copy operands while the block pins are still held, and secondary also holds its SuperVersion until the copy is done.

Implementation:
- Reset the advertised output range up front, forward the options into `MergeContext`, and check capacity before the copy loop. Both match `DBImpl::GetImpl`: the reset is skipped when no options are passed, while the capacity check dereferences them unconditionally.
- Both modes: hoist `PinnedIteratorsManager` to function scope, as `DBImpl::GetImpl` does. Secondary also moves the SuperVersion release after the copy.

Safety, compatibility, and limitations:
Only `GetMergeOperands` results change in read-only and secondary modes, and the primary path is untouched. For `Get` and `GetEntity`, the only change is that the block pins, and on secondary the SuperVersion, are released a few statements later. Read-only needs no SuperVersion change because it never installs a new SuperVersion. There is no API, ABI, or file-format impact. Callers that relied on the old behavior were writing past their declared capacity. Large-result zero-copy pinning for these modes is still a TODO (PR #10458) and is handled in the follow-up diff.

Reviewed By: jaykorean

Differential Revision: D123382954

fbshipit-source-id: 7b0c49b5e96f464a3b8c974940713261a9c0e15e
2026-10-28 11:09:41 -07:00
Josh Kang 4c38c45ac1 Avoid redundant executor scheduling in CoroDB::CoGet (#15488)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15488

`CoroDB::CoGet` queues its child read task onto the selected read EventBase even when the caller already uses that executor. The DB and `SstFileReader` overloads now await the child directly when `co_current_executor` matches that EventBase, avoiding redundant queues on task entry and return. Calls from another executor keep the existing dispatch.

This applies to reads that perform I/O as well as cached reads. Asynchronous I/O retains its existing suspension and resumption behavior on the read executor; cancellation and statistics handling are preserved.

Reviewed By: pdillinger

Differential Revision: D127665852

fbshipit-source-id: 73d969d0abeefc68cb5b34b1164f3a2da943516e
2026-10-27 17:34:39 -07:00
Jay Huh 5d9da10d28 Fix ASSERT_STATUS_CHECKED and MSVC build failures from BlobDB remote compaction (#15498)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15498

Why: "Support BlobDB in remote compaction" (#15496) broke four GitHub CI jobs on `main`: `build-linux-static_lib-alt_namespace-status_checked` and the three `build-windows-msvc` jobs. The `main` run just before it passed all four; every run that includes it, on `main` and on unrelated PRs (including reruns), fails them.

Previous behavior:
- `ASSERT_STATUS_CHECKED` builds abort with "Failed to check Status" in `compaction_job_test` and `compaction_service_test`:
  - `CompactionOutputs::AddToOutput()` gained an early `return c_iter.status();` after `PrepareOutputIfNeeded()`. It leaves the local `s` unchecked when nothing assigned it earlier in the call. This is production code; `ResumableCompactionServiceTest.BlobCompactionResumeRangeExhaustionFallsBackLocally` reaches it.
  - New tests never check `CompactionServiceResult::status`: `CompactionJobTest.ResultSerialization` (`deserialized_old_result`), `CompactionServiceTest.RemoteBlobWorkerDisablesBlobOutputSafely`, a sync point callback in a blob-file rollback test, and every test that reads all results through `MyTestCompactionService::GetResults()` but checks only the one it looks for.
- MSVC builds with `/W4 /WX`. Two new lines in `compaction_service_test.cc` pass `'a' + i` (an unbounded `int`) to the `char` parameter of `std::string(size_t, char)`. That narrowing is a warning MSVC reports and `/WX` turns into an error. The GitHub logs are not readable without repo admin rights, so this cause is inferred, not read from the log: clang's `-Wimplicit-int-conversion` flags exactly these two lines and nothing else the commit added, and the existing `'a' + (i % 26)` form, which compilers can prove fits in a `char`, already builds on Windows in `compact_files_test`.

New behavior:
- `AddToOutput()` assigns `c_iter.status()` to `s` and returns `s`, like its other exits. The returned status is unchanged.
- The tests check or explicitly permit each result status. `GetResults()` permits every result's status, the way `GetResult()` and `GetInput()` already permit their read status; tests that care still assert on the result they select.
- The two conversions use `static_cast<char>`; `i < 20`, so the values are unchanged.

No format, API, or behavior change.

___

landed-with-radar-review

Differential Revision: D127938230

fbshipit-source-id: 0c5b05e28b07898067a2038e9376225940e910ed
2026-10-27 17:12:11 -07:00
Bikash Chandra 83747eb79f Support BlobDB in remote compaction (#15496)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15496

Remote compaction previously transferred only SST inputs and output metadata. A worker could not reliably read blob-backed values for a compaction filter, output SST metadata did not preserve references to existing blob files, and blob garbage collection could derive a different cutoff from the worker view.

This change adds BlobDB support to the existing remote-compaction path. Existing `enable_blob_files` and `blob_file_starting_level` settings determine whether eligible output may create blobs; compactions whose input SSTs reference blobs transport those dependencies regardless of new-blob eligibility.

Each subcompaction transports the live blob-file suffix referenced by overlapping input SSTs. For blob-eligible output, the primary atomically reserves a half-open blob-file-number range. The worker uses native `CompactionJob`, `BlobFileBuilder`, and `BlobGarbageMeter` machinery and returns `BlobFileAddition`, `BlobFileGarbage`, and SST dependency metadata. When blob GC is enabled, the primary computes the cutoff from its authoritative input Version and serializes it in `CompactionServiceInput`; the worker consumes that cutoff rather than recomputing from its local view.

Workers advertise result-side BlobDB and GC-cutoff protocol markers. The primary declines old or non-capable results before installation. It validates reserved additions, transported-input garbage, output dependencies, cross-subcompaction aggregate garbage, staged file existence, optional content checksums, and each staged blob footer count plus the overflow-safe header+record-bytes+footer physical size. Metadata or structural corruption declines the result and reruns locally, while genuine validation I/O errors remain `kFailure`. Blob files move before SSTs and register through `BlobFileCompletionCallback`; rollback physically deletes each installed blob before removing its `SstFileManager` entry, and a cleanup error remains a failure with the tracking entry preserved.

Resumable workers checkpoint blob additions, cumulative garbage, and the next reserved number. Checkpoints cut SST and blob outputs before preparing the next key. `CompactionIterator` records when processing has already consumed an earlier input entry of the current user key, and progress is not checkpointed at that unsafe boundary. Resume therefore does not seek back over already-counted input, fixing both the baseline record-count mismatch and blob-garbage double counting. At safe boundaries, the raw pre-filter inflow for the current entry is subtracted from the blob-garbage checkpoint; per-entry inflow metering exists only for resumable jobs.

`REMOTE_COMPACT_INPUT_BLOB_FILE_COUNT` counts each distinct transported blob file once per compaction job. `REMOTE_COMPACT_BLOB_RESULT_DECLINED_COUNT` counts primary-side blob-result declines only; service-side `kUseLocal` and pre-dispatch reservation fallback do not increment it. Both tickers are exposed through C++ only; JNI and Java bindings are intentionally omitted because the Java ticker jbyte space is exhausted.

Compatibility and failure behavior:
New fields deserialize with safe defaults. A new primary declines an old worker when blob inputs, blob-eligible output, or a primary-provided GC cutoff requires the new protocol. A new worker receiving an old input without the cutoff field disables blob GC for that job, avoiding a worker-local cutoff. Primary-side declines rerun locally, so `CompactRange` and `CompactFiles` succeed without a background error. The `CompactionService` contract is unchanged: a service returning `kFailure` preserves the worker status, including `Status::Incomplete`; mapping a worker decline to `kUseLocal` remains the integrator's choice. ZippyDB production additionally maps service failures to local fallback through `HandleFailure(FALLBACK_LOCAL)`.

Reviewed By: jaykorean

Differential Revision: D122097088

fbshipit-source-id: 7790768fe8a25dd2451a5d69d809d356899cabab
2026-10-27 13:11:50 -07:00
cm-wang 6d03b3df45 Match blob direct write generations to the memtables a flush picked (#15481)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15481

With blob direct write, each memtable switch moves the column family's open blob files into a new generation, queued in `BlobFilePartitionManager::pending_generations_`. A flush then seals and publishes the generations of the memtables it flushes.

`PrepareFlushAdditions(n)` always used the first `n` queued generations, and `CommitPreparedGenerations(n)` always popped the first `n`. That is only right if flushes pick, and finish, the oldest memtables in order. With more than one flush running for a column family (`max_background_flushes` > 1, or `max_background_jobs` >= 8), a second flush skips the memtable the first one is still flushing and picks a newer one, but prepares the oldest generation, which belongs to the first flush. Both flush edits then add the same blob file, and installing them fails with `Corruption: VersionBuilder: Blob file #N already added`, which stops the database. The newer memtable's own blob files are never published, and committing the second flush pops a generation it did not own. Atomic flush prepares before it waits to install in order, so it has the same problem.

Removing generations by the flush's own memtables is not enough either. A newer flush that finishes first only marks its memtable completed: the older flush installs both, or rolls the newer one back too if it fails. A generation removed by the newer flush is then gone when the retried flush looks for it (`Corruption: Missing blob direct write generation metadata for flush`).

Fix:
- Tag each generation with the ID of the memtable whose switch created it (`RotateCurrentGeneration(memtable_id)`). `PrepareFlushAdditions()` takes the IDs of the memtables the flush picked and seals exactly those generations, in both flush paths.
- Remove generations only once installed: after a successful flush, `CommitFlushedGenerations()` drops the generations of the memtables older than the earliest one still in the immutable list (memtables leave it only by installation, in order), and stops tracking their sealed files as manager-owned.

Reviewed By: xingbowang, archang19

Differential Revision: D126852567

fbshipit-source-id: 910bf466674cf8bc89bc3ccbbcf08c684968ad72
2026-10-27 12:49:41 -07:00
cm-andrew f0434b6972 Let async reads record get_cpu_nanos without the CPU-time perf level (#15489)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15489

**Adds `SetAsyncReadCPUTimeEnabled()`, so an async read can report the CPU it used without turning on every perf timer.**

- **Problem:** a coroutine read runs on the read executor, not the caller's thread. RocksDB sums its CPU in `PerfContext::get_cpu_nanos`, but only at `kEnableTimeAndCPUTimeExceptForMutex`, which also turns on every wall-time timer.
- **Change:** with the new opt-in set and the perf level at least `kEnableCount`, an async read times only its running slices into `get_cpu_nanos`. The setting is thread-local, like the other async-read stats settings, so there are no option or C API changes. It resets after each read.
- **Unchanged:** reads without the opt-in, sync reads, and async reads that run synchronously on the caller's thread.

Reviewed By: archang19, joshkang97

Differential Revision: D127495339

fbshipit-source-id: 1b0ab016552f216409a1a2c99ed1733269e5dbc3
2026-10-27 08:34:10 -07:00
Jay Huh 0c79400bf7 Add range-based DB split and merge utilities (#15240)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15240

Why:
Shard splits and merges currently require scanning ranges and rewriting them through the write path. These utilities transfer selected half-open `[begin_key, end_key)` ranges through checkpoints, clipping compactions, and SST ingestion. Boundary-only clipping is deferred; kept keys are still rewritten by compaction.

Behavior:
- `SplitDB` builds and verifies a staged destination, rewrites its OPTIONS file with the source CF options without reopening it, and publishes it by rename and directory fsync. The destination is not opened with the source options; the caller's first open validates it, with sync or async file opening as the caller chooses. Source data remains available. After switching traffic, callers run retryable `DeleteSplitRanges`; combining those steps would leave a failure ambiguous after source deletion.
- `MergeDB` clips a source checkpoint and ingests the latest range contents into empty destination ranges in one crash-atomic multi-CF MANIFEST update. It transfers source sequence numbers rather than snapshot history.
- `db_split_merge` provides `split`, `delete-ranges`, and `merge` commands with hex-encoded bounds and strict OPTIONS loading.

Safety and compatibility:
- Callers must stop writes to affected ranges, reserve the temporary paths, and avoid destination snapshots during merge. Existing destination snapshots are rejected. Merge visibility across CFs is not atomic for concurrent readers.
- Clipping disables source filters, callbacks, and the WAL filter; source flushes and subsequent source deletion retain their normal filter behavior. Checkpoint opens use strict recovery, and failed splits remove partial outputs.
- Support is limited to plain writable DBs with compatible BlockBasedTable CFs, leveled/universal compaction, and colocated SSTs. Transaction/TTL/blob wrappers, timestamps, ingest-behind, and other incompatible options are rejected or documented as unsupported in `db_split_merge.h`. Merge requires the same FileSystem object and rejects destination tombstone conversion; old iterators created with conversion enabled must first be retired.

Review fixes:
Live blob detection now reads `kNumBlobFiles` without copying SST metadata. `ClipColumnFamily` now checks surviving file presence instead of treating empty boundary strings as an empty CF: previously, a lone empty-string key in L0 caused split/merge of an excluding range to fail verification. Depends on D123306376 for the separate begin-boundary fix.

Validation:
See the test plan for regression failures without the fixes, sanitizer runs, stress repetitions, full Make checks, and platform/tooling limitations.

Reviewed By: xingbowang

Differential Revision: D120757697

fbshipit-source-id: df04ea49100eb102ba04321fc23fdca48fc379e5
2026-10-26 17:58:13 -07:00
Anand Ananthabhotla 935cdad34d Bypass permanently inapplicable trie-index modes (#15484)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15484

Four `TrieIndexDBTest` mode combinations are permanently inapplicable: `MigrationFullPath`, `RollbackFromPrimaryWithoutCompactSucceeds`, and `EstimatedSizeNonZero` in `kCustomOnly`, and `PrefetchWithCustomIndexWrapper` in `kStandardDefault`. Using `ROCKSDB_GTEST_SKIP` for these intentional omissions generated eight GTest/TSan skipping issues in T294331850.

Change exactly those four calls to `ROCKSDB_GTEST_BYPASS`, matching the permanent-parameter-omission contract documented in `test_util/testharness.h` and already used elsewhere in this test file. Keep the original guards, `return`s, fixture, names, and all 253 registrations unchanged. BYPASS prints its reason and records GTest success instead of a skip; these inapplicable combinations still do not execute the test assertions. All eight applicable cases and every other byte of the test file are preserved.

No derived fixtures or suite splits remain. This changes test reporting only, not database behavior, API/ABI, persisted formats, or performance. Unrelated capability-dependent skips and existing bypasses are untouched. TestX recovery must be audited after landing; passing bypass cases are not evidence of additional functional coverage. Public GitHub export remains a separate prerequisite and is not authorized by this revision request.

___

`Trail`: https://trail.nest.x2p.facebook.net/c/da4fc690-13ac-410e-b1ee-faf2b010e677

___

landed-with-radar-review

Differential Revision: D127646540

fbshipit-source-id: 7159e20c88e358c68b1873008ff4134ceae29162
2026-10-26 14:02:41 -07:00
cm-rockee c631d45bb6 Notify listeners of FIFO temperature-change trivial copies (#15474)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15474

**Why:** With `CompactionOptionsFIFO::allow_trivial_copy_when_change_temperature`, a temperature change copies an SST byte for byte to a new file number, but nothing reported the new file. There was no `OnTableFileCreationStarted()` or `OnTableFileCreated()` call, no `table_file_creation` event in the LOG, and `CompactionJobInfo::table_properties` had no entry for the output. Anything that tracks SST files through listener events misses these files. Two `TODO (mikechuang)` comments in `DBImpl::BackgroundCompaction()` left this undone because reporting the input's properties "will break some existing stress tests".

**The stress test problem:** D73295404's history shows the attempts. It first reported empty `TableProperties`, then partly synthetic ones, then the input's exact properties with the reason `kCompaction`, and finally removed the calls.
- Empty or synthetic properties fail the `data_size`/`raw_key_size`/`num_entries > 0` asserts in `DbStressListener::OnTableFileCreated()`.
- The exact properties are correct, but a copy keeps its input's SST unique ID. The ID is derived from `db_id`, `db_session_id` and `orig_file_number` in the copied properties, and the copy's MANIFEST entry already reuses `in_file->unique_id`. `DbStressListener` asserts that every created file has a never-seen unique ID, so it aborts with "Duplicate partial unique ID found". With this diff's notifications but without the stress-listener change below, a FIFO tiered db_stress run aborts that way within seconds.
- Two more constraints apply. Every `OnTableFileCreationStarted()` needs a matching `OnTableFileCreated()`, including on failure, because `DbStressListener`'s destructor asserts that no creations are pending. And listeners must not be called with the DB mutex held, which this code path holds.

**New behavior:** For each file a trivial copy creates:
- `OnTableFileCreationStarted()` before the file is created, with reason `kCompaction`.
- `OnTableFileCreated()` and the `table_file_creation` LOG event once the copy is written, before the version edit is committed, the same timing as other compaction outputs. They carry the input file's table properties unchanged. Those properties exactly describe the copied bytes, including `orig_file_number` and therefore the unique ID. If the copy fails, `OnTableFileCreated()` gets the failure status with empty properties, as `CompactionJob` reports a failed output open.
- `CompactionJobInfo::table_properties` (in `OnCompactionPreCommit()`/`OnCompactionCompleted()`) includes the output file with those properties, as for other compaction outputs.

**Implementation:**
- In the trivial copy branch, the input's properties come from `Compaction::GetOrInitInputTableProperties()`, which `NotifyOnCompactionBegin()` has usually already loaded. The branch also records them with `Compaction::SetOutputTableProperties()`.
- The DB mutex is released while loading the properties and around each listener/event-log call, as `NotifyOnCompactionBegin()`/`NotifyOnCompactionPreCommit()` already do in this branch. The new file number is covered by the pending outputs captured in `BackgroundCallCompaction()`, and the input file by `being_compacted`.
- `DbStressListener::OnTableFileCreated()` checks for a new unique ID only when `orig_file_number` equals the file's own number. A file whose properties name another original file number is a copy, and its ID was checked when the original was created.

**Safety, compatibility, limitations:**
- No public API, option or file format change. Only FIFO compaction with `file_temperature_age_thresholds` and `allow_trivial_copy_when_change_temperature = true` is affected.
- Listener-visible change: these copies now produce file creation events, and their properties repeat the original file's unique ID. A listener that assumes every created SST has a new unique ID (as `DbStressListener` did) needs the same adjustment.
- If the input's properties cannot be loaded, the copy proceeds and is reported with empty properties.
- Unchanged and out of scope: the copy itself still runs with the DB mutex held.

Requested by: Peter Dillinger (peterd)

Reviewed By: pdillinger

Differential Revision: D127283628

fbshipit-source-id: 418051286e3527583874839afbd2c309b06d4096
2026-10-26 11:30:46 -07:00
Richard Barnes ff044b05ae Fix -Wunreachable-code-break findings in fbcode/internal_repo_rocksdb (#15480)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15480

D127336061 enables `-Wunreachable-code-break` through the PACKAGE files of `fbcode/internal_audit_infrastructure`, `fbcode/internal_chub`, `fbcode/internal_repo_rocksdb`, `fbcode/internal_tool_authorization_service`, `fbcode/internal_tree_sitter_tests`. This diff fixes the code that warning flags there, so the PACKAGE change builds cleanly on top of it.

Fixes the new warning in `fbcode/internal_repo_rocksdb/repo/db/c_test.c`: in the `CheckPutCF`, `CheckDelCF` and `CheckMergeCF` callbacks, the `default:` case is `CheckCondition(false);`, whose macro expansion always calls `abort()`, so the `break;` after it was unreachable and is removed. Test-only change; behavior is unchanged.

Reviewed By: dtolnay

Differential Revision: D127368328

fbshipit-source-id: c46ef678e13545b032ba4e5935c7864dc3e95c71
2026-10-26 09:54:17 -07:00
Xingbo Wang a5c1c345b2 Stress column family lifecycle recovery (#15434)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15434

Why the change is required: A filesystem write failure during column family creation can persist only one side of the MANIFEST and OPTIONS metadata update. Existing crash profiles did not repeatedly change column family topology or deliberately fault both metadata files, so this inconsistency could escape stress coverage.

Previous behavior: db_crashtest could randomize OPTIONS tracking across independent runs, but it had no column family lifecycle profile, no targeted OPTIONS or MANIFEST write faults during CreateColumnFamily, and no coverage gate proving repeated drop/create recovery. Unsafe verification and configuration combinations could also turn retained database contents into false positives after restart.

New behavior: --cf_lifecycle runs a four-phase blackbox schedule: an OPTIONS write fault with tracking enabled, clean recovery with tracking disabled, a MANIFEST write fault with tracking enabled, and clean repeated column family recreation with tracking enabled. Each writable open checks exact persisted OPTIONS column family membership against the MANIFEST view. The lifecycle wrapper requires observed OPTIONS and MANIFEST fault exits plus at least two successful lifecycle operations before final verification.

Implementation: add lifecycle flags and validation to db_stress, target only kWrite operations for the selected metadata file type, exit immediately with a dedicated status after an exact target marker, and preserve ordinary non-lifecycle column family replacement ordering. The Python driver owns lifecycle activation and appends authoritative phase, recovery-state, and sanitizer lock-bound flags after flagfile or forwarded overrides. It rejects arguments and modes that could bypass the schedule or invalidate the recovery oracle. Lifecycle-only completion checks enforce successful final verification; ordinary blackbox runs retain their historical final-verifier timeout handling and a single stabilization delay.

Safety and unsupported cases: this is limited to a local blackbox stress profile with two column families, one foreground thread, one database, and no persistent expected-state directory. It rejects remote filesystems, remote compaction, transactions, BlobDB and dynamic blob options, TTL, secondary/read-only modes, MANIFEST reuse, reopen, unrelated fault injection, and verification modes that can report retained keys as mismatches. Fault coverage is intentionally limited to selected OPTIONS and MANIFEST kWrite operations; it is not a general filesystem-fault API. Strict fault-observation, coverage, and final-verification exit checks are limited to --cf_lifecycle; ordinary blackbox behavior is unchanged.

Validation: focused Python tests cover scheduling, authoritative arguments including recovery-state and lock-bound precedence, exact marker counting, repeated-success requirements, and lifecycle-only final-verification failure handling. Normal, ASAN, and TSAN builds exercised both targeted metadata failures, the tracking transition, repeated lifecycle changes, and final recovery. The exact ordinary mini-crash CI profile also passes locally. Stress timing remains probabilistic, and local make check-sources is unavailable in a Sapling checkout; CI covers the Git-only source check.

Reviewed By: rban1

Differential Revision: D125875013

fbshipit-source-id: 895f1fb6ab66cddc5b4ad8ac1c3e017c3a59dd2d
2026-10-26 09:52:21 -07:00
Jay Huh ea31360536 Optionally record ReadOptions and WriteOptions in query traces (#15460)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15460

Why: a query trace keeps only each call's arguments (the write batch, keys, iterator bounds), and replay runs every call with default ReadOptions and WriteOptions. A workload that writes with `disableWAL` or reads with `fill_cache=false` therefore replays differently from how it ran: WAL writes and block cache fills that never happened live. That matters to anyone who evaluates option changes by replaying a trace, such as auto-tuning (D123483160).

New behavior:
- Add `TraceOptions::include_read_write_options` (default false). When set, every Write, Get, MultiGet, Seek and SeekForPrev record also holds the options of its call:
  - WriteOptions: `sync`, `disableWAL`, `no_slowdown`, `rate_limiter_priority`;
  - ReadOptions: `verify_checksums`, `fill_cache`, `ignore_range_deletions`, `async_io`, `optimize_multiget_for_io`, `tailing`, `total_order_seek`, `auto_prefix_mode`, `prefix_same_as_start`, `pin_data`, `adaptive_readahead`, `background_purge_on_iterator_cleanup`, `auto_readahead_size`, `allow_unprepared_value`, `read_tier`, `rate_limiter_priority`, `readahead_size`, `max_skippable_internal_keys`, `value_size_soft_limit`.
- Add `WriteQueryTraceRecord::GetWriteOptions()` and `GetReadOptions()` on the Get, MultiGet and iterator records (nullptr when not recorded). The default replayer uses them instead of default options.
- With recorded options, a replayed call can fail the way the live call could. The replayer reports such a status in the record's result, as it does NotFound, and goes on: for writes, `Incomplete` with `no_slowdown` and `InvalidArgument` with `sync` and `disableWAL` (rejected after tracing); for reads, `Incomplete` when `read_tier` is not `kReadAllTier`, `Aborted` with `value_size_soft_limit`, `NotSupported` with `kPersistedTier`, and for seeks also `Incomplete` with `max_skippable_internal_keys` and `NotSupported` with `tailing` (a tailing iterator fails `SeekForPrev()` and seeks over range tombstones). Every other failure (e.g. a column family or an option the replay DB lacks, or a replay DB that cannot create tailing iterators at all, such as a secondary) still stops the replay, and records without recorded options are handled as before.
- A captured seek result holds the value: with a recorded `allow_unprepared_value`, the replayer calls `PrepareValue()` before copying it (a blob load error becomes the seek's status). Without a result callback the value stays unprepared, as in the live call.
- Fix: `Replay()` returned OK when a record's execution returned `Incomplete`, taking it for the end of the trace, and the rest of the trace was not replayed. Only the trace reader's `Incomplete` now means trace end.
- C API: `rocksdb_trace_options_{set,get}_include_read_write_options`.

Trace format (version-2 query records; fields are encoded in payload_map bit order):
```
bit 0..10   existing fields                         unchanged
bit 11      kWriteOptions         \
bit 12      kGetReadOptions        |  only with include_read_write_options:
bit 13      kIterReadOptions       |  one length-prefixed blob, varint32 flags
bit 14      kMultiGetReadOptions  /   then varint scalars; later fields append
```

Implementation:
- `TracedWriteOptions` / `TracedReadOptions` (trace_replay.h) hold the recorded fields and encode/decode the blob; the decoder ignores bytes it does not know at the blob's end, and rejects out-of-range enums.
- `DBIter` keeps a `TracedReadOptions` built from its ReadOptions for the seeks it traces. Grouped writers' options come from `WriteThread::Writer`. `IteratorSeek` and `IteratorSeekForPrev` share one implementation.

Compatibility and limitations:
- Off by default: traces are byte-for-byte unchanged and every existing reader keeps working.
- With it on, the new fields come after all existing ones. Older decoders iterate the set bits; in release builds they skip unknown bits, in debug builds they assert. This version reads traces written without the flag as before (the accessors return nullptr).
- Not recorded: `low_pri` (not kept on grouped writers), `ignore_missing_column_families`, snapshots, timestamps, deadlines and `io_timeout`, callbacks and other pointers. Iterator `Next()`/`Prev()` are still not traced.
- The generated C bindings were added by hand, mirroring `preserve_write_order` (no clang++ on the devserver to run `regen_all.py`); CI's generated-code check confirms them.

Reviewed By: xingbowang

Differential Revision: D126954597

fbshipit-source-id: 50e128fcde6cb9de6e5cf7dd1e8d71bc33c86134
2026-10-23 16:01:05 -07:00
Anand Ananthabhotla e4cacf667f Fix false MANIFEST-growth failures in db_stress reopen verification (#15470)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15470

**Why the change is required:** `rocksdb-contrun-fbcode_crash_test_with_tiered_storage` (Sandcastle workflow 3256102530611462228) failed with `FATAL: MANIFEST file grew by 146 bytes after reopen despite ALL conditions for complete avoidance being met`, followed by the known secondary `assert(property_info != nullptr)` in `DBImpl::DumpStats` from the stats timer racing `exit(1)`. The engine did not violate the `optimize_manifest_for_recovery` + `reuse_manifest_on_open` guarantee; the db_stress check measured the wrong thing.

**Previous behavior:** `StressTest::Reopen()` records the MANIFEST size, calls `Open()`, then runs `VerifyManifestNotRewritten()`. `DB::Open()` ends by scheduling flush/compaction, so any compaction that is already eligible at open (periodic compaction, compaction TTL, files marked for compaction, universal size amplification) can `LogAndApply` before the size check. In the CI failure, `DB::Open()` returned at 20:15:57.963622, a delete-triggered universal compaction on CF "3" was picked 29us later and installed around 57.988, and decoding the MANIFEST bytes appended after the pre-reopen size with `ldb manifest_dump` shows exactly that one compaction edit (delete 0/350, 0/377, 5/340; add 5/385). Recovery itself wrote nothing. This race has existed since the check was added (5baa65680fdd). Separately, STRICT mode did not account for `enforce_write_buffer_manager_during_recovery`: with a WriteBufferManager limit (here `db_write_buffer_size=1048576` across 10 CFs), WAL replay can legitimately force recovery flushes even with `avoid_flush_during_recovery=1`, which STRICT mode also reported as a fatal engine bug.

**New behavior:** MANIFEST verification on reopen only measures writes made by `DB::Open()` itself. STRICT (fatal) mode is used only when no recovery flush is possible; when a WriteBufferManager limit is enforced during recovery, the check runs in NO_WRITE mode and prints a warning instead of exiting.

**Implementation:**
- Fix: when a reopen will be verified and auto compactions are not already disabled, `Open()` opens with `disable_auto_compactions=true`; `Reopen()` re-enables them with `EnableAutoCompaction()` and restores `options_` right after `VerifyManifestNotRewritten()`. This follows the existing `preserve_unverified_changes` pattern.
- Fix: `RecordManifestStateBeforeReopen()` treats `enforce_write_buffer_manager_during_recovery` with `db_write_buffer_size > 0` or a configured `write_buffer_manager` as "MANIFEST writes possible", and the STRICT-mode comment lists the new condition.

**Safety and limitations:** db_stress-only change; no engine, API, or format change. Compactions are held off only between the start of a verified reopen and the check, then resume immediately; when verification is off or auto compactions are already disabled, nothing changes. The `exit(1)`-time shutdown race that turns the FATAL into a `DumpStats` assert or hang is pre-existing and not addressed here.

Reviewed By: xingbowang

Differential Revision: D127204400

fbshipit-source-id: 0542597b8b6164945c70b3b8d8ddc3e0ee3206a0
2026-10-23 12:40:15 -07:00
Anand Ananthabhotla 58d929bc85 Update RocksDB main for 11.13 release (#15471)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15471

Why: keep internal RocksDB main ready for 11.14.0 development while the 11.13 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.13.0 to 11.14.0
- Add `11.13.fb` to `check_format_compatible.sh`
- Sync HISTORY.md `## 11.13.` section from `11.13.fb`
- Delete 10 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: omkarhgawde

Differential Revision: D126887226

fbshipit-source-id: 60e5f13913912a5582199183ca29e0150e431665
2026-10-23 11:52:54 -07:00
Xingbo Wang e38f68d6ce Shard the snapshot list so snapshots avoid the DB mutex (#15402)
Summary:
Move primary/read-only snapshot creation and all snapshot release operations
from the DB mutex to a bounded set of snapshot-list shard locks. Keep exact
DB-wide snapshot maintenance on existing DB-mutex-protected paths, and retain
the global column-family threshold optimization so most maintenance scans do
not walk column families. Maintenance aggregates shard-local release counts so
ordinary writes on an empty snapshot list cannot masquerade as snapshot
release progress and spuriously schedule compaction.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15402

Motivation and previous behavior:

`DB::GetSnapshot()` and `DB::ReleaseSnapshot()` previously serialized on the
DB mutex. Applications that use an explicit snapshot as a consistency token
across several reads therefore contended with flush, compaction, MANIFEST, and
other DB-wide work. This contention materially increased CPU cost and reduced
throughput at high concurrency.

New behavior and implementation:

- `ShardedSnapshotList` owns 16 cache-aligned shards. Snapshot creation selects
  a shard from the current CPU (or a per-thread relaxed round-robin fallback),
  and each snapshot records its shard so release remains correct after thread
  or coroutine migration.
- Primary and read-only snapshot creation uses one shard lock. Secondary and
  follower creation retains the DB mutex because catch-up can publish a new
  sequence before installing its matching SuperVersion; their clock read and
  allocation occur before that lock, while sequence/view capture and shard
  insertion occur under it. External-file ingestion can also gate creation and
  retry under the DB mutex to preserve its existing serialization contract.
  Atomic multi-CF ingestion checks gated shards one at a time and defers its
  exact scan until its arbitrary-size per-CF lock set has been released.
- Release removes the snapshot and increments a shard-local release count under
  its existing shard lock. It does not acquire the DB mutex, inspect column
  families, schedule work, perform I/O, or add an atomic read-modify-write.
- Existing DB-mutex-protected paths lock all shards in index order when they
  need an exact oldest snapshot and aggregate the per-shard release counts.
  They advance snapshot-dependent compaction state only after an actual
  release, so ordinary writes on an empty snapshot list do not spuriously
  schedule cleanup. If the oldest sequence then crosses a DB-wide compaction
  threshold, they reconcile affected column families and schedule newly
  eligible work. Triggers include SuperVersion installation, flush/compaction
  snapshot loading, WritePrepared snapshot loading, the trigger-compaction
  periodic task, and `WaitForCompact()`.
- `GetAll()` copies each sorted shard independently and performs a bounded
  S-way merge, preserving ordering, deduplication, maximum-sequence filtering,
  and write-conflict-boundary semantics. Statistics-only queries do not trigger
  maintenance.
- Timestamped snapshot insertion remains O(1). Equal-sequence ordering follows
  list insertion order; `GetOldestSnapshotTime()` returns the minimum shard-head
  Unix time and is documented as approximate when wall-clock time moves
  backward or snapshots are concurrently created.
- Secondary stress verification now reads each column family through a managed
  secondary snapshot after catch-up.

Alternatives evaluated:

- A reader/writer lock does not help because both creation and release mutate
  the list. A lock-free list would additionally require ordered insertion,
  lifetime management, and safe reclamation.
- Running maintenance from release would reintroduce the DB mutex on the read
  path. Running it only from flush would delay cleanup when a DB is otherwise
  idle. Reusing existing exact DB-mutex-protected scans keeps release cheap and
  provides periodic eventual progress.
- Moving `GetAll()` merging outside the DB mutex would require callers to pin
  and revalidate surrounding flush, compaction, and transaction state. It is
  deferred without evidence that the bounded merge is a material critical-
  section cost.
- Shard-count measurements did not justify a public option. At 64 workers, 32
  shards were 10.5% slower than 16 in the existing snapshot-backed
  `randomwithverify` workload. A 64-shard setting cannot be supported because
  maintenance holds the DB mutex plus all shard locks, exceeding GCC TSAN's
  fixed 64-lock set. The count is therefore an internal constant of 16.

Safety boundaries and limitations:

- This changes neither file format nor compaction-picking policy. Shard locks
  are leaf locks under the DB mutex, and multi-shard operations always acquire
  them in index order.
- Atomic multi-CF ingestion never adds all 16 shard locks to its arbitrary-size
  per-CF lock set. Its installed Versions first use the last exact oldest
  sequence, which is conservative, and one exact scan runs after those locks
  are released.
- Read-only, secondary, and follower instances do not run release-driven
  compaction maintenance because they cannot schedule that work.
- Per-shard release counts are updated under the same shard lock as removal.
  Exact readers aggregate them while already holding every shard, avoiding a
  shared counter on the release hot path.
- Release-driven compaction eligibility is eventually observed by the next
  exact maintenance trigger. With default statistics scheduling this is
  normally within 600 seconds; if both statistics tasks are disabled and no
  column family has a shorter time-based trigger, trigger-compaction provides
  a 12-hour fallback.
- `GetAll()` remains O(N log 16) under its caller's DB mutex. Snapshot-time
  statistics are approximate under wall-clock rollback.
- Local benchmarks are closed-loop and do not model remote latency. The
  existing `randomwithverify` benchmark performs three reads under each
  explicit snapshot; it has no directly comparable no-snapshot mode, so no
  no-snapshot ceiling is claimed.

Performance evidence:

Release-build, CPU-pinned, order-balanced trials compared an unsharded parent
with this change. The existing `randomwithverify` workload takes one explicit
snapshot per read operation and performs three point Gets under it; writes and
deletes atomically update the corresponding three keys. The fully cached
workload used 98% reads, 1% overwrites, 1% deletes, 100,000 distinct keys,
256-byte values, a 1 GiB write buffer and block cache, disabled compression and
automatic compaction, and fixed seed 8675309. Each variant and trial started
from a fresh database, warmed up for 200,000 operations per worker, and then
ran two million measured operations per worker. Five paired trials produced
these median throughputs:

| Workers | Parent | Change | Improvement |
| ---: | ---: | ---: | ---: |
| 1 | 0.132 Mops/s | 0.133 Mops/s | +1.1% |
| 2 | 0.254 Mops/s | 0.265 Mops/s | +4.3% |
| 4 | 0.492 Mops/s | 0.592 Mops/s | +20.2% |
| 16 | 0.586 Mops/s | 2.956 Mops/s | +404.2% |

The throughput medians are non-regressing at every worker count; 17 of 20
individual pairings improved, with three low-thread losses inside the observed
run-to-run spread.

A separate three-pair CPU/contention experiment used the same configuration
and fresh-DB setup at 1, 2, 4, 16, and 64 workers, with two million measured
operations per worker. All counters cover the measured process invocation,
including DB open/close; prefill and warmup are separate. One operation is a
benchmark operation, not one individual point Get. The following are medians
per variant, with aggregate CPU time from `task-clock`:

| Workers | Parent/Change Mops/s | Parent/Change cycles/op | Cycle change | Parent/Change CPU us/op | Parent/Change context switches/Mop |
| ---: | ---: | ---: | ---: | ---: | ---: |
| 1 | 0.129 / 0.130 | 22,647 / 22,425 | -1.0% | 7.78 / 7.69 | 551.0 / 561.0 |
| 2 | 0.246 / 0.266 | 22,566 / 21,760 | -3.6% | 8.02 / 7.45 | 4,530.5 / 328.5 |
| 4 | 0.383 / 0.594 | 23,495 / 19,242 | -18.1% | 9.77 / 6.56 | 29,709.3 / 134.4 |
| 16 | 0.629 / 2.924 | 33,924 / 15,088 | -55.5% | 22.84 / 5.14 | 190,879.1 / 74.8 |
| 64 | 0.635 / 5.582 | 240,076 / 22,858 | -90.5% | 93.66 / 10.53 | 200,175.0 / 46,537.8 |

At 16 and 64 workers, throughput improved 364.6% and 779.0%, while cycles/op
fell 55.5% and 90.5%. Aggregate CPU time/op fell 77.5% and 88.8%, and context
switches/op fell 99.96% and 76.75%. CPU time/op excludes blocked time; these
results show better CPU efficiency, not merely higher CPU utilization.
Single-worker cycles/op was nearly flat (-1.0%); an independent earlier series
measured +4.1%, so a small single-worker regression cannot be ruled out.
The 64-worker change throughput ranged from 4.178 to 5.611 Mops/s across the
three trials; these closed-loop VM results are not a production guarantee.

Separate futex-instrumented runs used 250,000 operations per worker and three
order-balanced pairs. Counts cover all futexes in the benchmark process, not
only snapshot mutexes. `FUTEX_WAIT` entry-to-exit duration includes sleep and
syscall time, including fast failed waits; it is not CPU time.

| Workers | Parent/Change FUTEX_WAIT calls/Mop | Parent/Change FUTEX_WAKE calls/Mop | Parent/Change summed WAIT us/op | WAIT duration reduction |
| ---: | ---: | ---: | ---: | ---: |
| 16 | 1,351,313.00 / 21.75 | 1,328,722.25 / 181.25 | 9.42994 / 0.00393 | 99.96% |
| 64 | 840,136.56 / 449,760.56 | 1,005,333.44 / 625,624.44 | 36.75790 / 0.73084 | 98.01% |

These measurements support less mutex blocking and scheduler overhead under
contention. At 64 workers, 16 shards still have residual contention: WAIT
calls/op fell 46.5%, but their summed duration fell 98.0%. User-cycle sampling
also showed residual pthread lock work; it cannot assign a precise fraction
of saved cycles to snapshot mutexes versus writes or allocation.
Generic cache misses/op fell 33.7% and 44.5% at 16 and 64 workers, respectively,
but cache misses are not a measurement of cache-line bouncing. This KVM guest
does not expose the memory-sampling PMU required by `perf c2c`, so cache-line
bouncing remains an explanation to test on suitable hardware, not a measured
claim. Context switches, futex duration, and cycles are not additive parts of
a CPU-cost breakdown.

The shard-count decision used the existing `randomwithverify` workload with
three reads per snapshot, fixed seed 8675309, one million operations per
worker, a 1 GiB write buffer, disabled compression and automatic compaction,
and three order-balanced 64-worker trials. The 16-shard trials measured 4.953,
5.049, and 4.634 Mops/s; the 32-shard trials measured 4.201, 4.433, and 4.540
Mops/s. The medians favor the fixed 16-shard count by 11.7% (or equivalently,
32 shards were 10.5% slower).

Validation coverage:

Focused tests cover shard selection and merging, timestamped ordering, exact
oldest-snapshot maintenance, global threshold gating,
SuperVersion-install release races, read-only/secondary behavior, external-file
ingestion gates, and concurrent snapshot use. The WritePrepared snapshot
matrix and format/lint checks also pass. Full diff CI supplies the broad build,
platform, sanitizer, and test-suite coverage.

Reviewed By: pdillinger

Differential Revision: D123507967

fbshipit-source-id: 2a067dbadc5382cf998cc4920d8aedd81136ac5f
2026-10-23 11:32:02 -07:00
cm-sally-mate 94c7f76da0 Keep read-path tombstone conversion from deleting ingested files an older iterator cannot see (#15461)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15461

**What:** With `min_tombstones_for_range_conversion` set, an iterator whose view predates an ingestion can delete the ingested keys for good.

**Why it matters:** Silent, permanent data loss. `Get` returns the ingested key right after ingestion. After one scan from the older iterator, `Get` returns NotFound, also after flush and reopen. Every MyRocks bulk load with the option set takes this path: MyRocks ingests with `allow_global_seqno=false` and `snapshot_consistency=false` (seqno 0).

**Root cause:**
- The older iterator does not see the ingested file. It sees the deleted keys on both sides of the file as one run, and inserts a range tombstone at its read sequence number. The tombstone covers the ingested keys.
- `ingest_seqno_barrier_` (D102044512) refuses a conversion only when the iterator's sequence number is below the ingested seqno. That misses two cases:
  1. The file gets no new seqno: no overlap (seqno 0), `ingest_behind`, or `allow_db_generated_files`. Ingestion does not move the barrier at all (`assigned > 0`).
  2. `NewIterator()` references the SuperVersion before it reads the sequence number. If an ingestion and a write happen in between, the iterator reads at a sequence number above the ingested file, with a SuperVersion that does not have it. This also loses files ingested with a new seqno.
- Ingestions that flush the memtable are safe: the older iterator's memtable becomes immutable.

**Fix:** Barrier on the Version, not only on the sequence number.
- After `LogAndApply()`, ingestion records the new Version number on the active memtable (`ingest_version_barrier_`), while it still holds the per-CF `ingest_sst_lock`.
- `DBIter` passes the number of the Version it reads. `AddLogicallyRedundantRangeTombstone()` refuses the conversion if that number is below the barrier.
- Version numbers only go up, and every later Version of the column family has the ingested files. So the check is true exactly when the iterator cannot see an ingestion into this memtable. Iterators created after the ingestion convert as before.
- The seqno barrier stays: it still covers old explicit snapshots that read a newer Version.

**Related:** D126796014 (`Disable tombstone conversion for kMemtableTier iterators`) fixes a different trigger of the same feature.

Reviewed By: joshkang97, archang19

Differential Revision: D126875720

fbshipit-source-id: 5767b866c6174d791f9ddae78311816300ac49f3
2026-10-22 15:31:52 -07:00
Yoshinori Matsunobu d6a7847d37 Add a DB option to log long-held global mutexes and release sites (#15436)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15436

## Why
Long-held global mutexes can stall foreground and background work. Existing mutex statistics measure acquisition waits, not continuous ownership. This adds opt-in logging to identify the mutex, owning thread, operation and release site.

## Enable for an investigation
Provide an INFO logger and shared `Statistics` object when opening the DB, then:
- Set the shared statistics level to `StatsLevel::kAll`.
- Set `mutex_hold_log_threshold_us` through `DB::SetDBOptions`; `100000` means 100 ms.
- Restore threshold 0 and the previous statistics level afterward.

The threshold defaults to **0 (disabled)**. `kAll` alone does not enable these hold probes; threshold 0 leaves existing statistics timers unchanged.

## Output
Illustrative records, not production observations:
```
SLOW_GLOBAL_MUTEX mutex=DBImpl::mutex_ mutex_addr=0x12340000 owner_thread_id=12345 thread_operation=Compaction hold_us=200000 threshold_us=100000 observed_end_us=1792205400000000 buffer_dropped_records=0 unlock=db_impl_compaction_flush.cc:4660 BackgroundCallCompaction()
SLOW_GLOBAL_MUTEX mutex=DBImpl::wal_write_mutex_ mutex_addr=0x12340100 owner_thread_id=12346 thread_operation= hold_us=35000 threshold_us=25000 observed_end_us=1792205401000000 buffer_dropped_records=0 scope_exit=db_impl_write.cc:2250 PreprocessWrite()
```
The first proves a completed 200 ms DB-mutex ownership interval on a thread tagged `Compaction`. The second uses a 25 ms threshold; an empty operation tag means none was available. Neither record reports acquisition wait or proves the cause of a stall.

Release attribution uses compiler-embedded strings available in optimized, stripped builds, with a GCC/Clang fallback where standard source locations are unavailable. Direct unlock and condition wait identify their call sites; scope exit and temporary unlock identify guard declarations, not closing braces. No stack unwinding or symbol lookup.

## Scope and safety
- Track six selected mutexes: DB, WAL, point-lock wait graph, two prepare-log bookkeeping mutexes and transaction name map. This is not comprehensive synchronization tracing.
- Acquisition waits and condition-variable release gaps are excluded from hold duration. Configuration is pre-first-use; repeated setup retains the first valid context.
- Sample at most once per mutex per second, with eight deferred records per thread. `buffer_dropped_records` counts capacity overflow only; rate limiting, pause and emission contention can omit samples.
- Emit after covered outer lock scopes release. Emission is nonblocking best-effort; shutdown drains/pauses emission and stops diagnostics before the owned logger closes. Closing from that logger's own callback is unsupported.
- No data-file format change. The mutable option is persisted in OPTIONS and wired into bench/stress tools. Typed C binding is deferred; string DB-option APIs are available.

Disabled bookkeeping still has a cost. Release A/B measurements found about 3 ns added per selected mutex pair at level 1, and about 70 ns for hold timing over `kAll`/0. Get and reused-iterator Seek showed no resolved small probe effect; fresh iterator+Seek at 16 threads showed +8.7% CPU/query and -9.4% QPS. No logs were emitted in those benchmarks; logging cost and broader workload qualification remain open.

Reviewed By: xingbowang

Differential Revision: D125790955

fbshipit-source-id: 1096163f3bd25ffee585dc6ec8ebfd7f1759851a
2026-10-22 09:50:20 -07:00
Andrew Chang 431ce83b9b Disable tombstone conversion for kMemtableTier iterators (#15451)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15451

Fix a data loss bug in read-path range tombstone conversion.

Why the change is required:
With `min_tombstones_for_range_conversion` > 0, a DBIter that scans a run of contiguous point tombstones inserts a range tombstone `[first_tombstone_key, next_live_key)` at its read sequence number into the active memtable. This is only logically redundant if the iterator observed every live key inside that range. An iterator created with `ReadOptions::read_tier = kMemtableTier` reads only the mutable and immutable memtables (`DBImpl::NewInternalIterator` adds no SST iterators for that tier), but DBIter still enabled conversion for it: the gates were the active memtable, `table_filter`, prefix mode, and timestamp visibility, not the read tier.

Previous behavior:
A live key that exists only in SST files and sorts between memtable tombstones is invisible to a memtable-only scan, so the scan treats the tombstones around it as contiguous and inserts a range tombstone covering it. The key is then deleted for all reads at or above the iterator's sequence number, and the deletion persists through flush and compaction. Example with `min_tombstones_for_range_conversion = 2`: `Put(c)`, `Flush()`, `Put(a)`, `Delete(b)`, `Delete(d)`, `Put(e)`; a `kMemtableTier` scan returns `a, e` and inserts `[b, e)`; afterwards `Get(c)` returns NotFound. Both forward and reverse scans trigger it.

Affected: column families with `min_tombstones_for_range_conversion` > 0 (EXPERIMENTAL, default 0) that create iterators with `read_tier = kMemtableTier`. Not affected: `kBlockCacheTier` (the scan stops with `Incomplete` before a run can be flushed, already covered by `NoInsertionOnBlockCacheTierIncomplete`), `kPersistedTier` (not supported for iterators), and all other iterators.

New behavior:
Conversion runs only for iterators with `read_tier = kReadAllTier`, and stays disabled for the iterator lifetime for every other tier. This also turns it off for `kBlockCacheTier` scans that are served fully from cache, which could convert before. The option documentation now lists reads with `read_tier` other than `kReadAllTier` among the configurations where conversion is skipped.

Implementation:
One more condition, `read_tier == kReadAllTier`, in the DBIter constructor's `min_tombstones_for_range_conversion_` gate, next to the existing `table_filter` and timestamp-visibility checks. Release note added.

Safety, compatibility, and limitations:
No API, format, or default changes. The change only drops a logically redundant optimization for read tiers that cannot guarantee a complete scan. A new test checks that a range tombstone inserted by a full-tier scan does not change what later `kMemtableTier` and `kBlockCacheTier` reads return. The only difference it found: with a cold block cache, a `kBlockCacheTier` `Get` of a covered key returns NotFound (the full-tier answer) instead of `Incomplete`.

Reviewed By: joshkang97, jaykorean

Differential Revision: D126796014

fbshipit-source-id: 70f5184dbf5bb9eff765c232ac130ab43379a814
2026-10-22 09:40:43 -07:00
Peter Dillinger 539532377d Remove Solaris support (#15449)
Summary:
Solaris support was contributed by outside users starting with d78c6b28c (2016), and no project CI has ever run on Solaris. The last Solaris-motivated change was c76688745 (https://github.com/facebook/rocksdb/issues/4012, June 2018). Four months later, d6ec28870 (https://github.com/facebook/rocksdb/issues/4226, 5.18) gave PerfContext a user-declared destructor while the Solaris-only definition was still `__thread PerfContext perf_context_`, which GCC rejects, so the Solaris build has not compiled since. fd00f39f9 (https://github.com/facebook/rocksdb/issues/8117, 2021) then renamed that definition to `perf_context` while keeping
`#define perf_context (*get_perf_context())` in perf_context_imp.h, so the definition expands to `thread_local PerfContext (*get_perf_context());`, an error still present today. The Solaris-only paths are dead and untestable, yet still attract new carve-outs.

Because Solaris support is apparently unused and complicating things, this change removes all OS_SOLARIS / SunOS handling from the build (Make, CMake, build_detect_platform, INSTALL.md), the portability layer and Env (endianness, gethrtime, directio, stack trace), the perf_context macro and destructor carve-out, Solaris-only test workarounds and exclusions, and RocksJava's Environment.isSolaris() and Solaris JNI library naming.

Not Solaris-specific and kept: SPARC unaligned-access handling in util/coding.h, sparc64 in Makefile machine lists (Linux/OpenBSD), and sparcv9 in RocksJava Environment.is64Bit(). Third-party sources are untouched.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15449

Test Plan: On non-Solaris platforms every removed condition was either an OS_SOLARIS-only branch or a `!defined(OS_SOLARIS)` term that was always true, so the code compiled is unchanged and the CI matrix covers it. The Solaris breakage was confirmed by syntax-checking monitoring/perf_context.cc with -DOS_SOLARIS (GCC 11, stub sys/isa_defs.h) at each commit touching the perf_context files from c31979205 through fd00f39f9: clean before d6ec28870, failing at every commit from d6ec28870 on.

Reviewed By: archang19

Differential Revision: D126714985

Pulled By: pdillinger

fbshipit-source-id: 40d22fcd5a462375c9d2c93f52407df651286ef7
2026-10-22 08:25:11 -07:00
zaidoon 553b52b4a7 Wire IndexFactory through block-based tables (#14963)
Summary:
Build block-based table indexes through `IndexFactory`, with modes for writing both indexes or only the custom index and a per-read index selector. Built-in reads retain their existing cache and prefetch paths.

The review fixes preserve the existing UDI behavior at the migration and rollback boundaries:

- Reopening an SST with index caching enabled reuses the serialized custom block. Each table reader constructs its own factory reader over those bytes and passes `NewReader()` a `Slice` view stored in the table reader. That view stays valid for the reader's lifetime, so a legacy reader can advance it or keep a pointer to it without changing the cached block. The view and the raw block are destroyed after the custom reader.
- `IndexFactoryReader::ApproximateMemoryUsage()` keeps its documented meaning: the reader's own allocations, excluding the serialized block. `MemoryUsageIncludesIndexBlock()` defaults to false. A reader whose estimate includes the block opts in, and the wrapper subtracts the block size from that estimate.
- Copying factory options and clearing a legacy UDI flag now rolls back that flag's policy. Legacy-derived options keep the requested default and resolve their effective mode without changing the inputs. Explicit factory modes retain precedence through cloning, failed updates, reconstruction, and the C API, including an explicitly selected `kStandardDefault`.
- OK empty custom-index output still produces a zero-size meta block and reads through the full standard index. This covers point data and files containing only range tombstones. Custom-only mode rejects empty output because it has no standard fallback.
- Default primary reads retain the table factory's precedence over a legacy per-read factory pointer. Explicit `kPreferCustom` still checks a supplied factory name; `kBuiltin` ignores that pointer.
- OPTIONS files write equivalent legacy booleans for `kStandardDefault`, `kStandardRequired`, and `kCustomDefault`. Older releases retain those policies when loading newer files with `ignore_unknown_options=true`; the explicit-mode marker remains authoritative for current readers.

Custom-only SSTs carry a footer feature bit so older readers reject them. Reject unsupported filter/index combinations before constructing filters. Trie size estimates account for shared prefixes, and block offsets or sizes beyond the 32-bit format limit return `NotSupported` instead of truncating.

## Safety and compatibility

- Custom-only files require format version 6 or later and the factory. Rewrite every custom-only SST with a full standard index before removing the factory or downgrading. The other modes have no equivalent legacy configuration beyond the three listed above.
- A raw C++ options struct cannot distinguish an explicitly assigned `kStandardDefault` from its default value. Configure the factory to make that choice explicit. `GetEffectiveIndexMode()` reports the resolved policy while preserving legacy inputs for copy-and-clear rollback.
- A custom factory with user-defined timestamps is rejected in every mode except `kStandardOnly`, which ignores the factory.
- Trie construction rejects a data-block offset or size greater than `UINT32_MAX`, including in `kStandardDefault`. That can fail flush, compaction, or `SstFileWriter::Finish()`; total SST size above 4 GiB alone is not the rejection condition. File-size targets do not split L0 or `SstFileWriter` outputs.
- Custom-index cache hits, misses, and adds are reported under `BLOCK_CACHE_INDEX_*` instead of `BLOCK_CACHE_DATA_*`. The existing cache setting still controls ownership and cache use.
- A table reader now charges a custom index block it owns (`cache_index_and_filter_blocks=false`); before, that block was not charged anywhere. Cached blocks are still charged only to the block cache. A reader whose `ApproximateMemoryUsage()` includes the serialized block, such as IntegerTrie or TimeSeries, must override `MemoryUsageIncludesIndexBlock()` to return true, or that block is charged twice.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/14963

Test Plan:
For this commit, on macOS: `table_test`, `trie_index_test`, `trie_index_db_test`, and `block_based_table_reader_test` pass on the full stack (11,416 cases), and the full `table_test` passes at this PR's head alone (6,969 cases). The four custom-reader compatibility tests pass 100 times with `ASSERT_STATUS_CHECKED=1` and `COERCE_CONTEXT_SWITCH=1`, and 84 UDI table cases pass under ASAN with stack-use-after-return detection. Release-mode syntax checks with `-DNDEBUG -fno-rtti` on the changed sources, source checks, formatting checks, and `git diff --check` pass.

Two new regressions cover the latest review, and both were checked failing before the fix:

- `RetainedReaderSliceOutlivesTableOpen`: a reader keeps a pointer to the `Slice` it received, the stack is overwritten after open, and the reader then serves a seek and a full scan. It covers all three custom-index modes with owned and cached blocks, including a cached reopen. Without the fix, the retained `Slice` no longer matched the block.
- `AuxiliaryReaderMemoryIsFullyCharged`: a reader that implements only the older reader methods reports a fixed allocation of half and twice the raw block size. Table usage must grow by that amount with owned and cached blocks, and a strict cache sized just below the required reservation must reject the open with `MemoryLimit`. Without the fix, usage fell short by `min(allocation, raw block size)` and the strict-cache open succeeded.

The consuming-reader and strict-cache accounting tests are kept, now on shared helpers.

From the previous revision: 498 focused C++ cases passed (82 table/factory, 156 trie, and 260 trie DB), along with the C API test and generated C API verification. Coverage includes both comparators, copied-options rollback, C factory reconstruction, explicit modes, OPTIONS serialization, SST Get/iteration, point and range-only flushes, compaction, reopen, cache disabled/enabled/evicted, and legacy factory precedence. The rollback, empty-output, primary-selector, and OPTIONS regressions were checked failing before their fixes. The cache test measures file reads at the custom block's actual offset: one on a cold open, zero on cached reopens, and one after eviction or with caching disabled. Disabling custom-block cache reuse makes that assertion fail.

The full suite and Linux/Windows/TSAN coverage are left to CI. Earlier full-suite runs had failures/timeouts (173/2095 jobs normally and 152/2088 with Status checking); that broad suite was not rerun for this commit.

## Series

Previous: https://github.com/facebook/rocksdb/issues/14962 (landed through import). Next: https://github.com/facebook/rocksdb/issues/14965.

Reviewed By: jaykorean

Differential Revision: D122796629

Pulled By: xingbowang

fbshipit-source-id: 4c9d6f3f548c588a37b08fd94de25e8c0b5ef743
2026-10-21 20:20:38 -07:00
Xingbo Wang 81369fc170 Fix async secondary catch-up on deleted SSTs (#15438)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15438

Fix a RocksDB secondary with `open_files_async = true` latching on `PathNotFound` during `TryCatchUpWithPrimary()`.

**Why:** With `open_files_async = true` and `max_open_files = -1`, a secondary's catch-up could fail with `PathNotFound` on a table that was live at open time (most reachably in a non-default column family) but which the primary compacted away before the background async opener pinned it. The error latched in the tailer's status, so the secondary never advanced again and had to be closed and reopened. With `open_files_async = false` the same sequence caught up fine. This blocks enabling async secondary open where it would otherwise cut cold-open time substantially.

**Previous behavior:** In `VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit`, a negative-edge `LoadTableHandlers()` failure skipped the unrecoverable point-in-time only when the status was `Corruption`; a `PathNotFound` was returned as-is and latched. During a secondary's catch-up, `open_files_async` leaves recovery-time tables unpinned until the background opener reaches them, so `LoadTableHandlers()` could try to open a file the primary had already deleted.

**New behavior:** That negative-edge failure path now also treats `PathNotFound` as a missing file (skip this point-in-time and continue) when `no_error_if_files_missing_` is set, mirroring the tolerance `VersionEditHandler::LoadTables()` already applies. Catch-up continues past the transient state and installs the current version, so the secondary advances instead of latching.

**Implementation:**
- `db/version_edit_handler.cc`: broaden the negative-edge skip condition in `VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit` from `s.IsCorruption()` to also include `s.IsPathNotFound() && no_error_if_files_missing_`.
- `db/db_secondary_test.cc`: add `DBSecondaryTest.OpenFilesAsyncNonDefaultCfCatchUp`, a regression test that opens an async secondary with a non-default column family, holds the background opener back so the open-time table stays unpinned, has the primary flush and `CompactRange(kForce)` to replace and delete that table, then asserts `TryCatchUpWithPrimary()` succeeds and reads the new value.

**Safety boundaries:** The change only affects point-in-time handlers that opted into missing-file tolerance (`no_error_if_files_missing_`, set by secondary/follower tailing and best-efforts recovery); it does not relax error handling for strict recovery. It does not change the `trust_manifest_recovery_` / `defer_sst_file_opening_` paths, which already skip opening.

**Validation and limitations:** The regression test covers async secondary catch-up after deletion of an open-time SST in a non-default column family. Validation covers async opening, catch-up, memtable retention, and repeated scheduling changes. Strict recovery, API and file formats are unchanged. The full supported platform matrix has not been run locally.

Reviewed By: jaykorean

Differential Revision: D125378257

fbshipit-source-id: 3dd23c2f763e4e744c9f38fb77dfdfb3f554ee7f
2026-10-21 17:51:53 -07:00
Josh Kang babce11705 Add public internal key encoding utilities (#15396)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15396

## Summary

`ParseEntry()` exposes the components of an internal key, but constructing one with a chosen sequence number and entry type currently requires private RocksDB utilities or duplicating the encoding. Add public `EncodeEntry()` to construct a stored internal key from `ParsedEntryInfo`, including its user key, timestamp, sequence number, and entry type. Also expose `PackSequenceAndType()` and `UnPackSequenceAndType()` to work with the sequence/type footer as a `uint64_t`.

The helpers map all nine representable public entry types to their internal types and use the existing footer encoding; `EncodeEntry()` reuses the public packer. Packing `kEntryOther` or a sequence number above the 56-bit limit returns `InvalidArgument` without changing the output. Unpacking reports internal value types without a public equivalent as `kEntryOther`. Timestamp size remains the caller's responsibility, and input slices must not alias the output string. These are additive C++ utilities using the existing internal-key format.

Reviewed By: anand1976, xingbowang

Differential Revision: D125082298

fbshipit-source-id: 6c09a31695f1d377b9cc208fa5b6edd3ad6638e2
2026-10-21 13:08:03 -07:00
Andrew Chang ee305ac1c3 Avoid a TLS init call on every perf counter update (#15428)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15428

RocksDB counts per-operation statistics (blocks read, bytes read, mutex waits,
timings) in `perf_context`. It is a `thread_local` variable: each thread has its
own copy, so threads never contend on it. This per-thread copy is "thread-local
storage" (TLS).

**Problem**: `PerfContext` has a non-trivial destructor. For such a variable, C++
must make sure the calling thread's copy is constructed before each use from
another source file, so the compiler emits a call to a small "TLS init"
function. That call goes through the PLT (procedure linkage table, the jump
table used to call functions that may live in another shared library). Every
`PERF_COUNTER_ADD` therefore paid a real function call before incrementing a
counter, and these macros run in hot loops such as key comparisons during block
iteration. `perf_level`, the on/off switch, has a smaller cost: it is
`extern thread_local` without `constinit`, so each read also tests whether it
has been initialized.

**Fix**:
- The perf macros read a `constinit thread_local PerfContext*` that each
  thread sets on first use. After that, a counter update is a pointer load,
  a null check and the increment.
- `perf_level` is declared and defined `constinit`, so reading it is a plain
  thread-local load.
- No public API change. `perf_context`, `get_perf_context()` and macro
  behavior are unchanged, and so is thread-exit behavior.

**Results**:
- Microbenchmark (`PERF_COUNTER_ADD` in a loop): 2.10 -> 0.84 ns per update
  with counting enabled; unchanged at 0.42 ns with counting disabled.
- Calls to the TLS init functions drop from 40 and 96 to 0 in block.cc.o
  and memtable.cc.o.
- In CPU profiles of large production RocksDB deployments, these accessors
  cost roughly 0.3-0.6% of total CPU. This change removes most of the
  `perf_context` part.

Reviewed By: xingbowang

Differential Revision: D125982209

fbshipit-source-id: f38b4fffda09611d28322e7534b135fb35631389
2026-10-21 11:47:50 -07:00
Anand Ananthabhotla 074134d98f Stop writes on retryable background IO errors when auto resume is disabled (#15444)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15444

**Why the change is required**

`max_bgerror_resume_count <= 0` turns off automatic `DB::Resume()`, so the application is responsible for recovering from background errors. Before automatic recovery was introduced, a background error under `paranoid_checks` latched `bg_error_` and made the DB read-only. Since then, two classes of retryable IO error are either never latched or latched as soft errors that leave the DB accepting writes. When auto resume is disabled, no one recovers from them and the application gets no clear signal.

**Previous behavior** (with `max_bgerror_resume_count <= 0`)

- A retryable or file-scoped IO error during compaction was never set in `bg_error_`. Compaction rescheduled itself and retried indefinitely. A persistent failure was visible only through `EventListener::OnBackgroundError` and the LOG, while compaction debt grew until writes slowed or stopped on L0/pending-compaction triggers.
- A retryable or file-scoped IO error while writing a flush SST or the MANIFEST after a successful WAL sync (`kFlushNoWAL` / `kManifestWriteNoWAL`, which covers almost every retryable flush failure, not only `disableWAL` writes) was a soft error with `soft_error_no_bg_work_` set. Background work stopped, but writes kept succeeding into the memtable. Nothing retried the flush, so writers eventually blocked indefinitely on the memtable limit instead of failing.

**New behavior**

With `max_bgerror_resume_count <= 0`, every retryable or file-scoped background IO error that reaches the retryable path in `ErrorHandler::SetBGErrorImpl` is latched as `kHardError`, whatever the reason (compaction, flush, MANIFEST, WAL). Writes fail immediately and reads continue to work. `DB::Resume()` clears the error, flushes and reschedules compaction as before.

**Implementation**

In `SetBGErrorImpl`, the compaction special case (notify the listener and return without setting `bg_error_`) now applies only when auto resume is enabled, and the severity selection maps everything to `kHardError` when auto resume is disabled. Because `kErrorRecoveryRetryFlush` is no longer selected in that mode, `soft_error_no_bg_work_` is not set. The option documentation in `include/rocksdb/options.h` and the comment above `SetBGError` are updated.

**Safety, compatibility, and limitations**

- This is an intentional behavior change, limited to DBs that set `max_bgerror_resume_count <= 0`. With the default (`INT_MAX`) or any positive value, nothing changes.
- fbcode callers that set the option to 0, and their apparent expectations:
  - `unicorn/live_updates/dispatcher/MessageStore.cpp`: the comment says "we do not allow any [background errors] as they should be retried by the underlying FS". Stopping on retryable compaction errors fits that intent better than silent retries.
  - `reliable_volumes/client/index_manager/RocksDBIndexStorage.cpp`: a JustKnob (`disableRocksDbBackgroundErrorAutoResume`) disables auto resume alongside other corruption-detection options, which suggests it wants background errors surfaced rather than masked.
  - `zippydb/server/store` (`RocksDBStoreResumeWritesTest`): uses 0 with `disableWAL` and drives recovery through its own `ResumeWrites` task calling `DB::Resume()`, so it already expects writes to stop until resume. I read the test and expect it to pass, but did not run it. No production zippydb configuration setting 0 was found.
- Out-of-space errors (`kNoSpace`/`kSpaceLimit`) are untouched: they skip this path and are still recovered through `SstFileManager`, which does not consult `max_bgerror_resume_count`. Errors ignored under `paranoid_checks=false` are also unchanged.
- No API, ABI or on-disk format change.
- Found during testing, pre-existing and not fixed here: `SetBGErrorImpl` releases the DB mutex in `NotifyOnBackgroundError` before calling `StartRecoverFromRetryableBGIOError`. If the last failed auto-resume attempt's flush error is reported in that window, the recovery thread can give up and clear `recovery_in_prog_` first, and the late report then starts a new recovery with a fresh retry budget, so `max_bgerror_resume_count` can be exceeded. This only applies with auto resume enabled. Worth a separate fix.

Reviewed By: joshkang97

Differential Revision: D126453053

fbshipit-source-id: 3f4be1d12a8d44552313b022db5afe200bc1c6ce
2026-10-20 20:21:11 -07:00
Xingbo Wang 7454655969 Small fix and stress coverage for MANIFEST-bound OPTIONS recovery (#15431)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15431

A filesystem failure during column family creation can leave the MANIFEST and OPTIONS snapshots at different points in the column family lifecycle. Recovery must select the snapshot consistent with the MANIFEST without deleting a newer valid legacy snapshot.

Fix: When the MANIFEST committed an older tracked OPTIONS file, recovery previously rejected the highest-numbered candidate without reading its tracking marker. Writable open then deleted a newer legacy file, or an unreadable highest file, as a rejected tracked orphan; read-only open also reported the older file number. Recovery now inspects every higher candidate. A newer legacy snapshot supersedes the older committed snapshot, while an unreadable higher candidate is retained rather than deleted.

Add: Crash-test invocations randomize `track_options_file_number_in_manifest`. Before writable open can replace evidence, db_stress compares the public latest-OPTIONS choice with read-only recovery, using only currently live column family descriptors and running before requested missing families are appended. After open, it verifies the persisted tracking marker and live column family set. TransactionDB still skips the unsupported read-only probe, but it now reads the MANIFEST first so an existing commit enables the post-open verification. The regression test also checks that writable open preserves the newer legacy file before creating another column family.

Change: Reopen MANIFEST-avoidance checks use the loaded `options_` value directly. This is a behavior-preserving simplification, not a separate correctness fix.

Safety and limitations: There is no API, file-format, or production hot-path change. Ordinary writable open still receives the caller options and complete requested column family set. The extra read-only open is stress-only and runs when tracking verification is active. The pre-open comparison remains skipped for best-efforts recovery, TransactionDB, and stacked BlobDB; post-open verification remains skipped for best-efforts recovery, TTL, and stacked BlobDB. Random crash coverage is probabilistic, and this parent diff does not add randomized column family operations; the dependent lifecycle diff adds that profile.

Reviewed By: pdillinger

Differential Revision: D125029493

fbshipit-source-id: 5e291deff735e3cbe6d7df0513f940e84b20f387
2026-10-20 18:28:13 -07:00
Jay Huh a7f6f33ccc Keep OPTIONS file alive while GetLiveFilesStorageInfo reads its size (#15430)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15430

A db_stress blackbox crash test failed in verification with:
`GetLiveFilesStorageInfo failed: IO error: No such file or directory: while stat a file for size: .../OPTIONS-857089`

Since D123104845, `GetLiveFilesStorageInfoImpl` picks the OPTIONS file number while holding the DB mutex, releases the mutex, and only then calls `GetFileSize` on that file. If a `SetOptions()` runs in that window, it writes a new OPTIONS file, and `MaybeDeleteObsoleteOptionsFiles()` keeps only the newest two plus the MANIFEST-committed one. So the file that was just picked can be deleted before it is stat'ed. Checkpoint calls `DisableFileDeletions()` first and is not affected, but a plain `GetLiveFilesStorageInfo()` call, as db_stress makes, gets a spurious IO error.

The fix increments `disable_delete_obsolete_files_` (`DisableFileDeletionsWithLock()`) before releasing the mutex, and decrements it again under the mutex right after the size read. `MaybeDeleteObsoleteOptionsFiles()` already skips cleanup while the counter is non-zero. Any OPTIONS cleanup skipped during that window happens on the next OPTIONS write.

The decrement is done directly rather than through `EnableFileDeletions()`. That method is virtual, and `DBImplReadOnly` and `CompactedDBImpl` override it to return `NotSupported` without decrementing. Read-only DBs do reach this code (their OPTIONS file number is set at open), so going through `EnableFileDeletions()` left the counter stuck at 1 after every call. Calling the non-virtual `DBImpl::EnableFileDeletions()` instead is not safe either: when the counter reaches 0 it runs a forced `FindObsoleteFiles` scan and `PurgeObsoleteFiles`, which a read-only DB must never do. Secondary DBs never take this path, because `ReactiveVersionSet` does not record an OPTIONS file number, so `options_number` is 0 there.

Cost: no extra I/O or scan. Checkpoint already disables deletions before calling, so for it this only bumps the counter. For a caller that has not, such as db_stress, obsolete files that become deletable during the short window are not purged at the decrement. They stay queued and are removed by the next `FindObsoleteFiles` (end of the next flush or compaction, or the periodic full scan). Reading the cached `VersionSet::options_file_size_` under the mutex, as before D123104845, would avoid the stat entirely. I kept the stat because D123104845 chose it deliberately. The cached size is the better option if it is always in sync with `options_file_number_`.

Reviewed By: xingbowang

Differential Revision: D126001916

fbshipit-source-id: 60f4f31c73e48b374f4015ed2847d45e706b85de
2026-10-19 18:44:50 -07:00
generatedunixname3846135475516776 61831d47be Rocksdb Crash Test failed: stderr: Failure: latest seqno changed from <N> to <N> with W (#15433)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/15433

Reviewed By: jaykorean, xingbowang

Differential Revision: D125887282

fbshipit-source-id: 611491c1ece2c6e3bc74f22545bd5d93467cf9b9
2026-10-19 12:56:16 -07:00
Peter Dillinger 8121c7e254 Expose kLatestSupportedFormatVersion in BlockBasedTableOptions (#15397)
Summary:
Before this change, the highest supported block-based table format_version was only available as the internal constant kLatestBbtFormatVersion in table/format.h. Applications that accept a format_version setting from users or configuration (especially for SetOptions()) had no public way to validate it against the linked RocksDB build before applying it.

This adds the public constant
BlockBasedTableOptions::kLatestSupportedFormatVersion (currently 8) and removes kLatestBbtFormatVersion, redirecting all internal and test uses to the new constant. Supported-version checks and option validation are unchanged: format_version above the constant is still rejected as InvalidArgument, and draft format_versions remain usable only under the TEST-only TEST_AllowUnsupportedFormatVersion() hook.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15397

Test Plan: Same value and same checks, so no new tests. Existing format_version validation coverage (e.g. TableTest.UnsupportedFormatVersionConfigTest) and the FormatLatest test instantiations now go through the public constant.

Reviewed By: rban1

Differential Revision: D125117976

Pulled By: pdillinger

fbshipit-source-id: 67e0c5b7eade4bf649957435aa0b28b187793f7c
2026-10-19 06:32:23 -07:00
Jay Huh 54732b0c8f Tolerate CacheLib startup INFO in crash tests (#15416)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15416

Why the change is required:
A crash test using `secondary_cache_uri=cachelibwrapper://...` constructs and resets CacheLib/Navy. Sandcastle suppresses CacheLib `XLOG(INFO)` by patching the fbcode XLOG header, but commit `831f00db6a47` enabled the exported xplat header unit. The build therefore consumed an unpatched XLOG header, normal CacheLib startup INFO reached stderr, and `db_crashtest` exited 2 before it could accept the intentional whitebox SIGTERM.

Previous behavior:
Any residual child stderr was fatal. `sanitize_known_stderr()` covered expected SIGTERM and io_uring diagnostics but not CacheLib construction/reset INFO, so an otherwise healthy secondary-cache whitebox iteration failed.

New behavior and implementation:
When `secondary_cache_uri` starts with `cachelibwrapper://`, the sanitizer moves only INFO-severity lines from an explicit CacheLib construction/reset source allowlist to stdout. Warnings, errors, fatal logs, unrelated INFO, and identical lines without the CacheLib URI remain fatal.

Safety and compatibility:
Default and non-CacheLib configurations are unchanged. No API, ABI, persisted format, database behavior, or performance-sensitive engine path changes.

___

landed-with-radar-review

Differential Revision: D123695714

fbshipit-source-id: 844ecd2cbf1b3c1415629d787748b5edeb63d456
2026-10-16 19:40:01 -07:00
Anand Ananthabhotla a3b0f187ff Add FileSystem::IsPreallocationSupported() to skip WAL truncation on open (#15395)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15395

**Why the change is required:**
During `DB::Open`, RocksDB reopens the last WAL file and truncates it to its logical size. This is done even when every column family's log number has already advanced past that WAL, so the file is obsolete and will be deleted later in the same open. The only purpose of the truncation is to release space preallocated beyond the logical end of file (e.g. by `fallocate` with `FALLOC_FL_KEEP_SIZE`); `GetFileSize()` reports the apparent size, so truncating to it can only free preallocated blocks. On a FileSystem that never preallocates, this work is pure overhead: a `GetFileSize`, a `ReopenWritableFile`, a no-op `Truncate` (the default `FSWritableFile::Truncate` returns OK without doing anything), and a `Close` on every open. On remote or append-oriented FileSystems the reopen can be expensive.

**Previous behavior:**
- If recovery flushed all data, or the WALs contained no data (including when all WALs are older than the min log number to keep), `MaybeFlushFinalMemtableOrRestoreActiveLogFiles()` always called `GetLogSizeAndMaybeTruncate()` on the last WAL. In read-only mode it still issued a `GetFileSize()` whose result was discarded.
- With `avoid_flush_during_recovery`, `RestoreAliveLogFiles()` always reopened and truncated the last live WAL.

**New behavior:**
A FileSystem can report that it never preallocates space by returning false from the new `FileSystem::IsPreallocationSupported()`. When it does, DB open skips reopening and truncating the WAL. In the flushed / no-data path it also skips the unused `GetFileSize()`, so no I/O is issued against the old WAL. The read-only no-data path no longer issues the discarded `GetFileSize()` either.

**Implementation:**
- `include/rocksdb/file_system.h`: new `virtual bool IsPreallocationSupported()`, defaulting to `true`. `FileSystemWrapper` forwards it to the target.
- The policy lives in the two callers in `db_impl_open.cc`; `GetLogSizeAndMaybeTruncate()` is unchanged. `RestoreAliveLogFiles()` still calls `GetFileSize()` because the size is needed for `alive_wal_files_` and `wals_total_size_`.

**Safety, compatibility, and limitations:**
- The default is `true`, so every existing FileSystem, including ones that override `SupportedOps()` without calling the base class, keeps the current behavior. This is why it is a separate virtual function instead of an opt-in `FSSupportedOps` bit, which would silently disable truncation for those FileSystems.
- No built-in FileSystem overrides the default: Posix, Windows, `MockFileSystem`, and `LegacyFileSystemWrapper` all report `true`, so their WAL truncation behavior is unchanged. Posix deliberately reports `true` even in builds without `ROCKSDB_FALLOCATE_PRESENT`: WALs may have been preallocated by a different build running on the same machine, and a build-dependent answer would leave that space unreclaimed.
- The function describes what the FileSystem can do, not per-file options such as `allow_fallocate` or build configuration. A WAL may have been preallocated by an earlier process with different options, so gating on the current options could leak space.
- A FileSystem that wrongly returns false can only leak preallocated space, in archived WALs (`WAL_ttl_seconds` / `WAL_size_limit_MB`) or across a crash loop before obsolete WALs are deleted. Data is unaffected.

Reviewed By: joshkang97

Differential Revision: D125049741

fbshipit-source-id: ac9795f2045c46911cd86b8faacef5aa49043496
2026-10-16 12:15:57 -07:00
Ranjan Banerjee a215c38a8e Fix libaio download after Folly update (#15404)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15404

The RocksDB Folly checkout rewrites a dead libaio source URL to Debian's mirror. The pinned Folly revision now uses releases.pagure.org, so the rewrite still matched the older pagure.io archive URL and became a no-op. Public Folly builds then downloaded an empty file and failed SHA-256 verification.

Update the rewrite to match Folly's current libaio URL. The Debian archive has the same libaio 0.3.113 contents and SHA-256 already recorded in the Folly manifest, so the checksum and extraction directory remain unchanged. This only affects the public Folly checkout path; RocksDB runtime behavior and system-package users are unaffected.

___

landed-with-radar-review

Differential Revision: D125347462

fbshipit-source-id: 4203b6a0b6037efb23aa1f44306b0ca0cd73a43c
2026-10-14 18:25:57 -07:00
Anand Ananthabhotla df90749560 Wire compaction to retry TableBuilder file cuts (#15344)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15344

Add the internal TableBuilder::TryAdd() contract so RocksDB-owned table builders can distinguish accepted rows, real builder errors, and clean requests to cut the current output table and retry the same row. Add diagnostic reason codes for retryable table boundaries; existing internal table builders inherit a default TryAdd() implementation that forwards to Add().

Translate the public ExternalTableBuilderBase::TryAdd() result in ExternalTableBuilderAdapter so external table implementations can request a file cut while RocksDB compaction uses the internal TableBuilder contract.

Teach compaction output writing to honor kRequiresNewTable by closing the current output file, opening a new one through the existing callbacks, and retrying the same key without advancing the input iterator. Split OutputValidator validation into CanAdd() and AddValidated() so validator state is not updated until the key is actually accepted by a table builder.

Add a DB compaction test with a custom TableFactory that wraps block-based tables and requests one mid-compaction file cut. The test verifies the cut produces two readable output files and preserves all key/value results.

Reviewed By: jaykorean, xingbowang

Differential Revision: D123932406

fbshipit-source-id: 9ed82c3782787de192bca15e40bfbf014ef28c15
2026-10-14 15:59:16 -07:00
Josh Kang 014e1c43c1 Apply readability fixes (isolate decls, braces) (#15394)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15394

## Summary

RocksDB's `.clang-tidy` enables several readability checks that existing
code still violates: `readability-isolate-declaration` (476 findings),
`readability-braces-around-statements` (49),
`readability-redundant-string-init` (25), `modernize-use-using` (10),
`misc-definitions-in-headers` (3) and `readability-duplicate-include`
(1). They keep getting flagged whenever nearby lines change. This diff
fixes 553 of the 564; upstream has since fixed the other 11.

What changed:
- Multi-variable declarations such as `std::string a, b;` are split into
  one declaration per variable. Pointer and reference declarators are kept
  per variable.
- Braces are added around single-statement `if`/`else`/`for`/`while`
  bodies, and empty spin loops get an empty `{}` body.
- `std::string s = ""` / `member("")` initializers are removed.
- Remaining `typedef`s in the touched C++ files become `using`. C headers
  and `extern "C"` code are untouched.
- `inline` is added to non-template function definitions in test headers.
- A duplicate `#include "util/udt_util.h"` in `db_impl.cc` is removed.

These are clang-tidy's own fix-its, applied mechanically and then
clang-formatted on the changed lines. Three kinds of broken fix-it output
were repaired by hand. Removing a `member("")` initializer left a dangling
comma in constructor initializer lists (`ExternalSstFileInfo`,
`ColumnFamilyMetaData` and others). The function-type typedefs in
`block_test.cc` came out malformed. Empty `while (cond);` spin loops got a
`{;` body. Splitting declarations also exposed two vectors in
`db_etc2_test.cc` to `performance-inefficient-vector-operation`, so a
`reserve()` was added for each.

The lint bot reports every finding in a file a diff touches. This diff
splits declarations in `options/configurable_test.cc`, so it also fixes
that file's 14 older `cert-err58-cpp` and
`cppcoreguidelines-avoid-non-const-global-variables` findings, which come
from seven mutable file-level maps. Six are now `const` and built where
they are used (function-local statics, or locals in their only user).
The unused `imm_struct_option_info` is deleted.

Compatibility: no behavior change. In public headers, the only edits are
removed redundant `""` initializers, split declarations and added braces.

Reviewed By: rban1

Differential Revision: D123186310

fbshipit-source-id: f9c8e88b00e997db0196f8725c7c8d880cf798da
2026-10-13 14:06:55 -07:00
Jay Huh 956c7042da Check async I/O support on the MultiGet thread and fall back to sync (#15337)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15337

## Why

RocksDB crash tests repeatedly fail with `MultiGetEntity error: Not implemented: ReadAsync: failed to init io_uring` when synchronous `MultiGet` requests async I/O on a thread that cannot create an io_uring. The same `NotSupported` status can reach users if capability detection succeeds but an individual asynchronous submission later fails.

## Previous behavior

`Version` cached `FSSupportedOps::kAsyncIO` when it was constructed. Posix io_uring instances are thread-local, so that result described the construction thread rather than the thread later executing MultiGet. A cached positive result could route a different thread into `ReadAsync`, where io_uring creation failed and the error reached the caller. A cached negative result could also unnecessarily disable async I/O on a capable read thread.

## New behavior and implementation

Synchronous `Version::MultiGet` now checks async-I/O support on the thread executing the read. Both the cross-level `optimize_multiget_for_io` path and the within-level overlap path enter their coroutine-based I/O only when that thread supports it. Posix `SupportedOps` eagerly creates and retains that thread-local io_uring, so subsequent submissions reuse the verified ring.

The capability remains advisory: if `ReadAsync` nevertheless returns `NotSupported`, `AsyncFileReader` completes the `FSReadRequest` synchronously, including requests that use filesystem-owned buffers. Poll errors are applied only to requests that were actually submitted asynchronously, so synchronous fallbacks in a mixed batch retain their successful status.

Regression coverage forces io_uring capability failure only on a dedicated read thread and exercises both synchronous MultiGet branches. Separate tests cover all-request fallback and a batch mixing submitted and synchronously fallen-back requests under an injected Poll error. A one-line bug-fix release note records the user-visible correction.

## Safety and compatibility

This changes no API, ABI, or persisted format. The support check is only performed when synchronous `MultiGet` receives `ReadOptions::async_io`; the Posix steady-state cost is a virtual call and thread-local ring lookup per such MultiGet. Direct I/O continues to use the existing synchronous `MultiRead` route before `AsyncFileReader`, and native coroutine APIs are unchanged.

Reviewed By: pdillinger

Differential Revision: D117802488

fbshipit-source-id: 0f2991a523607d894436cd53123e7a1b2fe93167
2026-10-12 19:22:21 -07:00
Anand Ananthabhotla 9198b11b46 Make InjectedErrorLog::Entry 256 bytes on 32-bit targets (#15388)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15388

`InjectedErrorLog::Entry` in `utilities/fault_injection_fs.h` is written to the injected error log in its native layout, and a `static_assert` requires `sizeof(Entry) == 256` so the record size stays stable. That assert fails on i386 builds.

Root cause: the fields add up to only 252 bytes. On targets where `alignof(uint64_t)` is 8 (x86_64, aarch64, armv7), the compiler adds 4 bytes of implicit tail padding, so the size is 256. On i386, `uint64_t` members are 4-byte aligned inside a struct, so there is no tail padding and the size is 252.

Fix: add an explicit trailing `uint8_t padding[4]` field so the size no longer depends on `uint64_t` alignment.

Compatibility: on 64-bit targets the new field takes the place of the old implicit tail padding, so every field offset and the 256-byte record layout are unchanged. `Record()` value-initializes the entry (`Entry entry{}`), so the padding bytes are always written as zero. No decoder in the repo depends on the padding bytes.

___

landed-with-radar-review

Differential Revision: D124873369

fbshipit-source-id: f5c6f43ca85013b0e769853ad13b8d4f3f89b958
2026-10-12 16:50:46 -07:00
Yoshinori Matsunobu 77eef9fa2e Make RocksDB reverse iteration honor query aborts (#15376)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15376

RocksDB `DBIter` polled `ROCKSDB_THREAD_YIELD_CHECK_ABORT()` while scanning ordinary forward entries, but reverse `Prev()` / `SeekForPrev()` traversal and `MergeValuesNewToOld()` could scan many internal keys without polling. Long tombstone, version, or merge-operand runs could therefore keep a MyRocks query running after `KILL QUERY` until the scan finished.

Poll the existing hook before each potentially expensive internal iterator movement in the forward scan, forward merge-operand scan, and four reverse traversal loops. Mark the abort branch `UNLIKELY`; on abort, invalidate the iterator and return `Status::Aborted("Query abort.")`.

MyRocks already defines the hook and maps `Status::Aborted` for a killed THD to `HA_ERR_QUERY_INTERRUPTED`, so no MyRocks change is required. There are no API, ABI, or persisted-format changes. The hook remains Linux-only, and cancellation remains cooperative: this cannot preempt an individual underlying iterator operation that never returns.

The change is rebased onto current master, including D124377137's fix for the unrelated `WriteBufferManagerTest.FlushPolicyRetiresAndRestartsHandoffWorkers` failures seen on V1.

Reviewed By: pdillinger

Differential Revision: D124441004

fbshipit-source-id: 53bf83a00121b42566dbd93de232e973cbe78688
2026-10-12 16:36:04 -07:00
Josh Kang 4b983da055 Spell out special member functions (rule of five) (#15387)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15387

## Summary

RocksDB's `.clang-tidy` enables `cppcoreguidelines-special-member-functions`,
`cppcoreguidelines-virtual-class-destructor` and the noexcept-move checks,
and clang warns `-Wdeprecated-copy-with-user-provided-dtor`. A full scan
reports 476 findings. In most of them, a class declares a destructor (or
copy operations) but leaves the rest of copy/move implicit. That has two
costs. Readers can't tell whether a class may be copied or moved. And
resource-owning classes keep an implicit copy that would double-free if
anyone ever used it. This diff resolves 453 of the 476.

Approach, picked per class from least to most invasive:
1. An empty user-provided destructor becomes `= default`. This covers most
   interface classes, such as `virtual ~X() {}` -> `= default`. If the
   class is still copied, that would only rename the deprecated-copy
   warning, so the destructor is removed instead when nothing needs it
   (`Customizable`, `TableFactory` and `InternalKeyComparator` inherit a
   virtual one; `IteratorWrapperBase` isn't polymorphic), and
   `MemTableRep::KeyComparator` gets defaulted copy and move.
2. Classes whose copy is already deleted get explicitly deleted move
   operations too. Move was already unavailable, so this only documents
   it.
3. Resource-owning classes that are never copied get deleted copy and move
   operations. Examples: the C API wrapper structs, which run a
   `destructor_` callback (edited in `tools/c_api_gen/c_base.cc` and
   regenerated into `db/c.cc`); `ShardedCache`, which frees its shards;
   iterator wrappers; and RAII handles.
4. Value types that are copied or moved get the missing operations as
   `= default`, which keeps their current behavior.
5. Exception specifications of existing functions are unchanged, except
   for `WriteBatch`'s move assignment (below): no other `noexcept` is
   added or removed. Only the move operations this diff
   newly declares (`Comparator`, `FileSampledStats` and the thread-local
   `Entry`) are `noexcept`; they copy only scalars or atomics.

`WriteBatch`'s copy and move assignment destroyed `*this` and
placement-new'ed a new `WriteBatch` over it. That is undefined behavior
when `*this` is a subclass, and if the copy constructor threw, `*this`
was left destroyed and was later destroyed again. Move assignment now
assigns member-wise and ends in the same state as the move constructor,
which leaves `has_key_with_ts_`, the timestamp-size tracking and the
other fields it doesn't copy at their defaults. `rep_` is swapped through
a temporary, because a plain string move-assignment can keep the old
buffer (e.g. a large batch reset with `batch = WriteBatch()` would keep
its memory). Nothing in it can throw, so it is now `noexcept`. Copy
assignment copies into a temporary and moves it in, so a throwing copy
leaves `*this` unchanged.

Bugs this exposed and fixed:
- `RestoreAfterCopyOrCreateWorkItem` (backup engine): the hand-written
  move constructor and assignment moved only some fields, so `from_file`
  and `to_file` were empty after `push_back(std::move(...))`. A restore
  that hit a checksum mismatch then reported "While restoring  -> : ..."
  without file names. It now uses the compiler-generated moves, which
  are still `noexcept`.
- `ExternalSstFileIngestionJob` lived in a `std::vector`, and its
  destructor frees raw-owned pointers, so the implicit copy used on
  vector reallocation would double-free. Its file batches also point into
  its own inline file storage, so neither a copy nor a move is safe after
  `Prepare()`. Copy and move are now deleted, and `DBImpl` holds jobs
  through `std::unique_ptr`.

Compatibility: no public class loses an operation users can call. Public
interfaces mostly only get `virtual ~X() = default;`.
`WriteBatch`'s move assignment becomes `noexcept`, which is not part of
the mangled name, so the ABI doesn't change.
`SubcompactionJobInfo` and `CompactionJobInfo` intentionally stay C++20
aggregates, so users' aggregate and designated initializers keep
compiling. `Configurable`, `CompareInterface`, `WriteBatchBase`,
`ObjectLibrary::Entry` and `MemTableRep::KeyComparator` declare defaulted
copy and move, and `Customizable` no longer declares a destructor. As a
result, `Configurable` and `Customizable` subclasses that declare no
destructor or copy operations of their own now move their
registered-options list instead of copying it. In-tree, these are the
mutable-options adapters, the table factories, the memory allocators and
test classes; all are held through smart pointers or used in place, and
`Clone()` copies, so none is moved by value.

Not changed (23): `IODebugContext` (custom deep copy plus a
`std::shared_mutex`), `DBWithColumnFamilies` in db_bench (stored in a
`std::vector` with non-movable members), `Snapshot`'s protected virtual
destructor (intended), `ManagedSnapshot`, the `SubcompactionJobInfo` and
`CompactionJobInfo` aggregates, and the 16 noexcept-move-constructor and
noexcept-swap findings on existing functions, including `autovector`'s
move assignment (adding `noexcept` would change their exception
specifications). Copying `ManagedSnapshot` already double-releases the
snapshot. Fixing that needs a real move implementation and is a behavior
change, so it is left for a separate diff.

Reviewed By: rban1

Differential Revision: D123186309

fbshipit-source-id: 6f8e57e233e12199a007407130bb9a1f6d52e8d1
2026-10-12 16:00:09 -07:00
Josh Kang ddac37a780 Serialize subset-checkpoint OPTIONS persistence with SetOptions (#15386)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15386

Fixes crash-test failure T292562874:
`Checkpoint failed: Corruption: [RocksDBOptionsParser]:failed the verification on BlockBasedTable::: filter_policy.id`

Root cause:
- D123104845 ("Commit authoritative OPTIONS file numbers in MANIFEST", 77d94c989243) made subset checkpoints (`CreateCheckpoint` with `include_cf_ids` that exclude some CFs) write a new OPTIONS file. `DBImpl::GetLiveFilesStorageInfoImpl` captures the snapshot under the DB mutex with `cfd->GetLatestCFOptions()`. That is a shallow copy: `table_factory`, and through it `filter_policy`, are `shared_ptr`s to the live objects. `DBImpl::CreateOptionsFileForSubsetCheckpoint` then calls `PersistRocksDBOptions` without holding `options_mutex_`. `PersistRocksDBOptions` serializes the file, then re-parses it and checks it against the same in-memory options.
- `RibbonFilterPolicy::bloom_before_level_` is a `kMutable` atomic that `SetOptions(table_factory.filter_policy.bloom_before_level=...)` changes in place, and `RibbonFilterPolicy::GetId()` includes it (`ribbonfilter:<bits>:<bloom_before_level>`). A concurrent `SetOptions` between the write and the verify makes the in-memory `filter_policy.id` differ from the file, so verification returns Corruption and the checkpoint fails.
- Every other OPTIONS writer (`WriteOptionsFile`, CF create/drop) holds `options_mutex_`. `SetOptions` holds it for its whole mutate-and-persist sequence. The new subset-checkpoint writer was the only one that skipped it.

Trigger in the crash test: db_stress randomly sets `table_factory.filter_policy.bloom_before_level` through `SetOptions` (when `keepRibbonFilterPolicyOnly`, i.e. `bloom_before_level` != INT_MAX with ribbon filters; see the OPTIONS dump with `ribbonfilter:18:1`, `ribbonfilter:8.9:2147483647`, `ribbonfilter:5.67:-1` across CFs). It also takes subset checkpoints. The two run concurrently on different threads.

Fix: hold `options_mutex_` in `CreateOptionsFileForSubsetCheckpoint` while persisting and verifying, the same as `WriteOptionsFile`. The only caller is `CheckpointImpl::CreateCheckpoint`, which holds no DB locks at that point, so lock ordering (options_mutex_ before mutex_) is kept.

Note: the snapshot's scalar fields are still captured at live-file time. Live pointer sub-options such as the filter policy reflect their state at persist time. This was already true of the shallow snapshot. The change only makes the persisted file self-consistent with what is verified.

Reviewed By: xingbowang

Differential Revision: D124728996

fbshipit-source-id: adb5511c2ddd73ab138fd7fd21109199ff4f5b1e
2026-10-12 12:01:42 -07:00
Peter Dillinger 76d7c29389 Update folly hash (#15375)
Summary:
has been neglected for a few months, finally taking care of it.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15375

Test Plan: local folly build (from internal wiki instructions) to verify the fixes from claude, CI

Reviewed By: archang19

Differential Revision: D124436669

Pulled By: pdillinger

fbshipit-source-id: 0a0e3f6857cc8ba20941281b9dfd618a6997b86f
2026-10-12 08:49:01 -07:00
Xingbo Wang 1334e4dd52 Add offline OPTIONS binding repair (#15310)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15310

Why the change is required:

The committed OPTIONS number in MANIFEST gives persisted-options readers a
precise source of truth. If that file is missing or corrupt, or an operator
must deliberately replace it with a known-good snapshot, persisted-options
loading can fail before a normal writable open can publish a replacement.
RocksDB therefore needs a supported offline recovery path that obeys the
binding and global file-number rules.

Previous behavior:

ldb update_manifest could rewrite file-temperature metadata but could not
repair an OPTIONS binding. Operators could not safely bind a selected snapshot
while accounting for the current MANIFEST boundary, every file-number
allocation root, recoverable WAL output, and crash durability. Blindly
selecting or reusing an existing OPTIONS number could bind the wrong snapshot
or collide with another RocksDB file.

New behavior:

ldb update_manifest --rebind_options_file --options_file=OPTIONS-<number>
performs an offline repair from an explicitly selected trusted snapshot whose
tracking marker is enabled. Optional --file_number_paths arguments identify
external allocation roots that are not serialized in OPTIONS.

The command locks the DB and validates that the selected OPTIONS snapshot
contains every live MANIFEST column family. Extra OPTIONS CFs are accepted as
a safe superset and are filtered by binding-aware persisted-options APIs; a
snapshot missing any live CF is rejected. The command raw-copies the selected
bytes to a newly allocated canonical OPTIONS number, syncs that file and its
directory entry, and commits the new number with one MANIFEST record. It
rewrites the current MANIFEST only through the last completely decoded record,
appends the commit and next-file boundary to a temporary copy, syncs it,
atomically renames it over the original, and syncs the MANIFEST directory.

The crash contract has one publication point. Before MANIFEST replacement,
the previous commit remains authoritative and the copied tracked OPTIONS file
is only an ignorable orphan. After replacement, the new snapshot is
authoritative.

Implementation:

- Parse the selected snapshot for validation but copy it byte-for-byte so
  unknown or newer option fields are preserved.
- Replay MANIFEST to recover live CF membership, the committed number, next
  file number, and last valid logical-record boundary; discard a torn physical
  tail during the bounded-memory rewrite.
- Scan the DB root, WAL/archive directories, serialized roots, and
  operator-supplied roots for allocated numbers.
- Parse recoverable WALs and reserve conservative headroom for recovery
  outputs, missing-column-family creation, persistent statistics, asynchronous
  WAL creation, MANIFEST rollover, and subsequent writable-open allocations.
- Reject file-number overflow, missing allocation roots, corrupt or unreadable
  WALs, invalid input names, missing-live-CF snapshots, and simultaneous
  temperature rewriting.
- Preserve the existing update_temperatures behavior and result text.

Safety boundaries and unsupported cases:

- This is an explicit offline mutation. It never auto-selects a snapshot and
  refuses to run while another process holds the DB lock.
- The selected input must be a canonical OPTIONS-<number> basename in the DB
  directory, must parse successfully, must enable MANIFEST tracking, and must
  contain all live MANIFEST CFs. Extra CFs are allowed but missing live CFs are
  not.
- The command does not repair snapshot contents or alter topology.
- OPTIONS does not serialize all DB/CF data roots. Operators must provide every
  external root through --file_number_paths; the command fails closed when a
  supplied or inferred root cannot be scanned.
- Conservative allocation headroom can reject a repair near kFileNumberMask
  even when a particular future open would allocate fewer files.
- Normal DB open and core publication do not depend on this maintenance
  command.

Validation coverage and known limitations:

Coverage includes parsing and incompatible modes, the DB lock, tracking and
membership validation, safe-superset acceptance, missing-live-CF rejection,
byte-preserving copy, torn MANIFEST tails, one-record commit publication,
file-number exhaustion and writable-open headroom, external and missing roots,
WAL recovery bounds, asynchronous WAL and persistent-stat allocations,
MANIFEST rollover, writable reopen, and later CF creation. The end-to-end test
confirms safe-superset rebinding and rejection of a higher tracked snapshot
that omits a live CF, verifies the final committed MANIFEST number, and runs
the real ldb scan --try_load_options subprocess. Cross-platform CI is not part
of local validation.

Reviewed By: pdillinger

Differential Revision: D123104846

fbshipit-source-id: 8cf7c54c1c7a360411034469614eed8aaffdcf8e
2026-10-11 13:13:46 -07:00
Xingbo Wang 9fb698e218 Synchronize IteratorCloseWALFile1 flushes (#15382)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15382

Why the change is required:
IteratorCloseWALFile1 is flaky under suite load. Its WAL-count assertions assume the iterator is the final owner of an obsolete SuperVersion, but the test did not prove that the preceding background flush had released its temporary references or that the next flush was unable to start.

Previous behavior:
The test occupied one HIGH-priority Env worker with a sleeping task. The shared Env can have multiple HIGH-priority workers, so another worker could run the flush concurrently. Also, Flush() can return before BackgroundCallFlush() has fully exited. Either race can leave two WAL writers open after iterator deletion. A fatal assertion could then destroy the stack-local SpecialEnv before fixture teardown closed the DB, causing a secondary use-after-return or segmentation fault.

New behavior:
The test waits for DBImpl::BGWorkFlush:done after the iterator-establishing flush, then replaces that dependency with a barrier at DBImpl::BackgroundCallFlush:start for the flush triggered by the final write. The iterator is therefore the final old-SuperVersion owner when it is destroyed, and the test releases the blocked flush only after checking synchronous WAL closure.

Implementation:
Replace the sleeping background task with existing SyncPoint dependencies. Own the iterator with unique_ptr, disable SyncPoint processing on every exit, and close the DB with a scope guard before the local SpecialEnv is destroyed.

Safety, compatibility, and limitations:
This changes only test synchronization and failure cleanup. It does not change production code, RocksDB APIs, persisted formats, locking, or runtime behavior. The test continues to cover foreground iterator cleanup with background_close_inactive_wals enabled; background purge behavior remains covered separately by IteratorCloseWALFile2.

___

landed-with-radar-review

Differential Revision: D124653963

fbshipit-source-id: 65d381128b5013b4d62ad60e42f0e4566928f000
2026-10-11 12:02:10 -07:00
Xingbo Wang baa4d373a6 Commit authoritative OPTIONS file numbers in MANIFEST (#15305)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15305

Why the change is required:

Column-family topology is durable in MANIFEST, while its operational
configuration snapshot is stored in a separate OPTIONS file.
CreateColumnFamily and DropColumnFamily previously committed MANIFEST first
and wrote OPTIONS afterward. An OPTIONS I/O failure could therefore make the
API report failure after the topology change was already durable.
Persisted-options readers also selected the highest file number and could
consume an orphan left by a failed publication.

Previous behavior:

RocksDB had no durable correspondence between a MANIFEST state and the
OPTIONS snapshot describing it. LoadLatestOptions and
GetLatestOptionsFileName treated the highest-numbered OPTIONS file as
authoritative. Column-family creation and deletion returned the later OPTIONS
persistence error even when their MANIFEST edit had already succeeded.
Multi-column-family operations wrote a complete OPTIONS snapshot after every
successful member, producing quadratic OPTIONS bytes.

Ordinary DB::Open consumes caller-supplied DB and column-family options rather
than loading OPTIONS. This change preserves that contract.

New behavior:

Add the default-disabled experimental DB option
track_options_file_number_in_manifest. Its value is serialized into each
OPTIONS file so the selection rule is self-describing and participates in the
normal DB-options compatibility checks.

When enabled, RocksDB first durably creates and renames OPTIONS-N and then
commits N with one safely ignorable MANIFEST field. A normal OPTIONS rotation
uses one MANIFEST commit. Column-family creation and deletion prewrite the
proposed snapshot and commit its number together with a topology edit in the
same VersionEdit. A failure before the OPTIONS file is durable changes no
topology. A failure after the file is durable but before MANIFEST commit leaves
an orphan that binding-aware readers ignore, while the previous committed
snapshot remains authoritative.

Each multi-CF create or drop writes one proposed snapshot, independent of the
batch size. Create commits the new number with the first successful add; drop
commits it with the last successful drop. Existing partial-success semantics
can therefore leave a committed snapshot with extra CFs, but never one missing
a live MANIFEST CF. Persisted-options APIs filter this safe superset against
recovered MANIFEST membership. A snapshot missing a live CF is corruption.
DB::Open with create_missing_column_families uses the same batched path.

If the newest OPTIONS file has tracking disabled, it retains the legacy
highest-numbered-file behavior, including after an old or disabled writer has
run. If it has tracking enabled, readers use the recovered MANIFEST commit and
reject newer tracked orphans. A missing usable commit falls back to the highest
tracked snapshot for RepairDB and legacy-rewrite recovery. DB open logs that
fallback through its configured logger; logger-less utility APIs remain
silent. Writable open removes rejected tracked orphans when local obsolete-file
deletion is enabled. Read-only open never mutates the DB directory.

A DB with no recovered commit and tracking disabled takes the legacy fast path
without parsing the newest OPTIONS file. When inspection is required, RocksDB
reads only the tracking marker. Marker-read failures used for selection do not
fail DB open; a recovered commit remains authoritative and ambiguous newer
candidates are conservatively retained.

The first topology change after enabling tracking first binds the current
topology, so failure of the proposed change cannot make its orphan
authoritative. MANIFEST failure during an ordinary tracked OPTIONS rotation
also enters the existing manifest-write background-error path.

Implementation:

- Add the safely ignorable CommittedOptionsFileNumber VersionEdit field and
  carry it through recovery, MANIFEST snapshots, rollover, and subset
  checkpoints.
- Persist the tracking marker through C++, C, and Java options, generated
  round-trip coverage, compatibility checking, and db_stress configuration.
- Centralize binding-aware selection for DB open and public persisted-options
  APIs. Reuse RecoveryContext's directory listing, use a narrow marker scan,
  and recover live CF names while replaying MANIFEST.
- Filter manifest-selected safe-superset snapshots to live MANIFEST CFs in
  LoadLatestOptions and compatibility verification without discarding their
  option maps.
- Prevalidate and sanitize CF batches outside the DB mutex, write one complete
  snapshot while options_mutex_ serializes topology changes, and hold the DB
  mutex only for the existing individual MANIFEST commits. Batch missing-CF
  creation during DB open follows the same locking boundary.
- Keep the committed snapshot live in cleanup and live-file APIs, and make
  subset checkpoints write an OPTIONS snapshot and filtered MANIFEST with
  identical membership in tracked and legacy modes.
- Preserve the first partial multi-CF drop error while consuming legacy
  OPTIONS persistence Status, and sync the checkpoint MANIFEST directory after
  replacement.
- Pass checkpoint helper callbacks by reference and group VersionEdit storage
  by alignment so the new paths add neither callback copies nor avoidable
  object padding.

Safety boundaries and unsupported cases:

- Tracking defaults to false. Disabled writers and never-tracked databases
  retain legacy write ordering and highest-numbered selection.
- Older binaries safely ignore the new MANIFEST tag and continue choosing the
  highest OPTIONS number. Downgrade is readable but not crash-atomic: an old
  binary can consume a tracked orphan. That is the same class of
  OPTIONS/MANIFEST inconsistency as the previous behavior.
- A rollback binary must know the serialized option name before the feature is
  enabled. Exact compatibility checks intentionally report a mismatch when the
  caller's tracking setting differs from the persisted snapshot.
- A newer untracked OPTIONS file deliberately supersedes an older committed
  number, allowing an old or disabled writer to become authoritative without
  rewriting MANIFEST.
- Reactive secondary and follower instances do not enforce or repair the
  binding. Ordinary DB::Open still uses caller-supplied options.
- Writable open does not delete rejected tracked or ambiguous files when a
  compaction service owns OPTIONS-file retention. Read-only open performs no
  repair writes.
- Direct consumers that scan OPTIONS filenames themselves are unchanged; the
  binding is enforced by RocksDB's persisted-options APIs.
- If the committed snapshot is missing and only tracked candidates remain,
  selection intentionally falls back to the highest candidate so RepairDB
  output remains usable.

Validation coverage and known limitations:

Focused coverage exercises encoding compatibility, tracked and legacy
selection, the never-tracked open fast path, marker-read failures, exact
tracking-marker compatibility, activation and disable/re-enable transitions,
batched create/drop and create-missing behavior, partial-batch safe supersets,
OPTIONS/MANIFEST failure injection, background-error propagation, orphan
cleanup and retention, read-only non-mutation, subset-checkpoint behavior,
stale OPTIONS recovery, RepairDB option sanitization, and transient
read-corruption reconstruction. The assertion-enabled full executable suite
passes. The repository check-format and check-sources wrappers cannot run
because they invoke Git inside this Sapling checkout; fbsource lint is used
instead. Cross-platform CI is not part of local validation.

Reviewed By: pdillinger

Differential Revision: D123104845

fbshipit-source-id: 77d94c989243c3f3f9fedfa81fa364c166250303
2026-10-11 10:39:30 -07:00
Ranjan Banerjee 4b44c2b4ba Fix WBM worker retirement and flush test accounting (#15373)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15373

Fix worker retirement when changing `WriteBufferManager` from `kFlushLargestAcrossDBs` to `kFlushOldest`. The coordinator could pass its active-policy check, consume the policy-change notification during refresh, and enter a one-second idle wait. This delayed retirement beyond the 300 ms deadline in `WriteBufferManagerTest.FlushPolicyRetiresAndRestartsHandoffWorkers`. Recheck the policy under `sorter_wait_mu` before waiting so the coordinator promptly enters the retirement path. The setter remains non-blocking, and retirement still waits for executor callbacks to finish.

Also fix startup pressure-wait accounting in `BackgroundFlushCycleTest.FastRejectedCandidateDoesNotAddPressureDelay`. Registering the healthy candidate can invalidate the coordinator's initial ranking snapshot and cause a legitimate pressure wait before either handoff runs. The old assertion counted waits from startup and incorrectly attributed that wait to the rejected candidate. Record the counts at rejected and healthy dispatch and require them to match, preserving the check that rejection adds no pressure interval.

Include both fixes in this diff so its `write_buffer_manager_test` target covers both corrected behaviors. Replace worker retirement and restart polling with sync-point synchronization. The rejection test deterministically invalidates the first snapshot during healthy-candidate registration, verifies that a startup wait occurred, and checks that advancing to the healthy candidate adds no further pressure wait. Retain the clang-format 21 line wrapping required by CI.

Reviewed By: pdillinger

Differential Revision: D124377137

fbshipit-source-id: 1420196e47ccba0268d0427c4ed623925aa3ad5b
2026-10-09 16:50:24 -07:00
Josh Kang da82001d81 Mark overriding methods and destructors override (#15366)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15366

## Summary

RocksDB's `.clang-tidy` enables `modernize-use-override`, and clang
reports `-Winconsistent-missing-destructor-override` when a class marks
its other overrides `override` but not its destructor. A full scan finds
190 `modernize-use-override`, 100
`inconsistent-missing-destructor-override` and 2 `suggest-override`
findings in existing code. Each one comes back whenever a nearby line is
edited. They also leave classes where it isn't obvious which methods are
overrides and which introduce new virtuals.

This diff marks every overriding member function and destructor
`override` and drops the redundant `virtual` on them. It also changes
`final override` to plain `final`, following the check's default. Methods
that introduce a new virtual keep `virtual`. Most edits are clang-tidy's
own fix-its, applied mechanically and then clang-formatted.

Compatibility: no signatures change. In public headers the only effect is
`override` on existing overrides (mostly destructors, plus
`Cache::Name()` and `Cache::GetPrintableOptions()`, whose base
declarations are unconditional), so user code that subclasses these types
is unaffected. Every touched class now marks all of its overrides
consistently. This matters because a partially-marked class would make
clang emit `-Winconsistent-missing-override` in user builds.

Reviewed By: rban1

Differential Revision: D123186308

fbshipit-source-id: 1f489ea32d938e3ec0faf9abc4f7ff49e042d37a
2026-10-09 14:39:14 -07:00
Peter Dillinger 72beaf8dfb Update RocksDB main for 11.12 release (#15372)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15372

Why: keep internal RocksDB main ready for 11.13.0 development while the 11.12 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.12.0 to 11.13.0
- Add `11.12.fb` to `check_format_compatible.sh`
- Sync HISTORY.md `## 11.12.` section from `11.12.fb`
- Delete 24 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: archang19

Differential Revision: D124370612

fbshipit-source-id: fcd3864695f118bf8be316878d92be5bf8ace43c
2026-10-09 12:32:51 -07:00
Andrew Chang 9b4b980c6a Check background error Status in DirSyncFailureAfterTrashFileDeleted (#15371)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15371

`DeleteSchedulerTest.DirSyncFailureAfterTrashFileDeleted` aborts with "Failed to check Status" in builds with `ASSERT_STATUS_CHECKED=1`.

**Why it matters:** this breaks the `ASSERT_STATUS_CHECKED=1` `make check` CI job. Only the test is wrong; `DeleteScheduler` is unchanged.

- **Problem:** the test calls `GetBackgroundErrors().size()`. The returned `std::map<std::string, Status>` holds unchecked `Status` copies, so destroying it aborts.
- **Fix:** keep the map, assert its size, and check each error with `ASSERT_NOK`, as the other tests in this file do.

___

landed-with-radar-review

Differential Revision: D124369694

fbshipit-source-id: 85429b0bc886f6c87af3836bfe34b5da6eb47459
2026-10-09 09:31:32 -07:00
Josh Kang 928527b869 Fix bugs and false positives from clang-analyzer/bugprone (#15325)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15325

## Summary

RocksDB's `.clang-tidy` enables the `clang-analyzer-*` and `bugprone-*`
checks. A full scan of the repo still reports 78 findings from these
checks in existing code. Each time a nearby line is edited, they come
back as noise, and a few of them are real defects. This diff resolves 73
of the 78; upstream has since fixed one more, and the four left are
listed under "Intentionally not changed". Some fixes repair real bugs.
The rest make the code's intent explicit to the analyzer without
changing behavior.

Real bugs fixed:
- `port/mmap.cc`: `MemMapping::operator=(MemMapping&&)` ran the
  destructor on `*this`, `memcpy`'d over a non-trivially-copyable object,
  then placement-new'd the source. That is undefined behavior, and `*this`
  can be the base subobject of a `TypedMemMapping`. It now moves the
  members one by one, and the previous mapping is released through a
  local `MemMapping`.
- `port/mmap.h`: `TypedMemMapping::operator=(MemMapping&&)` had no
  `return *this;`, so calling it was undefined behavior.
- `cache/typed_cache.h`: `if constexpr (sizeof(TCreateContext) > 0)` is
  always true, so the branch meant for empty create contexts (for example
  `BlobContentsCreator`) could never run. The check is now
  `!std::is_empty_v<TCreateContext>`. Existing instantiations still call
  the same `Create` function.
- `cassandra_row_merge_test.cc`: the test reused a vector after
  `std::move`-ing it into `RowValue::Merge(std::vector<RowValue>&&)`. That
  only worked because `Merge` happens not to consume its input. The test
  now rebuilds the input.

Behavior-preserving changes:
- Virtual calls in constructors and destructors are now explicitly
  qualified (for example `DBImpl::CloseImpl()` in `~DBImpl`). During
  construction and destruction, dispatch already resolved to these
  functions.
- Stale `/*name=*/` argument comments now match the parameter names.
- `unique_ptr::release()` after a successful cache `Insert` (where the
  cache takes ownership) uses `[[maybe_unused]] T* released = x.release();`,
  the pattern already used in `table_cache.cc`.
- Removed `class Mutex;` and `class StaticMeta;` forward declarations
  that sat in the wrong namespace.
- Added default member initializers for the
  `TablePropertiesCollectorFactory::Context` fields that its default
  constructor left uninitialized.
- Smaller cleanups: a redundant `cond ? 3 : 3` in a test,
  `sizeof(std::string)` in a memory estimate, and an explicit
  `Cleanable` base move via `up_cast`. Tests that intentionally use
  moved-from objects now carry `NOLINT(bugprone-use-after-move)`,
  matching the existing convention.

Intentionally not changed: enum casts in tests that check invalid-value
handling; three `clang-analyzer-core.NullDereference` reports the
analyzer can't prove safe (the C API timestamp out-parameters in
`db/c.cc`, which are only written when the caller asked for a
timestamp, and the waiter-list walk in `util/async_file_reader.cc`);
bit-flag enums in public headers (`FileOpenContract`,
`OptionTypeFlags`), where OR-combined values are valid; ownership
transfers the analyzer can't see across translation units
(`partitioned_index_reader.cc`, `fs_posix.cc`); and the intentional
two-separator check in `file/filename.cc`.

Reviewed By: rban1

Differential Revision: D123186307

fbshipit-source-id: 623e871286b1c73263b1309cb194692194199e0f
2026-10-08 17:19:47 -07:00
Andrew Chang f9b62a0567 Release trash accounting on immediate retry and after dir sync failure (#15352)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15352

DeleteScheduler now releases a trash file's accounting in two more cases where the file is gone but its size stayed counted until restart.

**Why it matters:** Leaked bytes stay in the total trash size. Once they pass `max_trash_db_ratio` of the tracked DB size, every later deletion skips rate limiting and is deleted immediately, which can cause I/O stalls.

- **Immediate retry:** A failed accounted trash deletion is retried through `DeleteExistingTrashFileImpl`. When the retry deletes immediately, for example with rate limiting disabled, and the file is already gone, `DeleteFileImmediately` skipped `OnDeleteFile` and the retry entry was put back. It now untracks the file and subtracts the entry's size. The NotFound status is still returned.
- **Dir sync failure:** If the unlink worked but `NewDirectory` or the dir fsync then failed, `DeleteTrashFile` reported 0 deleted bytes and skipped `OnDeleteFile`, so the accounting stayed. It now records the deleted bytes and untracks the file right after the unlink, and reports `file_gone` so `BackgroundEmptyTrash` releases the rest without parking a retry entry. The sync error is still reported as a background error.
- **Details:** Adds an `IsFileGone` helper for NotFound/PathNotFound and a `DeleteScheduler::DeleteTrashFile:BeforeSyncDir` sync point for tests. Both new tests keep one more tracked file, so they check that only the gone file's bytes are released.

Reviewed By: anand1976

Differential Revision: D124000703

fbshipit-source-id: 54ed8be66d5670709054a46a258ceaf72b10d899
2026-10-08 15:52:29 -07:00
Peter Dillinger 2e689e3c00 Fix hang in FlushLargestAcrossDBsPolicy test (#15365)
Summary:
DBWriteBufferManagerTest.FlushLargestAcrossDBsPolicy (added in https://github.com/facebook/rocksdb/issues/15047) can hang forever under load, as seen in nightly
build-linux-arm-test-full (db_write_buffer_manager_test shard 4).

The test's first phase waits for other_db to flush via the OnFlushCompleted listener, then immediately starts a second phase that writes 500KB to other_db and calls TEST_ScheduleFlushOnLargestDB(), expecting other_db to be picked. But OnFlushCompleted fires before the flush thread finishes its post-flush work, including refreshing other_db's WriteBufferManager flushable-memory accounting. If the second phase runs in that window, other_db's accounting is stale and the refresh (which only TryLocks the DB mutex) can leave other_db out of the ranking, so db_ is picked and flushed instead. The test then waits in the unbounded FlushedDBRecorder::WaitForFlush(other_db) forever. The same race occasionally fails ASSERT_TRUE(TEST_ScheduleFlushOnLargestDB()) instead, when other_db still has WBM flush work outstanding.

Fix the test by waiting for other_db's background work to finish (including any WBM flush job) after the first-phase flush, before starting the second phase. No production code changes.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15365

Test Plan: Reproduced by running db_write_buffer_manager_test shard 4 of 6 (as generated for a 4-core CI machine) many times concurrently on an oversubscribed machine. In an interleaved A/B of 1500 runs each, the unmodified test hung 71 times (all at the second-phase WaitForFlush, confirmed via stack dumps) and failed the line-995 assertion 16 times; with this change there were no hangs or failures of this test. DB LOG thread IDs from hung runs confirmed the test thread had flushed db_ in the second phase. The hang rate was similar before https://github.com/facebook/rocksdb/issues/15331, so the race dates to https://github.com/facebook/rocksdb/issues/15047.

Reviewed By: rban1

Differential Revision: D124196572

Pulled By: pdillinger

fbshipit-source-id: d621c5a0b441634fc339283caf9ad7a0d6eea401
2026-10-08 15:26:53 -07:00
Andrew Chang 78a9d3c98c Release trash accounting when a queued trash file vanishes (#15349)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15349

DeleteScheduler now releases the accounting for a queued trash file that disappears before it is deleted. Before this change, that size leaked until the process restarted.

**Why it matters:** The leaked bytes stay in the total trash size. Once they exceed `max_trash_db_ratio` of the tracked DB size, every later deletion bypasses rate limiting and is deleted immediately, which can cause I/O stalls.

- **Problem:** If a trash file's directory is renamed or removed while the file waits in the delete queue, `DeleteTrashFile` fails with PathNotFound. `BackgroundEmptyTrash` then keeps the file's remaining accounted bytes in `total_trash_size_` and records it in `failed_accounted_trash_deletions_` for a retry. The retry never happens, because nothing schedules that path again. The file also stays tracked in `SstFileManager`, so the total size is inflated too.
- **Fix:** On NotFound/PathNotFound, `DeleteTrashFile` untracks the file via `OnDeleteFile`, and `BackgroundEmptyTrash` subtracts the file's full remaining accounted size and skips the retry map. The error is still reported through `GetBackgroundErrors()`, so that behavior is unchanged.

Reviewed By: anand1976

Differential Revision: D123986893

fbshipit-source-id: 4c084241a8100ae9d28fb92edf02a4362d6f60cb
2026-10-08 15:06:39 -07:00
Anand Ananthabhotla 39d56d5ad7 add block read accounting to IODispatcher (#15363)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15363

as title. we were not accurately reporting block read sizes and counts to the thread local perf context

given that IODispatcher can coalesce requests, we also count any sub coalesce threshold added bytes to these totals

Reviewed By: chaitanyadatye, xingbowang

Differential Revision: D123941253

fbshipit-source-id: eddc33d144afa49a8137fb174008030d22f3bddd
2026-10-08 14:28:01 -07:00
Ranjan Banerjee dec67990f0 Add db_stress coverage for WriteBufferManager flush policies (#15048)
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
2026-10-07 16:30:03 -07:00
Ranjan Banerjee 9b4c5422f0 Avoid WBM pressure delay after fast rejection (#15343)
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
2026-10-07 16:03:20 -07:00
Davis Rollman 7292adcc9a AutoHeaders: migrate to explicit headers (batch 116)
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
2026-10-07 15:43:34 -07:00
Peter Dillinger 6b15f1643e Give nightly-candidate workflow a distinct name (#15345)
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
2026-10-07 15:35:08 -07:00
Ranjan Banerjee a38cd3a439 Bypass stalled cross-DB WBM flush candidates (#15331)
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
2026-10-07 14:47:58 -07:00
Anand Ananthabhotla 45203a867f Add retryable ExternalTableBuilder contract (#15342)
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
2026-10-07 13:54:38 -07:00
Ranjan Banerjee 0561153b73 Add configurable largest-memtable flush policies to WriteBufferManager (#15047)
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
2026-10-06 16:26:51 -07:00
Peter Dillinger 1c490a4a4d Fix "ancester" spelling; deprecate TablePropertiesNames (#15335)
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
2026-10-06 11:59:54 -07:00
generatedunixname3846135475516776 60f090647d Rocksdb Crash Test failed: Callstack - SharedState::SafeTerminate -> NonBatchedOpsStres (#15338)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/15338

Reviewed By: jaykorean

Differential Revision: D122865369

fbshipit-source-id: 65a67955dd79a68fe1e4f15116d40dc669e1519e
2026-10-06 11:32:30 -07:00
Josh Kang 5b77e34985 Add range deletion support to external tables (#15334)
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
2026-10-05 18:39:42 -07:00
Andrew Chang 3ea1ec56df Fix MultiGet reading from disk with kBlockCacheTier (#15321)
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
2026-10-05 15:13:28 -07:00
Jay Huh f16157cfc5 Fix ClipColumnFamily dropping the file that ends at begin_key (#15320)
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
2026-10-05 12:48:29 -07:00
Peter Dillinger c83a84e98c Rename TableProperties::creation_time to oldest_ancestor_time (#15304)
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
2026-10-05 10:34:04 -07:00
Josh Kang c0512c4c34 Remove deprecated write_global_seqno ingestion support (#15287)
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
2026-10-05 09:53:52 -07:00
Xingbo Wang 5f1132b884 Require DB mutex checks in AI code reviews (#15324)
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
2026-10-05 09:23:49 -07:00
Josh Kang c2b86f3ec0 Lazily allocate MultiGet coroutine scratch buffers (#15298)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15298

## Summary

BlockBasedTable::MultiGetCoroutine() embedded the synchronous 8 KiB scratch buffer in every coroutine frame, including cache-hit requests. The change uses zero coroutine stack capacity and allocates the required heap size only when I/O is needed, while preserving synchronous behavior.

## Benchmarks

| Variant | Coroutine frame allocation |
| Before | 21,048 bytes |
| After | 12,864 bytes |
| Change | -8,184 bytes (-38.9%) |

No API, format, or read-result changes.

Reviewed By: xingbowang

Differential Revision: D122810207

fbshipit-source-id: 003a8349b8da72de2eca2e62fb9f7b3d8802baed
2026-10-02 20:17:30 -07:00
Yoshinori Matsunobu 8585299552 Rate-limit DestroyDB cleanup of existing trash (#15285)
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
2026-10-02 16:21:19 -07:00
Xingbo Wang 866e8e9f06 Make full error-recovery flush atomic (#15293)
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
2026-10-02 14:43:36 -07:00
Xingbo Wang 19b6f85266 Support async file open for SecondaryDB (#15294)
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
2026-10-02 12:21:46 -07:00
xingbowang 4cd0450f31 Fix big-endian builds for the fault-injection filesystem (#15297)
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
2026-10-01 15:15:13 -07:00
Xingbo Wang b58cbcf13c Apply transaction visibility to entity reads (#15269)
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
2026-10-01 15:04:26 -07:00
Josh Kang 4d180247ae Add PreparedFileInfo serialization APIs (#15274)
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
2026-10-01 13:45:31 -07:00
Jay Huh ded3cb72a5 Fix trace replay on the default column family and error reporting in multi-threaded replay (#15296)
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
2026-10-01 11:43:44 -07:00
Josh Kang aeebcbded4 Add coroutine external table reads (#15291)
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
2026-10-01 09:28:24 -07:00
Xingbo Wang 11516577ab Pass blob file checksums through blob file opens (#15268)
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
2026-09-30 14:55:26 -07:00
Jay Huh 9d358e63d6 Widen compaction service counts to full kNumOfReasons (#14924)
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
2026-09-30 12:39:00 -07:00
Xingbo Wang 31675ca6d0 Recover from poisoned WAL handles on remote file systems (#15193)
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
2026-09-30 04:40:00 -07:00
Josh Kang ff29163e47 Fix GCC range-loop warning in table test (#15292)
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
2026-09-29 18:58:04 -07:00
Josh Kang 176d7c7ea1 Add full external table support and crash testing (#15271)
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
2026-09-29 17:57:17 -07:00
Omkar Gawde b4e068e1ed Reuse MANIFEST batch traversal after apply errors (#15288)
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
2026-09-29 17:31:56 -07:00
Xingbo Wang eed2f96676 Wait for in-flight secondary read-view reclamation (#15289)
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
2026-09-29 16:47:03 -07:00
Omkar Gawde 5fa16200aa Release MANIFEST writers after batch-build failures (#15286)
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
2026-09-29 11:59:49 -07:00
Omkar Gawde 7510773057 Fix ClipColumnFamily key lifetime and atomic-flush wait alignment (#15273)
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
2026-09-28 14:31:40 -07:00
xingbowang 5eed79a0e1 Scale container shared memory by runner core count (#15284)
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
2026-09-28 14:10:43 -07:00
Omkar Gawde 3d381e1bd4 Fix heap-use-after-free in DBSecondaryTest.SecondaryCloseFiles Env teardown (#15283)
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
2026-09-28 13:06:36 -07:00
Steph Pontikes f8d3141e5c Thread column family names to table readers and add ExternalTableFactory recovery (#15265)
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
2026-09-28 12:04:07 -07:00
Jay Huh 4052fccde9 Enable SecondaryDB in CF-consistency crash tests (#15270)
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
2026-09-25 16:27:49 -07:00
Xingbo Wang ac30c4d738 Add stress scan witness diagnostics (#15199)
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
2026-09-25 16:20:54 -07:00
Josh Kang dbc2db5228 Make external table APIs more consistent with native tables (#15241)
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
2026-09-24 11:46:13 -07:00
Xingbo Wang 07cbacadb4 Avoid materializing huge SSTs in size-boundary test (#15264)
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
2026-09-24 09:46:56 -07:00
Ranjan Banerjee 662cdbc9ec Update RocksDB main for 11.11 release (#15263)
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
2026-09-23 21:51:29 -07:00
Jay Huh ffd4ac31af Make SecondaryDB multi-CF iterator reads non-blocking (#15259)
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
2026-09-23 13:09:24 -07:00
Omkar Gawde 0675830967 Verify prefetched block checksums with retry-safe cleanup (#15254)
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
2026-09-23 11:34:25 -07:00
Xingbo Wang 14a82121d4 Add shared diff and pull request summary guidance (#15261)
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
2026-09-23 08:44:55 -07:00
Andrei Preda 507206e46b Fix stale readahead trim prefix on SeekToFirst (#15216)
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
2026-09-21 11:28:32 -07:00
zaidoon 3c30d43baf Promote IndexFactory as UDI SPI (#14962)
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
2026-09-21 09:28:49 -07:00
Denis Yaroshevskiy 96e7ef0269 Use memcpy instead of strncpy in Status::CopyState to speed up Status copies (#15238)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/15238

Reviewed By: MatzeB

Differential Revision: D116157932

fbshipit-source-id: 56f52f7e5a92154be2be256151e4cc6f63bbbc05
2026-09-21 05:17:50 -07:00
Xingbo Wang 5d6f321b70 Document behavioral compatibility review requirements (#15218)
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
2026-09-18 11:28:27 -07:00
Josh Kang 256a699be3 Support MultiGet and global seqnos in external tables (#15237)
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
2026-09-18 10:39:07 -07:00
Omkar Gawde 7530287bde Stop over-counting limited SstFileDumper reads (#15228)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15228

**What**: Correct the limited-read count and add regression coverage for recompression source-read errors.

**How**:
- `ReadSequential()` checks `read_num_limit` before consuming and counting the next entry.
- `RecompressReturnsSourceReadError` injects data-block read failures and verifies `ShowAllCompressionSizes()` returns the error.

**Why**: `sst_dump --read_num` reported one extra key, while the recompression input-materialization error path lacked regression coverage.

Reviewed By: joshkang97

Differential Revision: D120182510

fbshipit-source-id: d3b07794c9f7b74c29577242ad64c9c1f4a081cf
2026-09-17 15:31:21 -07:00
Peter Dillinger b03a058d61 Persist more SST table properties for compression analysis (#15231)
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
2026-09-17 13:18:07 -07:00
Xingbo Wang 8108b49e0c Add db_stress operation breadcrumbs (#15194)
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
2026-09-17 13:01:13 -07:00
Anand Ananthabhotla dd54c5abc9 Log DB session id for file deletion and manifest creation (#15229)
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
2026-09-17 12:46:43 -07:00
Omkar Gawde 7d721f398b Reject reopening an active SstFileWriter (#15232)
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
2026-09-17 11:33:08 -07:00
Jay Huh bcf8711165 Add per-column-family ReadOptions to NewIterators (#15230)
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
2026-09-17 11:26:24 -07:00
Peter Dillinger 34d5074bfa Cross-key coalescing for lazy wide-column blob reads (#15178)
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
2026-09-16 18:10:21 -07:00
Jay Huh 79b58b0dd6 Keep secondary multi-CF iterators consistent (#15211)
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
2026-09-16 15:27:11 -07:00
Omkar Gawde 613a642b5c Fix null dereferences in SstFileDumper error paths (#15215)
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
2026-09-15 15:54:16 -07:00
Peter Dillinger e231c1826c Revamp sst_dump recompress for serious benchmarking (#15195)
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
2026-09-15 12:40:17 -07:00
Peter Dillinger 1419ae41b1 Add Compressor::MaybeOverrideCompressionOptions() omnibus hook (#15177)
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
2026-09-15 10:38:56 -07:00
Peter Dillinger db114777b1 Optimize Slice::difference_offset with word-wise shared-prefix scan (#15198)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/15198

Reviewed By: cnli87

Differential Revision: D119236575

fbshipit-source-id: e8bd20b0a1bbc04414883a08c5463b4087d08e3d
2026-09-14 14:10:13 -07:00
zaidoon 7a90dc5ba8 Add built-in index factory wrappers (#14961)
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
2026-09-11 16:37:12 -07:00
Xingbo Wang a4e298b8c4 Validate block entry bounds in release builds (#15200)
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
2026-09-11 13:36:51 -07:00
Ranjan Banerjee 576dc39899 Fix destination type check in lossless_cast for pointers (#15188)
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
2026-09-11 08:31:56 -07:00
Xingbo Wang 4c2a977aa5 Track newer snapshot versions in RocksDB metadata reads (#15086)
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
2026-09-11 04:44:59 -07:00
Omkar Gawde 88a8fdf849 Compare excluded-file lookup against the engine it searched (#15197)
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
2026-09-11 04:35:03 -07:00
Josh Kang a844cbf5b7 Add coroutine reads for SstFileReader (#15189)
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
2026-09-10 14:56:12 -07:00
Omkar Gawde 3d8e4be2f5 Fix SstFileReader heap corruption past MAX_BATCH_SIZE keys (#15196)
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
2026-09-10 12:18:35 -07:00
Xingbo Wang 7d3150ffce Report RocksDB benchmark interval latency and perf metrics (#15101)
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
2026-09-10 08:08:38 -07:00
Jay Huh 91b96f41cf Retry the OpenAndCompact secondary open when the primary replaces CURRENT (#15179)
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
2026-09-09 19:32:00 -07:00
Omkar Gawde dfde6a60b0 Fix out-of-bounds read in SstFileWriter::DeleteRange
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
2026-09-09 19:26:02 -07:00
Peter Dillinger bcda8c4b1a Fix Cache::ApplyToAllEntries ignoring average_entries_per_lock (#15186)
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
2026-09-09 09:41:06 -07:00
Josh Kang 37234200b5 Handle benign io_uring stderr in crash tests (#15181)
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
2026-09-04 12:22:30 -07:00
Josh Kang ac0f4ae69f Isolate coroutine statistics across async reads (#15142)
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
2026-09-04 11:19:16 -07:00
Jay Huh bec9dcec7c Make db_stress actually tolerate transient remote IO errors (#15170)
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
2026-09-03 10:43:54 -07:00
Jay Huh 897162a6bc Add session-scoped temporary directory for remote compaction (#15169)
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
2026-09-02 21:46:24 -07:00
rocksdb-release-bot 1ca38e1442 Update version to 11.11.0 for 11.10 release (#15157)
Summary:
- Bump version.h from 11.10.0 to 11.11.0
- Add `11.10.fb` to `check_format_compatible.sh`
- Sync HISTORY.md `## 11.10.` section from `11.10.fb`
- Delete 13 consumed `unreleased_history/` note file(s) from main

Part of 11.10 release workflow.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15157

Reviewed By: jaykorean

Differential Revision: D118302742

Pulled By: xingbowang

fbshipit-source-id: 751fb106cc5a4f5dd502c5838c5653278b5a8529
2026-09-02 09:07:42 -07:00
Josh Kang da3a0961fa Add custom read context to ReadOptions (#15173)
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
2026-09-01 18:51:16 -07:00
Josh Kang 96ed085402 Blog post for coroutines (#15143)
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
2026-09-01 15:55:37 -07:00
Peter Dillinger 2c1c7ea2f1 Surface remote cx crash test failures and fix resumption output-dir cleanup (#15168)
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
2026-09-01 12:05:27 -07:00
Anand Ananthabhotla fe6406f7b2 Skip recovery SetNextFile for OPTIONS-only advances (#15163)
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
2026-08-31 20:25:51 -07:00
Anand Ananthabhotla 0df7ed563b Pass SST block CRC32C checksums to file writer (#15155)
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
2026-08-31 17:20:01 -07:00
Xingbo Wang 068aeda9d8 Account partitioned filter reads in PerfContext timing (#15164)
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
2026-08-31 14:49:48 -07:00
Xingbo Wang a0966fd94e Validate remote compaction snapshot ordering (#15160)
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
2026-08-31 12:56:55 -07:00
Ranjan Banerjee fe5123681e Fix GetEntity snapshot isolation for WritePrepared transactions (#15136)
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
2026-08-31 12:45:16 -07:00
zaidoon f9c117e203 Add optional UDI builder protocols (#14960)
Summary:
Part 3 of 13 in the UDI split.

Stack order:
1. https://github.com/facebook/rocksdb/issues/14954 Add IndexFactory compatibility names
2. https://github.com/facebook/rocksdb/issues/14959 Add UDI index mode API vocabulary
3. https://github.com/facebook/rocksdb/issues/14960 Add optional UDI builder protocols
4. https://github.com/facebook/rocksdb/issues/14961 Add built-in index factory wrappers
5. https://github.com/facebook/rocksdb/issues/14969 Add built-in index factory tests
6. https://github.com/facebook/rocksdb/issues/14962 Promote IndexFactory as UDI SPI
7. https://github.com/facebook/rocksdb/issues/14963 Wire IndexFactory through block-based tables
8. https://github.com/facebook/rocksdb/issues/14964 Add IndexFactory table routing tests
9. https://github.com/facebook/rocksdb/issues/14970 Add IndexFactory parallel routing tests
10. https://github.com/facebook/rocksdb/issues/14965 Account UDI blocks as index blocks
11. https://github.com/facebook/rocksdb/issues/14966 Support parallel AddIndexEntry in trie index
12. https://github.com/facebook/rocksdb/issues/14967 Cover trie index modes
13. https://github.com/facebook/rocksdb/issues/14968 Add IndexFactory stress and benchmark flags

Previous: https://github.com/facebook/rocksdb/issues/14959.
Next: https://github.com/facebook/rocksdb/issues/14961.

Depends on https://github.com/facebook/rocksdb/issues/14959. Until https://github.com/facebook/rocksdb/issues/14954 and https://github.com/facebook/rocksdb/issues/14959 land, GitHub shows the cumulative diff against main. After those land, the intended review diff is:

`zaidoon/udi-02-index-mode-api..zaidoon/udi-03-index-builder-protocols`

That final review delta is 1 file with 62 insertions.

What changed:
- Add optional UserDefinedIndexBuilder protocols with default no-op behavior.
- Keep the API additive. Runtime wiring comes in later PRs.

Validation:
- `git diff --check`
- `make check-sources`

Pull Request resolved: https://github.com/facebook/rocksdb/pull/14960

Reviewed By: jaykorean

Differential Revision: D117536541

Pulled By: xingbowang

fbshipit-source-id: affe3e49e7185af6cfdf85314bb6f2e002c84c08
2026-08-31 09:26:43 -07:00
xingbowang 82dfd6a510 Fix Folly build due to libaio dead download link (#15159)
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
2026-08-28 09:01:50 -07:00
Anand Ananthabhotla 80a5a6e4b2 Revert PR 15106 (Ignore OPTIONS files for manifest recovery advance) (#15158)
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
2026-08-27 21:08:29 -07:00
Omkar Gawde d699ffbd5c Support partial atomic range replacement (#15154)
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
2026-08-26 21:52:40 -07:00
Peter Dillinger e4a7136d3e Preferred-phase scheduling for periodic (time-based) compaction
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
2026-08-26 20:14:26 -07:00
Peter Dillinger f7b791b213 Fix bottommost-files infinite compaction loop with preserve/preclude (#15146)
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
2026-08-26 19:44:47 -07:00
Josh Kang 6dbb6f30e6 Avoid restoring coroutine stats on foreign threads (#15145)
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
2026-08-25 13:00:55 -07:00
Kunal Pathak bd16531142 Hoist top-level filter-partition index iterator out of MultiGet per-key loop (#15137)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/15137

Reviewed By: pdillinger

Differential Revision: D115489931

fbshipit-source-id: 9d5b607c834a6e8754d55619309ec3705094cc76
2026-08-25 09:13:49 -07:00
Xingbo Wang e6a2ee0bd2 Fix subset checkpoints with incomplete MANIFEST tails (#15123)
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
2026-08-22 12:17:33 -07:00
Andrei Preda ad81b654a4 Prune resolved 2PC recovery state on secondaries (#15121)
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
2026-08-21 11:39:01 -07:00
Peter Dillinger 8f492b2f47 Store common user-key prefix once per block (format_version 8) (#15117)
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
2026-08-20 17:32:41 -07:00
Peter Dillinger e8554f1b11 Faster and safer MANIFEST recovery for remote compaction (OpenAndCompact) (#15133)
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
2026-08-20 16:07:48 -07:00
rocksdb-release-bot 330ded5129 Update version to 11.10.0 for 11.9 release (#15135)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/15135

- Bump version.h from 11.9.0 to 11.10.0
- Add `11.9.fb` to `check_format_compatible.sh`
- Sync HISTORY.md `## 11.9.` section from `11.9.fb`
- Delete 17 consumed `unreleased_history/` note file(s) from main

Part of 11.9 release workflow.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15096

Reviewed By: anand1976

Differential Revision: D116833037

Pulled By: joshkang97

fbshipit-source-id: 04ebda8db5b2c4fdae71a2cf107784b6abdc0f3c
2026-08-20 15:13:31 -07:00
Anand Ananthabhotla 4b41ccd2a7 Fix full_history_ts_low race for long-running snapshots in db_stress (#15129)
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
2026-08-20 12:32:59 -07:00
Ranjan Banerjee 2846163d9e Fix remote compaction input record count mismatch due to uninitialized FileMetaData stats (#15130)
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
2026-08-20 10:54:16 -07:00
Josh Kang 03b8d1afa0 Prevent normal flushes from starving error recovery (#15132)
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
2026-08-20 10:54:15 -07:00
Xingbo Wang 42fca1bb39 Enable core dumps for sanitizer crash tests (#15131)
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
2026-08-20 04:59:40 -07:00
Peter Dillinger d3a82f28ce Optimize SstPartitioner overhead in compaction via ShouldPartitionByPrefix() (#15118)
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
2026-08-19 11:43:10 -07:00
Xingbo Wang d8536d3274 Enable continuous verification for secondary stress tests (#15114)
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
2026-08-19 11:34:01 -07:00
zaidoon 136d0d1fa1 Add UDI index mode API vocabulary (#14959)
Summary:
Part 2 of 13 in the UDI split.

Stack order:
1. https://github.com/facebook/rocksdb/issues/14954 Add IndexFactory compatibility names
2. https://github.com/facebook/rocksdb/issues/14959 Add UDI index mode API vocabulary
3. https://github.com/facebook/rocksdb/issues/14960 Add optional UDI builder protocols
4. https://github.com/facebook/rocksdb/issues/14961 Add built-in index factory wrappers
5. https://github.com/facebook/rocksdb/issues/14969 Add built-in index factory tests
6. https://github.com/facebook/rocksdb/issues/14962 Promote IndexFactory as UDI SPI
7. https://github.com/facebook/rocksdb/issues/14963 Wire IndexFactory through block-based tables
8. https://github.com/facebook/rocksdb/issues/14964 Add IndexFactory table routing tests
9. https://github.com/facebook/rocksdb/issues/14970 Add IndexFactory parallel routing tests
10. https://github.com/facebook/rocksdb/issues/14965 Account UDI blocks as index blocks
11. https://github.com/facebook/rocksdb/issues/14966 Support parallel AddIndexEntry in trie index
12. https://github.com/facebook/rocksdb/issues/14967 Cover trie index modes
13. https://github.com/facebook/rocksdb/issues/14968 Add IndexFactory stress and benchmark flags

Previous: https://github.com/facebook/rocksdb/issues/14954.
Next: https://github.com/facebook/rocksdb/issues/14960.

Depends on https://github.com/facebook/rocksdb/issues/14954. Until https://github.com/facebook/rocksdb/issues/14954 lands, GitHub shows the cumulative diff against main. After that lands, the intended review diff is:

`zaidoon/udi-01-index-factory-compat-shim..zaidoon/udi-02-index-mode-api`

That final review delta is 4 files with 52 insertions.

What changed:
- Add inert IndexMode and ReadIndex vocabulary to the public options headers.
- Block those experimental enum fields from automatic C API generation until the explicit C API mapping lands later in the stack.
- Keep behavior unchanged. Runtime wiring comes in later PRs.

Validation:
- `git diff --check`
- `make check-sources`
- `python3 tools/c_api_gen/verify_generated_up_to_date.py`
- `AUTO_CLEAN=1 make check-c-api-gen`
- `./c_test`

Pull Request resolved: https://github.com/facebook/rocksdb/pull/14959

Reviewed By: jaykorean

Differential Revision: D116211178

Pulled By: xingbowang

fbshipit-source-id: 42edd3f0991d90b0b5c90232e5023de6e3740dac
2026-08-19 10:31:27 -07:00
Anand Ananthabhotla 49faa51e11 Fix crash test Restore() failure with empty trace when replay_write_ops == 0 (#14840)
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
2026-08-18 21:09:30 -07:00
Anand Ananthabhotla 7d18ffe316 Ignore OPTIONS files for manifest recovery advance (#15106)
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
2026-08-18 17:53:22 -07:00
Xingbo Wang db1adf47bb Add bootstrap configuration for RocksDB external tables
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
2026-08-18 10:12:47 -07:00
Xingbo Wang bbdcd2825f Add remote storage liveness crash test signal (#15102)
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
2026-08-17 19:30:37 -07:00
Peter Dillinger 495c21dc5c Unify lazy wide-column blob read verify policy; tool coverage (#15115)
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
2026-08-17 17:06:32 -07:00
Anand Ananthabhotla 575b6df1b8 Force immediate creation of a new MANIFEST if the current one cannot be reused (#15103)
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
2026-08-17 16:18:06 -07:00
Peter Dillinger f0c1e3d7d9 Fix cleanup-on-failure and flaky liveness check in point_lock_manager_stress_test (#15116)
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
2026-08-17 16:01:35 -07:00
Andrei Preda b616b203b8 Drop flushed WAL data from the secondary's active memtable (#15066)
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
2026-08-17 13:47:30 -07:00
Anand Ananthabhotla 92a6308300 Verify compressed block bytes after checksum computation (#15098)
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
2026-08-17 12:34:23 -07:00
Peter Dillinger 021374c62c Start format_version=8 development toward new features (#15104)
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
2026-08-17 12:08:59 -07:00
Josh Kang 9475798205 Refresh recovery plan after error escalation (#15111)
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
2026-08-17 09:04:06 -07:00
Josh Kang 323d915dbc Prevent late async-read stats context restoration (#15100)
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
2026-08-13 22:52:30 -07:00
Peter Dillinger 2dc6bc51b4 Fix TSAN data race on speculative auto_skip_bypassed read (#15094)
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
2026-08-12 15:06:31 -07:00
Peter Dillinger 7217bf34a7 Add SST build context: is_remote_compaction, db_name, kSstFileWriter (#15085)
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
2026-08-12 14:31:47 -07:00
Xingbo Wang 58d835d951 Add db_stress liveness profile and fix lost flush request (#14830)
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
2026-08-11 18:05:08 -07:00
Michael C Huang 276495843d Add blob file writer buffer size option (#15061)
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
2026-08-11 17:38:34 -07:00
Peter Dillinger ec5106b3b8 Fix AI code review hiding details, stale+misleading driver (#15087)
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/31537781229 https://github.com/facebook/rocksdb/pull/15079#issuecomment-5259127037

Reviewed By: xingbowang

Differential Revision: D115635558

Pulled By: pdillinger

fbshipit-source-id: d3ca7188162a086cd42d7b8bd8e60f3fc3df3bad
2026-08-11 17:07:45 -07:00
Andrew Chang f9e4c5c51f Add stat for number of bottom-pri compactions. (#15071)
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
2026-08-11 16:08:45 -07:00
Peter Dillinger fcf382c95c Integrated and ratio-aware compression auto-skip (#15079)
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
2026-08-11 14:29:51 -07:00
Xingbo Wang 66d7035708 Do not test GetEntityLazy on read-only primary handle (#15084)
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
2026-08-11 13:30:57 -07:00
Andrew Chang 9381544cd9 Add num-running-remote-compactions DB property (#15074)
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
2026-08-11 10:10:41 -07:00
Xingbo Wang f8c601b91c Fix io_activity assertion in stress test after MultiGetEntityLazy (#15076)
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
2026-08-11 08:12:46 -07:00
Josh Kang 5dfde1c262 Add coroutine reads to stackable RocksDB utilities (#15080)
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
2026-08-10 20:28:02 -07:00
Xingbo Wang 44e0294c1d Fix remote compaction filter test flake (#15075)
Summary:
- Disable automatic compactions in `CompactionServiceTest.CompactionFilter`.
- Keep the test focused on the explicit `CompactRange()` path that verifies remote compaction filter behavior.
- Avoid a flaky race where background remote compaction can obsolete an input SST before the manual compaction runs.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15075

Test Plan:
- `make format-auto`
- `AUTO_CLEAN=1 COERCE_CONTEXT_SWITCH=1 make -j32 compaction_service_test`
- `tools/gtest_parallel_repro.py --binary ./compaction_service_test --gtest_filter=CompactionServiceTest.CompactionFilter --processes-per-iteration 32 --iteration-count 30 --cpu-count 4 --timeout 120 --stop-on-failure --out /tmp/ws28_compaction_service_flake_fixed_20260809_1`
- `git diff --check`
- `AUTO_CLEAN=1 make check-sources`

Reviewed By: jaykorean

Differential Revision: D115357593

Pulled By: xingbowang

fbshipit-source-id: 9c640b807b8d0c204a23fb805e511b39db69acbc
2026-08-10 15:27:15 -07:00
Xingbo Wang 3f4d840e1a Fix multi-DB checkpoints in legacy regression test (#15078)
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
2026-08-10 14:29:58 -07:00
Andrew Chang 3949208acf Make max_open_files configurable via options_map in OpenAndCompact (#15077)
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
2026-08-10 10:20:08 -07:00
Josh Kang 88752298ff Add native coroutine read dispatch to RocksDB (#15073)
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
2026-08-10 09:43:05 -07:00
Andrew Chang 1dfa729793 Expose number of unscheduled compactions as a statistic (#15060)
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
2026-08-07 13:34:58 -07:00
Peter Dillinger 8e1569b351 Lazy wide-column byte-range (partial) blob reads (#15067)
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
2026-08-07 12:08:03 -07:00
Darkhan Imangaliyev 5fe225357a Extend parallel checkpointing and db_stress coverage for column-family-subset checkpoints (#15059)
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
2026-08-06 16:17:04 -07:00
Xingbo Wang e8bbe08760 Make ARM nightly crash test swap setup idempotent (#15065)
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
2026-08-06 08:23:22 -07:00
Peter Dillinger fd7399004f Add lazy wide-column read API (GetEntityLazy / MultiGetEntityLazy) (#15012)
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
2026-08-05 22:21:40 -07:00
Josh Kang d1fbdffdae Log failed Status values in db_stress assertions (#15054)
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
2026-08-05 19:51:14 -07:00
Josh Kang dc4006480b Tolerate InvalidArgument from full_history_ts_low race when persist_user_defined_timestamps=false (#15052)
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
2026-08-05 17:06:48 -07:00
Peter Dillinger af0eae4de3 Rename TransactionLogIterator to WalIterator and document its behavior (#15056)
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
2026-08-05 13:21:37 -07:00
Omkar Gawde dbf9328c71 Add per-column-family compaction abort and resume (#14982)
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
2026-08-04 13:36:03 -07:00
zaidoon 751bde0f7f Add IndexFactory compatibility names (#14954)
Summary:
Part 1 of 13 in the UDI split.

Stack order:
1. https://github.com/facebook/rocksdb/issues/14954 Add IndexFactory compatibility names
2. https://github.com/facebook/rocksdb/issues/14959 Add UDI index mode API vocabulary
3. https://github.com/facebook/rocksdb/issues/14960 Add optional UDI builder protocols
4. https://github.com/facebook/rocksdb/issues/14961 Add built-in index factory wrappers
5. https://github.com/facebook/rocksdb/issues/14969 Add built-in index factory tests
6. https://github.com/facebook/rocksdb/issues/14962 Promote IndexFactory as UDI SPI
7. https://github.com/facebook/rocksdb/issues/14963 Wire IndexFactory through block-based tables
8. https://github.com/facebook/rocksdb/issues/14964 Add IndexFactory table routing tests
9. https://github.com/facebook/rocksdb/issues/14970 Add IndexFactory parallel routing tests
10. https://github.com/facebook/rocksdb/issues/14965 Account UDI blocks as index blocks
11. https://github.com/facebook/rocksdb/issues/14966 Support parallel AddIndexEntry in trie index
12. https://github.com/facebook/rocksdb/issues/14967 Cover trie index modes
13. https://github.com/facebook/rocksdb/issues/14968 Add IndexFactory stress and benchmark flags

Previous: none.
Next: https://github.com/facebook/rocksdb/issues/14959.

This PR adds `include/rocksdb/index_factory.h` as a source-compatible set of new names over the current experimental UDI API:

- `IndexFactoryBuilder` -> `UserDefinedIndexBuilder`
- `IndexFactoryIterator` -> `UserDefinedIndexIterator`
- `IndexFactoryReader` -> `UserDefinedIndexReader`
- `IndexFactoryOptions` -> `UserDefinedIndexOption`
- `IndexFactory` -> `UserDefinedIndexFactory`
- `kIndexFactoryMetaPrefix` -> `kUserDefinedIndexPrefix`

This intentionally does not change table building, table reading, SST format, OPTIONS parsing, or the C API. Existing `UserDefinedIndexFactory` subclasses that only implement the old `NewBuilder()` and `NewReader(Slice&)` shape still compile and work through the new `IndexFactory` name.

Validation:
- `build_tools/rockstest.sh table_test --gtest_filter='IndexFactoryCompatibilityTest.*'`
- `make check-sources`
- GitHub Actions PR matrix passed on the current head

Pull Request resolved: https://github.com/facebook/rocksdb/pull/14954

Reviewed By: jaykorean

Differential Revision: D114358164

Pulled By: xingbowang

fbshipit-source-id: 5c830dc1f9dfcf11373423a03103a595fd8ad921
2026-08-04 12:26:35 -07:00
Josh Kang 14ecf7f13c Route single-blob reads through BlobFetcher (#15046)
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
2026-08-03 12:59:37 -07:00
Ranjan Banerjee b1b50fe102 Parallel checkpointing stress test (#15037)
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
2026-08-03 11:11:46 -07:00
Ranjan Banerjee cda4a73a19 Fsync destination files in parallel CheckpointEngine copies (#15038)
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
2026-08-03 10:38:26 -07:00
Josh Kang 00e73b99a2 Centralize db_stress validation errors (#15034)
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
2026-08-03 10:33:36 -07:00
Xingbo Wang 460aea1523 Fix C API compatibility baseline selection in CI (#15039)
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
2026-08-03 06:42:26 -07:00
zaidoon 4b35e9966c Add batch-owned pinned MultiGet C API (#14988)
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
2026-08-02 14:13:23 -07:00
Peter Dillinger b7b6d7c8c0 Mirror CI third-party build deps on a GitHub release instead of S3 (#15041)
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
2026-08-01 14:47:28 -07:00
Peter Dillinger afc922bf5f Make ReadOptions::table_filter a caller-owned pointer for cheaper copies (#15005)
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
2026-07-31 10:20:25 -07:00
anand1976 6e73763b8a Update version to 11.9.0 for 11.8 release (#15023)
Summary:
- Bump version.h from 11.8.0 to 11.9.0
- Add `11.8.fb` to `check_format_compatible.sh`
- Sync HISTORY.md `## 11.8.` section from `11.8.fb`
- Delete 5 consumed `unreleased_history/` note file(s) from main

Part of 11.8 release workflow.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/15023

Reviewed By: xingbowang

Differential Revision: D114304679

Pulled By: anand1976

fbshipit-source-id: 6fecd5c5175140dfa1f6ba16aec42748eee91172
2026-07-31 04:30:52 -07:00
Darkhan Imangaliyev f9df618e4d Api to checkpoint only selected set of column families (#15019)
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
2026-07-30 16:16:58 -07:00
Peter Dillinger 026f63de6c Parallelize the non-unit-test checks in make check (#15028)
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
2026-07-30 15:49:09 -07:00
Anand Ananthabhotla e2ccb78cf2 Avoid deletefile test purge/flush deadlock (#15027)
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
2026-07-30 11:25:22 -07:00
Josh Kang c64f682946 Fix IoUringBackend crash in ReadIOExecutor worker threads (#15020)
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
2026-07-30 11:18:08 -07:00
Josh Kang 359d57be96 Fix blob file path misidentification in SstFileManager during compaction (#15021)
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
2026-07-30 10:38:04 -07:00
generatedunixname1395027625275998 ba81144d8d Force-inline xxph3 mix helpers on the NPHash64/Hash64 hot path (#14981)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/14981

Reviewed By: pdillinger

Differential Revision: D112965953

fbshipit-source-id: 9645d9287a9aba987bf1b37752df7f9dcb3a9669
2026-07-29 14:21:15 -07:00
Anand Ananthabhotla 4b9070bccb Defer obsolete OPTIONS file deletion on open (#15004)
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
2026-07-29 12:44:27 -07:00
Josh Kang 7247edbf0a Add opt-in stats for async RocksDB reads (#15017)
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
2026-07-28 09:32:26 -07:00
Ranjan Banerjee f5c0c3e274 Record CheckpointEngine copy bytes under checkpoint tickers (#14997)
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
2026-07-28 09:23:58 -07:00
Xingbo Wang 2fc58a678b Fix MultiScanPrepare fallback after io_uring init failure (#15016)
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
2026-07-27 16:15:50 -07:00
Josh Kang a4645e864d Implement Get/MultiGet coroutine (#15014)
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
2026-07-27 15:09:34 -07:00
Josh Kang 039c45075e Add db_stress coroutine read flag (#15013)
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
2026-07-27 13:22:07 -07:00
Xingbo Wang fbd446cebb Disable open_read_only_one_in when mmap_read is enabled (#15011)
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
2026-07-27 11:00:39 -07:00
Xingbo Wang 6f5587ae2d Fix GetCorruptionStatus() crash with separated KV blocks (#15006)
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
2026-07-27 10:14:36 -07:00
Peter Dillinger c73841572a Simplify ReadOptions plumbing for read-path blob fetchers (#15003)
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
2026-07-26 21:05:16 -07:00
Xingbo Wang 14a7646fb5 Fix PinnableSlice move construction from pinned slices (#14998)
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
2026-07-25 14:18:27 -07:00
Xingbo Wang 03c3c090fb Measure IODispatcher async read latency (#15001)
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
2026-07-24 13:51:09 -07:00
Jay Huh 71d3bd1773 Fix heap-buffer-overflow(read) parsing a corrupted data-block hash index (#14993)
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
2026-07-24 10:42:15 -07:00
Peter Dillinger bb77d17072 Make Flush() optionally wait for OnFlushCompleted (FlushOptions::listener_wait) (#14992)
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
2026-07-24 09:36:04 -07:00
Peter Dillinger 1eb4a6e3e1 Rocksdb Secondary Cache Test failed: assertion failed - corruption: corrupted atomic group The file (#15000)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/15000

Reviewed By: xingbowang

Differential Revision: D113225538

Pulled By: pdillinger

fbshipit-source-id: fea9a671e9291ed030c5396264734195b2fb78bf
2026-07-24 08:30:27 -07:00
Ranjan Banerjee 19482988e3 Add parallel CheckpointEngine [4/4] (#14948)
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
2026-07-23 20:58:25 -07:00
Ranjan Banerjee 0c0bfbe8be Add hard-link support to CopyEngine [3/4] (#14947)
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
2026-07-23 19:27:36 -07:00
Ranjan Banerjee fac47c3072 Extract shared CopyEngine [2/4] (#14946) (#14946)
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
2026-07-23 19:07:17 -07:00
Anusha Mukka f56112abb7 Plumb Env::IOActivity into DBImpl::ResumeImpl (#14952)
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
2026-07-23 18:43:39 -07:00
Peter Dillinger 744f120b12 Zero-copy reads of embedded (same-file) blob wide-column entities (#14990)
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
2026-07-23 16:15:49 -07:00
Peter Dillinger 0347b4df61 Add db_bench benchmarks for embedded-blob and wide-column entity reads (#14995)
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
2026-07-23 16:01:07 -07:00
Peter Dillinger 755c104bca Unify clang detection for the C API codegen and add make regen-c-api (#14994)
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
2026-07-23 15:50:16 -07:00
Ranjan Banerjee 0a2ba2a164 Add shared backup/checkpoint option bases [1/4] (#14945)
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
2026-07-23 14:41:33 -07:00
Jay Huh dcb5c2bf53 Restore MultiScan prefetch stats dropped in the IODispatcher refactor (#14991)
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
2026-07-23 14:09:07 -07:00
Jay Huh 7c385f4441 Add license headers to generated C API fragments (#14985)
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
2026-07-23 12:19:02 -07:00
Josh Kang 2c77c32ac9 Add Get and MultiGet sync/async plumbing (#14934)
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
2026-07-23 09:56:30 -07:00
Andrew Chang 2b628ccfe5 Preserve fast_sst_open metadata across MANIFEST rotation (#14987)
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
2026-07-22 16:01:44 -07:00
Andrew Chang 0745290ca6 Add OpenAndCompact DB open latency histogram (#14986)
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
2026-07-22 15:57:50 -07:00
Peter Dillinger ffa95117b9 Bug fix and clean up error/status handling for wide-column + blob support (#14980)
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
2026-07-22 14:29:35 -07:00
Peter Dillinger 30ba4908ba Enforce value_size_soft_limit for direct-write blob values in MultiGet (#14983)
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
2026-07-22 09:52:19 -07:00
Jay Huh 144deb248a Harden MaybeOpenReadOnlyOnPrimary stress test: fix TSAN lock-limit crash + drop false-positive read-only Get (#14979)
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
2026-07-21 16:57:11 -07:00
Jay Huh 990827e916 db_bench: tolerate remote IO errors by skipping ops and continuing (#14971)
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
2026-07-21 08:15:35 -07:00
Hui Xiao 7c4bf11044 CPU corruption injector: robust seed handling, per-run command logging, and doc fixes (#14940)
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
2026-07-21 07:37:03 -07:00
Peter Dillinger 81ea677072 PinnableWideColumns support for >1 backing buffer to avoid re-serialization (#14943)
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
2026-07-20 16:32:42 -07:00
Jay Huh 04cad97e7e Stress test concurrent primary and read-only DB on the same directory (#14938)
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
2026-07-20 15:23:12 -07:00
anand1976 865568398a Update version to 11.8.0 for 11.7 release (#14950)
Summary:
- Bump version.h from 11.7.0 to 11.8.0
- Add `11.7.fb` to `check_format_compatible.sh`

Part of 11.7 release workflow.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/14950

Reviewed By: rban1

Differential Revision: D112627463

Pulled By: anand1976

fbshipit-source-id: 0685a7a7a45c157c2a7e392420e3e16d5a364727
2026-07-20 11:02:10 -07:00
Jay Huh 2809ce9d0f Tolerate remote IO errors in db_stress crash test and db_bench (#14942)
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
2026-07-17 18:57:46 -07:00
Josh Kang 223b5ee13d Fix data race in VerifyPkSkFast between virtual call and TransactionDB destruction (#14930)
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
2026-07-17 11:52:34 -07:00
Jay Huh 6d8370ec56 cmake: discover gtest tests at ctest time to fix macOS CMake CI build (#14951)
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
2026-07-17 09:09:51 -07:00
Laurynas Biveinis d461606f0f Make range-lock waits interruptible with SetIsKilledCallback (#14927)
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
2026-07-15 21:21:45 -07:00
Peter Dillinger c9536aaef7 Fix quadratic point-in-time MANIFEST recovery in VersionBuilder (#14929)
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
2026-07-15 13:34:03 -07:00
Jay Huh cf22ee8f5b Harden RocksDB PR comment artifact downloads (#14941)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14941

Download GitHub Actions artifacts into `_artifact` before reading comment result files so untrusted artifacts cannot overwrite checked-out workflow helper scripts. Keep `post-pr-comment.js` loaded only from `.github/scripts` and remove unnecessary inherited secrets from the review-comment wrapper workflows.

Reviewed By: pdillinger

Differential Revision: D112129548

fbshipit-source-id: c807eab2197018b0672a5e1d9710e4c6bfcfd0ac
2026-07-15 12:54:46 -07:00
Peter Dillinger c7f7b1b2ae Skip redundant DeleteWalsBefore during recovery when markers already advanced (#14918)
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
2026-07-15 12:12:10 -07:00
Josh Kang dc72a64a5b Flush no-WAL live-file data with LockWAL (#14931)
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
2026-07-15 12:07:38 -07:00
Peter Dillinger 74aba599ab Fix WriteUnprepared transaction rollback after commit write failure (#14920)
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
2026-07-14 16:12:13 -07:00
xingbowang 329d9e7adf Add blog post on Blob Direct Write and partitioned blob files (#14873)
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
2026-07-14 07:31:34 -07:00
Josh Kang 5b1c3b37ac Disable codex reviews on PRs (#14933)
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
2026-07-13 15:10:40 -07:00
Jay Huh eca1a114e0 Count unreleased MultiScan prefetch blocks as wasted (#14926)
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
2026-07-11 08:20:42 -07:00
Hui Xiao 7c80a5ac85 Size-tolerant deserialization for compaction service response's counts array (#14807)
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
2026-07-09 10:37:35 -07:00
Josh Kang 053a9de468 Fix broken github codex review (#14923)
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
2026-07-09 09:55:44 -07:00
Jay Huh f32f747125 Address reverse MultiScan follow-up comments (#14922)
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
2026-07-08 20:56:36 -07:00
generatedunixname1395027625275998 c3f6e4d625 Cache GetKeysEndOffset in BlockIter to speed up Next inner loop (#14917)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/14917

Reviewed By: kunalspathak

Differential Revision: D110209012

fbshipit-source-id: 8ce3b94ea9617977d956f3856d92d50f2b547ddc
2026-07-08 14:03:21 -07:00
Jay Huh e1ea04d9ef Add reverse order support to MultiScan (#14876)
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
2026-07-08 13:58:00 -07:00
anand1976 27b48646cd Update folly hash for 11.6 release (#14912)
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
2026-07-07 18:44:23 -07:00
generatedunixname1395027625275998 66bccb467d Store BySmallestKey by value to avoid shared_ptr atomic copy in Rep (#14919)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/14919

Reviewed By: DenisYaroshevskiy

Differential Revision: D110842287

fbshipit-source-id: df84b4b319aefd9b21abdb7db20989ba1efd7eba
2026-07-07 07:42:34 -07:00
generatedunixname1395027625275998 0f15da1c2d Reserve key_context capacity in MultiGetCommon to avoid autovector reallocs (#14909)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/14909

Reviewed By: kunalspathak

Differential Revision: D110380492

fbshipit-source-id: d1d3214c52a65326de237adcbfe6ea5d23d94960
2026-07-06 19:48:44 -07:00
anand1976 13d55eba73 Update version to 11.7.0 for 11.6 release (#14913)
Summary:
- Bump version.h from 11.6.0 to 11.7.0
- Add `11.6.fb` to `check_format_compatible.sh`
- Sync HISTORY.md `## 11.6.` section from `11.6.fb`
- Delete 5 consumed `unreleased_history/` note file(s) from main

Part of 11.6 release workflow.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/14913

Reviewed By: anand1976

Differential Revision: D110520723

Pulled By: pdillinger

fbshipit-source-id: 20f2f9b4be03c08d78d6f2c4e6b70812a408d9f0
2026-07-02 16:16:22 -07:00
Peter Dillinger 1192897aad Fix table cache entry leak in VersionBuilder::UnrefFile on failed LogAndApply (#14914)
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
2026-07-02 16:08:21 -07:00
Peter Dillinger a40466d963 Three misc CI fixes (#14910)
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
2026-07-01 19:04:40 -07:00
Anand Ananthabhotla a0ad7887c9 Fix null scratch buffer submitted to async read on aligned direct-IO path (#14907)
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
2026-07-01 15:25:12 -07:00
Peter Dillinger 6cceef9497 Bug fixes: surface embedded-blob resolution errors (#14906)
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
2026-07-01 14:05:36 -07:00
generatedunixname1395027625275998 1f7a286413 Fix HyperClockCache naive Lookup fast-path chain indexing to skip full fallback (#14901)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/14901

Reviewed By: anand1976

Differential Revision: D109906106

fbshipit-source-id: 660a47f51250415286af4f9ddcb0c40410ef04cd
2026-07-01 13:45:18 -07:00
Xingbo Wang de7b8e8f7d Add RocksDB flush reason and atomic flush request metrics (#14898)
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
2026-07-01 10:34:16 -07:00
Peter Dillinger 741539da9b Fix backward iteration over embedded blob SSTs (#14902)
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
2026-07-01 10:02:52 -07:00
Hui Xiao 7e2223f4ab CPU corruption injector: randomize non-preset db_stress flags (#14867)
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
2026-06-30 20:54:30 -07:00
Hui Xiao eddfee8f56 CPU corruption injector: runner + db_stress preset flags (#14866)
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
2026-06-30 20:38:13 -07:00
Hui Xiao 3745f2e234 CPU corruption injector: gdb register flip into one db_stress op (#14858)
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
2026-06-30 19:34:21 -07:00
Hui Xiao 0f493506d9 Verify CPU corruption after op via --verify_cpu_corruption_dir (#14852)
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
2026-06-30 18:54:53 -07:00
Anand Ananthabhotla 779c7b58d1 Disable db_stress backward scan with embedded blob SSTs (#14896)
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
2026-06-29 22:46:48 -07:00
Anand Ananthabhotla ffb7788d45 Disable backward scan in stress test when separate_key_value_in_data_block is enabled (#14889)
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
2026-06-29 17:55:04 -07:00
Xingbo Wang 75c1ffd4e3 Add RocksDB rate limiter I/O usage metrics (#14884)
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
2026-06-29 16:22:57 -07:00
Peter Dillinger f6fabdb64f Improve build and test experience for Make builds (#14883)
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
2026-06-29 14:39:13 -07:00
Anand Ananthabhotla 8fae7ff39d Fix use-after-free in EmbeddedBlobResolvingIterator when key() called before value()
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
2026-06-29 10:27:39 -07:00
Jay Huh 43d60bfe9e Revert reverse-comparator handling in range tree lock manager (#14887)
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
2026-06-25 14:16:51 -07:00
Peter Dillinger 8c0790bdf8 Refresh platform010 build dependencies and remove TBB (#14882)
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
2026-06-25 10:05:45 -07:00
Peter Dillinger a004c2d850 Add experimental embedded blob SST support (#14851)
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
2026-06-25 09:29:50 -07:00
Xingbo Wang fe053a26aa Macos CI fix for jdk dependency issue (#14885)
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
2026-06-25 08:38:32 -07:00
Josh Kang 8ba4204b28 Do not allow read only DBs to delete obsolete files (#14881)
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
2026-06-24 16:04:52 -07:00
Jay Huh b0ecf86f6e Fix range lock manager assertion crash on reverse-comparator CF point locks (#14880)
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
2026-06-24 13:46:52 -07:00
zaidoon 1cec28d82d Finish C API code generation (continues #14572) (#14868)
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
2026-06-24 10:45:42 -07:00
Peter Dillinger 30295a4b92 Fix db_stress rollback retry exhaustion under concurrent Resume Busy (#14877)
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
2026-06-23 15:12:23 -07:00
Xingbo Wang 1f20db7a31 Add FIFO KV-ratio compaction blog post (#14871)
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
2026-06-23 05:54:51 -07:00
Josh Kang f8987bc7b9 Include PR and version for range tombstone blog post (#14875)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/14875

title

Reviewed By: pdillinger

Differential Revision: D109353179

fbshipit-source-id: c14c9ecbd29a8118b1d0f0689e2f29c162bd15e8
2026-06-22 16:07:39 -07:00
Maciej Szeszko 30f42e00ba fix package fault injection log parser in db stress fbpkg (#14874)
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
2026-06-22 14:05:47 -07:00
Josh Kang 6ccfbd4a86 Blog post for range tombstone conversion (#14862)
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
2026-06-22 12:04:16 -07:00
Xingbo Wang 5b324a2160 Fix CI: detect Windows MSVC toolchain (#14869)
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
2026-06-22 09:22:13 -07:00
Maciej Szeszko 02941016d0 Fix seqno sync race with WriteUnprepared (#14864)
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
2026-06-20 22:57:11 -07:00
Maciej Szeszko 2d13302949 Fix Windows CI: run build-windows-vs2022 on windows-2022 runner (#14872)
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
2026-06-20 22:07:08 -07:00
Xingbo Wang 214869aacd Persist fault injection logs and fail fast on expected-state trace writes (#14651)
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
2026-06-20 17:20:14 -07:00
anand1976 5bf78183db Update version to 11.6.0 for 11.5 release (#14861)
Summary:
- Bump version.h from 11.5.0 to 11.6.0
- Add `11.5.fb` to `check_format_compatible.sh`
- Cherry-pick HISTORY.md from `11.5.fb`
- Update folly hash in `folly.mk`
- Delete `unreleased_history/` note files

Part of 11.5 release workflow.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/14861

Reviewed By: anand1976

Differential Revision: D108971785

Pulled By: jaykorean

fbshipit-source-id: d4076d34f860c9caddfac38a5406e02112436cc2
2026-06-17 21:05:15 -07:00
Josh Kang e292aa95b3 Allow skipping lmax index and filter block prefetches for external file ingestion
Summary:
Bulk external file ingestion can spend significant commit time prefetching index/filter blocks for large Lmax files. This adds `IngestExternalFileOptions::prefetch_lmax_index_and_filter_blocks` (default true) so bulk-load callers can defer that metadata prefetch when the cache doesn't need immediate warming. The option threads through `FileMetaData::skip_index_and_filter_blocks_prefetch` to drive the existing `LoadTableHandlers()` prefetch logic. Benchmarks show ~40% commit latency reduction (3459 → 2112 μs) and ~55% lower IO time (2560 → 1145 μs) when disabled.

Benchmarks:

```
db_bench --benchmarks=ingestexternalfile --num=2200000 --ingest_external_file_num_batches=1 --ingest_external_file_batch_size=1 --ingest_external_file_use_file_info=true --ingest_external_file_fill_cache=true --cache_index_and_filter_blocks=true --bloom_bits=10 --statistics=true --stats_level=3 --ingest_external_file_prefetch_lmax_index_and_filter_blocks=<true|false>
```

| `ingest_external_file_prefetch_lmax_index_and_filter_blocks` | `rocksdb.ingest.external.file.run.micros` | `rocksdb.table.open.io.micros` | index/filter cache adds | last-level read bytes |
| `true` | `3459 us` | `2560 us` | `1 / 1` | `3551559` |
| `false` | `2112 us` | `1145 us` | `0 / 0` | `1333` |

Reviewed By: xingbowang

Differential Revision: D108678511

fbshipit-source-id: e8951e3aad36cc0accffb33ecc3cc1b4aeb89459
2026-06-16 11:20:07 -07:00
1077 changed files with 181059 additions and 25617 deletions

No files matched your search

@@ -1,9 +0,0 @@
name: install-jdk8-on-macos
runs:
using: composite
steps:
- name: Install JDK 8 on macos
run: |-
HOMEBREW_NO_AUTO_UPDATE=1 brew tap bell-sw/liberica
HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask liberica-jdk8
shell: bash
-38
View File
@@ -1,38 +0,0 @@
name: post-steps
description: Steps that are taken after a RocksDB job
inputs:
artifact-prefix:
description: Prefix to append to the name of artifacts that are uploaded
required: true
default: "${{ github.job }}"
runs:
using: composite
steps:
- name: Upload Test Results artifact
uses: actions/upload-artifact@v4.0.0
with:
name: "${{ inputs.artifact-prefix }}-test-results"
path: "${{ runner.temp }}/test-results/**"
- name: Upload DB LOG file artifact
uses: actions/upload-artifact@v4.0.0
with:
name: "${{ inputs.artifact-prefix }}-db-log-file"
path: LOG
- name: Copy Test Logs (on Failure)
if: ${{ failure() }}
run: |
mkdir -p ${{ runner.temp }}/failure-test-logs
cp -r t/* ${{ runner.temp }}/failure-test-logs
shell: bash
- name: Upload Test Logs (on Failure) artifact
uses: actions/upload-artifact@v4.0.0
with:
name: "${{ inputs.artifact-prefix }}-failure-test-logs"
path: ${{ runner.temp }}/failure-test-logs/**
if-no-files-found: ignore
- name: Upload Core Dumps artifact
uses: actions/upload-artifact@v4.0.0
with:
name: "${{ inputs.artifact-prefix }}-core-dumps"
path: "core.*"
if-no-files-found: ignore
+21 -7
View File
@@ -2,12 +2,21 @@ name: pre-steps
runs:
using: composite
steps:
- name: Dump ulimits for diagnostics
- name: Dump system info for diagnostics
run: |
echo "=== ulimits (soft) ==="
ulimit -a -S || true
echo "=== ulimits (hard) ==="
ulimit -a -H || true
echo "=== OS ==="
uname -a || true
echo "=== CPU ==="
if [ "$RUNNER_OS" = "macOS" ]; then
sysctl machdep.cpu.brand_string hw.ncpu || true
else
lscpu || true
grep -m1 '^microcode' /proc/cpuinfo || true
fi
shell: bash
- name: Install lld linker for faster builds
run: apt-get update -y && apt-get install -y lld 2>/dev/null || true
@@ -15,14 +24,19 @@ runs:
- name: Setup Environment Variables
run: |-
echo "GTEST_THROW_ON_FAILURE=0" >> "$GITHUB_ENV"
echo "GTEST_OUTPUT=\"xml:${{ runner.temp }}/test-results/\"" >> "$GITHUB_ENV"
echo "SKIP_FORMAT_BUCK_CHECKS=1" >> "$GITHUB_ENV"
echo "GTEST_COLOR=1" >> "$GITHUB_ENV"
echo "CTEST_OUTPUT_ON_FAILURE=1" >> "$GITHUB_ENV"
echo "CTEST_TEST_TIMEOUT=300" >> "$GITHUB_ENV"
echo "ZLIB_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zlib" >> "$GITHUB_ENV"
echo "BZIP2_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/bzip2" >> "$GITHUB_ENV"
echo "SNAPPY_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/snappy" >> "$GITHUB_ENV"
echo "LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> "$GITHUB_ENV"
echo "ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> "$GITHUB_ENV"
# These third-party sources are mirrored on the "third-party-deps" GitHub
# release. To add one, upload the asset here (intentionally hidden):
# https://github.com/meta/rocksdb/releases/edit/third-party-deps
# Note how file names are generated below (vs. from the original source)
# before uploading. Removing or modifying files will break builds for
# older releases/revisions (not found or checksum mismatch).
echo "ZLIB_DOWNLOAD_PREFIX=https://github.com/meta/rocksdb/releases/download/third-party-deps/zlib-" >> "$GITHUB_ENV"
echo "BZIP2_DOWNLOAD_PREFIX=https://github.com/meta/rocksdb/releases/download/third-party-deps/bzip2-" >> "$GITHUB_ENV"
echo "SNAPPY_DOWNLOAD_PREFIX=https://github.com/meta/rocksdb/releases/download/third-party-deps/snappy-" >> "$GITHUB_ENV"
echo "LZ4_DOWNLOAD_PREFIX=https://github.com/meta/rocksdb/releases/download/third-party-deps/lz4-" >> "$GITHUB_ENV"
echo "ZSTD_DOWNLOAD_PREFIX=https://github.com/meta/rocksdb/releases/download/third-party-deps/zstd-" >> "$GITHUB_ENV"
shell: bash
@@ -0,0 +1,17 @@
name: setup-jdk-on-macos
runs:
using: composite
steps:
- name: Set up JDK on macos
run: |-
set -euo pipefail
JAVA_HOME="$(/usr/libexec/java_home -v 21)"
echo "JAVA_HOME=${JAVA_HOME}" >> "$GITHUB_ENV"
echo "JAVAC_ARGS=--release 8" >> "$GITHUB_ENV"
echo "${JAVA_HOME}/bin" >> "$GITHUB_PATH"
echo "Using JDK at ${JAVA_HOME}"
"${JAVA_HOME}/bin/java" -version
"${JAVA_HOME}/bin/javac" -version
shell: bash
+1 -1
View File
@@ -8,7 +8,7 @@ runs:
run: chown `whoami` . || true
shell: bash
- name: Set upstream
run: git remote add upstream https://github.com/facebook/rocksdb.git
run: git remote add upstream https://github.com/meta/rocksdb.git
shell: bash
- name: Fetch upstream
run: git fetch upstream
+56 -7
View File
@@ -11,8 +11,49 @@ inputs:
runs:
using: composite
steps:
- name: Detect Visual Studio generator
id: detect_vs
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$vswhere = Join-Path ([Environment]::GetFolderPath("ProgramFilesX86")) "Microsoft Visual Studio\Installer\vswhere.exe"
if (!(Test-Path $vswhere)) {
throw "vswhere.exe was not found at $vswhere"
}
$vsInstallationsJson = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -format json -utf8
$vsInstallations = @($vsInstallationsJson | ConvertFrom-Json)
if ($vsInstallations.Count -eq 0) {
throw "No Visual Studio instance with C++ tools was found."
}
$vsInstallation = $vsInstallations[0]
$vsMajor = [int]$vsInstallation.catalog.productLineVersion
if ($vsMajor -eq 0) {
$vsMajor = [int]($vsInstallation.installationVersion.Split(".")[0])
}
$generatorLine = cmake --help | Where-Object {
$_ -match "^\*?\s*Visual Studio $vsMajor\s+\d{4}\s*="
} | Select-Object -First 1
if (!$generatorLine) {
throw "CMake does not support a Visual Studio generator for VS major version $vsMajor."
}
$cmakeGenerator = ($generatorLine -replace "^\*?\s*", "" -replace "\s*=.*$", "").Trim()
$vsVersionRange = "[{0}.0,{1}.0)" -f $vsMajor, ($vsMajor + 1)
echo "Detected Visual Studio at $($vsInstallation.installationPath)"
echo "Using CMake generator: $cmakeGenerator"
echo "Using setup-msbuild vs-version: $vsVersionRange"
"CMAKE_GENERATOR=$cmakeGenerator" >> $env:GITHUB_ENV
"vs_version=$vsVersionRange" >> $env:GITHUB_OUTPUT
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.3.1
uses: microsoft/setup-msbuild@v2
with:
vs-version: ${{ steps.detect_vs.outputs.vs_version }}
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
@@ -32,15 +73,23 @@ runs:
CMAKE_HOME: C:/Program Files/CMake
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
SNAPPY_HOME: ${{ github.workspace }}/thirdparty/snappy-1.2.2
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.2.2;${{ github.workspace }}/thirdparty/snappy-1.2.2/build
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.2.2/build/Debug/snappy.lib
run: |-
# NOTE: if ... Exit $LASTEXITCODE lines needed to exit and report failure
echo ===================== Install Dependencies =====================
choco install liberica8jdk -y
if(!$?) { Exit $LASTEXITCODE }
if (!$env:JAVA_HOME -or !(Test-Path (Join-Path $env:JAVA_HOME "bin\javac.exe"))) {
$javac = Get-Command javac -ErrorAction SilentlyContinue
if (!$javac) {
Write-Error "javac was not found on PATH and JAVA_HOME is not set."
Exit 1
}
$env:JAVA_HOME = Split-Path (Split-Path $javac.Source -Parent) -Parent
}
echo "JAVA_HOME=$env:JAVA_HOME"
& "$env:JAVA_HOME\bin\java.exe" -version
& "$env:JAVA_HOME\bin\javac.exe" -version
mkdir $Env:THIRDPARTY_HOME
cd $Env:THIRDPARTY_HOME
echo "Building Snappy dependency..."
@@ -53,11 +102,11 @@ runs:
cd build
& cmake -G "$Env:CMAKE_GENERATOR" .. -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF
if(!$?) { Exit $LASTEXITCODE }
msbuild Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
cmake --build . --config Debug --parallel 32 -- /p:Platform=x64
if(!$?) { Exit $LASTEXITCODE }
echo ======================== Build RocksDB =========================
cd ${{ github.workspace }}
$env:Path = $env:JAVA_HOME + ";" + $env:Path
$env:Path = "$env:JAVA_HOME\bin;" + $env:Path
mkdir build
cd build
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DWIN_CI=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DXPRESS=1 -DJNI=1 ..
@@ -65,7 +114,7 @@ runs:
cd ..
echo "Building with VS version: $Env:CMAKE_GENERATOR"
# use more parallel processes than the number of processes available, as most of the compile command would be cache hit
msbuild build/rocksdb.sln /m:32 /p:LinkIncremental=false -property:Configuration=Debug -property:Platform=x64
cmake --build build --config Debug --parallel 32 -- /p:LinkIncremental=false /p:Platform=x64
if(!$?) { Exit $LASTEXITCODE }
echo ========================= Test RocksDB =========================
$suiteRun = "${{ inputs.suite-run }}"
+33 -16
View File
@@ -5,23 +5,40 @@
// return build({ icon, headerTitle, triggerLine, responseBody, footerLines
// });
// GitHub rejects issue comments longer than this many characters. The model
// response (responseBody) is the only unbounded input, so we trim it to fit
// while keeping the fixed scaffolding (header, trigger, footer) intact.
const MAX_COMMENT_LENGTH = 65536;
const TRUNCATION_NOTE =
'\n\n*[Truncated — full output in execution log artifact]*';
module.exports = function buildAiReviewComment(
{icon, headerTitle, triggerLine, responseBody, footerLines}) {
return [
`## ${icon} ${headerTitle}`,
'',
triggerLine,
'',
'---',
'',
responseBody,
'',
'---',
'',
'<details>',
'<summary>ℹ️ About this response</summary>',
'',
...footerLines,
'</details>',
const assemble = (body) => [`## ${icon} ${headerTitle}`,
'',
triggerLine,
'',
'---',
'',
body,
'',
'---',
'',
'<details>',
'<summary>ℹ️ About this response</summary>',
'',
...footerLines,
'</details>',
].join('\n');
const body = responseBody || '';
const comment = assemble(body);
if (comment.length <= MAX_COMMENT_LENGTH) {
return comment;
}
// Drop only as much of the body as needed to fit, then re-assemble so the
// footer/<details> block is preserved rather than cut off mid-comment.
const overflow = comment.length - MAX_COMMENT_LENGTH;
const keep = Math.max(0, body.length - overflow - TRUNCATION_NOTE.length);
return assemble(body.substring(0, keep) + TRUNCATION_NOTE);
};
+1 -1
View File
@@ -103,7 +103,7 @@ module.exports = function parseClaude({executionFile, conclusion, meta}) {
responseBody,
footerLines: [
'Generated by [Claude Code](https://github.com/anthropics/claude-code-action).',
'Review methodology: `claude_md/code_review.md`',
'Review methodology: `claude_md/ci_review_prompt.md`',
'',
'**Limitations:**',
'- Claude may miss context from files not in the diff',
-98
View File
@@ -1,98 +0,0 @@
// Parse Codex review artifacts and produce a markdown review comment.
//
// Usage from actions/github-script:
// const parse = require('./.github/scripts/parse-codex-review.js');
// const markdown = parse({ responseFile, recoveryFile, findingsFile, logFile,
// exitCode, meta });
//
// Parameters:
// responseFile - path to Codex final response output
// recoveryFile - path to recovery output formatted from review-findings.md
// findingsFile - path to incremental findings file written during review
// logFile - path to Codex stdout/stderr log
// exitCode - Codex process exit code
// meta - { trigger, autoMode, headSha, reviewer, isQuery, isPartial }
const fs = require('fs');
const buildComment = require('./build-ai-review-comment.js');
module.exports = function parseCodex(
{responseFile, recoveryFile, findingsFile, logFile, exitCode, meta}) {
function getTriggerLine() {
if (meta.trigger !== 'auto') {
return `*Requested by @${meta.reviewer}*`;
}
const shortSha = meta.headSha ? meta.headSha.substring(0, 7) : 'unknown';
if (meta.autoMode === 'early') {
return `*Auto-triggered after CI reached the early-review threshold — reviewing commit ${
shortSha}*`;
}
return `*Auto-triggered after CI passed — reviewing commit ${shortSha}*`;
}
function readIfPresent(path) {
if (!path || !fs.existsSync(path)) {
return '';
}
const text = fs.readFileSync(path, 'utf8').trim();
return text;
}
function tailFile(path, maxChars = 12000) {
const text = readIfPresent(path);
if (!text) {
return '';
}
if (text.length <= maxChars) {
return text;
}
return text.slice(text.length - maxChars);
}
let responseBody = '';
const recovered = readIfPresent(recoveryFile);
const direct = readIfPresent(responseFile);
const findings = readIfPresent(findingsFile);
if (recovered) {
responseBody = recovered;
} else if (direct) {
responseBody = direct;
} else if (findings) {
responseBody =
'⚠️ **Review incomplete — Codex did not produce a final response.**\n\n' +
'Below are the incremental findings recovered from `review-findings.md`.\n\n---\n\n' +
findings;
} else {
const logTail = tailFile(logFile);
responseBody = logTail ?
`❌ **Codex review failed before producing findings.**\n\n\`\`\`\n${
logTail}\n\`\`\`` :
'❌ Codex review failed before producing any output.';
}
const isPartial = !!meta.isPartial;
const code = Number.parseInt(exitCode || '1', 10);
const icon = isPartial ? '🟡' : (code === 0 ? '✅' : '⚠️');
const triggerLine = getTriggerLine();
return buildComment({
icon,
headerTitle: meta.isQuery ? 'Codex Response' : 'Codex Code Review',
triggerLine,
responseBody,
footerLines: [
'Generated by Codex CLI.',
'Review methodology: `claude_md/code_review.md`',
'',
'**Limitations:**',
'- Codex may miss context from files not in the diff',
'- Large PRs may be truncated',
'- Always apply human judgment to AI suggestions',
'',
'**Commands:**',
'- `/codex-review [context]` — Request a code review',
'- `/codex-query <question>` — Ask about the PR or codebase',
],
});
};
+1 -1
View File
@@ -117,7 +117,7 @@ function assertObsoleteComment(comment, originalBody) {
const context = {
repo: {
owner: 'facebook',
owner: 'meta',
repo: 'rocksdb',
},
};
+5 -426
View File
@@ -1,18 +1,8 @@
# Shared analysis workflow for AI PR reviews.
#
# This workflow is invoked by provider-specific wrappers (Claude/Codex) so the
# event triggers and workflow names stay user-friendly while the implementation
# lives in one place.
#
# Security note for Codex:
# Codex runs in non-interactive CI, so the Codex CLI steps below pass
# `--dangerously-bypass-approvals-and-sandbox`. That disables Codex's
# approval prompts and sandbox protections, so treat those steps as
# arbitrary command execution on the runner. This file therefore keeps Codex
# in the read-only analysis workflow, restricts the pull_request_target path
# to same-repo PRs, and leaves write-scoped PR commenting in a separate
# workflow. Re-evaluate that trust boundary before expanding Codex auto
# review to broader trigger sources.
# This workflow is invoked by provider-specific wrappers (such as
# claude-review.yml) so the event triggers and workflow names stay
# user-friendly while the implementation lives in one place.
name: AI Review Analysis
@@ -20,7 +10,7 @@ on:
workflow_call:
inputs:
provider:
description: Provider key, for example "claude" or "codex"
description: Provider key, for example "claude"
required: true
type: string
display_name:
@@ -119,7 +109,7 @@ jobs:
const FAIL_CONCLUSIONS = new Set([
'failure', 'cancelled', 'timed_out', 'action_required'
]);
const AI_CHECK_TOKENS = ['claude', 'codex'];
const AI_CHECK_TOKENS = ['claude'];
const MIN_CHECKS = 10;
const THRESHOLD = 0.5;
const POLL_INTERVAL_MS = 90 * 1000;
@@ -419,24 +409,6 @@ jobs:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
if: >-
steps.pr_info.outputs.skip != 'true' &&
steps.check_existing.outputs.skip != 'true' &&
inputs.provider == 'codex'
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Codex CLI
if: >-
steps.pr_info.outputs.skip != 'true' &&
steps.check_existing.outputs.skip != 'true' &&
inputs.provider == 'codex'
run: |
npm install -g @openai/codex
codex --version
- name: Generate diff and prompt
if: steps.pr_info.outputs.skip != 'true' && steps.check_existing.outputs.skip != 'true'
env:
@@ -541,72 +513,6 @@ jobs:
core.setOutput('label', label);
core.setOutput('tokens', budget);
- name: Classify PR complexity (Codex)
if: >-
steps.pr_info.outputs.skip != 'true' &&
steps.check_existing.outputs.skip != 'true' &&
inputs.provider == 'codex'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CODEX_HOME: /tmp/codex-home
CODEX_MODEL: ${{ inputs.default_model }}
run: |
if [ -z "${OPENAI_API_KEY}" ]; then
echo "OPENAI_API_KEY not configured, skipping Codex review"
printf 'complex\n' > codex-complexity-output.txt
exit 0
fi
mkdir -p "${CODEX_HOME}"
set +e
codex exec \
-m "${CODEX_MODEL}" \
-c model_reasoning_effort="medium" \
--dangerously-bypass-approvals-and-sandbox \
--output-last-message codex-complexity-output.txt \
- < /tmp/complexity_prompt.txt > codex-complexity.log 2>&1
exit_code=$?
set -e
if [ "${exit_code}" -ne 0 ]; then
echo "Codex complexity classification failed with exit code ${exit_code}"
echo "Defaulting to complex review budget"
if [ -s codex-complexity.log ]; then
echo "Last 100 lines of codex-complexity.log:"
tail -100 codex-complexity.log
fi
printf 'complex\n' > codex-complexity-output.txt
fi
- name: Pick thinking budget (Codex)
if: >-
steps.pr_info.outputs.skip != 'true' &&
steps.check_existing.outputs.skip != 'true' &&
inputs.provider == 'codex'
id: budget_codex
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
let label = 'complex';
try {
const text = fs.readFileSync(
'codex-complexity-output.txt',
'utf8'
).trim().toLowerCase();
if (/\bsimple\b/.test(text) && !/\bcomplex\b/.test(text)) {
label = 'simple';
}
} catch (error) {
core.warning(
`Could not parse classifier output: ${error.message}`);
}
const effort = label === 'simple' ? 'high' : 'xhigh';
core.info(
`Complexity classified as: ${label} ` +
`-> model_reasoning_effort=${effort}`
);
core.setOutput('label', label);
core.setOutput('effort', effort);
- name: Run Claude
if: >-
steps.pr_info.outputs.skip != 'true' &&
@@ -703,103 +609,6 @@ jobs:
});
fs.writeFileSync(process.env.COMMENT_FILE, body);
- name: Run Codex
if: >-
steps.pr_info.outputs.skip != 'true' &&
steps.check_existing.outputs.skip != 'true' &&
inputs.provider == 'codex'
id: codex
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CODEX_HOME: /tmp/codex-home
CODEX_MODEL: ${{ inputs.default_model }}
CODEX_REASONING_EFFORT: ${{ steps.budget_codex.outputs.effort }}
BASE_SHA: ${{ steps.pr_info.outputs.base_sha }}
PR_TITLE: ${{ steps.pr_info.outputs.pr_title }}
run: |
if [ -z "${OPENAI_API_KEY}" ]; then
echo "OPENAI_API_KEY not configured, skipping Codex review"
exit 0
fi
mkdir -p "${CODEX_HOME}"
set +e
codex exec review \
--base "${BASE_SHA}" \
--title "${PR_TITLE}" \
-m "${CODEX_MODEL}" \
-c model_reasoning_effort="${CODEX_REASONING_EFFORT}" \
--dangerously-bypass-approvals-and-sandbox \
--output-last-message codex-review-output.md \
- < /tmp/prompt.txt > codex-review-execution.log 2>&1
exit_code=$?
set -e
echo "exit_code=${exit_code}" >> "${GITHUB_OUTPUT}"
echo "${exit_code}" > codex-review-exit-code.txt
- name: Recover partial review (Codex)
if: >-
steps.pr_info.outputs.skip != 'true' &&
steps.check_existing.outputs.skip != 'true' &&
inputs.provider == 'codex' &&
steps.codex.outputs.exit_code != '0' &&
hashFiles('review-findings.md') != ''
id: recover_codex
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CODEX_HOME: /tmp/codex-home
CODEX_MODEL: ${{ inputs.default_model }}
CODEX_REASONING_EFFORT: ${{ steps.budget_codex.outputs.effort }}
run: |
set +e
codex exec \
-m "${CODEX_MODEL}" \
-c model_reasoning_effort="${CODEX_REASONING_EFFORT}" \
--dangerously-bypass-approvals-and-sandbox \
--output-last-message codex-review-recovery.md \
- < claude_md/ci_recovery_prompt.md > codex-review-recovery.log 2>&1
exit_code=$?
set -e
echo "exit_code=${exit_code}" >> "${GITHUB_OUTPUT}"
- name: Build review comment (Codex)
if: >-
steps.pr_info.outputs.skip != 'true' &&
steps.check_existing.outputs.skip != 'true' &&
inputs.provider == 'codex'
env:
RESPONSE_FILE: codex-review-output.md
RECOVERY_FILE: codex-review-recovery.md
FINDINGS_FILE: review-findings.md
LOG_FILE: codex-review-execution.log
EXIT_CODE: ${{ steps.codex.outputs.exit_code }}
HEAD_SHA: ${{ steps.pr_info.outputs.head_sha }}
AUTO_MODE: ${{ steps.pr_info.outputs.auto_mode }}
USED_RECOVERY: ${{ steps.recover_codex.outputs.exit_code == '0' && 'true' || 'false' }}
COMMENT_FILE: ${{ inputs.comment_file }}
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const parse = require('./.github/scripts/parse-codex-review.js');
const body = parse({
responseFile: process.env.RESPONSE_FILE,
recoveryFile: process.env.RECOVERY_FILE,
findingsFile: process.env.FINDINGS_FILE,
logFile: process.env.LOG_FILE,
exitCode: process.env.EXIT_CODE,
meta: {
trigger: 'auto',
autoMode: process.env.AUTO_MODE,
headSha: process.env.HEAD_SHA,
reviewer: '',
isQuery: false,
isPartial: process.env.USED_RECOVERY === 'true' ||
(!fs.existsSync(process.env.RESPONSE_FILE) &&
fs.existsSync(process.env.FINDINGS_FILE)),
}
});
fs.writeFileSync(process.env.COMMENT_FILE, body);
- name: Save PR number
if: steps.pr_info.outputs.skip != 'true' && steps.check_existing.outputs.skip != 'true'
run: |
@@ -850,27 +659,6 @@ jobs:
retention-days: 7
if-no-files-found: ignore
- name: Upload review logs (Codex)
if: >-
always() &&
steps.pr_info.outputs.skip != 'true' &&
steps.check_existing.outputs.skip != 'true' &&
inputs.provider == 'codex'
uses: actions/upload-artifact@v4
with:
name: codex-review-logs
path: |
codex-review-execution.log
codex-complexity.log
codex-complexity-output.txt
codex-review-recovery.log
codex-review-output.md
codex-review-recovery.md
review-findings.md
codex-review-exit-code.txt
retention-days: 7
if-no-files-found: ignore
manual-review:
name: Manual ${{ inputs.display_name }} Review
runs-on: ubuntu-latest
@@ -994,18 +782,6 @@ jobs:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
if: steps.request.outputs.skip != 'true' && inputs.provider == 'codex'
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Codex CLI
if: steps.request.outputs.skip != 'true' && inputs.provider == 'codex'
run: |
npm install -g @openai/codex
codex --version
- name: Generate diff and prompt
if: steps.request.outputs.skip != 'true'
env:
@@ -1157,89 +933,6 @@ jobs:
core.setOutput('label', label);
core.setOutput('tokens', budget);
- name: Classify PR complexity (Codex)
if: >-
steps.request.outputs.skip != 'true' &&
steps.request.outputs.type == 'review' &&
inputs.provider == 'codex'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CODEX_HOME: /tmp/codex-home
CODEX_MODEL: ${{ inputs.selected_model != '' && inputs.selected_model || inputs.default_model }}
run: |
if [ -z "${OPENAI_API_KEY}" ]; then
echo "OPENAI_API_KEY not configured, skipping Codex review"
printf 'complex\n' > codex-complexity-output.txt
exit 0
fi
mkdir -p "${CODEX_HOME}"
set +e
codex exec \
-m "${CODEX_MODEL}" \
-c model_reasoning_effort="medium" \
--dangerously-bypass-approvals-and-sandbox \
--output-last-message codex-complexity-output.txt \
- < /tmp/complexity_prompt.txt > codex-complexity.log 2>&1
exit_code=$?
set -e
if [ "${exit_code}" -ne 0 ]; then
echo "Codex complexity classification failed with exit code ${exit_code}"
echo "Defaulting to complex review budget"
if [ -s codex-complexity.log ]; then
echo "Last 100 lines of codex-complexity.log:"
tail -100 codex-complexity.log
fi
printf 'complex\n' > codex-complexity-output.txt
fi
- name: Pick thinking budget (Codex)
if: steps.request.outputs.skip != 'true' && inputs.provider == 'codex'
id: manual_budget_codex
env:
OVERRIDE: ${{ inputs.thinking_budget }}
COMMAND_TYPE: ${{ steps.request.outputs.type }}
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
if (process.env.COMMAND_TYPE !== 'review') {
core.setOutput('label', 'query');
core.setOutput('effort', 'medium');
return;
}
const override = (process.env.OVERRIDE || '').trim();
if (override && /^\d+$/.test(override)) {
const budget = parseInt(override, 10);
const effort = budget >= 32000 ? 'xhigh' : 'high';
core.info(
`Using thinking_budget override: ${override} ` +
`-> model_reasoning_effort=${effort}`
);
core.setOutput('label', 'override');
core.setOutput('effort', effort);
return;
}
let label = 'complex';
try {
const text = fs.readFileSync(
'codex-complexity-output.txt',
'utf8'
).trim().toLowerCase();
if (/\bsimple\b/.test(text) && !/\bcomplex\b/.test(text)) {
label = 'simple';
}
} catch (error) {
core.warning(
`Could not parse classifier output: ${error.message}`);
}
const effort = label === 'simple' ? 'high' : 'xhigh';
core.info(
`Complexity classified as: ${label} ` +
`-> model_reasoning_effort=${effort}`
);
core.setOutput('label', label);
core.setOutput('effort', effort);
- name: Run Claude
if: steps.request.outputs.skip != 'true' && inputs.provider == 'claude'
id: manual_claude
@@ -1332,103 +1025,6 @@ jobs:
});
fs.writeFileSync(process.env.COMMENT_FILE, body);
- name: Run Codex
if: steps.request.outputs.skip != 'true' && inputs.provider == 'codex'
id: manual_codex
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CODEX_HOME: /tmp/codex-home
CODEX_MODEL: ${{ inputs.selected_model != '' && inputs.selected_model || inputs.default_model }}
CODEX_REASONING_EFFORT: ${{ steps.manual_budget_codex.outputs.effort }}
BASE_SHA: ${{ steps.pr_info.outputs.base_sha }}
PR_TITLE: ${{ steps.pr_info.outputs.pr_title }}
COMMAND_TYPE: ${{ steps.request.outputs.type }}
run: |
mkdir -p "${CODEX_HOME}"
set +e
if [ "${COMMAND_TYPE}" = "query" ]; then
codex exec \
-m "${CODEX_MODEL}" \
-c model_reasoning_effort="${CODEX_REASONING_EFFORT}" \
--dangerously-bypass-approvals-and-sandbox \
--output-last-message codex-review-output.md \
- < /tmp/prompt.txt > codex-review-execution.log 2>&1
else
codex exec review \
--base "${BASE_SHA}" \
--title "${PR_TITLE}" \
-m "${CODEX_MODEL}" \
-c model_reasoning_effort="${CODEX_REASONING_EFFORT}" \
--dangerously-bypass-approvals-and-sandbox \
--output-last-message codex-review-output.md \
- < /tmp/prompt.txt > codex-review-execution.log 2>&1
fi
exit_code=$?
set -e
echo "exit_code=${exit_code}" >> "${GITHUB_OUTPUT}"
echo "${exit_code}" > codex-review-exit-code.txt
- name: Recover partial review (Codex)
if: >-
steps.request.outputs.skip != 'true' &&
inputs.provider == 'codex' &&
steps.request.outputs.type == 'review' &&
steps.manual_codex.outputs.exit_code != '0' &&
hashFiles('review-findings.md') != ''
id: manual_recover_codex
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CODEX_HOME: /tmp/codex-home
CODEX_MODEL: ${{ inputs.selected_model != '' && inputs.selected_model || inputs.default_model }}
CODEX_REASONING_EFFORT: ${{ steps.manual_budget_codex.outputs.effort }}
run: |
set +e
codex exec \
-m "${CODEX_MODEL}" \
-c model_reasoning_effort="${CODEX_REASONING_EFFORT}" \
--dangerously-bypass-approvals-and-sandbox \
--output-last-message codex-review-recovery.md \
- < claude_md/ci_recovery_prompt.md > codex-review-recovery.log 2>&1
exit_code=$?
set -e
echo "exit_code=${exit_code}" >> "${GITHUB_OUTPUT}"
- name: Build review comment (Codex)
if: steps.request.outputs.skip != 'true' && inputs.provider == 'codex'
env:
RESPONSE_FILE: codex-review-output.md
RECOVERY_FILE: codex-review-recovery.md
FINDINGS_FILE: review-findings.md
LOG_FILE: codex-review-execution.log
EXIT_CODE: ${{ steps.manual_codex.outputs.exit_code }}
HEAD_SHA: ${{ steps.pr_info.outputs.head_sha }}
REVIEWER: ${{ steps.request.outputs.reviewer }}
COMMAND_TYPE: ${{ steps.request.outputs.type }}
USED_RECOVERY: ${{ steps.manual_recover_codex.outputs.exit_code == '0' && 'true' || 'false' }}
COMMENT_FILE: ${{ inputs.comment_file }}
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const parse = require('./.github/scripts/parse-codex-review.js');
const body = parse({
responseFile: process.env.RESPONSE_FILE,
recoveryFile: process.env.RECOVERY_FILE,
findingsFile: process.env.FINDINGS_FILE,
logFile: process.env.LOG_FILE,
exitCode: process.env.EXIT_CODE,
meta: {
trigger: 'manual',
headSha: process.env.HEAD_SHA,
reviewer: process.env.REVIEWER,
isQuery: process.env.COMMAND_TYPE !== 'review',
isPartial: process.env.USED_RECOVERY === 'true' ||
(!fs.existsSync(process.env.RESPONSE_FILE) &&
fs.existsSync(process.env.FINDINGS_FILE)),
}
});
fs.writeFileSync(process.env.COMMENT_FILE, body);
- name: Save PR number
if: steps.request.outputs.skip != 'true'
run: |
@@ -1482,20 +1078,3 @@ jobs:
path: ${{ steps.manual_format_recovery_claude.outputs.execution_file }}
retention-days: 7
if-no-files-found: ignore
- name: Upload review logs (Codex)
if: always() && steps.request.outputs.skip != 'true' && inputs.provider == 'codex'
uses: actions/upload-artifact@v4
with:
name: codex-review-logs
path: |
codex-review-execution.log
codex-complexity.log
codex-complexity-output.txt
codex-review-recovery.log
codex-review-output.md
codex-review-recovery.md
review-findings.md
codex-review-exit-code.txt
retention-days: 7
if-no-files-found: ignore
+35 -16
View File
@@ -6,7 +6,7 @@ on:
workflow_call:
inputs:
provider:
description: Provider key, for example "claude" or "codex"
description: Provider key, for example "claude"
required: true
type: string
result_artifact_name:
@@ -40,6 +40,7 @@ jobs:
name: ${{ inputs.result_artifact_name }}
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: _artifact
continue-on-error: true
- name: Post or update PR comment
@@ -52,25 +53,36 @@ jobs:
with:
script: |
const fs = require('fs');
if (!fs.existsSync(process.env.COMMENT_FILE) ||
!fs.existsSync('pr_number.txt')) {
const path = require('path');
const artifactDir = '_artifact';
const commentFile = path.join(
artifactDir,
process.env.COMMENT_FILE
);
const prNumberFile = path.join(artifactDir, 'pr_number.txt');
const triggerTypeFile = path.join(artifactDir, 'trigger_type.txt');
const headShaFile = path.join(artifactDir, 'head_sha.txt');
if (!fs.existsSync(commentFile) ||
!fs.existsSync(prNumberFile)) {
core.info('No review results found; skipping.');
return;
}
const post = require('./.github/scripts/post-pr-comment.js');
const provider = process.env.PROVIDER;
const providerTitle = provider === 'codex' ? 'Codex' : 'Claude';
const body = fs.readFileSync(process.env.COMMENT_FILE, 'utf8');
const providerTitle =
provider.charAt(0).toUpperCase() + provider.slice(1);
const body = fs.readFileSync(commentFile, 'utf8');
const prNumber = parseInt(
fs.readFileSync('pr_number.txt', 'utf8').trim(),
fs.readFileSync(prNumberFile, 'utf8').trim(),
10
);
const trigger = fs.existsSync('trigger_type.txt') ?
fs.readFileSync('trigger_type.txt', 'utf8').trim() :
const trigger = fs.existsSync(triggerTypeFile) ?
fs.readFileSync(triggerTypeFile, 'utf8').trim() :
'auto';
const headSha = fs.existsSync('head_sha.txt') ?
fs.readFileSync('head_sha.txt', 'utf8').trim() :
const headSha = fs.existsSync(headShaFile) ?
fs.readFileSync(headShaFile, 'utf8').trim() :
'';
const shortSha = headSha ? headSha.substring(0, 7) : '';
const runId = process.env.RUN_ID;
@@ -119,12 +131,16 @@ jobs:
with:
script: |
const fs = require('fs');
if (!fs.existsSync('trigger_type.txt')) return;
const trigger = fs.readFileSync('trigger_type.txt', 'utf8').trim();
const path = require('path');
const artifactDir = '_artifact';
const triggerTypeFile = path.join(artifactDir, 'trigger_type.txt');
const commentIdFile = path.join(artifactDir, 'comment_id.txt');
if (!fs.existsSync(triggerTypeFile)) return;
const trigger = fs.readFileSync(triggerTypeFile, 'utf8').trim();
if (trigger !== 'manual') return;
if (!fs.existsSync('comment_id.txt')) return;
if (!fs.existsSync(commentIdFile)) return;
const commentId = parseInt(
fs.readFileSync('comment_id.txt', 'utf8').trim(),
fs.readFileSync(commentIdFile, 'utf8').trim(),
10
);
if (!commentId) return;
@@ -147,6 +163,7 @@ jobs:
name: ${{ inputs.result_artifact_name }}
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: _artifact
continue-on-error: true
- name: React with failure
@@ -155,9 +172,11 @@ jobs:
with:
script: |
const fs = require('fs');
if (!fs.existsSync('comment_id.txt')) return;
const path = require('path');
const commentIdFile = path.join('_artifact', 'comment_id.txt');
if (!fs.existsSync(commentIdFile)) return;
const commentId = parseInt(
fs.readFileSync('comment_id.txt', 'utf8').trim(),
fs.readFileSync(commentIdFile, 'utf8').trim(),
10
);
if (!commentId) return;
+2 -2
View File
@@ -1,4 +1,4 @@
name: facebook/rocksdb/benchmark-linux
name: meta/rocksdb/benchmark-linux
on: workflow_dispatch
permissions: {}
# FIXME: Disabled temporarily
@@ -6,7 +6,7 @@ permissions: {}
# - cron: 7 */2 * * * # At minute 7 past every 2nd hour
jobs:
benchmark-linux:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on: ubuntu-latest # FIXME: change this back to self-hosted when ready
steps:
- uses: actions/checkout@v4.1.0
+11 -3
View File
@@ -25,6 +25,7 @@ jobs:
name: clang-tidy-result
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: _artifact
continue-on-error: true
- name: Post or update PR comment
@@ -33,12 +34,19 @@ jobs:
with:
script: |
const fs = require('fs');
if (!fs.existsSync('clang-tidy-comment.md') || !fs.existsSync('pr_number.txt')) {
if (!fs.existsSync('_artifact/clang-tidy-comment.md') ||
!fs.existsSync('_artifact/pr_number.txt')) {
core.info('No clang-tidy results found; skipping.');
return;
}
const body = fs.readFileSync('clang-tidy-comment.md', 'utf8');
const prNumber = parseInt(fs.readFileSync('pr_number.txt', 'utf8').trim());
const body = fs.readFileSync(
'_artifact/clang-tidy-comment.md',
'utf8'
);
const prNumber = parseInt(
fs.readFileSync('_artifact/pr_number.txt', 'utf8').trim(),
10
);
const post = require('./.github/scripts/post-pr-comment.js');
await post({
+3 -2
View File
@@ -7,11 +7,12 @@ permissions: {}
jobs:
clang-tidy:
if: github.repository_owner == 'facebook'
if: github.repository_owner == 'meta'
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
image: ghcr.io/meta/rocksdb_ubuntu:24.1
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
with:
@@ -22,4 +22,3 @@ jobs:
provider: claude
result_artifact_name: claude-review-result
comment_file: claude-review-comment.md
secrets: inherit
+1 -1
View File
@@ -8,7 +8,7 @@ name: Claude Code Review
on:
workflow_run:
workflows: ["facebook/rocksdb/pr-jobs"]
workflows: ["meta/rocksdb/pr-jobs"]
types: [completed]
# The early pull_request_target path is limited to same-repo PRs by the job
@@ -1,25 +0,0 @@
# Codex Code Review — Comment Posting Workflow
#
# Thin wrapper around .github/workflows/ai-review-comment.yml so the provider
# specific workflow name stays stable while the shared implementation lives in
# one reusable workflow.
name: Post Codex Review Comment
on:
workflow_run:
workflows: ["Codex Code Review"]
types: [completed]
permissions:
pull-requests: write
issues: write
jobs:
review-comment:
uses: ./.github/workflows/ai-review-comment.yml
with:
provider: codex
result_artifact_name: codex-review-result
comment_file: codex-review-comment.md
secrets: inherit
-68
View File
@@ -1,68 +0,0 @@
# Codex Code Review — Analysis Workflow
#
# Thin wrapper around .github/workflows/ai-review-analysis.yml so provider
# specific triggers and workflow_dispatch inputs stay stable while the shared
# implementation lives in one reusable workflow.
name: Codex Code Review
on:
workflow_run:
workflows: ["facebook/rocksdb/pr-jobs"]
types: [completed]
# The early pull_request_target path is limited to same-repo PRs by the job
# condition below. Fork PRs skip this path and rely on workflow_run instead.
pull_request_target:
types: [opened, reopened, synchronize]
issue_comment:
types: [created]
workflow_dispatch:
inputs:
pr_number:
description: PR number to review
required: true
type: number
model:
description: Codex model to use
required: false
type: choice
options:
- gpt-5.5
- gpt-5.3-codex
- gpt-5.2-codex
default: gpt-5.5
thinking_budget:
description: Override MAX_THINKING_TOKENS (blank = auto-classify)
required: false
type: string
default: ""
permissions:
contents: read
# The shared analysis workflow polls check state, inspects prior runs, and
# reads PR metadata. Keep those permissions read-only in the caller.
actions: read
checks: read
pull-requests: read
jobs:
review:
if: >-
github.event_name != 'pull_request_target' ||
github.event.pull_request.head.repo.full_name == github.repository
uses: ./.github/workflows/ai-review-analysis.yml
with:
provider: codex
display_name: Codex
review_command: /codex-review
query_command: /codex-query
result_artifact_name: codex-review-result
comment_file: codex-review-comment.md
default_model: gpt-5.5
selected_model: ${{ github.event_name == 'workflow_dispatch' && inputs.model || '' }}
thinking_budget: ${{ github.event_name == 'workflow_dispatch' && inputs.thinking_budget || '' }}
dispatch_pr_number: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || '' }}
secrets: inherit
+3 -4
View File
@@ -1,19 +1,18 @@
name: facebook/rocksdb/nightly
name: meta/rocksdb/nightly-candidate
on: workflow_dispatch
permissions: {}
jobs:
# These jobs would be in nightly but are failing or otherwise broken for
# some reason.
build-linux-arm-test-full:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: arm64large
container:
image: ubuntu-2004:202111-02
options: --shm-size=16gb
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/install-gflags"
- run: make V=1 J=4 -j4 check
- uses: "./.github/actions/post-steps"
+94 -42
View File
@@ -1,4 +1,4 @@
name: facebook/rocksdb/nightly
name: meta/rocksdb/nightly
on:
schedule:
- cron: 0 9 * * *
@@ -6,12 +6,12 @@ on:
permissions: {}
jobs:
build-format-compatible:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
with:
@@ -26,28 +26,44 @@ jobs:
mkdir /dev/shm/rocksdb
git config --global --add safe.directory /__w/rocksdb/rocksdb
tools/check_format_compatible.sh
- uses: "./.github/actions/post-steps"
- name: Check that old facebook/rocksdb URLs still redirect
# Alerts the RocksDB team if GitHub stops redirecting the pre-transfer
# repo URL to its current location (or the old name gets taken by
# another repo). Existing releases (e.g. dependency download URLs in
# java/Makefile), existing clones, and external links still use the old
# URL, so they would all break.
if: ${{ !cancelled() }}
run: |-
if ! result=$(curl -sSL --retry 3 --retry-all-errors -o /dev/null \
-w '%{http_code} %{url_effective}' https://github.com/facebook/rocksdb); then
echo "::error::Failed to fetch https://github.com/facebook/rocksdb (network problem?)"
exit 1
fi
echo "Old URL resolved to: $result"
if [ "$result" != "200 https://github.com/meta/rocksdb" ]; then
echo "::error::https://github.com/facebook/rocksdb no longer redirects to https://github.com/meta/rocksdb. Builds of existing releases, existing clones, and external links that use the old URL are likely broken."
exit 1
fi
build-linux-non-shm:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
env:
TEST_TMPDIR: "/tmp/rocksdb_test_tmp"
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: make V=1 -j32 check
- uses: "./.github/actions/post-steps"
build-linux-clang-21-asan-ubsan-with-folly:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:24.1
options: --shm-size=32gb
env:
CC: clang-21
CXX: clang++-21
@@ -77,14 +93,13 @@ jobs:
make build_folly
shell: bash
- run: LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
- uses: "./.github/actions/post-steps"
build-linux-cmake-with-folly:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -96,14 +111,13 @@ jobs:
with:
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make VERBOSE=1 -j20 && ctest -j20)"
- uses: "./.github/actions/post-steps"
build-linux-release-with-folly:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -112,18 +126,51 @@ jobs:
- run: "DEBUG_LEVEL=0 make -j20 build_folly"
- run: "USE_FOLLY=1 LIB_MODE=static DEBUG_LEVEL=0 V=1 make -j20 release"
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 -DCMAKE_BUILD_TYPE=Release .. && make VERBOSE=1 -j20 && ctest -j20)"
- uses: "./.github/actions/post-steps"
build-windows-vs2022-avx2:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: windows-2022
build-windows-msvc-avx2:
if: ${{ github.repository_owner == 'meta' }}
runs-on: windows-8-core
env:
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: AVX2
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/windows-build-steps"
build-macos-cmake:
if: ${{ github.repository_owner == 'meta' }}
runs-on: macos-15-xlarge
strategy:
matrix:
run_sharded_tests: [0, 1, 2, 3]
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
- uses: "./.github/actions/setup-ccache"
with:
cache-key-prefix: macos-cmake
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/pre-steps-macos"
- name: cmake generate project file
run: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
- name: Build tests
run: cd build && make VERBOSE=1 -j8
- name: Run shard 0 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 0,,4
if: ${{ matrix.run_sharded_tests == 0 }}
- name: Run shard 1 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 1,,4
if: ${{ matrix.run_sharded_tests == 1 }}
- name: Run shard 2 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 2,,4
if: ${{ matrix.run_sharded_tests == 2 }}
- name: Run shard 3 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 3,,4
if: ${{ matrix.run_sharded_tests == 3 }}
- uses: "./.github/actions/teardown-ccache"
if: always()
build-linux-arm-test-full:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 4-core-ubuntu-arm
steps:
@@ -131,9 +178,8 @@ jobs:
- uses: "./.github/actions/pre-steps"
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev
- run: make V=1 J=4 -j4 check
- uses: "./.github/actions/post-steps"
build-linux-arm-crashtest:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 4-core-ubuntu-arm
steps:
@@ -141,31 +187,39 @@ jobs:
- uses: "./.github/actions/pre-steps"
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
- run: sudo mount -o remount,size=16G /dev/shm
- run: sudo dd bs=1048576 count=4096 if=/dev/zero of=/swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
- name: Ensure swap space
run: |-
if sudo swapon --show=NAME --noheadings --raw | grep -Fxq /swapfile; then
echo "/swapfile is already active"
else
sudo dd bs=1048576 count=4096 if=/dev/zero of=/swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
fi
sudo swapon --show
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=1800 --max_key=2500000' blackbox_crash_test_with_atomic_flush
- run: rm -rf /dev/shm/rocksdb.*
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=1800 --max_key=2500000' blackbox_crash_test_with_multiops_wc_txn
- uses: "./.github/actions/post-steps"
build-examples:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- name: Build examples
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
- uses: "./.github/actions/post-steps"
build-fuzzers:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:24.1
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -173,18 +227,16 @@ jobs:
run: CC=clang-21 CXX=clang++-21 USE_CLANG=1 make -j4 static_lib
- name: Build fuzzers
run: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
- uses: "./.github/actions/post-steps"
build-linux-cmake-with-folly-lite:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/cache-getdeps-downloads"
- uses: "./.github/actions/setup-folly"
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 -DCMAKE_CXX_FLAGS=-DGLOG_USE_GLOG_EXPORT .. && make VERBOSE=1 -j20 && ctest -j20)"
- uses: "./.github/actions/post-steps"
+3 -5
View File
@@ -1,4 +1,4 @@
name: facebook/rocksdb/pr-jobs-candidate
name: meta/rocksdb/pr-jobs-candidate
on: workflow_dispatch
permissions: {}
jobs:
@@ -6,7 +6,7 @@ jobs:
# some reason.
# =========================== ARM Jobs ============================ #
build-linux-arm:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: arm64large # GitHub hosted ARM runners do not yet exist
steps:
@@ -14,9 +14,8 @@ jobs:
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/install-gflags"
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
- uses: "./.github/actions/post-steps"
build-linux-arm-cmake-no_test_run:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: arm64large # GitHub hosted ARM runners do not yet exist
env:
@@ -44,4 +43,3 @@ jobs:
cd build
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=1 ..
make -j4 rocksdb rocksdbjni
- uses: "./.github/actions/post-steps"
+127 -143
View File
@@ -1,9 +1,9 @@
name: facebook/rocksdb/pr-jobs
name: meta/rocksdb/pr-jobs
on: [push, pull_request]
permissions: {}
env:
# Set to a job name to run only that job (on any repo), or leave empty for
# normal behavior (all jobs on facebook repo only).
# normal behavior (all jobs on meta/rocksdb repo only).
ONLY_JOB: ''
jobs:
config:
@@ -19,7 +19,7 @@ jobs:
# workflow minimizes the problem because it will be suspicious if there are
# no GHA results.
#
# The if: ${{ github.repository_owner == 'facebook' }} lines prevent the
# The if: ${{ github.repository_owner == 'meta' }} lines prevent the
# jobs from attempting to run on repo forks, because of a few problems:
# * runs-on labels are repository (owner) specific, so the job might wait
# for days waiting for a runner that simply isn't available.
@@ -45,7 +45,7 @@ jobs:
# ======================== Fast Initial Checks ====================== #
check-format-and-targets:
if: needs.config.outputs.only_job == 'check-format-and-targets' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'check-format-and-targets' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on: ubuntu-24.04
steps:
@@ -65,7 +65,9 @@ jobs:
pip install https://files.pythonhosted.org/packages/fb/ac/3c04772acc0257f5730e83adb542b2603c1a62d1315010ab593a980af404/clang_format-21.1.2-py2.py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
clang-format --version
- name: Download clang-format-diff.py
run: wget https://rocksdb-deps.s3.us-west-2.amazonaws.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
# Mirrored on the "third-party-deps" GitHub release; add/update via
# https://github.com/meta/rocksdb/releases/edit/third-party-deps
run: wget --tries=3 --retry-on-host-error https://github.com/meta/rocksdb/releases/download/third-party-deps/clang-format-diff.py
- name: Check format
run: VERBOSE_CHECK=1 make check-format
- name: Compare buckify output
@@ -84,13 +86,13 @@ jobs:
SANITY_CHECK=1 LONG_TEST=1 tools/check_format_compatible.sh
# ========================= Linux With Tests ======================== #
build-linux:
if: needs.config.outputs.only_job == 'build-linux' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -100,15 +102,14 @@ jobs:
- run: make V=1 J=32 -j32 check
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-cmake-mingw:
if: needs.config.outputs.only_job == 'build-linux-cmake-mingw' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-cmake-mingw' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:24.1
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -125,15 +126,14 @@ jobs:
mkdir build && cd build && cmake -DJNI=1 -DWITH_GFLAGS=OFF .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-make-with-folly:
if: needs.config.outputs.only_job == 'build-linux-make-with-folly' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-make-with-folly' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 32-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=64gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -150,15 +150,14 @@ jobs:
- run: USE_FOLLY=1 LIB_MODE=static V=1 make -j64 check
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-make-with-folly-lite-no-test:
if: needs.config.outputs.only_job == 'build-linux-make-with-folly-lite-no-test' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-make-with-folly-lite-no-test' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -170,15 +169,14 @@ jobs:
- run: USE_FOLLY_LITE=1 EXTRA_CXXFLAGS=-DGLOG_USE_GLOG_EXPORT V=1 make -j32 all
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-cmake-with-folly-coroutines:
if: needs.config.outputs.only_job == 'build-linux-cmake-with-folly-coroutines' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-cmake-with-folly-coroutines' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 32-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=64gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -195,9 +193,8 @@ jobs:
- run: "(mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 -DPORTABLE=ON .. && make VERBOSE=1 -j64 && ctest -j64)"
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-cmake-with-benchmark-no-thread-status:
if: needs.config.outputs.only_job == 'build-linux-cmake-with-benchmark-no-thread-status' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-cmake-with-benchmark-no-thread-status' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 16-core-ubuntu
@@ -206,8 +203,8 @@ jobs:
matrix:
shard: [0, 1, 2, 3]
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -220,17 +217,14 @@ jobs:
run: cd build && ctest -j20 -I ${{ matrix.shard }},,4
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
with:
artifact-prefix: "${{ github.job }}-shard${{ matrix.shard }}"
build-linux-encrypted_env-no_compression:
if: needs.config.outputs.only_job == 'build-linux-encrypted_env-no_compression' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-encrypted_env-no_compression' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -241,16 +235,15 @@ jobs:
- run: "./sst_dump --help | grep -E -q 'Supported built-in compression types: kNoCompression$' # Verify no compiled in compression\n"
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
# ======================== Linux No Test Runs ======================= #
build-linux-release:
if: needs.config.outputs.only_job == 'build-linux-release' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-release' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/setup-ccache"
@@ -274,14 +267,13 @@ jobs:
- run: if ./trace_analyzer --version; then false; else true; fi
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-clang-13-no_test_run:
if: needs.config.outputs.only_job == 'build-linux-clang-13-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-clang-13-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 8-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=16gb
steps:
- uses: actions/checkout@v4.1.0
@@ -296,15 +288,14 @@ jobs:
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 EXTRA_CXXFLAGS=-stdlib=libc++ EXTRA_LDFLAGS=-stdlib=libc++ DEBUG_LEVEL=0 make -j32 shared_lib
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-clang-21-no_test_run:
if: needs.config.outputs.only_job == 'build-linux-clang-21-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-clang-21-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:24.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -314,17 +305,63 @@ jobs:
- run: CC=clang-21 CXX=clang++-21 USE_CLANG=1 make -j32 all microbench
- run: make clean
- run: CC=clang-21 CXX=clang++-21 USE_CLANG=1 DEBUG_LEVEL=0 make -j32 release
- name: Install clang-format-21 for C API gen checks
run: apt-get update && apt-get install -y clang-format-21
- name: Check C API codegen (link-complete, backward-compatible, up to date)
# Run the make target in STRICT mode so this core CI job HARD-FAILS rather
# than silently skipping if a prerequisite (clang++, clang-format, or the
# compat baseline ref) is ever missing. See `make check-c-api-gen`.
env:
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PUSH_BEFORE_SHA: ${{ github.event.before }}
run: |
# The container checkout is owned by a different user, so git refuses to
# operate on it ("dubious ownership") -- mark it safe for the `git fetch`
# and the `git show` the compat checker runs.
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git config --global --add safe.directory '*'
# A PR must preserve the API of its merge target, while a push must
# preserve the API of the branch before the push. Comparing every event
# with main produces false removals when a release branch predates APIs
# added to main.
case "$GITHUB_EVENT_NAME" in
pull_request) api_compat_ref="$PR_BASE_SHA" ;;
push) api_compat_ref="$PUSH_BEFORE_SHA" ;;
*)
echo "ERROR: unsupported event for C API compatibility: $GITHUB_EVENT_NAME" >&2
exit 1
;;
esac
if [ -z "$api_compat_ref" ]; then
echo "ERROR: C API compatibility baseline is empty" >&2
exit 1
fi
zero_sha=0000000000000000000000000000000000000000
if [ "$api_compat_ref" = "$zero_sha" ]; then
# A newly created branch or tag has no prior ref state to preserve.
api_compat_ref=HEAD
else
git fetch --no-tags --depth=1 origin "$api_compat_ref"
api_compat_ref=FETCH_HEAD
fi
CXX=clang++-21 make check-c-api-gen \
CHECK_C_API_GEN_STRICT=1 \
API_COMPAT_REF="$api_compat_ref" \
CLANG_FORMAT_BINARY=clang-format-21
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-gcc-14-no_test_run:
if: needs.config.outputs.only_job == 'build-linux-gcc-14-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-gcc-14-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:24.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -334,18 +371,17 @@ jobs:
- run: CC=gcc-14 CXX=g++-14 V=1 make -j32 all microbench
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
# ======================== Linux Other Checks ======================= #
build-linux-unity-and-headers:
if: needs.config.outputs.only_job == 'build-linux-unity-and-headers' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-unity-and-headers' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 4-core-ubuntu
container:
image: gcc:latest
options: --shm-size=16gb
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
- run: apt-get update -y && apt-get install -y libgflags-dev
@@ -357,9 +393,8 @@ jobs:
- run: make V=1 -j8 -k check-headers
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-mini-crashtest:
if: needs.config.outputs.only_job == 'build-linux-mini-crashtest' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-mini-crashtest' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 4-core-ubuntu
@@ -372,26 +407,30 @@ jobs:
- crash_test_target: blackbox_crash_test
crash_duration: 240
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb --ulimit nofile=1048576:1048576
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=8gb --ulimit nofile=1048576:1048576
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- uses: "./.github/actions/cache-getdeps-downloads"
- uses: "./.github/actions/setup-folly"
- uses: "./.github/actions/setup-ccache"
with:
cache-key-prefix: mini-crashtest
- uses: "./.github/actions/cache-folly"
id: cache-folly
- uses: "./.github/actions/build-folly"
with:
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
- run: |
ulimit -S -n $(ulimit -Hn) # Raise open files soft limit to hard limit
echo "Updated ulimit -Hn: $(ulimit -Hn) -Sn: $(ulimit -Sn)"
make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=${{ matrix.crash_duration }} --max_key=2500000' ${{ matrix.crash_test_target }}
USE_COROUTINES=1 USE_FOLLY=1 LIB_MODE=static make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=${{ matrix.crash_duration }} --max_key=2500000' ${{ matrix.crash_test_target }}
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
with:
artifact-prefix: "${{ github.job }}-${{ matrix.crash_test_target }}"
# ======================= Linux with Sanitizers ===================== #
build-linux-clang21-asan-ubsan:
if: needs.config.outputs.only_job == 'build-linux-clang21-asan-ubsan' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-clang21-asan-ubsan' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 32-core-ubuntu
@@ -400,8 +439,8 @@ jobs:
matrix:
shard: [0, 1, 2]
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:24.1
options: --shm-size=64gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -414,11 +453,8 @@ jobs:
CI_TOTAL_SHARDS: 3
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
with:
artifact-prefix: "${{ github.job }}-shard${{ matrix.shard }}"
build-linux-clang21-mini-tsan:
if: needs.config.outputs.only_job == 'build-linux-clang21-mini-tsan' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-clang21-mini-tsan' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 32-core-ubuntu
@@ -427,8 +463,8 @@ jobs:
matrix:
shard: [0, 1, 2]
container:
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:24.1
options: --shm-size=64gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -441,17 +477,14 @@ jobs:
CI_TOTAL_SHARDS: 3
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
with:
artifact-prefix: "${{ github.job }}-shard${{ matrix.shard }}"
build-linux-static_lib-alt_namespace-status_checked:
if: needs.config.outputs.only_job == 'build-linux-static_lib-alt_namespace-status_checked' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-static_lib-alt_namespace-status_checked' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 16-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -461,10 +494,10 @@ jobs:
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_USE_STD_SEMAPHORES -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
# ========================= MacOS build only ======================== #
# NOTE: build-macos-cmake (with tests) is in "nightly" to save resources
build-macos:
if: needs.config.outputs.only_job == 'build-macos' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-macos' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on: macos-15-xlarge
env:
@@ -484,49 +517,10 @@ jobs:
run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j8 all
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
# ========================= MacOS with Tests ======================== #
build-macos-cmake:
if: needs.config.outputs.only_job == 'build-macos-cmake' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
needs: config
runs-on: macos-15-xlarge
strategy:
matrix:
run_sharded_tests: [0, 1, 2, 3]
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: 16.4.0
- uses: "./.github/actions/setup-ccache"
with:
cache-key-prefix: macos-cmake
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/pre-steps-macos"
- name: cmake generate project file
run: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
- name: Build tests
run: cd build && make VERBOSE=1 -j8
- name: Run shard 0 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 0,,4
if: ${{ matrix.run_sharded_tests == 0 }}
- name: Run shard 1 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 1,,4
if: ${{ matrix.run_sharded_tests == 1 }}
- name: Run shard 2 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 2,,4
if: ${{ matrix.run_sharded_tests == 2 }}
- name: Run shard 3 out of 4 test shards
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 3,,4
if: ${{ matrix.run_sharded_tests == 3 }}
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
# ======================== Windows with Tests ======================= #
# NOTE: some windows jobs are in "nightly" to save resources
build-windows-vs2022:
if: needs.config.outputs.only_job == 'build-windows-vs2022' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
build-windows-msvc:
if: needs.config.outputs.only_job == 'build-windows-msvc' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on: windows-8-core
strategy:
@@ -543,7 +537,6 @@ jobs:
suite_run: ""
run_java: "true"
env:
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: 1
steps:
- uses: actions/checkout@v4.1.0
@@ -553,13 +546,13 @@ jobs:
run-java: ${{ matrix.run_java }}
# ============================ Java Jobs ============================ #
build-linux-java:
if: needs.config.outputs.only_job == 'build-linux-java' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-java' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -576,13 +569,13 @@ jobs:
- uses: "./.github/actions/teardown-ccache"
if: always()
build-linux-java-static:
if: needs.config.outputs.only_job == 'build-linux-java-static' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-java-static' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 4-core-ubuntu
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
@@ -599,11 +592,10 @@ jobs:
- uses: "./.github/actions/teardown-ccache"
if: always()
build-macos-java:
if: needs.config.outputs.only_job == 'build-macos-java' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-macos-java' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on: macos-15-xlarge
env:
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
ROCKSDB_DISABLE_JEMALLOC: 1
steps:
- uses: actions/checkout@v4.1.0
@@ -615,7 +607,7 @@ jobs:
cache-key-prefix: macos-java
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/install-jdk8-on-macos"
- uses: "./.github/actions/setup-jdk-on-macos"
- uses: "./.github/actions/pre-steps-macos"
- name: Set Java Environment
run: |-
@@ -626,13 +618,10 @@ jobs:
run: make V=1 J=16 -j16 jtest
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-macos-java-static:
if: needs.config.outputs.only_job == 'build-macos-java-static' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-macos-java-static' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on: macos-15-xlarge
env:
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
@@ -643,7 +632,7 @@ jobs:
cache-key-prefix: macos-java-static
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/install-jdk8-on-macos"
- uses: "./.github/actions/setup-jdk-on-macos"
- uses: "./.github/actions/pre-steps-macos"
- name: Set Java Environment
run: |-
@@ -654,13 +643,10 @@ jobs:
run: make V=1 J=16 -j16 rocksdbjavastaticosx
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-macos-java-static-universal:
if: needs.config.outputs.only_job == 'build-macos-java-static-universal' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-macos-java-static-universal' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on: macos-15-xlarge
env:
JAVA_HOME: "/Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home"
steps:
- uses: actions/checkout@v4.1.0
- uses: maxim-lobanov/setup-xcode@v1.6.0
@@ -671,7 +657,7 @@ jobs:
cache-key-prefix: macos-java-static-universal
- uses: "./.github/actions/increase-max-open-files-on-macos"
- uses: "./.github/actions/install-gflags-on-macos"
- uses: "./.github/actions/install-jdk8-on-macos"
- uses: "./.github/actions/setup-jdk-on-macos"
- uses: "./.github/actions/pre-steps-macos"
- name: Set Java Environment
run: |-
@@ -682,15 +668,14 @@ jobs:
run: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
- uses: "./.github/actions/teardown-ccache"
if: always()
- uses: "./.github/actions/post-steps"
build-linux-java-pmd:
if: needs.config.outputs.only_job == 'build-linux-java-pmd' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-java-pmd' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 4-core-ubuntu
container:
image: evolvedbinary/rocksjava:alpine3_x64-be
options: --shm-size=16gb
options: --shm-size=8gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/install-maven"
@@ -711,7 +696,7 @@ jobs:
name: maven-site
path: "${{ github.workspace }}/java/target/site"
build-linux-arm:
if: needs.config.outputs.only_job == 'build-linux-arm' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
if: needs.config.outputs.only_job == 'build-linux-arm' || (needs.config.outputs.only_job == '' && github.repository_owner == 'meta')
needs: config
runs-on:
labels: 4-core-ubuntu-arm
@@ -727,4 +712,3 @@ jobs:
run: ccache -s
if: always()
shell: bash
- uses: "./.github/actions/post-steps"
+4 -5
View File
@@ -1,4 +1,4 @@
name: facebook/rocksdb/weekly
name: meta/rocksdb/weekly
on:
schedule:
- cron: 0 9 * * 0
@@ -6,15 +6,14 @@ on:
permissions: {}
jobs:
build-linux-valgrind:
if: ${{ github.repository_owner == 'facebook' }}
if: ${{ github.repository_owner == 'meta' }}
runs-on:
labels: 16-core-ubuntu
timeout-minutes: 840
container:
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
options: --shm-size=16gb
image: ghcr.io/meta/rocksdb_ubuntu:22.1
options: --shm-size=32gb
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: make V=1 -j20 valgrind_test
- uses: "./.github/actions/post-steps"
+4
View File
@@ -1,5 +1,7 @@
make_config.mk
rocksdb.pc
.build_signature
build_tools/__pycache__/
*.a
*.arc
@@ -88,6 +90,8 @@ fbcode/
fbcode
buckifier/*.pyc
buckifier/__pycache__
tools/__pycache__/
tools/c_api_gen/__pycache__/
.arcconfig
compile_commands.json
+123 -11
View File
@@ -35,6 +35,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/blob/blob_file_partition_manager.cc",
"db/blob/blob_file_reader.cc",
"db/blob/blob_garbage_meter.cc",
"db/blob/blob_gen2_format.cc",
"db/blob/blob_log_format.cc",
"db/blob/blob_log_sequential_reader.cc",
"db/blob/blob_log_writer.cc",
@@ -58,6 +59,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/compaction/sst_partitioner.cc",
"db/compaction/subcompaction_state.cc",
"db/convenience.cc",
"db/coro_db.cc",
"db/db_filesnapshot.cc",
"db/db_impl/compacted_db_impl.cc",
"db/db_impl/db_impl.cc",
@@ -94,6 +96,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/merge_operator.cc",
"db/multi_scan.cc",
"db/output_validator.cc",
"db/periodic_compaction_phaser.cc",
"db/periodic_task_scheduler.cc",
"db/range_del_aggregator.cc",
"db/range_tombstone_fragmenter.cc",
@@ -102,7 +105,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/snapshot_impl.cc",
"db/table_cache.cc",
"db/table_properties_collector.cc",
"db/transaction_log_impl.cc",
"db/trim_history_scheduler.cc",
"db/version_builder.cc",
"db/version_edit.cc",
@@ -110,7 +112,9 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/version_set.cc",
"db/version_util.cc",
"db/wal_edit.cc",
"db/wal_iterator_impl.cc",
"db/wal_manager.cc",
"db/wide/lazy_wide_columns.cc",
"db/wide/read_path_blob_resolver.cc",
"db/wide/wide_column_serialization.cc",
"db/wide/wide_columns.cc",
@@ -202,6 +206,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"table/block_based/block_cache.cc",
"table/block_based/block_prefetcher.cc",
"table/block_based/block_prefix_index.cc",
"table/block_based/builtin_index_factory.cc",
"table/block_based/data_block_footer.cc",
"table/block_based/data_block_hash_index.cc",
"table/block_based/filter_block_reader_common.cc",
@@ -236,6 +241,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"table/plain/plain_table_index.cc",
"table/plain/plain_table_key_coding.cc",
"table/plain/plain_table_reader.cc",
"table/range_del_block.cc",
"table/sst_file_dumper.cc",
"table/sst_file_reader.cc",
"table/sst_file_writer.cc",
@@ -253,6 +259,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"tools/sst_dump_tool.cc",
"trace_replay/block_cache_tracer.cc",
"trace_replay/io_tracer.cc",
"trace_replay/trace_analyzer.cc",
"trace_replay/trace_record.cc",
"trace_replay/trace_record_handler.cc",
"trace_replay/trace_record_result.cc",
@@ -267,6 +274,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"util/compression.cc",
"util/compression_context_cache.cc",
"util/concurrent_task_limiter_impl.cc",
"util/coro_stats_util.cc",
"util/crc32c.cc",
"util/crc32c_arm64.cc",
"util/data_structure.cc",
@@ -274,6 +282,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"util/file_checksum_helper.cc",
"util/hash.cc",
"util/io_dispatcher_imp.cc",
"util/io_prefetch_scheduler.cc",
"util/murmurhash.cc",
"util/random.cc",
"util/rate_limiter.cc",
@@ -305,7 +314,9 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"utilities/compaction_filters.cc",
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
"utilities/convenience/info_log_finder.cc",
"utilities/copy_engine/copy_engine.cc",
"utilities/counted_fs.cc",
"utilities/db_split_merge.cc",
"utilities/debug.cc",
"utilities/env_mirror.cc",
"utilities/env_timed.cc",
@@ -313,6 +324,8 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"utilities/fault_injection_fs.cc",
"utilities/fault_injection_secondary_cache.cc",
"utilities/leveldb_options/leveldb_options.cc",
"utilities/live_tune/live_tune_options_helper.cc",
"utilities/live_tune/live_tuner.cc",
"utilities/memory/memory_util.cc",
"utilities/merge_operators.cc",
"utilities/merge_operators/bytesxor.cc",
@@ -377,12 +390,22 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"utilities/write_batch_with_index/write_batch_with_index.cc",
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
], deps=[
"//folly/container:f14_hash",
"//folly/coro:blocking_wait",
"//folly/coro:collect",
"//folly/coro:coroutine",
"//folly/coro:task",
"//folly/synchronization:distributed_mutex",
"fbsource//xplat/folly/container:f14_hash",
"fbsource//xplat/folly/coro:baton",
"fbsource//xplat/folly/coro:blocking_wait",
"fbsource//xplat/folly/coro:collect",
"fbsource//xplat/folly/coro:coroutine",
"fbsource//xplat/folly/coro:nothrow",
"fbsource//xplat/folly/coro:task",
"fbsource//xplat/folly/executors/thread_factory:named_thread_factory",
"fbsource//xplat/folly/executors:io_executor",
"fbsource//xplat/folly/executors:io_thread_pool_executor",
"fbsource//xplat/folly/io/async:async_base",
"fbsource//xplat/folly/io/async:event_base_manager",
"fbsource//xplat/folly/io/async:io_uring_backend",
"fbsource//xplat/folly/io/async:request_context",
"fbsource//xplat/folly/synchronization:distributed_mutex",
"fbsource//xplat/folly:executor",
], headers=glob(["**/*.h"]), link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=[], link_whole=True, extra_test_libs=False)
@@ -398,13 +421,27 @@ cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
"table/mock_table.cc",
"test_util/mock_time_env.cc",
"test_util/secondary_cache_test_util.cc",
"test_util/simple_external_table_factory.cc",
"test_util/testharness.cc",
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/trace_analyzer_tool.cc",
"utilities/agg_merge/test_agg_merge.cc",
"utilities/cassandra/test_utils.cc",
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=True)
], deps=[":rocksdb_lib"], headers=[
"db/db_test_util.h",
"db/db_with_timestamp_test_util.h",
"table/mock_table.h",
"test_util/mock_time_env.h",
"test_util/secondary_cache_test_util.h",
"test_util/simple_external_table_factory.h",
"test_util/testharness.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h",
"utilities/agg_merge/test_agg_merge.h",
"utilities/cassandra/test_utils.h",
], link_whole=False, extra_test_libs=True)
cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
"db/db_test_util.cc",
@@ -412,13 +449,27 @@ cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
"table/mock_table.cc",
"test_util/mock_time_env.cc",
"test_util/secondary_cache_test_util.cc",
"test_util/simple_external_table_factory.cc",
"test_util/testharness.cc",
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/trace_analyzer_tool.cc",
"utilities/agg_merge/test_agg_merge.cc",
"utilities/cassandra/test_utils.cc",
], deps=[":rocksdb_with_faiss_lib"], headers=[], link_whole=False, extra_test_libs=True)
], deps=[":rocksdb_with_faiss_lib"], headers=[
"db/db_test_util.h",
"db/db_with_timestamp_test_util.h",
"table/mock_table.h",
"test_util/mock_time_env.h",
"test_util/secondary_cache_test_util.h",
"test_util/simple_external_table_factory.h",
"test_util/testharness.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h",
"utilities/agg_merge/test_agg_merge.h",
"utilities/cassandra/test_utils.h",
], link_whole=False, extra_test_libs=True)
cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
"test_util/testutil.cc",
@@ -427,7 +478,12 @@ cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
"tools/simulated_hybrid_file_system.cc",
"tools/tool_hooks.cc",
"tools/trace_analyzer_tool.cc",
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
], deps=[":rocksdb_lib"], headers=[
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/simulated_hybrid_file_system.h",
"tools/trace_analyzer_tool.h",
], link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
@@ -451,14 +507,34 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
"db_stress_tool/expected_value.cc",
"db_stress_tool/multi_ops_txns_stress.cc",
"db_stress_tool/no_batched_ops_stress.cc",
"test_util/simple_external_table_factory.cc",
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/trace_analyzer_tool.cc",
], headers=[])
], headers=[
"db_stress_tool/db_stress_common.h",
"db_stress_tool/db_stress_compaction_service.h",
"db_stress_tool/db_stress_compression_manager.h",
"db_stress_tool/db_stress_driver.h",
"db_stress_tool/db_stress_filters.h",
"db_stress_tool/db_stress_listener.h",
"db_stress_tool/db_stress_shared_state.h",
"db_stress_tool/db_stress_test_base.h",
"db_stress_tool/db_stress_wide_merge_operator.h",
"db_stress_tool/expected_state.h",
"db_stress_tool/expected_value.h",
"db_stress_tool/multi_ops_txns_stress.h",
"test_util/simple_external_table_factory.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h",
])
cpp_binary_wrapper(name="ldb", srcs=["tools/ldb.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
cpp_binary_wrapper(name="db_split_merge", srcs=["tools/db_split_merge.cc"], deps=[":rocksdb_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
cpp_binary_wrapper(name="db_stress", srcs=["db_stress_tool/db_stress.cc"], deps=[":rocksdb_stress_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
cpp_binary_wrapper(name="db_bench", srcs=["tools/db_bench.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
@@ -4598,6 +4674,12 @@ cpp_unittest_wrapper(name="bloom_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="builtin_index_factory_test",
srcs=["table/block_based/builtin_index_factory_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="cache_reservation_manager_test",
srcs=["cache/cache_reservation_manager_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -4738,6 +4820,13 @@ cpp_unittest_wrapper(name="compression_test",
cpp_unittest_wrapper(name="configurable_test",
srcs=["options/configurable_test.cc"],
source_headers=["options/configurable_test.h"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="copy_engine_test",
srcs=["utilities/copy_engine/copy_engine_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
@@ -4928,6 +5017,12 @@ cpp_unittest_wrapper(name="db_kv_checksum_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="db_lazy_entity_test",
srcs=["db/wide/db_lazy_entity_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="db_log_iter_test",
srcs=["db/db_log_iter_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -5000,6 +5095,12 @@ cpp_unittest_wrapper(name="db_secondary_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="db_split_merge_test",
srcs=["utilities/db_split_merge_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="db_sst_test",
srcs=["db/db_sst_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -5296,6 +5397,12 @@ cpp_unittest_wrapper(name="listener_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="live_tuner_test",
srcs=["utilities/live_tune/live_tuner_test.cc"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="log_test",
srcs=["db/log_test.cc"],
deps=[":rocksdb_test_lib"],
@@ -5430,6 +5537,7 @@ cpp_unittest_wrapper(name="periodic_task_scheduler_test",
cpp_unittest_wrapper(name="persistent_cache_test",
srcs=["utilities/persistent_cache/persistent_cache_test.cc"],
source_headers=["utilities/persistent_cache/persistent_cache_test.h"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
@@ -5448,6 +5556,7 @@ cpp_unittest_wrapper(name="point_lock_manager_stress_test",
cpp_unittest_wrapper(name="point_lock_manager_test",
srcs=["utilities/transactions/lock/point/point_lock_manager_test.cc"],
source_headers=["utilities/transactions/lock/point/point_lock_manager_test.h"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
@@ -5658,6 +5767,7 @@ cpp_unittest_wrapper(name="trace_analyzer_test",
cpp_unittest_wrapper(name="transaction_test",
srcs=["utilities/transactions/transaction_test.cc"],
source_headers=["utilities/transactions/transaction_test.h"],
deps=[":rocksdb_test_lib"],
extra_compiler_flags=[])
@@ -5795,3 +5905,5 @@ cpp_unittest_wrapper(name="write_unprepared_transaction_test",
export_file(name = "tools/db_crashtest.py")
export_file(name = "tools/fault_injection_log_parser.py")
+102 -40
View File
@@ -14,11 +14,18 @@ This document provides guidance for generating and reviewing code in the RocksDB
**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! (Permitted exception: optional outputs that are generally safe to ignore.)
* 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.
**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. For non-OK Statuses, prefer comparing code() rather than using ASSERT_TRUE
which is bad for debugging.
**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.
@@ -32,13 +39,13 @@ This document provides guidance for generating and reviewing code in the RocksDB
**Memory Copy:** Avoid unnecessary memory copies. Use move semantics, `std::string_view`, `Slice`, and pass-by-reference where appropriate. Be aware of implicit copies in STL containers and function returns. Prefer in-place operations over copy-and-modify patterns.
**CPU Cache Efficiency:** Design data structures and access patterns to be cache-friendly. Keep frequently accessed data together (data locality). Prefer sequential memory access over random access. Be mindful of cache line sizes (typically 64 bytes) and avoid false sharing in concurrent code. Consider struct packing and field ordering to improve cache utilization.
**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. Consider branchless alternatives for simple conditionals. Order switch cases and if-else chains by frequency.
**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.
@@ -47,15 +54,17 @@ This document provides guidance for generating and reviewing code in the RocksDB
- **Cold path** (executed rarely, e.g., DB open, configuration parsing, error handling): Maintainability and clarity are more important. Prefer readable code over micro-optimizations. Complex optimizations here add maintenance burden with negligible performance benefit.
- **Warm path** (moderate frequency): Balance both concerns. Use profiling data to guide optimization decisions.
**Avoid Premature Optimization:** While performance is critical, focus on correctness first, then optimize based on profiling data. However, be performance-aware from the start—choosing the right algorithm and data structure upfront is not premature optimization. Use the hot path analysis above to decide how much optimization effort is warranted.
**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. Include usage examples, parameter descriptions, and notes on thread safety, performance characteristics, and compatibility considerations.
**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.
---
@@ -73,7 +82,7 @@ The database core handles write-ahead logging (WAL), memtables, compaction, and
**Testing:** Database core changes require extensive testing, including unit tests, integration tests, and stress tests. Test with various configurations, compaction styles, and concurrent workloads.
### Public Headers (`include`)
### Public Headers / "Public API" (`include/rocksdb/`)
Public headers define RocksDB's API surface. Changes here have the highest compatibility impact.
@@ -236,6 +245,11 @@ 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.
@@ -243,27 +257,25 @@ from an implementation detail instead of an explicit option.
* 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.
### When to run `make clean` (avoid mixing build modes)
### Avoiding mixed build modes with Make (use `AUTO_CLEAN=1`)
The Makefile does **not** track build mode, so object files from a prior
build are silently reused even when compiled with different flags, leading
to confusing linker errors, sanitizer false negatives, ODR violations, or
"phantom" bugs.
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.
Run `make clean` before switching any of these:
* **`ASSERT_STATUS_CHECKED=1` ↔ unset** — changes the `Status` class layout (ABI break).
* **Sanitizer builds** — toggling any of `COMPILE_WITH_ASAN=1`,
`COMPILE_WITH_UBSAN=1`, `COMPILE_WITH_TSAN=1` on/off.
* `DEBUG_LEVEL=0` (release) ↔ `DEBUG_LEVEL=1` (debug, default for `make dbg`).
* Different compilers, `OPT` levels, or other flags affecting codegen/ABI.
**Notable exception:** `DEBUG_LEVEL=2` can be safely mixed with
`DEBUG_LEVEL=1` — rebuild a subset of files with `DEBUG_LEVEL=2` to get
extra/more accurate runtime checks for those files without a full clean.
When in doubt, `make clean` is cheap insurance compared to chasing a
phantom bug.
`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
@@ -272,6 +284,22 @@ phantom bug.
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.
@@ -288,6 +316,7 @@ 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 |
| Compiler | GCC, Clang (libstdc++ **and** libc++), AppleClang, **MSVC (VS2022)**, MinGW (Linux cross-compile, build-only, no gflags) |
| Build system | Make, CMake, and BUCK (internal) -- keep all in sync (see "Build system" above) |
| Config | release (`-fno-rtti`), `ASSERT_STATUS_CHECKED`, ASAN/UBSAN/TSAN, folly, unity build, JNI/Java |
@@ -302,6 +331,12 @@ abstractions; otherwise guard with `#ifdef OS_WIN` (POSIX `<unistd.h>` ->
Windows `<process.h>`). Because libc++ is also tested, include what you use
rather than relying on libstdc++ transitive includes.
Do not use `static_assert` to reject a supported architecture for an optional
feature. Check an existing `port::` capability such as `port::kLittleEndian`
and disable only the unsupported feature so the rest of RocksDB still builds.
Use a portable representation when the feature itself must work across
architectures.
### Unit Test
* After all of the unit tests are added, review them and try to extract common
reusable utility functions to reduce code duplication due to copy past between
@@ -309,17 +344,29 @@ rather than relying on libstdc++ transitive includes.
* Don't use sleep to wait for certain events to happen. This will cause test to
be flaky. Instead, use sync point to synchronize thread progress.
* Cap unit test execution with 60 seconds timeout.
* When there are multiple unit tests need to be executed, try to use
gtest_parallel.py if available. E.g.
python3 ${GTEST_PARALLEL}/gtest_parallel.py ./table_test
* After writing a test, stress-test for flakiness:
* To build and run unit tests locally, prefer these helper scripts:
* `build_tools/rocksptest.sh <test_binary> [more_binaries...] [args...]`
builds the binary(ies) with parallel make and `AUTO_CLEAN=1` and runs
them under gtest-parallel, sharding the test cases across CPUs. Prefer
this whenever running more than a couple of test cases, e.g.
`build_tools/rocksptest.sh table_test` or
`build_tools/rocksptest.sh db_test env_test --gtest_filter=*Foo*`.
* `build_tools/rockstest.sh <test_binary> [args...]` builds with parallel
make and `AUTO_CLEAN=1` and runs the binary directly (serially).
Use it only for a very small number of test cases, e.g.
`build_tools/rockstest.sh db_test --gtest_filter=*MixedSlowdown*`.
* After writing a test, stress-test for flakiness (AUTO_CLEAN handles the
rebuild needed by the `COERCE_CONTEXT_SWITCH=1` flag change):
```bash
COERCE_CONTEXT_SWITCH=1 make {test_binary}
./{test_binary} --gtest_filter="*YourTestName*" --gtest_repeat=5
COERCE_CONTEXT_SWITCH=1 build_tools/rockstest.sh {test_binary} -r100 \
--gtest_filter="*YourTestName*"
```
* For CI-style flaky tests that do not reproduce with `gtest_parallel.py`,
`--gtest_repeat`, or normal coerce-mode runs, inspect
`tools/gtest_parallel_repro.py --help`.
* Each unit test file has overheads, so avoid creating new unit test files
for random minor features. Consider adding to slice_test, db_etc3_test, or
others.
### Unit test dedup guidelines
* Extract helper functions for repeated patterns such as object
@@ -340,6 +387,9 @@ rather than relying on libstdc++ transitive includes.
### Adding new public API
Refer to claude_md/add_public_api.md
### Changing existing public API behavior
Follow the Behavioral Compatibility requirements above.
### Adding new option
Refer to claude_md/add_option.md
@@ -367,17 +417,28 @@ rather than relying on libstdc++ transitive includes.
### Adding release note
* Release note should be kept short at high level for external user consumption.
Release notes identify what users might care about most in a release. They
are not exhaustive and are not a guide. PLEASE learn from past agents who
tried to build elaborate release notes with implementation details and
elsewhere-documented nuance. That wastes time. Fight the bias that
"my change" is important so must be worthy of release note mention.
* If more than single markdown line, consider how their formatting will be
integrated into HISTORY.md.
### Blog posts (docs/_posts)
* Blog post authors must be defined in `docs/_data/authors.yml` to be displayed
### Final verification of the change
* Execute make clean to clean all of the changes.
* Execute make check to build all of the changes and execute all of the tests.
Note that executing all of the tests could take multiple minutes.
* Run `ASSERT_STATUS_CHECKED=1 make check` to verify all Status objects are
properly checked. This catches missing error handling that can lead to
silent data corruption.
* Execute `AUTO_CLEAN=1 make check` to build all of the changes and execute all
of the tests. `AUTO_CLEAN=1` ensures a clean rebuild if your previous build
used different parameters. Note that executing all of the tests could take
multiple minutes.
* Run `AUTO_CLEAN=1 ASSERT_STATUS_CHECKED=1 make check` to verify all Status
objects are properly checked. This catches missing error handling that can
lead to silent data corruption. Merely building with ASSERT_STATUS_CHECKED=1
accomplishes nothing; it enables a runtime check. New unit tests missing
Status checks is a common failure point, even from agents. Avoid new uses
of PermitUncheckedError(). That's the lazy solution.
### Monitoring make check progress
* Use `make check-progress` to get machine-parseable JSON progress while
@@ -385,7 +446,7 @@ rather than relying on libstdc++ transitive includes.
builds without timeout issues.
* Run `make check` in background, then poll progress:
```bash
make check &
AUTO_CLEAN=1 make check &
# Poll periodically:
make check-progress
```
@@ -410,9 +471,10 @@ rather than relying on libstdc++ transitive includes.
### Executing benchmark using db_bench
* Since the goal is to measure performance, we need to build a release binary
using `make clean && DEBUG_LEVEL=0 make db_bench`. If there is an engine
crash due to bug, we need to switch back to debug build. Make sure to run
`make clean` before running `make dbg`.
using `AUTO_CLEAN=1 DEBUG_LEVEL=0 make db_bench`. If there is an engine
crash due to a bug, switch back to a debug build with
`AUTO_CLEAN=1 make dbg`; `AUTO_CLEAN=1` handles the release<->debug rebuild
automatically.
### Formatting code
* After making change, use `make format-auto` to auto-apply formatting without
+55 -13
View File
@@ -420,13 +420,6 @@ if(WITH_NUMA)
list(APPEND THIRDPARTY_LIBS NUMA::NUMA)
endif()
option(WITH_TBB "build with Threading Building Blocks (TBB)" OFF)
if(WITH_TBB)
find_package(TBB REQUIRED)
add_definitions(-DTBB)
list(APPEND THIRDPARTY_LIBS TBB::TBB)
endif()
# Stall notifications eat some performance from inserts
option(DISABLE_STALL_NOTIF "Build with stall notifications" OFF)
if(DISABLE_STALL_NOTIF)
@@ -529,8 +522,6 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=lld")
endif()
endif()
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
add_definitions(-DOS_SOLARIS)
elseif(CMAKE_SYSTEM_NAME MATCHES "kFreeBSD")
add_definitions(-DOS_GNU_KFREEBSD)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
@@ -657,6 +648,9 @@ if(USE_FOLLY)
exec_program(ls ARGS -d ${FOLLY_INST_PATH}/../glog* OUTPUT_VARIABLE
GLOG_INST_PATH)
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.83.0)
# Boost's per-library CMake configs find their dependencies (e.g.
# boost_assert) only through the package search path.
set(CMAKE_PREFIX_PATH ${BOOST_INST_PATH} ${CMAKE_PREFIX_PATH})
if(EXISTS ${FMT_INST_PATH}/lib64)
set(fmt_DIR ${FMT_INST_PATH}/lib64/cmake/fmt)
else()
@@ -703,12 +697,27 @@ if(USE_FOLLY)
exec_program(sed ARGS -i 's/gflags_shared//g'
${FOLLY_INST_PATH}/lib/cmake/folly/folly-targets.cmake)
# folly-config.cmake only checks for gflags under a target name that this
# install doesn't define, then falls back to our Findgflags.cmake, which
# mistakes the gflags_static target name for a library path. gflags is
# resolved below instead.
set(CMAKE_DISABLE_FIND_PACKAGE_gflags TRUE)
include(${FOLLY_INST_PATH}/lib/cmake/folly/folly-config.cmake)
unset(CMAKE_DISABLE_FIND_PACKAGE_gflags)
# Fix gflags library name for debug builds
# Fix gflags library name for debug builds. The getdeps gflags shared
# library is versioned (e.g. libgflags_debug.so.2.3), so resolve the
# actual file by glob rather than hard-coding a version.
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
file(GLOB GFLAGS_DEBUG_SHARED_LIBS
"${GFLAGS_INST_PATH}/lib/libgflags_debug.so.*")
if(NOT GFLAGS_DEBUG_SHARED_LIBS)
message(FATAL_ERROR
"Could not find getdeps libgflags_debug.so under ${GFLAGS_INST_PATH}/lib")
endif()
list(GET GFLAGS_DEBUG_SHARED_LIBS 0 GFLAGS_DEBUG_SHARED_LIB)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath=${GFLAGS_INST_PATH}/lib")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GFLAGS_INST_PATH}/lib/libgflags_debug.so.2.2")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GFLAGS_DEBUG_SHARED_LIB}")
endif()
endif()
@@ -775,6 +784,7 @@ set(SOURCES
db/blob/blob_file_meta.cc
db/blob/blob_file_reader.cc
db/blob/blob_garbage_meter.cc
db/blob/blob_gen2_format.cc
db/blob/blob_log_format.cc
db/blob/blob_log_sequential_reader.cc
db/blob/blob_log_writer.cc
@@ -798,6 +808,7 @@ set(SOURCES
db/compaction/sst_partitioner.cc
db/compaction/subcompaction_state.cc
db/convenience.cc
db/coro_db.cc
db/db_filesnapshot.cc
db/db_impl/compacted_db_impl.cc
db/db_impl/db_impl.cc
@@ -834,6 +845,7 @@ set(SOURCES
db/merge_operator.cc
db/multi_scan.cc
db/output_validator.cc
db/periodic_compaction_phaser.cc
db/periodic_task_scheduler.cc
db/range_del_aggregator.cc
db/range_tombstone_fragmenter.cc
@@ -842,7 +854,6 @@ set(SOURCES
db/snapshot_impl.cc
db/table_cache.cc
db/table_properties_collector.cc
db/transaction_log_impl.cc
db/trim_history_scheduler.cc
db/version_builder.cc
db/version_edit.cc
@@ -850,7 +861,9 @@ set(SOURCES
db/version_set.cc
db/version_util.cc
db/wal_edit.cc
db/wal_iterator_impl.cc
db/wal_manager.cc
db/wide/lazy_wide_columns.cc
db/wide/read_path_blob_resolver.cc
db/wide/wide_column_serialization.cc
db/wide/wide_columns.cc
@@ -932,6 +945,7 @@ set(SOURCES
table/block_based/block_cache.cc
table/block_based/block_prefetcher.cc
table/block_based/block_prefix_index.cc
table/block_based/builtin_index_factory.cc
table/block_based/data_block_hash_index.cc
table/block_based/data_block_footer.cc
table/block_based/filter_block_reader_common.cc
@@ -965,6 +979,7 @@ set(SOURCES
table/plain/plain_table_index.cc
table/plain/plain_table_key_coding.cc
table/plain/plain_table_reader.cc
table/range_del_block.cc
table/sst_file_dumper.cc
table/sst_file_reader.cc
table/sst_file_writer.cc
@@ -985,6 +1000,7 @@ set(SOURCES
tools/trace_analyzer_tool.cc
trace_replay/block_cache_tracer.cc
trace_replay/io_tracer.cc
trace_replay/trace_analyzer.cc
trace_replay/trace_record_handler.cc
trace_replay/trace_record_result.cc
trace_replay/trace_record.cc
@@ -999,11 +1015,13 @@ set(SOURCES
util/simple_mixed_compressor.cc
util/compression_context_cache.cc
util/concurrent_task_limiter_impl.cc
util/coro_stats_util.cc
util/crc32c.cc
util/data_structure.cc
util/dynamic_bloom.cc
util/hash.cc
util/io_dispatcher_imp.cc
util/io_prefetch_scheduler.cc
util/murmurhash.cc
util/random.cc
util/rate_limiter.cc
@@ -1020,6 +1038,7 @@ set(SOURCES
util/xxhash.cc
utilities/agg_merge/agg_merge.cc
utilities/backup/backup_engine.cc
utilities/copy_engine/copy_engine.cc
utilities/blob_db/blob_compaction_filter.cc
utilities/blob_db/blob_db.cc
utilities/blob_db/blob_db_impl.cc
@@ -1032,7 +1051,10 @@ set(SOURCES
utilities/cassandra/format.cc
utilities/cassandra/merge_operator.cc
utilities/checkpoint/checkpoint_impl.cc
utilities/live_tune/live_tune_options_helper.cc
utilities/live_tune/live_tuner.cc
utilities/compaction_filters.cc
utilities/db_split_merge.cc
utilities/sorted_run_builder/sorted_run_builder.cc
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc
utilities/counted_fs.cc
@@ -1489,6 +1511,7 @@ if(WITH_TESTS)
db/db_compaction_test.cc
db/db_compaction_abort_test.cc
db/db_clip_test.cc
utilities/db_split_merge_test.cc
db/db_dynamic_level_test.cc
db/db_encryption_test.cc
db/db_etc2_test.cc
@@ -1555,6 +1578,7 @@ if(WITH_TESTS)
db/version_set_test.cc
db/wal_manager_test.cc
db/wal_edit_test.cc
db/wide/db_lazy_entity_test.cc
db/wide/db_wide_basic_test.cc
db/wide/db_wide_blob_direct_write_test.cc
db/wide/wide_column_serialization_test.cc
@@ -1586,6 +1610,7 @@ if(WITH_TESTS)
options/options_test.cc
table/block_based/block_based_table_reader_test.cc
table/block_based/block_test.cc
table/block_based/builtin_index_factory_test.cc
table/block_based/data_block_hash_index_test.cc
table/block_based/full_filter_block_test.cc
table/block_based/partitioned_filter_block_test.cc
@@ -1637,6 +1662,8 @@ if(WITH_TESTS)
utilities/cassandra/cassandra_row_merge_test.cc
utilities/cassandra/cassandra_serialize_test.cc
utilities/checkpoint/checkpoint_test.cc
utilities/live_tune/live_tuner_test.cc
utilities/copy_engine/copy_engine_test.cc
utilities/sorted_run_builder/sorted_run_builder_test.cc
utilities/env_timed_test.cc
utilities/fault_injection_fs_test.cc
@@ -1676,6 +1703,7 @@ if(WITH_TESTS)
db/db_with_timestamp_test_util.cc
monitoring/thread_status_updater_debug.cc
table/mock_table.cc
test_util/simple_external_table_factory.cc
utilities/agg_merge/test_agg_merge.cc
utilities/cassandra/test_utils.cc
)
@@ -1704,7 +1732,21 @@ if(WITH_TESTS)
)
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} testharness gtest ${THIRDPARTY_LIBS} ${ROCKSDB_LIB})
if(NOT "${exename}" MATCHES "db_sanity_test")
gtest_discover_tests(${exename} DISCOVERY_TIMEOUT 120)
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.18)
# Discover tests at ctest time instead of during the parallel build.
# POST_BUILD discovery (the default) runs each freshly-built test
# binary while the -j build is still compiling; on resource-constrained
# CI (notably the arm64 macOS runners) that discovery run can yield
# empty output, and CMake's JSON parser then aborts the whole build
# with "Syntax error: value, object or array expected". PRE_TEST moves
# discovery out of the build phase so a slow/empty enumeration no
# longer fails the build.
gtest_discover_tests(${exename}
DISCOVERY_TIMEOUT 120
DISCOVERY_MODE PRE_TEST)
else()
gtest_discover_tests(${exename} DISCOVERY_TIMEOUT 120)
endif()
add_dependencies(rocksdb_check ${exename}${ARTIFACT_SUFFIX})
endif()
endforeach(sourcefile ${TESTS})
+306 -17
View File
@@ -1,6 +1,295 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 11.14.0 (11/05/2026)
### New Features
* Added experimental `SetAsyncReadCPUTimeEnabled()`: the next async or coroutine read started on the thread records its CPU time in `PerfContext::get_cpu_nanos` at any perf level except `kDisable`, without turning on the other timers of `kEnableTimeAndCPUTimeExceptForMutex`.
* Added `SplitDB()`, `DeleteSplitRanges()`, and `MergeDB()` in `rocksdb/utilities/db_split_merge.h`, plus a `db_split_merge` command-line tool, for moving selected column-family key ranges into a new DB and copying ranges into existing column families.
* Added opt-in sampled logging of long-held DB-wide mutexes under `StatsLevel::kAll`, with a runtime-changeable `mutex_hold_log_threshold_us` DB option defaulting to 0 (disabled).
* Add experimental live tuning of a DB's options (`StartLiveTuneCycle()`, `GetLiveTuneStatus()`, `StopLiveTuneCycle()` in `include/rocksdb/live_tune.h`): one call runs a cycle that measures the DB, asks an application-provided `LiveTuneAdvisor` for option changes, applies them, and reverts them when they break the application's constraints or do not prove better.
* Added `CompactionFilter::GetMergeOrder()` and `CompactionFilterFactory::GetMergeOrder()` to optionally run the merge operator before the compaction filter during compaction, so that the filter sees merge results. The default behavior is unchanged.
* Parallel compression (`CompressionOptions::parallel_threads > 1`) now works with a `user_defined_index_factory` whose builders support it, including the built-in trie index. Previously a custom index always forced single-threaded compression.
* Added option `reject_too_old_file` to `IngestExternalFileOptions` and `ImportColumnFamilyOptions` to reject files whose data is already older than the column family's `periodic_compaction_seconds` (or `ttl` for FIFO compaction).
* Added BlobDB support for remote compaction. Blob garbage collection uses the
cutoff computed by the primary; workers receiving legacy input without a cutoff
safely disable blob garbage collection for that job. When the primary rejects a
remote blob result because its metadata, files, or capabilities fail validation,
it reruns the compaction locally, so `CompactRange` and `CompactFiles` succeed.
`CompactionService` `kFailure` semantics are unchanged; ZippyDB production also
maps service failures to local fallback through `HandleFailure(FALLBACK_LOCAL)`.
* `CompactRangeOptions::skip_missing_blob_files` enables offline repair that deletes values referencing integrated external blob files absent from Version metadata, preserves available wide entity columns, and rewrites selected SSTs locally, bypassing `DBOptions::compaction_service`. It defaults to false; FIFO compaction and legacy BlobDB are unsupported.
* Added `db_ownership_start_time` and `data_file_provenance` (decoded by `DataFileProvenance`) to `SstFileMetaData`, recording when the DB began holding each SST file and how it got the file (e.g. flush, compaction, ingestion, or import).
* Added `TableFactory::IsTableBuilderSupported()` and `ExternalTableFactory::IsTableBuilderSupported()` for table factories that only read ingested or imported files. A column family whose factory returns false must set `disallow_memtable_writes`, is never scheduled for automatic compaction, does not stall writes for L0 files or pending compaction bytes, and returns `NotSupported` from `CompactRange()` and `CompactFiles()` instead of failing a background compaction.
* trace_analyzer: new `--analyze_put_entity` flag analyzes PutEntity queries (previously their statistics were neither enabled nor disabled explicitly).
* Added `TraceOptions::include_read_write_options`: query traces can record the main `ReadOptions` and `WriteOptions` of each traced call (e.g. `fill_cache`, `disableWAL`), which replay then uses instead of default options.
### Public API Changes
* Added `GetEncodedEntrySize()` and an `EncodeEntry()` overload that writes internal keys into caller-owned buffers without allocating on success.
* Added `Env::Priority::READ` and `ThreadStatus::READ_PRIORITY` for a dedicated Env-managed read-work thread pool. `Env::Priority::USER` remains reserved for internal statistics and is not backed by a thread pool.
* Added experimental `MultiScanArgs::use_parallel_async_io_block_decompression` for materializing asynchronously prefetched data blocks, including decompression, in parallel on the `Env::Priority::READ` thread pool. A zero-sized pool preserves inline materialization.
* Added the experimental `IndexFactory` interface (`include/rocksdb/index_factory.h`) for pluggable SST indexes; the `UserDefinedIndex*` names remain as aliases. `BlockBasedTableOptions::index_mode` chooses which indexes an SST is built with and which one reads use by default, superseding the now-deprecated `use_udi_as_primary_index` and `fail_if_no_udi_on_open`. `ReadOptions::read_index` overrides that choice per read, and the `standard_index_is_stub` table property marks SSTs built with `kCustomOnly`. Both options are also available in the C API.
* Added experimental `DB::GetEntityWithMetadata()` and `DB::MultiGetEntityWithMetadata()`, the wide-column entity counterparts of `DB::GetWithMetadata()` and `DB::MultiGetWithMetadata()` with opt-in explicit-snapshot newer-version detection. Requesting key timestamps returns `NotSupported`.
* Removed support for building on Solaris, which had not compiled since 5.18. Removed RocksJava `Environment.isSolaris()`.
### Behavior Changes
* With `ReadOptions::async_io`, `MultiGet` now reads data blocks of direct-IO files with asynchronous reads (`FSRandomAccessFile::ReadAsync`) instead of a synchronous `MultiRead`. Each block read is aligned separately rather than merged with adjacent reads. The bytes read are still charged to `DBOptions::rate_limiter` at `ReadOptions::rate_limiter_priority`.
* Asynchronous file reads (`FSRandomAccessFile::ReadAsync`, used with `ReadOptions::async_io` by `MultiGet` and iterator prefetching) are now charged to `DBOptions::rate_limiter` at `ReadOptions::rate_limiter_priority`, like synchronous reads. A read is charged once the file system accepts it.
* `CreateColumnFamily()` and `CreateColumnFamilies()` now run the table factory option validation that `DB::Open()` runs, so they return `InvalidArgument` instead of creating a column family that the next `DB::Open()` would reject. External table factories wrapped by `NewExternalTableFactory()` now receive `ValidateOptions()` calls.
* With `CompactionOptionsFIFO::allow_trivial_copy_when_change_temperature`, each file copied for a temperature change is now reported to `EventListener::OnTableFileCreationStarted()` and `OnTableFileCreated()` (with the original file's table properties) and included in `CompactionJobInfo::table_properties`.
* `DB::GetMergeOperands()` now resets only the `merge_operands` entries it fills, instead of all `expected_max_number_of_operands` entries. Entries past `*number_of_operands`, and the whole array on a non-OK result, are left as the caller passed them.
* The JSON written to the info LOG by `EventLogger` (`EVENT_LOG_v1` lines) and by `VersionEdit::DebugJSON()` now escapes special characters in keys and string values, and writes non-finite floating-point values as `null`, so the output is always valid JSON.
* Improved how `periodic_compaction_phase_recovery_percent` spreads catch-up periodic compactions after it is enabled or changed (or the DB is reopened). This reduces a transient burst in periodic compactions when many DBs make such a change at once.
* The new DB option `use_separate_remote_compaction_pool` defaults to false and can be changed through `SetDBOptions()` for newly scheduled jobs. When enabled with CompactionService, remote jobs use a dedicated pool and each local subcompaction uses LOW/BOTTOM and counts against the DB compaction limit. Existing jobs finish using their original scheduling model.
* `DB::SetOptions()` now returns OK without writing a new OPTIONS file or installing a new SuperVersion when the call changes no option value, as `DB::SetDBOptions()` already does. Calls that set an object option, such as `table_factory` or one of its fields, are still applied in full. After a failed OPTIONS file write, the next `SetOptions()` or `SetDBOptions()` call writes the file even if no option value changes.
* Custom index factories are rejected with user-defined timestamps in every index mode except `kStandardOnly`, which ignores the factory. Custom-index block-cache hits, misses, and adds now contribute to `BLOCK_CACHE_INDEX_*` instead of `BLOCK_CACHE_DATA_*`.
Trie index construction returns `NotSupported` when a data-block offset or size exceeds `UINT32_MAX`, including when the trie is optional in `kStandardDefault`. Flush, compaction, and `SstFileWriter::Finish()` can therefore fail for such output; an SST larger than 4 GiB is not itself the rejection condition. File-size targets do not split L0 outputs or `SstFileWriter` outputs. Bound those outputs or disable trie construction with `kStandardOnly`.
### Bug Fixes
* Fixed a race where `IngestExternalFiles()` with `atomic_replace_range` could delete an existing SST file that a concurrently picked compaction (e.g. a universal compaction outputting to L0) used as input, causing the compaction to fail with "Corruption: VersionBuilder: Cannot delete table file ... since it is not in the LSM tree" and putting the DB into read-only mode. Atomic range replacement now returns `NotSupported` with FIFO compaction.
* Fixed blob direct write when sealing a blob file fails during a flush or a write: the flush left its memtables marked as being flushed, so no later flush picked them up and error recovery waited for them forever, and the blob file was dropped from tracking, so a retried flush could install table files whose blob file was never added to the MANIFEST.
* Fixed blob direct write with more than one concurrent flush of a column family: a later flush sealed and published the blob files of the oldest pending memtable instead of its own, so flushes failed with `Corruption: VersionBuilder: Blob file #N already added` and the database stopped.
* Fixed blob direct write when the `SstFileManager` space limit is reached while a blob file is sealed: the sealed blob file was deleted while memtables still referenced it, so reads of its values failed and the values were lost, and reporting the error could deadlock with a concurrent memtable switch. The seal and the write or flush now succeed and keep the file, and writes then stop with a `SpaceLimit` background error, so for blob direct write the limit is soft: usage can exceed it by up to about `blob_file_size` × `blob_direct_write_partitions` for each memtable not flushed yet. Also fixed a write batch that failed after writing some of its values to blob files: those blobs are now counted as garbage, so their blob file can still be deleted.
* Values that retain external blob references now keep their original timestamps during history compaction, preventing blob key verification failures.
* Fixed `DB::ClipColumnFamily()` leaving an empty-string key outside the kept range when it is the only key in an L0 SST file.
* Fixed `format_version=8` with `ReverseBytewiseComparator`: a block's stored common key prefix could extend past a user key (one following longer user keys that extend it), so reads and iteration could miss that key (or its deletion, exposing an older value), and compactions could fail with `Corruption`. Files written by affected versions (11.10 through 11.13) are now read correctly, and new files no longer have this problem.
* Fixed SST file times that could defer periodic compaction and TTL. Files written by `SstFileWriter`, flushed during WAL recovery, or recovered by `RepairDB` now record a file creation time. Files added by `IngestExternalFile()` or `CreateColumnFamilyWithImport()` now keep the creation and oldest ancestor times from the file or export metadata (capped at the current time) instead of taking the time they were added, so ingesting or importing old data can make it due for periodic compaction or TTL soon afterward. FIFO TTL compaction now also deletes ingested `SstFileWriter` files, which previously blocked TTL deletion of themselves and all newer files. Files copied by FIFO compaction for a temperature change now keep the original file's creation time.
* Fixed a debug-build assertion failure and incorrect cache charge and block-cache byte statistics in release builds when a full, strict-capacity LRU cache with metadata charging creates an uncharged standalone handle, including during secondary-cache promotion.
* Fixed atomic flushes of all column families when another column family is dropped while they wait: `CompactRange()` no longer fails with a "Column family dropped" error, and `WaitForCompact()` and DB close no longer return OK without flushing the remaining column families.
* Fixed a bug where MemPurge returned with the DB mutex unlocked when the compaction filter created for flush did not ignore snapshots.
* Fixed a crash in MultiScan `Iterator::Prepare()` with a single scan range without a limit when the DB has L0 files, and an assertion failure in debug builds when `Prepare()` is called on an already-positioned iterator. `Prepare()` now leaves the iterator invalid until it is positioned again (e.g. by `Seek()`).
* Fixed `MultiGet` returning NotFound for existing keys that are outside the prefix extractor's domain, with partitioned filters, `whole_key_filtering=false`, and filter partitions cut together with index partitions (`decouple_partitioned_filters=false`, also the layout of files written by older versions). A filter partition holding only such keys is empty, and `MultiGet` treated every key mapped to it as absent. `Get` was not affected.
* Fixed prepared external file ingestions retaining unrelated obsolete SSTs until commit or abort. Prepared ingestions now protect only their staged files from deletion.
* Fixed data loss with `min_tombstones_for_range_conversion` and external file ingestion. An iterator whose view predated the ingestion could convert the deleted keys around the ingested keys into a range tombstone that deleted them. This happened when the file got no new sequence number (sequence number zero, `ingest_behind`, or `allow_db_generated_files`), and when an iterator was created during the ingestion.
* Fixed a data loss bug where an iterator with `ReadOptions::read_tier = kMemtableTier` could, with `min_tombstones_for_range_conversion` enabled, insert a range tombstone that deleted live keys stored only in SST files. Read-path range tombstone conversion now runs only for iterators with `read_tier = kReadAllTier`.
* Fixed a race with `recycle_log_file_num > 0` where an obsolete WAL could be reused for a new WAL while its old writer was still open. Closing the old writer then truncated the reused file to the old WAL's size, so writes in the new WAL past that point, including acknowledged `sync` writes, were lost on the next reopen.
* Fixed remote compaction ignoring `preclude_last_level_data_seconds` and `preserve_internal_time_seconds`, which caused recent data to be written to the last level and recent sequence numbers to be zeroed. Takes effect once both the primary and the compaction worker are upgraded.
* Fixed `DB::OpenAndCompact()` with `OpenAndCompactOptions::allow_resumption` not calling `EventListener::OnSubcompactionCompleted()` when the compaction fails to resume from its saved progress.
* Fixed MANIFEST catch-up on secondary instances (`TryCatchUpWithPrimary()`) and follower instances installing a column family with no files and still returning OK. This happened when catch-up switched to a new MANIFEST and the primary deleted a file that MANIFEST lists before catch-up checked it, recording the deletion in a newer MANIFEST. Catch-up now keeps the column family's previous view until a complete state from the new MANIFEST is available.
* Custom index blocks now reuse cached contents on SST reopen, and deprecated UDI boolean options can be cleared without overriding an explicitly configured index mode.
Legacy UDI options copied from a factory can now be cleared before constructing a new factory without retaining the derived mode. The C API preserves both that rollback behavior and explicitly selected modes. OPTIONS files serialize equivalent legacy booleans for `kStandardDefault`, `kStandardRequired`, and `kCustomDefault` so older readers retain routing.
Empty custom-index output retains the standard-index fallback in modes that write both indexes; custom-only output must remain nonempty. Default primary reads retain the table factory's precedence over a legacy per-read factory pointer, while explicit custom selectors still check its name.
* Preserve legacy UDI routing when loading old OPTIONS files, and reject invalid index modes in standalone SST readers and writers. Table readers now charge the custom index blocks they own; a custom reader whose `ApproximateMemoryUsage()` already includes that block should override `IndexFactoryReader::MemoryUsageIncludesIndexBlock()` to return true so the block is not charged twice.
Keep cached custom-index Slice views intact when a reader consumes its input. Register custom-index cache helpers without requiring a later change. Trie index size estimates now account for shared prefixes, preventing premature compaction file cuts, and reject data-block offsets or sizes exceeding the 32-bit on-disk limit instead of truncating them.
### Performance Improvements
* With `ReadOptions::pin_data`, iterators over read-only DBs opened with `max_open_files = -1` and `allow_mmap_reads = true` no longer accumulate memory as they move across SST files.
* On a read-write primary, `DB::GetSnapshot()` and `DB::ReleaseSnapshot()` no longer acquire the DB mutex in the common case. Secondary and follower snapshot creation retains the DB mutex needed to synchronize with view installation, while release remains sharded. The snapshot list is now split into 16 per-CPU shards, each with its own lock, so primary threads creating and releasing snapshots no longer serialize on one lock or wait behind flush, compaction, or other work holding the DB mutex. Snapshot-release maintenance is deferred to existing flush, compaction, and periodic maintenance paths. Primary snapshot creation serializes with the DB-mutex-held phases of external file ingestion but can proceed while the MANIFEST write temporarily releases that mutex.
## 11.13.0 (10/21/2026)
### New Features
* Added `FileSystem::IsPreallocationSupported()`. A `FileSystem` that never preallocates file space can return false so that DB open skips reopening and truncating the last WAL file.
### Public API Changes
* Added the experimental `DBOptions::track_options_file_number_in_manifest`
rollout option (with C and Java bindings). The option is persisted in each
OPTIONS file. When enabled, RocksDB durably creates an OPTIONS file and then
commits its file number in MANIFEST. Column-family create/drop commits the
configuration snapshot and membership in one MANIFEST record. A newer tracked
OPTIONS file without a commit is ignored after a crash, while a newer OPTIONS
file written with tracking disabled retains the legacy highest-numbered-file
behavior. Batched column-family changes publish one snapshot; after partial
success it can be a safe superset that persisted-options APIs filter against
live MANIFEST membership. Ordinary `DB::Open` continues to use caller-supplied
options and does not depend on the operational snapshot.
* Added `EncodeEntry()`, `PackSequenceAndType()`, and `UnPackSequenceAndType()` in `rocksdb/utilities/types_util.h` to construct internal keys and pack or unpack their sequence numbers and entry types. `kInternalKeyFooterSize` exposes the footer size in bytes.
* Added `ldb update_manifest --rebind_options_file --options_file=OPTIONS-<number>` for offline repair of a committed MANIFEST OPTIONS file number. Operators must select a trusted snapshot that has MANIFEST tracking enabled and provide any external DB/CF allocation roots that are not serialized in OPTIONS.
### Behavior Changes
* With `max_bgerror_resume_count <= 0` (auto resume disabled), a retryable or file-scoped IO error during compaction or flush is now a hard background error that stops writes until `DB::Resume()` is called. Previously, compaction errors were silently retried, and flush errors with a healthy WAL were soft errors that let writes continue while flushes were stopped.
### Bug Fixes
* Fixed two more `SstFileManager` trash accounting leaks: retrying a failed trash deletion immediately (for example with rate limiting disabled) after the file had disappeared, and a trash file that was deleted but whose directory sync then failed. In both cases the file's size stayed in the total trash size and tracked size until restart.
* Fixed `SstFileManager` trash accounting leaking the size of a queued trash file that disappeared before deletion (for example because its directory was renamed). The leaked bytes stayed in the total trash size until restart, which could push every later deletion past `max_trash_db_ratio` and make it immediate instead of rate limited.
* Fixed `MultiGet` with `ReadOptions::async_io` returning `NotSupported` when io_uring initialization or an asynchronous read submission fails. It now checks async I/O support on the reading thread and falls back to synchronous reads when needed.
* Fixed subset checkpoints so their newest OPTIONS file lists only the column
families retained by the checkpoint. This applies with both legacy
highest-numbered OPTIONS selection and MANIFEST-tracked OPTIONS selection, and
prevents `LoadLatestOptions` from recreating column families that the subset
checkpoint intentionally dropped from its copied MANIFEST.
### Performance Improvements
* Perf context counters and timers no longer call the thread-local init function of `perf_context` on every update. With `kEnableCount`, a counter update costs about 60% less.
## 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.
@@ -867,7 +1156,7 @@ want to continue to use force enabling, they need to explicitly pass a `true` to
* Overload the API CreateColumnFamilyWithImport() to support creating ColumnFamily by importing multiple ColumnFamilies It requires that CFs should not overlap in user key range.
### Behavior Changes
* Change the default value for option `level_compaction_dynamic_level_bytes` to true. This affects users who use leveled compaction and do not set this option explicitly. These users may see additional background compactions following DB open. These compactions help to shape the LSM according to `level_compaction_dynamic_level_bytes` such that the size of each level Ln is approximately size of Ln-1 * `max_bytes_for_level_multiplier`. Turning on this option has other benefits too: see more detail in wiki: https://github.com/facebook/rocksdb/wiki/Leveled-Compaction#option-level_compaction_dynamic_level_bytes-and-levels-target-size and in option comment in advanced_options.h (#11525).
* Change the default value for option `level_compaction_dynamic_level_bytes` to true. This affects users who use leveled compaction and do not set this option explicitly. These users may see additional background compactions following DB open. These compactions help to shape the LSM according to `level_compaction_dynamic_level_bytes` such that the size of each level Ln is approximately size of Ln-1 * `max_bytes_for_level_multiplier`. Turning on this option has other benefits too: see more detail in wiki: https://github.com/meta/rocksdb/wiki/Leveled-Compaction#option-level_compaction_dynamic_level_bytes-and-levels-target-size and in option comment in advanced_options.h (#11525).
* For Leveled Compaction users, `CompactRange()` will now always try to compact to the last non-empty level. (#11468)
For Leveled Compaction users, `CompactRange()` with `bottommost_level_compaction = BottommostLevelCompaction::kIfHaveCompactionFilter` will behave similar to `kForceOptimized` in that it will skip files created during this manual compaction when compacting files in the bottommost level. (#11468)
* RocksDB will try to drop range tombstones during non-bottommost compaction when it is safe to do so. (#11459)
@@ -912,7 +1201,7 @@ For Leveled Compaction users, `CompactRange()` with `bottommost_level_compaction
* Remove unnecessary constructor for CompressionOptions.
### Behavior changes
* Changed default block cache size from an 8MB to 32MB LRUCache, which increases the default number of cache shards from 16 to 64. This change is intended to minimize cache mutex contention under stress conditions. See https://github.com/facebook/rocksdb/wiki/Block-Cache for more information.
* Changed default block cache size from an 8MB to 32MB LRUCache, which increases the default number of cache shards from 16 to 64. This change is intended to minimize cache mutex contention under stress conditions. See https://github.com/meta/rocksdb/wiki/Block-Cache for more information.
* For level compaction with `level_compaction_dynamic_level_bytes=true`, RocksDB now trivially moves levels down to fill LSM starting from bottommost level during DB open. See more in comments for option `level_compaction_dynamic_level_bytes` (#11321).
* User-provided `ReadOptions` take effect for more reads of non-`CacheEntryRole::kDataBlock` blocks.
* For level compaction with `level_compaction_dynamic_level_bytes=true`, RocksDB now drains unnecessary levels through background compaction automatically (#11340). This together with #11321 makes it automatic to migrate other compaction settings to level compaction with `level_compaction_dynamic_level_bytes=true`. In addition, a live DB that becomes smaller will now have unnecessary levels drained which can help to reduce read and space amp.
@@ -1076,7 +1365,7 @@ For Leveled Compaction users, `CompactRange()` with `bottommost_level_compaction
* Only periodic stats dumper triggered by options.stats_dump_period_sec will update stats interval. Ones triggered by DB::GetProperty() will not update stats interval and will report based on an interval since the last time stats dump period.
### Public API changes
* Make kXXH3 checksum the new default, because it is faster on common hardware, especially with kCRC32c affected by a performance bug in some versions of clang (https://github.com/facebook/rocksdb/issues/9891). DBs written with this new setting can be read by RocksDB 6.27 and newer.
* Make kXXH3 checksum the new default, because it is faster on common hardware, especially with kCRC32c affected by a performance bug in some versions of clang (https://github.com/meta/rocksdb/issues/9891). DBs written with this new setting can be read by RocksDB 6.27 and newer.
* Refactor the classes, APIs and data structures for block cache tracing to allow a user provided trace writer to be used. Introduced an abstract BlockCacheTraceWriter class that takes a structured BlockCacheTraceRecord. The BlockCacheTraceWriter implementation can then format and log the record in whatever way it sees fit. The default BlockCacheTraceWriterImpl does file tracing using a user provided TraceWriter. More details in rocksdb/includb/block_cache_trace_writer.h.
## 7.7.0 (09/18/2022)
@@ -1196,7 +1485,7 @@ For Leveled Compaction users, `CompactRange()` with `bottommost_level_compaction
* Fix a bug in which backup/checkpoint can include a WAL deleted by RocksDB.
* Fix a bug where concurrent compactions might cause unnecessary further write stalling. In some cases, this might cause write rate to drop to minimum.
* Fix a bug in Logger where if dbname and db_log_dir are on different filesystems, dbname creation would fail wrt to db_log_dir path returning an error and fails to open the DB.
* Fix a CPU and memory efficiency issue introduce by https://github.com/facebook/rocksdb/pull/8336 which made InternalKeyComparator configurable as an unintended side effect.
* Fix a CPU and memory efficiency issue introduce by https://github.com/meta/rocksdb/pull/8336 which made InternalKeyComparator configurable as an unintended side effect.
## Behavior Change
* In leveled compaction with dynamic levelling, level multiplier is not anymore adjusted due to oversized L0. Instead, compaction score is adjusted by increasing size level target by adding incoming bytes from upper levels. This would deprioritize compactions from upper levels if more data from L0 is coming. This is to fix some unnecessary full stalling due to drastic change of level targets, while not wasting write bandwidth for compaction while writes are overloaded.
@@ -1235,7 +1524,7 @@ For Leveled Compaction users, `CompactRange()` with `bottommost_level_compaction
* Add User Defined Timestamp in C API.
* `rocksdb_comparator_with_ts_create` to create timestamp aware comparator
* Put, Get, Delete, SingleDelete, MultiGet APIs has corresponding timestamp aware APIs with suffix `with_ts`
* And Add C API's for Transaction, SstFileWriter, Compaction as mentioned [here](https://github.com/facebook/rocksdb/wiki/User-defined-Timestamp-(Experimental))
* And Add C API's for Transaction, SstFileWriter, Compaction as mentioned [here](https://github.com/meta/rocksdb/wiki/User-defined-Timestamp-(Experimental))
* The contract for implementations of Comparator::IsSameLengthImmediateSuccessor has been updated to work around a design bug in `auto_prefix_mode`.
* The API documentation for `auto_prefix_mode` now notes some corner cases in which it returns different results than `total_order_seek`, due to design bugs that are not easily fixed. Users using built-in comparators and keys at least the size of a fixed prefix length are not affected.
* Obsoleted the NUM_DATA_BLOCKS_READ_PER_LEVEL stat and introduced the NUM_LEVEL_READ_PER_MULTIGET and MULTIGET_COROUTINE_COUNT stats
@@ -1466,7 +1755,7 @@ cache at some point (i.e, causing a cache full under `LRUCacheOptions::strict_ca
* Experimental: Add support for new APIs ReadAsync in FSRandomAccessFile that reads the data asynchronously and Poll API in FileSystem that checks if requested read request has completed or not. ReadAsync takes a callback function. Poll API checks for completion of read IO requests and should call callback functions to indicate completion of read requests.
## 6.29.0 (01/21/2022)
Note: The next release will be major release 7.0. See https://github.com/facebook/rocksdb/issues/9390 for more info.
Note: The next release will be major release 7.0. See https://github.com/meta/rocksdb/issues/9390 for more info.
### Public API change
* Added values to `TraceFilterType`: `kTraceFilterIteratorSeek`, `kTraceFilterIteratorSeekForPrev`, and `kTraceFilterMultiGet`. They can be set in `TraceOptions` to filter out the operation types after which they are named.
* Added `TraceOptions::preserve_write_order`. When enabled it guarantees write records are traced in the same order they are logged to WAL and applied to the DB. By default it is disabled (false) to match the legacy behavior and prevent regression.
@@ -1615,7 +1904,7 @@ Note: The next release will be major release 7.0. See https://github.com/faceboo
### Java API Changes
* Add Java API bindings for new integrated BlobDB options
* `keyMayExist()` supports ByteBuffer.
* Fix multiget throwing Null Pointer Exception for num of keys > 70k (https://github.com/facebook/rocksdb/issues/8039).
* Fix multiget throwing Null Pointer Exception for num of keys > 70k (https://github.com/meta/rocksdb/issues/8039).
## 6.25.0 (2021-09-20)
### Bug Fixes
@@ -2302,7 +2591,7 @@ Note: The next release will be major release 7.0. See https://github.com/faceboo
* Errors related to the retrieval of the compression dictionary are now propagated to the user.
* db_bench adds a "benchmark" stats_history, which prints out the whole stats history.
* Overload GetAllKeyVersions() to support non-default column family.
* Added new APIs ExportColumnFamily() and CreateColumnFamilyWithImport() to support export and import of a Column Family. https://github.com/facebook/rocksdb/issues/3469
* Added new APIs ExportColumnFamily() and CreateColumnFamilyWithImport() to support export and import of a Column Family. https://github.com/meta/rocksdb/issues/3469
* ldb sometimes uses a string-append merge operator if no merge operator is passed in. This is to allow users to print keys from a DB with a merge operator.
* Replaces old Registra with ObjectRegistry to allow user to create custom object from string, also add LoadEnv() to Env.
* Added new overload of GetApproximateSizes which gets SizeApproximationOptions object and returns a Status. The older overloads are redirecting their calls to this new method and no longer assert if the include_flags doesn't have either of INCLUDE_MEMTABLES or INCLUDE_FILES bits set. It's recommended to use the new method only, as it is more type safe and returns a meaningful status in case of errors.
@@ -2889,7 +3178,7 @@ if set to something > 0 user will see 2 changes in iterators behavior 1) only ke
## 4.8.0 (2016-05-02)
### Public API Change
* Allow preset compression dictionary for improved compression of block-based tables. This is supported for zlib, zstd, and lz4. The compression dictionary's size is configurable via CompressionOptions::max_dict_bytes.
* Delete deprecated classes for creating backups (BackupableDB) and restoring from backups (RestoreBackupableDB). Now, BackupEngine should be used for creating backups, and BackupEngineReadOnly should be used for restorations. For more details, see https://github.com/facebook/rocksdb/wiki/How-to-backup-RocksDB%3F
* Delete deprecated classes for creating backups (BackupableDB) and restoring from backups (RestoreBackupableDB). Now, BackupEngine should be used for creating backups, and BackupEngineReadOnly should be used for restorations. For more details, see https://github.com/meta/rocksdb/wiki/How-to-backup-RocksDB%3F
* Expose estimate of per-level compression ratio via DB property: "rocksdb.compression-ratio-at-levelN".
* Added EventListener::OnTableFileCreationStarted. EventListener::OnTableFileCreated will be called on failure case. User can check creation status via TableFileCreationInfo::status.
@@ -2899,7 +3188,7 @@ if set to something > 0 user will see 2 changes in iterators behavior 1) only ke
## 4.7.0 (2016-04-08)
### Public API Change
* rename options compaction_measure_io_stats to report_bg_io_stats and include flush too.
* Change some default options. Now default options will optimize for server-workloads. Also enable slowdown and full stop triggers for pending compaction bytes. These changes may cause sub-optimal performance or significant increase of resource usage. To avoid these risks, users can open existing RocksDB with options extracted from RocksDB option files. See https://github.com/facebook/rocksdb/wiki/RocksDB-Options-File for how to use RocksDB option files. Or you can call Options.OldDefaults() to recover old defaults. DEFAULT_OPTIONS_HISTORY.md will track change history of default options.
* Change some default options. Now default options will optimize for server-workloads. Also enable slowdown and full stop triggers for pending compaction bytes. These changes may cause sub-optimal performance or significant increase of resource usage. To avoid these risks, users can open existing RocksDB with options extracted from RocksDB option files. See https://github.com/meta/rocksdb/wiki/RocksDB-Options-File for how to use RocksDB option files. Or you can call Options.OldDefaults() to recover old defaults. DEFAULT_OPTIONS_HISTORY.md will track change history of default options.
## 4.6.0 (2016-03-10)
### Public API Changes
@@ -3196,7 +3485,7 @@ Because of that, Options::disable_seek_compaction is now obsolete. It is still a
### Public API changes
* Added _LEVEL to all InfoLogLevel enums
* Deprecated ReadOptions.prefix and ReadOptions.prefix_seek. Seek() defaults to prefix-based seek when Options.prefix_extractor is supplied. More detail is documented in https://github.com/facebook/rocksdb/wiki/Prefix-Seek-API-Changes
* Deprecated ReadOptions.prefix and ReadOptions.prefix_seek. Seek() defaults to prefix-based seek when Options.prefix_extractor is supplied. More detail is documented in https://github.com/meta/rocksdb/wiki/Prefix-Seek-API-Changes
* MemTableRepFactory::CreateMemTableRep() takes info logger as an extra parameter.
### New Features
@@ -3247,8 +3536,8 @@ Because of that, Options::disable_seek_compaction is now obsolete. It is still a
### New Features
* Added [BackupableDB](https://github.com/facebook/rocksdb/wiki/How-to-backup-RocksDB%3F)
* Implemented [TailingIterator](https://github.com/facebook/rocksdb/wiki/Tailing-Iterator), a special type of iterator that
* Added [BackupableDB](https://github.com/meta/rocksdb/wiki/How-to-backup-RocksDB%3F)
* Implemented [TailingIterator](https://github.com/meta/rocksdb/wiki/Tailing-Iterator), a special type of iterator that
doesn't create a snapshot (can be used to read newly inserted data)
and is optimized for doing sequential reads.
* Added property block for table, which allows (1) a table to store
@@ -3265,8 +3554,8 @@ Because of that, Options::disable_seek_compaction is now obsolete. It is still a
* Huge benchmark performance improvements by multiple efforts. For example, increase in readonly QPS from about 530k in 2.6 release to 1.1 million in 2.7 [1]
* Speeding up a way RocksDB deleted obsolete files - no longer listing the whole directory under a lock -- decrease in p99
* Use raw pointer instead of shared pointer for statistics: [5b825d](https://github.com/facebook/rocksdb/commit/5b825d6964e26ec3b4bb6faa708ebb1787f1d7bd) -- huge increase in performance -- shared pointers are slow
* Optimized locking for `Get()` -- [1fdb3f](https://github.com/facebook/rocksdb/commit/1fdb3f7dc60e96394e3e5b69a46ede5d67fb976c) -- 1.5x QPS increase for some workloads
* Cache speedup - [e8d40c3](https://github.com/facebook/rocksdb/commit/e8d40c31b3cca0c3e1ae9abe9b9003b1288026a9)
* Implemented autovector, which allocates first N elements on stack. Most of vectors in RocksDB are small. Also, we never want to allocate heap objects while holding a mutex. -- [c01676e4](https://github.com/facebook/rocksdb/commit/c01676e46d3be08c3c140361ef1f5884f47d3b3c)
* Use raw pointer instead of shared pointer for statistics: [5b825d](https://github.com/meta/rocksdb/commit/5b825d6964e26ec3b4bb6faa708ebb1787f1d7bd) -- huge increase in performance -- shared pointers are slow
* Optimized locking for `Get()` -- [1fdb3f](https://github.com/meta/rocksdb/commit/1fdb3f7dc60e96394e3e5b69a46ede5d67fb976c) -- 1.5x QPS increase for some workloads
* Cache speedup - [e8d40c3](https://github.com/meta/rocksdb/commit/e8d40c31b3cca0c3e1ae9abe9b9003b1288026a9)
* Implemented autovector, which allocates first N elements on stack. Most of vectors in RocksDB are small. Also, we never want to allocate heap objects while holding a mutex. -- [c01676e4](https://github.com/meta/rocksdb/commit/c01676e46d3be08c3c140361ef1f5884f47d3b3c)
* Lots of efforts to move malloc, memcpy and IO outside of locks
+2 -13
View File
@@ -153,7 +153,7 @@ most processors made since roughly 2013.
* Build RocksDB from source:
cd ~
git clone https://github.com/facebook/rocksdb.git
git clone https://github.com/meta/rocksdb.git
cd rocksdb
gmake static_lib
@@ -174,7 +174,7 @@ most processors made since roughly 2013.
```bash
cd ~
git clone https://github.com/facebook/rocksdb.git
git clone https://github.com/meta/rocksdb.git
cd rocksdb
gmake static_lib
```
@@ -211,14 +211,3 @@ most processors made since roughly 2013.
export LIBPATH=/opt/freeware/lib
export JAVA_HOME=/usr/java8_64
export PATH=/opt/freeware/bin:$PATH
* **Solaris Sparc**
* Install GCC 11 and higher.
* Use these environment variables:
export CC=gcc
export EXTRA_CFLAGS=-m64
export EXTRA_CXXFLAGS=-m64
export EXTRA_LDFLAGS=-m64
export PORTABLE=1
export PLATFORM_LDFLAGS="-static-libstdc++ -static-libgcc"
+1 -1
View File
@@ -1,6 +1,6 @@
This is the list of all known third-party language bindings for RocksDB. If something is missing, please open a pull request to add it.
* Java - https://github.com/facebook/rocksdb/tree/main/java
* Java - https://github.com/meta/rocksdb/tree/main/java
* Python
* https://github.com/rocksdict/RocksDict
* http://python-rocksdb.readthedocs.io/en/latest/ (unmaintained)
+323 -48
View File
@@ -86,11 +86,20 @@ else
LIB_MODE?=shared
endif
$(info $$DEBUG_LEVEL is $(DEBUG_LEVEL), $$LIB_MODE is $(LIB_MODE))
# Set by the gen_check_scripts wrappers to mark a recursive make reusing the
# parent's make_config.mk: silence the parse-time banners and skip
# build_detect_platform (below).
$(if $(ROCKSDB_RECURSIVE_MAKE_SAME_CONFIG),,$(info $$DEBUG_LEVEL is $(DEBUG_LEVEL), $$LIB_MODE is $(LIB_MODE)))
# Detect what platform we're building on.
# Export some common variables that might have been passed as Make variables
# instead of environment variables.
#
# Skipped by the gen_check_scripts wrappers (which run concurrently under
# gnu_parallel): each would otherwise rewrite the shared make_config.mk
# non-atomically and a concurrent reader could catch it mid-write ("missing
# separator"). Requires an existing make_config.mk, so a normal build still detects.
ifeq ($(and $(ROCKSDB_RECURSIVE_MAKE_SAME_CONFIG),$(wildcard make_config.mk)),)
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
export CXXFLAGS="$(EXTRA_CXXFLAGS)"; \
export LDFLAGS="$(EXTRA_LDFLAGS)"; \
@@ -104,8 +113,9 @@ dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
export USE_FOLLY="$(USE_FOLLY)"; \
export USE_FOLLY_LITE="$(USE_FOLLY_LITE)"; \
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
# this file is generated by the previous line to set build flags and sources
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk" $(if $(ROCKSDB_RECURSIVE_MAKE_SAME_CONFIG),2>/dev/null)))
endif
# this file (generated above, or reused when skipped) sets build flags and sources
include make_config.mk
# Figure out optimize level.
@@ -181,7 +191,7 @@ ifeq ($(filter -DROCKSDB_ASSERT_STATUS_CHECKED,$(OPT)),)
endif
endif
$(warning Warning: Compiling in debug mode. Don't use the resulting binary in production)
$(if $(ROCKSDB_RECURSIVE_MAKE_SAME_CONFIG),,$(warning Warning: Compiling in debug mode. Don't use the resulting binary in production))
endif
# `USE_LTO=1` enables link-time optimizations. Among other things, this enables
@@ -288,7 +298,7 @@ endif
endif
export JAVAC_ARGS
CLEAN_FILES += make_config.mk rocksdb.pc
CLEAN_FILES += rocksdb.pc
ifeq ($(V), 1)
$(info $(shell uname -a))
@@ -345,10 +355,6 @@ ARFLAGS = -X64 rs
STRIPFLAGS = -X64 -x
endif
ifeq ($(PLATFORM), OS_SOLARIS)
PLATFORM_CXXFLAGS += -D _GLIBCXX_USE_C99
endif
ifeq ($(LIB_MODE),shared)
# So that binaries are executable from build location, in addition to install location
EXEC_LDFLAGS += -Wl,-rpath -Wl,'$$ORIGIN'
@@ -579,9 +585,13 @@ TESTS += $(PLUGIN_TESTS)
ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
# TODO: add/support JNI headers
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
FOLLY_DEPENDENT_HEADERS := \
include/rocksdb/coro_db.h \
include/rocksdb/coro_external_table.h \
include/rocksdb/utilities/coro_stackable_db.h
# Some headers like in port/ are platform-specific
DEV_HEADERS_TO_CHECK := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|range_tree/|secondary_index/')
PUBLIC_HEADERS_TO_CHECK := $(shell $(FIND) include/ -type f -name '*.h')
DEV_HEADERS_TO_CHECK := $(filter-out $(FOLLY_DEPENDENT_HEADERS), $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|range_tree/|secondary_index/'))
PUBLIC_HEADERS_TO_CHECK := $(filter-out $(FOLLY_DEPENDENT_HEADERS), $(shell $(FIND) include/ -type f -name '*.h'))
else
DEV_HEADERS_TO_CHECK :=
PUBLIC_HEADERS_TO_CHECK :=
@@ -650,6 +660,7 @@ TESTS_PLATFORM_DEPENDENT := \
dynamic_bloom_test \
c_test \
checkpoint_test \
live_tuner_test \
sorted_run_builder_test \
crc32c_test \
coding_test \
@@ -764,7 +775,6 @@ util/build_version.cc: $(filter-out $(OBJ_DIR)/util/build_version.o, $(LIB_OBJEC
$(AM_V_at)$(gen_build_version) > $@
endif
CLEAN_FILES += util/build_version.cc
default: all
#-----------------------------------------------
@@ -809,7 +819,8 @@ endif # PLATFORM_SHARED_EXT
.PHONY: check clean coverage ldb_tests package dbg gen-pc build_size \
release tags tags0 valgrind_check format static_lib shared_lib all \
rocksdbjavastatic rocksdbjava install install-static install-shared \
uninstall analyze tools tools_lib check-headers checkout_folly clang-tidy
uninstall analyze tools tools_lib check-headers checkout_folly clang-tidy \
check-c-api-c_test
# Auto-configure git hooks on first build so developers do not need to run
# "make install-hooks" manually. This is a no-op if already set.
@@ -949,6 +960,57 @@ gen_parallel_tests:
$(AM_V_at)$(FIND) t -type f -name 'run-*' -exec rm -f {} \;
$(MAKE) $(parallel_tests)
# Auxiliary (non-unit-test) checks that are part of `make check`. Kept as a
# single list so the same set can run as parallel jobs alongside the C++ unit
# tests (see gen_check_scripts + check_0) and serially in the J=1 fallback.
CHECK_EXTRA_TARGETS =
ifneq ($(PLATFORM), OS_AIX)
CHECK_EXTRA_TARGETS += check-all-python
ifndef ASSERT_STATUS_CHECKED # not yet working with these tests
CHECK_EXTRA_TARGETS += ldb_tests db_crashtest_tests rocksdb_dump_test
endif
endif
ifndef SKIP_FORMAT_BUCK_CHECKS
CHECK_EXTRA_TARGETS += check-format check-buck-targets check-sources check-workflow-yaml check-c-api-gen
endif
# Generate a t/run-<target> wrapper per enabled auxiliary check; check_0's
# `$(FIND) t -name 'run-*'` then runs them as gnu_parallel jobs alongside the
# unit tests, each framed with a gtest-style pass/fail line. Under CI sharding,
# round-robin across shards so each check runs on exactly one shard. Kept
# separate from gen_parallel_tests (which cleans t/run-* first) so the valgrind_*
# targets, which also glob t/run-*, don't pick these up.
.PHONY: gen_check_scripts
gen_check_scripts:
$(AM_V_at)mkdir -p t; \
idx=0; \
for tgt in $(CHECK_EXTRA_TARGETS); do \
if [ -z "$(CI_TOTAL_SHARDS)" ] || [ $$(( idx % $(CI_TOTAL_SHARDS) )) -eq $(CI_SHARD_INDEX) ]; then \
s=t/run-$$tgt; \
printf '%s\n' \
'#!/bin/sh' \
'# Generated by `make gen_check_scripts`.' \
"t=$$tgt" \
'# Drop the parent jobserver from MAKEFLAGS (its fds are lost through' \
'# gnu_parallel, causing a spurious warning); keep overrides after "--".' \
'MAKEFLAGS=$$(printf "%s" "$$MAKEFLAGS" | sed -e "s/ --jobserver-auth=[^ ]*//g" -e "s/ --jobserver-fds=[^ ]*//g" -e "s/ -j[0-9]*//g")' \
'export MAKEFLAGS' \
'echo; echo "[----------]"; echo "[ RUN ] >>> make $$t <<<"' \
'$(MAKE) --no-print-directory ROCKSDB_RECURSIVE_MAKE_SAME_CONFIG=1 "$$t"' \
'rc=$$?' \
'if [ "$$rc" = 0 ]; then' \
' echo "[ OK ] >>> make $$t <<<"' \
'else' \
' echo "[ FAILED ] >>> make $$t <<<"' \
'fi' \
'echo "[----------]"' \
'exit $$rc' \
> $$s; \
chmod a=rx $$s; \
fi; \
idx=$$((idx + 1)); \
done
# Reorder input lines (one per test or per shard) so the longest-running
# tests/shards start first under "make check" parallel scheduling. The trick
# is to prefix matched names with a fake duration (100), sort numerically
@@ -976,7 +1038,8 @@ gen_parallel_tests:
slow_test_regexp = \
^.*point_lock_manager_stress_test.*$$|^.*db_test.*$$|^.*external_sst_file_test.*$$|^.*compaction_service_test.*$$|^.*corruption_test.*$$|^.*comparator_db_test.*$$|^.*external_sst_file_basic_test.*$$|^.*rate_limiter_test.*$$|^.*db_compaction_test.*$$|^.*write_prepared_transaction_test.*$$|^.*db_merge_operator_test.*$$|\
^.*db_dynamic_level_test.*$$|^.*db_bloom_filter_test.*$$|^.*error_handler_fs_test.*$$|^.*merge_helper_test.*$$|^.*transaction_test.*$$|^.*db_kv_checksum_test.*$$|^.*inlineskiplist_test.*$$|\
^.*db_with_timestamp_basic_test.*$$|^.*table_test.*$$|^.*db_wal_test.*$$|^.*block_based_table_reader_test.*$$|^.*block_test.*$$
^.*db_with_timestamp_basic_test.*$$|^.*table_test.*$$|^.*db_wal_test.*$$|^.*block_based_table_reader_test.*$$|^.*block_test.*$$|\
^.*run-check-c-api-gen.*$$|^.*run-check-format.*$$
prioritize_long_running_tests = \
perl -pe 's,($(slow_test_regexp)),100 $$1,' \
| sort -k1,1gr \
@@ -1108,26 +1171,110 @@ check: all
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
grep -q 'GNU Parallel'; \
then \
$(MAKE) gen_check_scripts; \
$(MAKE) T="$$t" check_0; \
else \
for t in $(TESTS); do \
echo "===== Running $$t (`date`)"; ./$$t || exit 1; done; \
for t in $(CHECK_EXTRA_TARGETS); do \
echo "===== Running $$t (`date`)"; $(MAKE) $$t || exit 1; done; \
fi
rm -rf $(TEST_TMPDIR)
ifneq ($(PLATFORM), OS_AIX)
$(PYTHON) tools/check_all_python.py
ifndef ASSERT_STATUS_CHECKED # not yet working with these tests
$(PYTHON) tools/ldb_test.py
$(PYTHON) tools/db_crashtest_test.py
sh tools/rocksdb_dump_test.sh
endif
endif
ifndef SKIP_FORMAT_BUCK_CHECKS
$(MAKE) check-format
$(MAKE) check-buck-targets
$(MAKE) check-sources
$(MAKE) check-workflow-yaml
endif
# NOTE: ^^^^^^^^ don't add new auxiliary checks here, but instead to
# CHECK_EXTRA_TARGETS
# Check that the auto-generated C API files are up to date. It regenerates the
# fragments and the inlined c.h/c.cc and compares them against a snapshot of the
# checked-in copies (no net change when everything is up to date). It requires
# clang++ (libclang, used to parse the C++ headers) and clang-format. When those
# are unavailable the staleness/compat sub-checks are SKIPPED with a message so
# `make check` still works without the codegen toolchain; the link-completeness
# sub-check always runs (it needs no toolchain).
#
# Pin the formatter to match CI by setting CLANG_FORMAT_BINARY, e.g.:
# make check-c-api-gen CLANG_FORMAT_BINARY=clang-format-21
# This target is part of `make check` and is skipped by SKIP_FORMAT_BUCK_CHECKS.
#
# Set CHECK_C_API_GEN_STRICT=1 to turn every "skip" below into a hard error, so a
# core CI job that runs this target cannot silently regress to a no-op if a
# prerequisite (clang++, or the compat baseline ref) goes missing. The dedicated
# build-linux-clang-21-no_test_run CI job runs this target with the flag set.
# Any non-empty value other than 0/no/false enables strict mode.
CLANG_FORMAT_BINARY ?=
# Backward-compatibility baseline for the C API (signature-level) check. CI
# overrides this with the PR's merge target or the branch's pre-push tip;
# locally it falls back to main or origin/main and is skipped if neither resolves.
API_COMPAT_REF ?= main
CHECK_C_API_GEN_STRICT ?=
check-c-api-gen:
# Link-completeness is a property of the checked-in c.h/c.cc and needs no
# clang toolchain, so it always runs: every declared public C API function
# must have exactly one definition (guards against dropped wrappers that
# would break downstream language bindings at link time).
$(PYTHON) tools/c_api_gen/check_api_completeness.py
# Backward-compatibility: no public C function may be removed or have its
# signature changed vs the baseline. Skipped if the baseline ref is not
# resolvable locally (CI passes an explicit ref), unless CHECK_C_API_GEN_STRICT.
@strict=""; case "$(CHECK_C_API_GEN_STRICT)" in ""|0|no|NO|false|FALSE) ;; *) strict=1 ;; esac; \
ref=""; \
if git rev-parse --verify --quiet "$(API_COMPAT_REF)^{commit}" >/dev/null; then ref="$(API_COMPAT_REF)"; \
elif git rev-parse --verify --quiet "origin/$(API_COMPAT_REF)^{commit}" >/dev/null; then ref="origin/$(API_COMPAT_REF)"; fi; \
if [ -n "$$ref" ]; then \
$(PYTHON) tools/c_api_gen/check_api_compatibility.py --ref "$$ref"; \
elif [ -n "$$strict" ]; then \
echo "ERROR: C API compat baseline '$(API_COMPAT_REF)' not resolvable and CHECK_C_API_GEN_STRICT is set" >&2; exit 1; \
else \
echo "Skipping C API backward-compatibility check ($(API_COMPAT_REF) not found; set API_COMPAT_REF)"; \
fi
# Staleness: regenerate and confirm the checked-in output is current. Needs a
# clang++ (the generator parses C++ ASTs). Use CLANG_CXX, which
# build_tools/build_detect_platform resolves once (the fbcode clang++ in
# fbcode builds, else a versioned clang++ on PATH) and passes explicitly, so
# the check and the generator agree on the same binary without heuristics.
@strict=""; case "$(CHECK_C_API_GEN_STRICT)" in ""|0|no|NO|false|FALSE) ;; *) strict=1 ;; esac; \
cf_arg=""; \
if [ -n "$(CLANG_FORMAT_BINARY)" ]; then cf_arg="--clang-format $(CLANG_FORMAT_BINARY)"; fi; \
if [ -n "$(CLANG_CXX)" ] && command -v "$(CLANG_CXX)" >/dev/null 2>&1; then \
$(PYTHON) tools/c_api_gen/verify_generated_up_to_date.py --clang "$(CLANG_CXX)" $$cf_arg; \
elif [ -n "$$strict" ]; then \
echo "ERROR: no clang++ found (CLANG_CXX unset) and CHECK_C_API_GEN_STRICT is set; cannot run the C API staleness check" >&2; exit 1; \
else \
echo "Skipping C API codegen staleness check (no clang++ found; CLANG_CXX unset, install clang++ to enable)"; \
fi
# Regenerate the checked-in C API generated files (fragments under c_api_gen/,
# the inlined include/rocksdb/c.h and db/c.cc, and the round-trip tests). Uses
# CLANG_CXX (resolved by build_tools/build_detect_platform) so it works in a gcc
# build too. Aborts up front if no usable clang++ is available, so a bad
# toolchain never leaves the tree in a partially regenerated state. Pin the
# formatter to match CI with CLANG_FORMAT_BINARY, e.g.:
# make regen-c-api CLANG_FORMAT_BINARY=clang-format-21
.PHONY: regen-c-api
regen-c-api:
@if [ -z "$(CLANG_CXX)" ] || ! command -v "$(CLANG_CXX)" >/dev/null 2>&1; then \
echo "ERROR: no clang++ found (CLANG_CXX unset); build on a host with clang++ or the fbcode toolchain to regenerate the C API" >&2; exit 1; \
fi
@cf_arg=""; \
if [ -n "$(CLANG_FORMAT_BINARY)" ]; then cf_arg="--clang-format $(CLANG_FORMAT_BINARY)"; fi; \
$(PYTHON) tools/c_api_gen/regen_all.py --clang "$(CLANG_CXX)" $$cf_arg
# Quick local validation for C API generation plus the focused C API test.
# This verifies the checked-in generated fragments as well as the inlined
# include/rocksdb/c.h and db/c.cc outputs, then runs c_test in an isolated
# TEST_TMPDIR to avoid stale-state failures.
check-c-api-c_test:
$(PYTHON) tools/c_api_gen/verify_generated_up_to_date.py --clang "$(CLANG_CXX)"
$(MAKE) c_test
@tmpdir=$$(mktemp -d); \
trap 'rm -rf "$$tmpdir"' EXIT; \
echo "===== Running c_test with TEST_TMPDIR=$$tmpdir"; \
if command -v timeout >/dev/null 2>&1; then \
TEST_TMPDIR="$$tmpdir" timeout 60 ./c_test; \
elif command -v gtimeout >/dev/null 2>&1; then \
TEST_TMPDIR="$$tmpdir" gtimeout 60 ./c_test; \
else \
TEST_TMPDIR="$$tmpdir" ./c_test; \
fi
# TODO add ldb_tests
check_some: $(ROCKSDBTESTS_SUBSET)
@@ -1141,6 +1288,14 @@ ldb_tests: ldb
db_crashtest_tests:
$(PYTHON) tools/db_crashtest_test.py
.PHONY: check-all-python
check-all-python:
$(PYTHON) tools/check_all_python.py
.PHONY: rocksdb_dump_test
rocksdb_dump_test: rocksdb_dump rocksdb_undump
sh tools/rocksdb_dump_test.sh
include crash_test.mk
asan_check: clean
@@ -1278,8 +1433,13 @@ rocksdb.h rocksdb.cc: build_tools/amalgamate.py Makefile $(LIB_SOURCES) unity.cc
build_tools/amalgamate.py -I. -i./include unity.cc -x include/rocksdb/c.h -H rocksdb.h -o rocksdb.cc
clean: clean-ext-libraries-all clean-rocks clean-rocksjava
# Removed here rather than in clean-rocks (via CLEAN_FILES) so the build-parameter
# auto-clean, which runs clean-rocks, doesn't delete the make_config.mk already
# included by the in-progress make.
@rm -f make_config.mk
clean-not-downloaded: clean-ext-libraries-bin clean-rocks clean-not-downloaded-rocksjava
@rm -f make_config.mk
clean-rocks:
# Not practical to exactly match all versions/variants in naming (e.g. debug or not)
@@ -1294,6 +1454,11 @@ clean-rocks:
else \
$(FIND) . -name "*.[oda]" -exec rm -f {} \; ; \
fi
# Remove the build signature(s) LAST. Done after the object files are gone so
# that a Ctrl+C partway through clean leaves the old signature in place: it
# still matches the leftover objects, so a later build with different
# parameters is still detected (signature usefulness is preserved).
@rm -f $(BUILD_SIG_FILE) jl/.build_signature jls/.build_signature
clean-rocksjava: clean-rocks
rm -rf jl jls
@@ -1566,6 +1731,9 @@ db_readonly_with_timestamp_test: $(OBJ_DIR)/db/db_readonly_with_timestamp_test.o
db_wide_basic_test: $(OBJ_DIR)/db/wide/db_wide_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
db_lazy_entity_test: $(OBJ_DIR)/db/wide/db_lazy_entity_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
db_wide_blob_direct_write_test: $(OBJ_DIR)/db/wide/db_wide_blob_direct_write_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
@@ -1725,6 +1893,15 @@ backup_engine_test: $(OBJ_DIR)/utilities/backup/backup_engine_test.o $(TEST_LIBR
checkpoint_test: $(OBJ_DIR)/utilities/checkpoint/checkpoint_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
live_tuner_test: $(OBJ_DIR)/utilities/live_tune/live_tuner_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
db_split_merge_test: $(OBJ_DIR)/utilities/db_split_merge_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
copy_engine_test: $(OBJ_DIR)/utilities/copy_engine/copy_engine_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
sorted_run_builder_test: $(OBJ_DIR)/utilities/sorted_run_builder/sorted_run_builder_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
@@ -1845,6 +2022,9 @@ block_fetcher_test: table/block_fetcher_test.o $(TEST_LIBRARY) $(LIBRARY)
block_test: $(OBJ_DIR)/table/block_based/block_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
builtin_index_factory_test: $(OBJ_DIR)/table/block_based/builtin_index_factory_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
data_block_hash_index_test: $(OBJ_DIR)/table/block_based/data_block_hash_index_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_LINK)
@@ -2031,6 +2211,9 @@ ldb_cmd_test: $(OBJ_DIR)/tools/ldb_cmd_test.o $(TOOLS_LIBRARY) $(TEST_LIBRARY) $
ldb: $(OBJ_DIR)/tools/ldb.o $(TOOLS_LIBRARY) $(LIBRARY)
$(AM_LINK)
db_split_merge: $(OBJ_DIR)/tools/db_split_merge.o $(LIBRARY)
$(AM_LINK)
iostats_context_test: $(OBJ_DIR)/monitoring/iostats_context_test.o $(TEST_LIBRARY) $(LIBRARY)
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
@@ -2230,9 +2413,7 @@ gen-pc:
# Jni stuff
# ---------------------------------------------------------------------------
JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
ifeq ($(PLATFORM), OS_SOLARIS)
ARCH := $(shell isainfo -b)
else ifeq ($(PLATFORM), OS_OPENBSD)
ifeq ($(PLATFORM), OS_OPENBSD)
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 riscv64 sparc64 loongarch64, $(MACHINE)))
ARCH := 64
else
@@ -2267,22 +2448,27 @@ ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar
SHA256_CMD = sha256sum
# The *_DOWNLOAD_PREFIX defaults below point at the upstream projects; CI
# overrides them to a mirror on the "third-party-deps" GitHub release (see
# .github/actions/pre-steps/action.yml). When bumping a version, see
# instructions in CI config comments for how to update that release.
ZLIB_VER ?= 1.3.1
ZLIB_SHA256 ?= 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
ZLIB_DOWNLOAD_PREFIX ?= http://zlib.net/zlib-
BZIP2_VER ?= 1.0.8
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
BZIP2_DOWNLOAD_PREFIX ?= http://sourceware.org/pub/bzip2/bzip2-
SNAPPY_VER ?= 1.2.2
SNAPPY_SHA256 ?= 90f74bc1fbf78a6c56b3c4a082a05103b3a56bb17bca1a27e052ea11723292dc
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
SNAPPY_DOWNLOAD_PREFIX ?= https://github.com/google/snappy/archive/
LZ4_VER ?= 1.10.0
LZ4_SHA256 ?= 537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
LZ4_DOWNLOAD_PREFIX ?= https://github.com/lz4/lz4/archive/v
ZSTD_VER ?= 1.5.7
ZSTD_SHA256 ?= 37d7284556b20954e56e1ca85b80226768902e2edabd3b649e9e72c0c9012ee3
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
CURL_SSL_OPTS ?= --tlsv1
ZSTD_DOWNLOAD_PREFIX ?= https://github.com/facebook/zstd/archive/v
# curl with the common options used to fetch the pinned third-party sources above
CURL_DOWNLOAD = curl --fail --location --retry 3 --retry-all-errors
ifeq ($(PLATFORM), OS_MACOSX)
ifeq (,$(findstring librocksdbjni-osx,$(ROCKSDBJNILIB)))
@@ -2308,12 +2494,6 @@ ifeq ($(PLATFORM), OS_FREEBSD)
ROCKSDBJNILIB = librocksdbjni-freebsd$(ARCH).so
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-freebsd$(ARCH).jar
endif
ifeq ($(PLATFORM), OS_SOLARIS)
ROCKSDBJNILIB = librocksdbjni-solaris$(ARCH).so
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-solaris$(ARCH).jar
JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/solaris
SHA256_CMD = digest -a sha256
endif
ifeq ($(PLATFORM), OS_AIX)
JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/aix
ROCKSDBJNILIB = librocksdbjni-aix.so
@@ -2327,8 +2507,103 @@ ifeq ($(PLATFORM), OS_OPENBSD)
endif
export SHA256_CMD
# ----------------------------------------------------------------------------
# Build parameter change detection
# ----------------------------------------------------------------------------
# RocksDB writes object files to the same $(OBJ_DIR) paths regardless of
# DEBUG_LEVEL, sanitizers (ASAN/TSAN/UBSAN), ASSERT_STATUS_CHECKED, RTTI, LTO,
# COERCE_CONTEXT_SWITCH, etc. Most of those are applied in this Makefile after
# `include make_config.mk` and are therefore NOT reflected in make_config.mk,
# so switching them and rebuilding silently mixes incompatible object files.
# To guard against that, we hash the fully-resolved compile/link parameters
# (which already embed make_config.mk via PLATFORM_*) and compare against the
# value recorded from the previous build. On mismatch the build stops so the
# user can `make clean`. Knobs:
# AUTO_CLEAN=1
# run `make clean-rocks` automatically on mismatch
# ALLOW_BUILD_PARAMETER_CHANGE=1
# skip the check entirely (e.g. intentionally mixing DEBUG_LEVEL=1 and
# DEBUG_LEVEL=2)
# The signature is stored per-$(OBJ_DIR), so Java (jl/jls) builds are tracked
# independently from the default build.
BUILD_SIG_FILE := $(OBJ_DIR)/.build_signature
# NOTE: deliberately NOT added to CLEAN_FILES. The signature is removed as the
# very last step of clean-rocks so that an interrupted clean keeps it (see
# clean-rocks), preserving change detection across a Ctrl+C'd clean.
# Goals that do not compile object files into the tree (pure utilities,
# informational, source/config checks): the change check is irrelevant for them.
BUILD_SIG_NONBUILD_GOALS := \
clean clean-rocks clean-not-downloaded clean-rocksjava \
clean-not-downloaded-rocksjava clean-ext-libraries-all \
clean-ext-libraries-bin \
format format-auto check-format check-buck-targets check-headers \
check-sources check-workflow-yaml check-progress clang-tidy \
tags tags0 package jclean checkout_folly build_folly \
watch-log dump-log suggest-slow-tests list_all_tests gen-pc \
gen_parallel_tests gen_check_scripts check-c-api-gen check-all-python \
setup-hooks install-hooks uninstall-hooks uninstall db_crashtest_tests
# Goals that clean before building (depend on or invoke `clean`): they manage
# their own freshness, so the check must not block them (it would error before
# their built-in clean runs).
BUILD_SIG_NONBUILD_GOALS += \
release coverage build_size analyze analyze_incremental \
asan_check asan_crash_test asan_crash_test_with_atomic_flush \
asan_crash_test_with_txn asan_crash_test_with_best_efforts_recovery \
whitebox_asan_crash_test blackbox_asan_crash_test \
ubsan_check ubsan_crash_test ubsan_crash_test_with_atomic_flush \
ubsan_crash_test_with_txn ubsan_crash_test_with_best_efforts_recovery \
whitebox_ubsan_crash_test blackbox_ubsan_crash_test
# Goals that explicitly clean; never trip the check when one is requested.
BUILD_SIG_CLEAN_GOALS := \
clean clean-rocks clean-not-downloaded clean-rocksjava \
clean-not-downloaded-rocksjava clean-ext-libraries-all \
clean-ext-libraries-bin
BUILD_SIG_GOALS := $(if $(MAKECMDGOALS),$(MAKECMDGOALS),all)
BUILD_SIG_DO_BUILD := $(filter-out $(BUILD_SIG_NONBUILD_GOALS),$(BUILD_SIG_GOALS))
BUILD_SIG_CLEANING := $(filter $(BUILD_SIG_CLEAN_GOALS),$(MAKECMDGOALS))
# Dry runs (-n/--just-print) must not write the stamp, clean, or error; the
# parse-time $(shell) below would otherwise run even under -n. Note that
# actual options are canonicalized and shorted as possible such that all
# short options are in the first word of MAKEFLAGS.
BUILD_SIG_DRYRUN := $(findstring n,$(firstword -$(MAKEFLAGS)))
# Only enforce at the top level. Sub-makes (e.g. `check` invokes
# $(MAKE) gen_parallel_tests / check-c-api-gen / check_0) inherit the parent's
# build flags, so the top-level check already covers them; re-checking inside a
# sub-make only causes spurious failures.
ifneq ($(ALLOW_BUILD_PARAMETER_CHANGE),1)
ifeq ($(MAKELEVEL),0)
ifeq ($(BUILD_SIG_DRYRUN),)
ifeq ($(BUILD_SIG_CLEANING),)
ifneq ($(BUILD_SIG_DO_BUILD),)
BUILD_SIG := $(shell printf '%s' '$(CC)|$(CXX)|$(CFLAGS)|$(CXXFLAGS)|$(LDFLAGS)|$(EXEC_LDFLAGS)' | $(SHA256_CMD) | cut -d ' ' -f 1)
BUILD_SIG_OLD := $(shell cat $(BUILD_SIG_FILE) 2>/dev/null)
ifneq ($(BUILD_SIG_OLD),)
ifneq ($(BUILD_SIG_OLD),$(BUILD_SIG))
ifeq ($(AUTO_CLEAN),1)
$(info *** Build parameters changed since last build (OBJ_DIR=$(OBJ_DIR)); running 'make clean-rocks' because AUTO_CLEAN=1)
BUILD_SIG_CLEAN_OUTPUT := $(shell $(MAKE) clean-rocks 1>&2)
ifneq ($(.SHELLSTATUS),0)
$(error AUTO_CLEAN: 'make clean-rocks' failed (exit $(.SHELLSTATUS)); not building against a partially-cleaned tree)
endif
else
$(error Build parameters changed since the last build (OBJ_DIR=$(OBJ_DIR)). Existing object files are stale and must be removed. Run 'make clean', or set AUTO_CLEAN=1 to clean automatically, or ALLOW_BUILD_PARAMETER_CHANGE=1 to build anyway)
endif
endif
endif
# Record the current signature for the next build.
BUILD_SIG_WRITE := $(shell mkdir -p $(OBJ_DIR) 2>/dev/null; printf '%s\n' '$(BUILD_SIG)' > $(BUILD_SIG_FILE))
endif
endif
endif
endif
endif
zlib-$(ZLIB_VER).tar.gz:
curl --fail --output zlib-$(ZLIB_VER).tar.gz --location ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
$(CURL_DOWNLOAD) --output zlib-$(ZLIB_VER).tar.gz ${ZLIB_DOWNLOAD_PREFIX}$(ZLIB_VER).tar.gz
ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz | cut -d ' ' -f 1`; \
if [ "$(ZLIB_SHA256)" != "$$ZLIB_SHA256_ACTUAL" ]; then \
echo zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected=\"$(ZLIB_SHA256)\" actual=\"$$ZLIB_SHA256_ACTUAL\"; \
@@ -2346,7 +2621,7 @@ libz.a: zlib-$(ZLIB_VER).tar.gz
cp zlib-$(ZLIB_VER)/libz.a .
bzip2-$(BZIP2_VER).tar.gz:
curl --fail --output bzip2-$(BZIP2_VER).tar.gz --location ${CURL_SSL_OPTS} ${BZIP2_DOWNLOAD_BASE}/bzip2-$(BZIP2_VER).tar.gz
$(CURL_DOWNLOAD) --output bzip2-$(BZIP2_VER).tar.gz ${BZIP2_DOWNLOAD_PREFIX}$(BZIP2_VER).tar.gz
BZIP2_SHA256_ACTUAL=`$(SHA256_CMD) bzip2-$(BZIP2_VER).tar.gz | cut -d ' ' -f 1`; \
if [ "$(BZIP2_SHA256)" != "$$BZIP2_SHA256_ACTUAL" ]; then \
echo bzip2-$(BZIP2_VER).tar.gz checksum mismatch, expected=\"$(BZIP2_SHA256)\" actual=\"$$BZIP2_SHA256_ACTUAL\"; \
@@ -2360,7 +2635,7 @@ libbz2.a: bzip2-$(BZIP2_VER).tar.gz
cp bzip2-$(BZIP2_VER)/libbz2.a .
snappy-$(SNAPPY_VER).tar.gz:
curl --fail --output snappy-$(SNAPPY_VER).tar.gz --location ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER).tar.gz
$(CURL_DOWNLOAD) --output snappy-$(SNAPPY_VER).tar.gz ${SNAPPY_DOWNLOAD_PREFIX}$(SNAPPY_VER).tar.gz
SNAPPY_SHA256_ACTUAL=`$(SHA256_CMD) snappy-$(SNAPPY_VER).tar.gz | cut -d ' ' -f 1`; \
if [ "$(SNAPPY_SHA256)" != "$$SNAPPY_SHA256_ACTUAL" ]; then \
echo snappy-$(SNAPPY_VER).tar.gz checksum mismatch, expected=\"$(SNAPPY_SHA256)\" actual=\"$$SNAPPY_SHA256_ACTUAL\"; \
@@ -2375,7 +2650,7 @@ libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
lz4-$(LZ4_VER).tar.gz:
curl --fail --output lz4-$(LZ4_VER).tar.gz --location ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
$(CURL_DOWNLOAD) --output lz4-$(LZ4_VER).tar.gz ${LZ4_DOWNLOAD_PREFIX}$(LZ4_VER).tar.gz
LZ4_SHA256_ACTUAL=`$(SHA256_CMD) lz4-$(LZ4_VER).tar.gz | cut -d ' ' -f 1`; \
if [ "$(LZ4_SHA256)" != "$$LZ4_SHA256_ACTUAL" ]; then \
echo lz4-$(LZ4_VER).tar.gz checksum mismatch, expected=\"$(LZ4_SHA256)\" actual=\"$$LZ4_SHA256_ACTUAL\"; \
@@ -2389,7 +2664,7 @@ liblz4.a: lz4-$(LZ4_VER).tar.gz
cp lz4-$(LZ4_VER)/lib/liblz4.a .
zstd-$(ZSTD_VER).tar.gz:
curl --fail --output zstd-$(ZSTD_VER).tar.gz --location ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(ZSTD_VER).tar.gz
$(CURL_DOWNLOAD) --output zstd-$(ZSTD_VER).tar.gz ${ZSTD_DOWNLOAD_PREFIX}$(ZSTD_VER).tar.gz
ZSTD_SHA256_ACTUAL=`$(SHA256_CMD) zstd-$(ZSTD_VER).tar.gz | cut -d ' ' -f 1`; \
if [ "$(ZSTD_SHA256)" != "$$ZSTD_SHA256_ACTUAL" ]; then \
echo zstd-$(ZSTD_VER).tar.gz checksum mismatch, expected=\"$(ZSTD_SHA256)\" actual=\"$$ZSTD_SHA256_ACTUAL\"; \
+2 -4
View File
@@ -1,7 +1,5 @@
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
[![CircleCI Status](https://circleci.com/gh/facebook/rocksdb.svg?style=svg)](https://circleci.com/gh/facebook/rocksdb)
RocksDB is developed and maintained by Facebook Database Engineering Team.
It is built on earlier work on [LevelDB](https://github.com/google/leveldb) by Sanjay Ghemawat (sanjay@google.com)
and Jeff Dean (jeff@google.com)
@@ -14,9 +12,9 @@ and Space-Amplification-Factor (SAF). It has multi-threaded compactions,
making it especially suitable for storing multiple terabytes of data in a
single database.
Start with example usage here: https://github.com/facebook/rocksdb/tree/main/examples
Start with example usage here: https://github.com/meta/rocksdb/tree/main/examples
See the [github wiki](https://github.com/facebook/rocksdb/wiki) for more explanation.
See the [github wiki](https://github.com/meta/rocksdb/wiki) for more explanation.
The public interface is in `include/`. Callers should not include or
rely on the details of any other header files in this package. Those
+97 -17
View File
@@ -35,6 +35,49 @@ from util import ColorString
# tests to export as libraries for inclusion in other projects
_EXPORTED_TEST_LIBS = ["env_basic_test"]
_TEST_LIB_HEADERS = [
"db/db_test_util.h",
"db/db_with_timestamp_test_util.h",
"table/mock_table.h",
"test_util/mock_time_env.h",
"test_util/secondary_cache_test_util.h",
"test_util/simple_external_table_factory.h",
"test_util/testharness.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h",
"utilities/agg_merge/test_agg_merge.h",
"utilities/cassandra/test_utils.h",
]
_TEST_HEADERS = {
"configurable_test": ["options/configurable_test.h"],
"persistent_cache_test": ["utilities/persistent_cache/persistent_cache_test.h"],
"point_lock_manager_test": [
"utilities/transactions/lock/point/point_lock_manager_test.h",
],
"transaction_test": ["utilities/transactions/transaction_test.h"],
}
_STRESS_LIB_HEADERS = [
"db_stress_tool/db_stress_common.h",
"db_stress_tool/db_stress_compaction_service.h",
"db_stress_tool/db_stress_compression_manager.h",
"db_stress_tool/db_stress_driver.h",
"db_stress_tool/db_stress_filters.h",
"db_stress_tool/db_stress_listener.h",
"db_stress_tool/db_stress_shared_state.h",
"db_stress_tool/db_stress_test_base.h",
"db_stress_tool/db_stress_wide_merge_operator.h",
"db_stress_tool/expected_state.h",
"db_stress_tool/expected_value.h",
"db_stress_tool/multi_ops_txns_stress.h",
"test_util/simple_external_table_factory.h",
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/trace_analyzer_tool.h"
]
# Parse src.mk files as a Dictionary of
# VAR_NAME => list of files
def parse_src_mk(repo_path):
@@ -145,12 +188,22 @@ def generate_buck(repo_path, deps_map):
# always add range_tree, it's only excluded on ppc64, which we don't use internally
src_mk["RANGE_TREE_SOURCES"] + src_mk["TOOL_LIB_SOURCES"],
deps=[
"//folly/container:f14_hash",
"//folly/coro:blocking_wait",
"//folly/coro:collect",
"//folly/coro:coroutine",
"//folly/coro:task",
"//folly/synchronization:distributed_mutex",
"fbsource//xplat/folly:executor",
"fbsource//xplat/folly/container:f14_hash",
"fbsource//xplat/folly/coro:baton",
"fbsource//xplat/folly/coro:blocking_wait",
"fbsource//xplat/folly/coro:collect",
"fbsource//xplat/folly/coro:coroutine",
"fbsource//xplat/folly/coro:nothrow",
"fbsource//xplat/folly/coro:task",
"fbsource//xplat/folly/executors:io_executor",
"fbsource//xplat/folly/executors:io_thread_pool_executor",
"fbsource//xplat/folly/executors/thread_factory:named_thread_factory",
"fbsource//xplat/folly/io/async:async_base",
"fbsource//xplat/folly/io/async:event_base_manager",
"fbsource//xplat/folly/io/async:io_uring_backend",
"fbsource//xplat/folly/io/async:request_context",
"fbsource//xplat/folly/synchronization:distributed_mutex",
],
headers=LiteralValue("glob([\"**/*.h\"])")
)
@@ -173,27 +226,34 @@ def generate_buck(repo_path, deps_map):
":rocksdb_lib",
],
)
# rocksdb_test_lib
BUCK.add_library(
"rocksdb_test_lib",
test_lib_sources = (
src_mk.get("MOCK_LIB_SOURCES", [])
+ src_mk.get("TEST_LIB_SOURCES", [])
+ src_mk.get("EXP_LIB_SOURCES", [])
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
)
# Older release source lists may not compile every test helper yet.
test_lib_headers = [
header for header in _TEST_LIB_HEADERS
if header[:-2] + ".cc" in test_lib_sources
]
# rocksdb_test_lib
BUCK.add_library(
"rocksdb_test_lib",
test_lib_sources,
[":rocksdb_lib"],
extra_test_libs=True,
headers=test_lib_headers,
)
# rocksdb_with_faiss_test_lib
BUCK.add_library(
"rocksdb_with_faiss_test_lib",
src_mk.get("MOCK_LIB_SOURCES", [])
+ src_mk.get("TEST_LIB_SOURCES", [])
+ src_mk.get("EXP_LIB_SOURCES", [])
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
test_lib_sources,
deps=[
":rocksdb_with_faiss_lib",
],
extra_test_libs=True,
headers=test_lib_headers,
)
# rocksdb_tools_lib
BUCK.add_library(
@@ -202,6 +262,12 @@ def generate_buck(repo_path, deps_map):
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
+ ["test_util/testutil.cc"],
[":rocksdb_lib"],
headers=[
"test_util/testutil.h",
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
"tools/simulated_hybrid_file_system.h",
"tools/trace_analyzer_tool.h",
],
)
# rocksdb_cache_bench_tools_lib
BUCK.add_library(
@@ -215,17 +281,27 @@ def generate_buck(repo_path, deps_map):
src_mk.get("POINT_LOCK_BENCH_LIB_SOURCES", []),
[":rocksdb_lib"],
)
stress_sources = (
src_mk.get("ANALYZER_LIB_SOURCES", [])
+ src_mk.get("STRESS_LIB_SOURCES", [])
+ ["test_util/testutil.cc"]
)
# rocksdb_stress_lib
BUCK.add_rocksdb_library(
"rocksdb_stress_lib",
src_mk.get("ANALYZER_LIB_SOURCES", [])
+ src_mk.get("STRESS_LIB_SOURCES", [])
+ ["test_util/testutil.cc"],
stress_sources,
headers=[
header for header in _STRESS_LIB_HEADERS
if header[:-2] + ".cc" in stress_sources
],
)
# ldb binary
BUCK.add_binary(
"ldb", ["tools/ldb.cc"], [":rocksdb_tools_lib"]
)
BUCK.add_binary(
"db_split_merge", ["tools/db_split_merge.cc"], [":rocksdb_lib"]
)
# db_stress binary
BUCK.add_binary(
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
@@ -343,6 +419,7 @@ def generate_buck(repo_path, deps_map):
test_src,
deps=json.dumps(deps["extra_deps"] + [":" + test_library]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
source_headers=_TEST_HEADERS.get(test, []),
)
else:
if with_faiss:
@@ -351,6 +428,7 @@ def generate_buck(repo_path, deps_map):
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_with_faiss_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
source_headers=_TEST_HEADERS.get(test, []),
)
else:
BUCK.register_test(
@@ -358,8 +436,10 @@ def generate_buck(repo_path, deps_map):
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
source_headers=_TEST_HEADERS.get(test, []),
)
BUCK.export_file("tools/db_crashtest.py")
BUCK.export_file("tools/fault_injection_log_parser.py")
print(ColorString.info("Generated BUCK Summary:"))
print(ColorString.info("- %d libs" % BUCK.total_lib))
+12 -1
View File
@@ -118,6 +118,12 @@ class TARGETSBuilder:
self.total_bin = self.total_bin + 1
def add_c_test(self):
# The actual c_test_bin target is defined by add_c_test_wrapper in the
# internal //rocks/buckifier:defs.bzl (not in this OSS repo). db/c_test.c
# #includes the generated c_api_gen/*.inc fragments, so under Buck's
# hermetic sandbox that wrapper must expose them as headers, e.g.
# headers = native.glob(["c_api_gen/**/*.inc"])
# (Make/CMake resolve the include via -I. / PROJECT_SOURCE_DIR.)
with open(self.path, "ab") as targets_file:
targets_file.write(
b"""
@@ -156,12 +162,17 @@ add_c_test_wrapper()
).encode("utf-8")
)
def register_test(self, test_name, src, deps, extra_compiler_flags):
def register_test(self, test_name, src, deps, extra_compiler_flags, source_headers):
with open(self.path, "ab") as targets_file:
targets_file.write(
targets_cfg.unittests_template.format(
test_name=test_name,
test_cc=str(src),
source_headers=(
f" source_headers=[{pretty_list(source_headers, 16)}],\n"
if source_headers
else ""
),
deps=deps,
extra_compiler_flags=extra_compiler_flags,
).encode("utf-8")
+1 -1
View File
@@ -30,7 +30,7 @@ cpp_binary_wrapper(name="{name}", srcs=[{srcs}], deps=[{deps}], extra_preprocess
unittests_template = """
cpp_unittest_wrapper(name="{test_name}",
srcs=["{test_cc}"],
deps={deps},
{source_headers} deps={deps},
extra_compiler_flags={extra_compiler_flags})
"""
+43 -25
View File
@@ -27,7 +27,6 @@
# -DLZ4 if the LZ4 library is present
# -DZSTD if the ZSTD library is present
# -DNUMA if the NUMA library is present
# -DTBB if the TBB library is present
# -DMEMKIND if the memkind library is present
#
# Using gflags in rocksdb:
@@ -90,13 +89,36 @@ if test -z "$CC"; then
fi
fi
# CLANG_CXX is the single clang++ detection point for the whole script: it is
# used for CXX when building with clang, for CLANG_ANALYZER (scan-build), and by
# tooling that needs clang even in a gcc build (e.g. the C API codegen, which
# parses C++ ASTs). The fbcode config above already sets CLANG_CXX; otherwise
# resolve it here.
if test -z "$CLANG_CXX"; then
if [ -n "$CXX" ] && echo | $CXX -dM -E -x c++ - 2>/dev/null | grep -q '\b__clang__\b'; then
# Honor an already-configured clang compiler (e.g. CI sets
# CXX=clang++-21) so codegen parses headers with the same clang the
# build uses, not an unrelated clang++ that merely happens to come first
# on PATH (an image may ship both a bare clang++ and clang++-NN).
CLANG_CXX="$CXX"
else
# Otherwise take the first clang++ on PATH. Candidates cover a bare
# clang++, versioned names, and clang++.par (a Meta-internal wrapper
# often present on dev machines).
CLANG_CXX=$(command -v clang++ || command -v clang++-21 || command -v clang++-20 \
|| command -v clang++-19 || command -v clang++-18 || command -v clang++-17 \
|| command -v clang++-16 || command -v clang++-15 || command -v clang++-14 \
|| command -v clang++-13 || command -v clang++.par || true)
fi
fi
if test -z "$CXX"; then
if [ "$USE_CLANG" -a -x "$(command -v clang++)" ]; then
CXX=clang++
if [ "$USE_CLANG" ] && [ -n "$CLANG_CXX" ]; then
CXX="$CLANG_CXX"
elif [ -x "$(command -v g++)" ]; then
CXX=g++
elif [ -x "$(command -v clang++)" ]; then
CXX=clang++
elif [ -n "$CLANG_CXX" ]; then
CXX="$CLANG_CXX"
else
CXX=g++
fi
@@ -114,6 +136,17 @@ if test -z "$AR"; then
fi
fi
# If the selected C++ compiler is actually clang (e.g. clang was the only
# compiler available), reflect that as USE_CLANG so clang-specific logic in the
# Makefile applies consistently. Detect it via the compiler's own __clang__
# predefined macro rather than matching the binary path, which can false-positive
# (e.g. a home directory containing "clang").
if test -z "$USE_CLANG"; then
if echo | $CXX -dM -E -x c++ - 2>/dev/null | grep -q '\b__clang__\b'; then
USE_CLANG=1
fi
fi
# Detect OS
if test -z "$TARGET_OS"; then
TARGET_OS=`uname -s`
@@ -128,7 +161,7 @@ if test -z "$CLANG_SCAN_BUILD"; then
fi
if test -z "$CLANG_ANALYZER"; then
CLANG_ANALYZER=$(command -v clang++ 2> /dev/null)
CLANG_ANALYZER="$CLANG_CXX"
fi
if test -z "$FIND"; then
@@ -197,12 +230,6 @@ case "$TARGET_OS" in
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -ldl"
# PORT_FILES=port/linux/linux_specific.cc
;;
SunOS)
PLATFORM=OS_SOLARIS
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_SOLARIS -m64"
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -static-libstdc++ -static-libgcc -m64"
# PORT_FILES=port/sunos/sunos_specific.cc
;;
AIX)
PLATFORM=OS_AIX
CC=gcc
@@ -424,19 +451,6 @@ EOF
fi
fi
if ! test $ROCKSDB_DISABLE_TBB; then
# Test whether tbb is available
$CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -ltbb 2>/dev/null <<EOF
#include <tbb/tbb.h>
int main() {}
EOF
if [ "$?" = 0 ]; then
COMMON_FLAGS="$COMMON_FLAGS -DTBB"
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltbb"
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltbb"
fi
fi
if ! test $ROCKSDB_DISABLE_JEMALLOC; then
# Test whether jemalloc is available
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -ljemalloc \
@@ -832,6 +846,10 @@ echo "ROCKSDB_MINOR=$ROCKSDB_MINOR" >> "$OUTPUT"
echo "ROCKSDB_PATCH=$ROCKSDB_PATCH" >> "$OUTPUT"
echo "CLANG_SCAN_BUILD=$CLANG_SCAN_BUILD" >> "$OUTPUT"
echo "CLANG_ANALYZER=$CLANG_ANALYZER" >> "$OUTPUT"
echo "CLANG_CXX=$CLANG_CXX" >> "$OUTPUT"
if test -n "$USE_CLANG"; then
echo "USE_CLANG=$USE_CLANG" >> "$OUTPUT"
fi
echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
echo "FIND=$FIND" >> "$OUTPUT"
echo "WATCH=$WATCH" >> "$OUTPUT"
+4 -1
View File
@@ -1,5 +1,8 @@
#!/usr/bin/env bash
# Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved.
# 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).
#
# Check for some simple mistakes in public headers (on the command line)
# that should prevent commit or push
+14 -15
View File
@@ -1,21 +1,20 @@
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
# The file is generated using update_dependencies.sh.
GCC_BASE=/mnt/gvfs/third-party2/gcc/62de5a92e5f23c661c3d4b9f322e04eb14e7a5bd/11.x/centos8-native/886b5eb
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1f6edd1ff15c99c861afc8f3cd69054cd974dd64/15/platform010/72a2ff8
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/fed6e93d87571fb162734c86636119d45a398963/2.34/platform010/f259413
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/31a346126a1f3b64812c362511cb04cc1bd40855/1.1.8/platform010/76ebdda
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0c65c05468b5a38cef1a106a1f526463e120c8dd/1.2.8/platform010/76ebdda
GCC_BASE=/mnt/gvfs/third-party2/gcc/e9cb2d4550b5a95024125084420342d256b349cd/11.x/centos9-native/3bed279
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/4e46e6967e15412702e51960c69790a78933856f/21/platform010/72a2ff8
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/63c5179edc90fec473e53c1b2f92269c65c39e07/11.x/platform010/5684a5a
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/b4a6248a76a163bcccb1e4751e247712c535846e/2.34/platform010/f259413
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/ef895164499fdfedb9d9b9c85520de10aa8ff9ca/1.1.8/platform010/76ebdda
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/643dbf0136f869562485cfcb2953f5cab60447ef/1.2.8/platform010/76ebdda
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/09703139cfc376bd8a82642385a0e97726b28287/1.0.6/platform010/76ebdda
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ff23d17b932725cc1734a14896a8b67c518ba169/1.9.4/platform010/76ebdda
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/576397d8b1d9cea7306ad1e454d5e55caaa2ff1c/1.4.x/platform010/64091f4
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/fecac07861cb829f5e60dbeff0503d3272db73c0/2.2.0/platform010/76ebdda
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/0bb3f5756788ce26e2e16a1cb2f2af2c59b51abe/master/platform010/f57cc4a
LZ4_BASE=/mnt/gvfs/third-party2/lz4/823bf8e000654abef784311296ffc8166d9d3986/1.9.4/platform010/76ebdda
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/0cda78647b5712bec7ec6d9ba2f0dcdabfb6f44f/1.4.x/platform010/64091f4
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/a02e0de00945729ad553c187c64e86b57f45a33b/2.2.0/platform010/76ebdda
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/a1a50742bec7195bfafb594e174ea45dc197a420/master/platform010/779a252
NUMA_BASE=/mnt/gvfs/third-party2/numa/5b602edd46fda54cdd7ea45f77dbe4061206e174/2.0.11/platform010/76ebdda
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/97cac22a149c2e202917e05d44e87e516b68216f/1.4/platform010/5074a48
TBB_BASE=/mnt/gvfs/third-party2/tbb/53953ebc4e3eda85ad6fc3e429ba146035e97b90/2018_U5/platform010/76ebdda
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/f3cce2e1a79ac8d64e102886603a6997d6e88640/1.8/platform010/76ebdda
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/624a2f8f6c93c3c1df8aa4a6255d8202631a6c80/fb/platform010/da39a3e
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/39579e8603b48b3540f8b0633f43adf29acccb8b/2.37/centos8-native/da39a3e
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/cd9cc656d49ecb53797ce4d055e49fde29fd57ff/3.19.0/platform010/76ebdda
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/0883cccda758e68b47e4c04e4fa01142e1f60f32/fb/platform010/da39a3e
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/1d5d19135a406a941d3a441eb2bf506bb9d19210/2.43/centos9-native/be7139e
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/3e2b7ccc315c9a0f2b678ce6fd774f4970f9bf3b/3.22.0/platform010/76ebdda
+4 -13
View File
@@ -87,15 +87,6 @@ if test -z $PIC_BUILD; then
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
fi
# location of TBB
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
if test -z $PIC_BUILD; then
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
else
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
fi
CFLAGS+=" -DTBB"
test "$USE_SSE" || USE_SSE=1
export USE_SSE
test "$PORTABLE" || PORTABLE=1
@@ -104,7 +95,7 @@ export PORTABLE
BINUTILS="$BINUTILS_BASE/bin"
AR="$BINUTILS/ar"
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
STDLIBS="-L $GCC_BASE/lib64"
@@ -150,17 +141,17 @@ CFLAGS+=" $DEPS_INCLUDE"
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT"
CXXFLAGS+=" $CFLAGS"
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB"
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-5-glibc-2.23/lib/ld.so"
EXEC_LDFLAGS+=" $LIBUNWIND"
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-5-glibc-2.23/lib"
# required by libtbb
# dynamic loading
EXEC_LDFLAGS+=" -ldl"
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS"
VALGRIND_VER="$VALGRIND_BASE/bin/"
+9 -10
View File
@@ -82,11 +82,6 @@ CFLAGS+=" -DNUMA"
# location of libunwind
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind${MAYBE_PIC}.a"
# location of TBB
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
TBB_LIBS="$TBB_BASE/lib/libtbb${MAYBE_PIC}.a"
CFLAGS+=" -DTBB"
# location of LIBURING
LIBURING_INCLUDE=" -isystem $LIBURING_BASE/include/"
LIBURING_LIBS="$LIBURING_BASE/lib/liburing${MAYBE_PIC}.a"
@@ -101,7 +96,7 @@ BINUTILS="$BINUTILS_BASE/bin"
AR="$BINUTILS/ar"
AS="$BINUTILS/as"
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE $LIBURING_INCLUDE $BENCHMARK_INCLUDE"
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $LIBURING_INCLUDE $BENCHMARK_INCLUDE"
STDLIBS="-L $GCC_BASE/lib64"
@@ -109,6 +104,10 @@ CLANG_BIN="$CLANG_BASE/bin"
CLANG_LIB="$CLANG_BASE/lib"
CLANG_SRC="$CLANG_BASE/../../src"
# A clang++ is always available in the fbcode toolchain regardless of whether
# the build itself uses gcc or clang. CLANG_CXX exposes it for tooling (e.g. the
# C API codegen, which parses C++ ASTs) that needs clang even in a gcc build.
CLANG_CXX="$CLANG_BIN/clang++"
CLANG_ANALYZER="$CLANG_BIN/clang++"
CLANG_SCAN_BUILD="$CLANG_BIN/scan-build"
@@ -157,19 +156,19 @@ CFLAGS+=" $DEPS_INCLUDE"
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_IOURING_PRESENT"
CXXFLAGS+=" $CFLAGS"
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $LIBURING_LIBS $BENCHMARK_LIBS"
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform010/lib/ld.so"
EXEC_LDFLAGS+=" $LIBUNWIND"
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform010/lib"
EXEC_LDFLAGS+=" -Wl,-rpath=$GCC_BASE/lib64"
# required by libtbb
# dynamic loading
EXEC_LDFLAGS+=" -ldl"
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
PLATFORM_LDFLAGS+=" -B$BINUTILS"
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
VALGRIND_VER="$VALGRIND_BASE/bin/"
export CC CXX AR AS CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
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
+4 -3
View File
@@ -129,12 +129,13 @@ uncommitted_code=`git diff HEAD`
# If there's no uncommitted changes, we assume user are doing post-commit
# format check, in which case we'll try to check the modified lines vs. the
# facebook/rocksdb.git main branch. Otherwise, we'll check format of the
# meta/rocksdb.git main branch. Otherwise, we'll check format of the
# uncommitted code only.
if [ -z "$uncommitted_code" ]
then
# Attempt to get name of facebook/rocksdb.git remote.
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(LC_ALL=POSIX LANG=POSIX git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)"
# Attempt to get name of meta/rocksdb.git remote. Older clones might still
# use the pre-transfer facebook/rocksdb.git URL, which GitHub redirects.
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(LC_ALL=POSIX LANG=POSIX git remote -v | grep -E '[:/](meta|facebook)/rocksdb\.git' | head -n 1 | cut -f 1)"
# Fall back on 'origin' if that fails
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE=origin
# Use main branch from that remote
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
# Copyright 2017 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import gtest_parallel
import sys
sys.exit(gtest_parallel.main())
+959
View File
@@ -0,0 +1,959 @@
# 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 the Apache 2.0 License (found in the LICENSE.Apache file in the root directory).
# Copyright 2013 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import errno
from functools import total_ordering
import gzip
import io
import json
import multiprocessing
import optparse
import os
import re
import shutil
import signal
import subprocess
import sys
import tempfile
import threading
import time
if sys.version_info.major >= 3:
long = int
import _pickle as cPickle
import _thread as thread
else:
import cPickle
import thread
from pickle import HIGHEST_PROTOCOL as PICKLE_HIGHEST_PROTOCOL
if sys.platform == 'win32':
import msvcrt
else:
import fcntl
# An object that catches SIGINT sent to the Python process and notices
# if processes passed to wait() die by SIGINT (we need to look for
# both of those cases, because pressing Ctrl+C can result in either
# the main process or one of the subprocesses getting the signal).
#
# Before a SIGINT is seen, wait(p) will simply call p.wait() and
# return the result. Once a SIGINT has been seen (in the main process
# or a subprocess, including the one the current call is waiting for),
# wait(p) will call p.terminate() and raise ProcessWasInterrupted.
class SigintHandler(object):
class ProcessWasInterrupted(Exception):
pass
sigint_returncodes = {
-signal.SIGINT, # Unix
-1073741510, # Windows
}
def __init__(self):
self.__lock = threading.Lock()
self.__processes = set()
self.__got_sigint = False
signal.signal(signal.SIGINT, lambda signal_num, frame: self.interrupt())
def __on_sigint(self):
self.__got_sigint = True
while self.__processes:
try:
self.__processes.pop().terminate()
except OSError:
pass
def interrupt(self):
with self.__lock:
self.__on_sigint()
def got_sigint(self):
with self.__lock:
return self.__got_sigint
def wait(self, p, timeout_per_test):
with self.__lock:
if self.__got_sigint:
p.terminate()
self.__processes.add(p)
try:
code = p.wait(timeout_per_test)
except subprocess.TimeoutExpired :
p.terminate()
self.__processes.remove(p)
code = -errno.ETIME
with self.__lock:
self.__processes.discard(p)
if code in self.sigint_returncodes:
self.__on_sigint()
if self.__got_sigint:
raise self.ProcessWasInterrupted
return code
sigint_handler = SigintHandler()
# Return the width of the terminal, or None if it couldn't be
# determined (e.g. because we're not being run interactively).
def term_width(out):
if not out.isatty():
return None
try:
p = subprocess.Popen(["stty", "size"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(out, err) = p.communicate()
if p.returncode != 0 or err:
return None
return int(out.split()[1])
except (IndexError, OSError, ValueError):
return None
# Output transient and permanent lines of text. If several transient
# lines are written in sequence, the new will overwrite the old. We
# use this to ensure that lots of unimportant info (tests passing)
# won't drown out important info (tests failing).
class Outputter(object):
def __init__(self, out_file):
self.__out_file = out_file
self.__previous_line_was_transient = False
self.__width = term_width(out_file) # Line width, or None if not a tty.
def transient_line(self, msg):
if self.__width is None:
self.__out_file.write(msg + "\n")
self.__out_file.flush()
else:
self.__out_file.write("\r" + msg[:self.__width].ljust(self.__width))
self.__previous_line_was_transient = True
def flush_transient_output(self):
if self.__previous_line_was_transient:
self.__out_file.write("\n")
self.__previous_line_was_transient = False
def permanent_line(self, msg):
self.flush_transient_output()
self.__out_file.write(msg + "\n")
if self.__width is None:
self.__out_file.flush()
def get_save_file_path():
"""Return path to file for saving transient data."""
if sys.platform == 'win32':
default_cache_path = os.path.join(os.path.expanduser('~'), 'AppData',
'Local')
cache_path = os.environ.get('LOCALAPPDATA', default_cache_path)
else:
# We don't use xdg module since it's not a standard.
default_cache_path = os.path.join(os.path.expanduser('~'), '.cache')
cache_path = os.environ.get('XDG_CACHE_HOME', default_cache_path)
if os.path.isdir(cache_path):
return os.path.join(cache_path, 'gtest-parallel')
else:
sys.stderr.write('Directory {} does not exist'.format(cache_path))
return os.path.join(os.path.expanduser('~'), '.gtest-parallel-times')
@total_ordering
class Task(object):
"""Stores information about a task (single execution of a test).
This class stores information about the test to be executed (gtest binary and
test name), and its result (log file, exit code and runtime).
Each task is uniquely identified by the gtest binary, the test name and an
execution number that increases each time the test is executed.
Additionaly we store the last execution time, so that next time the test is
executed, the slowest tests are run first.
"""
def __init__(self, test_binary, test_name, test_command, execution_number,
last_execution_time, output_dir):
self.test_name = test_name
self.output_dir = output_dir
self.test_binary = test_binary
self.test_command = test_command
self.execution_number = execution_number
self.last_execution_time = last_execution_time
self.exit_code = None
self.runtime_ms = None
self.test_id = (test_binary, test_name)
self.task_id = (test_binary, test_name, self.execution_number)
self.log_file = Task._logname(self.output_dir, self.test_binary, test_name,
self.execution_number)
def __sorting_key(self):
# Unseen or failing tests (both missing execution time) take precedence over
# execution time. Tests are greater (seen as slower) when missing times so
# that they are executed first.
return (1 if self.last_execution_time is None else 0,
self.last_execution_time)
def __eq__(self, other):
return self.__sorting_key() == other.__sorting_key()
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
return self.__sorting_key() < other.__sorting_key()
@staticmethod
def _normalize(string):
return re.sub('[^A-Za-z0-9]', '_', string)
@staticmethod
def _logname(output_dir, test_binary, test_name, execution_number):
# Store logs to temporary files if there is no output_dir.
if output_dir is None:
(log_handle, log_name) = tempfile.mkstemp(prefix='gtest_parallel_',
suffix=".log")
os.close(log_handle)
return log_name
log_name = '%s-%s-%d.log' % (Task._normalize(os.path.basename(test_binary)),
Task._normalize(test_name), execution_number)
return os.path.join(output_dir, log_name)
def run(self, timeout_per_test):
begin = time.time()
with open(self.log_file, 'w') as log:
task = subprocess.Popen(self.test_command, stdout=log, stderr=log)
try:
self.exit_code = sigint_handler.wait(task, timeout_per_test)
except sigint_handler.ProcessWasInterrupted:
thread.exit()
self.runtime_ms = int(1000 * (time.time() - begin))
self.last_execution_time = None if self.exit_code else self.runtime_ms
class TaskManager(object):
"""Executes the tasks and stores the passed, failed and interrupted tasks.
When a task is run, this class keeps track if it passed, failed or was
interrupted. After a task finishes it calls the relevant functions of the
Logger, TestResults and TestTimes classes, and in case of failure, retries the
test as specified by the --retry_failed flag.
"""
def __init__(self, times, logger, test_results, task_factory, times_to_retry,
initial_execution_number):
self.times = times
self.logger = logger
self.test_results = test_results
self.task_factory = task_factory
self.times_to_retry = times_to_retry
self.initial_execution_number = initial_execution_number
self.global_exit_code = 0
self.passed = []
self.failed = []
self.started = {}
self.timed_out = []
self.execution_number = {}
self.lock = threading.Lock()
def __get_next_execution_number(self, test_id):
with self.lock:
next_execution_number = self.execution_number.setdefault(
test_id, self.initial_execution_number)
self.execution_number[test_id] += 1
return next_execution_number
def __register_start(self, task):
with self.lock:
self.started[task.task_id] = task
def register_exit(self, task):
self.logger.log_exit(task)
self.times.record_test_time(task.test_binary, task.test_name,
task.last_execution_time)
if self.test_results:
self.test_results.log(task.test_name, task.runtime_ms / 1000.0,
task.exit_code)
with self.lock:
self.started.pop(task.task_id)
if task.exit_code == 0:
self.passed.append(task)
elif task.exit_code == -errno.ETIME:
self.timed_out.append(task)
else:
self.failed.append(task)
def run_task(self, task, timeout_per_test):
for try_number in range(self.times_to_retry + 1):
self.__register_start(task)
task.run(timeout_per_test)
self.register_exit(task)
if task.exit_code == 0:
break
if try_number < self.times_to_retry:
execution_number = self.__get_next_execution_number(task.test_id)
# We need create a new Task instance. Each task represents a single test
# execution, with its own runtime, exit code and log file.
task = self.task_factory(task.test_binary, task.test_name,
task.test_command, execution_number,
task.last_execution_time, task.output_dir)
with self.lock:
if task.exit_code != 0:
self.global_exit_code = task.exit_code
class FilterFormat(object):
def __init__(self, output_dir):
if sys.stdout.isatty():
# stdout needs to be unbuffered since the output is interactive.
if isinstance(sys.stdout, io.TextIOWrapper):
# workaround for https://bugs.python.org/issue17404
sys.stdout = io.TextIOWrapper(sys.stdout.detach(),
line_buffering=True,
write_through=True,
newline='\n')
else:
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
self.output_dir = output_dir
self.total_tasks = 0
self.finished_tasks = 0
self.out = Outputter(sys.stdout)
self.stdout_lock = threading.Lock()
def move_to(self, destination_dir, tasks):
if self.output_dir is None:
return
destination_dir = os.path.join(self.output_dir, destination_dir)
os.makedirs(destination_dir)
for task in tasks:
shutil.move(task.log_file, destination_dir)
def print_tests(self, message, tasks, print_try_number, print_test_command):
self.out.permanent_line("%s (%s/%s):" %
(message, len(tasks), self.total_tasks))
for task in sorted(tasks):
runtime_ms = 'Interrupted'
if task.runtime_ms is not None:
runtime_ms = '%d ms' % task.runtime_ms
if print_test_command:
try:
cmd_str = " ".join(task.test_command)
except TypeError:
cmd_str = task.test_command
self.out.permanent_line(
"%11s: %s%s" %
(runtime_ms, cmd_str,
(" (try #%d)" % task.execution_number) if print_try_number else ""))
else:
self.out.permanent_line(
"%11s: %s %s%s" %
(runtime_ms, task.test_binary, task.test_name,
(" (try #%d)" % task.execution_number) if print_try_number else ""))
def log_exit(self, task):
with self.stdout_lock:
self.finished_tasks += 1
self.out.transient_line("[%d/%d] %s (%d ms)" %
(self.finished_tasks, self.total_tasks,
task.test_name, task.runtime_ms))
if task.exit_code != 0:
signal_name = None
if task.exit_code < 0:
try:
signal_name = signal.Signals(-task.exit_code).name
except ValueError:
pass
with open(task.log_file) as f:
for line in f.readlines():
self.out.permanent_line(line.rstrip())
if task.exit_code is None:
self.out.permanent_line("[%d/%d] %s aborted after %d ms" %
(self.finished_tasks, self.total_tasks,
task.test_name, task.runtime_ms))
elif task.exit_code == -errno.ETIME:
self.out.permanent_line(
"\033[31m[ TIMEOUT ]\033[0m %s timed out after %d s"
% (task.test_name, task.runtime_ms/1000))
elif signal_name is not None:
self.out.permanent_line(
"[%d/%d] %s killed by signal %s (%d ms)" %
(self.finished_tasks, self.total_tasks, task.test_name,
signal_name, task.runtime_ms))
else:
self.out.permanent_line(
"[%d/%d] %s returned with exit code %d (%d ms)" %
(self.finished_tasks, self.total_tasks, task.test_name,
task.exit_code, task.runtime_ms))
if self.output_dir is None:
# Try to remove the file 100 times (sleeping for 0.1 second in between).
# This is a workaround for a process handle seemingly holding on to the
# file for too long inside os.subprocess. This workaround is in place
# until we figure out a minimal repro to report upstream (or a better
# suspect) to prevent os.remove exceptions.
num_tries = 100
for i in range(num_tries):
try:
os.remove(task.log_file)
except OSError as e:
if e.errno is not errno.ENOENT:
if i is num_tries - 1:
self.out.permanent_line('Could not remove temporary log file: ' +
str(e))
else:
time.sleep(0.1)
continue
break
def log_tasks(self, total_tasks):
self.total_tasks += total_tasks
self.out.transient_line("[0/%d] Running tests..." % self.total_tasks)
def summarize(self, passed_tasks, failed_tasks, interrupted_tasks):
stats = {}
def add_stats(stats, task, idx):
task_key = (task.test_binary, task.test_name)
if not task_key in stats:
# (passed, failed, interrupted) task_key is added as tie breaker to get
# alphabetic sorting on equally-stable tests
stats[task_key] = [0, 0, 0, task_key]
stats[task_key][idx] += 1
for task in passed_tasks:
add_stats(stats, task, 0)
for task in failed_tasks:
add_stats(stats, task, 1)
for task in interrupted_tasks:
add_stats(stats, task, 2)
self.out.permanent_line("SUMMARY:")
for task_key in sorted(stats, key=stats.__getitem__):
(num_passed, num_failed, num_interrupted, _) = stats[task_key]
(test_binary, task_name) = task_key
total_runs = num_passed + num_failed + num_interrupted
if num_passed == total_runs:
continue
self.out.permanent_line(" %s %s passed %d / %d times%s." %
(test_binary, task_name, num_passed, total_runs,
"" if num_interrupted == 0 else
(" (%d interrupted)" % num_interrupted)))
def flush(self):
self.out.flush_transient_output()
class CollectTestResults(object):
def __init__(self, json_dump_filepath):
self.test_results_lock = threading.Lock()
self.json_dump_file = open(json_dump_filepath, 'w')
self.test_results = {
"interrupted": False,
"path_delimiter": ".",
# Third version of the file format. See the link in the flag description
# for details.
"version": 3,
"seconds_since_epoch": int(time.time()),
"num_failures_by_type": {
"PASS": 0,
"FAIL": 0,
"TIMEOUT": 0,
},
"tests": {},
}
def log(self, test, runtime_seconds, exit_code):
if exit_code is None:
actual_result = "TIMEOUT"
elif exit_code == 0:
actual_result = "PASS"
else:
actual_result = "FAIL"
with self.test_results_lock:
self.test_results['num_failures_by_type'][actual_result] += 1
results = self.test_results['tests']
for name in test.split('.'):
results = results.setdefault(name, {})
if results:
results['actual'] += ' ' + actual_result
results['times'].append(runtime_seconds)
else: # This is the first invocation of the test
results['actual'] = actual_result
results['times'] = [runtime_seconds]
results['time'] = runtime_seconds
results['expected'] = 'PASS'
def dump_to_file_and_close(self):
json.dump(self.test_results, self.json_dump_file)
self.json_dump_file.close()
# Record of test runtimes. Has built-in locking.
class TestTimes(object):
class LockedFile(object):
def __init__(self, filename, mode):
self._filename = filename
self._mode = mode
self._fo = None
def __enter__(self):
self._fo = open(self._filename, self._mode)
# Regardless of opening mode we always seek to the beginning of file.
# This simplifies code working with LockedFile and also ensures that
# we lock (and unlock below) always the same region in file on win32.
self._fo.seek(0)
try:
if sys.platform == 'win32':
# We are locking here fixed location in file to use it as
# an exclusive lock on entire file.
msvcrt.locking(self._fo.fileno(), msvcrt.LK_LOCK, 1)
else:
fcntl.flock(self._fo.fileno(), fcntl.LOCK_EX)
except IOError:
self._fo.close()
raise
return self._fo
def __exit__(self, exc_type, exc_value, traceback):
# Flush any buffered data to disk. This is needed to prevent race
# condition which happens from the moment of releasing file lock
# till closing the file.
self._fo.flush()
try:
if sys.platform == 'win32':
self._fo.seek(0)
msvcrt.locking(self._fo.fileno(), msvcrt.LK_UNLCK, 1)
else:
fcntl.flock(self._fo.fileno(), fcntl.LOCK_UN)
finally:
self._fo.close()
return exc_value is None
def __init__(self, save_file):
"Create new object seeded with saved test times from the given file."
self.__times = {} # (test binary, test name) -> runtime in ms
# Protects calls to record_test_time(); other calls are not
# expected to be made concurrently.
self.__lock = threading.Lock()
try:
with TestTimes.LockedFile(save_file, 'rb') as fd:
times = TestTimes.__read_test_times_file(fd)
except IOError:
# We couldn't obtain the lock.
return
# Discard saved times if the format isn't right.
if type(times) is not dict:
return
for ((test_binary, test_name), runtime) in times.items():
if (type(test_binary) is not str or type(test_name) is not str
or type(runtime) not in {int, long, type(None)}):
return
self.__times = times
def get_test_time(self, binary, testname):
"""Return the last duration for the given test as an integer number of
milliseconds, or None if the test failed or if there's no record for it."""
return self.__times.get((binary, testname), None)
def record_test_time(self, binary, testname, runtime_ms):
"""Record that the given test ran in the specified number of
milliseconds. If the test failed, runtime_ms should be None."""
with self.__lock:
self.__times[(binary, testname)] = runtime_ms
def write_to_file(self, save_file):
"Write all the times to file."
try:
with TestTimes.LockedFile(save_file, 'a+b') as fd:
times = TestTimes.__read_test_times_file(fd)
if times is None:
times = self.__times
else:
times.update(self.__times)
# We erase data from file while still holding a lock to it. This
# way reading old test times and appending new ones are atomic
# for external viewer.
fd.seek(0)
fd.truncate()
with gzip.GzipFile(fileobj=fd, mode='wb') as gzf:
cPickle.dump(times, gzf, PICKLE_HIGHEST_PROTOCOL)
except IOError:
pass # ignore errors---saving the times isn't that important
@staticmethod
def __read_test_times_file(fd):
try:
with gzip.GzipFile(fileobj=fd, mode='rb') as gzf:
times = cPickle.load(gzf)
except Exception:
# File doesn't exist, isn't readable, is malformed---whatever.
# Just ignore it.
return None
else:
return times
def find_tests(binaries, additional_args, options, times):
test_count = 0
tasks = []
for test_binary in binaries:
command = [test_binary] + additional_args
if options.gtest_also_run_disabled_tests:
command += ['--gtest_also_run_disabled_tests']
list_command = command + ['--gtest_list_tests']
if options.gtest_filter != '':
list_command += ['--gtest_filter=' + options.gtest_filter]
try:
test_list = subprocess.check_output(list_command,
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
sys.exit("%s: %s\n%s" % (test_binary, str(e), e.output))
try:
test_list = test_list.split('\n')
except TypeError:
# subprocess.check_output() returns bytes in python3
test_list = test_list.decode(sys.stdout.encoding).split('\n')
command += ['--gtest_color=' + options.gtest_color]
test_group = ''
for line in test_list:
if not line.strip():
continue
if line[0] != " ":
# Remove comments for typed tests and strip whitespace.
test_group = line.split('#')[0].strip()
continue
# Remove comments for parameterized tests and strip whitespace.
line = line.split('#')[0].strip()
if not line:
continue
test_name = test_group + line
if not options.gtest_also_run_disabled_tests and 'DISABLED_' in test_name:
continue
# Skip PRE_ tests which are used by Chromium.
if '.PRE_' in test_name:
continue
last_execution_time = times.get_test_time(test_binary, test_name)
if options.failed and last_execution_time is not None:
continue
test_command = command + ['--gtest_filter=' + test_name]
if (test_count - options.shard_index) % options.shard_count == 0:
for execution_number in range(options.repeat):
tasks.append(
Task(test_binary, test_name, test_command, execution_number + 1,
last_execution_time, options.output_dir))
test_count += 1
# Sort the tasks to run the slowest tests first, so that faster ones can be
# finished in parallel.
return sorted(tasks, reverse=True)
def execute_tasks(tasks, pool_size, task_manager, timeout_seconds,
timeout_per_test, serialize_test_cases):
class WorkerFn(object):
def __init__(self, tasks, running_groups, timeout_per_test):
self.tasks = tasks
self.running_groups = running_groups
self.timeout_per_test = timeout_per_test
self.task_lock = threading.Lock()
def __call__(self):
while True:
with self.task_lock:
for task_id in range(len(self.tasks)):
task = self.tasks[task_id]
if self.running_groups is not None:
test_group = task.test_name.split('.')[0]
if test_group in self.running_groups:
# Try to find other non-running test group.
continue
else:
self.running_groups.add(test_group)
del self.tasks[task_id]
break
else:
# Either there is no tasks left or number or remaining test
# cases (groups) is less than number or running threads.
return
task_manager.run_task(task, self.timeout_per_test)
if self.running_groups is not None:
with self.task_lock:
self.running_groups.remove(test_group)
def start_daemon(func):
t = threading.Thread(target=func)
t.daemon = True
t.start()
return t
timeout = None
try:
if timeout_seconds:
timeout = threading.Timer(timeout_seconds, sigint_handler.interrupt)
timeout.start()
running_groups = set() if serialize_test_cases else None
worker_fn = WorkerFn(tasks, running_groups, timeout_per_test)
workers = [start_daemon(worker_fn) for _ in range(pool_size)]
for worker in workers:
worker.join()
finally:
if timeout:
timeout.cancel()
for task in list(task_manager.started.values()):
task.runtime_ms = timeout_seconds * 1000
task_manager.register_exit(task)
def default_options_parser():
parser = optparse.OptionParser(
usage='usage: %prog [options] binary [binary ...] -- [additional args]')
parser.add_option('-d',
'--output_dir',
type='string',
default=None,
help='Output directory for test logs. Logs will be '
'available under gtest-parallel-logs/, so '
'--output_dir=/tmp will results in all logs being '
'available under /tmp/gtest-parallel-logs/.')
parser.add_option('-r',
'--repeat',
type='int',
default=1,
help='Number of times to execute all the tests.')
parser.add_option('--retry_failed',
type='int',
default=0,
help='Number of times to repeat failed tests.')
parser.add_option('--failed',
action='store_true',
default=False,
help='run only failed and new tests')
parser.add_option('-w',
'--workers',
type='int',
default=multiprocessing.cpu_count(),
help='number of workers to spawn')
parser.add_option('--gtest_color',
type='string',
default='yes',
help='color output')
parser.add_option('--gtest_filter',
type='string',
default='',
help='test filter')
parser.add_option('--gtest_also_run_disabled_tests',
action='store_true',
default=False,
help='run disabled tests too')
parser.add_option(
'--print_test_times',
action='store_true',
default=False,
help='list the run time of each test at the end of execution')
parser.add_option(
'--print_test_command',
action='store_true',
default=False,
help='Print full test command instead of name')
parser.add_option('--shard_count',
type='int',
default=1,
help='total number of shards (for sharding test execution '
'between multiple machines)')
parser.add_option('--shard_index',
type='int',
default=0,
help='zero-indexed number identifying this shard (for '
'sharding test execution between multiple machines)')
parser.add_option(
'--dump_json_test_results',
type='string',
default=None,
help='Saves the results of the tests as a JSON machine-'
'readable file. The format of the file is specified at '
'https://www.chromium.org/developers/the-json-test-results-format')
parser.add_option('--timeout',
type='int',
default=None,
help='Interrupt all remaining processes after the given '
'time (in seconds).')
parser.add_option('--timeout_per_test',
type='int',
default=None,
help='Interrupt single processes after the given '
'time (in seconds).')
parser.add_option('--serialize_test_cases',
action='store_true',
default=False,
help='Do not run tests from the same test '
'case in parallel.')
return parser
def main():
# Remove additional arguments (anything after --).
additional_args = []
for i in range(len(sys.argv)):
if sys.argv[i] == '--':
additional_args = sys.argv[i + 1:]
sys.argv = sys.argv[:i]
break
parser = default_options_parser()
(options, binaries) = parser.parse_args()
if (options.output_dir is not None and not os.path.isdir(options.output_dir)):
parser.error('--output_dir value must be an existing directory, '
'current value is "%s"' % options.output_dir)
# Append gtest-parallel-logs to log output, this is to avoid deleting user
# data if an user passes a directory where files are already present. If a
# user specifies --output_dir=Docs/, we'll create Docs/gtest-parallel-logs
# and clean that directory out on startup, instead of nuking Docs/.
if options.output_dir:
options.output_dir = os.path.join(options.output_dir, 'gtest-parallel-logs')
if binaries == []:
parser.print_usage()
sys.exit(1)
if options.shard_count < 1:
parser.error("Invalid number of shards: %d. Must be at least 1." %
options.shard_count)
if not (0 <= options.shard_index < options.shard_count):
parser.error("Invalid shard index: %d. Must be between 0 and %d "
"(less than the number of shards)." %
(options.shard_index, options.shard_count - 1))
# Check that all test binaries have an unique basename. That way we can ensure
# the logs are saved to unique files even when two different binaries have
# common tests.
unique_binaries = set(os.path.basename(binary) for binary in binaries)
assert len(unique_binaries) == len(binaries), (
"All test binaries must have an unique basename.")
if options.output_dir:
# Remove files from old test runs.
if os.path.isdir(options.output_dir):
shutil.rmtree(options.output_dir)
# Create directory for test log output.
try:
os.makedirs(options.output_dir)
except OSError as e:
# Ignore errors if this directory already exists.
if e.errno != errno.EEXIST or not os.path.isdir(options.output_dir):
raise e
test_results = None
if options.dump_json_test_results is not None:
test_results = CollectTestResults(options.dump_json_test_results)
save_file = get_save_file_path()
times = TestTimes(save_file)
logger = FilterFormat(options.output_dir)
task_manager = TaskManager(times, logger, test_results, Task,
options.retry_failed, options.repeat + 1)
tasks = find_tests(binaries, additional_args, options, times)
logger.log_tasks(len(tasks))
execute_tasks(tasks, options.workers, task_manager, options.timeout,
options.timeout_per_test, options.serialize_test_cases)
print_try_number = options.retry_failed > 0 or options.repeat > 1
if task_manager.passed:
logger.move_to('passed', task_manager.passed)
if options.print_test_times:
logger.print_tests('PASSED TESTS', task_manager.passed, print_try_number, options.print_test_command)
if task_manager.failed:
logger.print_tests('FAILED TESTS', task_manager.failed, print_try_number, options.print_test_command)
logger.move_to('failed', task_manager.failed)
if task_manager.timed_out:
logger.print_tests('TIMED OUT TESTS', task_manager.timed_out, print_try_number, options.print_test_command)
logger.move_to('timed_out', task_manager.timed_out)
if task_manager.started:
logger.print_tests('INTERRUPTED TESTS', task_manager.started.values(),
print_try_number, options.print_test_command)
logger.move_to('interrupted', task_manager.started.values())
if options.repeat > 1 and (task_manager.failed or task_manager.started):
logger.summarize(task_manager.passed, task_manager.failed,
task_manager.started.values())
logger.flush()
times.write_to_file(save_file)
if test_results:
test_results.dump_to_file_and_close()
if sigint_handler.got_sigint():
return -signal.SIGINT
return task_manager.global_exit_code
if __name__ == "__main__":
sys.exit(main())
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# 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).
#
# Build RocksDB unit test binaries and run them under gtest-parallel,
# which shards the test cases across CPUs for faster execution.
#
# Hardened version of a simple `make <bin> && gtest-parallel ./<bin>` helper:
# * AUTO_CLEAN=1 so the Makefile automatically runs a clean when the build
# parameters (DEBUG_LEVEL, sanitizers, ASSERT_STATUS_CHECKED, RTTI, etc.)
# have changed since the last build, preventing stale/mixed object files.
# * Parallel build with -j<NCORES>, computed the same way the Makefile does.
# * Uses the gtest-parallel checked in alongside this script (build_tools/),
# so it works regardless of PATH.
# * `set -euo pipefail` so any failure stops the script.
#
# Run from the repository root.
#
# Accepts one or more test binaries (gtest-parallel pools all their test cases
# into one shared worker queue). The leading non-option arguments are treated as
# binaries; everything from the first option onward is forwarded to
# gtest-parallel / the test binaries.
#
# Example usage:
# build_tools/rocksptest.sh db_test
# build_tools/rocksptest.sh db_test -r1000 --gtest_filter=*MixedSlowdown*
# build_tools/rocksptest.sh db_test env_test db_basic_test
# build_tools/rocksptest.sh db_test env_test --gtest_filter=*Foo*
set -euo pipefail
if [ "$#" -lt 1 ]; then
echo "usage: $0 <test_binary> [more_test_binaries...] [gtest-parallel/test args...]" >&2
echo "example: $0 db_test env_test -r1000 --gtest_filter=*MixedSlowdown*" >&2
exit 1
fi
# First argument must be a test binary, not an option (catches a forgotten name).
if [ "${1#-}" != "$1" ]; then
echo "$0: first argument must be a test binary name, not an option ('$1')" >&2
exit 1
fi
# Collect the leading non-option arguments as test binaries; everything from the
# first option onward is forwarded to gtest-parallel / the test binaries.
BINS=()
while [ "$#" -gt 0 ] && [ "${1#-}" = "$1" ]; do
BINS+=("$1")
shift
done
# Paths as gtest-parallel expects them (relative to the current directory).
BIN_PATHS=()
for b in "${BINS[@]}"; do
BIN_PATHS+=("./$b")
done
# Directory of this script, so we use the gtest-parallel checked in next to it.
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# Compute parallelism the same way the Makefile computes NCORES.
NCORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
make AUTO_CLEAN=1 -j"$NCORES" "${BINS[@]}"
"$SCRIPT_DIR/gtest-parallel" "${BIN_PATHS[@]}" "$@"
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# 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).
#
# Build a single RocksDB unit test binary and run it directly (serially).
# Only recommended with --gtest_filter=... because of speed. See also
# build_tools/rocksptest.sh
#
# Hardened version of a simple `make <bin> && ./<bin>` helper:
# * AUTO_CLEAN=1 so the Makefile automatically runs a clean when the build
# parameters (DEBUG_LEVEL, sanitizers, ASSERT_STATUS_CHECKED, RTTI, etc.)
# have changed since the last build, preventing stale/mixed object files.
# * Parallel build with -j<NCORES>, computed the same way the Makefile does.
# * `set -euo pipefail` so any failure stops the script.
#
# Run from the repository root.
#
# Example usage:
# build_tools/rockstest.sh db_test --gtest_filter=*MixedSlowdown*
# build_tools/rockstest.sh env_test # Slow to run many tests serially
#
# Install mode:
# build_tools/rockstest.sh install
# Creates ~/bin/rockstest and ~/bin/rocksptest shims that defer to
# build_tools/rockstest.sh / rocksptest.sh in whatever directory you run
# them from, so you can just type `rockstest db_test` from any rocksdb
# source root.
set -euo pipefail
# Install mode: write thin ~/bin shims that defer to the build_tools scripts in
# the current directory.
if [ "${1:-}" = "install" ]; then
mkdir -p "$HOME/bin"
for name in rockstest rocksptest; do
dest="$HOME/bin/$name"
cat > "$dest" <<EOF
#!/usr/bin/env bash
# Auto-generated by 'build_tools/rockstest.sh install'. Defers to
# build_tools/$name.sh in the current rocksdb source root.
if [ -x build_tools/$name.sh ]; then
exec build_tools/$name.sh "\$@"
else
echo "build_tools/$name.sh not found (Not in a rocksdb source root directory?)" >&2
exit 1
fi
EOF
chmod +x "$dest"
echo "Installed $dest"
done
exit 0
fi
if [ "$#" -lt 1 ]; then
echo "usage: $0 <test_binary> [test args...]" >&2
echo " $0 install # create ~/bin/rockstest and ~/bin/rocksptest shims" >&2
echo "example: $0 db_test --gtest_filter=*MixedSlowdown*" >&2
exit 1
fi
# First argument must be a test binary, not an option (catches a forgotten name).
if [ "${1#-}" != "$1" ]; then
echo "$0: first argument must be a test binary name, not an option ('$1')" >&2
exit 1
fi
BIN=$1
shift
# Compute parallelism the same way the Makefile computes NCORES.
NCORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
make AUTO_CLEAN=1 -j"$NCORES" "$BIN"
./"$BIN" "$@"
+1 -1
View File
@@ -23,7 +23,7 @@ mkdir -pv /usr/local/rocksdb-${ROCKSDB_VERSION}
ln -sfT /usr/local/rocksdb-${ROCKSDB_VERSION} /usr/local/rocksdb
wget -qO /tmp/zstd-${ZSTD_VERSION}.tar.gz https://github.com/facebook/zstd/archive/v${ZSTD_VERSION}.tar.gz
wget -qO /tmp/rocksdb-${ROCKSDB_VERSION}.tar.gz https://github.com/facebook/rocksdb/archive/v${ROCKSDB_VERSION}.tar.gz
wget -qO /tmp/rocksdb-${ROCKSDB_VERSION}.tar.gz https://github.com/meta/rocksdb/archive/v${ROCKSDB_VERSION}.tar.gz
cd /tmp
+2 -2
View File
@@ -17,8 +17,8 @@
# Then in the build_tools/ubuntu22_image directory, (bump minor version for
# random docker file updates, major version tracks Ubuntu release)
#
# $ docker build -t ghcr.io/facebook/rocksdb_ubuntu:22.2
# $ docker push ghcr.io/facebook/rocksdb_ubuntu:22.2
# $ docker build -t ghcr.io/meta/rocksdb_ubuntu:22.2
# $ docker push ghcr.io/meta/rocksdb_ubuntu:22.2
#
# Might need to change visibility to public through
# https://github.com/orgs/facebook/packages/container/rocksdb_ubuntu/settings
+2 -2
View File
@@ -17,8 +17,8 @@
# Then in the build_tools/ubuntu24_image directory, (bump minor version for
# random docker file updates, major version tracks Ubuntu release)
#
# $ docker build -t ghcr.io/facebook/rocksdb_ubuntu:24.1
# $ docker push ghcr.io/facebook/rocksdb_ubuntu:24.1
# $ docker build -t ghcr.io/meta/rocksdb_ubuntu:24.1
# $ docker push ghcr.io/meta/rocksdb_ubuntu:24.1
#
# Might need to change visibility to public through
# https://github.com/orgs/facebook/packages/container/rocksdb_ubuntu/settings
+24 -9
View File
@@ -32,7 +32,7 @@ function get_lib_base()
local lib_platform=$3
local result="$TP2_LATEST/$lib_name/"
# Lib Version
if [ -z "$lib_version" ] || [ "$lib_version" = "LATEST" ]; then
# version is not provided, use latest
@@ -40,7 +40,7 @@ function get_lib_base()
else
result="$result/$lib_version/"
fi
# Lib Platform
if [ -z "$lib_platform" ]; then
# platform is not provided, use latest gcc
@@ -49,17 +49,17 @@ function get_lib_base()
echo $lib_platform
result="$result/$lib_platform/"
fi
result=`ls -1d $result/*/ | head -n1`
echo Finding link $result
# lib_name => LIB_NAME_BASE
local __res_var=${lib_name^^}"_BASE"
__res_var=`echo $__res_var | tr - _`
# LIB_NAME_BASE=$result
eval $__res_var=`readlink -f $result`
log_variable $__res_var
}
@@ -75,17 +75,33 @@ touch "$OUTPUT"
echo "Writing dependencies to $OUTPUT"
# Compilers locations
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos8-native/*/`
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/15/platform010/*/`
# GCC is pinned to 11.x because the only newer GCC in third-party2 (13.x) is
# built for centos9/glibc>=2.35 -- its libgcc_s.so.1 has a hard reference
# to _dl_find_object@GLIBC_2.35, but platform010 ships glibc 2.34. Bumping
# GCC requires a platform with glibc >= 2.35.
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos9-native/*/`
# Clang is pinned to the latest tested major (21). Bump deliberately.
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/21/platform010/*/`
log_header
log_variable GCC_BASE
log_variable CLANG_BASE
# Libraries locations
# libgcc is pinned to 11.x to match the GCC 11 compiler above (libstdc++/
# libgcc runtime, ABI, and C++ headers). Bump in lockstep with GCC_BASE.
get_lib_base libgcc 11.x platform010
# glibc 2.34 is the platform010 ABI baseline (ld.so + libc); it is also the
# only version available in third-party2, and defines the platform -- do
# not bump independently.
get_lib_base glibc 2.34 platform010
get_lib_base snappy LATEST platform010
# zlib is pinned to 1.2.8: it is the latest version with an x86_64
# platform010 build (1.2.13 / 1.3.1 are centos9 / aarch64 only). At time of
# writing, LATEST here doesn't work: get_lib_base picks the newest version
# dir first and only then appends the platform, with no fallback -- so
# LATEST would resolve to 1.3.1, find no platform010 build, and emit an
# empty ZLIB_BASE.
get_lib_base zlib 1.2.8 platform010
get_lib_base bzip2 LATEST platform010
get_lib_base lz4 LATEST platform010
@@ -94,12 +110,11 @@ get_lib_base gflags LATEST platform010
get_lib_base jemalloc LATEST platform010
get_lib_base numa LATEST platform010
get_lib_base libunwind LATEST platform010
get_lib_base tbb 2018_U5 platform010
get_lib_base liburing LATEST platform010
get_lib_base benchmark LATEST platform010
get_lib_base kernel-headers fb platform010
get_lib_base binutils LATEST centos8-native
get_lib_base binutils LATEST centos9-native
get_lib_base valgrind LATEST platform010
git diff $OUTPUT
@@ -0,0 +1,43 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'BlockBasedOptions simple'
// --header-out
// c_api_gen/c_generated_block_based_options_subset.h.inc
// --source-out
// c_api_gen/c_generated_block_based_options_subset.cc.inc
/* BlockBasedOptions simple */
void rocksdb_block_based_options_set_data_block_hash_ratio(
rocksdb_block_based_table_options_t* options, double v) {
options->rep.data_block_hash_table_util_ratio = v;
}
void rocksdb_block_based_options_set_top_level_index_pinning_tier(
rocksdb_block_based_table_options_t* options, int v) {
options->rep.metadata_cache_options.top_level_index_pinning =
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
}
void rocksdb_block_based_options_set_partition_pinning_tier(
rocksdb_block_based_table_options_t* options, int v) {
options->rep.metadata_cache_options.partition_pinning =
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
}
void rocksdb_block_based_options_set_unpartitioned_pinning_tier(
rocksdb_block_based_table_options_t* options, int v) {
options->rep.metadata_cache_options.unpartitioned_pinning =
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
}
@@ -0,0 +1,36 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'BlockBasedOptions simple'
// --header-out
// c_api_gen/c_generated_block_based_options_subset.h.inc
// --source-out
// c_api_gen/c_generated_block_based_options_subset.cc.inc
/* BlockBasedOptions simple */
extern ROCKSDB_LIBRARY_API void
rocksdb_block_based_options_set_data_block_hash_ratio(
rocksdb_block_based_table_options_t* options, double v);
extern ROCKSDB_LIBRARY_API void
rocksdb_block_based_options_set_top_level_index_pinning_tier(
rocksdb_block_based_table_options_t* options, int v);
extern ROCKSDB_LIBRARY_API void
rocksdb_block_based_options_set_partition_pinning_tier(
rocksdb_block_based_table_options_t* options, int v);
extern ROCKSDB_LIBRARY_API void
rocksdb_block_based_options_set_unpartitioned_pinning_tier(
rocksdb_block_based_table_options_t* options, int v);
@@ -0,0 +1,25 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'CuckooOptions simple'
// --header-out
// c_api_gen/c_generated_cuckoo_options_subset.h.inc
// --source-out
// c_api_gen/c_generated_cuckoo_options_subset.cc.inc
/* CuckooOptions simple */
void rocksdb_cuckoo_options_set_hash_ratio(
rocksdb_cuckoo_table_options_t* options, double v) {
options->rep.hash_table_ratio = v;
}
@@ -0,0 +1,23 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'CuckooOptions simple'
// --header-out
// c_api_gen/c_generated_cuckoo_options_subset.h.inc
// --source-out
// c_api_gen/c_generated_cuckoo_options_subset.cc.inc
/* CuckooOptions simple */
extern ROCKSDB_LIBRARY_API void rocksdb_cuckoo_options_set_hash_ratio(
rocksdb_cuckoo_table_options_t* options, double v);
@@ -0,0 +1,193 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'DB data operations'
// --header-out
// c_api_gen/c_generated_db_simple_subset.h.inc
// --source-out
// c_api_gen/c_generated_db_simple_subset.cc.inc
/* DB data operations */
void rocksdb_put(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, const char* val, size_t vallen,
char** errptr) {
SaveError(errptr,
db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen)));
}
void rocksdb_put_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, const char* val,
size_t vallen, char** errptr) {
SaveError(errptr, db->rep->Put(options->rep, column_family->rep,
Slice(key, keylen), Slice(val, vallen)));
}
void rocksdb_delete(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, char** errptr) {
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen)));
}
void rocksdb_delete_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, char** errptr) {
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
Slice(key, keylen)));
}
void rocksdb_merge(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, const char* val,
size_t vallen, char** errptr) {
SaveError(errptr, db->rep->Merge(options->rep, Slice(key, keylen),
Slice(val, vallen)));
}
void rocksdb_merge_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, const char* val,
size_t vallen, char** errptr) {
SaveError(errptr, db->rep->Merge(options->rep, column_family->rep,
Slice(key, keylen), Slice(val, vallen)));
}
void rocksdb_write(rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_writebatch_t* batch, char** errptr) {
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
}
void rocksdb_put_with_ts(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, const char* ts,
size_t tslen, const char* val, size_t vallen,
char** errptr) {
SaveError(errptr, db->rep->Put(options->rep, Slice(key, keylen),
Slice(ts, tslen), Slice(val, vallen)));
}
void rocksdb_put_cf_with_ts(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, const char* ts,
size_t tslen, const char* val, size_t vallen,
char** errptr) {
SaveError(errptr,
db->rep->Put(options->rep, column_family->rep, Slice(key, keylen),
Slice(ts, tslen), Slice(val, vallen)));
}
void rocksdb_delete_with_ts(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, const char* ts,
size_t tslen, char** errptr) {
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen),
Slice(ts, tslen)));
}
void rocksdb_delete_cf_with_ts(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, const char* ts,
size_t tslen, char** errptr) {
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
Slice(key, keylen), Slice(ts, tslen)));
}
void rocksdb_singledelete(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, char** errptr) {
SaveError(errptr, db->rep->SingleDelete(options->rep, Slice(key, keylen)));
}
void rocksdb_singledelete_cf(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, char** errptr) {
SaveError(errptr, db->rep->SingleDelete(options->rep, column_family->rep,
Slice(key, keylen)));
}
void rocksdb_singledelete_with_ts(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
const char* key, size_t keylen,
const char* ts, size_t tslen, char** errptr) {
SaveError(errptr, db->rep->SingleDelete(options->rep, Slice(key, keylen),
Slice(ts, tslen)));
}
void rocksdb_singledelete_cf_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr) {
SaveError(errptr,
db->rep->SingleDelete(options->rep, column_family->rep,
Slice(key, keylen), Slice(ts, tslen)));
}
void rocksdb_delete_range_cf(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* start_key, size_t start_key_len,
const char* end_key, size_t end_key_len,
char** errptr) {
SaveError(errptr, db->rep->DeleteRange(options->rep, column_family->rep,
Slice(start_key, start_key_len),
Slice(end_key, end_key_len)));
}
void rocksdb_flush(rocksdb_t* db, const rocksdb_flushoptions_t* options,
char** errptr) {
SaveError(errptr, db->rep->Flush(options->rep));
}
void rocksdb_flush_cf(rocksdb_t* db, const rocksdb_flushoptions_t* options,
rocksdb_column_family_handle_t* column_family,
char** errptr) {
SaveError(errptr, db->rep->Flush(options->rep, column_family->rep));
}
void rocksdb_flush_wal(rocksdb_t* db, unsigned char sync, char** errptr) {
SaveError(errptr, db->rep->FlushWAL(sync));
}
void rocksdb_pause_background_work(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->PauseBackgroundWork());
}
void rocksdb_continue_background_work(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->ContinueBackgroundWork());
}
void rocksdb_disable_file_deletions(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->DisableFileDeletions());
}
void rocksdb_enable_file_deletions(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->EnableFileDeletions());
}
void rocksdb_verify_checksum(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->VerifyChecksum());
}
void rocksdb_verify_file_checksums(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->VerifyFileChecksums(ReadOptions()));
}
void rocksdb_destroy_db(const rocksdb_options_t* options, const char* name,
char** errptr) {
SaveError(errptr, DestroyDB(name, options->rep));
}
void rocksdb_repair_db(const rocksdb_options_t* options, const char* name,
char** errptr) {
SaveError(errptr, RepairDB(name, options->rep));
}
@@ -0,0 +1,130 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'DB data operations'
// --header-out
// c_api_gen/c_generated_db_simple_subset.h.inc
// --source-out
// c_api_gen/c_generated_db_simple_subset.cc.inc
/* DB data operations */
extern ROCKSDB_LIBRARY_API void rocksdb_put(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_put_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_merge(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_merge_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_write(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_writebatch_t* batch, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_put_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* ts, size_t tslen, const char* val, size_t vallen,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_put_cf_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* ts, size_t tslen, const char* val, size_t vallen,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete_cf_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_cf_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete_range_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* start_key,
size_t start_key_len, const char* end_key, size_t end_key_len,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_flush(
rocksdb_t* db, const rocksdb_flushoptions_t* options, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_flush_cf(
rocksdb_t* db, const rocksdb_flushoptions_t* options,
rocksdb_column_family_handle_t* column_family, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_flush_wal(rocksdb_t* db,
unsigned char sync,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_pause_background_work(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_continue_background_work(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_disable_file_deletions(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_enable_file_deletions(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_verify_checksum(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_verify_file_checksums(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_destroy_db(
const rocksdb_options_t* options, const char* name, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_repair_db(
const rocksdb_options_t* options, const char* name, char** errptr);
+271
View File
@@ -0,0 +1,271 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Sources:
// - include/rocksdb/listener.h
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
// - include/rocksdb/c.h
// - tools/c_api_gen/c_base.cc
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/auto_simple_bindings.py
// Output group: Auto-discovered JobInfo metadata simple
/* FlushJobInfo */
uint32_t rocksdb_flushjobinfo_cf_id(const rocksdb_flushjobinfo_t* info) {
return info->rep.cf_id;
}
const char* rocksdb_flushjobinfo_cf_name(const rocksdb_flushjobinfo_t* info,
size_t* size) {
*size = info->rep.cf_name.size();
return info->rep.cf_name.data();
}
const char* rocksdb_flushjobinfo_file_path(const rocksdb_flushjobinfo_t* info,
size_t* size) {
*size = info->rep.file_path.size();
return info->rep.file_path.data();
}
uint64_t rocksdb_flushjobinfo_file_number(const rocksdb_flushjobinfo_t* info) {
return info->rep.file_number;
}
uint64_t rocksdb_flushjobinfo_oldest_blob_file_number(
const rocksdb_flushjobinfo_t* info) {
return info->rep.oldest_blob_file_number;
}
uint64_t rocksdb_flushjobinfo_thread_id(const rocksdb_flushjobinfo_t* info) {
return info->rep.thread_id;
}
int rocksdb_flushjobinfo_job_id(const rocksdb_flushjobinfo_t* info) {
return info->rep.job_id;
}
unsigned char rocksdb_flushjobinfo_triggered_writes_slowdown(
const rocksdb_flushjobinfo_t* info) {
return info->rep.triggered_writes_slowdown;
}
unsigned char rocksdb_flushjobinfo_triggered_writes_stop(
const rocksdb_flushjobinfo_t* info) {
return info->rep.triggered_writes_stop;
}
uint64_t rocksdb_flushjobinfo_smallest_seqno(
const rocksdb_flushjobinfo_t* info) {
return info->rep.smallest_seqno;
}
uint64_t rocksdb_flushjobinfo_largest_seqno(
const rocksdb_flushjobinfo_t* info) {
return info->rep.largest_seqno;
}
uint32_t rocksdb_flushjobinfo_flush_reason(const rocksdb_flushjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.flush_reason);
}
uint32_t rocksdb_flushjobinfo_blob_compression_type(
const rocksdb_flushjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.blob_compression_type);
}
/* CompactionJobInfo */
uint32_t rocksdb_compactionjobinfo_cf_id(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.cf_id;
}
const char* rocksdb_compactionjobinfo_cf_name(
const rocksdb_compactionjobinfo_t* info, size_t* size) {
*size = info->rep.cf_name.size();
return info->rep.cf_name.data();
}
void rocksdb_compactionjobinfo_status(const rocksdb_compactionjobinfo_t* info,
char** errptr) {
SaveError(errptr, info->rep.status);
}
uint64_t rocksdb_compactionjobinfo_thread_id(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.thread_id;
}
int rocksdb_compactionjobinfo_job_id(const rocksdb_compactionjobinfo_t* info) {
return info->rep.job_id;
}
unsigned char
rocksdb_compactionjobinfo_separate_remote_compaction_pool_selected(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.separate_remote_compaction_pool_selected;
}
int rocksdb_compactionjobinfo_num_l0_files(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.num_l0_files;
}
int rocksdb_compactionjobinfo_base_input_level(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.base_input_level;
}
int rocksdb_compactionjobinfo_output_level(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.output_level;
}
uint32_t rocksdb_compactionjobinfo_compaction_reason(
const rocksdb_compactionjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.compaction_reason);
}
uint32_t rocksdb_compactionjobinfo_compression(
const rocksdb_compactionjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.compression);
}
uint32_t rocksdb_compactionjobinfo_blob_compression_type(
const rocksdb_compactionjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.blob_compression_type);
}
unsigned char rocksdb_compactionjobinfo_aborted(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.aborted;
}
/* SubcompactionJobInfo */
uint32_t rocksdb_subcompactionjobinfo_cf_id(
const rocksdb_subcompactionjobinfo_t* info) {
return info->rep.cf_id;
}
const char* rocksdb_subcompactionjobinfo_cf_name(
const rocksdb_subcompactionjobinfo_t* info, size_t* size) {
*size = info->rep.cf_name.size();
return info->rep.cf_name.data();
}
void rocksdb_subcompactionjobinfo_status(
const rocksdb_subcompactionjobinfo_t* info, char** errptr) {
SaveError(errptr, info->rep.status);
}
uint64_t rocksdb_subcompactionjobinfo_thread_id(
const rocksdb_subcompactionjobinfo_t* info) {
return info->rep.thread_id;
}
int rocksdb_subcompactionjobinfo_job_id(
const rocksdb_subcompactionjobinfo_t* info) {
return info->rep.job_id;
}
unsigned char
rocksdb_subcompactionjobinfo_separate_remote_compaction_pool_selected(
const rocksdb_subcompactionjobinfo_t* info) {
return info->rep.separate_remote_compaction_pool_selected;
}
int rocksdb_subcompactionjobinfo_subcompaction_job_id(
const rocksdb_subcompactionjobinfo_t* info) {
return info->rep.subcompaction_job_id;
}
int rocksdb_subcompactionjobinfo_base_input_level(
const rocksdb_subcompactionjobinfo_t* info) {
return info->rep.base_input_level;
}
int rocksdb_subcompactionjobinfo_output_level(
const rocksdb_subcompactionjobinfo_t* info) {
return info->rep.output_level;
}
uint32_t rocksdb_subcompactionjobinfo_compaction_reason(
const rocksdb_subcompactionjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.compaction_reason);
}
uint32_t rocksdb_subcompactionjobinfo_compression(
const rocksdb_subcompactionjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.compression);
}
uint32_t rocksdb_subcompactionjobinfo_blob_compression_type(
const rocksdb_subcompactionjobinfo_t* info) {
return static_cast<uint32_t>(info->rep.blob_compression_type);
}
/* ExternalFileIngestionInfo */
const char* rocksdb_externalfileingestioninfo_cf_name(
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
*size = info->rep.cf_name.size();
return info->rep.cf_name.data();
}
const char* rocksdb_externalfileingestioninfo_external_file_path(
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
*size = info->rep.external_file_path.size();
return info->rep.external_file_path.data();
}
const char* rocksdb_externalfileingestioninfo_internal_file_path(
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
*size = info->rep.internal_file_path.size();
return info->rep.internal_file_path.data();
}
uint64_t rocksdb_externalfileingestioninfo_global_seqno(
const rocksdb_externalfileingestioninfo_t* info) {
return info->rep.global_seqno;
}
/* MemTableInfo */
const char* rocksdb_memtableinfo_cf_name(const rocksdb_memtableinfo_t* info,
size_t* size) {
*size = info->rep.cf_name.size();
return info->rep.cf_name.data();
}
uint64_t rocksdb_memtableinfo_first_seqno(const rocksdb_memtableinfo_t* info) {
return info->rep.first_seqno;
}
uint64_t rocksdb_memtableinfo_earliest_seqno(
const rocksdb_memtableinfo_t* info) {
return info->rep.earliest_seqno;
}
uint64_t rocksdb_memtableinfo_num_entries(const rocksdb_memtableinfo_t* info) {
return info->rep.num_entries;
}
uint64_t rocksdb_memtableinfo_num_deletes(const rocksdb_memtableinfo_t* info) {
return info->rep.num_deletes;
}
const char* rocksdb_memtableinfo_newest_udt(const rocksdb_memtableinfo_t* info,
size_t* size) {
*size = info->rep.newest_udt.size();
return info->rep.newest_udt.data();
}
+185
View File
@@ -0,0 +1,185 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Sources:
// - include/rocksdb/listener.h
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
// - include/rocksdb/c.h
// - tools/c_api_gen/c_base.cc
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/auto_simple_bindings.py
// Output group: Auto-discovered JobInfo metadata simple
/* FlushJobInfo */
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_flushjobinfo_cf_id(const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API const char* rocksdb_flushjobinfo_cf_name(
const rocksdb_flushjobinfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API const char* rocksdb_flushjobinfo_file_path(
const rocksdb_flushjobinfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_flushjobinfo_file_number(const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_flushjobinfo_oldest_blob_file_number(
const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_flushjobinfo_thread_id(const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int rocksdb_flushjobinfo_job_id(
const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_flushjobinfo_triggered_writes_slowdown(
const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_flushjobinfo_triggered_writes_stop(const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_flushjobinfo_smallest_seqno(const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_flushjobinfo_largest_seqno(const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_flushjobinfo_flush_reason(const rocksdb_flushjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_flushjobinfo_blob_compression_type(const rocksdb_flushjobinfo_t* info);
/* CompactionJobInfo */
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_compactionjobinfo_cf_id(const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API const char* rocksdb_compactionjobinfo_cf_name(
const rocksdb_compactionjobinfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API void rocksdb_compactionjobinfo_status(
const rocksdb_compactionjobinfo_t* info, char** errptr);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compactionjobinfo_thread_id(const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int rocksdb_compactionjobinfo_job_id(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_compactionjobinfo_separate_remote_compaction_pool_selected(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int rocksdb_compactionjobinfo_num_l0_files(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int rocksdb_compactionjobinfo_base_input_level(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int rocksdb_compactionjobinfo_output_level(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint32_t rocksdb_compactionjobinfo_compaction_reason(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_compactionjobinfo_compression(const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_compactionjobinfo_blob_compression_type(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_compactionjobinfo_aborted(
const rocksdb_compactionjobinfo_t* info);
/* SubcompactionJobInfo */
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_subcompactionjobinfo_cf_id(const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API const char* rocksdb_subcompactionjobinfo_cf_name(
const rocksdb_subcompactionjobinfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API void rocksdb_subcompactionjobinfo_status(
const rocksdb_subcompactionjobinfo_t* info, char** errptr);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_subcompactionjobinfo_thread_id(
const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int rocksdb_subcompactionjobinfo_job_id(
const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_subcompactionjobinfo_separate_remote_compaction_pool_selected(
const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int
rocksdb_subcompactionjobinfo_subcompaction_job_id(
const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int rocksdb_subcompactionjobinfo_base_input_level(
const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API int rocksdb_subcompactionjobinfo_output_level(
const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_subcompactionjobinfo_compaction_reason(
const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint32_t rocksdb_subcompactionjobinfo_compression(
const rocksdb_subcompactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_subcompactionjobinfo_blob_compression_type(
const rocksdb_subcompactionjobinfo_t* info);
/* ExternalFileIngestionInfo */
extern ROCKSDB_LIBRARY_API const char*
rocksdb_externalfileingestioninfo_cf_name(
const rocksdb_externalfileingestioninfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_externalfileingestioninfo_external_file_path(
const rocksdb_externalfileingestioninfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_externalfileingestioninfo_internal_file_path(
const rocksdb_externalfileingestioninfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_externalfileingestioninfo_global_seqno(
const rocksdb_externalfileingestioninfo_t* info);
/* MemTableInfo */
extern ROCKSDB_LIBRARY_API const char* rocksdb_memtableinfo_cf_name(
const rocksdb_memtableinfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_memtableinfo_first_seqno(const rocksdb_memtableinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_memtableinfo_earliest_seqno(const rocksdb_memtableinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_memtableinfo_num_entries(const rocksdb_memtableinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_memtableinfo_num_deletes(const rocksdb_memtableinfo_t* info);
extern ROCKSDB_LIBRARY_API const char* rocksdb_memtableinfo_newest_udt(
const rocksdb_memtableinfo_t* info, size_t* size);
@@ -0,0 +1,88 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'JobInfo metadata simple'
// --header-out
// c_api_gen/c_generated_jobinfo_metadata_subset.h.inc
// --source-out
// c_api_gen/c_generated_jobinfo_metadata_subset.cc.inc
/* JobInfo metadata simple */
size_t rocksdb_compactionjobinfo_input_files_count(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.input_files.size();
}
size_t rocksdb_compactionjobinfo_output_files_count(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.output_files.size();
}
uint64_t rocksdb_compactionjobinfo_elapsed_micros(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.stats.elapsed_micros;
}
uint64_t rocksdb_compactionjobinfo_num_corrupt_keys(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.stats.num_corrupt_keys;
}
uint64_t rocksdb_compactionjobinfo_input_records(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.stats.num_input_records;
}
uint64_t rocksdb_compactionjobinfo_output_records(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.stats.num_output_records;
}
uint64_t rocksdb_compactionjobinfo_total_input_bytes(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.stats.total_input_bytes;
}
uint64_t rocksdb_compactionjobinfo_total_output_bytes(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.stats.total_output_bytes;
}
size_t rocksdb_compactionjobinfo_num_input_files(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.stats.num_input_files;
}
size_t rocksdb_compactionjobinfo_num_input_files_at_output_level(
const rocksdb_compactionjobinfo_t* info) {
return info->rep.stats.num_input_files_at_output_level;
}
const char* rocksdb_writestallinfo_cf_name(const rocksdb_writestallinfo_t* info,
size_t* size) {
*size = info->rep.cf_name.size();
return info->rep.cf_name.data();
}
const rocksdb_writestallcondition_t* rocksdb_writestallinfo_cur(
const rocksdb_writestallinfo_t* info) {
return reinterpret_cast<const rocksdb_writestallcondition_t*>(
&info->rep.condition.cur);
}
const rocksdb_writestallcondition_t* rocksdb_writestallinfo_prev(
const rocksdb_writestallinfo_t* info) {
return reinterpret_cast<const rocksdb_writestallcondition_t*>(
&info->rep.condition.prev);
}
@@ -0,0 +1,61 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'JobInfo metadata simple'
// --header-out
// c_api_gen/c_generated_jobinfo_metadata_subset.h.inc
// --source-out
// c_api_gen/c_generated_jobinfo_metadata_subset.cc.inc
/* JobInfo metadata simple */
extern ROCKSDB_LIBRARY_API size_t rocksdb_compactionjobinfo_input_files_count(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API size_t rocksdb_compactionjobinfo_output_files_count(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_elapsed_micros(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_num_corrupt_keys(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_input_records(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_output_records(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compactionjobinfo_total_input_bytes(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compactionjobinfo_total_output_bytes(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API size_t rocksdb_compactionjobinfo_num_input_files(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API size_t
rocksdb_compactionjobinfo_num_input_files_at_output_level(
const rocksdb_compactionjobinfo_t* info);
extern ROCKSDB_LIBRARY_API const char* rocksdb_writestallinfo_cf_name(
const rocksdb_writestallinfo_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API const rocksdb_writestallcondition_t*
rocksdb_writestallinfo_cur(const rocksdb_writestallinfo_t* info);
extern ROCKSDB_LIBRARY_API const rocksdb_writestallcondition_t*
rocksdb_writestallinfo_prev(const rocksdb_writestallinfo_t* info);
@@ -0,0 +1,537 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Sources:
// - include/rocksdb/compaction_job_stats.h
// - include/rocksdb/listener.h
// - include/rocksdb/table_properties.h
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
// - include/rocksdb/c.h
// - tools/c_api_gen/c_base.cc
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/auto_simple_bindings.py
// Output group: Auto-discovered metadata view structs simple
/* TableProperties */
uint64_t rocksdb_table_properties_orig_file_number(
const rocksdb_table_properties_t* props) {
return props->rep.orig_file_number;
}
uint64_t rocksdb_table_properties_data_size(
const rocksdb_table_properties_t* props) {
return props->rep.data_size;
}
uint64_t rocksdb_table_properties_uncompressed_data_size(
const rocksdb_table_properties_t* props) {
return props->rep.uncompressed_data_size;
}
uint64_t rocksdb_table_properties_index_size(
const rocksdb_table_properties_t* props) {
return props->rep.index_size;
}
uint64_t rocksdb_table_properties_index_partitions(
const rocksdb_table_properties_t* props) {
return props->rep.index_partitions;
}
uint64_t rocksdb_table_properties_top_level_index_size(
const rocksdb_table_properties_t* props) {
return props->rep.top_level_index_size;
}
uint64_t rocksdb_table_properties_index_key_is_user_key(
const rocksdb_table_properties_t* props) {
return props->rep.index_key_is_user_key;
}
uint64_t rocksdb_table_properties_index_value_is_delta_encoded(
const rocksdb_table_properties_t* props) {
return props->rep.index_value_is_delta_encoded;
}
uint64_t rocksdb_table_properties_udi_is_primary_index(
const rocksdb_table_properties_t* props) {
return props->rep.udi_is_primary_index;
}
uint64_t rocksdb_table_properties_standard_index_is_stub(
const rocksdb_table_properties_t* props) {
return props->rep.standard_index_is_stub;
}
uint64_t rocksdb_table_properties_filter_size(
const rocksdb_table_properties_t* props) {
return props->rep.filter_size;
}
uint64_t rocksdb_table_properties_raw_key_size(
const rocksdb_table_properties_t* props) {
return props->rep.raw_key_size;
}
uint64_t rocksdb_table_properties_raw_value_size(
const rocksdb_table_properties_t* props) {
return props->rep.raw_value_size;
}
uint64_t rocksdb_table_properties_num_data_blocks(
const rocksdb_table_properties_t* props) {
return props->rep.num_data_blocks;
}
uint64_t rocksdb_table_properties_num_data_blocks_compression_rejected(
const rocksdb_table_properties_t* props) {
return props->rep.num_data_blocks_compression_rejected;
}
uint64_t rocksdb_table_properties_num_data_blocks_compression_bypassed(
const rocksdb_table_properties_t* props) {
return props->rep.num_data_blocks_compression_bypassed;
}
uint64_t rocksdb_table_properties_num_uniform_blocks(
const rocksdb_table_properties_t* props) {
return props->rep.num_uniform_blocks;
}
uint64_t rocksdb_table_properties_num_entries(
const rocksdb_table_properties_t* props) {
return props->rep.num_entries;
}
uint64_t rocksdb_table_properties_num_filter_entries(
const rocksdb_table_properties_t* props) {
return props->rep.num_filter_entries;
}
uint64_t rocksdb_table_properties_num_deletions(
const rocksdb_table_properties_t* props) {
return props->rep.num_deletions;
}
uint64_t rocksdb_table_properties_num_merge_operands(
const rocksdb_table_properties_t* props) {
return props->rep.num_merge_operands;
}
uint64_t rocksdb_table_properties_num_range_deletions(
const rocksdb_table_properties_t* props) {
return props->rep.num_range_deletions;
}
uint64_t rocksdb_table_properties_format_version(
const rocksdb_table_properties_t* props) {
return props->rep.format_version;
}
uint64_t rocksdb_table_properties_fixed_key_len(
const rocksdb_table_properties_t* props) {
return props->rep.fixed_key_len;
}
uint64_t rocksdb_table_properties_column_family_id(
const rocksdb_table_properties_t* props) {
return props->rep.column_family_id;
}
uint64_t rocksdb_table_properties_oldest_ancestor_time(
const rocksdb_table_properties_t* props) {
return props->rep.oldest_ancestor_time;
}
uint64_t rocksdb_table_properties_creation_time(
const rocksdb_table_properties_t* props) {
return props->rep.creation_time;
}
uint64_t rocksdb_table_properties_oldest_key_time(
const rocksdb_table_properties_t* props) {
return props->rep.oldest_key_time;
}
uint64_t rocksdb_table_properties_newest_key_time(
const rocksdb_table_properties_t* props) {
return props->rep.newest_key_time;
}
uint64_t rocksdb_table_properties_file_creation_time(
const rocksdb_table_properties_t* props) {
return props->rep.file_creation_time;
}
uint64_t rocksdb_table_properties_slow_compression_estimated_data_size(
const rocksdb_table_properties_t* props) {
return props->rep.slow_compression_estimated_data_size;
}
uint64_t rocksdb_table_properties_fast_compression_estimated_data_size(
const rocksdb_table_properties_t* props) {
return props->rep.fast_compression_estimated_data_size;
}
uint64_t rocksdb_table_properties_tail_start_offset(
const rocksdb_table_properties_t* props) {
return props->rep.tail_start_offset;
}
uint64_t rocksdb_table_properties_user_defined_timestamps_persisted(
const rocksdb_table_properties_t* props) {
return props->rep.user_defined_timestamps_persisted;
}
uint64_t rocksdb_table_properties_key_largest_seqno(
const rocksdb_table_properties_t* props) {
return props->rep.key_largest_seqno;
}
uint64_t rocksdb_table_properties_key_smallest_seqno(
const rocksdb_table_properties_t* props) {
return props->rep.key_smallest_seqno;
}
uint64_t rocksdb_table_properties_data_block_restart_interval(
const rocksdb_table_properties_t* props) {
return props->rep.data_block_restart_interval;
}
uint64_t rocksdb_table_properties_index_block_restart_interval(
const rocksdb_table_properties_t* props) {
return props->rep.index_block_restart_interval;
}
uint64_t rocksdb_table_properties_separate_key_value_in_data_block(
const rocksdb_table_properties_t* props) {
return props->rep.separate_key_value_in_data_block;
}
uint64_t rocksdb_table_properties_lsm_info_at_creation(
const rocksdb_table_properties_t* props) {
return props->rep.lsm_info_at_creation;
}
const char* rocksdb_table_properties_db_id(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.db_id.size();
return props->rep.db_id.data();
}
const char* rocksdb_table_properties_db_session_id(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.db_session_id.size();
return props->rep.db_session_id.data();
}
const char* rocksdb_table_properties_db_host_id(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.db_host_id.size();
return props->rep.db_host_id.data();
}
const char* rocksdb_table_properties_column_family_name(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.column_family_name.size();
return props->rep.column_family_name.data();
}
const char* rocksdb_table_properties_filter_policy_name(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.filter_policy_name.size();
return props->rep.filter_policy_name.data();
}
const char* rocksdb_table_properties_comparator_name(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.comparator_name.size();
return props->rep.comparator_name.data();
}
const char* rocksdb_table_properties_merge_operator_name(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.merge_operator_name.size();
return props->rep.merge_operator_name.data();
}
const char* rocksdb_table_properties_prefix_extractor_name(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.prefix_extractor_name.size();
return props->rep.prefix_extractor_name.data();
}
const char* rocksdb_table_properties_property_collectors_names(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.property_collectors_names.size();
return props->rep.property_collectors_names.data();
}
const char* rocksdb_table_properties_compression_name(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.compression_name.size();
return props->rep.compression_name.data();
}
const char* rocksdb_table_properties_compression_options(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.compression_options.size();
return props->rep.compression_options.data();
}
const char* rocksdb_table_properties_seqno_to_time_mapping(
const rocksdb_table_properties_t* props, size_t* size) {
*size = props->rep.seqno_to_time_mapping.size();
return props->rep.seqno_to_time_mapping.data();
}
/* CompactionJobStats */
uint64_t rocksdb_compaction_job_stats_elapsed_micros(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.elapsed_micros;
}
uint64_t rocksdb_compaction_job_stats_cpu_micros(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.cpu_micros;
}
unsigned char rocksdb_compaction_job_stats_has_accurate_num_input_records(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.has_accurate_num_input_records;
}
uint64_t rocksdb_compaction_job_stats_num_input_records(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_input_records;
}
uint64_t rocksdb_compaction_job_stats_num_blobs_read(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_blobs_read;
}
size_t rocksdb_compaction_job_stats_num_input_files(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_input_files;
}
size_t rocksdb_compaction_job_stats_num_input_files_trivially_moved(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_input_files_trivially_moved;
}
size_t rocksdb_compaction_job_stats_num_input_files_at_output_level(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_input_files_at_output_level;
}
size_t rocksdb_compaction_job_stats_num_filtered_input_files(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_filtered_input_files;
}
size_t rocksdb_compaction_job_stats_num_filtered_input_files_at_output_level(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_filtered_input_files_at_output_level;
}
uint64_t rocksdb_compaction_job_stats_num_output_records(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_output_records;
}
size_t rocksdb_compaction_job_stats_num_output_files(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_output_files;
}
size_t rocksdb_compaction_job_stats_num_output_files_blob(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_output_files_blob;
}
unsigned char rocksdb_compaction_job_stats_is_full_compaction(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.is_full_compaction;
}
unsigned char rocksdb_compaction_job_stats_is_manual_compaction(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.is_manual_compaction;
}
unsigned char rocksdb_compaction_job_stats_is_remote_compaction(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.is_remote_compaction;
}
uint64_t rocksdb_compaction_job_stats_total_input_bytes(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.total_input_bytes;
}
uint64_t rocksdb_compaction_job_stats_total_blob_bytes_read(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.total_blob_bytes_read;
}
uint64_t rocksdb_compaction_job_stats_total_output_bytes(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.total_output_bytes;
}
uint64_t rocksdb_compaction_job_stats_total_output_bytes_blob(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.total_output_bytes_blob;
}
uint64_t rocksdb_compaction_job_stats_total_skipped_input_bytes(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.total_skipped_input_bytes;
}
uint64_t rocksdb_compaction_job_stats_num_records_replaced(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_records_replaced;
}
uint64_t rocksdb_compaction_job_stats_total_input_raw_key_bytes(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.total_input_raw_key_bytes;
}
uint64_t rocksdb_compaction_job_stats_total_input_raw_value_bytes(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.total_input_raw_value_bytes;
}
uint64_t rocksdb_compaction_job_stats_num_input_deletion_records(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_input_deletion_records;
}
uint64_t rocksdb_compaction_job_stats_num_expired_deletion_records(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_expired_deletion_records;
}
uint64_t rocksdb_compaction_job_stats_num_corrupt_keys(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_corrupt_keys;
}
uint64_t rocksdb_compaction_job_stats_file_write_nanos(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.file_write_nanos;
}
uint64_t rocksdb_compaction_job_stats_file_range_sync_nanos(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.file_range_sync_nanos;
}
uint64_t rocksdb_compaction_job_stats_file_fsync_nanos(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.file_fsync_nanos;
}
uint64_t rocksdb_compaction_job_stats_file_prepare_write_nanos(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.file_prepare_write_nanos;
}
const char* rocksdb_compaction_job_stats_smallest_output_key_prefix(
const rocksdb_compaction_job_stats_t* stats, size_t* size) {
*size = stats->rep.smallest_output_key_prefix.size();
return stats->rep.smallest_output_key_prefix.data();
}
const char* rocksdb_compaction_job_stats_largest_output_key_prefix(
const rocksdb_compaction_job_stats_t* stats, size_t* size) {
*size = stats->rep.largest_output_key_prefix.size();
return stats->rep.largest_output_key_prefix.data();
}
uint64_t rocksdb_compaction_job_stats_num_single_del_fallthru(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_single_del_fallthru;
}
uint64_t rocksdb_compaction_job_stats_num_single_del_mismatch(
const rocksdb_compaction_job_stats_t* stats) {
return stats->rep.num_single_del_mismatch;
}
/* CompactionFileInfo */
int rocksdb_compaction_file_info_level(
const rocksdb_compaction_file_info_t* info) {
return info->rep.level;
}
uint64_t rocksdb_compaction_file_info_file_number(
const rocksdb_compaction_file_info_t* info) {
return info->rep.file_number;
}
uint64_t rocksdb_compaction_file_info_oldest_blob_file_number(
const rocksdb_compaction_file_info_t* info) {
return info->rep.oldest_blob_file_number;
}
/* BlobFileAdditionInfo */
const char* rocksdb_blob_file_addition_info_blob_file_path(
const rocksdb_blob_file_addition_info_t* info, size_t* size) {
*size = info->rep.blob_file_path.size();
return info->rep.blob_file_path.data();
}
uint64_t rocksdb_blob_file_addition_info_blob_file_number(
const rocksdb_blob_file_addition_info_t* info) {
return info->rep.blob_file_number;
}
uint64_t rocksdb_blob_file_addition_info_total_blob_count(
const rocksdb_blob_file_addition_info_t* info) {
return info->rep.total_blob_count;
}
uint64_t rocksdb_blob_file_addition_info_total_blob_bytes(
const rocksdb_blob_file_addition_info_t* info) {
return info->rep.total_blob_bytes;
}
/* BlobFileGarbageInfo */
const char* rocksdb_blob_file_garbage_info_blob_file_path(
const rocksdb_blob_file_garbage_info_t* info, size_t* size) {
*size = info->rep.blob_file_path.size();
return info->rep.blob_file_path.data();
}
uint64_t rocksdb_blob_file_garbage_info_blob_file_number(
const rocksdb_blob_file_garbage_info_t* info) {
return info->rep.blob_file_number;
}
uint64_t rocksdb_blob_file_garbage_info_garbage_blob_count(
const rocksdb_blob_file_garbage_info_t* info) {
return info->rep.garbage_blob_count;
}
uint64_t rocksdb_blob_file_garbage_info_garbage_blob_bytes(
const rocksdb_blob_file_garbage_info_t* info) {
return info->rep.garbage_blob_bytes;
}
@@ -0,0 +1,389 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Sources:
// - include/rocksdb/compaction_job_stats.h
// - include/rocksdb/listener.h
// - include/rocksdb/table_properties.h
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
// - include/rocksdb/c.h
// - tools/c_api_gen/c_base.cc
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/auto_simple_bindings.py
// Output group: Auto-discovered metadata view structs simple
/* TableProperties */
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_orig_file_number(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_data_size(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_uncompressed_data_size(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_index_size(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_index_partitions(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_top_level_index_size(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_index_key_is_user_key(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_index_value_is_delta_encoded(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_udi_is_primary_index(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_standard_index_is_stub(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_filter_size(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_raw_key_size(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_raw_value_size(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_num_data_blocks(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_num_data_blocks_compression_rejected(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_num_data_blocks_compression_bypassed(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_num_uniform_blocks(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_num_entries(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_num_filter_entries(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_num_deletions(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_num_merge_operands(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_num_range_deletions(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_format_version(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_fixed_key_len(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_column_family_id(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_oldest_ancestor_time(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_creation_time(const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_oldest_key_time(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_newest_key_time(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_file_creation_time(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_slow_compression_estimated_data_size(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_fast_compression_estimated_data_size(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_tail_start_offset(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_user_defined_timestamps_persisted(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_key_largest_seqno(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_key_smallest_seqno(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_data_block_restart_interval(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_index_block_restart_interval(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_separate_key_value_in_data_block(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_table_properties_lsm_info_at_creation(
const rocksdb_table_properties_t* props);
extern ROCKSDB_LIBRARY_API const char* rocksdb_table_properties_db_id(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char* rocksdb_table_properties_db_session_id(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char* rocksdb_table_properties_db_host_id(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_table_properties_column_family_name(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_table_properties_filter_policy_name(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char* rocksdb_table_properties_comparator_name(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_table_properties_merge_operator_name(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_table_properties_prefix_extractor_name(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_table_properties_property_collectors_names(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_table_properties_compression_name(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_table_properties_compression_options(
const rocksdb_table_properties_t* props, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_table_properties_seqno_to_time_mapping(
const rocksdb_table_properties_t* props, size_t* size);
/* CompactionJobStats */
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compaction_job_stats_elapsed_micros(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compaction_job_stats_cpu_micros(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_compaction_job_stats_has_accurate_num_input_records(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_num_input_records(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compaction_job_stats_num_blobs_read(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API size_t rocksdb_compaction_job_stats_num_input_files(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API size_t
rocksdb_compaction_job_stats_num_input_files_trivially_moved(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API size_t
rocksdb_compaction_job_stats_num_input_files_at_output_level(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API size_t
rocksdb_compaction_job_stats_num_filtered_input_files(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API size_t
rocksdb_compaction_job_stats_num_filtered_input_files_at_output_level(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_num_output_records(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API size_t rocksdb_compaction_job_stats_num_output_files(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API size_t
rocksdb_compaction_job_stats_num_output_files_blob(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_compaction_job_stats_is_full_compaction(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_compaction_job_stats_is_manual_compaction(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_compaction_job_stats_is_remote_compaction(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_total_input_bytes(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_total_blob_bytes_read(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_total_output_bytes(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_total_output_bytes_blob(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_total_skipped_input_bytes(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_num_records_replaced(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_total_input_raw_key_bytes(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_total_input_raw_value_bytes(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_num_input_deletion_records(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_num_expired_deletion_records(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_num_corrupt_keys(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_file_write_nanos(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_file_range_sync_nanos(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_file_fsync_nanos(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_file_prepare_write_nanos(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_compaction_job_stats_smallest_output_key_prefix(
const rocksdb_compaction_job_stats_t* stats, size_t* size);
extern ROCKSDB_LIBRARY_API const char*
rocksdb_compaction_job_stats_largest_output_key_prefix(
const rocksdb_compaction_job_stats_t* stats, size_t* size);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_num_single_del_fallthru(
const rocksdb_compaction_job_stats_t* stats);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_job_stats_num_single_del_mismatch(
const rocksdb_compaction_job_stats_t* stats);
/* CompactionFileInfo */
extern ROCKSDB_LIBRARY_API int rocksdb_compaction_file_info_level(
const rocksdb_compaction_file_info_t* info);
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_compaction_file_info_file_number(
const rocksdb_compaction_file_info_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_compaction_file_info_oldest_blob_file_number(
const rocksdb_compaction_file_info_t* info);
/* BlobFileAdditionInfo */
extern ROCKSDB_LIBRARY_API const char*
rocksdb_blob_file_addition_info_blob_file_path(
const rocksdb_blob_file_addition_info_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_blob_file_addition_info_blob_file_number(
const rocksdb_blob_file_addition_info_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_blob_file_addition_info_total_blob_count(
const rocksdb_blob_file_addition_info_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_blob_file_addition_info_total_blob_bytes(
const rocksdb_blob_file_addition_info_t* info);
/* BlobFileGarbageInfo */
extern ROCKSDB_LIBRARY_API const char*
rocksdb_blob_file_garbage_info_blob_file_path(
const rocksdb_blob_file_garbage_info_t* info, size_t* size);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_blob_file_garbage_info_blob_file_number(
const rocksdb_blob_file_garbage_info_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_blob_file_garbage_info_garbage_blob_count(
const rocksdb_blob_file_garbage_info_t* info);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_blob_file_garbage_info_garbage_blob_bytes(
const rocksdb_blob_file_garbage_info_t* info);
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,88 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'Options simple'
// --header-out
// c_api_gen/c_generated_options_subset.h.inc
// --source-out
// c_api_gen/c_generated_options_subset.cc.inc
/* Options simple */
void rocksdb_options_set_compression_options_zstd_max_train_bytes(
rocksdb_options_t* opt, int zstd_max_train_bytes) {
opt->rep.compression_opts.zstd_max_train_bytes = zstd_max_train_bytes;
}
int rocksdb_options_get_compression_options_zstd_max_train_bytes(
rocksdb_options_t* opt) {
return opt->rep.compression_opts.zstd_max_train_bytes;
}
void rocksdb_options_set_compression_options_use_zstd_dict_trainer(
rocksdb_options_t* opt, unsigned char use_zstd_dict_trainer) {
opt->rep.compression_opts.use_zstd_dict_trainer = use_zstd_dict_trainer;
}
unsigned char rocksdb_options_get_compression_options_use_zstd_dict_trainer(
rocksdb_options_t* opt) {
return opt->rep.compression_opts.use_zstd_dict_trainer;
}
void rocksdb_options_set_compression_options_parallel_threads(
rocksdb_options_t* opt, int value) {
opt->rep.compression_opts.parallel_threads = value;
}
int rocksdb_options_get_compression_options_parallel_threads(
rocksdb_options_t* opt) {
return opt->rep.compression_opts.parallel_threads;
}
void rocksdb_options_set_compression_options_max_dict_buffer_bytes(
rocksdb_options_t* opt, uint64_t max_dict_buffer_bytes) {
opt->rep.compression_opts.max_dict_buffer_bytes = max_dict_buffer_bytes;
}
uint64_t rocksdb_options_get_compression_options_max_dict_buffer_bytes(
rocksdb_options_t* opt) {
return opt->rep.compression_opts.max_dict_buffer_bytes;
}
unsigned char
rocksdb_options_get_bottommost_compression_options_use_zstd_dict_trainer(
rocksdb_options_t* opt) {
return opt->rep.bottommost_compression_opts.use_zstd_dict_trainer;
}
void rocksdb_options_set_use_fsync(rocksdb_options_t* opt, int use_fsync) {
opt->rep.use_fsync = use_fsync;
}
int rocksdb_options_get_use_fsync(rocksdb_options_t* opt) {
return opt->rep.use_fsync;
}
void rocksdb_options_set_disable_auto_compactions(rocksdb_options_t* opt,
int disable) {
opt->rep.disable_auto_compactions = disable;
}
void rocksdb_options_set_optimize_filters_for_hits(rocksdb_options_t* opt,
int v) {
opt->rep.optimize_filters_for_hits = v;
}
void rocksdb_options_set_report_bg_io_stats(rocksdb_options_t* opt, int v) {
opt->rep.report_bg_io_stats = v;
}
@@ -0,0 +1,71 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'Options simple'
// --header-out
// c_api_gen/c_generated_options_subset.h.inc
// --source-out
// c_api_gen/c_generated_options_subset.cc.inc
/* Options simple */
extern ROCKSDB_LIBRARY_API void
rocksdb_options_set_compression_options_zstd_max_train_bytes(
rocksdb_options_t* opt, int zstd_max_train_bytes);
extern ROCKSDB_LIBRARY_API int
rocksdb_options_get_compression_options_zstd_max_train_bytes(
rocksdb_options_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_options_set_compression_options_use_zstd_dict_trainer(
rocksdb_options_t* opt, unsigned char use_zstd_dict_trainer);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_options_get_compression_options_use_zstd_dict_trainer(
rocksdb_options_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_options_set_compression_options_parallel_threads(rocksdb_options_t* opt,
int value);
extern ROCKSDB_LIBRARY_API int
rocksdb_options_get_compression_options_parallel_threads(
rocksdb_options_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_options_set_compression_options_max_dict_buffer_bytes(
rocksdb_options_t* opt, uint64_t max_dict_buffer_bytes);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_options_get_compression_options_max_dict_buffer_bytes(
rocksdb_options_t* opt);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_options_get_bottommost_compression_options_use_zstd_dict_trainer(
rocksdb_options_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_use_fsync(
rocksdb_options_t* opt, int use_fsync);
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_use_fsync(
rocksdb_options_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_disable_auto_compactions(
rocksdb_options_t* opt, int disable);
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_optimize_filters_for_hits(
rocksdb_options_t* opt, int v);
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_report_bg_io_stats(
rocksdb_options_t* opt, int v);
@@ -0,0 +1,240 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Sources:
// - include/rocksdb/options.h
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
// - include/rocksdb/c.h
// - tools/c_api_gen/c_base.cc
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/auto_simple_bindings.py
// Output group: Auto-discovered ReadOptions simple
/* ReadOptions */
void rocksdb_readoptions_set_deadline(rocksdb_readoptions_t* opt,
uint64_t microseconds) {
opt->rep.deadline = std::chrono::microseconds(microseconds);
}
uint64_t rocksdb_readoptions_get_deadline(rocksdb_readoptions_t* opt) {
return opt->rep.deadline.count();
}
void rocksdb_readoptions_set_io_timeout(rocksdb_readoptions_t* opt,
uint64_t microseconds) {
opt->rep.io_timeout = std::chrono::microseconds(microseconds);
}
uint64_t rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt) {
return opt->rep.io_timeout.count();
}
void rocksdb_readoptions_set_read_tier(rocksdb_readoptions_t* opt, int v) {
opt->rep.read_tier = static_cast<decltype(opt->rep.read_tier)>(v);
}
int rocksdb_readoptions_get_read_tier(rocksdb_readoptions_t* opt) {
return static_cast<int>(opt->rep.read_tier);
}
void rocksdb_readoptions_set_rate_limiter_priority(rocksdb_readoptions_t* opt,
int v) {
opt->rep.rate_limiter_priority =
static_cast<decltype(opt->rep.rate_limiter_priority)>(v);
}
int rocksdb_readoptions_get_rate_limiter_priority(rocksdb_readoptions_t* opt) {
return static_cast<int>(opt->rep.rate_limiter_priority);
}
void rocksdb_readoptions_set_value_size_soft_limit(rocksdb_readoptions_t* opt,
uint64_t v) {
opt->rep.value_size_soft_limit = v;
}
uint64_t rocksdb_readoptions_get_value_size_soft_limit(
rocksdb_readoptions_t* opt) {
return opt->rep.value_size_soft_limit;
}
void rocksdb_readoptions_set_verify_checksums(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.verify_checksums = v;
}
unsigned char rocksdb_readoptions_get_verify_checksums(
rocksdb_readoptions_t* opt) {
return opt->rep.verify_checksums;
}
void rocksdb_readoptions_set_fill_cache(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.fill_cache = v;
}
unsigned char rocksdb_readoptions_get_fill_cache(rocksdb_readoptions_t* opt) {
return opt->rep.fill_cache;
}
void rocksdb_readoptions_set_ignore_range_deletions(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.ignore_range_deletions = v;
}
unsigned char rocksdb_readoptions_get_ignore_range_deletions(
rocksdb_readoptions_t* opt) {
return opt->rep.ignore_range_deletions;
}
void rocksdb_readoptions_set_async_io(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.async_io = v;
}
unsigned char rocksdb_readoptions_get_async_io(rocksdb_readoptions_t* opt) {
return opt->rep.async_io;
}
void rocksdb_readoptions_set_optimize_multiget_for_io(
rocksdb_readoptions_t* opt, unsigned char v) {
opt->rep.optimize_multiget_for_io = v;
}
unsigned char rocksdb_readoptions_get_optimize_multiget_for_io(
rocksdb_readoptions_t* opt) {
return opt->rep.optimize_multiget_for_io;
}
void rocksdb_readoptions_set_readahead_size(rocksdb_readoptions_t* opt,
size_t v) {
opt->rep.readahead_size = v;
}
size_t rocksdb_readoptions_get_readahead_size(rocksdb_readoptions_t* opt) {
return opt->rep.readahead_size;
}
void rocksdb_readoptions_set_max_skippable_internal_keys(
rocksdb_readoptions_t* opt, uint64_t v) {
opt->rep.max_skippable_internal_keys = v;
}
uint64_t rocksdb_readoptions_get_max_skippable_internal_keys(
rocksdb_readoptions_t* opt) {
return opt->rep.max_skippable_internal_keys;
}
void rocksdb_readoptions_set_tailing(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.tailing = v;
}
unsigned char rocksdb_readoptions_get_tailing(rocksdb_readoptions_t* opt) {
return opt->rep.tailing;
}
void rocksdb_readoptions_set_total_order_seek(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.total_order_seek = v;
}
unsigned char rocksdb_readoptions_get_total_order_seek(
rocksdb_readoptions_t* opt) {
return opt->rep.total_order_seek;
}
void rocksdb_readoptions_set_auto_prefix_mode(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.auto_prefix_mode = v;
}
unsigned char rocksdb_readoptions_get_auto_prefix_mode(
rocksdb_readoptions_t* opt) {
return opt->rep.auto_prefix_mode;
}
void rocksdb_readoptions_set_prefix_same_as_start(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.prefix_same_as_start = v;
}
unsigned char rocksdb_readoptions_get_prefix_same_as_start(
rocksdb_readoptions_t* opt) {
return opt->rep.prefix_same_as_start;
}
void rocksdb_readoptions_set_pin_data(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.pin_data = v;
}
unsigned char rocksdb_readoptions_get_pin_data(rocksdb_readoptions_t* opt) {
return opt->rep.pin_data;
}
void rocksdb_readoptions_set_adaptive_readahead(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.adaptive_readahead = v;
}
unsigned char rocksdb_readoptions_get_adaptive_readahead(
rocksdb_readoptions_t* opt) {
return opt->rep.adaptive_readahead;
}
void rocksdb_readoptions_set_background_purge_on_iterator_cleanup(
rocksdb_readoptions_t* opt, unsigned char v) {
opt->rep.background_purge_on_iterator_cleanup = v;
}
unsigned char rocksdb_readoptions_get_background_purge_on_iterator_cleanup(
rocksdb_readoptions_t* opt) {
return opt->rep.background_purge_on_iterator_cleanup;
}
void rocksdb_readoptions_set_auto_readahead_size(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.auto_readahead_size = v;
}
unsigned char rocksdb_readoptions_get_auto_readahead_size(
rocksdb_readoptions_t* opt) {
return opt->rep.auto_readahead_size;
}
void rocksdb_readoptions_set_allow_unprepared_value(rocksdb_readoptions_t* opt,
unsigned char v) {
opt->rep.allow_unprepared_value = v;
}
unsigned char rocksdb_readoptions_get_allow_unprepared_value(
rocksdb_readoptions_t* opt) {
return opt->rep.allow_unprepared_value;
}
void rocksdb_readoptions_set_auto_refresh_iterator_with_snapshot(
rocksdb_readoptions_t* opt, unsigned char v) {
opt->rep.auto_refresh_iterator_with_snapshot = v;
}
unsigned char rocksdb_readoptions_get_auto_refresh_iterator_with_snapshot(
rocksdb_readoptions_t* opt) {
return opt->rep.auto_refresh_iterator_with_snapshot;
}
void rocksdb_readoptions_set_io_activity(rocksdb_readoptions_t* opt, int v) {
opt->rep.io_activity = static_cast<decltype(opt->rep.io_activity)>(v);
}
int rocksdb_readoptions_get_io_activity(rocksdb_readoptions_t* opt) {
return static_cast<int>(opt->rep.io_activity);
}
@@ -0,0 +1,165 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/auto_simple_bindings.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Sources:
// - include/rocksdb/options.h
// - tools/c_api_gen/auto_simple_bindings_blocklist.json
// - include/rocksdb/c.h
// - tools/c_api_gen/c_base.cc
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/auto_simple_bindings.py
// Output group: Auto-discovered ReadOptions simple
/* ReadOptions */
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_deadline(
rocksdb_readoptions_t* opt, uint64_t microseconds);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_readoptions_get_deadline(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_io_timeout(
rocksdb_readoptions_t* opt, uint64_t microseconds);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_read_tier(
rocksdb_readoptions_t* opt, int v);
extern ROCKSDB_LIBRARY_API int rocksdb_readoptions_get_read_tier(
rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_rate_limiter_priority(
rocksdb_readoptions_t* opt, int v);
extern ROCKSDB_LIBRARY_API int rocksdb_readoptions_get_rate_limiter_priority(
rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_value_size_soft_limit(
rocksdb_readoptions_t* opt, uint64_t v);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_readoptions_get_value_size_soft_limit(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_verify_checksums(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_verify_checksums(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_fill_cache(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_readoptions_get_fill_cache(
rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_ignore_range_deletions(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_ignore_range_deletions(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_async_io(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_readoptions_get_async_io(
rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_readoptions_set_optimize_multiget_for_io(rocksdb_readoptions_t* opt,
unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_optimize_multiget_for_io(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_readahead_size(
rocksdb_readoptions_t* opt, size_t v);
extern ROCKSDB_LIBRARY_API size_t
rocksdb_readoptions_get_readahead_size(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_readoptions_set_max_skippable_internal_keys(rocksdb_readoptions_t* opt,
uint64_t v);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_readoptions_get_max_skippable_internal_keys(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_tailing(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_readoptions_get_tailing(
rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_total_order_seek(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_total_order_seek(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_auto_prefix_mode(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_auto_prefix_mode(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_prefix_same_as_start(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_prefix_same_as_start(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_pin_data(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_readoptions_get_pin_data(
rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_adaptive_readahead(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_adaptive_readahead(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_readoptions_set_background_purge_on_iterator_cleanup(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_background_purge_on_iterator_cleanup(
rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_auto_readahead_size(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_auto_readahead_size(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_allow_unprepared_value(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_allow_unprepared_value(rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void
rocksdb_readoptions_set_auto_refresh_iterator_with_snapshot(
rocksdb_readoptions_t* opt, unsigned char v);
extern ROCKSDB_LIBRARY_API unsigned char
rocksdb_readoptions_get_auto_refresh_iterator_with_snapshot(
rocksdb_readoptions_t* opt);
extern ROCKSDB_LIBRARY_API void rocksdb_readoptions_set_io_activity(
rocksdb_readoptions_t* opt, int v);
extern ROCKSDB_LIBRARY_API int rocksdb_readoptions_get_io_activity(
rocksdb_readoptions_t* opt);
File diff suppressed because it is too large. Load diff
+375
View File
@@ -0,0 +1,375 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'DB data operations'
// --section 'WriteBatch' --section 'Transaction'
// --section 'TransactionDB simple'
// --header-out
// c_api_gen/c_generated_subset.h.inc
// --source-out
// c_api_gen/c_generated_subset.cc.inc
/* DB data operations */
void rocksdb_put(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, const char* val, size_t vallen,
char** errptr) {
SaveError(errptr,
db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen)));
}
void rocksdb_put_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, const char* val,
size_t vallen, char** errptr) {
SaveError(errptr, db->rep->Put(options->rep, column_family->rep,
Slice(key, keylen), Slice(val, vallen)));
}
void rocksdb_delete(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, char** errptr) {
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen)));
}
void rocksdb_delete_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, char** errptr) {
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
Slice(key, keylen)));
}
void rocksdb_merge(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, const char* val,
size_t vallen, char** errptr) {
SaveError(errptr, db->rep->Merge(options->rep, Slice(key, keylen),
Slice(val, vallen)));
}
void rocksdb_merge_cf(rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, const char* val,
size_t vallen, char** errptr) {
SaveError(errptr, db->rep->Merge(options->rep, column_family->rep,
Slice(key, keylen), Slice(val, vallen)));
}
void rocksdb_write(rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_writebatch_t* batch, char** errptr) {
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
}
void rocksdb_put_with_ts(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, const char* ts,
size_t tslen, const char* val, size_t vallen,
char** errptr) {
SaveError(errptr, db->rep->Put(options->rep, Slice(key, keylen),
Slice(ts, tslen), Slice(val, vallen)));
}
void rocksdb_put_cf_with_ts(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, const char* ts,
size_t tslen, const char* val, size_t vallen,
char** errptr) {
SaveError(errptr,
db->rep->Put(options->rep, column_family->rep, Slice(key, keylen),
Slice(ts, tslen), Slice(val, vallen)));
}
void rocksdb_delete_with_ts(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, const char* ts,
size_t tslen, char** errptr) {
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen),
Slice(ts, tslen)));
}
void rocksdb_delete_cf_with_ts(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, const char* ts,
size_t tslen, char** errptr) {
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
Slice(key, keylen), Slice(ts, tslen)));
}
void rocksdb_singledelete(rocksdb_t* db, const rocksdb_writeoptions_t* options,
const char* key, size_t keylen, char** errptr) {
SaveError(errptr, db->rep->SingleDelete(options->rep, Slice(key, keylen)));
}
void rocksdb_singledelete_cf(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen, char** errptr) {
SaveError(errptr, db->rep->SingleDelete(options->rep, column_family->rep,
Slice(key, keylen)));
}
void rocksdb_singledelete_with_ts(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
const char* key, size_t keylen,
const char* ts, size_t tslen, char** errptr) {
SaveError(errptr, db->rep->SingleDelete(options->rep, Slice(key, keylen),
Slice(ts, tslen)));
}
void rocksdb_singledelete_cf_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr) {
SaveError(errptr,
db->rep->SingleDelete(options->rep, column_family->rep,
Slice(key, keylen), Slice(ts, tslen)));
}
void rocksdb_delete_range_cf(rocksdb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* start_key, size_t start_key_len,
const char* end_key, size_t end_key_len,
char** errptr) {
SaveError(errptr, db->rep->DeleteRange(options->rep, column_family->rep,
Slice(start_key, start_key_len),
Slice(end_key, end_key_len)));
}
void rocksdb_flush(rocksdb_t* db, const rocksdb_flushoptions_t* options,
char** errptr) {
SaveError(errptr, db->rep->Flush(options->rep));
}
void rocksdb_flush_cf(rocksdb_t* db, const rocksdb_flushoptions_t* options,
rocksdb_column_family_handle_t* column_family,
char** errptr) {
SaveError(errptr, db->rep->Flush(options->rep, column_family->rep));
}
void rocksdb_flush_wal(rocksdb_t* db, unsigned char sync, char** errptr) {
SaveError(errptr, db->rep->FlushWAL(sync));
}
void rocksdb_pause_background_work(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->PauseBackgroundWork());
}
void rocksdb_continue_background_work(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->ContinueBackgroundWork());
}
void rocksdb_disable_file_deletions(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->DisableFileDeletions());
}
void rocksdb_enable_file_deletions(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->EnableFileDeletions());
}
void rocksdb_verify_checksum(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->VerifyChecksum());
}
void rocksdb_verify_file_checksums(rocksdb_t* db, char** errptr) {
SaveError(errptr, db->rep->VerifyFileChecksums(ReadOptions()));
}
void rocksdb_destroy_db(const rocksdb_options_t* options, const char* name,
char** errptr) {
SaveError(errptr, DestroyDB(name, options->rep));
}
void rocksdb_repair_db(const rocksdb_options_t* options, const char* name,
char** errptr) {
SaveError(errptr, RepairDB(name, options->rep));
}
/* WriteBatch */
void rocksdb_writebatch_clear(rocksdb_writebatch_t* b) { b->rep.Clear(); }
void rocksdb_writebatch_put(rocksdb_writebatch_t* b, const char* key,
size_t klen, const char* val, size_t vlen) {
b->rep.Put(Slice(key, klen), Slice(val, vlen));
}
void rocksdb_writebatch_put_cf(rocksdb_writebatch_t* b,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val,
size_t vlen) {
b->rep.Put(column_family->rep, Slice(key, klen), Slice(val, vlen));
}
void rocksdb_writebatch_delete(rocksdb_writebatch_t* b, const char* key,
size_t klen) {
b->rep.Delete(Slice(key, klen));
}
void rocksdb_writebatch_put_log_data(rocksdb_writebatch_t* b, const char* blob,
size_t len) {
b->rep.PutLogData(Slice(blob, len));
}
void rocksdb_writebatch_set_save_point(rocksdb_writebatch_t* b) {
b->rep.SetSavePoint();
}
void rocksdb_writebatch_rollback_to_save_point(rocksdb_writebatch_t* b,
char** errptr) {
SaveError(errptr, b->rep.RollbackToSavePoint());
}
void rocksdb_writebatch_pop_save_point(rocksdb_writebatch_t* b, char** errptr) {
SaveError(errptr, b->rep.PopSavePoint());
}
void rocksdb_writebatch_verify_checksum(rocksdb_writebatch_t* b,
char** errptr) {
SaveError(errptr, b->rep.VerifyChecksum());
}
/* Transaction */
void rocksdb_transaction_put(rocksdb_transaction_t* txn, const char* key,
size_t klen, const char* val, size_t vlen,
char** errptr) {
SaveError(errptr, txn->rep->Put(Slice(key, klen), Slice(val, vlen)));
}
void rocksdb_transaction_put_cf(rocksdb_transaction_t* txn,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val,
size_t vlen, char** errptr) {
SaveError(errptr, txn->rep->Put(column_family->rep, Slice(key, klen),
Slice(val, vlen)));
}
void rocksdb_transaction_merge(rocksdb_transaction_t* txn, const char* key,
size_t klen, const char* val, size_t vlen,
char** errptr) {
SaveError(errptr, txn->rep->Merge(Slice(key, klen), Slice(val, vlen)));
}
void rocksdb_transaction_merge_cf(rocksdb_transaction_t* txn,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val,
size_t vlen, char** errptr) {
SaveError(errptr, txn->rep->Merge(column_family->rep, Slice(key, klen),
Slice(val, vlen)));
}
void rocksdb_transaction_delete(rocksdb_transaction_t* txn, const char* key,
size_t klen, char** errptr) {
SaveError(errptr, txn->rep->Delete(Slice(key, klen)));
}
void rocksdb_transaction_delete_cf(
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, char** errptr) {
SaveError(errptr, txn->rep->Delete(column_family->rep, Slice(key, klen)));
}
void rocksdb_transaction_commit(rocksdb_transaction_t* txn, char** errptr) {
SaveError(errptr, txn->rep->Commit());
}
void rocksdb_transaction_rollback(rocksdb_transaction_t* txn, char** errptr) {
SaveError(errptr, txn->rep->Rollback());
}
void rocksdb_transaction_put_log_data(rocksdb_transaction_t* txn,
const char* blob, size_t len) {
txn->rep->PutLogData(Slice(blob, len));
}
void rocksdb_transaction_set_savepoint(rocksdb_transaction_t* txn) {
txn->rep->SetSavePoint();
}
void rocksdb_transaction_rollback_to_savepoint(rocksdb_transaction_t* txn,
char** errptr) {
SaveError(errptr, txn->rep->RollbackToSavePoint());
}
/* TransactionDB simple */
void rocksdb_transactiondb_put(rocksdb_transactiondb_t* txn_db,
const rocksdb_writeoptions_t* options,
const char* key, size_t klen, const char* val,
size_t vlen, char** errptr) {
SaveError(errptr,
txn_db->rep->Put(options->rep, Slice(key, klen), Slice(val, vlen)));
}
void rocksdb_transactiondb_put_cf(rocksdb_transactiondb_t* txn_db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen,
const char* val, size_t vallen,
char** errptr) {
SaveError(errptr, txn_db->rep->Put(options->rep, column_family->rep,
Slice(key, keylen), Slice(val, vallen)));
}
void rocksdb_transactiondb_write(rocksdb_transactiondb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_writebatch_t* batch, char** errptr) {
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
}
void rocksdb_transactiondb_merge(rocksdb_transactiondb_t* txn_db,
const rocksdb_writeoptions_t* options,
const char* key, size_t klen, const char* val,
size_t vlen, char** errptr) {
SaveError(errptr, txn_db->rep->Merge(options->rep, Slice(key, klen),
Slice(val, vlen)));
}
void rocksdb_transactiondb_merge_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
const char* val, size_t vlen, char** errptr) {
SaveError(errptr, txn_db->rep->Merge(options->rep, column_family->rep,
Slice(key, klen), Slice(val, vlen)));
}
void rocksdb_transactiondb_delete(rocksdb_transactiondb_t* txn_db,
const rocksdb_writeoptions_t* options,
const char* key, size_t klen, char** errptr) {
SaveError(errptr, txn_db->rep->Delete(options->rep, Slice(key, klen)));
}
void rocksdb_transactiondb_delete_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, char** errptr) {
SaveError(errptr, txn_db->rep->Delete(options->rep, column_family->rep,
Slice(key, keylen)));
}
void rocksdb_transactiondb_flush_wal(rocksdb_transactiondb_t* txn_db,
unsigned char sync, char** errptr) {
SaveError(errptr, txn_db->rep->FlushWAL(sync));
}
void rocksdb_transactiondb_flush(rocksdb_transactiondb_t* txn_db,
const rocksdb_flushoptions_t* options,
char** errptr) {
SaveError(errptr, txn_db->rep->Flush(options->rep));
}
void rocksdb_transactiondb_flush_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
rocksdb_column_family_handle_t* column_family, char** errptr) {
SaveError(errptr, txn_db->rep->Flush(options->rep, column_family->rep));
}
+249
View File
@@ -0,0 +1,249 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'DB data operations'
// --section 'WriteBatch' --section 'Transaction'
// --section 'TransactionDB simple'
// --header-out
// c_api_gen/c_generated_subset.h.inc
// --source-out
// c_api_gen/c_generated_subset.cc.inc
/* DB data operations */
extern ROCKSDB_LIBRARY_API void rocksdb_put(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_put_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_merge(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_merge_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_write(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_writebatch_t* batch, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_put_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* ts, size_t tslen, const char* val, size_t vallen,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_put_cf_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* ts, size_t tslen, const char* val, size_t vallen,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete_cf_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_singledelete_cf_with_ts(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* ts, size_t tslen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_delete_range_cf(
rocksdb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* start_key,
size_t start_key_len, const char* end_key, size_t end_key_len,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_flush(
rocksdb_t* db, const rocksdb_flushoptions_t* options, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_flush_cf(
rocksdb_t* db, const rocksdb_flushoptions_t* options,
rocksdb_column_family_handle_t* column_family, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_flush_wal(rocksdb_t* db,
unsigned char sync,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_pause_background_work(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_continue_background_work(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_disable_file_deletions(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_enable_file_deletions(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_verify_checksum(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_verify_file_checksums(rocksdb_t* db,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_destroy_db(
const rocksdb_options_t* options, const char* name, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_repair_db(
const rocksdb_options_t* options, const char* name, char** errptr);
/* WriteBatch */
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_clear(
rocksdb_writebatch_t* b);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put(rocksdb_writebatch_t* b,
const char* key,
size_t klen,
const char* val,
size_t vlen);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put_cf(
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val, size_t vlen);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_delete(
rocksdb_writebatch_t* b, const char* key, size_t klen);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put_log_data(
rocksdb_writebatch_t* b, const char* blob, size_t len);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_set_save_point(
rocksdb_writebatch_t* b);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_rollback_to_save_point(
rocksdb_writebatch_t* b, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_pop_save_point(
rocksdb_writebatch_t* b, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_verify_checksum(
rocksdb_writebatch_t* b, char** errptr);
/* Transaction */
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put(
rocksdb_transaction_t* txn, const char* key, size_t klen, const char* val,
size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put_cf(
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_merge(
rocksdb_transaction_t* txn, const char* key, size_t klen, const char* val,
size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_merge_cf(
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_delete(
rocksdb_transaction_t* txn, const char* key, size_t klen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_delete_cf(
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_commit(
rocksdb_transaction_t* txn, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_rollback(
rocksdb_transaction_t* txn, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put_log_data(
rocksdb_transaction_t* txn, const char* blob, size_t len);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_set_savepoint(
rocksdb_transaction_t* txn);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_rollback_to_savepoint(
rocksdb_transaction_t* txn, char** errptr);
/* TransactionDB simple */
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_put(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_put_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_write(
rocksdb_transactiondb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_writebatch_t* batch, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_merge(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_merge_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_delete(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
const char* key, size_t klen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_delete_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush_wal(
rocksdb_transactiondb_t* txn_db, unsigned char sync, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush(
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
rocksdb_column_family_handle_t* column_family, char** errptr);
@@ -0,0 +1,81 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'Transaction'
// --header-out
// c_api_gen/c_generated_transaction_subset.h.inc
// --source-out
// c_api_gen/c_generated_transaction_subset.cc.inc
/* Transaction */
void rocksdb_transaction_put(rocksdb_transaction_t* txn, const char* key,
size_t klen, const char* val, size_t vlen,
char** errptr) {
SaveError(errptr, txn->rep->Put(Slice(key, klen), Slice(val, vlen)));
}
void rocksdb_transaction_put_cf(rocksdb_transaction_t* txn,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val,
size_t vlen, char** errptr) {
SaveError(errptr, txn->rep->Put(column_family->rep, Slice(key, klen),
Slice(val, vlen)));
}
void rocksdb_transaction_merge(rocksdb_transaction_t* txn, const char* key,
size_t klen, const char* val, size_t vlen,
char** errptr) {
SaveError(errptr, txn->rep->Merge(Slice(key, klen), Slice(val, vlen)));
}
void rocksdb_transaction_merge_cf(rocksdb_transaction_t* txn,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val,
size_t vlen, char** errptr) {
SaveError(errptr, txn->rep->Merge(column_family->rep, Slice(key, klen),
Slice(val, vlen)));
}
void rocksdb_transaction_delete(rocksdb_transaction_t* txn, const char* key,
size_t klen, char** errptr) {
SaveError(errptr, txn->rep->Delete(Slice(key, klen)));
}
void rocksdb_transaction_delete_cf(
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, char** errptr) {
SaveError(errptr, txn->rep->Delete(column_family->rep, Slice(key, klen)));
}
void rocksdb_transaction_commit(rocksdb_transaction_t* txn, char** errptr) {
SaveError(errptr, txn->rep->Commit());
}
void rocksdb_transaction_rollback(rocksdb_transaction_t* txn, char** errptr) {
SaveError(errptr, txn->rep->Rollback());
}
void rocksdb_transaction_put_log_data(rocksdb_transaction_t* txn,
const char* blob, size_t len) {
txn->rep->PutLogData(Slice(blob, len));
}
void rocksdb_transaction_set_savepoint(rocksdb_transaction_t* txn) {
txn->rep->SetSavePoint();
}
void rocksdb_transaction_rollback_to_savepoint(rocksdb_transaction_t* txn,
char** errptr) {
SaveError(errptr, txn->rep->RollbackToSavePoint());
}
@@ -0,0 +1,58 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'Transaction'
// --header-out
// c_api_gen/c_generated_transaction_subset.h.inc
// --source-out
// c_api_gen/c_generated_transaction_subset.cc.inc
/* Transaction */
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put(
rocksdb_transaction_t* txn, const char* key, size_t klen, const char* val,
size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put_cf(
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_merge(
rocksdb_transaction_t* txn, const char* key, size_t klen, const char* val,
size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_merge_cf(
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_delete(
rocksdb_transaction_t* txn, const char* key, size_t klen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_delete_cf(
rocksdb_transaction_t* txn, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_commit(
rocksdb_transaction_t* txn, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_rollback(
rocksdb_transaction_t* txn, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_put_log_data(
rocksdb_transaction_t* txn, const char* blob, size_t len);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_set_savepoint(
rocksdb_transaction_t* txn);
extern ROCKSDB_LIBRARY_API void rocksdb_transaction_rollback_to_savepoint(
rocksdb_transaction_t* txn, char** errptr);
@@ -0,0 +1,91 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'TransactionDB simple'
// --header-out
// c_api_gen/c_generated_transactiondb_subset.h.inc
// --source-out
// c_api_gen/c_generated_transactiondb_subset.cc.inc
/* TransactionDB simple */
void rocksdb_transactiondb_put(rocksdb_transactiondb_t* txn_db,
const rocksdb_writeoptions_t* options,
const char* key, size_t klen, const char* val,
size_t vlen, char** errptr) {
SaveError(errptr,
txn_db->rep->Put(options->rep, Slice(key, klen), Slice(val, vlen)));
}
void rocksdb_transactiondb_put_cf(rocksdb_transactiondb_t* txn_db,
const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t keylen,
const char* val, size_t vallen,
char** errptr) {
SaveError(errptr, txn_db->rep->Put(options->rep, column_family->rep,
Slice(key, keylen), Slice(val, vallen)));
}
void rocksdb_transactiondb_write(rocksdb_transactiondb_t* db,
const rocksdb_writeoptions_t* options,
rocksdb_writebatch_t* batch, char** errptr) {
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
}
void rocksdb_transactiondb_merge(rocksdb_transactiondb_t* txn_db,
const rocksdb_writeoptions_t* options,
const char* key, size_t klen, const char* val,
size_t vlen, char** errptr) {
SaveError(errptr, txn_db->rep->Merge(options->rep, Slice(key, klen),
Slice(val, vlen)));
}
void rocksdb_transactiondb_merge_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
const char* val, size_t vlen, char** errptr) {
SaveError(errptr, txn_db->rep->Merge(options->rep, column_family->rep,
Slice(key, klen), Slice(val, vlen)));
}
void rocksdb_transactiondb_delete(rocksdb_transactiondb_t* txn_db,
const rocksdb_writeoptions_t* options,
const char* key, size_t klen, char** errptr) {
SaveError(errptr, txn_db->rep->Delete(options->rep, Slice(key, klen)));
}
void rocksdb_transactiondb_delete_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, char** errptr) {
SaveError(errptr, txn_db->rep->Delete(options->rep, column_family->rep,
Slice(key, keylen)));
}
void rocksdb_transactiondb_flush_wal(rocksdb_transactiondb_t* txn_db,
unsigned char sync, char** errptr) {
SaveError(errptr, txn_db->rep->FlushWAL(sync));
}
void rocksdb_transactiondb_flush(rocksdb_transactiondb_t* txn_db,
const rocksdb_flushoptions_t* options,
char** errptr) {
SaveError(errptr, txn_db->rep->Flush(options->rep));
}
void rocksdb_transactiondb_flush_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
rocksdb_column_family_handle_t* column_family, char** errptr) {
SaveError(errptr, txn_db->rep->Flush(options->rep, column_family->rep));
}
@@ -0,0 +1,62 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'TransactionDB simple'
// --header-out
// c_api_gen/c_generated_transactiondb_subset.h.inc
// --source-out
// c_api_gen/c_generated_transactiondb_subset.cc.inc
/* TransactionDB simple */
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_put(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_put_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, const char* val, size_t vallen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_write(
rocksdb_transactiondb_t* db, const rocksdb_writeoptions_t* options,
rocksdb_writebatch_t* batch, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_merge(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
const char* key, size_t klen, const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_merge_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
const char* val, size_t vlen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_delete(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
const char* key, size_t klen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_delete_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
rocksdb_column_family_handle_t* column_family, const char* key,
size_t keylen, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush_wal(
rocksdb_transactiondb_t* txn_db, unsigned char sync, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush(
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_transactiondb_flush_cf(
rocksdb_transactiondb_t* txn_db, const rocksdb_flushoptions_t* options,
rocksdb_column_family_handle_t* column_family, char** errptr);
@@ -0,0 +1,62 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'WriteBatch'
// --header-out
// c_api_gen/c_generated_writebatch_subset.h.inc
// --source-out
// c_api_gen/c_generated_writebatch_subset.cc.inc
/* WriteBatch */
void rocksdb_writebatch_clear(rocksdb_writebatch_t* b) { b->rep.Clear(); }
void rocksdb_writebatch_put(rocksdb_writebatch_t* b, const char* key,
size_t klen, const char* val, size_t vlen) {
b->rep.Put(Slice(key, klen), Slice(val, vlen));
}
void rocksdb_writebatch_put_cf(rocksdb_writebatch_t* b,
rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val,
size_t vlen) {
b->rep.Put(column_family->rep, Slice(key, klen), Slice(val, vlen));
}
void rocksdb_writebatch_delete(rocksdb_writebatch_t* b, const char* key,
size_t klen) {
b->rep.Delete(Slice(key, klen));
}
void rocksdb_writebatch_put_log_data(rocksdb_writebatch_t* b, const char* blob,
size_t len) {
b->rep.PutLogData(Slice(blob, len));
}
void rocksdb_writebatch_set_save_point(rocksdb_writebatch_t* b) {
b->rep.SetSavePoint();
}
void rocksdb_writebatch_rollback_to_save_point(rocksdb_writebatch_t* b,
char** errptr) {
SaveError(errptr, b->rep.RollbackToSavePoint());
}
void rocksdb_writebatch_pop_save_point(rocksdb_writebatch_t* b, char** errptr) {
SaveError(errptr, b->rep.PopSavePoint());
}
void rocksdb_writebatch_verify_checksum(rocksdb_writebatch_t* b,
char** errptr) {
SaveError(errptr, b->rep.VerifyChecksum());
}
@@ -0,0 +1,51 @@
// 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).
// @generated
// -----------------------------------------------------------------------------
// Auto-generated by tools/c_api_gen/generate_c_api.py.
// DO NOT EDIT THIS FILE DIRECTLY.
// Source: tools/c_api_gen/spec.json
// -----------------------------------------------------------------------------
// To regenerate this file:
// python3 tools/c_api_gen/generate_c_api.py
// --spec tools/c_api_gen/spec.json --section 'WriteBatch'
// --header-out
// c_api_gen/c_generated_writebatch_subset.h.inc
// --source-out
// c_api_gen/c_generated_writebatch_subset.cc.inc
/* WriteBatch */
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_clear(
rocksdb_writebatch_t* b);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put(rocksdb_writebatch_t* b,
const char* key,
size_t klen,
const char* val,
size_t vlen);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put_cf(
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
const char* key, size_t klen, const char* val, size_t vlen);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_delete(
rocksdb_writebatch_t* b, const char* key, size_t klen);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_put_log_data(
rocksdb_writebatch_t* b, const char* blob, size_t len);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_set_save_point(
rocksdb_writebatch_t* b);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_rollback_to_save_point(
rocksdb_writebatch_t* b, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_pop_save_point(
rocksdb_writebatch_t* b, char** errptr);
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_verify_checksum(
rocksdb_writebatch_t* b, char** errptr);
+3 -2
View File
@@ -180,7 +180,7 @@ DEFINE_uint32(
// than random." Even with this setting, file offsets are not randomized.
DEFINE_bool(sck_randomize, false,
"(-stress_cache_key) Randomize (hash) cache key");
// See https://github.com/facebook/rocksdb/pull/9058
// See https://github.com/meta/rocksdb/pull/9058
DEFINE_bool(sck_footer_unique_id, false,
"(-stress_cache_key) Simulate using proposed footer unique id");
// ## END stress_cache_key sub-tool options ##
@@ -276,7 +276,8 @@ struct KeyGen {
// location, so we fix hash bits starting at the bottom of that word.
auto hi_hash = uint64_t{0x9e3779b97f4a7c13U} ^
(key_hash << 1 << (FLAGS_degenerate_hash_bits - 1));
uint64_t un_hi, un_lo;
uint64_t un_hi;
uint64_t un_lo;
BijectiveUnhash2x64(hi_hash, key_hash, &un_hi, &un_lo);
un_lo ^= BitwiseAnd(FLAGS_seed, INT32_MAX);
EncodeFixed64(key_data, un_lo);
+18
View File
@@ -127,6 +127,24 @@ class OffsetableCacheKey : private CacheKey {
return CacheKey(file_num_etc64_, offset_etc64_ ^ offset);
}
// Construct a CacheKey for a record located at byte `offset` within a file in
// which every distinct cached region is at least 5 bytes long. The low two
// offset bits are dropped, which is collision-free under that >= 5-byte (>= 4
// would suffice) guarantee and lets different kinds of records in the same
// file share a single keyspace without colliding.
//
// This is the canonical cache-key scheme for byte-offset records that carry
// the block-based "min 5 byte" guarantee: block-based SST blocks (see
// BlockBasedTable::GetCacheKey) and SimpleGen2Blob records (which always
// include a >= 5-byte trailer; see db/blob/blob_gen2_format.h). Because both
// use this same function, an SST's data blocks and its embedded blob records
// never collide even when the block cache and blob cache are the same cache.
// Keeping a single implementation here avoids a divergence bug that would
// silently alias the two keyspaces.
inline CacheKey WithOffsetForMinSizeRecord(uint64_t offset) const {
return WithOffset(offset >> 2);
}
// The "common prefix" is a shared prefix for all the returned CacheKeys.
// It is specific to the file but the same for all offsets within the file.
static constexpr size_t kCommonPrefixSize = 8;
+7 -2
View File
@@ -32,9 +32,9 @@ class CacheReservationManager {
// for an incremental amount of memory used (i.e, incremental_memory_used)
class CacheReservationHandle {
public:
virtual ~CacheReservationHandle() {}
virtual ~CacheReservationHandle() = default;
};
virtual ~CacheReservationManager() {}
virtual ~CacheReservationManager() = default;
virtual Status UpdateCacheReservation(std::size_t new_memory_used) = 0;
// TODO(hx235): replace the usage of
// `UpdateCacheReservation(memory_used_delta, increase)` with
@@ -235,6 +235,11 @@ class ConcurrentCacheReservationManager
cache_res_mgr_ = cache_res_mgr;
cache_res_handle_ = std::move(cache_res_handle);
}
// No copying allowed
CacheReservationHandle(const CacheReservationHandle&) = delete;
CacheReservationHandle& operator=(const CacheReservationHandle&) = delete;
CacheReservationHandle(CacheReservationHandle&&) = delete;
CacheReservationHandle& operator=(CacheReservationHandle&&) = delete;
~CacheReservationHandle() override {
std::lock_guard<std::mutex> lock(cache_res_mgr_->cache_res_mgr_mu_);
+2 -2
View File
@@ -407,8 +407,8 @@ TEST(CacheReservationHandleTest, HandleTest) {
std::size_t mem_used = 0;
const std::size_t incremental_mem_used_handle_1 = 1 * kSizeDummyEntry;
const std::size_t incremental_mem_used_handle_2 = 2 * kSizeDummyEntry;
std::unique_ptr<CacheReservationManager::CacheReservationHandle> handle_1,
handle_2;
std::unique_ptr<CacheReservationManager::CacheReservationHandle> handle_1;
std::unique_ptr<CacheReservationManager::CacheReservationHandle> handle_2;
// To test consecutive CacheReservationManager::MakeCacheReservation works
// correctly in terms of returning the handle as well as updating cache
+19
View File
@@ -948,6 +948,25 @@ TEST_P(CacheTest, GetChargeAndDeleter) {
cache_->Release(h1);
}
TEST_P(LRUCacheTest, UnchargedStandaloneGetCharge) {
auto cache = NewCache(1000, 0, true, kFullChargeCacheMetadata);
Cache::Handle* pinned = nullptr;
ASSERT_OK(
cache->Insert(EncodeKey(1), EncodeValue(1), &kHelper, 800, &pinned));
ASSERT_EQ(nullptr, cache->CreateStandalone(EncodeKey(2), EncodeValue(2),
&kHelper, 500, false));
Cache::Handle* h = cache->CreateStandalone(EncodeKey(2), EncodeValue(2),
&kHelper, 500, true);
ASSERT_NE(h, nullptr);
EXPECT_EQ(0, cache->GetCharge(h));
EXPECT_EQ(0, cache->GetUsage(h));
cache->Release(h);
cache->Release(pinned);
}
namespace {
bool AreTwoCacheKeysOrdered(Cache* cache) {
std::vector<std::string> keys;
+13 -4
View File
@@ -975,7 +975,8 @@ void FixedHyperClockTable::Erase(const UniqueId64x2& hashed_key) {
// Could be multiple entries in rare cases. Erase them all.
// Optimistically increment acquire counter
auto add_acquire = AcquireCounter::PlusTransformPromiseNoOverflow(1);
SlotMeta old_meta, meta;
SlotMeta old_meta;
SlotMeta meta;
h->meta.Apply(add_acquire, &old_meta, &meta);
// Check if it's an entry visible to lookups
if (meta.IsVisible()) {
@@ -1738,7 +1739,8 @@ inline bool MatchAndRef(const UniqueId64x2* hashed_key, const ClockHandle& h,
// Must be at least something to match
assert(hashed_key || shift > 0);
SlotMeta old_meta, new_meta;
SlotMeta old_meta;
SlotMeta new_meta;
// (Optimistically) increment acquire counter.
auto add_acquire = AcquireCounter::PlusTransformPromiseNoOverflow(1);
h.meta.Apply(add_acquire, &old_meta, &new_meta);
@@ -1856,6 +1858,12 @@ class AutoHyperClockTable::ChainRewriteLock {
assert(saved_head_.IsLocked());
}
// No copying allowed
ChainRewriteLock(const ChainRewriteLock&) = delete;
ChainRewriteLock& operator=(const ChainRewriteLock&) = delete;
ChainRewriteLock(ChainRewriteLock&&) = delete;
ChainRewriteLock& operator=(ChainRewriteLock&&) = delete;
~ChainRewriteLock() {
if (!IsEnd()) {
// Release lock
@@ -3108,7 +3116,7 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
HandleImpl* const arr = array_.Get();
NextWithShift next_with_shift = arr[home].head_next_with_shift.LoadRelaxed();
for (size_t i = 0; !next_with_shift.IsEnd() && i < 10; ++i) {
HandleImpl* h = &arr[next_with_shift.IsEnd()];
HandleImpl* h = &arr[next_with_shift.GetNext()];
// Attempt cheap key match without acquiring a read ref. This could give a
// false positive, which is re-checked after acquiring read ref, or false
// negative, which is re-checked in the full Lookup. Also, this is a
@@ -3122,7 +3130,8 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
if (probably_equal) {
// Increment acquire counter for definitive check
auto add_acquire = AcquireCounter::PlusTransformPromiseNoOverflow(1);
SlotMeta old_meta, new_meta;
SlotMeta old_meta;
SlotMeta new_meta;
h->meta.Apply(add_acquire, &old_meta, &new_meta);
// Check if it's a referencable (sharable) entry
if (LIKELY(old_meta.IsShareable())) {
+4
View File
@@ -29,6 +29,10 @@ class CompressedSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
const CompressedSecondaryCacheResultHandle&) = delete;
CompressedSecondaryCacheResultHandle& operator=(
const CompressedSecondaryCacheResultHandle&) = delete;
CompressedSecondaryCacheResultHandle(CompressedSecondaryCacheResultHandle&&) =
delete;
CompressedSecondaryCacheResultHandle& operator=(
CompressedSecondaryCacheResultHandle&&) = delete;
bool IsReady() override { return true; }
+2 -1
View File
@@ -508,7 +508,8 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
std::string str1 = rnd.RandomString(1001);
auto item1 = std::make_unique<TestItem>(str1.data(), str1.length());
ASSERT_OK(cache->Insert(key1, item1.get(), GetHelper(), str1.length()));
item1.release(); // Appease clang-analyze "potential memory leak"
// Appease clang-analyze "potential memory leak"
[[maybe_unused]] TestItem* released_item1 = item1.release();
Cache::Handle* handle;
handle = cache->Lookup(key2, nullptr, this, Cache::Priority::LOW);
+1 -1
View File
@@ -574,7 +574,7 @@ LRUHandle* LRUCacheShard::CreateStandalone(const Slice& key, uint32_t hash,
if (strict_capacity_limit_ && (usage_ + e->total_charge) > capacity_) {
if (allow_uncharged) {
e->total_charge = 0;
e->total_charge = LRUHandle::kUnchargedStandaloneTotalCharge;
} else {
free(e);
e = nullptr;
+8
View File
@@ -87,6 +87,10 @@ struct LRUHandle : public Cache::Handle {
IM_IS_STANDALONE = (1 << 2),
};
// total_charge sentinel for a standalone entry that added nothing to usage_
// (strict cache full, created with allow_uncharged).
static constexpr size_t kUnchargedStandaloneTotalCharge = 0;
// Beginning of the key (MUST BE THE LAST FIELD IN THIS STRUCT!)
char key_data[1];
@@ -196,6 +200,10 @@ struct LRUHandle : public Cache::Handle {
inline size_t GetCharge(
CacheMetadataChargePolicy metadata_charge_policy) const {
if (IsStandalone() && total_charge == kUnchargedStandaloneTotalCharge) {
// Documented to report 0; no metadata was charged.
return 0;
}
size_t meta_charge = CalcuMetaCharge(metadata_charge_policy);
assert(total_charge >= meta_charge);
return total_charge - meta_charge;
+73 -59
View File
@@ -1366,7 +1366,8 @@ TEST_P(BasicSecondaryCacheTest, BasicFailTest) {
// str1.length()).IsInvalidArgument());
ASSERT_OK(
cache->Insert(k1.AsSlice(), item1.get(), GetHelper(), str1.length()));
item1.release(); // Appease clang-analyze "potential memory leak"
// Appease clang-analyze "potential memory leak"
[[maybe_unused]] TestItem* released_item1 = item1.release();
Cache::Handle* handle;
handle = cache->Lookup(k2.AsSlice(), nullptr, /*context*/ this,
@@ -1473,68 +1474,81 @@ TEST_P(BasicSecondaryCacheTest, CreateFailTest) {
}
TEST_P(BasicSecondaryCacheTest, FullCapacityTest) {
for (bool strict_capacity_limit : {false, true}) {
std::shared_ptr<TestSecondaryCache> secondary_cache =
std::make_shared<TestSecondaryCache>(2048, true);
std::shared_ptr<Cache> cache =
NewCache(1024 /* capacity */, 0 /* num_shard_bits */,
strict_capacity_limit, secondary_cache);
CacheKey k1 = CacheKey::CreateUniqueForCacheLifetime(cache.get());
CacheKey k2 = CacheKey::CreateUniqueForCacheLifetime(cache.get());
const size_t kValueSize = 700;
estimated_value_size_ = kValueSize;
for (CacheMetadataChargePolicy charge_policy :
{kDontChargeCacheMetadata, kFullChargeCacheMetadata}) {
SCOPED_TRACE(charge_policy);
for (bool strict_capacity_limit : {false, true}) {
SCOPED_TRACE(strict_capacity_limit);
std::shared_ptr<TestSecondaryCache> secondary_cache =
std::make_shared<TestSecondaryCache>(2048, true);
std::shared_ptr<Cache> cache =
NewCache(1024, [&](ShardedCacheOptions& opts) {
opts.num_shard_bits = 0;
opts.strict_capacity_limit = strict_capacity_limit;
opts.metadata_charge_policy = charge_policy;
opts.secondary_cache = secondary_cache;
});
CacheKey k1 = CacheKey::CreateUniqueForCacheLifetime(cache.get());
CacheKey k2 = CacheKey::CreateUniqueForCacheLifetime(cache.get());
Random rnd(301);
std::string str1 = rnd.RandomString(1020);
TestItem* item1 = new TestItem(str1.data(), str1.length());
ASSERT_OK(cache->Insert(k1.AsSlice(), item1, GetHelper(), str1.length()));
std::string str2 = rnd.RandomString(1020);
TestItem* item2 = new TestItem(str2.data(), str2.length());
// k1 should be demoted to NVM
ASSERT_OK(cache->Insert(k2.AsSlice(), item2, GetHelper(), str2.length()));
Random rnd(301);
std::string str1 = rnd.RandomString(kValueSize);
TestItem* item1 = new TestItem(str1.data(), str1.length());
ASSERT_OK(cache->Insert(k1.AsSlice(), item1, GetHelper(), str1.length()));
std::string str2 = rnd.RandomString(kValueSize);
TestItem* item2 = new TestItem(str2.data(), str2.length());
// k1 should be demoted to NVM
ASSERT_OK(cache->Insert(k2.AsSlice(), item2, GetHelper(), str2.length()));
Cache::Handle* handle2;
handle2 = cache->Lookup(k2.AsSlice(), GetHelper(),
/*context*/ this, Cache::Priority::LOW);
ASSERT_NE(handle2, nullptr);
// k1 lookup fails without secondary cache support
Cache::Handle* handle1;
handle1 = cache->Lookup(
k1.AsSlice(),
GetHelper(CacheEntryRole::kDataBlock, /*secondary_compatible=*/false),
/*context*/ this, Cache::Priority::LOW);
ASSERT_EQ(handle1, nullptr);
// k1 promotion can fail with strict_capacity_limit=true, but Lookup still
// succeeds using a standalone handle
handle1 = cache->Lookup(k1.AsSlice(), GetHelper(),
/*context*/ this, Cache::Priority::LOW);
ASSERT_NE(handle1, nullptr);
ASSERT_EQ(secondary_cache->num_inserts(), 1u);
ASSERT_EQ(secondary_cache->num_lookups(), 1u);
// Releasing k2's handle first, k2 is evicted from primary iff k1 promotion
// was charged to the cache (except HCC doesn't erase in Release() over
// capacity)
// FIXME: Insert to secondary from Release disabled
cache->Release(handle2);
cache->Release(handle1);
handle2 = cache->Lookup(
k2.AsSlice(),
GetHelper(CacheEntryRole::kDataBlock, /*secondary_compatible=*/false),
/*context*/ this, Cache::Priority::LOW);
if (strict_capacity_limit || IsHyperClock()) {
Cache::Handle* handle2;
handle2 = cache->Lookup(k2.AsSlice(), GetHelper(),
/*context*/ this, Cache::Priority::LOW);
ASSERT_NE(handle2, nullptr);
cache->Release(handle2);
ASSERT_EQ(secondary_cache->num_inserts(), 1u);
} else {
ASSERT_EQ(handle2, nullptr);
// FIXME: Insert to secondary from Release disabled
// ASSERT_EQ(secondary_cache->num_inserts(), 2u);
ASSERT_EQ(secondary_cache->num_inserts(), 1u);
}
// k1 lookup fails without secondary cache support
Cache::Handle* handle1;
handle1 = cache->Lookup(
k1.AsSlice(),
GetHelper(CacheEntryRole::kDataBlock, /*secondary_compatible=*/false),
/*context*/ this, Cache::Priority::LOW);
ASSERT_EQ(handle1, nullptr);
cache.reset();
secondary_cache.reset();
// k1 promotion can fail with strict_capacity_limit=true, but Lookup still
// succeeds using a standalone handle
handle1 = cache->Lookup(k1.AsSlice(), GetHelper(),
/*context*/ this, Cache::Priority::LOW);
ASSERT_NE(handle1, nullptr);
ASSERT_EQ(strict_capacity_limit ? 0 : str1.size(),
cache->GetUsage(handle1));
ASSERT_EQ(secondary_cache->num_inserts(), 1u);
ASSERT_EQ(secondary_cache->num_lookups(), 1u);
// Releasing k2's handle first, k2 is evicted from primary iff k1
// promotion was charged to the cache (except HCC doesn't erase in
// Release() over capacity)
// FIXME: Insert to secondary from Release disabled
cache->Release(handle2);
cache->Release(handle1);
handle2 = cache->Lookup(
k2.AsSlice(),
GetHelper(CacheEntryRole::kDataBlock, /*secondary_compatible=*/false),
/*context*/ this, Cache::Priority::LOW);
if (strict_capacity_limit || IsHyperClock()) {
ASSERT_NE(handle2, nullptr);
cache->Release(handle2);
ASSERT_EQ(secondary_cache->num_inserts(), 1u);
} else {
ASSERT_EQ(handle2, nullptr);
// FIXME: Insert to secondary from Release disabled
// ASSERT_EQ(secondary_cache->num_inserts(), 2u);
ASSERT_EQ(secondary_cache->num_inserts(), 1u);
}
cache.reset();
secondary_cache.reset();
}
}
}
+9 -3
View File
@@ -90,7 +90,7 @@ class CacheShardBase {
class ShardedCacheBase : public Cache {
public:
explicit ShardedCacheBase(const ShardedCacheOptions& opts);
virtual ~ShardedCacheBase() = default;
~ShardedCacheBase() override = default;
int GetNumShardBits() const;
uint32_t GetNumShards() const;
@@ -143,7 +143,13 @@ class ShardedCache : public ShardedCacheBase {
sizeof(CacheShard) * GetNumShards()))),
destroy_shards_in_dtor_(false) {}
virtual ~ShardedCache() {
// No copying allowed
ShardedCache(const ShardedCache&) = delete;
ShardedCache& operator=(const ShardedCache&) = delete;
ShardedCache(ShardedCache&&) = delete;
ShardedCache& operator=(ShardedCache&&) = delete;
~ShardedCache() override {
if (destroy_shards_in_dtor_) {
ForEachShard([](CacheShard* cs) { cs->~CacheShard(); });
}
@@ -245,7 +251,7 @@ class ShardedCache : public ShardedCacheBase {
std::unique_ptr<size_t[]> states(new size_t[num_shards]{});
size_t aepl = opts.average_entries_per_lock;
aepl = std::min(aepl, size_t{1});
aepl = std::max(aepl, size_t{1});
bool remaining_work;
do {
+2 -2
View File
@@ -36,7 +36,7 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
#endif
}
~TieredSecondaryCache() override {}
~TieredSecondaryCache() override = default;
const char* Name() const override { return "TieredSecondaryCache"; }
@@ -75,7 +75,7 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
class ResultHandle : public SecondaryCacheResultHandle {
public:
~ResultHandle() override {}
~ResultHandle() override = default;
bool IsReady() override {
if (inner_handle_ && inner_handle_->IsReady()) {
+1 -1
View File
@@ -951,7 +951,7 @@ TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
IODebugContext* dbg) override {
for (size_t i = 0; i < num_reqs; ++i) {
FSReadRequest& req = reqs[i];
// See https://github.com/facebook/rocksdb/pull/13195 for why we
// See https://github.com/meta/rocksdb/pull/13195 for why we
// want to set up our test implementation for FSAllocationPtr this
// way.
char* internalData = new char[req.len];
+1 -1
View File
@@ -241,7 +241,7 @@ class FullTypedCacheHelperFns : public BasicTypedCacheHelperFns<TValue> {
if (source != CacheTier::kVolatileTier) {
return Status::InvalidArgument();
}
if constexpr (sizeof(TCreateContext) > 0) {
if constexpr (!std::is_empty_v<TCreateContext>) {
TCreateContext* tcontext = static_cast<TCreateContext*>(context);
tcontext->Create(&value, out_charge, data, type, allocator);
} else {
+24 -1
View File
@@ -421,6 +421,30 @@ Example reference: commit `429b36c22d76403d275dd0e6877b08d4cea2bc90` (block_alig
If an option already exists but needs C API support:
**First decide whether the option is auto-managed.**
Many simple option fields are no longer maintained by hand in
`include/rocksdb/c.h` and `db/c.cc`. Before adding a manual wrapper:
- Check whether the option belongs to a family handled by
`tools/c_api_gen/auto_simple_bindings.py`.
- If it is a simple scalar/enum/string field in an auto-managed family, run
`python3 tools/c_api_gen/regen_all.py` and let the generator add the C API.
- After regenerating, run
`python3 tools/c_api_gen/verify_generated_up_to_date.py` to confirm the
checked-in generated fragments stay stable.
- Do not edit generated `.inc` files under `c_api_gen/` by hand.
- If the new field is not ready for C API support yet, add an entry to
`tools/c_api_gen/auto_simple_bindings_blocklist.json` with
`"policy": "deferred"` and a concrete reason so the build stays intentional
rather than silently drifting.
- If the field needs custom ownership, callback, vector, or nested-struct
marshalling, keep it manual or move it into `tools/c_api_gen/spec.json`
depending on the API shape.
If the option is not auto-managed, or the C API really is a custom/manual case,
the traditional hand-written pattern still applies:
**File: `db/c.cc`**
```cpp
@@ -509,4 +533,3 @@ Common option types used in serialization:
- [ ] unreleased_history markdown file
- [ ] Java API (for BlockBasedTableOptions)
- [ ] C API (if needed)
+46 -5
View File
@@ -168,9 +168,18 @@ Status YourNewAPI(const YourAPIOptions& /*options*/,
### Step 5: Add C API Bindings
**Header:** `include/rocksdb/c.h`
> **Important:** `include/rocksdb/c.h` and `db/c.cc` are `@generated` and must
> NOT be edited by hand — your changes would be overwritten on the next
> regeneration. Choose the right path first (see "Before writing C API code by
> hand" below). For hand-written (manual) wrappers, edit the source templates
> `tools/c_api_gen/c_base.h` (declarations) and `tools/c_api_gen/c_base.cc`
> (implementations), then run `python3 tools/c_api_gen/regen_all.py` to produce
> `c.h` / `c.cc`. The snippets below show the *declaration* and *implementation*
> you would add to those templates.
\`\`\`c
**Header (declaration → `tools/c_api_gen/c_base.h`):**
```c
// Basic version
extern ROCKSDB_LIBRARY_API void rocksdb_your_new_api(
rocksdb_t* db,
@@ -191,7 +200,7 @@ extern ROCKSDB_LIBRARY_API void rocksdb_your_new_api_opt(
char** errptr);
\`\`\`
**Implementation:** `db/c.cc`
**Implementation (→ `tools/c_api_gen/c_base.cc`):**
\`\`\`cpp
void rocksdb_your_new_api(rocksdb_t* db, const char* start_key,
@@ -217,6 +226,38 @@ void rocksdb_your_new_api_cf(rocksdb_t* db,
}
\`\`\`
**Before writing C API code by hand, choose the right path:**
- **Auto-managed simple struct fields:** If you added a new field to a managed
public struct such as `ReadOptions`, selected option structs, or managed
metadata structs, first check `tools/c_api_gen/auto_simple_bindings.py`.
Simple scalar/enum/string fields should be regenerated with
`python3 tools/c_api_gen/regen_all.py` rather than hand-editing
`include/rocksdb/c.h` / `db/c.cc`.
After regenerating, run
`python3 tools/c_api_gen/verify_generated_up_to_date.py` to confirm the
checked-in generated fragments are stable.
- **Spec-driven wrappers:** If the API is still mechanically generated but needs
an explicit C shape, naming, or `Status`/`char** errptr` policy, add it to
`tools/c_api_gen/spec.json`.
- **Fully manual wrappers:** If the API uses callbacks, ownership transfer,
vectors/maps, open flows, or otherwise irregular marshalling, keep the C API
hand-written.
**Temporary deferral for auto-managed families:**
If a new field lands in an auto-managed family but the C API shape is not ready
yet, add a checked-in entry to
`tools/c_api_gen/auto_simple_bindings_blocklist.json` with:
- `policy: "manual"` when the field is intentionally outside simple auto-gen
- `policy: "deferred"` when the field should be revisited later
- a concrete `reason`
- `tracking_issue` when available
If a field in an auto-managed family is not supported by the generator and is
not covered by the blocklist, regeneration should fail. That is intentional.
**If you have options, also add:**
\`\`\`cpp
@@ -482,8 +523,8 @@ public class YourAPIOptionsTest {
| StackableDB | `include/rocksdb/utilities/stackable_db.h` | ✓ |
| Secondary DB | `db/db_impl/db_impl_secondary.h` | If not supported |
| Compacted DB | `db/db_impl/compacted_db_impl.h` | If not supported |
| C API Header | `include/rocksdb/c.h` | ✓ |
| C API Implementation | `db/c.cc` | ✓ |
| C API Header (manual wrappers) | `tools/c_api_gen/c_base.h` → regen → `include/rocksdb/c.h` (`@generated`) | ✓ |
| C API Implementation (manual wrappers) | `tools/c_api_gen/c_base.cc` → regen → `db/c.cc` (`@generated`) | ✓ |
| Java API | `java/src/main/java/org/rocksdb/RocksDB.java` | ✓ |
| Java Options | `java/src/main/java/org/rocksdb/YourAPIOptions.java` | If needed |
| JNI Implementation | `java/rocksjni/rocksjni.cc` | ✓ |
+161 -15
View File
@@ -4,12 +4,135 @@
Thorough multi-agent code review for RocksDB commits following CLAUDE.md guidelines,
with structured codebase exploration, inter-agent debate, and verification.
IMPORTANT — Incremental output: After completing EACH major phase (Setup,
Codebase Context, Initial Review, Debate, Synthesis), append your findings
to review-findings.md using the Write tool. This ensures partial results are
saved even if the review is interrupted by the turn limit. After the final
synthesis, write the complete review to review-findings.md, replacing
previous content. Also output the final review as your response text.
## Behavioral Compatibility Review Perspective
Behavior changes are human judgment calls. They must be intentional,
well-documented (including release notes), and researched for potential
downstream production impact. Agents and reviewers must call out concrete gaps,
ambiguity, or adverse impacts in the change's intent, documentation, and
production-impact analysis.
When a diff changes how an existing accepted public or serialized input is
interpreted—including an undocumented, discouraged, or historically ignored
value—reviewers must:
- Compare the old and new observable behavior, including performance and
resource impact.
- Check that the rationale and user-facing documentation clearly describe the
change.
- Identify known direct callers and stored configurations that may depend on
the previous behavior.
- Check for appropriate regression coverage, or a documented reason why such
coverage is not practical.
A behavior change is not automatically a defect. Report a finding when the
intent, documentation, downstream-impact analysis, or mitigation has a concrete
gap or ambiguity that could lead to an unintended compatibility regression.
## DB Mutex Review Perspective
For every non-trivial change, determine whether the changed code, or any
transitive callee it adds or changes, can run while `DBImpl::mutex_` is held.
State the result explicitly, including when the DB mutex is not affected.
`DBImpl::mutex_` is the DB-wide metadata and coordination mutex. It protects
cross-object invariants involving `ColumnFamilyData`, memtable and
`SuperVersion` publication, `VersionSet`, snapshots, flush/compaction queues,
background-job state, and file lifetime. It is shared by all column families
and can be a hot lock. It is not the filesystem `db_lock_`, and it should not
replace data-path synchronization already provided by `WriteThread`,
`wal_write_mutex_`, atomics, referenced `SuperVersion`s, or component-local
synchronization.
When a DB-mutex critical section is affected:
### Locking and correctness
- Trace the full call path and mark the exact lock and unlock boundaries. Do
not inspect only the lines that directly acquire the mutex.
- State the locking contract. Helpers that require the lock should document it
and normally use `mutex_.AssertHeld()`. Check whether each caller expects the
lock to be held on return.
- Treat calls that can release and re-acquire the mutex as concurrency
boundaries. Examples include `VersionSet::LogAndApply()`, condition-variable
waits, write-thread coordination, and notification helpers. Revalidate state
inspected before such a call.
- Keep atomic metadata transitions under the mutex. Examples include moving a
memtable between mutable and immutable state, reserving compaction inputs,
installing a `Version` or `SuperVersion`, updating scheduling counters and
queues, and changing file-lifetime protection.
- For expensive work, prefer a prepare/run/install structure: reserve and pin
the required state while locked, do CPU or I/O work while unlocked, then
re-lock, validate, and publish. Before unlocking, copy mutable options and
acquire every required object reference or lifetime reservation.
- Check object lifetime across every unlocked region. Do not use a raw
`ColumnFamilyData*`, `Version*`, memtable, file metadata pointer, or DB-owned
container element after unlocking unless an explicit reference,
`SuperVersion`, job object, or pending-output reservation keeps it alive.
- Preserve the snapshot/input-selection boundary. Flush and compaction must not
miss a snapshot whose visible key versions they may remove. Missing a newly
created snapshot is a correctness bug; retaining a recently released
snapshot is conservative but safe.
- Never invoke untrusted or re-entrant code while holding the mutex. The mutex
is non-reentrant, so a callback into RocksDB can self-deadlock.
- Check lock ordering. `options_mutex_` is acquired before the DB mutex. When
both the DB mutex and `wal_write_mutex_` are needed, the DB mutex is acquired
first. Establish and document the order for other locks.
- Keep cleanup outside the critical section when possible. File deletion,
`JobContext::Clean()`, expensive `SuperVersion` destruction, logging, and
listener callbacks should normally run after protected state is detached or
snapshotted.
- Do not add the DB mutex to steady-state point reads or the normal write data
phase without strong justification. Reads normally use a referenced
`SuperVersion`; writes use `WriteThread` and component-specific
synchronization.
### Critical-section performance
- Compare the CPU and I/O performed under the mutex before and after the
change. Include call frequency, loop bounds, input-size scaling, hidden work
in callees, and worst-case cost.
- Account for filesystem or network I/O, waits, callbacks, logging and
formatting, allocation, container growth, copies, sorting, scans, reference
release and destructors, cache misses, and repeated metadata lookups.
- Do not allow a significant regression in mutex-held work without clear
justification, hold-time or contention measurements, and representative
benchmarks.
- Treat unavoidable work under the mutex as hot-path code and keep it highly
optimized. Precompute, allocate, copy, and perform I/O outside the lock;
remove repeated scans and lookups; cache or batch work where practical; and
keep locked operations O(1) or tightly bounded when possible.
- Treat new blocking I/O, waits, callbacks into user or plugin code, unbounded
loops, or work that scales with DB size, files, column families, snapshots,
or pending jobs as a blocker unless it is unavoidable and supported by strong
measurements and explicit design justification.
### Validation and evidence
- Test unlock windows with sync points rather than sleeps. Cover relevant races
with memtable switches, snapshot create/release, column-family drop,
flush/compaction install, shutdown, and background errors. Run TSAN where
appropriate.
- Distinguish wait time from hold time. `PerfContext::db_mutex_lock_nanos` and
`DB_MUTEX_WAIT_MICROS` primarily expose acquisition wait; use contention
profiles or scoped instrumentation to find long critical sections.
- Report affected critical sections, before/after cost, workload assumptions,
optimization choices, and remaining risk. If exact measurement is not
practical, provide a conservative static bound and do not claim no regression
without evidence.
IMPORTANT — Where the review goes: Your FINAL RESPONSE TEXT is the review.
The PR comment is generated verbatim from your final response, so the
complete, polished review MUST appear there in full. Do NOT end your turn with
a meta-summary such as "the review has been written to review-findings.md" —
paste the entire review as your response.
review-findings.md is a RECOVERY BACKSTOP, not the primary output: after
completing EACH major phase (Setup, Codebase Context, Initial Review, Debate,
Synthesis), append your current findings to it with the Write tool, and after
the final synthesis overwrite it with the complete review. This preserves
partial results if the turn limit interrupts the run (a recovery step reads
this file); it does NOT substitute for outputting the full review as your
final response.
## Workflow Steps
@@ -276,6 +399,9 @@ in isolation may be a critical bug when you consider the full call chain.
- **Callee side effects**: Does the change call functions that mutate shared
state (counters, seqnos, metadata)? Are the mutations correct for the new
calling context? (Use the callee-chain analysis from context.md)
- Apply the DB Mutex Review Perspective above to locking contracts, unlocked
object lifetimes, stale-state validation, lock ordering, and snapshot
boundaries.
#### Agent: Cross-Component & Adversarial Reviewer
@@ -379,6 +505,9 @@ Write findings to `findings-caller-audit.md`.
- Loop optimization opportunities
- Branch prediction (LIKELY/UNLIKELY)
- Zero-overhead design verification
- Apply the DB Mutex Review Perspective above, with special focus on transitive
CPU and I/O, before/after cost, critical-section hold time, and benchmark
evidence
#### Agent: API & Compatibility Reviewer
- Public API backwards compatibility
@@ -386,8 +515,8 @@ Write findings to `findings-caller-audit.md`.
- Documentation completeness
- Deprecation policy compliance
- Forward compatibility (struct extensibility)
- **Behavioral compatibility**: Do changed semantics (e.g., kOutOfBound vs
kUnknown) break implicit contracts with callers?
- Apply the Behavioral Compatibility Review Perspective above to changes in
observable semantics and implicit caller contracts.
#### Agent: Serialization & Deserialization Reviewer
- Format correctness and versioning
@@ -466,7 +595,9 @@ Team lead synthesizes the debate into a consensus document:
- Include detailed bug analysis (root cause, vulnerable code paths)
- Include suggested fixes
- Note which findings were debated and the outcome
- Write the complete review to review-findings.md and output as response
- Output the complete review as your final response text (this becomes the PR
comment verbatim), and also overwrite review-findings.md with the identical
content as a recovery backstop
**Final report quality rules:**
- The final report must be CLEAN and POLISHED. No stream-of-consciousness,
@@ -483,9 +614,10 @@ Team lead synthesizes the debate into a consensus document:
**REQUIRED output structure (so the PR page stays scrollable):**
The final response (and contents of `review-findings.md`) MUST follow this
exact structure. The summary appears first so reviewers can see HIGH findings
at a glance; everything else is hidden behind a `<details>` block.
Your final response — which becomes the PR comment verbatim — (and the
identical contents of `review-findings.md`) MUST follow this exact structure.
The summary appears first so reviewers can see HIGH findings at a glance;
everything else is hidden behind a `<details>` block.
```markdown
## Summary
@@ -533,6 +665,9 @@ Rules for this structure:
- Do NOT nest a `<details>` inside another `<details>`.
- Do NOT add any text after the closing `</details>` — the comment-builder
appends its own footer.
- Your final response MUST be the review itself in this structure — never a
meta-summary like "the review has been written to review-findings.md". The
PR comment is generated verbatim from your final response.
- If the review is partial (recovery path), still produce the summary block
first, and put whatever was salvaged inside the `<details>` block.
@@ -556,6 +691,8 @@ Rules for this structure:
- [ ] Multi-component interactions (compaction, recovery, snapshots, iterators)
- [ ] Configuration dependencies and unexpected option combinations
- [ ] Potential pitfalls from caller-chain analysis
- [ ] DB-mutex reachability checked through transitive callees; affected lock
and unlock boundaries documented
### Review Phase (verified by agents)
- [ ] Database semantics preserved (snapshot isolation, key ordering)
@@ -569,11 +706,20 @@ Rules for this structure:
- [ ] Code follows RocksDB style conventions
- [ ] Behavioral contracts with upstream callers preserved
- [ ] All callers enumerated with parameter range table
- [ ] Before/after CPU and I/O under the DB mutex compared, with call frequency,
scaling, hidden callee work, and worst-case cost included
- [ ] No significant DB-mutex-held work regression; unavoidable work is highly
optimized, bounded, and supported by measurements when material
## Output Structure
Write all review artifacts to the working directory root:
- `review-findings.md` — Incremental findings (appended after each phase),
then replaced with the final synthesized review at the end
Your **final response text is the primary output** — it becomes the PR comment
verbatim. The files below are working artifacts and recovery backstops only;
they are NOT a substitute for outputting the full review in your final response.
Write these artifacts to the working directory root:
- `review-findings.md` — Recovery backstop: incremental findings appended after
each phase, overwritten with the final synthesized review at the end. Used
only if the turn limit interrupts the run.
- `context.md` — Codebase context (call chains, invariants)
- `findings-*.md` — Per-agent findings (design, correctness, cross-component,
invariant-adversary, caller-audit, performance, api, serialization, tests)
-104
View File
@@ -1,104 +0,0 @@
# Code Review Skill
This document defines the methodology for performing thorough, multi-perspective
code reviews on RocksDB changes. It is used both by CI (GitHub Actions) and
local review workflows.
## Prerequisites
Before starting a review:
- Read `CLAUDE.md` in the repository root for project-specific guidelines
- Read any files in `claude_md/` for architecture context
- Identify the commit/diff/PR to review and parse the changed files
## Review Methodology
Conduct the review from multiple perspectives, then synthesize findings into a
single report. Each perspective catches different classes of bugs.
### Perspective 1: Codebase Context & Call-Chain Analysis
Before reviewing the diff itself, build deep context:
- For each changed function/method, trace the call chain UPWARD 3-5 levels.
Who consumes the changed behavior? What invariants do callers rely on?
- Trace DOWNWARD into callees. What side effects do they have? What shared
state do they mutate (counters, sequence numbers, flags, metadata)?
- Identify the "sibling" or "reference" implementation that handles the same
scenario the standard way. Compare behaviors.
- For data written to shared structures (memtable, SST block, cache entry),
find ALL readers and verify their visibility rules match the writer.
### Perspective 2: Correctness & Edge Cases
- Thread safety and concurrency (lock ordering, data races)
- Error handling and Status propagation
- Edge cases: empty inputs, overflow, boundary conditions
- Data corruption scenarios
- Behavioral contract changes: do return value semantics match callers?
### Perspective 3: Cross-Component & Adversarial Analysis
Check the change in ALL execution contexts:
| Context | Key difference |
|---------|---------------|
| WritePreparedTxnDB | read_callback_ controls visibility |
| ReadOnly DB / SecondaryInstance | No mutable memtable |
| CompactionService / Remote compaction | Different process |
| User-defined timestamps | Extra key comparison dimension |
| MemPurge | Memtable-to-memtable path |
| BlobDB | Values in blob files, not inline |
| Old snapshots | Seqno far behind current |
| Concurrent writers | Lock-free vs locked paths |
| FIFO / Universal compaction | Different invariants |
| Prefix seek / total order seek | Different iterator behavior |
When the change claims a property ("logically redundant", "safe because X"),
systematically try to break it: list preconditions, construct counterexamples.
### Perspective 4: Performance
RocksDB is a high-performance storage engine.
- Memory allocation on hot paths
- Unnecessary copies (prefer move semantics, Slice, string_view)
- Cache efficiency and data locality
- Loop optimization, branch prediction (LIKELY/UNLIKELY)
### Perspective 5: API, Compatibility & Testing
- Public API backwards compatibility
- Serialization format correctness and versioning
- Test coverage for edge cases, failure modes, and system-level interactions
## How to Explore the Codebase
Use available tools to explore deeply — do NOT just review the diff in isolation.
The most critical bugs hide at component boundaries.
- Read the header files for changed implementations
- Search for callers of changed functions (3-5 levels up)
- Read existing tests to understand guaranteed behaviors
- Check related implementations for conventions and patterns
## Output Format
### Summary
Brief overall assessment (1-2 sentences).
### Issues Found
Categorize by severity:
- :red_circle: **Critical**: Must fix (correctness, data corruption, security)
- :yellow_circle: **Suggestion**: Should consider (performance, edge cases)
- :green_circle: **Nitpick**: Minor style issues
For each issue include:
- File and line reference
- Which review perspective found it
- Description with root cause analysis
- Suggested fix
### Cross-Component Analysis
Execution context checks and assumption stress-test results.
### Positive Observations
Good patterns, clever optimizations, or improvements.
+67
View File
@@ -0,0 +1,67 @@
# Manual (Local) Code Review — Lightweight Wrapper
## What this is
A resource-light, **single-reviewer** variant of `claude_md/ci_review_prompt.md`
for running a review by hand in a local Claude Code / Devmate session. It reuses
the full CI review *methodology* but drops the multi-agent orchestration and the
CI-only file-persistence machinery, to approximate the heavyweight result at a
fraction of the cost.
**Scope: LOCAL manual review only. This file is NOT used by CI.** CI uses
`claude_md/ci_review_prompt.md`.
## How to use
Read `claude_md/ci_review_prompt.md` for the complete methodology — the analysis
techniques (caller-chain, callee side-effects, cross-component data consumers,
alternative execution contexts, assumption stress-testing), the per-perspective
reviewer instructions, the review checklist, and the anti-patterns table.
Then apply that methodology with the OVERRIDES below. **Where this file
conflicts with `ci_review_prompt.md`, this file wins.**
## Overrides (this is what makes it lightweight)
### 1. Single reviewer — no team, no subagents
- Do NOT create a team, spawn subagents, or run parallel/background agents.
- Do NOT run the round-robin Debate phase or the Consensus phase.
- Instead, YOU personally apply each reviewer role from `ci_review_prompt.md`
(Design, Correctness, Cross-Component & Adversarial, Invariant Adversary,
Caller-Context, Performance, API/Compat, Serialization, Test Coverage) as
sequential *lenses* in one pass. Spend a few focused minutes per lens. Skip
lenses that plainly don't apply (e.g., Serialization when there is no on-disk
or wire-format change) and note the skip in one line.
### 2. No intermediate files
- Do NOT write `context.md`, `findings-*.md`, `consensus.md`, or
`review-findings.md`. Keep working notes in your reasoning, not on disk.
- Rationale: those files exist only so CI can recover partial results when an
ephemeral runner hits the turn limit — a local session has neither problem.
`ci_review_prompt.md` already outputs the review as its final response, so
skipping the file loses nothing here.
### 3. Right-size the effort
- Do the Codebase Context phase, but proportionally: trace the 2-3 most
load-bearing call chains and the highest-risk cross-component data consumers,
rather than exhaustively enumerating every caller 3-5 levels up.
- Prioritize the highest-signal lenses for RocksDB: **Correctness**,
**Cross-Component & Adversarial**, and **Assumption Stress-Testing**. These
catch the bugs that matter most; give them the most attention.
- Escalation clause: if a thread looks dangerous, go deep on that specific
thread even though the overall pass is modest. Modest is the default, not a cap.
### 4. Identify the review target locally
- Unlike CI, the diff is NOT pre-pasted into this prompt. Determine what to
review from the user's request against the local worktree — e.g. the current
commit (`git show`), uncommitted changes (`git diff` / `git diff --staged`),
a named commit range, or a PR — and read the changed files plus their
surrounding context directly from the repo. When in doubt, include all changes
not yet upstream, as in
`git diff "$(git merge-base upstream/main HEAD 2> /dev/null || git merge-base origin/main HEAD 2> /dev/null)"`
plus any untracked files (`git status`) that seem relevant.
## Reminder
The goal is a thorough *single* reviewer, not a 9-agent debate: depth where it
matters, brevity everywhere else.
+106
View File
@@ -0,0 +1,106 @@
# RocksDB Diff and Pull Request Summary Guidance
This guidance applies to both internal Phabricator diff summaries and external
GitHub pull request summaries for changes under `fbcode/internal_repo_rocksdb/`.
## Goal
Write each diff or pull request summary for a reviewer who has not read the
patch. The summary must explain the behavioral story and the evidence for it,
not merely inventory the edited files or symbols.
Every new or updated diff or pull request summary must clearly cover:
1. Why the change is required.
2. What the previous behavior was, including the failure mode or limitation.
3. What the new behavior is.
4. What changed in the implementation.
5. Safety boundaries, compatibility, and unsupported or intentionally deferred
cases.
6. Tests performed and known limitations of the validation.
Use explicit headings for a broad, subtle, or high-risk change. A small,
focused change may combine these points into a few paragraphs, but none may be
left implicit when it affects a review decision.
## What Good RocksDB Summaries Make Concrete
- Name the exact conditions that activate a bug or behavior: relevant options,
call ordering, storage mode, concurrency mode, file format, platform, or
workload shape. Also state important cases that are not affected.
- Explain the causal chain from the old behavior to its externally observable
result. For correctness fixes, distinguish a symptom from the underlying
invariant violation.
- Describe the new contract before implementation mechanics. Then identify the
essential implementation choices and why they are sufficient.
- Call out API, ABI, serialized-format, behavioral, and performance
compatibility where relevant. For additive persisted fields, state how older
data is interpreted and whether older readers remain supported.
- State safety boundaries precisely: defaults, feature gates, fallback paths,
ownership and lifetime constraints, memory-ordering assumptions, and cases
deliberately postponed to a follow-up.
- Support performance claims with a baseline, workload and configuration,
measurement method, and results. Report meaningful tradeoffs, not only the
winning metric.
- Make the test plan evidence-based. Name the tests and configurations, say
what behavior each proves, and report failures, timeouts, untested platforms,
or stale pre-rebase results honestly. For a regression test, say how it fails
without the fix when that was verified.
- Separate incidental cleanup or a pre-existing test fix from the primary
change, and explain why it belongs in the diff.
Avoid vague claims such as "improves performance," "fixes an issue," or "tests
pass." Avoid copying the patch into prose, exhaustive file-by-file narration,
raw command output, and claims broader than the evidence.
## Suggested Structure
Adapt this structure to the change; do not keep empty or irrelevant sections.
```text
Summary:
Why the change is required:
<User or system impact, and why the current behavior is inadequate.>
Previous behavior:
<Old contract or mechanism, triggering conditions, and observable result.>
New behavior:
<New contract and externally visible effect.>
Implementation:
<Essential design and code changes; omit routine edit-by-edit narration.>
Safety, compatibility, and limitations:
<Defaults, unaffected paths, compatibility, unsupported cases, and follow-ups.>
Test Plan:
<Exact tests/benchmarks, configurations, what they demonstrate, and gaps.>
```
For a focused bug fix, a cohesive narrative can be clearer than six headings:
problem and root cause, trigger and unaffected cases, fix, then quantified or
counterfactual validation. For a multi-part change, labeled bullets such as
`Fix`, `Add`, and `Change` can make the implementation and review surface easy
to scan.
## Reference Pull Request Summaries
- [PR #15216](https://github.com/meta/rocksdb/pull/15216) is a strong
focused bug-fix example. It traces stale iterator state to readahead behavior
and dropped keys, identifies the exact prerequisites and unaffected mmap
case, gives before/after measurements, and explains how the regression test
fails when the fix is reverted.
- [PR #15218](https://github.com/meta/rocksdb/pull/15218) is a concise
example of explicit `Why`, `Previous behavior`, `New behavior`,
`Implementation`, and `Safety boundaries and unsupported cases` sections.
- Peter Dillinger's [PR #15231](https://github.com/meta/rocksdb/pull/15231)
is a strong recent multi-part example. Its `Fix`/`Add`/`Change` bullets
separate the behaviors, its compatibility sentence explains how old files
read back, and its test plan maps each new persisted property to concrete
coverage while explaining an adjacent test adjustment.
Use these pull requests as models for reasoning, specificity, and evidence. Do
not copy their length or headings mechanically; scale the summary to the risk
and complexity of the change.
-4
View File
@@ -44,10 +44,6 @@ if(@WITH_NUMA@)
find_dependency(NUMA)
endif()
if(@WITH_TBB@)
find_dependency(TBB)
endif()
find_dependency(Threads)
include("${CMAKE_CURRENT_LIST_DIR}/RocksDBTargets.cmake")
-33
View File
@@ -1,33 +0,0 @@
# - Find TBB
# Find the Thread Building Blocks library and includes
#
# TBB_INCLUDE_DIRS - where to find tbb.h, etc.
# TBB_LIBRARIES - List of libraries when using TBB.
# TBB_FOUND - True if TBB found.
if(NOT DEFINED TBB_ROOT_DIR)
set(TBB_ROOT_DIR "$ENV{TBBROOT}")
endif()
find_path(TBB_INCLUDE_DIRS
NAMES tbb/tbb.h
HINTS ${TBB_ROOT_DIR}/include)
find_library(TBB_LIBRARIES
NAMES tbb
HINTS ${TBB_ROOT_DIR}/lib ENV LIBRARY_PATH)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(TBB DEFAULT_MSG TBB_LIBRARIES TBB_INCLUDE_DIRS)
mark_as_advanced(
TBB_LIBRARIES
TBB_INCLUDE_DIRS)
if(TBB_FOUND AND NOT (TARGET TBB::TBB))
add_library (TBB::TBB UNKNOWN IMPORTED)
set_target_properties(TBB::TBB
PROPERTIES
IMPORTED_LOCATION ${TBB_LIBRARIES}
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS})
endif()
Loaded 100 of 1077 files, more files were not shown because too many files have changed in this diff. Show more