Compare commits

...
33 Commits
Author SHA1 Message Date
anand76 4b2122578e Update HISTORY and version 2025-04-24 22:12:38 -07:00
anand76 b8afcd1a48 Fix ExternalTableOptions initialization (#13572)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/13572

Reviewed By: moakbari

Differential Revision: D73568773

Pulled By: anand1976

fbshipit-source-id: d61d76cb864e3af111bb05dc1ee51a8b3f1eaf17
2025-04-24 22:08:46 -07:00
Jay Huh 8adcb6f153 Update HISTORY.md for 10.2 release 2025-04-21 13:34:27 -07:00
anand76 7eb1adb532 Pass FSWritableFile pointer to ExternalTableBuilder (#13560)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/13560

Reviewed By: jaykorean

Differential Revision: D73296242

Pulled By: anand1976

fbshipit-source-id: b692a5c6ad32b40b3c2c1ca7a93bd04139856bce
2025-04-21 10:36:45 -07:00
Jay Huh 0be3abf7b6 Arbitrary string map in CompactionServiceOptionsOverride (#13552)
Summary:
Adding an arbitrary options map so that any additional overridable options can be added without RocksDB change. Unknown options will be ignored

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

Test Plan:
Unit Test added
```
./db_secondary_test -- --gtest_filter="*OptionsOverrideTest*"
```

Reviewed By: hx235

Differential Revision: D73203789

Pulled By: jaykorean

fbshipit-source-id: 176bd9849d2bc60e78657c119e10a1a2a0988cd1
2025-04-21 10:19:14 -07:00
Jay Huh 05fa171beb Add Logger to CompactionServiceOptionsOverride (#13559)
Summary:
As title

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

Test Plan: CI

Reviewed By: anand1976

Differential Revision: D73267683

Pulled By: jaykorean

fbshipit-source-id: 6a3d3da07a36ad3bbfad3f749e7dfd67b7b626c8
2025-04-18 16:43:56 -07:00
Jay Huh 9b186c8d11 Add base_input_level and output_level in CompactionServiceJobInfo (#13555)
Summary:
Similar to https://github.com/facebook/rocksdb/pull/13029, add `base_input_level` (a.k.a. start_level) and `output_level` to `CompactionServiceJobInfo`

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

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

Reviewed By: anand1976

Differential Revision: D73213504

Pulled By: jaykorean

fbshipit-source-id: abb3b0025bc12245b812ef589fe77e9a30ba0c46
2025-04-17 17:43:05 -07:00
Yu Zhang 476a98ca30 Add a new GetNewestUserDefinedTimestamp API (#13547)
Summary:
This PR adds a DB::GetNewestUserDefinedTimestamp API to get the newest timestamp of the column family. This is only for when the column family enables user defined timestamp.
It checks the mutable memtable, the immutable memtable and the SST files, and returns the first newest user defined timestamp found. When user defined timestamp is not persisted in SST files, there is metadata in MANIFEST tracking upperbound of flushed timestamps, so the newest timestamp in SST files can be found. If user defined timestamps are
persisted in SST files, currently no timestamp metadata info is persisted. A NotSupported status will be returned if SST files need to be checked in that case.

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

Test Plan: Added tests

Reviewed By: cbi42

Differential Revision: D73123575

Pulled By: jowlyzhang

fbshipit-source-id: 460ac4f9c96926d3c8fcf7944edab8dc0feae1dd
2025-04-17 13:19:52 -07:00
Changyu Bi 925c63a96b Experimental API IngestWriteBatchWithIndex() (#13550)
Summary:
add support for ingesting a WriteBatchWithIndex into the DB with the new API `IngestWriteBatchWithIndex()`. This ingestion works similarly as `TransactionOptions::commit_bypass_memtable` where the WBWI will be ingested as an immutable memtable. Since this skips memtable writes, it improves the write performance when writing a large write batch into the DB. Currently this API only supports `disableWAL=true`. Support for WAL write will be in a follow up if needed.

For a WBWI to be ingestable, we needed to call `SetTrackPerCFStat()` at WBWI creation. This PR removes this step for simpler usage and per CF stats will always be tracked in WBWI. `WBWIIteratorImpl::TestOutOfBound()` is optimized to offset the performance impact.

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

Test Plan:
- new unit test
- stress test option ingest_wbwi_one_in and ran a few runs of `python3 ./tools/db_crashtest.py blackbox --enable_pipelined_write=0 --use_timed_put_one_in=0 --use_put_entity_one_in=0 --ingest_wbwi_one_in=10 --test_batches_snapshots=0 --enable_blob_files=0 --preserve_unverified_changes=1 --avoid_flush_during_recovery=1 --disable_wal=1 --inplace_update_support=0 --interval=40`

Reviewed By: jowlyzhang

Differential Revision: D73152223

Pulled By: cbi42

fbshipit-source-id: 339f8ed26ac5a798238870df3ba857ba1add759b
2025-04-17 12:06:40 -07:00
anand76 6d83a75595 Pass FileSystem pointer and FileOptions to ExternalTableReader (#13551)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/13551

Reviewed By: jaykorean

Differential Revision: D73157052

Pulled By: anand1976

fbshipit-source-id: 580a9104a86b11e3b0b624bb8aa2cf176dc7a27a
2025-04-17 11:25:11 -07:00
Zaidoon Abd Al Hadi 31b2397470 Expose Options::memtable_op_scan_flush_trigger through C API (#13537)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/13537

Reviewed By: jowlyzhang

Differential Revision: D73141407

Pulled By: cbi42

fbshipit-source-id: c7e04b403a17773e651f4922976f213b817f7adc
2025-04-16 20:45:38 -07:00
Yu Zhang 695c653e11 Correctly initialize file size for reopened writable file (#13534)
Summary:
A reopened writable file's size is not correctly tracked in the `WritableFile`'s internal state.  This PR adds a querying to the file system to get the initial file size in the reopen case and use it to populate posix `WritableFile`'s internal state.

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

Reviewed By: anand1976

Differential Revision: D72756628

Pulled By: jowlyzhang

fbshipit-source-id: 6f02b5c5da069fe49055d7b75bec9e7e47d5cd71
2025-04-16 17:24:12 -07:00
Yu Zhang 0e736666a0 Add a test for using atomic_replace_range to ingeset and replace data (#13549)
Summary:
Add a test to cover an internal user's expected behavior of using atomic_replace_range feature to atomically ingest a version key and a data file.

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

Test Plan: This is a test

Reviewed By: cbi42

Differential Revision: D73142626

Pulled By: jowlyzhang

fbshipit-source-id: a5bdc24b762cbe91dd4d94242b9e1539c9feaf61
2025-04-16 16:32:45 -07:00
Changyu Bi 1ec5a07d8e Support atomic_flush for ingesting WBWI (#13545)
Summary:
add support for atomic_flush when using WBWI ingestion [feature](https://github.com/facebook/rocksdb/blob/29c6610617ddc1b486f12b99c16e7c9851e80430/include/rocksdb/utilities/transaction_db.h#L387). Transaction DB usually uses WAL so atomic_flush is not as helpful. This is to prepare for a follow up PR that enables ingesting WBWI without using transaction DB.

This PR also removes a redundant parameter `prep_log` for the WBWI ingestion feature.

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

Test Plan:
- unti test added
- stress test will be added as we add support to ingest WBWI without using transaction DB.

Reviewed By: jowlyzhang

Differential Revision: D73062342

Pulled By: cbi42

fbshipit-source-id: e05da55dfabb8241a042214b9d50b1b49d42613e
2025-04-16 15:18:48 -07:00
Hui Xiao 29c6610617 Add compaction explicit prefetch stats (#13520)
Summary:
**Context/Summary:**
This PR adds new stats to measure compaction readahead size for rocksdb managed prefetching (not FS prefetching). It can be used to verify compaction read-ahead is doing what's configured. This PR also excludes compaction readahead stats from user scan readahead stats measured in existing stats so there is a cleaner separating between these two.

Bonus: this PR also included some typo fixing about "io activities"

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

Test Plan: Modified existing test to verify stats

Reviewed By: archang19

Differential Revision: D72892850

Pulled By: hx235

fbshipit-source-id: 1a73182061baa044c9c9193a2b0fd967ffe75c4a
2025-04-14 12:08:38 -07:00
anand76 84a8dd994c Some MultiScan code cleanup (#13530)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/13530

Reviewed By: pdillinger

Differential Revision: D72677865

Pulled By: anand1976

fbshipit-source-id: 63e7a15b6e8cd61b676e3b22e1c04c7446adcbd3
2025-04-11 11:35:57 -07:00
Peter Dillinger 2a0ee4ddd8 Refactor wal related naming and more (#13490)
Summary:
* Clarify in API comments which `log_` options in DBOptions relate to WALs, info log, and/or manifest files.
* Rename a bunch of "log" things to "wal" for clarity, especially in DBImpl. (More to go, especially some more challenging cases like `DBImpl::logs_`, but a step in the right direction IMHO)
* Simplify DBImpl ctor by moving constant initializers to field definitions.
* Use RelaxedAtomic for (renamed) `wals_total_size_`

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

Test Plan: existing tests

Reviewed By: cbi42

Differential Revision: D71939382

Pulled By: pdillinger

fbshipit-source-id: 852f4737eca83e6ad653010cc197ad1b6e6bae13
2025-04-11 10:08:29 -07:00
Changyu Bi 56359da691 Trigger memtable flush based on number of hidden entries scanned (#13523)
Summary:
Introduce a mutable CF option `memtable_op_scan_flush_trigger`. When a DB iterator scans this number of hidden entries (tombstones, overwritten puts) from the active memtable in a Seek() or Next() operation, it marks the memtable to be eligible for flush. Subsequent write operations will schedule the marked memtable for flush.

The main change is small and is in db_iter.cc. Some refactoring is done to consolidate and simplify creation of `ArenaWrappedDBIter` and `DBIter`.

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

Test Plan:
- new unit tests added.
- added `memtable_op_scan_flush_trigger` in crash test
- benchmark:
The following benchmark was done with a previous version of the PR where the option was `memtable_tombstone_scan_limit` and it concerns tombstone only. The results should still be applicable for the case when there's no overwritten puts.

Tests that when memtable has many tombstones, the option helps to improve scan performance:
```
TEST_TMPDIR=/dev/shm ./db_bench --benchmarks=seekrandomwhilewriting --expand_range_tombstones=true --writes_per_range_tombstone=1 --max_num_range_tombstones=10000000 --perf_level=2 --range_tombstone_width=100 --memtable_tombstone_scan_limit=

memtable_tombstone_scan_limit = 10000
seekrandomwhilewriting :      18.527 micros/op 53973 ops/sec 18.527 seconds 1000000 operations; (7348 of 1000000 found)
next_on_memtable_count = 122305248
grep "flush_started" /dev/shm/dbbench/LOG | wc
      8     200    2417

memtable_tombstone_scan_limit=200
seekrandomwhilewriting :       4.918 micros/op 203315 ops/sec 4.918 seconds 1000000 operations; (4510 of 1000000 found)
next_on_memtable_count = 1853167
grep "flush_started" /dev/shm/dbbench/LOG | wc
    184    4600   54121

When memtable_tombstone_scan_limit=200, more flush is trigged to drop tombstones sooner and improve scan performance.
```

Tests that the new option does not introduce noticeable regression:
```
TEST_TMPDIR=/dev/shm ./db_bench --benchmarks=seekrandomwhilewriting[-X5] --expand_range_tombstones=true --writes_per_range_tombstone=1 --max_num_range_tombstones=10000000 --perf_level=2 --range_tombstone_width=100 --seed=123

Main:
seekrandomwhilewriting [AVG 5 runs] : 46049 (± 4512) ops/sec
PR:
seekrandomwhilewriting [AVG 5 runs] : 46100 (± 4470) ops/sec

The results are noisy with this PR performing better and worse in different runs, with no noticeable regression.
```

Reviewed By: pdillinger

Differential Revision: D72596434

Pulled By: cbi42

fbshipit-source-id: 2d51a0221dc20dac844aeba2ad3999d075a4cf91
2025-04-10 17:53:33 -07:00
Yu Zhang 46c37a6327 Fix issue with reverse iteration with unprepared value (#13531)
Summary:
When ReadOptions.allow_unprepared_value is true, a `Iterator::PrepareValue()` call is needed to prepare the value after an entry is pinpointed, to only load the blob when it's actually needed. And it uses the `saved_key_.GetUserKey()` to prepare value.
https://github.com/facebook/rocksdb/blob/6d802639f7dc35bf765dbe1ed6b3942e4d76375d/db/db_iter.cc#L319

In the reverse iteration case, when the `FindValueForCurrentKeyUsingSeek()` path is used, `saved_key_` is only updated when `ReadOptions.iter_start_ts` is specified. This PR fixes it by updating `saved_key_` for the other case too.

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

Test Plan: The FIXME test that reproduce the bug is updated

Reviewed By: pdillinger

Differential Revision: D72681397

Pulled By: jowlyzhang

fbshipit-source-id: 6c239da53c9beed1560d30013474f2ba542b245c
2025-04-10 16:20:30 -07:00
anand76 f7764cb6b2 Remove fail_if_options_file_error DB option (#13504)
Summary:
The fail_if_options_file_error has been deprecated for more than a year. This PR removes it from the code base. https://github.com/facebook/rocksdb/issues/12056 fixed a bug that was blocking the option from removal. https://github.com/facebook/rocksdb/issues/12249 marked it as deprecated.

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

Reviewed By: hx235

Differential Revision: D72194063

Pulled By: anand1976

fbshipit-source-id: 0aa7cf56e60c48c7e7654743d3e64922ce65225d
2025-04-09 14:18:33 -07:00
Yu Zhang 6d802639f7 Fix a data race reported for secondary (#13529)
Summary:
Fix a reported data race, accessing `manifest_reader_` without locking `mutex_` could race with another `DBImpl::Secondary::TryCatchUpWithPrimary` thread that is updating to a new manifest in `ReactiveVersionSet::MaybeSwitchManifest`.

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

Test Plan: Existing tests

Reviewed By: hx235

Differential Revision: D72655645

Pulled By: jowlyzhang

fbshipit-source-id: 08599862346bb39a6872c3adfd7f0097fc633849
2025-04-08 15:16:55 -07:00
Yu Zhang 5e10baa412 Delete max_write_buffer_number_to_maintain (#13491)
Summary:
As titled. This option has been marked deprecated since introduction of a better option `max_write_buffer_size_to_maintain` and acts as its fallback since RocksDB 6.5.0 The internal user we know these options were created for migrated to `max_write_buffer_size_to_maintain` for a long time too.

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

Test Plan: existing tests

Reviewed By: cbi42

Differential Revision: D71984601

Pulled By: jowlyzhang

fbshipit-source-id: c264d4809e311f60fdbad817ebfade256db549b6
2025-04-07 21:44:36 -07:00
Hui Xiao 72571d09ad Clean up in repair, file ingestion and cf import (#13524)
Summary:
**Context/Summary:**
Rebased on https://github.com/facebook/rocksdb/pull/13522/files, this is to use the refactored function to calculate tail size from table property "tail_start_offset"

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

Test Plan: CI

Reviewed By: anand1976

Differential Revision: D72576262

Pulled By: hx235

fbshipit-source-id: 78c126bc64024c2341d183d6871e06d55fd27501
2025-04-07 12:50:56 -07:00
Hui Xiao 07b09c7548 Persist tail size of remote compaction output file to manifest (#13522)
Summary:
**Context/Summary:**

This is to fix a bug that tail size of remote compaction output SST file is not persisted to manifest in primary instance. This prevent us from using direct tail prefetch optimization each time opening this SST file.

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

Test Plan: Modify existing UT that failed before the fix

Reviewed By: anand1976

Differential Revision: D72479612

Pulled By: hx235

fbshipit-source-id: 1ba8aa66fac71b9196589f60076229c29a103706
2025-04-07 09:39:54 -07:00
Yu Zhang 4069afeede Add safeguarding from resurrected cutoff UDT from previous session (#13521)
Summary:
Public APIs like `DB::GetFullHistoryTsLow` and `DB::IncreaseFullHistoryTsLow` have such safeguarding, allowing them to only be invoked when user defined timestamp is enabled. This PR adds safeguarding into related internal APIs in `ColumnFamilyData` to properly handle the case when the UDT feature are toggled.

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

Test Plan: ./db_with_timestamp_basic_test --gtest_filter="*EnableDisableUDT*"

Reviewed By: cbi42

Differential Revision: D72475234

Pulled By: jowlyzhang

fbshipit-source-id: 194c07287e3100da95450b04c76552c9d4a86c2d
2025-04-04 17:13:56 -07:00
anand76 24e2b05e61 Multi scan API (#13473)
Summary:
A multi scan API for users to pass a set of scan ranges and have the table readers determine the optimal strategy for performing the scans. This might include coalescing of IOs across scans, for example. The requested scans should be in increasing key order. The scan start keys and other info is passed to NewMultiScanIterator, which in turn uses the newly added Prepare() interface in Iterator to update the iterator. The Prepare() takes a vector of ScanOptions, which contain the start keys and optional upper bounds, as well as user defined parameters in the property_bag taht are passed through as is to external table readers.

The initial implementation plumbs this through to the ExternalTableReader. This PR also fixes an issue of premature destruction of the external table iterator after the first scan of the multi-scan. The `LevelIterator` treats an invalid iterator as a potential end of file and destroys the table iterator in order to move to the next file. To prevent that, this PR defines the `NextAndGetResult` interface that the external table iterator must implement. The result returned by `NextAndGetResult` differentiates between iterator invalidation due to out of bound vs end of file.

Eventually, I envision the `MultiScanIterator` to be built on top of a producer-consumer queue like container, with RocksDB (producer) enqueueing keys and values into the container and the application (consumer) dequeueing them. Unlike a traditional producer consumer queue, there is no concurrency here. The results will be buffered in the container, and when the buffer is empty a new batch will be read from the child iterators. This will allow the virtual function call overhead to be amortized over many entries.

TODO (in future PRs):
1. Update the internal implementation of Prepare to trim the ScanOptions range based on the intersection with the table key range, taking into consideration unbounded scans and opaque user defined bounds.
2. Long term, take advantage of Prepare in BlockBasedTableIterator, atleast for the upper bound case.

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

Reviewed By: pdillinger

Differential Revision: D71447559

Pulled By: anand1976

fbshipit-source-id: 31668abb0c529aa1ac1738ae46c36cbddf9148f1
2025-04-02 16:07:56 -07:00
Hui Xiao 5735ff4e03 Update window build cmake to download newer snappy version and Java cmake_minimum_required (#13514)
Summary:
**Context/Summary:**

- This is an attempt to fix our [build-window-vs2022 failure](https://github.com/facebook/rocksdb/actions/runs/14215681026/job/39831770554?fbclid=IwZXh0bgNhZW0CMTAAAR2BQLjp8kC1u1yyvN1_S5qwmrHEZOfzxJdcbj2vq7mvwwq83n1cbkmiBCA_aem_ygYxQA5EUmxh2y4EjMlTfg) below. snappy-1.1.8's cmake_minimum_required  being less than 3.5 seems to trigger the complaint. Hopefully downloading the 1.2.2 which is the [first version starting to use higher cmake_minimum_required version](https://github.com/google/snappy/releases/tag/1.2.2) solves the failure.

```
    Directory: D:\a\rocksdb\rocksdb\thirdparty\snappy-1.1.8

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----            4/2/2025  9:02 AM                build
CMake Error at CMakeLists.txt:29 (cmake_minimum_required):
  Compatibility with CMake < 3.5 has been removed from CMake.

  Update the VERSION argument <min> value.  Or, use the <min>...<max> syntax
  to tell CMake that the project requires at least <min> but has been updated
  to work with policies introduced by <max> or earlier.

  Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3.5 to try configuring anyway.
 ```
- The downloaded snappy do not include the content under nested repos Google Test and Google Benchmark. But snappy cmake by default will attempt to build them. Since we don't change snappy, we don't need building such development suit. This PR also disabled snappy cmake's attempt to build them.

- By running above changes, the same build [complained](https://github.com/facebook/rocksdb/actions/runs/14228883966/job/39874927730?pr=13514) about java cmakelists requiring too low cmake_minimum_required as well.  So this PR also upgraded its cmake_minimum_required to be 3.11 aligning with its warning message
```
if(${CMAKE_VERSION} VERSION_LESS "3.11.4")
    message("Please consider switching to CMake 3.11.4 or newer")
endif()
```

**Test plan**
Monitor build-window-vs2022 for this PR

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

Reviewed By: pdillinger

Differential Revision: D72333581

Pulled By: hx235

fbshipit-source-id: 1a9096738d39c8b1d270fe17fbd78c1ea4c4c45e
2025-04-02 15:46:02 -07:00
Hui Xiao 30e097e365 Disable 2pc TXN with WAL write injection in db stress; Re-enable track_and_verify_wal (#13508)
Summary:
**Context/Summary:**
Pessimistic transactions use 2PC and can't auto-recover from WAL write errors. This is because RocksDB cannot easily discard the corrupted WAL without risking the loss of uncommitted prepared data within the same WAL. Stress test does not support injecting errors that can' be auto-recovered for now. Therefore disabling WAL write error injection in stress tests to prevent crashing.

Previously track_and_verify_wal was disabled due to it caught those corrupted WAL. We can enable the feature now since there won't be such corrupted WAL.

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

Test Plan:
- Previous failed command pass now
```
python3 tools/db_crashtest.py --simple blackbox --interval=15  --WAL_size_limit_MB=0 --WAL_ttl_seconds=60 --acquire_snapshot_one_in=100 --adaptive_readahead=0 --adm_policy=1 --advise_random_on_open=1 --allow_data_in_errors=True --allow_fallocate=0 --allow_setting_blob_options_dynamically=1 --allow_unprepared_value=1 --async_io=1 --auto_readahead_size=1 --avoid_flush_during_recovery=0 --avoid_flush_during_shutdown=0 --avoid_unnecessary_blocking_io=0 --backup_max_size=104857600 --backup_one_in=100000 --batch_protection_bytes_per_key=8 --bgerror_resume_retry_interval=100 --blob_cache_size=1048576 --blob_compaction_readahead_size=4194304 --blob_compression_type=snappy --blob_file_size=1073741824 --blob_file_starting_level=0 --blob_garbage_collection_age_cutoff=1.0 --blob_garbage_collection_force_threshold=1.0 --block_align=1 --block_protection_bytes_per_key=8 --block_size=16384 --bloom_before_level=2147483646 --bloom_bits=19 --bottommost_compression_type=none --bottommost_file_compaction_delay=86400 --bytes_per_sync=262144 --cache_index_and_filter_blocks=0 --cache_index_and_filter_blocks_with_high_priority=0 --cache_size=8388608 --cache_type=auto_hyper_clock_cache --charge_compression_dictionary_building_buffer=1 --charge_file_metadata=0 --charge_filter_construction=1 --charge_table_reader=0 --check_multiget_consistency=0 --check_multiget_entity_consistency=0 --checkpoint_one_in=0 --checksum_type=kxxHash --clear_column_family_one_in=0 --commit_bypass_memtable_one_in=0 --compact_files_one_in=1000000 --compact_range_one_in=1000000 --compaction_pri=1 --compaction_readahead_size=0 --compaction_style=0 --compaction_ttl=10 --compress_format_version=1 --compressed_secondary_cache_size=16777216 --compression_checksum=0 --compression_max_dict_buffer_bytes=0 --compression_max_dict_bytes=0 --compression_parallel_threads=1 --compression_type=none --compression_use_zstd_dict_trainer=1 --compression_zstd_max_train_bytes=0 --continuous_verification_interval=0 --create_timestamped_snapshot_one_in=0 --daily_offpeak_time_utc= --data_block_index_type=1 --db_write_buffer_size=0 --decouple_partitioned_filters=0 --default_temperature=kHot --default_write_temperature=kCold --delete_obsolete_files_period_micros=30000000 --delpercent=0 --delrangepercent=0 --destroy_db_initially=0 --detect_filter_construct_corruption=0 --disable_file_deletions_one_in=10000 --disable_manual_compaction_one_in=10000 --disable_wal=0 --dump_malloc_stats=1 --enable_blob_files=1 --enable_blob_garbage_collection=1 --enable_checksum_handoff=1 --enable_compaction_filter=0 --enable_custom_split_merge=1 --enable_do_not_compress_roles=1 --enable_index_compression=0 --enable_memtable_insert_with_hint_prefix_extractor=0 --enable_pipelined_write=0 --enable_sst_partitioner_factory=0 --enable_thread_tracking=0 --enable_write_thread_adaptive_yield=1 --error_recovery_with_no_fault_injection=1 --exclude_wal_from_write_fault_injection=0 --fail_if_options_file_error=1 --fifo_allow_compaction=0 --file_checksum_impl=none --file_temperature_age_thresholds= --fill_cache=0 --flush_one_in=1000000 --format_version=5 --get_all_column_family_metadata_one_in=10000 --get_current_wal_file_one_in=0 --get_live_files_apis_one_in=1000000 --get_properties_of_all_tables_one_in=100000 --get_property_one_in=100000 --get_sorted_wal_files_one_in=0 --hard_pending_compaction_bytes_limit=274877906944 --high_pri_pool_ratio=0.5 --index_block_restart_interval=12 --index_shortening=1 --index_type=0 --ingest_external_file_one_in=0 --initial_auto_readahead_size=16384 --inplace_update_support=0 --iterpercent=0 --key_len_percent_dist=1,30,69 --key_may_exist_one_in=100 --last_level_temperature=kUnknown --level_compaction_dynamic_level_bytes=0 --lock_wal_one_in=1000000 --log_file_time_to_roll=0 --log_readahead_size=0 --long_running_snapshots=1 --low_pri_pool_ratio=0.5 --lowest_used_cache_tier=0 --manifest_preallocation_size=0 --mark_for_compaction_one_file_in=10 --max_auto_readahead_size=0 --max_background_compactions=20 --max_bytes_for_level_base=10485760 --max_key=25000000 --max_key_len=3 --max_log_file_size=1048576 --max_manifest_file_size=1073741824 --max_sequential_skip_in_iterations=2 --max_total_wal_size=0 --max_write_batch_group_size_bytes=64 --max_write_buffer_number=10 --max_write_buffer_size_to_maintain=0 --memtable_insert_hint_per_batch=0 --memtable_max_range_deletions=0 --memtable_prefix_bloom_size_ratio=0.1 --memtable_protection_bytes_per_key=4 --memtable_whole_key_filtering=1 --memtablerep=skip_list --metadata_charge_policy=0 --metadata_read_fault_one_in=0 --metadata_write_fault_one_in=128 --min_blob_size=16 --min_write_buffer_number_to_merge=1 --mmap_read=0 --mock_direct_io=True --nooverwritepercent=1 --num_file_reads_for_auto_readahead=2 --open_files=-1 --open_metadata_read_fault_one_in=8 --open_metadata_write_fault_one_in=8 --open_read_fault_one_in=0 --open_write_fault_one_in=16 --ops_per_thread=100000000 --optimize_filters_for_hits=1 --optimize_filters_for_memory=0 --optimize_multiget_for_io=0 --paranoid_file_checks=1 --paranoid_memory_checks=0 --partition_filters=0 --partition_pinning=0 --pause_background_one_in=10000 --periodic_compaction_seconds=0 --prefix_size=1 --prefixpercent=0 --prepopulate_blob_cache=1 --prepopulate_block_cache=0 --preserve_internal_time_seconds=60 --progress_reports=0 --promote_l0_one_in=0 --read_amp_bytes_per_bit=0 --read_fault_one_in=1000 --readahead_size=524288 --readpercent=0 --recycle_log_file_num=0 --reopen=0 --report_bg_io_stats=1 --reset_stats_one_in=1000000 --sample_for_compression=5 --secondary_cache_fault_one_in=32 --secondary_cache_uri= --set_options_one_in=2000 --skip_stats_update_on_db_open=0 --snapshot_hold_ops=100000 --soft_pending_compaction_bytes_limit=68719476736 --sqfc_name=bar --sqfc_version=1 --sst_file_manager_bytes_per_sec=104857600 --sst_file_manager_bytes_per_truncate=1048576 --stats_dump_period_sec=10 --stats_history_buffer_size=1048576 --strict_bytes_per_sync=0 --subcompactions=1 --sync=0 --table_cache_numshardbits=6 --target_file_size_base=524288 --target_file_size_multiplier=2 --test_batches_snapshots=0 --test_ingest_standalone_range_deletion_one_in=0 --top_level_index_pinning=2 --track_and_verify_wals=1 --two_write_queues=0 --txn_write_policy=1 --uncache_aggressiveness=12 --universal_max_read_amp=-1 --unordered_write=0 --unpartitioned_pinning=3 --use_adaptive_mutex=0 --use_adaptive_mutex_lru=1 --use_attribute_group=1 --use_blob_cache=0 --use_delta_encoding=0 --use_direct_io_for_flush_and_compaction=1 --use_direct_reads=0 --use_full_merge_v1=0 --use_get_entity=0 --use_merge=0 --use_multi_cf_iterator=0 --use_multi_get_entity=1 --use_multiget=0 --use_optimistic_txn=0 --use_put_entity_one_in=0 --use_shared_block_and_blob_cache=1 --use_sqfc_for_range_queries=0 --use_timed_put_one_in=0 --use_txn=1 --use_write_buffer_manager=0 --user_timestamp_size=0 --value_size_mult=32 --verification_only=0 --verify_checksum=1 --verify_checksum_one_in=1000000 --verify_compression=0 --verify_db_one_in=100000 --verify_file_checksums_one_in=0 --verify_iterator_with_expected_state_one_in=5 --verify_sst_unique_id_in_manifest=1 --wal_bytes_per_sync=0 --wal_compression=zstd --write_buffer_size=1048576 --write_dbid_to_manifest=1 --write_fault_one_in=5 --write_identity_file=0 --writepercent=100
```
- Rehearsal stress test 10x of our normal run shows no relevant errors to track_and_verify_wal

Reviewed By: pdillinger

Differential Revision: D72191287

Pulled By: hx235

fbshipit-source-id: 08d3fd52645ad526aec34842215c68b3ef06a9c9
2025-04-02 11:35:41 -07:00
Peter Dillinger b7a9d414c8 Fix WriteBatch atomicity and WAL recovery for some failures (#13489)
Summary:
Essentially fix https://github.com/facebook/rocksdb/issues/13429 by
* Avoiding publishing to readers a partial write batch written to memtable. Also clarify in DB::Write that WriteBatch is applied atomically, and improve some logging.
* When we know we have written a bad write batch to WAL due to memtable insert failure, make a good effort to roll it back to make the DB recoverable. (Not compatible with all options.)

Fixes https://github.com/facebook/rocksdb/issues/13429

Follow-up items:
* More rigorously test and fix the code paths and option combinations where these features could be useful.
* Allow default CF with disallow_memtable_writes (with caveat that violation stops writes on your open DB)

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

Test Plan: Updated existing test, manually verified the DB went into a "stopped" state at least in this example.

Reviewed By: jaykorean

Differential Revision: D71917670

Pulled By: pdillinger

fbshipit-source-id: c9b9dfc102817fc4c160a6c7170c04011c228aaf
2025-04-01 18:16:07 -07:00
Peter Dillinger be99011f08 More separation of txn_write_policy for crash tests (#13499)
Summary:
We are seeing some occasional failures with WRITE_(UN)PREPARED crash test runs, and it's alarming when these are grouped in with WRITE_COMMITTED, which AFAIK is the only one considered mature and mission-critical at this point.

* Mark WRITE_(UN)PREPARED as EXPERIMENTAL in the public APIs
* Separate out the `_with_txn` crash test jobs by write policy, now `_with_wc_txn`, `_with_wp_txn` and `_with_wup_txn` so that the major functional and maturity differences are better grouped.
* Add `_with_multiops_wup_txn` which was apparently missing
* Clean up db_crashtest.py for better consistency
  * Get rid of awkard "write_policy" parameter that could conflict with authoritative "txn_write_policy" parameter.
  * Similarly, move some multiops logic from different parameter sets to finalize_and_sanitize logic.

Immediate internal follow-up:
* Migrate from `_with_txn` which are now deprecated aliases of `_with_wc_txn` to more jobs with the new variants. And likely also add new multiops job.

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

Test Plan: manual runs of modified jobs, at least long enough to spot check things like txn_write_policy

Reviewed By: hx235

Differential Revision: D72015307

Pulled By: pdillinger

fbshipit-source-id: 06b99b2d1f15ac76fe7b8e22c93a51aaa2a42ecf
2025-04-01 14:17:37 -07:00
Hui Xiao 48eb646787 Mark MaxMemCompactionLevel() deprecated (#13503)
Summary:
**Context/Summary:**

MaxMemCompactionLevel() developed 10 years ago simply returns the level a memtable flushed to, which has historically been L0 and have no plan to change to something different for future. It is also not used in test or internally.

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

Test Plan: CI + fake release

Reviewed By: cbi42

Differential Revision: D72066092

Pulled By: hx235

fbshipit-source-id: 5ff5b16a6664ef3efabd3a6fbd8a2d0529b62460
2025-03-31 19:29:40 -07:00
Changyu Bi 325dcdf2e5 Deprecate ReadOptions::ignore_range_deletions and experimental::PromoteL0() (#13500)
Summary:
based on the option comment, `ignore_range_deletions` was added due to the overhead of range deletions in read path when a DB does not use DeleteRange(). The current implementation should not have a noticeable performance difference in this case.

`experimental::PromoteL0()` can be replaced by doing a manual compaction with proper CompactRangeOptions.

There are some internal use of these option and API so we will remove them later after the usages are updated.

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

Test Plan:
comment change only.
Performance: benchmark the performance difference with `ignore_range_deletions` and without (borrowed flag `universal_incremental` for this purpose), ran at the same time on the same machine.

- random point get:
    - ignore_range_deletions=false: 343078 ops/sec
    - ignore_range_deletions=true: 340219 ops/sec (0.8% slower)
```
(for I in $(seq 1 1); do TEST_TMPDIR=/dev/shm/t1 /data/users/changyubi/vscode-root/rocksdb/db_bench --benchmarks=fillseq,waitforcompaction,readrandom --write_buffer_size=67108864 --writes=1000000 --num=2000000 --reads=1000000  --seed=1723056275 --universal_incremental=false 2>&1 | grep "readrandom"; done;) | awk '{ t += $5; c++; print } END { print 1.0 * t / c }';
```

- sequential scan:
  - ignore_range_deletions=false: 5378104 ops/sec
  - ignore_range_deletions=true: 5393809 ops/sec (0.3% faster)
```
(for I in $(seq 1 10); do TEST_TMPDIR=/dev/shm/t1 /data/users/changyubi/vscode-root/rocksdb/db_bench --benchmarks=fillseq,waitforcompaction,readseq[-X10] --write_buffer_size=67108864 --writes=1000000 --num=2000000  --universal_incremental=true --seed=1723056275 2>1 | grep "\[AVG 10 runs\]"; done;) | awk '{ t += $6; c++; print; } END { printf "%.0f\n", 1.0 * t / c }';
```

The difference in ops/sec for the two benchmarks is likely noise.

Reviewed By: hx235

Differential Revision: D72069223

Pulled By: cbi42

fbshipit-source-id: ad82a051aa4682790d2178cd4fb2d1467397fbb5
2025-03-28 14:49:28 -07:00
prerit 743a02d6f6 Check for yields while waiting for lock in a loop (#13498)
Summary:
Acquiring a lock here can take a long time and cause a user mode scheduler to hold up, as it relies on explicit yielding. Hence, forcing a check here but ignoring any abort requests. Would rely on upstream to take action on aborts.

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

Reviewed By: pdillinger

Differential Revision: D71987173

Pulled By: jainpr

fbshipit-source-id: 4aec40bdf0bc657e29f72c306c576b3117f97a25
2025-03-27 15:10:55 -07:00
131 changed files with 3263 additions and 1754 deletions

No files matched your search

@@ -11,9 +11,9 @@ runs:
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
SNAPPY_HOME: ${{ github.workspace }}/thirdparty/snappy-1.1.8
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.1.8;${{ github.workspace }}/thirdparty/snappy-1.1.8/build
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
SNAPPY_HOME: ${{ github.workspace }}/thirdparty/snappy-1.2.2
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.2.2;${{ github.workspace }}/thirdparty/snappy-1.2.2/build
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.2.2/build/Debug/snappy.lib
run: |-
# NOTE: if ... Exit $LASTEXITCODE lines needed to exit and report failure
echo ===================== Install Dependencies =====================
@@ -22,14 +22,14 @@ runs:
mkdir $Env:THIRDPARTY_HOME
cd $Env:THIRDPARTY_HOME
echo "Building Snappy dependency..."
curl -Lo snappy-1.1.8.zip https://github.com/google/snappy/archive/refs/tags/1.1.8.zip
curl -Lo snappy-1.2.2.zip https://github.com/google/snappy/archive/refs/tags/1.2.2.zip
if(!$?) { Exit $LASTEXITCODE }
unzip -q snappy-1.1.8.zip
unzip -q snappy-1.2.2.zip
if(!$?) { Exit $LASTEXITCODE }
cd snappy-1.1.8
cd snappy-1.2.2
mkdir build
cd build
& cmake -G "$Env:CMAKE_GENERATOR" ..
& cmake -G "$Env:CMAKE_GENERATOR" .. -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF
if(!$?) { Exit $LASTEXITCODE }
msbuild Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
if(!$?) { Exit $LASTEXITCODE }
+27
View File
@@ -1,6 +1,33 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 10.2.1 (04/24/2025)
### Bug Fixes
* Fix improper initialization of ExternalTableOptions
## 10.2.0 (04/21/2025)
### New Features
* Provide histogram stats `COMPACTION_PREFETCH_BYTES` to measure number of bytes for RocksDB's prefetching (as opposed to file
system's prefetch) on SST file during compaction read
* A new API DB::GetNewestUserDefinedTimestamp is added to return the newest user defined timestamp seen in a column family
* Introduce API `IngestWriteBatchWithIndex()` for ingesting updates into DB while bypassing memtable writes. This improves performance when writing a large write batch to the DB.
* Add a new CF option `memtable_op_scan_flush_trigger` that triggers a flush of the memtable if an iterator's Seek()/Next() scans over a certain number of invisible entries from the memtable.
### Public API Changes
* AdvancedColumnFamilyOptions.max_write_buffer_number_to_maintain is deleted. It's deprecated since introduction of a better option max_write_buffer_size_to_maintain since RocksDB 6.5.0.
* Deprecated API `DB::MaxMemCompactionLevel()`.
* Deprecated `ReadOptions::ignore_range_deletions`.
* Deprecated API `experimental::PromoteL0()`.
* Added arbitrary string map for additional options to be overriden for remote compactions
* The fail_if_options_file_error option in DBOptions has been removed. The behavior now is to always return failure in any API that fails to persist the OPTIONS file.
### Behavior Changes
* Make stats `PREFETCH_BYTES_USEFUL`, `PREFETCH_HITS`, `PREFETCH_BYTES` only account for prefetching during user initiated scan
### Bug Fixes
* Fix a bug in Posix file system that the FSWritableFile created via `FileSystem::ReopenWritableFile` internally does not track the correct file size.
* Fix a bug where tail size of remote compaction output is not persisted in primary db's manifest
## 10.1.0 (03/24/2025)
### New Features
* Added a new `DBOptions.calculate_sst_write_lifetime_hint_set` setting that allows to customize which compaction styles SST write lifetime hint calculation is allowed on. Today RocksDB supports only two modes `kCompactionStyleLevel` and `kCompactionStyleUniversal`.
+61 -15
View File
@@ -11,18 +11,29 @@ CRASHTEST_MAKE=$(MAKE) -f crash_test.mk
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --cleanup_cmd='$(DB_CLEANUP_CMD)'
.PHONY: crash_test crash_test_with_atomic_flush crash_test_with_txn \
crash_test_with_wc_txn crash_test_with_wp_txn crash_test_with_wup_txn \
crash_test_with_best_efforts_recovery crash_test_with_ts \
crash_test_with_multiops_wc_txn \
crash_test_with_multiops_wp_txn \
crash_test_with_multiops_wup_txn \
crash_test_with_optimistic_txn \
crash_test_with_tiered_storage \
blackbox_crash_test blackbox_crash_test_with_atomic_flush \
blackbox_crash_test_with_wc_txn blackbox_crash_test_with_wp_txn \
blackbox_crash_test_with_wup_txn \
blackbox_crash_test_with_txn blackbox_crash_test_with_ts \
blackbox_crash_test_with_best_efforts_recovery \
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
whitebox_crash_test_with_txn whitebox_crash_test_with_ts \
blackbox_crash_test_with_multiops_wc_txn \
blackbox_crash_test_with_multiops_wp_txn \
crash_test_with_tiered_storage blackbox_crash_test_with_tiered_storage \
whitebox_crash_test_with_tiered_storage \
whitebox_crash_test_with_optimistic_txn \
blackbox_crash_test_with_multiops_wup_txn \
blackbox_crash_test_with_optimistic_txn \
blackbox_crash_test_with_tiered_storage \
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
whitebox_crash_test_with_wc_txn whitebox_crash_test_with_wp_txn \
whitebox_crash_test_with_wup_txn \
whitebox_crash_test_with_txn whitebox_crash_test_with_ts \
whitebox_crash_test_with_optimistic_txn \
whitebox_crash_test_with_tiered_storage \
crash_test: $(DB_STRESS_CMD)
# Do not parallelize
@@ -34,10 +45,20 @@ crash_test_with_atomic_flush: $(DB_STRESS_CMD)
$(CRASHTEST_MAKE) whitebox_crash_test_with_atomic_flush
$(CRASHTEST_MAKE) blackbox_crash_test_with_atomic_flush
crash_test_with_txn: $(DB_STRESS_CMD)
crash_test_with_wc_txn: $(DB_STRESS_CMD)
# Do not parallelize
$(CRASHTEST_MAKE) whitebox_crash_test_with_txn
$(CRASHTEST_MAKE) blackbox_crash_test_with_txn
$(CRASHTEST_MAKE) whitebox_crash_test_with_wc_txn
$(CRASHTEST_MAKE) blackbox_crash_test_with_wc_txn
crash_test_with_wp_txn: $(DB_STRESS_CMD)
# Do not parallelize
$(CRASHTEST_MAKE) whitebox_crash_test_with_wp_txn
$(CRASHTEST_MAKE) blackbox_crash_test_with_wp_txn
crash_test_with_wup_txn: $(DB_STRESS_CMD)
# Do not parallelize
$(CRASHTEST_MAKE) whitebox_crash_test_with_wup_txn
$(CRASHTEST_MAKE) blackbox_crash_test_with_wup_txn
crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
# Do not parallelize
@@ -62,6 +83,9 @@ crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wp_txn
crash_test_with_multiops_wup_txn: $(DB_STRESS_CMD)
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wup_txn
blackbox_crash_test: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --simple blackbox $(CRASH_TEST_EXT_ARGS)
$(CRASHTEST_PY) blackbox $(CRASH_TEST_EXT_ARGS)
@@ -69,8 +93,14 @@ blackbox_crash_test: $(DB_STRESS_CMD)
blackbox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_wc_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 0 $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_wp_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 1 $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_wup_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 2 $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_best_efforts_recovery: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
@@ -79,10 +109,13 @@ blackbox_crash_test_with_ts: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --enable_ts blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_multiops_txn --write_policy write_committed blackbox $(CRASH_TEST_EXT_ARGS)
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 0 blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_multiops_txn --write_policy write_prepared blackbox $(CRASH_TEST_EXT_ARGS)
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 1 blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_multiops_wup_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 2 blackbox $(CRASH_TEST_EXT_ARGS)
blackbox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --test_tiered_storage blackbox $(CRASH_TEST_EXT_ARGS)
@@ -104,9 +137,17 @@ whitebox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --cf_consistency whitebox --random_kill_odd \
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn whitebox --random_kill_odd \
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_wc_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 0 \
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_wp_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 1 \
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_wup_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 2 \
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
whitebox_crash_test_with_ts: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --enable_ts whitebox --random_kill_odd \
@@ -119,3 +160,8 @@ whitebox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
whitebox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
$(CRASHTEST_PY) --optimistic_txn whitebox --random_kill_odd \
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
# Old names DEPRECATED
crash_test_with_txn: crash_test_with_wc_txn
whitebox_crash_test_with_txn: whitebox_crash_test_with_wc_txn
blackbox_crash_test_with_txn: blackbox_crash_test_with_wc_txn
+27 -23
View File
@@ -42,9 +42,9 @@ Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
void ArenaWrappedDBIter::Init(
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Version* version,
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iteration,
uint64_t version_number, ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
const SequenceNumber& sequence, uint64_t version_number,
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
bool expose_blob_index, bool allow_refresh, ReadOnlyMemTable* active_mem) {
read_options_ = read_options;
if (!CheckFSFeatureSupport(env->GetFileSystem().get(),
FSSupportedOps::kAsyncIO)) {
@@ -52,15 +52,14 @@ void ArenaWrappedDBIter::Init(
}
read_options_.total_order_seek |= ioptions.prefix_seek_opt_in_only;
auto mem = arena_.AllocateAligned(sizeof(DBIter));
db_iter_ = new (mem) DBIter(env, read_options_, ioptions, mutable_cf_options,
ioptions.user_comparator,
/* iter */ nullptr, version, sequence, true,
max_sequential_skip_in_iteration, read_callback,
cfh, expose_blob_index);
db_iter_ = DBIter::NewIter(
env, read_options_, ioptions, mutable_cf_options,
ioptions.user_comparator, /*internal_iter=*/nullptr, version, sequence,
read_callback, cfh, expose_blob_index, active_mem, &arena_);
sv_number_ = version_number;
allow_refresh_ = allow_refresh;
allow_mark_memtable_for_flush_ = active_mem;
memtable_range_tombstone_iter_ = nullptr;
}
@@ -166,9 +165,8 @@ void ArenaWrappedDBIter::DoRefresh(const Snapshot* snapshot,
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_);
read_seq, sv->version_number, read_callback_, cfh_, expose_blob_index_,
allow_refresh_, allow_mark_memtable_for_flush_ ? sv->mem : nullptr);
InternalIterator* internal_iter = db_impl->NewInternalIterator(
read_options_, cfd, sv, &arena_, read_seq,
@@ -253,20 +251,26 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
}
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Version* version,
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
uint64_t version_number, ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
iter->Init(env, read_options, ioptions, mutable_cf_options, version, sequence,
max_sequential_skip_in_iterations, version_number, read_callback,
cfh, expose_blob_index, allow_refresh);
Env* env, const ReadOptions& read_options, ColumnFamilyHandleImpl* cfh,
SuperVersion* sv, const SequenceNumber& sequence,
ReadCallback* read_callback, DBImpl* db_impl, bool expose_blob_index,
bool allow_refresh, bool allow_mark_memtable_for_flush) {
ArenaWrappedDBIter* db_iter = new ArenaWrappedDBIter();
db_iter->Init(env, read_options, cfh->cfd()->ioptions(),
sv->mutable_cf_options, sv->current, sequence,
sv->version_number, read_callback, cfh, expose_blob_index,
allow_refresh,
allow_mark_memtable_for_flush ? sv->mem : nullptr);
if (cfh != nullptr && allow_refresh) {
iter->StoreRefreshInfo(cfh, read_callback, expose_blob_index);
db_iter->StoreRefreshInfo(cfh, read_callback, expose_blob_index);
}
return iter;
InternalIterator* internal_iter = db_impl->NewInternalIterator(
db_iter->GetReadOptions(), cfh->cfd(), sv, db_iter->GetArena(), sequence,
/*allow_unprepared_value=*/true, db_iter);
db_iter->SetIterUnderDBIter(internal_iter);
return db_iter;
}
} // namespace ROCKSDB_NAMESPACE
+14 -13
View File
@@ -19,7 +19,6 @@
#include "options/cf_options.h"
#include "rocksdb/db.h"
#include "rocksdb/iterator.h"
#include "util/autovector.h"
namespace ROCKSDB_NAMESPACE {
@@ -99,13 +98,19 @@ class ArenaWrappedDBIter : public Iterator {
bool PrepareValue() override { return db_iter_->PrepareValue(); }
void Prepare(const std::vector<ScanOptions>& scan_opts) override {
db_iter_->Prepare(scan_opts);
}
// FIXME: we could just pass SV in for mutable cf option, version and version
// number, but this is used by SstFileReader which does not have a SV.
void Init(Env* env, const ReadOptions& read_options,
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Version* version,
const SequenceNumber& sequence,
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
const SequenceNumber& sequence, uint64_t version_number,
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
bool expose_blob_index, bool allow_refresh);
bool expose_blob_index, bool allow_refresh,
ReadOnlyMemTable* active_mem);
// Store some parameters so we can refresh the iterator at a later point
// with these same params
@@ -128,20 +133,16 @@ class ArenaWrappedDBIter : public Iterator {
ReadCallback* read_callback_;
bool expose_blob_index_ = false;
bool allow_refresh_ = true;
bool allow_mark_memtable_for_flush_ = true;
// If this is nullptr, it means the mutable memtable does not contain range
// tombstone when added under this DBIter.
std::unique_ptr<TruncatedRangeDelIterator>* memtable_range_tombstone_iter_ =
nullptr;
};
// Generate the arena wrapped iterator class.
// `cfh` is used for reneweal. If left null, renewal will not
// be supported.
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Version* version,
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
uint64_t version_number, ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh = nullptr, bool expose_blob_index = false,
bool allow_refresh = true);
Env* env, const ReadOptions& read_options, ColumnFamilyHandleImpl* cfh,
SuperVersion* sv, const SequenceNumber& sequence,
ReadCallback* read_callback, DBImpl* db_impl, bool expose_blob_index,
bool allow_refresh, bool allow_mark_memtable_for_flush);
} // namespace ROCKSDB_NAMESPACE
+10 -10
View File
@@ -3295,6 +3295,16 @@ uint64_t rocksdb_options_get_periodic_compaction_seconds(
return opt->rep.periodic_compaction_seconds;
}
void rocksdb_options_set_memtable_op_scan_flush_trigger(rocksdb_options_t* opt,
uint32_t n) {
opt->rep.memtable_op_scan_flush_trigger = n;
}
uint32_t rocksdb_options_get_memtable_op_scan_flush_trigger(
rocksdb_options_t* opt) {
return opt->rep.memtable_op_scan_flush_trigger;
}
void rocksdb_options_enable_statistics(rocksdb_options_t* opt) {
opt->rep.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
}
@@ -3804,16 +3814,6 @@ int rocksdb_options_get_min_write_buffer_number_to_merge(
return opt->rep.min_write_buffer_number_to_merge;
}
void rocksdb_options_set_max_write_buffer_number_to_maintain(
rocksdb_options_t* opt, int n) {
opt->rep.max_write_buffer_number_to_maintain = n;
}
int rocksdb_options_get_max_write_buffer_number_to_maintain(
rocksdb_options_t* opt) {
return opt->rep.max_write_buffer_number_to_maintain;
}
void rocksdb_options_set_max_write_buffer_size_to_maintain(
rocksdb_options_t* opt, int64_t n) {
opt->rep.max_write_buffer_size_to_maintain = n;
+10 -12
View File
@@ -2129,6 +2129,10 @@ int main(int argc, char** argv) {
CheckCondition(100000 ==
rocksdb_options_get_periodic_compaction_seconds(o));
rocksdb_options_set_memtable_op_scan_flush_trigger(o, 100);
CheckCondition(100 ==
rocksdb_options_get_memtable_op_scan_flush_trigger(o));
rocksdb_options_set_ttl(o, 5000);
CheckCondition(5000 == rocksdb_options_get_ttl(o));
@@ -2146,10 +2150,6 @@ int main(int argc, char** argv) {
CheckCondition(23 ==
rocksdb_options_get_min_write_buffer_number_to_merge(o));
rocksdb_options_set_max_write_buffer_number_to_maintain(o, 64);
CheckCondition(64 ==
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
rocksdb_options_set_max_write_buffer_size_to_maintain(o, 50000);
CheckCondition(50000 ==
rocksdb_options_get_max_write_buffer_size_to_maintain(o));
@@ -2407,8 +2407,6 @@ int main(int argc, char** argv) {
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(copy));
CheckCondition(23 ==
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
CheckCondition(
64 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
CheckCondition(50000 ==
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(copy));
@@ -2572,6 +2570,12 @@ int main(int argc, char** argv) {
CheckCondition(100000 ==
rocksdb_options_get_periodic_compaction_seconds(o));
rocksdb_options_set_memtable_op_scan_flush_trigger(copy, 800);
CheckCondition(800 ==
rocksdb_options_get_memtable_op_scan_flush_trigger(copy));
CheckCondition(100 ==
rocksdb_options_get_memtable_op_scan_flush_trigger(o));
rocksdb_options_set_ttl(copy, 8000);
CheckCondition(8000 == rocksdb_options_get_ttl(copy));
CheckCondition(5000 == rocksdb_options_get_ttl(o));
@@ -2596,12 +2600,6 @@ int main(int argc, char** argv) {
CheckCondition(23 ==
rocksdb_options_get_min_write_buffer_number_to_merge(o));
rocksdb_options_set_max_write_buffer_number_to_maintain(copy, 128);
CheckCondition(
128 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
CheckCondition(64 ==
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
rocksdb_options_set_max_write_buffer_size_to_maintain(copy, 9000);
CheckCondition(9000 ==
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
+7 -6
View File
@@ -262,15 +262,10 @@ ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
if (result.max_write_buffer_number < 2) {
result.max_write_buffer_number = 2;
}
// fall back max_write_buffer_number_to_maintain if
// max_write_buffer_size_to_maintain is not set
if (result.max_write_buffer_size_to_maintain < 0) {
result.max_write_buffer_size_to_maintain =
result.max_write_buffer_number *
static_cast<int64_t>(result.write_buffer_size);
} else if (result.max_write_buffer_size_to_maintain == 0 &&
result.max_write_buffer_number_to_maintain < 0) {
result.max_write_buffer_number_to_maintain = result.max_write_buffer_number;
}
// bloom filter size shouldn't exceed 1/4 of memtable size.
if (result.memtable_prefix_bloom_size_ratio > 0.25) {
@@ -453,6 +448,13 @@ ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
result.preclude_last_level_data_seconds = 0;
}
if (read_only && result.memtable_op_scan_flush_trigger != 0) {
ROCKS_LOG_WARN(db_options.info_log.get(),
"option memtable_op_scan_flush_trigger is sanitized to "
"0(disabled) for read only DB.");
result.memtable_op_scan_flush_trigger = 0;
}
return result;
}
@@ -577,7 +579,6 @@ ColumnFamilyData::ColumnFamilyData(
write_buffer_manager_(write_buffer_manager),
mem_(nullptr),
imm_(ioptions_.min_write_buffer_number_to_merge,
ioptions_.max_write_buffer_number_to_maintain,
ioptions_.max_write_buffer_size_to_maintain),
super_version_(nullptr),
super_version_number_(0),
+11
View File
@@ -537,6 +537,12 @@ class ColumnFamilyData {
assert(!ts_low.empty());
const Comparator* ucmp = user_comparator();
assert(ucmp);
// Guard against resurrected full_history_ts_low persisted in MANIFEST
// from previous DB sessions. This could happen if UDT was enabled and then
// disabled.
if (ucmp->timestamp_size() == 0) {
return;
}
if (full_history_ts_low_.empty() ||
ucmp->CompareTimestamp(ts_low, full_history_ts_low_) > 0) {
full_history_ts_low_ = std::move(ts_low);
@@ -544,6 +550,11 @@ class ColumnFamilyData {
}
const std::string& GetFullHistoryTsLow() const {
const Comparator* ucmp = user_comparator();
assert(ucmp);
if (ucmp->timestamp_size() == 0) {
assert(full_history_ts_low_.empty());
}
return full_history_ts_low_;
}
+1 -2
View File
@@ -72,7 +72,6 @@ class ColumnFamilyTestBase : public testing::Test {
env_->skip_fsync_ = true;
dbname_ = test::PerThreadDBPath("column_family_test");
db_options_.create_if_missing = true;
db_options_.fail_if_options_file_error = true;
db_options_.env = env_;
}
@@ -2176,7 +2175,7 @@ TEST_P(ColumnFamilyTest, FlushStaleColumnFamilies) {
ASSERT_TRUE(has_cf2_sst);
ASSERT_OK(Flush(0));
ASSERT_EQ(0, dbfull()->TEST_total_log_size());
ASSERT_EQ(0, dbfull()->TEST_wals_total_size());
Close();
}
+5 -2
View File
@@ -77,7 +77,9 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
dbname_, db_id_, db_session_id_, GetCompactionId(sub_compact),
thread_pri_, compaction->compaction_reason(),
compaction->is_full_compaction(), compaction->is_manual_compaction(),
compaction->bottommost_level());
compaction->bottommost_level(), compaction->start_level(),
compaction->output_level());
CompactionServiceScheduleResponse response =
db_options_.compaction_service->Schedule(info, compaction_input_binary);
switch (response.status) {
@@ -240,7 +242,8 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
meta.marked_for_compaction = file.marked_for_compaction;
meta.unique_id = file.unique_id;
meta.temperature = file.file_temperature;
meta.tail_size =
FileMetaData::CalculateTailSize(file_size, file.table_properties);
auto cfd = compaction->column_family_data();
CompactionOutputs* compaction_outputs =
sub_compact->Outputs(file.is_proximal_level_output);
+70 -15
View File
@@ -22,9 +22,9 @@ class MyTestCompactionService : public CompactionService {
options_(options),
statistics_(statistics),
start_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
false, false, false),
false, false, false, -1, -1),
wait_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
false, false, false),
false, false, false, -1, -1),
listeners_(listeners),
table_properties_collector_factories_(
std::move(table_properties_collector_factories)) {}
@@ -84,6 +84,7 @@ class MyTestCompactionService : public CompactionService {
options_override.table_factory = options_.table_factory;
options_override.sst_partitioner_factory = options_.sst_partitioner_factory;
options_override.statistics = statistics_;
options_override.info_log = options_.info_log;
if (!listeners_.empty()) {
options_override.listeners = listeners_;
}
@@ -277,8 +278,17 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
Statistics* primary_statistics = GetPrimaryStatistics();
Statistics* compactor_statistics = GetCompactorStatistics();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BlockBasedTable::PrefetchTail::TaiSizeNotRecorded",
[&](void* /* arg */) {
// Trigger assertion to verify precise tail prefetch size calculation
assert(false);
});
SyncPoint::GetInstance()->EnableProcessing();
GenerateTestData();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
SyncPoint::GetInstance()->DisableProcessing();
VerifyTestData();
auto my_cs = GetCompactionService();
@@ -380,6 +390,7 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
ASSERT_FALSE(result.stats.is_full_compaction);
Close();
SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(CompactionServiceTest, ManualCompaction) {
@@ -890,6 +901,12 @@ TEST_F(CompactionServiceTest, TruncatedOutput) {
Slice end(end_str);
uint64_t comp_num = my_cs->GetCompactionNum();
// Skip calculating tail size to avoid crashing due to truncated file size
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"FileMetaData::CalculateTailSize", [&](void* arg) {
bool* skip = static_cast<bool*>(arg);
*skip = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
CompactionServiceResult* compaction_result =
@@ -906,7 +923,7 @@ TEST_F(CompactionServiceTest, TruncatedOutput) {
ASSERT_OK(s);
ASSERT_GT(file_size, 0);
ASSERT_OK(test::TruncateFile(env_, file_name, file_size / 2));
ASSERT_OK(test::TruncateFile(env_, file_name, file_size / 4));
}
});
SyncPoint::GetInstance()->EnableProcessing();
@@ -1250,17 +1267,31 @@ TEST_F(CompactionServiceTest, PrecludeLastLevel) {
// Verify Output Stats
auto my_cs = GetCompactionService();
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_GT(result.internal_stats.output_level_stats.cpu_micros, 0);
ASSERT_GT(result.internal_stats.output_level_stats.micros, 0);
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_records +
result.internal_stats.proximal_level_stats.num_output_records,
kNumTrigger * kNumKeys);
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_files +
result.internal_stats.proximal_level_stats.num_output_files,
2);
{
CompactionServiceResult result;
my_cs->GetResult(&result);
ASSERT_OK(result.status);
ASSERT_GT(result.internal_stats.output_level_stats.cpu_micros, 0);
ASSERT_GT(result.internal_stats.output_level_stats.micros, 0);
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_records +
result.internal_stats.proximal_level_stats.num_output_records,
kNumTrigger * kNumKeys);
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_files +
result.internal_stats.proximal_level_stats.num_output_files,
2);
CompactionServiceJobInfo info = my_cs->GetCompactionInfoForStart();
ASSERT_EQ(0, info.base_input_level);
ASSERT_EQ(kNumLevels - 1, info.output_level);
}
SyncPoint::GetInstance()->DisableProcessing();
// Disable Preclude feature and run full compaction to the bottommost level
{
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
CompactionServiceJobInfo info = my_cs->GetCompactionInfoForStart();
ASSERT_EQ(kNumLevels - 2, info.base_input_level);
ASSERT_EQ(kNumLevels - 1, info.output_level);
}
}
TEST_F(CompactionServiceTest, ConcurrentCompaction) {
@@ -1330,12 +1361,16 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
ASSERT_EQ(true, info.is_manual_compaction);
ASSERT_EQ(false, info.is_full_compaction);
ASSERT_EQ(true, info.bottommost_level);
ASSERT_EQ(1, info.base_input_level);
ASSERT_EQ(2, info.output_level);
info = my_cs->GetCompactionInfoForWait();
ASSERT_EQ(Env::USER, info.priority);
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
ASSERT_EQ(true, info.is_manual_compaction);
ASSERT_EQ(false, info.is_full_compaction);
ASSERT_EQ(true, info.bottommost_level);
ASSERT_EQ(1, info.base_input_level);
ASSERT_EQ(2, info.output_level);
// Test priority BOTTOM
env_->SetBackgroundThreads(1, Env::BOTTOM);
@@ -1367,18 +1402,24 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
ASSERT_EQ(false, info.is_full_compaction);
ASSERT_EQ(true, info.bottommost_level);
ASSERT_EQ(Env::BOTTOM, info.priority);
ASSERT_EQ(0, info.base_input_level);
ASSERT_EQ(db_->NumberLevels() - 1, info.output_level);
info = my_cs->GetCompactionInfoForWait();
ASSERT_EQ(Env::BOTTOM, info.priority);
ASSERT_EQ(CompactionReason::kLevelL0FilesNum, info.compaction_reason);
ASSERT_EQ(false, info.is_manual_compaction);
ASSERT_EQ(false, info.is_full_compaction);
ASSERT_EQ(true, info.bottommost_level);
ASSERT_EQ(0, info.base_input_level);
ASSERT_EQ(db_->NumberLevels() - 1, info.output_level);
// Test Non-Bottommost Level
options.num_levels = 4;
ReopenWithCompactionService(&options);
my_cs =
static_cast_with_check<MyTestCompactionService>(GetCompactionService());
int compaction_num = my_cs->GetCompactionNum();
ASSERT_EQ(0, compaction_num);
for (int i = 0; i < options.level0_file_num_compaction_trigger; i++) {
for (int j = 0; j < 10; j++) {
@@ -1387,16 +1428,22 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
// This is trivial move. Done locally.
ASSERT_EQ(0, my_cs->GetCompactionNum());
info = my_cs->GetCompactionInfoForStart();
ASSERT_EQ(false, info.is_manual_compaction);
ASSERT_EQ(false, info.is_full_compaction);
ASSERT_EQ(false, info.bottommost_level);
ASSERT_EQ(-1, info.base_input_level);
ASSERT_EQ(-1, info.output_level);
info = my_cs->GetCompactionInfoForWait();
ASSERT_EQ(false, info.is_manual_compaction);
ASSERT_EQ(false, info.is_full_compaction);
ASSERT_EQ(false, info.bottommost_level);
ASSERT_EQ(-1, info.base_input_level);
ASSERT_EQ(-1, info.output_level);
// Test Full Compaction + Bottommost Level
options.num_levels = 6;
@@ -1411,7 +1458,10 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(options.num_levels - 1);
// Force final level compaction
// base_input_level == output_level == last_level
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
@@ -1423,10 +1473,15 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
ASSERT_EQ(true, info.bottommost_level);
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
info = my_cs->GetCompactionInfoForWait();
ASSERT_EQ(options.num_levels - 1, info.base_input_level);
ASSERT_EQ(options.num_levels - 1, info.output_level);
ASSERT_EQ(true, info.is_manual_compaction);
ASSERT_EQ(true, info.is_full_compaction);
ASSERT_EQ(true, info.bottommost_level);
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
ASSERT_EQ(options.num_levels - 1, info.base_input_level);
ASSERT_EQ(options.num_levels - 1, info.output_level);
ASSERT_EQ("0,0,0,0,0,1", FilesPerLevel());
}
TEST_F(CompactionServiceTest, FallbackLocalAuto) {
+10 -14
View File
@@ -75,11 +75,9 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
ret.emplace_back(CurrentFileName(""));
ret.emplace_back(DescriptorFileName("", versions_->manifest_file_number()));
// The OPTIONS file number is zero in read-write mode when OPTIONS file
// writing failed and the DB was configured with
// `fail_if_options_file_error == false`. In read-only mode the OPTIONS file
// number is zero when no OPTIONS file exist at all. In those cases we do not
// record any OPTIONS file in the live file list.
// In read-only mode the OPTIONS file number is zero when no OPTIONS file
// exist at all. In this cases we do not record any OPTIONS file in the live
// file list.
if (versions_->options_file_number() != 0) {
ret.emplace_back(OptionsFileName("", versions_->options_file_number()));
}
@@ -185,14 +183,14 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
return s;
}
Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) {
Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_wal_file) {
uint64_t current_logfile_number;
{
InstrumentedMutexLock l(&mutex_);
current_logfile_number = logfile_number_;
current_logfile_number = cur_wal_number_;
}
return wal_manager_.GetLiveWalFile(current_logfile_number, current_log_file);
return wal_manager_.GetLiveWalFile(current_logfile_number, current_wal_file);
}
Status DBImpl::GetLiveFilesStorageInfo(
@@ -332,7 +330,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
const uint64_t options_size = versions_->options_file_size_;
const uint64_t min_log_num = MinLogNumberToKeep();
// Ensure consistency with manifest for track_and_verify_wals_in_manifest
const uint64_t max_log_num = logfile_number_;
const uint64_t max_log_num = cur_wal_number_;
mutex_.Unlock();
@@ -369,11 +367,9 @@ Status DBImpl::GetLiveFilesStorageInfo(
}
}
// The OPTIONS file number is zero in read-write mode when OPTIONS file
// writing failed and the DB was configured with
// `fail_if_options_file_error == false`. In read-only mode the OPTIONS file
// number is zero when no OPTIONS file exist at all. In those cases we do not
// record any OPTIONS file in the live file list.
// In read-only mode the OPTIONS file number is zero when no OPTIONS file
// exist at all. In this cases we do not record any OPTIONS file in the live
// file list.
if (options_number != 0) {
results.emplace_back();
LiveFileStorageInfo& info = results.back();
+121 -102
View File
@@ -74,6 +74,7 @@
#include "options/cf_options.h"
#include "options/options_helper.h"
#include "options/options_parser.h"
#include "util/udt_util.h"
#ifdef ROCKSDB_JEMALLOC
#include "port/jemalloc_helper.h"
#endif
@@ -169,7 +170,6 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
bool read_only)
: dbname_(dbname),
own_info_log_(options.info_log == nullptr),
init_logger_creation_s_(),
initial_db_options_(SanitizeOptions(dbname, options, read_only,
&init_logger_creation_s_)),
env_(initial_db_options_.env),
@@ -185,7 +185,6 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
mutex_(stats_, immutable_db_options_.clock, DB_MUTEX_WAIT_MICROS,
immutable_db_options_.use_adaptive_mutex),
#endif // COERCE_CONTEXT_SWITCH
default_cf_handle_(nullptr),
error_handler_(this, immutable_db_options_, &mutex_),
event_logger_(immutable_db_options_.info_log.get()),
max_total_in_memory_state_(0),
@@ -194,45 +193,15 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
file_options_, immutable_db_options_)),
seq_per_batch_(seq_per_batch),
batch_per_txn_(batch_per_txn),
next_job_id_(1),
shutting_down_(false),
reject_new_background_jobs_(false),
db_lock_(nullptr),
manual_compaction_paused_(false),
bg_cv_(&mutex_),
logfile_number_(0),
log_dir_synced_(false),
log_empty_(true),
persist_stats_cf_handle_(nullptr),
log_sync_cv_(&log_write_mutex_),
total_log_size_(0),
is_snapshot_supported_(true),
wal_sync_cv_(&wal_write_mutex_),
write_buffer_manager_(immutable_db_options_.write_buffer_manager.get()),
write_thread_(immutable_db_options_),
nonmem_write_thread_(immutable_db_options_),
write_controller_(mutable_db_options_.delayed_write_rate),
last_batch_group_size_(0),
unscheduled_flushes_(0),
unscheduled_compactions_(0),
bg_bottom_compaction_scheduled_(0),
bg_compaction_scheduled_(0),
num_running_compactions_(0),
bg_flush_scheduled_(0),
num_running_flushes_(0),
bg_purge_scheduled_(0),
disable_delete_obsolete_files_(0),
pending_purge_obsolete_files_(0),
delete_obsolete_files_last_run_(immutable_db_options_.clock->NowMicros()),
has_unpersisted_data_(false),
unable_to_release_oldest_log_(false),
num_running_ingest_file_(0),
wal_manager_(immutable_db_options_, file_options_, io_tracer_,
seq_per_batch),
bg_work_paused_(0),
bg_compaction_paused_(0),
refitting_level_(false),
opened_successfully_(false),
periodic_task_scheduler_(),
two_write_queues_(options.two_write_queues),
manual_wal_flush_(options.manual_wal_flush),
// last_sequencee_ is always maintained by the main queue that also writes
@@ -250,14 +219,11 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
// requires a custom gc for compaction, we use that to set use_custom_gc_
// as well.
use_custom_gc_(seq_per_batch),
shutdown_initiated_(false),
own_sfm_(options.sst_file_manager == nullptr),
closed_(false),
atomic_flush_install_cv_(&mutex_),
blob_callback_(immutable_db_options_.sst_file_manager.get(), &mutex_,
&error_handler_, &event_logger_,
immutable_db_options_.listeners, dbname_),
lock_wal_count_(0) {
immutable_db_options_.listeners, dbname_) {
// !batch_per_trx_ implies seq_per_batch_ because it is only unset for
// WriteUnprepared, which should use seq_per_batch_.
assert(batch_per_txn_ || seq_per_batch_);
@@ -636,8 +602,8 @@ Status DBImpl::CloseHelper() {
mutex_.Lock();
}
{
InstrumentedMutexLock lock(&log_write_mutex_);
for (auto l : logs_to_free_) {
InstrumentedMutexLock lock(&wal_write_mutex_);
for (auto l : wals_to_free_) {
delete l;
}
for (auto& log : logs_) {
@@ -1180,11 +1146,11 @@ Status DBImpl::TablesRangeTombstoneSummary(ColumnFamilyHandle* column_family,
void DBImpl::ScheduleBgLogWriterClose(JobContext* job_context) {
mutex_.AssertHeld();
if (!job_context->logs_to_free.empty()) {
for (auto l : job_context->logs_to_free) {
if (!job_context->wals_to_free.empty()) {
for (auto l : job_context->wals_to_free) {
AddToLogsToFreeQueue(l);
}
job_context->logs_to_free.clear();
job_context->wals_to_free.clear();
}
}
@@ -1443,7 +1409,7 @@ Status DBImpl::SetDBOptions(
WriteThread::Writer w;
write_thread_.EnterUnbatched(&w, &mutex_);
if (wal_other_option_changed ||
total_log_size_ > GetMaxTotalWalSize()) {
wals_total_size_.LoadRelaxed() > GetMaxTotalWalSize()) {
Status purge_wal_status = SwitchWAL(&write_context);
if (!purge_wal_status.ok()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
@@ -1470,14 +1436,9 @@ Status DBImpl::SetDBOptions(
ROCKS_LOG_INFO(immutable_db_options_.info_log, "SetDBOptions() succeeded");
new_options.Dump(immutable_db_options_.info_log.get());
if (!persist_options_status.ok()) {
if (immutable_db_options_.fail_if_options_file_error) {
s = Status::IOError(
"SetDBOptions() succeeded, but unable to persist options",
persist_options_status.ToString());
}
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Unable to persist options in SetDBOptions() -- %s",
persist_options_status.ToString().c_str());
s = Status::IOError(
"SetDBOptions() succeeded, but unable to persist options",
persist_options_status.ToString());
}
} else {
ROCKS_LOG_WARN(immutable_db_options_.info_log, "SetDBOptions failed");
@@ -1512,8 +1473,8 @@ Status DBImpl::FlushWAL(const WriteOptions& write_options, bool sync) {
if (manual_wal_flush_) {
IOStatus io_s;
{
// We need to lock log_write_mutex_ since logs_ might change concurrently
InstrumentedMutexLock wl(&log_write_mutex_);
// We need to lock wal_write_mutex_ since logs_ might change concurrently
InstrumentedMutexLock wl(&wal_write_mutex_);
log::Writer* cur_log_writer = logs_.back().writer;
io_s = cur_log_writer->WriteBuffer(write_options);
}
@@ -1540,7 +1501,7 @@ Status DBImpl::FlushWAL(const WriteOptions& write_options, bool sync) {
}
bool DBImpl::WALBufferIsEmpty() {
InstrumentedMutexLock l(&log_write_mutex_);
InstrumentedMutexLock l(&wal_write_mutex_);
log::Writer* cur_log_writer = logs_.back().writer;
auto res = cur_log_writer->BufferIsEmpty();
return res;
@@ -1548,7 +1509,7 @@ bool DBImpl::WALBufferIsEmpty() {
Status DBImpl::GetOpenWalSizes(std::map<uint64_t, uint64_t>& number_to_size) {
assert(number_to_size.empty());
InstrumentedMutexLock l(&log_write_mutex_);
InstrumentedMutexLock l(&wal_write_mutex_);
for (auto& log : logs_) {
auto* open_file = log.writer->file();
if (open_file) {
@@ -1590,15 +1551,15 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
uint64_t up_to_number;
{
InstrumentedMutexLock l(&log_write_mutex_);
InstrumentedMutexLock l(&wal_write_mutex_);
assert(!logs_.empty());
maybe_active_number = logfile_number_;
maybe_active_number = cur_wal_number_;
up_to_number =
include_current_wal ? maybe_active_number : maybe_active_number - 1;
while (logs_.front().number <= up_to_number && logs_.front().IsSyncing()) {
log_sync_cv_.Wait();
wal_sync_cv_.Wait();
}
// First check that logs are safe to sync in background.
if (include_current_wal &&
@@ -1622,7 +1583,7 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
}
}
need_wal_dir_sync = !log_dir_synced_;
need_wal_dir_sync = !wal_dir_synced_;
}
if (include_current_wal) {
@@ -1695,7 +1656,7 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
/*arg=*/nullptr);
}
{
InstrumentedMutexLock l(&log_write_mutex_);
InstrumentedMutexLock l(&wal_write_mutex_);
for (auto* wal : wals_internally_closed) {
// We can only modify the state of log::Writer under the mutex
bool was_closed = wal->PublishIfClosed();
@@ -1812,9 +1773,9 @@ Status DBImpl::UnlockWAL() {
void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
VersionEdit* synced_wals) {
log_write_mutex_.AssertHeld();
if (synced_dir && logfile_number_ == up_to) {
log_dir_synced_ = true;
wal_write_mutex_.AssertHeld();
if (synced_dir && cur_wal_number_ == up_to) {
wal_dir_synced_ = true;
}
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;) {
auto& wal = *it;
@@ -1836,7 +1797,7 @@ void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
(immutable_db_options_.background_close_inactive_wals &&
wal.GetPreSyncSize() == wal.writer->file()->GetFlushedSize())) {
// Fully synced
logs_to_free_.push_back(wal.ReleaseWriter());
wals_to_free_.push_back(wal.ReleaseWriter());
it = logs_.erase(it);
} else {
wal.FinishSync();
@@ -1849,17 +1810,17 @@ void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
++it;
}
}
log_sync_cv_.SignalAll();
wal_sync_cv_.SignalAll();
}
void DBImpl::MarkLogsNotSynced(uint64_t up_to) {
log_write_mutex_.AssertHeld();
wal_write_mutex_.AssertHeld();
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;
++it) {
auto& wal = *it;
wal.FinishSync();
}
log_sync_cv_.SignalAll();
wal_sync_cv_.SignalAll();
}
SequenceNumber DBImpl::GetLatestSequenceNumber() const {
@@ -1895,6 +1856,69 @@ Status DBImpl::GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
return Status::OK();
}
Status DBImpl::GetNewestUserDefinedTimestamp(ColumnFamilyHandle* column_family,
std::string* newest_timestamp) {
if (newest_timestamp == nullptr) {
return Status::InvalidArgument("newest_timestamp is nullptr");
}
ColumnFamilyData* cfd = nullptr;
if (column_family == nullptr) {
cfd = default_cf_handle_->cfd();
} else {
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
assert(cfh != nullptr);
cfd = cfh->cfd();
}
assert(cfd != nullptr && cfd->user_comparator() != nullptr);
if (cfd->user_comparator()->timestamp_size() == 0) {
return Status::InvalidArgument(
"Timestamp is not enabled in this column family");
}
if (cfd->ioptions().persist_user_defined_timestamps) {
return Status::NotSupported(
"GetNewestUserDefinedTimestamp doesn't support the case when user"
"defined timestamps are persisted.");
}
Status status;
// Acquire SuperVersion
SuperVersion* sv = GetAndRefSuperVersion(cfd);
{
InstrumentedMutexLock l(&mutex_);
bool enter_write_thread = sv->mem == cfd->mem();
WriteThread::Writer w;
// Enter write thread to read the mutable memtable to avoid racing access
// with concurrent writes. No need to enter nonmem_write_thread_ since this
// call only care about memtable writes, not WAL writes.
if (enter_write_thread) {
write_thread_.EnterUnbatched(&w, &mutex_);
WaitForPendingWrites();
}
*newest_timestamp = sv->mem->GetNewestUDT().ToString();
assert(!newest_timestamp->empty() || sv->mem->IsEmpty());
if (enter_write_thread) {
write_thread_.ExitUnbatched(&w);
}
}
// Read from immutable memtables if nothing found in mutable memtable.
if (newest_timestamp->empty()) {
*newest_timestamp = sv->imm->GetNewestUDT().ToString();
}
// Read from SST files if no result can be found in memtables.
if (newest_timestamp->empty() && sv->current->GetSstFilesSize() != 0) {
// full_history_ts_low is used to track the exclusive upperbound of
// flushed user defined timestamp. So we can use it to deduce the newest
// timestamp in the SST files that the column family has seen.
Slice full_history_ts_low = sv->full_history_ts_low;
if (!full_history_ts_low.empty()) {
GetU64CutoffTsFromFullHistoryTsLow(&full_history_ts_low,
newest_timestamp);
}
}
ReturnAndCleanupSuperVersion(cfd, sv);
return status;
}
InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
Arena* arena,
SequenceNumber sequence,
@@ -1928,10 +1952,10 @@ void DBImpl::BackgroundCallPurge() {
TEST_SYNC_POINT("DBImpl::BackgroundCallPurge:beforeMutexLock");
mutex_.Lock();
while (!logs_to_free_queue_.empty()) {
assert(!logs_to_free_queue_.empty());
log::Writer* log_writer = *(logs_to_free_queue_.begin());
logs_to_free_queue_.pop_front();
while (!wals_to_free_queue_.empty()) {
assert(!wals_to_free_queue_.empty());
log::Writer* log_writer = *(wals_to_free_queue_.begin());
wals_to_free_queue_.pop_front();
mutex_.Unlock();
delete log_writer;
mutex_.Lock();
@@ -3597,7 +3621,7 @@ Status DBImpl::CreateColumnFamilyImpl(const ReadOptions& read_options,
edit.AddColumnFamily(column_family_name);
uint32_t new_id = versions_->GetColumnFamilySet()->GetNextColumnFamilyID();
edit.SetColumnFamily(new_id);
edit.SetLogNumber(logfile_number_);
edit.SetLogNumber(cur_wal_number_);
edit.SetComparatorName(cf_options.comparator->Name());
edit.SetPersistUserDefinedTimestamps(
cf_options.persist_user_defined_timestamps);
@@ -3796,6 +3820,16 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
return s.ok() || s.IsIncomplete();
}
std::unique_ptr<MultiScan> DBImpl::NewMultiScan(
const ReadOptions& _read_options, ColumnFamilyHandle* column_family,
const std::vector<ScanOptions>& scan_opts) {
std::unique_ptr<Iterator> iter(NewIterator(_read_options, column_family));
iter->Prepare(scan_opts);
std::unique_ptr<MultiScan> ms_iter =
std::make_unique<MultiScan>(scan_opts, std::move(iter));
return ms_iter;
}
Iterator* DBImpl::NewIterator(const ReadOptions& _read_options,
ColumnFamilyHandle* column_family) {
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
@@ -3852,11 +3886,12 @@ 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,
cfd->user_comparator(), iter, sv->current, kMaxSequenceNumber,
sv->mutable_cf_options.max_sequential_skip_in_iterations,
nullptr /* read_callback */, cfh);
result = DBIter::NewIter(env_, read_options, cfd->ioptions(),
sv->mutable_cf_options, cfd->user_comparator(),
iter, sv->current, kMaxSequenceNumber,
/*read_callback=*/nullptr, cfh,
/*expose_blob_index=*/false,
/*active_mem=*/sv->mem);
} else {
// Note: no need to consider the special case of
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
@@ -3934,18 +3969,9 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(
// Laying out the iterators in the order of being accessed makes it more
// 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,
sv->current, snapshot,
sv->mutable_cf_options.max_sequential_skip_in_iterations,
sv->version_number, read_callback, cfh, expose_blob_index, allow_refresh);
InternalIterator* internal_iter = NewInternalIterator(
db_iter->GetReadOptions(), cfh->cfd(), sv, db_iter->GetArena(), snapshot,
/* allow_unprepared_value */ true, db_iter);
db_iter->SetIterUnderDBIter(internal_iter);
return db_iter;
return NewArenaWrappedDbIterator(
env_, read_options, cfh, sv, snapshot, read_callback, this,
expose_blob_index, allow_refresh, /*allow_mark_memtable_for_flush=*/true);
}
std::unique_ptr<Iterator> DBImpl::NewCoalescingIterator(
@@ -4070,13 +4096,11 @@ Status DBImpl::NewIterators(
cf_sv_pair.super_version,
/* allow_unprepared_value */ true);
iterators->push_back(
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,
cf_sv_pair.super_version->mutable_cf_options
.max_sequential_skip_in_iterations,
nullptr /*read_callback*/, cf_sv_pair.cfh));
DBIter::NewIter(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,
nullptr /*read_callback*/, cf_sv_pair.cfh));
}
} else {
for (const auto& cf_sv_pair : cf_sv_pairs) {
@@ -5435,12 +5459,7 @@ Status DBImpl::WriteOptionsFile(const WriteOptions& write_options,
if (!s.ok()) {
ROCKS_LOG_WARN(immutable_db_options_.info_log,
"Unnable to persist options -- %s", s.ToString().c_str());
if (immutable_db_options_.fail_if_options_file_error) {
s = Status::IOError("Unable to persist options.", s.ToString().c_str());
} else {
// Ignore error
s = Status::OK();
}
s = Status::IOError("Unable to persist options.", s.ToString().c_str());
}
// Restore lock if appropriate
+171 -133
View File
@@ -173,10 +173,10 @@ struct DBOpenLogRecordReadReporter : public log::Reader::Reporter {
void OldLogRecord(size_t bytes) override;
uint64_t GetCorruptedLogNumber() const { return corrupted_log_number_; }
uint64_t GetCorruptedLogNumber() const { return corrupted_wal_number_; }
private:
uint64_t corrupted_log_number_ = kMaxSequenceNumber;
uint64_t corrupted_wal_number_ = kMaxSequenceNumber;
};
// While DB is the public interface of RocksDB, and DBImpl is the actual
@@ -256,6 +256,10 @@ class DBImpl : public DB {
Status WriteWithCallback(const WriteOptions& options, WriteBatch* updates,
UserWriteCallback* user_write_cb) override;
Status IngestWriteBatchWithIndex(
const WriteOptions& options,
std::shared_ptr<WriteBatchWithIndex> wbwi) override;
using DB::Get;
Status Get(const ReadOptions& _read_options,
ColumnFamilyHandle* column_family, const Slice& key,
@@ -379,6 +383,11 @@ class DBImpl : public DB {
const std::vector<ColumnFamilyHandle*>& column_families,
std::vector<Iterator*>* iterators) override;
using DB::NewMultiScan;
std::unique_ptr<MultiScan> NewMultiScan(
const ReadOptions& _read_options, ColumnFamilyHandle* column_family,
const std::vector<ScanOptions>& scan_opts) override;
const Snapshot* GetSnapshot() override;
void ReleaseSnapshot(const Snapshot* snapshot) override;
@@ -497,6 +506,9 @@ class DBImpl : public DB {
Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
std::string* ts_low) override;
Status GetNewestUserDefinedTimestamp(ColumnFamilyHandle* column_family,
std::string* newest_timestamp) override;
Status GetDbIdentity(std::string& identity) const override;
virtual Status GetDbIdentityFromIdentityFile(const IOOptions& opts,
@@ -530,11 +542,11 @@ class DBImpl : public DB {
// Get the known flushed sizes of WALs that might still be written to
// or have pending sync.
// NOTE: unlike alive_log_files_, this function includes WALs that might
// NOTE: unlike alive_wal_files_, this function includes WALs that might
// be obsolete (but not obsolete to a pending Checkpoint) and not yet fully
// synced.
Status GetOpenWalSizes(std::map<uint64_t, uint64_t>& number_to_size);
Status GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) override;
Status GetCurrentWalFile(std::unique_ptr<WalFile>* current_wal_file) override;
Status GetCreationTimeOfOldestFile(uint64_t* creation_time) override;
Status GetUpdatesSince(
@@ -1068,7 +1080,7 @@ class DBImpl : public DB {
void AddToLogsToFreeQueue(log::Writer* log_writer) {
mutex_.AssertHeld();
logs_to_free_queue_.push_back(log_writer);
wals_to_free_queue_.push_back(log_writer);
}
void AddSuperVersionsToFreeQueue(SuperVersion* sv) {
@@ -1133,7 +1145,7 @@ class DBImpl : public DB {
bool TEST_UnableToReleaseOldestLog() { return unable_to_release_oldest_log_; }
bool TEST_IsLogGettingFlushed() {
return alive_log_files_.begin()->getting_flushed;
return alive_wal_files_.begin()->getting_flushed;
}
Status TEST_SwitchMemtable(ColumnFamilyData* cfd = nullptr);
@@ -1213,7 +1225,9 @@ class DBImpl : public DB {
uint64_t TEST_LogfileNumber();
uint64_t TEST_total_log_size() const { return total_log_size_; }
uint64_t TEST_wals_total_size() const {
return wals_total_size_.LoadRelaxed();
}
void TEST_GetAllBlockCaches(std::unordered_set<const Cache*>* cache_set);
@@ -1371,16 +1385,16 @@ class DBImpl : public DB {
// State below is protected by mutex_
// With two_write_queues enabled, some of the variables that accessed during
// WriteToWAL need different synchronization: log_empty_, alive_log_files_,
// logs_, logfile_number_. Refer to the definition of each variable below for
// WriteToWAL need different synchronization: wal_empty_, alive_wal_files_,
// logs_, cur_wal_number_. Refer to the definition of each variable below for
// more description.
//
// `mutex_` can be a hot lock in some workloads, so it deserves dedicated
// cachelines.
mutable CacheAlignedInstrumentedMutex mutex_;
ColumnFamilyHandleImpl* default_cf_handle_;
InternalStats* default_cf_internal_stats_;
ColumnFamilyHandleImpl* default_cf_handle_ = nullptr;
InternalStats* default_cf_internal_stats_ = nullptr;
// table_cache_ provides its own synchronization
std::shared_ptr<Cache> table_cache_;
@@ -1392,7 +1406,7 @@ class DBImpl : public DB {
// only used for dynamically adjusting max_total_wal_size. it is a sum of
// [write_buffer_size * max_write_buffer_number] over all column families
std::atomic<uint64_t> max_total_in_memory_state_;
std::atomic<uint64_t> max_total_in_memory_state_ = 0;
// The options to access storage files
const FileOptions file_options_;
@@ -1419,14 +1433,14 @@ class DBImpl : public DB {
// Each flush or compaction gets its own job id. this counter makes sure
// they're unique
std::atomic<int> next_job_id_;
std::atomic<int> next_job_id_ = 1;
std::atomic<bool> shutting_down_;
std::atomic<bool> shutting_down_ = false;
// No new background jobs can be queued if true. This is used to prevent new
// background jobs from being queued after WaitForCompact() completes waiting
// all background jobs then attempts to close when close_db_ option is true.
bool reject_new_background_jobs_;
bool reject_new_background_jobs_ = false;
// RecoveryContext struct stores the context about version edits along
// with corresponding column_family_data and column_family_options.
@@ -1524,11 +1538,11 @@ class DBImpl : public DB {
// 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);
// error when IngestWBWIAsMemtable fails.
Status IngestWBWIAsMemtable(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
@@ -1558,18 +1572,17 @@ class DBImpl : public DB {
Status WriteImpl(const WriteOptions& options, WriteBatch* updates,
WriteCallback* callback = nullptr,
UserWriteCallback* user_write_cb = nullptr,
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
uint64_t* wal_used = nullptr, uint64_t log_ref = 0,
bool disable_memtable = false, uint64_t* seq_used = nullptr,
size_t batch_cnt = 0,
PreReleaseCallback* pre_release_callback = nullptr,
PostMemTableCallback* post_memtable_callback = nullptr,
std::shared_ptr<WriteBatchWithIndex> wbwi = nullptr,
uint64_t min_prep_log = 0);
std::shared_ptr<WriteBatchWithIndex> wbwi = nullptr);
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
WriteCallback* callback = nullptr,
UserWriteCallback* user_write_cb = nullptr,
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
uint64_t* wal_used = nullptr, uint64_t log_ref = 0,
bool disable_memtable = false,
uint64_t* seq_used = nullptr);
@@ -1596,7 +1609,7 @@ class DBImpl : public DB {
Status WriteImplWALOnly(
WriteThread* write_thread, const WriteOptions& options,
WriteBatch* updates, WriteCallback* callback,
UserWriteCallback* user_write_cb, uint64_t* log_used,
UserWriteCallback* user_write_cb, uint64_t* wal_used,
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
const PublishLastSeq publish_last_seq, const bool disable_memtable);
@@ -1757,9 +1770,9 @@ class DBImpl : public DB {
}
};
struct LogFileNumberSize {
explicit LogFileNumberSize(uint64_t _number) : number(_number) {}
LogFileNumberSize() {}
struct WalFileNumberSize {
explicit WalFileNumberSize(uint64_t _number) : number(_number) {}
WalFileNumberSize() {}
void AddSize(uint64_t new_size) { size += new_size; }
uint64_t number;
uint64_t size = 0;
@@ -1781,6 +1794,13 @@ class DBImpl : public DB {
if (writer->file()) {
// TODO: plumb Env::IOActivity, Env::IOPriority
s = writer->WriteBuffer(WriteOptions());
if (attempt_truncate_size < SIZE_MAX &&
attempt_truncate_size < writer->file()->GetFileSize()) {
Status s2 = writer->file()->writable_file()->Truncate(
attempt_truncate_size, IOOptions{}, nullptr);
// This is just a best effort attempt
s2.PermitUncheckedError();
}
}
delete writer;
writer = nullptr;
@@ -1813,6 +1833,11 @@ class DBImpl : public DB {
getting_synced = false;
}
void SetAttemptTruncateSize(uint64_t size) {
assert(attempt_truncate_size == SIZE_MAX);
attempt_truncate_size = size;
}
uint64_t number;
// Visual Studio doesn't support deque's member to be noncopyable because
// of a std::unique_ptr as a member.
@@ -1825,15 +1850,20 @@ class DBImpl : public DB {
// to be persisted even if appends happen during sync so it can be used for
// tracking the synced size in MANIFEST.
uint64_t pre_sync_size = 0;
// When < SIZE_MAX, attempt to truncate the WAL to this size on close,
// because a bad entry was written to it beyond that point and it likely
// won't be recoverable with the bad entry.
uint64_t attempt_truncate_size = SIZE_MAX;
};
struct LogContext {
explicit LogContext(bool need_sync = false)
: need_log_sync(need_sync), need_log_dir_sync(need_sync) {}
bool need_log_sync = false;
bool need_log_dir_sync = false;
struct WalContext {
explicit WalContext(bool need_sync = false)
: need_wal_sync(need_sync), need_wal_dir_sync(need_sync) {}
bool need_wal_sync = false;
bool need_wal_dir_sync = false;
log::Writer* writer = nullptr;
LogFileNumberSize* log_file_number_size = nullptr;
WalFileNumberSize* wal_file_number_size = nullptr;
uint64_t prev_size = SIZE_MAX;
};
// PurgeFileInfo is a structure to hold information of files to be deleted in
@@ -2041,10 +2071,10 @@ class DBImpl : public DB {
JobContext* job_context, LogBuffer* log_buffer, Env::Priority thread_pri);
// REQUIRES: log_numbers are sorted in ascending order
// corrupted_log_found is set to true if we recover from a corrupted log file.
// corrupted_wal_found is set to true if we recover from a corrupted log file.
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
SequenceNumber* next_sequence, bool read_only,
bool is_retry, bool* corrupted_log_found,
bool is_retry, bool* corrupted_wal_found,
RecoveryContext* recovery_ctx);
void SetupLogFilesRecovery(
@@ -2152,12 +2182,12 @@ class DBImpl : public DB {
// log file to its actual size, thereby freeing preallocated space.
// Return success even if truncate fails
Status GetLogSizeAndMaybeTruncate(uint64_t wal_number, bool truncate,
LogFileNumberSize* log);
WalFileNumberSize* log);
// Restore alive_log_files_ and total_log_size_ after recovery.
// Restore alive_wal_files_ and wals_total_size_ after recovery.
// It needs to run only when there's no flush during recovery
// (e.g. avoid_flush_during_recovery=true). May also trigger flush
// in case total_log_size > max_total_wal_size.
// in case wals_total_size > max_total_wal_size.
Status RestoreAliveLogFiles(const std::vector<uint64_t>& log_numbers);
// num_bytes: for slowdown case, delay time is calculated based on
@@ -2306,7 +2336,7 @@ class DBImpl : public DB {
// REQUIRES: mutex locked
Status PreprocessWrite(const WriteOptions& write_options,
LogContext* log_context, WriteContext* write_context);
WalContext* log_context, WriteContext* write_context);
// Merge write batches in the write group into merged_batch.
// Returns OK if merge is successful.
@@ -2317,20 +2347,21 @@ class DBImpl : public DB {
IOStatus WriteToWAL(const WriteBatch& merged_batch,
const WriteOptions& write_options,
log::Writer* log_writer, uint64_t* log_used,
log::Writer* log_writer, uint64_t* wal_used,
uint64_t* log_size,
LogFileNumberSize& log_file_number_size,
WalFileNumberSize& wal_file_number_size,
SequenceNumber sequence);
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
log::Writer* log_writer, uint64_t* log_used,
bool need_log_sync, bool need_log_dir_sync,
SequenceNumber sequence,
LogFileNumberSize& log_file_number_size);
IOStatus WriteGroupToWAL(const WriteThread::WriteGroup& write_group,
log::Writer* log_writer, uint64_t* wal_used,
bool need_wal_sync, bool need_wal_dir_sync,
SequenceNumber sequence,
WalFileNumberSize& wal_file_number_size);
IOStatus ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
uint64_t* log_used,
SequenceNumber* last_sequence, size_t seq_inc);
IOStatus ConcurrentWriteGroupToWAL(const WriteThread::WriteGroup& write_group,
uint64_t* wal_used,
SequenceNumber* last_sequence,
size_t seq_inc);
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
// Caller must hold mutex_.
@@ -2344,7 +2375,7 @@ class DBImpl : public DB {
void WALIOStatusCheck(const IOStatus& status);
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
void MemTableInsertStatusCheck(const Status& memtable_insert_status);
void HandleMemTableInsertFailure(const Status& nonok_memtable_insert_status);
Status CompactFilesImpl(const CompactionOptions& compact_options,
ColumnFamilyData* cfd, Version* version,
@@ -2697,7 +2728,7 @@ class DBImpl : public DB {
ErrorIteratorFuncType error_iterator_func);
// Lock over the persistent DB state. Non-nullptr iff successfully acquired.
FileLock* db_lock_;
FileLock* db_lock_ = nullptr;
// Guards changes to DB and CF options to ensure consistency between
// * In-memory options objects
@@ -2711,20 +2742,20 @@ class DBImpl : public DB {
// Guards reads and writes to in-memory stats_history_.
InstrumentedMutex stats_history_mutex_;
// In addition to mutex_, log_write_mutex_ protects writes to logs_ and
// logfile_number_. With two_write_queues it also protects alive_log_files_,
// and log_empty_. Refer to the definition of each variable below for more
// In addition to mutex_, wal_write_mutex_ protects writes to logs_ and
// cur_wal_number_. With two_write_queues it also protects alive_wal_files_,
// and wal_empty_. Refer to the definition of each variable below for more
// details.
// Note: to avoid deadlock, if needed to acquire both log_write_mutex_ and
// mutex_, the order should be first mutex_ and then log_write_mutex_.
InstrumentedMutex log_write_mutex_;
// Note: to avoid deadlock, if needed to acquire both wal_write_mutex_ and
// mutex_, the order should be first mutex_ and then wal_write_mutex_.
InstrumentedMutex wal_write_mutex_;
// If zero, manual compactions are allowed to proceed. If non-zero, manual
// compactions may still be running, but will quickly fail with
// `Status::Incomplete`. The value indicates how many threads have paused
// manual compactions. It is accessed in read mode outside the DB mutex in
// compaction code paths.
std::atomic<int> manual_compaction_paused_;
std::atomic<int> manual_compaction_paused_ = false;
// This condition variable is signaled on these conditions:
// * whenever bg_compaction_scheduled_ goes down to 0
@@ -2740,106 +2771,114 @@ class DBImpl : public DB {
// * whenever SetOptions successfully updates options.
// * whenever a column family is dropped.
InstrumentedCondVar bg_cv_;
// Writes are protected by locking both mutex_ and log_write_mutex_, and reads
// must be under either mutex_ or log_write_mutex_. Since after ::Open,
// logfile_number_ is currently updated only in write_thread_, it can be read
ColumnFamilyHandleImpl* persist_stats_cf_handle_ = nullptr;
bool persistent_stats_cfd_exists_ = true;
// Writes are protected by locking both mutex_ and wal_write_mutex_, and reads
// must be under either mutex_ or wal_write_mutex_. Since after ::Open,
// cur_wal_number_ is currently updated only in write_thread_, it can be read
// from the same write_thread_ without any locks.
uint64_t logfile_number_;
uint64_t cur_wal_number_ = 0;
// Log files that we can recycle. Must be protected by db mutex_.
std::deque<uint64_t> log_recycle_files_;
std::deque<uint64_t> wal_recycle_files_;
// The minimum log file number taht can be recycled, if log recycling is
// enabled. This is used to ensure that log files created by previous
// instances of the database are not recycled, as we cannot be sure they
// were created in the recyclable format.
uint64_t min_log_number_to_recycle_;
// Protected by log_write_mutex_.
bool log_dir_synced_;
// Without two_write_queues, read and writes to log_empty_ are protected by
uint64_t min_wal_number_to_recycle_ = 0;
// Protected by wal_write_mutex_.
bool wal_dir_synced_ = false;
// Without two_write_queues, read and writes to wal_empty_ are protected by
// mutex_. Since it is currently updated/read only in write_thread_, it can be
// accessed from the same write_thread_ without any locks. With
// two_write_queues writes, where it can be updated in different threads,
// read and writes are protected by log_write_mutex_ instead. This is to avoid
// expensive mutex_ lock during WAL write, which update log_empty_.
bool log_empty_;
ColumnFamilyHandleImpl* persist_stats_cf_handle_;
bool persistent_stats_cfd_exists_ = true;
// read and writes are protected by wal_write_mutex_ instead. This is to avoid
// expensive mutex_ lock during WAL write, which update wal_empty_.
bool wal_empty_ = true;
// The current WAL file and those that have not been found obsolete from
// memtable flushes. A WAL not on this list might still be pending writer
// flush and/or sync and close and might still be in logs_. alive_log_files_
// is protected by mutex_ and log_write_mutex_ with details as follows:
// flush and/or sync and close and might still be in logs_. alive_wal_files_
// is protected by mutex_ and wal_write_mutex_ with details as follows:
// 1. read by FindObsoleteFiles() which can be called in either application
// thread or RocksDB bg threads, both mutex_ and log_write_mutex_ are
// thread or RocksDB bg threads, both mutex_ and wal_write_mutex_ are
// held.
// 2. pop_front() by FindObsoleteFiles(), both mutex_ and log_write_mutex_
// 2. pop_front() by FindObsoleteFiles(), both mutex_ and wal_write_mutex_
// are held.
// 3. push_back() by DBImpl::Open() and DBImpl::RestoreAliveLogFiles()
// (actually called by Open()), only mutex_ is held because at this point,
// the DB::Open() call has not returned success to application, and the
// only other thread(s) that can conflict are bg threads calling
// FindObsoleteFiles() which ensure that both mutex_ and log_write_mutex_
// are held when accessing alive_log_files_.
// FindObsoleteFiles() which ensure that both mutex_ and wal_write_mutex_
// are held when accessing alive_wal_files_.
// 4. read by DBImpl::Open() is protected by mutex_.
// 5. push_back() by SwitchMemtable(). Both mutex_ and log_write_mutex_ are
// 5. push_back() by SwitchMemtable(). Both mutex_ and wal_write_mutex_ are
// held. This is done by the write group leader. Note that in the case of
// two-write-queues, another WAL-only write thread can be writing to the
// WAL concurrently. See 9.
// 6. read by SwitchWAL() with both mutex_ and log_write_mutex_ held. This is
// 6. read by SwitchWAL() with both mutex_ and wal_write_mutex_ held. This is
// done by write group leader.
// 7. read by ConcurrentWriteToWAL() by the write group leader in the case of
// two-write-queues. Only log_write_mutex_ is held to protect concurrent
// two-write-queues. Only wal_write_mutex_ is held to protect concurrent
// pop_front() by FindObsoleteFiles().
// 8. read by PreprocessWrite() by the write group leader. log_write_mutex_
// 8. read by PreprocessWrite() by the write group leader. wal_write_mutex_
// is held to protect the data structure from concurrent pop_front() by
// FindObsoleteFiles().
// 9. read by ConcurrentWriteToWAL() by a WAL-only write thread in the case
// of two-write-queues. Only log_write_mutex_ is held. This suffices to
// of two-write-queues. Only wal_write_mutex_ is held. This suffices to
// protect the data structure from concurrent push_back() by current
// write group leader as well as pop_front() by FindObsoleteFiles().
std::deque<LogFileNumberSize> alive_log_files_;
std::deque<WalFileNumberSize> alive_wal_files_;
// Total size of all "alive" WALs (for easy access without synchronization)
RelaxedAtomic<uint64_t> wals_total_size_{0};
// Log files that aren't fully synced, and the current log file.
// Synchronization:
// 1. read by FindObsoleteFiles() which can be called either in application
// thread or RocksDB bg threads. log_write_mutex_ is always held, while
// thread or RocksDB bg threads. wal_write_mutex_ is always held, while
// some reads are performed without mutex_.
// 2. pop_front() by FindObsoleteFiles() with only log_write_mutex_ held.
// 3. read by DBImpl::Open() with both mutex_ and log_write_mutex_.
// 4. emplace_back() by DBImpl::Open() with both mutex_ and log_write_mutex.
// 2. pop_front() by FindObsoleteFiles() with only wal_write_mutex_ held.
// 3. read by DBImpl::Open() with both mutex_ and wal_write_mutex_.
// 4. emplace_back() by DBImpl::Open() with both mutex_ and wal_write_mutex.
// Note that at this point, DB::Open() has not returned success to
// application, thus the only other thread(s) that can conflict are bg
// threads calling FindObsoleteFiles(). See 1.
// 5. iteration and clear() from CloseHelper() always hold log_write_mutex
// 5. iteration and clear() from CloseHelper() always hold wal_write_mutex
// and mutex_.
// 6. back() called by APIs FlushWAL() and LockWAL() are protected by only
// log_write_mutex_. These two can be called by application threads after
// wal_write_mutex_. These two can be called by application threads after
// DB::Open() returns success to applications.
// 7. read by SyncWAL(), another API, protected by only log_write_mutex_.
// 7. read by SyncWAL(), another API, protected by only wal_write_mutex_.
// 8. read by MarkLogsNotSynced() and MarkLogsSynced() are protected by
// log_write_mutex_.
// 9. erase() by MarkLogsSynced() protected by log_write_mutex_.
// 10. read by SyncClosedWals() protected by only log_write_mutex_. This can
// wal_write_mutex_.
// 9. erase() by MarkLogsSynced() protected by wal_write_mutex_.
// 10. read by SyncClosedWals() protected by only wal_write_mutex_. This can
// happen in bg flush threads after DB::Open() returns success to
// applications.
// 11. reads, e.g. front(), iteration, and back() called by PreprocessWrite()
// holds only the log_write_mutex_. This is done by the write group
// holds only the wal_write_mutex_. This is done by the write group
// leader. A bg thread calling FindObsoleteFiles() or MarkLogsSynced()
// can happen concurrently. This is fine because log_write_mutex_ is used
// can happen concurrently. This is fine because wal_write_mutex_ is used
// by all parties. See 2, 5, 9.
// 12. reads, empty(), back() called by SwitchMemtable() hold both mutex_ and
// log_write_mutex_. This happens in the write group leader.
// wal_write_mutex_. This happens in the write group leader.
// 13. emplace_back() by SwitchMemtable() hold both mutex_ and
// log_write_mutex_. This happens in the write group leader. Can conflict
// wal_write_mutex_. This happens in the write group leader. Can conflict
// with bg threads calling FindObsoleteFiles(), MarkLogsSynced(),
// SyncClosedWals(), etc. as well as application threads calling
// FlushWAL(), SyncWAL(), LockWAL(). This is fine because all parties
// require at least log_write_mutex_.
// require at least wal_write_mutex_.
// 14. iteration called in WriteToWAL(write_group) protected by
// log_write_mutex_. This is done by write group leader when
// wal_write_mutex_. This is done by write group leader when
// two-write-queues is disabled and write needs to sync logs.
// 15. back() called in ConcurrentWriteToWAL() protected by log_write_mutex_.
// 15. back() called in ConcurrentWriteToWAL() protected by wal_write_mutex_.
// This can be done by the write group leader if two-write-queues is
// enabled. It can also be done by another WAL-only write thread.
//
@@ -2856,23 +2895,22 @@ class DBImpl : public DB {
std::deque<LogWriterNumber> logs_;
// Signaled when getting_synced becomes false for some of the logs_.
InstrumentedCondVar log_sync_cv_;
InstrumentedCondVar wal_sync_cv_;
// This is the app-level state that is written to the WAL but will be used
// only during recovery. Using this feature enables not writing the state to
// memtable on normal writes and hence improving the throughput. Each new
// write of the state will replace the previous state entirely even if the
// keys in the two consecutive states do not overlap.
// It is protected by log_write_mutex_ when two_write_queues_ is enabled.
// It is protected by wal_write_mutex_ when two_write_queues_ is enabled.
// Otherwise only the heaad of write_thread_ can access it.
WriteBatch cached_recoverable_state_;
std::atomic<bool> cached_recoverable_state_empty_ = {true};
std::atomic<uint64_t> total_log_size_;
// If this is non-empty, we need to delete these log files in background
// threads. Protected by log_write_mutex_.
autovector<log::Writer*> logs_to_free_;
// threads. Protected by wal_write_mutex_.
autovector<log::Writer*> wals_to_free_;
bool is_snapshot_supported_;
bool is_snapshot_supported_ = true;
std::map<uint64_t, std::map<std::string, uint64_t>> stats_history_;
@@ -2896,7 +2934,7 @@ class DBImpl : public DB {
// sleep if it uses up the quota.
// Note: This is to protect memtable and compaction. If the batch only writes
// to the WAL its size need not to be included in this.
uint64_t last_batch_group_size_;
uint64_t last_batch_group_size_ = 0;
FlushScheduler flush_scheduler_;
@@ -2955,32 +2993,32 @@ class DBImpl : public DB {
std::unordered_set<uint64_t> files_grabbed_for_purge_;
// A queue to store log writers to close. Protected by db mutex_.
std::deque<log::Writer*> logs_to_free_queue_;
std::deque<log::Writer*> wals_to_free_queue_;
std::deque<SuperVersion*> superversions_to_free_queue_;
int unscheduled_flushes_;
int unscheduled_flushes_ = 0;
int unscheduled_compactions_;
int unscheduled_compactions_ = 0;
// count how many background compactions are running or have been scheduled in
// the BOTTOM pool
int bg_bottom_compaction_scheduled_;
int bg_bottom_compaction_scheduled_ = 0;
// count how many background compactions are running or have been scheduled
int bg_compaction_scheduled_;
int bg_compaction_scheduled_ = 0;
// stores the number of compactions are currently running
int num_running_compactions_;
int num_running_compactions_ = 0;
// number of background memtable flush jobs, submitted to the HIGH pool
int bg_flush_scheduled_;
int bg_flush_scheduled_ = 0;
// stores the number of flushes are currently running
int num_running_flushes_;
int num_running_flushes_ = 0;
// number of background obsolete file purge jobs, submitted to the HIGH pool
int bg_purge_scheduled_;
int bg_purge_scheduled_ = 0;
std::deque<ManualCompactionState*> manual_compaction_dequeue_;
@@ -2990,11 +3028,11 @@ class DBImpl : public DB {
// This enables two different threads to call
// EnableFileDeletions() and DisableFileDeletions()
// without any synchronization
int disable_delete_obsolete_files_;
int disable_delete_obsolete_files_ = 0;
// Number of times FindObsoleteFiles has found deletable files and the
// corresponding call to PurgeObsoleteFiles has not yet finished.
int pending_purge_obsolete_files_;
int pending_purge_obsolete_files_ = 0;
// last time when DeleteObsoleteFiles with full scan was executed. Originally
// initialized with startup time.
@@ -3006,12 +3044,12 @@ class DBImpl : public DB {
// The mutex used by switch_cv_. mutex_ should be acquired beforehand.
std::mutex switch_mutex_;
// Number of threads intending to write to memtable
std::atomic<size_t> pending_memtable_writes_ = {};
std::atomic<size_t> pending_memtable_writes_{0};
// A flag indicating whether the current rocksdb database has any
// data that is not yet persisted into either WAL or SST file.
// Used when disableWAL is true.
std::atomic<bool> has_unpersisted_data_;
std::atomic<bool> has_unpersisted_data_{false};
// if an attempt was made to flush all column families that
// the oldest log depends on but uncommitted data in the oldest
@@ -3019,26 +3057,26 @@ class DBImpl : public DB {
// We must attempt to free the dependent memtables again
// at a later time after the transaction in the oldest
// log is fully commited.
bool unable_to_release_oldest_log_;
bool unable_to_release_oldest_log_{false};
// Number of running IngestExternalFile() or CreateColumnFamilyWithImport()
// calls.
// REQUIRES: mutex held
int num_running_ingest_file_;
int num_running_ingest_file_ = 0;
WalManager wal_manager_;
// A value of > 0 temporarily disables scheduling of background work
int bg_work_paused_;
int bg_work_paused_ = 0;
// A value of > 0 temporarily disables scheduling of background compaction
int bg_compaction_paused_;
int bg_compaction_paused_ = 0;
// Guard against multiple concurrent refitting
bool refitting_level_;
bool refitting_level_ = false;
// Indicate DB was opened successfully
bool opened_successfully_;
bool opened_successfully_ = false;
// The min threshold to triggere bottommost compaction for removing
// garbages, among all column families.
@@ -3084,13 +3122,13 @@ class DBImpl : public DB {
// error recovery from going on in parallel. The latter, shutting_down_,
// is set a little later during the shutdown after scheduling memtable
// flushes
std::atomic<bool> shutdown_initiated_;
std::atomic<bool> shutdown_initiated_{false};
// Flag to indicate whether sst_file_manager object was allocated in
// DB::Open() or passed to us
bool own_sfm_;
// Flag to check whether Close() has been called on this DB
bool closed_;
bool closed_ = false;
// save the closing status, for re-calling the close()
Status closing_status_;
// mutex for DB::Close()
@@ -3126,7 +3164,7 @@ class DBImpl : public DB {
// The number of LockWAL called without matching UnlockWAL call.
// See also lock_wal_write_token_
uint32_t lock_wal_count_;
uint32_t lock_wal_count_ = 0;
};
class GetWithTimestampReadCallback : public ReadCallback {
+4 -4
View File
@@ -168,7 +168,7 @@ Status DBImpl::FlushMemTableToOutputFile(
// had not been committed yet. Make sure we sync them to keep the persisted
// WAL state at least as new as the persisted SST state.
const bool needs_to_sync_closed_wals =
logfile_number_ > 0 &&
cur_wal_number_ > 0 &&
(versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1 ||
allow_2pc());
@@ -224,7 +224,7 @@ Status DBImpl::FlushMemTableToOutputFile(
bool need_cancel = false;
IOStatus log_io_s = IOStatus::OK();
if (needs_to_sync_closed_wals) {
// SyncClosedWals() may unlock and re-lock the log_write_mutex multiple
// SyncClosedWals() may unlock and re-lock the wal_write_mutex multiple
// times.
VersionEdit synced_wals;
bool error_recovery_in_prog = error_handler_.IsRecoveryInProgress();
@@ -512,7 +512,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
job_context->job_id, flush_reason);
}
if (logfile_number_ > 0) {
if (cur_wal_number_ > 0) {
// TODO (yanqin) investigate whether we should sync the closed logs for
// single column family case.
VersionEdit synced_wals;
@@ -528,7 +528,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
if (!log_io_s.ok() && !log_io_s.IsShutdownInProgress() &&
!log_io_s.IsColumnFamilyDropped()) {
if (total_log_size_ > 0) {
if (wals_total_size_.LoadRelaxed() > 0) {
error_handler_.SetBGError(log_io_s, BackgroundErrorReason::kFlush);
} else {
// If the WAL is empty, we use different error reason
+3 -3
View File
@@ -224,13 +224,13 @@ void DBImpl::TEST_EndWrite(void* w) {
}
size_t DBImpl::TEST_LogsToFreeSize() {
InstrumentedMutexLock l(&log_write_mutex_);
return logs_to_free_.size();
InstrumentedMutexLock l(&wal_write_mutex_);
return wals_to_free_.size();
}
uint64_t DBImpl::TEST_LogfileNumber() {
InstrumentedMutexLock l(&mutex_);
return logfile_number_;
return cur_wal_number_;
}
void DBImpl::TEST_GetAllBlockCaches(
+35 -35
View File
@@ -28,7 +28,7 @@ uint64_t DBImpl::MinLogNumberToKeep() {
return versions_->min_log_number_to_keep();
}
uint64_t DBImpl::MinLogNumberToRecycle() { return min_log_number_to_recycle_; }
uint64_t DBImpl::MinLogNumberToRecycle() { return min_wal_number_to_recycle_; }
uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
mutex_.AssertHeld();
@@ -272,77 +272,77 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
// logs_ is empty when called during recovery, in which case there can't yet
// be any tracked obsolete logs
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
if (alive_log_files_.empty() || logs_.empty()) {
if (alive_wal_files_.empty() || logs_.empty()) {
mutex_.AssertHeld();
// We may reach here if the db is DBImplSecondary
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
return;
}
bool mutex_unlocked = false;
if (!alive_log_files_.empty() && !logs_.empty()) {
if (!alive_wal_files_.empty() && !logs_.empty()) {
uint64_t min_log_number = job_context->log_number;
size_t num_alive_log_files = alive_log_files_.size();
size_t num_alive_wal_files = alive_wal_files_.size();
// find newly obsoleted log files
while (alive_log_files_.begin()->number < min_log_number) {
auto& earliest = *alive_log_files_.begin();
while (alive_wal_files_.begin()->number < min_log_number) {
auto& earliest = *alive_wal_files_.begin();
if (immutable_db_options_.recycle_log_file_num >
log_recycle_files_.size() &&
wal_recycle_files_.size() &&
earliest.number >= MinLogNumberToRecycle()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"adding log %" PRIu64 " to recycle list\n",
earliest.number);
log_recycle_files_.push_back(earliest.number);
wal_recycle_files_.push_back(earliest.number);
} else {
job_context->log_delete_files.push_back(earliest.number);
}
if (job_context->size_log_to_delete == 0) {
job_context->prev_total_log_size = total_log_size_;
job_context->num_alive_log_files = num_alive_log_files;
job_context->prev_wals_total_size = wals_total_size_.LoadRelaxed();
job_context->num_alive_wal_files = num_alive_wal_files;
}
job_context->size_log_to_delete += earliest.size;
total_log_size_ -= earliest.size;
alive_log_files_.pop_front();
wals_total_size_.FetchSubRelaxed(earliest.size);
alive_wal_files_.pop_front();
// Current log should always stay alive since it can't have
// number < MinLogNumber().
assert(alive_log_files_.size());
assert(alive_wal_files_.size());
}
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
mutex_.Unlock();
mutex_unlocked = true;
TEST_SYNC_POINT_CALLBACK("FindObsoleteFiles::PostMutexUnlock", nullptr);
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
while (!logs_.empty() && logs_.front().number < min_log_number) {
auto& log = logs_.front();
if (log.IsSyncing()) {
log_sync_cv_.Wait();
wal_sync_cv_.Wait();
// logs_ could have changed while we were waiting.
continue;
}
// This WAL file is not live, so it's OK if we never sync the rest of it.
// If it's already closed, then it's been fully synced. If
// !background_close_inactive_wals then we need to Close it before
// removing from logs_ but not blocking while holding log_write_mutex_.
// removing from logs_ but not blocking while holding wal_write_mutex_.
if (!immutable_db_options_.background_close_inactive_wals &&
log.writer->file()) {
// We are taking ownership of and pinning the front entry, so we can
// expect it to be the same after releasing and re-acquiring the lock
log.PrepareForSync();
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
// TODO: maybe check the return value of Close.
// TODO: plumb Env::IOActivity, Env::IOPriority
auto s = log.writer->file()->Close({});
s.PermitUncheckedError();
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
log.writer->PublishIfClosed();
assert(&log == &logs_.front());
log.FinishSync();
log_sync_cv_.SignalAll();
wal_sync_cv_.SignalAll();
}
logs_to_free_.push_back(log.ReleaseWriter());
wals_to_free_.push_back(log.ReleaseWriter());
logs_.pop_front();
}
// Current log cannot be obsolete.
@@ -350,16 +350,16 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
}
// We're just cleaning up for DB::Write().
assert(job_context->logs_to_free.empty());
job_context->logs_to_free = logs_to_free_;
assert(job_context->wals_to_free.empty());
job_context->wals_to_free = wals_to_free_;
logs_to_free_.clear();
log_write_mutex_.Unlock();
wals_to_free_.clear();
wal_write_mutex_.Unlock();
if (mutex_unlocked) {
mutex_.Lock();
}
job_context->log_recycle_files.assign(log_recycle_files_.begin(),
log_recycle_files_.end());
job_context->log_recycle_files.assign(wal_recycle_files_.begin(),
wal_recycle_files_.end());
}
// Delete obsolete files and log status and information of file deletion
@@ -431,7 +431,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
state.sst_live.end());
std::unordered_set<uint64_t> blob_live_set(state.blob_live.begin(),
state.blob_live.end());
std::unordered_set<uint64_t> log_recycle_files_set(
std::unordered_set<uint64_t> wal_recycle_files_set(
state.log_recycle_files.begin(), state.log_recycle_files.end());
std::unordered_set<uint64_t> quarantine_files_set(
state.files_to_quarantine.begin(), state.files_to_quarantine.end());
@@ -491,13 +491,13 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
std::unique(candidate_files.begin(), candidate_files.end()),
candidate_files.end());
if (state.prev_total_log_size > 0) {
if (state.prev_wals_total_size > 0) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[JOB %d] Try to delete WAL files size %" PRIu64
", prev total WAL file size %" PRIu64
", number of live WAL files %" ROCKSDB_PRIszt ".\n",
state.job_id, state.size_log_to_delete,
state.prev_total_log_size, state.num_alive_log_files);
state.prev_wals_total_size, state.num_alive_wal_files);
}
std::vector<std::string> old_info_log_files;
@@ -532,7 +532,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
optsfile_num2 = std::min(optsfile_num2, state.min_options_file_number);
// Close WALs before trying to delete them.
for (const auto w : state.logs_to_free) {
for (const auto w : state.wals_to_free) {
// TODO: maybe check the return value of Close.
// TODO: plumb Env::IOActivity, Env::IOPriority
auto s = w->Close({});
@@ -559,8 +559,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
case kWalFile:
keep = ((number >= state.log_number) ||
(number == state.prev_log_number) ||
(log_recycle_files_set.find(number) !=
log_recycle_files_set.end()));
(wal_recycle_files_set.find(number) !=
wal_recycle_files_set.end()));
break;
case kDescriptorFile:
// Keep my manifest file, and any newer incarnations'
+18 -18
View File
@@ -1119,7 +1119,7 @@ void DBOpenLogRecordReadReporter::Corruption(size_t bytes, const Status& s,
static_cast<int>(bytes), s.ToString().c_str());
if (status != nullptr && status->ok()) {
*status = s;
corrupted_log_number_ = log_number;
corrupted_wal_number_ = log_number;
}
}
@@ -1902,8 +1902,8 @@ void DBImpl::FinishLogFilesRecovery(int job_id, const Status& status) {
}
Status DBImpl::GetLogSizeAndMaybeTruncate(uint64_t wal_number, bool truncate,
LogFileNumberSize* log_ptr) {
LogFileNumberSize log(wal_number);
WalFileNumberSize* log_ptr) {
WalFileNumberSize log(wal_number);
std::string fname =
LogFileName(immutable_db_options_.GetWalDir(), wal_number);
Status s;
@@ -1946,27 +1946,27 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
assert(immutable_db_options_.avoid_flush_during_recovery);
// Mark these as alive so they'll be considered for deletion later by
// FindObsoleteFiles()
total_log_size_ = 0;
log_empty_ = false;
wals_total_size_.StoreRelaxed(0);
wal_empty_ = false;
uint64_t min_wal_with_unflushed_data =
versions_->MinLogNumberWithUnflushedData();
for (auto wal_number : wal_numbers) {
if (!allow_2pc() && wal_number < min_wal_with_unflushed_data) {
// In non-2pc mode, the WAL files not backing unflushed data are not
// alive, thus should not be added to the alive_log_files_.
// alive, thus should not be added to the alive_wal_files_.
continue;
}
// We preallocate space for wals, but then after a crash and restart, those
// preallocated space are not needed anymore. It is likely only the last
// log has such preallocated space, so we only truncate for the last log.
LogFileNumberSize log;
WalFileNumberSize log;
s = GetLogSizeAndMaybeTruncate(
wal_number, /*truncate=*/(wal_number == wal_numbers.back()), &log);
if (!s.ok()) {
break;
}
total_log_size_ += log.size;
alive_log_files_.push_back(log);
wals_total_size_.FetchAddRelaxed(log.size);
alive_wal_files_.push_back(log);
}
return s;
}
@@ -2449,18 +2449,18 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
if (s.ok()) {
// Prevent log files created by previous instance from being recycled.
// They might be in alive_log_file_, and might get recycled otherwise.
impl->min_log_number_to_recycle_ = new_log_number;
impl->min_wal_number_to_recycle_ = new_log_number;
}
if (s.ok()) {
InstrumentedMutexLock wl(&impl->log_write_mutex_);
impl->logfile_number_ = new_log_number;
InstrumentedMutexLock wl(&impl->wal_write_mutex_);
impl->cur_wal_number_ = new_log_number;
assert(new_log != nullptr);
assert(impl->logs_.empty());
impl->logs_.emplace_back(new_log_number, new_log);
}
if (s.ok()) {
impl->alive_log_files_.emplace_back(impl->logfile_number_);
impl->alive_wal_files_.emplace_back(impl->cur_wal_number_);
// In WritePrepared there could be gap in sequence numbers. This breaks
// the trick we use in kPointInTimeRecovery which assumes the first seq in
// the log right after the corrupted log is one larger than the last seq
@@ -2473,14 +2473,14 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
if (recovered_seq != kMaxSequenceNumber) {
WriteBatch empty_batch;
WriteBatchInternal::SetSequence(&empty_batch, recovered_seq);
uint64_t log_used, log_size;
uint64_t wal_used, log_size;
log::Writer* log_writer = impl->logs_.back().writer;
LogFileNumberSize& log_file_number_size = impl->alive_log_files_.back();
WalFileNumberSize& wal_file_number_size = impl->alive_wal_files_.back();
assert(log_writer->get_log_number() == log_file_number_size.number);
assert(log_writer->get_log_number() == wal_file_number_size.number);
impl->mutex_.AssertHeld();
s = impl->WriteToWAL(empty_batch, write_options, log_writer, &log_used,
&log_size, log_file_number_size, recovered_seq);
s = impl->WriteToWAL(empty_batch, write_options, log_writer, &wal_used,
&log_size, wal_file_number_size, recovered_seq);
if (s.ok()) {
// Need to fsync, otherwise it might get lost after a power reset.
s = impl->FlushWAL(write_options, false);
+13 -23
View File
@@ -185,16 +185,10 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& _read_options,
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
: latest_snapshot;
ReadCallback* read_callback = nullptr; // No read callback provided.
auto db_iter = NewArenaWrappedDbIterator(
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);
auto internal_iter = NewInternalIterator(
db_iter->GetReadOptions(), cfd, super_version, db_iter->GetArena(),
read_seq, /* allow_unprepared_value */ true, db_iter);
db_iter->SetIterUnderDBIter(internal_iter);
return db_iter;
return NewArenaWrappedDbIterator(
env_, read_options, cfh, super_version, read_seq, read_callback, this,
/*expose_blob_index=*/false, /*allow_refresh=*/false,
/*allow_mark_memtable_for_flush=*/false);
}
Status DBImplReadOnly::NewIterators(
@@ -231,36 +225,32 @@ Status DBImplReadOnly::NewIterators(
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
: latest_snapshot;
autovector<std::tuple<ColumnFamilyData*, SuperVersion*>> cfd_to_sv;
autovector<std::tuple<ColumnFamilyHandleImpl*, SuperVersion*>> cfh_to_sv;
const bool check_read_ts =
read_options.timestamp && read_options.timestamp->size() > 0;
for (auto cfh : column_families) {
auto* cfd = static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
auto* sv = cfd->GetSuperVersion()->Ref();
cfd_to_sv.emplace_back(cfd, sv);
cfh_to_sv.emplace_back(static_cast_with_check<ColumnFamilyHandleImpl>(cfh),
sv);
if (check_read_ts) {
const Status s =
FailIfReadCollapsedHistory(cfd, sv, *(read_options.timestamp));
if (!s.ok()) {
for (auto prev_entry : cfd_to_sv) {
for (auto prev_entry : cfh_to_sv) {
std::get<1>(prev_entry)->Unref();
}
return s;
}
}
}
assert(cfd_to_sv.size() == column_families.size());
for (auto [cfd, sv] : cfd_to_sv) {
assert(cfh_to_sv.size() == column_families.size());
for (auto [cfh, sv] : cfh_to_sv) {
auto* db_iter = NewArenaWrappedDbIterator(
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);
auto* internal_iter = NewInternalIterator(
db_iter->GetReadOptions(), cfd, sv, db_iter->GetArena(), read_seq,
/* allow_unprepared_value */ true, db_iter);
db_iter->SetIterUnderDBIter(internal_iter);
env_, read_options, cfh, sv, read_seq, read_callback, this,
/*expose_blob_index=*/false, /*allow_refresh=*/false,
/*allow_mark_memtable_for_flush=*/false);
iterators->push_back(db_iter);
}
+34 -15
View File
@@ -566,17 +566,10 @@ ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
assert(snapshot == kMaxSequenceNumber);
snapshot = versions_->LastSequence();
assert(snapshot != kMaxSequenceNumber);
auto db_iter = NewArenaWrappedDbIterator(
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,
allow_refresh);
auto internal_iter = NewInternalIterator(
db_iter->GetReadOptions(), cfh->cfd(), super_version, db_iter->GetArena(),
snapshot, /* allow_unprepared_value */ true, db_iter);
db_iter->SetIterUnderDBIter(internal_iter);
return db_iter;
return NewArenaWrappedDbIterator(env_, read_options, cfh, super_version,
snapshot, read_callback, this,
expose_blob_index, allow_refresh,
/*allow_mark_memtable_for_flush=*/false);
}
Status DBImplSecondary::NewIterators(
@@ -705,13 +698,13 @@ Status DBImplSecondary::CheckConsistency() {
Status DBImplSecondary::TryCatchUpWithPrimary() {
assert(versions_.get() != nullptr);
assert(manifest_reader_.get() != nullptr);
Status s;
// read the manifest and apply new changes to the secondary instance
std::unordered_set<ColumnFamilyData*> cfds_changed;
JobContext job_context(0, true /*create_superversion*/);
{
InstrumentedMutexLock lock_guard(&mutex_);
assert(manifest_reader_.get() != nullptr);
s = static_cast_with_check<ReactiveVersionSet>(versions_.get())
->ReadAndApply(&mutex_, &manifest_reader_,
manifest_reader_status_.get(), &cfds_changed,
@@ -991,7 +984,7 @@ Status DB::OpenAndCompact(
}
// 2. Load the options
DBOptions db_options;
DBOptions base_db_options;
ConfigOptions config_options;
config_options.env = override_options.env;
config_options.ignore_unknown_options = true;
@@ -1004,13 +997,22 @@ Status DB::OpenAndCompact(
std::string options_file_name =
OptionsFileName(name, compaction_input.options_file_number);
s = LoadOptionsFromFile(config_options, options_file_name, &db_options,
s = LoadOptionsFromFile(config_options, options_file_name, &base_db_options,
&all_column_families);
if (!s.ok()) {
return s;
}
// 3. Override pointer configurations in DBOptions with
// 3. Options to Override
// Override serializable configurations from override_options.options_map
DBOptions db_options;
s = GetDBOptionsFromMap(config_options, base_db_options,
override_options.options_map, &db_options);
if (!s.ok()) {
return s;
}
// Override options that are directly set as shared ptrs in
// CompactionServiceOptionsOverride
db_options.env = override_options.env;
db_options.file_checksum_gen_factory =
@@ -1021,6 +1023,7 @@ Status DB::OpenAndCompact(
// We will close the DB after the compaction anyway.
// Open as many files as needed for the compaction.
db_options.max_open_files = -1;
db_options.info_log = override_options.info_log;
// 4. Filter CFs that are needed for OpenAndCompact()
// We do not need to open all column families for the remote compaction.
@@ -1030,6 +1033,18 @@ Status DB::OpenAndCompact(
std::vector<ColumnFamilyDescriptor> column_families;
for (auto& cf : all_column_families) {
if (cf.name == compaction_input.cf_name) {
ColumnFamilyOptions cf_options;
// Override serializable configurations from override_options.options_map
s = GetColumnFamilyOptionsFromMap(config_options, cf.options,
override_options.options_map,
&cf_options);
if (!s.ok()) {
return s;
}
cf.options = std::move(cf_options);
// Override options that are directly set as shared ptrs in
// CompactionServiceOptionsOverride
cf.options.comparator = override_options.comparator;
cf.options.merge_operator = override_options.merge_operator;
cf.options.compaction_filter = override_options.compaction_filter;
@@ -1041,6 +1056,7 @@ Status DB::OpenAndCompact(
override_options.sst_partitioner_factory;
cf.options.table_properties_collector_factories =
override_options.table_properties_collector_factories;
column_families.emplace_back(cf);
} else if (cf.name == kDefaultColumnFamilyName) {
column_families.emplace_back(cf);
@@ -1057,6 +1073,9 @@ Status DB::OpenAndCompact(
}
assert(db);
TEST_SYNC_POINT_CALLBACK(
"DBImplSecondary::OpenAndCompact::AfterOpenAsSecondary:0", db);
// 6. Find the handle of the Column Family that this will compact
ColumnFamilyHandle* cfh = nullptr;
for (auto* handle : handles) {
+274 -176
View File
@@ -157,7 +157,7 @@ Status DBImpl::Write(const WriteOptions& write_options, WriteBatch* my_batch) {
if (s.ok()) {
s = WriteImpl(write_options, my_batch, /*callback=*/nullptr,
/*user_write_cb=*/nullptr,
/*log_used=*/nullptr);
/*wal_used=*/nullptr);
}
return s;
}
@@ -190,11 +190,38 @@ Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
return s;
}
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) {
Status DBImpl::IngestWriteBatchWithIndex(
const WriteOptions& write_options,
std::shared_ptr<WriteBatchWithIndex> wbwi) {
if (!wbwi) {
return Status::InvalidArgument("Batch is nullptr!");
}
if (!write_options.disableWAL) {
return Status::NotSupported(
"IngestWriteBatchWithIndex does not support disableWAL=true");
}
Status s;
if (write_options.protection_bytes_per_key > 0) {
s = WriteBatchInternal::UpdateProtectionInfo(
wbwi->GetWriteBatch(), write_options.protection_bytes_per_key);
}
if (s.ok()) {
WriteBatch dummy_empty_batch;
s = WriteImpl(
write_options, /*updates=*/&dummy_empty_batch, /*callback=*/nullptr,
/*user_write_cb=*/nullptr, /*log_used=*/nullptr, /*log_ref=*/0,
/*disable_memtable=*/false, /*seq_used=*/nullptr,
/*batch_cnt=*/0, /*pre_release_callback=*/nullptr,
/*post_memtable_callback=*/nullptr, /*wbwi=*/wbwi);
}
return s;
}
Status DBImpl::IngestWBWIAsMemtable(
std::shared_ptr<WriteBatchWithIndex> wbwi,
const WBWIMemTable::SeqnoRange& assigned_seqno, uint64_t min_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.
@@ -238,12 +265,30 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
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);
wbwi_memtable->SetMinPrepLog(min_prep_log);
memtables.push_back(wbwi_memtable);
cfd->Ref();
cfds.push_back(cfd);
}
autovector<ColumnFamilyData*> cfds_for_atomic_flush;
if (immutable_db_options_.atomic_flush) {
SelectColumnFamiliesForAtomicFlush(&cfds_for_atomic_flush);
for (auto cfd : cfds_for_atomic_flush) {
bool found = false;
for (auto existing_cfd : cfds) {
if (existing_cfd == cfd) {
found = true;
break;
}
}
if (!found) {
cfd->Ref();
cfds.push_back(cfd);
}
}
}
// Stop writes to the DB by entering both write threads
WriteThread::Writer nonmem_w;
if (two_write_queues_) {
@@ -253,15 +298,16 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
// 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()
for (size_t i = 0; i < cfds.size(); ++i) {
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 (i < memtables.size()) {
s = SwitchMemtable(cfds[i], &write_context, memtables[i],
last_seqno_after_ingest);
} else {
s = SwitchMemtable(cfds[i], &write_context);
}
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
@@ -301,9 +347,18 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
continue;
}
cfd->imm()->FlushRequested();
if (!immutable_db_options_.atomic_flush) {
FlushRequest flush_req;
// TODO: a new flush reason for ingesting memtable
GenerateFlushRequest({cfd}, FlushReason::kExternalFileIngestion,
&flush_req);
EnqueuePendingFlush(flush_req);
}
}
if (immutable_db_options_.atomic_flush) {
AssignAtomicFlushSeq(cfds);
FlushRequest flush_req;
// TODO: a new flush reason for ingesting memtable
GenerateFlushRequest({cfd}, FlushReason::kExternalFileIngestion,
GenerateFlushRequest(cfds, FlushReason::kExternalFileIngestion,
&flush_req);
EnqueuePendingFlush(flush_req);
}
@@ -314,13 +369,12 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
Status DBImpl::WriteImpl(const WriteOptions& write_options,
WriteBatch* my_batch, WriteCallback* callback,
UserWriteCallback* user_write_cb, uint64_t* log_used,
UserWriteCallback* user_write_cb, uint64_t* wal_used,
uint64_t log_ref, bool disable_memtable,
uint64_t* seq_used, size_t batch_cnt,
PreReleaseCallback* pre_release_callback,
PostMemTableCallback* post_memtable_callback,
std::shared_ptr<WriteBatchWithIndex> wbwi,
uint64_t prep_log) {
std::shared_ptr<WriteBatchWithIndex> wbwi) {
assert(!seq_per_batch_ || batch_cnt != 0);
assert(my_batch == nullptr || my_batch->Count() == 0 ||
write_options.protection_bytes_per_key == 0 ||
@@ -409,9 +463,17 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
return Status::NotSupported(
"DeleteRange is not compatible with row cache.");
}
// Whether the WBWI is from transaction commit or a direct write
// (IngestWriteBatchWithIndex())
bool ingest_wbwi_for_commit = false;
if (wbwi) {
assert(prep_log > 0);
// Used only in WriteCommittedTxn::CommitInternal() with no `callback`.
if (my_batch->HasCommit()) {
ingest_wbwi_for_commit = true;
assert(log_ref);
} else {
// Only supports disableWAL for directly ingesting WBWI for now.
assert(write_options.disableWAL);
}
assert(!callback);
if (immutable_db_options_.unordered_write) {
return Status::NotSupported(
@@ -421,9 +483,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
return Status::NotSupported(
"Ingesting WriteBatch does not support pipelined_write");
}
if (immutable_db_options_.atomic_flush) {
if (!wbwi->GetOverwriteKey()) {
return Status::NotSupported(
"Ingesting WriteBatch does not support atomic_flush");
"WriteBatchWithIndex ingestion requires overwrite_key=true");
}
}
// Otherwise IsLatestPersistentState optimization does not make sense
@@ -444,7 +506,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
// they don't consume sequence.
return WriteImplWALOnly(
&nonmem_write_thread_, write_options, my_batch, callback, user_write_cb,
log_used, log_ref, seq_used, batch_cnt, pre_release_callback,
wal_used, log_ref, seq_used, batch_cnt, pre_release_callback,
assign_order, kDontPublishLastSeq, disable_memtable);
}
@@ -458,7 +520,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
// sequence in in increasing order, iii) call pre_release_callback serially
Status status = WriteImplWALOnly(
&write_thread_, write_options, my_batch, callback, user_write_cb,
log_used, log_ref, &seq, sub_batch_cnt, pre_release_callback,
wal_used, log_ref, &seq, sub_batch_cnt, pre_release_callback,
kDoAssignOrder, kDoPublishLastSeq, disable_memtable);
TEST_SYNC_POINT("DBImpl::WriteImpl:UnorderedWriteAfterWriteWAL");
if (!status.ok()) {
@@ -477,7 +539,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
if (immutable_db_options_.enable_pipelined_write) {
return PipelinedWriteImpl(write_options, my_batch, callback, user_write_cb,
log_used, log_ref, disable_memtable, seq_used);
wal_used, log_ref, disable_memtable, seq_used);
}
PERF_TIMER_GUARD(write_pre_and_post_process_time);
@@ -524,16 +586,19 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
assert(tmp_s.ok());
}
}
versions_->SetLastSequence(last_sequence);
MemTableInsertStatusCheck(w.status);
if (w.status.ok()) { // Don't publish a partial batch write
versions_->SetLastSequence(last_sequence);
} else {
HandleMemTableInsertFailure(w.status);
}
write_thread_.ExitAsBatchGroupFollower(&w);
}
assert(w.state == WriteThread::STATE_COMPLETED);
// STATE_COMPLETED conditional below handles exit
}
if (w.state == WriteThread::STATE_COMPLETED) {
if (log_used != nullptr) {
*log_used = w.log_used;
if (wal_used != nullptr) {
*wal_used = w.wal_used;
}
if (seq_used != nullptr) {
*seq_used = w.sequence;
@@ -549,7 +614,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
// when it finds suitable, and finish them in the same write batch.
// This is how a write job could be done by the other writer.
WriteContext write_context;
LogContext log_context(write_options.sync);
// FIXME: also check disableWAL like others?
WalContext wal_context(write_options.sync);
WriteThread::WriteGroup write_group;
bool in_parallel_group = false;
uint64_t last_sequence = kMaxSequenceNumber;
@@ -563,7 +629,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
// PreprocessWrite does its own perf timing.
PERF_TIMER_STOP(write_pre_and_post_process_time);
status = PreprocessWrite(write_options, &log_context, &write_context);
status = PreprocessWrite(write_options, &wal_context, &write_context);
if (!two_write_queues_) {
// Assign it after ::PreprocessWrite since the sequence might advance
// inside it by WriteRecoverableState
@@ -631,7 +697,13 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
continue;
}
// TODO: maybe handle the tracing status?
tracer_->Write(writer->batch).PermitUncheckedError();
if (wbwi && !ingest_wbwi_for_commit) {
// for transaction write, tracer only needs the commit marker which
// is in writer->batch
tracer_->Write(wbwi->GetWriteBatch()).PermitUncheckedError();
} else {
tracer_->Write(writer->batch).PermitUncheckedError();
}
}
}
}
@@ -689,22 +761,21 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
if (!two_write_queues_) {
if (status.ok() && !write_options.disableWAL) {
assert(log_context.log_file_number_size);
LogFileNumberSize& log_file_number_size =
*(log_context.log_file_number_size);
assert(wal_context.wal_file_number_size);
wal_context.prev_size = wal_context.writer->file()->GetFileSize();
PERF_TIMER_GUARD(write_wal_time);
io_s =
WriteToWAL(write_group, log_context.writer, log_used,
log_context.need_log_sync, log_context.need_log_dir_sync,
last_sequence + 1, log_file_number_size);
io_s = WriteGroupToWAL(write_group, wal_context.writer, wal_used,
wal_context.need_wal_sync,
wal_context.need_wal_dir_sync, last_sequence + 1,
*wal_context.wal_file_number_size);
}
} else {
if (status.ok() && !write_options.disableWAL) {
PERF_TIMER_GUARD(write_wal_time);
// LastAllocatedSequence is increased inside WriteToWAL under
// wal_write_mutex_ to ensure ordered events in WAL
io_s = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
seq_inc);
io_s = ConcurrentWriteGroupToWAL(write_group, wal_used, &last_sequence,
seq_inc);
} else {
// Otherwise we inc seq number for memtable writes
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
@@ -716,16 +787,16 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
last_sequence += seq_inc;
// Seqno assigned to this write are [current_sequence, last_sequence]
if (log_context.need_log_sync) {
if (wal_context.need_wal_sync) {
VersionEdit synced_wals;
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
if (status.ok()) {
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
MarkLogsSynced(cur_wal_number_, wal_context.need_wal_dir_sync,
&synced_wals);
} else {
MarkLogsNotSynced(logfile_number_);
MarkLogsNotSynced(cur_wal_number_);
}
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
if (status.ok() && synced_wals.IsWalAddition()) {
InstrumentedMutexLock l(&mutex_);
// TODO: plumb Env::IOActivity, Env::IOPriority
@@ -760,7 +831,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
writer->sequence = next_sequence;
if (writer->pre_release_callback) {
Status ws = writer->pre_release_callback->Callback(
writer->sequence, disable_memtable, writer->log_used, index++,
writer->sequence, disable_memtable, writer->wal_used, index++,
pre_release_callback_cnt);
if (!ws.ok()) {
status = pre_release_cb_status = ws;
@@ -834,12 +905,13 @@ 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()) {
if (wbwi && status.ok() && w.status.ok()) {
uint32_t wbwi_count = wbwi->GetWriteBatch()->Count();
// skip empty batch case
if (wbwi_count) {
// 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.
@@ -852,10 +924,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
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);
status =
IngestWBWIAsMemtable(wbwi, {/*lower_bound=*/lb, /*upper_bound=*/ub},
/*min_prep_log=*/log_ref, last_sequence,
/*memtable_updated=*/memtable_update_count > 0,
write_options.ignore_missing_column_families);
}
}
@@ -873,9 +946,19 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
}
// Note: if we are to resume after non-OK statuses we need to revisit how
// we react to non-OK statuses here.
versions_->SetLastSequence(last_sequence);
if (w.status.ok()) { // Don't publish a partial batch write
versions_->SetLastSequence(last_sequence);
}
}
if (!w.status.ok()) {
if (wal_context.prev_size < SIZE_MAX) {
InstrumentedMutexLock l(&wal_write_mutex_);
if (logs_.back().number == wal_context.wal_file_number_size->number) {
logs_.back().SetAttemptTruncateSize(wal_context.prev_size);
}
}
HandleMemTableInsertFailure(w.status);
}
MemTableInsertStatusCheck(w.status);
write_thread_.ExitAsBatchGroupLeader(write_group, status);
}
@@ -888,7 +971,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
WriteBatch* my_batch, WriteCallback* callback,
UserWriteCallback* user_write_cb,
uint64_t* log_used, uint64_t log_ref,
uint64_t* wal_used, uint64_t log_ref,
bool disable_memtable, uint64_t* seq_used) {
PERF_TIMER_GUARD(write_pre_and_post_process_time);
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
@@ -905,10 +988,10 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
if (w.callback && !w.callback->AllowWriteBatching()) {
write_thread_.WaitForMemTableWriters();
}
LogContext log_context(!write_options.disableWAL && write_options.sync);
WalContext wal_context(!write_options.disableWAL && write_options.sync);
// PreprocessWrite does its own perf timing.
PERF_TIMER_STOP(write_pre_and_post_process_time);
w.status = PreprocessWrite(write_options, &log_context, &write_context);
w.status = PreprocessWrite(write_options, &wal_context, &write_context);
PERF_TIMER_START(write_pre_and_post_process_time);
// This can set non-OK status if callback fail.
@@ -977,13 +1060,13 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
wal_write_group.size - 1);
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
}
assert(log_context.log_file_number_size);
LogFileNumberSize& log_file_number_size =
*(log_context.log_file_number_size);
io_s =
WriteToWAL(wal_write_group, log_context.writer, log_used,
log_context.need_log_sync, log_context.need_log_dir_sync,
current_sequence, log_file_number_size);
assert(wal_context.wal_file_number_size);
WalFileNumberSize& wal_file_number_size =
*(wal_context.wal_file_number_size);
io_s = WriteGroupToWAL(wal_write_group, wal_context.writer, wal_used,
wal_context.need_wal_sync,
wal_context.need_wal_dir_sync, current_sequence,
wal_file_number_size);
w.status = io_s;
}
@@ -995,13 +1078,13 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
}
VersionEdit synced_wals;
if (log_context.need_log_sync) {
InstrumentedMutexLock l(&log_write_mutex_);
if (wal_context.need_wal_sync) {
InstrumentedMutexLock l(&wal_write_mutex_);
if (w.status.ok()) {
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
MarkLogsSynced(cur_wal_number_, wal_context.need_wal_dir_sync,
&synced_wals);
} else {
MarkLogsNotSynced(logfile_number_);
MarkLogsNotSynced(cur_wal_number_);
}
}
if (w.status.ok() && synced_wals.IsWalAddition()) {
@@ -1032,7 +1115,12 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
&flush_scheduler_, &trim_history_scheduler_,
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
false /*concurrent_memtable_writes*/, seq_per_batch_, batch_per_txn_);
versions_->SetLastSequence(memtable_write_group.last_sequence);
if (memtable_write_group.status
.ok()) { // Don't publish a partial batch write
versions_->SetLastSequence(memtable_write_group.last_sequence);
} else {
HandleMemTableInsertFailure(memtable_write_group.status);
}
write_thread_.ExitAsMemTableWriter(&w, memtable_write_group);
}
} else {
@@ -1061,8 +1149,11 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
PERF_TIMER_START(write_pre_and_post_process_time);
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
MemTableInsertStatusCheck(w.status);
versions_->SetLastSequence(w.write_group->last_sequence);
if (w.status.ok()) { // Don't publish a partial batch write
versions_->SetLastSequence(w.write_group->last_sequence);
} else {
HandleMemTableInsertFailure(w.status);
}
write_thread_.ExitAsMemTableWriter(&w, *w.write_group);
}
}
@@ -1134,7 +1225,7 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
Status DBImpl::WriteImplWALOnly(
WriteThread* write_thread, const WriteOptions& write_options,
WriteBatch* my_batch, WriteCallback* callback,
UserWriteCallback* user_write_cb, uint64_t* log_used,
UserWriteCallback* user_write_cb, uint64_t* wal_used,
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
const PublishLastSeq publish_last_seq, const bool disable_memtable) {
@@ -1147,8 +1238,8 @@ Status DBImpl::WriteImplWALOnly(
write_thread->JoinBatchGroup(&w);
assert(w.state != WriteThread::STATE_PARALLEL_MEMTABLE_WRITER);
if (w.state == WriteThread::STATE_COMPLETED) {
if (log_used != nullptr) {
*log_used = w.log_used;
if (wal_used != nullptr) {
*wal_used = w.wal_used;
}
if (seq_used != nullptr) {
*seq_used = w.sequence;
@@ -1164,10 +1255,10 @@ Status DBImpl::WriteImplWALOnly(
// TODO(myabandeh): Make preliminary checks thread-safe so we could do them
// without paying the cost of obtaining the mutex.
LogContext log_context;
WalContext wal_context;
WriteContext write_context;
Status status =
PreprocessWrite(write_options, &log_context, &write_context);
PreprocessWrite(write_options, &wal_context, &write_context);
WriteStatusCheckOnLocked(status);
if (!status.ok()) {
@@ -1264,8 +1355,8 @@ Status DBImpl::WriteImplWALOnly(
}
Status status;
if (!write_options.disableWAL) {
IOStatus io_s =
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
IOStatus io_s = ConcurrentWriteGroupToWAL(write_group, wal_used,
&last_sequence, seq_inc);
status = io_s;
// last_sequence may not be set if there is an error
// This error checking and return is moved up to avoid using uninitialized
@@ -1317,7 +1408,7 @@ Status DBImpl::WriteImplWALOnly(
if (!writer->CallbackFailed() && writer->pre_release_callback) {
assert(writer->sequence != kMaxSequenceNumber);
Status ws = writer->pre_release_callback->Callback(
writer->sequence, disable_memtable, writer->log_used, index++,
writer->sequence, disable_memtable, writer->wal_used, index++,
pre_release_callback_cnt);
if (!ws.ok()) {
status = ws;
@@ -1386,24 +1477,22 @@ void DBImpl::WALIOStatusCheck(const IOStatus& io_status) {
}
}
void DBImpl::MemTableInsertStatusCheck(const Status& status) {
// A non-OK status here indicates that the state implied by the
// WAL has diverged from the in-memory state. This could be
// because of a corrupt write_batch (very bad), or because the
// client specified an invalid column family and didn't specify
// ignore_missing_column_families.
if (!status.ok()) {
mutex_.Lock();
assert(!error_handler_.IsBGWorkStopped());
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
mutex_.Unlock();
}
void DBImpl::HandleMemTableInsertFailure(const Status& status) {
assert(!status.ok());
// A non-OK status on memtable insert indicates that the state implied by the
// WAL has diverged from the in-memory state. This could be because of a
// corrupt write_batch (very bad), or because the client specified an invalid
// column family and didn't specify ignore_missing_column_families.
mutex_.Lock();
assert(!error_handler_.IsBGWorkStopped());
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
mutex_.Unlock();
}
Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
LogContext* log_context,
WalContext* wal_context,
WriteContext* write_context) {
assert(write_context != nullptr && log_context != nullptr);
assert(write_context != nullptr && wal_context != nullptr);
Status status;
if (error_handler_.IsDBStopped()) {
@@ -1413,7 +1502,8 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
PERF_TIMER_GUARD(write_scheduling_flushes_compactions_time);
if (UNLIKELY(status.ok() && total_log_size_ > GetMaxTotalWalSize())) {
if (UNLIKELY(status.ok() &&
wals_total_size_.LoadRelaxed() > GetMaxTotalWalSize())) {
assert(versions_);
InstrumentedMutexLock l(&mutex_);
const ColumnFamilySet* const column_families =
@@ -1482,17 +1572,17 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
WriteBufferManagerStallWrites();
}
}
InstrumentedMutexLock l(&log_write_mutex_);
if (status.ok() && log_context->need_log_sync) {
InstrumentedMutexLock l(&wal_write_mutex_);
if (status.ok() && wal_context->need_wal_sync) {
// Wait until the parallel syncs are finished. Any sync process has to sync
// the front log too so it is enough to check the status of front()
// We do a while loop since log_sync_cv_ is signalled when any sync is
// We do a while loop since wal_sync_cv_ is signalled when any sync is
// finished
// Note: there does not seem to be a reason to wait for parallel sync at
// this early step but it is not important since parallel sync (SyncWAL) and
// need_log_sync are usually not used together.
// need_wal_sync are usually not used together.
while (logs_.front().IsSyncing()) {
log_sync_cv_.Wait();
wal_sync_cv_.Wait();
}
for (auto& log : logs_) {
// This is just to prevent the logs to be synced by a parallel SyncWAL
@@ -1503,12 +1593,12 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
log.PrepareForSync();
}
} else {
log_context->need_log_sync = false;
wal_context->need_wal_sync = false;
}
log_context->writer = logs_.back().writer;
log_context->need_log_dir_sync =
log_context->need_log_dir_sync && !log_dir_synced_;
log_context->log_file_number_size = std::addressof(alive_log_files_.back());
wal_context->writer = logs_.back().writer;
wal_context->need_wal_dir_sync =
wal_context->need_wal_dir_sync && !wal_dir_synced_;
wal_context->wal_file_number_size = std::addressof(alive_wal_files_.back());
return status;
}
@@ -1559,12 +1649,12 @@ Status DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
}
// When two_write_queues_ is disabled, this function is called from the only
// write thread. Otherwise this must be called holding log_write_mutex_.
// write thread. Otherwise this must be called holding wal_write_mutex_.
IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
const WriteOptions& write_options,
log::Writer* log_writer, uint64_t* log_used,
log::Writer* log_writer, uint64_t* wal_used,
uint64_t* log_size,
LogFileNumberSize& log_file_number_size,
WalFileNumberSize& wal_file_number_size,
SequenceNumber sequence) {
assert(log_size != nullptr);
@@ -1576,7 +1666,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
}
*log_size = log_entry.size();
// When two_write_queues_ WriteToWAL has to be protected from concurretn calls
// from the two queues anyway and log_write_mutex_ is already held. Otherwise
// from the two queues anyway and wal_write_mutex_ is already held. Otherwise
// if manual_wal_flush_ is enabled we need to protect log_writer->AddRecord
// from possible concurrent calls via the FlushWAL by the application.
const bool needs_locking = manual_wal_flush_ && !two_write_queues_;
@@ -1584,7 +1674,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
// manual_wal_flush_ feature (by UNLIKELY) instead of the more common case
// when we do not need any locking.
if (UNLIKELY(needs_locking)) {
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
}
IOStatus io_s = log_writer->MaybeAddUserDefinedTimestampSizeRecord(
write_options, versions_->GetColumnFamiliesTimestampSizeForRecord());
@@ -1594,23 +1684,24 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
io_s = log_writer->AddRecord(write_options, log_entry, sequence);
if (UNLIKELY(needs_locking)) {
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
}
if (log_used != nullptr) {
*log_used = logfile_number_;
if (wal_used != nullptr) {
*wal_used = cur_wal_number_;
assert(*wal_used == wal_file_number_size.number);
}
total_log_size_ += log_entry.size();
log_file_number_size.AddSize(*log_size);
log_empty_ = false;
wals_total_size_.FetchAddRelaxed(log_entry.size());
wal_file_number_size.AddSize(*log_size);
wal_empty_ = false;
return io_s;
}
IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
log::Writer* log_writer, uint64_t* log_used,
bool need_log_sync, bool need_log_dir_sync,
SequenceNumber sequence,
LogFileNumberSize& log_file_number_size) {
IOStatus DBImpl::WriteGroupToWAL(const WriteThread::WriteGroup& write_group,
log::Writer* log_writer, uint64_t* wal_used,
bool need_wal_sync, bool need_wal_dir_sync,
SequenceNumber sequence,
WalFileNumberSize& wal_file_number_size) {
IOStatus io_s;
assert(!two_write_queues_);
assert(!write_group.leader->disable_wal);
@@ -1625,10 +1716,10 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
}
if (merged_batch == write_group.leader->batch) {
write_group.leader->log_used = logfile_number_;
write_group.leader->wal_used = cur_wal_number_;
} else if (write_with_wal > 1) {
for (auto writer : write_group) {
writer->log_used = logfile_number_;
writer->wal_used = cur_wal_number_;
}
}
@@ -1640,14 +1731,14 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
WriteOptions write_options;
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, sequence);
io_s = WriteToWAL(*merged_batch, write_options, log_writer, wal_used,
&log_size, wal_file_number_size, sequence);
if (to_be_cached_state) {
cached_recoverable_state_ = *to_be_cached_state;
cached_recoverable_state_empty_ = false;
}
if (io_s.ok() && need_log_sync) {
if (io_s.ok() && need_wal_sync) {
StopWatch sw(immutable_db_options_.clock, stats_, WAL_FILE_SYNC_MICROS);
// It's safe to access logs_ with unlocked mutex_ here because:
// - we've set getting_synced=true for all logs,
@@ -1657,15 +1748,15 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
// - as long as other threads don't modify it, it's safe to read
// from std::deque from multiple threads concurrently.
//
// Sync operation should work with locked log_write_mutex_, because:
// Sync operation should work with locked wal_write_mutex_, because:
// when DBOptions.manual_wal_flush_ is set,
// FlushWAL function will be invoked by another thread.
// if without locked log_write_mutex_, the log file may get data
// if without locked wal_write_mutex_, the log file may get data
// corruption
const bool needs_locking = manual_wal_flush_ && !two_write_queues_;
if (UNLIKELY(needs_locking)) {
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
}
if (io_s.ok()) {
@@ -1688,10 +1779,10 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
}
if (UNLIKELY(needs_locking)) {
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
}
if (io_s.ok() && need_log_dir_sync) {
if (io_s.ok() && need_wal_dir_sync) {
// We only sync WAL directory the first time WAL syncing is
// requested, so that in case users never turn on WAL sync,
// we can avoid the disk I/O in the write code path.
@@ -1706,7 +1797,7 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
}
if (io_s.ok()) {
auto stats = default_cf_internal_stats_;
if (need_log_sync) {
if (need_wal_sync) {
stats->AddDBStats(InternalStats::kIntStatsWalFileSynced, 1);
RecordTick(stats_, WAL_FILE_SYNCED);
}
@@ -1723,8 +1814,8 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
return io_s;
}
IOStatus DBImpl::ConcurrentWriteToWAL(
const WriteThread::WriteGroup& write_group, uint64_t* log_used,
IOStatus DBImpl::ConcurrentWriteGroupToWAL(
const WriteThread::WriteGroup& write_group, uint64_t* wal_used,
SequenceNumber* last_sequence, size_t seq_inc) {
IOStatus io_s;
@@ -1741,14 +1832,14 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
return io_s;
}
// We need to lock log_write_mutex_ since logs_ and alive_log_files might be
// We need to lock wal_write_mutex_ since logs_ and alive_wal_files might be
// pushed back concurrently
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
if (merged_batch == write_group.leader->batch) {
write_group.leader->log_used = logfile_number_;
write_group.leader->wal_used = cur_wal_number_;
} else if (write_with_wal > 1) {
for (auto writer : write_group) {
writer->log_used = logfile_number_;
writer->wal_used = cur_wal_number_;
}
}
*last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
@@ -1756,9 +1847,9 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
WriteBatchInternal::SetSequence(merged_batch, sequence);
log::Writer* log_writer = logs_.back().writer;
LogFileNumberSize& log_file_number_size = alive_log_files_.back();
WalFileNumberSize& wal_file_number_size = alive_wal_files_.back();
assert(log_writer->get_log_number() == log_file_number_size.number);
assert(log_writer->get_log_number() == wal_file_number_size.number);
uint64_t log_size;
@@ -1766,13 +1857,13 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
WriteOptions write_options;
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, sequence);
io_s = WriteToWAL(*merged_batch, write_options, log_writer, wal_used,
&log_size, wal_file_number_size, sequence);
if (to_be_cached_state) {
cached_recoverable_state_ = *to_be_cached_state;
cached_recoverable_state_empty_ = false;
}
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
if (io_s.ok()) {
const bool concurrent = true;
@@ -1800,7 +1891,7 @@ Status DBImpl::WriteRecoverableState() {
bool dont_care_bool;
SequenceNumber next_seq;
if (two_write_queues_) {
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
}
SequenceNumber seq;
if (two_write_queues_) {
@@ -1815,13 +1906,17 @@ Status DBImpl::WriteRecoverableState() {
0 /*recovery_log_number*/, this, false /* concurrent_memtable_writes */,
&next_seq, &dont_care_bool, seq_per_batch_);
auto last_seq = next_seq - 1;
if (two_write_queues_) {
versions_->FetchAddLastAllocatedSequence(last_seq - seq);
versions_->SetLastPublishedSequence(last_seq);
if (status.ok()) { // Don't publish a partial batch write
if (two_write_queues_) {
versions_->FetchAddLastAllocatedSequence(last_seq - seq);
versions_->SetLastPublishedSequence(last_seq);
}
versions_->SetLastSequence(last_seq);
} else {
HandleMemTableInsertFailure(status);
}
versions_->SetLastSequence(last_seq);
if (two_write_queues_) {
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
}
if (status.ok() && recoverable_state_pre_release_callback_) {
const bool DISABLE_MEMTABLE = true;
@@ -1893,7 +1988,10 @@ void DBImpl::AssignAtomicFlushSeq(const autovector<ColumnFamilyData*>& cfds) {
assert(immutable_db_options_.atomic_flush);
auto seq = versions_->LastSequence();
for (auto cfd : cfds) {
cfd->imm()->AssignAtomicFlushSeq(seq);
// cfd can be nullptr, see ScheduleFlushes()
if (cfd) {
cfd->imm()->AssignAtomicFlushSeq(seq);
}
}
}
@@ -1902,11 +2000,11 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
assert(write_context != nullptr);
Status status;
if (alive_log_files_.begin()->getting_flushed) {
if (alive_wal_files_.begin()->getting_flushed) {
return status;
}
auto oldest_alive_log = alive_log_files_.begin()->number;
auto oldest_alive_log = alive_wal_files_.begin()->number;
bool flush_wont_release_oldest_log = false;
if (allow_2pc()) {
auto oldest_log_with_uncommitted_prep =
@@ -1936,14 +2034,14 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
// transactions then we cannot flush this log until those transactions are
// commited.
unable_to_release_oldest_log_ = false;
alive_log_files_.begin()->getting_flushed = true;
alive_wal_files_.begin()->getting_flushed = true;
}
ROCKS_LOG_INFO(
immutable_db_options_.info_log,
"Flushing all column families with data in WAL number %" PRIu64
". Total log size is %" PRIu64 " while max_total_wal_size is %" PRIu64,
oldest_alive_log, total_log_size_.load(), GetMaxTotalWalSize());
oldest_alive_log, wals_total_size_.LoadRelaxed(), GetMaxTotalWalSize());
// no need to refcount because drop is happening in write thread, so can't
// happen while we're in the write thread
autovector<ColumnFamilyData*> cfds;
@@ -2413,21 +2511,21 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
// Do this without holding the dbmutex lock.
assert(versions_->prev_log_number() == 0);
if (two_write_queues_) {
log_write_mutex_.Lock();
wal_write_mutex_.Lock();
}
bool creating_new_log = !log_empty_;
bool creating_new_log = !wal_empty_;
if (two_write_queues_) {
log_write_mutex_.Unlock();
wal_write_mutex_.Unlock();
}
uint64_t recycle_log_number = 0;
// If file deletion is disabled, don't recycle logs since it'll result in
// the file getting renamed
if (creating_new_log && immutable_db_options_.recycle_log_file_num &&
!log_recycle_files_.empty() && IsFileDeletionsEnabled()) {
recycle_log_number = log_recycle_files_.front();
!wal_recycle_files_.empty() && IsFileDeletionsEnabled()) {
recycle_log_number = wal_recycle_files_.front();
}
uint64_t new_log_number =
creating_new_log ? versions_->NewFileNumber() : logfile_number_;
creating_new_log ? versions_->NewFileNumber() : cur_wal_number_;
// For use outside of holding DB mutex
const MutableCFOptions mutable_cf_options_copy =
cfd->GetLatestMutableCFOptions();
@@ -2453,14 +2551,14 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
mutex_.Unlock();
if (creating_new_log) {
PredecessorWALInfo info;
log_write_mutex_.Lock();
wal_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();
wal_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,
@@ -2501,11 +2599,11 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
// concurrent full purges don't delete the file while we're recycling it.
// To achieve that we hold the old log number in the recyclable list until
// after it has been renamed.
assert(log_recycle_files_.front() == recycle_log_number);
log_recycle_files_.pop_front();
assert(wal_recycle_files_.front() == recycle_log_number);
wal_recycle_files_.pop_front();
}
if (s.ok() && creating_new_log) {
InstrumentedMutexLock l(&log_write_mutex_);
InstrumentedMutexLock l(&wal_write_mutex_);
assert(new_log != nullptr);
if (!logs_.empty()) {
// Alway flush the buffer of the last log before switching to a new one
@@ -2527,11 +2625,11 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
}
}
if (s.ok()) {
logfile_number_ = new_log_number;
log_empty_ = true;
log_dir_synced_ = false;
logs_.emplace_back(logfile_number_, new_log);
alive_log_files_.emplace_back(logfile_number_);
cur_wal_number_ = new_log_number;
wal_empty_ = true;
wal_dir_synced_ = false;
logs_.emplace_back(cur_wal_number_, new_log);
alive_wal_files_.emplace_back(cur_wal_number_);
}
}
@@ -2562,7 +2660,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
// obsolete. So we should track the WAL obsoletion event before actually
// updating the empty CF's log number.
uint64_t min_wal_number_to_keep =
versions_->PreComputeMinLogNumberWithUnflushedData(logfile_number_);
versions_->PreComputeMinLogNumberWithUnflushedData(cur_wal_number_);
if (min_wal_number_to_keep >
versions_->GetWalSet().GetMinWalNumberToKeep()) {
// TODO: plumb Env::IOActivity, Env::IOPriority
@@ -2597,7 +2695,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
for (auto cf : empty_cfs) {
if (cf->IsEmpty()) {
cf->SetLogNumber(logfile_number_);
cf->SetLogNumber(cur_wal_number_);
// MEMPURGE: No need to change this, because new adds
// should still receive new sequence numbers.
cf->mem()->SetCreationSeq(versions_->LastSequence());
@@ -2614,14 +2712,14 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
// advance the log number. no need to persist this in the manifest
if (cf->IsEmpty()) {
if (creating_new_log) {
cf->SetLogNumber(logfile_number_);
cf->SetLogNumber(cur_wal_number_);
}
cf->mem()->SetCreationSeq(versions_->LastSequence());
}
}
}
cfd->mem()->SetNextLogNumber(logfile_number_);
cfd->mem()->SetNextLogNumber(cur_wal_number_);
assert(new_mem != nullptr);
cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_);
if (new_imm) {
@@ -2633,7 +2731,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
// 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_);
new_imm->SetNextLogNumber(cur_wal_number_);
cfd->imm()->Add(new_imm, &context->memtables_to_free_);
}
new_mem->Ref();
+29 -26
View File
@@ -9,7 +9,6 @@
#include "db/db_iter.h"
#include <iostream>
#include <limits>
#include <string>
@@ -42,9 +41,8 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
const MutableCFOptions& mutable_cf_options,
const Comparator* cmp, InternalIterator* iter,
const Version* version, SequenceNumber s, bool arena_mode,
uint64_t max_sequential_skip_in_iterations,
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
bool expose_blob_index)
bool expose_blob_index, ReadOnlyMemTable* active_mem)
: prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
env_(_env),
clock_(ioptions.clock),
@@ -58,11 +56,21 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
read_callback_(read_callback),
sequence_(s),
statistics_(ioptions.stats),
max_skip_(max_sequential_skip_in_iterations),
max_skip_(mutable_cf_options.max_sequential_skip_in_iterations),
max_skippable_internal_keys_(read_options.max_skippable_internal_keys),
num_internal_keys_skipped_(0),
iterate_lower_bound_(read_options.iterate_lower_bound),
iterate_upper_bound_(read_options.iterate_upper_bound),
cfh_(cfh),
timestamp_ub_(read_options.timestamp),
timestamp_lb_(read_options.iter_start_ts),
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0),
active_mem_(active_mem),
memtable_seqno_lb_((active_mem_ && !active_mem_->IsEmpty())
? active_mem_->GetFirstSequenceNumber()
: kMaxSequenceNumber),
memtable_op_scan_flush_trigger_(
mutable_cf_options.memtable_op_scan_flush_trigger),
direction_(kForward),
valid_(false),
current_entry_is_merged_(false),
@@ -76,11 +84,7 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
expose_blob_index_(expose_blob_index),
allow_unprepared_value_(read_options.allow_unprepared_value),
is_blob_(false),
arena_mode_(arena_mode),
cfh_(cfh),
timestamp_ub_(read_options.timestamp),
timestamp_lb_(read_options.iter_start_ts),
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0) {
arena_mode_(arena_mode) {
RecordTick(statistics_, NO_ITERATOR_CREATED);
if (pin_thru_lifetime_) {
pinned_iters_mgr_.StartPinning();
@@ -369,6 +373,8 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
// to one.
bool reseek_done = false;
uint64_t mem_ops_scanned = 0;
bool marked_for_flush = false;
do {
// Will update is_key_seqnum_zero_ as soon as we parsed the current key
// but we need to save the previous value to be used in the loop.
@@ -425,6 +431,12 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) <= 0) {
num_skipped++; // skip this entry
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
if (memtable_op_scan_flush_trigger_ && active_mem_ &&
ikey_.sequence >= memtable_seqno_lb_ && !marked_for_flush &&
++mem_ops_scanned >= memtable_op_scan_flush_trigger_) {
active_mem_->MarkForFlush();
marked_for_flush = true;
}
} else {
assert(!skipping_saved_key ||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) > 0);
@@ -446,6 +458,12 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
!iter_.iter()->IsKeyPinned() /* copy */);
skipping_saved_key = true;
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
if (memtable_op_scan_flush_trigger_ && active_mem_ &&
ikey_.sequence >= memtable_seqno_lb_ && !marked_for_flush &&
++mem_ops_scanned >= memtable_op_scan_flush_trigger_) {
active_mem_->MarkForFlush();
marked_for_flush = true;
}
}
break;
case kTypeValue:
@@ -1224,6 +1242,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
if (timestamp_lb_ != nullptr) {
saved_key_.SetInternalKey(ikey);
} else {
saved_key_.SetUserKey(ikey.user_key);
}
valid_ = true;
@@ -1790,21 +1810,4 @@ void DBIter::SeekToLast() {
StripTimestampFromUserKey(saved_key_.GetUserKey(), timestamp_size_)));
}
}
Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options,
const Comparator* user_key_comparator,
InternalIterator* internal_iter, const Version* version,
const SequenceNumber& sequence,
uint64_t max_sequential_skip_in_iterations,
ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh, bool expose_blob_index) {
DBIter* db_iter = new DBIter(
env, read_options, ioptions, mutable_cf_options, user_key_comparator,
internal_iter, version, sequence, false,
max_sequential_skip_in_iterations, read_callback, cfh, expose_blob_index);
return db_iter;
}
} // namespace ROCKSDB_NAMESPACE
+61 -30
View File
@@ -57,6 +57,33 @@ class Version;
// numbers, deletion markers, overwrites, etc.
class DBIter final : public Iterator {
public:
// Return a new DBIter that reads from `internal_iter` at the specified
// `sequence` number.
//
// @param active_mem Pointer to the active memtable that `internal_iter`
// is reading from. If not null, the memtable can be marked for flush
// according to option mutable_cf_options.memtable_op_scan_flush_trigger.
// @param arena_mode If true, the DBIter will be allocated from the arena.
static DBIter* NewIter(Env* env, const ReadOptions& read_options,
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options,
const Comparator* user_key_comparator,
InternalIterator* internal_iter,
const Version* version, const SequenceNumber& sequence,
ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh = nullptr,
bool expose_blob_index = false,
ReadOnlyMemTable* active_mem = nullptr,
Arena* arena = nullptr) {
void* mem = arena ? arena->AllocateAligned(sizeof(DBIter))
: operator new(sizeof(DBIter));
DBIter* db_iter = new (mem)
DBIter(env, read_options, ioptions, mutable_cf_options,
user_key_comparator, internal_iter, version, sequence, arena,
read_callback, cfh, expose_blob_index, active_mem);
return db_iter;
}
// The following is grossly complicated. TODO: clean it up
// Which direction is the iterator currently moving?
// (1) When moving forward:
@@ -113,14 +140,6 @@ class DBIter final : public Iterator {
uint64_t skip_count_;
};
DBIter(Env* _env, const ReadOptions& read_options,
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
InternalIterator* iter, const Version* version, SequenceNumber s,
bool arena_mode, uint64_t max_sequential_skip_in_iterations,
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
bool expose_blob_index);
// No copying allowed
DBIter(const DBIter&) = delete;
void operator=(const DBIter&) = delete;
@@ -220,7 +239,26 @@ class DBIter final : public Iterator {
bool PrepareValue() override;
void Prepare(const std::vector<ScanOptions>& scan_opts) override {
std::optional<std::vector<ScanOptions>> new_scan_opts;
new_scan_opts.emplace(scan_opts);
scan_opts_.swap(new_scan_opts);
if (!scan_opts.empty()) {
iter_.Prepare(&scan_opts_.value());
} else {
iter_.Prepare(nullptr);
}
}
private:
DBIter(Env* _env, const ReadOptions& read_options,
const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
InternalIterator* iter, const Version* version, SequenceNumber s,
bool arena_mode, ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh, bool expose_blob_index,
ReadOnlyMemTable* active_mem);
class BlobReader {
public:
BlobReader(const Version* version, ReadTier read_tier,
@@ -425,6 +463,21 @@ class DBIter final : public Iterator {
IterKey prefix_;
Status status_;
Slice lazy_blob_index_;
// List of operands for merge operator.
MergeContext merge_context_;
LocalStatistics local_stats_;
PinnedIteratorsManager pinned_iters_mgr_;
ColumnFamilyHandleImpl* cfh_;
const Slice* const timestamp_ub_;
const Slice* const timestamp_lb_;
const size_t timestamp_size_;
std::string saved_timestamp_;
std::optional<std::vector<ScanOptions>> scan_opts_;
ReadOnlyMemTable* active_mem_;
SequenceNumber memtable_seqno_lb_;
uint32_t memtable_op_scan_flush_trigger_;
Direction direction_;
bool valid_;
bool current_entry_is_merged_;
@@ -443,29 +496,7 @@ class DBIter final : public Iterator {
// the stacked BlobDB implementation is used, false otherwise.
bool expose_blob_index_;
bool allow_unprepared_value_;
Slice lazy_blob_index_;
bool is_blob_;
bool arena_mode_;
// List of operands for merge operator.
MergeContext merge_context_;
LocalStatistics local_stats_;
PinnedIteratorsManager pinned_iters_mgr_;
ColumnFamilyHandleImpl* cfh_;
const Slice* const timestamp_ub_;
const Slice* const timestamp_lb_;
const size_t timestamp_size_;
std::string saved_timestamp_;
};
// Return a new iterator that converts internal keys (yielded by
// "*internal_iter") that were live at the specified `sequence` number
// into appropriate user keys.
Iterator* NewDBIterator(
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
const MutableCFOptions& mutable_cf_options,
const Comparator* user_key_comparator, InternalIterator* internal_iter,
const Version* version, const SequenceNumber& sequence,
uint64_t max_sequential_skip_in_iterations, ReadCallback* read_callback,
ColumnFamilyHandleImpl* cfh = nullptr, bool expose_blob_index = false);
} // namespace ROCKSDB_NAMESPACE
+2 -3
View File
@@ -528,11 +528,10 @@ TEST_F(DBIteratorStressTest, StressTest) {
internal_iter->target_hidden_fraction =
target_hidden_fraction;
internal_iter->trace = trace;
db_iter.reset(NewDBIterator(
db_iter.reset(DBIter::NewIter(
env_, ropt, ImmutableOptions(options),
MutableCFOptions(options), BytewiseComparator(),
internal_iter, nullptr /* version */, sequence,
options.max_sequential_skip_in_iterations,
internal_iter, /*version=*/nullptr, sequence,
nullptr /*read_callback*/));
}
+105 -167
View File
@@ -259,10 +259,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
internal_iter->Finish();
ReadOptions ro;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -294,10 +293,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
internal_iter->Finish();
ReadOptions ro;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -322,10 +320,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
ReadOptions ro;
ro.iterate_upper_bound = &prefix;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -356,10 +353,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
ReadOptions ro;
ro.iterate_upper_bound = &prefix;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -393,10 +389,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
ReadOptions ro;
ro.iterate_upper_bound = &prefix;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -425,10 +420,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
ReadOptions ro;
ro.iterate_upper_bound = &prefix;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 7 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
SetPerfLevel(kEnableCount);
@@ -465,10 +459,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
ReadOptions ro;
ro.iterate_upper_bound = &prefix;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 4 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -492,10 +485,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
ReadOptions ro;
ro.iterate_upper_bound = &prefix;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -517,10 +509,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
ReadOptions ro;
ro.iterate_upper_bound = &prefix;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -554,10 +545,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
ReadOptions ro;
ro.iterate_upper_bound = &prefix;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 7 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
SetPerfLevel(kEnableCount);
@@ -586,10 +576,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
internal_iter->Finish();
ReadOptions ro;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -631,10 +620,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
internal_iter->Finish();
ReadOptions ro;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 2 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -664,10 +652,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
internal_iter->Finish();
ReadOptions ro;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -696,10 +683,9 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 0 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(!db_iter->Valid());
@@ -710,10 +696,9 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 0 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
ASSERT_TRUE(!db_iter->Valid());
@@ -735,11 +720,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkipCountSkips) {
}
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
2 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
2 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "c");
@@ -782,10 +766,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
internal_iter->Finish();
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, i + 2 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -820,10 +804,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
internal_iter->AddPut("c", "200");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, i + 2 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -851,10 +835,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
internal_iter->AddPut("c", "200");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 202 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -886,10 +870,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
}
internal_iter->AddPut("c", "200");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, i /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(!db_iter->Valid());
@@ -906,10 +890,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
}
internal_iter->AddPut("c", "200");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 200 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -944,10 +927,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
}
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, i + 2 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -981,10 +964,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
}
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, i + 2 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1033,10 +1016,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
internal_iter->Finish();
ro.max_skippable_internal_keys = 0;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -1081,10 +1063,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
internal_iter->Finish();
ro.max_skippable_internal_keys = 2;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -1127,10 +1108,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
internal_iter->Finish();
ro.max_skippable_internal_keys = 2;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -1167,10 +1147,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
internal_iter->Finish();
ro.max_skippable_internal_keys = 2;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -1204,10 +1183,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
internal_iter->Finish();
ro.max_skippable_internal_keys = 2;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
@@ -1236,10 +1214,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
internal_iter->Finish();
ro.max_skippable_internal_keys = 2;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -1275,10 +1252,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
internal_iter->Finish();
ro.max_skippable_internal_keys = 2;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -1314,10 +1290,10 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
internal_iter->Finish();
ro.max_skippable_internal_keys = i;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -1369,10 +1345,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
options.max_sequential_skip_in_iterations = 1000;
ro.max_skippable_internal_keys = i;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, MutableCFOptions(options), BytewiseComparator(),
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToFirst();
@@ -1412,11 +1387,11 @@ TEST_F(DBIteratorTest, DBIteratorTimedPutBasic) {
internal_iter->AddTimedPut("d", "3", /*write_unix_time=*/0);
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
options.max_sequential_skip_in_iterations = 1;
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
7 /* sequence */, /*max_sequential_skip_in_iterations*/ 1,
nullptr /* read_callback */));
7 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToFirst();
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "a");
@@ -1463,11 +1438,10 @@ TEST_F(DBIteratorTest, DBIterator1) {
internal_iter->AddMerge("b", "2");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
1 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
1 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToFirst();
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "a");
@@ -1493,11 +1467,10 @@ TEST_F(DBIteratorTest, DBIterator2) {
internal_iter->AddMerge("b", "2");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
0 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
0 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToFirst();
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "a");
@@ -1519,11 +1492,10 @@ TEST_F(DBIteratorTest, DBIterator3) {
internal_iter->AddMerge("b", "2");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
2 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
2 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToFirst();
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "a");
@@ -1545,11 +1517,10 @@ TEST_F(DBIteratorTest, DBIterator4) {
internal_iter->AddMerge("b", "2");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
4 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
4 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToFirst();
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "a");
@@ -1580,10 +1551,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 0 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1605,10 +1575,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 1 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1630,10 +1599,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 2 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1655,10 +1623,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 3 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1680,10 +1647,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 4 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1705,10 +1671,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 5 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1730,10 +1695,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 6 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1753,10 +1717,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
internal_iter->AddMerge("a", "merge_2");
internal_iter->AddPut("b", "val_b");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 10 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->Seek("b");
ASSERT_TRUE(db_iter->Valid());
@@ -1785,10 +1748,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 0 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1810,10 +1772,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 1 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1835,10 +1796,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 2 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1860,10 +1820,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 3 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(!db_iter->Valid());
@@ -1881,10 +1840,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 4 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1906,10 +1864,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 5 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1931,10 +1888,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
internal_iter->AddMerge("a", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 6 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -1976,10 +1932,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 0 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2013,10 +1968,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 2 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2056,10 +2010,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 4 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2099,10 +2052,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 5 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2147,10 +2099,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 6 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2196,10 +2147,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 7 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2239,10 +2189,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 9 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2288,10 +2237,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 13 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2338,10 +2286,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
internal_iter->AddDeletion("c");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
internal_iter, nullptr /* version */, 14 /* sequence */,
options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2371,11 +2318,10 @@ TEST_F(DBIteratorTest, DBIterator8) {
internal_iter->AddPut("b", "0");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
10 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "b");
@@ -2403,11 +2349,10 @@ TEST_F(DBIteratorTest, DBIterator9) {
internal_iter->AddMerge("d", "merge_6");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
10 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToLast();
ASSERT_TRUE(db_iter->Valid());
@@ -2471,11 +2416,10 @@ TEST_F(DBIteratorTest, DBIterator10) {
internal_iter->AddPut("d", "4");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
10 /* sequence */, nullptr /* read_callback */));
db_iter->Seek("c");
ASSERT_TRUE(db_iter->Valid());
@@ -2512,7 +2456,7 @@ TEST_F(DBIteratorTest, SeekToLastOccurrenceSeq0) {
internal_iter->AddPut("b", "2");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, 0 /* force seek */, nullptr /* read_callback */));
@@ -2542,11 +2486,10 @@ TEST_F(DBIteratorTest, DBIterator11) {
internal_iter->AddMerge("b", "2");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
1 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
1 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToFirst();
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "a");
@@ -2571,7 +2514,7 @@ TEST_F(DBIteratorTest, DBIterator12) {
internal_iter->AddSingleDeletion("b");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, 0 /* force seek */, nullptr /* read_callback */));
@@ -2610,11 +2553,11 @@ TEST_F(DBIteratorTest, DBIterator13) {
internal_iter->AddPut(key, "8");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
options.max_sequential_skip_in_iterations = 3;
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
2 /* sequence */, 3 /* max_sequential_skip_in_iterations */,
nullptr /* read_callback */));
2 /* sequence */, nullptr /* read_callback */));
db_iter->Seek("b");
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), key);
@@ -2640,11 +2583,11 @@ TEST_F(DBIteratorTest, DBIterator14) {
internal_iter->AddPut("c", "9");
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
options.max_sequential_skip_in_iterations = 1;
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
4 /* sequence */, 1 /* max_sequential_skip_in_iterations */,
nullptr /* read_callback */));
4 /* sequence */, nullptr /* read_callback */));
db_iter->Seek("b");
ASSERT_TRUE(db_iter->Valid());
ASSERT_EQ(db_iter->key().ToString(), "b");
@@ -2680,11 +2623,11 @@ class DBIterWithMergeIterTest : public testing::Test {
InternalIterator* merge_iter =
NewMergingIterator(&icomp_, child_iters.data(), 2u);
db_iter_.reset(NewDBIterator(
options_.max_sequential_skip_in_iterations = 3;
db_iter_.reset(DBIter::NewIter(
env_, ro_, ImmutableOptions(options_), MutableCFOptions(options_),
BytewiseComparator(), merge_iter, nullptr /* version */,
8 /* read data earlier than seqId 8 */,
3 /* max iterators before reseek */, nullptr /* read_callback */));
8 /* read data earlier than seqId 8 */, nullptr /* read_callback */));
}
Env* env_;
@@ -3120,11 +3063,10 @@ TEST_F(DBIteratorTest, SeekPrefixTombstones) {
internal_iter->Finish();
ro.prefix_same_as_start = true;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
10 /* sequence */, nullptr /* read_callback */));
int skipped_keys = 0;
@@ -3157,11 +3099,10 @@ TEST_F(DBIteratorTest, SeekToFirstLowerBound) {
Slice lower_bound(lower_bound_str);
ro.iterate_lower_bound = &lower_bound;
Options options;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
10 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToFirst();
if (i == kNumKeys + 1) {
@@ -3197,11 +3138,10 @@ TEST_F(DBIteratorTest, PrevLowerBound) {
Slice lower_bound(lower_bound_str);
ro.iterate_lower_bound = &lower_bound;
Options options;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
10 /* sequence */, nullptr /* read_callback */));
db_iter->SeekToLast();
for (int i = kNumKeys; i >= kLowerBound; --i) {
@@ -3226,11 +3166,10 @@ TEST_F(DBIteratorTest, SeekLessLowerBound) {
Slice lower_bound(lower_bound_str);
ro.iterate_lower_bound = &lower_bound;
Options options;
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
10 /* sequence */, nullptr /* read_callback */));
auto before_lower_bound_str = std::to_string(kLowerBound - 1);
Slice before_lower_bound(lower_bound_str);
@@ -3252,11 +3191,10 @@ TEST_F(DBIteratorTest, ReverseToForwardWithDisappearingKeys) {
}
internal_iter->Finish();
std::unique_ptr<Iterator> db_iter(NewDBIterator(
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
env_, ReadOptions(), ImmutableOptions(options), MutableCFOptions(options),
BytewiseComparator(), internal_iter, nullptr /* version */,
10 /* sequence */, options.max_sequential_skip_in_iterations,
nullptr /* read_callback */));
10 /* sequence */, nullptr /* read_callback */));
db_iter->SeekForPrev("a");
ASSERT_TRUE(db_iter->Valid());
+160
View File
@@ -3824,6 +3824,166 @@ TEST_F(DBIteratorTest, IteratorsConsistentViewExplicitSnapshot) {
}
}
TEST_P(DBIteratorTest, MemtableOpsScanFlushTriggerWithSeek) {
// Tests that option memtable_op_scan_flush_trigger works when the limit
// is reached during a Seek() operation.
const int kTrigger = 10;
Random* r = Random::GetTLSInstance();
for (int trigger : {kTrigger, kTrigger + 1}) {
for (bool delete_only : {false, true}) {
Options options;
options.create_if_missing = true;
options.memtable_op_scan_flush_trigger = trigger;
options.level_compaction_dynamic_level_bytes = true;
DestroyAndReopen(options);
// Base data that will be covered by a consecutive sequence of tombstones.
int kNumKeys = delete_only ? kTrigger : kTrigger / 2;
for (int i = 0; i < kNumKeys; ++i) {
ASSERT_OK(Put(Key(i), r->RandomString(100)));
}
ASSERT_OK(Flush());
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
ASSERT_EQ(1, NumTableFilesAtLevel(6));
if (delete_only) {
for (int i = 0; i < kNumKeys; ++i) {
ASSERT_OK(SingleDelete(Key(i)));
}
} else {
for (int i = 0; i < kNumKeys; ++i) {
ASSERT_OK(Put(Key(i), r->RandomString(100)));
}
for (int i = 0; i < kNumKeys; ++i) {
ASSERT_OK(Delete(Key(i)));
}
}
SetPerfLevel(PerfLevel::kEnableCount);
get_perf_context()->Reset();
ReadOptions ro;
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
// Seek to the first key, this will scan through all the tombstones and
// hidden puts
iter->Seek(Key(0));
ASSERT_FALSE(
iter->Valid()); // All keys are deleted, so iterator is not valid
ASSERT_OK(iter->status());
ASSERT_EQ(get_perf_context()->next_on_memtable_count, kTrigger);
// Skipping kNumTrigger memtable entries in a single iterator operation
// should mark the memtable for flush.
//
// At the end of a write, we check and update memtable to request a flush
ASSERT_OK(Put(Key(11), "val"));
// Before a write, we schedule memtables for flush if requested.
ASSERT_OK(Put(Key(12), "val"));
ASSERT_OK(db_->WaitForCompact({}));
if (trigger <= kTrigger) {
// Check if memtable was flushed due to scan trigger
ASSERT_EQ(1, NumTableFilesAtLevel(0));
uint64_t val = 0;
ASSERT_TRUE(
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
ASSERT_EQ(0, val);
} else {
uint64_t val = 0;
ASSERT_TRUE(
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
ASSERT_EQ(kNumKeys, val);
}
}
}
}
TEST_P(DBIteratorTest, MemtableOpsScanFlushTriggerWithNext) {
// Tests that option memtable_op_scan_flush_trigger works when the limit
// is reached during a Next() operation, and not trigger a flush when
// the limit is reached across multiple Next() operations.
const int kTrigger = 10;
Random* r = Random::GetTLSInstance();
for (int trigger : {kTrigger, kTrigger + 1}) {
for (bool delete_only : {false, true}) {
Options options;
options.create_if_missing = true;
options.memtable_op_scan_flush_trigger = trigger;
options.level_compaction_dynamic_level_bytes = true;
DestroyAndReopen(options);
// Base data that will be covered by a consecutive sequence of tombstones.
int kNumKeys = delete_only ? kTrigger : kTrigger / 2;
for (int i = 0; i <= kNumKeys; ++i) {
ASSERT_OK(Put(Key(i), r->RandomString(100)));
}
ASSERT_OK(Flush());
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
ASSERT_EQ(1, NumTableFilesAtLevel(6));
ASSERT_OK(Put(Key(0), "val"));
if (delete_only) {
for (int i = 1; i <= kNumKeys; ++i) {
ASSERT_OK(SingleDelete(Key(i)));
}
} else {
for (int i = 1; i <= kNumKeys; ++i) {
ASSERT_OK(Put(Key(i), r->RandomString(100)));
}
for (int i = 1; i <= kNumKeys; ++i) {
ASSERT_OK(Delete(Key(i)));
}
}
// Total number of tombstones and hidden puts scanned across multiple
// Next() operations below will be kTrigger, and it should not trigger a
// flush when the limit is kTrigger + 1.
ASSERT_OK(Put(Key(kNumKeys + 1), "v1"));
ASSERT_OK(Delete(Key(kNumKeys + 2)));
ASSERT_OK(Put(Key(kNumKeys + 3), "v3"));
SetPerfLevel(PerfLevel::kEnableCount);
get_perf_context()->Reset();
ReadOptions ro;
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
iter->Seek(Key(0));
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->value(), "val");
ASSERT_OK(iter->status());
ASSERT_EQ(get_perf_context()->next_on_memtable_count, 0);
iter->Next();
// kTrigger tombstones and invisible puts and 1 for the visible put
ASSERT_EQ(get_perf_context()->next_on_memtable_count, kTrigger + 1);
iter->Next();
ASSERT_EQ(get_perf_context()->next_on_memtable_count, kTrigger + 3);
// Skipping kNumTrigger memtable entries in a single iterator operation
// should mark the memtable for flush.
//
// At the end of a write, we check and update memtable to request a flush
ASSERT_OK(Put(Key(11), "val"));
// Before a write, we schedule memtables for flush if requested.
ASSERT_OK(Put(Key(12), "val"));
ASSERT_OK(db_->WaitForCompact({}));
if (trigger <= kTrigger) {
// Check if memtable was flushed due to scan trigger
ASSERT_EQ(1, NumTableFilesAtLevel(0));
uint64_t val = 0;
ASSERT_TRUE(
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
ASSERT_EQ(0, val);
} else {
uint64_t val = 0;
ASSERT_TRUE(
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
ASSERT_EQ(kNumKeys + 1, val);
}
}
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+8 -8
View File
@@ -312,12 +312,12 @@ TEST_P(DbKvChecksumTest, WriteToWALCorrupted) {
// Corrupted write batch leads to read-only mode, so we have to
// reopen for every attempt.
Reopen(options);
auto log_size_pre_write = dbfull()->TEST_total_log_size();
auto log_size_pre_write = dbfull()->TEST_wals_total_size();
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_TRUE(ExecuteWrite(nullptr /* cf_handle */).IsCorruption());
// Confirm that nothing was written to WAL
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_wals_total_size());
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
SyncPoint::GetInstance()->DisableProcessing();
@@ -350,12 +350,12 @@ TEST_P(DbKvChecksumTest, WriteToWALWithColumnFamilyCorrupted) {
// Corrupted write batch leads to read-only mode, so we have to
// reopen for every attempt.
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
auto log_size_pre_write = dbfull()->TEST_total_log_size();
auto log_size_pre_write = dbfull()->TEST_wals_total_size();
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_TRUE(ExecuteWrite(nullptr /* cf_handle */).IsCorruption());
// Confirm that nothing was written to WAL
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_wals_total_size());
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
SyncPoint::GetInstance()->DisableProcessing();
@@ -487,7 +487,7 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALCorrupted) {
// Reopen DB since it failed WAL write which lead to read-only mode
Reopen(options);
SyncPoint::GetInstance()->EnableProcessing();
auto log_size_pre_write = dbfull()->TEST_total_log_size();
auto log_size_pre_write = dbfull()->TEST_wals_total_size();
leader_batch_and_status =
GetWriteBatch(GetCFHandleToUse(nullptr, op_type1_),
8 /* protection_bytes_per_key */, op_type1_);
@@ -499,7 +499,7 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALCorrupted) {
SyncPoint::GetInstance()->ClearCallBack("WriteThread::JoinBatchGroup:Wait");
ASSERT_EQ(1, leader_count);
// Nothing should have been written to WAL
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_wals_total_size());
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
corrupt_byte_offset++;
@@ -599,7 +599,7 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALWithColumnFamilyCorrupted) {
// Reopen DB since it failed WAL write which lead to read-only mode
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "ramen"}, options);
SyncPoint::GetInstance()->EnableProcessing();
auto log_size_pre_write = dbfull()->TEST_total_log_size();
auto log_size_pre_write = dbfull()->TEST_wals_total_size();
leader_batch_and_status =
GetWriteBatch(GetCFHandleToUse(handles_[1], op_type1_),
8 /* protection_bytes_per_key */, op_type1_);
@@ -612,7 +612,7 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALWithColumnFamilyCorrupted) {
ASSERT_EQ(1, leader_count);
// Nothing should have been written to WAL
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_wals_total_size());
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
corrupt_byte_offset++;
+17 -22
View File
@@ -322,31 +322,26 @@ TEST_F(DBOptionsTest, SetWithCustomMemTableFactory) {
}
Options options;
options.create_if_missing = true;
// Try with fail_if_options_file_error=false/true to update the options
for (bool on_error : {false, true}) {
options.fail_if_options_file_error = on_error;
options.env = env_;
options.disable_auto_compactions = false;
options.env = env_;
options.disable_auto_compactions = false;
options.memtable_factory.reset(new DummySkipListFactory());
Reopen(options);
options.memtable_factory.reset(new DummySkipListFactory());
Reopen(options);
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
ASSERT_OK(
dbfull()->SetOptions(cfh, {{"disable_auto_compactions", "true"}}));
ColumnFamilyDescriptor cfd;
ASSERT_OK(cfh->GetDescriptor(&cfd));
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
DummySkipListFactory::kClassName());
ColumnFamilyHandle* test = nullptr;
ASSERT_OK(dbfull()->CreateColumnFamily(options, "test", &test));
ASSERT_OK(test->GetDescriptor(&cfd));
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
DummySkipListFactory::kClassName());
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
ASSERT_OK(dbfull()->SetOptions(cfh, {{"disable_auto_compactions", "true"}}));
ColumnFamilyDescriptor cfd;
ASSERT_OK(cfh->GetDescriptor(&cfd));
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
DummySkipListFactory::kClassName());
ColumnFamilyHandle* test = nullptr;
ASSERT_OK(dbfull()->CreateColumnFamily(options, "test", &test));
ASSERT_OK(test->GetDescriptor(&cfd));
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
DummySkipListFactory::kClassName());
ASSERT_OK(dbfull()->DropColumnFamily(test));
delete test;
}
ASSERT_OK(dbfull()->DropColumnFamily(test));
delete test;
}
TEST_F(DBOptionsTest, SetBytesPerSync) {
+75
View File
@@ -508,6 +508,81 @@ TEST_F(DBSecondaryTest, OpenAsSecondary) {
verify_db_func("new_foo_value", "new_bar_value");
}
TEST_F(DBSecondaryTest, OptionsOverrideTest) {
Options options;
options.env = env_;
options.preserve_internal_time_seconds = 300;
options.compaction_readahead_size = 200;
options.blob_compaction_readahead_size = 100;
Reopen(options);
for (int i = 0; i < 3; ++i) {
ASSERT_OK(Put("foo", "foo_value" + std::to_string(i)));
ASSERT_OK(Put("bar", "bar_value" + std::to_string(i)));
ASSERT_OK(Flush());
}
CompactionServiceInput input;
ColumnFamilyMetaData meta;
db_->GetColumnFamilyMetaData(&meta);
for (auto& file : meta.levels[0].files) {
ASSERT_EQ(0, meta.levels[0].level);
input.input_files.push_back(file.name);
}
ASSERT_EQ(input.input_files.size(), 3);
input.output_level = 1;
input.options_file_number = dbfull()->GetVersionSet()->options_file_number();
input.cf_name = kDefaultColumnFamilyName;
ASSERT_OK(db_->GetDbIdentity(input.db_id));
ASSERT_EQ(db_->GetOptions().compaction_readahead_size, 200);
ASSERT_EQ(db_->GetOptions().blob_compaction_readahead_size, 100);
Close();
std::string compaction_input_binary;
ASSERT_OK(input.Write(&compaction_input_binary));
std::string compaction_result_binary;
CompactionServiceOptionsOverride override_options;
override_options.env = env_;
override_options.table_factory.reset(
NewBlockBasedTableFactory(BlockBasedTableOptions()));
ASSERT_OK(
StringToMap("compaction_readahead_size=8388608;"
"blob_compaction_readahead_size=4194304;"
"some_invalid_option=ignore_me;"
"env=this_should_not_fail;"
"max_open_files=100;", // this should be always overriden as
// -1 in remote compaction
&override_options.options_map));
bool verified = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImplSecondary::OpenAndCompact::AfterOpenAsSecondary:0",
[&](void* arg) {
auto secondary_db = static_cast<DB*>(arg);
auto secondary_db_options = secondary_db->GetOptions();
// DBOption
ASSERT_EQ(secondary_db_options.compaction_readahead_size, 8388608);
ASSERT_EQ(secondary_db_options.max_open_files, -1);
// CFOption
ASSERT_EQ(secondary_db_options.blob_compaction_readahead_size, 4194304);
verified = true;
});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(DB::OpenAndCompact(OpenAndCompactOptions(), dbname_,
secondary_path_, compaction_input_binary,
&compaction_result_binary, override_options));
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
ASSERT_TRUE(verified);
}
namespace {
class TraceFileEnv : public EnvWrapper {
public:
+7 -7
View File
@@ -3351,11 +3351,6 @@ class ModelDB : public DB {
using DB::NumberLevels;
int NumberLevels(ColumnFamilyHandle* /*column_family*/) override { return 1; }
using DB::MaxMemCompactionLevel;
int MaxMemCompactionLevel(ColumnFamilyHandle* /*column_family*/) override {
return 1;
}
using DB::Level0StopWriteTrigger;
int Level0StopWriteTrigger(ColumnFamilyHandle* /*column_family*/) override {
return -1;
@@ -3412,7 +3407,7 @@ class ModelDB : public DB {
}
Status GetCurrentWalFile(
std::unique_ptr<LogFile>* /*current_log_file*/) override {
std::unique_ptr<LogFile>* /*current_wal_file*/) override {
return Status::OK();
}
@@ -3451,6 +3446,11 @@ class ModelDB : public DB {
return Status::OK();
}
Status GetNewestUserDefinedTimestamp(
ColumnFamilyHandle* /*cf*/, std::string* /*newest_timestamp*/) override {
return Status::OK();
}
ColumnFamilyHandle* DefaultColumnFamily() const override { return nullptr; }
private:
@@ -6419,7 +6419,7 @@ TEST_F(DBTest, TestLogCleanup) {
for (int i = 0; i < 100000; ++i) {
ASSERT_OK(Put(Key(i), "val"));
// only 2 memtables will be alive, so logs_to_free needs to always be below
// only 2 memtables will be alive, so wals_to_free needs to always be below
// 2
ASSERT_LT(dbfull()->TEST_LogsToFreeSize(), static_cast<size_t>(3));
}
-1
View File
@@ -592,7 +592,6 @@ Options DBTestBase::GetOptions(
options_override.level_compaction_dynamic_level_bytes;
options.env = env_;
options.create_if_missing = true;
options.fail_if_options_file_error = true;
return options;
}
+2 -2
View File
@@ -3024,13 +3024,13 @@ TEST_F(DBWALTest, GetCompressedWalsAfterSync) {
options.wal_compression = kZSTD;
DestroyAndReopen(options);
// Write something to memtable and WAL so that log_empty_ will be false after
// Write something to memtable and WAL so that wal_empty_ will be false after
// next DB::Open().
ASSERT_OK(Put("a", "v"));
Reopen(options);
// New WAL is created, thanks to !log_empty_.
// New WAL is created, thanks to !wal_empty_.
ASSERT_OK(dbfull()->TEST_SwitchWAL());
ASSERT_OK(Put("b", "v"));
+220 -39
View File
@@ -19,6 +19,13 @@
#include "utilities/merge_operators/string_append/stringappend2.h"
namespace ROCKSDB_NAMESPACE {
namespace {
std::string EncodeAsUint64(uint64_t v) {
std::string dst;
PutFixed64(&dst, v);
return dst;
}
} // namespace
class DBBasicTestWithTimestamp : public DBBasicTestWithTimestampBase {
public:
DBBasicTestWithTimestamp()
@@ -1480,13 +1487,24 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToUserKeyBeforeSavedKey) {
Close();
}
TEST_F(DBBasicTestWithTimestamp,
FIXME_ReverseIterationWithBlobAndUnpreparedValue) {
class ReverseIterationWithUnpreparedBlobTest
: public DBBasicTestWithTimestampBase,
public testing::WithParamInterface<std::tuple<bool, uint64_t>> {
public:
ReverseIterationWithUnpreparedBlobTest()
: DBBasicTestWithTimestampBase(
"db_basic_test_with_timestamp_reverse_with_unprepare") {}
};
INSTANTIATE_TEST_CASE_P(ReverseIterationWithUnpreparedBlobTest,
ReverseIterationWithUnpreparedBlobTest,
::testing::Combine(::testing::Values(true, false),
::testing::Values(0, 2)));
TEST_P(ReverseIterationWithUnpreparedBlobTest, Basic) {
Options options = CurrentOptions();
options.create_if_missing = true;
options.env = env_;
options.enable_blob_files = true;
options.max_sequential_skip_in_iterations = 0;
options.max_sequential_skip_in_iterations = std::get<1>(GetParam());
const size_t kTimestampSize = Timestamp(0, 0).size();
TestComparator test_cmp(kTimestampSize);
@@ -1501,7 +1519,7 @@ TEST_F(DBBasicTestWithTimestamp,
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)));
Key1(key) + "value" + std::to_string(i)));
}
}
@@ -1513,17 +1531,28 @@ TEST_F(DBBasicTestWithTimestamp,
ReadOptions read_opts;
read_opts.timestamp = &read_timestamp;
read_opts.allow_unprepared_value = true;
read_opts.allow_unprepared_value = std::get<0>(GetParam());
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
it->SeekForPrev(Key1(kMaxKey));
ASSERT_TRUE(it->Valid());
ASSERT_OK(it->status());
uint64_t key = kMaxKey;
int count = 0;
while (it->Valid()) {
ASSERT_OK(it->status());
// FIXME: PrepareValue() should succeed and status() should remain OK
ASSERT_FALSE(it->PrepareValue());
ASSERT_TRUE(it->status().IsCorruption());
ASSERT_TRUE(it->PrepareValue());
ASSERT_TRUE(it->Valid());
ASSERT_OK(it->status());
ASSERT_EQ(it->key(), Key1(key));
ASSERT_EQ(it->timestamp(), Timestamp(3, 0));
ASSERT_EQ(it->value(), Key1(key) + "value" + std::to_string(1));
key--;
count++;
it->Prev();
}
ASSERT_OK(it->status());
ASSERT_EQ(kMaxKey + 1, count);
}
Close();
@@ -3746,17 +3775,42 @@ INSTANTIATE_TEST_CASE_P(
test::UserDefinedTimestampTestMode::kStripUserDefinedTimestamp,
test::UserDefinedTimestampTestMode::kNormal));
TEST_F(DBBasicTestWithTimestamp, EnableDisableUDT) {
// Test params:
// 1) whether to flush before close
class EnableDisableUDTTest : public DBBasicTestWithTimestampBase,
public testing::WithParamInterface<bool> {
public:
EnableDisableUDTTest()
: DBBasicTestWithTimestampBase("/enable_disable_udt") {}
};
INSTANTIATE_TEST_CASE_P(EnableDisableUDTTest, EnableDisableUDTTest,
::testing::Values(true, false));
TEST_P(EnableDisableUDTTest, Basic) {
Options options = CurrentOptions();
// Un-flushed data before close will involve a WAL replay on DB reopen.
bool flush_before_close = GetParam();
options.env = env_;
// Create a column family without user-defined timestamps.
options.comparator = BytewiseComparator();
options.persist_user_defined_timestamps = true;
DestroyAndReopen(options);
ReadOptions ropts;
std::string read_ts;
std::string value;
std::string key_ts;
// Create one SST file, its user keys have no user-defined timestamps.
ASSERT_OK(db_->Put(WriteOptions(), "foo", "val1"));
ASSERT_OK(Flush(0));
ASSERT_OK(db_->Put(WriteOptions(), "foo", "val0"));
ASSERT_OK(db_->Put(WriteOptions(), "bar", "val0"));
ASSERT_OK(db_->DeleteRange(WriteOptions(), "bar", "baz"));
ASSERT_OK(db_->Get(ReadOptions(), "foo", &value));
ASSERT_EQ("val0", value);
ASSERT_TRUE(db_->Get(ReadOptions(), "bar", &value).IsNotFound());
if (flush_before_close) {
ASSERT_OK(Flush(0));
}
Close();
// Reopen the existing column family and enable user-defined timestamps
@@ -3765,47 +3819,63 @@ TEST_F(DBBasicTestWithTimestamp, EnableDisableUDT) {
options.persist_user_defined_timestamps = false;
options.allow_concurrent_memtable_write = false;
Reopen(options);
std::string value;
ASSERT_TRUE(db_->Get(ReadOptions(), "foo", &value).IsInvalidArgument());
std::string read_ts;
PutFixed64(&read_ts, 0);
ReadOptions ropts;
// Read data from previous session before and after compaction.
read_ts = EncodeAsUint64(1);
Slice read_ts_slice = read_ts;
ropts.timestamp = &read_ts_slice;
std::string key_ts;
// Entries in pre-existing SST files are treated as if they have minimum
// user-defined timestamps.
ASSERT_OK(db_->Get(ropts, "foo", &value, &key_ts));
ASSERT_EQ("val1", value);
ASSERT_EQ(read_ts, key_ts);
for (int i = 0; i < 2; i++) {
ASSERT_TRUE(db_->Get(ReadOptions(), "foo", &value).IsInvalidArgument());
// Entries in pre-existing SST files are treated as if they have minimum
// user-defined timestamps.
ASSERT_OK(db_->Get(ropts, "foo", &value, &key_ts));
ASSERT_EQ("val0", value);
ASSERT_EQ(EncodeAsUint64(0), key_ts);
ASSERT_TRUE(db_->Get(ropts, "bar", &value, &key_ts).IsNotFound());
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
}
// Do timestamped read / write.
std::string write_ts;
PutFixed64(&write_ts, 1);
ASSERT_OK(db_->Put(WriteOptions(), "foo", write_ts, "val2"));
read_ts.clear();
PutFixed64(&read_ts, 1);
ASSERT_OK(db_->Put(WriteOptions(), "foo", EncodeAsUint64(1), "val1"));
ASSERT_OK(db_->Put(WriteOptions(), "bar", EncodeAsUint64(1), "val1"));
ASSERT_OK(db_->DeleteRange(WriteOptions(), "bar", "baz", EncodeAsUint64(2)));
ASSERT_OK(db_->Get(ropts, "foo", &value, &key_ts));
ASSERT_EQ("val2", value);
ASSERT_EQ(write_ts, key_ts);
ASSERT_EQ("val1", value);
ASSERT_EQ(EncodeAsUint64(1), key_ts);
ASSERT_OK(db_->Get(ropts, "bar", &value, &key_ts));
ASSERT_EQ("val1", value);
ASSERT_EQ(EncodeAsUint64(1), key_ts);
read_ts = EncodeAsUint64(2);
ASSERT_TRUE(db_->Get(ropts, "bar", &value, &key_ts).IsNotFound());
// The user keys in this SST file don't have user-defined timestamps either,
// because `persist_user_defined_timestamps` flag is set to false.
ASSERT_OK(Flush(0));
if (flush_before_close) {
ASSERT_OK(Flush(0));
}
Close();
// Reopen the existing column family while disabling user-defined timestamps.
options.comparator = BytewiseComparator();
Reopen(options);
ASSERT_TRUE(db_->Get(ropts, "foo", &value).IsInvalidArgument());
ASSERT_OK(db_->Get(ReadOptions(), "foo", &value));
ASSERT_EQ("val2", value);
// Read data from previous session before and after compaction.
for (int i = 0; i < 2; i++) {
ASSERT_TRUE(db_->Get(ropts, "foo", &value).IsInvalidArgument());
ASSERT_OK(db_->Get(ReadOptions(), "foo", &value));
ASSERT_EQ("val1", value);
ASSERT_TRUE(db_->Get(ReadOptions(), "bar", &value).IsNotFound());
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
}
// Continue to write / read the column family without user-defined timestamps.
ASSERT_OK(db_->Put(WriteOptions(), "foo", "val3"));
ASSERT_OK(db_->Put(WriteOptions(), "foo", "val2"));
ASSERT_OK(db_->Put(WriteOptions(), "bar", "val2"));
ASSERT_OK(db_->DeleteRange(WriteOptions(), "bar", "baz"));
ASSERT_OK(db_->Get(ReadOptions(), "foo", &value));
ASSERT_EQ("val3", value);
ASSERT_EQ("val2", value);
ASSERT_TRUE(db_->Get(ReadOptions(), "bar", &value).IsNotFound());
if (flush_before_close) {
ASSERT_OK(Flush(0));
}
Close();
}
@@ -4844,6 +4914,117 @@ TEST_F(DBBasicTestWithTimestamp, TimestampFilterTableReadOnGet) {
Close();
}
class GetNewestUserDefinedTimestampTest : public DBBasicTestWithTimestampBase {
public:
explicit GetNewestUserDefinedTimestampTest()
: DBBasicTestWithTimestampBase("get_newest_udt_test") {}
};
TEST_F(GetNewestUserDefinedTimestampTest, Basic) {
std::string newest_timestamp;
// UDT disabled, get InvalidArgument.
ASSERT_TRUE(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp)
.IsInvalidArgument());
Options options = CurrentOptions();
options.env = env_;
options.create_if_missing = true;
options.max_write_buffer_number = 5;
options.min_write_buffer_number_to_merge = 4;
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
DestroyAndReopen(options);
// UDT persisted, get NotSupported.
ASSERT_TRUE(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp)
.IsNotSupported());
options.persist_user_defined_timestamps = false;
options.allow_concurrent_memtable_write = false;
DestroyAndReopen(options);
ASSERT_TRUE(
db_->GetNewestUserDefinedTimestamp(nullptr, nullptr).IsInvalidArgument());
ColumnFamilyHandleImpl* cfh = static_cast_with_check<ColumnFamilyHandleImpl>(
db_->DefaultColumnFamily());
ColumnFamilyData* cfd = cfh->cfd();
// The column family hasn't seen any user defined timestamp
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
ASSERT_TRUE(newest_timestamp.empty());
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(1), "val1"));
// Testing get newest timestamp from mutable memtable.
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
ASSERT_EQ(EncodeAsUint64(1), newest_timestamp);
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(2), "val2"));
ASSERT_OK(dbfull()->TEST_SwitchMemtable(cfd));
// Testing get the newest timestamp from immutable memtable because the
// mutable one is empty.
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
ASSERT_EQ(EncodeAsUint64(2), newest_timestamp);
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(3), "val3"));
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(4), "val4"));
ASSERT_OK(dbfull()->TEST_SwitchMemtable(cfd));
// Testing get the newest timestamp from the more recent immutable memtable
// when there are multiple immutable memtables.
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
ASSERT_EQ(EncodeAsUint64(4), newest_timestamp);
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(5), "val5"));
// Testing get newest timestamp from mutable memtable when it has data, in the
// presence of immutable memtables.
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
ASSERT_EQ(EncodeAsUint64(5), newest_timestamp);
ASSERT_OK(Flush());
// After flushing and all the user defined timestamp are flushed. User defined
// timestamp info for SST files is available from MANIFEST.
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
ASSERT_EQ(EncodeAsUint64(5), newest_timestamp);
Reopen(options);
// Similar after flush, when there is no memtables, but some SST files,
// if MANIFEST records the upperbound of flushed timestamps because timestamps
// are not persisted in SST files, this info can be found.
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
ASSERT_EQ(EncodeAsUint64(5), newest_timestamp);
Close();
}
TEST_F(GetNewestUserDefinedTimestampTest, ConcurrentWrites) {
Options options = CurrentOptions();
options.create_if_missing = true;
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
options.persist_user_defined_timestamps = false;
options.allow_concurrent_memtable_write = false;
DestroyAndReopen(options);
std::vector<std::thread> threads;
threads.reserve(10);
std::atomic<uint64_t> current_ts{0};
for (int i = 0; i < 10; i++) {
threads.emplace_back([this, i, &current_ts]() {
if (i % 2 == 0) {
std::string newest_timestamp;
ASSERT_OK(
db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
} else {
uint64_t write_ts = current_ts.fetch_add(1);
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(write_ts),
"val" + std::to_string(i)));
}
});
}
for (auto& t : threads) {
t.join();
}
Close();
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+75 -1
View File
@@ -987,7 +987,7 @@ TEST_P(DBWriteTest, RecycleLogToggleTest) {
options.recycle_log_file_num = 1;
Reopen(options);
// 1.log is added to alive_log_files_
// 1.log is added to alive_wal_files_
ASSERT_OK(Put(Key(2), "val1"));
ASSERT_OK(Flush());
// 1.log should be deleted and not recycled, since it
@@ -1000,6 +1000,80 @@ TEST_P(DBWriteTest, RecycleLogToggleTest) {
ASSERT_EQ(Get(Key(1)), "val2");
}
TEST_P(DBWriteTest, IngestWriteBatchWithIndex) {
if (GetParam() == kPipelinedWrite) {
return;
}
Options options = GetOptions();
options.disable_auto_compactions = true;
Reopen(options);
Options cf_options = GetOptions();
cf_options.merge_operator = MergeOperators::CreateStringAppendOperator();
CreateColumnFamilies({"cf1", "cf2"}, cf_options);
ReopenWithColumnFamilies({"default", "cf1", "cf2"},
{options, cf_options, cf_options});
// default cf
auto wbwi1 = std::make_shared<WriteBatchWithIndex>(options.comparator, 0,
/*overwrite_key=*/true);
ASSERT_OK(wbwi1->Put("key1", "value1"));
ASSERT_OK(wbwi1->Put("key2", "value2"));
if (GetParam() == kPipelinedWrite) {
ASSERT_TRUE(db_->IngestWriteBatchWithIndex({}, wbwi1).IsNotSupported());
return;
}
// Test disableWAL=false
ASSERT_TRUE(db_->IngestWriteBatchWithIndex({}, wbwi1).IsNotSupported());
WriteOptions wo;
wo.disableWAL = true;
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, wbwi1));
ASSERT_EQ("value1", Get("key1"));
ASSERT_EQ("value2", Get("key2"));
// Test with overwrites
auto wbwi = std::make_shared<WriteBatchWithIndex>(options.comparator, 0,
/*overwrite_key=*/true);
ASSERT_OK(wbwi->Put("key2", "value3"));
ASSERT_OK(wbwi->Delete("key1")); // Delete an existing key
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, wbwi));
ASSERT_EQ("NOT_FOUND", Get("key1"));
ASSERT_EQ("value3", Get("key2"));
auto wbwi2 = std::make_shared<WriteBatchWithIndex>(options.comparator, 0,
/*overwrite_key=*/true);
ASSERT_OK(wbwi2->Put(handles_[1], "cf1_key1", "cf1_value1"));
ASSERT_OK(wbwi2->Delete(handles_[1], "cf1_key2"));
// Test ingestion with column family
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, wbwi2));
ASSERT_EQ("cf1_value1", Get(1, "cf1_key1"));
ASSERT_EQ("NOT_FOUND", Get(1, "cf1_key2"));
auto wbwi3 = std::make_shared<WriteBatchWithIndex>(options.comparator, 0,
/*overwrite_key=*/true);
ASSERT_OK(wbwi3->Merge(handles_[2], "cf2_key1", "cf2_value1"));
ASSERT_OK(wbwi3->Merge(handles_[2], "cf2_key1", "cf2_value2"));
// Test ingestion with merge operations
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, wbwi3));
ASSERT_EQ("cf2_value1,cf2_value2", Get(2, "cf2_key1"));
// Test with overwrite_key = false
auto wbwi_no_overwrite = std::make_shared<WriteBatchWithIndex>(
options.comparator, 0, /*overwrite_key=*/false);
ASSERT_OK(wbwi_no_overwrite->Put("key1", "value1"));
Status s = db_->IngestWriteBatchWithIndex(wo, wbwi_no_overwrite);
ASSERT_TRUE(s.IsNotSupported());
auto empty_wbwi = std::make_shared<WriteBatchWithIndex>(
options.comparator, 0, /*overwrite_key=*/true);
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, empty_wbwi));
DestroyAndReopen(options);
// Should fail when trying to ingest to non-existent column family
ASSERT_NOK(db_->IngestWriteBatchWithIndex(wo, wbwi2));
}
INSTANTIATE_TEST_CASE_P(DBWriteTestInstance, DBWriteTest,
testing::Values(DBTestBase::kDefault,
DBTestBase::kConcurrentWALWrites,
+10 -4
View File
@@ -275,9 +275,6 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
return;
}
ROCKS_LOG_INFO(db_options_.info_log,
"ErrorHandler: Set regular background error\n");
bool paranoid = db_options_.paranoid_checks;
Status::Severity sev = Status::Severity::kFatalError;
Status new_bg_err;
@@ -335,12 +332,21 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
if (!s.ok() && (s.severity() > bg_error_.severity())) {
bg_error_ = s;
} else {
ROCKS_LOG_INFO(db_options_.info_log,
"ErrorHandler: Hit less severe background error\n");
// This error is less severe than previously encountered error. Don't
// take any further action
return;
}
}
bool stop = bg_error_.severity() >= Status::Severity::kHardError;
ROCKS_LOG_INFO(
db_options_.info_log,
"ErrorHandler: Set regular background error, auto_recovery=%d, stop=%d\n",
int{auto_recovery}, int{stop});
recover_context_ = context;
if (auto_recovery) {
recovery_in_prog_ = true;
@@ -351,7 +357,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
RecoverFromNoSpace();
}
}
if (bg_error_.severity() >= Status::Severity::kHardError) {
if (stop) {
is_db_stopped_.store(true, std::memory_order_release);
}
}
+75
View File
@@ -2693,6 +2693,81 @@ TEST_F(ExternalSSTFileBasicTest, IngestWithTemperature) {
}
}
// This tests an internal user's exact usage and expectation of the
// IngestExternalFiles APIs to bulk load and replace files.
TEST_F(ExternalSSTFileBasicTest,
AtomicReplaceColumnFamilyWithIngestedVersionKey) {
Options options = GetDefaultOptions();
options.create_if_missing = true;
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
options.num_levels = 7;
options.disallow_memtable_writes = false;
DestroyAndReopen(options);
SstFileWriter sst_file_writer(EnvOptions(), options);
std::string data_file_original = sst_files_dir_ + "data_original";
ASSERT_OK(sst_file_writer.Open(data_file_original));
ASSERT_OK(sst_file_writer.Put("ukey1", "uval1_orig"));
ASSERT_OK(sst_file_writer.Put("ukey2", "uval2_orig"));
ASSERT_OK(sst_file_writer.Finish());
ASSERT_OK(db_->IngestExternalFile(db_->DefaultColumnFamily(),
{data_file_original},
IngestExternalFileOptions()));
ASSERT_OK(Put("data_version", "v_original"));
ASSERT_OK(Flush());
std::string value;
ASSERT_OK(db_->Get(ReadOptions(), "data_version", &value));
ASSERT_EQ(value, "v_original");
ASSERT_OK(db_->Get(ReadOptions(), "ukey1", &value));
ASSERT_EQ(value, "uval1_orig");
ASSERT_OK(db_->Get(ReadOptions(), "ukey2", &value));
ASSERT_EQ(value, "uval2_orig");
// Set up a 1) data version key file on L0, and 2) a user data file on L6
// to test the initial transitioning to use `atomic_replace_range`.
ASSERT_EQ("1,0,0,0,0,0,1", FilesPerLevel());
// Test multiple cycles of replacing by atomically ingest a data file and a
// version key file while replace the whole range in the column family.
for (int i = 0; i < 10; i++) {
std::string version_file_path =
sst_files_dir_ + "version" + std::to_string(i);
ASSERT_OK(sst_file_writer.Open(version_file_path));
ASSERT_OK(sst_file_writer.Put("data_version", "v" + std::to_string(i)));
ASSERT_OK(sst_file_writer.Finish());
std::string file_path = sst_files_dir_ + std::to_string(i);
ASSERT_OK(sst_file_writer.Open(file_path));
ASSERT_OK(sst_file_writer.Put("ukey1", "uval1" + std::to_string(i)));
ASSERT_OK(sst_file_writer.Put("ukey2", "uval2" + std::to_string(i)));
ASSERT_OK(sst_file_writer.Finish());
IngestExternalFileArg arg;
arg.column_family = db_->DefaultColumnFamily();
arg.external_files = {version_file_path, file_path};
arg.atomic_replace_range = {{nullptr, nullptr}};
// Test both fail_if_not_bottomost_level: true and false
arg.options.fail_if_not_bottommost_level = i % 2 == 0;
arg.options.snapshot_consistency = false;
// Ingest 1) a new data version file and 2) a new user data file while erase
// the whole column family
Status s = db_->IngestExternalFiles({arg});
ASSERT_OK(s);
// Check ingestion result and the expected LSM shape:
// Two files on L6, 1) a data version file 2) a user data file.
ASSERT_OK(db_->Get(ReadOptions(), "ukey1", &value));
ASSERT_EQ(value, "uval1" + std::to_string(i));
ASSERT_OK(db_->Get(ReadOptions(), "ukey2", &value));
ASSERT_EQ(value, "uval2" + std::to_string(i));
ASSERT_OK(db_->Get(ReadOptions(), "data_version", &value));
ASSERT_EQ(value, "v" + std::to_string(i));
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
}
Close();
}
TEST_F(ExternalSSTFileBasicTest, FailIfNotBottommostLevelAndDisallowMemtable) {
for (bool disallow_memtable : {false, true}) {
Options options = GetDefaultOptions();
+2 -10
View File
@@ -610,16 +610,8 @@ Status ExternalSstFileIngestionJob::AssignLevelsForOneBatch(
current_time = oldest_ancester_time =
static_cast<uint64_t>(temp_current_time);
}
uint64_t tail_size = 0;
bool contain_no_data_blocks = file->table_properties.num_entries > 0 &&
(file->table_properties.num_entries ==
file->table_properties.num_range_deletions);
if (file->table_properties.tail_start_offset > 0 ||
contain_no_data_blocks) {
uint64_t file_size = file->fd.GetFileSize();
assert(file->table_properties.tail_start_offset <= file_size);
tail_size = file_size - file->table_properties.tail_start_offset;
}
uint64_t tail_size = FileMetaData::CalculateTailSize(
file->fd.GetFileSize(), file->table_properties);
bool marked_for_compaction =
file->table_properties.num_range_deletions == 1 &&
+2 -2
View File
@@ -901,9 +901,9 @@ Status FlushJob::WriteLevel0Table() {
for (ReadOnlyMemTable* m : mems_) {
ROCKS_LOG_INFO(db_options_.info_log,
"[%s] [JOB %d] Flushing memtable id %" PRIu64
" with next log file: %" PRIu64 "\n",
" with next log file: %" PRIu64 ", marked_for_flush: %d\n",
cfd_->GetName().c_str(), job_context_->job_id, m->GetID(),
m->GetNextLogNumber());
m->GetNextLogNumber(), m->IsMarkedForFlush());
if (logical_strip_timestamp) {
memtables.push_back(m->NewTimestampStrippingIterator(
ro, /*seqno_to_time_mapping=*/nullptr, &arena,
+2 -9
View File
@@ -201,15 +201,8 @@ Status ImportColumnFamilyJob::Run() {
const auto& f = files_to_import_[i][j];
const auto& file_metadata = *metadatas_[i][j];
uint64_t tail_size = 0;
bool contain_no_data_blocks = f.table_properties.num_entries > 0 &&
(f.table_properties.num_entries ==
f.table_properties.num_range_deletions);
if (f.table_properties.tail_start_offset > 0 || contain_no_data_blocks) {
uint64_t file_size = f.fd.GetFileSize();
assert(f.table_properties.tail_start_offset <= file_size);
tail_size = file_size - f.table_properties.tail_start_offset;
}
uint64_t tail_size = FileMetaData::CalculateTailSize(f.fd.GetFileSize(),
f.table_properties);
VersionEdit dummy_version_edit;
dummy_version_edit.AddFile(
+7 -7
View File
@@ -123,7 +123,7 @@ struct JobContext {
break;
}
}
return memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
return memtables_to_free.size() > 0 || wals_to_free.size() > 0 ||
job_snapshot != nullptr || sv_have_sth;
}
@@ -193,7 +193,7 @@ struct JobContext {
// contexts for installing superversions for multiple column families
std::vector<SuperVersionContext> superversion_contexts;
autovector<log::Writer*> logs_to_free;
autovector<log::Writer*> wals_to_free;
// the current manifest_file_number, log_number and prev_log_number
// that corresponds to the set of files in 'live'.
@@ -207,8 +207,8 @@ struct JobContext {
uint64_t prev_log_number;
uint64_t min_pending_output = 0;
uint64_t prev_total_log_size = 0;
size_t num_alive_log_files = 0;
uint64_t prev_wals_total_size = 0;
size_t num_alive_wal_files = 0;
uint64_t size_log_to_delete = 0;
// Snapshot taken before flush/compaction job.
@@ -237,18 +237,18 @@ struct JobContext {
for (auto m : memtables_to_free) {
delete m;
}
for (auto l : logs_to_free) {
for (auto l : wals_to_free) {
delete l;
}
memtables_to_free.clear();
logs_to_free.clear();
wals_to_free.clear();
job_snapshot.reset();
}
~JobContext() {
assert(memtables_to_free.size() == 0);
assert(logs_to_free.size() == 0);
assert(wals_to_free.size() == 0);
}
};
+7 -5
View File
@@ -147,7 +147,6 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
const Comparator* ucmp = cmp.user_comparator();
assert(ucmp);
ts_sz_ = ucmp->timestamp_size();
persist_user_defined_timestamps_ = ioptions.persist_user_defined_timestamps;
}
MemTable::~MemTable() {
@@ -175,6 +174,11 @@ size_t MemTable::ApproximateMemoryUsage() {
}
bool MemTable::ShouldFlushNow() {
if (IsMarkedForFlush()) {
// TODO: dedicated flush reason when marked for flush
return true;
}
// This is set if memtable_max_range_deletions is > 0,
// and that many range deletions are done
if (memtable_max_range_deletions_ > 0 &&
@@ -1801,7 +1805,7 @@ uint64_t MemTable::GetMinLogContainingPrepSection() {
}
void MemTable::MaybeUpdateNewestUDT(const Slice& user_key) {
if (ts_sz_ == 0 || persist_user_defined_timestamps_) {
if (ts_sz_ == 0) {
return;
}
const Comparator* ucmp = GetInternalKeyComparator().user_comparator();
@@ -1812,9 +1816,7 @@ void MemTable::MaybeUpdateNewestUDT(const Slice& user_key) {
}
const Slice& MemTable::GetNewestUDT() const {
// This path should not be invoked for MemTables that does not enable the UDT
// in Memtable only feature.
assert(ts_sz_ > 0 && !persist_user_defined_timestamps_);
assert(ts_sz_ > 0);
return newest_udt_;
}
+13 -8
View File
@@ -30,6 +30,7 @@
#include "rocksdb/db.h"
#include "rocksdb/memtablerep.h"
#include "table/multiget_context.h"
#include "util/atomic.h"
#include "util/cast_util.h"
#include "util/dynamic_bloom.h"
#include "util/hash.h"
@@ -354,13 +355,13 @@ class ReadOnlyMemTable {
// be flushed to storage
// REQUIRES: external synchronization to prevent simultaneous
// operations on the same MemTable.
uint64_t GetNextLogNumber() const { return mem_next_logfile_number_; }
uint64_t GetNextLogNumber() const { return mem_next_walfile_number_; }
// Sets the next active logfile number when this memtable is about to
// be flushed to storage
// REQUIRES: external synchronization to prevent simultaneous
// operations on the same MemTable.
void SetNextLogNumber(uint64_t num) { mem_next_logfile_number_ = num; }
void SetNextLogNumber(uint64_t num) { mem_next_walfile_number_ = num; }
// REQUIRES: db_mutex held.
void SetID(uint64_t id) { id_ = id; }
@@ -496,6 +497,10 @@ class ReadOnlyMemTable {
return false;
}
void MarkForFlush() { marked_for_flush_.StoreRelaxed(true); }
bool IsMarkedForFlush() const { return marked_for_flush_.LoadRelaxed(); }
protected:
friend class MemTableList;
@@ -511,7 +516,7 @@ class ReadOnlyMemTable {
VersionEdit edit_;
// The log files earlier than this number can be deleted.
uint64_t mem_next_logfile_number_{0};
uint64_t mem_next_walfile_number_{0};
// Memtable id to track flush.
uint64_t id_ = 0;
@@ -524,6 +529,8 @@ class ReadOnlyMemTable {
// Flush job info of the current memtable.
std::unique_ptr<FlushJobInfo> flush_job_info_;
RelaxedAtomic<bool> marked_for_flush_{false};
};
class MemTable final : public ReadOnlyMemTable {
@@ -818,6 +825,8 @@ class MemTable final : public ReadOnlyMemTable {
is_range_del_table_empty_;
}
// Gets the newest user defined timestamps in the memtable. This should only
// be called when user defined timestamp is enabled.
const Slice& GetNewestUDT() const override;
// Returns Corruption status if verification fails.
@@ -893,14 +902,10 @@ class MemTable final : public ReadOnlyMemTable {
// Size in bytes for the user-defined timestamps.
size_t ts_sz_;
// Whether to persist user-defined timestamps
bool persist_user_defined_timestamps_;
// Newest user-defined timestamp contained in this MemTable. For ts1, and ts2
// if Comparator::CompareTimestamp(ts1, ts2) > 0, ts1 is considered newer than
// ts2. We track this field for a MemTable if its column family has UDT
// feature enabled and the `persist_user_defined_timestamp` flag is false.
// Otherwise, this field just contains an empty Slice.
// feature enabled.
Slice newest_udt_;
// Updates flush_state_ using ShouldFlushNow()
+16 -11
View File
@@ -51,9 +51,7 @@ void MemTableListVersion::UnrefMemTable(
MemTableListVersion::MemTableListVersion(
size_t* parent_memtable_list_memory_usage, const MemTableListVersion& old)
: max_write_buffer_number_to_maintain_(
old.max_write_buffer_number_to_maintain_),
max_write_buffer_size_to_maintain_(
: max_write_buffer_size_to_maintain_(
old.max_write_buffer_size_to_maintain_),
parent_memtable_list_memory_usage_(parent_memtable_list_memory_usage) {
memlist_ = old.memlist_;
@@ -69,10 +67,8 @@ MemTableListVersion::MemTableListVersion(
MemTableListVersion::MemTableListVersion(
size_t* parent_memtable_list_memory_usage,
int max_write_buffer_number_to_maintain,
int64_t max_write_buffer_size_to_maintain)
: max_write_buffer_number_to_maintain_(max_write_buffer_number_to_maintain),
max_write_buffer_size_to_maintain_(max_write_buffer_size_to_maintain),
: max_write_buffer_size_to_maintain_(max_write_buffer_size_to_maintain),
parent_memtable_list_memory_usage_(parent_memtable_list_memory_usage) {}
void MemTableListVersion::Ref() { ++refs_; }
@@ -323,8 +319,7 @@ void MemTableListVersion::Remove(ReadOnlyMemTable* m,
memlist_.remove(m);
m->MarkFlushed();
if (max_write_buffer_size_to_maintain_ > 0 ||
max_write_buffer_number_to_maintain_ > 0) {
if (max_write_buffer_size_to_maintain_ > 0) {
memlist_history_.push_front(m);
// Unable to get size of mutable memtable at this point, pass 0 to
// TrimHistory as a best effort.
@@ -356,9 +351,6 @@ bool MemTableListVersion::MemtableLimitExceeded(size_t usage) {
// whether to trim history
return MemoryAllocatedBytesExcludingLast() + usage >=
static_cast<size_t>(max_write_buffer_size_to_maintain_);
} else if (max_write_buffer_number_to_maintain_ > 0) {
return memlist_.size() + memlist_history_.size() >
static_cast<size_t>(max_write_buffer_number_to_maintain_);
} else {
return false;
}
@@ -382,6 +374,19 @@ bool MemTableListVersion::TrimHistory(autovector<ReadOnlyMemTable*>* to_delete,
return ret;
}
const Slice& MemTableListVersion::GetNewestUDT() const {
static Slice kEmptySlice;
for (auto it = memlist_.begin(); it != memlist_.end(); ++it) {
ReadOnlyMemTable* m = *it;
Slice timestamp = m->GetNewestUDT();
assert(!timestamp.empty() || m->IsEmpty());
if (!timestamp.empty()) {
return m->GetNewestUDT();
}
}
return kEmptySlice;
}
// Returns true if there is at least one memtable on which flush has
// not yet started.
bool MemTableList::IsFlushPending() const {
+6 -5
View File
@@ -45,7 +45,6 @@ class MemTableListVersion {
explicit MemTableListVersion(size_t* parent_memtable_list_memory_usage,
const MemTableListVersion& old);
explicit MemTableListVersion(size_t* parent_memtable_list_memory_usage,
int max_write_buffer_number_to_maintain,
int64_t max_write_buffer_size_to_maintain);
void Ref();
@@ -150,6 +149,12 @@ class MemTableListVersion {
int NumFlushed() const { return static_cast<int>(memlist_history_.size()); }
// Gets the newest user defined timestamps from the immutable memtables.
// This returns the newest user defined timestamp found in the most recent
// immutable memtable. This should only be called when user defined timestamp
// is enabled.
const Slice& GetNewestUDT() const;
private:
friend class MemTableList;
@@ -209,8 +214,6 @@ class MemTableListVersion {
// (used during Transaction validation)
std::list<ReadOnlyMemTable*> memlist_history_;
// Maximum number of MemTables to keep in memory (including both flushed
const int max_write_buffer_number_to_maintain_;
// Maximum size of MemTables to keep in memory (including both flushed
// and not-yet-flushed tables).
const int64_t max_write_buffer_size_to_maintain_;
@@ -238,13 +241,11 @@ class MemTableList {
public:
// A list of memtables.
explicit MemTableList(int min_write_buffer_number_to_merge,
int max_write_buffer_number_to_maintain,
int64_t max_write_buffer_size_to_maintain)
: imm_flush_needed(false),
imm_trim_needed(false),
min_write_buffer_number_to_merge_(min_write_buffer_number_to_merge),
current_(new MemTableListVersion(&current_memory_usage_,
max_write_buffer_number_to_maintain,
max_write_buffer_size_to_maintain)),
num_flush_not_started_(0),
commit_in_progress_(false),
+1 -11
View File
@@ -220,7 +220,7 @@ class MemTableListTest : public testing::Test {
TEST_F(MemTableListTest, Empty) {
// Create an empty MemTableList and validate basic functions.
MemTableList list(1, 0, 0);
MemTableList list(1, 0);
ASSERT_EQ(0, list.NumNotFlushed());
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
@@ -239,10 +239,8 @@ TEST_F(MemTableListTest, Empty) {
TEST_F(MemTableListTest, GetTest) {
// Create MemTableList
int min_write_buffer_number_to_merge = 2;
int max_write_buffer_number_to_maintain = 0;
int64_t max_write_buffer_size_to_maintain = 0;
MemTableList list(min_write_buffer_number_to_merge,
max_write_buffer_number_to_maintain,
max_write_buffer_size_to_maintain);
SequenceNumber seq = 1;
@@ -407,10 +405,8 @@ TEST_F(MemTableListTest, GetTest) {
TEST_F(MemTableListTest, GetFromHistoryTest) {
// Create MemTableList
int min_write_buffer_number_to_merge = 2;
int max_write_buffer_number_to_maintain = 2;
int64_t max_write_buffer_size_to_maintain = 2 * Arena::kInlineSize;
MemTableList list(min_write_buffer_number_to_merge,
max_write_buffer_number_to_maintain,
max_write_buffer_size_to_maintain);
SequenceNumber seq = 1;
@@ -653,11 +649,9 @@ TEST_F(MemTableListTest, FlushPendingTest) {
// Create MemTableList
int min_write_buffer_number_to_merge = 3;
int max_write_buffer_number_to_maintain = 7;
int64_t max_write_buffer_size_to_maintain =
7 * static_cast<int>(options.write_buffer_size);
MemTableList list(min_write_buffer_number_to_merge,
max_write_buffer_number_to_maintain,
max_write_buffer_size_to_maintain);
// Create some MemTables
@@ -949,13 +943,11 @@ TEST_F(MemTableListTest, AtomicFlushTest) {
// Create MemTableLists
int min_write_buffer_number_to_merge = 3;
int max_write_buffer_number_to_maintain = 7;
int64_t max_write_buffer_size_to_maintain =
7 * static_cast<int64_t>(options.write_buffer_size);
autovector<MemTableList*> lists;
for (int i = 0; i != num_cfs; ++i) {
lists.emplace_back(new MemTableList(min_write_buffer_number_to_merge,
max_write_buffer_number_to_maintain,
max_write_buffer_size_to_maintain));
}
@@ -1104,11 +1096,9 @@ TEST_F(MemTableListWithTimestampTest, GetTableNewestUDT) {
// Create MemTableList
int min_write_buffer_number_to_merge = 1;
int max_write_buffer_number_to_maintain = 4;
int64_t max_write_buffer_size_to_maintain =
4 * static_cast<int>(options.write_buffer_size);
MemTableList list(min_write_buffer_number_to_merge,
max_write_buffer_number_to_maintain,
max_write_buffer_size_to_maintain);
// Create some MemTables
+10 -13
View File
@@ -1339,11 +1339,7 @@ TEST_P(PlainTableDBTest, AdaptiveTable) {
INSTANTIATE_TEST_CASE_P(PlainTableDBTest, PlainTableDBTest, ::testing::Bool());
TEST_P(PlainTableDBTest, DeleteRangeNotSupported) {
// XXX: After attempting DeleteRange with PlainTable, Writes will permanently
// fail. Even if re-opening the DB, if WAL is used, the WAL is not recoverable
// (without manual intervention). Furthermore, a partial write batch can
// be exposed to readers, breaking WriteBatch atomicity.
for (bool use_write_batch : {/*false, */ true}) {
for (bool use_write_batch : {false, true}) {
DestroyAndReopen();
ASSERT_OK(Put("a0001111", "1"));
@@ -1362,12 +1358,7 @@ TEST_P(PlainTableDBTest, DeleteRangeNotSupported) {
ASSERT_EQ(Get("a0001111"), "1");
ASSERT_EQ(Get("b0001111"), "2");
ASSERT_EQ(Get("c0001111"), "3");
if (use_write_batch) {
// XXX: broken WriteBatch atomicity
ASSERT_EQ(Get("d0001111"), "4");
} else {
ASSERT_EQ(Get("d0001111"), "NOT_FOUND");
}
ASSERT_EQ(Get("d0001111"), "NOT_FOUND"); // expect WriteBatch atomicity
ASSERT_EQ(Get("e0001111"), "NOT_FOUND");
ASSERT_EQ(Put("e0001111", "5").code(), Status::Code::kNotSupported);
@@ -1377,8 +1368,14 @@ TEST_P(PlainTableDBTest, DeleteRangeNotSupported) {
ASSERT_EQ(dbfull()->TEST_FlushMemTable().code(),
Status::Code::kNotSupported);
// XXX: WAL is not recoverable
ASSERT_EQ(TryReopen().code(), Status::Code::kNotSupported);
// WAL is recoverable (at least in standard configurations)
ASSERT_OK(TryReopen());
ASSERT_EQ(Get("a0001111"), "1");
ASSERT_EQ(Get("b0001111"), "2");
ASSERT_EQ(Get("c0001111"), "3");
ASSERT_EQ(Get("d0001111"), "NOT_FOUND");
ASSERT_EQ(Get("e0001111"), "NOT_FOUND");
}
}
+1 -8
View File
@@ -578,14 +578,7 @@ class Repairer {
static_cast<bool>(props->user_defined_timestamps_persisted);
}
if (status.ok()) {
uint64_t tail_size = 0;
bool contain_no_data_blocks =
props->num_entries > 0 &&
(props->num_entries == props->num_range_deletions);
if (props->tail_start_offset > 0 || contain_no_data_blocks) {
assert(props->tail_start_offset <= file_size);
tail_size = file_size - props->tail_start_offset;
}
uint64_t tail_size = FileMetaData::CalculateTailSize(file_size, *props);
t->meta.tail_size = tail_size;
}
ColumnFamilyData* cfd = nullptr;
+27
View File
@@ -380,6 +380,33 @@ struct FileMetaData {
assert(!res || fd.smallest_seqno == fd.largest_seqno);
return res;
}
static uint64_t CalculateTailSize(uint64_t file_size,
const TableProperties& props) {
#ifndef NDEBUG
bool skip = false;
TEST_SYNC_POINT_CALLBACK("FileMetaData::CalculateTailSize", &skip);
if (skip) {
return 0;
}
#endif // NDEBUG
uint64_t tail_size = 0;
// Differentiate between a file with no data blocks (tail_start_offset = 0)
// and a file with unknown tail_start_offset (also set to 0 due to
// non-negative integer storage limitation)
bool contain_no_data_blocks =
props.num_entries == 0 ||
(props.num_entries > 0 &&
(props.num_entries == props.num_range_deletions));
if (props.tail_start_offset > 0 || contain_no_data_blocks) {
assert(props.tail_start_offset <= file_size);
tail_size = file_size - props.tail_start_offset;
}
return tail_size;
}
};
// A compressed copy of file meta data that just contain minimum data needed
+15 -1
View File
@@ -1002,7 +1002,8 @@ class LevelIterator final : public InternalIterator {
skip_filters_(skip_filters),
allow_unprepared_value_(allow_unprepared_value),
is_next_read_sequential_(false),
to_return_sentinel_(false) {
to_return_sentinel_(false),
scan_opts_(nullptr) {
// Empty level is not supported.
assert(flevel_ != nullptr && flevel_->num_files > 0);
if (range_tombstone_iter_ptr_) {
@@ -1098,6 +1099,13 @@ class LevelIterator final : public InternalIterator {
read_seq_ = read_seq;
}
void Prepare(const std::vector<ScanOptions>* scan_opts) override {
scan_opts_ = scan_opts;
if (file_iter_.iter()) {
file_iter_.Prepare(scan_opts_);
}
}
private:
// Return true if at least one invalid file is seen and skipped.
bool SkipEmptyFileForward();
@@ -1223,6 +1231,7 @@ class LevelIterator final : public InternalIterator {
bool prefix_exhausted_ = false;
// Whether next/prev key is a sentinel key.
bool to_return_sentinel_ = false;
const std::vector<ScanOptions>* scan_opts_;
// Sets flags for if we should return the sentinel key next.
// The condition for returning sentinel is reaching the end of current
@@ -1533,6 +1542,11 @@ void LevelIterator::SetFileIterator(InternalIterator* iter) {
}
InternalIterator* old_iter = file_iter_.Set(iter);
// Since this is a new table iterator, no need to call Prepare() if
// scan_opts_ is null
if (iter && scan_opts_) {
file_iter_.Prepare(scan_opts_);
}
// Update the read pattern for PrefetchBuffer.
if (is_next_read_sequential_) {
+3 -3
View File
@@ -132,7 +132,7 @@ class WriteThread {
size_t protection_bytes_per_key;
PreReleaseCallback* pre_release_callback;
PostMemTableCallback* post_memtable_callback;
uint64_t log_used; // log number that this batch was inserted into
uint64_t wal_used; // log number that this batch was inserted into
uint64_t log_ref; // log number that memtable insert should reference
WriteCallback* callback;
UserWriteCallback* user_write_cb;
@@ -161,7 +161,7 @@ class WriteThread {
protection_bytes_per_key(0),
pre_release_callback(nullptr),
post_memtable_callback(nullptr),
log_used(0),
wal_used(0),
log_ref(0),
callback(nullptr),
user_write_cb(nullptr),
@@ -190,7 +190,7 @@ class WriteThread {
protection_bytes_per_key(_batch->GetProtectionBytesPerKey()),
pre_release_callback(_pre_release_callback),
post_memtable_callback(_post_memtable_callback),
log_used(0),
wal_used(0),
log_ref(_log_ref),
callback(_callback),
user_write_cb(_user_write_cb),
+2 -2
View File
@@ -106,7 +106,6 @@ DECLARE_uint64(db_write_buffer_size);
DECLARE_int32(write_buffer_size);
DECLARE_int32(max_write_buffer_number);
DECLARE_int32(min_write_buffer_number_to_merge);
DECLARE_int32(max_write_buffer_number_to_maintain);
DECLARE_int64(max_write_buffer_size_to_maintain);
DECLARE_bool(use_write_buffer_manager);
DECLARE_double(memtable_prefix_bloom_size_ratio);
@@ -321,7 +320,6 @@ DECLARE_int32(approximate_size_one_in);
DECLARE_bool(best_efforts_recovery);
DECLARE_bool(skip_verifydb);
DECLARE_bool(paranoid_file_checks);
DECLARE_bool(fail_if_options_file_error);
DECLARE_uint64(batch_protection_bytes_per_key);
DECLARE_uint32(memtable_protection_bytes_per_key);
DECLARE_uint32(block_protection_bytes_per_key);
@@ -424,6 +422,8 @@ DECLARE_uint32(commit_bypass_memtable_one_in);
DECLARE_bool(track_and_verify_wals);
DECLARE_bool(enable_remote_compaction);
DECLARE_bool(auto_refresh_iterator_with_snapshot);
DECLARE_uint32(memtable_op_scan_flush_trigger);
DECLARE_uint32(ingest_wbwi_one_in);
constexpr long KB = 1024;
constexpr int kRandomValueMaxFactor = 3;
+10 -18
View File
@@ -168,20 +168,6 @@ DEFINE_int32(min_write_buffer_number_to_merge,
"writing less data to storage if there are duplicate records in"
" each of these individual write buffers.");
DEFINE_int32(max_write_buffer_number_to_maintain,
ROCKSDB_NAMESPACE::Options().max_write_buffer_number_to_maintain,
"The total maximum number of write buffers to maintain in memory "
"including copies of buffers that have already been flushed. "
"Unlike max_write_buffer_number, this parameter does not affect "
"flushing. This controls the minimum amount of write history "
"that will be available in memory for conflict checking when "
"Transactions are used. If this value is too low, some "
"transactions may fail at commit time due to not being able to "
"determine whether there were any write conflicts. Setting this "
"value to 0 will cause write buffers to be freed immediately "
"after they are flushed. If this value is set to -1, "
"'max_write_buffer_number' will be used.");
DEFINE_int64(max_write_buffer_size_to_maintain,
ROCKSDB_NAMESPACE::Options().max_write_buffer_size_to_maintain,
"The total maximum size of write buffers to maintain in memory "
@@ -856,6 +842,11 @@ DEFINE_bool(track_and_verify_wals,
DEFINE_bool(enable_remote_compaction, false,
"Enable (simulated) Remote Compaction");
DEFINE_uint32(ingest_wbwi_one_in, 0,
"If set, will call"
"IngestWriteBatchWithIndex() instead of regular write operations "
"once every N writes.");
static bool ValidateInt32Percent(const char* flagname, int32_t value) {
if (value < 0 || value > 100) {
fprintf(stderr, "Invalid value for --%s: %d, 0<= pct <=100 \n", flagname,
@@ -1098,10 +1089,6 @@ DEFINE_bool(paranoid_file_checks, true,
"After writing every SST file, reopen it and read all the keys "
"and validate checksums");
DEFINE_bool(fail_if_options_file_error, false,
"Fail operations that fail to detect or properly persist options "
"file.");
DEFINE_uint64(batch_protection_bytes_per_key, 0,
"If nonzero, enables integrity protection in `WriteBatch` at the "
"specified number of bytes per key. Currently the only supported "
@@ -1487,4 +1474,9 @@ DEFINE_bool(
ROCKSDB_NAMESPACE::ReadOptions().auto_refresh_iterator_with_snapshot,
"ReadOptions.auto_refresh_iterator_with_snapshot");
DEFINE_uint32(
memtable_op_scan_flush_trigger,
ROCKSDB_NAMESPACE::ColumnFamilyOptions().memtable_op_scan_flush_trigger,
"Sets CF option memtable_op_scan_flush_trigger.");
#endif // GFLAGS
+2 -2
View File
@@ -265,7 +265,7 @@ class DbStressListener : public EventListener {
fault_fs_guard->DisableAllThreadLocalErrorInjection();
// TODO(hx235): only exempt the flush thread during error recovery instead
// of all the flush threads from error injection
fault_fs_guard->SetIOActivtiesExcludedFromFaultInjection(
fault_fs_guard->SetIOActivitiesExcludedFromFaultInjection(
{Env::IOActivity::kFlush});
}
}
@@ -275,7 +275,7 @@ class DbStressListener : public EventListener {
RandomSleep();
if (FLAGS_error_recovery_with_no_fault_injection && fault_fs_guard) {
fault_fs_guard->EnableAllThreadLocalErrorInjection();
fault_fs_guard->SetIOActivtiesExcludedFromFaultInjection({});
fault_fs_guard->SetIOActivitiesExcludedFromFaultInjection({});
}
}
+2 -5
View File
@@ -3376,8 +3376,6 @@ void StressTest::PrintEnv() const {
FLAGS_sync_fault_injection);
fprintf(stdout, "Best efforts recovery : %d\n",
static_cast<int>(FLAGS_best_efforts_recovery));
fprintf(stdout, "Fail if OPTIONS file error: %d\n",
static_cast<int>(FLAGS_fail_if_options_file_error));
fprintf(stdout, "User timestamp size bytes : %d\n",
static_cast<int>(FLAGS_user_timestamp_size));
fprintf(stdout, "Persist user defined timestamps : %d\n",
@@ -4047,8 +4045,6 @@ void InitializeOptionsFromFlags(
options.max_write_buffer_number = FLAGS_max_write_buffer_number;
options.min_write_buffer_number_to_merge =
FLAGS_min_write_buffer_number_to_merge;
options.max_write_buffer_number_to_maintain =
FLAGS_max_write_buffer_number_to_maintain;
options.max_write_buffer_size_to_maintain =
FLAGS_max_write_buffer_size_to_maintain;
options.memtable_prefix_bloom_size_ratio =
@@ -4257,7 +4253,6 @@ void InitializeOptionsFromFlags(
options.best_efforts_recovery = FLAGS_best_efforts_recovery;
options.paranoid_file_checks = FLAGS_paranoid_file_checks;
options.fail_if_options_file_error = FLAGS_fail_if_options_file_error;
if (FLAGS_user_timestamp_size > 0) {
CheckAndSetOptionsForUserTimestamp(options);
@@ -4321,6 +4316,8 @@ void InitializeOptionsFromFlags(
if (FLAGS_enable_remote_compaction) {
options.compaction_service = std::make_shared<DbStressCompactionService>();
}
options.memtable_op_scan_flush_trigger = FLAGS_memtable_op_scan_flush_trigger;
}
void InitializeOptionsGeneral(
+44 -4
View File
@@ -1845,7 +1845,17 @@ class NonBatchedOpsStressTest : public StressTest {
} else if (FLAGS_use_merge) {
if (!FLAGS_use_txn) {
if (FLAGS_user_timestamp_size == 0) {
s = db_->Merge(write_opts, cfh, k, v);
if (FLAGS_ingest_wbwi_one_in &&
thread->rand.OneIn(FLAGS_ingest_wbwi_one_in)) {
auto wbwi = std::make_shared<WriteBatchWithIndex>(
options_.comparator, 0, /*overwrite_key=*/true);
s = wbwi->Merge(cfh, k, v);
if (s.ok()) {
s = db_->IngestWriteBatchWithIndex(write_opts, wbwi);
}
} else {
s = db_->Merge(write_opts, cfh, k, v);
}
} else {
s = db_->Merge(write_opts, cfh, k, write_ts, v);
}
@@ -1857,7 +1867,17 @@ class NonBatchedOpsStressTest : public StressTest {
} else {
if (!FLAGS_use_txn) {
if (FLAGS_user_timestamp_size == 0) {
s = db_->Put(write_opts, cfh, k, v);
if (FLAGS_ingest_wbwi_one_in &&
thread->rand.OneIn(FLAGS_ingest_wbwi_one_in)) {
auto wbwi = std::make_shared<WriteBatchWithIndex>(
options_.comparator, 0, /*overwrite_key=*/true);
s = wbwi->Put(cfh, k, v);
if (s.ok()) {
s = db_->IngestWriteBatchWithIndex(write_opts, wbwi);
}
} else {
s = db_->Put(write_opts, cfh, k, v);
}
} else {
s = db_->Put(write_opts, cfh, k, write_ts, v);
}
@@ -1949,7 +1969,17 @@ class NonBatchedOpsStressTest : public StressTest {
}
if (!FLAGS_use_txn) {
if (FLAGS_user_timestamp_size == 0) {
s = db_->Delete(write_opts, cfh, key);
if (FLAGS_ingest_wbwi_one_in &&
thread->rand.OneIn(FLAGS_ingest_wbwi_one_in)) {
auto wbwi = std::make_shared<WriteBatchWithIndex>(
options_.comparator, 0, /*overwrite_key=*/true);
s = wbwi->Delete(cfh, key);
if (s.ok()) {
s = db_->IngestWriteBatchWithIndex(write_opts, wbwi);
}
} else {
s = db_->Delete(write_opts, cfh, key);
}
} else {
s = db_->Delete(write_opts, cfh, key, write_ts);
}
@@ -2006,7 +2036,17 @@ class NonBatchedOpsStressTest : public StressTest {
}
if (!FLAGS_use_txn) {
if (FLAGS_user_timestamp_size == 0) {
s = db_->SingleDelete(write_opts, cfh, key);
if (FLAGS_ingest_wbwi_one_in &&
thread->rand.OneIn(FLAGS_ingest_wbwi_one_in)) {
auto wbwi = std::make_shared<WriteBatchWithIndex>(
options_.comparator, 0, /*overwrite_key=*/true);
s = wbwi->SingleDelete(cfh, key);
if (s.ok()) {
s = db_->IngestWriteBatchWithIndex(write_opts, wbwi);
}
} else {
s = db_->SingleDelete(write_opts, cfh, key);
}
} else {
s = db_->SingleDelete(write_opts, cfh, key, write_ts);
}
+20 -10
View File
@@ -322,8 +322,17 @@ class PosixFileSystem : public FileSystem {
if (options.use_mmap_writes) {
MaybeForceDisableMmap(fd);
}
uint64_t initial_file_size = 0;
if (reopen) {
s = GetFileSize(fname, IOOptions(), &initial_file_size, nullptr);
if (!s.ok()) {
close(fd);
return s;
}
}
if (options.use_mmap_writes && !forceMmapOff_) {
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
result->reset(
new PosixMmapFile(fname, fd, page_size_, options, initial_file_size));
} else if (options.use_direct_writes && !options.use_mmap_writes) {
#ifdef OS_MACOSX
if (fcntl(fd, F_NOCACHE, 1) == -1) {
@@ -343,7 +352,7 @@ class PosixFileSystem : public FileSystem {
#endif
result->reset(new PosixWritableFile(
fname, fd, GetLogicalBlockSizeForWriteIfNeeded(options, fname, fd),
options));
options, initial_file_size));
} else {
// disable mmap writes
EnvOptions no_mmap_writes_options = options;
@@ -352,7 +361,7 @@ class PosixFileSystem : public FileSystem {
new PosixWritableFile(fname, fd,
GetLogicalBlockSizeForWriteIfNeeded(
no_mmap_writes_options, fname, fd),
no_mmap_writes_options));
no_mmap_writes_options, initial_file_size));
}
return s;
}
@@ -418,7 +427,8 @@ class PosixFileSystem : public FileSystem {
MaybeForceDisableMmap(fd);
}
if (options.use_mmap_writes && !forceMmapOff_) {
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
result->reset(new PosixMmapFile(fname, fd, page_size_, options,
/*initial_file_size=*/0));
} else if (options.use_direct_writes && !options.use_mmap_writes) {
#ifdef OS_MACOSX
if (fcntl(fd, F_NOCACHE, 1) == -1) {
@@ -438,16 +448,16 @@ class PosixFileSystem : public FileSystem {
#endif
result->reset(new PosixWritableFile(
fname, fd, GetLogicalBlockSizeForWriteIfNeeded(options, fname, fd),
options));
options, /*initial_file_size=*/0));
} else {
// disable mmap writes
FileOptions no_mmap_writes_options = options;
no_mmap_writes_options.use_mmap_writes = false;
result->reset(
new PosixWritableFile(fname, fd,
GetLogicalBlockSizeForWriteIfNeeded(
no_mmap_writes_options, fname, fd),
no_mmap_writes_options));
result->reset(new PosixWritableFile(
fname, fd,
GetLogicalBlockSizeForWriteIfNeeded(no_mmap_writes_options, fname,
fd),
no_mmap_writes_options, /*initial_file_size=*/0));
}
return s;
}
+6 -4
View File
@@ -1138,7 +1138,8 @@ IOStatus PosixMmapFile::Msync() {
}
PosixMmapFile::PosixMmapFile(const std::string& fname, int fd, size_t page_size,
const EnvOptions& options)
const EnvOptions& options,
uint64_t initial_file_size)
: filename_(fname),
fd_(fd),
page_size_(page_size),
@@ -1147,7 +1148,7 @@ PosixMmapFile::PosixMmapFile(const std::string& fname, int fd, size_t page_size,
limit_(nullptr),
dst_(nullptr),
last_sync_(nullptr),
file_offset_(0) {
file_offset_(initial_file_size) {
#ifdef ROCKSDB_FALLOCATE_PRESENT
allow_fallocate_ = options.allow_fallocate;
fallocate_with_keep_size_ = options.fallocate_with_keep_size;
@@ -1317,12 +1318,13 @@ IOStatus PosixMmapFile::Allocate(uint64_t offset, uint64_t len,
*/
PosixWritableFile::PosixWritableFile(const std::string& fname, int fd,
size_t logical_block_size,
const EnvOptions& options)
const EnvOptions& options,
uint64_t initial_file_size)
: FSWritableFile(options),
filename_(fname),
use_direct_io_(options.use_direct_writes),
fd_(fd),
filesize_(0),
filesize_(initial_file_size),
logical_sector_size_(logical_block_size) {
#ifdef ROCKSDB_FALLOCATE_PRESENT
allow_fallocate_ = options.allow_fallocate;
+3 -2
View File
@@ -374,7 +374,8 @@ class PosixWritableFile : public FSWritableFile {
public:
explicit PosixWritableFile(const std::string& fname, int fd,
size_t logical_block_size,
const EnvOptions& options);
const EnvOptions& options,
uint64_t initial_file_size);
virtual ~PosixWritableFile();
// Need to implement this so the file is truncated correctly
@@ -469,7 +470,7 @@ class PosixMmapFile : public FSWritableFile {
public:
PosixMmapFile(const std::string& fname, int fd, size_t page_size,
const EnvOptions& options);
const EnvOptions& options, uint64_t initial_file_size);
~PosixMmapFile();
// Means Close() will properly take care of truncate
+5 -1
View File
@@ -126,6 +126,8 @@ Status FilePrefetchBuffer::Read(BufferInfo* buf, const IOOptions& opts,
if (usage_ == FilePrefetchBufferUsage::kUserScanPrefetch) {
RecordTick(stats_, PREFETCH_BYTES, read_len);
} else if (usage_ == FilePrefetchBufferUsage::kCompactionPrefetch) {
RecordInHistogram(stats_, COMPACTION_PREFETCH_BYTES, read_len);
}
if (!use_fs_buffer) {
// Update the buffer size.
@@ -154,7 +156,9 @@ Status FilePrefetchBuffer::ReadAsync(BufferInfo* buf, const IOOptions& opts,
&(buf->del_fn_), /*aligned_buf =*/nullptr);
req.status.PermitUncheckedError();
if (s.ok()) {
RecordTick(stats_, PREFETCH_BYTES, read_len);
if (usage_ == FilePrefetchBufferUsage::kUserScanPrefetch) {
RecordTick(stats_, PREFETCH_BYTES, read_len);
}
buf->async_read_in_progress_ = true;
}
return s;
+4
View File
@@ -134,6 +134,7 @@ struct BufferInfo {
enum class FilePrefetchBufferUsage {
kTableOpenPrefetchTail,
kUserScanPrefetch,
kCompactionPrefetch,
kUnknown,
};
@@ -574,6 +575,9 @@ class FilePrefetchBuffer {
size_t& read_len, uint64_t& aligned_useful_len);
void UpdateStats(bool found_in_buffer, size_t length_found) {
if (usage_ != FilePrefetchBufferUsage::kUserScanPrefetch) {
return;
}
if (found_in_buffer) {
RecordTick(stats_, PREFETCH_HITS);
}
+31 -5
View File
@@ -299,9 +299,18 @@ TEST_P(PrefetchTest, Basic) {
const uint64_t prev_table_open_prefetch_tail_hit =
options.statistics->getTickerCount(TABLE_OPEN_PREFETCH_TAIL_HIT);
HistogramData pre_compaction_prefetch_bytes;
options.statistics->histogramData(COMPACTION_PREFETCH_BYTES,
&pre_compaction_prefetch_bytes);
ASSERT_EQ(pre_compaction_prefetch_bytes.count, 0);
// commenting out the line below causes the example to work correctly
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &least, &greatest));
HistogramData post_compaction_prefetch_bytes;
options.statistics->histogramData(COMPACTION_PREFETCH_BYTES,
&post_compaction_prefetch_bytes);
HistogramData cur_table_open_prefetch_tail_read;
options.statistics->histogramData(TABLE_OPEN_PREFETCH_TAIL_READ_BYTES,
&cur_table_open_prefetch_tail_read);
@@ -318,6 +327,7 @@ TEST_P(PrefetchTest, Basic) {
ASSERT_GT(fs->GetPrefetchCount(), 1);
ASSERT_EQ(0, buff_prefetch_count);
fs->ClearPrefetchCount();
ASSERT_EQ(post_compaction_prefetch_bytes.count, 0);
} else {
ASSERT_FALSE(fs->IsPrefetchCalled());
// To rule out false positive by the SST file tail prefetch during
@@ -331,6 +341,20 @@ TEST_P(PrefetchTest, Basic) {
prev_table_open_prefetch_tail_hit);
ASSERT_GE(cur_table_open_prefetch_tail_miss,
prev_table_open_prefetch_tail_miss);
ASSERT_GT(post_compaction_prefetch_bytes.count, 0);
// Not an exact match due to potential roundup/down for alignment
auto expected_compaction_readahead_size =
Options().compaction_readahead_size;
ASSERT_LE(post_compaction_prefetch_bytes.max,
expected_compaction_readahead_size * 1.1);
ASSERT_GE(post_compaction_prefetch_bytes.max,
expected_compaction_readahead_size * 0.9);
ASSERT_LE(post_compaction_prefetch_bytes.average,
expected_compaction_readahead_size * 1.1);
ASSERT_GE(post_compaction_prefetch_bytes.average,
expected_compaction_readahead_size * 0.9);
}
for (bool disable_io : {false, true}) {
@@ -3251,8 +3275,9 @@ TEST_F(FilePrefetchBufferTest, SyncReadaheadStats) {
ReadaheadParams readahead_params;
readahead_params.initial_readahead_size = 8192;
readahead_params.max_readahead_size = 8192;
FilePrefetchBuffer fpb(readahead_params, true, false, fs(), nullptr,
stats.get());
FilePrefetchBuffer fpb(
readahead_params, true, false, fs(), nullptr, stats.get(),
nullptr /* cb */, FilePrefetchBufferUsage::kUserScanPrefetch /* usage */);
Slice result;
// Simulate a seek of 4096 bytes at offset 0. Due to the readahead settings,
// it will do a read of offset 0 and length - (4096 + 8192) 12288.
@@ -3497,9 +3522,10 @@ TEST_P(FSBufferPrefetchTest, FSBufferPrefetchStatsInternals) {
size_t num_buffers = use_async_prefetch ? 2 : 1;
readahead_params.num_buffers = num_buffers;
FilePrefetchBuffer fpb(readahead_params, true /* enable */,
false /* track_min_offset */, fs(), clock(),
stats.get());
FilePrefetchBuffer fpb(
readahead_params, true /* enable */, false /* track_min_offset */, fs(),
clock(), stats.get(), nullptr /* cb */,
FilePrefetchBufferUsage::kUserScanPrefetch /* usage */);
int overlap_buffer_write_ct = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
+36
View File
@@ -0,0 +1,36 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#pragma once
#include "rocksdb/slice.h"
namespace ROCKSDB_NAMESPACE {
enum class IterBoundCheck : char {
kUnknown = 0,
kOutOfBound,
kInbound,
};
// This structure encapsulates the result of NextAndGetResult()
struct IterateResult {
// The lifetime of key is guaranteed until Next()/NextAndGetResult() is
// called.
Slice key;
// If the iterator becomes invalid after a NextAndGetResult(), the table
// iterator should set this to indicate whether it became invalid due
// to the next key being out of bound (kOutOfBound) or it reached end
// of file (kUnknown). If the iiterator is still valid, this should
// be set to kInbound.
IterBoundCheck bound_check_result = IterBoundCheck::kUnknown;
// If false, PrepareValue() needs to be called before value()
// This is useful if the table reader wants to materialize the value in a
// lazy manner. In that case, it can set this to false and RocksDB
// guarantees that it'll call PrepareValue() before calling value().
bool value_prepared = true;
};
} // namespace ROCKSDB_NAMESPACE
+14 -9
View File
@@ -171,15 +171,6 @@ struct AdvancedColumnFamilyOptions {
// Default: 1
int min_write_buffer_number_to_merge = 1;
// DEPRECATED
// The total maximum number of write buffers to maintain in memory including
// copies of buffers that have already been flushed. Unlike
// max_write_buffer_number, this parameter does not affect flushing.
// This parameter is being replaced by max_write_buffer_size_to_maintain.
// If both parameters are set to non-zero values, this parameter will be
// ignored.
int max_write_buffer_number_to_maintain = 0;
// The target number of write history bytes to hold in memory. Write history
// comprises the latest write buffers (memtables). To reach the target, write
// buffers that were most recently flushed to SST files may be retained in
@@ -1105,6 +1096,20 @@ struct AdvancedColumnFamilyOptions {
// additional key comparison during memtable lookup.
bool paranoid_memory_checks = false;
// When an iterator scans this number of invisible entries (tombstones or
// hidden puts) from the active memtable during a single iterator operation,
// we will attempt to flush the memtable. Currently only forward scans are
// supported (SeekToFirst(), Seek() and Next()).
// This option helps to reduce the overhead of scanning through a
// large number of entries in memtable.
// Users should consider enable deletion-triggered-compaction (see
// CompactOnDeletionCollectorFactory) together with this option to compact
// away tombstones after the memtable is flushed.
//
// Default: 0 (disabled)
// Dynamically changeable through the SetOptions() API.
uint32_t memtable_op_scan_flush_trigger = 0;
// Create ColumnFamilyOptions with default values for all fields
AdvancedColumnFamilyOptions();
// Create ColumnFamilyOptions from Options
+5 -5
View File
@@ -1341,6 +1341,11 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_periodic_compaction_seconds(
rocksdb_options_t*, uint64_t);
extern ROCKSDB_LIBRARY_API uint64_t
rocksdb_options_get_periodic_compaction_seconds(rocksdb_options_t*);
extern ROCKSDB_LIBRARY_API void
rocksdb_options_set_memtable_op_scan_flush_trigger(rocksdb_options_t*,
uint32_t);
extern ROCKSDB_LIBRARY_API uint32_t
rocksdb_options_get_memtable_op_scan_flush_trigger(rocksdb_options_t*);
enum {
rocksdb_statistics_level_disable_all = 0,
@@ -1448,11 +1453,6 @@ rocksdb_options_set_min_write_buffer_number_to_merge(rocksdb_options_t*, int);
extern ROCKSDB_LIBRARY_API int
rocksdb_options_get_min_write_buffer_number_to_merge(rocksdb_options_t*);
extern ROCKSDB_LIBRARY_API void
rocksdb_options_set_max_write_buffer_number_to_maintain(rocksdb_options_t*,
int);
extern ROCKSDB_LIBRARY_API int
rocksdb_options_get_max_write_buffer_number_to_maintain(rocksdb_options_t*);
extern ROCKSDB_LIBRARY_API void
rocksdb_options_set_max_write_buffer_size_to_maintain(rocksdb_options_t*,
int64_t);
extern ROCKSDB_LIBRARY_API int64_t
+56 -5
View File
@@ -22,6 +22,7 @@
#include "rocksdb/iterator.h"
#include "rocksdb/listener.h"
#include "rocksdb/metadata.h"
#include "rocksdb/multi_scan.h"
#include "rocksdb/options.h"
#include "rocksdb/snapshot.h"
#include "rocksdb/sst_file_writer.h"
@@ -30,6 +31,7 @@
#include "rocksdb/types.h"
#include "rocksdb/user_write_callback.h"
#include "rocksdb/utilities/table_properties_collectors.h"
#include "rocksdb/utilities/write_batch_with_index.h"
#include "rocksdb/version.h"
#include "rocksdb/wide_columns.h"
@@ -616,7 +618,7 @@ class DB {
const Slice& /*key*/, const Slice& /*ts*/,
const Slice& /*value*/);
// Apply the specified updates to the database.
// Apply the specified updates atomically to the database.
// If `updates` contains no update, WAL will still be synced if
// options.sync=true.
// Returns OK on success, non-OK on failure.
@@ -632,6 +634,21 @@ class DB {
"WriteWithCallback not implemented for this interface.");
}
// EXPERIMENTAL, subject to change
// Ingest a WriteBatchWithIndex into DB, bypassing memtable writes for better
// write performance. Useful when there is a large number of updates
// in the write batch.
// The WriteBatchWithIndex must be created with overwrite_key=true.
// Currently this requires WriteOptions::disableWAL=true.
// The following options are currently not supported:
// - unordered_write
// - enable_pipelined_write
virtual Status IngestWriteBatchWithIndex(
const WriteOptions& /*options*/,
std::shared_ptr<WriteBatchWithIndex> /*wbwi*/) {
return Status::NotSupported("IngestWriteBatchWithIndex not implemented.");
}
// If the column family specified by "column_family" contains an entry for
// "key", return the corresponding value in "*value". If the entry is a plain
// key-value, return the value as-is; if it is a wide-column entity, return
@@ -1073,6 +1090,18 @@ class DB {
const ReadOptions& options,
const std::vector<ColumnFamilyHandle*>& column_families) = 0;
// Get an iterator that scans multiple key ranges. The scan ranges should
// be in increasing order of start key. See multi_scan_iterator.h for more
// details.
virtual std::unique_ptr<MultiScan> NewMultiScan(
const ReadOptions& /*options*/, ColumnFamilyHandle* /*column_family*/,
const std::vector<ScanOptions>& /*scan_opts*/) {
std::unique_ptr<Iterator> iter(NewErrorIterator(Status::NotSupported()));
std::unique_ptr<MultiScan> ms_iter =
std::make_unique<MultiScan>(std::move(iter));
return ms_iter;
}
// Return a handle to the current DB state. Iterators created with
// this handle will all observe a stable snapshot of the current DB
// state. The caller must call ReleaseSnapshot(result) when the
@@ -1658,9 +1687,12 @@ class DB {
virtual int NumberLevels(ColumnFamilyHandle* column_family) = 0;
virtual int NumberLevels() { return NumberLevels(DefaultColumnFamily()); }
// DEPRECATED:
// Maximum level to which a new compacted memtable is pushed if it
// does not create overlap.
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) = 0;
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* /*column_family*/) {
return 0;
}
virtual int MaxMemCompactionLevel() {
return MaxMemCompactionLevel(DefaultColumnFamily());
}
@@ -1766,6 +1798,25 @@ class DB {
virtual Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
std::string* ts_low) = 0;
// EXPERIMENTAL
// Get the newest timestamp of the column family. This is only for when the
// column family enables user defined timestamp and when timestamps are not
// persisted in SST files, a.k.a `persist_user_defined_timestamps=false`.
// This checks the mutable memtable, the immutable memtable and the SST files,
// and returns the first newest user defined timestamp found.
// When user defined timestamp is not persisted in SST files, metadata in
// MANIFEST tracks the most recently seen timestamp for SST files, so the
// newest timestamp in SST files can be found.
// OK status is returned if finding the newest timestamp succeeds, if
// `newest_timestamp` is empty, it means the column family hasn't seen any
// timestamp. The returned timestamp is encoded, util method `DecodeU64Ts` can
// be used to decode it into uint64_t.
// User-defined timestamp is required to be increasing per key, the return
// value of this API would be most useful if the user-defined timestamp is
// monotonically increasing across keys.
virtual Status GetNewestUserDefinedTimestamp(
ColumnFamilyHandle* column_family, std::string* newest_timestamp) = 0;
// Suspend deleting obsolete files. Compactions will continue to occur,
// but no obsolete files will be deleted. To resume file deletions, each
// call to DisableFileDeletions() must be matched by a subsequent call to
@@ -1877,12 +1928,12 @@ class DB {
// Retrieve information about the current wal file
//
// Note that the log might have rolled after this call in which case
// the current_log_file would not point to the current log file.
// the current_wal_file would not point to the current log file.
//
// Additionally, for the sake of optimization current_log_file->StartSequence
// Additionally, for the sake of optimization current_wal_file->StartSequence
// would always be set to 0
virtual Status GetCurrentWalFile(
std::unique_ptr<WalFile>* current_log_file) = 0;
std::unique_ptr<WalFile>* current_wal_file) = 0;
// IngestExternalFile() will load a list of external SST files (1) into the DB
// Two primary modes are supported:
+5
View File
@@ -21,6 +21,11 @@ Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family,
const Slice* begin, const Slice* end);
Status SuggestCompactRange(DB* db, const Slice* begin, const Slice* end);
// DEPRECATED: this API may be removed in a future release.
// This operation can be done through CompactRange() by setting
// CompactRangeOptions::bottommost_level_compaction set to
// BottommostLevelCompaction::kSkip and setting target level.
//
// Move all L0 files to target_level skipping compaction.
// This operation succeeds only if the files in L0 have disjoint ranges; this
// is guaranteed to happen, for instance, if keys are inserted in sorted
+58 -6
View File
@@ -5,9 +5,11 @@
#pragma once
#include "rocksdb/advanced_iterator.h"
#include "rocksdb/customizable.h"
#include "rocksdb/file_checksum.h"
#include "rocksdb/iterator.h"
#include "rocksdb/file_system.h"
#include "rocksdb/iterator_base.h"
#include "rocksdb/options.h"
#include "rocksdb/status.h"
@@ -58,6 +60,47 @@ class ExternalTableFactory;
// the external table implementation.
// TODO: Specify which options are relevant
class ExternalTableIterator : public IteratorBase {
public:
virtual ~ExternalTableIterator() {}
// This can optionally be called to prepare the iterator for a series
// of scans. The scan_opts parameter specifies the order of scans to
// follow, as well as the limits for those scans. After calling this,
// the caller will Seek() the iterator to successive start keys in scan_opts.
//
// If Prepare() is called again with a different scan_opts pointer, it
// means the iterator will be reused for a new multi scan. If scan_opts
// is null, then the previous Prepare() can be discarded.
//
// The caller guarantees the lifetime of scan_opts until its either cleared
// or replaced by another Prepare().
// TODO: Update the contract to trim the scan_opts range to only include
// scans that potentially intersect the file key range.
//
// If the sequence of Seeks is interrupted by seeking to some other target
// key, then the iterator is free to discard anything done during Prepare.
virtual void Prepare(const ScanOptions scan_opts[], size_t num_opts) = 0;
// Similar to Next(), except it also fills the result and returns whether
// the iterator is on a valid key or not
virtual bool NextAndGetResult(IterateResult* result) = 0;
// Prepares the value if its lazily materialized. The implementation can
// request that this be called by setting value_prepared to false in
// IterateResult. Next() should always implicitly materialize the
// value.
bool PrepareValue() override = 0;
// Return the current key's value
virtual Slice value() const = 0;
// Return the current position bounds check result - kInbound if the
// position is a valid key, kOutOfBound if the key is out of bound (i.e
// scan has terminated), or kUnknown if end of file.
virtual IterBoundCheck UpperBoundCheckResult() = 0;
};
class ExternalTableReader {
public:
virtual ~ExternalTableReader() {}
@@ -65,8 +108,9 @@ class ExternalTableReader {
// Return an Iterator that can be used to scan the table file.
// The read_options can optionally contain the upper bound
// key (exclusive) of the scan in iterate_upper_bound.
virtual Iterator* NewIterator(const ReadOptions& read_options,
const SliceTransform* prefix_extractor) = 0;
virtual ExternalTableIterator* NewIterator(
const ReadOptions& read_options,
const SliceTransform* prefix_extractor) = 0;
// Point lookup the given key and return its value
virtual Status Get(const ReadOptions& read_options, const Slice& key,
@@ -153,11 +197,17 @@ class ExternalTableBuilder {
struct ExternalTableOptions {
const std::shared_ptr<const SliceTransform>& prefix_extractor;
const Comparator* comparator;
const std::shared_ptr<FileSystem>& fs;
const FileOptions& file_options;
ExternalTableOptions(
const std::shared_ptr<const SliceTransform>& _prefix_extractor,
const Comparator* _comparator)
: prefix_extractor(_prefix_extractor), comparator(_comparator) {}
const Comparator* _comparator, const std::shared_ptr<FileSystem>& _fs,
const FileOptions& _file_options)
: prefix_extractor(_prefix_extractor),
comparator(_comparator),
fs(_fs),
file_options(_file_options) {}
};
struct ExternalTableBuilderOptions {
@@ -194,9 +244,11 @@ class ExternalTableFactory : public Customizable {
const ExternalTableOptions& table_options,
std::unique_ptr<ExternalTableReader>* table_reader) const = 0;
// The table builder should use the file pointer to append to the file.
// Do not sync or close the file after finishing. RocksDB will do that.
virtual ExternalTableBuilder* NewTableBuilder(
const ExternalTableBuilderOptions& builder_options,
const std::string& file_path) const = 0;
const std::string& file_path, FSWritableFile* file) const = 0;
};
// Allocate a TableFactory that wraps around an ExternalTableFactory. Use this
+13
View File
@@ -21,6 +21,7 @@
#include <string>
#include "rocksdb/iterator_base.h"
#include "rocksdb/options.h"
#include "rocksdb/wide_columns.h"
namespace ROCKSDB_NAMESPACE {
@@ -93,6 +94,18 @@ class Iterator : public IteratorBase {
assert(false);
return Slice();
}
// RocksDB Internal - DO NOT USE
// Prepare the iterator to scan the ranges specified in scan_opts. The
// upper bound and other table specific limits may be specified. This will
// typically be followed by Seeks to the start keys in the order they're
// specified in scan_opts. If the user does a Seek to some other target key,
// the iterator should disregard the scan_opts from that point onwards and
// behave like a normal iterator. Its the user's responsibility to again
// call Prepare().
// If Prepare() is called, it overrides the iterate_upper_bound in
// ReadOptions
virtual void Prepare(const std::vector<ScanOptions>& /*scan_opts*/) {}
};
// Return an empty iterator (yields nothing).
+237
View File
@@ -0,0 +1,237 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#pragma once
#include "rocksdb/iterator.h"
#include "rocksdb/options.h"
namespace ROCKSDB_NAMESPACE {
// EXPERIMENTAL
//
// An iterator that returns results from multiple scan ranges. The ranges are
// expected to be in increasing sorted order.
// The results are returned in nested container objects that can be iterated
// using an std::input_iterator.
//
// MultiScan
// |
// ---
// |
// MultiScanIterator <-- std::input_iterator (returns a Scan object for each
// | scan range)
// ---
// |
// Scan
// |
// ---
// |
// ScanIterator <-- std::input_iterator (returns the KVs of a single
// scan range)
//
// The application on top of RocksDB
// would use this as follows -
//
// std::vector<ScanOptions> scans{{.start = Slice("bar")},
// {.start = Slice("foo")}};
// std::unique_ptr<MultiScan> iter.reset(
// db->NewMultiScan());
// try {
// for (auto scan : *iter) {
// for (auto it : scan) {
// // Do something with key - it.first
// // Do something with value - it.second
// }
// }
// } catch (MultiScanException& ex) {
// // Check ex.status()
// } catch (std::logic_error& ex) {
// // Check ex.what()
// }
class MultiScanException : public std::runtime_error {
public:
explicit MultiScanException(Status& s)
: std::runtime_error(s.ToString()), s_(s) {}
Status& status() { return s_; }
private:
Status s_;
};
// A container object encapsulating a single scan range. It supports an
// std::input_iterator for a single pass iteration of the KVs in the range.
// A Status exception is thrown if there is an error in scanning the range.
class Scan {
public:
class ScanIterator;
explicit Scan(Iterator* db_iter) : db_iter_(db_iter) {}
ScanIterator begin() { return ScanIterator(db_iter_); }
std::nullptr_t end() { return nullptr; }
class ScanIterator {
public:
using self_type = ScanIterator;
using value_type = std::pair<Slice, Slice>;
using reference = std::pair<Slice, Slice>&;
using pointer = std::pair<Slice, Slice>*;
using difference_type = int;
using iterator_category = std::input_iterator_tag;
explicit ScanIterator(Iterator* db_iter) : db_iter_(db_iter) {
valid_ = db_iter_->Valid();
if (valid_) {
result_ = value_type(db_iter_->key(), db_iter_->value());
}
}
ScanIterator() : db_iter_(nullptr), valid_(false) {}
~ScanIterator() { assert(status_.ok()); }
ScanIterator& operator++() {
if (!valid_) {
throw std::logic_error("Trying to advance invalid iterator");
} else {
db_iter_->Next();
status_ = db_iter_->status();
if (!status_.ok()) {
throw MultiScanException(status_);
} else {
valid_ = db_iter_->Valid();
if (valid_) {
result_ = value_type(db_iter_->key(), db_iter_->value());
}
}
}
return *this;
}
bool operator==(std::nullptr_t /*other*/) const { return !valid_; }
bool operator!=(std::nullptr_t /*other*/) const { return valid_; }
reference operator*() {
if (!valid_) {
throw std::logic_error("Trying to deref invalid iterator");
}
return result_;
}
reference operator->() {
if (!valid_) {
throw std::logic_error("Trying to deref invalid iterator");
}
return result_;
}
private:
Iterator* db_iter_;
bool valid_;
Status status_;
value_type result_;
};
private:
Iterator* db_iter_;
};
// A container object encapsulating the scan ranges for a multi scan.
// It supports an std::input_iterator for a single pass iteration of the
// ScanOptions in scan_opts, which can be dereferenced to get the container
// (Scan) for a single range.
// A Status exception is thrown if there is an error.
class MultiScan {
public:
MultiScan(const std::vector<ScanOptions>& scan_opts,
std::unique_ptr<Iterator>&& db_iter)
: scan_opts_(scan_opts), db_iter_(std::move(db_iter)) {}
explicit MultiScan(std::unique_ptr<Iterator>&& db_iter)
: db_iter_(std::move(db_iter)) {}
class MultiScanIterator {
public:
MultiScanIterator(const MultiScanIterator&) = delete;
MultiScanIterator operator=(MultiScanIterator&) = delete;
using self_type = MultiScanIterator;
using value_type = Scan;
using reference = Scan&;
using pointer = Scan*;
using difference_type = int;
using iterator_category = std::input_iterator_tag;
MultiScanIterator(const std::vector<ScanOptions>& scan_opts,
Iterator* db_iter)
: scan_opts_(scan_opts), idx_(0), db_iter_(db_iter), scan_(db_iter_) {
if (scan_opts_.empty()) {
throw std::logic_error("Zero scans in multi-scan");
}
db_iter_->Seek(*scan_opts_[idx_].range.start);
status_ = db_iter_->status();
if (!status_.ok()) {
throw MultiScanException(status_);
}
}
explicit MultiScanIterator(const std::vector<ScanOptions>& scan_opts)
: scan_opts_(scan_opts),
idx_(scan_opts_.size()),
db_iter_(nullptr),
scan_(nullptr) {}
~MultiScanIterator() { assert(status_.ok()); }
MultiScanIterator& operator++() {
if (idx_ >= scan_opts_.size()) {
throw std::logic_error("Index out of range");
}
idx_++;
if (idx_ < scan_opts_.size()) {
db_iter_->Seek(*scan_opts_[idx_].range.start);
status_ = db_iter_->status();
if (!status_.ok()) {
throw MultiScanException(status_);
}
}
return *this;
}
bool operator==(std::nullptr_t /*other*/) const {
return idx_ >= scan_opts_.size();
}
bool operator!=(std::nullptr_t /*other*/) const {
return idx_ < scan_opts_.size();
}
reference operator*() { return scan_; }
reference operator->() { return scan_; }
private:
const std::vector<ScanOptions>& scan_opts_;
size_t idx_;
Iterator* db_iter_;
Status status_;
Scan scan_;
};
MultiScanIterator begin() {
return MultiScanIterator(scan_opts_, db_iter_.get());
}
std::nullptr_t end() { return nullptr; }
private:
const std::vector<ScanOptions> scan_opts_;
std::unique_ptr<Iterator> db_iter_;
};
} // namespace ROCKSDB_NAMESPACE
+76 -55
View File
@@ -477,12 +477,19 @@ struct CompactionServiceJobInfo {
bool is_manual_compaction;
bool bottommost_level;
// the smallest input level of the compaction.
// (same as Compaction::start_level and CompactionJobInfo::base_input_level)
int base_input_level;
// the output level of the compaction.
int output_level;
CompactionServiceJobInfo(std::string db_name_, std::string db_id_,
std::string db_session_id_, uint64_t job_id_,
Env::Priority priority_,
CompactionReason compaction_reason_,
bool is_full_compaction_, bool is_manual_compaction_,
bool bottommost_level_)
bool bottommost_level_, int base_input_level_,
int output_level_)
: db_name(std::move(db_name_)),
db_id(std::move(db_id_)),
db_session_id(std::move(db_session_id_)),
@@ -491,7 +498,9 @@ struct CompactionServiceJobInfo {
compaction_reason(compaction_reason_),
is_full_compaction(is_full_compaction_),
is_manual_compaction(is_manual_compaction_),
bottommost_level(bottommost_level_) {}
bottommost_level(bottommost_level_),
base_input_level(base_input_level_),
output_level(output_level_) {}
};
struct CompactionServiceScheduleResponse {
@@ -815,6 +824,7 @@ struct DBOptions {
// If it is non empty, the log files will be in the specified dir,
// and the db data dir's absolute path will be used as the log file
// name's prefix.
// NOTE: not for WALs
std::string db_log_dir = "";
// This specifies the absolute dir path for write-ahead logs (WAL).
@@ -895,21 +905,24 @@ struct DBOptions {
// be created.
// If max_log_file_size == 0, all logs will be written to one
// log file.
// NOTE: not for WALs
size_t max_log_file_size = 0;
// Time for the info log file to roll (in seconds).
// If specified with non-zero value, log file will be rolled
// if it has been active longer than `log_file_time_to_roll`.
// Default: 0 (disabled)
// NOTE: not for WALs
size_t log_file_time_to_roll = 0;
// Maximal info log files to be kept.
// Default: 1000
// NOTE: not for WALs
size_t keep_log_file_num = 1000;
// Recycle log files.
// If non-zero, we will reuse previously written log files for new
// logs, overwriting the old data. The value indicates how many
// Recycle WAL files.
// If non-zero, we will reuse previously written WAL files for new
// WALs, overwriting the old data. The value indicates how many
// such files we will keep around at any point in time for later
// use. This is more efficient because the blocks are already
// allocated and fdatasync does not need to update the inode after
@@ -1297,14 +1310,6 @@ struct DBOptions {
// currently.
WalFilter* wal_filter = nullptr;
// DEPRECATED: This option might be removed in a future release.
//
// If true, then DB::Open, CreateColumnFamily, DropColumnFamily, and
// SetOptions will fail if options file is not properly persisted.
//
// DEFAULT: true
bool fail_if_options_file_error = true;
// If true, then print malloc stats together with rocksdb.stats
// when printing to LOG.
// DEFAULT: false
@@ -1423,9 +1428,10 @@ struct DBOptions {
// prefix_same_as_start=true can take advantage of prefix seek optimizations.
bool prefix_seek_opt_in_only = false;
// The number of bytes to prefetch when reading the log. This is mostly useful
// for reading a remotely located log, as it can save the number of
// round-trips. If 0, then the prefetching is disabled.
// The number of bytes to prefetch when reading the DB manifest and WAL files
// during DB::Open (and variants). This is mostly useful for reading a
// remotely located log, as it can save the number of round-trips. If 0, then
// the prefetching is disabled.
//
// Default: 0
size_t log_readahead_size = 0;
@@ -1703,6 +1709,50 @@ enum ReadTier {
kMemtableTier = 0x3 // data in memtable. used for memtable-only iterators.
};
// A range of keys. In case of user_defined timestamp, if enabled, `start` and
// `limit` should point to key without timestamp part.
struct Range {
Slice start;
Slice limit;
Range() {}
Range(const Slice& s, const Slice& l) : start(s), limit(l) {}
};
// A key range with optional endpoints. In case of user_defined timestamp, if
// enabled, `start` and `limit` should point to key without timestamp part.
struct RangeOpt {
// When start.has_value() == false, refers to starting before every key
OptSlice start;
// When limit.has_value() == false, refers to ending after every key
OptSlice limit;
RangeOpt() {}
RangeOpt(const OptSlice& s, const OptSlice& l) : start(s), limit(l) {}
};
// EXPERIMENTAL
//
// Options for a RocksDB scan request. Only forward scans for now.
// We may add other options such as prefix scan in the future.
struct ScanOptions {
// The scan range. Mandatory for start to be set, limit is optional
RangeOpt range;
// A map of name,value pairs that can be passed by the user to an
// external table reader. This is completely opaque to RocksDB and is
// ignored by the natively supported table readers like block based and plain
// table. This is only useful for Iterator.
std::optional<std::unordered_map<std::string, std::string>> property_bag;
// An unbounded scan with a start key
explicit ScanOptions(const Slice& _start) : range(_start, OptSlice()) {}
// A bounded scan with a start key and upper bound
ScanOptions(const Slice& _start, const Slice& _upper_bound)
: range(_start, _upper_bound) {}
};
// Options that control read operations
struct ReadOptions {
// *** BEGIN options relevant to point lookups as well as scans ***
@@ -1784,6 +1834,10 @@ struct ReadOptions {
// block cache.
bool fill_cache = true;
// DEPRECATED: This option might be removed in a future release.
// There should be no noticeable performance difference whether this option
// is turned on or off when a DB does not use DeleteRange().
//
// If true, range tombstones handling will be skipped in key lookup paths.
// For DB instances that don't use DeleteRange() calls, this setting can
// be used to optimize the read performance.
@@ -1996,22 +2050,6 @@ struct ReadOptions {
// EXPERIMENTAL
Env::IOActivity io_activity = Env::IOActivity::kUnknown;
// EXPERIMENTAL
// An optional weight of values to be returned by a scan. Once the
// weight is reached or exceeded the scan is terminated (i.e Next()
// invalidates the iterator). In the case of a DB with one of the built-in
// table formats, such as BlockBasedTable, the weight is simply the number
// of key-value pairs. In the case of an ExternalTableReader, the weight is
// passed through to the table reader and the interpretation is upto the
// reader implementation.
uint64_t weight = 0;
// A map of name,value pairs that can be passed by the user to an
// external table reader. This is completely opaque to RocksDB and is
// ignored by the natively supported table readers like block based and plain
// table. This is only useful for Iterator.
std::optional<std::unordered_map<std::string, std::string>> property_bag;
// *** END options for RocksDB internal use only ***
ReadOptions() {}
@@ -2245,29 +2283,6 @@ struct CompactRangeOptions {
double blob_garbage_collection_age_cutoff = -1;
};
// A range of keys. In case of user_defined timestamp, if enabled, `start` and
// `limit` should point to key without timestamp part.
struct Range {
Slice start;
Slice limit;
Range() {}
Range(const Slice& s, const Slice& l) : start(s), limit(l) {}
};
// A key range with optional endpoints. In case of user_defined timestamp, if
// enabled, `start` and `limit` should point to key without timestamp part.
struct RangeOpt {
// When start.has_value() == false, refers to starting before every key
OptSlice start;
// When limit.has_value() == false, refers to ending after every key
OptSlice limit;
RangeOpt() {}
RangeOpt(const OptSlice& s, const OptSlice& l) : start(s), limit(l) {}
// RangeOpt(const Slice& s, const Slice& l) : start(s), limit(l) {}
};
// IngestExternalFileOptions is used by IngestExternalFile()
struct IngestExternalFileOptions {
// Can be set to true to move the files instead of copying them.
@@ -2500,10 +2515,16 @@ struct CompactionServiceOptionsOverride {
// to set it here.
std::shared_ptr<Statistics> statistics = nullptr;
// Info Log. If not overriden, default one will be used.
std::shared_ptr<Logger> info_log = nullptr;
// Only compaction generated SST files use this user defined table properties
// collector.
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
table_properties_collector_factories;
// All other options to override. Unknown options will be ignored.
std::unordered_map<std::string, std::string> options_map;
};
struct OpenAndCompactOptions {
+8 -3
View File
@@ -520,10 +520,11 @@ enum Tickers : uint32_t {
// Number of bytes prefetched during user initiated scan
PREFETCH_BYTES,
// Number of prefetched bytes that were actually useful
// Number of prefetched bytes that were actually useful during user initiated
// scan
PREFETCH_BYTES_USEFUL,
// Number of FS reads avoided due to scan prefetching
// Number of FS reads avoided due to prefetching during user initiated scan
PREFETCH_HITS,
// Footer corruption detected when opening an SST file for reading
@@ -657,13 +658,17 @@ enum Histograms : uint32_t {
ASYNC_READ_BYTES,
POLL_WAIT_MICROS,
// Number of bytes for RocksDB's prefetching (as opposed to file
// system's prefetch) on SST file during compaction read
COMPACTION_PREFETCH_BYTES,
// Number of prefetched bytes discarded by RocksDB.
PREFETCHED_BYTES_DISCARDED,
// Wait time for aborting async read in FilePrefetchBuffer destructor
ASYNC_PREFETCH_ABORT_MICROS,
// Number of bytes read for RocksDB's prefetching contents (as opposed to file
// Number of bytes for RocksDB's prefetching contents (as opposed to file
// system's prefetch) from the end of SST table during block based table open
TABLE_OPEN_PREFETCH_TAIL_READ_BYTES,
+14 -2
View File
@@ -289,6 +289,13 @@ class StackableDB : public DB {
return db_->NewAttributeGroupIterator(options, column_families);
}
using DB::NewMultiScan;
std::unique_ptr<MultiScan> NewMultiScan(
const ReadOptions& opts, ColumnFamilyHandle* column_family,
const std::vector<ScanOptions>& scan_opts) override {
return db_->NewMultiScan(opts, column_family, scan_opts);
}
const Snapshot* GetSnapshot() override { return db_->GetSnapshot(); }
void ReleaseSnapshot(const Snapshot* snapshot) override {
@@ -505,13 +512,18 @@ class StackableDB : public DB {
return db_->GetFullHistoryTsLow(column_family, ts_low);
}
Status GetNewestUserDefinedTimestamp(ColumnFamilyHandle* column_family,
std::string* newest_timestamp) override {
return db_->GetNewestUserDefinedTimestamp(column_family, newest_timestamp);
}
Status GetSortedWalFiles(VectorWalPtr& files) override {
return db_->GetSortedWalFiles(files);
}
Status GetCurrentWalFile(
std::unique_ptr<WalFile>* current_log_file) override {
return db_->GetCurrentWalFile(current_log_file);
std::unique_ptr<WalFile>* current_wal_file) override {
return db_->GetCurrentWalFile(current_wal_file);
}
Status GetCreationTimeOfOldestFile(uint64_t* creation_time) override {
+12 -3
View File
@@ -24,9 +24,16 @@ class SecondaryIndex;
class TransactionDBMutexFactory;
enum TxnDBWritePolicy {
WRITE_COMMITTED = 0, // write only the committed data
WRITE_PREPARED, // write data after the prepare phase of 2pc
WRITE_UNPREPARED // write data before the prepare phase of 2pc
// Write data at transaction commit time
WRITE_COMMITTED = 0,
// EXPERIMENTAL: The remaining write policies are not as mature, well
// validated, nor as compatible with other features as WRITE_COMMITTED.
// Write data after the prepare phase of 2pc
WRITE_PREPARED,
// Write data before the prepare phase of 2pc
WRITE_UNPREPARED
};
constexpr uint32_t kInitialMaxDeadlocks = 5;
@@ -361,6 +368,8 @@ struct TransactionOptions {
// Only supports write-committed policy. If set to true, the transaction will
// skip memtable write and ingest into the DB directly during Commit(). This
// makes Commit() much faster for transactions with many operations.
// Transaction neeeds to call Prepare() before Commit() for this option to
// take effect.
// Transactions with Merge() or PutEntity() is not supported yet.
//
// Note that the transaction will be ingested as an immutable memtable for
@@ -90,6 +90,8 @@ class WBWIIterator {
// Returns n where the current entry is the n-th update to the current key.
// The update count starts from 1.
// Only valid if WBWI is created with overwrite_key = true.
// With overwrite_key=false, update count for each entry is not maintained,
// see UpdateExistingEntryWithCfId().
virtual uint32_t GetUpdateCount() const { return 0; }
};
@@ -374,9 +376,6 @@ class WriteBatchWithIndex : public WriteBatchBase {
uint32_t entry_count = 0;
uint32_t overwritten_sd_count = 0;
};
// Will track CF ID, per CF entry count and overwritten sd count.
// Should be enabled when WBWI is empty for correct tracking.
void SetTrackPerCFStat(bool track);
const std::unordered_map<uint32_t, CFStat>& GetCFStats() const;
bool GetOverwriteKey() const;
+1 -1
View File
@@ -13,7 +13,7 @@
// minor or major version number planned for release.
#define ROCKSDB_MAJOR 10
#define ROCKSDB_MINOR 2
#define ROCKSDB_PATCH 0
#define ROCKSDB_PATCH 1
// Do not use these. We made the mistake of declaring macros starting with
// double underscore. Now we have to live with our choice. We'll deprecate these
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.4)
cmake_minimum_required(VERSION 3.11)
set(JAVA_JUNIT_VERSION "4.13.1")
set(JAVA_HAMCR_VERSION "2.2")
-91
View File
@@ -2055,29 +2055,6 @@ void Java_org_rocksdb_Options_setWalFilter(JNIEnv*, jclass, jlong jhandle,
opt->wal_filter = wal_filter;
}
/*
* Class: org_rocksdb_Options
* Method: setFailIfOptionsFileError
* Signature: (JZ)V
*/
void Java_org_rocksdb_Options_setFailIfOptionsFileError(
JNIEnv*, jclass, jlong jhandle, jboolean jfail_if_options_file_error) {
auto* opt = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle);
opt->fail_if_options_file_error =
static_cast<bool>(jfail_if_options_file_error);
}
/*
* Class: org_rocksdb_Options
* Method: failIfOptionsFileError
* Signature: (J)Z
*/
jboolean Java_org_rocksdb_Options_failIfOptionsFileError(JNIEnv*, jclass,
jlong jhandle) {
auto* opt = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle);
return static_cast<jboolean>(opt->fail_if_options_file_error);
}
/*
* Class: org_rocksdb_Options
* Method: setDumpMallocStats
@@ -2456,28 +2433,6 @@ void Java_org_rocksdb_Options_setMinWriteBufferNumberToMerge(
->min_write_buffer_number_to_merge =
static_cast<int>(jmin_write_buffer_number_to_merge);
}
/*
* Class: org_rocksdb_Options
* Method: maxWriteBufferNumberToMaintain
* Signature: (J)I
*/
jint Java_org_rocksdb_Options_maxWriteBufferNumberToMaintain(JNIEnv*, jclass,
jlong jhandle) {
return reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle)
->max_write_buffer_number_to_maintain;
}
/*
* Class: org_rocksdb_Options
* Method: setMaxWriteBufferNumberToMaintain
* Signature: (JI)V
*/
void Java_org_rocksdb_Options_setMaxWriteBufferNumberToMaintain(
JNIEnv*, jclass, jlong jhandle, jint jmax_write_buffer_number_to_maintain) {
reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle)
->max_write_buffer_number_to_maintain =
static_cast<int>(jmax_write_buffer_number_to_maintain);
}
/*
* Class: org_rocksdb_Options
@@ -4496,29 +4451,6 @@ void Java_org_rocksdb_ColumnFamilyOptions_setMinWriteBufferNumberToMerge(
static_cast<int>(jmin_write_buffer_number_to_merge);
}
/*
* Class: org_rocksdb_ColumnFamilyOptions
* Method: maxWriteBufferNumberToMaintain
* Signature: (J)I
*/
jint Java_org_rocksdb_ColumnFamilyOptions_maxWriteBufferNumberToMaintain(
JNIEnv*, jclass, jlong jhandle) {
return reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyOptions*>(jhandle)
->max_write_buffer_number_to_maintain;
}
/*
* Class: org_rocksdb_ColumnFamilyOptions
* Method: setMaxWriteBufferNumberToMaintain
* Signature: (JI)V
*/
void Java_org_rocksdb_ColumnFamilyOptions_setMaxWriteBufferNumberToMaintain(
JNIEnv*, jclass, jlong jhandle, jint jmax_write_buffer_number_to_maintain) {
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyOptions*>(jhandle)
->max_write_buffer_number_to_maintain =
static_cast<int>(jmax_write_buffer_number_to_maintain);
}
/*
* Class: org_rocksdb_ColumnFamilyOptions
* Method: setCompressionType
@@ -7524,29 +7456,6 @@ void Java_org_rocksdb_DBOptions_setWalFilter(JNIEnv*, jclass, jlong jhandle,
opt->wal_filter = wal_filter;
}
/*
* Class: org_rocksdb_DBOptions
* Method: setFailIfOptionsFileError
* Signature: (JZ)V
*/
void Java_org_rocksdb_DBOptions_setFailIfOptionsFileError(
JNIEnv*, jclass, jlong jhandle, jboolean jfail_if_options_file_error) {
auto* opt = reinterpret_cast<ROCKSDB_NAMESPACE::DBOptions*>(jhandle);
opt->fail_if_options_file_error =
static_cast<bool>(jfail_if_options_file_error);
}
/*
* Class: org_rocksdb_DBOptions
* Method: failIfOptionsFileError
* Signature: (J)Z
*/
jboolean Java_org_rocksdb_DBOptions_failIfOptionsFileError(JNIEnv*, jclass,
jlong jhandle) {
auto* opt = reinterpret_cast<ROCKSDB_NAMESPACE::DBOptions*>(jhandle);
return static_cast<jboolean>(opt->fail_if_options_file_error);
}
/*
* Class: org_rocksdb_DBOptions
* Method: setDumpMallocStats
+8 -2
View File
@@ -5889,8 +5889,11 @@ class HistogramTypeJni {
return 0x3C;
case ROCKSDB_NAMESPACE::Histograms::TABLE_OPEN_PREFETCH_TAIL_READ_BYTES:
return 0x3D;
case ROCKSDB_NAMESPACE::Histograms::COMPACTION_PREFETCH_BYTES:
return 0x3F;
case ROCKSDB_NAMESPACE::Histograms::HISTOGRAM_ENUM_MAX:
// 0x3D for backwards compatibility on current minor version.
// 0x3E is reserved for backwards compatibility on current minor
// version.
return 0x3E;
default:
// undefined/default
@@ -6033,8 +6036,11 @@ class HistogramTypeJni {
case 0x3D:
return ROCKSDB_NAMESPACE::Histograms::
TABLE_OPEN_PREFETCH_TAIL_READ_BYTES;
case 0x3F:
return ROCKSDB_NAMESPACE::Histograms::COMPACTION_PREFETCH_BYTES;
case 0x3E:
// 0x1F for backwards compatibility on current minor version.
// 0x3E is reserved for backwards compatibility on current minor
// version.
return ROCKSDB_NAMESPACE::Histograms::HISTOGRAM_ENUM_MAX;
default:
@@ -44,53 +44,6 @@ public interface AdvancedColumnFamilyOptionsInterface<
*/
int minWriteBufferNumberToMerge();
/**
* The total maximum number of write buffers to maintain in memory including
* copies of buffers that have already been flushed. Unlike
* {@link AdvancedMutableColumnFamilyOptionsInterface#maxWriteBufferNumber()},
* this parameter does not affect flushing.
* This controls the minimum amount of write history that will be available
* in memory for conflict checking when Transactions are used.
* <p>
* When using an OptimisticTransactionDB:
* If this value is too low, some transactions may fail at commit time due
* to not being able to determine whether there were any write conflicts.
* <p>
* When using a TransactionDB:
* If Transaction::SetSnapshot is used, TransactionDB will read either
* in-memory write buffers or SST files to do write-conflict checking.
* Increasing this value can reduce the number of reads to SST files
* done for conflict detection.
* <p>
* Setting this value to 0 will cause write buffers to be freed immediately
* after they are flushed.
* If this value is set to -1,
* {@link AdvancedMutableColumnFamilyOptionsInterface#maxWriteBufferNumber()}
* will be used.
* <p>
* Default:
* If using a TransactionDB/OptimisticTransactionDB, the default value will
* be set to the value of
* {@link AdvancedMutableColumnFamilyOptionsInterface#maxWriteBufferNumber()}
* if it is not explicitly set by the user. Otherwise, the default is 0.
*
* @param maxWriteBufferNumberToMaintain The maximum number of write
* buffers to maintain
*
* @return the reference to the current options.
*/
T setMaxWriteBufferNumberToMaintain(
int maxWriteBufferNumberToMaintain);
/**
* The total maximum number of write buffers to maintain in memory including
* copies of buffers that have already been flushed.
*
* @return maxWriteBufferNumberToMaintain The maximum number of write buffers
* to maintain
*/
int maxWriteBufferNumberToMaintain();
/**
* Allows thread-safe inplace updates.
* If inplace_callback function is not set,
@@ -835,19 +835,6 @@ public class ColumnFamilyOptions
return paranoidFileChecks(nativeHandle_);
}
@Override
public ColumnFamilyOptions setMaxWriteBufferNumberToMaintain(
final int maxWriteBufferNumberToMaintain) {
setMaxWriteBufferNumberToMaintain(
nativeHandle_, maxWriteBufferNumberToMaintain);
return this;
}
@Override
public int maxWriteBufferNumberToMaintain() {
return maxWriteBufferNumberToMaintain(nativeHandle_);
}
@Override
public ColumnFamilyOptions setCompactionPriority(
final CompactionPriority compactionPriority) {
@@ -1467,9 +1454,6 @@ public class ColumnFamilyOptions
private static native int[] maxBytesForLevelMultiplierAdditional(long handle);
private static native void setParanoidFileChecks(long handle, boolean paranoidFileChecks);
private static native boolean paranoidFileChecks(long handle);
private static native void setMaxWriteBufferNumberToMaintain(
final long handle, final int maxWriteBufferNumberToMaintain);
private static native int maxWriteBufferNumberToMaintain(final long handle);
private static native void setCompactionPriority(
final long handle, final byte compactionPriority);
private static native byte compactionPriority(final long handle);
@@ -210,7 +210,9 @@ public enum HistogramType {
*/
TABLE_OPEN_PREFETCH_TAIL_READ_BYTES((byte) 0x3D),
// 0x3E for backwards compatibility on current minor version.
COMPACTION_PREFETCH_BYTES((byte) 0x3F),
// 0x3E is reserved for backwards compatibility on current minor version.
HISTOGRAM_ENUM_MAX((byte) 0x3E);
private final byte value;
@@ -1762,19 +1762,6 @@ public class Options extends RocksObject
return paranoidFileChecks(nativeHandle_);
}
@Override
public Options setMaxWriteBufferNumberToMaintain(
final int maxWriteBufferNumberToMaintain) {
setMaxWriteBufferNumberToMaintain(
nativeHandle_, maxWriteBufferNumberToMaintain);
return this;
}
@Override
public int maxWriteBufferNumberToMaintain() {
return maxWriteBufferNumberToMaintain(nativeHandle_);
}
@Override
public Options setCompactionPriority(
final CompactionPriority compactionPriority) {
@@ -2443,9 +2430,6 @@ public class Options extends RocksObject
private static native int[] maxBytesForLevelMultiplierAdditional(long handle);
private static native void setParanoidFileChecks(long handle, boolean paranoidFileChecks);
private static native boolean paranoidFileChecks(long handle);
private static native void setMaxWriteBufferNumberToMaintain(
final long handle, final int maxWriteBufferNumberToMaintain);
private static native int maxWriteBufferNumberToMaintain(final long handle);
private static native void setCompactionPriority(
final long handle, final byte compactionPriority);
private static native byte compactionPriority(final long handle);
@@ -398,7 +398,10 @@ public class ReadOptions extends RocksObject {
* Default: false
*
* @return true if keys deleted using the DeleteRange() API will be visible
*
* @deprecated This option may be remove in a future release.
*/
@Deprecated
public boolean ignoreRangeDeletions() {
assert(isOwningHandle());
return ignoreRangeDeletions(nativeHandle_);
@@ -414,7 +417,10 @@ public class ReadOptions extends RocksObject {
* @param ignoreRangeDeletions true if keys deleted using the DeleteRange()
* API should be visible
* @return the reference to the current ReadOptions.
*
* @deprecated This option may be remove in a future release.
*/
@Deprecated
public ReadOptions setIgnoreRangeDeletions(final boolean ignoreRangeDeletions) {
assert(isOwningHandle());
setIgnoreRangeDeletions(nativeHandle_, ignoreRangeDeletions);
+10 -4
View File
@@ -4126,6 +4126,7 @@ public class RocksDB extends RocksObject {
*
* @return the maximum level
*/
@Deprecated
public int maxMemCompactionLevel() {
return maxMemCompactionLevel(null);
}
@@ -4633,10 +4634,13 @@ public class RocksDB extends RocksObject {
* @param targetLevel the target level for L0
*
* @throws RocksDBException if an error occurs whilst promoting L0
*
* @deprecated this API may be removed in a future release.
*/
@Deprecated
public void promoteL0(
/* @Nullable */final ColumnFamilyHandle columnFamilyHandle,
final int targetLevel) throws RocksDBException {
/* @Nullable */ final ColumnFamilyHandle columnFamilyHandle, final int targetLevel)
throws RocksDBException {
promoteL0(nativeHandle_,
columnFamilyHandle == null ? 0 : columnFamilyHandle.nativeHandle_,
targetLevel);
@@ -4648,9 +4652,11 @@ public class RocksDB extends RocksObject {
* @param targetLevel the target level for L0
*
* @throws RocksDBException if an error occurs whilst promoting L0
*
* @deprecated this API may be removed in a future release.
*/
public void promoteL0(final int targetLevel)
throws RocksDBException {
@Deprecated
public void promoteL0(final int targetLevel) throws RocksDBException {
promoteL0(null, targetLevel);
}
+26 -51
View File
@@ -203,7 +203,7 @@ public class Transaction extends RocksObject {
* Status::Busy() may be returned if the transaction could not guarantee
* that there are no write conflicts. Status::TryAgain() may be returned
* if the memtable history size is not large enough
* (See max_write_buffer_number_to_maintain).
* (See max_write_buffer_size_to_maintain).
* <p>
* If this transaction was created by a {@link TransactionDB},
* Status::Expired() may be returned if this transaction has lived for
@@ -689,8 +689,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
* {@link Status.Code#MergeInProgress} if merge operations cannot be
* resolved.
*
@@ -770,8 +769,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
* {@link Status.Code#MergeInProgress} if merge operations cannot be
* resolved.
*
@@ -821,8 +819,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
* {@link Status.Code#MergeInProgress} if merge operations cannot be
* resolved.
*
@@ -880,8 +877,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
* {@link Status.Code#MergeInProgress} if merge operations cannot be
* resolved.
*
@@ -933,8 +929,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
* {@link Status.Code#MergeInProgress} if merge operations cannot be
* resolved.
*
@@ -988,8 +983,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
* {@link Status.Code#MergeInProgress} if merge operations cannot be
* resolved.
*
@@ -1050,8 +1044,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
* {@link Status.Code#MergeInProgress} if merge operations cannot be
* resolved.
*
@@ -1106,8 +1099,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
* {@link Status.Code#MergeInProgress} if merge operations cannot be
* resolved.
*
@@ -1393,8 +1385,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to put the key/value into
* @param key the specified key to be inserted.
@@ -1430,8 +1421,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to put the key/value into
* @param key the specified key to be inserted.
@@ -1460,8 +1450,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param key the specified key to be inserted.
* @param value the value associated with the specified key.
@@ -1536,8 +1525,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param key the specified key to be inserted.
* @param value the value associated with the specified key.
@@ -1575,8 +1563,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to put the key/value into
* @param key the specified key to be inserted.
@@ -1645,8 +1632,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to merge the key/value into
* @param key the specified key to be merged.
@@ -1683,8 +1669,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to merge the key/value into
* @param key the specified key to be merged.
@@ -1713,8 +1698,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param key the specified key to be merged.
* @param value the value associated with the specified key.
@@ -1741,8 +1725,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param key the specified key to be merged.
* @param value the value associated with the specified key.
@@ -1778,8 +1761,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle in which to apply the merge
* @param key the specified key to be merged.
@@ -1821,8 +1803,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle in which to apply the merge
* @param key the specified key to be merged.
@@ -1849,8 +1830,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to delete the key/value from
* @param key the specified key to be deleted.
@@ -1885,8 +1865,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to delete the key/value from
* @param key the specified key to be deleted.
@@ -1914,8 +1893,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param key the specified key to be deleted.
*
@@ -2001,8 +1979,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to delete the key/value from
* @param key the specified key to be deleted.
@@ -2038,8 +2015,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param columnFamilyHandle The column family to delete the key/value from
* @param key the specified key to be deleted.
@@ -2068,8 +2044,7 @@ public class Transaction extends RocksObject {
* {@link Status.Code#Busy} if there is a write conflict,
* {@link Status.Code#TimedOut} if a lock could not be acquired,
* {@link Status.Code#TryAgain} if the memtable history size is not large
* enough. See
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
* enough.
*
* @param key the specified key to be deleted.
*
@@ -561,19 +561,6 @@ public class ColumnFamilyOptionsTest {
}
}
@Test
public void maxWriteBufferNumberToMaintain() {
try (final ColumnFamilyOptions opt = new ColumnFamilyOptions()) {
int intValue = rand.nextInt();
// Size has to be positive
intValue = (intValue < 0) ? -intValue : intValue;
intValue = (intValue == 0) ? intValue + 1 : intValue;
opt.setMaxWriteBufferNumberToMaintain(intValue);
assertThat(opt.maxWriteBufferNumberToMaintain()).
isEqualTo(intValue);
}
}
@Test
public void compactionPriorities() {
try (final ColumnFamilyOptions opt = new ColumnFamilyOptions()) {
@@ -656,15 +656,6 @@ public class DBOptionsTest {
}
}
@Test
public void failIfOptionsFileError() {
try (final DBOptions opt = new DBOptions()) {
final boolean boolValue = rand.nextBoolean();
opt.setFailIfOptionsFileError(boolValue);
assertThat(opt.failIfOptionsFileError()).isEqualTo(boolValue);
}
}
@Test
public void dumpMallocStats() {
try (final DBOptions opt = new DBOptions()) {
@@ -96,30 +96,54 @@ public class MutableColumnFamilyOptionsTest {
public void mutableColumnFamilyOptions_parse_getOptions_output() {
final String optionsString =
"bottommost_compression=kDisableCompressionOption; sample_for_compression=0; "
+ "blob_garbage_collection_age_cutoff=0.250000; blob_garbage_collection_force_threshold=0.800000;"
+ "arena_block_size=1048576; enable_blob_garbage_collection=false; level0_stop_writes_trigger=36; min_blob_size=65536;"
+ "blob_compaction_readahead_size=262144; blob_file_starting_level=5; prepopulate_blob_cache=kDisable;"
+ "compaction_options_universal={allow_trivial_move=false;stop_style=kCompactionStopStyleTotalSize;min_merge_width=2;"
+ "compression_size_percent=-1;max_size_amplification_percent=200;max_merge_width=4294967295;size_ratio=1;}; "
+ "target_file_size_base=67108864; max_bytes_for_level_base=268435456; memtable_whole_key_filtering=false; "
+ "soft_pending_compaction_bytes_limit=68719476736; blob_compression_type=kNoCompression; max_write_buffer_number=2; "
+ "ttl=2592000; compaction_options_fifo={allow_compaction=false;age_for_warm=0;max_table_files_size=1073741824;}; "
+ "check_flush_compaction_key_order=true; max_successive_merges=0; inplace_update_num_locks=10000; "
+ "bottommost_compression_opts={enabled=false;parallel_threads=1;zstd_max_train_bytes=0;max_dict_bytes=0;"
+ "blob_garbage_collection_age_cutoff=0.250000; "
+ "blob_garbage_collection_force_threshold=0.800000;"
+ "arena_block_size=1048576; enable_blob_garbage_collection=false; "
+ "level0_stop_writes_trigger=36; min_blob_size=65536;"
+ "blob_compaction_readahead_size=262144; blob_file_starting_level=5; "
+ "prepopulate_blob_cache=kDisable;"
+ "compaction_options_universal={allow_trivial_move=false;stop_style="
+ "kCompactionStopStyleTotalSize;min_merge_width=2;"
+ "compression_size_percent=-1;max_size_amplification_percent=200;max_merge_width="
+ "4294967295;size_ratio=1;}; "
+ "target_file_size_base=67108864; max_bytes_for_level_base=268435456; "
+ "memtable_whole_key_filtering=false; "
+ "soft_pending_compaction_bytes_limit=68719476736; blob_compression_type=kNoCompression; "
+ " max_write_buffer_number=2; "
+ "ttl=2592000; "
+ "compaction_options_fifo={allow_compaction=false;age_for_warm=0;max_table_files_size="
+ "1073741824;}; "
+ "check_flush_compaction_key_order=true; max_successive_merges=0; "
+ "inplace_update_num_locks=10000; "
+ "bottommost_compression_opts={enabled=false;parallel_threads=1;zstd_max_train_bytes=0;"
+ "max_dict_bytes=0;"
+ "strategy=0;max_dict_buffer_bytes=0;level=32767;window_bits=-14;}; "
+ "target_file_size_multiplier=1; max_bytes_for_level_multiplier_additional=5:{7}:{9}:{11}:{13}:{15}:{17}; "
+ "enable_blob_files=true; level0_slowdown_writes_trigger=20; compression=kLZ4HCCompression; level0_file_num_compaction_trigger=4; "
+ "blob_file_size=268435456; prefix_extractor=nullptr; max_bytes_for_level_multiplier=10.000000; write_buffer_size=67108864; "
+ "disable_auto_compactions=false; max_compaction_bytes=1677721600; memtable_huge_page_size=0; "
+ "compression_opts={enabled=false;parallel_threads=1;zstd_max_train_bytes=0;max_dict_bytes=0;strategy=0;max_dict_buffer_bytes=0;"
+ "target_file_size_multiplier=1; "
+ "max_bytes_for_level_multiplier_additional=5:{7}:{9}:{11}:{13}:{15}:{17}; "
+ "enable_blob_files=true; level0_slowdown_writes_trigger=20; "
+ "compression=kLZ4HCCompression; level0_file_num_compaction_trigger=4; "
+ "blob_file_size=268435456; prefix_extractor=nullptr; "
+ "max_bytes_for_level_multiplier=10.000000; write_buffer_size=67108864; "
+ "disable_auto_compactions=false; max_compaction_bytes=1677721600; "
+ "memtable_huge_page_size=0; "
+ "compression_opts={enabled=false;parallel_threads=1;zstd_max_train_bytes=0;max_dict_"
+ "bytes=0;strategy=0;max_dict_buffer_bytes=0;"
+ "level=32767;window_bits=-14;}; "
+ "hard_pending_compaction_bytes_limit=274877906944; periodic_compaction_seconds=0; paranoid_file_checks=true; "
+ "memtable_prefix_bloom_size_ratio=7.500000; max_sequential_skip_in_iterations=8; report_bg_io_stats=true; "
+ "compaction_pri=kMinOverlappingRatio; compaction_style=kCompactionStyleLevel; memtable_factory=SkipListFactory; "
+ "comparator=leveldb.BytewiseComparator; bloom_locality=0; compaction_filter_factory=nullptr; "
+ "min_write_buffer_number_to_merge=1; max_write_buffer_number_to_maintain=0; compaction_filter=nullptr; merge_operator=nullptr; "
+ "num_levels=7; optimize_filters_for_hits=false; force_consistency_checks=true; table_factory=BlockBasedTable; "
+ "max_write_buffer_size_to_maintain=0; memtable_insert_with_hint_prefix_extractor=nullptr; level_compaction_dynamic_level_bytes=false; "
+ "hard_pending_compaction_bytes_limit=274877906944; periodic_compaction_seconds=0; "
+ "paranoid_file_checks=true; "
+ "memtable_prefix_bloom_size_ratio=7.500000; max_sequential_skip_in_iterations=8; "
+ "report_bg_io_stats=true; "
+ "compaction_pri=kMinOverlappingRatio; compaction_style=kCompactionStyleLevel; "
+ "memtable_factory=SkipListFactory; "
+ "comparator=leveldb.BytewiseComparator; bloom_locality=0; "
+ "compaction_filter_factory=nullptr; "
+ "min_write_buffer_number_to_merge=1; compaction_filter=nullptr; "
+ "merge_operator=nullptr; "
+ "num_levels=7; optimize_filters_for_hits=false; force_consistency_checks=true; "
+ "table_factory=BlockBasedTable; "
+ "max_write_buffer_size_to_maintain=0; "
+ "memtable_insert_with_hint_prefix_extractor=nullptr; "
+ "level_compaction_dynamic_level_bytes=false; "
+ "inplace_update_support=false; experimental_mempurge_threshold=0.003";
final MutableColumnFamilyOptions.MutableColumnFamilyOptionsBuilder cf =
@@ -902,15 +902,6 @@ public class OptionsTest {
}
}
@Test
public void failIfOptionsFileError() {
try (final Options opt = new Options()) {
final boolean boolValue = rand.nextBoolean();
opt.setFailIfOptionsFileError(boolValue);
assertThat(opt.failIfOptionsFileError()).isEqualTo(boolValue);
}
}
@Test
public void dumpMallocStats() {
try (final Options opt = new Options()) {
@@ -1201,19 +1192,6 @@ public class OptionsTest {
}
}
@Test
public void maxWriteBufferNumberToMaintain() {
try (final Options options = new Options()) {
int intValue = rand.nextInt();
// Size has to be positive
intValue = (intValue < 0) ? -intValue : intValue;
intValue = (intValue == 0) ? intValue + 1 : intValue;
options.setMaxWriteBufferNumberToMaintain(intValue);
assertThat(options.maxWriteBufferNumberToMaintain()).
isEqualTo(intValue);
}
}
@Test
public void compactionPriorities() {
try (final Options options = new Options()) {
@@ -1563,16 +1563,6 @@ public class RocksDBTest {
}
}
@Test
public void maxMemCompactionLevel() throws RocksDBException {
try (final Options options = new Options().setCreateIfMissing(true)) {
final String dbPath = dbFolder.getRoot().getAbsolutePath();
try (final RocksDB db = RocksDB.open(options, dbPath)) {
assertThat(db.maxMemCompactionLevel()).isEqualTo(0);
}
}
}
@Test
public void level0StopWriteTrigger() throws RocksDBException {
try (final Options options = new Options().setCreateIfMissing(true)) {
+1 -1
View File
@@ -235,7 +235,7 @@ class WBWIMemTable final : public ReadOnlyMemTable {
uint64_t num_entries_;
// WBWI can contains updates to multiple CFs. `cf_id_` determines which CF
// this memtable is for.
uint32_t cf_id_;
const uint32_t cf_id_;
};
class WBWIMemTableIterator final : public InternalIterator {
+1
View File
@@ -336,6 +336,7 @@ const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
"rocksdb.error.handler.autoresume.retry.count"},
{ASYNC_READ_BYTES, "rocksdb.async.read.bytes"},
{POLL_WAIT_MICROS, "rocksdb.poll.wait.micros"},
{COMPACTION_PREFETCH_BYTES, "rocksdb.compaction.prefetch.bytes"},
{PREFETCHED_BYTES_DISCARDED, "rocksdb.prefetched.bytes.discarded"},
{ASYNC_PREFETCH_ABORT_MICROS, "rocksdb.async.prefetch.abort.micros"},
{TABLE_OPEN_PREFETCH_TAIL_READ_BYTES,
+1 -1
View File
@@ -616,7 +616,7 @@ TEST_F(StatsHistoryTest, ForceManualFlushStatsCF) {
// LogNumbers: default: 16, stats: 10, pikachu: 5
// Since in recovery process, cfd_stats column is created after WAL is
// created, synced and MANIFEST is persisted, its log number which depends on
// logfile_number_ will be different. Since "pikachu" is never flushed, thus
// cur_wal_number_ will be different. Since "pikachu" is never flushed, thus
// its log_number should be the smallest of the three.
ASSERT_OK(Flush());
ASSERT_LT(cfd_test->GetLogNumber(), cfd_stats->GetLogNumber());
+7 -6
View File
@@ -694,7 +694,10 @@ static std::unordered_map<std::string, OptionTypeInfo>
{offsetof(struct MutableCFOptions, memtable_max_range_deletions),
OptionType::kUInt32T, OptionVerificationType::kNormal,
OptionTypeFlags::kMutable}},
{"memtable_op_scan_flush_trigger",
{offsetof(struct MutableCFOptions, memtable_op_scan_flush_trigger),
OptionType::kUInt32T, OptionVerificationType::kNormal,
OptionTypeFlags::kMutable}},
};
static std::unordered_map<std::string, OptionTypeInfo>
@@ -749,9 +752,7 @@ static std::unordered_map<std::string, OptionTypeInfo>
{0, OptionType::kInt, OptionVerificationType::kDeprecated,
OptionTypeFlags::kNone}},
{"max_write_buffer_number_to_maintain",
{offsetof(struct ImmutableCFOptions,
max_write_buffer_number_to_maintain),
OptionType::kInt, OptionVerificationType::kNormal,
{0, OptionType::kInt, OptionVerificationType::kDeprecated,
OptionTypeFlags::kNone, nullptr}},
{"max_write_buffer_size_to_maintain",
{offsetof(struct ImmutableCFOptions,
@@ -987,8 +988,6 @@ ImmutableCFOptions::ImmutableCFOptions(const ColumnFamilyOptions& cf_options)
compaction_filter_factory(cf_options.compaction_filter_factory),
min_write_buffer_number_to_merge(
cf_options.min_write_buffer_number_to_merge),
max_write_buffer_number_to_maintain(
cf_options.max_write_buffer_number_to_maintain),
max_write_buffer_size_to_maintain(
cf_options.max_write_buffer_size_to_maintain),
inplace_update_support(cf_options.inplace_update_support),
@@ -1180,6 +1179,8 @@ void MutableCFOptions::Dump(Logger* log) const {
bottommost_file_compaction_delay);
ROCKS_LOG_INFO(log, " uncache_aggressiveness: %" PRIu32,
uncache_aggressiveness);
ROCKS_LOG_INFO(log, " memtable_op_scan_flush_trigger: %" PRIu32,
memtable_op_scan_flush_trigger);
// Universal Compaction Options
ROCKS_LOG_INFO(log, "compaction_options_universal.size_ratio : %d",
+5 -4
View File
@@ -40,8 +40,6 @@ struct ImmutableCFOptions {
int min_write_buffer_number_to_merge;
int max_write_buffer_number_to_maintain;
int64_t max_write_buffer_size_to_maintain;
bool inplace_update_support;
@@ -175,7 +173,8 @@ struct MutableCFOptions {
memtable_max_range_deletions(options.memtable_max_range_deletions),
bottommost_file_compaction_delay(
options.bottommost_file_compaction_delay),
uncache_aggressiveness(options.uncache_aggressiveness) {
uncache_aggressiveness(options.uncache_aggressiveness),
memtable_op_scan_flush_trigger(options.memtable_op_scan_flush_trigger) {
RefreshDerivedOptions(options.num_levels, options.compaction_style);
}
@@ -230,7 +229,8 @@ struct MutableCFOptions {
sample_for_compression(0),
memtable_max_range_deletions(0),
bottommost_file_compaction_delay(0),
uncache_aggressiveness(0) {}
uncache_aggressiveness(0),
memtable_op_scan_flush_trigger(0) {}
explicit MutableCFOptions(const Options& options);
@@ -338,6 +338,7 @@ struct MutableCFOptions {
uint32_t memtable_max_range_deletions;
uint32_t bottommost_file_compaction_delay;
uint32_t uncache_aggressiveness;
uint32_t memtable_op_scan_flush_trigger;
// Derived options
// Per-level target file size.
+1 -3
View File
@@ -319,8 +319,7 @@ static std::unordered_map<std::string, OptionTypeInfo>
OptionType::kBoolean, OptionVerificationType::kNormal,
OptionTypeFlags::kNone}},
{"fail_if_options_file_error",
{offsetof(struct ImmutableDBOptions, fail_if_options_file_error),
OptionType::kBoolean, OptionVerificationType::kNormal,
{0, OptionType::kBoolean, OptionVerificationType::kDeprecated,
OptionTypeFlags::kNone}},
{"enable_pipelined_write",
{offsetof(struct ImmutableDBOptions, enable_pipelined_write),
@@ -772,7 +771,6 @@ ImmutableDBOptions::ImmutableDBOptions(const DBOptions& options)
allow_2pc(options.allow_2pc),
row_cache(options.row_cache),
wal_filter(options.wal_filter),
fail_if_options_file_error(options.fail_if_options_file_error),
dump_malloc_stats(options.dump_malloc_stats),
avoid_flush_during_recovery(options.avoid_flush_during_recovery),
allow_ingest_behind(options.allow_ingest_behind),
-1
View File
@@ -77,7 +77,6 @@ struct ImmutableDBOptions {
bool allow_2pc;
std::shared_ptr<Cache> row_cache;
WalFilter* wal_filter;
bool fail_if_options_file_error;
bool dump_malloc_stats;
bool avoid_flush_during_recovery;
bool allow_ingest_behind;
+5 -5
View File
@@ -43,8 +43,6 @@ AdvancedColumnFamilyOptions::AdvancedColumnFamilyOptions(const Options& options)
: max_write_buffer_number(options.max_write_buffer_number),
min_write_buffer_number_to_merge(
options.min_write_buffer_number_to_merge),
max_write_buffer_number_to_maintain(
options.max_write_buffer_number_to_maintain),
max_write_buffer_size_to_maintain(
options.max_write_buffer_size_to_maintain),
inplace_update_support(options.inplace_update_support),
@@ -113,7 +111,8 @@ AdvancedColumnFamilyOptions::AdvancedColumnFamilyOptions(const Options& options)
blob_file_starting_level(options.blob_file_starting_level),
blob_cache(options.blob_cache),
prepopulate_blob_cache(options.prepopulate_blob_cache),
persist_user_defined_timestamps(options.persist_user_defined_timestamps) {
persist_user_defined_timestamps(options.persist_user_defined_timestamps),
memtable_op_scan_flush_trigger(options.memtable_op_scan_flush_trigger) {
assert(memtable_factory.get() != nullptr);
if (max_bytes_for_level_multiplier_additional.size() <
static_cast<unsigned int>(num_levels)) {
@@ -192,8 +191,6 @@ void ColumnFamilyOptions::Dump(Logger* log) const {
ROCKS_LOG_HEADER(log, " Options.num_levels: %d", num_levels);
ROCKS_LOG_HEADER(log, " Options.min_write_buffer_number_to_merge: %d",
min_write_buffer_number_to_merge);
ROCKS_LOG_HEADER(log, " Options.max_write_buffer_number_to_maintain: %d",
max_write_buffer_number_to_maintain);
ROCKS_LOG_HEADER(log,
" Options.max_write_buffer_size_to_maintain: %" PRIu64,
max_write_buffer_size_to_maintain);
@@ -287,6 +284,9 @@ void ColumnFamilyOptions::Dump(Logger* log) const {
ROCKS_LOG_HEADER(log,
" Options.max_sequential_skip_in_iterations: %" PRIu64,
max_sequential_skip_in_iterations);
ROCKS_LOG_HEADER(
log, " Options.memtable_op_scan_flush_trigger: %" PRIu32,
memtable_op_scan_flush_trigger);
ROCKS_LOG_HEADER(log,
" Options.max_compaction_bytes: %" PRIu64,
max_compaction_bytes);
+2 -4
View File
@@ -153,8 +153,6 @@ void BuildDBOptions(const ImmutableDBOptions& immutable_db_options,
options.allow_2pc = immutable_db_options.allow_2pc;
options.row_cache = immutable_db_options.row_cache;
options.wal_filter = immutable_db_options.wal_filter;
options.fail_if_options_file_error =
immutable_db_options.fail_if_options_file_error;
options.dump_malloc_stats = immutable_db_options.dump_malloc_stats;
options.avoid_flush_during_recovery =
immutable_db_options.avoid_flush_during_recovery;
@@ -301,6 +299,8 @@ void UpdateColumnFamilyOptions(const MutableCFOptions& moptions,
cf_opts->default_write_temperature = moptions.default_write_temperature;
cf_opts->memtable_max_range_deletions = moptions.memtable_max_range_deletions;
cf_opts->uncache_aggressiveness = moptions.uncache_aggressiveness;
cf_opts->memtable_op_scan_flush_trigger =
moptions.memtable_op_scan_flush_trigger;
}
void UpdateColumnFamilyOptions(const ImmutableCFOptions& ioptions,
@@ -313,8 +313,6 @@ void UpdateColumnFamilyOptions(const ImmutableCFOptions& ioptions,
cf_opts->compaction_filter_factory = ioptions.compaction_filter_factory;
cf_opts->min_write_buffer_number_to_merge =
ioptions.min_write_buffer_number_to_merge;
cf_opts->max_write_buffer_number_to_maintain =
ioptions.max_write_buffer_number_to_maintain;
cf_opts->max_write_buffer_size_to_maintain =
ioptions.max_write_buffer_size_to_maintain;
cf_opts->inplace_update_support = ioptions.inplace_update_support;
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff. Show more