mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e00130da2 | ||
|
|
6357d00172 | ||
|
|
1b759e3f19 | ||
|
|
5c70943b68 | ||
|
|
6cf49ba1eb | ||
|
|
a63980ee5c | ||
|
|
fe8d347217 | ||
|
|
4c464fa6fa | ||
|
|
444b3f4845 | ||
|
|
23bec22a81 | ||
|
|
208310ad99 | ||
|
|
afa92034d9 | ||
|
|
a5e9481295 | ||
|
|
742035d21f | ||
|
|
62f3e58936 | ||
|
|
be728f83e8 | ||
|
|
3ff86f39ae | ||
|
|
ab9741a6a9 | ||
|
|
9399bb844b | ||
|
|
ed91ed0a50 | ||
|
|
3ff6da6bd5 | ||
|
|
e079d562af | ||
|
|
097f9f4425 | ||
|
|
3182beeffc | ||
|
|
a8a4ed52a4 | ||
|
|
8c0f5b1fcf | ||
|
|
8fa8780932 | ||
|
|
32520df1d9 | ||
|
|
b55e70357c | ||
|
|
b644baa1eb | ||
|
|
1562524e63 | ||
|
|
0993c9225f | ||
|
|
8515437594 | ||
|
|
ecba6a320e | ||
|
|
f321e8fc98 | ||
|
|
5e8947057b | ||
|
|
d1aca4a5ae | ||
|
|
dbc4101b89 | ||
|
|
9460d4b77e | ||
|
|
2ea109521f | ||
|
|
c62f322169 | ||
|
|
55d58d91e7 | ||
|
|
b8de2291ad | ||
|
|
fbd9077d66 | ||
|
|
75aca74017 | ||
|
|
aa0a11e1b9 | ||
|
|
aafe7bd376 | ||
|
|
18cb731f27 | ||
|
|
941d834739 | ||
|
|
cc8c8f6958 | ||
|
|
0d5dc5fdb9 | ||
|
|
0547cecb81 | ||
|
|
d80baa1396 | ||
|
|
781a387488 | ||
|
|
4a6906e28c | ||
|
|
5cf6ab6f31 | ||
|
|
ff9ad2c39b | ||
|
|
7d26e4c5a3 | ||
|
|
9f3475eccf | ||
|
|
7f5e438aee | ||
|
|
ea1982d010 | ||
|
|
d989300ad1 | ||
|
|
9079895aae | ||
|
|
36f5e19e33 | ||
|
|
900f79126d | ||
|
|
08a63ad10b | ||
|
|
7867a1112b | ||
|
|
1e6f1ef894 | ||
|
|
bf497e91ad | ||
|
|
22ff8c5af7 | ||
|
|
56715350d9 | ||
|
|
3e686c7cbe | ||
|
|
5fef34fd3a | ||
|
|
5d3953114f | ||
|
|
17553bdd5e | ||
|
|
bf78380851 | ||
|
|
b36ec37a4b | ||
|
|
a1a1dc6659 | ||
|
|
7fff38b1fe | ||
|
|
5f915b447d | ||
|
|
d484275230 | ||
|
|
48fe921754 | ||
|
|
95a1935cb1 | ||
|
|
84563a2701 | ||
|
|
727bad78b8 | ||
|
|
7a95938899 | ||
|
|
fc74abb436 | ||
|
|
2a551976f4 | ||
|
|
de34e7196f | ||
|
|
aca00006bf | ||
|
|
671753c43d | ||
|
|
935aae3bcf | ||
|
|
ee3dbdc083 | ||
|
|
0ed1a800ed | ||
|
|
7cf27eae0a | ||
|
|
4d9cb433fa | ||
|
|
966cd42c7d | ||
|
|
bb5ac1b524 | ||
|
|
b0d9776b70 | ||
|
|
deb6a24be2 | ||
|
|
daceb85c51 |
No files matched your search
+61
-79
@@ -1,7 +1,7 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
win: circleci/windows@2.4.0
|
||||
win: circleci/windows@5.0.0
|
||||
|
||||
commands:
|
||||
install-cmake-on-macos:
|
||||
@@ -45,6 +45,38 @@ commands:
|
||||
echo "export LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> $BASH_ENV
|
||||
echo "export ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> $BASH_ENV
|
||||
|
||||
windows-build-steps:
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Install thirdparty dependencies"
|
||||
command: |
|
||||
echo "Installing CMake..."
|
||||
choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y
|
||||
mkdir $Env:THIRDPARTY_HOME
|
||||
cd $Env:THIRDPARTY_HOME
|
||||
echo "Building Snappy dependency..."
|
||||
curl https://github.com/google/snappy/archive/refs/tags/1.1.8.zip -O snappy-1.1.8.zip
|
||||
unzip -q snappy-1.1.8.zip
|
||||
cd snappy-1.1.8
|
||||
mkdir build
|
||||
cd build
|
||||
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" ..
|
||||
msbuild.exe Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Build RocksDB"
|
||||
command: |
|
||||
mkdir build
|
||||
cd build
|
||||
& $Env:CMAKE_BIN -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DJNI=1 ..
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Test RocksDB"
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test,db_test2,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test -Concurrency 16
|
||||
pre-steps-macos:
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -154,11 +186,6 @@ commands:
|
||||
true
|
||||
|
||||
executors:
|
||||
windows-2xlarge:
|
||||
machine:
|
||||
image: 'windows-server-2019-vs2019:stable'
|
||||
resource_class: windows.2xlarge
|
||||
shell: bash.exe
|
||||
linux-docker:
|
||||
docker:
|
||||
# The image configuration is build_tools/ubuntu20_image/Dockerfile
|
||||
@@ -518,73 +545,35 @@ jobs:
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
build-windows:
|
||||
executor: windows-2xlarge
|
||||
parameters:
|
||||
extra_cmake_opt:
|
||||
default: ""
|
||||
type: string
|
||||
vs_year:
|
||||
default: "2019"
|
||||
type: string
|
||||
cmake_generator:
|
||||
default: "Visual Studio 16 2019"
|
||||
type: string
|
||||
build-windows-vs2022:
|
||||
executor:
|
||||
name: win/server-2022
|
||||
size: 2xlarge
|
||||
environment:
|
||||
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
|
||||
CMAKE_HOME: C:/Users/circleci/thirdparty/cmake-3.16.4-win64-x64
|
||||
CMAKE_BIN: C:/Users/circleci/thirdparty/cmake-3.16.4-win64-x64/bin/cmake.exe
|
||||
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.7
|
||||
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.7;C:/Users/circleci/thirdparty/snappy-1.1.7/build
|
||||
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.7/build/Debug/snappy.lib
|
||||
VS_YEAR: <<parameters.vs_year>>
|
||||
CMAKE_GENERATOR: <<parameters.cmake_generator>>
|
||||
CMAKE_HOME: C:/Program Files/CMake
|
||||
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
|
||||
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.8
|
||||
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Setup VS"
|
||||
command: |
|
||||
if [[ "${VS_YEAR}" == "2019" ]]; then
|
||||
echo "VS2019 already present."
|
||||
elif [[ "${VS_YEAR}" == "2017" ]]; then
|
||||
echo "Installing VS2017..."
|
||||
powershell .circleci/vs2017_install.ps1
|
||||
elif [[ "${VS_YEAR}" == "2015" ]]; then
|
||||
echo "Installing VS2015..."
|
||||
powershell .circleci/vs2015_install.ps1
|
||||
fi
|
||||
- store_artifacts:
|
||||
path: \Users\circleci\AppData\Local\Temp\vslogs.zip
|
||||
- run:
|
||||
name: "Install thirdparty dependencies"
|
||||
command: |
|
||||
mkdir ${THIRDPARTY_HOME}
|
||||
cd ${THIRDPARTY_HOME}
|
||||
echo "Installing CMake..."
|
||||
curl --fail --silent --show-error --output cmake-3.16.4-win64-x64.zip --location https://github.com/Kitware/CMake/releases/download/v3.16.4/cmake-3.16.4-win64-x64.zip
|
||||
unzip -q cmake-3.16.4-win64-x64.zip
|
||||
echo "Building Snappy dependency..."
|
||||
curl --fail --silent --show-error --output snappy-1.1.7.zip --location https://github.com/google/snappy/archive/1.1.7.zip
|
||||
unzip -q snappy-1.1.7.zip
|
||||
cd snappy-1.1.7
|
||||
mkdir build
|
||||
cd build
|
||||
${CMAKE_BIN} -G "${CMAKE_GENERATOR}" ..
|
||||
msbuild.exe Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Build RocksDB"
|
||||
command: |
|
||||
mkdir build
|
||||
cd build
|
||||
${CMAKE_BIN} -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DJNI=1 << parameters.extra_cmake_opt >> ..
|
||||
cd ..
|
||||
echo "Building with VS version: ${CMAKE_GENERATOR}"
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Test RocksDB"
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test,db_test2,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test -Concurrency 16
|
||||
- windows-build-steps
|
||||
|
||||
build-windows-vs2019:
|
||||
executor:
|
||||
name: win/server-2019
|
||||
size: 2xlarge
|
||||
environment:
|
||||
THIRDPARTY_HOME: C:/Users/circleci/thirdparty
|
||||
CMAKE_HOME: C:/Program Files/CMake
|
||||
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
|
||||
SNAPPY_HOME: C:/Users/circleci/thirdparty/snappy-1.1.8
|
||||
SNAPPY_INCLUDE: C:/Users/circleci/thirdparty/snappy-1.1.8;C:/Users/circleci/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: C:/Users/circleci/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
steps:
|
||||
- windows-build-steps
|
||||
|
||||
build-linux-java:
|
||||
executor: linux-docker
|
||||
@@ -859,15 +848,8 @@ workflows:
|
||||
- build-linux-mini-crashtest
|
||||
jobs-windows:
|
||||
jobs:
|
||||
- build-windows:
|
||||
name: "build-windows-vs2019"
|
||||
- build-windows:
|
||||
name: "build-windows-vs2019-cxx20"
|
||||
extra_cmake_opt: -DCMAKE_CXX_STANDARD=20
|
||||
- build-windows:
|
||||
name: "build-windows-vs2017"
|
||||
vs_year: "2017"
|
||||
cmake_generator: "Visual Studio 15 Win64"
|
||||
- build-windows-vs2022
|
||||
- build-windows-vs2019
|
||||
- build-cmake-mingw
|
||||
jobs-java:
|
||||
jobs:
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
$VS_DOWNLOAD_LINK = "https://go.microsoft.com/fwlink/?LinkId=691126"
|
||||
$COLLECT_DOWNLOAD_LINK = "https://aka.ms/vscollect.exe"
|
||||
curl.exe --retry 3 -kL $VS_DOWNLOAD_LINK --output vs_installer.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS 2015 installer failed"
|
||||
exit 1
|
||||
}
|
||||
$VS_INSTALL_ARGS = @("/Quiet", "/NoRestart")
|
||||
$process = Start-Process "${PWD}\vs_installer.exe" -ArgumentList $VS_INSTALL_ARGS -NoNewWindow -Wait -PassThru
|
||||
Remove-Item -Path vs_installer.exe -Force
|
||||
$exitCode = $process.ExitCode
|
||||
if (($exitCode -ne 0) -and ($exitCode -ne 3010)) {
|
||||
echo "VS 2015 installer exited with code $exitCode, which should be one of [0, 3010]."
|
||||
curl.exe --retry 3 -kL $COLLECT_DOWNLOAD_LINK --output Collect.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS Collect tool failed."
|
||||
exit 1
|
||||
}
|
||||
Start-Process "${PWD}\Collect.exe" -NoNewWindow -Wait -PassThru
|
||||
New-Item -Path "C:\w\build-results" -ItemType "directory" -Force
|
||||
Copy-Item -Path "C:\Users\circleci\AppData\Local\Temp\vslogs.zip" -Destination "C:\w\build-results\"
|
||||
exit 1
|
||||
}
|
||||
echo "VS 2015 installed."
|
||||
@@ -1,35 +0,0 @@
|
||||
$VS_DOWNLOAD_LINK = "https://aka.ms/vs/15/release/vs_buildtools.exe"
|
||||
$COLLECT_DOWNLOAD_LINK = "https://aka.ms/vscollect.exe"
|
||||
$VS_INSTALL_ARGS = @("--nocache","--quiet","--wait", "--add Microsoft.VisualStudio.Workload.VCTools",
|
||||
"--add Microsoft.VisualStudio.Component.VC.Tools.14.13",
|
||||
"--add Microsoft.Component.MSBuild",
|
||||
"--add Microsoft.VisualStudio.Component.Roslyn.Compiler",
|
||||
"--add Microsoft.VisualStudio.Component.TextTemplating",
|
||||
"--add Microsoft.VisualStudio.Component.VC.CoreIde",
|
||||
"--add Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
|
||||
"--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
|
||||
"--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Win81")
|
||||
|
||||
curl.exe --retry 3 -kL $VS_DOWNLOAD_LINK --output vs_installer.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS 2017 installer failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$process = Start-Process "${PWD}\vs_installer.exe" -ArgumentList $VS_INSTALL_ARGS -NoNewWindow -Wait -PassThru
|
||||
Remove-Item -Path vs_installer.exe -Force
|
||||
$exitCode = $process.ExitCode
|
||||
if (($exitCode -ne 0) -and ($exitCode -ne 3010)) {
|
||||
echo "VS 2017 installer exited with code $exitCode, which should be one of [0, 3010]."
|
||||
curl.exe --retry 3 -kL $COLLECT_DOWNLOAD_LINK --output Collect.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS Collect tool failed."
|
||||
exit 1
|
||||
}
|
||||
Start-Process "${PWD}\Collect.exe" -NoNewWindow -Wait -PassThru
|
||||
New-Item -Path "C:\w\build-results" -ItemType "directory" -Force
|
||||
Copy-Item -Path "C:\Users\circleci\AppData\Local\Temp\vslogs.zip" -Destination "C:\w\build-results\"
|
||||
exit 1
|
||||
}
|
||||
echo "VS 2017 installed."
|
||||
+11
-11
@@ -250,6 +250,8 @@ option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX "force building with AVX, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX2 "force building with AVX2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
add_definitions(-DROCKSDB_PORTABLE)
|
||||
|
||||
# MSVC does not need a separate compiler flag to enable SSE4.2; if nmmintrin.h
|
||||
# is available, it is available by default.
|
||||
if(FORCE_SSE42 AND NOT MSVC)
|
||||
@@ -292,8 +294,7 @@ if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
if (NOT PORTABLE OR FORCE_SSE42)
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
@@ -305,12 +306,11 @@ int main() {
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
|
||||
# Check if -latomic is required or not
|
||||
@@ -653,8 +653,8 @@ set(SOURCES
|
||||
cache/charged_cache.cc
|
||||
cache/clock_cache.cc
|
||||
cache/compressed_secondary_cache.cc
|
||||
cache/fast_lru_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/secondary_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_contents.cc
|
||||
@@ -1572,9 +1572,9 @@ endif()
|
||||
|
||||
option(WITH_TRACE_TOOLS "build with trace tools" ON)
|
||||
if(WITH_TRACE_TOOLS)
|
||||
add_executable(block_cache_trace_analyzer_tool${ARTIFACT_SUFFIX}
|
||||
add_executable(block_cache_trace_analyzer${ARTIFACT_SUFFIX}
|
||||
tools/block_cache_analyzer/block_cache_trace_analyzer_tool.cc)
|
||||
target_link_libraries(block_cache_trace_analyzer_tool${ARTIFACT_SUFFIX}
|
||||
target_link_libraries(block_cache_trace_analyzer${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB} ${FOLLY_LIBS})
|
||||
|
||||
add_executable(trace_analyzer${ARTIFACT_SUFFIX}
|
||||
|
||||
+38
-10
@@ -1,20 +1,47 @@
|
||||
# Rocksdb Change Log
|
||||
## 7.8.3 (11/29/2022)
|
||||
* Revert an internal change in 7.8.0 associated with some memory usage churn.
|
||||
## Unreleased
|
||||
### Bug Fixes
|
||||
* Fixed a bug in DB open/recovery from a compressed WAL that was caused due to incorrect handling of certain record fragments with the same offset within a WAL block.
|
||||
|
||||
## 7.9.3 (02/01/2023)
|
||||
### Bug Fixes
|
||||
* Fixed a data race on `ColumnFamilyData::flush_reason` caused by concurrent flushes.
|
||||
* Fixed `DisableManualCompaction()` and `CompactRangeOptions::canceled` to cancel compactions even when they are waiting on conflicting compactions to finish
|
||||
|
||||
## 7.9.2 (12/21/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a heap use after free bug in async scan prefetching when the scan thread and another thread try to read and load the same seek block into cache.
|
||||
|
||||
## 7.9.1 (12/8/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a regression in iterator where range tombstones after `iterate_upper_bound` is processed.
|
||||
* Fixed a memory leak in MultiGet with async_io read option, caused by IO errors during table file open
|
||||
|
||||
## 7.8.2 (11/27/2022)
|
||||
### Behavior changes
|
||||
* Make best-efforts recovery verify SST unique ID before Version construction (#10962)
|
||||
* Fix failed memtable flush retry bug that could cause wrongly ordered updates, which would surface to writers as `Status::Corruption` in case of `force_consistency_checks=true` (default). It affects use cases that enable both parallel flush (`max_background_flushes > 1` or `max_background_jobs >= 8`) and non-default memtable count (`max_write_buffer_number > 2`).
|
||||
* Tiered Storage: fixed excessive keys written to penultimate level in non-debug builds.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a regression in scan for async_io. During seek, valid buffers were getting cleared causing a regression.
|
||||
* Fixed a performance regression in iterator where range tombstones after `iterate_upper_bound` is processed.
|
||||
## 7.9.0 (11/21/2022)
|
||||
### Performance Improvements
|
||||
* Fixed an iterator performance regression for delete range users when scanning through a consecutive sequence of range tombstones (#10877).
|
||||
|
||||
## 7.8.1 (11/2/2022)
|
||||
### Bug Fixes
|
||||
* Fix memory corruption error in scans if async_io is enabled. Memory corruption happened if there is IOError while reading the data leading to empty buffer and other buffer already in progress of async read goes again for reading.
|
||||
* Fix failed memtable flush retry bug that could cause wrongly ordered updates, which would surface to writers as `Status::Corruption` in case of `force_consistency_checks=true` (default). It affects use cases that enable both parallel flush (`max_background_flushes > 1` or `max_background_jobs >= 8`) and non-default memtable count (`max_write_buffer_number > 2`).
|
||||
* Fixed an issue where the `READ_NUM_MERGE_OPERANDS` ticker was not updated when the base key-value or tombstone was read from an SST file.
|
||||
* Fixed a memory safety bug when using a SecondaryCache with `block_cache_compressed`. `block_cache_compressed` no longer attempts to use SecondaryCache features.
|
||||
* Fixed a regression in scan for async_io. During seek, valid buffers were getting cleared causing a regression.
|
||||
* Tiered Storage: fixed excessive keys written to penultimate level in non-debug builds.
|
||||
|
||||
### New Features
|
||||
* Add basic support for user-defined timestamp to Merge (#10819).
|
||||
* Add stats for ReadAsync time spent and async read errors.
|
||||
* Basic support for the wide-column data model is now available. Wide-column entities can be stored using the `PutEntity` API, and retrieved using `GetEntity` and the new `columns` API of iterator. For compatibility, the classic APIs `Get` and `MultiGet`, as well as iterator's `value` API return the value of the anonymous default column of wide-column entities; also, `GetEntity` and iterator's `columns` return any plain key-values in the form of an entity which only has the anonymous default column. `Merge` (and `GetMergeOperands`) currently also apply to the default column; any other columns of entities are unaffected by `Merge` operations. Note that some features like compaction filters, transactions, user-defined timestamps, and the SST file writer do not yet support wide-column entities; also, there is currently no `MultiGet`-like API to retrieve multiple entities at once. We plan to gradually close the above gaps and also implement new features like column-level operations (e.g. updating or querying only certain columns of an entity).
|
||||
* Marked HyperClockCache as a production-ready alternative to LRUCache for the block cache. HyperClockCache greatly improves hot-path CPU efficiency under high parallel load or high contention, with some documented caveats and limitations. As much as 4.5x higher ops/sec vs. LRUCache has been seen in db_bench under high parallel load.
|
||||
* Add periodic diagnostics to info_log (LOG file) for HyperClockCache block cache if performance is degraded by bad `estimated_entry_charge` option.
|
||||
|
||||
### Public API Changes
|
||||
* Marked `block_cache_compressed` as a deprecated feature. Use SecondaryCache instead.
|
||||
* Added a `SecondaryCache::InsertSaved()` API, with default implementation depending on `Insert()`. Some implementations might need to add a custom implementation of `InsertSaved()`. (Details in API comments.)
|
||||
|
||||
## 7.8.0 (10/22/2022)
|
||||
### New Features
|
||||
@@ -45,10 +72,11 @@
|
||||
* Try to align the compaction output file boundaries to the next level ones, which can reduce more than 10% compaction load for the default level compaction. The feature is enabled by default, to disable, set `AdvancedColumnFamilyOptions.level_compaction_dynamic_file_size` to false. As a side effect, it can create SSTs larger than the target_file_size (capped at 2x target_file_size) or smaller files.
|
||||
* Improve RoundRobin TTL compaction, which is going to be the same as normal RoundRobin compaction to move the compaction cursor.
|
||||
* Fix a small CPU regression caused by a change that UserComparatorWrapper was made Customizable, because Customizable itself has small CPU overhead for initialization.
|
||||
* Fixed an iterator performance regression for delete range users when scanning through a consecutive sequence of range tombstones (#10877).
|
||||
|
||||
### Behavior Changes
|
||||
* Sanitize min_write_buffer_number_to_merge to 1 if atomic flush is enabled to prevent unexpected data loss when WAL is disabled in a multi-column-family setting (#10773).
|
||||
* With periodic stat dumper waits up every options.stats_dump_period_sec seconds, it won't dump stats for a CF if it has no change in the period, unless 7 periods have been skipped.
|
||||
* Only periodic stats dumper triggered by options.stats_dump_period_sec will update stats interval. Ones triggered by DB::GetProperty() will not update stats interval and will report based on an interval since the last time stats dump period.
|
||||
|
||||
### Public API changes
|
||||
* Make kXXH3 checksum the new default, because it is faster on common hardware, especially with kCRC32c affected by a performance bug in some versions of clang (https://github.com/facebook/rocksdb/issues/9891). DBs written with this new setting can be read by RocksDB 6.27 and newer.
|
||||
|
||||
@@ -2092,8 +2092,8 @@ ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar
|
||||
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar
|
||||
SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.2.12
|
||||
ZLIB_SHA256 ?= 91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9
|
||||
ZLIB_VER ?= 1.2.13
|
||||
ZLIB_SHA256 ?= b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
|
||||
@@ -16,8 +16,8 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"cache/charged_cache.cc",
|
||||
"cache/clock_cache.cc",
|
||||
"cache/compressed_secondary_cache.cc",
|
||||
"cache/fast_lru_cache.cc",
|
||||
"cache/lru_cache.cc",
|
||||
"cache/secondary_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_contents.cc",
|
||||
@@ -355,8 +355,8 @@ cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"cache/charged_cache.cc",
|
||||
"cache/clock_cache.cc",
|
||||
"cache/compressed_secondary_cache.cc",
|
||||
"cache/fast_lru_cache.cc",
|
||||
"cache/lru_cache.cc",
|
||||
"cache/secondary_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_contents.cc",
|
||||
|
||||
Vendored
-5
@@ -13,7 +13,6 @@
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include "cache/fast_lru_cache.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "monitoring/histogram.h"
|
||||
#include "port/port.h"
|
||||
@@ -297,10 +296,6 @@ class CacheBench {
|
||||
cache_ = HyperClockCacheOptions(FLAGS_cache_size, FLAGS_value_bytes,
|
||||
FLAGS_num_shard_bits)
|
||||
.MakeSharedCache();
|
||||
} else if (FLAGS_cache_type == "fast_lru_cache") {
|
||||
cache_ = NewFastLRUCache(
|
||||
FLAGS_cache_size, FLAGS_value_bytes, FLAGS_num_shard_bits,
|
||||
false /*strict_capacity_limit*/, kDefaultCacheMetadataChargePolicy);
|
||||
} else if (FLAGS_cache_type == "lru_cache") {
|
||||
LRUCacheOptions opts(FLAGS_cache_size, FLAGS_num_shard_bits,
|
||||
false /* strict_capacity_limit */,
|
||||
|
||||
Vendored
+23
-52
@@ -15,15 +15,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cache/fast_lru_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
// FastLRUCache and HyperClockCache only support 16-byte keys, so some of
|
||||
// the tests originally wrote for LRUCache do not work on the other caches.
|
||||
// HyperClockCache only supports 16-byte keys, so some of the tests
|
||||
// originally written for LRUCache do not work on the other caches.
|
||||
// Those tests were adapted to use 16-byte keys. We kept the original ones.
|
||||
// TODO: Remove the original tests if they ever become unused.
|
||||
|
||||
@@ -76,7 +75,6 @@ void EraseDeleter2(const Slice& /*key*/, void* value) {
|
||||
|
||||
const std::string kLRU = "lru";
|
||||
const std::string kHyperClock = "hyper_clock";
|
||||
const std::string kFast = "fast";
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -86,7 +84,7 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
static std::string type_;
|
||||
|
||||
static void Deleter(const Slice& key, void* v) {
|
||||
if (type_ == kFast || type_ == kHyperClock) {
|
||||
if (type_ == kHyperClock) {
|
||||
current_->deleted_keys_.push_back(DecodeKey16Bytes(key));
|
||||
} else {
|
||||
current_->deleted_keys_.push_back(DecodeKey32Bits(key));
|
||||
@@ -126,11 +124,6 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
capacity, estimated_value_size_ /*estimated_value_size*/)
|
||||
.MakeSharedCache();
|
||||
}
|
||||
if (type == kFast) {
|
||||
return NewFastLRUCache(
|
||||
capacity, estimated_value_size_, -1 /*num_shard_bits*/,
|
||||
false /*strict_capacity_limit*/, kDefaultCacheMetadataChargePolicy);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -153,11 +146,6 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
nullptr /*allocator*/, charge_policy)
|
||||
.MakeSharedCache();
|
||||
}
|
||||
if (type == kFast) {
|
||||
return NewFastLRUCache(capacity, 1 /*estimated_value_size*/,
|
||||
num_shard_bits, strict_capacity_limit,
|
||||
charge_policy);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -167,7 +155,7 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
// LRUCache doesn't, so the encoding depends on the cache type.
|
||||
std::string EncodeKey(int k) {
|
||||
auto type = GetParam();
|
||||
if (type == kFast || type == kHyperClock) {
|
||||
if (type == kHyperClock) {
|
||||
return EncodeKey16Bytes(k);
|
||||
} else {
|
||||
return EncodeKey32Bits(k);
|
||||
@@ -176,7 +164,7 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
|
||||
int DecodeKey(const Slice& k) {
|
||||
auto type = GetParam();
|
||||
if (type == kFast || type == kHyperClock) {
|
||||
if (type == kHyperClock) {
|
||||
return DecodeKey16Bytes(k);
|
||||
} else {
|
||||
return DecodeKey32Bits(k);
|
||||
@@ -202,29 +190,21 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
cache->Erase(EncodeKey(key));
|
||||
}
|
||||
|
||||
int Lookup(int key) {
|
||||
return Lookup(cache_, key);
|
||||
}
|
||||
int Lookup(int key) { return Lookup(cache_, key); }
|
||||
|
||||
void Insert(int key, int value, int charge = 1) {
|
||||
Insert(cache_, key, value, charge);
|
||||
}
|
||||
|
||||
void Erase(int key) {
|
||||
Erase(cache_, key);
|
||||
}
|
||||
void Erase(int key) { Erase(cache_, key); }
|
||||
|
||||
int Lookup2(int key) {
|
||||
return Lookup(cache2_, key);
|
||||
}
|
||||
int Lookup2(int key) { return Lookup(cache2_, key); }
|
||||
|
||||
void Insert2(int key, int value, int charge = 1) {
|
||||
Insert(cache2_, key, value, charge);
|
||||
}
|
||||
|
||||
void Erase2(int key) {
|
||||
Erase(cache2_, key);
|
||||
}
|
||||
void Erase2(int key) { Erase(cache2_, key); }
|
||||
};
|
||||
|
||||
CacheTest* CacheTest::current_;
|
||||
@@ -418,13 +398,13 @@ TEST_P(CacheTest, HitAndMiss) {
|
||||
|
||||
Insert(100, 101);
|
||||
ASSERT_EQ(101, Lookup(100));
|
||||
ASSERT_EQ(-1, Lookup(200));
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
ASSERT_EQ(-1, Lookup(200));
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
|
||||
Insert(200, 201);
|
||||
ASSERT_EQ(101, Lookup(100));
|
||||
ASSERT_EQ(201, Lookup(200));
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
|
||||
Insert(100, 102);
|
||||
if (GetParam() == kHyperClock) {
|
||||
@@ -434,7 +414,7 @@ TEST_P(CacheTest, HitAndMiss) {
|
||||
ASSERT_EQ(102, Lookup(100));
|
||||
}
|
||||
ASSERT_EQ(201, Lookup(200));
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
ASSERT_EQ(-1, Lookup(300));
|
||||
|
||||
ASSERT_EQ(1U, deleted_keys_.size());
|
||||
ASSERT_EQ(100, deleted_keys_[0]);
|
||||
@@ -463,14 +443,14 @@ TEST_P(CacheTest, Erase) {
|
||||
Insert(100, 101);
|
||||
Insert(200, 201);
|
||||
Erase(100);
|
||||
ASSERT_EQ(-1, Lookup(100));
|
||||
ASSERT_EQ(-1, Lookup(100));
|
||||
ASSERT_EQ(201, Lookup(200));
|
||||
ASSERT_EQ(1U, deleted_keys_.size());
|
||||
ASSERT_EQ(100, deleted_keys_[0]);
|
||||
ASSERT_EQ(101, deleted_values_[0]);
|
||||
|
||||
Erase(100);
|
||||
ASSERT_EQ(-1, Lookup(100));
|
||||
ASSERT_EQ(-1, Lookup(100));
|
||||
ASSERT_EQ(201, Lookup(200));
|
||||
ASSERT_EQ(1U, deleted_keys_.size());
|
||||
}
|
||||
@@ -515,7 +495,7 @@ TEST_P(CacheTest, EvictionPolicy) {
|
||||
Insert(200, 201);
|
||||
// Frequently used entry must be kept around
|
||||
for (int i = 0; i < 2 * kCacheSize; i++) {
|
||||
Insert(1000+i, 2000+i);
|
||||
Insert(1000 + i, 2000 + i);
|
||||
ASSERT_EQ(101, Lookup(100));
|
||||
}
|
||||
ASSERT_EQ(101, Lookup(100));
|
||||
@@ -686,7 +666,7 @@ TEST_P(CacheTest, HeavyEntries) {
|
||||
ASSERT_EQ(1000 + i, r);
|
||||
}
|
||||
}
|
||||
ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10);
|
||||
ASSERT_LE(cached_weight, kCacheSize + kCacheSize / 10);
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, NewId) {
|
||||
@@ -704,7 +684,7 @@ class Value {
|
||||
|
||||
namespace {
|
||||
void deleter(const Slice& /*key*/, void* value) {
|
||||
delete static_cast<Value *>(value);
|
||||
delete static_cast<Value*>(value);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -741,7 +721,7 @@ TEST_P(CacheTest, ReleaseWithoutErase) {
|
||||
|
||||
TEST_P(CacheTest, SetCapacity) {
|
||||
auto type = GetParam();
|
||||
if (type == kFast || type == kHyperClock) {
|
||||
if (type == kHyperClock) {
|
||||
ROCKSDB_GTEST_BYPASS(
|
||||
"FastLRUCache and HyperClockCache don't support arbitrary capacity "
|
||||
"adjustments.");
|
||||
@@ -795,14 +775,6 @@ TEST_P(CacheTest, SetCapacity) {
|
||||
}
|
||||
|
||||
TEST_P(LRUCacheTest, SetStrictCapacityLimit) {
|
||||
auto type = GetParam();
|
||||
if (type == kFast) {
|
||||
ROCKSDB_GTEST_BYPASS(
|
||||
"FastLRUCache only supports a limited number of "
|
||||
"inserts beyond "
|
||||
"capacity.");
|
||||
return;
|
||||
}
|
||||
// test1: set the flag to false. Insert more keys than capacity. See if they
|
||||
// all go through.
|
||||
std::shared_ptr<Cache> cache = NewCache(5, 0, false);
|
||||
@@ -859,7 +831,7 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
// a LRUCache with n entries and one shard only
|
||||
std::shared_ptr<Cache> cache = NewCache(n, 0, false);
|
||||
|
||||
std::vector<Cache::Handle*> handles(n+1);
|
||||
std::vector<Cache::Handle*> handles(n + 1);
|
||||
|
||||
// Insert n+1 entries, but not releasing.
|
||||
for (int i = 0; i < static_cast<int>(n + 1); i++) {
|
||||
@@ -919,7 +891,7 @@ void legacy_callback(void* value, size_t charge) {
|
||||
legacy_callback_state.push_back(
|
||||
{DecodeValue(value), static_cast<int>(charge)});
|
||||
}
|
||||
};
|
||||
}; // namespace
|
||||
|
||||
TEST_P(CacheTest, ApplyToAllCacheEntriesTest) {
|
||||
std::vector<std::pair<int, int>> inserted;
|
||||
@@ -1053,9 +1025,8 @@ TEST_P(CacheTest, GetChargeAndDeleter) {
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
testing::Values(kLRU, kHyperClock, kFast));
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest,
|
||||
testing::Values(kLRU, kFast));
|
||||
testing::Values(kLRU, kHyperClock));
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest, testing::Values(kLRU));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
Vendored
+548
-345
File diff suppressed because it is too large.
Load diff
Vendored
+134
-65
@@ -27,12 +27,13 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace hyper_clock_cache {
|
||||
namespace clock_cache {
|
||||
|
||||
// Forward declaration of friend class.
|
||||
class ClockCacheTest;
|
||||
|
||||
// HyperClockCache is an experimental alternative to LRUCache.
|
||||
// HyperClockCache is an alternative to LRUCache specifically tailored for
|
||||
// use as BlockBasedTableOptions::block_cache
|
||||
//
|
||||
// Benefits
|
||||
// --------
|
||||
@@ -311,6 +312,14 @@ struct ClockHandleBasicData {
|
||||
UniqueId64x2 hashed_key = kNullUniqueId64x2;
|
||||
size_t total_charge = 0;
|
||||
|
||||
// For total_charge_and_flags
|
||||
// "Detached" means the handle is allocated separately from hash table.
|
||||
static constexpr uint64_t kFlagDetached = uint64_t{1} << 63;
|
||||
// Extract just the total charge
|
||||
static constexpr uint64_t kTotalChargeMask = kFlagDetached - 1;
|
||||
|
||||
inline size_t GetTotalCharge() const { return total_charge; }
|
||||
|
||||
// Calls deleter (if non-null) on cache key and value
|
||||
void FreeData() const;
|
||||
|
||||
@@ -318,9 +327,7 @@ struct ClockHandleBasicData {
|
||||
const UniqueId64x2& GetHash() const { return hashed_key; }
|
||||
};
|
||||
|
||||
// Target size to be exactly a common cache line size (see static_assert in
|
||||
// clock_cache.cc)
|
||||
struct ALIGN_AS(64U) ClockHandle : public ClockHandleBasicData {
|
||||
struct ClockHandle : public ClockHandleBasicData {
|
||||
// Constants for handling the atomic `meta` word, which tracks most of the
|
||||
// state of the handle. The meta word looks like this:
|
||||
// low bits high bits
|
||||
@@ -372,32 +379,54 @@ struct ALIGN_AS(64U) ClockHandle : public ClockHandleBasicData {
|
||||
|
||||
// See above
|
||||
std::atomic<uint64_t> meta{};
|
||||
// The number of elements that hash to this slot or a lower one, but wind
|
||||
// up in this slot or a higher one.
|
||||
std::atomic<uint32_t> displacements{};
|
||||
|
||||
// True iff the handle is allocated separately from hash table.
|
||||
bool detached = false;
|
||||
// Anticipating use for SecondaryCache support
|
||||
void* reserved_for_future_use = nullptr;
|
||||
}; // struct ClockHandle
|
||||
|
||||
class ClockHandleTable {
|
||||
class HyperClockTable {
|
||||
public:
|
||||
explicit ClockHandleTable(int hash_bits, bool initial_charge_metadata);
|
||||
~ClockHandleTable();
|
||||
// Target size to be exactly a common cache line size (see static_assert in
|
||||
// clock_cache.cc)
|
||||
struct ALIGN_AS(64U) HandleImpl : public ClockHandle {
|
||||
// The number of elements that hash to this slot or a lower one, but wind
|
||||
// up in this slot or a higher one.
|
||||
std::atomic<uint32_t> displacements{};
|
||||
|
||||
Status Insert(const ClockHandleBasicData& proto, ClockHandle** handle,
|
||||
// Whether this is a "deteched" handle that is independently allocated
|
||||
// with `new` (so must be deleted with `delete`).
|
||||
// TODO: ideally this would be packed into some other data field, such
|
||||
// as upper bits of total_charge, but that incurs a measurable performance
|
||||
// regression.
|
||||
bool detached = false;
|
||||
|
||||
inline bool IsDetached() const { return detached; }
|
||||
|
||||
inline void SetDetached() { detached = true; }
|
||||
}; // struct HandleImpl
|
||||
|
||||
struct Opts {
|
||||
size_t estimated_value_size;
|
||||
};
|
||||
|
||||
HyperClockTable(size_t capacity, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
const Opts& opts);
|
||||
~HyperClockTable();
|
||||
|
||||
Status Insert(const ClockHandleBasicData& proto, HandleImpl** handle,
|
||||
Cache::Priority priority, size_t capacity,
|
||||
bool strict_capacity_limit);
|
||||
|
||||
ClockHandle* Lookup(const UniqueId64x2& hashed_key);
|
||||
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
|
||||
|
||||
bool Release(ClockHandle* handle, bool useful, bool erase_if_last_ref);
|
||||
bool Release(HandleImpl* handle, bool useful, bool erase_if_last_ref);
|
||||
|
||||
void Ref(ClockHandle& handle);
|
||||
void Ref(HandleImpl& handle);
|
||||
|
||||
void Erase(const UniqueId64x2& hashed_key);
|
||||
|
||||
void ConstApplyToEntriesRange(std::function<void(const ClockHandle&)> func,
|
||||
void ConstApplyToEntriesRange(std::function<void(const HandleImpl&)> func,
|
||||
size_t index_begin, size_t index_end,
|
||||
bool apply_if_will_be_deleted) const;
|
||||
|
||||
@@ -407,12 +436,12 @@ class ClockHandleTable {
|
||||
|
||||
int GetLengthBits() const { return length_bits_; }
|
||||
|
||||
size_t GetOccupancyLimit() const { return occupancy_limit_; }
|
||||
|
||||
size_t GetOccupancy() const {
|
||||
return occupancy_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
size_t GetOccupancyLimit() const { return occupancy_limit_; }
|
||||
|
||||
size_t GetUsage() const { return usage_.load(std::memory_order_relaxed); }
|
||||
|
||||
size_t GetDetachedUsage() const {
|
||||
@@ -420,8 +449,8 @@ class ClockHandleTable {
|
||||
}
|
||||
|
||||
// Acquire/release N references
|
||||
void TEST_RefN(ClockHandle& handle, size_t n);
|
||||
void TEST_ReleaseN(ClockHandle* handle, size_t n);
|
||||
void TEST_RefN(HandleImpl& handle, size_t n);
|
||||
void TEST_ReleaseN(HandleImpl* handle, size_t n);
|
||||
|
||||
private: // functions
|
||||
// Returns x mod 2^{length_bits_}.
|
||||
@@ -432,8 +461,8 @@ class ClockHandleTable {
|
||||
// Runs the clock eviction algorithm trying to reclaim at least
|
||||
// requested_charge. Returns how much is evicted, which could be less
|
||||
// if it appears impossible to evict the requested amount without blocking.
|
||||
void Evict(size_t requested_charge, size_t* freed_charge,
|
||||
size_t* freed_count);
|
||||
inline void Evict(size_t requested_charge, size_t* freed_charge,
|
||||
size_t* freed_count);
|
||||
|
||||
// Returns the first slot in the probe sequence, starting from the given
|
||||
// probe number, with a handle e such that match(e) is true. At every
|
||||
@@ -446,15 +475,54 @@ class ClockHandleTable {
|
||||
// value of probe is one more than the last non-aborting probe during the
|
||||
// call. This is so that that the variable can be used to keep track of
|
||||
// progress across consecutive calls to FindSlot.
|
||||
inline ClockHandle* FindSlot(const UniqueId64x2& hashed_key,
|
||||
std::function<bool(ClockHandle*)> match,
|
||||
std::function<bool(ClockHandle*)> stop,
|
||||
std::function<void(ClockHandle*)> update,
|
||||
size_t& probe);
|
||||
inline HandleImpl* FindSlot(const UniqueId64x2& hashed_key,
|
||||
std::function<bool(HandleImpl*)> match,
|
||||
std::function<bool(HandleImpl*)> stop,
|
||||
std::function<void(HandleImpl*)> update,
|
||||
size_t& probe);
|
||||
|
||||
// Re-decrement all displacements in probe path starting from beginning
|
||||
// until (not including) the given handle
|
||||
void Rollback(const UniqueId64x2& hashed_key, const ClockHandle* h);
|
||||
inline void Rollback(const UniqueId64x2& hashed_key, const HandleImpl* h);
|
||||
|
||||
// Subtracts `total_charge` from `usage_` and 1 from `occupancy_`.
|
||||
// Ideally this comes after releasing the entry itself so that we
|
||||
// actually have the available occupancy/usage that is claimed.
|
||||
// However, that means total_charge has to be saved from the handle
|
||||
// before releasing it so that it can be provided to this function.
|
||||
inline void ReclaimEntryUsage(size_t total_charge);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=true rules. This
|
||||
// means the operation might fail with Status::MemoryLimit. If
|
||||
// `need_evict_for_occupancy`, then eviction of at least one entry is
|
||||
// required, and the operation should fail if not possible.
|
||||
// NOTE: Otherwise, occupancy_ is not managed in this function
|
||||
inline Status ChargeUsageMaybeEvictStrict(size_t total_charge,
|
||||
size_t capacity,
|
||||
bool need_evict_for_occupancy);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=false rules. This
|
||||
// means that updating `usage_` always succeeds even if forced to exceed
|
||||
// capacity. If `need_evict_for_occupancy`, then eviction of at least one
|
||||
// entry is required, and the operation should return false if such eviction
|
||||
// is not possible. `usage_` is not updated in that case. Otherwise, returns
|
||||
// true, indicating success.
|
||||
// NOTE: occupancy_ is not managed in this function
|
||||
inline bool ChargeUsageMaybeEvictNonStrict(size_t total_charge,
|
||||
size_t capacity,
|
||||
bool need_evict_for_occupancy);
|
||||
|
||||
// Creates a "detached" handle for returning from an Insert operation that
|
||||
// cannot be completed by actually inserting into the table.
|
||||
// Updates `detached_usage_` but not `usage_` nor `occupancy_`.
|
||||
inline HandleImpl* DetachedInsert(const ClockHandleBasicData& proto);
|
||||
|
||||
// Returns the number of bits used to hash an element in the hash
|
||||
// table.
|
||||
static int CalcHashBits(size_t capacity, size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
|
||||
private: // data
|
||||
// Number of hash bits used for table index.
|
||||
@@ -468,7 +536,7 @@ class ClockHandleTable {
|
||||
const size_t occupancy_limit_;
|
||||
|
||||
// Array of slots comprising the hash table.
|
||||
const std::unique_ptr<ClockHandle[]> array_;
|
||||
const std::unique_ptr<HandleImpl[]> array_;
|
||||
|
||||
// We partition the following members into different cache lines
|
||||
// to avoid false sharing among Lookup, Release, Erase and Insert
|
||||
@@ -487,17 +555,18 @@ class ClockHandleTable {
|
||||
|
||||
// Part of usage by detached entries (not in table)
|
||||
std::atomic<size_t> detached_usage_{};
|
||||
}; // class ClockHandleTable
|
||||
}; // class HyperClockTable
|
||||
|
||||
// A single shard of sharded cache.
|
||||
template <class Table>
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
|
||||
public:
|
||||
ClockCacheShard(size_t capacity, size_t estimated_value_size,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
ClockCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
const typename Table::Opts& opts);
|
||||
|
||||
// For CacheShard concept
|
||||
using HandleImpl = ClockHandle;
|
||||
using HandleImpl = typename Table::HandleImpl;
|
||||
// Hash is lossless hash of 128-bit key
|
||||
using HashVal = UniqueId64x2;
|
||||
using HashCref = const HashVal&;
|
||||
@@ -532,25 +601,31 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit);
|
||||
|
||||
Status Insert(const Slice& key, const UniqueId64x2& hashed_key, void* value,
|
||||
size_t charge, Cache::DeleterFn deleter, ClockHandle** handle,
|
||||
size_t charge, Cache::DeleterFn deleter, HandleImpl** handle,
|
||||
Cache::Priority priority);
|
||||
|
||||
ClockHandle* Lookup(const Slice& key, const UniqueId64x2& hashed_key);
|
||||
HandleImpl* Lookup(const Slice& key, const UniqueId64x2& hashed_key);
|
||||
|
||||
bool Release(ClockHandle* handle, bool useful, bool erase_if_last_ref);
|
||||
bool Release(HandleImpl* handle, bool useful, bool erase_if_last_ref);
|
||||
|
||||
bool Release(ClockHandle* handle, bool erase_if_last_ref = false);
|
||||
bool Release(HandleImpl* handle, bool erase_if_last_ref = false);
|
||||
|
||||
bool Ref(ClockHandle* handle);
|
||||
bool Ref(HandleImpl* handle);
|
||||
|
||||
void Erase(const Slice& key, const UniqueId64x2& hashed_key);
|
||||
|
||||
size_t GetCapacity() const;
|
||||
|
||||
size_t GetUsage() const;
|
||||
|
||||
size_t GetDetachedUsage() const;
|
||||
|
||||
size_t GetPinnedUsage() const;
|
||||
|
||||
size_t GetOccupancyCount() const;
|
||||
|
||||
size_t GetOccupancyLimit() const;
|
||||
|
||||
size_t GetTableAddressCount() const;
|
||||
|
||||
void ApplyToSomeEntries(
|
||||
@@ -565,40 +640,29 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
|
||||
// SecondaryCache not yet supported
|
||||
Status Insert(const Slice& key, const UniqueId64x2& hashed_key, void* value,
|
||||
const Cache::CacheItemHelper* helper, size_t charge,
|
||||
ClockHandle** handle, Cache::Priority priority) {
|
||||
HandleImpl** handle, Cache::Priority priority) {
|
||||
return Insert(key, hashed_key, value, charge, helper->del_cb, handle,
|
||||
priority);
|
||||
}
|
||||
|
||||
ClockHandle* Lookup(const Slice& key, const UniqueId64x2& hashed_key,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
const Cache::CreateCallback& /*create_cb*/,
|
||||
Cache::Priority /*priority*/, bool /*wait*/,
|
||||
Statistics* /*stats*/) {
|
||||
HandleImpl* Lookup(const Slice& key, const UniqueId64x2& hashed_key,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
const Cache::CreateCallback& /*create_cb*/,
|
||||
Cache::Priority /*priority*/, bool /*wait*/,
|
||||
Statistics* /*stats*/) {
|
||||
return Lookup(key, hashed_key);
|
||||
}
|
||||
|
||||
bool IsReady(ClockHandle* /*handle*/) { return true; }
|
||||
bool IsReady(HandleImpl* /*handle*/) { return true; }
|
||||
|
||||
void Wait(ClockHandle* /*handle*/) {}
|
||||
void Wait(HandleImpl* /*handle*/) {}
|
||||
|
||||
// Acquire/release N references
|
||||
void TEST_RefN(ClockHandle* handle, size_t n);
|
||||
void TEST_ReleaseN(ClockHandle* handle, size_t n);
|
||||
|
||||
private: // functions
|
||||
friend class ClockCache;
|
||||
friend class ClockCacheTest;
|
||||
|
||||
ClockHandle* DetachedInsert(const ClockHandleBasicData& h);
|
||||
|
||||
// Returns the number of bits used to hash an element in the hash
|
||||
// table.
|
||||
static int CalcHashBits(size_t capacity, size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
void TEST_RefN(HandleImpl* handle, size_t n);
|
||||
void TEST_ReleaseN(HandleImpl* handle, size_t n);
|
||||
|
||||
private: // data
|
||||
ClockHandleTable table_;
|
||||
Table table_;
|
||||
|
||||
// Maximum total charge of all elements stored in the table.
|
||||
std::atomic<size_t> capacity_;
|
||||
@@ -611,8 +675,10 @@ class HyperClockCache
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public ShardedCache<ClockCacheShard> {
|
||||
: public ShardedCache<ClockCacheShard<HyperClockTable>> {
|
||||
public:
|
||||
using Shard = ClockCacheShard<HyperClockTable>;
|
||||
|
||||
HyperClockCache(size_t capacity, size_t estimated_value_size,
|
||||
int num_shard_bits, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
@@ -625,8 +691,11 @@ class HyperClockCache
|
||||
size_t GetCharge(Handle* handle) const override;
|
||||
|
||||
DeleterFn GetDeleter(Handle* handle) const override;
|
||||
|
||||
void ReportProblems(
|
||||
const std::shared_ptr<Logger>& /*info_log*/) const override;
|
||||
}; // class HyperClockCache
|
||||
|
||||
} // namespace hyper_clock_cache
|
||||
} // namespace clock_cache
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-580
@@ -1,580 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "cache/fast_lru_cache.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "port/lang.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/math.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace fast_lru_cache {
|
||||
|
||||
LRUHandleTable::LRUHandleTable(int hash_bits)
|
||||
: length_bits_(hash_bits),
|
||||
length_bits_mask_((uint32_t{1} << length_bits_) - 1),
|
||||
occupancy_(0),
|
||||
occupancy_limit_(static_cast<uint32_t>((uint32_t{1} << length_bits_) *
|
||||
kStrictLoadFactor)),
|
||||
array_(new LRUHandle[size_t{1} << length_bits_]) {
|
||||
assert(hash_bits <= 32);
|
||||
}
|
||||
|
||||
LRUHandleTable::~LRUHandleTable() {
|
||||
ApplyToEntriesRange([](LRUHandle* h) { h->FreeData(); }, 0, GetTableSize());
|
||||
}
|
||||
|
||||
LRUHandle* LRUHandleTable::Lookup(const Slice& key, uint32_t hash) {
|
||||
int probe = 0;
|
||||
int slot = FindVisibleElement(key, hash, probe, 0);
|
||||
return (slot == -1) ? nullptr : &array_[slot];
|
||||
}
|
||||
|
||||
LRUHandle* LRUHandleTable::Insert(LRUHandle* h, LRUHandle** old) {
|
||||
int probe = 0;
|
||||
int slot = FindVisibleElementOrAvailableSlot(h->key(), h->hash, probe,
|
||||
1 /*displacement*/);
|
||||
*old = nullptr;
|
||||
if (slot == -1) {
|
||||
// TODO(Guido) Don't we need to roll back displacements here?
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (array_[slot].IsEmpty() || array_[slot].IsTombstone()) {
|
||||
bool empty = array_[slot].IsEmpty();
|
||||
Assign(slot, h);
|
||||
LRUHandle* new_entry = &array_[slot];
|
||||
if (empty) {
|
||||
// This used to be an empty slot.
|
||||
return new_entry;
|
||||
}
|
||||
// It used to be a tombstone, so there may already be a copy of the
|
||||
// key in the table.
|
||||
slot = FindVisibleElement(h->key(), h->hash, probe, 0 /*displacement*/);
|
||||
if (slot == -1) {
|
||||
// No existing copy of the key.
|
||||
return new_entry;
|
||||
}
|
||||
*old = &array_[slot];
|
||||
return new_entry;
|
||||
} else {
|
||||
// There is an existing copy of the key.
|
||||
*old = &array_[slot];
|
||||
// Find an available slot for the new element.
|
||||
array_[slot].displacements++;
|
||||
slot = FindAvailableSlot(h->key(), probe, 1 /*displacement*/);
|
||||
if (slot == -1) {
|
||||
// No available slots. Roll back displacements.
|
||||
probe = 0;
|
||||
slot = FindVisibleElement(h->key(), h->hash, probe, -1);
|
||||
array_[slot].displacements--;
|
||||
FindAvailableSlot(h->key(), probe, -1);
|
||||
return nullptr;
|
||||
}
|
||||
Assign(slot, h);
|
||||
return &array_[slot];
|
||||
}
|
||||
}
|
||||
|
||||
void LRUHandleTable::Remove(LRUHandle* h) {
|
||||
assert(h->next == nullptr &&
|
||||
h->prev == nullptr); // Already off the LRU list.
|
||||
int probe = 0;
|
||||
FindSlot(
|
||||
h->key(), [&h](LRUHandle* e) { return e == h; }, probe,
|
||||
-1 /*displacement*/);
|
||||
h->SetIsVisible(false);
|
||||
h->SetIsElement(false);
|
||||
occupancy_--;
|
||||
}
|
||||
|
||||
void LRUHandleTable::Assign(int slot, LRUHandle* h) {
|
||||
LRUHandle* dst = &array_[slot];
|
||||
uint32_t disp = dst->displacements;
|
||||
*dst = *h;
|
||||
dst->displacements = disp;
|
||||
dst->SetIsVisible(true);
|
||||
dst->SetIsElement(true);
|
||||
occupancy_++;
|
||||
}
|
||||
|
||||
void LRUHandleTable::Exclude(LRUHandle* h) { h->SetIsVisible(false); }
|
||||
|
||||
int LRUHandleTable::FindVisibleElement(const Slice& key, uint32_t hash,
|
||||
int& probe, int displacement) {
|
||||
return FindSlot(
|
||||
key,
|
||||
[&](LRUHandle* h) { return h->Matches(key, hash) && h->IsVisible(); },
|
||||
probe, displacement);
|
||||
}
|
||||
|
||||
int LRUHandleTable::FindAvailableSlot(const Slice& key, int& probe,
|
||||
int displacement) {
|
||||
return FindSlot(
|
||||
key, [](LRUHandle* h) { return h->IsEmpty() || h->IsTombstone(); }, probe,
|
||||
displacement);
|
||||
}
|
||||
|
||||
int LRUHandleTable::FindVisibleElementOrAvailableSlot(const Slice& key,
|
||||
uint32_t hash, int& probe,
|
||||
int displacement) {
|
||||
return FindSlot(
|
||||
key,
|
||||
[&](LRUHandle* h) {
|
||||
return h->IsEmpty() || h->IsTombstone() ||
|
||||
(h->Matches(key, hash) && h->IsVisible());
|
||||
},
|
||||
probe, displacement);
|
||||
}
|
||||
|
||||
inline int LRUHandleTable::FindSlot(const Slice& key,
|
||||
std::function<bool(LRUHandle*)> cond,
|
||||
int& probe, int displacement) {
|
||||
uint32_t base = ModTableSize(Hash(key.data(), key.size(), kProbingSeed1));
|
||||
uint32_t increment =
|
||||
ModTableSize((Hash(key.data(), key.size(), kProbingSeed2) << 1) | 1);
|
||||
uint32_t current = ModTableSize(base + probe * increment);
|
||||
while (true) {
|
||||
LRUHandle* h = &array_[current];
|
||||
probe++;
|
||||
if (current == base && probe > 1) {
|
||||
// We looped back.
|
||||
return -1;
|
||||
}
|
||||
if (cond(h)) {
|
||||
return current;
|
||||
}
|
||||
if (h->IsEmpty()) {
|
||||
// We check emptyness after the condition, because
|
||||
// the condition may be emptyness.
|
||||
return -1;
|
||||
}
|
||||
h->displacements += displacement;
|
||||
current = ModTableSize(current + increment);
|
||||
}
|
||||
}
|
||||
|
||||
LRUCacheShard::LRUCacheShard(size_t capacity, size_t estimated_value_size,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: CacheShardBase(metadata_charge_policy),
|
||||
capacity_(capacity),
|
||||
strict_capacity_limit_(strict_capacity_limit),
|
||||
table_(
|
||||
CalcHashBits(capacity, estimated_value_size, metadata_charge_policy)),
|
||||
usage_(0),
|
||||
lru_usage_(0) {
|
||||
// Make empty circular linked list.
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
lru_low_pri_ = &lru_;
|
||||
}
|
||||
|
||||
void LRUCacheShard::EraseUnRefEntries() {
|
||||
autovector<LRUHandle> last_reference_list;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
while (lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
// LRU list contains only elements which can be evicted.
|
||||
assert(old->IsVisible() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
last_reference_list.push_back(*old);
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entries here outside of mutex for performance reasons.
|
||||
for (auto& h : last_reference_list) {
|
||||
h.FreeData();
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::ApplyToSomeEntries(
|
||||
const std::function<void(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter)>& callback,
|
||||
size_t average_entries_per_lock, size_t* state) {
|
||||
// The state is essentially going to be the starting hash, which works
|
||||
// nicely even if we resize between calls because we use upper-most
|
||||
// hash bits for table indexes.
|
||||
DMutexLock l(mutex_);
|
||||
size_t length_bits = table_.GetLengthBits();
|
||||
size_t length = table_.GetTableSize();
|
||||
|
||||
assert(average_entries_per_lock > 0);
|
||||
// Assuming we are called with same average_entries_per_lock repeatedly,
|
||||
// this simplifies some logic (index_end will not overflow).
|
||||
assert(average_entries_per_lock < length || *state == 0);
|
||||
|
||||
size_t index_begin = *state >> (sizeof(size_t) * 8u - length_bits);
|
||||
size_t index_end = index_begin + average_entries_per_lock;
|
||||
if (index_end >= length) {
|
||||
// Going to end
|
||||
index_end = length;
|
||||
*state = SIZE_MAX;
|
||||
} else {
|
||||
*state = index_end << (sizeof(size_t) * 8u - length_bits);
|
||||
}
|
||||
|
||||
table_.ApplyToEntriesRange(
|
||||
[callback,
|
||||
metadata_charge_policy = metadata_charge_policy_](LRUHandle* h) {
|
||||
callback(h->key(), h->value, h->GetCharge(metadata_charge_policy),
|
||||
h->deleter);
|
||||
},
|
||||
index_begin, index_end);
|
||||
}
|
||||
|
||||
void LRUCacheShard::LRU_Remove(LRUHandle* h) {
|
||||
assert(h->next != nullptr);
|
||||
assert(h->prev != nullptr);
|
||||
h->next->prev = h->prev;
|
||||
h->prev->next = h->next;
|
||||
h->prev = h->next = nullptr;
|
||||
assert(lru_usage_ >= h->total_charge);
|
||||
lru_usage_ -= h->total_charge;
|
||||
}
|
||||
|
||||
void LRUCacheShard::LRU_Insert(LRUHandle* h) {
|
||||
assert(h->next == nullptr);
|
||||
assert(h->prev == nullptr);
|
||||
// Insert h to head of LRU list.
|
||||
h->next = &lru_;
|
||||
h->prev = lru_.prev;
|
||||
h->prev->next = h;
|
||||
h->next->prev = h;
|
||||
lru_usage_ += h->total_charge;
|
||||
}
|
||||
|
||||
void LRUCacheShard::EvictFromLRU(size_t charge,
|
||||
autovector<LRUHandle>* deleted) {
|
||||
while ((usage_ + charge) > capacity_ && lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
// LRU list contains only elements which can be evicted.
|
||||
assert(old->IsVisible() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
deleted->push_back(*old);
|
||||
}
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::CalcEstimatedHandleCharge(
|
||||
size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
LRUHandle h;
|
||||
h.CalcTotalCharge(estimated_value_size, metadata_charge_policy);
|
||||
return h.total_charge;
|
||||
}
|
||||
|
||||
int LRUCacheShard::CalcHashBits(
|
||||
size_t capacity, size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
size_t handle_charge =
|
||||
CalcEstimatedHandleCharge(estimated_value_size, metadata_charge_policy);
|
||||
assert(handle_charge > 0);
|
||||
uint32_t num_entries =
|
||||
static_cast<uint32_t>(capacity / (kLoadFactor * handle_charge)) + 1;
|
||||
assert(num_entries <= uint32_t{1} << 31);
|
||||
return FloorLog2((num_entries << 1) - 1);
|
||||
}
|
||||
|
||||
void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
autovector<LRUHandle> last_reference_list;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
if (capacity > capacity_) {
|
||||
assert(false); // Not supported.
|
||||
}
|
||||
capacity_ = capacity;
|
||||
EvictFromLRU(0, &last_reference_list);
|
||||
}
|
||||
|
||||
// Free the entries here outside of mutex for performance reasons.
|
||||
for (auto& h : last_reference_list) {
|
||||
h.FreeData();
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::SetStrictCapacityLimit(bool strict_capacity_limit) {
|
||||
DMutexLock l(mutex_);
|
||||
strict_capacity_limit_ = strict_capacity_limit;
|
||||
}
|
||||
|
||||
Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t charge, Cache::DeleterFn deleter,
|
||||
LRUHandle** handle, Cache::Priority /*priority*/) {
|
||||
if (key.size() != kCacheKeySize) {
|
||||
return Status::NotSupported("FastLRUCache only supports key size " +
|
||||
std::to_string(kCacheKeySize) + "B");
|
||||
}
|
||||
|
||||
LRUHandle tmp;
|
||||
tmp.value = value;
|
||||
tmp.deleter = deleter;
|
||||
tmp.hash = hash;
|
||||
tmp.CalcTotalCharge(charge, metadata_charge_policy_);
|
||||
for (int i = 0; i < kCacheKeySize; i++) {
|
||||
tmp.key_data[i] = key.data()[i];
|
||||
}
|
||||
|
||||
Status s = Status::OK();
|
||||
autovector<LRUHandle> last_reference_list;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
assert(table_.GetOccupancy() <= table_.GetOccupancyLimit());
|
||||
|
||||
// Free the space following strict LRU policy until enough space
|
||||
// is freed or the lru list is empty.
|
||||
EvictFromLRU(tmp.total_charge, &last_reference_list);
|
||||
if ((usage_ + tmp.total_charge > capacity_ &&
|
||||
(strict_capacity_limit_ || handle == nullptr)) ||
|
||||
table_.GetOccupancy() == table_.GetOccupancyLimit()) {
|
||||
// There are two measures of capacity:
|
||||
// - Space (or charge) capacity: The maximum possible sum of the charges
|
||||
// of the elements.
|
||||
// - Table capacity: The number of slots in the hash table.
|
||||
// These are incomparable, in the sense that one doesn't imply the other.
|
||||
// Typically we will reach space capacity before table capacity---
|
||||
// if the user always inserts values with size equal to
|
||||
// estimated_value_size, then at most a kLoadFactor fraction of slots
|
||||
// will ever be occupied. But in some cases we may reach table capacity
|
||||
// before space capacity---if the user initially claims a very large
|
||||
// estimated_value_size but then inserts tiny values, more elements than
|
||||
// initially estimated will be inserted.
|
||||
|
||||
// TODO(Guido) Some tests (at least two from cache_test, as well as the
|
||||
// stress tests) currently assume the table capacity is unbounded.
|
||||
if (handle == nullptr) {
|
||||
// Don't insert the entry but still return ok, as if the entry inserted
|
||||
// into cache and get evicted immediately.
|
||||
last_reference_list.push_back(tmp);
|
||||
} else {
|
||||
if (table_.GetOccupancy() == table_.GetOccupancyLimit()) {
|
||||
// TODO: Consider using a distinct status for this case, but usually
|
||||
// it will be handled the same way as reaching charge capacity limit
|
||||
s = Status::MemoryLimit(
|
||||
"Insert failed because all slots in the hash table are full.");
|
||||
} else {
|
||||
s = Status::MemoryLimit(
|
||||
"Insert failed because the total charge has exceeded the "
|
||||
"capacity.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Insert into the cache. Note that the cache might get larger than its
|
||||
// capacity if not enough space was freed up.
|
||||
LRUHandle* old;
|
||||
LRUHandle* h = table_.Insert(&tmp, &old);
|
||||
assert(h != nullptr); // We're below occupancy, so this insertion should
|
||||
// never fail.
|
||||
usage_ += h->total_charge;
|
||||
if (old != nullptr) {
|
||||
s = Status::OkOverwritten();
|
||||
assert(old->IsVisible());
|
||||
table_.Exclude(old);
|
||||
if (!old->HasRefs()) {
|
||||
// old is on LRU because it's in cache and its reference count is 0.
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old);
|
||||
assert(usage_ >= old->total_charge);
|
||||
usage_ -= old->total_charge;
|
||||
last_reference_list.push_back(*old);
|
||||
}
|
||||
}
|
||||
if (handle == nullptr) {
|
||||
LRU_Insert(h);
|
||||
} else {
|
||||
// If caller already holds a ref, no need to take one here.
|
||||
if (!h->HasRefs()) {
|
||||
h->Ref();
|
||||
}
|
||||
*handle = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entries here outside of mutex for performance reasons.
|
||||
for (auto& h : last_reference_list) {
|
||||
h.FreeData();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
LRUHandle* LRUCacheShard::Lookup(const Slice& key, uint32_t hash) {
|
||||
LRUHandle* h = nullptr;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
h = table_.Lookup(key, hash);
|
||||
if (h != nullptr) {
|
||||
assert(h->IsVisible());
|
||||
if (!h->HasRefs()) {
|
||||
// The entry is in LRU since it's in hash and has no external
|
||||
// references.
|
||||
LRU_Remove(h);
|
||||
}
|
||||
h->Ref();
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
bool LRUCacheShard::Ref(LRUHandle* h) {
|
||||
DMutexLock l(mutex_);
|
||||
// To create another reference - entry must be already externally referenced.
|
||||
assert(h->HasRefs());
|
||||
h->Ref();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LRUCacheShard::Release(LRUHandle* h, bool erase_if_last_ref) {
|
||||
if (h == nullptr) {
|
||||
return false;
|
||||
}
|
||||
LRUHandle copy;
|
||||
bool last_reference = false;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
last_reference = h->Unref();
|
||||
if (last_reference && h->IsVisible()) {
|
||||
// The item is still in cache, and nobody else holds a reference to it.
|
||||
if (usage_ > capacity_ || erase_if_last_ref) {
|
||||
// The LRU list must be empty since the cache is full.
|
||||
assert(lru_.next == &lru_ || erase_if_last_ref);
|
||||
// Take this opportunity and remove the item.
|
||||
table_.Remove(h);
|
||||
} else {
|
||||
// Put the item back on the LRU list, and don't free it.
|
||||
LRU_Insert(h);
|
||||
last_reference = false;
|
||||
}
|
||||
}
|
||||
// If it was the last reference, then decrement the cache usage.
|
||||
if (last_reference) {
|
||||
assert(usage_ >= h->total_charge);
|
||||
usage_ -= h->total_charge;
|
||||
copy = *h;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the entry here outside of mutex for performance reasons.
|
||||
if (last_reference) {
|
||||
copy.FreeData();
|
||||
}
|
||||
return last_reference;
|
||||
}
|
||||
|
||||
void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
LRUHandle copy;
|
||||
bool last_reference = false;
|
||||
{
|
||||
DMutexLock l(mutex_);
|
||||
LRUHandle* h = table_.Lookup(key, hash);
|
||||
if (h != nullptr) {
|
||||
table_.Exclude(h);
|
||||
if (!h->HasRefs()) {
|
||||
// The entry is in LRU since it's in cache and has no external
|
||||
// references.
|
||||
LRU_Remove(h);
|
||||
table_.Remove(h);
|
||||
assert(usage_ >= h->total_charge);
|
||||
usage_ -= h->total_charge;
|
||||
last_reference = true;
|
||||
copy = *h;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Free the entry here outside of mutex for performance reasons.
|
||||
// last_reference will only be true if e != nullptr.
|
||||
if (last_reference) {
|
||||
copy.FreeData();
|
||||
}
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetUsage() const {
|
||||
DMutexLock l(mutex_);
|
||||
return usage_;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetPinnedUsage() const {
|
||||
DMutexLock l(mutex_);
|
||||
assert(usage_ >= lru_usage_);
|
||||
return usage_ - lru_usage_;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetOccupancyCount() const {
|
||||
DMutexLock l(mutex_);
|
||||
return table_.GetOccupancy();
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetTableAddressCount() const {
|
||||
DMutexLock l(mutex_);
|
||||
return table_.GetTableSize();
|
||||
}
|
||||
|
||||
LRUCache::LRUCache(size_t capacity, size_t estimated_value_size,
|
||||
int num_shard_bits, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
nullptr /*allocator*/) {
|
||||
assert(estimated_value_size > 0 ||
|
||||
metadata_charge_policy != kDontChargeCacheMetadata);
|
||||
size_t per_shard = GetPerShardCapacity();
|
||||
InitShards([=](LRUCacheShard* cs) {
|
||||
new (cs) LRUCacheShard(per_shard, estimated_value_size,
|
||||
strict_capacity_limit, metadata_charge_policy);
|
||||
});
|
||||
}
|
||||
|
||||
void* LRUCache::Value(Handle* handle) {
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->value;
|
||||
}
|
||||
|
||||
size_t LRUCache::GetCharge(Handle* handle) const {
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
GetShard(0).metadata_charge_policy_);
|
||||
}
|
||||
|
||||
Cache::DeleterFn LRUCache::GetDeleter(Handle* handle) const {
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
return h->deleter;
|
||||
}
|
||||
|
||||
} // namespace fast_lru_cache
|
||||
|
||||
std::shared_ptr<Cache> NewFastLRUCache(
|
||||
size_t capacity, size_t estimated_value_size, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
if (num_shard_bits >= 20) {
|
||||
return nullptr; // The cache cannot be sharded into too many fine pieces.
|
||||
}
|
||||
if (num_shard_bits < 0) {
|
||||
num_shard_bits = GetDefaultCacheShardBits(capacity);
|
||||
}
|
||||
return std::make_shared<fast_lru_cache::LRUCache>(
|
||||
capacity, estimated_value_size, num_shard_bits, strict_capacity_limit,
|
||||
metadata_charge_policy);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-477
@@ -1,477 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/sharded_cache.h"
|
||||
#include "port/lang.h"
|
||||
#include "port/malloc.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace fast_lru_cache {
|
||||
|
||||
// Forward declaration of friend class.
|
||||
class FastLRUCacheTest;
|
||||
|
||||
// LRU cache implementation using an open-address hash table.
|
||||
//
|
||||
// Every slot in the hash table is an LRUHandle. Because handles can be
|
||||
// referenced externally, we can't discard them immediately once they are
|
||||
// deleted (via a delete or an LRU eviction) or replaced by a new version
|
||||
// (via an insert of the same key). The state of an element is defined by
|
||||
// the following two properties:
|
||||
// (R) Referenced: An element can be referenced externally (refs > 0), or not.
|
||||
// Importantly, an element can be evicted if and only if it's not
|
||||
// referenced. In particular, when an element becomes referenced, it's
|
||||
// temporarily taken out of the LRU list until all references to it
|
||||
// are dropped.
|
||||
// (V) Visible: An element can visible for lookups (IS_VISIBLE set), or not.
|
||||
// Initially, every element is visible. An element that is not visible is
|
||||
// called a ghost.
|
||||
// These properties induce 4 different states, with transitions defined as
|
||||
// follows:
|
||||
// - V --> not V: When a visible element is deleted or replaced by a new
|
||||
// version.
|
||||
// - Not V --> V: This cannot happen. A ghost remains in that state until it's
|
||||
// not referenced any more, at which point it's ready to be removed from the
|
||||
// hash table. (A ghost simply waits to transition to the afterlife---it will
|
||||
// never be visible again.)
|
||||
// - R --> not R: When all references to an element are dropped.
|
||||
// - Not R --> R: When an unreferenced element becomes referenced. This can only
|
||||
// happen if the element is V, since references to an element can only be
|
||||
// created when it's visible.
|
||||
//
|
||||
// Internally, the cache uses an open-addressed hash table to index the handles.
|
||||
// We use tombstone counters to keep track of displacements.
|
||||
// Because of the tombstones and the two possible visibility states of an
|
||||
// element, the table slots can be in 4 different states:
|
||||
// 1. Visible element (IS_ELEMENT set and IS_VISIBLE set): The slot contains a
|
||||
// key-value element.
|
||||
// 2. Ghost element (IS_ELEMENT set and IS_VISIBLE unset): The slot contains an
|
||||
// element that has been removed, but it's still referenced. It's invisible
|
||||
// to lookups.
|
||||
// 3. Tombstone (IS_ELEMENT unset and displacements > 0): The slot contains a
|
||||
// tombstone.
|
||||
// 4. Empty (IS_ELEMENT unset and displacements == 0): The slot is unused.
|
||||
// A slot that is an element can further have IS_VISIBLE set or not.
|
||||
// When a ghost is removed from the table, it can either transition to being a
|
||||
// tombstone or an empty slot, depending on the number of displacements of the
|
||||
// slot. In any case, the slot becomes available. When a handle is inserted
|
||||
// into that slot, it becomes a visible element again.
|
||||
|
||||
// The load factor p is a real number in (0, 1) such that at all
|
||||
// times at most a fraction p of all slots, without counting tombstones,
|
||||
// are occupied by elements. This means that the probability that a
|
||||
// random probe hits an empty slot is at most p, and thus at most 1/p probes
|
||||
// are required on average. For example, p = 70% implies that between 1 and 2
|
||||
// probes are needed on average (bear in mind that this reasoning doesn't
|
||||
// consider the effects of clustering over time).
|
||||
// Because the size of the hash table is always rounded up to the next
|
||||
// power of 2, p is really an upper bound on the actual load factor---the
|
||||
// actual load factor is anywhere between p/2 and p. This is a bit wasteful,
|
||||
// but bear in mind that slots only hold metadata, not actual values.
|
||||
// Since space cost is dominated by the values (the LSM blocks),
|
||||
// overprovisioning the table with metadata only increases the total cache space
|
||||
// usage by a tiny fraction.
|
||||
constexpr double kLoadFactor = 0.35;
|
||||
|
||||
// The user can exceed kLoadFactor if the sizes of the inserted values don't
|
||||
// match estimated_value_size, or if strict_capacity_limit == false. To
|
||||
// avoid performance to plunge, we set a strict upper bound on the load factor.
|
||||
constexpr double kStrictLoadFactor = 0.7;
|
||||
|
||||
// Arbitrary seeds.
|
||||
constexpr uint32_t kProbingSeed1 = 0xbc9f1d34;
|
||||
constexpr uint32_t kProbingSeed2 = 0x7a2bb9d5;
|
||||
|
||||
// An experimental (under development!) alternative to LRUCache
|
||||
|
||||
struct LRUHandle {
|
||||
void* value;
|
||||
Cache::DeleterFn deleter;
|
||||
LRUHandle* next;
|
||||
LRUHandle* prev;
|
||||
size_t total_charge; // TODO(opt): Only allow uint32_t?
|
||||
// The hash of key(). Used for fast sharding and comparisons.
|
||||
uint32_t hash;
|
||||
// The number of external refs to this entry.
|
||||
uint32_t refs;
|
||||
|
||||
enum Flags : uint8_t {
|
||||
// Whether the handle is visible to Lookups.
|
||||
IS_VISIBLE = (1 << 0),
|
||||
// Whether the slot is in use by an element.
|
||||
IS_ELEMENT = (1 << 1),
|
||||
};
|
||||
uint8_t flags;
|
||||
|
||||
// The number of elements that hash to this slot or a lower one,
|
||||
// but wind up in a higher slot.
|
||||
uint32_t displacements;
|
||||
|
||||
std::array<char, kCacheKeySize> key_data;
|
||||
|
||||
LRUHandle() {
|
||||
value = nullptr;
|
||||
deleter = nullptr;
|
||||
next = nullptr;
|
||||
prev = nullptr;
|
||||
total_charge = 0;
|
||||
hash = 0;
|
||||
refs = 0;
|
||||
flags = 0;
|
||||
displacements = 0;
|
||||
key_data.fill(0);
|
||||
}
|
||||
|
||||
Slice key() const { return Slice(key_data.data(), kCacheKeySize); }
|
||||
|
||||
// For HandleImpl concept
|
||||
uint32_t GetHash() const { return hash; }
|
||||
|
||||
// Increase the reference count by 1.
|
||||
void Ref() { refs++; }
|
||||
|
||||
// Just reduce the reference count by 1. Return true if it was last reference.
|
||||
bool Unref() {
|
||||
assert(refs > 0);
|
||||
refs--;
|
||||
return refs == 0;
|
||||
}
|
||||
|
||||
// Return true if there are external refs, false otherwise.
|
||||
bool HasRefs() const { return refs > 0; }
|
||||
|
||||
bool IsVisible() const { return flags & IS_VISIBLE; }
|
||||
|
||||
void SetIsVisible(bool is_visible) {
|
||||
if (is_visible) {
|
||||
flags |= IS_VISIBLE;
|
||||
} else {
|
||||
flags &= ~IS_VISIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsElement() const { return flags & IS_ELEMENT; }
|
||||
|
||||
void SetIsElement(bool is_element) {
|
||||
if (is_element) {
|
||||
flags |= IS_ELEMENT;
|
||||
} else {
|
||||
flags &= ~IS_ELEMENT;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeData() {
|
||||
assert(refs == 0);
|
||||
if (deleter) {
|
||||
(*deleter)(key(), value);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the memory usage by metadata.
|
||||
inline size_t CalcMetaCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) const {
|
||||
if (metadata_charge_policy != kFullChargeCacheMetadata) {
|
||||
return 0;
|
||||
} else {
|
||||
// #ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
// return malloc_usable_size(
|
||||
// const_cast<void*>(static_cast<const void*>(this)));
|
||||
// #else
|
||||
// TODO(Guido) malloc_usable_size only works when we call it on
|
||||
// a pointer allocated with malloc. Because our handles are all
|
||||
// allocated in a single shot as an array, the user can't call
|
||||
// CalcMetaCharge (or CalcTotalCharge or GetCharge) on a handle
|
||||
// pointer returned by the cache. Moreover, malloc_usable_size
|
||||
// expects a heap-allocated handle, but sometimes in our code we
|
||||
// wish to pass a stack-allocated handle (this is only a performance
|
||||
// concern).
|
||||
// What is the right way to compute metadata charges with pre-allocated
|
||||
// handles?
|
||||
return sizeof(LRUHandle);
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
inline void CalcTotalCharge(
|
||||
size_t charge, CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
total_charge = charge + CalcMetaCharge(metadata_charge_policy);
|
||||
}
|
||||
|
||||
inline size_t GetCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) const {
|
||||
size_t meta_charge = CalcMetaCharge(metadata_charge_policy);
|
||||
assert(total_charge >= meta_charge);
|
||||
return total_charge - meta_charge;
|
||||
}
|
||||
|
||||
inline bool IsEmpty() {
|
||||
return !this->IsElement() && this->displacements == 0;
|
||||
}
|
||||
|
||||
inline bool IsTombstone() {
|
||||
return !this->IsElement() && this->displacements > 0;
|
||||
}
|
||||
|
||||
inline bool Matches(const Slice& some_key, uint32_t some_hash) {
|
||||
return this->IsElement() && this->hash == some_hash &&
|
||||
this->key() == some_key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class LRUHandleTable {
|
||||
public:
|
||||
explicit LRUHandleTable(int hash_bits);
|
||||
~LRUHandleTable();
|
||||
|
||||
// Returns a pointer to a visible element matching the key/hash, or
|
||||
// nullptr if not present.
|
||||
LRUHandle* Lookup(const Slice& key, uint32_t hash);
|
||||
|
||||
// Inserts a copy of h into the hash table.
|
||||
// Returns a pointer to the inserted handle, or nullptr if no slot
|
||||
// available was found. If an existing visible element matching the
|
||||
// key/hash is already present in the hash table, the argument old
|
||||
// is set to pointe to it; otherwise, it's set to nullptr.
|
||||
LRUHandle* Insert(LRUHandle* h, LRUHandle** old);
|
||||
|
||||
// Removes h from the hash table. The handle must already be off
|
||||
// the LRU list.
|
||||
void Remove(LRUHandle* h);
|
||||
|
||||
// Turns a visible element h into a ghost (i.e., not visible).
|
||||
void Exclude(LRUHandle* h);
|
||||
|
||||
// Assigns a copy of h to the given slot.
|
||||
void Assign(int slot, LRUHandle* h);
|
||||
|
||||
template <typename T>
|
||||
void ApplyToEntriesRange(T func, size_t index_begin, size_t index_end) {
|
||||
for (size_t i = index_begin; i < index_end; i++) {
|
||||
LRUHandle* h = &array_[i];
|
||||
if (h->IsVisible()) {
|
||||
func(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetTableSize() const { return uint32_t{1} << length_bits_; }
|
||||
|
||||
int GetLengthBits() const { return length_bits_; }
|
||||
|
||||
uint32_t GetOccupancyLimit() const { return occupancy_limit_; }
|
||||
|
||||
uint32_t GetOccupancy() const { return occupancy_; }
|
||||
|
||||
// Returns x mod 2^{length_bits_}.
|
||||
uint32_t ModTableSize(uint32_t x) { return x & length_bits_mask_; }
|
||||
|
||||
private:
|
||||
int FindVisibleElement(const Slice& key, uint32_t hash, int& probe,
|
||||
int displacement);
|
||||
|
||||
int FindAvailableSlot(const Slice& key, int& probe, int displacement);
|
||||
|
||||
int FindVisibleElementOrAvailableSlot(const Slice& key, uint32_t hash,
|
||||
int& probe, int displacement);
|
||||
|
||||
// Returns the index of the first slot probed (hashing with
|
||||
// the given key) with a handle e such that cond(e) is true.
|
||||
// Otherwise, if no match is found, returns -1.
|
||||
// For every handle e probed except the final slot, updates
|
||||
// e->displacements += displacement.
|
||||
// The argument probe is modified such that consecutive calls
|
||||
// to FindSlot continue probing right after where the previous
|
||||
// call left.
|
||||
int FindSlot(const Slice& key, std::function<bool(LRUHandle*)> cond,
|
||||
int& probe, int displacement);
|
||||
|
||||
// Number of hash bits used for table index.
|
||||
// The size of the table is 1 << length_bits_.
|
||||
int length_bits_;
|
||||
|
||||
const uint32_t length_bits_mask_;
|
||||
|
||||
// Number of elements in the table.
|
||||
uint32_t occupancy_;
|
||||
|
||||
// Maximum number of elements the user can store in the table.
|
||||
uint32_t occupancy_limit_;
|
||||
|
||||
std::unique_ptr<LRUHandle[]> array_;
|
||||
};
|
||||
|
||||
// A single shard of sharded cache.
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShardBase {
|
||||
public:
|
||||
LRUCacheShard(size_t capacity, size_t estimated_value_size,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
|
||||
// For CacheShard concept
|
||||
using HandleImpl = LRUHandle;
|
||||
|
||||
// Keep 32-bit hashing for now (FIXME: upgrade to 64-bit)
|
||||
using HashVal = uint32_t;
|
||||
using HashCref = uint32_t;
|
||||
static inline HashVal ComputeHash(const Slice& key) {
|
||||
return Lower32of64(GetSliceNPHash64(key));
|
||||
}
|
||||
static inline uint32_t HashPieceForSharding(HashCref hash) { return hash; }
|
||||
|
||||
// Separate from constructor so caller can easily make an array of LRUCache
|
||||
// if current usage is more than new capacity, the function will attempt to
|
||||
// free the needed space.
|
||||
void SetCapacity(size_t capacity);
|
||||
|
||||
// Set the flag to reject insertion if cache if full.
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit);
|
||||
|
||||
// Like Cache methods, but with an extra "hash" parameter.
|
||||
// Insert an item into the hash table and, if handle is null, insert into
|
||||
// the LRU list. Older items are evicted as necessary. If the cache is full
|
||||
// and free_handle_on_fail is true, the item is deleted and handle is set to
|
||||
// nullptr.
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
Cache::DeleterFn deleter, LRUHandle** handle,
|
||||
Cache::Priority priority);
|
||||
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value,
|
||||
const Cache::CacheItemHelper* helper, size_t charge,
|
||||
LRUHandle** handle, Cache::Priority priority) {
|
||||
return Insert(key, hash, value, charge, helper->del_cb, handle, priority);
|
||||
}
|
||||
|
||||
LRUHandle* Lookup(const Slice& key, uint32_t hash,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
const Cache::CreateCallback& /*create_cb*/,
|
||||
Cache::Priority /*priority*/, bool /*wait*/,
|
||||
Statistics* /*stats*/) {
|
||||
return Lookup(key, hash);
|
||||
}
|
||||
LRUHandle* Lookup(const Slice& key, uint32_t hash);
|
||||
|
||||
bool Release(LRUHandle* handle, bool /*useful*/, bool erase_if_last_ref) {
|
||||
return Release(handle, erase_if_last_ref);
|
||||
}
|
||||
bool IsReady(LRUHandle* /*handle*/) { return true; }
|
||||
void Wait(LRUHandle* /*handle*/) {}
|
||||
|
||||
bool Ref(LRUHandle* handle);
|
||||
bool Release(LRUHandle* handle, bool erase_if_last_ref = false);
|
||||
void Erase(const Slice& key, uint32_t hash);
|
||||
|
||||
size_t GetUsage() const;
|
||||
size_t GetPinnedUsage() const;
|
||||
size_t GetOccupancyCount() const;
|
||||
size_t GetTableAddressCount() const;
|
||||
|
||||
void ApplyToSomeEntries(
|
||||
const std::function<void(const Slice& key, void* value, size_t charge,
|
||||
DeleterFn deleter)>& callback,
|
||||
size_t average_entries_per_lock, size_t* state);
|
||||
|
||||
void EraseUnRefEntries();
|
||||
|
||||
private:
|
||||
friend class LRUCache;
|
||||
friend class FastLRUCacheTest;
|
||||
|
||||
void LRU_Remove(LRUHandle* e);
|
||||
void LRU_Insert(LRUHandle* e);
|
||||
|
||||
// Free some space following strict LRU policy until enough space
|
||||
// to hold (usage_ + charge) is freed or the LRU list is empty
|
||||
// This function is not thread safe - it needs to be executed while
|
||||
// holding the mutex_.
|
||||
void EvictFromLRU(size_t charge, autovector<LRUHandle>* deleted);
|
||||
|
||||
// Returns the charge of a single handle.
|
||||
static size_t CalcEstimatedHandleCharge(
|
||||
size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
|
||||
// Returns the number of bits used to hash an element in the hash
|
||||
// table.
|
||||
static int CalcHashBits(size_t capacity, size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
|
||||
// Initialized before use.
|
||||
size_t capacity_;
|
||||
|
||||
// Whether to reject insertion if cache reaches its full capacity.
|
||||
bool strict_capacity_limit_;
|
||||
|
||||
// Dummy head of LRU list.
|
||||
// lru.prev is newest entry, lru.next is oldest entry.
|
||||
// LRU contains items which can be evicted, ie reference only by cache
|
||||
LRUHandle lru_;
|
||||
|
||||
// Pointer to head of low-pri pool in LRU list.
|
||||
LRUHandle* lru_low_pri_;
|
||||
|
||||
// ------------^^^^^^^^^^^^^-----------
|
||||
// Not frequently modified data members
|
||||
// ------------------------------------
|
||||
//
|
||||
// We separate data members that are updated frequently from the ones that
|
||||
// are not frequently updated so that they don't share the same cache line
|
||||
// which will lead into false cache sharing
|
||||
//
|
||||
// ------------------------------------
|
||||
// Frequently modified data members
|
||||
// ------------vvvvvvvvvvvvv-----------
|
||||
LRUHandleTable table_;
|
||||
|
||||
// Memory size for entries residing in the cache.
|
||||
size_t usage_;
|
||||
|
||||
// Memory size for entries residing only in the LRU list.
|
||||
size_t lru_usage_;
|
||||
|
||||
// mutex_ protects the following state.
|
||||
// We don't count mutex_ as the cache's internal state so semantically we
|
||||
// don't mind mutex_ invoking the non-const actions.
|
||||
mutable DMutex mutex_;
|
||||
};
|
||||
|
||||
class LRUCache
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public ShardedCache<LRUCacheShard> {
|
||||
public:
|
||||
LRUCache(size_t capacity, size_t estimated_value_size, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDontChargeCacheMetadata);
|
||||
const char* Name() const override { return "LRUCache"; }
|
||||
void* Value(Handle* handle) override;
|
||||
size_t GetCharge(Handle* handle) const override;
|
||||
DeleterFn GetDeleter(Handle* handle) const override;
|
||||
};
|
||||
} // namespace fast_lru_cache
|
||||
|
||||
std::shared_ptr<Cache> NewFastLRUCache(
|
||||
size_t capacity, size_t estimated_value_size, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+85
-164
@@ -10,7 +10,6 @@
|
||||
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/clock_cache.h"
|
||||
#include "cache/fast_lru_cache.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "port/port.h"
|
||||
@@ -364,152 +363,14 @@ TEST_F(LRUCacheTest, EntriesWithPriority) {
|
||||
ValidateLRUList({"x", "y", "g", "z", "d", "m"}, 2, 2, 2);
|
||||
}
|
||||
|
||||
// TODO: FastLRUCache and ClockCache use the same tests. We can probably remove
|
||||
// them from FastLRUCache after ClockCache becomes productive, and we don't plan
|
||||
// to use or maintain FastLRUCache any more.
|
||||
namespace fast_lru_cache {
|
||||
|
||||
// TODO(guido) Replicate LRU policy tests from LRUCache here.
|
||||
class FastLRUCacheTest : public testing::Test {
|
||||
public:
|
||||
FastLRUCacheTest() {}
|
||||
~FastLRUCacheTest() override { DeleteCache(); }
|
||||
|
||||
void DeleteCache() {
|
||||
if (cache_ != nullptr) {
|
||||
cache_->~LRUCacheShard();
|
||||
port::cacheline_aligned_free(cache_);
|
||||
cache_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NewCache(size_t capacity) {
|
||||
DeleteCache();
|
||||
cache_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard)));
|
||||
new (cache_) LRUCacheShard(capacity, 1 /*estimated_value_size*/,
|
||||
false /*strict_capacity_limit*/,
|
||||
kDontChargeCacheMetadata);
|
||||
}
|
||||
|
||||
Status Insert(const std::string& key) {
|
||||
return cache_->Insert(key, 0 /*hash*/, nullptr /*value*/, 1 /*charge*/,
|
||||
nullptr /*deleter*/, nullptr /*handle*/,
|
||||
Cache::Priority::LOW);
|
||||
}
|
||||
|
||||
Status Insert(char key, size_t len) { return Insert(std::string(len, key)); }
|
||||
|
||||
size_t CalcEstimatedHandleChargeWrapper(
|
||||
size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
return LRUCacheShard::CalcEstimatedHandleCharge(estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
}
|
||||
|
||||
int CalcHashBitsWrapper(size_t capacity, size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
return LRUCacheShard::CalcHashBits(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
}
|
||||
|
||||
// Maximum number of items that a shard can hold.
|
||||
double CalcMaxOccupancy(size_t capacity, size_t estimated_value_size,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
size_t handle_charge = LRUCacheShard::CalcEstimatedHandleCharge(
|
||||
estimated_value_size, metadata_charge_policy);
|
||||
return capacity / (kLoadFactor * handle_charge);
|
||||
}
|
||||
bool TableSizeIsAppropriate(int hash_bits, double max_occupancy) {
|
||||
if (hash_bits == 0) {
|
||||
return max_occupancy <= 1;
|
||||
} else {
|
||||
return (1 << hash_bits >= max_occupancy) &&
|
||||
(1 << (hash_bits - 1) <= max_occupancy);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
LRUCacheShard* cache_ = nullptr;
|
||||
};
|
||||
|
||||
TEST_F(FastLRUCacheTest, ValidateKeySize) {
|
||||
NewCache(3);
|
||||
EXPECT_OK(Insert('a', 16));
|
||||
EXPECT_NOK(Insert('b', 15));
|
||||
EXPECT_OK(Insert('b', 16));
|
||||
EXPECT_NOK(Insert('c', 17));
|
||||
EXPECT_NOK(Insert('d', 1000));
|
||||
EXPECT_NOK(Insert('e', 11));
|
||||
EXPECT_NOK(Insert('f', 0));
|
||||
}
|
||||
|
||||
TEST_F(FastLRUCacheTest, CalcHashBitsTest) {
|
||||
size_t capacity;
|
||||
size_t estimated_value_size;
|
||||
double max_occupancy;
|
||||
int hash_bits;
|
||||
CacheMetadataChargePolicy metadata_charge_policy;
|
||||
// Vary the cache capacity, fix the element charge.
|
||||
for (int i = 0; i < 2048; i++) {
|
||||
capacity = i;
|
||||
estimated_value_size = 0;
|
||||
metadata_charge_policy = kFullChargeCacheMetadata;
|
||||
max_occupancy = CalcMaxOccupancy(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, max_occupancy));
|
||||
}
|
||||
// Fix the cache capacity, vary the element charge.
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
capacity = 1024;
|
||||
estimated_value_size = i;
|
||||
metadata_charge_policy = kFullChargeCacheMetadata;
|
||||
max_occupancy = CalcMaxOccupancy(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, max_occupancy));
|
||||
}
|
||||
// Zero-capacity cache, and only values have charge.
|
||||
capacity = 0;
|
||||
estimated_value_size = 1;
|
||||
metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, 0 /* max_occupancy */));
|
||||
// Zero-capacity cache, and only metadata has charge.
|
||||
capacity = 0;
|
||||
estimated_value_size = 0;
|
||||
metadata_charge_policy = kFullChargeCacheMetadata;
|
||||
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, 0 /* max_occupancy */));
|
||||
// Small cache, large elements.
|
||||
capacity = 1024;
|
||||
estimated_value_size = 8192;
|
||||
metadata_charge_policy = kFullChargeCacheMetadata;
|
||||
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, 0 /* max_occupancy */));
|
||||
// Large capacity.
|
||||
capacity = 31924172;
|
||||
estimated_value_size = 8192;
|
||||
metadata_charge_policy = kFullChargeCacheMetadata;
|
||||
max_occupancy =
|
||||
CalcMaxOccupancy(capacity, estimated_value_size, metadata_charge_policy);
|
||||
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||
metadata_charge_policy);
|
||||
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, max_occupancy));
|
||||
}
|
||||
|
||||
} // namespace fast_lru_cache
|
||||
|
||||
namespace hyper_clock_cache {
|
||||
namespace clock_cache {
|
||||
|
||||
class ClockCacheTest : public testing::Test {
|
||||
public:
|
||||
using Shard = HyperClockCache::Shard;
|
||||
using Table = HyperClockTable;
|
||||
using HandleImpl = Shard::HandleImpl;
|
||||
|
||||
ClockCacheTest() {}
|
||||
~ClockCacheTest() override { DeleteShard(); }
|
||||
|
||||
@@ -523,10 +384,13 @@ class ClockCacheTest : public testing::Test {
|
||||
|
||||
void NewShard(size_t capacity, bool strict_capacity_limit = true) {
|
||||
DeleteShard();
|
||||
shard_ = reinterpret_cast<ClockCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(ClockCacheShard)));
|
||||
new (shard_) ClockCacheShard(capacity, 1, strict_capacity_limit,
|
||||
kDontChargeCacheMetadata);
|
||||
shard_ =
|
||||
reinterpret_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
|
||||
|
||||
Table::Opts opts;
|
||||
opts.estimated_value_size = 1;
|
||||
new (shard_)
|
||||
Shard(capacity, strict_capacity_limit, kDontChargeCacheMetadata, opts);
|
||||
}
|
||||
|
||||
Status Insert(const UniqueId64x2& hashed_key,
|
||||
@@ -580,7 +444,7 @@ class ClockCacheTest : public testing::Test {
|
||||
return {(static_cast<uint64_t>(key) << 56) + 1234U, 5678U};
|
||||
}
|
||||
|
||||
ClockCacheShard* shard_ = nullptr;
|
||||
Shard* shard_ = nullptr;
|
||||
};
|
||||
|
||||
TEST_F(ClockCacheTest, Misc) {
|
||||
@@ -604,7 +468,8 @@ TEST_F(ClockCacheTest, Misc) {
|
||||
}
|
||||
|
||||
TEST_F(ClockCacheTest, Limits) {
|
||||
NewShard(3, false /*strict_capacity_limit*/);
|
||||
constexpr size_t kCapacity = 3;
|
||||
NewShard(kCapacity, false /*strict_capacity_limit*/);
|
||||
for (bool strict_capacity_limit : {false, true, false}) {
|
||||
SCOPED_TRACE("strict_capacity_limit = " +
|
||||
std::to_string(strict_capacity_limit));
|
||||
@@ -628,7 +493,7 @@ TEST_F(ClockCacheTest, Limits) {
|
||||
|
||||
// Single entry fills capacity
|
||||
{
|
||||
ClockHandle* h;
|
||||
HandleImpl* h;
|
||||
ASSERT_OK(shard_->Insert(TestKey(hkey), hkey, nullptr /*value*/,
|
||||
3 /*charge*/, nullptr /*deleter*/, &h,
|
||||
Cache::Priority::LOW));
|
||||
@@ -644,15 +509,17 @@ TEST_F(ClockCacheTest, Limits) {
|
||||
shard_->Release(h, false /*useful*/, false /*erase_if_last_ref*/);
|
||||
}
|
||||
|
||||
// Insert more than table size can handle (cleverly using zero-charge
|
||||
// entries) to exceed occupancy limit.
|
||||
// Insert more than table size can handle to exceed occupancy limit.
|
||||
// (Cleverly using mostly zero-charge entries, but some non-zero to
|
||||
// verify usage tracking on detached entries.)
|
||||
{
|
||||
size_t n = shard_->GetTableAddressCount() + 1;
|
||||
std::unique_ptr<ClockHandle* []> ha { new ClockHandle* [n] {} };
|
||||
std::unique_ptr<HandleImpl* []> ha { new HandleImpl* [n] {} };
|
||||
Status s;
|
||||
for (size_t i = 0; i < n && s.ok(); ++i) {
|
||||
hkey[1] = i;
|
||||
s = shard_->Insert(TestKey(hkey), hkey, nullptr /*value*/, 0 /*charge*/,
|
||||
s = shard_->Insert(TestKey(hkey), hkey, nullptr /*value*/,
|
||||
(i + kCapacity < n) ? 0 : 1 /*charge*/,
|
||||
nullptr /*deleter*/, &ha[i], Cache::Priority::LOW);
|
||||
if (i == 0) {
|
||||
EXPECT_OK(s);
|
||||
@@ -798,7 +665,7 @@ void IncrementIntDeleter(const Slice& /*key*/, void* value) {
|
||||
// Testing calls to CorrectNearOverflow in Release
|
||||
TEST_F(ClockCacheTest, ClockCounterOverflowTest) {
|
||||
NewShard(6, /*strict_capacity_limit*/ false);
|
||||
ClockHandle* h;
|
||||
HandleImpl* h;
|
||||
int deleted = 0;
|
||||
UniqueId64x2 hkey = TestHashedKey('x');
|
||||
ASSERT_OK(shard_->Insert(TestKey(hkey), hkey, &deleted, 1,
|
||||
@@ -840,18 +707,18 @@ TEST_F(ClockCacheTest, CollidingInsertEraseTest) {
|
||||
Slice key2 = TestKey(hkey2);
|
||||
UniqueId64x2 hkey3 = TestHashedKey('z');
|
||||
Slice key3 = TestKey(hkey3);
|
||||
ClockHandle* h1;
|
||||
HandleImpl* h1;
|
||||
ASSERT_OK(shard_->Insert(key1, hkey1, &deleted, 1, IncrementIntDeleter, &h1,
|
||||
Cache::Priority::HIGH));
|
||||
ClockHandle* h2;
|
||||
HandleImpl* h2;
|
||||
ASSERT_OK(shard_->Insert(key2, hkey2, &deleted, 1, IncrementIntDeleter, &h2,
|
||||
Cache::Priority::HIGH));
|
||||
ClockHandle* h3;
|
||||
HandleImpl* h3;
|
||||
ASSERT_OK(shard_->Insert(key3, hkey3, &deleted, 1, IncrementIntDeleter, &h3,
|
||||
Cache::Priority::HIGH));
|
||||
|
||||
// Can repeatedly lookup+release despite the hash collision
|
||||
ClockHandle* tmp_h;
|
||||
HandleImpl* tmp_h;
|
||||
for (bool erase_if_last_ref : {true, false}) { // but not last ref
|
||||
tmp_h = shard_->Lookup(key1, hkey1);
|
||||
ASSERT_EQ(h1, tmp_h);
|
||||
@@ -999,7 +866,7 @@ TEST_F(ClockCacheTest, TableSizesTest) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace hyper_clock_cache
|
||||
} // namespace clock_cache
|
||||
|
||||
class TestSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
@@ -1265,14 +1132,19 @@ TEST_F(LRUCacheSecondaryCacheTest, BasicTest) {
|
||||
nullptr /* memory_allocator */, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(2048);
|
||||
std::make_shared<TestSecondaryCache>(4096);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
std::shared_ptr<Statistics> stats = CreateDBStatistics();
|
||||
CacheKey k1 = CacheKey::CreateUniqueForCacheLifetime(cache.get());
|
||||
CacheKey k2 = CacheKey::CreateUniqueForCacheLifetime(cache.get());
|
||||
CacheKey k3 = CacheKey::CreateUniqueForCacheLifetime(cache.get());
|
||||
|
||||
Random rnd(301);
|
||||
// Start with warming k3
|
||||
std::string str3 = rnd.RandomString(1021);
|
||||
ASSERT_OK(secondary_cache->InsertSaved(k3.AsSlice(), str3));
|
||||
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
TestItem* item1 = new TestItem(str1.data(), str1.length());
|
||||
ASSERT_OK(cache->Insert(k1.AsSlice(), item1,
|
||||
@@ -1289,15 +1161,27 @@ TEST_F(LRUCacheSecondaryCacheTest, BasicTest) {
|
||||
cache->Lookup(k2.AsSlice(), &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
ASSERT_EQ(static_cast<TestItem*>(cache->Value(handle))->Size(), str2.size());
|
||||
cache->Release(handle);
|
||||
|
||||
// This lookup should promote k1 and demote k2
|
||||
handle =
|
||||
cache->Lookup(k1.AsSlice(), &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
ASSERT_EQ(static_cast<TestItem*>(cache->Value(handle))->Size(), str1.size());
|
||||
cache->Release(handle);
|
||||
ASSERT_EQ(secondary_cache->num_inserts(), 2u);
|
||||
ASSERT_EQ(secondary_cache->num_lookups(), 1u);
|
||||
|
||||
// This lookup should promote k3 and demote k1
|
||||
handle =
|
||||
cache->Lookup(k3.AsSlice(), &LRUCacheSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
ASSERT_EQ(static_cast<TestItem*>(cache->Value(handle))->Size(), str3.size());
|
||||
cache->Release(handle);
|
||||
|
||||
ASSERT_EQ(secondary_cache->num_inserts(), 3u);
|
||||
ASSERT_EQ(secondary_cache->num_lookups(), 2u);
|
||||
ASSERT_EQ(stats->getTickerCount(SECONDARY_CACHE_HITS),
|
||||
secondary_cache->num_lookups());
|
||||
PerfContext perf_ctx = *get_perf_context();
|
||||
@@ -1878,6 +1762,43 @@ TEST_F(DBSecondaryCacheTest, SecondaryCacheFailureTest) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryCacheTest, TestSecondaryWithCompressedCache) {
|
||||
if (!Snappy_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("Compressed cache test requires snappy support");
|
||||
return;
|
||||
}
|
||||
LRUCacheOptions opts(2000 /* capacity */, 0 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */,
|
||||
0.5 /* high_pri_pool_ratio */,
|
||||
nullptr /* memory_allocator */, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache(
|
||||
new TestSecondaryCache(2048 * 1024));
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache_compressed = cache;
|
||||
table_options.no_block_cache = true;
|
||||
table_options.block_size = 1234;
|
||||
Options options = GetDefaultOptions();
|
||||
options.compression = kSnappyCompression;
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 6;
|
||||
for (int i = 0; i < N; i++) {
|
||||
// Partly compressible
|
||||
std::string p_v = rnd.RandomString(507) + std::string(500, ' ');
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int i = 0; i < 2 * N; i++) {
|
||||
std::string v = Get(Key(i % N));
|
||||
ASSERT_EQ(1007, v.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LRUCacheSecondaryCacheTest, BasicWaitAllTest) {
|
||||
LRUCacheOptions opts(1024 /* capacity */, 2 /* num_shard_bits */,
|
||||
false /* strict_capacity_limit */,
|
||||
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
size_t SliceSize(void* obj) { return static_cast<Slice*>(obj)->size(); }
|
||||
|
||||
Status SliceSaveTo(void* from_obj, size_t from_offset, size_t length,
|
||||
void* out) {
|
||||
const Slice& slice = *static_cast<Slice*>(from_obj);
|
||||
std::memcpy(out, slice.data() + from_offset, length);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status SecondaryCache::InsertSaved(const Slice& key, const Slice& saved) {
|
||||
static Cache::CacheItemHelper helper{
|
||||
&SliceSize, &SliceSaveTo, GetNoopDeleterForRole<CacheEntryRole::kMisc>()};
|
||||
// NOTE: depends on Insert() being synchronous, not keeping pointer `&saved`
|
||||
return Insert(key, const_cast<Slice*>(&saved), &helper);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+1
-1
@@ -8,7 +8,7 @@ DB_STRESS_CMD?=./db_stress
|
||||
include common.mk
|
||||
|
||||
CRASHTEST_MAKE=$(MAKE) -f crash_test.mk
|
||||
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD)
|
||||
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --cleanup_cmd='$(DB_CLEANUP_CMD)'
|
||||
|
||||
.PHONY: crash_test crash_test_with_atomic_flush crash_test_with_txn \
|
||||
crash_test_with_best_efforts_recovery crash_test_with_ts \
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
|
||||
#include "memory/arena.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
|
||||
@@ -22,6 +22,8 @@ constexpr uint32_t kVersion1 = 1;
|
||||
|
||||
using ExpirationRange = std::pair<uint64_t, uint64_t>;
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Format of blob log file header (30 bytes):
|
||||
//
|
||||
// +--------------+---------+---------+-------+-------------+-------------------+
|
||||
@@ -35,6 +37,9 @@ using ExpirationRange = std::pair<uint64_t, uint64_t>;
|
||||
//
|
||||
// Expiration range in the header is a rough range based on
|
||||
// blob_db_options.ttl_range_secs.
|
||||
|
||||
// clang-format on
|
||||
|
||||
struct BlobLogHeader {
|
||||
static constexpr size_t kSize = 30;
|
||||
|
||||
@@ -57,6 +62,8 @@ struct BlobLogHeader {
|
||||
Status DecodeFrom(Slice slice);
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Format of blob log file footer (32 bytes):
|
||||
//
|
||||
// +--------------+------------+-------------------+------------+
|
||||
@@ -69,6 +76,9 @@ struct BlobLogHeader {
|
||||
//
|
||||
// Unlike the same field in file header, expiration range in the footer is the
|
||||
// range of smallest and largest expiration of the data in this file.
|
||||
|
||||
// clang-format on
|
||||
|
||||
struct BlobLogFooter {
|
||||
static constexpr size_t kSize = 32;
|
||||
|
||||
@@ -81,6 +91,8 @@ struct BlobLogFooter {
|
||||
Status DecodeFrom(Slice slice);
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Blob record format (32 bytes header + key + value):
|
||||
//
|
||||
// +------------+--------------+------------+------------+----------+---------+-----------+
|
||||
@@ -100,6 +112,9 @@ struct BlobLogFooter {
|
||||
//
|
||||
// We could use variable length encoding (Varint64) to save more space, but it
|
||||
// make reader more complicated.
|
||||
|
||||
// clang-format on
|
||||
|
||||
struct BlobLogRecord {
|
||||
// header include fields up to blob CRC
|
||||
static constexpr size_t kHeaderSize = 32;
|
||||
|
||||
@@ -779,7 +779,7 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
LRUCacheOptions co;
|
||||
co.capacity = 2 << 20; // 2MB
|
||||
co.capacity = 2 << 20; // 2MB
|
||||
co.num_shard_bits = 2;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
auto backing_cache = NewLRUCache(co);
|
||||
|
||||
+2
-1
@@ -281,7 +281,8 @@ Status BuildTable(
|
||||
meta->fd.file_size = file_size;
|
||||
meta->marked_for_compaction = builder->NeedCompact();
|
||||
assert(meta->fd.GetFileSize() > 0);
|
||||
tp = builder->GetTableProperties(); // refresh now that builder is finished
|
||||
tp = builder
|
||||
->GetTableProperties(); // refresh now that builder is finished
|
||||
if (memtable_payload_bytes != nullptr &&
|
||||
memtable_garbage_bytes != nullptr) {
|
||||
const CompactionIterationStats& ci_stats = c_iter.iter_stats();
|
||||
|
||||
+171
-172
@@ -49,32 +49,32 @@ static void StartPhase(const char* name) {
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning (disable: 4996) // getenv security warning
|
||||
#pragma warning(disable : 4996) // getenv security warning
|
||||
#endif
|
||||
static const char* GetTempDir(void) {
|
||||
const char* ret = getenv("TEST_TMPDIR");
|
||||
if (ret == NULL || ret[0] == '\0')
|
||||
const char* ret = getenv("TEST_TMPDIR");
|
||||
if (ret == NULL || ret[0] == '\0')
|
||||
#ifdef OS_WIN
|
||||
ret = getenv("TEMP");
|
||||
ret = getenv("TEMP");
|
||||
#else
|
||||
ret = "/tmp";
|
||||
ret = "/tmp";
|
||||
#endif
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#define CheckNoError(err) \
|
||||
if ((err) != NULL) { \
|
||||
#define CheckNoError(err) \
|
||||
if ((err) != NULL) { \
|
||||
fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
|
||||
abort(); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
#define CheckCondition(cond) \
|
||||
if (!(cond)) { \
|
||||
#define CheckCondition(cond) \
|
||||
if (!(cond)) { \
|
||||
fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \
|
||||
abort(); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
static void CheckEqual(const char* expected, const char* v, size_t n) {
|
||||
@@ -98,21 +98,15 @@ static void Free(char** ptr) {
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckValue(
|
||||
char* err,
|
||||
const char* expected,
|
||||
char** actual,
|
||||
size_t actual_length) {
|
||||
static void CheckValue(char* err, const char* expected, char** actual,
|
||||
size_t actual_length) {
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, *actual, actual_length);
|
||||
Free(actual);
|
||||
}
|
||||
|
||||
static void CheckGet(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key,
|
||||
const char* expected) {
|
||||
static void CheckGet(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
const char* key, const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
@@ -122,12 +116,9 @@ static void CheckGet(
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckGetCF(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* handle,
|
||||
const char* key,
|
||||
const char* expected) {
|
||||
static void CheckGetCF(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* handle, const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
@@ -174,8 +165,8 @@ static void CheckMultiGetValues(size_t num_keys, char** values,
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckIter(rocksdb_iterator_t* iter,
|
||||
const char* key, const char* val) {
|
||||
static void CheckIter(rocksdb_iterator_t* iter, const char* key,
|
||||
const char* val) {
|
||||
size_t len;
|
||||
const char* str;
|
||||
str = rocksdb_iter_key(iter, &len);
|
||||
@@ -185,10 +176,9 @@ static void CheckIter(rocksdb_iterator_t* iter,
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate()
|
||||
static void CheckPut(void* ptr,
|
||||
const char* k, size_t klen,
|
||||
const char* v, size_t vlen) {
|
||||
int* state = (int*) ptr;
|
||||
static void CheckPut(void* ptr, const char* k, size_t klen, const char* v,
|
||||
size_t vlen) {
|
||||
int* state = (int*)ptr;
|
||||
CheckCondition(*state < 2);
|
||||
switch (*state) {
|
||||
case 0:
|
||||
@@ -205,7 +195,7 @@ static void CheckPut(void* ptr,
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate()
|
||||
static void CheckDel(void* ptr, const char* k, size_t klen) {
|
||||
int* state = (int*) ptr;
|
||||
int* state = (int*)ptr;
|
||||
CheckCondition(*state == 2);
|
||||
CheckEqual("bar", k, klen);
|
||||
(*state)++;
|
||||
@@ -213,14 +203,16 @@ static void CheckDel(void* ptr, const char* k, size_t klen) {
|
||||
|
||||
static void CmpDestroy(void* arg) { (void)arg; }
|
||||
|
||||
static int CmpCompare(void* arg, const char* a, size_t alen,
|
||||
const char* b, size_t blen) {
|
||||
static int CmpCompare(void* arg, const char* a, size_t alen, const char* b,
|
||||
size_t blen) {
|
||||
(void)arg;
|
||||
size_t n = (alen < blen) ? alen : blen;
|
||||
int r = memcmp(a, b, n);
|
||||
if (r == 0) {
|
||||
if (alen < blen) r = -1;
|
||||
else if (alen > blen) r = +1;
|
||||
if (alen < blen)
|
||||
r = -1;
|
||||
else if (alen > blen)
|
||||
r = +1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -405,11 +397,9 @@ static const char* MergeOperatorName(void* arg) {
|
||||
return "TestMergeOperator";
|
||||
}
|
||||
static char* MergeOperatorFullMerge(
|
||||
void* arg,
|
||||
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,
|
||||
void* arg, 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) {
|
||||
(void)arg;
|
||||
(void)key;
|
||||
@@ -425,12 +415,12 @@ static char* MergeOperatorFullMerge(
|
||||
memcpy(result, "fake", 4);
|
||||
return result;
|
||||
}
|
||||
static char* MergeOperatorPartialMerge(
|
||||
void* arg,
|
||||
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) {
|
||||
static char* MergeOperatorPartialMerge(void* arg, 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) {
|
||||
(void)arg;
|
||||
(void)key;
|
||||
(void)key_length;
|
||||
@@ -444,18 +434,16 @@ static char* MergeOperatorPartialMerge(
|
||||
return result;
|
||||
}
|
||||
|
||||
static void CheckTxnGet(
|
||||
rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transaction_get(txn, options, key, strlen(key), &val_len, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
static void CheckTxnGet(rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options, const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transaction_get(txn, options, key, strlen(key), &val_len, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnGetCF(rocksdb_transaction_t* txn,
|
||||
@@ -502,11 +490,9 @@ static void CheckTxnPinGetCF(rocksdb_transaction_t* txn,
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
static void CheckTxnDBGet(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key,
|
||||
const char* expected) {
|
||||
static void CheckTxnDBGet(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options, const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
@@ -632,7 +618,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_t* db;
|
||||
rocksdb_comparator_t* cmp;
|
||||
rocksdb_cache_t* cache;
|
||||
rocksdb_dbpath_t *dbpath;
|
||||
rocksdb_dbpath_t* dbpath;
|
||||
rocksdb_env_t* env;
|
||||
rocksdb_options_t* options;
|
||||
rocksdb_compactoptions_t* coptions;
|
||||
@@ -649,30 +635,20 @@ int main(int argc, char** argv) {
|
||||
char* err = NULL;
|
||||
int run = -1;
|
||||
|
||||
snprintf(dbname, sizeof(dbname),
|
||||
"%s/rocksdb_c_test-%d",
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
snprintf(dbbackupname, sizeof(dbbackupname),
|
||||
"%s/rocksdb_c_test-%d-backup",
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
snprintf(dbcheckpointname, sizeof(dbcheckpointname),
|
||||
"%s/rocksdb_c_test-%d-checkpoint",
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
snprintf(sstfilename, sizeof(sstfilename),
|
||||
"%s/rocksdb_c_test-%d-sst",
|
||||
GetTempDir(),
|
||||
snprintf(dbname, sizeof(dbname), "%s/rocksdb_c_test-%d", GetTempDir(),
|
||||
((int)geteuid()));
|
||||
|
||||
snprintf(dbpathname, sizeof(dbpathname),
|
||||
"%s/rocksdb_c_test-%d-dbpath",
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
snprintf(dbbackupname, sizeof(dbbackupname), "%s/rocksdb_c_test-%d-backup",
|
||||
GetTempDir(), ((int)geteuid()));
|
||||
|
||||
snprintf(dbcheckpointname, sizeof(dbcheckpointname),
|
||||
"%s/rocksdb_c_test-%d-checkpoint", GetTempDir(), ((int)geteuid()));
|
||||
|
||||
snprintf(sstfilename, sizeof(sstfilename), "%s/rocksdb_c_test-%d-sst",
|
||||
GetTempDir(), ((int)geteuid()));
|
||||
|
||||
snprintf(dbpathname, sizeof(dbpathname), "%s/rocksdb_c_test-%d-dbpath",
|
||||
GetTempDir(), ((int)geteuid()));
|
||||
|
||||
StartPhase("create_objects");
|
||||
cmp = rocksdb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
|
||||
@@ -746,7 +722,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_destroy_db(options, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_backup_engine_t *be = rocksdb_backup_engine_open(options, dbbackupname, &err);
|
||||
rocksdb_backup_engine_t* be =
|
||||
rocksdb_backup_engine_open(options, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_backup_engine_create_new_backup(be, db, &err);
|
||||
@@ -759,7 +736,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_backup_engine_create_new_backup(be, db, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
const rocksdb_backup_engine_info_t* bei = rocksdb_backup_engine_get_backup_info(be);
|
||||
const rocksdb_backup_engine_info_t* bei =
|
||||
rocksdb_backup_engine_get_backup_info(be);
|
||||
CheckCondition(rocksdb_backup_engine_info_count(bei) > 1);
|
||||
rocksdb_backup_engine_info_destroy(bei);
|
||||
|
||||
@@ -778,9 +756,11 @@ int main(int argc, char** argv) {
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_restore_options_t *restore_options = rocksdb_restore_options_create();
|
||||
rocksdb_restore_options_t* restore_options =
|
||||
rocksdb_restore_options_create();
|
||||
rocksdb_restore_options_set_keep_log_files(restore_options, 0);
|
||||
rocksdb_backup_engine_restore_db_from_latest_backup(be, dbname, dbname, restore_options, &err);
|
||||
rocksdb_backup_engine_restore_db_from_latest_backup(be, dbname, dbname,
|
||||
restore_options, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_restore_options_destroy(restore_options);
|
||||
|
||||
@@ -799,7 +779,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_destroy_db(options, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_t* checkpoint = rocksdb_checkpoint_object_create(db, &err);
|
||||
rocksdb_checkpoint_t* checkpoint =
|
||||
rocksdb_checkpoint_object_create(db, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
|
||||
@@ -976,10 +957,10 @@ int main(int argc, char** argv) {
|
||||
StartPhase("writebatch_vectors");
|
||||
{
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
const char* k_list[2] = { "z", "ap" };
|
||||
const size_t k_sizes[2] = { 1, 2 };
|
||||
const char* v_list[3] = { "x", "y", "z" };
|
||||
const size_t v_sizes[3] = { 1, 1, 1 };
|
||||
const char* k_list[2] = {"z", "ap"};
|
||||
const size_t k_sizes[2] = {1, 2};
|
||||
const char* v_list[3] = {"x", "y", "z"};
|
||||
const size_t v_sizes[3] = {1, 1, 1};
|
||||
rocksdb_writebatch_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
|
||||
rocksdb_write(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
@@ -1041,13 +1022,17 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(count == 3);
|
||||
size_t size;
|
||||
char* value;
|
||||
value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "box", 3, &size, &err);
|
||||
value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "box", 3, &size,
|
||||
&err);
|
||||
CheckValue(err, "c", &value, size);
|
||||
value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "bar", 3, &size, &err);
|
||||
value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "bar", 3, &size,
|
||||
&err);
|
||||
CheckValue(err, NULL, &value, size);
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions, "foo", 3, &size, &err);
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions,
|
||||
"foo", 3, &size, &err);
|
||||
CheckValue(err, "hello", &value, size);
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions, "box", 3, &size, &err);
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions,
|
||||
"box", 3, &size, &err);
|
||||
CheckValue(err, "c", &value, size);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wbi, &err);
|
||||
CheckNoError(err);
|
||||
@@ -1064,10 +1049,10 @@ int main(int argc, char** argv) {
|
||||
StartPhase("writebatch_wi_vectors");
|
||||
{
|
||||
rocksdb_writebatch_wi_t* wb = rocksdb_writebatch_wi_create(0, 1);
|
||||
const char* k_list[2] = { "z", "ap" };
|
||||
const size_t k_sizes[2] = { 1, 2 };
|
||||
const char* v_list[3] = { "x", "y", "z" };
|
||||
const size_t v_sizes[3] = { 1, 1, 1 };
|
||||
const char* k_list[2] = {"z", "ap"};
|
||||
const size_t k_sizes[2] = {1, 2};
|
||||
const char* v_list[3] = {"x", "y", "z"};
|
||||
const size_t v_sizes[3] = {1, 1, 1};
|
||||
rocksdb_writebatch_wi_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
@@ -1156,13 +1141,14 @@ int main(int argc, char** argv) {
|
||||
|
||||
StartPhase("multiget");
|
||||
{
|
||||
const char* keys[3] = { "box", "foo", "notfound" };
|
||||
const size_t keys_sizes[3] = { 3, 3, 8 };
|
||||
const char* keys[3] = {"box", "foo", "notfound"};
|
||||
const size_t keys_sizes[3] = {3, 3, 8};
|
||||
char* vals[3];
|
||||
size_t vals_sizes[3];
|
||||
char* errs[3];
|
||||
const char* expected[3] = {"c", "hello", NULL};
|
||||
rocksdb_multi_get(db, roptions, 3, keys, keys_sizes, vals, vals_sizes, errs);
|
||||
rocksdb_multi_get(db, roptions, 3, keys, keys_sizes, vals, vals_sizes,
|
||||
errs);
|
||||
CheckMultiGetValues(3, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
|
||||
@@ -1180,10 +1166,10 @@ int main(int argc, char** argv) {
|
||||
char keybuf[100];
|
||||
char valbuf[100];
|
||||
uint64_t sizes[2];
|
||||
const char* start[2] = { "a", "k00000000000000010000" };
|
||||
size_t start_len[2] = { 1, 21 };
|
||||
const char* limit[2] = { "k00000000000000010000", "z" };
|
||||
size_t limit_len[2] = { 21, 1 };
|
||||
const char* start[2] = {"a", "k00000000000000010000"};
|
||||
size_t start_len[2] = {1, 21};
|
||||
const char* limit[2] = {"k00000000000000010000", "z"};
|
||||
size_t limit_len[2] = {21, 1};
|
||||
rocksdb_writeoptions_set_sync(woptions, 0);
|
||||
for (i = 0; i < n; i++) {
|
||||
snprintf(keybuf, sizeof(keybuf), "k%020d", i);
|
||||
@@ -1393,8 +1379,8 @@ int main(int argc, char** argv) {
|
||||
factory);
|
||||
db = CheckCompaction(db, options_with_filter_factory, roptions, woptions);
|
||||
|
||||
rocksdb_options_set_compaction_filter_factory(
|
||||
options_with_filter_factory, NULL);
|
||||
rocksdb_options_set_compaction_filter_factory(options_with_filter_factory,
|
||||
NULL);
|
||||
rocksdb_options_destroy(options_with_filter_factory);
|
||||
}
|
||||
|
||||
@@ -1449,7 +1435,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_close(db);
|
||||
|
||||
size_t cflen;
|
||||
char** column_fams = rocksdb_list_column_families(db_options, dbname, &cflen, &err);
|
||||
char** column_fams =
|
||||
rocksdb_list_column_families(db_options, dbname, &cflen, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual("default", column_fams[0], 7);
|
||||
CheckEqual("cf1", column_fams[1], 3);
|
||||
@@ -1465,7 +1452,8 @@ int main(int argc, char** argv) {
|
||||
LoadAndCheckLatestOptions(dbname, env, false, cache, NULL, 2, cf_names,
|
||||
NULL);
|
||||
|
||||
db = rocksdb_open_column_families(db_options, dbname, 2, cf_names, cf_opts, handles, &err);
|
||||
db = rocksdb_open_column_families(db_options, dbname, 2, cf_names, cf_opts,
|
||||
handles, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_put_cf(db, woptions, handles[1], "foo", 3, "hello", 5, &err);
|
||||
@@ -1483,11 +1471,10 @@ int main(int argc, char** argv) {
|
||||
&err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_flushoptions_t *flush_options = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_t* flush_options = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_options, 1);
|
||||
rocksdb_flush_cf(db, flush_options, handles[1], &err);
|
||||
CheckNoError(err)
|
||||
rocksdb_flushoptions_destroy(flush_options);
|
||||
CheckNoError(err) rocksdb_flushoptions_destroy(flush_options);
|
||||
|
||||
CheckGetCF(db, roptions, handles[1], "foo", "hello");
|
||||
CheckPinGetCF(db, roptions, handles[1], "foo", "hello");
|
||||
@@ -1524,27 +1511,29 @@ int main(int argc, char** argv) {
|
||||
rocksdb_flush_wal(db, 1, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
const char* keys[3] = { "box", "box", "barfooxx" };
|
||||
const rocksdb_column_family_handle_t* get_handles[3] = { handles[0], handles[1], handles[1] };
|
||||
const size_t keys_sizes[3] = { 3, 3, 8 };
|
||||
const char* keys[3] = {"box", "box", "barfooxx"};
|
||||
const rocksdb_column_family_handle_t* get_handles[3] = {
|
||||
handles[0], handles[1], handles[1]};
|
||||
const size_t keys_sizes[3] = {3, 3, 8};
|
||||
char* vals[3];
|
||||
size_t vals_sizes[3];
|
||||
char* errs[3];
|
||||
rocksdb_multi_get_cf(db, roptions, get_handles, 3, keys, keys_sizes, vals, vals_sizes, errs);
|
||||
rocksdb_multi_get_cf(db, roptions, get_handles, 3, keys, keys_sizes, vals,
|
||||
vals_sizes, errs);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
CheckEqual(NULL, errs[i], 0);
|
||||
switch (i) {
|
||||
case 0:
|
||||
CheckEqual(NULL, vals[i], vals_sizes[i]); // wrong cf
|
||||
break;
|
||||
case 1:
|
||||
CheckEqual("c", vals[i], vals_sizes[i]); // bingo
|
||||
break;
|
||||
case 2:
|
||||
CheckEqual(NULL, vals[i], vals_sizes[i]); // normal not found
|
||||
break;
|
||||
case 0:
|
||||
CheckEqual(NULL, vals[i], vals_sizes[i]); // wrong cf
|
||||
break;
|
||||
case 1:
|
||||
CheckEqual("c", vals[i], vals_sizes[i]); // bingo
|
||||
break;
|
||||
case 2:
|
||||
CheckEqual(NULL, vals[i], vals_sizes[i]); // normal not found
|
||||
break;
|
||||
}
|
||||
Free(&vals[i]);
|
||||
}
|
||||
@@ -1592,7 +1581,8 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator_cf(db, roptions, handles[1]);
|
||||
rocksdb_iterator_t* iter =
|
||||
rocksdb_create_iterator_cf(db, roptions, handles[1]);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
@@ -1605,9 +1595,11 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
|
||||
rocksdb_column_family_handle_t* iters_cf_handles[2] = { handles[0], handles[1] };
|
||||
rocksdb_column_family_handle_t* iters_cf_handles[2] = {handles[0],
|
||||
handles[1]};
|
||||
rocksdb_iterator_t* iters_handles[2];
|
||||
rocksdb_create_iterators(db, roptions, iters_cf_handles, iters_handles, 2, &err);
|
||||
rocksdb_create_iterators(db, roptions, iters_cf_handles, iters_handles, 2,
|
||||
&err);
|
||||
CheckNoError(err);
|
||||
|
||||
iter = iters_handles[0];
|
||||
@@ -1652,7 +1644,8 @@ int main(int argc, char** argv) {
|
||||
{
|
||||
// Create new database
|
||||
rocksdb_options_set_allow_mmap_reads(options, 1);
|
||||
rocksdb_options_set_prefix_extractor(options, rocksdb_slicetransform_create_fixed_prefix(3));
|
||||
rocksdb_options_set_prefix_extractor(
|
||||
options, rocksdb_slicetransform_create_fixed_prefix(3));
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
|
||||
rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
|
||||
@@ -1747,8 +1740,9 @@ int main(int argc, char** argv) {
|
||||
// amount of memory used within memtables should grow
|
||||
CheckCondition(rocksdb_approximate_memory_usage_get_mem_table_total(mu2) >=
|
||||
rocksdb_approximate_memory_usage_get_mem_table_total(mu1));
|
||||
CheckCondition(rocksdb_approximate_memory_usage_get_mem_table_unflushed(mu2) >=
|
||||
rocksdb_approximate_memory_usage_get_mem_table_unflushed(mu1));
|
||||
CheckCondition(
|
||||
rocksdb_approximate_memory_usage_get_mem_table_unflushed(mu2) >=
|
||||
rocksdb_approximate_memory_usage_get_mem_table_unflushed(mu1));
|
||||
|
||||
rocksdb_memory_consumers_destroy(consumers);
|
||||
rocksdb_approximate_memory_usage_destroy(mu1);
|
||||
@@ -2839,53 +2833,57 @@ int main(int argc, char** argv) {
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_put(db, woptions, "a", 1, "0", 1, &err); CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "foo", 3, "bar", 3, &err); CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "foo1", 4, "bar1", 4, &err); CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "g1", 2, "0", 1, &err); CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "a", 1, "0", 1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "foo", 3, "bar", 3, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "foo1", 4, "bar1", 4, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "g1", 2, "0", 1, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// testing basic case with no iterate_upper_bound and no prefix_extractor
|
||||
{
|
||||
rocksdb_readoptions_set_iterate_upper_bound(roptions, NULL, 0);
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
|
||||
rocksdb_readoptions_set_iterate_upper_bound(roptions, NULL, 0);
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
|
||||
|
||||
rocksdb_iter_seek(iter, "foo", 3);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "foo", "bar");
|
||||
rocksdb_iter_seek(iter, "foo", 3);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "foo", "bar");
|
||||
|
||||
rocksdb_iter_next(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "foo1", "bar1");
|
||||
rocksdb_iter_next(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "foo1", "bar1");
|
||||
|
||||
rocksdb_iter_next(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "g1", "0");
|
||||
rocksdb_iter_next(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "g1", "0");
|
||||
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_iter_destroy(iter);
|
||||
}
|
||||
|
||||
// testing iterate_upper_bound and forward iterator
|
||||
// to make sure it stops at bound
|
||||
{
|
||||
// iterate_upper_bound points beyond the last expected entry
|
||||
rocksdb_readoptions_set_iterate_upper_bound(roptions, "foo2", 4);
|
||||
// iterate_upper_bound points beyond the last expected entry
|
||||
rocksdb_readoptions_set_iterate_upper_bound(roptions, "foo2", 4);
|
||||
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
|
||||
|
||||
rocksdb_iter_seek(iter, "foo", 3);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "foo", "bar");
|
||||
rocksdb_iter_seek(iter, "foo", 3);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "foo", "bar");
|
||||
|
||||
rocksdb_iter_next(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "foo1", "bar1");
|
||||
rocksdb_iter_next(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "foo1", "bar1");
|
||||
|
||||
rocksdb_iter_next(iter);
|
||||
// should stop here...
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_next(iter);
|
||||
// should stop here...
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_readoptions_set_iterate_upper_bound(roptions, NULL, 0);
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_readoptions_set_iterate_upper_bound(roptions, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3009,7 +3007,7 @@ int main(int argc, char** argv) {
|
||||
snapshot = rocksdb_transactiondb_create_snapshot(txn_db);
|
||||
rocksdb_readoptions_set_snapshot(roptions, snapshot);
|
||||
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hey", 3, &err);
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hey", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
CheckTxnDBGet(txn_db, roptions, "foo", "hello");
|
||||
@@ -3021,7 +3019,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
// iterate
|
||||
rocksdb_transaction_put(txn, "bar", 3, "hi", 2, &err);
|
||||
rocksdb_iterator_t* iter = rocksdb_transaction_create_iterator(txn, roptions);
|
||||
rocksdb_iterator_t* iter =
|
||||
rocksdb_transaction_create_iterator(txn, roptions);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
|
||||
+17
-16
@@ -192,8 +192,7 @@ Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
return Status::NotSupported(
|
||||
"More than one CF paths are only supported in "
|
||||
"universal and level compaction styles. ");
|
||||
} else if (cf_options.cf_paths.empty() &&
|
||||
db_options.db_paths.size() > 1) {
|
||||
} else if (cf_options.cf_paths.empty() && db_options.db_paths.size() > 1) {
|
||||
return Status::NotSupported(
|
||||
"More than one DB paths are only supported in "
|
||||
"universal and level compaction styles. ");
|
||||
@@ -205,7 +204,7 @@ Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
namespace {
|
||||
const uint64_t kDefaultTtl = 0xfffffffffffffffe;
|
||||
const uint64_t kDefaultPeriodicCompSecs = 0xfffffffffffffffe;
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src) {
|
||||
@@ -353,7 +352,8 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
// were not deleted yet, when we open the DB we will find these .trash files
|
||||
// and schedule them to be deleted (or delete immediately if SstFileManager
|
||||
// was not used)
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(db_options.sst_file_manager.get());
|
||||
auto sfm =
|
||||
static_cast<SstFileManagerImpl*>(db_options.sst_file_manager.get());
|
||||
for (size_t i = 0; i < result.cf_paths.size(); i++) {
|
||||
DeleteScheduler::CleanupDirectory(db_options.env, sfm,
|
||||
result.cf_paths[i].path)
|
||||
@@ -557,7 +557,6 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
next_(nullptr),
|
||||
prev_(nullptr),
|
||||
log_number_(0),
|
||||
flush_reason_(FlushReason::kOthers),
|
||||
column_family_set_(column_family_set),
|
||||
queued_for_flush_(false),
|
||||
queued_for_compaction_(false),
|
||||
@@ -610,8 +609,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
compaction_picker_.reset(
|
||||
new FIFOCompactionPicker(ioptions_, &internal_comparator_));
|
||||
} else if (ioptions_.compaction_style == kCompactionStyleNone) {
|
||||
compaction_picker_.reset(new NullCompactionPicker(
|
||||
ioptions_, &internal_comparator_));
|
||||
compaction_picker_.reset(
|
||||
new NullCompactionPicker(ioptions_, &internal_comparator_));
|
||||
ROCKS_LOG_WARN(ioptions_.logger,
|
||||
"Column family %s does not use any background compaction. "
|
||||
"Compactions can only be done via CompactFiles\n",
|
||||
@@ -878,7 +877,7 @@ int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
|
||||
return static_cast<int>(res);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
std::pair<WriteStallCondition, ColumnFamilyData::WriteStallCause>
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
@@ -919,7 +918,7 @@ ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
}
|
||||
|
||||
WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
auto write_stall_condition = WriteStallCondition::kNormal;
|
||||
if (current_ != nullptr) {
|
||||
auto* vstorage = current_->storage_info();
|
||||
@@ -1012,7 +1011,8 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
(compaction_needed_bytes -
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) >
|
||||
3 * (mutable_cf_options.hard_pending_compaction_bytes_limit -
|
||||
3 *
|
||||
(mutable_cf_options.hard_pending_compaction_bytes_limit -
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) /
|
||||
4;
|
||||
|
||||
@@ -1219,6 +1219,7 @@ Compaction* ColumnFamilyData::CompactRange(
|
||||
if (result != nullptr) {
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
TEST_SYNC_POINT("ColumnFamilyData::CompactRange:Return");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1302,8 +1303,8 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex) {
|
||||
void ColumnFamilyData::InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex) {
|
||||
db_mutex->AssertHeld();
|
||||
return InstallSuperVersion(sv_context, mutable_cf_options_);
|
||||
}
|
||||
@@ -1480,8 +1481,8 @@ Env::WriteLifeTimeHint ColumnFamilyData::CalculateSSTWriteHint(int level) {
|
||||
// than base_level.
|
||||
return Env::WLTH_MEDIUM;
|
||||
}
|
||||
return static_cast<Env::WriteLifeTimeHint>(level - base_level +
|
||||
static_cast<int>(Env::WLTH_MEDIUM));
|
||||
return static_cast<Env::WriteLifeTimeHint>(
|
||||
level - base_level + static_cast<int>(Env::WLTH_MEDIUM));
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::AddDirectories(
|
||||
@@ -1577,8 +1578,8 @@ ColumnFamilyData* ColumnFamilySet::GetColumnFamily(uint32_t id) const {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnFamilyData* ColumnFamilySet::GetColumnFamily(const std::string& name)
|
||||
const {
|
||||
ColumnFamilyData* ColumnFamilySet::GetColumnFamily(
|
||||
const std::string& name) const {
|
||||
auto cfd_iter = column_families_.find(name);
|
||||
if (cfd_iter != column_families_.end()) {
|
||||
auto cfd = GetColumnFamily(cfd_iter->second);
|
||||
|
||||
+7
-13
@@ -163,8 +163,8 @@ extern const double kIncSlowdownRatio;
|
||||
class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
public:
|
||||
// create while holding the mutex
|
||||
ColumnFamilyHandleImpl(
|
||||
ColumnFamilyData* cfd, DBImpl* db, InstrumentedMutex* mutex);
|
||||
ColumnFamilyHandleImpl(ColumnFamilyData* cfd, DBImpl* db,
|
||||
InstrumentedMutex* mutex);
|
||||
// destroy without mutex
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
@@ -189,7 +189,8 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
||||
public:
|
||||
ColumnFamilyHandleInternal()
|
||||
: ColumnFamilyHandleImpl(nullptr, nullptr, nullptr), internal_cfd_(nullptr) {}
|
||||
: ColumnFamilyHandleImpl(nullptr, nullptr, nullptr),
|
||||
internal_cfd_(nullptr) {}
|
||||
|
||||
void SetCFD(ColumnFamilyData* _cfd) { internal_cfd_ = _cfd; }
|
||||
virtual ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
@@ -309,10 +310,6 @@ class ColumnFamilyData {
|
||||
void SetLogNumber(uint64_t log_number) { log_number_ = log_number; }
|
||||
uint64_t GetLogNumber() const { return log_number_; }
|
||||
|
||||
void SetFlushReason(FlushReason flush_reason) {
|
||||
flush_reason_ = flush_reason;
|
||||
}
|
||||
FlushReason GetFlushReason() const { return flush_reason_; }
|
||||
// thread-safe
|
||||
const FileOptions* soptions() const;
|
||||
const ImmutableOptions* ioptions() const { return &ioptions_; }
|
||||
@@ -357,7 +354,7 @@ class ColumnFamilyData {
|
||||
Version* current() { return current_; }
|
||||
Version* dummy_versions() { return dummy_versions_; }
|
||||
void SetCurrent(Version* _current);
|
||||
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalBlobFileSize() const; // REQUIRE: DB mutex held
|
||||
@@ -552,7 +549,7 @@ class ColumnFamilyData {
|
||||
Version* dummy_versions_; // Head of circular doubly-linked list of versions.
|
||||
Version* current_; // == dummy_versions->prev_
|
||||
|
||||
std::atomic<int> refs_; // outstanding references to ColumnFamilyData
|
||||
std::atomic<int> refs_; // outstanding references to ColumnFamilyData
|
||||
std::atomic<bool> initialized_;
|
||||
std::atomic<bool> dropped_; // true if client dropped it
|
||||
|
||||
@@ -597,8 +594,6 @@ class ColumnFamilyData {
|
||||
// recovered from
|
||||
uint64_t log_number_;
|
||||
|
||||
std::atomic<FlushReason> flush_reason_;
|
||||
|
||||
// An object that keeps all the compaction stats
|
||||
// and picks the next compaction
|
||||
std::unique_ptr<CompactionPicker> compaction_picker_;
|
||||
@@ -656,8 +651,7 @@ class ColumnFamilySet {
|
||||
// ColumnFamilySet supports iteration
|
||||
class iterator {
|
||||
public:
|
||||
explicit iterator(ColumnFamilyData* cfd)
|
||||
: current_(cfd) {}
|
||||
explicit iterator(ColumnFamilyData* cfd) : current_(cfd) {}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
iterator& operator++() {
|
||||
current_ = current_->next_;
|
||||
|
||||
+17
-28
@@ -39,9 +39,7 @@ class EnvCounter : public SpecialEnv {
|
||||
public:
|
||||
explicit EnvCounter(Env* base)
|
||||
: SpecialEnv(base), num_new_writable_file_(0) {}
|
||||
int GetNumberOfNewWritableFileCalls() {
|
||||
return num_new_writable_file_;
|
||||
}
|
||||
int GetNumberOfNewWritableFileCalls() { return num_new_writable_file_; }
|
||||
Status NewWritableFile(const std::string& f, std::unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
++num_new_writable_file_;
|
||||
@@ -187,7 +185,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
}
|
||||
|
||||
Status OpenReadOnly(std::vector<std::string> cf,
|
||||
std::vector<ColumnFamilyOptions> options = {}) {
|
||||
std::vector<ColumnFamilyOptions> options = {}) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
names_.clear();
|
||||
for (size_t i = 0; i < cf.size(); ++i) {
|
||||
@@ -201,20 +199,17 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
|
||||
#ifndef ROCKSDB_LITE // ReadOnlyDB is not supported
|
||||
void AssertOpenReadOnly(std::vector<std::string> cf,
|
||||
std::vector<ColumnFamilyOptions> options = {}) {
|
||||
std::vector<ColumnFamilyOptions> options = {}) {
|
||||
ASSERT_OK(OpenReadOnly(cf, options));
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
|
||||
void Open(std::vector<std::string> cf,
|
||||
std::vector<ColumnFamilyOptions> options = {}) {
|
||||
ASSERT_OK(TryOpen(cf, options));
|
||||
}
|
||||
|
||||
void Open() {
|
||||
Open({"default"});
|
||||
}
|
||||
void Open() { Open({"default"}); }
|
||||
|
||||
DBImpl* dbfull() { return static_cast_with_check<DBImpl>(db_); }
|
||||
|
||||
@@ -253,7 +248,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
}
|
||||
|
||||
void Destroy(const std::vector<ColumnFamilyDescriptor>& column_families =
|
||||
std::vector<ColumnFamilyDescriptor>()) {
|
||||
std::vector<ColumnFamilyDescriptor>()) {
|
||||
Close();
|
||||
ASSERT_OK(DestroyDB(dbname_, Options(db_options_, column_family_options_),
|
||||
column_families));
|
||||
@@ -335,9 +330,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[cf]));
|
||||
}
|
||||
|
||||
void WaitForCompaction() {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
void WaitForCompaction() { ASSERT_OK(dbfull()->TEST_WaitForCompact()); }
|
||||
|
||||
uint64_t MaxTotalInMemoryState() {
|
||||
return dbfull()->TEST_MaxTotalInMemoryState();
|
||||
@@ -354,9 +347,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
Status Merge(int cf, const std::string& key, const std::string& value) {
|
||||
return db_->Merge(WriteOptions(), handles_[cf], Slice(key), Slice(value));
|
||||
}
|
||||
Status Flush(int cf) {
|
||||
return db_->Flush(FlushOptions(), handles_[cf]);
|
||||
}
|
||||
Status Flush(int cf) { return db_->Flush(FlushOptions(), handles_[cf]); }
|
||||
|
||||
std::string Get(int cf, const std::string& key) {
|
||||
ReadOptions options;
|
||||
@@ -409,8 +400,8 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(value, FilesPerLevel(cf));
|
||||
#else
|
||||
(void) value;
|
||||
(void) cf;
|
||||
(void)value;
|
||||
(void)cf;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -426,7 +417,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(expected_value, CountLiveFiles());
|
||||
#else
|
||||
(void) expected_value;
|
||||
(void)expected_value;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -476,7 +467,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
#ifndef ROCKSDB_LITE // GetSortedWalFiles is not supported
|
||||
ASSERT_EQ(value, CountLiveLogFiles());
|
||||
#else
|
||||
(void) value;
|
||||
(void)value;
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -521,14 +512,14 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
return static_cast<int>(files.size());
|
||||
}
|
||||
|
||||
void RecalculateWriteStallConditions(ColumnFamilyData* cfd,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
void RecalculateWriteStallConditions(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options) {
|
||||
// add lock to avoid race condition between
|
||||
// `RecalculateWriteStallConditions` which writes to CFStats and
|
||||
// background `DBImpl::DumpStats()` threads which read CFStats
|
||||
dbfull()->TEST_LockMutex();
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
dbfull()-> TEST_UnlockMutex();
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
}
|
||||
|
||||
std::vector<ColumnFamilyHandle*> handles_;
|
||||
@@ -970,8 +961,7 @@ TEST_P(ColumnFamilyTest, FlushTest) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
uint64_t max_total_in_memory_state =
|
||||
MaxTotalInMemoryState();
|
||||
uint64_t max_total_in_memory_state = MaxTotalInMemoryState();
|
||||
ASSERT_OK(Flush(i));
|
||||
AssertMaxTotalInMemoryState(max_total_in_memory_state);
|
||||
}
|
||||
@@ -1209,7 +1199,7 @@ TEST_P(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
WaitForFlush(2);
|
||||
AssertNumberOfImmutableMemtables({0, 0, 0, 0});
|
||||
AssertCountLiveLogFiles(12);
|
||||
PutRandomData(1, 2*200, 1000);
|
||||
PutRandomData(1, 2 * 200, 1000);
|
||||
WaitForFlush(1);
|
||||
AssertNumberOfImmutableMemtables({0, 0, 0, 0});
|
||||
AssertCountLiveLogFiles(7);
|
||||
@@ -2123,7 +2113,6 @@ TEST_P(ColumnFamilyTest, ReadOnlyDBTest) {
|
||||
ASSERT_EQ("bla", Get(1, "foo"));
|
||||
ASSERT_EQ("blablablabla", Get(2, "foo"));
|
||||
|
||||
|
||||
// test newiterators
|
||||
{
|
||||
std::vector<Iterator*> iterators;
|
||||
@@ -2488,7 +2477,7 @@ void DropSingleColumnFamily(ColumnFamilyTest* cf_test, int cf_id,
|
||||
}
|
||||
test_stage = kChildThreadFinishDroppingColumnFamily;
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_P(ColumnFamilyTest, CreateAndDropRace) {
|
||||
const int kCfCount = 5;
|
||||
|
||||
@@ -348,9 +348,7 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetDB(DB* db) {
|
||||
db_ = db;
|
||||
}
|
||||
void SetDB(DB* db) { db_ = db; }
|
||||
|
||||
const char* Name() const override { return "FilterWithGet"; }
|
||||
|
||||
@@ -358,7 +356,6 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
DB* db_;
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<FilterWithGet> cf(new FilterWithGet());
|
||||
|
||||
Options options;
|
||||
@@ -385,7 +382,6 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
db->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), {fname}, 0));
|
||||
}
|
||||
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
@@ -400,10 +396,9 @@ TEST_F(CompactFilesTest, SentinelCompressionType) {
|
||||
}
|
||||
// Check that passing `CompressionType::kDisableCompressionOption` to
|
||||
// `CompactFiles` causes it to use the column family compression options.
|
||||
for (auto compaction_style :
|
||||
{CompactionStyle::kCompactionStyleLevel,
|
||||
CompactionStyle::kCompactionStyleUniversal,
|
||||
CompactionStyle::kCompactionStyleNone}) {
|
||||
for (auto compaction_style : {CompactionStyle::kCompactionStyleLevel,
|
||||
CompactionStyle::kCompactionStyleUniversal,
|
||||
CompactionStyle::kCompactionStyleNone}) {
|
||||
ASSERT_OK(DestroyDB(db_name_, Options()));
|
||||
Options options;
|
||||
options.compaction_style = compaction_style;
|
||||
|
||||
@@ -168,7 +168,12 @@ void CompactionIterator::Next() {
|
||||
}
|
||||
|
||||
// Keep current_key_ in sync.
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
if (0 == timestamp_size_) {
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
} else {
|
||||
Slice ts = ikey_.GetTimestamp(timestamp_size_);
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type, &ts);
|
||||
}
|
||||
key_ = current_key_.GetInternalKey();
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
validity_info_.SetValid(ValidContext::kMerge1);
|
||||
@@ -825,8 +830,8 @@ void CompactionIterator::NextFromInput() {
|
||||
cmp_with_history_ts_low_ < 0)) &&
|
||||
bottommost_level_) {
|
||||
// Handle the case where we have a delete key at the bottom most level
|
||||
// We can skip outputting the key iff there are no subsequent puts for this
|
||||
// key
|
||||
// We can skip outputting the key iff there are no subsequent puts for
|
||||
// this key
|
||||
assert(!compaction_ || compaction_->KeyNotExistsBeyondOutputLevel(
|
||||
ikey_.user_key, &level_ptrs_));
|
||||
ParsedInternalKey next_ikey;
|
||||
@@ -853,8 +858,8 @@ void CompactionIterator::NextFromInput() {
|
||||
DefinitelyNotInSnapshot(next_ikey.sequence, prev_snapshot))) {
|
||||
AdvanceInputIter();
|
||||
}
|
||||
// If you find you still need to output a row with this key, we need to output the
|
||||
// delete too
|
||||
// If you find you still need to output a row with this key, we need to
|
||||
// output the delete too
|
||||
if (input_.Valid() &&
|
||||
(ParseInternalKey(input_.key(), &next_ikey, allow_data_in_errors_)
|
||||
.ok()) &&
|
||||
@@ -877,8 +882,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// object to minimize change to the existing flow.
|
||||
Status s = merge_helper_->MergeUntil(
|
||||
&input_, range_del_agg_, prev_snapshot, bottommost_level_,
|
||||
allow_data_in_errors_, blob_fetcher_.get(), prefetch_buffers_.get(),
|
||||
&iter_stats_);
|
||||
allow_data_in_errors_, blob_fetcher_.get(), full_history_ts_low_,
|
||||
prefetch_buffers_.get(), &iter_stats_);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
|
||||
if (!s.ok() && !s.IsMergeInProgress()) {
|
||||
@@ -1214,8 +1219,8 @@ inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"No snapshot left in findEarliestVisibleSnapshot");
|
||||
}
|
||||
auto snapshots_iter = std::lower_bound(
|
||||
snapshots_->begin(), snapshots_->end(), in);
|
||||
auto snapshots_iter =
|
||||
std::lower_bound(snapshots_->begin(), snapshots_->end(), in);
|
||||
assert(prev_snapshot != nullptr);
|
||||
if (snapshots_iter == snapshots_->begin()) {
|
||||
*prev_snapshot = 0;
|
||||
@@ -1230,8 +1235,8 @@ inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
}
|
||||
}
|
||||
if (snapshot_checker_ == nullptr) {
|
||||
return snapshots_iter != snapshots_->end()
|
||||
? *snapshots_iter : kMaxSequenceNumber;
|
||||
return snapshots_iter != snapshots_->end() ? *snapshots_iter
|
||||
: kMaxSequenceNumber;
|
||||
}
|
||||
bool has_released_snapshot = !released_snapshots_.empty();
|
||||
for (; snapshots_iter != snapshots_->end(); ++snapshots_iter) {
|
||||
|
||||
@@ -203,7 +203,8 @@ class TestSnapshotChecker : public SnapshotChecker {
|
||||
public:
|
||||
explicit TestSnapshotChecker(
|
||||
SequenceNumber last_committed_sequence,
|
||||
const std::unordered_map<SequenceNumber, SequenceNumber>& snapshots = {{}})
|
||||
const std::unordered_map<SequenceNumber, SequenceNumber>& snapshots =
|
||||
{{}})
|
||||
: last_committed_sequence_(last_committed_sequence),
|
||||
snapshots_(snapshots) {}
|
||||
|
||||
@@ -811,6 +812,8 @@ TEST_P(PerKeyPlacementCompIteratorTest, SplitLastLevelData) {
|
||||
c_iter_->Next();
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(PerKeyPlacementCompIteratorTest, SnapshotData) {
|
||||
@@ -876,6 +879,8 @@ TEST_P(PerKeyPlacementCompIteratorTest, ConflictWithSnapshot) {
|
||||
// output_to_penultimate_level.
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->status().IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(PerKeyPlacementCompIteratorTest,
|
||||
@@ -1038,7 +1043,7 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
NotRemoveDeletionIfValuePresentToEarlierSnapshot) {
|
||||
AddSnapshot(2,1);
|
||||
AddSnapshot(2, 1);
|
||||
RunTest({test::KeyStr("a", 4, kTypeDeletion),
|
||||
test::KeyStr("a", 1, kTypeValue), test::KeyStr("b", 3, kTypeValue)},
|
||||
{"", "", ""},
|
||||
@@ -1250,6 +1255,31 @@ TEST_P(CompactionIteratorTsGcTest, NoKeyEligibleForGC) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, NoMergeEligibleForGc) {
|
||||
constexpr char user_key[] = "a";
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr(10002, user_key, 102, kTypeMerge),
|
||||
test::KeyStr(10001, user_key, 101, kTypeMerge),
|
||||
test::KeyStr(10000, user_key, 100, kTypeValue)};
|
||||
const std::vector<std::string> input_values = {"2", "1", "a0"};
|
||||
std::shared_ptr<MergeOperator> merge_op =
|
||||
MergeOperators::CreateStringAppendTESTOperator();
|
||||
const std::vector<std::string>& expected_keys = input_keys;
|
||||
const std::vector<std::string>& expected_values = input_values;
|
||||
const std::vector<std::pair<bool, bool>> params = {
|
||||
{false, false}, {false, true}, {true, true}};
|
||||
for (const auto& param : params) {
|
||||
const bool bottommost_level = param.first;
|
||||
const bool key_not_exists_beyond_output_level = param.second;
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber, merge_op.get(),
|
||||
/*compaction_filter=*/nullptr, bottommost_level,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
key_not_exists_beyond_output_level,
|
||||
/*full_history_ts_low=*/nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, AllKeysOlderThanThreshold) {
|
||||
constexpr char user_key[][2] = {{'a', '\0'}, {'b', '\0'}};
|
||||
const std::vector<std::string> input_keys = {
|
||||
@@ -1303,6 +1333,91 @@ TEST_P(CompactionIteratorTsGcTest, AllKeysOlderThanThreshold) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, SomeMergesOlderThanThreshold) {
|
||||
constexpr char user_key[][2] = {"a", "f"};
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr(/*ts=*/25000, user_key[0], /*seq=*/2500, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/19000, user_key[0], /*seq=*/2300, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/18000, user_key[0], /*seq=*/1800, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/16000, user_key[0], /*seq=*/1600, kTypeValue),
|
||||
test::KeyStr(/*ts=*/19000, user_key[1], /*seq=*/2000, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/17000, user_key[1], /*seq=*/1700, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/15000, user_key[1], /*seq=*/1600,
|
||||
kTypeDeletionWithTimestamp)};
|
||||
const std::vector<std::string> input_values = {"25", "19", "18", "16",
|
||||
"19", "17", ""};
|
||||
std::shared_ptr<MergeOperator> merge_op =
|
||||
MergeOperators::CreateStringAppendTESTOperator();
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, 20000);
|
||||
|
||||
const std::vector<std::pair<bool, bool>> params = {
|
||||
{false, false}, {false, true}, {true, true}};
|
||||
|
||||
{
|
||||
AddSnapshot(1600);
|
||||
AddSnapshot(1900);
|
||||
const std::vector<std::string> expected_keys = {
|
||||
test::KeyStr(/*ts=*/25000, user_key[0], /*seq=*/2500, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/19000, user_key[0], /*seq=*/2300, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/18000, user_key[0], /*seq=*/1800, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/16000, user_key[0], /*seq=*/1600, kTypeValue),
|
||||
test::KeyStr(/*ts=*/19000, user_key[1], /*seq=*/2000, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/17000, user_key[1], /*seq=*/1700, kTypeMerge),
|
||||
test::KeyStr(/*ts=*/15000, user_key[1], /*seq=*/1600,
|
||||
kTypeDeletionWithTimestamp)};
|
||||
const std::vector<std::string> expected_values = {"25", "19", "18", "16",
|
||||
"19", "17", ""};
|
||||
for (const auto& param : params) {
|
||||
const bool bottommost_level = param.first;
|
||||
const bool key_not_exists_beyond_output_level = param.second;
|
||||
auto expected_keys_copy = expected_keys;
|
||||
auto expected_values_copy = expected_values;
|
||||
if (bottommost_level || key_not_exists_beyond_output_level) {
|
||||
// the kTypeDeletionWithTimestamp will be dropped
|
||||
expected_keys_copy.pop_back();
|
||||
expected_values_copy.pop_back();
|
||||
if (bottommost_level) {
|
||||
// seq zero
|
||||
expected_keys_copy[3] =
|
||||
test::KeyStr(/*ts=*/0, user_key[0], /*seq=*/0, kTypeValue);
|
||||
}
|
||||
}
|
||||
RunTest(input_keys, input_values, expected_keys_copy,
|
||||
expected_values_copy,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber, merge_op.get(),
|
||||
/*compaction_filter=*/nullptr, bottommost_level,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
key_not_exists_beyond_output_level, &full_history_ts_low);
|
||||
}
|
||||
ClearSnapshots();
|
||||
}
|
||||
|
||||
// No snapshots
|
||||
{
|
||||
const std::vector<std::string> expected_keys = {
|
||||
test::KeyStr(/*ts=*/25000, user_key[0], /*seq=*/2500, kTypeValue),
|
||||
test::KeyStr(/*ts=*/19000, user_key[1], /*seq=*/2000, kTypeValue)};
|
||||
const std::vector<std::string> expected_values = {"16,18,19,25", "17,19"};
|
||||
for (const auto& param : params) {
|
||||
const bool bottommost_level = param.first;
|
||||
const bool key_not_exists_beyond_output_level = param.second;
|
||||
auto expected_keys_copy = expected_keys;
|
||||
auto expected_values_copy = expected_values;
|
||||
if (bottommost_level) {
|
||||
expected_keys_copy[1] =
|
||||
test::KeyStr(/*ts=*/0, user_key[1], /*seq=*/0, kTypeValue);
|
||||
}
|
||||
RunTest(input_keys, input_values, expected_keys_copy,
|
||||
expected_values_copy,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber, merge_op.get(),
|
||||
/*compaction_filter=*/nullptr, bottommost_level,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
key_not_exists_beyond_output_level, &full_history_ts_low);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, NewHidesOldSameSnapshot) {
|
||||
constexpr char user_key[] = "a";
|
||||
const std::vector<std::string> input_keys = {
|
||||
|
||||
@@ -714,11 +714,12 @@ Status CompactionJob::Run() {
|
||||
break;
|
||||
}
|
||||
// Verify that the table is usable
|
||||
// We set for_compaction to false and don't OptimizeForCompactionTableRead
|
||||
// here because this is a special case after we finish the table building
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// we will regard this verification as user reads since the goal is
|
||||
// to cache it here for further user reads
|
||||
// We set for_compaction to false and don't
|
||||
// OptimizeForCompactionTableRead here because this is a special case
|
||||
// after we finish the table building No matter whether
|
||||
// use_direct_io_for_flush_and_compaction is true, we will regard this
|
||||
// verification as user reads since the goal is to cache it here for
|
||||
// further user reads
|
||||
ReadOptions read_options;
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
read_options, file_options_, cfd->internal_comparator(),
|
||||
@@ -764,8 +765,8 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
};
|
||||
for (size_t i = 1; i < compact_->sub_compact_states.size(); i++) {
|
||||
thread_pool.emplace_back(verify_table,
|
||||
std::ref(compact_->sub_compact_states[i].status));
|
||||
thread_pool.emplace_back(
|
||||
verify_table, std::ref(compact_->sub_compact_states[i].status));
|
||||
}
|
||||
verify_table(compact_->sub_compact_states[0].status);
|
||||
for (auto& thread : thread_pool) {
|
||||
|
||||
@@ -261,12 +261,12 @@ class CompactionJob {
|
||||
Status OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
CompactionOutputs& outputs);
|
||||
void UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const;
|
||||
const InternalStats::CompactionStats& stats) const;
|
||||
void RecordDroppedKeys(const CompactionIterationStats& c_iter_stats,
|
||||
CompactionJobStats* compaction_job_stats = nullptr);
|
||||
|
||||
void UpdateCompactionInputStatsHelper(
|
||||
int* num_files, uint64_t* bytes_read, int input_level);
|
||||
void UpdateCompactionInputStatsHelper(int* num_files, uint64_t* bytes_read,
|
||||
int input_level);
|
||||
|
||||
void NotifyOnSubcompactionBegin(SubcompactionState* sub_compact);
|
||||
|
||||
|
||||
@@ -155,9 +155,8 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
ASSERT_OK(TryReopenWithColumnFamilies(cfs, options));
|
||||
}
|
||||
|
||||
Status TryReopenWithColumnFamilies(
|
||||
const std::vector<std::string>& cfs,
|
||||
const std::vector<Options>& options) {
|
||||
Status TryReopenWithColumnFamilies(const std::vector<std::string>& cfs,
|
||||
const std::vector<Options>& options) {
|
||||
Close();
|
||||
EXPECT_EQ(cfs.size(), options.size());
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
@@ -175,9 +174,7 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
return TryReopenWithColumnFamilies(cfs, v_opts);
|
||||
}
|
||||
|
||||
void Reopen(const Options& options) {
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
void Reopen(const Options& options) { ASSERT_OK(TryReopen(options)); }
|
||||
|
||||
void Close() {
|
||||
for (auto h : handles_) {
|
||||
@@ -226,9 +223,7 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
return db_->Put(wo, handles_[cf], k, v);
|
||||
}
|
||||
|
||||
Status Delete(const std::string& k) {
|
||||
return db_->Delete(WriteOptions(), k);
|
||||
}
|
||||
Status Delete(const std::string& k) { return db_->Delete(WriteOptions(), k); }
|
||||
|
||||
Status Delete(int cf, const std::string& k) {
|
||||
return db_->Delete(WriteOptions(), handles_[cf], k);
|
||||
@@ -338,21 +333,21 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
}
|
||||
}
|
||||
|
||||
static void SetDeletionCompactionStats(
|
||||
CompactionJobStats *stats, uint64_t input_deletions,
|
||||
uint64_t expired_deletions, uint64_t records_replaced) {
|
||||
static void SetDeletionCompactionStats(CompactionJobStats* stats,
|
||||
uint64_t input_deletions,
|
||||
uint64_t expired_deletions,
|
||||
uint64_t records_replaced) {
|
||||
stats->num_input_deletion_records = input_deletions;
|
||||
stats->num_expired_deletion_records = expired_deletions;
|
||||
stats->num_records_replaced = records_replaced;
|
||||
}
|
||||
|
||||
void MakeTableWithKeyValues(
|
||||
Random* rnd, uint64_t smallest, uint64_t largest,
|
||||
int key_size, int value_size, uint64_t interval,
|
||||
double ratio, int cf = 0) {
|
||||
void MakeTableWithKeyValues(Random* rnd, uint64_t smallest, uint64_t largest,
|
||||
int key_size, int value_size, uint64_t interval,
|
||||
double ratio, int cf = 0) {
|
||||
for (auto key = smallest; key < largest; key += interval) {
|
||||
ASSERT_OK(Put(cf, Slice(Key(key, key_size)),
|
||||
Slice(RandomString(rnd, value_size, ratio))));
|
||||
Slice(RandomString(rnd, value_size, ratio))));
|
||||
}
|
||||
ASSERT_OK(Flush(cf));
|
||||
}
|
||||
@@ -361,9 +356,9 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
// rounds of keys are inserted into the database, as per the behavior
|
||||
// of the DeletionStatsTest.
|
||||
void SelectivelyDeleteKeys(uint64_t smallest, uint64_t largest,
|
||||
uint64_t interval, int deletion_interval, int key_size,
|
||||
uint64_t cutoff_key_num, CompactionJobStats* stats, int cf = 0) {
|
||||
|
||||
uint64_t interval, int deletion_interval,
|
||||
int key_size, uint64_t cutoff_key_num,
|
||||
CompactionJobStats* stats, int cf = 0) {
|
||||
// interval needs to be >= 2 so that deletion entries can be inserted
|
||||
// that are intended to not result in an actual key deletion by using
|
||||
// an offset of 1 from another existing key
|
||||
@@ -387,20 +382,19 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
|
||||
// Insert some deletions for keys that don't exist that
|
||||
// are both in and out of the key range
|
||||
ASSERT_OK(Delete(cf, Key(smallest+1, key_size)));
|
||||
ASSERT_OK(Delete(cf, Key(smallest + 1, key_size)));
|
||||
deletions_made++;
|
||||
|
||||
ASSERT_OK(Delete(cf, Key(smallest-1, key_size)));
|
||||
ASSERT_OK(Delete(cf, Key(smallest - 1, key_size)));
|
||||
deletions_made++;
|
||||
num_expired++;
|
||||
|
||||
ASSERT_OK(Delete(cf, Key(smallest-9, key_size)));
|
||||
ASSERT_OK(Delete(cf, Key(smallest - 9, key_size)));
|
||||
deletions_made++;
|
||||
num_expired++;
|
||||
|
||||
ASSERT_OK(Flush(cf));
|
||||
SetDeletionCompactionStats(stats, deletions_made, num_expired,
|
||||
num_deleted);
|
||||
SetDeletionCompactionStats(stats, deletions_made, num_expired, num_deleted);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -440,25 +434,20 @@ class CompactionJobStatsChecker : public EventListener {
|
||||
// use ASSERT_GE and ASSERT_LE with a reasonable bias ---
|
||||
// 10% in uncompressed case and 20% when compression is used.
|
||||
virtual void Verify(const CompactionJobStats& current_stats,
|
||||
const CompactionJobStats& stats) {
|
||||
const CompactionJobStats& stats) {
|
||||
// time
|
||||
ASSERT_GT(current_stats.elapsed_micros, 0U);
|
||||
|
||||
ASSERT_EQ(current_stats.num_input_records,
|
||||
stats.num_input_records);
|
||||
ASSERT_EQ(current_stats.num_input_files,
|
||||
stats.num_input_files);
|
||||
ASSERT_EQ(current_stats.num_input_records, stats.num_input_records);
|
||||
ASSERT_EQ(current_stats.num_input_files, stats.num_input_files);
|
||||
ASSERT_EQ(current_stats.num_input_files_at_output_level,
|
||||
stats.num_input_files_at_output_level);
|
||||
stats.num_input_files_at_output_level);
|
||||
|
||||
ASSERT_EQ(current_stats.num_output_records,
|
||||
stats.num_output_records);
|
||||
ASSERT_EQ(current_stats.num_output_files,
|
||||
stats.num_output_files);
|
||||
ASSERT_EQ(current_stats.num_output_records, stats.num_output_records);
|
||||
ASSERT_EQ(current_stats.num_output_files, stats.num_output_files);
|
||||
|
||||
ASSERT_EQ(current_stats.is_full_compaction, stats.is_full_compaction);
|
||||
ASSERT_EQ(current_stats.is_manual_compaction,
|
||||
stats.is_manual_compaction);
|
||||
ASSERT_EQ(current_stats.is_manual_compaction, stats.is_manual_compaction);
|
||||
|
||||
// file size
|
||||
double kFileSizeBias = compression_enabled_ ? 0.20 : 0.10;
|
||||
@@ -475,18 +464,14 @@ class CompactionJobStatsChecker : public EventListener {
|
||||
ASSERT_EQ(current_stats.total_input_raw_value_bytes,
|
||||
stats.total_input_raw_value_bytes);
|
||||
|
||||
ASSERT_EQ(current_stats.num_records_replaced,
|
||||
stats.num_records_replaced);
|
||||
ASSERT_EQ(current_stats.num_records_replaced, stats.num_records_replaced);
|
||||
|
||||
ASSERT_EQ(current_stats.num_corrupt_keys,
|
||||
stats.num_corrupt_keys);
|
||||
ASSERT_EQ(current_stats.num_corrupt_keys, stats.num_corrupt_keys);
|
||||
|
||||
ASSERT_EQ(
|
||||
std::string(current_stats.smallest_output_key_prefix),
|
||||
std::string(stats.smallest_output_key_prefix));
|
||||
ASSERT_EQ(
|
||||
std::string(current_stats.largest_output_key_prefix),
|
||||
std::string(stats.largest_output_key_prefix));
|
||||
ASSERT_EQ(std::string(current_stats.smallest_output_key_prefix),
|
||||
std::string(stats.smallest_output_key_prefix));
|
||||
ASSERT_EQ(std::string(current_stats.largest_output_key_prefix),
|
||||
std::string(stats.largest_output_key_prefix));
|
||||
}
|
||||
|
||||
// Add an expected compaction stats, which will be used to
|
||||
@@ -497,9 +482,7 @@ class CompactionJobStatsChecker : public EventListener {
|
||||
expected_stats_.push(stats);
|
||||
}
|
||||
|
||||
void EnableCompression(bool flag) {
|
||||
compression_enabled_ = flag;
|
||||
}
|
||||
void EnableCompression(bool flag) { compression_enabled_ = flag; }
|
||||
|
||||
bool verify_next_comp_io_stats() const { return verify_next_comp_io_stats_; }
|
||||
|
||||
@@ -517,45 +500,37 @@ class CompactionJobDeletionStatsChecker : public CompactionJobStatsChecker {
|
||||
// Verifies whether two CompactionJobStats match.
|
||||
void Verify(const CompactionJobStats& current_stats,
|
||||
const CompactionJobStats& stats) override {
|
||||
ASSERT_EQ(
|
||||
current_stats.num_input_deletion_records,
|
||||
stats.num_input_deletion_records);
|
||||
ASSERT_EQ(
|
||||
current_stats.num_expired_deletion_records,
|
||||
stats.num_expired_deletion_records);
|
||||
ASSERT_EQ(
|
||||
current_stats.num_records_replaced,
|
||||
stats.num_records_replaced);
|
||||
ASSERT_EQ(current_stats.num_input_deletion_records,
|
||||
stats.num_input_deletion_records);
|
||||
ASSERT_EQ(current_stats.num_expired_deletion_records,
|
||||
stats.num_expired_deletion_records);
|
||||
ASSERT_EQ(current_stats.num_records_replaced, stats.num_records_replaced);
|
||||
|
||||
ASSERT_EQ(current_stats.num_corrupt_keys,
|
||||
stats.num_corrupt_keys);
|
||||
ASSERT_EQ(current_stats.num_corrupt_keys, stats.num_corrupt_keys);
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t EstimatedFileSize(
|
||||
uint64_t num_records, size_t key_size, size_t value_size,
|
||||
double compression_ratio = 1.0,
|
||||
size_t block_size = 4096,
|
||||
int bloom_bits_per_key = 10) {
|
||||
uint64_t EstimatedFileSize(uint64_t num_records, size_t key_size,
|
||||
size_t value_size, double compression_ratio = 1.0,
|
||||
size_t block_size = 4096,
|
||||
int bloom_bits_per_key = 10) {
|
||||
const size_t kPerKeyOverhead = 8;
|
||||
const size_t kFooterSize = 512;
|
||||
|
||||
uint64_t data_size =
|
||||
static_cast<uint64_t>(
|
||||
num_records * (key_size + value_size * compression_ratio +
|
||||
kPerKeyOverhead));
|
||||
uint64_t data_size = static_cast<uint64_t>(
|
||||
num_records *
|
||||
(key_size + value_size * compression_ratio + kPerKeyOverhead));
|
||||
|
||||
return data_size + kFooterSize
|
||||
+ num_records * bloom_bits_per_key / 8 // filter block
|
||||
return data_size + kFooterSize +
|
||||
num_records * bloom_bits_per_key / 8 // filter block
|
||||
+ data_size * (key_size + 8) / block_size; // index block
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void CopyPrefix(
|
||||
const Slice& src, size_t prefix_length, std::string* dst) {
|
||||
void CopyPrefix(const Slice& src, size_t prefix_length, std::string* dst) {
|
||||
assert(prefix_length > 0);
|
||||
size_t length = src.size() > prefix_length ? prefix_length : src.size();
|
||||
dst->assign(src.data(), length);
|
||||
@@ -581,28 +556,24 @@ CompactionJobStats NewManualCompactionJobStats(
|
||||
stats.num_output_files = num_output_files;
|
||||
|
||||
stats.total_input_bytes =
|
||||
EstimatedFileSize(
|
||||
num_input_records / num_input_files,
|
||||
key_size, value_size, compression_ratio) * num_input_files;
|
||||
EstimatedFileSize(num_input_records / num_input_files, key_size,
|
||||
value_size, compression_ratio) *
|
||||
num_input_files;
|
||||
stats.total_output_bytes =
|
||||
EstimatedFileSize(
|
||||
num_output_records / num_output_files,
|
||||
key_size, value_size, compression_ratio) * num_output_files;
|
||||
stats.total_input_raw_key_bytes =
|
||||
num_input_records * (key_size + 8);
|
||||
stats.total_input_raw_value_bytes =
|
||||
num_input_records * value_size;
|
||||
EstimatedFileSize(num_output_records / num_output_files, key_size,
|
||||
value_size, compression_ratio) *
|
||||
num_output_files;
|
||||
stats.total_input_raw_key_bytes = num_input_records * (key_size + 8);
|
||||
stats.total_input_raw_value_bytes = num_input_records * value_size;
|
||||
|
||||
stats.is_full_compaction = is_full;
|
||||
stats.is_manual_compaction = is_manual;
|
||||
|
||||
stats.num_records_replaced = num_records_replaced;
|
||||
|
||||
CopyPrefix(smallest_key,
|
||||
CompactionJobStats::kMaxPrefixLength,
|
||||
CopyPrefix(smallest_key, CompactionJobStats::kMaxPrefixLength,
|
||||
&stats.smallest_output_key_prefix);
|
||||
CopyPrefix(largest_key,
|
||||
CompactionJobStats::kMaxPrefixLength,
|
||||
CopyPrefix(largest_key, CompactionJobStats::kMaxPrefixLength,
|
||||
&stats.largest_output_key_prefix);
|
||||
|
||||
return stats;
|
||||
@@ -662,13 +633,11 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
|
||||
// 1st Phase: generate "num_L0_files" L0 files.
|
||||
int num_L0_files = 0;
|
||||
for (uint64_t start_key = key_base;
|
||||
start_key <= key_base * kTestScale;
|
||||
start_key += key_base) {
|
||||
MakeTableWithKeyValues(
|
||||
&rnd, start_key, start_key + key_base - 1,
|
||||
kKeySize, kValueSize, key_interval,
|
||||
compression_ratio, 1);
|
||||
for (uint64_t start_key = key_base; start_key <= key_base * kTestScale;
|
||||
start_key += key_base) {
|
||||
MakeTableWithKeyValues(&rnd, start_key, start_key + key_base - 1,
|
||||
kKeySize, kValueSize, key_interval,
|
||||
compression_ratio, 1);
|
||||
snprintf(buf, kBufSize, "%d", ++num_L0_files);
|
||||
ASSERT_EQ(std::string(buf), FilesPerLevel(1));
|
||||
}
|
||||
@@ -684,13 +653,9 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
start_key += key_base, count++) {
|
||||
smallest_key = Key(start_key, 10);
|
||||
largest_key = Key(start_key + key_base - key_interval, 10);
|
||||
stats_checker->AddExpectedStats(
|
||||
NewManualCompactionJobStats(
|
||||
smallest_key, largest_key,
|
||||
1, 0, num_keys_per_L0_file,
|
||||
kKeySize, kValueSize,
|
||||
1, num_keys_per_L0_file,
|
||||
compression_ratio, 0));
|
||||
stats_checker->AddExpectedStats(NewManualCompactionJobStats(
|
||||
smallest_key, largest_key, 1, 0, num_keys_per_L0_file, kKeySize,
|
||||
kValueSize, 1, num_keys_per_L0_file, compression_ratio, 0));
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 1U);
|
||||
TEST_Compact(0, 1, smallest_key, largest_key);
|
||||
snprintf(buf, kBufSize, "%d,%d", num_L0_files - count, count);
|
||||
@@ -701,14 +666,10 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
int num_remaining_L0 = num_L0_files - L0_compaction_count;
|
||||
smallest_key = Key(key_base * (L0_compaction_count + 1), 10);
|
||||
largest_key = Key(key_base * (kTestScale + 1) - key_interval, 10);
|
||||
stats_checker->AddExpectedStats(
|
||||
NewManualCompactionJobStats(
|
||||
smallest_key, largest_key,
|
||||
num_remaining_L0,
|
||||
0, num_keys_per_L0_file * num_remaining_L0,
|
||||
kKeySize, kValueSize,
|
||||
1, num_keys_per_L0_file * num_remaining_L0,
|
||||
compression_ratio, 0));
|
||||
stats_checker->AddExpectedStats(NewManualCompactionJobStats(
|
||||
smallest_key, largest_key, num_remaining_L0, 0,
|
||||
num_keys_per_L0_file * num_remaining_L0, kKeySize, kValueSize, 1,
|
||||
num_keys_per_L0_file * num_remaining_L0, compression_ratio, 0));
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 1U);
|
||||
TEST_Compact(0, 1, smallest_key, largest_key);
|
||||
|
||||
@@ -719,13 +680,11 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
|
||||
// 3rd Phase: generate sparse L0 files (wider key-range, same num of keys)
|
||||
int sparseness = 2;
|
||||
for (uint64_t start_key = key_base;
|
||||
start_key <= key_base * kTestScale;
|
||||
start_key += key_base * sparseness) {
|
||||
for (uint64_t start_key = key_base; start_key <= key_base * kTestScale;
|
||||
start_key += key_base * sparseness) {
|
||||
MakeTableWithKeyValues(
|
||||
&rnd, start_key, start_key + key_base * sparseness - 1,
|
||||
kKeySize, kValueSize,
|
||||
key_base * sparseness / num_keys_per_L0_file,
|
||||
&rnd, start_key, start_key + key_base * sparseness - 1, kKeySize,
|
||||
kValueSize, key_base * sparseness / num_keys_per_L0_file,
|
||||
compression_ratio, 1);
|
||||
snprintf(buf, kBufSize, "%d,%d", ++num_L0_files, num_L1_files);
|
||||
ASSERT_EQ(std::string(buf), FilesPerLevel(1));
|
||||
@@ -737,21 +696,15 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
// output files without coordinating to see if the output could fit into
|
||||
// a smaller number of files like it does when it runs sequentially
|
||||
int num_output_files = options.max_subcompactions > 1 ? 2 : 1;
|
||||
for (uint64_t start_key = key_base;
|
||||
num_L0_files > 1;
|
||||
for (uint64_t start_key = key_base; num_L0_files > 1;
|
||||
start_key += key_base * sparseness) {
|
||||
smallest_key = Key(start_key, 10);
|
||||
largest_key =
|
||||
Key(start_key + key_base * sparseness - key_interval, 10);
|
||||
stats_checker->AddExpectedStats(
|
||||
NewManualCompactionJobStats(
|
||||
smallest_key, largest_key,
|
||||
3, 2, num_keys_per_L0_file * 3,
|
||||
kKeySize, kValueSize,
|
||||
num_output_files,
|
||||
num_keys_per_L0_file * 2, // 1/3 of the data will be updated.
|
||||
compression_ratio,
|
||||
num_keys_per_L0_file));
|
||||
largest_key = Key(start_key + key_base * sparseness - key_interval, 10);
|
||||
stats_checker->AddExpectedStats(NewManualCompactionJobStats(
|
||||
smallest_key, largest_key, 3, 2, num_keys_per_L0_file * 3, kKeySize,
|
||||
kValueSize, num_output_files,
|
||||
num_keys_per_L0_file * 2, // 1/3 of the data will be updated.
|
||||
compression_ratio, num_keys_per_L0_file));
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 1U);
|
||||
Compact(1, smallest_key, largest_key);
|
||||
if (options.max_subcompactions == 1) {
|
||||
@@ -766,14 +719,10 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
// In the first sub-compaction, we expect L0 compaction.
|
||||
smallest_key = Key(key_base, 10);
|
||||
largest_key = Key(key_base * (kTestScale + 1) - key_interval, 10);
|
||||
stats_checker->AddExpectedStats(
|
||||
NewManualCompactionJobStats(
|
||||
Key(key_base * (kTestScale + 1 - sparseness), 10), largest_key,
|
||||
2, 1, num_keys_per_L0_file * 3,
|
||||
kKeySize, kValueSize,
|
||||
1, num_keys_per_L0_file * 2,
|
||||
compression_ratio,
|
||||
num_keys_per_L0_file));
|
||||
stats_checker->AddExpectedStats(NewManualCompactionJobStats(
|
||||
Key(key_base * (kTestScale + 1 - sparseness), 10), largest_key, 2, 1,
|
||||
num_keys_per_L0_file * 3, kKeySize, kValueSize, 1,
|
||||
num_keys_per_L0_file * 2, compression_ratio, num_keys_per_L0_file));
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 1U);
|
||||
Compact(1, smallest_key, largest_key);
|
||||
|
||||
@@ -869,7 +818,7 @@ TEST_P(CompactionJobStatsTest, DeletionStatsTest) {
|
||||
Options options;
|
||||
options.listeners.emplace_back(stats_checker);
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = kTestScale+1;
|
||||
options.level0_file_num_compaction_trigger = kTestScale + 1;
|
||||
options.num_levels = 3;
|
||||
options.compression = kNoCompression;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
@@ -881,13 +830,10 @@ TEST_P(CompactionJobStatsTest, DeletionStatsTest) {
|
||||
// Stage 1: Generate several L0 files and then send them to L2 by
|
||||
// using CompactRangeOptions and CompactRange(). These files will
|
||||
// have a strict subset of the keys from the full key-range
|
||||
for (uint64_t start_key = key_base;
|
||||
start_key <= key_base * kTestScale / 2;
|
||||
start_key += key_base) {
|
||||
MakeTableWithKeyValues(
|
||||
&rnd, start_key, start_key + key_base - 1,
|
||||
kKeySize, kValueSize, key_interval,
|
||||
compression_ratio, 1);
|
||||
for (uint64_t start_key = key_base; start_key <= key_base * kTestScale / 2;
|
||||
start_key += key_base) {
|
||||
MakeTableWithKeyValues(&rnd, start_key, start_key + key_base - 1, kKeySize,
|
||||
kValueSize, key_interval, compression_ratio, 1);
|
||||
}
|
||||
|
||||
CompactRangeOptions cr_options;
|
||||
@@ -897,13 +843,10 @@ TEST_P(CompactionJobStatsTest, DeletionStatsTest) {
|
||||
ASSERT_GT(NumTableFilesAtLevel(2, 1), 0);
|
||||
|
||||
// Stage 2: Generate files including keys from the entire key range
|
||||
for (uint64_t start_key = key_base;
|
||||
start_key <= key_base * kTestScale;
|
||||
start_key += key_base) {
|
||||
MakeTableWithKeyValues(
|
||||
&rnd, start_key, start_key + key_base - 1,
|
||||
kKeySize, kValueSize, key_interval,
|
||||
compression_ratio, 1);
|
||||
for (uint64_t start_key = key_base; start_key <= key_base * kTestScale;
|
||||
start_key += key_base) {
|
||||
MakeTableWithKeyValues(&rnd, start_key, start_key + key_base - 1, kKeySize,
|
||||
kValueSize, key_interval, compression_ratio, 1);
|
||||
}
|
||||
|
||||
// Send these L0 files to L1
|
||||
@@ -912,16 +855,16 @@ TEST_P(CompactionJobStatsTest, DeletionStatsTest) {
|
||||
|
||||
// Add a new record and flush so now there is a L0 file
|
||||
// with a value too (not just deletions from the next step)
|
||||
ASSERT_OK(Put(1, Key(key_base-6, kKeySize), "test"));
|
||||
ASSERT_OK(Put(1, Key(key_base - 6, kKeySize), "test"));
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
// Stage 3: Generate L0 files with some deletions so now
|
||||
// there are files with the same key range in L0, L1, and L2
|
||||
int deletion_interval = 3;
|
||||
CompactionJobStats first_compaction_stats;
|
||||
SelectivelyDeleteKeys(key_base, largest_key_num,
|
||||
key_interval, deletion_interval, kKeySize, cutoff_key_num,
|
||||
&first_compaction_stats, 1);
|
||||
SelectivelyDeleteKeys(key_base, largest_key_num, key_interval,
|
||||
deletion_interval, kKeySize, cutoff_key_num,
|
||||
&first_compaction_stats, 1);
|
||||
|
||||
stats_checker->AddExpectedStats(first_compaction_stats);
|
||||
|
||||
@@ -932,8 +875,7 @@ TEST_P(CompactionJobStatsTest, DeletionStatsTest) {
|
||||
namespace {
|
||||
int GetUniversalCompactionInputUnits(uint32_t num_flushes) {
|
||||
uint32_t compaction_input_units;
|
||||
for (compaction_input_units = 1;
|
||||
num_flushes >= compaction_input_units;
|
||||
for (compaction_input_units = 1; num_flushes >= compaction_input_units;
|
||||
compaction_input_units *= 2) {
|
||||
if ((num_flushes & compaction_input_units) != 0) {
|
||||
return compaction_input_units > 1 ? compaction_input_units : 0;
|
||||
@@ -998,13 +940,10 @@ TEST_P(CompactionJobStatsTest, UniversalCompactionTest) {
|
||||
}
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 3U);
|
||||
|
||||
for (uint64_t start_key = key_base;
|
||||
start_key <= key_base * kTestScale;
|
||||
start_key += key_base) {
|
||||
MakeTableWithKeyValues(
|
||||
&rnd, start_key, start_key + key_base - 1,
|
||||
kKeySize, kValueSize, key_interval,
|
||||
compression_ratio, 1);
|
||||
for (uint64_t start_key = key_base; start_key <= key_base * kTestScale;
|
||||
start_key += key_base) {
|
||||
MakeTableWithKeyValues(&rnd, start_key, start_key + key_base - 1, kKeySize,
|
||||
kValueSize, key_interval, compression_ratio, 1);
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db_)->TEST_WaitForCompact());
|
||||
}
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 0U);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
|
||||
void VerifyInitializationOfCompactionJobStats(
|
||||
const CompactionJobStats& compaction_job_stats) {
|
||||
const CompactionJobStats& compaction_job_stats) {
|
||||
#if !defined(IOS_CROSS_COMPILE)
|
||||
ASSERT_EQ(compaction_job_stats.elapsed_micros, 0U);
|
||||
|
||||
@@ -499,8 +499,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
|
||||
// This is how the key will look like once it's written in bottommost
|
||||
// file
|
||||
InternalKey bottommost_internal_key(
|
||||
key, 0, kTypeValue);
|
||||
InternalKey bottommost_internal_key(key, 0, kTypeValue);
|
||||
|
||||
if (corrupt_id(k)) {
|
||||
test::CorruptKeyType(&internal_key);
|
||||
@@ -620,7 +619,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
CompactionInputFiles compaction_level;
|
||||
compaction_level.level = input_levels[i];
|
||||
compaction_level.files.insert(compaction_level.files.end(),
|
||||
level_files.begin(), level_files.end());
|
||||
level_files.begin(), level_files.end());
|
||||
compaction_input_files.push_back(compaction_level);
|
||||
num_input_files += level_files.size();
|
||||
}
|
||||
|
||||
@@ -308,9 +308,10 @@ void UniversalCompactionBuilder::SortedRun::Dump(char* out_buf,
|
||||
if (file->fd.GetPathId() == 0 || !print_path) {
|
||||
snprintf(out_buf, out_buf_size, "file %" PRIu64, file->fd.GetNumber());
|
||||
} else {
|
||||
snprintf(out_buf, out_buf_size, "file %" PRIu64
|
||||
"(path "
|
||||
"%" PRIu32 ")",
|
||||
snprintf(out_buf, out_buf_size,
|
||||
"file %" PRIu64
|
||||
"(path "
|
||||
"%" PRIu32 ")",
|
||||
file->fd.GetNumber(), file->fd.GetPathId());
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
|
||||
@@ -249,7 +248,7 @@ class TwoStrComparator : public Comparator {
|
||||
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
class ComparatorDBTest
|
||||
: public testing::Test,
|
||||
@@ -470,7 +469,7 @@ void VerifySuccessor(const Slice& s, const Slice& t) {
|
||||
ASSERT_FALSE(rbc->IsSameLengthImmediateSuccessor(t, s));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_P(ComparatorDBTest, IsSameLengthImmediateSuccessor) {
|
||||
{
|
||||
|
||||
+3
-5
@@ -26,8 +26,7 @@ Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
}
|
||||
|
||||
Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end) {
|
||||
const RangePtr* ranges, size_t n, bool include_end) {
|
||||
return (static_cast_with_check<DBImpl>(db->GetRootDB()))
|
||||
->DeleteFilesInRanges(column_family, ranges, n, include_end);
|
||||
}
|
||||
@@ -47,9 +46,8 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
InternalKeyComparator internal_comparator(options.comparator);
|
||||
ImmutableOptions ioptions(options);
|
||||
|
||||
Status s = ioptions.fs->NewRandomAccessFile(file_path,
|
||||
FileOptions(env_options),
|
||||
&file, nullptr);
|
||||
Status s = ioptions.fs->NewRandomAccessFile(
|
||||
file_path, FileOptions(env_options), &file, nullptr);
|
||||
if (s.ok()) {
|
||||
s = ioptions.fs->GetFileSize(file_path, IOOptions(), &file_size, nullptr);
|
||||
} else {
|
||||
|
||||
+10
-14
@@ -65,7 +65,7 @@ class ErrorEnv : public EnvWrapper {
|
||||
return target()->NewWritableFile(fname, result, soptions);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
class CorruptionTest : public testing::Test {
|
||||
public:
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
@@ -138,9 +138,7 @@ class CorruptionTest : public testing::Test {
|
||||
return DB::Open(opt, dbname_, &db_);
|
||||
}
|
||||
|
||||
void Reopen(Options* options = nullptr) {
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
void Reopen(Options* options = nullptr) { ASSERT_OK(TryReopen(options)); }
|
||||
|
||||
void RepairDB() {
|
||||
delete db_;
|
||||
@@ -156,7 +154,7 @@ class CorruptionTest : public testing::Test {
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
}
|
||||
//if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
|
||||
// if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
|
||||
Slice key = Key(i + start, &key_space);
|
||||
batch.Clear();
|
||||
ASSERT_OK(batch.Put(key, Value(i + start, &value_space)));
|
||||
@@ -183,8 +181,7 @@ class CorruptionTest : public testing::Test {
|
||||
ASSERT_OK(iter->status());
|
||||
uint64_t key;
|
||||
Slice in(iter->key());
|
||||
if (!ConsumeDecimalNumber(&in, &key) ||
|
||||
!in.empty() ||
|
||||
if (!ConsumeDecimalNumber(&in, &key) || !in.empty() ||
|
||||
key < next_expected) {
|
||||
bad_keys++;
|
||||
continue;
|
||||
@@ -200,10 +197,11 @@ class CorruptionTest : public testing::Test {
|
||||
iter->status().PermitUncheckedError();
|
||||
delete iter;
|
||||
|
||||
fprintf(stderr,
|
||||
"expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%llu\n",
|
||||
min_expected, max_expected, correct, bad_keys, bad_values,
|
||||
static_cast<unsigned long long>(missed));
|
||||
fprintf(
|
||||
stderr,
|
||||
"expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%llu\n",
|
||||
min_expected, max_expected, correct, bad_keys, bad_values,
|
||||
static_cast<unsigned long long>(missed));
|
||||
ASSERT_LE(min_expected, correct);
|
||||
ASSERT_GE(max_expected, correct);
|
||||
}
|
||||
@@ -217,8 +215,7 @@ class CorruptionTest : public testing::Test {
|
||||
std::string fname;
|
||||
int picked_number = -1;
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, &type) &&
|
||||
type == filetype &&
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == filetype &&
|
||||
static_cast<int>(number) > picked_number) { // Pick latest file
|
||||
fname = dbname_ + "/" + filenames[i];
|
||||
picked_number = static_cast<int>(number);
|
||||
@@ -244,7 +241,6 @@ class CorruptionTest : public testing::Test {
|
||||
FAIL() << "no file found at level";
|
||||
}
|
||||
|
||||
|
||||
int Property(const std::string& name) {
|
||||
std::string property;
|
||||
int result;
|
||||
|
||||
@@ -77,9 +77,7 @@ class CuckooTableDBTest : public testing::Test {
|
||||
return db_->Put(WriteOptions(), k, v);
|
||||
}
|
||||
|
||||
Status Delete(const std::string& k) {
|
||||
return db_->Delete(WriteOptions(), k);
|
||||
}
|
||||
Status Delete(const std::string& k) { return db_->Delete(WriteOptions(), k); }
|
||||
|
||||
std::string Get(const std::string& k) {
|
||||
ReadOptions options;
|
||||
@@ -313,23 +311,21 @@ TEST_F(CuckooTableDBTest, AdaptiveTable) {
|
||||
// Write some keys using plain table.
|
||||
std::shared_ptr<TableFactory> block_based_factory(
|
||||
NewBlockBasedTableFactory());
|
||||
std::shared_ptr<TableFactory> plain_table_factory(
|
||||
NewPlainTableFactory());
|
||||
std::shared_ptr<TableFactory> cuckoo_table_factory(
|
||||
NewCuckooTableFactory());
|
||||
std::shared_ptr<TableFactory> plain_table_factory(NewPlainTableFactory());
|
||||
std::shared_ptr<TableFactory> cuckoo_table_factory(NewCuckooTableFactory());
|
||||
options.create_if_missing = false;
|
||||
options.table_factory.reset(NewAdaptiveTableFactory(
|
||||
plain_table_factory, block_based_factory, plain_table_factory,
|
||||
cuckoo_table_factory));
|
||||
options.table_factory.reset(
|
||||
NewAdaptiveTableFactory(plain_table_factory, block_based_factory,
|
||||
plain_table_factory, cuckoo_table_factory));
|
||||
Reopen(&options);
|
||||
ASSERT_OK(Put("key4", "v4"));
|
||||
ASSERT_OK(Put("key1", "v5"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
|
||||
// Write some keys using block based table.
|
||||
options.table_factory.reset(NewAdaptiveTableFactory(
|
||||
block_based_factory, block_based_factory, plain_table_factory,
|
||||
cuckoo_table_factory));
|
||||
options.table_factory.reset(
|
||||
NewAdaptiveTableFactory(block_based_factory, block_based_factory,
|
||||
plain_table_factory, cuckoo_table_factory));
|
||||
Reopen(&options);
|
||||
ASSERT_OK(Put("key5", "v6"));
|
||||
ASSERT_OK(Put("key2", "v7"));
|
||||
|
||||
+74
-7
@@ -2158,11 +2158,11 @@ class DBMultiGetAsyncIOTest : public DBBasicTest,
|
||||
: DBBasicTest(), statistics_(ROCKSDB_NAMESPACE::CreateDBStatistics()) {
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = statistics_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
options_ = CurrentOptions();
|
||||
options_.disable_auto_compactions = true;
|
||||
options_.statistics = statistics_;
|
||||
options_.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options_);
|
||||
int num_keys = 0;
|
||||
|
||||
// Put all keys in the bottommost level, and overwrite some keys
|
||||
@@ -2227,8 +2227,12 @@ class DBMultiGetAsyncIOTest : public DBBasicTest,
|
||||
|
||||
const std::shared_ptr<Statistics>& statistics() { return statistics_; }
|
||||
|
||||
protected:
|
||||
void ReopenDB() { Reopen(options_); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Statistics> statistics_;
|
||||
Options options_;
|
||||
};
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL0) {
|
||||
@@ -2305,6 +2309,69 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
std::vector<PinnableSlice> values;
|
||||
std::vector<Status> statuses;
|
||||
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::GetTableReader:BeforeOpenFile", [&](void* status) {
|
||||
static int count = 0;
|
||||
count++;
|
||||
// Fail the last table reader open, which is the 6th SST file
|
||||
// since 3 overlapping L0 files + 3 L1 files containing the keys
|
||||
if (count == 6) {
|
||||
Status* s = static_cast<Status*>(status);
|
||||
*s = Status::IOError();
|
||||
}
|
||||
});
|
||||
// DB open will create table readers unless we reduce the table cache
|
||||
// capacity.
|
||||
// SanitizeOptions will set max_open_files to minimum of 20. Table cache
|
||||
// is allocated with max_open_files - 10 as capacity. So override
|
||||
// max_open_files to 11 so table cache capacity will become 1. This will
|
||||
// prevent file open during DB open and force the file to be opened
|
||||
// during MultiGet
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
|
||||
int* max_open_files = (int*)arg;
|
||||
*max_open_files = 11;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ReopenDB();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(statuses[0], Status::OK());
|
||||
ASSERT_EQ(statuses[1], Status::OK());
|
||||
ASSERT_EQ(statuses[2], Status::IOError());
|
||||
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
|
||||
// A batch of 3 async IOs is expected, one for each overlapping file in L1
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 2);
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
@@ -2805,7 +2872,7 @@ TEST_F(DBBasicTest, MultiGetIOBufferOverrun) {
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.block_size = 16 * 1024;
|
||||
ASSERT_TRUE(table_options.block_size >
|
||||
BlockBasedTable::kMultiGetReadStackBufSize);
|
||||
BlockBasedTable::kMultiGetReadStackBufSize);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
@@ -2914,7 +2981,7 @@ class TableFileListener : public EventListener {
|
||||
InstrumentedMutex mutex_;
|
||||
std::unordered_map<std::string, std::vector<std::string>> cf_to_paths_;
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBasicTest, LastSstFileNotInManifest) {
|
||||
// If the last sst file is not tracked in MANIFEST,
|
||||
|
||||
+121
-12
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "cache/cache_key.h"
|
||||
#include "cache/fast_lru_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
@@ -414,7 +413,7 @@ class ReadOnlyCacheWrapper : public CacheWrapper {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, TestWithSameCompressed) {
|
||||
auto table_options = GetTableOptions();
|
||||
@@ -944,10 +943,7 @@ TEST_F(DBBlockCacheTest, AddRedundantStats) {
|
||||
capacity,
|
||||
BlockBasedTableOptions().block_size /*estimated_value_size*/,
|
||||
num_shard_bits)
|
||||
.MakeSharedCache(),
|
||||
NewFastLRUCache(capacity, 1 /*estimated_value_size*/, num_shard_bits,
|
||||
false /*strict_capacity_limit*/,
|
||||
kDefaultCacheMetadataChargePolicy)}) {
|
||||
.MakeSharedCache()}) {
|
||||
if (!base_cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
@@ -1306,11 +1302,6 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
capacity,
|
||||
BlockBasedTableOptions().block_size /*estimated_value_size*/)
|
||||
.MakeSharedCache()}) {
|
||||
if (!cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
}
|
||||
|
||||
++iterations_tested;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -1543,6 +1534,124 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void DummyFillCache(Cache& cache, size_t entry_size,
|
||||
std::vector<CacheHandleGuard<void>>& handles) {
|
||||
// fprintf(stderr, "Entry size: %zu\n", entry_size);
|
||||
handles.clear();
|
||||
cache.EraseUnRefEntries();
|
||||
void* fake_value = &cache;
|
||||
size_t capacity = cache.GetCapacity();
|
||||
OffsetableCacheKey ck{"abc", "abc", 42};
|
||||
for (size_t my_usage = 0; my_usage < capacity;) {
|
||||
size_t charge = std::min(entry_size, capacity - my_usage);
|
||||
Cache::Handle* handle;
|
||||
Status st = cache.Insert(ck.WithOffset(my_usage).AsSlice(), fake_value,
|
||||
charge, /*deleter*/ nullptr, &handle);
|
||||
ASSERT_OK(st);
|
||||
handles.emplace_back(&cache, handle);
|
||||
my_usage += charge;
|
||||
}
|
||||
}
|
||||
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
~CountingLogger() override {}
|
||||
using Logger::Logv;
|
||||
void Logv(const InfoLogLevel log_level, const char* format,
|
||||
va_list /*ap*/) override {
|
||||
if (std::strstr(format, "HyperClockCache") == nullptr) {
|
||||
// Not a match
|
||||
return;
|
||||
}
|
||||
// static StderrLogger debug;
|
||||
// debug.Logv(log_level, format, ap);
|
||||
if (log_level == InfoLogLevel::INFO_LEVEL) {
|
||||
++info_count_;
|
||||
} else if (log_level == InfoLogLevel::WARN_LEVEL) {
|
||||
++warn_count_;
|
||||
} else if (log_level == InfoLogLevel::ERROR_LEVEL) {
|
||||
++error_count_;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<int, 3> PopCounts() {
|
||||
std::array<int, 3> rv{{info_count_, warn_count_, error_count_}};
|
||||
info_count_ = warn_count_ = error_count_ = 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
int info_count_{};
|
||||
int warn_count_{};
|
||||
int error_count_{};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, HyperClockCacheReportProblems) {
|
||||
size_t capacity = 1024 * 1024;
|
||||
size_t value_size_est = 8 * 1024;
|
||||
HyperClockCacheOptions hcc_opts{capacity, value_size_est};
|
||||
hcc_opts.num_shard_bits = 2; // 4 shards
|
||||
hcc_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<Cache> cache = hcc_opts.MakeSharedCache();
|
||||
std::shared_ptr<CountingLogger> logger = std::make_shared<CountingLogger>();
|
||||
|
||||
auto table_options = GetTableOptions();
|
||||
auto options = GetOptions(table_options);
|
||||
table_options.block_cache = cache;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.info_log = logger;
|
||||
// Going to sample more directly
|
||||
options.stats_dump_period_sec = 0;
|
||||
Reopen(options);
|
||||
|
||||
std::vector<CacheHandleGuard<void>> handles;
|
||||
|
||||
// Clear anything from DB startup
|
||||
logger->PopCounts();
|
||||
|
||||
// Fill cache based on expected size and check that when we
|
||||
// don't report anything relevant in periodic stats dump
|
||||
DummyFillCache(*cache, value_size_est, handles);
|
||||
dbfull()->DumpStats();
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 0, 0}}));
|
||||
|
||||
// Same, within reasonable bounds
|
||||
DummyFillCache(*cache, value_size_est - value_size_est / 4, handles);
|
||||
dbfull()->DumpStats();
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 0, 0}}));
|
||||
|
||||
DummyFillCache(*cache, value_size_est + value_size_est / 3, handles);
|
||||
dbfull()->DumpStats();
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 0, 0}}));
|
||||
|
||||
// Estimate too high (value size too low) eventually reports ERROR
|
||||
DummyFillCache(*cache, value_size_est / 2, handles);
|
||||
dbfull()->DumpStats();
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 1, 0}}));
|
||||
|
||||
DummyFillCache(*cache, value_size_est / 3, handles);
|
||||
dbfull()->DumpStats();
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 0, 1}}));
|
||||
|
||||
// Estimate too low (value size too high) starts with INFO
|
||||
// and is only WARNING in the worst case
|
||||
DummyFillCache(*cache, value_size_est * 2, handles);
|
||||
dbfull()->DumpStats();
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{1, 0, 0}}));
|
||||
|
||||
DummyFillCache(*cache, value_size_est * 3, handles);
|
||||
dbfull()->DumpStats();
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 1, 0}}));
|
||||
|
||||
DummyFillCache(*cache, value_size_est * 20, handles);
|
||||
dbfull()->DumpStats();
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 1, 0}}));
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
class DBBlockCacheKeyTest
|
||||
@@ -1973,7 +2082,7 @@ struct CacheKeyDecoder {
|
||||
DownwardInvolution(decoded_session_counter));
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(CacheKeyTest, Encodings) {
|
||||
// This test primarily verifies this claim from cache_key.cc:
|
||||
|
||||
+14
-16
@@ -43,7 +43,7 @@ const std::string kStandard128Ribbon =
|
||||
test::Standard128RibbonFilterPolicy::kClassName();
|
||||
const std::string kAutoBloom = BloomFilterPolicy::kClassName();
|
||||
const std::string kAutoRibbon = RibbonFilterPolicy::kClassName();
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
// DB tests related to bloom filter.
|
||||
|
||||
@@ -622,7 +622,7 @@ class AlwaysTrueFilterPolicy : public ReadOnlyBuiltinFilterPolicy {
|
||||
bool skip_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_P(DBBloomFilterTestWithParam, SkipFilterOnEssentiallyZeroBpk) {
|
||||
constexpr int maxKey = 10;
|
||||
@@ -767,10 +767,10 @@ INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatLatest, DBBloomFilterTestWithParam,
|
||||
::testing::Values(
|
||||
std::make_tuple(kAutoBloom, true, kLatestFormatVersion),
|
||||
std::make_tuple(kAutoBloom, false, kLatestFormatVersion),
|
||||
std::make_tuple(kAutoRibbon, false, kLatestFormatVersion)));
|
||||
::testing::Values(std::make_tuple(kAutoBloom, true, kLatestFormatVersion),
|
||||
std::make_tuple(kAutoBloom, false, kLatestFormatVersion),
|
||||
std::make_tuple(kAutoRibbon, false,
|
||||
kLatestFormatVersion)));
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
@@ -840,7 +840,7 @@ std::vector<CompatibilityConfig> kCompatibilityConfigs = {
|
||||
BlockBasedTableOptions().format_version},
|
||||
{kCompatibilityRibbonPolicy, true, BlockBasedTableOptions().format_version},
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterCompatibility) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -1678,7 +1678,7 @@ class TestingContextCustomFilterPolicy
|
||||
private:
|
||||
mutable std::string test_report_;
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBloomFilterTest, ContextCustomFilterPolicy) {
|
||||
auto policy = std::make_shared<TestingContextCustomFilterPolicy>(15, 8, 5);
|
||||
@@ -2186,16 +2186,14 @@ INSTANTIATE_TEST_CASE_P(DBBloomFilterTestVaryPrefixAndFormatVer,
|
||||
std::make_tuple(false, 2),
|
||||
std::make_tuple(false, 3),
|
||||
std::make_tuple(false, 4),
|
||||
std::make_tuple(false, 5),
|
||||
std::make_tuple(true, 2),
|
||||
std::make_tuple(true, 3),
|
||||
std::make_tuple(true, 4),
|
||||
std::make_tuple(false, 5), std::make_tuple(true, 2),
|
||||
std::make_tuple(true, 3), std::make_tuple(true, 4),
|
||||
std::make_tuple(true, 5)));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace {
|
||||
static const std::string kPlainTable = "test_PlainTableBloom";
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
class BloomStatsTestWithParam
|
||||
: public DBBloomFilterTest,
|
||||
@@ -2408,7 +2406,7 @@ void PrefixScanInit(DBBloomFilterTest* dbtest) {
|
||||
dbtest->Flush();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBloomFilterTest, PrefixScan) {
|
||||
while (ChangeFilterOptions()) {
|
||||
@@ -3169,7 +3167,7 @@ std::pair<uint64_t, uint64_t> CheckedAndUseful(uint64_t checked,
|
||||
uint64_t useful) {
|
||||
return {checked, useful};
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
// This uses a prefix_extractor + comparator combination that violates
|
||||
// one of the old obsolete, unnecessary axioms of prefix extraction:
|
||||
@@ -3377,7 +3375,7 @@ class NonIdempotentFixed4Transform : public SliceTransform {
|
||||
|
||||
bool InDomain(const Slice& src) const override { return src.size() >= 5; }
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
// This uses a prefix_extractor + comparator combination that violates
|
||||
// two of the old obsolete, unnecessary axioms of prefix extraction:
|
||||
|
||||
+105
-72
@@ -248,9 +248,8 @@ Options DeletionTriggerOptions(Options options) {
|
||||
return options;
|
||||
}
|
||||
|
||||
bool HaveOverlappingKeyRanges(
|
||||
const Comparator* c,
|
||||
const SstFileMetaData& a, const SstFileMetaData& b) {
|
||||
bool HaveOverlappingKeyRanges(const Comparator* c, const SstFileMetaData& a,
|
||||
const SstFileMetaData& b) {
|
||||
if (c->CompareWithoutTimestamp(a.smallestkey, b.smallestkey) >= 0) {
|
||||
if (c->CompareWithoutTimestamp(a.smallestkey, b.largestkey) <= 0) {
|
||||
// b.smallestkey <= a.smallestkey <= b.largestkey
|
||||
@@ -275,18 +274,15 @@ bool HaveOverlappingKeyRanges(
|
||||
// Identifies all files between level "min_level" and "max_level"
|
||||
// which has overlapping key range with "input_file_meta".
|
||||
void GetOverlappingFileNumbersForLevelCompaction(
|
||||
const ColumnFamilyMetaData& cf_meta,
|
||||
const Comparator* comparator,
|
||||
int min_level, int max_level,
|
||||
const SstFileMetaData* input_file_meta,
|
||||
const ColumnFamilyMetaData& cf_meta, const Comparator* comparator,
|
||||
int min_level, int max_level, const SstFileMetaData* input_file_meta,
|
||||
std::set<std::string>* overlapping_file_names) {
|
||||
std::set<const SstFileMetaData*> overlapping_files;
|
||||
overlapping_files.insert(input_file_meta);
|
||||
for (int m = min_level; m <= max_level; ++m) {
|
||||
for (auto& file : cf_meta.levels[m].files) {
|
||||
for (auto* included_file : overlapping_files) {
|
||||
if (HaveOverlappingKeyRanges(
|
||||
comparator, *included_file, file)) {
|
||||
if (HaveOverlappingKeyRanges(comparator, *included_file, file)) {
|
||||
overlapping_files.insert(&file);
|
||||
overlapping_file_names->insert(file.name);
|
||||
break;
|
||||
@@ -309,12 +305,9 @@ void VerifyCompactionResult(
|
||||
#endif
|
||||
}
|
||||
|
||||
const SstFileMetaData* PickFileRandomly(
|
||||
const ColumnFamilyMetaData& cf_meta,
|
||||
Random* rand,
|
||||
int* level = nullptr) {
|
||||
auto file_id = rand->Uniform(static_cast<int>(
|
||||
cf_meta.file_count)) + 1;
|
||||
const SstFileMetaData* PickFileRandomly(const ColumnFamilyMetaData& cf_meta,
|
||||
Random* rand, int* level = nullptr) {
|
||||
auto file_id = rand->Uniform(static_cast<int>(cf_meta.file_count)) + 1;
|
||||
for (auto& level_meta : cf_meta.levels) {
|
||||
if (file_id <= level_meta.files.size()) {
|
||||
if (level != nullptr) {
|
||||
@@ -740,7 +733,6 @@ TEST_F(DBCompactionTest, DisableStatsUpdateReopen) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, CompactionTrigger) {
|
||||
const int kNumKeysPerFile = 100;
|
||||
|
||||
@@ -883,7 +875,7 @@ TEST_F(DBCompactionTest, BGCompactionsAllowed) {
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, CompactionsGenerateMultipleFiles) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100000000; // Large write buffer
|
||||
options.write_buffer_size = 100000000; // Large write buffer
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
@@ -1069,7 +1061,7 @@ TEST_F(DBCompactionTest, ZeroSeqIdCompaction) {
|
||||
compact_opt.compression = kNoCompression;
|
||||
compact_opt.output_file_size_limit = 4096;
|
||||
const size_t key_len =
|
||||
static_cast<size_t>(compact_opt.output_file_size_limit) / 5;
|
||||
static_cast<size_t>(compact_opt.output_file_size_limit) / 5;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -1247,14 +1239,8 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveNonOverlappingFiles) {
|
||||
DestroyAndReopen(options);
|
||||
// non overlapping ranges
|
||||
std::vector<std::pair<int32_t, int32_t>> ranges = {
|
||||
{100, 199},
|
||||
{300, 399},
|
||||
{0, 99},
|
||||
{200, 299},
|
||||
{600, 699},
|
||||
{400, 499},
|
||||
{500, 550},
|
||||
{551, 599},
|
||||
{100, 199}, {300, 399}, {0, 99}, {200, 299},
|
||||
{600, 699}, {400, 499}, {500, 550}, {551, 599},
|
||||
};
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
|
||||
@@ -1297,14 +1283,15 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveNonOverlappingFiles) {
|
||||
DestroyAndReopen(options);
|
||||
// Same ranges as above but overlapping
|
||||
ranges = {
|
||||
{100, 199},
|
||||
{300, 399},
|
||||
{0, 99},
|
||||
{200, 299},
|
||||
{600, 699},
|
||||
{400, 499},
|
||||
{500, 560}, // this range overlap with the next one
|
||||
{551, 599},
|
||||
{100, 199},
|
||||
{300, 399},
|
||||
{0, 99},
|
||||
{200, 299},
|
||||
{600, 699},
|
||||
{400, 499},
|
||||
{500, 560}, // this range overlap with the next
|
||||
// one
|
||||
{551, 599},
|
||||
};
|
||||
for (size_t i = 0; i < ranges.size(); i++) {
|
||||
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
|
||||
@@ -1907,7 +1894,7 @@ TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
ASSERT_EQ("0,0,10", FilesPerLevel(0));
|
||||
|
||||
// file [0 => 100), [200 => 300), ... [800, 900)
|
||||
for (auto i = 0; i < 10; i+=2) {
|
||||
for (auto i = 0; i < 10; i += 2) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
@@ -2349,14 +2336,14 @@ TEST_P(DBCompactionTestWithParam, LevelCompactionCFPathUse) {
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_2", 4 * 1024 * 1024);
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_3", 1024 * 1024 * 1024);
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt1);
|
||||
CreateColumnFamilies({"one"},option_vector[1]);
|
||||
CreateColumnFamilies({"one"}, option_vector[1]);
|
||||
|
||||
// Configure CF2 specific paths.
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2", 500 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_2", 4 * 1024 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_3", 1024 * 1024 * 1024);
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt2);
|
||||
CreateColumnFamilies({"two"},option_vector[2]);
|
||||
CreateColumnFamilies({"two"}, option_vector[2]);
|
||||
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
|
||||
|
||||
@@ -2729,7 +2716,6 @@ TEST_P(DBCompactionTestWithParam, ManualCompaction) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, ManualLevelCompactionOutputPathId) {
|
||||
Options options = CurrentOptions();
|
||||
options.db_paths.emplace_back(dbname_ + "_2", 2 * 10485760);
|
||||
@@ -2866,14 +2852,13 @@ TEST_P(DBCompactionTestWithParam, DISABLED_CompactFilesOnLevelCompaction) {
|
||||
auto file_meta = PickFileRandomly(cf_meta, &rnd, &level);
|
||||
compaction_input_file_names.push_back(file_meta->name);
|
||||
GetOverlappingFileNumbersForLevelCompaction(
|
||||
cf_meta, options.comparator, level, output_level,
|
||||
file_meta, &overlapping_file_names);
|
||||
cf_meta, options.comparator, level, output_level, file_meta,
|
||||
&overlapping_file_names);
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->CompactFiles(
|
||||
CompactionOptions(), handles_[1],
|
||||
compaction_input_file_names,
|
||||
output_level));
|
||||
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), handles_[1],
|
||||
compaction_input_file_names,
|
||||
output_level));
|
||||
|
||||
// Make sure all overlapping files do not exist after compaction
|
||||
dbfull()->GetColumnFamilyMetaData(handles_[1], &cf_meta);
|
||||
@@ -2896,8 +2881,7 @@ TEST_P(DBCompactionTestWithParam, PartialCompactionFailure) {
|
||||
options.write_buffer_size = kKeysPerBuffer * kKvSize;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.target_file_size_base =
|
||||
options.write_buffer_size *
|
||||
(options.max_write_buffer_number - 1);
|
||||
options.write_buffer_size * (options.max_write_buffer_number - 1);
|
||||
options.level0_file_num_compaction_trigger = kNumL1Files;
|
||||
options.max_bytes_for_level_base =
|
||||
options.level0_file_num_compaction_trigger *
|
||||
@@ -2917,10 +2901,9 @@ TEST_P(DBCompactionTestWithParam, PartialCompactionFailure) {
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumInsertedKeys =
|
||||
options.level0_file_num_compaction_trigger *
|
||||
(options.max_write_buffer_number - 1) *
|
||||
kKeysPerBuffer;
|
||||
const int kNumInsertedKeys = options.level0_file_num_compaction_trigger *
|
||||
(options.max_write_buffer_number - 1) *
|
||||
kKeysPerBuffer;
|
||||
|
||||
Random rnd(301);
|
||||
std::vector<std::string> keys;
|
||||
@@ -3536,6 +3519,59 @@ TEST_P(DBCompactionTestWithParam, FullCompactionInBottomPriThreadPool) {
|
||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CancelCompactionWaitingOnConflict) {
|
||||
// This test verifies cancellation of a compaction waiting to be scheduled due
|
||||
// to conflict with a running compaction.
|
||||
//
|
||||
// A `CompactRange()` in universal compacts all files, waiting for files to
|
||||
// become available if they are locked for another compaction. This test
|
||||
// triggers an automatic compaction that blocks a `CompactRange()`, and
|
||||
// verifies that `DisableManualCompaction()` can successfully cancel the
|
||||
// `CompactRange()` without waiting for the automatic compaction to finish.
|
||||
const int kNumSortedRuns = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.level0_file_num_compaction_trigger = kNumSortedRuns;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
|
||||
Reopen(options);
|
||||
|
||||
test::SleepingBackgroundTask auto_compaction_sleeping_task;
|
||||
// Block automatic compaction when it runs in the callback
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Start",
|
||||
[&](void* /*arg*/) { auto_compaction_sleeping_task.DoSleep(); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Fill overlapping files in L0 to trigger an automatic compaction
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumSortedRuns; ++i) {
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx, true /* nowait */);
|
||||
}
|
||||
auto_compaction_sleeping_task.WaitUntilSleeping();
|
||||
|
||||
// Make sure the manual compaction has seen the conflict before being canceled
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyData::CompactRange:Return",
|
||||
"DBCompactionTest::CancelCompactionWaitingOnConflict:"
|
||||
"PreDisableManualCompaction"}});
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
.IsIncomplete());
|
||||
});
|
||||
|
||||
// Cancel it. Thread should be joinable, i.e., manual compaction was unblocked
|
||||
// despite finding a conflict with an automatic compaction that is still
|
||||
// running
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::CancelCompactionWaitingOnConflict:"
|
||||
"PreDisableManualCompaction");
|
||||
db_->DisableManualCompaction();
|
||||
manual_compaction_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, OptimizedDeletionObsoleting) {
|
||||
// Deletions can be dropped when compacted to non-last level if they fall
|
||||
// outside the lower-level files' key-ranges.
|
||||
@@ -3618,9 +3654,8 @@ TEST_F(DBCompactionTest, CompactFilesPendingL0Bug) {
|
||||
ASSERT_EQ(kNumL0Files, cf_meta.levels[0].files.size());
|
||||
std::vector<std::string> input_filenames;
|
||||
input_filenames.push_back(cf_meta.levels[0].files.front().name);
|
||||
ASSERT_OK(dbfull()
|
||||
->CompactFiles(CompactionOptions(), input_filenames,
|
||||
0 /* output_level */));
|
||||
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), input_filenames,
|
||||
0 /* output_level */));
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactFilesPendingL0Bug:ManualCompacted");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
@@ -4503,9 +4538,9 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
const int kValueSize = 100;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.ttl = 10 * 60 * 60; // 10 hours
|
||||
options.ttl = 10 * 60 * 60; // 10 hours
|
||||
options.periodic_compaction_seconds = 48 * 60 * 60; // 2 days
|
||||
options.max_open_files = -1; // needed for both periodic and ttl compactions
|
||||
options.max_open_files = -1; // needed for both periodic and ttl compactions
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
@@ -4927,7 +4962,7 @@ TEST_F(DBCompactionTest, CompactRangeSkipFlushAfterDelay) {
|
||||
{"DBImpl::FlushMemTable:StallWaitDone", "CompactionJob::Run():End"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
//used for the delayable flushes
|
||||
// used for the delayable flushes
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
for (int i = 0; i < kNumL0FilesLimit - 1; ++i) {
|
||||
@@ -4946,7 +4981,8 @@ TEST_F(DBCompactionTest, CompactRangeSkipFlushAfterDelay) {
|
||||
ASSERT_OK(Put(std::to_string(0), rnd.RandomString(1024)));
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts));
|
||||
ASSERT_OK(Put(std::to_string(0), rnd.RandomString(1024)));
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush");
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush");
|
||||
manual_compaction_thread.join();
|
||||
|
||||
// If CompactRange's flush was skipped, the final Put above will still be
|
||||
@@ -5239,10 +5275,10 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
}
|
||||
|
||||
std::shared_ptr<ConcurrentTaskLimiter> unique_limiter(
|
||||
NewConcurrentTaskLimiter("unique_limiter", -1));
|
||||
NewConcurrentTaskLimiter("unique_limiter", -1));
|
||||
|
||||
const char* cf_names[] = {"default", "0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
|
||||
const char* cf_names[] = {"default", "0", "1", "2", "3", "4", "5", "6", "7",
|
||||
"8", "9", "a", "b", "c", "d", "e", "f"};
|
||||
const unsigned int cf_count = sizeof cf_names / sizeof cf_names[0];
|
||||
|
||||
std::unordered_map<std::string, CompactionLimiter*> cf_to_limiter;
|
||||
@@ -5254,10 +5290,10 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.level0_slowdown_writes_trigger = 64;
|
||||
options.level0_stop_writes_trigger = 64;
|
||||
options.max_background_jobs = kMaxBackgroundThreads; // Enough threads
|
||||
options.max_background_jobs = kMaxBackgroundThreads; // Enough threads
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFile));
|
||||
options.max_write_buffer_number = 10; // Enough memtables
|
||||
options.max_write_buffer_number = 10; // Enough memtables
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::vector<Options> option_vector;
|
||||
@@ -5285,9 +5321,8 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
CreateColumnFamilies({cf_names[cf]}, option_vector[cf]);
|
||||
}
|
||||
|
||||
ReopenWithColumnFamilies(std::vector<std::string>(cf_names,
|
||||
cf_names + cf_count),
|
||||
option_vector);
|
||||
ReopenWithColumnFamilies(
|
||||
std::vector<std::string>(cf_names, cf_names + cf_count), option_vector);
|
||||
|
||||
port::Mutex mutex;
|
||||
|
||||
@@ -5349,7 +5384,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
// Enough L0 files to trigger compaction
|
||||
for (unsigned int cf = 0; cf < cf_count; cf++) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, cf),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
options.level0_file_num_compaction_trigger);
|
||||
}
|
||||
|
||||
// Create more files for one column family, which triggers speed up
|
||||
@@ -5392,7 +5427,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
|
||||
// flush one more file to cf 1
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(cf_test, Key(keyIndex++), ""));
|
||||
ASSERT_OK(Put(cf_test, Key(keyIndex++), ""));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put(cf_test, "", ""));
|
||||
@@ -5427,9 +5462,7 @@ TEST_P(DBCompactionDirectIOTest, DirectIO) {
|
||||
});
|
||||
if (options.use_direct_io_for_flush_and_compaction) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions:direct_io", [&](void* /*arg*/) {
|
||||
readahead = true;
|
||||
});
|
||||
"SanitizeOptions:direct_io", [&](void* /*arg*/) { readahead = true; });
|
||||
}
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -8240,8 +8273,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -500,8 +500,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
+12
-12
@@ -43,7 +43,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
|
||||
Env* target = GetTargetEnv();
|
||||
int hits = 0;
|
||||
for (auto it = fileNames.begin() ; it != fileNames.end(); ++it) {
|
||||
for (auto it = fileNames.begin(); it != fileNames.end(); ++it) {
|
||||
if (*it == "LOCK") {
|
||||
continue;
|
||||
}
|
||||
@@ -64,24 +64,24 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
ASSERT_OK(status);
|
||||
|
||||
if (data.ToString().find("foo567") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
hits++;
|
||||
// std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
if (data.ToString().find("v1.fetdq") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
hits++;
|
||||
// std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
if (data.ToString().find("bar123") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
hits++;
|
||||
// std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
if (data.ToString().find("v2.dfgkjdfghsd") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
hits++;
|
||||
// std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
if (data.ToString().find("dfgk") != std::string::npos) {
|
||||
hits++;
|
||||
//std::cout << "Hit in " << filePath << "\n";
|
||||
hits++;
|
||||
// std::cout << "Hit in " << filePath << "\n";
|
||||
}
|
||||
}
|
||||
if (encrypted_env_) {
|
||||
@@ -119,7 +119,7 @@ TEST_F(DBEncryptionTest, ReadEmptyFile) {
|
||||
ASSERT_TRUE(data.empty());
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -65,8 +65,7 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable) {
|
||||
uint64_t* manifest_file_size, bool flush_memtable) {
|
||||
*manifest_file_size = 0;
|
||||
|
||||
mutex_.Lock();
|
||||
|
||||
+68
-7
@@ -57,7 +57,7 @@ TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
Reopen(options);
|
||||
FlushOptions no_wait;
|
||||
no_wait.wait = false;
|
||||
no_wait.allow_write_stall=true;
|
||||
no_wait.allow_write_stall = true;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"VersionSet::LogAndApply:WriteManifest",
|
||||
@@ -746,6 +746,64 @@ class TestFlushListener : public EventListener {
|
||||
};
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// RocksDB lite does not support GetLiveFiles()
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBFlushTest, FixFlushReasonRaceFromConcurrentFlushes) {
|
||||
Options options = CurrentOptions();
|
||||
options.atomic_flush = true;
|
||||
options.disable_auto_compactions = true;
|
||||
CreateAndReopenWithCF({"cf1"}, options);
|
||||
|
||||
for (int idx = 0; idx < 1; ++idx) {
|
||||
ASSERT_OK(Put(0, Key(idx), std::string(1, 'v')));
|
||||
ASSERT_OK(Put(1, Key(idx), std::string(1, 'v')));
|
||||
}
|
||||
|
||||
// To coerce a manual flush happenning in the middle of GetLiveFiles's flush,
|
||||
// we need to pause background flush thread and enable it later.
|
||||
std::shared_ptr<test::SleepingBackgroundTask> sleeping_task =
|
||||
std::make_shared<test::SleepingBackgroundTask>();
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
sleeping_task.get(), Env::Priority::HIGH);
|
||||
sleeping_task->WaitUntilSleeping();
|
||||
|
||||
// Coerce a manual flush happenning in the middle of GetLiveFiles's flush
|
||||
bool get_live_files_paused_at_sync_point = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AtomicFlushMemTables:AfterScheduleFlush", [&](void* /* arg */) {
|
||||
if (get_live_files_paused_at_sync_point) {
|
||||
// To prevent non-GetLiveFiles() flush from pausing at this sync point
|
||||
return;
|
||||
}
|
||||
get_live_files_paused_at_sync_point = true;
|
||||
|
||||
FlushOptions fo;
|
||||
fo.wait = false;
|
||||
fo.allow_write_stall = true;
|
||||
ASSERT_OK(dbfull()->Flush(fo));
|
||||
|
||||
// Resume background flush thread so GetLiveFiles() can finish
|
||||
sleeping_task->WakeUp();
|
||||
sleeping_task->WaitUntilDone();
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> files;
|
||||
uint64_t manifest_file_size;
|
||||
// Before the fix, a race condition on default cf's flush reason due to
|
||||
// concurrent GetLiveFiles's flush and manual flush will fail
|
||||
// an internal assertion.
|
||||
// After the fix, such race condition is fixed and there is no assertion
|
||||
// failure.
|
||||
ASSERT_OK(db_->GetLiveFiles(files, &manifest_file_size, /*flush*/ true));
|
||||
ASSERT_TRUE(get_live_files_paused_at_sync_point);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -1822,8 +1880,8 @@ TEST_F(DBFlushTest, ManualFlushFailsInReadOnlyMode) {
|
||||
ASSERT_NOK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
#ifndef ROCKSDB_LITE
|
||||
uint64_t num_bg_errors;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBackgroundErrors,
|
||||
&num_bg_errors));
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kBackgroundErrors, &num_bg_errors));
|
||||
ASSERT_GT(num_bg_errors, 0);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -2440,7 +2498,9 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
options.atomic_flush = GetParam();
|
||||
// 64MB so that memtable flush won't be trigger by the small writes.
|
||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
flush_listener->expected_flush_reason = FlushReason::kManualFlush;
|
||||
options.listeners.push_back(flush_listener);
|
||||
// Destroy the DB to recreate as a TransactionDB.
|
||||
Close();
|
||||
Destroy(options, true);
|
||||
@@ -2507,7 +2567,6 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
ASSERT_EQ(cfh->cfd()->GetFlushReason(), FlushReason::kManualFlush);
|
||||
}
|
||||
|
||||
// The recovered min log number with prepared data should be non-zero.
|
||||
@@ -2520,13 +2579,15 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
ASSERT_TRUE(db_impl->allow_2pc());
|
||||
ASSERT_NE(db_impl->MinLogNumberToKeep(), 0);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = GetParam();
|
||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||
flush_listener->expected_flush_reason = FlushReason::kManualFlush;
|
||||
options.listeners.push_back(flush_listener);
|
||||
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
size_t num_cfs = handles_.size();
|
||||
@@ -2551,11 +2612,11 @@ TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(cfh->cfd()->GetFlushReason(), FlushReason::kManualFlush);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_P(DBAtomicFlushTest, PrecomputeMinLogNumberToKeepNon2PC) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -26,16 +26,16 @@ CompactedDBImpl::CompactedDBImpl(const DBOptions& options,
|
||||
version_(nullptr),
|
||||
user_comparator_(nullptr) {}
|
||||
|
||||
CompactedDBImpl::~CompactedDBImpl() {
|
||||
}
|
||||
CompactedDBImpl::~CompactedDBImpl() {}
|
||||
|
||||
size_t CompactedDBImpl::FindFile(const Slice& key) {
|
||||
size_t right = files_.num_files - 1;
|
||||
auto cmp = [&](const FdWithKeyRange& f, const Slice& k) -> bool {
|
||||
return user_comparator_->Compare(ExtractUserKey(f.largest_key), k) < 0;
|
||||
};
|
||||
return static_cast<size_t>(std::lower_bound(files_.files,
|
||||
files_.files + right, key, cmp) - files_.files);
|
||||
return static_cast<size_t>(
|
||||
std::lower_bound(files_.files, files_.files + right, key, cmp) -
|
||||
files_.files);
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
@@ -228,8 +228,8 @@ Status CompactedDBImpl::Init(const Options& options) {
|
||||
return Status::NotSupported("no file exists");
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Open(const Options& options,
|
||||
const std::string& dbname, DB** dbptr) {
|
||||
Status CompactedDBImpl::Open(const Options& options, const std::string& dbname,
|
||||
DB** dbptr) {
|
||||
*dbptr = nullptr;
|
||||
|
||||
if (options.max_open_files != -1) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
+28
-26
@@ -604,7 +604,7 @@ Status DBImpl::CloseHelper() {
|
||||
|
||||
while (!flush_queue_.empty()) {
|
||||
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
||||
for (const auto& iter : flush_req) {
|
||||
for (const auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
iter.first->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
@@ -1059,16 +1059,31 @@ void DBImpl::DumpStats() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Also probe block cache(s) for problems, dump to info log
|
||||
UnorderedSet<Cache*> probed_caches;
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:StartRunning");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
// Release DB mutex for gathering cache entry stats. Pass over all
|
||||
// column families for this first so that other stats are dumped
|
||||
// near-atomically.
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Release DB mutex for gathering cache entry stats. Pass over all
|
||||
// column families for this first so that other stats are dumped
|
||||
// near-atomically.
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
|
||||
// Probe block cache for problems (if not already via another CF)
|
||||
if (immutable_db_options_.info_log) {
|
||||
auto* table_factory = cfd->ioptions()->table_factory.get();
|
||||
assert(table_factory != nullptr);
|
||||
Cache* cache =
|
||||
table_factory->GetOptions<Cache>(TableFactory::kBlockCacheOpts());
|
||||
if (cache && probed_caches.insert(cache).second) {
|
||||
cache->ReportProblems(immutable_db_options_.info_log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1079,18 +1094,7 @@ void DBImpl::DumpStats() {
|
||||
default_cf_internal_stats_->GetStringProperty(*property_info, *property,
|
||||
&stats);
|
||||
|
||||
property = &DB::Properties::kCFStatsNoFileHistogram;
|
||||
property_info = GetPropertyInfo(*property);
|
||||
assert(property_info != nullptr);
|
||||
assert(!property_info->need_out_of_mutex);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
cfd->internal_stats()->GetStringProperty(*property_info, *property,
|
||||
&stats);
|
||||
}
|
||||
}
|
||||
|
||||
property = &DB::Properties::kCFFileHistogram;
|
||||
property = &InternalStats::kPeriodicCFStats;
|
||||
property_info = GetPropertyInfo(*property);
|
||||
assert(property_info != nullptr);
|
||||
assert(!property_info->need_out_of_mutex);
|
||||
@@ -1357,7 +1361,7 @@ Status DBImpl::SetDBOptions(
|
||||
file_options_for_compaction_ = fs_->OptimizeForCompactionTableWrite(
|
||||
file_options_for_compaction_, immutable_db_options_);
|
||||
versions_->ChangeFileOptions(mutable_db_options_);
|
||||
//TODO(xiez): clarify why apply optimize for read to write options
|
||||
// TODO(xiez): clarify why apply optimize for read to write options
|
||||
file_options_for_compaction_ = fs_->OptimizeForCompactionTableRead(
|
||||
file_options_for_compaction_, immutable_db_options_);
|
||||
file_options_for_compaction_.compaction_readahead_size =
|
||||
@@ -2358,8 +2362,8 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[keys_read] : nullptr;
|
||||
|
||||
LookupKey lkey(keys[keys_read], consistent_seqnum, read_options.timestamp);
|
||||
auto cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(column_family[keys_read]);
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(
|
||||
column_family[keys_read]);
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
auto mgd_iter = multiget_cf_data.find(cfh->cfd()->GetID());
|
||||
assert(mgd_iter != multiget_cf_data.end());
|
||||
@@ -3984,8 +3988,7 @@ SuperVersion* DBImpl::GetAndRefSuperVersion(uint32_t column_family_id) {
|
||||
void DBImpl::CleanupSuperVersion(SuperVersion* sv) {
|
||||
// Release SuperVersion
|
||||
if (sv->Unref()) {
|
||||
bool defer_purge =
|
||||
immutable_db_options().avoid_unnecessary_blocking_io;
|
||||
bool defer_purge = immutable_db_options().avoid_unnecessary_blocking_io;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
sv->Cleanup();
|
||||
@@ -5669,8 +5672,7 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
}
|
||||
}
|
||||
|
||||
bool defer_purge =
|
||||
immutable_db_options().avoid_unnecessary_blocking_io;
|
||||
bool defer_purge = immutable_db_options().avoid_unnecessary_blocking_io;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto sv : sv_list) {
|
||||
|
||||
+28
-14
@@ -16,6 +16,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -204,6 +205,9 @@ class DBImpl : public DB {
|
||||
using DB::Merge;
|
||||
Status Merge(const WriteOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& value) override;
|
||||
Status Merge(const WriteOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& ts, const Slice& value) override;
|
||||
|
||||
using DB::Delete;
|
||||
Status Delete(const WriteOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key) override;
|
||||
@@ -1380,7 +1384,7 @@ class DBImpl : public DB {
|
||||
|
||||
void NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id);
|
||||
int job_id, FlushReason flush_reason);
|
||||
|
||||
void NotifyOnFlushCompleted(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
@@ -1672,12 +1676,17 @@ class DBImpl : public DB {
|
||||
// Argument required by background flush thread.
|
||||
struct BGFlushArg {
|
||||
BGFlushArg()
|
||||
: cfd_(nullptr), max_memtable_id_(0), superversion_context_(nullptr) {}
|
||||
: cfd_(nullptr),
|
||||
max_memtable_id_(0),
|
||||
superversion_context_(nullptr),
|
||||
flush_reason_(FlushReason::kOthers) {}
|
||||
BGFlushArg(ColumnFamilyData* cfd, uint64_t max_memtable_id,
|
||||
SuperVersionContext* superversion_context)
|
||||
SuperVersionContext* superversion_context,
|
||||
FlushReason flush_reason)
|
||||
: cfd_(cfd),
|
||||
max_memtable_id_(max_memtable_id),
|
||||
superversion_context_(superversion_context) {}
|
||||
superversion_context_(superversion_context),
|
||||
flush_reason_(flush_reason) {}
|
||||
|
||||
// Column family to flush.
|
||||
ColumnFamilyData* cfd_;
|
||||
@@ -1688,6 +1697,7 @@ class DBImpl : public DB {
|
||||
// installs a new superversion for the column family. This operation
|
||||
// requires a SuperVersionContext object (currently embedded in JobContext).
|
||||
SuperVersionContext* superversion_context_;
|
||||
FlushReason flush_reason_;
|
||||
};
|
||||
|
||||
// Argument passed to flush thread.
|
||||
@@ -1816,7 +1826,7 @@ class DBImpl : public DB {
|
||||
// installs a new super version for the column family.
|
||||
Status FlushMemTableToOutputFile(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
bool* madeProgress, JobContext* job_context,
|
||||
bool* madeProgress, JobContext* job_context, FlushReason flush_reason,
|
||||
SuperVersionContext* superversion_context,
|
||||
std::vector<SequenceNumber>& snapshot_seqs,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
@@ -2034,18 +2044,22 @@ class DBImpl : public DB {
|
||||
|
||||
void MaybeScheduleFlushOrCompaction();
|
||||
|
||||
// A flush request specifies the column families to flush as well as the
|
||||
// largest memtable id to persist for each column family. Once all the
|
||||
// memtables whose IDs are smaller than or equal to this per-column-family
|
||||
// specified value, this flush request is considered to have completed its
|
||||
// work of flushing this column family. After completing the work for all
|
||||
// column families in this request, this flush is considered complete.
|
||||
using FlushRequest = std::vector<std::pair<ColumnFamilyData*, uint64_t>>;
|
||||
struct FlushRequest {
|
||||
FlushReason flush_reason;
|
||||
// A map from column family to flush to largest memtable id to persist for
|
||||
// each column family. Once all the memtables whose IDs are smaller than or
|
||||
// equal to this per-column-family specified value, this flush request is
|
||||
// considered to have completed its work of flushing this column family.
|
||||
// After completing the work for all column families in this request, this
|
||||
// flush is considered complete.
|
||||
std::unordered_map<ColumnFamilyData*, uint64_t>
|
||||
cfd_to_max_mem_id_to_persist;
|
||||
};
|
||||
|
||||
void GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
FlushRequest* req);
|
||||
FlushReason flush_reason, FlushRequest* req);
|
||||
|
||||
void SchedulePendingFlush(const FlushRequest& req, FlushReason flush_reason);
|
||||
void SchedulePendingFlush(const FlushRequest& req);
|
||||
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
|
||||
@@ -155,7 +155,7 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context,
|
||||
|
||||
Status DBImpl::FlushMemTableToOutputFile(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
bool* made_progress, JobContext* job_context,
|
||||
bool* made_progress, JobContext* job_context, FlushReason flush_reason,
|
||||
SuperVersionContext* superversion_context,
|
||||
std::vector<SequenceNumber>& snapshot_seqs,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
@@ -215,7 +215,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options, max_memtable_id,
|
||||
file_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
job_context, log_buffer, directories_.GetDbDir(), GetDataDir(cfd, 0U),
|
||||
job_context, flush_reason, log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(cfd, 0U),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||
@@ -260,7 +261,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id);
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id,
|
||||
flush_reason);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
bool switched_to_mempurge = false;
|
||||
@@ -390,8 +392,9 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
MutableCFOptions mutable_cf_options_copy = *cfd->GetLatestMutableCFOptions();
|
||||
SuperVersionContext* superversion_context =
|
||||
bg_flush_arg.superversion_context_;
|
||||
FlushReason flush_reason = bg_flush_arg.flush_reason_;
|
||||
Status s = FlushMemTableToOutputFile(
|
||||
cfd, mutable_cf_options_copy, made_progress, job_context,
|
||||
cfd, mutable_cf_options_copy, made_progress, job_context, flush_reason,
|
||||
superversion_context, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, log_buffer, thread_pri);
|
||||
return s;
|
||||
@@ -420,7 +423,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
for (const auto cfd : cfds) {
|
||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||
assert(cfd->imm()->IsFlushPending());
|
||||
assert(cfd->GetFlushReason() == cfds[0]->GetFlushReason());
|
||||
}
|
||||
for (const auto bg_flush_arg : bg_flush_args) {
|
||||
assert(bg_flush_arg.flush_reason_ == bg_flush_args[0].flush_reason_);
|
||||
}
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
@@ -459,13 +464,15 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
|
||||
uint64_t max_memtable_id = bg_flush_args[i].max_memtable_id_;
|
||||
FlushReason flush_reason = bg_flush_args[i].flush_reason_;
|
||||
jobs.emplace_back(new FlushJob(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
max_memtable_id, file_options_for_compaction_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, job_context, log_buffer, directories_.GetDbDir(),
|
||||
data_dir, GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
stats_, &event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
snapshot_checker, job_context, flush_reason, log_buffer,
|
||||
directories_.GetDbDir(), data_dir,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri, io_tracer_, seqno_time_mapping_, db_id_, db_session_id_,
|
||||
cfd->GetFullHistoryTsLow(), &blob_callback_));
|
||||
@@ -483,8 +490,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.at(i);
|
||||
// may temporarily unlock and lock the mutex.
|
||||
FlushReason flush_reason = bg_flush_args[i].flush_reason_;
|
||||
NotifyOnFlushBegin(cfds[i], &file_meta[i], mutable_cf_options,
|
||||
job_context->job_id);
|
||||
job_context->job_id, flush_reason);
|
||||
}
|
||||
#endif /* !ROCKSDB_LITE */
|
||||
|
||||
@@ -642,8 +650,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
bool resuming_from_bg_err =
|
||||
error_handler_.IsDBStopped() ||
|
||||
(cfds[0]->GetFlushReason() == FlushReason::kErrorRecovery ||
|
||||
cfds[0]->GetFlushReason() == FlushReason::kErrorRecoveryRetryFlush);
|
||||
(bg_flush_args[0].flush_reason_ == FlushReason::kErrorRecovery ||
|
||||
bg_flush_args[0].flush_reason_ ==
|
||||
FlushReason::kErrorRecoveryRetryFlush);
|
||||
while ((!resuming_from_bg_err || error_handler_.GetRecoveryError().ok())) {
|
||||
std::pair<Status, bool> res = wait_to_install_func();
|
||||
|
||||
@@ -660,8 +669,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
resuming_from_bg_err =
|
||||
error_handler_.IsDBStopped() ||
|
||||
(cfds[0]->GetFlushReason() == FlushReason::kErrorRecovery ||
|
||||
cfds[0]->GetFlushReason() == FlushReason::kErrorRecoveryRetryFlush);
|
||||
(bg_flush_args[0].flush_reason_ == FlushReason::kErrorRecovery ||
|
||||
bg_flush_args[0].flush_reason_ ==
|
||||
FlushReason::kErrorRecoveryRetryFlush);
|
||||
}
|
||||
|
||||
if (!resuming_from_bg_err) {
|
||||
@@ -816,7 +826,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id) {
|
||||
int job_id, FlushReason flush_reason) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (immutable_db_options_.listeners.size() == 0U) {
|
||||
return;
|
||||
@@ -849,7 +859,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
info.triggered_writes_stop = triggered_writes_stop;
|
||||
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
||||
info.largest_seqno = file_meta->fd.largest_seqno;
|
||||
info.flush_reason = cfd->GetFlushReason();
|
||||
info.flush_reason = flush_reason;
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushBegin(this, info);
|
||||
}
|
||||
@@ -862,6 +872,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
(void)file_meta;
|
||||
(void)mutable_cf_options;
|
||||
(void)job_id;
|
||||
(void)flush_reason;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -1919,11 +1930,19 @@ Status DBImpl::RunManualCompaction(
|
||||
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
||||
max_file_num_to_ignore, trim_ts)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
// exclusive manual compactions should not see a conflict during
|
||||
// CompactRange
|
||||
assert(!exclusive || !manual_conflict);
|
||||
// Running either this or some other manual compaction
|
||||
bg_cv_.Wait();
|
||||
if (!scheduled) {
|
||||
// There is a conflicting compaction
|
||||
if (manual_compaction_paused_ > 0 || manual.canceled == true) {
|
||||
// Stop waiting since it was canceled. Pretend the error came from
|
||||
// compaction so the below cleanup/error handling code can process it.
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
}
|
||||
if (!manual.done) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
if (manual_compaction_paused_ > 0 && scheduled && !unscheduled) {
|
||||
assert(thread_pool_priority != Env::Priority::TOTAL);
|
||||
// unschedule all manual compactions
|
||||
@@ -2001,16 +2020,17 @@ Status DBImpl::RunManualCompaction(
|
||||
}
|
||||
|
||||
void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
FlushRequest* req) {
|
||||
FlushReason flush_reason, FlushRequest* req) {
|
||||
assert(req != nullptr);
|
||||
req->reserve(cfds.size());
|
||||
req->flush_reason = flush_reason;
|
||||
req->cfd_to_max_mem_id_to_persist.reserve(cfds.size());
|
||||
for (const auto cfd : cfds) {
|
||||
if (nullptr == cfd) {
|
||||
// cfd may be null, see DBImpl::ScheduleFlushes
|
||||
continue;
|
||||
}
|
||||
uint64_t max_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
req->emplace_back(cfd, max_memtable_id);
|
||||
req->cfd_to_max_mem_id_to_persist.emplace(cfd, max_memtable_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2068,7 +2088,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
FlushRequest req{{cfd, flush_memtable_id}};
|
||||
FlushRequest req{flush_reason, {{cfd, flush_memtable_id}}};
|
||||
flush_reqs.emplace_back(std::move(req));
|
||||
memtable_ids_to_wait.emplace_back(cfd->imm()->GetLatestMemTableID());
|
||||
}
|
||||
@@ -2096,7 +2116,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
"to avoid holding old logs",
|
||||
cfd->GetName().c_str());
|
||||
s = SwitchMemtable(cfd_stats, &context);
|
||||
FlushRequest req{{cfd_stats, flush_memtable_id}};
|
||||
FlushRequest req{flush_reason, {{cfd_stats, flush_memtable_id}}};
|
||||
flush_reqs.emplace_back(std::move(req));
|
||||
memtable_ids_to_wait.emplace_back(
|
||||
cfd->imm()->GetLatestMemTableID());
|
||||
@@ -2107,8 +2127,9 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
|
||||
if (s.ok() && !flush_reqs.empty()) {
|
||||
for (const auto& req : flush_reqs) {
|
||||
assert(req.size() == 1);
|
||||
ColumnFamilyData* loop_cfd = req[0].first;
|
||||
assert(req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
ColumnFamilyData* loop_cfd =
|
||||
req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||
loop_cfd->imm()->FlushRequested();
|
||||
}
|
||||
// If the caller wants to wait for this flush to complete, it indicates
|
||||
@@ -2117,13 +2138,14 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
// Therefore, we increase the cfd's ref count.
|
||||
if (flush_options.wait) {
|
||||
for (const auto& req : flush_reqs) {
|
||||
assert(req.size() == 1);
|
||||
ColumnFamilyData* loop_cfd = req[0].first;
|
||||
assert(req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
ColumnFamilyData* loop_cfd =
|
||||
req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||
loop_cfd->Ref();
|
||||
}
|
||||
}
|
||||
for (const auto& req : flush_reqs) {
|
||||
SchedulePendingFlush(req, flush_reason);
|
||||
SchedulePendingFlush(req);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -2142,8 +2164,8 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
assert(flush_reqs.size() == memtable_ids_to_wait.size());
|
||||
for (size_t i = 0; i < flush_reqs.size(); ++i) {
|
||||
assert(flush_reqs[i].size() == 1);
|
||||
cfds.push_back(flush_reqs[i][0].first);
|
||||
assert(flush_reqs[i].cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
cfds.push_back(flush_reqs[i].cfd_to_max_mem_id_to_persist.begin()->first);
|
||||
flush_memtable_ids.push_back(&(memtable_ids_to_wait[i]));
|
||||
}
|
||||
s = WaitForFlushMemTables(
|
||||
@@ -2240,8 +2262,8 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
cfd->Ref();
|
||||
}
|
||||
}
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, flush_reason);
|
||||
GenerateFlushRequest(cfds, flush_reason, &flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
@@ -2256,7 +2278,7 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
for (auto& iter : flush_req) {
|
||||
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
flush_memtable_ids.push_back(&(iter.second));
|
||||
}
|
||||
s = WaitForFlushMemTables(
|
||||
@@ -2603,9 +2625,9 @@ DBImpl::FlushRequest DBImpl::PopFirstFromFlushQueue() {
|
||||
FlushRequest flush_req = flush_queue_.front();
|
||||
flush_queue_.pop_front();
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
assert(flush_req.size() == 1);
|
||||
assert(flush_req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
}
|
||||
for (const auto& elem : flush_req) {
|
||||
for (const auto& elem : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
ColumnFamilyData* cfd = elem.first;
|
||||
assert(cfd);
|
||||
@@ -2613,7 +2635,6 @@ DBImpl::FlushRequest DBImpl::PopFirstFromFlushQueue() {
|
||||
cfd->set_queued_for_flush(false);
|
||||
}
|
||||
}
|
||||
// TODO: need to unset flush reason?
|
||||
return flush_req;
|
||||
}
|
||||
|
||||
@@ -2643,31 +2664,29 @@ ColumnFamilyData* DBImpl::PickCompactionFromQueue(
|
||||
return cfd;
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req,
|
||||
FlushReason flush_reason) {
|
||||
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
|
||||
mutex_.AssertHeld();
|
||||
if (flush_req.empty()) {
|
||||
if (flush_req.cfd_to_max_mem_id_to_persist.empty()) {
|
||||
return;
|
||||
}
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
// For the non-atomic flush case, we never schedule multiple column
|
||||
// families in the same flush request.
|
||||
assert(flush_req.size() == 1);
|
||||
ColumnFamilyData* cfd = flush_req[0].first;
|
||||
assert(flush_req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
ColumnFamilyData* cfd =
|
||||
flush_req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||
assert(cfd);
|
||||
|
||||
if (!cfd->queued_for_flush() && cfd->imm()->IsFlushPending()) {
|
||||
cfd->Ref();
|
||||
cfd->set_queued_for_flush(true);
|
||||
cfd->SetFlushReason(flush_reason);
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
}
|
||||
} else {
|
||||
for (auto& iter : flush_req) {
|
||||
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
cfd->Ref();
|
||||
cfd->SetFlushReason(flush_reason);
|
||||
}
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
@@ -2799,10 +2818,12 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
while (!flush_queue_.empty()) {
|
||||
// This cfd is already referenced
|
||||
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
||||
FlushReason flush_reason = flush_req.flush_reason;
|
||||
superversion_contexts.clear();
|
||||
superversion_contexts.reserve(flush_req.size());
|
||||
superversion_contexts.reserve(
|
||||
flush_req.cfd_to_max_mem_id_to_persist.size());
|
||||
|
||||
for (const auto& iter : flush_req) {
|
||||
for (const auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
if (cfd->GetMempurgeUsed()) {
|
||||
// If imm() contains silent memtables (e.g.: because
|
||||
@@ -2818,7 +2839,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
}
|
||||
superversion_contexts.emplace_back(SuperVersionContext(true));
|
||||
bg_flush_args.emplace_back(cfd, iter.second,
|
||||
&(superversion_contexts.back()));
|
||||
&(superversion_contexts.back()), flush_reason);
|
||||
}
|
||||
if (!bg_flush_args.empty()) {
|
||||
break;
|
||||
@@ -2842,9 +2863,14 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
status = FlushMemTablesToOutputFiles(bg_flush_args, made_progress,
|
||||
job_context, log_buffer, thread_pri);
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundFlush:BeforeFlush");
|
||||
// All the CFDs in the FlushReq must have the same flush reason, so just
|
||||
// grab the first one
|
||||
*reason = bg_flush_args[0].cfd_->GetFlushReason();
|
||||
// All the CFD/bg_flush_arg in the FlushReq must have the same flush reason, so
|
||||
// just grab the first one
|
||||
#ifndef NDEBUG
|
||||
for (const auto bg_flush_arg : bg_flush_args) {
|
||||
assert(bg_flush_arg.flush_reason_ == bg_flush_args[0].flush_reason_);
|
||||
}
|
||||
#endif /* !NDEBUG */
|
||||
*reason = bg_flush_args[0].flush_reason_;
|
||||
for (auto& arg : bg_flush_args) {
|
||||
ColumnFamilyData* cfd = arg.cfd_;
|
||||
if (cfd->UnrefAndTryDelete()) {
|
||||
|
||||
@@ -270,7 +270,8 @@ Status DBImpl::ValidateOptions(const DBOptions& db_options) {
|
||||
if (db_options.unordered_write &&
|
||||
!db_options.allow_concurrent_memtable_write) {
|
||||
return Status::InvalidArgument(
|
||||
"unordered_write is incompatible with !allow_concurrent_memtable_write");
|
||||
"unordered_write is incompatible with "
|
||||
"!allow_concurrent_memtable_write");
|
||||
}
|
||||
|
||||
if (db_options.unordered_write && db_options.enable_pipelined_write) {
|
||||
@@ -1058,9 +1059,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
std::unique_ptr<SequentialFileReader> file_reader;
|
||||
{
|
||||
std::unique_ptr<FSSequentialFile> file;
|
||||
status = fs_->NewSequentialFile(fname,
|
||||
fs_->OptimizeForLogRead(file_options_),
|
||||
&file, nullptr);
|
||||
status = fs_->NewSequentialFile(
|
||||
fname, fs_->OptimizeForLogRead(file_options_), &file, nullptr);
|
||||
if (!status.ok()) {
|
||||
MaybeIgnoreError(&status);
|
||||
if (!status.ok()) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -157,8 +157,7 @@ Status DBImplSecondary::MaybeInitLogReader(
|
||||
{
|
||||
std::unique_ptr<FSSequentialFile> file;
|
||||
Status status = fs_->NewSequentialFile(
|
||||
fname, fs_->OptimizeForLogRead(file_options_), &file,
|
||||
nullptr);
|
||||
fname, fs_->OptimizeForLogRead(file_options_), &file, nullptr);
|
||||
if (!status.ok()) {
|
||||
*log_reader = nullptr;
|
||||
return status;
|
||||
@@ -200,7 +199,7 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
assert(reader != nullptr);
|
||||
}
|
||||
for (auto log_number : log_numbers) {
|
||||
auto it = log_readers_.find(log_number);
|
||||
auto it = log_readers_.find(log_number);
|
||||
assert(it != log_readers_.end());
|
||||
log::FragmentBufferedReader* reader = it->second->reader_;
|
||||
Status* wal_read_status = it->second->status_;
|
||||
|
||||
@@ -47,6 +47,7 @@ class LogReaderContainer {
|
||||
delete reporter_;
|
||||
delete status_;
|
||||
}
|
||||
|
||||
private:
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
@@ -247,7 +248,6 @@ class DBImplSecondary : public DBImpl {
|
||||
// method can take long time due to all the I/O and CPU costs.
|
||||
Status TryCatchUpWithPrimary() override;
|
||||
|
||||
|
||||
// Try to find log reader using log_number from log_readers_ map, initialize
|
||||
// if it doesn't exist
|
||||
Status MaybeInitLogReader(uint64_t log_number,
|
||||
|
||||
+39
-12
@@ -62,6 +62,15 @@ Status DBImpl::Merge(const WriteOptions& o, ColumnFamilyHandle* column_family,
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::Merge(const WriteOptions& o, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& ts, const Slice& val) {
|
||||
const Status s = FailIfTsMismatchCf(column_family, ts, /*ts_for_read=*/false);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return DB::Merge(o, column_family, key, ts, val);
|
||||
}
|
||||
|
||||
Status DBImpl::Delete(const WriteOptions& write_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key) {
|
||||
const Status s = FailIfCfHasTs(column_family);
|
||||
@@ -1635,14 +1644,14 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
cfd->imm()->FlushRequested();
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWalFull);
|
||||
GenerateFlushRequest({cfd}, FlushReason::kWalFull, &flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWalFull);
|
||||
GenerateFlushRequest(cfds, FlushReason::kWalFull, &flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -1726,14 +1735,15 @@ Status DBImpl::HandleWriteBufferManagerFlush(WriteContext* write_context) {
|
||||
cfd->imm()->FlushRequested();
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
GenerateFlushRequest({cfd}, FlushReason::kWriteBufferManager,
|
||||
&flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
GenerateFlushRequest(cfds, FlushReason::kWriteBufferManager, &flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -1989,13 +1999,13 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
GenerateFlushRequest(cfds, FlushReason::kWriteBufferFull, &flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
} else {
|
||||
for (auto* cfd : cfds) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
GenerateFlushRequest({cfd}, FlushReason::kWriteBufferFull, &flush_req);
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -2406,4 +2416,21 @@ Status DB::Merge(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
Status DB::Merge(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& ts, const Slice& value) {
|
||||
ColumnFamilyHandle* default_cf = DefaultColumnFamily();
|
||||
assert(default_cf);
|
||||
const Comparator* const default_cf_ucmp = default_cf->GetComparator();
|
||||
assert(default_cf_ucmp);
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */,
|
||||
opt.protection_bytes_per_key,
|
||||
default_cf_ucmp->timestamp_size());
|
||||
Status s = batch.Merge(column_family, key, ts, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "db/db_info_dumper.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
|
||||
+69
-42
@@ -158,6 +158,8 @@ void DBIter::Next() {
|
||||
|
||||
local_stats_.next_count_++;
|
||||
if (ok && iter_.Valid()) {
|
||||
ClearSavedValue();
|
||||
|
||||
if (prefix_same_as_start_) {
|
||||
assert(prefix_extractor_ != nullptr);
|
||||
const Slice prefix = prefix_.GetUserKey();
|
||||
@@ -523,11 +525,13 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
if (!user_comparator_.EqualWithoutTimestamp(ikey.user_key,
|
||||
saved_key_.GetUserKey())) {
|
||||
// hit the next user key, stop right here
|
||||
break;
|
||||
}
|
||||
if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type) {
|
||||
if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type ||
|
||||
kTypeDeletionWithTimestamp == ikey.type) {
|
||||
// hit a delete with the same user key, stop right here
|
||||
// iter_ is positioned after delete
|
||||
iter_.Next();
|
||||
@@ -542,8 +546,7 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
const Slice val = iter_.value();
|
||||
Status s = Merge(&val, ikey.user_key);
|
||||
if (!s.ok()) {
|
||||
if (!Merge(&val, ikey.user_key)) {
|
||||
return false;
|
||||
}
|
||||
// iter_ is positioned after put
|
||||
@@ -572,8 +575,7 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
Status s = Merge(&blob_value_, ikey.user_key);
|
||||
if (!s.ok()) {
|
||||
if (!Merge(&blob_value_, ikey.user_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -587,11 +589,18 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
}
|
||||
return true;
|
||||
} else if (kTypeWideColumnEntity == ikey.type) {
|
||||
// TODO: support wide-column entities
|
||||
status_ = Status::NotSupported(
|
||||
"Merge currently not supported for wide-column entities");
|
||||
valid_ = false;
|
||||
return false;
|
||||
if (!MergeEntity(iter_.value(), ikey.user_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// iter_ is positioned after put
|
||||
iter_.Next();
|
||||
if (!iter_.status().ok()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = Status::Corruption(
|
||||
@@ -610,8 +619,7 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
// a deletion marker.
|
||||
// feed null as the existing value to the merge operator, such that
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
Status s = Merge(nullptr, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
if (!Merge(nullptr, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
assert(status_.ok());
|
||||
@@ -634,6 +642,8 @@ void DBIter::Prev() {
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
ClearSavedValue();
|
||||
|
||||
Slice prefix;
|
||||
if (prefix_same_as_start_) {
|
||||
assert(prefix_extractor_ != nullptr);
|
||||
@@ -956,9 +966,9 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
case kTypeMerge:
|
||||
current_entry_is_merged_ = true;
|
||||
if (last_not_merge_type == kTypeDeletion ||
|
||||
last_not_merge_type == kTypeSingleDeletion) {
|
||||
s = Merge(nullptr, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
last_not_merge_type == kTypeSingleDeletion ||
|
||||
last_not_merge_type == kTypeDeletionWithTimestamp) {
|
||||
if (!Merge(nullptr, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -973,8 +983,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
s = Merge(&blob_value_, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
if (!Merge(&blob_value_, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -982,15 +991,14 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
|
||||
return true;
|
||||
} else if (last_not_merge_type == kTypeWideColumnEntity) {
|
||||
// TODO: support wide-column entities
|
||||
status_ = Status::NotSupported(
|
||||
"Merge currently not supported for wide-column entities");
|
||||
valid_ = false;
|
||||
return false;
|
||||
if (!MergeEntity(pinned_value_, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
assert(last_not_merge_type == kTypeValue);
|
||||
s = Merge(&pinned_value_, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
if (!Merge(&pinned_value_, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1159,10 +1167,12 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
if (!ParseKey(&ikey)) {
|
||||
return false;
|
||||
}
|
||||
if (!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
if (!user_comparator_.EqualWithoutTimestamp(ikey.user_key,
|
||||
saved_key_.GetUserKey())) {
|
||||
break;
|
||||
}
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion) {
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
ikey.type == kTypeDeletionWithTimestamp) {
|
||||
break;
|
||||
}
|
||||
if (!iter_.PrepareValue()) {
|
||||
@@ -1172,8 +1182,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
|
||||
if (ikey.type == kTypeValue) {
|
||||
const Slice val = iter_.value();
|
||||
Status s = Merge(&val, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
if (!Merge(&val, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1192,8 +1201,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
Status s = Merge(&blob_value_, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
if (!Merge(&blob_value_, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1201,11 +1209,11 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
|
||||
return true;
|
||||
} else if (ikey.type == kTypeWideColumnEntity) {
|
||||
// TODO: support wide-column entities
|
||||
status_ = Status::NotSupported(
|
||||
"Merge currently not supported for wide-column entities");
|
||||
valid_ = false;
|
||||
return false;
|
||||
if (!MergeEntity(iter_.value(), saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = Status::Corruption(
|
||||
@@ -1215,8 +1223,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
}
|
||||
}
|
||||
|
||||
Status s = Merge(nullptr, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
if (!Merge(nullptr, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1239,21 +1246,41 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
return true;
|
||||
}
|
||||
|
||||
Status DBIter::Merge(const Slice* val, const Slice& user_key) {
|
||||
bool DBIter::Merge(const Slice* val, const Slice& user_key) {
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, user_key, val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, clock_, &pinned_value_, true);
|
||||
&saved_value_, logger_, statistics_, clock_, &pinned_value_,
|
||||
/* update_num_ops_stats */ true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
return s;
|
||||
return false;
|
||||
}
|
||||
|
||||
SetValueAndColumnsFromPlain(pinned_value_.data() ? pinned_value_
|
||||
: saved_value_);
|
||||
|
||||
valid_ = true;
|
||||
return s;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBIter::MergeEntity(const Slice& entity, const Slice& user_key) {
|
||||
Status s = MergeHelper::TimedFullMergeWithEntity(
|
||||
merge_operator_, user_key, entity, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, clock_,
|
||||
/* update_num_ops_stats */ true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetValueAndColumnsFromEntity(saved_value_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move backwards until the key smaller than saved_key_.
|
||||
|
||||
+3
-1
@@ -317,7 +317,9 @@ class DBIter final : public Iterator {
|
||||
wide_columns_.clear();
|
||||
}
|
||||
|
||||
Status Merge(const Slice* val, const Slice& user_key);
|
||||
// If user-defined timestamp is enabled, `user_key` includes timestamp.
|
||||
bool Merge(const Slice* val, const Slice& user_key);
|
||||
bool MergeEntity(const Slice& entity, const Slice& user_key);
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
Env* const env_;
|
||||
|
||||
@@ -392,7 +392,7 @@ struct ReferenceIterator {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
// Use an internal iterator that sometimes returns errors and sometimes
|
||||
// adds/removes entries on the fly. Do random operations on a DBIter and
|
||||
@@ -482,12 +482,11 @@ TEST_F(DBIteratorStressTest, StressTest) {
|
||||
std::cout << "entries:";
|
||||
for (size_t i = 0; i < data.entries.size(); ++i) {
|
||||
Entry& e = data.entries[i];
|
||||
std::cout
|
||||
<< "\n idx " << i << ": \"" << e.key << "\": \""
|
||||
<< e.value << "\" seq: " << e.sequence << " type: "
|
||||
<< (e.type == kTypeValue
|
||||
? "val"
|
||||
: e.type == kTypeDeletion ? "del" : "merge");
|
||||
std::cout << "\n idx " << i << ": \"" << e.key << "\": \""
|
||||
<< e.value << "\" seq: " << e.sequence << " type: "
|
||||
<< (e.type == kTypeValue ? "val"
|
||||
: e.type == kTypeDeletion ? "del"
|
||||
: "merge");
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
+12
-10
@@ -3,12 +3,13 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "db/db_iter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/options.h"
|
||||
@@ -82,8 +83,8 @@ class TestIterator : public InternalIterator {
|
||||
std::sort(data_.begin(), data_.end(),
|
||||
[this](std::pair<std::string, std::string> a,
|
||||
std::pair<std::string, std::string> b) {
|
||||
return (cmp.Compare(a.first, b.first) < 0);
|
||||
});
|
||||
return (cmp.Compare(a.first, b.first) < 0);
|
||||
});
|
||||
}
|
||||
|
||||
// Removes the key from the set of keys over which this iterator iterates.
|
||||
@@ -429,7 +430,8 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
db_iter->SeekToLast();
|
||||
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(static_cast<int>(get_perf_context()->internal_key_skipped_count), 1);
|
||||
ASSERT_EQ(static_cast<int>(get_perf_context()->internal_key_skipped_count),
|
||||
1);
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
@@ -557,7 +559,8 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
db_iter->SeekToLast();
|
||||
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(static_cast<int>(get_perf_context()->internal_delete_skipped_count), 0);
|
||||
ASSERT_EQ(
|
||||
static_cast<int>(get_perf_context()->internal_delete_skipped_count), 0);
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
@@ -3013,7 +3016,6 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace8) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBIteratorTest, SeekPrefixTombstones) {
|
||||
ReadOptions ro;
|
||||
Options options;
|
||||
|
||||
+15
-13
@@ -236,7 +236,7 @@ namespace {
|
||||
std::string MakeLongKey(size_t length, char c) {
|
||||
return std::string(length, c);
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_P(DBIteratorTest, IterLongKeys) {
|
||||
ASSERT_OK(Put(MakeLongKey(20, 0), "0"));
|
||||
@@ -1037,7 +1037,8 @@ TEST_P(DBIteratorTest, DBIteratorBoundTest) {
|
||||
iter->Next();
|
||||
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(static_cast<int>(get_perf_context()->internal_delete_skipped_count), 2);
|
||||
ASSERT_EQ(
|
||||
static_cast<int>(get_perf_context()->internal_delete_skipped_count), 2);
|
||||
|
||||
// now testing with iterate_bound
|
||||
Slice prefix("c");
|
||||
@@ -1060,7 +1061,8 @@ TEST_P(DBIteratorTest, DBIteratorBoundTest) {
|
||||
// even though the key is deleted
|
||||
// hence internal_delete_skipped_count should be 0
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_EQ(static_cast<int>(get_perf_context()->internal_delete_skipped_count), 0);
|
||||
ASSERT_EQ(
|
||||
static_cast<int>(get_perf_context()->internal_delete_skipped_count), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1536,7 +1538,7 @@ class DBIteratorTestForPinnedData : public DBIteratorTest {
|
||||
}
|
||||
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
@@ -2180,8 +2182,8 @@ TEST_P(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_PREV), (uint64_t)total_prev);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_PREV_FOUND),
|
||||
(uint64_t)total_prev_found);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ITER_BYTES_READ), (uint64_t)total_bytes);
|
||||
|
||||
ASSERT_EQ(TestGetTickerCount(options, ITER_BYTES_READ),
|
||||
(uint64_t)total_bytes);
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, ReadAhead) {
|
||||
@@ -2310,8 +2312,8 @@ TEST_P(DBIteratorTest, DBIteratorSkipRecentDuplicatesTest) {
|
||||
EXPECT_EQ(get_perf_context()->internal_merge_count, 0);
|
||||
EXPECT_GE(get_perf_context()->internal_recent_skipped_count, 2);
|
||||
EXPECT_GE(get_perf_context()->seek_on_memtable_count, 2);
|
||||
EXPECT_EQ(1, options.statistics->getTickerCount(
|
||||
NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
EXPECT_EQ(1,
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, Refresh) {
|
||||
@@ -2592,7 +2594,7 @@ TEST_P(DBIteratorTest, SkipStatistics) {
|
||||
}
|
||||
ASSERT_EQ(count, 3);
|
||||
delete iter;
|
||||
skip_count += 8; // 3 deletes + 3 original keys + 2 lower in sequence
|
||||
skip_count += 8; // 3 deletes + 3 original keys + 2 lower in sequence
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
|
||||
iter = NewIterator(ReadOptions());
|
||||
@@ -2603,7 +2605,7 @@ TEST_P(DBIteratorTest, SkipStatistics) {
|
||||
}
|
||||
ASSERT_EQ(count, 3);
|
||||
delete iter;
|
||||
skip_count += 8; // Same as above, but in reverse order
|
||||
skip_count += 8; // Same as above, but in reverse order
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
|
||||
ASSERT_OK(Put("aa", "1"));
|
||||
@@ -2621,18 +2623,18 @@ TEST_P(DBIteratorTest, SkipStatistics) {
|
||||
|
||||
iter = NewIterator(ro);
|
||||
count = 0;
|
||||
for(iter->Seek("aa"); iter->Valid(); iter->Next()) {
|
||||
for (iter->Seek("aa"); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQ(count, 1);
|
||||
delete iter;
|
||||
skip_count += 6; // 3 deletes + 3 original keys
|
||||
skip_count += 6; // 3 deletes + 3 original keys
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
|
||||
iter = NewIterator(ro);
|
||||
count = 0;
|
||||
for(iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
ASSERT_OK(iter->status());
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -55,14 +55,13 @@ SequenceNumber ReadRecords(std::unique_ptr<TransactionLogIterator>& iter,
|
||||
return res.sequence;
|
||||
}
|
||||
|
||||
void ExpectRecords(
|
||||
const int expected_no_records,
|
||||
std::unique_ptr<TransactionLogIterator>& iter) {
|
||||
void ExpectRecords(const int expected_no_records,
|
||||
std::unique_ptr<TransactionLogIterator>& iter) {
|
||||
int num_records;
|
||||
ReadRecords(iter, num_records);
|
||||
ASSERT_EQ(num_records, expected_no_records);
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBTestXactLogIterator, TransactionLogIterator) {
|
||||
do {
|
||||
@@ -95,10 +94,9 @@ TEST_F(DBTestXactLogIterator, TransactionLogIterator) {
|
||||
TEST_F(DBTestXactLogIterator, TransactionLogIteratorRace) {
|
||||
static const int LOG_ITERATOR_RACE_TEST_COUNT = 2;
|
||||
static const char* sync_points[LOG_ITERATOR_RACE_TEST_COUNT][4] = {
|
||||
{"WalManager::GetSortedWalFiles:1", "WalManager::PurgeObsoleteFiles:1",
|
||||
{"WalManager::GetSortedWalFiles:1", "WalManager::PurgeObsoleteFiles:1",
|
||||
"WalManager::PurgeObsoleteFiles:2", "WalManager::GetSortedWalFiles:2"},
|
||||
{"WalManager::GetSortedWalsOfType:1",
|
||||
"WalManager::PurgeObsoleteFiles:1",
|
||||
{"WalManager::GetSortedWalsOfType:1", "WalManager::PurgeObsoleteFiles:1",
|
||||
"WalManager::PurgeObsoleteFiles:2",
|
||||
"WalManager::GetSortedWalsOfType:2"}};
|
||||
for (int test = 0; test < LOG_ITERATOR_RACE_TEST_COUNT; ++test) {
|
||||
@@ -300,8 +298,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -224,8 +224,8 @@ TEST_F(DBLogicalBlockSizeCacheTest, CreateColumnFamilies) {
|
||||
// Now cf_path_0_ in cache_ has been properly decreased and cf_path_0_'s entry
|
||||
// is dropped from cache
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(dbname_, options,
|
||||
{{"cf1", cf_options}, {"cf2", cf_options}}));
|
||||
ASSERT_OK(
|
||||
DestroyDB(dbname_, options, {{"cf1", cf_options}, {"cf2", cf_options}}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, OpenWithColumnFamilies) {
|
||||
@@ -313,8 +313,8 @@ TEST_F(DBLogicalBlockSizeCacheTest, OpenWithColumnFamilies) {
|
||||
delete db;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
}
|
||||
ASSERT_OK(DestroyDB(dbname_, options,
|
||||
{{"cf1", cf_options}, {"cf2", cf_options}}));
|
||||
ASSERT_OK(
|
||||
DestroyDB(dbname_, options, {{"cf1", cf_options}, {"cf2", cf_options}}));
|
||||
}
|
||||
|
||||
TEST_F(DBLogicalBlockSizeCacheTest, DestroyColumnFamilyHandle) {
|
||||
|
||||
@@ -39,7 +39,7 @@ class LimitedStringAppendMergeOp : public StringAppendTESTOperator {
|
||||
private:
|
||||
size_t limit_ = 0;
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
class DBMergeOperandTest : public DBTestBase {
|
||||
public:
|
||||
|
||||
@@ -84,8 +84,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator =
|
||||
std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
// All K1 values are in memtable.
|
||||
@@ -203,7 +202,6 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnIteration) {
|
||||
VerifyDBInternal({{"k1", "v1"}, {"k2", "corrupted"}, {"k2", "v2"}});
|
||||
}
|
||||
|
||||
|
||||
class MergeOperatorPinningTest : public DBMergeOperatorTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
@@ -471,7 +469,7 @@ TEST_F(DBMergeOperatorTest, TailingIteratorMemtableUnrefedBySomeoneElse) {
|
||||
"DBIter::MergeValuesNewToOld:SteppedToNextOperand", [&](void*) {
|
||||
EXPECT_FALSE(stepped_to_next_operand);
|
||||
stepped_to_next_operand = true;
|
||||
someone_else.reset(); // Unpin SuperVersion A
|
||||
someone_else.reset(); // Unpin SuperVersion A
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
|
||||
@@ -402,7 +402,7 @@ TEST_F(DBOptionsTest, SetWalBytesPerSync) {
|
||||
// Do not flush. If we flush here, SwitchWAL will reuse old WAL file since its
|
||||
// empty and will not get the new wal_bytes_per_sync value.
|
||||
low_bytes_per_sync = counter;
|
||||
//5242880 = 1024 * 1024 * 5
|
||||
// 5242880 = 1024 * 1024 * 5
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"wal_bytes_per_sync", "5242880"}}));
|
||||
ASSERT_EQ(5242880, dbfull()->GetDBOptions().wal_bytes_per_sync);
|
||||
counter = 0;
|
||||
@@ -604,7 +604,7 @@ TEST_F(DBOptionsTest, SetOptionsMayTriggerCompaction) {
|
||||
TEST_F(DBOptionsTest, SetBackgroundCompactionThreads) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.max_background_compactions = 1; // default value
|
||||
options.max_background_compactions = 1; // default value
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
@@ -627,7 +627,6 @@ TEST_F(DBOptionsTest, SetBackgroundFlushThreads) {
|
||||
ASSERT_EQ(3, dbfull()->TEST_BGFlushesAllowed());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBOptionsTest, SetBackgroundJobs) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -691,7 +690,8 @@ TEST_F(DBOptionsTest, SetDelayedWriteRateOption) {
|
||||
options.delayed_write_rate = 2 * 1024U * 1024U;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(2 * 1024U * 1024U, dbfull()->TEST_write_controler().max_delayed_write_rate());
|
||||
ASSERT_EQ(2 * 1024U * 1024U,
|
||||
dbfull()->TEST_write_controler().max_delayed_write_rate());
|
||||
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"delayed_write_rate", "20000"}}));
|
||||
ASSERT_EQ(20000, dbfull()->TEST_write_controler().max_delayed_write_rate());
|
||||
|
||||
@@ -270,7 +270,8 @@ void GetExpectedTableProperties(
|
||||
const int kDeletionCount = kTableCount * kDeletionsPerTable;
|
||||
const int kMergeCount = kTableCount * kMergeOperandsPerTable;
|
||||
const int kRangeDeletionCount = kTableCount * kRangeDeletionsPerTable;
|
||||
const int kKeyCount = kPutCount + kDeletionCount + kMergeCount + kRangeDeletionCount;
|
||||
const int kKeyCount =
|
||||
kPutCount + kDeletionCount + kMergeCount + kRangeDeletionCount;
|
||||
const int kAvgSuccessorSize = kKeySize / 5;
|
||||
const int kEncodingSavePerKey = kKeySize / 4;
|
||||
expected_tp->raw_key_size = kKeyCount * (kKeySize + 8);
|
||||
@@ -281,7 +282,8 @@ void GetExpectedTableProperties(
|
||||
expected_tp->num_merge_operands = kMergeCount;
|
||||
expected_tp->num_range_deletions = kRangeDeletionCount;
|
||||
expected_tp->num_data_blocks =
|
||||
kTableCount * (kKeysPerTable * (kKeySize - kEncodingSavePerKey + kValueSize)) /
|
||||
kTableCount *
|
||||
(kKeysPerTable * (kKeySize - kEncodingSavePerKey + kValueSize)) /
|
||||
kBlockSize;
|
||||
expected_tp->data_size =
|
||||
kTableCount * (kKeysPerTable * (kKeySize + 8 + kValueSize));
|
||||
@@ -1120,7 +1122,8 @@ class CountingUserTblPropCollector : public TablePropertiesCollector {
|
||||
std::string encoded;
|
||||
PutVarint32(&encoded, count_);
|
||||
*properties = UserCollectedProperties{
|
||||
{"CountingUserTblPropCollector", message_}, {"Count", encoded},
|
||||
{"CountingUserTblPropCollector", message_},
|
||||
{"Count", encoded},
|
||||
};
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -2122,7 +2125,7 @@ std::string PopMetaIndexKey(InternalIterator* meta_iter) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBPropertiesTest, TableMetaIndexKeys) {
|
||||
// This is to detect unexpected churn in metaindex block keys. This is more
|
||||
|
||||
@@ -238,7 +238,8 @@ TEST_F(DBRangeDelTest, SentinelsOmittedFromOutputFile) {
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// gaps between ranges creates sentinels in our internal representation
|
||||
std::vector<std::pair<std::string, std::string>> range_dels = {{"a", "b"}, {"c", "d"}, {"e", "f"}};
|
||||
std::vector<std::pair<std::string, std::string>> range_dels = {
|
||||
{"a", "b"}, {"c", "d"}, {"e", "f"}};
|
||||
for (const auto& range_del : range_dels) {
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
range_del.first, range_del.second));
|
||||
@@ -567,8 +568,8 @@ TEST_F(DBRangeDelTest, PutDeleteRangeMergeFlush) {
|
||||
std::string val;
|
||||
PutFixed64(&val, 1);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key", val));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
"key", "key_"));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "key",
|
||||
"key_"));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "key", val));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
@@ -1332,7 +1333,7 @@ TEST_F(DBRangeDelTest, UntruncatedTombstoneDoesNotDeleteNewerKey) {
|
||||
const int kFileBytes = 1 << 20;
|
||||
const int kValueBytes = 1 << 10;
|
||||
const int kNumFiles = 4;
|
||||
const int kMaxKey = kNumFiles* kFileBytes / kValueBytes;
|
||||
const int kMaxKey = kNumFiles * kFileBytes / kValueBytes;
|
||||
const int kKeysOverwritten = 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -1649,7 +1650,8 @@ TEST_F(DBRangeDelTest, RangeTombstoneWrittenToMinimalSsts) {
|
||||
const auto& table_props = name_and_table_props.second;
|
||||
// The range tombstone should only be output to the second L1 SST.
|
||||
if (name.size() >= l1_metadata[1].name.size() &&
|
||||
name.substr(name.size() - l1_metadata[1].name.size()).compare(l1_metadata[1].name) == 0) {
|
||||
name.substr(name.size() - l1_metadata[1].name.size())
|
||||
.compare(l1_metadata[1].name) == 0) {
|
||||
ASSERT_EQ(1, table_props->num_range_deletions);
|
||||
++num_range_deletions;
|
||||
} else {
|
||||
|
||||
@@ -499,7 +499,7 @@ class TraceFileEnv : public EnvWrapper {
|
||||
private:
|
||||
std::atomic<int> files_closed_{0};
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBSecondaryTest, SecondaryCloseFiles) {
|
||||
Options options;
|
||||
|
||||
@@ -70,9 +70,9 @@ TEST_F(DBStatisticsTest, CompressionStatsTest) {
|
||||
options.compression = kNoCompression;
|
||||
DestroyAndReopen(options);
|
||||
uint64_t currentCompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
uint64_t currentDecompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED);
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED);
|
||||
|
||||
// Check that compressions do not occur when turned off
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
@@ -80,14 +80,16 @@ TEST_F(DBStatisticsTest, CompressionStatsTest) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED)
|
||||
- currentCompressions, 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED) -
|
||||
currentCompressions,
|
||||
0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED)
|
||||
- currentDecompressions, 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED) -
|
||||
currentDecompressions,
|
||||
0);
|
||||
}
|
||||
|
||||
TEST_F(DBStatisticsTest, MutexWaitStatsDisabledByDefault) {
|
||||
|
||||
@@ -52,7 +52,7 @@ void VerifyTableProperties(DB* db, uint64_t expected_entries_size) {
|
||||
|
||||
VerifySstUniqueIds(props);
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
class DBTablePropertiesTest : public DBTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
@@ -240,7 +240,6 @@ TablePropertiesCollection
|
||||
DBTablePropertiesTest::TestGetPropertiesOfTablesInRange(
|
||||
std::vector<Range> ranges, std::size_t* num_properties,
|
||||
std::size_t* num_files) {
|
||||
|
||||
// Since we deref zero element in the vector it can not be empty
|
||||
// otherwise we pass an address to some random memory
|
||||
EXPECT_GT(ranges.size(), 0U);
|
||||
@@ -469,12 +468,12 @@ INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
class DeletionTriggeredCompactionTestListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionBegin(DB* , const CompactionJobInfo& ci) override {
|
||||
void OnCompactionBegin(DB*, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.compaction_reason,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
|
||||
void OnCompactionCompleted(DB* , const CompactionJobInfo& ci) override {
|
||||
void OnCompactionCompleted(DB*, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.compaction_reason,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
@@ -485,13 +484,13 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
int kWindowSize = 100;
|
||||
int kNumDelsTrigger = 90;
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> compact_on_del =
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger);
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger);
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
opts.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
|
||||
if(GetParam() == "kCompactionStyleUniversal") {
|
||||
if (GetParam() == "kCompactionStyleUniversal") {
|
||||
opts.compaction_style = kCompactionStyleUniversal;
|
||||
}
|
||||
Reopen(opts);
|
||||
@@ -502,8 +501,8 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
DeletionTriggeredCompactionTestListener *listener =
|
||||
new DeletionTriggeredCompactionTestListener();
|
||||
DeletionTriggeredCompactionTestListener* listener =
|
||||
new DeletionTriggeredCompactionTestListener();
|
||||
opts.listeners.emplace_back(listener);
|
||||
Reopen(opts);
|
||||
|
||||
@@ -524,10 +523,10 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
// effect
|
||||
kWindowSize = 50;
|
||||
kNumDelsTrigger = 40;
|
||||
static_cast<CompactOnDeletionCollectorFactory*>
|
||||
(compact_on_del.get())->SetWindowSize(kWindowSize);
|
||||
static_cast<CompactOnDeletionCollectorFactory*>
|
||||
(compact_on_del.get())->SetDeletionTrigger(kNumDelsTrigger);
|
||||
static_cast<CompactOnDeletionCollectorFactory*>(compact_on_del.get())
|
||||
->SetWindowSize(kWindowSize);
|
||||
static_cast<CompactOnDeletionCollectorFactory*>(compact_on_del.get())
|
||||
->SetDeletionTrigger(kNumDelsTrigger);
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
if (i >= kNumKeys - kWindowSize &&
|
||||
i < kNumKeys - kWindowSize + kNumDelsTrigger) {
|
||||
@@ -543,10 +542,10 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
// Change the window size to disable delete triggered compaction
|
||||
kWindowSize = 0;
|
||||
static_cast<CompactOnDeletionCollectorFactory*>
|
||||
(compact_on_del.get())->SetWindowSize(kWindowSize);
|
||||
static_cast<CompactOnDeletionCollectorFactory*>
|
||||
(compact_on_del.get())->SetDeletionTrigger(kNumDelsTrigger);
|
||||
static_cast<CompactOnDeletionCollectorFactory*>(compact_on_del.get())
|
||||
->SetWindowSize(kWindowSize);
|
||||
static_cast<CompactOnDeletionCollectorFactory*>(compact_on_del.get())
|
||||
->SetDeletionTrigger(kNumDelsTrigger);
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
if (i >= kNumKeys - kWindowSize &&
|
||||
i < kNumKeys - kWindowSize + kNumDelsTrigger) {
|
||||
@@ -611,13 +610,9 @@ TEST_P(DBTablePropertiesTest, RatioBasedDeletionTriggeredCompactionMarking) {
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTablePropertiesTest,
|
||||
DBTablePropertiesTest,
|
||||
::testing::Values(
|
||||
"kCompactionStyleLevel",
|
||||
"kCompactionStyleUniversal"
|
||||
));
|
||||
INSTANTIATE_TEST_CASE_P(DBTablePropertiesTest, DBTablePropertiesTest,
|
||||
::testing::Values("kCompactionStyleLevel",
|
||||
"kCompactionStyleUniversal"));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -399,7 +399,7 @@ TEST_P(DBTestTailingIterator, TailingIteratorSeekToSame) {
|
||||
// Write rows with keys 00000, 00002, 00004 etc.
|
||||
for (int i = 0; i < NROWS; ++i) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "%05d", 2*i);
|
||||
snprintf(buf, sizeof(buf), "%05d", 2 * i);
|
||||
std::string key(buf);
|
||||
std::string value("value");
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
@@ -539,7 +539,6 @@ TEST_P(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
||||
const Slice upper_bound("cc", 3);
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
|
||||
|
||||
// 1st L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -565,7 +564,6 @@ TEST_P(DBTestTailingIterator, SeekToFirstWithUpperBoundBug) {
|
||||
const Slice upper_bound("cc", 3);
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
|
||||
|
||||
// 1st L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -599,8 +597,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
+10
-5
@@ -1135,7 +1135,7 @@ class DelayFilterFactory : public CompactionFilterFactory {
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
@@ -1490,7 +1490,7 @@ bool MinLevelToCompress(CompressionType& type, Options& options, int wbits,
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBTest, MinLevelToCompress1) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -2843,7 +2843,7 @@ static void MTThreadBody(void* arg) {
|
||||
fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
class MultiThreadedDBTest
|
||||
: public DBTest,
|
||||
@@ -2929,7 +2929,7 @@ static void GCThreadBody(void* arg) {
|
||||
t->done = true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBTest, GroupCommitTest) {
|
||||
do {
|
||||
@@ -3075,6 +3075,11 @@ class ModelDB : public DB {
|
||||
}
|
||||
return Write(o, &batch);
|
||||
}
|
||||
Status Merge(const WriteOptions& /*o*/, ColumnFamilyHandle* /*cf*/,
|
||||
const Slice& /*k*/, const Slice& /*ts*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& /*options*/, ColumnFamilyHandle* /*cf*/,
|
||||
const Slice& key, PinnableSlice* /*value*/) override {
|
||||
@@ -4640,7 +4645,7 @@ void VerifyOperationCount(Env* env, ThreadStatus::OperationType op_type,
|
||||
}
|
||||
ASSERT_EQ(op_count, expected_count);
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBTest, GetThreadStatus) {
|
||||
Options options;
|
||||
|
||||
+174
-171
@@ -669,33 +669,33 @@ TEST_F(DBTest2, TestWriteBufferNoLimitWithCache) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
void ValidateKeyExistence(DB* db, const std::vector<Slice>& keys_must_exist,
|
||||
const std::vector<Slice>& keys_must_not_exist) {
|
||||
// Ensure that expected keys exist
|
||||
std::vector<std::string> values;
|
||||
if (keys_must_exist.size() > 0) {
|
||||
std::vector<Status> status_list =
|
||||
void ValidateKeyExistence(DB* db, const std::vector<Slice>& keys_must_exist,
|
||||
const std::vector<Slice>& keys_must_not_exist) {
|
||||
// Ensure that expected keys exist
|
||||
std::vector<std::string> values;
|
||||
if (keys_must_exist.size() > 0) {
|
||||
std::vector<Status> status_list =
|
||||
db->MultiGet(ReadOptions(), keys_must_exist, &values);
|
||||
for (size_t i = 0; i < keys_must_exist.size(); i++) {
|
||||
ASSERT_OK(status_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that given keys don't exist
|
||||
if (keys_must_not_exist.size() > 0) {
|
||||
std::vector<Status> status_list =
|
||||
db->MultiGet(ReadOptions(), keys_must_not_exist, &values);
|
||||
for (size_t i = 0; i < keys_must_not_exist.size(); i++) {
|
||||
ASSERT_TRUE(status_list[i].IsNotFound());
|
||||
}
|
||||
for (size_t i = 0; i < keys_must_exist.size(); i++) {
|
||||
ASSERT_OK(status_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
// Ensure that given keys don't exist
|
||||
if (keys_must_not_exist.size() > 0) {
|
||||
std::vector<Status> status_list =
|
||||
db->MultiGet(ReadOptions(), keys_must_not_exist, &values);
|
||||
for (size_t i = 0; i < keys_must_not_exist.size(); i++) {
|
||||
ASSERT_TRUE(status_list[i].IsNotFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBTest2, WalFilterTest) {
|
||||
class TestWalFilter : public WalFilter {
|
||||
private:
|
||||
private:
|
||||
// Processing option that is requested to be applied at the given index
|
||||
WalFilter::WalProcessingOption wal_processing_option_;
|
||||
// Index at which to apply wal_processing_option_
|
||||
@@ -705,12 +705,12 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
// Current record index, incremented with each record encountered.
|
||||
size_t current_record_index_;
|
||||
|
||||
public:
|
||||
public:
|
||||
TestWalFilter(WalFilter::WalProcessingOption wal_processing_option,
|
||||
size_t apply_option_for_record_index)
|
||||
: wal_processing_option_(wal_processing_option),
|
||||
apply_option_at_record_index_(apply_option_for_record_index),
|
||||
current_record_index_(0) {}
|
||||
size_t apply_option_for_record_index)
|
||||
: wal_processing_option_(wal_processing_option),
|
||||
apply_option_at_record_index_(apply_option_for_record_index),
|
||||
current_record_index_(0) {}
|
||||
|
||||
WalProcessingOption LogRecord(const WriteBatch& /*batch*/,
|
||||
WriteBatch* /*new_batch*/,
|
||||
@@ -719,8 +719,7 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
|
||||
if (current_record_index_ == apply_option_at_record_index_) {
|
||||
option_to_return = wal_processing_option_;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
option_to_return = WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
|
||||
@@ -747,12 +746,12 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
|
||||
// Test with all WAL processing options
|
||||
for (int option = 0;
|
||||
option < static_cast<int>(
|
||||
WalFilter::WalProcessingOption::kWalProcessingOptionMax);
|
||||
option++) {
|
||||
option < static_cast<int>(
|
||||
WalFilter::WalProcessingOption::kWalProcessingOptionMax);
|
||||
option++) {
|
||||
Options options = OptionsForLogIterTest();
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({ "pikachu" }, options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Write given keys in given batches
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
@@ -764,28 +763,27 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
}
|
||||
|
||||
WalFilter::WalProcessingOption wal_processing_option =
|
||||
static_cast<WalFilter::WalProcessingOption>(option);
|
||||
static_cast<WalFilter::WalProcessingOption>(option);
|
||||
|
||||
// Create a test filter that would apply wal_processing_option at the first
|
||||
// record
|
||||
size_t apply_option_for_record_index = 1;
|
||||
TestWalFilter test_wal_filter(wal_processing_option,
|
||||
apply_option_for_record_index);
|
||||
apply_option_for_record_index);
|
||||
|
||||
// Reopen database with option to use WAL filter
|
||||
options = OptionsForLogIterTest();
|
||||
options.wal_filter = &test_wal_filter;
|
||||
Status status =
|
||||
TryReopenWithColumnFamilies({ "default", "pikachu" }, options);
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
if (wal_processing_option ==
|
||||
WalFilter::WalProcessingOption::kCorruptedRecord) {
|
||||
WalFilter::WalProcessingOption::kCorruptedRecord) {
|
||||
ASSERT_NOK(status);
|
||||
// In case of corruption we can turn off paranoid_checks to reopen
|
||||
// databse
|
||||
options.paranoid_checks = false;
|
||||
ReopenWithColumnFamilies({ "default", "pikachu" }, options);
|
||||
}
|
||||
else {
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
} else {
|
||||
ASSERT_OK(status);
|
||||
}
|
||||
|
||||
@@ -794,56 +792,54 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
std::vector<Slice> keys_must_exist;
|
||||
std::vector<Slice> keys_must_not_exist;
|
||||
switch (wal_processing_option) {
|
||||
case WalFilter::WalProcessingOption::kCorruptedRecord:
|
||||
case WalFilter::WalProcessingOption::kContinueProcessing: {
|
||||
fprintf(stderr, "Testing with complete WAL processing\n");
|
||||
// we expect all records to be processed
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
for (size_t j = 0; j < batch_keys[i].size(); j++) {
|
||||
keys_must_exist.push_back(Slice(batch_keys[i][j]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WalFilter::WalProcessingOption::kIgnoreCurrentRecord: {
|
||||
fprintf(stderr,
|
||||
"Testing with ignoring record %" ROCKSDB_PRIszt " only\n",
|
||||
apply_option_for_record_index);
|
||||
// We expect the record with apply_option_for_record_index to be not
|
||||
// found.
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
for (size_t j = 0; j < batch_keys[i].size(); j++) {
|
||||
if (i == apply_option_for_record_index) {
|
||||
keys_must_not_exist.push_back(Slice(batch_keys[i][j]));
|
||||
}
|
||||
else {
|
||||
case WalFilter::WalProcessingOption::kCorruptedRecord:
|
||||
case WalFilter::WalProcessingOption::kContinueProcessing: {
|
||||
fprintf(stderr, "Testing with complete WAL processing\n");
|
||||
// we expect all records to be processed
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
for (size_t j = 0; j < batch_keys[i].size(); j++) {
|
||||
keys_must_exist.push_back(Slice(batch_keys[i][j]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WalFilter::WalProcessingOption::kStopReplay: {
|
||||
fprintf(stderr,
|
||||
"Testing with stopping replay from record %" ROCKSDB_PRIszt
|
||||
"\n",
|
||||
apply_option_for_record_index);
|
||||
// We expect records beyond apply_option_for_record_index to be not
|
||||
// found.
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
for (size_t j = 0; j < batch_keys[i].size(); j++) {
|
||||
if (i >= apply_option_for_record_index) {
|
||||
keys_must_not_exist.push_back(Slice(batch_keys[i][j]));
|
||||
}
|
||||
else {
|
||||
keys_must_exist.push_back(Slice(batch_keys[i][j]));
|
||||
case WalFilter::WalProcessingOption::kIgnoreCurrentRecord: {
|
||||
fprintf(stderr,
|
||||
"Testing with ignoring record %" ROCKSDB_PRIszt " only\n",
|
||||
apply_option_for_record_index);
|
||||
// We expect the record with apply_option_for_record_index to be not
|
||||
// found.
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
for (size_t j = 0; j < batch_keys[i].size(); j++) {
|
||||
if (i == apply_option_for_record_index) {
|
||||
keys_must_not_exist.push_back(Slice(batch_keys[i][j]));
|
||||
} else {
|
||||
keys_must_exist.push_back(Slice(batch_keys[i][j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
FAIL(); // unhandled case
|
||||
case WalFilter::WalProcessingOption::kStopReplay: {
|
||||
fprintf(stderr,
|
||||
"Testing with stopping replay from record %" ROCKSDB_PRIszt
|
||||
"\n",
|
||||
apply_option_for_record_index);
|
||||
// We expect records beyond apply_option_for_record_index to be not
|
||||
// found.
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
for (size_t j = 0; j < batch_keys[i].size(); j++) {
|
||||
if (i >= apply_option_for_record_index) {
|
||||
keys_must_not_exist.push_back(Slice(batch_keys[i][j]));
|
||||
} else {
|
||||
keys_must_exist.push_back(Slice(batch_keys[i][j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
FAIL(); // unhandled case
|
||||
}
|
||||
|
||||
bool checked_after_reopen = false;
|
||||
@@ -861,7 +857,7 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
//(even if they were skipped)
|
||||
// reopn database with option to use WAL filter
|
||||
options = OptionsForLogIterTest();
|
||||
ReopenWithColumnFamilies({ "default", "pikachu" }, options);
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
checked_after_reopen = true;
|
||||
}
|
||||
@@ -870,7 +866,7 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
|
||||
TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
class ChangeBatchHandler : public WriteBatch::Handler {
|
||||
private:
|
||||
private:
|
||||
// Batch to insert keys in
|
||||
WriteBatch* new_write_batch_;
|
||||
// Number of keys to add in the new batch
|
||||
@@ -878,12 +874,12 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
// Number of keys added to new batch
|
||||
size_t num_keys_added_;
|
||||
|
||||
public:
|
||||
public:
|
||||
ChangeBatchHandler(WriteBatch* new_write_batch,
|
||||
size_t num_keys_to_add_in_new_batch)
|
||||
: new_write_batch_(new_write_batch),
|
||||
num_keys_to_add_in_new_batch_(num_keys_to_add_in_new_batch),
|
||||
num_keys_added_(0) {}
|
||||
size_t num_keys_to_add_in_new_batch)
|
||||
: new_write_batch_(new_write_batch),
|
||||
num_keys_to_add_in_new_batch_(num_keys_to_add_in_new_batch),
|
||||
num_keys_added_(0) {}
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
if (num_keys_added_ < num_keys_to_add_in_new_batch_) {
|
||||
ASSERT_OK(new_write_batch_->Put(key, value));
|
||||
@@ -893,7 +889,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
};
|
||||
|
||||
class TestWalFilterWithChangeBatch : public WalFilter {
|
||||
private:
|
||||
private:
|
||||
// Index at which to start changing records
|
||||
size_t change_records_from_index_;
|
||||
// Number of keys to add in the new batch
|
||||
@@ -901,12 +897,12 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
// Current record index, incremented with each record encountered.
|
||||
size_t current_record_index_;
|
||||
|
||||
public:
|
||||
public:
|
||||
TestWalFilterWithChangeBatch(size_t change_records_from_index,
|
||||
size_t num_keys_to_add_in_new_batch)
|
||||
: change_records_from_index_(change_records_from_index),
|
||||
num_keys_to_add_in_new_batch_(num_keys_to_add_in_new_batch),
|
||||
current_record_index_(0) {}
|
||||
size_t num_keys_to_add_in_new_batch)
|
||||
: change_records_from_index_(change_records_from_index),
|
||||
num_keys_to_add_in_new_batch_(num_keys_to_add_in_new_batch),
|
||||
current_record_index_(0) {}
|
||||
|
||||
WalProcessingOption LogRecord(const WriteBatch& batch,
|
||||
WriteBatch* new_batch,
|
||||
@@ -925,7 +921,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
// object, however we modify it for our own purpose here and hence
|
||||
// cast the constness away.
|
||||
(const_cast<TestWalFilterWithChangeBatch*>(this)
|
||||
->current_record_index_)++;
|
||||
->current_record_index_)++;
|
||||
|
||||
return WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
@@ -944,7 +940,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
|
||||
Options options = OptionsForLogIterTest();
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({ "pikachu" }, options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Write given keys in given batches
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
@@ -960,12 +956,12 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
size_t change_records_from_index = 1;
|
||||
size_t num_keys_to_add_in_new_batch = 1;
|
||||
TestWalFilterWithChangeBatch test_wal_filter_with_change_batch(
|
||||
change_records_from_index, num_keys_to_add_in_new_batch);
|
||||
change_records_from_index, num_keys_to_add_in_new_batch);
|
||||
|
||||
// Reopen database with option to use WAL filter
|
||||
options = OptionsForLogIterTest();
|
||||
options.wal_filter = &test_wal_filter_with_change_batch;
|
||||
ReopenWithColumnFamilies({ "default", "pikachu" }, options);
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
// Ensure that all keys exist before change_records_from_index_
|
||||
// And after that index only single key exists
|
||||
@@ -977,8 +973,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
for (size_t j = 0; j < batch_keys[i].size(); j++) {
|
||||
if (i >= change_records_from_index && j >= num_keys_to_add_in_new_batch) {
|
||||
keys_must_not_exist.push_back(Slice(batch_keys[i][j]));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
keys_must_exist.push_back(Slice(batch_keys[i][j]));
|
||||
}
|
||||
}
|
||||
@@ -999,7 +994,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
//(even if they were skipped)
|
||||
// reopn database with option to use WAL filter
|
||||
options = OptionsForLogIterTest();
|
||||
ReopenWithColumnFamilies({ "default", "pikachu" }, options);
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
checked_after_reopen = true;
|
||||
}
|
||||
@@ -1007,22 +1002,23 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatch) {
|
||||
|
||||
TEST_F(DBTest2, WalFilterTestWithChangeBatchExtraKeys) {
|
||||
class TestWalFilterWithChangeBatchAddExtraKeys : public WalFilter {
|
||||
public:
|
||||
WalProcessingOption LogRecord(const WriteBatch& batch, WriteBatch* new_batch,
|
||||
bool* batch_changed) const override {
|
||||
*new_batch = batch;
|
||||
Status s = new_batch->Put("key_extra", "value_extra");
|
||||
if (s.ok()) {
|
||||
*batch_changed = true;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
return WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
public:
|
||||
WalProcessingOption LogRecord(const WriteBatch& batch,
|
||||
WriteBatch* new_batch,
|
||||
bool* batch_changed) const override {
|
||||
*new_batch = batch;
|
||||
Status s = new_batch->Put("key_extra", "value_extra");
|
||||
if (s.ok()) {
|
||||
*batch_changed = true;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
return WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "WalFilterTestWithChangeBatchExtraKeys";
|
||||
}
|
||||
const char* Name() const override {
|
||||
return "WalFilterTestWithChangeBatchExtraKeys";
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::vector<std::string>> batch_keys(3);
|
||||
@@ -1036,7 +1032,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatchExtraKeys) {
|
||||
|
||||
Options options = OptionsForLogIterTest();
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({ "pikachu" }, options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Write given keys in given batches
|
||||
for (size_t i = 0; i < batch_keys.size(); i++) {
|
||||
@@ -1059,7 +1055,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatchExtraKeys) {
|
||||
// Reopen without filter, now reopen should succeed - previous
|
||||
// attempt to open must not have altered the db.
|
||||
options = OptionsForLogIterTest();
|
||||
ReopenWithColumnFamilies({ "default", "pikachu" }, options);
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
std::vector<Slice> keys_must_exist;
|
||||
std::vector<Slice> keys_must_not_exist; // empty vector
|
||||
@@ -1075,7 +1071,7 @@ TEST_F(DBTest2, WalFilterTestWithChangeBatchExtraKeys) {
|
||||
|
||||
TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
class TestWalFilterWithColumnFamilies : public WalFilter {
|
||||
private:
|
||||
private:
|
||||
// column_family_id -> log_number map (provided to WALFilter)
|
||||
std::map<uint32_t, uint64_t> cf_log_number_map_;
|
||||
// column_family_name -> column_family_id map (provided to WALFilter)
|
||||
@@ -1085,31 +1081,34 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
// during recovery (i.e. aren't already flushed to SST file(s))
|
||||
// for verification against the keys we expect.
|
||||
std::map<uint32_t, std::vector<std::string>> cf_wal_keys_;
|
||||
public:
|
||||
void ColumnFamilyLogNumberMap(
|
||||
const std::map<uint32_t, uint64_t>& cf_lognumber_map,
|
||||
const std::map<std::string, uint32_t>& cf_name_id_map) override {
|
||||
cf_log_number_map_ = cf_lognumber_map;
|
||||
cf_name_id_map_ = cf_name_id_map;
|
||||
}
|
||||
|
||||
WalProcessingOption LogRecordFound(unsigned long long log_number,
|
||||
const std::string& /*log_file_name*/,
|
||||
const WriteBatch& batch,
|
||||
WriteBatch* /*new_batch*/,
|
||||
bool* /*batch_changed*/) override {
|
||||
class LogRecordBatchHandler : public WriteBatch::Handler {
|
||||
private:
|
||||
const std::map<uint32_t, uint64_t> & cf_log_number_map_;
|
||||
std::map<uint32_t, std::vector<std::string>> & cf_wal_keys_;
|
||||
public:
|
||||
void ColumnFamilyLogNumberMap(
|
||||
const std::map<uint32_t, uint64_t>& cf_lognumber_map,
|
||||
const std::map<std::string, uint32_t>& cf_name_id_map) override {
|
||||
cf_log_number_map_ = cf_lognumber_map;
|
||||
cf_name_id_map_ = cf_name_id_map;
|
||||
}
|
||||
|
||||
WalProcessingOption LogRecordFound(unsigned long long log_number,
|
||||
const std::string& /*log_file_name*/,
|
||||
const WriteBatch& batch,
|
||||
WriteBatch* /*new_batch*/,
|
||||
bool* /*batch_changed*/) override {
|
||||
class LogRecordBatchHandler : public WriteBatch::Handler {
|
||||
private:
|
||||
const std::map<uint32_t, uint64_t>& cf_log_number_map_;
|
||||
std::map<uint32_t, std::vector<std::string>>& cf_wal_keys_;
|
||||
unsigned long long log_number_;
|
||||
public:
|
||||
LogRecordBatchHandler(unsigned long long current_log_number,
|
||||
const std::map<uint32_t, uint64_t> & cf_log_number_map,
|
||||
std::map<uint32_t, std::vector<std::string>> & cf_wal_keys) :
|
||||
cf_log_number_map_(cf_log_number_map),
|
||||
cf_wal_keys_(cf_wal_keys),
|
||||
log_number_(current_log_number){}
|
||||
|
||||
public:
|
||||
LogRecordBatchHandler(
|
||||
unsigned long long current_log_number,
|
||||
const std::map<uint32_t, uint64_t>& cf_log_number_map,
|
||||
std::map<uint32_t, std::vector<std::string>>& cf_wal_keys)
|
||||
: cf_log_number_map_(cf_log_number_map),
|
||||
cf_wal_keys_(cf_wal_keys),
|
||||
log_number_(current_log_number) {}
|
||||
|
||||
Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& /*value*/) override {
|
||||
@@ -1120,8 +1119,8 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
// (i.e. isn't flushed to SST file(s) for column_family_id)
|
||||
// add it to the cf_wal_keys_ map for verification.
|
||||
if (log_number_ >= log_number_for_cf) {
|
||||
cf_wal_keys_[column_family_id].push_back(std::string(key.data(),
|
||||
key.size()));
|
||||
cf_wal_keys_[column_family_id].push_back(
|
||||
std::string(key.data(), key.size()));
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -1134,17 +1133,17 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
}
|
||||
|
||||
return WalProcessingOption::kContinueProcessing;
|
||||
}
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "WalFilterTestWithColumnFamilies";
|
||||
}
|
||||
const char* Name() const override {
|
||||
return "WalFilterTestWithColumnFamilies";
|
||||
}
|
||||
|
||||
const std::map<uint32_t, std::vector<std::string>>& GetColumnFamilyKeys() {
|
||||
return cf_wal_keys_;
|
||||
}
|
||||
|
||||
const std::map<std::string, uint32_t> & GetColumnFamilyNameIdMap() {
|
||||
const std::map<std::string, uint32_t>& GetColumnFamilyNameIdMap() {
|
||||
return cf_name_id_map_;
|
||||
}
|
||||
};
|
||||
@@ -1160,7 +1159,7 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
|
||||
Options options = OptionsForLogIterTest();
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({ "pikachu" }, options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Write given keys in given batches
|
||||
for (size_t i = 0; i < batch_keys_pre_flush.size(); i++) {
|
||||
@@ -1174,7 +1173,7 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
ASSERT_OK(dbfull()->Write(WriteOptions(), &batch));
|
||||
}
|
||||
|
||||
//Flush default column-family
|
||||
// Flush default column-family
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), handles_[0]));
|
||||
|
||||
// Do some more writes
|
||||
@@ -1208,8 +1207,7 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
// Reopen database with option to use WAL filter
|
||||
options = OptionsForLogIterTest();
|
||||
options.wal_filter = &test_wal_filter_column_families;
|
||||
Status status =
|
||||
TryReopenWithColumnFamilies({ "default", "pikachu" }, options);
|
||||
Status status = TryReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
// verify that handles_[0] only has post_flush keys
|
||||
@@ -1218,7 +1216,7 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
auto name_id_map = test_wal_filter_column_families.GetColumnFamilyNameIdMap();
|
||||
size_t index = 0;
|
||||
auto keys_cf = cf_wal_keys[name_id_map[kDefaultColumnFamilyName]];
|
||||
//default column-family, only post_flush keys are expected
|
||||
// default column-family, only post_flush keys are expected
|
||||
for (size_t i = 0; i < batch_keys_post_flush.size(); i++) {
|
||||
for (size_t j = 0; j < batch_keys_post_flush[i].size(); j++) {
|
||||
Slice key_from_the_log(keys_cf[index++]);
|
||||
@@ -1230,7 +1228,7 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
|
||||
index = 0;
|
||||
keys_cf = cf_wal_keys[name_id_map["pikachu"]];
|
||||
//pikachu column-family, all keys are expected
|
||||
// pikachu column-family, all keys are expected
|
||||
for (size_t i = 0; i < batch_keys_pre_flush.size(); i++) {
|
||||
for (size_t j = 0; j < batch_keys_pre_flush[i].size(); j++) {
|
||||
Slice key_from_the_log(keys_cf[index++]);
|
||||
@@ -1280,7 +1278,7 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
#if LZ4_VERSION_NUMBER >= 10400 // r124+
|
||||
compression_types.push_back(kLZ4Compression);
|
||||
compression_types.push_back(kLZ4HCCompression);
|
||||
#endif // LZ4_VERSION_NUMBER >= 10400
|
||||
#endif // LZ4_VERSION_NUMBER >= 10400
|
||||
if (ZSTD_Supported()) {
|
||||
compression_types.push_back(kZSTD);
|
||||
}
|
||||
@@ -1960,7 +1958,8 @@ TEST_F(DBTest2, CompressionOptions) {
|
||||
|
||||
class CompactionStallTestListener : public EventListener {
|
||||
public:
|
||||
CompactionStallTestListener() : compacting_files_cnt_(0), compacted_files_cnt_(0) {}
|
||||
CompactionStallTestListener()
|
||||
: compacting_files_cnt_(0), compacted_files_cnt_(0) {}
|
||||
|
||||
void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.cf_name, "default");
|
||||
@@ -2039,7 +2038,8 @@ TEST_F(DBTest2, CompactionStall) {
|
||||
options.level0_file_num_compaction_trigger);
|
||||
ASSERT_GT(listener->compacted_files_cnt_.load(),
|
||||
10 - options.level0_file_num_compaction_trigger);
|
||||
ASSERT_EQ(listener->compacting_files_cnt_.load(), listener->compacted_files_cnt_.load());
|
||||
ASSERT_EQ(listener->compacting_files_cnt_.load(),
|
||||
listener->compacted_files_cnt_.load());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
@@ -2664,7 +2664,7 @@ namespace {
|
||||
void CountSyncPoint() {
|
||||
TEST_SYNC_POINT_CALLBACK("DBTest2::MarkedPoint", nullptr /* arg */);
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBTest2, SyncPointMarker) {
|
||||
std::atomic<int> sync_point_called(0);
|
||||
@@ -2797,7 +2797,7 @@ TEST_F(DBTest2, ReadAmpBitmap) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef OS_SOLARIS // GetUniqueIdFromFile is not implemented
|
||||
#ifndef OS_SOLARIS // GetUniqueIdFromFile is not implemented
|
||||
TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
{
|
||||
const int kIdBufLen = 100;
|
||||
@@ -2899,7 +2899,6 @@ TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
size_t total_loaded_bytes_iter2 =
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
|
||||
// Read amp is on average 100% since we read all what we loaded in memory
|
||||
if (k == 0) {
|
||||
ASSERT_EQ(total_useful_bytes_iter1 + total_useful_bytes_iter2,
|
||||
@@ -2911,7 +2910,7 @@ TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !OS_SOLARIS
|
||||
#endif // !OS_SOLARIS
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, AutomaticCompactionOverlapManualCompaction) {
|
||||
@@ -5192,7 +5191,7 @@ TEST_F(DBTest2, TraceWithFilter) {
|
||||
ColumnFamilyDescriptor("pikachu", ColumnFamilyOptions()));
|
||||
handles.clear();
|
||||
|
||||
DB* db3 = nullptr;
|
||||
DB* db3 = nullptr;
|
||||
ASSERT_OK(DB::Open(db_opts, dbname3, column_families, &handles, &db3));
|
||||
|
||||
env_->SleepForMicroseconds(100);
|
||||
@@ -5200,12 +5199,12 @@ TEST_F(DBTest2, TraceWithFilter) {
|
||||
ASSERT_TRUE(db3->Get(ro, handles[0], "a", &value).IsNotFound());
|
||||
ASSERT_TRUE(db3->Get(ro, handles[0], "g", &value).IsNotFound());
|
||||
|
||||
//The tracer will not record the READ ops.
|
||||
// The tracer will not record the READ ops.
|
||||
trace_opts.filter = TraceFilterType::kTraceFilterGet;
|
||||
std::string trace_filename3 = dbname_ + "/rocksdb.trace_3";
|
||||
std::unique_ptr<TraceWriter> trace_writer3;
|
||||
ASSERT_OK(
|
||||
NewFileTraceWriter(env_, env_opts, trace_filename3, &trace_writer3));
|
||||
NewFileTraceWriter(env_, env_opts, trace_filename3, &trace_writer3));
|
||||
ASSERT_OK(db3->StartTrace(trace_opts, std::move(trace_writer3)));
|
||||
|
||||
ASSERT_OK(db3->Put(wo, handles[0], "a", "1"));
|
||||
@@ -5227,7 +5226,7 @@ TEST_F(DBTest2, TraceWithFilter) {
|
||||
|
||||
std::unique_ptr<TraceReader> trace_reader3;
|
||||
ASSERT_OK(
|
||||
NewFileTraceReader(env_, env_opts, trace_filename3, &trace_reader3));
|
||||
NewFileTraceReader(env_, env_opts, trace_filename3, &trace_reader3));
|
||||
|
||||
// Count the number of records in the trace file;
|
||||
int count = 0;
|
||||
@@ -5503,16 +5502,20 @@ TEST_F(DBTest2, TestGetColumnFamilyHandleUnlocked) {
|
||||
port::Thread user_thread1([&]() {
|
||||
auto cfh = dbi->GetColumnFamilyHandleUnlocked(handles_[0]->GetID());
|
||||
ASSERT_EQ(cfh->GetID(), handles_[0]->GetID());
|
||||
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked1");
|
||||
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::ReadColumnFamilyHandle1");
|
||||
TEST_SYNC_POINT(
|
||||
"TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked1");
|
||||
TEST_SYNC_POINT(
|
||||
"TestGetColumnFamilyHandleUnlocked::ReadColumnFamilyHandle1");
|
||||
ASSERT_EQ(cfh->GetID(), handles_[0]->GetID());
|
||||
});
|
||||
|
||||
port::Thread user_thread2([&]() {
|
||||
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::PreGetColumnFamilyHandleUnlocked2");
|
||||
TEST_SYNC_POINT(
|
||||
"TestGetColumnFamilyHandleUnlocked::PreGetColumnFamilyHandleUnlocked2");
|
||||
auto cfh = dbi->GetColumnFamilyHandleUnlocked(handles_[1]->GetID());
|
||||
ASSERT_EQ(cfh->GetID(), handles_[1]->GetID());
|
||||
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked2");
|
||||
TEST_SYNC_POINT(
|
||||
"TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked2");
|
||||
ASSERT_EQ(cfh->GetID(), handles_[1]->GetID());
|
||||
});
|
||||
|
||||
@@ -5666,7 +5669,7 @@ class DummyOldStats : public Statistics {
|
||||
std::atomic<int> num_rt{0};
|
||||
std::atomic<int> num_mt{0};
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBTest2, OldStatsInterface) {
|
||||
DummyOldStats* dos = new DummyOldStats();
|
||||
|
||||
+68
-66
@@ -29,7 +29,7 @@ int64_t MaybeCurrentTime(Env* env) {
|
||||
env->GetCurrentTime(&time).PermitUncheckedError();
|
||||
return time;
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
// Special Env used to delay background operations
|
||||
|
||||
@@ -125,54 +125,54 @@ DBTestBase::~DBTestBase() {
|
||||
|
||||
bool DBTestBase::ShouldSkipOptions(int option_config, int skip_mask) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
// These options are not supported in ROCKSDB_LITE
|
||||
if (option_config == kHashSkipList ||
|
||||
option_config == kPlainTableFirstBytePrefix ||
|
||||
option_config == kPlainTableCappedPrefix ||
|
||||
option_config == kPlainTableCappedPrefixNonMmap ||
|
||||
option_config == kPlainTableAllBytesPrefix ||
|
||||
option_config == kVectorRep || option_config == kHashLinkList ||
|
||||
option_config == kUniversalCompaction ||
|
||||
option_config == kUniversalCompactionMultiLevel ||
|
||||
option_config == kUniversalSubcompactions ||
|
||||
option_config == kFIFOCompaction ||
|
||||
option_config == kConcurrentSkipList) {
|
||||
return true;
|
||||
}
|
||||
// These options are not supported in ROCKSDB_LITE
|
||||
if (option_config == kHashSkipList ||
|
||||
option_config == kPlainTableFirstBytePrefix ||
|
||||
option_config == kPlainTableCappedPrefix ||
|
||||
option_config == kPlainTableCappedPrefixNonMmap ||
|
||||
option_config == kPlainTableAllBytesPrefix ||
|
||||
option_config == kVectorRep || option_config == kHashLinkList ||
|
||||
option_config == kUniversalCompaction ||
|
||||
option_config == kUniversalCompactionMultiLevel ||
|
||||
option_config == kUniversalSubcompactions ||
|
||||
option_config == kFIFOCompaction ||
|
||||
option_config == kConcurrentSkipList) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((skip_mask & kSkipUniversalCompaction) &&
|
||||
(option_config == kUniversalCompaction ||
|
||||
option_config == kUniversalCompactionMultiLevel ||
|
||||
option_config == kUniversalSubcompactions)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipMergePut) && option_config == kMergePut) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipNoSeekToLast) &&
|
||||
(option_config == kHashLinkList || option_config == kHashSkipList)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipPlainTable) &&
|
||||
(option_config == kPlainTableAllBytesPrefix ||
|
||||
option_config == kPlainTableFirstBytePrefix ||
|
||||
option_config == kPlainTableCappedPrefix ||
|
||||
option_config == kPlainTableCappedPrefixNonMmap)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipHashIndex) &&
|
||||
(option_config == kBlockBasedTableWithPrefixHashIndex ||
|
||||
option_config == kBlockBasedTableWithWholeKeyHashIndex)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipFIFOCompaction) && option_config == kFIFOCompaction) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipMmapReads) && option_config == kWalDirAndMmapReads) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if ((skip_mask & kSkipUniversalCompaction) &&
|
||||
(option_config == kUniversalCompaction ||
|
||||
option_config == kUniversalCompactionMultiLevel ||
|
||||
option_config == kUniversalSubcompactions)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipMergePut) && option_config == kMergePut) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipNoSeekToLast) &&
|
||||
(option_config == kHashLinkList || option_config == kHashSkipList)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipPlainTable) &&
|
||||
(option_config == kPlainTableAllBytesPrefix ||
|
||||
option_config == kPlainTableFirstBytePrefix ||
|
||||
option_config == kPlainTableCappedPrefix ||
|
||||
option_config == kPlainTableCappedPrefixNonMmap)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipHashIndex) &&
|
||||
(option_config == kBlockBasedTableWithPrefixHashIndex ||
|
||||
option_config == kBlockBasedTableWithWholeKeyHashIndex)) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipFIFOCompaction) && option_config == kFIFOCompaction) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipMmapReads) && option_config == kWalDirAndMmapReads) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Switch to a fresh database with the next option configuration to
|
||||
@@ -424,13 +424,13 @@ Options DBTestBase::GetOptions(
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.unordered_write = false;
|
||||
break;
|
||||
case kDirectIO: {
|
||||
options.use_direct_reads = true;
|
||||
options.use_direct_io_for_flush_and_compaction = true;
|
||||
options.compaction_readahead_size = 2 * 1024 * 1024;
|
||||
SetupSyncPointsToMockDirectIO();
|
||||
break;
|
||||
}
|
||||
case kDirectIO: {
|
||||
options.use_direct_reads = true;
|
||||
options.use_direct_io_for_flush_and_compaction = true;
|
||||
options.compaction_readahead_size = 2 * 1024 * 1024;
|
||||
SetupSyncPointsToMockDirectIO();
|
||||
break;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
case kMergePut:
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
@@ -1308,12 +1308,14 @@ void DBTestBase::GetSstFiles(Env* env, std::string path,
|
||||
std::vector<std::string>* files) {
|
||||
EXPECT_OK(env->GetChildren(path, files));
|
||||
|
||||
files->erase(
|
||||
std::remove_if(files->begin(), files->end(), [](std::string name) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
return !(ParseFileName(name, &number, &type) && type == kTableFile);
|
||||
}), files->end());
|
||||
files->erase(std::remove_if(files->begin(), files->end(),
|
||||
[](std::string name) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
return !(ParseFileName(name, &number, &type) &&
|
||||
type == kTableFile);
|
||||
}),
|
||||
files->end());
|
||||
}
|
||||
|
||||
int DBTestBase::GetSstFileCount(std::string path) {
|
||||
@@ -1583,8 +1585,8 @@ void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
ASSERT_EQ(data_iter, true_data.end()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
ASSERT_EQ(data_iter, true_data.end())
|
||||
<< iter_cnt << " / " << true_data.size();
|
||||
delete iter;
|
||||
|
||||
// Verify Iterator::Prev()
|
||||
@@ -1606,8 +1608,8 @@ void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
ASSERT_EQ(data_rev, true_data.rend()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
ASSERT_EQ(data_rev, true_data.rend())
|
||||
<< iter_cnt << " / " << true_data.size();
|
||||
|
||||
// Verify Iterator::Seek()
|
||||
for (auto kv : true_data) {
|
||||
@@ -1637,8 +1639,8 @@ void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
ASSERT_EQ(data_iter, true_data.end()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
ASSERT_EQ(data_iter, true_data.end())
|
||||
<< iter_cnt << " / " << true_data.size();
|
||||
|
||||
// Verify ForwardIterator::Seek()
|
||||
for (auto kv : true_data) {
|
||||
|
||||
+1
-3
@@ -217,9 +217,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
Env::IOPriority GetIOPriority() override {
|
||||
return base_->GetIOPriority();
|
||||
}
|
||||
bool use_direct_io() const override {
|
||||
return base_->use_direct_io();
|
||||
}
|
||||
bool use_direct_io() const override { return base_->use_direct_io(); }
|
||||
Status Allocate(uint64_t offset, uint64_t len) override {
|
||||
return base_->Allocate(offset, len);
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ class DBTestUniversalCompactionBase
|
||||
|
||||
class DBTestUniversalCompaction : public DBTestUniversalCompactionBase {
|
||||
public:
|
||||
DBTestUniversalCompaction() :
|
||||
DBTestUniversalCompactionBase("/db_universal_compaction_test") {}
|
||||
DBTestUniversalCompaction()
|
||||
: DBTestUniversalCompactionBase("/db_universal_compaction_test") {}
|
||||
};
|
||||
|
||||
class DBTestUniversalCompaction2 : public DBTestBase {
|
||||
@@ -93,7 +93,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
std::atomic_bool expect_full_compaction_;
|
||||
std::atomic_bool expect_manual_compaction_;
|
||||
};
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
// Make sure we don't trigger a problem if the trigger condtion is given
|
||||
// to be 0, which is invalid.
|
||||
@@ -563,8 +563,7 @@ TEST_P(DBTestUniversalCompaction, CompactFilesOnUniversalCompaction) {
|
||||
}
|
||||
|
||||
if (compaction_input_file_names.size() == 0) {
|
||||
compaction_input_file_names.push_back(
|
||||
cf_meta.levels[0].files[0].name);
|
||||
compaction_input_file_names.push_back(cf_meta.levels[0].files[0].name);
|
||||
}
|
||||
|
||||
// expect fail since universal compaction only allow L0 output
|
||||
@@ -574,28 +573,23 @@ TEST_P(DBTestUniversalCompaction, CompactFilesOnUniversalCompaction) {
|
||||
.ok());
|
||||
|
||||
// expect ok and verify the compacted files no longer exist.
|
||||
ASSERT_OK(dbfull()->CompactFiles(
|
||||
CompactionOptions(), handles_[1],
|
||||
compaction_input_file_names, 0));
|
||||
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), handles_[1],
|
||||
compaction_input_file_names, 0));
|
||||
|
||||
dbfull()->GetColumnFamilyMetaData(handles_[1], &cf_meta);
|
||||
VerifyCompactionResult(
|
||||
cf_meta,
|
||||
std::set<std::string>(compaction_input_file_names.begin(),
|
||||
compaction_input_file_names.end()));
|
||||
cf_meta, std::set<std::string>(compaction_input_file_names.begin(),
|
||||
compaction_input_file_names.end()));
|
||||
|
||||
compaction_input_file_names.clear();
|
||||
|
||||
// Pick the first and the last file, expect everything is
|
||||
// compacted into one single file.
|
||||
compaction_input_file_names.push_back(cf_meta.levels[0].files[0].name);
|
||||
compaction_input_file_names.push_back(
|
||||
cf_meta.levels[0].files[0].name);
|
||||
compaction_input_file_names.push_back(
|
||||
cf_meta.levels[0].files[
|
||||
cf_meta.levels[0].files.size() - 1].name);
|
||||
ASSERT_OK(dbfull()->CompactFiles(
|
||||
CompactionOptions(), handles_[1],
|
||||
compaction_input_file_names, 0));
|
||||
cf_meta.levels[0].files[cf_meta.levels[0].files.size() - 1].name);
|
||||
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), handles_[1],
|
||||
compaction_input_file_names, 0));
|
||||
|
||||
dbfull()->GetColumnFamilyMetaData(handles_[1], &cf_meta);
|
||||
ASSERT_EQ(cf_meta.levels[0].files.size(), 1U);
|
||||
@@ -604,7 +598,7 @@ TEST_P(DBTestUniversalCompaction, CompactFilesOnUniversalCompaction) {
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionTargetLevel) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.num_levels = 7;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
@@ -640,9 +634,9 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionTargetLevel) {
|
||||
class DBTestUniversalCompactionMultiLevels
|
||||
: public DBTestUniversalCompactionBase {
|
||||
public:
|
||||
DBTestUniversalCompactionMultiLevels() :
|
||||
DBTestUniversalCompactionBase(
|
||||
"/db_universal_compaction_multi_levels_test") {}
|
||||
DBTestUniversalCompactionMultiLevels()
|
||||
: DBTestUniversalCompactionBase(
|
||||
"/db_universal_compaction_multi_levels_test") {}
|
||||
};
|
||||
|
||||
TEST_P(DBTestUniversalCompactionMultiLevels, UniversalCompactionMultiLevels) {
|
||||
@@ -725,12 +719,11 @@ INSTANTIATE_TEST_CASE_P(MultiLevels, DBTestUniversalCompactionMultiLevels,
|
||||
::testing::Combine(::testing::Values(3, 20),
|
||||
::testing::Bool()));
|
||||
|
||||
class DBTestUniversalCompactionParallel :
|
||||
public DBTestUniversalCompactionBase {
|
||||
class DBTestUniversalCompactionParallel : public DBTestUniversalCompactionBase {
|
||||
public:
|
||||
DBTestUniversalCompactionParallel() :
|
||||
DBTestUniversalCompactionBase(
|
||||
"/db_universal_compaction_prallel_test") {}
|
||||
DBTestUniversalCompactionParallel()
|
||||
: DBTestUniversalCompactionBase("/db_universal_compaction_prallel_test") {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DBTestUniversalCompactionParallel, UniversalCompactionParallel) {
|
||||
@@ -919,8 +912,8 @@ INSTANTIATE_TEST_CASE_P(Parallel, DBTestUniversalCompactionParallel,
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionOptions) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.write_buffer_size = 105 << 10; // 105KB
|
||||
options.arena_block_size = 4 << 10; // 4KB
|
||||
options.write_buffer_size = 105 << 10; // 105KB
|
||||
options.arena_block_size = 4 << 10; // 4KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.num_levels = num_levels_;
|
||||
@@ -951,8 +944,8 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionOptions) {
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionStopStyleSimilarSize) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.write_buffer_size = 105 << 10; // 105KB
|
||||
options.arena_block_size = 4 << 10; // 4KB
|
||||
options.write_buffer_size = 105 << 10; // 105KB
|
||||
options.arena_block_size = 4 << 10; // 4KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
// trigger compaction if there are >= 4 files
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
@@ -1353,7 +1346,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionCFPathUse) {
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_3", 500 * 1024);
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_4", 1024 * 1024 * 1024);
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt1);
|
||||
CreateColumnFamilies({"one"},option_vector[1]);
|
||||
CreateColumnFamilies({"one"}, option_vector[1]);
|
||||
|
||||
// Configura CF2 specific paths.
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2", 300 * 1024);
|
||||
@@ -1361,7 +1354,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionCFPathUse) {
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_3", 500 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_4", 1024 * 1024 * 1024);
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt2);
|
||||
CreateColumnFamilies({"two"},option_vector[2]);
|
||||
CreateColumnFamilies({"two"}, option_vector[2]);
|
||||
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
|
||||
|
||||
@@ -1567,7 +1560,6 @@ TEST_P(DBTestUniversalCompaction, IncreaseUniversalCompactionNumLevels) {
|
||||
verify_func(max_key3);
|
||||
}
|
||||
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionSecondPathRatio) {
|
||||
if (!Snappy_Supported()) {
|
||||
return;
|
||||
@@ -1829,9 +1821,9 @@ INSTANTIATE_TEST_CASE_P(NumLevels, DBTestUniversalCompaction,
|
||||
class DBTestUniversalManualCompactionOutputPathId
|
||||
: public DBTestUniversalCompactionBase {
|
||||
public:
|
||||
DBTestUniversalManualCompactionOutputPathId() :
|
||||
DBTestUniversalCompactionBase(
|
||||
"/db_universal_compaction_manual_pid_test") {}
|
||||
DBTestUniversalManualCompactionOutputPathId()
|
||||
: DBTestUniversalCompactionBase(
|
||||
"/db_universal_compaction_manual_pid_test") {}
|
||||
};
|
||||
|
||||
TEST_P(DBTestUniversalManualCompactionOutputPathId,
|
||||
@@ -2236,8 +2228,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
#endif
|
||||
#include "test_util/testutil.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend2.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class DBBasicTestWithTimestamp : public DBBasicTestWithTimestampBase {
|
||||
@@ -50,7 +51,7 @@ TEST_F(DBBasicTestWithTimestamp, SanityChecks) {
|
||||
db_->Put(WriteOptions(), "key", dummy_ts, "value").IsInvalidArgument());
|
||||
ASSERT_TRUE(db_->Merge(WriteOptions(), db_->DefaultColumnFamily(), "key",
|
||||
dummy_ts, "value")
|
||||
.IsNotSupported());
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(db_->Delete(WriteOptions(), "key", dummy_ts).IsInvalidArgument());
|
||||
ASSERT_TRUE(
|
||||
db_->SingleDelete(WriteOptions(), "key", dummy_ts).IsInvalidArgument());
|
||||
@@ -96,7 +97,7 @@ TEST_F(DBBasicTestWithTimestamp, SanityChecks) {
|
||||
ASSERT_TRUE(db_->Put(WriteOptions(), handle, "key", wrong_ts, "value")
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(db_->Merge(WriteOptions(), handle, "key", wrong_ts, "value")
|
||||
.IsNotSupported());
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(
|
||||
db_->Delete(WriteOptions(), handle, "key", wrong_ts).IsInvalidArgument());
|
||||
ASSERT_TRUE(db_->SingleDelete(WriteOptions(), handle, "key", wrong_ts)
|
||||
@@ -3690,6 +3691,185 @@ TEST_F(DBBasicTestWithTimestamp, DeleteRangeGetIteratorWithSnapshot) {
|
||||
db_->ReleaseSnapshot(after_tombstone);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MergeBasic) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.merge_operator = std::make_shared<StringAppendTESTOperator>('.');
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const std::array<std::string, 3> write_ts_strs = {
|
||||
Timestamp(100, 0), Timestamp(200, 0), Timestamp(300, 0)};
|
||||
constexpr size_t kNumOfUniqKeys = 100;
|
||||
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
|
||||
|
||||
for (size_t i = 0; i < write_ts_strs.size(); ++i) {
|
||||
for (size_t j = 0; j < kNumOfUniqKeys; ++j) {
|
||||
Status s;
|
||||
if (i == 0) {
|
||||
const std::string val = "v" + std::to_string(j) + "_0";
|
||||
s = db_->Put(WriteOptions(), Key1(j), write_ts_strs[i], val);
|
||||
} else {
|
||||
const std::string merge_op = std::to_string(i);
|
||||
s = db_->Merge(WriteOptions(), default_cf, Key1(j), write_ts_strs[i],
|
||||
merge_op);
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<std::string, 3> read_ts_strs = {
|
||||
Timestamp(150, 0), Timestamp(250, 0), Timestamp(350, 0)};
|
||||
|
||||
const auto verify_db_with_get = [&]() {
|
||||
for (size_t i = 0; i < kNumOfUniqKeys; ++i) {
|
||||
const std::string base_val = "v" + std::to_string(i) + "_0";
|
||||
const std::array<std::string, 3> expected_values = {
|
||||
base_val, base_val + ".1", base_val + ".1.2"};
|
||||
const std::array<std::string, 3>& expected_ts = write_ts_strs;
|
||||
ReadOptions read_opts;
|
||||
for (size_t j = 0; j < read_ts_strs.size(); ++j) {
|
||||
Slice read_ts = read_ts_strs[j];
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::string value;
|
||||
std::string ts;
|
||||
const Status s = db_->Get(read_opts, Key1(i), &value, &ts);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(expected_values[j], value);
|
||||
ASSERT_EQ(expected_ts[j], ts);
|
||||
|
||||
// Do Seek/SeekForPrev
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
it->Seek(Key1(i));
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ(expected_values[j], it->value());
|
||||
ASSERT_EQ(expected_ts[j], it->timestamp());
|
||||
|
||||
it->SeekForPrev(Key1(i));
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ(expected_values[j], it->value());
|
||||
ASSERT_EQ(expected_ts[j], it->timestamp());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const auto verify_db_with_iterator = [&]() {
|
||||
std::string value_suffix;
|
||||
for (size_t i = 0; i < read_ts_strs.size(); ++i) {
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_ts_strs[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
size_t key_int_val = 0;
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next(), ++key_int_val) {
|
||||
const std::string key = Key1(key_int_val);
|
||||
const std::string value =
|
||||
"v" + std::to_string(key_int_val) + "_0" + value_suffix;
|
||||
ASSERT_EQ(key, it->key());
|
||||
ASSERT_EQ(value, it->value());
|
||||
ASSERT_EQ(write_ts_strs[i], it->timestamp());
|
||||
}
|
||||
ASSERT_EQ(kNumOfUniqKeys, key_int_val);
|
||||
|
||||
key_int_val = kNumOfUniqKeys - 1;
|
||||
for (it->SeekToLast(); it->Valid(); it->Prev(), --key_int_val) {
|
||||
const std::string key = Key1(key_int_val);
|
||||
const std::string value =
|
||||
"v" + std::to_string(key_int_val) + "_0" + value_suffix;
|
||||
ASSERT_EQ(key, it->key());
|
||||
ASSERT_EQ(value, it->value());
|
||||
ASSERT_EQ(write_ts_strs[i], it->timestamp());
|
||||
}
|
||||
ASSERT_EQ(std::numeric_limits<size_t>::max(), key_int_val);
|
||||
|
||||
value_suffix = value_suffix + "." + std::to_string(i + 1);
|
||||
}
|
||||
};
|
||||
|
||||
verify_db_with_get();
|
||||
verify_db_with_iterator();
|
||||
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
verify_db_with_get();
|
||||
verify_db_with_iterator();
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MergeAfterDeletion) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.merge_operator = std::make_shared<StringAppendTESTOperator>('.');
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ColumnFamilyHandle* const column_family = db_->DefaultColumnFamily();
|
||||
|
||||
const size_t num_keys_per_file = 10;
|
||||
const size_t num_merges_per_key = 2;
|
||||
for (size_t i = 0; i < num_keys_per_file; ++i) {
|
||||
std::string ts = Timestamp(i + 10000, 0);
|
||||
Status s = db_->Delete(WriteOptions(), Key1(i), ts);
|
||||
ASSERT_OK(s);
|
||||
for (size_t j = 1; j <= num_merges_per_key; ++j) {
|
||||
ts = Timestamp(i + 10000 + j, 0);
|
||||
s = db_->Merge(WriteOptions(), column_family, Key1(i), ts,
|
||||
std::to_string(j));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
const auto verify_db = [&]() {
|
||||
ReadOptions read_opts;
|
||||
std::string read_ts_str = Timestamp(20000, 0);
|
||||
Slice ts = read_ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
size_t count = 0;
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next(), ++count) {
|
||||
std::string key = Key1(count);
|
||||
ASSERT_EQ(key, it->key());
|
||||
std::string value;
|
||||
for (size_t j = 1; j <= num_merges_per_key; ++j) {
|
||||
value.append(std::to_string(j));
|
||||
if (j < num_merges_per_key) {
|
||||
value.push_back('.');
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(value, it->value());
|
||||
std::string ts1 = Timestamp(count + 10000 + num_merges_per_key, 0);
|
||||
ASSERT_EQ(ts1, it->timestamp());
|
||||
}
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_EQ(num_keys_per_file, count);
|
||||
for (it->SeekToLast(); it->Valid(); it->Prev(), --count) {
|
||||
std::string key = Key1(count - 1);
|
||||
ASSERT_EQ(key, it->key());
|
||||
std::string value;
|
||||
for (size_t j = 1; j <= num_merges_per_key; ++j) {
|
||||
value.append(std::to_string(j));
|
||||
if (j < num_merges_per_key) {
|
||||
value.push_back('.');
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(value, it->value());
|
||||
std::string ts1 = Timestamp(count - 1 + 10000 + num_merges_per_key, 0);
|
||||
ASSERT_EQ(ts1, it->timestamp());
|
||||
}
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_EQ(0, count);
|
||||
};
|
||||
|
||||
verify_db();
|
||||
|
||||
Close();
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+43
-40
@@ -170,7 +170,8 @@ TEST_P(DBWriteTest, WriteStallRemoveNoSlowdownWrite) {
|
||||
|
||||
TEST_P(DBWriteTest, WriteThreadHangOnWriteStall) {
|
||||
Options options = GetOptions();
|
||||
options.level0_stop_writes_trigger = options.level0_slowdown_writes_trigger = 4;
|
||||
options.level0_stop_writes_trigger = options.level0_slowdown_writes_trigger =
|
||||
4;
|
||||
std::vector<port::Thread> threads;
|
||||
std::atomic<int> thread_num(0);
|
||||
port::Mutex mutex;
|
||||
@@ -195,7 +196,7 @@ TEST_P(DBWriteTest, WriteThreadHangOnWriteStall) {
|
||||
Status s = dbfull()->Put(wo, key, "bar");
|
||||
ASSERT_TRUE(s.ok() || s.IsIncomplete());
|
||||
};
|
||||
std::function<void(void *)> unblock_main_thread_func = [&](void *) {
|
||||
std::function<void(void*)> unblock_main_thread_func = [&](void*) {
|
||||
mutex.Lock();
|
||||
++writers;
|
||||
cv.SignalAll();
|
||||
@@ -254,8 +255,9 @@ TEST_P(DBWriteTest, WriteThreadHangOnWriteStall) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(nullptr));
|
||||
// This would have triggered a write stall. Unblock the write group leader
|
||||
TEST_SYNC_POINT("DBWriteTest::WriteThreadHangOnWriteStall:2");
|
||||
// The leader is going to create missing newer links. When the leader finishes,
|
||||
// the next leader is going to delay writes and fail writers with no_slowdown
|
||||
// The leader is going to create missing newer links. When the leader
|
||||
// finishes, the next leader is going to delay writes and fail writers with
|
||||
// no_slowdown
|
||||
|
||||
TEST_SYNC_POINT("DBWriteTest::WriteThreadHangOnWriteStall:3");
|
||||
for (auto& t : threads) {
|
||||
@@ -623,42 +625,43 @@ TEST_P(DBWriteTest, LockWalInEffect) {
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, ConcurrentlyDisabledWAL) {
|
||||
Options options = GetOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kAll);
|
||||
Reopen(options);
|
||||
std::string wal_key_prefix = "WAL_KEY_";
|
||||
std::string no_wal_key_prefix = "K_";
|
||||
// 100 KB value each for NO-WAL operation
|
||||
std::string no_wal_value(1024 * 100, 'X');
|
||||
// 1B value each for WAL operation
|
||||
std::string wal_value = "0";
|
||||
std::thread threads[10];
|
||||
for (int t = 0; t < 10; t++) {
|
||||
threads[t] = std::thread([t, wal_key_prefix, wal_value, no_wal_key_prefix, no_wal_value, this] {
|
||||
for(int i = 0; i < 10; i++) {
|
||||
ROCKSDB_NAMESPACE::WriteOptions write_option_disable;
|
||||
write_option_disable.disableWAL = true;
|
||||
ROCKSDB_NAMESPACE::WriteOptions write_option_default;
|
||||
std::string no_wal_key = no_wal_key_prefix + std::to_string(t) +
|
||||
"_" + std::to_string(i);
|
||||
ASSERT_OK(
|
||||
this->Put(no_wal_key, no_wal_value, write_option_disable));
|
||||
std::string wal_key =
|
||||
wal_key_prefix + std::to_string(i) + "_" + std::to_string(i);
|
||||
ASSERT_OK(this->Put(wal_key, wal_value, write_option_default));
|
||||
ASSERT_OK(dbfull()->SyncWAL());
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
for (auto& t: threads) {
|
||||
t.join();
|
||||
}
|
||||
uint64_t bytes_num = options.statistics->getTickerCount(
|
||||
ROCKSDB_NAMESPACE::Tickers::WAL_FILE_BYTES);
|
||||
// written WAL size should less than 100KB (even included HEADER & FOOTER overhead)
|
||||
ASSERT_LE(bytes_num, 1024 * 100);
|
||||
Options options = GetOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kAll);
|
||||
Reopen(options);
|
||||
std::string wal_key_prefix = "WAL_KEY_";
|
||||
std::string no_wal_key_prefix = "K_";
|
||||
// 100 KB value each for NO-WAL operation
|
||||
std::string no_wal_value(1024 * 100, 'X');
|
||||
// 1B value each for WAL operation
|
||||
std::string wal_value = "0";
|
||||
std::thread threads[10];
|
||||
for (int t = 0; t < 10; t++) {
|
||||
threads[t] = std::thread([t, wal_key_prefix, wal_value, no_wal_key_prefix,
|
||||
no_wal_value, this] {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ROCKSDB_NAMESPACE::WriteOptions write_option_disable;
|
||||
write_option_disable.disableWAL = true;
|
||||
ROCKSDB_NAMESPACE::WriteOptions write_option_default;
|
||||
std::string no_wal_key =
|
||||
no_wal_key_prefix + std::to_string(t) + "_" + std::to_string(i);
|
||||
ASSERT_OK(this->Put(no_wal_key, no_wal_value, write_option_disable));
|
||||
std::string wal_key =
|
||||
wal_key_prefix + std::to_string(i) + "_" + std::to_string(i);
|
||||
ASSERT_OK(this->Put(wal_key, wal_value, write_option_default));
|
||||
ASSERT_OK(dbfull()->SyncWAL());
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
uint64_t bytes_num = options.statistics->getTickerCount(
|
||||
ROCKSDB_NAMESPACE::Tickers::WAL_FILE_BYTES);
|
||||
// written WAL size should less than 100KB (even included HEADER & FOOTER
|
||||
// overhead)
|
||||
ASSERT_LE(bytes_num, 1024 * 100);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBWriteTestInstance, DBWriteTest,
|
||||
|
||||
@@ -129,6 +129,12 @@ struct ParsedInternalKey {
|
||||
const char* addr = user_key.data() + user_key.size() - ts.size();
|
||||
memcpy(const_cast<char*>(addr), ts.data(), ts.size());
|
||||
}
|
||||
|
||||
Slice GetTimestamp(size_t ts_sz) {
|
||||
assert(ts_sz <= user_key.size());
|
||||
const char* addr = user_key.data() + user_key.size() - ts_sz;
|
||||
return Slice(const_cast<char*>(addr), ts_sz);
|
||||
}
|
||||
};
|
||||
|
||||
// Return the length of the encoding of "key".
|
||||
@@ -439,6 +445,8 @@ class IterKey {
|
||||
void SetIsUserKey(bool is_user_key) { is_user_key_ = is_user_key; }
|
||||
|
||||
// Returns the key in whichever format that was provided to KeyIter
|
||||
// If user-defined timestamp is enabled, then timestamp is included in the
|
||||
// return result.
|
||||
Slice GetKey() const { return Slice(key_, key_size_); }
|
||||
|
||||
Slice GetInternalKey() const {
|
||||
@@ -446,6 +454,8 @@ class IterKey {
|
||||
return Slice(key_, key_size_);
|
||||
}
|
||||
|
||||
// If user-defined timestamp is enabled, then timestamp is included in the
|
||||
// return result of GetUserKey();
|
||||
Slice GetUserKey() const {
|
||||
if (IsUserKey()) {
|
||||
return Slice(key_, key_size_);
|
||||
@@ -495,6 +505,9 @@ class IterKey {
|
||||
return SetKeyImpl(key, copy);
|
||||
}
|
||||
|
||||
// If user-defined timestamp is enabled, then `key` includes timestamp.
|
||||
// TODO(yanqin) this is also used to set prefix, which do not include
|
||||
// timestamp. Should be handled.
|
||||
Slice SetUserKey(const Slice& key, bool copy = true) {
|
||||
is_user_key_ = true;
|
||||
return SetKeyImpl(key, copy);
|
||||
@@ -689,6 +702,8 @@ extern bool ReadKeyFromWriteBatchEntry(Slice* input, Slice* key,
|
||||
// slice they point to.
|
||||
// Tag is defined as ValueType.
|
||||
// input will be advanced to after the record.
|
||||
// If user-defined timestamp is enabled for a column family, then the `key`
|
||||
// resulting from this call will include timestamp.
|
||||
extern Status ReadRecordFromWriteBatch(Slice* input, char* tag,
|
||||
uint32_t* column_family, Slice* key,
|
||||
Slice* value, Slice* blob, Slice* xid);
|
||||
|
||||
+35
-34
@@ -15,8 +15,7 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
static std::string IKey(const std::string& user_key,
|
||||
uint64_t seq,
|
||||
static std::string IKey(const std::string& user_key, uint64_t seq,
|
||||
ValueType vt) {
|
||||
std::string encoded;
|
||||
AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt));
|
||||
@@ -37,9 +36,7 @@ static std::string ShortSuccessor(const std::string& s) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void TestKey(const std::string& key,
|
||||
uint64_t seq,
|
||||
ValueType vt) {
|
||||
static void TestKey(const std::string& key, uint64_t seq, ValueType vt) {
|
||||
std::string encoded = IKey(key, seq, vt);
|
||||
|
||||
Slice in(encoded);
|
||||
@@ -56,13 +53,19 @@ static void TestKey(const std::string& key,
|
||||
class FormatTest : public testing::Test {};
|
||||
|
||||
TEST_F(FormatTest, InternalKey_EncodeDecode) {
|
||||
const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" };
|
||||
const uint64_t seq[] = {
|
||||
1, 2, 3,
|
||||
(1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1,
|
||||
(1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1,
|
||||
(1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1
|
||||
};
|
||||
const char* keys[] = {"", "k", "hello", "longggggggggggggggggggggg"};
|
||||
const uint64_t seq[] = {1,
|
||||
2,
|
||||
3,
|
||||
(1ull << 8) - 1,
|
||||
1ull << 8,
|
||||
(1ull << 8) + 1,
|
||||
(1ull << 16) - 1,
|
||||
1ull << 16,
|
||||
(1ull << 16) + 1,
|
||||
(1ull << 32) - 1,
|
||||
1ull << 32,
|
||||
(1ull << 32) + 1};
|
||||
for (unsigned int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) {
|
||||
for (unsigned int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) {
|
||||
TestKey(keys[k], seq[s], kTypeValue);
|
||||
@@ -74,27 +77,25 @@ TEST_F(FormatTest, InternalKey_EncodeDecode) {
|
||||
TEST_F(FormatTest, InternalKeyShortSeparator) {
|
||||
// When user keys are same
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foo", 99, kTypeValue)));
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foo", 101, kTypeValue)));
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foo", 100, kTypeValue)));
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foo", 100, kTypeDeletion)));
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 99, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 101, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 100, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 100, kTypeDeletion)));
|
||||
|
||||
// When user keys are misordered
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("bar", 99, kTypeValue)));
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("bar", 99, kTypeValue)));
|
||||
|
||||
// When user keys are different, but correctly ordered
|
||||
ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("hello", 200, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("hello", 200, kTypeValue)));
|
||||
|
||||
ASSERT_EQ(IKey("ABC2", kMaxSequenceNumber, kValueTypeForSeek),
|
||||
Shorten(IKey("ABC1AAAAA", 100, kTypeValue),
|
||||
@@ -121,14 +122,14 @@ TEST_F(FormatTest, InternalKeyShortSeparator) {
|
||||
Shorten(IKey("AAA1", 100, kTypeValue), IKey("AAA2", 200, kTypeValue)));
|
||||
|
||||
// When start user key is prefix of limit user key
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foobar", 200, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foobar", 200, kTypeValue)));
|
||||
|
||||
// When limit user key is prefix of start user key
|
||||
ASSERT_EQ(IKey("foobar", 100, kTypeValue),
|
||||
Shorten(IKey("foobar", 100, kTypeValue),
|
||||
IKey("foo", 200, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foobar", 100, kTypeValue),
|
||||
Shorten(IKey("foobar", 100, kTypeValue), IKey("foo", 200, kTypeValue)));
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, InternalKeyShortestSuccessor) {
|
||||
|
||||
+10
-11
@@ -10,9 +10,11 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/version_set.h"
|
||||
@@ -55,7 +57,7 @@ class DeleteFileTest : public DBTestBase {
|
||||
WriteOptions options;
|
||||
options.sync = false;
|
||||
ReadOptions roptions;
|
||||
for (int i = startkey; i < (numkeys + startkey) ; i++) {
|
||||
for (int i = startkey; i < (numkeys + startkey); i++) {
|
||||
std::string temp = std::to_string(i);
|
||||
Slice key(temp);
|
||||
Slice value(temp);
|
||||
@@ -63,10 +65,8 @@ class DeleteFileTest : public DBTestBase {
|
||||
}
|
||||
}
|
||||
|
||||
int numKeysInLevels(
|
||||
std::vector<LiveFileMetaData> &metadata,
|
||||
std::vector<int> *keysperlevel = nullptr) {
|
||||
|
||||
int numKeysInLevels(std::vector<LiveFileMetaData>& metadata,
|
||||
std::vector<int>* keysperlevel = nullptr) {
|
||||
if (keysperlevel != nullptr) {
|
||||
keysperlevel->resize(numlevels_);
|
||||
}
|
||||
@@ -82,8 +82,7 @@ class DeleteFileTest : public DBTestBase {
|
||||
}
|
||||
fprintf(stderr, "level %d name %s smallest %s largest %s\n",
|
||||
metadata[i].level, metadata[i].name.c_str(),
|
||||
metadata[i].smallestkey.c_str(),
|
||||
metadata[i].largestkey.c_str());
|
||||
metadata[i].smallestkey.c_str(), metadata[i].largestkey.c_str());
|
||||
}
|
||||
return numKeys;
|
||||
}
|
||||
@@ -214,7 +213,7 @@ TEST_F(DeleteFileTest, PurgeObsoleteFilesTest) {
|
||||
|
||||
// this time, we keep an iterator alive
|
||||
Reopen(options);
|
||||
Iterator *itr = nullptr;
|
||||
Iterator* itr = nullptr;
|
||||
CreateTwoLevels();
|
||||
itr = db_->NewIterator(ReadOptions());
|
||||
ASSERT_OK(itr->status());
|
||||
@@ -481,12 +480,12 @@ TEST_F(DeleteFileTest, DeleteFileWithIterator) {
|
||||
}
|
||||
|
||||
Status status = db_->DeleteFile(level2file);
|
||||
fprintf(stdout, "Deletion status %s: %s\n",
|
||||
level2file.c_str(), status.ToString().c_str());
|
||||
fprintf(stdout, "Deletion status %s: %s\n", level2file.c_str(),
|
||||
status.ToString().c_str());
|
||||
ASSERT_OK(status);
|
||||
it->SeekToFirst();
|
||||
int numKeysIterated = 0;
|
||||
while(it->Valid()) {
|
||||
while (it->Valid()) {
|
||||
numKeysIterated++;
|
||||
it->Next();
|
||||
}
|
||||
|
||||
+6
-6
@@ -234,8 +234,8 @@ void ErrorHandler::CancelErrorRecovery() {
|
||||
// We'll release the lock before calling sfm, so make sure no new
|
||||
// recovery gets scheduled at that point
|
||||
auto_recovery_ = false;
|
||||
SstFileManagerImpl* sfm = reinterpret_cast<SstFileManagerImpl*>(
|
||||
db_options_.sst_file_manager.get());
|
||||
SstFileManagerImpl* sfm =
|
||||
reinterpret_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
if (sfm) {
|
||||
// This may or may not cancel a pending recovery
|
||||
db_mutex_->Unlock();
|
||||
@@ -292,8 +292,8 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
bool found = false;
|
||||
|
||||
{
|
||||
auto entry = ErrorSeverityMap.find(std::make_tuple(reason, bg_err.code(),
|
||||
bg_err.subcode(), paranoid));
|
||||
auto entry = ErrorSeverityMap.find(
|
||||
std::make_tuple(reason, bg_err.code(), bg_err.subcode(), paranoid));
|
||||
if (entry != ErrorSeverityMap.end()) {
|
||||
sev = entry->second;
|
||||
found = true;
|
||||
@@ -301,8 +301,8 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
auto entry = DefaultErrorSeverityMap.find(std::make_tuple(reason,
|
||||
bg_err.code(), paranoid));
|
||||
auto entry = DefaultErrorSeverityMap.find(
|
||||
std::make_tuple(reason, bg_err.code(), paranoid));
|
||||
if (entry != DefaultErrorSeverityMap.end()) {
|
||||
sev = entry->second;
|
||||
found = true;
|
||||
|
||||
+76
-77
@@ -26,100 +26,99 @@ struct DBRecoverContext {
|
||||
};
|
||||
|
||||
class ErrorHandler {
|
||||
public:
|
||||
ErrorHandler(DBImpl* db, const ImmutableDBOptions& db_options,
|
||||
InstrumentedMutex* db_mutex)
|
||||
: db_(db),
|
||||
db_options_(db_options),
|
||||
cv_(db_mutex),
|
||||
end_recovery_(false),
|
||||
recovery_thread_(nullptr),
|
||||
db_mutex_(db_mutex),
|
||||
auto_recovery_(false),
|
||||
recovery_in_prog_(false),
|
||||
soft_error_no_bg_work_(false),
|
||||
is_db_stopped_(false),
|
||||
bg_error_stats_(db_options.statistics) {
|
||||
// Clear the checked flag for uninitialized errors
|
||||
bg_error_.PermitUncheckedError();
|
||||
recovery_error_.PermitUncheckedError();
|
||||
recovery_io_error_.PermitUncheckedError();
|
||||
}
|
||||
public:
|
||||
ErrorHandler(DBImpl* db, const ImmutableDBOptions& db_options,
|
||||
InstrumentedMutex* db_mutex)
|
||||
: db_(db),
|
||||
db_options_(db_options),
|
||||
cv_(db_mutex),
|
||||
end_recovery_(false),
|
||||
recovery_thread_(nullptr),
|
||||
db_mutex_(db_mutex),
|
||||
auto_recovery_(false),
|
||||
recovery_in_prog_(false),
|
||||
soft_error_no_bg_work_(false),
|
||||
is_db_stopped_(false),
|
||||
bg_error_stats_(db_options.statistics) {
|
||||
// Clear the checked flag for uninitialized errors
|
||||
bg_error_.PermitUncheckedError();
|
||||
recovery_error_.PermitUncheckedError();
|
||||
recovery_io_error_.PermitUncheckedError();
|
||||
}
|
||||
|
||||
void EnableAutoRecovery() { auto_recovery_ = true; }
|
||||
void EnableAutoRecovery() { auto_recovery_ = true; }
|
||||
|
||||
Status::Severity GetErrorSeverity(BackgroundErrorReason reason,
|
||||
Status::Code code,
|
||||
Status::SubCode subcode);
|
||||
Status::Severity GetErrorSeverity(BackgroundErrorReason reason,
|
||||
Status::Code code, Status::SubCode subcode);
|
||||
|
||||
const Status& SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
const Status& SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
|
||||
Status GetBGError() const { return bg_error_; }
|
||||
Status GetBGError() const { return bg_error_; }
|
||||
|
||||
Status GetRecoveryError() const { return recovery_error_; }
|
||||
Status GetRecoveryError() const { return recovery_error_; }
|
||||
|
||||
Status ClearBGError();
|
||||
Status ClearBGError();
|
||||
|
||||
bool IsDBStopped() { return is_db_stopped_.load(std::memory_order_acquire); }
|
||||
bool IsDBStopped() { return is_db_stopped_.load(std::memory_order_acquire); }
|
||||
|
||||
bool IsBGWorkStopped() {
|
||||
assert(db_mutex_);
|
||||
db_mutex_->AssertHeld();
|
||||
return !bg_error_.ok() &&
|
||||
(bg_error_.severity() >= Status::Severity::kHardError ||
|
||||
!auto_recovery_ || soft_error_no_bg_work_);
|
||||
}
|
||||
bool IsBGWorkStopped() {
|
||||
assert(db_mutex_);
|
||||
db_mutex_->AssertHeld();
|
||||
return !bg_error_.ok() &&
|
||||
(bg_error_.severity() >= Status::Severity::kHardError ||
|
||||
!auto_recovery_ || soft_error_no_bg_work_);
|
||||
}
|
||||
|
||||
bool IsSoftErrorNoBGWork() { return soft_error_no_bg_work_; }
|
||||
bool IsSoftErrorNoBGWork() { return soft_error_no_bg_work_; }
|
||||
|
||||
bool IsRecoveryInProgress() { return recovery_in_prog_; }
|
||||
bool IsRecoveryInProgress() { return recovery_in_prog_; }
|
||||
|
||||
Status RecoverFromBGError(bool is_manual = false);
|
||||
void CancelErrorRecovery();
|
||||
Status RecoverFromBGError(bool is_manual = false);
|
||||
void CancelErrorRecovery();
|
||||
|
||||
void EndAutoRecovery();
|
||||
void EndAutoRecovery();
|
||||
|
||||
private:
|
||||
DBImpl* db_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
Status bg_error_;
|
||||
// A separate Status variable used to record any errors during the
|
||||
// recovery process from hard errors
|
||||
Status recovery_error_;
|
||||
// A separate IO Status variable used to record any IO errors during
|
||||
// the recovery process. At the same time, recovery_error_ is also set.
|
||||
IOStatus recovery_io_error_;
|
||||
// The condition variable used with db_mutex during auto resume for time
|
||||
// wait.
|
||||
InstrumentedCondVar cv_;
|
||||
bool end_recovery_;
|
||||
std::unique_ptr<port::Thread> recovery_thread_;
|
||||
private:
|
||||
DBImpl* db_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
Status bg_error_;
|
||||
// A separate Status variable used to record any errors during the
|
||||
// recovery process from hard errors
|
||||
Status recovery_error_;
|
||||
// A separate IO Status variable used to record any IO errors during
|
||||
// the recovery process. At the same time, recovery_error_ is also set.
|
||||
IOStatus recovery_io_error_;
|
||||
// The condition variable used with db_mutex during auto resume for time
|
||||
// wait.
|
||||
InstrumentedCondVar cv_;
|
||||
bool end_recovery_;
|
||||
std::unique_ptr<port::Thread> recovery_thread_;
|
||||
|
||||
InstrumentedMutex* db_mutex_;
|
||||
// A flag indicating whether automatic recovery from errors is enabled
|
||||
bool auto_recovery_;
|
||||
bool recovery_in_prog_;
|
||||
// A flag to indicate that for the soft error, we should not allow any
|
||||
// background work except the work is from recovery.
|
||||
bool soft_error_no_bg_work_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
// A flag indicating whether automatic recovery from errors is enabled
|
||||
bool auto_recovery_;
|
||||
bool recovery_in_prog_;
|
||||
// A flag to indicate that for the soft error, we should not allow any
|
||||
// background work except the work is from recovery.
|
||||
bool soft_error_no_bg_work_;
|
||||
|
||||
// Used to store the context for recover, such as flush reason.
|
||||
DBRecoverContext recover_context_;
|
||||
std::atomic<bool> is_db_stopped_;
|
||||
// Used to store the context for recover, such as flush reason.
|
||||
DBRecoverContext recover_context_;
|
||||
std::atomic<bool> is_db_stopped_;
|
||||
|
||||
// The pointer of DB statistics.
|
||||
std::shared_ptr<Statistics> bg_error_stats_;
|
||||
// The pointer of DB statistics.
|
||||
std::shared_ptr<Statistics> bg_error_stats_;
|
||||
|
||||
const Status& HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason);
|
||||
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
|
||||
void RecoverFromNoSpace();
|
||||
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
void RecoverFromRetryableBGIOError();
|
||||
// First, if it is in recovery and the recovery_error is ok. Set the
|
||||
// recovery_error_ to bg_err. Second, if the severity is higher than the
|
||||
// current bg_error_, overwrite it.
|
||||
void CheckAndSetRecoveryAndBGError(const Status& bg_err);
|
||||
const Status& HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason);
|
||||
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
|
||||
void RecoverFromNoSpace();
|
||||
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
void RecoverFromRetryableBGIOError();
|
||||
// First, if it is in recovery and the recovery_error is ok. Set the
|
||||
// recovery_error_ to bg_err. Second, if the severity is higher than the
|
||||
// current bg_error_, overwrite it.
|
||||
void CheckAndSetRecoveryAndBGError(const Status& bg_err);
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+1
-1
@@ -23,7 +23,7 @@ template <class T>
|
||||
inline T SafeDivide(T a, T b) {
|
||||
return b == 0 ? 0 : a / b;
|
||||
}
|
||||
} // namespace
|
||||
} // anonymous namespace
|
||||
|
||||
void EventHelpers::AppendCurrentTime(JSONWriter* jwriter) {
|
||||
*jwriter << "time_micros"
|
||||
|
||||
+3
-3
@@ -39,9 +39,9 @@ class EventHelpers {
|
||||
const std::string& file_checksum,
|
||||
const std::string& file_checksum_func_name);
|
||||
static void LogAndNotifyTableFileDeletion(
|
||||
EventLogger* event_logger, int job_id,
|
||||
uint64_t file_number, const std::string& file_path,
|
||||
const Status& status, const std::string& db_name,
|
||||
EventLogger* event_logger, int job_id, uint64_t file_number,
|
||||
const std::string& file_path, const Status& status,
|
||||
const std::string& db_name,
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners);
|
||||
static void NotifyOnErrorRecoveryEnd(
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners,
|
||||
|
||||
@@ -106,9 +106,8 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
f.copy_file = false;
|
||||
const std::string path_outside_db = f.external_file_path;
|
||||
const std::string path_inside_db =
|
||||
TableFileName(cfd_->ioptions()->cf_paths, f.fd.GetNumber(),
|
||||
f.fd.GetPathId());
|
||||
const std::string path_inside_db = TableFileName(
|
||||
cfd_->ioptions()->cf_paths, f.fd.GetNumber(), f.fd.GetPathId());
|
||||
if (ingestion_options_.move_files) {
|
||||
status =
|
||||
fs_->LinkFile(path_outside_db, path_inside_db, IOOptions(), nullptr);
|
||||
@@ -491,7 +490,8 @@ void ExternalSstFileIngestionJob::UpdateStats() {
|
||||
stream.StartArray();
|
||||
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
InternalStats::CompactionStats stats(CompactionReason::kExternalSstIngestion, 1);
|
||||
InternalStats::CompactionStats stats(
|
||||
CompactionReason::kExternalSstIngestion, 1);
|
||||
stats.micros = total_time;
|
||||
// If actual copy occurred for this file, then we need to count the file
|
||||
// size as the actual bytes written. If the file was linked, then we ignore
|
||||
@@ -591,8 +591,8 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
std::unique_ptr<FSRandomAccessFile> sst_file;
|
||||
std::unique_ptr<RandomAccessFileReader> sst_file_reader;
|
||||
|
||||
status = fs_->NewRandomAccessFile(external_file, env_options_,
|
||||
&sst_file, nullptr);
|
||||
status =
|
||||
fs_->NewRandomAccessFile(external_file, env_options_, &sst_file, nullptr);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
@@ -658,9 +658,9 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
assert(seqno_iter == uprops.end());
|
||||
file_to_ingest->original_seqno = 0;
|
||||
if (ingestion_options_.allow_blocking_flush ||
|
||||
ingestion_options_.allow_global_seqno) {
|
||||
ingestion_options_.allow_global_seqno) {
|
||||
return Status::InvalidArgument(
|
||||
"External SST file V1 does not support global seqno");
|
||||
"External SST file V1 does not support global seqno");
|
||||
}
|
||||
} else {
|
||||
return Status::InvalidArgument("External file version is not supported");
|
||||
@@ -857,7 +857,7 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
return status;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile",
|
||||
&overlap_with_db);
|
||||
file_to_ingest->picked_level = target_level;
|
||||
@@ -872,10 +872,10 @@ Status ExternalSstFileIngestionJob::CheckLevelForIngestedBehindFile(
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
// first check if new files fit in the bottommost level
|
||||
int bottom_lvl = cfd_->NumberLevels() - 1;
|
||||
if(!IngestedFileFitInLevel(file_to_ingest, bottom_lvl)) {
|
||||
if (!IngestedFileFitInLevel(file_to_ingest, bottom_lvl)) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file as it doesn't fit "
|
||||
"at the bottommost level!");
|
||||
"Can't ingest_behind file as it doesn't fit "
|
||||
"at the bottommost level!");
|
||||
}
|
||||
|
||||
// second check if despite allow_ingest_behind=true we still have 0 seqnums
|
||||
@@ -884,8 +884,8 @@ Status ExternalSstFileIngestionJob::CheckLevelForIngestedBehindFile(
|
||||
for (auto file : vstorage->LevelFiles(lvl)) {
|
||||
if (file->fd.smallest_seqno == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file as despite allow_ingest_behind=true "
|
||||
"there are files with 0 seqno in database at upper levels!");
|
||||
"Can't ingest_behind file as despite allow_ingest_behind=true "
|
||||
"there are files with 0 seqno in database at upper levels!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -912,9 +912,8 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
// If the file system does not support random write, then we should not.
|
||||
// Otherwise we should.
|
||||
std::unique_ptr<FSRandomRWFile> rwfile;
|
||||
Status status =
|
||||
fs_->NewRandomRWFile(file_to_ingest->internal_file_path, env_options_,
|
||||
&rwfile, nullptr);
|
||||
Status status = fs_->NewRandomRWFile(file_to_ingest->internal_file_path,
|
||||
env_options_, &rwfile, nullptr);
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::NewRandomRWFile",
|
||||
&status);
|
||||
if (status.ok()) {
|
||||
|
||||
@@ -301,7 +301,8 @@ TEST_F(ExternalSSTFileTest, Basic) {
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Current file size should be 0 after sst_file_writer init and before open a file.
|
||||
// Current file size should be 0 after sst_file_writer init and before open
|
||||
// a file.
|
||||
ASSERT_EQ(sst_file_writer.FileSize(), 0);
|
||||
|
||||
// file1.sst (0 => 99)
|
||||
@@ -2318,7 +2319,6 @@ TEST_F(ExternalSSTFileTest, SkipBloomFilter) {
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
|
||||
// Create external SST file and include bloom filters
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -338,8 +338,7 @@ class FaultInjectionTest
|
||||
FaultInjectionTest::kValExpectNoError));
|
||||
}
|
||||
|
||||
void NoWriteTestPreFault() {
|
||||
}
|
||||
void NoWriteTestPreFault() {}
|
||||
|
||||
void NoWriteTestReopenWithFault(ResetMethod reset_method) {
|
||||
CloseDB();
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/file_indexer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "db/version_edit.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "memory/arena.h"
|
||||
#include "port/port.h"
|
||||
#include "util/autovector.h"
|
||||
@@ -66,7 +67,7 @@ class FileIndexer {
|
||||
|
||||
struct IndexUnit {
|
||||
IndexUnit()
|
||||
: smallest_lb(0), largest_lb(0), smallest_rb(-1), largest_rb(-1) {}
|
||||
: smallest_lb(0), largest_lb(0), smallest_rb(-1), largest_rb(-1) {}
|
||||
// During file search, a key is compared against smallest and largest
|
||||
// from a FileMetaData. It can have 3 possible outcomes:
|
||||
// (1) key is smaller than smallest, implying it is also smaller than
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/file_indexer.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -73,8 +75,8 @@ class FileIndexerTest : public testing::Test {
|
||||
}
|
||||
|
||||
void GetNextLevelIndex(const uint32_t level, const uint32_t file_index,
|
||||
const int cmp_smallest, const int cmp_largest, int32_t* left_index,
|
||||
int32_t* right_index) {
|
||||
const int cmp_smallest, const int cmp_largest,
|
||||
int32_t* left_index, int32_t* right_index) {
|
||||
*left_index = 100;
|
||||
*right_index = 100;
|
||||
indexer->GetNextLevelIndex(level, file_index, cmp_smallest, cmp_largest,
|
||||
|
||||
+27
-29
@@ -69,35 +69,33 @@ TEST_F(FileNameTest, Parse) {
|
||||
}
|
||||
|
||||
// Errors
|
||||
static const char* errors[] = {
|
||||
"",
|
||||
"foo",
|
||||
"foo-dx-100.log",
|
||||
".log",
|
||||
"",
|
||||
"manifest",
|
||||
"CURREN",
|
||||
"CURRENTX",
|
||||
"MANIFES",
|
||||
"MANIFEST",
|
||||
"MANIFEST-",
|
||||
"XMANIFEST-3",
|
||||
"MANIFEST-3x",
|
||||
"META",
|
||||
"METADB",
|
||||
"METADB-",
|
||||
"XMETADB-3",
|
||||
"METADB-3x",
|
||||
"LOC",
|
||||
"LOCKx",
|
||||
"LO",
|
||||
"LOGx",
|
||||
"18446744073709551616.log",
|
||||
"184467440737095516150.log",
|
||||
"100",
|
||||
"100.",
|
||||
"100.lop"
|
||||
};
|
||||
static const char* errors[] = {"",
|
||||
"foo",
|
||||
"foo-dx-100.log",
|
||||
".log",
|
||||
"",
|
||||
"manifest",
|
||||
"CURREN",
|
||||
"CURRENTX",
|
||||
"MANIFES",
|
||||
"MANIFEST",
|
||||
"MANIFEST-",
|
||||
"XMANIFEST-3",
|
||||
"MANIFEST-3x",
|
||||
"META",
|
||||
"METADB",
|
||||
"METADB-",
|
||||
"XMETADB-3",
|
||||
"METADB-3x",
|
||||
"LOC",
|
||||
"LOCKx",
|
||||
"LO",
|
||||
"LOGx",
|
||||
"18446744073709551616.log",
|
||||
"184467440737095516150.log",
|
||||
"100",
|
||||
"100.",
|
||||
"100.lop"};
|
||||
for (unsigned int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) {
|
||||
std::string f = errors[i];
|
||||
ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f;
|
||||
|
||||
+13
-19
@@ -48,7 +48,7 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
const char* GetFlushReasonString (FlushReason flush_reason) {
|
||||
const char* GetFlushReasonString(FlushReason flush_reason) {
|
||||
switch (flush_reason) {
|
||||
case FlushReason::kOthers:
|
||||
return "Other Reasons";
|
||||
@@ -90,7 +90,7 @@ FlushJob::FlushJob(
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FlushReason flush_reason, LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_file_directory, CompressionType output_compression,
|
||||
Statistics* stats, EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
@@ -113,6 +113,7 @@ FlushJob::FlushJob(
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
job_context_(job_context),
|
||||
flush_reason_(flush_reason),
|
||||
log_buffer_(log_buffer),
|
||||
db_directory_(db_directory),
|
||||
output_file_directory_(output_file_directory),
|
||||
@@ -136,17 +137,14 @@ FlushJob::FlushJob(
|
||||
TEST_SYNC_POINT("FlushJob::FlushJob()");
|
||||
}
|
||||
|
||||
FlushJob::~FlushJob() {
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
}
|
||||
FlushJob::~FlushJob() { ThreadStatusUtil::ResetThreadStatus(); }
|
||||
|
||||
void FlushJob::ReportStartedFlush() {
|
||||
ThreadStatusUtil::SetColumnFamily(cfd_, cfd_->ioptions()->env,
|
||||
db_options_.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_FLUSH);
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_JOB_ID,
|
||||
job_context_->job_id);
|
||||
ThreadStatusUtil::SetThreadOperationProperty(ThreadStatus::COMPACTION_JOB_ID,
|
||||
job_context_->job_id);
|
||||
IOSTATS_RESET(bytes_written);
|
||||
}
|
||||
|
||||
@@ -156,8 +154,7 @@ void FlushJob::ReportFlushInputSize(const autovector<MemTable*>& mems) {
|
||||
input_size += mem->ApproximateMemoryUsage();
|
||||
}
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::FLUSH_BYTES_MEMTABLES,
|
||||
input_size);
|
||||
ThreadStatus::FLUSH_BYTES_MEMTABLES, input_size);
|
||||
}
|
||||
|
||||
void FlushJob::RecordFlushIOStats() {
|
||||
@@ -220,8 +217,7 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker, FileMetaData* file_meta,
|
||||
double mempurge_threshold =
|
||||
mutable_cf_options_.experimental_mempurge_threshold;
|
||||
|
||||
AutoThreadOperationStageUpdater stage_run(
|
||||
ThreadStatus::STAGE_FLUSH_RUN);
|
||||
AutoThreadOperationStageUpdater stage_run(ThreadStatus::STAGE_FLUSH_RUN);
|
||||
if (mems_.empty()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Nothing in memtable to flush",
|
||||
cfd_->GetName().c_str());
|
||||
@@ -248,9 +244,8 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker, FileMetaData* file_meta,
|
||||
}
|
||||
Status mempurge_s = Status::NotFound("No MemPurge.");
|
||||
if ((mempurge_threshold > 0.0) &&
|
||||
(cfd_->GetFlushReason() == FlushReason::kWriteBufferFull) &&
|
||||
(!mems_.empty()) && MemPurgeDecider(mempurge_threshold) &&
|
||||
!(db_options_.atomic_flush)) {
|
||||
(flush_reason_ == FlushReason::kWriteBufferFull) && (!mems_.empty()) &&
|
||||
MemPurgeDecider(mempurge_threshold) && !(db_options_.atomic_flush)) {
|
||||
cfd_->SetMempurgeUsed();
|
||||
mempurge_s = MemPurge();
|
||||
if (!mempurge_s.ok()) {
|
||||
@@ -881,7 +876,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
<< total_num_deletes << "total_data_size"
|
||||
<< total_data_size << "memory_usage"
|
||||
<< total_memory_usage << "flush_reason"
|
||||
<< GetFlushReasonString(cfd_->GetFlushReason());
|
||||
<< GetFlushReasonString(flush_reason_);
|
||||
|
||||
{
|
||||
ScopedArenaIterator iter(
|
||||
@@ -906,8 +901,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
}
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
uint64_t oldest_key_time =
|
||||
mems_.front()->ApproximateOldestKeyTime();
|
||||
uint64_t oldest_key_time = mems_.front()->ApproximateOldestKeyTime();
|
||||
|
||||
// It's not clear whether oldest_key_time is always available. In case
|
||||
// it is not available, use current_time.
|
||||
@@ -1080,7 +1074,7 @@ std::unique_ptr<FlushJobInfo> FlushJob::GetFlushJobInfo() const {
|
||||
info->smallest_seqno = meta_.fd.smallest_seqno;
|
||||
info->largest_seqno = meta_.fd.largest_seqno;
|
||||
info->table_properties = table_properties_;
|
||||
info->flush_reason = cfd_->GetFlushReason();
|
||||
info->flush_reason = flush_reason_;
|
||||
info->blob_compression_type = mutable_cf_options_.blob_compression_type;
|
||||
|
||||
// Update BlobFilesInfo.
|
||||
|
||||
+3
-2
@@ -67,8 +67,8 @@ class FlushJob {
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_file_directory,
|
||||
FlushReason flush_reason, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_file_directory,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
@@ -150,6 +150,7 @@ class FlushJob {
|
||||
SequenceNumber earliest_write_conflict_snapshot_;
|
||||
SnapshotChecker* snapshot_checker_;
|
||||
JobContext* job_context_;
|
||||
FlushReason flush_reason_;
|
||||
LogBuffer* log_buffer_;
|
||||
FSDirectory* db_directory_;
|
||||
FSDirectory* output_file_directory_;
|
||||
|
||||
+31
-31
@@ -164,15 +164,15 @@ TEST_F(FlushJobTest, Empty) {
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */,
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
{}, kMaxSequenceNumber, snapshot_checker, &job_context,
|
||||
nullptr, nullptr, nullptr, kNoCompression, nullptr,
|
||||
&event_logger, false, true /* sync_output_directory */,
|
||||
true /* write_manifest */, Env::Priority::USER,
|
||||
nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, nullptr, &event_logger, false,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
flush_job.PickMemTable();
|
||||
@@ -214,8 +214,8 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
// Note: the first two blob references will not be considered when resolving
|
||||
// the oldest blob file referenced (the first one is inlined TTL, while the
|
||||
// second one is TTL and thus points to a TTL blob file).
|
||||
constexpr std::array<uint64_t, 6> blob_file_numbers{{
|
||||
kInvalidBlobFileNumber, 5, 103, 17, 102, 101}};
|
||||
constexpr std::array<uint64_t, 6> blob_file_numbers{
|
||||
{kInvalidBlobFileNumber, 5, 103, 17, 102, 101}};
|
||||
for (size_t i = 0; i < blob_file_numbers.size(); ++i) {
|
||||
std::string key(std::to_string(i + 10001));
|
||||
std::string blob_index;
|
||||
@@ -255,9 +255,9 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
|
||||
HistogramData hist;
|
||||
@@ -318,9 +318,9 @@ TEST_F(FlushJobTest, FlushMemTablesSingleColumnFamily) {
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), flush_memtable_id, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
HistogramData hist;
|
||||
FileMetaData file_meta;
|
||||
@@ -391,8 +391,8 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
memtable_ids[k], env_options_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshot_seqs, kMaxSequenceNumber, snapshot_checker,
|
||||
&job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
&job_context, FlushReason::kTest, nullptr, nullptr, nullptr,
|
||||
kNoCompression, db_options_.statistics.get(), &event_logger, true,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/,
|
||||
empty_seqno_to_time_mapping_));
|
||||
@@ -520,9 +520,9 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
@@ -576,9 +576,9 @@ TEST_F(FlushJobTest, GetRateLimiterPriorityForWrite) {
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), flush_memtable_id, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||
|
||||
// When the state from WriteController is normal.
|
||||
@@ -656,9 +656,9 @@ TEST_F(FlushJobTimestampTest, AllKeysExpired) {
|
||||
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_,
|
||||
/*db_id=*/"",
|
||||
/*db_session_id=*/"", full_history_ts_low);
|
||||
@@ -709,9 +709,9 @@ TEST_F(FlushJobTimestampTest, NoKeyExpired) {
|
||||
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_,
|
||||
/*db_id=*/"",
|
||||
/*db_session_id=*/"", full_history_ts_low);
|
||||
|
||||
+15
-21
@@ -104,9 +104,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
status_ = Status::NotSupported("ForwardLevelIterator::Prev()");
|
||||
valid_ = false;
|
||||
}
|
||||
bool Valid() const override {
|
||||
return valid_;
|
||||
}
|
||||
bool Valid() const override { return valid_; }
|
||||
void SeekToFirst() override {
|
||||
assert(file_iter_ != nullptr);
|
||||
if (!status_.ok()) {
|
||||
@@ -249,9 +247,7 @@ ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
immutable_status_.PermitUncheckedError();
|
||||
}
|
||||
|
||||
ForwardIterator::~ForwardIterator() {
|
||||
Cleanup(true);
|
||||
}
|
||||
ForwardIterator::~ForwardIterator() { Cleanup(true); }
|
||||
|
||||
void ForwardIterator::SVCleanup(DBImpl* db, SuperVersion* sv,
|
||||
bool background_purge_on_iterator_cleanup) {
|
||||
@@ -284,13 +280,13 @@ struct SVCleanupParams {
|
||||
SuperVersion* sv;
|
||||
bool background_purge_on_iterator_cleanup;
|
||||
};
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
// Used in PinnedIteratorsManager to release pinned SuperVersion
|
||||
void ForwardIterator::DeferredSVCleanup(void* arg) {
|
||||
auto d = reinterpret_cast<SVCleanupParams*>(arg);
|
||||
ForwardIterator::SVCleanup(
|
||||
d->db, d->sv, d->background_purge_on_iterator_cleanup);
|
||||
ForwardIterator::SVCleanup(d->db, d->sv,
|
||||
d->background_purge_on_iterator_cleanup);
|
||||
delete d;
|
||||
}
|
||||
|
||||
@@ -547,8 +543,7 @@ void ForwardIterator::Next() {
|
||||
assert(valid_);
|
||||
bool update_prev_key = false;
|
||||
|
||||
if (sv_ == nullptr ||
|
||||
sv_->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
if (sv_ == nullptr || sv_->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
std::string current_key = key().ToString();
|
||||
Slice old_key(current_key.data(), current_key.size());
|
||||
|
||||
@@ -578,7 +573,6 @@ void ForwardIterator::Next() {
|
||||
update_prev_key = true;
|
||||
}
|
||||
|
||||
|
||||
if (update_prev_key) {
|
||||
prev_key_.SetInternalKey(current_->key());
|
||||
is_prev_set_ = true;
|
||||
@@ -635,7 +629,7 @@ bool ForwardIterator::PrepareValue() {
|
||||
|
||||
assert(!current_->Valid());
|
||||
assert(!current_->status().ok());
|
||||
assert(current_ != mutable_iter_); // memtable iterator can't fail
|
||||
assert(current_ != mutable_iter_); // memtable iterator can't fail
|
||||
assert(immutable_status_.ok());
|
||||
|
||||
valid_ = false;
|
||||
@@ -950,11 +944,11 @@ bool ForwardIterator::NeedToSeekImmutable(const Slice& target) {
|
||||
}
|
||||
Slice prev_key = prev_key_.GetInternalKey();
|
||||
if (prefix_extractor_ && prefix_extractor_->Transform(target).compare(
|
||||
prefix_extractor_->Transform(prev_key)) != 0) {
|
||||
prefix_extractor_->Transform(prev_key)) != 0) {
|
||||
return true;
|
||||
}
|
||||
if (cfd_->internal_comparator().InternalKeyComparator::Compare(
|
||||
prev_key, target) >= (is_prev_inclusive_ ? 1 : 0)) {
|
||||
prev_key, target) >= (is_prev_inclusive_ ? 1 : 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -963,8 +957,8 @@ bool ForwardIterator::NeedToSeekImmutable(const Slice& target) {
|
||||
return false;
|
||||
}
|
||||
if (cfd_->internal_comparator().InternalKeyComparator::Compare(
|
||||
target, current_ == mutable_iter_ ? immutable_min_heap_.top()->key()
|
||||
: current_->key()) > 0) {
|
||||
target, current_ == mutable_iter_ ? immutable_min_heap_.top()->key()
|
||||
: current_->key()) > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1040,11 +1034,11 @@ uint32_t ForwardIterator::FindFileInRange(
|
||||
uint32_t left, uint32_t right) {
|
||||
auto cmp = [&](const FileMetaData* f, const Slice& k) -> bool {
|
||||
return cfd_->internal_comparator().InternalKeyComparator::Compare(
|
||||
f->largest.Encode(), k) < 0;
|
||||
f->largest.Encode(), k) < 0;
|
||||
};
|
||||
const auto &b = files.begin();
|
||||
return static_cast<uint32_t>(std::lower_bound(b + left,
|
||||
b + right, internal_key, cmp) - b);
|
||||
const auto& b = files.begin();
|
||||
return static_cast<uint32_t>(
|
||||
std::lower_bound(b + left, b + right, internal_key, cmp) - b);
|
||||
}
|
||||
|
||||
void ForwardIterator::DeleteIterator(InternalIterator* iter, bool is_arena) {
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include "rocksdb/comparator.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "memory/arena.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -35,6 +35,7 @@ class MinIterComparator {
|
||||
bool operator()(InternalIterator* a, InternalIterator* b) {
|
||||
return comparator_->Compare(a->key(), b->key()) > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const CompareInterface* comparator_;
|
||||
};
|
||||
@@ -92,8 +93,8 @@ class ForwardIterator : public InternalIterator {
|
||||
// either done immediately or deferred until this iterator is unpinned by
|
||||
// PinnedIteratorsManager.
|
||||
void SVCleanup();
|
||||
static void SVCleanup(
|
||||
DBImpl* db, SuperVersion* sv, bool background_purge_on_iterator_cleanup);
|
||||
static void SVCleanup(DBImpl* db, SuperVersion* sv,
|
||||
bool background_purge_on_iterator_cleanup);
|
||||
static void DeferredSVCleanup(void* arg);
|
||||
|
||||
void RebuildIterators(bool refresh_sv);
|
||||
@@ -107,9 +108,9 @@ class ForwardIterator : public InternalIterator {
|
||||
void UpdateCurrent();
|
||||
bool NeedToSeekImmutable(const Slice& internal_key);
|
||||
void DeleteCurrentIter();
|
||||
uint32_t FindFileInRange(
|
||||
const std::vector<FileMetaData*>& files, const Slice& internal_key,
|
||||
uint32_t left, uint32_t right);
|
||||
uint32_t FindFileInRange(const std::vector<FileMetaData*>& files,
|
||||
const Slice& internal_key, uint32_t left,
|
||||
uint32_t right);
|
||||
|
||||
bool IsOverUpperBound(const Slice& internal_key) const;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ int main() {
|
||||
int main() { return 0; }
|
||||
#else
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
@@ -281,8 +282,9 @@ struct StatsThread {
|
||||
}
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
double elapsed =
|
||||
std::chrono::duration_cast<std::chrono::duration<double> >(
|
||||
now - tlast).count();
|
||||
std::chrono::duration_cast<std::chrono::duration<double> >(now -
|
||||
tlast)
|
||||
.count();
|
||||
uint64_t w = ::stats.written.load();
|
||||
uint64_t r = ::stats.read.load();
|
||||
fprintf(stderr,
|
||||
|
||||
Loaded 100 of 576 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user