mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2732f11849 | ||
|
|
71f9e62906 | ||
|
|
5c2be544f5 | ||
|
|
4db41ec2ff | ||
|
|
aac648c1b6 | ||
|
|
e5907aa96a | ||
|
|
388e9f3564 | ||
|
|
f6d4775ed3 | ||
|
|
c2467b141e | ||
|
|
ef0cadd38f | ||
|
|
9d9014d4d0 | ||
|
|
412b7fd2bd | ||
|
|
8a201ce68a | ||
|
|
a53094983b | ||
|
|
cc20520d5f | ||
|
|
36797a43fe | ||
|
|
7c06a1e503 | ||
|
|
d9e7f6a3b9 | ||
|
|
bf61a59a5b | ||
|
|
ec2c2921e6 |
No files matched your search
@@ -547,7 +547,7 @@ jobs:
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000 --use_io_uring=0' blackbox_crash_test_with_atomic_flush
|
||||
- post-steps
|
||||
|
||||
build-linux-crashtest-tiered-storage-bb:
|
||||
@@ -557,7 +557,7 @@ jobs:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "run crashtest"
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' blackbox_crash_test_with_tiered_storage
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800 --use_io_uring=0' blackbox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
@@ -568,7 +568,7 @@ jobs:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "run crashtest"
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' whitebox_crash_test_with_tiered_storage
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800 --use_io_uring=0' whitebox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
|
||||
@@ -96,13 +96,3 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
build-linux-arm-test-full:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev
|
||||
- run: make V=1 J=4 -j4 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -90,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"
|
||||
@@ -107,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' }}
|
||||
@@ -364,7 +352,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000 --use_io_uring=0' blackbox_crash_test_with_atomic_flush
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================= Linux with Sanitizers ===================== #
|
||||
build-linux-clang10-asan:
|
||||
@@ -415,7 +403,7 @@ jobs:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS build only ======================== #
|
||||
# ========================= MacOS with Tests ======================== #
|
||||
build-macos:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
@@ -429,10 +417,9 @@ jobs:
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Build
|
||||
- name: Run all tests
|
||||
run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j16 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS with Tests ======================== #
|
||||
build-macos-cmake:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
@@ -618,14 +605,4 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: maven-site
|
||||
path: "${{ github.workspace }}/java/target/site"
|
||||
build-linux-arm:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: sudo apt-get update && sudo apt-get install -y build-essential
|
||||
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
|
||||
- uses: "./.github/actions/post-steps"
|
||||
path: "${{ github.workspace }}/java/target/site"
|
||||
@@ -47,9 +47,6 @@ package/
|
||||
unity.a
|
||||
tags
|
||||
etags
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_test2
|
||||
|
||||
+6
-49
@@ -44,29 +44,6 @@ project(rocksdb
|
||||
HOMEPAGE_URL https://rocksdb.org/
|
||||
LANGUAGES CXX C ASM)
|
||||
|
||||
if(APPLE)
|
||||
# On macOS Cmake, when cross-compiling, sometimes CMAKE_SYSTEM_PROCESSOR wrongfully stays
|
||||
# the same as CMAKE_HOST_SYSTEM_PROCESSOR regardless the target CPU.
|
||||
# The manual call to set(CMAKE_SYSTEM_PROCESSOR) has to be set after the project() call.
|
||||
# because project() might reset CMAKE_SYSTEM_PROCESSOR back to the value of CMAKE_HOST_SYSTEM_PROCESSOR.
|
||||
# Check if CMAKE_SYSTEM_PROCESSOR is not equal to CMAKE_OSX_ARCHITECTURES
|
||||
if(NOT CMAKE_OSX_ARCHITECTURES STREQUAL "")
|
||||
if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_OSX_ARCHITECTURES)
|
||||
# Split CMAKE_OSX_ARCHITECTURES into a list
|
||||
string(REPLACE ";" " " ARCH_LIST ${CMAKE_OSX_ARCHITECTURES})
|
||||
separate_arguments(ARCH_LIST UNIX_COMMAND ${ARCH_LIST})
|
||||
# Count the number of architectures
|
||||
list(LENGTH ARCH_LIST ARCH_COUNT)
|
||||
# Ensure that exactly one architecture is specified
|
||||
if(NOT ARCH_COUNT EQUAL 1)
|
||||
message(FATAL_ERROR "CMAKE_OSX_ARCHITECTURES must have exactly one value. Current value: ${CMAKE_OSX_ARCHITECTURES}")
|
||||
endif()
|
||||
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_OSX_ARCHITECTURES})
|
||||
message(STATUS "CMAKE_SYSTEM_PROCESSOR is manually set to ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
@@ -189,7 +166,7 @@ else()
|
||||
if(WITH_ZSTD)
|
||||
find_package(zstd REQUIRED)
|
||||
add_definitions(-DZSTD)
|
||||
include_directories(${ZSTD_INCLUDE_DIRS})
|
||||
include_directories(${ZSTD_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS zstd::zstd)
|
||||
endif()
|
||||
endif()
|
||||
@@ -205,7 +182,7 @@ endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4996 /wd4100 /wd4324")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4800 /wd4996 /wd4351 /wd4100 /wd4204 /wd4324")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall -pthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers -Wno-strict-aliasing -Wno-invalid-offsetof")
|
||||
@@ -214,7 +191,6 @@ else()
|
||||
endif()
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
endif()
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
@@ -267,8 +243,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")
|
||||
|
||||
@@ -395,7 +371,7 @@ option(WITH_NUMA "build with NUMA policy support" OFF)
|
||||
if(WITH_NUMA)
|
||||
find_package(NUMA REQUIRED)
|
||||
add_definitions(-DNUMA)
|
||||
include_directories(${NUMA_INCLUDE_DIRS})
|
||||
include_directories(${NUMA_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS NUMA::NUMA)
|
||||
endif()
|
||||
|
||||
@@ -488,8 +464,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 +591,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()
|
||||
@@ -655,7 +629,6 @@ set(SOURCES
|
||||
cache/sharded_cache.cc
|
||||
cache/tiered_secondary_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/attribute_group_iterator_impl.cc
|
||||
db/blob/blob_contents.cc
|
||||
db/blob/blob_fetcher.cc
|
||||
db/blob/blob_file_addition.cc
|
||||
@@ -672,7 +645,6 @@ set(SOURCES
|
||||
db/blob/prefetch_buffer_collection.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
db/coalescing_iterator.cc
|
||||
db/column_family.cc
|
||||
db/compaction/compaction.cc
|
||||
db/compaction/compaction_iterator.cc
|
||||
@@ -693,7 +665,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 +722,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
|
||||
@@ -939,7 +909,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
|
||||
@@ -960,7 +929,6 @@ set(SOURCES
|
||||
utilities/transactions/write_prepared_txn_db.cc
|
||||
utilities/transactions/write_unprepared_txn.cc
|
||||
utilities/transactions/write_unprepared_txn_db.cc
|
||||
utilities/types_util.cc
|
||||
utilities/ttl/db_ttl_impl.cc
|
||||
utilities/wal_filter.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index.cc
|
||||
@@ -1052,7 +1020,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
|
||||
@@ -1060,12 +1027,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()
|
||||
@@ -1375,7 +1336,6 @@ if(WITH_TESTS)
|
||||
db/file_indexer_test.cc
|
||||
db/filename_test.cc
|
||||
db/flush_job_test.cc
|
||||
db/db_follower_test.cc
|
||||
db/import_column_family_test.cc
|
||||
db/listener_test.cc
|
||||
db/log_test.cc
|
||||
@@ -1383,7 +1343,6 @@ if(WITH_TESTS)
|
||||
db/memtable_list_test.cc
|
||||
db/merge_helper_test.cc
|
||||
db/merge_test.cc
|
||||
db/multi_cf_iterator_test.cc
|
||||
db/options_file_test.cc
|
||||
db/perf_context_test.cc
|
||||
db/periodic_task_scheduler_test.cc
|
||||
@@ -1489,7 +1448,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
|
||||
@@ -1500,7 +1458,6 @@ if(WITH_TESTS)
|
||||
utilities/transactions/lock/range/range_locking_test.cc
|
||||
utilities/transactions/timestamped_snapshot_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/types_util_test.cc
|
||||
utilities/util_merge_operators_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
${PLUGIN_TESTS}
|
||||
|
||||
+11
-158
@@ -1,172 +1,25 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 9.4.1 (08/02/2024)
|
||||
### 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`.
|
||||
|
||||
## 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.
|
||||
|
||||
## 8.11.5 (11/26/2025)
|
||||
### 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`.
|
||||
* Backport new value types `kTypeValuePreferredSeqno` and `kTypeColumnFamilyValuePreferredSeqno` introduced in RocksDB 9.1. This fixes a forward compatibility issue to allow reading MANIFEST with entries containing kTypeMaxValid written by RocksDB 9.1+.
|
||||
|
||||
## 8.11.4 (04/09/2024)
|
||||
### 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.
|
||||
* Fixed CMake Javadoc build
|
||||
* Fixed Java `SstFileMetaData` to prevent throwing `java.lang.NoSuchMethodError`
|
||||
|
||||
### Performance Improvements
|
||||
* Improved write throughput to memtable when there's a large number of concurrent writers and allow_concurrent_memtable_write=true(#12545)
|
||||
## 8.11.3 (02/27/2024)
|
||||
* Correct CMake Javadoc and source jar builds
|
||||
|
||||
## 9.3.0 (05/17/2024)
|
||||
### New Features
|
||||
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `GetEntity` API.
|
||||
* Added new `Iterator` property, "rocksdb.iterator.is-value-pinned", for checking whether the `Slice` returned by `Iterator::value()` can be used until the `Iterator` is destroyed.
|
||||
* Optimistic transactions and WriteCommitted pessimistic transactions now support the `MultiGetEntity` API.
|
||||
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `PutEntity` API. Support for read APIs and other write policies (WritePrepared, WriteUnprepared) will be added later.
|
||||
|
||||
### Public API Changes
|
||||
* Exposed block based metadata cache options via C API
|
||||
* Exposed compaction pri via c api.
|
||||
* Add a kAdmPolicyAllowAll option to TieredAdmissionPolicy that admits all blocks evicted from the primary block cache into the compressed secondary cache.
|
||||
|
||||
### Behavior Changes
|
||||
* CompactRange() with change_level=true on a CF with FIFO compaction will return Status::NotSupported().
|
||||
* External file ingestion with FIFO compaction will always ingest to L0.
|
||||
## 8.11.2 (02/16/2024)
|
||||
* Update zlib to 1.3.1 for Java builds
|
||||
|
||||
## 8.11.1 (01/25/2024)
|
||||
### Bug Fixes
|
||||
* Fixed a bug for databases using `DBOptions::allow_2pc == true` (all `TransactionDB`s except `OptimisticTransactionDB`) that have exactly one column family. Due to a missing WAL sync, attempting to open the DB could have returned a `Status::Corruption` with a message like "SST file is ahead of WALs".
|
||||
* Fix a bug in CreateColumnFamilyWithImport() where if multiple CFs are imported, we were not resetting files' epoch number and L0 files can have overlapping key range but the same epoch number.
|
||||
* Fixed race conditions when `ColumnFamilyOptions::inplace_update_support == true` between user overwrites and reads on the same key.
|
||||
* Fix a bug where `CompactFiles()` can compact files of range conflict with other ongoing compactions' when `preclude_last_level_data_seconds > 0` is used
|
||||
* Fixed a false positive `Status::Corruption` reported when reopening a DB that used `DBOptions::recycle_log_file_num > 0` and `DBOptions::wal_compression != kNoCompression`.
|
||||
* While WAL is locked with LockWAL(), some operations like Flush() and IngestExternalFile() are now blocked as they should have been.
|
||||
* Fixed a bug causing stale memory access when using the TieredSecondaryCache with an NVM secondary cache, and a file system that supports return an FS allocated buffer for MultiRead (FSSupportedOps::kFSBuffer is set).
|
||||
|
||||
## 9.2.0 (05/01/2024)
|
||||
### New Features
|
||||
* Added two options `deadline` and `max_size_bytes` for CacheDumper to exit early
|
||||
* 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.
|
||||
* Add an option to `WaitForCompactOptions` - `wait_for_purge` to make `WaitForCompact()` API wait for background purge to complete
|
||||
|
||||
### Public API Changes
|
||||
* DeleteRange() will return NotSupported() if row_cache is configured since they don't work together in some cases.
|
||||
* Deprecated `CompactionOptions::compression` since `CompactionOptions`'s API for configuring compression was incomplete, unsafe, and likely unnecessary
|
||||
* Using `OptionChangeMigration()` to migrate from non-FIFO to FIFO compaction
|
||||
with `Options::compaction_options_fifo.max_table_files_size` > 0 can cause
|
||||
the whole DB to be dropped right after migration if the migrated data is larger than
|
||||
`max_table_files_size`
|
||||
|
||||
### Behavior Changes
|
||||
* Enabling `BlockBasedTableOptions::block_align` is now incompatible (i.e., APIs will return `Status::InvalidArgument`) with more ways of enabling compression: `CompactionOptions::compression`, `ColumnFamilyOptions::compression_per_level`, and `ColumnFamilyOptions::bottommost_compression`.
|
||||
* Changed the default value of `CompactionOptions::compression` to `kDisableCompressionOption`, which means the compression type is determined by the `ColumnFamilyOptions`.
|
||||
* `BlockBasedTableOptions::optimize_filters_for_memory` is now set to true by default. When `partition_filters=false`, this could lead to somewhat increased average RSS memory usage by the block cache, but this "extra" usage is within the allowed memory budget and should make memory usage more consistent (by minimizing internal fragmentation for more kinds of blocks).
|
||||
* Dump all keys for cache dumper impl if `SetDumpFilter()` is not called
|
||||
* `CompactRange()` with `CompactRangeOptions::change_level = true` and `CompactRangeOptions::target_level = 0` that ends up moving more than 1 file from non-L0 to L0 will return `Status::Aborted()`.
|
||||
* On distributed file systems that support file system level checksum verification and reconstruction reads, RocksDB will now retry a file read if the initial read fails RocksDB block level or record level checksum verification. This applies to MANIFEST file reads when the DB is opened, and to SST file reads at all times.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug causing `VerifyFileChecksums()` to return false-positive corruption under `BlockBasedTableOptions::block_align=true`
|
||||
* Provide consistent view of the database across the column families for `NewIterators()` API.
|
||||
* Fixed feature interaction bug for `DeleteRange()` together with `ColumnFamilyOptions::memtable_insert_with_hint_prefix_extractor`. The impact of this bug would likely be corruption or crashing.
|
||||
* Fixed hang in `DisableManualCompactions()` where compactions waiting to be scheduled due to conflicts would not be canceled promptly
|
||||
* Fixed a regression when `ColumnFamilyOptions::max_successive_merges > 0` where the CPU overhead for deciding whether to merge could have increased unless the user had set the option `ColumnFamilyOptions::strict_max_successive_merges`
|
||||
* Fixed a bug in `MultiGet()` and `MultiGetEntity()` together with blob files (`ColumnFamilyOptions::enable_blob_files == true`). An error looking up one of the keys could cause the results to be wrong for other keys for which the statuses were `Status::OK`.
|
||||
* 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.
|
||||
|
||||
## 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.
|
||||
*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.
|
||||
|
||||
### 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.
|
||||
* 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`.
|
||||
|
||||
### Behavior Changes
|
||||
* `RateLimiter`s created by `NewGenericRateLimiter()` no longer modify the refill period when `SetSingleBurstBytes()` is called.
|
||||
* Merge writes will only keep merge operand count within `ColumnFamilyOptions::max_successive_merges` when the key's merge operands are all found in memory, unless `strict_max_successive_merges` is explicitly set.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed `kBlockCacheTier` reads to return `Status::Incomplete` when I/O is needed to fetch a merge chain's base value from a blob file.
|
||||
* Fixed `kBlockCacheTier` reads to return `Status::Incomplete` on table cache miss rather than incorrectly returning an empty value.
|
||||
* Fixed a data race in WalManager that may affect how frequent PurgeObsoleteWALFiles() runs.
|
||||
* Re-enable the recycle_log_file_num option in DBOptions for kPointInTimeRecovery WAL recovery mode, which was previously disabled due to a bug in the recovery logic. This option is incompatible with WriteOptions::disableWAL. A Status::InvalidArgument() will be returned if disableWAL is specified.
|
||||
|
||||
### Performance Improvements
|
||||
* Java API `multiGet()` variants now take advantage of the underlying batched `multiGet()` performance improvements.
|
||||
Before
|
||||
```
|
||||
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
|
||||
MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 64 thrpt 25 6315.541 ± 8.106 ops/s
|
||||
MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 6975.468 ± 68.964 ops/s
|
||||
```
|
||||
After
|
||||
```
|
||||
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
|
||||
MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 64 thrpt 25 7046.739 ± 13.299 ops/s
|
||||
MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 7654.521 ± 60.121 ops/s
|
||||
```
|
||||
|
||||
## 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.
|
||||
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
|
||||
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
|
||||
|
||||
### Public API Changes
|
||||
* Allow setting Stderr logger via C API
|
||||
* Declare one Get and one MultiGet variant as pure virtual, and make all the other variants non-overridable. The methods required to be implemented by derived classes of DB allow returning timestamps. It is up to the implementation to check and return an error if timestamps are not supported. The non-batched MultiGet APIs are reimplemented in terms of batched MultiGet, so callers might see a performance improvement.
|
||||
* 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.
|
||||
* 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".
|
||||
* 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)
|
||||
|
||||
### Behavior Changes
|
||||
* format\_version=6 is the new default setting in BlockBasedTableOptions, for more robust data integrity checking. DBs and SST files written with this setting cannot be read by RocksDB versions before 8.6.0.
|
||||
* Compactions can be scheduled in parallel in an additional scenario: multiple files are marked for compaction within a single column family
|
||||
* For leveled compaction, RocksDB will try to do intra-L0 compaction if the total L0 size is small compared to Lbase (#12214). Users with atomic_flush=true are more likely to see the impact of this change.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a data race in `DBImpl::RenameTempFileToOptionsFile`.
|
||||
* Fix some perf context statistics error in write steps. which include missing write_memtable_time in unordered_write. missing write_memtable_time in PipelineWrite when Writer stat is STATE_PARALLEL_MEMTABLE_WRITER. missing write_delay_time when calling DelayWrite in WriteImplWALOnly function.
|
||||
* Fixed a bug that can, under rare circumstances, cause MultiGet to return an incorrect result for a duplicate key in a MultiGet batch.
|
||||
* Fix a bug where older data of an ingested key can be returned for read when universal compaction is used
|
||||
* Apply appropriate rate limiting and priorities in more places.
|
||||
|
||||
## 8.11.0 (01/19/2024)
|
||||
### New Features
|
||||
|
||||
+4
-8
@@ -126,6 +126,7 @@ most processors made since roughly 2013.
|
||||
* Update XCode: run `xcode-select --install` (or install it from XCode App's settting).
|
||||
* Install via [homebrew](http://brew.sh/).
|
||||
* If you're first time developer in MacOS, you still need to run: `xcode-select --install` in your command line.
|
||||
* run `brew tap homebrew/versions; brew install gcc7 --use-llvm` to install gcc 7 (or higher).
|
||||
* run `brew install rocksdb`
|
||||
|
||||
* **FreeBSD** (11.01):
|
||||
@@ -168,26 +169,21 @@ most processors made since roughly 2013.
|
||||
|
||||
* Install the dependencies for RocksDB:
|
||||
|
||||
`pkg_add gmake gflags snappy bzip2 lz4 zstd git bash findutils gnuwatch`
|
||||
pkg_add gmake gflags snappy bzip2 lz4 zstd git jdk bash findutils gnuwatch
|
||||
|
||||
* Build RocksDB from source:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
git clone https://github.com/facebook/rocksdb.git
|
||||
cd rocksdb
|
||||
gmake static_lib
|
||||
```
|
||||
|
||||
* Build RocksJava from source (optional):
|
||||
* In OpenBSD, JDK depends on XWindows system, so please check that you installed OpenBSD with `xbase` package.
|
||||
* Install dependencies : `pkg_add -v jdk%1.8`
|
||||
```bash
|
||||
|
||||
cd rocksdb
|
||||
export JAVA_HOME=/usr/local/jdk-1.8.0
|
||||
export PATH=$PATH:/usr/local/jdk-1.8.0/bin
|
||||
gmake rocksdbjava SHA256_CMD='sha256 -q'
|
||||
```
|
||||
gmake rocksdbjava
|
||||
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define an important pre-processing macros: `IOS_CROSS_COMPILE`.
|
||||
|
||||
@@ -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)
|
||||
@@ -551,8 +539,7 @@ endif
|
||||
|
||||
ifdef USE_CLANG
|
||||
# Used by some teams in Facebook
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator \
|
||||
-Wimplicit-fallthrough -Wreinterpret-base-class -Wundefined-reinterpret-cast
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator -Wimplicit-fallthrough
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
@@ -648,37 +635,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)
|
||||
|
||||
@@ -1633,9 +1609,6 @@ object_registry_test: $(OBJ_DIR)/utilities/object_registry_test.o $(TEST_LIBRARY
|
||||
ttl_test: $(OBJ_DIR)/utilities/ttl/ttl_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
types_util_test: $(OBJ_DIR)/utilities/types_util_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
write_batch_with_index_test: $(OBJ_DIR)/utilities/write_batch_with_index/write_batch_with_index_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1654,9 +1627,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)
|
||||
|
||||
@@ -1669,9 +1639,6 @@ wal_edit_test: $(OBJ_DIR)/db/wal_edit_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
dbformat_test: $(OBJ_DIR)/db/dbformat_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
multi_cf_iterator_test: $(OBJ_DIR)/db/multi_cf_iterator_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
env_basic_test: $(OBJ_DIR)/env/env_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1948,9 +1915,6 @@ sst_file_reader_test: $(OBJ_DIR)/table/sst_file_reader_test.o $(TEST_LIBRARY) $(
|
||||
db_secondary_test: $(OBJ_DIR)/db/db_secondary_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_follower_test: $(OBJ_DIR)/db/db_follower_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_tracer_test: $(OBJ_DIR)/trace_replay/block_cache_tracer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2367,47 +2331,47 @@ rocksdbjavastaticreleasedocker: rocksdbjavastaticosx rocksdbjavastaticdockerx86
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64le:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_arm64v8-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_arm64v8-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockers390x:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerriscv64:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64lemusl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticdockers390xmusl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
docker run --rm --name rocksdb_linux_s390x-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
@@ -2484,15 +2448,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 17054c85c2dee0e84cbee7ef28d7bae7d0b1630e
|
||||
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: this hack is only needed if building target build_folly in the future
|
||||
perl -pi -e 's,https://github.com/jedisct1/libsodium/releases/download/1.0.17/libsodium-1.0.17.tar.gz,https://download.libsodium.org/libsodium/releases/old/unsupported/libsodium-1.0.17.tar.gz,' third-party/folly/build/fbcode_builder/manifests/libsodium
|
||||
@# 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))
|
||||
|
||||
@@ -2504,6 +2467,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
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
|
||||
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
|
||||
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
@@ -22,7 +21,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"cache/sharded_cache.cc",
|
||||
"cache/tiered_secondary_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/attribute_group_iterator_impl.cc",
|
||||
"db/blob/blob_contents.cc",
|
||||
"db/blob/blob_fetcher.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
@@ -39,7 +37,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/blob/prefetch_buffer_collection.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
"db/coalescing_iterator.cc",
|
||||
"db/column_family.cc",
|
||||
"db/compaction/compaction.cc",
|
||||
"db/compaction/compaction_iterator.cc",
|
||||
@@ -61,7 +58,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/db_impl/db_impl_debug.cc",
|
||||
"db/db_impl/db_impl_experimental.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_readonly.cc",
|
||||
"db/db_impl/db_impl_secondary.cc",
|
||||
@@ -119,7 +115,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"env/env_posix.cc",
|
||||
"env/file_system.cc",
|
||||
"env/file_system_tracer.cc",
|
||||
"env/fs_on_demand.cc",
|
||||
"env/fs_posix.cc",
|
||||
"env/fs_remap.cc",
|
||||
"env/io_posix.cc",
|
||||
@@ -317,7 +312,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",
|
||||
@@ -351,7 +345,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"utilities/transactions/write_unprepared_txn.cc",
|
||||
"utilities/transactions/write_unprepared_txn_db.cc",
|
||||
"utilities/ttl/db_ttl_impl.cc",
|
||||
"utilities/types_util.cc",
|
||||
"utilities/wal_filter.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
@@ -395,7 +388,6 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"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",
|
||||
@@ -413,8 +405,6 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
], 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="cache_bench", srcs=["cache/cache_bench.cc"], deps=[":rocksdb_cache_bench_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
@@ -4628,12 +4618,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"],
|
||||
@@ -4808,12 +4792,6 @@ cpp_unittest_wrapper(name="db_flush_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="db_follower_test",
|
||||
srcs=["db/db_follower_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="db_inplace_update_test",
|
||||
srcs=["db/db_inplace_update_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5248,12 +5226,6 @@ cpp_unittest_wrapper(name="mock_env_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="multi_cf_iterator_test",
|
||||
srcs=["db/multi_cf_iterator_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="object_registry_test",
|
||||
srcs=["utilities/object_registry_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5548,12 +5520,6 @@ cpp_unittest_wrapper(name="ttl_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="types_util_test",
|
||||
srcs=["utilities/types_util_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="udt_util_test",
|
||||
srcs=["util/udt_util_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
|
||||
@@ -193,10 +193,6 @@ 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"]
|
||||
|
||||
@@ -7,7 +7,6 @@ rocksdb_target_header_template = """# This file \100generated by:
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
|
||||
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@@ -163,6 +163,24 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -ldl"
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if test "$ROCKSDB_USE_IO_URING" -ne 0; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
@@ -585,7 +603,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>
|
||||
@@ -596,29 +614,11 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if [ "$ROCKSDB_USE_IO_URING" -ne 0 -a "$PLATFORM" = OS_LINUX ]; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
# -Wshorten-64-to-32 breaks compilation on FreeBSD aarch64 and i386
|
||||
if ! { [ "$TARGET_OS" = FreeBSD -o "$TARGET_OS" = OpenBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
if ! { [ "$TARGET_OS" = FreeBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
# Test whether -Wshorten-64-to-32 is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
@@ -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
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# The file is generated using update_dependencies.sh.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/62de5a92e5f23c661c3d4b9f322e04eb14e7a5bd/11.x/centos8-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1f6edd1ff15c99c861afc8f3cd69054cd974dd64/15/platform010/72a2ff8
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/fed6e93d87571fb162734c86636119d45a398963/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/31a346126a1f3b64812c362511cb04cc1bd40855/1.1.8/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0c65c05468b5a38cef1a106a1f526463e120c8dd/1.2.8/platform010/76ebdda
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/e40bde78650fa91b8405a857e3f10bf336633fb0/11.x/centos7-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/2043340983c032915adbb6f78903dc855b65aee8/12/platform010/9520e0f
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c00dcc6a3e4125c7e8b248e9a79c14b78ac9e0ca/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/0b9c8e4b060eda62f3bc1c6127bbe1256697569b/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/bc9647f7912b131315827d65cb6189c21f381d05/1.1.3/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/a6f5f3f1d063d2d00cd02fc12f0f05fc3ab3a994/1.2.11/platform010/76ebdda
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/09703139cfc376bd8a82642385a0e97726b28287/1.0.6/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ff23d17b932725cc1734a14896a8b67c518ba169/1.9.4/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/576397d8b1d9cea7306ad1e454d5e55caaa2ff1c/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/fecac07861cb829f5e60dbeff0503d3272db73c0/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/0bb3f5756788ce26e2e16a1cb2f2af2c59b51abe/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/5b602edd46fda54cdd7ea45f77dbe4061206e174/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/97cac22a149c2e202917e05d44e87e516b68216f/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/53953ebc4e3eda85ad6fc3e429ba146035e97b90/2018_U5/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/60220d6a5bf7722b9cc239a1368c596619b12060/1.9.1/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/50eace8143eaaea9473deae1f3283e0049e05633/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/5d27e5919771603da06000a027b12f799e58a4f7/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/b62912d333ef33f9760efa6219dbe3fe6abb3b0e/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/6b412770957aa3c8a87e5e0dcd8cc2f45f393bc0/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/52f69816e936e147664ad717eb71a1a0e9dc973a/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/c9cc192099fa84c0dcd0ffeedd44a373ad6e4925/2018_U5/platform010/76ebdda
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/624a2f8f6c93c3c1df8aa4a6255d8202631a6c80/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/39579e8603b48b3540f8b0633f43adf29acccb8b/2.37/centos8-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/cd9cc656d49ecb53797ce4d055e49fde29fd57ff/3.19.0/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/02d9f76aaaba580611cf75e741753c800c7fdc12/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/938dc3f064ef3a48c0446f5b11d788d50b3eb5ee/2.37/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/429a6b3203eb415f1599bd15183659153129188e/3.15.0/platform010/76ebdda
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/363787fa5cac2a8aa20638909210443278fa138e/5.3.4/platform010/9079c97
|
||||
@@ -113,7 +113,7 @@ CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_BIN/scan-build
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
|
||||
@@ -110,7 +110,7 @@ CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_BIN/scan-build"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
|
||||
@@ -137,11 +137,11 @@ then
|
||||
# should be relevant for formatting fixes.
|
||||
FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
|
||||
# Get the differences
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1)
|
||||
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
|
||||
echo "Checking format of uncommitted changes..."
|
||||
fi
|
||||
|
||||
@@ -149,9 +149,6 @@ if [ -z "$diffs" ]
|
||||
then
|
||||
echo "Nothing needs to be reformatted!"
|
||||
exit 0
|
||||
elif [ $? -ne 1 ]; then
|
||||
# CLANG_FORMAT_DIFF will exit on 1 while there is suggested changes.
|
||||
exit $?
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
|
||||
@@ -75,8 +75,8 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos8-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/15/platform010/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/12/platform010/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
@@ -86,7 +86,7 @@ log_variable CLANG_BASE
|
||||
get_lib_base libgcc 11.x platform010
|
||||
get_lib_base glibc 2.34 platform010
|
||||
get_lib_base snappy LATEST platform010
|
||||
get_lib_base zlib 1.2.8 platform010
|
||||
get_lib_base zlib LATEST platform010
|
||||
get_lib_base bzip2 LATEST platform010
|
||||
get_lib_base lz4 LATEST platform010
|
||||
get_lib_base zstd LATEST platform010
|
||||
@@ -99,7 +99,7 @@ get_lib_base liburing LATEST platform010
|
||||
get_lib_base benchmark LATEST platform010
|
||||
|
||||
get_lib_base kernel-headers fb platform010
|
||||
get_lib_base binutils LATEST centos8-native
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST platform010
|
||||
get_lib_base lua 5.3.4 platform010
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -194,7 +194,7 @@ class SharedState {
|
||||
: cv_(&mu_),
|
||||
cache_bench_(cache_bench) {}
|
||||
|
||||
~SharedState() = default;
|
||||
~SharedState() {}
|
||||
|
||||
port::Mutex* GetMutex() { return &mu_; }
|
||||
|
||||
@@ -425,7 +425,7 @@ class CacheBench {
|
||||
}
|
||||
}
|
||||
|
||||
~CacheBench() = default;
|
||||
~CacheBench() {}
|
||||
|
||||
void PopulateCache() {
|
||||
Random64 rnd(FLAGS_seed);
|
||||
|
||||
+1
@@ -129,6 +129,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
|
||||
TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
IncreaseCacheReservationOnFullCache) {
|
||||
;
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
constexpr std::size_t kSmallCacheCapacity = 4 * kSizeDummyEntry;
|
||||
|
||||
Vendored
+3
-63
@@ -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"
|
||||
@@ -107,7 +106,7 @@ class CacheTest : public testing::Test,
|
||||
type_ = GetParam();
|
||||
}
|
||||
|
||||
~CacheTest() override = default;
|
||||
~CacheTest() override {}
|
||||
|
||||
// These functions encode/decode keys in tests cases that use
|
||||
// int keys.
|
||||
@@ -767,9 +766,7 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
auto h = cache.Lookup(key);
|
||||
ASSERT_TRUE(h != nullptr);
|
||||
if (h) {
|
||||
cache.Release(h);
|
||||
}
|
||||
if (h) cache.Release(h);
|
||||
}
|
||||
|
||||
// the cache is over capacity since nothing could be evicted
|
||||
@@ -780,7 +777,7 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
|
||||
if (IsHyperClock()) {
|
||||
// Make sure eviction is triggered.
|
||||
ASSERT_OK(cache.Insert(EncodeKey(-1), nullptr, 1, handles.data()));
|
||||
ASSERT_OK(cache.Insert(EncodeKey(-1), nullptr, 1, &handles[0]));
|
||||
|
||||
// cache is under capacity now since elements were released
|
||||
ASSERT_GE(n, cache.get()->GetUsage());
|
||||
@@ -1018,63 +1015,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
+4
-4
@@ -560,7 +560,7 @@ void BaseClockTable::TrackAndReleaseEvictedEntry(ClockHandle* h) {
|
||||
took_value_ownership =
|
||||
eviction_callback_(ClockCacheShard<FixedHyperClockTable>::ReverseHash(
|
||||
h->GetHash(), &unhashed, hash_seed_),
|
||||
static_cast<Cache::Handle*>(h),
|
||||
reinterpret_cast<Cache::Handle*>(h),
|
||||
h->meta.LoadRelaxed() & ClockHandle::kHitBitMask);
|
||||
}
|
||||
if (!took_value_ownership) {
|
||||
@@ -1428,19 +1428,19 @@ BaseHyperClockCache<Table>::BaseHyperClockCache(
|
||||
|
||||
template <class Table>
|
||||
Cache::ObjectPtr BaseHyperClockCache<Table>::Value(Handle* handle) {
|
||||
return static_cast<const typename Table::HandleImpl*>(handle)->value;
|
||||
return reinterpret_cast<const typename Table::HandleImpl*>(handle)->value;
|
||||
}
|
||||
|
||||
template <class Table>
|
||||
size_t BaseHyperClockCache<Table>::GetCharge(Handle* handle) const {
|
||||
return static_cast<const typename Table::HandleImpl*>(handle)
|
||||
return reinterpret_cast<const typename Table::HandleImpl*>(handle)
|
||||
->GetTotalCharge();
|
||||
}
|
||||
|
||||
template <class Table>
|
||||
const Cache::CacheItemHelper* BaseHyperClockCache<Table>::GetCacheItemHelper(
|
||||
Handle* handle) const {
|
||||
auto h = static_cast<const typename Table::HandleImpl*>(handle);
|
||||
auto h = reinterpret_cast<const typename Table::HandleImpl*>(handle);
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -298,7 +298,7 @@ class ClockCacheTest;
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
struct ClockHandleBasicData : public Cache::Handle {
|
||||
struct ClockHandleBasicData {
|
||||
Cache::ObjectPtr value = nullptr;
|
||||
const Cache::CacheItemHelper* helper = nullptr;
|
||||
// A lossless, reversible hash of the fixed-size (16 byte) cache key. This
|
||||
|
||||
Vendored
+2
-2
@@ -26,7 +26,7 @@ CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
cache_))),
|
||||
disable_cache_(opts.capacity == 0) {}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() = default;
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() {}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
@@ -384,7 +384,7 @@ const Cache::CacheItemHelper* CompressedSecondaryCache::GetHelper(
|
||||
chunks_head = chunks_head->next;
|
||||
tmp_chunk->Free();
|
||||
obj = nullptr;
|
||||
}
|
||||
};
|
||||
}};
|
||||
return &kHelper;
|
||||
} else {
|
||||
|
||||
+6
-6
@@ -33,7 +33,7 @@ const std::string key3 = "____ ____key3";
|
||||
class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
public WithCacheType {
|
||||
public:
|
||||
CompressedSecondaryCacheTestBase() = default;
|
||||
CompressedSecondaryCacheTestBase() {}
|
||||
~CompressedSecondaryCacheTestBase() override = default;
|
||||
|
||||
protected:
|
||||
@@ -1062,7 +1062,7 @@ bool CacheUsageWithinBounds(size_t val1, size_t val2, size_t error) {
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
// double explicit casts
|
||||
@@ -1085,7 +1085,7 @@ TEST_P(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
TEST_P(CompressedSecCacheTestWithTiered,
|
||||
CacheReservationManagerMultipleUpdate) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
@@ -1171,7 +1171,7 @@ TEST_P(CompressedSecCacheTestWithTiered, AdmissionPolicy) {
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdate) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
@@ -1235,7 +1235,7 @@ TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdate) {
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdateWithReservation) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(10 << 20));
|
||||
@@ -1329,7 +1329,7 @@ TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdateWithReservation) {
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, ReservationOverCapacity) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(110 << 20));
|
||||
|
||||
Vendored
+7
-8
@@ -277,8 +277,8 @@ void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
e->SetInHighPriPool(false);
|
||||
e->SetInLowPriPool(true);
|
||||
low_pri_pool_usage_ += e->total_charge;
|
||||
lru_low_pri_ = e;
|
||||
MaintainPoolSize();
|
||||
lru_low_pri_ = e;
|
||||
} else {
|
||||
// Insert "e" to the head of bottom-pri pool.
|
||||
e->next = lru_bottom_pri_->next;
|
||||
@@ -301,7 +301,6 @@ void LRUCacheShard::MaintainPoolSize() {
|
||||
// Overflow last entry in high-pri pool to low-pri pool.
|
||||
lru_low_pri_ = lru_low_pri_->next;
|
||||
assert(lru_low_pri_ != &lru_);
|
||||
assert(lru_low_pri_->InHighPriPool());
|
||||
lru_low_pri_->SetInHighPriPool(false);
|
||||
lru_low_pri_->SetInLowPriPool(true);
|
||||
assert(high_pri_pool_usage_ >= lru_low_pri_->total_charge);
|
||||
@@ -313,7 +312,6 @@ void LRUCacheShard::MaintainPoolSize() {
|
||||
// Overflow last entry in low-pri pool to bottom-pri pool.
|
||||
lru_bottom_pri_ = lru_bottom_pri_->next;
|
||||
assert(lru_bottom_pri_ != &lru_);
|
||||
assert(lru_bottom_pri_->InLowPriPool());
|
||||
lru_bottom_pri_->SetInHighPriPool(false);
|
||||
lru_bottom_pri_->SetInLowPriPool(false);
|
||||
assert(low_pri_pool_usage_ >= lru_bottom_pri_->total_charge);
|
||||
@@ -341,7 +339,8 @@ void LRUCacheShard::NotifyEvicted(
|
||||
MemoryAllocator* alloc = table_.GetAllocator();
|
||||
for (LRUHandle* entry : evicted_handles) {
|
||||
if (eviction_callback_ &&
|
||||
eviction_callback_(entry->key(), static_cast<Cache::Handle*>(entry),
|
||||
eviction_callback_(entry->key(),
|
||||
reinterpret_cast<Cache::Handle*>(entry),
|
||||
entry->HasHit())) {
|
||||
// Callback took ownership of obj; just free handle
|
||||
free(entry);
|
||||
@@ -507,7 +506,7 @@ bool LRUCacheShard::Release(LRUHandle* e, bool /*useful*/,
|
||||
// Only call eviction callback if we're sure no one requested erasure
|
||||
// FIXME: disabled because of test churn
|
||||
if (false && was_in_cache && !erase_if_last_ref && eviction_callback_ &&
|
||||
eviction_callback_(e->key(), static_cast<Cache::Handle*>(e),
|
||||
eviction_callback_(e->key(), reinterpret_cast<Cache::Handle*>(e),
|
||||
e->HasHit())) {
|
||||
// Callback took ownership of obj; just free handle
|
||||
free(e);
|
||||
@@ -662,18 +661,18 @@ LRUCache::LRUCache(const LRUCacheOptions& opts) : ShardedCache(opts) {
|
||||
}
|
||||
|
||||
Cache::ObjectPtr LRUCache::Value(Handle* handle) {
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
return h->value;
|
||||
}
|
||||
|
||||
size_t LRUCache::GetCharge(Handle* handle) const {
|
||||
return static_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
GetShard(0).metadata_charge_policy_);
|
||||
}
|
||||
|
||||
const Cache::CacheItemHelper* LRUCache::GetCacheItemHelper(
|
||||
Handle* handle) const {
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -47,7 +47,7 @@ namespace lru_cache {
|
||||
// LRUCacheShard::Lookup.
|
||||
// While refs > 0, public properties like value and deleter must not change.
|
||||
|
||||
struct LRUHandle : public Cache::Handle {
|
||||
struct LRUHandle {
|
||||
Cache::ObjectPtr value;
|
||||
const Cache::CacheItemHelper* helper;
|
||||
LRUHandle* next_hash;
|
||||
|
||||
Vendored
+10
-29
@@ -32,7 +32,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class LRUCacheTest : public testing::Test {
|
||||
public:
|
||||
LRUCacheTest() = default;
|
||||
LRUCacheTest() {}
|
||||
~LRUCacheTest() override { DeleteCache(); }
|
||||
|
||||
void DeleteCache() {
|
||||
@@ -47,7 +47,7 @@ class LRUCacheTest : public testing::Test {
|
||||
double low_pri_pool_ratio = 1.0,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex) {
|
||||
DeleteCache();
|
||||
cache_ = static_cast<LRUCacheShard*>(
|
||||
cache_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard)));
|
||||
new (cache_) LRUCacheShard(capacity, /*strict_capacity_limit=*/false,
|
||||
high_pri_pool_ratio, low_pri_pool_ratio,
|
||||
@@ -57,11 +57,10 @@ class LRUCacheTest : public testing::Test {
|
||||
}
|
||||
|
||||
void Insert(const std::string& key,
|
||||
Cache::Priority priority = Cache::Priority::LOW,
|
||||
size_t charge = 1) {
|
||||
Cache::Priority priority = Cache::Priority::LOW) {
|
||||
EXPECT_OK(cache_->Insert(key, 0 /*hash*/, nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, charge, nullptr /*handle*/,
|
||||
priority));
|
||||
&kNoopCacheItemHelper, 1 /*charge*/,
|
||||
nullptr /*handle*/, priority));
|
||||
}
|
||||
|
||||
void Insert(char key, Cache::Priority priority = Cache::Priority::LOW) {
|
||||
@@ -145,10 +144,8 @@ class LRUCacheTest : public testing::Test {
|
||||
ASSERT_EQ(num_bottom_pri_pool_keys, bottom_pri_pool_keys);
|
||||
}
|
||||
|
||||
protected:
|
||||
LRUCacheShard* cache_ = nullptr;
|
||||
|
||||
private:
|
||||
LRUCacheShard* cache_ = nullptr;
|
||||
Cache::EvictionCallback eviction_callback_;
|
||||
};
|
||||
|
||||
@@ -381,7 +378,7 @@ class ClockCacheTest : public testing::Test {
|
||||
using Table = typename Shard::Table;
|
||||
using TableOpts = typename Table::Opts;
|
||||
|
||||
ClockCacheTest() = default;
|
||||
ClockCacheTest() {}
|
||||
~ClockCacheTest() override { DeleteShard(); }
|
||||
|
||||
void DeleteShard() {
|
||||
@@ -395,7 +392,8 @@ class ClockCacheTest : public testing::Test {
|
||||
void NewShard(size_t capacity, bool strict_capacity_limit = true,
|
||||
int eviction_effort_cap = 30) {
|
||||
DeleteShard();
|
||||
shard_ = static_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
|
||||
shard_ =
|
||||
reinterpret_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
|
||||
|
||||
TableOpts opts{1 /*value_size*/, eviction_effort_cap};
|
||||
new (shard_)
|
||||
@@ -1979,7 +1977,7 @@ TEST_P(BasicSecondaryCacheTest, BasicWaitAllTest) {
|
||||
ah.priority = Cache::Priority::LOW;
|
||||
cache->StartAsyncLookup(ah);
|
||||
}
|
||||
cache->WaitAll(async_handles.data(), async_handles.size());
|
||||
cache->WaitAll(&async_handles[0], async_handles.size());
|
||||
for (size_t i = 0; i < async_handles.size(); ++i) {
|
||||
SCOPED_TRACE("i = " + std::to_string(i));
|
||||
Cache::Handle* result = async_handles[i].Result();
|
||||
@@ -2706,23 +2704,6 @@ TEST_P(DBSecondaryCacheTest, TestSecondaryCacheOptionTwoDB) {
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
TEST_F(LRUCacheTest, InsertAfterReducingCapacity) {
|
||||
// Fix a bug in LRU cache where it may try to remove a low pri entry's
|
||||
// charge from high pri pool. It causes
|
||||
// Assertion failed: (high_pri_pool_usage_ >= lru_low_pri_->total_charge),
|
||||
// function MaintainPoolSize, file lru_cache.cc
|
||||
NewCache(/*capacity=*/10, /*high_pri_pool_ratio=*/0.2,
|
||||
/*low_pri_pool_ratio=*/0.8);
|
||||
// high pri pool size and usage are both 2
|
||||
Insert("x", Cache::Priority::HIGH);
|
||||
Insert("y", Cache::Priority::HIGH);
|
||||
cache_->SetCapacity(5);
|
||||
// high_pri_pool_size is 1, the next time we try to maintain pool size,
|
||||
// we will move entries from high pri pool to low pri pool
|
||||
// The bug was deducting this entry's charge from high pri pool usage.
|
||||
Insert("aaa", Cache::Priority::LOW, /*charge=*/3);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
+3
-6
@@ -134,14 +134,12 @@ bool CacheWithSecondaryAdapter::EvictionHandler(const Slice& key,
|
||||
auto obj = target_->Value(handle);
|
||||
// Ignore dummy entry
|
||||
if (obj != kDummyObj) {
|
||||
bool force = false;
|
||||
bool hit = false;
|
||||
if (adm_policy_ == TieredAdmissionPolicy::kAdmPolicyAllowCacheHits) {
|
||||
force = was_hit;
|
||||
} else if (adm_policy_ == TieredAdmissionPolicy::kAdmPolicyAllowAll) {
|
||||
force = true;
|
||||
hit = was_hit;
|
||||
}
|
||||
// Spill into secondary cache.
|
||||
secondary_cache_->Insert(key, obj, helper, force).PermitUncheckedError();
|
||||
secondary_cache_->Insert(key, obj, helper, hit).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
// Never takes ownership of obj
|
||||
@@ -663,7 +661,6 @@ std::shared_ptr<Cache> NewTieredCache(const TieredCacheOptions& _opts) {
|
||||
break;
|
||||
case TieredAdmissionPolicy::kAdmPolicyPlaceholder:
|
||||
case TieredAdmissionPolicy::kAdmPolicyAllowCacheHits:
|
||||
case TieredAdmissionPolicy::kAdmPolicyAllowAll:
|
||||
if (opts.nvm_sec_cache) {
|
||||
valid_adm_policy = false;
|
||||
}
|
||||
|
||||
Vendored
+6
-6
@@ -139,7 +139,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
|
||||
explicit ShardedCache(const ShardedCacheOptions& opts)
|
||||
: ShardedCacheBase(opts),
|
||||
shards_(static_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
shards_(reinterpret_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
sizeof(CacheShard) * GetNumShards()))),
|
||||
destroy_shards_in_dtor_(false) {}
|
||||
|
||||
@@ -192,7 +192,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).CreateStandalone(
|
||||
key, hash, obj, helper, charge, allow_uncharged);
|
||||
return static_cast<Handle*>(result);
|
||||
return reinterpret_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper = nullptr,
|
||||
@@ -202,7 +202,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).Lookup(key, hash, helper,
|
||||
create_context, priority, stats);
|
||||
return static_cast<Handle*>(result);
|
||||
return reinterpret_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
void Erase(const Slice& key) override {
|
||||
@@ -212,11 +212,11 @@ class ShardedCache : public ShardedCacheBase {
|
||||
|
||||
bool Release(Handle* handle, bool useful,
|
||||
bool erase_if_last_ref = false) override {
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
auto h = reinterpret_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Release(h, useful, erase_if_last_ref);
|
||||
}
|
||||
bool Ref(Handle* handle) override {
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
auto h = reinterpret_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Ref(h);
|
||||
}
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
@@ -259,7 +259,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
} while (remaining_work);
|
||||
}
|
||||
|
||||
void EraseUnRefEntries() override {
|
||||
virtual void EraseUnRefEntries() override {
|
||||
ForEachShard([](CacheShard* cs) { cs->EraseUnRefEntries(); });
|
||||
}
|
||||
|
||||
|
||||
Vendored
+10
-8
@@ -42,25 +42,27 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
|
||||
|
||||
// This is a no-op as we currently don't allow demotion (i.e
|
||||
// insertion by the upper layer) of evicted blocks.
|
||||
Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
virtual Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Warm up the nvm tier directly
|
||||
Status InsertSaved(const Slice& key, const Slice& saved,
|
||||
CompressionType type = CompressionType::kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
virtual Status InsertSaved(
|
||||
const Slice& key, const Slice& saved,
|
||||
CompressionType type = CompressionType::kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
return nvm_sec_cache_->InsertSaved(key, saved, type, source);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) override;
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override;
|
||||
virtual void WaitAll(
|
||||
std::vector<SecondaryCacheResultHandle*> handles) override;
|
||||
|
||||
private:
|
||||
struct CreateContext : public Cache::CreateContext {
|
||||
|
||||
Vendored
+18
-181
@@ -386,7 +386,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
@@ -400,7 +400,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -414,7 +414,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -428,7 +428,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -442,7 +442,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -456,7 +456,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -470,7 +470,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -484,7 +484,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -528,7 +528,7 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
@@ -542,7 +542,7 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -561,7 +561,7 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
|
||||
@@ -582,7 +582,7 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
|
||||
@@ -629,7 +629,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
@@ -644,7 +644,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -659,7 +659,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -676,7 +676,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
@@ -691,7 +691,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
for (auto value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
@@ -816,174 +816,11 @@ TEST_P(DBTieredAdmPolicyTest, CompressedOnlyTest) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBTieredAdmPolicyTest, CompressedCacheAdmission) {
|
||||
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 10KB 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, 0, GetParam());
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
|
||||
// Disable paranoid_file_checks so that flush will not read back the newly
|
||||
// written file
|
||||
options.paranoid_file_checks = false;
|
||||
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());
|
||||
|
||||
// The second Get (for 5) will evict the data block loaded by the first
|
||||
// Get, which will be admitted into the compressed secondary cache only
|
||||
// for the kAdmPolicyAllowAll policy
|
||||
std::string v = Get(Key(0));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
|
||||
v = Get(Key(5));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
|
||||
if (GetParam() == TieredAdmissionPolicy::kAdmPolicyAllowAll) {
|
||||
ASSERT_GT(compressed_secondary_cache()->TEST_GetUsage(),
|
||||
comp_cache_usage + 128);
|
||||
} else {
|
||||
ASSERT_LT(compressed_secondary_cache()->TEST_GetUsage(),
|
||||
comp_cache_usage + 128);
|
||||
}
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
|
||||
class WrapFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit WrapFS(const std::shared_ptr<FileSystem>& _target)
|
||||
: FileSystemWrapper(_target) {}
|
||||
~WrapFS() override {}
|
||||
const char* Name() const override { return "WrapFS"; }
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
class WrappedRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
public:
|
||||
explicit WrappedRandomAccessFile(
|
||||
std::unique_ptr<FSRandomAccessFile>& file)
|
||||
: FSRandomAccessFileOwnerWrapper(std::move(file)) {}
|
||||
|
||||
IOStatus MultiRead(FSReadRequest* reqs, size_t num_reqs,
|
||||
const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
for (size_t i = 0; i < num_reqs; ++i) {
|
||||
FSReadRequest& req = reqs[i];
|
||||
FSAllocationPtr buffer(new char[req.len], [](void* ptr) {
|
||||
delete[] static_cast<char*>(ptr);
|
||||
});
|
||||
req.fs_scratch = std::move(buffer);
|
||||
req.status = Read(req.offset, req.len, options, &req.result,
|
||||
static_cast<char*>(req.fs_scratch.get()), dbg);
|
||||
}
|
||||
return IOStatus::OK();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
IOStatus s = target()->NewRandomAccessFile(fname, opts, &file, dbg);
|
||||
EXPECT_OK(s);
|
||||
result->reset(new WrappedRandomAccessFile(file));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void SupportedOps(int64_t& supported_ops) override {
|
||||
supported_ops = 1 << FSSupportedOps::kAsyncIO;
|
||||
supported_ops |= 1 << FSSupportedOps::kFSBuffer;
|
||||
}
|
||||
};
|
||||
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<WrapFS> wrap_fs =
|
||||
std::make_shared<WrapFS>(env_->GetFileSystem());
|
||||
std::unique_ptr<Env> wrap_env(new CompositeEnvWrapper(env_, wrap_fs));
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewCache(250 * 1024, 20 * 1024, 256 * 1024,
|
||||
TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
/*ready_before_wait=*/true);
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.env = wrap_env.get();
|
||||
|
||||
options.paranoid_file_checks = false;
|
||||
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());
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back(Key(0));
|
||||
keys.push_back(Key(4));
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
|
||||
|
||||
std::string v = Get(Key(12));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 4u);
|
||||
ASSERT_EQ(nvm_sec_cache()->num_misses(), 4u);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 4u);
|
||||
|
||||
Close();
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTieredAdmPolicyTest, DBTieredAdmPolicyTest,
|
||||
::testing::Values(TieredAdmissionPolicy::kAdmPolicyAuto,
|
||||
TieredAdmissionPolicy::kAdmPolicyPlaceholder,
|
||||
TieredAdmissionPolicy::kAdmPolicyAllowCacheHits,
|
||||
TieredAdmissionPolicy::kAdmPolicyAllowAll));
|
||||
TieredAdmissionPolicy::kAdmPolicyAllowCacheHits));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
Vendored
+4
-3
@@ -155,7 +155,7 @@ class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
using BaseCacheInterface<CachePtr>::BaseCacheInterface;
|
||||
struct TypedAsyncLookupHandle : public Cache::AsyncLookupHandle {
|
||||
TypedHandle* Result() {
|
||||
return static_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
return reinterpret_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -169,7 +169,8 @@ class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
}
|
||||
|
||||
inline TypedHandle* Lookup(const Slice& key, Statistics* stats = nullptr) {
|
||||
return static_cast<TypedHandle*>(this->cache_->BasicLookup(key, stats));
|
||||
return reinterpret_cast<TypedHandle*>(
|
||||
this->cache_->BasicLookup(key, stats));
|
||||
}
|
||||
|
||||
inline void StartAsyncLookup(TypedAsyncLookupHandle& async_handle) {
|
||||
@@ -346,7 +347,7 @@ class FullTypedCacheInterface
|
||||
Priority priority = Priority::LOW, Statistics* stats = nullptr,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier) {
|
||||
if (lowest_used_cache_tier > CacheTier::kVolatileTier) {
|
||||
return static_cast<TypedHandle*>(this->cache_->Lookup(
|
||||
return reinterpret_cast<TypedHandle*>(this->cache_->Lookup(
|
||||
key, GetFullHelper(), create_context, priority, stats));
|
||||
} else {
|
||||
return BasicTypedCacheInterface<TValue, kRole, CachePtr>::Lookup(key,
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
# - Find zstd
|
||||
# Find the zstd compression library and includes
|
||||
#
|
||||
# ZSTD_INCLUDE_DIRS - where to find zstd.h, etc.
|
||||
# ZSTD_LIBRARIES - List of libraries when using zstd.
|
||||
# ZSTD_FOUND - True if zstd found.
|
||||
# zstd_INCLUDE_DIRS - where to find zstd.h, etc.
|
||||
# zstd_LIBRARIES - List of libraries when using zstd.
|
||||
# zstd_FOUND - True if zstd found.
|
||||
|
||||
find_path(ZSTD_INCLUDE_DIRS
|
||||
find_path(zstd_INCLUDE_DIRS
|
||||
NAMES zstd.h
|
||||
HINTS ${zstd_ROOT_DIR}/include)
|
||||
|
||||
find_library(ZSTD_LIBRARIES
|
||||
find_library(zstd_LIBRARIES
|
||||
NAMES zstd
|
||||
HINTS ${zstd_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG ZSTD_LIBRARIES ZSTD_INCLUDE_DIRS)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES zstd_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
ZSTD_LIBRARIES
|
||||
ZSTD_INCLUDE_DIRS)
|
||||
zstd_LIBRARIES
|
||||
zstd_INCLUDE_DIRS)
|
||||
|
||||
if(ZSTD_FOUND AND NOT (TARGET zstd::zstd))
|
||||
if(zstd_FOUND AND NOT (TARGET zstd::zstd))
|
||||
add_library (zstd::zstd UNKNOWN IMPORTED)
|
||||
set_target_properties(zstd::zstd
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${ZSTD_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIRS})
|
||||
IMPORTED_LOCATION ${zstd_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${zstd_INCLUDE_DIRS})
|
||||
endif()
|
||||
+27
-30
@@ -43,13 +43,14 @@ void ArenaWrappedDBIter::Init(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
|
||||
uint64_t version_number, ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, 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);
|
||||
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, db_impl, cfd, expose_blob_index);
|
||||
sv_number_ = version_number;
|
||||
read_options_ = read_options;
|
||||
allow_refresh_ = allow_refresh;
|
||||
@@ -64,44 +65,40 @@ void ArenaWrappedDBIter::Init(
|
||||
Status ArenaWrappedDBIter::Refresh() { return Refresh(nullptr); }
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
if (cfh_ == nullptr || !allow_refresh_) {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr || !allow_refresh_) {
|
||||
return Status::NotSupported("Creating renew iterator is not allowed.");
|
||||
}
|
||||
assert(db_iter_ != nullptr);
|
||||
auto cfd = cfh_->cfd();
|
||||
auto db_impl = cfh_->db();
|
||||
|
||||
// TODO(yiwu): For last_seq_same_as_publish_seq_==false, this is not the
|
||||
// correct behavior. Will be corrected automatically when we take a snapshot
|
||||
// here for the case of WritePreparedTxnDB.
|
||||
uint64_t cur_sv_number = cfd->GetSuperVersionNumber();
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
// If we recreate a new internal iterator below (NewInternalIterator()),
|
||||
// we will pass in read_options_. We need to make sure it
|
||||
// has the right snapshot.
|
||||
read_options_.snapshot = snapshot;
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
|
||||
auto reinit_internal_iter = [&]() {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(db_impl);
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
assert(sv->version_number >= cur_sv_number);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl_, snapshot);
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(read_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd->ioptions()), sv->mutable_cf_options,
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, read_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback_, cfh_, expose_blob_index_,
|
||||
sv->version_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl->NewInternalIterator(
|
||||
read_options_, cfd, sv, &arena_, read_seq,
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, read_seq,
|
||||
/* allow_unprepared_value */ true, /* db_iter */ this);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
};
|
||||
@@ -110,10 +107,10 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
reinit_internal_iter();
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl_, snapshot);
|
||||
// Refresh range-tombstones in MemTable
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
SuperVersion* sv = cfd->GetThreadLocalSuperVersion(db_impl);
|
||||
SuperVersion* sv = cfd_->GetThreadLocalSuperVersion(db_impl_);
|
||||
TEST_SYNC_POINT_CALLBACK("ArenaWrappedDBIter::Refresh:SV", nullptr);
|
||||
auto t = sv->mem->NewRangeTombstoneIterator(
|
||||
read_options_, read_seq, false /* immutable_memtable */);
|
||||
@@ -126,13 +123,13 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
// will be freed during db_iter destruction there.
|
||||
if (memtable_range_tombstone_iter_) {
|
||||
assert(!*memtable_range_tombstone_iter_ ||
|
||||
sv_number_ != cfd->GetSuperVersionNumber());
|
||||
sv_number_ != cfd_->GetSuperVersionNumber());
|
||||
}
|
||||
delete t;
|
||||
} else { // current mutable memtable has range tombstones
|
||||
if (!memtable_range_tombstone_iter_) {
|
||||
delete t;
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
db_impl_->ReturnAndCleanupSuperVersion(cfd_, sv);
|
||||
// The memtable under DBIter did not have range tombstone before
|
||||
// refresh.
|
||||
reinit_internal_iter();
|
||||
@@ -141,13 +138,13 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
delete *memtable_range_tombstone_iter_;
|
||||
*memtable_range_tombstone_iter_ = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
|
||||
&cfd->internal_comparator(), nullptr, nullptr);
|
||||
&cfd_->internal_comparator(), nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
db_impl_->ReturnAndCleanupSuperVersion(cfd_, sv);
|
||||
}
|
||||
// Check again if the latest super version number is changed
|
||||
uint64_t latest_sv_number = cfd->GetSuperVersionNumber();
|
||||
uint64_t latest_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (latest_sv_number != cur_sv_number) {
|
||||
// If the super version number is changed after refreshing,
|
||||
// fallback to Re-Init the InternalIterator
|
||||
@@ -166,14 +163,14 @@ ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
|
||||
uint64_t version_number, ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index, bool allow_refresh) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, ioptions, mutable_cf_options, version, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
cfh, expose_blob_index, allow_refresh);
|
||||
if (cfh != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(cfh, read_callback, expose_blob_index);
|
||||
db_impl, cfd, expose_blob_index, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(db_impl, cfd, read_callback, expose_blob_index);
|
||||
}
|
||||
|
||||
return iter;
|
||||
|
||||
@@ -87,14 +87,15 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool expose_blob_index, bool allow_refresh);
|
||||
|
||||
// Store some parameters so we can refresh the iterator at a later point
|
||||
// with these same params
|
||||
void StoreRefreshInfo(ColumnFamilyHandleImpl* cfh,
|
||||
void StoreRefreshInfo(DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
ReadCallback* read_callback, bool expose_blob_index) {
|
||||
cfh_ = cfh;
|
||||
db_impl_ = db_impl;
|
||||
cfd_ = cfd;
|
||||
read_callback_ = read_callback;
|
||||
expose_blob_index_ = expose_blob_index;
|
||||
}
|
||||
@@ -103,7 +104,8 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
DBIter* db_iter_ = nullptr;
|
||||
Arena arena_;
|
||||
uint64_t sv_number_;
|
||||
ColumnFamilyHandleImpl* cfh_ = nullptr;
|
||||
ColumnFamilyData* cfd_ = nullptr;
|
||||
DBImpl* db_impl_ = nullptr;
|
||||
ReadOptions read_options_;
|
||||
ReadCallback* read_callback_;
|
||||
bool expose_blob_index_ = false;
|
||||
@@ -114,13 +116,13 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
// `cfh` is used for reneweal. If left null, renewal will not
|
||||
// `db_impl` and `cfd` are used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh = nullptr, bool expose_blob_index = false,
|
||||
bool allow_refresh = true);
|
||||
DBImpl* db_impl = nullptr, ColumnFamilyData* cfd = nullptr,
|
||||
bool expose_blob_index = false, bool allow_refresh = true);
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/attribute_group_iterator_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
const AttributeGroups kNoAttributeGroups;
|
||||
const IteratorAttributeGroups kNoIteratorAttributeGroups;
|
||||
|
||||
void AttributeGroupIteratorImpl::AddToAttributeGroups(
|
||||
const autovector<MultiCfIteratorInfo>& items) {
|
||||
for (const auto& item : items) {
|
||||
attribute_groups_.emplace_back(item.cfh, &item.iterator->columns());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "db/multi_cf_iterator_impl.h"
|
||||
#include "rocksdb/attribute_groups.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class AttributeGroupIteratorImpl : public AttributeGroupIterator {
|
||||
public:
|
||||
AttributeGroupIteratorImpl(
|
||||
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
|
||||
AttributeGroupIteratorImpl(const AttributeGroupIteratorImpl&) = delete;
|
||||
AttributeGroupIteratorImpl& operator=(const AttributeGroupIteratorImpl&) =
|
||||
delete;
|
||||
|
||||
bool Valid() const override { return impl_.Valid(); }
|
||||
void SeekToFirst() override { impl_.SeekToFirst(); }
|
||||
void SeekToLast() override { impl_.SeekToLast(); }
|
||||
void Seek(const Slice& target) override { impl_.Seek(target); }
|
||||
void SeekForPrev(const Slice& target) override { impl_.SeekForPrev(target); }
|
||||
void Next() override { impl_.Next(); }
|
||||
void Prev() override { impl_.Prev(); }
|
||||
Slice key() const override { return impl_.key(); }
|
||||
Status status() const override { return impl_.status(); }
|
||||
|
||||
const IteratorAttributeGroups& attribute_groups() const override {
|
||||
assert(Valid());
|
||||
return attribute_groups_;
|
||||
}
|
||||
|
||||
void Reset() { attribute_groups_.clear(); }
|
||||
|
||||
private:
|
||||
MultiCfIteratorImpl impl_;
|
||||
IteratorAttributeGroups attribute_groups_;
|
||||
void AddToAttributeGroups(const autovector<MultiCfIteratorInfo>& items);
|
||||
};
|
||||
|
||||
class EmptyAttributeGroupIterator : public AttributeGroupIterator {
|
||||
public:
|
||||
explicit EmptyAttributeGroupIterator(const Status& s) : status_(s) {}
|
||||
bool Valid() const override { return false; }
|
||||
void Seek(const Slice& /*target*/) override {}
|
||||
void SeekForPrev(const Slice& /*target*/) override {}
|
||||
void SeekToFirst() override {}
|
||||
void SeekToLast() override {}
|
||||
void Next() override { assert(false); }
|
||||
void Prev() override { assert(false); }
|
||||
Slice key() const override {
|
||||
assert(false);
|
||||
return Slice();
|
||||
}
|
||||
Status status() const override { return status_; }
|
||||
|
||||
const IteratorAttributeGroups& attribute_groups() const override {
|
||||
return kNoIteratorAttributeGroups;
|
||||
}
|
||||
|
||||
private:
|
||||
Status status_;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<AttributeGroupIterator> NewAttributeGroupErrorIterator(
|
||||
const Status& status) {
|
||||
return std::make_unique<EmptyAttributeGroupIterator>(status);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -405,7 +405,7 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
requests_buf[0] =
|
||||
BlobReadRequest(key_refs[0], blob_offsets[0], blob_sizes[0],
|
||||
kNoCompression, nullptr, statuses_buf.data());
|
||||
kNoCompression, nullptr, &statuses_buf[0]);
|
||||
requests_buf[1] =
|
||||
BlobReadRequest(key_refs[1], blob_offsets[1], blob_sizes[1] + 1,
|
||||
kNoCompression, nullptr, &statuses_buf[1]);
|
||||
|
||||
+13
-13
@@ -168,8 +168,8 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
|
||||
uint64_t file_size = BlobLogHeader::kSize;
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.emplace_back(key_strs[i]);
|
||||
blobs.emplace_back(blob_strs[i]);
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
file_size += BlobLogRecord::kHeaderSize + keys[i].size() + blobs[i].size();
|
||||
}
|
||||
file_size += BlobLogFooter::kSize;
|
||||
@@ -482,8 +482,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
std::vector<Slice> blobs;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.emplace_back(key_strs[i]);
|
||||
blobs.emplace_back(blob_strs[i]);
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
}
|
||||
|
||||
std::vector<uint64_t> blob_offsets(keys.size());
|
||||
@@ -610,8 +610,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
|
||||
uint64_t file_size = BlobLogHeader::kSize;
|
||||
uint64_t blob_value_bytes = 0;
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.emplace_back(key_strs[i]);
|
||||
blobs.emplace_back(blob_strs[i]);
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
blob_value_bytes += blobs[i].size();
|
||||
file_size += BlobLogRecord::kHeaderSize + keys[i].size() + blobs[i].size();
|
||||
}
|
||||
@@ -802,8 +802,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
|
||||
uint64_t file_size = BlobLogHeader::kSize;
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.emplace_back(key_strs[i]);
|
||||
blobs.emplace_back(blob_strs[i]);
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
file_size += BlobLogRecord::kHeaderSize + keys[i].size() + blobs[i].size();
|
||||
}
|
||||
file_size += BlobLogFooter::kSize;
|
||||
@@ -1164,7 +1164,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[0], file_number,
|
||||
blob_offsets[0], file_size, blob_sizes[0],
|
||||
kNoCompression, nullptr /* prefetch_buffer */,
|
||||
values.data(), nullptr /* bytes_read */));
|
||||
&values[0], nullptr /* bytes_read */));
|
||||
// Release cache handle
|
||||
values[0].Reset();
|
||||
|
||||
@@ -1183,7 +1183,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[0], file_number,
|
||||
blob_offsets[0], file_size, blob_sizes[0],
|
||||
kNoCompression, nullptr /* prefetch_buffer */,
|
||||
values.data(), nullptr /* bytes_read */));
|
||||
&values[0], nullptr /* bytes_read */));
|
||||
ASSERT_EQ(values[0], blobs[0]);
|
||||
ASSERT_TRUE(
|
||||
blob_source.TEST_BlobInCache(file_number, file_size, blob_offsets[0]));
|
||||
@@ -1263,7 +1263,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[0], file_number, blob_offsets[0], file_size,
|
||||
blob_sizes[0], kNoCompression, nullptr /* prefetch_buffer */,
|
||||
values.data(), nullptr /* bytes_read */));
|
||||
&values[0], nullptr /* bytes_read */));
|
||||
ASSERT_EQ(values[0], blobs[0]);
|
||||
|
||||
// Release cache handle
|
||||
@@ -1365,8 +1365,8 @@ class BlobSourceCacheReservationTest : public DBTestBase {
|
||||
|
||||
blob_file_size_ = BlobLogHeader::kSize;
|
||||
for (size_t i = 0; i < kNumBlobs; ++i) {
|
||||
keys_.emplace_back(key_strs_[i]);
|
||||
blobs_.emplace_back(blob_strs_[i]);
|
||||
keys_.push_back({key_strs_[i]});
|
||||
blobs_.push_back({blob_strs_[i]});
|
||||
blob_file_size_ +=
|
||||
BlobLogRecord::kHeaderSize + keys_[i].size() + blobs_[i].size();
|
||||
}
|
||||
|
||||
+31
-106
@@ -418,8 +418,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobs) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -441,8 +441,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobs) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -512,8 +512,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -534,8 +534,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -553,8 +553,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -574,8 +574,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -758,8 +758,8 @@ TEST_F(DBBlobBasicTest, MultiGetWithDirectIO) {
|
||||
//
|
||||
// [offset=0, len=12288]
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -829,8 +829,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
{
|
||||
std::array<PinnableSlice, kNumKeys> values;
|
||||
std::array<Status, kNumKeys> statuses;
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
@@ -843,8 +843,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
{
|
||||
std::array<PinnableSlice, kNumKeys> values;
|
||||
std::array<Status, kNumKeys> statuses;
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_TRUE(statuses[i].IsIncomplete());
|
||||
@@ -858,8 +858,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
{
|
||||
std::array<PinnableSlice, kNumKeys> values;
|
||||
std::array<Status, kNumKeys> statuses;
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
@@ -872,8 +872,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
{
|
||||
std::array<PinnableSlice, kNumKeys> values;
|
||||
std::array<Status, kNumKeys> statuses;
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
@@ -1182,30 +1182,6 @@ TEST_F(DBBlobBasicTest, GetMergeBlobWithPut) {
|
||||
ASSERT_EQ(Get("Key1"), "v1,v2,v3");
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetMergeBlobFromMemoryTier) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge(Key(0), "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Regular `Get()` loads data block to cache.
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), Key(0), &value));
|
||||
ASSERT_EQ("v1,v2", value);
|
||||
|
||||
// Base value blob is still uncached, so an in-memory read will fail.
|
||||
ReadOptions read_options;
|
||||
read_options.read_tier = kBlockCacheTier;
|
||||
ASSERT_TRUE(db_->Get(read_options, Key(0), &value).IsIncomplete());
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetMergeBlobWithPut) {
|
||||
constexpr size_t num_keys = 3;
|
||||
|
||||
@@ -1230,8 +1206,8 @@ TEST_F(DBBlobBasicTest, MultiGetMergeBlobWithPut) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], "v0_0,v0_1,v0_2");
|
||||
@@ -1494,8 +1470,8 @@ TEST_P(DBBlobBasicIOErrorMultiGetTest, MultiGetBlobs_IOError) {
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -1551,57 +1527,6 @@ TEST_P(DBBlobBasicIOErrorMultiGetTest, MultipleBlobFiles) {
|
||||
ASSERT_TRUE(statuses[1].IsIOError());
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetFindTable_IOError) {
|
||||
// Repro test for a specific bug where `MultiGet()` would fail to open a table
|
||||
// in `FindTable()` and then proceed to return raw blob handles for the other
|
||||
// keys.
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
// Force no table cache so every read will preload the SST file.
|
||||
dbfull()->TEST_table_cache()->SetCapacity(0);
|
||||
|
||||
constexpr size_t num_keys = 2;
|
||||
|
||||
constexpr char key1[] = "key1";
|
||||
constexpr char value1[] = "blob1";
|
||||
|
||||
ASSERT_OK(Put(key1, value1));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr char key2[] = "key2";
|
||||
constexpr char value2[] = "blob2";
|
||||
|
||||
ASSERT_OK(Put(key2, value2));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::atomic<int> num_files_opened = 0;
|
||||
// This test would be more realistic if we injected an `IOError` from the
|
||||
// `FileSystem`
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::MultiGet:FindTable", [&](void* status) {
|
||||
num_files_opened++;
|
||||
if (num_files_opened == 2) {
|
||||
Status* s = static_cast<Status*>(status);
|
||||
*s = Status::IOError();
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::array<Slice, num_keys> keys{{key1, key2}};
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_TRUE(statuses[0].IsIOError());
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(value2, values[1]);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ReadBlobCompactionFilter : public CompactionFilter {
|
||||
@@ -1895,7 +1820,7 @@ TEST_F(DBBlobBasicTest, GetEntityBlob) {
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), results.data(), statuses.data());
|
||||
&keys[0], &results[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(results[0].columns(), expected_columns);
|
||||
@@ -1992,8 +1917,8 @@ TEST_F(DBBlobWithTimestampTest, MultiGetBlobs) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -2076,8 +2001,8 @@ TEST_F(DBBlobWithTimestampTest, MultiGetMergeBlobWithPut) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_opts, db_->DefaultColumnFamily(), num_keys, keys.data(),
|
||||
values.data(), statuses.data());
|
||||
db_->MultiGet(read_opts, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], "v0_0,v0_1,v0_2");
|
||||
|
||||
@@ -45,10 +45,10 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
DBBlobIndexTest() : DBTestBase("db_blob_index_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
ColumnFamilyHandle* cfh() { return dbfull()->DefaultColumnFamily(); }
|
||||
ColumnFamilyHandleImpl* cfh_impl() {
|
||||
return static_cast_with_check<ColumnFamilyHandleImpl>(cfh());
|
||||
|
||||
ColumnFamilyData* cfd() {
|
||||
return static_cast_with_check<ColumnFamilyHandleImpl>(cfh())->cfd();
|
||||
}
|
||||
ColumnFamilyData* cfd() { return cfh_impl()->cfd(); }
|
||||
|
||||
Status PutBlobIndex(WriteBatch* batch, const Slice& key,
|
||||
const Slice& blob_index) {
|
||||
@@ -96,9 +96,11 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* GetBlobIterator() {
|
||||
ColumnFamilyData* column_family = cfd();
|
||||
DBImpl* db_impl = dbfull();
|
||||
return db_impl->NewIteratorImpl(
|
||||
ReadOptions(), cfh_impl(), cfd()->GetReferencedSuperVersion(db_impl),
|
||||
ReadOptions(), column_family,
|
||||
column_family->GetReferencedSuperVersion(db_impl),
|
||||
db_impl->GetLatestSequenceNumber(), nullptr /*read_callback*/,
|
||||
true /*expose_blob_index*/);
|
||||
}
|
||||
@@ -323,7 +325,8 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
|
||||
auto check_is_blob = [&](bool is_blob) {
|
||||
return [is_blob](Iterator* iterator) {
|
||||
ASSERT_EQ(is_blob, static_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
|
||||
ASSERT_EQ(is_blob,
|
||||
reinterpret_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+26
-55
@@ -64,14 +64,14 @@ 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,
|
||||
IOStatus* io_status, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCreationReason blob_creation_reason,
|
||||
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping,
|
||||
EventLogger* event_logger, int job_id, TableProperties* table_properties,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping, EventLogger* event_logger,
|
||||
int job_id, TableProperties* table_properties,
|
||||
Env::WriteLifeTimeHint write_hint, const std::string* full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback, Version* version,
|
||||
uint64_t* num_input_entries, uint64_t* memtable_payload_bytes,
|
||||
@@ -83,8 +83,11 @@ Status BuildTable(
|
||||
auto& ioptions = tboptions.ioptions;
|
||||
// Reports the IOStats for flush for every following bytes.
|
||||
const size_t kReportFlushIOStatsEvery = 1048576;
|
||||
OutputValidator output_validator(tboptions.internal_comparator,
|
||||
/*enable_hash=*/paranoid_file_checks);
|
||||
OutputValidator output_validator(
|
||||
tboptions.internal_comparator,
|
||||
/*enable_order_check=*/
|
||||
mutable_cf_options.check_flush_compaction_key_order,
|
||||
/*enable_hash=*/paranoid_file_checks);
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
iter->SeekToFirst();
|
||||
@@ -195,7 +198,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(),
|
||||
@@ -207,64 +210,37 @@ Status BuildTable(
|
||||
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
|
||||
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool logical_strip_timestamp =
|
||||
const bool 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;
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
const Slice& key = c_iter.key();
|
||||
const Slice& value = c_iter.value();
|
||||
ParsedInternalKey ikey = c_iter.ikey();
|
||||
key_after_flush_buf.assign(key.data(), key.size());
|
||||
Slice key_after_flush = key_after_flush_buf;
|
||||
Slice value_after_flush = value;
|
||||
|
||||
const ParsedInternalKey& ikey = c_iter.ikey();
|
||||
Slice key_after_flush = key;
|
||||
// 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) {
|
||||
if (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);
|
||||
SequenceNumber preferred_seqno =
|
||||
seqno_to_time_mapping
|
||||
? seqno_to_time_mapping->GetProximalSeqnoBeforeTime(
|
||||
unix_write_time)
|
||||
: kMaxSequenceNumber;
|
||||
if (preferred_seqno < ikey.sequence) {
|
||||
value_after_flush =
|
||||
PackValueAndSeqno(unpacked_value, preferred_seqno, &value_buf);
|
||||
smallest_preferred_seqno =
|
||||
std::min(smallest_preferred_seqno, preferred_seqno);
|
||||
} else {
|
||||
// Cannot get a useful preferred seqno, convert it to a kTypeValue.
|
||||
UpdateInternalKey(&key_after_flush_buf, ikey.sequence, kTypeValue);
|
||||
ikey = ParsedInternalKey(ikey.user_key, ikey.sequence, kTypeValue);
|
||||
key_after_flush = key_after_flush_buf;
|
||||
value_after_flush = ParsePackedValueForValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a rolling 64-bit hash of the key and values
|
||||
// Note :
|
||||
// Here "key" integrates 'sequence_number'+'kType'+'user key'.
|
||||
s = output_validator.Add(key_after_flush, value_after_flush);
|
||||
s = output_validator.Add(key_after_flush, value);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
builder->Add(key_after_flush, value_after_flush);
|
||||
builder->Add(key_after_flush, value);
|
||||
|
||||
s = meta->UpdateBoundaries(key_after_flush, value_after_flush,
|
||||
ikey.sequence, ikey.type);
|
||||
s = meta->UpdateBoundaries(key_after_flush, value, ikey.sequence,
|
||||
ikey.type);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -291,12 +267,9 @@ Status BuildTable(
|
||||
Slice last_tombstone_start_user_key{};
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
// 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();
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
// TODO(yuzhangyu): handle range deletion for UDT in memtables only.
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone_end, tombstone.seq_,
|
||||
@@ -328,14 +301,11 @@ Status BuildTable(
|
||||
builder->Abandon();
|
||||
} else {
|
||||
SeqnoToTimeMapping relevant_mapping;
|
||||
if (seqno_to_time_mapping) {
|
||||
relevant_mapping.CopyFromSeqnoRange(
|
||||
*seqno_to_time_mapping,
|
||||
std::min(meta->fd.smallest_seqno, smallest_preferred_seqno),
|
||||
meta->fd.largest_seqno);
|
||||
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
|
||||
relevant_mapping.Enforce(tboptions.file_creation_time);
|
||||
}
|
||||
relevant_mapping.CopyFromSeqnoRange(seqno_to_time_mapping,
|
||||
meta->fd.smallest_seqno,
|
||||
meta->fd.largest_seqno);
|
||||
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
|
||||
relevant_mapping.Enforce(tboptions.file_creation_time);
|
||||
builder->SetSeqnoTimeTableProperties(
|
||||
relevant_mapping,
|
||||
ioptions.compaction_style == CompactionStyle::kCompactionStyleFIFO
|
||||
@@ -452,6 +422,7 @@ Status BuildTable(
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
OutputValidator file_validator(tboptions.internal_comparator,
|
||||
/*enable_order_check=*/true,
|
||||
/*enable_hash=*/true);
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
// Generate a rolling 64-bit hash of the key and values
|
||||
|
||||
+4
-3
@@ -23,6 +23,7 @@
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -49,7 +50,7 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
//
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown.
|
||||
Status BuildTable(
|
||||
extern Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
const FileOptions& file_options, TableCache* table_cache,
|
||||
@@ -57,13 +58,13 @@ 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,
|
||||
IOStatus* io_status, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCreationReason blob_creation_reason,
|
||||
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
TableProperties* table_properties = nullptr,
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "util/stderr_logger.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using ROCKSDB_NAMESPACE::BackupEngine;
|
||||
@@ -116,7 +115,6 @@ using ROCKSDB_NAMESPACE::Snapshot;
|
||||
using ROCKSDB_NAMESPACE::SstFileMetaData;
|
||||
using ROCKSDB_NAMESPACE::SstFileWriter;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::StderrLogger;
|
||||
using ROCKSDB_NAMESPACE::TablePropertiesCollectorFactory;
|
||||
using ROCKSDB_NAMESPACE::Transaction;
|
||||
using ROCKSDB_NAMESPACE::TransactionDB;
|
||||
@@ -231,7 +229,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;
|
||||
@@ -447,7 +444,7 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
size_t new_value_len;
|
||||
char* tmp_new_value = (*full_merge_)(
|
||||
state_, merge_in.key.data(), merge_in.key.size(), existing_value_data,
|
||||
existing_value_len, operand_pointers.data(), operand_sizes.data(),
|
||||
existing_value_len, &operand_pointers[0], &operand_sizes[0],
|
||||
static_cast<int>(n), &success, &new_value_len);
|
||||
merge_out->new_value.assign(tmp_new_value, new_value_len);
|
||||
|
||||
@@ -476,9 +473,8 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
unsigned char success;
|
||||
size_t new_value_len;
|
||||
char* tmp_new_value = (*partial_merge_)(
|
||||
state_, key.data(), key.size(), operand_pointers.data(),
|
||||
operand_sizes.data(), static_cast<int>(operand_count), &success,
|
||||
&new_value_len);
|
||||
state_, key.data(), key.size(), &operand_pointers[0], &operand_sizes[0],
|
||||
static_cast<int>(operand_count), &success, &new_value_len);
|
||||
new_value->assign(tmp_new_value, new_value_len);
|
||||
|
||||
if (delete_value_ != nullptr) {
|
||||
@@ -547,7 +543,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());
|
||||
@@ -889,9 +884,9 @@ rocksdb_t* rocksdb_open_and_trim_history(
|
||||
size_t trim_tslen, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.emplace_back(
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
std::string trim_ts_(trim_ts, trim_tslen);
|
||||
@@ -908,7 +903,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;
|
||||
@@ -923,9 +917,9 @@ rocksdb_t* rocksdb_open_column_families(
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.emplace_back(
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
DB* db;
|
||||
@@ -939,7 +933,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;
|
||||
@@ -958,9 +951,9 @@ rocksdb_t* rocksdb_open_column_families_with_ttl(
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
ttls_vec.push_back(ttls[i]);
|
||||
|
||||
column_families.emplace_back(
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::DBWithTTL* db;
|
||||
@@ -975,7 +968,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;
|
||||
@@ -991,9 +983,9 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
unsigned char error_if_wal_file_exists, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.emplace_back(
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
DB* db;
|
||||
@@ -1009,7 +1001,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;
|
||||
@@ -1041,7 +1032,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;
|
||||
@@ -1079,7 +1069,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;
|
||||
}
|
||||
|
||||
@@ -1090,7 +1079,7 @@ rocksdb_column_family_handle_t** rocksdb_create_column_families(
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
std::vector<std::string> names;
|
||||
for (int i = 0; i != num_column_families; ++i) {
|
||||
names.emplace_back(column_family_names[i]);
|
||||
names.push_back(std::string(column_family_names[i]));
|
||||
}
|
||||
SaveError(errptr, db->rep->CreateColumnFamilies(
|
||||
ColumnFamilyOptions(column_family_options->rep), names,
|
||||
@@ -1103,7 +1092,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;
|
||||
@@ -1123,7 +1111,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;
|
||||
}
|
||||
|
||||
@@ -1145,19 +1132,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;
|
||||
}
|
||||
|
||||
@@ -1393,18 +1370,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,
|
||||
@@ -1720,11 +1685,6 @@ void rocksdb_release_snapshot(rocksdb_t* db,
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_snapshot_get_sequence_number(
|
||||
const rocksdb_snapshot_t* snapshot) {
|
||||
return snapshot->rep->GetSequenceNumber();
|
||||
}
|
||||
|
||||
char* rocksdb_property_value(rocksdb_t* db, const char* propname) {
|
||||
std::string tmp;
|
||||
if (db->rep->GetProperty(Slice(propname), &tmp)) {
|
||||
@@ -1912,8 +1872,9 @@ void rocksdb_disable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->DisableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions());
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, unsigned char force,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions(force));
|
||||
}
|
||||
|
||||
void rocksdb_destroy_db(const rocksdb_options_t* options, const char* name,
|
||||
@@ -1979,10 +1940,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;
|
||||
}
|
||||
@@ -1994,15 +1951,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(); }
|
||||
@@ -2268,35 +2216,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;
|
||||
@@ -2819,24 +2738,6 @@ void rocksdb_options_set_block_based_table_factory(
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_top_level_index_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.top_level_index_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_partition_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.partition_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_unpartitioned_pinning_tier(
|
||||
rocksdb_block_based_table_options_t* options, int v) {
|
||||
options->rep.metadata_cache_options.unpartitioned_pinning =
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
rocksdb_cuckoo_table_options_t* rocksdb_cuckoo_options_create() {
|
||||
return new rocksdb_cuckoo_table_options_t;
|
||||
}
|
||||
@@ -2881,9 +2782,7 @@ void rocksdb_options_set_cuckoo_table_factory(
|
||||
void rocksdb_set_options(rocksdb_t* db, int count, const char* const keys[],
|
||||
const char* const values[], char** errptr) {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
for (int i = 0; i < count; i++) {
|
||||
options_map[keys[i]] = values[i];
|
||||
}
|
||||
for (int i = 0; i < count; i++) options_map[keys[i]] = values[i];
|
||||
SaveError(errptr, db->rep->SetOptions(options_map));
|
||||
}
|
||||
|
||||
@@ -2892,9 +2791,7 @@ void rocksdb_set_options_cf(rocksdb_t* db,
|
||||
const char* const keys[],
|
||||
const char* const values[], char** errptr) {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
for (int i = 0; i < count; i++) {
|
||||
options_map[keys[i]] = values[i];
|
||||
}
|
||||
for (int i = 0; i < count; i++) options_map[keys[i]] = values[i];
|
||||
SaveError(errptr, db->rep->SetOptions(handle->rep, options_map));
|
||||
}
|
||||
|
||||
@@ -3022,23 +2919,6 @@ void rocksdb_options_set_cf_paths(rocksdb_options_t* opt,
|
||||
opt->rep.cf_paths = cf_paths;
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_logger_create_stderr_logger(int log_level,
|
||||
const char* prefix) {
|
||||
rocksdb_logger_t* logger = new rocksdb_logger_t;
|
||||
|
||||
if (prefix) {
|
||||
logger->rep = std::make_shared<StderrLogger>(
|
||||
static_cast<InfoLogLevel>(log_level), prefix);
|
||||
} else {
|
||||
logger->rep =
|
||||
std::make_shared<StderrLogger>(static_cast<InfoLogLevel>(log_level));
|
||||
}
|
||||
|
||||
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) {
|
||||
opt->rep.env = (env ? env->rep : nullptr);
|
||||
}
|
||||
@@ -3049,12 +2929,6 @@ void rocksdb_options_set_info_log(rocksdb_options_t* opt, rocksdb_logger_t* l) {
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_options_get_info_log(rocksdb_options_t* opt) {
|
||||
rocksdb_logger_t* info_log = new rocksdb_logger_t;
|
||||
info_log->rep = opt->rep.info_log;
|
||||
return info_log;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_info_log_level(rocksdb_options_t* opt, int v) {
|
||||
opt->rep.info_log_level = static_cast<InfoLogLevel>(v);
|
||||
}
|
||||
@@ -3245,7 +3119,7 @@ void rocksdb_options_set_enable_blob_files(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.enable_blob_files = val;
|
||||
}
|
||||
ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_blob_files;
|
||||
}
|
||||
@@ -3617,6 +3491,35 @@ unsigned char rocksdb_options_get_advise_random_on_open(
|
||||
return opt->rep.advise_random_on_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_access_hint_on_compaction_start(rocksdb_options_t* opt,
|
||||
int v) {
|
||||
switch (v) {
|
||||
case 0:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::NONE;
|
||||
break;
|
||||
case 1:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::NORMAL;
|
||||
break;
|
||||
case 2:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::SEQUENTIAL;
|
||||
break;
|
||||
case 3:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::WILLNEED;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
int rocksdb_options_get_access_hint_on_compaction_start(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.access_hint_on_compaction_start;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_use_adaptive_mutex(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.use_adaptive_mutex = v;
|
||||
@@ -3959,27 +3862,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_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;
|
||||
@@ -4043,14 +3925,6 @@ void rocksdb_options_set_fifo_compaction_options(
|
||||
opt->rep.compaction_options_fifo = fifo->rep;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_pri(rocksdb_options_t* opt, int pri) {
|
||||
opt->rep.compaction_pri = static_cast<ROCKSDB_NAMESPACE::CompactionPri>(pri);
|
||||
}
|
||||
|
||||
int rocksdb_options_get_compaction_pri(rocksdb_options_t* opt) {
|
||||
return opt->rep.compaction_pri;
|
||||
}
|
||||
|
||||
char* rocksdb_options_statistics_get_string(rocksdb_options_t* opt) {
|
||||
ROCKSDB_NAMESPACE::Statistics* statistics = opt->rep.statistics.get();
|
||||
if (statistics) {
|
||||
@@ -4131,16 +4005,6 @@ rocksdb_ratelimiter_t* rocksdb_ratelimiter_create_auto_tuned(
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
rocksdb_ratelimiter_t* rocksdb_ratelimiter_create_with_mode(
|
||||
int64_t rate_bytes_per_sec, int64_t refill_period_us, int32_t fairness,
|
||||
int mode, bool auto_tuned) {
|
||||
rocksdb_ratelimiter_t* rate_limiter = new rocksdb_ratelimiter_t;
|
||||
rate_limiter->rep.reset(
|
||||
NewGenericRateLimiter(rate_bytes_per_sec, refill_period_us, fairness,
|
||||
static_cast<RateLimiter::Mode>(mode), auto_tuned));
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
void rocksdb_ratelimiter_destroy(rocksdb_ratelimiter_t* limiter) {
|
||||
delete limiter;
|
||||
}
|
||||
@@ -4359,8 +4223,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
|
||||
@@ -4732,7 +4601,7 @@ void rocksdb_readoptions_set_io_timeout(rocksdb_readoptions_t* opt,
|
||||
opt->rep.io_timeout = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
ROCKSDB_LIBRARY_API uint64_t
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.io_timeout.count();
|
||||
}
|
||||
@@ -5181,9 +5050,7 @@ void rocksdb_env_lower_high_priority_thread_pool_cpu_priority(
|
||||
}
|
||||
|
||||
void rocksdb_env_destroy(rocksdb_env_t* env) {
|
||||
if (!env->is_default) {
|
||||
delete env->rep;
|
||||
}
|
||||
if (!env->is_default) delete env->rep;
|
||||
delete env;
|
||||
}
|
||||
|
||||
@@ -5567,7 +5434,9 @@ uint64_t rocksdb_livefiles_deletions(const rocksdb_livefiles_t* lf, int index) {
|
||||
return lf->rep[index].num_deletions;
|
||||
}
|
||||
|
||||
void rocksdb_livefiles_destroy(const rocksdb_livefiles_t* lf) { delete lf; }
|
||||
extern void rocksdb_livefiles_destroy(const rocksdb_livefiles_t* lf) {
|
||||
delete lf;
|
||||
}
|
||||
|
||||
void rocksdb_get_options_from_string(const rocksdb_options_t* base_options,
|
||||
const char* opts_str,
|
||||
@@ -5647,7 +5516,7 @@ size_t rocksdb_column_family_metadata_get_level_count(
|
||||
rocksdb_level_metadata_t* rocksdb_column_family_metadata_get_level_metadata(
|
||||
rocksdb_column_family_metadata_t* cf_meta, size_t i) {
|
||||
if (i >= cf_meta->rep.levels.size()) {
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
rocksdb_level_metadata_t* level_meta =
|
||||
(rocksdb_level_metadata_t*)malloc(sizeof(rocksdb_level_metadata_t));
|
||||
@@ -5837,7 +5706,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;
|
||||
}
|
||||
|
||||
@@ -5863,9 +5731,9 @@ rocksdb_transactiondb_t* rocksdb_transactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.emplace_back(
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
TransactionDB* txn_db;
|
||||
@@ -5880,7 +5748,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;
|
||||
@@ -6658,9 +6525,9 @@ rocksdb_optimistictransactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.emplace_back(
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
OptimisticTransactionDB* otxn_db;
|
||||
@@ -6675,7 +6542,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 =
|
||||
|
||||
+18
-150
@@ -50,13 +50,12 @@ static void StartPhase(const char* name) {
|
||||
#endif
|
||||
static const char* GetTempDir(void) {
|
||||
const char* ret = getenv("TEST_TMPDIR");
|
||||
if (ret == NULL || ret[0] == '\0') {
|
||||
if (ret == NULL || ret[0] == '\0')
|
||||
#ifdef OS_WIN
|
||||
ret = getenv("TEMP");
|
||||
#else
|
||||
ret = "/tmp";
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
@@ -207,11 +206,10 @@ static int CmpCompare(void* arg, const char* a, size_t alen, const char* b,
|
||||
size_t n = (alen < blen) ? alen : blen;
|
||||
int r = memcmp(a, b, n);
|
||||
if (r == 0) {
|
||||
if (alen < blen) {
|
||||
if (alen < blen)
|
||||
r = -1;
|
||||
} else if (alen > blen) {
|
||||
else if (alen > blen)
|
||||
r = +1;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -704,10 +702,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_block_based_options_set_block_cache(table_options, cache);
|
||||
rocksdb_block_based_options_set_data_block_index_type(table_options, 1);
|
||||
rocksdb_block_based_options_set_data_block_hash_ratio(table_options, 0.75);
|
||||
rocksdb_block_based_options_set_top_level_index_pinning_tier(table_options,
|
||||
1);
|
||||
rocksdb_block_based_options_set_partition_pinning_tier(table_options, 2);
|
||||
rocksdb_block_based_options_set_unpartitioned_pinning_tier(table_options, 3);
|
||||
rocksdb_options_set_block_based_table_factory(options, table_options);
|
||||
|
||||
rocksdb_options_set_compression(options, rocksdb_no_compression);
|
||||
@@ -724,11 +718,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_ratelimiter(options, rate_limiter);
|
||||
rocksdb_ratelimiter_destroy(rate_limiter);
|
||||
|
||||
rate_limiter = rocksdb_ratelimiter_create_with_mode(1000 * 1024 * 1024,
|
||||
100 * 1000, 10, 0, true);
|
||||
rocksdb_options_set_ratelimiter(options, rate_limiter);
|
||||
rocksdb_ratelimiter_destroy(rate_limiter);
|
||||
|
||||
roptions = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_verify_checksums(roptions, 1);
|
||||
rocksdb_readoptions_set_fill_cache(roptions, 1);
|
||||
@@ -798,11 +787,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);
|
||||
@@ -823,14 +807,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);
|
||||
}
|
||||
|
||||
@@ -846,14 +822,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);
|
||||
@@ -862,13 +830,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);
|
||||
@@ -879,73 +841,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);
|
||||
|
||||
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);
|
||||
CheckCondition(!write_to_manifest);
|
||||
write_to_manifest =
|
||||
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
|
||||
CheckCondition(write_to_manifest);
|
||||
|
||||
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");
|
||||
@@ -983,8 +878,9 @@ int main(int argc, char** argv) {
|
||||
StartPhase("addfile");
|
||||
{
|
||||
rocksdb_envoptions_t* env_opt = rocksdb_envoptions_create();
|
||||
rocksdb_options_t* io_options = rocksdb_options_create();
|
||||
rocksdb_sstfilewriter_t* writer =
|
||||
rocksdb_sstfilewriter_create(env_opt, options);
|
||||
rocksdb_sstfilewriter_create(env_opt, io_options);
|
||||
|
||||
remove(sstfilename);
|
||||
rocksdb_sstfilewriter_open(writer, sstfilename, &err);
|
||||
@@ -1043,6 +939,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
rocksdb_ingestexternalfileoptions_destroy(ing_opt);
|
||||
rocksdb_sstfilewriter_destroy(writer);
|
||||
rocksdb_options_destroy(io_options);
|
||||
rocksdb_envoptions_destroy(env_opt);
|
||||
|
||||
// Delete all keys we just ingested
|
||||
@@ -1416,8 +1313,6 @@ int main(int argc, char** argv) {
|
||||
policy = rocksdb_filterpolicy_create_ribbon_hybrid(8.0, 1);
|
||||
}
|
||||
rocksdb_block_based_options_set_filter_policy(table_options, policy);
|
||||
rocksdb_block_based_options_set_optimize_filters_for_memory(table_options,
|
||||
0);
|
||||
|
||||
// Create new database
|
||||
rocksdb_close(db);
|
||||
@@ -2139,6 +2034,9 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_advise_random_on_open(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(o));
|
||||
|
||||
rocksdb_options_set_access_hint_on_compaction_start(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_access_hint_on_compaction_start(o));
|
||||
|
||||
rocksdb_options_set_use_adaptive_mutex(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(o));
|
||||
|
||||
@@ -2209,9 +2107,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_compaction_style(o, 2);
|
||||
CheckCondition(2 == rocksdb_options_get_compaction_style(o));
|
||||
|
||||
rocksdb_options_set_compaction_pri(o, 4);
|
||||
CheckCondition(4 == rocksdb_options_get_compaction_pri(o));
|
||||
|
||||
rocksdb_options_set_atomic_flush(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_atomic_flush(o));
|
||||
|
||||
@@ -2234,12 +2129,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));
|
||||
@@ -2342,6 +2231,8 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(18 == rocksdb_options_get_stats_dump_period_sec(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_stats_persist_period_sec(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(copy));
|
||||
CheckCondition(3 ==
|
||||
rocksdb_options_get_access_hint_on_compaction_start(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(copy));
|
||||
CheckCondition(19 == rocksdb_options_get_bytes_per_sync(copy));
|
||||
CheckCondition(20 == rocksdb_options_get_wal_bytes_per_sync(copy));
|
||||
@@ -2624,6 +2515,11 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(0 == rocksdb_options_get_advise_random_on_open(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(o));
|
||||
|
||||
rocksdb_options_set_access_hint_on_compaction_start(copy, 2);
|
||||
CheckCondition(2 ==
|
||||
rocksdb_options_get_access_hint_on_compaction_start(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_access_hint_on_compaction_start(o));
|
||||
|
||||
rocksdb_options_set_use_adaptive_mutex(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_use_adaptive_mutex(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(o));
|
||||
@@ -2722,10 +2618,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(1 == rocksdb_options_get_compaction_style(copy));
|
||||
CheckCondition(2 == rocksdb_options_get_compaction_style(o));
|
||||
|
||||
rocksdb_options_set_compaction_pri(copy, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_compaction_pri(copy));
|
||||
CheckCondition(4 == rocksdb_options_get_compaction_pri(o));
|
||||
|
||||
rocksdb_options_set_atomic_flush(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_atomic_flush(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_atomic_flush(o));
|
||||
@@ -2896,30 +2788,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_memory_allocator_destroy(allocator);
|
||||
}
|
||||
|
||||
StartPhase("stderr_logger");
|
||||
{
|
||||
rocksdb_options_t* o_no_prefix = rocksdb_options_create();
|
||||
rocksdb_logger_t* no_prefix_logger =
|
||||
rocksdb_logger_create_stderr_logger(3, NULL);
|
||||
rocksdb_options_set_info_log(o_no_prefix, no_prefix_logger);
|
||||
rocksdb_logger_t* no_prefix_info_log =
|
||||
rocksdb_options_get_info_log(o_no_prefix);
|
||||
CheckCondition(no_prefix_info_log != NULL);
|
||||
rocksdb_logger_destroy(no_prefix_logger);
|
||||
rocksdb_logger_destroy(no_prefix_info_log);
|
||||
rocksdb_options_destroy(o_no_prefix);
|
||||
|
||||
rocksdb_options_t* o_prefix = rocksdb_options_create();
|
||||
rocksdb_logger_t* prefix_logger =
|
||||
rocksdb_logger_create_stderr_logger(3, "some prefix");
|
||||
rocksdb_options_set_info_log(o_prefix, prefix_logger);
|
||||
rocksdb_logger_t* prefix_info_log = rocksdb_options_get_info_log(o_prefix);
|
||||
CheckCondition(prefix_info_log != NULL);
|
||||
rocksdb_logger_destroy(prefix_logger);
|
||||
rocksdb_logger_destroy(prefix_info_log);
|
||||
rocksdb_options_destroy(o_prefix);
|
||||
}
|
||||
|
||||
StartPhase("env");
|
||||
{
|
||||
rocksdb_env_t* e;
|
||||
@@ -3826,7 +3694,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
StartPhase("statistics");
|
||||
{
|
||||
const uint32_t BYTES_WRITTEN_TICKER = 60;
|
||||
const uint32_t BYTES_WRITTEN_TICKER = 40;
|
||||
const uint32_t DB_WRITE_HIST = 1;
|
||||
|
||||
rocksdb_statistics_histogram_data_t* hist =
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/coalescing_iterator.h"
|
||||
|
||||
#include "db/wide/wide_columns_helper.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
void CoalescingIterator::Coalesce(
|
||||
const autovector<MultiCfIteratorInfo>& items) {
|
||||
assert(wide_columns_.empty());
|
||||
MinHeap heap;
|
||||
for (const auto& item : items) {
|
||||
assert(item.iterator);
|
||||
for (auto& column : item.iterator->columns()) {
|
||||
heap.push(WideColumnWithOrder{&column, item.order});
|
||||
}
|
||||
}
|
||||
if (heap.empty()) {
|
||||
return;
|
||||
}
|
||||
wide_columns_.reserve(heap.size());
|
||||
auto current = heap.top();
|
||||
heap.pop();
|
||||
while (!heap.empty()) {
|
||||
int comparison = current.column->name().compare(heap.top().column->name());
|
||||
if (comparison < 0) {
|
||||
wide_columns_.push_back(*current.column);
|
||||
} else if (comparison > 0) {
|
||||
// Shouldn't reach here.
|
||||
// Current item in the heap is greater than the top item in the min heap
|
||||
assert(false);
|
||||
}
|
||||
current = heap.top();
|
||||
heap.pop();
|
||||
}
|
||||
wide_columns_.push_back(*current.column);
|
||||
|
||||
if (WideColumnsHelper::HasDefaultColumn(wide_columns_)) {
|
||||
value_ = WideColumnsHelper::GetDefaultColumn(wide_columns_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "db/multi_cf_iterator_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// EXPERIMENTAL
|
||||
class CoalescingIterator : public Iterator {
|
||||
public:
|
||||
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
|
||||
CoalescingIterator(const CoalescingIterator&) = delete;
|
||||
CoalescingIterator& operator=(const CoalescingIterator&) = delete;
|
||||
|
||||
bool Valid() const override { return impl_.Valid(); }
|
||||
void SeekToFirst() override { impl_.SeekToFirst(); }
|
||||
void SeekToLast() override { impl_.SeekToLast(); }
|
||||
void Seek(const Slice& target) override { impl_.Seek(target); }
|
||||
void SeekForPrev(const Slice& target) override { impl_.SeekForPrev(target); }
|
||||
void Next() override { impl_.Next(); }
|
||||
void Prev() override { impl_.Prev(); }
|
||||
Slice key() const override { return impl_.key(); }
|
||||
Status status() const override { return impl_.status(); }
|
||||
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
return value_;
|
||||
}
|
||||
const WideColumns& columns() const override {
|
||||
assert(Valid());
|
||||
return wide_columns_;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
value_.clear();
|
||||
wide_columns_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
MultiCfIteratorImpl impl_;
|
||||
Slice value_;
|
||||
WideColumns wide_columns_;
|
||||
|
||||
struct WideColumnWithOrder {
|
||||
const WideColumn* column;
|
||||
int order;
|
||||
};
|
||||
|
||||
class WideColumnWithOrderComparator {
|
||||
public:
|
||||
explicit WideColumnWithOrderComparator() {}
|
||||
bool operator()(const WideColumnWithOrder& a,
|
||||
const WideColumnWithOrder& b) const {
|
||||
int c = a.column->name().compare(b.column->name());
|
||||
return c == 0 ? a.order - b.order > 0 : c > 0;
|
||||
}
|
||||
};
|
||||
|
||||
using MinHeap =
|
||||
BinaryHeap<WideColumnWithOrder, WideColumnWithOrderComparator>;
|
||||
|
||||
void Coalesce(const autovector<MultiCfIteratorInfo>& items);
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+14
-77
@@ -96,16 +96,16 @@ const Comparator* ColumnFamilyHandleImpl::GetComparator() const {
|
||||
return cfd()->user_comparator();
|
||||
}
|
||||
|
||||
void GetInternalTblPropCollFactory(
|
||||
void GetIntTblPropCollectorFactory(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
InternalTblPropCollFactories* internal_tbl_prop_coll_factories) {
|
||||
assert(internal_tbl_prop_coll_factories);
|
||||
IntTblPropCollectorFactories* int_tbl_prop_collector_factories) {
|
||||
assert(int_tbl_prop_collector_factories);
|
||||
|
||||
auto& collector_factories = ioptions.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < ioptions.table_properties_collector_factories.size();
|
||||
++i) {
|
||||
assert(collector_factories[i]);
|
||||
internal_tbl_prop_coll_factories->emplace_back(
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
new UserKeyTablePropertiesCollectorFactory(collector_factories[i]));
|
||||
}
|
||||
}
|
||||
@@ -411,13 +411,6 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
"periodic_compaction_seconds does not support FIFO compaction. You"
|
||||
"may want to set option TTL instead.");
|
||||
}
|
||||
if (result.last_level_temperature != Temperature::kUnknown) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options.info_log.get(),
|
||||
"last_level_temperature is ignored with FIFO compaction. Consider "
|
||||
"CompactionOptionsFIFO::file_temperature_age_thresholds.");
|
||||
result.last_level_temperature = Temperature::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
// For universal compaction, `ttl` and `periodic_compaction_seconds` mean the
|
||||
@@ -477,16 +470,13 @@ void SuperVersion::Cleanup() {
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
|
||||
void SuperVersion::Init(
|
||||
ColumnFamilyData* new_cfd, MemTable* new_mem, MemTableListVersion* new_imm,
|
||||
Version* new_current,
|
||||
std::shared_ptr<const SeqnoToTimeMapping> new_seqno_to_time_mapping) {
|
||||
void SuperVersion::Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
MemTableListVersion* new_imm, Version* new_current) {
|
||||
cfd = new_cfd;
|
||||
mem = new_mem;
|
||||
imm = new_imm;
|
||||
current = new_current;
|
||||
full_history_ts_low = cfd->GetFullHistoryTsLow();
|
||||
seqno_to_time_mapping = std::move(new_seqno_to_time_mapping);
|
||||
cfd->Ref();
|
||||
mem->Ref();
|
||||
imm->Ref();
|
||||
@@ -538,7 +528,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_),
|
||||
@@ -583,7 +572,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
GetInternalTblPropCollFactory(ioptions_, &internal_tbl_prop_coll_factories_);
|
||||
GetIntTblPropCollectorFactory(ioptions_, &int_tbl_prop_collector_factories_);
|
||||
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
@@ -904,14 +893,6 @@ uint64_t GetPendingCompactionBytesForCompactionSpeedup(
|
||||
uint64_t size_threshold = bottommost_files_size / kBottommostSizeDivisor;
|
||||
return std::min(size_threshold, slowdown_threshold);
|
||||
}
|
||||
|
||||
uint64_t GetMarkedFileCountForCompactionSpeedup() {
|
||||
// When just one file is marked, it is not clear that parallel compaction will
|
||||
// help the compaction that the user nicely requested to happen sooner. When
|
||||
// multiple files are marked, however, it is pretty clearly helpful, except
|
||||
// for the rare case in which a single compaction grabs all the marked files.
|
||||
return 2;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
std::pair<WriteStallCondition, WriteStallCause>
|
||||
@@ -1093,16 +1074,6 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"compaction "
|
||||
"bytes %" PRIu64,
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
|
||||
} else if (uint64_t(vstorage->FilesMarkedForCompaction().size()) >=
|
||||
GetMarkedFileCountForCompactionSpeedup()) {
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
ROCKS_LOG_INFO(
|
||||
ioptions_.logger,
|
||||
"[%s] Increasing compaction threads because we have %" PRIu64
|
||||
" files marked for compaction",
|
||||
name_.c_str(),
|
||||
uint64_t(vstorage->FilesMarkedForCompaction().size()));
|
||||
} else {
|
||||
write_controller_token_.reset();
|
||||
}
|
||||
@@ -1190,7 +1161,7 @@ bool ColumnFamilyData::RangeOverlapWithCompaction(
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
const autovector<UserKeyRange>& ranges, SuperVersion* super_version,
|
||||
const autovector<Range>& ranges, SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap) {
|
||||
assert(overlap != nullptr);
|
||||
*overlap = false;
|
||||
@@ -1200,12 +1171,11 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
ReadOptions read_opts;
|
||||
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));
|
||||
super_version->imm->AddIterators(read_opts, /*seqno_to_time_mapping=*/nullptr,
|
||||
&merge_iter_builder,
|
||||
merge_iter_builder.AddIterator(
|
||||
super_version->mem->NewIterator(read_opts, &arena));
|
||||
super_version->imm->AddIterators(read_opts, &merge_iter_builder,
|
||||
false /* add_range_tombstone_iter */);
|
||||
ScopedArenaPtr<InternalIterator> memtable_iter(merge_iter_builder.Finish());
|
||||
ScopedArenaIterator memtable_iter(merge_iter_builder.Finish());
|
||||
|
||||
auto read_seq = super_version->current->version_set()->LastSequence();
|
||||
ReadRangeDelAggregator range_del_agg(&internal_comparator_, read_seq);
|
||||
@@ -1235,8 +1205,7 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
|
||||
if (status.ok()) {
|
||||
if (memtable_iter->Valid() &&
|
||||
ucmp->CompareWithoutTimestamp(seek_result.user_key,
|
||||
ranges[i].limit) <= 0) {
|
||||
ucmp->Compare(seek_result.user_key, ranges[i].limit) <= 0) {
|
||||
*overlap = true;
|
||||
} else if (range_del_agg.IsRangeOverlapped(ranges[i].start,
|
||||
ranges[i].limit)) {
|
||||
@@ -1341,12 +1310,7 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
SuperVersion* new_superversion = sv_context->new_superversion.release();
|
||||
new_superversion->mutable_cf_options = mutable_cf_options;
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_,
|
||||
sv_context->new_seqno_to_time_mapping
|
||||
? std::move(sv_context->new_seqno_to_time_mapping)
|
||||
: super_version_
|
||||
? super_version_->ShareSeqnoToTimeMapping()
|
||||
: nullptr);
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_);
|
||||
SuperVersion* old_superversion = super_version_;
|
||||
super_version_ = new_superversion;
|
||||
if (old_superversion == nullptr || old_superversion->current != current() ||
|
||||
@@ -1529,20 +1493,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;
|
||||
}
|
||||
|
||||
@@ -1622,19 +1572,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();
|
||||
|
||||
+28
-63
@@ -26,7 +26,6 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "trace_replay/block_cache_tracer.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/hash_containers.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -169,12 +168,11 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
// destroy without mutex
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
virtual DBImpl* db() const { return db_; }
|
||||
|
||||
uint32_t GetID() const override;
|
||||
const std::string& GetName() const override;
|
||||
Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
const Comparator* GetComparator() const override;
|
||||
virtual uint32_t GetID() const override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
virtual const Comparator* GetComparator() const override;
|
||||
|
||||
private:
|
||||
ColumnFamilyData* cfd_;
|
||||
@@ -195,7 +193,7 @@ class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
||||
internal_cfd_(nullptr) {}
|
||||
|
||||
void SetCFD(ColumnFamilyData* _cfd) { internal_cfd_ = _cfd; }
|
||||
ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
virtual ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
|
||||
private:
|
||||
ColumnFamilyData* internal_cfd_;
|
||||
@@ -220,9 +218,6 @@ struct SuperVersion {
|
||||
// enable UDT feature, this is an empty string.
|
||||
std::string full_history_ts_low;
|
||||
|
||||
// A shared copy of the DB's seqno to time mapping.
|
||||
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping{nullptr};
|
||||
|
||||
// should be called outside the mutex
|
||||
SuperVersion() = default;
|
||||
~SuperVersion();
|
||||
@@ -236,23 +231,8 @@ struct SuperVersion {
|
||||
// that needs to be deleted in to_delete vector. Unrefing those
|
||||
// objects needs to be done in the mutex
|
||||
void Cleanup();
|
||||
void Init(
|
||||
ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
MemTableListVersion* new_imm, Version* new_current,
|
||||
std::shared_ptr<const SeqnoToTimeMapping> new_seqno_to_time_mapping);
|
||||
|
||||
// Share the ownership of the seqno to time mapping object referred to in this
|
||||
// SuperVersion. To be used by the new SuperVersion to be installed after this
|
||||
// one if seqno to time mapping does not change in between these two
|
||||
// SuperVersions. Or to share the ownership of the mapping with a FlushJob.
|
||||
std::shared_ptr<const SeqnoToTimeMapping> ShareSeqnoToTimeMapping() {
|
||||
return seqno_to_time_mapping;
|
||||
}
|
||||
|
||||
// Access the seqno to time mapping object in this SuperVersion.
|
||||
UnownedPtr<const SeqnoToTimeMapping> GetSeqnoToTimeMapping() const {
|
||||
return seqno_to_time_mapping.get();
|
||||
}
|
||||
void Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
MemTableListVersion* new_imm, Version* new_current);
|
||||
|
||||
// The value of dummy is not actually used. kSVInUse takes its address as a
|
||||
// mark in the thread local storage to indicate the SuperVersion is in use
|
||||
@@ -271,21 +251,22 @@ struct SuperVersion {
|
||||
autovector<MemTable*> to_delete;
|
||||
};
|
||||
|
||||
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
|
||||
Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckConcurrentWritesSupported(
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
extern ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
// Wrap user defined table properties collector factories `from cf_options`
|
||||
// into internal ones in internal_tbl_prop_coll_factories. Add a system internal
|
||||
// into internal ones in int_tbl_prop_collector_factories. Add a system internal
|
||||
// one too.
|
||||
void GetInternalTblPropCollFactory(
|
||||
extern void GetIntTblPropCollectorFactory(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
InternalTblPropCollFactories* internal_tbl_prop_coll_factories);
|
||||
IntTblPropCollectorFactories* int_tbl_prop_collector_factories);
|
||||
|
||||
class ColumnFamilySet;
|
||||
|
||||
@@ -329,10 +310,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; }
|
||||
|
||||
@@ -424,7 +401,7 @@ class ColumnFamilyData {
|
||||
// duration of this function.
|
||||
//
|
||||
// Thread-safe
|
||||
Status RangesOverlapWithMemtables(const autovector<UserKeyRange>& ranges,
|
||||
Status RangesOverlapWithMemtables(const autovector<Range>& ranges,
|
||||
SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap);
|
||||
|
||||
@@ -453,8 +430,8 @@ class ColumnFamilyData {
|
||||
return internal_comparator_;
|
||||
}
|
||||
|
||||
const InternalTblPropCollFactories* internal_tbl_prop_coll_factories() const {
|
||||
return &internal_tbl_prop_coll_factories_;
|
||||
const IntTblPropCollectorFactories* int_tbl_prop_collector_factories() const {
|
||||
return &int_tbl_prop_collector_factories_;
|
||||
}
|
||||
|
||||
SuperVersion* GetSuperVersion() { return super_version_; }
|
||||
@@ -572,10 +549,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,
|
||||
@@ -600,17 +573,8 @@ 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_;
|
||||
IntTblPropCollectorFactories int_tbl_prop_collector_factories_;
|
||||
|
||||
const ColumnFamilyOptions initial_cf_options_;
|
||||
const ImmutableOptions ioptions_;
|
||||
@@ -890,17 +854,17 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
// REQUIRES: Seek() called first
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
MemTable* GetMemTable() const override;
|
||||
virtual MemTable* GetMemTable() const override;
|
||||
|
||||
// Returns column family handle for the selected column family
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle() override;
|
||||
virtual ColumnFamilyHandle* GetColumnFamilyHandle() override;
|
||||
|
||||
// Cannot be called while another thread is calling Seek().
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
ColumnFamilyData* current() override { return current_; }
|
||||
virtual ColumnFamilyData* current() override { return current_; }
|
||||
|
||||
private:
|
||||
ColumnFamilySet* column_family_set_;
|
||||
@@ -908,11 +872,12 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
ColumnFamilyHandleInternal handle_;
|
||||
};
|
||||
|
||||
uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
extern uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
|
||||
const Comparator* GetColumnFamilyUserComparator(
|
||||
extern const Comparator* GetColumnFamilyUserComparator(
|
||||
ColumnFamilyHandle* column_family);
|
||||
|
||||
const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family);
|
||||
extern const ImmutableOptions& GetImmutableOptions(
|
||||
ColumnFamilyHandle* column_family);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+109
-368
@@ -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;
|
||||
|
||||
@@ -278,7 +270,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
|
||||
void Reopen(const std::vector<ColumnFamilyOptions> options = {}) {
|
||||
std::vector<std::string> names;
|
||||
for (const auto& name : names_) {
|
||||
for (auto name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
@@ -615,7 +607,7 @@ TEST_P(FlushEmptyCFTestWithParam, FlushEmptyCFTest) {
|
||||
// Preserve file system state up to here to simulate a crash condition.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
std::vector<std::string> names;
|
||||
for (const auto& name : names_) {
|
||||
for (auto name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
@@ -677,7 +669,7 @@ TEST_P(FlushEmptyCFTestWithParam, FlushEmptyCFTest2) {
|
||||
// Preserve file system state up to here to simulate a crash condition.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
std::vector<std::string> names;
|
||||
for (const auto& name : names_) {
|
||||
for (auto name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
@@ -1042,7 +1034,7 @@ TEST_P(ColumnFamilyTest, CrashAfterFlush) {
|
||||
fault_env->SetFilesystemActive(false);
|
||||
|
||||
std::vector<std::string> names;
|
||||
for (const auto& name : names_) {
|
||||
for (auto name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
@@ -2612,92 +2604,90 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(450, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(450);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(205, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(205);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(202, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(202);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(198, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(198);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(599, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(599);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(2001, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(2001);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(100, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(100);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2716,7 +2706,7 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2728,14 +2718,14 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(200, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(200);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(0, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(0);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2754,7 +2744,7 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(60);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2763,14 +2753,14 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
vstorage->set_l0_delay_trigger_count(70);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(71);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2795,21 +2785,19 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit = 200;
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
@@ -2865,55 +2853,53 @@ TEST_P(ColumnFamilyTest, WriteStallTwoColumnFamilies) {
|
||||
MutableCFOptions mutable_cf_options1 = mutable_cf_options;
|
||||
mutable_cf_options1.soft_pending_compaction_bytes_limit = 500;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(70, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(800, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(800);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(700, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(700);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2946,31 +2932,29 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
MutableCFOptions mutable_cf_options1 = mutable_cf_options;
|
||||
mutable_cf_options1.level0_slowdown_writes_trigger = 16;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
@@ -2996,13 +2980,10 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
{
|
||||
// No bottommost data, so debt ratio cannot trigger speedup.
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
|
||||
dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
@@ -3015,88 +2996,18 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
|
||||
// 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);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
|
||||
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);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(8);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
|
||||
const int kParallelismLimit = 3;
|
||||
class AlwaysCompactTpc : public TablePropertiesCollector {
|
||||
public:
|
||||
Status Finish(UserCollectedProperties* /* properties */) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
const char* Name() const override { return "AlwaysCompactTpc"; }
|
||||
|
||||
bool NeedCompact() const override { return true; }
|
||||
};
|
||||
|
||||
class AlwaysCompactTpcf : public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context /* context */) override {
|
||||
return new AlwaysCompactTpc();
|
||||
}
|
||||
|
||||
const char* Name() const override { return "AlwaysCompactTpcf"; }
|
||||
};
|
||||
|
||||
column_family_options_.num_levels = 2;
|
||||
column_family_options_.table_properties_collector_factories.emplace_back(
|
||||
std::make_shared<AlwaysCompactTpcf>());
|
||||
db_options_.max_background_compactions = kParallelismLimit;
|
||||
Open();
|
||||
|
||||
// Make a nonempty last level. Only marked files in upper levels count.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
WaitForCompaction();
|
||||
AssertFilesPerLevel("0,1", 0 /* cf */);
|
||||
|
||||
// Block the compaction thread pool so marked files accumulate in L0.
|
||||
test::SleepingBackgroundTask sleeping_tasks[kParallelismLimit];
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_tasks[i], Env::Priority::LOW);
|
||||
sleeping_tasks[i].WaitUntilSleeping();
|
||||
}
|
||||
|
||||
// Zero marked upper-level files. No speedup.
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("0,1", 0 /* cf */);
|
||||
|
||||
// One marked upper-level file. No speedup.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("1,1", 0 /* cf */);
|
||||
|
||||
// Two marked upper-level files. Speedup.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(kParallelismLimit, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("2,1", 0 /* cf */);
|
||||
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
sleeping_tasks[i].WakeUp();
|
||||
sleeping_tasks[i].WaitUntilDone();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CreateAndDestroyOptions) {
|
||||
std::unique_ptr<ColumnFamilyOptions> cfo(new ColumnFamilyOptions());
|
||||
ColumnFamilyHandle* cfh;
|
||||
@@ -3178,8 +3089,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();
|
||||
@@ -3232,8 +3141,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();
|
||||
@@ -3291,8 +3198,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;
|
||||
@@ -3421,13 +3326,9 @@ TEST_P(ColumnFamilyTest, DISABLED_LogTruncationTest) {
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (!(ParseFileName(filenames[i], &number, &type))) {
|
||||
continue;
|
||||
}
|
||||
if (!(ParseFileName(filenames[i], &number, &type))) continue;
|
||||
|
||||
if (type != kWalFile) {
|
||||
continue;
|
||||
}
|
||||
if (type != kWalFile) continue;
|
||||
|
||||
logfs.push_back(filenames[i]);
|
||||
}
|
||||
@@ -3617,9 +3518,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) {}
|
||||
@@ -3637,27 +3536,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 {
|
||||
@@ -3701,9 +3579,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);
|
||||
@@ -3713,22 +3589,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);
|
||||
@@ -3738,22 +3620,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);
|
||||
@@ -3763,218 +3650,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, 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) {
|
||||
|
||||
@@ -34,8 +34,8 @@ class CompactFilesTest : public testing::Test {
|
||||
// A class which remembers the name of each flushed file.
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() = default;
|
||||
~FlushedFileCollector() override = default;
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
@@ -45,7 +45,7 @@ class FlushedFileCollector : public EventListener {
|
||||
std::vector<std::string> GetFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> result;
|
||||
for (const auto& fname : flushed_files_) {
|
||||
for (auto fname : flushed_files_) {
|
||||
result.push_back(fname);
|
||||
}
|
||||
return result;
|
||||
@@ -159,9 +159,7 @@ TEST_F(CompactFilesTest, MultipleLevel) {
|
||||
// Compact files except the file in L3
|
||||
std::vector<std::string> files;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (i == 3) {
|
||||
continue;
|
||||
}
|
||||
if (i == 3) continue;
|
||||
for (auto& file : meta.levels[i].files) {
|
||||
files.push_back(file.db_path + "/" + file.name);
|
||||
}
|
||||
@@ -230,7 +228,7 @@ TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db)->TEST_WaitForCompact());
|
||||
|
||||
// verify all compaction input files are deleted
|
||||
for (const auto& fname : l0_files) {
|
||||
for (auto fname : l0_files) {
|
||||
ASSERT_EQ(Status::NotFound(), env_->FileExists(fname));
|
||||
}
|
||||
delete db;
|
||||
@@ -441,50 +439,6 @@ TEST_F(CompactFilesTest, SentinelCompressionType) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, CompressionWithBlockAlign) {
|
||||
Options options;
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
std::shared_ptr<FlushedFileCollector> collector =
|
||||
std::make_shared<FlushedFileCollector>();
|
||||
options.listeners.push_back(collector);
|
||||
|
||||
{
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_align = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
}
|
||||
|
||||
std::unique_ptr<DB> db;
|
||||
{
|
||||
DB* _db = nullptr;
|
||||
ASSERT_OK(DB::Open(options, db_name_, &_db));
|
||||
db.reset(_db);
|
||||
}
|
||||
|
||||
ASSERT_OK(db->Put(WriteOptions(), "key", "val"));
|
||||
ASSERT_OK(db->Flush(FlushOptions()));
|
||||
|
||||
// Ensure background work is fully finished including listener callbacks
|
||||
// before accessing listener state.
|
||||
ASSERT_OK(
|
||||
static_cast_with_check<DBImpl>(db.get())->TEST_WaitForBackgroundWork());
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
ASSERT_EQ(1, l0_files.size());
|
||||
|
||||
// We can run this test even without Snappy support because we expect the
|
||||
// `CompactFiles()` to fail before actually invoking Snappy compression.
|
||||
CompactionOptions compaction_opts;
|
||||
compaction_opts.compression = CompressionType::kSnappyCompression;
|
||||
ASSERT_TRUE(db->CompactFiles(compaction_opts, l0_files, 1 /* output_level */)
|
||||
.IsInvalidArgument());
|
||||
|
||||
compaction_opts.compression = CompressionType::kDisableCompressionOption;
|
||||
ASSERT_OK(db->CompactFiles(compaction_opts, l0_files, 1 /* output_level */));
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, GetCompactionJobInfo) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -538,3 +492,4 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
+10
-10
@@ -13,7 +13,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "logging/logging.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/sst_partitioner.h"
|
||||
@@ -22,6 +21,9 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
const uint64_t kRangeTombstoneSentinel =
|
||||
PackSequenceAndType(kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
|
||||
int sstableKeyCompare(const Comparator* uc, const Slice& a, const Slice& b) {
|
||||
auto c = uc->CompareWithoutTimestamp(ExtractUserKey(a), ExtractUserKey(b));
|
||||
if (c != 0) {
|
||||
@@ -160,9 +162,7 @@ std::vector<CompactionInputFiles> Compaction::PopulateWithAtomicBoundaries(
|
||||
AtomicCompactionUnitBoundary cur_boundary;
|
||||
size_t first_atomic_idx = 0;
|
||||
auto add_unit_boundary = [&](size_t to) {
|
||||
if (first_atomic_idx == to) {
|
||||
return;
|
||||
}
|
||||
if (first_atomic_idx == to) return;
|
||||
for (size_t k = first_atomic_idx; k < to; k++) {
|
||||
inputs[i].atomic_compaction_unit_boundaries.push_back(cur_boundary);
|
||||
}
|
||||
@@ -352,9 +352,11 @@ Compaction::Compaction(
|
||||
|
||||
// for the non-bottommost levels, it tries to build files match the target
|
||||
// file size, but not guaranteed. It could be 2x the size of the target size.
|
||||
max_output_file_size_ = bottommost_level_ || grandparents_.empty()
|
||||
? target_output_file_size_
|
||||
: 2 * target_output_file_size_;
|
||||
max_output_file_size_ =
|
||||
bottommost_level_ || grandparents_.empty() ||
|
||||
!_immutable_options.level_compaction_dynamic_file_size
|
||||
? target_output_file_size_
|
||||
: 2 * target_output_file_size_;
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i < inputs_.size(); ++i) {
|
||||
@@ -755,9 +757,7 @@ int InputSummary(const std::vector<FileMetaData*>& files, char* output,
|
||||
AppendHumanBytes(files.at(i)->fd.GetFileSize(), sztxt, 16);
|
||||
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
|
||||
files.at(i)->fd.GetNumber(), sztxt);
|
||||
if (ret < 0 || ret >= sz) {
|
||||
break;
|
||||
}
|
||||
if (ret < 0 || ret >= sz) break;
|
||||
write += ret;
|
||||
}
|
||||
// if files.size() is non-zero, overwrite the last space
|
||||
|
||||
@@ -595,6 +595,6 @@ struct PerKeyPlacementContext {
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
// Return sum of sizes of all files in `files`.
|
||||
uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
extern uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -44,13 +44,6 @@ struct CompactionIterationStats {
|
||||
uint64_t total_blob_bytes_read = 0;
|
||||
uint64_t num_blobs_relocated = 0;
|
||||
uint64_t total_blob_bytes_relocated = 0;
|
||||
|
||||
// TimedPut diagnostics
|
||||
// Total number of kTypeValuePreferredSeqno records encountered.
|
||||
uint64_t num_input_timed_put_records = 0;
|
||||
// Number of kTypeValuePreferredSeqno records we ended up swapping in
|
||||
// preferred seqno.
|
||||
uint64_t num_timed_put_swap_preferred_seqno = 0;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -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),
|
||||
@@ -491,8 +491,6 @@ void CompactionIterator::NextFromInput() {
|
||||
if (ikey_.type == kTypeDeletion || ikey_.type == kTypeSingleDeletion ||
|
||||
ikey_.type == kTypeDeletionWithTimestamp) {
|
||||
iter_stats_.num_input_deletion_records++;
|
||||
} else if (ikey_.type == kTypeValuePreferredSeqno) {
|
||||
iter_stats_.num_input_timed_put_records++;
|
||||
}
|
||||
iter_stats_.total_input_raw_key_bytes += key_.size();
|
||||
iter_stats_.total_input_raw_value_bytes += value_.size();
|
||||
@@ -620,8 +618,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// not compact out. We will keep this Put, but can drop it's data.
|
||||
// (See Optimization 3, below.)
|
||||
if (ikey_.type != kTypeValue && ikey_.type != kTypeBlobIndex &&
|
||||
ikey_.type != kTypeWideColumnEntity &&
|
||||
ikey_.type != kTypeValuePreferredSeqno) {
|
||||
ikey_.type != kTypeWideColumnEntity) {
|
||||
ROCKS_LOG_FATAL(info_log_, "Unexpected key %s for compaction output",
|
||||
ikey_.DebugString(allow_data_in_errors_, true).c_str());
|
||||
assert(false);
|
||||
@@ -635,8 +632,7 @@ void CompactionIterator::NextFromInput() {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex || ikey_.type == kTypeWideColumnEntity ||
|
||||
ikey_.type == kTypeValuePreferredSeqno) {
|
||||
if (ikey_.type == kTypeBlobIndex || ikey_.type == kTypeWideColumnEntity) {
|
||||
ikey_.type = kTypeValue;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
@@ -802,8 +798,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// happened
|
||||
if (next_ikey.type != kTypeValue &&
|
||||
next_ikey.type != kTypeBlobIndex &&
|
||||
next_ikey.type != kTypeWideColumnEntity &&
|
||||
next_ikey.type != kTypeValuePreferredSeqno) {
|
||||
next_ikey.type != kTypeWideColumnEntity) {
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
}
|
||||
|
||||
@@ -973,62 +968,6 @@ void CompactionIterator::NextFromInput() {
|
||||
validity_info_.SetValid(ValidContext::kKeepDel);
|
||||
at_next_ = true;
|
||||
}
|
||||
} else if (ikey_.type == kTypeValuePreferredSeqno &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
(bottommost_level_ ||
|
||||
(compaction_ != nullptr &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)))) {
|
||||
// This section that attempts to swap preferred sequence number will not
|
||||
// be invoked if this is a CompactionIterator created for flush, since
|
||||
// `compaction_` will be nullptr and it's not bottommost either.
|
||||
//
|
||||
// The entries with the same user key and smaller sequence numbers are
|
||||
// all in this earliest snapshot range to be iterated. Since those entries
|
||||
// will be hidden by this entry [rule A], it's safe to swap in the
|
||||
// preferred seqno now.
|
||||
//
|
||||
// It's otherwise not safe to swap in the preferred seqno since it's
|
||||
// possible for entries in earlier snapshots to have sequence number that
|
||||
// is smaller than this entry's sequence number but bigger than this
|
||||
// entry's preferred sequence number. Swapping in the preferred sequence
|
||||
// number will break the internal key ordering invariant for this key.
|
||||
//
|
||||
// A special case involving range deletion is handled separately below.
|
||||
auto [unpacked_value, preferred_seqno] =
|
||||
ParsePackedValueWithSeqno(value_);
|
||||
assert(preferred_seqno < ikey_.sequence || ikey_.sequence == 0);
|
||||
if (range_del_agg_->ShouldDelete(
|
||||
key_, RangeDelPositioningMode::kForwardTraversal)) {
|
||||
++iter_stats_.num_record_drop_hidden;
|
||||
++iter_stats_.num_record_drop_range_del;
|
||||
AdvanceInputIter();
|
||||
} else {
|
||||
InternalKey ikey_after_swap(ikey_.user_key,
|
||||
std::min(preferred_seqno, ikey_.sequence),
|
||||
kTypeValue);
|
||||
Slice ikey_after_swap_slice(*ikey_after_swap.rep());
|
||||
if (range_del_agg_->ShouldDelete(
|
||||
ikey_after_swap_slice,
|
||||
RangeDelPositioningMode::kForwardTraversal)) {
|
||||
// A range tombstone that doesn't cover this kTypeValuePreferredSeqno
|
||||
// entry will end up covering the entry, so it's not safe to swap
|
||||
// preferred sequence number. In this case, we output the entry as is.
|
||||
validity_info_.SetValid(ValidContext::kNewUserKey);
|
||||
} else {
|
||||
if (ikey_.sequence != 0) {
|
||||
iter_stats_.num_timed_put_swap_preferred_seqno++;
|
||||
saved_seq_for_penul_check_ = ikey_.sequence;
|
||||
ikey_.sequence = preferred_seqno;
|
||||
}
|
||||
ikey_.type = kTypeValue;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
key_ = current_key_.GetInternalKey();
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
value_ = unpacked_value;
|
||||
validity_info_.SetValid(ValidContext::kSwapPreferredSeqno);
|
||||
}
|
||||
}
|
||||
} else if (ikey_.type == kTypeMerge) {
|
||||
if (!merge_helper_->HasOperator()) {
|
||||
status_ = Status::InvalidArgument(
|
||||
@@ -1280,21 +1219,6 @@ void CompactionIterator::DecideOutputLevel() {
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
// saved_seq_for_penul_check_ is populated in `NextFromInput` when the
|
||||
// entry's sequence number is non zero and validity context for output this
|
||||
// entry is kSwapPreferredSeqno for use in `DecideOutputLevel`. It should be
|
||||
// cleared out here unconditionally. Otherwise, it may end up getting consumed
|
||||
// incorrectly by a different entry.
|
||||
SequenceNumber seq_for_range_check =
|
||||
(saved_seq_for_penul_check_.has_value() &&
|
||||
saved_seq_for_penul_check_.value() != kMaxSequenceNumber)
|
||||
? saved_seq_for_penul_check_.value()
|
||||
: ikey_.sequence;
|
||||
saved_seq_for_penul_check_ = std::nullopt;
|
||||
ParsedInternalKey ikey_for_range_check = ikey_;
|
||||
if (seq_for_range_check != ikey_.sequence) {
|
||||
ikey_for_range_check.sequence = seq_for_range_check;
|
||||
}
|
||||
if (output_to_penultimate_level_) {
|
||||
// If it's decided to output to the penultimate level, but unsafe to do so,
|
||||
// still output to the last level. For example, moving the data from a lower
|
||||
@@ -1303,7 +1227,7 @@ void CompactionIterator::DecideOutputLevel() {
|
||||
// not from this compaction.
|
||||
// TODO: add statistic for declined output_to_penultimate_level
|
||||
bool safe_to_penultimate_level =
|
||||
compaction_->WithinPenultimateLevelOutputRange(ikey_for_range_check);
|
||||
compaction_->WithinPenultimateLevelOutputRange(ikey_);
|
||||
if (!safe_to_penultimate_level) {
|
||||
output_to_penultimate_level_ = false;
|
||||
// It could happen when disable/enable `last_level_temperature` while
|
||||
@@ -1315,7 +1239,7 @@ void CompactionIterator::DecideOutputLevel() {
|
||||
// snapshot is released before enabling `last_level_temperature` feature
|
||||
// We will migrate the feature to `last_level_temperature` and maybe make
|
||||
// it not dynamically changeable.
|
||||
if (seq_for_range_check > earliest_snapshot_) {
|
||||
if (ikey_.sequence > earliest_snapshot_) {
|
||||
status_ = Status::Corruption(
|
||||
"Unsafe to store Seq later than snapshot in the last level if "
|
||||
"per_key_placement is enabled");
|
||||
|
||||
@@ -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,
|
||||
@@ -412,7 +410,6 @@ class CompactionIterator {
|
||||
kKeepDel = 9,
|
||||
kNewUserKey = 10,
|
||||
kRangeDeletion = 11,
|
||||
kSwapPreferredSeqno = 12,
|
||||
};
|
||||
|
||||
struct ValidityInfo {
|
||||
@@ -439,17 +436,6 @@ class CompactionIterator {
|
||||
// iterator output (or current key in the underlying iterator during
|
||||
// NextFromInput()).
|
||||
ParsedInternalKey ikey_;
|
||||
|
||||
// When a kTypeValuePreferredSeqno entry's preferred seqno is safely swapped
|
||||
// in in this compaction, this field saves its original sequence number for
|
||||
// range checking whether it's safe to be placed on the penultimate level.
|
||||
// This is to ensure when such an entry happens to be the right boundary of
|
||||
// penultimate safe range, it won't get excluded because with the preferred
|
||||
// seqno swapped in, it's now larger than the right boundary (itself before
|
||||
// the swap). This is safe to do, because preferred seqno is swapped in only
|
||||
// when no entries with the same user key exist on lower levels and this entry
|
||||
// is already visible in the earliest snapshot.
|
||||
std::optional<SequenceNumber> saved_seq_for_penul_check_ = kMaxSequenceNumber;
|
||||
// Stores whether ikey_.user_key is valid. If set to false, the user key is
|
||||
// not compared against the current key in the underlying iterator.
|
||||
bool has_current_user_key_ = false;
|
||||
|
||||
@@ -17,14 +17,6 @@
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
std::string ValueWithPreferredSeqno(std::string val,
|
||||
SequenceNumber preferred_seqno = 0) {
|
||||
std::string result = val;
|
||||
PutFixed64(&result, preferred_seqno);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Expects no merging attempts.
|
||||
class NoMergingMergeOp : public MergeOperator {
|
||||
@@ -296,7 +288,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(),
|
||||
@@ -401,17 +392,6 @@ TEST_P(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
// Tests compatibility of TimedPut and SingleDelete. TimedPut should act as if
|
||||
// it's a Put.
|
||||
TEST_P(CompactionIteratorTest, TimedPutAndSingleDelete) {
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValuePreferredSeqno)},
|
||||
{"", "val"}, {}, {}, 5);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValue),
|
||||
test::KeyStr("morning", 2, kTypeValue),
|
||||
@@ -451,31 +431,6 @@ TEST_P(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
// Tests compatibility of TimedPut and Range delete. TimedPut should act as if
|
||||
// it's a Put.
|
||||
TEST_P(CompactionIteratorTest, TimedPutAndRangeDeletion) {
|
||||
InitIterators(
|
||||
{test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("morning", 2, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("night", 3, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("zao5"), ValueWithPreferredSeqno("zao2"),
|
||||
ValueWithPreferredSeqno("wan")},
|
||||
{test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 5);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
c_iter_->key().ToString());
|
||||
ASSERT_EQ(ValueWithPreferredSeqno("zao5"), c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 3, kTypeValuePreferredSeqno),
|
||||
c_iter_->key().ToString());
|
||||
ASSERT_EQ(ValueWithPreferredSeqno("wan"), c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
class Filter : public CompactionFilter {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
@@ -547,11 +502,9 @@ TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
test::KeyStr("f", 25, kTypeValue), test::KeyStr("g", 90, kTypeValue),
|
||||
test::KeyStr("h", 91, kTypeValue), // keep
|
||||
test::KeyStr("i", 95, kTypeMerge), // skip to "z"
|
||||
test::KeyStr("j", 99, kTypeValue),
|
||||
test::KeyStr("k", 100, kTypeValuePreferredSeqno)},
|
||||
test::KeyStr("j", 99, kTypeValue)},
|
||||
{"av50", "am45", "bv60", "bv40", "cv35", "dm70", "em71", "fm65", "fm30",
|
||||
"fv25", "gv90", "hv91", "im95", "jv99",
|
||||
ValueWithPreferredSeqno("kv100")},
|
||||
"fv25", "gv90", "hv91", "im95", "jv99"},
|
||||
{}, {}, kMaxSequenceNumber, kMaxSequenceNumber, &merge_op, &filter);
|
||||
|
||||
// Compaction should output just "a", "e" and "h" keys.
|
||||
@@ -661,87 +614,87 @@ TEST_P(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
EXPECT_EQ(2, filter.last_seen.load());
|
||||
}
|
||||
|
||||
class Filter : public CompactionFilter {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
|
||||
// See InitIterators() call below for the sequence of keys and their
|
||||
// filtering decisions. Here we closely assert that compaction filter is
|
||||
// called with the expected keys and only them, and with the right values.
|
||||
if (k == "a") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
EXPECT_EQ("av1", v);
|
||||
return Decision::kKeep;
|
||||
} else if (k == "b") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
return Decision::kKeep;
|
||||
} else if (k == "c") {
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
ADD_FAILURE();
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest.SingleMergeOperand::Filter";
|
||||
}
|
||||
};
|
||||
|
||||
class SingleMergeOp : public MergeOperator {
|
||||
public:
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
// See InitIterators() call below for why "c" is the only key for which
|
||||
// FullMergeV2 should be called.
|
||||
EXPECT_EQ("c", merge_in.key.ToString());
|
||||
|
||||
std::string temp_value;
|
||||
if (merge_in.existing_value != nullptr) {
|
||||
temp_value = merge_in.existing_value->ToString();
|
||||
}
|
||||
|
||||
for (auto& operand : merge_in.operand_list) {
|
||||
temp_value.append(operand.ToString());
|
||||
}
|
||||
merge_out->new_value = temp_value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* /*logger*/) const override {
|
||||
std::string string_key = key.ToString();
|
||||
EXPECT_TRUE(string_key == "a" || string_key == "b");
|
||||
|
||||
if (string_key == "a") {
|
||||
EXPECT_EQ(1, operand_list.size());
|
||||
} else if (string_key == "b") {
|
||||
EXPECT_EQ(2, operand_list.size());
|
||||
}
|
||||
|
||||
std::string temp_value;
|
||||
for (auto& operand : operand_list) {
|
||||
temp_value.append(operand.ToString());
|
||||
}
|
||||
swap(temp_value, *new_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest SingleMergeOp";
|
||||
}
|
||||
|
||||
bool AllowSingleOperand() const override { return true; }
|
||||
};
|
||||
|
||||
TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
class Filter : public CompactionFilter {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
|
||||
// See InitIterators() call below for the sequence of keys and their
|
||||
// filtering decisions. Here we closely assert that compaction filter is
|
||||
// called with the expected keys and only them, and with the right values.
|
||||
if (k == "a") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
EXPECT_EQ("av1", v);
|
||||
return Decision::kKeep;
|
||||
} else if (k == "b") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
return Decision::kKeep;
|
||||
} else if (k == "c") {
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
ADD_FAILURE();
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest.SingleMergeOperand::Filter";
|
||||
}
|
||||
};
|
||||
|
||||
class SingleMergeOp : public MergeOperator {
|
||||
public:
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
// See InitIterators() call below for why "c" is the only key for which
|
||||
// FullMergeV2 should be called.
|
||||
EXPECT_EQ("c", merge_in.key.ToString());
|
||||
|
||||
std::string temp_value;
|
||||
if (merge_in.existing_value != nullptr) {
|
||||
temp_value = merge_in.existing_value->ToString();
|
||||
}
|
||||
|
||||
for (auto& operand : merge_in.operand_list) {
|
||||
temp_value.append(operand.ToString());
|
||||
}
|
||||
merge_out->new_value = temp_value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* /*logger*/) const override {
|
||||
std::string string_key = key.ToString();
|
||||
EXPECT_TRUE(string_key == "a" || string_key == "b");
|
||||
|
||||
if (string_key == "a") {
|
||||
EXPECT_EQ(1, operand_list.size());
|
||||
} else if (string_key == "b") {
|
||||
EXPECT_EQ(2, operand_list.size());
|
||||
}
|
||||
|
||||
std::string temp_value;
|
||||
for (auto& operand : operand_list) {
|
||||
temp_value.append(operand.ToString());
|
||||
}
|
||||
swap(temp_value, *new_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest SingleMergeOp";
|
||||
}
|
||||
|
||||
bool AllowSingleOperand() const override { return true; }
|
||||
};
|
||||
|
||||
SingleMergeOp merge_op;
|
||||
Filter filter;
|
||||
InitIterators(
|
||||
@@ -766,24 +719,6 @@ TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
ASSERT_EQ("cv1cv2", c_iter_->value().ToString());
|
||||
}
|
||||
|
||||
// Tests compatibility of TimedPut and Merge operation. When a TimedPut is
|
||||
// merged with some merge operand in compaction, it will become a regular Put
|
||||
// and lose its preferred sequence number.
|
||||
TEST_P(CompactionIteratorTest, TimedPutAndMerge) {
|
||||
SingleMergeOp merge_op;
|
||||
Filter filter;
|
||||
InitIterators({test::KeyStr("c", 90, kTypeMerge),
|
||||
test::KeyStr("c", 80, kTypeValuePreferredSeqno)},
|
||||
{"cv2", ValueWithPreferredSeqno("cv1")}, {}, {},
|
||||
kMaxSequenceNumber, kMaxSequenceNumber, &merge_op, &filter);
|
||||
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("c", 90, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_EQ("cv1cv2", c_iter_->value().ToString());
|
||||
}
|
||||
|
||||
// In bottommost level, values earlier than earliest snapshot can be output
|
||||
// with sequence = 0.
|
||||
TEST_P(CompactionIteratorTest, ZeroOutSequenceAtBottomLevel) {
|
||||
@@ -1028,22 +963,6 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Value) {
|
||||
{"v4", "v3", "v1"}, 3 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_TimedPut) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest({test::KeyStr("foo", 4, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 3, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 2, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 1, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("v4"), ValueWithPreferredSeqno("v3"),
|
||||
ValueWithPreferredSeqno("v2"), ValueWithPreferredSeqno("v1")},
|
||||
{test::KeyStr("foo", 4, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 3, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 1, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("v4"), ValueWithPreferredSeqno("v3"),
|
||||
ValueWithPreferredSeqno("v1")},
|
||||
3 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Deletion) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest(
|
||||
@@ -1209,149 +1128,6 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
2 /* earliest_write_conflict_snapshot */);
|
||||
}
|
||||
|
||||
// Same as above but with a value with preferred seqno entry. In addition to the
|
||||
// value getting trimmed, the type of the KV is changed to kTypeValue.
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
KeepSingleDeletionForWriteConflictChecking_TimedPut) {
|
||||
AddSnapshot(2, 0);
|
||||
RunTest({test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValuePreferredSeqno)},
|
||||
{"", ValueWithPreferredSeqno("v1")},
|
||||
{test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValue)},
|
||||
{"", ""}, 2 /* last_committed_seq */, nullptr /* merge_operator */,
|
||||
nullptr /* compaction_filter */, false /* bottommost_level */,
|
||||
2 /* earliest_write_conflict_snapshot */);
|
||||
}
|
||||
|
||||
// Tests when a kTypeValuePreferredSeqno entry can have its preferred sequence
|
||||
// number swapped in. The required and sufficient conditions for an entry's
|
||||
// preferred sequence number to get swapped in are:
|
||||
// 1) The entry is visible to the earliest snapshot, AND
|
||||
// 2) No more entries with the same user key on lower levels, AND
|
||||
// This is either because:
|
||||
// 2a) This is a compaction to the bottommost level, OR
|
||||
// 2b) Keys do not exist beyond output level
|
||||
// 3) The entry will not resurface a range deletion entry after swapping in the
|
||||
// preferred sequence number.
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_NotVisibleToEarliestSnapshot_NoSwapPreferredSeqno) {
|
||||
AddSnapshot(3);
|
||||
RunTest({test::KeyStr("bar", 5, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("bv2", 2)},
|
||||
{test::KeyStr("bar", 5, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("bv2", 2), "bv1"}, 5 /*last_committed_seq*/,
|
||||
nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
|
||||
true /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
true /*key_not_exists_beyond_output_level*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_MoreEntriesInLowerLevels_NoSwapPreferredSeqno) {
|
||||
// This tests mimics more entries in lower levels with `bottommost_level` and
|
||||
// `key_not_exists_beyond_output_level` set to false.
|
||||
RunTest({test::KeyStr("bar", 5, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("bv2", 2)},
|
||||
{test::KeyStr("bar", 5, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("bv2", 2)}, 5 /*last_committed_seq*/,
|
||||
nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
|
||||
false /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
false /*key_not_exists_beyond_output_level*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_ShouldBeCoverredByRangeDeletionBeforeSwap_NoOutput) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("morning", 2, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("night", 6, kTypeValue)},
|
||||
{ValueWithPreferredSeqno("zao", 3),
|
||||
ValueWithPreferredSeqno("zao", 1), "wan"},
|
||||
{test::KeyStr("ma", 6, kTypeRangeDeletion)}, {"mz"}, 6,
|
||||
kMaxSequenceNumber /*last_committed_sequence*/,
|
||||
nullptr /*merge_op*/, nullptr /*filter*/,
|
||||
false /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
true /*key_not_exists_beyond_output_level*/);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 6, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_EQ("wan", c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
ASSERT_OK(c_iter_->status());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_WillBeHiddenByRangeDeletionAfterSwap_NoSwap) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("night", 6, kTypeValue)},
|
||||
{ValueWithPreferredSeqno("zao", 3), "wan"},
|
||||
{test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 6,
|
||||
kMaxSequenceNumber /*last_committed_sequence*/,
|
||||
nullptr /*merge_op*/, nullptr /*filter*/,
|
||||
false /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
true /*key_not_exists_beyond_output_level*/);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
c_iter_->key().ToString());
|
||||
ASSERT_EQ(ValueWithPreferredSeqno("zao", 3), c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 6, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_EQ("wan", c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
ASSERT_OK(c_iter_->status());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_BottomMostLevelVisibleToEarliestSnapshot_SwapPreferredSeqno) {
|
||||
// Preferred seqno got swapped in and also zeroed out as a bottommost level
|
||||
// optimization.
|
||||
RunTest(
|
||||
{test::KeyStr("bar", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("bar", 4, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 6, kTypeValue)},
|
||||
{ValueWithPreferredSeqno("bv2", 2), ValueWithPreferredSeqno("bv1", 1),
|
||||
"fv1"},
|
||||
{test::KeyStr("bar", 0, kTypeValue), test::KeyStr("foo", 0, kTypeValue)},
|
||||
{"bv2", "fv1"}, 6 /*last_committed_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_NonBottomMostLevelVisibleToEarliestSnapshot_SwapPreferredSeqno) {
|
||||
RunTest(
|
||||
{test::KeyStr("bar", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("bar", 4, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 6, kTypeValue)},
|
||||
{ValueWithPreferredSeqno("bv2", 2), ValueWithPreferredSeqno("bv1", 1),
|
||||
"fv1"},
|
||||
{test::KeyStr("bar", 2, kTypeValue), test::KeyStr("foo", 6, kTypeValue)},
|
||||
{"bv2", "fv1"}, 6 /*last_committed_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, false /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
true /*key_not_exists_beyond_output_level*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_SequenceNumberAlreadyZeroedOut_ChangeType) {
|
||||
RunTest(
|
||||
{test::KeyStr("bar", 0, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("bar", 0, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 0, kTypeValue)},
|
||||
{ValueWithPreferredSeqno("bv2", 2), ValueWithPreferredSeqno("bv1", 1),
|
||||
"fv1"},
|
||||
{test::KeyStr("bar", 0, kTypeValue), test::KeyStr("foo", 0, kTypeValue)},
|
||||
{"bv2", "fv1"}, 6 /*last_committed_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
// Compaction filter should keep uncommitted key as-is, and
|
||||
// * Convert the latest value to deletion, and/or
|
||||
// * if latest value is a merge, apply filter to all subsequent merges.
|
||||
@@ -1369,22 +1145,6 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_Value) {
|
||||
nullptr /*merge_operator*/, compaction_filter.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_TimedPut) {
|
||||
// TODO(yuzhangyu): Add support for this type in compaction filter.
|
||||
// Type kTypeValuePreferredSeqno is not explicitly exposed in the compaction
|
||||
// filter API, so users can not operate on it through compaction filter API
|
||||
// to remove/purge/change value etc. But this type of entry can be impacted by
|
||||
// other entries' filter result, currently only kRemoveAndSkip type of result
|
||||
// can affect it.
|
||||
std::unique_ptr<CompactionFilter> compaction_filter(
|
||||
new FilterAllKeysCompactionFilter());
|
||||
RunTest({test::KeyStr("a", 2, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("v1")},
|
||||
{test::KeyStr("a", 2, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("v1")}, 2 /*last_committed_seq*/,
|
||||
nullptr /*merge_operator*/, compaction_filter.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_Deletion) {
|
||||
std::unique_ptr<CompactionFilter> compaction_filter(
|
||||
new FilterAllKeysCompactionFilter());
|
||||
|
||||
@@ -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),
|
||||
@@ -285,7 +282,6 @@ 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->immutable_options()->preserve_internal_time_seconds,
|
||||
c->immutable_options()->preclude_last_level_data_seconds);
|
||||
@@ -323,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->immutable_options()->preserve_internal_time_seconds,
|
||||
c->immutable_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.
|
||||
@@ -391,9 +404,7 @@ void CompactionJob::AcquireSubcompactionResources(
|
||||
|
||||
void CompactionJob::ShrinkSubcompactionResources(uint64_t num_extra_resources) {
|
||||
// Do nothing when we have zero resources to shrink
|
||||
if (num_extra_resources == 0) {
|
||||
return;
|
||||
}
|
||||
if (num_extra_resources == 0) return;
|
||||
db_mutex_->Lock();
|
||||
// We cannot release threads more than what we reserved before
|
||||
int extra_num_subcompaction_threads_released = env_->ReleaseThreads(
|
||||
@@ -439,6 +450,14 @@ void CompactionJob::ReleaseSubcompactionResources() {
|
||||
ShrinkSubcompactionResources(extra_num_subcompaction_threads_reserved_);
|
||||
}
|
||||
|
||||
struct RangeWithSize {
|
||||
Range range;
|
||||
uint64_t size;
|
||||
|
||||
RangeWithSize(const Slice& a, const Slice& b, uint64_t s = 0)
|
||||
: range(a, b), size(s) {}
|
||||
};
|
||||
|
||||
void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// The goal is to find some boundary keys so that we can evenly partition
|
||||
// the compaction input data into max_subcompactions ranges.
|
||||
@@ -573,9 +592,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::GenSubcompactionBoundaries:0",
|
||||
&num_planned_subcompactions);
|
||||
if (num_planned_subcompactions == 1) {
|
||||
return;
|
||||
}
|
||||
if (num_planned_subcompactions == 1) return;
|
||||
|
||||
// Group the ranges into subcompactions
|
||||
uint64_t target_range_size = std::max(
|
||||
@@ -632,7 +649,7 @@ Status CompactionJob::Run() {
|
||||
|
||||
// Always schedule the first subcompaction (whether or not there are also
|
||||
// others) in the current thread to be efficient with resources
|
||||
ProcessKeyValueCompaction(compact_->sub_compact_states.data());
|
||||
ProcessKeyValueCompaction(&compact_->sub_compact_states[0]);
|
||||
|
||||
// Wait for all other threads (if there are any) to finish execution
|
||||
for (auto& thread : thread_pool) {
|
||||
@@ -743,6 +760,7 @@ Status CompactionJob::Run() {
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
OutputValidator validator(cfd->internal_comparator(),
|
||||
/*_enable_order_check=*/true,
|
||||
/*_enable_hash=*/true);
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
s = validator.Add(iter->key(), iter->value());
|
||||
@@ -813,27 +831,24 @@ Status CompactionJob::Run() {
|
||||
// input keys. So the number of keys it processed is not suitable for
|
||||
// verification here.
|
||||
// TODO: support verification when trim_ts_ is non-empty.
|
||||
if (!(ts_sz > 0 && !trim_ts_.empty())) {
|
||||
if (!(ts_sz > 0 && !trim_ts_.empty()) &&
|
||||
db_options_.compaction_verify_record_count) {
|
||||
assert(compaction_stats_.stats.num_input_records > 0);
|
||||
// TODO: verify the number of range deletion entries.
|
||||
uint64_t expected =
|
||||
compaction_stats_.stats.num_input_records - num_input_range_del;
|
||||
uint64_t actual = compaction_job_stats_->num_input_records;
|
||||
if (expected != actual) {
|
||||
char scratch[2345];
|
||||
compact_->compaction->Summary(scratch, sizeof(scratch));
|
||||
std::string msg =
|
||||
"Compaction number of input keys does not match "
|
||||
"number of keys processed. Expected " +
|
||||
std::to_string(expected) + " but processed " +
|
||||
std::to_string(actual) + ". Compaction summary: " + scratch;
|
||||
"Total number of input records: " + std::to_string(expected) +
|
||||
", but processed " + std::to_string(actual) + " records.";
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log, "[%s] [JOB %d] Compaction with status: %s",
|
||||
db_options_.info_log, "[%s] [JOB %d] Compaction %s",
|
||||
compact_->compaction->column_family_data()->GetName().c_str(),
|
||||
job_context_->job_id, msg.c_str());
|
||||
if (db_options_.compaction_verify_record_count) {
|
||||
status = Status::Corruption(msg);
|
||||
}
|
||||
status = Status::Corruption(
|
||||
"Compaction number of input keys does not match number of keys "
|
||||
"processed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1270,9 +1285,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
: nullptr);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:1",
|
||||
static_cast<void*>(const_cast<std::atomic<bool>*>(
|
||||
&manual_compaction_canceled_)));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
|
||||
const std::string* const full_history_ts_low =
|
||||
full_history_ts_low_.empty() ? nullptr : &full_history_ts_low_;
|
||||
@@ -1282,9 +1298,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_,
|
||||
@@ -1321,7 +1336,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
Status status;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing",
|
||||
static_cast<void*>(const_cast<Compaction*>(sub_compact->compaction)));
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<Compaction*>(sub_compact->compaction)));
|
||||
uint64_t last_cpu_micros = prev_cpu_micros;
|
||||
while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
|
||||
// Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
|
||||
@@ -1352,9 +1368,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
break;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:2",
|
||||
static_cast<void*>(const_cast<std::atomic<bool>*>(
|
||||
&manual_compaction_canceled_)));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:2",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
c_iter->Next();
|
||||
if (c_iter->status().IsManualCompactionPaused()) {
|
||||
break;
|
||||
@@ -1842,14 +1859,13 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
// Pass temperature of the last level files to FileSystem.
|
||||
FileOptions fo_copy = file_options_;
|
||||
Temperature temperature = sub_compact->compaction->output_temperature();
|
||||
Temperature last_level_temp =
|
||||
sub_compact->compaction->mutable_cf_options()->last_level_temperature;
|
||||
// Here last_level_temperature supersedes default_write_temperature, when
|
||||
// enabled and applicable
|
||||
if (last_level_temp != Temperature::kUnknown &&
|
||||
// only set for the last level compaction and also it's not output to
|
||||
// penultimate level (when preclude_last_level feature is enabled)
|
||||
if (temperature == Temperature::kUnknown &&
|
||||
sub_compact->compaction->is_last_level() &&
|
||||
!sub_compact->IsCurrentPenultimateLevel()) {
|
||||
temperature = last_level_temp;
|
||||
temperature =
|
||||
sub_compact->compaction->mutable_cf_options()->last_level_temperature;
|
||||
}
|
||||
fo_copy.temperature = temperature;
|
||||
|
||||
@@ -1929,6 +1945,8 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
}
|
||||
|
||||
outputs.AddOutput(std::move(meta), cfd->internal_comparator(),
|
||||
sub_compact->compaction->mutable_cf_options()
|
||||
->check_flush_compaction_key_order,
|
||||
paranoid_file_checks_);
|
||||
}
|
||||
|
||||
@@ -1951,16 +1969,13 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
TableBuilderOptions tboptions(
|
||||
*cfd->ioptions(), *(sub_compact->compaction->mutable_cf_options()),
|
||||
read_options, write_options, cfd->internal_comparator(),
|
||||
cfd->internal_tbl_prop_coll_factories(),
|
||||
cfd->int_tbl_prop_collector_factories(),
|
||||
sub_compact->compaction->output_compression(),
|
||||
sub_compact->compaction->output_compression_opts(), cfd->GetID(),
|
||||
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);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
@@ -314,8 +315,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.
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/plain/plain_table_factory.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
@@ -130,7 +131,7 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
ColumnFamilyOptions cf_opts(options);
|
||||
size_t cfi = handles_.size();
|
||||
handles_.resize(cfi + cfs.size());
|
||||
for (const auto& cf : cfs) {
|
||||
for (auto cf : cfs) {
|
||||
ASSERT_OK(db_->CreateColumnFamily(cf_opts, cf, &handles_[cfi++]));
|
||||
}
|
||||
}
|
||||
@@ -159,7 +160,7 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
EXPECT_EQ(cfs.size(), options.size());
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (size_t i = 0; i < cfs.size(); ++i) {
|
||||
column_families.emplace_back(cfs[i], options[i]);
|
||||
column_families.push_back(ColumnFamilyDescriptor(cfs[i], options[i]));
|
||||
}
|
||||
DBOptions db_opts = DBOptions(options[0]);
|
||||
return DB::Open(db_opts, dbname_, column_families, &handles_, &db_);
|
||||
|
||||
@@ -302,13 +302,13 @@ class CompactionJobTestBase : public testing::Test {
|
||||
TableBuilderOptions(*cfd_->ioptions(), mutable_cf_options_,
|
||||
read_options, write_options,
|
||||
cfd_->internal_comparator(),
|
||||
cfd_->internal_tbl_prop_coll_factories(),
|
||||
cfd_->int_tbl_prop_collector_factories(),
|
||||
CompressionType::kNoCompression,
|
||||
CompressionOptions(), 0 /* column_family_id */,
|
||||
kDefaultColumnFamilyName, -1 /* level */),
|
||||
file_writer.get()));
|
||||
// Build table.
|
||||
for (const auto& kv : contents) {
|
||||
for (auto kv : contents) {
|
||||
std::string key;
|
||||
std::string value;
|
||||
std::tie(key, value) = kv;
|
||||
@@ -327,7 +327,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
SequenceNumber smallest_seqno = kMaxSequenceNumber;
|
||||
SequenceNumber largest_seqno = 0;
|
||||
uint64_t oldest_blob_file_number = kInvalidBlobFileNumber;
|
||||
for (const auto& kv : contents) {
|
||||
for (auto kv : contents) {
|
||||
ParsedInternalKey key;
|
||||
std::string skey;
|
||||
std::string value;
|
||||
@@ -1473,7 +1473,7 @@ TEST_F(CompactionJobTest, OldestBlobFileNumber) {
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
|
||||
cf_options_.last_level_temperature = Temperature::kCold;
|
||||
cf_options_.bottommost_temperature = Temperature::kCold;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"Compaction::SupportsPerKeyPlacement:Enabled", [&](void* arg) {
|
||||
auto supports_per_key_placement = static_cast<bool*>(arg);
|
||||
@@ -1752,9 +1752,23 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytes) {
|
||||
class CompactionJobDynamicFileSizeTest
|
||||
: public CompactionJobTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
CompactionJobDynamicFileSizeTest()
|
||||
: CompactionJobTestBase(
|
||||
test::PerThreadDBPath("compaction_job_dynamic_file_size_test"),
|
||||
BytewiseComparator(), [](uint64_t /*ts*/) { return ""; },
|
||||
/*test_io_priority=*/false, TableTypeForTest::kMockTable) {}
|
||||
};
|
||||
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutForMaxCompactionBytes) {
|
||||
// dynamic_file_size option should have no impact on cutting for max
|
||||
// compaction bytes.
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
mutable_cf_options_.target_file_size_base = 80;
|
||||
mutable_cf_options_.max_compaction_bytes = 21;
|
||||
@@ -1828,7 +1842,10 @@ TEST_F(CompactionJobTest, CutForMaxCompactionBytes) {
|
||||
{expected_file1, expected_file2});
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToSkipGrandparentFile) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
// Make sure the grandparent level file size (10) qualifies skipping.
|
||||
// Currently, it has to be > 1/8 of target file size.
|
||||
@@ -1863,15 +1880,28 @@ TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
|
||||
mock::MakeMockFile({{KeyStr("x", 4U, kTypeValue), "val"},
|
||||
{KeyStr("z", 6U, kTypeValue), "val3"}});
|
||||
|
||||
auto expected_file_disable_dynamic_file_size =
|
||||
mock::MakeMockFile({{KeyStr("a", 5U, kTypeValue), "val2"},
|
||||
{KeyStr("c", 3U, kTypeValue), "val"},
|
||||
{KeyStr("x", 4U, kTypeValue), "val"},
|
||||
{KeyStr("z", 6U, kTypeValue), "val3"}});
|
||||
|
||||
SetLastSequence(6U);
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundary) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
NewDB();
|
||||
|
||||
// MockTable has 1 byte per entry by default and each file is 10 bytes.
|
||||
@@ -1938,15 +1968,40 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
|
||||
}
|
||||
expected_file2.emplace_back(KeyStr("s", 4U, kTypeValue), "val");
|
||||
|
||||
mock::KVVector expected_file_disable_dynamic_file_size1;
|
||||
for (char i = 0; i < 10; i++) {
|
||||
expected_file_disable_dynamic_file_size1.emplace_back(
|
||||
KeyStr(std::string(1, ch + i), i + 10, kTypeValue),
|
||||
"val" + std::to_string(i));
|
||||
}
|
||||
|
||||
mock::KVVector expected_file_disable_dynamic_file_size2;
|
||||
for (char i = 10; i < 12; i++) {
|
||||
expected_file_disable_dynamic_file_size2.emplace_back(
|
||||
KeyStr(std::string(1, ch + i), i + 10, kTypeValue),
|
||||
"val" + std::to_string(i));
|
||||
}
|
||||
|
||||
expected_file_disable_dynamic_file_size2.emplace_back(
|
||||
KeyStr("s", 4U, kTypeValue), "val");
|
||||
|
||||
SetLastSequence(22U);
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size1,
|
||||
expected_file_disable_dynamic_file_size2});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
NewDB();
|
||||
|
||||
// MockTable has 1 byte per entry by default and each file is 10 bytes.
|
||||
@@ -1983,9 +2038,13 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
AddMockFile(file5, 2);
|
||||
|
||||
mock::KVVector expected_file1;
|
||||
mock::KVVector expected_file_disable_dynamic_file_size;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
expected_file1.emplace_back(KeyStr("a", 100 - i, kTypeValue),
|
||||
"val" + std::to_string(100 - i));
|
||||
expected_file_disable_dynamic_file_size.emplace_back(
|
||||
KeyStr("a", 100 - i, kTypeValue), "val" + std::to_string(100 - i));
|
||||
}
|
||||
|
||||
// make sure `b` is cut in a separated file (so internally it's not using
|
||||
@@ -1994,6 +2053,9 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
auto expected_file2 =
|
||||
mock::MakeMockFile({{KeyStr("b", 90U, kTypeValue), "valb"}});
|
||||
|
||||
expected_file_disable_dynamic_file_size.emplace_back(
|
||||
KeyStr("b", 90U, kTypeValue), "valb");
|
||||
|
||||
SetLastSequence(122U);
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -2004,13 +2066,20 @@ TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
for (int i = 80; i <= 100; i++) {
|
||||
snapshots.emplace_back(i);
|
||||
}
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2}, snapshots);
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size}, snapshots);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutForMaxCompactionBytesSameKey) {
|
||||
// dynamic_file_size option should have no impact on cutting for max
|
||||
// compaction bytes.
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
mutable_cf_options_.target_file_size_base = 80;
|
||||
@@ -2067,6 +2136,9 @@ TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
{expected_file1, expected_file2, expected_file3}, snapshots);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionJobDynamicFileSizeTest,
|
||||
CompactionJobDynamicFileSizeTest, testing::Bool());
|
||||
|
||||
class CompactionJobTimestampTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobTimestampTest()
|
||||
|
||||
@@ -27,9 +27,7 @@ Status CompactionOutputs::Finish(
|
||||
if (s.ok()) {
|
||||
SeqnoToTimeMapping relevant_mapping;
|
||||
relevant_mapping.CopyFromSeqnoRange(
|
||||
seqno_to_time_mapping,
|
||||
std::min(smallest_preferred_seqno_, meta->fd.smallest_seqno),
|
||||
meta->fd.largest_seqno);
|
||||
seqno_to_time_mapping, meta->fd.smallest_seqno, meta->fd.largest_seqno);
|
||||
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
|
||||
builder_->SetSeqnoTimeTableProperties(relevant_mapping,
|
||||
meta->oldest_ancester_time);
|
||||
@@ -322,6 +320,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
being_grandparent_gap_ ? 2 : 3;
|
||||
if (compaction_->immutable_options()->compaction_style ==
|
||||
kCompactionStyleLevel &&
|
||||
compaction_->immutable_options()->level_compaction_dynamic_file_size &&
|
||||
num_grandparent_boundaries_crossed >=
|
||||
num_skippable_boundaries_crossed &&
|
||||
grandparent_overlapped_bytes_ - previous_overlapped_bytes >
|
||||
@@ -343,6 +342,7 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
// improvement.
|
||||
if (compaction_->immutable_options()->compaction_style ==
|
||||
kCompactionStyleLevel &&
|
||||
compaction_->immutable_options()->level_compaction_dynamic_file_size &&
|
||||
current_output_file_size_ >=
|
||||
((compaction_->target_output_file_size() + 99) / 100) *
|
||||
(50 + std::min(grandparent_boundary_switched_num_ * 5,
|
||||
@@ -424,11 +424,6 @@ Status CompactionOutputs::AddToOutput(
|
||||
}
|
||||
|
||||
const ParsedInternalKey& ikey = c_iter.ikey();
|
||||
if (ikey.type == kTypeValuePreferredSeqno) {
|
||||
SequenceNumber preferred_seqno = ParsePackedValueForSeqno(value);
|
||||
smallest_preferred_seqno_ =
|
||||
std::min(smallest_preferred_seqno_, preferred_seqno);
|
||||
}
|
||||
s = current_output().meta.UpdateBoundaries(key, value, ikey.sequence,
|
||||
ikey.type);
|
||||
|
||||
|
||||
@@ -30,9 +30,11 @@ class CompactionOutputs {
|
||||
// compaction output file
|
||||
struct Output {
|
||||
Output(FileMetaData&& _meta, const InternalKeyComparator& _icmp,
|
||||
bool _enable_hash, bool _finished, uint64_t precalculated_hash)
|
||||
bool _enable_order_check, bool _enable_hash, bool _finished,
|
||||
uint64_t precalculated_hash)
|
||||
: meta(std::move(_meta)),
|
||||
validator(_icmp, _enable_hash, precalculated_hash),
|
||||
validator(_icmp, _enable_order_check, _enable_hash,
|
||||
precalculated_hash),
|
||||
finished(_finished) {}
|
||||
FileMetaData meta;
|
||||
OutputValidator validator;
|
||||
@@ -47,10 +49,10 @@ class CompactionOutputs {
|
||||
|
||||
// Add generated output to the list
|
||||
void AddOutput(FileMetaData&& meta, const InternalKeyComparator& icmp,
|
||||
bool enable_hash, bool finished = false,
|
||||
uint64_t precalculated_hash = 0) {
|
||||
outputs_.emplace_back(std::move(meta), icmp, enable_hash, finished,
|
||||
precalculated_hash);
|
||||
bool enable_order_check, bool enable_hash,
|
||||
bool finished = false, uint64_t precalculated_hash = 0) {
|
||||
outputs_.emplace_back(std::move(meta), icmp, enable_order_check,
|
||||
enable_hash, finished, precalculated_hash);
|
||||
}
|
||||
|
||||
// Set new table builder for the current output
|
||||
@@ -297,7 +299,6 @@ class CompactionOutputs {
|
||||
std::unique_ptr<TableBuilder> builder_;
|
||||
std::unique_ptr<WritableFileWriter> file_writer_;
|
||||
uint64_t current_output_file_size_ = 0;
|
||||
SequenceNumber smallest_preferred_seqno_ = kMaxSequenceNumber;
|
||||
|
||||
// all the compaction outputs so far
|
||||
std::vector<Output> outputs_;
|
||||
|
||||
@@ -130,7 +130,7 @@ CompactionPicker::CompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: ioptions_(ioptions), icmp_(icmp) {}
|
||||
|
||||
CompactionPicker::~CompactionPicker() = default;
|
||||
CompactionPicker::~CompactionPicker() {}
|
||||
|
||||
// Delete this compaction from the list of running compactions.
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
|
||||
@@ -340,8 +340,8 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
#ifndef NDEBUG
|
||||
assert(input_files.size());
|
||||
// This compaction output should not overlap with a running compaction as
|
||||
// `SanitizeAndConvertCompactionInputFiles` should've checked earlier and db
|
||||
// mutex shouldn't have been released since.
|
||||
// `SanitizeCompactionInputFiles` should've checked earlier and db mutex
|
||||
// shouldn't have been released since.
|
||||
int start_level = Compaction::kInvalidLevel;
|
||||
for (const auto& in : input_files) {
|
||||
// input_files should already be sorted by level
|
||||
@@ -377,8 +377,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
output_level, compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id, compression_type,
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_options.max_subcompactions,
|
||||
Temperature::kUnknown, compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
@@ -501,6 +500,7 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
// user key, while excluding other entries for the same user key. This
|
||||
// can happen when one user key spans multiple files.
|
||||
if (!output_level_inputs->empty()) {
|
||||
const uint64_t limit = mutable_cf_options.max_compaction_bytes;
|
||||
const uint64_t output_level_inputs_size =
|
||||
TotalFileSize(output_level_inputs->files);
|
||||
const uint64_t inputs_size = TotalFileSize(inputs->files);
|
||||
@@ -526,15 +526,10 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
if (!ExpandInputsToCleanCut(cf_name, vstorage, &expanded_inputs)) {
|
||||
try_overlapping_inputs = false;
|
||||
}
|
||||
// It helps to reduce write amp and avoid a further separate compaction
|
||||
// to include more input level files without expanding output level files.
|
||||
// So we apply a softer limit. We still need a limit to avoid overly large
|
||||
// compactions and potential high space amp spikes.
|
||||
const uint64_t limit =
|
||||
MultiplyCheckOverflow(mutable_cf_options.max_compaction_bytes, 2.0);
|
||||
if (try_overlapping_inputs && expanded_inputs.size() > inputs->size() &&
|
||||
!AreFilesInCompaction(expanded_inputs.files) &&
|
||||
output_level_inputs_size + expanded_inputs_size < limit) {
|
||||
(mutable_cf_options.ignore_max_compaction_bytes_for_input ||
|
||||
output_level_inputs_size + expanded_inputs_size < limit) &&
|
||||
!AreFilesInCompaction(expanded_inputs.files)) {
|
||||
InternalKey new_start, new_limit;
|
||||
GetRange(expanded_inputs, &new_start, &new_limit);
|
||||
CompactionInputFiles expanded_output_level_inputs;
|
||||
@@ -556,8 +551,9 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
base_index, nullptr);
|
||||
expanded_inputs_size = TotalFileSize(expanded_inputs.files);
|
||||
if (expanded_inputs.size() > inputs->size() &&
|
||||
!AreFilesInCompaction(expanded_inputs.files) &&
|
||||
(output_level_inputs_size + expanded_inputs_size) < limit) {
|
||||
(mutable_cf_options.ignore_max_compaction_bytes_for_input ||
|
||||
output_level_inputs_size + expanded_inputs_size < limit) &&
|
||||
!AreFilesInCompaction(expanded_inputs.files)) {
|
||||
expand_inputs = true;
|
||||
}
|
||||
}
|
||||
@@ -674,8 +670,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions,
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
@@ -863,9 +858,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
/* is manual */ true, trim_ts, /* score */ -1,
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
std::move(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,
|
||||
@@ -1040,18 +1034,19 @@ Status CompactionPicker::SanitizeCompactionInputFilesForAllLevels(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (RangeOverlapWithCompaction(smallestkey, largestkey, output_level)) {
|
||||
return Status::Aborted(
|
||||
"A running compaction is writing to the same output level in an "
|
||||
"overlapping key range");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
Status CompactionPicker::SanitizeCompactionInputFiles(
|
||||
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 {
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level) const {
|
||||
assert(static_cast<int>(cf_meta.levels.size()) - 1 ==
|
||||
cf_meta.levels[cf_meta.levels.size() - 1].level);
|
||||
assert(converted_input_files);
|
||||
|
||||
if (output_level >= static_cast<int>(cf_meta.levels.size())) {
|
||||
return Status::InvalidArgument(
|
||||
"Output level for column family " + cf_meta.name +
|
||||
@@ -1077,6 +1072,7 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
|
||||
Status s = SanitizeCompactionInputFilesForAllLevels(input_files, cf_meta,
|
||||
output_level);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1117,22 +1113,6 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
|
||||
}
|
||||
}
|
||||
|
||||
s = GetCompactionInputsFromFileNumbers(converted_input_files, input_files,
|
||||
vstorage, CompactionOptions());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
assert(converted_input_files->size() > 0);
|
||||
if (output_level != 0 &&
|
||||
FilesRangeOverlapWithCompaction(
|
||||
*converted_input_files, output_level,
|
||||
Compaction::EvaluatePenultimateLevel(
|
||||
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");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -86,20 +86,16 @@ class CompactionPicker {
|
||||
|
||||
virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const = 0;
|
||||
|
||||
// Sanitize the input set of compaction input files and convert it to
|
||||
// `std::vector<CompactionInputFiles>` in the output parameter
|
||||
// `converted_input_files`.
|
||||
// When the input parameters do not describe a valid
|
||||
// compaction, the function will try to fix the input_files by adding
|
||||
// necessary files. If it's not possible to convert an invalid input_files
|
||||
// into a valid one by adding more files, the function will return a
|
||||
// non-ok status with specific reason.
|
||||
//
|
||||
Status SanitizeAndConvertCompactionInputFiles(
|
||||
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;
|
||||
// Sanitize the input set of compaction input files.
|
||||
// When the input parameters do not describe a valid compaction, the
|
||||
// function will try to fix the input_files by adding necessary
|
||||
// files. If it's not possible to conver an invalid input_files
|
||||
// into a valid one by adding more files, the function will return a
|
||||
// non-ok status with specific reason.
|
||||
//
|
||||
Status SanitizeCompactionInputFiles(std::unordered_set<uint64_t>* input_files,
|
||||
const ColumnFamilyMetaData& cf_meta,
|
||||
const int output_level) const;
|
||||
|
||||
// Free up the files that participated in a compaction
|
||||
//
|
||||
@@ -113,8 +109,8 @@ class CompactionPicker {
|
||||
// object.
|
||||
//
|
||||
// Caller must provide a set of input files that has been passed through
|
||||
// `SanitizeAndConvertCompactionInputFiles` earlier. The lock should not be
|
||||
// released between that call and this one.
|
||||
// `SanitizeCompactionInputFiles` earlier. The lock should not be released
|
||||
// between that call and this one.
|
||||
Compaction* CompactFiles(const CompactionOptions& compact_options,
|
||||
const std::vector<CompactionInputFiles>& input_files,
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
@@ -124,7 +120,6 @@ class CompactionPicker {
|
||||
|
||||
// Converts a set of compaction input file numbers into
|
||||
// a list of CompactionInputFiles.
|
||||
// TODO(hx235): remove the unused paramter `compact_options`
|
||||
Status GetCompactionInputsFromFileNumbers(
|
||||
std::vector<CompactionInputFiles>* input_files,
|
||||
std::unordered_set<uint64_t>* input_set,
|
||||
@@ -230,8 +225,8 @@ class CompactionPicker {
|
||||
protected:
|
||||
const ImmutableOptions& ioptions_;
|
||||
|
||||
// A helper function to SanitizeAndConvertCompactionInputFiles() that
|
||||
// sanitizes "input_files" by adding necessary files.
|
||||
// A helper function to SanitizeCompactionInputFiles() that
|
||||
// sanitizes "input_files" by adding necessary files.
|
||||
virtual Status SanitizeCompactionInputFilesForAllLevels(
|
||||
std::unordered_set<uint64_t>* input_files,
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level) const;
|
||||
@@ -282,7 +277,8 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
}
|
||||
|
||||
// Always returns false.
|
||||
bool NeedsCompaction(const VersionStorageInfo* /*vstorage*/) const override {
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* /*vstorage*/) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -116,8 +116,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, /* l0_files_might_overlap */ true,
|
||||
@@ -186,8 +185,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
{comp_inputs}, 0, 16 * 1024 * 1024 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
0 /* max_subcompactions */, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
@@ -282,8 +280,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
/* target_file_size */ 0,
|
||||
/* max_compaction_bytes */ 0,
|
||||
/* output_path_id */ 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true,
|
||||
@@ -417,7 +414,6 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
if (inputs[0].files.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(compaction_target_temp != Temperature::kLastTemperature);
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
|
||||
@@ -18,27 +18,26 @@ 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,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) override;
|
||||
virtual 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,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage, int input_level,
|
||||
int output_level,
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore,
|
||||
const std::string& trim_ts) override;
|
||||
virtual Compaction* CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
int input_level, int output_level,
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) override;
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
int MaxOutputLevel() const override { return 0; }
|
||||
virtual int MaxOutputLevel() const override { return 0; }
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
private:
|
||||
Compaction* PickTTLCompaction(const std::string& cf_name,
|
||||
|
||||
@@ -113,15 +113,6 @@ class LevelCompactionBuilder {
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
// When total L0 size is small compared to Lbase, try to pick intra-L0
|
||||
// compaction starting from the newest L0 file. This helps to prevent
|
||||
// L0->Lbase compaction with large write-amp.
|
||||
//
|
||||
// Returns true iff an intra-L0 compaction is picked.
|
||||
// `start_level_inputs_` and `output_level_` will be updated accordingly if
|
||||
// a compaction is picked.
|
||||
bool PickSizeBasedIntraL0Compaction();
|
||||
|
||||
// Return true if TrivialMove is extended. `start_index` is the index of
|
||||
// the initial file picked, which should already be in `start_level_inputs_`.
|
||||
bool TryExtendNonL0TrivialMove(int start_index,
|
||||
@@ -355,9 +346,7 @@ void LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion() {
|
||||
TEST_SYNC_POINT("LevelCompactionPicker::RoundRobin");
|
||||
|
||||
// Only expand the inputs when we have selected a file in start_level_inputs_
|
||||
if (start_level_inputs_.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (start_level_inputs_.size() == 0) return;
|
||||
|
||||
uint64_t start_lvl_bytes_no_compacting = 0;
|
||||
uint64_t curr_bytes_to_compact = 0;
|
||||
@@ -553,7 +542,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level_,
|
||||
vstorage_->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
|
||||
l0_files_might_overlap, compaction_reason_);
|
||||
@@ -787,9 +776,6 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
// being compacted at level 0.
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
if (PickSizeBasedIntraL0Compaction()) {
|
||||
return true;
|
||||
}
|
||||
TEST_SYNC_POINT("LevelCompactionPicker::PickCompactionBySize:0");
|
||||
return false;
|
||||
}
|
||||
@@ -802,9 +788,6 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
if (TryPickL0TrivialMove()) {
|
||||
return true;
|
||||
}
|
||||
if (start_level_ == 0 && PickSizeBasedIntraL0Compaction()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
@@ -909,56 +892,6 @@ bool LevelCompactionBuilder::PickIntraL0Compaction() {
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
&start_level_inputs_);
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
|
||||
assert(start_level_ == 0);
|
||||
int base_level = vstorage_->base_level();
|
||||
if (base_level <= 0) {
|
||||
return false;
|
||||
}
|
||||
const std::vector<FileMetaData*>& l0_files =
|
||||
vstorage_->LevelFiles(/*level=*/0);
|
||||
size_t min_num_file =
|
||||
std::max(2, mutable_cf_options_.level0_file_num_compaction_trigger);
|
||||
if (l0_files.size() < min_num_file) {
|
||||
return false;
|
||||
}
|
||||
uint64_t l0_size = 0;
|
||||
for (const auto& file : l0_files) {
|
||||
l0_size += file->fd.GetFileSize();
|
||||
}
|
||||
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);
|
||||
uint64_t lbase_size = 0;
|
||||
for (const auto& file : lbase_files) {
|
||||
lbase_size += file->fd.GetFileSize();
|
||||
if (lbase_size > min_lbase_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lbase_size <= min_lbase_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.clear();
|
||||
start_level_inputs_.level = 0;
|
||||
for (const auto& file : l0_files) {
|
||||
if (file->being_compacted) {
|
||||
break;
|
||||
}
|
||||
start_level_inputs_.files.push_back(file);
|
||||
}
|
||||
if (start_level_inputs_.files.size() < min_num_file) {
|
||||
start_level_inputs_.clear();
|
||||
return false;
|
||||
}
|
||||
output_level_ = 0;
|
||||
return true /* picked an intra-L0 compaction */;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
|
||||
@@ -20,13 +20,14 @@ 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,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
virtual 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;
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -77,7 +77,7 @@ class CompactionPickerTestBase : public testing::Test {
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
}
|
||||
|
||||
~CompactionPickerTestBase() override = default;
|
||||
~CompactionPickerTestBase() override {}
|
||||
|
||||
void NewVersionStorage(int num_levels, CompactionStyle style) {
|
||||
DeleteVersionStorage();
|
||||
@@ -214,7 +214,7 @@ class CompactionPickerTest : public CompactionPickerTestBase {
|
||||
explicit CompactionPickerTest()
|
||||
: CompactionPickerTestBase(BytewiseComparator()) {}
|
||||
|
||||
~CompactionPickerTest() override = default;
|
||||
~CompactionPickerTest() override {}
|
||||
};
|
||||
|
||||
class CompactionPickerU64TsTest : public CompactionPickerTestBase {
|
||||
@@ -222,7 +222,7 @@ class CompactionPickerU64TsTest : public CompactionPickerTestBase {
|
||||
explicit CompactionPickerU64TsTest()
|
||||
: CompactionPickerTestBase(test::BytewiseComparatorWithU64TsWrapper()) {}
|
||||
|
||||
~CompactionPickerU64TsTest() override = default;
|
||||
~CompactionPickerU64TsTest() override {}
|
||||
};
|
||||
|
||||
TEST_F(CompactionPickerTest, Empty) {
|
||||
@@ -1443,12 +1443,15 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping4) {
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000000;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
mutable_cf_options_.ignore_max_compaction_bytes_for_input = false;
|
||||
|
||||
// Overlaps with file 26, 27, ratio is
|
||||
// (60000000U + 60000000U / 180000000U) = 0.67
|
||||
// file 7 and 8 over lap with the same file, but file 8 is smaller so
|
||||
// it will be picked.
|
||||
// Overlaps with file 26, 27. And the file is compensated so will be
|
||||
// picked up.
|
||||
Add(2, 6U, "150", "167", 60000000U, 0, 100, 100, 180000000U);
|
||||
Add(2, 7U, "178", "189", 60000000U); // Overlaps with file 28
|
||||
Add(2, 8U, "401", "500", 61000000U); // Overlaps with file 29
|
||||
Add(2, 7U, "168", "169", 60000000U); // Overlaps with file 27
|
||||
Add(2, 8U, "201", "300", 61000000U); // Overlaps with file 28
|
||||
|
||||
Add(3, 26U, "160", "165", 60000000U);
|
||||
// Boosted file size in output level is not considered.
|
||||
@@ -1462,7 +1465,7 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping4) {
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
// Picking file 6 because overlapping ratio is the biggest.
|
||||
// Picking file 8 because overlapping ratio is the biggest.
|
||||
ASSERT_EQ(6U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
@@ -2465,60 +2468,20 @@ TEST_F(CompactionPickerTest, IsBottommostLevelTest) {
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionLimitWhenAddFileFromInputLevel) {
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesHit) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 800000u;
|
||||
mutable_cf_options_.ignore_max_compaction_bytes_for_input = false;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// It pulls in other compaction input file from the input level L1
|
||||
// without pulling in more output level files.
|
||||
// Files 1, 3, 4 are eligible.
|
||||
// File 6 is excluded since it overlaps with file 7.
|
||||
// It can expand input level since in this case, the limit on compaction size
|
||||
// is 2 * max_compaction_bytes.
|
||||
// It can expand because adding file 1 and 3, the compaction size will
|
||||
// exceed mutable_cf_options_.max_bytes_for_level_base.
|
||||
Add(1, 1U, "100", "150", 300000U);
|
||||
Add(1, 2U, "151", "200", 300001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 300000U, 0, 0);
|
||||
Add(1, 6U, "325", "400", 300000U, 0, 0);
|
||||
Add(2, 5U, "100", "350", 1U);
|
||||
Add(2, 7U, "375", "425", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(0, 2)->fd.GetNumber());
|
||||
ASSERT_EQ(4U, compaction->input(0, 3)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, HitCompactionLimitWhenAddFileFromInputLevel) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 800000u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// It pulls in other compaction input file from the input level L1
|
||||
// without pulling in more output level files.
|
||||
// Files 1, 3, 4 are eligible.
|
||||
// File 6 is excluded since it overlaps with file 7.
|
||||
// It can not expand input level since total compaction size hit the limit
|
||||
// 2 * max_compaction_bytes.
|
||||
Add(1, 1U, "100", "150", 400000U);
|
||||
Add(1, 2U, "151", "200", 400001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 400000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 400000U, 0, 0);
|
||||
Add(1, 6U, "325", "400", 400000U, 0, 0);
|
||||
Add(2, 5U, "100", "350", 1U);
|
||||
Add(2, 7U, "375", "425", 1U);
|
||||
Add(2, 5U, "100", "256", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -2532,6 +2495,34 @@ TEST_F(CompactionPickerTest, HitCompactionLimitWhenAddFileFromInputLevel) {
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesNotHit) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 800000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 1000000u;
|
||||
mutable_cf_options_.ignore_max_compaction_bytes_for_input = false;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// and it expands to file 1 and 3 too.
|
||||
Add(1, 1U, "100", "150", 300000U);
|
||||
Add(1, 2U, "151", "200", 300001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 300000U, 0, 0);
|
||||
Add(2, 5U, "000", "251", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(3U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(0, 2)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IsTrivialMoveOn) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 10001u;
|
||||
@@ -4277,170 +4268,6 @@ TEST_F(CompactionPickerTest,
|
||||
INSTANTIATE_TEST_CASE_P(PerKeyPlacementCompactionPickerTest,
|
||||
PerKeyPlacementCompactionPickerTest, ::testing::Bool());
|
||||
|
||||
TEST_F(CompactionPickerTest, IntraL0WhenL0IsSmall) {
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
for (const uint64_t lbase_size_multiplier : {1, 10, 11, 40}) {
|
||||
SCOPED_TRACE("lbase_size_multiplier=" +
|
||||
std::to_string(lbase_size_multiplier));
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// When L0 size is <= Lbase size / max_bytes_for_level_multiplier,
|
||||
// intra-L0 compaction is picked. Otherwise, L0->L1
|
||||
// compaction is picked.
|
||||
Add(/*level=*/0, /*file_number=*/1U, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/1000, /*path_id=*/0,
|
||||
/*smallest_seq=*/10, /*largest_seq=*/11,
|
||||
/*compensated_file_size=*/1000);
|
||||
Add(/*level=*/0, /*file_number=*/2U, /*smallest=*/"100",
|
||||
/*largest=*/"100", /*file_size=*/1000, /*path_id=*/0,
|
||||
/*smallest_seq=*/20, /*largest_seq=*/21,
|
||||
/*compensated_file_size=*/1000);
|
||||
Add(/*level=*/0, /*file_number=*/3U, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/1000, /*path_id=*/0,
|
||||
/*smallest_seq=*/30, /*largest_seq=*/31,
|
||||
/*compensated_file_size=*/1000);
|
||||
Add(/*level=*/0, /*file_number=*/4U, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/1000, /*path_id=*/0,
|
||||
/*smallest_seq=*/40, /*largest_seq=*/41,
|
||||
/*compensated_file_size=*/1000);
|
||||
const uint64_t l0_size = 4000;
|
||||
const uint64_t lbase_size = l0_size * lbase_size_multiplier;
|
||||
Add(/*level=*/1, /*file_number=*/5U, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/lbase_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/0, /*largest_seq=*/0,
|
||||
/*compensated_file_size=*/lbase_size);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
LevelCompactionPicker compaction_picker(ioptions_, &icmp_);
|
||||
std::unique_ptr<Compaction> compaction(compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
if (lbase_size_multiplier >
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier) {
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
} else {
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1, compaction->output_level());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMaxReadAmpLargeDB) {
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
ioptions_.num_levels = 50;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
mutable_cf_options_.compaction_options_universal.size_ratio = 10;
|
||||
mutable_cf_options_.write_buffer_size = 256 << 20;
|
||||
// Avoid space amp compaction
|
||||
mutable_cf_options_.compaction_options_universal
|
||||
.max_size_amplification_percent = 200;
|
||||
const int kMaxRuns = 8;
|
||||
for (int max_read_amp : {kMaxRuns, 0, -1}) {
|
||||
SCOPED_TRACE("max_read_amp = " + std::to_string(max_read_amp));
|
||||
if (max_read_amp == -1) {
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = kMaxRuns;
|
||||
} else {
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
|
||||
}
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp =
|
||||
max_read_amp;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
uint64_t max_run_size = 20ull << 30;
|
||||
// When max_read_amp = 0, we estimate the number of levels needed based on
|
||||
// size_ratio and write_buffer_size. See more in
|
||||
// UniversalCompactionBuilder::PickCompaction().
|
||||
// With a 20GB last level, we estimate that 8 levels are needed:
|
||||
// L0 256MB
|
||||
// L1 256MB * 1.1 (size_ratio) = 282MB
|
||||
// L2 (256MB + 282MB) * 1.1 = 592MB
|
||||
// L3 1243MB
|
||||
// L4 2610MB
|
||||
// L5 5481MB
|
||||
// L6 11510MB
|
||||
// L7 24171MB > 20GB
|
||||
for (int i = 0; i <= kMaxRuns; ++i) {
|
||||
SCOPED_TRACE("i = " + std::to_string(i));
|
||||
NewVersionStorage(/*num_levels=*/50, kCompactionStyleUniversal);
|
||||
Add(/*level=*/49, /*file_number=*/10, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/max_run_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/0, /*largest_seq=*/0,
|
||||
/*compensated_file_size=*/max_run_size);
|
||||
// Besides the last sorted run, we add additional `i` sorted runs
|
||||
// without triggering space-amp or size-amp compactions.
|
||||
uint64_t file_size = 1 << 20;
|
||||
for (int j = 0; j < i; ++j) {
|
||||
Add(/*level=*/j, /*file_number=*/100 - j, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/file_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/100 - j, /*largest_seq=*/100 - j,
|
||||
/*compensated_file_size=*/file_size);
|
||||
// to avoid space-amp and size-amp compaction
|
||||
file_size *= 2;
|
||||
}
|
||||
UpdateVersionStorageInfo();
|
||||
// level0_file_num_compaction_trigger is still used as trigger to
|
||||
// check potential compactions
|
||||
ASSERT_EQ(
|
||||
universal_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
i + 1 >= mutable_cf_options_.level0_file_num_compaction_trigger);
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_,
|
||||
vstorage_.get(), &log_buffer_));
|
||||
if (i == kMaxRuns) {
|
||||
// There are in total i + 1 > kMaxRuns sorted runs.
|
||||
// This triggers compaction ignoring size_ratio.
|
||||
ASSERT_NE(nullptr, compaction);
|
||||
ASSERT_EQ(CompactionReason::kUniversalSortedRunNum,
|
||||
compaction->compaction_reason());
|
||||
// First two runs are compacted
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(1, compaction->output_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
} else {
|
||||
ASSERT_EQ(nullptr, compaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMaxReadAmpSmallDB) {
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
ioptions_.num_levels = 50;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 1;
|
||||
mutable_cf_options_.compaction_options_universal.size_ratio = 10;
|
||||
mutable_cf_options_.write_buffer_size = 256 << 20;
|
||||
mutable_cf_options_.compaction_options_universal
|
||||
.max_size_amplification_percent = 200;
|
||||
const int kMaxRuns = 1;
|
||||
for (int max_read_amp : {-1, kMaxRuns, 0}) {
|
||||
SCOPED_TRACE("max_read_amp = " + std::to_string(max_read_amp));
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp =
|
||||
max_read_amp;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
NewVersionStorage(/*num_levels=*/50, kCompactionStyleUniversal);
|
||||
// max_run_size is much smaller than write_buffer_size,
|
||||
// only 1 level is needed.
|
||||
uint64_t max_run_size = 8 << 10;
|
||||
Add(/*level=*/49, /*file_number=*/10, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/max_run_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/0, /*largest_seq=*/0,
|
||||
/*compensated_file_size=*/max_run_size);
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_TRUE(universal_compaction_picker.NeedsCompaction(vstorage_.get()));
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_EQ(nullptr, compaction);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -227,7 +227,6 @@ 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_;
|
||||
@@ -236,8 +235,7 @@ class UniversalCompactionBuilder {
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level,
|
||||
uint64_t* max_run_size);
|
||||
const VersionStorageInfo& vstorage, int last_level);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -442,15 +440,11 @@ 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)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
*max_run_size = std::max(*max_run_size, f->fd.GetFileSize());
|
||||
}
|
||||
for (int level = 1; level <= last_level; level++) {
|
||||
uint64_t total_compensated_size = 0U;
|
||||
@@ -472,7 +466,6 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
ret.emplace_back(level, nullptr, total_size, total_compensated_size,
|
||||
being_compacted);
|
||||
}
|
||||
*max_run_size = std::max(*max_run_size, total_size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -484,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(
|
||||
@@ -515,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",
|
||||
@@ -535,48 +527,13 @@ 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++) {
|
||||
@@ -587,25 +544,17 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
|
||||
// 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_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -921,7 +870,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
output_level, 1, enable_compression),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_,
|
||||
output_level, enable_compression),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, grandparents,
|
||||
/* is manual */ false, /* trim_ts */ "", score_,
|
||||
false /* deletion_compaction */,
|
||||
@@ -1256,7 +1205,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
@@ -1400,7 +1349,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
/* max_grandparent_overlap_bytes */ GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
@@ -1493,7 +1442,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true, compaction_reason);
|
||||
|
||||
@@ -17,13 +17,14 @@ 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,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
virtual int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -74,24 +74,24 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
compaction_input.output_level, input_files_oss.str().c_str());
|
||||
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) {
|
||||
CompactionServiceJobStatus compaction_status =
|
||||
db_options_.compaction_service->StartV2(info, compaction_input_binary);
|
||||
switch (compaction_status) {
|
||||
case CompactionServiceJobStatus::kSuccess:
|
||||
break;
|
||||
case CompactionServiceJobStatus::kFailure:
|
||||
sub_compact->status = Status::Incomplete(
|
||||
"CompactionService failed to schedule a remote compaction job.");
|
||||
"CompactionService failed to start compaction job.");
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction failed to start.",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return response.status;
|
||||
return compaction_status;
|
||||
case CompactionServiceJobStatus::kUseLocal:
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API (Schedule)",
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API Start.",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return response.status;
|
||||
return compaction_status;
|
||||
default:
|
||||
assert(false); // unknown status
|
||||
break;
|
||||
@@ -101,15 +101,14 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
"[%s] [JOB %d] Waiting for remote compaction...",
|
||||
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,
|
||||
&compaction_result_binary);
|
||||
compaction_status = db_options_.compaction_service->WaitForCompleteV2(
|
||||
info, &compaction_result_binary);
|
||||
|
||||
if (compaction_status == CompactionServiceJobStatus::kUseLocal) {
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API (Wait)",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API "
|
||||
"WaitForComplete.",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return compaction_status;
|
||||
}
|
||||
|
||||
@@ -196,8 +195,8 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
|
||||
auto cfd = compaction->column_family_data();
|
||||
sub_compact->Current().AddOutput(std::move(meta),
|
||||
cfd->internal_comparator(), false, true,
|
||||
file.paranoid_hash);
|
||||
cfd->internal_comparator(), false, false,
|
||||
true, file.paranoid_hash);
|
||||
}
|
||||
sub_compact->compaction_job_stats = compaction_result.stats;
|
||||
sub_compact->Current().SetNumOutputRecords(
|
||||
@@ -831,3 +830,4 @@ bool CompactionServiceInput::TEST_Equals(CompactionServiceInput* other,
|
||||
}
|
||||
#endif // NDEBUG
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -31,44 +31,40 @@ class MyTestCompactionService : public CompactionService {
|
||||
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
CompactionServiceScheduleResponse Schedule(
|
||||
CompactionServiceJobStatus StartV2(
|
||||
const CompactionServiceJobInfo& info,
|
||||
const std::string& compaction_service_input) override {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
start_info_ = info;
|
||||
assert(info.db_name == db_path_);
|
||||
std::string unique_id = Env::Default()->GenerateUniqueId();
|
||||
jobs_.emplace(unique_id, compaction_service_input);
|
||||
infos_.emplace(unique_id, info);
|
||||
CompactionServiceScheduleResponse response(
|
||||
unique_id, is_override_start_status_
|
||||
? override_start_status_
|
||||
: CompactionServiceJobStatus::kSuccess);
|
||||
return response;
|
||||
jobs_.emplace(info.job_id, compaction_service_input);
|
||||
CompactionServiceJobStatus s = CompactionServiceJobStatus::kSuccess;
|
||||
if (is_override_start_status_) {
|
||||
return override_start_status_;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
CompactionServiceJobStatus Wait(const std::string& scheduled_job_id,
|
||||
std::string* result) override {
|
||||
CompactionServiceJobStatus WaitForCompleteV2(
|
||||
const CompactionServiceJobInfo& info,
|
||||
std::string* compaction_service_result) override {
|
||||
std::string compaction_input;
|
||||
assert(info.db_name == db_path_);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto job_index = jobs_.find(scheduled_job_id);
|
||||
if (job_index == jobs_.end()) {
|
||||
wait_info_ = info;
|
||||
auto i = jobs_.find(info.job_id);
|
||||
if (i == jobs_.end()) {
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
compaction_input = std::move(job_index->second);
|
||||
jobs_.erase(job_index);
|
||||
|
||||
auto info_index = infos_.find(scheduled_job_id);
|
||||
if (info_index == infos_.end()) {
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
wait_info_ = std::move(info_index->second);
|
||||
infos_.erase(info_index);
|
||||
compaction_input = std::move(i->second);
|
||||
jobs_.erase(i);
|
||||
}
|
||||
|
||||
if (is_override_wait_status_) {
|
||||
return override_wait_status_;
|
||||
}
|
||||
|
||||
CompactionServiceOptionsOverride options_override;
|
||||
options_override.env = options_.env;
|
||||
options_override.file_checksum_gen_factory =
|
||||
@@ -94,11 +90,11 @@ class MyTestCompactionService : public CompactionService {
|
||||
OpenAndCompactOptions options;
|
||||
options.canceled = &canceled_;
|
||||
|
||||
Status s =
|
||||
DB::OpenAndCompact(options, db_path_, db_path_ + "/" + scheduled_job_id,
|
||||
compaction_input, result, options_override);
|
||||
Status s = DB::OpenAndCompact(
|
||||
options, db_path_, db_path_ + "/" + std::to_string(info.job_id),
|
||||
compaction_input, compaction_service_result, options_override);
|
||||
if (is_override_wait_result_) {
|
||||
*result = override_wait_result_;
|
||||
*compaction_service_result = override_wait_result_;
|
||||
}
|
||||
compaction_num_.fetch_add(1);
|
||||
if (s.ok()) {
|
||||
@@ -139,8 +135,7 @@ class MyTestCompactionService : public CompactionService {
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic_int compaction_num_{0};
|
||||
std::map<std::string, std::string> jobs_;
|
||||
std::map<std::string, CompactionServiceJobInfo> infos_;
|
||||
std::map<uint64_t, std::string> jobs_;
|
||||
const std::string db_path_;
|
||||
Options options_;
|
||||
std::shared_ptr<Statistics> statistics_;
|
||||
@@ -177,7 +172,6 @@ class CompactionServiceTest : public DBTestBase {
|
||||
remote_table_properties_collector_factories);
|
||||
options->compaction_service = compaction_service_;
|
||||
DestroyAndReopen(*options);
|
||||
CreateAndReopenWithCF({"cf_1", "cf_2", "cf_3"}, *options);
|
||||
}
|
||||
|
||||
Statistics* GetCompactorStatistics() { return compactor_statistics_.get(); }
|
||||
@@ -189,45 +183,36 @@ class CompactionServiceTest : public DBTestBase {
|
||||
return static_cast_with_check<MyTestCompactionService>(cs);
|
||||
}
|
||||
|
||||
void GenerateTestData(bool move_files_manually = false) {
|
||||
// Generate 20 files @ L2 Per CF
|
||||
for (int cf_id = 0; cf_id < static_cast<int>(handles_.size()); cf_id++) {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(cf_id, Key(key_id), "value" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush(cf_id));
|
||||
}
|
||||
if (move_files_manually) {
|
||||
MoveFilesToLevel(2, cf_id);
|
||||
}
|
||||
|
||||
// Generate 10 files @ L1 overlap with all 20 files @ L2
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(
|
||||
Put(cf_id, Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush(cf_id));
|
||||
}
|
||||
if (move_files_manually) {
|
||||
MoveFilesToLevel(1, cf_id);
|
||||
ASSERT_EQ(FilesPerLevel(cf_id), "0,10,20");
|
||||
void GenerateTestData() {
|
||||
// Generate 20 files @ L2
|
||||
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" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
// Generate 10 files @ L1 overlap with all 20 files @ L2
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(FilesPerLevel(), "0,10,20");
|
||||
}
|
||||
|
||||
void VerifyTestData() {
|
||||
for (int cf_id = 0; cf_id < static_cast<int>(handles_.size()); cf_id++) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(cf_id, Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,10 +234,32 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
Statistics* primary_statistics = GetPrimaryStatistics();
|
||||
Statistics* compactor_statistics = GetCompactorStatistics();
|
||||
|
||||
GenerateTestData();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
VerifyTestData();
|
||||
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" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
auto my_cs = GetCompactionService();
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), 1);
|
||||
|
||||
@@ -315,8 +322,7 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
assert(*id != kNullUniqueId64x2);
|
||||
verify_passed++;
|
||||
});
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "cf_1", "cf_2", "cf_3"},
|
||||
options);
|
||||
Reopen(options);
|
||||
ASSERT_GT(verify_passed, 0);
|
||||
Close();
|
||||
}
|
||||
@@ -484,9 +490,26 @@ TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
new PartialDeleteCompactionFilter());
|
||||
options.compaction_filter = delete_comp_filter.get();
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
|
||||
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" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
@@ -528,17 +551,17 @@ TEST_F(CompactionServiceTest, ConcurrentCompaction) {
|
||||
options.level0_file_num_compaction_trigger = 100;
|
||||
options.max_background_jobs = 20;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData(true);
|
||||
GenerateTestData();
|
||||
|
||||
ColumnFamilyMetaData meta;
|
||||
db_->GetColumnFamilyMetaData(&meta);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
for (const auto& file : meta.levels[1].files) {
|
||||
threads.emplace_back([&]() {
|
||||
threads.emplace_back(std::thread([&]() {
|
||||
std::string fname = file.db_path + "/" + file.name;
|
||||
ASSERT_OK(db_->CompactFiles(CompactionOptions(), {fname}, 2));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
@@ -547,7 +570,14 @@ TEST_F(CompactionServiceTest, ConcurrentCompaction) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// verify result
|
||||
VerifyTestData();
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
auto my_cs = GetCompactionService();
|
||||
ASSERT_EQ(my_cs->GetCompactionNum(), 10);
|
||||
ASSERT_EQ(FilesPerLevel(), "0,0,10");
|
||||
@@ -557,7 +587,21 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
Options options = CurrentOptions();
|
||||
ReopenWithCompactionService(&options);
|
||||
|
||||
GenerateTestData();
|
||||
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" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
auto my_cs =
|
||||
static_cast_with_check<MyTestCompactionService>(GetCompactionService());
|
||||
@@ -632,9 +676,32 @@ TEST_F(CompactionServiceTest, FallbackLocalAuto) {
|
||||
|
||||
my_cs->OverrideStartStatus(CompactionServiceJobStatus::kUseLocal);
|
||||
|
||||
GenerateTestData();
|
||||
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" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
VerifyTestData();
|
||||
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(my_cs->GetCompactionNum(), 0);
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
#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 "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class TieredCompactionTest : public DBTestBase {
|
||||
|
||||
class TieredCompactionTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
TieredCompactionTest()
|
||||
: DBTestBase("tiered_compaction_test", /*env_do_fsync=*/true),
|
||||
@@ -123,8 +123,14 @@ class TieredCompactionTest : public DBTestBase {
|
||||
pl_stats.Clear();
|
||||
}
|
||||
|
||||
// bottommost_temperature is renaming to last_level_temperature, set either
|
||||
// of them should have the same effect.
|
||||
void SetColdTemperature(Options& options) {
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
if (GetParam()) {
|
||||
options.bottommost_temperature = Temperature::kCold;
|
||||
} else {
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -166,7 +172,7 @@ class TieredCompactionTest : public DBTestBase {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
TEST_P(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -328,7 +334,7 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
|
||||
TEST_F(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
TEST_P(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -502,7 +508,7 @@ TEST_F(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_F(TieredCompactionTest, LevelColdRangeDelete) {
|
||||
TEST_P(TieredCompactionTest, LevelColdRangeDelete) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -608,7 +614,7 @@ class SingleKeySstPartitionerFactory : public SstPartitionerFactory {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TieredCompactionTest, LevelOutofBoundaryRangeDelete) {
|
||||
TEST_P(TieredCompactionTest, LevelOutofBoundaryRangeDelete) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 3;
|
||||
const int kNumKeys = 10;
|
||||
@@ -737,7 +743,7 @@ TEST_F(TieredCompactionTest, LevelOutofBoundaryRangeDelete) {
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
|
||||
TEST_F(TieredCompactionTest, UniversalRangeDelete) {
|
||||
TEST_P(TieredCompactionTest, UniversalRangeDelete) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 10;
|
||||
@@ -869,7 +875,7 @@ TEST_F(TieredCompactionTest, UniversalRangeDelete) {
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
|
||||
TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -1093,7 +1099,7 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
|
||||
TEST_F(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
TEST_P(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -1234,7 +1240,10 @@ TEST_F(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
db_->ReleaseSnapshot(temp_snap);
|
||||
}
|
||||
|
||||
TEST_F(TieredCompactionTest, CheckInternalKeyRange) {
|
||||
INSTANTIATE_TEST_CASE_P(TieredCompactionTest, TieredCompactionTest,
|
||||
testing::Bool());
|
||||
|
||||
TEST_P(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.
|
||||
@@ -1309,8 +1318,8 @@ TEST_F(TieredCompactionTest, CheckInternalKeyRange) {
|
||||
|
||||
class PrecludeLastLevelTest : public DBTestBase {
|
||||
public:
|
||||
PrecludeLastLevelTest(std::string test_name = "preclude_last_level_test")
|
||||
: DBTestBase(test_name, /*env_do_fsync=*/false) {
|
||||
PrecludeLastLevelTest()
|
||||
: DBTestBase("preclude_last_level_test", /*env_do_fsync=*/false) {
|
||||
mock_clock_ = std::make_shared<MockSystemClock>(env_->GetSystemClock());
|
||||
mock_clock_->SetCurrentTime(kMockStartTime);
|
||||
mock_env_ = std::make_unique<CompositeEnvWrapper>(env_, mock_clock_);
|
||||
@@ -1329,7 +1338,7 @@ class PrecludeLastLevelTest : public DBTestBase {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::StartPeriodicTaskScheduler:Init", [&](void* arg) {
|
||||
auto periodic_task_scheduler_ptr =
|
||||
static_cast<PeriodicTaskScheduler*>(arg);
|
||||
reinterpret_cast<PeriodicTaskScheduler*>(arg);
|
||||
periodic_task_scheduler_ptr->TEST_OverrideTimer(mock_clock_.get());
|
||||
});
|
||||
mock_clock_->SetCurrentTime(kMockStartTime);
|
||||
@@ -1585,235 +1594,6 @@ TEST_F(PrecludeLastLevelTest, SmallPrecludeTime) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// Test Param: protection_bytes_per_key for WriteBatch
|
||||
class TimedPutPrecludeLastLevelTest
|
||||
: public PrecludeLastLevelTest,
|
||||
public testing::WithParamInterface<size_t> {
|
||||
public:
|
||||
TimedPutPrecludeLastLevelTest()
|
||||
: PrecludeLastLevelTest("timed_put_preclude_last_level_test") {}
|
||||
};
|
||||
|
||||
TEST_P(TimedPutPrecludeLastLevelTest, FastTrackTimedPutToLastLevel) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
|
||||
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;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions wo;
|
||||
wo.protection_bytes_per_key = GetParam();
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(rnd.Uniform(10) + 1));
|
||||
});
|
||||
|
||||
for (int i = 0; i < kNumKeys / 2; 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.
|
||||
// With above mock clock operations, write_unix_time 50 should be before
|
||||
// current_time - preclude_last_level_seconds.
|
||||
// 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 / 2; i < kNumKeys; i++) {
|
||||
ASSERT_OK(TimedPut(0, Key(i), rnd.RandomString(100), 50, wo));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// TimedPut file moved to the last level immediately.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
|
||||
// Wait more than preclude_last_level time, Put file eventually moved to the
|
||||
// last level.
|
||||
mock_clock_->MockSleepForSeconds(100);
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(TimedPutPrecludeLastLevelTest, InterleavedTimedPutAndPut) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.disable_auto_compactions = true;
|
||||
options.preclude_last_level_data_seconds = 1 * 24 * 60 * 60;
|
||||
options.env = mock_env_.get();
|
||||
options.num_levels = 7;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
options.default_write_temperature = Temperature::kHot;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions wo;
|
||||
wo.protection_bytes_per_key = GetParam();
|
||||
|
||||
// Start time: kMockStartTime = 10000000;
|
||||
ASSERT_OK(TimedPut(0, Key(0), "v0", kMockStartTime - 1 * 24 * 60 * 60, wo));
|
||||
ASSERT_OK(Put(Key(1), "v1", wo));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(TimedPutPrecludeLastLevelTest, PreserveTimedPutOnPenultimateLevel) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.disable_auto_compactions = true;
|
||||
options.preclude_last_level_data_seconds = 3 * 24 * 60 * 60;
|
||||
int seconds_between_recording = (3 * 24 * 60 * 60) / kMaxSeqnoTimePairsPerCF;
|
||||
options.env = mock_env_.get();
|
||||
options.num_levels = 7;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
options.default_write_temperature = Temperature::kHot;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions wo;
|
||||
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
|
||||
// visible to the earliest snapshot. With this, we can test relevant seqno to
|
||||
// time mapping recorded in SST file also covers preferred seqno, not just
|
||||
// the seqno in the internal keys.
|
||||
auto* snap1 = db_->GetSnapshot();
|
||||
// Start time: kMockStartTime = 10000000;
|
||||
ASSERT_OK(TimedPut(0, Key(0), "v0", kMockStartTime - 1 * 24 * 60 * 60, wo));
|
||||
ASSERT_OK(TimedPut(0, Key(1), "v1", kMockStartTime - 1 * 24 * 60 * 60, wo));
|
||||
ASSERT_OK(TimedPut(0, Key(2), "v2", kMockStartTime - 1 * 24 * 60 * 60, wo));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Should still be in penultimate level.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
// Wait one more day and release snapshot. Data's preferred seqno should be
|
||||
// swapped in, but data should still stay in penultimate level. SST file's
|
||||
// seqno to time mapping should continue to cover preferred seqno after
|
||||
// compaction.
|
||||
db_->ReleaseSnapshot(snap1);
|
||||
mock_clock_->MockSleepForSeconds(1 * 24 * 60 * 60);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
// Wait one more day and data are eligible to be placed on last level.
|
||||
// Instead of waiting exactly one more day, here we waited
|
||||
// `seconds_between_recording` less seconds to show that it's not precise.
|
||||
// Data could start to be placed on cold tier one recording interval before
|
||||
// they exactly become cold based on the setting. For this one column family
|
||||
// setting preserving 3 days of recording, it's about 43 minutes.
|
||||
mock_clock_->MockSleepForSeconds(1 * 24 * 60 * 60 -
|
||||
seconds_between_recording);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
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 = GetParam();
|
||||
|
||||
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_F(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
@@ -2043,6 +1823,7 @@ TEST_P(PrecludeLastLevelTestWithParms, PeriodicCompactionToPenultimateLevel) {
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.num_levels = kNumLevels;
|
||||
options.ignore_max_compaction_bytes_for_input = false;
|
||||
options.periodic_compaction_seconds = 10000;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -2431,309 +2212,6 @@ TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// Tests DBIter::GetProperty("rocksdb.iterator.write-time") return a data's
|
||||
// approximate write unix time.
|
||||
// Test Param:
|
||||
// 1) use tailing iterator or regular iterator (when it applies)
|
||||
class IteratorWriteTimeTest : public PrecludeLastLevelTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
IteratorWriteTimeTest() : PrecludeLastLevelTest("iterator_write_time_test") {}
|
||||
|
||||
uint64_t VerifyKeyAndGetWriteTime(Iterator* iter,
|
||||
const std::string& expected_key) {
|
||||
std::string prop;
|
||||
uint64_t write_time = 0;
|
||||
EXPECT_TRUE(iter->Valid());
|
||||
EXPECT_EQ(expected_key, iter->key());
|
||||
EXPECT_OK(iter->GetProperty("rocksdb.iterator.write-time", &prop));
|
||||
Slice prop_slice = prop;
|
||||
EXPECT_TRUE(GetFixed64(&prop_slice, &write_time));
|
||||
return write_time;
|
||||
}
|
||||
|
||||
void VerifyKeyAndWriteTime(Iterator* iter, const std::string& expected_key,
|
||||
uint64_t expected_write_time) {
|
||||
std::string prop;
|
||||
uint64_t write_time = 0;
|
||||
EXPECT_TRUE(iter->Valid());
|
||||
EXPECT_EQ(expected_key, iter->key());
|
||||
EXPECT_OK(iter->GetProperty("rocksdb.iterator.write-time", &prop));
|
||||
Slice prop_slice = prop;
|
||||
EXPECT_TRUE(GetFixed64(&prop_slice, &write_time));
|
||||
EXPECT_EQ(expected_write_time, write_time);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(IteratorWriteTimeTest, ReadFromMemtables) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
const int kSecondsPerRecording = 101;
|
||||
const int kKeyWithWriteTime = 25;
|
||||
const uint64_t kUserSpecifiedWriteTime =
|
||||
kMockStartTime + kSecondsPerRecording * 15;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
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);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
if (i == kKeyWithWriteTime) {
|
||||
ASSERT_OK(
|
||||
TimedPut(Key(i), rnd.RandomString(100), kUserSpecifiedWriteTime));
|
||||
} else {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
}
|
||||
}
|
||||
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = GetParam();
|
||||
int i;
|
||||
|
||||
// Forward iteration
|
||||
uint64_t start_time = 0;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
for (iter->SeekToFirst(), i = 0; iter->Valid(); iter->Next(), i++) {
|
||||
if (start_time == 0) {
|
||||
start_time = VerifyKeyAndGetWriteTime(iter.get(), Key(i));
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), kUserSpecifiedWriteTime);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Backward iteration
|
||||
{
|
||||
ropts.tailing = false;
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
for (iter->SeekToLast(), i = kNumKeys - 1; iter->Valid();
|
||||
iter->Prev(), i--) {
|
||||
if (i == 0) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), start_time);
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), kUserSpecifiedWriteTime);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// 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));
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->Seek(Key(kKeyWithWriteTime));
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(kKeyWithWriteTime),
|
||||
kUserSpecifiedWriteTime);
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->Seek(Key(kKeyWithWriteTime));
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(kKeyWithWriteTime),
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(IteratorWriteTimeTest, ReadFromSstFile) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
const int kSecondsPerRecording = 101;
|
||||
const int kKeyWithWriteTime = 25;
|
||||
const uint64_t kUserSpecifiedWriteTime =
|
||||
kMockStartTime + kSecondsPerRecording * 15;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
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);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
if (i == kKeyWithWriteTime) {
|
||||
ASSERT_OK(
|
||||
TimedPut(Key(i), rnd.RandomString(100), kUserSpecifiedWriteTime));
|
||||
} else {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = GetParam();
|
||||
std::string prop;
|
||||
int i;
|
||||
|
||||
// Forward iteration
|
||||
uint64_t start_time = 0;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
for (iter->SeekToFirst(), i = 0; iter->Valid(); iter->Next(), i++) {
|
||||
if (start_time == 0) {
|
||||
start_time = VerifyKeyAndGetWriteTime(iter.get(), Key(i));
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
// It's not precisely kUserSpecifiedWriteTime, instead it has a margin
|
||||
// of error that is one recording apart while we convert write time to
|
||||
// sequence number, and then back to write time.
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
kUserSpecifiedWriteTime - kSecondsPerRecording);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Backward iteration
|
||||
{
|
||||
ropts.tailing = false;
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
for (iter->SeekToLast(), i = kNumKeys - 1; iter->Valid();
|
||||
iter->Prev(), i--) {
|
||||
if (i == 0) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), start_time);
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
kUserSpecifiedWriteTime - kSecondsPerRecording);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// 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); });
|
||||
ASSERT_OK(Put("a", "val"));
|
||||
ASSERT_TRUE(dbfull()->TEST_GetSeqnoToTimeMapping().Empty());
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
// "a" is retrieved from memtable, its write time is unknown because the
|
||||
// seqno to time mapping recording is not available.
|
||||
VerifyKeyAndWriteTime(iter.get(), "a",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
for (iter->Next(), i = 0; iter->Valid(); iter->Next(), i++) {
|
||||
if (i == 0) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), start_time);
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
kUserSpecifiedWriteTime - kSecondsPerRecording);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// There is no write time info for "a" after it's flushed to SST file either.
|
||||
ASSERT_OK(Flush());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
VerifyKeyAndWriteTime(iter.get(), "a",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
// Sequence number zeroed out after compacted to the last level, write time
|
||||
// all becomes zero.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
for (iter->Next(), i = 0; iter->Valid(); iter->Next(), i++) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), 0);
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(IteratorWriteTimeTest, MergeReturnsBaseValueWriteTime) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kSecondsPerRecording = 101;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
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);
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
ASSERT_OK(Put("foo", "fv1"));
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
ASSERT_OK(Put("bar", "bv1"));
|
||||
ASSERT_OK(Merge("foo", "bv1"));
|
||||
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = GetParam();
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
uint64_t bar_time = VerifyKeyAndGetWriteTime(iter.get(), "bar");
|
||||
iter->Next();
|
||||
uint64_t foo_time = VerifyKeyAndGetWriteTime(iter.get(), "foo");
|
||||
// "foo" has an older write time because its base value's write time is used
|
||||
ASSERT_GT(bar_time, foo_time);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(IteratorWriteTimeTest, IteratorWriteTimeTest,
|
||||
testing::Bool());
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -170,7 +170,7 @@ void DoRandomIteraratorTest(DB* db, std::vector<std::string> source_strings,
|
||||
|
||||
class DoubleComparator : public Comparator {
|
||||
public:
|
||||
DoubleComparator() = default;
|
||||
DoubleComparator() {}
|
||||
|
||||
const char* Name() const override { return "DoubleComparator"; }
|
||||
|
||||
@@ -198,7 +198,7 @@ class DoubleComparator : public Comparator {
|
||||
|
||||
class HashComparator : public Comparator {
|
||||
public:
|
||||
HashComparator() = default;
|
||||
HashComparator() {}
|
||||
|
||||
const char* Name() const override { return "HashComparator"; }
|
||||
|
||||
@@ -221,7 +221,7 @@ class HashComparator : public Comparator {
|
||||
|
||||
class TwoStrComparator : public Comparator {
|
||||
public:
|
||||
TwoStrComparator() = default;
|
||||
TwoStrComparator() {}
|
||||
|
||||
const char* Name() const override { return "TwoStrComparator"; }
|
||||
|
||||
@@ -372,7 +372,7 @@ TEST_P(ComparatorDBTest, Uint64Comparator) {
|
||||
uint64_t r = rnd64.Next();
|
||||
std::string str;
|
||||
str.resize(8);
|
||||
memcpy(str.data(), static_cast<void*>(&r), 8);
|
||||
memcpy(&str[0], static_cast<void*>(&r), 8);
|
||||
source_strings.push_back(str);
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
+38
-7
@@ -53,9 +53,10 @@ class ErrorFS : public FileSystemWrapper {
|
||||
num_writable_file_errors_(0) {}
|
||||
const char* Name() const override { return "ErrorEnv"; }
|
||||
|
||||
IOStatus NewWritableFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
virtual IOStatus NewWritableFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
result->reset();
|
||||
if (writable_file_error_) {
|
||||
++num_writable_file_errors_;
|
||||
@@ -822,6 +823,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
Status s;
|
||||
@@ -852,6 +854,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
Status s;
|
||||
for (const auto& mode : corruption_modes) {
|
||||
delete db_;
|
||||
@@ -883,6 +886,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
@@ -919,6 +923,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
@@ -958,6 +963,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
@@ -1026,6 +1032,7 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
@@ -1040,6 +1047,7 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
|
||||
mock->SetCorruptionMode(mock::MockTableFactory::kCorruptNone);
|
||||
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "true"}}));
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_NOK(
|
||||
@@ -1052,6 +1060,7 @@ TEST_F(CorruptionTest, FlushKeyOrderCheck) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "true"}}));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo1", "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo2", "v1"));
|
||||
@@ -1076,6 +1085,28 @@ TEST_F(CorruptionTest, FlushKeyOrderCheck) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, DisableKeyOrderCheck) {
|
||||
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "false"}}));
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"OutputValidator::Add:order_check",
|
||||
[&](void* /*arg*/) { ASSERT_TRUE(false); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo1", "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo3", "v1"));
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo2", "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo4", "v1"));
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(
|
||||
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
@@ -1102,7 +1133,7 @@ TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
int count{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::VerifyFullFileChecksum:mismatch", [&](void* arg) {
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
ASSERT_NE(s, nullptr);
|
||||
++count;
|
||||
ASSERT_NOK(*s);
|
||||
@@ -1247,7 +1278,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLogSizeAndMaybeTruncate:0", [&](void* arg) {
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
@@ -1429,7 +1460,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::Open::BeforeSyncWAL", [&](void* arg) {
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
@@ -1597,7 +1628,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecoveryWithFlush) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLogSizeAndMaybeTruncate:0", [&](void* arg) {
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
|
||||
@@ -131,7 +131,7 @@ TEST_F(CuckooTableDBTest, Flush) {
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
|
||||
TablePropertiesCollection ptc;
|
||||
ASSERT_OK(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(1U, ptc.size());
|
||||
ASSERT_EQ(3U, ptc.begin()->second->num_entries);
|
||||
@@ -148,7 +148,7 @@ TEST_F(CuckooTableDBTest, Flush) {
|
||||
ASSERT_OK(Put("key6", "v6"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
|
||||
ASSERT_OK(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(2U, ptc.size());
|
||||
auto row = ptc.begin();
|
||||
@@ -166,7 +166,7 @@ TEST_F(CuckooTableDBTest, Flush) {
|
||||
ASSERT_OK(Delete("key5"));
|
||||
ASSERT_OK(Delete("key4"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
ASSERT_OK(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(3U, ptc.size());
|
||||
row = ptc.begin();
|
||||
@@ -191,7 +191,7 @@ TEST_F(CuckooTableDBTest, FlushWithDuplicateKeys) {
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
|
||||
TablePropertiesCollection ptc;
|
||||
ASSERT_OK(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(1U, ptc.size());
|
||||
ASSERT_EQ(2U, ptc.begin()->second->num_entries);
|
||||
@@ -209,7 +209,7 @@ static std::string Key(int i) {
|
||||
static std::string Uint64Key(uint64_t i) {
|
||||
std::string str;
|
||||
str.resize(8);
|
||||
memcpy(str.data(), static_cast<void*>(&i), 8);
|
||||
memcpy(&str[0], static_cast<void*>(&i), 8);
|
||||
return str;
|
||||
}
|
||||
} // namespace.
|
||||
|
||||
+136
-308
@@ -25,60 +25,10 @@
|
||||
#include "util/random.h"
|
||||
#include "utilities/counted_fs.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
class MyFlushBlockPolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
explicit MyFlushBlockPolicy(const int num_keys_in_block,
|
||||
const BlockBuilder& data_block_builder)
|
||||
: num_keys_in_block_(num_keys_in_block),
|
||||
num_keys_(0),
|
||||
data_block_builder_(data_block_builder) {}
|
||||
|
||||
bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (data_block_builder_.empty()) {
|
||||
// First key in this block
|
||||
num_keys_ = 1;
|
||||
return false;
|
||||
}
|
||||
// Flush every 10 keys
|
||||
if (num_keys_ == num_keys_in_block_) {
|
||||
num_keys_ = 1;
|
||||
return true;
|
||||
}
|
||||
num_keys_++;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int num_keys_in_block_;
|
||||
int num_keys_;
|
||||
const BlockBuilder& data_block_builder_;
|
||||
};
|
||||
|
||||
class MyFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
|
||||
public:
|
||||
explicit MyFlushBlockPolicyFactory(const int num_keys_in_block)
|
||||
: num_keys_in_block_(num_keys_in_block) {}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "MyFlushBlockPolicyFactory";
|
||||
}
|
||||
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new MyFlushBlockPolicy(num_keys_in_block_, data_block_builder);
|
||||
}
|
||||
|
||||
private:
|
||||
const int num_keys_in_block_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static bool enable_io_uring = true;
|
||||
extern "C" bool RocksDbIOUringEnable() { return enable_io_uring; }
|
||||
@@ -442,43 +392,6 @@ TEST_F(DBBasicTest, PutSingleDeleteGet) {
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, TimedPutBasic) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(TimedPut(1, "foo", "v1", /*write_unix_time=*/0));
|
||||
// Read from memtable
|
||||
ASSERT_EQ("v1", Get(1, "foo"));
|
||||
ASSERT_OK(TimedPut(1, "foo", "v2.1", /*write_unix_time=*/3));
|
||||
ASSERT_EQ("v2.1", Get(1, "foo"));
|
||||
|
||||
// Read from sst file
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), handles_[1]));
|
||||
ASSERT_OK(Merge(1, "foo", "v2.2"));
|
||||
ASSERT_EQ("v2.1,v2.2", Get(1, "foo"));
|
||||
ASSERT_OK(Delete(1, "foo"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
|
||||
|
||||
ASSERT_OK(TimedPut(1, "bar", "bv1", /*write_unix_time=*/0));
|
||||
ASSERT_EQ("bv1", Get(1, "bar"));
|
||||
ASSERT_OK(TimedPut(1, "baz", "bzv1", /*write_unix_time=*/0));
|
||||
ASSERT_EQ("bzv1", Get(1, "baz"));
|
||||
if (option_config_ != kRowCache) {
|
||||
std::string range_del_begin = "b";
|
||||
std::string range_del_end = "baz";
|
||||
Slice begin_rdel = range_del_begin, end_rdel = range_del_end;
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), handles_[1], begin_rdel, end_rdel));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "bar"));
|
||||
}
|
||||
|
||||
ASSERT_EQ("bzv1", Get(1, "baz"));
|
||||
ASSERT_OK(SingleDelete(1, "baz"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "baz"));
|
||||
} while (ChangeOptions(kSkipPlainTable));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, EmptyFlush) {
|
||||
// It is possible to produce empty flushes when using single deletes. Tests
|
||||
// whether empty flushes cause issues.
|
||||
@@ -1405,9 +1318,9 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCF) {
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
int cf = i / 3;
|
||||
int cf_key = 1 % 3;
|
||||
cf_kv_vec.emplace_back(
|
||||
cf_kv_vec.emplace_back(std::make_tuple(
|
||||
cf, "cf" + std::to_string(cf) + "_key_" + std::to_string(cf_key),
|
||||
"cf" + std::to_string(cf) + "_val_" + std::to_string(cf_key));
|
||||
"cf" + std::to_string(cf) + "_val_" + std::to_string(cf_key)));
|
||||
ASSERT_OK(Put(std::get<0>(cf_kv_vec[i]), std::get<1>(cf_kv_vec[i]),
|
||||
std::get<2>(cf_kv_vec[i])));
|
||||
}
|
||||
@@ -1415,7 +1328,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCF) {
|
||||
int get_sv_count = 0;
|
||||
ROCKSDB_NAMESPACE::DBImpl* db = static_cast_with_check<DBImpl>(db_);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiCFSnapshot::AfterRefSV", [&](void* /*arg*/) {
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (++get_sv_count == 2) {
|
||||
// After MultiGet refs a couple of CFs, flush all CFs so MultiGet
|
||||
// is forced to repeat the process
|
||||
@@ -1515,10 +1428,9 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFMutex) {
|
||||
int retries = 0;
|
||||
bool last_try = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiCFSnapshot::LastTry",
|
||||
[&](void* /*arg*/) { last_try = true; });
|
||||
"DBImpl::MultiGet::LastTry", [&](void* /*arg*/) { last_try = true; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiCFSnapshot::AfterRefSV", [&](void* /*arg*/) {
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (last_try) {
|
||||
return;
|
||||
}
|
||||
@@ -1534,10 +1446,10 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFMutex) {
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::MultiCFSnapshot::AfterLastTryRefSV",
|
||||
{"DBImpl::MultiGet::AfterLastTryRefSV",
|
||||
"DBMultiGetTestWithParam::MultiGetMultiCFMutex:BeforeCreateSV"},
|
||||
{"DBMultiGetTestWithParam::MultiGetMultiCFMutex:AfterCreateSV",
|
||||
"DBImpl::MultiCFSnapshot::BeforeLastTryUnRefSV"},
|
||||
"DBImpl::MultiGet::BeforeLastTryUnRefSV"},
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -1603,7 +1515,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFSnapshot) {
|
||||
int get_sv_count = 0;
|
||||
ROCKSDB_NAMESPACE::DBImpl* db = static_cast_with_check<DBImpl>(db_);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiCFSnapshot::AfterRefSV", [&](void* /*arg*/) {
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (++get_sv_count == 2) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
@@ -1941,7 +1853,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedEmptyLevel) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test requires coroutine support");
|
||||
@@ -1953,37 +1865,12 @@ TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(env_->GetFileSystem()));
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fault_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
|
||||
LRUCacheOptions cache_opts;
|
||||
cache_opts.capacity = 1 << 20;
|
||||
|
||||
BlockBasedTableOptions table_opts;
|
||||
table_opts.metadata_cache_options.top_level_index_pinning = PinningTier::kAll;
|
||||
table_opts.metadata_cache_options.partition_pinning = PinningTier::kNone;
|
||||
table_opts.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
table_opts.cache_index_and_filter_blocks = true;
|
||||
table_opts.block_cache = cache_opts.MakeSharedCache();
|
||||
table_opts.flush_block_policy_factory.reset(new MyFlushBlockPolicyFactory(1));
|
||||
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_opts));
|
||||
Reopen(options);
|
||||
int key;
|
||||
|
||||
// Setup the LSM so that the following search bounds are generated for
|
||||
// key 9 for each level -
|
||||
// Level 1 - lb = 0, rb = max
|
||||
// Level 2 - lb = 0, rb = 0
|
||||
// Level 3 - lb = 0, rb = -1
|
||||
// Level 4 - lb = 0, rb = 0
|
||||
|
||||
key = 9;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -1994,9 +1881,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
key = 9;
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
const Snapshot* snap = dbfull()->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "val_l2_ext_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
// Leave level 3 empty
|
||||
MoveFilesToLevel(2);
|
||||
@@ -2015,138 +1899,8 @@ TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
keys.push_back("key_" + std::to_string(9));
|
||||
keys.push_back("key_" + std::to_string(9));
|
||||
|
||||
int num_reads = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FaultInjectionTestFS::RandomRead", [&](void*) {
|
||||
++num_reads;
|
||||
// Fail on the 2nd read. First read is index partition,
|
||||
// second read is data block in level 1
|
||||
if (num_reads == 2) {
|
||||
fault_fs->SetFilesystemActive(false);
|
||||
} else {
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
size_t capacity = table_opts.block_cache->GetCapacity();
|
||||
table_opts.block_cache->SetCapacity(0);
|
||||
table_opts.block_cache->SetCapacity(capacity);
|
||||
|
||||
values = MultiGet(keys, nullptr, std::get<1>(GetParam()));
|
||||
ASSERT_EQ(values.size(), 2);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
dbfull()->ReleaseSnapshot(snap);
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesNonEmptyLevel) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(env_->GetFileSystem()));
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fault_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
|
||||
LRUCacheOptions cache_opts;
|
||||
cache_opts.capacity = 1 << 20;
|
||||
|
||||
BlockBasedTableOptions table_opts;
|
||||
table_opts.metadata_cache_options.top_level_index_pinning = PinningTier::kAll;
|
||||
table_opts.metadata_cache_options.partition_pinning = PinningTier::kNone;
|
||||
table_opts.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
table_opts.cache_index_and_filter_blocks = true;
|
||||
table_opts.block_cache = cache_opts.MakeSharedCache();
|
||||
table_opts.flush_block_policy_factory.reset(new MyFlushBlockPolicyFactory(1));
|
||||
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_opts));
|
||||
Reopen(options);
|
||||
int key;
|
||||
|
||||
// Setup the LSM so that the following search bounds are generated for
|
||||
// key 9 for each level -
|
||||
// Level 1 - lb = 0, rb = max
|
||||
// Level 2 - lb = 0, rb = 0
|
||||
// Level 3 - lb = 0, rb = 1
|
||||
// Level 4 - N/A
|
||||
|
||||
key = 8;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
|
||||
key = 7;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
key = 9;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(3);
|
||||
|
||||
key = 5;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
key = 9;
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "merge1_l2_" + std::to_string(key)));
|
||||
const Snapshot* snap = dbfull()->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "merge2_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
key = 2;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
key = 6;
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back("key_" + std::to_string(9));
|
||||
keys.push_back("key_" + std::to_string(9));
|
||||
|
||||
int num_reads = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FaultInjectionTestFS::RandomRead", [&](void*) {
|
||||
++num_reads;
|
||||
// Fail on the 2nd read. First read is index partition,
|
||||
// second read is data block in level 1
|
||||
if (num_reads == 2) {
|
||||
fault_fs->SetFilesystemActive(false);
|
||||
} else {
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
size_t capacity = table_opts.block_cache->GetCapacity();
|
||||
table_opts.block_cache->SetCapacity(0);
|
||||
table_opts.block_cache->SetCapacity(capacity);
|
||||
|
||||
values = MultiGet(keys, nullptr, std::get<1>(GetParam()));
|
||||
ASSERT_EQ(values.size(), 2);
|
||||
ASSERT_EQ(values[0], "Corruption: Not active");
|
||||
ASSERT_EQ(values[1], "val_l2_9,merge1_l2_9,merge2_l2_9");
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
dbfull()->ReleaseSnapshot(snap);
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevelMerge) {
|
||||
@@ -2645,9 +2399,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2690,9 +2444,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2755,9 +2509,9 @@ TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||
key_strs.push_back(Key(21));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2800,9 +2554,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(56));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2843,8 +2597,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
||||
// 19 and 26 are in L2, but overlap with L0 and L1 file ranges
|
||||
key_strs.push_back(Key(19));
|
||||
key_strs.push_back(Key(26));
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2879,8 +2633,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeDelInL1) {
|
||||
// 139 and 163 are in L2, but overlap with a range deletes in L1
|
||||
key_strs.push_back(Key(139));
|
||||
key_strs.push_back(Key(163));
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2909,9 +2663,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2WithRangeDelInL1) {
|
||||
key_strs.push_back(Key(139));
|
||||
key_strs.push_back(Key(144));
|
||||
key_strs.push_back(Key(163));
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2942,9 +2696,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetNoIOUring) {
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -3323,9 +3077,9 @@ TEST_F(DBBasicTest, MultiGetIOBufferOverrun) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -3380,7 +3134,8 @@ TEST_F(DBBasicTest, BestEffortsRecoveryWithVersionBuildingFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("Inject corruption");
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -3699,10 +3454,10 @@ TEST_F(DBBasicTest, ConcurrentlyCloseDB) {
|
||||
DestroyAndReopen(options);
|
||||
std::vector<std::thread> workers;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
workers.emplace_back([&]() {
|
||||
workers.push_back(std::thread([&]() {
|
||||
auto s = db_->Close();
|
||||
ASSERT_OK(s);
|
||||
});
|
||||
}));
|
||||
}
|
||||
for (auto& w : workers) {
|
||||
w.join();
|
||||
@@ -3811,7 +3566,7 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
}
|
||||
table_options.flush_block_policy_factory.reset(
|
||||
new MyFlushBlockPolicyFactory(10));
|
||||
new MyFlushBlockPolicyFactory());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
if (!compression_enabled_) {
|
||||
options.compression = kNoCompression;
|
||||
@@ -3899,6 +3654,46 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
static void TearDownTestCase() {}
|
||||
|
||||
protected:
|
||||
class MyFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
|
||||
public:
|
||||
MyFlushBlockPolicyFactory() {}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "MyFlushBlockPolicyFactory";
|
||||
}
|
||||
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new MyFlushBlockPolicy(data_block_builder);
|
||||
}
|
||||
};
|
||||
|
||||
class MyFlushBlockPolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
explicit MyFlushBlockPolicy(const BlockBuilder& data_block_builder)
|
||||
: num_keys_(0), data_block_builder_(data_block_builder) {}
|
||||
|
||||
bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (data_block_builder_.empty()) {
|
||||
// First key in this block
|
||||
num_keys_ = 1;
|
||||
return false;
|
||||
}
|
||||
// Flush every 10 keys
|
||||
if (num_keys_ == 10) {
|
||||
num_keys_ = 1;
|
||||
return true;
|
||||
}
|
||||
num_keys_++;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int num_keys_;
|
||||
const BlockBuilder& data_block_builder_;
|
||||
};
|
||||
|
||||
class MyBlockCache : public CacheWrapper {
|
||||
public:
|
||||
explicit MyBlockCache(std::shared_ptr<Cache> target)
|
||||
@@ -3976,9 +3771,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGet) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -4157,9 +3952,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetDirectIO) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -4227,9 +4022,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetWithChecksumMismatch) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -4275,9 +4070,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetWithMissingFile) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(key_data.back());
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -4315,7 +4110,7 @@ class DeadlineRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
const IOOptions& options, IODebugContext* dbg) override;
|
||||
|
||||
IOStatus ReadAsync(FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(FSReadRequest&, void*)> cb,
|
||||
std::function<void(const FSReadRequest&, void*)> cb,
|
||||
void* cb_arg, void** io_handle, IOHandleDeleter* del_fn,
|
||||
IODebugContext* dbg) override;
|
||||
|
||||
@@ -4461,7 +4256,7 @@ IOStatus DeadlineRandomAccessFile::Read(uint64_t offset, size_t len,
|
||||
|
||||
IOStatus DeadlineRandomAccessFile::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn, IODebugContext* dbg) {
|
||||
const std::chrono::microseconds deadline = fs_.GetDeadline();
|
||||
const std::chrono::microseconds io_timeout = fs_.GetIOTimeout();
|
||||
@@ -4542,35 +4337,68 @@ TEST_P(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
ReopenWithColumnFamilies(GetCFNames(), options);
|
||||
|
||||
// Test batched MultiGet with an IO delay in the first data block read.
|
||||
// Both keys in the first CF should succeed as they're in the same data
|
||||
// block and would form one batch, and we check for deadline between
|
||||
// batches.
|
||||
// Test the non-batched version of MultiGet with multiple column
|
||||
// families
|
||||
std::vector<std::string> key_str;
|
||||
size_t i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
for (i = 0; i < 5; ++i) {
|
||||
key_str.emplace_back(Key(static_cast<int>(i)));
|
||||
}
|
||||
std::vector<ColumnFamilyHandle*> cfs(key_str.size());
|
||||
;
|
||||
std::vector<Slice> keys(key_str.size());
|
||||
std::vector<PinnableSlice> pin_values(keys.size());
|
||||
|
||||
std::vector<std::string> values(key_str.size());
|
||||
for (i = 0; i < key_str.size(); ++i) {
|
||||
// 2 keys per CF
|
||||
cfs[i] = handles_[i / 2];
|
||||
cfs[i] = handles_[i];
|
||||
keys[i] = Slice(key_str[i].data(), key_str[i].size());
|
||||
}
|
||||
|
||||
ReadOptions ro;
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
ro.async_io = GetParam();
|
||||
// Delay the first IO
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 0);
|
||||
|
||||
std::vector<Status> statuses(key_str.size());
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
std::vector<Status> statuses = dbfull()->MultiGet(ro, cfs, keys, &values);
|
||||
// The first key is successful because we check after the lookup, but
|
||||
// subsequent keys fail due to deadline exceeded
|
||||
CheckStatus(statuses, 1);
|
||||
|
||||
// Clear the cache
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
// Test non-batched Multiget with multiple column families and
|
||||
// introducing an IO delay in one of the middle CFs
|
||||
key_str.clear();
|
||||
for (i = 0; i < 10; ++i) {
|
||||
key_str.emplace_back(Key(static_cast<int>(i)));
|
||||
}
|
||||
cfs.resize(key_str.size());
|
||||
keys.resize(key_str.size());
|
||||
values.resize(key_str.size());
|
||||
for (i = 0; i < key_str.size(); ++i) {
|
||||
// 2 keys per CF
|
||||
cfs[i] = handles_[i / 2];
|
||||
keys[i] = Slice(key_str[i].data(), key_str[i].size());
|
||||
}
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 1);
|
||||
statuses = dbfull()->MultiGet(ro, cfs, keys, &values);
|
||||
CheckStatus(statuses, 3);
|
||||
|
||||
// Test batched MultiGet with an IO delay in the first data block read.
|
||||
// Both keys in the first CF should succeed as they're in the same data
|
||||
// block and would form one batch, and we check for deadline between
|
||||
// batches.
|
||||
std::vector<PinnableSlice> pin_values(keys.size());
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 0);
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 2);
|
||||
|
||||
// Similar to the previous one, but an IO delay in the third CF data block
|
||||
@@ -4648,7 +4476,7 @@ TEST_F(DBBasicTest, ManifestWriteFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::ProcessManifestWrites:AfterSyncManifest", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
ASSERT_OK(*s);
|
||||
// Manually overwrite return status
|
||||
*s = Status::IOError();
|
||||
@@ -4703,7 +4531,7 @@ TEST_F(DBBasicTest, FailOpenIfLoggerCreationFail) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"rocksdb::CreateLoggerFromOptions:AfterGetPath", [&](void* arg) {
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected");
|
||||
});
|
||||
@@ -4781,7 +4609,7 @@ TEST_F(DBBasicTest, VerifyFileChecksumsReadahead) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &filenames));
|
||||
for (const auto& name : filenames) {
|
||||
for (auto name : filenames) {
|
||||
if (ParseFileName(name, &number, &type)) {
|
||||
if (type == kTableFile) {
|
||||
sst_cnt++;
|
||||
|
||||
+121
-277
@@ -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"
|
||||
@@ -741,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 (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;
|
||||
@@ -879,7 +990,7 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
int iterations_tested = 0;
|
||||
for (bool partition : {false, true}) {
|
||||
SCOPED_TRACE("Partition? " + std::to_string(partition));
|
||||
for (const std::shared_ptr<Cache>& cache :
|
||||
for (std::shared_ptr<Cache> cache :
|
||||
{NewLRUCache(capacity),
|
||||
HyperClockCacheOptions(
|
||||
capacity,
|
||||
@@ -1140,7 +1251,7 @@ void DummyFillCache(Cache& cache, size_t entry_size,
|
||||
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
~CountingLogger() override = default;
|
||||
~CountingLogger() override {}
|
||||
using Logger::Logv;
|
||||
void Logv(const InfoLogLevel log_level, const char* format,
|
||||
va_list /*ap*/) override {
|
||||
@@ -1236,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,
|
||||
@@ -1530,7 +1373,7 @@ class StableCacheKeyTestFS : public FaultInjectionTestFS {
|
||||
SetFailGetUniqueId(true);
|
||||
}
|
||||
|
||||
~StableCacheKeyTestFS() override = default;
|
||||
virtual ~StableCacheKeyTestFS() override {}
|
||||
|
||||
IOStatus LinkFile(const std::string&, const std::string&, const IOOptions&,
|
||||
IODebugContext*) override {
|
||||
@@ -1581,7 +1424,7 @@ TEST_P(DBBlockCacheKeyTest, StableCacheKeys) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::BlockBasedTableBuilder:PreSetupBaseCacheKey",
|
||||
[&](void* arg) {
|
||||
TableProperties* props = static_cast<TableProperties*>(arg);
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
props->orig_file_number = 0;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -1723,7 +1566,7 @@ class CacheKeyTest : public testing::Test {
|
||||
tp_.db_id = std::to_string(db_id_);
|
||||
tp_.orig_file_number = file_number;
|
||||
bool is_stable;
|
||||
std::string cur_session_id; // ignored
|
||||
std::string cur_session_id = ""; // ignored
|
||||
uint64_t cur_file_number = 42; // ignored
|
||||
OffsetableCacheKey rv;
|
||||
BlockBasedTable::SetupBaseCacheKey(&tp_, cur_session_id, cur_file_number,
|
||||
@@ -1991,7 +1834,6 @@ class DBBlockCachePinningTest
|
||||
PinningTier unpartitioned_pinning_;
|
||||
};
|
||||
|
||||
#ifdef LZ4
|
||||
TEST_P(DBBlockCachePinningTest, TwoLevelDB) {
|
||||
// Creates one file in L0 and one file in L1. Both files have enough data that
|
||||
// their index and filter blocks are partitioned. The L1 file will also have
|
||||
@@ -2003,7 +1845,10 @@ TEST_P(DBBlockCachePinningTest, TwoLevelDB) {
|
||||
const int kNumKeysPerFile = kBlockSize * kNumBlocksPerFile / kKeySize;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kLZ4Compression;
|
||||
// `kNoCompression` makes the unit test more portable. But it relies on the
|
||||
// current behavior of persisting/accessing dictionary even when there's no
|
||||
// (de)compression happening, which seems fairly likely to change over time.
|
||||
options.compression = kNoCompression;
|
||||
options.compression_opts.max_dict_bytes = 4 << 10;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -2116,7 +1961,6 @@ TEST_P(DBBlockCachePinningTest, TwoLevelDB) {
|
||||
ASSERT_EQ(expected_compression_dict_misses,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
}
|
||||
#endif
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBBlockCachePinningTest, DBBlockCachePinningTest,
|
||||
|
||||
+6
-282
@@ -19,7 +19,6 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
@@ -78,7 +77,7 @@ class DBBloomFilterTestWithParam
|
||||
DBBloomFilterTestWithParam()
|
||||
: DBTestBase("db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
|
||||
~DBBloomFilterTestWithParam() override = default;
|
||||
~DBBloomFilterTestWithParam() override {}
|
||||
|
||||
void SetUp() override {
|
||||
bfp_impl_ = std::get<0>(GetParam());
|
||||
@@ -529,7 +528,6 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
table_options.filter_policy = Create(10, bfp_impl_);
|
||||
table_options.optimize_filters_for_memory = false;
|
||||
table_options.partition_filters = partition_filters_;
|
||||
if (partition_filters_) {
|
||||
table_options.index_type =
|
||||
@@ -1696,7 +1694,6 @@ TEST_F(DBBloomFilterTest, ContextCustomFilterPolicy) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy = policy;
|
||||
table_options.format_version = 5;
|
||||
table_options.optimize_filters_for_memory = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
@@ -2053,7 +2050,7 @@ class DBBloomFilterTestVaryPrefixAndFormatVer
|
||||
DBBloomFilterTestVaryPrefixAndFormatVer()
|
||||
: DBTestBase("db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
|
||||
~DBBloomFilterTestVaryPrefixAndFormatVer() override = default;
|
||||
~DBBloomFilterTestVaryPrefixAndFormatVer() override {}
|
||||
|
||||
void SetUp() override {
|
||||
use_prefix_ = std::get<0>(GetParam());
|
||||
@@ -2128,9 +2125,8 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
values[i] = PinnableSlice();
|
||||
}
|
||||
|
||||
db_->MultiGet(ropts, Q, column_families.data(), key_slices.data(),
|
||||
values.data(),
|
||||
/*timestamps=*/nullptr, statuses.data(), true);
|
||||
db_->MultiGet(ropts, Q, &column_families[0], &key_slices[0], &values[0],
|
||||
/*timestamps=*/nullptr, &statuses[0], true);
|
||||
|
||||
// Confirm correct status results
|
||||
uint32_t number_not_found = 0;
|
||||
@@ -2180,9 +2176,8 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
values[i] = PinnableSlice();
|
||||
}
|
||||
|
||||
db_->MultiGet(ropts, Q, column_families.data(), key_slices.data(),
|
||||
values.data(),
|
||||
/*timestamps=*/nullptr, statuses.data(), true);
|
||||
db_->MultiGet(ropts, Q, &column_families[0], &key_slices[0], &values[0],
|
||||
/*timestamps=*/nullptr, &statuses[0], true);
|
||||
|
||||
// Confirm correct status results
|
||||
uint32_t number_not_found = 0;
|
||||
@@ -3567,277 +3562,6 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter3) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, SstQueryFilter) {
|
||||
using experimental::KeySegmentsExtractor;
|
||||
using experimental::MakeSharedBytewiseMinMaxSQFC;
|
||||
using experimental::SelectKeySegment;
|
||||
using experimental::SstQueryFilterConfigs;
|
||||
using experimental::SstQueryFilterConfigsManager;
|
||||
using KeyCategorySet = KeySegmentsExtractor::KeyCategorySet;
|
||||
|
||||
struct MySegmentExtractor : public KeySegmentsExtractor {
|
||||
char min_first_char;
|
||||
char max_first_char;
|
||||
char delim_char;
|
||||
MySegmentExtractor(char _min_first_char, char _max_first_char,
|
||||
char _delim_char)
|
||||
: min_first_char(_min_first_char),
|
||||
max_first_char(_max_first_char),
|
||||
delim_char(_delim_char) {}
|
||||
|
||||
const char* Name() const override { return "MySegmentExtractor"; }
|
||||
|
||||
std::string GetId() const override {
|
||||
return std::string("MySegmentExtractor+") + min_first_char +
|
||||
max_first_char + delim_char;
|
||||
}
|
||||
|
||||
void Extract(const Slice& key_or_bound, KeyKind /*kind*/,
|
||||
Result* result) const override {
|
||||
size_t len = key_or_bound.size();
|
||||
if (len == 0) {
|
||||
result->category = KeySegmentsExtractor::kReservedLowCategory;
|
||||
} else if (static_cast<unsigned char>(key_or_bound[0]) <
|
||||
static_cast<unsigned char>(min_first_char)) {
|
||||
result->category = KeySegmentsExtractor::kReservedLowCategory;
|
||||
} else if (static_cast<unsigned char>(key_or_bound[0]) >
|
||||
static_cast<unsigned char>(max_first_char)) {
|
||||
result->category = KeySegmentsExtractor::kReservedHighCategory;
|
||||
}
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
if (key_or_bound[i] == delim_char || i + 1 == key_or_bound.size()) {
|
||||
result->segment_ends.push_back(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Use '_' as delimiter, but different spans for default category
|
||||
auto extractor_to_c = std::make_shared<MySegmentExtractor>('a', 'c', '_');
|
||||
auto extractor_to_z = std::make_shared<MySegmentExtractor>('a', 'z', '_');
|
||||
auto extractor_alt = std::make_shared<MySegmentExtractor>('0', '9', '_');
|
||||
|
||||
// Filter on 2nd field, only for default category
|
||||
auto filter1_def = MakeSharedBytewiseMinMaxSQFC(
|
||||
experimental::SelectKeySegment(1),
|
||||
KeyCategorySet{KeySegmentsExtractor::kDefaultCategory});
|
||||
|
||||
// Also filter on 3rd field regardless of category
|
||||
auto filter2_all =
|
||||
MakeSharedBytewiseMinMaxSQFC(experimental::SelectKeySegment(2));
|
||||
|
||||
SstQueryFilterConfigs configs1 = {{filter1_def, filter2_all}, extractor_to_c};
|
||||
SstQueryFilterConfigs configs2 = {{filter1_def, filter2_all}, extractor_to_z};
|
||||
SstQueryFilterConfigs configs3 = {{filter2_all}, extractor_alt};
|
||||
|
||||
SstQueryFilterConfigsManager::Data data = {
|
||||
{42, {{"foo", configs1}}}, {43, {{"foo", configs2}, {"bar", configs3}}}};
|
||||
|
||||
std::shared_ptr<SstQueryFilterConfigsManager> configs_manager;
|
||||
ASSERT_OK(SstQueryFilterConfigsManager::MakeShared(data, &configs_manager));
|
||||
|
||||
// Test manager behaviors
|
||||
auto MakeFactory = [configs_manager](
|
||||
const std::string& configs_name,
|
||||
SstQueryFilterConfigsManager::FilteringVersion ver)
|
||||
-> std::shared_ptr<SstQueryFilterConfigsManager::Factory> {
|
||||
std::shared_ptr<SstQueryFilterConfigsManager::Factory> factory;
|
||||
Status s = configs_manager->MakeSharedFactory(configs_name, ver, &factory);
|
||||
assert(s.ok());
|
||||
return factory;
|
||||
};
|
||||
std::shared_ptr<SstQueryFilterConfigsManager::Factory> factory;
|
||||
|
||||
// Version 0 is always OK, returning empty/not found configs
|
||||
ASSERT_TRUE(MakeFactory("blah", 0)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_TRUE(MakeFactory("foo", 0)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_TRUE(MakeFactory("bar", 0)->GetConfigs().IsEmptyNotFound());
|
||||
|
||||
// We can't be sure about the proper configuration for versions outside the
|
||||
// known range (and reserved version 0).
|
||||
ASSERT_TRUE(configs_manager->MakeSharedFactory("foo", 1, &factory)
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(configs_manager->MakeSharedFactory("foo", 41, &factory)
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(configs_manager->MakeSharedFactory("foo", 44, &factory)
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(configs_manager->MakeSharedFactory("foo", 500, &factory)
|
||||
.IsInvalidArgument());
|
||||
|
||||
ASSERT_TRUE(MakeFactory("blah", 42)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_TRUE(MakeFactory("blah", 43)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_FALSE(MakeFactory("foo", 42)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_FALSE(MakeFactory("foo", 43)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_TRUE(MakeFactory("bar", 42)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_FALSE(MakeFactory("bar", 43)->GetConfigs().IsEmptyNotFound());
|
||||
|
||||
ASSERT_OK(configs_manager->MakeSharedFactory("foo", 42, &factory));
|
||||
ASSERT_EQ(factory->GetConfigsName(), "foo");
|
||||
ASSERT_EQ(factory->GetConfigs().IsEmptyNotFound(), false);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.table_properties_collector_factories.push_back(factory);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// For lower level file
|
||||
ASSERT_OK(Put(" ", "val0"));
|
||||
ASSERT_OK(Put(" _345_678", "val0"));
|
||||
ASSERT_OK(Put("aaa", "val0"));
|
||||
ASSERT_OK(Put("abc_123", "val1"));
|
||||
ASSERT_OK(Put("abc_13", "val2"));
|
||||
ASSERT_OK(Put("abc_156_987", "val3"));
|
||||
ASSERT_OK(Put("bcd_1722", "val4"));
|
||||
ASSERT_OK(Put("xyz_145", "val5"));
|
||||
ASSERT_OK(Put("xyz_167", "val6"));
|
||||
ASSERT_OK(Put("xyz_178", "val7"));
|
||||
ASSERT_OK(Put("zzz", "val0"));
|
||||
ASSERT_OK(Put("~~~", "val0"));
|
||||
ASSERT_OK(Put("~~~_456_789", "val0"));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
ASSERT_EQ(factory->GetFilteringVersion(), 42U);
|
||||
ASSERT_NOK(factory->SetFilteringVersion(41));
|
||||
ASSERT_NOK(factory->SetFilteringVersion(44));
|
||||
ASSERT_EQ(factory->GetFilteringVersion(), 42U);
|
||||
ASSERT_OK(factory->SetFilteringVersion(43));
|
||||
ASSERT_EQ(factory->GetFilteringVersion(), 43U);
|
||||
|
||||
// For higher level file
|
||||
ASSERT_OK(Put(" ", "val0"));
|
||||
ASSERT_OK(Put(" _345_680", "val0"));
|
||||
ASSERT_OK(Put("aaa", "val9"));
|
||||
ASSERT_OK(Put("abc_234", "val1"));
|
||||
ASSERT_OK(Put("abc_245_567", "val2"));
|
||||
ASSERT_OK(Put("abc_25", "val3"));
|
||||
ASSERT_OK(Put("xyz_180", "val4"));
|
||||
ASSERT_OK(Put("xyz_191", "val4"));
|
||||
ASSERT_OK(Put("xyz_260", "val4"));
|
||||
ASSERT_OK(Put("zzz", "val9"));
|
||||
ASSERT_OK(Put("~~~", "val0"));
|
||||
ASSERT_OK(Put("~~~_456_790", "val0"));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
using Keys = std::vector<std::string>;
|
||||
auto RangeQueryKeys =
|
||||
[factory, db = db_](
|
||||
std::string lb, std::string ub,
|
||||
std::shared_ptr<SstQueryFilterConfigsManager::Factory> alt_factory =
|
||||
nullptr) {
|
||||
Slice lb_slice = lb;
|
||||
Slice ub_slice = ub;
|
||||
|
||||
ReadOptions ro;
|
||||
ro.iterate_lower_bound = &lb_slice;
|
||||
ro.iterate_upper_bound = &ub_slice;
|
||||
ro.table_filter = (alt_factory ? alt_factory : factory)
|
||||
->GetTableFilterForRangeQuery(lb_slice, ub_slice);
|
||||
auto it = db->NewIterator(ro);
|
||||
Keys ret;
|
||||
for (it->Seek(lb_slice); it->Valid(); it->Next()) {
|
||||
ret.push_back(it->key().ToString());
|
||||
}
|
||||
EXPECT_OK(it->status());
|
||||
delete it;
|
||||
return ret;
|
||||
};
|
||||
|
||||
// Control 1: range is not filtered but min/max filter is checked
|
||||
// because of common prefix leading up to 2nd segment
|
||||
// TODO/future: statistics for when filter is checked vs. not applicable
|
||||
EXPECT_EQ(RangeQueryKeys("abc_150", "abc_249"),
|
||||
Keys({"abc_156_987", "abc_234", "abc_245_567"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// Test 1: range is filtered to just lowest level, fully containing the
|
||||
// segments in that category
|
||||
EXPECT_EQ(RangeQueryKeys("abc_100", "abc_179"),
|
||||
Keys({"abc_123", "abc_13", "abc_156_987"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 2: range is filtered to just lowest level, partial overlap
|
||||
EXPECT_EQ(RangeQueryKeys("abc_1500_x_y", "abc_16QQ"), Keys({"abc_156_987"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 3: range is filtered to just highest level, fully containing the
|
||||
// segments in that category but would be overlapping the range for the other
|
||||
// file if the filter included all categories
|
||||
EXPECT_EQ(RangeQueryKeys("abc_200", "abc_300"),
|
||||
Keys({"abc_234", "abc_245_567", "abc_25"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 4: range is filtered to just highest level, partial overlap (etc.)
|
||||
EXPECT_EQ(RangeQueryKeys("abc_200", "abc_249"),
|
||||
Keys({"abc_234", "abc_245_567"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 5: range is filtered from both levels, because of category scope
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Control 2: range is not filtered because association between 1st and
|
||||
// 2nd segment is not represented
|
||||
EXPECT_EQ(RangeQueryKeys("abc_170", "abc_190"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// Control 3: range is not filtered because there's no (bloom) filter on
|
||||
// 1st segment (like prefix filtering)
|
||||
EXPECT_EQ(RangeQueryKeys("baa_170", "baa_190"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// Control 4: range is not filtered because difference in segments leading
|
||||
// up to 2nd segment
|
||||
EXPECT_EQ(RangeQueryKeys("abc_500", "abd_501"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// TODO: exclusive upper bound tests
|
||||
|
||||
// ======= Testing 3rd segment (cross-category filter) =======
|
||||
// Control 5: not filtered because of segment range overlap
|
||||
EXPECT_EQ(RangeQueryKeys(" z__700", " z__750"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// Test 6: filtered on both levels
|
||||
EXPECT_EQ(RangeQueryKeys(" z__100", " z__300"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Control 6: finding something, with 2nd segment filter helping
|
||||
EXPECT_EQ(RangeQueryKeys("abc_156_9", "abc_156_99"), Keys({"abc_156_987"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
EXPECT_EQ(RangeQueryKeys("abc_245_56", "abc_245_57"), Keys({"abc_245_567"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 6: filtered on both levels, for different segments
|
||||
EXPECT_EQ(RangeQueryKeys("abc_245_900", "abc_245_999"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// ======= Testing extractor read portability =======
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Only modifies how filters are written
|
||||
ASSERT_OK(factory->SetFilteringVersion(0));
|
||||
ASSERT_EQ(factory->GetFilteringVersion(), 0U);
|
||||
ASSERT_EQ(factory->GetConfigs().IsEmptyNotFound(), true);
|
||||
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Even a different config name with different extractor can read
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400", MakeFactory("bar", 43)),
|
||||
Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Or a "not found" config name
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400", MakeFactory("blah", 43)),
|
||||
Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ class ConditionalFilter : public CompactionFilter {
|
||||
|
||||
class ChangeFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ChangeFilter() = default;
|
||||
explicit ChangeFilter() {}
|
||||
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* new_value, bool* value_changed) const override {
|
||||
@@ -289,7 +289,7 @@ class ConditionalFilterFactory : public CompactionFilterFactory {
|
||||
|
||||
class ChangeFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit ChangeFilterFactory() = default;
|
||||
explicit ChangeFilterFactory() {}
|
||||
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
@@ -342,7 +342,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaPtr<InternalIterator> iter(dbfull()->NewInternalIterator(
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
read_options, &arena, kMaxSequenceNumber, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -434,7 +434,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaPtr<InternalIterator> iter(dbfull()->NewInternalIterator(
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
read_options, &arena, kMaxSequenceNumber, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -717,8 +717,8 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
Arena arena;
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaPtr<InternalIterator> iter(dbfull()->NewInternalIterator(
|
||||
read_options, &arena, kMaxSequenceNumber));
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
|
||||
+123
-410
@@ -16,7 +16,6 @@
|
||||
#include "env/mock_env.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/concurrent_task_limiter.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
@@ -42,7 +41,7 @@ class CompactionStatsCollector : public EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
~CompactionStatsCollector() override = default;
|
||||
~CompactionStatsCollector() override {}
|
||||
|
||||
void OnCompactionCompleted(DB* /* db */,
|
||||
const CompactionJobInfo& info) override {
|
||||
@@ -242,8 +241,8 @@ class RoundRobinSubcompactionsAgainstResources
|
||||
namespace {
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() = default;
|
||||
~FlushedFileCollector() override = default;
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
@@ -253,7 +252,7 @@ class FlushedFileCollector : public EventListener {
|
||||
std::vector<std::string> GetFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> result;
|
||||
for (const auto& fname : flushed_files_) {
|
||||
for (auto fname : flushed_files_) {
|
||||
result.push_back(fname);
|
||||
}
|
||||
return result;
|
||||
@@ -506,7 +505,7 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::FindTable:0", [&](void* arg) {
|
||||
assert(arg != nullptr);
|
||||
bool no_io = *(static_cast<bool*>(arg));
|
||||
bool no_io = *(reinterpret_cast<bool*>(arg));
|
||||
if (!no_io) {
|
||||
// filter out cases for table properties queries.
|
||||
num_table_cache_lookup++;
|
||||
@@ -682,7 +681,7 @@ TEST_F(DBCompactionTest, CompactRangeBottomPri) {
|
||||
int bottom_pri_count = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"ThreadPoolImpl::Impl::BGThread:BeforeRun", [&](void* arg) {
|
||||
Env::Priority* pri = static_cast<Env::Priority*>(arg);
|
||||
Env::Priority* pri = reinterpret_cast<Env::Priority*>(arg);
|
||||
// First time is low pri pool in the test case.
|
||||
if (low_pri_count == 0 && bottom_pri_count == 0) {
|
||||
ASSERT_EQ(Env::Priority::LOW, *pri);
|
||||
@@ -1871,60 +1870,13 @@ TEST_F(DBCompactionTest, ManualCompactionWithUnorderedWrite) {
|
||||
ASSERT_EQ(Get("foo"), "v2");
|
||||
}
|
||||
|
||||
// Test params:
|
||||
// 1) whether to enable user-defined timestamps.
|
||||
class DBDeleteFileRangeTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBDeleteFileRangeTest()
|
||||
: DBTestBase("db_delete_file_range_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
void SetUp() override { enable_udt_ = GetParam(); }
|
||||
|
||||
protected:
|
||||
void PutKeyValue(const Slice& key, const Slice& value) {
|
||||
if (enable_udt_) {
|
||||
EXPECT_OK(db_->Put(WriteOptions(), key, min_ts_, value));
|
||||
} else {
|
||||
EXPECT_OK(Put(key, value));
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetValue(const std::string& key) {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
if (enable_udt_) {
|
||||
roptions.timestamp = &min_ts_;
|
||||
}
|
||||
Status s = db_->Get(roptions, key, &result);
|
||||
EXPECT_TRUE(s.ok());
|
||||
return result;
|
||||
}
|
||||
|
||||
Status MaybeGetValue(const std::string& key, std::string* result) {
|
||||
ReadOptions roptions;
|
||||
if (enable_udt_) {
|
||||
roptions.timestamp = &min_ts_;
|
||||
}
|
||||
Status s = db_->Get(roptions, key, result);
|
||||
EXPECT_TRUE(s.IsNotFound() || s.ok());
|
||||
return s;
|
||||
}
|
||||
|
||||
bool enable_udt_ = false;
|
||||
Slice min_ts_ = MinU64Ts();
|
||||
};
|
||||
|
||||
TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 4;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
options.max_background_compactions = 3;
|
||||
if (enable_udt_) {
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
@@ -1936,14 +1888,14 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
// file 1 [0 => 100]
|
||||
for (int32_t i = 0; i < 100; i++) {
|
||||
values[i] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// file 2 [100 => 300]
|
||||
for (int32_t i = 100; i < 300; i++) {
|
||||
values[i] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -1959,7 +1911,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
// file 3 [ 0 => 200]
|
||||
for (int32_t i = 0; i < 200; i++) {
|
||||
values[i] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -1971,7 +1923,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
}
|
||||
values[j] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(j), values[j]);
|
||||
ASSERT_OK(Put(Key(j), values[j]));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
@@ -1996,10 +1948,11 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
int32_t deleted_count = 0;
|
||||
for (int32_t i = 0; i < 4300; i++) {
|
||||
if (i < 1000 || i > 2000) {
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
} else {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
Status s = MaybeGetValue(Key(i), &result);
|
||||
Status s = db_->Get(roptions, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound() || s.ok());
|
||||
if (s.IsNotFound()) {
|
||||
deleted_count++;
|
||||
@@ -2029,7 +1982,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
for (int32_t i = 0; i < 4300; i++) {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
ASSERT_TRUE(MaybeGetValue(Key(i), &result).IsNotFound());
|
||||
ASSERT_TRUE(db_->Get(roptions, Key(i), &result).IsNotFound());
|
||||
deleted_count2++;
|
||||
}
|
||||
ASSERT_GT(deleted_count2, deleted_count);
|
||||
@@ -2037,16 +1990,13 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
ASSERT_GT(old_num_files, new_num_files);
|
||||
}
|
||||
|
||||
TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 4;
|
||||
options.max_background_compactions = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
if (enable_udt_) {
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
@@ -2059,7 +2009,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
values[k] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(k), values[k]);
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -2074,7 +2024,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
for (auto i = 0; i < 10; i += 2) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
PutKeyValue(Key(k), values[k]);
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -2091,21 +2041,22 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
Slice begin2(begin_str2), end2(end_str2);
|
||||
Slice begin3(begin_str3), end3(end_str3);
|
||||
std::vector<RangePtr> ranges;
|
||||
ranges.emplace_back(&begin1, &end1);
|
||||
ranges.emplace_back(&begin2, &end2);
|
||||
ranges.emplace_back(&begin3, &end3);
|
||||
ranges.push_back(RangePtr(&begin1, &end1));
|
||||
ranges.push_back(RangePtr(&begin2, &end2));
|
||||
ranges.push_back(RangePtr(&begin3, &end3));
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(),
|
||||
ranges.data(), ranges.size()));
|
||||
ASSERT_EQ("0,3,7", FilesPerLevel(0));
|
||||
|
||||
// Keys [0, 300) should not exist.
|
||||
for (auto i = 0; i < 300; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 300; i < 1000; i++) {
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2118,24 +2069,25 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
Slice begin2(begin_str2), end2(end_str2);
|
||||
Slice begin3(begin_str3), end3(end_str3);
|
||||
std::vector<RangePtr> ranges;
|
||||
ranges.emplace_back(&begin1, &end1);
|
||||
ranges.emplace_back(&begin2, &end2);
|
||||
ranges.emplace_back(&begin3, &end3);
|
||||
ranges.push_back(RangePtr(&begin1, &end1));
|
||||
ranges.push_back(RangePtr(&begin2, &end2));
|
||||
ranges.push_back(RangePtr(&begin3, &end3));
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(),
|
||||
ranges.data(), ranges.size(), false));
|
||||
ASSERT_EQ("0,1,4", FilesPerLevel(0));
|
||||
|
||||
// Keys [600, 900) should not exist.
|
||||
for (auto i = 600; i < 900; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 300; i < 600; i++) {
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
for (auto i = 900; i < 1000; i++) {
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2146,14 +2098,15 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
for (auto i = 0; i < 1000; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
TEST_F(DBCompactionTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
// regression test for #2833: groups of files whose user-keys overlap at the
|
||||
// endpoints could be split by `DeleteFilesInRange`. This caused old data to
|
||||
// reappear, either because a new version of the key was removed, or a range
|
||||
@@ -2165,9 +2118,6 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.target_file_size_base = 1 << 10; // 1KB
|
||||
if (enable_udt_) {
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
}
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// The snapshot prevents key 1 from having its old version dropped. The low
|
||||
@@ -2191,8 +2141,8 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
std::string vals[kNumL0Files];
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
vals[i] = rnd.RandomString(kValSize);
|
||||
PutKeyValue(Key(i), vals[i]);
|
||||
PutKeyValue(Key(i + 1), vals[i]);
|
||||
ASSERT_OK(Put(Key(i), vals[i]));
|
||||
ASSERT_OK(Put(Key(i + 1), vals[i]));
|
||||
ASSERT_OK(Flush());
|
||||
if (i == 0) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
@@ -2205,14 +2155,11 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
std::string begin_str = Key(0), end_str = Key(1);
|
||||
Slice begin = begin_str, end = end_str;
|
||||
ASSERT_OK(DeleteFilesInRange(db_, db_->DefaultColumnFamily(), &begin, &end));
|
||||
ASSERT_EQ(vals[1], GetValue(Key(1)));
|
||||
ASSERT_EQ(vals[1], Get(Key(1)));
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBDeleteFileRangeTest, DBDeleteFileRangeTest,
|
||||
::testing::Bool());
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, TrivialMoveToLastLevelWithFiles) {
|
||||
int32_t trivial_move = 0;
|
||||
int32_t non_trivial_move = 0;
|
||||
@@ -3998,7 +3945,7 @@ TEST_P(DBCompactionTestWithParam, FullCompactionInBottomPriThreadPool) {
|
||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CancelCompactionWaitingOnRunningConflict) {
|
||||
TEST_F(DBCompactionTest, CancelCompactionWaitingOnConflict) {
|
||||
// This test verifies cancellation of a compaction waiting to be scheduled due
|
||||
// to conflict with a running compaction.
|
||||
//
|
||||
@@ -4037,7 +3984,7 @@ TEST_F(DBCompactionTest, CancelCompactionWaitingOnRunningConflict) {
|
||||
// Make sure the manual compaction has seen the conflict before being canceled
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyData::CompactRange:Return",
|
||||
"DBCompactionTest::CancelCompactionWaitingOnRunningConflict:"
|
||||
"DBCompactionTest::CancelCompactionWaitingOnConflict:"
|
||||
"PreDisableManualCompaction"}});
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
@@ -4048,73 +3995,12 @@ TEST_F(DBCompactionTest, CancelCompactionWaitingOnRunningConflict) {
|
||||
// despite finding a conflict with an automatic compaction that is still
|
||||
// running
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::CancelCompactionWaitingOnRunningConflict:"
|
||||
"DBCompactionTest::CancelCompactionWaitingOnConflict:"
|
||||
"PreDisableManualCompaction");
|
||||
db_->DisableManualCompaction();
|
||||
manual_compaction_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CancelCompactionWaitingOnScheduledConflict) {
|
||||
// This test verifies cancellation of a compaction waiting to be scheduled due
|
||||
// to conflict with a scheduled (but not running) compaction.
|
||||
//
|
||||
// A `CompactRange()` in universal compacts all files, waiting for files to
|
||||
// become available if they are locked for another compaction. This test
|
||||
// blocks the compaction thread pool and then calls `CompactRange()` twice.
|
||||
// The first call to `CompactRange()` schedules a compaction that is queued
|
||||
// in the thread pool. The second call to `CompactRange()` blocks on the first
|
||||
// call due to the conflict in file picking. The test verifies that
|
||||
// `DisableManualCompaction()` can cancel both while the thread pool remains
|
||||
// blocked.
|
||||
const int kNumSortedRuns = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.disable_auto_compactions = true;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
|
||||
Reopen(options);
|
||||
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
|
||||
// Fill overlapping files in L0
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumSortedRuns; ++i) {
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx, false /* nowait */);
|
||||
}
|
||||
|
||||
std::atomic<int> num_compact_range_calls{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ColumnFamilyData::CompactRange:Return",
|
||||
[&](void* /* arg */) { num_compact_range_calls++; });
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
const int kNumManualCompactions = 2;
|
||||
port::Thread manual_compaction_threads[kNumManualCompactions];
|
||||
for (int i = 0; i < kNumManualCompactions; i++) {
|
||||
manual_compaction_threads[i] = port::Thread([this]() {
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
.IsIncomplete());
|
||||
});
|
||||
}
|
||||
while (num_compact_range_calls < kNumManualCompactions) {
|
||||
}
|
||||
|
||||
// Cancel it. Threads should be joinable, i.e., both the scheduled and blocked
|
||||
// manual compactions were canceled despite no compaction could have ever run.
|
||||
db_->DisableManualCompaction();
|
||||
for (int i = 0; i < kNumManualCompactions; i++) {
|
||||
manual_compaction_threads[i].join();
|
||||
}
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, OptimizedDeletionObsoleting) {
|
||||
// Deletions can be dropped when compacted to non-last level if they fall
|
||||
// outside the lower-level files' key-ranges.
|
||||
@@ -4306,7 +4192,7 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
ASSERT_NE(kMaxSequenceNumber, dbfull()->bottommost_files_mark_threshold_);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kBottommostFiles);
|
||||
});
|
||||
@@ -4362,7 +4248,7 @@ TEST_F(DBCompactionTest, DelayCompactBottomLevelFilesWithDeletions) {
|
||||
std::atomic_int compaction_count = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kBottommostFiles);
|
||||
compaction_count++;
|
||||
@@ -4493,7 +4379,7 @@ TEST_F(DBCompactionTest, RoundRobinTtlCompactionNormal) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -4643,7 +4529,7 @@ TEST_F(DBCompactionTest, RoundRobinTtlCompactionUnsortedTime) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -4759,7 +4645,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -4807,7 +4693,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -4834,6 +4720,7 @@ TEST_F(DBCompactionTest, LevelTtlCompactionOutputCuttingIteractingWithOther) {
|
||||
options.env = env_;
|
||||
options.target_file_size_base = 4 << 10;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level_compaction_dynamic_file_size = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
@@ -4934,7 +4821,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
int ttl_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -5082,7 +4969,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
int periodic_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5266,7 +5153,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
bool set_creation_time_to_zero = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5274,7 +5161,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* arg) {
|
||||
TableProperties* props = static_cast<TableProperties*>(arg);
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
if (set_file_creation_time_to_zero) {
|
||||
props->file_creation_time = 0;
|
||||
}
|
||||
@@ -5338,7 +5225,7 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
int ttl_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5521,7 +5408,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
|
||||
int periodic_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -6144,14 +6031,6 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<std::string> pending_compaction_cfs;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SchedulePendingCompaction::cfd", [&](void* arg) {
|
||||
const std::string& cf_name =
|
||||
static_cast<ColumnFamilyData*>(arg)->GetName();
|
||||
pending_compaction_cfs.emplace_back(cf_name);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Block all compact threads in thread pool.
|
||||
@@ -6205,15 +6084,8 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
NumTableFilesAtLevel(0, 0));
|
||||
}
|
||||
|
||||
// Wait until all CFs are pending compaction. WaitForFlushMemtable() can
|
||||
// return before the next compaction is scheduled, so we need to do some
|
||||
// waiting here.
|
||||
unsigned int tp_len = env_->GetThreadPoolQueueLen(Env::LOW);
|
||||
for (int i = 0; i < 10000 && tp_len < cf_count; i++) {
|
||||
env_->SleepForMicroseconds(1000);
|
||||
tp_len = env_->GetThreadPoolQueueLen(Env::LOW);
|
||||
}
|
||||
ASSERT_EQ(cf_count, tp_len);
|
||||
// All CFs are pending compaction
|
||||
ASSERT_EQ(cf_count, env_->GetThreadPoolQueueLen(Env::LOW));
|
||||
|
||||
// Unblock all compaction threads
|
||||
for (size_t i = 0; i < kTotalCompactTasks; i++) {
|
||||
@@ -6718,7 +6590,7 @@ TEST_F(DBCompactionTest, RoundRobinCutOutputAtCompactCursor) {
|
||||
|
||||
class NoopMergeOperator : public MergeOperator {
|
||||
public:
|
||||
NoopMergeOperator() = default;
|
||||
NoopMergeOperator() {}
|
||||
|
||||
bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
@@ -7121,8 +6993,7 @@ class DBCompactionTestWithOngoingFileIngestionParam
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"ExternalSstFileIngestionJob::Run", [&](void*) {
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactFilesImpl::"
|
||||
"PostSanitizeAndConvertCompactionInputFiles",
|
||||
{{"DBImpl::CompactFilesImpl::PostSanitizeCompactionInputFiles",
|
||||
"VersionSet::LogAndApply:WriteManifest"}});
|
||||
});
|
||||
} else {
|
||||
@@ -7275,7 +7146,8 @@ TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency0", [&](void* arg) {
|
||||
auto p = static_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
@@ -7312,7 +7184,8 @@ TEST_F(DBCompactionTest, ConsistencyFailTest2) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency1", [&](void* arg) {
|
||||
auto p = static_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
@@ -7604,72 +7477,6 @@ class DBCompactionTestL0FilesMisorderCorruption : public DBCompactionTest {
|
||||
std::shared_ptr<test::SleepingBackgroundTask> sleeping_task_;
|
||||
};
|
||||
|
||||
TEST_F(DBCompactionTest, CompactFilesSupportKeyPlacementRangeConflict) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 3;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// To create LSM of below shape:
|
||||
// L0: [k2]
|
||||
// L1: [k3],[k4]
|
||||
// L2: [k1, k5]
|
||||
ASSERT_OK(Put("k1", "v"));
|
||||
ASSERT_OK(Put("k5", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel());
|
||||
|
||||
ASSERT_OK(Put("k3", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k4", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(experimental::PromoteL0(db_, db_->DefaultColumnFamily(), 1));
|
||||
ASSERT_EQ("0,2,1", FilesPerLevel());
|
||||
|
||||
ASSERT_OK(Put("k2", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("1,2,1", FilesPerLevel());
|
||||
|
||||
Close();
|
||||
|
||||
// To force below two CompactFiles() in order to coerce range conflict on L1
|
||||
// upon (2)
|
||||
// (1): Compact [k2] at L0 and [k3] at L1 with output to L1
|
||||
// (2): Compact [k4] at L1 and [k1, k5] at L2 and output to L1 and L2
|
||||
options.preclude_last_level_data_seconds = 1;
|
||||
Reopen(options);
|
||||
|
||||
ColumnFamilyMetaData cf_meta_data;
|
||||
db_->GetColumnFamilyMetaData(&cf_meta_data);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactFilesImpl:0", [&](void* /*arg*/) {
|
||||
std::vector<std::string> c2_input_files;
|
||||
c2_input_files.push_back(cf_meta_data.levels[1].files[1].name);
|
||||
c2_input_files.push_back(cf_meta_data.levels[2].files[0].name);
|
||||
// To verify CompactFiles() is aborted upon range conflict instead
|
||||
// of crashing upon internal assertion
|
||||
Status s = db_->CompactFiles(CompactionOptions(), c2_input_files,
|
||||
2 /* output_level */);
|
||||
ASSERT_TRUE(s.IsAborted());
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> c1_input_files;
|
||||
c1_input_files.push_back(cf_meta_data.levels[0].files[0].name);
|
||||
c1_input_files.push_back(cf_meta_data.levels[1].files[0].name);
|
||||
Status s = db_->CompactFiles(CompactionOptions(), c1_input_files,
|
||||
1 /* output_level */);
|
||||
ASSERT_OK(s);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTestL0FilesMisorderCorruption,
|
||||
FlushAfterIntraL0LevelCompactionWithIngestedFile) {
|
||||
SetupOptions(CompactionStyle::kCompactionStyleLevel, "");
|
||||
@@ -8191,7 +7998,7 @@ TEST_F(DBCompactionTest, UpdateLevelSubCompactionTest) {
|
||||
bool has_compaction = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 10);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8215,7 +8022,7 @@ TEST_F(DBCompactionTest, UpdateLevelSubCompactionTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 2);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8243,7 +8050,7 @@ TEST_F(DBCompactionTest, UpdateUniversalSubCompactionTest) {
|
||||
bool has_compaction = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 10);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8266,7 +8073,7 @@ TEST_F(DBCompactionTest, UpdateUniversalSubCompactionTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 2);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -9308,104 +9115,66 @@ TEST_F(DBCompactionTest, CompactionWithChecksumHandoffManifest2) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, FIFOChangeTemperature) {
|
||||
for (bool write_time_default : {false, true}) {
|
||||
SCOPED_TRACE("write time default? " + std::to_string(write_time_default));
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.file_temperature_age_thresholds = {{Temperature::kCold, 1000}};
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
Reopen(options);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.file_temperature_age_thresholds = {{Temperature::kCold, 1000}};
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
if (write_time_default) {
|
||||
options.default_write_temperature = Temperature::kWarm;
|
||||
}
|
||||
// Should be ignored (TODO: fail?)
|
||||
options.last_level_temperature = Temperature::kHot;
|
||||
Reopen(options);
|
||||
int total_cold = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
int total_cold = 0;
|
||||
int total_warm = 0;
|
||||
int total_hot = 0;
|
||||
int total_unknown = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
} else if (temperature == Temperature::kWarm) {
|
||||
total_warm++;
|
||||
} else if (temperature == Temperature::kHot) {
|
||||
total_hot++;
|
||||
} else {
|
||||
assert(temperature == Temperature::kUnknown);
|
||||
total_unknown++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
if (write_time_default) {
|
||||
// Also test dynamic option change
|
||||
ASSERT_OK(db_->SetOptions({{"default_write_temperature", "kHot"}}));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_cold);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
if (write_time_default) {
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[1].temperature);
|
||||
// Includes obsolete/deleted files moved to cold
|
||||
ASSERT_EQ(total_warm, 3);
|
||||
ASSERT_EQ(total_hot, 1);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 0);
|
||||
} else {
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(total_warm, 0);
|
||||
ASSERT_EQ(total_hot, 0);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 4);
|
||||
}
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_cold);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
@@ -9881,7 +9650,6 @@ TEST_F(DBCompactionTest, BottomPriCompactionCountsTowardConcurrencyLimit) {
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
sleeping_task_low.WaitUntilSleeping();
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
@@ -10022,7 +9790,7 @@ TEST_F(DBCompactionTest, TurnOnLevelCompactionDynamicLevelBytesUCToLC) {
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
std::string expected_lsm;
|
||||
std::string expected_lsm = "";
|
||||
for (int i = 0; i < 49; ++i) {
|
||||
expected_lsm += "0,";
|
||||
}
|
||||
@@ -10034,62 +9802,6 @@ TEST_F(DBCompactionTest, TurnOnLevelCompactionDynamicLevelBytesUCToLC) {
|
||||
ASSERT_EQ(expected_lsm, FilesPerLevel(1));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisallowRefitFilesFromNonL0ToL02) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
|
||||
options.num_levels = 3;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// To set up LSM shape:
|
||||
// L0
|
||||
// L1
|
||||
// L2:[a@1, k@3], [k@2, z@4] (sorted by ascending smallest key)
|
||||
// Both of these 2 files have epoch number = 1
|
||||
const Snapshot* s1 = db_->GetSnapshot();
|
||||
ASSERT_OK(Put("a", "@1"));
|
||||
ASSERT_OK(Put("k", "@2"));
|
||||
const Snapshot* s2 = db_->GetSnapshot();
|
||||
ASSERT_OK(Put("k", "@3"));
|
||||
ASSERT_OK(Put("z", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
// Cut file between k@3 and k@2
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionOutputs::ShouldStopBefore::manual_decision",
|
||||
[options](void* p) {
|
||||
auto* pair = (std::pair<bool*, const Slice>*)p;
|
||||
if ((options.comparator->Compare(ExtractUserKey(pair->second), "k") ==
|
||||
0) &&
|
||||
(GetInternalKeySeqno(pair->second) == 2)) {
|
||||
*(pair->first) = true;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
Status s = dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("0,0,2", FilesPerLevel());
|
||||
std::vector<LiveFileMetaData> files;
|
||||
dbfull()->GetLiveFilesMetaData(&files);
|
||||
ASSERT_EQ(files.size(), 2);
|
||||
ASSERT_EQ(files[0].smallestkey, "a");
|
||||
ASSERT_EQ(files[0].largestkey, "k");
|
||||
ASSERT_EQ(files[1].smallestkey, "k");
|
||||
ASSERT_EQ(files[1].largestkey, "z");
|
||||
|
||||
// Disallow moving 2 non-L0 files to L0
|
||||
CompactRangeOptions cro2;
|
||||
cro2.change_level = true;
|
||||
cro2.target_level = 0;
|
||||
s = dbfull()->CompactRange(cro2, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsAborted());
|
||||
|
||||
db_->ReleaseSnapshot(s1);
|
||||
db_->ReleaseSnapshot(s2);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DrainUnnecessaryLevelsAfterMultiplierChanged) {
|
||||
// When the level size multiplier increases such that fewer levels become
|
||||
// necessary, unnecessary levels should to be drained.
|
||||
@@ -10486,7 +10198,8 @@ TEST_F(DBCompactionTest, ErrorWhenReadFileHead) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::BeforeReturn",
|
||||
[&count, &error_file](void* pair_ptr) {
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
int cur = ++count;
|
||||
if (cur == error_file) {
|
||||
IOStatus* io_s = p->second;
|
||||
@@ -10594,20 +10307,20 @@ TEST_F(DBCompactionTest, ReleaseCompactionDuringManifestWrite) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.emplace_back([&]() {
|
||||
threads.emplace_back(std::thread([&]() {
|
||||
std::string k1_str = Key(1);
|
||||
std::string k2_str = Key(2);
|
||||
Slice k1 = k1_str;
|
||||
Slice k2 = k2_str;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &k1, &k2));
|
||||
});
|
||||
threads.emplace_back([&]() {
|
||||
}));
|
||||
threads.emplace_back(std::thread([&]() {
|
||||
std::string k10_str = Key(10);
|
||||
std::string k11_str = Key(11);
|
||||
Slice k10 = k10_str;
|
||||
Slice k11 = k11_str;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &k10, &k11));
|
||||
});
|
||||
}));
|
||||
std::string k100_str = Key(100);
|
||||
std::string k101_str = Key(101);
|
||||
Slice k100 = k100_str;
|
||||
|
||||
@@ -338,7 +338,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
|
||||
std::set<int> output_levels;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionPicker::CompactRange:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
output_levels.insert(compaction->output_level());
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
+26
-107
@@ -91,7 +91,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// Record tracked WALs as a (minimum) cross-check for directory scan
|
||||
std::vector<uint64_t> required_by_manifest;
|
||||
|
||||
@@ -121,7 +121,7 @@ Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
|
||||
|
||||
// DisableFileDeletions / EnableFileDeletions not supported in read-only DB
|
||||
if (deletions_disabled.ok()) {
|
||||
Status s2 = EnableFileDeletions();
|
||||
Status s2 = EnableFileDeletions(/*force=*/false);
|
||||
assert(s2.ok());
|
||||
s2.PermitUncheckedError();
|
||||
} else {
|
||||
@@ -151,33 +151,10 @@ Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
size_t wal_count = files.size();
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Number of WAL files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
|
||||
" required by manifest)",
|
||||
wal_count, required_by_manifest.size());
|
||||
#ifndef NDEBUG
|
||||
std::ostringstream wal_names;
|
||||
for (const auto& wal : files) {
|
||||
wal_names << wal->PathName() << " ";
|
||||
}
|
||||
|
||||
std::ostringstream wal_required_by_manifest_names;
|
||||
for (const auto& wal : required_by_manifest) {
|
||||
wal_required_by_manifest_names << wal << ".log ";
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Log files : %s .Log files required by manifest: %s.",
|
||||
wal_names.str().c_str(),
|
||||
wal_required_by_manifest_names.str().c_str());
|
||||
#endif // NDEBUG
|
||||
}
|
||||
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_);
|
||||
@@ -198,14 +175,13 @@ 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.
|
||||
// If the outstanding log files are small, we skip the flush.
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
|
||||
if (!s.ok()) {
|
||||
@@ -230,18 +206,12 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
// metadata.
|
||||
mutex_.Lock();
|
||||
if (flush_memtable) {
|
||||
bool wal_locked = lock_wal_count_ > 0;
|
||||
if (wal_locked) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Can't FlushForGetLiveFiles while WAL is locked");
|
||||
} else {
|
||||
Status status = FlushForGetLiveFiles();
|
||||
if (!status.ok()) {
|
||||
mutex_.Unlock();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Cannot Flush data %s\n", status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
Status status = FlushForGetLiveFiles();
|
||||
if (!status.ok()) {
|
||||
mutex_.Unlock();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "Cannot Flush data %s\n",
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,8 +287,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();
|
||||
|
||||
@@ -382,13 +350,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();
|
||||
}
|
||||
@@ -397,51 +362,25 @@ 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 = 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();
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Number of log files %" ROCKSDB_PRIszt, 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();
|
||||
@@ -450,29 +389,9 @@ 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();
|
||||
// Only last should need to be trimmed
|
||||
info.trim_to_size = (i + 1 == wal_size);
|
||||
if (opts.include_checksum_info) {
|
||||
info.file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
info.file_checksum = kUnknownFileChecksum;
|
||||
|
||||
+22
-26
@@ -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);
|
||||
@@ -272,7 +272,7 @@ TEST_F(DBFlushTest, ScheduleOnlyOneBgThread) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:AfterSchedule:0", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto unscheduled_flushes = *static_cast<int*>(arg);
|
||||
auto unscheduled_flushes = *reinterpret_cast<int*>(arg);
|
||||
ASSERT_EQ(0, unscheduled_flushes);
|
||||
++called;
|
||||
});
|
||||
@@ -1367,15 +1367,14 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
|
||||
ASSERT_OK(iter->status());
|
||||
key = (iter->key()).ToString(false);
|
||||
value = (iter->value()).ToString(false);
|
||||
if (key.compare(KEY3) == 0) {
|
||||
if (key.compare(KEY3) == 0)
|
||||
ASSERT_EQ(value, p_v3b);
|
||||
} else if (key.compare(KEY4) == 0) {
|
||||
else if (key.compare(KEY4) == 0)
|
||||
ASSERT_EQ(value, p_v4);
|
||||
} else if (key.compare(KEY5) == 0) {
|
||||
else if (key.compare(KEY5) == 0)
|
||||
ASSERT_EQ(value, p_v5);
|
||||
} else {
|
||||
else
|
||||
ASSERT_EQ(value, NOT_FOUND);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -1405,25 +1404,22 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
|
||||
ASSERT_OK(iter->status());
|
||||
key = (iter->key()).ToString(false);
|
||||
value = (iter->value()).ToString(false);
|
||||
if (key.compare(KEY2) == 0) {
|
||||
if (key.compare(KEY2) == 0)
|
||||
ASSERT_EQ(value, p_v2);
|
||||
} else if (key.compare(KEY3) == 0) {
|
||||
else if (key.compare(KEY3) == 0)
|
||||
ASSERT_EQ(value, p_v3b);
|
||||
} else if (key.compare(KEY4) == 0) {
|
||||
else if (key.compare(KEY4) == 0)
|
||||
ASSERT_EQ(value, p_v4);
|
||||
} else if (key.compare(KEY5) == 0) {
|
||||
else if (key.compare(KEY5) == 0)
|
||||
ASSERT_EQ(value, p_v5);
|
||||
} else {
|
||||
else
|
||||
ASSERT_EQ(value, NOT_FOUND);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
// Expected count here is 4: KEY2, KEY3, KEY4, KEY5.
|
||||
ASSERT_EQ(count, EXPECTED_COUNT_END);
|
||||
if (iter) {
|
||||
delete iter;
|
||||
}
|
||||
if (iter) delete iter;
|
||||
|
||||
Close();
|
||||
}
|
||||
@@ -1795,7 +1791,7 @@ TEST_F(DBFlushTest, MemPurgeCorrectLogNumberAndSSTFileCreation) {
|
||||
std::atomic<uint64_t> num_memtable_at_first_flush(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:num_memtables", [&](void* arg) {
|
||||
uint64_t* mems_size = static_cast<uint64_t*>(arg);
|
||||
uint64_t* mems_size = reinterpret_cast<uint64_t*>(arg);
|
||||
// atomic_compare_exchange_strong sometimes updates the value
|
||||
// of ZERO (the "expected" object), so we make sure ZERO is indeed...
|
||||
// zero.
|
||||
@@ -2042,7 +2038,7 @@ TEST_F(DBFlushTest, FireOnFlushCompletedAfterCommittedResult) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table", [&listener](void* arg) {
|
||||
// Wait for the second flush finished, out of mutex.
|
||||
auto* mems = static_cast<autovector<MemTable*>*>(arg);
|
||||
auto* mems = reinterpret_cast<autovector<MemTable*>*>(arg);
|
||||
if (mems->front()->GetEarliestSequenceNumber() == listener->seq1 - 1) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:"
|
||||
@@ -2381,7 +2377,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]);
|
||||
@@ -2390,7 +2386,7 @@ TEST_F(DBFlushTest, PickRightMemtables) {
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables", [&](void* arg) {
|
||||
auto* job = static_cast<FlushJob*>(arg);
|
||||
auto* job = reinterpret_cast<FlushJob*>(arg);
|
||||
assert(job);
|
||||
const auto& mems = job->GetMemTables();
|
||||
assert(mems.size() == 1);
|
||||
@@ -2503,7 +2499,7 @@ TEST_F(DBFlushTest, TombstoneVisibleInSnapshot) {
|
||||
class SimpleTestFlushListener : public EventListener {
|
||||
public:
|
||||
explicit SimpleTestFlushListener(DBFlushTest* _test) : test_(_test) {}
|
||||
~SimpleTestFlushListener() override = default;
|
||||
~SimpleTestFlushListener() override {}
|
||||
|
||||
void OnFlushBegin(DB* db, const FlushJobInfo& info) override {
|
||||
ASSERT_EQ(static_cast<uint32_t>(0), info.cf_id);
|
||||
@@ -2638,7 +2634,7 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
// it means atomic flush didn't write the min_log_number_to_keep to MANIFEST.
|
||||
cfs.push_back(kDefaultColumnFamilyName);
|
||||
ASSERT_OK(TryReopenWithColumnFamilies(cfs, options));
|
||||
DBImpl* db_impl = static_cast<DBImpl*>(db_);
|
||||
DBImpl* db_impl = reinterpret_cast<DBImpl*>(db_);
|
||||
ASSERT_TRUE(db_impl->allow_2pc());
|
||||
ASSERT_NE(db_impl->MinLogNumberToKeep(), 0);
|
||||
}
|
||||
@@ -3175,7 +3171,7 @@ TEST_P(DBAtomicFlushTest, BgThreadNoWaitAfterManifestError) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::ProcessManifestWrites:AfterSyncManifest", [&](void* arg) {
|
||||
auto* ptr = static_cast<IOStatus*>(arg);
|
||||
auto* ptr = reinterpret_cast<IOStatus*>(arg);
|
||||
assert(ptr);
|
||||
*ptr = IOStatus::IOError("Injected failure");
|
||||
});
|
||||
|
||||
@@ -1,527 +0,0 @@
|
||||
// Copyright (c) 2024-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
#ifdef OS_LINUX
|
||||
|
||||
class DBFollowerTest : public DBTestBase {
|
||||
public:
|
||||
// Create directories for leader and follower
|
||||
// Create the leader DB object
|
||||
DBFollowerTest() : DBTestBase("/db_follower_test", /*env_do_fsync*/ false) {
|
||||
follower_name_ = dbname_ + "/follower";
|
||||
db_parent_ = dbname_;
|
||||
Close();
|
||||
Destroy(CurrentOptions());
|
||||
EXPECT_EQ(env_->CreateDirIfMissing(dbname_), Status::OK());
|
||||
dbname_ = dbname_ + "/leader";
|
||||
Reopen(CurrentOptions());
|
||||
}
|
||||
|
||||
~DBFollowerTest() {
|
||||
follower_.reset();
|
||||
EXPECT_EQ(DestroyDB(follower_name_, CurrentOptions()), Status::OK());
|
||||
Destroy(CurrentOptions());
|
||||
dbname_ = db_parent_;
|
||||
}
|
||||
|
||||
protected:
|
||||
class DBFollowerTestFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit DBFollowerTestFS(const std::shared_ptr<FileSystem>& target)
|
||||
: FileSystemWrapper(target),
|
||||
cv_(&mutex_),
|
||||
barrier_(false),
|
||||
count_(0),
|
||||
reinit_count_(0) {}
|
||||
|
||||
const char* Name() const override { return "DBFollowerTestFS"; }
|
||||
|
||||
IOStatus NewSequentialFile(const std::string& fname,
|
||||
const FileOptions& file_opts,
|
||||
std::unique_ptr<FSSequentialFile>* result,
|
||||
IODebugContext* dbg = nullptr) override {
|
||||
class DBFollowerTestSeqFile : public FSSequentialFileWrapper {
|
||||
public:
|
||||
DBFollowerTestSeqFile(DBFollowerTestFS* fs,
|
||||
std::unique_ptr<FSSequentialFile>&& file,
|
||||
uint64_t /*size*/)
|
||||
: FSSequentialFileWrapper(file.get()),
|
||||
fs_(fs),
|
||||
file_(std::move(file)) {}
|
||||
|
||||
IOStatus Read(size_t n, const IOOptions& options, Slice* result,
|
||||
char* scratch, IODebugContext* dbg) override {
|
||||
fs_->BarrierWait();
|
||||
return target()->Read(n, options, result, scratch, dbg);
|
||||
}
|
||||
|
||||
private:
|
||||
DBFollowerTestFS* fs_;
|
||||
std::unique_ptr<FSSequentialFile> file_;
|
||||
};
|
||||
|
||||
std::unique_ptr<FSSequentialFile> file;
|
||||
IOStatus s = target()->NewSequentialFile(fname, file_opts, &file, dbg);
|
||||
|
||||
if (s.ok() && test::GetFileType(fname) == kDescriptorFile) {
|
||||
uint64_t size = 0;
|
||||
EXPECT_EQ(target()->GetFileSize(fname, IOOptions(), &size, nullptr),
|
||||
IOStatus::OK());
|
||||
result->reset(new DBFollowerTestSeqFile(this, std::move(file), size));
|
||||
} else {
|
||||
*result = std::move(file);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void BarrierInit(int count) {
|
||||
MutexLock l(&mutex_);
|
||||
barrier_ = true;
|
||||
count_ = count;
|
||||
}
|
||||
|
||||
void BarrierWait() {
|
||||
MutexLock l(&mutex_);
|
||||
if (!barrier_) {
|
||||
return;
|
||||
}
|
||||
if (--count_ == 0) {
|
||||
if (reinit_count_ > 0) {
|
||||
count_ = reinit_count_;
|
||||
reinit_count_ = 0;
|
||||
} else {
|
||||
barrier_ = false;
|
||||
}
|
||||
cv_.SignalAll();
|
||||
} else {
|
||||
cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void BarrierWaitAndReinit(int count) {
|
||||
MutexLock l(&mutex_);
|
||||
if (!barrier_) {
|
||||
return;
|
||||
}
|
||||
reinit_count_ = count;
|
||||
if (--count_ == 0) {
|
||||
if (reinit_count_ > 0) {
|
||||
count_ = reinit_count_;
|
||||
reinit_count_ = 0;
|
||||
} else {
|
||||
barrier_ = false;
|
||||
}
|
||||
cv_.SignalAll();
|
||||
} else {
|
||||
cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
port::CondVar cv_;
|
||||
bool barrier_;
|
||||
int count_;
|
||||
int reinit_count_;
|
||||
};
|
||||
|
||||
class DBFollowerTestSstPartitioner : public SstPartitioner {
|
||||
public:
|
||||
explicit DBFollowerTestSstPartitioner(uint64_t max_keys)
|
||||
: max_keys_(max_keys), num_keys_(0) {}
|
||||
|
||||
const char* Name() const override { return "DBFollowerTestSstPartitioner"; }
|
||||
|
||||
PartitionerResult ShouldPartition(
|
||||
const PartitionerRequest& /*request*/) override {
|
||||
if (++num_keys_ > max_keys_) {
|
||||
num_keys_ = 0;
|
||||
return PartitionerResult::kRequired;
|
||||
} else {
|
||||
return PartitionerResult::kNotRequired;
|
||||
}
|
||||
}
|
||||
|
||||
bool CanDoTrivialMove(const Slice& /*smallest_user_key*/,
|
||||
const Slice& /*largest_user_key*/) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t max_keys_;
|
||||
uint64_t num_keys_;
|
||||
};
|
||||
|
||||
class DBFollowerTestSstPartitionerFactory : public SstPartitionerFactory {
|
||||
public:
|
||||
explicit DBFollowerTestSstPartitionerFactory(uint64_t max_keys)
|
||||
: max_keys_(max_keys) {}
|
||||
|
||||
std::unique_ptr<SstPartitioner> CreatePartitioner(
|
||||
const SstPartitioner::Context& /*context*/) const override {
|
||||
std::unique_ptr<SstPartitioner> partitioner;
|
||||
partitioner.reset(new DBFollowerTestSstPartitioner(max_keys_));
|
||||
return partitioner;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "DBFollowerTestSstPartitionerFactory";
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t max_keys_;
|
||||
};
|
||||
|
||||
Status OpenAsFollower() {
|
||||
Options opts = CurrentOptions();
|
||||
if (!follower_env_) {
|
||||
follower_env_ = NewCompositeEnv(
|
||||
std::make_shared<DBFollowerTestFS>(env_->GetFileSystem()));
|
||||
}
|
||||
opts.env = follower_env_.get();
|
||||
opts.follower_refresh_catchup_period_ms = 100;
|
||||
return DB::OpenAsFollower(opts, follower_name_, dbname_, &follower_);
|
||||
}
|
||||
|
||||
std::string FollowerGet(const std::string& k) {
|
||||
ReadOptions options;
|
||||
options.verify_checksums = true;
|
||||
std::string result;
|
||||
Status s = follower()->Get(options, k, &result);
|
||||
if (s.IsNotFound()) {
|
||||
result = "NOT_FOUND";
|
||||
} else if (!s.ok()) {
|
||||
result = s.ToString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DB* follower() { return follower_.get(); }
|
||||
DBFollowerTestFS* follower_fs() {
|
||||
return static_cast<DBFollowerTestFS*>(follower_env_->GetFileSystem().get());
|
||||
}
|
||||
|
||||
void CheckDirs() {
|
||||
std::vector<std::string> db_children;
|
||||
std::vector<std::string> follower_children;
|
||||
EXPECT_OK(env_->GetChildren(dbname_, &db_children));
|
||||
EXPECT_OK(env_->GetChildren(follower_name_, &follower_children));
|
||||
|
||||
std::set<uint64_t> db_filenums;
|
||||
std::set<uint64_t> follower_filenums;
|
||||
for (auto& name : db_children) {
|
||||
if (test::GetFileType(name) != kTableFile) {
|
||||
continue;
|
||||
}
|
||||
db_filenums.insert(test::GetFileNumber(name));
|
||||
}
|
||||
for (auto& name : follower_children) {
|
||||
if (test::GetFileType(name) != kTableFile) {
|
||||
continue;
|
||||
}
|
||||
follower_filenums.insert(test::GetFileNumber(name));
|
||||
}
|
||||
db_filenums.merge(follower_filenums);
|
||||
EXPECT_EQ(follower_filenums.size(), db_filenums.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string follower_name_;
|
||||
std::string db_parent_;
|
||||
std::unique_ptr<Env> follower_env_;
|
||||
std::unique_ptr<DB> follower_;
|
||||
};
|
||||
|
||||
TEST_F(DBFollowerTest, Basic) {
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
std::string val;
|
||||
ASSERT_OK(follower()->Get(ReadOptions(), "k1", &val));
|
||||
ASSERT_EQ(val, "v1");
|
||||
CheckDirs();
|
||||
}
|
||||
|
||||
TEST_F(DBFollowerTest, Flush) {
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
|
||||
{"Leader::Done", "DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
{"DBImplFollower::TryCatchupWithLeader:End", "Follower::WaitForCatchup"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
TEST_SYNC_POINT("Leader::Start");
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
TEST_SYNC_POINT("Leader::Done");
|
||||
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup");
|
||||
std::string val;
|
||||
ASSERT_OK(follower()->Get(ReadOptions(), "k1", &val));
|
||||
ASSERT_EQ(val, "v1");
|
||||
CheckDirs();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test creates 4 L0 files, immediately followed by a compaction to L1.
|
||||
// The follower replays the 4 flush records from the MANIFEST unsuccessfully,
|
||||
// and then successfully recovers a Version from the compaction record
|
||||
TEST_F(DBFollowerTest, RetryCatchup) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
|
||||
{"DBImpl::BackgroundCompaction:Start",
|
||||
"DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
{"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin1",
|
||||
"DBImpl::BackgroundCompaction:BeforeCompaction"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin2"},
|
||||
{"DBImplFollower::TryCatchupWithLeader:End", "Follower::WaitForCatchup"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Leader::Start");
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup");
|
||||
ASSERT_EQ(FollowerGet("k1"), "v4");
|
||||
CheckDirs();
|
||||
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test validates the same as the previous test, except there is a
|
||||
// MANIFEST rollover between the flushes and compaction. The follower
|
||||
// does not switch to a new MANIFEST in ReadAndApply. So it would require
|
||||
// another round of refresh before catching up.
|
||||
TEST_F(DBFollowerTest, RetryCatchupManifestRollover) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
|
||||
{"Leader::Flushed", "DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
{"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin1",
|
||||
"Leader::Done"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin2"},
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Leader::Start");
|
||||
ASSERT_OK(Put("k1", "v4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
TEST_SYNC_POINT("Leader::Flushed");
|
||||
TEST_SYNC_POINT("Leader::Done");
|
||||
Reopen(opts);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:2"},
|
||||
});
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:2");
|
||||
ASSERT_EQ(FollowerGet("k1"), "v4");
|
||||
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test creates 4 L0 files and compacts them. The follower, during catchup,
|
||||
// successfully instantiates 4 Versions corresponding to the 4 files (but
|
||||
// donesn't install them yet), followed by deleting those 4 and adding a new
|
||||
// file from compaction. The test verifies that the 4 L0 files are deleted
|
||||
// correctly by the follower.
|
||||
// We use teh Barrier* functions to ensure that the follower first sees the 4
|
||||
// L0 files and is able to link them, and then sees the compaction that
|
||||
// obsoletes those L0 files (so those L0 files are intermediates that it has
|
||||
// to explicitly delete). Suppose we don't have any barriers, its possible
|
||||
// the follower reads the L0 records and compaction records from the MANIFEST
|
||||
// in one read, which means those L0 files would have already been deleted
|
||||
// by the leader and the follower cannot link to them.
|
||||
TEST_F(DBFollowerTest, IntermediateObsoleteFiles) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
Reopen(opts);
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
|
||||
follower_fs()->BarrierInit(2);
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k1", "v4"));
|
||||
ASSERT_OK(Flush());
|
||||
follower_fs()->BarrierWaitAndReinit(2);
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
follower_fs()->BarrierWait();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
|
||||
CheckDirs();
|
||||
ASSERT_EQ(FollowerGet("k1"), "v4");
|
||||
}
|
||||
|
||||
// This test verifies a scenario where the follower can recover a Version
|
||||
// partially (i.e some of the additions cannot be found), and the files
|
||||
// that are found are obsoleted by a subsequent VersionEdit.
|
||||
TEST_F(DBFollowerTest, PartialVersionRecovery) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.sst_partitioner_factory =
|
||||
std::make_shared<DBFollowerTestSstPartitionerFactory>(1);
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
ASSERT_OK(Put("k3", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k3", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
ASSERT_OK(dbfull()->SetOptions(dbfull()->DefaultColumnFamily(),
|
||||
{{"max_compaction_bytes", "1"}}));
|
||||
|
||||
follower_fs()->BarrierInit(2);
|
||||
Slice key("k1");
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
|
||||
|
||||
follower_fs()->BarrierWaitAndReinit(2);
|
||||
|
||||
// The second compaction input overlaps the previous compaction outputs
|
||||
// by one file. This file is never added to VersionStorageInfo since it
|
||||
// was added and deleted before the catch up completes. We later verify that
|
||||
// the follower correctly deleted this file.
|
||||
key = Slice("k3");
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
|
||||
follower_fs()->BarrierWait();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
|
||||
CheckDirs();
|
||||
ASSERT_EQ(FollowerGet("k1"), "v2");
|
||||
ASSERT_EQ(FollowerGet("k2"), "v1");
|
||||
ASSERT_EQ(FollowerGet("k3"), "v2");
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test verifies a scenario similar to the PartialVersionRecovery, except
|
||||
// with a MANIFEST rollover in between. When there is a rollover, the
|
||||
// follower's attempt ends without installing a new Version. The next catch up
|
||||
// attempt will recover a full Version.
|
||||
TEST_F(DBFollowerTest, PartialVersionRecoveryWithRollover) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.sst_partitioner_factory =
|
||||
std::make_shared<DBFollowerTestSstPartitionerFactory>(1);
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
ASSERT_OK(Put("k3", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
ASSERT_OK(Put("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("k3", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
opts.max_compaction_bytes = 1;
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_OK(OpenAsFollower());
|
||||
|
||||
follower_fs()->BarrierInit(2);
|
||||
Slice key("k1");
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
|
||||
|
||||
follower_fs()->BarrierWaitAndReinit(2);
|
||||
Reopen(opts);
|
||||
key = Slice("k3");
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
|
||||
follower_fs()->BarrierWait();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:Begin1",
|
||||
"Follower::WaitForCatchup:1"},
|
||||
{"Follower::WaitForCatchup:2",
|
||||
"DBImplFollower::TryCatchupWithLeader:Begin2"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:2");
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImplFollower::TryCatchupWithLeader:End",
|
||||
"Follower::WaitForCatchup:3"},
|
||||
});
|
||||
TEST_SYNC_POINT("Follower::WaitForCatchup:3");
|
||||
CheckDirs();
|
||||
ASSERT_EQ(FollowerGet("k1"), "v2");
|
||||
ASSERT_EQ(FollowerGet("k2"), "v1");
|
||||
ASSERT_EQ(FollowerGet("k3"), "v2");
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -13,6 +13,10 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
extern void MarkKeyMayExist(void* arg);
|
||||
extern bool SaveValue(void* arg, const ParsedInternalKey& parsed_key,
|
||||
const Slice& v, bool hit_and_return);
|
||||
|
||||
CompactedDBImpl::CompactedDBImpl(const DBOptions& options,
|
||||
const std::string& dbname)
|
||||
: DBImpl(options, dbname, /*seq_per_batch*/ false, +/*batch_per_txn*/ true,
|
||||
@@ -21,7 +25,7 @@ CompactedDBImpl::CompactedDBImpl(const DBOptions& options,
|
||||
version_(nullptr),
|
||||
user_comparator_(nullptr) {}
|
||||
|
||||
CompactedDBImpl::~CompactedDBImpl() = default;
|
||||
CompactedDBImpl::~CompactedDBImpl() {}
|
||||
|
||||
size_t CompactedDBImpl::FindFile(const Slice& key) {
|
||||
size_t right = files_.num_files - 1;
|
||||
@@ -33,6 +37,12 @@ size_t CompactedDBImpl::FindFile(const Slice& key) {
|
||||
files_.files);
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
const Slice& key, PinnableSlice* value) {
|
||||
return Get(options, /*column_family*/ nullptr, key, value,
|
||||
/*timestamp*/ nullptr);
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle*, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) {
|
||||
@@ -102,59 +112,62 @@ Status CompactedDBImpl::Get(const ReadOptions& _read_options,
|
||||
return Status::NotFound();
|
||||
}
|
||||
|
||||
void CompactedDBImpl::MultiGet(const ReadOptions& _read_options,
|
||||
size_t num_keys,
|
||||
ColumnFamilyHandle** /*column_families*/,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool /*sorted_input*/) {
|
||||
std::vector<Status> CompactedDBImpl::MultiGet(
|
||||
const ReadOptions& options, const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values) {
|
||||
return MultiGet(options, keys, values, /*timestamps*/ nullptr);
|
||||
}
|
||||
|
||||
std::vector<Status> CompactedDBImpl::MultiGet(
|
||||
const ReadOptions& _read_options, const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) {
|
||||
assert(user_comparator_);
|
||||
Status s;
|
||||
size_t num_keys = keys.size();
|
||||
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
|
||||
_read_options.io_activity != Env::IOActivity::kMultiGet) {
|
||||
s = Status::InvalidArgument(
|
||||
Status s = Status::InvalidArgument(
|
||||
"Can only call MultiGet with `ReadOptions::io_activity` is "
|
||||
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kMultiGet`");
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
|
||||
ReadOptions read_options(_read_options);
|
||||
if (s.ok()) {
|
||||
if (read_options.io_activity == Env::IOActivity::kUnknown) {
|
||||
read_options.io_activity = Env::IOActivity::kMultiGet;
|
||||
}
|
||||
|
||||
if (read_options.timestamp) {
|
||||
s = FailIfTsMismatchCf(DefaultColumnFamily(), *(read_options.timestamp));
|
||||
if (s.ok()) {
|
||||
if (read_options.timestamp->size() > 0) {
|
||||
s = FailIfReadCollapsedHistory(cfd_, cfd_->GetSuperVersion(),
|
||||
*(read_options.timestamp));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = FailIfCfHasTs(DefaultColumnFamily());
|
||||
}
|
||||
if (read_options.io_activity == Env::IOActivity::kUnknown) {
|
||||
read_options.io_activity = Env::IOActivity::kMultiGet;
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = s;
|
||||
if (read_options.timestamp) {
|
||||
Status s =
|
||||
FailIfTsMismatchCf(DefaultColumnFamily(), *(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
if (read_options.timestamp->size() > 0) {
|
||||
s = FailIfReadCollapsedHistory(cfd_, cfd_->GetSuperVersion(),
|
||||
*(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Status s = FailIfCfHasTs(DefaultColumnFamily());
|
||||
if (!s.ok()) {
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
if (timestamps) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
timestamps[i].clear();
|
||||
for (auto& ts : *timestamps) {
|
||||
ts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
GetWithTimestampReadCallback read_cb(kMaxSequenceNumber);
|
||||
autovector<TableReader*, 16> reader_list;
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
const Slice& key = keys[i];
|
||||
for (const auto& key : keys) {
|
||||
LookupKey lkey(key, kMaxSequenceNumber, read_options.timestamp);
|
||||
const FdWithKeyRange& f = files_.files[FindFile(lkey.user_key())];
|
||||
if (user_comparator_->CompareWithoutTimestamp(
|
||||
@@ -168,26 +181,30 @@ void CompactedDBImpl::MultiGet(const ReadOptions& _read_options,
|
||||
reader_list.push_back(f.fd.table_reader);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = Status::NotFound();
|
||||
std::vector<Status> statuses(num_keys, Status::NotFound());
|
||||
values->resize(num_keys);
|
||||
if (timestamps) {
|
||||
timestamps->resize(num_keys);
|
||||
}
|
||||
int idx = 0;
|
||||
for (auto* r : reader_list) {
|
||||
if (r != nullptr) {
|
||||
PinnableSlice& pinnable_val = values[idx];
|
||||
PinnableSlice pinnable_val;
|
||||
std::string& value = (*values)[idx];
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber, read_options.timestamp);
|
||||
std::string* timestamp = timestamps ? ×tamps[idx] : nullptr;
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[idx] : nullptr;
|
||||
GetContext get_context(
|
||||
user_comparator_, nullptr, nullptr, nullptr, GetContext::kNotFound,
|
||||
lkey.user_key(), &pinnable_val, /*columns=*/nullptr,
|
||||
user_comparator_->timestamp_size() > 0 ? timestamp : nullptr, nullptr,
|
||||
nullptr, true, nullptr, nullptr, nullptr, nullptr, &read_cb);
|
||||
Status status =
|
||||
Status s =
|
||||
r->Get(read_options, lkey.internal_key(), &get_context, nullptr);
|
||||
assert(static_cast<size_t>(idx) < num_keys);
|
||||
if (!status.ok() && !status.IsNotFound()) {
|
||||
statuses[idx] = status;
|
||||
assert(static_cast<size_t>(idx) < statuses.size());
|
||||
if (!s.ok() && !s.IsNotFound()) {
|
||||
statuses[idx] = s;
|
||||
} else {
|
||||
value.assign(pinnable_val.data(), pinnable_val.size());
|
||||
if (get_context.State() == GetContext::kFound) {
|
||||
statuses[idx] = Status::OK();
|
||||
}
|
||||
@@ -195,6 +212,7 @@ void CompactedDBImpl::MultiGet(const ReadOptions& _read_options,
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return statuses;
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Init(const Options& options) {
|
||||
|
||||
@@ -26,23 +26,33 @@ class CompactedDBImpl : public DBImpl {
|
||||
|
||||
// Implementations of the DB interface
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp) override;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) override;
|
||||
|
||||
Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) override;
|
||||
|
||||
using DB::MultiGet;
|
||||
// Note that CompactedDBImpl::MultiGet is not the optimized version of
|
||||
// MultiGet to use.
|
||||
// TODO: optimize CompactedDBImpl::MultiGet, see DBImpl::MultiGet for details.
|
||||
void MultiGet(const ReadOptions& options, size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses, const bool sorted_input) override;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options, const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override;
|
||||
|
||||
std::vector<Status> MultiGet(const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) override;
|
||||
|
||||
using DBImpl::Put;
|
||||
Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
virtual Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
@@ -55,53 +65,54 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DBImpl::Merge;
|
||||
Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
virtual Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Delete;
|
||||
Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
virtual Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DBImpl::CompactRange;
|
||||
Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/, const Slice* /*end*/) override {
|
||||
virtual Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/,
|
||||
const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
Status DisableFileDeletions() override {
|
||||
virtual Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
Status EnableFileDeletions() override {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
virtual Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
return DBImpl::GetLiveFiles(ret, manifest_file_size,
|
||||
false /* flush_memtable */);
|
||||
}
|
||||
using DBImpl::Flush;
|
||||
Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
virtual Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
Status SyncWAL() override {
|
||||
virtual Status SyncWAL() override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
Status IngestExternalFile(
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
@@ -109,7 +120,7 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
Status CreateColumnFamilyWithImport(
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
@@ -119,9 +130,9 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
|
||||
+557
-627
File diff suppressed because it is too large.
Load diff
+223
-294
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
@@ -51,17 +50,16 @@
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
#include "options/db_options.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/attribute_groups.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#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"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/repeatable_thread.h"
|
||||
@@ -231,16 +229,16 @@ class DBImpl : public DB {
|
||||
const Slice& end_key, const Slice& ts) override;
|
||||
|
||||
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;
|
||||
virtual Status Write(const WriteOptions& options,
|
||||
WriteBatch* updates) override;
|
||||
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) override;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) override;
|
||||
virtual Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) override;
|
||||
|
||||
using DB::GetEntity;
|
||||
Status GetEntity(const ReadOptions& options,
|
||||
@@ -265,6 +263,17 @@ class DBImpl : public DB {
|
||||
}
|
||||
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) override;
|
||||
|
||||
// This MultiGet is a batched version, which may be faster than calling Get
|
||||
// multiple times, especially if the keys have some spatial locality that
|
||||
// enables them to be queried in the same SST files/set of files. The larger
|
||||
@@ -272,6 +281,19 @@ class DBImpl : public DB {
|
||||
// The values and statuses parameters are arrays with number of elements
|
||||
// equal to keys.size(). This allows the storage for those to be alloacted
|
||||
// by the caller on the stack for small batches
|
||||
void MultiGet(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys, PinnableSlice* values,
|
||||
Status* statuses, const bool sorted_input = false) override;
|
||||
void MultiGet(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const size_t num_keys,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
|
||||
void MultiGet(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
void MultiGet(const ReadOptions& _read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
@@ -297,14 +319,9 @@ class DBImpl : public DB {
|
||||
const Slice* keys,
|
||||
PinnableAttributeGroups* results) override;
|
||||
|
||||
void MultiGetEntityWithCallback(
|
||||
const ReadOptions& read_options, ColumnFamilyHandle* column_family,
|
||||
ReadCallback* callback,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys);
|
||||
|
||||
Status CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle) override {
|
||||
virtual Status CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle) override {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
return CreateColumnFamily(ReadOptions(), WriteOptions(), cf_options,
|
||||
column_family, handle);
|
||||
@@ -314,7 +331,7 @@ class DBImpl : public DB {
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle);
|
||||
Status CreateColumnFamilies(
|
||||
virtual Status CreateColumnFamilies(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::vector<std::string>& column_family_names,
|
||||
std::vector<ColumnFamilyHandle*>* handles) override {
|
||||
@@ -328,7 +345,7 @@ class DBImpl : public DB {
|
||||
const std::vector<std::string>& column_family_names,
|
||||
std::vector<ColumnFamilyHandle*>* handles);
|
||||
|
||||
Status CreateColumnFamilies(
|
||||
virtual Status CreateColumnFamilies(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles) override {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
@@ -339,8 +356,8 @@ class DBImpl : public DB {
|
||||
const ReadOptions& read_options, const WriteOptions& write_options,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles);
|
||||
Status DropColumnFamily(ColumnFamilyHandle* column_family) override;
|
||||
Status DropColumnFamilies(
|
||||
virtual Status DropColumnFamily(ColumnFamilyHandle* column_family) override;
|
||||
virtual Status DropColumnFamilies(
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
|
||||
// Returns false if key doesn't exist in the database and true if it may.
|
||||
@@ -348,31 +365,21 @@ class DBImpl : public DB {
|
||||
// memory. On return, if value was found, then value_found will be set to true
|
||||
// , otherwise false.
|
||||
using DB::KeyMayExist;
|
||||
bool KeyMayExist(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value, std::string* timestamp,
|
||||
bool* value_found = nullptr) override;
|
||||
virtual bool KeyMayExist(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value, std::string* timestamp,
|
||||
bool* value_found = nullptr) override;
|
||||
|
||||
using DB::NewIterator;
|
||||
Iterator* NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
Status NewIterators(const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
|
||||
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;
|
||||
virtual Iterator* NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status NewIterators(
|
||||
const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
|
||||
virtual const Snapshot* GetSnapshot() override;
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
// Create a timestamped snapshot. This snapshot can be shared by multiple
|
||||
// readers. If any of them uses it for write conflict checking, then
|
||||
// is_write_conflict_boundary is true. For simplicity, set it to true by
|
||||
@@ -387,33 +394,35 @@ class DBImpl : public DB {
|
||||
timestamped_snapshots) const;
|
||||
|
||||
using DB::GetProperty;
|
||||
bool GetProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::string* value) override;
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) override;
|
||||
using DB::GetMapProperty;
|
||||
bool GetMapProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override;
|
||||
virtual bool GetMapProperty(
|
||||
ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override;
|
||||
using DB::GetIntProperty;
|
||||
bool GetIntProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
uint64_t* value) override;
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, uint64_t* value) override;
|
||||
using DB::GetAggregatedIntProperty;
|
||||
bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* aggregated_value) override;
|
||||
virtual bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* aggregated_value) override;
|
||||
using DB::GetApproximateSizes;
|
||||
Status GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes) override;
|
||||
virtual Status GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes) override;
|
||||
using DB::GetApproximateMemTableStats;
|
||||
void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range, uint64_t* const count,
|
||||
uint64_t* const size) override;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override;
|
||||
using DB::CompactRange;
|
||||
Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice* begin,
|
||||
const Slice* end) override;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) override;
|
||||
|
||||
using DB::CompactFiles;
|
||||
Status CompactFiles(
|
||||
virtual Status CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names, const int output_level,
|
||||
@@ -421,54 +430,55 @@ class DBImpl : public DB {
|
||||
std::vector<std::string>* const output_file_names = nullptr,
|
||||
CompactionJobInfo* compaction_job_info = nullptr) override;
|
||||
|
||||
Status PauseBackgroundWork() override;
|
||||
Status ContinueBackgroundWork() override;
|
||||
virtual Status PauseBackgroundWork() override;
|
||||
virtual Status ContinueBackgroundWork() override;
|
||||
|
||||
Status EnableAutoCompaction(
|
||||
virtual Status EnableAutoCompaction(
|
||||
const std::vector<ColumnFamilyHandle*>& column_family_handles) override;
|
||||
|
||||
void EnableManualCompaction() override;
|
||||
void DisableManualCompaction() override;
|
||||
virtual void EnableManualCompaction() override;
|
||||
virtual void DisableManualCompaction() override;
|
||||
|
||||
using DB::SetOptions;
|
||||
Status SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
Status SetDBOptions(
|
||||
virtual Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
using DB::NumberLevels;
|
||||
int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
using DB::MaxMemCompactionLevel;
|
||||
int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) override;
|
||||
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) override;
|
||||
using DB::Level0StopWriteTrigger;
|
||||
int Level0StopWriteTrigger(ColumnFamilyHandle* column_family) override;
|
||||
const std::string& GetName() const override;
|
||||
Env* GetEnv() const override;
|
||||
FileSystem* GetFileSystem() const override;
|
||||
virtual int Level0StopWriteTrigger(
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Env* GetEnv() const override;
|
||||
virtual FileSystem* GetFileSystem() const override;
|
||||
using DB::GetOptions;
|
||||
Options GetOptions(ColumnFamilyHandle* column_family) const override;
|
||||
virtual Options GetOptions(ColumnFamilyHandle* column_family) const override;
|
||||
using DB::GetDBOptions;
|
||||
DBOptions GetDBOptions() const override;
|
||||
virtual DBOptions GetDBOptions() const override;
|
||||
using DB::Flush;
|
||||
Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
Status Flush(
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status Flush(
|
||||
const FlushOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
Status FlushWAL(bool sync) override {
|
||||
virtual Status FlushWAL(bool sync) override {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
return FlushWAL(WriteOptions(), sync);
|
||||
}
|
||||
|
||||
virtual Status FlushWAL(const WriteOptions& write_options, bool sync);
|
||||
bool WALBufferIsEmpty();
|
||||
Status SyncWAL() override;
|
||||
Status LockWAL() override;
|
||||
Status UnlockWAL() override;
|
||||
virtual Status SyncWAL() override;
|
||||
virtual Status LockWAL() override;
|
||||
virtual Status UnlockWAL() override;
|
||||
|
||||
SequenceNumber GetLatestSequenceNumber() const override;
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
|
||||
// IncreaseFullHistoryTsLow(ColumnFamilyHandle*, std::string) will acquire
|
||||
// and release db_mutex
|
||||
@@ -480,21 +490,21 @@ class DBImpl : public DB {
|
||||
Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string* ts_low) override;
|
||||
|
||||
Status GetDbIdentity(std::string& identity) const override;
|
||||
virtual Status GetDbIdentity(std::string& identity) const override;
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
|
||||
|
||||
Status GetDbSessionId(std::string& session_id) const override;
|
||||
virtual Status GetDbSessionId(std::string& session_id) const override;
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override;
|
||||
|
||||
ColumnFamilyHandle* PersistentStatsColumnFamily() const;
|
||||
|
||||
Status Close() override;
|
||||
virtual Status Close() override;
|
||||
|
||||
Status DisableFileDeletions() override;
|
||||
virtual Status DisableFileDeletions() override;
|
||||
|
||||
Status EnableFileDeletions() override;
|
||||
virtual Status EnableFileDeletions(bool force) override;
|
||||
|
||||
virtual bool IsFileDeletionsEnabled() const;
|
||||
|
||||
@@ -503,41 +513,40 @@ class DBImpl : public DB {
|
||||
std::unique_ptr<StatsHistoryIterator>* stats_iterator) override;
|
||||
|
||||
using DB::ResetStats;
|
||||
Status ResetStats() override;
|
||||
virtual Status ResetStats() override;
|
||||
// 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;
|
||||
// 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 GetCreationTimeOfOldestFile(uint64_t* creation_time) override;
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true) override;
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& files) override;
|
||||
virtual Status GetCurrentWalFile(
|
||||
std::unique_ptr<LogFile>* current_log_file) override;
|
||||
virtual Status GetCreationTimeOfOldestFile(
|
||||
uint64_t* creation_time) override;
|
||||
|
||||
Status GetUpdatesSince(
|
||||
virtual Status GetUpdatesSince(
|
||||
SequenceNumber seq_number, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options =
|
||||
TransactionLogIterator::ReadOptions()) override;
|
||||
Status DeleteFile(std::string name) override;
|
||||
virtual Status DeleteFile(std::string name) override;
|
||||
Status DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end = true);
|
||||
|
||||
void GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) override;
|
||||
virtual void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData>* metadata) override;
|
||||
|
||||
Status GetLiveFilesChecksumInfo(FileChecksumList* checksum_list) override;
|
||||
virtual Status GetLiveFilesChecksumInfo(
|
||||
FileChecksumList* checksum_list) override;
|
||||
|
||||
Status GetLiveFilesStorageInfo(
|
||||
virtual Status GetLiveFilesStorageInfo(
|
||||
const LiveFilesStorageInfoOptions& opts,
|
||||
std::vector<LiveFileStorageInfo>* files) override;
|
||||
|
||||
// Obtains the meta data of the specified column family of the DB.
|
||||
// TODO(yhchiang): output parameter is placed in the end in this codebase.
|
||||
void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
virtual void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
|
||||
void GetAllColumnFamilyMetaData(
|
||||
std::vector<ColumnFamilyMetaData>* metadata) override;
|
||||
@@ -549,32 +558,32 @@ class DBImpl : public DB {
|
||||
int target_level) override;
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
Status IngestExternalFile(
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override;
|
||||
|
||||
using DB::IngestExternalFiles;
|
||||
Status IngestExternalFiles(
|
||||
virtual Status IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& args) override;
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
Status CreateColumnFamilyWithImport(
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const std::vector<const ExportImportFilesMetaData*>& metadatas,
|
||||
ColumnFamilyHandle** handle) override;
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key,
|
||||
const Slice& end_key) override;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key,
|
||||
const Slice& end_key) override;
|
||||
|
||||
using DB::VerifyFileChecksums;
|
||||
Status VerifyFileChecksums(const ReadOptions& read_options) override;
|
||||
|
||||
using DB::VerifyChecksum;
|
||||
Status VerifyChecksum(const ReadOptions& /*read_options*/) override;
|
||||
virtual Status VerifyChecksum(const ReadOptions& /*read_options*/) override;
|
||||
// Verify the checksums of files in db. Currently only tables are checked.
|
||||
//
|
||||
// read_options: controls file I/O behavior, e.g. read ahead size while
|
||||
@@ -595,16 +604,18 @@ class DBImpl : public DB {
|
||||
const ReadOptions& read_options);
|
||||
|
||||
using DB::StartTrace;
|
||||
Status StartTrace(const TraceOptions& options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) override;
|
||||
virtual Status StartTrace(
|
||||
const TraceOptions& options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) override;
|
||||
|
||||
using DB::EndTrace;
|
||||
Status EndTrace() override;
|
||||
virtual Status EndTrace() override;
|
||||
|
||||
using DB::NewDefaultReplayer;
|
||||
Status NewDefaultReplayer(const std::vector<ColumnFamilyHandle*>& handles,
|
||||
std::unique_ptr<TraceReader>&& reader,
|
||||
std::unique_ptr<Replayer>* replayer) override;
|
||||
virtual Status NewDefaultReplayer(
|
||||
const std::vector<ColumnFamilyHandle*>& handles,
|
||||
std::unique_ptr<TraceReader>&& reader,
|
||||
std::unique_ptr<Replayer>* replayer) override;
|
||||
|
||||
using DB::StartBlockCacheTrace;
|
||||
Status StartBlockCacheTrace(
|
||||
@@ -626,12 +637,14 @@ class DBImpl : public DB {
|
||||
Status EndIOTrace() override;
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
Status GetPropertiesOfAllTables(ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) override;
|
||||
Status GetPropertiesOfTablesInRange(
|
||||
virtual Status GetPropertiesOfAllTables(
|
||||
ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) override;
|
||||
virtual Status GetPropertiesOfTablesInRange(
|
||||
ColumnFamilyHandle* column_family, const Range* range, std::size_t n,
|
||||
TablePropertiesCollection* props) override;
|
||||
|
||||
|
||||
// ---- End of implementations of the DB interface ----
|
||||
SystemClock* GetSystemClock() const;
|
||||
|
||||
@@ -674,8 +687,8 @@ class DBImpl : public DB {
|
||||
|
||||
// If `snapshot` == kMaxSequenceNumber, set a recent one inside the file.
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& options,
|
||||
ColumnFamilyHandleImpl* cfh,
|
||||
SuperVersion* sv, SequenceNumber snapshot,
|
||||
ColumnFamilyData* cfd, SuperVersion* sv,
|
||||
SequenceNumber snapshot,
|
||||
ReadCallback* read_callback,
|
||||
bool expose_blob_index = false,
|
||||
bool allow_refresh = true);
|
||||
@@ -700,8 +713,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
|
||||
@@ -839,8 +851,6 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t MinLogNumberToKeep();
|
||||
|
||||
uint64_t MinLogNumberToRecycle();
|
||||
|
||||
// Returns the lower bound file number for SSTs that won't be deleted, even if
|
||||
// they're obsolete. This lower bound is used internally to prevent newly
|
||||
// created flush/compaction output files from being deleted before they're
|
||||
@@ -1077,8 +1087,7 @@ class DBImpl : public DB {
|
||||
static Status Open(const DBOptions& db_options, const std::string& name,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
const bool seq_per_batch, const bool batch_per_txn,
|
||||
const bool is_retry, bool* can_retry);
|
||||
const bool seq_per_batch, const bool batch_per_txn);
|
||||
|
||||
static IOStatus CreateAndNewDirectory(
|
||||
FileSystem* fs, const std::string& dirname,
|
||||
@@ -1172,8 +1181,6 @@ class DBImpl : public DB {
|
||||
|
||||
void TEST_UnlockMutex();
|
||||
|
||||
InstrumentedMutex* TEST_Mutex() { return &mutex_; }
|
||||
|
||||
void TEST_SignalAllBgCv();
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
@@ -1224,8 +1231,6 @@ class DBImpl : public DB {
|
||||
return logs_.back().number;
|
||||
}
|
||||
|
||||
void TEST_DeleteObsoleteFiles();
|
||||
|
||||
const std::unordered_set<uint64_t>& TEST_GetFilesGrabbedForPurge() const {
|
||||
return files_grabbed_for_purge_;
|
||||
}
|
||||
@@ -1253,22 +1258,6 @@ class DBImpl : public DB {
|
||||
// populate_historical_seconds, now].
|
||||
void RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds);
|
||||
|
||||
// Everytime DB's seqno to time mapping changed (which already hold the db
|
||||
// mutex), we install a new SuperVersion in each column family with a shared
|
||||
// copy of the new mapping while holding the db mutex.
|
||||
// This is done for all column families even though the column family does not
|
||||
// explicitly enabled the
|
||||
// `preclude_last_level_data_seconds` or `preserve_internal_time_seconds`
|
||||
// features.
|
||||
// This mapping supports iterators to fulfill the
|
||||
// "rocksdb.iterator.write-time" iterator property for entries in memtables.
|
||||
//
|
||||
// Since this new SuperVersion doesn't involve an LSM tree shape change, we
|
||||
// don't schedule work after installing this SuperVersion. It returns the used
|
||||
// `SuperVersionContext` for clean up after release mutex.
|
||||
void InstallSeqnoToTimeMappingInSV(
|
||||
std::vector<SuperVersionContext>* sv_contexts);
|
||||
|
||||
// Interface to block and signal the DB in case of stalling writes by
|
||||
// WriteBufferManager. Each DBImpl object contains ptr to WBMStallInterface.
|
||||
// When DB needs to be blocked or signalled by WriteBufferManager,
|
||||
@@ -1441,8 +1430,9 @@ class DBImpl : public DB {
|
||||
autovector<ColumnFamilyData*> cfds_;
|
||||
autovector<const MutableCFOptions*> mutable_cf_opts_;
|
||||
autovector<autovector<VersionEdit*>> edit_lists_;
|
||||
// All existing data files (SST files and Blob files) found during DB::Open.
|
||||
std::vector<std::string> existing_data_files_;
|
||||
// Stale SST files to delete found upon recovery. This stores a mapping from
|
||||
// such a file's absolute path to its parent directory.
|
||||
std::unordered_map<std::string, std::string> files_to_delete_;
|
||||
bool is_new_db_ = false;
|
||||
};
|
||||
|
||||
@@ -1462,9 +1452,6 @@ class DBImpl : public DB {
|
||||
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);
|
||||
@@ -1515,7 +1502,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,
|
||||
@@ -1524,7 +1510,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);
|
||||
@@ -1551,8 +1536,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);
|
||||
@@ -1562,7 +1546,7 @@ class DBImpl : public DB {
|
||||
Status WriteRecoverableState();
|
||||
|
||||
// Actual implementation of Close()
|
||||
virtual Status CloseImpl();
|
||||
Status CloseImpl();
|
||||
|
||||
// Recover the descriptor from persistent storage. May do a significant
|
||||
// amount of work to recover recently logged updates. Any changes to
|
||||
@@ -1574,9 +1558,9 @@ class DBImpl : public DB {
|
||||
virtual Status Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only = false, bool error_if_wal_file_exists = false,
|
||||
bool error_if_data_exists_in_wals = false, bool is_retry = false,
|
||||
bool error_if_data_exists_in_wals = false,
|
||||
uint64_t* recovered_seq = nullptr,
|
||||
RecoveryContext* recovery_ctx = nullptr, bool* can_retry = nullptr);
|
||||
RecoveryContext* recovery_ctx = nullptr);
|
||||
|
||||
virtual bool OwnTablesAndLogs() const { return true; }
|
||||
|
||||
@@ -1586,36 +1570,22 @@ class DBImpl : public DB {
|
||||
// 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.
|
||||
std::set<std::string> CollectAllDBPaths();
|
||||
|
||||
// REQUIRES: db mutex held when calling this function, but the db mutex can
|
||||
// be released and re-acquired. Db mutex will be held when the function
|
||||
// returns.
|
||||
// It stores all existing data files (SST and Blob) in RecoveryContext. In
|
||||
// the meantime, we find out the largest file number present in the paths, and
|
||||
// bump up the version set's next_file_number_ to be 1 + largest_file_number.
|
||||
// recovery_ctx stores the context about version edits. All those edits are
|
||||
// persisted to new Manifest after successfully syncing the new WAL.
|
||||
Status MaybeUpdateNextFileNumber(RecoveryContext* recovery_ctx);
|
||||
|
||||
// Track existing data files, including both referenced and unreferenced SST
|
||||
// and Blob files in SstFileManager. This is only called during DB::Open and
|
||||
// it's called before any file deletion start so that their deletion can be
|
||||
// properly rate limited.
|
||||
// Files may not be referenced in the MANIFEST because (e.g.
|
||||
// After recovery, there may be SST files in db/cf paths that are
|
||||
// not referenced in the MANIFEST (e.g.
|
||||
// 1. It's best effort recovery;
|
||||
// 2. The VersionEdits referencing the SST files are appended to
|
||||
// RecoveryContext, DB crashes when syncing the MANIFEST, the VersionEdits are
|
||||
// still not synced to MANIFEST during recovery.)
|
||||
//
|
||||
// If the file is referenced in Manifest (typically that's the
|
||||
// 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.
|
||||
void TrackExistingDataFiles(
|
||||
const std::vector<std::string>& existing_data_files);
|
||||
// It stores the SST files to be deleted in RecoveryContext. In the
|
||||
// meantime, we find out the largest file number present in the paths, and
|
||||
// bump up the version set's next_file_number_ to be 1 + largest_file_number.
|
||||
// recovery_ctx stores the context about version edits and files to be
|
||||
// deleted. All those edits are persisted to new Manifest after successfully
|
||||
// syncing the new WAL.
|
||||
Status DeleteUnreferencedSstFiles(RecoveryContext* recovery_ctx);
|
||||
|
||||
// SetDbSessionId() should be called in the constuctor DBImpl()
|
||||
// to ensure that db_session_id_ gets updated every time the DB is opened
|
||||
@@ -1669,7 +1639,6 @@ class DBImpl : public DB {
|
||||
friend class ForwardIterator;
|
||||
friend struct SuperVersion;
|
||||
friend class CompactedDBImpl;
|
||||
friend class DBImplFollower;
|
||||
#ifndef NDEBUG
|
||||
friend class DBTest_ConcurrentFlushWAL_Test;
|
||||
friend class DBTest_MixedSlowdownOptionsStop_Test;
|
||||
@@ -1724,11 +1693,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;
|
||||
@@ -1743,16 +1709,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() {
|
||||
@@ -1950,7 +1910,7 @@ class DBImpl : public DB {
|
||||
void ReleaseFileNumberFromPendingOutputs(
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v);
|
||||
|
||||
IOStatus SyncClosedWals(const WriteOptions& write_options,
|
||||
IOStatus SyncClosedLogs(const WriteOptions& write_options,
|
||||
JobContext* job_context, VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog);
|
||||
|
||||
@@ -1980,7 +1940,7 @@ class DBImpl : public DB {
|
||||
// corrupted_log_found is set to true if we recover from a corrupted log file.
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only,
|
||||
bool is_retry, bool* corrupted_log_found,
|
||||
bool* corrupted_log_found,
|
||||
RecoveryContext* recovery_ctx);
|
||||
|
||||
// The following two methods are used to flush a memtable to
|
||||
@@ -2084,22 +2044,17 @@ class DBImpl : public DB {
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
if (immutable_db_options_.unordered_write) {
|
||||
// Wait for the ones who already wrote to the WAL to finish their
|
||||
// memtable write.
|
||||
if (pending_memtable_writes_.load() != 0) {
|
||||
// XXX: suspicious wait while holding DB mutex?
|
||||
std::unique_lock<std::mutex> guard(switch_mutex_);
|
||||
switch_cv_.wait(guard,
|
||||
[&] { return pending_memtable_writes_.load() == 0; });
|
||||
}
|
||||
} else {
|
||||
// (Writes are finished before the next write group starts.)
|
||||
if (!immutable_db_options_.unordered_write) {
|
||||
// Then the writes are finished before the next write group starts
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for any LockWAL to clear
|
||||
while (lock_wal_count_ > 0) {
|
||||
bg_cv_.Wait();
|
||||
// Wait for the ones who already wrote to the WAL to finish their
|
||||
// memtable write.
|
||||
if (pending_memtable_writes_.load() != 0) {
|
||||
std::unique_lock<std::mutex> guard(switch_mutex_);
|
||||
switch_cv_.wait(guard,
|
||||
[&] { return pending_memtable_writes_.load() == 0; });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2209,8 +2164,7 @@ class DBImpl : public DB {
|
||||
void GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
FlushReason flush_reason, FlushRequest* req);
|
||||
|
||||
// Returns true if `req` is successfully enqueued.
|
||||
bool SchedulePendingFlush(const FlushRequest& req);
|
||||
void SchedulePendingFlush(const FlushRequest& req);
|
||||
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
@@ -2286,11 +2240,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,
|
||||
@@ -2399,7 +2348,10 @@ class DBImpl : public DB {
|
||||
// A structure to hold the information required to process MultiGet of keys
|
||||
// belonging to one column family. For a multi column family MultiGet, there
|
||||
// will be a container of these objects.
|
||||
struct MultiGetKeyRangePerCf {
|
||||
struct MultiGetColumnFamilyData {
|
||||
ColumnFamilyHandle* cf;
|
||||
ColumnFamilyData* cfd;
|
||||
|
||||
// For the batched MultiGet which relies on sorted keys, start specifies
|
||||
// the index of first key belonging to this column family in the sorted
|
||||
// list.
|
||||
@@ -2409,33 +2361,31 @@ class DBImpl : public DB {
|
||||
// belonging to this column family in the sorted list
|
||||
size_t num_keys;
|
||||
|
||||
MultiGetKeyRangePerCf() : start(0), num_keys(0) {}
|
||||
|
||||
MultiGetKeyRangePerCf(size_t first, size_t count)
|
||||
: start(first), num_keys(count) {}
|
||||
};
|
||||
|
||||
// A structure to contain ColumnFamilyData and the SuperVersion obtained for
|
||||
// the consistent view of DB
|
||||
struct ColumnFamilySuperVersionPair {
|
||||
ColumnFamilyHandleImpl* cfh;
|
||||
ColumnFamilyData* cfd;
|
||||
|
||||
// SuperVersion for the column family obtained in a manner that ensures a
|
||||
// consistent view across all column families in the DB
|
||||
SuperVersion* super_version;
|
||||
ColumnFamilySuperVersionPair(ColumnFamilyHandle* column_family,
|
||||
SuperVersion* sv)
|
||||
: cfh(static_cast<ColumnFamilyHandleImpl*>(column_family)),
|
||||
cfd(cfh->cfd()),
|
||||
MultiGetColumnFamilyData(ColumnFamilyHandle* column_family,
|
||||
SuperVersion* sv)
|
||||
: cf(column_family),
|
||||
cfd(static_cast<ColumnFamilyHandleImpl*>(cf)->cfd()),
|
||||
start(0),
|
||||
num_keys(0),
|
||||
super_version(sv) {}
|
||||
|
||||
ColumnFamilySuperVersionPair() = default;
|
||||
MultiGetColumnFamilyData(ColumnFamilyHandle* column_family, size_t first,
|
||||
size_t count, SuperVersion* sv)
|
||||
: cf(column_family),
|
||||
cfd(static_cast<ColumnFamilyHandleImpl*>(cf)->cfd()),
|
||||
start(first),
|
||||
num_keys(count),
|
||||
super_version(sv) {}
|
||||
|
||||
MultiGetColumnFamilyData() = default;
|
||||
};
|
||||
|
||||
// A common function to obtain a consistent snapshot, which can be implicit
|
||||
// if the user doesn't specify a snapshot in read_options, across
|
||||
// multiple column families. It will attempt to get an implicit
|
||||
// multiple column families for MultiGet. It will attempt to get an implicit
|
||||
// snapshot without acquiring the db_mutes, but will give up after a few
|
||||
// tries and acquire the mutex if a memtable flush happens. The template
|
||||
// allows both the batched and non-batched MultiGet to call this with
|
||||
@@ -2444,26 +2394,18 @@ class DBImpl : public DB {
|
||||
// If callback is non-null, the callback is refreshed with the snapshot
|
||||
// sequence number
|
||||
//
|
||||
// `extra_sv_ref` is used to indicate whether thread-local SuperVersion
|
||||
// should be obtained with an extra ref (by GetReferencedSuperVersion()) or
|
||||
// not (by GetAndRefSuperVersion()). For instance, point lookup like MultiGet
|
||||
// does not require SuperVersion to be re-acquired throughout the entire
|
||||
// invocation (no need extra ref), while MultiCfIterators may need the
|
||||
// SuperVersion to be updated during Refresh() (requires extra ref).
|
||||
//
|
||||
// `sv_from_thread_local` being set to false indicates that the SuperVersion
|
||||
// obtained from the ColumnFamilyData, whereas true indicates they are thread
|
||||
// local.
|
||||
//
|
||||
// A non-OK status will be returned if for a column family that enables
|
||||
// user-defined timestamp feature, the specified `ReadOptions.timestamp`
|
||||
// attemps to read collapsed history.
|
||||
template <class T, typename IterDerefFuncType>
|
||||
Status MultiCFSnapshot(const ReadOptions& read_options,
|
||||
ReadCallback* callback,
|
||||
IterDerefFuncType iter_deref_func, T* cf_list,
|
||||
bool extra_sv_ref, SequenceNumber* snapshot,
|
||||
bool* sv_from_thread_local);
|
||||
template <class T>
|
||||
Status MultiCFSnapshot(
|
||||
const ReadOptions& read_options, ReadCallback* callback,
|
||||
std::function<MultiGetColumnFamilyData*(typename T::iterator&)>&
|
||||
iter_deref_func,
|
||||
T* cf_list, SequenceNumber* snapshot, bool* sv_from_thread_local);
|
||||
|
||||
// The actual implementation of the batching MultiGet. The caller is expected
|
||||
// to have acquired the SuperVersion and pass in a snapshot sequence number
|
||||
@@ -2486,13 +2428,6 @@ class DBImpl : public DB {
|
||||
|
||||
bool ShouldReferenceSuperVersion(const MergeContext& merge_context);
|
||||
|
||||
template <typename IterType, typename ImplType,
|
||||
typename ErrorIteratorFuncType>
|
||||
std::unique_ptr<IterType> NewMultiCfIterator(
|
||||
const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
ErrorIteratorFuncType error_iterator_func);
|
||||
|
||||
// Lock over the persistent DB state. Non-nullptr iff successfully acquired.
|
||||
FileLock* db_lock_;
|
||||
|
||||
@@ -2544,11 +2479,6 @@ class DBImpl : public DB {
|
||||
uint64_t logfile_number_;
|
||||
// Log files that we can recycle. Must be protected by db mutex_.
|
||||
std::deque<uint64_t> log_recycle_files_;
|
||||
// The minimum log file number taht can be recycled, if log recycling is
|
||||
// enabled. This is used to ensure that log files created by previous
|
||||
// instances of the database are not recycled, as we cannot be sure they
|
||||
// were created in the recyclable format.
|
||||
uint64_t min_log_number_to_recycle_;
|
||||
// Protected by log_write_mutex_.
|
||||
bool log_dir_synced_;
|
||||
// Without two_write_queues, read and writes to log_empty_ are protected by
|
||||
@@ -2563,10 +2493,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.
|
||||
@@ -2617,7 +2545,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()
|
||||
@@ -2630,7 +2558,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
|
||||
@@ -2925,16 +2853,17 @@ class GetWithTimestampReadCallback : public ReadCallback {
|
||||
}
|
||||
};
|
||||
|
||||
Options SanitizeOptions(const std::string& db, const Options& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
extern Options SanitizeOptions(const std::string& db, const Options& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
|
||||
DBOptions SanitizeOptions(const std::string& db, const DBOptions& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
extern DBOptions SanitizeOptions(const std::string& db, const DBOptions& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
|
||||
CompressionType GetCompressionFlush(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
extern CompressionType GetCompressionFlush(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Return the earliest log file to keep after the memtable flush is
|
||||
// finalized.
|
||||
@@ -2942,13 +2871,13 @@ CompressionType GetCompressionFlush(const ImmutableCFOptions& ioptions,
|
||||
// `memtables_to_flush`) will be flushed and thus will not depend on any WAL
|
||||
// file.
|
||||
// The function is only applicable to 2pc mode.
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
extern uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker);
|
||||
// For atomic flush.
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
extern uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush,
|
||||
@@ -2956,21 +2885,21 @@ uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
|
||||
// In non-2PC mode, WALs with log number < the returned number can be
|
||||
// deleted after the cfd_to_flush column family is flushed successfully.
|
||||
uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
extern uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list);
|
||||
// For atomic flush.
|
||||
uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
extern uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists);
|
||||
|
||||
// `cfd_to_flush` is the column family whose memtable will be flushed and thus
|
||||
// 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(
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<MemTable*>& memtables_to_flush);
|
||||
// For atomic flush.
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush);
|
||||
|
||||
@@ -3036,9 +2965,9 @@ inline Status DBImpl::FailIfReadCollapsedHistory(const ColumnFamilyData* cfd,
|
||||
if (!full_history_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(ts, full_history_ts_low) < 0) {
|
||||
std::stringstream oss;
|
||||
oss << "Read timestamp: " << ucmp->TimestampToString(ts)
|
||||
oss << "Read timestamp: " << ts.ToString(true)
|
||||
<< " is smaller than full_history_ts_low: "
|
||||
<< ucmp->TimestampToString(full_history_ts_low) << std::endl;
|
||||
<< Slice(full_history_ts_low).ToString(true) << std::endl;
|
||||
return Status::InvalidArgument(oss.str());
|
||||
}
|
||||
return Status::OK();
|
||||
|
||||
@@ -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,87 @@ 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();
|
||||
}
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
io_s = log->Close(WriteOptions());
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -162,25 +222,19 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// the host crashes after flushing and before WAL is persistent, the
|
||||
// flushed SST may contain data from write batches whose updates to
|
||||
// other (unflushed) column families are missing.
|
||||
//
|
||||
// When 2PC is enabled, non-recent WAL(s) may be needed for crash-recovery,
|
||||
// even when there is only one CF in the DB, for prepared transactions that
|
||||
// had not been committed yet. Make sure we sync them to keep the persisted
|
||||
// WAL state at least as new as the persisted SST state.
|
||||
const bool needs_to_sync_closed_wals =
|
||||
logfile_number_ > 0 &&
|
||||
(versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1 ||
|
||||
allow_2pc());
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1;
|
||||
|
||||
// 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 +248,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.
|
||||
@@ -216,20 +270,20 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||
io_tracer_, cfd->GetSuperVersion()->ShareSeqnoToTimeMapping(), db_id_,
|
||||
db_session_id_, cfd->GetFullHistoryTsLow(), &blob_callback_);
|
||||
io_tracer_, seqno_to_time_mapping_, db_id_, db_session_id_,
|
||||
cfd->GetFullHistoryTsLow(), &blob_callback_);
|
||||
FileMetaData file_meta;
|
||||
|
||||
Status s;
|
||||
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 +298,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;
|
||||
|
||||
@@ -491,9 +545,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri, io_tracer_,
|
||||
cfd->GetSuperVersion()->ShareSeqnoToTimeMapping(), db_id_,
|
||||
db_session_id_, cfd->GetFullHistoryTsLow(), &blob_callback_));
|
||||
thread_pri, io_tracer_, seqno_to_time_mapping_, db_id_, db_session_id_,
|
||||
cfd->GetFullHistoryTsLow(), &blob_callback_));
|
||||
}
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
@@ -518,7 +571,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()) {
|
||||
@@ -917,7 +970,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
||||
info.largest_seqno = file_meta->fd.largest_seqno;
|
||||
info.flush_reason = flush_reason;
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushBegin(this, info);
|
||||
}
|
||||
}
|
||||
@@ -949,7 +1002,7 @@ void DBImpl::NotifyOnFlushCompleted(
|
||||
for (auto& info : *flush_jobs_info) {
|
||||
info->triggered_writes_slowdown = triggered_writes_slowdown;
|
||||
info->triggered_writes_stop = triggered_writes_stop;
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushCompleted(this, *info);
|
||||
}
|
||||
TEST_SYNC_POINT(
|
||||
@@ -1032,12 +1085,7 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
assert(ucmp->timestamp_size() == ts_low.size() && !ts_low.empty());
|
||||
if (!current_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(ts_low, current_ts_low) < 0) {
|
||||
std::stringstream oss;
|
||||
oss << "Current full_history_ts_low: "
|
||||
<< ucmp->TimestampToString(current_ts_low)
|
||||
<< " is higher than provided ts: " << ucmp->TimestampToString(ts_low)
|
||||
<< std::endl;
|
||||
return Status::InvalidArgument(oss.str());
|
||||
return Status::InvalidArgument("Cannot decrease full_history_ts_low");
|
||||
}
|
||||
|
||||
Status s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
@@ -1069,11 +1117,6 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
if (options.target_path_id >= cfd->ioptions()->cf_paths.size()) {
|
||||
return Status::InvalidArgument("Invalid target path ID");
|
||||
}
|
||||
if (options.change_level &&
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
return Status::NotSupported(
|
||||
"FIFO compaction does not support change_level.");
|
||||
}
|
||||
|
||||
bool flush_needed = true;
|
||||
|
||||
@@ -1097,7 +1140,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
// TODO(ajkr): We could also optimize away the flush in certain cases where
|
||||
// one/both sides of the interval are unbounded. But it requires more
|
||||
// changes to RangesOverlapWithMemtables.
|
||||
UserKeyRange range(*begin, *end);
|
||||
Range range(*begin, *end);
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
s = cfd->RangesOverlapWithMemtables(
|
||||
{range}, super_version, immutable_db_options_.allow_data_in_errors,
|
||||
@@ -1134,16 +1177,6 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
final_output_level, options, begin, end, exclusive,
|
||||
false /* disable_trivial_move */,
|
||||
std::numeric_limits<uint64_t>::max(), trim_ts);
|
||||
} else if (cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
// FIFOCompactionPicker::CompactRange() will ignore the input key range
|
||||
// [begin, end] and just try to pick compaction based on the configured
|
||||
// option `compaction_options_fifo`. So we skip checking if [begin, end]
|
||||
// overlaps with the DB here.
|
||||
final_output_level = 0;
|
||||
s = RunManualCompaction(cfd, /*input_level=*/0, final_output_level, options,
|
||||
begin, end, exclusive,
|
||||
false /* disable_trivial_move */,
|
||||
std::numeric_limits<uint64_t>::max(), trim_ts);
|
||||
} else {
|
||||
int first_overlapped_level = kInvalidLevel;
|
||||
{
|
||||
@@ -1228,7 +1261,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
if (s.ok() && first_overlapped_level != kInvalidLevel) {
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal) {
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
assert(first_overlapped_level == 0);
|
||||
s = RunManualCompaction(
|
||||
cfd, first_overlapped_level, first_overlapped_level, options, begin,
|
||||
@@ -1383,19 +1417,12 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
|
||||
if (compact_options.compression !=
|
||||
CompressionType::kDisableCompressionOption) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] [JOB %d] Found use of deprecated option "
|
||||
"`CompactionOptions::compression`",
|
||||
cfd->GetName().c_str(), job_context.job_id);
|
||||
}
|
||||
|
||||
// Perform CompactFiles
|
||||
TEST_SYNC_POINT("TestCompactFiles::IngestExternalFile2");
|
||||
TEST_SYNC_POINT_CALLBACK("TestCompactFiles:PausingManualCompaction:3",
|
||||
static_cast<void*>(const_cast<std::atomic<int>*>(
|
||||
&manual_compaction_paused_)));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"TestCompactFiles:PausingManualCompaction:3",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<int>*>(&manual_compaction_paused_)));
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto* current = cfd->current();
|
||||
@@ -1480,11 +1507,16 @@ Status DBImpl::CompactFilesImpl(
|
||||
std::to_string(cfd->ioptions()->num_levels - 1));
|
||||
}
|
||||
|
||||
Status s = cfd->compaction_picker()->SanitizeCompactionInputFiles(
|
||||
&input_set, cf_meta, output_level);
|
||||
TEST_SYNC_POINT("DBImpl::CompactFilesImpl::PostSanitizeCompactionInputFiles");
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> input_files;
|
||||
Status s = cfd->compaction_picker()->SanitizeAndConvertCompactionInputFiles(
|
||||
&input_set, cf_meta, output_level, version->storage_info(), &input_files);
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::CompactFilesImpl::PostSanitizeAndConvertCompactionInputFiles");
|
||||
s = cfd->compaction_picker()->GetCompactionInputsFromFileNumbers(
|
||||
&input_files, &input_set, version->storage_info(), compact_options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1578,9 +1610,9 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
if (status.ok()) {
|
||||
assert(compaction_job.io_status().ok());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
}
|
||||
// status above captures any error during compaction_job.Install, so its ok
|
||||
// not check compaction_job.io_status() explicitly if we're not calling
|
||||
@@ -1663,7 +1695,7 @@ Status DBImpl::PauseBackgroundWork() {
|
||||
Status DBImpl::ContinueBackgroundWork() {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
if (bg_work_paused_ == 0) {
|
||||
return Status::InvalidArgument("Background work already unpaused");
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
assert(bg_work_paused_ > 0);
|
||||
assert(bg_compaction_paused_ > 0);
|
||||
@@ -1700,7 +1732,7 @@ void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
{
|
||||
CompactionJobInfo info{};
|
||||
BuildCompactionJobInfo(cfd, c, st, job_stats, job_id, &info);
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionBegin(this, info);
|
||||
}
|
||||
info.status.PermitUncheckedError();
|
||||
@@ -1729,7 +1761,7 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
{
|
||||
CompactionJobInfo info{};
|
||||
BuildCompactionJobInfo(cfd, c, st, compaction_job_stats, job_id, &info);
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionCompleted(this, info);
|
||||
}
|
||||
}
|
||||
@@ -1740,8 +1772,6 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
|
||||
// REQUIREMENT: block all background work by calling PauseBackgroundWork()
|
||||
// before calling this function
|
||||
// TODO (hx235): Replace Status::NotSupported() with Status::Aborted() for
|
||||
// better semantics like CompactFiles()
|
||||
Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
assert(level < cfd->NumberLevels());
|
||||
if (target_level >= cfd->NumberLevels()) {
|
||||
@@ -1777,8 +1807,6 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
if (to_level != level) {
|
||||
std::vector<CompactionInputFiles> input(1);
|
||||
input[0].level = level;
|
||||
// TODO (hx235): Only refit the output files in the current manual
|
||||
// compaction instead of all the files in the output level
|
||||
for (auto& f : vstorage->LevelFiles(level)) {
|
||||
input[0].files.push_back(f);
|
||||
}
|
||||
@@ -1810,12 +1838,6 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
}
|
||||
} else {
|
||||
// to_level < level
|
||||
if (to_level == 0 && input[0].files.size() > 1) {
|
||||
refitting_level_ = false;
|
||||
return Status::Aborted(
|
||||
"Moving more than 1 file from non-L0 to L0 is not allowed as it "
|
||||
"does not bring any benefit to read nor write throughput.");
|
||||
}
|
||||
// Check levels are empty for a trivial move
|
||||
for (int l = to_level; l < level; l++) {
|
||||
if (vstorage->NumLevelFiles(l) > 0) {
|
||||
@@ -1847,8 +1869,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
,
|
||||
LLONG_MAX /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID, not applicable */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
0 /* max_subcompactions, not applicable */,
|
||||
{} /* grandparents, not applicable */, false /* is manual */,
|
||||
"" /* trim_ts */, -1 /* score, not applicable */,
|
||||
@@ -2130,6 +2151,16 @@ Status DBImpl::RunManualCompaction(
|
||||
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
||||
max_file_num_to_ignore, trim_ts)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
if (!scheduled) {
|
||||
// There is a conflicting compaction
|
||||
if (manual_compaction_paused_ > 0 || manual.canceled == true) {
|
||||
// Stop waiting since it was canceled. Pretend the error came from
|
||||
// compaction so the below cleanup/error handling code can process it.
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
}
|
||||
if (!manual.done) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
@@ -2204,17 +2235,6 @@ Status DBImpl::RunManualCompaction(
|
||||
*final_output_level = compaction->output_level();
|
||||
}
|
||||
}
|
||||
if (!scheduled) {
|
||||
// There is nothing scheduled to wait on, so any cancellation can end the
|
||||
// manual now.
|
||||
if (manual_compaction_paused_ > 0 || manual.canceled == true) {
|
||||
// Stop waiting since it was canceled. Pretend the error came from
|
||||
// compaction so the below cleanup/error handling code can process it.
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_buffer.FlushBufferToLog();
|
||||
@@ -2248,23 +2268,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,
|
||||
@@ -2369,14 +2372,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 = SchedulePendingFlush(req);
|
||||
if (already_queued_for_flush || flush_req_enqueued) {
|
||||
loop_cfd->SetFlushSkipReschedule();
|
||||
}
|
||||
SchedulePendingFlush(req);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -2388,8 +2384,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) {
|
||||
@@ -2534,7 +2528,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) {
|
||||
@@ -2592,9 +2585,7 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
|
||||
flush_reason,
|
||||
{{cfd,
|
||||
std::numeric_limits<uint64_t>::max() /* max_mem_id_to_persist */}}};
|
||||
if (SchedulePendingFlush(flush_req)) {
|
||||
cfd->SetFlushSkipReschedule();
|
||||
};
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -2682,13 +2673,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();
|
||||
@@ -3000,14 +2991,13 @@ ColumnFamilyData* DBImpl::PickCompactionFromQueue(
|
||||
return cfd;
|
||||
}
|
||||
|
||||
bool DBImpl::SchedulePendingFlush(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
|
||||
@@ -3022,7 +3012,6 @@ bool DBImpl::SchedulePendingFlush(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) {
|
||||
@@ -3031,9 +3020,7 @@ bool DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
|
||||
}
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
enqueued = true;
|
||||
}
|
||||
return enqueued;
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingCompaction(ColumnFamilyData* cfd) {
|
||||
@@ -3042,8 +3029,6 @@ void DBImpl::SchedulePendingCompaction(ColumnFamilyData* cfd) {
|
||||
return;
|
||||
}
|
||||
if (!cfd->queued_for_compaction() && cfd->NeedsCompaction()) {
|
||||
TEST_SYNC_POINT_CALLBACK("SchedulePendingCompaction::cfd",
|
||||
static_cast<void*>(cfd));
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
}
|
||||
@@ -3060,8 +3045,8 @@ void DBImpl::SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkFlush(void* arg) {
|
||||
FlushThreadArg fta = *(static_cast<FlushThreadArg*>(arg));
|
||||
delete static_cast<FlushThreadArg*>(arg);
|
||||
FlushThreadArg fta = *(reinterpret_cast<FlushThreadArg*>(arg));
|
||||
delete reinterpret_cast<FlushThreadArg*>(arg);
|
||||
|
||||
IOSTATS_SET_THREAD_POOL_ID(fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush");
|
||||
@@ -3070,8 +3055,8 @@ void DBImpl::BGWorkFlush(void* arg) {
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkCompaction(void* arg) {
|
||||
CompactionArg ca = *(static_cast<CompactionArg*>(arg));
|
||||
delete static_cast<CompactionArg*>(arg);
|
||||
CompactionArg ca = *(reinterpret_cast<CompactionArg*>(arg));
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::LOW);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkCompaction");
|
||||
auto prepicked_compaction =
|
||||
@@ -3095,12 +3080,12 @@ void DBImpl::BGWorkBottomCompaction(void* arg) {
|
||||
void DBImpl::BGWorkPurge(void* db) {
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::HIGH);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkPurge:start");
|
||||
static_cast<DBImpl*>(db)->BackgroundCallPurge();
|
||||
reinterpret_cast<DBImpl*>(db)->BackgroundCallPurge();
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkPurge:end");
|
||||
}
|
||||
|
||||
void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
CompactionArg* ca_ptr = static_cast<CompactionArg*>(arg);
|
||||
CompactionArg* ca_ptr = reinterpret_cast<CompactionArg*>(arg);
|
||||
Env::Priority compaction_pri = ca_ptr->compaction_pri_;
|
||||
if (Env::Priority::BOTTOM == compaction_pri) {
|
||||
// Decrement bg_bottom_compaction_scheduled_ if priority is BOTTOM
|
||||
@@ -3110,7 +3095,7 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
ca_ptr->db->bg_compaction_scheduled_--;
|
||||
}
|
||||
CompactionArg ca = *(ca_ptr);
|
||||
delete static_cast<CompactionArg*>(arg);
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
if (ca.prepicked_compaction != nullptr) {
|
||||
// if it's a manual compaction, set status to ManualCompactionPaused
|
||||
if (ca.prepicked_compaction->manual_compaction_state) {
|
||||
@@ -3130,14 +3115,14 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
|
||||
void DBImpl::UnscheduleFlushCallback(void* arg) {
|
||||
// Decrement bg_flush_scheduled_ in flush callback
|
||||
static_cast<FlushThreadArg*>(arg)->db_->bg_flush_scheduled_--;
|
||||
Env::Priority flush_pri = static_cast<FlushThreadArg*>(arg)->thread_pri_;
|
||||
reinterpret_cast<FlushThreadArg*>(arg)->db_->bg_flush_scheduled_--;
|
||||
Env::Priority flush_pri = reinterpret_cast<FlushThreadArg*>(arg)->thread_pri_;
|
||||
if (Env::Priority::LOW == flush_pri) {
|
||||
TEST_SYNC_POINT("DBImpl::UnscheduleLowFlushCallback");
|
||||
} else if (Env::Priority::HIGH == flush_pri) {
|
||||
TEST_SYNC_POINT("DBImpl::UnscheduleHighFlushCallback");
|
||||
}
|
||||
delete static_cast<FlushThreadArg*>(arg);
|
||||
delete reinterpret_cast<FlushThreadArg*>(arg);
|
||||
TEST_SYNC_POINT("DBImpl::UnscheduleFlushCallback");
|
||||
}
|
||||
|
||||
@@ -3236,7 +3221,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
column_families_not_to_flush.push_back(cfd);
|
||||
continue;
|
||||
}
|
||||
superversion_contexts.emplace_back(true);
|
||||
superversion_contexts.emplace_back(SuperVersionContext(true));
|
||||
bg_flush_args.emplace_back(cfd, max_memtable_id,
|
||||
&(superversion_contexts.back()), flush_reason);
|
||||
}
|
||||
@@ -3331,7 +3316,7 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"[JOB %d] Waiting after background flush error: %s, "
|
||||
"[JOB %d] Waiting after background flush error: %s"
|
||||
"Accumulated background error counts: %" PRIu64,
|
||||
job_context.job_id, s.ToString().c_str(), error_cnt);
|
||||
log_buffer.FlushBufferToLog();
|
||||
@@ -3741,9 +3726,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
compaction_released = true;
|
||||
});
|
||||
io_s = versions_->io_status();
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Deleted %d files\n",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
c->num_input_files(0));
|
||||
@@ -3816,9 +3801,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
});
|
||||
io_s = versions_->io_status();
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
c->column_family_data()->internal_stats()->IncBytesMoved(c->output_level(),
|
||||
@@ -3911,9 +3896,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
|
||||
io_s = compaction_job.io_status();
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
}
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
@@ -4060,6 +4045,7 @@ void DBImpl::RemoveManualCompaction(DBImpl::ManualCompactionState* m) {
|
||||
++it;
|
||||
}
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool DBImpl::ShouldntRunManualCompaction(ManualCompactionState* m) {
|
||||
@@ -4356,7 +4342,6 @@ Status DBImpl::WaitForCompact(
|
||||
}
|
||||
if ((bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ || unscheduled_compactions_ ||
|
||||
(wait_for_compact_options.wait_for_purge && bg_purge_scheduled_) ||
|
||||
unscheduled_flushes_ || error_handler_.IsRecoveryInProgress()) &&
|
||||
(error_handler_.GetBGError().ok())) {
|
||||
if (wait_for_compact_options.timeout.count()) {
|
||||
@@ -4364,7 +4349,6 @@ Status DBImpl::WaitForCompact(
|
||||
return Status::TimedOut();
|
||||
}
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::WaitForCompact:InsideLoop");
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
} else if (wait_for_compact_options.close_db) {
|
||||
|
||||
@@ -208,11 +208,11 @@ void DBImpl::TEST_SignalAllBgCv() { bg_cv_.SignalAll(); }
|
||||
void* DBImpl::TEST_BeginWrite() {
|
||||
auto w = new WriteThread::Writer();
|
||||
write_thread_.EnterUnbatched(w, &mutex_);
|
||||
return static_cast<void*>(w);
|
||||
return reinterpret_cast<void*>(w);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_EndWrite(void* w) {
|
||||
auto writer = static_cast<WriteThread::Writer*>(w);
|
||||
auto writer = reinterpret_cast<WriteThread::Writer*>(w);
|
||||
write_thread_.ExitUnbatched(writer);
|
||||
delete writer;
|
||||
}
|
||||
@@ -314,11 +314,6 @@ const autovector<uint64_t>& DBImpl::TEST_GetFilesToQuarantine() const {
|
||||
return error_handler_.GetFilesToQuarantine();
|
||||
}
|
||||
|
||||
void DBImpl::TEST_DeleteObsoleteFiles() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
DeleteObsoleteFiles();
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_EstimateInMemoryStatsHistorySize() const {
|
||||
InstrumentedMutexLock l(&const_cast<DBImpl*>(this)->stats_history_mutex_);
|
||||
return EstimateInMemoryStatsHistorySize();
|
||||
|
||||
@@ -104,9 +104,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
if (i == 0) continue;
|
||||
auto prev_f = l0_files[i - 1];
|
||||
if (icmp->Compare(prev_f->largest, f->smallest) >= 0) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
@@ -150,9 +148,9 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, job_context.superversion_contexts.data(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
} // lock released here
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
+59
-62
@@ -28,8 +28,6 @@ uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
return versions_->min_log_number_to_keep();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToRecycle() { return min_log_number_to_recycle_; }
|
||||
|
||||
uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
mutex_.AssertHeld();
|
||||
if (!pending_outputs_.empty()) {
|
||||
@@ -54,7 +52,7 @@ Status DBImpl::DisableFileDeletions() {
|
||||
if (my_disable_delete_obsolete_files == 1) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Disabled");
|
||||
} else {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
my_disable_delete_obsolete_files);
|
||||
}
|
||||
@@ -69,14 +67,17 @@ Status DBImpl::DisableFileDeletionsWithLock() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::EnableFileDeletions() {
|
||||
Status DBImpl::EnableFileDeletions(bool force) {
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
int saved_counter; // initialize on all paths
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (disable_delete_obsolete_files_ > 0) {
|
||||
if (force) {
|
||||
// if force, we need to enable file deletions right away
|
||||
disable_delete_obsolete_files_ = 0;
|
||||
} else if (disable_delete_obsolete_files_ > 0) {
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
saved_counter = disable_delete_obsolete_files_;
|
||||
@@ -91,7 +92,7 @@ Status DBImpl::EnableFileDeletions() {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
saved_counter);
|
||||
}
|
||||
@@ -177,10 +178,31 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
versions_->AddLiveFiles(&job_context->sst_live, &job_context->blob_live);
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
// PurgeObsoleteFiles will dedupe duplicate files.
|
||||
std::set<std::string> paths;
|
||||
for (size_t path_id = 0; path_id < immutable_db_options_.db_paths.size();
|
||||
path_id++) {
|
||||
paths.insert(immutable_db_options_.db_paths[path_id].path);
|
||||
}
|
||||
|
||||
// Note that if cf_paths is not specified in the ColumnFamilyOptions
|
||||
// of a particular column family, we use db_paths as the cf_paths
|
||||
// setting. Hence, there can be multiple duplicates of files from db_paths
|
||||
// in the following code. The duplicate are removed while identifying
|
||||
// unique files in PurgeObsoleteFiles.
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (size_t path_id = 0; path_id < cfd->ioptions()->cf_paths.size();
|
||||
path_id++) {
|
||||
auto& path = cfd->ioptions()->cf_paths[path_id].path;
|
||||
|
||||
if (paths.find(path) == paths.end()) {
|
||||
paths.insert(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IOOptions io_opts;
|
||||
io_opts.do_not_recurse = true;
|
||||
for (auto& path : CollectAllDBPaths()) {
|
||||
for (auto& path : paths) {
|
||||
// set of all files in the directory. We'll exclude files that are still
|
||||
// alive in the subsequent processings.
|
||||
std::vector<std::string> files;
|
||||
@@ -279,8 +301,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
while (alive_log_files_.begin()->number < min_log_number) {
|
||||
auto& earliest = *alive_log_files_.begin();
|
||||
if (immutable_db_options_.recycle_log_file_num >
|
||||
log_recycle_files_.size() &&
|
||||
earliest.number >= MinLogNumberToRecycle()) {
|
||||
log_recycle_files_.size()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"adding log %" PRIu64 " to recycle list\n",
|
||||
earliest.number);
|
||||
@@ -312,26 +333,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();
|
||||
}
|
||||
@@ -430,24 +431,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);
|
||||
if (handle == nullptr) {
|
||||
// For files not "pinned" in table cache
|
||||
handle = TableCache::Lookup(table_cache_.get(), number);
|
||||
}
|
||||
if (handle) {
|
||||
TableCache::ReleaseObsolete(table_cache_.get(), 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();
|
||||
}
|
||||
@@ -523,7 +512,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -609,6 +598,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) {
|
||||
@@ -975,26 +966,28 @@ Status DBImpl::SetupDBId(const WriteOptions& write_options, bool read_only,
|
||||
return s;
|
||||
}
|
||||
|
||||
std::set<std::string> DBImpl::CollectAllDBPaths() {
|
||||
std::set<std::string> all_db_paths;
|
||||
all_db_paths.insert(NormalizePath(dbname_));
|
||||
Status DBImpl::DeleteUnreferencedSstFiles(RecoveryContext* recovery_ctx) {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<std::string> paths;
|
||||
paths.push_back(NormalizePath(dbname_ + std::string(1, kFilePathSeparator)));
|
||||
for (const auto& db_path : immutable_db_options_.db_paths) {
|
||||
all_db_paths.insert(NormalizePath(db_path.path));
|
||||
paths.push_back(
|
||||
NormalizePath(db_path.path + std::string(1, kFilePathSeparator)));
|
||||
}
|
||||
for (const auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (const auto& cf_path : cfd->ioptions()->cf_paths) {
|
||||
all_db_paths.insert(NormalizePath(cf_path.path));
|
||||
paths.push_back(
|
||||
NormalizePath(cf_path.path + std::string(1, kFilePathSeparator)));
|
||||
}
|
||||
}
|
||||
return all_db_paths;
|
||||
}
|
||||
// Dedup paths
|
||||
std::sort(paths.begin(), paths.end());
|
||||
paths.erase(std::unique(paths.begin(), paths.end()), paths.end());
|
||||
|
||||
Status DBImpl::MaybeUpdateNextFileNumber(RecoveryContext* recovery_ctx) {
|
||||
mutex_.AssertHeld();
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
uint64_t largest_file_number = next_file_number;
|
||||
Status s;
|
||||
for (const auto& path : CollectAllDBPaths()) {
|
||||
for (const auto& path : paths) {
|
||||
std::vector<std::string> files;
|
||||
s = env_->GetChildren(path, &files);
|
||||
if (!s.ok()) {
|
||||
@@ -1006,10 +999,13 @@ Status DBImpl::MaybeUpdateNextFileNumber(RecoveryContext* recovery_ctx) {
|
||||
if (!ParseFileName(fname, &number, &type)) {
|
||||
continue;
|
||||
}
|
||||
const std::string normalized_fpath = path + kFilePathSeparator + fname;
|
||||
// path ends with '/' or '\\'
|
||||
const std::string normalized_fpath = path + fname;
|
||||
largest_file_number = std::max(largest_file_number, number);
|
||||
if ((type == kTableFile || type == kBlobFile)) {
|
||||
recovery_ctx->existing_data_files_.push_back(normalized_fpath);
|
||||
if (type == kTableFile && number >= next_file_number &&
|
||||
recovery_ctx->files_to_delete_.find(normalized_fpath) ==
|
||||
recovery_ctx->files_to_delete_.end()) {
|
||||
recovery_ctx->files_to_delete_.emplace(normalized_fpath, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1029,4 +1025,5 @@ Status DBImpl::MaybeUpdateNextFileNumber(RecoveryContext* recovery_ctx) {
|
||||
recovery_ctx->UpdateVersionEdits(default_cfd, edit);
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,348 +0,0 @@
|
||||
// Copyright (c) 2024-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_impl/db_impl_follower.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "env/fs_on_demand.h"
|
||||
#include "logging/auto_roll_logger.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "rocksdb/configurable.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/write_batch_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
DBImplFollower::DBImplFollower(const DBOptions& db_options,
|
||||
std::unique_ptr<Env>&& env,
|
||||
const std::string& dbname, std::string src_path)
|
||||
: DBImplSecondary(db_options, dbname, ""),
|
||||
env_guard_(std::move(env)),
|
||||
stop_requested_(false),
|
||||
src_path_(std::move(src_path)),
|
||||
cv_(&mu_) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Opening the db in follower mode");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
DBImplFollower::~DBImplFollower() {
|
||||
Status s = Close();
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Error closing DB : %s",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Recover a follower DB instance by reading the MANIFEST. The verification
|
||||
// as part of the MANIFEST replay will ensure that local links to the
|
||||
// leader's files are created, thus ensuring we can continue reading them
|
||||
// even if the leader deletes those files due to compaction.
|
||||
// TODO:
|
||||
// 1. Devise a mechanism to prevent misconfiguration by, for example,
|
||||
// keeping a local copy of the IDENTITY file and cross checking
|
||||
// 2. Make the recovery more robust by retrying if the first attempt
|
||||
// fails.
|
||||
Status DBImplFollower::Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool /*readonly*/, bool /*error_if_wal_file_exists*/,
|
||||
bool /*error_if_data_exists_in_wals*/, bool /*is_retry*/, uint64_t*,
|
||||
RecoveryContext* /*recovery_ctx*/, bool* /*can_retry*/) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
JobContext job_context(0);
|
||||
Status s;
|
||||
s = static_cast<ReactiveVersionSet*>(versions_.get())
|
||||
->Recover(column_families, &manifest_reader_, &manifest_reporter_,
|
||||
&manifest_reader_status_);
|
||||
if (!s.ok()) {
|
||||
if (manifest_reader_status_) {
|
||||
manifest_reader_status_->PermitUncheckedError();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
if (s.ok()) {
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), this, &mutex_);
|
||||
default_cf_internal_stats_ = default_cf_handle_->cfd()->internal_stats();
|
||||
|
||||
// Start the periodic catch-up thread
|
||||
// TODO: See if it makes sense to have a threadpool, rather than a thread
|
||||
// per follower DB instance
|
||||
catch_up_thread_.reset(
|
||||
new port::Thread(&DBImplFollower::PeriodicRefresh, this));
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// Try to catch up by tailing the MANIFEST.
|
||||
// TODO:
|
||||
// 1. Cleanup obsolete files afterward
|
||||
// 2. Add some error notifications and statistics
|
||||
Status DBImplFollower::TryCatchUpWithLeader() {
|
||||
assert(versions_.get() != nullptr);
|
||||
assert(manifest_reader_.get() != nullptr);
|
||||
Status s;
|
||||
|
||||
TEST_SYNC_POINT("DBImplFollower::TryCatchupWithLeader:Begin1");
|
||||
TEST_SYNC_POINT("DBImplFollower::TryCatchupWithLeader:Begin2");
|
||||
// read the manifest and apply new changes to the follower instance
|
||||
std::unordered_set<ColumnFamilyData*> cfds_changed;
|
||||
JobContext job_context(0, true /*create_superversion*/);
|
||||
{
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
std::vector<std::string> files_to_delete;
|
||||
s = static_cast_with_check<ReactiveVersionSet>(versions_.get())
|
||||
->ReadAndApply(&mutex_, &manifest_reader_,
|
||||
manifest_reader_status_.get(), &cfds_changed,
|
||||
&files_to_delete);
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem_);
|
||||
pending_outputs_inserted_elem_.reset(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Last sequence is %" PRIu64,
|
||||
static_cast<uint64_t>(versions_->LastSequence()));
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log, "Next file number is %" PRIu64,
|
||||
static_cast<uint64_t>(versions_->current_next_file_number()));
|
||||
for (ColumnFamilyData* cfd : cfds_changed) {
|
||||
if (cfd->IsDropped()) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] is dropped\n",
|
||||
cfd->GetName().c_str());
|
||||
continue;
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] Level summary: %s\n", cfd->GetName().c_str(),
|
||||
cfd->current()->storage_info()->LevelSummary(&tmp));
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : cfds_changed) {
|
||||
if (cfd->mem()->GetEarliestSequenceNumber() <
|
||||
versions_->LastSequence()) {
|
||||
// Construct a new memtable with earliest sequence number set to the
|
||||
// last sequence number in the VersionSet. This matters when
|
||||
// DBImpl::MultiCFSnapshot tries to get consistent references
|
||||
// to super versions in a lock free manner, it checks the earliest
|
||||
// sequence number to detect if there was a change in version in
|
||||
// the meantime.
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MemTable* new_mem = cfd->ConstructNewMemtable(
|
||||
mutable_cf_options, versions_->LastSequence());
|
||||
cfd->mem()->SetNextLogNumber(cfd->GetLogNumber());
|
||||
cfd->mem()->ConstructFragmentedRangeTombstones();
|
||||
cfd->imm()->Add(cfd->mem(), &job_context.memtables_to_free);
|
||||
new_mem->Ref();
|
||||
cfd->SetMemtable(new_mem);
|
||||
}
|
||||
|
||||
// This will check if the old memtable is still referenced
|
||||
cfd->imm()->RemoveOldMemTables(cfd->GetLogNumber(),
|
||||
&job_context.memtables_to_free);
|
||||
auto& sv_context = job_context.superversion_contexts.back();
|
||||
cfd->InstallSuperVersion(&sv_context, &mutex_);
|
||||
sv_context.NewSuperVersion();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& file : files_to_delete) {
|
||||
IOStatus io_s = fs_->DeleteFile(file, IOOptions(), nullptr);
|
||||
if (!io_s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Cannot delete file %s: %s", file.c_str(),
|
||||
io_s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
job_context.Clean();
|
||||
|
||||
// Cleanup unused, obsolete files.
|
||||
JobContext purge_files_job_context(0);
|
||||
{
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
// Currently, follower instance does not create any database files, thus
|
||||
// is unnecessary for the follower to force full scan.
|
||||
FindObsoleteFiles(&purge_files_job_context, /*force=*/false);
|
||||
}
|
||||
if (purge_files_job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(purge_files_job_context);
|
||||
}
|
||||
purge_files_job_context.Clean();
|
||||
|
||||
TEST_SYNC_POINT("DBImplFollower::TryCatchupWithLeader:End");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBImplFollower::PeriodicRefresh() {
|
||||
while (!stop_requested_.load()) {
|
||||
MutexLock l(&mu_);
|
||||
int64_t wait_until =
|
||||
immutable_db_options_.clock->NowMicros() +
|
||||
immutable_db_options_.follower_refresh_catchup_period_ms * 1000;
|
||||
immutable_db_options_.clock->TimedWait(
|
||||
&cv_, std::chrono::microseconds(wait_until));
|
||||
if (stop_requested_.load()) {
|
||||
break;
|
||||
}
|
||||
Status s;
|
||||
for (uint64_t i = 0;
|
||||
i < immutable_db_options_.follower_catchup_retry_count &&
|
||||
!stop_requested_.load();
|
||||
++i) {
|
||||
s = TryCatchUpWithLeader();
|
||||
|
||||
if (s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Successful catch up on attempt %llu",
|
||||
static_cast<unsigned long long>(i));
|
||||
break;
|
||||
}
|
||||
wait_until = immutable_db_options_.clock->NowMicros() +
|
||||
immutable_db_options_.follower_catchup_retry_wait_ms * 1000;
|
||||
immutable_db_options_.clock->TimedWait(
|
||||
&cv_, std::chrono::microseconds(wait_until));
|
||||
}
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Catch up unsuccessful");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImplFollower::Close() {
|
||||
if (catch_up_thread_) {
|
||||
stop_requested_.store(true);
|
||||
{
|
||||
MutexLock l(&mu_);
|
||||
cv_.SignalAll();
|
||||
}
|
||||
catch_up_thread_->join();
|
||||
catch_up_thread_.reset();
|
||||
}
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem_);
|
||||
|
||||
return DBImpl::Close();
|
||||
}
|
||||
|
||||
Status DB::OpenAsFollower(const Options& options, const std::string& dbname,
|
||||
const std::string& leader_path,
|
||||
std::unique_ptr<DB>* dbptr) {
|
||||
dbptr->reset();
|
||||
|
||||
DBOptions db_options(options);
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
Status s = DB::OpenAsFollower(db_options, dbname, leader_path,
|
||||
column_families, &handles, dbptr);
|
||||
if (s.ok()) {
|
||||
assert(handles.size() == 1);
|
||||
delete handles[0];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DB::OpenAsFollower(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::string& src_path,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, std::unique_ptr<DB>* dbptr) {
|
||||
dbptr->reset();
|
||||
|
||||
FileSystem* fs = db_options.env->GetFileSystem().get();
|
||||
{
|
||||
IOStatus io_s;
|
||||
if (db_options.create_if_missing) {
|
||||
io_s = fs->CreateDirIfMissing(dbname, IOOptions(), nullptr);
|
||||
} else {
|
||||
io_s = fs->FileExists(dbname, IOOptions(), nullptr);
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
return static_cast<Status>(io_s);
|
||||
}
|
||||
}
|
||||
std::unique_ptr<Env> new_env(new CompositeEnvWrapper(
|
||||
db_options.env, NewOnDemandFileSystem(db_options.env->GetFileSystem(),
|
||||
src_path, dbname)));
|
||||
|
||||
DBOptions tmp_opts(db_options);
|
||||
Status s;
|
||||
tmp_opts.env = new_env.get();
|
||||
if (nullptr == tmp_opts.info_log) {
|
||||
s = CreateLoggerFromOptions(dbname, tmp_opts, &tmp_opts.info_log);
|
||||
if (!s.ok()) {
|
||||
tmp_opts.info_log = nullptr;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
handles->clear();
|
||||
DBImplFollower* impl =
|
||||
new DBImplFollower(tmp_opts, std::move(new_env), dbname, src_path);
|
||||
impl->versions_.reset(new ReactiveVersionSet(
|
||||
dbname, &impl->immutable_db_options_, impl->file_options_,
|
||||
impl->table_cache_.get(), impl->write_buffer_manager_,
|
||||
&impl->write_controller_, impl->io_tracer_));
|
||||
impl->column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
|
||||
impl->wal_in_db_path_ = impl->immutable_db_options_.IsWalDirSameAsDBPath();
|
||||
|
||||
impl->mutex_.Lock();
|
||||
s = impl->Recover(column_families, /*read_only=*/true,
|
||||
/*error_if_wal_file_exists=*/false,
|
||||
/*error_if_data_exists_in_wals=*/false);
|
||||
if (s.ok()) {
|
||||
for (const auto& cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (nullptr == cfd) {
|
||||
s = Status::InvalidArgument("Column family not found", cf.name);
|
||||
break;
|
||||
}
|
||||
handles->push_back(new ColumnFamilyHandleImpl(cfd, impl, &impl->mutex_));
|
||||
}
|
||||
}
|
||||
SuperVersionContext sv_context(false /* create_superversion */);
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
sv_context.NewSuperVersion();
|
||||
cfd->InstallSuperVersion(&sv_context, &impl->mutex_);
|
||||
}
|
||||
}
|
||||
impl->mutex_.Unlock();
|
||||
sv_context.Clean();
|
||||
if (s.ok()) {
|
||||
dbptr->reset(impl);
|
||||
for (auto h : *handles) {
|
||||
impl->NewThreadStatusCfInfo(
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(h)->cfd());
|
||||
}
|
||||
} else {
|
||||
for (auto h : *handles) {
|
||||
delete h;
|
||||
}
|
||||
handles->clear();
|
||||
delete impl;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (c) 2024-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_impl/db_impl_secondary.h"
|
||||
#include "logging/logging.h"
|
||||
#include "port/port.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBImplFollower : public DBImplSecondary {
|
||||
public:
|
||||
DBImplFollower(const DBOptions& db_options, std::unique_ptr<Env>&& env,
|
||||
const std::string& dbname, std::string src_path);
|
||||
~DBImplFollower();
|
||||
|
||||
Status Close() override;
|
||||
|
||||
protected:
|
||||
bool OwnTablesAndLogs() const override {
|
||||
// TODO: Change this to true once we've properly implemented file
|
||||
// deletion for the read scaling case
|
||||
return true;
|
||||
}
|
||||
|
||||
Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool /*readonly*/, bool /*error_if_wal_file_exists*/,
|
||||
bool /*error_if_data_exists_in_wals*/,
|
||||
bool /*is_retry*/ = false, uint64_t* = nullptr,
|
||||
RecoveryContext* /*recovery_ctx*/ = nullptr,
|
||||
bool* /*can_retry*/ = nullptr) override;
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
Status TryCatchUpWithLeader();
|
||||
void PeriodicRefresh();
|
||||
|
||||
std::unique_ptr<Env> env_guard_;
|
||||
std::unique_ptr<port::Thread> catch_up_thread_;
|
||||
std::atomic<bool> stop_requested_;
|
||||
std::string src_path_;
|
||||
port::Mutex mu_;
|
||||
port::CondVar cv_;
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> pending_outputs_inserted_elem_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+135
-156
@@ -104,6 +104,7 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
if (result.recycle_log_file_num &&
|
||||
(result.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords ||
|
||||
result.wal_recovery_mode == WALRecoveryMode::kPointInTimeRecovery ||
|
||||
result.wal_recovery_mode == WALRecoveryMode::kAbsoluteConsistency)) {
|
||||
// - kTolerateCorruptedTailRecords is inconsistent with recycle log file
|
||||
// feature. WAL recycling expects recovery success upon encountering a
|
||||
@@ -175,6 +176,15 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
}
|
||||
}
|
||||
}
|
||||
// When the DB is stopped, it's possible that there are some .trash files that
|
||||
// were not deleted yet, when we open the DB we will find these .trash files
|
||||
// and schedule them to be deleted (or delete immediately if SstFileManager
|
||||
// was not used)
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(result.sst_file_manager.get());
|
||||
for (size_t i = 0; i < result.db_paths.size(); i++) {
|
||||
DeleteScheduler::CleanupDirectory(result.env, sfm, result.db_paths[i].path)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
// Create a default SstFileManager for purposes of tracking compaction size
|
||||
// and facilitating recovery from out of space errors.
|
||||
@@ -271,6 +281,12 @@ Status DBImpl::ValidateOptions(const DBOptions& db_options) {
|
||||
"atomic_flush is incompatible with enable_pipelined_write");
|
||||
}
|
||||
|
||||
// TODO remove this restriction
|
||||
if (db_options.atomic_flush && db_options.best_efforts_recovery) {
|
||||
return Status::InvalidArgument(
|
||||
"atomic_flush is currently incompatible with best-efforts recovery");
|
||||
}
|
||||
|
||||
if (db_options.use_direct_io_for_flush_and_compaction &&
|
||||
0 == db_options.writable_file_max_buffer_size) {
|
||||
return Status::InvalidArgument(
|
||||
@@ -301,10 +317,7 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
}
|
||||
if (immutable_db_options_.write_dbid_to_manifest) {
|
||||
std::string temp_db_id;
|
||||
s = GetDbIdentityFromIdentityFile(&temp_db_id);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
GetDbIdentityFromIdentityFile(&temp_db_id);
|
||||
new_db.SetDBId(temp_db_id);
|
||||
}
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -407,8 +420,7 @@ IOStatus Directories::SetDirectories(FileSystem* fs, const std::string& dbname,
|
||||
Status DBImpl::Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families, bool read_only,
|
||||
bool error_if_wal_file_exists, bool error_if_data_exists_in_wals,
|
||||
bool is_retry, uint64_t* recovered_seq, RecoveryContext* recovery_ctx,
|
||||
bool* can_retry) {
|
||||
uint64_t* recovered_seq, RecoveryContext* recovery_ctx) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
const WriteOptions write_options(Env::IOActivity::kDBOpen);
|
||||
@@ -524,31 +536,7 @@ Status DBImpl::Recover(
|
||||
Status s;
|
||||
bool missing_table_file = false;
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
// Status of reading the descriptor file
|
||||
Status desc_status;
|
||||
s = versions_->Recover(column_families, read_only, &db_id_,
|
||||
/*no_error_if_files_missing=*/false, is_retry,
|
||||
&desc_status);
|
||||
desc_status.PermitUncheckedError();
|
||||
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
|
||||
// detecting a corrupt record, or SST files not found error due to
|
||||
// discarding badly formed tail records)
|
||||
if (!is_retry &&
|
||||
(desc_status.IsCorruption() || s.IsNotFound() || s.IsCorruption()) &&
|
||||
CheckFSFeatureSupport(fs_.get(),
|
||||
FSSupportedOps::kVerifyAndReconstructRead)) {
|
||||
*can_retry = true;
|
||||
ROCKS_LOG_ERROR(
|
||||
immutable_db_options_.info_log,
|
||||
"Possible corruption detected while replaying MANIFEST %s, %s. "
|
||||
"Will be retried.",
|
||||
desc_status.ToString().c_str(), s.ToString().c_str());
|
||||
} else {
|
||||
*can_retry = false;
|
||||
}
|
||||
}
|
||||
s = versions_->Recover(column_families, read_only, &db_id_);
|
||||
} else {
|
||||
assert(!files_in_dbname.empty());
|
||||
s = versions_->TryRecover(column_families, read_only, files_in_dbname,
|
||||
@@ -663,7 +651,7 @@ Status DBImpl::Recover(
|
||||
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);
|
||||
s = DeleteUnreferencedSstFiles(recovery_ctx);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
@@ -786,8 +774,8 @@ Status DBImpl::Recover(
|
||||
std::sort(wals.begin(), wals.end());
|
||||
|
||||
bool corrupted_wal_found = false;
|
||||
s = RecoverLogFiles(wals, &next_sequence, read_only, is_retry,
|
||||
&corrupted_wal_found, recovery_ctx);
|
||||
s = RecoverLogFiles(wals, &next_sequence, read_only, &corrupted_wal_found,
|
||||
recovery_ctx);
|
||||
if (corrupted_wal_found && recovered_seq != nullptr) {
|
||||
*recovered_seq = next_sequence;
|
||||
}
|
||||
@@ -965,6 +953,19 @@ Status DBImpl::LogAndApplyForRecovery(const RecoveryContext& recovery_ctx) {
|
||||
recovery_ctx.mutable_cf_opts_, read_options,
|
||||
write_options, recovery_ctx.edit_lists_,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
if (s.ok() && !(recovery_ctx.files_to_delete_.empty())) {
|
||||
mutex_.Unlock();
|
||||
for (const auto& stale_sst_file : recovery_ctx.files_to_delete_) {
|
||||
s = DeleteDBFile(&immutable_db_options_, stale_sst_file.first,
|
||||
stale_sst_file.second,
|
||||
/*force_bg=*/false,
|
||||
/*force_fg=*/false);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1084,14 +1085,13 @@ bool DBImpl::InvokeWalFilterIfNeededOnWalRecord(uint64_t wal_number,
|
||||
// REQUIRES: wal_numbers are sorted in ascending order
|
||||
Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only,
|
||||
bool is_retry, bool* corrupted_wal_found,
|
||||
bool* corrupted_wal_found,
|
||||
RecoveryContext* recovery_ctx) {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
const char* fname;
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
bool* old_log_record;
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
|
||||
(status == nullptr ? "(ignoring error) " : ""), fname,
|
||||
@@ -1100,19 +1100,10 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
*status = s;
|
||||
}
|
||||
}
|
||||
|
||||
void OldLogRecord(size_t bytes) override {
|
||||
if (old_log_record != nullptr) {
|
||||
*old_log_record = true;
|
||||
}
|
||||
ROCKS_LOG_WARN(info_log, "%s: dropping %d bytes; possibly recycled",
|
||||
fname, static_cast<int>(bytes));
|
||||
}
|
||||
};
|
||||
|
||||
mutex_.AssertHeld();
|
||||
Status status;
|
||||
bool old_log_record = false;
|
||||
std::unordered_map<int, VersionEdit> version_edits;
|
||||
// no need to refcount because iteration is under mutex
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -1195,7 +1186,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
file_reader.reset(new SequentialFileReader(
|
||||
std::move(file), fname, immutable_db_options_.log_readahead_size,
|
||||
io_tracer_, /*listeners=*/{}, /*rate_limiter=*/nullptr, is_retry));
|
||||
io_tracer_));
|
||||
}
|
||||
|
||||
// Create the log reader.
|
||||
@@ -1203,7 +1194,6 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
reporter.env = env_;
|
||||
reporter.info_log = immutable_db_options_.info_log.get();
|
||||
reporter.fname = fname.c_str();
|
||||
reporter.old_log_record = &old_log_record;
|
||||
if (!immutable_db_options_.paranoid_checks ||
|
||||
immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kSkipAnyCorruptedRecords) {
|
||||
@@ -1351,7 +1341,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
}
|
||||
|
||||
if (!status.ok() || old_log_record) {
|
||||
if (!status.ok()) {
|
||||
if (status.IsNotSupported()) {
|
||||
// We should not treat NotSupported as corruption. It is rather a clear
|
||||
// sign that we are processing a WAL that is produced by an incompatible
|
||||
@@ -1376,7 +1366,6 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
// We should ignore the error but not continue replaying
|
||||
status = Status::OK();
|
||||
old_log_record = false;
|
||||
stop_replay_for_corruption = true;
|
||||
corrupted_wal_number = wal_number;
|
||||
if (corrupted_wal_found != nullptr) {
|
||||
@@ -1647,8 +1636,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
Status s;
|
||||
TableProperties table_properties;
|
||||
{
|
||||
ScopedArenaPtr<InternalIterator> iter(
|
||||
mem->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena));
|
||||
ScopedArenaIterator iter(mem->NewIterator(ro, &arena));
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
" Level-0 table #%" PRIu64 ": started",
|
||||
@@ -1673,8 +1661,6 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
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();
|
||||
@@ -1694,31 +1680,30 @@ 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(),
|
||||
cfd->internal_comparator(), cfd->int_tbl_prop_collector_factories(),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
mutable_cf_options.compression_opts, cfd->GetID(), cfd->GetName(),
|
||||
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(), kMaxSequenceNumber);
|
||||
0 /* target_file_size */, meta.fd.GetNumber());
|
||||
SeqnoToTimeMapping empty_seqno_to_time_mapping;
|
||||
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,
|
||||
empty_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,
|
||||
@@ -1814,9 +1799,11 @@ Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) {
|
||||
DBOptions db_options(options);
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
if (db_options.persist_stats_to_disk) {
|
||||
column_families.emplace_back(kPersistentStatsColumnFamilyName, cf_options);
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kPersistentStatsColumnFamilyName, cf_options));
|
||||
}
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
Status s = DB::Open(db_options, dbname, column_families, &handles, dbptr);
|
||||
@@ -1843,12 +1830,8 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
const bool kBatchPerTxn = true;
|
||||
ThreadStatusUtil::SetEnableTracking(db_options.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OperationType::OP_DBOPEN);
|
||||
bool can_retry = false;
|
||||
Status s;
|
||||
do {
|
||||
s = DBImpl::Open(db_options, dbname, column_families, handles, dbptr,
|
||||
!kSeqPerBatch, kBatchPerTxn, can_retry, &can_retry);
|
||||
} while (!s.ok() && can_retry);
|
||||
Status s = DBImpl::Open(db_options, dbname, column_families, handles, dbptr,
|
||||
!kSeqPerBatch, kBatchPerTxn);
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
return s;
|
||||
}
|
||||
@@ -1967,55 +1950,10 @@ IOStatus DBImpl::CreateWAL(const WriteOptions& write_options,
|
||||
return io_s;
|
||||
}
|
||||
|
||||
void DBImpl::TrackExistingDataFiles(
|
||||
const std::vector<std::string>& existing_data_files) {
|
||||
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,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
const bool seq_per_batch, const bool batch_per_txn,
|
||||
const bool is_retry, bool* can_retry) {
|
||||
const bool seq_per_batch, const bool batch_per_txn) {
|
||||
const WriteOptions write_options(Env::IOActivity::kDBOpen);
|
||||
const ReadOptions read_options(Env::IOActivity::kDBOpen);
|
||||
|
||||
@@ -2034,7 +1972,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
handles->clear();
|
||||
|
||||
size_t max_write_buffer_size = 0;
|
||||
for (const auto& cf : column_families) {
|
||||
for (auto cf : column_families) {
|
||||
max_write_buffer_size =
|
||||
std::max(max_write_buffer_size, cf.options.write_buffer_size);
|
||||
}
|
||||
@@ -2058,7 +1996,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
paths.emplace_back(cf_path.path);
|
||||
}
|
||||
}
|
||||
for (const auto& path : paths) {
|
||||
for (auto& path : paths) {
|
||||
s = impl->env_->CreateDirIfMissing(path);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
@@ -2088,8 +2026,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
uint64_t recovered_seq(kMaxSequenceNumber);
|
||||
s = impl->Recover(column_families, false /* read_only */,
|
||||
false /* error_if_wal_file_exists */,
|
||||
false /* error_if_data_exists_in_wals */, is_retry,
|
||||
&recovered_seq, &recovery_ctx, can_retry);
|
||||
false /* error_if_data_exists_in_wals */, &recovered_seq,
|
||||
&recovery_ctx);
|
||||
if (s.ok()) {
|
||||
uint64_t new_log_number = impl->versions_->NewFileNumber();
|
||||
log::Writer* new_log = nullptr;
|
||||
@@ -2097,11 +2035,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
impl->GetWalPreallocateBlockSize(max_write_buffer_size);
|
||||
s = impl->CreateWAL(write_options, new_log_number, 0 /*recycle_log_number*/,
|
||||
preallocate_block_size, &new_log);
|
||||
if (s.ok()) {
|
||||
// Prevent log files created by previous instance from being recycled.
|
||||
// They might be in alive_log_file_, and might get recycled otherwise.
|
||||
impl->min_log_number_to_recycle_ = new_log_number;
|
||||
}
|
||||
if (s.ok()) {
|
||||
InstrumentedMutexLock wl(&impl->log_write_mutex_);
|
||||
impl->logfile_number_ = new_log_number;
|
||||
@@ -2111,7 +2044,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
impl->alive_log_files_.emplace_back(impl->logfile_number_);
|
||||
impl->alive_log_files_.push_back(
|
||||
DBImpl::LogFileNumberSize(impl->logfile_number_));
|
||||
// In WritePrepared there could be gap in sequence numbers. This breaks
|
||||
// the trick we use in kPointInTimeRecovery which assumes the first seq in
|
||||
// the log right after the corrupted log is one larger than the last seq
|
||||
@@ -2159,7 +2093,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
|
||||
if (s.ok()) {
|
||||
// set column family handles
|
||||
for (const auto& cf : column_families) {
|
||||
for (auto cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (cfd != nullptr) {
|
||||
@@ -2229,6 +2163,9 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
impl->WriteOptionsFile(write_options, true /*db_mutex_already_held*/);
|
||||
*dbptr = impl;
|
||||
impl->opened_successfully_ = true;
|
||||
impl->DeleteObsoleteFiles();
|
||||
TEST_SYNC_POINT("DBImpl::Open:AfterDeleteFiles");
|
||||
impl->MaybeScheduleFlushOrCompaction();
|
||||
} else {
|
||||
persist_options_status.PermitUncheckedError();
|
||||
}
|
||||
@@ -2243,10 +2180,73 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
ROCKS_LOG_INFO(impl->immutable_db_options_.info_log,
|
||||
"SstFileManager instance %p", sfm);
|
||||
|
||||
impl->TrackExistingDataFiles(recovery_ctx.existing_data_files_);
|
||||
// Notify SstFileManager about all sst files that already exist in
|
||||
// db_paths[0] and cf_paths[0] when the DB is opened.
|
||||
|
||||
// SstFileManagerImpl needs to know sizes of the files. For files whose size
|
||||
// we already know (sst files that appear in manifest - typically that's the
|
||||
// vast majority of all files), we'll pass the size to SstFileManager.
|
||||
// For all other files SstFileManager will query the size from filesystem.
|
||||
|
||||
std::vector<ColumnFamilyMetaData> metadata;
|
||||
impl->GetAllColumnFamilyMetaData(&metadata);
|
||||
|
||||
std::unordered_map<std::string, uint64_t> known_file_sizes;
|
||||
for (const auto& md : metadata) {
|
||||
for (const auto& lmd : md.levels) {
|
||||
for (const auto& fmd : lmd.files) {
|
||||
known_file_sizes[fmd.relative_filename] = fmd.size;
|
||||
}
|
||||
}
|
||||
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] == '/') {
|
||||
name = name.substr(1);
|
||||
}
|
||||
known_file_sizes[name] = bmd.blob_file_size;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> paths;
|
||||
paths.emplace_back(impl->immutable_db_options_.db_paths[0].path);
|
||||
for (auto& cf : column_families) {
|
||||
if (!cf.options.cf_paths.empty()) {
|
||||
paths.emplace_back(cf.options.cf_paths[0].path);
|
||||
}
|
||||
}
|
||||
// Remove duplicate paths.
|
||||
std::sort(paths.begin(), paths.end());
|
||||
paths.erase(std::unique(paths.begin(), paths.end()), paths.end());
|
||||
IOOptions io_opts;
|
||||
io_opts.do_not_recurse = true;
|
||||
for (auto& path : paths) {
|
||||
std::vector<std::string> existing_files;
|
||||
impl->immutable_db_options_.fs
|
||||
->GetChildren(path, io_opts, &existing_files,
|
||||
/*IODebugContext*=*/nullptr)
|
||||
.PermitUncheckedError(); //**TODO: What do to on error?
|
||||
for (auto& file_name : existing_files) {
|
||||
uint64_t file_number;
|
||||
FileType file_type;
|
||||
std::string file_path = path + "/" + file_name;
|
||||
if (ParseFileName(file_name, &file_number, &file_type) &&
|
||||
(file_type == kTableFile || file_type == kBlobFile)) {
|
||||
// TODO: Check for errors from OnAddFile?
|
||||
if (known_file_sizes.count(file_name)) {
|
||||
// We're assuming that each sst file name exists in at most one of
|
||||
// the paths.
|
||||
sfm->OnAddFile(file_path, known_file_sizes.at(file_name))
|
||||
.PermitUncheckedError();
|
||||
} else {
|
||||
sfm->OnAddFile(file_path).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve some disk buffer space. This is a heuristic - when we run out
|
||||
// of disk space, this ensures that there is at least write_buffer_size
|
||||
// of disk space, this ensures that there is atleast write_buffer_size
|
||||
// amount of free space before we resume DB writes. In low disk space
|
||||
// conditions, we want to avoid a lot of small L0 files due to frequent
|
||||
// WAL write failures and resultant forced flushes
|
||||
@@ -2254,27 +2254,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
impl->immutable_db_options_.db_paths[0].path);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
// When the DB is stopped, it's possible that there are some .trash files
|
||||
// that were not deleted yet, when we open the DB we will find these .trash
|
||||
// files and schedule them to be deleted (or delete immediately if
|
||||
// SstFileManager was not used).
|
||||
// Note that we only start doing this and below delete obsolete file after
|
||||
// `TrackExistingDataFiles` are called, the `max_trash_db_ratio` is
|
||||
// ineffective otherwise and these files' deletion won't be rate limited
|
||||
// which can cause discard stall.
|
||||
for (const auto& path : impl->CollectAllDBPaths()) {
|
||||
DeleteScheduler::CleanupDirectory(impl->immutable_db_options_.env, sfm,
|
||||
path)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
impl->mutex_.Lock();
|
||||
// This will do a full scan.
|
||||
impl->DeleteObsoleteFiles();
|
||||
TEST_SYNC_POINT("DBImpl::Open:AfterDeleteFiles");
|
||||
impl->MaybeScheduleFlushOrCompaction();
|
||||
impl->mutex_.Unlock();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
ROCKS_LOG_HEADER(impl->immutable_db_options_.info_log, "DB pointer %p",
|
||||
|
||||
@@ -26,7 +26,7 @@ DBImplReadOnly::DBImplReadOnly(const DBOptions& db_options,
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
DBImplReadOnly::~DBImplReadOnly() = default;
|
||||
DBImplReadOnly::~DBImplReadOnly() {}
|
||||
|
||||
// Implementations of the DB interface
|
||||
Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
|
||||
@@ -169,7 +169,8 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& _read_options,
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
SequenceNumber read_seq =
|
||||
read_options.snapshot != nullptr
|
||||
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot;
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
@@ -215,7 +216,8 @@ Status DBImplReadOnly::NewIterators(
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
SequenceNumber read_seq =
|
||||
read_options.snapshot != nullptr
|
||||
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot;
|
||||
|
||||
autovector<std::tuple<ColumnFamilyData*, SuperVersion*>> cfd_to_sv;
|
||||
@@ -293,7 +295,8 @@ Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
DBOptions db_options(options);
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
s = DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
@@ -338,7 +341,7 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
error_if_wal_file_exists);
|
||||
if (s.ok()) {
|
||||
// set column family handles
|
||||
for (const auto& cf : column_families) {
|
||||
for (auto cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (cfd == nullptr) {
|
||||
|
||||
Loaded 100 of 790 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user