mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 19:12:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01393464ea | ||
|
|
97a152b841 | ||
|
|
537ccd2fbe | ||
|
|
86e421fba9 | ||
|
|
c5201abc4a | ||
|
|
4eda1b79fa | ||
|
|
030f7c4a7b | ||
|
|
6c60c8f3f3 | ||
|
|
2660754dbc | ||
|
|
94a867c845 | ||
|
|
1b5474c8e1 | ||
|
|
b135fa7a8b |
No files matched your search
@@ -1,13 +1,13 @@
|
||||
name: facebook/rocksdb/benchmark-linux
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
# FIXME: Disabled temporarily
|
||||
# schedule:
|
||||
# - cron: 7 */2 * * * # At minute 7 past every 2nd hour
|
||||
jobs:
|
||||
# FIXME: when this job is fixed, it should be given a cron schedule like
|
||||
# schedule:
|
||||
# - cron: 0 * * * *
|
||||
# workflow_dispatch:
|
||||
benchmark-linux:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: ubuntu-latest # FIXME: change this back to self-hosted when ready
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/build-for-benchmarks"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
name: facebook/rocksdb/nightly
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
jobs:
|
||||
# These jobs would be in nightly but are failing or otherwise broken for
|
||||
# some reason.
|
||||
|
||||
@@ -3,7 +3,6 @@ on:
|
||||
schedule:
|
||||
- cron: 0 9 * * *
|
||||
workflow_dispatch:
|
||||
permissions: {}
|
||||
jobs:
|
||||
build-format-compatible:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
@@ -60,15 +59,12 @@ jobs:
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: clang-13
|
||||
CXX: clang++-13
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
|
||||
- run: CC=clang-13 CXX=clang++-13 LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-valgrind:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
@@ -80,7 +76,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: make V=1 -j32 valgrind_test
|
||||
- run: PORTABLE=1 make V=1 -j32 valgrind_test
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-windows-vs2022-avx2:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
name: facebook/rocksdb/pr-jobs-candidate
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
jobs:
|
||||
# These jobs would be in pr-jobs but are failing or otherwise broken for
|
||||
# some reason.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
name: facebook/rocksdb/pr-jobs
|
||||
on: [push, pull_request]
|
||||
permissions: {}
|
||||
jobs:
|
||||
# NOTE: multiple workflows would be recommended, but the current GHA UI in
|
||||
# PRs doesn't make it clear when there's an overall error with a workflow,
|
||||
@@ -91,9 +90,6 @@ jobs:
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -108,47 +104,38 @@ jobs:
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly:
|
||||
build-linux-gcc-7-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: USE_FOLLY=1 LIB_MODE=static V=1 make -j32 check
|
||||
- run: USE_FOLLY=1 LIB_MODE=static CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly-lite-no-test:
|
||||
build-linux-gcc-7-with-folly-lite-no-test:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: USE_FOLLY_LITE=1 V=1 make -j32 all
|
||||
- run: USE_FOLLY_LITE=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-coroutines:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
|
||||
@@ -47,9 +47,6 @@ package/
|
||||
unity.a
|
||||
tags
|
||||
etags
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_test2
|
||||
|
||||
+6
-18
@@ -32,7 +32,7 @@
|
||||
# 3. cmake ..
|
||||
# 4. make -j
|
||||
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
include(ReadVersion)
|
||||
@@ -267,8 +267,8 @@ endif(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
CHECK_C_COMPILER_FLAG("-march=loongarch64" HAS_LOONGARCH64)
|
||||
if(HAS_LOONGARCH64)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
|
||||
endif(HAS_LOONGARCH64)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
|
||||
@@ -488,8 +488,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Cygwin")
|
||||
add_definitions(-fno-builtin-memcmp -DCYGWIN)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
add_definitions(-DOS_MACOSX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "iOS")
|
||||
add_definitions(-DOS_MACOSX -DIOS_CROSS_COMPILE)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_definitions(-DOS_LINUX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
|
||||
@@ -617,7 +615,7 @@ if(USE_FOLLY)
|
||||
FMT_INST_PATH)
|
||||
exec_program(ls ARGS -d ${FOLLY_INST_PATH}/../gflags* OUTPUT_VARIABLE
|
||||
GFLAGS_INST_PATH)
|
||||
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.83.0)
|
||||
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.78.0)
|
||||
if(EXISTS ${FMT_INST_PATH}/lib64)
|
||||
set(fmt_DIR ${FMT_INST_PATH}/lib64/cmake/fmt)
|
||||
else()
|
||||
@@ -693,7 +691,6 @@ set(SOURCES
|
||||
db/db_impl/db_impl_write.cc
|
||||
db/db_impl/db_impl_compaction_flush.cc
|
||||
db/db_impl/db_impl_files.cc
|
||||
db/db_impl/db_impl_follower.cc
|
||||
db/db_impl/db_impl_open.cc
|
||||
db/db_impl/db_impl_debug.cc
|
||||
db/db_impl/db_impl_experimental.cc
|
||||
@@ -751,7 +748,6 @@ set(SOURCES
|
||||
env/env_encryption.cc
|
||||
env/file_system.cc
|
||||
env/file_system_tracer.cc
|
||||
env/fs_on_demand.cc
|
||||
env/fs_remap.cc
|
||||
env/mock_env.cc
|
||||
env/unique_id_gen.cc
|
||||
@@ -779,7 +775,6 @@ set(SOURCES
|
||||
memtable/hash_skiplist_rep.cc
|
||||
memtable/skiplistrep.cc
|
||||
memtable/vectorrep.cc
|
||||
memtable/wbwi_memtable.cc
|
||||
memtable/write_buffer_manager.cc
|
||||
monitoring/histogram.cc
|
||||
monitoring/histogram_windowing.cc
|
||||
@@ -940,7 +935,6 @@ set(SOURCES
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/simulator_cache/cache_simulator.cc
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/table_properties_collectors/compact_for_tiering_collector.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
utilities/trace/replayer_impl.cc
|
||||
@@ -1043,8 +1037,10 @@ endif()
|
||||
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
db/db_impl/db_impl_follower.cc
|
||||
port/port_posix.cc
|
||||
env/env_posix.cc
|
||||
env/fs_on_demand.cc
|
||||
env/fs_posix.cc
|
||||
env/io_posix.cc)
|
||||
endif()
|
||||
@@ -1053,7 +1049,6 @@ if(USE_FOLLY_LITE)
|
||||
list(APPEND SOURCES
|
||||
third-party/folly/folly/container/detail/F14Table.cpp
|
||||
third-party/folly/folly/detail/Futex.cpp
|
||||
third-party/folly/folly/lang/Exception.cpp
|
||||
third-party/folly/folly/lang/SafeAssert.cpp
|
||||
third-party/folly/folly/lang/ToAscii.cpp
|
||||
third-party/folly/folly/ScopeGuard.cpp
|
||||
@@ -1061,12 +1056,6 @@ if(USE_FOLLY_LITE)
|
||||
third-party/folly/folly/synchronization/DistributedMutex.cpp
|
||||
third-party/folly/folly/synchronization/ParkingLot.cpp)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
exec_program(python3 ${PROJECT_SOURCE_DIR}/third-party/folly ARGS
|
||||
build/fbcode_builder/getdeps.py show-source-dir boost OUTPUT_VARIABLE
|
||||
BOOST_SOURCE_PATH)
|
||||
exec_program(ls ARGS -d ${BOOST_SOURCE_PATH}/boost* OUTPUT_VARIABLE
|
||||
BOOST_INCLUDE_DIR)
|
||||
include_directories(${BOOST_INCLUDE_DIR})
|
||||
add_definitions(-DUSE_FOLLY -DFOLLY_NO_CONFIG)
|
||||
list(APPEND THIRDPARTY_LIBS glog)
|
||||
endif()
|
||||
@@ -1490,7 +1479,6 @@ if(WITH_TESTS)
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/simulator_cache/cache_simulator_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_for_tiering_collector_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
|
||||
+12
-146
@@ -1,148 +1,14 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 9.9.3 (12/03/2024)
|
||||
### Performance Improvements
|
||||
* In buffered IO mode, try to align writes on power of 2 if checksum handoff is not enabled for the file type being written.
|
||||
|
||||
## 9.9.2 (11/22/2024)
|
||||
## 9.3.2 (08/02/2024)
|
||||
### Bug Fixes
|
||||
* Honor ConfigOptions.ignore_unknown_options in ParseStruct()
|
||||
|
||||
## 9.9.1 (11/30/2024)
|
||||
### Behavior Changes
|
||||
* Updates the hidden hook RocksDbThreadYieldAndCheckAbort() to support MySQL to abort long-running query.
|
||||
|
||||
## 9.9.0 (11/18/2024)
|
||||
### New Features
|
||||
* Multi-Column-Family-Iterator (CoalescingIterator/AttributeGroupIterator) is no longer marked as experimental
|
||||
* Adds a new table property "rocksdb.newest.key.time" which records the unix timestamp of the newest key. Uses this table property for FIFO TTL and temperature change compaction.
|
||||
|
||||
### Public API Changes
|
||||
* Added a new API `Transaction::GetAttributeGroupIterator` that can be used to create a multi-column-family attribute group iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
|
||||
* Added a new API `Transaction::GetCoalescingIterator` that can be used to create a multi-column-family coalescing iterator over the specified column families, including the data from both the transaction and the underlying database. This API is currently supported for optimistic and write-committed pessimistic transactions.
|
||||
|
||||
### Behavior Changes
|
||||
* `BaseDeltaIterator` now honors the read option `allow_unprepared_value`.
|
||||
|
||||
### Bug Fixes
|
||||
* `BaseDeltaIterator` now calls `PrepareValue` on the base iterator in case it has been created with the `allow_unprepared_value` read option set. Earlier, such base iterators could lead to incorrect values being exposed from `BaseDeltaIterator`.
|
||||
* Fix a leak of obsolete blob files left open until DB::Close(). This bug was introduced in version 9.4.0.
|
||||
* Fix missing cases of corruption retry during DB open and read API processing.
|
||||
* Fix a bug for transaction db with 2pc where an old WAL may be retained longer than needed (#13127).
|
||||
* Fix leaks of some open SST files (until `DB::Close()`) that are written but never become live due to various failures. (We now have a check for such leaks with no outstanding issues.)
|
||||
* Fix a bug for replaying WALs for WriteCommitted transaction DB when its user-defined timestamps setting is toggled on/off between DB sessions.
|
||||
|
||||
### Performance Improvements
|
||||
* Fix regression in issue #12038 due to `Options::compaction_readahead_size` greater than `max_sectors_kb` (i.e, largest I/O size that the OS issues to a block device defined in linux)
|
||||
|
||||
## 9.8.0 (10/25/2024)
|
||||
### New Features
|
||||
* All non-`block_cache` options in `BlockBasedTableOptions` are now mutable with `DB::SetOptions()`. See also Bug Fixes below.
|
||||
* When using iterators with BlobDB, it is now possible to load large values on an on-demand basis, i.e. only if they are actually needed by the application. This can save I/O in use cases where the values associated with certain keys are not needed. For more details, see the new read option `allow_unprepared_value` and the iterator API `PrepareValue`.
|
||||
* Add a new file ingestion option `IngestExternalFileOptions::fill_cache` to support not adding blocks from ingested files into block cache during file ingestion.
|
||||
* The option `allow_unprepared_value` is now also supported for multi-column-family iterators (i.e. `CoalescingIterator` and `AttributeGroupIterator`).
|
||||
* When a file with just one range deletion (standalone range deletion file) is ingested via bulk loading, it will be marked for compaction. During compaction, this type of files can be used to directly filter out some input files that are not protected by any snapshots and completely deleted by the standalone range deletion file.
|
||||
|
||||
### Behavior Changes
|
||||
* During file ingestion, overlapping files level assignment are done in multiple batches, so that they can potentially be assigned to lower levels other than always land on L0.
|
||||
* OPTIONS file to be loaded by remote worker is now preserved so that it does not get purged by the primary host. A similar technique as how we are preserving new SST files from getting purged is used for this. min_options_file_numbers_ is tracked like pending_outputs_ is tracked.
|
||||
* Trim readahead_size during scans so data blocks containing keys that are not in the same prefix as the seek key in `Seek()` are not prefetched when `ReadOptions::auto_readahead_size=true` (default value) and `ReadOptions::prefix_same_as_start = true`
|
||||
* Assigning levels for external files are done in the same way for universal compaction and leveled compaction. The old behavior tends to assign files to L0 while the new behavior will assign the files to the lowest level possible.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a longstanding race condition in SetOptions for `block_based_table_factory` options. The fix has some subtle behavior changes because of copying and replacing the TableFactory on a change with SetOptions, including requiring an Iterator::Refresh() for an existing Iterator to use the latest options.
|
||||
* Fix under counting of allocated memory in the compressed secondary cache due to looking at the compressed block size rather than the actual memory allocated, which could be larger due to internal fragmentation.
|
||||
* `GetApproximateMemTableStats()` could return disastrously bad estimates 5-25% of the time. The function has been re-engineered to return much better estimates with similar CPU cost.
|
||||
* Skip insertion of compressed blocks in the secondary cache if the lowest_used_cache_tier DB option is kVolatileTier.
|
||||
* Fix an issue in level compaction where a small CF with small compaction debt can cause the DB to allow parallel compactions. (#13054)
|
||||
* Several DB option settings could be lost through `GetOptionsFromString()`, possibly elsewhere as well. Affected options, now fixed:`background_close_inactive_wals`, `write_dbid_to_manifest`, `write_identity_file`, `prefix_seek_opt_in_only`
|
||||
|
||||
## 9.7.0 (09/20/2024)
|
||||
### New Features
|
||||
* Make Cache a customizable class that can be instantiated by the object registry.
|
||||
* Add new option `prefix_seek_opt_in_only` that makes iterators generally safer when you might set a `prefix_extractor`. When `prefix_seek_opt_in_only=true`, which is expected to be the future default, prefix seek is only used when `prefix_same_as_start` or `auto_prefix_mode` are set. Also, `prefix_same_as_start` and `auto_prefix_mode` now allow prefix filtering even with `total_order_seek=true`.
|
||||
* Add a new table property "rocksdb.key.largest.seqno" which records the largest sequence number of all keys in file. It is verified to be zero during SST file ingestion.
|
||||
|
||||
### Behavior Changes
|
||||
* Changed the semantics of the BlobDB configuration option `blob_garbage_collection_force_threshold` to define a threshold for the overall garbage ratio of all blob files currently eligible for garbage collection (according to `blob_garbage_collection_age_cutoff`). This can provide better control over space amplification at the cost of slightly higher write amplification.
|
||||
* Set `write_dbid_to_manifest=true` by default. This means DB ID will now be preserved through backups, checkpoints, etc. by default. Also add `write_identity_file` option which can be set to false for anticipated future behavior.
|
||||
* In FIFO compaction, compactions for changing file temperature (configured by option `file_temperature_age_thresholds`) will compact one file at a time, instead of merging multiple eligible file together (#13018).
|
||||
* Support ingesting db generated files using hard link, i.e. IngestExternalFileOptions::move_files/link_files and IngestExternalFileOptions::allow_db_generated_files.
|
||||
* Add a new file ingestion option `IngestExternalFileOptions::link_files` to hard link input files and preserve original files links after ingestion.
|
||||
* DB::Close now untracks files in SstFileManager, making avaialble any space used
|
||||
by them. Prior to this change they would be orphaned until the DB is re-opened.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in CompactRange() where result files may not be compacted in any future compaction. This can only happen when users configure CompactRangeOptions::change_level to true and the change level step of manual compaction fails (#13009).
|
||||
* Fix handling of dynamic change of `prefix_extractor` with memtable prefix filter. Previously, prefix seek could mix different prefix interpretations between memtable and SST files. Now the latest `prefix_extractor` at the time of iterator creation or refresh is respected.
|
||||
* Fix a bug with manual_wal_flush and auto error recovery from WAL failure that may cause CFs to be inconsistent (#12995). The fix will set potential WAL write failure as fatal error when manual_wal_flush is true, and disables auto error recovery from these errors.
|
||||
|
||||
## 9.6.0 (08/19/2024)
|
||||
### New Features
|
||||
* Best efforts recovery supports recovering to incomplete Version with a clean seqno cut that presents a valid point in time view from the user's perspective, if versioning history doesn't include atomic flush.
|
||||
* New option `BlockBasedTableOptions::decouple_partitioned_filters` should improve efficiency in serving read queries because filter and index partitions can consistently target the configured `metadata_block_size`. This option is currently opt-in.
|
||||
* Introduce a new mutable CF option `paranoid_memory_checks`. It enables additional validation on data integrity during reads/scanning. Currently, skip list based memtable will validate key ordering during look up and scans.
|
||||
|
||||
### Public API Changes
|
||||
* Add ticker stats to count file read retries due to checksum mismatch
|
||||
* Adds optional installation callback function for remote compaction
|
||||
|
||||
### Behavior Changes
|
||||
* There may be less intra-L0 compaction triggered by total L0 size being too small. We now use compensated file size (tombstones are assigned some value size) when calculating L0 size and reduce the threshold for L0 size limit. This is to avoid accumulating too much data/tombstones in L0.
|
||||
|
||||
### Bug Fixes
|
||||
* Make DestroyDB supports slow deletion when it's configured in `SstFileManager`. The slow deletion is subject to the configured `rate_bytes_per_sec`, but not subject to the `max_trash_db_ratio`.
|
||||
* Fixed a bug where we set unprep_seqs_ even when WriteImpl() fails. This was caught by stress test write fault injection in WriteImpl(). This may have incorrectly caused iteration creation failure for unvalidated writes or returned wrong result for WriteUnpreparedTxn::GetUnpreparedSequenceNumbers().
|
||||
* Fixed a bug where successful write right after error recovery for last failed write finishes causes duplicate WAL entries
|
||||
* Fixed a data race involving the background error status in `unordered_write` mode.
|
||||
* Fix a bug where file snapshot functions like backup, checkpoint may attempt to copy a non-existing manifest file. #12882
|
||||
* Fix a bug where per kv checksum corruption may be ignored in MultiGet().
|
||||
* *Make DestroyDB supports slow deletion when it's configured in `SstFileManager`. The slow deletion is subject to the configured `rate_bytes_per_sec`, but not subject to the `max_trash_db_ratio`.
|
||||
* Fix a race condition in pessimistic transactions that could allow multiple transactions with the same name to be registered simultaneously, resulting in a crash or other unpredictable behavior.
|
||||
|
||||
## 9.5.0 (07/19/2024)
|
||||
### Public API Changes
|
||||
* Introduced new C API function rocksdb_writebatch_iterate_cf for column family-aware iteration over the contents of a WriteBatch
|
||||
* Add support to ingest SST files generated by a DB instead of SstFileWriter. This can be enabled with experimental option `IngestExternalFileOptions::allow_db_generated_files`.
|
||||
|
||||
### Behavior Changes
|
||||
* When calculating total log size for the `log_size_for_flush` argument in `CreateCheckpoint` API, the size of the archived log will not be included to avoid unnecessary flush
|
||||
|
||||
## 9.3.1 (05/25/2024)
|
||||
### Bug Fixes
|
||||
* Fix a major bug in which an iterator using prefix filtering and SeekForPrev might miss data when the DB is using `whole_key_filtering=false` and `partition_filters=true`.
|
||||
* Fixed a bug where `OnErrorRecoveryBegin()` is not called before auto recovery starts.
|
||||
* Fixed a bug where event listener reads ErrorHandler's `bg_error_` member without holding db mutex(#12803).
|
||||
* Fixed a bug in handling MANIFEST write error that caused the latest valid MANIFEST file to get deleted, resulting in the DB being unopenable.
|
||||
* Fixed a race between error recovery due to manifest sync or write failure and external SST file ingestion. Both attempt to write a new manifest file, which causes an assertion failure.
|
||||
|
||||
### Performance Improvements
|
||||
* Fix an issue where compactions were opening table files and reading table properties while holding db mutex_.
|
||||
* Reduce unnecessary filesystem queries and DB mutex acquires in creating backups and checkpoints.
|
||||
|
||||
## 9.4.0 (06/23/2024)
|
||||
### New Features
|
||||
* Added a `CompactForTieringCollectorFactory` to auto trigger compaction for tiering use case.
|
||||
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `GetEntityForUpdate` API.
|
||||
* Added a new "count" command to the ldb repl shell. By default, it prints a count of keys in the database from start to end. The options --from=<key> and/or --to=<key> can be specified to limit the range.
|
||||
* Add `rocksdb_writebatch_update_timestamps`, `rocksdb_writebatch_wi_update_timestamps` in C API.
|
||||
* Add `rocksdb_iter_refresh` in C API.
|
||||
* Add `rocksdb_writebatch_create_with_params`, `rocksdb_writebatch_wi_create_with_params` to create WB and WBWI with all options in C API
|
||||
|
||||
### Public API Changes
|
||||
* Deprecated names `LogFile` and `VectorLogPtr` in favor of new names `WalFile` and `VectorWalPtr`.
|
||||
* Introduce a new universal compaction option CompactionOptionsUniversal::max_read_amp which allows user to define the limit on the number of sorted runs separately from the trigger for compaction (`level0_file_num_compaction_trigger`) #12477.
|
||||
|
||||
### Behavior Changes
|
||||
* Inactive WALs are immediately closed upon being fully sync-ed rather than in a background thread. This is to ensure LinkFile() is not called on files still open for write, which might not be supported by some FileSystem implementations. This should not be a performance issue, but an opt-out is available with with new DB option `background_close_inactive_wals`.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a rare case in which a hard-linked WAL in a Checkpoint is not fully synced (so might lose data on power loss).
|
||||
* Fixed the output of the `ldb dump_wal` command for `PutEntity` records so it prints the key and correctly resets the hexadecimal formatting flag after printing the wide-column entity.
|
||||
* Fixed an issue where `PutEntity` records were handled incorrectly while rebuilding transactions during recovery.
|
||||
* Various read operations could ignore various ReadOptions that might be relevant. Fixed many such cases, which can result in behavior change but a better reflection of specified options.
|
||||
|
||||
### Performance Improvements
|
||||
* Improved write throughput to memtable when there's a large number of concurrent writers and allow_concurrent_memtable_write=true(#12545)
|
||||
* [internal only] Build script improvement
|
||||
|
||||
## 9.3.0 (05/17/2024)
|
||||
### New Features
|
||||
@@ -175,7 +41,7 @@ by them. Prior to this change they would be orphaned until the DB is re-opened.
|
||||
* Added a new API `GetEntityFromBatchAndDB` to `WriteBatchWithIndex` that can be used for wide-column point lookups with read-your-own-writes consistency. Similarly to `GetFromBatchAndDB`, the API can combine data from the write batch with data from the underlying database if needed. See the API comments for more details.
|
||||
* [Experimental] Introduce two new cross-column-family iterators - CoalescingIterator and AttributeGroupIterator. The CoalescingIterator enables users to iterate over multiple column families and access their values and columns. During this iteration, if the same key exists in more than one column family, the keys in the later column family will overshadow the previous ones. The AttributeGroupIterator allows users to gather wide columns per Column Family and create attribute groups while iterating over keys across all CFs.
|
||||
* Added a new API `MultiGetEntityFromBatchAndDB` to `WriteBatchWithIndex` that can be used for batched wide-column point lookups with read-your-own-writes consistency. Similarly to `MultiGetFromBatchAndDB`, the API can combine data from the write batch with data from the underlying database if needed. See the API comments for more details.
|
||||
* Adds a `SstFileReader::NewTableIterator` API to support programmatically read a SST file as a raw table file.
|
||||
* *Adds a `SstFileReader::NewTableIterator` API to support programmatically read a SST file as a raw table file.
|
||||
* Add an option to `WaitForCompactOptions` - `wait_for_purge` to make `WaitForCompact()` API wait for background purge to complete
|
||||
|
||||
### Public API Changes
|
||||
@@ -204,24 +70,24 @@ the whole DB to be dropped right after migration if the migrated data is larger
|
||||
* Fixed a bug where wrong padded bytes are used to generate file checksum and `DataVerificationInfo::checksum` upon file creation
|
||||
* Correctly implemented the move semantics of `PinnableWideColumns`.
|
||||
* Fixed a bug when the recycle_log_file_num in DBOptions is changed from 0 to non-zero when a DB is reopened. On a subsequent reopen, if a log file created when recycle_log_file_num==0 was reused previously, is alive and is empty, we could end up inserting stale WAL records into the memtable.
|
||||
* Fix a bug where obsolete files' deletion during DB::Open are not rate limited with `SstFilemManager`'s slow deletion feature even if it's configured.
|
||||
* *Fix a bug where obsolete files' deletion during DB::Open are not rate limited with `SstFilemManager`'s slow deletion feature even if it's configured.
|
||||
|
||||
## 9.1.0 (03/22/2024)
|
||||
### New Features
|
||||
* Added an option, `GetMergeOperandsOptions::continue_cb`, to give users the ability to end `GetMergeOperands()`'s lookup process before all merge operands were found.
|
||||
* Add sanity checks for ingesting external files that currently checks if the user key comparator used to create the file is compatible with the column family's user key comparator.
|
||||
* *Add sanity checks for ingesting external files that currently checks if the user key comparator used to create the file is compatible with the column family's user key comparator.
|
||||
*Support ingesting external files for column family that has user-defined timestamps in memtable only enabled.
|
||||
* On file systems that support storage level data checksum and reconstruction, retry SST block reads for point lookups, scans, and flush and compaction if there's a checksum mismatch on the initial read.
|
||||
* Some enhancements and fixes to experimental Temperature handling features, including new `default_write_temperature` CF option and opening an `SstFileWriter` with a temperature.
|
||||
* `WriteBatchWithIndex` now supports wide-column point lookups via the `GetEntityFromBatch` API. See the API comments for more details.
|
||||
* Implement experimental features: API `Iterator::GetProperty("rocksdb.iterator.write-time")` to allow users to get data's approximate write unix time and write data with a specific write time via `WriteBatch::TimedPut` API.
|
||||
* *Implement experimental features: API `Iterator::GetProperty("rocksdb.iterator.write-time")` to allow users to get data's approximate write unix time and write data with a specific write time via `WriteBatch::TimedPut` API.
|
||||
|
||||
### Public API Changes
|
||||
* Best-effort recovery (`best_efforts_recovery == true`) may now be used together with atomic flush (`atomic_flush == true`). The all-or-nothing recovery guarantee for atomically flushed data will be upheld.
|
||||
* Remove deprecated option `bottommost_temperature`, already replaced by `last_level_temperature`
|
||||
* Added new PerfContext counters for block cache bytes read - block_cache_index_read_byte, block_cache_filter_read_byte, block_cache_compression_dict_read_byte, and block_cache_read_byte.
|
||||
* Deprecate experimental Remote Compaction APIs - StartV2() and WaitForCompleteV2() and introduce Schedule() and Wait(). The new APIs essentially does the same thing as the old APIs. They allow taking externally generated unique id to wait for remote compaction to complete.
|
||||
* For API `WriteCommittedTransaction::GetForUpdate`, if the column family enables user-defined timestamp, it was mandated that argument `do_validate` cannot be false, and UDT based validation has to be done with a user set read timestamp. It's updated to make the UDT based validation optional if user sets `do_validate` to false and does not set a read timestamp. With this, `GetForUpdate` skips UDT based validation and it's users' responsibility to enforce the UDT invariant. SO DO NOT skip this UDT-based validation if users do not have ways to enforce the UDT invariant. Ways to enforce the invariant on the users side include manage a monotonically increasing timestamp, commit transactions in a single thread etc.
|
||||
* *For API `WriteCommittedTransaction::GetForUpdate`, if the column family enables user-defined timestamp, it was mandated that argument `do_validate` cannot be false, and UDT based validation has to be done with a user set read timestamp. It's updated to make the UDT based validation optional if user sets `do_validate` to false and does not set a read timestamp. With this, `GetForUpdate` skips UDT based validation and it's users' responsibility to enforce the UDT invariant. SO DO NOT skip this UDT-based validation if users do not have ways to enforce the UDT invariant. Ways to enforce the invariant on the users side include manage a monotonically increasing timestamp, commit transactions in a single thread etc.
|
||||
* Defined a new PerfLevel `kEnableWait` to measure time spent by user threads blocked in RocksDB other than mutex, such as a write thread waiting to be added to a write group, a write thread delayed or stalled etc.
|
||||
* `RateLimiter`'s API no longer requires the burst size to be the refill size. Users of `NewGenericRateLimiter()` can now provide burst size in `single_burst_bytes`. Implementors of `RateLimiter::SetSingleBurstBytes()` need to adapt their implementations to match the changed API doc.
|
||||
* Add `write_memtable_time` to the newly introduced PerfLevel `kEnableWait`.
|
||||
@@ -254,7 +120,7 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
|
||||
## 9.0.0 (02/16/2024)
|
||||
### New Features
|
||||
* Provide support for FSBuffer for point lookups. Also added support for scans and compactions that don't go through prefetching.
|
||||
* Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
|
||||
* *Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
|
||||
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
|
||||
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
|
||||
|
||||
@@ -264,9 +130,9 @@ MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 76
|
||||
* Exposed mode option to Rate Limiter via c api.
|
||||
* Removed deprecated option `access_hint_on_compaction_start`
|
||||
* Removed deprecated option `ColumnFamilyOptions::check_flush_compaction_key_order`
|
||||
* Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
|
||||
* *Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
|
||||
* Removed deprecated option `ColumnFamilyOptions::level_compaction_dynamic_file_size`
|
||||
* Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
|
||||
* *Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
|
||||
* Remove the force mode for `EnableFileDeletions` API because it is unsafe with no known legitimate use.
|
||||
* Removed deprecated option `ColumnFamilyOptions::ignore_max_compaction_bytes_for_input`
|
||||
* `sst_dump --command=check` now compares the number of records in a table with `num_entries` in table property, and reports corruption if there is a mismatch. API `SstFileDumper::ReadSequential()` is updated to optionally do this verification. (#12322)
|
||||
|
||||
@@ -103,7 +103,6 @@ dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export LIB_MODE="$(LIB_MODE)"; \
|
||||
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
|
||||
export USE_FOLLY="$(USE_FOLLY)"; \
|
||||
export USE_FOLLY_LITE="$(USE_FOLLY_LITE)"; \
|
||||
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
@@ -501,17 +500,6 @@ endif
|
||||
ifeq ($(USE_FOLLY_LITE),1)
|
||||
# Path to the Folly source code and include files
|
||||
FOLLY_DIR = ./third-party/folly
|
||||
ifneq ($(strip $(BOOST_SOURCE_PATH)),)
|
||||
BOOST_INCLUDE = $(shell (ls -d $(BOOST_SOURCE_PATH)/boost*/))
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(BOOST_INCLUDE)
|
||||
PLATFORM_CXXFLAGS += -I$(BOOST_INCLUDE)
|
||||
else
|
||||
PLATFORM_CCFLAGS += -isystem $(BOOST_INCLUDE)
|
||||
PLATFORM_CXXFLAGS += -isystem $(BOOST_INCLUDE)
|
||||
endif
|
||||
endif # BOOST_SOURCE_PATH
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(FOLLY_DIR)
|
||||
@@ -630,11 +618,6 @@ VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
|
||||
VALGRIND_OPTS = --error-exitcode=$(VALGRIND_ERROR) --leak-check=full
|
||||
# Not yet supported: --show-leak-kinds=definite,possible,reachable --errors-for-leak-kinds=definite,possible,reachable
|
||||
|
||||
# Work around valgrind hanging on systems with limited internet access
|
||||
ifneq ($(shell which git 2>/dev/null && git config --get https.proxy),)
|
||||
export DEBUGINFOD_URLS=
|
||||
endif
|
||||
|
||||
TEST_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(TEST_LIB_SOURCES) $(MOCK_LIB_SOURCES)) $(GTEST)
|
||||
BENCH_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(BENCH_LIB_SOURCES))
|
||||
CACHE_BENCH_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(CACHE_BENCH_LIB_SOURCES))
|
||||
@@ -653,37 +636,26 @@ TESTS = $(patsubst %.cc, %, $(notdir $(TEST_MAIN_SOURCES)))
|
||||
TESTS += $(patsubst %.c, %, $(notdir $(TEST_MAIN_SOURCES_C)))
|
||||
TESTS += $(PLUGIN_TESTS)
|
||||
|
||||
# `make check-headers` to verify that each header file includes its own deps
|
||||
# and that public headers do not depend on internal headers
|
||||
# `make check-headers` to very that each header file includes its own
|
||||
# dependencies
|
||||
ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
|
||||
# TODO: add/support JNI headers
|
||||
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
|
||||
# Some headers like in port/ are platform-specific
|
||||
DEV_HEADERS_TO_CHECK := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/')
|
||||
PUBLIC_HEADERS_TO_CHECK := $(shell $(FIND) include/ -type f -name '*.h' | grep -E -v 'lua/')
|
||||
DEV_HEADERS := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/')
|
||||
else
|
||||
DEV_HEADERS_TO_CHECK :=
|
||||
PUBLIC_HEADERS_TO_CHECK :=
|
||||
DEV_HEADERS :=
|
||||
endif
|
||||
HEADER_OK_FILES = $(patsubst %.h, %.h.ok, $(DEV_HEADERS_TO_CHECK)) \
|
||||
$(patsubst %.h, %.h.pub, $(PUBLIC_HEADERS_TO_CHECK))
|
||||
HEADER_OK_FILES = $(patsubst %.h, %.h.ok, $(DEV_HEADERS))
|
||||
|
||||
AM_V_CCH = $(am__v_CCH_$(V))
|
||||
am__v_CCH_ = $(am__v_CCH_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_CCH_0 = @echo " CC.h " $<;
|
||||
am__v_CCH_1 =
|
||||
|
||||
# verify headers include their own dependencies, under dev build settings
|
||||
%.h.ok: %.h # .h.ok not actually created, so re-checked on each invocation
|
||||
# -DROCKSDB_NAMESPACE=42 ensures the namespace header is included
|
||||
$(AM_V_CCH) echo '#include "$<"' | $(CXX) $(CXXFLAGS) \
|
||||
-DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
|
||||
# verify public headers do not depend on internal headers, under typical
|
||||
# user build settings
|
||||
%.h.pub: %.h # .h.pub not actually created, so re-checked on each invocation
|
||||
$(AM_V_CCH) cd include/ && echo '#include "$(patsubst include/%,%,$<)"' | \
|
||||
$(CXX) -I. -DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
$(AM_V_CCH) echo '#include "$<"' | $(CXX) $(CXXFLAGS) -DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
|
||||
check-headers: $(HEADER_OK_FILES)
|
||||
|
||||
@@ -1169,16 +1141,16 @@ ubsan_crash_test_with_best_efforts_recovery: clean
|
||||
$(MAKE) clean
|
||||
|
||||
full_valgrind_test:
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
full_valgrind_test_some:
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check_some
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check_some
|
||||
|
||||
valgrind_test:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
valgrind_test_some:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 PORTABLE=1 $(MAKE) valgrind_check_some
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check_some
|
||||
|
||||
valgrind_check: $(TESTS)
|
||||
$(MAKE) DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" gen_parallel_tests
|
||||
@@ -1659,9 +1631,6 @@ compaction_job_stats_test: $(OBJ_DIR)/db/compaction/compaction_job_stats_test.o
|
||||
compaction_service_test: $(OBJ_DIR)/db/compaction/compaction_service_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_for_tiering_collector_test: $(OBJ_DIR)/utilities/table_properties_collectors/compact_for_tiering_collector_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_on_deletion_collector_test: $(OBJ_DIR)/utilities/table_properties_collectors/compact_on_deletion_collector_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2144,8 +2113,8 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
SNAPPY_VER ?= 1.2.1
|
||||
SNAPPY_SHA256 ?= 736aeb64d86566d2236ddffa2865ee5d7a82d26c9016b36218fcc27ea4f09f86
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.4
|
||||
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
|
||||
@@ -2242,7 +2211,7 @@ libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
mkdir snappy-$(SNAPPY_VER)/build
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DSNAPPY_BUILD_BENCHMARKS=OFF -DSNAPPY_BUILD_TESTS=OFF --compile-no-warning-as-error ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
|
||||
|
||||
lz4-$(LZ4_VER).tar.gz:
|
||||
@@ -2489,13 +2458,14 @@ checkout_folly:
|
||||
fi
|
||||
@# Pin to a particular version for public CI, so that PR authors don't
|
||||
@# need to worry about folly breaking our integration. Update periodically
|
||||
cd third-party/folly && git reset --hard 03041f014b6e6ebb6119ffae8b7a37308f52e913
|
||||
cd third-party/folly && git reset --hard beacd86d63cd71c904632262e6c36f60874d78ba
|
||||
@# A hack to remove boost dependency.
|
||||
@# NOTE: this hack is only needed if building using USE_FOLLY_LITE
|
||||
perl -pi -e 's/^(#include <boost)/\/\/$$1/' third-party/folly/folly/functional/Invoke.h
|
||||
@# NOTE: this hack is required for clang in some cases
|
||||
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
|
||||
@# NOTE: this hack is required for gcc in some cases
|
||||
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
|
||||
@# NOTE: boost source will be needed for any build including `USE_FOLLY_LITE` builds as those depend on boost headers
|
||||
cd third-party/folly && $(PYTHON) build/fbcode_builder/getdeps.py fetch boost
|
||||
|
||||
CXX_M_FLAGS = $(filter -m%, $(CXXFLAGS))
|
||||
|
||||
@@ -2507,6 +2477,8 @@ build_folly:
|
||||
echo "Please run checkout_folly first"; \
|
||||
false; \
|
||||
fi
|
||||
# Restore the original version of Invoke.h with boost dependency
|
||||
cd third-party/folly && ${GIT_COMMAND} checkout folly/functional/Invoke.h
|
||||
cd third-party/folly && \
|
||||
CXXFLAGS=" $(CXX_M_FLAGS) -DHAVE_CXX11_ATOMIC " $(PYTHON) build/fbcode_builder/getdeps.py build --no-tests
|
||||
|
||||
|
||||
@@ -149,7 +149,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
"memtable/skiplistrep.cc",
|
||||
"memtable/vectorrep.cc",
|
||||
"memtable/wbwi_memtable.cc",
|
||||
"memtable/write_buffer_manager.cc",
|
||||
"monitoring/histogram.cc",
|
||||
"monitoring/histogram_windowing.cc",
|
||||
@@ -318,7 +317,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/simulator_cache/cache_simulator.cc",
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_for_tiering_collector.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/trace/replayer_impl.cc",
|
||||
@@ -363,9 +361,9 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
], headers=glob(["**/*.h"]), link_whole=False, extra_test_libs=False)
|
||||
], headers=None, link_whole=False, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=[], link_whole=True, extra_test_libs=False)
|
||||
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=None, link_whole=True, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
|
||||
"db/db_test_util.cc",
|
||||
@@ -379,7 +377,7 @@ cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"utilities/agg_merge/test_agg_merge.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=True)
|
||||
], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=True)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
|
||||
"test_util/testutil.cc",
|
||||
@@ -387,16 +385,15 @@ cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
|
||||
"tools/db_bench_tool.cc",
|
||||
"tools/simulated_hybrid_file_system.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
|
||||
], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
|
||||
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=None, link_whole=False, extra_test_libs=False)
|
||||
|
||||
rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"db_stress_tool/batched_ops_stress.cc",
|
||||
"db_stress_tool/cf_consistency_stress.cc",
|
||||
"db_stress_tool/db_stress_common.cc",
|
||||
"db_stress_tool/db_stress_driver.cc",
|
||||
"db_stress_tool/db_stress_filters.cc",
|
||||
"db_stress_tool/db_stress_gflags.cc",
|
||||
"db_stress_tool/db_stress_listener.cc",
|
||||
"db_stress_tool/db_stress_shared_state.cc",
|
||||
@@ -411,15 +408,11 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
], headers=[])
|
||||
], headers=None)
|
||||
|
||||
|
||||
cpp_binary_wrapper(name="ldb", srcs=["tools/ldb.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="db_stress", srcs=["db_stress_tool/db_stress.cc"], deps=[":rocksdb_stress_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="db_bench", srcs=["tools/db_bench.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="cache_bench", srcs=["cache/cache_bench.cc"], deps=[":rocksdb_cache_bench_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="ribbon_bench", srcs=["microbench/ribbon_bench.cc"], deps=[], extra_preprocessor_flags=[], extra_bench_libs=True)
|
||||
@@ -4631,12 +4624,6 @@ cpp_unittest_wrapper(name="compact_files_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="compact_for_tiering_collector_test",
|
||||
srcs=["utilities/table_properties_collectors/compact_for_tiering_collector_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="compact_on_deletion_collector_test",
|
||||
srcs=["utilities/table_properties_collectors/compact_on_deletion_collector_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5027,7 +5014,7 @@ cpp_unittest_wrapper(name="dynamic_bloom_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_library_wrapper(name="env_basic_test_lib", srcs=["env/env_basic_test.cc"], deps=[":rocksdb_test_lib"], headers=[], link_whole=False, extra_test_libs=True)
|
||||
cpp_library_wrapper(name="env_basic_test_lib", srcs=["env/env_basic_test.cc"], deps=[":rocksdb_test_lib"], headers=None, link_whole=False, extra_test_libs=True)
|
||||
|
||||
cpp_unittest_wrapper(name="env_basic_test",
|
||||
srcs=["env/env_basic_test.cc"],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
try:
|
||||
from builtins import str
|
||||
@@ -10,7 +11,7 @@ import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from targets_builder import TARGETSBuilder, LiteralValue
|
||||
from targets_builder import TARGETSBuilder
|
||||
|
||||
from util import ColorString
|
||||
|
||||
@@ -131,7 +132,7 @@ def generate_targets(repo_path, deps_map):
|
||||
if len(sys.argv) >= 2:
|
||||
# Heuristically quote and canonicalize whitespace for inclusion
|
||||
# in how the file was generated.
|
||||
extra_argv = " '{}'".format(" ".join(sys.argv[1].split()))
|
||||
extra_argv = " '{0}'".format(" ".join(sys.argv[1].split()))
|
||||
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path, extra_argv)
|
||||
|
||||
@@ -149,7 +150,6 @@ def generate_targets(repo_path, deps_map):
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
],
|
||||
headers=LiteralValue("glob([\"**/*.h\"])")
|
||||
)
|
||||
# rocksdb_whole_archive_lib
|
||||
TARGETS.add_library(
|
||||
@@ -158,6 +158,7 @@ def generate_targets(repo_path, deps_map):
|
||||
deps=[
|
||||
":rocksdb_lib",
|
||||
],
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
link_whole=True,
|
||||
)
|
||||
@@ -192,18 +193,10 @@ def generate_targets(repo_path, deps_map):
|
||||
+ src_mk.get("STRESS_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
)
|
||||
# ldb binary
|
||||
TARGETS.add_binary(
|
||||
"ldb", ["tools/ldb.cc"], [":rocksdb_tools_lib"]
|
||||
)
|
||||
# db_stress binary
|
||||
TARGETS.add_binary(
|
||||
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
|
||||
)
|
||||
# db_bench binary
|
||||
TARGETS.add_binary(
|
||||
"db_bench", ["tools/db_bench.cc"], [":rocksdb_tools_lib"]
|
||||
)
|
||||
# cache_bench binary
|
||||
TARGETS.add_binary(
|
||||
"cache_bench", ["cache/cache_bench.cc"], [":rocksdb_cache_bench_tools_lib"]
|
||||
@@ -212,7 +205,7 @@ def generate_targets(repo_path, deps_map):
|
||||
for src in src_mk.get("MICROBENCH_SOURCES", []):
|
||||
name = src.rsplit("/", 1)[1].split(".")[0] if "/" in src else src.split(".")[0]
|
||||
TARGETS.add_binary(name, [src], [], extra_bench_libs=True)
|
||||
print(f"Extra dependencies:\n{json.dumps(deps_map)}")
|
||||
print("Extra dependencies:\n{0}".format(json.dumps(deps_map)))
|
||||
|
||||
# Dictionary test executable name -> relative source file path
|
||||
test_source_map = {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
try:
|
||||
from builtins import object, str
|
||||
@@ -8,28 +9,17 @@ import pprint
|
||||
|
||||
import targets_cfg
|
||||
|
||||
class LiteralValue:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
def smart_quote_value(val):
|
||||
if isinstance(val, LiteralValue):
|
||||
return str(val)
|
||||
return '"%s"' % val
|
||||
|
||||
def pretty_list(lst, indent=8):
|
||||
if lst is None or len(lst) == 0:
|
||||
return ""
|
||||
|
||||
if len(lst) == 1:
|
||||
return smart_quote_value(lst[0])
|
||||
return '"%s"' % lst[0]
|
||||
|
||||
separator = ',\n%s' % (" " * indent)
|
||||
res = separator.join(sorted(map(smart_quote_value, lst)))
|
||||
res = "\n" + (" " * indent) + res + ',\n' + (" " * (indent - 4))
|
||||
separator = '",\n%s"' % (" " * indent)
|
||||
res = separator.join(sorted(lst))
|
||||
res = "\n" + (" " * indent) + '"' + res + '",\n' + (" " * (indent - 4))
|
||||
return res
|
||||
|
||||
|
||||
@@ -58,12 +48,7 @@ class TARGETSBuilder:
|
||||
extra_test_libs=False,
|
||||
):
|
||||
if headers is not None:
|
||||
if isinstance(headers, LiteralValue):
|
||||
headers = str(headers)
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
else:
|
||||
headers = "[]"
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.library_template.format(
|
||||
@@ -80,7 +65,8 @@ class TARGETSBuilder:
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None, external_dependencies=None):
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
if headers is not None:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.rocksdb_library_template.format(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
rocksdb_target_header_template = """# This file \100generated by:
|
||||
#$ python3 buckifier/buckify_rocksdb.py{extra_argv}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"""
|
||||
This module keeps commonly used components.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
try:
|
||||
from builtins import object
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#
|
||||
# The solution is to move the include out of the #ifdef.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import re
|
||||
@@ -61,7 +62,7 @@ def expand_include(
|
||||
|
||||
included.add(include_path)
|
||||
with open(include_path) as f:
|
||||
print(f'#line 1 "{include_path}"', file=source_out)
|
||||
print('#line 1 "{}"'.format(include_path), file=source_out)
|
||||
process_file(
|
||||
f, include_path, source_out, header_out, include_paths, public_include_paths
|
||||
)
|
||||
@@ -117,7 +118,7 @@ def process_file(
|
||||
)
|
||||
|
||||
if expanded:
|
||||
print(f'#line {line + 1} "{abs_path}"', file=source_out)
|
||||
print('#line {} "{}"'.format(line + 1, abs_path), file=source_out)
|
||||
elif text != "#pragma once\n":
|
||||
source_out.write(text)
|
||||
|
||||
@@ -156,8 +157,8 @@ def main():
|
||||
with open(filename) as f, open(args.source_out, "w") as source_out, open(
|
||||
args.header_out, "w"
|
||||
) as header_out:
|
||||
print(f'#line 1 "{filename}"', file=source_out)
|
||||
print(f'#include "{header_out.name}"', file=source_out)
|
||||
print('#line 1 "{}"'.format(filename), file=source_out)
|
||||
print('#include "{}"'.format(header_out.name), file=source_out)
|
||||
process_file(
|
||||
f, abs_path, source_out, header_out, include_paths, public_include_paths
|
||||
)
|
||||
|
||||
@@ -102,7 +102,7 @@ class BenchmarkUtils:
|
||||
|
||||
|
||||
class ResultParser:
|
||||
def __init__(self, field=r"(\w|[+-:.%])+", intrafield=r"(\s)+", separator="\t"):
|
||||
def __init__(self, field="(\w|[+-:.%])+", intrafield="(\s)+", separator="\t"):
|
||||
self.field = re.compile(field)
|
||||
self.intra = re.compile(intrafield)
|
||||
self.sep = re.compile(separator)
|
||||
@@ -159,7 +159,7 @@ class ResultParser:
|
||||
|
||||
|
||||
def load_report_from_tsv(filename: str):
|
||||
file = open(filename)
|
||||
file = open(filename, "r")
|
||||
contents = file.readlines()
|
||||
file.close()
|
||||
parser = ResultParser()
|
||||
|
||||
@@ -585,7 +585,7 @@ EOF
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbenchmark"
|
||||
fi
|
||||
fi
|
||||
if test $USE_FOLLY || test $USE_FOLLY_LITE; then
|
||||
if test $USE_FOLLY; then
|
||||
# Test whether libfolly library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <folly/synchronization/DistributedMutex.h>
|
||||
@@ -751,11 +751,6 @@ if [ "$USE_FOLLY" ]; then
|
||||
FOLLY_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-inst-dir folly`
|
||||
fi
|
||||
fi
|
||||
if [ "$USE_FOLLY_LITE" ]; then
|
||||
if [ "$FOLLY_DIR" ]; then
|
||||
BOOST_SOURCE_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-source-dir boost`
|
||||
fi
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
@@ -797,7 +792,6 @@ echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
|
||||
echo "FIND=$FIND" >> "$OUTPUT"
|
||||
echo "WATCH=$WATCH" >> "$OUTPUT"
|
||||
echo "FOLLY_PATH=$FOLLY_PATH" >> "$OUTPUT"
|
||||
echo "BOOST_SOURCE_PATH=$BOOST_SOURCE_PATH" >> "$OUTPUT"
|
||||
|
||||
# This will enable some related identifiers for the preprocessor
|
||||
if test -n "$JEMALLOC"; then
|
||||
|
||||
+11
-10
@@ -9,6 +9,7 @@
|
||||
- Prints those error messages to stdout
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import re
|
||||
import sys
|
||||
@@ -42,7 +43,7 @@ class GTestErrorParser(ErrorParserBase):
|
||||
return None
|
||||
gtest_fail_match = self._GTEST_FAIL_PATTERN.match(line)
|
||||
if gtest_fail_match:
|
||||
return "{} failed: {}".format(self._last_gtest_name, gtest_fail_match.group(1))
|
||||
return "%s failed: %s" % (self._last_gtest_name, gtest_fail_match.group(1))
|
||||
return None
|
||||
|
||||
|
||||
@@ -65,52 +66,52 @@ class CompilerErrorParser(MatchErrorParser):
|
||||
# format (link error):
|
||||
# '<filename>:<line #>: error: <error msg>'
|
||||
# The below regex catches both
|
||||
super().__init__(r"\S+:\d+: error:")
|
||||
super(CompilerErrorParser, self).__init__(r"\S+:\d+: error:")
|
||||
|
||||
|
||||
class ScanBuildErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"scan-build: \d+ bugs found.$")
|
||||
super(ScanBuildErrorParser, self).__init__(r"scan-build: \d+ bugs found.$")
|
||||
|
||||
|
||||
class DbCrashErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"\*\*\*.*\^$|TEST FAILED.")
|
||||
super(DbCrashErrorParser, self).__init__(r"\*\*\*.*\^$|TEST FAILED.")
|
||||
|
||||
|
||||
class WriteStressErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
super(WriteStressErrorParser, self).__init__(
|
||||
r"ERROR: write_stress died with exitcode=\d+"
|
||||
)
|
||||
|
||||
|
||||
class AsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"==\d+==ERROR: AddressSanitizer:")
|
||||
super(AsanErrorParser, self).__init__(r"==\d+==ERROR: AddressSanitizer:")
|
||||
|
||||
|
||||
class UbsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
# format: '<filename>:<line #>:<column #>: runtime error: <error msg>'
|
||||
super().__init__(r"\S+:\d+:\d+: runtime error:")
|
||||
super(UbsanErrorParser, self).__init__(r"\S+:\d+:\d+: runtime error:")
|
||||
|
||||
|
||||
class ValgrindErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
# just grab the summary, valgrind doesn't clearly distinguish errors
|
||||
# from other log messages.
|
||||
super().__init__(r"==\d+== ERROR SUMMARY:")
|
||||
super(ValgrindErrorParser, self).__init__(r"==\d+== ERROR SUMMARY:")
|
||||
|
||||
|
||||
class CompatErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"==== .*[Ee]rror.* ====$")
|
||||
super(CompatErrorParser, self).__init__(r"==== .*[Ee]rror.* ====$")
|
||||
|
||||
|
||||
class TsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"WARNING: ThreadSanitizer:")
|
||||
super(TsanErrorParser, self).__init__(r"WARNING: ThreadSanitizer:")
|
||||
|
||||
|
||||
_TEST_NAME_TO_PARSERS = {
|
||||
|
||||
Vendored
+12
-18
@@ -133,25 +133,19 @@ Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
std::shared_ptr<Cache>* result) {
|
||||
Status status;
|
||||
std::shared_ptr<Cache> cache;
|
||||
if (StartsWith(value, "null")) {
|
||||
cache = nullptr;
|
||||
} else if (value.find("://") == std::string::npos) {
|
||||
if (value.find('=') == std::string::npos) {
|
||||
cache = NewLRUCache(ParseSizeT(value));
|
||||
} else {
|
||||
LRUCacheOptions cache_opts;
|
||||
status = OptionTypeInfo::ParseStruct(config_options, "",
|
||||
&lru_cache_options_type_info, "",
|
||||
value, &cache_opts);
|
||||
if (status.ok()) {
|
||||
cache = NewLRUCache(cache_opts);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
result->swap(cache);
|
||||
}
|
||||
if (value.find('=') == std::string::npos) {
|
||||
cache = NewLRUCache(ParseSizeT(value));
|
||||
} else {
|
||||
status = LoadSharedObject<Cache>(config_options, value, result);
|
||||
LRUCacheOptions cache_opts;
|
||||
status = OptionTypeInfo::ParseStruct(config_options, "",
|
||||
&lru_cache_options_type_info, "",
|
||||
value, &cache_opts);
|
||||
if (status.ok()) {
|
||||
cache = NewLRUCache(cache_opts);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
result->swap(cache);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Vendored
-84
@@ -18,7 +18,6 @@
|
||||
#include "cache/lru_cache.h"
|
||||
#include "cache/typed_cache.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "table/block_based/block_cache.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
@@ -886,32 +885,6 @@ TEST_P(CacheTest, ApplyToAllEntriesDuringResize) {
|
||||
ASSERT_EQ(special_count, kSpecialCount);
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, ApplyToHandleTest) {
|
||||
std::string callback_state;
|
||||
const auto callback = [&](const Slice& key, Cache::ObjectPtr value,
|
||||
size_t charge,
|
||||
const Cache::CacheItemHelper* helper) {
|
||||
callback_state = std::to_string(DecodeKey(key)) + "," +
|
||||
std::to_string(DecodeValue(value)) + "," +
|
||||
std::to_string(charge);
|
||||
assert(helper == &CacheTest::kHelper);
|
||||
};
|
||||
|
||||
std::vector<std::string> inserted;
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Insert(i, i * 2, i + 1);
|
||||
inserted.push_back(std::to_string(i) + "," + std::to_string(i * 2) + "," +
|
||||
std::to_string(i + 1));
|
||||
}
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Cache::Handle* handle = cache_->Lookup(EncodeKey(i));
|
||||
cache_->ApplyToHandle(cache_.get(), handle, callback);
|
||||
EXPECT_EQ(inserted[i], callback_state);
|
||||
cache_->Release(handle);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, DefaultShardBits) {
|
||||
// Prevent excessive allocation (to save time & space)
|
||||
estimated_value_size_ = 100000;
|
||||
@@ -1044,63 +1017,6 @@ INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest,
|
||||
testing::Values(secondary_cache_test_util::kLRU));
|
||||
|
||||
TEST(MiscBlockCacheTest, UncacheAggressivenessAdvisor) {
|
||||
// Aggressiveness to a sequence of Report() calls (as string of 0s and 1s)
|
||||
// exactly until the first ShouldContinue() == false.
|
||||
const std::vector<std::pair<uint32_t, Slice>> expectedTraces{
|
||||
// Aggressiveness 1 aborts on first unsuccessful erasure.
|
||||
{1, "0"},
|
||||
{1, "11111111111111111111110"},
|
||||
// For sufficient evidence, aggressiveness 2 requires a minimum of two
|
||||
// unsuccessful erasures.
|
||||
{2, "00"},
|
||||
{2, "0110"},
|
||||
{2, "1100"},
|
||||
{2, "011111111111111111111111111111111111111111111111111111111111111100"},
|
||||
{2, "0111111111111111111111111111111111110"},
|
||||
// For sufficient evidence, aggressiveness 3 and higher require a minimum
|
||||
// of three unsuccessful erasures.
|
||||
{3, "000"},
|
||||
{3, "01010"},
|
||||
{3, "111000"},
|
||||
{3, "00111111111111111111111111111111111100"},
|
||||
{3, "00111111111111111111110"},
|
||||
|
||||
{4, "000"},
|
||||
{4, "01010"},
|
||||
{4, "111000"},
|
||||
{4, "001111111111111111111100"},
|
||||
{4, "0011111111111110"},
|
||||
|
||||
{6, "000"},
|
||||
{6, "01010"},
|
||||
{6, "111000"},
|
||||
{6, "00111111111111100"},
|
||||
{6, "0011111110"},
|
||||
|
||||
// 69 -> 50% threshold, now up to minimum of 4
|
||||
{69, "0000"},
|
||||
{69, "010000"},
|
||||
{69, "01010000"},
|
||||
{69, "101010100010101000"},
|
||||
|
||||
// 230 -> 10% threshold, appropriately higher minimum
|
||||
{230, "000000000000"},
|
||||
{230, "0000000000010000000000"},
|
||||
{230, "00000000000100000000010000000000"}};
|
||||
for (const auto& [aggressiveness, t] : expectedTraces) {
|
||||
SCOPED_TRACE("aggressiveness=" + std::to_string(aggressiveness) + " with " +
|
||||
t.ToString());
|
||||
UncacheAggressivenessAdvisor uaa(aggressiveness);
|
||||
for (size_t i = 0; i < t.size(); ++i) {
|
||||
SCOPED_TRACE("i=" + std::to_string(i));
|
||||
ASSERT_TRUE(uaa.ShouldContinue());
|
||||
uaa.Report(t[i] & 1);
|
||||
}
|
||||
ASSERT_FALSE(uaa.ShouldContinue());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
-16
@@ -1444,22 +1444,6 @@ const Cache::CacheItemHelper* BaseHyperClockCache<Table>::GetCacheItemHelper(
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
template <class Table>
|
||||
void BaseHyperClockCache<Table>::ApplyToHandle(
|
||||
Cache* cache, Handle* handle,
|
||||
const std::function<void(const Slice& key, Cache::ObjectPtr value,
|
||||
size_t charge, const CacheItemHelper* helper)>&
|
||||
callback) {
|
||||
BaseHyperClockCache<Table>* cache_ptr =
|
||||
static_cast<BaseHyperClockCache<Table>*>(cache);
|
||||
auto h = static_cast<const typename Table::HandleImpl*>(handle);
|
||||
UniqueId64x2 unhashed;
|
||||
auto hash_seed = cache_ptr->GetShard(h->GetHash()).GetTable().GetHashSeed();
|
||||
callback(
|
||||
ClockCacheShard<Table>::ReverseHash(h->hashed_key, &unhashed, hash_seed),
|
||||
h->value, h->GetTotalCharge(), h->helper);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// For each cache shard, estimate what the table load factor would be if
|
||||
|
||||
Vendored
-6
@@ -1128,12 +1128,6 @@ class BaseHyperClockCache : public ShardedCache<ClockCacheShard<Table>> {
|
||||
|
||||
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override;
|
||||
|
||||
void ApplyToHandle(
|
||||
Cache* cache, Handle* handle,
|
||||
const std::function<void(const Slice& key, Cache::ObjectPtr obj,
|
||||
size_t charge, const CacheItemHelper* helper)>&
|
||||
callback) override;
|
||||
|
||||
void ReportProblems(
|
||||
const std::shared_ptr<Logger>& /*info_log*/) const override;
|
||||
};
|
||||
|
||||
Vendored
+8
-38
@@ -79,11 +79,7 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
data_ptr = GetVarint32Ptr(data_ptr, data_ptr + 1,
|
||||
static_cast<uint32_t*>(&source_32));
|
||||
source = static_cast<CacheTier>(source_32);
|
||||
uint64_t data_size = 0;
|
||||
data_ptr = GetVarint64Ptr(data_ptr, ptr->get() + handle_value_charge,
|
||||
static_cast<uint64_t*>(&data_size));
|
||||
assert(handle_value_charge > data_size);
|
||||
handle_value_charge = data_size;
|
||||
handle_value_charge -= (data_ptr - ptr->get());
|
||||
}
|
||||
MemoryAllocator* allocator = cache_options_.memory_allocator.get();
|
||||
|
||||
@@ -173,15 +169,13 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
}
|
||||
|
||||
auto internal_helper = GetHelper(cache_options_.enable_custom_split_merge);
|
||||
char header[20];
|
||||
char header[10];
|
||||
char* payload = header;
|
||||
payload = EncodeVarint32(payload, static_cast<uint32_t>(type));
|
||||
payload = EncodeVarint32(payload, static_cast<uint32_t>(source));
|
||||
size_t data_size = (*helper->size_cb)(value);
|
||||
char* data_size_ptr = payload;
|
||||
payload = EncodeVarint64(payload, data_size);
|
||||
|
||||
size_t header_size = payload - header;
|
||||
size_t data_size = (*helper->size_cb)(value);
|
||||
size_t total_size = data_size + header_size;
|
||||
CacheAllocationPtr ptr =
|
||||
AllocateBlock(total_size, cache_options_.memory_allocator.get());
|
||||
@@ -216,8 +210,6 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
|
||||
val = Slice(compressed_val);
|
||||
data_size = compressed_val.size();
|
||||
payload = EncodeVarint64(data_size_ptr, data_size);
|
||||
header_size = payload - header;
|
||||
total_size = header_size + data_size;
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_compressed_bytes, data_size);
|
||||
|
||||
@@ -230,21 +222,14 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_insert_real_count, 1);
|
||||
if (cache_options_.enable_custom_split_merge) {
|
||||
size_t split_charge{0};
|
||||
CacheValueChunk* value_chunks_head = SplitValueIntoChunks(
|
||||
val, cache_options_.compression_type, split_charge);
|
||||
return cache_->Insert(key, value_chunks_head, internal_helper,
|
||||
split_charge);
|
||||
size_t charge{0};
|
||||
CacheValueChunk* value_chunks_head =
|
||||
SplitValueIntoChunks(val, cache_options_.compression_type, charge);
|
||||
return cache_->Insert(key, value_chunks_head, internal_helper, charge);
|
||||
} else {
|
||||
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
size_t charge = malloc_usable_size(ptr.get());
|
||||
#else
|
||||
size_t charge = total_size;
|
||||
#endif
|
||||
std::memcpy(ptr.get(), header, header_size);
|
||||
CacheAllocationPtr* buf = new CacheAllocationPtr(std::move(ptr));
|
||||
charge += sizeof(CacheAllocationPtr);
|
||||
return cache_->Insert(key, buf, internal_helper, charge);
|
||||
return cache_->Insert(key, buf, internal_helper, total_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,21 +398,6 @@ const Cache::CacheItemHelper* CompressedSecondaryCache::GetHelper(
|
||||
}
|
||||
}
|
||||
|
||||
size_t CompressedSecondaryCache::TEST_GetCharge(const Slice& key) {
|
||||
Cache::Handle* lru_handle = cache_->Lookup(key);
|
||||
if (lru_handle == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t charge = cache_->GetCharge(lru_handle);
|
||||
if (cache_->Value(lru_handle) != nullptr &&
|
||||
!cache_options_.enable_custom_split_merge) {
|
||||
charge -= 10;
|
||||
}
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/false);
|
||||
return charge;
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache>
|
||||
CompressedSecondaryCacheOptions::MakeSharedSecondaryCache() const {
|
||||
return std::make_shared<CompressedSecondaryCache>(*this);
|
||||
|
||||
Vendored
-2
@@ -139,8 +139,6 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
const Cache::CacheItemHelper* helper,
|
||||
CompressionType type, CacheTier source);
|
||||
|
||||
size_t TEST_GetCharge(const Slice& key);
|
||||
|
||||
// TODO: clean up to use cleaner interfaces in typed_cache.h
|
||||
const Cache::CacheItemHelper* GetHelper(bool enable_custom_split_merge) const;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
|
||||
-4
@@ -39,8 +39,6 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
protected:
|
||||
void BasicTestHelper(std::shared_ptr<SecondaryCache> sec_cache,
|
||||
bool sec_cache_is_compressed) {
|
||||
CompressedSecondaryCache* comp_sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(sec_cache.get());
|
||||
get_perf_context()->Reset();
|
||||
bool kept_in_sec_cache{true};
|
||||
// Lookup an non-existent key.
|
||||
@@ -68,8 +66,6 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1);
|
||||
|
||||
ASSERT_GT(comp_sec_cache->TEST_GetCharge(key1), 1000);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle1_2 =
|
||||
sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
|
||||
/*stats=*/nullptr, kept_in_sec_cache);
|
||||
|
||||
Vendored
-11
@@ -677,17 +677,6 @@ const Cache::CacheItemHelper* LRUCache::GetCacheItemHelper(
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
void LRUCache::ApplyToHandle(
|
||||
Cache* cache, Handle* handle,
|
||||
const std::function<void(const Slice& key, ObjectPtr value, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback) {
|
||||
auto cache_ptr = static_cast<LRUCache*>(cache);
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
callback(h->key(), h->value,
|
||||
h->GetCharge(cache_ptr->GetShard(0).metadata_charge_policy_),
|
||||
h->helper);
|
||||
}
|
||||
|
||||
size_t LRUCache::TEST_GetLRUSize() {
|
||||
return SumOverShards([](LRUCacheShard& cs) { return cs.TEST_GetLRUSize(); });
|
||||
}
|
||||
|
||||
Vendored
-6
@@ -452,12 +452,6 @@ class LRUCache
|
||||
size_t GetCharge(Handle* handle) const override;
|
||||
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override;
|
||||
|
||||
void ApplyToHandle(
|
||||
Cache* cache, Handle* handle,
|
||||
const std::function<void(const Slice& key, ObjectPtr obj, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback)
|
||||
override;
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only.
|
||||
size_t TEST_GetLRUSize();
|
||||
// Retrieves high pri pool ratio.
|
||||
|
||||
Vendored
+1
-2
@@ -271,8 +271,7 @@ Status CacheWithSecondaryAdapter::Insert(const Slice& key, ObjectPtr value,
|
||||
// Warm up the secondary cache with the compressed block. The secondary
|
||||
// cache may choose to ignore it based on the admission policy.
|
||||
if (value != nullptr && !compressed_value.empty() &&
|
||||
adm_policy_ == TieredAdmissionPolicy::kAdmPolicyThreeQueue &&
|
||||
helper->IsSecondaryCacheCompatible()) {
|
||||
adm_policy_ == TieredAdmissionPolicy::kAdmPolicyThreeQueue) {
|
||||
Status status = secondary_cache_->InsertSaved(key, compressed_value, type);
|
||||
assert(status.ok() || status.IsNotSupported());
|
||||
}
|
||||
|
||||
Vendored
-56
@@ -253,7 +253,6 @@ TEST_F(DBTieredSecondaryCacheTest, BasicTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
// Disable paranoid_file_checks so that flush will not read back the newly
|
||||
@@ -365,7 +364,6 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
@@ -508,7 +506,6 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
@@ -609,7 +606,6 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
@@ -721,7 +717,6 @@ TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
@@ -765,54 +760,6 @@ TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBTieredSecondaryCacheTest, VolatileTierTest) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
// We want a block cache of size 5KB, and a compressed secondary cache of
|
||||
// size 5KB. However, we specify a block cache size of 256KB here in order
|
||||
// to take into account the cache reservation in the block cache on
|
||||
// behalf of the compressed cache. The unit of cache reservation is 256KB.
|
||||
// The effective block cache capacity will be calculated as 256 + 5 = 261KB,
|
||||
// and 256KB will be reserved for the compressed cache, leaving 5KB for
|
||||
// the primary block cache. We only have to worry about this here because
|
||||
// the cache size is so small.
|
||||
table_options.block_cache = NewCache(256 * 1024, 5 * 1024, 256 * 1024);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
// Disable paranoid_file_checks so that flush will not read back the newly
|
||||
// written file
|
||||
options.paranoid_file_checks = false;
|
||||
options.lowest_used_cache_tier = CacheTier::kVolatileTier;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v;
|
||||
test::CompressibleString(&rnd, 0.5, 1007, &p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Since lowest_used_cache_tier is the volatile tier, nothing should be
|
||||
// inserted in the secondary cache.
|
||||
std::string v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 0u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 0u);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
class DBTieredAdmPolicyTest
|
||||
: public DBTieredSecondaryCacheTest,
|
||||
public testing::WithParamInterface<TieredAdmissionPolicy> {};
|
||||
@@ -837,7 +784,6 @@ TEST_P(DBTieredAdmPolicyTest, CompressedOnlyTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
|
||||
@@ -890,7 +836,6 @@ TEST_P(DBTieredAdmPolicyTest, CompressedCacheAdmission) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
|
||||
@@ -992,7 +937,6 @@ TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compression = kLZ4Compression;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.env = wrap_env.get();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import optparse
|
||||
import re
|
||||
@@ -108,11 +109,11 @@ def report_coverage():
|
||||
|
||||
# Check if we need to display coverage info for interested files.
|
||||
if len(interested_files):
|
||||
per_file_coverage = {
|
||||
fname: per_file_coverage[fname]
|
||||
per_file_coverage = dict(
|
||||
(fname, per_file_coverage[fname])
|
||||
for fname in interested_files
|
||||
if fname in per_file_coverage
|
||||
}
|
||||
)
|
||||
# If we only interested in several files, it makes no sense to report
|
||||
# the total_coverage
|
||||
total_coverage = None
|
||||
|
||||
+13
-16
@@ -45,23 +45,20 @@ void ArenaWrappedDBIter::Init(
|
||||
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) {
|
||||
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);
|
||||
sv_number_ = version_number;
|
||||
read_options_ = read_options;
|
||||
allow_refresh_ = allow_refresh;
|
||||
memtable_range_tombstone_iter_ = nullptr;
|
||||
|
||||
if (!CheckFSFeatureSupport(env->GetFileSystem().get(),
|
||||
FSSupportedOps::kAsyncIO)) {
|
||||
read_options_.async_io = false;
|
||||
}
|
||||
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);
|
||||
|
||||
sv_number_ = version_number;
|
||||
allow_refresh_ = allow_refresh;
|
||||
memtable_range_tombstone_iter_ = nullptr;
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh() { return Refresh(nullptr); }
|
||||
@@ -141,10 +138,10 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
reinit_internal_iter();
|
||||
break;
|
||||
} else {
|
||||
*memtable_range_tombstone_iter_ =
|
||||
std::make_unique<TruncatedRangeDelIterator>(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
|
||||
&cfd->internal_comparator(), nullptr, nullptr);
|
||||
delete *memtable_range_tombstone_iter_;
|
||||
*memtable_range_tombstone_iter_ = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
|
||||
&cfd->internal_comparator(), nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
|
||||
@@ -55,8 +55,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
db_iter_->SetIter(iter);
|
||||
}
|
||||
|
||||
void SetMemtableRangetombstoneIter(
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* iter) {
|
||||
void SetMemtableRangetombstoneIter(TruncatedRangeDelIterator** iter) {
|
||||
memtable_range_tombstone_iter_ = iter;
|
||||
}
|
||||
|
||||
@@ -83,8 +82,6 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
Status Refresh() override;
|
||||
Status Refresh(const Snapshot*) override;
|
||||
|
||||
bool PrepareValue() override { return db_iter_->PrepareValue(); }
|
||||
|
||||
void Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
@@ -113,8 +110,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
bool allow_refresh_ = 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;
|
||||
TruncatedRangeDelIterator** memtable_range_tombstone_iter_ = nullptr;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
|
||||
@@ -13,11 +13,14 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class AttributeGroupIteratorImpl : public AttributeGroupIterator {
|
||||
public:
|
||||
AttributeGroupIteratorImpl(
|
||||
const ReadOptions& read_options, const Comparator* comparator,
|
||||
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
|
||||
cfh_iter_pairs)
|
||||
: impl_(read_options, comparator, std::move(cfh_iter_pairs),
|
||||
ResetFunc(this), PopulateFunc(this)) {}
|
||||
const Comparator* comparator,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
const std::vector<Iterator*>& child_iterators)
|
||||
: impl_(
|
||||
comparator, column_families, child_iterators, [this]() { Reset(); },
|
||||
[this](const autovector<MultiCfIteratorInfo>& items) {
|
||||
AddToAttributeGroups(items);
|
||||
}) {}
|
||||
~AttributeGroupIteratorImpl() override {}
|
||||
|
||||
// No copy allowed
|
||||
@@ -42,36 +45,8 @@ class AttributeGroupIteratorImpl : public AttributeGroupIterator {
|
||||
|
||||
void Reset() { attribute_groups_.clear(); }
|
||||
|
||||
bool PrepareValue() override { return impl_.PrepareValue(); }
|
||||
|
||||
private:
|
||||
class ResetFunc {
|
||||
public:
|
||||
explicit ResetFunc(AttributeGroupIteratorImpl* iter) : iter_(iter) {}
|
||||
|
||||
void operator()() const {
|
||||
assert(iter_);
|
||||
iter_->Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
AttributeGroupIteratorImpl* iter_;
|
||||
};
|
||||
|
||||
class PopulateFunc {
|
||||
public:
|
||||
explicit PopulateFunc(AttributeGroupIteratorImpl* iter) : iter_(iter) {}
|
||||
|
||||
void operator()(const autovector<MultiCfIteratorInfo>& items) const {
|
||||
assert(iter_);
|
||||
iter_->AddToAttributeGroups(items);
|
||||
}
|
||||
|
||||
private:
|
||||
AttributeGroupIteratorImpl* iter_;
|
||||
};
|
||||
|
||||
MultiCfIteratorImpl<ResetFunc, PopulateFunc> impl_;
|
||||
MultiCfIteratorImpl impl_;
|
||||
IteratorAttributeGroups attribute_groups_;
|
||||
void AddToAttributeGroups(const autovector<MultiCfIteratorInfo>& items);
|
||||
};
|
||||
|
||||
@@ -42,7 +42,6 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
assert(blob_file_reader);
|
||||
assert(blob_file_reader->IsEmpty());
|
||||
|
||||
// NOTE: sharing same Cache with table_cache
|
||||
const Slice key = GetSliceForKey(&blob_file_number);
|
||||
|
||||
assert(cache_);
|
||||
@@ -99,13 +98,4 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void BlobFileCache::Evict(uint64_t blob_file_number) {
|
||||
// NOTE: sharing same Cache with table_cache
|
||||
const Slice key = GetSliceForKey(&blob_file_number);
|
||||
|
||||
assert(cache_);
|
||||
|
||||
cache_.get()->Erase(key);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -36,15 +36,6 @@ class BlobFileCache {
|
||||
uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader);
|
||||
|
||||
// Called when a blob file is obsolete to ensure it is removed from the cache
|
||||
// to avoid effectively leaking the open file and assicated memory
|
||||
void Evict(uint64_t blob_file_number);
|
||||
|
||||
// Used to identify cache entries for blob files (not normally useful)
|
||||
static const Cache::CacheItemHelper* GetHelper() {
|
||||
return CacheInterface::GetBasicHelper();
|
||||
}
|
||||
|
||||
private:
|
||||
using CacheInterface =
|
||||
BasicTypedCacheInterface<BlobFileReader, CacheEntryRole::kMisc>;
|
||||
|
||||
@@ -20,24 +20,23 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobSource::BlobSource(const ImmutableOptions& immutable_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
BlobSource::BlobSource(const ImmutableOptions* immutable_options,
|
||||
const std::string& db_id,
|
||||
const std::string& db_session_id,
|
||||
BlobFileCache* blob_file_cache)
|
||||
: db_id_(db_id),
|
||||
db_session_id_(db_session_id),
|
||||
statistics_(immutable_options.statistics.get()),
|
||||
statistics_(immutable_options->statistics.get()),
|
||||
blob_file_cache_(blob_file_cache),
|
||||
blob_cache_(immutable_options.blob_cache),
|
||||
lowest_used_cache_tier_(immutable_options.lowest_used_cache_tier) {
|
||||
blob_cache_(immutable_options->blob_cache),
|
||||
lowest_used_cache_tier_(immutable_options->lowest_used_cache_tier) {
|
||||
auto bbto =
|
||||
mutable_cf_options.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
immutable_options->table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
if (bbto &&
|
||||
bbto->cache_usage_options.options_overrides.at(CacheEntryRole::kBlobCache)
|
||||
.charged == CacheEntryRoleOptions::Decision::kEnabled) {
|
||||
blob_cache_ = SharedCacheInterface{std::make_shared<ChargedCache>(
|
||||
immutable_options.blob_cache, bbto->block_cache)};
|
||||
immutable_options->blob_cache, bbto->block_cache)};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
struct ImmutableOptions;
|
||||
struct MutableCFOptions;
|
||||
class Status;
|
||||
class FilePrefetchBuffer;
|
||||
class Slice;
|
||||
@@ -32,10 +31,7 @@ class Slice;
|
||||
// storage with minimal cost.
|
||||
class BlobSource {
|
||||
public:
|
||||
// NOTE: db_id, db_session_id, and blob_file_cache are saved by reference or
|
||||
// pointer.
|
||||
BlobSource(const ImmutableOptions& immutable_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
BlobSource(const ImmutableOptions* immutable_options,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
BlobFileCache* blob_file_cache);
|
||||
|
||||
|
||||
+14
-22
@@ -148,7 +148,6 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -194,8 +193,8 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
@@ -465,7 +464,6 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -500,8 +498,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
column_family_id, nullptr /*HistogramImpl*/, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
@@ -591,7 +589,6 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -647,8 +644,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
@@ -785,7 +782,6 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -831,8 +827,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
column_family_id, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
@@ -1109,7 +1105,6 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -1142,8 +1137,8 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
backing_cache.get(), &immutable_options, &file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/));
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
CacheHandleGuard<BlobFileReader> file_reader;
|
||||
ReadOptions read_options;
|
||||
@@ -1410,7 +1405,6 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr ExpirationRange expiration_range;
|
||||
|
||||
@@ -1432,8 +1426,8 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
kColumnFamilyId, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ConcurrentCacheReservationManager* cache_res_mgr =
|
||||
static_cast<ChargedCache*>(blob_source.GetBlobCache())
|
||||
@@ -1525,8 +1519,6 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
ImmutableOptions immutable_options(options_);
|
||||
MutableCFOptions mutable_cf_options(options_);
|
||||
|
||||
constexpr size_t blob_size = 24 << 10; // 24KB
|
||||
for (size_t i = 0; i < kNumBlobs; ++i) {
|
||||
blob_file_size_ -= blobs_[i].size(); // old blob size
|
||||
@@ -1554,8 +1546,8 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
|
||||
backing_cache.get(), &immutable_options, &file_options,
|
||||
kColumnFamilyId, blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
BlobSource blob_source(immutable_options, mutable_cf_options, db_id_,
|
||||
db_session_id_, blob_file_cache.get());
|
||||
BlobSource blob_source(&immutable_options, db_id_, db_session_id_,
|
||||
blob_file_cache.get());
|
||||
|
||||
ConcurrentCacheReservationManager* cache_res_mgr =
|
||||
static_cast<ChargedCache*>(blob_source.GetBlobCache())
|
||||
|
||||
@@ -374,115 +374,6 @@ TEST_F(DBBlobBasicTest, IterateBlobsFromCachePinning) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, IterateBlobsAllowUnpreparedValue) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr size_t num_blobs = 5;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> blobs;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.emplace_back("key" + std::to_string(i));
|
||||
blobs.emplace_back("blob" + std::to_string(i));
|
||||
ASSERT_OK(Put(keys[i], blobs[i]));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.allow_unprepared_value = true;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(iter->key(), keys[i]);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
|
||||
ASSERT_EQ(iter->key(), keys[i]);
|
||||
ASSERT_EQ(iter->value(), blobs[i]);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
}
|
||||
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
|
||||
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
|
||||
ASSERT_EQ(iter->value(), blobs[num_blobs - 1 - i]);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
}
|
||||
|
||||
{
|
||||
size_t i = 1;
|
||||
|
||||
for (iter->Seek(keys[i]); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(iter->key(), keys[i]);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
|
||||
ASSERT_EQ(iter->key(), keys[i]);
|
||||
ASSERT_EQ(iter->value(), blobs[i]);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
}
|
||||
|
||||
{
|
||||
size_t i = 1;
|
||||
|
||||
for (iter->SeekForPrev(keys[num_blobs - 1 - i]); iter->Valid();
|
||||
iter->Prev()) {
|
||||
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
|
||||
ASSERT_EQ(iter->key(), keys[num_blobs - 1 - i]);
|
||||
ASSERT_EQ(iter->value(), blobs[num_blobs - 1 - i]);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(i, num_blobs);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetBlobs) {
|
||||
constexpr size_t min_blob_size = 6;
|
||||
|
||||
@@ -1764,46 +1655,6 @@ TEST_P(DBBlobBasicIOErrorTest, CompactionFilterReadBlob_IOError) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBBlobBasicIOErrorTest, IterateBlobsAllowUnpreparedValue_IOError) {
|
||||
Options options;
|
||||
options.env = fault_injection_env_.get();
|
||||
options.enable_blob_files = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob_value[] = "blob_value";
|
||||
|
||||
ASSERT_OK(Put(key, blob_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_->SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.allow_unprepared_value = true;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
iter->SeekToFirst();
|
||||
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), key);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_FALSE(iter->PrepareValue());
|
||||
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsIOError());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, WarmCacheWithBlobsDuringFlush) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
|
||||
+26
-15
@@ -53,7 +53,7 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
assert((tboptions.column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
tboptions.column_family_name.empty());
|
||||
return tboptions.moptions.table_factory->NewTableBuilder(tboptions, file);
|
||||
return tboptions.ioptions.table_factory->NewTableBuilder(tboptions, file);
|
||||
}
|
||||
|
||||
Status BuildTable(
|
||||
@@ -64,7 +64,7 @@ Status BuildTable(
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
std::vector<SequenceNumber> snapshots, SequenceNumber earliest_snapshot,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
@@ -195,7 +195,7 @@ Status BuildTable(
|
||||
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
CompactionIterator c_iter(
|
||||
iter, ucmp, &merge, kMaxSequenceNumber, &snapshots, earliest_snapshot,
|
||||
iter, ucmp, &merge, kMaxSequenceNumber, &snapshots,
|
||||
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
|
||||
ShouldReportDetailedTime(env, ioptions.stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
@@ -206,6 +206,10 @@ Status BuildTable(
|
||||
/*compaction=*/nullptr, compaction_filter.get(),
|
||||
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
|
||||
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool logical_strip_timestamp =
|
||||
ts_sz > 0 && !ioptions.persist_user_defined_timestamps;
|
||||
|
||||
SequenceNumber smallest_preferred_seqno = kMaxSequenceNumber;
|
||||
std::string key_after_flush_buf;
|
||||
std::string value_buf;
|
||||
@@ -218,6 +222,16 @@ Status BuildTable(
|
||||
Slice key_after_flush = key_after_flush_buf;
|
||||
Slice value_after_flush = value;
|
||||
|
||||
// If user defined timestamps will be stripped from user key after flush,
|
||||
// the in memory version of the key act logically the same as one with a
|
||||
// minimum timestamp. We update the timestamp here so file boundary and
|
||||
// output validator, block builder all see the effect of the stripping.
|
||||
if (logical_strip_timestamp) {
|
||||
key_after_flush_buf.clear();
|
||||
ReplaceInternalKeyWithMinTimestamp(&key_after_flush_buf, key, ts_sz);
|
||||
key_after_flush = key_after_flush_buf;
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeValuePreferredSeqno) {
|
||||
auto [unpacked_value, unix_write_time] =
|
||||
ParsePackedValueWithWriteTime(value);
|
||||
@@ -277,7 +291,11 @@ Status BuildTable(
|
||||
Slice last_tombstone_start_user_key{};
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
// When user timestamp should not be persisted, we logically strip a
|
||||
// range tombstone's start and end key's timestamp (replace it with min
|
||||
// timestamp) before passing them along to table builder and to update
|
||||
// file boundaries.
|
||||
auto tombstone = range_del_it->Tombstone(logical_strip_timestamp);
|
||||
std::pair<InternalKey, Slice> kv = tombstone.Serialize();
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
@@ -420,7 +438,8 @@ Status BuildTable(
|
||||
// the goal is to cache it here for further user reads.
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
tboptions.read_options, file_options, tboptions.internal_comparator,
|
||||
*meta, nullptr /* range_del_agg */, mutable_cf_options, nullptr,
|
||||
*meta, nullptr /* range_del_agg */,
|
||||
mutable_cf_options.prefix_extractor, nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
@@ -428,7 +447,8 @@ Status BuildTable(
|
||||
MaxFileSizeForL0MetaPin(mutable_cf_options),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr,
|
||||
/*allow_unprepared_value*/ false));
|
||||
/*allow_unprepared_value*/ false,
|
||||
mutable_cf_options.block_protection_bytes_per_key));
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
OutputValidator file_validator(tboptions.internal_comparator,
|
||||
@@ -460,18 +480,9 @@ Status BuildTable(
|
||||
Status prepare =
|
||||
WritableFileWriter::PrepareIOOptions(tboptions.write_options, opts);
|
||||
if (prepare.ok()) {
|
||||
// FIXME: track file for "slow" deletion, e.g. into the
|
||||
// VersionSet::obsolete_files_ pipeline
|
||||
Status ignored = fs->DeleteFile(fname, opts, dbg);
|
||||
ignored.PermitUncheckedError();
|
||||
}
|
||||
// Ensure we don't leak table cache entries when throwing away output
|
||||
// files. (The usual logic in PurgeObsoleteFiles is not applicable because
|
||||
// this function deletes the obsolete file itself, while they should
|
||||
// probably go into the VersionSet::obsolete_files_ pipeline.)
|
||||
TableCache::ReleaseObsolete(table_cache->get_cache().get(),
|
||||
meta->fd.GetNumber(), nullptr /*handle*/,
|
||||
mutable_cf_options.uncache_aggressiveness);
|
||||
}
|
||||
|
||||
assert(blob_file_additions || blob_file_paths.empty());
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ Status BuildTable(
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
std::vector<SequenceNumber> snapshots, SequenceNumber earliest_snapshot,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
|
||||
@@ -231,7 +231,6 @@ struct rocksdb_livefiles_t {
|
||||
};
|
||||
struct rocksdb_column_family_handle_t {
|
||||
ColumnFamilyHandle* rep;
|
||||
bool immortal; /* only true for default cf */
|
||||
};
|
||||
struct rocksdb_column_family_metadata_t {
|
||||
ColumnFamilyMetaData rep;
|
||||
@@ -532,42 +531,6 @@ struct rocksdb_universal_compaction_options_t {
|
||||
ROCKSDB_NAMESPACE::CompactionOptionsUniversal* rep;
|
||||
};
|
||||
|
||||
struct rocksdb_callback_logger_t : public Logger {
|
||||
static const ssize_t STACK_BUFSZ = 512;
|
||||
rocksdb_callback_logger_t(InfoLogLevel log_level,
|
||||
void (*logv_cb)(void*, unsigned, char*, size_t),
|
||||
void* priv)
|
||||
: Logger(log_level), logv_cb_(logv_cb), priv_(priv) {}
|
||||
|
||||
using Logger::Logv;
|
||||
void Logv(const InfoLogLevel level, const char* fmt, va_list ap0) override {
|
||||
char stack_buf[STACK_BUFSZ];
|
||||
char* alloc_buf = nullptr;
|
||||
char* buf = stack_buf;
|
||||
int len = 0;
|
||||
va_list ap1;
|
||||
if (!logv_cb_) return;
|
||||
va_copy(ap1, ap0);
|
||||
len = vsnprintf(buf, STACK_BUFSZ, fmt, ap0);
|
||||
if (len <= 0)
|
||||
goto cleanup;
|
||||
else if (len >= STACK_BUFSZ) {
|
||||
buf = alloc_buf = reinterpret_cast<char*>(malloc(len + 1));
|
||||
if (!buf) goto cleanup;
|
||||
len = vsnprintf(buf, len + 1, fmt, ap1);
|
||||
if (len <= 0) goto cleanup;
|
||||
}
|
||||
logv_cb_(priv_, unsigned(level), buf, size_t(len));
|
||||
cleanup:
|
||||
va_end(ap1);
|
||||
free(alloc_buf);
|
||||
}
|
||||
|
||||
private:
|
||||
void (*logv_cb_)(void*, unsigned, char*, size_t) = nullptr;
|
||||
void* priv_ = nullptr;
|
||||
};
|
||||
|
||||
static bool SaveError(char** errptr, const Status& s) {
|
||||
assert(errptr != nullptr);
|
||||
if (s.ok()) {
|
||||
@@ -583,7 +546,6 @@ static bool SaveError(char** errptr, const Status& s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copies str to a new malloc()-ed buffer. The buffer is not NUL terminated.
|
||||
static char* CopyString(const std::string& str) {
|
||||
char* result = reinterpret_cast<char*>(malloc(sizeof(char) * str.size()));
|
||||
memcpy(result, str.data(), sizeof(char) * str.size());
|
||||
@@ -944,7 +906,6 @@ rocksdb_t* rocksdb_open_and_trim_history(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -975,7 +936,6 @@ rocksdb_t* rocksdb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1011,7 +971,6 @@ rocksdb_t* rocksdb_open_column_families_with_ttl(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1045,7 +1004,6 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1077,7 +1035,6 @@ rocksdb_t* rocksdb_open_as_secondary_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1115,7 +1072,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family(
|
||||
SaveError(errptr, db->rep->CreateColumnFamily(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep)));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -1139,7 +1095,6 @@ rocksdb_column_family_handle_t** rocksdb_create_column_families(
|
||||
for (size_t i = 0; i != handles.size(); ++i) {
|
||||
c_handles[i] = new rocksdb_column_family_handle_t;
|
||||
c_handles[i]->rep = handles[i];
|
||||
c_handles[i]->immortal = false;
|
||||
}
|
||||
|
||||
return c_handles;
|
||||
@@ -1159,7 +1114,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family_with_ttl(
|
||||
SaveError(errptr, db_with_ttl->CreateColumnFamilyWithTtl(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep), ttl));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -1181,19 +1135,9 @@ char* rocksdb_column_family_handle_get_name(
|
||||
return CopyString(name);
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_get_default_column_family_handle(
|
||||
rocksdb_t* db) {
|
||||
rocksdb_column_family_handle_t* handle = new rocksdb_column_family_handle_t;
|
||||
handle->rep = db->rep->DefaultColumnFamily();
|
||||
handle->immortal = true;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void rocksdb_column_family_handle_destroy(
|
||||
rocksdb_column_family_handle_t* handle) {
|
||||
if (!handle->immortal) {
|
||||
delete handle->rep;
|
||||
}
|
||||
delete handle->rep;
|
||||
delete handle;
|
||||
}
|
||||
|
||||
@@ -1429,18 +1373,6 @@ char* rocksdb_get_cf_with_ts(rocksdb_t* db,
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_get_db_identity(rocksdb_t* db, size_t* id_len) {
|
||||
std::string identity_tmp;
|
||||
Status s = db->rep->GetDbIdentity(identity_tmp);
|
||||
if (!s.ok()) {
|
||||
*id_len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*id_len = identity_tmp.size();
|
||||
return CopyString(identity_tmp);
|
||||
}
|
||||
|
||||
void rocksdb_multi_get(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
size_t num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes, char** values_list,
|
||||
@@ -1838,26 +1770,6 @@ void rocksdb_approximate_sizes_cf(
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf_with_flags(
|
||||
rocksdb_t* db, rocksdb_column_family_handle_t* column_family,
|
||||
int num_ranges, const char* const* range_start_key,
|
||||
const size_t* range_start_key_len, const char* const* range_limit_key,
|
||||
const size_t* range_limit_key_len, uint8_t include_flags, uint64_t* sizes,
|
||||
char** errptr) {
|
||||
Range* ranges = new Range[num_ranges];
|
||||
for (int i = 0; i < num_ranges; i++) {
|
||||
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
|
||||
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
|
||||
}
|
||||
Status s = db->rep->GetApproximateSizes(
|
||||
column_family->rep, ranges, num_ranges, sizes,
|
||||
static_cast<DB::SizeApproximationFlags>(include_flags));
|
||||
if (!s.ok()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
void rocksdb_delete_file(rocksdb_t* db, const char* name) {
|
||||
db->rep->DeleteFile(name);
|
||||
}
|
||||
@@ -2035,10 +1947,6 @@ void rocksdb_iter_get_error(const rocksdb_iterator_t* iter, char** errptr) {
|
||||
SaveError(errptr, iter->rep->status());
|
||||
}
|
||||
|
||||
void rocksdb_iter_refresh(const rocksdb_iterator_t* iter, char** errptr) {
|
||||
SaveError(errptr, iter->rep->Refresh());
|
||||
}
|
||||
|
||||
rocksdb_writebatch_t* rocksdb_writebatch_create() {
|
||||
return new rocksdb_writebatch_t;
|
||||
}
|
||||
@@ -2050,15 +1958,6 @@ rocksdb_writebatch_t* rocksdb_writebatch_create_from(const char* rep,
|
||||
return b;
|
||||
}
|
||||
|
||||
rocksdb_writebatch_t* rocksdb_writebatch_create_with_params(
|
||||
size_t reserved_bytes, size_t max_bytes, size_t protection_bytes_per_key,
|
||||
size_t default_cf_ts_sz) {
|
||||
rocksdb_writebatch_t* b = new rocksdb_writebatch_t;
|
||||
b->rep = WriteBatch(reserved_bytes, max_bytes, protection_bytes_per_key,
|
||||
default_cf_ts_sz);
|
||||
return b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_destroy(rocksdb_writebatch_t* b) { delete b; }
|
||||
|
||||
void rocksdb_writebatch_clear(rocksdb_writebatch_t* b) { b->rep.Clear(); }
|
||||
@@ -2286,32 +2185,6 @@ class H : public WriteBatch::Handler {
|
||||
}
|
||||
};
|
||||
|
||||
class HCF : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen);
|
||||
void (*deleted_cf_)(void*, uint32_t cfid, const char* k, size_t klen);
|
||||
void (*merge_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen);
|
||||
Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
(*put_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
|
||||
value.size());
|
||||
return Status::OK();
|
||||
}
|
||||
Status DeleteCF(uint32_t column_family_id, const Slice& key) override {
|
||||
(*deleted_cf_)(state_, column_family_id, key.data(), key.size());
|
||||
return Status::OK();
|
||||
}
|
||||
Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
(*merge_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
|
||||
value.size());
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
|
||||
void (*put)(void*, const char* k, size_t klen,
|
||||
const char* v, size_t vlen),
|
||||
@@ -2324,21 +2197,6 @@ void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
|
||||
b->rep.Iterate(&handler);
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_iterate_cf(
|
||||
rocksdb_writebatch_t* b, void* state,
|
||||
void (*put_cf)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen),
|
||||
void (*deleted_cf)(void*, uint32_t cfid, const char* k, size_t klen),
|
||||
void (*merge_cf)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen)) {
|
||||
HCF handler;
|
||||
handler.state_ = state;
|
||||
handler.put_cf_ = put_cf;
|
||||
handler.deleted_cf_ = deleted_cf;
|
||||
handler.merge_cf_ = merge_cf;
|
||||
b->rep.Iterate(&handler);
|
||||
}
|
||||
|
||||
const char* rocksdb_writebatch_data(rocksdb_writebatch_t* b, size_t* size) {
|
||||
*size = b->rep.GetDataSize();
|
||||
return b->rep.Data().c_str();
|
||||
@@ -2365,35 +2223,6 @@ rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create(
|
||||
return b;
|
||||
}
|
||||
|
||||
rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create_with_params(
|
||||
rocksdb_comparator_t* backup_index_comparator, size_t reserved_bytes,
|
||||
unsigned char overwrite_key, size_t max_bytes,
|
||||
size_t protection_bytes_per_key) {
|
||||
rocksdb_writebatch_wi_t* b = new rocksdb_writebatch_wi_t;
|
||||
b->rep = new WriteBatchWithIndex(backup_index_comparator, reserved_bytes,
|
||||
overwrite_key, max_bytes,
|
||||
protection_bytes_per_key);
|
||||
return b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_update_timestamps(
|
||||
rocksdb_writebatch_t* wb, const char* ts, size_t tslen, void* state,
|
||||
size_t (*get_ts_size)(void*, uint32_t), char** errptr) {
|
||||
SaveError(errptr, wb->rep.UpdateTimestamps(
|
||||
Slice(ts, tslen), [&get_ts_size, &state](uint32_t cf) {
|
||||
return (*get_ts_size)(state, cf);
|
||||
}));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_update_timestamps(
|
||||
rocksdb_writebatch_wi_t* wb, const char* ts, size_t tslen, void* state,
|
||||
size_t (*get_ts_size)(void*, uint32_t), char** errptr) {
|
||||
SaveError(errptr, wb->rep->GetWriteBatch()->UpdateTimestamps(
|
||||
Slice(ts, tslen), [&get_ts_size, &state](uint32_t cf) {
|
||||
return (*get_ts_size)(state, cf);
|
||||
}));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_destroy(rocksdb_writebatch_wi_t* b) {
|
||||
if (b->rep) {
|
||||
delete b->rep;
|
||||
@@ -3134,15 +2963,6 @@ rocksdb_logger_t* rocksdb_logger_create_stderr_logger(int log_level,
|
||||
return logger;
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_logger_create_callback_logger(
|
||||
int log_level, void (*callback)(void*, unsigned, char*, size_t),
|
||||
void* priv) {
|
||||
rocksdb_logger_t* logger = new rocksdb_logger_t;
|
||||
logger->rep = std::make_shared<rocksdb_callback_logger_t>(
|
||||
static_cast<InfoLogLevel>(log_level), callback, priv);
|
||||
return logger;
|
||||
}
|
||||
|
||||
void rocksdb_logger_destroy(rocksdb_logger_t* logger) { delete logger; }
|
||||
|
||||
void rocksdb_options_set_env(rocksdb_options_t* opt, rocksdb_env_t* env) {
|
||||
@@ -4065,36 +3885,6 @@ void rocksdb_options_set_plain_table_factory(
|
||||
opt->rep.table_factory.reset(factory);
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_write_dbid_to_manifest(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.write_dbid_to_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_dbid_to_manifest(
|
||||
rocksdb_options_t* opt, unsigned char write_dbid_to_manifest) {
|
||||
opt->rep.write_dbid_to_manifest = write_dbid_to_manifest;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_write_identity_file(rocksdb_options_t* opt) {
|
||||
return opt->rep.write_identity_file;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_identity_file(
|
||||
rocksdb_options_t* opt, unsigned char write_identity_file) {
|
||||
opt->rep.write_identity_file = write_identity_file;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_track_and_verify_wals_in_manifest(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.track_and_verify_wals_in_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_track_and_verify_wals_in_manifest(
|
||||
rocksdb_options_t* opt, unsigned char track_and_verify_wals_in_manifest) {
|
||||
opt->rep.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_successive_merges(rocksdb_options_t* opt,
|
||||
size_t v) {
|
||||
opt->rep.max_successive_merges = v;
|
||||
@@ -4474,8 +4264,13 @@ void rocksdb_perfcontext_destroy(rocksdb_perfcontext_t* context) {
|
||||
|
||||
/*
|
||||
TODO:
|
||||
DB::OpenForReadOnly
|
||||
DB::KeyMayExist
|
||||
DB::GetOptions
|
||||
DB::GetSortedWalFiles
|
||||
DB::GetLatestSequenceNumber
|
||||
DB::GetUpdatesSince
|
||||
DB::GetDbIdentity
|
||||
DB::RunManualCompaction
|
||||
custom cache
|
||||
table_properties_collectors
|
||||
@@ -5952,7 +5747,6 @@ rocksdb_column_family_handle_t* rocksdb_transactiondb_create_column_family(
|
||||
SaveError(errptr, txn_db->rep->CreateColumnFamily(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep)));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -5995,7 +5789,6 @@ rocksdb_transactiondb_t* rocksdb_transactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_transactiondb_t* result = new rocksdb_transactiondb_t;
|
||||
@@ -6790,7 +6583,6 @@ rocksdb_optimistictransactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_optimistictransactiondb_t* result =
|
||||
|
||||
+1
-200
@@ -199,79 +199,6 @@ static void CheckDel(void* ptr, const char* k, size_t klen) {
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckPutCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 0:
|
||||
CheckEqual("bar", k, klen);
|
||||
CheckEqual("b", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 1:
|
||||
CheckEqual("box", k, klen);
|
||||
CheckEqual("c", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 4:
|
||||
CheckEqual("foo", k, klen);
|
||||
CheckEqual("f", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
case 6:
|
||||
CheckEqual("baz", k, klen);
|
||||
CheckEqual("a", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckDelCF(void* ptr, uint32_t cfid, const char* k, size_t klen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 2:
|
||||
CheckEqual("bar", k, klen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 5:
|
||||
CheckEqual("foo", k, klen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckMergeCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 3:
|
||||
CheckEqual("box", k, klen);
|
||||
CheckEqual("cc", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 7:
|
||||
CheckEqual("baz", k, klen);
|
||||
CheckEqual("aa", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
static void CmpDestroy(void* arg) { (void)arg; }
|
||||
|
||||
static int CmpCompare(void* arg, const char* a, size_t alen, const char* b,
|
||||
@@ -772,8 +699,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_write_buffer_size(options, 100000);
|
||||
rocksdb_options_set_paranoid_checks(options, 1);
|
||||
rocksdb_options_set_max_open_files(options, 10);
|
||||
/* Compatibility with how test was written */
|
||||
rocksdb_options_set_write_dbid_to_manifest(options, 0);
|
||||
|
||||
table_options = rocksdb_block_based_options_create();
|
||||
rocksdb_block_based_options_set_block_cache(table_options, cache);
|
||||
@@ -873,11 +798,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_delete(db, woptions, "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// get the identity before the backup
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
rocksdb_close(db);
|
||||
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
@@ -898,14 +818,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
// the db_identity after the backup is different
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) != 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
|
||||
rocksdb_backup_engine_close(be);
|
||||
}
|
||||
|
||||
@@ -921,14 +833,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
checkpoint = NULL;
|
||||
|
||||
// get the identity before the checkpoint
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
// start a new database from the checkpoint
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
@@ -937,13 +841,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
// the db_identity after the checkpoint is different
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) != 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbcheckpointname, &err);
|
||||
@@ -954,82 +852,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("checkpoint_db_id_in_manifest");
|
||||
{
|
||||
// create new DB with set_write_dbid_to_manifest=true
|
||||
// db_identity is now the same across checkpoints
|
||||
rocksdb_close(db);
|
||||
db = NULL;
|
||||
|
||||
rocksdb_options_t* options_dbid_in_manifest = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options_dbid_in_manifest, 1);
|
||||
|
||||
rocksdb_options_set_write_dbid_to_manifest(options_dbid_in_manifest, false);
|
||||
unsigned char write_to_manifest =
|
||||
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
|
||||
CheckCondition(!write_to_manifest);
|
||||
rocksdb_options_set_write_dbid_to_manifest(options_dbid_in_manifest, true);
|
||||
write_to_manifest =
|
||||
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
|
||||
CheckCondition(write_to_manifest);
|
||||
|
||||
rocksdb_options_set_write_identity_file(options_dbid_in_manifest, true);
|
||||
unsigned char write_identity_file =
|
||||
rocksdb_options_get_write_identity_file(options_dbid_in_manifest);
|
||||
CheckCondition(write_identity_file);
|
||||
rocksdb_options_set_write_identity_file(options_dbid_in_manifest, false);
|
||||
write_identity_file =
|
||||
rocksdb_options_get_write_identity_file(options_dbid_in_manifest);
|
||||
CheckCondition(!write_identity_file);
|
||||
|
||||
db = rocksdb_open(options_dbid_in_manifest, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_t* checkpoint =
|
||||
rocksdb_checkpoint_object_create(db, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
checkpoint = NULL;
|
||||
|
||||
// get the identity before the backup
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
// open the checkpoint
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options_dbid_in_manifest, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_error_if_exists(options_dbid_in_manifest, 0);
|
||||
db = rocksdb_open(options_dbid_in_manifest, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// the db_identity after the checkpoint is the same
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) == 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options_dbid_in_manifest, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_options_destroy(options_dbid_in_manifest);
|
||||
options_dbid_in_manifest = NULL;
|
||||
|
||||
// re-open the default database
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("compactall");
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
@@ -1772,21 +1594,6 @@ int main(int argc, char** argv) {
|
||||
CheckPinGetCF(db, roptions, handles[1], "bar", NULL);
|
||||
CheckPinGetCF(db, roptions, handles[1], "box", "c");
|
||||
CheckPinGetCF(db, roptions, handles[1], "buff", "rocksdb");
|
||||
rocksdb_writebatch_clear(wb);
|
||||
// Test WriteBatch iteration with Column Family
|
||||
int pos = 0;
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
|
||||
rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
|
||||
rocksdb_writebatch_merge_cf(wb, handles[1], "box", 3, "cc", 2);
|
||||
rocksdb_writebatch_put(wb, "foo", 3, "f", 1);
|
||||
rocksdb_writebatch_delete(wb, "foo", 3);
|
||||
rocksdb_writebatch_put(wb, "baz", 3, "a", 1);
|
||||
rocksdb_writebatch_merge(wb, "baz", 3, "aa", 2);
|
||||
rocksdb_writebatch_iterate_cf(wb, &pos, CheckPutCF, CheckDelCF,
|
||||
CheckMergeCF);
|
||||
CheckCondition(pos == 8);
|
||||
rocksdb_writebatch_clear(wb);
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
|
||||
rocksdb_flush_wal(db, 1, &err);
|
||||
@@ -2333,12 +2140,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(rocksdb_statistics_level_all ==
|
||||
rocksdb_options_get_statistics_level(o));
|
||||
|
||||
CheckCondition(0 ==
|
||||
rocksdb_options_get_track_and_verify_wals_in_manifest(o));
|
||||
rocksdb_options_set_track_and_verify_wals_in_manifest(o, 42);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_track_and_verify_wals_in_manifest(o));
|
||||
|
||||
/* Blob Options */
|
||||
rocksdb_options_set_enable_blob_files(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_blob_files(o));
|
||||
|
||||
+10
-35
@@ -9,14 +9,17 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// EXPERIMENTAL
|
||||
class CoalescingIterator : public Iterator {
|
||||
public:
|
||||
CoalescingIterator(
|
||||
const ReadOptions& read_options, const Comparator* comparator,
|
||||
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>&&
|
||||
cfh_iter_pairs)
|
||||
: impl_(read_options, comparator, std::move(cfh_iter_pairs),
|
||||
ResetFunc(this), PopulateFunc(this)) {}
|
||||
CoalescingIterator(const Comparator* comparator,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
const std::vector<Iterator*>& child_iterators)
|
||||
: impl_(
|
||||
comparator, column_families, child_iterators, [this]() { Reset(); },
|
||||
[this](const autovector<MultiCfIteratorInfo>& items) {
|
||||
Coalesce(items);
|
||||
}) {}
|
||||
~CoalescingIterator() override {}
|
||||
|
||||
// No copy allowed
|
||||
@@ -47,36 +50,8 @@ class CoalescingIterator : public Iterator {
|
||||
wide_columns_.clear();
|
||||
}
|
||||
|
||||
bool PrepareValue() override { return impl_.PrepareValue(); }
|
||||
|
||||
private:
|
||||
class ResetFunc {
|
||||
public:
|
||||
explicit ResetFunc(CoalescingIterator* iter) : iter_(iter) {}
|
||||
|
||||
void operator()() const {
|
||||
assert(iter_);
|
||||
iter_->Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
CoalescingIterator* iter_;
|
||||
};
|
||||
|
||||
class PopulateFunc {
|
||||
public:
|
||||
explicit PopulateFunc(CoalescingIterator* iter) : iter_(iter) {}
|
||||
|
||||
void operator()(const autovector<MultiCfIteratorInfo>& items) const {
|
||||
assert(iter_);
|
||||
iter_->Coalesce(items);
|
||||
}
|
||||
|
||||
private:
|
||||
CoalescingIterator* iter_;
|
||||
};
|
||||
|
||||
MultiCfIteratorImpl<ResetFunc, PopulateFunc> impl_;
|
||||
MultiCfIteratorImpl impl_;
|
||||
Slice value_;
|
||||
WideColumns wide_columns_;
|
||||
|
||||
|
||||
+32
-54
@@ -466,7 +466,7 @@ void SuperVersion::Cleanup() {
|
||||
// decrement reference to the immutable MemtableList
|
||||
// this SV object was pointing to.
|
||||
imm->Unref(&to_delete);
|
||||
ReadOnlyMemTable* m = mem->Unref();
|
||||
MemTable* m = mem->Unref();
|
||||
if (m != nullptr) {
|
||||
auto* memory_usage = current->cfd()->imm()->current_memory_usage();
|
||||
assert(*memory_usage >= m->ApproximateMemoryUsage());
|
||||
@@ -538,7 +538,6 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
refs_(0),
|
||||
initialized_(false),
|
||||
dropped_(false),
|
||||
flush_skip_reschedule_(false),
|
||||
internal_comparator_(cf_options.comparator),
|
||||
initial_cf_options_(SanitizeOptions(db_options, cf_options)),
|
||||
ioptions_(db_options, initial_cf_options_),
|
||||
@@ -595,8 +594,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
blob_file_cache_.reset(
|
||||
new BlobFileCache(_table_cache, ioptions(), soptions(), id_,
|
||||
internal_stats_->GetBlobFileReadHist(), io_tracer));
|
||||
blob_source_.reset(new BlobSource(ioptions_, mutable_cf_options_, db_id,
|
||||
db_session_id, blob_file_cache_.get()));
|
||||
blob_source_.reset(new BlobSource(ioptions(), db_id, db_session_id,
|
||||
blob_file_cache_.get()));
|
||||
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
@@ -693,9 +692,9 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
if (mem_ != nullptr) {
|
||||
delete mem_->Unref();
|
||||
}
|
||||
autovector<ReadOnlyMemTable*> to_delete;
|
||||
autovector<MemTable*> to_delete;
|
||||
imm_.current()->Unref(&to_delete);
|
||||
for (auto* m : to_delete) {
|
||||
for (MemTable* m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
|
||||
@@ -901,11 +900,7 @@ uint64_t GetPendingCompactionBytesForCompactionSpeedup(
|
||||
return slowdown_threshold;
|
||||
}
|
||||
|
||||
// Prevent a small CF from triggering parallel compactions for other CFs.
|
||||
// Require compaction debt to be more than a full L0 to Lbase compaction.
|
||||
const uint64_t kMinDebtSize = 2 * mutable_cf_options.max_bytes_for_level_base;
|
||||
uint64_t size_threshold =
|
||||
std::max(bottommost_files_size / kBottommostSizeDivisor, kMinDebtSize);
|
||||
uint64_t size_threshold = bottommost_files_size / kBottommostSizeDivisor;
|
||||
return std::min(size_threshold, slowdown_threshold);
|
||||
}
|
||||
|
||||
@@ -1176,12 +1171,10 @@ bool ColumnFamilyData::NeedsCompaction() const {
|
||||
|
||||
Compaction* ColumnFamilyData::PickCompaction(
|
||||
const MutableCFOptions& mutable_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer) {
|
||||
const MutableDBOptions& mutable_db_options, LogBuffer* log_buffer) {
|
||||
auto* result = compaction_picker_->PickCompaction(
|
||||
GetName(), mutable_options, mutable_db_options, existing_snapshots,
|
||||
snapshot_checker, current_->storage_info(), log_buffer);
|
||||
GetName(), mutable_options, mutable_db_options, current_->storage_info(),
|
||||
log_buffer);
|
||||
if (result != nullptr) {
|
||||
result->FinalizeInputInfo(current_);
|
||||
}
|
||||
@@ -1207,10 +1200,8 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
read_opts.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&internal_comparator_, &arena);
|
||||
merge_iter_builder.AddIterator(super_version->mem->NewIterator(
|
||||
read_opts, /*seqno_to_time_mapping=*/nullptr, &arena,
|
||||
/*prefix_extractor=*/nullptr));
|
||||
read_opts, /*seqno_to_time_mapping=*/nullptr, &arena));
|
||||
super_version->imm->AddIterators(read_opts, /*seqno_to_time_mapping=*/nullptr,
|
||||
/*prefix_extractor=*/nullptr,
|
||||
&merge_iter_builder,
|
||||
false /* add_range_tombstone_iter */);
|
||||
ScopedArenaPtr<InternalIterator> memtable_iter(merge_iter_builder.Finish());
|
||||
@@ -1537,20 +1528,6 @@ Status ColumnFamilyData::ValidateOptions(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cf_options.compaction_style == kCompactionStyleUniversal) {
|
||||
int max_read_amp = cf_options.compaction_options_universal.max_read_amp;
|
||||
if (max_read_amp < -1) {
|
||||
return Status::NotSupported(
|
||||
"CompactionOptionsUniversal::max_read_amp should be at least -1.");
|
||||
} else if (0 < max_read_amp &&
|
||||
max_read_amp < cf_options.level0_file_num_compaction_trigger) {
|
||||
return Status::NotSupported(
|
||||
"CompactionOptionsUniversal::max_read_amp limits the number of sorted"
|
||||
" runs but is smaller than the compaction trigger "
|
||||
"level0_file_num_compaction_trigger.");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1561,11 +1538,6 @@ Status ColumnFamilyData::SetOptions(
|
||||
BuildColumnFamilyOptions(initial_cf_options_, mutable_cf_options_);
|
||||
ConfigOptions config_opts;
|
||||
config_opts.mutable_options_only = true;
|
||||
#ifndef NDEBUG
|
||||
if (TEST_allowSetOptionsImmutableInMutable) {
|
||||
config_opts.mutable_options_only = false;
|
||||
}
|
||||
#endif
|
||||
Status s = GetColumnFamilyOptionsFromMap(config_opts, cf_opts, options_map,
|
||||
&cf_opts);
|
||||
if (s.ok()) {
|
||||
@@ -1578,6 +1550,28 @@ Status ColumnFamilyData::SetOptions(
|
||||
return s;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
Env::WriteLifeTimeHint ColumnFamilyData::CalculateSSTWriteHint(int level) {
|
||||
if (initial_cf_options_.compaction_style != kCompactionStyleLevel) {
|
||||
return Env::WLTH_NOT_SET;
|
||||
}
|
||||
if (level == 0) {
|
||||
return Env::WLTH_MEDIUM;
|
||||
}
|
||||
int base_level = current_->storage_info()->base_level();
|
||||
|
||||
// L1: medium, L2: long, ...
|
||||
if (level - base_level >= 2) {
|
||||
return Env::WLTH_EXTREME;
|
||||
} else if (level < base_level) {
|
||||
// There is no restriction which prevents level passed in to be smaller
|
||||
// than base_level.
|
||||
return Env::WLTH_MEDIUM;
|
||||
}
|
||||
return static_cast<Env::WriteLifeTimeHint>(
|
||||
level - base_level + static_cast<int>(Env::WLTH_MEDIUM));
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::AddDirectories(
|
||||
std::map<std::string, std::shared_ptr<FSDirectory>>* created_dirs) {
|
||||
Status s;
|
||||
@@ -1613,19 +1607,6 @@ FSDirectory* ColumnFamilyData::GetDataDir(size_t path_id) const {
|
||||
return data_dirs_[path_id].get();
|
||||
}
|
||||
|
||||
void ColumnFamilyData::SetFlushSkipReschedule() {
|
||||
const Comparator* ucmp = user_comparator();
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
if (ts_sz == 0 || ioptions_.persist_user_defined_timestamps) {
|
||||
return;
|
||||
}
|
||||
flush_skip_reschedule_.store(true);
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::GetAndClearFlushSkipReschedule() {
|
||||
return flush_skip_reschedule_.exchange(false);
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::ShouldPostponeFlushToRetainUDT(
|
||||
uint64_t max_memtable_id) {
|
||||
const Comparator* ucmp = user_comparator();
|
||||
@@ -1643,9 +1624,6 @@ bool ColumnFamilyData::ShouldPostponeFlushToRetainUDT(
|
||||
}
|
||||
for (const Slice& table_newest_udt :
|
||||
imm()->GetTablesNewestUDT(max_memtable_id)) {
|
||||
if (table_newest_udt.empty()) {
|
||||
continue;
|
||||
}
|
||||
assert(table_newest_udt.size() == full_history_ts_low.size());
|
||||
// Checking the newest UDT contained in MemTable with ascending ID up to
|
||||
// `max_memtable_id`. Return immediately on finding the first MemTable that
|
||||
|
||||
+10
-29
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
@@ -207,7 +206,7 @@ struct SuperVersion {
|
||||
// Accessing members of this class is not thread-safe and requires external
|
||||
// synchronization (ie db mutex held or on write thread).
|
||||
ColumnFamilyData* cfd;
|
||||
ReadOnlyMemTable* mem;
|
||||
MemTable* mem;
|
||||
MemTableListVersion* imm;
|
||||
Version* current;
|
||||
MutableCFOptions mutable_cf_options;
|
||||
@@ -269,7 +268,7 @@ struct SuperVersion {
|
||||
// We need to_delete because during Cleanup(), imm->Unref() returns
|
||||
// all memtables that we need to free through this vector. We then
|
||||
// delete all those memtables outside of mutex, during destruction
|
||||
autovector<ReadOnlyMemTable*> to_delete;
|
||||
autovector<MemTable*> to_delete;
|
||||
};
|
||||
|
||||
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
@@ -330,10 +329,6 @@ class ColumnFamilyData {
|
||||
void SetDropped();
|
||||
bool IsDropped() const { return dropped_.load(std::memory_order_relaxed); }
|
||||
|
||||
void SetFlushSkipReschedule();
|
||||
|
||||
bool GetAndClearFlushSkipReschedule();
|
||||
|
||||
// thread-safe
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
|
||||
@@ -386,9 +381,9 @@ class ColumnFamilyData {
|
||||
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalBlobFileSize() const; // REQUIRE: DB mutex held
|
||||
// REQUIRE: DB mutex held
|
||||
void SetMemtable(MemTable* new_mem) {
|
||||
new_mem->SetID(++last_memtable_id_);
|
||||
uint64_t memtable_id = last_memtable_id_.fetch_add(1) + 1;
|
||||
new_mem->SetID(memtable_id);
|
||||
mem_ = new_mem;
|
||||
}
|
||||
|
||||
@@ -402,18 +397,15 @@ class ColumnFamilyData {
|
||||
SequenceNumber earliest_seq);
|
||||
|
||||
TableCache* table_cache() const { return table_cache_.get(); }
|
||||
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
|
||||
BlobSource* blob_source() const { return blob_source_.get(); }
|
||||
|
||||
// See documentation in compaction_picker.h
|
||||
// REQUIRES: DB mutex held
|
||||
bool NeedsCompaction() const;
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* PickCompaction(
|
||||
const MutableCFOptions& mutable_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, LogBuffer* log_buffer);
|
||||
Compaction* PickCompaction(const MutableCFOptions& mutable_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Check if the passed range overlap with any running compactions.
|
||||
// REQUIRES: DB mutex held
|
||||
@@ -515,6 +507,8 @@ class ColumnFamilyData {
|
||||
return initial_cf_options_;
|
||||
}
|
||||
|
||||
Env::WriteLifeTimeHint CalculateSSTWriteHint(int level);
|
||||
|
||||
// created_dirs remembers directory created, so that we don't need to call
|
||||
// the same data creation operation again.
|
||||
Status AddDirectories(
|
||||
@@ -574,10 +568,6 @@ class ColumnFamilyData {
|
||||
// of its files (if missing)
|
||||
void RecoverEpochNumbers();
|
||||
|
||||
int GetUnflushedMemTableCountForWriteStallCheck() const {
|
||||
return (mem_->IsEmpty() ? 0 : 1) + imm_.NumNotFlushed();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
@@ -602,15 +592,6 @@ class ColumnFamilyData {
|
||||
std::atomic<bool> initialized_;
|
||||
std::atomic<bool> dropped_; // true if client dropped it
|
||||
|
||||
// When user-defined timestamps in memtable only feature is enabled, this
|
||||
// flag indicates a successfully requested flush that should
|
||||
// skip being rescheduled and haven't undergone the rescheduling check yet.
|
||||
// This flag is cleared when a check skips rescheduling a FlushRequest.
|
||||
// With this flag, automatic flushes in regular cases can continue to
|
||||
// retain UDTs by getting rescheduled as usual while manual flushes and
|
||||
// error recovery flushes will proceed without getting rescheduled.
|
||||
std::atomic<bool> flush_skip_reschedule_;
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
InternalTblPropCollFactories internal_tbl_prop_coll_factories_;
|
||||
|
||||
@@ -673,7 +654,7 @@ class ColumnFamilyData {
|
||||
bool allow_2pc_;
|
||||
|
||||
// Memtable id to track flush.
|
||||
uint64_t last_memtable_id_;
|
||||
std::atomic<uint64_t> last_memtable_id_;
|
||||
|
||||
// Directories corresponding to cf_paths.
|
||||
std::vector<std::shared_ptr<FSDirectory>> data_dirs_;
|
||||
|
||||
+74
-347
@@ -23,7 +23,6 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
@@ -35,13 +34,6 @@
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
std::string EncodeAsUint64(uint64_t number) {
|
||||
std::string result;
|
||||
PutFixed64(&result, number);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static const int kValueSize = 1000;
|
||||
|
||||
@@ -3012,25 +3004,19 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
{
|
||||
// 1MB debt is way bigger than bottommost data so definitely triggers
|
||||
// speedup.
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
|
||||
dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
// Eight bytes is way smaller than bottommost data so definitely does not
|
||||
// trigger speedup.
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(8, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
// 1MB is much larger than bottommost level size. However, since it's too
|
||||
// small in terms of absolute size, it does not trigger parallel compaction
|
||||
// in this case (see GetPendingCompactionBytesForCompactionSpeedup()).
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
|
||||
dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(
|
||||
2 * mutable_cf_options.max_bytes_for_level_base, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3073,20 +3059,12 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
|
||||
WaitForCompaction();
|
||||
AssertFilesPerLevel("0,1", 0 /* cf */);
|
||||
|
||||
// We should calculate the limit by obtaining the number of env background
|
||||
// threads, because the current test case will share the same env
|
||||
// with another case that may have already increased the number of
|
||||
// background threads which is larger than kParallelismLimit
|
||||
const auto limit = env_->GetBackgroundThreads(Env::Priority::LOW);
|
||||
|
||||
// Block the compaction thread pool so marked files accumulate in L0.
|
||||
std::vector<std::shared_ptr<test::SleepingBackgroundTask>> sleeping_tasks;
|
||||
for (int i = 0; i < limit; i++) {
|
||||
sleeping_tasks.emplace_back(
|
||||
std::make_shared<test::SleepingBackgroundTask>());
|
||||
test::SleepingBackgroundTask sleeping_tasks[kParallelismLimit];
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
sleeping_tasks[i].get(), Env::Priority::LOW);
|
||||
sleeping_tasks[i]->WaitUntilSleeping();
|
||||
&sleeping_tasks[i], Env::Priority::LOW);
|
||||
sleeping_tasks[i].WaitUntilSleeping();
|
||||
}
|
||||
|
||||
// Zero marked upper-level files. No speedup.
|
||||
@@ -3105,9 +3083,9 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
|
||||
ASSERT_EQ(kParallelismLimit, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("2,1", 0 /* cf */);
|
||||
|
||||
for (int i = 0; i < limit; i++) {
|
||||
sleeping_tasks[i]->WakeUp();
|
||||
sleeping_tasks[i]->WaitUntilDone();
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
sleeping_tasks[i].WakeUp();
|
||||
sleeping_tasks[i].WaitUntilDone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3192,8 +3170,6 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile1) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(2));
|
||||
Open();
|
||||
@@ -3246,8 +3222,6 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile2) {
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(2));
|
||||
Open();
|
||||
@@ -3305,8 +3279,6 @@ TEST_P(ColumnFamilyTest, ForwardIteratorCloseWALFile) {
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(3));
|
||||
column_family_options_.level0_file_num_compaction_trigger = 2;
|
||||
@@ -3631,9 +3603,7 @@ TEST(ColumnFamilyTest, ValidateMemtableKVChecksumOption) {
|
||||
}
|
||||
|
||||
// Tests the flushing behavior of a column family to retain user-defined
|
||||
// timestamp when `persist_user_defined_timestamp` is false. The behavior of
|
||||
// auto flush is it makes some effort to retain user-defined timestamps while
|
||||
// the behavior of manual flush is that it skips retaining UDTs.
|
||||
// timestamp when `persist_user_defined_timestamp` is false.
|
||||
class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
|
||||
public:
|
||||
ColumnFamilyRetainUDTTest() : ColumnFamilyTestBase(kLatestFormatVersion) {}
|
||||
@@ -3651,27 +3621,6 @@ class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
|
||||
return db_->Put(WriteOptions(), handles_[cf], Slice(key), Slice(ts),
|
||||
Slice(value));
|
||||
}
|
||||
|
||||
std::string Get(int cf, const std::string& key, const std::string& read_ts) {
|
||||
ReadOptions ropts;
|
||||
Slice timestamp = read_ts;
|
||||
ropts.timestamp = ×tamp;
|
||||
std::string value;
|
||||
Status s = db_->Get(ropts, handles_[cf], Slice(key), &value);
|
||||
if (s.IsNotFound()) {
|
||||
return "NOT_FOUND";
|
||||
} else if (s.ok()) {
|
||||
return value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void CheckEffectiveCutoffTime(uint64_t expected_cutoff) {
|
||||
std::string effective_full_history_ts_low;
|
||||
EXPECT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
EXPECT_EQ(EncodeAsUint64(expected_cutoff), effective_full_history_ts_low);
|
||||
}
|
||||
};
|
||||
|
||||
class TestTsComparator : public Comparator {
|
||||
@@ -3715,9 +3664,7 @@ TEST_F(ColumnFamilyRetainUDTTest, SanityCheck) {
|
||||
Close();
|
||||
}
|
||||
|
||||
class AutoFlushRetainUDTTest : public ColumnFamilyRetainUDTTest {};
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3727,22 +3674,28 @@ TEST_F(AutoFlushRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
// No `full_history_ts_low` explicitly set by user, auto flush is continued
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
// No `full_history_ts_low` explicitly set by user, flush is continued
|
||||
// without checking if its UDTs expired.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// After flush, `full_history_ts_low` should be automatically advanced to
|
||||
// the effective cutoff timestamp: write_ts + 1
|
||||
CheckEffectiveCutoffTime(2);
|
||||
std::string cutoff_ts;
|
||||
PutFixed64(&cutoff_ts, 2);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, AllKeysExpired) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, AllKeysExpired) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3752,22 +3705,27 @@ TEST_F(AutoFlushRetainUDTTest, AllKeysExpired) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(3)));
|
||||
// All keys expired w.r.t the configured `full_history_ts_low`, auto flush
|
||||
// continue without the need for a re-schedule.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
std::string cutoff_ts;
|
||||
PutFixed64(&cutoff_ts, 3);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
// All keys expired w.r.t the configured `full_history_ts_low`, flush continue
|
||||
// without the need for a re-schedule.
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// `full_history_ts_low` stays unchanged after flush.
|
||||
CheckEffectiveCutoffTime(3);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3777,303 +3735,72 @@ TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(1)));
|
||||
std::string cutoff_ts;
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
PutFixed64(&cutoff_ts, 1);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
ASSERT_OK(db_->SetOptions(handles_[0], {{"max_write_buffer_number", "1"}}));
|
||||
// Not all keys expired, but auto flush is continued without a re-schedule
|
||||
// because of risk of write stall.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Not all keys expired, but flush is continued without a re-schedule because
|
||||
// of risk of write stall.
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// After flush, `full_history_ts_low` should be automatically advanced to
|
||||
// the effective cutoff timestamp: write_ts + 1
|
||||
CheckEffectiveCutoffTime(2);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
|
||||
cutoff_ts.clear();
|
||||
PutFixed64(&cutoff_ts, 2);
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushRescheduled) {
|
||||
std::atomic<int> local_counter{1};
|
||||
TEST_F(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushRescheduled) {
|
||||
std::string cutoff_ts;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AfterRetainUDTReschedule:cb", [&](void* /*arg*/) {
|
||||
// Increasing full_history_ts_low so all keys expired after the initial
|
||||
// FlushRequest is rescheduled
|
||||
ASSERT_OK(
|
||||
db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(3)));
|
||||
cutoff_ts.clear();
|
||||
PutFixed64(&cutoff_ts, 3);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(2, reschedule_count);
|
||||
local_counter.fetch_add(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(1)));
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
PutFixed64(&cutoff_ts, 1);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
// Not all keys expired, and there is no risk of write stall. Flush is
|
||||
// rescheduled. The actual flush happens after `full_history_ts_low` is
|
||||
// increased to mark all keys expired.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Make sure callback is not skipped.
|
||||
ASSERT_EQ(2, local_counter);
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
CheckEffectiveCutoffTime(3);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
// `full_history_ts_low` stays unchanged.
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
class ManualFlushSkipRetainUDTTest : public ColumnFamilyRetainUDTTest {
|
||||
public:
|
||||
// Write an entry with timestamp that is not expired w.r.t cutoff timestamp,
|
||||
// and make sure automatic flush would be rescheduled to retain UDT.
|
||||
void CheckAutomaticFlushRetainUDT(uint64_t write_ts) {
|
||||
std::atomic<int> local_counter{1};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AfterRetainUDTReschedule:cb", [&](void* /*arg*/) {
|
||||
// Increasing full_history_ts_low so all keys expired after the
|
||||
// initial FlushRequest is rescheduled
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(
|
||||
handles_[0], EncodeAsUint64(write_ts + 1)));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(2, reschedule_count);
|
||||
local_counter.fetch_add(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
EXPECT_OK(Put(0, "foo", EncodeAsUint64(write_ts),
|
||||
"foo" + std::to_string(write_ts)));
|
||||
EXPECT_OK(dbfull()->TEST_SwitchWAL());
|
||||
EXPECT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Make sure callback is not skipped.
|
||||
EXPECT_EQ(2, local_counter);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ManualFlush) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
// Manual flush proceeds without trying to retain UDT.
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(Flush(0));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, FlushRemovesStaleEntries) {
|
||||
column_family_options_.max_write_buffer_number = 4;
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
|
||||
ColumnFamilyData* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
|
||||
for (int version = 0; version < 100; version++) {
|
||||
if (version == 50) {
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db_)->TEST_SwitchMemtable(cfd));
|
||||
}
|
||||
ASSERT_OK(
|
||||
Put(0, "foo", EncodeAsUint64(version), "v" + std::to_string(version)));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush(0));
|
||||
TablePropertiesCollection tables_properties;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&tables_properties));
|
||||
ASSERT_EQ(1, tables_properties.size());
|
||||
std::shared_ptr<const TableProperties> table_properties =
|
||||
tables_properties.begin()->second;
|
||||
ASSERT_EQ(1, table_properties->num_entries);
|
||||
ASSERT_EQ(0, table_properties->num_deletions);
|
||||
ASSERT_EQ(0, table_properties->num_range_deletions);
|
||||
CheckEffectiveCutoffTime(100);
|
||||
CheckAutomaticFlushRetainUDT(101);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, RangeDeletionFlushRemovesStaleEntries) {
|
||||
column_family_options_.max_write_buffer_number = 4;
|
||||
Open();
|
||||
// TODO(yuzhangyu): a non 0 full history ts low is needed for this garbage
|
||||
// collection to kick in. This doesn't work well for the very first flush of
|
||||
// the column family. Not a big issue, but would be nice to improve this.
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(9)));
|
||||
|
||||
for (int i = 10; i < 100; i++) {
|
||||
ASSERT_OK(Put(0, "foo" + std::to_string(i), EncodeAsUint64(i),
|
||||
"val" + std::to_string(i)));
|
||||
if (i % 2 == 1) {
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), "foo" + std::to_string(i - 1),
|
||||
"foo" + std::to_string(i), EncodeAsUint64(i)));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush(0));
|
||||
CheckEffectiveCutoffTime(100);
|
||||
std::string read_ts = EncodeAsUint64(100);
|
||||
std::string min_ts = EncodeAsUint64(0);
|
||||
ReadOptions ropts;
|
||||
Slice read_ts_slice = read_ts;
|
||||
std::string value;
|
||||
ropts.timestamp = &read_ts_slice;
|
||||
{
|
||||
Iterator* iter = db_->NewIterator(ropts);
|
||||
iter->SeekToFirst();
|
||||
int i = 11;
|
||||
while (iter->Valid()) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("foo" + std::to_string(i), iter->key());
|
||||
ASSERT_EQ("val" + std::to_string(i), iter->value());
|
||||
ASSERT_EQ(min_ts, iter->timestamp());
|
||||
iter->Next();
|
||||
i += 2;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
}
|
||||
TablePropertiesCollection tables_properties;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&tables_properties));
|
||||
ASSERT_EQ(1, tables_properties.size());
|
||||
std::shared_ptr<const TableProperties> table_properties =
|
||||
tables_properties.begin()->second;
|
||||
// 45 point data + 45 range deletions. 45 obsolete point data are garbage
|
||||
// collected.
|
||||
ASSERT_EQ(90, table_properties->num_entries);
|
||||
ASSERT_EQ(45, table_properties->num_deletions);
|
||||
ASSERT_EQ(45, table_properties->num_range_deletions);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ManualCompaction) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
// Manual compaction proceeds without trying to retain UDT.
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v2"));
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(CompactRangeOptions(), handles_[0], nullptr, nullptr));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, BulkLoading) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
|
||||
// Test flush behavior in bulk loading scenarios.
|
||||
Options options(db_options_, column_family_options_);
|
||||
std::string sst_files_dir = dbname_ + "/sst_files/";
|
||||
ASSERT_OK(DestroyDir(env_, sst_files_dir));
|
||||
ASSERT_OK(env_->CreateDir(sst_files_dir));
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
std::string file1 = sst_files_dir + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
ASSERT_OK(sst_file_writer.Put("foo", EncodeAsUint64(0), "v2"));
|
||||
ExternalSstFileInfo file1_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file1_info));
|
||||
|
||||
// Bulk loading in UDT mode doesn't support external file key range overlap
|
||||
// with DB key range.
|
||||
ASSERT_TRUE(db_->IngestExternalFile({file1}, IngestExternalFileOptions())
|
||||
.IsInvalidArgument());
|
||||
|
||||
std::string file2 = sst_files_dir + "file2.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
ASSERT_OK(sst_file_writer.Put("bar", EncodeAsUint64(0), "val"));
|
||||
ExternalSstFileInfo file2_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
||||
// A successful bulk loading, and it doesn't trigger any flush. As a result
|
||||
// the effective cutoff timestamp is also unchanged.
|
||||
ASSERT_OK(db_->IngestExternalFile({file2}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get(0, "foo", EncodeAsUint64(1)), "v1");
|
||||
ASSERT_EQ(Get(0, "bar", EncodeAsUint64(0)), "val");
|
||||
CheckEffectiveCutoffTime(0);
|
||||
CheckAutomaticFlushRetainUDT(1);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, AutomaticFlushQueued) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
CheckEffectiveCutoffTime(0);
|
||||
|
||||
// Default `max_write_buffer_number=2` used, writing another memtable can get
|
||||
// automatic flush to proceed because of memory pressure. Not doing that so
|
||||
// we can test automatic flush gets to proceed because of an ongoing manual
|
||||
// flush attempt.
|
||||
ASSERT_OK(Flush(0));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ConcurrentManualFlushes) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
std::vector<ROCKSDB_NAMESPACE::port::Thread> manual_flush_tds;
|
||||
std::atomic<int> next_ts{0};
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
|
||||
auto manual_flush = [&](int write_ts) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock,
|
||||
[&write_ts, &next_ts] { return write_ts == next_ts.load(); });
|
||||
ASSERT_OK(Put(0, "foo" + std::to_string(write_ts),
|
||||
EncodeAsUint64(write_ts),
|
||||
"val_" + std::to_string(write_ts)));
|
||||
next_ts.fetch_add(1);
|
||||
cv.notify_all();
|
||||
}
|
||||
if (write_ts % 2 == 0) {
|
||||
ASSERT_OK(Flush(0));
|
||||
} else {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[0], nullptr,
|
||||
nullptr));
|
||||
}
|
||||
};
|
||||
|
||||
for (int write_ts = 0; write_ts < 10; write_ts++) {
|
||||
manual_flush_tds.emplace_back(manual_flush, write_ts);
|
||||
}
|
||||
|
||||
for (auto& td : manual_flush_tds) {
|
||||
td.join();
|
||||
}
|
||||
|
||||
CheckEffectiveCutoffTime(10);
|
||||
CheckAutomaticFlushRetainUDT(11);
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+21
-137
@@ -63,9 +63,7 @@ uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
// TODO(hx235): consider making this function part of the construction so we
|
||||
// don't forget to call it
|
||||
void Compaction::FinalizeInputInfo(Version* _input_version) {
|
||||
void Compaction::SetInputVersion(Version* _input_version) {
|
||||
input_version_ = _input_version;
|
||||
cfd_ = input_version_->cfd();
|
||||
|
||||
@@ -283,10 +281,9 @@ Compaction::Compaction(
|
||||
uint32_t _output_path_id, CompressionType _compression,
|
||||
CompressionOptions _compression_opts, Temperature _output_temperature,
|
||||
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
|
||||
std::optional<SequenceNumber> _earliest_snapshot,
|
||||
const SnapshotChecker* _snapshot_checker, bool _manual_compaction,
|
||||
const std::string& _trim_ts, double _score, bool _deletion_compaction,
|
||||
bool l0_files_might_overlap, CompactionReason _compaction_reason,
|
||||
bool _manual_compaction, const std::string& _trim_ts, double _score,
|
||||
bool _deletion_compaction, bool l0_files_might_overlap,
|
||||
CompactionReason _compaction_reason,
|
||||
BlobGarbageCollectionPolicy _blob_garbage_collection_policy,
|
||||
double _blob_garbage_collection_age_cutoff)
|
||||
: input_vstorage_(vstorage),
|
||||
@@ -308,8 +305,6 @@ Compaction::Compaction(
|
||||
l0_files_might_overlap_(l0_files_might_overlap),
|
||||
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
|
||||
grandparents_(std::move(_grandparents)),
|
||||
earliest_snapshot_(_earliest_snapshot),
|
||||
snapshot_checker_(_snapshot_checker),
|
||||
score_(_score),
|
||||
bottommost_level_(
|
||||
// For simplicity, we don't support the concept of "bottommost level"
|
||||
@@ -345,9 +340,8 @@ Compaction::Compaction(
|
||||
_compaction_reason == CompactionReason::kExternalSstIngestion ||
|
||||
_compaction_reason == CompactionReason::kRefitLevel
|
||||
? Compaction::kInvalidLevel
|
||||
: EvaluatePenultimateLevel(vstorage, mutable_cf_options_,
|
||||
immutable_options_, start_level_,
|
||||
output_level_)) {
|
||||
: EvaluatePenultimateLevel(vstorage, immutable_options_,
|
||||
start_level_, output_level_)) {
|
||||
MarkFilesBeingCompacted(true);
|
||||
if (is_manual_compaction_) {
|
||||
compaction_reason_ = CompactionReason::kManualCompaction;
|
||||
@@ -368,17 +362,12 @@ Compaction::Compaction(
|
||||
}
|
||||
#endif
|
||||
|
||||
// setup input_levels_ and filtered_input_levels_
|
||||
// setup input_levels_
|
||||
{
|
||||
input_levels_.resize(num_input_levels());
|
||||
filtered_input_levels_.resize(num_input_levels());
|
||||
if (earliest_snapshot_.has_value()) {
|
||||
FilterInputsForCompactionIterator();
|
||||
} else {
|
||||
for (size_t which = 0; which < num_input_levels(); which++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
|
||||
&arena_);
|
||||
}
|
||||
for (size_t which = 0; which < num_input_levels(); which++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
|
||||
&arena_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,11 +684,12 @@ bool Compaction::KeyRangeNotExistsBeyondOutputLevel(
|
||||
};
|
||||
|
||||
// Mark (or clear) each file that is being compacted
|
||||
void Compaction::MarkFilesBeingCompacted(bool being_compacted) const {
|
||||
void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) {
|
||||
for (size_t i = 0; i < num_input_levels(); i++) {
|
||||
for (size_t j = 0; j < inputs_[i].size(); j++) {
|
||||
assert(being_compacted != inputs_[i][j]->being_compacted);
|
||||
inputs_[i][j]->being_compacted = being_compacted;
|
||||
assert(mark_as_compacted ? !inputs_[i][j]->being_compacted
|
||||
: inputs_[i][j]->being_compacted);
|
||||
inputs_[i][j]->being_compacted = mark_as_compacted;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -743,7 +733,7 @@ uint64_t Compaction::CalculateTotalInputSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
void Compaction::ReleaseCompactionFiles(const Status& status) {
|
||||
void Compaction::ReleaseCompactionFiles(Status status) {
|
||||
MarkFilesBeingCompacted(false);
|
||||
cfd_->compaction_picker()->ReleaseCompactionFiles(this, status);
|
||||
}
|
||||
@@ -754,10 +744,8 @@ void Compaction::ResetNextCompactionIndex() {
|
||||
}
|
||||
|
||||
namespace {
|
||||
int InputSummary(const std::vector<FileMetaData*>& files,
|
||||
const std::vector<bool>& files_filtered, char* output,
|
||||
int InputSummary(const std::vector<FileMetaData*>& files, char* output,
|
||||
int len) {
|
||||
assert(files_filtered.empty() || (files.size() == files_filtered.size()));
|
||||
*output = '\0';
|
||||
int write = 0;
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
@@ -765,14 +753,8 @@ int InputSummary(const std::vector<FileMetaData*>& files,
|
||||
int ret;
|
||||
char sztxt[16];
|
||||
AppendHumanBytes(files.at(i)->fd.GetFileSize(), sztxt, 16);
|
||||
if (files_filtered.empty()) {
|
||||
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
|
||||
files.at(i)->fd.GetNumber(), sztxt);
|
||||
} else {
|
||||
ret = snprintf(output + write, sz, "%" PRIu64 "(%s filtered:%s) ",
|
||||
files.at(i)->fd.GetNumber(), sztxt,
|
||||
files_filtered.at(i) ? "true" : "false");
|
||||
}
|
||||
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
|
||||
files.at(i)->fd.GetNumber(), sztxt);
|
||||
if (ret < 0 || ret >= sz) {
|
||||
break;
|
||||
}
|
||||
@@ -798,15 +780,8 @@ void Compaction::Summary(char* output, int len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(non_start_level_input_files_filtered_.empty() ||
|
||||
non_start_level_input_files_filtered_.size() == inputs_.size() - 1);
|
||||
write += InputSummary(
|
||||
inputs_[level_iter].files,
|
||||
(level_iter == 0 || non_start_level_input_files_filtered_.empty())
|
||||
? std::vector<bool>{}
|
||||
: non_start_level_input_files_filtered_[level_iter - 1],
|
||||
output + write, len - write);
|
||||
write +=
|
||||
InputSummary(inputs_[level_iter].files, output + write, len - write);
|
||||
if (write < 0 || write >= len) {
|
||||
return;
|
||||
}
|
||||
@@ -889,11 +864,6 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mutable_cf_options_.table_factory->Name() ==
|
||||
TableFactory::kPlainTableName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Round-Robin pri under leveled compaction allows subcompactions by default
|
||||
// and the number of subcompactions can be larger than max_subcompactions_
|
||||
if (cfd_->ioptions()->compaction_pri == kRoundRobin &&
|
||||
@@ -937,25 +907,6 @@ bool Compaction::DoesInputReferenceBlobFiles() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t Compaction::MaxInputFileNewestKeyTime(const InternalKey* start,
|
||||
const InternalKey* end) const {
|
||||
uint64_t newest_key_time = kUnknownNewestKeyTime;
|
||||
const InternalKeyComparator& icmp =
|
||||
column_family_data()->internal_comparator();
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
if (start != nullptr && icmp.Compare(file->largest, *start) < 0) {
|
||||
continue;
|
||||
}
|
||||
if (end != nullptr && icmp.Compare(file->smallest, *end) > 0) {
|
||||
continue;
|
||||
}
|
||||
newest_key_time = std::max(newest_key_time, file->TryGetNewestKeyTime());
|
||||
}
|
||||
}
|
||||
return newest_key_time;
|
||||
}
|
||||
|
||||
uint64_t Compaction::MinInputFileOldestAncesterTime(
|
||||
const InternalKey* start, const InternalKey* end) const {
|
||||
uint64_t min_oldest_ancester_time = std::numeric_limits<uint64_t>::max();
|
||||
@@ -991,7 +942,6 @@ uint64_t Compaction::MinInputFileEpochNumber() const {
|
||||
|
||||
int Compaction::EvaluatePenultimateLevel(
|
||||
const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableOptions& immutable_options, const int start_level,
|
||||
const int output_level) {
|
||||
// TODO: currently per_key_placement feature only support level and universal
|
||||
@@ -1023,7 +973,7 @@ int Compaction::EvaluatePenultimateLevel(
|
||||
}
|
||||
|
||||
bool supports_per_key_placement =
|
||||
mutable_cf_options.preclude_last_level_data_seconds > 0;
|
||||
immutable_options.preclude_last_level_data_seconds > 0;
|
||||
|
||||
// it could be overridden by unittest
|
||||
TEST_SYNC_POINT_CALLBACK("Compaction::SupportsPerKeyPlacement:Enabled",
|
||||
@@ -1035,70 +985,4 @@ int Compaction::EvaluatePenultimateLevel(
|
||||
return penultimate_level;
|
||||
}
|
||||
|
||||
void Compaction::FilterInputsForCompactionIterator() {
|
||||
assert(earliest_snapshot_.has_value());
|
||||
// cfd_ is not populated at Compaction construction time, get it from
|
||||
// VersionStorageInfo instead.
|
||||
assert(input_vstorage_);
|
||||
const auto* ucmp = input_vstorage_->user_comparator();
|
||||
assert(ucmp);
|
||||
// Simply comparing file boundaries when user-defined timestamp is defined
|
||||
// is not as safe because we need to also compare timestamp to know for
|
||||
// sure. Although entries with higher timestamp is also supposed to have
|
||||
// higher sequence number for the same user key (without timestamp).
|
||||
assert(ucmp->timestamp_size() == 0);
|
||||
size_t num_input_levels = inputs_.size();
|
||||
// TODO(yuzhangyu): filtering of older L0 file by new L0 file is not
|
||||
// supported yet.
|
||||
FileMetaData* rangedel_candidate = inputs_[0].level == 0
|
||||
? inputs_[0].files.back()
|
||||
: inputs_[0].files.front();
|
||||
assert(rangedel_candidate);
|
||||
if (!rangedel_candidate->FileIsStandAloneRangeTombstone() ||
|
||||
!DataIsDefinitelyInSnapshot(rangedel_candidate->fd.smallest_seqno,
|
||||
earliest_snapshot_.value(),
|
||||
snapshot_checker_)) {
|
||||
for (size_t level = 0; level < num_input_levels; level++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[level], inputs_[level].files,
|
||||
&arena_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Slice rangedel_start_ukey = rangedel_candidate->smallest.user_key();
|
||||
Slice rangedel_end_ukey = rangedel_candidate->largest.user_key();
|
||||
SequenceNumber rangedel_seqno = rangedel_candidate->fd.smallest_seqno;
|
||||
|
||||
std::vector<std::vector<FileMetaData*>> non_start_level_input_files;
|
||||
non_start_level_input_files.reserve(num_input_levels - 1);
|
||||
non_start_level_input_files_filtered_.reserve(num_input_levels - 1);
|
||||
for (size_t level = 1; level < num_input_levels; level++) {
|
||||
non_start_level_input_files.emplace_back();
|
||||
non_start_level_input_files_filtered_.emplace_back();
|
||||
for (FileMetaData* file : inputs_[level].files) {
|
||||
non_start_level_input_files_filtered_.back().push_back(false);
|
||||
// When range data and point data has the same sequence number, point
|
||||
// data wins. Range deletion end key is exclusive, so check it's bigger
|
||||
// than file right boundary user key.
|
||||
if (rangedel_seqno > file->fd.largest_seqno &&
|
||||
ucmp->CompareWithoutTimestamp(rangedel_start_ukey,
|
||||
file->smallest.user_key()) <= 0 &&
|
||||
ucmp->CompareWithoutTimestamp(rangedel_end_ukey,
|
||||
file->largest.user_key()) > 0) {
|
||||
non_start_level_input_files_filtered_.back().back() = true;
|
||||
filtered_input_levels_[level].push_back(file);
|
||||
} else {
|
||||
non_start_level_input_files.back().push_back(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DoGenerateLevelFilesBrief(&input_levels_[0], inputs_[0].files, &arena_);
|
||||
assert(non_start_level_input_files.size() == num_input_levels - 1);
|
||||
for (size_t level = 1; level < num_input_levels; level++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[level],
|
||||
non_start_level_input_files[level - 1], &arena_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+18
-66
@@ -8,8 +8,6 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/version_set.h"
|
||||
#include "memory/arena.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -92,8 +90,6 @@ class Compaction {
|
||||
CompressionOptions compression_opts,
|
||||
Temperature output_temperature, uint32_t max_subcompactions,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
std::optional<SequenceNumber> earliest_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
bool manual_compaction = false, const std::string& trim_ts = "",
|
||||
double score = -1, bool deletion_compaction = false,
|
||||
bool l0_files_might_overlap = true,
|
||||
@@ -184,16 +180,6 @@ class Compaction {
|
||||
return &input_levels_[compaction_input_level];
|
||||
}
|
||||
|
||||
// Returns the filtered input files of the specified compaction input level.
|
||||
// For now, only non start level is filtered.
|
||||
const std::vector<FileMetaData*>& filtered_input_levels(
|
||||
size_t compaction_input_level) const {
|
||||
const std::vector<FileMetaData*>& filtered_input_level =
|
||||
filtered_input_levels_[compaction_input_level];
|
||||
assert(compaction_input_level != 0 || filtered_input_level.size() == 0);
|
||||
return filtered_input_level;
|
||||
}
|
||||
|
||||
// Maximum size of files to build during this compaction.
|
||||
uint64_t max_output_file_size() const { return max_output_file_size_; }
|
||||
|
||||
@@ -244,7 +230,7 @@ class Compaction {
|
||||
// Delete this compaction from the list of running compactions.
|
||||
//
|
||||
// Requirement: DB mutex held
|
||||
void ReleaseCompactionFiles(const Status& status);
|
||||
void ReleaseCompactionFiles(Status status);
|
||||
|
||||
// Returns the summary of the compaction in "output" with maximum "len"
|
||||
// in bytes. The caller is responsible for the memory management of
|
||||
@@ -303,7 +289,14 @@ class Compaction {
|
||||
// is the sum of all input file sizes.
|
||||
uint64_t OutputFilePreallocationSize() const;
|
||||
|
||||
void FinalizeInputInfo(Version* input_version);
|
||||
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
|
||||
// status and fail the compaction if needed
|
||||
// TODO(hx235): consider making this function part of the construction so we
|
||||
// don't forget to call it
|
||||
void FinalizeInputInfo(Version* input_version) {
|
||||
SetInputVersion(input_version);
|
||||
InitInputTableProperties().PermitUncheckedError();
|
||||
}
|
||||
|
||||
struct InputLevelSummaryBuffer {
|
||||
char buffer[128];
|
||||
@@ -340,16 +333,6 @@ class Compaction {
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs);
|
||||
|
||||
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
|
||||
// status and fail the compaction if needed
|
||||
//
|
||||
// May open and read table files for table property.
|
||||
// Should not be called while holding mutex_.
|
||||
const TablePropertiesCollection& GetOrInitInputTableProperties() {
|
||||
InitInputTableProperties().PermitUncheckedError();
|
||||
return input_table_properties_;
|
||||
}
|
||||
|
||||
const TablePropertiesCollection& GetInputTableProperties() const {
|
||||
return input_table_properties_;
|
||||
}
|
||||
@@ -415,12 +398,6 @@ class Compaction {
|
||||
return blob_garbage_collection_age_cutoff_;
|
||||
}
|
||||
|
||||
// start and end are sub compact range. Null if no boundary.
|
||||
// This is used to calculate the newest_key_time table property after
|
||||
// compaction.
|
||||
uint64_t MaxInputFileNewestKeyTime(const InternalKey* start,
|
||||
const InternalKey* end) const;
|
||||
|
||||
// start and end are sub compact range. Null if no boundary.
|
||||
// This is used to filter out some input files' ancester's time range.
|
||||
uint64_t MinInputFileOldestAncesterTime(const InternalKey* start,
|
||||
@@ -450,19 +427,19 @@ class Compaction {
|
||||
// penultimate level. The safe key range is populated by
|
||||
// `PopulatePenultimateLevelOutputRange()`.
|
||||
// Which could potentially disable all penultimate level output.
|
||||
static int EvaluatePenultimateLevel(
|
||||
const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableOptions& immutable_options, const int start_level,
|
||||
const int output_level);
|
||||
|
||||
// mark (or clear) all files that are being compacted
|
||||
void MarkFilesBeingCompacted(bool being_compacted) const;
|
||||
static int EvaluatePenultimateLevel(const VersionStorageInfo* vstorage,
|
||||
const ImmutableOptions& immutable_options,
|
||||
const int start_level,
|
||||
const int output_level);
|
||||
|
||||
private:
|
||||
void SetInputVersion(Version* input_version);
|
||||
|
||||
Status InitInputTableProperties();
|
||||
|
||||
// mark (or clear) all files that are being compacted
|
||||
void MarkFilesBeingCompacted(bool mark_as_compacted);
|
||||
|
||||
// get the smallest and largest key present in files to be compacted
|
||||
static void GetBoundaryKeys(VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs,
|
||||
@@ -481,13 +458,6 @@ class Compaction {
|
||||
// `Compaction::WithinPenultimateLevelOutputRange()`.
|
||||
void PopulatePenultimateLevelOutputRange();
|
||||
|
||||
// If oldest snapshot is specified at Compaction construction time, we have
|
||||
// an opportunity to optimize inputs for compaction iterator for this case:
|
||||
// When a standalone range deletion file on the start level is recognized and
|
||||
// can be determined to completely shadow some input files on non-start level.
|
||||
// These files will be filtered out and later not feed to compaction iterator.
|
||||
void FilterInputsForCompactionIterator();
|
||||
|
||||
// Get the atomic file boundaries for all files in the compaction. Necessary
|
||||
// in order to avoid the scenario described in
|
||||
// https://github.com/facebook/rocksdb/pull/4432#discussion_r221072219 and
|
||||
@@ -538,30 +508,12 @@ class Compaction {
|
||||
// Compaction input files organized by level. Constant after construction
|
||||
const std::vector<CompactionInputFiles> inputs_;
|
||||
|
||||
// All files from inputs_ that are not filtered and will be fed to compaction
|
||||
// iterator, organized more closely in memory.
|
||||
// A copy of inputs_, organized more closely in memory
|
||||
autovector<LevelFilesBrief, 2> input_levels_;
|
||||
|
||||
// State used to check for number of overlapping grandparent files
|
||||
// (grandparent == "output_level_ + 1")
|
||||
std::vector<FileMetaData*> grandparents_;
|
||||
|
||||
// The earliest snapshot and snapshot checker at compaction picking time.
|
||||
// These fields are only set for deletion triggered compactions picked in
|
||||
// universal compaction. And when user-defined timestamp is not enabled.
|
||||
// It will be used to possibly filter out some non start level input files.
|
||||
std::optional<SequenceNumber> earliest_snapshot_;
|
||||
const SnapshotChecker* snapshot_checker_;
|
||||
|
||||
// Markers for which non start level input files are filtered out if
|
||||
// applicable. Only applicable if earliest_snapshot_ is provided and input
|
||||
// start level has a standalone range deletion file. Filtered files are
|
||||
// tracked in `filtered_input_levels_`.
|
||||
std::vector<std::vector<bool>> non_start_level_input_files_filtered_;
|
||||
|
||||
// All files from inputs_ that are filtered.
|
||||
std::vector<std::vector<FileMetaData*>> filtered_input_levels_;
|
||||
|
||||
const double score_; // score that was used to pick this compaction.
|
||||
|
||||
// Is this compaction creating a file in the bottom most level?
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace ROCKSDB_NAMESPACE {
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
@@ -41,7 +40,7 @@ CompactionIterator::CompactionIterator(
|
||||
const SequenceNumber preserve_time_min_seqno,
|
||||
const SequenceNumber preclude_last_level_min_seqno)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots, earliest_snapshot,
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
|
||||
report_detailed_time, expect_valid_internal_key, range_del_agg,
|
||||
blob_file_builder, allow_data_in_errors, enforce_single_del_contracts,
|
||||
@@ -55,7 +54,6 @@ CompactionIterator::CompactionIterator(
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber /*last_sequence*/, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
@@ -93,7 +91,9 @@ CompactionIterator::CompactionIterator(
|
||||
// snapshots_ cannot be nullptr, but we will assert later in the body of
|
||||
// the constructor.
|
||||
visible_at_tip_(snapshots_ ? snapshots_->empty() : false),
|
||||
earliest_snapshot_(earliest_snapshot),
|
||||
earliest_snapshot_(!snapshots_ || snapshots_->empty()
|
||||
? kMaxSequenceNumber
|
||||
: snapshots_->at(0)),
|
||||
info_log_(info_log),
|
||||
allow_data_in_errors_(allow_data_in_errors),
|
||||
enforce_single_del_contracts_(enforce_single_del_contracts),
|
||||
|
||||
@@ -203,7 +203,6 @@ class CompactionIterator {
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
@@ -223,7 +222,6 @@ class CompactionIterator {
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
@@ -540,12 +538,18 @@ class CompactionIterator {
|
||||
|
||||
inline bool CompactionIterator::DefinitelyInSnapshot(SequenceNumber seq,
|
||||
SequenceNumber snapshot) {
|
||||
return DataIsDefinitelyInSnapshot(seq, snapshot, snapshot_checker_);
|
||||
return ((seq) <= (snapshot) &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
|
||||
SnapshotCheckerResult::kInSnapshot)));
|
||||
}
|
||||
|
||||
inline bool CompactionIterator::DefinitelyNotInSnapshot(
|
||||
SequenceNumber seq, SequenceNumber snapshot) {
|
||||
return DataIsDefinitelyNotInSnapshot(seq, snapshot, snapshot_checker_);
|
||||
return ((seq) > (snapshot) ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
|
||||
SnapshotCheckerResult::kNotInSnapshot)));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -296,7 +296,6 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
snapshots_.empty() ? kMaxSequenceNumber : snapshots_.at(0),
|
||||
earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker_.get(), Env::Default(),
|
||||
false /* report_detailed_time */, false, range_del_agg_.get(),
|
||||
|
||||
@@ -174,9 +174,6 @@ CompactionJob::CompactionJob(
|
||||
db_mutex_(db_mutex),
|
||||
db_error_handler_(db_error_handler),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
earliest_snapshot_(existing_snapshots_.empty()
|
||||
? kMaxSequenceNumber
|
||||
: existing_snapshots_.at(0)),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
job_context_(job_context),
|
||||
@@ -251,13 +248,12 @@ void CompactionJob::Prepare() {
|
||||
|
||||
// Generate file_levels_ for compaction before making Iterator
|
||||
auto* c = compact_->compaction;
|
||||
[[maybe_unused]] ColumnFamilyData* cfd = c->column_family_data();
|
||||
ColumnFamilyData* cfd = c->column_family_data();
|
||||
assert(cfd != nullptr);
|
||||
const VersionStorageInfo* storage_info = c->input_version()->storage_info();
|
||||
assert(storage_info);
|
||||
assert(storage_info->NumLevelFiles(compact_->compaction->level()) > 0);
|
||||
assert(cfd->current()->storage_info()->NumLevelFiles(
|
||||
compact_->compaction->level()) > 0);
|
||||
|
||||
write_hint_ = storage_info->CalculateSSTWriteHint(c->output_level());
|
||||
write_hint_ = cfd->CalculateSSTWriteHint(c->output_level());
|
||||
bottommost_level_ = c->bottommost_level();
|
||||
|
||||
if (c->ShouldFormSubcompactions()) {
|
||||
@@ -286,10 +282,9 @@ void CompactionJob::Prepare() {
|
||||
|
||||
// collect all seqno->time information from the input files which will be used
|
||||
// to encode seqno->time to the output files.
|
||||
|
||||
uint64_t preserve_time_duration =
|
||||
std::max(c->mutable_cf_options()->preserve_internal_time_seconds,
|
||||
c->mutable_cf_options()->preclude_last_level_data_seconds);
|
||||
std::max(c->immutable_options()->preserve_internal_time_seconds,
|
||||
c->immutable_options()->preclude_last_level_data_seconds);
|
||||
|
||||
if (preserve_time_duration > 0) {
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
@@ -298,8 +293,8 @@ void CompactionJob::Prepare() {
|
||||
for (const auto& each_level : *c->inputs()) {
|
||||
for (const auto& fmd : each_level.files) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
Status s = c->input_version()->GetTableProperties(read_options, &tp,
|
||||
fmd, nullptr);
|
||||
Status s =
|
||||
cfd->current()->GetTableProperties(read_options, &tp, fmd, nullptr);
|
||||
if (s.ok()) {
|
||||
s = seqno_to_time_mapping_.DecodeFrom(tp->seqno_to_time_mapping);
|
||||
}
|
||||
@@ -324,11 +319,28 @@ void CompactionJob::Prepare() {
|
||||
seqno_to_time_mapping_.Enforce();
|
||||
} else {
|
||||
seqno_to_time_mapping_.Enforce(_current_time);
|
||||
seqno_to_time_mapping_.GetCurrentTieringCutoffSeqnos(
|
||||
static_cast<uint64_t>(_current_time),
|
||||
c->mutable_cf_options()->preserve_internal_time_seconds,
|
||||
c->mutable_cf_options()->preclude_last_level_data_seconds,
|
||||
&preserve_time_min_seqno_, &preclude_last_level_min_seqno_);
|
||||
uint64_t preserve_time =
|
||||
static_cast<uint64_t>(_current_time) > preserve_time_duration
|
||||
? _current_time - preserve_time_duration
|
||||
: 0;
|
||||
// GetProximalSeqnoBeforeTime tells us the last seqno known to have been
|
||||
// written at or before the given time. + 1 to get the minimum we should
|
||||
// preserve without excluding anything that might have been written on or
|
||||
// after the given time.
|
||||
preserve_time_min_seqno_ =
|
||||
seqno_to_time_mapping_.GetProximalSeqnoBeforeTime(preserve_time) + 1;
|
||||
if (c->immutable_options()->preclude_last_level_data_seconds > 0) {
|
||||
uint64_t preclude_last_level_time =
|
||||
static_cast<uint64_t>(_current_time) >
|
||||
c->immutable_options()->preclude_last_level_data_seconds
|
||||
? _current_time -
|
||||
c->immutable_options()->preclude_last_level_data_seconds
|
||||
: 0;
|
||||
preclude_last_level_min_seqno_ =
|
||||
seqno_to_time_mapping_.GetProximalSeqnoBeforeTime(
|
||||
preclude_last_level_time) +
|
||||
1;
|
||||
}
|
||||
}
|
||||
// For accuracy of the GetProximalSeqnoBeforeTime queries above, we only
|
||||
// limit the capacity after them.
|
||||
@@ -469,11 +481,6 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
read_options.rate_limiter_priority = GetRateLimiterPriority();
|
||||
auto* c = compact_->compaction;
|
||||
if (c->mutable_cf_options()->table_factory->Name() ==
|
||||
TableFactory::kPlainTableName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->max_subcompactions() <= 1 &&
|
||||
!(c->immutable_options()->compaction_pri == kRoundRobin &&
|
||||
c->immutable_options()->compaction_style == kCompactionStyleLevel)) {
|
||||
@@ -506,7 +513,9 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
FileMetaData* f = flevel->files[i].file_metadata;
|
||||
std::vector<TableReader::Anchor> my_anchors;
|
||||
Status s = cfd->table_cache()->ApproximateKeyAnchors(
|
||||
read_options, icomp, *f, *c->mutable_cf_options(), my_anchors);
|
||||
read_options, icomp, *f,
|
||||
c->mutable_cf_options()->block_protection_bytes_per_key,
|
||||
my_anchors);
|
||||
if (!s.ok() || my_anchors.empty()) {
|
||||
my_anchors.emplace_back(f->largest.user_key(), f->fd.GetFileSize());
|
||||
}
|
||||
@@ -625,7 +634,6 @@ Status CompactionJob::Run() {
|
||||
const size_t num_threads = compact_->sub_compact_states.size();
|
||||
assert(num_threads > 0);
|
||||
const uint64_t start_micros = db_options_.clock->NowMicros();
|
||||
compact_->compaction->GetOrInitInputTableProperties();
|
||||
|
||||
// Launch a thread for each of subcompactions 1...num_threads-1
|
||||
std::vector<port::Thread> thread_pool;
|
||||
@@ -709,6 +717,8 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
}
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
auto& prefix_extractor =
|
||||
compact_->compaction->mutable_cf_options()->prefix_extractor;
|
||||
std::atomic<size_t> next_file_idx(0);
|
||||
auto verify_table = [&](Status& output_status) {
|
||||
while (true) {
|
||||
@@ -729,8 +739,7 @@ Status CompactionJob::Run() {
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
verify_table_read_options, file_options_,
|
||||
cfd->internal_comparator(), files_output[file_idx]->meta,
|
||||
/*range_del_agg=*/nullptr,
|
||||
*compact_->compaction->mutable_cf_options(),
|
||||
/*range_del_agg=*/nullptr, prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr,
|
||||
cfd->internal_stats()->GetFileReadHist(
|
||||
compact_->compaction->output_level()),
|
||||
@@ -740,7 +749,9 @@ Status CompactionJob::Run() {
|
||||
*compact_->compaction->mutable_cf_options()),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
/*allow_unprepared_value=*/false,
|
||||
compact_->compaction->mutable_cf_options()
|
||||
->block_protection_bytes_per_key);
|
||||
auto s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
@@ -801,12 +812,6 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
}
|
||||
|
||||
// Before the compaction starts, is_remote_compaction was set to true if
|
||||
// compaction_service is set. We now know whether each sub_compaction was
|
||||
// done remotely or not. Reset is_remote_compaction back to false and allow
|
||||
// AggregateCompactionStats() to set the right value.
|
||||
compaction_job_stats_->is_remote_compaction = false;
|
||||
|
||||
// Finish up all bookkeeping to unify the subcompaction results.
|
||||
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
|
||||
uint64_t num_input_range_del = 0;
|
||||
@@ -911,23 +916,19 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options,
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
"files in(%d, %d) filtered(%d, %d) out(%d +%d blob) "
|
||||
"MB in(%.1f, %.1f +%.1f blob) filtered(%.1f, %.1f) out(%.1f +%.1f blob), "
|
||||
"files in(%d, %d) out(%d +%d blob) "
|
||||
"MB in(%.1f, %.1f +%.1f blob) out(%.1f +%.1f blob), "
|
||||
"read-write-amplify(%.1f) write-amplify(%.1f) %s, records in: %" PRIu64
|
||||
", records dropped: %" PRIu64 " output_compression: %s\n",
|
||||
column_family_name.c_str(), vstorage->LevelSummary(&tmp),
|
||||
bytes_read_per_sec, bytes_written_per_sec,
|
||||
compact_->compaction->output_level(),
|
||||
stats.num_input_files_in_non_output_levels,
|
||||
stats.num_input_files_in_output_level,
|
||||
stats.num_filtered_input_files_in_non_output_levels,
|
||||
stats.num_filtered_input_files_in_output_level, stats.num_output_files,
|
||||
stats.num_input_files_in_output_level, stats.num_output_files,
|
||||
stats.num_output_files_blob, stats.bytes_read_non_output_levels / kMB,
|
||||
stats.bytes_read_output_level / kMB, stats.bytes_read_blob / kMB,
|
||||
stats.bytes_skipped_non_output_levels / kMB,
|
||||
stats.bytes_skipped_output_level / kMB, stats.bytes_written / kMB,
|
||||
stats.bytes_written_blob / kMB, read_write_amp, write_amp,
|
||||
status.ToString().c_str(), stats.num_input_records,
|
||||
stats.bytes_written / kMB, stats.bytes_written_blob / kMB, read_write_amp,
|
||||
write_amp, status.ToString().c_str(), stats.num_input_records,
|
||||
stats.num_dropped_records,
|
||||
CompressionTypeToString(compact_->compaction->output_compression())
|
||||
.c_str());
|
||||
@@ -1089,7 +1090,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
// fallback to local compaction
|
||||
assert(comp_status == CompactionServiceJobStatus::kUseLocal);
|
||||
sub_compact->compaction_job_stats.is_remote_compaction = false;
|
||||
}
|
||||
|
||||
uint64_t prev_cpu_micros = db_options_.clock->CPUMicros();
|
||||
@@ -1295,9 +1295,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
auto c_iter = std::make_unique<CompactionIterator>(
|
||||
input, cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_snapshot_,
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq, snapshot_checker_,
|
||||
env_, ShouldReportDetailedTime(env_, stats_),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_, job_snapshot_seq,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_),
|
||||
/*expect_valid_internal_key=*/true, range_del_agg.get(),
|
||||
blob_file_builder.get(), db_options_.allow_data_in_errors,
|
||||
db_options_.enforce_single_del_contracts, manual_compaction_canceled_,
|
||||
@@ -1586,8 +1585,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
const uint64_t current_entries = outputs.NumEntries();
|
||||
|
||||
s = outputs.Finish(s, seqno_to_time_mapping_);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::FinishCompactionOutputFile()::AfterFinish", &s);
|
||||
|
||||
if (s.ok()) {
|
||||
// With accurate smallest and largest key, we can get a slightly more
|
||||
@@ -1920,10 +1917,6 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
oldest_ancester_time = current_time;
|
||||
}
|
||||
|
||||
uint64_t newest_key_time = sub_compact->compaction->MaxInputFileNewestKeyTime(
|
||||
sub_compact->start.has_value() ? &tmp_start : nullptr,
|
||||
sub_compact->end.has_value() ? &tmp_end : nullptr);
|
||||
|
||||
// Initialize a SubcompactionState::Output and add it to sub_compact->outputs
|
||||
uint64_t epoch_number = sub_compact->compaction->MinInputFileEpochNumber();
|
||||
{
|
||||
@@ -1973,13 +1966,10 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
cfd->internal_tbl_prop_coll_factories(),
|
||||
sub_compact->compaction->output_compression(),
|
||||
sub_compact->compaction->output_compression_opts(), cfd->GetID(),
|
||||
cfd->GetName(), sub_compact->compaction->output_level(), newest_key_time,
|
||||
cfd->GetName(), sub_compact->compaction->output_level(),
|
||||
bottommost_level_, TableFileCreationReason::kCompaction,
|
||||
0 /* oldest_key_time */, current_time, db_id_, db_session_id_,
|
||||
sub_compact->compaction->max_output_file_size(), file_number,
|
||||
preclude_last_level_min_seqno_ == kMaxSequenceNumber
|
||||
? preclude_last_level_min_seqno_
|
||||
: std::min(earliest_snapshot_, preclude_last_level_min_seqno_));
|
||||
sub_compact->compaction->max_output_file_size(), file_number);
|
||||
|
||||
outputs.NewBuilder(tboptions);
|
||||
|
||||
@@ -2016,8 +2006,7 @@ bool CompactionJob::UpdateCompactionStats(uint64_t* num_input_range_del) {
|
||||
for (int input_level = 0;
|
||||
input_level < static_cast<int>(compaction->num_input_levels());
|
||||
++input_level) {
|
||||
const LevelFilesBrief* flevel = compaction->input_levels(input_level);
|
||||
size_t num_input_files = flevel->num_files;
|
||||
size_t num_input_files = compaction->num_input_files(input_level);
|
||||
uint64_t* bytes_read;
|
||||
if (compaction->level(input_level) != compaction->output_level()) {
|
||||
compaction_stats_.stats.num_input_files_in_non_output_levels +=
|
||||
@@ -2029,7 +2018,7 @@ bool CompactionJob::UpdateCompactionStats(uint64_t* num_input_range_del) {
|
||||
bytes_read = &compaction_stats_.stats.bytes_read_output_level;
|
||||
}
|
||||
for (size_t i = 0; i < num_input_files; ++i) {
|
||||
const FileMetaData* file_meta = flevel->files[i].file_metadata;
|
||||
const FileMetaData* file_meta = compaction->input(input_level, i);
|
||||
*bytes_read += file_meta->fd.GetFileSize();
|
||||
uint64_t file_input_entries = file_meta->num_entries;
|
||||
uint64_t file_num_range_del = file_meta->num_range_deletions;
|
||||
@@ -2052,23 +2041,6 @@ bool CompactionJob::UpdateCompactionStats(uint64_t* num_input_range_del) {
|
||||
*num_input_range_del += file_num_range_del;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<FileMetaData*>& filtered_flevel =
|
||||
compaction->filtered_input_levels(input_level);
|
||||
size_t num_filtered_input_files = filtered_flevel.size();
|
||||
uint64_t* bytes_skipped;
|
||||
if (compaction->level(input_level) != compaction->output_level()) {
|
||||
compaction_stats_.stats.num_filtered_input_files_in_non_output_levels +=
|
||||
static_cast<int>(num_filtered_input_files);
|
||||
bytes_skipped = &compaction_stats_.stats.bytes_skipped_non_output_levels;
|
||||
} else {
|
||||
compaction_stats_.stats.num_filtered_input_files_in_output_level +=
|
||||
static_cast<int>(num_filtered_input_files);
|
||||
bytes_skipped = &compaction_stats_.stats.bytes_skipped_output_level;
|
||||
}
|
||||
for (const FileMetaData* filtered_file_meta : filtered_flevel) {
|
||||
*bytes_skipped += filtered_file_meta->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
|
||||
assert(compaction_job_stats_);
|
||||
@@ -2093,13 +2065,6 @@ void CompactionJob::UpdateCompactionJobStats(
|
||||
stats.num_input_files_in_output_level;
|
||||
compaction_job_stats_->num_input_files_at_output_level =
|
||||
stats.num_input_files_in_output_level;
|
||||
compaction_job_stats_->num_filtered_input_files =
|
||||
stats.num_filtered_input_files_in_non_output_levels +
|
||||
stats.num_filtered_input_files_in_output_level;
|
||||
compaction_job_stats_->num_filtered_input_files_at_output_level =
|
||||
stats.num_filtered_input_files_in_output_level;
|
||||
compaction_job_stats_->total_skipped_input_bytes =
|
||||
stats.bytes_skipped_non_output_levels + stats.bytes_skipped_output_level;
|
||||
|
||||
// output information
|
||||
compaction_job_stats_->total_output_bytes = stats.bytes_written;
|
||||
|
||||
@@ -209,13 +209,12 @@ class CompactionJob {
|
||||
// Returns true iff compaction_stats_.stats.num_input_records and
|
||||
// num_input_range_del are calculated successfully.
|
||||
bool UpdateCompactionStats(uint64_t* num_input_range_del = nullptr);
|
||||
virtual void UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const;
|
||||
void LogCompaction();
|
||||
virtual void RecordCompactionIOStats();
|
||||
void CleanupCompaction();
|
||||
|
||||
// Iterate through input and compact the kv-pairs.
|
||||
// Call compaction filter. Then iterate through input and compact the
|
||||
// kv-pairs
|
||||
void ProcessKeyValueCompaction(SubcompactionState* sub_compact);
|
||||
|
||||
CompactionState* compact_;
|
||||
@@ -280,7 +279,8 @@ class CompactionJob {
|
||||
bool* compaction_released);
|
||||
Status OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
CompactionOutputs& outputs);
|
||||
|
||||
void UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const;
|
||||
void RecordDroppedKeys(const CompactionIterationStats& c_iter_stats,
|
||||
CompactionJobStats* compaction_job_stats = nullptr);
|
||||
|
||||
@@ -314,8 +314,6 @@ class CompactionJob {
|
||||
// deleted because that version is not visible in any snapshot.
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
|
||||
SequenceNumber earliest_snapshot_;
|
||||
|
||||
// This is the earliest snapshot that could be used for write-conflict
|
||||
// checking by a transaction. For any user-key newer than this snapshot, we
|
||||
// should make sure not to remove evidence that a write occurred.
|
||||
@@ -377,7 +375,9 @@ class CompactionJob {
|
||||
// doesn't contain the LSM tree information, which is passed though MANIFEST
|
||||
// file.
|
||||
struct CompactionServiceInput {
|
||||
std::string cf_name;
|
||||
ColumnFamilyDescriptor column_family;
|
||||
|
||||
DBOptions db_options;
|
||||
|
||||
std::vector<SequenceNumber> snapshots;
|
||||
|
||||
@@ -385,7 +385,7 @@ struct CompactionServiceInput {
|
||||
// files needed for this compaction, for both input level files and output
|
||||
// level files.
|
||||
std::vector<std::string> input_files;
|
||||
int output_level = 0;
|
||||
int output_level;
|
||||
|
||||
// db_id is used to generate unique id of sst on the remote compactor
|
||||
std::string db_id;
|
||||
@@ -396,12 +396,13 @@ struct CompactionServiceInput {
|
||||
bool has_end = false;
|
||||
std::string end;
|
||||
|
||||
uint64_t options_file_number = 0;
|
||||
|
||||
// serialization interface to read and write the object
|
||||
static Status Read(const std::string& data_str, CompactionServiceInput* obj);
|
||||
Status Write(std::string* output);
|
||||
|
||||
// Initialize a dummy ColumnFamilyDescriptor
|
||||
CompactionServiceInput() : column_family("", ColumnFamilyOptions()) {}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool TEST_Equals(CompactionServiceInput* other);
|
||||
bool TEST_Equals(CompactionServiceInput* other, std::string* mismatch);
|
||||
@@ -415,25 +416,20 @@ struct CompactionServiceOutputFile {
|
||||
SequenceNumber largest_seqno;
|
||||
std::string smallest_internal_key;
|
||||
std::string largest_internal_key;
|
||||
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
|
||||
uint64_t file_creation_time = kUnknownFileCreationTime;
|
||||
uint64_t epoch_number = kUnknownEpochNumber;
|
||||
std::string file_checksum = kUnknownFileChecksum;
|
||||
std::string file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
uint64_t oldest_ancester_time;
|
||||
uint64_t file_creation_time;
|
||||
uint64_t epoch_number;
|
||||
uint64_t paranoid_hash;
|
||||
bool marked_for_compaction;
|
||||
UniqueId64x2 unique_id{};
|
||||
TableProperties table_properties;
|
||||
UniqueId64x2 unique_id;
|
||||
|
||||
CompactionServiceOutputFile() = default;
|
||||
CompactionServiceOutputFile(
|
||||
const std::string& name, SequenceNumber smallest, SequenceNumber largest,
|
||||
std::string _smallest_internal_key, std::string _largest_internal_key,
|
||||
uint64_t _oldest_ancester_time, uint64_t _file_creation_time,
|
||||
uint64_t _epoch_number, const std::string& _file_checksum,
|
||||
const std::string& _file_checksum_func_name, uint64_t _paranoid_hash,
|
||||
bool _marked_for_compaction, UniqueId64x2 _unique_id,
|
||||
const TableProperties& _table_properties)
|
||||
uint64_t _epoch_number, uint64_t _paranoid_hash,
|
||||
bool _marked_for_compaction, UniqueId64x2 _unique_id)
|
||||
: file_name(name),
|
||||
smallest_seqno(smallest),
|
||||
largest_seqno(largest),
|
||||
@@ -442,12 +438,9 @@ struct CompactionServiceOutputFile {
|
||||
oldest_ancester_time(_oldest_ancester_time),
|
||||
file_creation_time(_file_creation_time),
|
||||
epoch_number(_epoch_number),
|
||||
file_checksum(_file_checksum),
|
||||
file_checksum_func_name(_file_checksum_func_name),
|
||||
paranoid_hash(_paranoid_hash),
|
||||
marked_for_compaction(_marked_for_compaction),
|
||||
unique_id(std::move(_unique_id)),
|
||||
table_properties(_table_properties) {}
|
||||
unique_id(std::move(_unique_id)) {}
|
||||
};
|
||||
|
||||
// CompactionServiceResult contains the compaction result from a different db
|
||||
@@ -456,11 +449,14 @@ struct CompactionServiceOutputFile {
|
||||
struct CompactionServiceResult {
|
||||
Status status;
|
||||
std::vector<CompactionServiceOutputFile> output_files;
|
||||
int output_level = 0;
|
||||
int output_level;
|
||||
|
||||
// location of the output files
|
||||
std::string output_path;
|
||||
|
||||
// some statistics about the compaction
|
||||
uint64_t num_output_records = 0;
|
||||
uint64_t total_bytes = 0;
|
||||
uint64_t bytes_read = 0;
|
||||
uint64_t bytes_written = 0;
|
||||
CompactionJobStats stats;
|
||||
@@ -506,9 +502,6 @@ class CompactionServiceCompactionJob : private CompactionJob {
|
||||
protected:
|
||||
void RecordCompactionIOStats() override;
|
||||
|
||||
void UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const override;
|
||||
|
||||
private:
|
||||
// Get table file name in output_path
|
||||
std::string GetTableFileName(uint64_t file_number) override;
|
||||
|
||||
@@ -50,8 +50,7 @@ void VerifyInitializationOfCompactionJobStats(
|
||||
ASSERT_EQ(compaction_job_stats.num_output_records, 0U);
|
||||
ASSERT_EQ(compaction_job_stats.num_output_files, 0U);
|
||||
|
||||
ASSERT_TRUE(compaction_job_stats.is_manual_compaction);
|
||||
ASSERT_FALSE(compaction_job_stats.is_remote_compaction);
|
||||
ASSERT_EQ(compaction_job_stats.is_manual_compaction, true);
|
||||
|
||||
ASSERT_EQ(compaction_job_stats.total_input_bytes, 0U);
|
||||
ASSERT_EQ(compaction_job_stats.total_output_bytes, 0U);
|
||||
@@ -250,7 +249,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
mutable_cf_options_.table_factory = cf_options_.table_factory;
|
||||
}
|
||||
|
||||
std::string GenerateFileName(uint64_t file_number) {
|
||||
@@ -301,13 +299,13 @@ class CompactionJobTestBase : public testing::Test {
|
||||
const WriteOptions write_options;
|
||||
std::unique_ptr<TableBuilder> table_builder(
|
||||
cf_options_.table_factory->NewTableBuilder(
|
||||
TableBuilderOptions(
|
||||
*cfd_->ioptions(), mutable_cf_options_, read_options,
|
||||
write_options, cfd_->internal_comparator(),
|
||||
cfd_->internal_tbl_prop_coll_factories(),
|
||||
CompressionType::kNoCompression, CompressionOptions(),
|
||||
0 /* column_family_id */, kDefaultColumnFamilyName,
|
||||
-1 /* level */, kUnknownNewestKeyTime),
|
||||
TableBuilderOptions(*cfd_->ioptions(), mutable_cf_options_,
|
||||
read_options, write_options,
|
||||
cfd_->internal_comparator(),
|
||||
cfd_->internal_tbl_prop_coll_factories(),
|
||||
CompressionType::kNoCompression,
|
||||
CompressionOptions(), 0 /* column_family_id */,
|
||||
kDefaultColumnFamilyName, -1 /* level */),
|
||||
file_writer.get()));
|
||||
// Build table.
|
||||
for (const auto& kv : contents) {
|
||||
@@ -547,14 +545,14 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ASSERT_OK(s);
|
||||
db_options_.info_log = info_log;
|
||||
|
||||
versions_.reset(
|
||||
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
|
||||
test::kUnitTestDbId, /*db_session_id=*/"",
|
||||
/*daily_offpeak_time_utc=*/"",
|
||||
/*error_handler=*/nullptr, /*read_only=*/false));
|
||||
versions_.reset(new VersionSet(
|
||||
dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
|
||||
/*db_id=*/"", /*db_session_id=*/"", /*daily_offpeak_time_utc=*/"",
|
||||
/*error_handler=*/nullptr, /*read_only=*/false));
|
||||
compaction_job_stats_.Reset();
|
||||
ASSERT_OK(SetIdentityFile(WriteOptions(), env_, dbname_));
|
||||
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -577,8 +575,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(WriteOptions(), fs_.get(), dbname_, 1,
|
||||
Temperature::kUnknown, nullptr);
|
||||
s = SetCurrentFile(WriteOptions(), fs_.get(), dbname_, 1, nullptr);
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
@@ -652,8 +649,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
mutable_cf_options_.target_file_size_base,
|
||||
mutable_cf_options_.max_compaction_bytes, 0, kNoCompression,
|
||||
cfd->GetLatestMutableCFOptions()->compression_opts,
|
||||
Temperature::kUnknown, max_subcompactions, grandparents,
|
||||
/*earliest_snapshot*/ std::nullopt, /*snapshot_checker*/ nullptr, true);
|
||||
Temperature::kUnknown, max_subcompactions, grandparents, true);
|
||||
compaction.FinalizeInputInfo(cfd->current());
|
||||
|
||||
assert(db_options_.info_log);
|
||||
@@ -1571,7 +1567,17 @@ TEST_F(CompactionJobTest, InputSerialization) {
|
||||
const int kStrMaxLen = 1000;
|
||||
Random rnd(static_cast<uint32_t>(time(nullptr)));
|
||||
Random64 rnd64(time(nullptr));
|
||||
input.cf_name = rnd.RandomString(rnd.Uniform(kStrMaxLen));
|
||||
input.column_family.name = rnd.RandomString(rnd.Uniform(kStrMaxLen));
|
||||
input.column_family.options.comparator = ReverseBytewiseComparator();
|
||||
input.column_family.options.max_bytes_for_level_base =
|
||||
rnd64.Uniform(UINT64_MAX);
|
||||
input.column_family.options.disable_auto_compactions = rnd.OneIn(2);
|
||||
input.column_family.options.compression = kZSTD;
|
||||
input.column_family.options.compression_opts.level = 4;
|
||||
input.db_options.max_background_flushes = 10;
|
||||
input.db_options.paranoid_checks = rnd.OneIn(2);
|
||||
input.db_options.statistics = CreateDBStatistics();
|
||||
input.db_options.env = env_;
|
||||
while (!rnd.OneIn(10)) {
|
||||
input.snapshots.emplace_back(rnd64.Uniform(UINT64_MAX));
|
||||
}
|
||||
@@ -1599,10 +1605,10 @@ TEST_F(CompactionJobTest, InputSerialization) {
|
||||
ASSERT_TRUE(deserialized1.TEST_Equals(&input));
|
||||
|
||||
// Test mismatch
|
||||
deserialized1.output_level += 10;
|
||||
deserialized1.db_options.max_background_flushes += 10;
|
||||
std::string mismatch;
|
||||
ASSERT_FALSE(deserialized1.TEST_Equals(&input, &mismatch));
|
||||
ASSERT_EQ(mismatch, "output_level");
|
||||
ASSERT_EQ(mismatch, "db_options.max_background_flushes");
|
||||
|
||||
// Test unknown field
|
||||
CompactionServiceInput deserialized2;
|
||||
@@ -1658,40 +1664,20 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
};
|
||||
result.status =
|
||||
status_list.at(rnd.Uniform(static_cast<int>(status_list.size())));
|
||||
|
||||
std::string file_checksum = rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen));
|
||||
std::string file_checksum_func_name = "MyAwesomeChecksumGenerator";
|
||||
while (!rnd.OneIn(10)) {
|
||||
TableProperties tp;
|
||||
tp.user_collected_properties.emplace(
|
||||
"UCP_Key1", rnd.RandomString(rnd.Uniform(kStrMaxLen)));
|
||||
tp.user_collected_properties.emplace(
|
||||
"UCP_Key2", rnd.RandomString(rnd.Uniform(kStrMaxLen)));
|
||||
tp.readable_properties.emplace("RP_Key1",
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)));
|
||||
tp.readable_properties.emplace("RP_K2y2",
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)));
|
||||
|
||||
UniqueId64x2 id{rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX)};
|
||||
result.output_files.emplace_back(
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)) /* file_name */,
|
||||
rnd64.Uniform(UINT64_MAX) /* smallest_seqno */,
|
||||
rnd64.Uniform(UINT64_MAX) /* largest_seqno */,
|
||||
rnd.RandomBinaryString(
|
||||
rnd.Uniform(kStrMaxLen)) /* smallest_internal_key */,
|
||||
rnd.RandomBinaryString(
|
||||
rnd.Uniform(kStrMaxLen)) /* largest_internal_key */,
|
||||
rnd64.Uniform(UINT64_MAX) /* oldest_ancester_time */,
|
||||
rnd64.Uniform(UINT64_MAX) /* file_creation_time */,
|
||||
rnd64.Uniform(UINT64_MAX) /* epoch_number */,
|
||||
file_checksum /* file_checksum */,
|
||||
file_checksum_func_name /* file_checksum_func_name */,
|
||||
rnd64.Uniform(UINT64_MAX) /* paranoid_hash */,
|
||||
rnd.OneIn(2) /* marked_for_compaction */, id /* unique_id */, tp);
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)), rnd64.Uniform(UINT64_MAX),
|
||||
rnd64.Uniform(UINT64_MAX),
|
||||
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
|
||||
rnd.RandomBinaryString(rnd.Uniform(kStrMaxLen)),
|
||||
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX),
|
||||
rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX), rnd.OneIn(2), id);
|
||||
}
|
||||
result.output_level = rnd.Uniform(10);
|
||||
result.output_path = rnd.RandomString(rnd.Uniform(kStrMaxLen));
|
||||
result.stats.num_output_records = rnd64.Uniform(UINT64_MAX);
|
||||
result.num_output_records = rnd64.Uniform(UINT64_MAX);
|
||||
result.total_bytes = rnd64.Uniform(UINT64_MAX);
|
||||
result.bytes_read = 123;
|
||||
result.bytes_written = rnd64.Uniform(UINT64_MAX);
|
||||
result.stats.elapsed_micros = rnd64.Uniform(UINT64_MAX);
|
||||
@@ -1708,21 +1694,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
ASSERT_OK(CompactionServiceResult::Read(output, &deserialized1));
|
||||
ASSERT_TRUE(deserialized1.TEST_Equals(&result));
|
||||
|
||||
for (size_t i = 0; i < result.output_files.size(); i++) {
|
||||
for (const auto& prop :
|
||||
result.output_files[i].table_properties.user_collected_properties) {
|
||||
ASSERT_EQ(deserialized1.output_files[i]
|
||||
.table_properties.user_collected_properties[prop.first],
|
||||
prop.second);
|
||||
}
|
||||
for (const auto& prop :
|
||||
result.output_files[i].table_properties.readable_properties) {
|
||||
ASSERT_EQ(deserialized1.output_files[i]
|
||||
.table_properties.readable_properties[prop.first],
|
||||
prop.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Test mismatch
|
||||
deserialized1.stats.num_input_files += 10;
|
||||
std::string mismatch;
|
||||
@@ -1737,10 +1708,6 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
ASSERT_FALSE(deserialized_tmp.TEST_Equals(&result, &mismatch));
|
||||
ASSERT_EQ(mismatch, "output_files.unique_id");
|
||||
deserialized_tmp.status.PermitUncheckedError();
|
||||
|
||||
ASSERT_EQ(deserialized_tmp.output_files[0].file_checksum, file_checksum);
|
||||
ASSERT_EQ(deserialized_tmp.output_files[0].file_checksum_func_name,
|
||||
file_checksum_func_name);
|
||||
}
|
||||
|
||||
// Test unknown field
|
||||
|
||||
@@ -62,9 +62,8 @@ class CompactionOutputs {
|
||||
}
|
||||
|
||||
// TODO: Remove it when remote compaction support tiered compaction
|
||||
void AddBytesWritten(uint64_t bytes) { stats_.bytes_written += bytes; }
|
||||
void SetTotalBytes(uint64_t bytes) { stats_.bytes_written += bytes; }
|
||||
void SetNumOutputRecords(uint64_t num) { stats_.num_output_records = num; }
|
||||
void SetNumOutputFiles(uint64_t num) { stats_.num_output_files = num; }
|
||||
|
||||
// TODO: Move the BlobDB builder into CompactionOutputs
|
||||
const std::vector<BlobFileAddition>& GetBlobFileAdditions() const {
|
||||
@@ -108,12 +107,6 @@ class CompactionOutputs {
|
||||
Status Finish(const Status& intput_status,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping);
|
||||
|
||||
// Update output table properties from already populated TableProperties.
|
||||
// Used for remote compaction
|
||||
void UpdateTableProperties(const TableProperties& table_properties) {
|
||||
current_output().table_properties =
|
||||
std::make_shared<TableProperties>(table_properties);
|
||||
}
|
||||
// Update output table properties from table builder
|
||||
void UpdateTableProperties() {
|
||||
current_output().table_properties =
|
||||
|
||||
@@ -133,8 +133,7 @@ CompactionPicker::CompactionPicker(const ImmutableOptions& ioptions,
|
||||
CompactionPicker::~CompactionPicker() = default;
|
||||
|
||||
// Delete this compaction from the list of running compactions.
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c,
|
||||
const Status& status) {
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
|
||||
UnregisterCompaction(c);
|
||||
if (!status.ok()) {
|
||||
c->ResetNextCompactionIndex();
|
||||
@@ -351,11 +350,11 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(output_level == 0 || !FilesRangeOverlapWithCompaction(
|
||||
input_files, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage, mutable_cf_options, ioptions_,
|
||||
start_level, output_level)));
|
||||
assert(output_level == 0 ||
|
||||
!FilesRangeOverlapWithCompaction(
|
||||
input_files, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, ioptions_,
|
||||
start_level, output_level)));
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
CompressionType compression_type;
|
||||
@@ -380,8 +379,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr, true);
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
@@ -659,9 +657,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, mutable_cf_options,
|
||||
ioptions_, start_level,
|
||||
output_level))) {
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, ioptions_,
|
||||
start_level, output_level))) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
*manual_conflict = true;
|
||||
@@ -679,9 +676,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
compact_range_options.blob_garbage_collection_policy,
|
||||
@@ -847,8 +842,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(
|
||||
compaction_inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, mutable_cf_options,
|
||||
ioptions_, input_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage, ioptions_, input_level,
|
||||
output_level))) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
@@ -871,7 +865,6 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
|
||||
/* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
@@ -1051,12 +1044,10 @@ Status CompactionPicker::SanitizeCompactionInputFilesForAllLevels(
|
||||
}
|
||||
|
||||
Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
std::unordered_set<uint64_t>* input_files, const int output_level,
|
||||
Version* version,
|
||||
std::unordered_set<uint64_t>* input_files,
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level,
|
||||
const VersionStorageInfo* vstorage,
|
||||
std::vector<CompactionInputFiles>* converted_input_files) const {
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
version->GetColumnFamilyMetaData(&cf_meta);
|
||||
|
||||
assert(static_cast<int>(cf_meta.levels.size()) - 1 ==
|
||||
cf_meta.levels[cf_meta.levels.size() - 1].level);
|
||||
assert(converted_input_files);
|
||||
@@ -1127,8 +1118,7 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
}
|
||||
|
||||
s = GetCompactionInputsFromFileNumbers(converted_input_files, input_files,
|
||||
version->storage_info(),
|
||||
CompactionOptions());
|
||||
vstorage, CompactionOptions());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1137,8 +1127,8 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
FilesRangeOverlapWithCompaction(
|
||||
*converted_input_files, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
version->storage_info(), version->GetMutableCFOptions(),
|
||||
ioptions_, (*converted_input_files)[0].level, output_level))) {
|
||||
vstorage, ioptions_, (*converted_input_files)[0].level,
|
||||
output_level))) {
|
||||
return Status::Aborted(
|
||||
"A running compaction is writing to the same output level(s) in an "
|
||||
"overlapping key range");
|
||||
@@ -1180,8 +1170,7 @@ void CompactionPicker::UnregisterCompaction(Compaction* c) {
|
||||
|
||||
void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
const std::string& cf_name, VersionStorageInfo* vstorage, int* start_level,
|
||||
int* output_level, CompactionInputFiles* start_level_inputs,
|
||||
std::function<bool(const FileMetaData*)> skip_marked_file) {
|
||||
int* output_level, CompactionInputFiles* start_level_inputs) {
|
||||
if (vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1191,9 +1180,6 @@ void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
if (skip_marked_file(level_file.second)) {
|
||||
return false;
|
||||
}
|
||||
*start_level = level_file.first;
|
||||
*output_level =
|
||||
(*start_level == 0) ? vstorage->base_level() : *start_level + 1;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/version_set.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -56,17 +55,17 @@ class CompactionPicker {
|
||||
// Returns nullptr if there is no compaction to be done.
|
||||
// Otherwise returns a pointer to a heap-allocated object that
|
||||
// describes the compaction. Caller should delete the result.
|
||||
// Currently, only universal compaction will query existing snapshots and
|
||||
// pass it to aid compaction picking. And it's only passed when user-defined
|
||||
// timestamps is not enabled. The other compaction styles do not pass or use
|
||||
// `existing_snapshots` or `snapshot_checker`.
|
||||
virtual Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) = 0;
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) = 0;
|
||||
|
||||
// Return a compaction object for compacting the range [begin,end] in
|
||||
// the specified level. Returns nullptr if there is nothing in that
|
||||
// level that overlaps the specified range. Caller should delete
|
||||
// the result.
|
||||
//
|
||||
// The returned Compaction might not include the whole requested range.
|
||||
// In that case, compaction_end will be set to the next key that needs
|
||||
// compacting. In case the compaction will compact the whole range,
|
||||
@@ -97,14 +96,15 @@ class CompactionPicker {
|
||||
// non-ok status with specific reason.
|
||||
//
|
||||
Status SanitizeAndConvertCompactionInputFiles(
|
||||
std::unordered_set<uint64_t>* input_files, const int output_level,
|
||||
Version* version,
|
||||
std::unordered_set<uint64_t>* input_files,
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level,
|
||||
const VersionStorageInfo* vstorage,
|
||||
std::vector<CompactionInputFiles>* converted_input_files) const;
|
||||
|
||||
// Free up the files that participated in a compaction
|
||||
//
|
||||
// Requirement: DB mutex held
|
||||
void ReleaseCompactionFiles(Compaction* c, const Status& status);
|
||||
void ReleaseCompactionFiles(Compaction* c, Status status);
|
||||
|
||||
// Returns true if any one of the specified files are being compacted
|
||||
bool AreFilesInCompaction(const std::vector<FileMetaData*>& files);
|
||||
@@ -203,11 +203,10 @@ class CompactionPicker {
|
||||
const CompactionInputFiles& output_level_inputs,
|
||||
std::vector<FileMetaData*>* grandparents);
|
||||
|
||||
void PickFilesMarkedForCompaction(
|
||||
const std::string& cf_name, VersionStorageInfo* vstorage,
|
||||
int* start_level, int* output_level,
|
||||
CompactionInputFiles* start_level_inputs,
|
||||
std::function<bool(const FileMetaData*)> skip_marked_file);
|
||||
void PickFilesMarkedForCompaction(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
int* start_level, int* output_level,
|
||||
CompactionInputFiles* start_level_inputs);
|
||||
|
||||
bool GetOverlappingL0Files(VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* start_level_inputs,
|
||||
@@ -258,13 +257,11 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
virtual ~NullCompactionPicker() {}
|
||||
|
||||
// Always return "nullptr"
|
||||
Compaction* PickCompaction(
|
||||
const std::string& /*cf_name*/,
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
const MutableDBOptions& /*mutable_db_options*/,
|
||||
const std::vector<SequenceNumber>& /*existing_snapshots*/,
|
||||
const SnapshotChecker* /*snapshot_checker*/,
|
||||
VersionStorageInfo* /*vstorage*/, LogBuffer* /* log_buffer */) override {
|
||||
Compaction* PickCompaction(const std::string& /*cf_name*/,
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
const MutableDBOptions& /*mutable_db_options*/,
|
||||
VersionStorageInfo* /*vstorage*/,
|
||||
LogBuffer* /* log_buffer */) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,14 +79,10 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
FileMetaData* f = *ritr;
|
||||
assert(f);
|
||||
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
|
||||
uint64_t newest_key_time = f->TryGetNewestKeyTime();
|
||||
uint64_t creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
|
||||
? creation_time
|
||||
: newest_key_time;
|
||||
if (est_newest_key_time == kUnknownNewestKeyTime ||
|
||||
est_newest_key_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
if (creation_time == 0 ||
|
||||
creation_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -106,19 +102,15 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
}
|
||||
|
||||
for (const auto& f : inputs[0].files) {
|
||||
assert(f);
|
||||
uint64_t newest_key_time = f->TryGetNewestKeyTime();
|
||||
uint64_t creation_time = 0;
|
||||
assert(f);
|
||||
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
|
||||
creation_time = f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
}
|
||||
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
|
||||
? creation_time
|
||||
: newest_key_time;
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with estimated newest key time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), est_newest_key_time);
|
||||
" with creation time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), creation_time);
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
@@ -126,9 +118,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kFIFOTtl);
|
||||
@@ -198,9 +188,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
0 /* max_subcompactions */, {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr, /* is manual */ false,
|
||||
0 /* max_subcompactions */, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
/* l0_files_might_overlap */ true,
|
||||
@@ -296,9 +284,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
/* output_path_id */ 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true,
|
||||
/* l0_files_might_overlap */ true, CompactionReason::kFIFOMaxSize);
|
||||
@@ -308,7 +294,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) const {
|
||||
LogBuffer* log_buffer) {
|
||||
const std::vector<FileTemperatureAge>& ages =
|
||||
mutable_cf_options.compaction_options_fifo
|
||||
.file_temperature_age_thresholds;
|
||||
@@ -358,47 +344,73 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
Temperature compaction_target_temp = Temperature::kLastTemperature;
|
||||
if (current_time > min_age) {
|
||||
uint64_t create_time_threshold = current_time - min_age;
|
||||
uint64_t compaction_size = 0;
|
||||
// We will ideally identify a file qualifying for temperature change by
|
||||
// knowing the timestamp for the youngest entry in the file. However, right
|
||||
// now we don't have the information. We infer it by looking at timestamp of
|
||||
// the previous file's (which is just younger) oldest entry's timestamp.
|
||||
Temperature cur_target_temp;
|
||||
// avoid index underflow
|
||||
assert(level_files.size() >= 1);
|
||||
for (size_t index = level_files.size(); index >= 1; --index) {
|
||||
for (size_t index = level_files.size() - 1; index >= 1; --index) {
|
||||
// Try to add cur_file to compaction inputs.
|
||||
FileMetaData* cur_file = level_files[index - 1];
|
||||
FileMetaData* prev_file = index < 2 ? nullptr : level_files[index - 2];
|
||||
FileMetaData* cur_file = level_files[index];
|
||||
// prev_file is just younger than cur_file
|
||||
FileMetaData* prev_file = level_files[index - 1];
|
||||
if (cur_file->being_compacted) {
|
||||
// Should not happen since we check for
|
||||
// `level0_compactions_in_progress_` above. Here we simply just don't
|
||||
// schedule anything.
|
||||
return nullptr;
|
||||
}
|
||||
uint64_t est_newest_key_time = cur_file->TryGetNewestKeyTime(prev_file);
|
||||
// Newer file could have newest_key_time populated
|
||||
if (est_newest_key_time == kUnknownNewestKeyTime) {
|
||||
continue;
|
||||
}
|
||||
if (est_newest_key_time > create_time_threshold) {
|
||||
uint64_t oldest_ancestor_time = prev_file->TryGetOldestAncesterTime();
|
||||
if (oldest_ancestor_time == kUnknownOldestAncesterTime) {
|
||||
// Older files might not have enough information. It is possible to
|
||||
// handle these files by looking at newer files, but maintaining the
|
||||
// logic isn't worth it.
|
||||
break;
|
||||
}
|
||||
Temperature cur_target_temp = ages[0].temperature;
|
||||
if (oldest_ancestor_time > create_time_threshold) {
|
||||
// cur_file is too fresh
|
||||
break;
|
||||
}
|
||||
cur_target_temp = ages[0].temperature;
|
||||
for (size_t i = 1; i < ages.size(); ++i) {
|
||||
if (current_time >= ages[i].age &&
|
||||
est_newest_key_time <= current_time - ages[i].age) {
|
||||
oldest_ancestor_time <= current_time - ages[i].age) {
|
||||
cur_target_temp = ages[i].temperature;
|
||||
}
|
||||
}
|
||||
if (cur_file->temperature == cur_target_temp) {
|
||||
continue;
|
||||
if (inputs[0].empty()) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// cur_file needs to change temperature
|
||||
assert(compaction_target_temp == Temperature::kLastTemperature);
|
||||
compaction_target_temp = cur_target_temp;
|
||||
inputs[0].files.push_back(cur_file);
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with estimated newest key time %" PRIu64 " for temperature %s.",
|
||||
cf_name.c_str(), cur_file->fd.GetNumber(), est_newest_key_time,
|
||||
temperature_to_string[cur_target_temp].c_str());
|
||||
break;
|
||||
if (compaction_target_temp == Temperature::kLastTemperature) {
|
||||
assert(inputs[0].empty());
|
||||
compaction_target_temp = cur_target_temp;
|
||||
} else if (cur_target_temp != compaction_target_temp) {
|
||||
assert(!inputs[0].empty());
|
||||
break;
|
||||
}
|
||||
if (inputs[0].empty() || compaction_size + cur_file->fd.GetFileSize() <=
|
||||
mutable_cf_options.max_compaction_bytes) {
|
||||
inputs[0].files.push_back(cur_file);
|
||||
compaction_size += cur_file->fd.GetFileSize();
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with next file's oldest time %" PRIu64 " for temperature %s.",
|
||||
cf_name.c_str(), cur_file->fd.GetNumber(), oldest_ancestor_time,
|
||||
temperature_to_string[cur_target_temp].c_str());
|
||||
}
|
||||
if (compaction_size > mutable_cf_options.max_compaction_bytes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,18 +418,15 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
return nullptr;
|
||||
}
|
||||
assert(compaction_target_temp != Temperature::kLastTemperature);
|
||||
// Only compact one file at a time.
|
||||
assert(inputs.size() == 1);
|
||||
assert(inputs[0].size() == 1);
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
std::move(inputs), 0, 0 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */, 0 /* output path ID */,
|
||||
mutable_cf_options.compression, mutable_cf_options.compression_opts,
|
||||
compaction_target_temp,
|
||||
/* max_subcompactions */ 0, {}, /* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false, /* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "",
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kChangeTemperature);
|
||||
return c;
|
||||
@@ -425,9 +434,7 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */, VersionStorageInfo* vstorage,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
Compaction* c = nullptr;
|
||||
if (mutable_cf_options.ttl > 0) {
|
||||
@@ -462,10 +469,8 @@ Compaction* FIFOCompactionPicker::CompactRange(
|
||||
assert(output_level == 0);
|
||||
*compaction_end = nullptr;
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, ioptions_.logger);
|
||||
Compaction* c =
|
||||
PickCompaction(cf_name, mutable_cf_options, mutable_db_options,
|
||||
/*existing_snapshots*/ {}, /*snapshot_checker*/ nullptr,
|
||||
vstorage, &log_buffer);
|
||||
Compaction* c = PickCompaction(cf_name, mutable_cf_options,
|
||||
mutable_db_options, vstorage, &log_buffer);
|
||||
log_buffer.FlushBufferToLog();
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
|
||||
Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */,
|
||||
VersionStorageInfo* version, LogBuffer* log_buffer) override;
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
Compaction* CompactRange(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
@@ -54,10 +53,9 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Will pick one file to compact at a time, starting from the oldest file.
|
||||
Compaction* PickTemperatureChangeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) const;
|
||||
LogBuffer* log_buffer);
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -262,10 +262,7 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_,
|
||||
/*skip_marked_file*/ [](const FileMetaData* /* file */) {
|
||||
return false;
|
||||
});
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
@@ -414,9 +411,8 @@ void LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion() {
|
||||
&tmp_start_level_inputs) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{tmp_start_level_inputs}, output_level_,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, ioptions_, start_level_, output_level_))) {
|
||||
// Constraint 1a
|
||||
tmp_start_level_inputs.clear();
|
||||
return;
|
||||
@@ -490,9 +486,8 @@ bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
// We need to disallow this from happening.
|
||||
if (compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
compaction_inputs_, output_level_,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, ioptions_, start_level_, output_level_))) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
return false;
|
||||
@@ -559,9 +554,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
vstorage_->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_),
|
||||
/* earliest_snapshot */ std::nullopt, /* snapshot_checker */ nullptr,
|
||||
is_manual_,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
|
||||
l0_files_might_overlap, compaction_reason_);
|
||||
|
||||
@@ -846,9 +839,8 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
&start_level_inputs_) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, mutable_cf_options_,
|
||||
ioptions_, start_level_,
|
||||
output_level_))) {
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, ioptions_, start_level_, output_level_))) {
|
||||
// A locked (pending compaction) input-level file was pulled in due to
|
||||
// user-key overlap.
|
||||
start_level_inputs_.clear();
|
||||
@@ -933,15 +925,11 @@ bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
|
||||
}
|
||||
uint64_t l0_size = 0;
|
||||
for (const auto& file : l0_files) {
|
||||
assert(file->compensated_file_size >= file->fd.GetFileSize());
|
||||
// Compact down L0s with more deletions.
|
||||
l0_size += file->compensated_file_size;
|
||||
l0_size += file->fd.GetFileSize();
|
||||
}
|
||||
|
||||
// Avoid L0->Lbase compactions that are inefficient for write-amp.
|
||||
const double kMultiplier =
|
||||
std::max(10.0, mutable_cf_options_.max_bytes_for_level_multiplier) * 2;
|
||||
const uint64_t min_lbase_size = MultiplyCheckOverflow(l0_size, kMultiplier);
|
||||
const uint64_t min_lbase_size =
|
||||
l0_size * static_cast<uint64_t>(std::max(
|
||||
10.0, mutable_cf_options_.max_bytes_for_level_multiplier));
|
||||
assert(min_lbase_size >= l0_size);
|
||||
const std::vector<FileMetaData*>& lbase_files =
|
||||
vstorage_->LevelFiles(/*level=*/base_level);
|
||||
@@ -975,9 +963,7 @@ bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /*existing_snapshots */,
|
||||
const SnapshotChecker* /*snapshot_checker*/, VersionStorageInfo* vstorage,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
|
||||
mutable_cf_options, ioptions_,
|
||||
|
||||
@@ -20,12 +20,11 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
LevelCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& /* existing_snapshots */,
|
||||
const SnapshotChecker* /* snapshot_checker */,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) override;
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -35,9 +35,7 @@ class UniversalCompactionBuilder {
|
||||
UniversalCompactionBuilder(
|
||||
const ImmutableOptions& ioptions, const InternalKeyComparator* icmp,
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
UniversalCompactionPicker* picker, LogBuffer* log_buffer)
|
||||
: ioptions_(ioptions),
|
||||
icmp_(icmp),
|
||||
@@ -46,19 +44,7 @@ class UniversalCompactionBuilder {
|
||||
mutable_db_options_(mutable_db_options),
|
||||
vstorage_(vstorage),
|
||||
picker_(picker),
|
||||
log_buffer_(log_buffer) {
|
||||
assert(icmp_);
|
||||
const auto* ucmp = icmp_->user_comparator();
|
||||
assert(ucmp);
|
||||
// These parameters are only passed when user-defined timestamp is not
|
||||
// enabled.
|
||||
if (ucmp->timestamp_size() == 0) {
|
||||
earliest_snapshot_ = existing_snapshots.empty()
|
||||
? kMaxSequenceNumber
|
||||
: existing_snapshots.at(0);
|
||||
snapshot_checker_ = snapshot_checker;
|
||||
}
|
||||
}
|
||||
log_buffer_(log_buffer) {}
|
||||
|
||||
// Form and return the compaction object. The caller owns return object.
|
||||
Compaction* PickCompaction();
|
||||
@@ -66,15 +52,12 @@ class UniversalCompactionBuilder {
|
||||
private:
|
||||
struct SortedRun {
|
||||
SortedRun(int _level, FileMetaData* _file, uint64_t _size,
|
||||
uint64_t _compensated_file_size, bool _being_compacted,
|
||||
bool _level_has_marked_standalone_rangedel)
|
||||
uint64_t _compensated_file_size, bool _being_compacted)
|
||||
: level(_level),
|
||||
file(_file),
|
||||
size(_size),
|
||||
compensated_file_size(_compensated_file_size),
|
||||
being_compacted(_being_compacted),
|
||||
level_has_marked_standalone_rangedel(
|
||||
_level_has_marked_standalone_rangedel) {
|
||||
being_compacted(_being_compacted) {
|
||||
assert(compensated_file_size > 0);
|
||||
assert(level != 0 || file != nullptr);
|
||||
}
|
||||
@@ -96,10 +79,6 @@ class UniversalCompactionBuilder {
|
||||
uint64_t size;
|
||||
uint64_t compensated_file_size;
|
||||
bool being_compacted;
|
||||
// True if this level has any file that is a standalone range deletion file
|
||||
// marked for compaction. Best effort is made to make only deletion
|
||||
// triggered compaction pick this type of file.
|
||||
bool level_has_marked_standalone_rangedel;
|
||||
};
|
||||
|
||||
// Pick Universal compaction to limit read amplification
|
||||
@@ -119,11 +98,6 @@ class UniversalCompactionBuilder {
|
||||
|
||||
Compaction* PickDeleteTriggeredCompaction();
|
||||
|
||||
// Returns true if this given file (that is marked be compaction) should be
|
||||
// skipped from being picked for now. We do this to best use standalone range
|
||||
// tombstone files.
|
||||
bool ShouldSkipMarkedFile(const FileMetaData* file) const;
|
||||
|
||||
// Form a compaction from the sorted run indicated by start_index to the
|
||||
// oldest sorted run.
|
||||
// The caller is responsible for making sure that those files are not in
|
||||
@@ -142,7 +116,7 @@ class UniversalCompactionBuilder {
|
||||
|
||||
bool ShouldSkipLastSortedRunForSizeAmpCompaction() const {
|
||||
assert(!sorted_runs_.empty());
|
||||
return mutable_cf_options_.preclude_last_level_data_seconds > 0 &&
|
||||
return ioptions_.preclude_last_level_data_seconds > 0 &&
|
||||
ioptions_.num_levels > 2 &&
|
||||
sorted_runs_.back().level == ioptions_.num_levels - 1 &&
|
||||
sorted_runs_.size() > 1;
|
||||
@@ -253,27 +227,15 @@ class UniversalCompactionBuilder {
|
||||
const InternalKeyComparator* icmp_;
|
||||
double score_;
|
||||
std::vector<SortedRun> sorted_runs_;
|
||||
uint64_t max_run_size_;
|
||||
const std::string& cf_name_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const MutableDBOptions& mutable_db_options_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
UniversalCompactionPicker* picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
// Optional earliest snapshot at time of compaction picking. This is only
|
||||
// provided if the column family doesn't enable user-defined timestamps.
|
||||
// And this information is only passed to `Compaction` picked by deletion
|
||||
// triggered compaction for possible optimizations.
|
||||
std::optional<SequenceNumber> earliest_snapshot_;
|
||||
const SnapshotChecker* snapshot_checker_;
|
||||
// Mapping from file id to its index in the sorted run for the files that are
|
||||
// marked for compaction. This is only populated when snapshot info is
|
||||
// populated.
|
||||
std::map<uint64_t, size_t> file_marked_for_compaction_to_sorted_run_index_;
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level,
|
||||
uint64_t* max_run_size);
|
||||
static std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -430,13 +392,11 @@ bool UniversalCompactionPicker::NeedsCompaction(
|
||||
|
||||
Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
UniversalCompactionBuilder builder(
|
||||
ioptions_, icmp_, cf_name, mutable_cf_options, mutable_db_options,
|
||||
existing_snapshots, snapshot_checker, vstorage, this, log_buffer);
|
||||
UniversalCompactionBuilder builder(ioptions_, icmp_, cf_name,
|
||||
mutable_cf_options, mutable_db_options,
|
||||
vstorage, this, log_buffer);
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
|
||||
@@ -480,26 +440,16 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level,
|
||||
uint64_t* max_run_size) {
|
||||
assert(max_run_size);
|
||||
*max_run_size = 0;
|
||||
const VersionStorageInfo& vstorage, int last_level) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
if (earliest_snapshot_.has_value() && f->marked_for_compaction) {
|
||||
file_marked_for_compaction_to_sorted_run_index_.emplace(f->fd.GetNumber(),
|
||||
ret.size());
|
||||
}
|
||||
ret.emplace_back(
|
||||
0, f, f->fd.GetFileSize(), f->compensated_file_size, f->being_compacted,
|
||||
f->marked_for_compaction && f->FileIsStandAloneRangeTombstone());
|
||||
*max_run_size = std::max(*max_run_size, f->fd.GetFileSize());
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
}
|
||||
for (int level = 1; level <= last_level; level++) {
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
bool level_has_marked_standalone_rangedel = false;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(level)) {
|
||||
total_compensated_size += f->compensated_file_size;
|
||||
total_size += f->fd.GetFileSize();
|
||||
@@ -511,57 +461,15 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
}
|
||||
level_has_marked_standalone_rangedel =
|
||||
level_has_marked_standalone_rangedel ||
|
||||
(f->marked_for_compaction && f->FileIsStandAloneRangeTombstone());
|
||||
if (earliest_snapshot_.has_value() && f->marked_for_compaction) {
|
||||
file_marked_for_compaction_to_sorted_run_index_.emplace(
|
||||
f->fd.GetNumber(), ret.size());
|
||||
}
|
||||
}
|
||||
if (total_compensated_size > 0) {
|
||||
ret.emplace_back(level, nullptr, total_size, total_compensated_size,
|
||||
being_compacted, level_has_marked_standalone_rangedel);
|
||||
being_compacted);
|
||||
}
|
||||
*max_run_size = std::max(*max_run_size, total_size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool UniversalCompactionBuilder::ShouldSkipMarkedFile(
|
||||
const FileMetaData* file) const {
|
||||
assert(file->marked_for_compaction);
|
||||
if (!earliest_snapshot_.has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (!file->FileIsStandAloneRangeTombstone()) {
|
||||
return false;
|
||||
}
|
||||
// Skip until earliest snapshot advances at or above this standalone range
|
||||
// tombstone file. `DB::ReleaseSnapshot` will re-examine and schedule
|
||||
// compaction for it.
|
||||
if (!DataIsDefinitelyInSnapshot(file->fd.largest_seqno,
|
||||
earliest_snapshot_.value(),
|
||||
snapshot_checker_)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto iter = file_marked_for_compaction_to_sorted_run_index_.find(
|
||||
file->fd.GetNumber());
|
||||
assert(iter != file_marked_for_compaction_to_sorted_run_index_.end());
|
||||
size_t idx = iter->second;
|
||||
const SortedRun* succeeding_sorted_run =
|
||||
idx < sorted_runs_.size() - 1 ? &sorted_runs_[idx + 1] : nullptr;
|
||||
// Marked standalone range tombstone file is best used if it's in the start
|
||||
// input level. Skip to let that compaction happen first.
|
||||
if (succeeding_sorted_run &&
|
||||
succeeding_sorted_run->level_has_marked_standalone_rangedel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Universal style of compaction. Pick files that are contiguous in
|
||||
// time-range to compact.
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
@@ -569,16 +477,13 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
max_run_size_ = 0;
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, max_output_level, &max_run_size_);
|
||||
int file_num_compaction_trigger =
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger;
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_, max_output_level);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
vstorage_->FilesMarkedForCompaction().empty() &&
|
||||
sorted_runs_.size() < (unsigned int)file_num_compaction_trigger)) {
|
||||
sorted_runs_.size() < (unsigned int)mutable_cf_options_
|
||||
.level0_file_num_compaction_trigger)) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: nothing to do\n",
|
||||
cf_name_.c_str());
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
@@ -600,9 +505,11 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
TEST_SYNC_POINT_CALLBACK("PostPickPeriodicCompaction", c);
|
||||
}
|
||||
|
||||
// Check for size amplification.
|
||||
if (c == nullptr &&
|
||||
sorted_runs_.size() >= static_cast<size_t>(file_num_compaction_trigger)) {
|
||||
// Check for size amplification.
|
||||
sorted_runs_.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger)) {
|
||||
if ((c = PickCompactionToReduceSizeAmp()) != nullptr) {
|
||||
TEST_SYNC_POINT("PickCompactionToReduceSizeAmpReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: compacting for size amp\n",
|
||||
@@ -620,78 +527,34 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
cf_name_.c_str());
|
||||
} else {
|
||||
// Size amplification and file size ratios are within configured limits.
|
||||
// If max read amplification exceeds configured limits, then force
|
||||
// compaction to reduce the number sorted runs without looking at file
|
||||
// size ratios.
|
||||
|
||||
// If max read amplification is exceeding configured limits, then force
|
||||
// compaction without looking at filesize ratios and try to reduce
|
||||
// the number of files to fewer than level0_file_num_compaction_trigger.
|
||||
// This is guaranteed by NeedsCompaction()
|
||||
assert(sorted_runs_.size() >=
|
||||
static_cast<size_t>(file_num_compaction_trigger));
|
||||
int max_num_runs =
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp;
|
||||
if (max_num_runs < 0) {
|
||||
// any value < -1 is not valid
|
||||
assert(max_num_runs == -1);
|
||||
// By default, fall back to `level0_file_num_compaction_trigger`
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
} else if (max_num_runs == 0) {
|
||||
if (mutable_cf_options_.compaction_options_universal.stop_style ==
|
||||
kCompactionStopStyleTotalSize) {
|
||||
// 0 means auto-tuning by RocksDB. We estimate max num run based on
|
||||
// max_run_size, size_ratio and write buffer size:
|
||||
// Assume the size of the lowest level size is equal to
|
||||
// write_buffer_size. Each subsequent level is the max size without
|
||||
// triggering size_ratio compaction. `max_num_runs` is the minimum
|
||||
// number of levels required such that the target size of the
|
||||
// largest level is at least `max_run_size_`.
|
||||
max_num_runs = 1;
|
||||
double cur_level_max_size =
|
||||
static_cast<double>(mutable_cf_options_.write_buffer_size);
|
||||
double total_run_size = 0;
|
||||
while (cur_level_max_size < static_cast<double>(max_run_size_)) {
|
||||
// This loop should not take too many iterations since
|
||||
// cur_level_max_size at least doubles each iteration.
|
||||
total_run_size += cur_level_max_size;
|
||||
cur_level_max_size = (100.0 + ratio) / 100.0 * total_run_size;
|
||||
++max_num_runs;
|
||||
}
|
||||
} else {
|
||||
// TODO: implement the auto-tune logic for this stop style
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
}
|
||||
} else {
|
||||
// max_num_runs > 0, it's the limit on the number of sorted run
|
||||
}
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger));
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs_.size(); i++) {
|
||||
if (sorted_runs_[i].being_compacted == false &&
|
||||
!sorted_runs_[i].level_has_marked_standalone_rangedel) {
|
||||
if (sorted_runs_[i].being_compacted == false) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater
|
||||
// than the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted > max_num_runs) {
|
||||
unsigned int num_files = num_sr_not_compacted - max_num_runs + 1;
|
||||
if (num_sr_not_compacted >
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger) {
|
||||
unsigned int num_files =
|
||||
num_sr_not_compacted -
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionToReduceSortedRuns(UINT_MAX, num_files)) !=
|
||||
nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: compacting for file num, to "
|
||||
"compact file num -- %u, max num runs allowed"
|
||||
"-- %d, max_run_size -- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_files, max_num_runs,
|
||||
max_run_size_);
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name_.c_str(), num_files);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: skipping compaction for file num, num runs not "
|
||||
"being compacted -- %u, max num runs allowed -- %d, max_run_size "
|
||||
"-- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_sr_not_compacted, max_num_runs,
|
||||
max_run_size_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -829,24 +692,16 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
for (sr = nullptr; loop < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
|
||||
if (!sr->being_compacted && !sr->level_has_marked_standalone_rangedel) {
|
||||
if (!sr->being_compacted) {
|
||||
candidate_count = 1;
|
||||
break;
|
||||
}
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf));
|
||||
if (sr->being_compacted) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] being compacted, skipping",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
} else if (sr->level_has_marked_standalone_rangedel) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] has standalone range tombstone files marked for "
|
||||
"compaction, skipping",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
}
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] being compacted, skipping",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
|
||||
sr = nullptr;
|
||||
}
|
||||
@@ -867,8 +722,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
candidate_count < max_files_to_compact && i < sorted_runs_.size();
|
||||
i++) {
|
||||
const SortedRun* succeeding_sr = &sorted_runs_[i];
|
||||
if (succeeding_sr->being_compacted ||
|
||||
succeeding_sr->level_has_marked_standalone_rangedel) {
|
||||
if (succeeding_sr->being_compacted) {
|
||||
break;
|
||||
}
|
||||
// Pick files if the total/last candidate file size (increased by the
|
||||
@@ -994,11 +848,11 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
grandparents = vstorage_->LevelFiles(sorted_runs_[first_index_after].level);
|
||||
}
|
||||
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
if (output_level != 0 &&
|
||||
picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
CompactionReason compaction_reason;
|
||||
@@ -1018,8 +872,6 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
output_level, enable_compression),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, grandparents,
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false, /* trim_ts */ "", score_,
|
||||
false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true, compaction_reason);
|
||||
@@ -1036,8 +888,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
const size_t end_index = ShouldSkipLastSortedRunForSizeAmpCompaction()
|
||||
? sorted_runs_.size() - 2
|
||||
: sorted_runs_.size() - 1;
|
||||
if (sorted_runs_[end_index].being_compacted ||
|
||||
sorted_runs_[end_index].level_has_marked_standalone_rangedel) {
|
||||
if (sorted_runs_[end_index].being_compacted) {
|
||||
return nullptr;
|
||||
}
|
||||
const uint64_t base_sr_size = sorted_runs_[end_index].size;
|
||||
@@ -1048,23 +899,14 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
// Get longest span (i.e, [start_index, end_index]) of available sorted runs
|
||||
while (start_index > 0) {
|
||||
const SortedRun* sr = &sorted_runs_[start_index - 1];
|
||||
if (sr->being_compacted || sr->level_has_marked_standalone_rangedel) {
|
||||
if (sr->being_compacted) {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
if (sr->being_compacted) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: stopping at sorted run undergoing compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
} else if (sr->level_has_marked_standalone_rangedel) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: stopping at sorted run that has standalone range "
|
||||
"tombstone files marked for compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
}
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: stopping at sorted run undergoing compaction: "
|
||||
"%s[%" ROCKSDB_PRIszt "]",
|
||||
cf_name_.c_str(), file_num_buf, start_index - 1);
|
||||
break;
|
||||
}
|
||||
candidate_size += sr->compensated_file_size;
|
||||
@@ -1343,11 +1185,11 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
}
|
||||
|
||||
// intra L0 compactions outputs could have overlap
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
if (output_level != 0 &&
|
||||
picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1364,10 +1206,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
@@ -1398,7 +1237,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
continue;
|
||||
}
|
||||
FileMetaData* f = vstorage_->LevelFiles(0)[loop];
|
||||
if (f->marked_for_compaction && !ShouldSkipMarkedFile(f)) {
|
||||
if (f->marked_for_compaction) {
|
||||
start_level_inputs.files.push_back(f);
|
||||
start_index =
|
||||
static_cast<int>(loop); // Consider this as the first candidate.
|
||||
@@ -1412,7 +1251,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
|
||||
for (size_t loop = start_index + 1; loop < sorted_runs_.size(); loop++) {
|
||||
SortedRun* sr = &sorted_runs_[loop];
|
||||
if (sr->being_compacted || sr->level_has_marked_standalone_rangedel) {
|
||||
if (sr->being_compacted) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1431,10 +1270,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
// leveled. We pick one of the files marked for compaction and compact with
|
||||
// overlapping files in the adjacent level.
|
||||
picker_->PickFilesMarkedForCompaction(cf_name_, vstorage_, &start_level,
|
||||
&output_level, &start_level_inputs,
|
||||
[this](const FileMetaData* file) {
|
||||
return ShouldSkipMarkedFile(file);
|
||||
});
|
||||
&output_level, &start_level_inputs);
|
||||
if (start_level_inputs.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1487,8 +1323,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
if (picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_, start_level,
|
||||
output_level))) {
|
||||
vstorage_, ioptions_, start_level, output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1515,9 +1350,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, grandparents, earliest_snapshot_,
|
||||
snapshot_checker_,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
@@ -1588,11 +1421,11 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
}
|
||||
|
||||
// intra L0 compactions outputs could have overlap
|
||||
if (output_level != 0 && picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
vstorage_, mutable_cf_options_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
if (output_level != 0 &&
|
||||
picker_->FilesRangeOverlapWithCompaction(
|
||||
inputs, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(vstorage_, ioptions_,
|
||||
start_level, output_level))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1610,10 +1443,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {},
|
||||
/* earliest_snapshot */ std::nullopt,
|
||||
/* snapshot_checker */ nullptr,
|
||||
/* is manual */ false,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true, compaction_reason);
|
||||
}
|
||||
@@ -1634,8 +1464,7 @@ Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
|
||||
// included in the compaction.
|
||||
|
||||
size_t start_index = sorted_runs_.size();
|
||||
while (start_index > 0 && !sorted_runs_[start_index - 1].being_compacted &&
|
||||
!sorted_runs_[start_index - 1].level_has_marked_standalone_rangedel) {
|
||||
while (start_index > 0 && !sorted_runs_[start_index - 1].being_compacted) {
|
||||
start_index--;
|
||||
}
|
||||
if (start_index == sorted_runs_.size()) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class UniversalCompactionPicker : public CompactionPicker {
|
||||
@@ -18,12 +17,11 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
UniversalCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
Compaction* PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
const std::vector<SequenceNumber>& existing_snapshots,
|
||||
const SnapshotChecker* snapshot_checker, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
@@ -39,8 +39,12 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
MakeTableFileName(file->fd.GetNumber()));
|
||||
}
|
||||
}
|
||||
|
||||
compaction_input.cf_name = compaction->column_family_data()->GetName();
|
||||
compaction_input.column_family.name =
|
||||
compaction->column_family_data()->GetName();
|
||||
compaction_input.column_family.options =
|
||||
compaction->column_family_data()->GetLatestCFOptions();
|
||||
compaction_input.db_options =
|
||||
BuildDBOptions(db_options_, mutable_db_options_copy_);
|
||||
compaction_input.snapshots = existing_snapshots_;
|
||||
compaction_input.has_begin = sub_compact->start.has_value();
|
||||
compaction_input.begin =
|
||||
@@ -48,14 +52,6 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
compaction_input.has_end = sub_compact->end.has_value();
|
||||
compaction_input.end =
|
||||
compaction_input.has_end ? sub_compact->end->ToString() : "";
|
||||
compaction_input.options_file_number =
|
||||
sub_compact->compaction->input_version()
|
||||
->version_set()
|
||||
->options_file_number();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
|
||||
&compaction_input);
|
||||
|
||||
std::string compaction_input_binary;
|
||||
Status s = compaction_input.Write(&compaction_input_binary);
|
||||
@@ -74,13 +70,10 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Starting remote compaction (output level: %d): %s",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction_input.column_family.name.c_str(), job_id_,
|
||||
compaction_input.output_level, input_files_oss.str().c_str());
|
||||
CompactionServiceJobInfo info(
|
||||
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());
|
||||
CompactionServiceJobInfo info(dbname_, db_id_, db_session_id_,
|
||||
GetCompactionId(sub_compact), thread_pri_);
|
||||
CompactionServiceScheduleResponse response =
|
||||
db_options_.compaction_service->Schedule(info, compaction_input_binary);
|
||||
switch (response.status) {
|
||||
@@ -91,14 +84,13 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
"CompactionService failed to schedule a remote compaction job.");
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction failed to start.",
|
||||
compaction->column_family_data()->GetName().c_str(),
|
||||
job_id_);
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return response.status;
|
||||
case CompactionServiceJobStatus::kUseLocal:
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API (Schedule)",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return response.status;
|
||||
default:
|
||||
assert(false); // unknown status
|
||||
@@ -107,7 +99,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Waiting for remote compaction...",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
std::string compaction_result_binary;
|
||||
CompactionServiceJobStatus compaction_status =
|
||||
db_options_.compaction_service->Wait(response.scheduled_job_id,
|
||||
@@ -117,7 +109,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API (Wait)",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return compaction_status;
|
||||
}
|
||||
|
||||
@@ -142,19 +134,15 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
"result is returned).");
|
||||
compaction_result.status.PermitUncheckedError();
|
||||
}
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log, "[%s] [JOB %d] Remote compaction failed.",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_);
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction failed.",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return compaction_status;
|
||||
}
|
||||
|
||||
// CompactionServiceJobStatus::kSuccess was returned, but somehow we failed to
|
||||
// read the result. Consider this as an installation failure
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
compaction_result.status.PermitUncheckedError();
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
sub_compact->status = compaction_result.status;
|
||||
@@ -166,14 +154,18 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
is_first_one = false;
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Received remote compaction result, output path: "
|
||||
"%s, files: %s",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction_result.output_path.c_str(), output_files_oss.str().c_str());
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Receive remote compaction result, output path: "
|
||||
"%s, files: %s",
|
||||
compaction_input.column_family.name.c_str(), job_id_,
|
||||
compaction_result.output_path.c_str(),
|
||||
output_files_oss.str().c_str());
|
||||
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
|
||||
// Installation Starts
|
||||
for (const auto& file : compaction_result.output_files) {
|
||||
uint64_t file_num = versions_->NewFileNumber();
|
||||
auto src_file = compaction_result.output_path + "/" + file.file_name;
|
||||
@@ -182,8 +174,6 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
s = fs_->RenameFile(src_file, tgt_file, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
|
||||
@@ -192,8 +182,6 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
s = fs_->GetFileSize(tgt_file, IOOptions(), &file_size, nullptr);
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
meta.fd = FileDescriptor(file_num, compaction->output_path_id(), file_size,
|
||||
@@ -203,8 +191,6 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
meta.oldest_ancester_time = file.oldest_ancester_time;
|
||||
meta.file_creation_time = file.file_creation_time;
|
||||
meta.epoch_number = file.epoch_number;
|
||||
meta.file_checksum = file.file_checksum;
|
||||
meta.file_checksum_func_name = file.file_checksum_func_name;
|
||||
meta.marked_for_compaction = file.marked_for_compaction;
|
||||
meta.unique_id = file.unique_id;
|
||||
|
||||
@@ -212,19 +198,14 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
sub_compact->Current().AddOutput(std::move(meta),
|
||||
cfd->internal_comparator(), false, true,
|
||||
file.paranoid_hash);
|
||||
sub_compact->Current().UpdateTableProperties(file.table_properties);
|
||||
}
|
||||
sub_compact->compaction_job_stats = compaction_result.stats;
|
||||
sub_compact->Current().SetNumOutputRecords(
|
||||
compaction_result.stats.num_output_records);
|
||||
sub_compact->Current().SetNumOutputFiles(
|
||||
compaction_result.stats.num_output_files);
|
||||
sub_compact->Current().AddBytesWritten(compaction_result.bytes_written);
|
||||
compaction_result.num_output_records);
|
||||
sub_compact->Current().SetTotalBytes(compaction_result.total_bytes);
|
||||
RecordTick(stats_, REMOTE_COMPACT_READ_BYTES, compaction_result.bytes_read);
|
||||
RecordTick(stats_, REMOTE_COMPACT_WRITE_BYTES,
|
||||
compaction_result.bytes_written);
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kSuccess);
|
||||
return CompactionServiceJobStatus::kSuccess;
|
||||
}
|
||||
|
||||
@@ -239,18 +220,6 @@ void CompactionServiceCompactionJob::RecordCompactionIOStats() {
|
||||
CompactionJob::RecordCompactionIOStats();
|
||||
}
|
||||
|
||||
void CompactionServiceCompactionJob::UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const {
|
||||
compaction_job_stats_->elapsed_micros = stats.micros;
|
||||
|
||||
// output information only in remote compaction
|
||||
compaction_job_stats_->total_output_bytes = stats.bytes_written;
|
||||
compaction_job_stats_->total_output_bytes_blob = stats.bytes_written_blob;
|
||||
compaction_job_stats_->num_output_records = stats.num_output_records;
|
||||
compaction_job_stats_->num_output_files = stats.num_output_files;
|
||||
compaction_job_stats_->num_output_files_blob = stats.num_output_files_blob;
|
||||
}
|
||||
|
||||
CompactionServiceCompactionJob::CompactionServiceCompactionJob(
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const MutableDBOptions& mutable_db_options, const FileOptions& file_options,
|
||||
@@ -286,11 +255,11 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
|
||||
auto* c = compact_->compaction;
|
||||
assert(c->column_family_data() != nullptr);
|
||||
const VersionStorageInfo* storage_info = c->input_version()->storage_info();
|
||||
assert(storage_info);
|
||||
assert(storage_info->NumLevelFiles(compact_->compaction->level()) > 0);
|
||||
write_hint_ = storage_info->CalculateSSTWriteHint(c->output_level());
|
||||
assert(c->column_family_data()->current()->storage_info()->NumLevelFiles(
|
||||
compact_->compaction->level()) > 0);
|
||||
|
||||
write_hint_ =
|
||||
c->column_family_data()->CalculateSSTWriteHint(c->output_level());
|
||||
bottommost_level_ = c->bottommost_level();
|
||||
|
||||
Slice begin = compaction_input_.begin;
|
||||
@@ -305,12 +274,7 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
|
||||
log_buffer_->FlushBufferToLog();
|
||||
LogCompaction();
|
||||
|
||||
compaction_result_->stats.Reset();
|
||||
|
||||
const uint64_t start_micros = db_options_.clock->NowMicros();
|
||||
c->GetOrInitInputTableProperties();
|
||||
|
||||
// Pick the only sub-compaction we should have
|
||||
assert(compact_->sub_compact_states.size() == 1);
|
||||
SubcompactionState* sub_compact = compact_->sub_compact_states.data();
|
||||
@@ -348,50 +312,38 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
if (status.ok()) {
|
||||
status = io_s;
|
||||
}
|
||||
if (status.ok()) {
|
||||
// TODO: Add verify_table()
|
||||
}
|
||||
|
||||
// Finish up all book-keeping to unify the subcompaction results
|
||||
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
|
||||
UpdateCompactionStats();
|
||||
RecordCompactionIOStats();
|
||||
|
||||
LogFlush(db_options_.info_log);
|
||||
compact_->status = status;
|
||||
compact_->status.PermitUncheckedError();
|
||||
|
||||
// Build Compaction Job Stats
|
||||
|
||||
// 1. Aggregate CompactionOutputStats into Internal Compaction Stats
|
||||
// (compaction_stats_) and aggregate Compaction Job Stats
|
||||
// (compaction_job_stats_) from the sub compactions
|
||||
compact_->AggregateCompactionStats(compaction_stats_, *compaction_job_stats_);
|
||||
|
||||
// 2. Update the Output information in the Compaction Job Stats with
|
||||
// aggregated Internal Compaction Stats.
|
||||
UpdateCompactionJobStats(compaction_stats_.stats);
|
||||
|
||||
// 3. Set fields that are not propagated as part of aggregations above
|
||||
compaction_result_->stats.is_manual_compaction = c->is_manual_compaction();
|
||||
compaction_result_->stats.is_full_compaction = c->is_full_compaction();
|
||||
compaction_result_->stats.is_remote_compaction = true;
|
||||
|
||||
// 4. Update IO Stats that are not part of the aggregations above (bytes_read,
|
||||
// bytes_written)
|
||||
RecordCompactionIOStats();
|
||||
|
||||
// Build Output
|
||||
// Build compaction result
|
||||
compaction_result_->output_level = compact_->compaction->output_level();
|
||||
compaction_result_->output_path = output_path_;
|
||||
if (status.ok()) {
|
||||
for (const auto& output_file : sub_compact->GetOutputs()) {
|
||||
auto& meta = output_file.meta;
|
||||
compaction_result_->output_files.emplace_back(
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
|
||||
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
|
||||
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.epoch_number, meta.file_checksum,
|
||||
meta.file_checksum_func_name, output_file.validator.GetHash(),
|
||||
meta.marked_for_compaction, meta.unique_id,
|
||||
*output_file.table_properties);
|
||||
}
|
||||
for (const auto& output_file : sub_compact->GetOutputs()) {
|
||||
auto& meta = output_file.meta;
|
||||
compaction_result_->output_files.emplace_back(
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
|
||||
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
|
||||
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.epoch_number,
|
||||
output_file.validator.GetHash(), meta.marked_for_compaction,
|
||||
meta.unique_id);
|
||||
}
|
||||
InternalStats::CompactionStatsFull compaction_stats;
|
||||
sub_compact->AggregateCompactionStats(compaction_stats);
|
||||
compaction_result_->num_output_records =
|
||||
compaction_stats.stats.num_output_records;
|
||||
compaction_result_->total_bytes = compaction_stats.TotalBytesWritten();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionServiceCompactionJob::Run:0",
|
||||
&compaction_result_);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -444,9 +396,42 @@ static std::unordered_map<std::string, OptionTypeInfo> cfd_type_info = {
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo> cs_input_type_info = {
|
||||
{"cf_name",
|
||||
{offsetof(struct CompactionServiceInput, cf_name),
|
||||
OptionType::kEncodedString}},
|
||||
{"column_family",
|
||||
OptionTypeInfo::Struct(
|
||||
"column_family", &cfd_type_info,
|
||||
offsetof(struct CompactionServiceInput, column_family),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
|
||||
{"db_options",
|
||||
{offsetof(struct CompactionServiceInput, db_options),
|
||||
OptionType::kConfigurable, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone,
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const std::string& value, void* addr) {
|
||||
auto options = static_cast<DBOptions*>(addr);
|
||||
return GetDBOptionsFromString(opts, DBOptions(), value, options);
|
||||
},
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const void* addr, std::string* value) {
|
||||
const auto options = static_cast<const DBOptions*>(addr);
|
||||
std::string result;
|
||||
auto status = GetStringFromDBOptions(opts, *options, &result);
|
||||
*value = "{" + result + "}";
|
||||
return status;
|
||||
},
|
||||
[](const ConfigOptions& opts, const std::string& name, const void* addr1,
|
||||
const void* addr2, std::string* mismatch) {
|
||||
const auto this_one = static_cast<const DBOptions*>(addr1);
|
||||
const auto that_one = static_cast<const DBOptions*>(addr2);
|
||||
auto this_conf = DBOptionsAsConfigurable(*this_one);
|
||||
auto that_conf = DBOptionsAsConfigurable(*that_one);
|
||||
std::string mismatch_opt;
|
||||
bool result =
|
||||
this_conf->AreEquivalent(opts, that_conf.get(), &mismatch_opt);
|
||||
if (!result) {
|
||||
*mismatch = name + "." + mismatch_opt;
|
||||
}
|
||||
return result;
|
||||
}}},
|
||||
{"snapshots", OptionTypeInfo::Vector<uint64_t>(
|
||||
offsetof(struct CompactionServiceInput, snapshots),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
|
||||
@@ -474,10 +459,6 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_input_type_info = {
|
||||
{"end",
|
||||
{offsetof(struct CompactionServiceInput, end), OptionType::kEncodedString,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
|
||||
{"options_file_number",
|
||||
{offsetof(struct CompactionServiceInput, options_file_number),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
@@ -514,14 +495,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct CompactionServiceOutputFile, epoch_number),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"file_checksum",
|
||||
{offsetof(struct CompactionServiceOutputFile, file_checksum),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"file_checksum_func_name",
|
||||
{offsetof(struct CompactionServiceOutputFile, file_checksum_func_name),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"paranoid_hash",
|
||||
{offsetof(struct CompactionServiceOutputFile, paranoid_hash),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
@@ -535,30 +508,7 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
offsetof(struct CompactionServiceOutputFile, unique_id),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
|
||||
{0, OptionType::kUInt64T})},
|
||||
{"table_properties",
|
||||
{offsetof(struct CompactionServiceOutputFile, table_properties),
|
||||
OptionType::kStruct, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone,
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const std::string& value, void* addr) {
|
||||
auto table_properties = static_cast<TableProperties*>(addr);
|
||||
return TableProperties::Parse(opts, value, table_properties);
|
||||
},
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const void* addr, std::string* value) {
|
||||
const auto table_properties =
|
||||
static_cast<const TableProperties*>(addr);
|
||||
std::string result;
|
||||
auto status = table_properties->Serialize(opts, &result);
|
||||
*value = "{" + result + "}";
|
||||
return status;
|
||||
},
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const void* addr1, const void* addr2, std::string* mismatch) {
|
||||
const auto this_one = static_cast<const TableProperties*>(addr1);
|
||||
const auto that_one = static_cast<const TableProperties*>(addr2);
|
||||
return this_one->AreEqual(opts, that_one, mismatch);
|
||||
}}}};
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
compaction_job_stats_type_info = {
|
||||
@@ -605,10 +555,6 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct CompactionJobStats, is_manual_compaction),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"is_remote_compaction",
|
||||
{offsetof(struct CompactionJobStats, is_remote_compaction),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"total_input_bytes",
|
||||
{offsetof(struct CompactionJobStats, total_input_bytes),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
@@ -777,6 +723,14 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_result_type_info = {
|
||||
{offsetof(struct CompactionServiceResult, output_path),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"num_output_records",
|
||||
{offsetof(struct CompactionServiceResult, num_output_records),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"total_bytes",
|
||||
{offsetof(struct CompactionServiceResult, total_bytes),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"bytes_read",
|
||||
{offsetof(struct CompactionServiceResult, bytes_read),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/utilities/options_util.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -21,10 +21,8 @@ class MyTestCompactionService : public CompactionService {
|
||||
: db_path_(std::move(db_path)),
|
||||
options_(options),
|
||||
statistics_(statistics),
|
||||
start_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false),
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false),
|
||||
start_info_("na", "na", "na", 0, Env::TOTAL),
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL),
|
||||
listeners_(listeners),
|
||||
table_properties_collector_factories_(
|
||||
std::move(table_properties_collector_factories)) {}
|
||||
@@ -99,12 +97,8 @@ class MyTestCompactionService : public CompactionService {
|
||||
Status s =
|
||||
DB::OpenAndCompact(options, db_path_, db_path_ + "/" + scheduled_job_id,
|
||||
compaction_input, result, options_override);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (is_override_wait_result_) {
|
||||
*result = override_wait_result_;
|
||||
}
|
||||
result_ = *result;
|
||||
if (is_override_wait_result_) {
|
||||
*result = override_wait_result_;
|
||||
}
|
||||
compaction_num_.fetch_add(1);
|
||||
if (s.ok()) {
|
||||
@@ -114,11 +108,6 @@ class MyTestCompactionService : public CompactionService {
|
||||
}
|
||||
}
|
||||
|
||||
void OnInstallation(const std::string& /*scheduled_job_id*/,
|
||||
CompactionServiceJobStatus status) override {
|
||||
final_updated_status_ = status;
|
||||
}
|
||||
|
||||
int GetCompactionNum() { return compaction_num_.load(); }
|
||||
|
||||
CompactionServiceJobInfo GetCompactionInfoForStart() { return start_info_; }
|
||||
@@ -147,14 +136,6 @@ class MyTestCompactionService : public CompactionService {
|
||||
|
||||
void SetCanceled(bool canceled) { canceled_ = canceled; }
|
||||
|
||||
void GetResult(CompactionServiceResult* deserialized) {
|
||||
CompactionServiceResult::Read(result_, deserialized).PermitUncheckedError();
|
||||
}
|
||||
|
||||
CompactionServiceJobStatus GetFinalCompactionServiceJobStatus() {
|
||||
return final_updated_status_.load();
|
||||
}
|
||||
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic_int compaction_num_{0};
|
||||
@@ -172,14 +153,11 @@ class MyTestCompactionService : public CompactionService {
|
||||
CompactionServiceJobStatus override_wait_status_ =
|
||||
CompactionServiceJobStatus::kFailure;
|
||||
bool is_override_wait_result_ = false;
|
||||
std::string result_;
|
||||
std::string override_wait_result_;
|
||||
std::vector<std::shared_ptr<EventListener>> listeners_;
|
||||
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
|
||||
table_properties_collector_factories_;
|
||||
std::atomic_bool canceled_{false};
|
||||
std::atomic<CompactionServiceJobStatus> final_updated_status_{
|
||||
CompactionServiceJobStatus::kUseLocal};
|
||||
};
|
||||
|
||||
class CompactionServiceTest : public DBTestBase {
|
||||
@@ -277,8 +255,6 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), 1);
|
||||
ASSERT_EQ(CompactionServiceJobStatus::kSuccess,
|
||||
my_cs->GetFinalCompactionServiceJobStatus());
|
||||
|
||||
// make sure the compaction statistics is only recorded on the remote side
|
||||
ASSERT_GE(compactor_statistics->getTickerCount(COMPACT_WRITE_BYTES), 1);
|
||||
@@ -342,34 +318,6 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "cf_1", "cf_2", "cf_3"},
|
||||
options);
|
||||
ASSERT_GT(verify_passed, 0);
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
if (s.IsAborted()) {
|
||||
ASSERT_NOK(result.status);
|
||||
} else {
|
||||
ASSERT_OK(result.status);
|
||||
}
|
||||
ASSERT_GE(result.stats.elapsed_micros, 1);
|
||||
ASSERT_GE(result.stats.cpu_micros, 1);
|
||||
|
||||
ASSERT_EQ(20, result.stats.num_output_records);
|
||||
ASSERT_EQ(result.output_files.size(), result.stats.num_output_files);
|
||||
|
||||
uint64_t total_size = 0;
|
||||
for (auto output_file : result.output_files) {
|
||||
std::string file_name = result.output_path + "/" + output_file.file_name;
|
||||
|
||||
uint64_t file_size = 0;
|
||||
ASSERT_OK(options.env->GetFileSize(file_name, &file_size));
|
||||
ASSERT_GT(file_size, 0);
|
||||
total_size += file_size;
|
||||
}
|
||||
ASSERT_EQ(total_size, result.stats.total_output_bytes);
|
||||
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_FALSE(result.stats.is_full_compaction);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -408,547 +356,6 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
VerifyTestData();
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CompactionOutputFileIOError) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::FinishCompactionOutputFile()::AfterFinish",
|
||||
[&](void* status) {
|
||||
// override status
|
||||
auto s = static_cast<Status*>(status);
|
||||
*s = Status::IOError("Injected IOError!");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
ASSERT_NOK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_NOK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, PreservedOptionsLocalCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
ASSERT_OK(
|
||||
Put("foo" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing", [&](void* arg) {
|
||||
auto compaction = static_cast<Compaction*>(arg);
|
||||
std::string options_file_name = OptionsFileName(
|
||||
dbname_,
|
||||
compaction->input_version()->version_set()->options_file_number());
|
||||
|
||||
// Change option twice to make sure the very first OPTIONS file gets
|
||||
// purged
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"level0_file_num_compaction_trigger", "4"}}));
|
||||
ASSERT_EQ(4, dbfull()->GetOptions().level0_file_num_compaction_trigger);
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"level0_file_num_compaction_trigger", "6"}}));
|
||||
ASSERT_EQ(6, dbfull()->GetOptions().level0_file_num_compaction_trigger);
|
||||
dbfull()->TEST_DeleteObsoleteFiles();
|
||||
|
||||
// For non-remote compactions, OPTIONS file can be deleted while
|
||||
// using option at the start of the compaction
|
||||
Status s = env_->FileExists(options_file_name);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
// Should be old value
|
||||
ASSERT_EQ(2, compaction->mutable_cf_options()
|
||||
->level0_file_num_compaction_trigger);
|
||||
ASSERT_TRUE(dbfull()->min_options_file_numbers_.empty());
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(s.ok());
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, PreservedOptionsRemoteCompaction) {
|
||||
// For non-remote compaction do not preserve options file
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
ASSERT_OK(
|
||||
Put("foo" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"CompactionServiceTest::OptionsFileChanged",
|
||||
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:1"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:0",
|
||||
[&](void* arg) {
|
||||
auto options_file_number = static_cast<uint64_t*>(arg);
|
||||
// Change the option twice before the compaction run
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"level0_file_num_compaction_trigger", "4"}}));
|
||||
ASSERT_EQ(4, dbfull()->GetOptions().level0_file_num_compaction_trigger);
|
||||
ASSERT_TRUE(dbfull()->versions_->options_file_number() >
|
||||
*options_file_number);
|
||||
|
||||
// Change the option twice before the compaction run
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"level0_file_num_compaction_trigger", "5"}}));
|
||||
ASSERT_EQ(5, dbfull()->GetOptions().level0_file_num_compaction_trigger);
|
||||
ASSERT_TRUE(dbfull()->versions_->options_file_number() >
|
||||
*options_file_number);
|
||||
|
||||
TEST_SYNC_POINT("CompactionServiceTest::OptionsFileChanged");
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceJob::ProcessKeyValueCompactionWithCompactionService",
|
||||
[&](void* arg) {
|
||||
auto input = static_cast<CompactionServiceInput*>(arg);
|
||||
std::string options_file_name =
|
||||
OptionsFileName(dbname_, input->options_file_number);
|
||||
|
||||
ASSERT_OK(env_->FileExists(options_file_name));
|
||||
ASSERT_FALSE(dbfull()->min_options_file_numbers_.empty());
|
||||
ASSERT_EQ(dbfull()->min_options_file_numbers_.front(),
|
||||
input->options_file_number);
|
||||
|
||||
DBOptions db_options;
|
||||
ConfigOptions config_options;
|
||||
std::vector<ColumnFamilyDescriptor> all_column_families;
|
||||
config_options.env = env_;
|
||||
ASSERT_OK(LoadOptionsFromFile(config_options, options_file_name,
|
||||
&db_options, &all_column_families));
|
||||
bool has_cf = false;
|
||||
for (auto& cf : all_column_families) {
|
||||
if (cf.name == input->cf_name) {
|
||||
// Should be old value
|
||||
ASSERT_EQ(2, cf.options.level0_file_num_compaction_trigger);
|
||||
has_cf = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(has_cf);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing", [&](void* arg) {
|
||||
auto compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_EQ(2, compaction->mutable_cf_options()
|
||||
->level0_file_num_compaction_trigger);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(s.ok());
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
class EventVerifier : public EventListener {
|
||||
public:
|
||||
explicit EventVerifier(uint64_t expected_num_input_records,
|
||||
size_t expected_num_input_files,
|
||||
uint64_t expected_num_output_records,
|
||||
size_t expected_num_output_files,
|
||||
const std::string& expected_smallest_output_key_prefix,
|
||||
const std::string& expected_largest_output_key_prefix,
|
||||
bool expected_is_remote_compaction_on_begin,
|
||||
bool expected_is_remote_compaction_on_complete)
|
||||
: expected_num_input_records_(expected_num_input_records),
|
||||
expected_num_input_files_(expected_num_input_files),
|
||||
expected_num_output_records_(expected_num_output_records),
|
||||
expected_num_output_files_(expected_num_output_files),
|
||||
expected_smallest_output_key_prefix_(
|
||||
expected_smallest_output_key_prefix),
|
||||
expected_largest_output_key_prefix_(expected_largest_output_key_prefix),
|
||||
expected_is_remote_compaction_on_begin_(
|
||||
expected_is_remote_compaction_on_begin),
|
||||
expected_is_remote_compaction_on_complete_(
|
||||
expected_is_remote_compaction_on_complete) {}
|
||||
void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(expected_num_input_files_, ci.input_files.size());
|
||||
ASSERT_EQ(expected_num_input_files_, ci.input_file_infos.size());
|
||||
ASSERT_EQ(expected_is_remote_compaction_on_begin_,
|
||||
ci.stats.is_remote_compaction);
|
||||
ASSERT_TRUE(ci.stats.is_manual_compaction);
|
||||
ASSERT_FALSE(ci.stats.is_full_compaction);
|
||||
}
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_GT(ci.stats.elapsed_micros, 0);
|
||||
ASSERT_GT(ci.stats.cpu_micros, 0);
|
||||
ASSERT_EQ(expected_num_input_records_, ci.stats.num_input_records);
|
||||
ASSERT_EQ(expected_num_input_files_, ci.stats.num_input_files);
|
||||
ASSERT_EQ(expected_num_output_records_, ci.stats.num_output_records);
|
||||
ASSERT_EQ(expected_num_output_files_, ci.stats.num_output_files);
|
||||
ASSERT_EQ(expected_smallest_output_key_prefix_,
|
||||
ci.stats.smallest_output_key_prefix);
|
||||
ASSERT_EQ(expected_largest_output_key_prefix_,
|
||||
ci.stats.largest_output_key_prefix);
|
||||
ASSERT_GT(ci.stats.total_input_bytes, 0);
|
||||
ASSERT_GT(ci.stats.total_output_bytes, 0);
|
||||
ASSERT_EQ(ci.stats.num_input_records,
|
||||
ci.stats.num_output_records + ci.stats.num_records_replaced);
|
||||
ASSERT_EQ(expected_is_remote_compaction_on_complete_,
|
||||
ci.stats.is_remote_compaction);
|
||||
ASSERT_TRUE(ci.stats.is_manual_compaction);
|
||||
ASSERT_FALSE(ci.stats.is_full_compaction);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t expected_num_input_records_;
|
||||
size_t expected_num_input_files_;
|
||||
uint64_t expected_num_output_records_;
|
||||
size_t expected_num_output_files_;
|
||||
std::string expected_smallest_output_key_prefix_;
|
||||
std::string expected_largest_output_key_prefix_;
|
||||
bool expected_is_remote_compaction_on_begin_;
|
||||
bool expected_is_remote_compaction_on_complete_;
|
||||
};
|
||||
|
||||
TEST_F(CompactionServiceTest, VerifyStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
auto event_verifier = std::make_shared<EventVerifier>(
|
||||
30 /* expected_num_input_records */, 3 /* expected_num_input_files */,
|
||||
20 /* expected_num_output_records */, 1 /* expected_num_output_files */,
|
||||
"key00000" /* expected_smallest_output_key_prefix */,
|
||||
"key00001" /* expected_largest_output_key_prefix */,
|
||||
true /* expected_is_remote_compaction_on_begin */,
|
||||
true /* expected_is_remote_compaction_on_complete */);
|
||||
options.listeners.push_back(event_verifier);
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(0);
|
||||
std::string end_str = Key(1);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
VerifyTestData();
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, VerifyStatsLocalFallback) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
auto event_verifier = std::make_shared<EventVerifier>(
|
||||
30 /* expected_num_input_records */, 3 /* expected_num_input_files */,
|
||||
20 /* expected_num_output_records */, 1 /* expected_num_output_files */,
|
||||
"key00000" /* expected_smallest_output_key_prefix */,
|
||||
"key00001" /* expected_largest_output_key_prefix */,
|
||||
true /* expected_is_remote_compaction_on_begin */,
|
||||
false /* expected_is_remote_compaction_on_complete */);
|
||||
options.listeners.push_back(event_verifier);
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
my_cs->OverrideStartStatus(CompactionServiceJobStatus::kUseLocal);
|
||||
|
||||
std::string start_str = Key(0);
|
||||
std::string end_str = Key(1);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
// Remote Compaction did not happen
|
||||
ASSERT_EQ(my_cs->GetCompactionNum(), comp_num);
|
||||
VerifyTestData();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CorruptedOutput) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
*(static_cast<CompactionServiceResult**>(arg));
|
||||
ASSERT_TRUE(compaction_result != nullptr &&
|
||||
!compaction_result->output_files.empty());
|
||||
// Corrupt files here
|
||||
for (const auto& output_file : compaction_result->output_files) {
|
||||
std::string file_name =
|
||||
compaction_result->output_path + "/" + output_file.file_name;
|
||||
|
||||
uint64_t file_size = 0;
|
||||
Status s = options.env->GetFileSize(file_name, &file_size);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_GT(file_size, 0);
|
||||
|
||||
ASSERT_OK(test::CorruptFile(env_, file_name, 0,
|
||||
static_cast<int>(file_size),
|
||||
true /* verifyChecksum */));
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// CompactRange() should fail
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// On the worker side, the compaction is considered success
|
||||
// Verification is done on the primary side
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CorruptedOutputParanoidFileCheck) {
|
||||
for (bool paranoid_file_check_enabled : {false, true}) {
|
||||
SCOPED_TRACE("paranoid_file_check_enabled=" +
|
||||
std::to_string(paranoid_file_check_enabled));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
Destroy(options);
|
||||
options.disable_auto_compactions = true;
|
||||
options.paranoid_file_checks = paranoid_file_check_enabled;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
*(static_cast<CompactionServiceResult**>(arg));
|
||||
ASSERT_TRUE(compaction_result != nullptr &&
|
||||
!compaction_result->output_files.empty());
|
||||
// Corrupt files here
|
||||
for (const auto& output_file : compaction_result->output_files) {
|
||||
std::string file_name =
|
||||
compaction_result->output_path + "/" + output_file.file_name;
|
||||
|
||||
// Corrupt very small range of bytes. This corruption is so small
|
||||
// that this isn't caught by default light-weight check
|
||||
ASSERT_OK(test::CorruptFile(env_, file_name, 0, 1,
|
||||
false /* verifyChecksum */));
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
if (paranoid_file_check_enabled) {
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_EQ(Status::Corruption("Paranoid checksums do not match"), s);
|
||||
} else {
|
||||
// CompactRange() goes through if paranoid file check is not enabled
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// On the worker side, the compaction is considered success
|
||||
// Verification is done on the primary side
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, TruncatedOutput) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
*(static_cast<CompactionServiceResult**>(arg));
|
||||
ASSERT_TRUE(compaction_result != nullptr &&
|
||||
!compaction_result->output_files.empty());
|
||||
// Truncate files here
|
||||
for (const auto& output_file : compaction_result->output_files) {
|
||||
std::string file_name =
|
||||
compaction_result->output_path + "/" + output_file.file_name;
|
||||
|
||||
uint64_t file_size = 0;
|
||||
Status s = options.env->GetFileSize(file_name, &file_size);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_GT(file_size, 0);
|
||||
|
||||
ASSERT_OK(test::TruncateFile(env_, file_name, file_size / 2));
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// CompactRange() should fail
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// On the worker side, the compaction is considered success
|
||||
// Verification is done on the primary side
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CustomFileChecksum) {
|
||||
Options options = CurrentOptions();
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
|
||||
std::string start_str = Key(15);
|
||||
std::string end_str = Key(45);
|
||||
Slice start(start_str);
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
*(static_cast<CompactionServiceResult**>(arg));
|
||||
ASSERT_TRUE(compaction_result != nullptr &&
|
||||
!compaction_result->output_files.empty());
|
||||
// Validate Checksum files here
|
||||
for (const auto& output_file : compaction_result->output_files) {
|
||||
std::string file_name =
|
||||
compaction_result->output_path + "/" + output_file.file_name;
|
||||
|
||||
FileChecksumGenContext gen_context;
|
||||
gen_context.file_name = file_name;
|
||||
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
|
||||
options.file_checksum_gen_factory->CreateFileChecksumGenerator(
|
||||
gen_context);
|
||||
|
||||
std::unique_ptr<SequentialFile> file_reader;
|
||||
uint64_t file_size = 0;
|
||||
Status s = options.env->GetFileSize(file_name, &file_size);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_GT(file_size, 0);
|
||||
|
||||
s = options.env->NewSequentialFile(file_name, &file_reader,
|
||||
EnvOptions());
|
||||
ASSERT_OK(s);
|
||||
|
||||
Slice result;
|
||||
std::unique_ptr<char[]> scratch(new char[file_size]);
|
||||
s = file_reader->Read(file_size, &result, scratch.get());
|
||||
ASSERT_OK(s);
|
||||
|
||||
file_checksum_gen->Update(scratch.get(), result.size());
|
||||
file_checksum_gen->Finalize();
|
||||
|
||||
// Verify actual checksum and the func name
|
||||
ASSERT_EQ(file_checksum_gen->Name(),
|
||||
output_file.file_checksum_func_name);
|
||||
ASSERT_EQ(file_checksum_gen->GetChecksum(),
|
||||
output_file.file_checksum);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CancelCompactionOnRemoteSide) {
|
||||
@@ -1030,8 +437,6 @@ TEST_F(CompactionServiceTest, InvalidResult) {
|
||||
Slice end(end_str);
|
||||
Status s = db_->CompactRange(CompactRangeOptions(), &start, &end);
|
||||
ASSERT_FALSE(s.ok());
|
||||
ASSERT_EQ(CompactionServiceJobStatus::kFailure,
|
||||
my_cs->GetFinalCompactionServiceJobStatus());
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, SubCompaction) {
|
||||
@@ -1181,20 +586,11 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
{file.db_path + "/" + file.name}, 2));
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
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);
|
||||
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);
|
||||
|
||||
// Test priority BOTTOM
|
||||
env_->SetBackgroundThreads(1, Env::BOTTOM);
|
||||
// This will set bottommost_level = true but is_full_compaction = false
|
||||
options.num_levels = 2;
|
||||
ReopenWithCompactionService(&options);
|
||||
my_cs =
|
||||
@@ -1217,71 +613,9 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
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(Env::BOTTOM, info.priority);
|
||||
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);
|
||||
|
||||
// Test Non-Bottommost Level
|
||||
options.num_levels = 4;
|
||||
ReopenWithCompactionService(&options);
|
||||
my_cs =
|
||||
static_cast_with_check<MyTestCompactionService>(GetCompactionService());
|
||||
|
||||
for (int i = 0; i < options.level0_file_num_compaction_trigger; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(false, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(false, info.bottommost_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);
|
||||
|
||||
// Test Full Compaction + Bottommost Level
|
||||
options.num_levels = 6;
|
||||
ReopenWithCompactionService(&options);
|
||||
my_cs =
|
||||
static_cast_with_check<MyTestCompactionService>(GetCompactionService());
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
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);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, FallbackLocalAuto) {
|
||||
|
||||
@@ -39,7 +39,7 @@ void CompactionState::AggregateCompactionStats(
|
||||
InternalStats::CompactionStatsFull& compaction_stats,
|
||||
CompactionJobStats& compaction_job_stats) {
|
||||
for (const auto& sc : sub_compact_states) {
|
||||
sc.AggregateCompactionOutputStats(compaction_stats);
|
||||
sc.AggregateCompactionStats(compaction_stats);
|
||||
compaction_job_stats.Add(sc.compaction_job_stats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "rocksdb/sst_partitioner.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
void SubcompactionState::AggregateCompactionOutputStats(
|
||||
void SubcompactionState::AggregateCompactionStats(
|
||||
InternalStats::CompactionStatsFull& compaction_stats) const {
|
||||
compaction_stats.stats.Add(compaction_outputs_.stats_);
|
||||
if (HasPenultimateLevelOutputs()) {
|
||||
@@ -34,16 +34,9 @@ void SubcompactionState::Cleanup(Cache* cache) {
|
||||
|
||||
if (!status.ok()) {
|
||||
for (const auto& out : GetOutputs()) {
|
||||
// If this file was inserted into the table cache then remove it here
|
||||
// because this compaction was not committed. This is not strictly
|
||||
// required because of a backstop TableCache::Evict() in
|
||||
// PurgeObsoleteFiles() but is our opportunity to apply
|
||||
// uncache_aggressiveness. TODO: instead, put these files into the
|
||||
// VersionSet::obsolete_files_ pipeline so that they don't have to
|
||||
// be picked up by scanning the DB directory.
|
||||
TableCache::ReleaseObsolete(
|
||||
cache, out.meta.fd.GetNumber(), nullptr /*handle*/,
|
||||
compaction->mutable_cf_options()->uncache_aggressiveness);
|
||||
// If this file was inserted into the table cache then remove
|
||||
// them here because this compaction was not committed.
|
||||
TableCache::Evict(cache, out.meta.fd.GetNumber());
|
||||
}
|
||||
}
|
||||
// TODO: sub_compact.io_status is not checked like status. Not sure if thats
|
||||
|
||||
@@ -179,7 +179,7 @@ class SubcompactionState {
|
||||
|
||||
void Cleanup(Cache* cache);
|
||||
|
||||
void AggregateCompactionOutputStats(
|
||||
void AggregateCompactionStats(
|
||||
InternalStats::CompactionStatsFull& compaction_stats) const;
|
||||
|
||||
CompactionOutputs& Current() const {
|
||||
|
||||
@@ -9,26 +9,14 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "test_util/mock_time_env.h"
|
||||
#include "util/defer.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
ConfigOptions GetStrictConfigOptions() {
|
||||
ConfigOptions config_options;
|
||||
config_options.ignore_unknown_options = false;
|
||||
config_options.ignore_unsupported_options = false;
|
||||
config_options.input_strings_escaped = false;
|
||||
return config_options;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class TieredCompactionTest : public DBTestBase {
|
||||
public:
|
||||
@@ -1245,9 +1233,82 @@ TEST_F(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
db_->ReleaseSnapshot(temp_snap);
|
||||
}
|
||||
|
||||
class PrecludeLastLevelTestBase : public DBTestBase {
|
||||
TEST_F(TieredCompactionTest, CheckInternalKeyRange) {
|
||||
// When compacting keys from the last level to penultimate level,
|
||||
// output to penultimate level should be within internal key range
|
||||
// of input files from penultimate level.
|
||||
// Set up:
|
||||
// L5:
|
||||
// File 1: DeleteRange[1, 3)@4, File 2: [3@5, 100@6]
|
||||
// L6:
|
||||
// File 3: [2@1, 3@2], File 4: [50@3]
|
||||
//
|
||||
// When File 1 and File 3 are being compacted,
|
||||
// Key(3) cannot be compacted up, otherwise it causes
|
||||
// inconsistency where File 3's Key(3) has a lower sequence number
|
||||
// than File 2's Key(3).
|
||||
const int kNumLevels = 7;
|
||||
auto options = CurrentOptions();
|
||||
SetColdTemperature(options);
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.num_levels = kNumLevels;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.max_subcompactions = 10;
|
||||
options.preclude_last_level_data_seconds = 10000;
|
||||
DestroyAndReopen(options);
|
||||
auto cmp = options.comparator;
|
||||
|
||||
std::string hot_start = Key(0);
|
||||
std::string hot_end = Key(0);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator::PrepareOutput.context", [&](void* arg) {
|
||||
auto context = static_cast<PerKeyPlacementContext*>(arg);
|
||||
context->output_to_penultimate_level =
|
||||
cmp->Compare(context->key, hot_start) >= 0 &&
|
||||
cmp->Compare(context->key, hot_end) < 0;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
// File 1
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
ASSERT_OK(Put(Key(3), "val3"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
// File 2
|
||||
ASSERT_OK(Put(Key(50), "val50"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
hot_end = Key(100);
|
||||
std::string start = Key(1);
|
||||
std::string end = Key(3);
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), start, end));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(5);
|
||||
// File 3
|
||||
ASSERT_OK(Put(Key(3), "vall"));
|
||||
ASSERT_OK(Put(Key(100), "val100"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(5);
|
||||
// Try to compact keys up
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
start = Key(1);
|
||||
end = Key(2);
|
||||
Slice begin_slice(start);
|
||||
Slice end_slice(end);
|
||||
ASSERT_OK(db_->CompactRange(cro, &begin_slice, &end_slice));
|
||||
// Without internal key range checking, we get the following error:
|
||||
// Corruption: force_consistency_checks(DEBUG): VersionBuilder: L5 has
|
||||
// overlapping ranges: file #18 largest key: '6B6579303030303033' seq:102,
|
||||
// type:1 vs. file #15 smallest key: '6B6579303030303033' seq:104, type:1
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
class PrecludeLastLevelTest : public DBTestBase {
|
||||
public:
|
||||
PrecludeLastLevelTestBase(std::string test_name = "preclude_last_level_test")
|
||||
PrecludeLastLevelTest(std::string test_name = "preclude_last_level_test")
|
||||
: DBTestBase(test_name, /*env_do_fsync=*/false) {
|
||||
mock_clock_ = std::make_shared<MockSystemClock>(env_->GetSystemClock());
|
||||
mock_clock_->SetCurrentTime(kMockStartTime);
|
||||
@@ -1272,52 +1333,9 @@ class PrecludeLastLevelTestBase : public DBTestBase {
|
||||
});
|
||||
mock_clock_->SetCurrentTime(kMockStartTime);
|
||||
}
|
||||
|
||||
void ApplyConfigChangeImpl(
|
||||
bool dynamic, Options* options,
|
||||
const std::unordered_map<std::string, std::string>& config_change,
|
||||
const std::unordered_map<std::string, std::string>& db_config_change) {
|
||||
if (dynamic) {
|
||||
if (config_change.size() > 0) {
|
||||
// FIXME: temporary while preserve/preclude options are not user mutable
|
||||
SaveAndRestore<bool> m(&TEST_allowSetOptionsImmutableInMutable, true);
|
||||
ASSERT_OK(db_->SetOptions(config_change));
|
||||
}
|
||||
if (db_config_change.size() > 0) {
|
||||
ASSERT_OK(db_->SetDBOptions(db_config_change));
|
||||
}
|
||||
} else {
|
||||
if (config_change.size() > 0) {
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(
|
||||
GetStrictConfigOptions(), *options, config_change, options));
|
||||
}
|
||||
if (db_config_change.size() > 0) {
|
||||
ASSERT_OK(GetDBOptionsFromMap(GetStrictConfigOptions(), *options,
|
||||
db_config_change, options));
|
||||
}
|
||||
Reopen(*options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PrecludeLastLevelTest : public PrecludeLastLevelTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
using PrecludeLastLevelTestBase::PrecludeLastLevelTestBase;
|
||||
|
||||
bool UseDynamicConfig() const { return GetParam(); }
|
||||
|
||||
void ApplyConfigChange(
|
||||
Options* options,
|
||||
const std::unordered_map<std::string, std::string>& config_change,
|
||||
const std::unordered_map<std::string, std::string>& db_config_change =
|
||||
{}) {
|
||||
ApplyConfigChangeImpl(UseDynamicConfig(), options, config_change,
|
||||
db_config_change);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimeManualCompaction) {
|
||||
TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeManualCompaction) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -1348,8 +1366,9 @@ TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimeManualCompaction) {
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
// enable preclude feature
|
||||
ApplyConfigChange(&options, {{"preclude_last_level_data_seconds", "10000"},
|
||||
{"last_level_temperature", "kCold"}});
|
||||
options.preclude_last_level_data_seconds = 10000;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
Reopen(options);
|
||||
|
||||
// all data is hot, even they're in the last level
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -1373,7 +1392,7 @@ TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimeManualCompaction) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -1403,17 +1422,16 @@ TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
// enable preclude feature, and...
|
||||
// enable preclude feature
|
||||
options.preclude_last_level_data_seconds = 10000;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
// make sure it won't trigger Size Amp compaction, unlike normal Size Amp
|
||||
// compaction which is typically a last level compaction, when tiered Storage
|
||||
// ("preclude_last_level") is enabled, size amp won't include the last level.
|
||||
// As the last level would be in cold tier and the size would not be a
|
||||
// problem, which also avoid frequent hot to cold storage compaction.
|
||||
ApplyConfigChange(
|
||||
&options,
|
||||
{{"preclude_last_level_data_seconds", "10000"},
|
||||
{"last_level_temperature", "kCold"},
|
||||
{"compaction_options_universal.max_size_amplification_percent", "400"}});
|
||||
options.compaction_options_universal.max_size_amplification_percent = 400;
|
||||
Reopen(options);
|
||||
|
||||
// all data is hot, even they're in the last level
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -1445,7 +1463,7 @@ TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimePartial) {
|
||||
TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimePartial) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -1493,8 +1511,9 @@ TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimePartial) {
|
||||
}
|
||||
|
||||
// enable preclude feature
|
||||
ApplyConfigChange(&options, {{"preclude_last_level_data_seconds", "2000"},
|
||||
{"last_level_temperature", "kCold"}});
|
||||
options.preclude_last_level_data_seconds = 2000;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
Reopen(options);
|
||||
|
||||
// Generate a sstable and trigger manual compaction
|
||||
ASSERT_OK(Put(Key(10), "value"));
|
||||
@@ -1512,7 +1531,7 @@ TEST_P(PrecludeLastLevelTest, MigrationFromPreserveTimePartial) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, SmallPrecludeTime) {
|
||||
TEST_F(PrecludeLastLevelTest, SmallPrecludeTime) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -1565,203 +1584,13 @@ TEST_P(PrecludeLastLevelTest, SmallPrecludeTime) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, CheckInternalKeyRange) {
|
||||
// When compacting keys from the last level to penultimate level,
|
||||
// output to penultimate level should be within internal key range
|
||||
// of input files from penultimate level.
|
||||
// Set up:
|
||||
// L5:
|
||||
// File 1: DeleteRange[1, 3)@4, File 2: [3@5, 100@6]
|
||||
// L6:
|
||||
// File 3: [2@1, 3@2], File 4: [50@3]
|
||||
//
|
||||
// When File 1 and File 3 are being compacted,
|
||||
// Key(3) cannot be compacted up, otherwise it causes
|
||||
// inconsistency where File 3's Key(3) has a lower sequence number
|
||||
// than File 2's Key(3).
|
||||
const int kNumLevels = 7;
|
||||
auto options = CurrentOptions();
|
||||
options.env = mock_env_.get();
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.num_levels = kNumLevels;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.max_subcompactions = 10;
|
||||
options.preserve_internal_time_seconds = 10000;
|
||||
DestroyAndReopen(options);
|
||||
// File 3
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
ASSERT_OK(Put(Key(3), "val3"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
// File 4
|
||||
ASSERT_OK(Put(Key(50), "val50"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
|
||||
ApplyConfigChange(&options, {{"preclude_last_level_data_seconds", "10000"}});
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// File 1
|
||||
std::string start = Key(1);
|
||||
std::string end = Key(3);
|
||||
ASSERT_OK(db_->DeleteRange({}, db_->DefaultColumnFamily(), start, end));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(5);
|
||||
// File 2
|
||||
ASSERT_OK(Put(Key(3), "vall"));
|
||||
ASSERT_OK(Put(Key(100), "val100"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(5);
|
||||
|
||||
ASSERT_EQ("0,0,0,0,0,2,2", FilesPerLevel());
|
||||
|
||||
auto VerifyLogicalState = [&]() {
|
||||
// First with snapshot
|
||||
ASSERT_EQ("val2", Get(Key(2), snapshot));
|
||||
ASSERT_EQ("val3", Get(Key(3), snapshot));
|
||||
ASSERT_EQ("val50", Get(Key(50), snapshot));
|
||||
ASSERT_EQ("NOT_FOUND", Get(Key(100), snapshot));
|
||||
|
||||
// Then without snapshot
|
||||
ASSERT_EQ("NOT_FOUND", Get(Key(2)));
|
||||
ASSERT_EQ("vall", Get(Key(3)));
|
||||
ASSERT_EQ("val50", Get(Key(50)));
|
||||
ASSERT_EQ("val100", Get(Key(100)));
|
||||
};
|
||||
|
||||
VerifyLogicalState();
|
||||
|
||||
// Try to compact keys up
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
// Without internal key range checking, we get the following error:
|
||||
// Corruption: force_consistency_checks(DEBUG): VersionBuilder: L5 has
|
||||
// overlapping ranges: file #18 largest key: '6B6579303030303033' seq:102,
|
||||
// type:1 vs. file #15 smallest key: '6B6579303030303033' seq:104, type:1
|
||||
ASSERT_OK(CompactRange(cro, Key(1), Key(2)));
|
||||
|
||||
VerifyLogicalState();
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, RangeTombstoneSnapshotMigrateFromLast) {
|
||||
// Reproducer for issue originally described in
|
||||
// https://github.com/facebook/rocksdb/pull/9964/files#r1024449523
|
||||
if (!UseDynamicConfig()) {
|
||||
// Depends on config change while holding a snapshot
|
||||
return;
|
||||
}
|
||||
const int kNumLevels = 7;
|
||||
auto options = CurrentOptions();
|
||||
options.env = mock_env_.get();
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.num_levels = kNumLevels;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.max_subcompactions = 10;
|
||||
options.preserve_internal_time_seconds = 30000;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Entries with much older write time
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
ASSERT_OK(Put(Key(6), "val6"));
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(1000)); });
|
||||
}
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
ASSERT_OK(db_->DeleteRange({}, db_->DefaultColumnFamily(), Key(1), Key(5)));
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
ApplyConfigChange(&options, {{"preclude_last_level_data_seconds", "10000"}});
|
||||
|
||||
// To exercise the WithinPenultimateLevelOutputRange feature, we want files
|
||||
// around the middle file to be compacted on the penultimate level
|
||||
ASSERT_OK(Put(Key(0), "val0"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(3), "val3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(7), "val7"));
|
||||
|
||||
// FIXME: ideally this wouldn't be necessary to get a seqno to time entry
|
||||
// into a later compaction to get data into the last level
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(1000)); });
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(5);
|
||||
ASSERT_EQ("0,0,0,0,0,3,1", FilesPerLevel());
|
||||
|
||||
auto VerifyLogicalState = [&]() {
|
||||
// First with snapshot
|
||||
if (snapshot) {
|
||||
ASSERT_EQ("NOT_FOUND", Get(Key(0), snapshot));
|
||||
ASSERT_EQ("NOT_FOUND", Get(Key(1), snapshot));
|
||||
ASSERT_EQ("val2", Get(Key(2), snapshot));
|
||||
ASSERT_EQ("NOT_FOUND", Get(Key(3), snapshot));
|
||||
ASSERT_EQ("val6", Get(Key(6), snapshot));
|
||||
ASSERT_EQ("NOT_FOUND", Get(Key(7), snapshot));
|
||||
}
|
||||
|
||||
// Then without snapshot
|
||||
ASSERT_EQ("val0", Get(Key(0)));
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_EQ("NOT_FOUND", Get(Key(2)));
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
ASSERT_EQ("val6", Get(Key(6)));
|
||||
ASSERT_EQ("val7", Get(Key(7)));
|
||||
};
|
||||
|
||||
VerifyLogicalState();
|
||||
|
||||
// Try a limited range compaction
|
||||
// FIXME: this currently hits the "Unsafe to store Seq later than snapshot"
|
||||
// error. Needs to work safely for preclude option to be user mutable.
|
||||
// ASSERT_OK(CompactRange({}, Key(3), Key(4)));
|
||||
EXPECT_EQ("0,0,0,0,0,3,1", FilesPerLevel());
|
||||
VerifyLogicalState();
|
||||
|
||||
// Compact everything, but some data still goes to both penultimate and last
|
||||
// levels. A full-range compaction should be safe to "migrate" data from the
|
||||
// last level to penultimate (because of preclude setting change).
|
||||
ASSERT_OK(CompactRange({}, {}, {}));
|
||||
EXPECT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
VerifyLogicalState();
|
||||
// Key1 should have been migrated out of the last level
|
||||
auto& meta = *GetLevelFileMetadatas(6)[0];
|
||||
ASSERT_LT(Key(1), meta.smallest.user_key().ToString());
|
||||
|
||||
// Make data eligible for last level
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
snapshot = nullptr;
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(10000));
|
||||
|
||||
ASSERT_OK(CompactRange({}, {}, {}));
|
||||
EXPECT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
VerifyLogicalState();
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(PrecludeLastLevelTest, PrecludeLastLevelTest,
|
||||
::testing::Bool());
|
||||
|
||||
// Test Param: protection_bytes_per_key for WriteBatch
|
||||
class TimedPutPrecludeLastLevelTest
|
||||
: public PrecludeLastLevelTestBase,
|
||||
: public PrecludeLastLevelTest,
|
||||
public testing::WithParamInterface<size_t> {
|
||||
public:
|
||||
TimedPutPrecludeLastLevelTest()
|
||||
: PrecludeLastLevelTestBase("timed_put_preclude_last_level_test") {}
|
||||
|
||||
size_t ProtectionBytesPerKey() const { return GetParam(); }
|
||||
: PrecludeLastLevelTest("timed_put_preclude_last_level_test") {}
|
||||
};
|
||||
|
||||
TEST_P(TimedPutPrecludeLastLevelTest, FastTrackTimedPutToLastLevel) {
|
||||
@@ -1779,7 +1608,7 @@ TEST_P(TimedPutPrecludeLastLevelTest, FastTrackTimedPutToLastLevel) {
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions wo;
|
||||
wo.protection_bytes_per_key = ProtectionBytesPerKey();
|
||||
wo.protection_bytes_per_key = GetParam();
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
@@ -1833,7 +1662,7 @@ TEST_P(TimedPutPrecludeLastLevelTest, InterleavedTimedPutAndPut) {
|
||||
options.default_write_temperature = Temperature::kHot;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions wo;
|
||||
wo.protection_bytes_per_key = ProtectionBytesPerKey();
|
||||
wo.protection_bytes_per_key = GetParam();
|
||||
|
||||
// Start time: kMockStartTime = 10000000;
|
||||
ASSERT_OK(TimedPut(0, Key(0), "v0", kMockStartTime - 1 * 24 * 60 * 60, wo));
|
||||
@@ -1859,7 +1688,7 @@ TEST_P(TimedPutPrecludeLastLevelTest, PreserveTimedPutOnPenultimateLevel) {
|
||||
options.default_write_temperature = Temperature::kHot;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions wo;
|
||||
wo.protection_bytes_per_key = ProtectionBytesPerKey();
|
||||
wo.protection_bytes_per_key = GetParam();
|
||||
|
||||
// Creating a snapshot to manually control when preferred sequence number is
|
||||
// swapped in. An entry's preferred seqno won't get swapped in until it's
|
||||
@@ -1905,86 +1734,10 @@ TEST_P(TimedPutPrecludeLastLevelTest, PreserveTimedPutOnPenultimateLevel) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(TimedPutPrecludeLastLevelTest, AutoTriggerCompaction) {
|
||||
const int kNumTrigger = 10;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 200;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.preclude_last_level_data_seconds = 60;
|
||||
options.preserve_internal_time_seconds = 0;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.num_levels = kNumLevels;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
ConfigOptions config_options;
|
||||
config_options.ignore_unsupported_options = false;
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> factory;
|
||||
std::string id = CompactForTieringCollectorFactory::kClassName();
|
||||
ASSERT_OK(TablePropertiesCollectorFactory::CreateFromString(
|
||||
config_options, "compaction_trigger_ratio=0.4; id=" + id, &factory));
|
||||
auto collector_factory =
|
||||
factory->CheckedCast<CompactForTieringCollectorFactory>();
|
||||
options.table_properties_collector_factories.push_back(factory);
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions wo;
|
||||
wo.protection_bytes_per_key = ProtectionBytesPerKey();
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(rnd.Uniform(10) + 1));
|
||||
});
|
||||
|
||||
for (int i = 0; i < kNumKeys / 4; i++) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100), wo));
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(rnd.Uniform(2)));
|
||||
});
|
||||
}
|
||||
// Create one file with regular Put.
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Create one file with TimedPut.
|
||||
// These data are eligible to be put on the last level once written to db
|
||||
// and compaction will fast track them to the last level.
|
||||
for (int i = kNumKeys / 4; i < kNumKeys / 2; i++) {
|
||||
ASSERT_OK(TimedPut(0, Key(i), rnd.RandomString(100), 50, wo));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// TimedPut file moved to the last level via auto triggered compaction.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("1,0,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
collector_factory->SetCompactionTriggerRatio(1.1);
|
||||
for (int i = kNumKeys / 2; i < kNumKeys * 3 / 4; i++) {
|
||||
ASSERT_OK(TimedPut(0, Key(i), rnd.RandomString(100), 50, wo));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("2,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
collector_factory->SetCompactionTriggerRatio(0);
|
||||
for (int i = kNumKeys * 3 / 4; i < kNumKeys; i++) {
|
||||
ASSERT_OK(TimedPut(0, Key(i), rnd.RandomString(100), 50, wo));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("3,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(TimedPutPrecludeLastLevelTest,
|
||||
TimedPutPrecludeLastLevelTest, ::testing::Values(0, 8));
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
TEST_F(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -2015,8 +1768,9 @@ TEST_P(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
// enable preclude feature
|
||||
ApplyConfigChange(&options, {{"preclude_last_level_data_seconds", "2000"},
|
||||
{"last_level_temperature", "kCold"}});
|
||||
options.preclude_last_level_data_seconds = 2000;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
Reopen(options);
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
@@ -2047,30 +1801,21 @@ TEST_P(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
Close();
|
||||
}
|
||||
|
||||
class PrecludeLastLevelOptionalTest
|
||||
: public PrecludeLastLevelTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
class PrecludeLastLevelTestWithParms
|
||||
: public PrecludeLastLevelTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
bool UseDynamicConfig() const { return std::get<0>(GetParam()); }
|
||||
|
||||
void ApplyConfigChange(
|
||||
Options* options,
|
||||
const std::unordered_map<std::string, std::string>& config_change,
|
||||
const std::unordered_map<std::string, std::string>& db_config_change =
|
||||
{}) {
|
||||
ApplyConfigChangeImpl(UseDynamicConfig(), options, config_change,
|
||||
db_config_change);
|
||||
}
|
||||
|
||||
bool EnablePrecludeLastLevel() const { return std::get<1>(GetParam()); }
|
||||
PrecludeLastLevelTestWithParms() : PrecludeLastLevelTest() {}
|
||||
};
|
||||
|
||||
TEST_P(PrecludeLastLevelOptionalTest, LastLevelOnlyCompactionNoPreclude) {
|
||||
TEST_P(PrecludeLastLevelTestWithParms, LastLevelOnlyCompactionNoPreclude) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
const int kKeyPerSec = 10;
|
||||
|
||||
bool enable_preclude_last_level = GetParam();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.preserve_internal_time_seconds = 2000;
|
||||
@@ -2128,7 +1873,7 @@ TEST_P(PrecludeLastLevelOptionalTest, LastLevelOnlyCompactionNoPreclude) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
auto compaction = static_cast<Compaction*>(arg);
|
||||
if (EnablePrecludeLastLevel() && is_manual_compaction_running) {
|
||||
if (enable_preclude_last_level && is_manual_compaction_running) {
|
||||
ASSERT_TRUE(compaction == nullptr);
|
||||
verified_compaction_order = true;
|
||||
} else {
|
||||
@@ -2155,11 +1900,12 @@ TEST_P(PrecludeLastLevelOptionalTest, LastLevelOnlyCompactionNoPreclude) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// only enable if the Parameter is true
|
||||
ApplyConfigChange(&options,
|
||||
{{"preclude_last_level_data_seconds",
|
||||
EnablePrecludeLastLevel() ? "2000" : "0"},
|
||||
{"last_level_temperature", "kCold"}},
|
||||
{{"max_background_jobs", "8"}});
|
||||
if (enable_preclude_last_level) {
|
||||
options.preclude_last_level_data_seconds = 2000;
|
||||
}
|
||||
options.max_background_jobs = 8;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
Reopen(options);
|
||||
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
CompactRangeOptions cro;
|
||||
@@ -2188,7 +1934,7 @@ TEST_P(PrecludeLastLevelOptionalTest, LastLevelOnlyCompactionNoPreclude) {
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
if (EnablePrecludeLastLevel()) {
|
||||
if (enable_preclude_last_level) {
|
||||
ASSERT_NE("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
} else {
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
@@ -2202,7 +1948,7 @@ TEST_P(PrecludeLastLevelOptionalTest, LastLevelOnlyCompactionNoPreclude) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(PrecludeLastLevelOptionalTest, PeriodicCompactionToPenultimateLevel) {
|
||||
TEST_P(PrecludeLastLevelTestWithParms, PeriodicCompactionToPenultimateLevel) {
|
||||
// Test the last level only periodic compaction should also be blocked by an
|
||||
// ongoing compaction in penultimate level if tiered compaction is enabled
|
||||
// otherwise, the periodic compaction should just run for the last level.
|
||||
@@ -2212,6 +1958,8 @@ TEST_P(PrecludeLastLevelOptionalTest, PeriodicCompactionToPenultimateLevel) {
|
||||
const int kKeyPerSec = 1;
|
||||
const int kNumKeys = 100;
|
||||
|
||||
bool enable_preclude_last_level = GetParam();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.preserve_internal_time_seconds = 20000;
|
||||
@@ -2238,11 +1986,12 @@ TEST_P(PrecludeLastLevelOptionalTest, PeriodicCompactionToPenultimateLevel) {
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
// enable preclude feature
|
||||
ApplyConfigChange(&options,
|
||||
{{"preclude_last_level_data_seconds",
|
||||
EnablePrecludeLastLevel() ? "2000" : "0"},
|
||||
{"last_level_temperature", "kCold"}},
|
||||
{{"max_background_jobs", "8"}});
|
||||
if (enable_preclude_last_level) {
|
||||
options.preclude_last_level_data_seconds = 20000;
|
||||
}
|
||||
options.max_background_jobs = 8;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
Reopen(options);
|
||||
|
||||
std::atomic_bool is_size_ratio_compaction_running = false;
|
||||
std::atomic_bool verified_last_level_compaction = false;
|
||||
@@ -2267,7 +2016,7 @@ TEST_P(PrecludeLastLevelOptionalTest, PeriodicCompactionToPenultimateLevel) {
|
||||
auto compaction = static_cast<Compaction*>(arg);
|
||||
|
||||
if (is_size_ratio_compaction_running) {
|
||||
if (EnablePrecludeLastLevel()) {
|
||||
if (enable_preclude_last_level) {
|
||||
ASSERT_TRUE(compaction == nullptr);
|
||||
} else {
|
||||
ASSERT_TRUE(compaction != nullptr);
|
||||
@@ -2325,10 +2074,8 @@ TEST_P(PrecludeLastLevelOptionalTest, PeriodicCompactionToPenultimateLevel) {
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(PrecludeLastLevelOptionalTest,
|
||||
PrecludeLastLevelOptionalTest,
|
||||
::testing::Combine(::testing::Bool(),
|
||||
::testing::Bool()));
|
||||
INSTANTIATE_TEST_CASE_P(PrecludeLastLevelTestWithParms,
|
||||
PrecludeLastLevelTestWithParms, testing::Bool());
|
||||
|
||||
// partition the SST into 3 ranges [0, 19] [20, 39] [40, ...]
|
||||
class ThreeRangesPartitioner : public SstPartitioner {
|
||||
@@ -2372,7 +2119,7 @@ class ThreeRangesPartitionerFactory : public SstPartitionerFactory {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, PartialPenultimateLevelCompaction) {
|
||||
TEST_F(PrecludeLastLevelTest, PartialPenultimateLevelCompaction) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kKeyPerSec = 10;
|
||||
@@ -2383,7 +2130,6 @@ TEST_P(PrecludeLastLevelTest, PartialPenultimateLevelCompaction) {
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.preserve_internal_time_seconds = 10000;
|
||||
options.num_levels = kNumLevels;
|
||||
options.statistics = CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -2421,10 +2167,11 @@ TEST_P(PrecludeLastLevelTest, PartialPenultimateLevelCompaction) {
|
||||
// L6: [0, 299]
|
||||
ASSERT_EQ("0,0,0,0,0,3,1", FilesPerLevel());
|
||||
|
||||
ASSERT_OK(options.statistics->Reset());
|
||||
// enable tiered storage feature
|
||||
ApplyConfigChange(&options, {{"preclude_last_level_data_seconds", "10000"},
|
||||
{"last_level_temperature", "kCold"}});
|
||||
options.preclude_last_level_data_seconds = 10000;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
options.statistics = CreateDBStatistics();
|
||||
Reopen(options);
|
||||
|
||||
ColumnFamilyMetaData meta;
|
||||
db_->GetColumnFamilyMetaData(&meta);
|
||||
@@ -2470,7 +2217,7 @@ TEST_P(PrecludeLastLevelTest, PartialPenultimateLevelCompaction) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
const int kNumLevels = 7;
|
||||
const int kSecondsPerKey = 10;
|
||||
const int kNumFiles = 3;
|
||||
@@ -2609,25 +2356,12 @@ TEST_P(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
|
||||
// Tests DBIter::GetProperty("rocksdb.iterator.write-time") return a data's
|
||||
// approximate write unix time.
|
||||
class IteratorWriteTimeTest
|
||||
: public PrecludeLastLevelTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
// Test Param:
|
||||
// 1) use tailing iterator or regular iterator (when it applies)
|
||||
class IteratorWriteTimeTest : public PrecludeLastLevelTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
IteratorWriteTimeTest()
|
||||
: PrecludeLastLevelTestBase("iterator_write_time_test") {}
|
||||
|
||||
bool UseTailingIterator() const { return std::get<0>(GetParam()); }
|
||||
|
||||
bool UseDynamicConfig() const { return std::get<1>(GetParam()); }
|
||||
|
||||
void ApplyConfigChange(
|
||||
Options* options,
|
||||
const std::unordered_map<std::string, std::string>& config_change,
|
||||
const std::unordered_map<std::string, std::string>& db_config_change =
|
||||
{}) {
|
||||
ApplyConfigChangeImpl(UseDynamicConfig(), options, config_change,
|
||||
db_config_change);
|
||||
}
|
||||
IteratorWriteTimeTest() : PrecludeLastLevelTest("iterator_write_time_test") {}
|
||||
|
||||
uint64_t VerifyKeyAndGetWriteTime(Iterator* iter,
|
||||
const std::string& expected_key) {
|
||||
@@ -2667,14 +2401,10 @@ TEST_P(IteratorWriteTimeTest, ReadFromMemtables) {
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.preserve_internal_time_seconds = 10000;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// While there are issues with tracking seqno 0
|
||||
ASSERT_OK(Delete("something_to_bump_seqno"));
|
||||
|
||||
ApplyConfigChange(&options, {{"preserve_internal_time_seconds", "10000"}});
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
@@ -2688,7 +2418,7 @@ TEST_P(IteratorWriteTimeTest, ReadFromMemtables) {
|
||||
}
|
||||
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = UseTailingIterator();
|
||||
ropts.tailing = GetParam();
|
||||
int i;
|
||||
|
||||
// Forward iteration
|
||||
@@ -2705,7 +2435,6 @@ TEST_P(IteratorWriteTimeTest, ReadFromMemtables) {
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(kNumKeys, i);
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
@@ -2725,12 +2454,12 @@ TEST_P(IteratorWriteTimeTest, ReadFromMemtables) {
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(-1, i);
|
||||
}
|
||||
|
||||
// Disable the seqno to time recording. Data with user specified write time
|
||||
// can still get a write time before it's flushed.
|
||||
ApplyConfigChange(&options, {{"preserve_internal_time_seconds", "0"}});
|
||||
// Reopen the DB and disable the seqno to time recording, data with user
|
||||
// specified write time can still get a write time before it's flushed.
|
||||
options.preserve_internal_time_seconds = 0;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(TimedPut(Key(kKeyWithWriteTime), rnd.RandomString(100),
|
||||
kUserSpecifiedWriteTime));
|
||||
{
|
||||
@@ -2766,14 +2495,10 @@ TEST_P(IteratorWriteTimeTest, ReadFromSstFile) {
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.preserve_internal_time_seconds = 10000;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// While there are issues with tracking seqno 0
|
||||
ASSERT_OK(Delete("something_to_bump_seqno"));
|
||||
|
||||
ApplyConfigChange(&options, {{"preserve_internal_time_seconds", "10000"}});
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
@@ -2788,7 +2513,7 @@ TEST_P(IteratorWriteTimeTest, ReadFromSstFile) {
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = UseTailingIterator();
|
||||
ropts.tailing = GetParam();
|
||||
std::string prop;
|
||||
int i;
|
||||
|
||||
@@ -2811,7 +2536,6 @@ TEST_P(IteratorWriteTimeTest, ReadFromSstFile) {
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(kNumKeys, i);
|
||||
}
|
||||
|
||||
// Backward iteration
|
||||
@@ -2831,12 +2555,12 @@ TEST_P(IteratorWriteTimeTest, ReadFromSstFile) {
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(-1, i);
|
||||
}
|
||||
|
||||
// Disable the seqno to time recording. Data retrieved from SST files still
|
||||
// have write time available.
|
||||
ApplyConfigChange(&options, {{"preserve_internal_time_seconds", "0"}});
|
||||
// Reopen the DB and disable the seqno to time recording. Data retrieved from
|
||||
// SST files still have write time available.
|
||||
options.preserve_internal_time_seconds = 0;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
@@ -2862,7 +2586,6 @@ TEST_P(IteratorWriteTimeTest, ReadFromSstFile) {
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(kNumKeys, i);
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
@@ -2886,7 +2609,6 @@ TEST_P(IteratorWriteTimeTest, ReadFromSstFile) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), 0);
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(kNumKeys, i);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
@@ -2900,12 +2622,11 @@ TEST_P(IteratorWriteTimeTest, MergeReturnsBaseValueWriteTime) {
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.preserve_internal_time_seconds = 10000;
|
||||
options.num_levels = kNumLevels;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ApplyConfigChange(&options, {{"preserve_internal_time_seconds", "10000"}});
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
ASSERT_OK(Put("foo", "fv1"));
|
||||
@@ -2916,7 +2637,7 @@ TEST_P(IteratorWriteTimeTest, MergeReturnsBaseValueWriteTime) {
|
||||
ASSERT_OK(Merge("foo", "bv1"));
|
||||
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = UseTailingIterator();
|
||||
ropts.tailing = GetParam();
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
@@ -2934,7 +2655,7 @@ TEST_P(IteratorWriteTimeTest, MergeReturnsBaseValueWriteTime) {
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(IteratorWriteTimeTest, IteratorWriteTimeTest,
|
||||
testing::Combine(testing::Bool(), testing::Bool()));
|
||||
testing::Bool());
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
+1
-4
@@ -71,9 +71,6 @@ Status VerifySstFileChecksumInternal(const Options& options,
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(
|
||||
@@ -87,7 +84,7 @@ Status VerifySstFileChecksumInternal(const Options& options,
|
||||
options.block_protection_bytes_per_key, false /* skip_filters */,
|
||||
!kImmortal, false /* force_direct_prefetch */, -1 /* level */);
|
||||
reader_options.largest_seqno = largest_seqno;
|
||||
s = options.table_factory->NewTableReader(
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
read_options, reader_options, std::move(file_reader), file_size,
|
||||
&table_reader, false /* prefetch_index_and_filter_in_cache */);
|
||||
if (!s.ok()) {
|
||||
|
||||
+67
-246
@@ -688,100 +688,76 @@ TEST_F(DBBasicTest, IdentityAcrossRestarts) {
|
||||
constexpr size_t kMinIdSize = 10;
|
||||
do {
|
||||
for (bool with_manifest : {false, true}) {
|
||||
for (bool write_file : {false, true}) {
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
std::string id1, tmp;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
ASSERT_GE(id1.size(), kMinIdSize);
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
std::string id1, tmp;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
ASSERT_GE(id1.size(), kMinIdSize);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = with_manifest;
|
||||
options.write_identity_file = true; // initially
|
||||
Reopen(options);
|
||||
std::string id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id2));
|
||||
// id2 should match id1 because identity was not regenerated
|
||||
ASSERT_EQ(id1, id2);
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id2);
|
||||
Options options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = with_manifest;
|
||||
Reopen(options);
|
||||
std::string id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id2));
|
||||
// id2 should match id1 because identity was not regenerated
|
||||
ASSERT_EQ(id1, id2);
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id2);
|
||||
|
||||
if (write_file) {
|
||||
// Recover from deleted/missing IDENTITY
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
} else {
|
||||
// Transition to no IDENTITY file
|
||||
options.write_identity_file = false;
|
||||
if (!with_manifest) {
|
||||
// Incompatible options, should fail
|
||||
ASSERT_NOK(TryReopen(options));
|
||||
// Back to a usable config and continue
|
||||
options.write_identity_file = true;
|
||||
Reopen(options);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
if (with_manifest) {
|
||||
// id3 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id3);
|
||||
} else {
|
||||
// id3 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id3);
|
||||
ASSERT_GE(id3.size(), kMinIdSize);
|
||||
}
|
||||
if (write_file) {
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id3);
|
||||
|
||||
// Recover from truncated IDENTITY
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Close());
|
||||
} else {
|
||||
ASSERT_TRUE(env_->FileExists(idfilename).IsNotFound());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id4;
|
||||
ASSERT_OK(db_->GetDbIdentity(id4));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id4);
|
||||
} else {
|
||||
// id4 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id4);
|
||||
ASSERT_GE(id4.size(), kMinIdSize);
|
||||
}
|
||||
std::string silly_id = "asdf123456789";
|
||||
if (write_file) {
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id4);
|
||||
|
||||
// Recover from overwritten IDENTITY
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Append(silly_id));
|
||||
ASSERT_OK(w->Close());
|
||||
} else {
|
||||
ASSERT_TRUE(env_->FileExists(idfilename).IsNotFound());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id5;
|
||||
ASSERT_OK(db_->GetDbIdentity(id5));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id5);
|
||||
} else {
|
||||
ASSERT_EQ(id5, silly_id);
|
||||
}
|
||||
if (write_file) {
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id5);
|
||||
} else {
|
||||
ASSERT_TRUE(env_->FileExists(idfilename).IsNotFound());
|
||||
}
|
||||
// Recover from deleted/missing IDENTITY
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
if (with_manifest) {
|
||||
// id3 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id3);
|
||||
} else {
|
||||
// id3 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id3);
|
||||
ASSERT_GE(id3.size(), kMinIdSize);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id3);
|
||||
|
||||
// Recover from truncated IDENTITY
|
||||
{
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Close());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id4;
|
||||
ASSERT_OK(db_->GetDbIdentity(id4));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id4);
|
||||
} else {
|
||||
// id4 should NOT match id1 because identity was regenerated
|
||||
ASSERT_NE(id1, id4);
|
||||
ASSERT_GE(id4.size(), kMinIdSize);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id4);
|
||||
|
||||
// Recover from overwritten IDENTITY
|
||||
std::string silly_id = "asdf123456789";
|
||||
{
|
||||
std::unique_ptr<WritableFile> w;
|
||||
ASSERT_OK(env_->NewWritableFile(idfilename, &w, EnvOptions()));
|
||||
ASSERT_OK(w->Append(silly_id));
|
||||
ASSERT_OK(w->Close());
|
||||
}
|
||||
Reopen(options);
|
||||
std::string id5;
|
||||
ASSERT_OK(db_->GetDbIdentity(id5));
|
||||
if (with_manifest) {
|
||||
// id4 should match id1 because identity was restored from manifest
|
||||
ASSERT_EQ(id1, id5);
|
||||
} else {
|
||||
ASSERT_EQ(id5, silly_id);
|
||||
}
|
||||
ASSERT_OK(ReadFileToString(env_, idfilename, &tmp));
|
||||
ASSERT_EQ(tmp, id5);
|
||||
}
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
@@ -3431,46 +3407,6 @@ class TableFileListener : public EventListener {
|
||||
InstrumentedMutex mutex_;
|
||||
std::unordered_map<std::string, std::vector<std::string>> cf_to_paths_;
|
||||
};
|
||||
|
||||
class FlushTableFileListener : public EventListener {
|
||||
public:
|
||||
void OnTableFileCreated(const TableFileCreationInfo& info) override {
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
if (info.reason != TableFileCreationReason::kFlush) {
|
||||
return;
|
||||
}
|
||||
cf_to_flushed_files_[info.cf_name].push_back(info.file_path);
|
||||
}
|
||||
std::vector<std::string>& GetFlushedFiles(const std::string& cf_name) {
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
return cf_to_flushed_files_[cf_name];
|
||||
}
|
||||
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::unordered_map<std::string, std::vector<std::string>>
|
||||
cf_to_flushed_files_;
|
||||
};
|
||||
|
||||
class FlushBlobFileListener : public EventListener {
|
||||
public:
|
||||
void OnBlobFileCreated(const BlobFileCreationInfo& info) override {
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
if (info.reason != BlobFileCreationReason::kFlush) {
|
||||
return;
|
||||
}
|
||||
cf_to_flushed_blobs_files_[info.cf_name].push_back(info.file_path);
|
||||
}
|
||||
std::vector<std::string>& GetFlushedBlobFiles(const std::string& cf_name) {
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
return cf_to_flushed_blobs_files_[cf_name];
|
||||
}
|
||||
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::unordered_map<std::string, std::vector<std::string>>
|
||||
cf_to_flushed_blobs_files_;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBasicTest, LastSstFileNotInManifest) {
|
||||
@@ -3576,121 +3512,6 @@ TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
// Param 0: whether to enable blob DB.
|
||||
// Param 1: when blob DB is enabled, whether to also delete the missing L0
|
||||
// file's associated blob file.
|
||||
class BestEffortsRecoverIncompleteVersionTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
BestEffortsRecoverIncompleteVersionTest()
|
||||
: DBTestBase("best_efforts_recover_incomplete_version_test",
|
||||
/*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
TEST_P(BestEffortsRecoverIncompleteVersionTest, Basic) {
|
||||
Options options = CurrentOptions();
|
||||
options.enable_blob_files = std::get<0>(GetParam());
|
||||
bool delete_blob_file_too = std::get<1>(GetParam());
|
||||
DestroyAndReopen(options);
|
||||
FlushTableFileListener* flush_table_listener = new FlushTableFileListener();
|
||||
FlushBlobFileListener* flush_blob_listener = new FlushBlobFileListener();
|
||||
// Disable auto compaction to simplify SST file name tracking.
|
||||
options.disable_auto_compactions = true;
|
||||
options.listeners.emplace_back(flush_table_listener);
|
||||
options.listeners.emplace_back(flush_blob_listener);
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
std::vector<std::string> all_cf_names = {kDefaultColumnFamilyName, "pikachu",
|
||||
"eevee"};
|
||||
int num_cfs = static_cast<int>(handles_.size());
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
std::string start = "a";
|
||||
Slice start_slice = start;
|
||||
std::string end = "d";
|
||||
Slice end_slice = end;
|
||||
for (int cf = 0; cf != num_cfs; ++cf) {
|
||||
ASSERT_OK(Put(cf, "a", "a_value"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
// Compact file to L1 to avoid trivial file move in the next compaction
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[cf],
|
||||
&start_slice, &end_slice));
|
||||
ASSERT_OK(Put(cf, "a", "a_value_new"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(Put(cf, "b", "b_value"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(Put(cf, "f", "f_value"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[cf],
|
||||
&start_slice, &end_slice));
|
||||
}
|
||||
|
||||
dbfull()->TEST_DeleteObsoleteFiles();
|
||||
|
||||
// Delete the most recent L0 file which is before a compaction.
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
std::vector<std::string>& files =
|
||||
flush_table_listener->GetFlushedFiles(all_cf_names[i]);
|
||||
ASSERT_EQ(4, files.size());
|
||||
ASSERT_OK(env_->DeleteFile(files[files.size() - 1]));
|
||||
if (options.enable_blob_files) {
|
||||
std::vector<std::string>& blob_files =
|
||||
flush_blob_listener->GetFlushedBlobFiles(all_cf_names[i]);
|
||||
ASSERT_EQ(4, blob_files.size());
|
||||
if (delete_blob_file_too) {
|
||||
ASSERT_OK(env_->DeleteFile(blob_files[files.size() - 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
options.best_efforts_recovery = true;
|
||||
ReopenWithColumnFamilies(all_cf_names, options);
|
||||
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
// The L0 file flushed right before the last compaction is missing.
|
||||
ASSERT_EQ(0, vstorage->LevelFiles(0).size());
|
||||
// Only the output of the last compaction is available.
|
||||
ASSERT_EQ(1, vstorage->LevelFiles(1).size());
|
||||
}
|
||||
// Verify data
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts, handles_[i]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("a", iter->key());
|
||||
ASSERT_EQ("a_value_new", iter->value());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("b", iter->key());
|
||||
ASSERT_EQ("b_value", iter->value());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Write more data.
|
||||
for (int cf = 0; cf < num_cfs; ++cf) {
|
||||
ASSERT_OK(Put(cf, "g", "g_value"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[cf], nullptr,
|
||||
nullptr));
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), handles_[cf], "g", &value));
|
||||
ASSERT_EQ("g_value", value);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(BestEffortsRecoverIncompleteVersionTest,
|
||||
BestEffortsRecoverIncompleteVersionTest,
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(true, false),
|
||||
std::make_tuple(true, true)));
|
||||
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
|
||||
+139
-331
@@ -26,7 +26,6 @@
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/hash.h"
|
||||
@@ -563,7 +562,7 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, DynamicOptions) {
|
||||
TEST_F(DBBlockCacheTest, DynamicallyWarmCacheDuringFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
@@ -578,74 +577,39 @@ TEST_F(DBBlockCacheTest, DynamicOptions) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
auto st = options.statistics;
|
||||
|
||||
size_t i = 1;
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
for (size_t i = 1; i <= 5; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(
|
||||
0, options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
|
||||
++i;
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"block_based_table_factory", "{prepopulate_block_cache=kDisable;}"}}));
|
||||
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
for (size_t i = 6; i <= kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
++i;
|
||||
ASSERT_OK(dbfull()->SetOptions({{"block_based_table_factory",
|
||||
"{prepopulate_block_cache=kFlushOnly;}"}}));
|
||||
|
||||
ASSERT_OK(Put(std::to_string(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
++i;
|
||||
// NOT YET SUPPORTED
|
||||
// FIXME: find a way to make this fail again (until well supported)
|
||||
// ASSERT_NOK(dbfull()->SetOptions(
|
||||
// {{"block_based_table_factory", "{block_cache=null;}"}}));
|
||||
|
||||
// ASSERT_OK(Put(std::to_string(i), value));
|
||||
// ASSERT_OK(Flush());
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
// ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
// ++i;
|
||||
|
||||
// NOT YET SUPPORTED
|
||||
// FIXME: find a way to make this fail again (until well supported)
|
||||
// ASSERT_NOK(dbfull()->SetOptions(
|
||||
// {{"block_based_table_factory", "{block_cache=1M;}"}}));
|
||||
|
||||
// ASSERT_OK(Put(std::to_string(i), value));
|
||||
// ASSERT_OK(Flush());
|
||||
// ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
// ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
// ASSERT_EQ(0, st->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
// ASSERT_EQ(1, st->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
ASSERT_EQ(value, Get(std::to_string(i)));
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(
|
||||
1, options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -776,6 +740,118 @@ class LookupLiarCache : public CacheWrapper {
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, AddRedundantStats) {
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
int iterations_tested = 0;
|
||||
for (const std::shared_ptr<Cache>& base_cache :
|
||||
{NewLRUCache(capacity, num_shard_bits),
|
||||
// FixedHyperClockCache
|
||||
HyperClockCacheOptions(
|
||||
capacity,
|
||||
BlockBasedTableOptions().block_size /*estimated_value_size*/,
|
||||
num_shard_bits)
|
||||
.MakeSharedCache(),
|
||||
// AutoHyperClockCache
|
||||
HyperClockCacheOptions(capacity, 0 /*estimated_value_size*/,
|
||||
num_shard_bits)
|
||||
.MakeSharedCache()}) {
|
||||
if (!base_cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
}
|
||||
++iterations_tested;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<LookupLiarCache> cache =
|
||||
std::make_shared<LookupLiarCache>(base_cache);
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// Normal access filter+index+data.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Againt access filter+index+data, but force redundant load+insert on index
|
||||
cache->SetNthLookupNotFound(2);
|
||||
ASSERT_EQ("value", Get("bar"));
|
||||
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just filter (with high probability), and force redundant
|
||||
// load+insert
|
||||
cache->SetNthLookupNotFound(1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("this key was not added"));
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(5, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just data, forcing redundant load+insert
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(read_options)};
|
||||
cache->SetNthLookupNotFound(1);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), "bar");
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(6, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
}
|
||||
EXPECT_GE(iterations_tested, 1);
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -1271,274 +1347,6 @@ TEST_F(DBBlockCacheTest, HyperClockCacheReportProblems) {
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 1, 0}}));
|
||||
}
|
||||
|
||||
class DBBlockCacheTypeTest
|
||||
: public DBBlockCacheTest,
|
||||
public secondary_cache_test_util::WithCacheTypeParam {};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBlockCacheTypeTestInstance, DBBlockCacheTypeTest,
|
||||
secondary_cache_test_util::GetTestingCacheTypes());
|
||||
|
||||
TEST_P(DBBlockCacheTypeTest, AddRedundantStats) {
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
estimated_value_size_ = table_options.block_size;
|
||||
std::shared_ptr<Cache> base_cache =
|
||||
NewCache(capacity, num_shard_bits, /*strict_capacity_limit=*/false);
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<LookupLiarCache> cache =
|
||||
std::make_shared<LookupLiarCache>(base_cache);
|
||||
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// Normal access filter+index+data.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Againt access filter+index+data, but force redundant load+insert on index
|
||||
cache->SetNthLookupNotFound(2);
|
||||
ASSERT_EQ("value", Get("bar"));
|
||||
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just filter (with high probability), and force redundant
|
||||
// load+insert
|
||||
cache->SetNthLookupNotFound(1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("this key was not added"));
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(5, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just data, forcing redundant load+insert
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(read_options)};
|
||||
cache->SetNthLookupNotFound(1);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), "bar");
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(6, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string AltKey(int i) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "altkey%06d", i);
|
||||
return std::string(buf);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_P(DBBlockCacheTypeTest, Uncache) {
|
||||
for (bool partitioned : {false, true}) {
|
||||
SCOPED_TRACE("partitioned=" + std::to_string(partitioned));
|
||||
for (uint32_t ua : {0, 1, 2, 10000}) {
|
||||
SCOPED_TRACE("ua=" + std::to_string(ua));
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
options.uncache_aggressiveness = ua;
|
||||
options.create_if_missing = true;
|
||||
// Don't allow background operations to keep Versions referenced
|
||||
options.stats_dump_period_sec = 0;
|
||||
options.stats_persist_period_sec = 0;
|
||||
auto stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.statistics = stats;
|
||||
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
estimated_value_size_ = table_options.block_size;
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(capacity, num_shard_bits, /*strict_capacity_limit=*/false);
|
||||
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.partition_filters = partitioned;
|
||||
table_options.index_type =
|
||||
partitioned ? BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch
|
||||
: BlockBasedTableOptions::IndexType::kBinarySearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
size_t kBaselineCount = 1; // Because of entry stats collector
|
||||
|
||||
ASSERT_EQ(kBaselineCount, cache->GetOccupancyCount());
|
||||
ASSERT_EQ(0U, cache->GetUsage());
|
||||
|
||||
constexpr uint8_t kNumDataBlocks = 10;
|
||||
constexpr uint8_t kNumFiles = 3;
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
// Force some overlap with ordering
|
||||
ASSERT_OK(Put(Key((i * 7) % kNumDataBlocks),
|
||||
Random::GetTLSInstance()->RandomBinaryString(
|
||||
static_cast<int>(table_options.block_size))));
|
||||
if (i >= kNumDataBlocks - kNumFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(int{kNumFiles}, NumTableFilesAtLevel(0));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
size_t meta_blocks_per_file = /*index & filter*/ 2U * (1U + partitioned);
|
||||
ASSERT_EQ(
|
||||
cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks + meta_blocks_per_file * kNumFiles);
|
||||
ASSERT_GE(cache->GetUsage(), kNumDataBlocks * table_options.block_size);
|
||||
|
||||
// Combine into one file, making the originals obsolete
|
||||
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
if (ua == 0) {
|
||||
// Expect to see cache entries for new file and obsolete files
|
||||
EXPECT_EQ(cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks * 2U +
|
||||
meta_blocks_per_file * (kNumFiles + 1));
|
||||
EXPECT_GE(cache->GetUsage(),
|
||||
kNumDataBlocks * table_options.block_size * 2U);
|
||||
} else {
|
||||
// Expect only to see cache entries for new file
|
||||
EXPECT_EQ(cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks + meta_blocks_per_file);
|
||||
EXPECT_GE(cache->GetUsage(), kNumDataBlocks * table_options.block_size);
|
||||
EXPECT_LT(cache->GetUsage(),
|
||||
kNumDataBlocks * table_options.block_size * 2U);
|
||||
}
|
||||
|
||||
size_t alt_baseline_count = cache->GetOccupancyCount();
|
||||
size_t alt_baseline_usage = cache->GetUsage();
|
||||
ASSERT_OK(stats->Reset());
|
||||
// We aren't generally cleaning up cache entries on DB::Close, especially
|
||||
// because someone might just re-open the same DB.
|
||||
Reopen(options);
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
EXPECT_EQ(cache->GetOccupancyCount(), alt_baseline_count);
|
||||
EXPECT_EQ(cache->GetUsage(), alt_baseline_usage);
|
||||
|
||||
// Check for unnecessary unncessary cache churn
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_ADD), 0U);
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_MISS), 0U);
|
||||
ASSERT_GT(stats->getTickerCount(BLOCK_CACHE_HIT), 0U);
|
||||
|
||||
// And now do a similar test as above except with trivial moves, making
|
||||
// sure that we aren't falsely uncaching in that case, which would cause
|
||||
// unnecessary cache misses. Using AltKey instead of Key to avoid
|
||||
// interference.
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
// No overlap
|
||||
ASSERT_OK(
|
||||
Put(AltKey(i), Random::GetTLSInstance()->RandomBinaryString(
|
||||
static_cast<int>(table_options.block_size))));
|
||||
if (i >= kNumDataBlocks - kNumFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(int{kNumFiles}, NumTableFilesAtLevel(0));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(AltKey(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
ASSERT_EQ(cache->GetOccupancyCount(),
|
||||
alt_baseline_count + kNumDataBlocks +
|
||||
meta_blocks_per_file * kNumFiles);
|
||||
ASSERT_GE(cache->GetUsage(),
|
||||
alt_baseline_usage + kNumDataBlocks * table_options.block_size);
|
||||
|
||||
ASSERT_OK(stats->Reset());
|
||||
|
||||
// Make trivial move
|
||||
{
|
||||
auto a = AltKey(0);
|
||||
auto b = AltKey(kNumDataBlocks);
|
||||
Slice slice_a{a};
|
||||
Slice slice_b{b};
|
||||
ASSERT_OK(db_->CompactRange({}, &slice_a, &slice_b));
|
||||
}
|
||||
ASSERT_EQ(/*old*/ 1 + /*new*/ int{kNumFiles}, NumTableFilesAtLevel(1));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(AltKey(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
// Should be the same if trivial move
|
||||
ASSERT_EQ(cache->GetOccupancyCount(),
|
||||
alt_baseline_count + kNumDataBlocks +
|
||||
meta_blocks_per_file * kNumFiles);
|
||||
|
||||
// Check for unnecessary unncessary cache churn
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_ADD), 0U);
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_MISS), 0U);
|
||||
ASSERT_GT(stats->getTickerCount(BLOCK_CACHE_HIT), 0U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DBBlockCacheKeyTest
|
||||
: public DBTestBase,
|
||||
|
||||
+561
-1059
File diff suppressed because it is too large.
Load diff
@@ -6146,7 +6146,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
|
||||
std::vector<std::string> pending_compaction_cfs;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"EnqueuePendingCompaction::cfd", [&](void* arg) {
|
||||
"SchedulePendingCompaction::cfd", [&](void* arg) {
|
||||
const std::string& cf_name =
|
||||
static_cast<ColumnFamilyData*>(arg)->GetName();
|
||||
pending_compaction_cfs.emplace_back(cf_name);
|
||||
@@ -9357,13 +9357,12 @@ TEST_F(DBCompactionTest, FIFOChangeTemperature) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// First two L0 files both become eligible for temperature change compaction
|
||||
// They should be compacted one-by-one.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(1200);
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -10623,97 +10622,6 @@ TEST_F(DBCompactionTest, ReleaseCompactionDuringManifestWrite) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
|
||||
Options options;
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
options.env = CurrentOptions().env;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.arena_block_size = 4096;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.num_levels = 1;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
// Generate and flush 4 files, each about 10KB
|
||||
// Compaction is manually disabled at this point so we can check
|
||||
// each file's newest_key_time
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
env_->MockSleepForSeconds(5);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 4);
|
||||
|
||||
// Check that we are populating newest_key_time on flush
|
||||
std::vector<FileMetaData*> file_metadatas = GetLevelFileMetadatas(0);
|
||||
ASSERT_EQ(file_metadatas.size(), 4);
|
||||
uint64_t first_newest_key_time =
|
||||
file_metadatas[0]->fd.table_reader->GetTableProperties()->newest_key_time;
|
||||
ASSERT_NE(first_newest_key_time, kUnknownNewestKeyTime);
|
||||
// Check that the newest_key_times are in expected ordering
|
||||
uint64_t prev_newest_key_time = first_newest_key_time;
|
||||
for (size_t idx = 1; idx < file_metadatas.size(); idx++) {
|
||||
uint64_t newest_key_time = file_metadatas[idx]
|
||||
->fd.table_reader->GetTableProperties()
|
||||
->newest_key_time;
|
||||
|
||||
ASSERT_LT(newest_key_time, prev_newest_key_time);
|
||||
prev_newest_key_time = newest_key_time;
|
||||
ASSERT_EQ(newest_key_time, file_metadatas[idx]
|
||||
->fd.table_reader->GetTableProperties()
|
||||
->creation_time);
|
||||
}
|
||||
// The delta between the first and last newest_key_times is 15s
|
||||
uint64_t last_newest_key_time = prev_newest_key_time;
|
||||
ASSERT_EQ(15, first_newest_key_time - last_newest_key_time);
|
||||
|
||||
// After compaction, the newest_key_time of the output file should be the max
|
||||
// of the input files
|
||||
options.compaction_options_fifo.allow_compaction = true;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
||||
file_metadatas = GetLevelFileMetadatas(0);
|
||||
ASSERT_EQ(file_metadatas.size(), 1);
|
||||
ASSERT_EQ(
|
||||
file_metadatas[0]->fd.table_reader->GetTableProperties()->newest_key_time,
|
||||
first_newest_key_time);
|
||||
// Contrast newest_key_time with creation_time, which records the oldest
|
||||
// ancestor time (15s older than newest_key_time)
|
||||
ASSERT_EQ(
|
||||
file_metadatas[0]->fd.table_reader->GetTableProperties()->creation_time,
|
||||
last_newest_key_time);
|
||||
ASSERT_EQ(file_metadatas[0]->oldest_ancester_time, last_newest_key_time);
|
||||
|
||||
// Make sure TTL of 5s causes compaction
|
||||
env_->MockSleepForSeconds(6);
|
||||
|
||||
// The oldest input file is older than 15s
|
||||
// However the newest of the compaction input files is younger than 15s, so
|
||||
// we don't compact
|
||||
ASSERT_OK(dbfull()->SetOptions({{"ttl", "15"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 15);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
||||
|
||||
// Now even the youngest input file is too old
|
||||
ASSERT_OK(dbfull()->SetOptions({{"ttl", "5"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 5);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+24
-93
@@ -21,7 +21,6 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/metadata.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/file_checksum_helper.h"
|
||||
@@ -92,12 +91,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
|
||||
return GetSortedWalFilesImpl(files,
|
||||
/*need_seqnos*/ true);
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// Record tracked WALs as a (minimum) cross-check for directory scan
|
||||
std::vector<uint64_t> required_by_manifest;
|
||||
|
||||
@@ -123,10 +117,7 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: need to include archived WALs because needed WALs might have been
|
||||
// archived since getting required_by_manifest set
|
||||
Status s = wal_manager_.GetSortedWalFiles(files, need_seqnos,
|
||||
/*include_archived*/ true);
|
||||
Status s = wal_manager_.GetSortedWalFiles(files);
|
||||
|
||||
// DisableFileDeletions / EnableFileDeletions not supported in read-only DB
|
||||
if (deletions_disabled.ok()) {
|
||||
@@ -161,11 +152,11 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
size_t wal_count = files.size();
|
||||
size_t wal_size = files.size();
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Number of WAL files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
|
||||
"Number of log files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
|
||||
" required by manifest)",
|
||||
wal_count, required_by_manifest.size());
|
||||
wal_size, required_by_manifest.size());
|
||||
#ifndef NDEBUG
|
||||
std::ostringstream wal_names;
|
||||
for (const auto& wal : files) {
|
||||
@@ -186,7 +177,7 @@ 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<LogFile>* current_log_file) {
|
||||
uint64_t current_logfile_number;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -207,20 +198,14 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
// NOTE: This implementation was largely migrated from Checkpoint.
|
||||
|
||||
Status s;
|
||||
VectorWalPtr live_wal_files;
|
||||
VectorLogPtr live_wal_files;
|
||||
bool flush_memtable = true;
|
||||
if (!immutable_db_options_.allow_2pc) {
|
||||
if (opts.wal_size_for_flush == std::numeric_limits<uint64_t>::max()) {
|
||||
flush_memtable = false;
|
||||
} else if (opts.wal_size_for_flush > 0) {
|
||||
// FIXME: avoid querying the filesystem for current WAL state
|
||||
// If the outstanding WAL files are small, we skip the flush.
|
||||
// Don't take archived log size into account when calculating wal
|
||||
// size for flush, and don't need to verify consistency with manifest
|
||||
// here & now.
|
||||
s = wal_manager_.GetSortedWalFiles(live_wal_files,
|
||||
/* need_seqnos */ false,
|
||||
/*include_archived*/ false);
|
||||
// If the outstanding log files are small, we skip the flush.
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -231,7 +216,6 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
// We may be able to cover 2PC case too.
|
||||
uint64_t total_wal_size = 0;
|
||||
for (auto& wal : live_wal_files) {
|
||||
assert(wal->Type() == kAliveLogFile);
|
||||
total_wal_size += wal->SizeFileBytes();
|
||||
}
|
||||
if (total_wal_size < opts.wal_size_for_flush) {
|
||||
@@ -332,8 +316,6 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
const uint64_t options_number = versions_->options_file_number();
|
||||
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_;
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -397,13 +379,10 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCheckpoint:SavedLiveFiles2");
|
||||
|
||||
if (s.ok()) {
|
||||
// FlushWAL is required to ensure we can physically copy everything
|
||||
// logically written to the WAL. (Sync not strictly required for
|
||||
// active WAL to be copied rather than hard linked, even when
|
||||
// Checkpoint guarantees that the copied-to file is sync-ed. Plus we can't
|
||||
// help track_and_verify_wals_in_manifest after manifest_size is
|
||||
// already determined.)
|
||||
s = FlushWAL(/*sync=*/false);
|
||||
// To maximize the effectiveness of track_and_verify_wals_in_manifest,
|
||||
// sync WAL when it is enabled.
|
||||
s = FlushWAL(
|
||||
immutable_db_options_.track_and_verify_wals_in_manifest /* sync */);
|
||||
if (s.IsNotSupported()) { // read-only DB or similar
|
||||
s = Status::OK();
|
||||
}
|
||||
@@ -412,52 +391,21 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive1");
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive2");
|
||||
|
||||
// Even after WAL flush, there could be multiple WALs that are not
|
||||
// fully synced. Although the output DB of a Checkpoint or Backup needs
|
||||
// to be fully synced on return, we don't strictly need to sync this
|
||||
// DB (the input DB). If we allow Checkpoint to hard link an inactive
|
||||
// WAL that isn't fully synced, that could result in an insufficiently
|
||||
// sync-ed Checkpoint. Here we get the set of WALs that are potentially
|
||||
// unsynced or still being written to, to prevent them from being hard
|
||||
// linked. Enforcing max_log_num from above ensures any new WALs after
|
||||
// GetOpenWalSizes() and before GetSortedWalFiles() are not included in
|
||||
// the results.
|
||||
// NOTE: we might still hard link a file that is open for writing, even
|
||||
// if we don't do any more writes to it.
|
||||
//
|
||||
// In a step toward reducing unnecessary file metadata queries, we also
|
||||
// get and use our known flushed sizes for those WALs.
|
||||
// FIXME: eventually we should not be using filesystem queries at all for
|
||||
// the required set of WAL files.
|
||||
//
|
||||
// However for recycled log files, we just copy the whole file,
|
||||
// for better or worse.
|
||||
//
|
||||
std::map<uint64_t, uint64_t> open_wal_number_to_size;
|
||||
bool recycling_log_files = immutable_db_options_.recycle_log_file_num > 0;
|
||||
if (s.ok() && !recycling_log_files) {
|
||||
s = GetOpenWalSizes(open_wal_number_to_size);
|
||||
}
|
||||
|
||||
// [old comment] If we have more than one column family, we also need to get
|
||||
// WAL files.
|
||||
// If we have more than one column family, we also need to get WAL files.
|
||||
if (s.ok()) {
|
||||
// FIXME: avoid querying the filesystem for current WAL state
|
||||
s = GetSortedWalFilesImpl(live_wal_files,
|
||||
/* need_seqnos */ false);
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
size_t wal_count = live_wal_files.size();
|
||||
size_t wal_size = live_wal_files.size();
|
||||
// Link WAL files. Copy exact size of last one because it is the only one
|
||||
// that has changes after the last flush.
|
||||
auto wal_dir = immutable_db_options_.GetWalDir();
|
||||
for (size_t i = 0; s.ok() && i < wal_count; ++i) {
|
||||
for (size_t i = 0; s.ok() && i < wal_size; ++i) {
|
||||
if ((live_wal_files[i]->Type() == kAliveLogFile) &&
|
||||
(!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num) &&
|
||||
live_wal_files[i]->LogNumber() <= max_log_num) {
|
||||
(!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num)) {
|
||||
results.emplace_back();
|
||||
LiveFileStorageInfo& info = results.back();
|
||||
auto f = live_wal_files[i]->PathName();
|
||||
@@ -466,29 +414,12 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
info.directory = wal_dir;
|
||||
info.file_number = live_wal_files[i]->LogNumber();
|
||||
info.file_type = kWalFile;
|
||||
if (recycling_log_files) {
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
// Recyclable WAL files must be copied instead of hard linked
|
||||
info.trim_to_size = true;
|
||||
} else {
|
||||
auto it = open_wal_number_to_size.find(info.file_number);
|
||||
if (it == open_wal_number_to_size.end()) {
|
||||
// Known fully synced and no future writes (in part from
|
||||
// max_log_num check). Ok to hard link
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
assert(!info.trim_to_size);
|
||||
} else {
|
||||
// Marked as (possibly) still open -> use our known flushed size
|
||||
// and force file copy instead of hard link
|
||||
info.size = it->second;
|
||||
info.trim_to_size = true;
|
||||
// FIXME: this is needed as long as db_stress uses
|
||||
// SetReadUnsyncedData(false), because it will only be able to
|
||||
// copy the synced portion of the WAL, which under
|
||||
// SetReadUnsyncedData(false) is given by the reported file size.
|
||||
info.size = std::min(info.size, live_wal_files[i]->SizeFileBytes());
|
||||
}
|
||||
}
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
// Trim the log either if its the last one, or log file recycling is
|
||||
// enabled. In the latter case, a hard link doesn't prevent the file
|
||||
// from being renamed and recycled. So we need to copy it instead.
|
||||
info.trim_to_size = (i + 1 == wal_size) ||
|
||||
(immutable_db_options_.recycle_log_file_num > 0);
|
||||
if (opts.include_checksum_info) {
|
||||
info.file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
info.file_checksum = kUnknownFileChecksum;
|
||||
|
||||
+5
-5
@@ -86,8 +86,8 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedWals:Start"},
|
||||
{"DBImpl::SyncClosedWals:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{"DBImpl::SyncClosedLogs:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -111,8 +111,8 @@ TEST_F(DBFlushTest, SyncSkip) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncSkip:1", "DBImpl::SyncClosedWals:Skip"},
|
||||
{"DBImpl::SyncClosedWals:Skip", "DBFlushTest::SyncSkip:2"}});
|
||||
{{"DBFlushTest::SyncSkip:1", "DBImpl::SyncClosedLogs:Skip"},
|
||||
{"DBImpl::SyncClosedLogs:Skip", "DBFlushTest::SyncSkip:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
@@ -2381,7 +2381,7 @@ TEST_F(DBFlushTest, PickRightMemtables) {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::SyncClosedWals:BeforeReLock", [&](void* /*arg*/) {
|
||||
"DBImpl::SyncClosedLogs:BeforeReLock", [&](void* /*arg*/) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "what", "v"));
|
||||
auto* cfhi =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(handles_[1]);
|
||||
|
||||
@@ -289,12 +289,10 @@ TEST_F(DBFollowerTest, RetryCatchup) {
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
|
||||
{"DBImpl::BackgroundCompaction:Start",
|
||||
"DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
{"VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit:"
|
||||
"Begin1",
|
||||
{"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin1",
|
||||
"DBImpl::BackgroundCompaction:BeforeCompaction"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit:"
|
||||
"Begin2"},
|
||||
"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin2"},
|
||||
{"DBImplFollower::TryCatchupWithLeader:End", "Follower::WaitForCatchup"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -337,12 +335,10 @@ TEST_F(DBFollowerTest, RetryCatchupManifestRollover) {
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
|
||||
{"Leader::Flushed", "DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
{"VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit:"
|
||||
"Begin1",
|
||||
{"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin1",
|
||||
"Leader::Done"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"VersionEditHandlerPointInTime::MaybeCreateVersionBeforeApplyEdit:"
|
||||
"Begin2"},
|
||||
"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin2"},
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
});
|
||||
|
||||
+144
-399
@@ -392,8 +392,8 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
if (!s.ok()) {
|
||||
io_s = versions_->io_status();
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
s = error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -473,7 +473,7 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
EnqueuePendingCompaction(cfd);
|
||||
SchedulePendingCompaction(cfd);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -530,11 +530,6 @@ Status DBImpl::MaybeReleaseTimestampedSnapshotsAndCheck() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::UntrackDataFiles() {
|
||||
TrackOrUntrackFiles(/*existing_data_files=*/{},
|
||||
/*track=*/false);
|
||||
}
|
||||
|
||||
Status DBImpl::CloseHelper() {
|
||||
// Guarantee that there is no background error recovery in progress before
|
||||
// continuing with the shutdown
|
||||
@@ -643,7 +638,7 @@ Status DBImpl::CloseHelper() {
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Unable to clear writer for WAL %s with error -- %s",
|
||||
"Unable to Sync WAL file %s with error -- %s",
|
||||
LogFileName(immutable_db_options_.GetWalDir(), log_number).c_str(),
|
||||
s.ToString().c_str());
|
||||
// Retain the first error
|
||||
@@ -659,9 +654,8 @@ Status DBImpl::CloseHelper() {
|
||||
// We need to release them before the block cache is destroyed. The block
|
||||
// cache may be destroyed inside versions_.reset(), when column family data
|
||||
// list is destroyed, so leaving handles in table cache after
|
||||
// versions_.reset() may cause issues. Here we clean all unreferenced handles
|
||||
// in table cache, and (for certain builds/conditions) assert that no obsolete
|
||||
// files are hanging around unreferenced (leak) in the table/blob file cache.
|
||||
// versions_.reset() may cause issues.
|
||||
// Here we clean all unreferenced handles in table cache.
|
||||
// Now we assume all user queries have finished, so only version set itself
|
||||
// can possibly hold the blocks from block cache. After releasing unreferenced
|
||||
// handles here, only handles held by version set left and inside
|
||||
@@ -669,22 +663,12 @@ Status DBImpl::CloseHelper() {
|
||||
// time a handle is released, we erase it from the cache too. By doing that,
|
||||
// we can guarantee that after versions_.reset(), table cache is empty
|
||||
// so the cache can be safely destroyed.
|
||||
#ifndef NDEBUG
|
||||
TEST_VerifyNoObsoleteFilesCached(/*db_mutex_already_held=*/true);
|
||||
#endif // !NDEBUG
|
||||
table_cache_->EraseUnRefEntries();
|
||||
|
||||
for (auto& txn_entry : recovered_transactions_) {
|
||||
delete txn_entry.second;
|
||||
}
|
||||
|
||||
// Return an unowned SstFileManager to a consistent state
|
||||
if (immutable_db_options_.sst_file_manager && !own_sfm_) {
|
||||
mutex_.Unlock();
|
||||
UntrackDataFiles();
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
// versions need to be destroyed before table_cache since it can hold
|
||||
// references to table_cache.
|
||||
{
|
||||
@@ -847,22 +831,20 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
|
||||
|
||||
uint64_t min_preserve_seconds = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t max_preserve_seconds = std::numeric_limits<uint64_t>::min();
|
||||
std::vector<SuperVersionContext> sv_contexts;
|
||||
bool mapping_was_empty = false;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
auto& mopts = *cfd->GetLatestMutableCFOptions();
|
||||
// preserve time is the max of 2 options.
|
||||
uint64_t preserve_seconds =
|
||||
std::max(mopts.preserve_internal_time_seconds,
|
||||
mopts.preclude_last_level_data_seconds);
|
||||
std::max(cfd->ioptions()->preserve_internal_time_seconds,
|
||||
cfd->ioptions()->preclude_last_level_data_seconds);
|
||||
if (!cfd->IsDropped() && preserve_seconds > 0) {
|
||||
min_preserve_seconds = std::min(preserve_seconds, min_preserve_seconds);
|
||||
max_preserve_seconds = std::max(preserve_seconds, max_preserve_seconds);
|
||||
}
|
||||
}
|
||||
size_t old_mapping_size = seqno_to_time_mapping_.Size();
|
||||
if (min_preserve_seconds == std::numeric_limits<uint64_t>::max()) {
|
||||
// Don't track
|
||||
seqno_to_time_mapping_.SetCapacity(0);
|
||||
@@ -874,17 +856,9 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
|
||||
seqno_to_time_mapping_.SetCapacity(cap);
|
||||
seqno_to_time_mapping_.SetMaxTimeSpan(max_preserve_seconds);
|
||||
}
|
||||
if (old_mapping_size != seqno_to_time_mapping_.Size()) {
|
||||
InstallSeqnoToTimeMappingInSV(&sv_contexts);
|
||||
}
|
||||
mapping_was_empty = seqno_to_time_mapping_.Empty();
|
||||
}
|
||||
|
||||
// clean up outside db mutex
|
||||
for (SuperVersionContext& sv_context : sv_contexts) {
|
||||
sv_context.Clean();
|
||||
}
|
||||
sv_contexts.clear();
|
||||
|
||||
uint64_t seqno_time_cadence = 0;
|
||||
if (min_preserve_seconds != std::numeric_limits<uint64_t>::max()) {
|
||||
// round up to 1 when the time_duration is smaller than
|
||||
@@ -924,6 +898,8 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
|
||||
assert(!is_new_db || last_seqno_zero);
|
||||
if (is_new_db && last_seqno_zero) {
|
||||
// Pre-allocate seqnos and pre-populate historical mapping
|
||||
assert(mapping_was_empty);
|
||||
|
||||
// We can simply modify these, before writes are allowed
|
||||
constexpr uint64_t kMax = kMaxSeqnoTimePairsPerSST;
|
||||
versions_->SetLastAllocatedSequence(kMax);
|
||||
@@ -941,17 +917,16 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
|
||||
read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (!s.ok() && versions_->io_status().IsIOError()) {
|
||||
error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
s = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-populate mappings for reserved sequence numbers.
|
||||
RecordSeqnoToTimeMapping(max_preserve_seconds);
|
||||
} else {
|
||||
} else if (mapping_was_empty) {
|
||||
if (!last_seqno_zero) {
|
||||
// Ensure at least one mapping (or log a warning), and
|
||||
// an updated entry whenever relevant SetOptions is called
|
||||
// Ensure at least one mapping (or log a warning)
|
||||
RecordSeqnoToTimeMapping(/*populate_historical_seconds=*/0);
|
||||
} else {
|
||||
// FIXME (see limitation described above)
|
||||
@@ -1166,13 +1141,6 @@ void DBImpl::DumpStats() {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* table_factory =
|
||||
cfd->GetCurrentMutableCFOptions()->table_factory.get();
|
||||
assert(table_factory != nullptr);
|
||||
// FIXME: need to a shared_ptr if/when block_cache is going to be mutable
|
||||
Cache* cache =
|
||||
table_factory->GetOptions<Cache>(TableFactory::kBlockCacheOpts());
|
||||
|
||||
// Release DB mutex for gathering cache entry stats. Pass over all
|
||||
// column families for this first so that other stats are dumped
|
||||
// near-atomically.
|
||||
@@ -1181,6 +1149,10 @@ void DBImpl::DumpStats() {
|
||||
|
||||
// Probe block cache for problems (if not already via another CF)
|
||||
if (immutable_db_options_.info_log) {
|
||||
auto* table_factory = cfd->ioptions()->table_factory.get();
|
||||
assert(table_factory != nullptr);
|
||||
Cache* cache =
|
||||
table_factory->GetOptions<Cache>(TableFactory::kBlockCacheOpts());
|
||||
if (cache && probed_caches.insert(cache).second) {
|
||||
cache->ReportProblems(immutable_db_options_.info_log);
|
||||
}
|
||||
@@ -1312,12 +1284,6 @@ Status DBImpl::SetOptions(
|
||||
}
|
||||
sv_context.Clean();
|
||||
|
||||
if (s.ok() && (options_map.count("preserve_internal_time_seconds") > 0 ||
|
||||
options_map.count("preclude_last_level_data_seconds") > 0)) {
|
||||
s = RegisterRecordSeqnoTimeWorker(read_options, write_options,
|
||||
false /* is_new_db*/);
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"SetOptions() on column family [%s], inputs:", cfd->GetName().c_str());
|
||||
@@ -1475,6 +1441,8 @@ Status DBImpl::SetDBOptions(
|
||||
// TODO(xiez): clarify why apply optimize for read to write options
|
||||
file_options_for_compaction_ = fs_->OptimizeForCompactionTableRead(
|
||||
file_options_for_compaction_, immutable_db_options_);
|
||||
file_options_for_compaction_.compaction_readahead_size =
|
||||
mutable_db_options_.compaction_readahead_size;
|
||||
if (wal_other_option_changed || wal_size_option_changed) {
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
@@ -1558,7 +1526,7 @@ Status DBImpl::FlushWAL(const WriteOptions& write_options, bool sync) {
|
||||
io_s.ToString().c_str());
|
||||
// In case there is a fs error we should set it globally to prevent the
|
||||
// future writes
|
||||
WALIOStatusCheck(io_s);
|
||||
IOStatusCheck(io_s);
|
||||
// whether sync or not, we should abort the rest of function upon error
|
||||
return static_cast<Status>(io_s);
|
||||
}
|
||||
@@ -1582,132 +1550,64 @@ bool DBImpl::WALBufferIsEmpty() {
|
||||
return res;
|
||||
}
|
||||
|
||||
Status DBImpl::GetOpenWalSizes(std::map<uint64_t, uint64_t>& number_to_size) {
|
||||
assert(number_to_size.empty());
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
for (auto& log : logs_) {
|
||||
auto* open_file = log.writer->file();
|
||||
if (open_file) {
|
||||
number_to_size[log.number] = open_file->GetFlushedSize();
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::SyncWAL() {
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:Begin");
|
||||
WriteOptions write_options;
|
||||
VersionEdit synced_wals;
|
||||
Status s = SyncWalImpl(/*include_current_wal=*/true, write_options,
|
||||
/*job_context=*/nullptr, &synced_wals,
|
||||
/*error_recovery_in_prog=*/false);
|
||||
|
||||
if (s.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
s = ApplyWALToManifest(read_options, write_options, &synced_wals);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
|
||||
const WriteOptions& write_options,
|
||||
JobContext* job_context, VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog) {
|
||||
autovector<log::Writer*, 1> wals_to_sync;
|
||||
bool need_wal_dir_sync;
|
||||
// Number of a WAL that was active at the start of call and maybe is by
|
||||
// the end of the call.
|
||||
uint64_t maybe_active_number;
|
||||
// Sync WALs up to this number
|
||||
uint64_t up_to_number;
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
bool need_log_dir_sync;
|
||||
uint64_t current_log_number;
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
assert(!logs_.empty());
|
||||
|
||||
maybe_active_number = logfile_number_;
|
||||
up_to_number =
|
||||
include_current_wal ? maybe_active_number : maybe_active_number - 1;
|
||||
// This SyncWAL() call only cares about logs up to this number.
|
||||
current_log_number = logfile_number_;
|
||||
|
||||
while (logs_.front().number <= up_to_number && logs_.front().IsSyncing()) {
|
||||
while (logs_.front().number <= current_log_number &&
|
||||
logs_.front().IsSyncing()) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
// First check that logs are safe to sync in background.
|
||||
if (include_current_wal &&
|
||||
!logs_.back().writer->file()->writable_file()->IsSyncThreadSafe()) {
|
||||
return IOStatus::NotSupported(
|
||||
"SyncWAL() is not supported for this implementation of WAL file",
|
||||
immutable_db_options_.allow_mmap_writes
|
||||
? "try setting Options::allow_mmap_writes to false"
|
||||
: Slice());
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number <= current_log_number; ++it) {
|
||||
if (!it->writer->file()->writable_file()->IsSyncThreadSafe()) {
|
||||
return Status::NotSupported(
|
||||
"SyncWAL() is not supported for this implementation of WAL file",
|
||||
immutable_db_options_.allow_mmap_writes
|
||||
? "try setting Options::allow_mmap_writes to false"
|
||||
: Slice());
|
||||
}
|
||||
}
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number <= up_to_number; ++it) {
|
||||
it != logs_.end() && it->number <= current_log_number; ++it) {
|
||||
auto& log = *it;
|
||||
// Ensure the head of logs_ is marked as getting_synced if any is.
|
||||
log.PrepareForSync();
|
||||
// If last sync failed on a later WAL, this could be a fully synced
|
||||
// and closed WAL that just needs to be recorded as synced in the
|
||||
// manifest.
|
||||
if (log.writer->file()) {
|
||||
wals_to_sync.push_back(log.writer);
|
||||
}
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
need_wal_dir_sync = !log_dir_synced_;
|
||||
need_log_dir_sync = !log_dir_synced_;
|
||||
}
|
||||
|
||||
if (include_current_wal) {
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
}
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
const WriteOptions write_options;
|
||||
IOOptions opts;
|
||||
IOStatus io_s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
std::list<log::Writer*> wals_internally_closed;
|
||||
io_s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (!io_s.ok()) {
|
||||
status = io_s;
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
for (log::Writer* log : wals_to_sync) {
|
||||
if (job_context) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
log->get_log_number());
|
||||
}
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
if (log->get_log_number() >= maybe_active_number) {
|
||||
assert(log->get_log_number() == maybe_active_number);
|
||||
io_s = log->file()->SyncWithoutFlush(opts,
|
||||
immutable_db_options_.use_fsync);
|
||||
} else {
|
||||
io_s = log->file()->Sync(opts, immutable_db_options_.use_fsync);
|
||||
}
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
io_s =
|
||||
log->file()->SyncWithoutFlush(opts, immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
status = io_s;
|
||||
break;
|
||||
}
|
||||
// WALs can be closed when purging obsolete files, but if recycling is
|
||||
// enabled, the log file is closed here so that it can be reused. And
|
||||
// immediate closure here upon final sync makes it easier to guarantee
|
||||
// that Checkpoint doesn't LinkFile on a WAL still open for write, which
|
||||
// might be unsupported for some FileSystem implementations. Close here
|
||||
// should be inexpensive because flush and sync are done, so the kill
|
||||
// switch background_close_inactive_wals is expected to be removed in
|
||||
// the future.
|
||||
if (log->get_log_number() < maybe_active_number &&
|
||||
(immutable_db_options_.recycle_log_file_num > 0 ||
|
||||
!immutable_db_options_.background_close_inactive_wals)) {
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
io_s = log->file()->Close(opts);
|
||||
wals_internally_closed.push_back(log);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
@@ -1715,36 +1615,33 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
|
||||
io_s.ToString().c_str());
|
||||
// In case there is a fs error we should set it globally to prevent the
|
||||
// future writes
|
||||
WALIOStatusCheck(io_s);
|
||||
IOStatusCheck(io_s);
|
||||
}
|
||||
if (io_s.ok() && need_wal_dir_sync) {
|
||||
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
status = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
IOOptions(), nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
if (include_current_wal) {
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:1");
|
||||
} else {
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::SyncClosedWals:BeforeReLock",
|
||||
/*arg=*/nullptr);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:1");
|
||||
VersionEdit synced_wals;
|
||||
{
|
||||
InstrumentedMutexLock l(&log_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();
|
||||
assert(was_closed);
|
||||
(void)was_closed;
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
MarkLogsSynced(up_to_number, need_wal_dir_sync, synced_wals);
|
||||
if (status.ok()) {
|
||||
MarkLogsSynced(current_log_number, need_log_dir_sync, &synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(up_to_number);
|
||||
MarkLogsNotSynced(current_log_number);
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::ApplyWALToManifest(const ReadOptions& read_options,
|
||||
@@ -1757,8 +1654,8 @@ Status DBImpl::ApplyWALToManifest(const ReadOptions& read_options,
|
||||
read_options, write_options, synced_wals, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (!status.ok() && versions_->io_status().IsIOError()) {
|
||||
error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
status = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -1862,19 +1759,16 @@ void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
|
||||
wal.GetPreSyncSize() > 0) {
|
||||
synced_wals->AddWal(wal.number, WalMetadata(wal.GetPreSyncSize()));
|
||||
}
|
||||
// Reclaim closed WALs (wal.writer->file() == nullptr), and if we don't
|
||||
// need to close before that (background_close_inactive_wals) we can
|
||||
// opportunistically reclaim WALs that happen to be fully synced.
|
||||
// (Probably not worth extra code and mutex release to opportunistically
|
||||
// close WALs that became eligible since last holding the mutex.
|
||||
// FindObsoleteFiles can take care of it.)
|
||||
// Check if the file has been closed, i.e wal.writer->file() == nullptr
|
||||
// which can happen if log recycling is enabled, or if all the data in
|
||||
// the log has been synced
|
||||
if (wal.writer->file() == nullptr ||
|
||||
(immutable_db_options_.background_close_inactive_wals &&
|
||||
wal.GetPreSyncSize() == wal.writer->file()->GetFlushedSize())) {
|
||||
wal.GetPreSyncSize() == wal.writer->file()->GetFlushedSize()) {
|
||||
// Fully synced
|
||||
logs_to_free_.push_back(wal.ReleaseWriter());
|
||||
it = logs_.erase(it);
|
||||
} else {
|
||||
assert(wal.GetPreSyncSize() < wal.writer->file()->GetFlushedSize());
|
||||
wal.FinishSync();
|
||||
++it;
|
||||
}
|
||||
@@ -2086,34 +1980,30 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
bool allow_unprepared_value, ArenaWrappedDBIter* db_iter) {
|
||||
InternalIterator* internal_iter;
|
||||
assert(arena != nullptr);
|
||||
auto prefix_extractor =
|
||||
super_version->mutable_cf_options.prefix_extractor.get();
|
||||
// Need to create internal iterator from the arena.
|
||||
MergeIteratorBuilder merge_iter_builder(
|
||||
&cfd->internal_comparator(), arena,
|
||||
// FIXME? It's not clear what interpretation of prefix seek is needed
|
||||
// here, and no unit test cares about the value provided here.
|
||||
!read_options.total_order_seek && prefix_extractor != nullptr,
|
||||
!read_options.total_order_seek &&
|
||||
super_version->mutable_cf_options.prefix_extractor != nullptr,
|
||||
read_options.iterate_upper_bound);
|
||||
// Collect iterator for mutable memtable
|
||||
auto mem_iter = super_version->mem->NewIterator(
|
||||
read_options, super_version->GetSeqnoToTimeMapping(), arena,
|
||||
super_version->mutable_cf_options.prefix_extractor.get());
|
||||
read_options, super_version->GetSeqnoToTimeMapping(), arena);
|
||||
Status s;
|
||||
if (!read_options.ignore_range_deletions) {
|
||||
std::unique_ptr<TruncatedRangeDelIterator> mem_tombstone_iter;
|
||||
TruncatedRangeDelIterator* mem_tombstone_iter = nullptr;
|
||||
auto range_del_iter = super_version->mem->NewRangeTombstoneIterator(
|
||||
read_options, sequence, false /* immutable_memtable */);
|
||||
if (range_del_iter == nullptr || range_del_iter->empty()) {
|
||||
delete range_del_iter;
|
||||
} else {
|
||||
mem_tombstone_iter = std::make_unique<TruncatedRangeDelIterator>(
|
||||
mem_tombstone_iter = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(range_del_iter),
|
||||
&cfd->ioptions()->internal_comparator, nullptr /* smallest */,
|
||||
nullptr /* largest */);
|
||||
}
|
||||
merge_iter_builder.AddPointAndTombstoneIterator(
|
||||
mem_iter, std::move(mem_tombstone_iter));
|
||||
merge_iter_builder.AddPointAndTombstoneIterator(mem_iter,
|
||||
mem_tombstone_iter);
|
||||
} else {
|
||||
merge_iter_builder.AddIterator(mem_iter);
|
||||
}
|
||||
@@ -2122,7 +2012,6 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
if (s.ok()) {
|
||||
super_version->imm->AddIterators(
|
||||
read_options, super_version->GetSeqnoToTimeMapping(),
|
||||
super_version->mutable_cf_options.prefix_extractor.get(),
|
||||
&merge_iter_builder, !read_options.ignore_range_deletions);
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::NewInternalIterator:StatusCallback", &s);
|
||||
@@ -2513,8 +2402,7 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
}
|
||||
if (!s.ok() && !s.IsMergeInProgress() && !s.IsNotFound()) {
|
||||
assert(done);
|
||||
if (!done && !s.ok() && !s.IsMergeInProgress()) {
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
return s;
|
||||
}
|
||||
@@ -3180,11 +3068,10 @@ Status DBImpl::MultiGetImpl(
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_MULTIGET);
|
||||
|
||||
assert(sorted_keys);
|
||||
assert(start_key + num_keys <= sorted_keys->size());
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
for (size_t i = start_key; i < start_key + num_keys; ++i) {
|
||||
KeyContext* kctx = (*sorted_keys)[i];
|
||||
for (auto* kctx : *sorted_keys) {
|
||||
assert(kctx);
|
||||
if (kctx->timestamp) {
|
||||
kctx->timestamp->clear();
|
||||
}
|
||||
@@ -3247,13 +3134,6 @@ Status DBImpl::MultiGetImpl(
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
|
||||
// This could be a long-running operation
|
||||
bool aborted = ROCKSDB_THREAD_YIELD_CHECK_ABORT();
|
||||
if (aborted) {
|
||||
s = Status::Aborted("Query abort.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Post processing (decrement reference counts and record statistics)
|
||||
@@ -3498,8 +3378,6 @@ void DBImpl::MultiGetEntityWithCallback(
|
||||
Status DBImpl::WrapUpCreateColumnFamilies(
|
||||
const ReadOptions& read_options, const WriteOptions& write_options,
|
||||
const std::vector<const ColumnFamilyOptions*>& cf_options) {
|
||||
options_mutex_.AssertHeld();
|
||||
|
||||
// NOTE: this function is skipped for create_missing_column_families and
|
||||
// DB::Open, so new functionality here might need to go into Open also.
|
||||
bool register_worker = false;
|
||||
@@ -3722,8 +3600,6 @@ Status DBImpl::DropColumnFamilies(
|
||||
}
|
||||
|
||||
Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
options_mutex_.AssertHeld();
|
||||
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
const WriteOptions write_options;
|
||||
@@ -3741,9 +3617,6 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
|
||||
Status s;
|
||||
// Save re-aquiring lock for RegisterRecordSeqnoTimeWorker when not
|
||||
// applicable
|
||||
bool used_preserve_preclude = false;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (cfd->IsDropped()) {
|
||||
@@ -3759,11 +3632,9 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
if (s.ok()) {
|
||||
auto& moptions = *cfd->GetLatestMutableCFOptions();
|
||||
max_total_in_memory_state_ -=
|
||||
moptions.write_buffer_size * moptions.max_write_buffer_number;
|
||||
used_preserve_preclude = moptions.preserve_internal_time_seconds > 0 ||
|
||||
moptions.preclude_last_level_data_seconds > 0;
|
||||
auto* mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
max_total_in_memory_state_ -= mutable_cf_options->write_buffer_size *
|
||||
mutable_cf_options->max_write_buffer_number;
|
||||
}
|
||||
|
||||
if (!cf_support_snapshot) {
|
||||
@@ -3781,7 +3652,8 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
|
||||
if (used_preserve_preclude) {
|
||||
if (cfd->ioptions()->preserve_internal_time_seconds > 0 ||
|
||||
cfd->ioptions()->preclude_last_level_data_seconds > 0) {
|
||||
s = RegisterRecordSeqnoTimeWorker(read_options, write_options,
|
||||
/* is_new_db */ false);
|
||||
}
|
||||
@@ -3883,9 +3755,6 @@ Iterator* DBImpl::NewIterator(const ReadOptions& _read_options,
|
||||
}
|
||||
}
|
||||
if (read_options.tailing) {
|
||||
read_options.total_order_seek |=
|
||||
immutable_db_options_.prefix_seek_opt_in_only;
|
||||
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv,
|
||||
/* allow_unprepared_value */ true);
|
||||
result = NewDBIterator(
|
||||
@@ -4019,25 +3888,14 @@ std::unique_ptr<IterType> DBImpl::NewMultiCfIterator(
|
||||
"Different comparators are being used across CFs"));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Iterator*> child_iterators;
|
||||
Status s = NewIterators(_read_options, column_families, &child_iterators);
|
||||
if (!s.ok()) {
|
||||
return error_iterator_func(s);
|
||||
}
|
||||
|
||||
assert(column_families.size() == child_iterators.size());
|
||||
|
||||
std::vector<std::pair<ColumnFamilyHandle*, std::unique_ptr<Iterator>>>
|
||||
cfh_iter_pairs;
|
||||
cfh_iter_pairs.reserve(column_families.size());
|
||||
for (size_t i = 0; i < column_families.size(); ++i) {
|
||||
cfh_iter_pairs.emplace_back(column_families[i], child_iterators[i]);
|
||||
}
|
||||
|
||||
return std::make_unique<ImplType>(_read_options,
|
||||
column_families[0]->GetComparator(),
|
||||
std::move(cfh_iter_pairs));
|
||||
return std::make_unique<ImplType>(column_families[0]->GetComparator(),
|
||||
column_families,
|
||||
std::move(child_iterators));
|
||||
}
|
||||
|
||||
Status DBImpl::NewIterators(
|
||||
@@ -4098,9 +3956,6 @@ Status DBImpl::NewIterators(
|
||||
|
||||
assert(cf_sv_pairs.size() == column_families.size());
|
||||
if (read_options.tailing) {
|
||||
read_options.total_order_seek |=
|
||||
immutable_db_options_.prefix_seek_opt_in_only;
|
||||
|
||||
for (const auto& cf_sv_pair : cf_sv_pairs) {
|
||||
auto iter = new ForwardIterator(this, read_options, cf_sv_pair.cfd,
|
||||
cf_sv_pair.super_version,
|
||||
@@ -4341,8 +4196,8 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
}
|
||||
// Avoid to go through every column family by checking a global threshold
|
||||
// first.
|
||||
CfdList cf_scheduled;
|
||||
if (oldest_snapshot > bottommost_files_mark_threshold_) {
|
||||
CfdList cf_scheduled;
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!cfd->ioptions()->allow_ingest_behind) {
|
||||
cfd->current()->storage_info()->UpdateOldestSnapshot(
|
||||
@@ -4351,7 +4206,7 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
->storage_info()
|
||||
->BottommostFilesMarkedForCompaction()
|
||||
.empty()) {
|
||||
EnqueuePendingCompaction(cfd);
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
cf_scheduled.push_back(cfd);
|
||||
}
|
||||
@@ -4374,24 +4229,6 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
}
|
||||
bottommost_files_mark_threshold_ = new_bottommost_files_mark_threshold;
|
||||
}
|
||||
|
||||
// Avoid to go through every column family by checking a global threshold
|
||||
// first.
|
||||
if (oldest_snapshot >= standalone_range_deletion_files_mark_threshold_) {
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped() || CfdListContains(cf_scheduled, cfd)) {
|
||||
continue;
|
||||
}
|
||||
if (oldest_snapshot >=
|
||||
cfd->current()
|
||||
->storage_info()
|
||||
->standalone_range_tombstone_files_mark_threshold()) {
|
||||
EnqueuePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
cf_scheduled.push_back(cfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete casted_s;
|
||||
}
|
||||
@@ -4633,11 +4470,8 @@ bool DBImpl::GetAggregatedIntProperty(const Slice& property,
|
||||
if (property_info == nullptr || property_info->handle_int == nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto aggregator = CreateIntPropertyAggregator(property);
|
||||
if (aggregator == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t sum = 0;
|
||||
bool ret = true;
|
||||
{
|
||||
// Needs mutex to protect the list of column families.
|
||||
@@ -4651,14 +4485,14 @@ bool DBImpl::GetAggregatedIntProperty(const Slice& property,
|
||||
// GetIntPropertyInternal may release db mutex and re-acquire it.
|
||||
mutex_.AssertHeld();
|
||||
if (ret) {
|
||||
aggregator->Add(cfd, value);
|
||||
sum += value;
|
||||
} else {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*aggregated_value = aggregator->Aggregate();
|
||||
*aggregated_value = sum;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -4767,9 +4601,9 @@ void DBImpl::GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
// Convert user_key into a corresponding internal key.
|
||||
InternalKey k1(start.value(), kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey k2(limit.value(), kMaxSequenceNumber, kValueTypeForSeek);
|
||||
ReadOnlyMemTable::MemTableStats memStats =
|
||||
MemTable::MemTableStats memStats =
|
||||
sv->mem->ApproximateStats(k1.Encode(), k2.Encode());
|
||||
ReadOnlyMemTable::MemTableStats immStats =
|
||||
MemTable::MemTableStats immStats =
|
||||
sv->imm->ApproximateStats(k1.Encode(), k2.Encode());
|
||||
*count = memStats.count + immStats.count;
|
||||
*size = memStats.size + immStats.size;
|
||||
@@ -4843,24 +4677,6 @@ void DBImpl::ReleaseFileNumberFromPendingOutputs(
|
||||
}
|
||||
}
|
||||
|
||||
std::list<uint64_t>::iterator DBImpl::CaptureOptionsFileNumber() {
|
||||
// We need to remember the iterator of our insert, because after the
|
||||
// compaction is done, we need to remove that element from
|
||||
// min_options_file_numbers_.
|
||||
min_options_file_numbers_.push_back(versions_->options_file_number());
|
||||
auto min_options_file_numbers_inserted_elem = min_options_file_numbers_.end();
|
||||
--min_options_file_numbers_inserted_elem;
|
||||
return min_options_file_numbers_inserted_elem;
|
||||
}
|
||||
|
||||
void DBImpl::ReleaseOptionsFileNumber(
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v) {
|
||||
if (v.get() != nullptr) {
|
||||
min_options_file_numbers_.erase(*v.get());
|
||||
v.reset();
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::GetUpdatesSince(
|
||||
SequenceNumber seq, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options) {
|
||||
@@ -5224,12 +5040,11 @@ Status DBImpl::GetDbIdentity(std::string& identity) const {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::GetDbIdentityFromIdentityFile(const IOOptions& opts,
|
||||
std::string* identity) const {
|
||||
Status DBImpl::GetDbIdentityFromIdentityFile(std::string* identity) const {
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
const FileOptions soptions;
|
||||
|
||||
Status s = ReadFileToString(fs_.get(), idfilename, opts, identity);
|
||||
Status s = ReadFileToString(fs_.get(), idfilename, identity);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -5540,8 +5355,7 @@ Status DBImpl::WriteOptionsFile(const WriteOptions& write_options,
|
||||
file_name, fs_.get());
|
||||
|
||||
if (s.ok()) {
|
||||
s = RenameTempFileToOptionsFile(file_name,
|
||||
db_options.compaction_service != nullptr);
|
||||
s = RenameTempFileToOptionsFile(file_name);
|
||||
}
|
||||
|
||||
if (!s.ok() && GetEnv()->FileExists(file_name).ok()) {
|
||||
@@ -5618,8 +5432,7 @@ Status DBImpl::DeleteObsoleteOptionsFiles() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name,
|
||||
bool is_remote_compaction_enabled) {
|
||||
Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
|
||||
Status s;
|
||||
|
||||
uint64_t options_file_number = versions_->NewFileNumber();
|
||||
@@ -5663,7 +5476,7 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name,
|
||||
my_disable_delete_obsolete_files = disable_delete_obsolete_files_;
|
||||
}
|
||||
|
||||
if (!my_disable_delete_obsolete_files && !is_remote_compaction_enabled) {
|
||||
if (!my_disable_delete_obsolete_files) {
|
||||
// TODO: Should we check for errors here?
|
||||
DeleteObsoleteOptionsFiles().PermitUncheckedError();
|
||||
}
|
||||
@@ -5896,6 +5709,7 @@ Status DBImpl::IngestExternalFile(
|
||||
Status DBImpl::IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& args) {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
const WriteOptions write_options;
|
||||
|
||||
if (args.empty()) {
|
||||
@@ -5921,10 +5735,6 @@ Status DBImpl::IngestExternalFiles(
|
||||
snprintf(err_msg, 128, "external_files[%zu] is empty", i);
|
||||
return Status::InvalidArgument(err_msg);
|
||||
}
|
||||
if (i && args[i].options.fill_cache != args[i - 1].options.fill_cache) {
|
||||
return Status::InvalidArgument(
|
||||
"fill_cache should be the same across ingestion options.");
|
||||
}
|
||||
}
|
||||
for (const auto& arg : args) {
|
||||
const IngestExternalFileOptions& ingest_opts = arg.options;
|
||||
@@ -5941,17 +5751,6 @@ Status DBImpl::IngestExternalFiles(
|
||||
"timestamps enabled doesn't support ingest behind.");
|
||||
}
|
||||
}
|
||||
if (ingest_opts.allow_db_generated_files) {
|
||||
if (ingest_opts.write_global_seqno) {
|
||||
return Status::NotSupported(
|
||||
"write_global_seqno is deprecated and does not work with "
|
||||
"allow_db_generated_files.");
|
||||
}
|
||||
}
|
||||
if (ingest_opts.move_files && ingest_opts.link_files) {
|
||||
return Status::InvalidArgument(
|
||||
"`move_files` and `link_files` can not both be true.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (yanqin) maybe handle the case in which column_families have
|
||||
@@ -5984,9 +5783,9 @@ Status DBImpl::IngestExternalFiles(
|
||||
uint64_t start_file_number = next_file_number;
|
||||
for (size_t i = 1; i != num_cfs; ++i) {
|
||||
start_file_number += args[i - 1].external_files.size();
|
||||
SuperVersion* super_version =
|
||||
ingestion_jobs[i].GetColumnFamilyData()->GetReferencedSuperVersion(
|
||||
this);
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
Status es = ingestion_jobs[i].Prepare(
|
||||
args[i].external_files, args[i].files_checksums,
|
||||
args[i].files_checksum_func_names, args[i].file_temperature,
|
||||
@@ -6000,9 +5799,9 @@ Status DBImpl::IngestExternalFiles(
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:BeforeLastJobPrepare:0");
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:BeforeLastJobPrepare:1");
|
||||
{
|
||||
SuperVersion* super_version =
|
||||
ingestion_jobs[0].GetColumnFamilyData()->GetReferencedSuperVersion(
|
||||
this);
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[0].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
Status es = ingestion_jobs[0].Prepare(
|
||||
args[0].external_files, args[0].files_checksums,
|
||||
args[0].files_checksum_func_names, args[0].file_temperature,
|
||||
@@ -6053,7 +5852,8 @@ Status DBImpl::IngestExternalFiles(
|
||||
bool at_least_one_cf_need_flush = false;
|
||||
std::vector<bool> need_flush(num_cfs, false);
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto* cfd = ingestion_jobs[i].GetColumnFamilyData();
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
if (cfd->IsDropped()) {
|
||||
// TODO (yanqin) investigate whether we should abort ingestion or
|
||||
// proceed with other non-dropped column families.
|
||||
@@ -6085,10 +5885,12 @@ Status DBImpl::IngestExternalFiles(
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
if (need_flush[i]) {
|
||||
mutex_.Unlock();
|
||||
status =
|
||||
FlushMemTable(ingestion_jobs[i].GetColumnFamilyData(),
|
||||
flush_opts, FlushReason::kExternalFileIngestion,
|
||||
true /* entered_write_thread */);
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)
|
||||
->cfd();
|
||||
status = FlushMemTable(cfd, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* entered_write_thread */);
|
||||
mutex_.Lock();
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
@@ -6096,13 +5898,6 @@ Status DBImpl::IngestExternalFiles(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
if (immutable_db_options_.atomic_flush || need_flush[i]) {
|
||||
ingestion_jobs[i].SetFlushedBeforeRun();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Run ingestion jobs.
|
||||
if (status.ok()) {
|
||||
@@ -6116,15 +5911,16 @@ Status DBImpl::IngestExternalFiles(
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
ReadOptions read_options;
|
||||
read_options.fill_cache = args[0].options.fill_cache;
|
||||
autovector<ColumnFamilyData*> cfds_to_commit;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<autovector<VersionEdit*>> edit_lists;
|
||||
uint32_t num_entries = 0;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto* cfd = ingestion_jobs[i].GetColumnFamilyData();
|
||||
assert(!cfd->IsDropped());
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfds_to_commit.push_back(cfd);
|
||||
mutable_cf_options_list.push_back(cfd->GetLatestMutableCFOptions());
|
||||
autovector<VersionEdit*> edit_list;
|
||||
@@ -6174,16 +5970,20 @@ Status DBImpl::IngestExternalFiles(
|
||||
|
||||
if (status.ok()) {
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto* cfd = ingestion_jobs[i].GetColumnFamilyData();
|
||||
assert(!cfd->IsDropped());
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_ctxs[i],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
if (!cfd->IsDropped()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_ctxs[i],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
#ifndef NDEBUG
|
||||
if (0 == i && num_cfs > 1) {
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:InstallSVForFirstCF:0");
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFiles:InstallSVForFirstCF:1");
|
||||
}
|
||||
if (0 == i && num_cfs > 1) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::IngestExternalFiles:InstallSVForFirstCF:0");
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::IngestExternalFiles:InstallSVForFirstCF:1");
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
}
|
||||
}
|
||||
} else if (versions_->io_status().IsIOError()) {
|
||||
// Error while writing to MANIFEST.
|
||||
@@ -6225,7 +6025,8 @@ Status DBImpl::IngestExternalFiles(
|
||||
}
|
||||
if (status.ok()) {
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto* cfd = ingestion_jobs[i].GetColumnFamilyData();
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
if (!cfd->IsDropped()) {
|
||||
NotifyOnExternalFileIngested(cfd, ingestion_jobs[i]);
|
||||
}
|
||||
@@ -6856,62 +6657,6 @@ void DBImpl::RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds) {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::TrackOrUntrackFiles(
|
||||
const std::vector<std::string>& existing_data_files, bool track) {
|
||||
auto sfm = static_cast_with_check<SstFileManagerImpl>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
assert(sfm);
|
||||
std::vector<ColumnFamilyMetaData> metadata;
|
||||
GetAllColumnFamilyMetaData(&metadata);
|
||||
auto action = [&](const std::string& file_path,
|
||||
std::optional<uint64_t> size) {
|
||||
if (track) {
|
||||
if (size) {
|
||||
sfm->OnAddFile(file_path, *size).PermitUncheckedError();
|
||||
} else {
|
||||
sfm->OnAddFile(file_path).PermitUncheckedError();
|
||||
}
|
||||
} else {
|
||||
sfm->OnUntrackFile(file_path).PermitUncheckedError();
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_set<std::string> referenced_files;
|
||||
for (const auto& md : metadata) {
|
||||
for (const auto& lmd : md.levels) {
|
||||
for (const auto& fmd : lmd.files) {
|
||||
// We're assuming that each sst file name exists in at most one of
|
||||
// the paths.
|
||||
std::string file_path =
|
||||
fmd.directory + kFilePathSeparator + fmd.relative_filename;
|
||||
action(file_path, fmd.size);
|
||||
referenced_files.insert(file_path);
|
||||
}
|
||||
}
|
||||
for (const auto& bmd : md.blob_files) {
|
||||
std::string name = bmd.blob_file_name;
|
||||
// The BlobMetaData.blob_file_name may start with "/".
|
||||
if (!name.empty() && name[0] == kFilePathSeparator) {
|
||||
name = name.substr(1);
|
||||
}
|
||||
// We're assuming that each blob file name exists in at most one of
|
||||
// the paths.
|
||||
std::string file_path = bmd.blob_file_path + kFilePathSeparator + name;
|
||||
action(file_path, bmd.blob_file_size);
|
||||
referenced_files.insert(file_path);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& file_path : existing_data_files) {
|
||||
if (referenced_files.find(file_path) != referenced_files.end()) {
|
||||
continue;
|
||||
}
|
||||
// There shouldn't be any duplicated files. In case there is, SstFileManager
|
||||
// will take care of deduping it.
|
||||
action(file_path, /*size=*/std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::InstallSeqnoToTimeMappingInSV(
|
||||
std::vector<SuperVersionContext>* sv_contexts) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
+41
-151
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
@@ -58,7 +57,6 @@
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/trace_reader_writer.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/user_write_callback.h"
|
||||
#include "rocksdb/utilities/replayer.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/merging_iterator.h"
|
||||
@@ -233,10 +231,6 @@ class DBImpl : public DB {
|
||||
using DB::Write;
|
||||
Status Write(const WriteOptions& options, WriteBatch* updates) override;
|
||||
|
||||
using DB::WriteWithCallback;
|
||||
Status WriteWithCallback(const WriteOptions& options, WriteBatch* updates,
|
||||
UserWriteCallback* user_write_cb) override;
|
||||
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
@@ -363,10 +357,12 @@ class DBImpl : public DB {
|
||||
const Snapshot* GetSnapshot() override;
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
|
||||
// EXPERIMENTAL
|
||||
std::unique_ptr<Iterator> NewCoalescingIterator(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
|
||||
// EXPERIMENTAL
|
||||
std::unique_ptr<AttributeGroupIterator> NewAttributeGroupIterator(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
@@ -480,8 +476,7 @@ class DBImpl : public DB {
|
||||
|
||||
Status GetDbIdentity(std::string& identity) const override;
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(const IOOptions& opts,
|
||||
std::string* identity) const;
|
||||
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
|
||||
|
||||
Status GetDbSessionId(std::string& session_id) const override;
|
||||
|
||||
@@ -506,16 +501,8 @@ class DBImpl : public DB {
|
||||
// All the returned filenames start with "/"
|
||||
Status GetLiveFiles(std::vector<std::string>&, uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true) override;
|
||||
Status GetSortedWalFiles(VectorWalPtr& files) override;
|
||||
Status GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos);
|
||||
|
||||
// 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
|
||||
// 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 GetSortedWalFiles(VectorLogPtr& files) override;
|
||||
Status GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) override;
|
||||
Status GetCreationTimeOfOldestFile(uint64_t* creation_time) override;
|
||||
|
||||
Status GetUpdatesSince(
|
||||
@@ -701,8 +688,7 @@ class DBImpl : public DB {
|
||||
// thread to determine whether it is safe to perform the write.
|
||||
virtual Status WriteWithCallback(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch,
|
||||
WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb = nullptr);
|
||||
WriteCallback* callback);
|
||||
|
||||
// Returns the sequence number that is guaranteed to be smaller than or equal
|
||||
// to the sequence number of any key that could be inserted into the current
|
||||
@@ -852,8 +838,6 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t GetObsoleteSstFilesSize();
|
||||
|
||||
uint64_t MinOptionsFileNumberToKeep();
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
// of all files in the filesystem in 'candidate_files'.
|
||||
// If force == false and the last call was less than
|
||||
@@ -1152,8 +1136,6 @@ class DBImpl : public DB {
|
||||
// Get the background error status
|
||||
Status TEST_GetBGError();
|
||||
|
||||
bool TEST_IsRecoveryInProgress();
|
||||
|
||||
// Return the maximum overlapping data (in bytes) at next level for any
|
||||
// file at a level >= 1.
|
||||
uint64_t TEST_MaxNextLevelOverlappingBytes(
|
||||
@@ -1198,7 +1180,9 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t TEST_total_log_size() const { return total_log_size_; }
|
||||
|
||||
void TEST_GetAllBlockCaches(std::unordered_set<const Cache*>* cache_set);
|
||||
// Returns column family name to ImmutableCFOptions map.
|
||||
Status TEST_GetAllImmutableCFOptions(
|
||||
std::unordered_map<std::string, const ImmutableCFOptions*>* iopts_map);
|
||||
|
||||
// Return the lastest MutableCFOptions of a column family
|
||||
Status TEST_GetLatestMutableCFOptions(ColumnFamilyHandle* column_family,
|
||||
@@ -1238,13 +1222,8 @@ class DBImpl : public DB {
|
||||
static Status TEST_ValidateOptions(const DBOptions& db_options) {
|
||||
return ValidateOptions(db_options);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
// In certain configurations, verify that the table/blob file cache only
|
||||
// contains entries for live files, to check for effective leaks of open
|
||||
// files. This can only be called when purging of obsolete files has
|
||||
// "settled," such as during parts of DB Close().
|
||||
void TEST_VerifyNoObsoleteFilesCached(bool db_mutex_already_held) const;
|
||||
#endif // NDEBUG
|
||||
|
||||
// persist stats to column family "_persistent_stats"
|
||||
void PersistStats();
|
||||
@@ -1467,13 +1446,9 @@ class DBImpl : public DB {
|
||||
// The following two functions can only be called when:
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
// 2. db_mutex is NOT held
|
||||
Status RenameTempFileToOptionsFile(const std::string& file_name,
|
||||
bool is_remote_compaction_enabled);
|
||||
Status RenameTempFileToOptionsFile(const std::string& file_name);
|
||||
Status DeleteObsoleteOptionsFiles();
|
||||
|
||||
void NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
|
||||
FlushReason flush_reason);
|
||||
|
||||
void NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, FlushReason flush_reason);
|
||||
@@ -1524,7 +1499,6 @@ class DBImpl : public DB {
|
||||
// batch that does not have duplicate keys.
|
||||
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,
|
||||
bool disable_memtable = false, uint64_t* seq_used = nullptr,
|
||||
size_t batch_cnt = 0,
|
||||
@@ -1533,7 +1507,6 @@ class DBImpl : public DB {
|
||||
|
||||
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,
|
||||
bool disable_memtable = false,
|
||||
uint64_t* seq_used = nullptr);
|
||||
@@ -1560,8 +1533,7 @@ class DBImpl : public DB {
|
||||
// marks start of a new sub-batch.
|
||||
Status WriteImplWALOnly(
|
||||
WriteThread* write_thread, const WriteOptions& options,
|
||||
WriteBatch* updates, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
WriteBatch* updates, WriteCallback* callback, uint64_t* log_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);
|
||||
@@ -1589,12 +1561,11 @@ class DBImpl : public DB {
|
||||
|
||||
virtual bool OwnTablesAndLogs() const { return true; }
|
||||
|
||||
// Read/create DB identity file (as appropriate), and write DB ID to
|
||||
// version_edit if provided.
|
||||
// Setup DB identity file, and write DB ID to manifest if necessary.
|
||||
Status SetupDBId(const WriteOptions& write_options, bool read_only,
|
||||
bool is_new_db, bool is_retry, VersionEdit* version_edit);
|
||||
// Assign db_id_ and write DB ID to version_edit if provided.
|
||||
void SetDBId(std::string&& id, bool read_only, VersionEdit* version_edit);
|
||||
RecoveryContext* recovery_ctx);
|
||||
// Assign db_id_ and write DB ID to manifest if necessary.
|
||||
void SetDBId(std::string&& id, bool read_only, RecoveryContext* recovery_ctx);
|
||||
|
||||
// Collect a deduplicated collection of paths used by this DB, including
|
||||
// dbname_, DBOptions.db_paths, ColumnFamilyOptions.cf_paths.
|
||||
@@ -1624,15 +1595,9 @@ class DBImpl : public DB {
|
||||
// vast majority of all files), since it already has the file size
|
||||
// on record, we don't need to query the file system. Otherwise, we query the
|
||||
// file system for the size of an unreferenced file.
|
||||
// REQUIRES: mutex unlocked
|
||||
void TrackExistingDataFiles(
|
||||
const std::vector<std::string>& existing_data_files);
|
||||
|
||||
// Untrack data files in sst manager. This is only called during DB::Close on
|
||||
// an unowned SstFileManager, to return it to a consistent state.
|
||||
// REQUIRES: mutex unlocked
|
||||
void UntrackDataFiles();
|
||||
|
||||
// SetDbSessionId() should be called in the constuctor DBImpl()
|
||||
// to ensure that db_session_id_ gets updated every time the DB is opened
|
||||
void SetDbSessionId();
|
||||
@@ -1699,8 +1664,6 @@ class DBImpl : public DB {
|
||||
friend class XFTransactionWriteHandler;
|
||||
friend class DBBlobIndexTest;
|
||||
friend class WriteUnpreparedTransactionTest_RecoveryTest_Test;
|
||||
friend class CompactionServiceTest_PreservedOptionsLocalCompaction_Test;
|
||||
friend class CompactionServiceTest_PreservedOptionsRemoteCompaction_Test;
|
||||
#endif
|
||||
|
||||
struct CompactionState;
|
||||
@@ -1709,7 +1672,7 @@ class DBImpl : public DB {
|
||||
|
||||
struct WriteContext {
|
||||
SuperVersionContext superversion_context;
|
||||
autovector<ReadOnlyMemTable*> memtables_to_free_;
|
||||
autovector<MemTable*> memtables_to_free_;
|
||||
|
||||
explicit WriteContext(bool create_superversion = false)
|
||||
: superversion_context(create_superversion) {}
|
||||
@@ -1742,11 +1705,8 @@ class DBImpl : public DB {
|
||||
return w;
|
||||
}
|
||||
Status ClearWriter() {
|
||||
Status s;
|
||||
if (writer->file()) {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
s = writer->WriteBuffer(WriteOptions());
|
||||
}
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
Status s = writer->WriteBuffer(WriteOptions());
|
||||
delete writer;
|
||||
writer = nullptr;
|
||||
return s;
|
||||
@@ -1761,16 +1721,10 @@ class DBImpl : public DB {
|
||||
|
||||
void PrepareForSync() {
|
||||
assert(!getting_synced);
|
||||
// Ensure the head of logs_ is marked as getting_synced if any is.
|
||||
// Size is expected to be monotonically increasing.
|
||||
assert(writer->file()->GetFlushedSize() >= pre_sync_size);
|
||||
getting_synced = true;
|
||||
// If last sync failed on a later WAL, this could be a fully synced
|
||||
// and closed WAL that just needs to be recorded as synced in the
|
||||
// manifest.
|
||||
if (writer->file()) {
|
||||
// Size is expected to be monotonically increasing.
|
||||
assert(writer->file()->GetFlushedSize() >= pre_sync_size);
|
||||
pre_sync_size = writer->file()->GetFlushedSize();
|
||||
}
|
||||
pre_sync_size = writer->file()->GetFlushedSize();
|
||||
}
|
||||
|
||||
void FinishSync() {
|
||||
@@ -1906,10 +1860,6 @@ class DBImpl : public DB {
|
||||
Env::Priority compaction_pri_;
|
||||
};
|
||||
|
||||
static bool IsRecoveryFlush(FlushReason flush_reason) {
|
||||
return flush_reason == FlushReason::kErrorRecoveryRetryFlush ||
|
||||
flush_reason == FlushReason::kErrorRecovery;
|
||||
}
|
||||
// Initialize the built-in column family for persistent stats. Depending on
|
||||
// whether on-disk persistent stats have been enabled before, it may either
|
||||
// create a new column family and column family handle or just a column family
|
||||
@@ -1972,14 +1922,7 @@ class DBImpl : public DB {
|
||||
void ReleaseFileNumberFromPendingOutputs(
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v);
|
||||
|
||||
// Similar to pending_outputs, preserve OPTIONS file. Used for remote
|
||||
// compaction.
|
||||
std::list<uint64_t>::iterator CaptureOptionsFileNumber();
|
||||
void ReleaseOptionsFileNumber(
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v);
|
||||
|
||||
// Sets bg error if there is an error writing to WAL.
|
||||
IOStatus SyncClosedWals(const WriteOptions& write_options,
|
||||
IOStatus SyncClosedLogs(const WriteOptions& write_options,
|
||||
JobContext* job_context, VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog);
|
||||
|
||||
@@ -2051,8 +1994,6 @@ class DBImpl : public DB {
|
||||
|
||||
Status TrimMemtableHistory(WriteContext* context);
|
||||
|
||||
// Switches the current live memtable to immutable/read-only memtable.
|
||||
// A new WAL is created if the current WAL is not empty.
|
||||
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context);
|
||||
|
||||
// Select and output column families qualified for atomic flush in
|
||||
@@ -2063,8 +2004,7 @@ class DBImpl : public DB {
|
||||
// REQUIRES: mutex held
|
||||
void SelectColumnFamiliesForAtomicFlush(
|
||||
autovector<ColumnFamilyData*>* selected_cfds,
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds = {},
|
||||
FlushReason flush_reason = FlushReason::kOthers);
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds = {});
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
@@ -2091,18 +2031,17 @@ class DBImpl : public DB {
|
||||
// memtable pending flush.
|
||||
// resuming_from_bg_err indicates whether the caller is attempting to resume
|
||||
// from background error.
|
||||
Status WaitForFlushMemTable(
|
||||
ColumnFamilyData* cfd, const uint64_t* flush_memtable_id = nullptr,
|
||||
bool resuming_from_bg_err = false,
|
||||
std::optional<FlushReason> flush_reason = std::nullopt) {
|
||||
Status WaitForFlushMemTable(ColumnFamilyData* cfd,
|
||||
const uint64_t* flush_memtable_id = nullptr,
|
||||
bool resuming_from_bg_err = false) {
|
||||
return WaitForFlushMemTables({cfd}, {flush_memtable_id},
|
||||
resuming_from_bg_err, flush_reason);
|
||||
resuming_from_bg_err);
|
||||
}
|
||||
// Wait for memtables to be flushed for multiple column families.
|
||||
Status WaitForFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const uint64_t*>& flush_memtable_ids,
|
||||
bool resuming_from_bg_err, std::optional<FlushReason> flush_reason);
|
||||
bool resuming_from_bg_err);
|
||||
|
||||
inline void WaitForPendingWrites() {
|
||||
mutex_.AssertHeld();
|
||||
@@ -2200,7 +2139,7 @@ class DBImpl : public DB {
|
||||
|
||||
// Used by WriteImpl to update bg_error_ when IO error happens, e.g., write
|
||||
// WAL, sync WAL fails, if paranoid check is enabled.
|
||||
void WALIOStatusCheck(const IOStatus& status);
|
||||
void IOStatusCheck(const IOStatus& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
|
||||
void MemTableInsertStatusCheck(const Status& memtable_insert_status);
|
||||
@@ -2213,9 +2152,7 @@ class DBImpl : public DB {
|
||||
JobContext* job_context, LogBuffer* log_buffer,
|
||||
CompactionJobInfo* compaction_job_info);
|
||||
|
||||
// REQUIRES: mutex unlocked
|
||||
void TrackOrUntrackFiles(const std::vector<std::string>& existing_data_files,
|
||||
bool track);
|
||||
ColumnFamilyData* GetColumnFamilyDataByName(const std::string& cf_name);
|
||||
|
||||
void MaybeScheduleFlushOrCompaction();
|
||||
|
||||
@@ -2244,27 +2181,9 @@ class DBImpl : public DB {
|
||||
void GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
FlushReason flush_reason, FlushRequest* req);
|
||||
|
||||
// Below functions are for executing flush, compaction in the background. A
|
||||
// dequeue is the communication channel between threads that asks for the work
|
||||
// to be done and the available threads in the thread pool that pick it up to
|
||||
// execute it. We use these terminologies to describe the state of the work
|
||||
// and its transitions:
|
||||
// 1) It becomes pending once it's successfully enqueued into the
|
||||
// corresponding dequeue, a work in this state is also called unscheduled.
|
||||
// Counter `unscheduled_*_` counts work in this state.
|
||||
// 2) When `MaybeScheduleFlushOrCompaction` schedule a thread to run `BGWork*`
|
||||
// for the work, it becomes scheduled
|
||||
// Counter `bg_*_scheduled_` counts work in this state.
|
||||
// 3) Once the thread start to execute `BGWork*`, the work is popped from the
|
||||
// dequeue, it is now in running state
|
||||
// Counter `num_running_*_` counts work in this state.
|
||||
// 4) Eventually, the work is finished. We don't need to specifically track
|
||||
// finished work.
|
||||
void SchedulePendingFlush(const FlushRequest& req);
|
||||
|
||||
// Returns true if `req` is successfully enqueued.
|
||||
bool EnqueuePendingFlush(const FlushRequest& req);
|
||||
|
||||
void EnqueuePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
FileType type, uint64_t number, int job_id);
|
||||
static void BGWorkCompaction(void* arg);
|
||||
@@ -2338,11 +2257,6 @@ class DBImpl : public DB {
|
||||
ColumnFamilyData* PickCompactionFromQueue(
|
||||
std::unique_ptr<TaskLimiterToken>* token, LogBuffer* log_buffer);
|
||||
|
||||
IOStatus SyncWalImpl(bool include_current_wal,
|
||||
const WriteOptions& write_options,
|
||||
JobContext* job_context, VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog);
|
||||
|
||||
// helper function to call after some of the logs_ were synced
|
||||
void MarkLogsSynced(uint64_t up_to, bool synced_dir, VersionEdit* edit);
|
||||
Status ApplyWALToManifest(const ReadOptions& read_options,
|
||||
@@ -2400,9 +2314,6 @@ class DBImpl : public DB {
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
|
||||
void UpdateDeletionCompactionStats(const std::unique_ptr<Compaction>& c);
|
||||
|
||||
// May open and read table files for table property.
|
||||
// Should not be called while holding mutex_.
|
||||
void BuildCompactionJobInfo(const ColumnFamilyData* cfd, Compaction* c,
|
||||
const Status& st,
|
||||
const CompactionJobStats& compaction_job_stats,
|
||||
@@ -2618,10 +2529,8 @@ class DBImpl : public DB {
|
||||
|
||||
bool persistent_stats_cfd_exists_ = 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:
|
||||
// alive_log_files_ is protected by mutex_ and log_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
|
||||
// held.
|
||||
@@ -2672,7 +2581,7 @@ class DBImpl : public DB {
|
||||
// 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
|
||||
// 10. read by SyncClosedLogs() protected by only log_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()
|
||||
@@ -2685,7 +2594,7 @@ class DBImpl : public DB {
|
||||
// 13. emplace_back() by SwitchMemtable() hold both mutex_ and
|
||||
// log_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
|
||||
// SyncClosedLogs(), etc. as well as application threads calling
|
||||
// FlushWAL(), SyncWAL(), LockWAL(). This is fine because all parties
|
||||
// require at least log_write_mutex_.
|
||||
// 14. iteration called in WriteToWAL(write_group) protected by
|
||||
@@ -2769,11 +2678,6 @@ class DBImpl : public DB {
|
||||
// State is protected with db mutex.
|
||||
std::list<uint64_t> pending_outputs_;
|
||||
|
||||
// Similar to pending_outputs_, FindObsoleteFiles()/PurgeObsoleteFiles() never
|
||||
// deletes any OPTIONS file that has number bigger than any of the file number
|
||||
// in min_options_file_numbers_.
|
||||
std::list<uint64_t> min_options_file_numbers_;
|
||||
|
||||
// flush_queue_ and compaction_queue_ hold column families that we need to
|
||||
// flush and compact, respectively.
|
||||
// A column family is inserted into flush_queue_ when it satisfies condition
|
||||
@@ -2896,11 +2800,6 @@ class DBImpl : public DB {
|
||||
// garbages, among all column families.
|
||||
SequenceNumber bottommost_files_mark_threshold_ = kMaxSequenceNumber;
|
||||
|
||||
// The min threshold to trigger compactions for standalone range deletion
|
||||
// files that are marked for compaction.
|
||||
SequenceNumber standalone_range_deletion_files_mark_threshold_ =
|
||||
kMaxSequenceNumber;
|
||||
|
||||
LogsWithPrepTracker logs_with_prep_tracker_;
|
||||
|
||||
// Callback for compaction to check if a key is visible to a snapshot.
|
||||
@@ -3001,15 +2900,6 @@ DBOptions SanitizeOptions(const std::string& db, const DBOptions& src,
|
||||
CompressionType GetCompressionFlush(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Return a VersionEdit for the DB's recovery when the `memtables` of the
|
||||
// specified column family are obsolete. Specifically, the min log number to
|
||||
// keep, and the WAL files that can be deleted.
|
||||
VersionEdit GetDBRecoveryEditForObsoletingMemTables(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd,
|
||||
const autovector<VersionEdit*>& edit_list,
|
||||
const autovector<ReadOnlyMemTable*>& memtables,
|
||||
LogsWithPrepTracker* prep_tracker);
|
||||
|
||||
// Return the earliest log file to keep after the memtable flush is
|
||||
// finalized.
|
||||
// `cfd_to_flush` is the column family whose memtable (specified in
|
||||
@@ -3019,13 +2909,13 @@ VersionEdit GetDBRecoveryEditForObsoletingMemTables(
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list,
|
||||
const autovector<ReadOnlyMemTable*>& memtables_to_flush,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker);
|
||||
// For atomic flush.
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists,
|
||||
const autovector<const autovector<ReadOnlyMemTable*>*>& memtables_to_flush,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker);
|
||||
|
||||
// In non-2PC mode, WALs with log number < the returned number can be
|
||||
@@ -3042,11 +2932,11 @@ uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
// will not depend on any WAL file. nullptr means no memtable is being flushed.
|
||||
// The function is only applicable to 2pc mode.
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<ReadOnlyMemTable*>& memtables_to_flush);
|
||||
VersionSet* vset, const autovector<MemTable*>& memtables_to_flush);
|
||||
// For atomic flush.
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset,
|
||||
const autovector<const autovector<ReadOnlyMemTable*>*>& memtables_to_flush);
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush);
|
||||
|
||||
// Fix user-supplied options to be reasonable
|
||||
template <class T, class V>
|
||||
|
||||
@@ -87,9 +87,6 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
|
||||
mutex_.AssertHeld();
|
||||
assert(flush_req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
ColumnFamilyData* cfd = flush_req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||
if (cfd->GetAndClearFlushSkipReschedule()) {
|
||||
return false;
|
||||
}
|
||||
uint64_t max_memtable_id =
|
||||
flush_req.cfd_to_max_mem_id_to_persist.begin()->second;
|
||||
if (cfd->IsDropped() ||
|
||||
@@ -101,20 +98,15 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
|
||||
// alleviated if we continue with the flush instead of postponing it.
|
||||
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
|
||||
// Use the same criteria as WaitUntilFlushWouldNotStallWrites does w.r.t
|
||||
// defining what a write stall is about to happen means. If this uses a
|
||||
// stricter criteria, for example, a write stall is about to happen if the
|
||||
// last memtable is 10% full, there is a possibility that manual flush could
|
||||
// be waiting in `WaitUntilFlushWouldNotStallWrites` with the incorrect
|
||||
// expectation that others will clear up the excessive memtables and
|
||||
// eventually let it proceed. The others in this case won't start clearing
|
||||
// until the last memtable is 10% full. To avoid that scenario, the criteria
|
||||
// this uses should be the same or less strict than
|
||||
// `WaitUntilFlushWouldNotStallWrites` does.
|
||||
// Taking the status of the active Memtable into consideration so that we are
|
||||
// not just checking if DB is currently already in write stall mode.
|
||||
int mem_to_flush = cfd->mem()->ApproximateMemoryUsageFast() >=
|
||||
cfd->mem()->write_buffer_size() / 2
|
||||
? 1
|
||||
: 0;
|
||||
WriteStallCondition write_stall =
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
|
||||
/*num_l0_files=*/0,
|
||||
cfd->imm()->NumNotFlushed() + mem_to_flush, /*num_l0_files=*/0,
|
||||
/*num_compaction_needed_bytes=*/0, mutable_cf_options,
|
||||
*cfd->ioptions())
|
||||
.first;
|
||||
@@ -124,19 +116,89 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
|
||||
return true;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::SyncClosedWals(const WriteOptions& write_options,
|
||||
IOStatus DBImpl::SyncClosedLogs(const WriteOptions& write_options,
|
||||
JobContext* job_context,
|
||||
VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedWals:Start");
|
||||
|
||||
IOStatus io_s = SyncWalImpl(/*include_current_wal*/ false, write_options,
|
||||
job_context, synced_wals, error_recovery_in_prog);
|
||||
if (!io_s.ok()) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedWals:Failed");
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedWals:end");
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
uint64_t current_log_number = logfile_number_;
|
||||
while (logs_.front().number < current_log_number &&
|
||||
logs_.front().IsSyncing()) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number < current_log_number; ++it) {
|
||||
auto& log = *it;
|
||||
log.PrepareForSync();
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
IOStatus io_s;
|
||||
if (!logs_to_sync.empty()) {
|
||||
log_write_mutex_.Unlock();
|
||||
|
||||
assert(job_context);
|
||||
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
log->get_log_number());
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
|
||||
IOOptions io_options;
|
||||
io_s = WritableFileWriter::PrepareIOOptions(write_options, io_options);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
io_s = log->file()->Sync(io_options, immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (immutable_db_options_.recycle_log_file_num > 0) {
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
// Normally the log file is closed when purging obsolete file, but if
|
||||
// log recycling is enabled, the log file is closed here so that it
|
||||
// can be reused.
|
||||
io_s = log->Close(write_options);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
IOOptions io_options;
|
||||
io_s = WritableFileWriter::PrepareIOOptions(write_options, io_options);
|
||||
if (io_s.ok()) {
|
||||
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
io_options, nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::SyncClosedLogs:BeforeReLock",
|
||||
/*arg=*/nullptr);
|
||||
log_write_mutex_.Lock();
|
||||
|
||||
// "number <= current_log_number - 1" is equivalent to
|
||||
// "number < current_log_number".
|
||||
if (io_s.ok()) {
|
||||
MarkLogsSynced(current_log_number - 1, true, synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(current_log_number - 1);
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return io_s;
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:end");
|
||||
return io_s;
|
||||
}
|
||||
|
||||
@@ -175,12 +237,12 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// If needs_to_sync_closed_wals is true, we need to record the current
|
||||
// maximum memtable ID of this column family so that a later PickMemtables()
|
||||
// call will not pick memtables whose IDs are higher. This is due to the fact
|
||||
// that SyncClosedWals() may release the db mutex, and memtable switch can
|
||||
// that SyncClosedLogs() may release the db mutex, and memtable switch can
|
||||
// happen for this column family in the meantime. The newly created memtables
|
||||
// have their data backed by unsynced WALs, thus they cannot be included in
|
||||
// this flush job.
|
||||
// Another reason why we must record the current maximum memtable ID of this
|
||||
// column family: SyncClosedWals() may release db mutex, thus it's possible
|
||||
// column family: SyncClosedLogs() may release db mutex, thus it's possible
|
||||
// for application to continue to insert into memtables increasing db's
|
||||
// sequence number. The application may take a snapshot, but this snapshot is
|
||||
// not included in `snapshot_seqs` which will be passed to flush job because
|
||||
@@ -194,7 +256,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
|
||||
// If needs_to_sync_closed_wals is false, then the flush job will pick ALL
|
||||
// existing memtables of the column family when PickMemTable() is called
|
||||
// later. Although we won't call SyncClosedWals() in this case, we may still
|
||||
// later. Although we won't call SyncClosedLogs() in this case, we may still
|
||||
// call the callbacks of the listeners, i.e. NotifyOnFlushBegin() which also
|
||||
// releases and re-acquires the db mutex. In the meantime, the application
|
||||
// can still insert into the memtables and increase the db's sequence number.
|
||||
@@ -224,12 +286,12 @@ 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
|
||||
// SyncClosedLogs() may unlock and re-lock the log_write_mutex multiple
|
||||
// times.
|
||||
VersionEdit synced_wals;
|
||||
bool error_recovery_in_prog = error_handler_.IsRecoveryInProgress();
|
||||
mutex_.Unlock();
|
||||
log_io_s = SyncClosedWals(write_options, job_context, &synced_wals,
|
||||
log_io_s = SyncClosedLogs(write_options, job_context, &synced_wals,
|
||||
error_recovery_in_prog);
|
||||
mutex_.Lock();
|
||||
if (log_io_s.ok() && synced_wals.IsWalAddition()) {
|
||||
@@ -244,7 +306,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
error_handler_.SetBGError(log_io_s, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedWals:Skip");
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Skip");
|
||||
}
|
||||
s = log_io_s;
|
||||
|
||||
@@ -518,7 +580,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
VersionEdit synced_wals;
|
||||
bool error_recovery_in_prog = error_handler_.IsRecoveryInProgress();
|
||||
mutex_.Unlock();
|
||||
log_io_s = SyncClosedWals(write_options, job_context, &synced_wals,
|
||||
log_io_s = SyncClosedLogs(write_options, job_context, &synced_wals,
|
||||
error_recovery_in_prog);
|
||||
mutex_.Lock();
|
||||
if (log_io_s.ok() && synced_wals.IsWalAddition()) {
|
||||
@@ -753,7 +815,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
if (s.ok()) {
|
||||
autovector<ColumnFamilyData*> tmp_cfds;
|
||||
autovector<const autovector<ReadOnlyMemTable*>*> mems_list;
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<FileMetaData*> tmp_file_meta;
|
||||
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
|
||||
@@ -1457,6 +1519,11 @@ Status DBImpl::CompactFilesImpl(
|
||||
input_set.insert(TableFileNameToNumber(file_name));
|
||||
}
|
||||
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
// TODO(yhchiang): can directly use version here if none of the
|
||||
// following functions call is pluggable to external developers.
|
||||
version->GetColumnFamilyMetaData(&cf_meta);
|
||||
|
||||
if (output_path_id < 0) {
|
||||
if (cfd->ioptions()->cf_paths.size() == 1U) {
|
||||
output_path_id = 0;
|
||||
@@ -1477,7 +1544,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
std::vector<CompactionInputFiles> input_files;
|
||||
Status s = cfd->compaction_picker()->SanitizeAndConvertCompactionInputFiles(
|
||||
&input_set, output_level, version, &input_files);
|
||||
&input_set, cf_meta, output_level, version->storage_info(), &input_files);
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::CompactFilesImpl::PostSanitizeAndConvertCompactionInputFiles");
|
||||
if (!s.ok()) {
|
||||
@@ -1556,12 +1623,6 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
compaction_job.Prepare();
|
||||
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> min_options_file_number_elem;
|
||||
if (immutable_db_options().compaction_service != nullptr) {
|
||||
min_options_file_number_elem.reset(
|
||||
new std::list<uint64_t>::iterator(CaptureOptionsFileNumber()));
|
||||
}
|
||||
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("CompactFilesImpl:0");
|
||||
TEST_SYNC_POINT("CompactFilesImpl:1");
|
||||
@@ -1571,10 +1632,6 @@ Status DBImpl::CompactFilesImpl(
|
||||
TEST_SYNC_POINT("CompactFilesImpl:3");
|
||||
mutex_.Lock();
|
||||
|
||||
if (immutable_db_options().compaction_service != nullptr) {
|
||||
ReleaseOptionsFileNumber(min_options_file_number_elem);
|
||||
}
|
||||
|
||||
bool compaction_released = false;
|
||||
Status status =
|
||||
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
|
||||
@@ -1600,12 +1657,10 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
mutex_.Unlock();
|
||||
if (compaction_job_info != nullptr) {
|
||||
BuildCompactionJobInfo(cfd, c.get(), s, compaction_job_stats,
|
||||
job_context->job_id, compaction_job_info);
|
||||
}
|
||||
mutex_.Lock();
|
||||
|
||||
if (status.ok()) {
|
||||
// Done
|
||||
@@ -1857,9 +1912,8 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
0 /* max_subcompactions, not applicable */,
|
||||
{} /* grandparents, not applicable */,
|
||||
std::nullopt /* earliest_snapshot */, nullptr /* snapshot_checker */,
|
||||
false /* is manual */, "" /* trim_ts */, -1 /* score, not applicable */,
|
||||
{} /* grandparents, not applicable */, false /* is manual */,
|
||||
"" /* trim_ts */, -1 /* score, not applicable */,
|
||||
false /* is deletion compaction, not applicable */,
|
||||
false /* l0_files_might_overlap, not applicable */,
|
||||
CompactionReason::kRefitLevel));
|
||||
@@ -1886,7 +1940,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
Status status = versions_->LogAndApply(cfd, mutable_cf_options,
|
||||
read_options, write_options, &edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
c->MarkFilesBeingCompacted(false);
|
||||
|
||||
cfd->compaction_picker()->UnregisterCompaction(c.get());
|
||||
c.reset();
|
||||
|
||||
@@ -2256,23 +2310,6 @@ void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
|
||||
FlushReason flush_reason) {
|
||||
if (immutable_db_options_.listeners.size() == 0U) {
|
||||
return;
|
||||
}
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
std::vector<ManualFlushInfo> info;
|
||||
for (ColumnFamilyData* cfd : cfds) {
|
||||
info.push_back({cfd->GetID(), cfd->GetName(), flush_reason});
|
||||
}
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnManualFlushScheduled(this, info);
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
FlushReason flush_reason,
|
||||
@@ -2312,15 +2349,13 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
WaitForPendingWrites();
|
||||
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load() ||
|
||||
IsRecoveryFlush(flush_reason)) {
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load()) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
}
|
||||
const uint64_t flush_memtable_id = std::numeric_limits<uint64_t>::max();
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load() ||
|
||||
IsRecoveryFlush(flush_reason)) {
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
FlushRequest req{flush_reason, {{cfd, flush_memtable_id}}};
|
||||
flush_reqs.emplace_back(std::move(req));
|
||||
memtable_ids_to_wait.emplace_back(
|
||||
@@ -2379,14 +2414,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
for (const auto& req : flush_reqs) {
|
||||
assert(req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
ColumnFamilyData* loop_cfd =
|
||||
req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||
bool already_queued_for_flush = loop_cfd->queued_for_flush();
|
||||
bool flush_req_enqueued = EnqueuePendingFlush(req);
|
||||
if (already_queued_for_flush || flush_req_enqueued) {
|
||||
loop_cfd->SetFlushSkipReschedule();
|
||||
}
|
||||
SchedulePendingFlush(req);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -2398,8 +2426,6 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotifyOnManualFlushScheduled({cfd}, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:AfterScheduleFlush");
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
@@ -2413,8 +2439,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
s = WaitForFlushMemTables(
|
||||
cfds, flush_memtable_ids,
|
||||
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */,
|
||||
flush_reason);
|
||||
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */);
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
for (auto* tmp_cfd : cfds) {
|
||||
tmp_cfd->UnrefAndTryDelete();
|
||||
@@ -2498,7 +2523,7 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
WaitForPendingWrites();
|
||||
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds, candidate_cfds, flush_reason);
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds, candidate_cfds);
|
||||
|
||||
// Unref the newly generated candidate cfds (when not provided) in
|
||||
// `candidate_cfds`
|
||||
@@ -2509,8 +2534,7 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
|
||||
for (auto cfd : cfds) {
|
||||
if (cfd->mem()->IsEmpty() && cached_recoverable_state_empty_.load() &&
|
||||
!IsRecoveryFlush(flush_reason)) {
|
||||
if (cfd->mem()->IsEmpty() && cached_recoverable_state_empty_.load()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
@@ -2535,7 +2559,7 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
}
|
||||
GenerateFlushRequest(cfds, flush_reason, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
@@ -2546,7 +2570,6 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
}
|
||||
}
|
||||
NotifyOnManualFlushScheduled(cfds, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
@@ -2556,8 +2579,7 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
s = WaitForFlushMemTables(
|
||||
cfds, flush_memtable_ids,
|
||||
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */,
|
||||
flush_reason);
|
||||
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */);
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
for (auto* cfd : cfds) {
|
||||
cfd->UnrefAndTryDelete();
|
||||
@@ -2591,7 +2613,7 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, flush_reason, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
flush_memtable_ids.push_back(iter.second);
|
||||
}
|
||||
@@ -2605,9 +2627,7 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
|
||||
flush_reason,
|
||||
{{cfd,
|
||||
std::numeric_limits<uint64_t>::max() /* max_mem_id_to_persist */}}};
|
||||
if (EnqueuePendingFlush(flush_req)) {
|
||||
cfd->SetFlushSkipReschedule();
|
||||
};
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -2620,7 +2640,7 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
|
||||
flush_memtable_id_ptrs.push_back(&flush_memtable_id);
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_id_ptrs,
|
||||
true /* resuming_from_bg_err */, flush_reason);
|
||||
true /* resuming_from_bg_err */);
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
@@ -2695,13 +2715,13 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
// mode due to pending compaction bytes, but that's less common
|
||||
// No extra immutable Memtable will be created if the current Memtable is
|
||||
// empty.
|
||||
write_stall_condition =
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
|
||||
*cfd->ioptions())
|
||||
.first;
|
||||
int mem_to_flush = cfd->mem()->IsEmpty() ? 0 : 1;
|
||||
write_stall_condition = ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->imm()->NumNotFlushed() + mem_to_flush,
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(),
|
||||
mutable_cf_options, *cfd->ioptions())
|
||||
.first;
|
||||
} while (write_stall_condition != WriteStallCondition::kNormal);
|
||||
}
|
||||
return Status::OK();
|
||||
@@ -2720,7 +2740,7 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
Status DBImpl::WaitForFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const uint64_t*>& flush_memtable_ids,
|
||||
bool resuming_from_bg_err, std::optional<FlushReason> flush_reason) {
|
||||
bool resuming_from_bg_err) {
|
||||
int num = static_cast<int>(cfds.size());
|
||||
// Wait until the compaction completes
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -2758,15 +2778,7 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
(flush_memtable_ids[i] != nullptr &&
|
||||
cfds[i]->imm()->GetEarliestMemTableID() >
|
||||
*flush_memtable_ids[i])) {
|
||||
// Make file ingestion's flush wait until SuperVersion is also updated
|
||||
// since after flush, it does range overlapping check and file level
|
||||
// assignment with the current SuperVersion.
|
||||
if (!flush_reason.has_value() ||
|
||||
flush_reason.value() != FlushReason::kExternalFileIngestion ||
|
||||
cfds[i]->GetSuperVersion()->imm->GetID() ==
|
||||
cfds[i]->imm()->current()->GetID()) {
|
||||
++num_finished;
|
||||
}
|
||||
++num_finished;
|
||||
}
|
||||
}
|
||||
if (1 == num_dropped && 1 == num) {
|
||||
@@ -2966,7 +2978,6 @@ void DBImpl::AddToCompactionQueue(ColumnFamilyData* cfd) {
|
||||
cfd->Ref();
|
||||
compaction_queue_.push_back(cfd);
|
||||
cfd->set_queued_for_compaction(true);
|
||||
++unscheduled_compactions_;
|
||||
}
|
||||
|
||||
ColumnFamilyData* DBImpl::PopFirstFromCompactionQueue() {
|
||||
@@ -3022,14 +3033,13 @@ ColumnFamilyData* DBImpl::PickCompactionFromQueue(
|
||||
return cfd;
|
||||
}
|
||||
|
||||
bool DBImpl::EnqueuePendingFlush(const FlushRequest& flush_req) {
|
||||
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
|
||||
mutex_.AssertHeld();
|
||||
bool enqueued = false;
|
||||
if (reject_new_background_jobs_) {
|
||||
return enqueued;
|
||||
return;
|
||||
}
|
||||
if (flush_req.cfd_to_max_mem_id_to_persist.empty()) {
|
||||
return enqueued;
|
||||
return;
|
||||
}
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
// For the non-atomic flush case, we never schedule multiple column
|
||||
@@ -3044,7 +3054,6 @@ bool DBImpl::EnqueuePendingFlush(const FlushRequest& flush_req) {
|
||||
cfd->set_queued_for_flush(true);
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
enqueued = true;
|
||||
}
|
||||
} else {
|
||||
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
@@ -3053,20 +3062,19 @@ bool DBImpl::EnqueuePendingFlush(const FlushRequest& flush_req) {
|
||||
}
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
enqueued = true;
|
||||
}
|
||||
return enqueued;
|
||||
}
|
||||
|
||||
void DBImpl::EnqueuePendingCompaction(ColumnFamilyData* cfd) {
|
||||
void DBImpl::SchedulePendingCompaction(ColumnFamilyData* cfd) {
|
||||
mutex_.AssertHeld();
|
||||
if (reject_new_background_jobs_) {
|
||||
return;
|
||||
}
|
||||
if (!cfd->queued_for_compaction() && cfd->NeedsCompaction()) {
|
||||
TEST_SYNC_POINT_CALLBACK("EnqueuePendingCompaction::cfd",
|
||||
TEST_SYNC_POINT_CALLBACK("SchedulePendingCompaction::cfd",
|
||||
static_cast<void*>(cfd));
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3234,7 +3242,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
#ifndef NDEBUG
|
||||
flush_req.reschedule_count += 1;
|
||||
#endif /* !NDEBUG */
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
*reason = flush_reason;
|
||||
*flush_rescheduled_to_retain_udt = true;
|
||||
return Status::TryAgain();
|
||||
@@ -3557,14 +3565,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
is_manual && manual_compaction->disallow_trivial_move;
|
||||
|
||||
CompactionJobStats compaction_job_stats;
|
||||
// Set is_remote_compaction to true on CompactionBegin Event if
|
||||
// compaction_service is set except for trivial moves. We do not know whether
|
||||
// remote compaction will actually be successfully scheduled, or fall back to
|
||||
// local at this time. CompactionCompleted event will tell the truth where
|
||||
// the compaction actually happened.
|
||||
compaction_job_stats.is_remote_compaction =
|
||||
immutable_db_options().compaction_service != nullptr;
|
||||
|
||||
Status status;
|
||||
if (!error_handler_.IsBGWorkStopped()) {
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
@@ -3685,20 +3685,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// compaction is not necessary. Need to make sure mutex is held
|
||||
// until we make a copy in the following code
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():BeforePickCompaction");
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
// This info is not useful for other scenarios, so save querying existing
|
||||
// snapshots for those cases.
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal &&
|
||||
cfd->user_comparator()->timestamp_size() == 0) {
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot,
|
||||
&snapshot_checker);
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
}
|
||||
c.reset(cfd->PickCompaction(*mutable_cf_options, mutable_db_options_,
|
||||
snapshot_seqs, snapshot_checker, log_buffer));
|
||||
log_buffer));
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():AfterPickCompaction");
|
||||
|
||||
if (c != nullptr) {
|
||||
@@ -3714,6 +3702,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
->ComputeCompactionScore(*(c->immutable_options()),
|
||||
*(c->mutable_cf_options()));
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
|
||||
c.reset();
|
||||
// Don't need to sleep here, because BackgroundCallCompaction
|
||||
@@ -3742,6 +3731,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
if (cfd->NeedsCompaction()) {
|
||||
// Yes, we need more compactions!
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
}
|
||||
@@ -3802,8 +3792,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
|
||||
|
||||
compaction_job_stats.num_input_files = c->num_input_files(0);
|
||||
// Trivial moves do not get compacted remotely
|
||||
compaction_job_stats.is_remote_compaction = false;
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
@@ -3939,12 +3927,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&bg_bottom_compaction_scheduled_);
|
||||
compaction_job.Prepare();
|
||||
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> min_options_file_number_elem;
|
||||
if (immutable_db_options().compaction_service != nullptr) {
|
||||
min_options_file_number_elem.reset(
|
||||
new std::list<uint64_t>::iterator(CaptureOptionsFileNumber()));
|
||||
}
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
mutex_.Unlock();
|
||||
@@ -3954,11 +3936,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
compaction_job.Run().PermitUncheckedError();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:NonTrivial:AfterRun");
|
||||
mutex_.Lock();
|
||||
|
||||
if (immutable_db_options().compaction_service != nullptr) {
|
||||
ReleaseOptionsFileNumber(min_options_file_number_elem);
|
||||
}
|
||||
|
||||
status =
|
||||
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
|
||||
io_s = compaction_job.io_status();
|
||||
@@ -3986,10 +3963,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// Sanity checking that compaction files are freed.
|
||||
for (size_t i = 0; i < c->num_input_levels(); i++) {
|
||||
for (size_t j = 0; j < c->inputs(i)->size(); j++) {
|
||||
// When status is not OK, compaction's result installation failed and
|
||||
// no new Version installed. The files could have been released and
|
||||
// picked up again by other compaction attempts.
|
||||
assert(!c->input(i, j)->being_compacted || !status.ok());
|
||||
assert(!c->input(i, j)->being_compacted);
|
||||
}
|
||||
}
|
||||
std::unordered_set<Compaction*>* cip = c->column_family_data()
|
||||
@@ -4047,6 +4021,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
*(c->mutable_cf_options()));
|
||||
if (!cfd->queued_for_compaction()) {
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4215,7 +4190,7 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
compaction_job_info->base_input_level = c->start_level();
|
||||
compaction_job_info->output_level = c->output_level();
|
||||
compaction_job_info->stats = compaction_job_stats;
|
||||
const auto& input_table_properties = c->GetOrInitInputTableProperties();
|
||||
const auto& input_table_properties = c->GetInputTableProperties();
|
||||
const auto& output_table_properties = c->GetOutputTableProperties();
|
||||
compaction_job_info->table_properties.insert(input_table_properties.begin(),
|
||||
input_table_properties.end());
|
||||
@@ -4308,23 +4283,17 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
// newer snapshot created and released frequently, the compaction will be
|
||||
// triggered soon anyway.
|
||||
bottommost_files_mark_threshold_ = kMaxSequenceNumber;
|
||||
standalone_range_deletion_files_mark_threshold_ = kMaxSequenceNumber;
|
||||
for (auto* my_cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!my_cfd->ioptions()->allow_ingest_behind) {
|
||||
bottommost_files_mark_threshold_ = std::min(
|
||||
bottommost_files_mark_threshold_,
|
||||
my_cfd->current()->storage_info()->bottommost_files_mark_threshold());
|
||||
}
|
||||
standalone_range_deletion_files_mark_threshold_ =
|
||||
std::min(standalone_range_deletion_files_mark_threshold_,
|
||||
cfd->current()
|
||||
->storage_info()
|
||||
->standalone_range_tombstone_files_mark_threshold());
|
||||
}
|
||||
|
||||
// Whenever we install new SuperVersion, we might need to issue new flushes or
|
||||
// compactions.
|
||||
EnqueuePendingCompaction(cfd);
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
// Update max_total_in_memory_state_
|
||||
|
||||
+16
-76
@@ -9,7 +9,6 @@
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "db/blob/blob_file_cache.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
@@ -200,11 +199,6 @@ Status DBImpl::TEST_GetBGError() {
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
bool DBImpl::TEST_IsRecoveryInProgress() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return error_handler_.IsRecoveryInProgress();
|
||||
}
|
||||
|
||||
void DBImpl::TEST_LockMutex() { mutex_.Lock(); }
|
||||
|
||||
void DBImpl::TEST_UnlockMutex() { mutex_.Unlock(); }
|
||||
@@ -233,16 +227,23 @@ uint64_t DBImpl::TEST_LogfileNumber() {
|
||||
return logfile_number_;
|
||||
}
|
||||
|
||||
void DBImpl::TEST_GetAllBlockCaches(
|
||||
std::unordered_set<const Cache*>* cache_set) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (const auto bbto =
|
||||
cfd->GetCurrentMutableCFOptions()
|
||||
->table_factory->GetOptions<BlockBasedTableOptions>()) {
|
||||
cache_set->insert(bbto->block_cache.get());
|
||||
Status DBImpl::TEST_GetAllImmutableCFOptions(
|
||||
std::unordered_map<std::string, const ImmutableCFOptions*>* iopts_map) {
|
||||
std::vector<std::string> cf_names;
|
||||
std::vector<const ImmutableCFOptions*> iopts;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
cf_names.push_back(cfd->GetName());
|
||||
iopts.push_back(cfd->ioptions());
|
||||
}
|
||||
}
|
||||
iopts_map->clear();
|
||||
for (size_t i = 0; i < cf_names.size(); ++i) {
|
||||
iopts_map->insert({cf_names[i], iopts[i]});
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_FindMinLogContainingOutstandingPrep() {
|
||||
@@ -258,7 +259,7 @@ size_t DBImpl::TEST_LogsWithPrepSize() {
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_FindMinPrepLogReferencedByMemTable() {
|
||||
autovector<ReadOnlyMemTable*> empty_list;
|
||||
autovector<MemTable*> empty_list;
|
||||
return FindMinPrepLogReferencedByMemTable(versions_.get(), empty_list);
|
||||
}
|
||||
|
||||
@@ -322,66 +323,5 @@ size_t DBImpl::TEST_EstimateInMemoryStatsHistorySize() const {
|
||||
InstrumentedMutexLock l(&const_cast<DBImpl*>(this)->stats_history_mutex_);
|
||||
return EstimateInMemoryStatsHistorySize();
|
||||
}
|
||||
|
||||
void DBImpl::TEST_VerifyNoObsoleteFilesCached(
|
||||
bool db_mutex_already_held) const {
|
||||
// This check is somewhat expensive and obscure to make a part of every
|
||||
// unit test in every build variety. Thus, we only enable it for ASAN builds.
|
||||
if (!kMustFreeHeapAllocations) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<InstrumentedMutexLock> l;
|
||||
if (db_mutex_already_held) {
|
||||
mutex_.AssertHeld();
|
||||
} else {
|
||||
l.emplace(&mutex_);
|
||||
}
|
||||
|
||||
if (!opened_successfully_) {
|
||||
// We don't need to pro-actively clean up open files during DB::Open()
|
||||
// if we know we are about to fail and clean up in Close().
|
||||
return;
|
||||
}
|
||||
if (disable_delete_obsolete_files_ > 0) {
|
||||
// For better or worse, DB::Close() is allowed with deletions disabled.
|
||||
// Since we generally associate clean-up of open files with deleting them,
|
||||
// we allow "obsolete" open files when deletions are disabled.
|
||||
return;
|
||||
}
|
||||
|
||||
// Live and "quarantined" files are allowed to be open in table cache
|
||||
std::set<uint64_t> live_and_quar_files;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
// Iterate over live versions
|
||||
Version* current = cfd->current();
|
||||
Version* ver = current;
|
||||
do {
|
||||
// Sneakily add both SST and blob files to the same list
|
||||
std::vector<uint64_t> live_files_vec;
|
||||
ver->AddLiveFiles(&live_files_vec, &live_files_vec);
|
||||
live_and_quar_files.insert(live_files_vec.begin(), live_files_vec.end());
|
||||
|
||||
ver = ver->Next();
|
||||
} while (ver != current);
|
||||
}
|
||||
{
|
||||
const auto& quar_files = error_handler_.GetFilesToQuarantine();
|
||||
live_and_quar_files.insert(quar_files.begin(), quar_files.end());
|
||||
}
|
||||
auto fn = [&live_and_quar_files](const Slice& key, Cache::ObjectPtr, size_t,
|
||||
const Cache::CacheItemHelper*) {
|
||||
// See TableCache and BlobFileCache
|
||||
assert(key.size() == sizeof(uint64_t));
|
||||
uint64_t file_number;
|
||||
GetUnaligned(reinterpret_cast<const uint64_t*>(key.data()), &file_number);
|
||||
// Assert file is in live/quarantined set
|
||||
assert(live_and_quar_files.find(file_number) != live_and_quar_files.end());
|
||||
};
|
||||
table_cache_->ApplyToAllEntries(fn, {});
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // NDEBUG
|
||||
@@ -47,7 +47,7 @@ Status DBImpl::SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
// compaction score
|
||||
vstorage->ComputeCompactionScore(*cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
EnqueuePendingCompaction(cfd);
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
return Status::OK();
|
||||
|
||||
+50
-137
@@ -43,14 +43,6 @@ uint64_t DBImpl::GetObsoleteSstFilesSize() {
|
||||
return versions_->GetObsoleteSstFilesSize();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinOptionsFileNumberToKeep() {
|
||||
mutex_.AssertHeld();
|
||||
if (!min_options_file_numbers_.empty()) {
|
||||
return *min_options_file_numbers_.begin();
|
||||
}
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
Status DBImpl::DisableFileDeletions() {
|
||||
Status s;
|
||||
int my_disable_delete_obsolete_files;
|
||||
@@ -155,7 +147,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// here but later find newer generated unfinalized files while scanning.
|
||||
job_context->min_pending_output = MinObsoleteSstNumberToKeep();
|
||||
job_context->files_to_quarantine = error_handler_.GetFilesToQuarantine();
|
||||
job_context->min_options_file_number = MinOptionsFileNumberToKeep();
|
||||
|
||||
// Get obsolete files. This function will also update the list of
|
||||
// pending files in VersionSet().
|
||||
@@ -321,26 +312,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// 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_.
|
||||
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();
|
||||
// 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();
|
||||
log.writer->PublishIfClosed();
|
||||
assert(&log == &logs_.front());
|
||||
log.FinishSync();
|
||||
log_sync_cv_.SignalAll();
|
||||
}
|
||||
logs_to_free_.push_back(log.ReleaseWriter());
|
||||
logs_.pop_front();
|
||||
}
|
||||
@@ -439,18 +410,12 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
state.manifest_delete_files.size());
|
||||
// We may ignore the dbname when generating the file names.
|
||||
for (auto& file : state.sst_delete_files) {
|
||||
auto* handle = file.metadata->table_reader_handle;
|
||||
if (file.only_delete_metadata) {
|
||||
if (handle) {
|
||||
// Simply release handle of file that is not being deleted
|
||||
table_cache_->Release(handle);
|
||||
}
|
||||
} else {
|
||||
// File is being deleted (actually obsolete)
|
||||
auto number = file.metadata->fd.GetNumber();
|
||||
candidate_files.emplace_back(MakeTableFileName(number), file.path);
|
||||
TableCache::ReleaseObsolete(table_cache_.get(), number, handle,
|
||||
file.uncache_aggressiveness);
|
||||
if (!file.only_delete_metadata) {
|
||||
candidate_files.emplace_back(
|
||||
MakeTableFileName(file.metadata->fd.GetNumber()), file.path);
|
||||
}
|
||||
if (file.metadata->table_reader_handle) {
|
||||
table_cache_->Release(file.metadata->table_reader_handle);
|
||||
}
|
||||
file.DeleteMetadata();
|
||||
}
|
||||
@@ -501,7 +466,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
dbname_);
|
||||
|
||||
// File numbers of most recent two OPTIONS file in candidate_files (found in
|
||||
// previous FindObsoleteFiles(full_scan=true))
|
||||
// previos FindObsoleteFiles(full_scan=true))
|
||||
// At this point, there must not be any duplicate file numbers in
|
||||
// candidate_files.
|
||||
uint64_t optsfile_num1 = std::numeric_limits<uint64_t>::min();
|
||||
@@ -522,16 +487,11 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
}
|
||||
}
|
||||
|
||||
// For remote compactions, we need to keep OPTIONS file that may get
|
||||
// referenced by the remote worker
|
||||
|
||||
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) {
|
||||
// TODO: maybe check the return value of Close.
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
auto s = w->Close({});
|
||||
auto s = w->Close(WriteOptions());
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
@@ -566,17 +526,9 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
case kTableFile:
|
||||
// If the second condition is not there, this makes
|
||||
// DontDeletePendingOutputs fail
|
||||
// FIXME: but should NOT keep if it came from sst_delete_files?
|
||||
keep = (sst_live_set.find(number) != sst_live_set.end()) ||
|
||||
number >= state.min_pending_output;
|
||||
if (!keep) {
|
||||
// NOTE: sometimes redundant (if came from sst_delete_files)
|
||||
// We don't know which column family is applicable here so we don't
|
||||
// know what uncache_aggressiveness would be used with
|
||||
// ReleaseObsolete(). Anyway, obsolete files ideally go into
|
||||
// sst_delete_files for better/quicker handling, and this is just a
|
||||
// backstop.
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
files_to_del.insert(number);
|
||||
}
|
||||
break;
|
||||
@@ -625,6 +577,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
std::string fname;
|
||||
std::string dir_to_sync;
|
||||
if (type == kTableFile) {
|
||||
// evict from cache
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
fname = MakeTableFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else if (type == kBlobFile) {
|
||||
@@ -738,46 +692,13 @@ void DBImpl::DeleteObsoleteFiles() {
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
VersionEdit GetDBRecoveryEditForObsoletingMemTables(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd,
|
||||
const autovector<VersionEdit*>& edit_list,
|
||||
const autovector<ReadOnlyMemTable*>& memtables,
|
||||
LogsWithPrepTracker* prep_tracker) {
|
||||
VersionEdit wal_deletion_edit;
|
||||
uint64_t min_wal_number_to_keep = 0;
|
||||
assert(edit_list.size() > 0);
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
// Note that if mempurge is successful, the edit_list will
|
||||
// not be applicable (contains info of new min_log number to keep,
|
||||
// and level 0 file path of SST file created during normal flush,
|
||||
// so both pieces of information are irrelevant after a successful
|
||||
// mempurge operation).
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, cfd, edit_list, memtables, prep_tracker);
|
||||
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// manifest entry for the last file flushed.
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfd, edit_list);
|
||||
}
|
||||
|
||||
wal_deletion_edit.SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
if (vset->db_options()->track_and_verify_wals_in_manifest) {
|
||||
if (min_wal_number_to_keep > vset->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
wal_deletion_edit.DeleteWalsBefore(min_wal_number_to_keep);
|
||||
}
|
||||
}
|
||||
return wal_deletion_edit;
|
||||
}
|
||||
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<ReadOnlyMemTable*>& memtables_to_flush) {
|
||||
VersionSet* vset, const autovector<MemTable*>& memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
// we must look through the memtables for two phase transactions
|
||||
// that have been committed but not yet flushed
|
||||
std::unordered_set<ReadOnlyMemTable*> memtables_to_flush_set(
|
||||
std::unordered_set<MemTable*> memtables_to_flush_set(
|
||||
memtables_to_flush.begin(), memtables_to_flush.end());
|
||||
for (auto loop_cfd : *vset->GetColumnFamilySet()) {
|
||||
if (loop_cfd->IsDropped()) {
|
||||
@@ -802,12 +723,12 @@ uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
}
|
||||
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<const autovector<ReadOnlyMemTable*>*>&
|
||||
memtables_to_flush) {
|
||||
VersionSet* vset,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
std::unordered_set<ReadOnlyMemTable*> memtables_to_flush_set;
|
||||
for (const autovector<ReadOnlyMemTable*>* memtables : memtables_to_flush) {
|
||||
std::unordered_set<MemTable*> memtables_to_flush_set;
|
||||
for (const autovector<MemTable*>* memtables : memtables_to_flush) {
|
||||
memtables_to_flush_set.insert(memtables->begin(), memtables->end());
|
||||
}
|
||||
for (auto loop_cfd : *vset->GetColumnFamilySet()) {
|
||||
@@ -899,7 +820,7 @@ uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list,
|
||||
const autovector<ReadOnlyMemTable*>& memtables_to_flush,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker) {
|
||||
assert(vset != nullptr);
|
||||
assert(prep_tracker != nullptr);
|
||||
@@ -940,7 +861,7 @@ uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists,
|
||||
const autovector<const autovector<ReadOnlyMemTable*>*>& memtables_to_flush,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker) {
|
||||
assert(vset != nullptr);
|
||||
assert(prep_tracker != nullptr);
|
||||
@@ -970,65 +891,57 @@ uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
}
|
||||
|
||||
void DBImpl::SetDBId(std::string&& id, bool read_only,
|
||||
VersionEdit* version_edit) {
|
||||
RecoveryContext* recovery_ctx) {
|
||||
assert(db_id_.empty());
|
||||
assert(!id.empty());
|
||||
db_id_ = std::move(id);
|
||||
if (!read_only && version_edit) {
|
||||
assert(version_edit != nullptr);
|
||||
if (!read_only && immutable_db_options_.write_dbid_to_manifest) {
|
||||
assert(recovery_ctx != nullptr);
|
||||
assert(versions_->GetColumnFamilySet() != nullptr);
|
||||
version_edit->SetDBId(db_id_);
|
||||
VersionEdit edit;
|
||||
edit.SetDBId(db_id_);
|
||||
versions_->db_id_ = db_id_;
|
||||
recovery_ctx->UpdateVersionEdits(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), edit);
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::SetupDBId(const WriteOptions& write_options, bool read_only,
|
||||
bool is_new_db, bool is_retry,
|
||||
VersionEdit* version_edit) {
|
||||
RecoveryContext* recovery_ctx) {
|
||||
Status s;
|
||||
if (!is_new_db) {
|
||||
// Check for the IDENTITY file and create it if not there or
|
||||
// broken or not matching manifest
|
||||
std::string db_id_in_file;
|
||||
s = fs_->FileExists(IdentityFileName(dbname_), IOOptions(), nullptr);
|
||||
if (s.ok()) {
|
||||
IOOptions opts;
|
||||
if (is_retry) {
|
||||
opts.verify_and_reconstruct_read = true;
|
||||
}
|
||||
s = GetDbIdentityFromIdentityFile(opts, &db_id_in_file);
|
||||
if (s.ok() && !db_id_in_file.empty()) {
|
||||
if (db_id_.empty()) {
|
||||
// Loaded from file and wasn't already known from manifest
|
||||
SetDBId(std::move(db_id_in_file), read_only, version_edit);
|
||||
return s;
|
||||
} else if (db_id_ == db_id_in_file) {
|
||||
// Loaded from file and matches manifest
|
||||
return s;
|
||||
}
|
||||
// Check for the IDENTITY file and create it if not there or
|
||||
// broken or not matching manifest
|
||||
std::string db_id_in_file;
|
||||
s = fs_->FileExists(IdentityFileName(dbname_), IOOptions(), nullptr);
|
||||
if (s.ok()) {
|
||||
s = GetDbIdentityFromIdentityFile(&db_id_in_file);
|
||||
if (s.ok() && !db_id_in_file.empty()) {
|
||||
if (db_id_.empty()) {
|
||||
// Loaded from file and wasn't already known from manifest
|
||||
SetDBId(std::move(db_id_in_file), read_only, recovery_ctx);
|
||||
return s;
|
||||
} else if (db_id_ == db_id_in_file) {
|
||||
// Loaded from file and matches manifest
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (s.IsNotFound()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (s.IsNotFound()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
// Otherwise IDENTITY file is missing or no good.
|
||||
// Generate new id if needed
|
||||
if (db_id_.empty()) {
|
||||
SetDBId(env_->GenerateUniqueId(), read_only, version_edit);
|
||||
SetDBId(env_->GenerateUniqueId(), read_only, recovery_ctx);
|
||||
}
|
||||
// Persist it to IDENTITY file if allowed
|
||||
if (!read_only && immutable_db_options_.write_identity_file) {
|
||||
s = SetIdentityFile(write_options, env_, dbname_,
|
||||
immutable_db_options_.metadata_write_temperature,
|
||||
db_id_);
|
||||
if (!read_only) {
|
||||
s = SetIdentityFile(write_options, env_, dbname_, db_id_);
|
||||
}
|
||||
// NOTE: an obsolete IDENTITY file with write_identity_file=false is handled
|
||||
// elsewhere, so that it's only deleted after successful recovery
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include "db/db_impl/db_impl_follower.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
|
||||
+83
-98
@@ -289,25 +289,24 @@ Status DBImpl::ValidateOptions(const DBOptions& db_options) {
|
||||
"start_time and end_time cannot be the same");
|
||||
}
|
||||
}
|
||||
|
||||
if (!db_options.write_dbid_to_manifest && !db_options.write_identity_file) {
|
||||
return Status::InvalidArgument(
|
||||
"write_dbid_to_manifest and write_identity_file cannot both be false");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
VersionEdit new_db_edit;
|
||||
VersionEdit new_db;
|
||||
const WriteOptions write_options(Env::IOActivity::kDBOpen);
|
||||
Status s = SetupDBId(write_options, /*read_only=*/false, /*is_new_db=*/true,
|
||||
/*is_retry=*/false, &new_db_edit);
|
||||
Status s = SetIdentityFile(write_options, env_, dbname_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
new_db_edit.SetLogNumber(0);
|
||||
new_db_edit.SetNextFile(2);
|
||||
new_db_edit.SetLastSequence(0);
|
||||
if (immutable_db_options_.write_dbid_to_manifest) {
|
||||
std::string temp_db_id;
|
||||
GetDbIdentityFromIdentityFile(&temp_db_id);
|
||||
new_db.SetDBId(temp_db_id);
|
||||
}
|
||||
new_db.SetLogNumber(0);
|
||||
new_db.SetNextFile(2);
|
||||
new_db.SetLastSequence(0);
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Creating manifest 1 \n");
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
@@ -317,12 +316,6 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
}
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
FileOptions file_options = fs_->OptimizeForManifestWrite(file_options_);
|
||||
// DB option takes precedence when not kUnknown
|
||||
if (immutable_db_options_.metadata_write_temperature !=
|
||||
Temperature::kUnknown) {
|
||||
file_options.temperature =
|
||||
immutable_db_options_.metadata_write_temperature;
|
||||
}
|
||||
s = NewWritableFile(fs_.get(), manifest, &file, file_options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -339,7 +332,7 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
new_db_edit.EncodeTo(&record);
|
||||
new_db.EncodeTo(&record);
|
||||
s = log.AddRecord(write_options, record);
|
||||
if (s.ok()) {
|
||||
s = SyncManifest(&immutable_db_options_, write_options, log.file());
|
||||
@@ -348,7 +341,6 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
if (s.ok()) {
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(write_options, fs_.get(), dbname_, 1,
|
||||
immutable_db_options_.metadata_write_temperature,
|
||||
directories_.GetDbDir());
|
||||
if (new_filenames) {
|
||||
new_filenames->emplace_back(
|
||||
@@ -525,7 +517,7 @@ Status DBImpl::Recover(
|
||||
}
|
||||
assert(s.ok());
|
||||
}
|
||||
assert(is_new_db || db_id_.empty());
|
||||
assert(db_id_.empty());
|
||||
Status s;
|
||||
bool missing_table_file = false;
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
@@ -535,12 +527,6 @@ Status DBImpl::Recover(
|
||||
/*no_error_if_files_missing=*/false, is_retry,
|
||||
&desc_status);
|
||||
desc_status.PermitUncheckedError();
|
||||
if (is_retry) {
|
||||
RecordTick(stats_, FILE_READ_CORRUPTION_RETRY_COUNT);
|
||||
if (desc_status.ok()) {
|
||||
RecordTick(stats_, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
|
||||
}
|
||||
}
|
||||
if (can_retry) {
|
||||
// If we're opening for the first time and the failure is likely due to
|
||||
// a corrupt MANIFEST file (could result in either the log::Reader
|
||||
@@ -575,7 +561,6 @@ Status DBImpl::Recover(
|
||||
}
|
||||
if (s.ok() && !read_only) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
auto& moptions = *cfd->GetLatestMutableCFOptions();
|
||||
// Try to trivially move files down the LSM tree to start from bottommost
|
||||
// level when level_compaction_dynamic_level_bytes is enabled. This should
|
||||
// only be useful when user is migrating to turning on this option.
|
||||
@@ -593,14 +578,14 @@ Status DBImpl::Recover(
|
||||
if (cfd->ioptions()->compaction_style ==
|
||||
CompactionStyle::kCompactionStyleLevel &&
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
!moptions.disable_auto_compactions) {
|
||||
!cfd->GetLatestMutableCFOptions()->disable_auto_compactions) {
|
||||
int to_level = cfd->ioptions()->num_levels - 1;
|
||||
// last level is reserved
|
||||
// allow_ingest_behind does not support Level Compaction,
|
||||
// and per_key_placement can have infinite compaction loop for Level
|
||||
// Compaction. Adjust to_level here just to be safe.
|
||||
if (cfd->ioptions()->allow_ingest_behind ||
|
||||
moptions.preclude_last_level_data_seconds > 0) {
|
||||
cfd->ioptions()->preclude_last_level_data_seconds > 0) {
|
||||
to_level -= 1;
|
||||
}
|
||||
// Whether this column family has a level trivially moved
|
||||
@@ -652,7 +637,7 @@ Status DBImpl::Recover(
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction,
|
||||
f->temperature, // this can be different from
|
||||
// `last_level_temperature`
|
||||
// `last_level_temperature`
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number,
|
||||
f->file_checksum, f->file_checksum_func_name,
|
||||
@@ -672,17 +657,7 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_new_db) {
|
||||
// Already set up DB ID in NewDB
|
||||
} else if (immutable_db_options_.write_dbid_to_manifest && recovery_ctx) {
|
||||
VersionEdit edit;
|
||||
s = SetupDBId(write_options, read_only, is_new_db, is_retry, &edit);
|
||||
recovery_ctx->UpdateVersionEdits(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), edit);
|
||||
} else {
|
||||
s = SetupDBId(write_options, read_only, is_new_db, is_retry, nullptr);
|
||||
}
|
||||
assert(!s.ok() || !db_id_.empty());
|
||||
s = SetupDBId(write_options, read_only, recovery_ctx);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "DB ID: %s\n", db_id_.c_str());
|
||||
if (s.ok() && !read_only) {
|
||||
s = MaybeUpdateNextFileNumber(recovery_ctx);
|
||||
@@ -1275,8 +1250,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
reader.GetRecordedTimestampSize();
|
||||
status = HandleWriteBatchTimestampSizeDifference(
|
||||
&batch, running_ts_sz, record_ts_sz,
|
||||
TimestampSizeConsistencyMode::kReconcileInconsistency, seq_per_batch_,
|
||||
batch_per_txn_, &new_batch);
|
||||
TimestampSizeConsistencyMode::kReconcileInconsistency, &new_batch);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
@@ -1669,19 +1643,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
Arena arena;
|
||||
Status s;
|
||||
TableProperties table_properties;
|
||||
const auto* ucmp = cfd->internal_comparator().user_comparator();
|
||||
assert(ucmp);
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool logical_strip_timestamp =
|
||||
ts_sz > 0 && !cfd->ioptions()->persist_user_defined_timestamps;
|
||||
{
|
||||
ScopedArenaPtr<InternalIterator> iter(
|
||||
logical_strip_timestamp
|
||||
? mem->NewTimestampStrippingIterator(
|
||||
ro, /*seqno_to_time_mapping=*/nullptr, &arena,
|
||||
/*prefix_extractor=*/nullptr, ts_sz)
|
||||
: mem->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena,
|
||||
/*prefix_extractor=*/nullptr));
|
||||
mem->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena));
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
" Level-0 table #%" PRIu64 ": started",
|
||||
@@ -1700,15 +1664,12 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
meta.oldest_ancester_time = current_time;
|
||||
meta.epoch_number = cfd->NewEpochNumber();
|
||||
{
|
||||
auto write_hint =
|
||||
cfd->current()->storage_info()->CalculateSSTWriteHint(/*level=*/0);
|
||||
auto write_hint = cfd->CalculateSSTWriteHint(0);
|
||||
mutex_.Unlock();
|
||||
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
SequenceNumber earliest_snapshot =
|
||||
(snapshot_seqs.empty() ? kMaxSequenceNumber : snapshot_seqs.at(0));
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
@@ -1716,14 +1677,11 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters;
|
||||
auto range_del_iter =
|
||||
logical_strip_timestamp
|
||||
? mem->NewTimestampStrippingRangeTombstoneIterator(
|
||||
ro, kMaxSequenceNumber, ts_sz)
|
||||
// This is called during recovery, where a live memtable is
|
||||
// flushed directly. In this case, no fragmented tombstone list is
|
||||
// cached in this memtable yet.
|
||||
: mem->NewRangeTombstoneIterator(ro, kMaxSequenceNumber,
|
||||
false /* immutable_memtable */);
|
||||
// This is called during recovery, where a live memtable is flushed
|
||||
// directly. In this case, no fragmented tombstone list is cached in
|
||||
// this memtable yet.
|
||||
mem->NewRangeTombstoneIterator(ro, kMaxSequenceNumber,
|
||||
false /* immutable_memtable */);
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
@@ -1731,32 +1689,29 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
IOStatus io_s;
|
||||
const ReadOptions read_option(Env::IOActivity::kDBOpen);
|
||||
const WriteOptions write_option(Env::IO_HIGH, Env::IOActivity::kDBOpen);
|
||||
|
||||
TableBuilderOptions tboptions(
|
||||
*cfd->ioptions(), mutable_cf_options, read_option, write_option,
|
||||
cfd->internal_comparator(), cfd->internal_tbl_prop_coll_factories(),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
mutable_cf_options.compression_opts, cfd->GetID(), cfd->GetName(),
|
||||
0 /* level */, current_time /* newest_key_time */,
|
||||
false /* is_bottommost */, TableFileCreationReason::kRecovery,
|
||||
0 /* oldest_key_time */, 0 /* file_creation_time */, db_id_,
|
||||
db_session_id_, 0 /* target_file_size */, meta.fd.GetNumber(),
|
||||
kMaxSequenceNumber);
|
||||
0 /* level */, false /* is_bottommost */,
|
||||
TableFileCreationReason::kRecovery, 0 /* oldest_key_time */,
|
||||
0 /* file_creation_time */, db_id_, db_session_id_,
|
||||
0 /* target_file_size */, meta.fd.GetNumber());
|
||||
Version* version = cfd->current();
|
||||
version->Ref();
|
||||
uint64_t num_input_entries = 0;
|
||||
s = BuildTable(dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(),
|
||||
iter.get(), std::move(range_del_iters), &meta,
|
||||
&blob_file_additions, snapshot_seqs, earliest_snapshot,
|
||||
earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks,
|
||||
cfd->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_,
|
||||
job_id, nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
&num_input_entries);
|
||||
s = BuildTable(
|
||||
dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::move(range_del_iters), &meta, &blob_file_additions,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks, cfd->internal_stats(), &io_s,
|
||||
io_tracer_, BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_, job_id,
|
||||
nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
&num_input_entries);
|
||||
version->Unref();
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
@@ -1771,7 +1726,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
s = io_s;
|
||||
}
|
||||
|
||||
uint64_t total_num_entries = mem->NumEntries();
|
||||
uint64_t total_num_entries = mem->num_entries();
|
||||
if (s.ok() && total_num_entries != num_input_entries) {
|
||||
std::string msg = "Expected " + std::to_string(total_num_entries) +
|
||||
" entries in memtable, but read " +
|
||||
@@ -1810,7 +1765,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
|
||||
// For UDT in memtable only feature, move up the cutoff timestamp whenever
|
||||
// a flush happens.
|
||||
if (logical_strip_timestamp) {
|
||||
const Comparator* ucmp = cfd->user_comparator();
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
if (ts_sz > 0 && !cfd->ioptions()->persist_user_defined_timestamps) {
|
||||
Slice mem_newest_udt = mem->GetNewestUDT();
|
||||
std::string full_history_ts_low = cfd->GetFullHistoryTsLow();
|
||||
if (full_history_ts_low.empty() ||
|
||||
@@ -1966,10 +1923,6 @@ IOStatus DBImpl::CreateWAL(const WriteOptions& write_options,
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_);
|
||||
FileOptions opt_file_options =
|
||||
fs_->OptimizeForLogWrite(file_options_, db_options);
|
||||
// DB option takes precedence when not kUnknown
|
||||
if (immutable_db_options_.wal_write_temperature != Temperature::kUnknown) {
|
||||
opt_file_options.temperature = immutable_db_options_.wal_write_temperature;
|
||||
}
|
||||
std::string wal_dir = immutable_db_options_.GetWalDir();
|
||||
std::string log_fname = LogFileName(wal_dir, log_file_num);
|
||||
|
||||
@@ -2009,7 +1962,46 @@ IOStatus DBImpl::CreateWAL(const WriteOptions& write_options,
|
||||
|
||||
void DBImpl::TrackExistingDataFiles(
|
||||
const std::vector<std::string>& existing_data_files) {
|
||||
TrackOrUntrackFiles(existing_data_files, /*track=*/true);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
assert(sfm);
|
||||
std::vector<ColumnFamilyMetaData> metadata;
|
||||
GetAllColumnFamilyMetaData(&metadata);
|
||||
|
||||
std::unordered_set<std::string> referenced_files;
|
||||
for (const auto& md : metadata) {
|
||||
for (const auto& lmd : md.levels) {
|
||||
for (const auto& fmd : lmd.files) {
|
||||
// We're assuming that each sst file name exists in at most one of
|
||||
// the paths.
|
||||
std::string file_path =
|
||||
fmd.directory + kFilePathSeparator + fmd.relative_filename;
|
||||
sfm->OnAddFile(file_path, fmd.size).PermitUncheckedError();
|
||||
referenced_files.insert(file_path);
|
||||
}
|
||||
}
|
||||
for (const auto& bmd : md.blob_files) {
|
||||
std::string name = bmd.blob_file_name;
|
||||
// The BlobMetaData.blob_file_name may start with "/".
|
||||
if (!name.empty() && name[0] == kFilePathSeparator) {
|
||||
name = name.substr(1);
|
||||
}
|
||||
// We're assuming that each blob file name exists in at most one of
|
||||
// the paths.
|
||||
std::string file_path = bmd.blob_file_path + kFilePathSeparator + name;
|
||||
sfm->OnAddFile(file_path, bmd.blob_file_size).PermitUncheckedError();
|
||||
referenced_files.insert(file_path);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& file_path : existing_data_files) {
|
||||
if (referenced_files.find(file_path) != referenced_files.end()) {
|
||||
continue;
|
||||
}
|
||||
// There shouldn't be any duplicated files. In case there is, SstFileManager
|
||||
// will take care of deduping it.
|
||||
sfm->OnAddFile(file_path).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
@@ -2153,13 +2145,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
s = impl->LogAndApplyForRecovery(recovery_ctx);
|
||||
}
|
||||
|
||||
if (s.ok() && !impl->immutable_db_options_.write_identity_file) {
|
||||
// On successful recovery, delete an obsolete IDENTITY file to avoid DB ID
|
||||
// inconsistency
|
||||
impl->env_->DeleteFile(IdentityFileName(impl->dbname_))
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
if (s.ok() && impl->immutable_db_options_.persist_stats_to_disk) {
|
||||
impl->mutex_.AssertHeld();
|
||||
s = impl->InitPersistStatsColumnFamily();
|
||||
|
||||
@@ -265,8 +265,7 @@ Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
|
||||
const std::shared_ptr<FileSystem>& fs = db_options.env->GetFileSystem();
|
||||
std::string manifest_path;
|
||||
uint64_t manifest_file_number;
|
||||
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), /*is_retry=*/false,
|
||||
&manifest_path,
|
||||
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), &manifest_path,
|
||||
&manifest_file_number);
|
||||
} else {
|
||||
// Historic behavior that doesn't necessarily make sense
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include "logging/auto_roll_logger.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/utilities/options_util.h"
|
||||
#include "rocksdb/configurable.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/write_batch_util.h"
|
||||
|
||||
@@ -233,8 +232,7 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
reader->GetRecordedTimestampSize();
|
||||
status = HandleWriteBatchTimestampSizeDifference(
|
||||
&batch, running_ts_sz, record_ts_sz,
|
||||
TimestampSizeConsistencyMode::kVerifyConsistency, seq_per_batch_,
|
||||
batch_per_txn_);
|
||||
TimestampSizeConsistencyMode::kVerifyConsistency);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -248,7 +246,9 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
if (cfd == nullptr) {
|
||||
continue;
|
||||
}
|
||||
cfds_changed->insert(cfd);
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
}
|
||||
const std::vector<FileMetaData*>& l0_files =
|
||||
cfd->current()->storage_info()->LevelFiles(0);
|
||||
SequenceNumber seq =
|
||||
@@ -938,105 +938,69 @@ Status DB::OpenAndCompact(
|
||||
const std::string& output_directory, const std::string& input,
|
||||
std::string* output,
|
||||
const CompactionServiceOptionsOverride& override_options) {
|
||||
// Check for cancellation
|
||||
if (options.canceled && options.canceled->load(std::memory_order_acquire)) {
|
||||
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
|
||||
// 1. Deserialize Compaction Input
|
||||
CompactionServiceInput compaction_input;
|
||||
Status s = CompactionServiceInput::Read(input, &compaction_input);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// 2. Load the options
|
||||
DBOptions db_options;
|
||||
ConfigOptions config_options;
|
||||
config_options.env = override_options.env;
|
||||
std::vector<ColumnFamilyDescriptor> all_column_families;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:0",
|
||||
&compaction_input.options_file_number);
|
||||
TEST_SYNC_POINT("DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:1");
|
||||
std::string options_file_name =
|
||||
OptionsFileName(name, compaction_input.options_file_number);
|
||||
|
||||
s = LoadOptionsFromFile(config_options, options_file_name, &db_options,
|
||||
&all_column_families);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
compaction_input.db_options.max_open_files = -1;
|
||||
compaction_input.db_options.compaction_service = nullptr;
|
||||
if (compaction_input.db_options.statistics) {
|
||||
compaction_input.db_options.statistics.reset();
|
||||
}
|
||||
|
||||
// 3. Override pointer configurations in DBOptions with
|
||||
// CompactionServiceOptionsOverride
|
||||
db_options.env = override_options.env;
|
||||
db_options.file_checksum_gen_factory =
|
||||
compaction_input.db_options.env = override_options.env;
|
||||
compaction_input.db_options.file_checksum_gen_factory =
|
||||
override_options.file_checksum_gen_factory;
|
||||
db_options.statistics = override_options.statistics;
|
||||
db_options.listeners = override_options.listeners;
|
||||
db_options.compaction_service = nullptr;
|
||||
// We will close the DB after the compaction anyway.
|
||||
// Open as many files as needed for the compaction.
|
||||
db_options.max_open_files = -1;
|
||||
compaction_input.db_options.statistics = override_options.statistics;
|
||||
compaction_input.column_family.options.comparator =
|
||||
override_options.comparator;
|
||||
compaction_input.column_family.options.merge_operator =
|
||||
override_options.merge_operator;
|
||||
compaction_input.column_family.options.compaction_filter =
|
||||
override_options.compaction_filter;
|
||||
compaction_input.column_family.options.compaction_filter_factory =
|
||||
override_options.compaction_filter_factory;
|
||||
compaction_input.column_family.options.prefix_extractor =
|
||||
override_options.prefix_extractor;
|
||||
compaction_input.column_family.options.table_factory =
|
||||
override_options.table_factory;
|
||||
compaction_input.column_family.options.sst_partitioner_factory =
|
||||
override_options.sst_partitioner_factory;
|
||||
compaction_input.column_family.options.table_properties_collector_factories =
|
||||
override_options.table_properties_collector_factories;
|
||||
compaction_input.db_options.listeners = override_options.listeners;
|
||||
|
||||
// 4. Filter CFs that are needed for OpenAndCompact()
|
||||
// We do not need to open all column families for the remote compaction.
|
||||
// Only open default CF + target CF. If target CF == default CF, we will open
|
||||
// just the default CF (Due to current limitation, DB cannot open without the
|
||||
// default CF)
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (auto& cf : all_column_families) {
|
||||
if (cf.name == compaction_input.cf_name) {
|
||||
cf.options.comparator = override_options.comparator;
|
||||
cf.options.merge_operator = override_options.merge_operator;
|
||||
cf.options.compaction_filter = override_options.compaction_filter;
|
||||
cf.options.compaction_filter_factory =
|
||||
override_options.compaction_filter_factory;
|
||||
cf.options.prefix_extractor = override_options.prefix_extractor;
|
||||
cf.options.table_factory = override_options.table_factory;
|
||||
cf.options.sst_partitioner_factory =
|
||||
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);
|
||||
}
|
||||
column_families.push_back(compaction_input.column_family);
|
||||
// TODO: we have to open default CF, because of an implementation limitation,
|
||||
// currently we just use the same CF option from input, which is not collect
|
||||
// and open may fail.
|
||||
if (compaction_input.column_family.name != kDefaultColumnFamilyName) {
|
||||
column_families.emplace_back(kDefaultColumnFamilyName,
|
||||
compaction_input.column_family.options);
|
||||
}
|
||||
|
||||
// 5. Open db As Secondary
|
||||
DB* db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
s = DB::OpenAsSecondary(db_options, name, output_directory, column_families,
|
||||
&handles, &db);
|
||||
|
||||
s = DB::OpenAsSecondary(compaction_input.db_options, name, output_directory,
|
||||
column_families, &handles, &db);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
assert(db);
|
||||
|
||||
// 6. Find the handle of the Column Family that this will compact
|
||||
ColumnFamilyHandle* cfh = nullptr;
|
||||
for (auto* handle : handles) {
|
||||
if (compaction_input.cf_name == handle->GetName()) {
|
||||
cfh = handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(cfh);
|
||||
|
||||
// 7. Run the compaction without installation.
|
||||
// Output will be stored in the directory specified by output_directory
|
||||
CompactionServiceResult compaction_result;
|
||||
DBImplSecondary* db_secondary = static_cast_with_check<DBImplSecondary>(db);
|
||||
s = db_secondary->CompactWithoutInstallation(options, cfh, compaction_input,
|
||||
&compaction_result);
|
||||
assert(handles.size() > 0);
|
||||
s = db_secondary->CompactWithoutInstallation(
|
||||
options, handles[0], compaction_input, &compaction_result);
|
||||
|
||||
// 8. Serialize the result
|
||||
Status serialization_status = compaction_result.Write(output);
|
||||
|
||||
// 9. Close the db and return
|
||||
for (auto& handle : handles) {
|
||||
delete handle;
|
||||
}
|
||||
|
||||
+118
-179
@@ -155,36 +155,21 @@ 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);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb) {
|
||||
Status s;
|
||||
if (write_options.protection_bytes_per_key > 0) {
|
||||
s = WriteBatchInternal::UpdateProtectionInfo(
|
||||
my_batch, write_options.protection_bytes_per_key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = WriteImpl(write_options, my_batch, callback, user_write_cb);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch,
|
||||
UserWriteCallback* user_write_cb) {
|
||||
WriteCallback* callback) {
|
||||
Status s;
|
||||
if (write_options.protection_bytes_per_key > 0) {
|
||||
s = WriteBatchInternal::UpdateProtectionInfo(
|
||||
my_batch, write_options.protection_bytes_per_key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = WriteImpl(write_options, my_batch, /*callback=*/nullptr, user_write_cb);
|
||||
s = WriteImpl(write_options, my_batch, callback, nullptr);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -194,9 +179,9 @@ Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
// published sequence.
|
||||
Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
uint64_t log_ref, bool disable_memtable,
|
||||
uint64_t* seq_used, size_t batch_cnt,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable, uint64_t* seq_used,
|
||||
size_t batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback,
|
||||
PostMemTableCallback* post_memtable_callback) {
|
||||
assert(!seq_per_batch_ || batch_cnt != 0);
|
||||
@@ -283,10 +268,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
return Status::NotSupported(
|
||||
"seq_per_batch currently does not honor post_memtable_callback");
|
||||
}
|
||||
if (my_batch->HasDeleteRange() && immutable_db_options_.row_cache) {
|
||||
return Status::NotSupported(
|
||||
"DeleteRange is not compatible with row cache.");
|
||||
}
|
||||
// Otherwise IsLatestPersistentState optimization does not make sense
|
||||
assert(!WriteBatchInternal::IsLatestPersistentState(my_batch) ||
|
||||
disable_memtable);
|
||||
@@ -303,10 +284,10 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
seq_per_batch_ ? kDoAssignOrder : kDontAssignOrder;
|
||||
// Otherwise it is WAL-only Prepare batches in WriteCommitted policy and
|
||||
// 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,
|
||||
assign_order, kDontPublishLastSeq, disable_memtable);
|
||||
return WriteImplWALOnly(&nonmem_write_thread_, write_options, my_batch,
|
||||
callback, log_used, log_ref, seq_used, batch_cnt,
|
||||
pre_release_callback, assign_order,
|
||||
kDontPublishLastSeq, disable_memtable);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.unordered_write) {
|
||||
@@ -318,9 +299,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// Use a write thread to i) optimize for WAL write, ii) publish last
|
||||
// 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,
|
||||
kDoAssignOrder, kDoPublishLastSeq, disable_memtable);
|
||||
&write_thread_, write_options, my_batch, callback, log_used, log_ref,
|
||||
&seq, sub_batch_cnt, pre_release_callback, kDoAssignOrder,
|
||||
kDoPublishLastSeq, disable_memtable);
|
||||
TEST_SYNC_POINT("DBImpl::WriteImpl:UnorderedWriteAfterWriteWAL");
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -337,20 +318,17 @@ 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);
|
||||
return PipelinedWriteImpl(write_options, my_batch, callback, log_used,
|
||||
log_ref, disable_memtable, seq_used);
|
||||
}
|
||||
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
|
||||
log_ref, disable_memtable, batch_cnt,
|
||||
pre_release_callback, post_memtable_callback);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, batch_cnt, pre_release_callback,
|
||||
post_memtable_callback);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_CALLER) {
|
||||
write_thread_.SetMemWritersEachStride(&w);
|
||||
}
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
// we are a non-leader in a parallel group
|
||||
|
||||
@@ -556,35 +534,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += seq_inc;
|
||||
|
||||
if (log_context.need_log_sync) {
|
||||
VersionEdit synced_wals;
|
||||
log_write_mutex_.Lock();
|
||||
if (status.ok()) {
|
||||
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
|
||||
&synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(logfile_number_);
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
|
||||
}
|
||||
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hence provide a simple implementation that is not necessarily
|
||||
// efficient.
|
||||
if (status.ok() && two_write_queues_) {
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
} else {
|
||||
status = SyncWAL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PreReleaseCallback is called after WAL write and before memtable write
|
||||
if (status.ok()) {
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
@@ -656,7 +605,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (!io_s.ok()) {
|
||||
// Check WriteToWAL status
|
||||
WALIOStatusCheck(io_s);
|
||||
IOStatusCheck(io_s);
|
||||
}
|
||||
if (!w.CallbackFailed()) {
|
||||
if (!io_s.ok()) {
|
||||
@@ -668,6 +617,34 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
assert(pre_release_cb_status.ok());
|
||||
}
|
||||
|
||||
if (log_context.need_log_sync) {
|
||||
VersionEdit synced_wals;
|
||||
log_write_mutex_.Lock();
|
||||
if (status.ok()) {
|
||||
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
|
||||
&synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(logfile_number_);
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
|
||||
}
|
||||
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hence provide a simple implementation that is not necessarily efficient.
|
||||
if (two_write_queues_) {
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
} else {
|
||||
status = SyncWAL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool should_exit_batch_group = true;
|
||||
if (in_parallel_group) {
|
||||
// CompleteParallelWorker returns true if this thread should
|
||||
@@ -687,7 +664,7 @@ 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.
|
||||
// we reacts to non-OK statuses here.
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
@@ -702,7 +679,6 @@ 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,
|
||||
bool disable_memtable, uint64_t* seq_used) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
@@ -710,8 +686,8 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
WriteContext write_context;
|
||||
|
||||
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
|
||||
log_ref, disable_memtable, /*_batch_cnt=*/0,
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, /*_batch_cnt=*/0,
|
||||
/*_pre_release_callback=*/nullptr);
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
TEST_SYNC_POINT("DBImplWrite::PipelinedWriteImpl:AfterJoinBatchGroup");
|
||||
@@ -735,6 +711,17 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
size_t total_byte_size = 0;
|
||||
|
||||
if (w.status.ok()) {
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
|
||||
for (auto* writer : wal_write_group) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
for (auto* writer : wal_write_group) {
|
||||
assert(writer);
|
||||
@@ -749,22 +736,6 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
|
||||
for (auto* writer : wal_write_group) {
|
||||
if (writer->CallbackFailed()) {
|
||||
// When optimisitc txn conflict checking fails, we should
|
||||
// not record to trace.
|
||||
continue;
|
||||
}
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (w.disable_wal) {
|
||||
has_unpersisted_data_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
@@ -804,7 +775,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (!io_s.ok()) {
|
||||
// Check WriteToWAL status
|
||||
WALIOStatusCheck(io_s);
|
||||
IOStatusCheck(io_s);
|
||||
} else if (!w.CallbackFailed()) {
|
||||
WriteStatusCheck(w.status);
|
||||
}
|
||||
@@ -855,9 +826,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
// so we need to set its status to pass ASSERT_STATUS_CHECKED
|
||||
memtable_write_group.status.PermitUncheckedError();
|
||||
}
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_CALLER) {
|
||||
write_thread_.SetMemWritersEachStride(&w);
|
||||
}
|
||||
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_memtable_time);
|
||||
@@ -897,8 +866,7 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
WriteThread::Writer w(write_options, my_batch, callback,
|
||||
/*user_write_cb=*/nullptr, log_ref,
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
false /*disable_memtable*/);
|
||||
|
||||
if (w.CheckCallback(this) && w.ShouldWriteToMemtable()) {
|
||||
@@ -948,15 +916,13 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
// applicable in a two-queue setting.
|
||||
Status DBImpl::WriteImplWALOnly(
|
||||
WriteThread* write_thread, const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
WriteBatch* my_batch, WriteCallback* callback, uint64_t* log_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) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
|
||||
log_ref, disable_memtable, sub_batch_cnt,
|
||||
pre_release_callback);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, sub_batch_cnt, pre_release_callback);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
write_thread->JoinBatchGroup(&w);
|
||||
@@ -974,17 +940,21 @@ Status DBImpl::WriteImplWALOnly(
|
||||
assert(w.state == WriteThread::STATE_GROUP_LEADER);
|
||||
|
||||
if (publish_last_seq == kDoPublishLastSeq) {
|
||||
Status status;
|
||||
|
||||
// Currently we only use kDoPublishLastSeq in unordered_write
|
||||
assert(immutable_db_options_.unordered_write);
|
||||
|
||||
WriteContext write_context;
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
status = error_handler_.GetBGError();
|
||||
}
|
||||
// TODO(myabandeh): Make preliminary checks thread-safe so we could do them
|
||||
// without paying the cost of obtaining the mutex.
|
||||
LogContext log_context;
|
||||
WriteContext write_context;
|
||||
Status status =
|
||||
PreprocessWrite(write_options, &log_context, &write_context);
|
||||
WriteStatusCheckOnLocked(status);
|
||||
|
||||
if (status.ok()) {
|
||||
LogContext log_context;
|
||||
status = PreprocessWrite(write_options, &log_context, &write_context);
|
||||
WriteStatusCheckOnLocked(status);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
WriteThread::WriteGroup write_group;
|
||||
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
|
||||
@@ -1010,6 +980,19 @@ Status DBImpl::WriteImplWALOnly(
|
||||
WriteThread::WriteGroup write_group;
|
||||
uint64_t last_sequence;
|
||||
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
|
||||
// Note: no need to update last_batch_group_size_ here since the batch writes
|
||||
// to WAL only
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
|
||||
for (auto* writer : write_group) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t pre_release_callback_cnt = 0;
|
||||
size_t total_byte_size = 0;
|
||||
@@ -1024,23 +1007,6 @@ Status DBImpl::WriteImplWALOnly(
|
||||
}
|
||||
}
|
||||
|
||||
// Note: no need to update last_batch_group_size_ here since the batch writes
|
||||
// to WAL only
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CallbackFailed()) {
|
||||
continue;
|
||||
}
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool concurrent_update = true;
|
||||
// Update stats while we are an exclusive group leader, so we know
|
||||
// that nobody else can be writing to these particular stats.
|
||||
@@ -1086,7 +1052,7 @@ Status DBImpl::WriteImplWALOnly(
|
||||
// This error checking and return is moved up to avoid using uninitialized
|
||||
// last_sequence.
|
||||
if (!io_s.ok()) {
|
||||
WALIOStatusCheck(io_s);
|
||||
IOStatusCheck(io_s);
|
||||
write_thread->ExitAsBatchGroupLeader(write_group, status);
|
||||
return status;
|
||||
}
|
||||
@@ -1184,7 +1150,7 @@ void DBImpl::WriteStatusCheck(const Status& status) {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::WALIOStatusCheck(const IOStatus& io_status) {
|
||||
void DBImpl::IOStatusCheck(const IOStatus& io_status) {
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
if ((immutable_db_options_.paranoid_checks && !io_status.ok() &&
|
||||
@@ -1192,8 +1158,7 @@ void DBImpl::WALIOStatusCheck(const IOStatus& io_status) {
|
||||
io_status.IsIOFenced()) {
|
||||
mutex_.Lock();
|
||||
// Maybe change the return status to void?
|
||||
error_handler_.SetBGError(io_status, BackgroundErrorReason::kWriteCallback,
|
||||
/*wal_related=*/true);
|
||||
error_handler_.SetBGError(io_status, BackgroundErrorReason::kWriteCallback);
|
||||
mutex_.Unlock();
|
||||
} else {
|
||||
// Force writable file to be continue writable.
|
||||
@@ -1211,7 +1176,8 @@ void DBImpl::MemTableInsertStatusCheck(const Status& status) {
|
||||
mutex_.Lock();
|
||||
assert(!error_handler_.IsBGWorkStopped());
|
||||
// Maybe change the return status to void?
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable)
|
||||
.PermitUncheckedError();
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -1417,7 +1383,6 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
total_log_size_ += log_entry.size();
|
||||
log_file_number_size.AddSize(*log_size);
|
||||
log_empty_ = false;
|
||||
|
||||
return io_s;
|
||||
}
|
||||
|
||||
@@ -1490,14 +1455,9 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
// If last sync failed on a later WAL, this could be a fully synced
|
||||
// and closed WAL that just needs to be recorded as synced in the
|
||||
// manifest.
|
||||
if (auto* f = log.writer->file()) {
|
||||
io_s = f->Sync(opts, immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
io_s = log.writer->file()->Sync(opts, immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1529,11 +1489,6 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed()) {
|
||||
writer->CheckPostWalWriteCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -1598,11 +1553,6 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal,
|
||||
concurrent);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed()) {
|
||||
writer->CheckPostWalWriteCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -1610,8 +1560,6 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
Status DBImpl::WriteRecoverableState() {
|
||||
mutex_.AssertHeld();
|
||||
if (!cached_recoverable_state_empty_) {
|
||||
// Only for write-prepared and write-unprepared.
|
||||
assert(seq_per_batch_);
|
||||
bool dont_care_bool;
|
||||
SequenceNumber next_seq;
|
||||
if (two_write_queues_) {
|
||||
@@ -1664,8 +1612,7 @@ Status DBImpl::WriteRecoverableState() {
|
||||
|
||||
void DBImpl::SelectColumnFamiliesForAtomicFlush(
|
||||
autovector<ColumnFamilyData*>* selected_cfds,
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
|
||||
FlushReason flush_reason) {
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds) {
|
||||
mutex_.AssertHeld();
|
||||
assert(selected_cfds);
|
||||
|
||||
@@ -1688,8 +1635,7 @@ void DBImpl::SelectColumnFamiliesForAtomicFlush(
|
||||
continue;
|
||||
}
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load() ||
|
||||
IsRecoveryFlush(flush_reason)) {
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
selected_cfds->push_back(cfd);
|
||||
}
|
||||
}
|
||||
@@ -1801,13 +1747,13 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, FlushReason::kWalFull, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, FlushReason::kWalFull, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -1893,13 +1839,13 @@ Status DBImpl::HandleWriteBufferManagerFlush(WriteContext* write_context) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, FlushReason::kWriteBufferManager,
|
||||
&flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, FlushReason::kWriteBufferManager, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -2175,12 +2121,12 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, FlushReason::kWriteBufferFull, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
} else {
|
||||
for (auto* cfd : cfds) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, FlushReason::kWriteBufferFull, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -2253,13 +2199,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
memtable_info.cf_name = cfd->GetName();
|
||||
memtable_info.first_seqno = cfd->mem()->GetFirstSequenceNumber();
|
||||
memtable_info.earliest_seqno = cfd->mem()->GetEarliestSequenceNumber();
|
||||
memtable_info.num_entries = cfd->mem()->NumEntries();
|
||||
memtable_info.num_deletes = cfd->mem()->NumDeletion();
|
||||
if (!cfd->ioptions()->persist_user_defined_timestamps &&
|
||||
cfd->user_comparator()->timestamp_size() > 0) {
|
||||
const Slice& newest_udt = cfd->mem()->GetNewestUDT();
|
||||
memtable_info.newest_udt.assign(newest_udt.data(), newest_udt.size());
|
||||
}
|
||||
memtable_info.num_entries = cfd->mem()->num_entries();
|
||||
memtable_info.num_deletes = cfd->mem()->num_deletes();
|
||||
// Log this later after lock release. It may be outdated, e.g., if background
|
||||
// flush happens before logging, but that should be ok.
|
||||
int num_imm_unflushed = cfd->imm()->NumNotFlushed();
|
||||
@@ -2279,15 +2220,14 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
SequenceNumber seq = versions_->LastSequence();
|
||||
new_mem = cfd->ConstructNewMemtable(mutable_cf_options, seq);
|
||||
context->superversion_context.NewSuperVersion();
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] New memtable created with log file: #%" PRIu64
|
||||
". Immutable memtables: %d.\n",
|
||||
cfd->GetName().c_str(), new_log_number, num_imm_unflushed);
|
||||
// There should be no concurrent write as the thread is at the front of
|
||||
// writer queue
|
||||
cfd->mem()->ConstructFragmentedRangeTombstones();
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] New memtable created with log file: #%" PRIu64
|
||||
". Immutable memtables: %d.\n",
|
||||
cfd->GetName().c_str(), new_log_number, num_imm_unflushed);
|
||||
// There should be no concurrent write as the thread is at the front of
|
||||
// writer queue
|
||||
cfd->mem()->ConstructFragmentedRangeTombstones();
|
||||
|
||||
mutex_.Lock();
|
||||
if (recycle_log_number != 0) {
|
||||
@@ -2338,8 +2278,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
// We may have lost data from the WritableFileBuffer in-memory buffer for
|
||||
// the current log, so treat it as a fatal error and set bg_error
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kMemTable,
|
||||
/*wal_related=*/true);
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kMemTable);
|
||||
} else {
|
||||
error_handler_.SetBGError(s, BackgroundErrorReason::kMemTable);
|
||||
}
|
||||
@@ -2380,8 +2319,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
read_options, write_options, &wal_deletion, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (!s.ok() && versions_->io_status().IsIOError()) {
|
||||
error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
s = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
|
||||
+5
-259
@@ -27,14 +27,12 @@ class CorruptionFS : public FileSystemWrapper {
|
||||
num_writable_file_errors_(0),
|
||||
corruption_trigger_(INT_MAX),
|
||||
read_count_(0),
|
||||
corrupt_offset_(0),
|
||||
corrupt_len_(0),
|
||||
rnd_(300),
|
||||
fs_buffer_(fs_buffer),
|
||||
verify_read_(verify_read) {}
|
||||
~CorruptionFS() override {
|
||||
// Assert that the corruption was reset, which means it got triggered
|
||||
assert(corruption_trigger_ == INT_MAX || corrupt_len_ > 0);
|
||||
assert(corruption_trigger_ == INT_MAX);
|
||||
}
|
||||
const char* Name() const override { return "ErrorEnv"; }
|
||||
|
||||
@@ -50,10 +48,8 @@ class CorruptionFS : public FileSystemWrapper {
|
||||
}
|
||||
|
||||
void SetCorruptionTrigger(const int trigger) {
|
||||
MutexLock l(&mutex_);
|
||||
corruption_trigger_ = trigger;
|
||||
read_count_ = 0;
|
||||
corrupt_fname_.clear();
|
||||
}
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
@@ -62,31 +58,25 @@ class CorruptionFS : public FileSystemWrapper {
|
||||
IODebugContext* dbg) override {
|
||||
class CorruptionRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
public:
|
||||
CorruptionRandomAccessFile(CorruptionFS& fs, const std::string& fname,
|
||||
CorruptionRandomAccessFile(CorruptionFS& fs,
|
||||
std::unique_ptr<FSRandomAccessFile>& file)
|
||||
: FSRandomAccessFileOwnerWrapper(std::move(file)),
|
||||
fs_(fs),
|
||||
fname_(fname) {}
|
||||
: FSRandomAccessFileOwnerWrapper(std::move(file)), fs_(fs) {}
|
||||
|
||||
IOStatus Read(uint64_t offset, size_t len, const IOOptions& opts,
|
||||
Slice* result, char* scratch,
|
||||
IODebugContext* dbg) const override {
|
||||
IOStatus s = target()->Read(offset, len, opts, result, scratch, dbg);
|
||||
if (opts.verify_and_reconstruct_read) {
|
||||
fs_.MaybeResetOverlapWithCorruptedChunk(fname_, offset,
|
||||
result->size());
|
||||
return s;
|
||||
}
|
||||
|
||||
MutexLock l(&fs_.mutex_);
|
||||
if (s.ok() && ++fs_.read_count_ >= fs_.corruption_trigger_) {
|
||||
fs_.read_count_ = 0;
|
||||
fs_.corruption_trigger_ = INT_MAX;
|
||||
char* data = const_cast<char*>(result->data());
|
||||
std::memcpy(
|
||||
data,
|
||||
fs_.rnd_.RandomString(static_cast<int>(result->size())).c_str(),
|
||||
result->size());
|
||||
fs_.SetCorruptedChunk(fname_, offset, result->size());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -111,76 +101,14 @@ class CorruptionFS : public FileSystemWrapper {
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
IOStatus Prefetch(uint64_t /*offset*/, size_t /*n*/,
|
||||
const IOOptions& /*options*/,
|
||||
IODebugContext* /*dbg*/) override {
|
||||
return IOStatus::NotSupported("Prefetch");
|
||||
}
|
||||
|
||||
private:
|
||||
CorruptionFS& fs_;
|
||||
std::string fname_;
|
||||
};
|
||||
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
IOStatus s = target()->NewRandomAccessFile(fname, opts, &file, dbg);
|
||||
EXPECT_OK(s);
|
||||
result->reset(new CorruptionRandomAccessFile(*this, fname, file));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus NewSequentialFile(const std::string& fname,
|
||||
const FileOptions& file_opts,
|
||||
std::unique_ptr<FSSequentialFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
class CorruptionSequentialFile : public FSSequentialFileOwnerWrapper {
|
||||
public:
|
||||
CorruptionSequentialFile(CorruptionFS& fs, const std::string& fname,
|
||||
std::unique_ptr<FSSequentialFile>& file)
|
||||
: FSSequentialFileOwnerWrapper(std::move(file)),
|
||||
fs_(fs),
|
||||
fname_(fname),
|
||||
offset_(0) {}
|
||||
|
||||
IOStatus Read(size_t len, const IOOptions& opts, Slice* result,
|
||||
char* scratch, IODebugContext* dbg) override {
|
||||
IOStatus s = target()->Read(len, opts, result, scratch, dbg);
|
||||
if (result->size() == 0 ||
|
||||
fname_.find("IDENTITY") != std::string::npos) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (opts.verify_and_reconstruct_read) {
|
||||
fs_.MaybeResetOverlapWithCorruptedChunk(fname_, offset_,
|
||||
result->size());
|
||||
return s;
|
||||
}
|
||||
|
||||
MutexLock l(&fs_.mutex_);
|
||||
if (s.ok() && ++fs_.read_count_ >= fs_.corruption_trigger_) {
|
||||
fs_.corruption_trigger_ = INT_MAX;
|
||||
char* data = const_cast<char*>(result->data());
|
||||
std::memcpy(
|
||||
data,
|
||||
fs_.rnd_.RandomString(static_cast<int>(result->size())).c_str(),
|
||||
result->size());
|
||||
fs_.SetCorruptedChunk(fname_, offset_, result->size());
|
||||
}
|
||||
offset_ += result->size();
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
CorruptionFS& fs_;
|
||||
std::string fname_;
|
||||
size_t offset_;
|
||||
};
|
||||
|
||||
std::unique_ptr<FSSequentialFile> file;
|
||||
IOStatus s = target()->NewSequentialFile(fname, file_opts, &file, dbg);
|
||||
EXPECT_OK(s);
|
||||
result->reset(new CorruptionSequentialFile(*this, fname, file));
|
||||
result->reset(new CorruptionRandomAccessFile(*this, file));
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -195,40 +123,12 @@ class CorruptionFS : public FileSystemWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
void SetCorruptedChunk(const std::string& fname, size_t offset, size_t len) {
|
||||
assert(corrupt_fname_.empty());
|
||||
|
||||
corrupt_fname_ = fname;
|
||||
corrupt_offset_ = offset;
|
||||
corrupt_len_ = len;
|
||||
}
|
||||
|
||||
void MaybeResetOverlapWithCorruptedChunk(const std::string& fname,
|
||||
size_t offset, size_t len) {
|
||||
if (fname == corrupt_fname_ &&
|
||||
((offset <= corrupt_offset_ && (offset + len) > corrupt_offset_) ||
|
||||
(offset >= corrupt_offset_ &&
|
||||
offset < (corrupt_offset_ + corrupt_len_)))) {
|
||||
corrupt_fname_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool VerifyRetry() { return corrupt_len_ > 0 && corrupt_fname_.empty(); }
|
||||
|
||||
int read_count() { return read_count_; }
|
||||
|
||||
int corruption_trigger() { return corruption_trigger_; }
|
||||
|
||||
private:
|
||||
int corruption_trigger_;
|
||||
int read_count_;
|
||||
std::string corrupt_fname_;
|
||||
size_t corrupt_offset_;
|
||||
size_t corrupt_len_;
|
||||
Random rnd_;
|
||||
bool fs_buffer_;
|
||||
bool verify_read_;
|
||||
port::Mutex mutex_;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -805,7 +705,6 @@ class DBIOCorruptionTest
|
||||
DBIOCorruptionTest() : DBIOFailureTest() {
|
||||
BlockBasedTableOptions bbto;
|
||||
options_ = CurrentOptions();
|
||||
options_.statistics = CreateDBStatistics();
|
||||
|
||||
base_env_ = env_;
|
||||
EXPECT_NE(base_env_, nullptr);
|
||||
@@ -817,7 +716,6 @@ class DBIOCorruptionTest
|
||||
bbto.num_file_reads_for_auto_readahead = 0;
|
||||
options_.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options_.disable_auto_compactions = true;
|
||||
options_.max_file_opening_threads = 0;
|
||||
|
||||
Reopen(options_);
|
||||
}
|
||||
@@ -829,8 +727,6 @@ class DBIOCorruptionTest
|
||||
|
||||
Status ReopenDB() { return TryReopen(options_); }
|
||||
|
||||
Statistics* stats() { return options_.statistics.get(); }
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Env> env_guard_;
|
||||
std::shared_ptr<CorruptionFS> fs_;
|
||||
@@ -853,12 +749,8 @@ TEST_P(DBIOCorruptionTest, GetReadCorruptionRetry) {
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(val, "val1");
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
} else {
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -881,12 +773,8 @@ TEST_P(DBIOCorruptionTest, IterReadCorruptionRetry) {
|
||||
}
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
} else {
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
delete iter;
|
||||
}
|
||||
@@ -911,13 +799,9 @@ TEST_P(DBIOCorruptionTest, MultiGetReadCorruptionRetry) {
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_EQ(values[0].ToString(), "val1");
|
||||
ASSERT_EQ(values[1].ToString(), "val2");
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
} else {
|
||||
ASSERT_TRUE(statuses[0].IsCorruption());
|
||||
ASSERT_TRUE(statuses[1].IsCorruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,9 +818,6 @@ TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
|
||||
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
@@ -945,7 +826,6 @@ TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
|
||||
ASSERT_EQ(val, "val1");
|
||||
} else {
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,9 +838,6 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
|
||||
Status s = Flush();
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_GT(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_GT(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
@@ -969,7 +846,6 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
|
||||
ASSERT_EQ(val, "val1");
|
||||
} else {
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,142 +862,12 @@ TEST_P(DBIOCorruptionTest, ManifestCorruptionRetry) {
|
||||
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(ReopenDB());
|
||||
ASSERT_GT(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_GT(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
} else {
|
||||
ASSERT_EQ(ReopenDB(), Status::Corruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBIOCorruptionTest, FooterReadCorruptionRetry) {
|
||||
Random rnd(300);
|
||||
bool retry = false;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ReadFooterFromFileInternal:0", [&](void* arg) {
|
||||
Slice* data = static_cast<Slice*>(arg);
|
||||
if (!retry) {
|
||||
std::memcpy(const_cast<char*>(data->data()),
|
||||
rnd.RandomString(static_cast<int>(data->size())).c_str(),
|
||||
data->size());
|
||||
retry = true;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
Status s = Flush();
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
ASSERT_OK(dbfull()->Get(ro, "key1", &val));
|
||||
ASSERT_EQ(val, "val1");
|
||||
} else {
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
ASSERT_GT(stats()->getTickerCount(SST_FOOTER_CORRUPTION_COUNT), 0);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBIOCorruptionTest, TablePropertiesCorruptionRetry) {
|
||||
Random rnd(300);
|
||||
bool retry = false;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ReadTablePropertiesHelper:0", [&](void* arg) {
|
||||
Slice* data = static_cast<Slice*>(arg);
|
||||
if (!retry) {
|
||||
std::memcpy(const_cast<char*>(data->data()),
|
||||
rnd.RandomString(static_cast<int>(data->size())).c_str(),
|
||||
data->size());
|
||||
retry = true;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
Status s = Flush();
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
ASSERT_OK(dbfull()->Get(ro, "key1", &val));
|
||||
ASSERT_EQ(val, "val1");
|
||||
} else {
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBIOCorruptionTest, DBOpenReadCorruptionRetry) {
|
||||
if (!std::get<2>(GetParam())) {
|
||||
return;
|
||||
}
|
||||
CorruptionFS* fs =
|
||||
static_cast<CorruptionFS*>(env_guard_->GetFileSystem().get());
|
||||
|
||||
for (int sst = 0; sst < 3; ++sst) {
|
||||
for (int key = 0; key < 100; ++key) {
|
||||
std::stringstream ss;
|
||||
ss << std::setw(3) << 100 * sst + key;
|
||||
ASSERT_OK(Put("key" + ss.str(), "val" + ss.str()));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
Close();
|
||||
|
||||
// DB open will create table readers unless we reduce the table cache
|
||||
// capacity.
|
||||
// SanitizeOptions will set max_open_files to minimum of 20. Table cache
|
||||
// is allocated with max_open_files - 10 as capacity. So override
|
||||
// max_open_files to 11 so table cache capacity will become 1. This will
|
||||
// prevent file open during DB open and force the file to be opened
|
||||
// during MultiGet
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
|
||||
int* max_open_files = (int*)arg;
|
||||
*max_open_files = 11;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Progressively increase the IO count trigger for corruption, and verify
|
||||
// that it was retried
|
||||
int corruption_trigger = 1;
|
||||
fs->SetCorruptionTrigger(corruption_trigger);
|
||||
do {
|
||||
fs->SetCorruptionTrigger(corruption_trigger);
|
||||
ASSERT_OK(ReopenDB());
|
||||
for (int sst = 0; sst < 3; ++sst) {
|
||||
for (int key = 0; key < 100; ++key) {
|
||||
std::stringstream ss;
|
||||
ss << std::setw(3) << 100 * sst + key;
|
||||
ASSERT_EQ(Get("key" + ss.str()), "val" + ss.str());
|
||||
}
|
||||
}
|
||||
// Verify that the injected corruption was repaired
|
||||
ASSERT_TRUE(fs->VerifyRetry());
|
||||
corruption_trigger++;
|
||||
} while (fs->corruption_trigger() == INT_MAX);
|
||||
}
|
||||
|
||||
// The parameters are - 1. Use FS provided buffer, 2. Use async IO ReadOption,
|
||||
// 3. Retry with verify_and_reconstruct_read IOOption
|
||||
INSTANTIATE_TEST_CASE_P(DBIOCorruptionTest, DBIOCorruptionTest,
|
||||
|
||||
+88
-124
@@ -52,9 +52,7 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
user_comparator_(cmp),
|
||||
merge_operator_(ioptions.merge_operator.get()),
|
||||
iter_(iter),
|
||||
blob_reader_(version, read_options.read_tier,
|
||||
read_options.verify_checksums, read_options.fill_cache,
|
||||
read_options.io_activity),
|
||||
version_(version),
|
||||
read_callback_(read_callback),
|
||||
sequence_(s),
|
||||
statistics_(ioptions.stats),
|
||||
@@ -67,16 +65,20 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
valid_(false),
|
||||
current_entry_is_merged_(false),
|
||||
is_key_seqnum_zero_(false),
|
||||
prefix_same_as_start_(
|
||||
prefix_extractor_ ? read_options.prefix_same_as_start : false),
|
||||
prefix_same_as_start_(mutable_cf_options.prefix_extractor
|
||||
? read_options.prefix_same_as_start
|
||||
: false),
|
||||
pin_thru_lifetime_(read_options.pin_data),
|
||||
expect_total_order_inner_iter_(prefix_extractor_ == nullptr ||
|
||||
read_options.total_order_seek ||
|
||||
read_options.auto_prefix_mode),
|
||||
read_tier_(read_options.read_tier),
|
||||
fill_cache_(read_options.fill_cache),
|
||||
verify_checksums_(read_options.verify_checksums),
|
||||
expose_blob_index_(expose_blob_index),
|
||||
allow_unprepared_value_(read_options.allow_unprepared_value),
|
||||
is_blob_(false),
|
||||
arena_mode_(arena_mode),
|
||||
io_activity_(read_options.io_activity),
|
||||
cfh_(cfh),
|
||||
timestamp_ub_(read_options.timestamp),
|
||||
timestamp_lb_(read_options.iter_start_ts),
|
||||
@@ -91,9 +93,6 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
status_.PermitUncheckedError();
|
||||
assert(timestamp_size_ ==
|
||||
user_comparator_.user_comparator()->timestamp_size());
|
||||
// prefix_seek_opt_in_only should force total_order_seek whereever the caller
|
||||
// is duplicating the original ReadOptions
|
||||
assert(!ioptions.prefix_seek_opt_in_only || read_options.total_order_seek);
|
||||
}
|
||||
|
||||
Status DBIter::GetProperty(std::string prop_name, std::string* prop) {
|
||||
@@ -150,7 +149,7 @@ void DBIter::Next() {
|
||||
PERF_CPU_TIMER_GUARD(iter_next_cpu_nanos, clock_);
|
||||
// Release temporarily pinned blocks from last operation
|
||||
ReleaseTempPinnedData();
|
||||
ResetBlobData();
|
||||
ResetBlobValue();
|
||||
ResetValueAndColumns();
|
||||
local_stats_.skip_count_ += num_internal_keys_skipped_;
|
||||
local_stats_.skip_count_--;
|
||||
@@ -193,21 +192,29 @@ void DBIter::Next() {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBIter::BlobReader::RetrieveAndSetBlobValue(const Slice& user_key,
|
||||
const Slice& blob_index) {
|
||||
bool DBIter::SetBlobValueIfNeeded(const Slice& user_key,
|
||||
const Slice& blob_index) {
|
||||
assert(!is_blob_);
|
||||
assert(blob_value_.empty());
|
||||
|
||||
if (expose_blob_index_) { // Stacked BlobDB implementation
|
||||
is_blob_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!version_) {
|
||||
return Status::Corruption("Encountered unexpected blob index.");
|
||||
status_ = Status::Corruption("Encountered unexpected blob index.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: consider moving ReadOptions from ArenaWrappedDBIter to DBIter to
|
||||
// avoid having to copy options back and forth.
|
||||
// TODO: plumb Env::IOPriority
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
ReadOptions read_options;
|
||||
read_options.read_tier = read_tier_;
|
||||
read_options.verify_checksums = verify_checksums_;
|
||||
read_options.fill_cache = fill_cache_;
|
||||
read_options.verify_checksums = verify_checksums_;
|
||||
read_options.io_activity = io_activity_;
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
constexpr uint64_t* bytes_read = nullptr;
|
||||
@@ -215,49 +222,14 @@ Status DBIter::BlobReader::RetrieveAndSetBlobValue(const Slice& user_key,
|
||||
const Status s = version_->GetBlob(read_options, user_key, blob_index,
|
||||
prefetch_buffer, &blob_value_, bytes_read);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool DBIter::SetValueAndColumnsFromBlobImpl(const Slice& user_key,
|
||||
const Slice& blob_index) {
|
||||
const Status s = blob_reader_.RetrieveAndSetBlobValue(user_key, blob_index);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
is_blob_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
SetValueAndColumnsFromPlain(blob_reader_.GetBlobValue());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBIter::SetValueAndColumnsFromBlob(const Slice& user_key,
|
||||
const Slice& blob_index) {
|
||||
assert(!is_blob_);
|
||||
is_blob_ = true;
|
||||
|
||||
if (expose_blob_index_) {
|
||||
SetValueAndColumnsFromPlain(blob_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (allow_unprepared_value_) {
|
||||
assert(value_.empty());
|
||||
assert(wide_columns_.empty());
|
||||
|
||||
assert(lazy_blob_index_.empty());
|
||||
lazy_blob_index_ = blob_index;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return SetValueAndColumnsFromBlobImpl(user_key, blob_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBIter::SetValueAndColumnsFromEntity(Slice slice) {
|
||||
@@ -305,24 +277,6 @@ bool DBIter::SetValueAndColumnsFromMergeResult(const Status& merge_status,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBIter::PrepareValue() {
|
||||
assert(valid_);
|
||||
|
||||
if (lazy_blob_index_.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(allow_unprepared_value_);
|
||||
assert(is_blob_);
|
||||
|
||||
const bool result =
|
||||
SetValueAndColumnsFromBlobImpl(saved_key_.GetUserKey(), lazy_blob_index_);
|
||||
|
||||
lazy_blob_index_.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// PRE: saved_key_ has the current user key if skipping_saved_key
|
||||
// POST: saved_key_ should have the next user key if valid_,
|
||||
// if the current entry is a result of merge
|
||||
@@ -452,7 +406,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
case kTypeValuePreferredSeqno:
|
||||
case kTypeBlobIndex:
|
||||
case kTypeWideColumnEntity:
|
||||
if (!PrepareValueInternal()) {
|
||||
if (!PrepareValue()) {
|
||||
return false;
|
||||
}
|
||||
if (timestamp_lb_) {
|
||||
@@ -464,9 +418,12 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
}
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
if (!SetValueAndColumnsFromBlob(ikey_.user_key, iter_.value())) {
|
||||
if (!SetBlobValueIfNeeded(ikey_.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SetValueAndColumnsFromPlain(expose_blob_index_ ? iter_.value()
|
||||
: blob_value_);
|
||||
} else if (ikey_.type == kTypeWideColumnEntity) {
|
||||
if (!SetValueAndColumnsFromEntity(iter_.value())) {
|
||||
return false;
|
||||
@@ -486,7 +443,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
return true;
|
||||
break;
|
||||
case kTypeMerge:
|
||||
if (!PrepareValueInternal()) {
|
||||
if (!PrepareValue()) {
|
||||
return false;
|
||||
}
|
||||
saved_key_.SetUserKey(
|
||||
@@ -581,14 +538,6 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
} else {
|
||||
iter_.Next();
|
||||
}
|
||||
|
||||
// This could be a long-running operation due to tombstones, etc.
|
||||
bool aborted = ROCKSDB_THREAD_YIELD_CHECK_ABORT();
|
||||
if (aborted) {
|
||||
valid_ = false;
|
||||
status_ = Status::Aborted("Query abort.");
|
||||
return false;
|
||||
}
|
||||
} while (iter_.Valid());
|
||||
|
||||
valid_ = false;
|
||||
@@ -639,7 +588,7 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
iter_.Next();
|
||||
break;
|
||||
}
|
||||
if (!PrepareValueInternal()) {
|
||||
if (!PrepareValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -668,9 +617,23 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
iter_.value(), iter_.iter()->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
} else if (kTypeBlobIndex == ikey.type) {
|
||||
if (!MergeWithBlobBaseValue(iter_.value(), ikey.user_key)) {
|
||||
if (expose_blob_index_) {
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
if (!SetBlobValueIfNeeded(ikey.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
if (!MergeWithPlainBaseValue(blob_value_, ikey.user_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResetBlobValue();
|
||||
|
||||
// iter_ is positioned after put
|
||||
iter_.Next();
|
||||
@@ -678,7 +641,6 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (kTypeWideColumnEntity == ikey.type) {
|
||||
if (!MergeWithWideColumnBaseValue(iter_.value(), ikey.user_key)) {
|
||||
@@ -725,7 +687,7 @@ void DBIter::Prev() {
|
||||
PERF_COUNTER_ADD(iter_prev_count, 1);
|
||||
PERF_CPU_TIMER_GUARD(iter_prev_cpu_nanos, clock_);
|
||||
ReleaseTempPinnedData();
|
||||
ResetBlobData();
|
||||
ResetBlobValue();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
bool ok = true;
|
||||
@@ -962,7 +924,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
return FindValueForCurrentKeyUsingSeek();
|
||||
}
|
||||
|
||||
if (!PrepareValueInternal()) {
|
||||
if (!PrepareValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1077,9 +1039,21 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
}
|
||||
return true;
|
||||
} else if (last_not_merge_type == kTypeBlobIndex) {
|
||||
if (!MergeWithBlobBaseValue(pinned_value_, saved_key_.GetUserKey())) {
|
||||
if (expose_blob_index_) {
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (!SetBlobValueIfNeeded(saved_key_.GetUserKey(), pinned_value_)) {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
if (!MergeWithPlainBaseValue(blob_value_, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResetBlobValue();
|
||||
|
||||
return true;
|
||||
} else if (last_not_merge_type == kTypeWideColumnEntity) {
|
||||
@@ -1104,9 +1078,13 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
|
||||
break;
|
||||
case kTypeBlobIndex:
|
||||
if (!SetValueAndColumnsFromBlob(saved_key_.GetUserKey(), pinned_value_)) {
|
||||
if (!SetBlobValueIfNeeded(saved_key_.GetUserKey(), pinned_value_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SetValueAndColumnsFromPlain(expose_blob_index_ ? pinned_value_
|
||||
: blob_value_);
|
||||
|
||||
break;
|
||||
case kTypeWideColumnEntity:
|
||||
if (!SetValueAndColumnsFromEntity(pinned_value_)) {
|
||||
@@ -1193,7 +1171,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!PrepareValueInternal()) {
|
||||
if (!PrepareValue()) {
|
||||
return false;
|
||||
}
|
||||
if (timestamp_size_ > 0) {
|
||||
@@ -1210,9 +1188,12 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
pinned_value_ = iter_.value();
|
||||
}
|
||||
if (ikey.type == kTypeBlobIndex) {
|
||||
if (!SetValueAndColumnsFromBlob(ikey.user_key, pinned_value_)) {
|
||||
if (!SetBlobValueIfNeeded(ikey.user_key, pinned_value_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SetValueAndColumnsFromPlain(expose_blob_index_ ? pinned_value_
|
||||
: blob_value_);
|
||||
} else if (ikey.type == kTypeWideColumnEntity) {
|
||||
if (!SetValueAndColumnsFromEntity(pinned_value_)) {
|
||||
return false;
|
||||
@@ -1260,7 +1241,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
ikey.type == kTypeDeletionWithTimestamp) {
|
||||
break;
|
||||
}
|
||||
if (!PrepareValueInternal()) {
|
||||
if (!PrepareValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1278,9 +1259,21 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
iter_.value(), iter_.iter()->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
} else if (ikey.type == kTypeBlobIndex) {
|
||||
if (!MergeWithBlobBaseValue(iter_.value(), saved_key_.GetUserKey())) {
|
||||
if (expose_blob_index_) {
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (!SetBlobValueIfNeeded(ikey.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
if (!MergeWithPlainBaseValue(blob_value_, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResetBlobValue();
|
||||
|
||||
return true;
|
||||
} else if (ikey.type == kTypeWideColumnEntity) {
|
||||
@@ -1347,35 +1340,6 @@ bool DBIter::MergeWithPlainBaseValue(const Slice& value,
|
||||
return SetValueAndColumnsFromMergeResult(s, result_type);
|
||||
}
|
||||
|
||||
bool DBIter::MergeWithBlobBaseValue(const Slice& blob_index,
|
||||
const Slice& user_key) {
|
||||
assert(!is_blob_);
|
||||
|
||||
if (expose_blob_index_) {
|
||||
status_ =
|
||||
Status::NotSupported("Legacy BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const Status s = blob_reader_.RetrieveAndSetBlobValue(user_key, blob_index);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
|
||||
if (!MergeWithPlainBaseValue(blob_reader_.GetBlobValue(), user_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blob_reader_.ResetBlobValue();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBIter::MergeWithWideColumnBaseValue(const Slice& entity,
|
||||
const Slice& user_key) {
|
||||
// `op_failure_scope` (an output parameter) is not provided (set to nullptr)
|
||||
@@ -1565,7 +1529,7 @@ void DBIter::Seek(const Slice& target) {
|
||||
|
||||
status_ = Status::OK();
|
||||
ReleaseTempPinnedData();
|
||||
ResetBlobData();
|
||||
ResetBlobValue();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
|
||||
@@ -1641,7 +1605,7 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
|
||||
status_ = Status::OK();
|
||||
ReleaseTempPinnedData();
|
||||
ResetBlobData();
|
||||
ResetBlobValue();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
|
||||
@@ -1702,7 +1666,7 @@ void DBIter::SeekToFirst() {
|
||||
status_.PermitUncheckedError();
|
||||
direction_ = kForward;
|
||||
ReleaseTempPinnedData();
|
||||
ResetBlobData();
|
||||
ResetBlobValue();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
ClearSavedValue();
|
||||
@@ -1765,7 +1729,7 @@ void DBIter::SeekToLast() {
|
||||
status_.PermitUncheckedError();
|
||||
direction_ = kReverse;
|
||||
ReleaseTempPinnedData();
|
||||
ResetBlobData();
|
||||
ResetBlobValue();
|
||||
ResetValueAndColumns();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
ClearSavedValue();
|
||||
|
||||
+17
-44
@@ -218,34 +218,7 @@ class DBIter final : public Iterator {
|
||||
}
|
||||
void set_valid(bool v) { valid_ = v; }
|
||||
|
||||
bool PrepareValue() override;
|
||||
|
||||
private:
|
||||
class BlobReader {
|
||||
public:
|
||||
BlobReader(const Version* version, ReadTier read_tier,
|
||||
bool verify_checksums, bool fill_cache,
|
||||
Env::IOActivity io_activity)
|
||||
: version_(version),
|
||||
read_tier_(read_tier),
|
||||
verify_checksums_(verify_checksums),
|
||||
fill_cache_(fill_cache),
|
||||
io_activity_(io_activity) {}
|
||||
|
||||
const Slice& GetBlobValue() const { return blob_value_; }
|
||||
Status RetrieveAndSetBlobValue(const Slice& user_key,
|
||||
const Slice& blob_index);
|
||||
void ResetBlobValue() { blob_value_.Reset(); }
|
||||
|
||||
private:
|
||||
PinnableSlice blob_value_;
|
||||
const Version* version_;
|
||||
ReadTier read_tier_;
|
||||
bool verify_checksums_;
|
||||
bool fill_cache_;
|
||||
Env::IOActivity io_activity_;
|
||||
};
|
||||
|
||||
// For all methods in this block:
|
||||
// PRE: iter_->Valid() && status_.ok()
|
||||
// Return false if there was an error, and status() is non-ok, valid_ = false;
|
||||
@@ -326,6 +299,15 @@ class DBIter final : public Iterator {
|
||||
: user_comparator_.CompareWithoutTimestamp(a, b);
|
||||
}
|
||||
|
||||
// Retrieves the blob value for the specified user key using the given blob
|
||||
// index when using the integrated BlobDB implementation.
|
||||
bool SetBlobValueIfNeeded(const Slice& user_key, const Slice& blob_index);
|
||||
|
||||
void ResetBlobValue() {
|
||||
is_blob_ = false;
|
||||
blob_value_.Reset();
|
||||
}
|
||||
|
||||
void SetValueAndColumnsFromPlain(const Slice& slice) {
|
||||
assert(value_.empty());
|
||||
assert(wide_columns_.empty());
|
||||
@@ -334,11 +316,6 @@ class DBIter final : public Iterator {
|
||||
wide_columns_.emplace_back(kDefaultWideColumnName, slice);
|
||||
}
|
||||
|
||||
bool SetValueAndColumnsFromBlobImpl(const Slice& user_key,
|
||||
const Slice& blob_index);
|
||||
bool SetValueAndColumnsFromBlob(const Slice& user_key,
|
||||
const Slice& blob_index);
|
||||
|
||||
bool SetValueAndColumnsFromEntity(Slice slice);
|
||||
|
||||
bool SetValueAndColumnsFromMergeResult(const Status& merge_status,
|
||||
@@ -349,21 +326,14 @@ class DBIter final : public Iterator {
|
||||
wide_columns_.clear();
|
||||
}
|
||||
|
||||
void ResetBlobData() {
|
||||
blob_reader_.ResetBlobValue();
|
||||
lazy_blob_index_.clear();
|
||||
is_blob_ = false;
|
||||
}
|
||||
|
||||
// The following methods perform the actual merge operation for the
|
||||
// no/plain/blob/wide-column base value cases.
|
||||
// no base value/plain base value/wide-column base value cases.
|
||||
// If user-defined timestamp is enabled, `user_key` includes timestamp.
|
||||
bool MergeWithNoBaseValue(const Slice& user_key);
|
||||
bool MergeWithPlainBaseValue(const Slice& value, const Slice& user_key);
|
||||
bool MergeWithBlobBaseValue(const Slice& blob_index, const Slice& user_key);
|
||||
bool MergeWithWideColumnBaseValue(const Slice& entity, const Slice& user_key);
|
||||
|
||||
bool PrepareValueInternal() {
|
||||
bool PrepareValue() {
|
||||
if (!iter_.PrepareValue()) {
|
||||
assert(!iter_.status().ok());
|
||||
valid_ = false;
|
||||
@@ -386,7 +356,7 @@ class DBIter final : public Iterator {
|
||||
UserComparatorWrapper user_comparator_;
|
||||
const MergeOperator* const merge_operator_;
|
||||
IteratorWrapper iter_;
|
||||
BlobReader blob_reader_;
|
||||
const Version* version_;
|
||||
ReadCallback* read_callback_;
|
||||
// Max visible sequence number. It is normally the snapshot seq unless we have
|
||||
// uncommitted data in db as in WriteUnCommitted.
|
||||
@@ -406,6 +376,7 @@ class DBIter final : public Iterator {
|
||||
std::string saved_value_;
|
||||
Slice pinned_value_;
|
||||
// for prefix seek mode to support prev()
|
||||
PinnableSlice blob_value_;
|
||||
// Value of the default column
|
||||
Slice value_;
|
||||
// All columns (i.e. name-value pairs)
|
||||
@@ -439,13 +410,15 @@ class DBIter final : public Iterator {
|
||||
// Expect the inner iterator to maintain a total order.
|
||||
// prefix_extractor_ must be non-NULL if the value is false.
|
||||
const bool expect_total_order_inner_iter_;
|
||||
ReadTier read_tier_;
|
||||
bool fill_cache_;
|
||||
bool verify_checksums_;
|
||||
// Whether the iterator is allowed to expose blob references. Set to true when
|
||||
// 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_;
|
||||
const Env::IOActivity io_activity_;
|
||||
// List of operands for merge operator.
|
||||
MergeContext merge_context_;
|
||||
LocalStatistics local_stats_;
|
||||
|
||||
@@ -684,14 +684,13 @@ class DbMemtableKVChecksumTest : public DbKvChecksumTest {
|
||||
DbMemtableKVChecksumTest() : DbKvChecksumTest() {}
|
||||
|
||||
protected:
|
||||
const size_t kValueLenOffset = 12;
|
||||
// Indices in the memtable entry that we will not corrupt.
|
||||
// For memtable entry format, see comments in MemTable::Add().
|
||||
// We do not corrupt key length and value length fields in this test
|
||||
// case since it causes segfault and ASAN will complain.
|
||||
// For this test case, key and value are all of length 3, so
|
||||
// key length field is at index 0 and value length field is at index 12.
|
||||
const std::set<size_t> index_not_to_corrupt{0, kValueLenOffset};
|
||||
const std::set<size_t> index_not_to_corrupt{0, 12};
|
||||
|
||||
void SkipNotToCorruptEntry() {
|
||||
if (index_not_to_corrupt.find(corrupt_byte_offset_) !=
|
||||
@@ -738,8 +737,6 @@ TEST_P(DbMemtableKVChecksumTest, GetWithCorruptAfterMemtableInsert) {
|
||||
buf[corrupt_byte_offset_] += corrupt_byte_addend_;
|
||||
++corrupt_byte_offset_;
|
||||
});
|
||||
// Corrupt value only so that MultiGet below can find the key.
|
||||
corrupt_byte_offset_ = kValueLenOffset + 1;
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options = CurrentOptions();
|
||||
options.memtable_protection_bytes_per_key =
|
||||
@@ -748,17 +745,12 @@ TEST_P(DbMemtableKVChecksumTest, GetWithCorruptAfterMemtableInsert) {
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
}
|
||||
|
||||
std::string key = "key";
|
||||
SkipNotToCorruptEntry();
|
||||
while (MoreBytesToCorrupt()) {
|
||||
Reopen(options);
|
||||
ASSERT_OK(ExecuteWrite(nullptr));
|
||||
std::string val;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), key, &val).IsCorruption());
|
||||
std::vector<std::string> vals = {val};
|
||||
std::vector<Status> statuses = db_->MultiGet(
|
||||
ReadOptions(), {db_->DefaultColumnFamily()}, {key}, &vals, nullptr);
|
||||
ASSERT_TRUE(statuses[0].IsCorruption());
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "key", &val).IsCorruption());
|
||||
Destroy(options);
|
||||
SkipNotToCorruptEntry();
|
||||
}
|
||||
|
||||
@@ -339,91 +339,6 @@ TEST_F(DBMemTableTest, ColumnFamilyId) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBMemTableTest, IntegrityChecks) {
|
||||
// We insert keys key000000, key000001 and key000002 into skiplist at fixed
|
||||
// height 1 (smallest height). Then we corrupt the second key to aey000001 to
|
||||
// make it smaller. With `paranoid_memory_checks` set to true, if the
|
||||
// skip list sees key000000 and then aey000001, then it will report out of
|
||||
// order keys with corruption status. With `paranoid_memory_checks` set
|
||||
// to false, read/scan may return wrong results.
|
||||
for (bool allow_data_in_error : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
options.allow_data_in_errors = allow_data_in_error;
|
||||
options.paranoid_memory_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"InlineSkipList::RandomHeight::height", [](void* h) {
|
||||
auto height_ptr = static_cast<int*>(h);
|
||||
*height_ptr = 1;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(Put(Key(0), "val0"));
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
// p will point to the buffer for encoded key000001
|
||||
char* p = nullptr;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTable::Add:BeforeReturn:Encoded", [&](void* encoded) {
|
||||
p = const_cast<char*>(static_cast<Slice*>(encoded)->data());
|
||||
});
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ASSERT_TRUE(p);
|
||||
// Offset 0 is key size, key bytes start at offset 1.
|
||||
// "key000001 -> aey000001"
|
||||
p[1] = 'a';
|
||||
|
||||
ReadOptions rops;
|
||||
std::string val;
|
||||
Status s = db_->Get(rops, Key(1), &val);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
std::string key0 = Slice(Key(0)).ToString(true);
|
||||
ASSERT_EQ(s.ToString().find(key0) != std::string::npos,
|
||||
allow_data_in_error);
|
||||
// Without `paranoid_memory_checks`, NotFound will be returned.
|
||||
// This would fail an assertion in InlineSkipList::FindGreaterOrEqual().
|
||||
// If we remove the assertion, this passes.
|
||||
// ASSERT_TRUE(db_->Get(ReadOptions(), Key(1), &val).IsNotFound());
|
||||
|
||||
std::vector<std::string> vals;
|
||||
std::vector<Status> statuses = db_->MultiGet(
|
||||
rops, {db_->DefaultColumnFamily()}, {Key(1)}, &vals, nullptr);
|
||||
ASSERT_TRUE(statuses[0].IsCorruption());
|
||||
ASSERT_EQ(statuses[0].ToString().find(key0) != std::string::npos,
|
||||
allow_data_in_error);
|
||||
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(rops)};
|
||||
ASSERT_OK(iter->status());
|
||||
iter->Seek(Key(1));
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
ASSERT_EQ(iter->status().ToString().find(key0) != std::string::npos,
|
||||
allow_data_in_error);
|
||||
|
||||
iter->Seek(Key(0));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
// iterating through skip list at height at 1 should catch out-of-order keys
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
ASSERT_EQ(iter->status().ToString().find(key0) != std::string::npos,
|
||||
allow_data_in_error);
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->SeekForPrev(Key(2));
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
ASSERT_EQ(iter->status().ToString().find(key0) != std::string::npos,
|
||||
allow_data_in_error);
|
||||
|
||||
// Internally DB Iter will iterate backwards (call Prev()) after
|
||||
// SeekToLast() to find the correct internal key with the last user key.
|
||||
// Prev() will do integrity checks and catch corruption.
|
||||
iter->SeekToLast();
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
ASSERT_EQ(iter->status().ToString().find(key0) != std::string::npos,
|
||||
allow_data_in_error);
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+6
-27
@@ -56,11 +56,6 @@ class DBOptionsTest : public DBTestBase {
|
||||
EXPECT_OK(GetStringFromMutableCFOptions(
|
||||
config_options, MutableCFOptions(options), &options_str));
|
||||
EXPECT_OK(StringToMap(options_str, &mutable_map));
|
||||
for (auto& opt : TEST_GetImmutableInMutableCFOptions()) {
|
||||
// Not yet mutable but migrated to MutableCFOptions in preparation for
|
||||
// being mutable
|
||||
mutable_map.erase(opt);
|
||||
}
|
||||
return mutable_map;
|
||||
}
|
||||
|
||||
@@ -236,33 +231,21 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
cfh, {{"table_factory.block_size", "16384"},
|
||||
{"table_factory.block_restart_interval", "11"}}));
|
||||
// Old c_bbto
|
||||
ASSERT_EQ(c_bbto->block_size, 8192);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 7);
|
||||
// New c_bbto
|
||||
c_opts = dbfull()->GetOptions(cfh);
|
||||
c_bbto = c_opts.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 11);
|
||||
|
||||
// Now set an option that is not mutable - options should not change
|
||||
// FIXME: find a way to make this fail again
|
||||
// ASSERT_NOK(
|
||||
// dbfull()->SetOptions(cfh, {{"table_factory.no_block_cache", "false"}}));
|
||||
c_opts = dbfull()->GetOptions(cfh);
|
||||
ASSERT_EQ(c_bbto, c_opts.table_factory->GetOptions<BlockBasedTableOptions>());
|
||||
ASSERT_NOK(
|
||||
dbfull()->SetOptions(cfh, {{"table_factory.no_block_cache", "false"}}));
|
||||
ASSERT_EQ(c_bbto->no_block_cache, true);
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 11);
|
||||
|
||||
// Set some that are mutable and some that are not - options should not change
|
||||
// FIXME: find a way to make this fail again
|
||||
// ASSERT_NOK(dbfull()->SetOptions(
|
||||
// cfh, {{"table_factory.no_block_cache", "false"},
|
||||
// {"table_factory.block_size", "8192"},
|
||||
// {"table_factory.block_restart_interval", "7"}}));
|
||||
c_opts = dbfull()->GetOptions(cfh);
|
||||
ASSERT_EQ(c_bbto, c_opts.table_factory->GetOptions<BlockBasedTableOptions>());
|
||||
ASSERT_NOK(dbfull()->SetOptions(
|
||||
cfh, {{"table_factory.no_block_cache", "false"},
|
||||
{"table_factory.block_size", "8192"},
|
||||
{"table_factory.block_restart_interval", "7"}}));
|
||||
ASSERT_EQ(c_bbto->no_block_cache, true);
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 11);
|
||||
@@ -273,8 +256,6 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
|
||||
cfh, {{"table_factory.block_size", "8192"},
|
||||
{"table_factory.does_not_exist", "true"},
|
||||
{"table_factory.block_restart_interval", "7"}}));
|
||||
c_opts = dbfull()->GetOptions(cfh);
|
||||
ASSERT_EQ(c_bbto, c_opts.table_factory->GetOptions<BlockBasedTableOptions>());
|
||||
ASSERT_EQ(c_bbto->no_block_cache, true);
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 11);
|
||||
@@ -290,7 +271,6 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
|
||||
{"table_factory.block_restart_interval", "13"}}));
|
||||
c_opts = dbfull()->GetOptions(cfh);
|
||||
ASSERT_EQ(c_opts.blob_file_size, 32768);
|
||||
c_bbto = c_opts.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 13);
|
||||
// Set some on the table and a bad one on the ColumnFamily - options should
|
||||
@@ -299,7 +279,6 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
|
||||
cfh, {{"table_factory.block_size", "1024"},
|
||||
{"no_such_option", "32768"},
|
||||
{"table_factory.block_restart_interval", "7"}}));
|
||||
ASSERT_EQ(c_bbto, c_opts.table_factory->GetOptions<BlockBasedTableOptions>());
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 13);
|
||||
}
|
||||
|
||||
@@ -192,49 +192,6 @@ TEST_F(DBPropertiesTest, GetAggregatedIntPropertyTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, AggregateBlockCacheProperty) {
|
||||
constexpr size_t kCapacity = 1000;
|
||||
LRUCacheOptions co;
|
||||
co.capacity = kCapacity;
|
||||
co.num_shard_bits = 0;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
auto block_cache = NewLRUCache(co);
|
||||
|
||||
// All columns families share the same block cache.
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_opt;
|
||||
table_opt.no_block_cache = false;
|
||||
table_opt.block_cache = block_cache;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_opt));
|
||||
|
||||
CreateAndReopenWithCF({"one", "two", "three", "four"}, options);
|
||||
|
||||
// Insert unpinned block to the cache
|
||||
constexpr size_t kSize1 = 100;
|
||||
ASSERT_OK(block_cache->Insert("block1", nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, kSize1));
|
||||
// Insert pinned block to the cache
|
||||
constexpr size_t kSize2 = 200;
|
||||
Cache::Handle* block2 = nullptr;
|
||||
ASSERT_OK(block_cache->Insert("block2", nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, kSize2, &block2));
|
||||
|
||||
uint64_t value;
|
||||
ASSERT_TRUE(db_->GetAggregatedIntProperty(DB::Properties::kBlockCacheCapacity,
|
||||
&value));
|
||||
ASSERT_EQ(value, kCapacity);
|
||||
|
||||
ASSERT_TRUE(
|
||||
db_->GetAggregatedIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
ASSERT_EQ(value, kSize1 + kSize2);
|
||||
|
||||
ASSERT_TRUE(db_->GetAggregatedIntProperty(
|
||||
DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
ASSERT_EQ(value, kSize2);
|
||||
|
||||
block_cache->Release(block2);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
ASSERT_EQ(a == 0U, b == 0U);
|
||||
@@ -1126,6 +1083,7 @@ TEST_F(DBPropertiesTest, EstimateCompressionRatio) {
|
||||
ASSERT_GT(CompressionRatioAtLevel(1), 10.0);
|
||||
}
|
||||
|
||||
|
||||
class CountingUserTblPropCollector : public TablePropertiesCollector {
|
||||
public:
|
||||
const char* Name() const override { return "CountingUserTblPropCollector"; }
|
||||
@@ -2408,6 +2366,7 @@ TEST_F(DBPropertiesTest, TableMetaIndexKeys) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -3820,10 +3820,6 @@ TEST_F(DBRangeDelTest, RowCache) {
|
||||
ASSERT_OK(wb.DeleteRange(Key(1), Key(5)));
|
||||
ASSERT_TRUE(db_->Write(WriteOptions(), &wb).IsNotSupported());
|
||||
ASSERT_EQ(Get(Key(3)), "val");
|
||||
// By default, memtable insertion failure will turn the DB to read-only mode.
|
||||
// The check for delete range should happen before that to fail early
|
||||
// and should not turn db into read-only mdoe.
|
||||
ASSERT_OK(Put(Key(5), "foo"));
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -245,13 +245,7 @@ TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
|
||||
// In DirectIO, where we support tail prefetching, during table open, we only
|
||||
// do 1 read instead of 4 as described above. Actual checksum verification
|
||||
// reads stay the same.
|
||||
#ifdef OS_WIN
|
||||
// No file system prefetch implemented for OS Win. During table open,
|
||||
// we only do 1 read for BufferedIO.
|
||||
int num_read_per_file = 4;
|
||||
#else
|
||||
int num_read_per_file = (!use_direct_io_) ? 7 : 4;
|
||||
#endif
|
||||
int expected = kNumFiles * num_read_per_file;
|
||||
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
|
||||
@@ -244,7 +244,7 @@ TEST_F(DBSecondaryTest, SimpleInternalCompaction) {
|
||||
ASSERT_EQ(largest.user_key().ToString(), "foo");
|
||||
ASSERT_EQ(result.output_level, 1);
|
||||
ASSERT_EQ(result.output_path, this->secondary_path_);
|
||||
ASSERT_EQ(result.stats.num_output_records, 2);
|
||||
ASSERT_EQ(result.num_output_records, 2);
|
||||
ASSERT_GT(result.bytes_written, 0);
|
||||
ASSERT_OK(result.status);
|
||||
}
|
||||
|
||||
Loaded 100 of 420 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user