mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec93377878 | ||
|
|
0e1c7f9e62 | ||
|
|
c12d364ad1 | ||
|
|
766ca8f985 | ||
|
|
3c8f8f417d |
No files matched your search
@@ -12,7 +12,6 @@ jobs:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=8gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: facebook/rocksdb/nightly-candidate
|
||||
name: facebook/rocksdb/nightly
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
jobs:
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
labels: arm64large
|
||||
container:
|
||||
image: ubuntu-2004:202111-02
|
||||
options: --shm-size=8gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
TEST_TMPDIR: "/tmp/rocksdb_test_tmp"
|
||||
steps:
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: clang-21
|
||||
CXX: clang++-21
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -161,7 +161,7 @@ jobs:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=8gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -174,7 +174,7 @@ jobs:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=8gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -189,7 +189,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=8gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
labels: 32-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=64gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -180,7 +180,7 @@ jobs:
|
||||
labels: 32-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=64gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -209,7 +209,7 @@ jobs:
|
||||
shard: [0, 1, 2, 3]
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -232,7 +232,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -252,7 +252,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
@@ -306,7 +306,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -373,7 +373,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -394,7 +394,7 @@ jobs:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: gcc:latest
|
||||
options: --shm-size=8gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
@@ -422,7 +422,7 @@ jobs:
|
||||
crash_duration: 240
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=8gb --ulimit nofile=1048576:1048576
|
||||
options: --shm-size=16gb --ulimit nofile=1048576:1048576
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -457,7 +457,7 @@ jobs:
|
||||
shard: [0, 1, 2]
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=64gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -484,7 +484,7 @@ jobs:
|
||||
shard: [0, 1, 2]
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=64gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -507,7 +507,7 @@ jobs:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -614,7 +614,7 @@ jobs:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=8gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -637,7 +637,7 @@ jobs:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=8gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -740,7 +740,7 @@ jobs:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:alpine3_x64-be
|
||||
options: --shm-size=8gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/install-maven"
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
timeout-minutes: 840
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=32gb
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
|
||||
@@ -241,7 +241,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"table/plain/plain_table_index.cc",
|
||||
"table/plain/plain_table_key_coding.cc",
|
||||
"table/plain/plain_table_reader.cc",
|
||||
"table/range_del_block.cc",
|
||||
"table/sst_file_dumper.cc",
|
||||
"table/sst_file_reader.cc",
|
||||
"table/sst_file_writer.cc",
|
||||
@@ -416,27 +415,13 @@ cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
|
||||
"table/mock_table.cc",
|
||||
"test_util/mock_time_env.cc",
|
||||
"test_util/secondary_cache_test_util.cc",
|
||||
"test_util/simple_external_table_factory.cc",
|
||||
"test_util/testharness.cc",
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"utilities/agg_merge/test_agg_merge.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
], deps=[":rocksdb_lib"], headers=[
|
||||
"db/db_test_util.h",
|
||||
"db/db_with_timestamp_test_util.h",
|
||||
"table/mock_table.h",
|
||||
"test_util/mock_time_env.h",
|
||||
"test_util/secondary_cache_test_util.h",
|
||||
"test_util/simple_external_table_factory.h",
|
||||
"test_util/testharness.h",
|
||||
"test_util/testutil.h",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
|
||||
"tools/trace_analyzer_tool.h",
|
||||
"utilities/agg_merge/test_agg_merge.h",
|
||||
"utilities/cassandra/test_utils.h",
|
||||
], link_whole=False, extra_test_libs=True)
|
||||
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=True)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
|
||||
"db/db_test_util.cc",
|
||||
@@ -444,27 +429,13 @@ cpp_library_wrapper(name="rocksdb_with_faiss_test_lib", srcs=[
|
||||
"table/mock_table.cc",
|
||||
"test_util/mock_time_env.cc",
|
||||
"test_util/secondary_cache_test_util.cc",
|
||||
"test_util/simple_external_table_factory.cc",
|
||||
"test_util/testharness.cc",
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"utilities/agg_merge/test_agg_merge.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
], deps=[":rocksdb_with_faiss_lib"], headers=[
|
||||
"db/db_test_util.h",
|
||||
"db/db_with_timestamp_test_util.h",
|
||||
"table/mock_table.h",
|
||||
"test_util/mock_time_env.h",
|
||||
"test_util/secondary_cache_test_util.h",
|
||||
"test_util/simple_external_table_factory.h",
|
||||
"test_util/testharness.h",
|
||||
"test_util/testutil.h",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
|
||||
"tools/trace_analyzer_tool.h",
|
||||
"utilities/agg_merge/test_agg_merge.h",
|
||||
"utilities/cassandra/test_utils.h",
|
||||
], link_whole=False, extra_test_libs=True)
|
||||
], deps=[":rocksdb_with_faiss_lib"], headers=[], link_whole=False, extra_test_libs=True)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
|
||||
"test_util/testutil.cc",
|
||||
@@ -473,12 +444,7 @@ cpp_library_wrapper(name="rocksdb_tools_lib", srcs=[
|
||||
"tools/simulated_hybrid_file_system.cc",
|
||||
"tools/tool_hooks.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
], deps=[":rocksdb_lib"], headers=[
|
||||
"test_util/testutil.h",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
|
||||
"tools/simulated_hybrid_file_system.h",
|
||||
"tools/trace_analyzer_tool.h",
|
||||
], link_whole=False, extra_test_libs=False)
|
||||
], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_cache_bench_tools_lib", srcs=["cache/cache_bench_tool.cc"], deps=[":rocksdb_lib"], headers=[], link_whole=False, extra_test_libs=False)
|
||||
|
||||
@@ -502,28 +468,10 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"db_stress_tool/expected_value.cc",
|
||||
"db_stress_tool/multi_ops_txns_stress.cc",
|
||||
"db_stress_tool/no_batched_ops_stress.cc",
|
||||
"test_util/simple_external_table_factory.cc",
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
], headers=[
|
||||
"db_stress_tool/db_stress_common.h",
|
||||
"db_stress_tool/db_stress_compaction_service.h",
|
||||
"db_stress_tool/db_stress_compression_manager.h",
|
||||
"db_stress_tool/db_stress_driver.h",
|
||||
"db_stress_tool/db_stress_filters.h",
|
||||
"db_stress_tool/db_stress_listener.h",
|
||||
"db_stress_tool/db_stress_shared_state.h",
|
||||
"db_stress_tool/db_stress_test_base.h",
|
||||
"db_stress_tool/db_stress_wide_merge_operator.h",
|
||||
"db_stress_tool/expected_state.h",
|
||||
"db_stress_tool/expected_value.h",
|
||||
"db_stress_tool/multi_ops_txns_stress.h",
|
||||
"test_util/simple_external_table_factory.h",
|
||||
"test_util/testutil.h",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
|
||||
"tools/trace_analyzer_tool.h",
|
||||
])
|
||||
], headers=[])
|
||||
|
||||
|
||||
cpp_binary_wrapper(name="ldb", srcs=["tools/ldb.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
@@ -4813,7 +4761,6 @@ cpp_unittest_wrapper(name="compression_test",
|
||||
|
||||
cpp_unittest_wrapper(name="configurable_test",
|
||||
srcs=["options/configurable_test.cc"],
|
||||
source_headers=["options/configurable_test.h"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
@@ -5518,7 +5465,6 @@ cpp_unittest_wrapper(name="periodic_task_scheduler_test",
|
||||
|
||||
cpp_unittest_wrapper(name="persistent_cache_test",
|
||||
srcs=["utilities/persistent_cache/persistent_cache_test.cc"],
|
||||
source_headers=["utilities/persistent_cache/persistent_cache_test.h"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
@@ -5537,7 +5483,6 @@ cpp_unittest_wrapper(name="point_lock_manager_stress_test",
|
||||
|
||||
cpp_unittest_wrapper(name="point_lock_manager_test",
|
||||
srcs=["utilities/transactions/lock/point/point_lock_manager_test.cc"],
|
||||
source_headers=["utilities/transactions/lock/point/point_lock_manager_test.h"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
@@ -5748,7 +5693,6 @@ cpp_unittest_wrapper(name="trace_analyzer_test",
|
||||
|
||||
cpp_unittest_wrapper(name="transaction_test",
|
||||
srcs=["utilities/transactions/transaction_test.cc"],
|
||||
source_headers=["utilities/transactions/transaction_test.h"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
@@ -244,11 +244,6 @@ from an implementation detail instead of an explicit option.
|
||||
|
||||
## Important tips
|
||||
|
||||
### Pull request and diff summaries
|
||||
* Before writing or updating an external RocksDB pull request summary or its
|
||||
corresponding internal Phabricator diff summary, read and follow
|
||||
`claude_md/pull_request_summary_guideline.md`.
|
||||
|
||||
### Build system
|
||||
* There are 3 build system. Make for git clones, BUCK (meta internal) for hg
|
||||
clones, and CMake for some special cases.
|
||||
|
||||
@@ -650,9 +650,6 @@ if(USE_FOLLY)
|
||||
exec_program(ls ARGS -d ${FOLLY_INST_PATH}/../glog* OUTPUT_VARIABLE
|
||||
GLOG_INST_PATH)
|
||||
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.83.0)
|
||||
# Boost's per-library CMake configs find their dependencies (e.g.
|
||||
# boost_assert) only through the package search path.
|
||||
set(CMAKE_PREFIX_PATH ${BOOST_INST_PATH} ${CMAKE_PREFIX_PATH})
|
||||
if(EXISTS ${FMT_INST_PATH}/lib64)
|
||||
set(fmt_DIR ${FMT_INST_PATH}/lib64/cmake/fmt)
|
||||
else()
|
||||
@@ -699,13 +696,7 @@ if(USE_FOLLY)
|
||||
exec_program(sed ARGS -i 's/gflags_shared//g'
|
||||
${FOLLY_INST_PATH}/lib/cmake/folly/folly-targets.cmake)
|
||||
|
||||
# folly-config.cmake only checks for gflags under a target name that this
|
||||
# install doesn't define, then falls back to our Findgflags.cmake, which
|
||||
# mistakes the gflags_static target name for a library path. gflags is
|
||||
# resolved below instead.
|
||||
set(CMAKE_DISABLE_FIND_PACKAGE_gflags TRUE)
|
||||
include(${FOLLY_INST_PATH}/lib/cmake/folly/folly-config.cmake)
|
||||
unset(CMAKE_DISABLE_FIND_PACKAGE_gflags)
|
||||
|
||||
# Fix gflags library name for debug builds. The getdeps gflags shared
|
||||
# library is versioned (e.g. libgflags_debug.so.2.3), so resolve the
|
||||
@@ -981,7 +972,6 @@ set(SOURCES
|
||||
table/plain/plain_table_index.cc
|
||||
table/plain/plain_table_key_coding.cc
|
||||
table/plain/plain_table_reader.cc
|
||||
table/range_del_block.cc
|
||||
table/sst_file_dumper.cc
|
||||
table/sst_file_reader.cc
|
||||
table/sst_file_writer.cc
|
||||
@@ -1698,7 +1688,6 @@ if(WITH_TESTS)
|
||||
db/db_with_timestamp_test_util.cc
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
test_util/simple_external_table_factory.cc
|
||||
utilities/agg_merge/test_agg_merge.cc
|
||||
utilities/cassandra/test_utils.cc
|
||||
)
|
||||
|
||||
+1
-75
@@ -1,86 +1,13 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 11.13.2 (10/21/2026)
|
||||
### Bug Fixes
|
||||
* Fixed blob direct write with more than one concurrent flush of a column family: a later flush sealed and published the blob files of the oldest pending memtable instead of its own, so flushes failed with `Corruption: VersionBuilder: Blob file #N already added` and the database stopped.
|
||||
|
||||
## 11.13.1 (10/23/2026)
|
||||
### Bug Fixes
|
||||
* Fixed data loss with `min_tombstones_for_range_conversion` and external file ingestion. An iterator whose view predated the ingestion could convert the deleted keys around the ingested keys into a range tombstone that deleted them. This happened when the file got no new sequence number (sequence number zero, `ingest_behind`, or `allow_db_generated_files`), and when an iterator was created during the ingestion.
|
||||
* Fixed a data loss bug where an iterator with `ReadOptions::read_tier = kMemtableTier` could, with `min_tombstones_for_range_conversion` enabled, insert a range tombstone that deleted live keys stored only in SST files. Read-path range tombstone conversion now runs only for iterators with `read_tier = kReadAllTier`.
|
||||
|
||||
## 11.13.0 (10/21/2026)
|
||||
### New Features
|
||||
* Added `FileSystem::IsPreallocationSupported()`. A `FileSystem` that never preallocates file space can return false so that DB open skips reopening and truncating the last WAL file.
|
||||
|
||||
### Public API Changes
|
||||
* Added the experimental `DBOptions::track_options_file_number_in_manifest`
|
||||
rollout option (with C and Java bindings). The option is persisted in each
|
||||
OPTIONS file. When enabled, RocksDB durably creates an OPTIONS file and then
|
||||
commits its file number in MANIFEST. Column-family create/drop commits the
|
||||
configuration snapshot and membership in one MANIFEST record. A newer tracked
|
||||
OPTIONS file without a commit is ignored after a crash, while a newer OPTIONS
|
||||
file written with tracking disabled retains the legacy highest-numbered-file
|
||||
behavior. Batched column-family changes publish one snapshot; after partial
|
||||
success it can be a safe superset that persisted-options APIs filter against
|
||||
live MANIFEST membership. Ordinary `DB::Open` continues to use caller-supplied
|
||||
options and does not depend on the operational snapshot.
|
||||
* Added `EncodeEntry()`, `PackSequenceAndType()`, and `UnPackSequenceAndType()` in `rocksdb/utilities/types_util.h` to construct internal keys and pack or unpack their sequence numbers and entry types. `kInternalKeyFooterSize` exposes the footer size in bytes.
|
||||
* Added `ldb update_manifest --rebind_options_file --options_file=OPTIONS-<number>` for offline repair of a committed MANIFEST OPTIONS file number. Operators must select a trusted snapshot that has MANIFEST tracking enabled and provide any external DB/CF allocation roots that are not serialized in OPTIONS.
|
||||
|
||||
## 11.11.1 (09/24/2026)
|
||||
### Behavior Changes
|
||||
* With `max_bgerror_resume_count <= 0` (auto resume disabled), a retryable or file-scoped IO error during compaction or flush is now a hard background error that stops writes until `DB::Resume()` is called. Previously, compaction errors were silently retried, and flush errors with a healthy WAL were soft errors that let writes continue while flushes were stopped.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed two more `SstFileManager` trash accounting leaks: retrying a failed trash deletion immediately (for example with rate limiting disabled) after the file had disappeared, and a trash file that was deleted but whose directory sync then failed. In both cases the file's size stayed in the total trash size and tracked size until restart.
|
||||
* Fixed `SstFileManager` trash accounting leaking the size of a queued trash file that disappeared before deletion (for example because its directory was renamed). The leaked bytes stayed in the total trash size until restart, which could push every later deletion past `max_trash_db_ratio` and make it immediate instead of rate limited.
|
||||
* Fixed `MultiGet` with `ReadOptions::async_io` returning `NotSupported` when io_uring initialization or an asynchronous read submission fails. It now checks async I/O support on the reading thread and falls back to synchronous reads when needed.
|
||||
* Fixed subset checkpoints so their newest OPTIONS file lists only the column
|
||||
families retained by the checkpoint. This applies with both legacy
|
||||
highest-numbered OPTIONS selection and MANIFEST-tracked OPTIONS selection, and
|
||||
prevents `LoadLatestOptions` from recreating column families that the subset
|
||||
checkpoint intentionally dropped from its copied MANIFEST.
|
||||
|
||||
### Performance Improvements
|
||||
* Perf context counters and timers no longer call the thread-local init function of `perf_context` on every update. With `kEnableCount`, a counter update costs about 60% less.
|
||||
|
||||
## 11.12.0 (10/07/2026)
|
||||
### New Features
|
||||
* Added `FullExternalTableFactory` and related full-mode external table APIs that preserve RocksDB internal keys, enabling external tables to support nonzero sequence numbers, point and single deletions, merge operands, blob indexes, wide-column entities, snapshots, live DB writes, and multi-level compactions.
|
||||
|
||||
### Public API Changes
|
||||
* Added experimental coroutine capability interfaces for basic and full `ExternalTableReader` implementations. Coroutine-capable external readers can initiate file reads through `FSRandomAccessFile::SubmitReadAsync()`.
|
||||
* `ExternalTableFactory::NewTableReader()` now receives an already-opened `FSRandomAccessFile` and its file size instead of requiring implementations to reopen `file_path`. The supplied file preserves RocksDB's direct-I/O alignment handling, rate limiting, read statistics and listener notifications, prefetching, and synchronous, asynchronous, and batched reads.
|
||||
* Added `PutRangeDeletionBlock()`, `GetRangeDeletionBlock()`, and `IsDeleteRangeSupported()` to the external table interfaces so formats can opt in to range deletions using RocksDB's raw range-deletion metadata block.
|
||||
* Opaque `PreparedFileInfo` metadata can now be serialized to a checksummed representation with `SerializePreparedFileInfo()` and restored with `DeserializePreparedFileInfo()` for reuse across processes.
|
||||
* Removed the deprecated `IngestExternalFileOptions::write_global_seqno` option and its C, Java, and `ldb` APIs, `TableProperties::external_sst_file_global_seqno_offset`, and the `file_offset` output from `ExternalTableReader::GetPropertiesBlock()`. Existing external SST files whose global sequence number was rewritten without updating the properties-block checksum remain readable.
|
||||
* `TablePropertiesNames` has no apparent use in the public API so has been deprecated there.
|
||||
* `TableProperties::creation_time` is renamed to `oldest_ancestor_time`, which better describes its meaning, along with `TablePropertiesNames::kCreationTime` to `kOldestAncestorTime`. The misspelled `SstFileMetaData::oldest_ancester_time` is renamed to `oldest_ancestor_time`. The old names remain available as deprecated aliases and will be removed in a future release.
|
||||
* Added `WriteBufferFlushPolicy` and `WriteBufferManager` policy selection for flushing the oldest or largest column family, including configurable batched flushing of the largest column families across DBs sharing a manager, with C and Java bindings.
|
||||
|
||||
### Behavior Changes
|
||||
* When `ExternalTableBuilderBase::PutPropertiesBlock()` returns `NotSupported`, RocksDB now overrides `num_entries`, `raw_key_size`, and `raw_value_size` from `ExternalTableBuilderBase::GetTableProperties()` with the logical totals tracked by the external-table adapter. Format-specific properties such as `data_size` remain unchanged.
|
||||
* The `FSWritableFile` passed to `ExternalTableFactory::NewTableBuilder()` now reports logical file size including RocksDB-buffered data and exposes the underlying file's direct-I/O mode and required buffer alignment.
|
||||
* `Iterator::Refresh()` now returns `NotSupported` on iterators from a secondary DB's `DB::NewIterators()` only, because those iterators are pinned to one cross-column-family read view. Iterators from `DB::NewIterator()` are unaffected; to advance a batch, call `DB::NewIterators()` again after `TryCatchUpWithPrimary()`.
|
||||
* `SstFileReader` now reports standard SST read statistics and file I/O listener notifications, and honors the configured read rate limiter.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed full recovery from retryable background errors to atomically advance WAL replay boundaries for all column families, preventing partial recovery of multi-column-family write batches after a failed recovery flush.
|
||||
* Fixed RocksDB builds on big-endian architectures such as s390x by disabling the optional persistent fault-injection log, whose binary format is little-endian-only.
|
||||
* Fixed `DB::ClipColumnFamily()` deleting a non-L0 SST file whose largest key equals `begin_key`, which lost keys at the start of the kept range.
|
||||
* Fixed swapped `oldest_key_time` and `newest_key_time` values in the `table_file_creation` event log entry.
|
||||
* Fixed `ExternalTableReader::VerifyChecksum()` results not being honored by the external table adapter.
|
||||
* Fixed a bug in manifest handling that could lead to a hang or memory corruption in the case of LSM sanity checks being fatally violated (exceptional; see `force_consistency_checks`).
|
||||
* Fixed `MultiGet()` with `ReadOptions::read_tier == kBlockCacheTier` reading data blocks from disk when `allow_mmap_reads` is false. Keys whose data block is not in the block cache now return `Status::Incomplete()`, the same as `Get()`.
|
||||
* Fixed trace replay (`DB::NewDefaultReplayer()`, db_bench `replay`) failing every read record (Get, MultiGet, iterator seek) on the default column family with `Corruption: Invalid Column Family ID` when the default column family handle was not passed, which is always the case in db_bench with `--num_column_families=1`.
|
||||
* Fixed multi-threaded `Replayer::Replay()` ignoring errors from executing trace records and returning OK. It now stops and returns the error, including `Status::NotSupported()` from executing a record, as single-threaded replay does.
|
||||
* Fixed secondary DB `NewIterators()` returning iterators from different database states or blocking for `TryCatchUpWithPrimary()` by serving the last completed catch-up view.
|
||||
|
||||
### Performance Improvements
|
||||
* Single Get coroutine reads using non-mmap, non-direct I/O on a `kFSBuffer`-capable `FileSystem` now use `FSRandomAccessFile::SubmitReadAsync()` instead of falling back to synchronous `Read()`. When rate-limited, these reads acquire their full token budget before submission to preserve a single filesystem-owned buffer, which can make physical I/O burstier than reads using caller-provided buffers.
|
||||
|
||||
|
||||
## 11.11.0 (09/21/2026)
|
||||
### New Features
|
||||
* External tables now support DB-assigned global sequence numbers when `IngestExternalFileOptions::allow_global_seqno` is enabled, allowing overlapping external tables to be ingested across multiple levels when compactions are disabled. This includes the deprecated `write_global_seqno` path when the reader supplies the properties block's file offset.
|
||||
@@ -111,7 +38,6 @@ checkpoint intentionally dropped from its copied MANIFEST.
|
||||
### Performance Improvements
|
||||
* `DB::MultiGetEntityLazy()` now reads all keys of a batch under a single shared per-column-family SuperVersion pin, and a single `LazyWideColumnsBatch::MultiResolve()` coalesces the resulting blob reads across keys: one read per blob file (and one per SST for embedded blobs), for whole-column and byte-range reads alike, instead of reading each key's blobs separately.
|
||||
|
||||
|
||||
## 11.10.0 (08/28/2026)
|
||||
### New Features
|
||||
* Added `BlockBasedTableOptions::optimize_key_common_prefix` as part of new `format_version=8`. When enabled, each data block and index block stores the common user-key prefix of its keys once instead of at every restart point, reducing block size and block-cache footprint for prefix-heavy data and speeding up Seek for bytewise-ordered keys.
|
||||
|
||||
@@ -591,7 +591,6 @@ ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
|
||||
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
|
||||
FOLLY_DEPENDENT_HEADERS := \
|
||||
include/rocksdb/coro_db.h \
|
||||
include/rocksdb/coro_external_table.h \
|
||||
include/rocksdb/utilities/coro_stackable_db.h
|
||||
# Some headers like in port/ are platform-specific
|
||||
DEV_HEADERS_TO_CHECK := $(filter-out $(FOLLY_DEPENDENT_HEADERS), $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|range_tree/|secondary_index/'))
|
||||
|
||||
@@ -35,49 +35,6 @@ from util import ColorString
|
||||
# tests to export as libraries for inclusion in other projects
|
||||
_EXPORTED_TEST_LIBS = ["env_basic_test"]
|
||||
|
||||
_TEST_LIB_HEADERS = [
|
||||
"db/db_test_util.h",
|
||||
"db/db_with_timestamp_test_util.h",
|
||||
"table/mock_table.h",
|
||||
"test_util/mock_time_env.h",
|
||||
"test_util/secondary_cache_test_util.h",
|
||||
"test_util/simple_external_table_factory.h",
|
||||
"test_util/testharness.h",
|
||||
"test_util/testutil.h",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
|
||||
"tools/trace_analyzer_tool.h",
|
||||
"utilities/agg_merge/test_agg_merge.h",
|
||||
"utilities/cassandra/test_utils.h",
|
||||
]
|
||||
|
||||
_TEST_HEADERS = {
|
||||
"configurable_test": ["options/configurable_test.h"],
|
||||
"persistent_cache_test": ["utilities/persistent_cache/persistent_cache_test.h"],
|
||||
"point_lock_manager_test": [
|
||||
"utilities/transactions/lock/point/point_lock_manager_test.h",
|
||||
],
|
||||
"transaction_test": ["utilities/transactions/transaction_test.h"],
|
||||
}
|
||||
|
||||
_STRESS_LIB_HEADERS = [
|
||||
"db_stress_tool/db_stress_common.h",
|
||||
"db_stress_tool/db_stress_compaction_service.h",
|
||||
"db_stress_tool/db_stress_compression_manager.h",
|
||||
"db_stress_tool/db_stress_driver.h",
|
||||
"db_stress_tool/db_stress_filters.h",
|
||||
"db_stress_tool/db_stress_listener.h",
|
||||
"db_stress_tool/db_stress_shared_state.h",
|
||||
"db_stress_tool/db_stress_test_base.h",
|
||||
"db_stress_tool/db_stress_wide_merge_operator.h",
|
||||
"db_stress_tool/expected_state.h",
|
||||
"db_stress_tool/expected_value.h",
|
||||
"db_stress_tool/multi_ops_txns_stress.h",
|
||||
"test_util/simple_external_table_factory.h",
|
||||
"test_util/testutil.h",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
|
||||
"tools/trace_analyzer_tool.h"
|
||||
]
|
||||
|
||||
# Parse src.mk files as a Dictionary of
|
||||
# VAR_NAME => list of files
|
||||
def parse_src_mk(repo_path):
|
||||
@@ -226,34 +183,27 @@ def generate_buck(repo_path, deps_map):
|
||||
":rocksdb_lib",
|
||||
],
|
||||
)
|
||||
test_lib_sources = (
|
||||
src_mk.get("MOCK_LIB_SOURCES", [])
|
||||
+ src_mk.get("TEST_LIB_SOURCES", [])
|
||||
+ src_mk.get("EXP_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
)
|
||||
# Older release source lists may not compile every test helper yet.
|
||||
test_lib_headers = [
|
||||
header for header in _TEST_LIB_HEADERS
|
||||
if header[:-2] + ".cc" in test_lib_sources
|
||||
]
|
||||
# rocksdb_test_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_test_lib",
|
||||
test_lib_sources,
|
||||
src_mk.get("MOCK_LIB_SOURCES", [])
|
||||
+ src_mk.get("TEST_LIB_SOURCES", [])
|
||||
+ src_mk.get("EXP_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"],
|
||||
extra_test_libs=True,
|
||||
headers=test_lib_headers,
|
||||
)
|
||||
# rocksdb_with_faiss_test_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_with_faiss_test_lib",
|
||||
test_lib_sources,
|
||||
src_mk.get("MOCK_LIB_SOURCES", [])
|
||||
+ src_mk.get("TEST_LIB_SOURCES", [])
|
||||
+ src_mk.get("EXP_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
deps=[
|
||||
":rocksdb_with_faiss_lib",
|
||||
],
|
||||
extra_test_libs=True,
|
||||
headers=test_lib_headers,
|
||||
)
|
||||
# rocksdb_tools_lib
|
||||
BUCK.add_library(
|
||||
@@ -262,12 +212,6 @@ def generate_buck(repo_path, deps_map):
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"],
|
||||
headers=[
|
||||
"test_util/testutil.h",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.h",
|
||||
"tools/simulated_hybrid_file_system.h",
|
||||
"tools/trace_analyzer_tool.h",
|
||||
],
|
||||
)
|
||||
# rocksdb_cache_bench_tools_lib
|
||||
BUCK.add_library(
|
||||
@@ -281,19 +225,12 @@ def generate_buck(repo_path, deps_map):
|
||||
src_mk.get("POINT_LOCK_BENCH_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"],
|
||||
)
|
||||
stress_sources = (
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get("STRESS_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"]
|
||||
)
|
||||
# rocksdb_stress_lib
|
||||
BUCK.add_rocksdb_library(
|
||||
"rocksdb_stress_lib",
|
||||
stress_sources,
|
||||
headers=[
|
||||
header for header in _STRESS_LIB_HEADERS
|
||||
if header[:-2] + ".cc" in stress_sources
|
||||
],
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get("STRESS_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
)
|
||||
# ldb binary
|
||||
BUCK.add_binary(
|
||||
@@ -416,7 +353,6 @@ def generate_buck(repo_path, deps_map):
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":" + test_library]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
source_headers=_TEST_HEADERS.get(test, []),
|
||||
)
|
||||
else:
|
||||
if with_faiss:
|
||||
@@ -425,7 +361,6 @@ def generate_buck(repo_path, deps_map):
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":rocksdb_with_faiss_test_lib"]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
source_headers=_TEST_HEADERS.get(test, []),
|
||||
)
|
||||
else:
|
||||
BUCK.register_test(
|
||||
@@ -433,7 +368,6 @@ def generate_buck(repo_path, deps_map):
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
source_headers=_TEST_HEADERS.get(test, []),
|
||||
)
|
||||
BUCK.export_file("tools/db_crashtest.py")
|
||||
BUCK.export_file("tools/fault_injection_log_parser.py")
|
||||
|
||||
@@ -162,17 +162,12 @@ add_c_test_wrapper()
|
||||
).encode("utf-8")
|
||||
)
|
||||
|
||||
def register_test(self, test_name, src, deps, extra_compiler_flags, source_headers):
|
||||
def register_test(self, test_name, src, deps, extra_compiler_flags):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.unittests_template.format(
|
||||
test_name=test_name,
|
||||
test_cc=str(src),
|
||||
source_headers=(
|
||||
f" source_headers=[{pretty_list(source_headers, 16)}],\n"
|
||||
if source_headers
|
||||
else ""
|
||||
),
|
||||
deps=deps,
|
||||
extra_compiler_flags=extra_compiler_flags,
|
||||
).encode("utf-8")
|
||||
|
||||
@@ -30,7 +30,7 @@ cpp_binary_wrapper(name="{name}", srcs=[{srcs}], deps=[{deps}], extra_preprocess
|
||||
unittests_template = """
|
||||
cpp_unittest_wrapper(name="{test_name}",
|
||||
srcs=["{test_cc}"],
|
||||
{source_headers} deps={deps},
|
||||
deps={deps},
|
||||
extra_compiler_flags={extra_compiler_flags})
|
||||
|
||||
"""
|
||||
|
||||
@@ -142,11 +142,6 @@ uint64_t rocksdb_table_properties_column_family_id(
|
||||
return props->rep.column_family_id;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_oldest_ancestor_time(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.oldest_ancestor_time;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_creation_time(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.creation_time;
|
||||
@@ -177,6 +172,11 @@ uint64_t rocksdb_table_properties_fast_compression_estimated_data_size(
|
||||
return props->rep.fast_compression_estimated_data_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_external_sst_file_global_seqno_offset(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.external_sst_file_global_seqno_offset;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_tail_start_offset(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.tail_start_offset;
|
||||
|
||||
@@ -102,10 +102,6 @@ rocksdb_table_properties_fixed_key_len(const rocksdb_table_properties_t* props);
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_column_family_id(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_oldest_ancestor_time(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_creation_time(const rocksdb_table_properties_t* props);
|
||||
|
||||
@@ -126,6 +122,10 @@ extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_fast_compression_estimated_data_size(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_table_properties_external_sst_file_global_seqno_offset(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t rocksdb_table_properties_tail_start_offset(
|
||||
const rocksdb_table_properties_t* props);
|
||||
|
||||
|
||||
@@ -741,16 +741,6 @@ unsigned char rocksdb_options_get_write_dbid_to_manifest(
|
||||
return opt->rep.write_dbid_to_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_track_options_file_number_in_manifest(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.track_options_file_number_in_manifest = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_track_options_file_number_in_manifest(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.track_options_file_number_in_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_identity_file(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.write_identity_file = v;
|
||||
@@ -2067,6 +2057,16 @@ unsigned char rocksdb_ingestexternalfileoptions_get_ingest_behind(
|
||||
return opt->rep.ingest_behind;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_write_global_seqno(
|
||||
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v) {
|
||||
opt->rep.write_global_seqno = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_ingestexternalfileoptions_get_write_global_seqno(
|
||||
rocksdb_ingestexternalfileoptions_t* opt) {
|
||||
return opt->rep.write_global_seqno;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_verify_checksums_before_ingest(
|
||||
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v) {
|
||||
opt->rep.verify_checksums_before_ingest = v;
|
||||
|
||||
@@ -204,14 +204,6 @@ rocksdb_options_set_background_close_inactive_wals(rocksdb_options_t* opt,
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_background_close_inactive_wals(rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_track_options_file_number_in_manifest(
|
||||
rocksdb_options_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_track_options_file_number_in_manifest(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_prefix_seek_opt_in_only(
|
||||
rocksdb_options_t* opt, unsigned char v);
|
||||
|
||||
@@ -824,6 +816,14 @@ extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_ingestexternalfileoptions_get_ingest_behind(
|
||||
rocksdb_ingestexternalfileoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_ingestexternalfileoptions_set_write_global_seqno(
|
||||
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_ingestexternalfileoptions_get_write_global_seqno(
|
||||
rocksdb_ingestexternalfileoptions_t* opt);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_ingestexternalfileoptions_set_verify_checksums_before_ingest(
|
||||
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v);
|
||||
|
||||
@@ -556,6 +556,12 @@ static void rocksdb_roundtrip_test_rocksdb_ingestexternalfileoptions(void) {
|
||||
rocksdb_ingestexternalfileoptions_set_verify_file_checksum(obj, 0);
|
||||
CheckCondition(
|
||||
rocksdb_ingestexternalfileoptions_get_verify_file_checksum(obj) == 0);
|
||||
rocksdb_ingestexternalfileoptions_set_write_global_seqno(obj, 1);
|
||||
CheckCondition(
|
||||
rocksdb_ingestexternalfileoptions_get_write_global_seqno(obj) == 1);
|
||||
rocksdb_ingestexternalfileoptions_set_write_global_seqno(obj, 0);
|
||||
CheckCondition(
|
||||
rocksdb_ingestexternalfileoptions_get_write_global_seqno(obj) == 0);
|
||||
rocksdb_ingestexternalfileoptions_destroy(obj);
|
||||
}
|
||||
|
||||
@@ -913,12 +919,6 @@ static void rocksdb_roundtrip_test_rocksdb_options(void) {
|
||||
CheckCondition(rocksdb_options_get_track_and_verify_wals(obj) == 1);
|
||||
rocksdb_options_set_track_and_verify_wals(obj, 0);
|
||||
CheckCondition(rocksdb_options_get_track_and_verify_wals(obj) == 0);
|
||||
rocksdb_options_set_track_options_file_number_in_manifest(obj, 1);
|
||||
CheckCondition(
|
||||
rocksdb_options_get_track_options_file_number_in_manifest(obj) == 1);
|
||||
rocksdb_options_set_track_options_file_number_in_manifest(obj, 0);
|
||||
CheckCondition(
|
||||
rocksdb_options_get_track_options_file_number_in_manifest(obj) == 0);
|
||||
rocksdb_options_set_two_write_queues(obj, 1);
|
||||
CheckCondition(rocksdb_options_get_two_write_queues(obj) == 1);
|
||||
rocksdb_options_set_two_write_queues(obj, 0);
|
||||
|
||||
Vendored
+1
-2
@@ -276,8 +276,7 @@ struct KeyGen {
|
||||
// location, so we fix hash bits starting at the bottom of that word.
|
||||
auto hi_hash = uint64_t{0x9e3779b97f4a7c13U} ^
|
||||
(key_hash << 1 << (FLAGS_degenerate_hash_bits - 1));
|
||||
uint64_t un_hi;
|
||||
uint64_t un_lo;
|
||||
uint64_t un_hi, un_lo;
|
||||
BijectiveUnhash2x64(hi_hash, key_hash, &un_hi, &un_lo);
|
||||
un_lo ^= BitwiseAnd(FLAGS_seed, INT32_MAX);
|
||||
EncodeFixed64(key_data, un_lo);
|
||||
|
||||
Vendored
+2
-7
@@ -32,9 +32,9 @@ class CacheReservationManager {
|
||||
// for an incremental amount of memory used (i.e, incremental_memory_used)
|
||||
class CacheReservationHandle {
|
||||
public:
|
||||
virtual ~CacheReservationHandle() = default;
|
||||
virtual ~CacheReservationHandle() {}
|
||||
};
|
||||
virtual ~CacheReservationManager() = default;
|
||||
virtual ~CacheReservationManager() {}
|
||||
virtual Status UpdateCacheReservation(std::size_t new_memory_used) = 0;
|
||||
// TODO(hx235): replace the usage of
|
||||
// `UpdateCacheReservation(memory_used_delta, increase)` with
|
||||
@@ -235,11 +235,6 @@ class ConcurrentCacheReservationManager
|
||||
cache_res_mgr_ = cache_res_mgr;
|
||||
cache_res_handle_ = std::move(cache_res_handle);
|
||||
}
|
||||
// No copying allowed
|
||||
CacheReservationHandle(const CacheReservationHandle&) = delete;
|
||||
CacheReservationHandle& operator=(const CacheReservationHandle&) = delete;
|
||||
CacheReservationHandle(CacheReservationHandle&&) = delete;
|
||||
CacheReservationHandle& operator=(CacheReservationHandle&&) = delete;
|
||||
|
||||
~CacheReservationHandle() override {
|
||||
std::lock_guard<std::mutex> lock(cache_res_mgr_->cache_res_mgr_mu_);
|
||||
|
||||
+2
-2
@@ -407,8 +407,8 @@ TEST(CacheReservationHandleTest, HandleTest) {
|
||||
std::size_t mem_used = 0;
|
||||
const std::size_t incremental_mem_used_handle_1 = 1 * kSizeDummyEntry;
|
||||
const std::size_t incremental_mem_used_handle_2 = 2 * kSizeDummyEntry;
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> handle_1;
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> handle_2;
|
||||
std::unique_ptr<CacheReservationManager::CacheReservationHandle> handle_1,
|
||||
handle_2;
|
||||
|
||||
// To test consecutive CacheReservationManager::MakeCacheReservation works
|
||||
// correctly in terms of returning the handle as well as updating cache
|
||||
|
||||
Vendored
+3
-12
@@ -975,8 +975,7 @@ void FixedHyperClockTable::Erase(const UniqueId64x2& hashed_key) {
|
||||
// Could be multiple entries in rare cases. Erase them all.
|
||||
// Optimistically increment acquire counter
|
||||
auto add_acquire = AcquireCounter::PlusTransformPromiseNoOverflow(1);
|
||||
SlotMeta old_meta;
|
||||
SlotMeta meta;
|
||||
SlotMeta old_meta, meta;
|
||||
h->meta.Apply(add_acquire, &old_meta, &meta);
|
||||
// Check if it's an entry visible to lookups
|
||||
if (meta.IsVisible()) {
|
||||
@@ -1739,8 +1738,7 @@ inline bool MatchAndRef(const UniqueId64x2* hashed_key, const ClockHandle& h,
|
||||
// Must be at least something to match
|
||||
assert(hashed_key || shift > 0);
|
||||
|
||||
SlotMeta old_meta;
|
||||
SlotMeta new_meta;
|
||||
SlotMeta old_meta, new_meta;
|
||||
// (Optimistically) increment acquire counter.
|
||||
auto add_acquire = AcquireCounter::PlusTransformPromiseNoOverflow(1);
|
||||
h.meta.Apply(add_acquire, &old_meta, &new_meta);
|
||||
@@ -1858,12 +1856,6 @@ class AutoHyperClockTable::ChainRewriteLock {
|
||||
assert(saved_head_.IsLocked());
|
||||
}
|
||||
|
||||
// No copying allowed
|
||||
ChainRewriteLock(const ChainRewriteLock&) = delete;
|
||||
ChainRewriteLock& operator=(const ChainRewriteLock&) = delete;
|
||||
ChainRewriteLock(ChainRewriteLock&&) = delete;
|
||||
ChainRewriteLock& operator=(ChainRewriteLock&&) = delete;
|
||||
|
||||
~ChainRewriteLock() {
|
||||
if (!IsEnd()) {
|
||||
// Release lock
|
||||
@@ -3130,8 +3122,7 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
|
||||
if (probably_equal) {
|
||||
// Increment acquire counter for definitive check
|
||||
auto add_acquire = AcquireCounter::PlusTransformPromiseNoOverflow(1);
|
||||
SlotMeta old_meta;
|
||||
SlotMeta new_meta;
|
||||
SlotMeta old_meta, new_meta;
|
||||
h->meta.Apply(add_acquire, &old_meta, &new_meta);
|
||||
// Check if it's a referencable (sharable) entry
|
||||
if (LIKELY(old_meta.IsShareable())) {
|
||||
|
||||
Vendored
-4
@@ -29,10 +29,6 @@ class CompressedSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
const CompressedSecondaryCacheResultHandle&) = delete;
|
||||
CompressedSecondaryCacheResultHandle& operator=(
|
||||
const CompressedSecondaryCacheResultHandle&) = delete;
|
||||
CompressedSecondaryCacheResultHandle(CompressedSecondaryCacheResultHandle&&) =
|
||||
delete;
|
||||
CompressedSecondaryCacheResultHandle& operator=(
|
||||
CompressedSecondaryCacheResultHandle&&) = delete;
|
||||
|
||||
bool IsReady() override { return true; }
|
||||
|
||||
|
||||
+1
-2
@@ -508,8 +508,7 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
std::string str1 = rnd.RandomString(1001);
|
||||
auto item1 = std::make_unique<TestItem>(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert(key1, item1.get(), GetHelper(), str1.length()));
|
||||
// Appease clang-analyze "potential memory leak"
|
||||
[[maybe_unused]] TestItem* released_item1 = item1.release();
|
||||
item1.release(); // Appease clang-analyze "potential memory leak"
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup(key2, nullptr, this, Cache::Priority::LOW);
|
||||
|
||||
Vendored
+1
-2
@@ -1366,8 +1366,7 @@ TEST_P(BasicSecondaryCacheTest, BasicFailTest) {
|
||||
// str1.length()).IsInvalidArgument());
|
||||
ASSERT_OK(
|
||||
cache->Insert(k1.AsSlice(), item1.get(), GetHelper(), str1.length()));
|
||||
// Appease clang-analyze "potential memory leak"
|
||||
[[maybe_unused]] TestItem* released_item1 = item1.release();
|
||||
item1.release(); // Appease clang-analyze "potential memory leak"
|
||||
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup(k2.AsSlice(), nullptr, /*context*/ this,
|
||||
|
||||
Vendored
+2
-8
@@ -90,7 +90,7 @@ class CacheShardBase {
|
||||
class ShardedCacheBase : public Cache {
|
||||
public:
|
||||
explicit ShardedCacheBase(const ShardedCacheOptions& opts);
|
||||
~ShardedCacheBase() override = default;
|
||||
virtual ~ShardedCacheBase() = default;
|
||||
|
||||
int GetNumShardBits() const;
|
||||
uint32_t GetNumShards() const;
|
||||
@@ -143,13 +143,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
sizeof(CacheShard) * GetNumShards()))),
|
||||
destroy_shards_in_dtor_(false) {}
|
||||
|
||||
// No copying allowed
|
||||
ShardedCache(const ShardedCache&) = delete;
|
||||
ShardedCache& operator=(const ShardedCache&) = delete;
|
||||
ShardedCache(ShardedCache&&) = delete;
|
||||
ShardedCache& operator=(ShardedCache&&) = delete;
|
||||
|
||||
~ShardedCache() override {
|
||||
virtual ~ShardedCache() {
|
||||
if (destroy_shards_in_dtor_) {
|
||||
ForEachShard([](CacheShard* cs) { cs->~CacheShard(); });
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -36,7 +36,7 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
|
||||
#endif
|
||||
}
|
||||
|
||||
~TieredSecondaryCache() override = default;
|
||||
~TieredSecondaryCache() override {}
|
||||
|
||||
const char* Name() const override { return "TieredSecondaryCache"; }
|
||||
|
||||
@@ -75,7 +75,7 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
|
||||
|
||||
class ResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
~ResultHandle() override = default;
|
||||
~ResultHandle() override {}
|
||||
|
||||
bool IsReady() override {
|
||||
if (inner_handle_ && inner_handle_->IsReady()) {
|
||||
|
||||
Vendored
+1
-1
@@ -241,7 +241,7 @@ class FullTypedCacheHelperFns : public BasicTypedCacheHelperFns<TValue> {
|
||||
if (source != CacheTier::kVolatileTier) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
if constexpr (!std::is_empty_v<TCreateContext>) {
|
||||
if constexpr (sizeof(TCreateContext) > 0) {
|
||||
TCreateContext* tcontext = static_cast<TCreateContext*>(context);
|
||||
tcontext->Create(&value, out_charge, data, type, allocator);
|
||||
} else {
|
||||
|
||||
@@ -28,98 +28,6 @@ A behavior change is not automatically a defect. Report a finding when the
|
||||
intent, documentation, downstream-impact analysis, or mitigation has a concrete
|
||||
gap or ambiguity that could lead to an unintended compatibility regression.
|
||||
|
||||
## DB Mutex Review Perspective
|
||||
|
||||
For every non-trivial change, determine whether the changed code, or any
|
||||
transitive callee it adds or changes, can run while `DBImpl::mutex_` is held.
|
||||
State the result explicitly, including when the DB mutex is not affected.
|
||||
|
||||
`DBImpl::mutex_` is the DB-wide metadata and coordination mutex. It protects
|
||||
cross-object invariants involving `ColumnFamilyData`, memtable and
|
||||
`SuperVersion` publication, `VersionSet`, snapshots, flush/compaction queues,
|
||||
background-job state, and file lifetime. It is shared by all column families
|
||||
and can be a hot lock. It is not the filesystem `db_lock_`, and it should not
|
||||
replace data-path synchronization already provided by `WriteThread`,
|
||||
`wal_write_mutex_`, atomics, referenced `SuperVersion`s, or component-local
|
||||
synchronization.
|
||||
|
||||
When a DB-mutex critical section is affected:
|
||||
|
||||
### Locking and correctness
|
||||
|
||||
- Trace the full call path and mark the exact lock and unlock boundaries. Do
|
||||
not inspect only the lines that directly acquire the mutex.
|
||||
- State the locking contract. Helpers that require the lock should document it
|
||||
and normally use `mutex_.AssertHeld()`. Check whether each caller expects the
|
||||
lock to be held on return.
|
||||
- Treat calls that can release and re-acquire the mutex as concurrency
|
||||
boundaries. Examples include `VersionSet::LogAndApply()`, condition-variable
|
||||
waits, write-thread coordination, and notification helpers. Revalidate state
|
||||
inspected before such a call.
|
||||
- Keep atomic metadata transitions under the mutex. Examples include moving a
|
||||
memtable between mutable and immutable state, reserving compaction inputs,
|
||||
installing a `Version` or `SuperVersion`, updating scheduling counters and
|
||||
queues, and changing file-lifetime protection.
|
||||
- For expensive work, prefer a prepare/run/install structure: reserve and pin
|
||||
the required state while locked, do CPU or I/O work while unlocked, then
|
||||
re-lock, validate, and publish. Before unlocking, copy mutable options and
|
||||
acquire every required object reference or lifetime reservation.
|
||||
- Check object lifetime across every unlocked region. Do not use a raw
|
||||
`ColumnFamilyData*`, `Version*`, memtable, file metadata pointer, or DB-owned
|
||||
container element after unlocking unless an explicit reference,
|
||||
`SuperVersion`, job object, or pending-output reservation keeps it alive.
|
||||
- Preserve the snapshot/input-selection boundary. Flush and compaction must not
|
||||
miss a snapshot whose visible key versions they may remove. Missing a newly
|
||||
created snapshot is a correctness bug; retaining a recently released
|
||||
snapshot is conservative but safe.
|
||||
- Never invoke untrusted or re-entrant code while holding the mutex. The mutex
|
||||
is non-reentrant, so a callback into RocksDB can self-deadlock.
|
||||
- Check lock ordering. `options_mutex_` is acquired before the DB mutex. When
|
||||
both the DB mutex and `wal_write_mutex_` are needed, the DB mutex is acquired
|
||||
first. Establish and document the order for other locks.
|
||||
- Keep cleanup outside the critical section when possible. File deletion,
|
||||
`JobContext::Clean()`, expensive `SuperVersion` destruction, logging, and
|
||||
listener callbacks should normally run after protected state is detached or
|
||||
snapshotted.
|
||||
- Do not add the DB mutex to steady-state point reads or the normal write data
|
||||
phase without strong justification. Reads normally use a referenced
|
||||
`SuperVersion`; writes use `WriteThread` and component-specific
|
||||
synchronization.
|
||||
|
||||
### Critical-section performance
|
||||
|
||||
- Compare the CPU and I/O performed under the mutex before and after the
|
||||
change. Include call frequency, loop bounds, input-size scaling, hidden work
|
||||
in callees, and worst-case cost.
|
||||
- Account for filesystem or network I/O, waits, callbacks, logging and
|
||||
formatting, allocation, container growth, copies, sorting, scans, reference
|
||||
release and destructors, cache misses, and repeated metadata lookups.
|
||||
- Do not allow a significant regression in mutex-held work without clear
|
||||
justification, hold-time or contention measurements, and representative
|
||||
benchmarks.
|
||||
- Treat unavoidable work under the mutex as hot-path code and keep it highly
|
||||
optimized. Precompute, allocate, copy, and perform I/O outside the lock;
|
||||
remove repeated scans and lookups; cache or batch work where practical; and
|
||||
keep locked operations O(1) or tightly bounded when possible.
|
||||
- Treat new blocking I/O, waits, callbacks into user or plugin code, unbounded
|
||||
loops, or work that scales with DB size, files, column families, snapshots,
|
||||
or pending jobs as a blocker unless it is unavoidable and supported by strong
|
||||
measurements and explicit design justification.
|
||||
|
||||
### Validation and evidence
|
||||
|
||||
- Test unlock windows with sync points rather than sleeps. Cover relevant races
|
||||
with memtable switches, snapshot create/release, column-family drop,
|
||||
flush/compaction install, shutdown, and background errors. Run TSAN where
|
||||
appropriate.
|
||||
- Distinguish wait time from hold time. `PerfContext::db_mutex_lock_nanos` and
|
||||
`DB_MUTEX_WAIT_MICROS` primarily expose acquisition wait; use contention
|
||||
profiles or scoped instrumentation to find long critical sections.
|
||||
- Report affected critical sections, before/after cost, workload assumptions,
|
||||
optimization choices, and remaining risk. If exact measurement is not
|
||||
practical, provide a conservative static bound and do not claim no regression
|
||||
without evidence.
|
||||
|
||||
IMPORTANT — Where the review goes: Your FINAL RESPONSE TEXT is the review.
|
||||
The PR comment is generated verbatim from your final response, so the
|
||||
complete, polished review MUST appear there in full. Do NOT end your turn with
|
||||
@@ -399,9 +307,6 @@ in isolation may be a critical bug when you consider the full call chain.
|
||||
- **Callee side effects**: Does the change call functions that mutate shared
|
||||
state (counters, seqnos, metadata)? Are the mutations correct for the new
|
||||
calling context? (Use the callee-chain analysis from context.md)
|
||||
- Apply the DB Mutex Review Perspective above to locking contracts, unlocked
|
||||
object lifetimes, stale-state validation, lock ordering, and snapshot
|
||||
boundaries.
|
||||
|
||||
#### Agent: Cross-Component & Adversarial Reviewer
|
||||
|
||||
@@ -505,9 +410,6 @@ Write findings to `findings-caller-audit.md`.
|
||||
- Loop optimization opportunities
|
||||
- Branch prediction (LIKELY/UNLIKELY)
|
||||
- Zero-overhead design verification
|
||||
- Apply the DB Mutex Review Perspective above, with special focus on transitive
|
||||
CPU and I/O, before/after cost, critical-section hold time, and benchmark
|
||||
evidence
|
||||
|
||||
#### Agent: API & Compatibility Reviewer
|
||||
- Public API backwards compatibility
|
||||
@@ -691,8 +593,6 @@ Rules for this structure:
|
||||
- [ ] Multi-component interactions (compaction, recovery, snapshots, iterators)
|
||||
- [ ] Configuration dependencies and unexpected option combinations
|
||||
- [ ] Potential pitfalls from caller-chain analysis
|
||||
- [ ] DB-mutex reachability checked through transitive callees; affected lock
|
||||
and unlock boundaries documented
|
||||
|
||||
### Review Phase (verified by agents)
|
||||
- [ ] Database semantics preserved (snapshot isolation, key ordering)
|
||||
@@ -706,10 +606,6 @@ Rules for this structure:
|
||||
- [ ] Code follows RocksDB style conventions
|
||||
- [ ] Behavioral contracts with upstream callers preserved
|
||||
- [ ] All callers enumerated with parameter range table
|
||||
- [ ] Before/after CPU and I/O under the DB mutex compared, with call frequency,
|
||||
scaling, hidden callee work, and worst-case cost included
|
||||
- [ ] No significant DB-mutex-held work regression; unavoidable work is highly
|
||||
optimized, bounded, and supported by measurements when material
|
||||
|
||||
## Output Structure
|
||||
Your **final response text is the primary output** — it becomes the PR comment
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
# RocksDB Diff and Pull Request Summary Guidance
|
||||
|
||||
This guidance applies to both internal Phabricator diff summaries and external
|
||||
GitHub pull request summaries for changes under `fbcode/internal_repo_rocksdb/`.
|
||||
|
||||
## Goal
|
||||
|
||||
Write each diff or pull request summary for a reviewer who has not read the
|
||||
patch. The summary must explain the behavioral story and the evidence for it,
|
||||
not merely inventory the edited files or symbols.
|
||||
|
||||
Every new or updated diff or pull request summary must clearly cover:
|
||||
|
||||
1. Why the change is required.
|
||||
2. What the previous behavior was, including the failure mode or limitation.
|
||||
3. What the new behavior is.
|
||||
4. What changed in the implementation.
|
||||
5. Safety boundaries, compatibility, and unsupported or intentionally deferred
|
||||
cases.
|
||||
6. Tests performed and known limitations of the validation.
|
||||
|
||||
Use explicit headings for a broad, subtle, or high-risk change. A small,
|
||||
focused change may combine these points into a few paragraphs, but none may be
|
||||
left implicit when it affects a review decision.
|
||||
|
||||
## What Good RocksDB Summaries Make Concrete
|
||||
|
||||
- Name the exact conditions that activate a bug or behavior: relevant options,
|
||||
call ordering, storage mode, concurrency mode, file format, platform, or
|
||||
workload shape. Also state important cases that are not affected.
|
||||
- Explain the causal chain from the old behavior to its externally observable
|
||||
result. For correctness fixes, distinguish a symptom from the underlying
|
||||
invariant violation.
|
||||
- Describe the new contract before implementation mechanics. Then identify the
|
||||
essential implementation choices and why they are sufficient.
|
||||
- Call out API, ABI, serialized-format, behavioral, and performance
|
||||
compatibility where relevant. For additive persisted fields, state how older
|
||||
data is interpreted and whether older readers remain supported.
|
||||
- State safety boundaries precisely: defaults, feature gates, fallback paths,
|
||||
ownership and lifetime constraints, memory-ordering assumptions, and cases
|
||||
deliberately postponed to a follow-up.
|
||||
- Support performance claims with a baseline, workload and configuration,
|
||||
measurement method, and results. Report meaningful tradeoffs, not only the
|
||||
winning metric.
|
||||
- Make the test plan evidence-based. Name the tests and configurations, say
|
||||
what behavior each proves, and report failures, timeouts, untested platforms,
|
||||
or stale pre-rebase results honestly. For a regression test, say how it fails
|
||||
without the fix when that was verified.
|
||||
- Separate incidental cleanup or a pre-existing test fix from the primary
|
||||
change, and explain why it belongs in the diff.
|
||||
|
||||
Avoid vague claims such as "improves performance," "fixes an issue," or "tests
|
||||
pass." Avoid copying the patch into prose, exhaustive file-by-file narration,
|
||||
raw command output, and claims broader than the evidence.
|
||||
|
||||
## Suggested Structure
|
||||
|
||||
Adapt this structure to the change; do not keep empty or irrelevant sections.
|
||||
|
||||
```text
|
||||
Summary:
|
||||
|
||||
Why the change is required:
|
||||
<User or system impact, and why the current behavior is inadequate.>
|
||||
|
||||
Previous behavior:
|
||||
<Old contract or mechanism, triggering conditions, and observable result.>
|
||||
|
||||
New behavior:
|
||||
<New contract and externally visible effect.>
|
||||
|
||||
Implementation:
|
||||
<Essential design and code changes; omit routine edit-by-edit narration.>
|
||||
|
||||
Safety, compatibility, and limitations:
|
||||
<Defaults, unaffected paths, compatibility, unsupported cases, and follow-ups.>
|
||||
|
||||
Test Plan:
|
||||
<Exact tests/benchmarks, configurations, what they demonstrate, and gaps.>
|
||||
```
|
||||
|
||||
For a focused bug fix, a cohesive narrative can be clearer than six headings:
|
||||
problem and root cause, trigger and unaffected cases, fix, then quantified or
|
||||
counterfactual validation. For a multi-part change, labeled bullets such as
|
||||
`Fix`, `Add`, and `Change` can make the implementation and review surface easy
|
||||
to scan.
|
||||
|
||||
## Reference Pull Request Summaries
|
||||
|
||||
- [PR #15216](https://github.com/facebook/rocksdb/pull/15216) is a strong
|
||||
focused bug-fix example. It traces stale iterator state to readahead behavior
|
||||
and dropped keys, identifies the exact prerequisites and unaffected mmap
|
||||
case, gives before/after measurements, and explains how the regression test
|
||||
fails when the fix is reverted.
|
||||
- [PR #15218](https://github.com/facebook/rocksdb/pull/15218) is a concise
|
||||
example of explicit `Why`, `Previous behavior`, `New behavior`,
|
||||
`Implementation`, and `Safety boundaries and unsupported cases` sections.
|
||||
- Peter Dillinger's [PR #15231](https://github.com/facebook/rocksdb/pull/15231)
|
||||
is a strong recent multi-part example. Its `Fix`/`Add`/`Change` bullets
|
||||
separate the behaviors, its compatibility sentence explains how old files
|
||||
read back, and its test plan maps each new persisted property to concrete
|
||||
coverage while explaining an adjacent test adjustment.
|
||||
|
||||
Use these pull requests as models for reasoning, specificity, and evidence. Do
|
||||
not copy their length or headings mechanically; scale the summary to the risk
|
||||
and complexity of the change.
|
||||
@@ -25,7 +25,6 @@ CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --
|
||||
crash_test_with_multiops_wup_txn \
|
||||
crash_test_with_optimistic_txn \
|
||||
crash_test_with_tiered_storage \
|
||||
crash_test_with_full_external_table \
|
||||
liveness_crash_test \
|
||||
blackbox_crash_test blackbox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_wc_txn blackbox_crash_test_with_wp_txn \
|
||||
@@ -37,7 +36,6 @@ CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --
|
||||
blackbox_crash_test_with_multiops_wup_txn \
|
||||
blackbox_crash_test_with_optimistic_txn \
|
||||
blackbox_crash_test_with_tiered_storage \
|
||||
blackbox_crash_test_with_full_external_table \
|
||||
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
|
||||
whitebox_crash_test_with_wc_txn whitebox_crash_test_with_wp_txn \
|
||||
whitebox_crash_test_with_wup_txn \
|
||||
@@ -88,8 +86,6 @@ crash_test_with_tiered_storage: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_tiered_storage
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_tiered_storage
|
||||
|
||||
crash_test_with_full_external_table: blackbox_crash_test_with_full_external_table
|
||||
|
||||
crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wc_txn
|
||||
|
||||
@@ -136,9 +132,6 @@ blackbox_crash_test_with_multiops_wup_txn: $(DB_STRESS_CMD)
|
||||
blackbox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_tiered_storage blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_full_external_table: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_full_external_table blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --optimistic_txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
|
||||
@@ -42,13 +42,6 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
std::unique_ptr<ColumnFamilyData, ColumnFamilyDataUnrefDeleter>;
|
||||
|
||||
public:
|
||||
ArenaWrappedDBIter() = default;
|
||||
// No copying allowed
|
||||
ArenaWrappedDBIter(const ArenaWrappedDBIter&) = delete;
|
||||
ArenaWrappedDBIter& operator=(const ArenaWrappedDBIter&) = delete;
|
||||
ArenaWrappedDBIter(ArenaWrappedDBIter&&) = delete;
|
||||
ArenaWrappedDBIter& operator=(ArenaWrappedDBIter&&) = delete;
|
||||
|
||||
~ArenaWrappedDBIter() override {
|
||||
if (db_iter_ != nullptr) {
|
||||
DestroyDBIter();
|
||||
|
||||
@@ -24,8 +24,6 @@ class AttributeGroupIteratorImpl : public AttributeGroupIterator {
|
||||
AttributeGroupIteratorImpl(const AttributeGroupIteratorImpl&) = delete;
|
||||
AttributeGroupIteratorImpl& operator=(const AttributeGroupIteratorImpl&) =
|
||||
delete;
|
||||
AttributeGroupIteratorImpl(AttributeGroupIteratorImpl&&) = delete;
|
||||
AttributeGroupIteratorImpl& operator=(AttributeGroupIteratorImpl&&) = delete;
|
||||
|
||||
bool Valid() const override { return impl_.Valid(); }
|
||||
void SeekToFirst() override { impl_.SeekToFirst(); }
|
||||
|
||||
@@ -68,8 +68,6 @@ class BlobFileBuilder {
|
||||
|
||||
BlobFileBuilder(const BlobFileBuilder&) = delete;
|
||||
BlobFileBuilder& operator=(const BlobFileBuilder&) = delete;
|
||||
BlobFileBuilder(BlobFileBuilder&&) = delete;
|
||||
BlobFileBuilder& operator=(BlobFileBuilder&&) = delete;
|
||||
|
||||
~BlobFileBuilder();
|
||||
|
||||
|
||||
+24
-88
@@ -38,14 +38,12 @@ BlobFileCache::BlobFileCache(Cache* cache,
|
||||
}
|
||||
|
||||
Status BlobFileCache::GetBlobFileReader(
|
||||
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
|
||||
const ReadOptions& read_options, uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader,
|
||||
bool allow_footer_skip_retry) {
|
||||
assert(blob_file_reader);
|
||||
assert(blob_file_reader->IsEmpty());
|
||||
|
||||
const uint64_t blob_file_number = blob_file.file_number;
|
||||
|
||||
// NOTE: sharing same Cache with table_cache
|
||||
const Slice key = GetSliceForKey(&blob_file_number);
|
||||
|
||||
@@ -76,8 +74,18 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
{
|
||||
Status s = OpenBlobFileReader(read_options, blob_file,
|
||||
allow_footer_skip_retry, &reader);
|
||||
assert(file_options_);
|
||||
Status s = BlobFileReader::Create(
|
||||
*immutable_options_, read_options, *file_options_, column_family_id_,
|
||||
blob_file_read_hist_, blob_file_number, io_tracer_,
|
||||
/*skip_footer_validation=*/false, &reader);
|
||||
if (!s.ok() && s.IsCorruption() && allow_footer_skip_retry) {
|
||||
reader.reset();
|
||||
s = BlobFileReader::Create(
|
||||
*immutable_options_, read_options, *file_options_, column_family_id_,
|
||||
blob_file_read_hist_, blob_file_number, io_tracer_,
|
||||
/*skip_footer_validation=*/true, &reader);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(immutable_options_->logger,
|
||||
"BlobFileCache open failed for blob file %" PRIu64
|
||||
@@ -98,7 +106,7 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] BlobFileReader* released_reader = reader.release();
|
||||
reader.release();
|
||||
|
||||
*blob_file_reader = cache_.Guard(handle);
|
||||
|
||||
@@ -106,7 +114,7 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
}
|
||||
|
||||
Status BlobFileCache::OpenBlobFileReaderUncached(
|
||||
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
|
||||
const ReadOptions& read_options, uint64_t blob_file_number,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader,
|
||||
bool allow_footer_skip_retry) {
|
||||
assert(blob_file_reader);
|
||||
@@ -115,77 +123,21 @@ Status BlobFileCache::OpenBlobFileReaderUncached(
|
||||
Statistics* const statistics = immutable_options_->stats;
|
||||
RecordTick(statistics, NO_FILE_OPENS);
|
||||
|
||||
Status s = OpenBlobFileReader(read_options, blob_file,
|
||||
allow_footer_skip_retry, blob_file_reader);
|
||||
if (!s.ok()) {
|
||||
RecordTick(statistics, NO_FILE_ERRORS);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void BlobFileCache::RegisterBlobFileChecksum(
|
||||
const BlobFileOpenInfo& blob_file) {
|
||||
assert(blob_file.file_checksum.empty() ==
|
||||
blob_file.file_checksum_func_name.empty());
|
||||
if (blob_file.file_checksum.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Slice key = GetSliceForKey(&blob_file.file_number);
|
||||
MutexLock cache_lock(&mutex_.Get(key));
|
||||
{
|
||||
MutexLock checksum_lock(&blob_file_checksums_mutex_);
|
||||
BlobFileChecksum& checksum = blob_file_checksums_[blob_file.file_number];
|
||||
checksum.value = blob_file.file_checksum.ToString();
|
||||
checksum.function_name = blob_file.file_checksum_func_name.ToString();
|
||||
}
|
||||
|
||||
// Readers opened while the file was active did not carry finalized metadata
|
||||
// and may also have observed a footer-less file size.
|
||||
cache_.get()->Erase(key);
|
||||
}
|
||||
|
||||
void BlobFileCache::UnregisterBlobFileChecksum(uint64_t blob_file_number) {
|
||||
MutexLock checksum_lock(&blob_file_checksums_mutex_);
|
||||
blob_file_checksums_.erase(blob_file_number);
|
||||
}
|
||||
|
||||
Status BlobFileCache::OpenBlobFileReader(
|
||||
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
|
||||
bool allow_footer_skip_retry,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader) {
|
||||
assert(file_options_);
|
||||
assert(blob_file.file_checksum.empty() ==
|
||||
blob_file.file_checksum_func_name.empty());
|
||||
|
||||
FileOptions file_options = *file_options_;
|
||||
if (!blob_file.file_checksum.empty()) {
|
||||
file_options.file_checksum = blob_file.file_checksum.ToString();
|
||||
file_options.file_checksum_func_name =
|
||||
blob_file.file_checksum_func_name.ToString();
|
||||
} else {
|
||||
MutexLock checksum_lock(&blob_file_checksums_mutex_);
|
||||
const auto it = blob_file_checksums_.find(blob_file.file_number);
|
||||
if (it != blob_file_checksums_.end()) {
|
||||
file_options.file_checksum = it->second.value;
|
||||
file_options.file_checksum_func_name = it->second.function_name;
|
||||
} else {
|
||||
file_options.file_checksum.clear();
|
||||
file_options.file_checksum_func_name.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Status s = BlobFileReader::Create(
|
||||
*immutable_options_, read_options, file_options, column_family_id_,
|
||||
blob_file_read_hist_, blob_file.file_number, io_tracer_,
|
||||
*immutable_options_, read_options, *file_options_, column_family_id_,
|
||||
blob_file_read_hist_, blob_file_number, io_tracer_,
|
||||
/*skip_footer_validation=*/false, blob_file_reader);
|
||||
if (!s.ok() && s.IsCorruption() && allow_footer_skip_retry) {
|
||||
blob_file_reader->reset();
|
||||
s = BlobFileReader::Create(
|
||||
*immutable_options_, read_options, file_options, column_family_id_,
|
||||
blob_file_read_hist_, blob_file.file_number, io_tracer_,
|
||||
*immutable_options_, read_options, *file_options_, column_family_id_,
|
||||
blob_file_read_hist_, blob_file_number, io_tracer_,
|
||||
/*skip_footer_validation=*/true, blob_file_reader);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
RecordTick(statistics, NO_FILE_ERRORS);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -226,7 +178,7 @@ Status BlobFileCache::InsertBlobFileReader(
|
||||
}
|
||||
|
||||
Status BlobFileCache::RefreshBlobFileReader(
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
uint64_t blob_file_number,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader,
|
||||
CacheHandleGuard<BlobFileReader>* cached_blob_file_reader) {
|
||||
assert(blob_file_reader);
|
||||
@@ -234,26 +186,10 @@ Status BlobFileCache::RefreshBlobFileReader(
|
||||
assert(cached_blob_file_reader);
|
||||
assert(cached_blob_file_reader->IsEmpty());
|
||||
|
||||
const Slice key = GetSliceForKey(&blob_file.file_number);
|
||||
const Slice key = GetSliceForKey(&blob_file_number);
|
||||
MutexLock lock(&mutex_.Get(key));
|
||||
|
||||
TypedHandle* handle = cache_.Lookup(key);
|
||||
|
||||
if (blob_file.file_checksum.empty()) {
|
||||
MutexLock checksum_lock(&blob_file_checksums_mutex_);
|
||||
if (blob_file_checksums_.find(blob_file.file_number) !=
|
||||
blob_file_checksums_.end()) {
|
||||
// This reader can have been opened just before finalization registered
|
||||
// checksum metadata. Never let such a racing refresh repopulate the
|
||||
// cache.
|
||||
if (handle) {
|
||||
*cached_blob_file_reader = cache_.Guard(handle);
|
||||
}
|
||||
blob_file_reader->reset();
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
BlobFileReader* const cached_reader = cache_.Value(handle);
|
||||
assert(cached_reader != nullptr);
|
||||
|
||||
@@ -6,12 +6,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "cache/typed_cache.h"
|
||||
#include "db/blob/blob_file_reader.h"
|
||||
#include "db/blob/blob_read_request.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
@@ -34,35 +31,21 @@ class BlobFileCache {
|
||||
|
||||
BlobFileCache(const BlobFileCache&) = delete;
|
||||
BlobFileCache& operator=(const BlobFileCache&) = delete;
|
||||
BlobFileCache(BlobFileCache&&) = delete;
|
||||
BlobFileCache& operator=(BlobFileCache&&) = delete;
|
||||
|
||||
~BlobFileCache() = default;
|
||||
|
||||
// Returns a cached reader for `blob_file`, opening and caching it on miss.
|
||||
// Checksum metadata is forwarded to the FileSystem through FileOptions when
|
||||
// the file is opened. If `allow_footer_skip_retry` is true, a
|
||||
// footer-validation corruption retries once without requiring a footer.
|
||||
// Returns a cached reader for `blob_file_number`, opening and caching it on
|
||||
// miss. If `allow_footer_skip_retry` is true, a footer-validation corruption
|
||||
// retries once without requiring a footer.
|
||||
Status GetBlobFileReader(const ReadOptions& read_options,
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader,
|
||||
bool allow_footer_skip_retry = false);
|
||||
|
||||
// Opens a blob file reader without inserting it into the cache.
|
||||
Status OpenBlobFileReaderUncached(
|
||||
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
|
||||
const ReadOptions& read_options, uint64_t blob_file_number,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader,
|
||||
bool allow_footer_skip_retry = false);
|
||||
|
||||
// Makes finalized checksum metadata available to direct-write fallback reads
|
||||
// whose pinned Version predates the blob-file addition. Any cached
|
||||
// footer-less reader is evicted as part of the same per-file transition.
|
||||
void RegisterBlobFileChecksum(const BlobFileOpenInfo& blob_file);
|
||||
|
||||
// Stops retaining checksum metadata once no direct-write fallback can refer
|
||||
// to the file. Manifest-backed callers continue to provide it directly.
|
||||
void UnregisterBlobFileChecksum(uint64_t blob_file_number);
|
||||
|
||||
// Inserts a freshly opened uncached reader unless another thread already
|
||||
// cached the same blob file.
|
||||
Status InsertBlobFileReader(
|
||||
@@ -75,7 +58,7 @@ class BlobFileCache {
|
||||
// reader instead so a racing refresh cannot reintroduce an older active-file
|
||||
// view after the blob file grows.
|
||||
Status RefreshBlobFileReader(
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
uint64_t blob_file_number,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader,
|
||||
CacheHandleGuard<BlobFileReader>* cached_blob_file_reader);
|
||||
|
||||
@@ -89,11 +72,6 @@ class BlobFileCache {
|
||||
}
|
||||
|
||||
private:
|
||||
Status OpenBlobFileReader(const ReadOptions& read_options,
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
bool allow_footer_skip_retry,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader);
|
||||
|
||||
using CacheInterface =
|
||||
BasicTypedCacheInterface<BlobFileReader, CacheEntryRole::kMisc>;
|
||||
using TypedHandle = CacheInterface::TypedHandle;
|
||||
@@ -107,13 +85,6 @@ class BlobFileCache {
|
||||
HistogramImpl* blob_file_read_hist_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
|
||||
struct BlobFileChecksum {
|
||||
std::string value;
|
||||
std::string function_name;
|
||||
};
|
||||
std::unordered_map<uint64_t, BlobFileChecksum> blob_file_checksums_;
|
||||
mutable port::Mutex blob_file_checksums_mutex_;
|
||||
|
||||
static constexpr size_t kNumberOfMutexStripes = 1 << 7;
|
||||
};
|
||||
|
||||
|
||||
@@ -115,13 +115,13 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
|
||||
|
||||
// First try: reader should be opened and put in cache
|
||||
CacheHandleGuard<BlobFileReader> first;
|
||||
|
||||
const ReadOptions read_options;
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file, &first));
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
|
||||
&first));
|
||||
ASSERT_NE(first.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
@@ -129,8 +129,8 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
|
||||
// Second try: reader should be served from cache
|
||||
CacheHandleGuard<BlobFileReader> second;
|
||||
|
||||
ASSERT_OK(
|
||||
blob_file_cache.GetBlobFileReader(read_options, blob_file, &second));
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
|
||||
&second));
|
||||
ASSERT_NE(second.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
@@ -163,7 +163,6 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
|
||||
|
||||
CacheHandleGuard<BlobFileReader> first;
|
||||
CacheHandleGuard<BlobFileReader> second;
|
||||
@@ -173,15 +172,16 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
|
||||
"BlobFileCache::GetBlobFileReader:DoubleCheck", [&](void* /* arg */) {
|
||||
// Disabling sync points to prevent infinite recursion
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file,
|
||||
&second));
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options,
|
||||
blob_file_number, &second));
|
||||
ASSERT_NE(second.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file, &first));
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
|
||||
&first));
|
||||
ASSERT_NE(first.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
@@ -245,22 +245,21 @@ TEST_F(BlobFileCacheTest, RefreshBlobFileReaderPrefersLargestObservedFileSize) {
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
|
||||
|
||||
const ReadOptions read_options;
|
||||
std::unique_ptr<BlobFileReader> stale_reader;
|
||||
ASSERT_OK(blob_file_cache.OpenBlobFileReaderUncached(
|
||||
read_options, blob_file, &stale_reader,
|
||||
read_options, blob_file_number, &stale_reader,
|
||||
/*allow_footer_skip_retry=*/true));
|
||||
|
||||
std::unique_ptr<BlobFileReader> initial_cached_reader;
|
||||
ASSERT_OK(blob_file_cache.OpenBlobFileReaderUncached(
|
||||
read_options, blob_file, &initial_cached_reader,
|
||||
read_options, blob_file_number, &initial_cached_reader,
|
||||
/*allow_footer_skip_retry=*/true));
|
||||
|
||||
CacheHandleGuard<BlobFileReader> cached_reader;
|
||||
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(
|
||||
blob_file, &initial_cached_reader, &cached_reader));
|
||||
blob_file_number, &initial_cached_reader, &cached_reader));
|
||||
ASSERT_NE(cached_reader.GetValue(), nullptr);
|
||||
const uint64_t initial_file_size = cached_reader.GetValue()->GetFileSize();
|
||||
|
||||
@@ -270,20 +269,20 @@ TEST_F(BlobFileCacheTest, RefreshBlobFileReaderPrefersLargestObservedFileSize) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> fresh_reader;
|
||||
ASSERT_OK(blob_file_cache.OpenBlobFileReaderUncached(
|
||||
read_options, blob_file, &fresh_reader,
|
||||
read_options, blob_file_number, &fresh_reader,
|
||||
/*allow_footer_skip_retry=*/true));
|
||||
ASSERT_GT(fresh_reader->GetFileSize(), initial_file_size);
|
||||
|
||||
CacheHandleGuard<BlobFileReader> refreshed_reader;
|
||||
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(blob_file, &fresh_reader,
|
||||
&refreshed_reader));
|
||||
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(
|
||||
blob_file_number, &fresh_reader, &refreshed_reader));
|
||||
ASSERT_NE(refreshed_reader.GetValue(), nullptr);
|
||||
ASSERT_GT(refreshed_reader.GetValue()->GetFileSize(), initial_file_size);
|
||||
BlobFileReader* const largest_reader = refreshed_reader.GetValue();
|
||||
|
||||
CacheHandleGuard<BlobFileReader> preserved_reader;
|
||||
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(blob_file, &stale_reader,
|
||||
&preserved_reader));
|
||||
ASSERT_OK(blob_file_cache.RefreshBlobFileReader(
|
||||
blob_file_number, &stale_reader, &preserved_reader));
|
||||
ASSERT_NE(preserved_reader.GetValue(), nullptr);
|
||||
ASSERT_EQ(preserved_reader.GetValue(), largest_reader);
|
||||
ASSERT_EQ(stale_reader.get(), nullptr);
|
||||
@@ -313,13 +312,12 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_IOError) {
|
||||
|
||||
// Note: there is no blob file with the below number
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
|
||||
|
||||
CacheHandleGuard<BlobFileReader> reader;
|
||||
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(
|
||||
blob_file_cache.GetBlobFileReader(read_options, blob_file, &reader)
|
||||
blob_file_cache.GetBlobFileReader(read_options, blob_file_number, &reader)
|
||||
.IsIOError());
|
||||
ASSERT_EQ(reader.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
@@ -354,7 +352,6 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
const BlobFileOpenInfo blob_file{blob_file_number, Slice(), Slice()};
|
||||
|
||||
// Insert into cache should fail since it has zero capacity and
|
||||
// strict_capacity_limit is set
|
||||
@@ -362,7 +359,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
|
||||
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(
|
||||
blob_file_cache.GetBlobFileReader(read_options, blob_file, &reader)
|
||||
blob_file_cache.GetBlobFileReader(read_options, blob_file_number, &reader)
|
||||
.IsMemoryLimit());
|
||||
ASSERT_EQ(reader.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
|
||||
@@ -52,8 +52,6 @@ class SharedBlobFileMetaData {
|
||||
SharedBlobFileMetaData(SharedBlobFileMetaData&&) = delete;
|
||||
SharedBlobFileMetaData& operator=(SharedBlobFileMetaData&&) = delete;
|
||||
|
||||
~SharedBlobFileMetaData() = default;
|
||||
|
||||
uint64_t GetBlobFileSize() const;
|
||||
uint64_t GetBlobFileNumber() const { return blob_file_number_; }
|
||||
uint64_t GetTotalBlobCount() const { return total_blob_count_; }
|
||||
@@ -112,8 +110,6 @@ class BlobFileMetaData {
|
||||
BlobFileMetaData(BlobFileMetaData&&) = delete;
|
||||
BlobFileMetaData& operator=(BlobFileMetaData&&) = delete;
|
||||
|
||||
~BlobFileMetaData() = default;
|
||||
|
||||
const std::shared_ptr<SharedBlobFileMetaData>& GetSharedMeta() const {
|
||||
return shared_meta_;
|
||||
}
|
||||
|
||||
@@ -330,11 +330,6 @@ Status BlobFilePartitionManager::FinalizeBlobFile(
|
||||
*addition =
|
||||
BlobFileAddition(file_number, blob_count, total_blob_bytes,
|
||||
std::move(checksum_method), std::move(checksum_value));
|
||||
if (blob_file_cache_ != nullptr) {
|
||||
const BlobFileOpenInfo blob_file{file_number, addition->GetChecksumValue(),
|
||||
addition->GetChecksumMethod()};
|
||||
blob_file_cache_->RegisterBlobFileChecksum(blob_file);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -543,11 +538,10 @@ Status BlobFilePartitionManager::WriteBlob(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void BlobFilePartitionManager::RotateCurrentGeneration(uint64_t memtable_id) {
|
||||
void BlobFilePartitionManager::RotateCurrentGeneration() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
GenerationBatch batch;
|
||||
batch.memtable_id = memtable_id;
|
||||
batch.sealed_files = std::move(current_generation_sealed_files_);
|
||||
current_generation_sealed_files_.clear();
|
||||
|
||||
@@ -570,19 +564,8 @@ void BlobFilePartitionManager::RotateCurrentGeneration(uint64_t memtable_id) {
|
||||
pending_generations_.emplace_back(std::move(batch));
|
||||
}
|
||||
|
||||
BlobFilePartitionManager::GenerationBatch*
|
||||
BlobFilePartitionManager::FindPendingGeneration(uint64_t memtable_id) {
|
||||
for (auto& batch : pending_generations_) {
|
||||
if (batch.memtable_id == memtable_id) {
|
||||
return &batch;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Status BlobFilePartitionManager::PrepareFlushAdditions(
|
||||
const WriteOptions& write_options,
|
||||
const std::vector<uint64_t>& memtable_ids,
|
||||
const WriteOptions& write_options, size_t num_generations,
|
||||
std::vector<BlobFileAddition>* additions,
|
||||
std::vector<BlobFileGarbage>* garbages,
|
||||
std::vector<std::vector<uint64_t>>* generation_blob_file_numbers) {
|
||||
@@ -595,13 +578,13 @@ Status BlobFilePartitionManager::PrepareFlushAdditions(
|
||||
}
|
||||
|
||||
MutexLock lock(&mutex_);
|
||||
for (const uint64_t memtable_id : memtable_ids) {
|
||||
GenerationBatch* const batch_ptr = FindPendingGeneration(memtable_id);
|
||||
if (batch_ptr == nullptr) {
|
||||
return Status::Corruption(
|
||||
"Missing blob direct write generation metadata for flush");
|
||||
}
|
||||
GenerationBatch& batch = *batch_ptr;
|
||||
if (num_generations > pending_generations_.size()) {
|
||||
return Status::Corruption(
|
||||
"Missing blob direct write generation metadata for flush");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_generations; ++i) {
|
||||
GenerationBatch& batch = pending_generations_[i];
|
||||
while (!batch.deferred_files.empty()) {
|
||||
DeferredFile deferred = std::move(batch.deferred_files.front());
|
||||
batch.deferred_files.pop_front();
|
||||
@@ -680,22 +663,12 @@ Status BlobFilePartitionManager::MarkBlobWriteAsGarbage(uint64_t file_number,
|
||||
return Status::Corruption(message);
|
||||
}
|
||||
|
||||
void BlobFilePartitionManager::CommitFlushedGenerations(
|
||||
uint64_t earliest_unflushed_memtable_id) {
|
||||
std::vector<uint64_t> sealed_file_numbers;
|
||||
{
|
||||
MutexLock lock(&mutex_);
|
||||
while (!pending_generations_.empty() &&
|
||||
pending_generations_.front().memtable_id <
|
||||
earliest_unflushed_memtable_id) {
|
||||
for (const auto& sealed_file :
|
||||
pending_generations_.front().sealed_files) {
|
||||
sealed_file_numbers.push_back(sealed_file.addition.GetBlobFileNumber());
|
||||
}
|
||||
pending_generations_.pop_front();
|
||||
}
|
||||
void BlobFilePartitionManager::CommitPreparedGenerations(
|
||||
size_t num_generations) {
|
||||
MutexLock lock(&mutex_);
|
||||
while (num_generations-- > 0 && !pending_generations_.empty()) {
|
||||
pending_generations_.pop_front();
|
||||
}
|
||||
RemoveFilePartitionMappings(sealed_file_numbers);
|
||||
}
|
||||
|
||||
Status BlobFilePartitionManager::SyncAllOpenFiles(
|
||||
@@ -780,9 +753,6 @@ void BlobFilePartitionManager::UnprotectSealedBlobFileNumbers(
|
||||
// manifest-visible path. Evict it here so delayed protection does not
|
||||
// leave an obsolete blob reader behind until DB close.
|
||||
blob_file_cache_->Evict(file_number);
|
||||
if (file_to_partition_.find(file_number) == file_to_partition_.end()) {
|
||||
blob_file_cache_->UnregisterBlobFileChecksum(file_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -802,10 +772,6 @@ void BlobFilePartitionManager::RemoveFilePartitionMappings(
|
||||
// this file before it was sealed. Drop it now so future manifest-visible
|
||||
// reads reopen against the finalized on-disk size and footer state.
|
||||
blob_file_cache_->Evict(file_number);
|
||||
if (protected_blob_file_refs_.find(file_number) ==
|
||||
protected_blob_file_refs_.end()) {
|
||||
blob_file_cache_->UnregisterBlobFileChecksum(file_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -843,9 +809,9 @@ Status BlobFilePartitionManager::ResolveBlobDirectWriteIndex(
|
||||
}
|
||||
|
||||
Status s;
|
||||
const BlobFileOpenInfo blob_file{blob_idx.file_number(), Slice(), Slice()};
|
||||
CacheHandleGuard<BlobFileReader> reader;
|
||||
s = blob_file_cache->GetBlobFileReader(read_options, blob_file, &reader,
|
||||
s = blob_file_cache->GetBlobFileReader(read_options, blob_idx.file_number(),
|
||||
&reader,
|
||||
/*allow_footer_skip_retry=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -870,7 +836,7 @@ Status BlobFilePartitionManager::ResolveBlobDirectWriteIndex(
|
||||
|
||||
std::unique_ptr<BlobFileReader> fresh_reader;
|
||||
s = blob_file_cache->OpenBlobFileReaderUncached(
|
||||
read_options, blob_file, &fresh_reader,
|
||||
read_options, blob_idx.file_number(), &fresh_reader,
|
||||
/*allow_footer_skip_retry=*/true);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -884,7 +850,8 @@ Status BlobFilePartitionManager::ResolveBlobDirectWriteIndex(
|
||||
if (s.ok()) {
|
||||
blob_value->PinSelf(fresh_contents->data());
|
||||
CacheHandleGuard<BlobFileReader> ignored;
|
||||
blob_file_cache->RefreshBlobFileReader(blob_file, &fresh_reader, &ignored)
|
||||
blob_file_cache
|
||||
->RefreshBlobFileReader(blob_idx.file_number(), &fresh_reader, &ignored)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
|
||||
@@ -88,31 +88,27 @@ class BlobFilePartitionManager {
|
||||
const Slice& key,
|
||||
const WideColumns& columns) const;
|
||||
|
||||
// Move the current active partition files into the generation of the
|
||||
// memtable `memtable_id`, which just became immutable. Called from
|
||||
// SwitchMemtable() while DB mutex is held. No I/O is performed here.
|
||||
void RotateCurrentGeneration(uint64_t memtable_id);
|
||||
// Move the current active partition files into the next immutable
|
||||
// memtable-generation batch. Called from SwitchMemtable() while DB mutex is
|
||||
// held. No I/O is performed here.
|
||||
void RotateCurrentGeneration();
|
||||
|
||||
// Seal the queued generations of the memtables `memtable_ids` and return all
|
||||
// blob additions and initial-garbage updates that must be registered with
|
||||
// the flush of those memtables. Concurrent flushes pick different
|
||||
// memtables, so generations are matched by memtable ID, not by queue
|
||||
// position. Generations stay queued until CommitFlushedGenerations() removes
|
||||
// them. If `generation_blob_file_numbers` is non-null, it receives the
|
||||
// sealed blob file numbers for each memtable, in `memtable_ids` order.
|
||||
// Seal the first `num_generations` queued immutable generations and return
|
||||
// all blob additions and initial-garbage updates that must be registered
|
||||
// with the matching flush. Generations stay queued until
|
||||
// CommitPreparedGenerations() is called. If `generation_blob_file_numbers`
|
||||
// is non-null, it receives the sealed blob file numbers for each prepared
|
||||
// generation in the same FIFO order.
|
||||
Status PrepareFlushAdditions(const WriteOptions& write_options,
|
||||
const std::vector<uint64_t>& memtable_ids,
|
||||
size_t num_generations,
|
||||
std::vector<BlobFileAddition>* additions,
|
||||
std::vector<BlobFileGarbage>* garbages,
|
||||
std::vector<std::vector<uint64_t>>*
|
||||
generation_blob_file_numbers = nullptr);
|
||||
|
||||
// Remove the generations of the memtables older than
|
||||
// `earliest_unflushed_memtable_id`, whose flush results are installed, and
|
||||
// stop tracking their sealed files as manager-owned. A flush that finishes
|
||||
// before an older one is installed with it, or rolled back if it fails, so
|
||||
// the flush that prepared a generation cannot remove it on its own.
|
||||
void CommitFlushedGenerations(uint64_t earliest_unflushed_memtable_id);
|
||||
// Remove the first `num_generations` prepared immutable generations after
|
||||
// their blob-file additions and garbage edits were committed to MANIFEST.
|
||||
void CommitPreparedGenerations(size_t num_generations);
|
||||
|
||||
// In v1 flush-on-write mode each AddRecord flushes to the OS immediately, so
|
||||
// there is nothing extra to do for the non-sync write path.
|
||||
@@ -204,8 +200,6 @@ class BlobFilePartitionManager {
|
||||
};
|
||||
|
||||
struct GenerationBatch {
|
||||
// The memtable that became immutable when this generation was rotated.
|
||||
uint64_t memtable_id = 0;
|
||||
// Files that were still open when the memtable became immutable.
|
||||
std::deque<DeferredFile> deferred_files;
|
||||
// Files already sealed for this memtable generation.
|
||||
@@ -214,8 +208,6 @@ class BlobFilePartitionManager {
|
||||
|
||||
// Clears all active-file bookkeeping from a partition after sealing.
|
||||
void ResetPartitionState(Partition* partition);
|
||||
// Returns the queued generation of `memtable_id`, or null.
|
||||
GenerationBatch* FindPendingGeneration(uint64_t memtable_id);
|
||||
// Marks a blob file as manager-owned until it is committed or abandoned.
|
||||
void AddFilePartitionMapping(uint64_t file_number, uint32_t partition_idx);
|
||||
// Stops tracking a blob file after open/seal failure.
|
||||
|
||||
@@ -278,24 +278,22 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
return s;
|
||||
}
|
||||
|
||||
FSReadRequest read_req;
|
||||
read_req.offset = read_offset;
|
||||
read_req.len = read_size;
|
||||
if (file_reader->use_direct_io()) {
|
||||
read_req.scratch = nullptr;
|
||||
constexpr char* scratch = nullptr;
|
||||
AlignedBufferAllocationContext direct_io_context{direct_io_buffer};
|
||||
file_reader->Read(io_options, &read_req, &direct_io_context, &dbg);
|
||||
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, scratch,
|
||||
&direct_io_context, &dbg);
|
||||
} else {
|
||||
buf->reset(new char[read_size]);
|
||||
read_req.scratch = buf->get();
|
||||
file_reader->Read(io_options, &read_req, nullptr, &dbg);
|
||||
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, buf->get(),
|
||||
nullptr, &dbg);
|
||||
}
|
||||
s = std::move(read_req.status);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
*slice = read_req.result;
|
||||
|
||||
if (slice->size() != read_size) {
|
||||
return Status::Corruption("Incomplete blob read from " +
|
||||
|
||||
@@ -53,8 +53,6 @@ class BlobFileReader {
|
||||
|
||||
BlobFileReader(const BlobFileReader&) = delete;
|
||||
BlobFileReader& operator=(const BlobFileReader&) = delete;
|
||||
BlobFileReader(BlobFileReader&&) = delete;
|
||||
BlobFileReader& operator=(BlobFileReader&&) = delete;
|
||||
|
||||
~BlobFileReader();
|
||||
|
||||
|
||||
@@ -82,7 +82,8 @@ Status ReadAndVerifySimpleGen2BlobRecord(
|
||||
IODebugContext dbg;
|
||||
Status s = file->PrepareIOOptions(read_options, opts, &dbg);
|
||||
if (s.ok()) {
|
||||
s = file->Read(record_offset, record_size, opts, &result, buf, &dbg);
|
||||
s = file->Read(opts, record_offset, record_size, &result, buf, nullptr,
|
||||
&dbg);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -170,8 +171,8 @@ Status ReadSimpleGen2BlobRange(const ReadOptions& read_options,
|
||||
IODebugContext dbg;
|
||||
Status s = file->PrepareIOOptions(read_options, opts, &dbg);
|
||||
if (s.ok()) {
|
||||
s = file->Read(record_offset + range_offset, range_length, opts, &result,
|
||||
buf, &dbg);
|
||||
s = file->Read(opts, record_offset + range_offset, range_length, &result,
|
||||
buf, nullptr, &dbg);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -423,12 +424,12 @@ IOStatus WriteSimpleGen2BlobRecord(WritableFileWriter* file,
|
||||
return io_s;
|
||||
}
|
||||
if (!payload.empty()) {
|
||||
io_s = file->Append(payload, opts);
|
||||
io_s = file->Append(opts, payload);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
}
|
||||
return file->Append(Slice(trailer.data(), trailer.size()), opts);
|
||||
return file->Append(opts, Slice(trailer.data(), trailer.size()));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -55,10 +55,6 @@ class BlobIndex {
|
||||
|
||||
BlobIndex(const BlobIndex&) = default;
|
||||
BlobIndex& operator=(const BlobIndex&) = default;
|
||||
BlobIndex(BlobIndex&&) = default;
|
||||
BlobIndex& operator=(BlobIndex&&) = default;
|
||||
|
||||
~BlobIndex() = default;
|
||||
|
||||
bool IsInlined() const { return type_ == Type::kInlinedTTL; }
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Status BlobLogSequentialReader::ReadSlice(uint64_t size, Slice* slice,
|
||||
|
||||
StopWatch read_sw(clock_, statistics_, BLOB_DB_BLOB_FILE_READ_MICROS);
|
||||
// TODO: rate limit `BlobLogSequentialReader` reads (it appears unused?)
|
||||
Status s = file_->Read(next_byte_, static_cast<size_t>(size), IOOptions(),
|
||||
Status s = file_->Read(IOOptions(), next_byte_, static_cast<size_t>(size),
|
||||
slice, buf, nullptr);
|
||||
next_byte_ += size;
|
||||
if (!s.ok()) {
|
||||
|
||||
@@ -43,8 +43,6 @@ class BlobLogSequentialReader {
|
||||
// No copying allowed
|
||||
BlobLogSequentialReader(const BlobLogSequentialReader&) = delete;
|
||||
BlobLogSequentialReader& operator=(const BlobLogSequentialReader&) = delete;
|
||||
BlobLogSequentialReader(BlobLogSequentialReader&&) = delete;
|
||||
BlobLogSequentialReader& operator=(BlobLogSequentialReader&&) = delete;
|
||||
|
||||
~BlobLogSequentialReader();
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Status BlobLogWriter::Sync(const WriteOptions& write_options) {
|
||||
IOOptions opts;
|
||||
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (s.ok()) {
|
||||
s = use_fsync_ ? dest_->Fsync(opts) : dest_->Sync(opts);
|
||||
s = dest_->Sync(opts, use_fsync_);
|
||||
}
|
||||
if (s.ok()) {
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_SYNCED);
|
||||
@@ -58,7 +58,7 @@ Status BlobLogWriter::WriteHeader(const WriteOptions& write_options,
|
||||
IOOptions opts;
|
||||
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(str), opts);
|
||||
s = dest_->Append(opts, Slice(str));
|
||||
}
|
||||
if (s.ok()) {
|
||||
block_offset_ += str.size();
|
||||
@@ -92,7 +92,7 @@ Status BlobLogWriter::AppendFooter(const WriteOptions& write_options,
|
||||
IOOptions opts;
|
||||
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(str), opts);
|
||||
s = dest_->Append(opts, Slice(str));
|
||||
}
|
||||
if (s.ok()) {
|
||||
block_offset_ += str.size();
|
||||
@@ -181,13 +181,13 @@ Status BlobLogWriter::EmitPhysicalRecord(const WriteOptions& write_options,
|
||||
IOOptions opts;
|
||||
Status s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(headerbuf), opts);
|
||||
s = dest_->Append(opts, Slice(headerbuf));
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(key, opts);
|
||||
s = dest_->Append(opts, key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(val, opts);
|
||||
s = dest_->Append(opts, val);
|
||||
}
|
||||
if (do_flush_ && s.ok()) {
|
||||
s = dest_->Flush(opts);
|
||||
|
||||
@@ -37,8 +37,6 @@ class BlobLogWriter {
|
||||
// No copying allowed
|
||||
BlobLogWriter(const BlobLogWriter&) = delete;
|
||||
BlobLogWriter& operator=(const BlobLogWriter&) = delete;
|
||||
BlobLogWriter(BlobLogWriter&&) = delete;
|
||||
BlobLogWriter& operator=(BlobLogWriter&&) = delete;
|
||||
|
||||
~BlobLogWriter();
|
||||
|
||||
|
||||
@@ -14,16 +14,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Non-owning metadata needed when opening a blob file for reading. The
|
||||
// checksum slices must remain valid while this object is in use. A file without
|
||||
// finalized checksum metadata, such as an active direct-write blob file, uses
|
||||
// empty slices for both checksum fields.
|
||||
struct BlobFileOpenInfo {
|
||||
uint64_t file_number = 0;
|
||||
Slice file_checksum;
|
||||
Slice file_checksum_func_name;
|
||||
};
|
||||
|
||||
// A read Blob request structure for use in BlobSource::MultiGetBlob and
|
||||
// BlobFileReader::MultiGetBlob.
|
||||
struct BlobReadRequest {
|
||||
@@ -60,13 +50,10 @@ struct BlobReadRequest {
|
||||
BlobReadRequest() = default;
|
||||
BlobReadRequest(const BlobReadRequest& other) = default;
|
||||
BlobReadRequest& operator=(const BlobReadRequest& other) = default;
|
||||
BlobReadRequest(BlobReadRequest&& other) = default;
|
||||
BlobReadRequest& operator=(BlobReadRequest&& other) = default;
|
||||
~BlobReadRequest() = default;
|
||||
};
|
||||
|
||||
using BlobFileReadRequests =
|
||||
std::tuple<BlobFileOpenInfo, uint64_t /* file_size */,
|
||||
std::tuple<uint64_t /* file_number */, uint64_t /* file_size */,
|
||||
autovector<BlobReadRequest>>;
|
||||
|
||||
// A byte-range (partial) blob read request for the lazy blob-read multi
|
||||
@@ -110,7 +97,7 @@ struct BlobRangeReadRequest {
|
||||
};
|
||||
|
||||
using BlobFileRangeReadRequests =
|
||||
std::tuple<BlobFileOpenInfo, uint64_t /* file_size */,
|
||||
std::tuple<uint64_t /* file_number */, uint64_t /* file_size */,
|
||||
autovector<BlobRangeReadRequest>>;
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+26
-30
@@ -50,10 +50,9 @@ Status AppendBlobRefreshRetryFailure(const Status& stale_status,
|
||||
template <typename ReadFn>
|
||||
Status ReadBlobWithReaderRetry(BlobFileCache* blob_file_cache,
|
||||
const ReadOptions& read_options,
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
const ReadFn& read) {
|
||||
uint64_t file_number, const ReadFn& read) {
|
||||
CacheHandleGuard<BlobFileReader> blob_file_reader;
|
||||
Status s = blob_file_cache->GetBlobFileReader(read_options, blob_file,
|
||||
Status s = blob_file_cache->GetBlobFileReader(read_options, file_number,
|
||||
&blob_file_reader);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -67,11 +66,11 @@ Status ReadBlobWithReaderRetry(BlobFileCache* blob_file_cache,
|
||||
|
||||
const Status stale_status = s;
|
||||
blob_file_reader.Reset();
|
||||
blob_file_cache->Evict(blob_file.file_number);
|
||||
blob_file_cache->Evict(file_number);
|
||||
|
||||
std::unique_ptr<BlobFileReader> fresh_reader;
|
||||
s = blob_file_cache->OpenBlobFileReaderUncached(
|
||||
read_options, blob_file, &fresh_reader,
|
||||
read_options, file_number, &fresh_reader,
|
||||
/*allow_footer_skip_retry=*/false);
|
||||
if (!s.ok()) {
|
||||
return AppendBlobRefreshRetryFailure(stale_status, s);
|
||||
@@ -84,7 +83,7 @@ Status ReadBlobWithReaderRetry(BlobFileCache* blob_file_cache,
|
||||
|
||||
CacheHandleGuard<BlobFileReader> ignored_reader;
|
||||
blob_file_cache
|
||||
->RefreshBlobFileReader(blob_file, &fresh_reader, &ignored_reader)
|
||||
->RefreshBlobFileReader(file_number, &fresh_reader, &ignored_reader)
|
||||
.PermitUncheckedError();
|
||||
return s;
|
||||
}
|
||||
@@ -202,7 +201,7 @@ Status BlobSource::PutBlobIntoCache(
|
||||
const Status s = InsertEntryIntoCache(cache_key, blob->get(), &cache_handle,
|
||||
Cache::Priority::BOTTOM);
|
||||
if (s.ok()) {
|
||||
[[maybe_unused]] BlobContents* released_blob = blob->release();
|
||||
blob->release();
|
||||
|
||||
assert(cache_handle != nullptr);
|
||||
*cached_blob =
|
||||
@@ -273,9 +272,9 @@ Status BlobSource::InsertEntryIntoCache(const Slice& key, BlobContents* value,
|
||||
}
|
||||
|
||||
Status BlobSource::GetBlob(const ReadOptions& read_options,
|
||||
const Slice& user_key,
|
||||
const BlobFileOpenInfo& blob_file, uint64_t offset,
|
||||
uint64_t file_size, uint64_t value_size,
|
||||
const Slice& user_key, uint64_t file_number,
|
||||
uint64_t offset, uint64_t file_size,
|
||||
uint64_t value_size,
|
||||
CompressionType compression_type,
|
||||
FilePrefetchBuffer* prefetch_buffer,
|
||||
PinnableSlice* value, uint64_t* bytes_read) {
|
||||
@@ -283,8 +282,7 @@ Status BlobSource::GetBlob(const ReadOptions& read_options,
|
||||
|
||||
Status s;
|
||||
|
||||
const CacheKey cache_key =
|
||||
GetCacheKey(blob_file.file_number, file_size, offset);
|
||||
const CacheKey cache_key = GetCacheKey(file_number, file_size, offset);
|
||||
|
||||
CacheHandleGuard<BlobContents> blob_handle;
|
||||
|
||||
@@ -334,7 +332,8 @@ Status BlobSource::GetBlob(const ReadOptions& read_options,
|
||||
|
||||
uint64_t read_size = 0;
|
||||
s = ReadBlobWithReaderRetry(
|
||||
blob_file_cache_, read_options, blob_file, [&](BlobFileReader* reader) {
|
||||
blob_file_cache_, read_options, file_number,
|
||||
[&](BlobFileReader* reader) {
|
||||
if (compression_type != reader->GetCompressionType()) {
|
||||
return Status::Corruption(
|
||||
"Compression type mismatch when reading blob");
|
||||
@@ -378,8 +377,7 @@ Status BlobSource::GetBlob(const ReadOptions& read_options,
|
||||
}
|
||||
|
||||
Status BlobSource::GetBlobRange(const ReadOptions& read_options,
|
||||
const Slice& user_key,
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
const Slice& user_key, uint64_t file_number,
|
||||
uint64_t offset, uint64_t file_size,
|
||||
uint64_t value_size,
|
||||
CompressionType compression_type,
|
||||
@@ -396,8 +394,7 @@ Status BlobSource::GetBlobRange(const ReadOptions& read_options,
|
||||
|
||||
Status s;
|
||||
|
||||
const CacheKey cache_key =
|
||||
GetCacheKey(blob_file.file_number, file_size, offset);
|
||||
const CacheKey cache_key = GetCacheKey(file_number, file_size, offset);
|
||||
|
||||
CacheHandleGuard<BlobContents> blob_handle;
|
||||
|
||||
@@ -438,7 +435,8 @@ Status BlobSource::GetBlobRange(const ReadOptions& read_options,
|
||||
// case (offset/size mismatch surfacing as Corruption), not a payload
|
||||
// checksum failure.
|
||||
s = ReadBlobWithReaderRetry(
|
||||
blob_file_cache_, read_options, blob_file, [&](BlobFileReader* reader) {
|
||||
blob_file_cache_, read_options, file_number,
|
||||
[&](BlobFileReader* reader) {
|
||||
if (compression_type != reader->GetCompressionType()) {
|
||||
return Status::Corruption(
|
||||
"Compression type mismatch when reading blob");
|
||||
@@ -829,7 +827,7 @@ void BlobSource::MultiGetBlob(const ReadOptions& read_options,
|
||||
uint64_t total_bytes_read = 0;
|
||||
uint64_t bytes_read_in_file = 0;
|
||||
|
||||
for (auto& [blob_file, file_size, blob_reqs_in_file] : blob_reqs) {
|
||||
for (auto& [file_number, file_size, blob_reqs_in_file] : blob_reqs) {
|
||||
// sort blob_reqs_in_file by file offset.
|
||||
std::sort(
|
||||
blob_reqs_in_file.begin(), blob_reqs_in_file.end(),
|
||||
@@ -837,7 +835,7 @@ void BlobSource::MultiGetBlob(const ReadOptions& read_options,
|
||||
return lhs.offset < rhs.offset;
|
||||
});
|
||||
|
||||
MultiGetBlobFromOneFile(read_options, blob_file, file_size,
|
||||
MultiGetBlobFromOneFile(read_options, file_number, file_size,
|
||||
blob_reqs_in_file, &bytes_read_in_file);
|
||||
|
||||
total_bytes_read += bytes_read_in_file;
|
||||
@@ -849,7 +847,7 @@ void BlobSource::MultiGetBlob(const ReadOptions& read_options,
|
||||
}
|
||||
|
||||
void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
uint64_t file_number,
|
||||
uint64_t /*file_size*/,
|
||||
autovector<BlobReadRequest>& blob_reqs,
|
||||
uint64_t* bytes_read) {
|
||||
@@ -867,7 +865,6 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
|
||||
Mask cache_hit_mask = 0;
|
||||
|
||||
uint64_t total_bytes = 0;
|
||||
const uint64_t file_number = blob_file.file_number;
|
||||
const OffsetableCacheKey base_cache_key(db_id_, db_session_id_, file_number);
|
||||
|
||||
if (blob_cache_) {
|
||||
@@ -939,7 +936,7 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
|
||||
}
|
||||
|
||||
CacheHandleGuard<BlobFileReader> blob_file_reader;
|
||||
Status s = blob_file_cache_->GetBlobFileReader(read_options, blob_file,
|
||||
Status s = blob_file_cache_->GetBlobFileReader(read_options, file_number,
|
||||
&blob_file_reader);
|
||||
if (!s.ok()) {
|
||||
for (size_t i = 0; i < _blob_reqs.size(); ++i) {
|
||||
@@ -979,7 +976,7 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
|
||||
|
||||
std::unique_ptr<BlobFileReader> fresh_reader;
|
||||
s = blob_file_cache_->OpenBlobFileReaderUncached(
|
||||
read_options, blob_file, &fresh_reader,
|
||||
read_options, file_number, &fresh_reader,
|
||||
/*allow_footer_skip_retry=*/false);
|
||||
if (!s.ok()) {
|
||||
for (const auto& blob_req : _blob_reqs) {
|
||||
@@ -1040,7 +1037,7 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
|
||||
if (install_fresh_reader) {
|
||||
CacheHandleGuard<BlobFileReader> ignored_reader;
|
||||
blob_file_cache_
|
||||
->RefreshBlobFileReader(blob_file, &fresh_reader, &ignored_reader)
|
||||
->RefreshBlobFileReader(file_number, &fresh_reader, &ignored_reader)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
@@ -1106,7 +1103,7 @@ void BlobSource::MultiGetBlobRange(
|
||||
uint64_t total_bytes_read = 0;
|
||||
uint64_t bytes_read_in_file = 0;
|
||||
|
||||
for (auto& [blob_file, file_size, blob_reqs_in_file] : blob_reqs) {
|
||||
for (auto& [file_number, file_size, blob_reqs_in_file] : blob_reqs) {
|
||||
// Sort by effective read offset so the file system layer can coalesce
|
||||
// adjacent sub-range reads within one MultiRead.
|
||||
std::sort(blob_reqs_in_file.begin(), blob_reqs_in_file.end(),
|
||||
@@ -1116,7 +1113,7 @@ void BlobSource::MultiGetBlobRange(
|
||||
rhs.offset + rhs.range_offset;
|
||||
});
|
||||
|
||||
MultiGetBlobRangeFromOneFile(read_options, blob_file, file_size,
|
||||
MultiGetBlobRangeFromOneFile(read_options, file_number, file_size,
|
||||
blob_reqs_in_file, &bytes_read_in_file);
|
||||
|
||||
total_bytes_read += bytes_read_in_file;
|
||||
@@ -1129,7 +1126,7 @@ void BlobSource::MultiGetBlobRange(
|
||||
}
|
||||
|
||||
void BlobSource::MultiGetBlobRangeFromOneFile(
|
||||
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
|
||||
const ReadOptions& read_options, uint64_t file_number,
|
||||
uint64_t /*file_size*/, autovector<BlobRangeReadRequest>& blob_reqs,
|
||||
uint64_t* bytes_read) {
|
||||
const size_t num_blobs = blob_reqs.size();
|
||||
@@ -1143,7 +1140,6 @@ void BlobSource::MultiGetBlobRangeFromOneFile(
|
||||
Mask cache_hit_mask = 0;
|
||||
|
||||
uint64_t total_bytes = 0;
|
||||
const uint64_t file_number = blob_file.file_number;
|
||||
const OffsetableCacheKey base_cache_key(db_id_, db_session_id_, file_number);
|
||||
|
||||
// Probe the whole-value cache per request; on a hit, slice the requested
|
||||
@@ -1200,7 +1196,7 @@ void BlobSource::MultiGetBlobRangeFromOneFile(
|
||||
// is simply surfaced per request. Hence no reader-refresh retry and no
|
||||
// cache-fill (a partial value cannot represent the whole-record cache entry).
|
||||
CacheHandleGuard<BlobFileReader> blob_file_reader;
|
||||
Status s = blob_file_cache_->GetBlobFileReader(read_options, blob_file,
|
||||
Status s = blob_file_cache_->GetBlobFileReader(read_options, file_number,
|
||||
&blob_file_reader);
|
||||
if (!s.ok()) {
|
||||
for (auto& blob_req : _blob_reqs) {
|
||||
|
||||
+10
-16
@@ -67,8 +67,6 @@ class BlobSource {
|
||||
|
||||
BlobSource(const BlobSource&) = delete;
|
||||
BlobSource& operator=(const BlobSource&) = delete;
|
||||
BlobSource(BlobSource&&) = delete;
|
||||
BlobSource& operator=(BlobSource&&) = delete;
|
||||
|
||||
~BlobSource();
|
||||
|
||||
@@ -82,9 +80,8 @@ class BlobSource {
|
||||
// sets "*bytes_read" to the size of on-disk (possibly compressed) blob
|
||||
// record.
|
||||
Status GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
const BlobFileOpenInfo& blob_file, uint64_t offset,
|
||||
uint64_t file_size, uint64_t value_size,
|
||||
CompressionType compression_type,
|
||||
uint64_t file_number, uint64_t offset, uint64_t file_size,
|
||||
uint64_t value_size, CompressionType compression_type,
|
||||
FilePrefetchBuffer* prefetch_buffer, PinnableSlice* value,
|
||||
uint64_t* bytes_read);
|
||||
|
||||
@@ -108,11 +105,10 @@ class BlobSource {
|
||||
// *bytes_read (when non-null) is the number of bytes read from the file; on a
|
||||
// hit it is 0.
|
||||
Status GetBlobRange(const ReadOptions& read_options, const Slice& user_key,
|
||||
const BlobFileOpenInfo& blob_file, uint64_t offset,
|
||||
uint64_t file_size, uint64_t value_size,
|
||||
CompressionType compression_type, uint64_t range_offset,
|
||||
size_t range_length, PinnableSlice* value,
|
||||
uint64_t* bytes_read);
|
||||
uint64_t file_number, uint64_t offset, uint64_t file_size,
|
||||
uint64_t value_size, CompressionType compression_type,
|
||||
uint64_t range_offset, size_t range_length,
|
||||
PinnableSlice* value, uint64_t* bytes_read);
|
||||
|
||||
// Reads a SimpleGen2Blob payload (see db/blob/blob_gen2_format.h) through the
|
||||
// blob value cache and BLOB_DB_* statistics. This is the counterpart to
|
||||
@@ -236,8 +232,7 @@ class BlobSource {
|
||||
// "*bytes_read" to the total size of on-disk (possibly compressed) blob
|
||||
// records.
|
||||
void MultiGetBlobFromOneFile(const ReadOptions& read_options,
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
uint64_t file_size,
|
||||
uint64_t file_number, uint64_t file_size,
|
||||
autovector<BlobReadRequest>& blob_reqs,
|
||||
uint64_t* bytes_read);
|
||||
|
||||
@@ -255,15 +250,14 @@ class BlobSource {
|
||||
// Byte-range (partial) multi-read counterpart of MultiGetBlobFromOneFile for
|
||||
// a single blob file. See MultiGetBlobRange.
|
||||
void MultiGetBlobRangeFromOneFile(const ReadOptions& read_options,
|
||||
const BlobFileOpenInfo& blob_file,
|
||||
uint64_t file_size,
|
||||
uint64_t file_number, uint64_t file_size,
|
||||
autovector<BlobRangeReadRequest>& blob_reqs,
|
||||
uint64_t* bytes_read);
|
||||
|
||||
inline Status GetBlobFileReader(
|
||||
const ReadOptions& read_options, const BlobFileOpenInfo& blob_file,
|
||||
const ReadOptions& read_options, uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader) {
|
||||
return blob_file_cache_->GetBlobFileReader(read_options, blob_file,
|
||||
return blob_file_cache_->GetBlobFileReader(read_options, blob_file_number,
|
||||
blob_file_reader);
|
||||
}
|
||||
|
||||
|
||||
+75
-93
@@ -104,10 +104,6 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
&checksum_method, &checksum_value));
|
||||
}
|
||||
|
||||
BlobFileOpenInfo OpenInfoWithoutChecksum(uint64_t file_number) {
|
||||
return BlobFileOpenInfo{file_number, Slice(), Slice()};
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class BlobSourceTest : public DBTestBase {
|
||||
@@ -216,10 +212,10 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
|
||||
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
|
||||
prefetch_buffer, &values[i], &bytes_read));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i]);
|
||||
ASSERT_TRUE(values[i].IsPinned());
|
||||
ASSERT_EQ(bytes_read,
|
||||
@@ -255,10 +251,10 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
|
||||
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
|
||||
prefetch_buffer, &values[i], &bytes_read));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i]);
|
||||
ASSERT_TRUE(values[i].IsPinned());
|
||||
ASSERT_EQ(bytes_read,
|
||||
@@ -303,10 +299,10 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
|
||||
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
|
||||
prefetch_buffer, &values[i], &bytes_read));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i]);
|
||||
ASSERT_TRUE(values[i].IsPinned());
|
||||
ASSERT_EQ(bytes_read,
|
||||
@@ -343,10 +339,10 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
ASSERT_TRUE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
|
||||
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
|
||||
prefetch_buffer, &values[i], &bytes_read));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i]);
|
||||
ASSERT_TRUE(values[i].IsPinned());
|
||||
ASSERT_EQ(bytes_read,
|
||||
@@ -389,8 +385,7 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_TRUE(blob_source
|
||||
.GetBlob(read_options, keys[i],
|
||||
OpenInfoWithoutChecksum(blob_file_number),
|
||||
.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read)
|
||||
@@ -432,8 +427,7 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_TRUE(blob_source
|
||||
.GetBlob(read_options, keys[i],
|
||||
OpenInfoWithoutChecksum(file_number),
|
||||
.GetBlob(read_options, keys[i], file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer, &values[i],
|
||||
&bytes_read)
|
||||
@@ -528,8 +522,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
compression, blob_offsets, blob_sizes);
|
||||
|
||||
CacheHandleGuard<BlobFileReader> blob_file_reader;
|
||||
ASSERT_OK(blob_source.GetBlobFileReader(
|
||||
read_options, OpenInfoWithoutChecksum(file_number), &blob_file_reader));
|
||||
ASSERT_OK(blob_source.GetBlobFileReader(read_options, file_number,
|
||||
&blob_file_reader));
|
||||
ASSERT_NE(blob_file_reader.GetValue(), nullptr);
|
||||
|
||||
const uint64_t file_size = blob_file_reader.GetValue()->GetFileSize();
|
||||
@@ -547,10 +541,10 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[i], OpenInfoWithoutChecksum(file_number),
|
||||
blob_offsets[i], file_size, blob_sizes[i], compression,
|
||||
nullptr /*prefetch_buffer*/, &values[i], &bytes_read));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
compression, nullptr /*prefetch_buffer*/,
|
||||
&values[i], &bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i] /*uncompressed blob*/);
|
||||
ASSERT_NE(values[i].size(), blob_sizes[i] /*compressed size*/);
|
||||
ASSERT_EQ(bytes_read,
|
||||
@@ -570,10 +564,10 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
blob_offsets[i]));
|
||||
|
||||
// Compressed blob size is passed in GetBlob
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[i], OpenInfoWithoutChecksum(file_number),
|
||||
blob_offsets[i], file_size, blob_sizes[i], compression,
|
||||
nullptr /*prefetch_buffer*/, &values[i], &bytes_read));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
compression, nullptr /*prefetch_buffer*/,
|
||||
&values[i], &bytes_read));
|
||||
ASSERT_EQ(values[i], blobs[i] /*uncompressed blob*/);
|
||||
ASSERT_NE(values[i].size(), blob_sizes[i] /*compressed size*/);
|
||||
ASSERT_EQ(bytes_read,
|
||||
@@ -680,8 +674,7 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
|
||||
keys[j], blob_offsets[j], blob_sizes[j], kNoCompression,
|
||||
&value_buf[i * num_blobs + j], &statuses_buf[i * num_blobs + j]);
|
||||
}
|
||||
blob_reqs.emplace_back(OpenInfoWithoutChecksum(file_number), file_size,
|
||||
blob_reqs_in_file[i]);
|
||||
blob_reqs.emplace_back(file_number, file_size, blob_reqs_in_file[i]);
|
||||
}
|
||||
|
||||
get_perf_context()->Reset();
|
||||
@@ -738,8 +731,7 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
|
||||
}
|
||||
|
||||
// Add a fake multi-get blob request.
|
||||
blob_reqs.emplace_back(OpenInfoWithoutChecksum(fake_file_number), file_size,
|
||||
fake_blob_reqs_in_file);
|
||||
blob_reqs.emplace_back(fake_file_number, file_size, fake_blob_reqs_in_file);
|
||||
|
||||
blob_source.MultiGetBlob(read_options, blob_reqs, &bytes_read);
|
||||
|
||||
@@ -873,9 +865,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
statistics->Reset().PermitUncheckedError();
|
||||
|
||||
// Get half of blobs
|
||||
blob_source.MultiGetBlobFromOneFile(
|
||||
read_options, OpenInfoWithoutChecksum(blob_file_number), file_size,
|
||||
blob_reqs, &bytes_read);
|
||||
blob_source.MultiGetBlobFromOneFile(read_options, blob_file_number,
|
||||
file_size, blob_reqs, &bytes_read);
|
||||
|
||||
uint64_t fs_read_bytes = 0;
|
||||
uint64_t ca_read_bytes = 0;
|
||||
@@ -920,10 +911,10 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
ASSERT_FALSE(blob_source.TEST_BlobInCache(blob_file_number, file_size,
|
||||
blob_offsets[i]));
|
||||
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[i], OpenInfoWithoutChecksum(blob_file_number),
|
||||
blob_offsets[i], file_size, blob_sizes[i], kNoCompression,
|
||||
prefetch_buffer, &value_buf[i], &bytes_read));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[i], blob_file_number,
|
||||
blob_offsets[i], file_size, blob_sizes[i],
|
||||
kNoCompression, prefetch_buffer,
|
||||
&value_buf[i], &bytes_read));
|
||||
ASSERT_EQ(value_buf[i], blobs[i]);
|
||||
ASSERT_TRUE(value_buf[i].IsPinned());
|
||||
ASSERT_EQ(bytes_read,
|
||||
@@ -944,9 +935,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
kNoCompression, &value_buf[i], &statuses_buf[i]);
|
||||
}
|
||||
|
||||
blob_source.MultiGetBlobFromOneFile(
|
||||
read_options, OpenInfoWithoutChecksum(blob_file_number), file_size,
|
||||
blob_reqs, &bytes_read);
|
||||
blob_source.MultiGetBlobFromOneFile(read_options, blob_file_number,
|
||||
file_size, blob_reqs, &bytes_read);
|
||||
|
||||
uint64_t blob_bytes = 0;
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
@@ -995,9 +985,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
get_perf_context()->Reset();
|
||||
statistics->Reset().PermitUncheckedError();
|
||||
|
||||
blob_source.MultiGetBlobFromOneFile(
|
||||
read_options, OpenInfoWithoutChecksum(blob_file_number), file_size,
|
||||
blob_reqs, &bytes_read);
|
||||
blob_source.MultiGetBlobFromOneFile(read_options, blob_file_number,
|
||||
file_size, blob_reqs, &bytes_read);
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
ASSERT_TRUE(statuses_buf[i].IsIncomplete());
|
||||
@@ -1040,9 +1029,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
get_perf_context()->Reset();
|
||||
statistics->Reset().PermitUncheckedError();
|
||||
|
||||
blob_source.MultiGetBlobFromOneFile(
|
||||
read_options, OpenInfoWithoutChecksum(non_existing_file_number),
|
||||
file_size, blob_reqs, &bytes_read);
|
||||
blob_source.MultiGetBlobFromOneFile(read_options, non_existing_file_number,
|
||||
file_size, blob_reqs, &bytes_read);
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
ASSERT_TRUE(statuses_buf[i].IsIOError());
|
||||
@@ -1116,9 +1104,8 @@ TEST_F(BlobSourceTest, GetBlobPreservesCorruptionDetailsWhenRefreshOpenFails) {
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[0], OpenInfoWithoutChecksum(blob_file_number),
|
||||
blob_offsets[0], file_size, blob_sizes[0], kNoCompression,
|
||||
prefetch_buffer, &value, &bytes_read));
|
||||
read_options, keys[0], blob_file_number, blob_offsets[0], file_size,
|
||||
blob_sizes[0], kNoCompression, prefetch_buffer, &value, &bytes_read));
|
||||
ASSERT_EQ(value, blobs[0]);
|
||||
|
||||
const std::string blob_file_path =
|
||||
@@ -1127,10 +1114,10 @@ TEST_F(BlobSourceTest, GetBlobPreservesCorruptionDetailsWhenRefreshOpenFails) {
|
||||
|
||||
PinnableSlice invalid_value;
|
||||
bytes_read = 0;
|
||||
Status s = blob_source.GetBlob(
|
||||
read_options, keys[0], OpenInfoWithoutChecksum(blob_file_number),
|
||||
file_size, file_size, blob_sizes[0], kNoCompression, prefetch_buffer,
|
||||
&invalid_value, &bytes_read);
|
||||
Status s =
|
||||
blob_source.GetBlob(read_options, keys[0], blob_file_number, file_size,
|
||||
file_size, blob_sizes[0], kNoCompression,
|
||||
prefetch_buffer, &invalid_value, &bytes_read);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
@@ -1191,9 +1178,8 @@ TEST_F(BlobSourceTest,
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[0], OpenInfoWithoutChecksum(blob_file_number),
|
||||
blob_offsets[0], file_size, blob_sizes[0], kNoCompression,
|
||||
prefetch_buffer, &value, &bytes_read));
|
||||
read_options, keys[0], blob_file_number, blob_offsets[0], file_size,
|
||||
blob_sizes[0], kNoCompression, prefetch_buffer, &value, &bytes_read));
|
||||
ASSERT_EQ(value, blobs[0]);
|
||||
|
||||
const std::string blob_file_path =
|
||||
@@ -1207,9 +1193,8 @@ TEST_F(BlobSourceTest,
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
bytes_read = 0;
|
||||
blob_source.MultiGetBlobFromOneFile(read_options,
|
||||
OpenInfoWithoutChecksum(blob_file_number),
|
||||
file_size, blob_reqs, &bytes_read);
|
||||
blob_source.MultiGetBlobFromOneFile(read_options, blob_file_number, file_size,
|
||||
blob_reqs, &bytes_read);
|
||||
ASSERT_TRUE(statuses[0].IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
@@ -1318,8 +1303,8 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
|
||||
CacheHandleGuard<BlobFileReader> file_reader;
|
||||
ReadOptions read_options;
|
||||
ASSERT_OK(blob_source.GetBlobFileReader(
|
||||
read_options, OpenInfoWithoutChecksum(file_number), &file_reader));
|
||||
ASSERT_OK(
|
||||
blob_source.GetBlobFileReader(read_options, file_number, &file_reader));
|
||||
ASSERT_NE(file_reader.GetValue(), nullptr);
|
||||
const uint64_t file_size = file_reader.GetValue()->GetFileSize();
|
||||
ASSERT_EQ(file_reader.GetValue()->GetCompressionType(), kNoCompression);
|
||||
@@ -1337,8 +1322,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
get_perf_context()->Reset();
|
||||
|
||||
// key0 should be filled to the primary cache from the blob file.
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[0],
|
||||
OpenInfoWithoutChecksum(file_number),
|
||||
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 */));
|
||||
@@ -1347,18 +1331,17 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
|
||||
// key0 should be evicted and key0's dummy item is inserted into secondary
|
||||
// cache. key1 should be filled to the primary cache from the blob file.
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[1], OpenInfoWithoutChecksum(file_number),
|
||||
blob_offsets[1], file_size, blob_sizes[1], kNoCompression,
|
||||
nullptr /* prefetch_buffer */, &values[1], nullptr /* bytes_read */));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[1], file_number,
|
||||
blob_offsets[1], file_size, blob_sizes[1],
|
||||
kNoCompression, nullptr /* prefetch_buffer */,
|
||||
&values[1], nullptr /* bytes_read */));
|
||||
|
||||
// Release cache handle
|
||||
values[1].Reset();
|
||||
|
||||
// key0 should be filled to the primary cache from the blob file. key1
|
||||
// should be evicted and key1's dummy item is inserted into secondary cache.
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[0],
|
||||
OpenInfoWithoutChecksum(file_number),
|
||||
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 */));
|
||||
@@ -1371,10 +1354,10 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
|
||||
// key0 should be evicted and is inserted into secondary cache.
|
||||
// key1 should be filled to the primary cache from the blob file.
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[1], OpenInfoWithoutChecksum(file_number),
|
||||
blob_offsets[1], file_size, blob_sizes[1], kNoCompression,
|
||||
nullptr /* prefetch_buffer */, &values[1], nullptr /* bytes_read */));
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[1], file_number,
|
||||
blob_offsets[1], file_size, blob_sizes[1],
|
||||
kNoCompression, nullptr /* prefetch_buffer */,
|
||||
&values[1], nullptr /* bytes_read */));
|
||||
ASSERT_EQ(values[1], blobs[1]);
|
||||
ASSERT_TRUE(
|
||||
blob_source.TEST_BlobInCache(file_number, file_size, blob_offsets[1]));
|
||||
@@ -1439,10 +1422,9 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
// fetch key0 from the blob file to the primary cache.
|
||||
// key1 is evicted and inserted into the secondary cache.
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[0], OpenInfoWithoutChecksum(file_number),
|
||||
blob_offsets[0], file_size, blob_sizes[0], kNoCompression,
|
||||
nullptr /* prefetch_buffer */, values.data(),
|
||||
nullptr /* bytes_read */));
|
||||
read_options, keys[0], file_number, blob_offsets[0], file_size,
|
||||
blob_sizes[0], kNoCompression, nullptr /* prefetch_buffer */,
|
||||
values.data(), nullptr /* bytes_read */));
|
||||
ASSERT_EQ(values[0], blobs[0]);
|
||||
|
||||
// Release cache handle
|
||||
@@ -1624,8 +1606,8 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
|
||||
|
||||
for (size_t i = 0; i < kNumBlobs; ++i) {
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys_[i], OpenInfoWithoutChecksum(kBlobFileNumber),
|
||||
blob_offsets[i], blob_file_size_, blob_sizes[i], kNoCompression,
|
||||
read_options, keys_[i], kBlobFileNumber, blob_offsets[i],
|
||||
blob_file_size_, blob_sizes[i], kNoCompression,
|
||||
nullptr /* prefetch_buffer */, &values[i], nullptr /* bytes_read */));
|
||||
ASSERT_EQ(cache_res_mgr->GetTotalReservedCacheSize(), 0);
|
||||
ASSERT_EQ(cache_res_mgr->GetTotalMemoryUsed(), 0);
|
||||
@@ -1643,8 +1625,8 @@ TEST_F(BlobSourceCacheReservationTest, SimpleCacheReservation) {
|
||||
uint64_t blob_bytes = 0;
|
||||
for (size_t i = 0; i < kNumBlobs; ++i) {
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys_[i], OpenInfoWithoutChecksum(kBlobFileNumber),
|
||||
blob_offsets[i], blob_file_size_, blob_sizes[i], kNoCompression,
|
||||
read_options, keys_[i], kBlobFileNumber, blob_offsets[i],
|
||||
blob_file_size_, blob_sizes[i], kNoCompression,
|
||||
nullptr /* prefetch_buffer */, &values[i], nullptr /* bytes_read */));
|
||||
|
||||
size_t charge = 0;
|
||||
@@ -1746,8 +1728,8 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
|
||||
|
||||
for (size_t i = 0; i < kNumBlobs; ++i) {
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys_[i], OpenInfoWithoutChecksum(kBlobFileNumber),
|
||||
blob_offsets[i], blob_file_size_, blob_sizes[i], kNoCompression,
|
||||
read_options, keys_[i], kBlobFileNumber, blob_offsets[i],
|
||||
blob_file_size_, blob_sizes[i], kNoCompression,
|
||||
nullptr /* prefetch_buffer */, &values[i], nullptr /* bytes_read */));
|
||||
ASSERT_EQ(cache_res_mgr->GetTotalReservedCacheSize(), 0);
|
||||
ASSERT_EQ(cache_res_mgr->GetTotalMemoryUsed(), 0);
|
||||
@@ -1762,8 +1744,8 @@ TEST_F(BlobSourceCacheReservationTest, IncreaseCacheReservation) {
|
||||
uint64_t blob_bytes = 0;
|
||||
for (size_t i = 0; i < kNumBlobs; ++i) {
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys_[i], OpenInfoWithoutChecksum(kBlobFileNumber),
|
||||
blob_offsets[i], blob_file_size_, blob_sizes[i], kNoCompression,
|
||||
read_options, keys_[i], kBlobFileNumber, blob_offsets[i],
|
||||
blob_file_size_, blob_sizes[i], kNoCompression,
|
||||
nullptr /* prefetch_buffer */, &values[i], nullptr /* bytes_read */));
|
||||
|
||||
// Release cache handle
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@@ -20,19 +19,15 @@
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/db_with_timestamp_test_util.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/trace_reader_writer.h"
|
||||
#include "rocksdb/trace_record.h"
|
||||
#include "rocksdb/utilities/replayer.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/file_checksum_helper.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -43,49 +38,6 @@ class DBBlobBasicTest : public DBTestBase {
|
||||
: DBTestBase("db_blob_basic_test", /* env_do_fsync */ false) {}
|
||||
};
|
||||
|
||||
class BlobFileChecksumCapturingFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit BlobFileChecksumCapturingFS(const std::shared_ptr<FileSystem>& base)
|
||||
: FileSystemWrapper(base) {}
|
||||
|
||||
static const char* kClassName() { return "BlobFileChecksumCapturingFS"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
if (fname.find(".blob") != std::string::npos) {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
file_checksum_ = opts.file_checksum;
|
||||
file_checksum_func_name_ = opts.file_checksum_func_name;
|
||||
++capture_count_;
|
||||
}
|
||||
return target()->NewRandomAccessFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
std::string GetFileChecksum() {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
return file_checksum_;
|
||||
}
|
||||
|
||||
std::string GetFileChecksumFuncName() {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
return file_checksum_func_name_;
|
||||
}
|
||||
|
||||
int GetCaptureCount() {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
return capture_count_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mu_;
|
||||
std::string file_checksum_;
|
||||
std::string file_checksum_func_name_;
|
||||
int capture_count_ = 0;
|
||||
};
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetBlob) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
@@ -113,41 +65,6 @@ TEST_F(DBBlobBasicTest, GetBlob) {
|
||||
.IsIncomplete());
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, BlobFileChecksumInFileOptions) {
|
||||
auto capturing_fs =
|
||||
std::make_shared<BlobFileChecksumCapturingFS>(env_->GetFileSystem());
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, capturing_fs));
|
||||
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.env = env.get();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
Defer close_db([this]() { Close(); });
|
||||
Reopen(options);
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob_value[] = "blob_value";
|
||||
ASSERT_OK(Put(key, blob_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::vector<ColumnFamilyMetaData> column_family_metadata;
|
||||
db_->GetAllColumnFamilyMetaData(&column_family_metadata);
|
||||
ASSERT_EQ(column_family_metadata.size(), 1);
|
||||
ASSERT_EQ(column_family_metadata[0].blob_files.size(), 1);
|
||||
const BlobMetaData& blob_metadata = column_family_metadata[0].blob_files[0];
|
||||
ASSERT_FALSE(blob_metadata.checksum_value.empty());
|
||||
ASSERT_EQ(blob_metadata.checksum_method, "FileChecksumCrc32c");
|
||||
|
||||
ASSERT_EQ(Get(key), blob_value);
|
||||
|
||||
ASSERT_GT(capturing_fs->GetCaptureCount(), 0);
|
||||
ASSERT_EQ(capturing_fs->GetFileChecksum(), blob_metadata.checksum_value);
|
||||
ASSERT_EQ(capturing_fs->GetFileChecksumFuncName(),
|
||||
blob_metadata.checksum_method);
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, BlobFileWritableFileMaxBufferSize) {
|
||||
constexpr uint64_t kBlobWriterBufferSize = 128 * 1024;
|
||||
|
||||
@@ -837,15 +754,6 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
ASSERT_EQ(values[2], third_value);
|
||||
}
|
||||
|
||||
// Load the SST blocks into the block cache. The inlined key does not read
|
||||
// the blob file, so the blob cache stays empty.
|
||||
{
|
||||
ReadOptions fill_block_cache_options;
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(fill_block_cache_options, first_key, &value));
|
||||
ASSERT_EQ(value, first_value);
|
||||
}
|
||||
|
||||
// Try again with no I/O allowed. The first (inlined) value should be
|
||||
// successfully read; however, the two blob values could only be read from the
|
||||
// blob file, so for those the read should return Incomplete.
|
||||
@@ -1729,13 +1637,11 @@ TEST_F(DBBlobBasicTest, PropertiesMultiVersion) {
|
||||
|
||||
class DBBlobBasicIOErrorTest : public DBBlobBasicTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
~DBBlobBasicIOErrorTest() override { Close(); }
|
||||
|
||||
protected:
|
||||
DBBlobBasicIOErrorTest() : sync_point_(GetParam()) {
|
||||
fault_injection_env_.reset(new FaultInjectionTestEnv(env_));
|
||||
}
|
||||
~DBBlobBasicIOErrorTest() { Close(); }
|
||||
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env_;
|
||||
std::string sync_point_;
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
@@ -42,8 +40,6 @@
|
||||
#include "rocksdb/utilities/replayer.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/file_checksum_helper.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -125,59 +121,6 @@ bool IsBlobFilePath(const std::string& fname) {
|
||||
file_type == kBlobFile;
|
||||
}
|
||||
|
||||
class BlobFileChecksumCapturingFileSystem : public FileSystemWrapper {
|
||||
public:
|
||||
explicit BlobFileChecksumCapturingFileSystem(
|
||||
const std::shared_ptr<FileSystem>& fs)
|
||||
: FileSystemWrapper(fs) {}
|
||||
|
||||
static const char* kClassName() {
|
||||
return "BlobFileChecksumCapturingFileSystem";
|
||||
}
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
if (IsBlobFilePath(fname)) {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
file_checksum_ = opts.file_checksum;
|
||||
file_checksum_func_name_ = opts.file_checksum_func_name;
|
||||
++capture_count_;
|
||||
}
|
||||
return target()->NewRandomAccessFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
file_checksum_.clear();
|
||||
file_checksum_func_name_.clear();
|
||||
capture_count_ = 0;
|
||||
}
|
||||
|
||||
std::string GetFileChecksum() const {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
return file_checksum_;
|
||||
}
|
||||
|
||||
std::string GetFileChecksumFuncName() const {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
return file_checksum_func_name_;
|
||||
}
|
||||
|
||||
int GetCaptureCount() const {
|
||||
std::lock_guard<std::mutex> lock(mu_);
|
||||
return capture_count_;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex mu_;
|
||||
std::string file_checksum_;
|
||||
std::string file_checksum_func_name_;
|
||||
int capture_count_ = 0;
|
||||
};
|
||||
|
||||
// Keeps track of when an active blob file stops being writer-visible so the
|
||||
// test filesystem can model current-size visibility only while the file remains
|
||||
// active.
|
||||
@@ -1462,9 +1405,7 @@ TEST_F(DBBlobDirectWriteTest,
|
||||
std::vector<BlobFileGarbage> garbages1;
|
||||
// Simulate a flush retry: prepare once, skip commit, then prepare the same
|
||||
// generation again and verify it reuses the already sealed blob file.
|
||||
const std::vector<uint64_t> memtable_ids = {
|
||||
cfh->cfd()->imm()->GetEarliestMemTableID()};
|
||||
ASSERT_OK(mgr->PrepareFlushAdditions(WriteOptions(), memtable_ids,
|
||||
ASSERT_OK(mgr->PrepareFlushAdditions(WriteOptions(), /*num_generations=*/1,
|
||||
&additions1, &garbages1));
|
||||
ASSERT_EQ(additions1.size(), 1U);
|
||||
ASSERT_TRUE(garbages1.empty());
|
||||
@@ -1473,7 +1414,7 @@ TEST_F(DBBlobDirectWriteTest,
|
||||
|
||||
std::vector<BlobFileAddition> additions2;
|
||||
std::vector<BlobFileGarbage> garbages2;
|
||||
ASSERT_OK(mgr->PrepareFlushAdditions(WriteOptions(), memtable_ids,
|
||||
ASSERT_OK(mgr->PrepareFlushAdditions(WriteOptions(), /*num_generations=*/1,
|
||||
&additions2, &garbages2));
|
||||
ASSERT_EQ(additions2.size(), 1U);
|
||||
ASSERT_TRUE(garbages2.empty());
|
||||
@@ -1814,54 +1755,6 @@ TEST_F(
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->value().ToString(), values[target_idx]);
|
||||
}
|
||||
|
||||
TEST_F(DBBlobDirectWriteTest, DirectWriteDelayedReadUsesFinalizedFileChecksum) {
|
||||
auto capturing_fs = std::make_shared<BlobFileChecksumCapturingFileSystem>(
|
||||
env_->GetFileSystem());
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, capturing_fs));
|
||||
|
||||
Options options = GetDirectWriteOptions();
|
||||
options.env = env.get();
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
Defer close_db([this]() { Close(); });
|
||||
Reopen(options);
|
||||
|
||||
const std::string key = "delayed-read-key";
|
||||
const std::string value(96, 'v');
|
||||
ASSERT_OK(Put(key, value));
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.allow_unprepared_value = true;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
iter->Seek(key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), key);
|
||||
ASSERT_TRUE(iter->value().empty());
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::vector<ColumnFamilyMetaData> column_family_metadata;
|
||||
db_->GetAllColumnFamilyMetaData(&column_family_metadata);
|
||||
ASSERT_EQ(column_family_metadata.size(), 1);
|
||||
ASSERT_EQ(column_family_metadata[0].blob_files.size(), 1);
|
||||
const BlobMetaData& blob_metadata = column_family_metadata[0].blob_files[0];
|
||||
ASSERT_FALSE(blob_metadata.checksum_value.empty());
|
||||
ASSERT_EQ(blob_metadata.checksum_method, "FileChecksumCrc32c");
|
||||
|
||||
capturing_fs->Reset();
|
||||
ASSERT_TRUE(iter->PrepareValue());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->value().ToString(), value);
|
||||
ASSERT_GT(capturing_fs->GetCaptureCount(), 0);
|
||||
ASSERT_EQ(capturing_fs->GetFileChecksum(), blob_metadata.checksum_value);
|
||||
ASSERT_EQ(capturing_fs->GetFileChecksumFuncName(),
|
||||
blob_metadata.checksum_method);
|
||||
|
||||
const int capture_count = capturing_fs->GetCaptureCount();
|
||||
ASSERT_EQ(Get(key), value);
|
||||
ASSERT_EQ(capturing_fs->GetCaptureCount(), capture_count);
|
||||
}
|
||||
|
||||
TEST_F(DBBlobDirectWriteTest, OrderedTraceUsesLogicalBatchForBlobDirectWrite) {
|
||||
AssertOrderedTraceStoresLogicalPut(GetDirectWriteOptions(),
|
||||
/*expect_blob_files=*/true);
|
||||
@@ -1879,131 +1772,6 @@ TEST_F(DBBlobDirectWriteTest,
|
||||
AssertOrderedTraceStoresLogicalPut(options, /*expect_blob_files=*/false);
|
||||
}
|
||||
|
||||
TEST_F(DBBlobDirectWriteTest, OverlappingFlushesPublishTheirOwnBlobFiles) {
|
||||
for (bool atomic_flush : {false, true}) {
|
||||
SCOPED_TRACE("atomic_flush=" + std::to_string(atomic_flush));
|
||||
Options options = GetDirectWriteOptions();
|
||||
options.atomic_flush = atomic_flush;
|
||||
options.max_background_jobs = 8;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
const std::string value(1024, 'v');
|
||||
|
||||
// The flush of the memtable holding "key1" waits while it writes its
|
||||
// table file, until a second flush, of the memtable holding "key2", has
|
||||
// finished its own table file.
|
||||
std::atomic<int> table_writes{0};
|
||||
std::atomic<bool> released{false};
|
||||
std::promise<void> first_flush_writing;
|
||||
std::promise<void> release_first_flush;
|
||||
std::shared_future<void> release_first_flush_future =
|
||||
release_first_flush.get_future().share();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:num_memtables", [&](void*) {
|
||||
if (table_writes.fetch_add(1) == 0) {
|
||||
first_flush_writing.set_value();
|
||||
release_first_flush_future.wait();
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
atomic_flush ? "DBImpl::AtomicFlushMemTablesToOutputFiles:WaitCV"
|
||||
: "DBImpl::FlushMemTableToOutputFile:Finish",
|
||||
[&](void*) {
|
||||
if (!released.exchange(true)) {
|
||||
release_first_flush.set_value();
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
FlushOptions no_wait;
|
||||
no_wait.wait = false;
|
||||
no_wait.allow_write_stall = true;
|
||||
ASSERT_OK(Put("key1", value));
|
||||
ASSERT_OK(db_->Flush(no_wait));
|
||||
first_flush_writing.get_future().wait();
|
||||
ASSERT_OK(Put("key2", value));
|
||||
ASSERT_OK(db_->Flush(no_wait));
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ASSERT_OK(dbfull()->TEST_GetBGError());
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ(value, Get("key1"));
|
||||
ASSERT_EQ(value, Get("key2"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobDirectWriteTest, RetryAfterOlderOverlappingFlushFails) {
|
||||
Options options = GetDirectWriteOptions();
|
||||
options.max_background_jobs = 8;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_bgerror_resume_count = 0;
|
||||
DestroyAndReopen(options);
|
||||
const std::string value(1024, 'v');
|
||||
|
||||
// The flush of the memtable holding "key2" finishes while the older flush,
|
||||
// of the memtable holding "key1", is still writing its table file. The
|
||||
// newer flush's results wait to be installed after the older one's, but the
|
||||
// older flush fails, which rolls the newer memtable back too.
|
||||
std::atomic<int> table_writes{0};
|
||||
std::atomic<bool> released{false};
|
||||
std::atomic<bool> failed{false};
|
||||
std::promise<void> first_flush_writing;
|
||||
std::promise<void> release_first_flush;
|
||||
std::shared_future<void> release_first_flush_future =
|
||||
release_first_flush.get_future().share();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:num_memtables", [&](void*) {
|
||||
if (table_writes.fetch_add(1) == 0) {
|
||||
first_flush_writing.set_value();
|
||||
release_first_flush_future.wait();
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushMemTableToOutputFile:Finish", [&](void*) {
|
||||
if (!released.exchange(true)) {
|
||||
release_first_flush.set_value();
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:s", [&](void* arg) {
|
||||
if (released.load() && !failed.exchange(true)) {
|
||||
IOStatus injected = IOStatus::IOError("injected");
|
||||
injected.SetRetryable(true);
|
||||
*static_cast<Status*>(arg) = injected;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
FlushOptions no_wait;
|
||||
no_wait.wait = false;
|
||||
no_wait.allow_write_stall = true;
|
||||
ASSERT_OK(Put("key1", value));
|
||||
ASSERT_OK(db_->Flush(no_wait));
|
||||
first_flush_writing.get_future().wait();
|
||||
ASSERT_OK(Put("key2", value));
|
||||
ASSERT_OK(db_->Flush(no_wait));
|
||||
|
||||
ASSERT_NOK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ASSERT_TRUE(failed.load());
|
||||
|
||||
// Retrying the flush must still find the blob files of both memtables.
|
||||
ASSERT_OK(dbfull()->Resume());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(dbfull()->TEST_GetBGError());
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ(value, Get("key1"));
|
||||
ASSERT_EQ(value, Get("key2"));
|
||||
}
|
||||
|
||||
class DBBlobDirectWriteWithTimestampTest : public DBBasicTestWithTimestampBase {
|
||||
protected:
|
||||
DBBlobDirectWriteWithTimestampTest()
|
||||
|
||||
+2
-3
@@ -396,7 +396,7 @@ Status BuildTable(
|
||||
relevant_mapping,
|
||||
ioptions.compaction_style == CompactionStyle::kCompactionStyleFIFO
|
||||
? meta->file_creation_time
|
||||
: meta->oldest_ancestor_time);
|
||||
: meta->oldest_ancester_time);
|
||||
s = builder->Finish();
|
||||
}
|
||||
if (io_status->ok()) {
|
||||
@@ -452,8 +452,7 @@ Status BuildTable(
|
||||
WritableFileWriter::PrepareIOOptions(tboptions.write_options, opts);
|
||||
if (s.ok() && io_status->ok() && !empty) {
|
||||
StopWatch sw(ioptions.clock, ioptions.stats, TABLE_SYNC_MICROS);
|
||||
*io_status = ioptions.use_fsync ? file_writer->Fsync(opts)
|
||||
: file_writer->Sync(opts);
|
||||
*io_status = file_writer->Sync(opts, ioptions.use_fsync);
|
||||
}
|
||||
TEST_SYNC_POINT("BuildTable:BeforeCloseTableFile");
|
||||
if (s.ok() && io_status->ok() && !empty) {
|
||||
|
||||
@@ -195,7 +195,6 @@ using ROCKSDB_NAMESPACE::WALRecoveryMode;
|
||||
using ROCKSDB_NAMESPACE::WritableFile;
|
||||
using ROCKSDB_NAMESPACE::WriteBatch;
|
||||
using ROCKSDB_NAMESPACE::WriteBatchWithIndex;
|
||||
using ROCKSDB_NAMESPACE::WriteBufferFlushPolicy;
|
||||
using ROCKSDB_NAMESPACE::WriteBufferManager;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
using ROCKSDB_NAMESPACE::WriteStallCondition;
|
||||
@@ -419,13 +418,6 @@ struct rocksdb_readoptions_t {
|
||||
std::function<bool(const TableProperties&)> table_filter_func;
|
||||
std::shared_ptr<UserDefinedIndexFactory> table_index_factory;
|
||||
|
||||
rocksdb_readoptions_t() = default;
|
||||
// No copying allowed
|
||||
rocksdb_readoptions_t(const rocksdb_readoptions_t&) = delete;
|
||||
rocksdb_readoptions_t& operator=(const rocksdb_readoptions_t&) = delete;
|
||||
rocksdb_readoptions_t(rocksdb_readoptions_t&&) = delete;
|
||||
rocksdb_readoptions_t& operator=(rocksdb_readoptions_t&&) = delete;
|
||||
|
||||
~rocksdb_readoptions_t() { ClearTableFilter(); }
|
||||
|
||||
void ClearTableFilter() {
|
||||
@@ -812,22 +804,14 @@ struct rocksdb_open_and_compact_options_t {
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
void* state_ = nullptr;
|
||||
void (*destructor_)(void*) = nullptr;
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
unsigned char (*filter_)(void*, int level, const char* key, size_t key_length,
|
||||
const char* existing_value, size_t value_length,
|
||||
char** new_value, size_t* new_value_length,
|
||||
unsigned char* value_changed) = nullptr;
|
||||
const char* (*name_)(void*) = nullptr;
|
||||
unsigned char ignore_snapshots_ = true;
|
||||
|
||||
rocksdb_compactionfilter_t() = default;
|
||||
// No copying allowed
|
||||
rocksdb_compactionfilter_t(const rocksdb_compactionfilter_t&) = delete;
|
||||
rocksdb_compactionfilter_t& operator=(const rocksdb_compactionfilter_t&) =
|
||||
delete;
|
||||
rocksdb_compactionfilter_t(rocksdb_compactionfilter_t&&) = delete;
|
||||
rocksdb_compactionfilter_t& operator=(rocksdb_compactionfilter_t&&) = delete;
|
||||
unsigned char* value_changed);
|
||||
const char* (*name_)(void*);
|
||||
unsigned char ignore_snapshots_;
|
||||
|
||||
~rocksdb_compactionfilter_t() override { (*destructor_)(state_); }
|
||||
|
||||
@@ -853,22 +837,11 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
void* state_ = nullptr;
|
||||
void (*destructor_)(void*) = nullptr;
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
rocksdb_compactionfilter_t* (*create_compaction_filter_)(
|
||||
void*, rocksdb_compactionfiltercontext_t* context) = nullptr;
|
||||
const char* (*name_)(void*) = nullptr;
|
||||
|
||||
rocksdb_compactionfilterfactory_t() = default;
|
||||
// No copying allowed
|
||||
rocksdb_compactionfilterfactory_t(const rocksdb_compactionfilterfactory_t&) =
|
||||
delete;
|
||||
rocksdb_compactionfilterfactory_t& operator=(
|
||||
const rocksdb_compactionfilterfactory_t&) = delete;
|
||||
rocksdb_compactionfilterfactory_t(rocksdb_compactionfilterfactory_t&&) =
|
||||
delete;
|
||||
rocksdb_compactionfilterfactory_t& operator=(
|
||||
rocksdb_compactionfilterfactory_t&&) = delete;
|
||||
void*, rocksdb_compactionfiltercontext_t* context);
|
||||
const char* (*name_)(void*);
|
||||
|
||||
~rocksdb_compactionfilterfactory_t() override { (*destructor_)(state_); }
|
||||
|
||||
@@ -899,12 +872,6 @@ struct rocksdb_comparator_t : public Comparator {
|
||||
|
||||
rocksdb_comparator_t(size_t ts_size) : Comparator(ts_size) {}
|
||||
|
||||
// No copying allowed
|
||||
rocksdb_comparator_t(const rocksdb_comparator_t&) = delete;
|
||||
rocksdb_comparator_t& operator=(const rocksdb_comparator_t&) = delete;
|
||||
rocksdb_comparator_t(rocksdb_comparator_t&&) = delete;
|
||||
rocksdb_comparator_t& operator=(rocksdb_comparator_t&&) = delete;
|
||||
|
||||
~rocksdb_comparator_t() override { (*destructor_)(state_); }
|
||||
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
@@ -936,16 +903,9 @@ struct rocksdb_comparator_t : public Comparator {
|
||||
};
|
||||
|
||||
struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
void* state_ = nullptr;
|
||||
void (*destructor_)(void*) = nullptr;
|
||||
const char* (*name_)(void*) = nullptr;
|
||||
|
||||
rocksdb_filterpolicy_t() = default;
|
||||
// No copying allowed
|
||||
rocksdb_filterpolicy_t(const rocksdb_filterpolicy_t&) = delete;
|
||||
rocksdb_filterpolicy_t& operator=(const rocksdb_filterpolicy_t&) = delete;
|
||||
rocksdb_filterpolicy_t(rocksdb_filterpolicy_t&&) = delete;
|
||||
rocksdb_filterpolicy_t& operator=(rocksdb_filterpolicy_t&&) = delete;
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
const char* (*name_)(void*);
|
||||
|
||||
~rocksdb_filterpolicy_t() override { (*destructor_)(state_); }
|
||||
|
||||
@@ -953,29 +913,19 @@ struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
};
|
||||
|
||||
struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
void* state_ = nullptr;
|
||||
void (*destructor_)(void*) = nullptr;
|
||||
const char* (*name_)(void*) = nullptr;
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
const char* (*name_)(void*);
|
||||
char* (*full_merge_)(void*, const char* key, size_t key_length,
|
||||
const char* existing_value, size_t existing_value_length,
|
||||
const char* const* operands_list,
|
||||
const size_t* operands_list_length, int num_operands,
|
||||
unsigned char* success,
|
||||
size_t* new_value_length) = nullptr;
|
||||
unsigned char* success, size_t* new_value_length);
|
||||
char* (*partial_merge_)(void*, const char* key, size_t key_length,
|
||||
const char* const* operands_list,
|
||||
const size_t* operands_list_length, int num_operands,
|
||||
unsigned char* success,
|
||||
size_t* new_value_length) = nullptr;
|
||||
void (*delete_value_)(void*, const char* value,
|
||||
size_t value_length) = nullptr;
|
||||
|
||||
rocksdb_mergeoperator_t() = default;
|
||||
// No copying allowed
|
||||
rocksdb_mergeoperator_t(const rocksdb_mergeoperator_t&) = delete;
|
||||
rocksdb_mergeoperator_t& operator=(const rocksdb_mergeoperator_t&) = delete;
|
||||
rocksdb_mergeoperator_t(rocksdb_mergeoperator_t&&) = delete;
|
||||
rocksdb_mergeoperator_t& operator=(rocksdb_mergeoperator_t&&) = delete;
|
||||
unsigned char* success, size_t* new_value_length);
|
||||
void (*delete_value_)(void*, const char* value, size_t value_length);
|
||||
|
||||
~rocksdb_mergeoperator_t() override { (*destructor_)(state_); }
|
||||
|
||||
@@ -1057,19 +1007,12 @@ struct rocksdb_env_t {
|
||||
};
|
||||
|
||||
struct rocksdb_slicetransform_t : public SliceTransform {
|
||||
void* state_ = nullptr;
|
||||
void (*destructor_)(void*) = nullptr;
|
||||
const char* (*name_)(void*) = nullptr;
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
const char* (*name_)(void*);
|
||||
char* (*transform_)(void*, const char* key, size_t length,
|
||||
size_t* dst_length) = nullptr;
|
||||
unsigned char (*in_domain_)(void*, const char* key, size_t length) = nullptr;
|
||||
|
||||
rocksdb_slicetransform_t() = default;
|
||||
// No copying allowed
|
||||
rocksdb_slicetransform_t(const rocksdb_slicetransform_t&) = delete;
|
||||
rocksdb_slicetransform_t& operator=(const rocksdb_slicetransform_t&) = delete;
|
||||
rocksdb_slicetransform_t(rocksdb_slicetransform_t&&) = delete;
|
||||
rocksdb_slicetransform_t& operator=(rocksdb_slicetransform_t&&) = delete;
|
||||
size_t* dst_length);
|
||||
unsigned char (*in_domain_)(void*, const char* key, size_t length);
|
||||
|
||||
~rocksdb_slicetransform_t() override { (*destructor_)(state_); }
|
||||
|
||||
@@ -1393,14 +1336,6 @@ struct rocksdb_compactionservice_t : public CompactionService {
|
||||
cancel_awaiting_jobs_(cancel_awaiting_jobs),
|
||||
on_installation_(on_installation) {}
|
||||
|
||||
// No copying allowed
|
||||
rocksdb_compactionservice_t(const rocksdb_compactionservice_t&) = delete;
|
||||
rocksdb_compactionservice_t& operator=(const rocksdb_compactionservice_t&) =
|
||||
delete;
|
||||
rocksdb_compactionservice_t(rocksdb_compactionservice_t&&) = delete;
|
||||
rocksdb_compactionservice_t& operator=(rocksdb_compactionservice_t&&) =
|
||||
delete;
|
||||
|
||||
~rocksdb_compactionservice_t() override {
|
||||
if (destructor_) {
|
||||
(*destructor_)(state_);
|
||||
@@ -5262,11 +5197,6 @@ uint64_t rocksdb_table_properties_column_family_id(
|
||||
return props->rep.column_family_id;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_oldest_ancestor_time(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.oldest_ancestor_time;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_creation_time(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.creation_time;
|
||||
@@ -5297,6 +5227,11 @@ uint64_t rocksdb_table_properties_fast_compression_estimated_data_size(
|
||||
return props->rep.fast_compression_estimated_data_size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_external_sst_file_global_seqno_offset(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.external_sst_file_global_seqno_offset;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_table_properties_tail_start_offset(
|
||||
const rocksdb_table_properties_t* props) {
|
||||
return props->rep.tail_start_offset;
|
||||
@@ -7027,12 +6962,7 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(
|
||||
// they delegate to a NewBloomFilterPolicy() instead of user
|
||||
// supplied C functions.
|
||||
struct Wrapper : public rocksdb_filterpolicy_t {
|
||||
const FilterPolicy* rep_ = nullptr;
|
||||
Wrapper() = default;
|
||||
Wrapper(const Wrapper&) = delete;
|
||||
Wrapper& operator=(const Wrapper&) = delete;
|
||||
Wrapper(Wrapper&&) = delete;
|
||||
Wrapper& operator=(Wrapper&&) = delete;
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
@@ -7072,12 +7002,7 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_format(
|
||||
// they delegate to a NewRibbonFilterPolicy() instead of user
|
||||
// supplied C functions.
|
||||
struct Wrapper : public rocksdb_filterpolicy_t {
|
||||
const FilterPolicy* rep_ = nullptr;
|
||||
Wrapper() = default;
|
||||
Wrapper(const Wrapper&) = delete;
|
||||
Wrapper& operator=(const Wrapper&) = delete;
|
||||
Wrapper(Wrapper&&) = delete;
|
||||
Wrapper& operator=(Wrapper&&) = delete;
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
@@ -7836,35 +7761,6 @@ rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create(
|
||||
return wbm;
|
||||
}
|
||||
|
||||
static bool rocksdb_write_buffer_manager_parse_flush_policy(
|
||||
int value, WriteBufferFlushPolicy* policy) {
|
||||
switch (value) {
|
||||
case rocksdb_write_buffer_manager_flush_policy_oldest:
|
||||
*policy = WriteBufferFlushPolicy::kFlushOldest;
|
||||
return true;
|
||||
case rocksdb_write_buffer_manager_flush_policy_largest:
|
||||
*policy = WriteBufferFlushPolicy::kFlushLargest;
|
||||
return true;
|
||||
case rocksdb_write_buffer_manager_flush_policy_largest_across_dbs:
|
||||
*policy = WriteBufferFlushPolicy::kFlushLargestAcrossDBs;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create_with_policy(
|
||||
size_t buffer_size, bool allow_stall, int flush_policy) {
|
||||
WriteBufferFlushPolicy policy;
|
||||
if (!rocksdb_write_buffer_manager_parse_flush_policy(flush_policy, &policy)) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_write_buffer_manager_t* wbm = new rocksdb_write_buffer_manager_t;
|
||||
wbm->rep = std::make_shared<WriteBufferManager>(
|
||||
buffer_size, std::shared_ptr<Cache>{}, allow_stall, policy);
|
||||
return wbm;
|
||||
}
|
||||
|
||||
rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create_with_cache(
|
||||
size_t buffer_size, const rocksdb_cache_t* cache, bool allow_stall) {
|
||||
rocksdb_write_buffer_manager_t* wbm = new rocksdb_write_buffer_manager_t;
|
||||
@@ -7873,20 +7769,6 @@ rocksdb_write_buffer_manager_t* rocksdb_write_buffer_manager_create_with_cache(
|
||||
return wbm;
|
||||
}
|
||||
|
||||
rocksdb_write_buffer_manager_t*
|
||||
rocksdb_write_buffer_manager_create_with_cache_and_policy(
|
||||
size_t buffer_size, const rocksdb_cache_t* cache, bool allow_stall,
|
||||
int flush_policy) {
|
||||
WriteBufferFlushPolicy policy;
|
||||
if (!rocksdb_write_buffer_manager_parse_flush_policy(flush_policy, &policy)) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_write_buffer_manager_t* wbm = new rocksdb_write_buffer_manager_t;
|
||||
wbm->rep = std::make_shared<WriteBufferManager>(buffer_size, cache->rep,
|
||||
allow_stall, policy);
|
||||
return wbm;
|
||||
}
|
||||
|
||||
void rocksdb_write_buffer_manager_destroy(rocksdb_write_buffer_manager_t* wbm) {
|
||||
delete wbm;
|
||||
}
|
||||
@@ -7927,19 +7809,6 @@ ROCKSDB_LIBRARY_API void rocksdb_write_buffer_manager_set_allow_stall(
|
||||
wbm->rep->SetAllowStall(new_allow_stall);
|
||||
}
|
||||
|
||||
int rocksdb_write_buffer_manager_flush_policy(
|
||||
rocksdb_write_buffer_manager_t* wbm) {
|
||||
return static_cast<int>(wbm->rep->flush_policy());
|
||||
}
|
||||
|
||||
void rocksdb_write_buffer_manager_set_flush_policy(
|
||||
rocksdb_write_buffer_manager_t* wbm, int flush_policy) {
|
||||
WriteBufferFlushPolicy policy;
|
||||
if (rocksdb_write_buffer_manager_parse_flush_policy(flush_policy, &policy)) {
|
||||
wbm->rep->SetFlushPolicy(policy);
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_sst_file_manager_t* rocksdb_sst_file_manager_create(
|
||||
rocksdb_env_t* env) {
|
||||
rocksdb_sst_file_manager_t* sfm = new rocksdb_sst_file_manager_t;
|
||||
@@ -10091,16 +9960,6 @@ unsigned char rocksdb_options_get_write_dbid_to_manifest(
|
||||
return opt->rep.write_dbid_to_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_track_options_file_number_in_manifest(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.track_options_file_number_in_manifest = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_track_options_file_number_in_manifest(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.track_options_file_number_in_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_identity_file(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.write_identity_file = v;
|
||||
@@ -11417,6 +11276,16 @@ unsigned char rocksdb_ingestexternalfileoptions_get_ingest_behind(
|
||||
return opt->rep.ingest_behind;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_write_global_seqno(
|
||||
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v) {
|
||||
opt->rep.write_global_seqno = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_ingestexternalfileoptions_get_write_global_seqno(
|
||||
rocksdb_ingestexternalfileoptions_t* opt) {
|
||||
return opt->rep.write_global_seqno;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_verify_checksums_before_ingest(
|
||||
rocksdb_ingestexternalfileoptions_t* opt, unsigned char v) {
|
||||
opt->rep.verify_checksums_before_ingest = v;
|
||||
|
||||
-30
@@ -6716,36 +6716,6 @@ int main(int argc, char** argv) {
|
||||
300 == rocksdb_write_buffer_manager_buffer_size(write_buffer_manager));
|
||||
|
||||
rocksdb_write_buffer_manager_destroy(write_buffer_manager);
|
||||
|
||||
write_buffer_manager = rocksdb_write_buffer_manager_create_with_policy(
|
||||
200, true,
|
||||
rocksdb_write_buffer_manager_flush_policy_largest_across_dbs);
|
||||
CheckCondition(write_buffer_manager != NULL);
|
||||
CheckCondition(
|
||||
rocksdb_write_buffer_manager_flush_policy_largest_across_dbs ==
|
||||
rocksdb_write_buffer_manager_flush_policy(write_buffer_manager));
|
||||
rocksdb_write_buffer_manager_set_flush_policy(
|
||||
write_buffer_manager,
|
||||
rocksdb_write_buffer_manager_flush_policy_largest);
|
||||
CheckCondition(
|
||||
rocksdb_write_buffer_manager_flush_policy_largest ==
|
||||
rocksdb_write_buffer_manager_flush_policy(write_buffer_manager));
|
||||
rocksdb_write_buffer_manager_set_flush_policy(write_buffer_manager, 999);
|
||||
CheckCondition(
|
||||
rocksdb_write_buffer_manager_flush_policy_largest ==
|
||||
rocksdb_write_buffer_manager_flush_policy(write_buffer_manager));
|
||||
rocksdb_write_buffer_manager_destroy(write_buffer_manager);
|
||||
|
||||
write_buffer_manager =
|
||||
rocksdb_write_buffer_manager_create_with_cache_and_policy(
|
||||
200, lru, true,
|
||||
rocksdb_write_buffer_manager_flush_policy_largest_across_dbs);
|
||||
CheckCondition(write_buffer_manager != NULL);
|
||||
rocksdb_write_buffer_manager_destroy(write_buffer_manager);
|
||||
|
||||
write_buffer_manager =
|
||||
rocksdb_write_buffer_manager_create_with_policy(200, true, 999);
|
||||
CheckCondition(write_buffer_manager == NULL);
|
||||
rocksdb_cache_destroy(lru);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ class CoalescingIterator : public Iterator {
|
||||
// No copy allowed
|
||||
CoalescingIterator(const CoalescingIterator&) = delete;
|
||||
CoalescingIterator& operator=(const CoalescingIterator&) = delete;
|
||||
CoalescingIterator(CoalescingIterator&&) = delete;
|
||||
CoalescingIterator& operator=(CoalescingIterator&&) = delete;
|
||||
|
||||
bool Valid() const override { return impl_.Valid(); }
|
||||
void SeekToFirst() override { impl_.SeekToFirst(); }
|
||||
|
||||
+7
-126
@@ -33,7 +33,6 @@
|
||||
#include "db/write_controller.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "logging/logging.h"
|
||||
#include "memtable/flush_initiator.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
@@ -613,8 +612,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
const FileOptions* file_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, const std::string& db_id,
|
||||
const std::string& db_session_id, bool read_only,
|
||||
bool options_are_sanitized, bool fast_sst_open)
|
||||
const std::string& db_session_id, bool read_only, bool fast_sst_open)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(_dummy_versions),
|
||||
@@ -624,10 +622,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
dropped_(false),
|
||||
flush_skip_reschedule_(false),
|
||||
internal_comparator_(cf_options.comparator),
|
||||
initial_cf_options_(
|
||||
options_are_sanitized
|
||||
? cf_options
|
||||
: SanitizeCfOptions(db_options, read_only, cf_options)),
|
||||
initial_cf_options_(SanitizeCfOptions(db_options, read_only, cf_options)),
|
||||
ioptions_(db_options, initial_cf_options_),
|
||||
mutable_cf_options_(initial_cf_options_),
|
||||
is_delete_range_supported_(
|
||||
@@ -677,7 +672,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
new InternalStats(ioptions_.num_levels, ioptions_.clock, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, file_options, _table_cache,
|
||||
block_cache_tracer, io_tracer,
|
||||
db_session_id, fast_sst_open, name_));
|
||||
db_session_id, fast_sst_open));
|
||||
blob_file_cache_.reset(
|
||||
new BlobFileCache(_table_cache, &ioptions(), soptions(), id_,
|
||||
internal_stats_->GetBlobFileReadHist(), io_tracer));
|
||||
@@ -839,11 +834,6 @@ void ColumnFamilyData::SetDropped() {
|
||||
// can't drop default CF
|
||||
assert(id_ != 0);
|
||||
dropped_ = true;
|
||||
// A tracker can still contain accounting published while a different WBM
|
||||
// policy was active. Cleanup therefore cannot depend on the current policy.
|
||||
if (mem_ != nullptr) {
|
||||
mem_->StopWBMTracking();
|
||||
}
|
||||
write_controller_token_.reset();
|
||||
|
||||
// remove from column_family_set
|
||||
@@ -1256,10 +1246,7 @@ uint64_t ColumnFamilyData::GetLiveSstFilesSize() const {
|
||||
MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
return new MemTable(internal_comparator_, ioptions_, mutable_cf_options,
|
||||
write_buffer_manager_, earliest_seq, id_,
|
||||
column_family_set_ != nullptr
|
||||
? column_family_set_->flush_initiator()
|
||||
: nullptr);
|
||||
write_buffer_manager_, earliest_seq, id_);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::CreateNewMemtable(SequenceNumber earliest_seq) {
|
||||
@@ -1842,7 +1829,7 @@ ColumnFamilySet::ColumnFamilySet(
|
||||
ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr,
|
||||
nullptr, ColumnFamilyOptions(), *db_options, &file_options_, nullptr,
|
||||
block_cache_tracer, io_tracer, db_id, db_session_id,
|
||||
/*read_only=*/true, /*options_are_sanitized=*/false)),
|
||||
/*read_only*/ true)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
@@ -1912,112 +1899,15 @@ size_t ColumnFamilySet::NumberOfColumnFamilies() const {
|
||||
return column_families_.size();
|
||||
}
|
||||
|
||||
void ColumnFamilySet::RefreshFlushableMemAccounting() {
|
||||
if (flush_initiator_ == nullptr || write_buffer_manager_ == nullptr ||
|
||||
!write_buffer_manager_->ShouldTrackFlushInitiator()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bulk_flushable_mem_accounting_depth_ > 0) {
|
||||
bulk_flushable_mem_accounting_dirty_ = true;
|
||||
flush_initiator_->InvalidateLargestFlushableCFMem();
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep candidate readers from consuming a mixture of the previous snapshot
|
||||
// and the values published by this rebuild.
|
||||
flush_initiator_->InvalidateLargestFlushableCFMem();
|
||||
constexpr int kMaxRefreshAttempts = 3;
|
||||
for (int attempt = 0; attempt < kMaxRefreshAttempts; ++attempt) {
|
||||
const uint64_t update_seq =
|
||||
flush_initiator_->GetFlushableMemUpdateSequence();
|
||||
bool has_mutable_mem = false;
|
||||
bool has_ineligible_cf = false;
|
||||
size_t largest = 0;
|
||||
size_t waiting_immutable_mem = 0;
|
||||
for (auto cfd : *this) {
|
||||
if (cfd->IsDropped() || !cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
const bool mutable_not_empty = !cfd->mem()->IsEmpty();
|
||||
if (mutable_not_empty) {
|
||||
cfd->mem()->RefreshWBMTracking();
|
||||
}
|
||||
const bool has_mutable =
|
||||
mutable_not_empty && cfd->mem()->IsWBMTrackingActive();
|
||||
has_mutable_mem = has_mutable_mem || has_mutable;
|
||||
if (flush_initiator_->UsesTotalMutableMem()) {
|
||||
waiting_immutable_mem +=
|
||||
cfd->imm()->WBMTrackedUnstartedMemTablesMemoryUsage();
|
||||
continue;
|
||||
}
|
||||
if (cfd->imm()->IsFlushPendingOrRunning()) {
|
||||
has_ineligible_cf = true;
|
||||
continue;
|
||||
}
|
||||
const size_t mutable_mem =
|
||||
has_mutable ? cfd->mem()->WBMTrackedMemoryUsage() : 0;
|
||||
const size_t immutable_mem =
|
||||
cfd->imm()->WBMTrackedUnflushedMemTablesMemoryUsage();
|
||||
waiting_immutable_mem += immutable_mem;
|
||||
largest = std::max(largest, mutable_mem + immutable_mem);
|
||||
}
|
||||
if (flush_initiator_->UsesTotalMutableMem()) {
|
||||
flush_initiator_->SetHasIneligibleCF(false);
|
||||
flush_initiator_->SetHasFlushableCF(has_mutable_mem ||
|
||||
waiting_immutable_mem > 0);
|
||||
if (flush_initiator_->TrySetLargestFlushableCFMem(
|
||||
0, waiting_immutable_mem, update_seq)) {
|
||||
write_buffer_manager_->NotifyFlushInitiatorChanged();
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
flush_initiator_->SetHasIneligibleCF(has_ineligible_cf);
|
||||
flush_initiator_->SetHasFlushableCF(largest > 0);
|
||||
if (flush_initiator_->TrySetLargestFlushableCFMem(
|
||||
largest, waiting_immutable_mem, update_seq)) {
|
||||
write_buffer_manager_->NotifyFlushInitiatorChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
flush_initiator_->InvalidateLargestFlushableCFMem();
|
||||
}
|
||||
|
||||
void ColumnFamilySet::BeginBulkFlushableMemAccountingUpdate() {
|
||||
++bulk_flushable_mem_accounting_depth_;
|
||||
if (bulk_flushable_mem_accounting_depth_ == 1) {
|
||||
bulk_flushable_mem_accounting_dirty_ = true;
|
||||
if (flush_initiator_ != nullptr && write_buffer_manager_ != nullptr &&
|
||||
write_buffer_manager_->ShouldTrackFlushInitiator()) {
|
||||
flush_initiator_->InvalidateLargestFlushableCFMem();
|
||||
write_buffer_manager_->NotifyFlushInitiatorChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ColumnFamilySet::EndBulkFlushableMemAccountingUpdate() {
|
||||
assert(bulk_flushable_mem_accounting_depth_ > 0);
|
||||
--bulk_flushable_mem_accounting_depth_;
|
||||
if (bulk_flushable_mem_accounting_depth_ == 0 &&
|
||||
bulk_flushable_mem_accounting_dirty_) {
|
||||
bulk_flushable_mem_accounting_dirty_ = false;
|
||||
TEST_SYNC_POINT(
|
||||
"ColumnFamilySet::EndBulkFlushableMemAccountingUpdate:Refresh");
|
||||
RefreshFlushableMemAccounting();
|
||||
}
|
||||
}
|
||||
|
||||
// under a DB mutex AND write thread
|
||||
ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
const std::string& name, uint32_t id, Version* dummy_versions,
|
||||
const ColumnFamilyOptions& options, bool read_only,
|
||||
bool options_are_sanitized) {
|
||||
const ColumnFamilyOptions& options, bool read_only) {
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
*db_options_, &file_options_, this, block_cache_tracer_, io_tracer_,
|
||||
db_id_, db_session_id_, read_only, options_are_sanitized, fast_sst_open_);
|
||||
db_id_, db_session_id_, read_only, fast_sst_open_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
auto ucmp = new_cfd->user_comparator();
|
||||
@@ -2049,15 +1939,6 @@ void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
|
||||
column_families_.erase(cfd->GetName());
|
||||
running_ts_sz_.erase(cf_id);
|
||||
ts_sz_for_record_.erase(cf_id);
|
||||
if (bulk_flushable_mem_accounting_depth_ > 0) {
|
||||
bulk_flushable_mem_accounting_dirty_ = true;
|
||||
if (flush_initiator_ != nullptr) {
|
||||
flush_initiator_->InvalidateLargestFlushableCFMem();
|
||||
}
|
||||
return;
|
||||
}
|
||||
TEST_SYNC_POINT("ColumnFamilySet::RemoveColumnFamily:Refresh");
|
||||
RefreshFlushableMemAccounting();
|
||||
}
|
||||
|
||||
// under a DB mutex OR from a write thread
|
||||
|
||||
+13
-35
@@ -52,7 +52,6 @@ struct SuperVersionContext;
|
||||
class BlobFileCache;
|
||||
class BlobFilePartitionManager;
|
||||
class BlobSource;
|
||||
class FlushInitiator;
|
||||
|
||||
extern const double kIncSlowdownRatio;
|
||||
// This file contains a list of data structures for managing column family
|
||||
@@ -171,7 +170,7 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
ColumnFamilyHandleImpl(ColumnFamilyData* cfd, DBImpl* db,
|
||||
InstrumentedMutex* mutex);
|
||||
// destroy without mutex
|
||||
~ColumnFamilyHandleImpl() override;
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
virtual DBImpl* db() const { return db_; }
|
||||
|
||||
@@ -654,15 +653,17 @@ class ColumnFamilyData {
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* dummy_versions,
|
||||
Cache* table_cache, WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& options, const ImmutableDBOptions& db_options,
|
||||
const FileOptions* file_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, const std::string& db_id,
|
||||
const std::string& db_session_id, bool read_only,
|
||||
bool options_are_sanitized, bool fast_sst_open = false);
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const FileOptions* file_options,
|
||||
ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
bool read_only, bool fast_sst_open = false);
|
||||
|
||||
std::vector<std::string> GetDbPaths() const;
|
||||
|
||||
@@ -848,8 +849,7 @@ class ColumnFamilySet {
|
||||
ColumnFamilyData* CreateColumnFamily(const std::string& name, uint32_t id,
|
||||
Version* dummy_version,
|
||||
const ColumnFamilyOptions& options,
|
||||
bool read_only,
|
||||
bool options_are_sanitized);
|
||||
bool read_only);
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& GetRunningColumnFamiliesTimestampSize()
|
||||
const {
|
||||
@@ -871,19 +871,6 @@ class ColumnFamilySet {
|
||||
|
||||
WriteBufferManager* write_buffer_manager() { return write_buffer_manager_; }
|
||||
|
||||
void SetFlushInitiator(FlushInitiator* flush_initiator) {
|
||||
flush_initiator_ = flush_initiator;
|
||||
}
|
||||
|
||||
FlushInitiator* flush_initiator() const { return flush_initiator_; }
|
||||
|
||||
void RefreshFlushableMemAccounting();
|
||||
|
||||
// Coalesces accounting rebuilds while a caller removes multiple column
|
||||
// families. Both methods require the DB mutex.
|
||||
void BeginBulkFlushableMemAccountingUpdate();
|
||||
void EndBulkFlushableMemAccountingUpdate();
|
||||
|
||||
WriteController* write_controller() { return write_controller_; }
|
||||
|
||||
private:
|
||||
@@ -911,9 +898,6 @@ class ColumnFamilySet {
|
||||
// column families with non-zero user-defined timestamp size.
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_for_record_;
|
||||
|
||||
int bulk_flushable_mem_accounting_depth_ = 0;
|
||||
bool bulk_flushable_mem_accounting_dirty_ = false;
|
||||
|
||||
uint32_t max_column_family_;
|
||||
const FileOptions file_options_;
|
||||
|
||||
@@ -928,7 +912,6 @@ class ColumnFamilySet {
|
||||
const ImmutableDBOptions* const db_options_;
|
||||
Cache* table_cache_;
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
FlushInitiator* flush_initiator_ = nullptr;
|
||||
WriteController* write_controller_;
|
||||
BlockCacheTracer* const block_cache_tracer_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
@@ -950,11 +933,6 @@ class RefedColumnFamilySet {
|
||||
explicit iterator(ColumnFamilySet::iterator wrapped) : wrapped_(wrapped) {
|
||||
MaybeRef(*wrapped_);
|
||||
}
|
||||
// No copying allowed
|
||||
iterator(const iterator&) = delete;
|
||||
iterator& operator=(const iterator&) = delete;
|
||||
iterator(iterator&&) = delete;
|
||||
iterator& operator=(iterator&&) = delete;
|
||||
~iterator() { MaybeUnref(*wrapped_); }
|
||||
inline void MaybeRef(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
|
||||
+38
-545
@@ -25,7 +25,6 @@
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "rocksdb/utilities/options_util.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
@@ -33,7 +32,6 @@
|
||||
#include "util/defer.h"
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -115,8 +113,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
}
|
||||
|
||||
void Build(int base, int n, int flush_every = 0) {
|
||||
std::string key_space;
|
||||
std::string value_space;
|
||||
std::string key_space, value_space;
|
||||
WriteBatch batch;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
@@ -182,35 +179,6 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
db_.reset();
|
||||
}
|
||||
|
||||
void AssertColumnFamilyNames(const std::vector<std::string>& expected) {
|
||||
std::vector<std::string> column_families;
|
||||
ASSERT_OK(DB::ListColumnFamilies(db_options_, dbname_, &column_families));
|
||||
ASSERT_EQ(expected, column_families);
|
||||
}
|
||||
|
||||
void LoadLatestColumnFamilyDescriptors(
|
||||
std::vector<ColumnFamilyDescriptor>* descriptors) {
|
||||
ConfigOptions config_options;
|
||||
config_options.env = db_options_.env;
|
||||
DBOptions loaded_db_options;
|
||||
ASSERT_OK(LoadLatestOptions(config_options, dbname_, &loaded_db_options,
|
||||
descriptors));
|
||||
}
|
||||
|
||||
size_t CountOptionsFiles() {
|
||||
std::vector<std::string> children;
|
||||
EXPECT_OK(db_options_.env->GetChildren(dbname_, &children));
|
||||
size_t count = 0;
|
||||
for (const auto& child : children) {
|
||||
uint64_t number = 0;
|
||||
FileType type;
|
||||
if (ParseFileName(child, &number, &type) && type == kOptionsFile) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
Status TryOpen(std::vector<std::string> cf,
|
||||
std::vector<ColumnFamilyOptions> options = {}) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
@@ -546,9 +514,8 @@ class ColumnFamilyTest
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FormatDef, ColumnFamilyTest,
|
||||
testing::Values(test::kDefaultFormatVersion));
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatLatest, ColumnFamilyTest,
|
||||
testing::Values(BlockBasedTableOptions::kLatestSupportedFormatVersion));
|
||||
INSTANTIATE_TEST_CASE_P(FormatLatest, ColumnFamilyTest,
|
||||
testing::Values(kLatestBbtFormatVersion));
|
||||
|
||||
TEST_P(ColumnFamilyTest, DontReuseColumnFamilyID) {
|
||||
for (int iter = 0; iter < 3; ++iter) {
|
||||
@@ -738,11 +705,8 @@ INSTANTIATE_TEST_CASE_P(
|
||||
std::make_tuple(test::kDefaultFormatVersion, false)));
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatLatest, FlushEmptyCFTestWithParam,
|
||||
testing::Values(
|
||||
std::make_tuple(BlockBasedTableOptions::kLatestSupportedFormatVersion,
|
||||
true),
|
||||
std::make_tuple(BlockBasedTableOptions::kLatestSupportedFormatVersion,
|
||||
false)));
|
||||
testing::Values(std::make_tuple(kLatestBbtFormatVersion, true),
|
||||
std::make_tuple(kLatestBbtFormatVersion, false)));
|
||||
|
||||
TEST_P(ColumnFamilyTest, AddDrop) {
|
||||
Open();
|
||||
@@ -810,8 +774,6 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
|
||||
constexpr int kNumCF = 1000;
|
||||
ColumnFamilyOptions cf_options;
|
||||
WriteOptions write_options;
|
||||
db_options_.write_buffer_manager = std::make_shared<WriteBufferManager>(
|
||||
1 << 30, nullptr, false, WriteBufferFlushPolicy::kFlushLargestAcrossDBs);
|
||||
Open();
|
||||
std::vector<std::string> cf_names;
|
||||
std::vector<ColumnFamilyHandle*> cf_handles;
|
||||
@@ -822,22 +784,7 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
ASSERT_OK(db_->Put(write_options, cf_handles[i - 1], "foo", "bar"));
|
||||
}
|
||||
std::atomic<int> per_removal_refreshes{0};
|
||||
std::atomic<int> bulk_refreshes{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ColumnFamilySet::RemoveColumnFamily:Refresh",
|
||||
[&](void*) { ++per_removal_refreshes; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ColumnFamilySet::EndBulkFlushableMemAccountingUpdate:Refresh",
|
||||
[&](void*) { ++bulk_refreshes; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Defer cleanup_sync_points([] {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
});
|
||||
ASSERT_OK(db_->DropColumnFamilies(cf_handles));
|
||||
EXPECT_EQ(0, per_removal_refreshes.load());
|
||||
EXPECT_EQ(1, bulk_refreshes.load());
|
||||
std::vector<ColumnFamilyDescriptor> cf_descriptors;
|
||||
for (auto* handle : cf_handles) {
|
||||
delete handle;
|
||||
@@ -851,10 +798,7 @@ TEST_P(ColumnFamilyTest, BulkAddDrop) {
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
ASSERT_OK(db_->Put(write_options, cf_handles[i - 1], "foo", "bar"));
|
||||
}
|
||||
bulk_refreshes.store(0);
|
||||
ASSERT_OK(db_->DropColumnFamilies(cf_handles));
|
||||
EXPECT_EQ(0, per_removal_refreshes.load());
|
||||
EXPECT_EQ(1, bulk_refreshes.load());
|
||||
for (auto* handle : cf_handles) {
|
||||
delete handle;
|
||||
}
|
||||
@@ -953,9 +897,7 @@ TEST_P(ColumnFamilyTest, IgnoreRecoveredLog) {
|
||||
CreateColumnFamilies({"cf1", "cf2"});
|
||||
|
||||
// fill up the DB
|
||||
std::string one;
|
||||
std::string two;
|
||||
std::string three;
|
||||
std::string one, two, three;
|
||||
PutFixed64(&one, 1);
|
||||
PutFixed64(&two, 2);
|
||||
PutFixed64(&three, 3);
|
||||
@@ -1163,10 +1105,7 @@ TEST_P(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
db_options_.max_total_wal_size = std::numeric_limits<uint64_t>::max();
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two", "three"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions two;
|
||||
ColumnFamilyOptions three;
|
||||
ColumnFamilyOptions default_cf, one, two, three;
|
||||
// setup options. all column families have max_write_buffer_number setup to 10
|
||||
// "default" -> 100KB memtable, start flushing immediately
|
||||
// "one" -> 200KB memtable, start flushing with two immutable memtables
|
||||
@@ -1318,9 +1257,7 @@ TEST_P(ColumnFamilyTest, GetComparator) {
|
||||
|
||||
// Add three column families - one with no comparator and two
|
||||
// with comparators specified
|
||||
ColumnFamilyOptions second;
|
||||
ColumnFamilyOptions third;
|
||||
ColumnFamilyOptions fourth;
|
||||
ColumnFamilyOptions second, third, fourth;
|
||||
second.comparator = &third_comparator;
|
||||
third.comparator = &fourth_comparator;
|
||||
CreateColumnFamilies({"second", "third", "fourth"}, {second, third, fourth});
|
||||
@@ -1333,16 +1270,12 @@ TEST_P(ColumnFamilyTest, GetComparator) {
|
||||
TEST_P(ColumnFamilyTest, DifferentMergeOperators) {
|
||||
Open();
|
||||
CreateColumnFamilies({"first", "second"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions first;
|
||||
ColumnFamilyOptions second;
|
||||
ColumnFamilyOptions default_cf, first, second;
|
||||
first.merge_operator = MergeOperators::CreateUInt64AddOperator();
|
||||
second.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
Reopen({default_cf, first, second});
|
||||
|
||||
std::string one;
|
||||
std::string two;
|
||||
std::string three;
|
||||
std::string one, two, three;
|
||||
PutFixed64(&one, 1);
|
||||
PutFixed64(&two, 2);
|
||||
PutFixed64(&three, 3);
|
||||
@@ -1367,9 +1300,7 @@ TEST_P(ColumnFamilyTest, DifferentMergeOperators) {
|
||||
TEST_P(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions two;
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
@@ -1441,9 +1372,7 @@ TEST_P(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
TEST_P(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions two;
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
@@ -1543,9 +1472,7 @@ TEST_P(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions two;
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
@@ -1642,9 +1569,7 @@ TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
TEST_P(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions two;
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
@@ -1743,8 +1668,7 @@ TEST_P(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
TEST_P(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions default_cf, one;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
@@ -1844,8 +1768,7 @@ TEST_P(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions default_cf, one;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
@@ -1936,8 +1859,7 @@ TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions default_cf, one;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
@@ -2034,8 +1956,7 @@ TEST_P(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
TEST_P(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions default_cf, one;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
@@ -2248,9 +2169,7 @@ TEST_P(ColumnFamilyTest, DontRollEmptyLogs) {
|
||||
TEST_P(ColumnFamilyTest, FlushStaleColumnFamilies) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
ColumnFamilyOptions default_cf;
|
||||
ColumnFamilyOptions one;
|
||||
ColumnFamilyOptions two;
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
default_cf.write_buffer_size = 100000; // small write buffer size
|
||||
default_cf.arena_block_size = 4096;
|
||||
default_cf.disable_auto_compactions = true;
|
||||
@@ -2311,378 +2230,8 @@ struct CountOptionsFilesFs : public FileSystemWrapper {
|
||||
|
||||
std::atomic<int> options_files_created{};
|
||||
};
|
||||
|
||||
struct FailOptionsFilesFs : public FileSystemWrapper {
|
||||
explicit FailOptionsFilesFs(const std::shared_ptr<FileSystem>& t)
|
||||
: FileSystemWrapper(t) {}
|
||||
const char* Name() const override { return "FailOptionsFilesFs"; }
|
||||
|
||||
IOStatus NewWritableFile(const std::string& f, const FileOptions& file_opts,
|
||||
std::unique_ptr<FSWritableFile>* r,
|
||||
IODebugContext* dbg) override {
|
||||
if (fail_options_writes.load(std::memory_order_relaxed) &&
|
||||
f.find("OPTIONS-") != std::string::npos) {
|
||||
return IOStatus::IOError("injected OPTIONS-file creation failure");
|
||||
}
|
||||
return FileSystemWrapper::NewWritableFile(f, file_opts, r, dbg);
|
||||
}
|
||||
|
||||
std::atomic<bool> fail_options_writes{false};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_P(ColumnFamilyTest, TrackedOptionsWriteFailureDoesNotChangeManifest) {
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
db_options_.max_manifest_file_size = 1;
|
||||
db_options_.max_manifest_space_amp_pct = 0;
|
||||
auto fail_fs =
|
||||
std::make_shared<FailOptionsFilesFs>(db_options_.env->GetFileSystem());
|
||||
auto fail_env =
|
||||
std::make_unique<CompositeEnvWrapper>(db_options_.env, fail_fs);
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, fail_env.get());
|
||||
|
||||
Open();
|
||||
OptionsFileManifestState before_failure;
|
||||
ASSERT_OK(VersionSet::GetOptionsFileManifestState(
|
||||
dbname_, db_options_.env->GetFileSystem().get(), &before_failure));
|
||||
fail_fs->fail_options_writes.store(true, std::memory_order_relaxed);
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ASSERT_NOK(db_->CreateColumnFamily(ColumnFamilyOptions(), "one", &handle));
|
||||
ASSERT_EQ(nullptr, handle);
|
||||
fail_fs->fail_options_writes.store(false, std::memory_order_relaxed);
|
||||
|
||||
OptionsFileManifestState after_failure;
|
||||
ASSERT_OK(VersionSet::GetOptionsFileManifestState(
|
||||
dbname_, db_options_.env->GetFileSystem().get(), &after_failure));
|
||||
ASSERT_EQ(before_failure.has_committed_options_file_number,
|
||||
after_failure.has_committed_options_file_number);
|
||||
ASSERT_EQ(before_failure.committed_options_file_number,
|
||||
after_failure.committed_options_file_number);
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> loaded_cfs;
|
||||
LoadLatestColumnFamilyDescriptors(&loaded_cfs);
|
||||
ASSERT_EQ(1U, loaded_cfs.size());
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, loaded_cfs.front().name);
|
||||
|
||||
const uint64_t old_manifest_number =
|
||||
dbfull()->GetVersionSet()->manifest_file_number();
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key", "value"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_NE(old_manifest_number,
|
||||
dbfull()->GetVersionSet()->manifest_file_number());
|
||||
|
||||
OptionsFileManifestState after_rotation;
|
||||
ASSERT_OK(VersionSet::GetOptionsFileManifestState(
|
||||
dbname_, db_options_.env->GetFileSystem().get(), &after_rotation));
|
||||
ASSERT_EQ(before_failure.has_committed_options_file_number,
|
||||
after_rotation.has_committed_options_file_number);
|
||||
ASSERT_EQ(before_failure.committed_options_file_number,
|
||||
after_rotation.committed_options_file_number);
|
||||
Close();
|
||||
|
||||
AssertColumnFamilyNames({kDefaultColumnFamilyName});
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, TrackedDropFailsBeforeManifestOnOptionsError) {
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
auto fail_fs =
|
||||
std::make_shared<FailOptionsFilesFs>(db_options_.env->GetFileSystem());
|
||||
auto fail_env =
|
||||
std::make_unique<CompositeEnvWrapper>(db_options_.env, fail_fs);
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, fail_env.get());
|
||||
|
||||
Open();
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "one", &handle));
|
||||
ASSERT_NE(nullptr, handle);
|
||||
|
||||
fail_fs->fail_options_writes.store(true, std::memory_order_relaxed);
|
||||
ASSERT_NOK(db_->DropColumnFamily(handle));
|
||||
fail_fs->fail_options_writes.store(false, std::memory_order_relaxed);
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(handle));
|
||||
Close();
|
||||
|
||||
AssertColumnFamilyNames({kDefaultColumnFamilyName, "one"});
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, TrackedManifestFailureKeepsPreviousOptions) {
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
auto fault_fs =
|
||||
std::make_shared<FaultInjectionTestFS>(db_options_.env->GetFileSystem());
|
||||
std::unique_ptr<Env> fault_env(NewCompositeEnv(fault_fs));
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, fault_env.get());
|
||||
|
||||
Open();
|
||||
std::string selected_options_before;
|
||||
ASSERT_OK(GetLatestOptionsFileName(dbname_, db_options_.env,
|
||||
&selected_options_before));
|
||||
|
||||
std::atomic<bool> inject_error{true};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest", [&](void*) {
|
||||
if (inject_error.exchange(false)) {
|
||||
fault_fs->SetThreadLocalErrorContext(
|
||||
FaultInjectionIOType::kWrite, /*seed=*/0, /*one_in=*/1,
|
||||
/*retryable=*/false, /*has_data_loss=*/false);
|
||||
fault_fs->EnableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kWrite);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ASSERT_NOK(db_->CreateColumnFamily(ColumnFamilyOptions(), "one", &handle));
|
||||
ASSERT_EQ(nullptr, handle);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
fault_fs->DisableThreadLocalErrorInjection(FaultInjectionIOType::kWrite);
|
||||
|
||||
std::string selected_options_after;
|
||||
ASSERT_OK(GetLatestOptionsFileName(dbname_, db_options_.env,
|
||||
&selected_options_after));
|
||||
ASSERT_EQ(selected_options_before, selected_options_after);
|
||||
Close();
|
||||
|
||||
AssertColumnFamilyNames({kDefaultColumnFamilyName});
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, FirstTrackedCreateFailureIgnoresUncommittedOptions) {
|
||||
// Create a legacy DB without a committed number, then enable tracking while
|
||||
// Open also
|
||||
// needs to create a missing CF. The current topology must be bound before
|
||||
// the proposed topology OPTIONS file can become a durable orphan.
|
||||
Open();
|
||||
Close();
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
db_options_.create_missing_column_families = true;
|
||||
|
||||
auto fault_fs =
|
||||
std::make_shared<FaultInjectionTestFS>(db_options_.env->GetFileSystem());
|
||||
std::unique_ptr<Env> fault_env(NewCompositeEnv(fault_fs));
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, fault_env.get());
|
||||
|
||||
bool injected = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::CreateColumnFamilyImpl:AfterPersistOptions", [&](void*) {
|
||||
if (!injected) {
|
||||
injected = true;
|
||||
fault_fs->SetThreadLocalErrorContext(
|
||||
FaultInjectionIOType::kWrite, /*seed=*/0, /*one_in=*/1,
|
||||
/*retryable=*/false, /*has_data_loss=*/false);
|
||||
fault_fs->EnableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kWrite);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_NOK(TryOpen({kDefaultColumnFamilyName, "one"}));
|
||||
ASSERT_TRUE(injected);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
fault_fs->DisableThreadLocalErrorInjection(FaultInjectionIOType::kWrite);
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> loaded_cfs;
|
||||
LoadLatestColumnFamilyDescriptors(&loaded_cfs);
|
||||
ASSERT_EQ(1U, loaded_cfs.size());
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, loaded_cfs.front().name);
|
||||
|
||||
OptionsFileManifestState protocol_state;
|
||||
ASSERT_OK(VersionSet::GetOptionsFileManifestState(
|
||||
dbname_, db_options_.env->GetFileSystem().get(), &protocol_state));
|
||||
ASSERT_TRUE(protocol_state.has_committed_options_file_number);
|
||||
std::string selected_options;
|
||||
ASSERT_OK(
|
||||
GetLatestOptionsFileName(dbname_, db_options_.env, &selected_options));
|
||||
ASSERT_EQ(OptionsFileName(protocol_state.committed_options_file_number),
|
||||
selected_options);
|
||||
|
||||
AssertColumnFamilyNames({kDefaultColumnFamilyName});
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, TrackedCreatePersistsCommittedOptions) {
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
Open();
|
||||
|
||||
ColumnFamilyOptions requested;
|
||||
requested.arena_block_size = 0;
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ASSERT_OK(db_->CreateColumnFamily(requested, "one", &handle));
|
||||
const ColumnFamilyOptions effective = db_->GetOptions(handle);
|
||||
ASSERT_GT(effective.arena_block_size, 0U);
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> loaded_cfs;
|
||||
LoadLatestColumnFamilyDescriptors(&loaded_cfs);
|
||||
ASSERT_EQ(2U, loaded_cfs.size());
|
||||
ASSERT_EQ("one", loaded_cfs[1].name);
|
||||
ASSERT_EQ(effective.arena_block_size, loaded_cfs[1].options.arena_block_size);
|
||||
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(handle));
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, TrackedBatchTopologyWritesOneOptionsSnapshot) {
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
auto count_fs =
|
||||
std::make_shared<CountOptionsFilesFs>(db_options_.env->GetFileSystem());
|
||||
auto count_env =
|
||||
std::make_unique<CompositeEnvWrapper>(db_options_.env, count_fs);
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, count_env.get());
|
||||
|
||||
Open();
|
||||
count_fs->options_files_created.store(0, std::memory_order_relaxed);
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_OK(db_->CreateColumnFamilies(ColumnFamilyOptions(),
|
||||
{"one", "two", "three"}, &handles));
|
||||
ASSERT_EQ(3U, handles.size());
|
||||
ASSERT_EQ(1, count_fs->options_files_created.load(std::memory_order_relaxed));
|
||||
|
||||
count_fs->options_files_created.store(0, std::memory_order_relaxed);
|
||||
ASSERT_OK(db_->DropColumnFamilies(handles));
|
||||
ASSERT_EQ(1, count_fs->options_files_created.load(std::memory_order_relaxed));
|
||||
for (auto* handle : handles) {
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(handle));
|
||||
}
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> loaded_cfs;
|
||||
LoadLatestColumnFamilyDescriptors(&loaded_cfs);
|
||||
ASSERT_EQ(1U, loaded_cfs.size());
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, loaded_cfs.front().name);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, TrackedPartialBatchFiltersOptionsSuperset) {
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
auto fault_fs =
|
||||
std::make_shared<FaultInjectionTestFS>(db_options_.env->GetFileSystem());
|
||||
std::unique_ptr<Env> fault_env(NewCompositeEnv(fault_fs));
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, fault_env.get());
|
||||
|
||||
Open();
|
||||
std::atomic<int> manifest_writes{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest", [&](void*) {
|
||||
if (manifest_writes.fetch_add(1, std::memory_order_relaxed) == 1) {
|
||||
fault_fs->SetThreadLocalErrorContext(
|
||||
FaultInjectionIOType::kWrite, /*seed=*/0, /*one_in=*/1,
|
||||
/*retryable=*/false, /*has_data_loss=*/false);
|
||||
fault_fs->EnableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kWrite);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_NOK(db_->CreateColumnFamilies(ColumnFamilyOptions(), {"one", "two"},
|
||||
&handles));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
fault_fs->DisableThreadLocalErrorInjection(FaultInjectionIOType::kWrite);
|
||||
ASSERT_EQ(1U, handles.size());
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> loaded_cfs;
|
||||
LoadLatestColumnFamilyDescriptors(&loaded_cfs);
|
||||
ASSERT_EQ(2U, loaded_cfs.size());
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, loaded_cfs[0].name);
|
||||
ASSERT_EQ("one", loaded_cfs[1].name);
|
||||
|
||||
ConfigOptions config_options;
|
||||
config_options.env = db_options_.env;
|
||||
DBOptions loaded_db_options;
|
||||
ASSERT_OK(LoadLatestOptions(config_options, dbname_, &loaded_db_options,
|
||||
&loaded_cfs));
|
||||
ASSERT_OK(CheckOptionsCompatibility(config_options, dbname_,
|
||||
loaded_db_options, loaded_cfs));
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(handles.front()));
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, TrackedPartialBatchDropFiltersOptionsSuperset) {
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
auto fault_fs =
|
||||
std::make_shared<FaultInjectionTestFS>(db_options_.env->GetFileSystem());
|
||||
std::unique_ptr<Env> fault_env(NewCompositeEnv(fault_fs));
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, fault_env.get());
|
||||
|
||||
Open();
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_OK(db_->CreateColumnFamilies(ColumnFamilyOptions(), {"one", "two"},
|
||||
&handles));
|
||||
ASSERT_EQ(2U, handles.size());
|
||||
|
||||
std::atomic<int> manifest_writes{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest", [&](void*) {
|
||||
if (manifest_writes.fetch_add(1, std::memory_order_relaxed) == 1) {
|
||||
fault_fs->SetThreadLocalErrorContext(
|
||||
FaultInjectionIOType::kWrite, /*seed=*/0, /*one_in=*/1,
|
||||
/*retryable=*/false, /*has_data_loss=*/false);
|
||||
fault_fs->EnableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kWrite);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_NOK(db_->DropColumnFamilies(handles));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
fault_fs->DisableThreadLocalErrorInjection(FaultInjectionIOType::kWrite);
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> loaded_cfs;
|
||||
LoadLatestColumnFamilyDescriptors(&loaded_cfs);
|
||||
ASSERT_EQ(2U, loaded_cfs.size());
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, loaded_cfs[0].name);
|
||||
ASSERT_EQ("two", loaded_cfs[1].name);
|
||||
for (auto* handle : handles) {
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(handle));
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, InvalidTrackedTopologyChangeCreatesNoOptionsOrphan) {
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
Open();
|
||||
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "one", &handle));
|
||||
const size_t before_duplicate_create = CountOptionsFiles();
|
||||
ColumnFamilyHandle* duplicate = nullptr;
|
||||
ASSERT_TRUE(db_->CreateColumnFamily(ColumnFamilyOptions(), "one", &duplicate)
|
||||
.IsInvalidArgument());
|
||||
ASSERT_EQ(nullptr, duplicate);
|
||||
ASSERT_EQ(before_duplicate_create, CountOptionsFiles());
|
||||
|
||||
ASSERT_OK(db_->DropColumnFamily(handle));
|
||||
const size_t before_duplicate_drop = CountOptionsFiles();
|
||||
ASSERT_TRUE(db_->DropColumnFamily(handle).IsInvalidArgument());
|
||||
ASSERT_EQ(before_duplicate_drop, CountOptionsFiles());
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(handle));
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest,
|
||||
PartialMultiDropConsumesLegacyOptionsPersistenceFailure) {
|
||||
auto fail_fs =
|
||||
std::make_shared<FailOptionsFilesFs>(db_options_.env->GetFileSystem());
|
||||
auto fail_env =
|
||||
std::make_unique<CompositeEnvWrapper>(db_options_.env, fail_fs);
|
||||
SaveAndRestore<Env*> save_restore_env(&db_options_.env, fail_env.get());
|
||||
|
||||
Open();
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "one", &handle));
|
||||
|
||||
fail_fs->fail_options_writes.store(true, std::memory_order_relaxed);
|
||||
Status s = db_->DropColumnFamilies({handle, db_->DefaultColumnFamily()});
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
fail_fs->fail_options_writes.store(false, std::memory_order_relaxed);
|
||||
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(handle));
|
||||
Close();
|
||||
AssertColumnFamilyNames({kDefaultColumnFamilyName});
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
// Can't accidentally add CFs to an existing DB
|
||||
Open();
|
||||
@@ -2715,16 +2264,6 @@ TEST_P(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
ASSERT_OK(TryOpen({"default", "one", "two", "three", "four"}));
|
||||
Close();
|
||||
ASSERT_EQ(my_fs->options_files_created.load(), 2);
|
||||
|
||||
Destroy();
|
||||
db_options_.track_options_file_number_in_manifest = true;
|
||||
my_fs->options_files_created.store(0, std::memory_order_relaxed);
|
||||
ASSERT_OK(TryOpen({"default", "one", "two", "three", "four"}));
|
||||
Close();
|
||||
// A new tracked DB writes its initial snapshot, one proposed batch snapshot,
|
||||
// and the normal final DB::Open snapshot, independent of the number of
|
||||
// missing CFs.
|
||||
ASSERT_EQ(my_fs->options_files_created.load(), 3);
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, SanitizeCfOptions) {
|
||||
@@ -3628,32 +3167,6 @@ TEST_P(ColumnFamilyTest, CreateDropAndDestroy) {
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh));
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, DropWaitsForUnorderedMemtableWrite) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::WriteImpl:UnorderedWriteAfterWriteWAL",
|
||||
"ColumnFamilyTest::DropWaitsForUnorderedMemtableWrite:AfterWAL"},
|
||||
{"DBImpl::WaitForPendingWrites:BeforeBlock",
|
||||
"DBImpl::WriteImpl:BeforeUnorderedWriteMemtable"}});
|
||||
|
||||
db_options_.unordered_write = true;
|
||||
Open();
|
||||
ColumnFamilyHandle* cfh;
|
||||
ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "yoyo", &cfh));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
port::Thread writer(
|
||||
[&] { ASSERT_OK(db_->Put(WriteOptions(), cfh, "foo", "bar")); });
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"ColumnFamilyTest::DropWaitsForUnorderedMemtableWrite:AfterWAL");
|
||||
ASSERT_OK(db_->DropColumnFamily(cfh));
|
||||
writer.join();
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CreateDropAndDestroyWithoutFileDeletion) {
|
||||
ColumnFamilyHandle* cfh;
|
||||
Open();
|
||||
@@ -3712,12 +3225,6 @@ TEST_P(ColumnFamilyTest, FlushCloseWALFiles) {
|
||||
TEST_P(ColumnFamilyTest, IteratorCloseWALFile1) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
// The DB keeps its Env pointer until Close(). Close it before the local Env
|
||||
// is destroyed, including when an ASSERT below exits the test early.
|
||||
Defer close_db_on_exit([this] {
|
||||
Close();
|
||||
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;
|
||||
@@ -3727,38 +3234,25 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile1) {
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
// Create an iterator holding the current super version.
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions(), handles_[1]));
|
||||
Iterator* it = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
ASSERT_OK(it->status());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::BGWorkFlush:done",
|
||||
"ColumnFamilyTest::IteratorCloseWALFile1:SetupFlushDone"},
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Defer disable_sync_points(
|
||||
[] { ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); });
|
||||
|
||||
// A flush will make `it` hold the last reference of its super version.
|
||||
ASSERT_OK(Flush(1));
|
||||
// Flush() can return before the background function releases all temporary
|
||||
// references to the old super version.
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile1:SetupFlushDone");
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile1:ReleaseFlush",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
// Flush jobs close previous WAL files after finishing. Keep the next flush
|
||||
// from starting so the iterator destructor is responsible for closing the
|
||||
// WAL. Occupying one Env worker is insufficient because the shared Env can
|
||||
// have multiple HIGH-priority workers.
|
||||
// Flush jobs will close previous WAL files after finishing. By
|
||||
// block flush jobs from running, we trigger a condition where
|
||||
// the iterator destructor should close the WAL files.
|
||||
test::SleepingBackgroundTask sleeping_task;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task,
|
||||
Env::Priority::HIGH);
|
||||
// Make sure the task is sleeping. Otherwise, it might start to execute
|
||||
// after sleeping_task.WaitUntilDone() and cause TSAN warning.
|
||||
sleeping_task.WaitUntilSleeping();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
@@ -3766,15 +3260,18 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile1) {
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
// Deleting the iterator will clear its super version, triggering
|
||||
// closing all files
|
||||
it.reset();
|
||||
delete it;
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile1:ReleaseFlush");
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
WaitForFlush(1);
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, IteratorCloseWALFile2) {
|
||||
@@ -4031,8 +3528,7 @@ TEST_P(ColumnFamilyTest, DefaultCfPathsTest) {
|
||||
// Leave cf_paths for one column families to be empty.
|
||||
// Files should be generated according to db_paths for that
|
||||
// column family.
|
||||
ColumnFamilyOptions cf_opt1;
|
||||
ColumnFamilyOptions cf_opt2;
|
||||
ColumnFamilyOptions cf_opt1, cf_opt2;
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "_one_1",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
CreateColumnFamilies({"one", "two"}, {cf_opt1, cf_opt2});
|
||||
@@ -4057,8 +3553,7 @@ TEST_P(ColumnFamilyTest, DefaultCfPathsTest) {
|
||||
TEST_P(ColumnFamilyTest, MultipleCFPathsTest) {
|
||||
Open();
|
||||
// Configure Column family specific paths.
|
||||
ColumnFamilyOptions cf_opt1;
|
||||
ColumnFamilyOptions cf_opt2;
|
||||
ColumnFamilyOptions cf_opt1, cf_opt2;
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "_one_1",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "_two_1",
|
||||
@@ -4174,9 +3669,7 @@ TEST(ColumnFamilyTest, ValidateMemtableKVChecksumOption) {
|
||||
// the behavior of manual flush is that it skips retaining UDTs.
|
||||
class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
|
||||
public:
|
||||
ColumnFamilyRetainUDTTest()
|
||||
: ColumnFamilyTestBase(
|
||||
BlockBasedTableOptions::kLatestSupportedFormatVersion) {}
|
||||
ColumnFamilyRetainUDTTest() : ColumnFamilyTestBase(kLatestBbtFormatVersion) {}
|
||||
|
||||
void SetUp() override {
|
||||
db_options_.allow_concurrent_memtable_write = false;
|
||||
|
||||
@@ -214,8 +214,7 @@ bool Compaction::IsBottommostLevel(
|
||||
} else {
|
||||
output_l0_idx = -1;
|
||||
}
|
||||
Slice smallest_key;
|
||||
Slice largest_key;
|
||||
Slice smallest_key, largest_key;
|
||||
GetBoundaryKeys(vstorage, inputs, &smallest_key, &largest_key);
|
||||
return !vstorage->RangeMightExistAfterSortedRun(smallest_key, largest_key,
|
||||
output_level, output_l0_idx);
|
||||
@@ -987,9 +986,9 @@ uint64_t Compaction::MaxInputFileNewestKeyTime(const InternalKey* start,
|
||||
return newest_key_time;
|
||||
}
|
||||
|
||||
uint64_t Compaction::MinInputFileOldestAncestorTime(
|
||||
uint64_t Compaction::MinInputFileOldestAncesterTime(
|
||||
const InternalKey* start, const InternalKey* end) const {
|
||||
uint64_t min_oldest_ancestor_time = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t min_oldest_ancester_time = std::numeric_limits<uint64_t>::max();
|
||||
const InternalKeyComparator& icmp =
|
||||
column_family_data()->internal_comparator();
|
||||
for (const auto& level_files : inputs_) {
|
||||
@@ -1000,14 +999,14 @@ uint64_t Compaction::MinInputFileOldestAncestorTime(
|
||||
if (end != nullptr && icmp.Compare(file->smallest, *end) > 0) {
|
||||
continue;
|
||||
}
|
||||
uint64_t oldest_ancestor_time = file->TryGetOldestAncestorTime();
|
||||
if (oldest_ancestor_time != 0) {
|
||||
min_oldest_ancestor_time =
|
||||
std::min(min_oldest_ancestor_time, oldest_ancestor_time);
|
||||
uint64_t oldest_ancester_time = file->TryGetOldestAncesterTime();
|
||||
if (oldest_ancester_time != 0) {
|
||||
min_oldest_ancester_time =
|
||||
std::min(min_oldest_ancester_time, oldest_ancester_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
return min_oldest_ancestor_time;
|
||||
return min_oldest_ancester_time;
|
||||
}
|
||||
|
||||
uint64_t Compaction::MinInputFileEpochNumber() const {
|
||||
|
||||
@@ -114,8 +114,6 @@ class Compaction {
|
||||
// No copying allowed
|
||||
Compaction(const Compaction&) = delete;
|
||||
void operator=(const Compaction&) = delete;
|
||||
Compaction(Compaction&&) = delete;
|
||||
void operator=(Compaction&&) = delete;
|
||||
|
||||
~Compaction();
|
||||
|
||||
@@ -440,8 +438,8 @@ class Compaction {
|
||||
const InternalKey* end) const;
|
||||
|
||||
// start and end are sub compact range. Null if no boundary.
|
||||
// This is used to filter out some input files' ancestor's time range.
|
||||
uint64_t MinInputFileOldestAncestorTime(const InternalKey* start,
|
||||
// This is used to filter out some input files' ancester's time range.
|
||||
uint64_t MinInputFileOldestAncesterTime(const InternalKey* start,
|
||||
const InternalKey* end) const;
|
||||
// Return the minimum epoch number among
|
||||
// input files' associated with this compaction
|
||||
|
||||
@@ -1593,8 +1593,8 @@ TEST_P(CompactionIteratorTsGcTest, DropTombstones) {
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, 102);
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*last_committed_sequence=*/kMaxSequenceNumber,
|
||||
/*merge_op=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/false,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/true, &full_history_ts_low);
|
||||
|
||||
@@ -2160,20 +2160,20 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
|
||||
if (s.ok()) {
|
||||
// With accurate smallest and largest key, we can get a slightly more
|
||||
// accurate oldest ancestor time.
|
||||
// This makes oldest ancestor time in manifest more accurate than in
|
||||
// accurate oldest ancester time.
|
||||
// This makes oldest ancester time in manifest more accurate than in
|
||||
// table properties. Not sure how to resolve it.
|
||||
if (meta->smallest.size() > 0 && meta->largest.size() > 0) {
|
||||
uint64_t refined_oldest_ancestor_time;
|
||||
uint64_t refined_oldest_ancester_time;
|
||||
Slice new_smallest = meta->smallest.user_key();
|
||||
Slice new_largest = meta->largest.user_key();
|
||||
if (!new_largest.empty() && !new_smallest.empty()) {
|
||||
refined_oldest_ancestor_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncestorTime(
|
||||
refined_oldest_ancester_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime(
|
||||
&(meta->smallest), &(meta->largest));
|
||||
if (refined_oldest_ancestor_time !=
|
||||
if (refined_oldest_ancester_time !=
|
||||
std::numeric_limits<uint64_t>::max()) {
|
||||
meta->oldest_ancestor_time = refined_oldest_ancestor_time;
|
||||
meta->oldest_ancester_time = refined_oldest_ancester_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2555,7 +2555,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
return s;
|
||||
}
|
||||
|
||||
// Try to figure out the output file's oldest ancestor time.
|
||||
// Try to figure out the output file's oldest ancester time.
|
||||
int64_t temp_current_time = 0;
|
||||
auto get_time_status = db_options_.clock->GetCurrentTime(&temp_current_time);
|
||||
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
|
||||
@@ -2565,22 +2565,21 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
get_time_status.ToString().c_str());
|
||||
}
|
||||
uint64_t current_time = static_cast<uint64_t>(temp_current_time);
|
||||
InternalKey tmp_start;
|
||||
InternalKey tmp_end;
|
||||
InternalKey tmp_start, tmp_end;
|
||||
if (sub_compact->start.has_value()) {
|
||||
tmp_start.SetMinPossibleForUserKey(*(sub_compact->start));
|
||||
}
|
||||
if (sub_compact->end.has_value()) {
|
||||
tmp_end.SetMinPossibleForUserKey(*(sub_compact->end));
|
||||
}
|
||||
uint64_t oldest_ancestor_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncestorTime(
|
||||
uint64_t oldest_ancester_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime(
|
||||
sub_compact->start.has_value() ? &tmp_start : nullptr,
|
||||
sub_compact->end.has_value() ? &tmp_end : nullptr);
|
||||
if (oldest_ancestor_time == std::numeric_limits<uint64_t>::max()) {
|
||||
if (oldest_ancester_time == std::numeric_limits<uint64_t>::max()) {
|
||||
// TODO: fix DBSSTTest.GetTotalSstFilesSize and use
|
||||
// kUnknownOldestAncestorTime
|
||||
oldest_ancestor_time = current_time;
|
||||
// kUnknownOldestAncesterTime
|
||||
oldest_ancester_time = current_time;
|
||||
}
|
||||
|
||||
uint64_t newest_key_time = sub_compact->compaction->MaxInputFileNewestKeyTime(
|
||||
@@ -2593,7 +2592,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
FileMetaData meta;
|
||||
meta.fd = FileDescriptor(file_number,
|
||||
sub_compact->compaction->output_path_id(), 0);
|
||||
meta.oldest_ancestor_time = oldest_ancestor_time;
|
||||
meta.oldest_ancester_time = oldest_ancester_time;
|
||||
meta.file_creation_time = current_time;
|
||||
meta.epoch_number = epoch_number;
|
||||
meta.temperature = temperature;
|
||||
@@ -3269,8 +3268,7 @@ Status CompactionJob::PersistSubcompactionProgress(
|
||||
s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = db_options_.use_fsync ? compaction_progress_writer_->file()->Fsync(opts)
|
||||
: compaction_progress_writer_->file()->Sync(opts);
|
||||
s = compaction_progress_writer_->file()->Sync(opts, db_options_.use_fsync);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
|
||||
@@ -175,7 +175,6 @@ class CompactionJob {
|
||||
CompactionJob(CompactionJob&& job) = delete;
|
||||
CompactionJob(const CompactionJob& job) = delete;
|
||||
CompactionJob& operator=(const CompactionJob& job) = delete;
|
||||
CompactionJob& operator=(CompactionJob&& job) = delete;
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Prepare for the compaction by setting up boundaries for each subcompaction
|
||||
@@ -509,7 +508,7 @@ class CompactionJob {
|
||||
std::atomic<int>* num_running_remote_compactions_;
|
||||
|
||||
// Stores the sequence number to time mapping gathered from all input files
|
||||
// it also collects the smallest_seqno -> oldest_ancestor_time from the SST.
|
||||
// it also collects the smallest_seqno -> oldest_ancester_time from the SST.
|
||||
SeqnoToTimeMapping seqno_to_time_mapping_;
|
||||
|
||||
// Max seqno that can be zeroed out in last level, including for preserving
|
||||
@@ -642,7 +641,7 @@ struct CompactionServiceOutputFile {
|
||||
SequenceNumber largest_seqno{};
|
||||
std::string smallest_internal_key;
|
||||
std::string largest_internal_key;
|
||||
uint64_t oldest_ancestor_time = kUnknownOldestAncestorTime;
|
||||
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
|
||||
uint64_t file_creation_time = kUnknownFileCreationTime;
|
||||
uint64_t epoch_number = kUnknownEpochNumber;
|
||||
std::string file_checksum = kUnknownFileChecksum;
|
||||
@@ -658,7 +657,7 @@ struct CompactionServiceOutputFile {
|
||||
CompactionServiceOutputFile(
|
||||
const std::string& name, uint64_t size, SequenceNumber smallest,
|
||||
SequenceNumber largest, std::string _smallest_internal_key,
|
||||
std::string _largest_internal_key, uint64_t _oldest_ancestor_time,
|
||||
std::string _largest_internal_key, uint64_t _oldest_ancester_time,
|
||||
uint64_t _file_creation_time, uint64_t _epoch_number,
|
||||
const std::string& _file_checksum,
|
||||
const std::string& _file_checksum_func_name, uint64_t _paranoid_hash,
|
||||
@@ -671,7 +670,7 @@ struct CompactionServiceOutputFile {
|
||||
largest_seqno(largest),
|
||||
smallest_internal_key(std::move(_smallest_internal_key)),
|
||||
largest_internal_key(std::move(_largest_internal_key)),
|
||||
oldest_ancestor_time(_oldest_ancestor_time),
|
||||
oldest_ancester_time(_oldest_ancester_time),
|
||||
file_creation_time(_file_creation_time),
|
||||
epoch_number(_epoch_number),
|
||||
file_checksum(_file_checksum),
|
||||
|
||||
@@ -322,10 +322,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
assert(contents.size() > 0);
|
||||
|
||||
bool first_key = true;
|
||||
std::string smallest;
|
||||
std::string largest;
|
||||
InternalKey smallest_key;
|
||||
InternalKey largest_key;
|
||||
std::string smallest, largest;
|
||||
InternalKey smallest_key, largest_key;
|
||||
SequenceNumber smallest_seqno = kMaxSequenceNumber;
|
||||
SequenceNumber largest_seqno = 0;
|
||||
uint64_t oldest_blob_file_number = kInvalidBlobFileNumber;
|
||||
@@ -389,7 +387,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
edit.AddFile(
|
||||
level, file_number, 0, file_size, smallest_key, largest_key,
|
||||
smallest_seqno, largest_seqno, false, Temperature::kUnknown,
|
||||
oldest_blob_file_number, kUnknownOldestAncestorTime,
|
||||
oldest_blob_file_number, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime,
|
||||
versions_->GetColumnFamilySet()->GetDefault()->NewEpochNumber(),
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2,
|
||||
@@ -1704,7 +1702,7 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
rnd.Uniform(kStrMaxLen)) /* smallest_internal_key */,
|
||||
rnd.RandomBinaryString(
|
||||
rnd.Uniform(kStrMaxLen)) /* largest_internal_key */,
|
||||
rnd64.Uniform(UINT64_MAX) /* oldest_ancestor_time */,
|
||||
rnd64.Uniform(UINT64_MAX) /* oldest_ancester_time */,
|
||||
rnd64.Uniform(UINT64_MAX) /* file_creation_time */,
|
||||
rnd64.Uniform(UINT64_MAX) /* epoch_number */,
|
||||
file_checksum /* file_checksum */,
|
||||
|
||||
@@ -10,10 +10,7 @@
|
||||
|
||||
#include "db/compaction/compaction_outputs.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -35,7 +32,7 @@ Status CompactionOutputs::Finish(
|
||||
meta->fd.largest_seqno);
|
||||
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
|
||||
builder_->SetSeqnoTimeTableProperties(relevant_mapping,
|
||||
meta->oldest_ancestor_time);
|
||||
meta->oldest_ancester_time);
|
||||
s = builder_->Finish();
|
||||
|
||||
} else {
|
||||
@@ -76,7 +73,7 @@ IOStatus CompactionOutputs::WriterSyncClose(const Status& input_status,
|
||||
WriteOptions(Env::IOActivity::kCompaction), opts);
|
||||
if (input_status.ok() && io_s.ok()) {
|
||||
StopWatch sw(clock, statistics, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
io_s = use_fsync ? file_writer_->Fsync(opts) : file_writer_->Sync(opts);
|
||||
io_s = file_writer_->Sync(opts, use_fsync);
|
||||
}
|
||||
if (input_status.ok() && io_s.ok()) {
|
||||
io_s = file_writer_->Close(opts);
|
||||
@@ -376,24 +373,6 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void CompactionOutputs::PrepareNewOutputForFirstKey(
|
||||
const CompactionIterator& c_iter) {
|
||||
// A new output file starts a fresh partition-tracking window. Establish the
|
||||
// partitioner fast path from this file's first key: if the partitioner can
|
||||
// express its next boundary as "first key not sharing this prefix", cache
|
||||
// that prefix; otherwise fall back to per-key ShouldPartition(). This is the
|
||||
// only place the prefix needs to be (re)derived, since a partition boundary
|
||||
// always forces a file cut, and re-deriving on non-partition cuts just
|
||||
// recomputes the same still-valid prefix.
|
||||
if (partitioner_) {
|
||||
OptSlice prefix = partitioner_->ShouldPartitionByPrefix(c_iter.user_key());
|
||||
partitioner_prefix_valid_ = prefix.has_value();
|
||||
if (partitioner_prefix_valid_) {
|
||||
partitioner_prefix_.assign(prefix->data(), prefix->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status CompactionOutputs::AddToOutput(
|
||||
const CompactionIterator& c_iter,
|
||||
const CompactionFileOpenFunc& open_file_func,
|
||||
@@ -433,117 +412,42 @@ Status CompactionOutputs::AddToOutput(
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
PrepareNewOutputForFirstKey(c_iter);
|
||||
// A new output file starts a fresh partition-tracking window. Establish the
|
||||
// partitioner fast path from this file's first key: if the partitioner can
|
||||
// express its next boundary as "first key not sharing this prefix", cache
|
||||
// that prefix; otherwise fall back to per-key ShouldPartition(). This is
|
||||
// the only place the prefix needs to be (re)derived, since a partition
|
||||
// boundary always forces a file cut, and re-deriving on non-partition cuts
|
||||
// just recomputes the same still-valid prefix.
|
||||
if (partitioner_) {
|
||||
OptSlice prefix =
|
||||
partitioner_->ShouldPartitionByPrefix(c_iter.user_key());
|
||||
partitioner_prefix_valid_ = prefix.has_value();
|
||||
if (partitioner_prefix_valid_) {
|
||||
partitioner_prefix_.assign(prefix->data(), prefix->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// c_iter may emit range deletion keys, so update `last_key_for_partitioner_`
|
||||
// here before returning below when `is_range_del` is true. Only needed in the
|
||||
// fallback mode; the prefix fast path does not use it.
|
||||
if (partitioner_ && !partitioner_prefix_valid_) {
|
||||
last_key_for_partitioner_.assign(c_iter.user_key().data_,
|
||||
c_iter.user_key().size_);
|
||||
}
|
||||
|
||||
if (UNLIKELY(is_range_del)) {
|
||||
// c_iter may emit range deletion keys, so update
|
||||
// `last_key_for_partitioner_` before returning. Only needed in the fallback
|
||||
// mode; the prefix fast path does not use it.
|
||||
if (partitioner_ && !partitioner_prefix_valid_) {
|
||||
last_key_for_partitioner_.assign(c_iter.user_key().data_,
|
||||
c_iter.user_key().size_);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
assert(builder_ != nullptr);
|
||||
const Slice& value = c_iter.value();
|
||||
bool retried_after_new_table = false;
|
||||
while (true) {
|
||||
s = current_output().validator.CanAdd(key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
TableBuilderAddContext add_context;
|
||||
TableBuilderAddResult add_result =
|
||||
builder_->TryAdd(key, value, &add_context);
|
||||
|
||||
if (add_result == TableBuilderAddResult::kError) {
|
||||
s = builder_->status();
|
||||
if (s.ok()) {
|
||||
return Status::Corruption(
|
||||
"TableBuilder returned kError without a non-OK status");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
if (add_result == TableBuilderAddResult::kAdded) {
|
||||
current_output().validator.AddValidated(key, value);
|
||||
break;
|
||||
}
|
||||
|
||||
assert(add_result == TableBuilderAddResult::kRequiresNewTable);
|
||||
s = builder_->status();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
const Compaction* compaction = compaction_;
|
||||
if (compaction == nullptr) {
|
||||
return Status::Corruption("Compaction output is missing compaction");
|
||||
}
|
||||
ColumnFamilyData* cfd = compaction->column_family_data();
|
||||
if (cfd == nullptr) {
|
||||
return Status::Corruption(
|
||||
"Compaction output is missing column family data");
|
||||
}
|
||||
const bool allow_data_in_errors =
|
||||
compaction->immutable_options().allow_data_in_errors;
|
||||
const std::string key_for_diagnostics =
|
||||
allow_data_in_errors ? key.ToString(/* hex */ true) : "hidden";
|
||||
if (compaction->output_level() == 0) {
|
||||
const std::string detail =
|
||||
"cf: " + cfd->GetName() + ", file_number: " +
|
||||
std::to_string(current_output().meta.fd.GetNumber()) + ", reason: " +
|
||||
TableBuilderNewTableReasonToString(add_context.new_table_reason) +
|
||||
", key: " + key_for_diagnostics;
|
||||
return Status::Corruption(
|
||||
"TableBuilder requested a new table while producing level 0 output",
|
||||
detail);
|
||||
}
|
||||
if (retried_after_new_table || builder_->IsEmpty()) {
|
||||
const std::string detail =
|
||||
"cf: " + cfd->GetName() + ", file_number: " +
|
||||
std::to_string(current_output().meta.fd.GetNumber()) + ", reason: " +
|
||||
TableBuilderNewTableReasonToString(add_context.new_table_reason) +
|
||||
", key: " + key_for_diagnostics;
|
||||
return Status::Corruption(
|
||||
"TableBuilder requested a new table without accepting any rows",
|
||||
detail);
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(
|
||||
compaction->immutable_options().info_log,
|
||||
"[%s] Compaction output table builder requested a new table before "
|
||||
"key %s for file #%" PRIu64 ". reason: %s",
|
||||
cfd->GetName().c_str(), key_for_diagnostics.c_str(),
|
||||
current_output().meta.fd.GetNumber(),
|
||||
TableBuilderNewTableReasonToString(add_context.new_table_reason));
|
||||
|
||||
s = close_file_func(c_iter.InputStatus(), prev_iter_output_internal_key,
|
||||
key, &c_iter, *this);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
grandparent_boundary_switched_num_ = 0;
|
||||
grandparent_overlapped_bytes_ =
|
||||
GetCurrentKeyGrandparentOverlappedBytes(key);
|
||||
range_tombstone_lower_bound_.Clear();
|
||||
|
||||
s = open_file_func(*this);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
PrepareNewOutputForFirstKey(c_iter);
|
||||
retried_after_new_table = true;
|
||||
}
|
||||
|
||||
if (partitioner_ && !partitioner_prefix_valid_) {
|
||||
last_key_for_partitioner_.assign(c_iter.user_key().data_,
|
||||
c_iter.user_key().size_);
|
||||
s = current_output().validator.Add(key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
builder_->Add(key, value);
|
||||
|
||||
stats_.num_output_records++;
|
||||
current_output_file_size_ = builder_->EstimatedFileSize();
|
||||
@@ -608,13 +512,10 @@ Status CompactionOutputs::AddRangeDels(
|
||||
assert(!range_del_agg.IsEmpty());
|
||||
FileMetaData& meta = current_output().meta;
|
||||
const Comparator* ucmp = icmp.user_comparator();
|
||||
InternalKey lower_bound_buf;
|
||||
InternalKey upper_bound_buf;
|
||||
Slice lower_bound_guard;
|
||||
Slice upper_bound_guard;
|
||||
InternalKey lower_bound_buf, upper_bound_buf;
|
||||
Slice lower_bound_guard, upper_bound_guard;
|
||||
std::string smallest_user_key;
|
||||
const Slice* lower_bound;
|
||||
const Slice* upper_bound;
|
||||
const Slice *lower_bound, *upper_bound;
|
||||
|
||||
// We first determine the internal key lower_bound and upper_bound for
|
||||
// this output file. All and only range tombstones that overlap with
|
||||
@@ -916,10 +817,10 @@ void CompactionOutputs::FillFilesToCutForTtl() {
|
||||
*(compaction_->inputs(compaction_->num_input_levels() - 1));
|
||||
for (FileMetaData* file : olevel) {
|
||||
// Worth filtering out by start and end?
|
||||
uint64_t oldest_ancestor_time = file->TryGetOldestAncestorTime();
|
||||
uint64_t oldest_ancester_time = file->TryGetOldestAncesterTime();
|
||||
// We put old files if they are not too small to prevent a flood
|
||||
// of small files.
|
||||
if (oldest_ancestor_time < old_age_thres &&
|
||||
if (oldest_ancester_time < old_age_thres &&
|
||||
file->fd.GetFileSize() >
|
||||
compaction_->mutable_cf_options().target_file_size_base / 2) {
|
||||
files_to_cut_for_ttl_.push_back(file);
|
||||
|
||||
@@ -245,11 +245,6 @@ class CompactionOutputs {
|
||||
// before processing the current key in compaction iterator.
|
||||
bool ShouldStopBefore(const CompactionIterator& c_iter);
|
||||
|
||||
// Re-establishes per-file state that depends on the first key accepted by a
|
||||
// freshly opened builder. This keeps caller-requested file cuts equivalent to
|
||||
// RocksDB's built-in file-boundary decisions for partition tracking.
|
||||
void PrepareNewOutputForFirstKey(const CompactionIterator& c_iter);
|
||||
|
||||
void Cleanup() {
|
||||
if (builder_ != nullptr) {
|
||||
// May happen if we get a shutdown call in the middle of compaction
|
||||
@@ -376,7 +371,7 @@ class CompactionOutputs {
|
||||
// repetitive comparison in ShouldStopBefore()
|
||||
const InternalKey* local_output_split_key_ = nullptr;
|
||||
|
||||
// Some identified files with old oldest ancestor time and the range should be
|
||||
// Some identified files with old oldest ancester time and the range should be
|
||||
// isolated out so that the output file(s) in that range can be merged down
|
||||
// for TTL and clear the timestamps for the range.
|
||||
std::vector<FileMetaData*> files_to_cut_for_ttl_;
|
||||
|
||||
@@ -236,10 +236,7 @@ void CompactionPicker::GetRange(const CompactionInputFiles& inputs1,
|
||||
} else if (inputs2.empty()) {
|
||||
GetRange(inputs1, smallest, largest);
|
||||
} else {
|
||||
InternalKey smallest1;
|
||||
InternalKey smallest2;
|
||||
InternalKey largest1;
|
||||
InternalKey largest2;
|
||||
InternalKey smallest1, smallest2, largest1, largest2;
|
||||
GetRange(inputs1, &smallest1, &largest1);
|
||||
GetRange(inputs2, &smallest2, &largest2);
|
||||
*smallest =
|
||||
@@ -289,8 +286,7 @@ bool CompactionPicker::ExpandInputsToCleanCut(const std::string& /*cf_name*/,
|
||||
return true;
|
||||
}
|
||||
|
||||
InternalKey smallest;
|
||||
InternalKey largest;
|
||||
InternalKey smallest, largest;
|
||||
|
||||
// Keep expanding inputs until we are sure that there is a "clean cut"
|
||||
// boundary between the files in input and the surrounding files.
|
||||
@@ -364,8 +360,7 @@ bool CompactionPicker::FilesRangeOverlapWithCompaction(
|
||||
|
||||
// TODO: Intra L0 compactions can have the ranges overlapped, but the input
|
||||
// files cannot be overlapped in the order of L0 files.
|
||||
InternalKey smallest;
|
||||
InternalKey largest;
|
||||
InternalKey smallest, largest;
|
||||
GetRange(inputs, &smallest, &largest, Compaction::kInvalidLevel);
|
||||
if (proximal_level != Compaction::kInvalidLevel) {
|
||||
if (ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
@@ -375,8 +370,7 @@ bool CompactionPicker::FilesRangeOverlapWithCompaction(
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
InternalKey proximal_smallest;
|
||||
InternalKey proximal_largest;
|
||||
InternalKey proximal_smallest, proximal_largest;
|
||||
GetRange(inputs, &proximal_smallest, &proximal_largest, level);
|
||||
if (RangeOverlapWithCompaction(proximal_smallest.user_key(),
|
||||
proximal_largest.user_key(),
|
||||
@@ -552,8 +546,7 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
assert(vstorage->NumLevelFiles(l) == 0);
|
||||
}
|
||||
|
||||
InternalKey smallest;
|
||||
InternalKey largest;
|
||||
InternalKey smallest, largest;
|
||||
|
||||
// Get the range one last time.
|
||||
GetRange(*inputs, &smallest, &largest);
|
||||
@@ -586,8 +579,7 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
CompactionInputFiles expanded_inputs;
|
||||
expanded_inputs.level = input_level;
|
||||
// Get closed interval of output level
|
||||
InternalKey all_start;
|
||||
InternalKey all_limit;
|
||||
InternalKey all_start, all_limit;
|
||||
GetRange(*inputs, *output_level_inputs, &all_start, &all_limit);
|
||||
bool try_overlapping_inputs = true;
|
||||
if (only_expand_towards_right) {
|
||||
@@ -613,8 +605,7 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
if (try_overlapping_inputs && expanded_inputs.size() > inputs->size() &&
|
||||
!AreFilesInCompaction(expanded_inputs.files) &&
|
||||
output_level_inputs_size + expanded_inputs_size < limit) {
|
||||
InternalKey new_start;
|
||||
InternalKey new_limit;
|
||||
InternalKey new_start, new_limit;
|
||||
GetRange(expanded_inputs, &new_start, &new_limit);
|
||||
CompactionInputFiles expanded_output_level_inputs;
|
||||
expanded_output_level_inputs.level = output_level;
|
||||
@@ -664,8 +655,7 @@ void CompactionPicker::GetGrandparents(
|
||||
VersionStorageInfo* vstorage, const CompactionInputFiles& inputs,
|
||||
const CompactionInputFiles& output_level_inputs,
|
||||
std::vector<FileMetaData*>* grandparents) {
|
||||
InternalKey start;
|
||||
InternalKey limit;
|
||||
InternalKey start, limit;
|
||||
GetRange(inputs, output_level_inputs, &start, &limit);
|
||||
// Compute the set of grandparent files that overlap this compaction
|
||||
// (parent == level+1; grandparent == level+2 or the first
|
||||
@@ -1313,8 +1303,7 @@ bool CompactionPicker::GetOverlappingL0Files(
|
||||
// Two level 0 compaction won't run at the same time, so don't need to worry
|
||||
// about files on level 0 being compacted.
|
||||
assert(level0_compactions_in_progress()->empty());
|
||||
InternalKey smallest;
|
||||
InternalKey largest;
|
||||
InternalKey smallest, largest;
|
||||
GetRange(*start_level_inputs, &smallest, &largest);
|
||||
// Note that the next call will discard the file we placed in
|
||||
// c->inputs_[0] earlier and replace it with an overlapping set
|
||||
|
||||
@@ -276,7 +276,7 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
NullCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
~NullCompactionPicker() override = default;
|
||||
virtual ~NullCompactionPicker() {}
|
||||
|
||||
// Always return "nullptr"
|
||||
Compaction* PickCompaction(
|
||||
|
||||
@@ -105,10 +105,9 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
TableReader* reader = f->fd.pinned_reader.Get();
|
||||
if (reader != nullptr && reader->GetTableProperties() != nullptr) {
|
||||
uint64_t newest_key_time = f->TryGetNewestKeyTime();
|
||||
uint64_t oldest_ancestor_time =
|
||||
reader->GetTableProperties()->oldest_ancestor_time;
|
||||
uint64_t creation_time = reader->GetTableProperties()->creation_time;
|
||||
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
|
||||
? oldest_ancestor_time
|
||||
? creation_time
|
||||
: newest_key_time;
|
||||
if (est_newest_key_time == kUnknownNewestKeyTime ||
|
||||
est_newest_key_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
@@ -166,13 +165,13 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
for (const auto& f : inputs[0].files) {
|
||||
assert(f);
|
||||
uint64_t newest_key_time = f->TryGetNewestKeyTime();
|
||||
uint64_t oldest_ancestor_time = 0;
|
||||
uint64_t creation_time = 0;
|
||||
TableReader* reader = f->fd.pinned_reader.Get();
|
||||
if (reader != nullptr && reader->GetTableProperties() != nullptr) {
|
||||
oldest_ancestor_time = reader->GetTableProperties()->oldest_ancestor_time;
|
||||
creation_time = reader->GetTableProperties()->creation_time;
|
||||
}
|
||||
uint64_t est_newest_key_time = newest_key_time == kUnknownNewestKeyTime
|
||||
? oldest_ancestor_time
|
||||
? creation_time
|
||||
: newest_key_time;
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
@@ -226,8 +225,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
vstorage->LevelFiles(last_level);
|
||||
|
||||
// Compute effective size and limit for comparison.
|
||||
uint64_t effective_size;
|
||||
uint64_t effective_max;
|
||||
uint64_t effective_size, effective_max;
|
||||
GetEffectiveSizeAndLimit(fifo_opts, total_size,
|
||||
vstorage->GetBlobStats().total_file_size,
|
||||
&effective_size, &effective_max);
|
||||
|
||||
@@ -393,8 +393,7 @@ void LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion() {
|
||||
}
|
||||
|
||||
size_t start_index = vstorage_->FilesByCompactionPri(start_level_)[0];
|
||||
InternalKey smallest;
|
||||
InternalKey largest;
|
||||
InternalKey smallest, largest;
|
||||
// Constraint 4 (No need to check again later)
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
CompactionInputFiles output_level_inputs;
|
||||
@@ -676,8 +675,7 @@ bool LevelCompactionBuilder::TryPickL0TrivialMove() {
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
|
||||
InternalKey my_smallest;
|
||||
InternalKey my_largest;
|
||||
InternalKey my_smallest, my_largest;
|
||||
for (auto it = level_files.rbegin(); it != level_files.rend(); ++it) {
|
||||
CompactionInputFiles output_level_inputs;
|
||||
output_level_inputs.level = output_level_;
|
||||
@@ -888,8 +886,7 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
// Note we rely on ExpandInputsToCleanCut() to tell us whether any output-
|
||||
// level files are locked, not just the extra ones pulled in for user-key
|
||||
// overlap.
|
||||
InternalKey smallest;
|
||||
InternalKey largest;
|
||||
InternalKey smallest, largest;
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
CompactionInputFiles output_level_inputs;
|
||||
output_level_inputs.level = output_level_;
|
||||
|
||||
@@ -120,7 +120,7 @@ class CompactionPickerTestBase : public testing::Test {
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0, bool marked_for_compact = false,
|
||||
Temperature temperature = Temperature::kUnknown,
|
||||
uint64_t oldest_ancestor_time = kUnknownOldestAncestorTime,
|
||||
uint64_t oldest_ancestor_time = kUnknownOldestAncesterTime,
|
||||
uint64_t newest_key_time = kUnknownNewestKeyTime,
|
||||
Slice ts_of_smallest = Slice(), Slice ts_of_largest = Slice(),
|
||||
uint64_t epoch_number = kUnknownEpochNumber) {
|
||||
@@ -162,15 +162,15 @@ class CompactionPickerTestBase : public testing::Test {
|
||||
FileMetaData* f = new FileMetaData(
|
||||
file_number, path_id, file_size, smallest_ikey, largest_ikey,
|
||||
smallest_seq, largest_seq, marked_for_compact, temperature,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncestorTime,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, epoch_number, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
|
||||
true /* user_defined_timestamps_persisted */, "" /* min timestamp */,
|
||||
"" /* max timestamp */);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
// oldest_ancestor_time is only used if newest_key_time is not available
|
||||
f->oldest_ancestor_time = oldest_ancestor_time;
|
||||
// oldest_ancester_time is only used if newest_key_time is not available
|
||||
f->oldest_ancester_time = oldest_ancestor_time;
|
||||
// Set min/max timestamps for UDT support
|
||||
if (!ts_of_smallest.empty()) {
|
||||
f->min_timestamp = ts_of_smallest.ToString();
|
||||
@@ -325,7 +325,7 @@ class CompactionPickerU64TsTest : public CompactionPickerTestBase {
|
||||
/*smallest_seq=*/10, /*largest_seq=*/40,
|
||||
/*compensated_file_size=*/1000,
|
||||
/*marked_for_compact=*/false, Temperature::kUnknown,
|
||||
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts_small, ts_large);
|
||||
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts_small, ts_large);
|
||||
|
||||
std::string full_history_ts_low = MakeU64Timestamp(full_history_ts_low_val);
|
||||
|
||||
@@ -350,12 +350,12 @@ class CompactionPickerU64TsTest : public CompactionPickerTestBase {
|
||||
/*smallest_seq=*/100, /*largest_seq=*/100,
|
||||
/*compensated_file_size=*/file_size,
|
||||
/*marked_for_compact=*/false, Temperature::kUnknown,
|
||||
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts1, ts2);
|
||||
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts1, ts2);
|
||||
Add(0, 2U, "150", "250", file_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/200, /*largest_seq=*/200,
|
||||
/*compensated_file_size=*/file_size,
|
||||
/*marked_for_compact=*/false, Temperature::kUnknown,
|
||||
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts1, ts2);
|
||||
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts1, ts2);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1452,14 +1452,14 @@ TEST_F(CompactionPickerTest, FIFOToCold1) {
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0 /* level */, 4U /* file_number */, "260", "300", 1 * kFileSize, 0,
|
||||
2500, 2600, 0, true, Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: threshold_time - 2000 /* oldest_ancestor_time */,
|
||||
newestKeyTimeKnown ? threshold_time - 2000
|
||||
: kUnknownNewestKeyTime /* newest_key_time */);
|
||||
// Qualifies for compaction to kCold.
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 3000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 3000,
|
||||
newestKeyTimeKnown ? threshold_time - 3000
|
||||
: kUnknownNewestKeyTime /* newest_key_time */);
|
||||
UpdateVersionStorageInfo();
|
||||
@@ -1504,32 +1504,32 @@ TEST_F(CompactionPickerTest, FIFOToColdMaxCompactionSize) {
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: static_cast<uint64_t>(current_time) - 100,
|
||||
newestKeyTimeKnown ? static_cast<uint64_t>(current_time) - 100
|
||||
: kUnknownNewestKeyTime);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time + 100,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time + 100,
|
||||
newestKeyTimeKnown ? threshold_time + 100 : kUnknownNewestKeyTime);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 2000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 2000,
|
||||
newestKeyTimeKnown ? threshold_time - 2000 : kUnknownNewestKeyTime);
|
||||
// The following two files qualify for compaction to kCold.
|
||||
// But only the last two should be included to respect
|
||||
// `max_compaction_bytes`.
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 3000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 3000,
|
||||
newestKeyTimeKnown ? threshold_time - 3000 : kUnknownNewestKeyTime);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 4000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 4000,
|
||||
newestKeyTimeKnown ? threshold_time - 4000 : kUnknownNewestKeyTime);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 5000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 5000,
|
||||
newestKeyTimeKnown ? threshold_time - 5000 : kUnknownNewestKeyTime);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -1574,30 +1574,30 @@ TEST_F(CompactionPickerTest, FIFOToColdWithExistingCold) {
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: static_cast<uint64_t>(current_time) - 100,
|
||||
newestKeyTimeKnown ? static_cast<uint64_t>(current_time) - 100
|
||||
: kUnknownNewestKeyTime);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time + 100,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time + 100,
|
||||
newestKeyTimeKnown ? threshold_time + 100 : kUnknownNewestKeyTime);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 2000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 2000,
|
||||
newestKeyTimeKnown ? threshold_time - 2000 : kUnknownNewestKeyTime);
|
||||
// The following two files qualify for compaction to kCold.
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 3000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 3000,
|
||||
newestKeyTimeKnown ? threshold_time - 3000 : kUnknownNewestKeyTime);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 4000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 4000,
|
||||
newestKeyTimeKnown ? threshold_time - 4000 : kUnknownNewestKeyTime);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kCold,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 5000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 5000,
|
||||
newestKeyTimeKnown ? threshold_time - 5000 : kUnknownNewestKeyTime);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -1642,30 +1642,30 @@ TEST_F(CompactionPickerTest, FIFOToColdWithHotBetweenCold) {
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: static_cast<uint64_t>(current_time) - 100,
|
||||
newestKeyTimeKnown ? static_cast<uint64_t>(current_time) - 100
|
||||
: kUnknownNewestKeyTime);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time + 100,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time + 100,
|
||||
newestKeyTimeKnown ? threshold_time + 100 : kUnknownNewestKeyTime);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 2000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 2000,
|
||||
newestKeyTimeKnown ? threshold_time - 2000 : kUnknownNewestKeyTime);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kCold,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 3000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 3000,
|
||||
newestKeyTimeKnown ? threshold_time - 3000 : kUnknownNewestKeyTime);
|
||||
// Qualifies for compaction to kCold.
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 4000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 4000,
|
||||
newestKeyTimeKnown ? threshold_time - 4000 : kUnknownNewestKeyTime);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kCold,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime : threshold_time - 5000,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime : threshold_time - 5000,
|
||||
newestKeyTimeKnown ? threshold_time - 5000 : kUnknownNewestKeyTime);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -1714,36 +1714,36 @@ TEST_F(CompactionPickerTest, FIFOToHotAndWarm) {
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: static_cast<uint64_t>(current_time) - 100,
|
||||
newestKeyTimeKnown ? static_cast<uint64_t>(current_time) - 100
|
||||
: kUnknownNewestKeyTime);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: hot_threshold_time + 100,
|
||||
newestKeyTimeKnown ? hot_threshold_time + 100 : kUnknownNewestKeyTime);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: hot_threshold_time - 200,
|
||||
newestKeyTimeKnown ? hot_threshold_time - 200 : kUnknownNewestKeyTime);
|
||||
// Qualifies for Hot
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: warm_threshold_time - 100,
|
||||
newestKeyTimeKnown ? warm_threshold_time - 100 : kUnknownNewestKeyTime);
|
||||
// Qualifies for Warm
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: warm_threshold_time - 4000,
|
||||
newestKeyTimeKnown ? warm_threshold_time - 4000
|
||||
: kUnknownNewestKeyTime);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kUnknown,
|
||||
newestKeyTimeKnown ? kUnknownOldestAncestorTime
|
||||
newestKeyTimeKnown ? kUnknownOldestAncesterTime
|
||||
: warm_threshold_time - 5000,
|
||||
newestKeyTimeKnown ? warm_threshold_time - 5000
|
||||
: kUnknownNewestKeyTime);
|
||||
@@ -2490,7 +2490,7 @@ TEST_F(CompactionPickerTest, FileTtlBoosterLargeNumLevels) {
|
||||
FileTtlBooster booster(kCurrentTime, /*ttl=*/2048,
|
||||
/*num_non_empty_levels=*/100, /*level=*/1);
|
||||
FileMetaData meta;
|
||||
meta.oldest_ancestor_time = kCurrentTime - 1023;
|
||||
meta.oldest_ancester_time = kCurrentTime - 1023;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
}
|
||||
|
||||
@@ -2509,47 +2509,47 @@ TEST_F(CompactionPickerTest, FileTtlBooster) {
|
||||
FileTtlBooster booster(kCurrentTime, 2048, 7, 3);
|
||||
|
||||
// Not triggering if the file is younger than ttl/2
|
||||
meta.oldest_ancestor_time = kCurrentTime - 1023;
|
||||
meta.oldest_ancester_time = kCurrentTime - 1023;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancestor_time = kCurrentTime - 1024;
|
||||
meta.oldest_ancester_time = kCurrentTime - 1024;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancestor_time = kCurrentTime + 10;
|
||||
meta.oldest_ancester_time = kCurrentTime + 10;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
|
||||
// Within one boosting step
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 6);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 6);
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
|
||||
// One boosting step
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 7);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 7);
|
||||
ASSERT_EQ(2, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 8);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 8);
|
||||
ASSERT_EQ(2, booster.GetBoostScore(&meta));
|
||||
|
||||
// Multiple boosting steps
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 30);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 30);
|
||||
ASSERT_EQ(5, booster.GetBoostScore(&meta));
|
||||
|
||||
// Very high boosting steps
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 120 + 700);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 700);
|
||||
ASSERT_EQ(101, booster.GetBoostScore(&meta));
|
||||
}
|
||||
{
|
||||
// Test second last level
|
||||
FileTtlBooster booster(kCurrentTime, 2048, 7, 5);
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 480);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 480);
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 480 + 60);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 480 + 60);
|
||||
ASSERT_EQ(3, booster.GetBoostScore(&meta));
|
||||
}
|
||||
{
|
||||
// Test last level
|
||||
FileTtlBooster booster(kCurrentTime, 2048, 7, 6);
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 480);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 480);
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancestor_time = kCurrentTime - (1024 + 480 + 60);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 480 + 60);
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancestor_time = kCurrentTime - 3000;
|
||||
meta.oldest_ancester_time = kCurrentTime - 3000;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
}
|
||||
}
|
||||
@@ -3577,21 +3577,21 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap) {
|
||||
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550, /*compensated_file_size*/ 0,
|
||||
/*marked_for_compact*/ false, /* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 3);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450,
|
||||
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 2);
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300,
|
||||
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 1);
|
||||
@@ -3619,7 +3619,7 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap) {
|
||||
// Simulate a flush and mark the file for compaction
|
||||
Add(0, 7U, "150", "200", kFileSize, 0, 551, 600, 0, true,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 4);
|
||||
@@ -3647,7 +3647,7 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap2) {
|
||||
// Mark file number 4 for compaction
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 1);
|
||||
@@ -3674,14 +3674,14 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap2) {
|
||||
AddVersionStorage();
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550, /*compensated_file_size*/ 0,
|
||||
/*marked_for_compact*/ false, /* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 3);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450,
|
||||
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 2);
|
||||
@@ -3859,27 +3859,27 @@ TEST_F(CompactionPickerTest, UniversalMarkedL0Overlap2) {
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 260, 300,
|
||||
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 4);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 201, 250, 0, true,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 3);
|
||||
Add(0, 3U, "301", "350", 4 * kFileSize, 0, 101, 150,
|
||||
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 2);
|
||||
Add(0, 6U, "501", "750", 8 * kFileSize, 0, 50, 100,
|
||||
/*compensated_file_size*/ 0, /*marked_for_compact*/ false,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 1);
|
||||
@@ -3906,14 +3906,14 @@ TEST_F(CompactionPickerTest, UniversalMarkedL0Overlap2) {
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550, /*compensated_file_size*/ 0,
|
||||
/*marked_for_compact*/ false,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 6);
|
||||
Add(0, 2U, "201", "250", kFileSize, 0, 401, 450, /*compensated_file_size*/ 0,
|
||||
/*marked_for_compact*/ false,
|
||||
/* temperature*/ Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime,
|
||||
/*ts_of_smallest*/ Slice(), /*ts_of_largest*/ Slice(),
|
||||
/*epoch_number*/ 5);
|
||||
@@ -4149,7 +4149,7 @@ TEST_F(CompactionPickerU64TsTest, Overlap) {
|
||||
/*file_size=*/1U, /*path_id=*/0,
|
||||
/*smallest_seq=*/100, /*largest_seq=*/100, /*compensated_file_size=*/0,
|
||||
/*marked_for_compact=*/false, /*temperature=*/Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime, ts1, ts2);
|
||||
UpdateVersionStorageInfo();
|
||||
}
|
||||
@@ -4231,12 +4231,12 @@ TEST_F(CompactionPickerU64TsTest, CannotTrivialMoveUniversal) {
|
||||
Add(1, 1U, "150", "150", kFileSize, /*path_id=*/0, /*smallest_seq=*/100,
|
||||
/*largest_seq=*/100, /*compensated_file_size=*/kFileSize,
|
||||
/*marked_for_compact=*/false, Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime, ts1, ts2);
|
||||
Add(2, 2U, "150", "150", kFileSize, /*path_id=*/0, /*smallest_seq=*/100,
|
||||
/*largest_seq=*/100, /*compensated_file_size=*/kFileSize,
|
||||
/*marked_for_compact=*/false, Temperature::kUnknown,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncestorTime,
|
||||
/*oldest_ancestor_time*/ kUnknownOldestAncesterTime,
|
||||
/*newest_key_time*/ kUnknownNewestKeyTime, ts3, ts4);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -5033,12 +5033,12 @@ TEST_F(CompactionPickerTest, StandaloneRangeDeletionOnlyPicksOlderFiles) {
|
||||
// Create L0 files with overlapping ranges
|
||||
// File 1: newest regular file (epoch 5), keys [100, 200]
|
||||
Add(0, 1U, "100", "200", 1U, 0, 100, 100, 0, false, Temperature::kUnknown,
|
||||
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, Slice(), Slice(), 5);
|
||||
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, Slice(), Slice(), 5);
|
||||
|
||||
// File 2: standalone range deletion (epoch 4), keys [150, 250]
|
||||
// This file should be marked as having only range deletions
|
||||
Add(0, 2U, "150", "250", 1U, 0, 200, 200, 0, true, Temperature::kUnknown,
|
||||
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, Slice(), Slice(), 4);
|
||||
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, Slice(), Slice(), 4);
|
||||
|
||||
// Manually set file 2 as standalone range deletion
|
||||
FileMetaData* range_del_file = file_map_[2U].first;
|
||||
@@ -5169,7 +5169,7 @@ TEST_F(CompactionPickerU64TsTest,
|
||||
/*smallest_seq=*/10, /*largest_seq=*/40,
|
||||
/*compensated_file_size=*/1000,
|
||||
/*marked_for_compact=*/false, Temperature::kUnknown,
|
||||
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts_small, ts_large);
|
||||
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts_small, ts_large);
|
||||
|
||||
// Update version storage with empty full_history_ts_low
|
||||
UpdateVersionStorageInfo();
|
||||
@@ -5209,7 +5209,7 @@ TEST_F(CompactionPickerU64TsTest,
|
||||
/*smallest_seq=*/10, /*largest_seq=*/40,
|
||||
/*compensated_file_size=*/1000,
|
||||
/*marked_for_compact=*/false, Temperature::kUnknown,
|
||||
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts_small, ts_large);
|
||||
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts_small, ts_large);
|
||||
|
||||
// Simulate a file whose min/max timestamp was never recorded.
|
||||
ASSERT_EQ(1U, vstorage_->LevelFiles(5).size());
|
||||
@@ -5249,7 +5249,7 @@ TEST_F(CompactionPickerU64TsTest,
|
||||
/*smallest_seq=*/10, /*largest_seq=*/40,
|
||||
/*compensated_file_size=*/1000,
|
||||
/*marked_for_compact=*/false, Temperature::kUnknown,
|
||||
kUnknownOldestAncestorTime, kUnknownNewestKeyTime, ts_small, ts_large);
|
||||
kUnknownOldestAncesterTime, kUnknownNewestKeyTime, ts_small, ts_large);
|
||||
|
||||
// Simulate a file whose min/max timestamp was never recorded.
|
||||
ASSERT_EQ(1U, vstorage_->LevelFiles(5).size());
|
||||
@@ -5715,13 +5715,13 @@ TEST_F(CompactionPickerTest, FIFOTTLBlobEstimationSingleLevel) {
|
||||
|
||||
uint64_t recent_time = static_cast<uint64_t>(time(nullptr));
|
||||
Add(0, 1U, "100", "200", 50 * 1024, 0, 100, 100, 0, false,
|
||||
Temperature::kUnknown, kUnknownOldestAncestorTime, recent_time);
|
||||
Temperature::kUnknown, kUnknownOldestAncesterTime, recent_time);
|
||||
Add(0, 2U, "200", "300", 50 * 1024, 0, 100, 100, 0, false,
|
||||
Temperature::kUnknown, kUnknownOldestAncestorTime, recent_time);
|
||||
Temperature::kUnknown, kUnknownOldestAncesterTime, recent_time);
|
||||
Add(0, 3U, "300", "400", 50 * 1024, 0, 100, 100, 0, false,
|
||||
Temperature::kUnknown, kUnknownOldestAncestorTime, 1);
|
||||
Temperature::kUnknown, kUnknownOldestAncesterTime, 1);
|
||||
Add(0, 4U, "400", "500", 50 * 1024, 0, 100, 100, 0, false,
|
||||
Temperature::kUnknown, kUnknownOldestAncestorTime, 1);
|
||||
Temperature::kUnknown, kUnknownOldestAncesterTime, 1);
|
||||
if (blob_total > 0) {
|
||||
AddBlobFile(100, blob_total / 2);
|
||||
AddBlobFile(101, blob_total / 2);
|
||||
@@ -5794,13 +5794,13 @@ TEST_F(CompactionPickerTest, FIFOTTLBlobEstimationMultiLevel) {
|
||||
uint64_t recent_time = static_cast<uint64_t>(time(nullptr));
|
||||
// L0 files: 2 recent, 2 expired
|
||||
Add(0, 1U, "100", "200", 50 * 1024, 0, 100, 100, 0, false,
|
||||
Temperature::kUnknown, kUnknownOldestAncestorTime, recent_time);
|
||||
Temperature::kUnknown, kUnknownOldestAncesterTime, recent_time);
|
||||
Add(0, 2U, "200", "300", 50 * 1024, 0, 100, 100, 0, false,
|
||||
Temperature::kUnknown, kUnknownOldestAncestorTime, recent_time);
|
||||
Temperature::kUnknown, kUnknownOldestAncesterTime, recent_time);
|
||||
Add(0, 3U, "300", "400", 50 * 1024, 0, 100, 100, 0, false,
|
||||
Temperature::kUnknown, kUnknownOldestAncestorTime, 1);
|
||||
Temperature::kUnknown, kUnknownOldestAncesterTime, 1);
|
||||
Add(0, 4U, "400", "500", 50 * 1024, 0, 100, 100, 0, false,
|
||||
Temperature::kUnknown, kUnknownOldestAncestorTime, 1);
|
||||
Temperature::kUnknown, kUnknownOldestAncesterTime, 1);
|
||||
// L2 legacy migration file
|
||||
Add(2, 10U, "100", "600", 200 * 1024);
|
||||
// Blob files (associated with ALL levels)
|
||||
@@ -5825,8 +5825,8 @@ TEST_F(CompactionPickerTest, FIFOBlobAwareSizeDropping) {
|
||||
// Sub-case 1: Single-level. SST = 200KB, blob = 500MB, limit = 200MB.
|
||||
// effective_size ~ 500MB >> 200MB -> drops from L0.
|
||||
{
|
||||
SetupFIFORatioBased(/*max_table_files_size=*/200ULL * 1024 * 1024,
|
||||
/*max_data_files_size=*/200ULL * 1024 * 1024,
|
||||
SetupFIFORatioBased(/*max_table=*/200ULL * 1024 * 1024,
|
||||
/*max_data=*/200ULL * 1024 * 1024,
|
||||
/*trigger=*/4,
|
||||
/*allow_compaction=*/true,
|
||||
/*use_kv_ratio=*/false);
|
||||
@@ -5880,8 +5880,8 @@ TEST_F(CompactionPickerTest, FIFOBlobAwareSizeDropping) {
|
||||
// Sub-case 3: Under limit. SST = 256KB, blob = 200MB, limit = 1GB.
|
||||
// effective_size ~ 200MB < 1GB -> no dropping.
|
||||
{
|
||||
SetupFIFORatioBased(/*max_table_files_size=*/1ULL * 1024 * 1024 * 1024,
|
||||
/*max_data_files_size=*/1ULL * 1024 * 1024 * 1024,
|
||||
SetupFIFORatioBased(/*max_table=*/1ULL * 1024 * 1024 * 1024,
|
||||
/*max_data=*/1ULL * 1024 * 1024 * 1024,
|
||||
/*trigger=*/4,
|
||||
/*allow_compaction=*/true,
|
||||
/*use_kv_ratio=*/true);
|
||||
@@ -6034,9 +6034,7 @@ class FIFORatioBasedCompactionPickingTest : public CompactionPickerTest {
|
||||
/*existing_snapshots=*/{}, /*snapshot_checker=*/nullptr,
|
||||
vstorage_.get(), &log_buffer_, /*full_history_ts_low=*/""));
|
||||
|
||||
if (!compaction) {
|
||||
return {};
|
||||
}
|
||||
if (!compaction) return {};
|
||||
|
||||
if (out_reason) {
|
||||
*out_reason = compaction->compaction_reason();
|
||||
@@ -6082,9 +6080,7 @@ class FIFORatioBasedCompactionPickingTest : public CompactionPickerTest {
|
||||
|
||||
size_t insert_pos = oldest_input_pos;
|
||||
for (size_t idx : sorted_indices) {
|
||||
if (idx < oldest_input_pos) {
|
||||
insert_pos--;
|
||||
}
|
||||
if (idx < oldest_input_pos) insert_pos--;
|
||||
}
|
||||
insert_pos = std::min(insert_pos, files.size());
|
||||
files.insert(files.begin() + insert_pos,
|
||||
@@ -6108,13 +6104,10 @@ class FIFORatioBasedCompactionPickingTest : public CompactionPickerTest {
|
||||
sizes.push_back(f.size);
|
||||
}
|
||||
}
|
||||
if (sizes.empty()) {
|
||||
return {0, 0, 0, 0.0, 0.0};
|
||||
}
|
||||
if (sizes.empty()) return {0, 0, 0, 0.0, 0.0};
|
||||
|
||||
uint64_t sum = 0;
|
||||
uint64_t min_s = UINT64_MAX;
|
||||
uint64_t max_s = 0;
|
||||
uint64_t min_s = UINT64_MAX, max_s = 0;
|
||||
for (uint64_t s : sizes) {
|
||||
sum += s;
|
||||
min_s = std::min(min_s, s);
|
||||
@@ -6251,8 +6244,7 @@ class FIFORatioBasedCompactionPickingTest : public CompactionPickerTest {
|
||||
if (!inputs.empty() && reason == CompactionReason::kFIFOReduceNumFiles) {
|
||||
// Compute target from the picker's perspective: we need to estimate
|
||||
// it the same way the picker does.
|
||||
uint64_t total_sst = 0;
|
||||
uint64_t total_blob = 0;
|
||||
uint64_t total_sst = 0, total_blob = 0;
|
||||
for (const auto& f : files) {
|
||||
total_sst += f.size;
|
||||
total_blob += f.blob_size;
|
||||
|
||||
@@ -573,8 +573,7 @@ bool UniversalCompactionBuilder::IsInputFilesNonOverlapping(Compaction* c) {
|
||||
auto comparator = icmp_->user_comparator();
|
||||
int first_iter = 1;
|
||||
|
||||
InputFileInfo prev;
|
||||
InputFileInfo curr;
|
||||
InputFileInfo prev, curr;
|
||||
|
||||
SmallestKeyHeap smallest_key_priority_q =
|
||||
create_level_heap(c, icmp_->user_comparator());
|
||||
@@ -1422,8 +1421,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
// Since we need to go from lower level up and this is in the reverse
|
||||
// order, compared to level order, we first write to an reversed
|
||||
// data structure and finally copy them to compaction inputs.
|
||||
InternalKey smallest;
|
||||
InternalKey largest;
|
||||
InternalKey smallest, largest;
|
||||
picker_->GetRange(second_last_level_inputs, &smallest, &largest);
|
||||
std::vector<CompactionInputFiles> inputs_reverse;
|
||||
for (auto it = ++(++sorted_runs_.rbegin()); it != sorted_runs_.rend(); it++) {
|
||||
|
||||
@@ -271,7 +271,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
file.smallest_seqno, file.largest_seqno);
|
||||
meta.smallest.DecodeFrom(file.smallest_internal_key);
|
||||
meta.largest.DecodeFrom(file.largest_internal_key);
|
||||
meta.oldest_ancestor_time = file.oldest_ancestor_time;
|
||||
meta.oldest_ancester_time = file.oldest_ancester_time;
|
||||
meta.file_creation_time = file.file_creation_time;
|
||||
meta.epoch_number = file.epoch_number;
|
||||
meta.file_checksum = file.file_checksum;
|
||||
@@ -469,7 +469,7 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.GetFileSize(),
|
||||
meta.fd.smallest_seqno, meta.fd.largest_seqno,
|
||||
meta.smallest.Encode().ToString(), meta.largest.Encode().ToString(),
|
||||
meta.oldest_ancestor_time, meta.file_creation_time, meta.epoch_number,
|
||||
meta.oldest_ancester_time, meta.file_creation_time, meta.epoch_number,
|
||||
meta.file_checksum, meta.file_checksum_func_name,
|
||||
output_file.validator.GetHash(), meta.marked_for_compaction,
|
||||
meta.unique_id, *output_file.table_properties,
|
||||
@@ -601,9 +601,8 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct CompactionServiceOutputFile, largest_internal_key),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
// Misspelled key kept for compatibility with other versions
|
||||
{"oldest_ancester_time",
|
||||
{offsetof(struct CompactionServiceOutputFile, oldest_ancestor_time),
|
||||
{offsetof(struct CompactionServiceOutputFile, oldest_ancester_time),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"file_creation_time",
|
||||
@@ -921,11 +920,24 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct InternalStats::CompactionStats, count),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"counts", SizeTolerantDeserializeArray<
|
||||
int, static_cast<int>(CompactionReason::kNumOfReasons)>(
|
||||
offsetof(struct InternalStats::CompactionStats, counts),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
|
||||
{0, OptionType::kInt})},
|
||||
// The remote worker may serialize a different number of
|
||||
// CompactionReason counts than this primary expects (RocksDB versions
|
||||
// can disagree on kNumOfReasons). Deserialization tolerates the
|
||||
// mismatch: extra trailing elements are dropped, missing ones are
|
||||
// zero-filled.
|
||||
//
|
||||
// The serialized width is intentionally kept at kNumOfReasons - 1 (the
|
||||
// reduced width) so this change does NOT alter what is written -- only
|
||||
// the read path becomes tolerant. Widening the serialized width to the
|
||||
// full kNumOfReasons is a separate, later change that must not land
|
||||
// until this tolerant read has been deployed to every primary,
|
||||
// including rollback targets.
|
||||
{"counts",
|
||||
SizeTolerantDeserializeArray<
|
||||
int, static_cast<int>(CompactionReason::kNumOfReasons) - 1>(
|
||||
offsetof(struct InternalStats::CompactionStats, counts),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone,
|
||||
{0, OptionType::kInt})},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
|
||||
@@ -1900,7 +1900,7 @@ TEST_F(CompactionServiceTest, InvalidResultFallsBackToLocal) {
|
||||
TEST_F(CompactionServiceTest, CompatCheckCountsWidthTolerance) {
|
||||
// Number of per-CompactionReason counts this binary serializes and expects.
|
||||
constexpr size_t kExpected =
|
||||
static_cast<size_t>(CompactionReason::kNumOfReasons);
|
||||
static_cast<size_t>(CompactionReason::kNumOfReasons) - 1;
|
||||
constexpr size_t kLastIndex = kExpected - 1;
|
||||
constexpr int kFirstCount = 3;
|
||||
constexpr int kLastCount = 5;
|
||||
@@ -2452,8 +2452,6 @@ TEST_F(CompactionServiceTest, RemoteCompactionManifestFloorKillSwitchOff) {
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 7;
|
||||
options.max_open_files = -1;
|
||||
options.open_files_async = true;
|
||||
options.skip_stats_update_on_db_open = true;
|
||||
options.level0_file_num_compaction_trigger = 100; // no auto compaction
|
||||
options.remote_compaction_manifest_floor = false; // kill switch OFF
|
||||
ReopenWithCompactionService(&options);
|
||||
@@ -2466,15 +2464,8 @@ TEST_F(CompactionServiceTest, RemoteCompactionManifestFloorKillSwitchOff) {
|
||||
ASSERT_OK(Flush()); // L0: Delete(k)
|
||||
|
||||
// Reopen so the MANIFEST is rewritten as current state before we perturb it.
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkAsyncFileOpen:Done",
|
||||
"CompactionServiceTest::ManifestFloorKillSwitchOff:OpenDone"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "cf_1", "cf_2", "cf_3"},
|
||||
options);
|
||||
TEST_SYNC_POINT("CompactionServiceTest::ManifestFloorKillSwitchOff:OpenDone");
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
GetCompactionService()->SetCanceled(false);
|
||||
|
||||
bool floor_provided_seen = true;
|
||||
@@ -2783,8 +2774,7 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
|
||||
CompactionServiceJobInfo info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(dbname_, info.db_name);
|
||||
std::string db_id;
|
||||
std::string db_session_id;
|
||||
std::string db_id, db_session_id;
|
||||
ASSERT_OK(db_->GetDbIdentity(db_id));
|
||||
ASSERT_EQ(db_id, info.db_id);
|
||||
ASSERT_OK(db_->GetDbSessionId(db_session_id));
|
||||
|
||||
@@ -68,11 +68,11 @@ class FileTtlBooster {
|
||||
if (!enabled_) {
|
||||
return 1;
|
||||
}
|
||||
uint64_t oldest_ancestor_time = f->TryGetOldestAncestorTime();
|
||||
if (oldest_ancestor_time >= current_time_) {
|
||||
uint64_t oldest_ancester_time = f->TryGetOldestAncesterTime();
|
||||
if (oldest_ancester_time >= current_time_) {
|
||||
return 1;
|
||||
}
|
||||
uint64_t age = current_time_ - oldest_ancestor_time;
|
||||
uint64_t age = current_time_ - oldest_ancester_time;
|
||||
if (age > boost_age_start_) {
|
||||
// Use integer just for convenience.
|
||||
// We could make all file_to_order double if we want.
|
||||
|
||||
@@ -168,9 +168,6 @@ class SubcompactionState {
|
||||
? &proximal_level_outputs_
|
||||
: &compaction_outputs_;
|
||||
}
|
||||
SubcompactionState& operator=(SubcompactionState&&) = delete;
|
||||
|
||||
~SubcompactionState() = default;
|
||||
|
||||
// Add all the new files from this compaction to version_edit
|
||||
void AddOutputsEdit(VersionEdit* out_edit) const {
|
||||
|
||||
@@ -389,8 +389,7 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
// the range dels in both cold and hot SSTs
|
||||
auto snap = db_->GetSnapshot();
|
||||
latest_cold_seq = seq_history[2];
|
||||
std::string start = Key(25);
|
||||
std::string end = Key(35);
|
||||
std::string start = Key(25), end = Key(35);
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), start, end));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -621,8 +620,7 @@ TEST_F(TieredCompactionTest, DISABLED_RangeBasedTieredStorageUniversal) {
|
||||
hot_start = Key(50);
|
||||
hot_end = Key(100);
|
||||
}
|
||||
std::string start = Key(1);
|
||||
std::string end = Key(70);
|
||||
std::string start = Key(1), end = Key(70);
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), start, end));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -1270,8 +1268,7 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
// the range dels in bottommost
|
||||
auto snap = db_->GetSnapshot();
|
||||
|
||||
std::string start = Key(25);
|
||||
std::string end = Key(35);
|
||||
std::string start = Key(25), end = Key(35);
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), start, end));
|
||||
// add one small key and large key in the input level, to make sure it's able
|
||||
@@ -3048,10 +3045,9 @@ TEST_P(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
verify_db();
|
||||
|
||||
// Rewrite the middle file only. File endpoints should not change.
|
||||
std::string begin_key_buf = Key(kNumKeysPerFile + 1);
|
||||
std::string end_key_buf = Key(kNumKeysPerFile + 2);
|
||||
Slice begin_key(begin_key_buf);
|
||||
Slice end_key(end_key_buf);
|
||||
std::string begin_key_buf = Key(kNumKeysPerFile + 1),
|
||||
end_key_buf = Key(kNumKeysPerFile + 2);
|
||||
Slice begin_key(begin_key_buf), end_key(end_key_buf);
|
||||
ASSERT_OK(db_->SuggestCompactRange(db_->DefaultColumnFamily(), &begin_key,
|
||||
&end_key));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
@@ -315,9 +315,8 @@ class ComparatorDBTest
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FormatDef, ComparatorDBTest,
|
||||
testing::Values(test::kDefaultFormatVersion));
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatLatest, ComparatorDBTest,
|
||||
testing::Values(BlockBasedTableOptions::kLatestSupportedFormatVersion));
|
||||
INSTANTIATE_TEST_CASE_P(FormatLatest, ComparatorDBTest,
|
||||
testing::Values(kLatestBbtFormatVersion));
|
||||
|
||||
TEST_P(ComparatorDBTest, Bytewise) {
|
||||
for (int rand_seed = 301; rand_seed < 306; rand_seed++) {
|
||||
|
||||
+6
-12
@@ -143,8 +143,7 @@ class CorruptionTest : public testing::Test {
|
||||
}
|
||||
|
||||
void Build(int n, int start, int flush_every) {
|
||||
std::string key_space;
|
||||
std::string value_space;
|
||||
std::string key_space, value_space;
|
||||
WriteBatch batch;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (flush_every != 0 && i != 0 && i % flush_every == 0) {
|
||||
@@ -774,8 +773,7 @@ TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
|
||||
// Write must eventually fail because of corrupted table
|
||||
Status s;
|
||||
std::string tmp1;
|
||||
std::string tmp2;
|
||||
std::string tmp1, tmp2;
|
||||
bool failed = false;
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
|
||||
@@ -794,8 +792,7 @@ TEST_F(CorruptionTest, UnrelatedKeys) {
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
ASSERT_NOK(db_->VerifyChecksum());
|
||||
|
||||
std::string tmp1;
|
||||
std::string tmp2;
|
||||
std::string tmp1, tmp2;
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
|
||||
std::string v;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
|
||||
@@ -943,8 +940,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
db_.reset();
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
std::string start;
|
||||
std::string end;
|
||||
std::string start, end;
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(3, &start), Key(7, &end)));
|
||||
@@ -980,8 +976,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
Build(10, 0, 0);
|
||||
std::string start;
|
||||
std::string end;
|
||||
std::string start, end;
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(5, &start), Key(15, &end)));
|
||||
auto snap = db_->GetSnapshot();
|
||||
@@ -1017,8 +1012,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
assert(db_ != nullptr); // suppress false clang-analyze report
|
||||
std::string start;
|
||||
std::string end;
|
||||
std::string start, end;
|
||||
Build(10);
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(3, &start), Key(7, &end)));
|
||||
|
||||
+12
-188
@@ -13,9 +13,6 @@
|
||||
#include "db/log_writer.h"
|
||||
#include "db/manifest_ops.h"
|
||||
#include "db/version_edit.h"
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
#include "env/io_posix.h"
|
||||
#endif
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -26,7 +23,6 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "table/block_based/block_builder.h"
|
||||
#include "table/format.h"
|
||||
@@ -89,9 +85,11 @@ class MyFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
|
||||
explicit MyFlushBlockPolicyFactory(const int num_keys_in_block)
|
||||
: num_keys_in_block_(num_keys_in_block) {}
|
||||
|
||||
const char* Name() const override { return "MyFlushBlockPolicyFactory"; }
|
||||
virtual const char* Name() const override {
|
||||
return "MyFlushBlockPolicyFactory";
|
||||
}
|
||||
|
||||
FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new MyFlushBlockPolicy(num_keys_in_block_, data_block_builder);
|
||||
@@ -1235,10 +1233,7 @@ TEST_F(DBBasicTest, EnableDirectIOWithZeroBuf) {
|
||||
|
||||
TEST_F(DBBasicTest, UniqueSession) {
|
||||
Options options = CurrentOptions();
|
||||
std::string sid1;
|
||||
std::string sid2;
|
||||
std::string sid3;
|
||||
std::string sid4;
|
||||
std::string sid1, sid2, sid3, sid4;
|
||||
|
||||
ASSERT_OK(db_->GetDbSessionId(sid1));
|
||||
Reopen(options);
|
||||
@@ -1689,8 +1684,7 @@ TEST_P(DBBasicGetWithParam, TimedPutBasic) {
|
||||
if (option_config_ != kRowCache) {
|
||||
std::string range_del_begin = "b";
|
||||
std::string range_del_end = "baz";
|
||||
Slice begin_rdel = range_del_begin;
|
||||
Slice end_rdel = range_del_end;
|
||||
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", nullptr, use_coroutine_));
|
||||
@@ -2313,8 +2307,7 @@ TEST_F(DBBasicTest, IdentityAcrossRestarts) {
|
||||
for (bool with_manifest : {false, true}) {
|
||||
for (bool write_file : {false, true}) {
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
std::string id1;
|
||||
std::string tmp;
|
||||
std::string id1, tmp;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
ASSERT_GE(id1.size(), kMinIdSize);
|
||||
|
||||
@@ -2416,8 +2409,7 @@ TEST_F(DBBasicTest, LockFileRecovery) {
|
||||
for (bool ber : {false, true}) {
|
||||
options.best_efforts_recovery = ber;
|
||||
DestroyAndReopen(options);
|
||||
std::string id1;
|
||||
std::string id2;
|
||||
std::string id1, id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
Close();
|
||||
|
||||
@@ -4980,10 +4972,6 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2WithRangeDelInL1) {
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetNoIOUring) {
|
||||
std::vector<std::string> key_strs{Key(33), Key(54), Key(102)};
|
||||
|
||||
const bool previous_enable_io_uring = enable_io_uring;
|
||||
Defer restore_enable_io_uring([previous_enable_io_uring]() {
|
||||
enable_io_uring = previous_enable_io_uring;
|
||||
});
|
||||
enable_io_uring = false;
|
||||
ReopenDB();
|
||||
|
||||
@@ -5010,164 +4998,6 @@ TEST_P(DBMultiGetAsyncIOTest, GetNoIOUring) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, AsyncIOSupportCheckedOnReadThread) {
|
||||
#ifndef ROCKSDB_IOURING_PRESENT
|
||||
ROCKSDB_GTEST_SKIP("This test requires io_uring support");
|
||||
#else
|
||||
if (use_coroutine_) {
|
||||
ROCKSDB_GTEST_BYPASS(
|
||||
"This test targets synchronous MultiGet with async IO");
|
||||
return;
|
||||
}
|
||||
if (!CheckFSFeatureSupport(db_->GetFileSystem(), FSSupportedOps::kAsyncIO)) {
|
||||
ROCKSDB_GTEST_SKIP("io_uring is unavailable on the test thread");
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
std::atomic<uint64_t> read_thread_id{0};
|
||||
int read_async_count = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"PosixFileSystem::SupportedOps:CreateIOUring", [&](void* arg) {
|
||||
if (read_thread_id.load(std::memory_order_acquire) !=
|
||||
Env::Default()->GetThreadID()) {
|
||||
return;
|
||||
}
|
||||
auto** iu = static_cast<struct io_uring**>(arg);
|
||||
if (*iu != nullptr) {
|
||||
DeleteIOUring(*iu);
|
||||
*iu = nullptr;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::ReadAsync:InjectStatus",
|
||||
[&](void*) { ++read_async_count; });
|
||||
Defer cleanup([]() {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> values;
|
||||
std::thread read_thread([&]() {
|
||||
read_thread_id.store(Env::Default()->GetThreadID(),
|
||||
std::memory_order_release);
|
||||
values =
|
||||
MultiGet({Key(33), Key(54), Key(102)}, /*snapshot=*/nullptr,
|
||||
/*async=*/true, optimize_multiget_for_io_, use_coroutine_);
|
||||
});
|
||||
read_thread.join();
|
||||
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(values[0], "val_l1_" + std::to_string(33));
|
||||
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
ASSERT_EQ(read_async_count, 0);
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
}
|
||||
|
||||
// SupportedOps is advisory, so ReadAsync can still report NotSupported when a
|
||||
// request is submitted. MultiGet must then read synchronously rather than
|
||||
// returning that error to the caller.
|
||||
TEST_P(DBMultiGetAsyncIOTest, ReadAsyncNotSupportedFallsBackToSync) {
|
||||
const bool previous_enable_io_uring = enable_io_uring;
|
||||
Defer cleanup([previous_enable_io_uring]() {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
enable_io_uring = previous_enable_io_uring;
|
||||
});
|
||||
|
||||
enable_io_uring = true;
|
||||
PrepareDBForTest();
|
||||
|
||||
int sync_fallback_count = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::ReadAsync:InjectStatus", [](void* arg) {
|
||||
*static_cast<IOStatus*>(arg) =
|
||||
IOStatus::NotSupported("ReadAsync: failed to init io_uring");
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"AsyncFileReader::MultiReadAsyncImpl:SyncFallback",
|
||||
[&](void*) { ++sync_fallback_count; });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"AsyncFileReader::Wait:InjectPollStatus", [](void* arg) {
|
||||
*static_cast<IOStatus*>(arg) = IOStatus::IOError("injected Poll error");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> key_strs{Key(33), Key(54), Key(102)};
|
||||
std::vector<std::string> values =
|
||||
MultiGet(key_strs, /*snapshot=*/nullptr, /*async=*/true,
|
||||
optimize_multiget_for_io_, use_coroutine_);
|
||||
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(values[0], "val_l1_" + std::to_string(33));
|
||||
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
// Both synchronous async IO paths use AsyncFileReader when reads do not go
|
||||
// through the coroutine read executor.
|
||||
if (!UseCoroutineRead()) {
|
||||
ASSERT_GT(sync_fallback_count, 0);
|
||||
}
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
TEST_P(DBMultiGetAsyncIOTest, MixedReadAsyncFallbackPropagatesPollError) {
|
||||
if (UseCoroutineRead()) {
|
||||
ROCKSDB_GTEST_BYPASS(
|
||||
"This test targets the synchronous MultiGet AsyncFileReader path");
|
||||
return;
|
||||
}
|
||||
if (!CheckFSFeatureSupport(db_->GetFileSystem(), FSSupportedOps::kAsyncIO)) {
|
||||
ROCKSDB_GTEST_SKIP("io_uring is unavailable on the test thread");
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
std::atomic<int> read_async_count{0};
|
||||
std::atomic<int> sync_fallback_count{0};
|
||||
Defer cleanup([]() {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::ReadAsync:InjectStatus", [&](void* arg) {
|
||||
if (read_async_count.fetch_add(1) % 2 == 0) {
|
||||
*static_cast<IOStatus*>(arg) =
|
||||
IOStatus::NotSupported("injected ReadAsync fallback");
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"AsyncFileReader::MultiReadAsyncImpl:SyncFallback",
|
||||
[&](void*) { ++sync_fallback_count; });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"AsyncFileReader::Wait:InjectPollStatus", [](void* arg) {
|
||||
*static_cast<IOStatus*>(arg) = IOStatus::IOError("injected Poll error");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> key_strs{Key(33), Key(54), Key(102)};
|
||||
std::vector<PinnableSlice> values;
|
||||
std::vector<Status> statuses;
|
||||
MultiGet(key_strs, /*snapshot=*/nullptr, /*async=*/true,
|
||||
optimize_multiget_for_io_, use_coroutine_, values, statuses);
|
||||
|
||||
ASSERT_EQ(statuses.size(), 3);
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], "val_l1_" + std::to_string(33));
|
||||
ASSERT_TRUE(statuses[1].IsIOError());
|
||||
ASSERT_OK(statuses[2]);
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
ASSERT_EQ(read_async_count, 3);
|
||||
ASSERT_EQ(sync_fallback_count, 2);
|
||||
}
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBMultiGetAsyncIOTest, DBMultiGetAsyncIOTest,
|
||||
testing::Combine(testing::Bool(), testing::Bool()));
|
||||
#endif // USE_COROUTINES
|
||||
@@ -5937,9 +5767,6 @@ INSTANTIATE_TEST_CASE_P(BestEffortsRecoverIncompleteVersionTest,
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.write_buffer_manager = std::make_shared<WriteBufferManager>(
|
||||
64 << 20, nullptr /* cache */, false /* allow_stall */,
|
||||
WriteBufferFlushPolicy::kFlushLargestAcrossDBs);
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -5961,9 +5788,6 @@ TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
options.write_buffer_manager->TEST_RefreshFlushInitiatorCandidate();
|
||||
ASSERT_TRUE(
|
||||
options.write_buffer_manager->TEST_ScheduleFlushOnLargestDB(nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, RecoverWithNoCurrentFile) {
|
||||
@@ -6545,9 +6369,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGet) {
|
||||
ASSERT_TRUE(CheckUncompressableValue(key_uncmp[i], values[i].ToString()));
|
||||
}
|
||||
if (compression_enabled() && !has_compressed_cache()) {
|
||||
expected_reads += 3;
|
||||
expected_reads += (read_from_cache ? 3 : 3);
|
||||
} else {
|
||||
expected_reads += 4;
|
||||
expected_reads += (read_from_cache ? 4 : 4);
|
||||
}
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), expected_reads);
|
||||
|
||||
@@ -6614,7 +6438,7 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetDirectIO) {
|
||||
public:
|
||||
FakeDirectIOSequentialFile(std::unique_ptr<SequentialFile>&& file)
|
||||
: SequentialFileWrapper(file.get()), file_(std::move(file)) {}
|
||||
~FakeDirectIOSequentialFile() override {}
|
||||
~FakeDirectIOSequentialFile() {}
|
||||
|
||||
bool use_direct_io() const override { return true; }
|
||||
size_t GetRequiredBufferAlignment() const override { return 1; }
|
||||
@@ -6627,7 +6451,7 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetDirectIO) {
|
||||
public:
|
||||
FakeDirectIORandomAccessFile(std::unique_ptr<RandomAccessFile>&& file)
|
||||
: RandomAccessFileWrapper(file.get()), file_(std::move(file)) {}
|
||||
~FakeDirectIORandomAccessFile() override {}
|
||||
~FakeDirectIORandomAccessFile() {}
|
||||
|
||||
bool use_direct_io() const override { return true; }
|
||||
size_t GetRequiredBufferAlignment() const override { return 1; }
|
||||
|
||||
@@ -1921,10 +1921,7 @@ TEST_F(CacheKeyTest, DBImplSessionIdStructure) {
|
||||
// counter."
|
||||
std::string session_id1 = DBImpl::GenerateDbSessionId(/*env*/ nullptr);
|
||||
std::string session_id2 = DBImpl::GenerateDbSessionId(/*env*/ nullptr);
|
||||
uint64_t upper1;
|
||||
uint64_t upper2;
|
||||
uint64_t lower1;
|
||||
uint64_t lower2;
|
||||
uint64_t upper1, upper2, lower1, lower2;
|
||||
ASSERT_OK(DecodeSessionId(session_id1, &upper1, &lower1));
|
||||
ASSERT_OK(DecodeSessionId(session_id2, &upper2, &lower2));
|
||||
// Because generated in same process
|
||||
@@ -1984,8 +1981,7 @@ struct CacheKeyDecoder {
|
||||
}
|
||||
|
||||
void Decode(const CacheKey& key) {
|
||||
uint64_t file_num_etc64;
|
||||
uint64_t offset_etc64;
|
||||
uint64_t file_num_etc64, offset_etc64;
|
||||
DeconstructNonemptyCacheKey(key, &file_num_etc64, &offset_etc64);
|
||||
|
||||
// First decode session counter
|
||||
|
||||
@@ -917,15 +917,14 @@ INSTANTIATE_TEST_CASE_P(
|
||||
::testing::Values(
|
||||
std::make_tuple(kAutoBloom,
|
||||
FilterPartitioning::kCoupledPartitionedFilter,
|
||||
BlockBasedTableOptions::kLatestSupportedFormatVersion),
|
||||
kLatestBbtFormatVersion),
|
||||
std::make_tuple(kAutoBloom,
|
||||
FilterPartitioning::kDecoupledPartitionedFilter,
|
||||
BlockBasedTableOptions::kLatestSupportedFormatVersion),
|
||||
kLatestBbtFormatVersion),
|
||||
std::make_tuple(kAutoBloom, FilterPartitioning::kUnpartitionedFilter,
|
||||
BlockBasedTableOptions::kLatestSupportedFormatVersion),
|
||||
std::make_tuple(
|
||||
kAutoRibbon, FilterPartitioning::kUnpartitionedFilter,
|
||||
BlockBasedTableOptions::kLatestSupportedFormatVersion)));
|
||||
kLatestBbtFormatVersion),
|
||||
std::make_tuple(kAutoRibbon, FilterPartitioning::kUnpartitionedFilter,
|
||||
kLatestBbtFormatVersion)));
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
|
||||
+2
-85
@@ -3,11 +3,8 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -17,62 +14,6 @@ class DBClipTest : public DBTestBase {
|
||||
DBClipTest() : DBTestBase("db_clip_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
// ClipColumnFamily reads the CF's boundary keys under the DB mutex, then drops
|
||||
// it and issues several writes with those keys. Those keys must not point into
|
||||
// Version-owned FileMetaData, which a background job can replace and free in
|
||||
// that window. Here a full compaction runs inside the window and obsoletes the
|
||||
// Version the keys were read from.
|
||||
TEST_F(DBClipTest, BoundaryKeysSurviveConcurrentVersionChange) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 3;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::map<int32_t, std::string> values;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 100; j++) {
|
||||
int k = i * 100 + j;
|
||||
values[k] = rnd.RandomString(1024);
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
std::atomic<bool> fired{false};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::ClipColumnFamily:PostBoundaryKeys", [&](void*) {
|
||||
if (fired.exchange(true)) {
|
||||
return;
|
||||
}
|
||||
// Replace and free the Version the boundary keys were read from, on
|
||||
// another thread so this does not re-enter Clip's own call stack.
|
||||
port::Thread compactor([&] {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForPurge());
|
||||
});
|
||||
compactor.join();
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
const auto begin_key = Key(251);
|
||||
const auto end_key = Key(751);
|
||||
ASSERT_OK(
|
||||
db_->ClipColumnFamily(db_->DefaultColumnFamily(), begin_key, end_key));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ASSERT_TRUE(fired.load());
|
||||
|
||||
for (int i = 251; i < 751; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
for (int i : {0, 250, 751, 999}) {
|
||||
std::string result;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), Key(i), &result).IsNotFound());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBClipTest, TestClipRange) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
@@ -98,8 +39,7 @@ TEST_F(DBClipTest, TestClipRange) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("10", FilesPerLevel(0));
|
||||
auto begin_key = Key(251);
|
||||
auto end_key = Key(751);
|
||||
auto begin_key = Key(251), end_key = Key(751);
|
||||
ASSERT_OK(
|
||||
db_->ClipColumnFamily(db_->DefaultColumnFamily(), begin_key, end_key));
|
||||
|
||||
@@ -159,8 +99,7 @@ TEST_F(DBClipTest, TestClipRange) {
|
||||
}
|
||||
ASSERT_EQ("5,5,3", FilesPerLevel(0));
|
||||
|
||||
auto begin_key_2 = Key(222);
|
||||
auto end_key_2 = Key(888);
|
||||
auto begin_key_2 = Key(222), end_key_2 = Key(888);
|
||||
|
||||
ASSERT_OK(db_->ClipColumnFamily(db_->DefaultColumnFamily(), begin_key_2,
|
||||
end_key_2));
|
||||
@@ -194,28 +133,6 @@ TEST_F(DBClipTest, TestClipRange) {
|
||||
ASSERT_TRUE(in_range);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBClipTest, KeepFileEndingAtBeginKey) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("a", "va"));
|
||||
ASSERT_OK(Put("b", "vb"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_OK(Put("c", "vc"));
|
||||
ASSERT_OK(Put("d", "vd"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ("0,2", FilesPerLevel(0));
|
||||
|
||||
ASSERT_OK(db_->ClipColumnFamily(db_->DefaultColumnFamily(), "b", "d"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("a"));
|
||||
ASSERT_EQ("vb", Get("b"));
|
||||
ASSERT_EQ("vc", Get("c"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("d"));
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -601,10 +601,7 @@ TEST_P(DBTestCompactionFilterWithCompactParam,
|
||||
}
|
||||
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterWithMergeOperator) {
|
||||
std::string one;
|
||||
std::string two;
|
||||
std::string three;
|
||||
std::string four;
|
||||
std::string one, two, three, four;
|
||||
PutFixed64(&one, 1);
|
||||
PutFixed64(&two, 2);
|
||||
PutFixed64(&three, 3);
|
||||
|
||||
+31
-319
@@ -7,9 +7,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
@@ -29,8 +26,6 @@
|
||||
#include "rocksdb/file_checksum.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "test_util/mock_time_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testutil.h"
|
||||
@@ -178,264 +173,6 @@ class DBCompactionTest : public DBTestBase {
|
||||
}
|
||||
};
|
||||
|
||||
struct FileCuttingTableFactoryState {
|
||||
std::atomic<int> cut_requests{0};
|
||||
int max_cut_requests = 1;
|
||||
bool allow_empty_builder_cut = false;
|
||||
};
|
||||
|
||||
class FileCuttingTableBuilder : public TableBuilder {
|
||||
public:
|
||||
FileCuttingTableBuilder(std::unique_ptr<TableBuilder> base_builder,
|
||||
std::string cut_before_user_key,
|
||||
std::shared_ptr<FileCuttingTableFactoryState> state)
|
||||
: base_builder_(std::move(base_builder)),
|
||||
cut_before_user_key_(std::move(cut_before_user_key)),
|
||||
state_(std::move(state)) {}
|
||||
|
||||
void Add(const Slice& key, const Slice& value) override {
|
||||
base_builder_->Add(key, value);
|
||||
}
|
||||
|
||||
TableBuilderAddResult TryAdd(const Slice& key, const Slice& value,
|
||||
TableBuilderAddContext* context) override {
|
||||
const Slice user_key = ExtractUserKey(key);
|
||||
const bool cut_request_limit_not_reached =
|
||||
state_->cut_requests.load() < state_->max_cut_requests;
|
||||
const bool should_cut =
|
||||
cut_request_limit_not_reached &&
|
||||
(state_->allow_empty_builder_cut || !base_builder_->IsEmpty()) &&
|
||||
user_key.compare(Slice(cut_before_user_key_)) == 0;
|
||||
if (should_cut) {
|
||||
state_->cut_requests++;
|
||||
context->new_table_reason = TableBuilderNewTableReason::kBuilderPolicy;
|
||||
return TableBuilderAddResult::kRequiresNewTable;
|
||||
}
|
||||
return base_builder_->TryAdd(key, value, context);
|
||||
}
|
||||
|
||||
Status status() const override { return base_builder_->status(); }
|
||||
|
||||
IOStatus io_status() const override { return base_builder_->io_status(); }
|
||||
|
||||
Status Finish() override { return base_builder_->Finish(); }
|
||||
|
||||
void Abandon() override { base_builder_->Abandon(); }
|
||||
|
||||
uint64_t NumEntries() const override { return base_builder_->NumEntries(); }
|
||||
|
||||
bool IsEmpty() const override { return base_builder_->IsEmpty(); }
|
||||
|
||||
uint64_t PreCompressionSize() const override {
|
||||
return base_builder_->PreCompressionSize();
|
||||
}
|
||||
|
||||
uint64_t FileSize() const override { return base_builder_->FileSize(); }
|
||||
|
||||
uint64_t EstimatedFileSize() const override {
|
||||
return base_builder_->EstimatedFileSize();
|
||||
}
|
||||
|
||||
uint64_t EstimatedTailSize() const override {
|
||||
return base_builder_->EstimatedTailSize();
|
||||
}
|
||||
|
||||
uint64_t GetTailSize() const override { return base_builder_->GetTailSize(); }
|
||||
|
||||
bool NeedCompact() const override { return base_builder_->NeedCompact(); }
|
||||
|
||||
TableProperties GetTableProperties() const override {
|
||||
return base_builder_->GetTableProperties();
|
||||
}
|
||||
|
||||
std::string GetFileChecksum() const override {
|
||||
return base_builder_->GetFileChecksum();
|
||||
}
|
||||
|
||||
const char* GetFileChecksumFuncName() const override {
|
||||
return base_builder_->GetFileChecksumFuncName();
|
||||
}
|
||||
|
||||
void SetSeqnoTimeTableProperties(const SeqnoToTimeMapping& relevant_mapping,
|
||||
uint64_t oldest_ancestor_time) override {
|
||||
base_builder_->SetSeqnoTimeTableProperties(relevant_mapping,
|
||||
oldest_ancestor_time);
|
||||
}
|
||||
|
||||
uint64_t GetWorkerCPUMicros() const override {
|
||||
return base_builder_->GetWorkerCPUMicros();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<TableBuilder> base_builder_;
|
||||
std::string cut_before_user_key_;
|
||||
std::shared_ptr<FileCuttingTableFactoryState> state_;
|
||||
};
|
||||
|
||||
class FileCuttingTableFactory : public TableFactory {
|
||||
public:
|
||||
FileCuttingTableFactory(std::string cut_before_user_key,
|
||||
std::shared_ptr<FileCuttingTableFactoryState> state)
|
||||
: cut_before_user_key_(std::move(cut_before_user_key)),
|
||||
state_(std::move(state)) {}
|
||||
|
||||
const char* Name() const override { return "FileCuttingTableFactory"; }
|
||||
|
||||
using TableFactory::NewTableReader;
|
||||
|
||||
Status NewTableReader(
|
||||
const ReadOptions& read_options,
|
||||
const TableReaderOptions& table_reader_options,
|
||||
std::unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
std::unique_ptr<TableReader>* table_reader,
|
||||
bool prefetch_index_and_filter_in_cache) const override {
|
||||
return base_factory_.NewTableReader(
|
||||
read_options, table_reader_options, std::move(file), file_size,
|
||||
table_reader, prefetch_index_and_filter_in_cache);
|
||||
}
|
||||
|
||||
TableBuilder* NewTableBuilder(
|
||||
const TableBuilderOptions& table_builder_options,
|
||||
WritableFileWriter* file) const override {
|
||||
return new FileCuttingTableBuilder(
|
||||
std::unique_ptr<TableBuilder>(
|
||||
base_factory_.NewTableBuilder(table_builder_options, file)),
|
||||
cut_before_user_key_, state_);
|
||||
}
|
||||
|
||||
std::unique_ptr<TableFactory> Clone() const override {
|
||||
return std::make_unique<FileCuttingTableFactory>(cut_before_user_key_,
|
||||
state_);
|
||||
}
|
||||
|
||||
bool IsDeleteRangeSupported() const override {
|
||||
return base_factory_.IsDeleteRangeSupported();
|
||||
}
|
||||
|
||||
std::string GetPrintableOptions() const override {
|
||||
return base_factory_.GetPrintableOptions();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string cut_before_user_key_;
|
||||
std::shared_ptr<FileCuttingTableFactoryState> state_;
|
||||
BlockBasedTableFactory base_factory_;
|
||||
};
|
||||
|
||||
TEST_F(DBCompactionTest, TableBuilderCanRequestNewTableDuringCompaction) {
|
||||
auto factory_state = std::make_shared<FileCuttingTableFactoryState>();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_file_num_compaction_trigger = 10;
|
||||
options.target_file_size_base = 1 << 30;
|
||||
options.table_factory =
|
||||
std::make_shared<FileCuttingTableFactory>(Key(5), factory_state);
|
||||
Reopen(options);
|
||||
|
||||
for (int key : {0, 2, 4, 6, 8}) {
|
||||
ASSERT_OK(Put(Key(key), "value" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int key : {1, 3, 5, 7, 9}) {
|
||||
ASSERT_OK(Put(Key(key), "value" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_EQ(1, factory_state->cut_requests.load());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(1));
|
||||
for (int key = 0; key < 10; ++key) {
|
||||
ASSERT_EQ("value" + std::to_string(key), Get(Key(key)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, TableBuilderCutRequestForLevelZeroFailsCompaction) {
|
||||
auto factory_state = std::make_shared<FileCuttingTableFactoryState>();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 1;
|
||||
options.table_factory =
|
||||
std::make_shared<FileCuttingTableFactory>(Key(5), factory_state);
|
||||
Reopen(options);
|
||||
|
||||
for (int key : {0, 2, 4, 6, 8}) {
|
||||
ASSERT_OK(Put(Key(key), "value" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int key : {1, 3, 5, 7, 9}) {
|
||||
ASSERT_OK(Put(Key(key), "value" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Status status = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(status.IsCorruption()) << status.ToString();
|
||||
ASSERT_NE(std::string::npos, status.ToString().find("level 0 output"))
|
||||
<< status.ToString();
|
||||
ASSERT_EQ(1, factory_state->cut_requests.load());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, TableBuilderCutOnEmptyBuilderFailsCompaction) {
|
||||
auto factory_state = std::make_shared<FileCuttingTableFactoryState>();
|
||||
factory_state->allow_empty_builder_cut = true;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.allow_data_in_errors = false;
|
||||
options.level0_file_num_compaction_trigger = 10;
|
||||
options.target_file_size_base = 1 << 30;
|
||||
options.table_factory =
|
||||
std::make_shared<FileCuttingTableFactory>(Key(0), factory_state);
|
||||
Reopen(options);
|
||||
|
||||
for (int key : {0, 2, 4, 6, 8}) {
|
||||
ASSERT_OK(Put(Key(key), "value" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int key : {1, 3, 5, 7, 9}) {
|
||||
ASSERT_OK(Put(Key(key), "value" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Status status = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(status.IsCorruption()) << status.ToString();
|
||||
ASSERT_NE(std::string::npos, status.ToString().find("key: hidden"))
|
||||
<< status.ToString();
|
||||
ASSERT_EQ(1, factory_state->cut_requests.load());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, TableBuilderRepeatedCutRequestFailsCompaction) {
|
||||
auto factory_state = std::make_shared<FileCuttingTableFactoryState>();
|
||||
factory_state->max_cut_requests = 2;
|
||||
factory_state->allow_empty_builder_cut = true;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_file_num_compaction_trigger = 10;
|
||||
options.target_file_size_base = 1 << 30;
|
||||
options.table_factory =
|
||||
std::make_shared<FileCuttingTableFactory>(Key(5), factory_state);
|
||||
Reopen(options);
|
||||
|
||||
for (int key : {0, 2, 4, 6, 8}) {
|
||||
ASSERT_OK(Put(Key(key), "value" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int key : {1, 3, 5, 7, 9}) {
|
||||
ASSERT_OK(Put(Key(key), "value" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Status status = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(status.IsCorruption()) << status.ToString();
|
||||
ASSERT_EQ(2, factory_state->cut_requests.load());
|
||||
}
|
||||
|
||||
class DBCompactionTestWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<uint32_t, bool>> {
|
||||
@@ -2721,12 +2458,9 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
|
||||
// Delete files in range [0, 299] (inclusive)
|
||||
{
|
||||
auto begin_str1 = Key(0);
|
||||
auto end_str1 = Key(100);
|
||||
auto begin_str2 = Key(100);
|
||||
auto end_str2 = Key(200);
|
||||
auto begin_str3 = Key(200);
|
||||
auto end_str3 = Key(299);
|
||||
auto begin_str1 = Key(0), end_str1 = Key(100);
|
||||
auto begin_str2 = Key(100), end_str2 = Key(200);
|
||||
auto begin_str3 = Key(200), end_str3 = Key(299);
|
||||
std::vector<RangeOpt> ranges;
|
||||
ranges.emplace_back(begin_str1, end_str1);
|
||||
ranges.emplace_back(begin_str2, end_str2);
|
||||
@@ -2748,18 +2482,12 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
|
||||
// Delete files in range [600, 999) (exclusive)
|
||||
{
|
||||
auto begin_str1 = Key(600);
|
||||
auto end_str1 = Key(800);
|
||||
auto begin_str2 = Key(700);
|
||||
auto end_str2 = Key(900);
|
||||
auto begin_str3 = Key(800);
|
||||
auto end_str3 = Key(999);
|
||||
Slice begin1(begin_str1);
|
||||
Slice end1(end_str1);
|
||||
Slice begin2(begin_str2);
|
||||
Slice end2(end_str2);
|
||||
Slice begin3(begin_str3);
|
||||
Slice end3(end_str3);
|
||||
auto begin_str1 = Key(600), end_str1 = Key(800);
|
||||
auto begin_str2 = Key(700), end_str2 = Key(900);
|
||||
auto begin_str3 = Key(800), end_str3 = Key(999);
|
||||
Slice begin1(begin_str1), end1(end_str1);
|
||||
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);
|
||||
@@ -2846,10 +2574,8 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
|
||||
// Verify `DeleteFilesInRange` can't drop only file 0 which would cause
|
||||
// "1 -> vals[0]" to reappear.
|
||||
std::string begin_str = Key(0);
|
||||
std::string end_str = Key(1);
|
||||
Slice begin = begin_str;
|
||||
Slice end = end_str;
|
||||
std::string begin_str = Key(0), end_str = Key(1);
|
||||
Slice begin = begin_str, end = end_str;
|
||||
ASSERT_OK(
|
||||
DeleteFilesInRange(db_.get(), db_->DefaultColumnFamily(), &begin, &end));
|
||||
ASSERT_EQ(vals[1], GetValue(Key(1)));
|
||||
@@ -3157,8 +2883,7 @@ TEST_P(DBCompactionTestWithParam, LevelCompactionCFPathUse) {
|
||||
|
||||
std::vector<Options> option_vector;
|
||||
option_vector.emplace_back(options);
|
||||
ColumnFamilyOptions cf_opt1(options);
|
||||
ColumnFamilyOptions cf_opt2(options);
|
||||
ColumnFamilyOptions cf_opt1(options), cf_opt2(options);
|
||||
// Configure CF1 specific paths.
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1", 500 * 1024);
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_2", 4 * 1024 * 1024);
|
||||
@@ -3934,10 +3659,7 @@ TEST_P(DBCompactionTestWithParam, CompressLevelCompaction) {
|
||||
// move to level 3 will not be allowed
|
||||
options.compression_per_level = {kNoCompression, kNoCompression,
|
||||
kZlibCompression};
|
||||
int matches = 0;
|
||||
int didnt_match = 0;
|
||||
int trivial_move = 0;
|
||||
int non_trivial = 0;
|
||||
int matches = 0, didnt_match = 0, trivial_move = 0, non_trivial = 0;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"Compaction::InputCompressionMatchesOutput:Matches",
|
||||
@@ -5013,8 +4735,7 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(kNumLevelFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
std::vector<LiveFileMetaData> pre_release_metadata;
|
||||
std::vector<LiveFileMetaData> post_release_metadata;
|
||||
std::vector<LiveFileMetaData> pre_release_metadata, post_release_metadata;
|
||||
db_->GetLiveFilesMetaData(&pre_release_metadata);
|
||||
// just need to bump seqnum so ReleaseSnapshot knows the newest key in the SST
|
||||
// files does not need to be preserved in case of a future snapshot.
|
||||
@@ -5161,8 +4882,7 @@ TEST_F(DBCompactionTest, NoCompactBottomLevelFilesWithDeletions) {
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr));
|
||||
ASSERT_EQ(kNumLevelFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
std::vector<LiveFileMetaData> pre_release_metadata;
|
||||
std::vector<LiveFileMetaData> post_release_metadata;
|
||||
std::vector<LiveFileMetaData> pre_release_metadata, post_release_metadata;
|
||||
db_->GetLiveFilesMetaData(&pre_release_metadata);
|
||||
// just need to bump seqnum so ReleaseSnapshot knows the newest key in the SST
|
||||
// files does not need to be preserved in case of a future snapshot.
|
||||
@@ -5630,10 +5350,10 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
*max_open_files = 2;
|
||||
});
|
||||
// In the case where all files are opened and doing DB restart
|
||||
// forcing the oldest ancestor time in manifest file to be 0 to
|
||||
// forcing the oldest ancester time in manifest file to be 0 to
|
||||
// simulate the case of reading from an old version.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionEdit::EncodeTo:VarintOldestAncestorTime", [&](void* arg) {
|
||||
"VersionEdit::EncodeTo:VarintOldestAncesterTime", [&](void* arg) {
|
||||
if (if_restart && if_open_all_files) {
|
||||
std::string* encoded_field = static_cast<std::string*>(arg);
|
||||
*encoded_field = "";
|
||||
@@ -5670,7 +5390,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
|
||||
&level_to_files);
|
||||
uint64_t oldest_time = level_to_files[0][0].oldest_ancestor_time;
|
||||
uint64_t oldest_time = level_to_files[0][0].oldest_ancester_time;
|
||||
// Add 1 hour and do another flush.
|
||||
env_->MockSleepForSeconds(1 * 60 * 60);
|
||||
for (int i = 101; i <= 200; ++i) {
|
||||
@@ -5738,7 +5458,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
|
||||
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
|
||||
&level_to_files);
|
||||
ASSERT_EQ(oldest_time, level_to_files[6][0].oldest_ancestor_time);
|
||||
ASSERT_EQ(oldest_time, level_to_files[6][0].oldest_ancester_time);
|
||||
|
||||
env_->MockSleepForSeconds(25 * 60 * 60);
|
||||
ASSERT_OK(Put(Key(2), "1"));
|
||||
@@ -6161,7 +5881,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
props->file_creation_time = 0;
|
||||
}
|
||||
if (set_creation_time_to_zero) {
|
||||
props->oldest_ancestor_time = 0;
|
||||
props->creation_time = 0;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -6712,8 +6432,7 @@ TEST_F(DBCompactionTest, CompactRangeFlushOverlappingMemtable) {
|
||||
CompactRangeOptions compact_range_opts;
|
||||
ASSERT_OK(db_->CompactRange(compact_range_opts, begin_ptr, end_ptr));
|
||||
|
||||
uint64_t get_prop_tmp;
|
||||
uint64_t num_memtable_entries = 0;
|
||||
uint64_t get_prop_tmp, num_memtable_entries = 0;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesImmMemTables,
|
||||
&get_prop_tmp));
|
||||
num_memtable_entries += get_prop_tmp;
|
||||
@@ -8482,8 +8201,7 @@ TEST_F(DBCompactionTest, ManualCompactionBottomLevelOptimized) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ManualCompactionMax) {
|
||||
uint64_t l1_avg_size = 0;
|
||||
uint64_t l2_avg_size = 0;
|
||||
uint64_t l1_avg_size = 0, l2_avg_size = 0;
|
||||
auto generate_sst_func = [&]() {
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 100; i++) {
|
||||
@@ -9671,8 +9389,7 @@ TEST_P(DBCompactionTestL0FilesMisorderCorruptionWithParam,
|
||||
// `start` and `end` is carefully chosen so that compact range:
|
||||
// (1) doesn't overlap with memtable therefore the memtable won't be flushed
|
||||
// (2) should target at compacting s0 with s1 and s2
|
||||
Slice start("k3");
|
||||
Slice end("k5");
|
||||
Slice start("k3"), end("k5");
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
// After compaction, we have LSM tree:
|
||||
//
|
||||
@@ -11187,12 +10904,8 @@ TEST_F(DBCompactionTest, FIFOMultiTierTemperatureAging) {
|
||||
env_->SetMockSleep();
|
||||
|
||||
// Track all temperature file creations
|
||||
int total_hot = 0;
|
||||
int total_warm = 0;
|
||||
int total_cool = 0;
|
||||
int total_cold = 0;
|
||||
int total_ice = 0;
|
||||
int total_unknown = 0;
|
||||
int total_hot = 0, total_warm = 0, total_cool = 0, total_cold = 0,
|
||||
total_ice = 0, total_unknown = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
@@ -12028,8 +11741,7 @@ TEST_F(DBCompactionTest, DrainUnnecessaryLevelsAfterMultiplierChanged) {
|
||||
// necessary, unnecessary levels should to be drained.
|
||||
const int kBaseLevelBytes = 256 << 10; // 256KB
|
||||
const int kFileBytes = 64 << 10; // 64KB
|
||||
const int kInitMultiplier = 2;
|
||||
const int kChangedMultiplier = 10;
|
||||
const int kInitMultiplier = 2, kChangedMultiplier = 10;
|
||||
const int kNumFiles = 32;
|
||||
const int kNumLevels = 5;
|
||||
const int kValueBytes = 1 << 10; // 1KB
|
||||
@@ -12705,7 +12417,7 @@ TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
|
||||
ASSERT_EQ(newest_key_time, file_metadatas[idx]
|
||||
->fd.pinned_reader.Get()
|
||||
->GetTableProperties()
|
||||
->oldest_ancestor_time);
|
||||
->creation_time);
|
||||
}
|
||||
// The delta between the first and last newest_key_times is 15s
|
||||
uint64_t last_newest_key_time = prev_newest_key_time;
|
||||
@@ -12724,14 +12436,14 @@ TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
|
||||
->GetTableProperties()
|
||||
->newest_key_time,
|
||||
first_newest_key_time);
|
||||
// Contrast newest_key_time with oldest_ancestor_time (15s older than
|
||||
// newest_key_time)
|
||||
// Contrast newest_key_time with creation_time, which records the oldest
|
||||
// ancestor time (15s older than newest_key_time)
|
||||
ASSERT_EQ(file_metadatas[0]
|
||||
->fd.pinned_reader.Get()
|
||||
->GetTableProperties()
|
||||
->oldest_ancestor_time,
|
||||
->creation_time,
|
||||
last_newest_key_time);
|
||||
ASSERT_EQ(file_metadatas[0]->oldest_ancestor_time, last_newest_key_time);
|
||||
ASSERT_EQ(file_metadatas[0]->oldest_ancester_time, last_newest_key_time);
|
||||
|
||||
// Make sure TTL of 5s causes compaction
|
||||
env_->MockSleepForSeconds(6);
|
||||
|
||||
+2
-223
@@ -1357,8 +1357,7 @@ TEST_F(DBTest2, DuplicateSnapshot) {
|
||||
options = CurrentOptions(options);
|
||||
std::vector<const Snapshot*> snapshots;
|
||||
DBImpl* dbi = dbfull();
|
||||
SequenceNumber oldest_ww_snap;
|
||||
SequenceNumber first_ww_snap;
|
||||
SequenceNumber oldest_ww_snap, first_ww_snap;
|
||||
|
||||
ASSERT_OK(Put("k", "v")); // inc seq
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
@@ -2381,12 +2380,10 @@ TEST_F(DBTest2, PausingManualCompaction1) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> files_before_compact;
|
||||
std::vector<std::string> files_after_compact;
|
||||
std::vector<std::string> files_before_compact, files_after_compact;
|
||||
// Remember file name before compaction is triggered
|
||||
std::vector<LiveFileMetaData> files_meta;
|
||||
dbfull()->GetLiveFilesMetaData(&files_meta);
|
||||
files_before_compact.reserve(files_meta.size());
|
||||
for (const auto& file : files_meta) {
|
||||
files_before_compact.push_back(file.name);
|
||||
}
|
||||
@@ -2402,7 +2399,6 @@ TEST_F(DBTest2, PausingManualCompaction1) {
|
||||
// Get file names after compaction is stopped
|
||||
files_meta.clear();
|
||||
dbfull()->GetLiveFilesMetaData(&files_meta);
|
||||
files_after_compact.reserve(files_meta.size());
|
||||
for (const auto& file : files_meta) {
|
||||
files_after_compact.push_back(file.name);
|
||||
}
|
||||
@@ -3517,40 +3513,6 @@ TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, LiveFilesStorageInfoWithConcurrentSetOptions) {
|
||||
// SetOptions() writes a new OPTIONS file and deletes all but the newest
|
||||
// two. Without file deletions disabled by the caller, the OPTIONS file
|
||||
// chosen by GetLiveFilesStorageInfo must still survive until its size is
|
||||
// read.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLiveFilesStorageInfo:BeforeOptionsFileSize",
|
||||
[&](void* /*arg*/) {
|
||||
ASSERT_OK(db_->SetOptions({{"disable_auto_compactions", "true"}}));
|
||||
ASSERT_OK(db_->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<LiveFileStorageInfo> infos;
|
||||
ASSERT_OK(
|
||||
db_->GetLiveFilesStorageInfo(LiveFilesStorageInfoOptions(), &infos));
|
||||
ASSERT_TRUE(dbfull()->IsFileDeletionsEnabled());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, LiveFilesStorageInfoReadOnlyKeepsDeletionsEnabled) {
|
||||
ASSERT_OK(Put("key", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(ReadOnlyReopen(CurrentOptions()));
|
||||
|
||||
LiveFilesStorageInfoOptions opts;
|
||||
opts.wal_size_for_flush = std::numeric_limits<uint64_t>::max();
|
||||
std::vector<LiveFileStorageInfo> infos;
|
||||
ASSERT_OK(db_->GetLiveFilesStorageInfo(opts, &infos));
|
||||
ASSERT_TRUE(dbfull()->IsFileDeletionsEnabled());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TestNumPread) {
|
||||
Options options = CurrentOptions();
|
||||
bool prefetch_supported =
|
||||
@@ -4309,147 +4271,6 @@ TEST_F(DBTest2, TraceWithLimit) {
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
namespace {
|
||||
Status ReplayTraceFile(Env* env, const std::string& trace_filename, DB* db,
|
||||
const std::vector<ColumnFamilyHandle*>& handles,
|
||||
uint32_t num_threads,
|
||||
TraceExecutionResultHandler* res_handler = nullptr) {
|
||||
std::unique_ptr<TraceReader> trace_reader;
|
||||
Status s =
|
||||
NewFileTraceReader(env, EnvOptions(), trace_filename, &trace_reader);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
std::unique_ptr<Replayer> replayer;
|
||||
s = db->NewDefaultReplayer(handles, std::move(trace_reader), &replayer);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
s = replayer->Prepare();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// Count the replayed records by type in `res_handler`, if given.
|
||||
std::function<void(Status, std::unique_ptr<TraceRecordResult>&&)> res_cb;
|
||||
if (res_handler != nullptr) {
|
||||
res_cb = [res_handler](const Status& exec_s,
|
||||
std::unique_ptr<TraceRecordResult>&& res) {
|
||||
exec_s.PermitUncheckedError();
|
||||
if (res != nullptr) {
|
||||
EXPECT_OK(res->Accept(res_handler));
|
||||
}
|
||||
};
|
||||
}
|
||||
return replayer->Replay(ReplayOptions(num_threads, 1.0), res_cb);
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBTest2, TraceReplayDefaultColumnFamilyWithoutHandles) {
|
||||
Options options = CurrentOptions();
|
||||
std::string trace_filename = dbname_ + "/rocksdb.trace";
|
||||
std::unique_ptr<TraceWriter> trace_writer;
|
||||
ASSERT_OK(
|
||||
NewFileTraceWriter(env_, EnvOptions(), trace_filename, &trace_writer));
|
||||
ASSERT_OK(db_->StartTrace(TraceOptions(), std::move(trace_writer)));
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
ASSERT_OK(Put("b", "2"));
|
||||
ASSERT_EQ("1", Get("a"));
|
||||
std::vector<std::string> values;
|
||||
for (const Status& s :
|
||||
db_->MultiGet(ReadOptions(), {Slice("a"), Slice("b")}, &values)) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->Seek("a");
|
||||
iter->SeekForPrev("b");
|
||||
ASSERT_OK(iter->status());
|
||||
iter.reset();
|
||||
ASSERT_OK(db_->EndTrace());
|
||||
|
||||
std::string dbname2 = test::PerThreadDBPath(env_, "/db_replay2");
|
||||
options.create_if_missing = true;
|
||||
for (uint32_t num_threads : {1U, 2U}) {
|
||||
SCOPED_TRACE("num_threads=" + std::to_string(num_threads));
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
std::unique_ptr<DB> db2;
|
||||
ASSERT_OK(DB::Open(options, dbname2, &db2));
|
||||
// No handles: every read record on the default column family must still
|
||||
// replay.
|
||||
TraceExecutionResultHandler res_handler;
|
||||
ASSERT_OK(ReplayTraceFile(env_, trace_filename, db2.get(),
|
||||
/*handles=*/{}, num_threads, &res_handler));
|
||||
// Each record replayed and produced a result.
|
||||
ASSERT_EQ(2, res_handler.GetNumWrites());
|
||||
ASSERT_EQ(1, res_handler.GetNumGets());
|
||||
ASSERT_EQ(1, res_handler.GetNumMultiGets());
|
||||
ASSERT_EQ(2, res_handler.GetNumIterSeeks());
|
||||
std::string value;
|
||||
ASSERT_OK(db2->Get(ReadOptions(), "a", &value));
|
||||
ASSERT_EQ("1", value);
|
||||
ASSERT_OK(db2->Get(ReadOptions(), "b", &value));
|
||||
ASSERT_EQ("2", value);
|
||||
}
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TraceReplayReturnsExecutionError) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::string trace_filename = dbname_ + "/rocksdb.trace";
|
||||
std::unique_ptr<TraceWriter> trace_writer;
|
||||
ASSERT_OK(
|
||||
NewFileTraceWriter(env_, EnvOptions(), trace_filename, &trace_writer));
|
||||
ASSERT_OK(db_->StartTrace(TraceOptions(), std::move(trace_writer)));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "a"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "b"));
|
||||
ASSERT_OK(db_->EndTrace());
|
||||
|
||||
// The replay DB has no "pikachu" column family, so both Get records fail.
|
||||
std::string dbname2 = test::PerThreadDBPath(env_, "/db_replay2");
|
||||
options.create_if_missing = true;
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
std::unique_ptr<DB> db2;
|
||||
ASSERT_OK(DB::Open(options, dbname2, &db2));
|
||||
for (uint32_t num_threads : {1U, 2U}) {
|
||||
SCOPED_TRACE("num_threads=" + std::to_string(num_threads));
|
||||
Status s = ReplayTraceFile(env_, trace_filename, db2.get(),
|
||||
{db2->DefaultColumnFamily()}, num_threads);
|
||||
ASSERT_TRUE(s.IsCorruption()) << s.ToString();
|
||||
}
|
||||
db2.reset();
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TraceReplayReturnsNotSupportedExecutionError) {
|
||||
Options options = CurrentOptions();
|
||||
std::string trace_filename = dbname_ + "/rocksdb.trace";
|
||||
std::unique_ptr<TraceWriter> trace_writer;
|
||||
ASSERT_OK(
|
||||
NewFileTraceWriter(env_, EnvOptions(), trace_filename, &trace_writer));
|
||||
ASSERT_OK(db_->StartTrace(TraceOptions(), std::move(trace_writer)));
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
ASSERT_OK(db_->EndTrace());
|
||||
|
||||
// DeleteRange is not compatible with row_cache, so replaying the DeleteRange
|
||||
// write fails with NotSupported. Unlike an unsupported trace type, this must
|
||||
// not be skipped.
|
||||
std::string dbname2 = test::PerThreadDBPath(env_, "/db_replay2");
|
||||
options.create_if_missing = true;
|
||||
options.row_cache = NewLRUCache(1 << 20);
|
||||
for (uint32_t num_threads : {1U, 2U}) {
|
||||
SCOPED_TRACE("num_threads=" + std::to_string(num_threads));
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
std::unique_ptr<DB> db2;
|
||||
ASSERT_OK(DB::Open(options, dbname2, &db2));
|
||||
Status s = ReplayTraceFile(env_, trace_filename, db2.get(),
|
||||
/*handles=*/{}, num_threads);
|
||||
ASSERT_TRUE(s.IsNotSupported()) << s.ToString();
|
||||
}
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TraceWithSampling) {
|
||||
Options options = CurrentOptions();
|
||||
ReadOptions ro;
|
||||
@@ -7830,48 +7651,6 @@ TEST_F(DBTest2, TableCacheMissDuringReadFromBlockCacheTier) {
|
||||
ASSERT_EQ(orig_num_file_opens, TestGetTickerCount(options, NO_FILE_OPENS));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, MultiGetBlockCacheTierSkipsColdSstData) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
// Use buffered reads to reach the non-mmap RetrieveMultipleBlocks path.
|
||||
options.allow_mmap_reads = false;
|
||||
BlockBasedTableOptions table_options;
|
||||
// Data blocks use the block cache. Index and filter blocks stay in the table
|
||||
// reader (cache_index_and_filter_blocks is false by default), so a
|
||||
// kBlockCacheTier read gets to the data block without metadata I/O.
|
||||
table_options.block_cache = NewLRUCache(1 << 20);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Open the table reader so that kBlockCacheTier does not fail at table
|
||||
// open. Do not add the data block to the block cache.
|
||||
ReadOptions warm_ro;
|
||||
warm_ro.fill_cache = false;
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(warm_ro, "k1", &value));
|
||||
ASSERT_EQ(value, "v1");
|
||||
// Make sure the data block is not in the block cache.
|
||||
table_options.block_cache->EraseUnRefEntries();
|
||||
|
||||
ReadOptions no_io_ro;
|
||||
no_io_ro.read_tier = kBlockCacheTier;
|
||||
|
||||
ASSERT_TRUE(db_->Get(no_io_ro, "k1", &value).IsIncomplete());
|
||||
ASSERT_TRUE(db_->Get(no_io_ro, "k2", &value).IsIncomplete());
|
||||
|
||||
Slice keys[2] = {Slice("k1"), Slice("k2")};
|
||||
PinnableSlice values[2];
|
||||
Status statuses[2];
|
||||
db_->MultiGet(no_io_ro, db_->DefaultColumnFamily(), 2, keys, values,
|
||||
statuses);
|
||||
ASSERT_TRUE(statuses[0].IsIncomplete());
|
||||
ASSERT_TRUE(statuses[1].IsIncomplete());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, GetFileChecksumsFromCurrentManifest_CRC32) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.create_if_missing = true;
|
||||
|
||||
+1
-2
@@ -61,8 +61,7 @@ TEST_F(DBEtc3Test, AutoTuneManifestSize) {
|
||||
// auto-tuning phases continue to test the unrelaxed background threshold
|
||||
// even though CF manipulation itself is foreground. Phase-specific foreground
|
||||
// checks disable that piggy-backed background write.
|
||||
uint64_t prev_manifest_num = 0;
|
||||
uint64_t cur_manifest_num = 0;
|
||||
uint64_t prev_manifest_num = 0, cur_manifest_num = 0;
|
||||
std::deque<ColumnFamilyHandle*> handles;
|
||||
int counter = 5;
|
||||
auto UpdateManifestNumsFrom = [&](uint64_t before_manifest_num) {
|
||||
|
||||
+7
-82
@@ -17,7 +17,6 @@
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/logging.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -199,8 +198,7 @@ Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_wal_file) {
|
||||
|
||||
Status DBImpl::AppendColumnFamilyDropsToManifest(
|
||||
const std::string& manifest_path, uint64_t manifest_size,
|
||||
const std::vector<uint32_t>& cf_ids,
|
||||
uint64_t committed_options_file_number) {
|
||||
const std::vector<uint32_t>& cf_ids) {
|
||||
const WriteOptions write_options;
|
||||
// Snapshot manifest_preallocation_size_ under the DB mutex per VersionSet's
|
||||
// contract (the field is mutated by SetDBOptions under this mutex).
|
||||
@@ -210,44 +208,21 @@ Status DBImpl::AppendColumnFamilyDropsToManifest(
|
||||
preallocation_size = versions_->manifest_preallocation_size_;
|
||||
}
|
||||
return versions_->AppendColumnFamilyDropsToManifest(
|
||||
manifest_path, manifest_size, cf_ids, write_options, preallocation_size,
|
||||
committed_options_file_number);
|
||||
}
|
||||
|
||||
Status DBImpl::CreateOptionsFileForSubsetCheckpoint(
|
||||
const std::string& checkpoint_dir,
|
||||
const SubsetCheckpointOptionsSnapshot& options_snapshot) {
|
||||
if (options_snapshot.options_file_number == 0) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// The snapshot's CF options are shallow copies that share live pointer
|
||||
// sub-options (e.g. RibbonFilterPolicy, whose bloom_before_level is mutated
|
||||
// in place by SetOptions). Hold options_mutex_, as WriteOptionsFile does, so
|
||||
// SetOptions cannot change them between writing the file and verifying it.
|
||||
InstrumentedMutexLock ol(&options_mutex_);
|
||||
|
||||
const std::string options_path =
|
||||
OptionsFileName(checkpoint_dir, options_snapshot.options_file_number);
|
||||
return PersistRocksDBOptions(
|
||||
WriteOptions(), options_snapshot.db_options, options_snapshot.cf_names,
|
||||
options_snapshot.cf_options, options_path, fs_.get());
|
||||
manifest_path, manifest_size, cf_ids, write_options, preallocation_size);
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFilesStorageInfo(
|
||||
const LiveFilesStorageInfoOptions& opts,
|
||||
std::vector<LiveFileStorageInfo>* files) {
|
||||
return GetLiveFilesStorageInfoImpl(opts, /*include_cf_ids=*/{}, files,
|
||||
/*excluded_cf_ids=*/nullptr,
|
||||
/*options_snapshot=*/nullptr);
|
||||
/*excluded_cf_ids=*/nullptr);
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFilesStorageInfoForSubsetCheckpoint(
|
||||
const LiveFilesStorageInfoOptions& opts,
|
||||
const std::vector<uint32_t>& include_cf_ids,
|
||||
std::vector<LiveFileStorageInfo>* files,
|
||||
std::vector<uint32_t>* excluded_cf_ids,
|
||||
SubsetCheckpointOptionsSnapshot* options_snapshot) {
|
||||
std::vector<uint32_t>* excluded_cf_ids) {
|
||||
// Internal contract: caller must coalesce ids and always include the default
|
||||
// CF (it cannot be dropped, so a subset checkpoint without it would produce
|
||||
// an unopenable DB). CheckpointImpl enforces this before calling here.
|
||||
@@ -255,17 +230,15 @@ Status DBImpl::GetLiveFilesStorageInfoForSubsetCheckpoint(
|
||||
assert(std::find(include_cf_ids.begin(), include_cf_ids.end(),
|
||||
default_cf_handle_->GetID()) != include_cf_ids.end());
|
||||
assert(excluded_cf_ids != nullptr);
|
||||
assert(options_snapshot != nullptr);
|
||||
return GetLiveFilesStorageInfoImpl(opts, include_cf_ids, files,
|
||||
excluded_cf_ids, options_snapshot);
|
||||
excluded_cf_ids);
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFilesStorageInfoImpl(
|
||||
const LiveFilesStorageInfoOptions& opts,
|
||||
const std::vector<uint32_t>& include_cf_ids,
|
||||
std::vector<LiveFileStorageInfo>* files,
|
||||
std::vector<uint32_t>* excluded_cf_ids,
|
||||
SubsetCheckpointOptionsSnapshot* options_snapshot) {
|
||||
std::vector<uint32_t>* excluded_cf_ids) {
|
||||
// To avoid returning partial results, only move results to files on success.
|
||||
assert(files);
|
||||
files->clear();
|
||||
@@ -273,7 +246,6 @@ Status DBImpl::GetLiveFilesStorageInfoImpl(
|
||||
|
||||
const bool cf_subset = !include_cf_ids.empty();
|
||||
assert(!cf_subset || excluded_cf_ids != nullptr);
|
||||
assert(!cf_subset || options_snapshot != nullptr);
|
||||
const std::unordered_set<uint32_t> include_cf_id_set(include_cf_ids.begin(),
|
||||
include_cf_ids.end());
|
||||
// Tracks which requested ids remain to be observed as live under the DB
|
||||
@@ -441,29 +413,6 @@ Status DBImpl::GetLiveFilesStorageInfoImpl(
|
||||
"Requested column family id was not live at checkpoint time");
|
||||
}
|
||||
|
||||
if (cf_subset) {
|
||||
assert(options_snapshot != nullptr);
|
||||
assert(excluded_cf_ids != nullptr);
|
||||
*options_snapshot = SubsetCheckpointOptionsSnapshot();
|
||||
if (!excluded_cf_ids->empty()) {
|
||||
options_snapshot->options_file_number = versions_->NewFileNumber();
|
||||
options_snapshot->commit_options_file_number =
|
||||
versions_->has_committed_options_file_number();
|
||||
options_snapshot->db_options =
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_);
|
||||
ColumnFamilySet* column_family_set = versions_->GetColumnFamilySet();
|
||||
assert(column_family_set != nullptr);
|
||||
for (auto* cfd : *column_family_set) {
|
||||
assert(cfd != nullptr);
|
||||
if (!cfd->IsDropped() &&
|
||||
include_cf_id_set.find(cfd->GetID()) != include_cf_id_set.end()) {
|
||||
options_snapshot->cf_names.push_back(cfd->GetName());
|
||||
options_snapshot->cf_options.push_back(cfd->GetLatestCFOptions());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Capture some final info before releasing mutex
|
||||
const uint64_t manifest_number = versions_->manifest_file_number();
|
||||
uint64_t manifest_size = versions_->manifest_file_size();
|
||||
@@ -475,37 +424,13 @@ Status DBImpl::GetLiveFilesStorageInfoImpl(
|
||||
}
|
||||
}
|
||||
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 = cur_wal_number_;
|
||||
|
||||
if (options_number != 0) {
|
||||
// A concurrent SetOptions() may otherwise delete this OPTIONS file before
|
||||
// its size is read below, even when the caller has not disabled deletions.
|
||||
DisableFileDeletionsWithLock().PermitUncheckedError();
|
||||
}
|
||||
mutex_.Unlock();
|
||||
|
||||
uint64_t options_size = 0;
|
||||
if (options_number != 0) {
|
||||
// File deletions must be disabled by callers while they copy this snapshot.
|
||||
// The number belongs to the MANIFEST/CF snapshot captured above, while the
|
||||
// immutable file's size can be read without holding the DB mutex.
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFilesStorageInfo:BeforeOptionsFileSize");
|
||||
Status options_s = GetEnv()->GetFileSize(
|
||||
OptionsFileName(GetName(), options_number), &options_size);
|
||||
{
|
||||
// Not EnableFileDeletions(): read-only DB implementations override it to
|
||||
// return NotSupported without undoing DisableFileDeletionsWithLock().
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
assert(disable_delete_obsolete_files_ > 0);
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
if (!options_s.ok()) {
|
||||
return options_s;
|
||||
}
|
||||
}
|
||||
|
||||
std::string manifest_fname = DescriptorFileName(manifest_number);
|
||||
{ // MANIFEST
|
||||
results.emplace_back();
|
||||
|
||||
+14
-80
@@ -157,8 +157,7 @@ TEST_F(DBFlushTest, FlushInLowPriThreadPool) {
|
||||
env_->SetBackgroundThreads(0, Env::HIGH);
|
||||
|
||||
std::thread::id tid;
|
||||
int num_flushes = 0;
|
||||
int num_compactions = 0;
|
||||
int num_flushes = 0, num_compactions = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkFlush", [&](void* /*arg*/) {
|
||||
if (tid == std::thread::id()) {
|
||||
@@ -1018,9 +1017,7 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
std::string KEY7 = "IamKey7";
|
||||
std::string KEY8 = "IamKey8";
|
||||
std::string KEY9 = "IamKey9";
|
||||
std::string RNDKEY1;
|
||||
std::string RNDKEY2;
|
||||
std::string RNDKEY3;
|
||||
std::string RNDKEY1, RNDKEY2, RNDKEY3;
|
||||
const std::string NOT_FOUND = "NOT_FOUND";
|
||||
|
||||
// Heavy overwrite workload,
|
||||
@@ -1029,18 +1026,8 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
const size_t NUM_REPEAT = 100;
|
||||
const size_t RAND_KEYS_LENGTH = 57;
|
||||
const size_t RAND_VALUES_LENGTH = 10240;
|
||||
std::string p_v1;
|
||||
std::string p_v2;
|
||||
std::string p_v3;
|
||||
std::string p_v4;
|
||||
std::string p_v5;
|
||||
std::string p_v6;
|
||||
std::string p_v7;
|
||||
std::string p_v8;
|
||||
std::string p_v9;
|
||||
std::string p_rv1;
|
||||
std::string p_rv2;
|
||||
std::string p_rv3;
|
||||
std::string p_v1, p_v2, p_v3, p_v4, p_v5, p_v6, p_v7, p_v8, p_v9, p_rv1,
|
||||
p_rv2, p_rv3;
|
||||
|
||||
// Insert a very first set of keys that will be
|
||||
// mempurged at least once.
|
||||
@@ -1407,14 +1394,7 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
|
||||
const size_t NUM_REPEAT = 100;
|
||||
const size_t RAND_VALUES_LENGTH = 10240;
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
std::string p_v1;
|
||||
std::string p_v2;
|
||||
std::string p_v3;
|
||||
std::string p_v3b;
|
||||
std::string p_v4;
|
||||
std::string p_v5;
|
||||
std::string key, value, p_v1, p_v2, p_v3, p_v3b, p_v4, p_v5;
|
||||
int count = 0;
|
||||
const int EXPECTED_COUNT_FORLOOP = 3;
|
||||
const int EXPECTED_COUNT_END = 4;
|
||||
@@ -1617,15 +1597,7 @@ TEST_F(DBFlushTest, MemPurgeAndCompactionFilter) {
|
||||
Random rnd(53);
|
||||
const size_t NUM_REPEAT = 1000;
|
||||
const size_t RAND_VALUES_LENGTH = 10240;
|
||||
std::string p_v1;
|
||||
std::string p_v2;
|
||||
std::string p_v3;
|
||||
std::string p_v4;
|
||||
std::string p_v5;
|
||||
std::string p_v6;
|
||||
std::string p_v7;
|
||||
std::string p_v8;
|
||||
std::string p_v9;
|
||||
std::string p_v1, p_v2, p_v3, p_v4, p_v5, p_v6, p_v7, p_v8, p_v9;
|
||||
|
||||
p_v1 = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
p_v2 = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
@@ -1719,8 +1691,7 @@ TEST_F(DBFlushTest, DISABLED_MemPurgeWALSupport) {
|
||||
keys.push_back("IamKey" + std::to_string(k));
|
||||
}
|
||||
|
||||
std::string RNDKEY;
|
||||
std::string RNDVALUE;
|
||||
std::string RNDKEY, RNDVALUE;
|
||||
const std::string NOT_FOUND = "NOT_FOUND";
|
||||
|
||||
// Heavy overwrite workload,
|
||||
@@ -1729,8 +1700,7 @@ TEST_F(DBFlushTest, DISABLED_MemPurgeWALSupport) {
|
||||
const size_t NUM_REPEAT = 100;
|
||||
const size_t RAND_KEY_LENGTH = 4096;
|
||||
const size_t RAND_VALUES_LENGTH = 1024;
|
||||
std::vector<std::string> values_default(KVSIZE);
|
||||
std::vector<std::string> values_pikachu(KVSIZE);
|
||||
std::vector<std::string> values_default(KVSIZE), values_pikachu(KVSIZE);
|
||||
|
||||
// Insert a very first set of keys that will be
|
||||
// mempurged at least once.
|
||||
@@ -1918,8 +1888,7 @@ TEST_F(DBFlushTest, MemPurgeCorrectLogNumberAndSSTFileCreation) {
|
||||
const uint64_t NUM_RAND_INSERTS = 500;
|
||||
const uint64_t RAND_VALUES_LENGTH = 10240;
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
std::string key, value;
|
||||
std::vector<std::string> values(9, "");
|
||||
|
||||
// Keys used to check that no SST file disappeared.
|
||||
@@ -3013,34 +2982,6 @@ TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
}
|
||||
}
|
||||
|
||||
// Two handles naming the same column family make the selected-cfd list longer
|
||||
// than the per-CF memtable-id list, which is keyed by CF. The wait loop must
|
||||
// not read the id list past its own length.
|
||||
TEST_P(DBAtomicFlushTest, ManualAtomicFlushDuplicateColumnFamilyHandles) {
|
||||
if (!GetParam()) {
|
||||
// Only the atomic path reaches the shared wait list; without atomic_flush
|
||||
// each handle is flushed independently.
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = true;
|
||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
WriteOptions wopts;
|
||||
wopts.disableWAL = true;
|
||||
ASSERT_OK(Put("key", "value", wopts));
|
||||
|
||||
FlushOptions flush_options; // wait == true
|
||||
ASSERT_OK(db_->Flush(
|
||||
flush_options, {db_->DefaultColumnFamily(), db_->DefaultColumnFamily()}));
|
||||
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily());
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, PrecomputeMinLogNumberToKeepNon2PC) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -3483,8 +3424,7 @@ TEST_P(DBAtomicFlushTest, BgThreadNoWaitAfterManifestError) {
|
||||
{"BgFlushThr2:WaitToCommit", "BgFlushThr1:BeforeWriteManifest"},
|
||||
});
|
||||
|
||||
std::thread::id bg_flush_thr1;
|
||||
std::thread::id bg_flush_thr2;
|
||||
std::thread::id bg_flush_thr1, bg_flush_thr2;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCallFlush:start", [&](void*) {
|
||||
if (bg_flush_thr1 == std::thread::id()) {
|
||||
@@ -4259,14 +4199,9 @@ TEST_F(DBFlushTest, SuperBlockAlignmentValueDeltaEscape) {
|
||||
|
||||
for (bool separate_kv : {false, true}) {
|
||||
SCOPED_TRACE("separate_kv=" + std::to_string(separate_kv));
|
||||
std::vector<std::string> get7;
|
||||
std::vector<std::string> iter7;
|
||||
std::vector<std::string> get8;
|
||||
std::vector<std::string> iter8;
|
||||
int pad7 = 0;
|
||||
int pad8 = 0;
|
||||
bool delta7 = false;
|
||||
bool delta8 = false;
|
||||
std::vector<std::string> get7, iter7, get8, iter8;
|
||||
int pad7 = 0, pad8 = 0;
|
||||
bool delta7 = false, delta8 = false;
|
||||
build_and_read(7, separate_kv, &get7, &iter7, &pad7, &delta7);
|
||||
build_and_read(/*format_version=*/8, separate_kv, &get8, &iter8, &pad8,
|
||||
&delta8);
|
||||
@@ -4433,8 +4368,7 @@ TEST_F(DBFlushTest, FlushAfterReadPathRangeTombstoneInsertion) {
|
||||
// already immutable. Without the fix, this succeeds and creates
|
||||
// an entry count mismatch.
|
||||
cfh->cfd()->mem()->AddLogicallyRedundantRangeTombstone(
|
||||
1 /* seq */, "b", "f", 0 /* version_number */,
|
||||
cfh->cfd()->GetIngestSstLock());
|
||||
1 /* seq */, "b", "f", cfh->cfd()->GetIngestSstLock());
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class DBFollowerTest : public DBTestBase {
|
||||
Reopen(CurrentOptions());
|
||||
}
|
||||
|
||||
~DBFollowerTest() override {
|
||||
~DBFollowerTest() {
|
||||
follower_.reset();
|
||||
EXPECT_EQ(DestroyDB(follower_name_, CurrentOptions()), Status::OK());
|
||||
Destroy(CurrentOptions());
|
||||
|
||||
@@ -18,8 +18,6 @@ class CompactedDBImpl : public DBImpl {
|
||||
// No copying allowed
|
||||
CompactedDBImpl(const CompactedDBImpl&) = delete;
|
||||
void operator=(const CompactedDBImpl&) = delete;
|
||||
CompactedDBImpl(CompactedDBImpl&&) = delete;
|
||||
void operator=(CompactedDBImpl&&) = delete;
|
||||
|
||||
~CompactedDBImpl() override;
|
||||
|
||||
|
||||
+211
-895
File diff suppressed because it is too large.
Load diff
+12
-218
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
@@ -52,7 +51,6 @@
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "logging/event_logger.h"
|
||||
#include "memtable/flush_initiator.h"
|
||||
#include "memtable/wbwi_memtable.h"
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
#include "options/db_options.h"
|
||||
@@ -101,17 +99,6 @@ struct JobContext;
|
||||
struct ExternalSstFileInfo;
|
||||
struct MemTableInfo;
|
||||
|
||||
// Captured under the same DB mutex hold as a subset checkpoint's MANIFEST
|
||||
// boundary and live CF set. This prevents the generated OPTIONS membership
|
||||
// from describing a different point in time than the copied MANIFEST.
|
||||
struct SubsetCheckpointOptionsSnapshot {
|
||||
uint64_t options_file_number = 0;
|
||||
bool commit_options_file_number = false;
|
||||
DBOptions db_options;
|
||||
std::vector<std::string> cf_names;
|
||||
std::vector<ColumnFamilyOptions> cf_options;
|
||||
};
|
||||
|
||||
// Class to maintain directories for all database paths other than main one.
|
||||
class Directories {
|
||||
public:
|
||||
@@ -234,7 +221,7 @@ class DBImpl : public DB
|
||||
DBImpl(DBImpl&&) = delete;
|
||||
void operator=(DBImpl&&) = delete;
|
||||
|
||||
~DBImpl() override;
|
||||
virtual ~DBImpl();
|
||||
|
||||
// ---- Implementations of the DB interface ----
|
||||
|
||||
@@ -653,8 +640,7 @@ class DBImpl : public DB
|
||||
const LiveFilesStorageInfoOptions& opts,
|
||||
const std::vector<uint32_t>& include_cf_ids,
|
||||
std::vector<LiveFileStorageInfo>* files,
|
||||
std::vector<uint32_t>* excluded_cf_ids,
|
||||
SubsetCheckpointOptionsSnapshot* options_snapshot);
|
||||
std::vector<uint32_t>* excluded_cf_ids);
|
||||
|
||||
// Shared body of GetLiveFilesStorageInfo and its subset-checkpoint variant.
|
||||
// include_cf_ids empty -> no filter; otherwise records skipped CF ids in
|
||||
@@ -663,23 +649,15 @@ class DBImpl : public DB
|
||||
const LiveFilesStorageInfoOptions& opts,
|
||||
const std::vector<uint32_t>& include_cf_ids,
|
||||
std::vector<LiveFileStorageInfo>* files,
|
||||
std::vector<uint32_t>* excluded_cf_ids,
|
||||
SubsetCheckpointOptionsSnapshot* options_snapshot);
|
||||
std::vector<uint32_t>* excluded_cf_ids);
|
||||
|
||||
// Appends kColumnFamilyDrop records to the MANIFEST file at manifest_path for
|
||||
// the given column family ids. Used by Checkpoint to make a subset-CF
|
||||
// checkpoint's copied MANIFEST consistent with the reduced file set. Operates
|
||||
// only on the given file; does not mutate this DB's live state.
|
||||
Status AppendColumnFamilyDropsToManifest(
|
||||
const std::string& manifest_path, uint64_t manifest_size,
|
||||
const std::vector<uint32_t>& cf_ids,
|
||||
uint64_t committed_options_file_number);
|
||||
|
||||
// Writes a previously captured subset-specific OPTIONS snapshot into the
|
||||
// checkpoint. A zero file number is a no-op for a legacy source DB.
|
||||
Status CreateOptionsFileForSubsetCheckpoint(
|
||||
const std::string& checkpoint_dir,
|
||||
const SubsetCheckpointOptionsSnapshot& options_snapshot);
|
||||
Status AppendColumnFamilyDropsToManifest(const std::string& manifest_path,
|
||||
uint64_t manifest_size,
|
||||
const std::vector<uint32_t>& cf_ids);
|
||||
|
||||
Status GetPreparedFileInfoForExternalSstIngestion(
|
||||
const std::string& file_path,
|
||||
@@ -1213,12 +1191,6 @@ class DBImpl : public DB
|
||||
batches_[seq] = {log, batch, batch_cnt};
|
||||
}
|
||||
|
||||
// No copying allowed
|
||||
RecoveredTransaction(const RecoveredTransaction&) = delete;
|
||||
RecoveredTransaction& operator=(const RecoveredTransaction&) = delete;
|
||||
RecoveredTransaction(RecoveredTransaction&&) = delete;
|
||||
RecoveredTransaction& operator=(RecoveredTransaction&&) = delete;
|
||||
|
||||
~RecoveredTransaction() {
|
||||
for (auto& it : batches_) {
|
||||
delete it.second.batch_;
|
||||
@@ -1370,9 +1342,6 @@ class DBImpl : public DB
|
||||
|
||||
Status TEST_SwitchMemtable(ColumnFamilyData* cfd = nullptr);
|
||||
|
||||
size_t TEST_GetWBMFlushableMemUsage();
|
||||
bool TEST_IsWBMFlushInitiatorFlushable() const;
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status TEST_FlushMemTable(bool wait = true, bool allow_write_stall = false,
|
||||
ColumnFamilyHandle* cfh = nullptr);
|
||||
@@ -1393,10 +1362,7 @@ class DBImpl : public DB
|
||||
// finishes. For example in CompactRange.
|
||||
Status TEST_AtomicFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
|
||||
const FlushOptions& flush_opts, bool non_blocking_write_thread = false);
|
||||
|
||||
void TEST_BeginWriteStall();
|
||||
void TEST_EndWriteStall();
|
||||
const FlushOptions& flush_opts);
|
||||
|
||||
// Wait for background threads to complete scheduled work.
|
||||
Status TEST_WaitForBackgroundWork();
|
||||
@@ -1589,21 +1555,6 @@ class DBImpl : public DB
|
||||
}
|
||||
}
|
||||
|
||||
// Returns false when the wait times out while the DB is still blocked.
|
||||
bool BlockFor(uint64_t timeout_micros) {
|
||||
MutexLock lock(&state_mutex_);
|
||||
const std::chrono::microseconds deadline(
|
||||
SystemClock::Default()->NowMicros() + timeout_micros);
|
||||
while (state_ == State::BLOCKED) {
|
||||
TEST_SYNC_POINT("WBMStallInterface::BlockDB");
|
||||
if (SystemClock::Default()->TimedWait(&state_cv_, deadline) &&
|
||||
state_ == State::BLOCKED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called from WriteBufferManager. This function changes the state_
|
||||
// to State::RUNNING indicating the stall is cleared and DB can proceed.
|
||||
void Signal() override {
|
||||
@@ -1624,24 +1575,6 @@ class DBImpl : public DB
|
||||
State state_;
|
||||
};
|
||||
|
||||
// Adapts DBImpl to the shared WriteBufferManager flush registry.
|
||||
class WBMFlushInitiator : public FlushInitiator {
|
||||
public:
|
||||
WBMFlushInitiator(DBImpl* db, bool atomic_flush)
|
||||
: FlushInitiator(atomic_flush), db_(db) {}
|
||||
|
||||
bool ScheduleFlush() override {
|
||||
return db_->ScheduleWriteBufferManagerFlush();
|
||||
}
|
||||
|
||||
bool TryRefreshMemoryAccounting() override {
|
||||
return db_->TryRefreshFlushableMemAccounting();
|
||||
}
|
||||
|
||||
private:
|
||||
DBImpl* const db_;
|
||||
};
|
||||
|
||||
static void TEST_ResetDbSessionIdGen();
|
||||
static std::string GenerateDbSessionId(Env* env);
|
||||
|
||||
@@ -1659,9 +1592,6 @@ class DBImpl : public DB
|
||||
// db_session_id_ is an identifier that gets reset
|
||||
// every time the DB is opened
|
||||
std::string db_session_id_;
|
||||
// Declared before versions_ so memtables cannot outlive their allocation
|
||||
// accounting target during destruction.
|
||||
std::unique_ptr<FlushInitiator> wbm_flush_initiator_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
@@ -1754,13 +1684,6 @@ class DBImpl : public DB
|
||||
// with corresponding column_family_data and column_family_options.
|
||||
class RecoveryContext {
|
||||
public:
|
||||
RecoveryContext() = default;
|
||||
// No copying allowed
|
||||
RecoveryContext(const RecoveryContext&) = delete;
|
||||
RecoveryContext& operator=(const RecoveryContext&) = delete;
|
||||
RecoveryContext(RecoveryContext&&) = delete;
|
||||
RecoveryContext& operator=(RecoveryContext&&) = delete;
|
||||
|
||||
~RecoveryContext() {
|
||||
for (auto& edit_list : edit_lists_) {
|
||||
for (auto* edit : edit_list) {
|
||||
@@ -1793,10 +1716,6 @@ class DBImpl : public DB
|
||||
std::unordered_map<uint32_t, uint32_t> map_; // cf_id to index;
|
||||
autovector<ColumnFamilyData*> cfds_;
|
||||
autovector<autovector<VersionEdit*>> edit_lists_;
|
||||
// Files found in the primary DB directory by MaybeUpdateNextFileNumber().
|
||||
// Reused for OPTIONS selection so writable open performs one directory
|
||||
// scan instead of two.
|
||||
std::vector<std::string> existing_db_files_;
|
||||
// All existing data files (SST files and Blob files) found during DB::Open.
|
||||
std::vector<std::string> existing_data_files_;
|
||||
bool is_new_db_ = false;
|
||||
@@ -1807,24 +1726,6 @@ class DBImpl : public DB
|
||||
Status WriteOptionsFile(const WriteOptions& write_options,
|
||||
bool db_mutex_already_held);
|
||||
|
||||
// Writes, renames, and directory-syncs an OPTIONS snapshot, but does not
|
||||
// publish it in VersionSet. The caller must either commit its file number in
|
||||
// MANIFEST or leave it as an unreferenced orphan.
|
||||
Status PersistOptionsFile(const WriteOptions& write_options,
|
||||
const DBOptions& db_options,
|
||||
const std::vector<std::string>& cf_names,
|
||||
const std::vector<ColumnFamilyOptions>& cf_opts,
|
||||
uint64_t* options_file_number,
|
||||
uint64_t* options_file_size);
|
||||
|
||||
// For tracked DBs, writes the OPTIONS snapshot that is committed with a
|
||||
// column-family add/drop. Returns file number zero in legacy mode.
|
||||
Status PersistOptionsFileForColumnFamilyManipulation(
|
||||
const WriteOptions& write_options,
|
||||
const std::vector<ColumnFamilyDescriptor>& added_column_families,
|
||||
const std::vector<uint32_t>& excluded_cf_ids,
|
||||
uint64_t* options_file_number, uint64_t* options_file_size);
|
||||
|
||||
Status CompactRangeInternal(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end,
|
||||
@@ -1834,9 +1735,7 @@ class DBImpl : public DB
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
// 2. db_mutex is NOT held
|
||||
Status RenameTempFileToOptionsFile(const std::string& file_name,
|
||||
uint64_t options_file_number,
|
||||
uint64_t* options_file_size);
|
||||
void MaybeDeleteObsoleteOptionsFiles();
|
||||
bool is_remote_compaction_enabled);
|
||||
Status DeleteObsoleteOptionsFiles(bool schedule_only);
|
||||
|
||||
void NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
|
||||
@@ -2277,12 +2176,6 @@ class DBImpl : public DB
|
||||
explicit WriteContext(bool create_superversion = false)
|
||||
: superversion_context(create_superversion) {}
|
||||
|
||||
// No copying allowed
|
||||
WriteContext(const WriteContext&) = delete;
|
||||
WriteContext& operator=(const WriteContext&) = delete;
|
||||
WriteContext(WriteContext&&) = delete;
|
||||
WriteContext& operator=(WriteContext&&) = delete;
|
||||
|
||||
~WriteContext() {
|
||||
superversion_context.Clean();
|
||||
for (auto& m : memtables_to_free_) {
|
||||
@@ -2536,15 +2429,6 @@ class DBImpl : public DB
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::string& cf_name,
|
||||
ColumnFamilyHandle** handle);
|
||||
Status CreateColumnFamiliesImpl(
|
||||
const ReadOptions& read_options, const WriteOptions& write_options,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles);
|
||||
Status CreateColumnFamilyWithPreparedOptions(
|
||||
const ReadOptions& read_options, const WriteOptions& write_options,
|
||||
const ColumnFamilyOptions& sanitized_cf_options,
|
||||
const std::string& cf_name, uint64_t options_file_number,
|
||||
uint64_t options_file_size, ColumnFamilyHandle** handle);
|
||||
|
||||
// Follow-up work to user creating a column family or (families)
|
||||
Status WrapUpCreateColumnFamilies(
|
||||
@@ -2552,9 +2436,6 @@ class DBImpl : public DB
|
||||
const std::vector<const ColumnFamilyOptions*>& cf_options);
|
||||
|
||||
Status DropColumnFamilyImpl(ColumnFamilyHandle* column_family);
|
||||
Status DropColumnFamilyWithOptionsFile(ColumnFamilyHandle* column_family,
|
||||
uint64_t options_file_number,
|
||||
uint64_t options_file_size);
|
||||
|
||||
// Delete any unneeded files and stale in-memory entries.
|
||||
void DeleteObsoleteFiles();
|
||||
@@ -2750,7 +2631,7 @@ class DBImpl : public DB
|
||||
|
||||
// Begin stalling of writes when memory usage increases beyond a certain
|
||||
// threshold.
|
||||
Status WriteBufferManagerStallWrites(WriteContext* write_context);
|
||||
void WriteBufferManagerStallWrites();
|
||||
|
||||
Status ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch);
|
||||
@@ -2778,16 +2659,6 @@ class DBImpl : public DB
|
||||
ReadOnlyMemTable* new_imm = nullptr,
|
||||
SequenceNumber last_seqno = 0);
|
||||
|
||||
// `refresh_wbm_accounting` can be false when a caller switches a batch of
|
||||
// memtables and refreshes the DB-wide accounting once after the batch.
|
||||
Status SwitchMemtableImpl(ColumnFamilyData* cfd, WriteContext* context,
|
||||
ReadOnlyMemTable* new_imm,
|
||||
SequenceNumber last_seqno,
|
||||
bool refresh_wbm_accounting);
|
||||
|
||||
// REQUIRES: mutex_ is held
|
||||
void UpdateWBMAccountingAfterMemtableSwitch(bool refresh_now);
|
||||
|
||||
// Select and output column families qualified for atomic flush in
|
||||
// `selected_cfds`. If `provided_candidate_cfds` is non-empty, it will be used
|
||||
// as candidate CFs to select qualified ones from. Otherwise, all column
|
||||
@@ -2812,18 +2683,6 @@ class DBImpl : public DB
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds = {},
|
||||
bool entered_write_thread = false);
|
||||
|
||||
enum class WriteThreadJoinMode { kBlocking, kNonBlocking };
|
||||
|
||||
Status FlushMemTableImpl(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
FlushReason flush_reason, bool entered_write_thread,
|
||||
WriteThreadJoinMode join_mode, bool* made_progress);
|
||||
|
||||
Status AtomicFlushMemTablesImpl(
|
||||
const FlushOptions& options, FlushReason flush_reason,
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
|
||||
bool entered_write_thread, WriteThreadJoinMode join_mode,
|
||||
bool* made_progress);
|
||||
|
||||
// REQUIRES: mutex locked and write queues drained up to the recovery flush
|
||||
// fence that is about to switch memtables.
|
||||
void MaybeSyncLastSequenceWithAllocatedForRecovery(FlushReason flush_reason);
|
||||
@@ -2910,18 +2769,6 @@ class DBImpl : public DB
|
||||
return static_cast<uint8_t*>(static_cast<void*>(this)) + type;
|
||||
}
|
||||
|
||||
// Checks DB-local write queues; manager-wide stall state would reject healthy
|
||||
// DBs. REQUIRES: mutex_ held.
|
||||
bool WouldBlockJoiningWriteThread() {
|
||||
mutex_.AssertHeld();
|
||||
return write_thread_.GetBegunCountOfOutstandingStall() != 0 ||
|
||||
(two_write_queues_ &&
|
||||
nonmem_write_thread_.GetBegunCountOfOutstandingStall() != 0);
|
||||
}
|
||||
|
||||
Status EnterWriteThreadForNonBlockingFlush(WriteThread::Writer* w,
|
||||
WriteThread::Writer* nonmem_w);
|
||||
|
||||
// REQUIRES: mutex locked and in write thread.
|
||||
void AssignAtomicFlushSeq(const autovector<ColumnFamilyData*>& cfds);
|
||||
|
||||
@@ -2931,31 +2778,6 @@ class DBImpl : public DB
|
||||
// REQUIRES: mutex locked and in write thread.
|
||||
Status HandleWriteBufferManagerFlush(WriteContext* write_context);
|
||||
|
||||
// Column families and memory eligible for a WriteBufferManager flush.
|
||||
struct FlushableCFs {
|
||||
ColumnFamilyData* largest = nullptr; // most reclaimable memtable memory
|
||||
ColumnFamilyData* oldest = nullptr; // smallest memtable creation seq
|
||||
size_t largest_mem = 0;
|
||||
size_t total_mem = 0;
|
||||
};
|
||||
|
||||
// Shared selection for bidding and flushing. REQUIRES: mutex_ held.
|
||||
FlushableCFs CollectFlushableCFs(bool include_waiting_immutable);
|
||||
|
||||
// Registers an opened read-write DB without holding mutex_.
|
||||
void MaybeRegisterFlushInitiator();
|
||||
|
||||
// Refreshes the published mutable-memory counters after memtables change.
|
||||
// REQUIRES: mutex_ held.
|
||||
void RefreshFlushableMemAccounting();
|
||||
|
||||
bool TryRefreshFlushableMemAccounting();
|
||||
|
||||
// Queues one WBM flush; false lets the coordinator try another DB.
|
||||
bool ScheduleWriteBufferManagerFlush();
|
||||
|
||||
void BackgroundCallWBMFlush();
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status PreprocessWrite(const WriteOptions& write_options,
|
||||
WalContext* log_context, WriteContext* write_context);
|
||||
@@ -2994,13 +2816,7 @@ class DBImpl : public DB
|
||||
|
||||
// Used by WriteImpl to update bg_error_ when IO error happens, e.g., write
|
||||
// WAL, sync WAL fails, if paranoid check is enabled.
|
||||
void WALIOStatusCheck(const IOStatus& status, uint64_t failed_wal_number = 0,
|
||||
SequenceNumber failed_wal_sequence = 0,
|
||||
SequenceNumber failed_wal_sequence_start = 0);
|
||||
|
||||
// Extends the inclusive WAL-number cutoff that must not receive more I/O.
|
||||
// This is lock-free because a WAL error can race with public SyncWAL().
|
||||
void ExtendWALRecoveryCutoff(uint64_t failed_wal_number);
|
||||
void WALIOStatusCheck(const IOStatus& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
|
||||
void HandleMemTableInsertFailure(const Status& nonok_memtable_insert_status);
|
||||
@@ -3307,8 +3123,6 @@ class DBImpl : public DB
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UnpublishedWAL() = default;
|
||||
|
||||
void Reset() {
|
||||
log_number = 0;
|
||||
writer.reset();
|
||||
@@ -3588,16 +3402,6 @@ class DBImpl : public DB
|
||||
// * whenever a column family is dropped.
|
||||
InstrumentedCondVar bg_cv_;
|
||||
|
||||
// Set only while ResumeImpl performs a file-scoped WAL recovery. Protected
|
||||
// by mutex_; SwitchMemtable uses it to avoid touching the failed writer.
|
||||
bool recovering_from_wal_write_error_ = false;
|
||||
uint64_t recover_wal_through_number_ = 0;
|
||||
|
||||
// Once a file-scoped WAL error is reported, all writers through this
|
||||
// inclusive cutoff are quarantined until recovery detaches them. A
|
||||
// concurrent SyncWAL() reads this without taking the DB mutex.
|
||||
std::atomic<uint64_t> wal_recovery_cutoff_{0};
|
||||
|
||||
ColumnFamilyHandleImpl* persist_stats_cf_handle_ = nullptr;
|
||||
|
||||
bool persistent_stats_cfd_exists_ = true;
|
||||
@@ -3861,12 +3665,6 @@ class DBImpl : public DB
|
||||
// number of background memtable flush jobs, submitted to the HIGH pool
|
||||
int bg_flush_scheduled_ = 0;
|
||||
|
||||
// LOW avoids consuming a flush worker while joining the target write thread.
|
||||
static constexpr Env::Priority kWBMFlushPriority = Env::Priority::LOW;
|
||||
|
||||
// Outstanding cross-DB WBM flush jobs, drained during shutdown.
|
||||
int bg_wbm_flush_scheduled_ = 0;
|
||||
|
||||
// stores the number of flushes are currently running
|
||||
int num_running_flushes_ = 0;
|
||||
|
||||
@@ -4177,12 +3975,8 @@ uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
// Fix user-supplied options to be reasonable
|
||||
template <class T, class V>
|
||||
static void ClipToRange(T* ptr, V minvalue, V maxvalue) {
|
||||
if (static_cast<V>(*ptr) > maxvalue) {
|
||||
*ptr = maxvalue;
|
||||
}
|
||||
if (static_cast<V>(*ptr) < minvalue) {
|
||||
*ptr = minvalue;
|
||||
}
|
||||
if (static_cast<V>(*ptr) > maxvalue) *ptr = maxvalue;
|
||||
if (static_cast<V>(*ptr) < minvalue) *ptr = minvalue;
|
||||
}
|
||||
|
||||
inline Status DBImpl::FailIfCfHasTs(
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "db/blob/blob_file_partition_manager.h"
|
||||
@@ -62,15 +61,6 @@ void RecordAtomicFlushRequestReason(Statistics* stats,
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint64_t> MemTableIds(const autovector<ReadOnlyMemTable*>& mems) {
|
||||
std::vector<uint64_t> ids;
|
||||
ids.reserve(mems.size());
|
||||
for (const ReadOnlyMemTable* mem : mems) {
|
||||
ids.push_back(mem->GetID());
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool DBImpl::EnoughRoomForCompaction(
|
||||
@@ -320,7 +310,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
flush_reason);
|
||||
|
||||
bool switched_to_mempurge = false;
|
||||
std::vector<uint64_t> prepared_blob_memtable_ids;
|
||||
size_t prepared_blob_generations = 0;
|
||||
std::vector<uint64_t> sealed_blob_numbers;
|
||||
// Within flush_job.Run, rocksdb may call event listener to notify
|
||||
// file creation and deletion.
|
||||
//
|
||||
@@ -331,17 +322,17 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
auto mgr_handle = cfd->blob_partition_manager_handle();
|
||||
auto* mgr = mgr_handle.get();
|
||||
if (mgr != nullptr) {
|
||||
prepared_blob_memtable_ids = MemTableIds(flush_job.GetMemTables());
|
||||
prepared_blob_generations = flush_job.GetMemTables().size();
|
||||
std::vector<BlobFileAddition> write_path_additions;
|
||||
std::vector<BlobFileGarbage> write_path_garbages;
|
||||
std::vector<std::vector<uint64_t>> generation_blob_file_numbers;
|
||||
mutex_.Unlock();
|
||||
s = mgr->PrepareFlushAdditions(
|
||||
write_options, prepared_blob_memtable_ids, &write_path_additions,
|
||||
write_options, prepared_blob_generations, &write_path_additions,
|
||||
&write_path_garbages, &generation_blob_file_numbers);
|
||||
mutex_.Lock();
|
||||
if (!s.ok()) {
|
||||
prepared_blob_memtable_ids.clear();
|
||||
prepared_blob_generations = 0;
|
||||
} else if (!write_path_additions.empty()) {
|
||||
const auto& memtables = flush_job.GetMemTables();
|
||||
assert(generation_blob_file_numbers.size() == memtables.size());
|
||||
@@ -352,6 +343,9 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
memtables[i]->ProtectSealedBlobFiles(mgr_handle,
|
||||
generation_blob_file_numbers[i]);
|
||||
}
|
||||
for (const auto& addition : write_path_additions) {
|
||||
sealed_blob_numbers.push_back(addition.GetBlobFileNumber());
|
||||
}
|
||||
flush_job.AddExternalBlobFileAdditions(std::move(write_path_additions));
|
||||
flush_job.AddExternalBlobFileGarbages(std::move(write_path_garbages));
|
||||
} else if (!write_path_garbages.empty()) {
|
||||
@@ -395,7 +389,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
});
|
||||
|
||||
if (cfd->blob_partition_manager() != nullptr &&
|
||||
!prepared_blob_memtable_ids.empty()) {
|
||||
prepared_blob_generations > 0) {
|
||||
auto unconsumed_additions = flush_job.TakeExternalBlobFileAdditions();
|
||||
auto unconsumed_garbages = flush_job.TakeExternalBlobFileGarbages();
|
||||
if (!s.ok() || !unconsumed_additions.empty() ||
|
||||
@@ -404,7 +398,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// in pending_generations_. The same immutable memtables still reference
|
||||
// these sealed blob files, so a later flush retry must publish the exact
|
||||
// same additions rather than abandoning them here.
|
||||
prepared_blob_memtable_ids.clear();
|
||||
prepared_blob_generations = 0;
|
||||
sealed_blob_numbers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,9 +407,12 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
flush_job.Cancel();
|
||||
}
|
||||
|
||||
if (s.ok() && !prepared_blob_memtable_ids.empty()) {
|
||||
cfd->blob_partition_manager()->CommitFlushedGenerations(
|
||||
cfd->imm()->GetEarliestMemTableID());
|
||||
if (s.ok() && prepared_blob_generations > 0) {
|
||||
auto* mgr = cfd->blob_partition_manager();
|
||||
mgr->CommitPreparedGenerations(prepared_blob_generations);
|
||||
if (!sealed_blob_numbers.empty()) {
|
||||
mgr->RemoveFilePartitionMappings(sealed_blob_numbers);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -657,12 +655,13 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// <bool /* executed */, Status /* status code */>
|
||||
autovector<std::pair<bool, Status>> exec_status;
|
||||
std::vector<bool> pick_status;
|
||||
std::vector<std::vector<uint64_t>> prepared_blob_memtable_ids(num_cfs);
|
||||
std::vector<size_t> prepared_blob_generations(num_cfs, 0);
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
// Initially all jobs are not executed, with status OK.
|
||||
exec_status.emplace_back(false, Status::OK());
|
||||
pick_status.push_back(false);
|
||||
}
|
||||
std::unordered_map<int, std::vector<uint64_t>> sealed_blob_numbers_by_cf;
|
||||
|
||||
bool flush_for_recovery =
|
||||
bg_flush_args[0].flush_reason_ == FlushReason::kErrorRecovery ||
|
||||
@@ -694,17 +693,17 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
continue;
|
||||
}
|
||||
|
||||
prepared_blob_memtable_ids[i] = MemTableIds(jobs[i]->GetMemTables());
|
||||
prepared_blob_generations[i] = jobs[i]->GetMemTables().size();
|
||||
std::vector<BlobFileAddition> write_path_additions;
|
||||
std::vector<BlobFileGarbage> write_path_garbages;
|
||||
std::vector<std::vector<uint64_t>> generation_blob_file_numbers;
|
||||
mutex_.Unlock();
|
||||
s = mgr->PrepareFlushAdditions(
|
||||
write_options, prepared_blob_memtable_ids[i], &write_path_additions,
|
||||
write_options, prepared_blob_generations[i], &write_path_additions,
|
||||
&write_path_garbages, &generation_blob_file_numbers);
|
||||
mutex_.Lock();
|
||||
if (!s.ok()) {
|
||||
prepared_blob_memtable_ids[i].clear();
|
||||
prepared_blob_generations[i] = 0;
|
||||
break;
|
||||
}
|
||||
if (!write_path_additions.empty()) {
|
||||
@@ -714,6 +713,10 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
memtables[j]->ProtectSealedBlobFiles(mgr_handle,
|
||||
generation_blob_file_numbers[j]);
|
||||
}
|
||||
auto& sealed_blob_numbers = sealed_blob_numbers_by_cf[i];
|
||||
for (const auto& addition : write_path_additions) {
|
||||
sealed_blob_numbers.push_back(addition.GetBlobFileNumber());
|
||||
}
|
||||
jobs[i]->AddExternalBlobFileAdditions(std::move(write_path_additions));
|
||||
}
|
||||
if (!write_path_garbages.empty()) {
|
||||
@@ -983,7 +986,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
auto* mgr = cfds[i]->blob_partition_manager();
|
||||
if (mgr == nullptr || prepared_blob_memtable_ids[i].empty()) {
|
||||
if (mgr == nullptr || prepared_blob_generations[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -994,11 +997,15 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// Same retry semantics as the single-CF flush path above: do not
|
||||
// consume these prepared generations yet, because the rolled-back
|
||||
// memtables still need to reuse the same sealed blob files on retry.
|
||||
prepared_blob_memtable_ids[i].clear();
|
||||
prepared_blob_generations[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
mgr->CommitFlushedGenerations(cfds[i]->imm()->GetEarliestMemTableID());
|
||||
mgr->CommitPreparedGenerations(prepared_blob_generations[i]);
|
||||
auto it = sealed_blob_numbers_by_cf.find(i);
|
||||
if (it != sealed_blob_numbers_by_cf.end() && !it->second.empty()) {
|
||||
mgr->RemoveFilePartitionMappings(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -1213,8 +1220,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
end_without_ts, "" /*trim_ts*/);
|
||||
}
|
||||
|
||||
std::string begin_str;
|
||||
std::string end_str;
|
||||
std::string begin_str, end_str;
|
||||
auto [begin, end] =
|
||||
MaybeAddTimestampsToRange(OptSlice::CopyFromPtr(begin_without_ts),
|
||||
OptSlice::CopyFromPtr(end_without_ts), ts_sz,
|
||||
@@ -1477,8 +1483,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
// at L1 (or LBase), if applicable.
|
||||
int level = first_overlapped_level;
|
||||
final_output_level = level;
|
||||
int output_level = 0;
|
||||
int base_level = 0;
|
||||
int output_level = 0, base_level = 0;
|
||||
for (;;) {
|
||||
// Always allow L0 -> L1 compaction
|
||||
if (level > 0) {
|
||||
@@ -1707,7 +1712,7 @@ void DBImpl::PrepareTrivialMoveEdit(Compaction& c, LogBuffer* log_buffer,
|
||||
c.output_level(), f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest, f->fd.smallest_seqno,
|
||||
f->fd.largest_seqno, f->marked_for_compaction, f->temperature,
|
||||
f->oldest_blob_file_number, f->oldest_ancestor_time,
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number, f->file_checksum,
|
||||
f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
@@ -2028,7 +2033,7 @@ Status DBImpl::PauseBackgroundWork() {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
bg_compaction_paused_++;
|
||||
while (bg_bottom_compaction_scheduled_ > 0 || bg_compaction_scheduled_ > 0 ||
|
||||
bg_flush_scheduled_ > 0 || bg_wbm_flush_scheduled_ > 0) {
|
||||
bg_flush_scheduled_ > 0) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
bg_work_paused_++;
|
||||
@@ -2284,7 +2289,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
to_level, f->fd.GetNumber(), f->fd.GetPathId(), f->fd.GetFileSize(),
|
||||
f->smallest, f->largest, f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction, f->temperature, f->oldest_blob_file_number,
|
||||
f->oldest_ancestor_time, f->file_creation_time, f->epoch_number,
|
||||
f->oldest_ancester_time, f->file_creation_time, f->epoch_number,
|
||||
f->file_checksum, f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
f->user_defined_timestamps_persisted, f->min_timestamp,
|
||||
@@ -2440,8 +2445,7 @@ Status DBImpl::RunManualCompaction(
|
||||
assert(input_level == ColumnFamilyData::kCompactAllLevels ||
|
||||
input_level >= 0);
|
||||
|
||||
InternalKey begin_storage;
|
||||
InternalKey end_storage;
|
||||
InternalKey begin_storage, end_storage;
|
||||
CompactionArg* ca = nullptr;
|
||||
|
||||
bool scheduled = false;
|
||||
@@ -2720,54 +2724,12 @@ void DBImpl::MaybeSyncLastSequenceWithAllocatedForRecovery(
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::EnterWriteThreadForNonBlockingFlush(
|
||||
WriteThread::Writer* w, WriteThread::Writer* nonmem_w) {
|
||||
mutex_.AssertHeld();
|
||||
if (!write_thread_.EnterUnbatchedNonBlocking(w, &mutex_)) {
|
||||
return Status::Incomplete(
|
||||
"Write stall in progress, unable to join the write thread to switch "
|
||||
"memtables");
|
||||
}
|
||||
if (two_write_queues_ &&
|
||||
!nonmem_write_thread_.EnterUnbatchedNonBlocking(nonmem_w, &mutex_)) {
|
||||
write_thread_.ExitUnbatched(w);
|
||||
return Status::Incomplete(
|
||||
"Write stall in progress, unable to join the write thread to switch "
|
||||
"memtables");
|
||||
}
|
||||
if (!error_handler_.IsBGWorkStopped() && !write_controller_.IsStopped() &&
|
||||
!WouldBlockJoiningWriteThread()) {
|
||||
return Status::OK();
|
||||
}
|
||||
write_thread_.ExitUnbatched(w);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(nonmem_w);
|
||||
}
|
||||
return Status::Incomplete(
|
||||
"Write stop started while joining the write thread to switch memtables");
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
FlushReason flush_reason,
|
||||
bool entered_write_thread) {
|
||||
return FlushMemTableImpl(cfd, flush_options, flush_reason,
|
||||
entered_write_thread, WriteThreadJoinMode::kBlocking,
|
||||
nullptr /* made_progress */);
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
FlushReason flush_reason,
|
||||
bool entered_write_thread,
|
||||
WriteThreadJoinMode join_mode,
|
||||
bool* made_progress) {
|
||||
if (made_progress != nullptr) {
|
||||
*made_progress = false;
|
||||
}
|
||||
// This method should not be called if atomic_flush is true.
|
||||
assert(!immutable_db_options_.atomic_flush);
|
||||
assert(!IsRecoveryFlush(flush_reason));
|
||||
if (!flush_options.wait && write_controller_.IsStopped()) {
|
||||
std::ostringstream oss;
|
||||
oss << "Writes have been stopped, thus unable to perform manual flush. "
|
||||
@@ -2801,45 +2763,13 @@ Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
|
||||
WriteThread::Writer w;
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (needs_to_join_write_thread) {
|
||||
if (join_mode == WriteThreadJoinMode::kNonBlocking) {
|
||||
s = EnterWriteThreadForNonBlockingFlush(&w, &nonmem_w);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
}
|
||||
if (join_mode == WriteThreadJoinMode::kNonBlocking && cfd->IsDropped()) {
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableImpl:Dropped");
|
||||
if (needs_to_join_write_thread) {
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
}
|
||||
s.PermitUncheckedOk();
|
||||
return Status::ColumnFamilyDropped();
|
||||
}
|
||||
WaitForPendingWrites();
|
||||
|
||||
if (join_mode == WriteThreadJoinMode::kNonBlocking &&
|
||||
flush_reason == FlushReason::kWriteBufferManager &&
|
||||
cfd->imm()->IsFlushPendingOrRunning()) {
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableImpl:AlreadyFlushing");
|
||||
if (needs_to_join_write_thread) {
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
}
|
||||
return Status::Incomplete(
|
||||
"Column family started flushing before the WBM handoff completed");
|
||||
}
|
||||
|
||||
// Recovery may have released `mutex_` after the earlier `ResumeImpl()`
|
||||
// sync. Refresh sequence state at the actual memtable-switch fence, after
|
||||
// both write queues are drained and before `SwitchMemtable()` consumes
|
||||
@@ -2849,9 +2779,6 @@ Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load() ||
|
||||
IsRecoveryFlush(flush_reason)) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
if (s.ok() && made_progress != nullptr) {
|
||||
*made_progress = true;
|
||||
}
|
||||
}
|
||||
const uint64_t flush_memtable_id = std::numeric_limits<uint64_t>::max();
|
||||
if (s.ok()) {
|
||||
@@ -2924,11 +2851,7 @@ Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
|
||||
req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||
bool already_queued_for_flush = loop_cfd->queued_for_flush();
|
||||
bool flush_req_enqueued = EnqueuePendingFlush(req);
|
||||
if (flush_req_enqueued && made_progress != nullptr) {
|
||||
*made_progress = true;
|
||||
}
|
||||
if (flush_reason != FlushReason::kWriteBufferManager &&
|
||||
(already_queued_for_flush || flush_req_enqueued)) {
|
||||
if (already_queued_for_flush || flush_req_enqueued) {
|
||||
loop_cfd->SetFlushSkipReschedule();
|
||||
}
|
||||
}
|
||||
@@ -2943,9 +2866,7 @@ Status DBImpl::FlushMemTableImpl(ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
|
||||
if (flush_reason != FlushReason::kWriteBufferManager) {
|
||||
NotifyOnManualFlushScheduled({cfd}, flush_reason);
|
||||
}
|
||||
NotifyOnManualFlushScheduled({cfd}, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:AfterScheduleFlush");
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
@@ -2974,20 +2895,6 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
const FlushOptions& flush_options, FlushReason flush_reason,
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
|
||||
bool entered_write_thread) {
|
||||
return AtomicFlushMemTablesImpl(flush_options, flush_reason,
|
||||
provided_candidate_cfds, entered_write_thread,
|
||||
WriteThreadJoinMode::kBlocking,
|
||||
nullptr /* made_progress */);
|
||||
}
|
||||
|
||||
Status DBImpl::AtomicFlushMemTablesImpl(
|
||||
const FlushOptions& flush_options, FlushReason flush_reason,
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
|
||||
bool entered_write_thread, WriteThreadJoinMode join_mode,
|
||||
bool* made_progress) {
|
||||
if (made_progress != nullptr) {
|
||||
*made_progress = false;
|
||||
}
|
||||
if (!flush_options.wait && write_controller_.IsStopped()) {
|
||||
std::ostringstream oss;
|
||||
oss << "Writes have been stopped, thus unable to perform manual flush. "
|
||||
@@ -3018,30 +2925,32 @@ Status DBImpl::AtomicFlushMemTablesImpl(
|
||||
candidate_cfds = provided_candidate_cfds;
|
||||
}
|
||||
|
||||
const auto unref_generated_candidates = [&]() {
|
||||
if (!provided_candidate_cfds.empty()) {
|
||||
return;
|
||||
}
|
||||
for (auto candidate_cfd : candidate_cfds) {
|
||||
candidate_cfd->UnrefAndTryDelete();
|
||||
}
|
||||
candidate_cfds.clear();
|
||||
};
|
||||
|
||||
if (!flush_options.allow_write_stall) {
|
||||
int num_cfs_to_flush = 0;
|
||||
for (auto cfd : candidate_cfds) {
|
||||
bool flush_needed = true;
|
||||
s = WaitUntilFlushWouldNotStallWrites(cfd, &flush_needed);
|
||||
if (!s.ok()) {
|
||||
unref_generated_candidates();
|
||||
// Unref the newly generated candidate cfds (when not provided) in
|
||||
// `candidate_cfds`
|
||||
if (provided_candidate_cfds.empty()) {
|
||||
for (auto candidate_cfd : candidate_cfds) {
|
||||
candidate_cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
return s;
|
||||
} else if (flush_needed) {
|
||||
++num_cfs_to_flush;
|
||||
}
|
||||
}
|
||||
if (0 == num_cfs_to_flush) {
|
||||
unref_generated_candidates();
|
||||
// Unref the newly generated candidate cfds (when not provided) in
|
||||
// `candidate_cfds`
|
||||
if (provided_candidate_cfds.empty()) {
|
||||
for (auto candidate_cfd : candidate_cfds) {
|
||||
candidate_cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -3056,54 +2965,39 @@ Status DBImpl::AtomicFlushMemTablesImpl(
|
||||
WriteThread::Writer w;
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (needs_to_join_write_thread) {
|
||||
if (join_mode == WriteThreadJoinMode::kNonBlocking) {
|
||||
s = EnterWriteThreadForNonBlockingFlush(&w, &nonmem_w);
|
||||
if (!s.ok()) {
|
||||
unref_generated_candidates();
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
}
|
||||
WaitForPendingWrites();
|
||||
|
||||
// Recovery may have released `mutex_` after the earlier `ResumeImpl()`
|
||||
// sync. Refresh sequence state at the atomic memtable-switch fence, after
|
||||
// both write queues are drained and before `SwitchMemtable()` consumes
|
||||
// `LastSequence()`.
|
||||
// Keep atomic recovery flushes consistent with the single-CF path: the
|
||||
// sequence sync must happen after both write queues are drained and before
|
||||
// any recovery memtable switch reads `LastSequence()`.
|
||||
MaybeSyncLastSequenceWithAllocatedForRecovery(flush_reason);
|
||||
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds, candidate_cfds, flush_reason);
|
||||
|
||||
unref_generated_candidates();
|
||||
// Unref the newly generated candidate cfds (when not provided) in
|
||||
// `candidate_cfds`
|
||||
if (provided_candidate_cfds.empty()) {
|
||||
for (auto candidate_cfd : candidate_cfds) {
|
||||
candidate_cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
|
||||
const bool defer_wbm_accounting_refresh = cfds.size() > 1;
|
||||
bool switch_attempted = false;
|
||||
for (auto cfd : cfds) {
|
||||
if (cfd->mem()->IsEmpty() && cached_recoverable_state_empty_.load() &&
|
||||
!IsRecoveryFlush(flush_reason)) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
switch_attempted = true;
|
||||
s = defer_wbm_accounting_refresh
|
||||
? SwitchMemtableImpl(cfd, &context, nullptr, 0,
|
||||
false /* refresh_wbm_accounting */)
|
||||
: SwitchMemtable(cfd, &context);
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
if (made_progress != nullptr) {
|
||||
*made_progress = true;
|
||||
}
|
||||
}
|
||||
if (defer_wbm_accounting_refresh && switch_attempted) {
|
||||
UpdateWBMAccountingAfterMemtableSwitch(true /* refresh_now */);
|
||||
}
|
||||
if (s.ok()) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
@@ -3120,9 +3014,7 @@ Status DBImpl::AtomicFlushMemTablesImpl(
|
||||
}
|
||||
}
|
||||
GenerateFlushRequest(cfds, flush_reason, &flush_req);
|
||||
if (EnqueuePendingFlush(flush_req) && made_progress != nullptr) {
|
||||
*made_progress = true;
|
||||
}
|
||||
EnqueuePendingFlush(flush_req);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
@@ -3133,39 +3025,14 @@ Status DBImpl::AtomicFlushMemTablesImpl(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flush_reason != FlushReason::kWriteBufferManager) {
|
||||
NotifyOnManualFlushScheduled(cfds, flush_reason);
|
||||
}
|
||||
NotifyOnManualFlushScheduled(cfds, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
// Walk cfds and look each one up, so the ids line up with cfds by
|
||||
// construction. Iterating the map instead pairs ids with the wrong CF
|
||||
// (its order is unrelated to cfds), and a CF that dropped out of the map
|
||||
// (an idle CF de-selected during the switch loop, or a duplicate handle
|
||||
// collapsed by the map) would make the two vectors different lengths.
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
flush_memtable_ids.reserve(cfds.size());
|
||||
for (auto* cfd : cfds) {
|
||||
auto iter = flush_req.cfd_to_max_mem_id_to_persist.find(cfd);
|
||||
flush_memtable_ids.push_back(
|
||||
iter == flush_req.cfd_to_max_mem_id_to_persist.end()
|
||||
? nullptr
|
||||
: &(iter->second));
|
||||
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
flush_memtable_ids.push_back(&(iter.second));
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
// WaitForFlushMemTables pairs flush_memtable_ids[i] with cfds[i], so each
|
||||
// entry must point at this cfd's own map id (or be null when absent). Fires
|
||||
// if the ids are ever rebuilt in the request map's order.
|
||||
assert(flush_memtable_ids.size() == cfds.size());
|
||||
for (size_t i = 0; i < cfds.size(); ++i) {
|
||||
auto iter = flush_req.cfd_to_max_mem_id_to_persist.find(cfds[i]);
|
||||
assert(flush_memtable_ids[i] ==
|
||||
(iter == flush_req.cfd_to_max_mem_id_to_persist.end()
|
||||
? nullptr
|
||||
: &(iter->second)));
|
||||
}
|
||||
#endif // NDEBUG
|
||||
s = WaitForFlushMemTables(
|
||||
cfds, flush_memtable_ids,
|
||||
flush_reason == FlushReason::kErrorRecovery /* resuming_from_bg_err */,
|
||||
@@ -3205,29 +3072,9 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
|
||||
flush_req.atomic_flush = true;
|
||||
GenerateFlushRequest(cfds, flush_reason, &flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
// Keep flush_memtable_ids aligned with cfds by walking cfds, since the
|
||||
// request map's order is unrelated to cfds. A CF not in the map waits on
|
||||
// its whole imm list, matching the max-bound the non-atomic branch uses.
|
||||
for (auto* cfd : cfds) {
|
||||
auto iter = flush_req.cfd_to_max_mem_id_to_persist.find(cfd);
|
||||
flush_memtable_ids.push_back(
|
||||
iter == flush_req.cfd_to_max_mem_id_to_persist.end()
|
||||
? std::numeric_limits<uint64_t>::max()
|
||||
: iter->second);
|
||||
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
flush_memtable_ids.push_back(iter.second);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
// WaitForFlushMemTables pairs flush_memtable_ids[i] with cfds[i], so each
|
||||
// entry must be this cfd's own max id. Fires if the ids are ever rebuilt in
|
||||
// the request map's order, which is unrelated to cfds order.
|
||||
assert(flush_memtable_ids.size() == cfds.size());
|
||||
for (size_t i = 0; i < cfds.size(); ++i) {
|
||||
auto iter = flush_req.cfd_to_max_mem_id_to_persist.find(cfds[i]);
|
||||
assert(flush_memtable_ids[i] ==
|
||||
(iter == flush_req.cfd_to_max_mem_id_to_persist.end()
|
||||
? std::numeric_limits<uint64_t>::max()
|
||||
: iter->second));
|
||||
}
|
||||
#endif // NDEBUG
|
||||
} else {
|
||||
for (auto cfd : cfds) {
|
||||
flush_memtable_ids.push_back(
|
||||
@@ -3988,34 +3835,11 @@ bool DBImpl::EnqueuePendingFlush(const FlushRequest& flush_req) {
|
||||
enqueued = true;
|
||||
}
|
||||
} else {
|
||||
if (flush_req.flush_reason == FlushReason::kWriteBufferManager &&
|
||||
write_buffer_manager_->ShouldTrackFlushInitiator()) {
|
||||
for (const FlushRequest& queued : flush_queue_) {
|
||||
if (!queued.atomic_flush) {
|
||||
continue;
|
||||
}
|
||||
bool covered = true;
|
||||
for (const auto& requested : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
const auto queued_entry =
|
||||
queued.cfd_to_max_mem_id_to_persist.find(requested.first);
|
||||
if (queued_entry == queued.cfd_to_max_mem_id_to_persist.end() ||
|
||||
queued_entry->second < requested.second) {
|
||||
covered = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (covered) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::EnqueuePendingFlush:AtomicWBMRequestDeduplicated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Atomic flush requests normally bypass the per-CF queued_for_flush()
|
||||
// guard. Cross-DB WBM requests are deduplicated above because its
|
||||
// autonomous retry loop must not repeatedly enqueue the same atomic cut
|
||||
// while HIGH workers are unavailable. Other atomic requests retain their
|
||||
// existing behavior.
|
||||
// Atomic flush requests bypass the queued_for_flush() deduplication guard.
|
||||
// This means an atomic request and a non-atomic request for the same CF
|
||||
// can coexist in the flush queue. This is safe because PickMemtablesToFlush
|
||||
// uses flush_in_progress_ to prevent double-picking of memtables, and
|
||||
// BackgroundFlush filters out CFs where !IsFlushPending().
|
||||
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
cfd->Ref();
|
||||
@@ -4140,178 +3964,6 @@ void DBImpl::UnscheduleFlushCallback(void* arg) {
|
||||
TEST_SYNC_POINT("DBImpl::UnscheduleFlushCallback");
|
||||
}
|
||||
|
||||
void DBImpl::RefreshFlushableMemAccounting() {
|
||||
mutex_.AssertHeld();
|
||||
versions_->GetColumnFamilySet()->RefreshFlushableMemAccounting();
|
||||
}
|
||||
|
||||
bool DBImpl::TryRefreshFlushableMemAccounting() {
|
||||
if (!mutex_.TryLock()) {
|
||||
return false;
|
||||
}
|
||||
Defer unlock_mutex([this] { mutex_.Unlock(); });
|
||||
RefreshFlushableMemAccounting();
|
||||
return wbm_flush_initiator_ != nullptr &&
|
||||
wbm_flush_initiator_->HasAccurateFlushableMemUsage();
|
||||
}
|
||||
|
||||
DBImpl::FlushableCFs DBImpl::CollectFlushableCFs(
|
||||
bool include_waiting_immutable) {
|
||||
mutex_.AssertHeld();
|
||||
FlushableCFs result;
|
||||
SequenceNumber oldest_seq = kMaxSequenceNumber;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped() || !cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
const size_t mutable_mem =
|
||||
cfd->mem()->IsEmpty() ? 0 : cfd->mem()->WBMTrackedMemoryUsage();
|
||||
size_t waiting_immutable_mem = 0;
|
||||
if (include_waiting_immutable) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
waiting_immutable_mem =
|
||||
cfd->imm()->WBMTrackedUnstartedMemTablesMemoryUsage();
|
||||
} else if (!cfd->imm()->IsFlushPendingOrRunning()) {
|
||||
waiting_immutable_mem =
|
||||
cfd->imm()->WBMTrackedUnflushedMemTablesMemoryUsage();
|
||||
}
|
||||
}
|
||||
result.total_mem += mutable_mem + waiting_immutable_mem;
|
||||
// Non-atomic flushes skip CFs already flushing.
|
||||
if (cfd->imm()->IsFlushPendingOrRunning()) {
|
||||
continue;
|
||||
}
|
||||
size_t mem = mutable_mem;
|
||||
if (include_waiting_immutable) {
|
||||
mem += waiting_immutable_mem;
|
||||
}
|
||||
if (mem == 0) {
|
||||
continue;
|
||||
}
|
||||
if (result.largest == nullptr || mem > result.largest_mem) {
|
||||
result.largest = cfd;
|
||||
result.largest_mem = mem;
|
||||
}
|
||||
const SequenceNumber seq = cfd->mem()->GetCreationSeq();
|
||||
if (result.oldest == nullptr || seq < oldest_seq) {
|
||||
result.oldest = cfd;
|
||||
oldest_seq = seq;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DBImpl::ScheduleWriteBufferManagerFlush() {
|
||||
{
|
||||
if (!mutex_.TryLock()) {
|
||||
return false;
|
||||
}
|
||||
Defer unlock_mutex([this] { mutex_.Unlock(); });
|
||||
// Recheck bid eligibility because DB state may have changed.
|
||||
if (!write_buffer_manager_->ShouldTrackFlushInitiator() ||
|
||||
shutdown_initiated_.load(std::memory_order_acquire) ||
|
||||
shutting_down_.load(std::memory_order_acquire) ||
|
||||
reject_new_background_jobs_ || !opened_successfully_ || read_only_ ||
|
||||
error_handler_.IsBGWorkStopped() || write_controller_.IsStopped() ||
|
||||
// Reject both the pause drain and fully paused states.
|
||||
bg_work_paused_ > 0 || bg_compaction_paused_ > 0 ||
|
||||
// Never queue work that would wait behind this DB's stall.
|
||||
WouldBlockJoiningWriteThread()) {
|
||||
return false;
|
||||
}
|
||||
// A queued or running job does not count as new progress. Returning false
|
||||
// lets the coordinator try another DB instead of queuing more work behind
|
||||
// a busy LOW pool.
|
||||
if (bg_wbm_flush_scheduled_ > 0) {
|
||||
return false;
|
||||
}
|
||||
const FlushableCFs flushable =
|
||||
CollectFlushableCFs(true /* include_waiting_immutable */);
|
||||
const bool has_flushable_cf = immutable_db_options_.atomic_flush
|
||||
? flushable.total_mem > 0
|
||||
: flushable.largest != nullptr;
|
||||
if (!has_flushable_cf) {
|
||||
wbm_flush_initiator_->SetHasFlushableCF(false);
|
||||
write_buffer_manager_->NotifyFlushInitiatorChanged();
|
||||
return false;
|
||||
}
|
||||
wbm_flush_initiator_->SetHasFlushableCF(false);
|
||||
write_buffer_manager_->NotifyFlushInitiatorChanged();
|
||||
++bg_wbm_flush_scheduled_;
|
||||
}
|
||||
|
||||
// The WBM-owned executor provides bounded capacity independent of Env::LOW.
|
||||
// Run outside mutex_ so DB-local work cannot block other DB mutex users.
|
||||
BackgroundCallWBMFlush();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DBImpl::BackgroundCallWBMFlush() {
|
||||
const bool atomic_flush = immutable_db_options_.atomic_flush;
|
||||
ColumnFamilyData* cfd_to_flush = nullptr;
|
||||
bool has_flushable_cf = false;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (!shutdown_initiated_.load(std::memory_order_acquire) &&
|
||||
!shutting_down_.load(std::memory_order_acquire) &&
|
||||
!reject_new_background_jobs_ && opened_successfully_ && !read_only_ &&
|
||||
!error_handler_.IsBGWorkStopped() && !write_controller_.IsStopped() &&
|
||||
bg_work_paused_ == 0 && bg_compaction_paused_ == 0 &&
|
||||
!WouldBlockJoiningWriteThread()) {
|
||||
const FlushableCFs flushable =
|
||||
CollectFlushableCFs(true /* include_waiting_immutable */);
|
||||
has_flushable_cf =
|
||||
atomic_flush ? flushable.total_mem > 0 : flushable.largest != nullptr;
|
||||
if (!atomic_flush) {
|
||||
cfd_to_flush = flushable.largest;
|
||||
if (cfd_to_flush != nullptr) {
|
||||
cfd_to_flush->Ref();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallWBMFlush:AfterPick");
|
||||
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = false;
|
||||
flush_options.allow_write_stall = true;
|
||||
bool made_progress = false;
|
||||
if (has_flushable_cf) {
|
||||
Status s;
|
||||
if (atomic_flush) {
|
||||
s = AtomicFlushMemTablesImpl(
|
||||
flush_options, FlushReason::kWriteBufferManager,
|
||||
{} /* provided_candidate_cfds */, false /* entered_write_thread */,
|
||||
WriteThreadJoinMode::kNonBlocking, &made_progress);
|
||||
} else {
|
||||
s = FlushMemTableImpl(cfd_to_flush, flush_options,
|
||||
FlushReason::kWriteBufferManager,
|
||||
false /* entered_write_thread */,
|
||||
WriteThreadJoinMode::kNonBlocking, &made_progress);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Flush on behalf of a shared WriteBufferManager failed: %s",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCallWBMFlush:MadeProgress",
|
||||
&made_progress);
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (cfd_to_flush != nullptr) {
|
||||
cfd_to_flush->UnrefAndTryDelete();
|
||||
}
|
||||
RefreshFlushableMemAccounting();
|
||||
--bg_wbm_flush_scheduled_;
|
||||
write_buffer_manager_->NotifyFlushInitiatorFlushCompleted(
|
||||
wbm_flush_initiator_.get(), made_progress);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BGWorkWBMFlush:done", this);
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
|
||||
Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
LogBuffer* log_buffer, FlushReason* reason,
|
||||
bool* flush_rescheduled_to_retain_udt,
|
||||
@@ -4542,9 +4194,6 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
assert(num_running_flushes_ > 0);
|
||||
num_running_flushes_--;
|
||||
bg_flush_scheduled_--;
|
||||
if (write_buffer_manager_->ShouldTrackFlushInitiator()) {
|
||||
RefreshFlushableMemAccounting();
|
||||
}
|
||||
// See if there's more work to be done
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
@@ -5203,7 +4852,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
false /* marked_for_compact */,
|
||||
c->GetOutputTemperature() /* temperature */,
|
||||
in_file->oldest_blob_file_number,
|
||||
in_file->oldest_ancestor_time,
|
||||
in_file->oldest_ancester_time,
|
||||
out_file_creation_time,
|
||||
c->MinInputFileEpochNumber(),
|
||||
dest_writer->GetFileChecksum(),
|
||||
@@ -6009,47 +5658,15 @@ Status DBImpl::WaitForCompact(
|
||||
const auto deadline = immutable_db_options_.clock->NowMicros() +
|
||||
wait_for_compact_options.timeout.count();
|
||||
for (;;) {
|
||||
// The coordinator reserves this DB before invoking its non-blocking
|
||||
// scheduling callback. Drain that DB-specific reservation without holding
|
||||
// mutex_; successful callbacks publish bg_wbm_flush_scheduled_ before
|
||||
// releasing the reservation.
|
||||
if (write_buffer_manager_ != nullptr &&
|
||||
write_buffer_manager_->ShouldTrackFlushInitiator() &&
|
||||
wbm_flush_initiator_ != nullptr) {
|
||||
// Fence all coordinator requests visible before this local quiescence
|
||||
// check. Candidate retrieval either observes this generation and skips
|
||||
// the DB, or atomically publishes a handoff reservation first.
|
||||
write_buffer_manager_->FenceFlushInitiator(wbm_flush_initiator_.get());
|
||||
}
|
||||
if (wbm_flush_initiator_ != nullptr &&
|
||||
wbm_flush_initiator_->HasPendingFlushHandoff()) {
|
||||
std::chrono::microseconds handoff_timeout{0};
|
||||
if (wait_for_compact_options.timeout.count() != 0) {
|
||||
const uint64_t now = immutable_db_options_.clock->NowMicros();
|
||||
if (now >= deadline) {
|
||||
return Status::TimedOut();
|
||||
}
|
||||
handoff_timeout = std::chrono::microseconds(deadline - now);
|
||||
}
|
||||
mutex_.Unlock();
|
||||
const bool handoff_finished =
|
||||
wbm_flush_initiator_->WaitForPendingFlushHandoff(handoff_timeout);
|
||||
mutex_.Lock();
|
||||
if (!handoff_finished) {
|
||||
return Status::TimedOut();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
if (bg_work_paused_ && wait_for_compact_options.abort_on_pause) {
|
||||
return Status::Aborted();
|
||||
}
|
||||
// A WBM job can enqueue a regular flush, so wait for both to quiesce.
|
||||
if ((bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ || bg_wbm_flush_scheduled_ ||
|
||||
unscheduled_compactions_ || !parked_compaction_cfds_.empty() ||
|
||||
bg_flush_scheduled_ || unscheduled_compactions_ ||
|
||||
!parked_compaction_cfds_.empty() ||
|
||||
(wait_for_compact_options.wait_for_purge && bg_purge_scheduled_) ||
|
||||
unscheduled_flushes_ || error_handler_.IsRecoveryInProgress()) &&
|
||||
(error_handler_.GetBGError().ok())) {
|
||||
|
||||
@@ -144,18 +144,6 @@ Status DBImpl::TEST_SwitchMemtable(ColumnFamilyData* cfd) {
|
||||
return s;
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_GetWBMFlushableMemUsage() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
assert(wbm_flush_initiator_ != nullptr);
|
||||
RefreshFlushableMemAccounting();
|
||||
return wbm_flush_initiator_->GetFlushableMemUsage();
|
||||
}
|
||||
|
||||
bool DBImpl::TEST_IsWBMFlushInitiatorFlushable() const {
|
||||
assert(wbm_flush_initiator_ != nullptr);
|
||||
return wbm_flush_initiator_->IsFlushable();
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_FlushMemTable(bool wait, bool allow_write_stall,
|
||||
ColumnFamilyHandle* cfh) {
|
||||
FlushOptions fo;
|
||||
@@ -194,31 +182,9 @@ Status DBImpl::TEST_FlushMemTableWithListenerWait(bool allow_write_stall,
|
||||
|
||||
Status DBImpl::TEST_AtomicFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& provided_candidate_cfds,
|
||||
const FlushOptions& flush_opts, bool non_blocking_write_thread) {
|
||||
const auto join_mode = non_blocking_write_thread
|
||||
? WriteThreadJoinMode::kNonBlocking
|
||||
: WriteThreadJoinMode::kBlocking;
|
||||
return AtomicFlushMemTablesImpl(
|
||||
flush_opts, FlushReason::kTest, provided_candidate_cfds,
|
||||
false /* entered_write_thread */, join_mode, nullptr /* made_progress */);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_BeginWriteStall() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
write_thread_.BeginWriteStall();
|
||||
if (wbm_flush_initiator_ != nullptr &&
|
||||
write_buffer_manager_->ShouldTrackFlushInitiator()) {
|
||||
wbm_flush_initiator_->SetFlushable(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::TEST_EndWriteStall() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
write_thread_.EndWriteStall();
|
||||
if (wbm_flush_initiator_ != nullptr &&
|
||||
write_buffer_manager_->ShouldTrackFlushInitiator()) {
|
||||
wbm_flush_initiator_->SetFlushable(true);
|
||||
}
|
||||
const FlushOptions& flush_opts) {
|
||||
return AtomicFlushMemTables(flush_opts, FlushReason::kTest,
|
||||
provided_candidate_cfds);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForBackgroundWork() {
|
||||
|
||||
@@ -24,8 +24,7 @@ Status DBImpl::SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
InternalKey start_key;
|
||||
InternalKey end_key;
|
||||
InternalKey start_key, end_key;
|
||||
if (begin != nullptr) {
|
||||
start_key.SetMinPossibleForUserKey(*begin);
|
||||
}
|
||||
@@ -141,7 +140,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction, f->temperature,
|
||||
f->oldest_blob_file_number, f->oldest_ancestor_time,
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number, f->file_checksum,
|
||||
f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
|
||||
@@ -244,14 +244,9 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// Since job_context->min_pending_output is set, until file scan finishes,
|
||||
// mutex_ cannot be released. Otherwise, we might see no min_pending_output
|
||||
// here but later find newer generated unfinalized files while scanning.
|
||||
assert(job_context != nullptr);
|
||||
job_context->min_pending_output = MinObsoleteSstNumberToKeep();
|
||||
job_context->files_to_quarantine = error_handler_.GetFilesToQuarantine();
|
||||
job_context->min_options_file_number = MinOptionsFileNumberToKeep();
|
||||
job_context->manifest_options_file_number =
|
||||
versions_->has_committed_options_file_number()
|
||||
? versions_->options_file_number()
|
||||
: 0;
|
||||
job_context->min_blob_file_number_to_keep =
|
||||
versions_->current_next_file_number();
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -735,8 +730,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
}
|
||||
break;
|
||||
case kOptionsFile:
|
||||
keep = (number >= optsfile_num2) ||
|
||||
(number == state.manifest_options_file_number);
|
||||
keep = (number >= optsfile_num2);
|
||||
break;
|
||||
case kCompactionProgressFile:
|
||||
// Keep compaction progress files - they are managed
|
||||
@@ -1208,9 +1202,6 @@ Status DBImpl::MaybeUpdateNextFileNumber(RecoveryContext* recovery_ctx) {
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
if (path == NormalizePath(dbname_)) {
|
||||
recovery_ctx->existing_db_files_ = files;
|
||||
}
|
||||
for (const auto& fname : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type;
|
||||
|
||||
Loaded 100 of 621 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user