Compare commits

...
163 Commits
Author SHA1 Message Date
Jay Huh 45f7473909 set ignore_unknown_options when parsing options (#13443)
Summary:
In case the primary host has a new option added which isn't available in the remote worker yet, the remote compaction currently fails. In most cases, these new options are not relevant to the remote compaction and the worker should be able to move on by ignoring it.

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

Test Plan: Verified internally in Meta Infra.

Reviewed By: anand1976

Differential Revision: D70744359

Pulled By: jaykorean

fbshipit-source-id: eb6a388c2358a7f8089f2e35a378b7017b9e03f3
2025-03-06 17:32:20 -08:00
anand76 884f57ccc1 Fix version numbering from 10.1.0 to 10.0.1 2025-03-06 09:53:11 -08:00
anand76 6fff8f6849 Update HISTORY and version to 10.1.0 2025-03-06 09:51:05 -08:00
Jay Huh 28788c6337 Introduce kAborted Status (#13438)
Summary:
If compaction job needs to be aborted inside `Schedule()` or `Wait()` today (e.g. Primary host is shutting down), the only two options are the following
- Handle it as failure by returning `CompactionServiceJobStatus::kFailure`
- Return `CompactionServiceJobStatus::kUseLocal` and let the compaction move on locally and eventually succeed or fail depending on the timing

In this PR, we are introducing a new status, `CompactionServiceJobStatus::kAborted`,  so that the implementation of `Schedule()` and `Wait()` can return it. Just like how `CompactionServiceJobStatus::kFailure` is handled, compaction will not move on and fail, but the status will be returned as `Status::Aborted()` instead of `Status::Incomplete()`

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

Test Plan:
Unit Test added
```
 ./compaction_service_test --gtest_filter="*CompactionServiceTest.AbortedWhileWait*"
```

Reviewed By: anand1976, hx235

Differential Revision: D70655355

Pulled By: jaykorean

fbshipit-source-id: 22614ce9c7455cda649b15465625edc93978fe11
2025-03-06 09:48:17 -08:00
Jay Huh b09612881c Add Logging for debugging InputFileCheck Failure (#13427)
Summary:
Add detailed log for debugging purpose

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

Test Plan: CI

Reviewed By: cbi42, hx235

Differential Revision: D70274613

Pulled By: jaykorean

fbshipit-source-id: de4bc61853136b923aa786717e7979be8886b9bd
2025-03-06 09:48:05 -08:00
anand76 513c8d2f16 Initial implementation of ExternalTableBuilder (#13434)
Summary:
This PR adds the ability to use an ExternalTableBuilder through the SstFileWriter to create external tables. This is a counterpart to https://github.com/facebook/rocksdb/issues/13401 , which adds the ExternalTableReader. The support for external tables is confined to ingestion only DBs, with external table files ingested into the bottommost level only. https://github.com/facebook/rocksdb/issues/13431 enforces ingestion only DBs by adding a disallow_memtable_writes column family option.

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

Test Plan: New unit tests in table_test.cc

Reviewed By: pdillinger

Differential Revision: D70532054

Pulled By: anand1976

fbshipit-source-id: a837487eadfabed9627a0eceb403bfc5fc2c427c
2025-03-06 09:47:30 -08:00
anand76 82ada7502d Add opaque options in ReadOptions for external tables (#13436)
Summary:
Add an unordered_map of name/value pairs in ReadOptions::property_bag, similar to IOOptions::property_bag. It allows users to pass through some custom options to an external table.

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

Reviewed By: jaykorean

Differential Revision: D70649609

Pulled By: anand1976

fbshipit-source-id: 9b14806a9f3599b861827bd4ae6e948861edc51a
2025-03-06 09:47:16 -08:00
Peter Dillinger 20b93e3bf7 New CF option disallow_memtable_writes (#13431)
Summary:
... to better support "ingestion only" column families such as those using an external file reader as in https://github.com/facebook/rocksdb/issues/13401.

It would be possible to implement this by getting rid of the memtable for that CF, but it quickly because clear that such an approach would need to update a lot of places to deal with such a possibility. And we already have logic to optimize reads when a memtable is empty. We put a vector memtable in place to minimize overheads of an empty memtable.

There are three layers of defense against writes to the memtable:
* WriteBatch ops to a disallowed CF will fail immediately, without waiting for Write(). For this check to work, we need a ColumnFamilyHandle and because of that, we don't support disallow_memtable_writes on the default column family.
* MemtableInserter will reject writes to disallowed CFs. This is needed to protect re-open with disallow when there are existing writes in a WAL.
* The placeholder memtable is marked immutable. This will cause an assertion failure on attempt to write, such as in case of bug or regression.

Suggested follow-up:
* Remove the limitation on using the option with the default column family, perhaps by solving https://github.com/facebook/rocksdb/issues/13429 more generally or perhaps with some specific check before the first memtable write of the batch (but potential CPU overhead for such a check - there's likely optimization opportunities around ColumnFamilyMemTables).

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

Test Plan:
unit tests added

Performance: A db_bench call designed to realistically focus on the CPU cost of writes:

```
./db_bench -db=/dev/shm/dbbench1 --benchmarks=fillrandom -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=1000 -fifo_compaction_allow_compaction=0 -num_column_families=20 -disable_wal -write_buffer_size=1234000
```

Running before & after tests at the same time on the same machine, 40 iterations each, average ops/s, DEBUG_LEVEL=0, remove slowest run of each:
Before: 772466
After: 773785 (0.2% faster)

Likely within the noise, as if there was any change, we would expect a slight regression.

Reviewed By: anand1976

Differential Revision: D70495936

Pulled By: pdillinger

fbshipit-source-id: 306f7e737f87c1fbb52c5805f3cadb6e8ced9b40
2025-03-06 09:46:56 -08:00
Changyu Bi c1f63e16f0 Add missing txn updates to HISTORY.md 2025-02-22 17:44:19 -08:00
Changyu Bi ba27c3a0f9 Update HISTORY.md for 10.0 release 2025-02-21 20:29:21 -08:00
Changyu Bi 4c975a7c22 Disable flaky unit test RoundRobinSubcompactionsAgainstPressureToken (#13416)
Summary:
The test is [flaky](https://github.com/facebook/rocksdb/actions/runs/13417174378/job/37480755623?fbclid=IwZXh0bgNhZW0CMTEAAR2pj4E1ua6zMxz4FxnPAPLIz011t1ddjaWPbmFlldfSG7dZGjWGVy-mDkg_aem_40kU2iCmcN93WsmzLZxGsA) and my previous [fix](https://github.com/facebook/rocksdb/pull/13347) did not seem to work. It's likely a test set up issue and disable the test for now since RoundRobin compaction style is not used to reduce some test failure noise.

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

Test Plan: CI

Reviewed By: hx235

Differential Revision: D70002097

Pulled By: cbi42

fbshipit-source-id: afe0f56363501dab2c9dc297bfbe0dff0ac6aeb3
2025-02-21 12:55:29 -08:00
Maciej Szeszko 5139ff5c29 Conditional check reordering (#13415)
Summary:
This change is addressing a valid concern raised in https://github.com/facebook/rocksdb/pull/13408#discussion_r1966000661.

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

Test Plan: Existing test collateral.

Reviewed By: cbi42

Differential Revision: D69999071

Pulled By: mszeszko-meta

fbshipit-source-id: 5ebb195b2b83701e06c33bfcb19c57d9ac1c1dc6
2025-02-21 12:42:14 -08:00
Changyu Bi d7aea6955c Fix stress test DB verification methods (#13409)
Summary:
update VerifyDB() to respect user specified flags when choosing verification method.

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

Test Plan: existing CI.

Reviewed By: hx235

Differential Revision: D69885644

Pulled By: cbi42

fbshipit-source-id: bbaa931cece3525f00d775639ec7b63ff0101d94
2025-02-21 10:50:01 -08:00
Changyu Bi 3c2c2689b9 Merge support in WBWIMemTable (#13410)
Summary:
added merge support for WBWIMemTable. Most of the preparation work is done in https://github.com/facebook/rocksdb/issues/13387 and https://github.com/facebook/rocksdb/issues/13400. The main code change to support merge is in wbwi_memtable.cc to support reading the Merge value type. The rest of the changes are mostly comment change and tests.

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

Test Plan:
- new unit test
- ran `python3 ./tools/db_crashtest.py --txn blackbox  --txn_write_policy=0 --commit_bypass_memtable_one_in=100 --test_batches_snapshots=0 --use_merge=1` for several runs.

Reviewed By: jowlyzhang

Differential Revision: D69885868

Pulled By: cbi42

fbshipit-source-id: b127d95a3027dc35910f6e5d65f3409ba27e2b6b
2025-02-20 20:21:45 -08:00
Maciej Szeszko 1e1c199316 Fix dbstress run - attempt 1 (#13408)
Summary:
This PR attempts to fix the **dbstress failures** post https://github.com/facebook/rocksdb/pull/13354. There are at least 2 high level categories of errors: 1) likely caused by wide-scope snapshot initialization ([issue found by Peter](https://github.com/facebook/rocksdb/pull/13354#discussion_r1960177118)), 2) lack of proper error propagation. Wrt 2), part of the problem is a real miss (we should condition auto refresh on `status().ok()` after calling to `Next` / `Prev`), but another part - [failure in propagating dbstress-injected read error](https://github.com/facebook/rocksdb/pull/13354#discussion_r1960913871) in file deletion is expected and should not be asserted on in dbstress.

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

Test Plan:
Confirmed there are no more errors after running sandcastle crashtest for each of the failing flavors:

```hcl
https://www.internalfb.com/sandcastle/workflow/252201579138859344
https://www.internalfb.com/sandcastle/workflow/3233584532458171962
https://www.internalfb.com/sandcastle/workflow/1283525893806766134
https://www.internalfb.com/sandcastle/workflow/2796735368603351293
https://www.internalfb.com/sandcastle/workflow/3792030886252148966
https://www.internalfb.com/sandcastle/workflow/67553994428973733
https://www.internalfb.com/sandcastle/workflow/3886606478427208295
https://www.internalfb.com/sandcastle/workflow/1684346260642682928
https://www.internalfb.com/sandcastle/workflow/4197354852715406516
https://www.internalfb.com/sandcastle/workflow/535928355663233170
https://www.internalfb.com/sandcastle/workflow/3409224917925569737
```

Reviewed By: cbi42

Differential Revision: D69869766

Pulled By: mszeszko-meta

fbshipit-source-id: 7a5b121218fb1dc0a37887d6fe2a5c07e2b894cf
2025-02-20 11:07:01 -08:00
Peter Dillinger 836e88ab7a Add test for memtable bloom filter with WriteBufferManager (#13398)
Summary:
... to ensure proper cache charging. However, this is a somewhat hazardous combination if there are many CFs and could be the target of future work.

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

Test Plan: this is the test

Reviewed By: hx235

Differential Revision: D69619977

Pulled By: pdillinger

fbshipit-source-id: 9841768584e4688d8fdd0258f3ba9608b67408e5
2025-02-20 10:16:12 -08:00
Sarang Masti 129b7791f9 Bugfix: Ensure statuses are initialized with OK() in SSTFileReader::MultiGet (#13411)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13411

We should intialize statuses with OK rather than IOError to correctly handle cases
like NotFound due to bloom filter. In case of IOError status would be updated
appropriately by the reader

Reviewed By: anand1976

Differential Revision: D69886976

fbshipit-source-id: 92b130168f23633224ff4153bfe46a7d86482b90
2025-02-19 19:38:53 -08:00
Changyu Bi 36838bbf51 Update sequence number assignment method in WBWIMemTable (#13400)
Summary:
This is a preparation for supporting merge in `WBWIMemTable`. This PR updates the sequence number assignment method so that it allows efficient and simple assignment when there are multiple entries with the same user key. This can happen when the WBWI contains Merge operations. This assignment relies on tracking the number of updates issued for each key in each WBWI entry (`WriteBatchIndexEntry::update_count`). Some refactoring is done in WBWI to remove `last_entry_offset` as part of the WBWI state which I find it harder to use correctly.

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

Test Plan: updated unit tests to check that update count is tracked correctly and WBWIMemTable is assigning sequence number as expected.

Reviewed By: pdillinger

Differential Revision: D69666462

Pulled By: cbi42

fbshipit-source-id: 9b18291825017a67c4da3318e8a556aa2971326b
2025-02-19 12:35:56 -08:00
anand76 920d25e34e Initial version of an external table reader interface (#13401)
Summary:
This PR introduces an interface to plug in an external table file reader into RocksDB. The external table reader may support custom file formats that might work better for a specific use case compared to RocksDB native formats. This initial version allows the external table file to be loaded and queried using an `SstFileReader`. In the near future, we will allow it to be used with a limited RocksDB instance that allows bulkload but not live writes.

The model of a DB using an external table reader is a read only database allowing bulkload and atomic replace in the bottommost level only. Live writes, if supported in the future, are expected to use block based table files in higher levels. Tombstones, merge operands, and non-zero sequence numbers are expected to be present only in non-bottommost levels. External table files are assumed to have only Puts, and all keys implicitly have sequence number 0.

TODO (in future PRs) -
1. Add support for external file ingestion, with safety mechanisms to prevent accidental writes
2. Add support for atomic column family replace
3. Allow custom table file extensions
4. Add a TableBuilder interface for use with `SstFileWriter`

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

Reviewed By: pdillinger

Differential Revision: D69689351

Pulled By: anand1976

fbshipit-source-id: c5d5b92d56fd4d0fc43a77c4ceb0463d4f479bda
2025-02-19 09:52:08 -08:00
Sarang Masti a0edca32cf MultiGet support in SstReader (#13403)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13403

Add MultiGet support in SstReader. Today we only have iteration support and this change
also adds MultiGet support to SstFileReader if some application wants to use it.

Reviewed By: anand1976

Differential Revision: D69514499

fbshipit-source-id: 20e85a4bd13a3a9f45dacb223c1a4541fb87f561
2025-02-19 08:09:36 -08:00
Hui Xiao 9be5e15a26 Disable auto_refresh_iterator_with_snapshot temporarily in stress test (#13402)
Summary:
Context/Summary: recent crash test failures seem to find issues with recently added auto_refresh_iterator_with_snapshot and prefix/scan, injected read. For now, let's disable the auto_refresh_iterator_with_snapshot.

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

Test Plan: monitor CI

Reviewed By: mszeszko-meta

Differential Revision: D69677731

Pulled By: hx235

fbshipit-source-id: eea6630e96d53fba8dbf3877a49819690dfab2f6
2025-02-18 11:51:27 -08:00
Changyu Bi 4b5f0a4fcc Fix GetMergeOperands() in ReadOnly and SecondaryDB (#13396)
Summary:
Noticed that the `do_merge` parameter is not properly set while working on memtable code.

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

Test Plan: updated unit test for the read-only db case.

Reviewed By: jaykorean

Differential Revision: D69505015

Pulled By: cbi42

fbshipit-source-id: d4c64ca7bba31fe26aa41a29cbc55835d9f1f116
2025-02-18 11:01:19 -08:00
Hui Xiao 7069691f7e Disable track_and_verify_wals completely temporarily (#13405)
Summary:
**Context/Summary:**

https://github.com/facebook/rocksdb/pull/13263 and https://github.com/facebook/rocksdb/pull/13360 disabled `track_and_verify_wals` with some injection under TXN temporarily but recent stress tests has found more issues this feature surfaced even with the previous disabling. Disabling the feature **completely** now for stabilizing CI while debugging.

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

Test Plan: Monitor CI

Reviewed By: cbi42

Differential Revision: D69759276

Pulled By: hx235

fbshipit-source-id: 501a3561acb9daa834f874095f9a66ae6ae5aa42
2025-02-18 09:39:00 -08:00
Hui Xiao 6aacec07dc Call Clean() on JobContext before destruction in UT (#13406)
Summary:
**Context/Summary:**
It's [documented (https://github.com/facebook/rocksdb/blob/affcad0cc997958e93bc560202ed107c80d00395/db/job_context.h#L230) that `// For non-empty JobContext Clean() has to be called at least once before before destruction`. This is violated in a UT accidentally so causing the assertion failure `assert(logs_to_free.size() == 0);` in` ~JobContext`. This PR is to fix it.

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

Test Plan: Monitor for future UT assertion failure in `TEST_F(DBWALTest, FullPurgePreservesRecycledLog) `

Reviewed By: cbi42

Differential Revision: D69759725

Pulled By: hx235

fbshipit-source-id: dd1617b370a2c69daba657287dcf258542f92ef5
2025-02-18 09:37:03 -08:00
Hui Xiao affcad0cc9 Fix corrupted wal number when predecessor wal corrupts + minor cleanup (#13359)
Summary:
**Context/Summary:**

https://github.com/facebook/rocksdb/commit/02b4197544f758bdf84d80fe9319238611848c48 recently added the ability to detect WAL hole presents in the predecessor WAL. It forgot to update the corrupted wal number to point to the predecessor WAL in that corruption case. This PR fixed it.

As a bonus, this PR also (1) fixed the `FragmentBufferedReader()` constructor API to expose less parameters as they are never explicitly passed in in the codebase (2) a INFO log wording (3) a parameter naming typo (4) the reporter naming

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

Test Plan:
1. Manual printing to ensure the corrupted wal number is set to the right number
2. Existing UTs

Reviewed By: jowlyzhang

Differential Revision: D69068089

Pulled By: hx235

fbshipit-source-id: f7f8a887cded2d3a26cf9982f5d1d1ab6a78e9e1
2025-02-13 21:49:51 -08:00
Hui Xiao f6b2cdd350 Disable secondary test with sst truncation deletion; API clarification (#13395)
Summary:
**Context/Summary:**
Secondary DB relies on open file descriptor of the shared SST file in primary DB to continue being able to read the file even if that file is deleted in the primary DB. However, this won't work if the file is truncated instead of deleted, which triggers an "truncated block read" corruption in stress test on secondary db reads. Truncation can happen if RocksDB implementation of SSTFileManager and `bytes_max_delete_chunk>0` are used. This PR is to disable such testing combination in stress test and clarify the related API.

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

Test Plan:
- Manually repro-ed with below UT. I'm in favor of not including this UT in the codebase as it should be self-evident from the API comment now about the incompatiblity. Secondary DB is in a direction of being replaced by Follower so we should minimize edge-case tests for code with no functional change for a to-be-replaced functionality.
```
TEST_F(DBSecondaryTest, IncompatibleWithPrimarySSTTruncation) {
  Options options;
  options.env = env_;
  options.disable_auto_compactions = true;
  options.sst_file_manager.reset(NewSstFileManager(
      env_, nullptr /*fs*/, "" /*trash_dir*/, 2024000 /*rate_bytes_per_sec*/,
      true /*delete_existing_trash*/, nullptr /*status*/,
      0.25 /*max_trash_db_ratio*/, 1129 /*bytes_max_delete_chunk*/));
  Reopen(options);

  ASSERT_OK(Put("key1", "old_value"));
  ASSERT_OK(Put("key2", "old_value"));
  ASSERT_OK(Flush());
  ASSERT_OK(Put("key1", "new_value"));
  ASSERT_OK(Put("key3", "new_value"));
  ASSERT_OK(Flush());

  Options options1;
  options1.env = env_;
  options1.max_open_files = -1;
  Reopen(options);
  OpenSecondary(options1);
  ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());

  ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
      "DeleteScheduler::DeleteTrashFile:Fsync", [&](void*) {
        std::string value;
        Status s = db_secondary_->Get(ReadOptions(), "key2", &value);
        assert(s.IsCorruption());
        assert(s.ToString().find("truncated block read") !=
            std::string::npos);
      });
  ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();

  ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));

  ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
```
- Monitor future stress test

Reviewed By: jowlyzhang

Differential Revision: D69499694

Pulled By: hx235

fbshipit-source-id: 57525b9841897f42aecb758a4d3dd3589367dcd9
2025-02-12 11:00:36 -08:00
Maciej Szeszko 8234d67e5a Auto refresh iterator with snapshot (#13354)
Summary:
# Problem
Once opened, iterator will preserve its' respective RocksDB snapshot for read consistency. Unless explicitly `Refresh'ed`, the iterator will hold on to the `Init`-time assigned `SuperVersion` throughout its lifetime. As time goes by, this might result in artificially long holdup of the obsolete memtables (_potentially_ referenced by that superversion alone) consequently limiting the supply of the reclaimable memory on the DB instance. This behavior proved to be especially problematic in case of _logical_ backups (outside of RocksDB `BackupEngine`).

# Solution
Building on top of the `Refresh(const Snapshot* snapshot)` API introduced in https://github.com/facebook/rocksdb/pull/10594, we're adding a new `ReadOptions` opt-in knob that (when enabled) will instruct the iterator to automatically refresh itself to the latest superversion - all that while retaining the originally assigned, explicit snapshot (supplied in `read_options.snapshot` at the time of iterator creation) for consistency. To ensure minimal performance overhead we're leveraging relaxed atomic for superversion freshness lookups.

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

Test Plan:
**Correctness:** New test to demonstrate the auto refresh behavior in contrast to legacy iterator: `./db_iterator_test --gtest_filter=*AutoRefreshIterator*`.

**Stress testing:** We're adding command line parameter controlling the feature and hooking it up to as many iterator use cases in `db_stress` as we reasonably can with random feature on/off configuration in db_crashtest.py.

# Benchmarking

The goal of this benchmark is to validate that throughput did not regress substantially. Benchmark was run on optimized build, 3-5 times for each respective category or till convergence. In addition, we configured aggressive threshold of 1 second for new `Superversion` creation. Experiments have been run 'in parallel' (at the same time) on separate db instances within a single host to evenly spread the potential adverse impact of noisy neighbor activities. Host specs [1].

**TLDR;** Baseline & new solution are practically indistinguishable from performance standpoint. Difference (positive or negative) in throughput relative to the baseline, if any, is no more than 1-2%.

**Snapshot initialization approach:**

This feature is only effective on iterators with well-defined `snapshot` passed via `ReadOptions` config. We modified the existing `db_bench` program to reflect that constraint. However, it quickly turned out that the actual `Snapshot*` initialization is quite expensive. Especially in case of 'tiny scans' (100 rows) contributing as much as 25-35 microseconds, which is ~20-30% of the average per/op latency unintentionally masking _potentially_ adverse performance impact of this change. As a result, we ended up creating a single, explicit 'global' `Snapshot*` for all the future scans _before_ running multiple experiments en masse. This is also a valuable data point for us to keep in mind in case of any future discussions about taking implicit snapshots - now we know what the lower bound cost could be.

## "DB in memory" benchmark

**DB Setup**

1. Allow a single memtable to grow large enough (~572MB) to fit in all the rows. Upon shutdown all the rows will be flushed to the WAL file (inspected `000004.log` file is 541MB in size).

```
./db_bench -db=/tmp/testdb_in_mem -benchmarks="fillseq" -key_size=32 -value_size=512 -num=1000000 -write_buffer_size=600000000  max_write_buffer_number=2 -compression_type=none
```

2. As a part of recovery in subsequent DB open, WAL will be processed to one or more SST files during the recovery. We're selecting a large block cache (`cache_size` parameter in `db_bench` script) suitable for holding the entire DB to test the “hot path” CPU overhead.

```
./db_bench -use_existing_db=true -db=/tmp/testdb_in_mem -statistics=false -cache_index_and_filter_blocks=true -benchmarks=seekrandom -preserve_internal_time_seconds=1 max_write_buffer_number=2 -explicit_snapshot=1 -use_direct_reads=1 -async_io=1 -num=? -seek_nexts=? -cache_size=? -write_buffer_size=? -auto_refresh_iterator_with_snapshot={0|1}
```

  | seek_nexts=100; num=2,000,000 | seek_nexts = 20,000; num=50000  | seek_nexts = 400,000; num=2000
-- | -- | -- | --
baseline | 36362 (± 300) ops/sec, 928.8 (± 23) MB/s, 99.11% block cache hit  | 52.5 (± 0.5) ops/sec, 1402.05 (± 11.85) MB/s, 99.99% block cache hit | 156.2 (± 6.3) ms / op, 1330.45 (± 54) MB/s, 99.95% block cache hit
auto refresh |  35775.5 (± 537) ops/sec, 926.65 (± 13.75) MB/s, 99.11% block cache hit |  53.5 (± 0.5) ops/sec, 1367.9 (± 9.5) MB/s, 99.99% block cache hit |  162 (± 4.14) ms / op, 1281.35 (± 32.75) MB/s, 99.95% block cache hit

_-cache_size=5000000000 -write_buffer_size=3200000000 -max_write_buffer_number=2_

  | seek_nexts=3,500,000; num=100
-- | --
baseline | 1447.5 (± 34.5) ms / op, 1255.1 (± 30) MB/s, 98.98% block cache hit
auto refresh | 1473.5 (± 26.5) ms / op, 1232.6 (± 22.2) MB/s, 98.98% block cache hit

_-cache_size=17680000000 -write_buffer_size=14500000000 -max_write_buffer_number=2_

  | seek_nexts=17,500,000; num=10
-- | --
baseline | 9.11 (± 0.185) s/op, 997 (± 20) MB/s
auto refresh | 9.22 (± 0.1) s/op, 984 (± 11.4) MB/s

[1]

### Specs

  | Property | Value
-- | --
RocksDB | version 10.0.0
Date | Mon Feb  3 23:21:03 2025
CPU | 32 * Intel Xeon Processor (Skylake)
CPUCache | 16384 KB
Keys | 16 bytes each (+ 0 bytes user-defined timestamp)
Values | 100 bytes each (50 bytes after compression)
Prefix | 0 bytes
RawSize | 5.5 MB (estimated)
FileSize | 3.1 MB (estimated)
Compression | Snappy
Compression sampling rate | 0
Memtablerep | SkipListFactory
Perf Level | 1

Reviewed By: pdillinger

Differential Revision: D69122091

Pulled By: mszeszko-meta

fbshipit-source-id: 147ef7c4fe9507b6fb77f6de03415bf3bec337a8
2025-02-11 19:36:47 -08:00
Jay Huh a30c0204cc Set Options File Number for CompactionInput under Mutex Lock (#13394)
Summary:
Options File Number to be read by remote worker is part of the `CompactionServiceInput`. We've been setting this in `ProcessKeyValueCompactionWithCompactionService()` while the db_mutex is not held. This needs to be accessed while the mutex is held. The value can change as part of `SetOptions() -> RenameTempFileToOptionsFile()` as in following.

https://github.com/facebook/rocksdb/blob/e6972196bca115e841a6b88d361ba945b49e1e5d/db/db_impl/db_impl.cc#L5595-L5596

Keep this value in memory during `CompactionJob::Prepare()` which is called while the mutex is held, so that we can easily access this later without mutex when building the CompactionInput for the remote compaction.

Thanks to the crash test. This was surfaced after https://github.com/facebook/rocksdb/issues/13378 merged.

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

Test Plan:
Unit Test
```
./compaction_service_test
```

Crash Test
```
COERCE_CONTEXT_SWITCH=1 COMPILE_WITH_TSAN=1 CC=clang-13 CXX=clang++-13 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j100 dbg
```
```
python3 -u tools/db_crashtest.py blackbox --enable_remote_compaction=1
```

Reviewed By: jowlyzhang

Differential Revision: D69496313

Pulled By: jaykorean

fbshipit-source-id: 7e38e3cb75d5a7708beb4883e1a138e2b09ff837
2025-02-11 19:11:36 -08:00
aletar89 e6972196bc Add python binding to LANGUAGE-BINDINGS.md (#13391)
Summary:
The only actively maintained python binding is RocksDict

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

Reviewed By: jowlyzhang

Differential Revision: D69431688

Pulled By: cbi42

fbshipit-source-id: 5e564118769a86cb8834a4faa5d852a54bbfea64
2025-02-10 17:38:40 -08:00
Changyu Bi 3f460ad0d2 Reverse the order of updates to the same key in WriteBatchWithIndex (#13387)
Summary:
as a preparation to support merge in [WBWIMemtable](https://github.com/facebook/rocksdb/blob/d48af213860054a7696e7ea2764f266c88a3263e/memtable/wbwi_memtable.h#L31), this PR updates how we [order updates to the same key](https://github.com/facebook/rocksdb/blob/d48af213860054a7696e7ea2764f266c88a3263e/utilities/write_batch_with_index/write_batch_with_index_internal.cc#L694-L697) in WriteBatchWithIndex. Specifically, the order is now reversed such that more recent update is ordered first. This will make iterating from WriteBatchWithIndex much easier since the key ordering in WBWI now matches internal key order where keys with larger sequence number are ordered first. The ordering is now explicitly documented above the declaration for `WriteBatchWithIndex` class.

Places that use `WBWIIteratorImpl` and assume key ordering are updated. The rest is test and comments update.
This will affect users who use WBWIIterator directly, the output of GetFromBatch, GetFromBatchAndDB or NewIteratorWithBase are not affected. Users are only affected if they may issue multiple updates to the same key. If WriteBatchWithIndex is created with `overwrite_key=true`, one the the updates needs to be Merge.

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

Test Plan: we have some good coverage of WBWI, I updated some existing tests and added a test for `WBWIIteratorImpl`.

Reviewed By: pdillinger

Differential Revision: D69421268

Pulled By: cbi42

fbshipit-source-id: d97eec4ee74aeac3937c9758041c7713f07f9676
2025-02-10 17:15:47 -08:00
Peter Dillinger c9ce4a3d6b Improve atomicity of SetOptions, skip manifest write (#13384)
Summary:
Motivated by code review issue in https://github.com/facebook/rocksdb/issues/13316, we don't want to release the DB mutex in SetOptions between updating the cfd latest options and installing the new Version and SuperVersion. SetOptions uses LogAndApply to install a new Version but this currently incurs an unnecessary manifest write. (This is not a big performance concern because SetOptions dumps a new OPTIONS file, which is much larger than the redundant manifest update.) Since we don't want IO while holding the DB mutex, we need to get rid of the manifest write, and that's what this change does. We introduce a kind of dummy VersionEdit that allows the existing code paths of LogAndApply to install a new Version (with the updated mutable options), recompute resulting compaction scores etc., but without the manifest write.

Part of the validation for this is new assertions in SetOptions verifying the consistency of the various copies of MutableCFOptions. (I'm not convinced we need it in SuperVersion in addition to Version, but that's not for here and now.) These checks depend on defaulted `operator==` so depend on C++20.

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

Test Plan:
New unit test in addition to new assertions. SetOptions already tested heavily in crash test. Used
`ROCKSDB_CXX_STANDARD=c++20 make -j100 check` to ensure the new assertions are verified

Reviewed By: cbi42

Differential Revision: D69408829

Pulled By: pdillinger

fbshipit-source-id: 4cf026010c6bb381e0ea27567cce2708d4678e7d
2025-02-10 16:46:13 -08:00
Hui Xiao 1f36399a77 Blog post about the mitigated misconfig bug (#13386)
Summary:
**Context/Summary:** as title

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

Test Plan: Run the webpage locally according to https://github.com/facebook/rocksdb/tree/main/docs and check everything is fine

Reviewed By: jaykorean

Differential Revision: D69334257

Pulled By: hx235

fbshipit-source-id: 4e9b6dbddd5035b9277044c6cb0ac97b3819ec6c
2025-02-10 13:10:33 -08:00
Yu Zhang d48af21386 Fix some race conditions in listener_test (#13385)
Summary:
There are some data races reported for this test. This PR fixes two races:
1) Test main body and event listener callback race to access a variable `call_count_` in the test's event listener.

2) Test event listener access `ColumnFamilyData::current_` during `OnFlushCompleted` without locking DB mutex and raced with a background compaction job.

Example [run](https://github.com/facebook/rocksdb/actions/runs/13208433475/job/36876956677?fbclid=IwZXh0bgNhZW0CMTEAAR0_gG1Brx7I6bhN3PVD267c2d06GSf7QBEQ8cbNcFHNvn-ZX2JWHtr05qg_aem_915NHkfFh-6cMk83uTHWKw)

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

Reviewed By: cbi42

Differential Revision: D69333371

Pulled By: jowlyzhang

fbshipit-source-id: dee4a5f5e161d9b1f5b47b37163ee5b91fe18977
2025-02-07 16:58:02 -08:00
Changyu Bi dd01f73e26 Fix a bug in GetMergeOperands() with continue_cb set (#13383)
Summary:
Noticed this while I was working on memtable code. Wrong status (MergeInProgress()) and wrong number of merge operands can be returned if the `continue_cb` stop at an immutable memtable. This is due to

1. Get from memtable sets MergeInProress() status https://github.com/facebook/rocksdb/blob/302254d928402c80ecabb0cf2b76d18be35a87b9/db/memtable.cc#L1461
2. Get from immutable memtable does not update status but stops the get:  https://github.com/facebook/rocksdb/blob/302254d928402c80ecabb0cf2b76d18be35a87b9/db/memtable.cc#L1364
3. GetImpl() only returns merge_operands for OK status: https://github.com/facebook/rocksdb/blob/302254d928402c80ecabb0cf2b76d18be35a87b9/db/db_impl/db_impl.cc#L2552

Also updated some comments for GetMergeOperands().

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

Test Plan: added a unit test that fails GetMergeOperands() with MergeInProgress() status before this fix.

Reviewed By: jowlyzhang

Differential Revision: D69322133

Pulled By: cbi42

fbshipit-source-id: aebfccd8446e9640cff02877915076e2d10f7a5b
2025-02-07 16:44:06 -08:00
Andrew Chang a377bded9f Set manual_wal_flush_one_in to 0 when disable_wal is 1 (#13382)
Summary:
I found a failed crash test with this error message:

```
Verification failed: Failed to flush primary's WAL before secondary verification
```

`manual_wal_flush_one_in` does not make sense / is not applicable when we are disabling the WAL.

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

Test Plan: Monitor future crash test runs

Reviewed By: jowlyzhang, anand1976

Differential Revision: D69314053

Pulled By: archang19

fbshipit-source-id: b69d2e1e2869943c0df8cdc4f0623906f4ec7a7a
2025-02-07 13:30:13 -08:00
Andrew Chang c1fb33e1d0 Prefetch buffer may not contain all of requested data if EOF is hit (#13376)
Summary:
There was a stress test that failed at the assertion check for `IsDataBlockInBuffer`.

`IsDataBlockInBuffer` is too strict of a condition if we are trying to read past the end of the file.

This seems to be a bug from the original 2019 commit https://github.com/siying/rocksdb/commit/3737d06adc01a59e7eb29710a2a4ec64adfaa528: https://github.com/siying/rocksdb/blob/4eb51130917c260f5637731cd77baaa45dfdc5ec/file/file_prefetch_buffer.cc#L130

If the caller tries requesting more bytes than are available, then we still return `n` bytes, even if the buffer really only contains `m < n` bytes.

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

Test Plan: I added a unit test which caused the original `IsDataBlockInBuffer ` assertion to fail. I also updated the unit test to check for the result size, which triggered the bug (without this fix) where we return a size of `n` even if less than `n` bytes exist.

Reviewed By: anand1976

Differential Revision: D69269608

Pulled By: archang19

fbshipit-source-id: 1dc0d5930e2b73089850f6e996afbd6192cd5ac8
2025-02-07 13:25:56 -08:00
Jay Huh 302254d928 Add enable_remote_compaction option to DB Stress (#13378)
Summary:
First step to add (simulated) Remote Compaction in Stress Test. More PRs to come. Just first PR to add the FLAG to enable it. `DbStressCompactionService` will return `kUseLocal` for all compactions.

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

Test Plan:
```
python3 -u tools/db_crashtest.py whitebox --enable_remote_compaction=1
```
```
python3 -u tools/db_crashtest.py blackbox --enable_remote_compaction=1
```

Reviewed By: hx235

Differential Revision: D69269568

Pulled By: jaykorean

fbshipit-source-id: 5119bb6afd4d52f66923fb095150d3132226f7ba
2025-02-06 16:44:25 -08:00
Andrew Chang 62531da510 Track the total number of compaction sorted runs from inside CompactionMergingIterator (#13325)
Summary:
**This PR adds a new statistic to track the total number of sorted runs for running compactions.**

Context: I am currently working on a separate project, where I am trying to tune the read request sizes made by `FilePrefetchBuffer` to the storage backend. In this particular case, `FilePrefetchBuffer` will issue larger reads and have to buffer larger read responses. This means we expect to see higher memory utilization. At least for the initial rollout, we only want to enable this optimization for compaction reads.

**I want some way to get a sense of what the memory usage _impact_ will be if the prefetch read request size is increased from (for instance) 8MB to 64MB.**

**If I know the number of files that compactions are actively reading from (i.e. the number of sorted runs / "input iterators"), I can determine how much the memory usage will increase if I bump up the readahead size inside `FilePrefetchBuffer`.** For instance, if there are 16 sorted runs at any given point in time and I bump up the readahead size by 64MB, I can project an increase of 16 * 64 MB.

In most cases, the number of sorted runs processed per compaction is the number of L0 files plus the number of non-L0 levels. However, we need to be aware of exceptions like trivial compactions, deletion compactions, and subcompactions. This is a major reason why this PR chooses to implement the stats counting inside `CompactionMergingIterator`, since by the time we get down to that part of the stack, we know the "true" values for the number of input iterators / sorted runs.

Alternatives considered:
- https://github.com/facebook/rocksdb/issues/13299 gives you a histogram for the number of sorted runs ("input iterators") for a _single compaction_. While this statistic is interested and in the direction of what we want, we are going to be assessing the memory impact across _all_ compactions that are currently running. Thus, this statistic does not give us all the information we need.
- https://github.com/facebook/rocksdb/issues/13302 gives you the total prefetch buffer memory usage, but it doesn't tell you what happens when the readahead size is increased. Furthermore, the code change is error prone and very "invasive" -- look at how many places in the code had to be updated. This would be useful in the future for general memory accounting purposes, but it does not serve our immediate needs.
- https://github.com/facebook/rocksdb/issues/13320 aimed to track the same metric, but did this inside `DbImpl:: BackgroundCallCompaction`. It turns out that this does not handle the case where a compaction is divided into multiple subcompactions (in which case, there would be _more_ sorted runs being processed at the same time than you would otherwise predict.) The current PR handles subcompactions automatically, and I think it is cleaner overall.

Note: When I attempted to put this statistic as part of the `cf_stats_value_` array, even after updating the array to use `std::atomic<uint64_t>`, I still was able to get assertions to _fail_ inside the crash tests. These assertions checked that the unsigned integer would not underflow below zero during compaction. I experimented for many hours but could not figure out a solution, even though it would seem like things "should" work with `fetch_add` and `fetch_sub`. One possibility is that the values in `cf_stats_value_` are being cleared to 0, but I added a `fprintf` to that portion of the code and didn't see it getting printed out before my assertions failed. Regardless, I think that this statistic is different enough from the CF-specific and the other DB-wide stats that the best solution is to just have it defined as a separate `std::atomic<uint64_t>`. I also do not want to spend more hours trying to debug why the crash test assertions break, when the solution in the current version of the PR can get the assertions to consistently pass.

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

Test Plan:
- I updated one unit test to confirm that `num_running_compaction_sorted_runs` starts and ends at 0. This checks that all the additions and subtractions cancel out. I also made sure the statistic got incremented at least once.
- When I added `fprintf` manually, I confirmed that my statistics updating code was being exercised numerous times inside `db_compaction_test`. I printed out the results before and after the increments/decrements, and the numbers looked good.
- We will monitor the generated statistics after this PR is merged.
- There are assertion checks after each increment and before each decrement. If there are bugs, the crash test will almost certainly find them, since they quickly found issues with my initial implementation for this PR which tried using the `cf_stats_value_` array (modified to use `std::atomic`).

Reviewed By: anand1976, hx235

Differential Revision: D68527895

Pulled By: archang19

fbshipit-source-id: 135cf210e0ff1550ea28ae4384d429ae620b1784
2025-02-06 13:25:51 -08:00
Yu Zhang 354025fc86 Fix flaky test ExternalSSTFileBasicTest.Basic (#13374)
Summary:
This test is flaky likely due to synchronization of the file ingestion thread and the live write thread with test sync points are not working as expected sometimes. Very occasionally, the live write thread can enter the write queue after file ingestion job already dequeued. Or it entered and waited for a very short period of time and quickly returned in the fast path: https://github.com/facebook/rocksdb/blob/833a2266a394fe5f140d2a22f406c82bb605c726/db/write_thread.cc#L83-L86

To fix the flakiness, I moved the test sync points to make sure the write thread is already linked into the write queue before the file ingestion writer get dequeued, so it definitely would need to wait some time in order to do its write.

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

Test Plan:
I'm able to reproduce the flakiness with this command before the fix  with every two or three runs:
./gtest-parallel external_sst_file_basic_test --gtest_filter=ExternalSSTFileBasicTest.Basic --repeat=10000 --workers=100

After the fix, I have tried the command for 10 runs, and there is no failure detected.

Reviewed By: cbi42

Differential Revision: D69258712

Pulled By: jowlyzhang

fbshipit-source-id: adcbad4dd53ccddab5c137d3f9d740b9f9623207
2025-02-06 13:00:59 -08:00
Levi Tamasi 4ace09f6eb Add a release note for the recent secondary index query API changes (#13377)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/13377

Reviewed By: jowlyzhang

Differential Revision: D69258159

fbshipit-source-id: 6a5aa099d10df15e17fb9babc433d8463dcdf1c7
2025-02-06 12:28:19 -08:00
Levi Tamasi 833a2266a3 Expose a simple secondary index implementation (#13370)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13370

We have a class called `DefaultSecondaryIndex` in `TransactionTest.SecondaryIndexPutDelete` that contains generally useful functionality. The patch generalizes it a bit to make the column name configurable, renames it to `SimpleSecondaryIndex`, and moves it to the public API so applications can use it.

Reviewed By: jowlyzhang

Differential Revision: D69147890

fbshipit-source-id: 0d2d1cc5adcde01f3978a450ec841c9e990d2170
2025-02-05 15:43:54 -08:00
Andrew Chang ed2a87db07 BlobDB is not compatible with secondary instances (#13371)
Summary:
There was a failed TSAN crash test run that involved BlobDB and secondary instances. ltamasi said that BlobDB is not compatible with secondary instances, so I have updated the crash test script accordingly.

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

Test Plan:
I confirmed there were no blob-related parameters after running

```
python3 tools/db_crashtest.py --simple blackbox --test_secondary=1
```

Reviewed By: jowlyzhang

Differential Revision: D69193105

Pulled By: archang19

fbshipit-source-id: b545d7765928a385a792fc070c1d432d1c002b3d
2025-02-05 14:59:16 -08:00
Yu Zhang 242e69f067 Remove release note files that were released starting from 9.11.0 (#13373)
Summary:
As titled. unreleased_history directory now only contain release notes for the next 10.0 release.

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

Reviewed By: ltamasi

Differential Revision: D69196468

Pulled By: jowlyzhang

fbshipit-source-id: 849193c7901c5938d3d7c938e3b6c805532d7de4
2025-02-05 13:45:25 -08:00
Abhishek Chanda 61ee80faa0 Update error message for allowing concurrent memtable writes (#13364)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/13364

Reviewed By: archang19

Differential Revision: D69156065

Pulled By: cbi42

fbshipit-source-id: 5393f439a8eee5009aa63d1be683a3dfd9419272
2025-02-05 13:41:51 -08:00
Andrew Chang f5f4f83fcc disable_wal and reopen are incompatible (#13372)
Summary:
We want to disable WAL for RoWS stress tests (anand1976 made a config change to explicitly do this), but it turns out that is not compatible with `reopen` > 0.

I found this error in the logs:
```
Error: Db cannot reopen safely with disable_wal set!
```

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

Test Plan: We should not get this error message in the RoWS stress tests.

Reviewed By: jowlyzhang

Differential Revision: D69193849

Pulled By: archang19

fbshipit-source-id: 933252926a906183c9abdef0b47f641073c5de37
2025-02-05 12:35:57 -08:00
Maciej Szeszko 22270dea6f Fix flaky test post final DB::DeleteFile refactoring (#13349)
Summary:
`DynamicLevelCompressionPerLevel` test started _somewhat occasionally_ failing post refactoring in https://github.com/facebook/rocksdb/pull/13322. In order for `DeleteFilesInRange`-replacement to behave according to our expectations (that is delete exactly that very single file given its' key range), we must first ensure that input `keys` are NOT randomly shuffled, but rather preserved in their natural, sequential order. That change was originally a part of the PR, but got somehow deleted due to human error and since tests passed locally and in CI, spilled unnoticed. We're removing random keys reshuffling (as intended originally) and, in addition, asserting that all such constructed files are 1) non-overlapping and 2) contain full range of keys BEFORE we actually get to test the on table deletion callbacks.

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

Test Plan: Confirmed that key range overlap is an issue by volume testing: `./db_test --gtest_filter=*DynamicLevelCompressionPerLevel --gtest_repeat=1000 --gtest_break_on_failure` (2-3 times is enough). Could not longer repro after the fix.

Reviewed By: jaykorean

Differential Revision: D68857018

Pulled By: mszeszko-meta

fbshipit-source-id: 873b1ba44f32d40192da4265aeeb39702c22a1d0
2025-02-05 11:59:40 -08:00
Hui Xiao 864964b008 Return injected error when injecting empty result and corrupted bytes read error (#13369)
Summary:
**Context/Summary:**

archang19 found the place in code where no injected error status is returned on effectively injected error (empty result or corrupted bytes). I can't find a good argument for doing so. In these cases where such empty result and corrupted result is not expected, the file system should return error (< 0). Our fault injection framework should align with that to simulate fault returned by file system.

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

Test Plan: Monitor stress test

Reviewed By: archang19

Differential Revision: D69136015

Pulled By: hx235

fbshipit-source-id: 6ee7a7bd5e0aa19837e4dfd73817d4a9d5af76f9
2025-02-04 16:55:09 -08:00
Andrew Chang 7774a4de17 Disable and re-enable error injection before secondary db verification (#13368)
Summary:
The crash tests are failing during secondary database verification due to a "truncated block read" error.

https://github.com/facebook/rocksdb/issues/13366 attempted to resolve the issue by checking for injected errors. However, that did not work.

It turns out that sometimes faults are injected yet the return status is still "OK."

See https://github.com/facebook/rocksdb/blob/main/utilities/fault_injection_fs.cc#L1407-L1414 for an example:
```cpp
    } else if (Random::GetTLSInstance()->OneIn(8)) {
      assert(result);
      // For a small chance, set the failure to status but turn the
      // result to be empty, which is supposed to be caught for a check.
      *result = Slice();
      msg << "empty result";
      ctx->message = msg.str();
      ret_fault_injected = true;
```

My hypothesis is that this particular fault injection is the root cause of the "truncated block read" error.

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

Test Plan: Hopefully the recurring crash tests start passing consistently for secondary db verification

Reviewed By: hx235

Differential Revision: D69132024

Pulled By: archang19

fbshipit-source-id: 941406165a2fd306f10048614457261cda99d762
2025-02-04 12:45:09 -08:00
Peter Dillinger 7f271a3fa8 Require ZSTD >= 1.4.0, greatly simplifying related code (#13362)
Summary:
Leading up to some compression code refactoring, we have a bit of an ifdef nightmare in compression.h relating to zstd support. With the major release RocksDB 10.0.0 coming up, it is a good time to clean up much of this tech debt by requiring zstd >= 1.4.0 (April 2019) if building RocksDB with ZSTD support. For example, Ubuntu 20, the first LTS version to properly support C++17 in its built-in gcc, comes with zstd version 1.4.4. This should not be a significant limitation.

* Almost all of the `ZSTD_VERSION_NUMBER` checks are simplified to just `ZSTD`, though
  * `ROCKSDB_ZSTD_DDICT` still needs to be separate because of dependency on `ZSTD_STATIC_LINKING_ONLY` (added to fbcode_config_platform010.sh by the way)
  * Similar for ZDICT_finalizeDictionary, which is only generally available in >= 1.4.5
* Eliminate deprecated `kZSTDNotFinalCompression`
* Reduce some cases of unnecessary copying definitions across `#if` branches (e.g. `ZSTDUncompressCachedData`)

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

Test Plan:
minor unit test updates. `make check` on several build variants with/without zstd and with/without `ZSTD_STATIC_LINKING_ONLY`

Also deflaked DBTest.DynamicLevelCompressionPerLevel which was flaky before this change but failed once in CI

Reviewed By: cbi42

Differential Revision: D69129453

Pulled By: pdillinger

fbshipit-source-id: ef0cbf9f0fea4e7684fa0999320aa170cfbec233
2025-02-04 12:03:32 -08:00
Hui Xiao a10b4aa9a3 Disable track_and_verify_wals=1 with write fault injection only when pessimistic txn in stress test (#13360)
Summary:
**Context/Summary:**

https://github.com/facebook/rocksdb/pull/13263 temporally disable `track_and_verify_wals=1` with write fault injection in all cases to mitigate a WAL hole not fully debugged. Fully debugging shows the WAL hole only happens under pessimistic TXN  when two-phase-commit (2pc) was used.

The bug essentially is about 2pc won't be able to discard the corrupted WAL as it would in non-2pc case as part of the WAL write error recovery. So the corrupted WAL will still present in the next DB open and caught by `track_and_verify_wals=1`.

This fix is going to take a while. So for now, let's reduce the scope of disabling the testing.

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

Test Plan: Monitor stress test for WAL recovery error/corruption

Reviewed By: jaykorean

Differential Revision: D68973022

Pulled By: hx235

fbshipit-source-id: ea8db6fa11ba25ace896da7cdb1dc1cd757742f6
2025-02-03 14:22:05 -08:00
Andrew Chang 1341c0c670 Skip secondary verification on injected read error (#13366)
Summary:
https://github.com/facebook/rocksdb/issues/13281 added secondary database verification to the crash tests.

I am seeing failures in the crash test that trace back to these two code sections:

1. https://github.com/facebook/rocksdb/blob/main/db_stress_tool/no_batched_ops_stress.cc#L2969-L2975
```cpp
VerificationAbort(
          shared,
          msg_prefix + "Non-OK status" + read_u64ts.str() + s.ToString(), cf,
          key, "", Slice(expected_value_data, expected_value_data_size));
```
2. https://github.com/facebook/rocksdb/blob/main/table/block_fetcher.cc#L327-L331
```cpp
      io_status_ = IOStatus::Corruption(
          "truncated block read from " + file_->file_name() + " offset " +
          std::to_string(handle_.offset()) + ", expected " +
          std::to_string(block_size_with_trailer_) + " bytes, got " +
          std::to_string(slice_.size()));
```

The error messages look like
```
Secondary get verificationNon-OK statusCorruption: truncated block read from /dev/shm/rocksdb_test/rocksdb_crashtest_blackbox/011887.sst offset 11780096, expected 16274 bytes, got 0
```

As you can see, the issue is not that the values of the secondary DB differ from what we expect. Rather, the `get` request itself is returning a non-OK status. I looked at the test configurations for the failed test runs, and I saw that both of them enabled fault injections (e.g. `read_fault_one_in`).

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

Test Plan:
Before merging: `python3 tools/db_crashtest.py --simple blackbox --test_secondary=1`
After merging: monitor for crash test failures

Reviewed By: jaykorean

Differential Revision: D69059138

Pulled By: archang19

fbshipit-source-id: a9c07d80381f52bdff220b0db3302748ebccd96c
2025-02-03 11:28:52 -08:00
Levi Tamasi ce6065ef70 Add a dedicated API for KNN search (#13361)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13361

After https://github.com/facebook/rocksdb/pull/13346 and https://github.com/facebook/rocksdb/pull/13348, K-nearest-neighbors queries no longer have to be exposed via an iterator API. The patch makes the interface for KNN search more natural by replacing `KNNIterator` in `FaissIVFIndex` with a new method `FindKNearestNeighbors`. This simplifies both the use and the implementation of `FaissIVFIndex`.

Reviewed By: jowlyzhang

Differential Revision: D68973541

fbshipit-source-id: cd6fec44c202e7cfa7219af482d1ca800e2d672d
2025-01-31 18:11:45 -08:00
Levi Tamasi d000134cb5 Revise the SecondaryIndexIterator interface and make it public (#13353)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13353

The patch changes `SecondaryIndexIterator` to a standalone concrete class that mimics most of `Iterator`'s interface but no longer derives from `Iterator`. This eliminates the need to implement `Iterator` methods which are not applicable in the context of secondary indices (namely `SeekToFirst`, `SeekToLast`, and `SeekForPrev`). The class is also moved to the public interface; with this move, the earlier factory method doesn't really add much value anymore and is thus removed.

Reviewed By: jowlyzhang

Differential Revision: D68923662

fbshipit-source-id: 9e1af250bb392535537d6c867f36d23dae5b01b9
2025-01-31 10:27:27 -08:00
Jay Huh 78210c82ad Fix wget in check-format-and-targets (#13352)
Summary:
https://raw.githubusercontent.com is not as reliable as we hoped. The same file is now available in RocksDB dependency bucket in S3. Replacing it with the new location to see if it's better.

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

Test Plan:
[PR Job](https://github.com/facebook/rocksdb/actions/runs/13061435219/job/36445107386?pr=13352)

```
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

--2025-01-30 21:19:39--  https://rocksdb-deps.s3.us-west-2.amazonaws.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
Resolving rocksdb-deps.s3.us-west-2.amazonaws.com (rocksdb-deps.s3.us-west-2.amazonaws.com)... 3.5.80.189, 3.5.79.1[4](https://github.com/facebook/rocksdb/actions/runs/13061435219/job/36445107386?pr=13352#step:7:4), 52.92.2[4](https://github.com/facebook/rocksdb/actions/runs/13061435219/job/36445107386?pr=13352#step:7:5)3.114, ...
Connecting to rocksdb-deps.s3.us-west-2.amazonaws.com (rocksdb-deps.s3.us-west-2.amazonaws.com)|3.[5](https://github.com/facebook/rocksdb/actions/runs/13061435219/job/36445107386?pr=13352#step:7:6).80.189|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4881 (4.8K) [text/x-python-script]
Saving to: ‘clang-format-diff.py’
     0K ....                                                  100%  213M=0s
2025-01-30 21:19:39 (213 MB/s) - ‘clang-format-diff.py’ saved [4881/4881]
```

Reviewed By: mszeszko-meta

Differential Revision: D68917940

Pulled By: jaykorean

fbshipit-source-id: 3348d6dc362401af92733c6abe1568bc4da67726
2025-01-30 14:58:10 -08:00
Po-Chuan Hsieh 1f0426c44b Fix build with -Wrange-loop-construct (#13273)
Summary:
db/db_impl/db_impl_write.cc:208:19: error: loop variable '[cf_id, stat]' creates a copy from type 'const value_type' (aka 'const pair<const unsigned int, rocksdb::WriteBatchWithIndex::CFStat>') [-Werror,-Wrange-loop-construct]
  208 |   for (const auto [cf_id, stat] : wbwi->GetCFStats()) {
      |                   ^
db/db_impl/db_impl_write.cc:208:8: note: use reference type 'const value_type &' (aka 'const pair<const unsigned int, rocksdb::WriteBatchWithIndex::CFStat> &') to prevent copying
  208 |   for (const auto [cf_id, stat] : wbwi->GetCFStats()) {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~
      |                   &
1 error generated.

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

Reviewed By: jaykorean

Differential Revision: D68109780

Pulled By: cbi42

fbshipit-source-id: a0bc86bb82e8eaf7175c9cae4ae5dbad4f461d8c
2025-01-30 14:30:51 -08:00
Andrew Chang 57c177be55 Use secondary_cfhs in secondary_db_->Get (#13351)
Summary:
This bug was spotted by cbi42 and should be the root cause for the crash test data races 🤞 .

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

Test Plan: Monitor recurring crash tests.

Reviewed By: hx235

Differential Revision: D68909000

Pulled By: archang19

fbshipit-source-id: e0bdfda9f92eacd2513fc8894f8cde35da88da68
2025-01-30 11:44:23 -08:00
Levi Tamasi d4676bfadd Remove the NewIterator virtual from the SecondaryIndex interface (#13348)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13348

This eliminates the need to shoehorn all index queries into a single method signature. With this change, `SecondaryIndex` implementations can expose the queries they support via the most natural interface. For `FaissIVFIndex`, this means that KNN search need not be modeled using an iterator anymore; however, for now, the class still has a (non-virtual) `NewIterator` method that takes a read options structure `FaissIVFIndexReadOptions`.

Reviewed By: jowlyzhang

Differential Revision: D68852927

fbshipit-source-id: b4f63bfea9cd73a6c99a547de2a0676e1e8dee0d
2025-01-30 10:38:54 -08:00
Levi Tamasi c3b71a6706 Expose FaissIVFIndex in the public API (#13346)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13346

As the first step of revising the secondary index query API, the patch moves `FaissIVFIndex` to the public header. This will enable querying the index without having a `NewIterator` virtual in the `SecondaryIndex` interface (which will be removed in the next step of this cleanup).

Reviewed By: jowlyzhang

Differential Revision: D68846678

fbshipit-source-id: 37617d7da87a5c31b1ec7d82ef9694f8519d78d6
2025-01-29 17:13:41 -08:00
Changyu Bi d79a5e5854 Deflake unit test RoundRobinSubcompactionsAgainstPressureToken.PressureTokenTest (#13347)
Summary:
The test has been [flaky](https://github.com/facebook/rocksdb/actions/runs/12220443012/job/34088263578?fbclid=IwZXh0bgNhZW0CMTEAAR3iDUK20Z4kdFkYZOT_PgQMYuj3Ebmpf4O-OOLLyeFQs4HAb8pRTWpFnUo_aem_09A_yiv7cwoD5lKjxFKimA). The cause for flakiness is that background threads may not be immediately available after calling env_->SetBackgroundThreads() while the test expects all background threads to be available for compaction. There's no way to get the number of available threads and I don't want to update threadpool implementation just for this test. So I added a fix to wait until background threads being available that relies on sync point.

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

Test Plan: monitor future test failure

Reviewed By: hx235

Differential Revision: D68851929

Pulled By: cbi42

fbshipit-source-id: 2dddda98ccc4c299eb1dd05ee7fd154b7a31f163
2025-01-29 15:55:20 -08:00
Ryan Hancock a6322b9ec6 Allow for Customizable DB Open Hooks for DB Bench (#13326)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13326

This diff introduces ToolHooks, a class which allows for users to interpose their own set of logic for various functionality with db_bench_tool (i.e., various OpenDB implementations).

Reviewed By: anand1976

Differential Revision: D67868126

fbshipit-source-id: df433b0c8a064a86735b92a8ef5f38527dbc9112
2025-01-29 15:30:12 -08:00
Jay Huh 2e0dc21a9f Fix GetMergeOperands in ReadOnlyDB and SecondaryDB (#13340)
Summary:
Fixing the GetMergeOperands() in ReadOnlyDB and SecondaryDB as reported in https://github.com/facebook/rocksdb/issues/13243. Refactor in https://github.com/facebook/rocksdb/issues/11799 introduced this regression.

Follow ups to come
- Large Result Optimization (done in https://github.com/facebook/rocksdb/issues/10458 ) for ReadOnlyDB and SecondaryDB
- Stress Test / Crash Test coverage
- Consider removing some duplicate logic between ReadOnlyDB's GetImpl() and SecondaryDB's `GetImpl()`. The only difference is between acquiring/referencing Superversion.

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

Test Plan:
`DBMergeOperandTest` and `DBSecondaryTest` updated

```
./db_merge_operand_test --gtest_filter="*GetMergeOperandsBasic*"
```
```
./db_secondary_test -- --gtest_filter="*GetMergeOperands*"
```

Reviewed By: ltamasi

Differential Revision: D68791652

Pulled By: jaykorean

fbshipit-source-id: 760925e257ab10993c207094718dc0659822ae64
2025-01-28 18:03:22 -08:00
Andrew Chang f37ce33fcc Clean up secondary column families alongside primary column families (#13343)
Summary:
This is a continuation of https://github.com/facebook/rocksdb/pull/13338, which aims to address crash test failures caused by https://github.com/facebook/rocksdb/pull/13281.

This PR attempts to address the TSAN failures.

I searched for wherever we call `column_families_.clear()` and made sure that we also clear the secondary column families as well. I made a helper method since it is easy to forget to clear both sets of column families.

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

Test Plan: Monitor recurring crash test results.

Reviewed By: cbi42

Differential Revision: D68790580

Pulled By: archang19

fbshipit-source-id: 96ed758a21545dd20181b8db71b81dd660546e18
2025-01-28 13:57:34 -08:00
Andrew Chang b8d915c7fa Force a primary flush before secondary verification when WAL is disabled or manual_wal_flush is set (#13338)
Summary:
https://github.com/facebook/rocksdb/issues/13281 added support for verifying secondaries in the crash tests. We are trying to check that the values returned by the secondary in `Get` requests fall within an expected range of values. We do reads from the shared expected state before and after we read from the secondary.

There are some rare verification failures where `VerifyValueRange` fails with `Unexpected value found outside of the value base range`.

I have some ideas on what the root cause could be. The secondary can read the WAL, MANIFEST, and SST files, but in some scenarios some of these pieces may not be present.

I noticed that the failures had `manual_wal_flush_one_in=1000`, which means that `options.manual_wal_flush` is set to `true`. With this setting, RocksDB has its own internal buffers that need to be manually flushed for the WAL to be persisted.

Although the test failures I looked at did not disable the WAL, I realized that, when the WAL is disabled, we should flush the primary's memtables, since the secondary needs to be able to find SST files to fully catch up.

Injected faults further complicate matters, so I have a check to skip secondary verification whenever the WAL or memtable flushes fail due to fault injection.

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

Test Plan:
Locally:
```
python3 tools/db_crashtest.py --simple blackbox --test_secondary=1
python3 tools/db_crashtest.py --simple whitebox --test_secondary=1
python3 tools/db_crashtest.py --simple blackbox --test_secondary=1 --disable_wal=1
python3 tools/db_crashtest.py --simple blackbox --test_secondary=1 --disable_wal=0 --manual_wal_flush_one_in=1000
```

I will monitor the recurring crash tests after this gets merged.

Reviewed By: anand1976

Differential Revision: D68741287

Pulled By: archang19

fbshipit-source-id: 86f474c41a68b7b06f2ed80a851c6cb52a47ebe7
2025-01-28 10:56:13 -08:00
Andrew Chang 880f85a162 Call delete on secondary_db_ wherever it is done on db_ (#13337)
Summary:
https://github.com/facebook/rocksdb/issues/13281 added support to the crash tests for secondary DB verification.

I looked at our recurring crash tests to see what impact https://github.com/facebook/rocksdb/issues/13281 had. The actual secondary verification looks okay to me (no `assert` failures), but I noticed memory leaks were detected.

The problematic areas were tracked down to the call to `DB::OpenAsSecondary` from `rocksdb::StressTest::Open`.

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

Test Plan:
Monitor recurring crash tests. It is likely hard to reproduce the ASAN failures locally if they are rare enough.

```
make -j100 db_stress COMPILE_WITH_ASAN=1
python3 tools/db_crashtest.py --simple blackbox --test_secondary=1
```

Reviewed By: cbi42

Differential Revision: D68721624

Pulled By: archang19

fbshipit-source-id: 9c3044884c505c43c1819a3e98ce99b2d171f3ca
2025-01-27 13:19:58 -08:00
Maciej Szeszko 591f5b1266 Remove deprecated DB::DeleteFile API references (#13322)
Summary:
Cleanup post https://github.com/facebook/rocksdb/pull/13284.

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

Test Plan:
1. We did not find any evidence of breakage in internal pre-release integration pipeline runs after renaming the deprecated API in `9.10`.
2. _To the extent possible_, we manually validated partner use cases of file deletion and confirmed deprecated API is no longer in use.

Reviewed By: jaykorean

Differential Revision: D68476852

Pulled By: mszeszko-meta

fbshipit-source-id: fbe1f873e16ae7c60d7706a3c44ecc695ab86a4b
2025-01-24 22:28:41 -08:00
Levi Tamasi ac6c671308 Add a couple of convenience methods for converting embeddings (#13329)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13329

The patch adds two convenience methods `ConvertFloatsToSlice` and `ConvertSliceToFloats` that can be used to convert embeddings from a contiguous range of floats to a RocksDB `Slice` or vice versa. The methods are added to the public API so they can be utilized by applications as well.

Reviewed By: jowlyzhang

Differential Revision: D68581494

fbshipit-source-id: 2207fa3e668a6546b7de6d8ab78be2ba9f2ffd8c
2025-01-24 17:50:52 -08:00
Andrew Chang a8bd6a3ffe Verify values in secondary database against expected state (#13281)
Summary:
TLDR: This PR enables secondary DB verification inside the "simple" crash tests (`NonBatchedOpsStressTest`). Essentially, we want to be able to verify that the secondary is a valid "prefix" of the primary. This PR allows us to do this by piggybacking on the existing verification of the primary through `Get()` requests.

I originally proposed replaying the trace file to recreate the `ExpectedState` as of a specific sequence number. This could be used to run verifications against the secondary database. I did some experimenting in https://github.com/facebook/rocksdb/issues/13266 and got a "mostly working" implementation of this approach. I could sometimes get through entire key space verifications but eventually one of the keys would fail verification. I have not figured out the root cause yet, but I assume that something caused the sequence number to trace record alignment to break.

The approach in this PR is considerably simpler. We can just check that the secondary database's value is in the correct "range," which we already have functionality for checking that. Compared to the approach in https://github.com/facebook/rocksdb/issues/13266, this approach is _much, much simpler_ since we do not have to go through the whole headache of replaying the trace and creating an entire new `ExpectedState`. (Look at https://github.com/facebook/rocksdb/issues/13266 to see how much of a mess that creates.) I think this approach is better than my original approach in almost most aspects: it's faster, uses less space, and has less room for implementation errors.

Other nice aspects of this approach:
1. We don't need to block the primary. (Another approach you could imagine would be to block writes to the primary, have the secondary catch up, do the whole verification, and then re-enable writes to the primary.)
2. We don't need to block the secondary or do any special coordination (locks, sync points, etc). (If we insist on one "golden" expected value to be read from the secondary, then we need to make sure that another thread does not call `TryCatchUpWithPrimary` while we are trying to perform a `Get()`)
3. More "realistic" usage of the secondary. For instance, writes to the primary and secondary would continue on in production while we try to read from the secondary.

The main drawback of course is that we verify against a range of expected values, rather than one particular expected value. However, I think this is acceptable and "good enough" especially with all of other the aforementioned benefits.

Historical context: There is some very old code that attempted to verify secondaries, but is not enabled. This code has not been touched or executed in an extremely long time, and the crash tests started failing when I tried enabling it, most likely because the code is not compatible with certain other crash test options. This code is for the "continuous verification" and involves long iterator scans over the secondary database. Some of the code involved the cross CF consistency test type. I don't think the old checks are what we really want for our purposes of verifying the secondary functionality. Since I don't think we will get much value out of this old "continuous verification" code, I integrated my secondary verification with the "regular" database verification. This also makes the rollout simpler on my end, since I can control whether my secondary verifications are enabled through one `test_secondary` configuration. To make sure the old code does not execute for our recurring crash test runs, I had to enforce that `continuous_verification_interval` is 0 whenever `test_secondary` is set.

Monitoring: I will want to monitor the Sandcastle "simple" runs for failures where `test_secondary` is set. All of my error messages are prefixed with "Secondary" so it should be easy to tell if this PR causes any crash test issues.

Future work:
1. Extend this to followers. I think the same verification method should work, so most of the code from this PR should be reusable
2. Add additional checks to make sure the sequence number of the follower/secondary is actually increasing. For instance, if the primary's sequence number has advanced, and in that period the secondary has not (even after calling `TryCatchUpWithPrimary`), then we know there is a problem
3. Potentially checking things other than `Get()` for the secondary (i.e. iterators). I think the focus here should be testing replication-specific logic, and since we will already have separate unit tests, we do not need to repeat all of tests against both the primary and the secondary.

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

Test Plan:
The primary crash test commands I ran were:
```
python3 tools/db_crashtest.py --simple blackbox --test_secondary=1
python3 tools/db_crashtest.py --simple whitebox --test_secondary=1
```

As a sanity check, I added an `assert(false)` right after my secondary verification code to make sure that my code was actually being run.

Reviewed By: anand1976

Differential Revision: D67953821

Pulled By: archang19

fbshipit-source-id: 0bd853580ea53566be41639f5499eb9b5e0e9376
2025-01-24 15:05:15 -08:00
Levi Tamasi ac2ad2160d Repro a bug affecting UDTS+BlobDB+reverse iteration+allow_unprepared_value+max_sequential_skip_in_iterations (#13332)
Summary:
The patch adds a unit test that reproduces an issue we have been seeing in our stress tests that affects reverse iteration when BlobDB and user-defined timestamps are both enabled. If in addition to the above, lazy loading of blobs (`allow_unprepared_value`) is enabled and `max_sequential_skip_in_iterations` is exceeded during the reverse scan, calling `PrepareValue` can result in an error status (`Corruption: Key mismatch when reading blob`). We plan to fix the issue in a follow-up patch.

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

Reviewed By: jowlyzhang

Differential Revision: D68642615

fbshipit-source-id: a09b24e2dda6b5fa97ae576708ab278f540251bf
2025-01-24 14:06:54 -08:00
Levi Tamasi 4ac85f0a79 Add a public factory method for SecondaryIndexIterator (#13327)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13327

The patch adds a public API method `NewSecondaryIndexIterator` that can be leveraged by users providing their own `SecondaryIndex` implementations.

Reviewed By: jaykorean

Differential Revision: D68569198

fbshipit-source-id: 07f77837c3ce7ab8ea2d9bac172df3d64ce4f745
2025-01-23 15:40:20 -08:00
Levi Tamasi 35a27d859b Bring back the ability to leverage the primary key in secondary indices (#13324)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13324

There are actually some use cases which would benefit from the ability to use the primary key when forming the secondary key prefix or value. One such use case, which is demonstrated using a unit test, is building a secondary index on non-initial part(s) of the primary key. The patch adds back this ability, which was was removed in https://github.com/facebook/rocksdb/pull/13207, with a twist: the earlier `GetSecondaryKeyPrefix` is essentially split into two parts, with `GetSecondaryKeyPrefix` now being responsible only for computing whatever the secondary index is built on (let's call this "index function result") and a new `FinalizeSecondaryKeyPrefix` method having the responsibility of dealing with serialization concerns like adding a length indicator for disambiguation. This also means a slight change for the `SecondaryIndexIterator` class: it now treats its `Seek` argument as an "index function result" and thus only calls the new `FinalizeSecondaryKeyPrefix` on it (but not `GetSecondaryKeyPrefix`).

Reviewed By: jaykorean

Differential Revision: D68514201

fbshipit-source-id: d3750d049b0aee37e6c20edc19f5e4a0d3fce91e
2025-01-22 15:53:03 -08:00
Maciej Szeszko 0e469c7f99 Parallelize backup verification (#13292)
Summary:
Today, backup verification is serial, which could pose a challenge in rare, high urgency recovery scenarios where we want to timely assess whether candidate backup is not corrupted and eligible for the restore. The _timely_ part will become increasingly more important in case of disaggregated storage.

### Semantics
Given the very simple thread pool implementation in `backup_engine` today, we do not really have a control over initialized threads and consequently do not have an option to unschedule / cancel in-progress tasks. As a result, `VerifyBackup` won't bail out on a very first mismatch (as it was the case for serial implementation) and instead will iterate over all the files logging success / degree_of_failure for each. We _could_, in theory, not `.wait()` on remaining `std::future<WorkItem>`s (upon previously detected failure) and therefore decrease the observed API latency, but that _could_ cause more confusion down the road as verification threads would still be occupied with inflight/scheduled work and would not be reclaimed by the pool for a while. It's a tradeoff where we choose a solution with clear and intuitive semantics.

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

Test Plan:
Kudos to pdillinger who pointed out that we should already have appropriate fuzzing for max_background_operations and verify_checksum=true parameters in scope of ::VerifyBackup calls in existing backup restore stress test collateral.

[1]

https://github.com/facebook/rocksdb/blob/main/db_stress_tool/db_stress_test_base.cc#L1296

Reviewed By: pdillinger

Differential Revision: D68046714

Pulled By: mszeszko-meta

fbshipit-source-id: 980253174aa9dfd3064866a51c53345277e3a032
2025-01-21 13:39:45 -08:00
Changyu Bi f6c1489c7a Add a new TransactionDBOptions txn_commit_bypass_memtable_threshold (#13304)
Summary:
... to makes it easier to use the new transaction feature `commit_bypass_memtable`. Instead of needing to specify the option when creating a transaction, this option allows users to specify a threshold on the number of updates in a transaction to determine when to skip memtables writes for a transaction.

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

Test Plan: a new unit test for the new option

Reviewed By: pdillinger

Differential Revision: D68288579

Pulled By: cbi42

fbshipit-source-id: d3076629891d8b1d427878d20f0ac40dc0dadd35
2025-01-21 11:47:29 -08:00
anand76 5405835505 Update for next release 10.0.0 (#13313)
Summary:
Update HISTORY, version and folly hash for next month's 10.0.0 release

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

Reviewed By: pdillinger, mszeszko-meta

Differential Revision: D68362823

Pulled By: anand1976

fbshipit-source-id: 481ad999f6e8268b566ff8621c79d15f293b1df2
2025-01-17 23:24:55 -08:00
Maciej Szeszko 2257f4fae5 Native support for incremental restore (#13239)
Summary:
With this change we are adding native library support for incremental restores. When designing the solution we decided to follow 'tiered' approach where users can pick one of the three predefined, and for now, mutually exclusive restore modes (`kKeepLatestDbSessionIdFiles`, `kVerifyChecksum` and `kPurgeAllFiles` [default]) - trading write IO / CPU for the degree of certainty that the existing destination db files match selected backup files contents. New mode option is exposed via existing `RestoreOptions` configuration, which by this time has been already well-baked into our APIs. Restore engine will consume this configuration and infer which of the existing destination db files are 'in policy' to be retained during restore.

### Motivation

This work is motivated by internal customer who is running write-heavy, 1M+ QPS service and is using RocksDB restore functionality to scale up their fleet. Given already high QPS on their end, additional write IO from restores as-is today is contributing to prolonged spikes which lead the service to hit BLOB storage write quotas, which finally results in slowing down the pace of their scaling. See [T206217267](https://www.internalfb.com/intern/tasks/?t=206217267) for more.

### Impact
Enable faster service scaling by reducing write IO footprint on BLOB storage (coming from restore) to the absolute minimum.

### Key technical nuances

1. According to prior investigations, the risk of collisions on [file #, db session id, file size] metadata triplets is low enough to the point that we can confidently use it to uniquely describe the file and its' *perceived* contents, which is the rationale behind the `kKeepLatestDbSessionIdFiles` mode. To find more about the risks / tradeoffs for using this mode, please check the related comment in `backup_engine.cc`. This mode is only supported for SSTs where we persist the `db_session_id` information in the metadata footer.
2. `kVerifyChecksum` mode requires a full blob / SST file scan (assuming backup file has its' `checksum_hex` metadata set appropriately, if not additional file scan for backup file). While it saves us on write IOs (if checksums match), it's still fairly complex and _potentially_ CPU intensive operation.
3. We're extending the `WorkItemType` enum introduced in https://github.com/facebook/rocksdb/pull/13228 to accommodate a new simple request to `ComputeChecksum`, which will enable us to run 2) in parallel. This will become increasingly more important as we're moving towards disaggregated storage and holding up the sequence of checksum evaluations on a single lagging remote file scan would not be acceptable.
4. Note that it's necessary to compute the checksum on the restored file if corresponding backup file and existing destination db file checksums didn't match.

### Test plan  ✅

1. Manual testing using debugger: ✅
2. Automated tests:
* `./backup_engine_test --gtest_filter=*IncrementalRestore*` covering the following scenarios: ✅
  * Full clean restore
  * Integration with `exclude files` feature (with proper writes counting)
  * User workflow simulation: happy path with mix of added new files and deleted original backup files,
  * Existing db files corruptions and the difference in handling between `kVerifyChecksum` and `kKeepLatestDbSessionIdFiles` modes.
* `./backup_engine_test --gtest_filter=*ExcludedFiles*`  ✅
  * Integrate existing test collateral with newly introduced restore modes

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

Reviewed By: pdillinger

Differential Revision: D67513875

Pulled By: mszeszko-meta

fbshipit-source-id: 273642accd7c97ea52e42f9dc1cc1479f86cf30e
2025-01-17 21:28:46 -08:00
Peter Dillinger 602e19fd8d Deprecate raw DB pointer in public APIs (#13311)
Summary:
Offer new DB::Open and variants that use `std::unique_ptr<DB>*` output parameters and deprecate the old versions that use `DB**` output parameters.

This shouldn't have weird downstream effects because these are just static functions. (And a constructor for StackableDB)

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

Test Plan: existing tests

Reviewed By: anand1976

Differential Revision: D68340779

Pulled By: pdillinger

fbshipit-source-id: 30f4448398b479b5abecfc2406447f200a5fe073
2025-01-17 13:33:25 -08:00
Levi Tamasi c86d6c5317 Introduce a SecondaryIndexHelper class (#13312)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13312

The patch moves the `AsSlice` and `AsString` methods to a new `SecondaryIndexHelper` class to facilitate reuse and eliminate some code duplication.

Reviewed By: jaykorean

Differential Revision: D68342378

fbshipit-source-id: 9cb55bfd64a7db810898739dde01b128e15c81f4
2025-01-17 13:20:18 -08:00
Changyu Bi 0013acacc2 Fix manual compaction to try to not exceed max_compaction_bytes (#13306)
Summary:
CompactRange() currently [picks](https://github.com/facebook/rocksdb/blob/6e97a813dc8c4b574fa0743df3099b09e87af7e0/db/compaction/compaction_picker.cc?fbclid=IwZXh0bgNhZW0CMTEAAR08agyVwgQW-Tq5XApF52y9gw5UzmfIn3cEG44yvClFRIsTDH7zykfcb9Q_aem_23mjVBO1jSxQZ4_M4UyntA#L747-L753) input files until compaction just exceeds `max_compaction_bytes`. This can cause an overly large compaction in some cases. For example, consider the following example. If the size of L6 files are large, picking an additional L5 file F3 (after picking F2) can cause the compaction to be too big.
```
L5 F1[1,2] F2[3,4]                                        F3[1000, 1001]
L6                  [5,8][9,12]...[998,999]
```
This PR updates the file picking logic to try to keep compaction size under `max_compaction_bytes`.

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

Test Plan: a new unit test to test the above example

Reviewed By: jaykorean, archang19

Differential Revision: D68290846

Pulled By: cbi42

fbshipit-source-id: ffb4647002b47e5a92dd0a06afd4b4a4fbf94b7a
2025-01-17 11:14:14 -08:00
Roman Puchkovskiy b98c21b281 Support flush reasons above 12 in Java integration (#13246)
Summary:
FlushReason enum in C++ has members up to 15, but in Java, the mirroring FlushReason only supports reason codes up to 12. This causes exceptions when adding a flush listener.

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

Reviewed By: pdillinger

Differential Revision: D68241620

Pulled By: jaykorean

fbshipit-source-id: 1e2856dad28dff0cbb1772f5a8ea03cc1e224088
2025-01-17 10:04:10 -08:00
Jurriaan Mous 97988074f0 Add additional methods to C api for compactoptions (#13271)
Summary:
Add extra C API calls for:

- rocksdb_compactoptions_set_target_path_id
- rocksdb_compactoptions_get_target_path_id
- rocksdb_compactoptions_set_allow_write_stall
- rocksdb_compactoptions_get_allow_write_stall
- rocksdb_compactoptions_set_max_subcompactions
- rocksdb_compactoptions_get_max_subcompactions

And tests

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

Reviewed By: archang19

Differential Revision: D68289764

Pulled By: cbi42

fbshipit-source-id: bd67e6b7cf600e368ac3136e70438a8e994fa337
2025-01-16 15:46:37 -08:00
Jay Huh 4c617a4b13 Add Meta internal config file to gitignore (#13307)
Summary:
As title

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

Test Plan: CI

Reviewed By: cbi42

Differential Revision: D68289968

Pulled By: jaykorean

fbshipit-source-id: ddcdcb57ad53479f322b2492279d7eec74bb624d
2025-01-16 14:59:42 -08:00
Levi Tamasi 43bcee600a Add a factory function for FaissIVFIndex (#13305)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13305

The patch adds a public factory method `NewFaissIVFIndex` that can be used to create a FAISS inverted file based secondary index object. (Note that at the moment, FAISS secondary indices require using the Meta-internal BUCK build; this will be addressed in a follow-up patch.) As a small code organization improvement, the patch also moves `SecondaryIndexReadOptions` to its own header file.

Reviewed By: jaykorean

Differential Revision: D68284544

fbshipit-source-id: b46351c110589ec05606710452016deaa5028626
2025-01-16 14:31:09 -08:00
Peter Dillinger 780280b52e Reduce unnecessary MutableCFOptions copies and parameters (#13301)
Summary:
As follow-up to https://github.com/facebook/rocksdb/issues/13239, this change is primarily motivated by simplifying the calling conventions of LogAndApply. Since it must be called while holding the DB mutex, it can read safely read cfd->GetLatestMutableCFOptions(), until it releases the mutex within ProcessManifestWrites. Before it releases the mutex, it makes a copy of the mutable options in a new, unpublished Version object, which can be used when not holding the DB mutex. This eliminates the need for callers of LogAndApply to copy mutable options for its sake, or even specify mutable options at all. And it eliminates the need for *another* copy to be saved in ManifestWriter.

Other functions that don't need the mutable options parameter:
* ColumnFamilyData::CreateNewMemtable()
* CompactionJob::Install() / InstallCompactionResults()
* MemTableList::*InstallMemtable*()
* Version::PrepareAppend()

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

Test Plan: existing tests, CI with sanitizers

Reviewed By: mszeszko-meta

Differential Revision: D68234865

Pulled By: pdillinger

fbshipit-source-id: 6ce95f9cc479834e09ffc8ce93cbae7b664329e5
2025-01-16 14:10:11 -08:00
Levi Tamasi 77d4663447 Extend the test coverage of FaissIVFIndex (#13300)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13300

The patch adds a new unit test for `FaissIVFIndex` that compares its results with a regular in-memory FAISS index. Specifically, it trains two identical IVF indices using the same training vectors, passes the ownership of one to `FaissIVFIndex`, adds the same set of database vectors to both, and then queries them using the same query vectors (with a variety of values for number of neighbors and number of probes).

Reviewed By: jaykorean

Differential Revision: D68233815

fbshipit-source-id: 7577a65c03c7b811707a4dbcd81e69ed85202a51
2025-01-15 18:03:59 -08:00
Peter Dillinger b3333587eb Clean up some CFOptions code hygiene, fix SetOptions() bug (#13294)
Summary:
To start, I wanted to remove the unnecessary new_options parameter of `InstallSuperVersionAndScheduleWork()`. Passing it something other than the latest mutable options would be inconsistent/outdated. There was even a comment "Use latest MutableCFOptions" on a place that was using the saved options in effect for the compaction.

On investigation, this fixes an undiagnosed but longstanding serious bug in SetOptions() where the new settings can be reverted if a flush or compaction started before the SetOptions() finishes after. Fix confirmed with new unit test in db_test.cc.

I also got tired of seeing the cumbersome usage of pointer rather than const reference for related options accesses, so there's kind of a large (but trivial) refactoring tied in here as well. (Sorry for combining them; wasn't planning a major bug fix)

Intended follow-up: Clarify/simplify the crazy calling conventions of LogAndApply, and remove some unnecessary copying of MutableCFOptions (see new FIXMEs)

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

Test Plan: test for bug fix, confirmed fails on main and at least as far back as version 8.10. Plus existing tests and CI

Reviewed By: mszeszko-meta

Differential Revision: D68141563

Pulled By: pdillinger

fbshipit-source-id: f6c3290145afa06cc2fe8b485a5de17560a5deea
2025-01-15 13:11:40 -08:00
Levi Tamasi 1076caf5eb Mark secondary indices experimental, add release note (#13298)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/13298

Reviewed By: jaykorean

Differential Revision: D68220111

fbshipit-source-id: 51ec80d7804ea78d39d8c84aec615abb2034746f
2025-01-15 11:55:45 -08:00
Jay Huh f9791d44d0 Introduce CancelAwaitingJobs() API in CompactionService (#13286)
Summary:
Currently, when the primary instance shuts down, remote compaction continues to run and `CompactionService::Wait()` does not get aborted. This slows down `DB::Close()` as it waits for the completion of `CompactionService::Wait()`. Moreover, since shutdown has already begun, the compaction is unnecessary and will be wasted.

This PR introduces `CancelAwaitingJobs()` to the CompactionService interface. This allows users to implement cancellation of running remote compactions from the primary instance. When `CancelAllBackgroundWork()` is called on the primary instance, `CancelAwaitingJobs()` will be invoked, enabling a more efficient shutdown process.

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

Test Plan:
Unit Test added
```
./compaction_service_test --gtest_filter="*CancelCompactionOnPrimarySide*"
```

Reviewed By: anand1976, cbi42

Differential Revision: D68035191

Pulled By: jaykorean

fbshipit-source-id: 47da641f7cbed1267f0a1f16924f57efde46216d
2025-01-15 11:55:33 -08:00
Levi Tamasi 5938ad270b Add support for Delete/SingleDelete with secondary indices (#13291)
Summary:
The patch implements support for `Delete` and `SingleDelete` with secondary indices, leveraging the earlier pieces built for `Put` / `PutEntity`. As expected, deleting an entry using these APIs also deletes any associated secondary index entries.

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

Reviewed By: jaykorean

Differential Revision: D68041422

fbshipit-source-id: c8afc9ff69dea834f89ae855a72c1d76e7db0e35
2025-01-14 16:20:05 -08:00
Levi Tamasi 8bccd39bfd Add support for Put with secondary indices (#13289)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13289

The patch adds support for `Put` / `PutUntracked` to the secondary indexing logic. Similarly to `PutEntity` (see https://github.com/facebook/rocksdb/pull/13180), calling these APIs automatically add or remove secondary index entries as needed in an atomic and transparent fashion.

Reviewed By: jaykorean

Differential Revision: D68035089

fbshipit-source-id: db37bce62151ae1909b46b1020592c8348156653
2025-01-14 12:33:06 -08:00
Maciej Szeszko 6e97a813dc Deprecate db delete file public API (#13284)
Summary:
We added a removal warning for public `DB::DeleteFile` API ~4 years ago in https://github.com/facebook/rocksdb/pull/7337. This API seems to sit at wrong layer of abstraction, where instead of exposing a clear interface to delete specific range of keys, callers rely on their own discovery / interpretation of where their data / log possibly resides 'as-of-now'. For example, in case of data, the physical location of the keys might very well change after user obtained their mapping from key(s) to specific SST file. This will lead to `InvalidArgument` response, which if repeated, would put a user in a race condition spinning wheel - the behavior that's inefficient, fairly indeterministic and therefore one that should be strongly discouraged. We're employing a graceful approach to prefixing the public API with `DEPRECATED_` first for better discoverability and ease of self service for product teams should they still use that legacy API. If everything goes smoothly, we intend to remove all the deprecated API references in the next release.

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

Reviewed By: pdillinger

Differential Revision: D67981502

Pulled By: mszeszko-meta

fbshipit-source-id: adc7fe5cf4e2180bcfd21878b8f78f3fb6ead355
2025-01-10 19:07:33 -08:00
Maciej Szeszko 541761eaaa Deprecate random access max buffer size references - take #2 (#13288)
Summary:
This time properly marking db option as `kDeprecated` in `db_options.cc`. Original PR: https://github.com/facebook/rocksdb/pull/13278.

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

Reviewed By: pdillinger

Differential Revision: D68024379

Pulled By: mszeszko-meta

fbshipit-source-id: 8e1f08b048ccf5971d899811edaf0b0ef16581ef
2025-01-10 15:32:38 -08:00
anand76 3040868e5f Don't fail crash test if cleanup cmd fails after successful test (#13287)
Summary:
The warm storage crash test sometimes fails due to the cleanup command failing if the db_stress exited successfully and we already cleaned up. This results in false alarms. Don't treat a cleanup command failure as crash test failure.

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

Reviewed By: archang19

Differential Revision: D68023398

Pulled By: anand1976

fbshipit-source-id: f95fff030a5ea8eb7d2dfb248d08d7876e2de2b2
2025-01-10 11:29:53 -08:00
Maciej Szeszko 6c6defe3b8 Revert "Deprecate random access max buffer size references (#13278)" (#13285)
Summary:
This reverts commit d4bd67fb09. There are total of 4 call sites as referenced [here](https://www.internalfb.com/code/search?q=repo%3Aall%20-filepath%3Afbcode%2Frocksdb%2F%7Cthird-party%2Frocksdb%2F%7Clibrocksdb%2F%7Cfb_mysql%2F.*%2Frocksdb%7Cinternal_repo_rocksdb%20regex%3Aon%20random_access_max_buffer_size&lang_filter=cpp). None of them have a strict reliance of this setting, which should make followup cleanup fairly easy.

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

Reviewed By: pdillinger

Differential Revision: D67984325

Pulled By: mszeszko-meta

fbshipit-source-id: 12c0b6281c2af6c32261fdf6092856b0566d389e
2025-01-09 13:20:30 -08:00
Maciej Szeszko 44b741e9cc Clean up obsolete code in BlockBasedTable::PrefetchIndexAndFilterBlocks (#13277)
Summary:
As advertised and recommended by original authors comment, we're removing the now-outdated special handling logic for bloom filters perf regression (timing ~release 7.0.X). I decided to keep the `CompatibilityName` as-is since 1) it's publicly exposed API and 2) it's generally useful to have a dedicated name used for identifying whether a filter on disk is readable by the FilterPolicy.

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

Test Plan:
'Dead code' / tech debt. As a smoke test, I manually run a similar benchmark to the one in https://github.com/facebook/rocksdb/pull/9736, with ./db_bench built pre and post change.

**Generate DB:**

```hcl
./db_bench -db=/dev/shm/rocksdb.9.11 -bloom_bits=10 -cache_index_and_filter_blocks=1 -benchmarks=fillrandom -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=10000 -fifo_compaction_allow_compaction=0
```

**Before removing the 'if' block:**

```hcl
./db_bench -db=/dev/shm/rocksdb.9.11 -use_existing_db -readonly -bloom_bits=10 -benchmarks=readrandom -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=10000 -fifo_compaction_allow_compaction=0 -duration=10 2>&1 | grep micros/op

readrandom   :      17.216 micros/op 58085 ops/sec 10.002 seconds 580999 operations;    4.1 MB/s (367256 of 580999 found)
```

**After removing the 'if' block:**

```hcl
./db_bench -db=/dev/shm/rocksdb.9.11 -use_existing_db -readonly -bloom_bits=10 -benchmarks=readrandom -num=10000000 -compaction_style=2 -fifo_compaction_max_table_files_size_mb=10000 -fifo_compaction_allow_compaction=0 -duration=10 2>&1 | grep micros/op

readrandom   :      16.776 micros/op 59607 ops/sec 10.015 seconds 596999 operations;    4.2 MB/s (377846 of 596999 found)
```

Reviewed By: jaykorean, pdillinger

Differential Revision: D67908020

Pulled By: mszeszko-meta

fbshipit-source-id: b904b8eaf9d106f0b47e4ff175242795ac1c5e73
2025-01-08 18:03:46 -08:00
Andrew Chang b5e4162582 Unify compaction prefetching logic (#13187)
Summary:
In https://github.com/facebook/rocksdb/pull/13177, I discussed an unsigned integer overflow issue that affects compaction reads inside `FilePrefetchBuffer` when we attempt to enable the file system buffer reuse optimization. In that PR, I disabled the optimization whenever `for_compaction` was `true` to eliminate the source of the bug.

**This PR safely re-enables the optimization when `for_compaction` is `true`.** We need to properly set the overlap buffer through `PrefetchInternal` rather than simply calling `Prefetch`. `Prefetch` assumes `num_buffers_` is 1 (i.e. async IO is disabled), so historically it did not have any overlap buffer logic. What ends up happening (with the old bug) is that, when we try to reuse the file system provided buffer, inside the `Prefetch` method, we read the remaining missing data. However, since we do not do any `RefitTail` method when `use_fs_buffer` is true, normally we would rely on copying the partial relevant data into an overlap buffer. That overlap buffer logic was missing, so the final main buffer ends up storing data from an offset that is greater than the requested offset, and we effectively end up "throwing away" part of the requested data.

**This PR also unifies the prefetching logic for compaction and non-compaction reads:**
- The same readahead size is used. Previously, we read only `std::max(n, readahead_size_)` bytes for compaction reads, rather than `n + readahead_size_` bytes
- The stats for `PREFETCH_HITS` and `PREFETCH_BYTES_USEFUL` are tracked for both. Previously, they were only tracked for non-compaction reads.

These two small changes should help reduce some of the cognitive load required to understand the codebase. The test suite also became easier to maintain. We could not come up with good reasons why the logic for the readahead size and stats should be different for compaction reads.

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

Test Plan:
I removed the temporary test case from https://github.com/facebook/rocksdb/issues/13200 and incorporated the same test cases into my updated parameterized test case, which tests the valid combinations between `use_async_prefetch` and `for_compaction`.

I went further and added a randomized test case that will simply try to hit `assert`ion failures and catch any missing areas in the logic.

I also added a test case for compaction reads _without_ the file system buffer reuse optimization. I am thinking that it may be valuable to make a future PR that unifies a lot of these prefetch tests and parametrizes as much of them as possible. This way we can avoid writing duplicate tests and just look over different parameters for async IO, direct IO, file system buffer reuse, and `for_compaction`.

Reviewed By: anand1976

Differential Revision: D66903373

Pulled By: archang19

fbshipit-source-id: 351b56abea2f0ec146b83e3d8065ccc69d40405d
2025-01-08 15:22:05 -08:00
Maciej Szeszko d4bd67fb09 Deprecate random access max buffer size references (#13278)
Summary:
This option has been officially deprecated in 5.4.0. We're removing all the references to `random_access_max_buffer_size`, related rules and all the clients wrappers. As a part of this refactoring, we're also getting rid of the `options-1-false` (and consequently its' `multiple-conds-all-false` corresponding rule), as condition would not make much sense anymore without the bounding RA max buffer size limit. Motivated by ongoing tech debt reduction effort.

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

Test Plan: Validated that internal users do not rely on this long-gone option in their workflows.

Reviewed By: jaykorean

Differential Revision: D67909674

Pulled By: mszeszko-meta

fbshipit-source-id: 8f4b59a4a92b0b32b8b91b71ac318aafc17f1da2
2025-01-08 09:59:18 -08:00
Peter Dillinger b341dc8b05 Fix possible out-of-order/inconsistent seqno-to-time mapping (#13279)
Summary:
The crash test with COERCE_CONTEXT_SWITCH=1 is showing a failure:

```
db_stress: db/seqno_to_time_mapping.cc:480: bool rocksdb::SeqnoToTimeMapping::Append(rocksdb::SequenceNumber, uint64_t): Assertion `false' failed.
```

with `DBImpl::SetOptions()` in the call stack. This assertion and those around it are mostly there for catching systematic problems with recording the mappings, as small imprecisions here and there are not a problem in production. Nevertheless, we need to fix this to maintain the assertions for catching possible future systematic problems.

Because the seqno and time are acquired before holding the DB mutex, there could be a race where T1 acquires latest seqno, T1 acquires latest seqno, T2 acquires unix time, T1 acquires unix time, and entries are not just saved out-of-order, but would represent an inconsistent (time traveling) mapping if they were saved.

We can fix this by getting the seqno and unix times while under the mutex. (Hopefully this is not caused by non-monotonic clock adjustments.)

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

Test Plan: local run blackbox_crash_test with COERCE_CONTEXT_SWITCH=1. This is not really a production concern, and the conditions are not really reproducible in a unit test after the fix.

Reviewed By: cbi42

Differential Revision: D67923314

Pulled By: pdillinger

fbshipit-source-id: 6bfb6b05d6d449154fbaeb9196eedcfa21fe5ae1
2025-01-07 18:25:21 -08:00
Alan Paxton 9b1d0c02e9 Add [set]DailyOffpeakTimeUTC option to Java API (#13148)
Summary:
Reflect RocksDB DailyOffpeakTimeUTC  option in Java API. As is standard for options, there are a number of different places where this option needs to be added: it is an option, a DB option, and it is mutable (can be changed while running).

The new option is a string value. This requires an extension to the internal MutableDBOptions parse code, which received the entire options string from C++ and parses it on the Java side.

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

Reviewed By: cbi42

Differential Revision: D67870402

Pulled By: jaykorean

fbshipit-source-id: 975af69773206da936d230cbadb5f69a002d92a3
2025-01-07 09:39:01 -08:00
Levi Tamasi c2de7832bc Support KNN search for FAISS IVF indices (#13258)
Summary:
The patch is the read-side counterpart of https://github.com/facebook/rocksdb/pull/13197 . It adds support for K-nearest-neighbor vector similarity searches to `FaissIVFIndex`. There are two main pieces to this:

1) `KNNIterator` is an `Iterator` implementation that is returned by `FaissIVFIndex` upon a call to `NewIterator`. `KNNIterator` treats its `Seek` target as a vector embedding and passes it to FAISS along with the number of neighbors requested `k` as well as the number of probes to use (i.e. the number of inverted lists to check). Applications can then use `Next` (and `Prev`) to iterate over the the vectors in the result set. `KNNIterator` exposes the primary keys associated with the result vectors (see below how this is done), while `value` and `columns` are empty. The iterator also supports a property `rocksdb.faiss.ivf.index.distance` that can be used to retrieve the distance/similarity metric for the current result vector.
2) `IteratorAdapter` takes a RocksDB secondary index iterator (see https://github.com/facebook/rocksdb/pull/13257) and adapts it to the interface required by FAISS (`faiss::InvertedListsIterator`), enabling FAISS to read the inverted lists stored in RocksDB. Since FAISS only supports numerical vector ids of type `faiss::idx_t`, `IteratorAdapter` uses `KNNIterator` to assign ephemeral (per-query) ids to the inverted list items read during iteration, which are later mapped back to the original primary keys by `KNNIterator`.

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

Reviewed By: jaykorean

Differential Revision: D67684898

fbshipit-source-id: 5b5c4c438deb86b35d5d45262ce290caee083bca
2025-01-06 11:39:31 -08:00
Peter Dillinger 09d7f6a5c2 Temporary work around nullptr seqno_to_time_mapping in FlushJob (#13269)
Summary:
To resolve a crash test failure in
`FlushJob::GetPrecludeLastLevelMinSeqno()`

To fix this properly, I will work on ensuring that (a) FlushJob is created with a consistent view on mutable options and seqno_to_time_mapping (from a single SuperVersion) and (b) SuperVersions always have a non-null seqno_to_time_mapping when a relevant option is set.

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

Test Plan: watch crash test

Reviewed By: ltamasi

Differential Revision: D67843008

Pulled By: pdillinger

fbshipit-source-id: cedbac4b2255398eefade46240c5481b57a98b1e
2025-01-05 21:52:43 -08:00
Peter Dillinger 631b6796b0 Rework, simplify some tiering logic for mutable options (#13256)
Summary:
The primary goal of this change was to support full dynamic mutability of options `preclude_last_level_data_seconds` and `preserve_internal_time_seconds`, which was challenging because of subtle design holes referenced from https://github.com/facebook/rocksdb/issues/13124.

The fix is, in a sense, "doubling down" on the idea of write-time-based tiering, by simplifying the output level decision with a single sequence number threshold. This approach has some advantages:
* Allows option mutability in presence of long snapshots (or UDT)
* Simpler to believe correct because there's no special treatment for range tombstones, and output level assignment does not affect sequence number assignment to the entries (which takes some care to avoid circular dependency; see CompactionIterator stuff below).
* Avoids extra key comparisons, in `WithinPenultimateLevelOutputRange()`, in relevant compactions (more CPU efficient, though untested).

There are two big pieces/changes to enable this simplification to a single `penultimate_after_seqno_` threshold:
* Allow range tombstones to be sent to either output level, based on sequence number.
* Use sequence numbers instead of range checks to avoid data in the last level from moving to penultimate level outside of the permissable range on that level (due to compaction selecting wider range in the later input level, which is the normal output level). With this change, data can only move "back up the LSM" when entire sorted runs are selected for comapction.

Possible disadvantages:
* Extra CPU to iterate over range tombstones in relevant compactions *twice* instead of once. However, work loads with lots of range tombstones relative to other entries should be rare.
* Data might not migrate back up the LSM tree on option changes as aggressively or consistently. This should a a rare concern, however, especially for universal compaction where selecting full sorted runs is normal compaction.
* This approach is arguably "further away from" a design that allows for other kinds of output level placement decisions, such as range-based input data hotness. However, properly handling range tombstones with such policies will likely require flexible placement into outputs, as this change introduces.

Additional details:
* For good code abstraction, separate CompactionIterator from the concern of where to place compaction outputs. CompactionIterator is supposed to provide a stream of entries, including the "best" sequence number we can assign to those entries. If it's safe and proper to zero out a sequence number, the placement of entries to outputs should deal with that safely rather than having complex inter-dependency between sequence number assignment and placement. To achieve this, we migrate all the compaction output placement logic that was in CompactionIterator to CompactionJob and similar. This unfortunately renders some unit tests (PerKeyPlacementCompIteratorTest) depending on the bad abstraction as obsolete, but tiered_compaction_test has pretty good coverage overall, catching many issues during this development.

Intended follow-up:
* See FIXME items in tiered_compaction_test
* More testing / validation / support for tiering + UDT
* Consider generalizing this work to split results at other levels as appropriate based on stats (auto-tuning essentially). Allowing only the last level to be cold is limiting.

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

Test Plan: tests were added in previous changes (https://github.com/facebook/rocksdb/issues/13244 #13124), and updated here to reflect correct operation (with some known problems for leveled compaction)

Reviewed By: cbi42

Differential Revision: D67683210

Pulled By: pdillinger

fbshipit-source-id: ca3f2bbc2fcc6891516a2a4220f1b0da09af5ade
2025-01-03 09:39:07 -08:00
Andrew Chang f7d32c18a3 Add io_buffer_size to BackupEngineOptions (#13236)
Summary:
The RocksDB backup engine code currently derives the IO buffer size based on the following criteria:

1. If specified, use the rate limiter burst size
2. Otherwise, use the default size (5 MiB)

We want to be able to explicitly choose the IO size based on the storage backend. We want the new criteria to be:

1. If specified, use the size in `BackupEngineOptions`
2. If specified, use the rate limiter burst size
3. Otherwise, use the default size (5 MiB)

This PR adds a new option called `io_buffer_size` to `BackupEngineOptions` and updates the logic used to set the buffer size.

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

Test Plan:
I added a separate unit test and verified that we can either use the `io_buffer_size`, rate limiter burst size, or the default size.

I decided to use a `TEST_SYNC_POINT_CALLBACK`. I considered the alternative of updating the `Read` implementation of `DummySequentialFile` / `CheckIOOptsSequentialFile` to check the value of `n`. However, that would have considerably complicated the whole test code, and we also do not need to be checking for this in every single test case. I think the `TEST_SYNC_POINT_CALLBACK` turned out to be quite elegant.

Reviewed By: sushilpa

Differential Revision: D67765000

Pulled By: archang19

fbshipit-source-id: 2122fab7379335de44ba4423af47aa0563635688
2025-01-02 16:19:10 -08:00
Levi Tamasi 3579d323c0 Add a new interface method SecondaryIndex::NewIterator to enable querying the index (#13257)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13257

The patch adds a new API `NewIterator` to `SecondaryIndex`, which should return an iterator that can be used by applications to query the index. This method takes a `ReadOptions` structure, which can be used by applications to provide (implementation-specific) query parameters to the index, and an underlying iterator, which should be an iterator over the index's secondary column family, and is expected to be leveraged by the returned iterator to read the actual secondary index entries. (Providing the underlying iterator this way enables querying the index as of a specific point in time for example.)

Querying the index can be performed by calling the returned iterator's `Seek` API with a search target, and then using `Next` (and potentially `Prev`) to iterate through the matching index entries. `SeekToFirst`, `SeekToLast`, and `SeekForPrev` are not expected to be supported by the iterator. The iterator should expose primary keys, that is, the secondary key prefix should be stripped from the index entries.

The exact semantics of the returned iterator depend on the index and are implementation-specific. For simple indices, the search target might be a primary column value, and the iterator might return all primary keys that have the given column value. (This behavior can be achieved using the new class `SecondaryIndexIterator`.) However, other semantics are also possible: for vector indices, the search target might be a vector, and the iterator might return similar vectors from the index. (This will be implemented for `FaissIVFIndex` in a subsequent patch.)

Reviewed By: jaykorean

Differential Revision: D67684777

fbshipit-source-id: 59bc33919405a3e9e316a1fa4790c1708788eb85
2025-01-02 14:23:05 -08:00
Changyu Bi d2db80caa2 Try to deflake unit test RoundRobinSubcompactionsAgainstPressureToken (#13254)
Summary:
unit test `RoundRobinSubcompactionsAgainstPressureToken.PressureTokenTest` has been [flaky](https://github.com/facebook/rocksdb/actions/runs/12220443012/job/34088263578?fbclid=IwZXh0bgNhZW0CMTEAAR3Vi0p8xxzU1tSpvaeB0RfP_97nOMiONGyZbhdcnN8IXW4tChNVHN3iIhc_aem_SGy-iqplt0GaEHAel_BGQQ). num_planned_subcompactions can be 1 for two reasons: compactions not having enough input files or that there were not enough bg threads. This PR updates the test to try to trigger a larger compaction for subcompactions, and added a callback to verify compactions have enough input files.

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

Test Plan: monitor future failure.

Reviewed By: hx235

Differential Revision: D67764944

Pulled By: cbi42

fbshipit-source-id: 4fc9c0bef76c8bfaa54be4f3d78071e2bebee8aa
2025-01-02 12:10:31 -08:00
jonahgao 00a3eb6990 Remove unused variable: number_of_files_to_sort_ (#13259)
Summary:
This variable is not used, the one actually being used is `kNumberFilesToSort`.
https://github.com/facebook/rocksdb/blob/02b4197544f758bdf84d80fe9319238611848c48/db/version_set.h#L544-L549

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

Reviewed By: archang19

Differential Revision: D67765254

Pulled By: cbi42

fbshipit-source-id: 3c32b0396d0ba9e08e77b96b098e6c26d0bf8aac
2025-01-02 11:34:58 -08:00
Hui Xiao 601a6b59e8 Temporarily disable track_and_verify_wals with write related injection (#13263)
Summary:
**Context/Summary:**

After https://github.com/facebook/rocksdb/pull/13226, our crash test appears to find a WAL hole caused by mishandling of an injected error during writing the buffer in writable file writer into the underlying log file. It will take some time for me to fully root-cause and fix it. Before then, let's disable this combination.

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

Test Plan: Monitor crash test

Reviewed By: ltamasi

Differential Revision: D67755485

Pulled By: hx235

fbshipit-source-id: 5f7bb422f7722c2696872232b1fed8ffa5c0f4c3
2025-01-02 11:30:50 -08:00
Changyu Bi 159fa7741d Fix an assertion failure in error handler (#13251)
Summary:
we saw this [assertion](https://github.com/facebook/rocksdb/blob/02b4197544f758bdf84d80fe9319238611848c48/db/error_handler.cc#L576) failing in crash test. The LOG shows that there's a call to SetOptions() concurrent to ResumeImpl(). It's possible that while waiting for error recovery flush (with mutex released), SetOptions() failed to write to MANIFEST and added a file to be quarantined. This triggered the assertion failure when ResumeImpl() calls ClearBGError().

This PR fixes the issue by setting background error when SetOptions() fails to write to MANIFEST.

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

Test Plan: monitor future crash test failures.

Reviewed By: hx235

Differential Revision: D67660106

Pulled By: cbi42

fbshipit-source-id: 1b52bb23005c4b544f8f9bceefd3b9dcbaf0edfa
2025-01-02 10:51:02 -08:00
Peter Dillinger e48ccc28f4 Reduce unnecessary manifest data when no file checksum (#13250)
Summary:
Don't write file checksum manifest entries when unused, to avoid using extra manifest file space.

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

Test Plan: very minor performance improvement, existing tests

Reviewed By: cbi42

Differential Revision: D67653954

Pulled By: pdillinger

fbshipit-source-id: 9156e093ed5e4a5152cc55354a4beea9a841b89f
2025-01-02 10:48:46 -08:00
Vaqxai 62a7ddb39c Reapply fix for https://github.com/facebook/rocksdb/issues/13166 (#13265)
Summary:
There was a fix in https://github.com/facebook/rocksdb/pull/13171 for issue https://github.com/facebook/rocksdb/issues/13166 but it was overwritten by commit https://github.com/facebook/rocksdb/commit/d5345a8ff72c05c3d014fb18bed030d60d1d8e4d. This PR is to reapply the fix.

Fixes https://github.com/facebook/rocksdb/issues/13264 to have Rocks compile under Ubuntu again

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

Reviewed By: archang19

Differential Revision: D67764706

Pulled By: cbi42

fbshipit-source-id: c8822fff6769bf3be1e6ceee3f2b58b09d8569b3
2025-01-02 10:42:20 -08:00
Levi Tamasi 3570e4f5ff Remove the primary_key parameter of SecondaryIndex::GetSecondary{KeyPrefix,Value} (#13207)
Summary:
The patch tweaks the new `SecondaryIndex` interface a bit by removing the `primary_key` parameter of `GetSecondaryKeyPrefix` and `GetSecondaryValue`. This parameter is currently unused by existing implementations and it actually does not make sense to have the secondary index prefix depend on the primary key since it would lead to potential chicken-and-egg problems at query time.

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

Reviewed By: jaykorean

Differential Revision: D67184936

fbshipit-source-id: 5707a35225a0160132e5e87e9fe6c36bee5eada1
2024-12-30 09:18:58 -08:00
Hui Xiao 02b4197544 Detect WAL hole (#13226)
Summary:
**Context/Summary:**

This PR provides a new Options `track_and_verify_wals` to detect and handle WAL hole where new WAL data presents while some old WAL data is missing as well as db opened with no WAL. It's for https://github.com/facebook/rocksdb/issues/12488.

It's intended to be a future replacement to `track_and_verify_wals_in_manifest` for its simplicity, better handling of WAL hole in  `WALRecoveryMode::kPointInTimeRecovery` and potentials to cover more scenarios for `WALRecoveryMode::kTolerateCorruptedTailRecords/kAbsoluteConsistency`(in future PRs).

The verification is done in `LogReader::MaybeVerifyPredecessorWALInfo()` and tracking is done in `log::Writer::MaybeAddPredecessorWALInfo()`. This PR also groups common utilities in `log::Writer` into functions  `MaybeHandleSeenFileWriterError()`, `MaybeSwitchToNewBlock()` to avoid adding redundant code

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

Test Plan:
- New UT
- Integrate into existing UT
- Intense rehearsal stress/crash test
- db bench
   - The only potential performance implication it has is to the write path since now we keep track of the last seqno recorded in the WAL in `log::Writer`. Below benchmark show no regression.
```
./db_bench --benchmarks=fillrandom[-X3] --num=2500000 --db=/dev/shm/db_bench_new --disable_auto_compactions=1 --threads=1 --enable_pipelined_write=0 --disable_wal=0 --track_and_verify_wals=1

Pre
fillrandom [AVG    3 runs] : 310517 (± 5641) ops/sec;   34.4 (± 0.6) MB/sec
fillrandom [MEDIAN 3 runs] : 308848 ops/sec;   34.2 MB/sec

Post
fillrandom [AVG    3 runs] : 311469 (± 4096) ops/sec;   34.5 (± 0.5) MB/sec
fillrandom [MEDIAN 3 runs] : 311961 ops/sec;   34.5 MB/sec
```

Reviewed By: pdillinger

Differential Revision: D67550260

Pulled By: hx235

fbshipit-source-id: 623e29bbe293ef03a45c20c348f84c8cb5bdaf91
2024-12-26 13:20:35 -08:00
Hui Xiao e3024e7b58 Verify flushed data are recovered upon reopen in crash test (#12787)
Summary:
**Context/Summary:**

This is to solve https://github.com/facebook/rocksdb/issues/12152. We persist the largest flushed seqno before crash just like how we persist the ExpectedState. And we verify the db lates seqno after recovery is no smaller than this flushed seqno.

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

Test Plan:
- Manually observe that the persisted sequence after flush completion is used to verify db's latest sequence
- python3 tools/db_crashtest.py --simple blackbox --interval=30
- CI

Reviewed By: archang19

Differential Revision: D58860150

Pulled By: hx235

fbshipit-source-id: 99cb4403964d0737908855f92af7327867079e3e
2024-12-24 16:27:35 -08:00
Peter Dillinger 30d5162298 Universal test for RangeTombstoneSnapshotMigrateFromLast, refactor (#13244)
Summary:
* Expand RangeTombstoneSnapshotMigrateFromLast in tiered_compaction_test (originally from https://github.com/facebook/rocksdb/issues/13124) to reproduce a failure in universal compaciton (as well as leveled), when a specific part of the test is uncommented.
* Small refactoring to eliminate unnecessary fields in SubcompactionState. Adding a bool parameter to SubcompactionState::AddToOutput here will make more sense in the next PR (which I'm trying to keep
from getting too big).
* Improve debuggability and performance of some other tests
* Remove accidentally committed test "BlahPrecludeLastLevel" which was a temporary copy of CompactionServiceTest.PrecludeLastLevel

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

Test Plan: existing tests, updated/expanded tests

Reviewed By: cbi42

Differential Revision: D67605076

Pulled By: pdillinger

fbshipit-source-id: 9be83c2173f77545b5fe17ff9dc67db497c7afc9
2024-12-23 12:00:36 -08:00
Maciej Szeszko 18cecb9c46 Properly propagate the result io_status handle upstream (#13238)
Summary:
Followup to https://github.com/facebook/rocksdb/pull/13228. This fix is not a critical one in a sense that `else`-branch is only supposed to act as a guard just in case when new work item type is being introduced, scheduled but not handled. However, we're in control of the work item types and currently we only support a single one (which has appropriate handling logic to it).

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

Reviewed By: pdillinger

Differential Revision: D67512001

Pulled By: mszeszko-meta

fbshipit-source-id: 71e74b3dac388882dd3757871f500c334667fbd1
2024-12-20 14:45:52 -08:00
Yu Zhang 19e4aba3db Fix flaky test asserting concurrent write thread's waiting time (#13241)
Summary:
This test assertion was added in https://github.com/facebook/rocksdb/issues/13219. It checks the concurrent write thread's wait time is not longer than the file ingestion thread's write blocking time since the former entered the write thread after the blocking already started in the test. This test runs into flakiness like this:
```db/external_sst_file_basic_test.cc:300: Failure
Expected: (perf_context.file_ingestion_blocking_live_writes_nanos) > (write_thread_perf_context->write_thread_wait_nanos), actual: 166210 vs 279681
```
 In reality the write thread is yielding starting with a 1 micro period and then every 100 micros: https://github.com/facebook/rocksdb/blob/54b614de5bd3e26d332b85557d44bde86b2a2e87/db/write_thread.cc#L68-L70

So this 113 micros errors is within this margin
This fix the test with just removing this assertion. The other assertion `ASSERT_GT(write_thread_perf_context->write_thread_wait_nanos, 0)` should be sufficient for the test's purpose.

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

Reviewed By: hx235

Differential Revision: D67526804

Pulled By: jowlyzhang

fbshipit-source-id: 23ee9771247e4c13444054a1e86ad9293902cb56
2024-12-20 11:19:36 -08:00
Peter Dillinger 54b614de5b Some tiered storage refactorings setting up more work (#13230)
Summary:
* Simplify some testing callbacks for tiered_compaction_test ahead of some significant functional updates.
* Refactor CompactionJob::Prepare() for sharing with CompactionServiceCompactionJob. This is a minor functional change in computing preserve/preclude sequence numbers for remote compaction, but it is a start toward support for tiered storage with remote compaction. A test is added that is only partly working but does check that outputs are being split (just not to the correct levels).

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

Test Plan: mostly test changes and additions. Arguably makes tiered storage + remote compaction MORE broken as a step toward supporting it.

Reviewed By: jaykorean

Differential Revision: D67493682

Pulled By: pdillinger

fbshipit-source-id: fd6db74e08ef0e4fc7fdd599ff8555aab0c8ddc4
2024-12-19 20:52:42 -08:00
Hui Xiao cf768a2f9e Begin forward compatibility for WAL entry (#13225)
Summary:
**Context/Summary:**

This PR made WAL reader safely ignore type that is greater than some hard-coded value so old code can ignore new WAL
entry developed by new code.

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

Test Plan:
Manually run crash test alternating on old (this PR) and new code (this PR + a new WAL entry kPredecessorWALInfo https://github.com/hx235/rocksdb/commit/37e8b1ad59e678796d6e6b0cb12bae654405ffe3)

```
Running db_stress with pid=3201845: /data/users/huixiao/rocksdb/db_stress_new ....

KILLED 3201845

Running db_stress with pid=3203911: /data/users/huixiao/rocksdb_2/rocksdb/db_stress_old ...

KILLED 3203911

Running db_stress with pid=3218107: /data/users/huixiao/rocksdb/db_stress_new ....

```

db_crashtest.py
```
 diff --git a/tools/db_crashtest.py b/tools/db_crashtest.py
index 59912dbe9..b1d505367 100644
 --- a/tools/db_crashtest.py
+++ b/tools/db_crashtest.py
@@ -1074,6 +1072,10 @@ def gen_cmd_params(args):

 def gen_cmd(params, unknown_params):
     finalzied_params = finalize_and_sanitize(params)
+    if random.randint(0, 1) == 0:
+        stress_cmd = "/data/users/huixiao/rocksdb/db_stress_new"
+    else:
+        stress_cmd = "/data/users/huixiao/rocksdb_2/rocksdb/db_stress_old"
     cmd = (
         [stress_cmd]
         + [
```

New code
https://github.com/hx235/rocksdb/commit/37e8b1ad59e678796d6e6b0cb12bae654405ffe3
```
 --- a/db_stress_tool/db_stress_test_base.cc
+++ b/db_stress_tool/db_stress_test_base.cc
@@ -4117,7 +4117,7 @@ void InitializeOptionsFromFlags(
   options.level_compaction_dynamic_level_bytes =
       FLAGS_level_compaction_dynamic_level_bytes;
   options.track_and_verify_wals_in_manifest = true;
-  options.track_and_verify_wals = FLAGS_track_and_verify_wals;
+  options.track_and_verify_wals = true;
   options.verify_sst_unique_id_in_manifest =
       FLAGS_verify_sst_unique_id_in_manifest;
```

Reviewed By: pdillinger

Differential Revision: D67434637

Pulled By: hx235

fbshipit-source-id: 94245d090ed035a0e2a658804d3d856af453bbad
2024-12-19 18:34:23 -08:00
Changyu Bi cc30226b1f Deflake unit test DBErrorHandlingFSTest.AtomicFlushNoSpaceError (#13234)
Summary:
`DBErrorHandlingFSTest.AtomicFlushNoSpaceError` is flaky due to seg fault during error recovery:
```
...
frame https://github.com/facebook/rocksdb/issues/5: 0x00007f0b3ea0a9d6 librocksdb.so.9.10`rocksdb::VersionSet::GetObsoleteFiles(std::vector<rocksdb::ObsoleteFileInfo, std::allocator<rocksdb::ObsoleteFileInfo>>*, std::vector<rocksdb::ObsoleteBlobFileInfo, std::allocator<rocksdb::ObsoleteBlobFileInfo>>*, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>*, unsigned long) [inlined] std::vector<rocksdb::ObsoleteFileInfo, std::allocator<rocksdb::ObsoleteFileInfo>>::begin(this=<unavailable>) at stl_vector.h:812:16
frame https://github.com/facebook/rocksdb/issues/6: 0x00007f0b3ea0a9d6 librocksdb.so.9.10`rocksdb::VersionSet::GetObsoleteFiles(this=0x0000000000000000, files=size=0, blob_files=size=0, manifest_filenames=size=0, min_pending_output=18446744073709551615) at version_set.cc:7258:18
frame https://github.com/facebook/rocksdb/issues/7: 0x00007f0b3e8ccbc0 librocksdb.so.9.10`rocksdb::DBImpl::FindObsoleteFiles(this=<unavailable>, job_context=<unavailable>, force=<unavailable>, no_full_scan=<unavailable>) at db_impl_files.cc:162:30
frame https://github.com/facebook/rocksdb/issues/8: 0x00007f0b3e85e698 librocksdb.so.9.10`rocksdb::DBImpl::ResumeImpl(this=<unavailable>, context=<unavailable>) at db_impl.cc:434:20
frame https://github.com/facebook/rocksdb/issues/9: 0x00007f0b3e921516 librocksdb.so.9.10`rocksdb::ErrorHandler::RecoverFromBGError(this=<unavailable>, is_manual=<unavailable>) at error_handler.cc:632:46
```

I suspect this is due to DB being destructed and reopened during recovery. Specifically, the [ClearBGError() call](https://github.com/facebook/rocksdb/blob/c72e79a262bf696faf5f8becabf92374fc14b464/db/db_impl/db_impl.cc#L425) can release and reacquire mutex, and DB can be closed during this time. So it's not safe to access DB state after ClearBGError(). There was a similar story in https://github.com/facebook/rocksdb/issues/9496. [Moving the obsolete files logic after ClearBGError()](https://github.com/facebook/rocksdb/pull/11955) probably makes the seg fault more easily triggered.

This PR updates `ClearBGError()` to guarantee that db close cannot finish until the method is returned and the mutex is released. So that we can safely access DB state after calling it.

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

Test Plan: I could not trigger the seg fault locally, will just monitor future test failures.

Reviewed By: jowlyzhang

Differential Revision: D67476836

Pulled By: cbi42

fbshipit-source-id: dfb3e9ccd4eb3d43fc596ec10e4052861eeec002
2024-12-19 16:57:51 -08:00
Maciej Szeszko f7b4216628 Generalize work item definition in BackupEngineImpl (#13228)
Summary:
This change refactors existing `CopyOrCreateWorkItem` async task definition to a more generic one (`WorkItem`) with an assigned `type` indicative of intended action. This would allow us to reuse existing, battle-tested async tasks initialization code to handle wider range of incoming use cases in B/R space.

### Motivation
Historically, the two main use cases for `BackupEngineImpl`'s async work items were either creating a file in backup workflow or copying files in restore workflow. However, as we're now exploring opportunities in incremental restore (and potentially speeding up backup verification), we need the work item abstraction to be capable of processing different workflow types concurrently (computing checksum comes to mind).

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

Test Plan: Since this is purely cosmetic change where behavior remains intact, existing test collateral will suffice.

Reviewed By: pdillinger

Differential Revision: D67441210

Pulled By: mszeszko-meta

fbshipit-source-id: 78803e8cf3cf40b9d81831fac3a99193e1a30ef0
2024-12-19 16:57:03 -08:00
Yu Zhang c8bc2b63fc Add time measuring metrics for file ingestion in PerfContext (#13219)
Summary:
As titled. And also added some documentation for an approach to name perf context metrics that can help identify the starting `PerfLevel` that enables collecting it.

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

Test Plan: Unit test

Reviewed By: hx235

Differential Revision: D67362022

Pulled By: jowlyzhang

fbshipit-source-id: 7ed1bb475b5497961612d4e331600609da42074b
2024-12-19 16:53:42 -08:00
Peter Dillinger 1acd315c20 Refactor range_del_agg from CompactionOutputs -> SubcompactionState (#13231)
Summary:
To set up for splitting range deletes between penultimate and last level with per-key-placement compaction. This will solve some issues in combining RangeDelete+snapshot+mutable preclude_last, and probably also RangeDelete+UDT+preclude_last

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

Test Plan: existing tests

Reviewed By: cbi42

Differential Revision: D67481038

Pulled By: pdillinger

fbshipit-source-id: 597f0c991e4d7eae73b36b36aad493c2d2a15f24
2024-12-19 15:07:45 -08:00
Andrew Chang 2af12ea69c Remove .circleci folder (#13232)
Summary:
Originally I was trying to update `build-linux-clang10-mini-tsan` to actually use `clang10` (as the name implied). https://github.com/facebook/rocksdb/issues/13220 was supposed to also update this configuration, but I did not see that we had a definition for `build-linux-clang10-mini-tsan` in both `config.yml` and `pr-jobs.yml`. I was wondering why I could not see my changes reflected in the CI checks after merging. After I updated `pr-jobs.yml` for this PR, I found that the CI check started failing https://github.com/facebook/rocksdb/actions/runs/12417441052/job/34668411263?pr=13232. I don't think it makes sense for me to tackle looking into all the TSAN warnings being reported in `clang10` (at least in this PR), so for now I have updated the name of the PR job to accurately reflect the command that is being run.

This PR also gets rid of the entire `.circleci` folder, which I think is the more significant change.

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

Test Plan: Existing CI check is unchanged

Reviewed By: pdillinger

Differential Revision: D67462454

Pulled By: archang19

fbshipit-source-id: f1aabfe4c8793616d6cbaae36fdf007319bf7ab2
2024-12-19 13:06:33 -08:00
Peter Dillinger c72e79a262 Standardize on clang-format version 18 (#13233)
Summary:
... which is the default for CentOS 9 and Ubuntu 24, the latter of which is now available in GitHub Actions. Relevant CI job updated.

Re-formatted all cc|c|h files except in third-party/, using

```
clang-format -i `git ls-files | grep -E '[.](cc|c|h)$' | grep -v third-party/`
```

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

Test Plan: CI

Reviewed By: jaykorean, archang19

Differential Revision: D67461638

Pulled By: pdillinger

fbshipit-source-id: 0c9ac21a3f5eea6f5ade68bb6af7b6ba16c8b301
2024-12-19 10:58:40 -08:00
Andrew Chang 1d919ac414 Update build-linux-clang10-mini-tsan (#13220)
Summary:
I found this mismatch between the CI job title and the actual command ran incidentally while trying to work on https://github.com/facebook/rocksdb/issues/13213.

`build-linux-clang10-mini-tsan` was added in https://github.com/facebook/rocksdb/issues/7122 with `clang-10`.

In https://github.com/facebook/rocksdb/issues/10496 it was changed to use `clang-13` but the name was not also updated. I do not know what the author's intent was, but given that `build-linux-clang10-mini-tsan` is right next to`build-linux-clang10-ubsan` and `build-linux-clang10-asan`, I think it is more likely we originally intended to use `clang-10`.

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

Test Plan: I think we need to wait for the next set of CI checks after this PR is merged, since I don't see my changes incorporated into this PR's `build-linux-clang10-mini-tsan` check.

Reviewed By: hx235

Differential Revision: D67407034

Pulled By: archang19

fbshipit-source-id: 9c22b6c6c330a367920eb3d4a387f37b760d722c
2024-12-19 08:30:43 -08:00
Andrew Chang d957e1a33a Update test implementations for MultiRead with fs_scratch reuse (#13195)
Summary:
This is a follow up to https://github.com/facebook/rocksdb/issues/13189. As mentioned in the description in the previous PR, to guard against similar bugs in the future, we should update our test implementations to reflect the real-world assumptions that we can make about `fs_scratch` when we issue reads with the filesystem buffer reuse optimization. The current test implementations reinforce the misconception that `fs_scratch` points to the same place as `result.data()` (i.e. to the start of the valid data buffer for the read result). `fs_scratch` can point to any arbitrary data structure, but for our purposes, I think we achieve what we want if we just have it point to a `Slice` which wraps the underlying result buffer inside one of its class variables.

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

Test Plan: Existing unit tests test the same functionality but in an improved way with this change.

Reviewed By: hx235

Differential Revision: D66896380

Pulled By: archang19

fbshipit-source-id: 377e67ec70427716f2b7b7388d99b78003c01eb0
2024-12-17 16:36:04 -08:00
Andrew Chang 6ae3412244 Explain why RandomAccessFileReader* is not passed into FilePrefetchBuffer constructor (#13159)
Summary:
In https://github.com/facebook/rocksdb/pull/13118#discussion_r1842848359, we decided to make a separate follow-up PR that refactors `FilePrefetchBuffer` to determine `use_fs_buffer` once at construction time.

The change would have involved passing in the `RandomAccessFileReader*` directly to the constructor, and using that to determine `use_fs_buffer`. This would avoid repeatedly calling `UseFSBuffer(RandomAccessFileReader* reader)` during the actual prefetch requests.

I started working on this refactoring change but ran into issues with these 2 files, which used `GetOrCreatePrefetchBuffer`
- https://github.com/facebook/rocksdb/blob/main/db/compaction/compaction_iterator.cc
- https://github.com/facebook/rocksdb/blob/main/db/merge_helper.cc

As I explained in the added code comments, sometimes the `RandomAccessFileReader*` is not available when we construct the `FilePrefetchBuffer`, so although it is not the most elegant, I think right now it makes sense to pass in the `reader` into the `Prefetch` / `PrefetchAsync` / `TryReadFromCache` calls. Maybe there is a workaround but I don't think the refactor would be worth it.

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

Test Plan: N/A (comments)

Reviewed By: anand1976

Differential Revision: D66473731

Pulled By: archang19

fbshipit-source-id: ce3473694c2cd82513da1a76ad5995afa5bc9cfa
2024-12-17 15:25:04 -08:00
Andrew Chang 09c989fbcb Explicitly mark destructors with override (#13212)
Summary:
I saw these compiler warnings while preparing for the 9.10 release:

```cpp
'~CompactOnDeletionCollectorFactory' overrides a destructor but is not marked 'override' [-Werror,-Wsuggest-destructor-override]

'~CompactForTieringCollectorFactory' overrides a destructor but is not marked 'override' [-Werror,-Wsuggest-destructor-override]
```

This code is from a while ago so I assume that this CI check has been failing for quite some time. We should still clean this up to avoid confusion in the future.

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

Test Plan: Existing CI checks should pass, and we should not see this CI check failure the next time we try to make a release/patch.

Reviewed By: jaykorean

Differential Revision: D67287794

Pulled By: archang19

fbshipit-source-id: a11230a919c0b7ef21a7219bf05f567d3d44b2d1
2024-12-16 11:17:27 -08:00
Andrew Chang a45b9478ee Remove extra comma after 9.9.fb (#13211)
Summary:
I had an extra comma after `9.9.fb` when I updated `tools/check_format_compatible.sh` in https://github.com/facebook/rocksdb/issues/13210. This caused the nightly builds to start failing https://github.com/facebook/rocksdb/actions/workflows/nightly.yml on the `build-format-compatible` step. The error message is

```
2024-12-14T11:55:23.3413129Z == Building 9.9.fb, debug
2024-12-14T11:55:23.3427208Z fatal: ambiguous argument '_tmp_origin/9.9.fb,': unknown revision or path not in the working tree.
```

Notice the extra comma after `9.9.fb`.

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

Test Plan: The nightly builds should start passing again.

Reviewed By: jowlyzhang

Differential Revision: D67286484

Pulled By: archang19

fbshipit-source-id: 57a754c88af004ee879d9c9f82819b3c410a66a9
2024-12-16 10:28:36 -08:00
Hui Xiao 389f75c86c Break DBImpl::RecoverLogFiles() into smaller functions (#13184)
Summary:
**Context/Summary:**

`DBImpl::RecoverLogFiles()` has ~500 lines of code with nested loops and various return/continue/break statements. This becomes too difficult to understand and make change for the upcoming wal hole detection.

This PR broke it into multiple smaller functions and left a couple FIXME where the EXISTING ugly code is too complicated to clean up right now. Most of them are copy-and-paste excepts for `ProcessLogRecord()` that needs some thoughts into how to translate existing behaviors of `break`, `continue`, `return non-ok status`

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

Test Plan: Pass existing test

Reviewed By: jowlyzhang

Differential Revision: D66799568

Pulled By: hx235

fbshipit-source-id: d15617a47ee2d1c02652f1fd8336e82a2c5434b1
2024-12-14 21:41:15 -08:00
Andrew Chang 2ff9f95f59 Add files for 9.10 release (#13210)
Summary:
I followed the release instructions and referenced https://github.com/facebook/rocksdb/pull/13146

1. HISOTRY update
2. version.h
3. Format compatability test
4. Folly Git hash

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

Test Plan: CI

Reviewed By: pdillinger

Differential Revision: D67210980

Pulled By: archang19

fbshipit-source-id: cfbc02c643aeae19453c8c36d03d93478ea81c4e
2024-12-13 12:17:02 -08:00
Changyu Bi 2bf11e1e96 Enable two_write_queues in more stress tests (#13209)
Summary:
optionally enable two_write_queues whenever transaction db is used.

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

Test Plan: ran some crash tests internally: https://fburl.com/sandcastle/bjhg1wr5

Reviewed By: jowlyzhang

Differential Revision: D67207808

Pulled By: cbi42

fbshipit-source-id: 2fc40d3f81771968bd09b3b57f58c7ab66d91de4
2024-12-13 11:33:51 -08:00
Changyu Bi 7de9c0f39e Enable commit_bypass_memtable in no_batched_ops_stress tests (#13203)
Summary:
expand the test coverage to the more comprehensive no_batched_ops_stress. Small refactoring in db_crashtest.py.

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

Test Plan: ran a couple stress test jobs internally: https://fburl.com/sandcastle/nohosh7i

Reviewed By: jowlyzhang

Differential Revision: D67057497

Pulled By: cbi42

fbshipit-source-id: eccc033f3ae3dbd20729cd8f1f8f8d8b7c2cd057
2024-12-12 16:33:34 -08:00
Peter Dillinger 85d8ee7844 Improve paranoid_checks API comment (#13206)
Summary:
see comment change

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

Test Plan: no functional change

Reviewed By: cbi42

Differential Revision: D67108123

Pulled By: pdillinger

fbshipit-source-id: 669de1fff8df452c3e279f311452f02b40a03aaf
2024-12-12 11:41:16 -08:00
Levi Tamasi b339d089ed Write-side support for FAISS IVF indices (#13197)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13197

The patch adds initial support for backing FAISS's inverted file based indices with data stored in RocksDB. It introduces a `SecondaryIndex` implementation called `FaissIVFIndex` which takes ownership of a `faiss::IndexIVF` object. During indexing, `FaissIVFIndex` treats the original value of the specified primary column as an embedding vector, and passes it to the provided FAISS index object to perform quantization. It replaces the original embedding vector with the result of the coarse quantizer (i.e. the inverted list id), and puts the result of the fine quantizer (if any) into the secondary index value. Note that this patch is only one half of the equation; it provides a way of storing FAISS inverted lists in RocksDB but there is currently no retrieval/search support (this will be a follow-up change). Also, the integration currently works only with our internal Buck build. I plan to add support for `cmake` / `make` based builds similarly to how we handle Folly.

Reviewed By: jowlyzhang

Differential Revision: D66907065

fbshipit-source-id: 63fdf29895d5feeffc230254a7ddfb0aac050967
2024-12-09 18:56:27 -08:00
Andrew Chang 5aead7af3d Pass through use_fs_buffer to Read method (#13200)
Summary:
This is a follow up to https://github.com/facebook/rocksdb/issues/13177, which was supposed to disable the file system buffer optimization for compaction reads. However, it did not work as expected because I did not pass through `use_fs_buffer` to the `Read` method, which also calls `UseFSBuffer`.

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

Test Plan:
I added simple tests to verify we do not hit the overflow issue when we are doing compaction prefetches.

```
 ./prefetch_test --gtest_filter="*FSBufferPrefetchForCompaction*"
```

Of course I will be looking through the warm storage crash test logs as well once the change is merged.

Reviewed By: anand1976

Differential Revision: D66996079

Pulled By: archang19

fbshipit-source-id: b4d9254f1354ccfc53a307174de5f2388b7e5474
2024-12-09 17:58:55 -08:00
Andrew Chang d386385e0b Temporarily disable file system buffer reuse optimization for compaction prefetches (#13177)
Summary:
https://github.com/facebook/rocksdb/issues/13182 successfully fixed the heap `use-after-free` issue.

However, there was one additional error I found while looking through the warm storage crash test logs. There are repeated (though infrequent) unsigned pointer arithmetic overflow errors that look like this:
```cpp
file_prefetch_buffer.cc:860:46: runtime error: addition of unsigned offset to 0x7f282001880f overflowed to 0x7f2820017667
```

It took me a while to figure it out, but I was finally able to reproduce the issue locally. It turns out the issue is when we call `TryReadFromCache` with `for_compaction` set to `true`. The default value for `for_compaction` is `false`, and this was not covered in the unit tests written for https://github.com/facebook/rocksdb/issues/13118.

When I run the same unit tests with `for_compaction` set to `true`, I am able to break this assertion that I added at the end of `TryReadFromCacheUntracked`:
```cpp
assert(buf->offset_ <= offset);
```

If `buf->offset_` is greater than `offset`, then that explains the overflow we get in the following lines:
```cpp
uint64_t offset_in_buffer = offset - buf->offset_;
*result = Slice(buf->buffer_.BufferStart() + offset_in_buffer, n);
```

I will have another PR out that fixes the issue and enables the optimization when `for_compaction` is set to `true`. I will need to add some overlap buffer logic, similar to what I have inside `PrefetchInternal`. For now, since I have confirmed that there is indeed a bug, we should disable the optimization where needed. It will take me some time to implement the fix and write new test cases.

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

Test Plan: I kept the existing unit tests which test the file system buffer reuse code when `for_compaction` is `false`. I expect that the warm storage crash test logs will no longer show the integer overflow issue once we merge this PR.

Reviewed By: anand1976

Differential Revision: D66721857

Pulled By: archang19

fbshipit-source-id: 22d523646f969a7a0ccbbea73f63c32601f1179a
2024-12-09 13:24:16 -08:00
Maciej Szeszko bfe6cc218c Refresh check_buck_targets.sh (#13196)
Summary:
This is a followup to https://github.com/facebook/rocksdb/issues/13190. We're patching the targets generating script to construct `BUCK` file instead of deprecated `TARGETS` file + adding safety checks to ensure that `BUCK` file does not go missing (either as a direct renaming / removal OR as a modification to buckfier's script(s)).

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

Test Plan:
1. Manually verify 'Compare buckify output' step produces expected results (vs previously soft-failed one [here](https://github.com/facebook/rocksdb/actions/runs/12202756083/job/34044173548?pr=13178)).
2. Manually test following scenarios (for both of which we expect the buckifier script to fail):
-> Simulate removing `BUCK` file via commit
-> Simulate buckifier script removing the `BUCK` file

Reviewed By: pdillinger

Differential Revision: D66903948

Pulled By: mszeszko-meta

fbshipit-source-id: 0f83fd2f87b600981f640ccdbc3a4640974a63d4
2024-12-09 10:02:57 -08:00
Andrew Chang 31408c0d8d Set filesystem constructor parameter for FilePrefetchBuffer inside PrefetchTail (#13157)
Summary:
After https://github.com/facebook/rocksdb/pull/13118 was merged, I did some investigation to see whether the file system buffer reuse code was actually being used.

The good news is that I was able to see from the CPU profiling results that my code is getting invoked through the warm storage stress tests.

The bad news is that most of the time, the optimization is not being used, so we end up going through the regular old `RandomAccessFileReader::Read` path.

Here is the entire function call chain up to `FilePrefetchBuffer::Read`

1. rocksdb::DB::MultiGet
2. rocksdb::DBImpl::MultiGet
3. rocksdb::DBImpl::MultiGetCommon
4. rocksdb::DBImpl::MultiGetImpl
5. rocksdb::Version::MultiGet
6. rocksdb::Version::MultiGetFromSST
7. rocksdb::TableCache::MultiGet
8. rocksdb::TableCache::FindTable
9. rocksdb::TableCache::GetTableReader
10. rocksdb::BlockBasedTableFactory::NewTableReader
11. rocksdb::BlockBasedTable::Open
12. rocksdb::BlockBasedTable::PrefetchTail
13. rocksdb::FilePrefetchBuffer::Prefetch
14. rocksdb::FilePrefetchBuffer::Read

At this point, we split into `rocksdb::RandomAccessFileReader::Read` and
`rocksdb::FilePrefetchBuffer::FSBufferDirectRead`. `FSBufferDirectRead` gets called <3% of the time.

I think the root cause is that the `FileSystem* fs` parameter is not getting passed into the `FilePrefetchBuffer` constructor. When `fs` is `nullptr`, `UseFSBuffer()` will always return `false` and we do not end up calling `FSBufferDirectRead`.

Luckily, it does not seem like there are too many places I need to change. `BlockBasedTable` resets its `prefetch_buffer` in 3 separate places. When it disables the prefetch buffer (2/3 of the instances), we don't care about whether the `fs` parameter is there. This PR is addressing the third instance, where it is not trying to disable the buffer.

Note that there is another method, `PrefetchBufferCollection::GetOrCreatePrefetchBuffer` that creates new `FilePrefetchBuffer`s without the `fs` parameter. This method gets called by `compaction_iterator` and `merge_helper`. I think we can address this in a subsequent PR:
1. Each of these changes effectively "unlocks" the buffer reuse feature. Separating the changes would be helpful when I look at the profiling results again, since I can isolate what impact this PR had on the percentage of time that `rocksdb::FilePrefetchBuffer::FSBufferDirectRead` was invoked.
2. I still need to look into what exactly I would need to changes I need to make to `PrefetchBufferCollection`
3. This code seems to be for blob prefetching in particular, and I don't think it has the biggest ROI anyways.

```cpp
      const Status s = blob_fetcher_->FetchBlob(
          user_key(), blob_index, prefetch_buffer, &blob_value_, &bytes_read);
```
4. I am not sure if the current benchmark I am using for warm storage exercises this blob prefetching code, so I may need to find another way to assess the performance impact.

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

Test Plan: The existing unit test coverage guards against obvious bugs. I ran another set of performance tests to confirm there were no regressions in CPU utilization.

Reviewed By: anand1976

Differential Revision: D66464704

Pulled By: archang19

fbshipit-source-id: 260145cfcc05ac46cf2dd77a53a85e8808031dea
2024-12-06 15:37:44 -08:00
Maciej Szeszko be7703b27d Offline file checksum manifest retriever (#13178)
Summary:
This change introduces a new, lightweight _experimental_ API that reconstructs the [file # -> file checksum -> file checksum function] 1-1-1 mapping directly from the `MANIFEST` file considered `CURRENT` in scope of specific DB instance at the time. The goal is to provide a cheap alternative to `DB::GetLiveFilesMetaData` that doesn't require opening the database, reconstructing version sets and/or accessing files that are _potentially_ in disaggregated storage.

### Housekeeping:

1. Moved the `GetCurrentManifestPath` out of `version_set` to a new `manifest_ops` file(s) dedicated to manifest related operations.
2. Introduced new `Env::IOActivity::kReadManifest` to better reflect the IO intent in offline file checksum retrieving function.

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

Test Plan:
Added a unit test comparing the outcome of newly introduced API against the established `GetLiveFilesMetaData`:

```hcl
./db_test2 --gtest_filter="*GetFileChecksumsFromCurrentManifest_CRC32*"
```

Reviewed By: pdillinger

Differential Revision: D66711910

Pulled By: mszeszko-meta

fbshipit-source-id: 57091c550a14ac2e832bf7eea136dab5450e71bc
2024-12-06 13:29:52 -08:00
Andrew Chang 4ac7fcb033 Add comment clarifying proper use of fs_scratch (#13189)
Summary:
https://github.com/facebook/rocksdb/pull/13182 seems to have resolved the `heap-use-after-free` / `heap-buffer-overflow` issues, but not for the reasons we had in mind.

I believe I have figured out the root cause after doing more thinking / reading into the warm storage code.

**`fs_scratch` cannot be assumed to point to the start of the data buffer. It must be treated as a pointer to any arbitrary object / data structure. As such, we must rely only on result.data().**

I think that part of the reason for the bug was that the comment for `fs_scratch` was

> fs_scratch is a data buffer allocated and provided by underlying FileSystem

which is _extremely misleading_.

To avoid confusion in the future, I have updated the comments related to `FsReadRequest` with some of my learnings and included `WARNING`s in all caps to hopefully steer future engineers aware from the same issue.

In another PR, I will update some of our mock file system test classes that support `FSSupportedOps::kFSBuffer`. The test class implementation also contributed to my confusion, since `fs_scratch` did point to the start of the valid data in those implementations. This cannot and should not be assumed to be true in general, and we should try to guard against potential future bugs by updating those mock implementations.

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

Test Plan: These are just comments.

Reviewed By: anand1976, hx235

Differential Revision: D66849436

Pulled By: archang19

fbshipit-source-id: c264007647af9cc2a4dfd58dbe7287af86fa2261
2024-12-06 10:15:18 -08:00
Facebook GitHub Bot 10958102aa Re-sync with internal repository
The internal and external repositories are out of sync. This Pull Request attempts to brings them back in sync by patching the GitHub repository. Please carefully review this patch. You must disable ShipIt for your project in order to merge this pull request. DO NOT IMPORT this pull request. Instead, merge it directly on GitHub using the MERGE BUTTON. Re-enable ShipIt after merging.
2024-12-05 22:51:47 -08:00
Levi Tamasi 1f96e652b3 Support using secondary indices with write-committed transactions (#13180)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13180

The patch adds initial support for secondary indices using write-committed transactions. Currently, only the `PutEntity` API is supported; other APIs like `Put` and `Delete` will be added separately. Applications can set up secondary indices using the new configuration option `TransactionDBOptions::secondary_indices`. When secondary indices are enabled, calling `PutEntity` via a (n explicit or implicit) transaction performs the following steps:
1) It retrieves the current value (if any) of the primary key using `GetEntityForUpdate`.
2) If there is an existing primary key-value, it removes any existing secondary index entries using `SingleDelete`. (Note: as a later optimization, we can avoid removing and recreating secondary index entries when neither the secondary key nor the value changes during an update.)
3) It invokes `UpdatePrimaryColumnValue` for all applicable `SecondaryIndex` objects, that is, those for which the primary column family matches the column family from the `PutEntity` call and for which the primary column appears in the new wide-column structure.
4) It writes the new primary key-value. Note that the values of the indexing columns might have been changed in step 3 above.
5) It builds the secondary key-value for each applicable secondary index using `GetSecondaryKeyPrefix` and `GetSecondaryValue`, and writes it to the appropriate secondary column family.

All the above operations are performed as part of the same transaction. The logic uses `SavePoint`s to roll back any earlier operations related to a primary key if a subsequent step fails.

Implementation-wise, the code uses a mixin template `SecondaryIndexMixin` that can inherit from any kind of transaction and use the write APIs and concurrency control mechanisms of the base class to implement the index maintenance logic. The mixin will enable us to later extend secondary indices to optimistic or write-prepared/write-unprepared pessimistic transactions as well.

Reviewed By: jowlyzhang

Differential Revision: D66672931

fbshipit-source-id: cdf6ef9c40dec46d928156bad0a3cc546aa8b887
2024-12-05 19:05:56 -08:00
Jay Huh 1347bfb07f Remove deprecated remote compaction apis (#13188)
Summary:
`StartV2()` and `WaitForCompleteV2()` were deprecated and replaced by`Schedule()` and `Wait()` in 9.1.0. This PR removes them from the codebase completely.

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

Test Plan: CI

Reviewed By: archang19

Differential Revision: D66843687

Pulled By: jaykorean

fbshipit-source-id: f13d05845bf5ac4ae736c105035ca1a4d5a96047
2024-12-05 15:27:07 -08:00
Changyu Bi d5345a8ff7 Introduce a transaction option to skip memtable write during commit (#13144)
Summary:
add a new transaction option `TransactionOptions::commit_bypass_memtable` that will ingest the transaction into a DB as an immutable memtables, skipping memtable writes during transaction commit. This helps to reduce the blocking time of committing a large transaction, which is mostly spent on memtable writes. The ingestion is done by creating WBWIMemTable using transaction's underlying WBWI, and ingest it as the latest immutable memtable. The feature will be experimental.

Major changes are:
1. write path change to ingest the transaction, mostly in WriteImpl() and IngestWBWI() in db_impl_write.cc.
2. WBWI changes to track some per CF stats like entry count and overwritten single deletion count, and track which keys have overwritten single deletions (see 3.). Per CF stat is used to precompute the number of entries in each WBWIMemTable.
3. WBWIMemTable Iterator changes to emit overwritten single deletions. The motivation is explained in the comment above class WBWIMemTable definition. The rest of the changes in WBWIMemTable are moving the iterator definition around.

Some intended follow ups:
1. support for merge operations
2. stats/logging around this option
3. tests improvement, including stress test support for the more comprehensive no_batched_op_stress.

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

Test Plan:
* added new unit tests
* enabled in multi_ops_txns_stress test
* Benchmark: applying the change in 8222c0cafc4c6eb3a0d05807f7014b44998acb7a, I tested txn size of 10k and check perf context for write_memtable_time, write_wal_time and key_lock_wait_time(repurposed for transaction unlock time). Though the benchmark result number can be flaky, this shows memtable write time improved a lot (more than 100 times). The benchmark also shows that the remaining commit latency is from transaction unlock.
```
./db_bench --benchmarks=fillrandom --seed=1727376962 --threads=1 --disable_auto_compactions=1 --max_write_buffer_number=100 --min_write_buffer_number_to_merge=100 --writes=100000 --batch_size=10000 --transaction_db=1 --perf_level=4 --enable_pipelined_write=false --commit_bypass_memtable=1

commit_bypass_memtable = false
fillrandom   :       3.982 micros/op 251119 ops/sec 0.398 seconds 100000 operations;   27.8 MB/s PERF_CONTEXT:
write_memtable_time = 116950422
write_wal_time =      8535565
txn unlock time =     32979883

commit_bypass_memtable = true
fillrandom   :       2.627 micros/op 380559 ops/sec 0.263 seconds 100000 operations;   42.1 MB/s PERF_CONTEXT:
write_memtable_time = 740784
write_wal_time =      11993119
txn unlock time =     21735685
```

Reviewed By: jowlyzhang

Differential Revision: D66307632

Pulled By: cbi42

fbshipit-source-id: 6619af58c4c537aed1f76c4a7e869fb3f5098999
2024-12-05 15:00:17 -08:00
Koorous Vargha 2a9a8da97c Add Venice as a RocksDB user (#13179)
Summary:
[Venice](https://venicedb.org/) is a derived data platform using RocksDB as its storage engine. It is LinkedIn's ML feature store, powering thousands of recommender use cases, including the Feed, Video recommendations, and People You May Know.

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

Reviewed By: hx235

Differential Revision: D66724729

Pulled By: cbi42

fbshipit-source-id: a027d6664f2924473884a3d5d129748ea1e5fe37
2024-12-05 12:09:44 -08:00
Andrew Chang debd87596a Potential fix for heap use-after-free issue (#13182)
Summary:
This PR is an attempt to address https://github.com/facebook/rocksdb/issues/13118. The warm storage crash tests show use-after-free errors. They do not occur in every single crash test run, but with enough attempts, they are repeatable.

Theory 1:
I am wondering if the `fs_buffer` is being prematurely freed before we take ownership of it. In `SetBuffer`, I was passing in `FSAllocationPtr&& new_buf` rather than `FSAllocationPtr new_buf`. When I pass the parameter as `FSAllocationPtr&& new_buf`, only after the `buf_ = std::move(new_buf);` line is run is ownership transferred from the original `FSAllocationPtr`. But before that I had a line `bufstart_ = reinterpret_cast<char*>(buf_.get());`. So I am hypothesizing that it is possible, under certain race conditions, that between the first `buf_.get()` and the `buf_ = std::move(new_buf);`, the `fs_buffer` was altered, leaving `bufstart_` pointing to some freed memory area.

Theory 2 (from anand1976):
Perhaps we need to set the `bufstart_` based on the `Slice` rather than the `FSAllocationPtr`. This would be more consistent with what we do here https://github.com/facebook/rocksdb/blob/main/table/block_fetcher.cc#L275.

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

Test Plan: The existing unit tests and CI ensures I am not making anything worse, but I will want to wait and see if the daily crash tests runs still have the same `heap-use-after-free` errors with this change. Alternatively, if we fail the `assert` I just added, then I can make a follow-up PR to return `false` from `TryReadFromCache` whenever we get handed back a `nullptr`.

Reviewed By: anand1976

Differential Revision: D66771852

Pulled By: archang19

fbshipit-source-id: 5b585d86d657ec050a04e892d3b1cf4383f377f9
2024-12-05 08:36:41 -08:00
Changyu Bi 138c7b6182 Fix KeyMayExist() to allow value parameter to be null (#13156)
Summary:
fixes issue https://github.com/facebook/rocksdb/issues/13048. value can be null according to the function [comment](https://github.com/facebook/rocksdb/blob/389e66bef56b4f81d3c9683469acb5affc32bd7f/include/rocksdb/db.h#L955-L956).

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

Test Plan: update a unit test to cover null value case.

Reviewed By: jowlyzhang

Differential Revision: D66473040

Pulled By: cbi42

fbshipit-source-id: 2b6e4da8b674a2c74b0ab78b6eac25052fdff2ae
2024-12-04 19:03:55 -08:00
Jay Huh d46d1fa105 Skip building remote compaction output file when not OK (#13183)
Summary:
During `FinishCompactionOutputFile()` if there's an IOError, we may end up having the output in memory, but table properties are not populated, because `outputs.UpdateTableProperties();` is called only when `s.ok()` is true.

However, during remote compaction result serialization, we always try to access the `table_properties` which may be null.  This was causing a segfault.

We can skip building the output files in the result completely if the status is not ok.

# Unit Test
New test added
```
./compaction_service_test --gtest_filter="*CompactionOutputFileIOError*"
```

Before the fix
```
Received signal 11 (Segmentation fault)
Invoking GDB for stack trace...
https://github.com/facebook/rocksdb/issues/4  0x00000000004708ed in rocksdb::TableProperties::TableProperties (this=0x7fae070fb4e8) at ./include/rocksdb/table_properties.h:212
212     struct TableProperties {
https://github.com/facebook/rocksdb/issues/5  0x00007fae0b195b9e in rocksdb::CompactionServiceOutputFile::CompactionServiceOutputFile (this=0x7fae070fb400, name=..., smallest=0, largest=0, _smallest_internal_key=..., _largest_internal_key=..., _oldest_ancester_time=1733335023, _file_creation_time=1733335026, _epoch_number=1, _file_checksum=..., _file_checksum_func_name=..., _paranoid_hash=0, _marked_for_compaction=false, _unique_id=..., _table_properties=...) at ./db/compaction/compaction_job.h:450
450             table_properties(_table_properties) {}
```

After the fix
```
[ RUN      ] CompactionServiceTest.CompactionOutputFileIOError
[       OK ] CompactionServiceTest.CompactionOutputFileIOError (4499 ms)
[----------] 1 test from CompactionServiceTest (4499 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (4499 ms total)
[  PASSED  ] 1 test.
```

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

Reviewed By: anand1976

Differential Revision: D66770876

Pulled By: jaykorean

fbshipit-source-id: 63df7c2786ce0353f38a93e493ae4e7b591f4ed9
2024-12-04 17:25:31 -08:00
Peter Dillinger 3b91fe817f Expand crash testing of tiered storage, especially FIFO (#13176)
Summary:
* Test tiered storage FIFO setting `file_temperature_age_thresholds` in crash test, with dynamic mutability.
* Re-organize db_crashtest.py slightly to better handle tiered storage parameters and their interaction with compaction_style and num_levels. I have put most of this logic in the python script so that `db_stress` command lines reflect settings in effect as best as possible.
* Tweak crash test settings for preclude_last_level_data_seconds. This seems to have amplified the possibility of hitting "Corruption: Unsafe to store Seq later" even with universal compaction, which I am working on a fix for. We should also be able to enable tiered+leveled when this is fixed. (TODO / follow-up items)
* Code formatting / small simplifications in db_crashtest.py

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

Test Plan:
no production code changes

Kicked off about 24 CI jobs (temporary internal link https://fburl.com/sandcastle/s61rzusr)

Reviewed By: cbi42

Differential Revision: D66674123

Pulled By: pdillinger

fbshipit-source-id: 33dd7f9d291ec4a9516665b4adb998fd9a2b9266
2024-12-04 09:02:26 -08:00
Jon Janzen 80c3705262 Update codegen script to generate the correct file
Summary: I missed in the previous diff that this is generated. Let's fix that codegen script

Reviewed By: dtolnay

Differential Revision: D66725403

fbshipit-source-id: ec9fa773c8309040da98677a128c4cb0309542a8
2024-12-03 15:26:02 -08:00
Jon Janzen 7dab484aa9 Rewrite TARGETS files to BUCK files for facebook/rocksdb (#13165)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13165

This diff migrates TARGETS file to BUCK files that are synced for an open source project.

Reviewed By: dtolnay

Differential Revision: D66561335

fbshipit-source-id: 9c91a19ef59a81adc31b763a63134aeef1eb00ed
2024-12-03 13:08:00 -08:00
Levi Tamasi b045f4a122 Add a secondary index interface (#13175)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13175

The patch is the first step in adding support for secondary indices via the transaction layer. It introduces a new `SecondaryIndex` interface, which enables creating secondary indices over a set of (plain or wide-column) primary key-values to facilitate queries by (column) value instead of key. This interface will be automagically invoked by the transaction logic to add and remove secondary index entries as needed when the application issues write operations for the primary data. Classes deriving from `SecondaryIndex` can implement the methods `GetPrimaryColumn{Family,Name}` and `GetSecondaryColumnFamily` to respectively define the primary column family and wide column to index and the column family to use for the secondary index entries. The format of the secondary index entries can be defined by implementing `GetSecondaryKeyPrefix` and `GetSecondaryValue`. In addition, `UpdatePrimaryColumnValue` can be used to optionally update the value of the indexing column in the primary key-value before it is added to the transaction.

Reviewed By: jowlyzhang

Differential Revision: D66672758

fbshipit-source-id: 0b7441ffff626c13956220e6efc98215303ef57e
2024-12-03 11:01:13 -08:00
anand76 a1be80c5c2 Try to align WritableFileWriter buffered writes (#13158)
Summary:
In buffered IO mode, without checksum calculation for buffered data enabled, try to align writes to the file system on a power of two. This can improve performance, especially on a distributed file system like Warm Storage that does erasure coding and benefits from full stripe writes. We do this by filling up the writable buffer, with a partial append if necessary, before flushing. When checksum calculation for buffered data is enabled, we don't do this since its preferable to not split the data, especially if the caller provides the checksum. We don't guarantee alignment if the caller manually flushes before finishing the file.

Tests:
Add unit tests in file_reader_writer_test and external_sst_file_basic_test.

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

Reviewed By: pdillinger

Differential Revision: D66669367

Pulled By: anand1976

fbshipit-source-id: 6df1b4538bda696e2170515420ee4c3766c83bb8
2024-12-03 10:41:24 -08:00
Yu Zhang b96432aadd Add public API definitions for surfacing data age (#13138)
Summary:
This PR adds the definition for the public APIs for surfacing data write time info. It only contains minimum implementation. The implementations will be in follow ups. I need to sync with customers if these public APIs meet their requirements and are easy to use. And make modifications accordingly before proceeding with implementations.

- `struct DataCollectionUnixWriteTimeInfo` is a struct for the unix write time info for a collection of data
- `DB::GetPropertiesOfTablesForLevels` returns table properties collection per level
- `GetDataCollectionUnixWriteTimeInfoForFile` returns the data write time info for a file.
- `GetDataCollectionUnixWriteTimeInfoForLevels` returns the data write time info for levels.
- The user property names for recording write time stats in the user collected properties are defined.
Follow ups:

Implement collecting the write time related user table properties
Use the data write time info recorded in the table properties to implement these APIs

Test Plan:
No functional change, also follow ups should have tests covering the minimum implementation added in this PR.

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

No functional change, also follow ups should have tests covering the minimum implementation added in this PR.

Reviewed By: pdillinger

Differential Revision: D65952586

Pulled By: jowlyzhang

fbshipit-source-id: b1ebf61a35005e9ca6b4ecc28c864beb6fb4bc59
2024-12-02 16:32:02 -08:00
tcwzxx 4ed79f5bd1 Fix the issue where compaction incorrectly drops a key when there is a snapshot with a sequence number of zero. (#13155)
Summary:
The compaction will incorrectly drop a key under the following conditions:

1. Open an empty database.
2. Use the `IngestExternalFile` API to ingest an SST file (the global sequence number will be 0).
3. Create a snapshot (the snapshot sequence number will be 0).
4. Trigger compaction; the key in the above SST file will be dropped.

The drop condition is found here: https://github.com/facebook/rocksdb/blob/f20d12adc85ece3e75fb238872959c702c0e5535/db/compaction/compaction_iterator.cc#L875-L878
The condition does not explicitly check if a previous key exists.

Fix: Add a check of `last_sequence != kMaxSequenceNumber` to verify if there is a previous key

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

Reviewed By: jowlyzhang

Differential Revision: D66473015

Pulled By: cbi42

fbshipit-source-id: 93a3ec5c103f95e9bb97e3944ba6e752a5394421
2024-12-02 13:31:19 -08:00
Levi Tamasi 346055a9ea Generalize and move the wide-column Find method to facilitate reuse (#13168)
Summary:
Pull Request resolved: https://github.com/facebook/rocksdb/pull/13168

The patch moves `WideColumnSerialization::Find` to `WideColumnsHelper` to facilitate reuse in non-serialization-related contexts. It also generalizes the method to take a range of iterators, and templatizes it on the iterator type to enable using it with both `const` and non-`const` iterators. Finally, it adds an assertion to ensure the method is called with a properly sorted range, which is a precondition for binary search.

Reviewed By: jaykorean

Differential Revision: D66602558

fbshipit-source-id: 841a885af31e183edeb7e3314167c55f8ed53ff1
2024-12-02 12:02:22 -08:00
Changyu Bi 1a76289be4 Small fix in WBWIMemtable::UpdateKey() (#13171)
Summary:
fixes issue https://github.com/facebook/rocksdb/issues/13166.

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

Test Plan: the same command in https://github.com/facebook/rocksdb/issues/13166 doesn't reproduce for me.

Reviewed By: ltamasi

Differential Revision: D66621871

Pulled By: cbi42

fbshipit-source-id: 08820a22071091606b437181e2b5e9343202d637
2024-12-02 10:19:35 -08:00
George Reynya 6f9d8260f0 Add abort check to yield hook (#13164)
Summary:
Adding ability to kill mysql queries traversing long lists of tombstones. Outside of mysql where RocksDbThreadYieldAndCheckAbort is not implemented all of this should still be optimized out by the compiler.

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

Reviewed By: cbi42

Differential Revision: D66556004

Pulled By: george-reynya

fbshipit-source-id: 727875569209cd6d2f29c07f89ecfa641d5ee36f
2024-11-27 16:45:59 -08:00
Kasper Isager Dalsgarð a294529ca9 Fix Android compilation (#12914)
Summary:
I was seeing errors like these prior to this patch:

```
env/io_posix.cc:174:17: error: variable has incomplete type 'struct statfs'
  struct statfs buf;
```

```
env/io_posix.h:40:9: error: 'POSIX_MADV_NORMAL' macro redefined [-Werror,-Wmacro-redefined]
#define POSIX_MADV_NORMAL 0     /* [MC1] no further special treatment */
```

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

Reviewed By: hx235

Differential Revision: D66307960

Pulled By: cbi42

fbshipit-source-id: f38034c56986e7877c9a04046b910bbeec9be1fe
2024-11-27 12:56:32 -08:00
Maciej Szeszko f20d12adc8 Reduce use of snprintf and fixed-size buffers (#13154)
Summary:
This change aims at increasing general memory safety in scope of selected `/db` files (`db_impl/db_impl.cc`, `dbformat.cc`, `log_reader.cc` and `transaction_log_impl.cc`).

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

Test Plan:
Verify logging structure & formatting parity by manually running the `/db` related tests exercising respective code paths pre and post change.

Note: As per request, we'll address the `internal_stats.cc` in the followup PR.

Reviewed By: pdillinger

Differential Revision: D66392729

Pulled By: mszeszko-meta

fbshipit-source-id: 107fd11221554721d9c1669a24031be3049afd01
2024-11-22 17:53:35 -08:00
Jay Huh e9b15738d9 Honor ConfigOptions.ignore_unknown_options in ParseStruct() (#13152)
Summary:
`OptionTypeInfo::ParseStruct()` was not honoring `config_options.ignore_unknown_options` when unknown properties are found in the serialized string. This caused a compatibility issue in Remote Compaction. When the worker was updated with RocksDB 9.9, the remote worker started including a new table property, `newest_key_time` (added in PR https://github.com/facebook/rocksdb/issues/13083), in the compaction output files. However, parsing that table property in the serialized compaction result from the primary (running with `9.8`) was returning a non-ok status, even though `config_options.ignore_unknown_options` was `true`.

In this fix, we will ignore unused properties if `config_options.ignore_unknown_options` is set to true.

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

Test Plan: Unit Test Added

Reviewed By: archang19

Differential Revision: D66374541

Pulled By: jaykorean

fbshipit-source-id: 78fd8309909279390438c247c4d390bbee4fa914
2024-11-22 16:38:05 -08:00
Andrew Chang 26b480609c Update FilePrefetchBuffer::Read to reuse file system buffer when possible (#13118)
Summary:
This PR adds support for reusing the file system provided buffer to avoid an extra `memcpy` into RockDB's buffer. This optimization has already been implemented for point lookups, as well as compaction and scan reads _when prefetching is disabled_.

This PR extends this optimization to work with synchronous prefetching (`num_buffers == 1`). Asynchronous prefetching can be addressed in a future PR (and probably should be to keep this PR from growing too large).

Remarks
- To handle the case where the main buffer only has part of the requested data, I used the existing `overlap_buf_` (currently used in the async prefetching case) instead of defining a separate buffer. This was discussed in https://github.com/facebook/rocksdb/pull/13118#discussion_r1842839360.
- We use `MultiRead` with a single request to take advantage of the file system buffer. This is consistent with previous work (e.g. https://github.com/facebook/rocksdb/pull/12266).
- Even without the tests I added, there was some code coverage inside in at least `DBIOCorruptionTest.IterReadCorruptionRetry`, since those tests were failing before I addressed a bug in my code for this PR. [Run with failed test](https://github.com/facebook/rocksdb/actions/runs/11708830448/job/32611508818?pr=13118).
- This prefetching code is not too easy to follow, so I added quite a bit of comments to both the code and test case to try to make it easier to understand the exact internal state of the prefetch buffer at every point in time.

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

Test Plan:
I wrote pretty thorough unit tests that cover synchronous prefetching with file system buffer reuse.  The flows for partial hits, complete hits, and complete misses are tested. I also parametrized the test to make sure the async prefetching (without file system buffer reuse) still work as expected.

Once we agree on the changes, I will run a long stress test before merging.

Reviewed By: anand1976

Differential Revision: D65559101

Pulled By: archang19

fbshipit-source-id: 1a56d846e918c20a009b83f1371c1791f69849ae
2024-11-21 12:32:13 -08:00
Hui Xiao 0f35db55d8 Print file number when TEST_VerifyNoObsoleteFilesCached fails (#13145)
Summary:
**Context/Summary:**

https://github.com/facebook/rocksdb/pull/13117 added check for obsolete SST files that are not cleaned up timely. It caused a infrequent stress test failure  `assertion="live_and_quar_files.find(file_number) != live_and_quar_files.end()"` that I haven't repro-ed yet.

This PR prints the file number so we can find out what happens to that file through info logs when encountering the same failure.

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

Test Plan:
Manually fail the assertion and observe the stderr printing
```
[ RUN      ] DBBasicTest.UniqueSession
File 12 is not live nor quarantined
db_basic_test: db/db_impl/db_impl_debug.cc:384: rocksdb::DBImpl::TEST_VerifyNoObsoleteFilesCached(bool) const::<lambda(const rocksdb::Slice&, rocksdb::Cache::ObjectPtr, size_t, const rocksdb::Cache::CacheItemHelper*)>: Assertion `false' failed.
```

Reviewed By: pdillinger

Differential Revision: D66134154

Pulled By: hx235

fbshipit-source-id: 353164c373d3d674cee676b24468dfc79a1d4563
2024-11-20 12:22:52 -08:00
Yu Zhang bee8d5560e Start version 9.10.0 (#13146)
Summary:
Pull in HISTORY for 9.9.0, update version.h for next version, update check_format_compatible.sh, update git hash for folly

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

Test Plan: CI

Reviewed By: ltamasi

Differential Revision: D66142259

Pulled By: jowlyzhang

fbshipit-source-id: 90216b2d7cff2e0befb4f56567e3bd074f97c484
2024-11-18 21:49:47 -08:00
448 changed files with 15879 additions and 6174 deletions

No files matched your search

-905
View File
@@ -1,905 +0,0 @@
version: 2.1
orbs:
win: circleci/windows@5.0.0
commands:
install-cmake-on-macos:
steps:
- run:
name: Install cmake on macos
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
install-jdk8-on-macos:
steps:
- run:
name: Install JDK 8 on macos
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew tap bell-sw/liberica
HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask liberica-jdk8
increase-max-open-files-on-macos:
steps:
- run:
name: Increase max open files
command: |
sudo sysctl -w kern.maxfiles=1048576
sudo sysctl -w kern.maxfilesperproc=1048576
sudo launchctl limit maxfiles 1048576
pre-steps:
steps:
- checkout
- run:
name: Setup Environment Variables
command: |
echo "export GTEST_THROW_ON_FAILURE=0" >> $BASH_ENV
echo "export GTEST_OUTPUT=\"xml:/tmp/test-results/\"" >> $BASH_ENV
echo "export SKIP_FORMAT_BUCK_CHECKS=1" >> $BASH_ENV
echo "export GTEST_COLOR=1" >> $BASH_ENV
echo "export CTEST_OUTPUT_ON_FAILURE=1" >> $BASH_ENV
echo "export CTEST_TEST_TIMEOUT=300" >> $BASH_ENV
echo "export ZLIB_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zlib" >> $BASH_ENV
echo "export BZIP2_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/bzip2" >> $BASH_ENV
echo "export SNAPPY_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/snappy" >> $BASH_ENV
echo "export LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> $BASH_ENV
echo "export ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> $BASH_ENV
windows-build-steps:
steps:
- checkout
- run:
name: "Install thirdparty dependencies"
command: |
echo "Installing CMake..."
choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y
choco install liberica8jdk -y
mkdir $Env:THIRDPARTY_HOME
cd $Env:THIRDPARTY_HOME
echo "Building Snappy dependency..."
curl https://github.com/google/snappy/archive/refs/tags/1.1.8.zip -O snappy-1.1.8.zip
unzip -q snappy-1.1.8.zip
cd snappy-1.1.8
mkdir build
cd build
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" ..
msbuild.exe Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
- run:
name: "Build RocksDB"
command: |
$env:Path = $env:JAVA_HOME + ";" + $env:Path
mkdir build
cd build
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DJNI=1 ..
cd ..
echo "Building with VS version: $Env:CMAKE_GENERATOR"
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
- run:
name: "Test RocksDB"
shell: powershell.exe
command: |
build_tools\run_ci_db_test.ps1 -SuiteRun arena_test,db_basic_test,db_test,db_test2,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test -Concurrency 16
- run:
name: "Test RocksJava"
command: |
cd build\java
& $Env:CTEST_BIN -C Debug -j 16
pre-steps-macos:
steps:
- pre-steps
post-steps:
steps:
- store_test_results: # store test result if there's any
path: /tmp/test-results
- store_artifacts: # store LOG for debugging if there's any
path: LOG
- run: # on fail, compress Test Logs for diagnosing the issue
name: Compress Test Logs
command: tar -cvzf t.tar.gz t
when: on_fail
- store_artifacts: # on fail, store Test Logs for diagnosing the issue
path: t.tar.gz
destination: test_logs
when: on_fail
- run: # store core dumps if there's any
command: |
mkdir -p /tmp/core_dumps
cp core.* /tmp/core_dumps
when: on_fail
- store_artifacts:
path: /tmp/core_dumps
when: on_fail
post-pmd-steps:
steps:
- store_artifacts:
path: /home/circleci/project/java/target/pmd.xml
when: on_fail
- store_artifacts:
path: /home/circleci/project/java/target/site
when: on_fail
upgrade-cmake:
steps:
- run:
name: Upgrade cmake
command: |
sudo apt remove --purge cmake
sudo snap install cmake --classic
install-gflags:
steps:
- run:
name: Install gflags
command: |
sudo apt-get update -y && sudo apt-get install -y libgflags-dev
install-gflags-on-macos:
steps:
- run:
name: Install gflags on macos
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install gflags
install-maven:
steps:
- run:
name: Install Maven
command: |
wget --no-check-certificate https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
tar zxf apache-maven-3.9.6-bin.tar.gz
echo "export M2_HOME=$(pwd)/apache-maven-3.9.6" >> $BASH_ENV
echo 'export PATH=$M2_HOME/bin:$PATH' >> $BASH_ENV
setup-folly:
steps:
- run:
name: Checkout folly sources
command: |
make checkout_folly
build-folly:
steps:
- run:
name: Build folly and dependencies
command: |
make build_folly
build-for-benchmarks:
steps:
- pre-steps
- run:
name: "Linux build for benchmarks"
command: #sized for the resource-class rocksdb-benchmark-sys1
make V=1 J=8 -j8 release
perform-benchmarks:
steps:
- run:
name: "Test low-variance benchmarks"
command: ./tools/benchmark_ci.py --db_dir /tmp/rocksdb-benchmark-datadir --output_dir /tmp/benchmark-results --num_keys 20000000
environment:
LD_LIBRARY_PATH: /usr/local/lib
# How long to run parts of the test(s)
DURATION_RO: 300
DURATION_RW: 500
# Keep threads within physical capacity of server (much lower than default)
NUM_THREADS: 1
MAX_BACKGROUND_JOBS: 4
# Don't run a couple of "optional" initial tests
CI_TESTS_ONLY: "true"
# Reduce configured size of levels to ensure more levels in the leveled compaction LSM tree
WRITE_BUFFER_SIZE_MB: 16
TARGET_FILE_SIZE_BASE_MB: 16
MAX_BYTES_FOR_LEVEL_BASE_MB: 64
# The benchmark host has 32GB memory
# The following values are tailored to work with that
# Note, tests may not exercise the targeted issues if the memory is increased on new test hosts.
COMPRESSION_TYPE: "none"
CACHE_INDEX_AND_FILTER_BLOCKS: 1
MIN_LEVEL_TO_COMPRESS: 3
CACHE_SIZE_MB: 10240
MB_WRITE_PER_SEC: 2
post-benchmarks:
steps:
- store_artifacts: # store the benchmark output
path: /tmp/benchmark-results
destination: test_logs
- run:
name: Send benchmark report to visualisation
command: |
set +e
set +o pipefail
./build_tools/benchmark_log_tool.py --tsvfile /tmp/benchmark-results/report.tsv --esdocument https://search-rocksdb-bench-k2izhptfeap2hjfxteolsgsynm.us-west-2.es.amazonaws.com/bench_test3_rix/_doc
true
executors:
linux-docker:
docker:
# The image configuration is build_tools/ubuntu20_image/Dockerfile
# To update and build the image:
# $ cd build_tools/ubuntu20_image
# $ docker build -t zjay437/rocksdb:0.5 .
# $ docker push zjay437/rocksdb:0.5
# `zjay437` is the account name for zjay@meta.com which readwrite token is shared internally. To login:
# $ docker login --username zjay437
# Or please feel free to change it to your docker hub account for hosting the image, meta employee should already have the account and able to login with SSO.
# To avoid impacting the existing CI runs, please bump the version every time creating a new image
# to run the CI image environment locally:
# $ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it zjay437/rocksdb:0.5 bash
# option `--cap-add=SYS_PTRACE --security-opt seccomp=unconfined` is used to enable gdb to attach an existing process
- image: zjay437/rocksdb:0.6
linux-java-docker:
docker:
# This is the Docker Image used for building RocksJava releases, see: https://github.com/evolvedbinary/docker-rocksjava
- image: evolvedbinary/rocksjava:centos6_x64-be
jobs:
build-macos:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
environment:
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc cause env_test hang, disable it for now
steps:
- increase-max-open-files-on-macos
- install-gflags-on-macos
- pre-steps-macos
- run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j16 all
- post-steps
build-macos-cmake:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
parameters:
run_even_tests:
description: run even or odd tests, used to split tests to 2 groups
type: boolean
default: true
steps:
- increase-max-open-files-on-macos
- install-cmake-on-macos
- install-gflags-on-macos
- pre-steps-macos
- run:
name: "cmake generate project file"
command: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
- run:
name: "Build tests"
command: cd build && make V=1 -j16
- when:
condition: << parameters.run_even_tests >>
steps:
- run:
name: "Run even tests"
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 0,,2
- when:
condition:
not: << parameters.run_even_tests >>
steps:
- run:
name: "Run odd tests"
command: ulimit -S -n `ulimit -H -n` && cd build && ctest -j16 -I 1,,2
- post-steps
build-linux:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: make V=1 J=32 -j32 check
- post-steps
build-linux-encrypted_env-no_compression:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
- run: |
./sst_dump --help | grep -E -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression
- post-steps
build-linux-static_lib-alt_namespace-status_checked:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
- post-steps
build-linux-release:
executor: linux-docker
resource_class: 2xlarge
steps:
- checkout # check out the code in the project directory
- run: make V=1 -j32 LIB_MODE=shared release
- run: ls librocksdb.so # ensure shared lib built
- run: ./db_stress --version # ensure with gflags
- run: make clean
- run: make V=1 -j32 release
- run: ls librocksdb.a # ensure static lib built
- run: ./db_stress --version # ensure with gflags
- run: make clean
- run: apt-get remove -y libgflags-dev
- run: make V=1 -j32 LIB_MODE=shared release
- run: ls librocksdb.so # ensure shared lib built
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
- run: make clean
- run: make V=1 -j32 release
- run: ls librocksdb.a # ensure static lib built
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
- post-steps
build-linux-release-rtti:
executor: linux-docker
resource_class: xlarge
steps:
- checkout # check out the code in the project directory
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
- run: ./db_stress --version # ensure with gflags
- run: make clean
- run: apt-get remove -y libgflags-dev
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j16 static_lib tools db_bench
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
build-linux-clang-no_test_run:
executor: linux-docker
resource_class: xlarge
steps:
- checkout # check out the code in the project directory
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make V=1 -j16 all
- post-steps
build-linux-clang10-asan:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
- post-steps
build-linux-clang10-mini-tsan:
executor: linux-docker
resource_class: 2xlarge+
steps:
- pre-steps
- run: COMPILE_WITH_TSAN=1 CC=clang-13 CXX=clang++-13 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
- post-steps
build-linux-clang10-ubsan:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check # aligned new doesn't work for reason we haven't figured out
- post-steps
build-linux-valgrind:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: PORTABLE=1 make V=1 -j32 valgrind_test
- post-steps
build-linux-clang10-clang-analyze:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze # aligned new doesn't work for reason we haven't figured out. For unknown, reason passing "clang++-10" as CLANG_ANALYZER doesn't work, and we need a full path.
- post-steps
- run:
name: "compress test report"
command: tar -cvzf scan_build_report.tar.gz scan_build_report
when: on_fail
- store_artifacts:
path: scan_build_report.tar.gz
destination: scan_build_report
when: on_fail
build-linux-runner:
machine: true
resource_class: facebook/rocksdb-benchmark-sys1
steps:
- pre-steps
- run:
name: "Checked Linux build (Runner)"
command: make V=1 J=8 -j8 check
environment:
LD_LIBRARY_PATH: /usr/local/lib
- post-steps
build-linux-cmake-with-folly:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- build-folly
- run: (mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)
- post-steps
build-linux-cmake-with-folly-lite-no-test:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- run: (mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)
- post-steps
build-linux-cmake-with-benchmark:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: mkdir build && cd build && cmake -DWITH_GFLAGS=1 -DWITH_BENCHMARK=1 .. && make V=1 -j20 && ctest -j20
- post-steps
build-linux-unity-and-headers:
docker: # executor type
- image: gcc:latest
environment:
EXTRA_CXXFLAGS: -mno-avx512f # Warnings-as-error in avx512fintrin.h, would be used on newer hardware
resource_class: large
steps:
- checkout # check out the code in the project directory
- run: apt-get update -y && apt-get install -y libgflags-dev
- run:
name: "Unity build"
command: make V=1 -j8 unity_test
no_output_timeout: 20m
- run: make V=1 -j8 -k check-headers # could be moved to a different build
- post-steps
build-linux-gcc-7-with-folly:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- build-folly
- run: USE_FOLLY=1 LIB_MODE=static CC=gcc-7 CXX=g++-7 V=1 make -j32 check # TODO: LIB_MODE only to work around unresolved linker failures
- post-steps
build-linux-gcc-7-with-folly-lite-no-test:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- run: USE_FOLLY_LITE=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 all
- post-steps
build-linux-gcc-8-no_test_run:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: CC=gcc-8 CXX=g++-8 V=1 make -j32 all
- post-steps
build-linux-cmake-with-folly-coroutines:
executor: linux-docker
resource_class: 2xlarge
environment:
CC: gcc-10
CXX: g++-10
steps:
- pre-steps
- setup-folly
- build-folly
- run: (mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make V=1 -j20 && ctest -j20)
- post-steps
build-linux-gcc-10-cxx20-no_test_run:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: CC=gcc-10 CXX=g++-10 V=1 ROCKSDB_CXX_STANDARD=c++20 make -j32 all
- post-steps
build-linux-gcc-11-no_test_run:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench # TODO: LIB_MODE only to work around unresolved linker failures
- post-steps
build-linux-clang-13-no_test_run:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j32 all microbench
- post-steps
# Ensure ASAN+UBSAN with folly, and full testsuite with clang 13
build-linux-clang-13-asan-ubsan-with-folly:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- setup-folly
- build-folly
- run: CC=clang-13 CXX=clang++-13 LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check # TODO: LIB_MODE only to work around unresolved linker failures
- post-steps
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
build-linux-run-microbench:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run: DEBUG_LEVEL=0 make -j32 run_microbench
- post-steps
build-linux-mini-crashtest:
executor: linux-docker
resource_class: large
steps:
- pre-steps
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
- post-steps
build-linux-crashtest-tiered-storage-bb:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run:
name: "run crashtest"
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' blackbox_crash_test_with_tiered_storage
no_output_timeout: 100m
- post-steps
build-linux-crashtest-tiered-storage-wb:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run:
name: "run crashtest"
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' whitebox_crash_test_with_tiered_storage
no_output_timeout: 100m
- post-steps
build-windows-vs2022-avx2:
executor:
name: win/server-2022
size: 2xlarge
environment:
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
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: C:/Users/circleci/thirdparty/snappy-1.1.8
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: AVX2
steps:
- windows-build-steps
build-windows-vs2022:
executor:
name: win/server-2022
size: 2xlarge
environment:
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
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: C:/Users/circleci/thirdparty/snappy-1.1.8
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
CMAKE_GENERATOR: Visual Studio 17 2022
CMAKE_PORTABLE: 1
steps:
- windows-build-steps
build-windows-vs2019:
executor:
name: win/server-2019
size: 2xlarge
environment:
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
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: C:/Users/circleci/thirdparty/snappy-1.1.8
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_PORTABLE: 1
steps:
- windows-build-steps
build-linux-java:
executor: linux-java-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Test RocksDBJava"
command: scl enable devtoolset-7 'make V=1 J=8 -j8 jtest'
- post-steps
build-linux-java-pmd:
executor: linux-java-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- install-maven
- run:
name: "PMD RocksDBJava"
command: scl enable devtoolset-7 'make V=1 J=8 -j8 jpmd'
- post-pmd-steps
build-linux-java-static:
executor: linux-java-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Build RocksDBJava Static Library"
command: scl enable devtoolset-7 'make V=1 J=8 -j8 rocksdbjavastatic'
- post-steps
build-macos-java:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
environment:
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc causes java 8 crash
steps:
- increase-max-open-files-on-macos
- install-gflags-on-macos
- install-jdk8-on-macos
- pre-steps-macos
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Test RocksDBJava"
command: make V=1 J=16 -j16 jtest
no_output_timeout: 20m
- post-steps
build-macos-java-static:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
environment:
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
steps:
- increase-max-open-files-on-macos
- install-gflags-on-macos
- install-cmake-on-macos
- install-jdk8-on-macos
- pre-steps-macos
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Build RocksDBJava x86 and ARM Static Libraries"
command: make V=1 J=16 -j16 rocksdbjavastaticosx
no_output_timeout: 20m
- post-steps
build-macos-java-static-universal:
macos:
xcode: 14.3.1
resource_class: macos.m1.medium.gen1
environment:
JAVA_HOME: /Library/Java/JavaVirtualMachines/liberica-jdk-8.jdk/Contents/Home
steps:
- increase-max-open-files-on-macos
- install-gflags-on-macos
- install-cmake-on-macos
- install-jdk8-on-macos
- pre-steps-macos
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Build RocksDBJava Universal Binary Static Library"
command: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
no_output_timeout: 20m
- post-steps
build-examples:
executor: linux-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Build examples"
command: |
make V=1 -j4 static_lib && cd examples && make V=1 -j4
- post-steps
build-cmake-mingw:
executor: linux-docker
resource_class: large
steps:
- pre-steps
- run: update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
- run:
name: "Build cmake-mingw"
command: |
export PATH=$JAVA_HOME/bin:$PATH
echo "JAVA_HOME=${JAVA_HOME}"
which java && java -version
which javac && javac -version
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
- post-steps
build-linux-non-shm:
executor: linux-docker
resource_class: 2xlarge
environment:
TEST_TMPDIR: /tmp/rocksdb_test_tmp
steps:
- pre-steps
- run: make V=1 -j32 check
- post-steps
build-linux-arm-test-full:
machine:
image: ubuntu-2004:202111-02
resource_class: arm.large
steps:
- pre-steps
- install-gflags
- run: make V=1 J=4 -j4 check
- post-steps
build-linux-arm:
machine:
image: ubuntu-2004:202111-02
resource_class: arm.large
steps:
- pre-steps
- install-gflags
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
- post-steps
build-linux-arm-cmake-no_test_run:
machine:
image: ubuntu-2004:202111-02
resource_class: arm.large
environment:
JAVA_HOME: /usr/lib/jvm/java-8-openjdk-arm64
steps:
- pre-steps
- install-gflags
- run:
name: "Set Java Environment"
command: |
echo "JAVA_HOME=${JAVA_HOME}"
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
which java && java -version
which javac && javac -version
- run:
name: "Build with cmake"
command: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=1 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 ..
make -j4
- run:
name: "Build Java with cmake"
command: |
rm -rf build
mkdir build
cd build
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=1 ..
make -j4 rocksdb rocksdbjni
- post-steps
build-format-compatible:
executor: linux-docker
resource_class: 2xlarge
steps:
- pre-steps
- run:
name: "test"
command: |
export TEST_TMPDIR=/dev/shm/rocksdb
rm -rf /dev/shm/rocksdb
mkdir /dev/shm/rocksdb
tools/check_format_compatible.sh
- post-steps
build-fuzzers:
executor: linux-docker
resource_class: large
steps:
- pre-steps
- run:
name: "Build rocksdb lib"
command: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j4 static_lib
- run:
name: "Build fuzzers"
command: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
- post-steps
benchmark-linux: #use a private Circle CI runner (resource_class) to run the job
machine: true
resource_class: facebook/rocksdb-benchmark-sys1
steps:
- build-for-benchmarks
- perform-benchmarks
- post-benchmarks
workflows: # Only jobs that haven't been successfully migrated to GitHub Actions
version: 2
jobs-linux-arm:
jobs:
- build-linux-arm
benchmark-linux:
triggers:
- schedule:
cron: "0 * * * *"
filters:
branches:
only:
- main
jobs:
- benchmark-linux
nightly:
triggers:
- schedule:
cron: "0 9 * * *"
filters:
branches:
only:
- main
jobs:
- build-linux-arm-test-full
-6
View File
@@ -1,6 +0,0 @@
# Supress UBSAN warnings related to stl_tree.h, e.g.
# UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_tree.h:1505:43 in
# /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_tree.h:1505:43:
# runtime error: upcast of address 0x000001fa8820 with insufficient space for an object of type
# 'std::_Rb_tree_node<std::pair<const std::__cxx11::basic_string<char>, rocksdb::(anonymous namespace)::LockHoldingInfo> >'
src:*bits/stl_tree.h
+4 -4
View File
@@ -31,7 +31,7 @@ jobs:
# ======================== Fast Initial Checks ====================== #
check-format-and-targets:
if: ${{ github.repository_owner == 'facebook' }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4.1.0
with:
@@ -45,7 +45,7 @@ jobs:
- name: Install argparse
run: pip install argparse
- name: Download clang-format-diff.py
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/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
- name: Check format
run: VERBOSE_CHECK=1 make check-format
- name: Compare buckify output
@@ -390,9 +390,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.0
- uses: "./.github/actions/pre-steps"
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check
- run: COMPILE_WITH_UBSAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check
- uses: "./.github/actions/post-steps"
build-linux-clang10-mini-tsan:
build-linux-clang13-mini-tsan:
if: ${{ github.repository_owner == 'facebook' }}
runs-on:
labels: 32-core-ubuntu
+1
View File
@@ -91,6 +91,7 @@ buckifier/__pycache__
.arcconfig
compile_commands.json
meta_gen_cpp_compile_commands.json
clang-format-diff.py
.py3/
+30
View File
@@ -83,6 +83,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"db/log_writer.cc",
"db/logs_with_prep_tracker.cc",
"db/malloc_stats.cc",
"db/manifest_ops.cc",
"db/memtable.cc",
"db/memtable_list.cc",
"db/merge_helper.cc",
@@ -213,6 +214,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"table/cuckoo/cuckoo_table_builder.cc",
"table/cuckoo/cuckoo_table_factory.cc",
"table/cuckoo/cuckoo_table_reader.cc",
"table/external_table.cc",
"table/format.cc",
"table/get_context.cc",
"table/iterator.cc",
@@ -316,6 +318,8 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
"utilities/persistent_cache/block_cache_tier_metadata.cc",
"utilities/persistent_cache/persistent_cache_tier.cc",
"utilities/persistent_cache/volatile_tier_impl.cc",
"utilities/secondary_index/secondary_index_iterator.cc",
"utilities/secondary_index/simple_secondary_index.cc",
"utilities/simulator_cache/cache_simulator.cc",
"utilities/simulator_cache/sim_cache.cc",
"utilities/table_properties_collectors/compact_for_tiering_collector.cc",
@@ -367,6 +371,11 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=[], link_whole=True, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_with_faiss_lib", srcs=["utilities/secondary_index/faiss_ivf_index.cc"], deps=[
"//faiss:faiss",
":rocksdb_lib",
], headers=[], link_whole=False, extra_test_libs=False)
cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
"db/db_test_util.cc",
"db/db_with_timestamp_test_util.cc",
@@ -381,11 +390,26 @@ cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
"utilities/cassandra/test_utils.cc",
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=True)
cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
"db/db_test_util.cc",
"db/db_with_timestamp_test_util.cc",
"table/mock_table.cc",
"test_util/mock_time_env.cc",
"test_util/secondary_cache_test_util.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)
cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
"test_util/testutil.cc",
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
"tools/db_bench_tool.cc",
"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)
@@ -5077,6 +5101,12 @@ cpp_unittest_wrapper(name="external_sst_file_test",
extra_compiler_flags=[])
cpp_unittest_wrapper(name="faiss_ivf_index_test",
srcs=["utilities/secondary_index/faiss_ivf_index_test.cc"],
deps=[":rocksdb_with_faiss_test_lib"],
extra_compiler_flags=[])
cpp_unittest_wrapper(name="fault_injection_test",
srcs=["db/fault_injection_test.cc"],
deps=[":rocksdb_test_lib"],
+5
View File
@@ -716,6 +716,7 @@ set(SOURCES
db/log_reader.cc
db/log_writer.cc
db/malloc_stats.cc
db/manifest_ops.cc
db/memtable.cc
db/memtable_list.cc
db/merge_helper.cc
@@ -834,6 +835,7 @@ set(SOURCES
table/cuckoo/cuckoo_table_builder.cc
table/cuckoo/cuckoo_table_factory.cc
table/cuckoo/cuckoo_table_reader.cc
table/external_table.cc
table/format.cc
table/get_context.cc
table/iterator.cc
@@ -938,6 +940,8 @@ set(SOURCES
utilities/persistent_cache/block_cache_tier_metadata.cc
utilities/persistent_cache/persistent_cache_tier.cc
utilities/persistent_cache/volatile_tier_impl.cc
utilities/secondary_index/secondary_index_iterator.cc
utilities/secondary_index/simple_secondary_index.cc
utilities/simulator_cache/cache_simulator.cc
utilities/simulator_cache/sim_cache.cc
utilities/table_properties_collectors/compact_for_tiering_collector.cc
@@ -1570,6 +1574,7 @@ if(WITH_BENCHMARK_TOOLS)
add_executable(db_bench${ARTIFACT_SUFFIX}
tools/simulated_hybrid_file_system.cc
tools/db_bench.cc
tools/tool_hooks.cc
tools/db_bench_tool.cc)
target_link_libraries(db_bench${ARTIFACT_SUFFIX}
${ROCKSDB_LIB} ${THIRDPARTY_LIBS})
+90
View File
@@ -1,6 +1,96 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 10.0.1 (03/05/2025)
### Public API Changes
* Add an unordered map of name/value pairs, ReadOptions::property_bag, to pass opaque options through to an external table when creating an Iterator.
* Introduced CompactionServiceJobStatus::kAborted to allow handling aborted scenario in Schedule(), Wait() or OnInstallation() APIs in Remote Compactions.
* Added a column family option disallow_memtable_writes to safely fail any attempts to write to a non-default column family. This can be used for column families that are ingest only.
## 10.0.0 (02/21/2025)
### New Features
* Introduced new `auto_refresh_iterator_with_snapshot` opt-in knob that (when enabled) will periodically release obsolete memory and storage resources for as long as the iterator is making progress and its supplied `read_options.snapshot` was initialized with non-nullptr value.
* Added the ability to plug-in a custom table reader implementation. See include/rocksdb/external_table_reader.h for more details.
* Experimental feature: RocksDB now supports FAISS inverted file based indices via the secondary indexing framework. Applications can use FAISS secondary indices to automatically quantize embeddings and perform K-nearest-neighbors similarity searches. See `FaissIVFIndex` and `SecondaryIndex` for more details. Note: the FAISS integration currently requires using the BUCK build.
* Add new DB property `num_running_compaction_sorted_runs` that tracks the number of sorted runs being processed by currently running compactions
* Experimental feature: added support for simple secondary indices that index the specified column as-is. See `SimpleSecondaryIndex` and `SecondaryIndex` for more details.
* Added new `TransactionDBOptions::txn_commit_bypass_memtable_threshold`, which enables optimized transaction commit (see `TransactionOptions::commit_bypass_memtable`) when the transaction size exceeds a configured threshold.
### Public API Changes
* Updated the query API of the experimental secondary indexing feature by removing the earlier `SecondaryIndex::NewIterator` virtual and adding a `SecondaryIndexIterator` class that can be utilized by applications to find the primary keys for a given search target.
* Added back the ability to leverage the primary key when building secondary index entries. This involved changes to the signatures of `SecondaryIndex::GetSecondary{KeyPrefix,Value}` as well as the addition of a new method `SecondaryIndex::FinalizeSecondaryKeyPrefix`. See the API comments for more details.
* Minimum supported version of ZSTD is now 1.4.0, for code simplification. Obsolete `CompressionType` `kZSTDNotFinalCompression` is also removed.
### Behavior Changes
* `VerifyBackup` in `verify_with_checksum`=`true` mode will now evaluate checksums in parallel. As a result, unlike in case of original implementation, the API won't bail out on a very first corruption / mismatch and instead will iterate over all the backup files logging success / _degree_of_failure_ for each.
* Reversed the order of updates to the same key in WriteBatchWithIndex. This means if there are multiple updates to the same key, the most recent update is ordered first. This affects the output of WBWIIterator. When WriteBatchWithIndex is created with `overwrite_key=true`, this affects the output only if Merge is used (#13387).
* Added support for Merge operations in transactions using option `TransactionOptions::commit_bypass_memtable`.
### Bug Fixes
* Fixed GetMergeOperands() API in ReadOnlyDB and SecondaryDB
* Fix a bug in `GetMergeOperands()` that can return incorrect status (MergeInProgress) and incorrect number of merge operands. This can happen when `GetMergeOperandsOptions::continue_cb` is set, both active and immutable memtables have merge operands and the callback stops the look up at the immutable memtable.
## 9.11.0 (01/17/2025)
### New Features
* Introduce CancelAwaitingJobs() in CompactionService interface which will allow users to implement cancellation of running remote compactions from the primary instance
* Experimental feature: RocksDB now supports defining secondary indices, which are automatically maintained by the storage engine. Secondary indices provide a new customization point: applications can provide their own by implementing the new `SecondaryIndex` interface. See the `SecondaryIndex` API comments for more details. Note: this feature is currently only available in conjunction with write-committed pessimistic transactions, and `Merge` is not yet supported.
* Provide a new option `track_and_verify_wals` to track and verify various information about WAL during WAL recovery. This is intended to be a better replacement to `track_and_verify_wals_in_manifest`.
### Public API Changes
* Add `io_buffer_size` to BackupEngineOptions to enable optimal configuration of IO size
* Clean up all the references to `random_access_max_buffer_size`, related rules and all the clients wrappers. This option has been officially deprecated in 5.4.0.
* Add `file_ingestion_nanos` and `file_ingestion_blocking_live_writes_nanos` in PerfContext to observe file ingestions
* Offer new DB::Open and variants that use `std::unique_ptr<DB>*` output parameters and deprecate the old versions that use `DB**` output parameters.
* The DB::DeleteFile API is officially deprecated.
### Behavior Changes
* For leveled compaction, manual compaction (CompactRange()) will be more strict about keeping compaction size under `max_compaction_bytes`. This prevents overly large compactions in some cases (#13306).
* Experimental tiering options `preclude_last_level_data_seconds` and `preserve_internal_time_seconds` are now mutable with `SetOptions()`. Some changes to handling of these features along with long-lived snapshots and range deletes made this possible.
### Bug Fixes
* Fix a longstanding major bug with SetOptions() in which setting changes can be quietly reverted.
## 9.10.0 (12/12/2024)
### New Features
* Introduce `TransactionOptions::commit_bypass_memtable` to enable transaction commit to bypass memtable insertions. This can be beneficial for transactions with many operations, as it reduces commit time that is mostly spent on memtable insertion.
### Public API Changes
* Deprecated Remote Compaction APIs (StartV2, WaitForCompleteV2) are completely removed from the codebase
### Behavior Changes
* DB::KeyMayExist() now follows its function comment, which means `value` parameter can be null, and it will be set only if `value_found` is passed in.
### Bug Fixes
* Fix the issue where compaction incorrectly drops a key when there is a snapshot with a sequence number of zero.
* Honor ConfigOptions.ignore_unknown_options in ParseStruct()
### Performance Improvements
* Enable reuse of file system allocated buffer for synchronous prefetching.
* In buffered IO mode, try to align writes on power of 2 if checksum handoff is not enabled for the file type being written.
## 9.9.0 (11/18/2024)
### New Features
* Multi-Column-Family-Iterator (CoalescingIterator/AttributeGroupIterator) is no longer marked as experimental
* Adds a new table property "rocksdb.newest.key.time" which records the unix timestamp of the newest key. Uses this table property for FIFO TTL and temperature change compaction.
### Public API Changes
* Added a new API `Transaction::GetAttributeGroupIterator` that can be used to create a multi-column-family attribute group iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
* Added a new API `Transaction::GetCoalescingIterator` that can be used to create a multi-column-family coalescing iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
### Behavior Changes
* `BaseDeltaIterator` now honors the read option `allow_unprepared_value`.
### Bug Fixes
* `BaseDeltaIterator` now calls `PrepareValue` on the base iterator in case it has been created with the `allow_unprepared_value` read option set. Earlier, such base iterators could lead to incorrect values being exposed from `BaseDeltaIterator`.
* Fix a leak of obsolete blob files left open until DB::Close(). This bug was introduced in version 9.4.0.
* Fix missing cases of corruption retry during DB open and read API processing.
* Fix a bug for transaction db with 2pc where an old WAL may be retained longer than needed (#13127).
* Fix leaks of some open SST files (until `DB::Close()`) that are written but never become live due to various failures. (We now have a check for such leaks with no outstanding issues.)
* Fix a bug for replaying WALs for WriteCommitted transaction DB when its user-defined timestamps setting is toggled on/off between DB sessions.
### Performance Improvements
* Fix regression in issue #12038 due to `Options::compaction_readahead_size` greater than `max_sectors_kb` (i.e, largest I/O size that the OS issues to a block device defined in linux)
## 9.8.0 (10/25/2024)
### New Features
* All non-`block_cache` options in `BlockBasedTableOptions` are now mutable with `DB::SetOptions()`. See also Bug Fixes below.
+2 -1
View File
@@ -2,7 +2,8 @@ This is the list of all known third-party language bindings for RocksDB. If some
* Java - https://github.com/facebook/rocksdb/tree/main/java
* Python
* http://python-rocksdb.readthedocs.io/en/latest/
* https://github.com/rocksdict/RocksDict
* http://python-rocksdb.readthedocs.io/en/latest/ (unmaintained)
* http://pyrocksdb.readthedocs.org/en/latest/ (unmaintained)
* Perl - https://metacpan.org/pod/RocksDB
* Node.js - https://npmjs.org/package/rocksdb
+2 -2
View File
@@ -659,7 +659,7 @@ ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
# TODO: add/support JNI headers
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
# 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/|lua/|range_tree/')
DEV_HEADERS_TO_CHECK := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/|secondary_index/')
PUBLIC_HEADERS_TO_CHECK := $(shell $(FIND) include/ -type f -name '*.h' | grep -E -v 'lua/')
else
DEV_HEADERS_TO_CHECK :=
@@ -2489,7 +2489,7 @@ checkout_folly:
fi
@# Pin to a particular version for public CI, so that PR authors don't
@# need to worry about folly breaking our integration. Update periodically
cd third-party/folly && git reset --hard 03041f014b6e6ebb6119ffae8b7a37308f52e913
cd third-party/folly && git reset --hard f1c1542fb071e9d88181279ec24043400222aef1
@# NOTE: this hack is required for clang in some cases
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
@# NOTE: this hack is required for gcc in some cases
+4 -5
View File
@@ -38,12 +38,11 @@ Snowflake [uses](https://www.snowflake.com/blog/how-foundationdb-powers-snowflak
The Bing search engine from Microsoft uses RocksDB as the storage engine for its web data platform: https://blogs.bing.com/Engineering-Blog/october-2021/RocksDB-in-Microsoft-Bing
## LinkedIn
Two different use cases at Linkedin are using RocksDB as a storage engine:
1. [Venice](https://venicedb.org/) is a derived data platform using RocksDB as its storage engine. It is LinkedIn's ML feature store, powering thousands of recommender use cases, including the Feed, Video recommendations, and People You May Know.
2. LinkedIn's follow feed for storing user's activities. Check out the blog post: https://engineering.linkedin.com/blog/2016/03/followfeed--linkedin-s-feed-made-faster-and-smarter
3. Apache Samza, open source framework for stream processing.
1. LinkedIn's follow feed for storing user's activities. Check out the blog post: https://engineering.linkedin.com/blog/2016/03/followfeed--linkedin-s-feed-made-faster-and-smarter
2. Apache Samza, open source framework for stream processing
Learn more about those use cases in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
Learn more about LinkedIn's follow feed and Apache Samza in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
## Yahoo
Yahoo is using RocksDB as a storage engine for their biggest distributed data store Sherpa. Learn more about it here: http://yahooeng.tumblr.com/post/120730204806/sherpa-scales-new-heights
+76 -42
View File
@@ -14,10 +14,10 @@ from targets_builder import TARGETSBuilder, LiteralValue
from util import ColorString
# This script generates TARGETS file for Buck.
# This script generates BUCK file for Buck.
# Buck is a build tool specifying dependencies among different build targets.
# User can pass extra dependencies as a JSON object via command line, and this
# script can include these dependencies in the generate TARGETS file.
# script can include these dependencies in the generate BUCK file.
# Usage:
# $python3 buckifier/buckify_rocksdb.py
# (This generates a TARGET file without user-specified dependency for unit
@@ -28,7 +28,7 @@ from util import ColorString
# "extra_compiler_flags": ["-DFOO_BAR", "-Os"]
# }
# }'
# (Generated TARGETS file has test_dep and mock1 as dependencies for RocksDB
# (Generated BUCK file has test_dep and mock1 as dependencies for RocksDB
# unit tests, and will use the extra_compiler_flags to compile the unit test
# source.)
@@ -114,9 +114,9 @@ def get_dependencies():
return deps_map
# Prepare TARGETS file for buck
def generate_targets(repo_path, deps_map):
print(ColorString.info("Generating TARGETS"))
# Prepare BUCK file for buck
def generate_buck(repo_path, deps_map):
print(ColorString.info("Generating BUCK"))
# parsed src.mk file
src_mk = parse_src_mk(repo_path)
# get all .cc files
@@ -133,10 +133,10 @@ def generate_targets(repo_path, deps_map):
# in how the file was generated.
extra_argv = " '{}'".format(" ".join(sys.argv[1].split()))
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path, extra_argv)
BUCK = TARGETSBuilder("%s/BUCK" % repo_path, extra_argv)
# rocksdb_lib
TARGETS.add_library(
BUCK.add_library(
"rocksdb_lib",
src_mk["LIB_SOURCES"] +
# always add range_tree, it's only excluded on ppc64, which we don't use internally
@@ -152,7 +152,7 @@ def generate_targets(repo_path, deps_map):
headers=LiteralValue("glob([\"**/*.h\"])")
)
# rocksdb_whole_archive_lib
TARGETS.add_library(
BUCK.add_library(
"rocksdb_whole_archive_lib",
[],
deps=[
@@ -161,8 +161,17 @@ def generate_targets(repo_path, deps_map):
extra_external_deps="",
link_whole=True,
)
# rocksdb_with_faiss_lib
BUCK.add_library(
"rocksdb_with_faiss_lib",
src_mk.get("WITH_FAISS_LIB_SOURCES", []),
deps=[
"//faiss:faiss",
":rocksdb_lib",
],
)
# rocksdb_test_lib
TARGETS.add_library(
BUCK.add_library(
"rocksdb_test_lib",
src_mk.get("MOCK_LIB_SOURCES", [])
+ src_mk.get("TEST_LIB_SOURCES", [])
@@ -171,8 +180,20 @@ def generate_targets(repo_path, deps_map):
[":rocksdb_lib"],
extra_test_libs=True,
)
# 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", []),
deps=[
":rocksdb_with_faiss_lib",
],
extra_test_libs=True,
)
# rocksdb_tools_lib
TARGETS.add_library(
BUCK.add_library(
"rocksdb_tools_lib",
src_mk.get("BENCH_LIB_SOURCES", [])
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
@@ -180,51 +201,51 @@ def generate_targets(repo_path, deps_map):
[":rocksdb_lib"],
)
# rocksdb_cache_bench_tools_lib
TARGETS.add_library(
BUCK.add_library(
"rocksdb_cache_bench_tools_lib",
src_mk.get("CACHE_BENCH_LIB_SOURCES", []),
[":rocksdb_lib"],
)
# rocksdb_stress_lib
TARGETS.add_rocksdb_library(
BUCK.add_rocksdb_library(
"rocksdb_stress_lib",
src_mk.get("ANALYZER_LIB_SOURCES", [])
+ src_mk.get("STRESS_LIB_SOURCES", [])
+ ["test_util/testutil.cc"],
)
# ldb binary
TARGETS.add_binary(
BUCK.add_binary(
"ldb", ["tools/ldb.cc"], [":rocksdb_tools_lib"]
)
# db_stress binary
TARGETS.add_binary(
BUCK.add_binary(
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
)
# db_bench binary
TARGETS.add_binary(
BUCK.add_binary(
"db_bench", ["tools/db_bench.cc"], [":rocksdb_tools_lib"]
)
# cache_bench binary
TARGETS.add_binary(
BUCK.add_binary(
"cache_bench", ["cache/cache_bench.cc"], [":rocksdb_cache_bench_tools_lib"]
)
# bench binaries
for src in src_mk.get("MICROBENCH_SOURCES", []):
name = src.rsplit("/", 1)[1].split(".")[0] if "/" in src else src.split(".")[0]
TARGETS.add_binary(name, [src], [], extra_bench_libs=True)
BUCK.add_binary(name, [src], [], extra_bench_libs=True)
print(f"Extra dependencies:\n{json.dumps(deps_map)}")
# Dictionary test executable name -> relative source file path
test_source_map = {}
# c_test.c is added through TARGETS.add_c_test(). If there
# c_test.c is added through BUCK.add_c_test(). If there
# are more than one .c test file, we need to extend
# TARGETS.add_c_test() to include other C tests too.
# BUCK.add_c_test() to include other C tests too.
for test_src in src_mk.get("TEST_MAIN_SOURCES_C", []):
if test_src != "db/c_test.c":
print("Don't know how to deal with " + test_src)
return False
TARGETS.add_c_test()
BUCK.add_c_test()
try:
with open(f"{repo_path}/buckifier/bench.json") as json_file:
@@ -239,7 +260,7 @@ def generate_targets(repo_path, deps_map):
for metric in overloaded_metric_list:
if not isinstance(metric, dict):
clean_benchmarks[binary][benchmark].append(metric)
TARGETS.add_fancy_bench_config(
BUCK.add_fancy_bench_config(
config_dict["name"],
clean_benchmarks,
False,
@@ -261,7 +282,7 @@ def generate_targets(repo_path, deps_map):
if not isinstance(metric, dict):
clean_benchmarks[binary][benchmark].append(metric)
for config_dict in slow_fancy_bench_config_list:
TARGETS.add_fancy_bench_config(
BUCK.add_fancy_bench_config(
config_dict["name"] + "_slow",
clean_benchmarks,
True,
@@ -274,15 +295,20 @@ def generate_targets(repo_path, deps_map):
except Exception:
pass
TARGETS.add_test_header()
BUCK.add_test_header()
for test_src in src_mk.get("TEST_MAIN_SOURCES", []):
test = test_src.split(".c")[0].strip().split("/")[-1].strip()
test_source_map[test] = test_src
test_source_map[test] = (test_src, False)
print("" + test + " " + test_src)
for test_src in src_mk.get("WITH_FAISS_TEST_MAIN_SOURCES", []):
test = test_src.split(".c")[0].strip().split("/")[-1].strip()
test_source_map[test] = (test_src, True)
print("" + test + " " + test_src + " [FAISS]")
for target_alias, deps in deps_map.items():
for test, test_src in sorted(test_source_map.items()):
for test, (test_src, with_faiss) in sorted(test_source_map.items()):
if len(test) == 0:
print(ColorString.warning("Failed to get test name for %s" % test_src))
continue
@@ -291,31 +317,39 @@ def generate_targets(repo_path, deps_map):
if test in _EXPORTED_TEST_LIBS:
test_library = "%s_lib" % test_target_name
TARGETS.add_library(
BUCK.add_library(
test_library,
[test_src],
deps=[":rocksdb_test_lib"],
extra_test_libs=True,
)
TARGETS.register_test(
BUCK.register_test(
test_target_name,
test_src,
deps=json.dumps(deps["extra_deps"] + [":" + test_library]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
)
else:
TARGETS.register_test(
test_target_name,
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
)
TARGETS.export_file("tools/db_crashtest.py")
if with_faiss:
BUCK.register_test(
test_target_name,
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_with_faiss_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
)
else:
BUCK.register_test(
test_target_name,
test_src,
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
)
BUCK.export_file("tools/db_crashtest.py")
print(ColorString.info("Generated TARGETS Summary:"))
print(ColorString.info("- %d libs" % TARGETS.total_lib))
print(ColorString.info("- %d binarys" % TARGETS.total_bin))
print(ColorString.info("- %d tests" % TARGETS.total_test))
print(ColorString.info("Generated BUCK Summary:"))
print(ColorString.info("- %d libs" % BUCK.total_lib))
print(ColorString.info("- %d binarys" % BUCK.total_bin))
print(ColorString.info("- %d tests" % BUCK.total_test))
return True
@@ -335,10 +369,10 @@ def exit_with_error(msg):
def main():
deps_map = get_dependencies()
# Generate TARGETS file for buck
ok = generate_targets(get_rocksdb_path(), deps_map)
# Generate BUCK file for buck
ok = generate_buck(get_rocksdb_path(), deps_map)
if not ok:
exit_with_error("Failed to generate TARGETS files")
exit_with_error("Failed to generate BUCK files")
if __name__ == "__main__":
+23 -11
View File
@@ -1,32 +1,44 @@
#!/usr/bin/env bash
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
# If clang_format_diff.py command is not specfied, we assume we are able to
# access directly without any path.
TGT_DIFF=`git diff TARGETS | head -n 1`
if [[ ! -f "BUCK" ]]
then
echo "BUCK file is missing!"
echo "Please do not remove / rename BUCK file in your commit(s)."
exit 1
fi
TGT_DIFF=`git diff BUCK | head -n 1`
if [ ! -z "$TGT_DIFF" ]
then
echo "TARGETS file has uncommitted changes. Skip this check."
echo "BUCK file has uncommitted changes. Skip this check."
exit 0
fi
echo Backup original TARGETS file.
echo Backup original BUCK file.
cp TARGETS TARGETS.bkp
cp BUCK BUCK.bkp
${PYTHON:-python3} buckifier/buckify_rocksdb.py
TGT_DIFF=`git diff TARGETS | head -n 1`
if [[ ! -f "BUCK" ]]
then
echo "BUCK file went missing after running buckifier/buckify_rocksdb.py!"
echo "Please do not remove the BUCK file."
exit 1
fi
TGT_DIFF=`git diff BUCK | head -n 1`
if [ -z "$TGT_DIFF" ]
then
mv TARGETS.bkp TARGETS
mv BUCK.bkp BUCK
exit 0
else
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update TARGETS file."
echo "Do not manually update TARGETS file."
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update BUCK file."
echo "Do not manually update BUCK file."
${PYTHON:-python3} --version
mv TARGETS.bkp TARGETS
mv BUCK.bkp BUCK
exit 1
fi
+5 -1
View File
@@ -360,9 +360,13 @@ EOF
fi
if ! test $ROCKSDB_DISABLE_ZSTD; then
# Test whether zstd library is installed
# Test whether zstd library is installed with minimum version
# (Keep in sync with compression.h)
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
#include <zstd.h>
#if ZSTD_VERSION_NUMBER < 10400
#error "ZSTD support requires version >= 1.4.0 (libzstd-devel)"
#endif // ZSTD_VERSION_NUMBER
int main() {}
EOF
if [ "$?" = 0 ]; then
+1 -1
View File
@@ -59,7 +59,7 @@ fi
if ! test $ROCKSDB_DISABLE_ZSTD; then
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
CFLAGS+=" -DZSTD"
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
fi
# location of gflags headers and libraries
-1
View File
@@ -118,7 +118,6 @@ Status SecondaryCache::CreateFromString(
sec_cache = NewCompressedSecondaryCache(sec_cache_opts);
}
if (status.ok()) {
result->swap(sec_cache);
}
+1 -2
View File
@@ -191,8 +191,7 @@ namespace {
class SharedState {
public:
explicit SharedState(CacheBench* cache_bench)
: cv_(&mu_),
cache_bench_(cache_bench) {}
: cv_(&mu_), cache_bench_(cache_bench) {}
~SharedState() = default;
-1
View File
@@ -845,7 +845,6 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
BasicTestHelper(sec_cache, sec_cache_is_compressed_);
}
TEST_P(CompressedSecondaryCacheTestWithCompressionParam, FailsTest) {
FailsTest(sec_cache_is_compressed_);
}
+2 -3
View File
@@ -93,9 +93,8 @@ void LRUHandleTable::Resize() {
uint32_t old_length = uint32_t{1} << length_bits_;
int new_length_bits = length_bits_ + 1;
std::unique_ptr<LRUHandle* []> new_list {
new LRUHandle* [size_t{1} << new_length_bits] {}
};
std::unique_ptr<LRUHandle*[]> new_list{
new LRUHandle* [size_t{1} << new_length_bits] {}};
[[maybe_unused]] uint32_t count = 0;
for (uint32_t i = 0; i < old_length; i++) {
LRUHandle* h = list_[i];
+2 -2
View File
@@ -546,7 +546,7 @@ TYPED_TEST(ClockCacheTest, Limits) {
// verify usage tracking on detached entries.)
{
size_t n = kCapacity * 5 + 1;
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [n] {} };
std::unique_ptr<HandleImpl*[]> ha{new HandleImpl* [n] {}};
Status s;
for (size_t i = 0; i < n && s.ok(); ++i) {
hkey[1] = i;
@@ -713,7 +713,7 @@ TYPED_TEST(ClockCacheTest, ClockEvictionEffortCapTest) {
// evictable entries are present beyond the cache capacity, despite
// being evictable.
constexpr size_t kCount = kCapacity - 1;
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [kCount] {} };
std::unique_ptr<HandleImpl*[]> ha{new HandleImpl* [kCount] {}};
for (size_t i = 0; i < 2 * kCount; ++i) {
UniqueId64x2 hkey = this->CheapHash(i);
ASSERT_OK(shard.Insert(
+1 -3
View File
@@ -7,6 +7,4 @@
#include "cache/cache_entry_roles.h"
namespace ROCKSDB_NAMESPACE {
} // namespace ROCKSDB_NAMESPACE
namespace ROCKSDB_NAMESPACE {} // namespace ROCKSDB_NAMESPACE
+13 -5
View File
@@ -951,12 +951,20 @@ TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
IODebugContext* dbg) override {
for (size_t i = 0; i < num_reqs; ++i) {
FSReadRequest& req = reqs[i];
FSAllocationPtr buffer(new char[req.len], [](void* ptr) {
delete[] static_cast<char*>(ptr);
});
req.fs_scratch = std::move(buffer);
// See https://github.com/facebook/rocksdb/pull/13195 for why we
// want to set up our test implementation for FSAllocationPtr this
// way.
char* internalData = new char[req.len];
req.status = Read(req.offset, req.len, options, &req.result,
static_cast<char*>(req.fs_scratch.get()), dbg);
internalData, dbg);
Slice* internalSlice = new Slice(internalData, req.len);
FSAllocationPtr internalPtr(internalSlice, [](void* ptr) {
delete[] static_cast<const char*>(
static_cast<Slice*>(ptr)->data_);
delete static_cast<Slice*>(ptr);
});
req.fs_scratch = std::move(internalPtr);
}
return IOStatus::OK();
}
+112 -25
View File
@@ -64,8 +64,118 @@ void ArenaWrappedDBIter::Init(
memtable_range_tombstone_iter_ = nullptr;
}
void ArenaWrappedDBIter::MaybeAutoRefresh(bool is_seek,
DBIter::Direction direction) {
if (cfh_ != nullptr && read_options_.snapshot != nullptr && allow_refresh_ &&
read_options_.auto_refresh_iterator_with_snapshot) {
// The intent here is to capture the superversion number change
// reasonably soon from the time it actually happened. As such,
// we're fine with weaker synchronization / ordering guarantees
// provided by relaxed atomic (in favor of less CPU / mem overhead).
uint64_t cur_sv_number = cfh_->cfd()->GetSuperVersionNumberRelaxed();
if ((sv_number_ != cur_sv_number) && status().ok()) {
// Changing iterators' direction is pretty heavy-weight operation and
// could have unintended consequences when it comes to prefix seek.
// Therefore, we need an efficient implementation that does not duplicate
// the effort by doing things like double seek(forprev).
//
// Auto refresh can be triggered on the following groups of operations:
//
// 1. [Seek]: Seek(), SeekForPrev()
// 2. [Non-Seek]: Next(), Prev()
//
// In case of 'Seek' group, procedure is fairly straightforward as we'll
// simply call refresh and then invoke the operation on intended target.
//
// In case of 'Non-Seek' group, we'll first advance the cursor by invoking
// intended user operation (Next() or Prev()), capture the target key T,
// refresh the iterator and then reconcile the refreshed iterator by
// explicitly calling [Seek(T) or SeekForPrev(T)]. Below is an example
// flow for Next(), but same principle applies to Prev():
//
//
// T0: Before the operation T1: Execute Next()
// | |
// | -------------
// | | * capture the key (T)
// DBIter(SV#A) | |
// --------------\ /------\ /---------
// SV #A | ... -> [ X ] -> [ T ] -> ... |
// -----------------------------------
// / |
// / |
// / T2: Refresh iterator
// /
// DBIter(SV#A') /
// ----------------------------------
// SV #A' | ... -> [ T ] -> ... |
// ----------------/ \---------------
// |
// ---- T3: Seek(T)
//
bool valid = false;
std::string key;
if (!is_seek && db_iter_->Valid()) {
// The key() Slice is valid until the iterator state changes.
// Given that refresh is heavy-weight operation it itself,
// we should copy the target key upfront to avoid reading bad value.
valid = true;
key = db_iter_->key().ToString();
}
// It's perfectly fine to unref the corresponding superversion
// as we rely on pinning behavior of snapshot for consistency.
DoRefresh(read_options_.snapshot, cur_sv_number);
if (!is_seek && valid) { // Reconcile new iterator after Next() / Prev()
if (direction == DBIter::kForward) {
db_iter_->Seek(key);
} else {
assert(direction == DBIter::kReverse);
db_iter_->SeekForPrev(key);
}
}
}
}
}
Status ArenaWrappedDBIter::Refresh() { return Refresh(nullptr); }
void ArenaWrappedDBIter::DoRefresh(const Snapshot* snapshot,
[[maybe_unused]] uint64_t sv_number) {
Env* env = db_iter_->env();
// NOTE:
//
// Errors like file deletion (as a part of SV cleanup in ~DBIter) will be
// present in the error log, but won't be reflected in the iterator status.
// This is by design as we expect compaction to clean up those obsolete files
// eventually.
db_iter_->~DBIter();
arena_.~Arena();
new (&arena_) Arena();
auto cfd = cfh_->cfd();
auto db_impl = cfh_->db();
SuperVersion* sv = cfd->GetReferencedSuperVersion(db_impl);
assert(sv->version_number >= sv_number);
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
if (read_callback_) {
read_callback_->Refresh(read_seq);
}
Init(env, read_options_, cfd->ioptions(), sv->mutable_cf_options, sv->current,
read_seq, sv->mutable_cf_options.max_sequential_skip_in_iterations,
sv->version_number, read_callback_, cfh_, expose_blob_index_,
allow_refresh_);
InternalIterator* internal_iter = db_impl->NewInternalIterator(
read_options_, cfd, sv, &arena_, read_seq,
/* allow_unprepared_value */ true, /* db_iter */ this);
SetIterUnderDBIter(internal_iter);
}
Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
if (cfh_ == nullptr || !allow_refresh_) {
return Status::NotSupported("Creating renew iterator is not allowed.");
@@ -85,32 +195,9 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
auto reinit_internal_iter = [&]() {
Env* env = db_iter_->env();
db_iter_->~DBIter();
arena_.~Arena();
new (&arena_) Arena();
SuperVersion* sv = cfd->GetReferencedSuperVersion(db_impl);
assert(sv->version_number >= cur_sv_number);
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
if (read_callback_) {
read_callback_->Refresh(read_seq);
}
Init(env, read_options_, *(cfd->ioptions()), sv->mutable_cf_options,
sv->current, read_seq,
sv->mutable_cf_options.max_sequential_skip_in_iterations,
sv->version_number, read_callback_, cfh_, expose_blob_index_,
allow_refresh_);
InternalIterator* internal_iter = db_impl->NewInternalIterator(
read_options_, cfd, sv, &arena_, read_seq,
/* allow_unprepared_value */ true, /* db_iter */ this);
SetIterUnderDBIter(internal_iter);
};
while (true) {
if (sv_number_ != cur_sv_number) {
reinit_internal_iter();
DoRefresh(snapshot, cur_sv_number);
break;
} else {
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
@@ -138,7 +225,7 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
// The memtable under DBIter did not have range tombstone before
// refresh.
reinit_internal_iter();
DoRefresh(snapshot, cur_sv_number);
break;
} else {
*memtable_range_tombstone_iter_ =
+20 -3
View File
@@ -65,12 +65,26 @@ class ArenaWrappedDBIter : public Iterator {
void SeekToLast() override { db_iter_->SeekToLast(); }
// 'target' does not contain timestamp, even if user timestamp feature is
// enabled.
void Seek(const Slice& target) override { db_iter_->Seek(target); }
void Seek(const Slice& target) override {
MaybeAutoRefresh(true /* is_seek */, DBIter::kForward);
db_iter_->Seek(target);
}
void SeekForPrev(const Slice& target) override {
MaybeAutoRefresh(true /* is_seek */, DBIter::kReverse);
db_iter_->SeekForPrev(target);
}
void Next() override { db_iter_->Next(); }
void Prev() override { db_iter_->Prev(); }
void Next() override {
db_iter_->Next();
MaybeAutoRefresh(false /* is_seek */, DBIter::kForward);
}
void Prev() override {
db_iter_->Prev();
MaybeAutoRefresh(false /* is_seek */, DBIter::kReverse);
}
Slice key() const override { return db_iter_->key(); }
Slice value() const override { return db_iter_->value(); }
const WideColumns& columns() const override { return db_iter_->columns(); }
@@ -103,6 +117,9 @@ class ArenaWrappedDBIter : public Iterator {
}
private:
void DoRefresh(const Snapshot* snapshot, uint64_t sv_number);
void MaybeAutoRefresh(bool is_seek, DBIter::Direction direction);
DBIter* db_iter_ = nullptr;
Arena arena_;
uint64_t sv_number_;
+2 -2
View File
@@ -82,8 +82,8 @@ Status BlobSource::PutBlobIntoCache(
assert(cached_blob->IsEmpty());
TypedHandle* cache_handle = nullptr;
const Status s = InsertEntryIntoCache(cache_key, blob->get(),
&cache_handle, Cache::Priority::BOTTOM);
const Status s = InsertEntryIntoCache(cache_key, blob->get(), &cache_handle,
Cache::Priority::BOTTOM);
if (s.ok()) {
blob->release();
+30 -5
View File
@@ -231,7 +231,7 @@ struct rocksdb_livefiles_t {
};
struct rocksdb_column_family_handle_t {
ColumnFamilyHandle* rep;
bool immortal; /* only true for default cf */
bool immortal; /* only true for default cf */
};
struct rocksdb_column_family_metadata_t {
ColumnFamilyMetaData rep;
@@ -1858,10 +1858,6 @@ extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf_with_flags(
delete[] ranges;
}
void rocksdb_delete_file(rocksdb_t* db, const char* name) {
db->rep->DeleteFile(name);
}
const rocksdb_livefiles_t* rocksdb_livefiles(rocksdb_t* db) {
rocksdb_livefiles_t* result = new rocksdb_livefiles_t;
db->rep->GetLiveFilesMetaData(&result->rep);
@@ -5000,6 +4996,35 @@ int rocksdb_compactoptions_get_target_level(rocksdb_compactoptions_t* opt) {
return opt->rep.target_level;
}
void rocksdb_compactoptions_set_target_path_id(rocksdb_compactoptions_t* opt,
int n) {
opt->rep.target_path_id = n;
}
int rocksdb_compactoptions_get_target_path_id(rocksdb_compactoptions_t* opt) {
return opt->rep.target_path_id;
}
void rocksdb_compactoptions_set_allow_write_stall(rocksdb_compactoptions_t* opt,
unsigned char v) {
opt->rep.allow_write_stall = v;
}
unsigned char rocksdb_compactoptions_get_allow_write_stall(
rocksdb_compactoptions_t* opt) {
return opt->rep.allow_write_stall;
}
void rocksdb_compactoptions_set_max_subcompactions(
rocksdb_compactoptions_t* opt, int n) {
opt->rep.max_subcompactions = n;
}
int rocksdb_compactoptions_get_max_subcompactions(
rocksdb_compactoptions_t* opt) {
return opt->rep.max_subcompactions;
}
void rocksdb_compactoptions_set_full_history_ts_low(
rocksdb_compactoptions_t* opt, char* ts, size_t tslen) {
if (ts == nullptr) {
+9
View File
@@ -2938,6 +2938,15 @@ int main(int argc, char** argv) {
rocksdb_compactoptions_set_target_level(co, 1);
CheckCondition(1 == rocksdb_compactoptions_get_target_level(co));
rocksdb_compactoptions_set_target_path_id(co, 1);
CheckCondition(1 == rocksdb_compactoptions_get_target_path_id(co));
rocksdb_compactoptions_set_allow_write_stall(co, 1);
CheckCondition(1 == rocksdb_compactoptions_get_allow_write_stall(co));
rocksdb_compactoptions_set_max_subcompactions(co, 1);
CheckCondition(1 == rocksdb_compactoptions_get_max_subcompactions(co));
rocksdb_compactoptions_destroy(co);
}
+16 -13
View File
@@ -53,7 +53,7 @@ ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(
ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
if (cfd_ != nullptr) {
for (auto& listener : cfd_->ioptions()->listeners) {
for (auto& listener : cfd_->ioptions().listeners) {
listener->OnColumnFamilyHandleDeletionStarted(this);
}
// Job id == 0 means that this is not our background process, but rather
@@ -168,7 +168,8 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options) {
}
if (!cf_options.memtable_factory->IsInsertConcurrentlySupported()) {
return Status::InvalidArgument(
"Memtable doesn't concurrent writes (allow_concurrent_memtable_write)");
"Memtable doesn't allow concurrent writes "
"(allow_concurrent_memtable_write)");
}
return Status::OK();
}
@@ -239,6 +240,10 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
result.min_write_buffer_number_to_merge = 1;
}
if (result.disallow_memtable_writes) {
// A simple memtable that enforces MarkReadOnly (unlike skip list)
result.memtable_factory = std::make_shared<VectorRepFactory>();
}
if (result.num_levels < 1) {
result.num_levels = 1;
@@ -593,7 +598,7 @@ ColumnFamilyData::ColumnFamilyData(
block_cache_tracer, io_tracer,
db_session_id));
blob_file_cache_.reset(
new BlobFileCache(_table_cache, ioptions(), soptions(), id_,
new BlobFileCache(_table_cache, &ioptions(), soptions(), id_,
internal_stats_->GetBlobFileReadHist(), io_tracer));
blob_source_.reset(new BlobSource(ioptions_, mutable_cf_options_, db_id,
db_session_id, blob_file_cache_.get()));
@@ -968,7 +973,7 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
auto write_stall_condition_and_cause = GetWriteStallConditionAndCause(
imm()->NumNotFlushed(), vstorage->l0_delay_trigger_count(),
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
*ioptions());
ioptions());
write_stall_condition = write_stall_condition_and_cause.first;
auto write_stall_cause = write_stall_condition_and_cause.second;
@@ -1160,12 +1165,13 @@ MemTable* ColumnFamilyData::ConstructNewMemtable(
write_buffer_manager_, earliest_seq, id_);
}
void ColumnFamilyData::CreateNewMemtable(
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
void ColumnFamilyData::CreateNewMemtable(SequenceNumber earliest_seq) {
if (mem_ != nullptr) {
delete mem_->Unref();
}
SetMemtable(ConstructNewMemtable(mutable_cf_options, earliest_seq));
// NOTE: db mutex must be locked for SetMemtable, so safe for
// GetLatestMutableCFOptions
SetMemtable(ConstructNewMemtable(GetLatestMutableCFOptions(), earliest_seq));
mem_->Ref();
}
@@ -1208,7 +1214,7 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
MergeIteratorBuilder merge_iter_builder(&internal_comparator_, &arena);
merge_iter_builder.AddIterator(super_version->mem->NewIterator(
read_opts, /*seqno_to_time_mapping=*/nullptr, &arena,
/*prefix_extractor=*/nullptr));
/*prefix_extractor=*/nullptr, /*for_flush=*/false));
super_version->imm->AddIterators(read_opts, /*seqno_to_time_mapping=*/nullptr,
/*prefix_extractor=*/nullptr,
&merge_iter_builder,
@@ -1384,7 +1390,7 @@ void ColumnFamilyData::InstallSuperVersion(
new_superversion->write_stall_condition) {
sv_context->PushWriteStallNotification(
old_superversion->write_stall_condition,
new_superversion->write_stall_condition, GetName(), ioptions());
new_superversion->write_stall_condition, GetName(), &ioptions());
}
if (old_superversion->Unref()) {
old_superversion->Cleanup();
@@ -1844,10 +1850,7 @@ const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family) {
const ColumnFamilyData* const cfd = handle->cfd();
assert(cfd);
const ImmutableOptions* ioptions = cfd->ioptions();
assert(ioptions);
return *ioptions;
return cfd->ioptions();
}
} // namespace ROCKSDB_NAMESPACE
+24 -11
View File
@@ -210,6 +210,7 @@ struct SuperVersion {
ReadOnlyMemTable* mem;
MemTableListVersion* imm;
Version* current;
// TODO: do we really need this in addition to what's in current Version?
MutableCFOptions mutable_cf_options;
// Version number of the current SuperVersion
uint64_t version_number;
@@ -221,7 +222,8 @@ struct SuperVersion {
// enable UDT feature, this is an empty string.
std::string full_history_ts_low;
// A shared copy of the DB's seqno to time mapping.
// An immutable snapshot of the DB's seqno to time mapping, usually shared
// between SuperVersions.
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping{nullptr};
// should be called outside the mutex
@@ -342,17 +344,17 @@ class ColumnFamilyData {
// thread-safe
const FileOptions* soptions() const;
const ImmutableOptions* ioptions() const { return &ioptions_; }
const ImmutableOptions& ioptions() const { return ioptions_; }
// REQUIRES: DB mutex held
// This returns the MutableCFOptions used by current SuperVersion
// You should use this API to reference MutableCFOptions most of the time.
const MutableCFOptions* GetCurrentMutableCFOptions() const {
return &(super_version_->mutable_cf_options);
const MutableCFOptions& GetCurrentMutableCFOptions() const {
return super_version_->mutable_cf_options;
}
// REQUIRES: DB mutex held
// This returns the latest MutableCFOptions, which may be not in effect yet.
const MutableCFOptions* GetLatestMutableCFOptions() const {
return &mutable_cf_options_;
const MutableCFOptions& GetLatestMutableCFOptions() const {
return mutable_cf_options_;
}
// REQUIRES: DB mutex held
@@ -379,27 +381,35 @@ class ColumnFamilyData {
return mem()->GetFirstSequenceNumber() == 0 && imm()->NumNotFlushed() == 0;
}
Version* current() { return current_; }
Version* dummy_versions() { return dummy_versions_; }
void SetCurrent(Version* _current);
Version* current() { return current_; } // REQUIRE: DB mutex held
void SetCurrent(Version* _current); // REQUIRE: DB mutex held
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
uint64_t GetTotalBlobFileSize() const; // REQUIRE: DB mutex held
// REQUIRE: DB mutex held
void SetMemtable(MemTable* new_mem) {
new_mem->SetID(++last_memtable_id_);
AssignMemtableID(new_mem);
mem_ = new_mem;
if (ioptions_.disallow_memtable_writes) {
mem_->MarkImmutable();
}
}
void AssignMemtableID(ReadOnlyMemTable* new_imm) {
new_imm->SetID(++last_memtable_id_);
}
// calculate the oldest log needed for the durability of this column family
uint64_t OldestLogToKeep();
// See Memtable constructor for explanation of earliest_seq param.
// `mutable_cf_options` might need to be a saved copy if calling this without
// holding the DB mutex.
MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options,
SequenceNumber earliest_seq);
void CreateNewMemtable(const MutableCFOptions& mutable_cf_options,
SequenceNumber earliest_seq);
void CreateNewMemtable(SequenceNumber earliest_seq);
TableCache* table_cache() const { return table_cache_.get(); }
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
@@ -477,6 +487,9 @@ class ColumnFamilyData {
uint64_t GetSuperVersionNumber() const {
return super_version_number_.load();
}
uint64_t GetSuperVersionNumberRelaxed() const {
return super_version_number_.load(std::memory_order_relaxed);
}
// will return a pointer to SuperVersion* if previous SuperVersion
// if its reference count is zero and needs deletion or nullptr if not
// As argument takes a pointer to allocated SuperVersion to enable
-1
View File
@@ -3,7 +3,6 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include <mutex>
#include <string>
#include <thread>
+53 -24
View File
@@ -332,11 +332,11 @@ Compaction::Compaction(
: (_blob_garbage_collection_policy ==
BlobGarbageCollectionPolicy::kDisable
? false
: mutable_cf_options()->enable_blob_garbage_collection)),
: mutable_cf_options().enable_blob_garbage_collection)),
blob_garbage_collection_age_cutoff_(
_blob_garbage_collection_age_cutoff < 0 ||
_blob_garbage_collection_age_cutoff > 1
? mutable_cf_options()->blob_garbage_collection_age_cutoff
? mutable_cf_options().blob_garbage_collection_age_cutoff
: _blob_garbage_collection_age_cutoff),
penultimate_level_(
// For simplicity, we don't support the concept of "penultimate level"
@@ -410,6 +410,7 @@ Compaction::Compaction(
void Compaction::PopulatePenultimateLevelOutputRange() {
if (!SupportsPerKeyPlacement()) {
assert(keep_in_last_level_through_seqno_ == kMaxSequenceNumber);
return;
}
@@ -452,6 +453,27 @@ void Compaction::PopulatePenultimateLevelOutputRange() {
GetBoundaryInternalKeys(input_vstorage_, inputs_,
&penultimate_level_smallest_,
&penultimate_level_largest_, exclude_level);
if (penultimate_output_range_type_ !=
PenultimateOutputRangeType::kFullRange) {
// If not full range in penultimate level, must keep everything already
// in the last level there, because moving it back up might cause
// overlap/placement issues that are difficult to resolve properly in the
// presence of range deletes
SequenceNumber max_last_level_seqno = 0;
for (const auto& input_lvl : inputs_) {
if (input_lvl.level == output_level_) {
for (const auto& file : input_lvl.files) {
max_last_level_seqno =
std::max(max_last_level_seqno, file->fd.largest_seqno);
}
}
}
keep_in_last_level_through_seqno_ = max_last_level_seqno;
} else {
keep_in_last_level_through_seqno_ = 0;
}
}
Compaction::~Compaction() {
@@ -494,22 +516,29 @@ bool Compaction::OverlapPenultimateLevelOutputRange(
}
// key includes timestamp if user-defined timestamp is enabled.
bool Compaction::WithinPenultimateLevelOutputRange(
const ParsedInternalKey& ikey) const {
if (!SupportsPerKeyPlacement()) {
return false;
}
void Compaction::TEST_AssertWithinPenultimateLevelOutputRange(
const Slice& user_key, bool expect_failure) const {
#ifdef NDEBUG
(void)user_key;
(void)expect_failure;
#else
assert(SupportsPerKeyPlacement());
if (penultimate_level_smallest_.size() == 0 ||
penultimate_level_largest_.size() == 0) {
return false;
}
assert(penultimate_level_smallest_.size() > 0);
assert(penultimate_level_largest_.size() > 0);
const InternalKeyComparator* icmp = input_vstorage_->InternalComparator();
auto* cmp = input_vstorage_->user_comparator();
// op_type of a key can change during compaction, e.g. Merge -> Put.
return icmp->CompareKeySeq(ikey, penultimate_level_smallest_.Encode()) >= 0 &&
icmp->CompareKeySeq(ikey, penultimate_level_largest_.Encode()) <= 0;
if (!(cmp->Compare(user_key, penultimate_level_smallest_.user_key()) >= 0)) {
assert(expect_failure);
} else if (!(cmp->Compare(user_key, penultimate_level_largest_.user_key()) <=
0)) {
assert(expect_failure);
} else {
assert(!expect_failure);
}
#endif
}
bool Compaction::InputCompressionMatchesOutput() const {
@@ -563,7 +592,7 @@ bool Compaction::IsTrivialMove() const {
// input files are non overlapping
if ((mutable_cf_options_.compaction_options_universal.allow_trivial_move) &&
(output_level_ != 0) &&
(cfd_->ioptions()->compaction_style == kCompactionStyleUniversal)) {
(cfd_->ioptions().compaction_style == kCompactionStyleUniversal)) {
return is_trivial_move_;
}
@@ -621,7 +650,7 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
if (bottommost_level_) {
return true;
} else if (output_level_ != 0 &&
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
// Maybe use binary search to find right entry instead of linear search?
const Comparator* user_cmp = cfd_->user_comparator();
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
@@ -662,7 +691,7 @@ bool Compaction::KeyRangeNotExistsBeyondOutputLevel(
if (bottommost_level_) {
return true /* does not overlap */;
} else if (output_level_ != 0 &&
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
const Comparator* user_cmp = cfd_->user_comparator();
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
const std::vector<FileMetaData*>& files =
@@ -838,12 +867,12 @@ uint64_t Compaction::OutputFilePreallocationSize() const {
}
std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
if (!cfd_->ioptions()->compaction_filter_factory) {
if (!cfd_->ioptions().compaction_filter_factory) {
return nullptr;
}
if (!cfd_->ioptions()
->compaction_filter_factory->ShouldFilterTableFileCreation(
.compaction_filter_factory->ShouldFilterTableFileCreation(
TableFileCreationReason::kCompaction)) {
return nullptr;
}
@@ -862,7 +891,7 @@ std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
"for compaction.");
}
return cfd_->ioptions()->compaction_filter_factory->CreateCompactionFilter(
return cfd_->ioptions().compaction_filter_factory->CreateCompactionFilter(
context);
}
@@ -896,8 +925,8 @@ bool Compaction::ShouldFormSubcompactions() const {
// Round-Robin pri under leveled compaction allows subcompactions by default
// and the number of subcompactions can be larger than max_subcompactions_
if (cfd_->ioptions()->compaction_pri == kRoundRobin &&
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
if (cfd_->ioptions().compaction_pri == kRoundRobin &&
cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
return output_level_ > 0;
}
@@ -905,9 +934,9 @@ bool Compaction::ShouldFormSubcompactions() const {
return false;
}
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
if (cfd_->ioptions().compaction_style == kCompactionStyleLevel) {
return (start_level_ == 0 || is_manual_compaction_) && output_level_ > 0;
} else if (cfd_->ioptions()->compaction_style == kCompactionStyleUniversal) {
} else if (cfd_->ioptions().compaction_style == kCompactionStyleUniversal) {
return number_levels_ > 1 && output_level_ > 0;
} else {
return false;
+19 -11
View File
@@ -288,14 +288,14 @@ class Compaction {
// Return the ImmutableOptions that should be used throughout the compaction
// procedure
const ImmutableOptions* immutable_options() const {
return &immutable_options_;
const ImmutableOptions& immutable_options() const {
return immutable_options_;
}
// Return the MutableCFOptions that should be used throughout the compaction
// procedure
const MutableCFOptions* mutable_cf_options() const {
return &mutable_cf_options_;
const MutableCFOptions& mutable_cf_options() const {
return mutable_cf_options_;
}
// Returns the size in bytes that the output file should be preallocated to.
@@ -388,12 +388,12 @@ class Compaction {
bool OverlapPenultimateLevelOutputRange(const Slice& smallest_key,
const Slice& largest_key) const;
// Return true if the key is within penultimate level output range for
// per_key_placement feature, which is safe to place the key to the
// penultimate level. different compaction strategy has different rules.
// If per_key_placement is not supported, always return false.
// key includes timestamp if user-defined timestamp is enabled.
bool WithinPenultimateLevelOutputRange(const ParsedInternalKey& ikey) const;
// For testing purposes, check that a key is within penultimate level
// output range for per_key_placement feature, which is safe to place the key
// to the penultimate level. Different compaction strategies have different
// rules. `user_key` includes timestamp if user-defined timestamp is enabled.
void TEST_AssertWithinPenultimateLevelOutputRange(
const Slice& user_key, bool expect_failure = false) const;
CompactionReason compaction_reason() const { return compaction_reason_; }
@@ -456,11 +456,17 @@ class Compaction {
const ImmutableOptions& immutable_options, const int start_level,
const int output_level);
// If some data cannot be safely migrated "up" the LSM tree due to a change
// in the preclude_last_level_data_seconds setting, this indicates a sequence
// number for the newest data that must be kept in the last level.
SequenceNumber GetKeepInLastLevelThroughSeqno() const {
return keep_in_last_level_through_seqno_;
}
// mark (or clear) all files that are being compacted
void MarkFilesBeingCompacted(bool being_compacted) const;
private:
Status InitInputTableProperties();
// get the smallest and largest key present in files to be compacted
@@ -606,6 +612,8 @@ class Compaction {
// Blob garbage collection age cutoff.
double blob_garbage_collection_age_cutoff_;
SequenceNumber keep_in_last_level_through_seqno_ = kMaxSequenceNumber;
// only set when per_key_placement feature is enabled, -1 (kInvalidLevel)
// means not supported.
const int penultimate_level_;
+9 -88
View File
@@ -38,19 +38,16 @@ CompactionIterator::CompactionIterator(
const std::atomic<bool>* shutting_down,
const std::shared_ptr<Logger> info_log,
const std::string* full_history_ts_low,
const SequenceNumber preserve_time_min_seqno,
const SequenceNumber preclude_last_level_min_seqno)
std::optional<SequenceNumber> preserve_seqno_min)
: CompactionIterator(
input, cmp, merge_helper, last_sequence, snapshots, earliest_snapshot,
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
report_detailed_time, expect_valid_internal_key, range_del_agg,
blob_file_builder, allow_data_in_errors, enforce_single_del_contracts,
manual_compaction_canceled,
std::unique_ptr<CompactionProxy>(
compaction ? new RealCompaction(compaction) : nullptr),
compaction ? std::make_unique<RealCompaction>(compaction) : nullptr,
must_count_input_entries, compaction_filter, shutting_down, info_log,
full_history_ts_low, preserve_time_min_seqno,
preclude_last_level_min_seqno) {}
full_history_ts_low, preserve_seqno_min) {}
CompactionIterator::CompactionIterator(
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
@@ -68,8 +65,7 @@ CompactionIterator::CompactionIterator(
const std::atomic<bool>* shutting_down,
const std::shared_ptr<Logger> info_log,
const std::string* full_history_ts_low,
const SequenceNumber preserve_time_min_seqno,
const SequenceNumber preclude_last_level_min_seqno)
std::optional<SequenceNumber> preserve_seqno_min)
: input_(input, cmp, must_count_input_entries),
cmp_(cmp),
merge_helper_(merge_helper),
@@ -110,10 +106,9 @@ CompactionIterator::CompactionIterator(
current_key_committed_(false),
cmp_with_history_ts_low_(0),
level_(compaction_ == nullptr ? 0 : compaction_->level()),
preserve_time_min_seqno_(preserve_time_min_seqno),
preclude_last_level_min_seqno_(preclude_last_level_min_seqno) {
preserve_seqno_after_(preserve_seqno_min.value_or(earliest_snapshot)) {
assert(snapshots_ != nullptr);
assert(preserve_time_min_seqno_ <= preclude_last_level_min_seqno_);
assert(preserve_seqno_after_ <= earliest_snapshot_);
if (compaction_ != nullptr) {
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
@@ -872,8 +867,8 @@ void CompactionIterator::NextFromInput() {
if (Valid()) {
at_next_ = true;
}
} else if (last_snapshot == current_user_key_snapshot_ ||
(last_snapshot > 0 &&
} else if (last_sequence != kMaxSequenceNumber &&
(last_snapshot == current_user_key_snapshot_ ||
last_snapshot < current_user_key_snapshot_)) {
// If the earliest snapshot is which this key is visible in
// is the same as the visibility of a previous instance of the
@@ -1018,7 +1013,6 @@ void CompactionIterator::NextFromInput() {
} else {
if (ikey_.sequence != 0) {
iter_stats_.num_timed_put_swap_preferred_seqno++;
saved_seq_for_penul_check_ = ikey_.sequence;
ikey_.sequence = preferred_seqno;
}
ikey_.type = kTypeValue;
@@ -1259,71 +1253,6 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
}
}
void CompactionIterator::DecideOutputLevel() {
assert(compaction_->SupportsPerKeyPlacement());
output_to_penultimate_level_ = false;
// if the key is newer than the cutoff sequence or within the earliest
// snapshot, it should output to the penultimate level.
if (ikey_.sequence > preclude_last_level_min_seqno_ ||
ikey_.sequence > earliest_snapshot_) {
output_to_penultimate_level_ = true;
}
#ifndef NDEBUG
// Could be overridden by unittest
PerKeyPlacementContext context(level_, ikey_.user_key, value_, ikey_.sequence,
output_to_penultimate_level_);
TEST_SYNC_POINT_CALLBACK("CompactionIterator::PrepareOutput.context",
&context);
if (ikey_.sequence > earliest_snapshot_) {
output_to_penultimate_level_ = true;
}
#endif // NDEBUG
// saved_seq_for_penul_check_ is populated in `NextFromInput` when the
// entry's sequence number is non zero and validity context for output this
// entry is kSwapPreferredSeqno for use in `DecideOutputLevel`. It should be
// cleared out here unconditionally. Otherwise, it may end up getting consumed
// incorrectly by a different entry.
SequenceNumber seq_for_range_check =
(saved_seq_for_penul_check_.has_value() &&
saved_seq_for_penul_check_.value() != kMaxSequenceNumber)
? saved_seq_for_penul_check_.value()
: ikey_.sequence;
saved_seq_for_penul_check_ = std::nullopt;
ParsedInternalKey ikey_for_range_check = ikey_;
if (seq_for_range_check != ikey_.sequence) {
ikey_for_range_check.sequence = seq_for_range_check;
}
if (output_to_penultimate_level_) {
// If it's decided to output to the penultimate level, but unsafe to do so,
// still output to the last level. For example, moving the data from a lower
// level to a higher level outside of the higher-level input key range is
// considered unsafe, because the key may conflict with higher-level SSTs
// not from this compaction.
// TODO: add statistic for declined output_to_penultimate_level
bool safe_to_penultimate_level =
compaction_->WithinPenultimateLevelOutputRange(ikey_for_range_check);
if (!safe_to_penultimate_level) {
output_to_penultimate_level_ = false;
// It could happen when disable/enable `last_level_temperature` while
// holding a snapshot. When `last_level_temperature` is not set
// (==kUnknown), the data newer than any snapshot is pushed to the last
// level, but when the per_key_placement feature is enabled on the fly,
// the data later than the snapshot has to be moved to the penultimate
// level, which may or may not be safe. So the user needs to make sure all
// snapshot is released before enabling `last_level_temperature` feature
// We will migrate the feature to `last_level_temperature` and maybe make
// it not dynamically changeable.
if (seq_for_range_check > earliest_snapshot_) {
status_ = Status::Corruption(
"Unsafe to store Seq later than snapshot in the last level if "
"per_key_placement is enabled");
}
}
}
}
void CompactionIterator::PrepareOutput() {
if (Valid()) {
if (LIKELY(!is_range_del_)) {
@@ -1332,13 +1261,6 @@ void CompactionIterator::PrepareOutput() {
} else if (ikey_.type == kTypeBlobIndex) {
GarbageCollectBlobIfNeeded();
}
// For range del sentinel, we don't use it to cut files for bottommost
// compaction. So it should not make a difference which output level we
// decide.
if (compaction_ != nullptr && compaction_->SupportsPerKeyPlacement()) {
DecideOutputLevel();
}
}
// Zeroing out the sequence number leads to better compression.
@@ -1356,8 +1278,7 @@ void CompactionIterator::PrepareOutput() {
!compaction_->allow_ingest_behind() && bottommost_level_ &&
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
ikey_.type != kTypeMerge && current_key_committed_ &&
!output_to_penultimate_level_ &&
ikey_.sequence < preserve_time_min_seqno_ && !is_range_del_) {
ikey_.sequence <= preserve_seqno_after_ && !is_range_del_) {
if (ikey_.type == kTypeDeletion ||
(ikey_.type == kTypeSingleDeletion && timestamp_size_ == 0)) {
ROCKS_LOG_FATAL(
+26 -68
View File
@@ -117,10 +117,6 @@ class CompactionIterator {
virtual const Compaction* real_compaction() const = 0;
virtual bool SupportsPerKeyPlacement() const = 0;
// `key` includes timestamp if user-defined timestamp is enabled.
virtual bool WithinPenultimateLevelOutputRange(
const ParsedInternalKey&) const = 0;
};
class RealCompaction : public CompactionProxy {
@@ -128,8 +124,6 @@ class CompactionIterator {
explicit RealCompaction(const Compaction* compaction)
: compaction_(compaction) {
assert(compaction_);
assert(compaction_->immutable_options());
assert(compaction_->mutable_cf_options());
}
int level() const override { return compaction_->level(); }
@@ -151,11 +145,11 @@ class CompactionIterator {
}
bool allow_ingest_behind() const override {
return compaction_->immutable_options()->allow_ingest_behind;
return compaction_->immutable_options().allow_ingest_behind;
}
bool allow_mmap_reads() const override {
return compaction_->immutable_options()->allow_mmap_reads;
return compaction_->immutable_options().allow_mmap_reads;
}
bool enable_blob_garbage_collection() const override {
@@ -167,7 +161,7 @@ class CompactionIterator {
}
uint64_t blob_compaction_readahead_size() const override {
return compaction_->mutable_cf_options()->blob_compaction_readahead_size;
return compaction_->mutable_cf_options().blob_compaction_readahead_size;
}
const Version* input_version() const override {
@@ -184,14 +178,6 @@ class CompactionIterator {
return compaction_->SupportsPerKeyPlacement();
}
// Check if key is within penultimate level output range, to see if it's
// safe to output to the penultimate level for per_key_placement feature.
// `key` includes timestamp if user-defined timestamp is enabled.
bool WithinPenultimateLevelOutputRange(
const ParsedInternalKey& ikey) const override {
return compaction_->WithinPenultimateLevelOutputRange(ikey);
}
private:
const Compaction* compaction_;
};
@@ -216,29 +202,29 @@ class CompactionIterator {
const std::atomic<bool>* shutting_down = nullptr,
const std::shared_ptr<Logger> info_log = nullptr,
const std::string* full_history_ts_low = nullptr,
const SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber,
const SequenceNumber preclude_last_level_min_seqno = kMaxSequenceNumber);
std::optional<SequenceNumber> preserve_seqno_min = {});
// Constructor with custom CompactionProxy, used for tests.
CompactionIterator(
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
SequenceNumber earliest_snapshot,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
CompactionRangeDelAggregator* range_del_agg,
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
bool enforce_single_del_contracts,
const std::atomic<bool>& manual_compaction_canceled,
std::unique_ptr<CompactionProxy> compaction,
bool must_count_input_entries,
const CompactionFilter* compaction_filter = nullptr,
const std::atomic<bool>* shutting_down = nullptr,
const std::shared_ptr<Logger> info_log = nullptr,
const std::string* full_history_ts_low = nullptr,
const SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber,
const SequenceNumber preclude_last_level_min_seqno = kMaxSequenceNumber);
CompactionIterator(InternalIterator* input, const Comparator* cmp,
MergeHelper* merge_helper, SequenceNumber last_sequence,
std::vector<SequenceNumber>* snapshots,
SequenceNumber earliest_snapshot,
SequenceNumber earliest_write_conflict_snapshot,
SequenceNumber job_snapshot,
const SnapshotChecker* snapshot_checker, Env* env,
bool report_detailed_time, bool expect_valid_internal_key,
CompactionRangeDelAggregator* range_del_agg,
BlobFileBuilder* blob_file_builder,
bool allow_data_in_errors,
bool enforce_single_del_contracts,
const std::atomic<bool>& manual_compaction_canceled,
std::unique_ptr<CompactionProxy> compaction,
bool must_count_input_entries,
const CompactionFilter* compaction_filter = nullptr,
const std::atomic<bool>* shutting_down = nullptr,
const std::shared_ptr<Logger> info_log = nullptr,
const std::string* full_history_ts_low = nullptr,
std::optional<SequenceNumber> preserve_seqno_min = {});
~CompactionIterator();
@@ -269,11 +255,6 @@ class CompactionIterator {
const CompactionIterationStats& iter_stats() const { return iter_stats_; }
bool HasNumInputEntryScanned() const { return input_.HasNumItered(); }
uint64_t NumInputEntryScanned() const { return input_.NumItered(); }
// If the current key should be placed on penultimate level, only valid if
// per_key_placement is supported
bool output_to_penultimate_level() const {
return output_to_penultimate_level_;
}
Status InputStatus() const { return input_.status(); }
bool IsDeleteRangeSentinelKey() const { return is_range_del_; }
@@ -285,10 +266,6 @@ class CompactionIterator {
// Do final preparations before presenting the output to the callee.
void PrepareOutput();
// Decide the current key should be output to the last level or penultimate
// level, only call for compaction supports per key placement
void DecideOutputLevel();
// Passes the output value to the blob file builder (if any), and replaces it
// with the corresponding blob reference if it has been actually written to a
// blob file (i.e. if it passed the value size check). Returns true if the
@@ -440,16 +417,6 @@ class CompactionIterator {
// NextFromInput()).
ParsedInternalKey ikey_;
// When a kTypeValuePreferredSeqno entry's preferred seqno is safely swapped
// in in this compaction, this field saves its original sequence number for
// range checking whether it's safe to be placed on the penultimate level.
// This is to ensure when such an entry happens to be the right boundary of
// penultimate safe range, it won't get excluded because with the preferred
// seqno swapped in, it's now larger than the right boundary (itself before
// the swap). This is safe to do, because preferred seqno is swapped in only
// when no entries with the same user key exist on lower levels and this entry
// is already visible in the earliest snapshot.
std::optional<SequenceNumber> saved_seq_for_penul_check_ = kMaxSequenceNumber;
// Stores whether ikey_.user_key is valid. If set to false, the user key is
// not compared against the current key in the underlying iterator.
bool has_current_user_key_ = false;
@@ -507,17 +474,8 @@ class CompactionIterator {
// just been zeroed out during bottommost compaction.
bool last_key_seq_zeroed_{false};
// True if the current key should be output to the penultimate level if
// possible, compaction logic makes the final decision on which level to
// output to.
bool output_to_penultimate_level_{false};
// min seqno for preserving the time information.
const SequenceNumber preserve_time_min_seqno_ = kMaxSequenceNumber;
// min seqno to preclude the data from the last level, if the key seqno larger
// than this, it will be output to penultimate level
const SequenceNumber preclude_last_level_min_seqno_ = kMaxSequenceNumber;
// Max seqno that can be zeroed out at last level (various reasons)
const SequenceNumber preserve_seqno_after_ = kMaxSequenceNumber;
void AdvanceInputIter() { input_.Next(); }
+24 -122
View File
@@ -192,11 +192,6 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
return supports_per_key_placement;
}
bool WithinPenultimateLevelOutputRange(
const ParsedInternalKey& key) const override {
return (!key.user_key.starts_with("unsafe_pb"));
}
bool key_not_exists_beyond_output_level = false;
bool is_bottommost_level = false;
@@ -833,126 +828,17 @@ TEST_P(CompactionIteratorTest, ConvertToPutAtBottom) {
true /*bottomost_level*/);
}
TEST_P(CompactionIteratorTest, ZeroSeqOfKeyAndSnapshot) {
AddSnapshot(0);
const std::vector<std::string> input_keys = {
test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 0, kTypeValue)};
const std::vector<std::string> input_values = {"a1", "b1"};
RunTest(input_keys, input_values, input_keys, input_values);
}
INSTANTIATE_TEST_CASE_P(CompactionIteratorTestInstance, CompactionIteratorTest,
testing::Values(true, false));
class PerKeyPlacementCompIteratorTest : public CompactionIteratorTest {
public:
bool SupportsPerKeyPlacement() const override { return true; }
};
TEST_P(PerKeyPlacementCompIteratorTest, SplitLastLevelData) {
std::atomic_uint64_t latest_cold_seq = 0;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
});
SyncPoint::GetInstance()->EnableProcessing();
latest_cold_seq = 5;
InitIterators(
{test::KeyStr("a", 7, kTypeValue), test::KeyStr("b", 6, kTypeValue),
test::KeyStr("c", 5, kTypeValue)},
{"vala", "valb", "valc"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
nullptr, nullptr, true);
c_iter_->SeekToFirst();
ASSERT_TRUE(c_iter_->Valid());
// the first 2 keys are hot, which should has
// `output_to_penultimate_level()==true` and seq num not zeroed out
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
ASSERT_EQ(test::KeyStr("b", 6, kTypeValue), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
// `a` is cold data, which should be output to bottommost
ASSERT_EQ(test::KeyStr("c", 0, kTypeValue), c_iter_->key().ToString());
ASSERT_FALSE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_OK(c_iter_->status());
ASSERT_FALSE(c_iter_->Valid());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(PerKeyPlacementCompIteratorTest, SnapshotData) {
AddSnapshot(5);
InitIterators(
{test::KeyStr("a", 7, kTypeValue), test::KeyStr("b", 6, kTypeDeletion),
test::KeyStr("b", 5, kTypeValue)},
{"vala", "", "valb"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
nullptr, nullptr, true);
c_iter_->SeekToFirst();
ASSERT_TRUE(c_iter_->Valid());
// The first key and the tombstone are within snapshot, which should output
// to the penultimate level (and seq num cannot be zeroed out).
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
ASSERT_EQ(test::KeyStr("b", 6, kTypeDeletion), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_TRUE(c_iter_->Valid());
// `a` is not protected by the snapshot, the sequence number is zero out and
// should output bottommost
ASSERT_EQ(test::KeyStr("b", 0, kTypeValue), c_iter_->key().ToString());
ASSERT_FALSE(c_iter_->output_to_penultimate_level());
c_iter_->Next();
ASSERT_OK(c_iter_->status());
ASSERT_FALSE(c_iter_->Valid());
}
TEST_P(PerKeyPlacementCompIteratorTest, ConflictWithSnapshot) {
std::atomic_uint64_t latest_cold_seq = 0;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
});
SyncPoint::GetInstance()->EnableProcessing();
latest_cold_seq = 6;
AddSnapshot(5);
InitIterators({test::KeyStr("a", 7, kTypeValue),
test::KeyStr("unsafe_pb", 6, kTypeValue),
test::KeyStr("c", 5, kTypeValue)},
{"vala", "valb", "valc"}, {}, {}, kMaxSequenceNumber,
kMaxSequenceNumber, nullptr, nullptr, true);
c_iter_->SeekToFirst();
ASSERT_TRUE(c_iter_->Valid());
ASSERT_EQ(test::KeyStr("a", 7, kTypeValue), c_iter_->key().ToString());
ASSERT_TRUE(c_iter_->output_to_penultimate_level());
// the 2nd key is unsafe to output_to_penultimate_level, but it's within
// snapshot so for per_key_placement feature it has to be outputted to the
// penultimate level. which is a corruption. We should never see
// such case as the data with seq num (within snapshot) should always come
// from higher compaction input level, which makes it safe to
// output_to_penultimate_level.
c_iter_->Next();
ASSERT_TRUE(c_iter_->status().IsCorruption());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
INSTANTIATE_TEST_CASE_P(PerKeyPlacementCompIteratorTest,
PerKeyPlacementCompIteratorTest,
testing::Values(true, false));
// Tests how CompactionIterator work together with SnapshotChecker.
class CompactionIteratorWithSnapshotCheckerTest
: public CompactionIteratorTest {
@@ -1846,6 +1732,22 @@ TEST_P(CompactionIteratorTsGcTest, SingleDeleteAllKeysOlderThanThreshold) {
}
}
TEST_P(CompactionIteratorTsGcTest, ZeroSeqOfKeyAndSnapshot) {
AddSnapshot(0);
std::string full_history_ts_low;
PutFixed64(&full_history_ts_low, std::numeric_limits<uint64_t>::max());
const std::vector<std::string> input_keys = {
test::KeyStr(101, "a", 0, kTypeValue),
test::KeyStr(102, "b", 0, kTypeValue)};
const std::vector<std::string> input_values = {"a1", "b1"};
RunTest(input_keys, input_values, input_keys, input_values,
/*last_committed_seq=*/kMaxSequenceNumber,
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
/*bottommost_level=*/false,
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
/*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
}
INSTANTIATE_TEST_CASE_P(CompactionIteratorTsGcTestInstance,
CompactionIteratorTsGcTest,
testing::Values(true, false));
+172 -90
View File
@@ -245,7 +245,10 @@ void CompactionJob::ReportStartedCompaction(Compaction* compaction) {
compaction_job_stats_->is_full_compaction = compaction->is_full_compaction();
}
void CompactionJob::Prepare() {
void CompactionJob::Prepare(
std::optional<std::pair<std::optional<Slice>, std::optional<Slice>>>
known_single_subcompact) {
db_mutex_->AssertHeld();
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_PREPARE);
@@ -260,11 +263,12 @@ void CompactionJob::Prepare() {
write_hint_ = storage_info->CalculateSSTWriteHint(c->output_level());
bottommost_level_ = c->bottommost_level();
if (c->ShouldFormSubcompactions()) {
if (!known_single_subcompact.has_value() && c->ShouldFormSubcompactions()) {
StopWatch sw(db_options_.clock, stats_, SUBCOMPACTION_SETUP_TIME);
GenSubcompactionBoundaries();
}
if (boundaries_.size() >= 1) {
assert(!known_single_subcompact.has_value());
for (size_t i = 0; i <= boundaries_.size(); i++) {
compact_->sub_compact_states.emplace_back(
c, (i != 0) ? std::optional<Slice>(boundaries_[i - 1]) : std::nullopt,
@@ -280,16 +284,25 @@ void CompactionJob::Prepare() {
RecordInHistogram(stats_, NUM_SUBCOMPACTIONS_SCHEDULED,
compact_->sub_compact_states.size());
} else {
compact_->sub_compact_states.emplace_back(c, std::nullopt, std::nullopt,
std::optional<Slice> start_key;
std::optional<Slice> end_key;
if (known_single_subcompact.has_value()) {
start_key = known_single_subcompact.value().first;
end_key = known_single_subcompact.value().second;
} else {
assert(!start_key.has_value() && !end_key.has_value());
}
compact_->sub_compact_states.emplace_back(c, start_key, end_key,
/*sub_job_id*/ 0);
}
// collect all seqno->time information from the input files which will be used
// to encode seqno->time to the output files.
SequenceNumber preserve_time_min_seqno = kMaxSequenceNumber;
SequenceNumber preclude_last_level_min_seqno = kMaxSequenceNumber;
uint64_t preserve_time_duration =
std::max(c->mutable_cf_options()->preserve_internal_time_seconds,
c->mutable_cf_options()->preclude_last_level_data_seconds);
std::max(c->mutable_cf_options().preserve_internal_time_seconds,
c->mutable_cf_options().preclude_last_level_data_seconds);
if (preserve_time_duration > 0) {
const ReadOptions read_options(Env::IOActivity::kCompaction);
@@ -319,16 +332,16 @@ void CompactionJob::Prepare() {
"Failed to get current time in compaction: Status: %s",
s.ToString().c_str());
// preserve all time information
preserve_time_min_seqno_ = 0;
preclude_last_level_min_seqno_ = 0;
preserve_time_min_seqno = 0;
preclude_last_level_min_seqno = 0;
seqno_to_time_mapping_.Enforce();
} else {
seqno_to_time_mapping_.Enforce(_current_time);
seqno_to_time_mapping_.GetCurrentTieringCutoffSeqnos(
static_cast<uint64_t>(_current_time),
c->mutable_cf_options()->preserve_internal_time_seconds,
c->mutable_cf_options()->preclude_last_level_data_seconds,
&preserve_time_min_seqno_, &preclude_last_level_min_seqno_);
c->mutable_cf_options().preserve_internal_time_seconds,
c->mutable_cf_options().preclude_last_level_data_seconds,
&preserve_time_min_seqno, &preclude_last_level_min_seqno);
}
// For accuracy of the GetProximalSeqnoBeforeTime queries above, we only
// limit the capacity after them.
@@ -341,6 +354,45 @@ void CompactionJob::Prepare() {
// larger than kMaxSeqnoTimePairsPerSST.
seqno_to_time_mapping_.SetCapacity(kMaxSeqnoToTimeEntries);
}
#ifndef NDEBUG
assert(preserve_time_min_seqno <= preclude_last_level_min_seqno);
TEST_SYNC_POINT_CALLBACK(
"CompactionJob::PrepareTimes():preclude_last_level_min_seqno",
static_cast<void*>(&preclude_last_level_min_seqno));
// Restore the invariant asserted above, in case it was broken under the
// callback
preserve_time_min_seqno =
std::min(preclude_last_level_min_seqno, preserve_time_min_seqno);
#endif
// Preserve sequence numbers for preserved write times and snapshots, though
// the specific sequence number of the earliest snapshot can be zeroed.
preserve_seqno_after_ =
std::max(preserve_time_min_seqno, SequenceNumber{1}) - 1;
preserve_seqno_after_ = std::min(preserve_seqno_after_, earliest_snapshot_);
// If using preclude feature, also preclude snapshots from last level, just
// because they are heuristically more likely to be accessed than non-snapshot
// data.
if (preclude_last_level_min_seqno < kMaxSequenceNumber &&
earliest_snapshot_ < preclude_last_level_min_seqno) {
preclude_last_level_min_seqno = earliest_snapshot_;
}
// Now combine what we would like to preclude from last level with what we
// can safely support without dangerously moving data back up the LSM tree,
// to get the final seqno threshold for penultimate vs. last. In particular,
// when the reserved output key range for the penultimate level does not
// include the entire last level input key range, we need to keep entries
// already in the last level there. (Even allowing within-range entries to
// move back up could cause problems with range tombstones. Perhaps it
// would be better in some rare cases to keep entries in the last level
// one-by-one rather than based on sequence number, but that would add extra
// tracking and complexity to CompactionIterator that is probably not
// worthwhile overall. Correctness is also more clear when splitting by
// seqno threshold.)
penultimate_after_seqno_ = std::max(preclude_last_level_min_seqno,
c->GetKeepInLastLevelThroughSeqno());
options_file_number_ = versions_->options_file_number();
}
uint64_t CompactionJob::GetSubcompactionsLimit() {
@@ -469,14 +521,14 @@ void CompactionJob::GenSubcompactionBoundaries() {
ReadOptions read_options(Env::IOActivity::kCompaction);
read_options.rate_limiter_priority = GetRateLimiterPriority();
auto* c = compact_->compaction;
if (c->mutable_cf_options()->table_factory->Name() ==
if (c->mutable_cf_options().table_factory->Name() ==
TableFactory::kPlainTableName()) {
return;
}
if (c->max_subcompactions() <= 1 &&
!(c->immutable_options()->compaction_pri == kRoundRobin &&
c->immutable_options()->compaction_style == kCompactionStyleLevel)) {
!(c->immutable_options().compaction_pri == kRoundRobin &&
c->immutable_options().compaction_style == kCompactionStyleLevel)) {
return;
}
auto* cfd = c->column_family_data();
@@ -506,7 +558,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
FileMetaData* f = flevel->files[i].file_metadata;
std::vector<TableReader::Anchor> my_anchors;
Status s = cfd->table_cache()->ApproximateKeyAnchors(
read_options, icomp, *f, *c->mutable_cf_options(), my_anchors);
read_options, icomp, *f, c->mutable_cf_options(), my_anchors);
if (!s.ok() || my_anchors.empty()) {
my_anchors.emplace_back(f->largest.user_key(), f->fd.GetFileSize());
}
@@ -545,8 +597,8 @@ void CompactionJob::GenSubcompactionBoundaries() {
// Get the number of planned subcompactions, may update reserve threads
// and update extra_num_subcompaction_threads_reserved_ for round-robin
uint64_t num_planned_subcompactions;
if (c->immutable_options()->compaction_pri == kRoundRobin &&
c->immutable_options()->compaction_style == kCompactionStyleLevel) {
if (c->immutable_options().compaction_pri == kRoundRobin &&
c->immutable_options().compaction_style == kCompactionStyleLevel) {
// For round-robin compaction prioity, we need to employ more
// subcompactions (may exceed the max_subcompaction limit). The extra
// subcompactions will be executed using reserved threads and taken into
@@ -585,9 +637,9 @@ void CompactionJob::GenSubcompactionBoundaries() {
uint64_t target_range_size = std::max(
total_size / num_planned_subcompactions,
MaxFileSizeForLevel(
*(c->mutable_cf_options()), out_lvl,
c->immutable_options()->compaction_style, base_level,
c->immutable_options()->level_compaction_dynamic_level_bytes));
c->mutable_cf_options(), out_lvl,
c->immutable_options().compaction_style, base_level,
c->immutable_options().level_compaction_dynamic_level_bytes));
if (target_range_size >= total_size) {
return;
@@ -730,14 +782,13 @@ Status CompactionJob::Run() {
verify_table_read_options, file_options_,
cfd->internal_comparator(), files_output[file_idx]->meta,
/*range_del_agg=*/nullptr,
*compact_->compaction->mutable_cf_options(),
compact_->compaction->mutable_cf_options(),
/*table_reader_ptr=*/nullptr,
cfd->internal_stats()->GetFileReadHist(
compact_->compaction->output_level()),
TableReaderCaller::kCompactionRefill, /*arena=*/nullptr,
/*skip_filters=*/false, compact_->compaction->output_level(),
MaxFileSizeForL0MetaPin(
*compact_->compaction->mutable_cf_options()),
MaxFileSizeForL0MetaPin(compact_->compaction->mutable_cf_options()),
/*smallest_compaction_key=*/nullptr,
/*largest_compaction_key=*/nullptr,
/*allow_unprepared_value=*/false);
@@ -794,7 +845,7 @@ Status CompactionJob::Run() {
for (const auto& state : compact_->sub_compact_states) {
for (const auto& output : state.GetOutputs()) {
auto fn =
TableFileName(state.compaction->immutable_options()->cf_paths,
TableFileName(state.compaction->immutable_options().cf_paths,
output.meta.fd.GetNumber(), output.meta.fd.GetPathId());
compact_->compaction->SetOutputTableProperties(fn,
output.table_properties);
@@ -853,8 +904,7 @@ Status CompactionJob::Run() {
return status;
}
Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options,
bool* compaction_released) {
Status CompactionJob::Install(bool* compaction_released) {
assert(compact_);
AutoThreadOperationStageUpdater stage_updater(
@@ -870,7 +920,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options,
compaction_stats_);
if (status.ok()) {
status = InstallCompactionResults(mutable_cf_options, compaction_released);
status = InstallCompactionResults(compaction_released);
}
if (!versions_->io_status().ok()) {
io_status_ = versions_->io_status();
@@ -959,8 +1009,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options,
UpdateCompactionJobStats(stats);
auto stream = event_logger_->LogToBuffer(log_buffer_, 8192);
stream << "job" << job_id_ << "event"
<< "compaction_finished"
stream << "job" << job_id_ << "event" << "compaction_finished"
<< "compaction_time_micros" << stats.micros
<< "compaction_time_cpu_micros" << stats.cpu_micros << "output_level"
<< compact_->compaction->output_level() << "num_output_files"
@@ -1050,12 +1099,10 @@ void CompactionJob::NotifyOnSubcompactionBegin(
listener->OnSubcompactionBegin(info);
}
info.status.PermitUncheckedError();
}
void CompactionJob::NotifyOnSubcompactionCompleted(
SubcompactionState* sub_compact) {
if (db_options_.listeners.empty()) {
return;
}
@@ -1083,8 +1130,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
if (db_options_.compaction_service) {
CompactionServiceJobStatus comp_status =
ProcessKeyValueCompactionWithCompactionService(sub_compact);
if (comp_status == CompactionServiceJobStatus::kSuccess ||
comp_status == CompactionServiceJobStatus::kFailure) {
if (comp_status != CompactionServiceJobStatus::kUseLocal) {
return;
}
// fallback to local compaction
@@ -1098,8 +1144,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
// Create compaction filter and fail the compaction if
// IgnoreSnapshots() = false because it is not supported anymore
const CompactionFilter* compaction_filter =
cfd->ioptions()->compaction_filter;
const CompactionFilter* compaction_filter = cfd->ioptions().compaction_filter;
std::unique_ptr<CompactionFilter> compaction_filter_from_factory = nullptr;
if (compaction_filter == nullptr) {
compaction_filter_from_factory =
@@ -1115,9 +1160,12 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
NotifyOnSubcompactionBegin(sub_compact);
auto range_del_agg = std::make_unique<CompactionRangeDelAggregator>(
&cfd->internal_comparator(), existing_snapshots_, &full_history_ts_low_,
&trim_ts_);
// This is assigned after creation of SubcompactionState to simplify that
// creation across both CompactionJob and CompactionServiceCompactionJob
sub_compact->AssignRangeDelAggregator(
std::make_unique<CompactionRangeDelAggregator>(
&cfd->internal_comparator(), existing_snapshots_,
&full_history_ts_low_, &trim_ts_));
// TODO: since we already use C++17, should use
// std::optional<const Slice> instead.
@@ -1162,7 +1210,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
// Although the v2 aggregator is what the level iterator(s) know about,
// the AddTombstones calls will be propagated down to the v1 aggregator.
std::unique_ptr<InternalIterator> raw_input(versions_->MakeInputIterator(
read_options, sub_compact->compaction, range_del_agg.get(),
read_options, sub_compact->compaction, sub_compact->RangeDelAgg(),
file_options_for_read_, start, end));
InternalIterator* input = raw_input.get();
@@ -1254,28 +1302,27 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
}
MergeHelper merge(
env_, cfd->user_comparator(), cfd->ioptions()->merge_operator.get(),
env_, cfd->user_comparator(), cfd->ioptions().merge_operator.get(),
compaction_filter, db_options_.info_log.get(),
false /* internal key corruption is expected */,
existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
snapshot_checker_, compact_->compaction->level(), db_options_.stats);
const MutableCFOptions* mutable_cf_options =
const auto& mutable_cf_options =
sub_compact->compaction->mutable_cf_options();
assert(mutable_cf_options);
std::vector<std::string> blob_file_paths;
// TODO: BlobDB to support output_to_penultimate_level compaction, which needs
// 2 builders, so may need to move to `CompactionOutputs`
std::unique_ptr<BlobFileBuilder> blob_file_builder(
(mutable_cf_options->enable_blob_files &&
(mutable_cf_options.enable_blob_files &&
sub_compact->compaction->output_level() >=
mutable_cf_options->blob_file_starting_level)
mutable_cf_options.blob_file_starting_level)
? new BlobFileBuilder(
versions_, fs_.get(),
sub_compact->compaction->immutable_options(),
mutable_cf_options, &file_options_, &write_options, db_id_,
&sub_compact->compaction->immutable_options(),
&mutable_cf_options, &file_options_, &write_options, db_id_,
db_session_id_, job_id_, cfd->GetID(), cfd->GetName(),
write_hint_, io_tracer_, blob_callback_,
BlobFileCreationReason::kCompaction, &blob_file_paths,
@@ -1298,20 +1345,15 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
&existing_snapshots_, earliest_snapshot_,
earliest_write_conflict_snapshot_, job_snapshot_seq, snapshot_checker_,
env_, ShouldReportDetailedTime(env_, stats_),
/*expect_valid_internal_key=*/true, range_del_agg.get(),
/*expect_valid_internal_key=*/true, sub_compact->RangeDelAgg(),
blob_file_builder.get(), db_options_.allow_data_in_errors,
db_options_.enforce_single_del_contracts, manual_compaction_canceled_,
sub_compact->compaction
->DoesInputReferenceBlobFiles() /* must_count_input_entries */,
sub_compact->compaction, compaction_filter, shutting_down_,
db_options_.info_log, full_history_ts_low, preserve_time_min_seqno_,
preclude_last_level_min_seqno_);
db_options_.info_log, full_history_ts_low, preserve_seqno_after_);
c_iter->SeekToFirst();
// Assign range delete aggregator to the target output level, which makes sure
// it only output to single level
sub_compact->AssignRangeDelAggregator(std::move(range_del_agg));
const auto& c_iter_stats = c_iter->iter_stats();
// define the open and close functions for the compaction files, which will be
@@ -1355,12 +1397,41 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
last_cpu_micros = cur_cpu_micros;
}
const auto& ikey = c_iter->ikey();
bool use_penultimate_output = ikey.sequence > penultimate_after_seqno_;
#ifndef NDEBUG
if (sub_compact->compaction->SupportsPerKeyPlacement()) {
// Could be overridden by unittest
PerKeyPlacementContext context(sub_compact->compaction->output_level(),
ikey.user_key, c_iter->value(),
ikey.sequence, use_penultimate_output);
TEST_SYNC_POINT_CALLBACK("CompactionIterator::PrepareOutput.context",
&context);
if (use_penultimate_output) {
// Verify that entries sent to the penultimate level are within the
// allowed range (because the input key range of the last level could
// be larger than the allowed output key range of the penultimate
// level). This check uses user keys (ignores sequence numbers) because
// compaction boundaries are a "clean cut" between user keys (see
// CompactionPicker::ExpandInputsToCleanCut()), which is especially
// important when preferred sequence numbers has been swapped in for
// kTypeValuePreferredSeqno / TimedPut.
sub_compact->compaction->TEST_AssertWithinPenultimateLevelOutputRange(
c_iter->user_key());
}
} else {
assert(penultimate_after_seqno_ == kMaxSequenceNumber);
assert(!use_penultimate_output);
}
#endif // NDEBUG
// Add current compaction_iterator key to target compaction output, if the
// output file needs to be close or open, it will call the `open_file_func`
// and `close_file_func`.
// TODO: it would be better to have the compaction file open/close moved
// into `CompactionOutputs` which has the output file information.
status = sub_compact->AddToOutput(*c_iter, open_file_func, close_file_func);
status = sub_compact->AddToOutput(*c_iter, use_penultimate_output,
open_file_func, close_file_func);
if (!status.ok()) {
break;
}
@@ -1567,16 +1638,31 @@ Status CompactionJob::FinishCompactionOutputFile(
earliest_snapshot = existing_snapshots_[0];
}
if (s.ok()) {
// Inclusive lower bound, exclusive upper bound
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range{
0, kMaxSequenceNumber};
if (sub_compact->compaction->SupportsPerKeyPlacement()) {
if (outputs.IsPenultimateLevel()) {
keep_seqno_range.first = penultimate_after_seqno_;
} else {
keep_seqno_range.second = penultimate_after_seqno_;
}
}
CompactionIterationStats range_del_out_stats;
// if the compaction supports per_key_placement, only output range dels to
// the penultimate level.
// Note: Use `bottommost_level_ = true` for both bottommost and
// NOTE1: Use `bottommost_level_ = true` for both bottommost and
// output_to_penultimate_level compaction here, as it's only used to decide
// if range dels could be dropped.
if (outputs.HasRangeDel()) {
s = outputs.AddRangeDels(comp_start_user_key, comp_end_user_key,
range_del_out_stats, bottommost_level_,
cfd->internal_comparator(), earliest_snapshot,
// if range dels could be dropped. (Logically, we are taking a single sorted
// run returned from CompactionIterator and physically splitting it between
// two output levels.)
// NOTE2: with per-key placement, range tombstones will be filtered on
// each output level based on sequence number (traversed twice). This is
// CPU-inefficient for a large number of range tombstones, but that would
// be an unusual work load.
if (sub_compact->HasRangeDel()) {
s = outputs.AddRangeDels(*sub_compact->RangeDelAgg(), comp_start_user_key,
comp_end_user_key, range_del_out_stats,
bottommost_level_, cfd->internal_comparator(),
earliest_snapshot, keep_seqno_range,
next_table_min_key, full_history_ts_low_);
}
RecordDroppedKeys(range_del_out_stats, &sub_compact->compaction_job_stats);
@@ -1586,6 +1672,8 @@ Status CompactionJob::FinishCompactionOutputFile(
const uint64_t current_entries = outputs.NumEntries();
s = outputs.Finish(s, seqno_to_time_mapping_);
TEST_SYNC_POINT_CALLBACK(
"CompactionJob::FinishCompactionOutputFile()::AfterFinish", &s);
if (s.ok()) {
// With accurate smallest and largest key, we can get a slightly more
@@ -1637,7 +1725,7 @@ Status CompactionJob::FinishCompactionOutputFile(
// This happens when the output level is bottom level, at the same time
// the sub_compact output nothing.
std::string fname =
TableFileName(sub_compact->compaction->immutable_options()->cf_paths,
TableFileName(sub_compact->compaction->immutable_options().cf_paths,
meta->fd.GetNumber(), meta->fd.GetPathId());
// TODO(AR) it is not clear if there are any larger implications if
@@ -1684,7 +1772,7 @@ Status CompactionJob::FinishCompactionOutputFile(
}
}
EventHelpers::LogAndNotifyTableFileCreationFinished(
event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(), fname,
event_logger_, cfd->ioptions().listeners, dbname_, cfd->GetName(), fname,
job_id_, output_fd, oldest_blob_file_number, tp,
TableFileCreationReason::kCompaction, status_for_listener, file_checksum,
file_checksum_func_name);
@@ -1712,8 +1800,7 @@ Status CompactionJob::FinishCompactionOutputFile(
return s;
}
Status CompactionJob::InstallCompactionResults(
const MutableCFOptions& mutable_cf_options, bool* compaction_released) {
Status CompactionJob::InstallCompactionResults(bool* compaction_released) {
assert(compact_);
db_mutex_->AssertHeld();
@@ -1787,7 +1874,7 @@ Status CompactionJob::InstallCompactionResults(
if ((compaction->compaction_reason() ==
CompactionReason::kLevelMaxLevelSize ||
compaction->compaction_reason() == CompactionReason::kRoundRobinTtl) &&
compaction->immutable_options()->compaction_pri == kRoundRobin) {
compaction->immutable_options().compaction_pri == kRoundRobin) {
int start_level = compaction->start_level();
if (start_level > 0) {
auto vstorage = compaction->input_version()->storage_info();
@@ -1802,11 +1889,11 @@ Status CompactionJob::InstallCompactionResults(
*compaction_released = true;
};
return versions_->LogAndApply(
compaction->column_family_data(), mutable_cf_options, read_options,
write_options, edit, db_mutex_, db_directory_,
/*new_descriptor_log=*/false,
/*column_family_options=*/nullptr, manifest_wcb);
return versions_->LogAndApply(compaction->column_family_data(), read_options,
write_options, edit, db_mutex_, db_directory_,
/*new_descriptor_log=*/false,
/*column_family_options=*/nullptr,
manifest_wcb);
}
void CompactionJob::RecordCompactionIOStats() {
@@ -1842,7 +1929,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
// Fire events.
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
EventHelpers::NotifyTableFileCreationStarted(
cfd->ioptions()->listeners, dbname_, cfd->GetName(), fname, job_id_,
cfd->ioptions().listeners, dbname_, cfd->GetName(), fname, job_id_,
TableFileCreationReason::kCompaction);
// Make the output file
std::unique_ptr<FSWritableFile> writable_file;
@@ -1856,12 +1943,12 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
FileOptions fo_copy = file_options_;
Temperature temperature = sub_compact->compaction->output_temperature();
Temperature last_level_temp =
sub_compact->compaction->mutable_cf_options()->last_level_temperature;
sub_compact->compaction->mutable_cf_options().last_level_temperature;
// Here last_level_temperature supersedes default_write_temperature, when
// enabled and applicable
if (last_level_temp != Temperature::kUnknown &&
sub_compact->compaction->is_last_level() &&
!sub_compact->IsCurrentPenultimateLevel()) {
!outputs.IsPenultimateLevel()) {
temperature = last_level_temp;
}
fo_copy.temperature = temperature;
@@ -1884,7 +1971,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
job_id_, file_number, s.ToString().c_str());
LogFlush(db_options_.info_log);
EventHelpers::LogAndNotifyTableFileCreationFinished(
event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(),
event_logger_, cfd->ioptions().listeners, dbname_, cfd->GetName(),
fname, job_id_, FileDescriptor(), kInvalidBlobFileNumber,
TableProperties(), TableFileCreationReason::kCompaction, s,
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
@@ -1955,7 +2042,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
writable_file->SetPreallocationBlockSize(static_cast<size_t>(
sub_compact->compaction->OutputFilePreallocationSize()));
const auto& listeners =
sub_compact->compaction->immutable_options()->listeners;
sub_compact->compaction->immutable_options().listeners;
outputs.AssignFileWriter(new WritableFileWriter(
std::move(writable_file), fname, fo_copy, db_options_.clock, io_tracer_,
db_options_.stats, Histograms::SST_WRITE_MICROS, listeners,
@@ -1966,7 +2053,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
const ReadOptions read_options(Env::IOActivity::kCompaction);
const WriteOptions write_options(Env::IOActivity::kCompaction);
TableBuilderOptions tboptions(
*cfd->ioptions(), *(sub_compact->compaction->mutable_cf_options()),
cfd->ioptions(), sub_compact->compaction->mutable_cf_options(),
read_options, write_options, cfd->internal_comparator(),
cfd->internal_tbl_prop_coll_factories(),
sub_compact->compaction->output_compression(),
@@ -1975,9 +2062,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
bottommost_level_, TableFileCreationReason::kCompaction,
0 /* oldest_key_time */, current_time, db_id_, db_session_id_,
sub_compact->compaction->max_output_file_size(), file_number,
preclude_last_level_min_seqno_ == kMaxSequenceNumber
? preclude_last_level_min_seqno_
: std::min(earliest_snapshot_, preclude_last_level_min_seqno_));
penultimate_after_seqno_ /*last_level_inclusive_max_seqno_threshold*/);
outputs.NewBuilder(tboptions);
@@ -2034,9 +2119,8 @@ bool CompactionJob::UpdateCompactionStats(uint64_t* num_input_range_del) {
if (file_input_entries == 0) {
uint64_t file_number = file_meta->fd.GetNumber();
// Try getting info from table property
std::string fn =
TableFileName(compaction->immutable_options()->cf_paths,
file_number, file_meta->fd.GetPathId());
std::string fn = TableFileName(compaction->immutable_options().cf_paths,
file_number, file_meta->fd.GetPathId());
const auto& tp = input_table_properties.find(fn);
if (tp != input_table_properties.end()) {
file_input_entries = tp->second->num_entries;
@@ -2118,7 +2202,6 @@ void CompactionJob::UpdateCompactionJobStats(
void CompactionJob::LogCompaction() {
Compaction* compaction = compact_->compaction;
ColumnFamilyData* cfd = compaction->column_family_data();
// Let's check if anything will get logged. Don't prepare all the info if
// we're not logging
if (db_options_.info_log_level <= InfoLogLevel::INFO_LEVEL) {
@@ -2133,8 +2216,7 @@ void CompactionJob::LogCompaction() {
cfd->GetName().c_str(), scratch);
// build event logger report
auto stream = event_logger_->Log();
stream << "job" << job_id_ << "event"
<< "compaction_started"
stream << "job" << job_id_ << "event" << "compaction_started"
<< "compaction_reason"
<< GetCompactionReasonString(compaction->compaction_reason());
for (size_t i = 0; i < compaction->num_input_levels(); ++i) {
@@ -2151,8 +2233,8 @@ void CompactionJob::LogCompaction() {
? int64_t{-1} // Use -1 for "none"
: static_cast<int64_t>(existing_snapshots_[0]));
if (compaction->SupportsPerKeyPlacement()) {
stream << "preclude_last_level_min_seqno"
<< preclude_last_level_min_seqno_;
stream << "prenultimate_after_seqno" << penultimate_after_seqno_;
stream << "preserve_seqno_after" << preserve_seqno_after_;
stream << "penultimate_output_level" << compaction->GetPenultimateLevel();
stream << "penultimate_output_range"
<< GetCompactionPenultimateOutputRangeTypeString(
@@ -2171,7 +2253,7 @@ void CompactionJob::LogCompaction() {
}
std::string CompactionJob::GetTableFileName(uint64_t file_number) {
return TableFileName(compact_->compaction->immutable_options()->cf_paths,
return TableFileName(compact_->compaction->immutable_options().cf_paths,
file_number, compact_->compaction->output_path_id());
}
+21 -10
View File
@@ -176,7 +176,13 @@ class CompactionJob {
// REQUIRED: mutex held
// Prepare for the compaction by setting up boundaries for each subcompaction
void Prepare();
// and organizing seqno <-> time info. `known_single_subcompact` is non-null
// if we already have a known single subcompaction, with optional key bounds
// (currently for executing a remote compaction).
void Prepare(
std::optional<std::pair<std::optional<Slice>, std::optional<Slice>>>
known_single_subcompact);
// REQUIRED mutex not held
// Launch threads for each subcompaction and wait for them to finish. After
// that, verify table is usable and finally do bookkeeping to unify
@@ -187,8 +193,7 @@ class CompactionJob {
// Add compaction input/output to the current version
// Releases compaction file through Compaction::ReleaseCompactionFiles().
// Sets *compaction_released to true if compaction is released.
Status Install(const MutableCFOptions& mutable_cf_options,
bool* compaction_released);
Status Install(bool* compaction_released);
// Return the IO status
IOStatus io_status() const { return io_status_; }
@@ -276,8 +281,7 @@ class CompactionJob {
const Slice& next_table_min_key,
const Slice* comp_start_user_key,
const Slice* comp_end_user_key);
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options,
bool* compaction_released);
Status InstallCompactionResults(bool* compaction_released);
Status OpenCompactionOutputFile(SubcompactionState* sub_compact,
CompactionOutputs& outputs);
@@ -353,15 +357,18 @@ class CompactionJob {
// it also collects the smallest_seqno -> oldest_ancester_time from the SST.
SeqnoToTimeMapping seqno_to_time_mapping_;
// Minimal sequence number for preserving the time information. The time info
// older than this sequence number won't be preserved after the compaction and
// if it's bottommost compaction, the seq num will be zeroed out.
SequenceNumber preserve_time_min_seqno_ = kMaxSequenceNumber;
// Max seqno that can be zeroed out in last level, including for preserving
// write times.
SequenceNumber preserve_seqno_after_ = kMaxSequenceNumber;
// Minimal sequence number to preclude the data from the last level. If the
// key has bigger (newer) sequence number than this, it will be precluded from
// the last level (output to penultimate level).
SequenceNumber preclude_last_level_min_seqno_ = kMaxSequenceNumber;
SequenceNumber penultimate_after_seqno_ = kMaxSequenceNumber;
// Options File Number used for Remote Compaction
// Setting this requires DBMutex.
uint64_t options_file_number_ = 0;
// Get table file name in where it's outputting to, which should also be in
// `output_directory_`.
@@ -496,6 +503,10 @@ class CompactionServiceCompactionJob : private CompactionJob {
const CompactionServiceInput& compaction_service_input,
CompactionServiceResult* compaction_service_result);
// REQUIRED: mutex held
// Like CompactionJob::Prepare()
void Prepare();
// Run the compaction in current thread and return the result
Status Run();
@@ -959,7 +959,6 @@ int main(int argc, char** argv) {
return RUN_ALL_TESTS();
}
#else
int main(int /*argc*/, char** /*argv*/) { return 0; }
+18 -23
View File
@@ -3,7 +3,6 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include "db/compaction/compaction_job.h"
#include <algorithm>
@@ -302,7 +301,7 @@ class CompactionJobTestBase : public testing::Test {
std::unique_ptr<TableBuilder> table_builder(
cf_options_.table_factory->NewTableBuilder(
TableBuilderOptions(
*cfd_->ioptions(), mutable_cf_options_, read_options,
cfd_->ioptions(), mutable_cf_options_, read_options,
write_options, cfd_->internal_comparator(),
cfd_->internal_tbl_prop_coll_factories(),
CompressionType::kNoCompression, CompressionOptions(),
@@ -398,8 +397,8 @@ class CompactionJobTestBase : public testing::Test {
mutex_.Lock();
EXPECT_OK(versions_->LogAndApply(
versions_->GetColumnFamilySet()->GetDefault(), mutable_cf_options_,
read_options_, write_options_, &edit, &mutex_, nullptr));
versions_->GetColumnFamilySet()->GetDefault(), read_options_,
write_options_, &edit, &mutex_, nullptr));
mutex_.Unlock();
}
@@ -461,7 +460,7 @@ class CompactionJobTestBase : public testing::Test {
ReadOptions read_opts;
Status s = cf_options_.table_factory->NewTableReader(
read_opts,
TableReaderOptions(*cfd->ioptions(), nullptr, FileOptions(),
TableReaderOptions(cfd->ioptions(), nullptr, FileOptions(),
cfd_->internal_comparator(),
0 /* block_protection_bytes_per_key */),
std::move(freader), file_size, &table_reader, false);
@@ -646,12 +645,12 @@ class CompactionJobTestBase : public testing::Test {
}
Compaction compaction(
cfd->current()->storage_info(), *cfd->ioptions(),
*cfd->GetLatestMutableCFOptions(), mutable_db_options_,
cfd->current()->storage_info(), cfd->ioptions(),
cfd->GetLatestMutableCFOptions(), mutable_db_options_,
compaction_input_files, output_level,
mutable_cf_options_.target_file_size_base,
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
cfd->GetLatestMutableCFOptions()->compression_opts,
cfd->GetLatestMutableCFOptions().compression_opts,
Temperature::kUnknown, max_subcompactions, grandparents,
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr, true);
compaction.FinalizeInputInfo(cfd->current());
@@ -678,15 +677,14 @@ class CompactionJobTestBase : public testing::Test {
full_history_ts_low_);
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
compaction_job.Prepare();
compaction_job.Prepare(std::nullopt /*subcompact to be computed*/);
mutex_.Unlock();
Status s = compaction_job.Run();
ASSERT_OK(s);
ASSERT_OK(compaction_job.io_status());
mutex_.Lock();
bool compaction_released = false;
ASSERT_OK(compaction_job.Install(*cfd->GetLatestMutableCFOptions(),
&compaction_released));
ASSERT_OK(compaction_job.Install(&compaction_released));
ASSERT_OK(compaction_job.io_status());
mutex_.Unlock();
log_buffer.FlushBufferToLog();
@@ -1536,14 +1534,11 @@ TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
/*verify_func=*/[&](Compaction& comp) {
for (char c = 'a'; c <= 'z'; c++) {
if (c == 'a') {
ParsedInternalKey pik("a", 0U, kTypeValue);
ASSERT_FALSE(comp.WithinPenultimateLevelOutputRange(pik));
comp.TEST_AssertWithinPenultimateLevelOutputRange(
"a", true /*expect_failure*/);
} else {
std::string c_str{c};
// WithinPenultimateLevelOutputRange checks internal key range.
// 'z' is the last key, so set seqno properly.
ParsedInternalKey pik(c_str, c == 'z' ? 12U : 0U, kTypeValue);
ASSERT_TRUE(comp.WithinPenultimateLevelOutputRange(pik));
comp.TEST_AssertWithinPenultimateLevelOutputRange(c_str);
}
}
});
@@ -1900,8 +1895,8 @@ TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
const std::vector<int> input_levels = {0, 1};
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2});
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2});
}
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
@@ -1975,8 +1970,8 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
const std::vector<int> input_levels = {0, 1};
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2});
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2});
}
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
@@ -2037,8 +2032,8 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
for (int i = 80; i <= 100; i++) {
snapshots.emplace_back(i);
}
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2}, snapshots);
RunCompaction({lvl0_files, lvl1_files}, input_levels,
{expected_file1, expected_file2}, snapshots);
}
TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
+20 -14
View File
@@ -320,7 +320,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
// More details, check PR #1963
const size_t num_skippable_boundaries_crossed =
being_grandparent_gap_ ? 2 : 3;
if (compaction_->immutable_options()->compaction_style ==
if (compaction_->immutable_options().compaction_style ==
kCompactionStyleLevel &&
num_grandparent_boundaries_crossed >=
num_skippable_boundaries_crossed &&
@@ -341,7 +341,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
// target file size. The test shows it can generate larger files than a
// static threshold like 75% and has a similar write amplification
// improvement.
if (compaction_->immutable_options()->compaction_style ==
if (compaction_->immutable_options().compaction_style ==
kCompactionStyleLevel &&
current_output_file_size_ >=
((compaction_->target_output_file_size() + 99) / 100) *
@@ -455,9 +455,11 @@ void SetMaxSeqAndTs(InternalKey& internal_key, const Slice& user_key,
} // namespace
Status CompactionOutputs::AddRangeDels(
CompactionRangeDelAggregator& range_del_agg,
const Slice* comp_start_user_key, const Slice* comp_end_user_key,
CompactionIterationStats& range_del_out_stats, bool bottommost_level,
const InternalKeyComparator& icmp, SequenceNumber earliest_snapshot,
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range,
const Slice& next_table_min_key, const std::string& full_history_ts_low) {
// The following example does not happen since
// CompactionOutput::ShouldStopBefore() always return false for the first
@@ -470,7 +472,7 @@ Status CompactionOutputs::AddRangeDels(
// Then meta.smallest will be set to comp_start_user_key@seqno
// and meta.largest will be set to comp_start_user_key@kMaxSequenceNumber
// which violates the assumption that meta.smallest should be <= meta.largest.
assert(HasRangeDel());
assert(!range_del_agg.IsEmpty());
FileMetaData& meta = current_output().meta;
const Comparator* ucmp = icmp.user_comparator();
InternalKey lower_bound_buf, upper_bound_buf;
@@ -579,13 +581,19 @@ Status CompactionOutputs::AddRangeDels(
assert(comp_end_user_key == nullptr || upper_bound == nullptr ||
ucmp->CompareWithoutTimestamp(ExtractUserKey(*upper_bound),
*comp_end_user_key) <= 0);
auto it = range_del_agg_->NewIterator(lower_bound, upper_bound);
auto it = range_del_agg.NewIterator(lower_bound, upper_bound);
Slice last_tombstone_start_user_key{};
bool reached_lower_bound = false;
const ReadOptions read_options(Env::IOActivity::kCompaction);
for (it->SeekToFirst(); it->Valid(); it->Next()) {
auto tombstone = it->Tombstone();
auto kv = tombstone.Serialize();
// Filter out by seqno for per-key placement
if (tombstone.seq_ < keep_seqno_range.first ||
tombstone.seq_ >= keep_seqno_range.second) {
continue;
}
InternalKey tombstone_end = tombstone.SerializeEndKey();
// TODO: the underlying iterator should support clamping the bounds.
// tombstone_end.Encode is of form user_key@kMaxSeqno
@@ -744,11 +752,10 @@ Status CompactionOutputs::AddRangeDels(
}
void CompactionOutputs::FillFilesToCutForTtl() {
if (compaction_->immutable_options()->compaction_style !=
if (compaction_->immutable_options().compaction_style !=
kCompactionStyleLevel ||
compaction_->immutable_options()->compaction_pri !=
kMinOverlappingRatio ||
compaction_->mutable_cf_options()->ttl == 0 ||
compaction_->immutable_options().compaction_pri != kMinOverlappingRatio ||
compaction_->mutable_cf_options().ttl == 0 ||
compaction_->num_input_levels() < 2 || compaction_->bottommost_level()) {
return;
}
@@ -756,20 +763,19 @@ void CompactionOutputs::FillFilesToCutForTtl() {
// We define new file with the oldest ancestor time to be younger than 1/4
// TTL, and an old one to be older than 1/2 TTL time.
int64_t temp_current_time;
auto get_time_status =
compaction_->immutable_options()->clock->GetCurrentTime(
&temp_current_time);
auto get_time_status = compaction_->immutable_options().clock->GetCurrentTime(
&temp_current_time);
if (!get_time_status.ok()) {
return;
}
auto current_time = static_cast<uint64_t>(temp_current_time);
if (current_time < compaction_->mutable_cf_options()->ttl) {
if (current_time < compaction_->mutable_cf_options().ttl) {
return;
}
uint64_t old_age_thres =
current_time - compaction_->mutable_cf_options()->ttl / 2;
current_time - compaction_->mutable_cf_options().ttl / 2;
const std::vector<FileMetaData*>& olevel =
*(compaction_->inputs(compaction_->num_input_levels() - 1));
for (FileMetaData* file : olevel) {
@@ -779,7 +785,7 @@ void CompactionOutputs::FillFilesToCutForTtl() {
// of small files.
if (oldest_ancester_time < old_age_thres &&
file->fd.GetFileSize() >
compaction_->mutable_cf_options()->target_file_size_base / 2) {
compaction_->mutable_cf_options().target_file_size_base / 2) {
files_to_cut_for_ttl_.push_back(file);
}
}
+15 -25
View File
@@ -45,6 +45,8 @@ class CompactionOutputs {
explicit CompactionOutputs(const Compaction* compaction,
const bool is_penultimate_level);
bool IsPenultimateLevel() const { return is_penultimate_level_; }
// Add generated output to the list
void AddOutput(FileMetaData&& meta, const InternalKeyComparator& icmp,
bool enable_hash, bool finished = false,
@@ -182,18 +184,13 @@ class CompactionOutputs {
// @param next_table_min_key internal key lower bound for the next compaction
// output.
// @param full_history_ts_low used for range tombstone garbage collection.
Status AddRangeDels(const Slice* comp_start_user_key,
const Slice* comp_end_user_key,
CompactionIterationStats& range_del_out_stats,
bool bottommost_level, const InternalKeyComparator& icmp,
SequenceNumber earliest_snapshot,
const Slice& next_table_min_key,
const std::string& full_history_ts_low);
// if the outputs have range delete, range delete is also data
bool HasRangeDel() const {
return range_del_agg_ && !range_del_agg_->IsEmpty();
}
Status AddRangeDels(
CompactionRangeDelAggregator& range_del_agg,
const Slice* comp_start_user_key, const Slice* comp_end_user_key,
CompactionIterationStats& range_del_out_stats, bool bottommost_level,
const InternalKeyComparator& icmp, SequenceNumber earliest_snapshot,
std::pair<SequenceNumber, SequenceNumber> keep_seqno_range,
const Slice& next_table_min_key, const std::string& full_history_ts_low);
private:
friend class SubcompactionState;
@@ -259,11 +256,15 @@ class CompactionOutputs {
// Close the current output. `open_file_func` is needed for creating new file
// for range-dels only output file.
Status CloseOutput(const Status& curr_status,
CompactionRangeDelAggregator* range_del_agg,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func) {
Status status = curr_status;
// handle subcompaction containing only range deletions
if (status.ok() && !HasBuilder() && !HasOutput() && HasRangeDel()) {
// Handle subcompaction containing only range deletions. They could
// be dropped or sent to another output level, so this is only an
// over-approximate check for whether opening is needed.
if (status.ok() && !HasBuilder() && !HasOutput() && range_del_agg &&
!range_del_agg->IsEmpty()) {
status = open_file_func(*this);
}
if (HasBuilder()) {
@@ -288,16 +289,6 @@ class CompactionOutputs {
return outputs_.back();
}
// Assign the range_del_agg to the target output level. There's only one
// range-del-aggregator per compaction outputs, for
// output_to_penultimate_level compaction it is only assigned to the
// penultimate level.
void AssignRangeDelAggregator(
std::unique_ptr<CompactionRangeDelAggregator>&& range_del_agg) {
assert(range_del_agg_ == nullptr);
range_del_agg_ = std::move(range_del_agg);
}
const Compaction* compaction_;
// current output builder and writer
@@ -319,7 +310,6 @@ class CompactionOutputs {
// indicate if this CompactionOutputs obj for penultimate_level, should always
// be false if per_key_placement feature is not enabled.
const bool is_penultimate_level_;
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_ = nullptr;
// partitioner information
std::string last_key_for_partitioner_;
+13 -12
View File
@@ -721,14 +721,14 @@ Compaction* CompactionPicker::CompactRange(
// two files overlap.
if (input_level > 0) {
const uint64_t limit = mutable_cf_options.max_compaction_bytes;
uint64_t input_level_total = 0;
int hint_index = -1;
InternalKey* smallest = nullptr;
assert(!inputs.empty());
// Always include first file for progress.
uint64_t input_level_total = inputs[0]->fd.GetFileSize();
InternalKey* smallest = &(inputs[0]->smallest);
InternalKey* largest = nullptr;
for (size_t i = 0; i + 1 < inputs.size(); ++i) {
if (!smallest) {
smallest = &inputs[i]->smallest;
}
for (size_t i = 1; i < inputs.size(); ++i) {
// Consider whether to include inputs[i]
largest = &inputs[i]->largest;
uint64_t input_file_size = inputs[i]->fd.GetFileSize();
@@ -744,13 +744,11 @@ Compaction* CompactionPicker::CompactRange(
input_level_total += input_file_size;
if (input_level_total + output_level_total >= limit) {
if (input_level_total + output_level_total > limit) {
// To ensure compaction size is <= limit, leave out inputs from
// index i onwards.
covering_the_whole_range = false;
// still include the current file, so the compaction could be larger
// than max_compaction_bytes, which is also to make sure the compaction
// can make progress even `max_compaction_bytes` is small (e.g. smaller
// than an SST file).
inputs.files.resize(i + 1);
inputs.files.resize(i);
break;
}
}
@@ -819,6 +817,9 @@ Compaction* CompactionPicker::CompactRange(
output_level = vstorage->base_level();
assert(output_level > 0);
}
for (int i = input_level + 1; i < output_level; i++) {
assert(vstorage->NumLevelFiles(i) == 0);
}
output_level_inputs.level = output_level;
if (input_level != output_level) {
int parent_index = -1;
+2
View File
@@ -73,6 +73,8 @@ class CompactionPicker {
// compaction_end will be set to nullptr.
// Client is responsible for compaction_end storage -- when called,
// *compaction_end should point to valid InternalKey!
// REQUIRES: If not compacting all levels (input_level == kCompactAllLevels),
// then levels between input_level and output_level should be empty.
virtual Compaction* CompactRange(
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
+31
View File
@@ -2656,6 +2656,37 @@ TEST_F(CompactionPickerTest, HitCompactionLimitWhenAddFileFromInputLevel) {
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
}
TEST_F(CompactionPickerTest, CompactRangeMaxCompactionBytes) {
mutable_cf_options_.max_compaction_bytes = 800000U;
NewVersionStorage(6, kCompactionStyleLevel);
// We will first pick file 1 and 2 and then stop before file 3.
// Since picking file 3 will pull in file 4 and 5 from L2 and
// exceed max_compaction_bytes.
Add(1, 1U, "100", "110", 10000U);
Add(1, 2U, "200", "210", 10000U, 0, 0);
Add(1, 3U, "400", "410", 10000U, 0, 0);
Add(2, 4U, "300", "310", 400000U);
Add(2, 5U, "320", "330", 400000U);
UpdateVersionStorageInfo();
bool manual_conflict = false;
InternalKey manual_end;
InternalKey* manual_end_ptr = &manual_end;
std::unique_ptr<Compaction> compaction(level_compaction_picker.CompactRange(
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
/*input_level=*/1, /*output_level=*/2,
/*compact_range_options*/ {}, /*begin=*/nullptr, /*end=*/nullptr,
&manual_end_ptr, &manual_conflict,
/*max_file_num_to_ignore=*/std::numeric_limits<uint64_t>::max(),
/*trim_ts=*/""));
ASSERT_TRUE(compaction.get() != nullptr);
ASSERT_EQ(1U, compaction->num_input_levels());
ASSERT_EQ(2, compaction->output_level());
ASSERT_EQ(2U, compaction->num_input_files(0));
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
}
TEST_F(CompactionPickerTest, IsTrivialMoveOn) {
mutable_cf_options_.max_bytes_for_level_base = 10000u;
mutable_cf_options_.max_compaction_bytes = 10001u;
+59 -43
View File
@@ -48,10 +48,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
compaction_input.has_end = sub_compact->end.has_value();
compaction_input.end =
compaction_input.has_end ? sub_compact->end->ToString() : "";
compaction_input.options_file_number =
sub_compact->compaction->input_version()
->version_set()
->options_file_number();
compaction_input.options_file_number = options_file_number_;
TEST_SYNC_POINT_CALLBACK(
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
@@ -86,6 +83,14 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
switch (response.status) {
case CompactionServiceJobStatus::kSuccess:
break;
case CompactionServiceJobStatus::kAborted:
sub_compact->status =
Status::Aborted("Scheduling a remote compaction job was aborted");
ROCKS_LOG_WARN(
db_options_.info_log,
"[%s] [JOB %d] Remote compaction was aborted at Schedule()",
compaction->column_family_data()->GetName().c_str(), job_id_);
return response.status;
case CompactionServiceJobStatus::kFailure:
sub_compact->status = Status::Incomplete(
"CompactionService failed to schedule a remote compaction job.");
@@ -121,6 +126,16 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
return compaction_status;
}
if (compaction_status == CompactionServiceJobStatus::kAborted) {
sub_compact->status =
Status::Aborted("Waiting a remote compaction job was aborted");
ROCKS_LOG_INFO(db_options_.info_log,
"[%s] [JOB %d] Remote compaction was aborted during Wait()",
compaction->column_family_data()->GetName().c_str(),
job_id_);
return compaction_status;
}
CompactionServiceResult compaction_result;
s = CompactionServiceResult::Read(compaction_result_binary,
&compaction_result);
@@ -177,7 +192,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
for (const auto& file : compaction_result.output_files) {
uint64_t file_num = versions_->NewFileNumber();
auto src_file = compaction_result.output_path + "/" + file.file_name;
auto tgt_file = TableFileName(compaction->immutable_options()->cf_paths,
auto tgt_file = TableFileName(compaction->immutable_options().cf_paths,
file_num, compaction->output_path_id());
s = fs_->RenameFile(src_file, tgt_file, IOOptions(), nullptr);
if (!s.ok()) {
@@ -189,6 +204,10 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
FileMetaData meta;
uint64_t file_size;
// FIXME: file_size should be part of CompactionServiceOutputFile so that
// we don't get DB corruption if the full file size has not been propagated
// back to the caller through the file system (which could have metadata
// lag or caching bugs).
s = fs_->GetFileSize(tgt_file, IOOptions(), &file_size, nullptr);
if (!s.ok()) {
sub_compact->status = s;
@@ -265,43 +284,38 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
std::string output_path,
const CompactionServiceInput& compaction_service_input,
CompactionServiceResult* compaction_service_result)
: CompactionJob(
job_id, compaction, db_options, mutable_db_options, file_options,
versions, shutting_down, log_buffer, nullptr, output_directory,
nullptr, stats, db_mutex, db_error_handler,
std::move(existing_snapshots), kMaxSequenceNumber, nullptr, nullptr,
std::move(table_cache), event_logger,
compaction->mutable_cf_options()->paranoid_file_checks,
compaction->mutable_cf_options()->report_bg_io_stats, dbname,
&(compaction_service_result->stats), Env::Priority::USER, io_tracer,
manual_compaction_canceled, db_id, db_session_id,
compaction->column_family_data()->GetFullHistoryTsLow()),
: CompactionJob(job_id, compaction, db_options, mutable_db_options,
file_options, versions, shutting_down, log_buffer, nullptr,
output_directory, nullptr, stats, db_mutex,
db_error_handler, std::move(existing_snapshots),
kMaxSequenceNumber, nullptr, nullptr,
std::move(table_cache), event_logger,
compaction->mutable_cf_options().paranoid_file_checks,
compaction->mutable_cf_options().report_bg_io_stats, dbname,
&(compaction_service_result->stats), Env::Priority::USER,
io_tracer, manual_compaction_canceled, db_id, db_session_id,
compaction->column_family_data()->GetFullHistoryTsLow()),
output_path_(std::move(output_path)),
compaction_input_(compaction_service_input),
compaction_result_(compaction_service_result) {}
void CompactionServiceCompactionJob::Prepare() {
std::optional<Slice> begin;
if (compaction_input_.has_begin) {
begin = compaction_input_.begin;
}
std::optional<Slice> end;
if (compaction_input_.has_end) {
end = compaction_input_.end;
}
CompactionJob::Prepare(std::make_pair(begin, end));
}
Status CompactionServiceCompactionJob::Run() {
AutoThreadOperationStageUpdater stage_updater(
ThreadStatus::STAGE_COMPACTION_RUN);
auto* c = compact_->compaction;
assert(c->column_family_data() != nullptr);
const VersionStorageInfo* storage_info = c->input_version()->storage_info();
assert(storage_info);
assert(storage_info->NumLevelFiles(compact_->compaction->level()) > 0);
write_hint_ = storage_info->CalculateSSTWriteHint(c->output_level());
bottommost_level_ = c->bottommost_level();
Slice begin = compaction_input_.begin;
Slice end = compaction_input_.end;
compact_->sub_compact_states.emplace_back(
c,
compaction_input_.has_begin ? std::optional<Slice>(begin)
: std::optional<Slice>(),
compaction_input_.has_end ? std::optional<Slice>(end)
: std::optional<Slice>(),
/*sub_job_id*/ 0);
log_buffer_->FlushBufferToLog();
LogCompaction();
@@ -376,16 +390,18 @@ Status CompactionServiceCompactionJob::Run() {
// Build Output
compaction_result_->output_level = compact_->compaction->output_level();
compaction_result_->output_path = output_path_;
for (const auto& output_file : sub_compact->GetOutputs()) {
auto& meta = output_file.meta;
compaction_result_->output_files.emplace_back(
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
meta.file_creation_time, meta.epoch_number, meta.file_checksum,
meta.file_checksum_func_name, output_file.validator.GetHash(),
meta.marked_for_compaction, meta.unique_id,
*output_file.table_properties);
if (status.ok()) {
for (const auto& output_file : sub_compact->GetOutputs()) {
auto& meta = output_file.meta;
compaction_result_->output_files.emplace_back(
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
meta.file_creation_time, meta.epoch_number, meta.file_checksum,
meta.file_checksum_func_name, output_file.validator.GetHash(),
meta.marked_for_compaction, meta.unique_id,
*output_file.table_properties);
}
}
TEST_SYNC_POINT_CALLBACK("CompactionServiceCompactionJob::Run:0",
+169 -3
View File
@@ -114,6 +114,8 @@ class MyTestCompactionService : public CompactionService {
}
}
void CancelAwaitingJobs() override { canceled_ = true; }
void OnInstallation(const std::string& /*scheduled_job_id*/,
CompactionServiceJobStatus status) override {
final_updated_status_ = status;
@@ -146,6 +148,7 @@ class MyTestCompactionService : public CompactionService {
}
void SetCanceled(bool canceled) { canceled_ = canceled; }
bool GetCanceled() { return canceled_; }
void GetResult(CompactionServiceResult* deserialized) {
CompactionServiceResult::Read(result_, deserialized).PermitUncheckedError();
@@ -194,12 +197,15 @@ class CompactionServiceTest : public DBTestBase {
options->statistics = primary_statistics_;
compactor_statistics_ = CreateDBStatistics();
compaction_service_ = std::make_shared<MyTestCompactionService>(
auto my_cs = std::make_shared<MyTestCompactionService>(
dbname_, *options, compactor_statistics_, remote_listeners,
remote_table_properties_collector_factories);
compaction_service_ = my_cs;
options->compaction_service = compaction_service_;
DestroyAndReopen(*options);
CreateAndReopenWithCF({"cf_1", "cf_2", "cf_3"}, *options);
my_cs->SetCanceled(false);
}
Statistics* GetCompactorStatistics() { return compactor_statistics_.get(); }
@@ -339,6 +345,8 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
assert(*id != kNullUniqueId64x2);
verify_passed++;
});
Close();
my_cs->SetCanceled(false);
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "cf_1", "cf_2", "cf_3"},
options);
ASSERT_GT(verify_passed, 0);
@@ -416,6 +424,38 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
ASSERT_TRUE(result.stats.is_remote_compaction);
}
TEST_F(CompactionServiceTest, CompactionOutputFileIOError) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::FinishCompactionOutputFile()::AfterFinish",
[&](void* status) {
// override status
auto s = static_cast<Status*>(status);
*s = Status::IOError("Injected IOError!");
});
SyncPoint::GetInstance()->EnableProcessing();
std::string start_str = Key(15);
std::string end_str = Key(45);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ASSERT_NOK(db_->CompactRange(CompactRangeOptions(), &start, &end));
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_NOK(result.status);
ASSERT_TRUE(result.stats.is_manual_compaction);
ASSERT_TRUE(result.stats.is_remote_compaction);
}
TEST_F(CompactionServiceTest, PreservedOptionsLocalCompaction) {
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
@@ -455,7 +495,7 @@ TEST_F(CompactionServiceTest, PreservedOptionsLocalCompaction) {
ASSERT_TRUE(s.IsNotFound());
// Should be old value
ASSERT_EQ(2, compaction->mutable_cf_options()
->level0_file_num_compaction_trigger);
.level0_file_num_compaction_trigger);
ASSERT_TRUE(dbfull()->min_options_file_numbers_.empty());
});
@@ -541,7 +581,7 @@ TEST_F(CompactionServiceTest, PreservedOptionsRemoteCompaction) {
"CompactionJob::ProcessKeyValueCompaction()::Processing", [&](void* arg) {
auto compaction = static_cast<Compaction*>(arg);
ASSERT_EQ(2, compaction->mutable_cf_options()
->level0_file_num_compaction_trigger);
.level0_file_num_compaction_trigger);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
@@ -964,6 +1004,41 @@ TEST_F(CompactionServiceTest, CancelCompactionOnRemoteSide) {
VerifyTestData();
}
TEST_F(CompactionServiceTest, CancelCompactionOnPrimarySide) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
ReopenWithCompactionService(&options);
GenerateTestData();
auto my_cs = GetCompactionService();
std::string start_str = Key(15);
std::string end_str = Key(45);
Slice start(start_str);
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
ReopenWithCompactionService(&options);
GenerateTestData();
my_cs = GetCompactionService();
// Primary DB calls CancelAllBackgroundWork() while the compaction is running
SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::Run():Inprogress",
[&](void* /*arg*/) { CancelAllBackgroundWork(db_, false /*wait*/); });
SyncPoint::GetInstance()->EnableProcessing();
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
ASSERT_TRUE(s.IsIncomplete());
// Check canceled_ was set to true by CancelAwaitingJobs()
ASSERT_TRUE(my_cs->GetCanceled());
// compaction number is not increased
ASSERT_GE(my_cs->GetCompactionNum(), comp_num);
}
TEST_F(CompactionServiceTest, FailedToStart) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
@@ -1086,6 +1161,63 @@ TEST_F(CompactionServiceTest, Snapshot) {
db_->ReleaseSnapshot(s1);
}
TEST_F(CompactionServiceTest, PrecludeLastLevel) {
const int kNumTrigger = 4;
const int kNumLevels = 7;
const int kNumKeys = 100;
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleUniversal;
options.last_level_temperature = Temperature::kCold;
options.level0_file_num_compaction_trigger = 4;
options.max_subcompactions = 10;
options.num_levels = kNumLevels;
ReopenWithCompactionService(&options);
// Alternate for comparison: DestroyAndReopen(options);
// This is simpler than setting up mock time to make the user option work,
// but is not as direct as testing with preclude option itself.
SyncPoint::GetInstance()->SetCallBack(
"Compaction::SupportsPerKeyPlacement:Enabled",
[&](void* arg) { *static_cast<bool*>(arg) = true; });
SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::PrepareTimes():preclude_last_level_min_seqno",
[&](void* arg) { *static_cast<SequenceNumber*>(arg) = 100; });
SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < kNumTrigger; i++) {
for (int j = 0; j < kNumKeys; j++) {
// FIXME: need to assign outputs to levels to allow overlapping ranges:
// ASSERT_OK(Put(Key(j * kNumTrigger + i), "v" + std::to_string(i)));
// instead of this (too easy):
ASSERT_OK(Put(Key(i * kNumKeys + j), "v" + std::to_string(i)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
// Data split between penultimate (kUnknown) and last (kCold) levels
// FIXME: need to assign outputs to levels to get this:
// ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
// ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
// ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
// instead of this (WRONG but currently expected):
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
// Check manifest temperatures
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
// TODO: Check FileSystem temperatures with FileTemperatureTestFS
for (int i = 0; i < kNumTrigger; i++) {
for (int j = 0; j < kNumKeys; j++) {
// FIXME
// ASSERT_EQ(Get(Key(j * kNumTrigger + i)), "v" + std::to_string(i));
ASSERT_EQ(Get(Key(i * kNumKeys + j)), "v" + std::to_string(i));
}
}
}
TEST_F(CompactionServiceTest, ConcurrentCompaction) {
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 100;
@@ -1339,6 +1471,40 @@ TEST_F(CompactionServiceTest, FallbackLocalManual) {
VerifyTestData();
}
TEST_F(CompactionServiceTest, AbortedWhileWait) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
ReopenWithCompactionService(&options);
GenerateTestData();
VerifyTestData();
auto my_cs = GetCompactionService();
Statistics* compactor_statistics = GetCompactorStatistics();
Statistics* primary_statistics = GetPrimaryStatistics();
my_cs->ResetOverride();
std::string start_str = Key(15);
std::string end_str = Key(45);
Slice start(start_str);
Slice end(end_str);
// Override Wait() result with kAborted
my_cs->OverrideWaitStatus(CompactionServiceJobStatus::kAborted);
start_str = Key(120);
start = start_str;
Status s = db_->CompactRange(CompactRangeOptions(), &start, nullptr);
ASSERT_NOK(s);
ASSERT_TRUE(s.IsAborted());
// no remote compaction is run
ASSERT_EQ(my_cs->GetCompactionNum(), 0);
// make sure the compaction statistics is not recorded any side
ASSERT_EQ(primary_statistics->getTickerCount(COMPACT_WRITE_BYTES), 0);
ASSERT_EQ(primary_statistics->getTickerCount(REMOTE_COMPACT_WRITE_BYTES), 0);
ASSERT_EQ(compactor_statistics->getTickerCount(COMPACT_WRITE_BYTES), 0);
}
TEST_F(CompactionServiceTest, RemoteEventListener) {
class RemoteEventListenerTest : public EventListener {
public:
+20 -14
View File
@@ -15,8 +15,19 @@
namespace ROCKSDB_NAMESPACE {
void SubcompactionState::AggregateCompactionOutputStats(
InternalStats::CompactionStatsFull& compaction_stats) const {
// Outputs should be closed. By extension, any files created just for
// range deletes have already been written also.
assert(compaction_outputs_.HasBuilder() == false);
assert(penultimate_level_outputs_.HasBuilder() == false);
// FIXME: These stats currently include abandonned output files
// assert(compaction_outputs_.stats_.num_output_files ==
// compaction_outputs_.outputs_.size());
// assert(penultimate_level_outputs_.stats_.num_output_files ==
// penultimate_level_outputs_.outputs_.size());
compaction_stats.stats.Add(compaction_outputs_.stats_);
if (HasPenultimateLevelOutputs()) {
if (penultimate_level_outputs_.HasOutput()) {
compaction_stats.has_penultimate_level_output = true;
compaction_stats.penultimate_level_stats.Add(
penultimate_level_outputs_.stats_);
@@ -43,7 +54,7 @@ void SubcompactionState::Cleanup(Cache* cache) {
// be picked up by scanning the DB directory.
TableCache::ReleaseObsolete(
cache, out.meta.fd.GetNumber(), nullptr /*handle*/,
compaction->mutable_cf_options()->uncache_aggressiveness);
compaction->mutable_cf_options().uncache_aggressiveness);
}
}
// TODO: sub_compact.io_status is not checked like status. Not sure if thats
@@ -52,7 +63,7 @@ void SubcompactionState::Cleanup(Cache* cache) {
}
Slice SubcompactionState::SmallestUserKey() const {
if (has_penultimate_level_outputs_) {
if (penultimate_level_outputs_.HasOutput()) {
Slice a = compaction_outputs_.SmallestUserKey();
Slice b = penultimate_level_outputs_.SmallestUserKey();
if (a.empty()) {
@@ -74,7 +85,7 @@ Slice SubcompactionState::SmallestUserKey() const {
}
Slice SubcompactionState::LargestUserKey() const {
if (has_penultimate_level_outputs_) {
if (penultimate_level_outputs_.HasOutput()) {
Slice a = compaction_outputs_.LargestUserKey();
Slice b = penultimate_level_outputs_.LargestUserKey();
if (a.empty()) {
@@ -96,18 +107,13 @@ Slice SubcompactionState::LargestUserKey() const {
}
Status SubcompactionState::AddToOutput(
const CompactionIterator& iter,
const CompactionIterator& iter, bool use_penultimate_output,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func) {
// update target output first
is_current_penultimate_level_ = iter.output_to_penultimate_level();
current_outputs_ = is_current_penultimate_level_ ? &penultimate_level_outputs_
: &compaction_outputs_;
if (is_current_penultimate_level_) {
has_penultimate_level_outputs_ = true;
}
return Current().AddToOutput(iter, open_file_func, close_file_func);
// update target output
current_outputs_ = use_penultimate_output ? &penultimate_level_outputs_
: &compaction_outputs_;
return current_outputs_->AddToOutput(iter, open_file_func, close_file_func);
}
} // namespace ROCKSDB_NAMESPACE
+32 -35
View File
@@ -81,22 +81,13 @@ class SubcompactionState {
// it returns both the last level outputs and penultimate level outputs.
OutputIterator GetOutputs() const;
// Assign range dels aggregator, for each range_del, it can only be assigned
// to one output level, for per_key_placement, it's going to be the
// penultimate level.
// TODO: This does not work for per_key_placement + user-defined timestamp +
// DeleteRange() combo. If user-defined timestamp is enabled,
// it is possible for a range tombstone to belong to bottommost level (
// seqno < earliest snapshot) without being dropped (garbage collection
// for user-defined timestamp).
// Assign range dels aggregator. The various tombstones will potentially
// be filtered to different outputs.
void AssignRangeDelAggregator(
std::unique_ptr<CompactionRangeDelAggregator>&& range_del_agg) {
if (compaction->SupportsPerKeyPlacement()) {
penultimate_level_outputs_.AssignRangeDelAggregator(
std::move(range_del_agg));
} else {
compaction_outputs_.AssignRangeDelAggregator(std::move(range_del_agg));
}
assert(range_del_agg_ == nullptr);
assert(range_del_agg);
range_del_agg_ = std::move(range_del_agg);
}
void RemoveLastEmptyOutput() {
@@ -151,20 +142,11 @@ class SubcompactionState {
sub_job_id(state.sub_job_id),
compaction_outputs_(std::move(state.compaction_outputs_)),
penultimate_level_outputs_(std::move(state.penultimate_level_outputs_)),
is_current_penultimate_level_(state.is_current_penultimate_level_),
has_penultimate_level_outputs_(state.has_penultimate_level_outputs_) {
current_outputs_ = is_current_penultimate_level_
? &penultimate_level_outputs_
: &compaction_outputs_;
}
bool HasPenultimateLevelOutputs() const {
return has_penultimate_level_outputs_ ||
penultimate_level_outputs_.HasRangeDel();
}
bool IsCurrentPenultimateLevel() const {
return is_current_penultimate_level_;
range_del_agg_(std::move(state.range_del_agg_)) {
current_outputs_ =
state.current_outputs_ == &state.penultimate_level_outputs_
? &penultimate_level_outputs_
: &compaction_outputs_;
}
// Add all the new files from this compaction to version_edit
@@ -187,8 +169,18 @@ class SubcompactionState {
return *current_outputs_;
}
CompactionRangeDelAggregator* RangeDelAgg() const {
return range_del_agg_.get();
}
// if the outputs have range delete, range delete is also data
bool HasRangeDel() const {
return range_del_agg_ && !range_del_agg_->IsEmpty();
}
// Add compaction_iterator key/value to the `Current` output group.
Status AddToOutput(const CompactionIterator& iter,
bool use_penultimate_output,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func);
@@ -197,14 +189,20 @@ class SubcompactionState {
Status CloseCompactionFiles(const Status& curr_status,
const CompactionFileOpenFunc& open_file_func,
const CompactionFileCloseFunc& close_file_func) {
auto per_key = compaction->SupportsPerKeyPlacement();
// Call FinishCompactionOutputFile() even if status is not ok: it needs to
// close the output file.
// CloseOutput() may open new compaction output files.
is_current_penultimate_level_ = true;
Status s = penultimate_level_outputs_.CloseOutput(
curr_status, open_file_func, close_file_func);
is_current_penultimate_level_ = false;
s = compaction_outputs_.CloseOutput(s, open_file_func, close_file_func);
Status s = curr_status;
if (per_key) {
s = penultimate_level_outputs_.CloseOutput(
s, range_del_agg_.get(), open_file_func, close_file_func);
} else {
assert(penultimate_level_outputs_.HasBuilder() == false);
assert(penultimate_level_outputs_.HasOutput() == false);
}
s = compaction_outputs_.CloseOutput(s, range_del_agg_.get(), open_file_func,
close_file_func);
return s;
}
@@ -213,8 +211,7 @@ class SubcompactionState {
CompactionOutputs compaction_outputs_;
CompactionOutputs penultimate_level_outputs_;
CompactionOutputs* current_outputs_ = &compaction_outputs_;
bool is_current_penultimate_level_ = false;
bool has_penultimate_level_outputs_ = false;
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
};
} // namespace ROCKSDB_NAMESPACE
+109 -58
View File
@@ -195,10 +195,9 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
std::vector<SequenceNumber> seq_history;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
"CompactionJob::PrepareTimes():preclude_last_level_min_seqno",
[&](void* arg) {
*static_cast<SequenceNumber*>(arg) = latest_cold_seq.load();
});
SyncPoint::GetInstance()->EnableProcessing();
@@ -339,7 +338,10 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
}
TEST_F(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
// This test was essentially for a hacked-up version on future functionality.
// It can be resurrected if/when a form of range-based tiering is properly
// implemented.
TEST_F(TieredCompactionTest, DISABLED_RangeBasedTieredStorageUniversal) {
const int kNumTrigger = 4;
const int kNumLevels = 7;
const int kNumKeys = 100;
@@ -440,12 +442,12 @@ TEST_F(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
{
MutexLock l(&mutex);
hot_start = Key(1);
hot_end = Key(1000);
hot_end = Key(300);
}
// generate files just enough to trigger compaction
for (int i = 0; i < kNumTrigger - 1; i++) {
for (int j = 0; j < 1000; j++) {
for (int j = 0; j < 300; j++) {
ASSERT_OK(Put(Key(j), "value" + std::to_string(j)));
}
ASSERT_OK(Flush());
@@ -494,6 +496,7 @@ TEST_F(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
// verify data
std::string value;
for (int i = 0; i < kNumKeys; i++) {
SCOPED_TRACE(Key(i));
if (i < 70) {
ASSERT_TRUE(db_->Get(ReadOptions(), Key(i), &value).IsNotFound());
} else {
@@ -527,13 +530,13 @@ TEST_F(TieredCompactionTest, LevelColdRangeDelete) {
options.max_subcompactions = 10;
DestroyAndReopen(options);
std::atomic_uint64_t latest_cold_seq = 0;
// Initially let everything into cold
std::atomic_uint64_t latest_cold_seq = kMaxSequenceNumber;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
"CompactionJob::PrepareTimes():preclude_last_level_min_seqno",
[&](void* arg) {
*static_cast<SequenceNumber*>(arg) = latest_cold_seq.load();
});
SyncPoint::GetInstance()->EnableProcessing();
@@ -634,13 +637,13 @@ TEST_F(TieredCompactionTest, LevelOutofBoundaryRangeDelete) {
options.max_subcompactions = 10;
DestroyAndReopen(options);
std::atomic_uint64_t latest_cold_seq = 0;
// Initially let everything into cold
std::atomic_uint64_t latest_cold_seq = kMaxSequenceNumber;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
"CompactionJob::PrepareTimes():preclude_last_level_min_seqno",
[&](void* arg) {
*static_cast<SequenceNumber*>(arg) = latest_cold_seq.load();
});
SyncPoint::GetInstance()->EnableProcessing();
@@ -654,6 +657,8 @@ TEST_F(TieredCompactionTest, LevelOutofBoundaryRangeDelete) {
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
ASSERT_EQ("0,0,10", FilesPerLevel());
// Stop admitting to cold tier
latest_cold_seq = dbfull()->GetLatestSequenceNumber();
auto snap = db_->GetSnapshot();
// only range delete
@@ -767,10 +772,9 @@ TEST_F(TieredCompactionTest, UniversalRangeDelete) {
std::atomic_uint64_t latest_cold_seq = 0;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
"CompactionJob::PrepareTimes():preclude_last_level_min_seqno",
[&](void* arg) {
*static_cast<SequenceNumber*>(arg) = latest_cold_seq.load();
});
SyncPoint::GetInstance()->EnableProcessing();
@@ -894,14 +898,13 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
options.max_subcompactions = 10;
DestroyAndReopen(options);
std::atomic_uint64_t latest_cold_seq = 0;
std::atomic_uint64_t latest_cold_seq = kMaxSequenceNumber;
std::vector<SequenceNumber> seq_history;
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
auto context = static_cast<PerKeyPlacementContext*>(arg);
context->output_to_penultimate_level =
context->seq_num > latest_cold_seq;
"CompactionJob::PrepareTimes():preclude_last_level_min_seqno",
[&](void* arg) {
*static_cast<SequenceNumber*>(arg) = latest_cold_seq.load();
});
SyncPoint::GetInstance()->EnableProcessing();
@@ -952,6 +955,7 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
VerifyCompactionStats(expect_stats, expect_pl_stats);
// Add new data, which is all hot and overriding all existing data
latest_cold_seq = dbfull()->GetLatestSequenceNumber();
for (int i = 0; i < kNumTrigger; i++) {
for (int j = 0; j < kNumKeys; j++) {
ASSERT_OK(Put(Key(i * 10 + j), "value" + std::to_string(i)));
@@ -1104,7 +1108,13 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
}
TEST_F(TieredCompactionTest, RangeBasedTieredStorageLevel) {
// This test was essentially for a hacked-up version on future functionality.
// It can be resurrected if/when a form of range-based tiering is properly
// implemented.
// FIXME: aside from that, this test reproduces a near-endless compaction
// cycle that needs to be reproduced independently and fixed before
// leveled compaction can be used with the preclude feature in production.
TEST_F(TieredCompactionTest, DISABLED_RangeBasedTieredStorageLevel) {
const int kNumTrigger = 4;
const int kNumLevels = 7;
const int kNumKeys = 100;
@@ -1213,6 +1223,9 @@ TEST_F(TieredCompactionTest, RangeBasedTieredStorageLevel) {
// Tests that we only compact keys up to penultimate level
// that are within penultimate level input's internal key range.
// UPDATE: this functionality has changed. With penultimate-enabled
// compaction, the expanded potential output range in the penultimate
// level is reserved so should be safe to use.
{
MutexLock l(&mutex);
hot_start = Key(0);
@@ -1221,7 +1234,6 @@ TEST_F(TieredCompactionTest, RangeBasedTieredStorageLevel) {
const Snapshot* temp_snap = db_->GetSnapshot();
// Key(0) and Key(1) here are inserted with higher sequence number
// than Key(0) and Key(1) inserted above.
// Only Key(0) in last level will be compacted up, not Key(1).
ASSERT_OK(Put(Key(0), "value" + std::to_string(0)));
ASSERT_OK(Put(Key(1), "value" + std::to_string(100)));
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
@@ -1231,14 +1243,17 @@ TEST_F(TieredCompactionTest, RangeBasedTieredStorageLevel) {
db_->GetLiveFilesMetaData(&metas);
for (const auto& f : metas) {
if (f.temperature == Temperature::kUnknown) {
// Expect Key(0), Key(0), Key(1)
ASSERT_EQ(f.num_entries, 3);
// UPDATED (was 3 entries, Key0..Key1)
ASSERT_EQ(f.num_entries, 52);
ASSERT_EQ(f.smallestkey, Key(0));
ASSERT_EQ(f.largestkey, Key(1));
ASSERT_EQ(f.largestkey, Key(49));
} else {
ASSERT_EQ(f.temperature, Temperature::kCold);
// Key(2)-Key(49) and Key(100).
ASSERT_EQ(f.num_entries, 50);
// UPDATED (was 50 entries, Key0..Key49)
// Key(100) is outside the hot range
ASSERT_EQ(f.num_entries, 1);
ASSERT_EQ(f.smallestkey, Key(100));
ASSERT_EQ(f.largestkey, Key(100));
}
}
}
@@ -1279,8 +1294,6 @@ class PrecludeLastLevelTestBase : public DBTestBase {
const std::unordered_map<std::string, std::string>& db_config_change) {
if (dynamic) {
if (config_change.size() > 0) {
// FIXME: temporary while preserve/preclude options are not user mutable
SaveAndRestore<bool> m(&TEST_allowSetOptionsImmutableInMutable, true);
ASSERT_OK(db_->SetOptions(config_change));
}
if (db_config_change.size() > 0) {
@@ -1616,7 +1629,8 @@ TEST_P(PrecludeLastLevelTest, CheckInternalKeyRange) {
ASSERT_EQ("0,0,0,0,0,2,2", FilesPerLevel());
auto VerifyLogicalState = [&]() {
auto VerifyLogicalState = [&](int line) {
SCOPED_TRACE("Called from line " + std::to_string(line));
// First with snapshot
ASSERT_EQ("val2", Get(Key(2), snapshot));
ASSERT_EQ("val3", Get(Key(3), snapshot));
@@ -1630,7 +1644,7 @@ TEST_P(PrecludeLastLevelTest, CheckInternalKeyRange) {
ASSERT_EQ("val100", Get(Key(100)));
};
VerifyLogicalState();
VerifyLogicalState(__LINE__);
// Try to compact keys up
CompactRangeOptions cro;
@@ -1641,24 +1655,40 @@ TEST_P(PrecludeLastLevelTest, CheckInternalKeyRange) {
// type:1 vs. file #15 smallest key: '6B6579303030303033' seq:104, type:1
ASSERT_OK(CompactRange(cro, Key(1), Key(2)));
VerifyLogicalState();
VerifyLogicalState(__LINE__);
db_->ReleaseSnapshot(snapshot);
Close();
}
TEST_P(PrecludeLastLevelTest, RangeTombstoneSnapshotMigrateFromLast) {
INSTANTIATE_TEST_CASE_P(PrecludeLastLevelTest, PrecludeLastLevelTest,
::testing::Bool());
class PrecludeWithCompactStyleTest : public PrecludeLastLevelTestBase,
public testing::WithParamInterface<bool> {
public:
void ApplyConfigChange(
Options* options,
const std::unordered_map<std::string, std::string>& config_change,
const std::unordered_map<std::string, std::string>& db_config_change =
{}) {
// Depends on dynamic config change while holding a snapshot
ApplyConfigChangeImpl(true /*dynamic*/, options, config_change,
db_config_change);
}
};
TEST_P(PrecludeWithCompactStyleTest, RangeTombstoneSnapshotMigrateFromLast) {
// Reproducer for issue originally described in
// https://github.com/facebook/rocksdb/pull/9964/files#r1024449523
if (!UseDynamicConfig()) {
// Depends on config change while holding a snapshot
return;
}
const bool universal = GetParam();
const int kNumLevels = 7;
auto options = CurrentOptions();
options.env = mock_env_.get();
options.last_level_temperature = Temperature::kCold;
options.level_compaction_dynamic_level_bytes = true;
options.compaction_style =
universal ? kCompactionStyleUniversal : kCompactionStyleLevel;
options.compaction_options_universal.allow_trivial_move = true;
options.num_levels = kNumLevels;
options.statistics = CreateDBStatistics();
options.max_subcompactions = 10;
@@ -1678,7 +1708,13 @@ TEST_P(PrecludeLastLevelTest, RangeTombstoneSnapshotMigrateFromLast) {
ASSERT_OK(db_->DeleteRange({}, db_->DefaultColumnFamily(), Key(1), Key(5)));
ASSERT_OK(Put(Key(1), "val1"));
ASSERT_OK(Flush());
MoveFilesToLevel(6);
// Send to last level
if (universal) {
ASSERT_OK(CompactRange({}, {}, {}));
} else {
MoveFilesToLevel(6);
}
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
ApplyConfigChange(&options, {{"preclude_last_level_data_seconds", "10000"}});
@@ -1697,10 +1733,18 @@ TEST_P(PrecludeLastLevelTest, RangeTombstoneSnapshotMigrateFromLast) {
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(1000)); });
ASSERT_OK(Flush());
MoveFilesToLevel(5);
// Send three files to next-to-last level (if explicitly needed)
if (universal) {
ASSERT_OK(dbfull()->TEST_WaitForCompact());
} else {
MoveFilesToLevel(5);
}
ASSERT_EQ("0,0,0,0,0,3,1", FilesPerLevel());
auto VerifyLogicalState = [&]() {
auto VerifyLogicalState = [&](int line) {
SCOPED_TRACE("Called from line " + std::to_string(line));
// First with snapshot
if (snapshot) {
ASSERT_EQ("NOT_FOUND", Get(Key(0), snapshot));
@@ -1720,24 +1764,31 @@ TEST_P(PrecludeLastLevelTest, RangeTombstoneSnapshotMigrateFromLast) {
ASSERT_EQ("val7", Get(Key(7)));
};
VerifyLogicalState();
VerifyLogicalState(__LINE__);
// Try a limited range compaction
// FIXME: this currently hits the "Unsafe to store Seq later than snapshot"
// error. Needs to work safely for preclude option to be user mutable.
// ASSERT_OK(CompactRange({}, Key(3), Key(4)));
// (These would previously hit "Unsafe to store Seq later than snapshot")
if (universal) {
uint64_t middle_l5 = GetLevelFileMetadatas(5)[1]->fd.GetNumber();
ASSERT_OK(db_->CompactFiles({}, {MakeTableFileName(middle_l5)}, 6));
} else {
ASSERT_OK(CompactRange({}, Key(3), Key(4)));
}
EXPECT_EQ("0,0,0,0,0,3,1", FilesPerLevel());
VerifyLogicalState();
VerifyLogicalState(__LINE__);
// Compact everything, but some data still goes to both penultimate and last
// levels. A full-range compaction should be safe to "migrate" data from the
// last level to penultimate (because of preclude setting change).
ASSERT_OK(CompactRange({}, {}, {}));
EXPECT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
VerifyLogicalState();
VerifyLogicalState(__LINE__);
// Key1 should have been migrated out of the last level
auto& meta = *GetLevelFileMetadatas(6)[0];
ASSERT_LT(Key(1), meta.smallest.user_key().ToString());
// FIXME: doesn't yet work with leveled compaction
if (universal) {
auto& meta = *GetLevelFileMetadatas(6)[0];
ASSERT_LT(Key(1), meta.smallest.user_key().ToString());
}
// Make data eligible for last level
db_->ReleaseSnapshot(snapshot);
@@ -1746,13 +1797,13 @@ TEST_P(PrecludeLastLevelTest, RangeTombstoneSnapshotMigrateFromLast) {
ASSERT_OK(CompactRange({}, {}, {}));
EXPECT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
VerifyLogicalState();
VerifyLogicalState(__LINE__);
Close();
}
INSTANTIATE_TEST_CASE_P(PrecludeLastLevelTest, PrecludeLastLevelTest,
::testing::Bool());
INSTANTIATE_TEST_CASE_P(PrecludeWithCompactStyleTest,
PrecludeWithCompactStyleTest, ::testing::Bool());
class TimedPutPrecludeLastLevelTest
: public PrecludeLastLevelTestBase,
-1
View File
@@ -4,7 +4,6 @@
// (found in the LICENSE.Apache file in the root directory).
//
#include "rocksdb/convenience.h"
#include "db/convenience_impl.h"
+1 -2
View File
@@ -7,8 +7,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "rocksdb/options.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -25,6 +23,7 @@
#include "rocksdb/convenience.h"
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/options.h"
#include "rocksdb/table.h"
#include "rocksdb/utilities/transaction_db.h"
#include "rocksdb/write_batch.h"
-2
View File
@@ -3,7 +3,6 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include "db/db_impl/db_impl.h"
#include "db/db_test_util.h"
#include "rocksdb/db.h"
@@ -349,4 +348,3 @@ int main(int argc, char** argv) {
return 0;
}
}
+92 -3
View File
@@ -878,7 +878,6 @@ TEST_F(DBBasicTest, Snapshot) {
} while (ChangeOptions());
}
class DBBasicMultiConfigs : public DBBasicTest,
public ::testing::WithParamInterface<int> {
public:
@@ -2604,8 +2603,7 @@ class DBMultiGetAsyncIOTest : public DBBasicTest,
// Warm up the block cache so we don't need to use the IO uring
Iterator* iter = dbfull()->NewIterator(ReadOptions());
for (iter->SeekToFirst(); iter->Valid() && iter->status().ok();
iter->Next())
;
iter->Next());
EXPECT_OK(iter->status());
delete iter;
#endif // ROCKSDB_IOURING_PRESENT
@@ -4996,6 +4994,97 @@ TEST_F(DBBasicTest, VerifyFileChecksumsReadahead) {
(sst_size + alignment - 1) / (alignment));
}
TEST_F(DBBasicTest, DisallowMemtableWrite) {
// This test is mostly about what you can't do with memtable writes
// disallowed. For what you can do, see
// ExternalSSTFileBasicTest.FailIfNotBottommostLevelAndDisallowMemtable
Options options_allow = GetDefaultOptions();
options_allow.create_if_missing = true;
Options options_disallow = options_allow;
options_disallow.disallow_memtable_writes = true;
DestroyAndReopen(options_allow);
// CFs allowing and disallowing memtable write
CreateColumnFamilies({"cf1", "cf2"}, options_allow);
CreateColumnFamilies({"cf3"}, options_disallow);
// XXX: needed to get consistent handles_ mappings
ReopenWithColumnFamilies(
{"default", "cf1", "cf2", "cf3"},
{options_allow, options_allow, options_allow, options_disallow});
EXPECT_EQ(Put(0, "a0", "1").code(), Status::Code::kOk);
EXPECT_EQ(Put(1, "a1", "1").code(), Status::Code::kOk);
EXPECT_EQ(Put(2, "a2", "1").code(), Status::Code::kOk);
EXPECT_EQ(Put(3, "a3", "1").code(), Status::Code::kInvalidArgument);
EXPECT_EQ(Get(0, "a0"), "1");
EXPECT_EQ(Get(1, "a1"), "1");
EXPECT_EQ(Get(2, "a2"), "1");
EXPECT_EQ(Get(3, "a3"), "NOT_FOUND");
EXPECT_EQ(Delete(0, "z0").code(), Status::Code::kOk);
EXPECT_EQ(Delete(1, "z1").code(), Status::Code::kOk);
EXPECT_EQ(Delete(2, "z2").code(), Status::Code::kOk);
EXPECT_EQ(Delete(3, "z3").code(), Status::Code::kInvalidArgument);
WriteBatch wb;
EXPECT_EQ(wb.Put(handles_[0], "b0", "2").code(), Status::Code::kOk);
EXPECT_EQ(wb.Put(handles_[1], "b1", "2").code(), Status::Code::kOk);
EXPECT_EQ(wb.Put(handles_[2], "b2", "2").code(), Status::Code::kOk);
EXPECT_EQ(wb.Put(handles_[3], "b3", "2").code(),
Status::Code::kInvalidArgument);
ASSERT_OK(db_->Write({}, &wb));
wb.Clear();
EXPECT_EQ(Get(0, "b0"), "2");
EXPECT_EQ(Get(1, "b1"), "2");
EXPECT_EQ(Get(2, "b2"), "2");
EXPECT_EQ(Get(3, "b3"), "NOT_FOUND");
// When the DB is re-opened with WAL entries for a CF that is newly setting
// disallow_memtable_writes, we detect that and fail the open gracefully.
ASSERT_EQ(TryReopenWithColumnFamilies(
{"default", "cf1", "cf2", "cf3"},
{options_allow, options_allow, options_disallow, options_allow})
.code(),
Status::Code::kInvalidArgument);
// Successfully opening with allow creates L0 files from the WAL
ReopenWithColumnFamilies({"default", "cf1", "cf2", "cf3"}, options_allow);
EXPECT_EQ(Get(0, "a0"), "1");
EXPECT_EQ(Get(1, "a1"), "1");
EXPECT_EQ(Get(2, "a2"), "1");
EXPECT_EQ(Get(3, "a3"), "NOT_FOUND");
// Now able to disallow on CF2 because no relevant WAL entries
ReopenWithColumnFamilies(
{"default", "cf1", "cf2", "cf3"},
{options_allow, options_allow, options_disallow, options_allow});
EXPECT_EQ(Get(0, "a0"), "1");
EXPECT_EQ(Get(1, "a1"), "1");
EXPECT_EQ(Get(2, "a2"), "1");
EXPECT_EQ(Get(3, "a3"), "NOT_FOUND");
// Now able to write to CF 3 but not CF 2
EXPECT_EQ(Put(0, "c0", "3").code(), Status::Code::kOk);
EXPECT_EQ(Put(1, "c1", "3").code(), Status::Code::kOk);
EXPECT_EQ(Put(2, "c2", "3").code(), Status::Code::kInvalidArgument);
EXPECT_EQ(Put(3, "c3", "3").code(), Status::Code::kOk);
EXPECT_EQ(Get(0, "c0"), "3");
EXPECT_EQ(Get(1, "c1"), "3");
EXPECT_EQ(Get(2, "c2"), "NOT_FOUND");
EXPECT_EQ(Get(3, "c3"), "3");
// disallow_memtable_writes not supported on default column family.
// (Would be complicated to make a WriteBatch aware of the setting in order
// to reject the write before entering the write path.)
Destroy(options_allow);
EXPECT_EQ(TryReopen(options_disallow).code(), Status::Code::kInvalidArgument);
}
// TODO: re-enable after we provide finer-grained control for WAL tracking to
// meet the needs of different use cases, durability levels and recovery modes.
TEST_F(DBBasicTest, DISABLED_ManualWalSync) {
+2 -5
View File
@@ -305,7 +305,6 @@ class ReadOnlyCacheWrapper : public CacheWrapper {
} // anonymous namespace
#endif // SNAPPY
// Make sure that when options.block_cache is set, after a new table is
// created its index/filter blocks are added to block cache.
TEST_F(DBBlockCacheTest, IndexAndFilterBlocksOfNewTableAddedToCache) {
@@ -836,8 +835,6 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
}
if (ZSTD_Supported()) {
compression_types.push_back(kZSTD);
} else if (ZSTDNotFinal_Supported()) {
compression_types.push_back(kZSTDNotFinalCompression);
}
Random rnd(301);
for (auto compression_type : compression_types) {
@@ -1758,8 +1755,8 @@ class CacheKeyTest : public testing::Test {
tp_.db_id = std::to_string(db_id_);
tp_.orig_file_number = file_number;
bool is_stable;
std::string cur_session_id; // ignored
uint64_t cur_file_number = 42; // ignored
std::string cur_session_id; // ignored
uint64_t cur_file_number = 42; // ignored
OffsetableCacheKey rv;
BlockBasedTable::SetupBaseCacheKey(&tp_, cur_session_id, cur_file_number,
&rv, &is_stable);
+48
View File
@@ -2244,6 +2244,54 @@ TEST_F(DBBloomFilterTest, MemtableWholeKeyBloomFilterMultiGet) {
db_->ReleaseSnapshot(snapshot);
}
TEST_F(DBBloomFilterTest, TestMemtableBloomAndWBM) {
Options options = CurrentOptions();
options.arena_block_size = 4096;
options.write_buffer_size = 4000000;
std::shared_ptr<Cache> cache = NewLRUCache(LRUCacheOptions(
options.write_buffer_size * 3 /* capacity */, 1 /* num_shard_bits */,
false /* strict_capacity_limit */, 0.0 /* high_pri_pool_ratio */,
nullptr /* memory_allocator */, kDefaultToAdaptiveMutex,
kDontChargeCacheMetadata));
options.write_buffer_manager.reset(
new WriteBufferManager(options.write_buffer_size, cache));
Reopen(options);
ASSERT_OK(Put("foo", "bar"));
const auto kDummyEntrySize =
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
// Just the start of a memtable, no Bloom
ASSERT_GE(cache->GetUsage(), options.arena_block_size);
ASSERT_LE(cache->GetUsage(), kDummyEntrySize);
// Now enable memtable bloom filter
const double kRatio = 0.25;
options.memtable_prefix_bloom_size_ratio = kRatio;
options.memtable_whole_key_filtering = true;
Reopen(options);
ASSERT_OK(Put("foo2", "bar2"));
// Expecting a memtable Bloom of ratio times write_buffer_size, memory tracked
auto bloom_size = static_cast<size_t>(kRatio * options.write_buffer_size);
ASSERT_GE(cache->GetUsage(), bloom_size);
ASSERT_LE(cache->GetUsage(), bloom_size + 2U * kDummyEntrySize);
// Now get another memtable and test
// Pin this memtable with an iterator
std::unique_ptr<Iterator> iter{db_->NewIterator({})};
iter->Seek("foo2");
ASSERT_TRUE(iter->Valid());
ASSERT_OK(Flush());
ASSERT_OK(Put("foo2", "bar2"));
// Expecting twice as much
ASSERT_GE(cache->GetUsage(), 2U * bloom_size);
ASSERT_LE(cache->GetUsage(), 2U * bloom_size + 2U * kDummyEntrySize);
}
namespace {
std::pair<uint64_t, uint64_t> GetBloomStat(const Options& options, bool sst) {
if (sst) {
+84 -16
View File
@@ -5856,6 +5856,26 @@ TEST_F(DBCompactionTest, CompactionStatsTest) {
options.listeners.emplace_back(collector);
DestroyAndReopen(options);
// Verify that the internal statistics for num_running_compactions and
// num_running_compaction_sorted_runs start and end at valid states
uint64_t num_running_compactions = 0;
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningCompactions,
&num_running_compactions));
ASSERT_EQ(num_running_compactions, 0);
uint64_t num_running_compaction_sorted_runs = 0;
ASSERT_TRUE(
db_->GetIntProperty(DB::Properties::kNumRunningCompactionSortedRuns,
&num_running_compaction_sorted_runs));
ASSERT_EQ(num_running_compaction_sorted_runs, 0);
// Check that the stat actually gets changed some time between the start and
// end of compaction
std::atomic<bool> sorted_runs_count_incremented = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionMergingIterator::UpdateInternalStats",
[&](void*) { sorted_runs_count_incremented = true; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 5000; j++) {
ASSERT_OK(Put(std::to_string(j), std::string(1, 'A')));
@@ -5869,6 +5889,19 @@ TEST_F(DBCompactionTest, CompactionStatsTest) {
ColumnFamilyData* cfd = cfh->cfd();
VerifyCompactionStats(*cfd, *collector);
// There should be no more running compactions, and thus no more sorted runs
// to process
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningCompactions,
&num_running_compactions));
ASSERT_EQ(num_running_compactions, 0);
ASSERT_TRUE(
db_->GetIntProperty(DB::Properties::kNumRunningCompactionSortedRuns,
&num_running_compaction_sorted_runs));
ASSERT_EQ(num_running_compaction_sorted_runs, 0);
ASSERT_TRUE(sorted_runs_count_incremented);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBCompactionTest, SubcompactionEvent) {
@@ -6407,20 +6440,40 @@ TEST_F(DBCompactionTest, PersistRoundRobinCompactCursor) {
}
}
TEST_P(RoundRobinSubcompactionsAgainstPressureToken, PressureTokenTest) {
// FIXME: the test is flaky and failing the assertion
// ASSERT_EQ(num_planned_subcompactions, kNumSubcompactions);
// It's likely a test set up issue, fix if we are to use RoubdRobin compaction.
TEST_P(RoundRobinSubcompactionsAgainstPressureToken,
DISABLED_PressureTokenTest) {
const int kKeysPerBuffer = 100;
const int kNumSubcompactions = 2;
const int kFilesPerLevel = 50;
SyncPoint::GetInstance()->LoadDependency({
// SetBackgroundThreads() only starts the bg threads. Background
// threads may not be immediately available after SetBackgroundThreads
// returns. There are some initialization before they start waiting for
// new jobs, see ThreadPoolImpl::Impl::BGThread(). Here is a hacky way
// to wait until there are at least two background threads (thread id
// starts from 0) start waiting to accept jobs.
{"ThreadPoolImpl::BGThread::Start:th1", "WaitForThreadAvailable"},
});
SyncPoint::GetInstance()->EnableProcessing();
env_->SetBackgroundThreads(kNumSubcompactions, Env::LOW);
TEST_SYNC_POINT("WaitForThreadAvailable");
SyncPoint::GetInstance()->DisableProcessing();
Options options = CurrentOptions();
options.num_levels = 3;
options.max_bytes_for_level_multiplier = 2;
options.level0_file_num_compaction_trigger = 4;
options.target_file_size_base = kKeysPerBuffer * 1024;
options.compaction_pri = CompactionPri::kRoundRobin;
// Target size is chosen so that filling the level with `kFilesPerLevel` files
// will make it oversized by `kNumSubcompactions` files.
// Pick a small target level size so that round-robin compaction will pick
// more files to compact and trigger subcompactions. Actual number of
// subcompactions will be limited by number of bg threads available.
// Cannot be too small to cause compaction pressure. See
// GetPendingCompactionBytesForCompactionSpeedup().
options.max_bytes_for_level_base =
(kFilesPerLevel - kNumSubcompactions) * kKeysPerBuffer * 1024;
(kFilesPerLevel - 10) * kKeysPerBuffer * 1024;
options.disable_auto_compactions = true;
// Setup `kNumSubcompactions` threads but limited subcompactions so
// that RoundRobin requires extra compactions from reserved threads
@@ -6428,7 +6481,6 @@ TEST_P(RoundRobinSubcompactionsAgainstPressureToken, PressureTokenTest) {
options.max_background_compactions = kNumSubcompactions;
options.max_compaction_bytes = 100000000;
DestroyAndReopen(options);
env_->SetBackgroundThreads(kNumSubcompactions, Env::LOW);
Random rnd(301);
for (int lvl = 2; lvl > 0; lvl--) {
@@ -6446,21 +6498,34 @@ TEST_P(RoundRobinSubcompactionsAgainstPressureToken, PressureTokenTest) {
ASSERT_EQ(kFilesPerLevel, NumTableFilesAtLevel(lvl, 0));
}
bool compaction_num_input_file_verified = false;
SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
if (!compaction_num_input_file_verified) {
Compaction* compaction = static_cast<Compaction*>(arg);
// In Round-Robin, # of subcompactions needed is the # of input files
ASSERT_GT(compaction->num_input_files(0), 1);
compaction_num_input_file_verified = true;
}
});
// This is a variable for making sure the following callback is called
// and the assertions in it are indeed excuted.
bool num_planned_subcompactions_verified = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::GenSubcompactionBoundaries:0", [&](void* arg) {
uint64_t num_planned_subcompactions = *(static_cast<uint64_t*>(arg));
if (grab_pressure_token_) {
// `kNumSubcompactions` files are selected for round-robin under auto
// compaction. The number of planned subcompaction is restricted by
// the limited number of max_background_compactions
ASSERT_EQ(num_planned_subcompactions, kNumSubcompactions);
} else {
ASSERT_EQ(num_planned_subcompactions, 1);
if (!num_planned_subcompactions_verified) {
uint64_t num_planned_subcompactions = *(static_cast<uint64_t*>(arg));
if (grab_pressure_token_) {
// `kNumSubcompactions` files are selected for round-robin under
// auto compaction. The number of planned subcompaction is
// restricted by the limited number of max_background_compactions
ASSERT_EQ(num_planned_subcompactions, kNumSubcompactions);
} else {
ASSERT_EQ(num_planned_subcompactions, 1);
}
num_planned_subcompactions_verified = true;
}
num_planned_subcompactions_verified = true;
});
// The following 3 dependencies have to be added to ensure the auto
@@ -6911,7 +6976,8 @@ TEST_F(DBCompactionTest, ManualCompactionMax) {
DestroyAndReopen(opts);
generate_sst_func();
uint64_t total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
opts.max_compaction_bytes = total_size / num_split;
// Slightly inflate max_compaction_bytes since it's usually a strict bound.
opts.max_compaction_bytes = total_size / num_split + l2_avg_size * 10;
opts.target_file_size_base = total_size / num_split;
Reopen(opts);
num_compactions.store(0);
@@ -6933,8 +6999,10 @@ TEST_F(DBCompactionTest, ManualCompactionMax) {
DestroyAndReopen(opts);
generate_sst_func();
total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
// Slightly inflate max_compaction_bytes since it's usually a strict bound.
Status s = db_->SetOptions(
{{"max_compaction_bytes", std::to_string(total_size / num_split)},
{{"max_compaction_bytes",
std::to_string(total_size / num_split + 10 * l2_avg_size)},
{"target_file_size_base", std::to_string(total_size / num_split)}});
ASSERT_OK(s);
-1
View File
@@ -491,7 +491,6 @@ TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
::testing::InitGoogleTest(&argc, argv);
+3 -4
View File
@@ -3,12 +3,13 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
//
#include <iostream>
#include <string>
#include "db/db_test_util.h"
#include "port/stack_trace.h"
#include "rocksdb/perf_context.h"
#include "test_util/sync_point.h"
#include <iostream>
#include <string>
namespace ROCKSDB_NAMESPACE {
@@ -25,7 +26,6 @@ class DBEncryptionTest : public DBTestBase {
}
};
TEST_F(DBEncryptionTest, CheckEncrypted) {
ASSERT_OK(Put("foo567", "v1.fetdq"));
ASSERT_OK(Put("bar123", "v2.dfgkjdfghsd"));
@@ -116,7 +116,6 @@ TEST_F(DBEncryptionTest, ReadEmptyFile) {
ASSERT_TRUE(data.empty());
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+1 -2
View File
@@ -4,7 +4,6 @@
// (found in the LICENSE.Apache file in the root directory).
//
#include <algorithm>
#include <cstdint>
#include <memory>
@@ -266,7 +265,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
continue;
}
VersionStorageInfo& vsi = *cfd->current()->storage_info();
auto& cf_paths = cfd->ioptions()->cf_paths;
auto& cf_paths = cfd->ioptions().cf_paths;
auto GetDir = [&](size_t path_id) {
// Matching TableFileName() behavior
+2 -2
View File
@@ -248,7 +248,7 @@ Status CompactedDBImpl::Init(const Options& options) {
}
Status CompactedDBImpl::Open(const Options& options, const std::string& dbname,
DB** dbptr) {
std::unique_ptr<DB>* dbptr) {
*dbptr = nullptr;
if (options.max_open_files != -1) {
@@ -267,7 +267,7 @@ Status CompactedDBImpl::Open(const Options& options, const std::string& dbname,
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
"Opened the db as fully compacted mode");
LogFlush(db->immutable_db_options_.info_log);
*dbptr = db.release();
*dbptr = std::move(db);
}
return s;
}
+1 -1
View File
@@ -22,7 +22,7 @@ class CompactedDBImpl : public DBImpl {
~CompactedDBImpl() override;
static Status Open(const Options& options, const std::string& dbname,
DB** dbptr);
std::unique_ptr<DB>* dbptr);
// Implementations of the DB interface
using DB::Get;
+155 -206
View File
@@ -386,9 +386,8 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
static_cast_with_check<ColumnFamilyHandleImpl>(default_cf_handle_);
assert(cfh);
ColumnFamilyData* cfd = cfh->cfd();
const MutableCFOptions& cf_opts = *cfd->GetLatestMutableCFOptions();
s = versions_->LogAndApply(cfd, cf_opts, read_options, write_options,
&edit, &mutex_, directories_.GetDbDir());
s = versions_->LogAndApply(cfd, read_options, write_options, &edit,
&mutex_, directories_.GetDbDir());
if (!s.ok()) {
io_s = versions_->io_status();
if (!io_s.ok()) {
@@ -418,10 +417,19 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
}
}
JobContext job_context(0);
FindObsoleteFiles(&job_context, true);
mutex_.Unlock();
// If DB shutdown initiated here, it will wait for this ongoing recovery.
job_context.manifest_file_number = 1;
if (job_context.HaveSomethingToDelete()) {
PurgeObsoleteFiles(job_context);
}
job_context.Clean();
mutex_.Lock();
if (s.ok()) {
// This will notify and unblock threads waiting for error recovery to
// finish. Those previouly waiting threads can now proceed, which may
// include closing the db.
// Will notify and unblock threads waiting for error recovery to finish.
s = error_handler_.ClearBGError();
} else {
// NOTE: this is needed to pass ASSERT_STATUS_CHECKED
@@ -430,15 +438,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
error_handler_.GetRecoveryError().PermitUncheckedError();
}
JobContext job_context(0);
FindObsoleteFiles(&job_context, true);
mutex_.Unlock();
job_context.manifest_file_number = 1;
if (job_context.HaveSomethingToDelete()) {
PurgeObsoleteFiles(job_context);
}
job_context.Clean();
if (s.ok()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Successfully resumed DB");
} else {
@@ -446,7 +445,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
s.ToString().c_str());
}
mutex_.Lock();
// Check for shutdown again before scheduling further compactions,
// since we released and re-acquired the lock above
if (shutdown_initiated_) {
@@ -509,6 +507,11 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
s.PermitUncheckedError(); //**TODO: What to do on error?
}
// Cancel awaiting remote compactions
if (immutable_db_options_.compaction_service) {
immutable_db_options_.compaction_service->CancelAwaitingJobs();
}
shutting_down_.store(true, std::memory_order_release);
bg_cv_.SignalAll();
if (!wait) {
@@ -540,8 +543,8 @@ Status DBImpl::CloseHelper() {
// continuing with the shutdown
mutex_.Lock();
shutdown_initiated_ = true;
error_handler_.CancelErrorRecovery();
while (error_handler_.IsRecoveryInProgress()) {
error_handler_.CancelErrorRecoveryForShutDown();
while (!error_handler_.ReadyForShutdown()) {
bg_cv_.Wait();
}
mutex_.Unlock();
@@ -852,7 +855,7 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
InstrumentedMutexLock l(&mutex_);
for (auto cfd : *versions_->GetColumnFamilySet()) {
auto& mopts = *cfd->GetLatestMutableCFOptions();
auto& mopts = cfd->GetLatestMutableCFOptions();
// preserve time is the max of 2 options.
uint64_t preserve_seconds =
std::max(mopts.preserve_internal_time_seconds,
@@ -1167,7 +1170,7 @@ void DBImpl::DumpStats() {
}
auto* table_factory =
cfd->GetCurrentMutableCFOptions()->table_factory.get();
cfd->GetCurrentMutableCFOptions().table_factory.get();
assert(table_factory != nullptr);
// FIXME: need to a shared_ptr if/when block_cache is going to be mutable
Cache* cache =
@@ -1286,28 +1289,64 @@ Status DBImpl::SetOptions(
}
InstrumentedMutexLock ol(&options_mutex_);
MutableCFOptions new_options;
MutableCFOptions new_options_copy; // For logging outside of DB mutex
Status s;
Status persist_options_status;
SuperVersionContext sv_context(/* create_superversion */ true);
{
auto db_options = GetDBOptions();
InstrumentedMutexLock l(&mutex_);
s = cfd->SetOptions(db_options, options_map);
// Manifest writers + Version appenders like flush and compaction use
// LogAndApply, which releases DB mutex to wait for other manifest writers
// and for the manifest write. We need to append a Version for the options
// to take full effect (e.g. compaction scores), but we don't want to
// interleave with other callers of LogAndApply, which could at least
// temporarily roll back option changes. Thus, we use a special call to
// LogAndApply that allows us to
//
// (a) Apply the options update when we know we are the exclusive version
// appender + (fake) manifest writer, and
//
// (b) Append a new Version without manifest write nor DB mutex release
//
// Thus aren't releasing the DB mutex again until the end of this block,
// after installing the new SuperVersion.
auto pre_cb = [&]() -> Status {
Status cb_s = cfd->SetOptions(db_options, options_map);
if (cb_s.ok()) {
new_options_copy = cfd->GetLatestMutableCFOptions();
}
return cb_s;
};
VersionEdit dummy_edit;
dummy_edit.MarkNoManifestWriteDummy();
TEST_SYNC_POINT_CALLBACK("DBImpl::SetOptions:dummy_edit", &dummy_edit);
s = versions_->LogAndApply(
cfd, read_options, write_options, &dummy_edit, &mutex_,
directories_.GetDbDir(), false /*new_descriptor_log=*/,
nullptr /*new_opts*/, {} /*manifest_wcb*/, pre_cb);
if (!versions_->io_status().ok()) {
assert(!s.ok());
error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
}
if (s.ok()) {
new_options = *cfd->GetLatestMutableCFOptions();
// Append new version to recompute compaction score.
VersionEdit dummy_edit;
s = versions_->LogAndApply(cfd, new_options, read_options, write_options,
&dummy_edit, &mutex_, directories_.GetDbDir());
// Trigger possible flush/compactions. This has to be before we persist
// options to file, otherwise there will be a deadlock with writer
// thread.
InstallSuperVersionAndScheduleWork(cfd, &sv_context, new_options);
InstallSuperVersionAndScheduleWork(cfd, &sv_context);
persist_options_status =
WriteOptionsFile(write_options, true /*db_mutex_already_held*/);
bg_cv_.SignalAll();
#if __cplusplus >= 202002L
assert(new_options_copy == cfd->GetLatestMutableCFOptions());
assert(cfd->GetLatestMutableCFOptions() ==
cfd->GetCurrentMutableCFOptions());
assert(cfd->GetCurrentMutableCFOptions() ==
cfd->current()->GetMutableCFOptions());
#endif
}
}
sv_context.Clean();
@@ -1328,7 +1367,7 @@ Status DBImpl::SetOptions(
if (s.ok()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[%s] SetOptions() succeeded", cfd->GetName().c_str());
new_options.Dump(immutable_db_options_.info_log.get());
new_options_copy.Dump(immutable_db_options_.info_log.get());
if (!persist_options_status.ok()) {
// NOTE: WriteOptionsFile already logs on failure
s = persist_options_status;
@@ -2098,7 +2137,8 @@ InternalIterator* DBImpl::NewInternalIterator(
// Collect iterator for mutable memtable
auto mem_iter = super_version->mem->NewIterator(
read_options, super_version->GetSeqnoToTimeMapping(), arena,
super_version->mutable_cf_options.prefix_extractor.get());
super_version->mutable_cf_options.prefix_extractor.get(),
/*for_flush=*/false);
Status s;
if (!read_options.ignore_range_deletions) {
std::unique_ptr<TruncatedRangeDelIterator> mem_tombstone_iter;
@@ -2109,7 +2149,7 @@ InternalIterator* DBImpl::NewInternalIterator(
} else {
mem_tombstone_iter = std::make_unique<TruncatedRangeDelIterator>(
std::unique_ptr<FragmentedRangeTombstoneIterator>(range_del_iter),
&cfd->ioptions()->internal_comparator, nullptr /* smallest */,
&cfd->ioptions().internal_comparator, nullptr /* smallest */,
nullptr /* largest */);
}
merge_iter_builder.AddPointAndTombstoneIterator(
@@ -2558,6 +2598,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
// Return all merge operands for get_impl_options.key
*get_impl_options.number_of_operands =
static_cast<int>(merge_context.GetNumOperands());
// OK status is returned, some merge operand is found.
assert(*get_impl_options.number_of_operands > 0);
if (*get_impl_options.number_of_operands >
get_impl_options.get_merge_operands_options
->expected_max_number_of_operands) {
@@ -3247,8 +3289,13 @@ Status DBImpl::MultiGetImpl(
s = Status::Aborted();
break;
}
// This could be a long-running operation
ROCKSDB_THREAD_YIELD_HOOK();
bool aborted = ROCKSDB_THREAD_YIELD_CHECK_ABORT();
if (aborted) {
s = Status::Aborted("Query abort.");
break;
}
}
// Post processing (decrement reference counts and record statistics)
@@ -3637,9 +3684,9 @@ Status DBImpl::CreateColumnFamilyImpl(const ReadOptions& read_options,
write_thread_.EnterUnbatched(&w, &mutex_);
// LogAndApply will both write the creation in MANIFEST and create
// ColumnFamilyData object
s = versions_->LogAndApply(nullptr, MutableCFOptions(cf_options),
read_options, write_options, &edit, &mutex_,
directories_.GetDbDir(), false, &cf_options);
s = versions_->LogAndApply(nullptr, read_options, write_options, &edit,
&mutex_, directories_.GetDbDir(), false,
&cf_options);
write_thread_.ExitUnbatched(&w);
}
if (s.ok()) {
@@ -3653,8 +3700,7 @@ Status DBImpl::CreateColumnFamilyImpl(const ReadOptions& read_options,
auto* cfd =
versions_->GetColumnFamilySet()->GetColumnFamily(column_family_name);
assert(cfd != nullptr);
InstallSuperVersionAndScheduleWork(cfd, &sv_context,
*cfd->GetLatestMutableCFOptions());
InstallSuperVersionAndScheduleWork(cfd, &sv_context);
if (!cfd->mem()->IsSnapshotSupported()) {
is_snapshot_supported_ = false;
@@ -3748,13 +3794,12 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
// we drop column family from a single write thread
WriteThread::Writer w;
write_thread_.EnterUnbatched(&w, &mutex_);
s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
read_options, write_options, &edit, &mutex_,
directories_.GetDbDir());
s = versions_->LogAndApply(cfd, read_options, write_options, &edit,
&mutex_, directories_.GetDbDir());
write_thread_.ExitUnbatched(&w);
}
if (s.ok()) {
auto& moptions = *cfd->GetLatestMutableCFOptions();
auto& moptions = cfd->GetLatestMutableCFOptions();
max_total_in_memory_state_ -=
moptions.write_buffer_size * moptions.max_write_buffer_number;
used_preserve_preclude = moptions.preserve_internal_time_seconds > 0 ||
@@ -3802,7 +3847,6 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
ColumnFamilyHandle* column_family, const Slice& key,
std::string* value, std::string* timestamp,
bool* value_found) {
assert(value != nullptr);
assert(read_options.io_activity == Env::IOActivity::kUnknown);
if (value_found != nullptr) {
@@ -3819,7 +3863,9 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
get_impl_options.value_found = value_found;
get_impl_options.timestamp = timestamp;
auto s = GetImpl(roptions, key, get_impl_options);
value->assign(pinnable_val.data(), pinnable_val.size());
if (value_found && *value_found && value) {
value->assign(pinnable_val.data(), pinnable_val.size());
}
// If block_cache is enabled and the index block of the table didn't
// not present in block_cache, the return value will be Status::Incomplete.
@@ -3884,7 +3930,7 @@ Iterator* DBImpl::NewIterator(const ReadOptions& _read_options,
auto iter = new ForwardIterator(this, read_options, cfd, sv,
/* allow_unprepared_value */ true);
result = NewDBIterator(
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
env_, read_options, cfd->ioptions(), sv->mutable_cf_options,
cfd->user_comparator(), iter, sv->current, kMaxSequenceNumber,
sv->mutable_cf_options.max_sequential_skip_in_iterations,
nullptr /* read_callback */, cfh);
@@ -3966,7 +4012,7 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(
// likely that any iterator pointer is close to the iterator it points to so
// that they are likely to be in the same cache line and/or page.
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
env_, read_options, *cfh->cfd()->ioptions(), sv->mutable_cf_options,
env_, read_options, cfh->cfd()->ioptions(), sv->mutable_cf_options,
sv->current, snapshot,
sv->mutable_cf_options.max_sequential_skip_in_iterations,
sv->version_number, read_callback, cfh, expose_blob_index, allow_refresh);
@@ -4101,7 +4147,7 @@ Status DBImpl::NewIterators(
cf_sv_pair.super_version,
/* allow_unprepared_value */ true);
iterators->push_back(
NewDBIterator(env_, read_options, *cf_sv_pair.cfd->ioptions(),
NewDBIterator(env_, read_options, cf_sv_pair.cfd->ioptions(),
cf_sv_pair.super_version->mutable_cf_options,
cf_sv_pair.cfd->user_comparator(), iter,
cf_sv_pair.super_version->current, kMaxSequenceNumber,
@@ -4339,7 +4385,7 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
CfdList cf_scheduled;
if (oldest_snapshot > bottommost_files_mark_threshold_) {
for (auto* cfd : *versions_->GetColumnFamilySet()) {
if (!cfd->ioptions()->allow_ingest_behind) {
if (!cfd->ioptions().allow_ingest_behind) {
cfd->current()->storage_info()->UpdateOldestSnapshot(
oldest_snapshot, /*allow_ingest_behind=*/false);
if (!cfd->current()
@@ -4360,7 +4406,7 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
SequenceNumber new_bottommost_files_mark_threshold = kMaxSequenceNumber;
for (auto* cfd : *versions_->GetColumnFamilySet()) {
if (CfdListContains(cf_scheduled, cfd) ||
cfd->ioptions()->allow_ingest_behind) {
cfd->ioptions().allow_ingest_behind) {
continue;
}
new_bottommost_files_mark_threshold = std::min(
@@ -4871,110 +4917,6 @@ Status DBImpl::GetUpdatesSince(
return wal_manager_.GetUpdatesSince(seq, iter, read_options, versions_.get());
}
Status DBImpl::DeleteFile(std::string name) {
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
const WriteOptions write_options;
uint64_t number;
FileType type;
WalFileType log_type;
if (!ParseFileName(name, &number, &type, &log_type) ||
(type != kTableFile && type != kWalFile)) {
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "DeleteFile %s failed.\n",
name.c_str());
return Status::InvalidArgument("Invalid file name");
}
if (type == kWalFile) {
// Only allow deleting archived log files
if (log_type != kArchivedLogFile) {
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"DeleteFile %s failed - not archived log.\n",
name.c_str());
return Status::NotSupported("Delete only supported for archived logs");
}
Status status = wal_manager_.DeleteFile(name, number);
if (!status.ok()) {
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"DeleteFile %s failed -- %s.\n", name.c_str(),
status.ToString().c_str());
}
return status;
}
Status status;
int level;
FileMetaData* metadata;
ColumnFamilyData* cfd;
VersionEdit edit;
JobContext job_context(next_job_id_.fetch_add(1), true);
{
InstrumentedMutexLock l(&mutex_);
status = versions_->GetMetadataForFile(number, &level, &metadata, &cfd);
if (!status.ok()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"DeleteFile %s failed. File not found\n", name.c_str());
job_context.Clean();
return Status::InvalidArgument("File not found");
}
assert(level < cfd->NumberLevels());
// If the file is being compacted no need to delete.
if (metadata->being_compacted) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"DeleteFile %s Skipped. File about to be compacted\n",
name.c_str());
job_context.Clean();
return Status::OK();
}
// Only the files in the last level can be deleted externally.
// This is to make sure that any deletion tombstones are not
// lost. Check that the level passed is the last level.
auto* vstoreage = cfd->current()->storage_info();
for (int i = level + 1; i < cfd->NumberLevels(); i++) {
if (vstoreage->NumLevelFiles(i) != 0) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"DeleteFile %s FAILED. File not in last level\n",
name.c_str());
job_context.Clean();
return Status::InvalidArgument("File not in last level");
}
}
// if level == 0, it has to be the oldest file
if (level == 0 &&
vstoreage->LevelFiles(0).back()->fd.GetNumber() != number) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"DeleteFile %s failed ---"
" target file in level 0 must be the oldest.",
name.c_str());
job_context.Clean();
return Status::InvalidArgument("File in level 0, but not oldest");
}
edit.SetColumnFamily(cfd->GetID());
edit.DeleteFile(level, number);
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
read_options, write_options, &edit, &mutex_,
directories_.GetDbDir());
if (status.ok()) {
InstallSuperVersionAndScheduleWork(
cfd, job_context.superversion_contexts.data(),
*cfd->GetLatestMutableCFOptions());
}
FindObsoleteFiles(&job_context, false);
} // lock released here
LogFlush(immutable_db_options_.info_log);
// remove files outside the db-lock
if (job_context.HaveSomethingToDelete()) {
// Call PurgeObsoleteFiles() without holding mutex.
PurgeObsoleteFiles(job_context);
}
job_context.Clean();
return status;
}
Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
const RangePtr* ranges, size_t n,
bool include_end) {
@@ -5059,21 +5001,19 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
}
}
if (!deleted_files.empty()) {
vstorage->ComputeCompactionScore(*cfd->ioptions(),
*cfd->GetLatestMutableCFOptions());
vstorage->ComputeCompactionScore(cfd->ioptions(),
cfd->GetLatestMutableCFOptions());
}
if (edit.GetDeletedFiles().empty()) {
job_context.Clean();
return status;
}
input_version->Ref();
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
read_options, write_options, &edit, &mutex_,
directories_.GetDbDir());
status = versions_->LogAndApply(cfd, read_options, write_options, &edit,
&mutex_, directories_.GetDbDir());
if (status.ok()) {
InstallSuperVersionAndScheduleWork(
cfd, job_context.superversion_contexts.data(),
*cfd->GetLatestMutableCFOptions());
cfd, job_context.superversion_contexts.data());
}
for (auto* deleted_file : deleted_files) {
deleted_file->being_compacted = false;
@@ -5672,7 +5612,7 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name,
void DBImpl::NewThreadStatusCfInfo(ColumnFamilyData* cfd) const {
if (immutable_db_options_.enable_thread_tracking) {
ThreadStatusUtil::NewColumnFamilyInfo(this, cfd, cfd->GetName(),
cfd->ioptions()->env);
cfd->ioptions().env);
}
}
@@ -5890,6 +5830,7 @@ Status DBImpl::IngestExternalFile(
Status DBImpl::IngestExternalFiles(
const std::vector<IngestExternalFileArg>& args) {
PERF_TIMER_GUARD(file_ingestion_nanos);
// TODO: plumb Env::IOActivity, Env::IOPriority
const WriteOptions write_options;
@@ -5912,8 +5853,8 @@ Status DBImpl::IngestExternalFiles(
const size_t num_cfs = args.size();
for (size_t i = 0; i != num_cfs; ++i) {
if (args[i].external_files.empty()) {
char err_msg[128] = {0};
snprintf(err_msg, 128, "external_files[%zu] is empty", i);
std::string err_msg =
"external_files[" + std::to_string(i) + "] is empty";
return Status::InvalidArgument(err_msg);
}
if (i && args[i].options.fill_cache != args[i - 1].options.fill_cache) {
@@ -6034,6 +5975,7 @@ Status DBImpl::IngestExternalFiles(
if (two_write_queues_) {
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
}
PERF_TIMER_GUARD(file_ingestion_blocking_live_writes_nanos);
// When unordered_write is enabled, the keys are writing to memtable in an
// unordered way. If the ingestion job checks memtable key range before the
@@ -6044,6 +5986,7 @@ Status DBImpl::IngestExternalFiles(
num_running_ingest_file_ += static_cast<int>(num_cfs);
TEST_SYNC_POINT("DBImpl::IngestExternalFile:AfterIncIngestFileCounter");
TEST_SYNC_POINT("DBImpl::IngestExternalFile:AfterIncIngestFileCounter:2");
bool at_least_one_cf_need_flush = false;
std::vector<bool> need_flush(num_cfs, false);
@@ -6114,14 +6057,12 @@ Status DBImpl::IngestExternalFiles(
ReadOptions read_options;
read_options.fill_cache = args[0].options.fill_cache;
autovector<ColumnFamilyData*> cfds_to_commit;
autovector<const MutableCFOptions*> mutable_cf_options_list;
autovector<autovector<VersionEdit*>> edit_lists;
uint32_t num_entries = 0;
for (size_t i = 0; i != num_cfs; ++i) {
auto* cfd = ingestion_jobs[i].GetColumnFamilyData();
assert(!cfd->IsDropped());
cfds_to_commit.push_back(cfd);
mutable_cf_options_list.push_back(cfd->GetLatestMutableCFOptions());
autovector<VersionEdit*> edit_list;
edit_list.push_back(ingestion_jobs[i].edit());
edit_lists.push_back(edit_list);
@@ -6136,10 +6077,10 @@ Status DBImpl::IngestExternalFiles(
}
assert(0 == num_entries);
}
status = versions_->LogAndApply(
cfds_to_commit, mutable_cf_options_list, read_options, write_options,
status =
versions_->LogAndApply(cfds_to_commit, read_options, write_options,
edit_lists, &mutex_, directories_.GetDbDir());
edit_lists, &mutex_, directories_.GetDbDir());
// It is safe to update VersionSet last seqno here after LogAndApply since
// LogAndApply persists last sequence number from VersionEdits,
// which are from file's largest seqno and not from VersionSet.
@@ -6171,8 +6112,7 @@ Status DBImpl::IngestExternalFiles(
for (size_t i = 0; i != num_cfs; ++i) {
auto* cfd = ingestion_jobs[i].GetColumnFamilyData();
assert(!cfd->IsDropped());
InstallSuperVersionAndScheduleWork(cfd, &sv_ctxs[i],
*cfd->GetLatestMutableCFOptions());
InstallSuperVersionAndScheduleWork(cfd, &sv_ctxs[i]);
#ifndef NDEBUG
if (0 == i && num_cfs > 1) {
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:InstallSVForFirstCF:0");
@@ -6196,6 +6136,7 @@ Status DBImpl::IngestExternalFiles(
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
}
write_thread_.ExitUnbatched(&w);
PERF_TIMER_STOP(file_ingestion_blocking_live_writes_nanos);
if (status.ok()) {
for (auto& job : ingestion_jobs) {
@@ -6290,12 +6231,11 @@ Status DBImpl::CreateColumnFamilyWithImport(
// and this will overwrite the external file. To protect the external
// file, we have to make sure the file number will never being reused.
next_file_number = versions_->FetchAddFileNumber(total_file_num);
auto cf_options = cfd->GetLatestMutableCFOptions();
status =
versions_->LogAndApply(cfd, *cf_options, read_options, write_options,
&dummy_edit, &mutex_, directories_.GetDbDir());
versions_->LogAndApply(cfd, read_options, write_options, &dummy_edit,
&mutex_, directories_.GetDbDir());
if (status.ok()) {
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx, *cf_options);
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx);
}
}
}
@@ -6328,12 +6268,11 @@ Status DBImpl::CreateColumnFamilyWithImport(
// Install job edit [Mutex will be unlocked here]
if (status.ok()) {
auto cf_options = cfd->GetLatestMutableCFOptions();
status = versions_->LogAndApply(cfd, *cf_options, read_options,
write_options, import_job.edit(),
&mutex_, directories_.GetDbDir());
status = versions_->LogAndApply(cfd, read_options, write_options,
import_job.edit(), &mutex_,
directories_.GetDbDir());
if (status.ok()) {
InstallSuperVersionAndScheduleWork(cfd, &sv_context, *cf_options);
InstallSuperVersionAndScheduleWork(cfd, &sv_context);
}
}
@@ -6534,7 +6473,7 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
const auto& fd = fd_with_krange.fd;
const FileMetaData* fmeta = fd_with_krange.file_metadata;
assert(fmeta);
std::string fname = TableFileName(cfd->ioptions()->cf_paths,
std::string fname = TableFileName(cfd->ioptions().cf_paths,
fd.GetNumber(), fd.GetPathId());
if (use_file_checksum) {
s = VerifyFullFileChecksum(fmeta->file_checksum,
@@ -6558,7 +6497,7 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
const uint64_t blob_file_number = meta->GetBlobFileNumber();
const std::string blob_file_name = BlobFileName(
cfd->ioptions()->cf_paths.front().path, blob_file_number);
cfd->ioptions().cf_paths.front().path, blob_file_number);
s = VerifyFullFileChecksum(meta->GetChecksumValue(),
meta->GetChecksumMethod(), blob_file_name,
read_options);
@@ -6751,16 +6690,15 @@ Status DBImpl::ReserveFileNumbersBeforeIngestion(
pending_output_elem.reset(new std::list<uint64_t>::iterator(
CaptureCurrentFileNumberInPendingOutputs()));
*next_file_number = versions_->FetchAddFileNumber(static_cast<uint64_t>(num));
auto cf_options = cfd->GetLatestMutableCFOptions();
VersionEdit dummy_edit;
// If crash happen after a hard link established, Recover function may
// reuse the file number that has already assigned to the internal file,
// and this will overwrite the external file. To protect the external
// file, we have to make sure the file number will never being reused.
s = versions_->LogAndApply(cfd, *cf_options, read_options, write_options,
&dummy_edit, &mutex_, directories_.GetDbDir());
s = versions_->LogAndApply(cfd, read_options, write_options, &dummy_edit,
&mutex_, directories_.GetDbDir());
if (s.ok()) {
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx, *cf_options);
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx);
}
dummy_sv_ctx.Clean();
return s;
@@ -6799,17 +6737,24 @@ void DBImpl::RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds) {
// for SeqnoToTimeMapping. We don't know how long it has been since the last
// sequence number was written, so we at least have a one-sided bound by
// sampling in this order.
SequenceNumber seqno = GetLatestSequenceNumber();
int64_t unix_time_signed = 0;
immutable_db_options_.clock->GetCurrentTime(&unix_time_signed)
.PermitUncheckedError(); // Ignore error
uint64_t unix_time = static_cast<uint64_t>(unix_time_signed);
// ALSO, to avoid out-of-order mappings, we need to get the seqno and times
// while holding the DB mutex. (This is really to make testing happy because
// it's fine to throw out extra close-but-not-quite-consistent mappings in
// production.)
std::vector<SuperVersionContext> sv_contexts;
if (populate_historical_seconds > 0) {
bool success = true;
{
InstrumentedMutexLock l(&mutex_);
bool success = true;
SequenceNumber seqno;
uint64_t unix_time;
{
InstrumentedMutexLock l(&mutex_);
seqno = GetLatestSequenceNumber();
int64_t unix_time_signed = 0;
immutable_db_options_.clock->GetCurrentTime(&unix_time_signed)
.PermitUncheckedError(); // Ignore error
unix_time = static_cast<uint64_t>(unix_time_signed);
if (populate_historical_seconds > 0) {
if (seqno > 1 && unix_time > populate_historical_seconds) {
// seqno=0 is reserved
SequenceNumber from_seqno = 1;
@@ -6823,7 +6768,20 @@ void DBImpl::RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds) {
assert(unix_time > populate_historical_seconds);
success = false;
}
} else {
// FIXME: assert(seqno > 0);
// Always successful assuming seqno never go backwards
seqno_to_time_mapping_.Append(seqno, unix_time);
InstallSeqnoToTimeMappingInSV(&sv_contexts);
}
}
// clean up & report outside db mutex
for (SuperVersionContext& sv_context : sv_contexts) {
sv_context.Clean();
}
if (populate_historical_seconds > 0) {
if (success) {
ROCKS_LOG_INFO(
immutable_db_options_.info_log,
@@ -6838,16 +6796,7 @@ void DBImpl::RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds) {
seqno, unix_time - populate_historical_seconds, unix_time);
}
} else {
InstrumentedMutexLock l(&mutex_);
// FIXME: assert(seqno > 0);
// Always successful assuming seqno never go backwards
seqno_to_time_mapping_.Append(seqno, unix_time);
InstallSeqnoToTimeMappingInSV(&sv_contexts);
}
// clean up outside db mutex
for (SuperVersionContext& sv_context : sv_contexts) {
sv_context.Clean();
assert(success);
}
}
@@ -6920,7 +6869,7 @@ void DBImpl::InstallSeqnoToTimeMappingInSV(
sv_contexts->emplace_back(/*create_superversion=*/true);
sv_contexts->back().new_seqno_to_time_mapping = new_seqno_to_time_mapping;
cfd->InstallSuperVersion(&sv_contexts->back(),
*(cfd->GetLatestMutableCFOptions()));
cfd->GetLatestMutableCFOptions());
}
bg_cv_.SignalAll();
}
+161 -21
View File
@@ -48,6 +48,7 @@
#include "db/write_controller.h"
#include "db/write_thread.h"
#include "logging/event_logger.h"
#include "memtable/wbwi_memtable.h"
#include "monitoring/instrumented_mutex.h"
#include "options/db_options.h"
#include "port/port.h"
@@ -60,6 +61,7 @@
#include "rocksdb/transaction_log.h"
#include "rocksdb/user_write_callback.h"
#include "rocksdb/utilities/replayer.h"
#include "rocksdb/utilities/write_batch_with_index.h"
#include "rocksdb/write_buffer_manager.h"
#include "table/merging_iterator.h"
#include "util/autovector.h"
@@ -160,6 +162,23 @@ class Directories {
std::unique_ptr<FSDirectory> wal_dir_;
};
struct DBOpenLogRecordReadReporter : public log::Reader::Reporter {
Env* env;
Logger* info_log;
const char* fname;
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
bool* old_log_record;
void Corruption(size_t bytes, const Status& s,
uint64_t log_number = kMaxSequenceNumber) override;
void OldLogRecord(size_t bytes) override;
uint64_t GetCorruptedLogNumber() const { return corrupted_log_number_; }
private:
uint64_t corrupted_log_number_ = kMaxSequenceNumber;
};
// While DB is the public interface of RocksDB, and DBImpl is the actual
// class implementing it. It's the entrance of the core RocksdB engine.
// All other DB implementations, e.g. TransactionDB, BlobDB, etc, wrap a
@@ -522,7 +541,6 @@ class DBImpl : public DB {
SequenceNumber seq_number, std::unique_ptr<TransactionLogIterator>* iter,
const TransactionLogIterator::ReadOptions& read_options =
TransactionLogIterator::ReadOptions()) override;
Status DeleteFile(std::string name) override;
Status DeleteFilesInRanges(ColumnFamilyHandle* column_family,
const RangePtr* ranges, size_t n,
bool include_end = true);
@@ -937,13 +955,6 @@ class DBImpl : public DB {
return num_running_flushes_;
}
// Returns the number of currently running compactions.
// REQUIREMENT: mutex_ must be held when calling this function.
int num_running_compactions() {
mutex_.AssertHeld();
return num_running_compactions_;
}
const WriteController& write_controller() { return write_controller_; }
// hollow transactions shell used for recovery.
@@ -1079,9 +1090,10 @@ class DBImpl : public DB {
// This is to be used only by internal rocksdb classes.
static Status Open(const DBOptions& db_options, const std::string& name,
const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
const bool seq_per_batch, const bool batch_per_txn,
const bool is_retry, bool* can_retry);
std::vector<ColumnFamilyHandle*>* handles,
std::unique_ptr<DB>* dbptr, const bool seq_per_batch,
const bool batch_per_txn, const bool is_retry,
bool* can_retry);
static IOStatus CreateAndNewDirectory(
FileSystem* fs, const std::string& dirname,
@@ -1438,7 +1450,6 @@ class DBImpl : public DB {
uint32_t size = static_cast<uint32_t>(map_.size());
map_.emplace(cfd->GetID(), size);
cfds_.emplace_back(cfd);
mutable_cf_opts_.emplace_back(cfd->GetLatestMutableCFOptions());
edit_lists_.emplace_back(autovector<VersionEdit*>());
}
uint32_t i = map_[cfd->GetID()];
@@ -1447,7 +1458,6 @@ class DBImpl : public DB {
std::unordered_map<uint32_t, uint32_t> map_; // cf_id to index;
autovector<ColumnFamilyData*> cfds_;
autovector<const MutableCFOptions*> mutable_cf_opts_;
autovector<autovector<VersionEdit*>> edit_lists_;
// All existing data files (SST files and Blob files) found during DB::Open.
std::vector<std::string> existing_data_files_;
@@ -1507,6 +1517,23 @@ class DBImpl : public DB {
void EraseThreadStatusDbInfo() const;
// For CFs that has updates in `wbwi`, their memtable will be switched,
// and `wbwi` will be added as the latest immutable memtable.
//
// REQUIRES: this thread is currently at the front of the main writer queue.
// @param prep_log refers to the WAL that contains prepare record
// for the transaction based on wbwi.
// @param assigned_seqno Sequence numbers for the ingested memtable.
// @param last_seqno the value of versions_->LastSequence() after the write
// ingests `wbwi` is done.
// @param memtable_updated Whether the same write that ingests wbwi has
// updated memtable. This is useful for determining whether to set bg
// error when IngestWBWI fails.
Status IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
const WBWIMemTable::SeqnoRange& assigned_seqno,
uint64_t min_prep_log, SequenceNumber last_seqno,
bool memtable_updated, bool ignore_missing_cf);
// If disable_memtable is set the application logic must guarantee that the
// batch will still be skipped from memtable during the recovery. An excption
// to this is seq_per_batch_ mode, in which since each batch already takes one
@@ -1522,6 +1549,16 @@ class DBImpl : public DB {
// batch_cnt is expected to be non-zero in seq_per_batch mode and
// indicates the number of sub-patches. A sub-patch is a subset of the write
// batch that does not have duplicate keys.
// `callback` is called before WAL write.
// See more in comment above WriteCallback::Callback().
// pre_release_callback is called after WAL write and before memtable write.
// See more in comment above PreReleaseCallback::Callback().
// post_memtable_callback is called after memtable write but before publishing
// the sequence number to readers.
//
// The main write queue. This is the only write queue that updates
// LastSequence. When using one write queue, the same sequence also indicates
// the last published sequence.
Status WriteImpl(const WriteOptions& options, WriteBatch* updates,
WriteCallback* callback = nullptr,
UserWriteCallback* user_write_cb = nullptr,
@@ -1529,7 +1566,9 @@ class DBImpl : public DB {
bool disable_memtable = false, uint64_t* seq_used = nullptr,
size_t batch_cnt = 0,
PreReleaseCallback* pre_release_callback = nullptr,
PostMemTableCallback* post_memtable_callback = nullptr);
PostMemTableCallback* post_memtable_callback = nullptr,
std::shared_ptr<WriteBatchWithIndex> wbwi = nullptr,
uint64_t min_prep_log = 0);
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
WriteCallback* callback = nullptr,
@@ -1879,8 +1918,8 @@ class DBImpl : public DB {
const InternalKey* begin = nullptr; // nullptr means beginning of key range
const InternalKey* end = nullptr; // nullptr means end of key range
InternalKey* manual_end = nullptr; // how far we are compacting
InternalKey tmp_storage; // Used to keep track of compaction progress
InternalKey tmp_storage1; // Used to keep track of compaction progress
InternalKey tmp_storage; // Used to keep track of compaction progress
InternalKey tmp_storage1; // Used to keep track of compaction progress
// When the user provides a canceled pointer in CompactRangeOptions, the
// above varaibe is the reference of the user-provided
@@ -2012,6 +2051,94 @@ class DBImpl : public DB {
bool is_retry, bool* corrupted_log_found,
RecoveryContext* recovery_ctx);
void SetupLogFilesRecovery(
const std::vector<uint64_t>& wal_numbers,
std::unordered_map<int, VersionEdit>* version_edits, int* job_id,
uint64_t* min_wal_number);
Status ProcessLogFiles(const std::vector<uint64_t>& wal_numbers,
bool read_only, bool is_retry, uint64_t min_wal_number,
int job_id, SequenceNumber* next_sequence,
std::unordered_map<int, VersionEdit>* version_edits,
bool* corrupted_wal_found,
RecoveryContext* recovery_ctx);
Status ProcessLogFile(
uint64_t wal_number, uint64_t min_wal_number, bool is_retry,
bool read_only, int job_id, SequenceNumber* next_sequence,
bool* stop_replay_for_corruption, bool* stop_replay_by_wal_filter,
uint64_t* corrupted_wal_number, bool* corrupted_wal_found,
std::unordered_map<int, VersionEdit>* version_edits, bool* flushed,
PredecessorWALInfo& predecessor_wal_info);
void SetupLogFileProcessing(uint64_t wal_number);
Status InitializeLogReader(uint64_t wal_number, bool is_retry,
std::string& fname,
bool stop_replay_for_corruption,
uint64_t min_wal_number,
const PredecessorWALInfo& predecessor_wal_info,
bool* const old_log_record,
Status* const reporter_status,
DBOpenLogRecordReadReporter* reporter,
std::unique_ptr<log::Reader>& reader);
Status ProcessLogRecord(
Slice record, const std::unique_ptr<log::Reader>& reader,
const UnorderedMap<uint32_t, size_t>& running_ts_sz, uint64_t wal_number,
const std::string& fname, bool read_only, int job_id,
const std::function<void()>& logFileDropped,
DBOpenLogRecordReadReporter* reporter, uint64_t* record_checksum,
SequenceNumber* last_seqno_observed, SequenceNumber* next_sequence,
bool* stop_replay_for_corruption, Status* status,
bool* stop_replay_by_wal_filter,
std::unordered_map<int, VersionEdit>* version_edits, bool* flushed);
Status InitializeWriteBatchForLogRecord(
Slice record, const std::unique_ptr<log::Reader>& reader,
const UnorderedMap<uint32_t, size_t>& running_ts_sz, WriteBatch* batch,
std::unique_ptr<WriteBatch>& new_batch, WriteBatch*& batch_to_use,
uint64_t* record_checksum);
void MaybeReviseStopReplayForCorruption(
SequenceNumber sequence, SequenceNumber const* const next_sequence,
bool* stop_replay_for_corruption);
Status InsertLogRecordToMemtable(WriteBatch* batch_to_use,
uint64_t wal_number,
SequenceNumber* next_sequence,
bool* has_valid_writes);
Status MaybeWriteLevel0TableForRecovery(
bool has_valid_writes, bool read_only, uint64_t wal_number, int job_id,
SequenceNumber const* const next_sequence,
std::unordered_map<int, VersionEdit>* version_edits, bool* flushed);
Status HandleNonOkStatusOrOldLogRecord(
uint64_t wal_number, SequenceNumber const* const next_sequence,
Status status, const DBOpenLogRecordReadReporter& reporter,
bool* old_log_record, bool* stop_replay_for_corruption,
uint64_t* corrupted_wal_number, bool* corrupted_wal_found);
Status UpdatePredecessorWALInfo(uint64_t wal_number,
const SequenceNumber last_seqno_observed,
const std::string& fname,
PredecessorWALInfo& predecessor_wal_info);
void FinishLogFileProcessing(const Status& status,
const SequenceNumber* next_sequence);
// Return `Status::Corruption()` when `stop_replay_for_corruption == true` and
// exits inconsistency between SST and WAL data
Status MaybeHandleStopReplayForCorruptionForInconsistency(
bool stop_replay_for_corruption, uint64_t corrupted_wal_number);
Status MaybeFlushFinalMemtableOrRestoreActiveLogFiles(
const std::vector<uint64_t>& wal_numbers, bool read_only, int job_id,
bool flushed, std::unordered_map<int, VersionEdit>* version_edits,
RecoveryContext* recovery_ctx);
void FinishLogFilesRecovery(int job_id, const Status& status);
// The following two methods are used to flush a memtable to
// storage. The first one is used at database RecoveryTime (when the
// database is opened) and is heavyweight because it holds the mutex
@@ -2053,7 +2180,19 @@ class DBImpl : public DB {
// Switches the current live memtable to immutable/read-only memtable.
// A new WAL is created if the current WAL is not empty.
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context);
// If `new_imm` is not nullptr, it will be added as the newest immutable
// memtable, if and only if OK status is returned.
// `last_seqno` needs to be provided if `new_imm` is not nullptr. It is
// the value of versions_->LastSequence() after the write that ingests new_imm
// is done.
//
// REQUIRES: mutex_ is held
// REQUIRES: this thread is currently at the front of the writer queue
// REQUIRES: this thread is currently at the front of the 2nd writer queue if
// two_write_queues_ is true (This is to simplify the reasoning.)
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
ReadOnlyMemTable* new_imm = nullptr,
SequenceNumber last_seqno = 0);
// Select and output column families qualified for atomic flush in
// `selected_cfds`. If `provided_candidate_cfds` is non-empty, it will be used
@@ -2179,7 +2318,8 @@ class DBImpl : public DB {
const WriteOptions& write_options,
log::Writer* log_writer, uint64_t* log_used,
uint64_t* log_size,
LogFileNumberSize& log_file_number_size);
LogFileNumberSize& log_file_number_size,
SequenceNumber sequence);
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
log::Writer* log_writer, uint64_t* log_used,
@@ -2383,9 +2523,8 @@ class DBImpl : public DB {
// All ColumnFamily state changes go through this function. Here we analyze
// the new state and we schedule background work if we detect that the new
// state needs flush or compaction.
void InstallSuperVersionAndScheduleWork(
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
const MutableCFOptions& mutable_cf_options);
void InstallSuperVersionAndScheduleWork(ColumnFamilyData* cfd,
SuperVersionContext* sv_context);
bool GetIntPropertyInternal(ColumnFamilyData* cfd,
const DBPropertyInfo& property_info,
@@ -2424,6 +2563,7 @@ class DBImpl : public DB {
IOStatus CreateWAL(const WriteOptions& write_options, uint64_t log_file_num,
uint64_t recycle_log_number, size_t preallocate_block_size,
const PredecessorWALInfo& predecessor_wal_info,
log::Writer** new_log);
// Validate self-consistency of DB options
+95 -107
View File
@@ -66,7 +66,7 @@ bool DBImpl::RequestCompactionToken(ColumnFamilyData* cfd, bool force,
LogBuffer* log_buffer) {
assert(*token == nullptr);
auto limiter = static_cast<ConcurrentTaskLimiterImpl*>(
cfd->ioptions()->compaction_thread_limiter.get());
cfd->ioptions().compaction_thread_limiter.get());
if (limiter == nullptr) {
return true;
}
@@ -99,7 +99,7 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
// Check if holding on the flush will cause entering write stall mode.
// Write stall entered because of the accumulation of write buffers can be
// alleviated if we continue with the flush instead of postponing it.
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
// Use the same criteria as WaitUntilFlushWouldNotStallWrites does w.r.t
// defining what a write stall is about to happen means. If this uses a
@@ -116,7 +116,7 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
/*num_l0_files=*/0,
/*num_compaction_needed_bytes=*/0, mutable_cf_options,
*cfd->ioptions())
cfd->ioptions())
.first;
if (write_stall != WriteStallCondition::kNormal) {
return false;
@@ -213,7 +213,7 @@ Status DBImpl::FlushMemTableToOutputFile(
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
job_context, flush_reason, log_buffer, directories_.GetDbDir(),
GetDataDir(cfd, 0U),
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
GetCompressionFlush(cfd->ioptions(), mutable_cf_options), stats_,
&event_logger_, mutable_cf_options.report_bg_io_stats,
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
io_tracer_, cfd->GetSuperVersion()->ShareSeqnoToTimeMapping(), db_id_,
@@ -299,8 +299,7 @@ Status DBImpl::FlushMemTableToOutputFile(
}
if (s.ok()) {
InstallSuperVersionAndScheduleWork(cfd, superversion_context,
mutable_cf_options);
InstallSuperVersionAndScheduleWork(cfd, superversion_context);
if (made_progress) {
*made_progress = true;
}
@@ -369,7 +368,7 @@ Status DBImpl::FlushMemTableToOutputFile(
if (sfm) {
// Notify sst_file_manager that a new file was added
std::string file_path = MakeTableFileName(
cfd->ioptions()->cf_paths[0].path, file_meta.fd.GetNumber());
cfd->ioptions().cf_paths[0].path, file_meta.fd.GetNumber());
// TODO (PR7798). We should only add the file to the FileManager if it
// exists. Otherwise, some tests may fail. Ignore the error in the
// interim.
@@ -404,7 +403,8 @@ Status DBImpl::FlushMemTablesToOutputFiles(
const auto& bg_flush_arg = bg_flush_args[0];
ColumnFamilyData* cfd = bg_flush_arg.cfd_;
// intentional infrequent copy for each flush
MutableCFOptions mutable_cf_options_copy = *cfd->GetLatestMutableCFOptions();
const MutableCFOptions mutable_cf_options_copy =
cfd->GetLatestMutableCFOptions();
SuperVersionContext* superversion_context =
bg_flush_arg.superversion_context_;
FlushReason flush_reason = bg_flush_arg.flush_reason_;
@@ -461,7 +461,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
for (int i = 0; i < num_cfs; ++i) {
auto cfd = cfds[i];
FSDirectory* data_dir = GetDataDir(cfd, 0U);
const std::string& curr_path = cfd->ioptions()->cf_paths[0].path;
const std::string& curr_path = cfd->ioptions().cf_paths[0].path;
// Add to distinct output directories if eligible. Use linear search. Since
// the number of elements in the vector is not large, performance should be
@@ -478,7 +478,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
distinct_output_dirs.emplace_back(data_dir);
}
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
all_mutable_cf_options.emplace_back(cfd->GetLatestMutableCFOptions());
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
uint64_t max_memtable_id = bg_flush_args[i].max_memtable_id_;
FlushReason flush_reason = bg_flush_args[i].flush_reason_;
@@ -488,7 +488,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
snapshot_checker, job_context, flush_reason, log_buffer,
directories_.GetDbDir(), data_dir,
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
GetCompressionFlush(cfd->ioptions(), mutable_cf_options), stats_,
&event_logger_, mutable_cf_options.report_bg_io_stats,
false /* sync_output_directory */, false /* write_manifest */,
thread_pri, io_tracer_,
@@ -754,7 +754,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
if (s.ok()) {
autovector<ColumnFamilyData*> tmp_cfds;
autovector<const autovector<ReadOnlyMemTable*>*> mems_list;
autovector<const MutableCFOptions*> mutable_cf_options_list;
autovector<FileMetaData*> tmp_file_meta;
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
committed_flush_jobs_info;
@@ -763,7 +762,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
if (!cfds[i]->IsDropped() && !mems.empty()) {
tmp_cfds.emplace_back(cfds[i]);
mems_list.emplace_back(&mems);
mutable_cf_options_list.emplace_back(&all_mutable_cf_options[i]);
tmp_file_meta.emplace_back(&file_meta[i]);
committed_flush_jobs_info.emplace_back(
jobs[i]->GetCommittedFlushJobsInfo());
@@ -771,8 +769,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
}
s = InstallMemtableAtomicFlushResults(
nullptr /* imm_lists */, tmp_cfds, mutable_cf_options_list, mems_list,
versions_.get(), &logs_with_prep_tracker_, &mutex_, tmp_file_meta,
nullptr /* imm_lists */, tmp_cfds, mems_list, versions_.get(),
&logs_with_prep_tracker_, &mutex_, tmp_file_meta,
committed_flush_jobs_info, &job_context->memtables_to_free,
directories_.GetDbDir(), log_buffer);
}
@@ -786,9 +784,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
if (cfds[i]->IsDropped()) {
continue;
}
InstallSuperVersionAndScheduleWork(cfds[i],
&job_context->superversion_contexts[i],
all_mutable_cf_options[i]);
InstallSuperVersionAndScheduleWork(
cfds[i], &job_context->superversion_contexts[i]);
const std::string& column_family_name = cfds[i]->GetName();
@@ -834,7 +831,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
jobs[i]->GetCommittedFlushJobsInfo());
if (sfm) {
std::string file_path = MakeTableFileName(
cfds[i]->ioptions()->cf_paths[0].path, file_meta[i].fd.GetNumber());
cfds[i]->ioptions().cf_paths[0].path, file_meta[i].fd.GetNumber());
// TODO (PR7798). We should only add the file to the FileManager if it
// exists. Otherwise, some tests may fail. Ignore the error in the
// interim.
@@ -908,7 +905,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
// go to L0 in the future.
const uint64_t file_number = file_meta->fd.GetNumber();
info.file_path =
MakeTableFileName(cfd->ioptions()->cf_paths[0].path, file_number);
MakeTableFileName(cfd->ioptions().cf_paths[0].path, file_number);
info.file_number = file_number;
info.thread_id = env_->GetThreadID();
info.job_id = job_id;
@@ -1040,9 +1037,8 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
return Status::InvalidArgument(oss.str());
}
Status s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
read_options, write_options, &edit, &mutex_,
directories_.GetDbDir());
Status s = versions_->LogAndApply(cfd, read_options, write_options, &edit,
&mutex_, directories_.GetDbDir());
if (!s.ok()) {
return s;
}
@@ -1066,11 +1062,11 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
auto cfd = cfh->cfd();
if (options.target_path_id >= cfd->ioptions()->cf_paths.size()) {
if (options.target_path_id >= cfd->ioptions().cf_paths.size()) {
return Status::InvalidArgument("Invalid target path ID");
}
if (options.change_level &&
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
cfd->ioptions().compaction_style == kCompactionStyleFIFO) {
return Status::NotSupported(
"FIFO compaction does not support change_level.");
}
@@ -1122,7 +1118,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
constexpr int kInvalidLevel = -1;
int final_output_level = kInvalidLevel;
bool exclusive = options.exclusive_manual_compaction;
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal &&
if (cfd->ioptions().compaction_style == kCompactionStyleUniversal &&
cfd->NumberLevels() > 1) {
// Always compact all files together.
final_output_level = cfd->NumberLevels() - 1;
@@ -1134,7 +1130,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
final_output_level, options, begin, end, exclusive,
false /* disable_trivial_move */,
std::numeric_limits<uint64_t>::max(), trim_ts);
} else if (cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
} else if (cfd->ioptions().compaction_style == kCompactionStyleFIFO) {
// FIFOCompactionPicker::CompactRange() will ignore the input key range
// [begin, end] and just try to pick compaction based on the configured
// option `compaction_options_fifo`. So we skip checking if [begin, end]
@@ -1152,7 +1148,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
// Might need to query the partitioner
SstPartitionerFactory* partitioner_factory =
current_version->cfd()->ioptions()->sst_partitioner_factory.get();
current_version->cfd()->ioptions().sst_partitioner_factory.get();
std::unique_ptr<SstPartitioner> partitioner;
if (partitioner_factory && begin != nullptr && end != nullptr) {
SstPartitioner::Context context;
@@ -1228,7 +1224,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
CleanupSuperVersion(super_version);
}
if (s.ok() && first_overlapped_level != kInvalidLevel) {
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal) {
if (cfd->ioptions().compaction_style == kCompactionStyleUniversal) {
assert(first_overlapped_level == 0);
s = RunManualCompaction(
cfd, first_overlapped_level, first_overlapped_level, options, begin,
@@ -1237,7 +1233,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
trim_ts);
final_output_level = first_overlapped_level;
} else {
assert(cfd->ioptions()->compaction_style == kCompactionStyleLevel);
assert(cfd->ioptions().compaction_style == kCompactionStyleLevel);
uint64_t next_file_number = versions_->current_next_file_number();
// Start compaction from `first_overlapped_level`, one level down at a
// time, until output level >= max_overlapped_level.
@@ -1250,9 +1246,9 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
for (;;) {
// Always allow L0 -> L1 compaction
if (level > 0) {
if (cfd->ioptions()->level_compaction_dynamic_level_bytes) {
assert(final_output_level < cfd->ioptions()->num_levels);
if (final_output_level + 1 == cfd->ioptions()->num_levels) {
if (cfd->ioptions().level_compaction_dynamic_level_bytes) {
assert(final_output_level < cfd->ioptions().num_levels);
if (final_output_level + 1 == cfd->ioptions().num_levels) {
break;
}
} else {
@@ -1271,7 +1267,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
}
}
output_level = level + 1;
if (cfd->ioptions()->level_compaction_dynamic_level_bytes &&
if (cfd->ioptions().level_compaction_dynamic_level_bytes &&
level == 0) {
output_level = ColumnFamilyData::kCompactToBaseLevel;
}
@@ -1303,8 +1299,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
// bottommost level intra-level compaction
if ((options.bottommost_level_compaction ==
BottommostLevelCompaction::kIfHaveCompactionFilter &&
(cfd->ioptions()->compaction_filter != nullptr ||
cfd->ioptions()->compaction_filter_factory != nullptr)) ||
(cfd->ioptions().compaction_filter != nullptr ||
cfd->ioptions().compaction_filter_factory != nullptr)) ||
options.bottommost_level_compaction ==
BottommostLevelCompaction::kForceOptimized ||
options.bottommost_level_compaction ==
@@ -1458,7 +1454,7 @@ Status DBImpl::CompactFilesImpl(
}
if (output_path_id < 0) {
if (cfd->ioptions()->cf_paths.size() == 1U) {
if (cfd->ioptions().cf_paths.size() == 1U) {
output_path_id = 0;
} else {
return Status::NotSupported(
@@ -1467,12 +1463,12 @@ Status DBImpl::CompactFilesImpl(
}
}
if (cfd->ioptions()->allow_ingest_behind &&
output_level >= cfd->ioptions()->num_levels - 1) {
if (cfd->ioptions().allow_ingest_behind &&
output_level >= cfd->ioptions().num_levels - 1) {
return Status::InvalidArgument(
"Exceed the maximum output level defined by "
"the current compaction algorithm with ingest_behind --- " +
std::to_string(cfd->ioptions()->num_levels - 1));
std::to_string(cfd->ioptions().num_levels - 1));
}
std::vector<CompactionInputFiles> input_files;
@@ -1509,7 +1505,7 @@ Status DBImpl::CompactFilesImpl(
assert(cfd->compaction_picker());
c.reset(cfd->compaction_picker()->CompactFiles(
compact_options, input_files, output_level, version->storage_info(),
*cfd->GetLatestMutableCFOptions(), mutable_db_options_, output_path_id));
cfd->GetLatestMutableCFOptions(), mutable_db_options_, output_path_id));
// we already sanitized the set of input files and checked for conflicts
// without releasing the lock, so we're guaranteed a compaction can be formed.
assert(c != nullptr);
@@ -1539,8 +1535,8 @@ Status DBImpl::CompactFilesImpl(
GetDataDir(c->column_family_data(), 0), stats_, &mutex_, &error_handler_,
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
job_context, table_cache_, &event_logger_,
c->mutable_cf_options()->paranoid_file_checks,
c->mutable_cf_options()->report_bg_io_stats, dbname_,
c->mutable_cf_options().paranoid_file_checks,
c->mutable_cf_options().report_bg_io_stats, dbname_,
&compaction_job_stats, Env::Priority::USER, io_tracer_,
kManualCompactionCanceledFalse_, db_id_, db_session_id_,
c->column_family_data()->GetFullHistoryTsLow(), c->trim_ts(),
@@ -1551,10 +1547,10 @@ Status DBImpl::CompactFilesImpl(
// takes running compactions into account (by skipping files that are already
// being compacted). Since we just changed compaction score, we recalculate it
// here.
version->storage_info()->ComputeCompactionScore(*cfd->ioptions(),
*c->mutable_cf_options());
version->storage_info()->ComputeCompactionScore(cfd->ioptions(),
c->mutable_cf_options());
compaction_job.Prepare();
compaction_job.Prepare(std::nullopt /*subcompact to be computed*/);
std::unique_ptr<std::list<uint64_t>::iterator> min_options_file_number_elem;
if (immutable_db_options().compaction_service != nullptr) {
@@ -1576,16 +1572,14 @@ Status DBImpl::CompactFilesImpl(
}
bool compaction_released = false;
Status status =
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
Status status = compaction_job.Install(&compaction_released);
if (!compaction_released) {
c->ReleaseCompactionFiles(s);
}
if (status.ok()) {
assert(compaction_job.io_status().ok());
InstallSuperVersionAndScheduleWork(
c->column_family_data(), job_context->superversion_contexts.data(),
*c->mutable_cf_options());
c->column_family_data(), job_context->superversion_contexts.data());
}
// status above captures any error during compaction_job.Install, so its ok
// not check compaction_job.io_status() explicitly if we're not calling
@@ -1633,13 +1627,13 @@ Status DBImpl::CompactFilesImpl(
if (output_file_names != nullptr) {
for (const auto& newf : c->edit()->GetNewFiles()) {
output_file_names->push_back(TableFileName(
c->immutable_options()->cf_paths, newf.second.fd.GetNumber(),
c->immutable_options().cf_paths, newf.second.fd.GetNumber(),
newf.second.fd.GetPathId()));
}
for (const auto& blob_file : c->edit()->GetBlobFileAdditions()) {
output_file_names->push_back(
BlobFileName(c->immutable_options()->cf_paths.front().path,
BlobFileName(c->immutable_options().cf_paths.front().path,
blob_file.GetBlobFileNumber()));
}
}
@@ -1774,7 +1768,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
}
refitting_level_ = true;
const MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
// move to a smaller level
int to_level = target_level;
if (target_level < 0) {
@@ -1845,12 +1839,12 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
cfd->current()->DebugString().data());
std::unique_ptr<Compaction> c(new Compaction(
vstorage, *cfd->ioptions(), mutable_cf_options, mutable_db_options_,
vstorage, cfd->ioptions(), mutable_cf_options, mutable_db_options_,
{input}, to_level,
MaxFileSizeForLevel(
mutable_cf_options, to_level,
cfd->ioptions()
->compaction_style) /* output file size limit, not applicable */
.compaction_style) /* output file size limit, not applicable */
,
LLONG_MAX /* max compaction bytes, not applicable */,
0 /* output path ID, not applicable */, mutable_cf_options.compression,
@@ -1883,14 +1877,14 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
"[%s] Apply version edit:\n%s", cfd->GetName().c_str(),
edit.DebugString().data());
Status status = versions_->LogAndApply(cfd, mutable_cf_options,
read_options, write_options, &edit,
&mutex_, directories_.GetDbDir());
Status status =
versions_->LogAndApply(cfd, read_options, write_options, &edit, &mutex_,
directories_.GetDbDir());
c->MarkFilesBeingCompacted(false);
cfd->compaction_picker()->UnregisterCompaction(c.get());
c.reset();
InstallSuperVersionAndScheduleWork(cfd, &sv_context, mutable_cf_options);
InstallSuperVersionAndScheduleWork(cfd, &sv_context);
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] LogAndApply: %s\n",
cfd->GetName().c_str(), status.ToString().data());
@@ -2043,16 +2037,16 @@ Status DBImpl::RunManualCompaction(
// For universal compaction, we enforce every manual compaction to compact
// all files.
if (begin == nullptr ||
cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
cfd->ioptions().compaction_style == kCompactionStyleUniversal ||
cfd->ioptions().compaction_style == kCompactionStyleFIFO) {
manual.begin = nullptr;
} else {
begin_storage.SetMinPossibleForUserKey(*begin);
manual.begin = &begin_storage;
}
if (end == nullptr ||
cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
cfd->ioptions().compaction_style == kCompactionStyleUniversal ||
cfd->ioptions().compaction_style == kCompactionStyleFIFO) {
manual.end = nullptr;
} else {
end_storage.SetMaxPossibleForUserKey(*end);
@@ -2133,7 +2127,7 @@ Status DBImpl::RunManualCompaction(
scheduled ||
(((manual.manual_end = &manual.tmp_storage1) != nullptr) &&
((compaction = manual.cfd->CompactRange(
*manual.cfd->GetLatestMutableCFOptions(), mutable_db_options_,
manual.cfd->GetLatestMutableCFOptions(), mutable_db_options_,
manual.input_level, manual.output_level, compact_range_options,
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
max_file_num_to_ignore, trim_ts)) == nullptr &&
@@ -2676,7 +2670,7 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
return Status::OK();
}
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
const auto* vstorage = cfd->current()->storage_info();
// Skip stalling check if we're below auto-flush and auto-compaction
@@ -2684,7 +2678,7 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
// triggers are so low that stalling is needed for any background work. In
// that case we shouldn't wait since background work won't be scheduled.
if (cfd->imm()->NumNotFlushed() <
cfd->ioptions()->min_write_buffer_number_to_merge &&
cfd->ioptions().min_write_buffer_number_to_merge &&
vstorage->l0_delay_trigger_count() <
mutable_cf_options.level0_file_num_compaction_trigger) {
break;
@@ -2700,7 +2694,7 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
vstorage->l0_delay_trigger_count() + 1,
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
*cfd->ioptions())
cfd->ioptions())
.first;
} while (write_stall_condition != WriteStallCondition::kNormal);
}
@@ -3528,6 +3522,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
}
}
// Precondition: mutex_ must be held when calling this function.
Status DBImpl::BackgroundCompaction(bool* made_progress,
JobContext* job_context,
LogBuffer* log_buffer,
@@ -3679,8 +3674,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
// Compaction makes a copy of the latest MutableCFOptions. It should be used
// throughout the compaction procedure to make sure consistency. It will
// eventually be installed into SuperVersion
auto* mutable_cf_options = cfd->GetLatestMutableCFOptions();
if (!mutable_cf_options->disable_auto_compactions && !cfd->IsDropped()) {
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
if (!mutable_cf_options.disable_auto_compactions && !cfd->IsDropped()) {
// NOTE: try to avoid unnecessary copy of MutableCFOptions if
// compaction is not necessary. Need to make sure mutex is held
// until we make a copy in the following code
@@ -3689,7 +3684,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
std::vector<SequenceNumber> snapshot_seqs;
// This info is not useful for other scenarios, so save querying existing
// snapshots for those cases.
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal &&
if (cfd->ioptions().compaction_style == kCompactionStyleUniversal &&
cfd->user_comparator()->timestamp_size() == 0) {
SequenceNumber earliest_write_conflict_snapshot;
GetSnapshotContext(job_context, &snapshot_seqs,
@@ -3697,7 +3692,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
&snapshot_checker);
assert(is_snapshot_supported_ || snapshots_.empty());
}
c.reset(cfd->PickCompaction(*mutable_cf_options, mutable_db_options_,
c.reset(cfd->PickCompaction(mutable_cf_options, mutable_db_options_,
snapshot_seqs, snapshot_checker, log_buffer));
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():AfterPickCompaction");
@@ -3711,8 +3706,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
c->column_family_data()
->current()
->storage_info()
->ComputeCompactionScore(*(c->immutable_options()),
*(c->mutable_cf_options()));
->ComputeCompactionScore(c->immutable_options(),
c->mutable_cf_options());
AddToCompactionQueue(cfd);
c.reset();
@@ -3760,7 +3755,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:BeforeCompaction",
c->column_family_data());
assert(c->num_input_files(1) == 0);
assert(c->column_family_data()->ioptions()->compaction_style ==
assert(c->column_family_data()->ioptions().compaction_style ==
kCompactionStyleFIFO);
compaction_job_stats.num_input_files = c->num_input_files(0);
@@ -3772,8 +3767,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
c->edit()->DeleteFile(c->level(), f->fd.GetNumber());
}
status = versions_->LogAndApply(
c->column_family_data(), *c->mutable_cf_options(), read_options,
write_options, c->edit(), &mutex_, directories_.GetDbDir(),
c->column_family_data(), read_options, write_options, c->edit(),
&mutex_, directories_.GetDbDir(),
/*new_descriptor_log=*/false, /*column_family_options=*/nullptr,
[&c, &compaction_released](const Status& s) {
c->ReleaseCompactionFiles(s);
@@ -3781,8 +3776,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
});
io_s = versions_->io_status();
InstallSuperVersionAndScheduleWork(
c->column_family_data(), job_context->superversion_contexts.data(),
*c->mutable_cf_options());
c->column_family_data(), job_context->superversion_contexts.data());
ROCKS_LOG_BUFFER(log_buffer, "[%s] Deleted %d files\n",
c->column_family_data()->GetName().c_str(),
c->num_input_files(0));
@@ -3838,7 +3832,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
}
}
if (c->compaction_reason() == CompactionReason::kLevelMaxLevelSize &&
c->immutable_options()->compaction_pri == kRoundRobin) {
c->immutable_options().compaction_pri == kRoundRobin) {
int start_level = c->start_level();
if (start_level > 0) {
auto vstorage = c->input_version()->storage_info();
@@ -3848,26 +3842,23 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
}
}
status = versions_->LogAndApply(
c->column_family_data(), *c->mutable_cf_options(), read_options,
write_options, c->edit(), &mutex_, directories_.GetDbDir(),
c->column_family_data(), read_options, write_options, c->edit(),
&mutex_, directories_.GetDbDir(),
/*new_descriptor_log=*/false, /*column_family_options=*/nullptr,
[&c, &compaction_released](const Status& s) {
c->ReleaseCompactionFiles(s);
compaction_released = true;
});
io_s = versions_->io_status();
// Use latest MutableCFOptions
InstallSuperVersionAndScheduleWork(
c->column_family_data(), job_context->superversion_contexts.data(),
*c->mutable_cf_options());
c->column_family_data(), job_context->superversion_contexts.data());
VersionStorageInfo::LevelSummaryStorage tmp;
c->column_family_data()->internal_stats()->IncBytesMoved(c->output_level(),
moved_bytes);
{
event_logger_.LogToBuffer(log_buffer)
<< "job" << job_context->job_id << "event"
<< "trivial_move"
<< "job" << job_context->job_id << "event" << "trivial_move"
<< "destination_level" << c->output_level() << "files" << moved_files
<< "total_files_size" << moved_bytes;
}
@@ -3929,15 +3920,15 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
GetDataDir(c->column_family_data(), 0), stats_, &mutex_,
&error_handler_, snapshot_seqs, earliest_write_conflict_snapshot,
snapshot_checker, job_context, table_cache_, &event_logger_,
c->mutable_cf_options()->paranoid_file_checks,
c->mutable_cf_options()->report_bg_io_stats, dbname_,
c->mutable_cf_options().paranoid_file_checks,
c->mutable_cf_options().report_bg_io_stats, dbname_,
&compaction_job_stats, thread_pri, io_tracer_,
is_manual ? manual_compaction->canceled
: kManualCompactionCanceledFalse_,
db_id_, db_session_id_, c->column_family_data()->GetFullHistoryTsLow(),
c->trim_ts(), &blob_callback_, &bg_compaction_scheduled_,
&bg_bottom_compaction_scheduled_);
compaction_job.Prepare();
compaction_job.Prepare(std::nullopt /*subcompact to be computed*/);
std::unique_ptr<std::list<uint64_t>::iterator> min_options_file_number_elem;
if (immutable_db_options().compaction_service != nullptr) {
@@ -3959,13 +3950,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
ReleaseOptionsFileNumber(min_options_file_number_elem);
}
status =
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
status = compaction_job.Install(&compaction_released);
io_s = compaction_job.io_status();
if (status.ok()) {
InstallSuperVersionAndScheduleWork(
c->column_family_data(), job_context->superversion_contexts.data(),
*c->mutable_cf_options());
c->column_family_data(), job_context->superversion_contexts.data());
}
*made_progress = true;
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
@@ -4043,8 +4032,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
c->column_family_data()
->current()
->storage_info()
->ComputeCompactionScore(*(c->immutable_options()),
*(c->mutable_cf_options()));
->ComputeCompactionScore(c->immutable_options(),
c->mutable_cf_options());
if (!cfd->queued_for_compaction()) {
AddToCompactionQueue(cfd);
}
@@ -4079,10 +4068,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
// We only compacted part of the requested range. Update *m
// to the range that is left to be compacted.
// Universal and FIFO compactions should always compact the whole range
assert(m->cfd->ioptions()->compaction_style !=
kCompactionStyleUniversal ||
m->cfd->ioptions()->num_levels > 1);
assert(m->cfd->ioptions()->compaction_style != kCompactionStyleFIFO);
assert(m->cfd->ioptions().compaction_style != kCompactionStyleUniversal ||
m->cfd->ioptions().num_levels > 1);
assert(m->cfd->ioptions().compaction_style != kCompactionStyleFIFO);
m->tmp_storage = *m->manual_end;
m->begin = &m->tmp_storage;
m->incomplete = true;
@@ -4229,7 +4217,7 @@ void DBImpl::BuildCompactionJobInfo(
for (const auto fmd : *c->inputs(i)) {
const FileDescriptor& desc = fmd->fd;
const uint64_t file_number = desc.GetNumber();
auto fn = TableFileName(c->immutable_options()->cf_paths, file_number,
auto fn = TableFileName(c->immutable_options().cf_paths, file_number,
desc.GetPathId());
compaction_job_info->input_files.push_back(fn);
compaction_job_info->input_file_infos.push_back(CompactionFileInfo{
@@ -4242,17 +4230,17 @@ void DBImpl::BuildCompactionJobInfo(
const FileDescriptor& desc = meta.fd;
const uint64_t file_number = desc.GetNumber();
compaction_job_info->output_files.push_back(TableFileName(
c->immutable_options()->cf_paths, file_number, desc.GetPathId()));
c->immutable_options().cf_paths, file_number, desc.GetPathId()));
compaction_job_info->output_file_infos.push_back(CompactionFileInfo{
newf.first, file_number, meta.oldest_blob_file_number});
}
compaction_job_info->blob_compression_type =
c->mutable_cf_options()->blob_compression_type;
c->mutable_cf_options().blob_compression_type;
// Update BlobFilesInfo.
for (const auto& blob_file : c->edit()->GetBlobFileAdditions()) {
BlobFileAdditionInfo blob_file_addition_info(
BlobFileName(c->immutable_options()->cf_paths.front().path,
BlobFileName(c->immutable_options().cf_paths.front().path,
blob_file.GetBlobFileNumber()) /*blob_file_path*/,
blob_file.GetBlobFileNumber(), blob_file.GetTotalBlobCount(),
blob_file.GetTotalBlobBytes());
@@ -4263,7 +4251,7 @@ void DBImpl::BuildCompactionJobInfo(
// Update BlobFilesGarbageInfo.
for (const auto& blob_file : c->edit()->GetBlobFileGarbages()) {
BlobFileGarbageInfo blob_file_garbage_info(
BlobFileName(c->immutable_options()->cf_paths.front().path,
BlobFileName(c->immutable_options().cf_paths.front().path,
blob_file.GetBlobFileNumber()) /*blob_file_path*/,
blob_file.GetBlobFileNumber(), blob_file.GetGarbageBlobCount(),
blob_file.GetGarbageBlobBytes());
@@ -4285,9 +4273,9 @@ void DBImpl::BuildCompactionJobInfo(
// for superversion_to_free
void DBImpl::InstallSuperVersionAndScheduleWork(
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
const MutableCFOptions& mutable_cf_options) {
ColumnFamilyData* cfd, SuperVersionContext* sv_context) {
mutex_.AssertHeld();
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
// Update max_total_in_memory_state_
size_t old_memtable_size = 0;
@@ -4310,7 +4298,7 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
bottommost_files_mark_threshold_ = kMaxSequenceNumber;
standalone_range_deletion_files_mark_threshold_ = kMaxSequenceNumber;
for (auto* my_cfd : *versions_->GetColumnFamilySet()) {
if (!my_cfd->ioptions()->allow_ingest_behind) {
if (!my_cfd->ioptions().allow_ingest_behind) {
bottommost_files_mark_threshold_ = std::min(
bottommost_files_mark_threshold_,
my_cfd->current()->storage_info()->bottommost_files_mark_threshold());
+10 -6
View File
@@ -6,8 +6,8 @@
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#ifndef NDEBUG
#include <iostream>
#include "db/blob/blob_file_cache.h"
#include "db/column_family.h"
@@ -103,8 +103,8 @@ Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
cfd = cfh->cfd();
}
int output_level =
(cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO)
(cfd->ioptions().compaction_style == kCompactionStyleUniversal ||
cfd->ioptions().compaction_style == kCompactionStyleFIFO)
? level
: level + 1;
return RunManualCompaction(
@@ -239,7 +239,7 @@ void DBImpl::TEST_GetAllBlockCaches(
for (auto cfd : *versions_->GetColumnFamilySet()) {
if (const auto bbto =
cfd->GetCurrentMutableCFOptions()
->table_factory->GetOptions<BlockBasedTableOptions>()) {
.table_factory->GetOptions<BlockBasedTableOptions>()) {
cache_set->insert(bbto->block_cache.get());
}
}
@@ -267,7 +267,7 @@ Status DBImpl::TEST_GetLatestMutableCFOptions(
InstrumentedMutexLock l(&mutex_);
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
*mutable_cf_options = *cfh->cfd()->GetLatestMutableCFOptions();
*mutable_cf_options = cfh->cfd()->GetLatestMutableCFOptions();
return Status::OK();
}
@@ -379,7 +379,11 @@ void DBImpl::TEST_VerifyNoObsoleteFilesCached(
uint64_t file_number;
GetUnaligned(reinterpret_cast<const uint64_t*>(key.data()), &file_number);
// Assert file is in live/quarantined set
assert(live_and_quar_files.find(file_number) != live_and_quar_files.end());
if (live_and_quar_files.find(file_number) == live_and_quar_files.end()) {
std::cerr << "File " << file_number << " is not live nor quarantined"
<< std::endl;
assert(false);
}
};
table_cache_->ApplyToAllEntries(fn, {});
}
+5 -7
View File
@@ -45,8 +45,8 @@ Status DBImpl::SuggestCompactRange(ColumnFamilyHandle* column_family,
}
// Since we have some more files to compact, we should also recompute
// compaction score
vstorage->ComputeCompactionScore(*cfd->ioptions(),
*cfd->GetLatestMutableCFOptions());
vstorage->ComputeCompactionScore(cfd->ioptions(),
cfd->GetLatestMutableCFOptions());
EnqueuePendingCompaction(cfd);
MaybeScheduleFlushOrCompaction();
}
@@ -146,13 +146,11 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
f->user_defined_timestamps_persisted);
}
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
read_options, write_options, &edit, &mutex_,
directories_.GetDbDir());
status = versions_->LogAndApply(cfd, read_options, write_options, &edit,
&mutex_, directories_.GetDbDir());
if (status.ok()) {
InstallSuperVersionAndScheduleWork(
cfd, job_context.superversion_contexts.data(),
*cfd->GetLatestMutableCFOptions());
cfd, job_context.superversion_contexts.data());
}
} // lock released here
LogFlush(immutable_db_options_.info_log);
+2 -1
View File
@@ -159,6 +159,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
// Get obsolete files. This function will also update the list of
// pending files in VersionSet().
assert(versions_);
versions_->GetObsoleteFiles(
&job_context->sst_delete_files, &job_context->blob_delete_files,
&job_context->manifest_delete_files, job_context->min_pending_output);
@@ -1039,7 +1040,7 @@ std::set<std::string> DBImpl::CollectAllDBPaths() {
all_db_paths.insert(NormalizePath(db_path.path));
}
for (const auto* cfd : *versions_->GetColumnFamilySet()) {
for (const auto& cf_path : cfd->ioptions()->cf_paths) {
for (const auto& cf_path : cfd->ioptions().cf_paths) {
all_db_paths.insert(NormalizePath(cf_path.path));
}
}
+1 -3
View File
@@ -140,10 +140,8 @@ Status DBImplFollower::TryCatchUpWithLeader() {
// to super versions in a lock free manner, it checks the earliest
// sequence number to detect if there was a change in version in
// the meantime.
const MutableCFOptions mutable_cf_options =
*cfd->GetLatestMutableCFOptions();
MemTable* new_mem = cfd->ConstructNewMemtable(
mutable_cf_options, versions_->LastSequence());
cfd->GetLatestMutableCFOptions(), versions_->LastSequence());
cfd->mem()->SetNextLogNumber(cfd->GetLogNumber());
cfd->mem()->ConstructFragmentedRangeTombstones();
cfd->imm()->Add(cfd->mem(), &job_context.memtables_to_free);
+626 -318
View File
File diff suppressed because it is too large. Load diff
+27 -15
View File
@@ -8,7 +8,7 @@
#include "db/arena_wrapped_db_iter.h"
#include "db/db_impl/compacted_db_impl.h"
#include "db/db_impl/db_impl.h"
#include "db/db_iter.h"
#include "db/manifest_ops.h"
#include "db/merge_context.h"
#include "logging/logging.h"
#include "monitoring/perf_context_imp.h"
@@ -16,7 +16,6 @@
namespace ROCKSDB_NAMESPACE {
DBImplReadOnly::DBImplReadOnly(const DBOptions& db_options,
const std::string& dbname)
: DBImpl(db_options, dbname, /*seq_per_batch*/ false,
@@ -33,7 +32,8 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
const Slice& key,
GetImplOptions& get_impl_options) {
assert(get_impl_options.value != nullptr ||
get_impl_options.columns != nullptr);
get_impl_options.columns != nullptr ||
get_impl_options.merge_operands != nullptr);
assert(get_impl_options.column_family);
Status s;
@@ -87,7 +87,11 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
return s;
}
}
// Prepare to store a list of merge operations if merge occurs.
MergeContext merge_context;
// TODO - Large Result Optimization for Read Only DB
// (https://github.com/facebook/rocksdb/pull/10458)
SequenceNumber max_covering_tombstone_seq = 0;
LookupKey lkey(key, snapshot, read_options.timestamp);
PERF_TIMER_STOP(get_snapshot_time);
@@ -98,7 +102,8 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
get_impl_options.value ? get_impl_options.value->GetSelf() : nullptr,
get_impl_options.columns, ts, &s, &merge_context,
&max_covering_tombstone_seq, read_options,
false /* immutable_memtable */, &read_cb)) {
false /* immutable_memtable */, &read_cb,
/*is_blob_index=*/nullptr, /*do_merge=*/get_impl_options.get_value)) {
if (get_impl_options.value) {
get_impl_options.value->PinSelf();
}
@@ -112,7 +117,7 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
/*value_found*/ nullptr,
/*key_exists*/ nullptr, /*seq*/ nullptr, &read_cb,
/*is_blob*/ nullptr,
/*do_merge*/ true);
/*do_merge=*/get_impl_options.get_value);
RecordTick(stats_, MEMTABLE_MISS);
}
{
@@ -122,6 +127,14 @@ Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
size = get_impl_options.value->size();
} else if (get_impl_options.columns) {
size = get_impl_options.columns->serialized_size();
} else if (get_impl_options.merge_operands) {
*get_impl_options.number_of_operands =
static_cast<int>(merge_context.GetNumOperands());
for (const Slice& sl : merge_context.GetOperands()) {
size += sl.size();
get_impl_options.merge_operands->PinSelf(sl);
get_impl_options.merge_operands++;
}
}
RecordTick(stats_, BYTES_READ, size);
RecordInHistogram(stats_, BYTES_PER_READ, size);
@@ -173,7 +186,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& _read_options,
: latest_snapshot;
ReadCallback* read_callback = nullptr; // No read callback provided.
auto db_iter = NewArenaWrappedDbIterator(
env_, read_options, *cfd->ioptions(), super_version->mutable_cf_options,
env_, read_options, cfd->ioptions(), super_version->mutable_cf_options,
super_version->current, read_seq,
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
super_version->version_number, read_callback);
@@ -240,7 +253,7 @@ Status DBImplReadOnly::NewIterators(
assert(cfd_to_sv.size() == column_families.size());
for (auto [cfd, sv] : cfd_to_sv) {
auto* db_iter = NewArenaWrappedDbIterator(
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
env_, read_options, cfd->ioptions(), sv->mutable_cf_options,
sv->current, read_seq,
sv->mutable_cf_options.max_sequential_skip_in_iterations,
sv->version_number, read_callback);
@@ -265,9 +278,8 @@ Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
const std::shared_ptr<FileSystem>& fs = db_options.env->GetFileSystem();
std::string manifest_path;
uint64_t manifest_file_number;
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), /*is_retry=*/false,
&manifest_path,
&manifest_file_number);
s = GetCurrentManifestPath(dbname, fs.get(), /*is_retry=*/false,
&manifest_path, &manifest_file_number);
} else {
// Historic behavior that doesn't necessarily make sense
s = db_options.env->CreateDirIfMissing(dbname);
@@ -277,7 +289,8 @@ Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
} // namespace
Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
DB** dbptr, bool /*error_if_wal_file_exists*/) {
std::unique_ptr<DB>* dbptr,
bool /*error_if_wal_file_exists*/) {
Status s = OpenForReadOnlyCheckExistence(options, dbname);
if (!s.ok()) {
return s;
@@ -311,7 +324,7 @@ Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
Status DB::OpenForReadOnly(
const DBOptions& db_options, const std::string& dbname,
const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr,
bool error_if_wal_file_exists) {
// If dbname does not exist in the file system, should not do anything
Status s = OpenForReadOnlyCheckExistence(db_options, dbname);
@@ -327,7 +340,7 @@ Status DB::OpenForReadOnly(
Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
const DBOptions& db_options, const std::string& dbname,
const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr,
bool error_if_wal_file_exists) {
*dbptr = nullptr;
handles->clear();
@@ -358,7 +371,7 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
impl->mutex_.Unlock();
sv_context.Clean();
if (s.ok()) {
*dbptr = impl;
dbptr->reset(impl);
for (auto* h : *handles) {
impl->NewThreadStatusCfInfo(
static_cast_with_check<ColumnFamilyHandleImpl>(h)->cfd());
@@ -373,5 +386,4 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
return s;
}
} // namespace ROCKSDB_NAMESPACE
+1 -2
View File
@@ -5,7 +5,6 @@
#pragma once
#include <string>
#include <vector>
@@ -172,7 +171,7 @@ class DBImplReadOnly : public DBImpl {
static Status OpenForReadOnlyWithoutCheck(
const DBOptions& db_options, const std::string& dbname,
const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr,
bool error_if_wal_file_exists = false);
friend class DB;
};
+80 -40
View File
@@ -55,9 +55,9 @@ Status DBImplSecondary::Recover(
// Initial max_total_in_memory_state_ before recovery logs.
max_total_in_memory_state_ = 0;
for (auto cfd : *versions_->GetColumnFamilySet()) {
auto* mutable_cf_options = cfd->GetLatestMutableCFOptions();
max_total_in_memory_state_ += mutable_cf_options->write_buffer_size *
mutable_cf_options->max_write_buffer_number;
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
max_total_in_memory_state_ += mutable_cf_options.write_buffer_size *
mutable_cf_options.max_write_buffer_number;
}
if (s.ok()) {
default_cf_handle_ = new ColumnFamilyHandleImpl(
@@ -270,10 +270,8 @@ Status DBImplSecondary::RecoverLogFiles(
if (!cfd->mem()->IsEmpty() &&
(curr_log_num == std::numeric_limits<uint64_t>::max() ||
curr_log_num != log_number)) {
const MutableCFOptions mutable_cf_options =
*cfd->GetLatestMutableCFOptions();
MemTable* new_mem =
cfd->ConstructNewMemtable(mutable_cf_options, seq_of_batch);
MemTable* new_mem = cfd->ConstructNewMemtable(
cfd->GetLatestMutableCFOptions(), seq_of_batch);
cfd->mem()->SetNextLogNumber(log_number);
cfd->mem()->ConstructFragmentedRangeTombstones();
cfd->imm()->Add(cfd->mem(), &job_context->memtables_to_free);
@@ -344,7 +342,8 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
const Slice& key,
GetImplOptions& get_impl_options) {
assert(get_impl_options.value != nullptr ||
get_impl_options.columns != nullptr);
get_impl_options.columns != nullptr ||
get_impl_options.merge_operands != nullptr);
assert(get_impl_options.column_family);
Status s;
@@ -399,37 +398,64 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
}
}
MergeContext merge_context;
// TODO - Large Result Optimization for Secondary DB
// (https://github.com/facebook/rocksdb/pull/10458)
SequenceNumber max_covering_tombstone_seq = 0;
LookupKey lkey(key, snapshot, read_options.timestamp);
PERF_TIMER_STOP(get_snapshot_time);
bool done = false;
// Look up starts here
if (super_version->mem->Get(
lkey,
get_impl_options.value ? get_impl_options.value->GetSelf() : nullptr,
get_impl_options.columns, ts, &s, &merge_context,
&max_covering_tombstone_seq, read_options,
false /* immutable_memtable */, &read_cb)) {
done = true;
if (get_impl_options.value) {
get_impl_options.value->PinSelf();
if (get_impl_options.get_value) {
if (super_version->mem->Get(
lkey,
get_impl_options.value ? get_impl_options.value->GetSelf()
: nullptr,
get_impl_options.columns, ts, &s, &merge_context,
&max_covering_tombstone_seq, read_options,
false /* immutable_memtable */, &read_cb,
/*is_blob_index=*/nullptr, /*do_merge=*/true)) {
done = true;
if (get_impl_options.value) {
get_impl_options.value->PinSelf();
}
RecordTick(stats_, MEMTABLE_HIT);
} else if ((s.ok() || s.IsMergeInProgress()) &&
super_version->imm->Get(
lkey,
get_impl_options.value ? get_impl_options.value->GetSelf()
: nullptr,
get_impl_options.columns, ts, &s, &merge_context,
&max_covering_tombstone_seq, read_options, &read_cb)) {
done = true;
if (get_impl_options.value) {
get_impl_options.value->PinSelf();
}
RecordTick(stats_, MEMTABLE_HIT);
}
RecordTick(stats_, MEMTABLE_HIT);
} else if ((s.ok() || s.IsMergeInProgress()) &&
super_version->imm->Get(
lkey,
get_impl_options.value ? get_impl_options.value->GetSelf()
: nullptr,
get_impl_options.columns, ts, &s, &merge_context,
&max_covering_tombstone_seq, read_options, &read_cb)) {
done = true;
if (get_impl_options.value) {
get_impl_options.value->PinSelf();
} else {
// GetMergeOperands
if (super_version->mem->Get(
lkey,
get_impl_options.value ? get_impl_options.value->GetSelf()
: nullptr,
get_impl_options.columns, ts, &s, &merge_context,
&max_covering_tombstone_seq, read_options,
false /* immutable_memtable */, &read_cb,
/*is_blob_index=*/nullptr, /*do_merge=*/false)) {
done = true;
RecordTick(stats_, MEMTABLE_HIT);
} else if ((s.ok() || s.IsMergeInProgress()) &&
super_version->imm->GetMergeOperands(lkey, &s, &merge_context,
&max_covering_tombstone_seq,
read_options)) {
done = true;
RecordTick(stats_, MEMTABLE_HIT);
}
RecordTick(stats_, MEMTABLE_HIT);
}
if (!done && !s.ok() && !s.IsMergeInProgress()) {
if (!s.ok() && !s.IsMergeInProgress() && !s.IsNotFound()) {
assert(done);
ReturnAndCleanupSuperVersion(cfd, super_version);
return s;
}
@@ -453,6 +479,14 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
size = get_impl_options.value->size();
} else if (get_impl_options.columns) {
size = get_impl_options.columns->serialized_size();
} else if (get_impl_options.merge_operands) {
*get_impl_options.number_of_operands =
static_cast<int>(merge_context.GetNumOperands());
for (const Slice& sl : merge_context.GetOperands()) {
size += sl.size();
get_impl_options.merge_operands->PinSelf(sl);
get_impl_options.merge_operands++;
}
}
RecordTick(stats_, BYTES_READ, size);
RecordTimeToHistogram(stats_, BYTES_PER_READ, size);
@@ -533,7 +567,7 @@ ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
snapshot = versions_->LastSequence();
assert(snapshot != kMaxSequenceNumber);
auto db_iter = NewArenaWrappedDbIterator(
env_, read_options, *cfh->cfd()->ioptions(),
env_, read_options, cfh->cfd()->ioptions(),
super_version->mutable_cf_options, super_version->current, snapshot,
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
super_version->version_number, read_callback, cfh, expose_blob_index,
@@ -737,7 +771,8 @@ Status DBImplSecondary::TryCatchUpWithPrimary() {
}
Status DB::OpenAsSecondary(const Options& options, const std::string& dbname,
const std::string& secondary_path, DB** dbptr) {
const std::string& secondary_path,
std::unique_ptr<DB>* dbptr) {
*dbptr = nullptr;
DBOptions db_options(options);
@@ -759,7 +794,7 @@ Status DB::OpenAsSecondary(
const DBOptions& db_options, const std::string& dbname,
const std::string& secondary_path,
const std::vector<ColumnFamilyDescriptor>& column_families,
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr) {
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr) {
*dbptr = nullptr;
DBOptions tmp_opts(db_options);
@@ -826,7 +861,7 @@ Status DB::OpenAsSecondary(
impl->mutex_.Unlock();
sv_context.Clean();
if (s.ok()) {
*dbptr = impl;
dbptr->reset(impl);
for (auto h : *handles) {
impl->NewThreadStatusCfInfo(
static_cast_with_check<ColumnFamilyHandleImpl>(h)->cfd());
@@ -864,21 +899,24 @@ Status DBImplSecondary::CompactWithoutInstallation(
ColumnFamilyMetaData cf_meta;
version->GetColumnFamilyMetaData(&cf_meta);
const MutableCFOptions* mutable_cf_options = cfd->GetLatestMutableCFOptions();
ColumnFamilyOptions cf_options = cfd->GetLatestCFOptions();
VersionStorageInfo* vstorage = version->storage_info();
// Use comp_options to reuse some CompactFiles functions
CompactionOptions comp_options;
comp_options.compression = kDisableCompressionOption;
comp_options.output_file_size_limit = MaxFileSizeForLevel(
*mutable_cf_options, input.output_level, cf_options.compaction_style,
vstorage->base_level(), cf_options.level_compaction_dynamic_level_bytes);
cfd->GetLatestMutableCFOptions(), input.output_level,
cfd->ioptions().compaction_style, vstorage->base_level(),
cfd->ioptions().level_compaction_dynamic_level_bytes);
std::vector<CompactionInputFiles> input_files;
Status s = cfd->compaction_picker()->GetCompactionInputsFromFileNumbers(
&input_files, &input_set, vstorage, comp_options);
if (!s.ok()) {
ROCKS_LOG_ERROR(
immutable_db_options_.info_log,
"GetCompactionInputsFromFileNumbers() failed - %s.\n DebugString: %s",
s.ToString().c_str(), version->DebugString().c_str());
return s;
}
@@ -886,7 +924,7 @@ Status DBImplSecondary::CompactWithoutInstallation(
assert(cfd->compaction_picker());
c.reset(cfd->compaction_picker()->CompactFiles(
comp_options, input_files, input.output_level, vstorage,
*mutable_cf_options, mutable_db_options_, 0));
cfd->GetLatestMutableCFOptions(), mutable_db_options_, 0));
assert(c != nullptr);
c->FinalizeInputInfo(version);
@@ -917,6 +955,8 @@ Status DBImplSecondary::CompactWithoutInstallation(
options.canceled ? *options.canceled : kManualCompactionCanceledFalse_,
input.db_id, db_session_id_, secondary_path_, input, result);
compaction_job.Prepare();
mutex_.Unlock();
s = compaction_job.Run();
mutex_.Lock();
@@ -954,6 +994,7 @@ Status DB::OpenAndCompact(
DBOptions db_options;
ConfigOptions config_options;
config_options.env = override_options.env;
config_options.ignore_unknown_options = true;
std::vector<ColumnFamilyDescriptor> all_column_families;
TEST_SYNC_POINT_CALLBACK(
@@ -1057,5 +1098,4 @@ Status DB::OpenAndCompact(
output, override_options);
}
} // namespace ROCKSDB_NAMESPACE
+2 -2
View File
@@ -5,7 +5,6 @@
#pragma once
#include <string>
#include <vector>
@@ -53,7 +52,8 @@ class LogReaderContainer {
Logger* info_log;
std::string fname;
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
void Corruption(size_t bytes, const Status& s) override {
void Corruption(size_t bytes, const Status& s,
uint64_t /*log_number*/ = kMaxSequenceNumber) override {
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
(this->status == nullptr ? "(ignoring error) " : ""),
fname.c_str(), static_cast<int>(bytes),
+249 -27
View File
@@ -12,6 +12,7 @@
#include "db/error_handler.h"
#include "db/event_helpers.h"
#include "logging/logging.h"
#include "memtable/wbwi_memtable.h"
#include "monitoring/perf_context_imp.h"
#include "options/options_helper.h"
#include "test_util/sync_point.h"
@@ -66,7 +67,7 @@ Status DBImpl::Merge(const WriteOptions& o, ColumnFamilyHandle* column_family,
return s;
}
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
if (!cfh->cfd()->ioptions()->merge_operator) {
if (!cfh->cfd()->ioptions().merge_operator) {
return Status::NotSupported("Provide a merge_operator when opening DB");
} else {
return DB::Merge(o, column_family, key, val);
@@ -189,16 +190,137 @@ Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
return s;
}
// The main write queue. This is the only write queue that updates LastSequence.
// When using one write queue, the same sequence also indicates the last
// published sequence.
Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
const WBWIMemTable::SeqnoRange& assigned_seqno,
uint64_t prep_log,
SequenceNumber last_seqno_after_ingest,
bool memtable_updated, bool ignore_missing_cf) {
// Keys in new memtable have seqno > last_seqno_after_ingest >= keys in wbwi.
assert(assigned_seqno.upper_bound <= last_seqno_after_ingest);
// Keys in the current memtable have seqno <= LastSequence() < keys in wbwi.
assert(assigned_seqno.lower_bound > versions_->LastSequence());
autovector<ReadOnlyMemTable*> memtables;
autovector<ColumnFamilyData*> cfds;
InstrumentedMutexLock lock(&mutex_);
ColumnFamilySet* cf_set = versions_->GetColumnFamilySet();
// Create WBWIMemTables
for (const auto& [cf_id, stat] : wbwi->GetCFStats()) {
ColumnFamilyData* cfd = cf_set->GetColumnFamily(cf_id);
if (!cfd) {
if (ignore_missing_cf) {
continue;
}
for (auto mem : memtables) {
mem->Unref();
delete mem;
}
for (auto cfd_ptr : cfds) {
cfd_ptr->UnrefAndTryDelete();
}
Status s = Status::InvalidArgument(
"Invalid column family id from WriteBatchWithIndex: " +
std::to_string(cf_id));
if (memtable_updated) {
s = Status::Corruption(
"Part of the write batch is applied. Memtable is in a inconsistent "
"state. " +
s.ToString());
error_handler_.SetBGError(s, BackgroundErrorReason::kMemTable);
}
return s;
}
WBWIMemTable* wbwi_memtable =
new WBWIMemTable(wbwi, cfd->user_comparator(), cf_id, &cfd->ioptions(),
&cfd->GetLatestMutableCFOptions(), stat);
wbwi_memtable->Ref();
wbwi_memtable->AssignSequenceNumbers(assigned_seqno);
// This is needed to keep the WAL that contains Prepare alive until
// committed data in this memtable is persisted.
wbwi_memtable->SetMinPrepLog(prep_log);
memtables.push_back(wbwi_memtable);
cfd->Ref();
cfds.push_back(cfd);
}
// Stop writes to the DB by entering both write threads
WriteThread::Writer nonmem_w;
if (two_write_queues_) {
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
}
WaitForPendingWrites();
// Switch memtable and add WBWIMemTables
Status s;
for (size_t i = 0; i < memtables.size(); ++i) {
assert(!immutable_db_options_.atomic_flush);
// NOTE: to support atomic flush, need to call
// SelectColumnFamiliesForAtomicFlush()
WriteContext write_context;
// TODO: not switch on empty memtable, may need to update metadata
// like NextLogNumber(), earliest_seqno and memtable id.
s = SwitchMemtable(cfds[i], &write_context, memtables[i],
last_seqno_after_ingest);
if (!s.ok()) {
// SwitchMemtable() can only fail if a new WAL is to be created, this
// should only happen for the first call to SwitchMemtable(). log will
// be empty and no new WAL is created for the rest of the calls.
assert(i == 0);
if (i != 0 || memtable_updated) {
// escalate error to non-recoverable
s = Status::Corruption(
"Part of the write batch is applied. Memtable is in a inconsistent "
"state. " +
s.ToString());
error_handler_.SetBGError(s, BackgroundErrorReason::kMemTable);
} else {
// SwitchMemtable() already sets appropriate bg error
}
for (size_t j = i; j < memtables.size(); j++) {
memtables[j]->Unref();
delete memtables[j];
}
break;
}
}
for (size_t i = 0; i < cfds.size(); ++i) {
if (cfds[i]->UnrefAndTryDelete()) {
cfds[i] = nullptr;
}
}
// exit the second queue before returning
if (two_write_queues_) {
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
}
if (s.ok()) {
// Trigger flushes for the new immutable memtables.
for (const auto cfd : cfds) {
if (cfd == nullptr) {
continue;
}
cfd->imm()->FlushRequested();
FlushRequest flush_req;
// TODO: a new flush reason for ingesting memtable
GenerateFlushRequest({cfd}, FlushReason::kExternalFileIngestion,
&flush_req);
EnqueuePendingFlush(flush_req);
}
MaybeScheduleFlushOrCompaction();
}
return s;
}
Status DBImpl::WriteImpl(const WriteOptions& write_options,
WriteBatch* my_batch, WriteCallback* callback,
UserWriteCallback* user_write_cb, uint64_t* log_used,
uint64_t log_ref, bool disable_memtable,
uint64_t* seq_used, size_t batch_cnt,
PreReleaseCallback* pre_release_callback,
PostMemTableCallback* post_memtable_callback) {
PostMemTableCallback* post_memtable_callback,
std::shared_ptr<WriteBatchWithIndex> wbwi,
uint64_t prep_log) {
assert(!seq_per_batch_ || batch_cnt != 0);
assert(my_batch == nullptr || my_batch->Count() == 0 ||
write_options.protection_bytes_per_key == 0 ||
@@ -287,6 +409,23 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
return Status::NotSupported(
"DeleteRange is not compatible with row cache.");
}
if (wbwi) {
assert(prep_log > 0);
// Used only in WriteCommittedTxn::CommitInternal() with no `callback`.
assert(!callback);
if (immutable_db_options_.unordered_write) {
return Status::NotSupported(
"Ingesting WriteBatch does not support unordered_write");
}
if (immutable_db_options_.enable_pipelined_write) {
return Status::NotSupported(
"Ingesting WriteBatch does not support pipelined_write");
}
if (immutable_db_options_.atomic_flush) {
return Status::NotSupported(
"Ingesting WriteBatch does not support atomic_flush");
}
}
// Otherwise IsLatestPersistentState optimization does not make sense
assert(!WriteBatchInternal::IsLatestPersistentState(my_batch) ||
disable_memtable);
@@ -344,7 +483,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
PERF_TIMER_GUARD(write_pre_and_post_process_time);
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
log_ref, disable_memtable, batch_cnt,
pre_release_callback, post_memtable_callback);
pre_release_callback, post_memtable_callback,
/*_ingest_wbwi=*/wbwi != nullptr);
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
write_thread_.JoinBatchGroup(&w);
@@ -441,9 +581,13 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
TEST_SYNC_POINT("DBImpl::WriteImpl:BeforeLeaderEnters");
last_batch_group_size_ =
write_thread_.EnterAsBatchGroupLeader(&w, &write_group);
if (wbwi) {
assert(write_group.size == 1);
}
IOStatus io_s;
Status pre_release_cb_status;
size_t seq_inc = 0;
if (status.ok()) {
// Rules for when we can update the memtable concurrently
// 1. supported by memtable
@@ -494,10 +638,25 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
// Note about seq_per_batch_: either disableWAL is set for the entire write
// group or not. In either case we inc seq for each write batch with no
// failed callback. This means that there could be a batch with
// disalbe_memtable in between; although we do not write this batch to
// disable_memtable in between; although we do not write this batch to
// memtable it still consumes a seq. Otherwise, if !seq_per_batch_, we inc
// the seq per valid written key to mem.
size_t seq_inc = seq_per_batch_ ? valid_batches : total_count;
seq_inc = seq_per_batch_ ? valid_batches : total_count;
if (wbwi) {
// Reserve sequence numbers for the ingested memtable. We need to reserve
// at lease this amount for recovery. During recovery,
// transactions do not commit by ingesting WBWI. The sequence number
// associated with the commit entry in WAL is used as the starting
// sequence number for inserting into memtable. We need to reserve
// enough sequence numbers here (at least the number of operations
// in write batch) to assign to memtable entries for this transaction.
// This prevents updates in different transactions from using out-of-order
// sequence numbers or the same key+seqno.
//
// WBWI ingestion requires not grouping writes, so we don't need to
// consider incrementing sequence number for WBWI from other writers.
seq_inc += wbwi->GetWriteBatch()->Count();
}
const bool concurrent_update = two_write_queues_;
// Update stats while we are an exclusive group leader, so we know
@@ -555,6 +714,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
assert(last_sequence != kMaxSequenceNumber);
const SequenceNumber current_sequence = last_sequence + 1;
last_sequence += seq_inc;
// Seqno assigned to this write are [current_sequence, last_sequence]
if (log_context.need_log_sync) {
VersionEdit synced_wals;
@@ -674,6 +834,31 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
// handle exit, false means somebody else did
should_exit_batch_group = write_thread_.CompleteParallelMemTableWriter(&w);
}
if (wbwi) {
if (status.ok() && w.status.ok()) {
// w.batch contains (potentially empty) commit time batch updates,
// only ingest wbwi if w.batch is applied to memtable successfully
uint32_t memtable_update_count = w.batch->Count();
uint32_t wbwi_count = wbwi->GetWriteBatch()->Count();
// Seqno assigned to this write are [last_seq + 1 - seq_inc, last_seq].
// seq_inc includes w.batch (memtable updates) and wbwi
// w.batch gets first `memtable_update_count` sequence numbers.
// wbwi gets the rest `wbwi_count` sequence numbers.
assert(seq_inc == memtable_update_count + wbwi_count);
assert(wbwi_count > 0);
assert(last_sequence != kMaxSequenceNumber);
SequenceNumber lb = last_sequence + 1 - wbwi_count;
SequenceNumber ub = last_sequence;
if (two_write_queues_) {
assert(ub <= versions_->LastAllocatedSequence());
}
status = IngestWBWI(wbwi, {/*lower_bound=*/lb, /*upper_bound=*/ub},
prep_log, last_sequence,
/*memtable_updated=*/memtable_update_count > 0,
write_options.ignore_missing_column_families);
}
}
if (should_exit_batch_group) {
if (status.ok()) {
for (auto* tmp_w : write_group) {
@@ -1210,7 +1395,6 @@ void DBImpl::MemTableInsertStatusCheck(const Status& status) {
if (!status.ok()) {
mutex_.Lock();
assert(!error_handler_.IsBGWorkStopped());
// Maybe change the return status to void?
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
mutex_.Unlock();
}
@@ -1380,7 +1564,8 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
const WriteOptions& write_options,
log::Writer* log_writer, uint64_t* log_used,
uint64_t* log_size,
LogFileNumberSize& log_file_number_size) {
LogFileNumberSize& log_file_number_size,
SequenceNumber sequence) {
assert(log_size != nullptr);
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
@@ -1406,7 +1591,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
if (!io_s.ok()) {
return io_s;
}
io_s = log_writer->AddRecord(write_options, log_entry);
io_s = log_writer->AddRecord(write_options, log_entry, sequence);
if (UNLIKELY(needs_locking)) {
log_write_mutex_.Unlock();
@@ -1456,7 +1641,7 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
write_options.rate_limiter_priority =
write_group.leader->rate_limiter_priority;
io_s = WriteToWAL(*merged_batch, write_options, log_writer, log_used,
&log_size, log_file_number_size);
&log_size, log_file_number_size, sequence);
if (to_be_cached_state) {
cached_recoverable_state_ = *to_be_cached_state;
cached_recoverable_state_empty_ = false;
@@ -1582,7 +1767,7 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
write_options.rate_limiter_priority =
write_group.leader->rate_limiter_priority;
io_s = WriteToWAL(*merged_batch, write_options, log_writer, log_used,
&log_size, log_file_number_size);
&log_size, log_file_number_size, sequence);
if (to_be_cached_state) {
cached_recoverable_state_ = *to_be_cached_state;
cached_recoverable_state_empty_ = false;
@@ -2204,16 +2389,13 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* /*cfd*/,
mutex_.Lock();
}
// REQUIRES: mutex_ is held
// REQUIRES: this thread is currently at the front of the writer queue
// REQUIRES: this thread is currently at the front of the 2nd writer queue if
// two_write_queues_ is true (This is to simplify the reasoning.)
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
ReadOnlyMemTable* new_imm,
SequenceNumber last_seqno) {
mutex_.AssertHeld();
assert(lock_wal_count_ == 0);
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
const WriteOptions write_options;
log::Writer* new_log = nullptr;
@@ -2246,16 +2428,19 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
}
uint64_t new_log_number =
creating_new_log ? versions_->NewFileNumber() : logfile_number_;
const MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
// For use outside of holding DB mutex
const MutableCFOptions mutable_cf_options_copy =
cfd->GetLatestMutableCFOptions();
// Set memtable_info for memtable sealed callback
// TODO: memtable_info for `new_imm`
MemTableInfo memtable_info;
memtable_info.cf_name = cfd->GetName();
memtable_info.first_seqno = cfd->mem()->GetFirstSequenceNumber();
memtable_info.earliest_seqno = cfd->mem()->GetEarliestSequenceNumber();
memtable_info.num_entries = cfd->mem()->NumEntries();
memtable_info.num_deletes = cfd->mem()->NumDeletion();
if (!cfd->ioptions()->persist_user_defined_timestamps &&
if (!cfd->ioptions().persist_user_defined_timestamps &&
cfd->user_comparator()->timestamp_size() > 0) {
const Slice& newest_udt = cfd->mem()->GetNewestUDT();
memtable_info.newest_udt.assign(newest_udt.data(), newest_udt.size());
@@ -2264,20 +2449,41 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
// flush happens before logging, but that should be ok.
int num_imm_unflushed = cfd->imm()->NumNotFlushed();
const auto preallocate_block_size =
GetWalPreallocateBlockSize(mutable_cf_options.write_buffer_size);
GetWalPreallocateBlockSize(mutable_cf_options_copy.write_buffer_size);
mutex_.Unlock();
if (creating_new_log) {
PredecessorWALInfo info;
log_write_mutex_.Lock();
if (!logs_.empty()) {
log::Writer* cur_log_writer = logs_.back().writer;
info = PredecessorWALInfo(cur_log_writer->get_log_number(),
cur_log_writer->file()->GetFileSize(),
cur_log_writer->GetLastSeqnoRecorded());
}
log_write_mutex_.Unlock();
// TODO: Write buffer size passed in should be max of all CF's instead
// of mutable_cf_options.write_buffer_size.
io_s = CreateWAL(write_options, new_log_number, recycle_log_number,
preallocate_block_size, &new_log);
preallocate_block_size, info, &new_log);
if (s.ok()) {
s = io_s;
}
}
if (s.ok()) {
SequenceNumber seq = versions_->LastSequence();
new_mem = cfd->ConstructNewMemtable(mutable_cf_options, seq);
// FIXME: from the comment for GetEarliestSequenceNumber(), any key with
// seqno >= earliest_seqno should be in this or later memtable. This means
// we should use LastSequence() + 1 or last_seqno + 1 here. And it needs to
// be incremented with file ingestion and other operations that consumes
// sequence number.
SequenceNumber seq;
if (new_imm) {
assert(last_seqno > versions_->LastSequence());
seq = last_seqno;
} else {
seq = versions_->LastSequence();
}
new_mem = cfd->ConstructNewMemtable(mutable_cf_options_copy,
/*earliest_seq=*/seq);
context->superversion_context.NewSuperVersion();
ROCKS_LOG_INFO(immutable_db_options_.info_log,
@@ -2359,6 +2565,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
versions_->PreComputeMinLogNumberWithUnflushedData(logfile_number_);
if (min_wal_number_to_keep >
versions_->GetWalSet().GetMinWalNumberToKeep()) {
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
// Get a snapshot of the empty column families.
// LogAndApply may release and reacquire db
// mutex, during that period, column family may become empty (e.g. its
@@ -2416,10 +2624,21 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
cfd->mem()->SetNextLogNumber(logfile_number_);
assert(new_mem != nullptr);
cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_);
if (new_imm) {
// Need to assign memtable id here before SetMemtable() below assigns id to
// the new live memtable
cfd->AssignMemtableID(new_imm);
// NOTE: new_imm and cfd->mem() references the same WAL and has the same
// NextLogNumber(). They should be flushed together. For non-atomic-flush,
// we always try to flush all immutable memtable. For atomic flush, these
// two memtables will be marked eligible for flush in the same call to
// AssignAtomicFlushSeq().
new_imm->SetNextLogNumber(logfile_number_);
cfd->imm()->Add(new_imm, &context->memtables_to_free_);
}
new_mem->Ref();
cfd->SetMemtable(new_mem);
InstallSuperVersionAndScheduleWork(cfd, &context->superversion_context,
mutable_cf_options);
InstallSuperVersionAndScheduleWork(cfd, &context->superversion_context);
// Notify client that memtable is sealed, now that we have successfully
// installed a new memtable
@@ -2428,6 +2647,9 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
// that is okay. If we did, it most likely means that s was already an error.
// In any case, ignore any unchecked error for i_os here.
io_s.PermitUncheckedError();
// We guarantee that if a non-ok status is returned, `new_imm` was not added
// to the db.
assert(s.ok());
return s;
}
+13 -5
View File
@@ -97,12 +97,20 @@ class CorruptionFS : public FileSystemWrapper {
for (size_t i = 0; i < num_reqs; ++i) {
FSReadRequest& req = reqs[i];
if (fs_.fs_buffer_) {
FSAllocationPtr buffer(new char[req.len], [](void* ptr) {
delete[] static_cast<char*>(ptr);
});
req.fs_scratch = std::move(buffer);
// See https://github.com/facebook/rocksdb/pull/13195 for why we
// want to set up our test implementation for FSAllocationPtr this
// way.
char* internalData = new char[req.len];
req.status = Read(req.offset, req.len, options, &req.result,
static_cast<char*>(req.fs_scratch.get()), dbg);
internalData, dbg);
Slice* internalSlice = new Slice(internalData, req.len);
FSAllocationPtr internalPtr(internalSlice, [](void* ptr) {
delete[] static_cast<const char*>(
static_cast<Slice*>(ptr)->data_);
delete static_cast<Slice*>(ptr);
});
req.fs_scratch = std::move(internalPtr);
} else {
req.status = Read(req.offset, req.len, options, &req.result,
req.scratch, dbg);
+7 -1
View File
@@ -581,8 +581,14 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
} else {
iter_.Next();
}
// This could be a long-running operation due to tombstones, etc.
ROCKSDB_THREAD_YIELD_HOOK();
bool aborted = ROCKSDB_THREAD_YIELD_CHECK_ABORT();
if (aborted) {
valid_ = false;
status_ = Status::Aborted("Query abort.");
return false;
}
} while (iter_.Valid());
valid_ = false;
+139
View File
@@ -2544,6 +2544,145 @@ TEST_P(DBIteratorTest, RefreshWithSnapshot) {
ASSERT_OK(db_->Close());
}
TEST_P(DBIteratorTest, AutoRefreshIterator) {
constexpr int kNumKeys = 1000;
Options options = CurrentOptions();
options.disable_auto_compactions = true;
for (const DBIter::Direction direction :
{DBIter::kForward, DBIter::kReverse}) {
for (const bool auto_refresh_enabled : {false, true}) {
for (const bool explicit_snapshot : {false, true}) {
DestroyAndReopen(options);
// Multi dimensional iterator:
//
// L0 (level iterator): [key000000]
// L1 (table iterator): [key000001]
// Memtable : [key000000, key000999]
for (int i = 0; i < kNumKeys + 2; i++) {
ASSERT_OK(Put(Key(i % kNumKeys), "val" + std::to_string(i)));
if (i <= 1) {
ASSERT_OK(Flush());
}
if (i == 0) {
MoveFilesToLevel(1);
}
}
ReadOptions read_options;
std::unique_ptr<ManagedSnapshot> snapshot = nullptr;
if (explicit_snapshot) {
snapshot = std::make_unique<ManagedSnapshot>(db_);
}
read_options.snapshot =
explicit_snapshot ? snapshot->snapshot() : nullptr;
read_options.auto_refresh_iterator_with_snapshot = auto_refresh_enabled;
std::unique_ptr<Iterator> iter(NewIterator(read_options));
int trigger_compact_on_it = kNumKeys / 2;
// This update should NOT be visible from the iterator.
ASSERT_OK(Put(Key(trigger_compact_on_it + 1), "new val"));
ASSERT_EQ(1, NumTableFilesAtLevel(1));
ASSERT_EQ(1, NumTableFilesAtLevel(0));
uint64_t all_memtables_size_before_refresh;
uint64_t all_memtables_size_after_refresh;
std::string prop_value;
ASSERT_OK(iter->GetProperty("rocksdb.iterator.super-version-number",
&prop_value));
int superversion_number = std::stoi(prop_value);
std::vector<LiveFileMetaData> old_files;
db_->GetLiveFilesMetaData(&old_files);
int expected_next_key_int;
if (direction == DBIter::kForward) {
expected_next_key_int = 0;
iter->SeekToFirst();
} else { // DBIter::kReverse
expected_next_key_int = kNumKeys - 1;
iter->SeekToLast();
}
int it_num = 0;
std::unordered_map<std::string, std::string> kvs;
while (iter->Valid()) {
ASSERT_OK(iter->status());
it_num++;
if (it_num == trigger_compact_on_it) {
// Bump the superversion by manually scheduling flush + compaction.
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr,
nullptr));
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
// For accuracy, capture the memtables size right before consecutive
// iterator call to Next() will update its' stale superversion ref.
dbfull()->GetIntProperty("rocksdb.size-all-mem-tables",
&all_memtables_size_before_refresh);
}
if (it_num == trigger_compact_on_it + 1) {
dbfull()->GetIntProperty("rocksdb.size-all-mem-tables",
&all_memtables_size_after_refresh);
ASSERT_OK(iter->GetProperty("rocksdb.iterator.super-version-number",
&prop_value));
uint64_t new_superversion_number = std::stoi(prop_value);
Status expected_status_for_preexisting_files;
if (auto_refresh_enabled && explicit_snapshot) {
// Iterator is expected to detect its' superversion staleness.
ASSERT_LT(superversion_number, new_superversion_number);
// ... and since our iterator was the only reference to that very
// superversion, we expect most of the active memory to be
// returned upon automatical iterator refresh.
ASSERT_GT(all_memtables_size_before_refresh,
all_memtables_size_after_refresh);
expected_status_for_preexisting_files = Status::NotFound();
} else {
ASSERT_EQ(superversion_number, new_superversion_number);
ASSERT_EQ(all_memtables_size_after_refresh,
all_memtables_size_before_refresh);
expected_status_for_preexisting_files = Status::OK();
}
for (const auto& file : old_files) {
ASSERT_EQ(env_->FileExists(file.db_path + "/" + file.name),
expected_status_for_preexisting_files);
}
}
// Ensure we're visiting the keys in desired order and at most once!
ASSERT_EQ(IdFromKey(iter->key().ToString()), expected_next_key_int);
kvs[iter->key().ToString()] = iter->value().ToString();
if (direction == DBIter::kForward) {
iter->Next();
expected_next_key_int++;
} else {
iter->Prev();
expected_next_key_int--;
}
}
ASSERT_OK(iter->status());
// Data validation.
ASSERT_EQ(kvs.size(), kNumKeys);
for (int i = 0; i < kNumKeys; i++) {
auto kv = kvs.find(Key(i));
ASSERT_TRUE(kv != kvs.end());
int val = i;
if (i <= 1) {
val += kNumKeys;
}
ASSERT_EQ(kv->second, "val" + std::to_string(val));
}
}
}
}
}
TEST_P(DBIteratorTest, CreationFailure) {
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::NewInternalIterator:StatusCallback", [](void* arg) {
-1
View File
@@ -332,7 +332,6 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorBlobs) {
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
::testing::InitGoogleTest(&argc, argv);
+60 -2
View File
@@ -5,11 +5,11 @@
#include "db/db_test_util.h"
#include "port/stack_trace.h"
#include "rocksdb/merge_operator.h"
#include "rocksdb/perf_context.h"
#include "rocksdb/utilities/debug.h"
#include "table/block_based/block_builder.h"
#include "test_util/sync_point.h"
#include "rocksdb/merge_operator.h"
#include "utilities/fault_injection_env.h"
#include "utilities/merge_operators.h"
#include "utilities/merge_operators/sortlist.h"
@@ -120,8 +120,11 @@ TEST_F(DBMergeOperandTest, FlushedMergeOperandReadAfterFreeBug) {
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
Options options = CurrentOptions();
int limit = 2;
// Use only the latest two merge operands.
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
options.merge_operator =
std::make_shared<LimitedStringAppendMergeOp>(limit, ',');
Reopen(options);
int num_records = 4;
int number_of_operands = 0;
@@ -263,6 +266,7 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
"k3.2", values.data(), &merge_operands_info,
&number_of_operands));
ASSERT_EQ(number_of_operands, 2);
ASSERT_EQ(values[0], "cd");
ASSERT_EQ(values[1], "de");
@@ -280,6 +284,7 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
"k4", values.data(), &merge_operands_info,
&number_of_operands));
ASSERT_EQ(number_of_operands, 4);
ASSERT_EQ(values[0], "ba");
ASSERT_EQ(values[1], "cb");
ASSERT_EQ(values[2], "dc");
@@ -299,9 +304,26 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
"k5", values.data(), &merge_operands_info,
&number_of_operands));
ASSERT_EQ(number_of_operands, 4);
ASSERT_EQ(values[0], "remember");
ASSERT_EQ(values[1], "i");
ASSERT_EQ(values[2], "am");
ASSERT_EQ(values[3], "rocks");
// GetMergeOperands() in ReadOnly DB
ASSERT_OK(Merge("k6", "better"));
ASSERT_OK(Merge("k6", "call"));
ASSERT_OK(Merge("k6", "saul"));
ASSERT_OK(ReadOnlyReopen(options));
std::vector<PinnableSlice> readonly_values(num_records);
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
"k6", readonly_values.data(),
&merge_operands_info, &number_of_operands));
ASSERT_EQ(number_of_operands, 3);
ASSERT_EQ(readonly_values[0], "better");
ASSERT_EQ(readonly_values[1], "call");
ASSERT_EQ(readonly_values[2], "saul");
}
TEST_F(DBMergeOperandTest, BlobDBGetMergeOperandsBasic) {
@@ -577,6 +599,42 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBaseDeletionInImmMem) {
}
}
TEST_F(DBMergeOperandTest, GetMergeOperandCallbackStopAtImm) {
Options options = CurrentOptions();
options.max_write_buffer_number = 10;
options.merge_operator = MergeOperators::CreateStringAppendOperator();
DestroyAndReopen(options);
Random rnd(301);
ASSERT_OK(db_->PauseBackgroundWork());
ASSERT_OK(Merge("key", "v1"));
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
// Keep this merge in an immutable memtable
uint64_t num_imm = 0;
ASSERT_TRUE(
db_->GetIntProperty(DB::Properties::kNumImmutableMemTable, &num_imm));
ASSERT_EQ(num_imm, 1);
ASSERT_OK(Merge("key", "v2"));
std::vector<PinnableSlice> merge_operands(2);
GetMergeOperandsOptions merge_operands_info;
merge_operands_info.expected_max_number_of_operands = 2;
int num_fetched = 0;
merge_operands_info.continue_cb = [&num_fetched](Slice /* value */) {
num_fetched++;
// Stop in the first immutable memtable.
return num_fetched < 2;
};
int num_merge_operands = 0;
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
"key", merge_operands.data(),
&merge_operands_info, &num_merge_operands));
ASSERT_EQ(2, num_merge_operands);
ASSERT_EQ(2, num_fetched);
ASSERT_EQ("v1", merge_operands[0]);
ASSERT_EQ("v2", merge_operands[1]);
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+4
View File
@@ -120,6 +120,10 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
ASSERT_OK(Merge("k3", "de"));
ASSERT_OK(db_->Get(ReadOptions(), "k3", &value));
ASSERT_EQ(value, "cd,de");
// Tests that merge operands reach exact limit at memtable.
ASSERT_OK(Merge("k3", "fg"));
ASSERT_OK(db_->Get(ReadOptions(), "k3", &value));
ASSERT_EQ(value, "de,fg");
// All K4 values are in different levels
ASSERT_OK(Merge("k4", "ab"));
+63 -1
View File
@@ -1451,7 +1451,6 @@ TEST_F(DBOptionsTest, ChangeCompression) {
SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBOptionsTest, BottommostCompressionOptsWithFallbackType) {
// Verify the bottommost compression options still take effect even when the
// bottommost compression type is left at its default value. Verify for both
@@ -1596,6 +1595,69 @@ TEST_F(DBOptionsTest, TempOptionsFailTest) {
ASSERT_FALSE(found_temp_file);
}
TEST_F(DBOptionsTest, SetOptionsNoManifestWrite) {
ASSERT_OK(Put("x", "x"));
ASSERT_OK(Flush());
// In addition to checking manifest file, we want to ensure that SetOptions
// is essentially atomic, without releasing the DB mutex between applying
// the options to the cfd and installing new Version and SuperVersion. We
// probabilistically verify that by attempting to catch an inconsistency.
auto* const cfd =
static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())->cfd();
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
std::optional<std::thread> t;
SyncPoint::GetInstance()->SetCallBack(
"VersionSet::LogAndApply:WakeUpAndNotDone", [&](void* arg) {
auto* mu = static_cast<InstrumentedMutex*>(arg);
// Option not yet modified
ASSERT_FALSE(cfd->GetLatestMutableCFOptions().disable_auto_compactions);
ASSERT_FALSE(
cfd->current()->GetMutableCFOptions().disable_auto_compactions);
ASSERT_FALSE(
cfd->GetCurrentMutableCFOptions().disable_auto_compactions);
t = std::thread([mu, cfd]() {
InstrumentedMutexLock l(mu);
// Assuming above correctness, we can only acquire the mutex after
// options fully installed.
ASSERT_TRUE(
cfd->GetLatestMutableCFOptions().disable_auto_compactions);
ASSERT_TRUE(
cfd->current()->GetMutableCFOptions().disable_auto_compactions);
ASSERT_TRUE(
cfd->GetCurrentMutableCFOptions().disable_auto_compactions);
});
});
SyncPoint::GetInstance()->EnableProcessing();
// Baseline manifest file info
std::vector<std::string> live_files;
uint64_t orig_manifest_file_size;
ASSERT_OK(dbfull()->GetLiveFiles(live_files, &orig_manifest_file_size));
uint64_t orig_manifest_file_num = dbfull()->TEST_Current_Manifest_FileNo();
// Although this test mostly concerns SetOptions, we also include SetDBOptions
// just for the added scope
ASSERT_OK(db_->SetDBOptions({{"max_open_files", "100"}}));
ASSERT_OK(db_->SetOptions({{"disable_auto_compactions", "true"}}));
// Verify that our above check was activated and completed
ASSERT_TRUE(t.has_value());
t->join();
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// Verify manifest was not written to
uint64_t new_manifest_file_size;
ASSERT_OK(dbfull()->GetLiveFiles(live_files, &new_manifest_file_size));
uint64_t new_manifest_file_num = dbfull()->TEST_Current_Manifest_FileNo();
ASSERT_EQ(orig_manifest_file_num, new_manifest_file_num);
ASSERT_EQ(orig_manifest_file_size, new_manifest_file_size);
ASSERT_EQ(Get("x"), "x");
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
-2
View File
@@ -226,7 +226,6 @@ TEST_P(DBRateLimiterOnReadTest, Iterator) {
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
}
TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
if (use_direct_io_ && !IsDirectIOSupported()) {
return;
@@ -271,7 +270,6 @@ TEST_P(DBRateLimiterOnReadTest, VerifyFileChecksums) {
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
}
class DBRateLimiterOnWriteTest : public DBTestBase {
public:
explicit DBRateLimiterOnWriteTest()
+38 -2
View File
@@ -14,10 +14,9 @@
#include "rocksdb/utilities/transaction_db.h"
#include "test_util/sync_point.h"
#include "test_util/testutil.h"
#include "utilities/fault_injection_env.h"
#include "utilities/merge_operators/string_append/stringappend2.h"
namespace ROCKSDB_NAMESPACE {
class DBSecondaryTestBase : public DBBasicTestWithTimestampBase {
public:
explicit DBSecondaryTestBase(const std::string& dbname)
@@ -331,6 +330,43 @@ TEST_F(DBSecondaryTest, InternalCompactionMultiLevels) {
// cfh, input1, &result));
}
TEST_F(DBSecondaryTest, GetMergeOperands) {
Options options;
options.merge_operator = MergeOperators::CreateStringAppendOperator();
options.env = env_;
Reopen(options);
ASSERT_OK(Merge("k1", "v1"));
ASSERT_OK(Merge("k1", "v2"));
ASSERT_OK(Merge("k1", "v3"));
ASSERT_OK(Merge("k1", "v4"));
options.max_open_files = -1;
OpenSecondary(options);
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
int num_records = 4;
int number_of_operands = 0;
std::vector<PinnableSlice> values(num_records);
GetMergeOperandsOptions merge_operands_info;
merge_operands_info.expected_max_number_of_operands = num_records;
auto cfh = db_secondary_->DefaultColumnFamily();
const Status s = db_secondary_->GetMergeOperands(
ReadOptions(), cfh, "k1", values.data(), &merge_operands_info,
&number_of_operands);
ASSERT_NOK(s);
ASSERT_TRUE(s.IsMergeInProgress());
ASSERT_EQ(number_of_operands, 4);
ASSERT_EQ(values[0].ToString(), "v1");
ASSERT_EQ(values[1].ToString(), "v2");
ASSERT_EQ(values[2].ToString(), "v3");
ASSERT_EQ(values[3].ToString(), "v4");
}
TEST_F(DBSecondaryTest, InternalCompactionCompactedFiles) {
Options options;
options.env = env_;
-1
View File
@@ -1991,7 +1991,6 @@ TEST_F(DBSSTTest, DBWithSFMForBlobFilesAtomicFlush) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
-1
View File
@@ -198,7 +198,6 @@ TEST_F(DBStatisticsTest, ExcludeTickers) {
ASSERT_GT(options.statistics->getTickerCount(BYTES_READ), 0);
}
TEST_F(DBStatisticsTest, VerifyChecksumReadStat) {
Options options = CurrentOptions();
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
-2
View File
@@ -25,7 +25,6 @@
#include "util/atomic.h"
#include "util/random.h"
namespace ROCKSDB_NAMESPACE {
// A helper function that ensures the table properties returned in
@@ -744,7 +743,6 @@ INSTANTIATE_TEST_CASE_P(DBTablePropertiesTest, DBTablePropertiesTest,
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
::testing::InitGoogleTest(&argc, argv);
-1
View File
@@ -703,7 +703,6 @@ TEST_P(DBTestTailingIterator, SeekToFirstWithUpperBoundBug) {
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
::testing::InitGoogleTest(&argc, argv);
+174 -11
View File
@@ -1161,7 +1161,6 @@ class DelayFilterFactory : public CompactionFilterFactory {
};
} // anonymous namespace
static std::string CompressibleString(Random* rnd, int len) {
std::string r;
test::CompressibleString(rnd, 0.8, len, &r);
@@ -3410,8 +3409,6 @@ class ModelDB : public DB {
return Status::NotSupported();
}
Status DeleteFile(std::string /*name*/) override { return Status::OK(); }
Status GetUpdatesSince(
ROCKSDB_NAMESPACE::SequenceNumber,
std::unique_ptr<ROCKSDB_NAMESPACE::TransactionLogIterator>*,
@@ -3715,6 +3712,139 @@ TEST_F(DBTest, BlockBasedTablePrefixIndexTest) {
ASSERT_EQ("v2", Get("k2"));
}
TEST_F(DBTest, SetOptionsEffectiveInSuperVersions) {
// Basically, to test the SetOptions take effect with (and only with)
// new SuperVersions, and remain in effect through some things like
// flush and compaction, we use some queries that depend on the current
// prefix extractor.
//
// Making the semantics of read options dependent on the current state of
// mutable options is kind of an anti-pattern that prefix_seek_opt_in_only
// is helping to phase out. However, this is useful for rather directly
// testing the expected behavior of mutable options handling.
ReadOptions ropts;
ropts.prefix_same_as_start = true;
Options options = CurrentOptions();
options.prefix_extractor.reset(NewFixedPrefixTransform(5));
options.prefix_seek_opt_in_only = false;
Reopen(options);
ASSERT_OK(Put("goat1", "g1"));
ASSERT_OK(Put("goat2", "g2"));
std::unique_ptr<Iterator> iter(db_->NewIterator(ropts));
auto VerifyTransform4 = [&](int caller_line) {
SCOPED_TRACE("Called from " + std::to_string(caller_line));
// Nothing with this prefix
iter->Seek("game1");
ASSERT_OK(iter->status());
ASSERT_FALSE(iter->Valid());
iter->Seek("goat1");
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->Valid());
ASSERT_EQ("goat1", iter->key());
iter->Next();
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->Valid());
ASSERT_EQ("goat2", iter->key());
};
auto VerifyTransform5 = [&](int caller_line) {
SCOPED_TRACE("Called from " + std::to_string(caller_line));
// Nothing with this prefix
iter->Seek("game1");
ASSERT_OK(iter->status());
ASSERT_FALSE(iter->Valid());
iter->Seek("goat1");
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->Valid());
ASSERT_EQ("goat1", iter->key());
iter->Next();
ASSERT_OK(iter->status());
ASSERT_FALSE(iter->Valid());
iter->Seek("goat2");
ASSERT_OK(iter->status());
ASSERT_TRUE(iter->Valid());
ASSERT_EQ("goat2", iter->key());
iter->Next();
ASSERT_OK(iter->status());
ASSERT_FALSE(iter->Valid());
};
for (int i = 0;; ++i) {
SCOPED_TRACE("Iteration " + std::to_string(i));
// Baseline
VerifyTransform5(__LINE__);
if (i == 0) {
// Test a "normal" change with nothing happening in parallel
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "fixed:4"}}));
// Iterator still uses old superversion
VerifyTransform5(__LINE__);
// Refresh updates the SuperVersion
ASSERT_OK(iter->Refresh());
} else if (i == 1) {
// Test a setting change in parallel with flush
iter = nullptr;
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->SetCallBack(
"FlushJob::WriteLevel0Table:num_memtables", [&](void*) {
// During flush, without DB mutex held
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "fixed:4"}}));
iter.reset(db_->NewIterator(ropts));
VerifyTransform4(__LINE__);
});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Flush());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// Callback was called
ASSERT_NE(iter, nullptr);
} else if (i == 2) {
// Test a setting change in parallel with compaction
iter = nullptr;
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::Run():EndStatusSet", [&](void*) {
// During compaction, without DB mutex held
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "fixed:4"}}));
iter.reset(db_->NewIterator(ropts));
VerifyTransform4(__LINE__);
});
SyncPoint::GetInstance()->EnableProcessing();
// Need data overlapping that L0 file to prevent trivial move
ASSERT_OK(Put("aaaaa", "a"));
ASSERT_OK(Put("zzzzz", "a"));
ASSERT_OK(CompactRange({}, {}, {}));
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
// Callback was called
ASSERT_NE(iter, nullptr);
} else {
break;
}
// Change has taken effect
VerifyTransform4(__LINE__);
// Same after a new iterator (in case a new SuperVersion reverted the
// setting)
iter.reset(db_->NewIterator(ropts));
VerifyTransform4(__LINE__);
// Back to baseline setting
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "fixed:5"}}));
// New iterator uses latest SuperVersion
iter.reset(db_->NewIterator(ropts));
}
}
TEST_F(DBTest, BlockBasedTablePrefixHashIndexTest) {
// create a DB with block prefix index
BlockBasedTableOptions table_options;
@@ -4378,7 +4508,6 @@ TEST_F(DBTest, ConcurrentMemtableNotSupported) {
ASSERT_NOK(db_->CreateColumnFamily(cf_options, "name", &handle));
}
TEST_F(DBTest, SanitizeNumThreads) {
for (int attempt = 0; attempt < 2; attempt++) {
const size_t kTotalTasks = 8;
@@ -5161,7 +5290,6 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
for (int i = 0; i < kNKeys; i++) {
keys[i] = i;
}
RandomShuffle(std::begin(keys), std::end(keys));
Random rnd(301);
Options options;
@@ -5186,7 +5314,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
options.compression_per_level[0] = kNoCompression;
// No compression for the Ln whre L0 is compacted to
options.compression_per_level[1] = kNoCompression;
// Snpapy compression for Ln+1
// Snappy compression for Ln+1
options.compression_per_level[2] = kSnappyCompression;
OnFileDeletionListener* listener = new OnFileDeletionListener();
@@ -5198,6 +5326,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
// be compressed, so there shouldn't be any compression.
for (int i = 0; i < 20; i++) {
ASSERT_OK(Put(Key(keys[i]), CompressibleString(&rnd, 4000)));
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
@@ -5216,8 +5345,9 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
// above compression settings for each level, there will be some compression.
ASSERT_OK(options.statistics->Reset());
ASSERT_EQ(num_block_compressed, 0);
for (int i = 21; i < 120; i++) {
for (int i = 20; i < 120; i++) {
ASSERT_OK(Put(Key(keys[i]), CompressibleString(&rnd, 4000)));
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
@@ -5238,9 +5368,45 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
}));
ColumnFamilyMetaData cf_meta;
db_->GetColumnFamilyMetaData(&cf_meta);
// Ensure that L1+ files are non-overlapping and together with L0 encompass
// full key range between smallestkey and largestkey from CF file metadata.
int largestkey_in_prev_level = -1;
int keys_found = 0;
for (int level = (int)cf_meta.levels.size() - 1; level >= 0; level--) {
int files_in_level = (int)cf_meta.levels[level].files.size();
int largestkey_in_prev_file = -1;
for (int j = 0; j < files_in_level; j++) {
int smallestkey = IdFromKey(cf_meta.levels[level].files[j].smallestkey);
int largestkey = IdFromKey(cf_meta.levels[level].files[j].largestkey);
int num_entries = (int)cf_meta.levels[level].files[j].num_entries;
ASSERT_EQ(num_entries, largestkey - smallestkey + 1);
keys_found += num_entries;
if (level > 0) {
if (j == 0) {
ASSERT_GT(smallestkey, largestkey_in_prev_level);
}
if (j > 0) {
ASSERT_GT(smallestkey, largestkey_in_prev_file);
}
if (j == files_in_level - 1) {
largestkey_in_prev_level = largestkey;
}
}
largestkey_in_prev_file = largestkey;
}
}
ASSERT_EQ(keys_found, kNKeys);
for (const auto& file : cf_meta.levels[4].files) {
listener->SetExpectedFileName(dbname_ + file.name);
ASSERT_OK(dbfull()->DeleteFile(file.name));
Slice start(file.smallestkey), limit(file.largestkey);
const RangePtr ranges(&start, &limit);
// Given verification from above, we're guaranteed that by deleting all the
// files in [<smallestkey>, <largestkey>] range, we're effectively deleting
// that very single file and nothing more.
EXPECT_OK(dbfull()->DeleteFilesInRanges(dbfull()->DefaultColumnFamily(),
&ranges, true /* include_end */));
}
listener->VerifyMatchedCount(cf_meta.levels[4].files.size());
@@ -5788,7 +5954,6 @@ TEST_F(DBTest, FileCreationRandomFailure) {
}
}
TEST_F(DBTest, DynamicMiscOptions) {
// Test max_sequential_skip_in_iterations
Options options;
@@ -7230,7 +7395,6 @@ TEST_F(DBTest, ReusePinnableSlice) {
1);
}
TEST_F(DBTest, DeletingOldWalAfterDrop) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"Test:AllowFlushes", "DBImpl::BGWorkFlush"},
@@ -7355,7 +7519,6 @@ TEST_F(DBTest, LargeBlockSizeTest) {
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
}
TEST_F(DBTest, CreationTimeOfOldestFile) {
const int kNumKeysPerFile = 32;
const int kNumLevelFiles = 2;
+65 -17
View File
@@ -16,6 +16,7 @@
#include "db/db_test_util.h"
#include "db/read_callback.h"
#include "db/version_edit.h"
#include "env/fs_readonly.h"
#include "options/options_helper.h"
#include "port/port.h"
#include "port/stack_trace.h"
@@ -134,7 +135,6 @@ TEST_F(DBTest2, PartitionedIndexUserToInternalKey) {
}
}
class PrefixFullBloomWithReverseComparator
: public DBTestBase,
public ::testing::WithParamInterface<bool> {
@@ -1475,8 +1475,7 @@ TEST_P(PresetCompressionDictTest, Flush) {
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
// number of bytes needs to be adjusted in case the cached block is in
// ZSTD's digested dictionary format.
if (compression_type_ != kZSTD &&
compression_type_ != kZSTDNotFinalCompression) {
if (compression_type_ != kZSTD) {
// Although we limited buffering to `kBlockLen`, there may be up to two
// blocks of data included in the dictionary since we only check limit
// after each block is built.
@@ -1553,8 +1552,7 @@ TEST_P(PresetCompressionDictTest, CompactNonBottommost) {
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
// number of bytes needs to be adjusted in case the cached block is in
// ZSTD's digested dictionary format.
if (compression_type_ != kZSTD &&
compression_type_ != kZSTDNotFinalCompression) {
if (compression_type_ != kZSTD) {
// Although we limited buffering to `kBlockLen`, there may be up to two
// blocks of data included in the dictionary since we only check limit
// after each block is built.
@@ -1615,8 +1613,7 @@ TEST_P(PresetCompressionDictTest, CompactBottommost) {
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
// number of bytes needs to be adjusted in case the cached block is in ZSTD's
// digested dictionary format.
if (compression_type_ != kZSTD &&
compression_type_ != kZSTDNotFinalCompression) {
if (compression_type_ != kZSTD) {
// Although we limited buffering to `kBlockLen`, there may be up to two
// blocks of data included in the dictionary since we only check limit after
// each block is built.
@@ -1976,7 +1973,6 @@ TEST_F(DBTest2, CompactionStall) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBTest2, FirstSnapshotTest) {
Options options;
options.write_buffer_size = 100000; // Small write buffer
@@ -2103,16 +2099,15 @@ TEST_P(PinL0IndexAndFilterBlocksTest,
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD));
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_MISS));
std::string value;
// Miss and hit count should remain the same, they're all pinned.
ASSERT_TRUE(db_->KeyMayExist(ReadOptions(), handles_[1], "key", &value));
ASSERT_TRUE(db_->KeyMayExist(ReadOptions(), handles_[1], "key", nullptr));
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
// Miss and hit count should remain the same, they're all pinned.
value = Get(1, "key");
std::string value = Get(1, "key");
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
@@ -3600,7 +3595,6 @@ TEST_F(DBTest2, OptimizeForSmallDB) {
value.Reset();
}
TEST_F(DBTest2, IterRaceFlush1) {
ASSERT_OK(Put("foo", "v1"));
@@ -4104,7 +4098,6 @@ TEST_F(DBTest2, ReadCallbackTest) {
}
}
TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
// Regression test for race condition where an obsolete file is returned to
// user as a "live file" but then deleted, all while file deletions are
@@ -4432,7 +4425,7 @@ TEST_F(DBTest2, TraceAndReplay) {
db2->NewDefaultReplayer(handles, std::move(trace_reader), &replayer));
TraceExecutionResultHandler res_handler;
std::function<void(Status, std::unique_ptr<TraceRecordResult> &&)> res_cb =
std::function<void(Status, std::unique_ptr<TraceRecordResult>&&)> res_cb =
[&res_handler](Status exec_s, std::unique_ptr<TraceRecordResult>&& res) {
ASSERT_TRUE(exec_s.ok() || exec_s.IsNotSupported());
if (res != nullptr) {
@@ -5164,7 +5157,6 @@ TEST_F(DBTest2, TraceWithFilter) {
ASSERT_EQ(count, 6);
}
TEST_F(DBTest2, PinnableSliceAndMmapReads) {
Options options = CurrentOptions();
options.env = env_;
@@ -7693,7 +7685,6 @@ TEST_F(DBTest2, RecoverEpochNumber) {
}
}
TEST_F(DBTest2, RenameDirectory) {
Options options = CurrentOptions();
DestroyAndReopen(options);
@@ -8006,7 +7997,7 @@ TEST_F(DBTest2, GetLatestSeqAndTsForKey) {
ASSERT_EQ(0, options.statistics->getTickerCount(GET_HIT_L0));
}
#if defined(ZSTD_ADVANCED)
#if defined(ZSTD)
TEST_F(DBTest2, ZSTDChecksum) {
// Verify that corruption during decompression is caught.
Options options = CurrentOptions();
@@ -8062,6 +8053,63 @@ TEST_F(DBTest2, TableCacheMissDuringReadFromBlockCacheTier) {
ASSERT_EQ(orig_num_file_opens, TestGetTickerCount(options, NO_FILE_OPENS));
}
TEST_F(DBTest2, GetFileChecksumsFromCurrentManifest_CRC32) {
Options opts = CurrentOptions();
opts.create_if_missing = true;
opts.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
opts.level0_file_num_compaction_trigger = 10;
// Bootstrap the test database.
DB* db = nullptr;
std::string dbname = test::PerThreadDBPath("file_chksum");
ASSERT_OK(DB::Open(opts, dbname, &db));
WriteOptions wopts;
FlushOptions fopts;
fopts.wait = true;
Random rnd(test::RandomSeed());
for (int i = 0; i < 4; i++) {
ASSERT_OK(db->Put(wopts, Key(i), rnd.RandomString(100)));
ASSERT_OK(db->Flush(fopts));
}
// Obtain rich files metadata for source of truth.
std::vector<LiveFileMetaData> live_files;
db->GetLiveFilesMetaData(&live_files);
ASSERT_OK(db->Close());
delete db;
db = nullptr;
// Process current MANIFEST file and build internal file checksum mappings.
std::unique_ptr<FileChecksumList> checksum_list(NewFileChecksumList());
auto read_only_fs =
std::make_shared<ReadOnlyFileSystem>(env_->GetFileSystem());
ASSERT_OK(experimental::GetFileChecksumsFromCurrentManifest(
read_only_fs.get(), dbname, checksum_list.get()));
ASSERT_TRUE(checksum_list != nullptr);
// Retrieve files, related checksums and checksum functions.
std::vector<uint64_t> file_numbers;
std::vector<std::string> checksums;
std::vector<std::string> checksum_func_names;
ASSERT_OK(checksum_list->GetAllFileChecksums(&file_numbers, &checksums,
&checksum_func_names));
// Compare results.
ASSERT_EQ(live_files.size(), checksum_list->size());
for (size_t i = 0; i < live_files.size(); i++) {
std::string stored_checksum;
std::string stored_func_name;
ASSERT_OK(checksum_list->SearchOneFileChecksum(
live_files[i].file_number, &stored_checksum, &stored_func_name));
ASSERT_EQ(live_files[i].file_checksum, stored_checksum);
ASSERT_EQ(live_files[i].file_checksum_func_name, stored_func_name);
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+79 -50
View File
@@ -122,7 +122,6 @@ DBTestBase::~DBTestBase() {
}
bool DBTestBase::ShouldSkipOptions(int option_config, int skip_mask) {
if ((skip_mask & kSkipUniversalCompaction) &&
(option_config == kUniversalCompaction ||
option_config == kUniversalCompactionMultiLevel ||
@@ -1208,7 +1207,6 @@ std::string DBTestBase::FilesPerLevel(int cf) {
return result;
}
std::vector<uint64_t> DBTestBase::GetBlobFileNumbers() {
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
@@ -1600,42 +1598,74 @@ std::vector<std::uint64_t> DBTestBase::ListTableFiles(Env* env,
return file_numbers;
}
void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
size_t* total_reads_res, bool tailing_iter,
std::map<std::string, Status> status) {
size_t total_reads = 0;
void DBTestBase::VerifyDBFromMap(
std::map<std::string, std::string> true_data, size_t* total_reads_res,
bool tailing_iter, ReadOptions* ro, ColumnFamilyHandle* cf,
std::unordered_set<std::string>* not_found) const {
ReadOptions temp_ro;
if (!ro) {
ro = &temp_ro;
ro->verify_checksums = true;
}
if (!cf) {
cf = db_->DefaultColumnFamily();
}
for (auto& kv : true_data) {
Status s = status[kv.first];
if (s.ok()) {
ASSERT_EQ(Get(kv.first), kv.second);
} else {
std::string value;
ASSERT_EQ(s, db_->Get(ReadOptions(), kv.first, &value));
}
// Get
size_t total_reads = 0;
std::string result;
for (auto& [k, v] : true_data) {
ASSERT_OK(db_->Get(*ro, cf, k, &result)) << "key is " << k;
ASSERT_EQ(v, result);
total_reads++;
}
if (not_found) {
for (const auto& k : *not_found) {
ASSERT_TRUE(db_->Get(*ro, cf, k, &result).IsNotFound())
<< "key is " << k << " val is " << result;
}
}
// MultiGet
std::vector<Slice> key_slice;
for (const auto& [k, _] : true_data) {
key_slice.emplace_back(k);
}
std::vector<std::string> values;
std::vector<ColumnFamilyHandle*> cfs(key_slice.size(), cf);
std::vector<Status> status = db_->MultiGet(*ro, cfs, key_slice, &values);
total_reads += key_slice.size();
auto data_iter = true_data.begin();
for (size_t i = 0; i < key_slice.size(); ++i, ++data_iter) {
ASSERT_OK(status[i]);
ASSERT_EQ(values[i], data_iter->second);
}
// MultiGet - not found
if (not_found) {
key_slice.clear();
for (const auto& k : *not_found) {
key_slice.emplace_back(k);
}
cfs = std::vector<ColumnFamilyHandle*>(key_slice.size(), cf);
values.clear();
status = db_->MultiGet(*ro, cfs, key_slice, &values);
for (const auto& s : status) {
ASSERT_TRUE(s.IsNotFound());
}
}
// Normal Iterator
{
int iter_cnt = 0;
ReadOptions ro;
ro.total_order_seek = true;
Iterator* iter = db_->NewIterator(ro);
ReadOptions ro_ = *ro;
ro_.total_order_seek = true;
Iterator* iter = db_->NewIterator(ro_, cf);
// Verify Iterator::Next()
iter_cnt = 0;
auto data_iter = true_data.begin();
Status s;
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), data_iter++) {
data_iter = true_data.begin();
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), ++data_iter) {
ASSERT_EQ(iter->key().ToString(), data_iter->first);
Status current_status = status[data_iter->first];
if (!current_status.ok()) {
s = current_status;
}
ASSERT_EQ(iter->status(), s);
if (current_status.ok()) {
ASSERT_EQ(iter->value().ToString(), data_iter->second);
}
ASSERT_EQ(iter->value().ToString(), data_iter->second);
iter_cnt++;
total_reads++;
}
@@ -1646,20 +1676,12 @@ void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
// Verify Iterator::Prev()
// Use a new iterator to make sure its status is clean.
iter = db_->NewIterator(ro);
iter = db_->NewIterator(ro_, cf);
iter_cnt = 0;
s = Status::OK();
auto data_rev = true_data.rbegin();
for (iter->SeekToLast(); iter->Valid(); iter->Prev(), data_rev++) {
ASSERT_EQ(iter->key().ToString(), data_rev->first);
Status current_status = status[data_rev->first];
if (!current_status.ok()) {
s = current_status;
}
ASSERT_EQ(iter->status(), s);
if (current_status.ok()) {
ASSERT_EQ(iter->value().ToString(), data_rev->second);
}
ASSERT_EQ(iter->value().ToString(), data_rev->second);
iter_cnt++;
total_reads++;
}
@@ -1667,12 +1689,20 @@ void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
ASSERT_EQ(data_rev, true_data.rend())
<< iter_cnt << " / " << true_data.size();
// Verify Iterator::Seek()
for (const auto& kv : true_data) {
iter->Seek(kv.first);
ASSERT_EQ(kv.first, iter->key().ToString());
ASSERT_EQ(kv.second, iter->value().ToString());
total_reads++;
// Verify Iterator::Seek() and SeekForPrev()
for (const auto& [k, v] : true_data) {
for (bool prev : {false, true}) {
if (prev) {
iter->SeekForPrev(k);
} else {
iter->Seek(k);
}
ASSERT_TRUE(iter->Valid());
ASSERT_OK(iter->status());
ASSERT_EQ(iter->key(), k);
ASSERT_EQ(iter->value(), v);
++total_reads;
}
}
delete iter;
}
@@ -1680,14 +1710,14 @@ void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
if (tailing_iter) {
// Tailing iterator
int iter_cnt = 0;
ReadOptions ro;
ro.tailing = true;
ro.total_order_seek = true;
Iterator* iter = db_->NewIterator(ro);
ReadOptions ro_ = *ro;
ro_.tailing = true;
ro_.total_order_seek = true;
Iterator* iter = db_->NewIterator(ro_, cf);
// Verify ForwardIterator::Next()
iter_cnt = 0;
auto data_iter = true_data.begin();
data_iter = true_data.begin();
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), data_iter++) {
ASSERT_EQ(iter->key().ToString(), data_iter->first);
ASSERT_EQ(iter->value().ToString(), data_iter->second);
@@ -1733,7 +1763,6 @@ void DBTestBase::VerifyDBInternal(
iter->~InternalIterator();
}
uint64_t DBTestBase::GetNumberOfSstFilesForColumnFamily(
DB* db, std::string column_family_name) {
std::vector<LiveFileMetaData> metadata;
+7 -1
View File
@@ -1106,6 +1106,11 @@ class DBTestBase : public testing::Test {
return std::string(buf);
}
// Expects valid key created by Key().
static int IdFromKey(const std::string& key) {
return std::stoi(key.substr(3));
}
static bool ShouldSkipOptions(int option_config, int skip_mask = kNoSkip);
// Switch to a fresh database with the next option configuration to
@@ -1367,7 +1372,8 @@ class DBTestBase : public testing::Test {
void VerifyDBFromMap(
std::map<std::string, std::string> true_data,
size_t* total_reads_res = nullptr, bool tailing_iter = false,
std::map<std::string, Status> status = std::map<std::string, Status>());
ReadOptions* ro = nullptr, ColumnFamilyHandle* cf = nullptr,
std::unordered_set<std::string>* not_found = nullptr) const;
void VerifyDBInternal(
std::vector<std::pair<std::string, std::string>> true_data);
-1
View File
@@ -2369,7 +2369,6 @@ TEST_F(DBTestUniversalCompaction2, PeriodicCompactionOffpeak) {
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
::testing::InitGoogleTest(&argc, argv);
+201 -12
View File
@@ -1153,6 +1153,7 @@ TEST_F(DBWALTest, FullPurgePreservesRecycledLog) {
dbfull()->FindObsoleteFiles(&job_context, true /* force */);
dbfull()->TEST_UnlockMutex();
dbfull()->PurgeObsoleteFiles(job_context);
job_context.Clean();
if (i == 0) {
ASSERT_OK(
@@ -1838,9 +1839,194 @@ class RecoveryTestHelper {
}
};
class DBWALTestWithParams : public DBWALTestBase,
public ::testing::WithParamInterface<
std::tuple<bool, int, int, CompressionType>> {
TEST_F(DBWALTest, TrackAndVerifyWALsRecycleWAL) {
Options options = CurrentOptions();
options.avoid_flush_during_shutdown = true;
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
options.recycle_log_file_num = 1;
options.track_and_verify_wals = true;
DestroyAndReopen(options);
ASSERT_OK(Put("key_ignore", "wal_to_recycle"));
ASSERT_OK(Put("key_ignore1", "wal_to_recycle"));
ASSERT_OK(Put("key_ignore2", "wal_to_recycle"));
ASSERT_OK(Flush());
ASSERT_OK(Put("key_ignore", "wal_to_recycle"));
ASSERT_OK(Put("key_ignore1", "wal_to_recycle"));
ASSERT_OK(Put("key_ignore2", "wal_to_recycle"));
ASSERT_OK(Flush());
// Stop background flush to avoid deleting any WAL
options.env->SetBackgroundThreads(1, Env::HIGH);
test::SleepingBackgroundTask sleeping_task;
options.env->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
&sleeping_task, Env::Priority::HIGH);
// Recycle the first WAL
ASSERT_OK(Put("key1", "old_value"));
// Recycle the second WAL
ASSERT_OK(dbfull()->TEST_SwitchWAL());
ASSERT_OK(Put("key1", "new_value"));
// Create a WAL hole on sequence number by truncating the first WAL to 0 byte
VectorWalPtr log_files;
ASSERT_OK(db_->GetSortedWalFiles(log_files));
ASSERT_EQ(log_files.size(), 2);
std::string log_name = LogFileName(dbname_, log_files.front()->LogNumber());
Close();
// Drop `Put("key1", "old_value")` in the first WAL
ASSERT_OK(test::TruncateFile(options.env, log_name, 0 /* new_length */));
Status s = DB::Open(options, dbname_, &db_);
ASSERT_OK(s);
ASSERT_EQ("wal_to_recycle", Get("key_ignore2"));
ASSERT_EQ("NOT_FOUND", Get("key1"));
Close();
}
class DBWALTrackAndVerifyWALsWithParamsTest
: public DBWALTestBase,
public ::testing::WithParamInterface<WALRecoveryMode> {
public:
DBWALTrackAndVerifyWALsWithParamsTest()
: DBWALTestBase("/db_wal_track_and_verify_wals_with_params_test") {}
};
INSTANTIATE_TEST_CASE_P(
DBWALTrackAndVerifyWALsWithParamsTest,
DBWALTrackAndVerifyWALsWithParamsTest,
::testing::Values(WALRecoveryMode::kTolerateCorruptedTailRecords,
WALRecoveryMode::kAbsoluteConsistency,
WALRecoveryMode::kPointInTimeRecovery,
WALRecoveryMode::kSkipAnyCorruptedRecords));
TEST_P(DBWALTrackAndVerifyWALsWithParamsTest, Basic) {
Options options = CurrentOptions();
options.avoid_flush_during_shutdown = true;
options.track_and_verify_wals = true;
options.wal_recovery_mode = GetParam();
// Stop background flush to avoid deleting any WAL
options.env->SetBackgroundThreads(1, Env::HIGH);
test::SleepingBackgroundTask sleeping_task;
options.env->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
&sleeping_task, Env::Priority::HIGH);
for (int i = 0; i < 5; i++) {
DestroyAndReopen(options);
ASSERT_OK(Put("key1", "old_value"));
SequenceNumber last_seqno_recorded_in_fist_wal =
dbfull()->GetLatestSequenceNumber();
ASSERT_OK(dbfull()->TEST_SwitchWAL());
ASSERT_OK(Put("key1", "new_value"));
VectorWalPtr log_files;
ASSERT_OK(db_->GetSortedWalFiles(log_files));
ASSERT_EQ(log_files.size(), 2);
uint64_t first_log_number = log_files.front()->LogNumber();
std::string first_log_name = LogFileName(dbname_, first_log_number);
std::string second_log_name =
LogFileName(dbname_, log_files.back()->LogNumber());
if (i == 0) {
// Delete the obsolete WAL and verify it will not be seen as a WAL hole
sleeping_task.WakeUp();
sleeping_task.WaitUntilDone();
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
// Stop background flush to avoid deleting any WAL
sleeping_task.Reset();
options.env->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
&sleeping_task, Env::Priority::HIGH);
Close();
} else if (i == 1) {
// Create a WAL hole on WAL number by deleting the first WAL and verify
// the hole will be detected
Close();
ASSERT_OK(options.env->DeleteFile(first_log_name));
} else if (i == 2) {
// Create a WAL hole on sequence number by truncating the first WAL and
// verify the hole will be detected
Close();
ASSERT_OK(
test::TruncateFile(options.env, first_log_name, 0 /* new_length */));
} else if (i == 3) {
// Create a WAL hole on size difference by truncating the first WAL and
// mocking a correct sequence number to force triggering corruption based
// on size instead of sequence number and verify the hole will be detected
Close();
ASSERT_OK(
test::TruncateFile(options.env, first_log_name, 0 /* new_length */));
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::UpdatePredecessorWALInfo", [&](void* arg) {
std::pair<uint64_t, SequenceNumber*>* pair =
static_cast<std::pair<uint64_t, SequenceNumber*>*>(arg);
if (pair->first == first_log_number) {
*(pair->second) = last_seqno_recorded_in_fist_wal;
}
});
SyncPoint::GetInstance()->EnableProcessing();
} else if (i == 4) {
// Delete all wals and verify opening a DB with no WAL will be detected
Close();
ASSERT_OK(options.env->DeleteFile(first_log_name));
ASSERT_OK(options.env->DeleteFile(second_log_name));
}
Status s = DB::Open(options, dbname_, &db_);
if (i == 0) {
ASSERT_OK(s);
ASSERT_EQ("new_value", Get("key1"));
continue;
} else if (i == 3) {
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->DisableProcessing();
} else if (i == 4) {
ASSERT_TRUE(s.IsCorruption());
ASSERT_TRUE(
s.ToString().find("Opening an existing DB with no WAL files") !=
std::string::npos);
Close();
continue;
}
if (options.wal_recovery_mode == WALRecoveryMode::kPointInTimeRecovery) {
ASSERT_OK(s);
ASSERT_EQ("NOT_FOUND", Get("key1"));
} else if (options.wal_recovery_mode ==
WALRecoveryMode::kAbsoluteConsistency ||
options.wal_recovery_mode ==
WALRecoveryMode::kTolerateCorruptedTailRecords) {
ASSERT_TRUE(s.IsCorruption());
std::string msg;
if (i == 1) {
msg = "Missing WAL";
} else if (i == 2) {
msg = "Mismatched last sequence number recorded in the WAL";
} else if (i == 3) {
msg = "Mismatched size of the WAL";
}
ASSERT_TRUE(s.ToString().find(msg) != std::string::npos);
} else {
ASSERT_OK(s);
ASSERT_EQ("new_value", Get("key1"));
}
Close();
}
}
class DBWALTestWithParams
: public DBWALTestBase,
public ::testing::WithParamInterface<
std::tuple<bool, int, int, CompressionType, bool>> {
public:
DBWALTestWithParams() : DBWALTestBase("/db_wal_test_with_params") {}
};
@@ -1853,7 +2039,8 @@ INSTANTIATE_TEST_CASE_P(
RecoveryTestHelper::kWALFilesCount,
1),
::testing::Values(CompressionType::kNoCompression,
CompressionType::kZSTD)));
CompressionType::kZSTD),
::testing::Bool()));
class DBWALTestWithParamsVaryingRecoveryMode
: public DBWALTestBase,
@@ -1891,6 +2078,7 @@ TEST_P(DBWALTestWithParams, kTolerateCorruptedTailRecords) {
// Fill data for testing
Options options = CurrentOptions();
options.track_and_verify_wals = std::get<4>(GetParam());
const size_t row_count = RecoveryTestHelper::FillData(this, &options);
// test checksum failure or parsing
RecoveryTestHelper::CorruptWAL(this, options, corrupt_offset * .3,
@@ -1914,6 +2102,7 @@ TEST_P(DBWALTestWithParams, kTolerateCorruptedTailRecords) {
TEST_P(DBWALTestWithParams, kAbsoluteConsistency) {
// Verify clean slate behavior
Options options = CurrentOptions();
options.track_and_verify_wals = std::get<4>(GetParam());
const size_t row_count = RecoveryTestHelper::FillData(this, &options);
options.create_if_missing = false;
ASSERT_OK(TryReopen(options));
@@ -2124,7 +2313,6 @@ TEST_F(DBWALTest, FixSyncWalOnObseletedWalWithNewManifestCausingMissingWAL) {
wal_synced = true;
});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Flush());
@@ -2165,6 +2353,7 @@ TEST_P(DBWALTestWithParams, kPointInTimeRecovery) {
// Fill data for testing
Options options = CurrentOptions();
options.track_and_verify_wals = std::get<4>(GetParam());
options.wal_compression = compression_type;
const size_t row_count = RecoveryTestHelper::FillData(this, &options);
@@ -2222,6 +2411,7 @@ TEST_P(DBWALTestWithParams, kSkipAnyCorruptedRecords) {
// Fill data for testing
Options options = CurrentOptions();
options.track_and_verify_wals = std::get<4>(GetParam());
options.wal_compression = compression_type;
const size_t row_count = RecoveryTestHelper::FillData(this, &options);
@@ -2699,12 +2889,12 @@ TEST_F(DBWALTest, ReadOnlyRecoveryNoTruncate) {
// create DB and close with file truncate disabled
std::atomic_bool enable_truncate{false};
SyncPoint::GetInstance()->SetCallBack(
"PosixWritableFile::Close", [&](void* arg) {
if (!enable_truncate) {
*(static_cast<size_t*>(arg)) = 0;
}
});
SyncPoint::GetInstance()->SetCallBack("PosixWritableFile::Close",
[&](void* arg) {
if (!enable_truncate) {
*(static_cast<size_t*>(arg)) = 0;
}
});
SyncPoint::GetInstance()->EnableProcessing();
DestroyAndReopen(options);
@@ -2793,7 +2983,6 @@ TEST_F(DBWALTest, WalInManifestButNotInSortedWals) {
Close();
}
TEST_F(DBWALTest, WalTermTest) {
Options options = CurrentOptions();
options.env = env_;
+49 -1
View File
@@ -1480,6 +1480,55 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToUserKeyBeforeSavedKey) {
Close();
}
TEST_F(DBBasicTestWithTimestamp,
FIXME_ReverseIterationWithBlobAndUnpreparedValue) {
Options options = CurrentOptions();
options.create_if_missing = true;
options.env = env_;
options.enable_blob_files = true;
options.max_sequential_skip_in_iterations = 0;
const size_t kTimestampSize = Timestamp(0, 0).size();
TestComparator test_cmp(kTimestampSize);
options.comparator = &test_cmp;
DestroyAndReopen(options);
constexpr uint64_t kMaxKey = 1024;
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
Timestamp(3, 0)};
for (uint64_t key = 0; key <= kMaxKey; ++key) {
for (size_t i = 0; i < write_timestamps.size(); ++i) {
ASSERT_OK(db_->Put(WriteOptions(), Key1(key), write_timestamps[i],
"value" + std::to_string(i)));
}
}
ASSERT_OK(Flush());
{
const std::string read_timestamp_str = Timestamp(4, 0);
const Slice read_timestamp(read_timestamp_str);
ReadOptions read_opts;
read_opts.timestamp = &read_timestamp;
read_opts.allow_unprepared_value = true;
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
it->SeekForPrev(Key1(kMaxKey));
ASSERT_TRUE(it->Valid());
ASSERT_OK(it->status());
// FIXME: PrepareValue() should succeed and status() should remain OK
ASSERT_FALSE(it->PrepareValue());
ASSERT_TRUE(it->status().IsCorruption());
}
Close();
}
TEST_F(DBBasicTestWithTimestamp, MultiGetWithFastLocalBloom) {
Options options = CurrentOptions();
options.env = env_;
@@ -3257,7 +3306,6 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetNoReturnTs) {
Close();
}
INSTANTIATE_TEST_CASE_P(
Timestamp, DBBasicTestWithTimestampCompressionSettings,
::testing::Combine(
-1
View File
@@ -780,7 +780,6 @@ TEST_P(DBWriteBufferManagerTest, MixedSlowDownOptionsMultipleDB) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
// Tests a `WriteBufferManager` constructed with `allow_stall == false` does not
// thrash memtable switching when full and a CF receives multiple writes.
// Instead, we expect to switch a CF's memtable for flush only when that CF does
+2 -4
View File
@@ -178,11 +178,9 @@ std::string ParsedInternalKey::DebugString(bool log_err_key, bool hex,
result += "<redacted>";
}
char buf[50];
snprintf(buf, sizeof(buf), "' seq:%" PRIu64 ", type:%d", sequence,
static_cast<int>(type));
result += "' seq:" + std::to_string(sequence);
result += ", type:" + std::to_string(type);
result += buf;
return result;
}
+62
View File
@@ -498,6 +498,8 @@ class InternalKey {
// Intended only to be used together with ConvertFromUserKey().
std::string* rep() { return &rep_; }
const std::string* const_rep() const { return &rep_; }
// Assuming that *rep() contains a user key, this method makes internal key
// out of it in-place. This saves a memcpy compared to Set()/SetFrom().
void ConvertFromUserKey(SequenceNumber s, ValueType t) {
@@ -1179,4 +1181,64 @@ struct ParsedInternalKeyComparator {
const InternalKeyComparator* cmp;
};
class PredecessorWALInfo {
public:
PredecessorWALInfo()
: log_number_(0),
size_bytes_(0),
last_seqno_recorded_(0),
initialized_(false) {}
explicit PredecessorWALInfo(uint64_t log_number, uint64_t size_bytes,
SequenceNumber last_seqno_recorded)
: log_number_(log_number),
size_bytes_(size_bytes),
last_seqno_recorded_(last_seqno_recorded),
initialized_(true) {}
uint64_t GetLogNumber() const {
assert(initialized_);
return log_number_;
}
uint64_t GetSizeBytes() const {
assert(initialized_);
return size_bytes_;
}
SequenceNumber GetLastSeqnoRecorded() const {
assert(initialized_);
return last_seqno_recorded_;
}
bool IsInitialized() const { return initialized_; }
inline void EncodeTo(std::string* dst) const {
assert(dst != nullptr);
assert(initialized_);
PutFixed64(dst, log_number_);
PutFixed64(dst, size_bytes_);
PutFixed64(dst, last_seqno_recorded_);
}
inline Status DecodeFrom(Slice* src) {
if (!GetFixed64(src, &log_number_)) {
return Status::Corruption("Error decoding log number");
}
if (!GetFixed64(src, &size_bytes_)) {
return Status::Corruption("Error decoding size bytes");
}
if (!GetFixed64(src, &last_seqno_recorded_)) {
return Status::Corruption("Error decoding last seqno recorded");
}
initialized_ = true;
return Status::OK();
}
private:
uint64_t log_number_;
uint64_t size_bytes_;
SequenceNumber last_seqno_recorded_;
bool initialized_;
};
} // namespace ROCKSDB_NAMESPACE
-190
View File
@@ -135,57 +135,6 @@ class DeleteFileTest : public DBTestBase {
}
};
TEST_F(DeleteFileTest, AddKeysAndQueryLevels) {
Options options = CurrentOptions();
SetOptions(&options);
Destroy(options);
options.create_if_missing = true;
Reopen(options);
CreateTwoLevels();
std::vector<LiveFileMetaData> metadata;
db_->GetLiveFilesMetaData(&metadata);
std::string level1file;
int level1keycount = 0;
std::string level2file;
int level2keycount = 0;
int level1index = 0;
int level2index = 1;
ASSERT_EQ((int)metadata.size(), 2);
if (metadata[0].level == 2) {
level1index = 1;
level2index = 0;
}
level1file = metadata[level1index].name;
int startkey = atoi(metadata[level1index].smallestkey.c_str());
int endkey = atoi(metadata[level1index].largestkey.c_str());
level1keycount = (endkey - startkey + 1);
level2file = metadata[level2index].name;
startkey = atoi(metadata[level2index].smallestkey.c_str());
endkey = atoi(metadata[level2index].largestkey.c_str());
level2keycount = (endkey - startkey + 1);
// COntrolled setup. Levels 1 and 2 should both have 50K files.
// This is a little fragile as it depends on the current
// compaction heuristics.
ASSERT_EQ(level1keycount, 50000);
ASSERT_EQ(level2keycount, 50000);
Status status = db_->DeleteFile("0.sst");
ASSERT_TRUE(status.IsInvalidArgument());
// intermediate level files cannot be deleted.
status = db_->DeleteFile(level1file);
ASSERT_TRUE(status.IsInvalidArgument());
// Lowest level file deletion should succeed.
status = db_->DeleteFile(level2file);
ASSERT_OK(status);
}
TEST_F(DeleteFileTest, PurgeObsoleteFilesTest) {
Options options = CurrentOptions();
SetOptions(&options);
@@ -496,145 +445,6 @@ TEST_F(DeleteFileTest, BackgroundPurgeTestMultipleJobs) {
CheckFileTypeCounts(dbname_, 0, 1, 1);
}
TEST_F(DeleteFileTest, DeleteFileWithIterator) {
Options options = CurrentOptions();
SetOptions(&options);
Destroy(options);
options.create_if_missing = true;
Reopen(options);
CreateTwoLevels();
ReadOptions read_options;
Iterator* it = db_->NewIterator(read_options);
ASSERT_OK(it->status());
std::vector<LiveFileMetaData> metadata;
db_->GetLiveFilesMetaData(&metadata);
std::string level2file;
ASSERT_EQ(metadata.size(), static_cast<size_t>(2));
if (metadata[0].level == 1) {
level2file = metadata[1].name;
} else {
level2file = metadata[0].name;
}
Status status = db_->DeleteFile(level2file);
fprintf(stdout, "Deletion status %s: %s\n", level2file.c_str(),
status.ToString().c_str());
ASSERT_OK(status);
it->SeekToFirst();
int numKeysIterated = 0;
while (it->Valid()) {
numKeysIterated++;
it->Next();
}
ASSERT_EQ(numKeysIterated, 50000);
delete it;
}
TEST_F(DeleteFileTest, DeleteLogFiles) {
Options options = CurrentOptions();
SetOptions(&options);
Destroy(options);
options.create_if_missing = true;
Reopen(options);
AddKeys(10, 0);
VectorLogPtr logfiles;
ASSERT_OK(db_->GetSortedWalFiles(logfiles));
ASSERT_GT(logfiles.size(), 0UL);
// Take the last log file which is expected to be alive and try to delete it
// Should not succeed because live logs are not allowed to be deleted
std::unique_ptr<LogFile> alive_log = std::move(logfiles.back());
ASSERT_EQ(alive_log->Type(), kAliveLogFile);
ASSERT_OK(env_->FileExists(wal_dir_ + "/" + alive_log->PathName()));
fprintf(stdout, "Deleting alive log file %s\n",
alive_log->PathName().c_str());
ASSERT_NOK(db_->DeleteFile(alive_log->PathName()));
ASSERT_OK(env_->FileExists(wal_dir_ + "/" + alive_log->PathName()));
logfiles.clear();
// Call Flush to bring about a new working log file and add more keys
// Call Flush again to flush out memtable and move alive log to archived log
// and try to delete the archived log file
FlushOptions fopts;
ASSERT_OK(db_->Flush(fopts));
AddKeys(10, 0);
ASSERT_OK(db_->Flush(fopts));
ASSERT_OK(db_->GetSortedWalFiles(logfiles));
ASSERT_GT(logfiles.size(), 0UL);
std::unique_ptr<LogFile> archived_log = std::move(logfiles.front());
ASSERT_EQ(archived_log->Type(), kArchivedLogFile);
ASSERT_OK(env_->FileExists(wal_dir_ + "/" + archived_log->PathName()));
fprintf(stdout, "Deleting archived log file %s\n",
archived_log->PathName().c_str());
ASSERT_OK(db_->DeleteFile(archived_log->PathName()));
ASSERT_TRUE(
env_->FileExists(wal_dir_ + "/" + archived_log->PathName()).IsNotFound());
}
TEST_F(DeleteFileTest, DeleteNonDefaultColumnFamily) {
Options options = CurrentOptions();
SetOptions(&options);
Destroy(options);
options.create_if_missing = true;
Reopen(options);
CreateAndReopenWithCF({"new_cf"}, options);
Random rnd(5);
for (int i = 0; i < 1000; ++i) {
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], test::RandomKey(&rnd, 10),
test::RandomKey(&rnd, 10)));
}
ASSERT_OK(db_->Flush(FlushOptions(), handles_[1]));
for (int i = 0; i < 1000; ++i) {
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], test::RandomKey(&rnd, 10),
test::RandomKey(&rnd, 10)));
}
ASSERT_OK(db_->Flush(FlushOptions(), handles_[1]));
std::vector<LiveFileMetaData> metadata;
db_->GetLiveFilesMetaData(&metadata);
ASSERT_EQ(2U, metadata.size());
ASSERT_EQ("new_cf", metadata[0].column_family_name);
ASSERT_EQ("new_cf", metadata[1].column_family_name);
auto old_file = metadata[0].smallest_seqno < metadata[1].smallest_seqno
? metadata[0].name
: metadata[1].name;
auto new_file = metadata[0].smallest_seqno > metadata[1].smallest_seqno
? metadata[0].name
: metadata[1].name;
ASSERT_TRUE(db_->DeleteFile(new_file).IsInvalidArgument());
ASSERT_OK(db_->DeleteFile(old_file));
{
std::unique_ptr<Iterator> itr(db_->NewIterator(ReadOptions(), handles_[1]));
ASSERT_OK(itr->status());
int count = 0;
for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
ASSERT_OK(itr->status());
++count;
}
ASSERT_OK(itr->status());
ASSERT_EQ(count, 1000);
}
Close();
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "new_cf"}, options);
{
std::unique_ptr<Iterator> itr(db_->NewIterator(ReadOptions(), handles_[1]));
int count = 0;
for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
ASSERT_OK(itr->status());
++count;
}
ASSERT_OK(itr->status());
ASSERT_EQ(count, 1000);
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+12 -3
View File
@@ -227,7 +227,7 @@ std::map<std::tuple<BackgroundErrorReason, bool>, Status::Severity>
Status::Severity::kFatalError},
};
void ErrorHandler::CancelErrorRecovery() {
void ErrorHandler::CancelErrorRecoveryForShutDown() {
db_mutex_->AssertHeld();
// We'll release the lock before calling sfm, so make sure no new
@@ -573,6 +573,8 @@ Status ErrorHandler::ClearBGError() {
// Signal that recovery succeeded
if (recovery_error_.ok()) {
// If this assertion fails, it means likely bg error is not set after a
// file is quarantined during MANIFEST write.
assert(files_to_quarantine_.empty());
Status old_bg_error = bg_error_;
// old_bg_error is only for notifying listeners, so may not be checked
@@ -585,8 +587,15 @@ Status ErrorHandler::ClearBGError() {
recovery_error_.PermitUncheckedError();
recovery_in_prog_ = false;
soft_error_no_bg_work_ = false;
EventHelpers::NotifyOnErrorRecoveryEnd(db_options_.listeners, old_bg_error,
bg_error_, db_mutex_);
if (!db_->shutdown_initiated_) {
// NotifyOnErrorRecoveryEnd() may release and re-acquire db_mutex_.
// Prevent DB from being closed while we notify listeners. DB close will
// wait until allow_db_shutdown_ = true, see ReadyForShutdown().
allow_db_shutdown_ = false;
EventHelpers::NotifyOnErrorRecoveryEnd(
db_options_.listeners, old_bg_error, bg_error_, db_mutex_);
allow_db_shutdown_ = true;
}
}
return recovery_error_;
}
+16 -1
View File
@@ -44,6 +44,7 @@ class ErrorHandler {
auto_recovery_(false),
recovery_in_prog_(false),
soft_error_no_bg_work_(false),
allow_db_shutdown_(true),
is_db_stopped_(false),
bg_error_stats_(db_options.statistics) {
// Clear the checked flag for uninitialized errors
@@ -63,6 +64,12 @@ class ErrorHandler {
Status GetRecoveryError() const { return recovery_error_; }
// REQUIREs: db mutex held
//
// Returns non-OK status if encountered error during recovery.
// Returns OK if bg error is successfully cleared. May releases and
// re-acquire db mutex to notify listeners. However, DB close (if initiated)
// will be blocked until db mutex is released after return.
Status ClearBGError();
bool IsDBStopped() { return is_db_stopped_.load(std::memory_order_acquire); }
@@ -79,8 +86,14 @@ class ErrorHandler {
bool IsRecoveryInProgress() { return recovery_in_prog_; }
// REQUIRES: db mutex held
bool ReadyForShutdown() {
db_mutex_->AssertHeld();
return !recovery_in_prog_ && allow_db_shutdown_;
}
Status RecoverFromBGError(bool is_manual = false);
void CancelErrorRecovery();
void CancelErrorRecoveryForShutDown();
void EndAutoRecovery();
@@ -121,6 +134,8 @@ class ErrorHandler {
// A flag to indicate that for the soft error, we should not allow any
// background work except the work is from recovery.
bool soft_error_no_bg_work_;
// Used in ClearBGError() to prevent DB from being closed.
bool allow_db_shutdown_;
// Used to store the context for recover, such as flush reason.
DBRecoverContext recover_context_;
+9 -12
View File
@@ -81,9 +81,8 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
JSONWriter jwriter;
AppendCurrentTime(&jwriter);
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
<< "table_file_creation"
<< "file_number" << fd.GetNumber() << "file_size"
<< fd.GetFileSize() << "file_checksum"
<< "table_file_creation" << "file_number" << fd.GetNumber()
<< "file_size" << fd.GetFileSize() << "file_checksum"
<< Slice(file_checksum).ToString(true) << "file_checksum_func_name"
<< file_checksum_func_name << "smallest_seqno" << fd.smallest_seqno
<< "largest_seqno" << fd.largest_seqno;
@@ -199,8 +198,7 @@ void EventHelpers::LogAndNotifyTableFileDeletion(
JSONWriter jwriter;
AppendCurrentTime(&jwriter);
jwriter << "job" << job_id << "event"
<< "table_file_deletion"
jwriter << "job" << job_id << "event" << "table_file_deletion"
<< "file_number" << file_number;
if (!status.ok()) {
jwriter << "status" << status.ToString();
@@ -280,11 +278,11 @@ void EventHelpers::LogAndNotifyBlobFileCreationFinished(
JSONWriter jwriter;
AppendCurrentTime(&jwriter);
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
<< "blob_file_creation"
<< "file_number" << file_number << "total_blob_count"
<< total_blob_count << "total_blob_bytes" << total_blob_bytes
<< "file_checksum" << file_checksum << "file_checksum_func_name"
<< file_checksum_func_name << "status" << s.ToString();
<< "blob_file_creation" << "file_number" << file_number
<< "total_blob_count" << total_blob_count << "total_blob_bytes"
<< total_blob_bytes << "file_checksum" << file_checksum
<< "file_checksum_func_name" << file_checksum_func_name << "status"
<< s.ToString();
jwriter.EndObject();
event_logger->Log(jwriter);
@@ -311,8 +309,7 @@ void EventHelpers::LogAndNotifyBlobFileDeletion(
JSONWriter jwriter;
AppendCurrentTime(&jwriter);
jwriter << "job" << job_id << "event"
<< "blob_file_deletion"
jwriter << "job" << job_id << "event" << "blob_file_deletion"
<< "file_number" << file_number;
if (!status.ok()) {
jwriter << "status" << status.ToString();
Loaded 100 of 448 files, more files were not shown because too many files have changed in this diff. Show more