mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32fda17a2e | ||
|
|
2594970c39 | ||
|
|
1b4234f331 | ||
|
|
54ceceafb3 | ||
|
|
9ec602f5f7 | ||
|
|
6d1896f4ca | ||
|
|
98084910a7 | ||
|
|
fbd1c98255 | ||
|
|
c743289d81 | ||
|
|
2be165b291 | ||
|
|
0f4c98d305 | ||
|
|
c75f4faa9d | ||
|
|
56f497bb21 | ||
|
|
051c88dd79 | ||
|
|
071893eb27 | ||
|
|
7d7234a9c8 | ||
|
|
d641bb8fb0 | ||
|
|
e98b76dd9b | ||
|
|
4001e799ab | ||
|
|
9ae92f50b2 | ||
|
|
7c01d65344 | ||
|
|
8796934af7 | ||
|
|
5e2c796589 | ||
|
|
557748ff7b | ||
|
|
811ee21112 | ||
|
|
afad5bd1c5 | ||
|
|
6920cde893 | ||
|
|
e72ea485ed | ||
|
|
9c8ac144bd | ||
|
|
d4c45428af | ||
|
|
d3bfd33972 | ||
|
|
7efd9c25c4 | ||
|
|
50b8d29b9c | ||
|
|
5c858ddd20 | ||
|
|
e5b5f12b81 | ||
|
|
0ce258f9b3 | ||
|
|
ac0d93b08f | ||
|
|
1ae46094de | ||
|
|
8745f013ff | ||
|
|
e12270dfee | ||
|
|
bbd6a5a184 | ||
|
|
9498069fc9 | ||
|
|
f8061a237e | ||
|
|
16e225f70d | ||
|
|
a4955b39ac | ||
|
|
ae0ad719de | ||
|
|
b2a8016df1 | ||
|
|
c6654588bd | ||
|
|
2a6d0cde72 | ||
|
|
ee8bf2e41f | ||
|
|
f85df120f2 | ||
|
|
89e4c48820 | ||
|
|
b06ca5f860 | ||
|
|
4ea0ab3cc5 | ||
|
|
12767b3130 | ||
|
|
a9d512a76b | ||
|
|
d5a51d4de3 | ||
|
|
89f319c2df | ||
|
|
b50632920d | ||
|
|
b9a97181aa | ||
|
|
663afef884 | ||
|
|
32604e6601 | ||
|
|
9ab38c45ad | ||
|
|
08ab1d83ac | ||
|
|
f9b14be495 | ||
|
|
68f3eb746c | ||
|
|
e70020e4f6 | ||
|
|
7bedd94406 | ||
|
|
68a8e6b8fa | ||
|
|
e70ba4e40e | ||
|
|
2a282e5f54 | ||
|
|
d9cfaa2b16 | ||
|
|
9430333f84 |
No files matched your search
@@ -64,3 +64,7 @@ java/javadoc
|
||||
scan_build_report/
|
||||
t
|
||||
LOG
|
||||
|
||||
db_logs/
|
||||
tp2/
|
||||
fbcode/
|
||||
+7
-3
@@ -32,8 +32,8 @@ project(rocksdb)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format MM_dd_yyyy" OUTPUT_VARIABLE DATE)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format HH:mm:ss" OUTPUT_VARIABLE TIME)
|
||||
string(REGEX REPLACE "(..)_(..)_..(..).*" "\\1/\\2/\\3" DATE ${DATE})
|
||||
string(REGEX REPLACE "(..):(.....).*" " \\1:\\2" TIME ${TIME})
|
||||
string(REGEX REPLACE "(..)_(..)_..(..).*" "\\1/\\2/\\3" DATE "${DATE}")
|
||||
string(REGEX REPLACE "(..):(.....).*" " \\1:\\2" TIME "${TIME}")
|
||||
string(CONCAT GIT_DATE_TIME ${DATE} ${TIME})
|
||||
|
||||
find_package(Git)
|
||||
@@ -44,7 +44,7 @@ else()
|
||||
set(GIT_SHA 0)
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA ${GIT_SHA})
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_CURRENT_SOURCE_DIR}/util/build_version.cc)
|
||||
|
||||
@@ -264,6 +264,7 @@ set(SOURCES
|
||||
utilities/merge_operators/put.cc
|
||||
utilities/merge_operators/max.cc
|
||||
utilities/merge_operators/uint64add.cc
|
||||
utilities/option_change_migration/option_change_migration.cc
|
||||
utilities/options/options_util.cc
|
||||
utilities/persistent_cache/persistent_cache_tier.cc
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
@@ -356,6 +357,7 @@ set(TESTS
|
||||
db/db_test2.cc
|
||||
db/db_block_cache_test.cc
|
||||
db/db_bloom_filter_test.cc
|
||||
db/db_flush_test.cc
|
||||
db/db_iterator_test.cc
|
||||
db/db_sst_test.cc
|
||||
db/db_universal_compaction_test.cc
|
||||
@@ -420,6 +422,7 @@ set(TESTS
|
||||
util/options_test.cc
|
||||
util/rate_limiter_test.cc
|
||||
util/slice_transform_test.cc
|
||||
util/statistics_test.cc
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
@@ -430,6 +433,7 @@ set(TESTS
|
||||
utilities/geodb/geodb_test.cc
|
||||
utilities/memory/memory_test.cc
|
||||
utilities/merge_operators/string_append/stringappend_test.cc
|
||||
utilities/option_change_migration/option_change_migration_test.cc
|
||||
utilities/options/options_util_test.cc
|
||||
utilities/persistent_cache/hash_table_test.cc
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
|
||||
+19
-1
@@ -1,5 +1,23 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 4.11.2 (9/15/2016)
|
||||
### Bug fixes
|
||||
* Segfault when failing to open an SST file for read-ahead iterators.
|
||||
* WAL without data for all CFs is not deleted after recovery.
|
||||
|
||||
## 4.11.1 (8/30/2016)
|
||||
### Bug Fixes
|
||||
* Mitigate the regression bug of deadlock condition during recovery when options.max_successive_merges hits.
|
||||
* Fix data race condition related to hash index in block based table when putting indexes in the block cache.
|
||||
|
||||
## 4.11.0 (8/1/2016)
|
||||
### Public API Change
|
||||
* options.memtable_prefix_bloom_huge_page_tlb_size => memtable_huge_page_size. When it is set, RocksDB will try to allocate memory from huge page for memtable too, rather than just memtable bloom filter.
|
||||
|
||||
### New Features
|
||||
* A tool to migrate DB after options change. See include/rocksdb/utilities/option_change_migration.h.
|
||||
* Add ReadOptions.background_purge_on_iterator_cleanup. If true, we avoid file deletion when destorying iterators.
|
||||
|
||||
## 4.10.0 (7/5/2016)
|
||||
### Public API Change
|
||||
* options.memtable_prefix_bloom_bits changes to options.memtable_prefix_bloom_bits_ratio and deprecate options.memtable_prefix_bloom_probes
|
||||
* enum type CompressionType and PerfLevel changes from char to unsigned char. Value of all PerfLevel shift by one.
|
||||
|
||||
@@ -130,8 +130,12 @@ am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_AR_0 = @echo " AR " $@;
|
||||
am__v_AR_1 =
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
ifdef ROCKSDB_USE_LIBRADOS
|
||||
LIB_SOURCES += utilities/env_librados.cc
|
||||
LDFLAGS += -lrados
|
||||
endif
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
# detect what platform we're building on
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; "$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
@@ -212,10 +216,6 @@ default: all
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
|
||||
ifndef DISABLE_WARNING_AS_ERROR
|
||||
WARNING_FLAGS += -Werror
|
||||
endif
|
||||
|
||||
CFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers
|
||||
|
||||
@@ -274,8 +274,10 @@ TESTS = \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_dynamic_level_test \
|
||||
db_flush_test \
|
||||
db_inplace_update_test \
|
||||
db_iterator_test \
|
||||
db_options_test \
|
||||
db_sst_test \
|
||||
db_tailing_iter_test \
|
||||
db_universal_compaction_test \
|
||||
@@ -368,16 +370,19 @@ TESTS = \
|
||||
heap_test \
|
||||
compact_on_deletion_collector_test \
|
||||
compaction_job_stats_test \
|
||||
option_change_migration_test \
|
||||
transaction_test \
|
||||
ldb_cmd_test \
|
||||
iostats_context_test \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
compact_on_deletion_collector_test \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_sst_test \
|
||||
db_test \
|
||||
db_universal_compaction_test \
|
||||
fault_injection_test \
|
||||
@@ -613,7 +618,7 @@ check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| parallel -j$(J) --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
| build_tools/gnu_parallel -j$(J) --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0:
|
||||
@@ -628,7 +633,7 @@ valgrind_check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| parallel -j$(J) --joblog=LOG $$eta --gnu \
|
||||
| build_tools/gnu_parallel -j$(J) --joblog=LOG $$eta --gnu \
|
||||
'if [[ "{}" == "./"* ]] ; then $(DRIVER) {} >& t/valgrind_log-{/}; ' \
|
||||
'else {} >& t/valgrind_log-{/}; fi'
|
||||
|
||||
@@ -649,7 +654,7 @@ watch-log:
|
||||
check: all
|
||||
$(MAKE) gen_parallel_tests
|
||||
$(AM_V_GEN)if test "$(J)" != 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) T="$$t" TMPD=$(TMPD) check_0; \
|
||||
@@ -676,9 +681,15 @@ blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple blackbox
|
||||
python -u tools/db_crashtest.py blackbox
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple whitebox
|
||||
python -u tools/db_crashtest.py whitebox
|
||||
python -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD)
|
||||
python -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD)
|
||||
|
||||
asan_check:
|
||||
$(MAKE) clean
|
||||
@@ -703,7 +714,7 @@ ubsan_crash_test:
|
||||
valgrind_check: $(TESTS)
|
||||
$(MAKE) gen_parallel_tests
|
||||
$(AM_V_GEN)if test "$(J)" != 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) TMPD=$(TMPD) \
|
||||
@@ -725,10 +736,10 @@ parloop:
|
||||
for t in $(PAR_TEST); do \
|
||||
echo "===== Running $$t in parallel $(NUM_PAR)";\
|
||||
if [ $(db_test) -eq 1 ]; then \
|
||||
seq $(J) | v="$$t" parallel --gnu 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
seq $(J) | v="$$t" build_tools/gnu_parallel --gnu 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
'timeout 2m ./db_test --gtest_filter=$$v >> $$s/log-{} 2>1'; \
|
||||
else\
|
||||
seq $(J) | v="./$$t" parallel --gnu 's=$(TMPD)/rdb-{};' \
|
||||
seq $(J) | v="./$$t" build_tools/gnu_parallel --gnu 's=$(TMPD)/rdb-{};' \
|
||||
'export TEST_TMPDIR=$$s; timeout 10m $$v >> $$s/log-{} 2>1'; \
|
||||
fi; \
|
||||
ret_code=$$?; \
|
||||
@@ -749,7 +760,7 @@ test_names = \
|
||||
|
||||
parallel_check: $(TESTS)
|
||||
$(AM_V_GEN)if test "$(J)" > 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
echo Running in parallel $(J); \
|
||||
@@ -759,7 +770,7 @@ parallel_check: $(TESTS)
|
||||
ret_bad=0; \
|
||||
echo $(J);\
|
||||
echo Test Dir: $(TMPD); \
|
||||
seq $(J) | parallel --gnu 's=$(TMPD)/rdb-{}; rm -rf $$s; mkdir $$s'; \
|
||||
seq $(J) | build_tools/gnu_parallel --gnu 's=$(TMPD)/rdb-{}; rm -rf $$s; mkdir $$s'; \
|
||||
$(MAKE) PAR_TEST="$(shell $(test_names))" TMPD=$(TMPD) \
|
||||
J=$(J) db_test=1 parloop; \
|
||||
$(MAKE) PAR_TEST="$(filter-out db_test, $(TESTS))" \
|
||||
@@ -851,7 +862,7 @@ arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_family_test: db/column_family_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
column_family_test: db/column_family_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
table_properties_collector_test: db/table_properties_collector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -872,6 +883,9 @@ cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
option_change_migration_test: utilities/option_change_migration/option_change_migration_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
stringappend_test: utilities/merge_operators/string_append/stringappend_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -920,6 +934,9 @@ db_compaction_test: db/db_compaction_test.o db/db_test_util.o $(LIBOBJECTS) $(TE
|
||||
db_dynamic_level_test: db/db_dynamic_level_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_flush_test: db/db_flush_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_inplace_update_test: db/db_inplace_update_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -992,6 +1009,11 @@ spatial_db_test: utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNE
|
||||
env_mirror_test: utilities/env_mirror_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
ifdef ROCKSDB_USE_LIBRADOS
|
||||
env_librados_test: utilities/env_librados_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
endif
|
||||
|
||||
env_registry_test: utilities/env_registry_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1199,6 +1221,9 @@ iostats_context_test: util/iostats_context_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
persistent_cache_test: utilities/persistent_cache/persistent_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
statistics_test: util/statistics_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
@@ -1353,6 +1378,12 @@ rocksdbjava: $(java_libobjects)
|
||||
jclean:
|
||||
cd java;$(MAKE) clean;
|
||||
|
||||
jtest_compile: rocksdbjava
|
||||
cd java;$(MAKE) java_test
|
||||
|
||||
jtest_run:
|
||||
cd java;$(MAKE) run_test
|
||||
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample;$(MAKE) test;
|
||||
|
||||
@@ -1361,7 +1392,7 @@ jdb_bench:
|
||||
|
||||
commit_prereq: build_tools/rocksdb-lego-determinator \
|
||||
build_tools/precommit_checker.py
|
||||
J=$(J) build_tools/precommit_checker.py unit unit_481 clang_unit tsan asan ubsan lite
|
||||
J=$(J) build_tools/precommit_checker.py unit unit_481 clang_unit release release_481 clang_release tsan asan ubsan lite unit_non_shm
|
||||
$(MAKE) clean && $(MAKE) jclean && $(MAKE) rocksdbjava;
|
||||
|
||||
xfunc:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
It is built on earlier work on LevelDB by Sanjay Ghemawat (sanjay@google.com)
|
||||
|
||||
@@ -75,9 +75,10 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
);
|
||||
|
||||
// arc demands certain permission on its config.
|
||||
// also fix the sticky bit issue in sandcastle
|
||||
$fix_permission = array(
|
||||
"name" => "Fix environment",
|
||||
"shell" => "chmod 600 ~/.arcrc",
|
||||
"shell" => "chmod 600 ~/.arcrc && chmod +t /dev/shm",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
@@ -115,7 +116,7 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
}
|
||||
|
||||
// Run the actual command.
|
||||
$cmd = $cmd . "./build_tools/precommit_checker.py " . $test
|
||||
$cmd = $cmd . "J=$(nproc) ./build_tools/precommit_checker.py " . $test
|
||||
. "; exit_code=$?; ";
|
||||
|
||||
if ($applyDiff) {
|
||||
@@ -134,6 +135,7 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
"name" => "Run " . $test,
|
||||
"shell" => $cmd,
|
||||
"user" => "root",
|
||||
"parser" => "python build_tools/error_filter.py " . $test,
|
||||
);
|
||||
|
||||
$steps[] = $run_test;
|
||||
@@ -223,8 +225,8 @@ function getSandcastleConfig() {
|
||||
} else {
|
||||
// Manually list of tests we want to run in Sandcastle.
|
||||
$tests = array(
|
||||
"unit", "unit_481", "clang_unit", "tsan", "asan", "lite_test",
|
||||
"valgrind", "release"
|
||||
"unit", "unit_non_shm", "unit_481", "clang_unit", "tsan", "asan",
|
||||
"lite_test", "valgrind", "release", "release_481", "clang_release"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,12 +52,7 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
fi
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
@@ -288,7 +283,7 @@ EOF
|
||||
# Test whether numa is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null <<EOF
|
||||
#include <numa.h>
|
||||
#inlcude <numaif.h>
|
||||
#include <numaif.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
|
||||
+19
-16
@@ -1,16 +1,19 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/ebc96bc2fb751b5a0300b8d91a95bdf24ac1d88b/4.9.x/centos6-native/108cf83
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/b91de48a4974ec839946d824402b098d43454cef/stable/centos6-native/7aaccbe
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/53e0eac8911888a105aa98b9a35fe61cf1d8b278/4.9.x/gcc-4.9-glibc-2.20/024dbc3
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/ee36ac9a72dfac4a995f1b215bb4c0fc8a0f6f91/2.20/gcc-4.9-glibc-2.20/500e281
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.9-glibc-2.20/e9936bf
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2b24f1e99454f9ca7b0301720f94836dae1bf71b/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/af7c14c9b652cdd5ec34eadd25c3f38a9b306c5d/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/1408484d03b15492aa54b10356104e9dc22e1cc5/0.6.1/gcc-5-glibc-2.23/9bc6787
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/5a5c7a6608cb32f1e1e7f814023d5bdfbd136370/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/a4c2adecffcaa68d5585d06be2252e3efa52555b/master/gcc-5-glibc-2.23/1c32b4b
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/1abc0d3c01743b854676423cf2d3629912f34930/2.0.8/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-5-glibc-2.23/b1847cb
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/f24be37d170e04be6e469af487644d4d62e1c6c1/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/9d65c666b9adf8f2a989fd4b98a9a5e7d3afa233/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/9cee5a3628dc9d4b93897972c58eba865e25b270/3.10.0/gcc-4.9-glibc-2.20/e9936bf
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/963d9aeda70cc4779885b1277484fe7544a04e3e/9.0.0/platform007/9e92d53/
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/ab9f09bba370e7066cafd4eb59752db93f2e8312/2.29.1/platform007/15a3614
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -119,7 +119,9 @@ _TEST_NAME_TO_PARSERS = {
|
||||
'unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'release': [CompilerErrorParser, GTestErrorParser],
|
||||
'unit_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'release_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_release': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_analyze': [CompilerErrorParser, ScanBuildErrorParser],
|
||||
'code_cov': [CompilerErrorParser, GTestErrorParser],
|
||||
'unity': [CompilerErrorParser, GTestErrorParser],
|
||||
|
||||
@@ -13,7 +13,7 @@ source "$BASEDIR/dependencies.sh"
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
@@ -43,12 +43,16 @@ if test -z $PIC_BUILD; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
@@ -56,7 +60,7 @@ if test -z $PIC_BUILD; then
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=google"
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
@@ -72,13 +76,22 @@ if test -z $PIC_BUILD; then
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -95,8 +108,8 @@ if [ -z "$USE_CLANG" ]; then
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
@@ -107,8 +120,8 @@ else
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.9.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.9.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
@@ -119,18 +132,21 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.9-glibc-2.20/lib/ld.so"
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.9-glibc-2.20/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS"
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
Executable
+7936
File diff suppressed because it is too large.
Load diff
@@ -94,7 +94,7 @@ class PreCommitChecker(Env):
|
||||
#
|
||||
def get_commands(self, test):
|
||||
status, out = self.GetOutput(
|
||||
"build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
"RATIO=1 build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
return status, out
|
||||
|
||||
#
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# Input Value
|
||||
# -------------------------------------------------------------------------
|
||||
# EMAIL Email address to report on trigger conditions
|
||||
# ONCAL Email address to raise a task on failure
|
||||
# ONCALL Email address to raise a task on failure
|
||||
# TRIGGER Trigger conditions for email. Valid values are fail, warn, all
|
||||
# SUBSCRIBER Email addresss to add as subscriber for task
|
||||
#
|
||||
@@ -42,6 +42,9 @@ if [ ! -z $ONCALL ]; then
|
||||
},"
|
||||
fi
|
||||
|
||||
# For now, create the tasks using only the dedicated task creation tool.
|
||||
CREATE_TASK=
|
||||
|
||||
REPORT=
|
||||
if [[ ! -z $REPORT_EMAIL || ! -z $CREATE_TASK ]]; then
|
||||
REPORT="'report': [
|
||||
@@ -56,10 +59,26 @@ fi
|
||||
CLEANUP_ENV="
|
||||
{
|
||||
'name':'Cleanup environment',
|
||||
'shell':'rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && make clean',
|
||||
'shell':'rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && chmod +t /dev/shm && make clean',
|
||||
'user':'root'
|
||||
}"
|
||||
|
||||
# We will eventually set the RATIO to 1, but we want do this
|
||||
# in steps. RATIO=$(nproc) will make it work as J=1
|
||||
if [ -z $RATIO ]; then
|
||||
RATIO=$(nproc)
|
||||
fi
|
||||
|
||||
if [ -z $PARALLEL_J ]; then
|
||||
PARALLEL_J="J=$(expr $(nproc) / ${RATIO})"
|
||||
fi
|
||||
|
||||
if [ -z $PARALLEL_j ]; then
|
||||
PARALLEL_j="-j$(expr $(nproc) / ${RATIO})"
|
||||
fi
|
||||
|
||||
PARALLELISM="$PARALLEL_J $PARALLEL_j"
|
||||
|
||||
DEBUG="OPT=-g"
|
||||
SHM="TEST_TMPDIR=/dev/shm/rocksdb"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
@@ -73,6 +92,19 @@ HTTP_PROXY="https_proxy=http://fwdproxy.29.prn1:8080 http_proxy=http://fwdproxy.
|
||||
SETUP_JAVA_ENV="export $HTTP_PROXY; export JAVA_HOME=/usr/local/jdk-7u10-64/; export PATH=\$PATH:\$JAVA_HOME/bin"
|
||||
PARSER="'parser':'python build_tools/error_filter.py $1'"
|
||||
|
||||
CONTRUN_NAME="ROCKSDB_CONTRUN_NAME"
|
||||
|
||||
# This code is getting called under various scenarios. What we care about is to
|
||||
# understand when it's called from nightly contruns because in that case we'll
|
||||
# create tasks for any failures. To follow the existing pattern, we'll check
|
||||
# the value of $ONCALL. If it's a diff then just call `false` to make sure
|
||||
# that errors will be properly propagated to the caller.
|
||||
if [ ! -z $ONCALL ]; then
|
||||
TASK_CREATION_TOOL="/usr/local/bin/mysql_mtr_filter --rocksdb"
|
||||
else
|
||||
TASK_CREATION_TOOL="false"
|
||||
fi
|
||||
|
||||
ARTIFACTS=" 'artifacts': [
|
||||
{
|
||||
'name':'database',
|
||||
@@ -110,14 +142,14 @@ PARALLEL_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || cat t/log-*',
|
||||
'shell':'$DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || $CONTRUN_NAME=punit_check $TASK_CREATION_TOOL || cat t/log-*',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version under gcc-4.8.1',
|
||||
'shell':'$GCC_481 $DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || cat t/log-*',
|
||||
'shell':'$GCC_481 $DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || $CONTRUN_NAME=punit_check_gcc481 $TASK_CREATION_TOOL || cat t/log-*',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -137,7 +169,29 @@ UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $DEBUG make J=1 check',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test not under /dev/shm
|
||||
#
|
||||
UNIT_TEST_NON_SHM_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'timeout': 86400,
|
||||
'shell':'$DEBUG make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -149,15 +203,15 @@ UNIT_TEST_COMMANDS="[
|
||||
#
|
||||
# RocksDB release build and unit tests
|
||||
#
|
||||
RELEASE_UNIT_TEST_COMMANDS="[
|
||||
RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Release Unit Test',
|
||||
'name':'Rocksdb Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $DEBUG make J=1 release',
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'make $PARALLEL_j release || $CONTRUN_NAME=release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -177,7 +231,27 @@ UNIT_TEST_COMMANDS_481="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $GCC_481 $DEBUG make J=1 check',
|
||||
'shell':'$SHM $GCC_481 $DEBUG make $PARALLELISM check || $CONTRUN_NAME=unit_gcc_481_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build and unit tests
|
||||
#
|
||||
RELEASE_BUILD_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Release on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release on GCC 4.8.1',
|
||||
'shell':'$GCC_481 make $PARALLEL_j release || $CONTRUN_NAME=release_gcc481 $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -197,7 +271,27 @@ CLANG_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug',
|
||||
'shell':'$CLANG $SHM $DEBUG make J=1 check',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=clang_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build with CLANG
|
||||
#
|
||||
CLANG_RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb CLANG Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'$CLANG make $PARALLEL_j release|| $CONTRUN_NAME=clang_release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -217,7 +311,7 @@ CLANG_ANALYZE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'RocksDB build and analyze',
|
||||
'shell':'$CLANG $SHM $DEBUG make J=1 analyze',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLEL_j analyze || $CONTRUN_NAME=clang_analyze $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -237,7 +331,7 @@ CODE_COV_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test and collect code coverage info',
|
||||
'shell':'$SHM $DEBUG make J=1 coverage',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM coverage || $CONTRUN_NAME=coverage $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -257,7 +351,7 @@ UNITY_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test unity test',
|
||||
'shell':'$SHM $DEBUG V=1 make J=1 unity_test',
|
||||
'shell':'$SHM $DEBUG V=1 make J=1 unity_test || $CONTRUN_NAME=unity_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -277,7 +371,7 @@ LITE_BUILD_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'$LITE make J=1 static_lib',
|
||||
'shell':'$LITE make J=1 static_lib || $CONTRUN_NAME=lite_static_lib $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -297,7 +391,7 @@ LITE_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'$SHM $LITE make J=1 check',
|
||||
'shell':'$SHM $LITE make J=1 check || $CONTRUN_NAME=lite_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -318,14 +412,14 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG make J=1 db_stress',
|
||||
'shell':'$SHM $DEBUG make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 crash_test || $CONTRUN_NAME=crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -346,7 +440,7 @@ WRITE_STRESS_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB write stress tests',
|
||||
'shell':'make write_stress && python tools/write_stress_runner.py --runtime_sec=3600 --db=/tmp/rocksdb_write_stress',
|
||||
'shell':'make write_stress && python tools/write_stress_runner.py --runtime_sec=3600 --db=/tmp/rocksdb_write_stress || $CONTRUN_NAME=write_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -368,7 +462,7 @@ ASAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under ASAN',
|
||||
'shell':'set -o pipefail && $SHM $ASAN $DEBUG make J=1 asan_check |& /usr/facebook/ops/scripts/asan_symbolize.py -d',
|
||||
'shell':'set -o pipefail && ($SHM $ASAN $DEBUG make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL) |& /usr/facebook/ops/scripts/asan_symbolize.py -d',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -390,7 +484,7 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 asan_crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -410,7 +504,7 @@ UBSAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under UBSAN',
|
||||
'shell':'set -o pipefail && $SHM $UBSAN $DEBUG make J=1 ubsan_check',
|
||||
'shell':'set -o pipefail && $SHM $UBSAN $DEBUG make $PARALLELISM ubsan_check || $CONTRUN_NAME=ubsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -432,7 +526,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 ubsan_crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -448,11 +542,13 @@ VALGRIND_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under valgrind',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit tests',
|
||||
'shell':'$DISABLE_JEMALLOC $SHM $DEBUG make valgrind_check',
|
||||
'timeout': 86400,
|
||||
'shell':'$DISABLE_JEMALLOC $SHM $DEBUG make $PARALLELISM valgrind_check || $CONTRUN_NAME=valgrind_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -468,11 +564,13 @@ TSAN_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make J=1 check',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make $PARALLELISM check || $CONTRUN_NAME=tsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -494,7 +592,7 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make J=1 crash_test',
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN CRASH_TEST_KILL_ODD=1887 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -503,8 +601,6 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
TSAN_CRASH_TEST_COMMANDS=$DISABLE_COMMANDS
|
||||
|
||||
#
|
||||
# RocksDB format compatible
|
||||
#
|
||||
@@ -558,7 +654,7 @@ FORMAT_COMPATIBLE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_format_compatible',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_format_compatible || $CONTRUN_NAME=run_format_compatible $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -591,7 +687,7 @@ NO_COMPRESSION_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_no_compression',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_no_compression || $CONTRUN_NAME=run_no_compression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -648,7 +744,7 @@ REGRESSION_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Make and run script',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_regression',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_regression || $CONTRUN_NAME=run_regression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -668,7 +764,7 @@ JAVA_BUILD_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB for Java',
|
||||
'shell':'$SETUP_JAVA_ENV; $SHM make rocksdbjava',
|
||||
'shell':'$SETUP_JAVA_ENV; $SHM make rocksdbjava || $CONTRUN_NAME=rocksdbjava $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -685,15 +781,24 @@ case $1 in
|
||||
unit)
|
||||
echo $UNIT_TEST_COMMANDS
|
||||
;;
|
||||
unit_non_shm)
|
||||
echo $UNIT_TEST_NON_SHM_COMMANDS
|
||||
;;
|
||||
release)
|
||||
echo $RELEASE_UNIT_TEST_COMMANDS
|
||||
echo $RELEASE_BUILD_COMMANDS
|
||||
;;
|
||||
unit_481)
|
||||
echo $UNIT_TEST_COMMANDS_481
|
||||
;;
|
||||
release_481)
|
||||
echo $RELEASE_BUILD_COMMANDS_481
|
||||
;;
|
||||
clang_unit)
|
||||
echo $CLANG_UNIT_TEST_COMMANDS
|
||||
;;
|
||||
clang_release)
|
||||
echo $CLANG_RELEASE_BUILD_COMMANDS
|
||||
;;
|
||||
clang_analyze)
|
||||
echo $CLANG_ANALYZE_COMMANDS
|
||||
;;
|
||||
|
||||
@@ -269,33 +269,31 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool FullMerge(const Slice& key, const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
size_t n = operand_list.size();
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
size_t n = merge_in.operand_list.size();
|
||||
std::vector<const char*> operand_pointers(n);
|
||||
std::vector<size_t> operand_sizes(n);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
Slice operand(operand_list[i]);
|
||||
Slice operand(merge_in.operand_list[i]);
|
||||
operand_pointers[i] = operand.data();
|
||||
operand_sizes[i] = operand.size();
|
||||
}
|
||||
|
||||
const char* existing_value_data = nullptr;
|
||||
size_t existing_value_len = 0;
|
||||
if (existing_value != nullptr) {
|
||||
existing_value_data = existing_value->data();
|
||||
existing_value_len = existing_value->size();
|
||||
if (merge_in.existing_value != nullptr) {
|
||||
existing_value_data = merge_in.existing_value->data();
|
||||
existing_value_len = merge_in.existing_value->size();
|
||||
}
|
||||
|
||||
unsigned char success;
|
||||
size_t new_value_len;
|
||||
char* tmp_new_value = (*full_merge_)(
|
||||
state_, key.data(), key.size(), existing_value_data, existing_value_len,
|
||||
&operand_pointers[0], &operand_sizes[0], static_cast<int>(n), &success,
|
||||
&new_value_len);
|
||||
new_value->assign(tmp_new_value, new_value_len);
|
||||
state_, merge_in.key.data(), merge_in.key.size(), existing_value_data,
|
||||
existing_value_len, &operand_pointers[0], &operand_sizes[0],
|
||||
static_cast<int>(n), &success, &new_value_len);
|
||||
merge_out->new_value.assign(tmp_new_value, new_value_len);
|
||||
|
||||
if (delete_value_ != nullptr) {
|
||||
(*delete_value_)(state_, tmp_new_value, new_value_len);
|
||||
@@ -1799,9 +1797,9 @@ void rocksdb_options_set_memtable_prefix_bloom_size_ratio(
|
||||
opt->rep.memtable_prefix_bloom_size_ratio = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_prefix_bloom_huge_page_tlb_size(
|
||||
rocksdb_options_t* opt, size_t v) {
|
||||
opt->rep.memtable_prefix_bloom_huge_page_tlb_size = v;
|
||||
void rocksdb_options_set_memtable_huge_page_size(rocksdb_options_t* opt,
|
||||
size_t v) {
|
||||
opt->rep.memtable_huge_page_size = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_hash_skip_list_rep(
|
||||
|
||||
+9
-5
@@ -558,8 +558,9 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"(waiting for flush), max_write_buffer_number is set to %d",
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number);
|
||||
} else if (vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::LEVEL0_NUM_FILES_TOTAL, 1);
|
||||
if (compaction_picker_->IsLevel0CompactionInProgress()) {
|
||||
@@ -569,7 +570,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
compaction_needed_bytes >=
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
@@ -594,7 +596,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number,
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
write_controller_token_ =
|
||||
@@ -611,7 +614,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"rate %" PRIu64,
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count(),
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
vstorage->estimated_compaction_needed_bytes() >=
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) {
|
||||
write_controller_token_ =
|
||||
|
||||
+377
-3
@@ -18,6 +18,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/options_parser.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -500,6 +501,135 @@ TEST_F(ColumnFamilyTest, DontReuseColumnFamilyID) {
|
||||
}
|
||||
}
|
||||
|
||||
class FlushEmptyCFTestWithParam : public ColumnFamilyTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
FlushEmptyCFTestWithParam() { allow_2pc_ = GetParam(); }
|
||||
|
||||
// Required if inheriting from testing::WithParamInterface<>
|
||||
static void SetUpTestCase() {}
|
||||
static void TearDownTestCase() {}
|
||||
|
||||
bool allow_2pc_;
|
||||
};
|
||||
|
||||
TEST_P(FlushEmptyCFTestWithParam, FlushEmptyCFTest) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
db_options_.env = fault_env.get();
|
||||
db_options_.allow_2pc = allow_2pc_;
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
// Generate log file A.
|
||||
ASSERT_OK(Put(1, "foo", "v1")); // seqID 1
|
||||
|
||||
Reopen();
|
||||
// Log file A is not dropped after reopening because default column family's
|
||||
// min log number is 0.
|
||||
// It flushes to SST file X
|
||||
ASSERT_OK(Put(1, "foo", "v1")); // seqID 2
|
||||
ASSERT_OK(Put(1, "bar", "v2")); // seqID 3
|
||||
// Current log file is file B now. While flushing, a new log file C is created
|
||||
// and is set to current. Boths' min log number is set to file C in memory, so
|
||||
// after flushing file B is deleted. At the same time, the min log number of
|
||||
// default CF is not written to manifest. Log file A still remains.
|
||||
// Flushed to SST file Y.
|
||||
Flush(1);
|
||||
Flush(0);
|
||||
ASSERT_OK(Put(1, "bar", "v3")); // seqID 4
|
||||
ASSERT_OK(Put(1, "foo", "v4")); // seqID 5
|
||||
|
||||
// Preserve file system state up to here to simulate a crash condition.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
std::vector<std::string> names;
|
||||
for (auto name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
fault_env->ResetState();
|
||||
|
||||
// Before opening, there are four files:
|
||||
// Log file A contains seqID 1
|
||||
// Log file C contains seqID 4, 5
|
||||
// SST file X contains seqID 1
|
||||
// SST file Y contains seqID 2, 3
|
||||
// Min log number:
|
||||
// default CF: 0
|
||||
// CF one, two: C
|
||||
// When opening the DB, all the seqID should be preserved.
|
||||
Open(names, {});
|
||||
ASSERT_EQ("v4", Get(1, "foo"));
|
||||
ASSERT_EQ("v3", Get(1, "bar"));
|
||||
Close();
|
||||
|
||||
db_options_.env = env_;
|
||||
}
|
||||
|
||||
TEST_P(FlushEmptyCFTestWithParam, FlushEmptyCFTest2) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
db_options_.env = fault_env.get();
|
||||
db_options_.allow_2pc = allow_2pc_;
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
// Generate log file A.
|
||||
ASSERT_OK(Put(1, "foo", "v1")); // seqID 1
|
||||
|
||||
Reopen();
|
||||
// Log file A is not dropped after reopening because default column family's
|
||||
// min log number is 0.
|
||||
// It flushes to SST file X
|
||||
ASSERT_OK(Put(1, "foo", "v1")); // seqID 2
|
||||
ASSERT_OK(Put(1, "bar", "v2")); // seqID 3
|
||||
// Current log file is file B now. While flushing, a new log file C is created
|
||||
// and is set to current. Both CFs' min log number is set to file C so after
|
||||
// flushing file B is deleted. Log file A still remains.
|
||||
// Flushed to SST file Y.
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(0, "bar", "v2")); // seqID 4
|
||||
ASSERT_OK(Put(2, "bar", "v2")); // seqID 5
|
||||
ASSERT_OK(Put(1, "bar", "v3")); // seqID 6
|
||||
// Flushing all column families. This forces all CFs' min log to current. This
|
||||
// is written to the manifest file. Log file C is cleared.
|
||||
Flush(0);
|
||||
Flush(1);
|
||||
Flush(2);
|
||||
// Write to log file D
|
||||
ASSERT_OK(Put(1, "bar", "v4")); // seqID 7
|
||||
ASSERT_OK(Put(1, "bar", "v5")); // seqID 8
|
||||
// Preserve file system state up to here to simulate a crash condition.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
std::vector<std::string> names;
|
||||
for (auto name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
fault_env->ResetState();
|
||||
// Before opening, there are two logfiles:
|
||||
// Log file A contains seqID 1
|
||||
// Log file D contains seqID 7, 8
|
||||
// Min log number:
|
||||
// default CF: D
|
||||
// CF one, two: D
|
||||
// When opening the DB, log file D should be replayed using the seqID
|
||||
// specified in the file.
|
||||
Open(names, {});
|
||||
ASSERT_EQ("v1", Get(1, "foo"));
|
||||
ASSERT_EQ("v5", Get(1, "bar"));
|
||||
Close();
|
||||
|
||||
db_options_.env = env_;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FlushEmptyCFTestWithParam, FlushEmptyCFTestWithParam,
|
||||
::testing::Bool());
|
||||
|
||||
TEST_F(ColumnFamilyTest, AddDrop) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two", "three"});
|
||||
@@ -782,6 +912,38 @@ TEST_F(ColumnFamilyTest, LogDeletionTest) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, CrashAfterFlush) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
db_options_.env = fault_env.get();
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
|
||||
WriteBatch batch;
|
||||
batch.Put(handles_[0], Slice("foo"), Slice("bar"));
|
||||
batch.Put(handles_[1], Slice("foo"), Slice("bar"));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
Flush(0);
|
||||
fault_env->SetFilesystemActive(false);
|
||||
|
||||
std::vector<std::string> names;
|
||||
for (auto name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
fault_env->DropUnsyncedFileData();
|
||||
fault_env->ResetState();
|
||||
Open(names, {});
|
||||
|
||||
// Write batch should be atomic.
|
||||
ASSERT_EQ(Get(0, "foo"), Get(1, "foo"));
|
||||
|
||||
Close();
|
||||
db_options_.env = env_;
|
||||
}
|
||||
|
||||
// Makes sure that obsolete log files get deleted
|
||||
TEST_F(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
// disable flushing stale column families
|
||||
@@ -2251,6 +2413,7 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
mutable_cf_options.level0_stop_writes_trigger = 10000;
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit = 200;
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2397,16 +2560,17 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
vstorage->set_l0_delay_trigger_count(50);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(60);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
vstorage->set_l0_delay_trigger_count(70);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2414,7 +2578,6 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
vstorage->set_l0_delay_trigger_count(71);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2641,6 +2804,217 @@ TEST_F(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(ColumnFamilyTest, FlushCloseWALFiles) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
column_family_options_.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
// Block flush jobs from running
|
||||
test::SleepingBackgroundTask sleeping_task;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task,
|
||||
Env::Priority::HIGH);
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
WaitForFlush(1);
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(ColumnFamilyTest, IteratorCloseWALFile1) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
column_family_options_.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
// Create an iterator holding the current super version.
|
||||
Iterator* it = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
// A flush will make `it` hold the last reference of its super version.
|
||||
Flush(1);
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
// Flush jobs will close previous WAL files after finishing. By
|
||||
// block flush jobs from running, we trigger a condition where
|
||||
// the iterator destructor should close the WAL files.
|
||||
test::SleepingBackgroundTask sleeping_task;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task,
|
||||
Env::Priority::HIGH);
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
// Deleting the iterator will clear its super version, triggering
|
||||
// closing all files
|
||||
delete it;
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
WaitForFlush(1);
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, IteratorCloseWALFile2) {
|
||||
SpecialEnv env(Env::Default());
|
||||
// Allow both of flush and purge job to schedule.
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
column_family_options_.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
// Create an iterator holding the current super version.
|
||||
ReadOptions ro;
|
||||
ro.background_purge_on_iterator_cleanup = true;
|
||||
Iterator* it = db_->NewIterator(ro, handles_[1]);
|
||||
// A flush will make `it` hold the last reference of its super version.
|
||||
Flush(1);
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile2:0",
|
||||
"DBImpl::BGWorkPurge:start"},
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile2:2",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BGWorkPurge:end", "ColumnFamilyTest::IteratorCloseWALFile2:1"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
// Deleting the iterator will clear its super version, triggering
|
||||
// closing all files
|
||||
delete it;
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:0");
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:1");
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:2");
|
||||
WaitForFlush(1);
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE // TEST functions are not supported in lite
|
||||
TEST_F(ColumnFamilyTest, ForwardIteratorCloseWALFile) {
|
||||
SpecialEnv env(Env::Default());
|
||||
// Allow both of flush and purge job to schedule.
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
column_family_options_.memtable_factory.reset(new SpecialSkipListFactory(3));
|
||||
column_family_options_.level0_file_num_compaction_trigger = 2;
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodar2", "mirko"));
|
||||
Flush(1);
|
||||
|
||||
// Create an iterator holding the current super version, as well as
|
||||
// the SST file just flushed.
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
ro.background_purge_on_iterator_cleanup = true;
|
||||
Iterator* it = db_->NewIterator(ro, handles_[1]);
|
||||
// A flush will make `it` hold the last reference of its super version.
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodar2", "mirko"));
|
||||
Flush(1);
|
||||
|
||||
WaitForCompaction();
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile2:0",
|
||||
"DBImpl::BGWorkPurge:start"},
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile2:2",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BGWorkPurge:end", "ColumnFamilyTest::IteratorCloseWALFile2:1"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
|
||||
env.delete_count_.store(0);
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
// Deleting the iterator will clear its super version, triggering
|
||||
// closing all files
|
||||
it->Seek("");
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
ASSERT_EQ(0, env.delete_count_.load());
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:0");
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:1");
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
ASSERT_EQ(1, env.delete_count_.load());
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:2");
|
||||
WaitForFlush(1);
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
ASSERT_EQ(1, env.delete_count_.load());
|
||||
|
||||
delete it;
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Disable on windows because SyncWAL requires env->IsSyncThreadSafe()
|
||||
// to return true which is not so in unbuffered mode.
|
||||
#ifndef OS_WIN
|
||||
|
||||
@@ -49,6 +49,12 @@ CompactionIterator::CompactionIterator(
|
||||
} else {
|
||||
ignore_snapshots_ = false;
|
||||
}
|
||||
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
CompactionIterator::~CompactionIterator() {
|
||||
// input_ Iteartor lifetime is longer than pinned_iters_mgr_ lifetime
|
||||
input_->SetPinnedItersMgr(nullptr);
|
||||
}
|
||||
|
||||
void CompactionIterator::ResetRecordCounts() {
|
||||
@@ -83,6 +89,8 @@ void CompactionIterator::Next() {
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
valid_ = true;
|
||||
} else {
|
||||
// We consumed all pinned merge operands, release pinned iterators
|
||||
pinned_iters_mgr_.ReleasePinnedIterators();
|
||||
// MergeHelper moves the iterator to the first record after the merged
|
||||
// records, so even though we reached the end of the merge output, we do
|
||||
// not want to advance the iterator.
|
||||
@@ -368,6 +376,7 @@ void CompactionIterator::NextFromInput() {
|
||||
return;
|
||||
}
|
||||
|
||||
pinned_iters_mgr_.StartPinning();
|
||||
// We know the merge type entry is not hidden, otherwise we would
|
||||
// have hit (A)
|
||||
// We encapsulate the merge related state machine in a different
|
||||
@@ -395,6 +404,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// batch consumed by the merge operator should not shadow any keys
|
||||
// coming after the merges
|
||||
has_current_user_key_ = false;
|
||||
pinned_iters_mgr_.ReleasePinnedIterators();
|
||||
}
|
||||
} else {
|
||||
valid_ = true;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "util/log_buffer.h"
|
||||
|
||||
@@ -46,6 +47,8 @@ class CompactionIterator {
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
LogBuffer* log_buffer = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
void ResetRecordCounts();
|
||||
|
||||
// Seek to the beginning of the compaction iterator output.
|
||||
@@ -136,6 +139,9 @@ class CompactionIterator {
|
||||
bool clear_and_output_next_key_ = false;
|
||||
|
||||
MergeOutputIterator merge_out_iter_;
|
||||
// PinnedIteratorsManager used to pin input_ Iterator blocks while reading
|
||||
// merge operands and then releasing them after consuming them.
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
std::string compaction_filter_value_;
|
||||
// "level_ptrs" holds indices that remember which file of an associated
|
||||
// level we were last checking during the last call to compaction->
|
||||
|
||||
@@ -390,6 +390,9 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
vstorage->GetOverlappingInputs(output_level, &smallest, &largest,
|
||||
&output_level_inputs->files, *parent_index,
|
||||
parent_index);
|
||||
if (!output_level_inputs->empty()) {
|
||||
ExpandWhileOverlapping(cf_name, vstorage, output_level_inputs);
|
||||
}
|
||||
|
||||
if (FilesInCompaction(output_level_inputs->files)) {
|
||||
return false;
|
||||
|
||||
@@ -621,9 +621,9 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys2) {
|
||||
// Overlapping user keys on same level and output level
|
||||
Add(1, 1U, "200", "400", 1000000000U);
|
||||
Add(1, 2U, "400", "500", 1U, 0, 0);
|
||||
Add(2, 3U, "400", "600", 1U);
|
||||
// The following file is not in the compaction despite overlapping user keys
|
||||
Add(2, 4U, "600", "700", 1U, 0, 0);
|
||||
Add(2, 3U, "000", "100", 1U);
|
||||
Add(2, 4U, "100", "600", 1U, 0, 0);
|
||||
Add(2, 5U, "600", "700", 1U, 0, 0);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -631,10 +631,12 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys2) {
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(4U, compaction->input(1, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 2)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys3) {
|
||||
@@ -666,6 +668,31 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys3) {
|
||||
ASSERT_EQ(7U, compaction->input(1, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys4) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000;
|
||||
|
||||
Add(1, 1U, "100", "150", 1U);
|
||||
Add(1, 2U, "150", "199", 1U, 0, 0);
|
||||
Add(1, 3U, "200", "250", 1100000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 1U, 0, 0);
|
||||
Add(1, 5U, "300", "350", 1U, 0, 0);
|
||||
|
||||
Add(2, 6U, "100", "115", 1U);
|
||||
Add(2, 7U, "125", "325", 1U);
|
||||
Add(2, 8U, "350", "400", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(7U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NotScheduleL1IfL0WithHigherPri1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
|
||||
@@ -270,16 +270,16 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksOfNewTableAddedToCache) {
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
|
||||
// Make sure index block is in cache.
|
||||
auto index_block_hit = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
auto index_block_hit = TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT);
|
||||
value = Get(1, "key");
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(index_block_hit + 1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
|
||||
value = Get(1, "key");
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(index_block_hit + 2,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
|
||||
@@ -649,6 +649,72 @@ TEST_F(DBCompactionTest, MinorCompactionsHappen) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, UserKeyCrossFile1) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// create first file and flush to l0
|
||||
Put("4", "A");
|
||||
Put("3", "A");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
Put("2", "A");
|
||||
Delete("3");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
// move both files down to l1
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Put("2", "B");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, UserKeyCrossFile2) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// create first file and flush to l0
|
||||
Put("4", "A");
|
||||
Put("3", "A");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
Put("2", "A");
|
||||
SingleDelete("3");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
// move both files down to l1
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Put("2", "B");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ZeroSeqIdCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same 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 "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBFlushTest : public DBTestBase {
|
||||
public:
|
||||
DBFlushTest() : DBTestBase("/db_flush_test") {}
|
||||
};
|
||||
|
||||
// We had issue when two background threads trying to flush at the same time,
|
||||
// only one of them get committed. The test verifies the issue is fixed.
|
||||
TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_background_flushes = 2;
|
||||
Reopen(options);
|
||||
FlushOptions no_wait;
|
||||
no_wait.wait = false;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"VersionSet::LogAndApply:WriteManifest",
|
||||
"DBFlushTest::FlushWhileWritingManifest:1"},
|
||||
{"MemTableList::InstallMemtableFlushResults:InProgress",
|
||||
"VersionSet::LogAndApply:WriteManifestDone"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("foo", "v"));
|
||||
ASSERT_OK(dbfull()->Flush(no_wait));
|
||||
TEST_SYNC_POINT("DBFlushTest::FlushWhileWritingManifest:1");
|
||||
ASSERT_OK(Put("bar", "v"));
|
||||
ASSERT_OK(dbfull()->Flush(no_wait));
|
||||
// If the issue is hit we will wait here forever.
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(2, TotalTableFiles());
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+132
-44
@@ -716,6 +716,16 @@ uint64_t DBImpl::FindMinLogContainingOutstandingPrep() {
|
||||
return min_log;
|
||||
}
|
||||
|
||||
void DBImpl::ScheduleBgLogWriterClose(JobContext* job_context) {
|
||||
if (!job_context->logs_to_free.empty()) {
|
||||
for (auto l : job_context->logs_to_free) {
|
||||
AddToLogsToFreeQueue(l);
|
||||
}
|
||||
job_context->logs_to_free.clear();
|
||||
SchedulePurge();
|
||||
}
|
||||
}
|
||||
|
||||
// * Returns the list of live files in 'sst_live'
|
||||
// If it's doing full scan:
|
||||
// * Returns the list of all files in the filesystem in
|
||||
@@ -1478,9 +1488,11 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
}
|
||||
|
||||
recovered_sequence = sequence;
|
||||
bool no_prev_seq = true;
|
||||
if (*next_sequence == kMaxSequenceNumber) {
|
||||
*next_sequence = sequence;
|
||||
} else {
|
||||
no_prev_seq = false;
|
||||
WriteBatchInternal::SetSequence(&batch, *next_sequence);
|
||||
}
|
||||
|
||||
@@ -1563,10 +1575,24 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// insert. We don't want to fail the whole write batch in that case --
|
||||
// we just ignore the update.
|
||||
// That's why we set ignore missing column families to true
|
||||
bool has_valid_writes = false;
|
||||
// If we pass DB through and options.max_successive_merges is hit
|
||||
// during recovery, Get() will be issued which will try to acquire
|
||||
// DB mutex and cause deadlock, as DB mutex is already held.
|
||||
// The DB pointer is not needed unless 2PC is used.
|
||||
// TODO(sdong) fix the allow_2pc case too.
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
&batch, column_family_memtables_.get(), &flush_scheduler_, true,
|
||||
log_number, this, false /* concurrent_memtable_writes */,
|
||||
next_sequence);
|
||||
log_number, db_options_.allow_2pc ? this : nullptr,
|
||||
false /* concurrent_memtable_writes */, next_sequence,
|
||||
&has_valid_writes);
|
||||
// If it is the first log file and there is no column family updated
|
||||
// after replaying the file, this file may be a stale file. We ignore
|
||||
// sequence IDs from the file. Otherwise, if a newer stale log file that
|
||||
// has been deleted, the sequenceID may be wrong.
|
||||
if (no_prev_seq && !has_valid_writes) {
|
||||
*next_sequence = kMaxSequenceNumber;
|
||||
}
|
||||
MaybeIgnoreError(&status);
|
||||
if (!status.ok()) {
|
||||
// We are treating this as a failure while reading since we read valid
|
||||
@@ -1575,7 +1601,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!read_only) {
|
||||
if (has_valid_writes && !read_only) {
|
||||
// we can do this because this is called before client has access to the
|
||||
// DB and there is only a single thread operating on DB
|
||||
ColumnFamilyData* cfd;
|
||||
@@ -1675,7 +1701,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// recovered and should be ignored on next reincarnation.
|
||||
// Since we already recovered max_log_number, we want all logs
|
||||
// with numbers `<= max_log_number` (includes this one) to be ignored
|
||||
if (flushed) {
|
||||
if (flushed || cfd->mem()->GetFirstSequenceNumber() == 0) {
|
||||
edit->SetLogNumber(max_log_number + 1);
|
||||
}
|
||||
// we must mark the next log number as used, even though it's
|
||||
@@ -1771,6 +1797,49 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
mutex_.AssertHeld();
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
uint64_t current_log_number = logfile_number_;
|
||||
while (logs_.front().number < current_log_number &&
|
||||
logs_.front().getting_synced) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number < current_log_number; ++it) {
|
||||
auto& log = *it;
|
||||
assert(!log.getting_synced);
|
||||
log.getting_synced = true;
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
Status s;
|
||||
if (!logs_to_sync.empty()) {
|
||||
mutex_.Unlock();
|
||||
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
log->get_log_number());
|
||||
s = log->file()->Sync(db_options_.use_fsync);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = directories_.GetWalDir()->Fsync();
|
||||
}
|
||||
|
||||
mutex_.Lock();
|
||||
|
||||
// "number <= current_log_number - 1" is equivalent to
|
||||
// "number < current_log_number".
|
||||
MarkLogsSynced(current_log_number - 1, true, s);
|
||||
if (!s.ok()) {
|
||||
bg_error_ = s;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTableToOutputFile(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
bool* made_progress, JobContext* job_context, LogBuffer* log_buffer) {
|
||||
@@ -1792,13 +1861,30 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
|
||||
FileMetaData file_meta;
|
||||
|
||||
flush_job.PickMemTable();
|
||||
|
||||
Status s;
|
||||
if (logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 0 &&
|
||||
!db_options_.disableDataSync) {
|
||||
// If there are more than one column families, we need to make sure that
|
||||
// all the log files except the most recent one are synced. Otherwise if
|
||||
// the host crashes after flushing and before WAL is persistent, the
|
||||
// flushed SST may contain data from write batches whose updates to
|
||||
// other column families are missing.
|
||||
// SyncClosedLogs() may unlock and re-lock the db_mutex.
|
||||
s = SyncClosedLogs(job_context);
|
||||
}
|
||||
|
||||
// Within flush_job.Run, rocksdb may call event listener to notify
|
||||
// file creation and deletion.
|
||||
//
|
||||
// Note that flush_job.Run will unlock and lock the db_mutex,
|
||||
// and EventListener callback will be called when the db_mutex
|
||||
// is unlocked by the current thread.
|
||||
Status s = flush_job.Run(&file_meta);
|
||||
if (s.ok()) {
|
||||
s = flush_job.Run(&file_meta);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWorkWrapper(cfd, job_context,
|
||||
@@ -2288,20 +2374,6 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
bool DBImpl::NeedFlushOrCompaction(const MutableCFOptions& base_options,
|
||||
const MutableCFOptions& new_options) {
|
||||
return (base_options.disable_auto_compactions &&
|
||||
!new_options.disable_auto_compactions) ||
|
||||
base_options.level0_slowdown_writes_trigger <
|
||||
new_options.level0_slowdown_writes_trigger ||
|
||||
base_options.level0_stop_writes_trigger <
|
||||
new_options.level0_stop_writes_trigger ||
|
||||
base_options.soft_pending_compaction_bytes_limit <
|
||||
new_options.soft_pending_compaction_bytes_limit ||
|
||||
base_options.hard_pending_compaction_bytes_limit <
|
||||
new_options.hard_pending_compaction_bytes_limit;
|
||||
}
|
||||
|
||||
Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
@@ -2315,7 +2387,6 @@ Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
return Status::InvalidArgument("empty input");
|
||||
}
|
||||
|
||||
MutableCFOptions prev_options = *cfd->GetLatestMutableCFOptions();
|
||||
MutableCFOptions new_options;
|
||||
Status s;
|
||||
Status persist_options_status;
|
||||
@@ -2324,14 +2395,12 @@ Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
s = cfd->SetOptions(options_map);
|
||||
if (s.ok()) {
|
||||
new_options = *cfd->GetLatestMutableCFOptions();
|
||||
if (NeedFlushOrCompaction(prev_options, new_options)) {
|
||||
// Trigger possible flush/compactions. This has to be before we persist
|
||||
// options to file, otherwise there will be a deadlock with writer
|
||||
// thread.
|
||||
auto* old_sv =
|
||||
InstallSuperVersionAndScheduleWork(cfd, nullptr, new_options);
|
||||
delete old_sv;
|
||||
}
|
||||
// Trigger possible flush/compactions. This has to be before we persist
|
||||
// options to file, otherwise there will be a deadlock with writer
|
||||
// thread.
|
||||
auto* old_sv =
|
||||
InstallSuperVersionAndScheduleWork(cfd, nullptr, new_options);
|
||||
delete old_sv;
|
||||
|
||||
// Persist RocksDB options under the single write thread
|
||||
WriteThread::Writer w;
|
||||
@@ -2549,12 +2618,11 @@ Status DBImpl::SyncWAL() {
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:1");
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
MarkLogsSynced(current_log_number, need_log_dir_sync, status);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -2936,8 +3004,9 @@ void DBImpl::BGWorkCompaction(void* arg) {
|
||||
|
||||
void DBImpl::BGWorkPurge(void* db) {
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::HIGH);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkPurge");
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkPurge:start");
|
||||
reinterpret_cast<DBImpl*>(db)->BackgroundCallPurge();
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkPurge:end");
|
||||
}
|
||||
|
||||
void DBImpl::UnscheduleCallback(void* arg) {
|
||||
@@ -2952,20 +3021,32 @@ void DBImpl::UnscheduleCallback(void* arg) {
|
||||
void DBImpl::BackgroundCallPurge() {
|
||||
mutex_.Lock();
|
||||
|
||||
while (!purge_queue_.empty()) {
|
||||
auto purge_file = purge_queue_.begin();
|
||||
auto fname = purge_file->fname;
|
||||
auto type = purge_file->type;
|
||||
auto number = purge_file->number;
|
||||
auto path_id = purge_file->path_id;
|
||||
auto job_id = purge_file->job_id;
|
||||
purge_queue_.pop_front();
|
||||
// We use one single loop to clear both queues so that after existing the loop
|
||||
// both queues are empty. This is stricter than what is needed, but can make
|
||||
// it easier for us to reason the correctness.
|
||||
while (!purge_queue_.empty() || !logs_to_free_queue_.empty()) {
|
||||
if (!purge_queue_.empty()) {
|
||||
auto purge_file = purge_queue_.begin();
|
||||
auto fname = purge_file->fname;
|
||||
auto type = purge_file->type;
|
||||
auto number = purge_file->number;
|
||||
auto path_id = purge_file->path_id;
|
||||
auto job_id = purge_file->job_id;
|
||||
purge_queue_.pop_front();
|
||||
|
||||
mutex_.Unlock();
|
||||
Status file_deletion_status;
|
||||
DeleteObsoleteFileImpl(file_deletion_status, job_id, fname, type, number,
|
||||
path_id);
|
||||
mutex_.Lock();
|
||||
mutex_.Unlock();
|
||||
Status file_deletion_status;
|
||||
DeleteObsoleteFileImpl(file_deletion_status, job_id, fname, type, number,
|
||||
path_id);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
assert(!logs_to_free_queue_.empty());
|
||||
log::Writer* log_writer = *(logs_to_free_queue_.begin());
|
||||
logs_to_free_queue_.pop_front();
|
||||
mutex_.Unlock();
|
||||
delete log_writer;
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
bg_purge_scheduled_--;
|
||||
|
||||
@@ -3032,6 +3113,8 @@ void DBImpl::BackgroundCallFlush() {
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
assert(bg_flush_scheduled_);
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:start");
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -3274,7 +3357,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// NOTE: try to avoid unnecessary copy of MutableCFOptions if
|
||||
// compaction is not necessary. Need to make sure mutex is held
|
||||
// until we make a copy in the following code
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():BeforePickCompaction");
|
||||
c.reset(cfd->PickCompaction(*mutable_cf_options, log_buffer));
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():AfterPickCompaction");
|
||||
if (c != nullptr) {
|
||||
// update statistics
|
||||
MeasureTime(stats_, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
@@ -3601,6 +3686,9 @@ static void CleanupIteratorState(void* arg1, void* arg2) {
|
||||
state->mu->Lock();
|
||||
state->super_version->Cleanup();
|
||||
state->db->FindObsoleteFiles(&job_context, false, true);
|
||||
if (state->background_purge) {
|
||||
state->db->ScheduleBgLogWriterClose(&job_context);
|
||||
}
|
||||
state->mu->Unlock();
|
||||
|
||||
delete state->super_version;
|
||||
|
||||
+12
-5
@@ -365,6 +365,10 @@ class DBImpl : public DB {
|
||||
// compaction status.
|
||||
int BGCompactionsAllowed() const;
|
||||
|
||||
// move logs pending closing from job_context to the DB queue and
|
||||
// schedule a purge
|
||||
void ScheduleBgLogWriterClose(JobContext* job_context);
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
// of all files in the filesystem in 'candidate_files'.
|
||||
// If force == false and the last call was less than
|
||||
@@ -493,6 +497,9 @@ class DBImpl : public DB {
|
||||
|
||||
void MarkLogAsHavingPrepSectionFlushed(uint64_t log);
|
||||
void MarkLogAsContainingPrepSection(uint64_t log);
|
||||
void AddToLogsToFreeQueue(log::Writer* log_writer) {
|
||||
logs_to_free_queue_.push_back(log_writer);
|
||||
}
|
||||
|
||||
Status NewDB();
|
||||
|
||||
@@ -601,6 +608,8 @@ class DBImpl : public DB {
|
||||
// and blocked by any other pending_outputs_ calls)
|
||||
void ReleaseFileNumberFromPendingOutputs(std::list<uint64_t>::iterator v);
|
||||
|
||||
Status SyncClosedLogs(JobContext* job_context);
|
||||
|
||||
// Flush the in-memory write buffer to storage. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful.
|
||||
Status FlushMemTableToOutputFile(ColumnFamilyData* cfd,
|
||||
@@ -670,11 +679,6 @@ class DBImpl : public DB {
|
||||
Status BackgroundFlush(bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Compare options before and after to see whether flush or compaction is
|
||||
// needed immediately after dynamic option change.
|
||||
bool NeedFlushOrCompaction(const MutableCFOptions& base_options,
|
||||
const MutableCFOptions& new_options);
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
// dump rocksdb.stats to LOG
|
||||
@@ -882,6 +886,9 @@ class DBImpl : public DB {
|
||||
|
||||
// A queue to store filenames of the files to be purged
|
||||
std::deque<PurgeFileInfo> purge_queue_;
|
||||
|
||||
// A queue to store log writers to close
|
||||
std::deque<log::Writer*> logs_to_free_queue_;
|
||||
int unscheduled_flushes_;
|
||||
int unscheduled_compactions_;
|
||||
|
||||
|
||||
+31
-19
@@ -155,7 +155,9 @@ class DBIter: public Iterator {
|
||||
virtual Slice value() const override {
|
||||
assert(valid_);
|
||||
if (current_entry_is_merged_) {
|
||||
return saved_value_;
|
||||
// If pinned_value_ is set then the result of merge operator is one of
|
||||
// the merge operands and we should return it.
|
||||
return pinned_value_.data() ? pinned_value_ : saved_value_;
|
||||
} else if (direction_ == kReverse) {
|
||||
return pinned_value_;
|
||||
} else {
|
||||
@@ -286,9 +288,9 @@ inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
void DBIter::Next() {
|
||||
assert(valid_);
|
||||
|
||||
// Release temporarily pinned blocks from last operation
|
||||
ReleaseTempPinnedData();
|
||||
if (direction_ == kReverse) {
|
||||
// We only pin blocks when doing kReverse
|
||||
ReleaseTempPinnedData();
|
||||
FindNextUserKey();
|
||||
direction_ = kForward;
|
||||
if (!iter_->Valid()) {
|
||||
@@ -433,9 +435,12 @@ void DBIter::MergeValuesNewToOld() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Temporarily pin the blocks that hold merge operands
|
||||
TempPinData();
|
||||
merge_context_.Clear();
|
||||
// Start the merge process by pushing the first operand
|
||||
merge_context_.PushOperand(iter_->value());
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
|
||||
ParsedInternalKey ikey;
|
||||
for (iter_->Next(); iter_->Valid(); iter_->Next()) {
|
||||
@@ -459,15 +464,15 @@ void DBIter::MergeValuesNewToOld() {
|
||||
const Slice val = iter_->value();
|
||||
MergeHelper::TimedFullMerge(merge_operator_, ikey.user_key, &val,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
// iter_ is positioned after put
|
||||
iter_->Next();
|
||||
return;
|
||||
} else if (kTypeMerge == ikey.type) {
|
||||
// hit a merge, add the value as an operand and run associative merge.
|
||||
// when complete, add result to operands and continue.
|
||||
const Slice& val = iter_->value();
|
||||
merge_context_.PushOperand(val);
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
@@ -479,15 +484,15 @@ void DBIter::MergeValuesNewToOld() {
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
}
|
||||
|
||||
void DBIter::Prev() {
|
||||
assert(valid_);
|
||||
ReleaseTempPinnedData();
|
||||
if (direction_ == kForward) {
|
||||
ReverseToBackward();
|
||||
}
|
||||
ReleaseTempPinnedData();
|
||||
PrevInternal();
|
||||
if (statistics_ != nullptr) {
|
||||
local_stats_.prev_count_++;
|
||||
@@ -580,6 +585,9 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
|
||||
// Temporarily pin blocks that hold (merge operands / the value)
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
size_t num_skipped = 0;
|
||||
while (iter_->Valid() && ikey.sequence <= sequence_ &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
@@ -592,8 +600,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeValue:
|
||||
merge_context_.Clear();
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
assert(iter_->IsValuePinned());
|
||||
pinned_value_ = iter_->value();
|
||||
last_not_merge_type = kTypeValue;
|
||||
break;
|
||||
@@ -605,7 +612,8 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
break;
|
||||
case kTypeMerge:
|
||||
assert(merge_operator_ != nullptr);
|
||||
merge_context_.PushOperandBack(iter_->value());
|
||||
merge_context_.PushOperandBack(
|
||||
iter_->value(), iter_->IsValuePinned() /* operand_pinned */);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@@ -628,13 +636,14 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
if (last_not_merge_type == kTypeDeletion) {
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(),
|
||||
nullptr, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_);
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_);
|
||||
} else {
|
||||
assert(last_not_merge_type == kTypeValue);
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(),
|
||||
&pinned_value_,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
@@ -651,6 +660,9 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
// This function is used in FindValueForCurrentKey.
|
||||
// We use Seek() function instead of Prev() to find necessary value
|
||||
bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
// FindValueForCurrentKey will enable pinning before calling
|
||||
// FindValueForCurrentKeyUsingSeek()
|
||||
assert(pinned_iters_mgr_.PinningEnabled());
|
||||
std::string last_key;
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
@@ -664,8 +676,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeDeletion ||
|
||||
ikey.type == kTypeSingleDeletion) {
|
||||
if (ikey.type == kTypeValue) {
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
assert(iter_->IsValuePinned());
|
||||
pinned_value_ = iter_->value();
|
||||
valid_ = true;
|
||||
return true;
|
||||
@@ -681,7 +692,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
while (iter_->Valid() &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey()) &&
|
||||
ikey.type == kTypeMerge) {
|
||||
merge_context_.PushOperand(iter_->value());
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
iter_->Next();
|
||||
FindParseableKey(&ikey, kForward);
|
||||
}
|
||||
@@ -691,7 +703,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion) {
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
// Make iter_ valid and point to saved_key_
|
||||
if (!iter_->Valid() ||
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
@@ -705,7 +717,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
const Slice& val = iter_->value();
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), &val,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -150,6 +150,9 @@ class TestIterator : public InternalIterator {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual bool IsKeyPinned() const override { return true; }
|
||||
virtual bool IsValuePinned() const override { return true; }
|
||||
|
||||
private:
|
||||
bool initialized_;
|
||||
bool valid_;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "port/port.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -979,7 +980,7 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
|
||||
{
|
||||
// Test Seek to random keys
|
||||
printf("Testing seek on %zu keys\n", random_keys.size());
|
||||
printf("Testing seek on %" ROCKSDB_PRIszt " keys\n", random_keys.size());
|
||||
std::vector<Slice> keys_slices;
|
||||
std::vector<std::string> true_keys;
|
||||
for (auto& k : random_keys) {
|
||||
|
||||
+100
-70
@@ -6,6 +6,9 @@
|
||||
// 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 <limits>
|
||||
#include <string>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -17,80 +20,107 @@ class DBOptionsTest : public DBTestBase {
|
||||
DBOptionsTest() : DBTestBase("/db_options_test") {}
|
||||
};
|
||||
|
||||
// When write stalls, user can enable auto compaction to unblock writes.
|
||||
// However, we had an issue where the stalled write thread blocks the attempt
|
||||
// to persist auto compaction option, thus creating a deadlock. The test
|
||||
// verifies the issue is fixed.
|
||||
TEST_F(DBOptionsTest, EnableAutoCompactionToUnblockWrites) {
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 1000 * 1000; // 1M
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.level0_slowdown_writes_trigger = 1;
|
||||
options.level0_stop_writes_trigger = 1;
|
||||
options.compression = kNoCompression;
|
||||
// RocksDB lite don't support dynamic options.
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::DelayWrite:Wait",
|
||||
"DBOptionsTest::EnableAutoCompactionToUnblockWrites:1"},
|
||||
{"DBImpl::BackgroundCompaction:Finish",
|
||||
"DBOptionsTest::EnableAutoCompactionToUnblockWrites:1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TEST_F(DBOptionsTest, EnableAutoCompactionAndTriggerStall) {
|
||||
const std::string kValue(1024, 'v');
|
||||
for (int method_type = 0; method_type < 2; method_type++) {
|
||||
for (int option_type = 0; option_type < 4; option_type++) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 1024 * 1024;
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.level0_stop_writes_trigger = std::numeric_limits<int>::max();
|
||||
options.level0_slowdown_writes_trigger = std::numeric_limits<int>::max();
|
||||
options.hard_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
options.soft_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
|
||||
// Stall writes.
|
||||
Reopen(options);
|
||||
env_->StartThread(
|
||||
[](void* arg) {
|
||||
std::string value(1000, 'v');
|
||||
auto* t = static_cast<DBOptionsTest*>(arg);
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
ASSERT_OK(t->Put(t->Key(i), value));
|
||||
}
|
||||
},
|
||||
this);
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionToUnblockWrites:1");
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ColumnFamilyHandle* handle = dbfull()->DefaultColumnFamily();
|
||||
// We will get a deadlock here if we hit the issue.
|
||||
dbfull()->EnableAutoCompaction({handle});
|
||||
env_->WaitForJoin();
|
||||
DestroyAndReopen(options);
|
||||
for (int i = 0; i < 1024 * 2; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
uint64_t l0_size = SizeAtLevel(0);
|
||||
|
||||
switch (option_type) {
|
||||
case 0:
|
||||
// test with level0_stop_writes_trigger
|
||||
options.level0_stop_writes_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
break;
|
||||
case 1:
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
break;
|
||||
case 2:
|
||||
options.hard_pending_compaction_bytes_limit = l0_size;
|
||||
options.soft_pending_compaction_bytes_limit = l0_size;
|
||||
break;
|
||||
case 3:
|
||||
options.soft_pending_compaction_bytes_limit = l0_size;
|
||||
break;
|
||||
}
|
||||
Reopen(options);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBOptionsTest::EnableAutoCompactionAndTriggerStall:1",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"DBImpl::BackgroundCompaction():BeforePickCompaction",
|
||||
"DBOptionsTest::EnableAutoCompactionAndTriggerStall:2"},
|
||||
{"DBOptionsTest::EnableAutoCompactionAndTriggerStall:3",
|
||||
"DBImpl::BackgroundCompaction():AfterPickCompaction"}});
|
||||
// Block background compaction.
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
switch (method_type) {
|
||||
case 0:
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
break;
|
||||
case 1:
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction(
|
||||
{dbfull()->DefaultColumnFamily()}));
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:1");
|
||||
// Wait for stall condition recalculate.
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:2");
|
||||
|
||||
switch (option_type) {
|
||||
case 0:
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
break;
|
||||
case 1:
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
break;
|
||||
case 2:
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
break;
|
||||
case 3:
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:3");
|
||||
|
||||
// Background compaction executed.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to EnableAutoCompactionAfterStallDeadlock. See comments there.
|
||||
TEST_F(DBOptionsTest, ToggleStopTriggerToUnblockWrites) {
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 1000 * 1000; // 1M
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.level0_slowdown_writes_trigger = 1;
|
||||
options.level0_stop_writes_trigger = 1;
|
||||
options.compression = kNoCompression;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::DelayWrite:Wait",
|
||||
"DBOptionsTest::ToggleStopTriggerToUnblockWrites:1"},
|
||||
{"DBImpl::BackgroundCompaction:Finish",
|
||||
"DBOptionsTest::ToggleStopTriggerToUnblockWrites:1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Stall writes.
|
||||
Reopen(options);
|
||||
env_->StartThread(
|
||||
[](void* arg) {
|
||||
std::string value(1000, 'v');
|
||||
auto* t = static_cast<DBOptionsTest*>(arg);
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
ASSERT_OK(t->Put(t->Key(i), value));
|
||||
}
|
||||
},
|
||||
this);
|
||||
TEST_SYNC_POINT("DBOptionsTest::ToggleStopTriggerToUnblockWrites:1");
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
// We will get a deadlock here if we hit the issue.
|
||||
dbfull()->SetOptions({{"level0_stop_writes_trigger", "1000000"},
|
||||
{"level0_slowdown_writes_trigger", "1000000"}});
|
||||
env_->WaitForJoin();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
+6
-4
@@ -12,6 +12,7 @@
|
||||
#include "rocksdb/sst_file_manager.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "port/port.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -144,12 +145,13 @@ TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) {
|
||||
}
|
||||
};
|
||||
env_->table_write_callback_ = &block_first_time;
|
||||
// Create 1MB sst file
|
||||
// Insert 2.5MB data, which should trigger a flush because we exceed
|
||||
// write_buffer_size. The flush will be blocked with block_first_time
|
||||
// pending_file is protecting all the files created after
|
||||
for (int j = 0; j < 256; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 10 * 1024)));
|
||||
}
|
||||
// this should trigger a flush, which is blocked with block_first_time
|
||||
// pending_file is protecting all the files created after
|
||||
blocking_thread.WaitUntilSleeping();
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(2, nullptr, nullptr));
|
||||
|
||||
@@ -1455,7 +1457,7 @@ TEST_F(DBSSTTest, AddExternalSstFileOverlappingRanges) {
|
||||
}
|
||||
|
||||
printf(
|
||||
"Total: %zu ranges\n"
|
||||
"Total: %" ROCKSDB_PRIszt " ranges\n"
|
||||
"AddFile()|Success: %d ranges\n"
|
||||
"AddFile()|RangeConflict: %d ranges\n"
|
||||
"Put(): %d ranges\n",
|
||||
|
||||
@@ -138,14 +138,14 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 4096;
|
||||
options.max_write_buffer_number = 8;
|
||||
options.max_write_buffer_number = 3;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
options.level0_stop_writes_trigger = 4;
|
||||
options.target_file_size_base = 2048;
|
||||
options.max_bytes_for_level_base = 10240;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.soft_rate_limit = 1.1;
|
||||
options.hard_pending_compaction_bytes_limit = 16 * 1024;
|
||||
options.num_levels = 8;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
@@ -155,6 +155,12 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), RandomString(&rnd, 102)));
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
if (NumTableFilesAtLevel(0) == 0) {
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), RandomString(&rnd, 102)));
|
||||
Flush();
|
||||
}
|
||||
|
||||
db_->PauseBackgroundWork();
|
||||
|
||||
// Ensure that we have at least L0, L1 and L2
|
||||
|
||||
+81
-5
@@ -1189,6 +1189,12 @@ bool MinLevelToCompress(CompressionType& type, Options& options, int wbits,
|
||||
} else if (LZ4_Supported()) {
|
||||
type = kLZ4Compression;
|
||||
fprintf(stderr, "using lz4\n");
|
||||
} else if (XPRESS_Supported()) {
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTDNotFinalCompression;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
return false;
|
||||
@@ -4685,6 +4691,77 @@ TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, CompressionStatsTest) {
|
||||
CompressionType type;
|
||||
|
||||
if (Snappy_Supported()) {
|
||||
type = kSnappyCompression;
|
||||
fprintf(stderr, "using snappy\n");
|
||||
} else if (Zlib_Supported()) {
|
||||
type = kZlibCompression;
|
||||
fprintf(stderr, "using zlib\n");
|
||||
} else if (BZip2_Supported()) {
|
||||
type = kBZip2Compression;
|
||||
fprintf(stderr, "using bzip2\n");
|
||||
} else if (LZ4_Supported()) {
|
||||
type = kLZ4Compression;
|
||||
fprintf(stderr, "using lz4\n");
|
||||
} else if (XPRESS_Supported()) {
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTDNotFinalCompression;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = type;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.statistics->stats_level_ = StatsLevel::kAll;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int kNumKeysWritten = 100000;
|
||||
|
||||
// Check that compressions occur and are counted when compression is turned on
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED), 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED), 0);
|
||||
|
||||
options.compression = kNoCompression;
|
||||
DestroyAndReopen(options);
|
||||
uint64_t currentCompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
uint64_t currentDecompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED);
|
||||
|
||||
// Check that compressions do not occur when turned off
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MutexWaitStatsDisabledByDefault) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -4773,12 +4850,11 @@ class DelayedMergeOperator : public MergeOperator {
|
||||
|
||||
public:
|
||||
explicit DelayedMergeOperator(DBTest* d) : db_test_(d) {}
|
||||
virtual bool FullMerge(const Slice& key, const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
db_test_->env_->addon_time_.fetch_add(1000);
|
||||
*new_value = "";
|
||||
merge_out->new_value = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+374
-29
@@ -1210,6 +1210,37 @@ class PinL0IndexAndFilterBlocksTest : public DBTestBase,
|
||||
PinL0IndexAndFilterBlocksTest() : DBTestBase("/db_pin_l0_index_bloom_test") {}
|
||||
virtual void SetUp() override { infinite_max_files_ = GetParam(); }
|
||||
|
||||
void CreateTwoLevels(Options* options) {
|
||||
if (infinite_max_files_) {
|
||||
options->max_open_files = -1;
|
||||
}
|
||||
options->create_if_missing = true;
|
||||
options->statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options->table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, *options);
|
||||
|
||||
Put(1, "a", "begin");
|
||||
Put(1, "z", "end");
|
||||
ASSERT_OK(Flush(1));
|
||||
// move this table to L1
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
|
||||
// reset block cache
|
||||
table_options.block_cache = NewLRUCache(64 * 1024);
|
||||
options->table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, *options);
|
||||
// create new table at L0
|
||||
Put(1, "a2", "begin2");
|
||||
Put(1, "z2", "end2");
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
table_options.block_cache->EraseUnRefEntries();
|
||||
}
|
||||
|
||||
bool infinite_max_files_;
|
||||
};
|
||||
|
||||
@@ -1261,35 +1292,7 @@ TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
MultiLevelIndexAndFilterBlocksCachedWithPinning) {
|
||||
Options options = CurrentOptions();
|
||||
if (infinite_max_files_) {
|
||||
options.max_open_files = -1;
|
||||
}
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
Put(1, "a", "begin");
|
||||
Put(1, "z", "end");
|
||||
ASSERT_OK(Flush(1));
|
||||
// move this table to L1
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
|
||||
// reset block cache
|
||||
table_options.block_cache = NewLRUCache(64 * 1024);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
// create new table at L0
|
||||
Put(1, "a2", "begin2");
|
||||
Put(1, "z2", "end2");
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
table_options.block_cache->EraseUnRefEntries();
|
||||
|
||||
PinL0IndexAndFilterBlocksTest::CreateTwoLevels(&options);
|
||||
// get base cache values
|
||||
uint64_t fm = TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
uint64_t fh = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
@@ -1309,11 +1312,76 @@ TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
// the file is opened, prefetching results in a cache filter miss
|
||||
// the block is loaded and added to the cache,
|
||||
// then the get results in a cache hit for L1
|
||||
// When we have inifinite max_files, there is still cache miss because we have
|
||||
// reset the block cache
|
||||
value = Get(1, "a");
|
||||
ASSERT_EQ(fm + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(im + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
}
|
||||
|
||||
TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
Options options = CurrentOptions();
|
||||
PinL0IndexAndFilterBlocksTest::CreateTwoLevels(&options);
|
||||
|
||||
// Get base cache values
|
||||
uint64_t fm = TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
uint64_t fh = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
uint64_t im = TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS);
|
||||
uint64_t ih = TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT);
|
||||
|
||||
// Reopen database. If max_open_files is set as -1, table readers will be
|
||||
// preloaded. This will trigger a BlockBasedTable::Open() and prefetch
|
||||
// L0 index and filter. Level 1's prefetching is disabled in DB::Open()
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
if (infinite_max_files_) {
|
||||
// After reopen, cache miss are increased by one because we read (and only
|
||||
// read) filter and index on L0
|
||||
ASSERT_EQ(fm + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
// If max_open_files is not -1, we do not preload table readers, so there is
|
||||
// no change.
|
||||
ASSERT_EQ(fm, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
std::string value;
|
||||
// this should be read from L0
|
||||
value = Get(1, "a2");
|
||||
// If max_open_files is -1, we have pinned index and filter in Rep, so there
|
||||
// will not be changes in index and filter misses or hits. If max_open_files
|
||||
// is not -1, Get() will open a TableReader and prefetch index and filter.
|
||||
ASSERT_EQ(fm + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
|
||||
// this should be read from L1
|
||||
value = Get(1, "a");
|
||||
if (infinite_max_files_) {
|
||||
// In inifinite max files case, there's a cache miss in executing Get()
|
||||
// because index and filter are not prefetched before.
|
||||
ASSERT_EQ(fm + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
// In this case, cache miss will be increased by one in
|
||||
// BlockBasedTable::Open() because this is not in DB::Open() code path so we
|
||||
// will prefetch L1's index and filter. Cache hit will also be increased by
|
||||
// one because Get() will read index and filter from the block cache
|
||||
// prefetched in previous Open() call.
|
||||
ASSERT_EQ(fm + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(PinL0IndexAndFilterBlocksTest,
|
||||
PinL0IndexAndFilterBlocksTest, ::testing::Bool());
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -1487,6 +1555,283 @@ TEST_F(DBTest2, SyncPointMarker) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif
|
||||
|
||||
class MergeOperatorPinningTest : public DBTest2,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
MergeOperatorPinningTest() { disable_block_cache_ = GetParam(); }
|
||||
|
||||
bool disable_block_cache_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MergeOperatorPinningTest, MergeOperatorPinningTest,
|
||||
::testing::Bool());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(MergeOperatorPinningTest, OperandsMultiBlocks) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1; // every block will contain one entry
|
||||
table_options.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kKeysPerFile = 10;
|
||||
const int kOperandsPerKeyPerFile = 7;
|
||||
const int kOperandSize = 100;
|
||||
// Filse to write in L0 before compacting to lower level
|
||||
const int kFilesPerLevel = 3;
|
||||
|
||||
Random rnd(301);
|
||||
std::map<std::string, std::string> true_data;
|
||||
int batch_num = 1;
|
||||
int lvl_to_fill = 4;
|
||||
int key_id = 0;
|
||||
while (true) {
|
||||
for (int j = 0; j < kKeysPerFile; j++) {
|
||||
std::string key = Key(key_id % 35);
|
||||
key_id++;
|
||||
for (int k = 0; k < kOperandsPerKeyPerFile; k++) {
|
||||
std::string val = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, val));
|
||||
if (true_data[key].size() == 0) {
|
||||
true_data[key] = val;
|
||||
} else {
|
||||
true_data[key] += "," + val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lvl_to_fill == -1) {
|
||||
// Keep last batch in memtable and stop
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
if (batch_num % kFilesPerLevel == 0) {
|
||||
if (lvl_to_fill != 0) {
|
||||
MoveFilesToLevel(lvl_to_fill);
|
||||
}
|
||||
lvl_to_fill--;
|
||||
}
|
||||
batch_num++;
|
||||
}
|
||||
|
||||
// 3 L0 files
|
||||
// 1 L1 file
|
||||
// 3 L2 files
|
||||
// 1 L3 file
|
||||
// 3 L4 Files
|
||||
ASSERT_EQ(FilesPerLevel(), "3,1,3,1,3");
|
||||
|
||||
// Verify Get()
|
||||
for (auto kv : true_data) {
|
||||
ASSERT_EQ(Get(kv.first), kv.second);
|
||||
}
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
|
||||
// Verify Iterator::Next()
|
||||
auto data_iter = true_data.begin();
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), data_iter++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_iter->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_iter->second);
|
||||
}
|
||||
ASSERT_EQ(data_iter, true_data.end());
|
||||
|
||||
// Verify Iterator::Prev()
|
||||
auto data_rev = true_data.rbegin();
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev(), data_rev++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_rev->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_rev->second);
|
||||
}
|
||||
ASSERT_EQ(data_rev, true_data.rend());
|
||||
|
||||
// Verify Iterator::Seek()
|
||||
for (auto kv : true_data) {
|
||||
iter->Seek(kv.first);
|
||||
ASSERT_EQ(kv.first, iter->key().ToString());
|
||||
ASSERT_EQ(kv.second, iter->value().ToString());
|
||||
}
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, Randomized) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateMaxOperator();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::map<std::string, std::string> true_data;
|
||||
|
||||
const int kTotalMerges = 10000;
|
||||
// Every key gets ~10 operands
|
||||
const int kKeyRange = kTotalMerges / 10;
|
||||
const int kOperandSize = 20;
|
||||
const int kNumPutBefore = kKeyRange / 10; // 10% value
|
||||
const int kNumPutAfter = kKeyRange / 10; // 10% overwrite
|
||||
const int kNumDelete = kKeyRange / 10; // 10% delete
|
||||
|
||||
// kNumPutBefore keys will have base values
|
||||
for (int i = 0; i < kNumPutBefore; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
true_data[key] = value;
|
||||
}
|
||||
|
||||
// Do kTotalMerges merges
|
||||
for (int i = 0; i < kTotalMerges; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, value));
|
||||
|
||||
if (true_data[key] < value) {
|
||||
true_data[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite random kNumPutAfter keys
|
||||
for (int i = 0; i < kNumPutAfter; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
true_data[key] = value;
|
||||
}
|
||||
|
||||
// Delete random kNumDelete keys
|
||||
for (int i = 0; i < kNumDelete; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), key));
|
||||
|
||||
true_data.erase(key);
|
||||
}
|
||||
|
||||
VerifyDBFromMap(true_data);
|
||||
|
||||
// Skip HashCuckoo since it does not support merge operators
|
||||
} while (ChangeOptions(kSkipMergePut | kSkipHashCuckoo));
|
||||
}
|
||||
|
||||
class MergeOperatorHook : public MergeOperator {
|
||||
public:
|
||||
explicit MergeOperatorHook(std::shared_ptr<MergeOperator> _merge_op)
|
||||
: merge_op_(_merge_op) {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
before_merge_();
|
||||
bool res = merge_op_->FullMergeV2(merge_in, merge_out);
|
||||
after_merge_();
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return merge_op_->Name(); }
|
||||
|
||||
std::shared_ptr<MergeOperator> merge_op_;
|
||||
std::function<void()> before_merge_ = []() {};
|
||||
std::function<void()> after_merge_ = []() {};
|
||||
};
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, EvictCacheBeforeMerge) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
auto merge_hook =
|
||||
std::make_shared<MergeOperatorHook>(MergeOperators::CreateMaxOperator());
|
||||
options.merge_operator = merge_hook;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.max_open_files = 20;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = disable_block_cache_;
|
||||
if (bbto.no_block_cache == false) {
|
||||
bbto.block_cache = NewLRUCache(64 * 1024 * 1024);
|
||||
} else {
|
||||
bbto.block_cache = nullptr;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumOperands = 30;
|
||||
const int kNumKeys = 1000;
|
||||
const int kOperandSize = 100;
|
||||
Random rnd(301);
|
||||
|
||||
// 1000 keys every key have 30 operands, every operand is in a different file
|
||||
std::map<std::string, std::string> true_data;
|
||||
for (int i = 0; i < kNumOperands; i++) {
|
||||
for (int j = 0; j < kNumKeys; j++) {
|
||||
std::string k = Key(j);
|
||||
std::string v = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), k, v));
|
||||
|
||||
true_data[k] = std::max(true_data[k], v);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
std::vector<uint64_t> file_numbers = ListTableFiles(env_, dbname_);
|
||||
ASSERT_EQ(file_numbers.size(), kNumOperands);
|
||||
int merge_cnt = 0;
|
||||
|
||||
// Code executed before merge operation
|
||||
merge_hook->before_merge_ = [&]() {
|
||||
// Evict all tables from cache before every merge operation
|
||||
for (uint64_t num : file_numbers) {
|
||||
TableCache::Evict(dbfull()->TEST_table_cache(), num);
|
||||
}
|
||||
// Decrease cache capacity to force all unrefed blocks to be evicted
|
||||
if (bbto.block_cache) {
|
||||
bbto.block_cache->SetCapacity(1);
|
||||
}
|
||||
merge_cnt++;
|
||||
};
|
||||
|
||||
// Code executed after merge operation
|
||||
merge_hook->after_merge_ = [&]() {
|
||||
// Increase capacity again after doing the merge
|
||||
if (bbto.block_cache) {
|
||||
bbto.block_cache->SetCapacity(64 * 1024 * 1024);
|
||||
}
|
||||
};
|
||||
|
||||
VerifyDBFromMap(true_data);
|
||||
ASSERT_EQ(merge_cnt, kNumKeys * 4 /* get + next + prev + seek */);
|
||||
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
VerifyDBFromMap(true_data);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, MaxSuccessiveMergesInRecovery) {
|
||||
Options options;
|
||||
options = CurrentOptions(options);
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
db_->Put(WriteOptions(), "foo", "bar");
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "foo", "bar"));
|
||||
|
||||
options.max_successive_merges = 3;
|
||||
Reopen(options);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -30,6 +30,8 @@ SpecialEnv::SpecialEnv(Env* base)
|
||||
manifest_write_error_.store(false, std::memory_order_release);
|
||||
log_write_error_.store(false, std::memory_order_release);
|
||||
random_file_open_counter_.store(0, std::memory_order_relaxed);
|
||||
delete_count_.store(0, std::memory_order_relaxed);
|
||||
num_open_wal_file_.store(0);
|
||||
log_write_slowdown_ = 0;
|
||||
bytes_written_ = 0;
|
||||
sync_counter_ = 0;
|
||||
@@ -1074,6 +1076,40 @@ std::vector<std::uint64_t> DBTestBase::ListTableFiles(Env* env,
|
||||
return file_numbers;
|
||||
}
|
||||
|
||||
void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data) {
|
||||
for (auto& kv : true_data) {
|
||||
ASSERT_EQ(Get(kv.first), kv.second);
|
||||
}
|
||||
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
// Verify Iterator::Next()
|
||||
auto data_iter = true_data.begin();
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), data_iter++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_iter->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_iter->second);
|
||||
}
|
||||
ASSERT_EQ(data_iter, true_data.end());
|
||||
|
||||
// Verify Iterator::Prev()
|
||||
auto data_rev = true_data.rbegin();
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev(), data_rev++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_rev->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_rev->second);
|
||||
}
|
||||
ASSERT_EQ(data_rev, true_data.rend());
|
||||
|
||||
// Verify Iterator::Seek()
|
||||
for (auto kv : true_data) {
|
||||
iter->Seek(kv.first);
|
||||
ASSERT_EQ(kv.first, iter->key().ToString());
|
||||
ASSERT_EQ(kv.second, iter->value().ToString());
|
||||
}
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status DBTestBase::GenerateAndAddExternalFile(const Options options,
|
||||
|
||||
+16
-1
@@ -285,7 +285,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
class WalFile : public WritableFile {
|
||||
public:
|
||||
WalFile(SpecialEnv* env, unique_ptr<WritableFile>&& b)
|
||||
: env_(env), base_(std::move(b)) {}
|
||||
: env_(env), base_(std::move(b)) {
|
||||
env_->num_open_wal_file_.fetch_add(1);
|
||||
}
|
||||
virtual ~WalFile() { env_->num_open_wal_file_.fetch_add(-1); }
|
||||
Status Append(const Slice& data) override {
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_SYNC_POINT("SpecialEnv::WalFile::Append:1");
|
||||
@@ -443,6 +446,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
addon_time_.load();
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(const std::string& fname) override {
|
||||
delete_count_.fetch_add(1);
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
Random rnd_;
|
||||
port::Mutex rnd_mutex_; // Lock to pretect rnd_
|
||||
|
||||
@@ -470,6 +478,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Slow down every log write, in micro-seconds.
|
||||
std::atomic<int> log_write_slowdown_;
|
||||
|
||||
// Number of WAL files that are still open for write.
|
||||
std::atomic<int> num_open_wal_file_;
|
||||
|
||||
bool count_random_reads_;
|
||||
anon::AtomicCounter random_read_counter_;
|
||||
std::atomic<size_t> random_read_bytes_counter_;
|
||||
@@ -494,6 +505,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
std::atomic<int64_t> addon_time_;
|
||||
|
||||
std::atomic<int> delete_count_;
|
||||
|
||||
bool time_elapse_only_sleep_;
|
||||
|
||||
bool no_sleep_;
|
||||
@@ -797,6 +810,8 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
std::vector<std::uint64_t> ListTableFiles(Env* env, const std::string& path);
|
||||
|
||||
void VerifyDBFromMap(std::map<std::string, std::string> true_data);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status GenerateAndAddExternalFile(const Options options,
|
||||
std::vector<int> keys, size_t file_id);
|
||||
|
||||
@@ -292,6 +292,44 @@ TEST_F(DBWALTest, RecoveryWithEmptyLog) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBWALTest, GetSortedWalFiles) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
ASSERT_EQ(0, log_files.size());
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "v1"));
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
ASSERT_EQ(1, log_files.size());
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoveryWithLogDataForSomeCFs) {
|
||||
// Test for regression of WAL cleanup missing files that don't contain data
|
||||
// for every column family.
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
ASSERT_OK(Put(1, "foo", "v1"));
|
||||
ASSERT_OK(Put(1, "foo", "v2"));
|
||||
std::array<uint64_t, 2> earliest_log_nums;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i > 0) {
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
|
||||
}
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
if (log_files.size() > 0) {
|
||||
earliest_log_nums[i] = log_files[0]->LogNumber();
|
||||
} else {
|
||||
earliest_log_nums[i] = port::kMaxUint64;
|
||||
}
|
||||
}
|
||||
// Check at least the first WAL was cleaned up during the recovery.
|
||||
ASSERT_LT(earliest_log_nums[0], earliest_log_nums[1]);
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithLargeLog) {
|
||||
do {
|
||||
{
|
||||
|
||||
+61
-51
@@ -84,7 +84,8 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
output_compression_(output_compression),
|
||||
stats_(stats),
|
||||
event_logger_(event_logger),
|
||||
measure_io_stats_(measure_io_stats) {
|
||||
measure_io_stats_(measure_io_stats),
|
||||
pick_memtable_called(false) {
|
||||
// Update the thread status to indicate flush.
|
||||
ReportStartedFlush();
|
||||
TEST_SYNC_POINT("FlushJob::FlushJob()");
|
||||
@@ -121,9 +122,47 @@ void FlushJob::RecordFlushIOStats() {
|
||||
IOSTATS_RESET(bytes_written);
|
||||
}
|
||||
|
||||
void FlushJob::PickMemTable() {
|
||||
db_mutex_->AssertHeld();
|
||||
assert(!pick_memtable_called);
|
||||
pick_memtable_called = true;
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
cfd_->imm()->PickMemtablesToFlush(&mems_);
|
||||
if (mems_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReportFlushInputSize(mems_);
|
||||
|
||||
// entries mems are (implicitly) sorted in ascending order by their created
|
||||
// time. We will use the first memtable's `edit` to keep the meta info for
|
||||
// this flush.
|
||||
MemTable* m = mems_[0];
|
||||
edit_ = m->GetEdits();
|
||||
edit_->SetPrevLogNumber(0);
|
||||
// SetLogNumber(log_num) indicates logs with number smaller than log_num
|
||||
// will no longer be picked up for recovery.
|
||||
edit_->SetLogNumber(mems_.back()->GetNextLogNumber());
|
||||
edit_->SetColumnFamily(cfd_->GetID());
|
||||
|
||||
// path 0 for level 0 file.
|
||||
meta_.fd = FileDescriptor(versions_->NewFileNumber(), 0, 0);
|
||||
|
||||
base_ = cfd_->current();
|
||||
base_->Ref(); // it is likely that we do not need this reference
|
||||
}
|
||||
|
||||
Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
db_mutex_->AssertHeld();
|
||||
assert(pick_memtable_called);
|
||||
AutoThreadOperationStageUpdater stage_run(
|
||||
ThreadStatus::STAGE_FLUSH_RUN);
|
||||
if (mems_.empty()) {
|
||||
LogToBuffer(log_buffer_, "[%s] Nothing in memtable to flush",
|
||||
cfd_->GetName().c_str());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// I/O measurement variables
|
||||
PerfLevel prev_perf_level = PerfLevel::kEnableTime;
|
||||
uint64_t prev_write_nanos = 0;
|
||||
@@ -139,31 +178,8 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
prev_prepare_write_nanos = IOSTATS(prepare_write_nanos);
|
||||
}
|
||||
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
FileMetaData meta;
|
||||
autovector<MemTable*> mems;
|
||||
cfd_->imm()->PickMemtablesToFlush(&mems);
|
||||
if (mems.empty()) {
|
||||
LogToBuffer(log_buffer_, "[%s] Nothing in memtable to flush",
|
||||
cfd_->GetName().c_str());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ReportFlushInputSize(mems);
|
||||
|
||||
// entries mems are (implicitly) sorted in ascending order by their created
|
||||
// time. We will use the first memtable's `edit` to keep the meta info for
|
||||
// this flush.
|
||||
MemTable* m = mems[0];
|
||||
VersionEdit* edit = m->GetEdits();
|
||||
edit->SetPrevLogNumber(0);
|
||||
// SetLogNumber(log_num) indicates logs with number smaller than log_num
|
||||
// will no longer be picked up for recovery.
|
||||
edit->SetLogNumber(mems.back()->GetNextLogNumber());
|
||||
edit->SetColumnFamily(cfd_->GetID());
|
||||
|
||||
// This will release and re-acquire the mutex.
|
||||
Status s = WriteLevel0Table(mems, edit, &meta);
|
||||
Status s = WriteLevel0Table();
|
||||
|
||||
if (s.ok() &&
|
||||
(shutting_down_->load(std::memory_order_acquire) || cfd_->IsDropped())) {
|
||||
@@ -172,18 +188,18 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
cfd_->imm()->RollbackMemtableFlush(mems, meta.fd.GetNumber());
|
||||
cfd_->imm()->RollbackMemtableFlush(mems_, meta_.fd.GetNumber());
|
||||
} else {
|
||||
TEST_SYNC_POINT("FlushJob::InstallResults");
|
||||
// Replace immutable memtable with the generated Table
|
||||
s = cfd_->imm()->InstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems, versions_, db_mutex_,
|
||||
meta.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
cfd_, mutable_cf_options_, mems_, versions_, db_mutex_,
|
||||
meta_.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_);
|
||||
}
|
||||
|
||||
if (s.ok() && file_meta != nullptr) {
|
||||
*file_meta = meta;
|
||||
*file_meta = meta_;
|
||||
}
|
||||
RecordFlushIOStats();
|
||||
|
||||
@@ -214,17 +230,11 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
VersionEdit* edit, FileMetaData* meta) {
|
||||
Status FlushJob::WriteLevel0Table() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = db_options_.env->NowMicros();
|
||||
// path 0 for level 0 file.
|
||||
meta->fd = FileDescriptor(versions_->NewFileNumber(), 0, 0);
|
||||
|
||||
Version* base = cfd_->current();
|
||||
base->Ref(); // it is likely that we do not need this reference
|
||||
Status s;
|
||||
{
|
||||
db_mutex_->Unlock();
|
||||
@@ -237,7 +247,7 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
Arena arena;
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
for (MemTable* m : mems) {
|
||||
for (MemTable* m : mems_) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Flushing memtable with next log file: %" PRIu64 "\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, m->GetNextLogNumber());
|
||||
@@ -249,7 +259,7 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
|
||||
event_logger_->Log() << "job" << job_context_->job_id << "event"
|
||||
<< "flush_started"
|
||||
<< "num_memtables" << mems.size() << "num_entries"
|
||||
<< "num_memtables" << mems_.size() << "num_entries"
|
||||
<< total_num_entries << "num_deletes"
|
||||
<< total_num_deletes << "memory_usage"
|
||||
<< total_memory_usage;
|
||||
@@ -260,13 +270,13 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta->fd.GetNumber());
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta_.fd.GetNumber());
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:output_compression",
|
||||
&output_compression_);
|
||||
s = BuildTable(
|
||||
dbname_, db_options_.env, *cfd_->ioptions(), mutable_cf_options_,
|
||||
env_options_, cfd_->table_cache(), iter.get(), meta,
|
||||
env_options_, cfd_->table_cache(), iter.get(), &meta_,
|
||||
cfd_->internal_comparator(), cfd_->int_tbl_prop_collector_factories(),
|
||||
cfd_->GetID(), cfd_->GetName(), existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, output_compression_,
|
||||
@@ -280,9 +290,9 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": %" PRIu64
|
||||
" bytes %s"
|
||||
"%s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta->fd.GetNumber(),
|
||||
meta->fd.GetFileSize(), s.ToString().c_str(),
|
||||
meta->marked_for_compaction ? " (needs compaction)" : "");
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta_.fd.GetNumber(),
|
||||
meta_.fd.GetFileSize(), s.ToString().c_str(),
|
||||
meta_.marked_for_compaction ? " (needs compaction)" : "");
|
||||
|
||||
if (!db_options_.disableDataSync && output_file_directory_ != nullptr) {
|
||||
output_file_directory_->Fsync();
|
||||
@@ -290,29 +300,29 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
TEST_SYNC_POINT("FlushJob::WriteLevel0Table");
|
||||
db_mutex_->Lock();
|
||||
}
|
||||
base->Unref();
|
||||
base_->Unref();
|
||||
|
||||
// Note that if file_size is zero, the file has been deleted and
|
||||
// should not be added to the manifest.
|
||||
if (s.ok() && meta->fd.GetFileSize() > 0) {
|
||||
if (s.ok() && meta_.fd.GetFileSize() > 0) {
|
||||
// if we have more than 1 background thread, then we cannot
|
||||
// insert files directly into higher levels because some other
|
||||
// threads could be concurrently producing compacted files for
|
||||
// that key range.
|
||||
// Add file to L0
|
||||
edit->AddFile(0 /* level */, meta->fd.GetNumber(), meta->fd.GetPathId(),
|
||||
meta->fd.GetFileSize(), meta->smallest, meta->largest,
|
||||
meta->smallest_seqno, meta->largest_seqno,
|
||||
meta->marked_for_compaction);
|
||||
edit_->AddFile(0 /* level */, meta_.fd.GetNumber(), meta_.fd.GetPathId(),
|
||||
meta_.fd.GetFileSize(), meta_.smallest, meta_.largest,
|
||||
meta_.smallest_seqno, meta_.largest_seqno,
|
||||
meta_.marked_for_compaction);
|
||||
}
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(1);
|
||||
stats.micros = db_options_.env->NowMicros() - start_micros;
|
||||
stats.bytes_written = meta->fd.GetFileSize();
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, stats);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
meta->fd.GetFileSize());
|
||||
meta_.fd.GetFileSize());
|
||||
RecordFlushIOStats();
|
||||
return s;
|
||||
}
|
||||
|
||||
+16
-8
@@ -16,12 +16,17 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -33,11 +38,6 @@
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "db/job_context.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -66,6 +66,8 @@ class FlushJob {
|
||||
|
||||
~FlushJob();
|
||||
|
||||
// Require db_mutex held
|
||||
void PickMemTable();
|
||||
Status Run(FileMetaData* file_meta = nullptr);
|
||||
TableProperties GetTableProperties() const { return table_properties_; }
|
||||
|
||||
@@ -73,8 +75,7 @@ class FlushJob {
|
||||
void ReportStartedFlush();
|
||||
void ReportFlushInputSize(const autovector<MemTable*>& mems);
|
||||
void RecordFlushIOStats();
|
||||
Status WriteLevel0Table(const autovector<MemTable*>& mems, VersionEdit* edit,
|
||||
FileMetaData* meta);
|
||||
Status WriteLevel0Table();
|
||||
const std::string& dbname_;
|
||||
ColumnFamilyData* cfd_;
|
||||
const DBOptions& db_options_;
|
||||
@@ -94,6 +95,13 @@ class FlushJob {
|
||||
EventLogger* event_logger_;
|
||||
TableProperties table_properties_;
|
||||
bool measure_io_stats_;
|
||||
|
||||
// Variables below are set by PickMemTable():
|
||||
FileMetaData meta_;
|
||||
autovector<MemTable*> mems_;
|
||||
VersionEdit* edit_;
|
||||
Version* base_;
|
||||
bool pick_memtable_called;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -94,7 +94,11 @@ TEST_F(FlushJobTest, Empty) {
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
{}, kMaxSequenceNumber, &job_context, nullptr, nullptr,
|
||||
nullptr, kNoCompression, nullptr, &event_logger, false);
|
||||
ASSERT_OK(flush_job.Run());
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
@@ -135,6 +139,7 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
nullptr, kNoCompression, nullptr, &event_logger, true);
|
||||
FileMetaData fd;
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run(&fd));
|
||||
mutex_.Unlock();
|
||||
ASSERT_EQ(ToString(0), fd.smallest.user_key().ToString());
|
||||
@@ -198,6 +203,7 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
&shutting_down_, snapshots, kMaxSequenceNumber, &job_context, nullptr,
|
||||
nullptr, nullptr, kNoCompression, nullptr, &event_logger, true);
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
mutex_.Unlock();
|
||||
mock_table_factory_->AssertSingleFile(inserted_keys);
|
||||
|
||||
@@ -153,10 +153,14 @@ void ForwardIterator::SVCleanup() {
|
||||
db_->mutex_.Lock();
|
||||
sv_->Cleanup();
|
||||
db_->FindObsoleteFiles(&job_context, false, true);
|
||||
if (read_options_.background_purge_on_iterator_cleanup) {
|
||||
db_->ScheduleBgLogWriterClose(&job_context);
|
||||
}
|
||||
db_->mutex_.Unlock();
|
||||
delete sv_;
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(job_context);
|
||||
db_->PurgeObsoleteFiles(
|
||||
job_context, read_options_.background_purge_on_iterator_cleanup);
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
@@ -615,15 +615,15 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
// Data
|
||||
// writes: total number of write requests.
|
||||
// keys: total number of key updates issued by all the write requests
|
||||
// batches: number of group commits issued to the DB. Each group can contain
|
||||
// one or more writes.
|
||||
// commit groups: number of group commits issued to the DB. Each group can
|
||||
// contain one or more writes.
|
||||
// so writes/keys is the average number of put in multi-put or put
|
||||
// writes/batches is the average group commit size.
|
||||
// writes/groups is the average group commit size.
|
||||
//
|
||||
// The format is the same for interval stats.
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative writes: %s writes, %s keys, %s batches, "
|
||||
"%.1f writes per batch, ingest: %.2f GB, %.2f MB/s\n",
|
||||
"Cumulative writes: %s writes, %s keys, %s commit groups, "
|
||||
"%.1f writes per commit group, ingest: %.2f GB, %.2f MB/s\n",
|
||||
NumberToHumanString(write_other + write_self).c_str(),
|
||||
NumberToHumanString(num_keys_written).c_str(),
|
||||
NumberToHumanString(write_self).c_str(),
|
||||
@@ -654,8 +654,8 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
uint64_t interval_num_keys_written =
|
||||
num_keys_written - db_stats_snapshot_.num_keys_written;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval writes: %s writes, %s keys, %s batches, "
|
||||
"%.1f writes per batch, ingest: %.2f MB, %.2f MB/s\n",
|
||||
"Interval writes: %s writes, %s keys, %s commit groups, "
|
||||
"%.1f writes per commit group, ingest: %.2f MB, %.2f MB/s\n",
|
||||
NumberToHumanString(
|
||||
interval_write_other + interval_write_self).c_str(),
|
||||
NumberToHumanString(interval_num_keys_written).c_str(),
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/log_writer.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -81,6 +81,8 @@ class Writer {
|
||||
WritableFileWriter* file() { return dest_.get(); }
|
||||
const WritableFileWriter* file() const { return dest_.get(); }
|
||||
|
||||
uint64_t get_log_number() const { return log_number_; }
|
||||
|
||||
private:
|
||||
unique_ptr<WritableFileWriter> dest_;
|
||||
size_t block_offset_; // Current offset in block
|
||||
|
||||
+16
-9
@@ -44,8 +44,7 @@ MemTableOptions::MemTableOptions(const ImmutableCFOptions& ioptions,
|
||||
static_cast<double>(mutable_cf_options.write_buffer_size) *
|
||||
mutable_cf_options.memtable_prefix_bloom_size_ratio) *
|
||||
8u),
|
||||
memtable_prefix_bloom_huge_page_tlb_size(
|
||||
mutable_cf_options.memtable_prefix_bloom_huge_page_tlb_size),
|
||||
memtable_huge_page_size(mutable_cf_options.memtable_huge_page_size),
|
||||
inplace_update_support(ioptions.inplace_update_support),
|
||||
inplace_update_num_locks(mutable_cf_options.inplace_update_num_locks),
|
||||
inplace_callback(ioptions.inplace_callback),
|
||||
@@ -63,7 +62,8 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
moptions_(ioptions, mutable_cf_options),
|
||||
refs_(0),
|
||||
kArenaBlockSize(OptimizeBlockSize(moptions_.arena_block_size)),
|
||||
arena_(moptions_.arena_block_size, 0),
|
||||
arena_(moptions_.arena_block_size,
|
||||
mutable_cf_options.memtable_huge_page_size),
|
||||
allocator_(&arena_, write_buffer_manager),
|
||||
table_(ioptions.memtable_factory->CreateMemTableRep(
|
||||
comparator_, &allocator_, ioptions.prefix_extractor,
|
||||
@@ -92,7 +92,7 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
prefix_bloom_.reset(new DynamicBloom(
|
||||
&allocator_, moptions_.memtable_prefix_bloom_bits,
|
||||
ioptions.bloom_locality, 6 /* hard coded 6 probes */, nullptr,
|
||||
moptions_.memtable_prefix_bloom_huge_page_tlb_size, ioptions.info_log));
|
||||
moptions_.memtable_huge_page_size, ioptions.info_log));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,12 +218,13 @@ const char* EncodeKey(std::string* scratch, const Slice& target) {
|
||||
|
||||
class MemTableIterator : public InternalIterator {
|
||||
public:
|
||||
MemTableIterator(
|
||||
const MemTable& mem, const ReadOptions& read_options, Arena* arena)
|
||||
MemTableIterator(const MemTable& mem, const ReadOptions& read_options,
|
||||
Arena* arena)
|
||||
: bloom_(nullptr),
|
||||
prefix_extractor_(mem.prefix_extractor_),
|
||||
valid_(false),
|
||||
arena_mode_(arena != nullptr) {
|
||||
arena_mode_(arena != nullptr),
|
||||
value_pinned_(!mem.GetMemTableOptions()->inplace_update_support) {
|
||||
if (prefix_extractor_ != nullptr && !read_options.total_order_seek) {
|
||||
bloom_ = mem.prefix_bloom_.get();
|
||||
iter_ = mem.table_->GetDynamicPrefixIterator(arena);
|
||||
@@ -306,12 +307,18 @@ class MemTableIterator : public InternalIterator {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool IsValuePinned() const override {
|
||||
// memtable value is always pinned, except if we allow inplace update.
|
||||
return value_pinned_;
|
||||
}
|
||||
|
||||
private:
|
||||
DynamicBloom* bloom_;
|
||||
const SliceTransform* const prefix_extractor_;
|
||||
MemTableRep::Iterator* iter_;
|
||||
bool valid_;
|
||||
bool arena_mode_;
|
||||
bool value_pinned_;
|
||||
|
||||
// No copying allowed
|
||||
MemTableIterator(const MemTableIterator&);
|
||||
@@ -508,7 +515,6 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
case kTypeDeletion:
|
||||
case kTypeSingleDeletion: {
|
||||
if (*(s->merge_in_progress)) {
|
||||
*(s->status) = Status::OK();
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), nullptr,
|
||||
merge_context->GetOperands(), s->value, s->logger, s->statistics,
|
||||
@@ -532,7 +538,8 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
}
|
||||
Slice v = GetLengthPrefixedSlice(key_ptr + key_length);
|
||||
*(s->merge_in_progress) = true;
|
||||
merge_context->PushOperand(v);
|
||||
merge_context->PushOperand(
|
||||
v, s->inplace_update_support == false /* operand_pinned */);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
||||
+5
-3
@@ -41,7 +41,7 @@ struct MemTableOptions {
|
||||
size_t write_buffer_size;
|
||||
size_t arena_block_size;
|
||||
uint32_t memtable_prefix_bloom_bits;
|
||||
size_t memtable_prefix_bloom_huge_page_tlb_size;
|
||||
size_t memtable_huge_page_size;
|
||||
bool inplace_update_support;
|
||||
size_t inplace_update_num_locks;
|
||||
UpdateStatus (*inplace_callback)(char* existing_value,
|
||||
@@ -232,8 +232,10 @@ class MemTable {
|
||||
num_entries_.fetch_add(update_counters.num_entries,
|
||||
std::memory_order_relaxed);
|
||||
data_size_.fetch_add(update_counters.data_size, std::memory_order_relaxed);
|
||||
num_deletes_.fetch_add(update_counters.num_deletes,
|
||||
std::memory_order_relaxed);
|
||||
if (update_counters.num_deletes != 0) {
|
||||
num_deletes_.fetch_add(update_counters.num_deletes,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
UpdateFlushState();
|
||||
}
|
||||
|
||||
|
||||
+66
-55
@@ -11,14 +11,15 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include "rocksdb/db.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "table/merger.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/thread_status_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -297,69 +298,79 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
// if some other thread is already committing, then return
|
||||
Status s;
|
||||
if (commit_in_progress_) {
|
||||
TEST_SYNC_POINT("MemTableList::InstallMemtableFlushResults:InProgress");
|
||||
return s;
|
||||
}
|
||||
|
||||
// Only a single thread can be executing this piece of code
|
||||
commit_in_progress_ = true;
|
||||
|
||||
// scan all memtables from the earliest, and commit those
|
||||
// (in that order) that have finished flushing. Memetables
|
||||
// are always committed in the order that they were created.
|
||||
uint64_t batch_file_number = 0;
|
||||
size_t batch_count = 0;
|
||||
autovector<VersionEdit*> edit_list;
|
||||
auto& memlist = current_->memlist_;
|
||||
// enumerate from the last (earliest) element to see how many batch finished
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
if (!m->flush_completed_) {
|
||||
// Retry until all completed flushes are committed. New flushes can finish
|
||||
// while the current thread is writing manifest where mutex is released.
|
||||
while (s.ok()) {
|
||||
auto& memlist = current_->memlist_;
|
||||
if (memlist.empty() || !memlist.back()->flush_completed_) {
|
||||
break;
|
||||
}
|
||||
if (it == memlist.rbegin() || batch_file_number != m->file_number_) {
|
||||
batch_file_number = m->file_number_;
|
||||
LogToBuffer(log_buffer, "[%s] Level-0 commit table #%" PRIu64 " started",
|
||||
cfd->GetName().c_str(), m->file_number_);
|
||||
edit_list.push_back(&m->edit_);
|
||||
}
|
||||
batch_count++;
|
||||
}
|
||||
|
||||
if (batch_count > 0) {
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(cfd, mutable_cf_options, edit_list, mu, db_directory);
|
||||
|
||||
// we will be changing the version in the next code path,
|
||||
// so we better create a new one, since versions are immutable
|
||||
InstallNewVersion();
|
||||
|
||||
// All the later memtables that have the same filenum
|
||||
// are part of the same batch. They can be committed now.
|
||||
uint64_t mem_id = 1; // how many memtables have been flushed.
|
||||
if (s.ok()) { // commit new state
|
||||
while (batch_count-- > 0) {
|
||||
MemTable* m = current_->memlist_.back();
|
||||
LogToBuffer(log_buffer, "[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " done",
|
||||
cfd->GetName().c_str(), m->file_number_, mem_id);
|
||||
assert(m->file_number_ > 0);
|
||||
current_->Remove(m, to_delete);
|
||||
++mem_id;
|
||||
// scan all memtables from the earliest, and commit those
|
||||
// (in that order) that have finished flushing. Memetables
|
||||
// are always committed in the order that they were created.
|
||||
uint64_t batch_file_number = 0;
|
||||
size_t batch_count = 0;
|
||||
autovector<VersionEdit*> edit_list;
|
||||
// enumerate from the last (earliest) element to see how many batch finished
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
if (!m->flush_completed_) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (auto it = current_->memlist_.rbegin(); batch_count-- > 0; it++) {
|
||||
MemTable* m = *it;
|
||||
// commit failed. setup state so that we can flush again.
|
||||
LogToBuffer(log_buffer, "Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " failed",
|
||||
m->file_number_, mem_id);
|
||||
m->flush_completed_ = false;
|
||||
m->flush_in_progress_ = false;
|
||||
m->edit_.Clear();
|
||||
num_flush_not_started_++;
|
||||
m->file_number_ = 0;
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
++mem_id;
|
||||
if (it == memlist.rbegin() || batch_file_number != m->file_number_) {
|
||||
batch_file_number = m->file_number_;
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Level-0 commit table #%" PRIu64 " started",
|
||||
cfd->GetName().c_str(), m->file_number_);
|
||||
edit_list.push_back(&m->edit_);
|
||||
}
|
||||
batch_count++;
|
||||
}
|
||||
|
||||
if (batch_count > 0) {
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(cfd, mutable_cf_options, edit_list, mu,
|
||||
db_directory);
|
||||
|
||||
// we will be changing the version in the next code path,
|
||||
// so we better create a new one, since versions are immutable
|
||||
InstallNewVersion();
|
||||
|
||||
// All the later memtables that have the same filenum
|
||||
// are part of the same batch. They can be committed now.
|
||||
uint64_t mem_id = 1; // how many memtables have been flushed.
|
||||
if (s.ok()) { // commit new state
|
||||
while (batch_count-- > 0) {
|
||||
MemTable* m = current_->memlist_.back();
|
||||
LogToBuffer(log_buffer, "[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " done",
|
||||
cfd->GetName().c_str(), m->file_number_, mem_id);
|
||||
assert(m->file_number_ > 0);
|
||||
current_->Remove(m, to_delete);
|
||||
++mem_id;
|
||||
}
|
||||
} else {
|
||||
for (auto it = current_->memlist_.rbegin(); batch_count-- > 0; it++) {
|
||||
MemTable* m = *it;
|
||||
// commit failed. setup state so that we can flush again.
|
||||
LogToBuffer(log_buffer, "Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " failed",
|
||||
m->file_number_, mem_id);
|
||||
m->flush_completed_ = false;
|
||||
m->flush_in_progress_ = false;
|
||||
m->edit_.Clear();
|
||||
num_flush_not_started_++;
|
||||
m->file_number_ = 0;
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
++mem_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+72
-29
@@ -4,70 +4,113 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
const std::deque<std::string> empty_operand_list;
|
||||
const std::vector<Slice> empty_operand_list;
|
||||
|
||||
// The merge context for merging a user key.
|
||||
// When doing a Get(), DB will create such a class and pass it when
|
||||
// issuing Get() operation to memtables and version_set. The operands
|
||||
// will be fetched from the context when issuing partial of full merge.
|
||||
class MergeContext {
|
||||
public:
|
||||
public:
|
||||
// Clear all the operands
|
||||
void Clear() {
|
||||
if (operand_list) {
|
||||
operand_list->clear();
|
||||
if (operand_list_) {
|
||||
operand_list_->clear();
|
||||
copied_operands_->clear();
|
||||
}
|
||||
}
|
||||
// Replace all operands with merge_result, which are expected to be the
|
||||
// merge result of them.
|
||||
void PushPartialMergeResult(std::string& merge_result) {
|
||||
assert (operand_list);
|
||||
operand_list->clear();
|
||||
operand_list->push_front(std::move(merge_result));
|
||||
}
|
||||
|
||||
// Push a merge operand
|
||||
void PushOperand(const Slice& operand_slice) {
|
||||
void PushOperand(const Slice& operand_slice, bool operand_pinned = false) {
|
||||
Initialize();
|
||||
operand_list->push_front(operand_slice.ToString());
|
||||
SetDirectionBackward();
|
||||
|
||||
if (operand_pinned) {
|
||||
operand_list_->push_back(operand_slice);
|
||||
} else {
|
||||
// We need to have our own copy of the operand since it's not pinned
|
||||
copied_operands_->emplace_back(
|
||||
new std::string(operand_slice.data(), operand_slice.size()));
|
||||
operand_list_->push_back(*copied_operands_->back());
|
||||
}
|
||||
}
|
||||
|
||||
// Push back a merge operand
|
||||
void PushOperandBack(const Slice& operand_slice) {
|
||||
void PushOperandBack(const Slice& operand_slice,
|
||||
bool operand_pinned = false) {
|
||||
Initialize();
|
||||
operand_list->push_back(operand_slice.ToString());
|
||||
SetDirectionForward();
|
||||
|
||||
if (operand_pinned) {
|
||||
operand_list_->push_back(operand_slice);
|
||||
} else {
|
||||
// We need to have our own copy of the operand since it's not pinned
|
||||
copied_operands_->emplace_back(
|
||||
new std::string(operand_slice.data(), operand_slice.size()));
|
||||
operand_list_->push_back(*copied_operands_->back());
|
||||
}
|
||||
}
|
||||
|
||||
// return total number of operands in the list
|
||||
size_t GetNumOperands() const {
|
||||
if (!operand_list) {
|
||||
if (!operand_list_) {
|
||||
return 0;
|
||||
}
|
||||
return operand_list->size();
|
||||
return operand_list_->size();
|
||||
}
|
||||
|
||||
// Get the operand at the index.
|
||||
Slice GetOperand(int index) const {
|
||||
assert (operand_list);
|
||||
return (*operand_list)[index];
|
||||
Slice GetOperand(int index) {
|
||||
assert(operand_list_);
|
||||
|
||||
SetDirectionForward();
|
||||
return (*operand_list_)[index];
|
||||
}
|
||||
|
||||
// Return all the operands.
|
||||
const std::deque<std::string>& GetOperands() const {
|
||||
if (!operand_list) {
|
||||
const std::vector<Slice>& GetOperands() {
|
||||
if (!operand_list_) {
|
||||
return empty_operand_list;
|
||||
}
|
||||
return *operand_list;
|
||||
|
||||
SetDirectionForward();
|
||||
return *operand_list_;
|
||||
}
|
||||
private:
|
||||
|
||||
private:
|
||||
void Initialize() {
|
||||
if (!operand_list) {
|
||||
operand_list.reset(new std::deque<std::string>());
|
||||
if (!operand_list_) {
|
||||
operand_list_.reset(new std::vector<Slice>());
|
||||
copied_operands_.reset(new std::vector<std::unique_ptr<std::string>>());
|
||||
}
|
||||
}
|
||||
std::unique_ptr<std::deque<std::string>> operand_list;
|
||||
|
||||
void SetDirectionForward() {
|
||||
if (operands_reversed_ == true) {
|
||||
std::reverse(operand_list_->begin(), operand_list_->end());
|
||||
operands_reversed_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetDirectionBackward() {
|
||||
if (operands_reversed_ == false) {
|
||||
std::reverse(operand_list_->begin(), operand_list_->end());
|
||||
operands_reversed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// List of operands
|
||||
std::unique_ptr<std::vector<Slice>> operand_list_;
|
||||
// Copy of operands that are not pinned.
|
||||
std::unique_ptr<std::vector<std::unique_ptr<std::string>>> copied_operands_;
|
||||
bool operands_reversed_ = true;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+40
-20
@@ -20,9 +20,10 @@ namespace rocksdb {
|
||||
|
||||
Status MergeHelper::TimedFullMerge(const MergeOperator* merge_operator,
|
||||
const Slice& key, const Slice* value,
|
||||
const std::deque<std::string>& operands,
|
||||
const std::vector<Slice>& operands,
|
||||
std::string* result, Logger* logger,
|
||||
Statistics* statistics, Env* env) {
|
||||
Statistics* statistics, Env* env,
|
||||
Slice* result_operand) {
|
||||
assert(merge_operator != nullptr);
|
||||
|
||||
if (operands.size() == 0) {
|
||||
@@ -32,13 +33,28 @@ Status MergeHelper::TimedFullMerge(const MergeOperator* merge_operator,
|
||||
}
|
||||
|
||||
bool success;
|
||||
Slice tmp_result_operand(nullptr, 0);
|
||||
const MergeOperator::MergeOperationInput merge_in(key, value, operands,
|
||||
logger);
|
||||
MergeOperator::MergeOperationOutput merge_out(*result, tmp_result_operand);
|
||||
{
|
||||
// Setup to time the merge
|
||||
StopWatchNano timer(env, statistics != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
|
||||
// Do the merge
|
||||
success = merge_operator->FullMerge(key, value, operands, result, logger);
|
||||
success = merge_operator->FullMergeV2(merge_in, &merge_out);
|
||||
|
||||
if (tmp_result_operand.data()) {
|
||||
// FullMergeV2 result is an existing operand
|
||||
if (result_operand != nullptr) {
|
||||
*result_operand = tmp_result_operand;
|
||||
} else {
|
||||
result->assign(tmp_result_operand.data(), tmp_result_operand.size());
|
||||
}
|
||||
} else if (result_operand) {
|
||||
*result_operand = Slice(nullptr, 0);
|
||||
}
|
||||
|
||||
RecordTick(statistics, MERGE_OPERATION_TOTAL_TIME,
|
||||
statistics ? timer.ElapsedNanos() : 0);
|
||||
@@ -65,7 +81,7 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
// Also maintain the list of merge operands seen.
|
||||
assert(HasOperator());
|
||||
keys_.clear();
|
||||
operands_.clear();
|
||||
merge_context_.Clear();
|
||||
assert(user_merge_operator_);
|
||||
bool first_key = true;
|
||||
|
||||
@@ -87,7 +103,7 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
bool hit_the_next_user_key = false;
|
||||
for (; iter->Valid(); iter->Next(), original_key_is_iter = false) {
|
||||
ParsedInternalKey ikey;
|
||||
assert(keys_.size() == operands_.size());
|
||||
assert(keys_.size() == merge_context_.GetNumOperands());
|
||||
|
||||
if (!ParseInternalKey(iter->key(), &ikey)) {
|
||||
// stop at corrupted key
|
||||
@@ -142,7 +158,8 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
const Slice* val_ptr = (kTypeValue == ikey.type) ? &val : nullptr;
|
||||
std::string merge_result;
|
||||
s = TimedFullMerge(user_merge_operator_, ikey.user_key, val_ptr,
|
||||
operands_, &merge_result, logger_, stats_, env_);
|
||||
merge_context_.GetOperands(), &merge_result, logger_,
|
||||
stats_, env_);
|
||||
|
||||
// We store the result in keys_.back() and operands_.back()
|
||||
// if nothing went wrong (i.e.: no operand corruption on disk)
|
||||
@@ -152,9 +169,9 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
orig_ikey.type = kTypeValue;
|
||||
UpdateInternalKey(&original_key, orig_ikey.sequence, orig_ikey.type);
|
||||
keys_.clear();
|
||||
operands_.clear();
|
||||
merge_context_.Clear();
|
||||
keys_.emplace_front(std::move(original_key));
|
||||
operands_.emplace_front(std::move(merge_result));
|
||||
merge_context_.PushOperand(merge_result);
|
||||
}
|
||||
|
||||
// move iter to the next entry
|
||||
@@ -188,12 +205,13 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
// original_key before
|
||||
ParseInternalKey(keys_.back(), &orig_ikey);
|
||||
}
|
||||
operands_.push_front(value_slice.ToString());
|
||||
merge_context_.PushOperand(value_slice,
|
||||
iter->IsValuePinned() /* operand_pinned */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (operands_.size() == 0) {
|
||||
if (merge_context_.GetNumOperands() == 0) {
|
||||
// we filtered out all the merge operands
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -218,11 +236,12 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
// do a final merge with nullptr as the existing value and say
|
||||
// bye to the merge type (it's now converted to a Put)
|
||||
assert(kTypeMerge == orig_ikey.type);
|
||||
assert(operands_.size() >= 1);
|
||||
assert(operands_.size() == keys_.size());
|
||||
assert(merge_context_.GetNumOperands() >= 1);
|
||||
assert(merge_context_.GetNumOperands() == keys_.size());
|
||||
std::string merge_result;
|
||||
s = TimedFullMerge(user_merge_operator_, orig_ikey.user_key, nullptr,
|
||||
operands_, &merge_result, logger_, stats_, env_);
|
||||
merge_context_.GetOperands(), &merge_result, logger_,
|
||||
stats_, env_);
|
||||
if (s.ok()) {
|
||||
// The original key encountered
|
||||
// We are certain that keys_ is not empty here (see assertions couple of
|
||||
@@ -231,9 +250,9 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
orig_ikey.type = kTypeValue;
|
||||
UpdateInternalKey(&original_key, orig_ikey.sequence, orig_ikey.type);
|
||||
keys_.clear();
|
||||
operands_.clear();
|
||||
merge_context_.Clear();
|
||||
keys_.emplace_front(std::move(original_key));
|
||||
operands_.emplace_front(std::move(merge_result));
|
||||
merge_context_.PushOperand(merge_result);
|
||||
}
|
||||
} else {
|
||||
// We haven't seen the beginning of the key nor a Put/Delete.
|
||||
@@ -244,8 +263,8 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
// partial merge returns Status::OK(). Should we change the status code
|
||||
// after a successful partial merge?
|
||||
s = Status::MergeInProgress();
|
||||
if (operands_.size() >= 2 &&
|
||||
operands_.size() >= min_partial_merge_operands_) {
|
||||
if (merge_context_.GetNumOperands() >= 2 &&
|
||||
merge_context_.GetNumOperands() >= min_partial_merge_operands_) {
|
||||
bool merge_success = false;
|
||||
std::string merge_result;
|
||||
{
|
||||
@@ -253,7 +272,8 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
merge_success = user_merge_operator_->PartialMergeMulti(
|
||||
orig_ikey.user_key,
|
||||
std::deque<Slice>(operands_.begin(), operands_.end()),
|
||||
std::deque<Slice>(merge_context_.GetOperands().begin(),
|
||||
merge_context_.GetOperands().end()),
|
||||
&merge_result, logger_);
|
||||
RecordTick(stats_, MERGE_OPERATION_TOTAL_TIME,
|
||||
stats_ ? timer.ElapsedNanosSafe() : 0);
|
||||
@@ -261,8 +281,8 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
if (merge_success) {
|
||||
// Merging of operands (associative merge) was successful.
|
||||
// Replace operands with the merge result
|
||||
operands_.clear();
|
||||
operands_.emplace_front(std::move(merge_result));
|
||||
merge_context_.Clear();
|
||||
merge_context_.PushOperand(merge_result);
|
||||
keys_.erase(keys_.begin(), keys_.end() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-8
@@ -8,8 +8,10 @@
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
@@ -42,14 +44,13 @@ class MergeHelper {
|
||||
latest_snapshot_(latest_snapshot),
|
||||
level_(level),
|
||||
keys_(),
|
||||
operands_(),
|
||||
filter_timer_(env_),
|
||||
total_filter_time_(0U),
|
||||
stats_(stats) {
|
||||
assert(user_comparator_ != nullptr);
|
||||
}
|
||||
|
||||
// Wrapper around MergeOperator::FullMerge() that records perf statistics.
|
||||
// Wrapper around MergeOperator::FullMergeV2() that records perf statistics.
|
||||
// Result of merge will be written to result if status returned is OK.
|
||||
// If operands is empty, the value will simply be copied to result.
|
||||
// Returns one of the following statuses:
|
||||
@@ -57,9 +58,10 @@ class MergeHelper {
|
||||
// - Corruption: Merge operator reported unsuccessful merge.
|
||||
static Status TimedFullMerge(const MergeOperator* merge_operator,
|
||||
const Slice& key, const Slice* value,
|
||||
const std::deque<std::string>& operands,
|
||||
const std::vector<Slice>& operands,
|
||||
std::string* result, Logger* logger,
|
||||
Statistics* statistics, Env* env);
|
||||
Statistics* statistics, Env* env,
|
||||
Slice* result_operand = nullptr);
|
||||
|
||||
// Merge entries until we hit
|
||||
// - a corrupted key
|
||||
@@ -116,7 +118,9 @@ class MergeHelper {
|
||||
// So keys().back() was the first key seen by iterator.
|
||||
// TODO: Re-style this comment to be like the first one
|
||||
const std::deque<std::string>& keys() const { return keys_; }
|
||||
const std::deque<std::string>& values() const { return operands_; }
|
||||
const std::vector<Slice>& values() const {
|
||||
return merge_context_.GetOperands();
|
||||
}
|
||||
uint64_t TotalFilterTime() const { return total_filter_time_; }
|
||||
bool HasOperator() const { return user_merge_operator_ != nullptr; }
|
||||
|
||||
@@ -133,8 +137,11 @@ class MergeHelper {
|
||||
|
||||
// the scratch area that holds the result of MergeUntil
|
||||
// valid up to the next MergeUntil call
|
||||
std::deque<std::string> keys_; // Keeps track of the sequence of keys seen
|
||||
std::deque<std::string> operands_; // Parallel with keys_; stores the values
|
||||
|
||||
// Keeps track of the sequence of keys seen
|
||||
std::deque<std::string> keys_;
|
||||
// Parallel with keys_; stores the operands
|
||||
mutable MergeContext merge_context_;
|
||||
|
||||
StopWatchNano filter_timer_;
|
||||
uint64_t total_filter_time_;
|
||||
@@ -159,7 +166,7 @@ class MergeOutputIterator {
|
||||
private:
|
||||
const MergeHelper* merge_helper_;
|
||||
std::deque<std::string>::const_reverse_iterator it_keys_;
|
||||
std::deque<std::string>::const_reverse_iterator it_values_;
|
||||
std::vector<Slice>::const_reverse_iterator it_values_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+21
-12
@@ -11,6 +11,18 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
bool MergeOperator::FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const {
|
||||
// If FullMergeV2 is not implemented, we convert the operand_list to
|
||||
// std::deque<std::string> and pass it to FullMerge
|
||||
std::deque<std::string> operand_list_str;
|
||||
for (auto& op : merge_in.operand_list) {
|
||||
operand_list_str.emplace_back(op.data(), op.size());
|
||||
}
|
||||
return FullMerge(merge_in.key, merge_in.existing_value, operand_list_str,
|
||||
&merge_out->new_value, merge_in.logger);
|
||||
}
|
||||
|
||||
// The default implementation of PartialMergeMulti, which invokes
|
||||
// PartialMerge multiple times internally and merges two operands at
|
||||
// a time.
|
||||
@@ -39,23 +51,20 @@ bool MergeOperator::PartialMergeMulti(const Slice& key,
|
||||
// Given a "real" merge from the library, call the user's
|
||||
// associative merge function one-by-one on each of the operands.
|
||||
// NOTE: It is assumed that the client's merge-operator will handle any errors.
|
||||
bool AssociativeMergeOperator::FullMerge(
|
||||
const Slice& key,
|
||||
const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const {
|
||||
|
||||
bool AssociativeMergeOperator::FullMergeV2(
|
||||
const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const {
|
||||
// Simply loop through the operands
|
||||
Slice temp_existing;
|
||||
for (const auto& operand : operand_list) {
|
||||
Slice value(operand);
|
||||
const Slice* existing_value = merge_in.existing_value;
|
||||
for (const auto& operand : merge_in.operand_list) {
|
||||
std::string temp_value;
|
||||
if (!Merge(key, existing_value, value, &temp_value, logger)) {
|
||||
if (!Merge(merge_in.key, existing_value, operand, &temp_value,
|
||||
merge_in.logger)) {
|
||||
return false;
|
||||
}
|
||||
swap(temp_value, *new_value);
|
||||
temp_existing = Slice(*new_value);
|
||||
swap(temp_value, merge_out->new_value);
|
||||
temp_existing = Slice(merge_out->new_value);
|
||||
existing_value = &temp_existing;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace rocksdb {
|
||||
class PinnedIteratorsManager {
|
||||
public:
|
||||
PinnedIteratorsManager() : pinning_enabled(false), pinned_iters_(nullptr) {}
|
||||
~PinnedIteratorsManager() { assert(!pinning_enabled); }
|
||||
~PinnedIteratorsManager() { ReleasePinnedIterators(); }
|
||||
|
||||
// Enable Iterators pinning
|
||||
void StartPinning() {
|
||||
@@ -43,7 +43,7 @@ class PinnedIteratorsManager {
|
||||
}
|
||||
|
||||
// Release pinned Iterators
|
||||
void ReleasePinnedIterators() {
|
||||
inline void ReleasePinnedIterators() {
|
||||
if (pinning_enabled) {
|
||||
pinning_enabled = false;
|
||||
|
||||
|
||||
@@ -324,27 +324,27 @@ class TestPlainTableFactory : public PlainTableFactory {
|
||||
column_family_id_(column_family_id),
|
||||
column_family_name_(std::move(column_family_name)) {}
|
||||
|
||||
Status NewTableReader(const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file,
|
||||
uint64_t file_size,
|
||||
unique_ptr<TableReader>* table) const override {
|
||||
Status NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table,
|
||||
bool prefetch_index_and_filter_in_cache) const override {
|
||||
TableProperties* props = nullptr;
|
||||
auto s =
|
||||
ReadTableProperties(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions.env,
|
||||
table_reader_options.ioptions.info_log, &props);
|
||||
table_reader_options.ioptions, &props);
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
if (store_index_in_file_) {
|
||||
BlockHandle bloom_block_handle;
|
||||
s = FindMetaBlock(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions.env,
|
||||
table_reader_options.ioptions,
|
||||
BloomBlockBuilder::kBloomBlock, &bloom_block_handle);
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
BlockHandle index_block_handle;
|
||||
s = FindMetaBlock(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions.env,
|
||||
table_reader_options.ioptions,
|
||||
PlainTableIndexBuilder::kPlainTableIndexBlock,
|
||||
&index_block_handle);
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
+80
-3
@@ -44,7 +44,7 @@ DEFINE_int32(max_write_buffer_number, 2, "");
|
||||
DEFINE_int32(min_write_buffer_number_to_merge, 1, "");
|
||||
DEFINE_int32(skiplist_height, 4, "");
|
||||
DEFINE_double(memtable_prefix_bloom_size_ratio, 0.1, "");
|
||||
DEFINE_int32(memtable_prefix_bloom_huge_page_tlb_size, 2 * 1024 * 1024, "");
|
||||
DEFINE_int32(memtable_huge_page_size, 2 * 1024 * 1024, "");
|
||||
DEFINE_int32(value_size, 40, "");
|
||||
|
||||
// Path to the database on file system
|
||||
@@ -146,6 +146,35 @@ std::string Get(DB* db, const ReadOptions& read_options, uint64_t prefix,
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class SamePrefixTransform : public SliceTransform {
|
||||
private:
|
||||
const Slice prefix_;
|
||||
std::string name_;
|
||||
|
||||
public:
|
||||
explicit SamePrefixTransform(const Slice& prefix)
|
||||
: prefix_(prefix), name_("rocksdb.SamePrefix." + prefix.ToString()) {}
|
||||
|
||||
virtual const char* Name() const override { return name_.c_str(); }
|
||||
|
||||
virtual Slice Transform(const Slice& src) const override {
|
||||
assert(InDomain(src));
|
||||
return prefix_;
|
||||
}
|
||||
|
||||
virtual bool InDomain(const Slice& src) const override {
|
||||
if (src.size() >= prefix_.size()) {
|
||||
return Slice(src.data(), prefix_.size()) == prefix_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& dst) const override {
|
||||
return dst == prefix_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class PrefixTest : public testing::Test {
|
||||
@@ -161,8 +190,7 @@ class PrefixTest : public testing::Test {
|
||||
|
||||
options.memtable_prefix_bloom_size_ratio =
|
||||
FLAGS_memtable_prefix_bloom_size_ratio;
|
||||
options.memtable_prefix_bloom_huge_page_tlb_size =
|
||||
FLAGS_memtable_prefix_bloom_huge_page_tlb_size;
|
||||
options.memtable_huge_page_size = FLAGS_memtable_huge_page_size;
|
||||
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
|
||||
BlockBasedTableOptions bbto;
|
||||
@@ -227,6 +255,55 @@ class PrefixTest : public testing::Test {
|
||||
Options options;
|
||||
};
|
||||
|
||||
TEST(SamePrefixTest, InDomainTest) {
|
||||
DB* db;
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(new SamePrefixTransform("HHKB"));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
WriteOptions write_options;
|
||||
ReadOptions read_options;
|
||||
{
|
||||
ASSERT_OK(DB::Open(options, kDbName, &db));
|
||||
ASSERT_OK(db->Put(write_options, "HHKB pro2", "Mar 24, 2006"));
|
||||
ASSERT_OK(db->Put(write_options, "HHKB pro2 Type-S", "June 29, 2011"));
|
||||
ASSERT_OK(db->Put(write_options, "Realforce 87u", "idk"));
|
||||
db->Flush(FlushOptions());
|
||||
std::string result;
|
||||
auto db_iter = db->NewIterator(ReadOptions());
|
||||
|
||||
db_iter->Seek("Realforce 87u");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
ASSERT_EQ(db_iter->key(), "Realforce 87u");
|
||||
ASSERT_EQ(db_iter->value(), "idk");
|
||||
|
||||
delete db_iter;
|
||||
delete db;
|
||||
ASSERT_OK(DestroyDB(kDbName, Options()));
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_OK(DB::Open(options, kDbName, &db));
|
||||
ASSERT_OK(db->Put(write_options, "pikachu", "1"));
|
||||
ASSERT_OK(db->Put(write_options, "Meowth", "1"));
|
||||
ASSERT_OK(db->Put(write_options, "Mewtwo", "idk"));
|
||||
db->Flush(FlushOptions());
|
||||
std::string result;
|
||||
auto db_iter = db->NewIterator(ReadOptions());
|
||||
|
||||
db_iter->Seek("Mewtwo");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
delete db_iter;
|
||||
delete db;
|
||||
ASSERT_OK(DestroyDB(kDbName, Options()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefixTest, TestResult) {
|
||||
for (int num_buckets = 1; num_buckets <= 2; num_buckets++) {
|
||||
FirstOption();
|
||||
|
||||
+9
-7
@@ -89,17 +89,17 @@ Status TableCache::GetTableReader(
|
||||
const InternalKeyComparator& internal_comparator, const FileDescriptor& fd,
|
||||
bool sequential_mode, size_t readahead, bool record_read_stats,
|
||||
HistogramImpl* file_read_hist, unique_ptr<TableReader>* table_reader,
|
||||
bool skip_filters, int level) {
|
||||
bool skip_filters, int level, bool prefetch_index_and_filter_in_cache) {
|
||||
std::string fname =
|
||||
TableFileName(ioptions_.db_paths, fd.GetNumber(), fd.GetPathId());
|
||||
unique_ptr<RandomAccessFile> file;
|
||||
Status s = ioptions_.env->NewRandomAccessFile(fname, &file, env_options);
|
||||
|
||||
if (readahead > 0) {
|
||||
file = NewReadaheadRandomAccessFile(std::move(file), readahead);
|
||||
}
|
||||
RecordTick(ioptions_.statistics, NO_FILE_OPENS);
|
||||
if (s.ok()) {
|
||||
if (readahead > 0) {
|
||||
file = NewReadaheadRandomAccessFile(std::move(file), readahead);
|
||||
}
|
||||
if (!sequential_mode && ioptions_.advise_random_on_open) {
|
||||
file->Hint(RandomAccessFile::RANDOM);
|
||||
}
|
||||
@@ -111,7 +111,8 @@ Status TableCache::GetTableReader(
|
||||
s = ioptions_.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions_, env_options, internal_comparator,
|
||||
skip_filters, level),
|
||||
std::move(file_reader), fd.GetFileSize(), table_reader);
|
||||
std::move(file_reader), fd.GetFileSize(), table_reader,
|
||||
prefetch_index_and_filter_in_cache);
|
||||
TEST_SYNC_POINT("TableCache::GetTableReader:0");
|
||||
}
|
||||
return s;
|
||||
@@ -129,7 +130,8 @@ Status TableCache::FindTable(const EnvOptions& env_options,
|
||||
const FileDescriptor& fd, Cache::Handle** handle,
|
||||
const bool no_io, bool record_read_stats,
|
||||
HistogramImpl* file_read_hist, bool skip_filters,
|
||||
int level) {
|
||||
int level,
|
||||
bool prefetch_index_and_filter_in_cache) {
|
||||
PERF_TIMER_GUARD(find_table_nanos);
|
||||
Status s;
|
||||
uint64_t number = fd.GetNumber();
|
||||
@@ -146,7 +148,7 @@ Status TableCache::FindTable(const EnvOptions& env_options,
|
||||
s = GetTableReader(env_options, internal_comparator, fd,
|
||||
false /* sequential mode */, 0 /* readahead */,
|
||||
record_read_stats, file_read_hist, &table_reader,
|
||||
skip_filters, level);
|
||||
skip_filters, level, prefetch_index_and_filter_in_cache);
|
||||
if (!s.ok()) {
|
||||
assert(table_reader == nullptr);
|
||||
RecordTick(ioptions_.statistics, NO_FILE_ERRORS);
|
||||
|
||||
+4
-2
@@ -79,7 +79,8 @@ class TableCache {
|
||||
const FileDescriptor& file_fd, Cache::Handle**,
|
||||
const bool no_io = false, bool record_read_stats = true,
|
||||
HistogramImpl* file_read_hist = nullptr,
|
||||
bool skip_filters = false, int level = -1);
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true);
|
||||
|
||||
// Get TableReader from a cache handle.
|
||||
TableReader* GetTableReaderFromHandle(Cache::Handle* handle);
|
||||
@@ -114,7 +115,8 @@ class TableCache {
|
||||
size_t readahead, bool record_read_stats,
|
||||
HistogramImpl* file_read_hist,
|
||||
unique_ptr<TableReader>* table_reader,
|
||||
bool skip_filters = false, int level = -1);
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true);
|
||||
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
const EnvOptions& env_options_;
|
||||
|
||||
@@ -276,7 +276,7 @@ void TestCustomizedTablePropertiesCollector(
|
||||
new test::StringSource(fwf->contents())));
|
||||
TableProperties* props;
|
||||
Status s = ReadTableProperties(fake_file_reader.get(), fwf->contents().size(),
|
||||
magic_number, Env::Default(), nullptr, &props);
|
||||
magic_number, ioptions, &props);
|
||||
std::unique_ptr<TableProperties> props_guard(props);
|
||||
ASSERT_OK(s);
|
||||
|
||||
@@ -417,7 +417,7 @@ void TestInternalKeyPropertiesCollector(
|
||||
TableProperties* props;
|
||||
Status s =
|
||||
ReadTableProperties(reader.get(), fwf->contents().size(), magic_number,
|
||||
Env::Default(), nullptr, &props);
|
||||
ioptions, &props);
|
||||
ASSERT_OK(s);
|
||||
|
||||
std::unique_ptr<TableProperties> props_guard(props);
|
||||
|
||||
+13
-9
@@ -287,7 +287,8 @@ class VersionBuilder::Rep {
|
||||
CheckConsistency(vstorage);
|
||||
}
|
||||
|
||||
void LoadTableHandlers(InternalStats* internal_stats, int max_threads) {
|
||||
void LoadTableHandlers(InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache) {
|
||||
assert(table_cache_ != nullptr);
|
||||
// <file metadata, level>
|
||||
std::vector<std::pair<FileMetaData*, int>> files_meta;
|
||||
@@ -309,11 +310,12 @@ class VersionBuilder::Rep {
|
||||
|
||||
auto* file_meta = files_meta[file_idx].first;
|
||||
int level = files_meta[file_idx].second;
|
||||
table_cache_->FindTable(
|
||||
env_options_, *(base_vstorage_->InternalComparator()),
|
||||
file_meta->fd, &file_meta->table_reader_handle, false /*no_io */,
|
||||
true /* record_read_stats */,
|
||||
internal_stats->GetFileReadHist(level), false, level);
|
||||
table_cache_->FindTable(env_options_,
|
||||
*(base_vstorage_->InternalComparator()),
|
||||
file_meta->fd, &file_meta->table_reader_handle,
|
||||
false /*no_io */, true /* record_read_stats */,
|
||||
internal_stats->GetFileReadHist(level), false,
|
||||
level, prefetch_index_and_filter_in_cache);
|
||||
if (file_meta->table_reader_handle != nullptr) {
|
||||
// Load table_reader
|
||||
file_meta->fd.table_reader = table_cache_->GetTableReaderFromHandle(
|
||||
@@ -363,9 +365,11 @@ void VersionBuilder::Apply(VersionEdit* edit) { rep_->Apply(edit); }
|
||||
void VersionBuilder::SaveTo(VersionStorageInfo* vstorage) {
|
||||
rep_->SaveTo(vstorage);
|
||||
}
|
||||
void VersionBuilder::LoadTableHandlers(InternalStats* internal_stats,
|
||||
int max_threads) {
|
||||
rep_->LoadTableHandlers(internal_stats, max_threads);
|
||||
void VersionBuilder::LoadTableHandlers(
|
||||
InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache) {
|
||||
rep_->LoadTableHandlers(internal_stats, max_threads,
|
||||
prefetch_index_and_filter_in_cache);
|
||||
}
|
||||
void VersionBuilder::MaybeAddFile(VersionStorageInfo* vstorage, int level,
|
||||
FileMetaData* f) {
|
||||
|
||||
@@ -31,7 +31,8 @@ class VersionBuilder {
|
||||
int level);
|
||||
void Apply(VersionEdit* edit);
|
||||
void SaveTo(VersionStorageInfo* vstorage);
|
||||
void LoadTableHandlers(InternalStats* internal_stats, int max_threads = 1);
|
||||
void LoadTableHandlers(InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache);
|
||||
void MaybeAddFile(VersionStorageInfo* vstorage, int level, FileMetaData* f);
|
||||
|
||||
private:
|
||||
|
||||
+15
-6
@@ -31,6 +31,7 @@
|
||||
#include "db/memtable.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_builder.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -597,8 +598,7 @@ Status Version::GetTableProperties(std::shared_ptr<const TableProperties>* tp,
|
||||
new RandomAccessFileReader(std::move(file)));
|
||||
s = ReadTableProperties(
|
||||
file_reader.get(), file_meta->fd.GetFileSize(),
|
||||
Footer::kInvalidTableMagicNumber /* table's magic number */, vset_->env_,
|
||||
ioptions->info_log, &raw_table_properties);
|
||||
Footer::kInvalidTableMagicNumber /* table's magic number */, *ioptions, &raw_table_properties);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -918,10 +918,17 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
*key_exists = true;
|
||||
}
|
||||
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
GetContext get_context(
|
||||
user_comparator(), merge_operator_, info_log_, db_statistics_,
|
||||
status->ok() ? GetContext::kNotFound : GetContext::kMerge, user_key,
|
||||
value, value_found, merge_context, this->env_, seq);
|
||||
value, value_found, merge_context, this->env_, seq,
|
||||
merge_operator_ ? &pinned_iters_mgr : nullptr);
|
||||
|
||||
// Pin blocks that we read to hold merge operands
|
||||
if (merge_operator_) {
|
||||
pinned_iters_mgr.StartPinning();
|
||||
}
|
||||
|
||||
FilePicker fp(
|
||||
storage_info_.files_, user_key, ikey, &storage_info_.level_files_brief_,
|
||||
@@ -2265,7 +2272,8 @@ Status VersionSet::LogAndApply(ColumnFamilyData* column_family_data,
|
||||
// Need to do it out of the mutex.
|
||||
builder_guard->version_builder()->LoadTableHandlers(
|
||||
column_family_data->internal_stats(),
|
||||
column_family_data->ioptions()->optimize_filters_for_hits);
|
||||
column_family_data->ioptions()->optimize_filters_for_hits,
|
||||
true /* prefetch_index_and_filter_in_cache */);
|
||||
}
|
||||
|
||||
// This is fine because everything inside of this block is serialized --
|
||||
@@ -2707,8 +2715,9 @@ Status VersionSet::Recover(
|
||||
if (db_options_->max_open_files == -1) {
|
||||
// unlimited table cache. Pre-load table handle now.
|
||||
// Need to do it out of the mutex.
|
||||
builder->LoadTableHandlers(cfd->internal_stats(),
|
||||
db_options_->max_file_opening_threads);
|
||||
builder->LoadTableHandlers(
|
||||
cfd->internal_stats(), db_options_->max_file_opening_threads,
|
||||
false /* prefetch_index_and_filter_in_cache */);
|
||||
}
|
||||
|
||||
Version* v = new Version(cfd, this, current_version_number_++);
|
||||
|
||||
+4
-3
@@ -383,7 +383,7 @@ Status WalManager::ReadFirstRecord(const WalFileType type,
|
||||
Status s;
|
||||
if (type == kAliveLogFile) {
|
||||
std::string fname = LogFileName(db_options_.wal_dir, number);
|
||||
s = ReadFirstLine(fname, sequence);
|
||||
s = ReadFirstLine(fname, number, sequence);
|
||||
if (env_->FileExists(fname).ok() && !s.ok()) {
|
||||
// return any error that is not caused by non-existing file
|
||||
return s;
|
||||
@@ -394,7 +394,7 @@ Status WalManager::ReadFirstRecord(const WalFileType type,
|
||||
// check if the file got moved to archive.
|
||||
std::string archived_file =
|
||||
ArchivedLogFileName(db_options_.wal_dir, number);
|
||||
s = ReadFirstLine(archived_file, sequence);
|
||||
s = ReadFirstLine(archived_file, number, sequence);
|
||||
// maybe the file was deleted from archive dir. If that's the case, return
|
||||
// Status::OK(). The caller with identify this as empty file because
|
||||
// *sequence == 0
|
||||
@@ -413,6 +413,7 @@ Status WalManager::ReadFirstRecord(const WalFileType type,
|
||||
// the function returns status.ok() and sequence == 0 if the file exists, but is
|
||||
// empty
|
||||
Status WalManager::ReadFirstLine(const std::string& fname,
|
||||
const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
@@ -449,7 +450,7 @@ Status WalManager::ReadFirstLine(const std::string& fname,
|
||||
reporter.status = &status;
|
||||
reporter.ignore_error = !db_options_.paranoid_checks;
|
||||
log::Reader reader(db_options_.info_log, std::move(file_reader), &reporter,
|
||||
true /*checksum*/, 0 /*initial_offset*/, *sequence);
|
||||
true /*checksum*/, 0 /*initial_offset*/, number);
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
|
||||
|
||||
+4
-3
@@ -54,9 +54,9 @@ class WalManager {
|
||||
return ReadFirstRecord(type, number, sequence);
|
||||
}
|
||||
|
||||
Status TEST_ReadFirstLine(const std::string& fname,
|
||||
Status TEST_ReadFirstLine(const std::string& fname, const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
return ReadFirstLine(fname, sequence);
|
||||
return ReadFirstLine(fname, number, sequence);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -71,7 +71,8 @@ class WalManager {
|
||||
Status ReadFirstRecord(const WalFileType type, const uint64_t number,
|
||||
SequenceNumber* sequence);
|
||||
|
||||
Status ReadFirstLine(const std::string& fname, SequenceNumber* sequence);
|
||||
Status ReadFirstLine(const std::string& fname, const uint64_t number,
|
||||
SequenceNumber* sequence);
|
||||
|
||||
// ------- state from DBImpl ------
|
||||
const DBOptions& db_options_;
|
||||
|
||||
@@ -119,10 +119,11 @@ TEST_F(WalManagerTest, ReadFirstRecordCache) {
|
||||
ASSERT_OK(env_->NewWritableFile(path, &file, EnvOptions()));
|
||||
|
||||
SequenceNumber s;
|
||||
ASSERT_OK(wal_manager_->TEST_ReadFirstLine(path, &s));
|
||||
ASSERT_OK(wal_manager_->TEST_ReadFirstLine(path, 1 /* number */, &s));
|
||||
ASSERT_EQ(s, 0U);
|
||||
|
||||
ASSERT_OK(wal_manager_->TEST_ReadFirstRecord(kAliveLogFile, 1, &s));
|
||||
ASSERT_OK(
|
||||
wal_manager_->TEST_ReadFirstRecord(kAliveLogFile, 1 /* number */, &s));
|
||||
ASSERT_EQ(s, 0U);
|
||||
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
|
||||
+22
-12
@@ -694,6 +694,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
uint64_t log_number_ref_;
|
||||
DBImpl* db_;
|
||||
const bool concurrent_memtable_writes_;
|
||||
bool* has_valid_writes_;
|
||||
typedef std::map<MemTable*, MemTablePostProcessInfo> MemPostInfoMap;
|
||||
MemPostInfoMap mem_post_info_map_;
|
||||
// current recovered transaction we are rebuilding (recovery)
|
||||
@@ -704,7 +705,8 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
FlushScheduler* flush_scheduler,
|
||||
bool ignore_missing_column_families,
|
||||
uint64_t recovering_log_number, DB* db,
|
||||
bool concurrent_memtable_writes)
|
||||
bool concurrent_memtable_writes,
|
||||
bool* has_valid_writes = nullptr)
|
||||
: sequence_(sequence),
|
||||
cf_mems_(cf_mems),
|
||||
flush_scheduler_(flush_scheduler),
|
||||
@@ -713,6 +715,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
log_number_ref_(0),
|
||||
db_(reinterpret_cast<DBImpl*>(db)),
|
||||
concurrent_memtable_writes_(concurrent_memtable_writes),
|
||||
has_valid_writes_(has_valid_writes),
|
||||
rebuilding_trx_(nullptr) {
|
||||
assert(cf_mems_);
|
||||
}
|
||||
@@ -756,6 +759,10 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (has_valid_writes_ != nullptr) {
|
||||
*has_valid_writes_ = true;
|
||||
}
|
||||
|
||||
if (log_number_ref_ > 0) {
|
||||
cf_mems_->GetMemTable()->RefLogContainingPrepSection(log_number_ref_);
|
||||
}
|
||||
@@ -922,12 +929,10 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
auto merge_operator = moptions->merge_operator;
|
||||
assert(merge_operator);
|
||||
|
||||
std::deque<std::string> operands;
|
||||
operands.push_front(value.ToString());
|
||||
std::string new_value;
|
||||
|
||||
Status merge_status = MergeHelper::TimedFullMerge(
|
||||
merge_operator, key, &get_value_slice, operands, &new_value,
|
||||
merge_operator, key, &get_value_slice, {value}, &new_value,
|
||||
moptions->info_log, moptions->statistics, Env::Default());
|
||||
|
||||
if (!merge_status.ok()) {
|
||||
@@ -978,6 +983,9 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
// we are now iterating through a prepared section
|
||||
rebuilding_trx_ = new WriteBatch();
|
||||
if (has_valid_writes_ != nullptr) {
|
||||
*has_valid_writes_ = true;
|
||||
}
|
||||
} else {
|
||||
// in non-recovery we ignore prepare markers
|
||||
// and insert the values directly. making sure we have a
|
||||
@@ -1031,6 +1039,9 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
if (s.ok()) {
|
||||
db_->DeleteRecoveredTransaction(name.ToString());
|
||||
}
|
||||
if (has_valid_writes_ != nullptr) {
|
||||
*has_valid_writes_ = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// in non recovery we simply ignore this tag
|
||||
@@ -1114,16 +1125,15 @@ Status WriteBatchInternal::InsertInto(WriteThread::Writer* writer,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status WriteBatchInternal::InsertInto(const WriteBatch* batch,
|
||||
ColumnFamilyMemTables* memtables,
|
||||
FlushScheduler* flush_scheduler,
|
||||
bool ignore_missing_column_families,
|
||||
uint64_t log_number, DB* db,
|
||||
bool concurrent_memtable_writes,
|
||||
SequenceNumber* last_seq_used) {
|
||||
Status WriteBatchInternal::InsertInto(
|
||||
const WriteBatch* batch, ColumnFamilyMemTables* memtables,
|
||||
FlushScheduler* flush_scheduler, bool ignore_missing_column_families,
|
||||
uint64_t log_number, DB* db, bool concurrent_memtable_writes,
|
||||
SequenceNumber* last_seq_used, bool* has_valid_writes) {
|
||||
MemTableInserter inserter(WriteBatchInternal::Sequence(batch), memtables,
|
||||
flush_scheduler, ignore_missing_column_families,
|
||||
log_number, db, concurrent_memtable_writes);
|
||||
log_number, db, concurrent_memtable_writes,
|
||||
has_valid_writes);
|
||||
Status s = batch->Iterate(&inserter);
|
||||
if (last_seq_used != nullptr) {
|
||||
*last_seq_used = inserter.get_final_sequence();
|
||||
|
||||
@@ -160,7 +160,8 @@ class WriteBatchInternal {
|
||||
bool ignore_missing_column_families = false,
|
||||
uint64_t log_number = 0, DB* db = nullptr,
|
||||
bool concurrent_memtable_writes = false,
|
||||
SequenceNumber* last_seq_used = nullptr);
|
||||
SequenceNumber* last_seq_used = nullptr,
|
||||
bool* has_valid_writes = nullptr);
|
||||
|
||||
static Status InsertInto(WriteThread::Writer* writer,
|
||||
ColumnFamilyMemTables* memtables,
|
||||
|
||||
@@ -10,19 +10,18 @@
|
||||
|
||||
class MyMerge : public rocksdb::MergeOperator {
|
||||
public:
|
||||
bool FullMerge(const rocksdb::Slice& key,
|
||||
const rocksdb::Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
rocksdb::Logger* logger) const override {
|
||||
new_value->clear();
|
||||
if (existing_value != nullptr) {
|
||||
new_value->assign(existing_value->data(), existing_value->size());
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
merge_out->new_value.clear();
|
||||
if (merge_in.existing_value != nullptr) {
|
||||
merge_out->new_value.assign(merge_in.existing_value->data(),
|
||||
merge_in.existing_value->size());
|
||||
}
|
||||
for (const std::string& m : operand_list) {
|
||||
fprintf(stderr, "Merge(%s)\n", m.c_str());
|
||||
assert(m != "bad"); // the compaction filter filters out bad values
|
||||
new_value->assign(m);
|
||||
for (const rocksdb::Slice& m : merge_in.operand_list) {
|
||||
fprintf(stderr, "Merge(%s)\n", m.ToString().c_str());
|
||||
// the compaction filter filters out bad values
|
||||
assert(m.ToString() != "bad");
|
||||
merge_out->new_value.assign(m.data(), m.size());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+2
-2
@@ -678,8 +678,8 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_memtable_prefix_bloom_bits(
|
||||
rocksdb_options_t*, uint32_t);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_memtable_prefix_bloom_probes(rocksdb_options_t*, uint32_t);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_memtable_prefix_bloom_huge_page_tlb_size(rocksdb_options_t*, size_t);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_memtable_huge_page_size(
|
||||
rocksdb_options_t*, size_t);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_successive_merges(
|
||||
rocksdb_options_t*, size_t);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
@@ -32,7 +33,7 @@ class Logger;
|
||||
// into rocksdb); numeric addition and string concatenation are examples;
|
||||
//
|
||||
// b) MergeOperator - the generic class for all the more abstract / complex
|
||||
// operations; one method (FullMerge) to merge a Put/Delete value with a
|
||||
// operations; one method (FullMergeV2) to merge a Put/Delete value with a
|
||||
// merge operand; and another method (PartialMerge) that merges multiple
|
||||
// operands together. this is especially useful if your key values have
|
||||
// complex structures but you would still like to support client-specific
|
||||
@@ -69,7 +70,49 @@ class MergeOperator {
|
||||
const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const = 0;
|
||||
Logger* logger) const {
|
||||
// deprecated, please use FullMergeV2()
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct MergeOperationInput {
|
||||
explicit MergeOperationInput(const Slice& _key,
|
||||
const Slice* _existing_value,
|
||||
const std::vector<Slice>& _operand_list,
|
||||
Logger* _logger)
|
||||
: key(_key),
|
||||
existing_value(_existing_value),
|
||||
operand_list(_operand_list),
|
||||
logger(_logger) {}
|
||||
|
||||
// The key associated with the merge operation.
|
||||
const Slice& key;
|
||||
// The existing value of the current key, nullptr means that the
|
||||
// value dont exist.
|
||||
const Slice* existing_value;
|
||||
// A list of operands to apply.
|
||||
const std::vector<Slice>& operand_list;
|
||||
// Logger could be used by client to log any errors that happen during
|
||||
// the merge operation.
|
||||
Logger* logger;
|
||||
};
|
||||
|
||||
struct MergeOperationOutput {
|
||||
explicit MergeOperationOutput(std::string& _new_value,
|
||||
Slice& _existing_operand)
|
||||
: new_value(_new_value), existing_operand(_existing_operand) {}
|
||||
|
||||
// Client is responsible for filling the merge result here.
|
||||
std::string& new_value;
|
||||
// If the merge result is one of the existing operands (or existing_value),
|
||||
// client can set this field to the operand (or existing_value) instead of
|
||||
// using new_value.
|
||||
Slice& existing_operand;
|
||||
};
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const;
|
||||
|
||||
// This function performs merge(left_op, right_op)
|
||||
// when both the operands are themselves merge operation types
|
||||
@@ -99,7 +142,7 @@ class MergeOperator {
|
||||
// TODO: Presently there is no way to differentiate between error/corruption
|
||||
// and simply "return false". For now, the client should simply return
|
||||
// false in any case it cannot perform partial-merge, regardless of reason.
|
||||
// If there is corruption in the data, handle it in the FullMerge() function,
|
||||
// If there is corruption in the data, handle it in the FullMergeV2() function
|
||||
// and return false there. The default implementation of PartialMerge will
|
||||
// always return false.
|
||||
virtual bool PartialMerge(const Slice& key, const Slice& left_operand,
|
||||
@@ -171,11 +214,8 @@ class AssociativeMergeOperator : public MergeOperator {
|
||||
|
||||
private:
|
||||
// Default implementations of the MergeOperator functions
|
||||
virtual bool FullMerge(const Slice& key,
|
||||
const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override;
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override;
|
||||
|
||||
virtual bool PartialMerge(const Slice& key,
|
||||
const Slice& left_operand,
|
||||
|
||||
@@ -746,14 +746,17 @@ struct ColumnFamilyOptions {
|
||||
// Dynamically changeable through SetOptions() API
|
||||
double memtable_prefix_bloom_size_ratio;
|
||||
|
||||
// Page size for huge page TLB for bloom in memtable. If <=0, not allocate
|
||||
// from huge page TLB but from malloc.
|
||||
// Need to reserve huge pages for it to be allocated. For example:
|
||||
// Page size for huge page for the arena used by the memtable. If <=0, it
|
||||
// won't allocate from huge page but from malloc.
|
||||
// Users are responsible to reserve huge pages for it to be allocated. For
|
||||
// example:
|
||||
// sysctl -w vm.nr_hugepages=20
|
||||
// See linux doc Documentation/vm/hugetlbpage.txt
|
||||
// If there isn't enough free huge page available, it will fall back to
|
||||
// malloc.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t memtable_prefix_bloom_huge_page_tlb_size;
|
||||
size_t memtable_huge_page_size;
|
||||
|
||||
// Control locality of bloom filter probes to improve cache miss rate.
|
||||
// This option only applies to memtable prefix bloom and plaintable
|
||||
|
||||
@@ -179,6 +179,11 @@ enum Tickers : uint32_t {
|
||||
NUMBER_SUPERVERSION_ACQUIRES,
|
||||
NUMBER_SUPERVERSION_RELEASES,
|
||||
NUMBER_SUPERVERSION_CLEANUPS,
|
||||
|
||||
// # of compressions/decompressions executed
|
||||
NUMBER_BLOCK_COMPRESSED,
|
||||
NUMBER_BLOCK_DECOMPRESSED,
|
||||
|
||||
NUMBER_BLOCK_NOT_COMPRESSED,
|
||||
MERGE_OPERATION_TOTAL_TIME,
|
||||
FILTER_OPERATION_TOTAL_TIME,
|
||||
@@ -211,6 +216,8 @@ const std::vector<std::pair<Tickers, std::string>> TickersNameMap = {
|
||||
{BLOCK_CACHE_BYTES_READ, "rocksdb.block.cache.bytes.read"},
|
||||
{BLOCK_CACHE_BYTES_WRITE, "rocksdb.block.cache.bytes.write"},
|
||||
{BLOOM_FILTER_USEFUL, "rocksdb.bloom.filter.useful"},
|
||||
{PERSISTENT_CACHE_HIT, "rocksdb.persistent.cache.hit"},
|
||||
{PERSISTENT_CACHE_MISS, "rocksdb.persistent.cache.miss"},
|
||||
{MEMTABLE_HIT, "rocksdb.memtable.hit"},
|
||||
{MEMTABLE_MISS, "rocksdb.memtable.miss"},
|
||||
{GET_HIT_L0, "rocksdb.l0.hit"},
|
||||
@@ -260,15 +267,18 @@ const std::vector<std::pair<Tickers, std::string>> TickersNameMap = {
|
||||
{WAL_FILE_BYTES, "rocksdb.wal.bytes"},
|
||||
{WRITE_DONE_BY_SELF, "rocksdb.write.self"},
|
||||
{WRITE_DONE_BY_OTHER, "rocksdb.write.other"},
|
||||
{WRITE_TIMEDOUT, "rocksdb.write.timeout"},
|
||||
{WRITE_WITH_WAL, "rocksdb.write.wal"},
|
||||
{FLUSH_WRITE_BYTES, "rocksdb.flush.write.bytes"},
|
||||
{COMPACT_READ_BYTES, "rocksdb.compact.read.bytes"},
|
||||
{COMPACT_WRITE_BYTES, "rocksdb.compact.write.bytes"},
|
||||
{FLUSH_WRITE_BYTES, "rocksdb.flush.write.bytes"},
|
||||
{NUMBER_DIRECT_LOAD_TABLE_PROPERTIES,
|
||||
"rocksdb.number.direct.load.table.properties"},
|
||||
{NUMBER_SUPERVERSION_ACQUIRES, "rocksdb.number.superversion_acquires"},
|
||||
{NUMBER_SUPERVERSION_RELEASES, "rocksdb.number.superversion_releases"},
|
||||
{NUMBER_SUPERVERSION_CLEANUPS, "rocksdb.number.superversion_cleanups"},
|
||||
{NUMBER_BLOCK_COMPRESSED, "rocksdb.number.block.compressed"},
|
||||
{NUMBER_BLOCK_DECOMPRESSED, "rocksdb.number.block.decompressed"},
|
||||
{NUMBER_BLOCK_NOT_COMPRESSED, "rocksdb.number.block.not_compressed"},
|
||||
{MERGE_OPERATION_TOTAL_TIME, "rocksdb.merge.operation.time.nanos"},
|
||||
{FILTER_OPERATION_TOTAL_TIME, "rocksdb.filter.operation.time.nanos"},
|
||||
@@ -313,6 +323,14 @@ enum Histograms : uint32_t {
|
||||
BYTES_PER_READ,
|
||||
BYTES_PER_WRITE,
|
||||
BYTES_PER_MULTIGET,
|
||||
|
||||
// number of bytes compressed/decompressed
|
||||
// number of bytes is when uncompressed; i.e. before/after respectively
|
||||
BYTES_COMPRESSED,
|
||||
BYTES_DECOMPRESSED,
|
||||
COMPRESSION_TIMES_NANOS,
|
||||
DECOMPRESSION_TIMES_NANOS,
|
||||
|
||||
HISTOGRAM_ENUM_MAX, // TODO(ldemailly): enforce HistogramsNameMap match
|
||||
};
|
||||
|
||||
@@ -343,6 +361,10 @@ const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
|
||||
{BYTES_PER_READ, "rocksdb.bytes.per.read"},
|
||||
{BYTES_PER_WRITE, "rocksdb.bytes.per.write"},
|
||||
{BYTES_PER_MULTIGET, "rocksdb.bytes.per.multiget"},
|
||||
{BYTES_COMPRESSED, "rocksdb.bytes.compressed"},
|
||||
{BYTES_DECOMPRESSED, "rocksdb.bytes.decompressed"},
|
||||
{COMPRESSION_TIMES_NANOS, "rocksdb.compression.times.nanos"},
|
||||
{DECOMPRESSION_TIMES_NANOS, "rocksdb.decompression.times.nanos"},
|
||||
};
|
||||
|
||||
struct HistogramData {
|
||||
@@ -357,6 +379,9 @@ enum StatsLevel {
|
||||
// Collect all stats except the counters requiring to get time inside the
|
||||
// mutex lock.
|
||||
kExceptTimeForMutex,
|
||||
// Collect all stats expect time inside mutex lock AND time spent on
|
||||
// compression
|
||||
kExceptDetailedTimers,
|
||||
// Collect all stats, including measuring duration of mutex operations.
|
||||
// If getting time is expensive on the platform to run, it can
|
||||
// reduce scalability to more threads, especially for writes.
|
||||
|
||||
@@ -390,7 +390,8 @@ class TableFactory {
|
||||
virtual Status NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table_reader) const = 0;
|
||||
unique_ptr<TableReader>* table_reader,
|
||||
bool prefetch_index_and_filter_in_cache = true) const = 0;
|
||||
|
||||
// Return a table builder to write to a file for this table type.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
||||
// vim: ts=8 sw=2 smarttab
|
||||
#ifndef ROCKSDB_UTILITIES_ENV_LIBRADOS_H
|
||||
#define ROCKSDB_UTILITIES_ENV_LIBRADOS_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/utilities/env_mirror.h"
|
||||
|
||||
#include <rados/librados.hpp>
|
||||
|
||||
namespace rocksdb {
|
||||
class LibradosWritableFile;
|
||||
|
||||
class EnvLibrados : public EnvWrapper {
|
||||
public:
|
||||
// Create a brand new sequentially-readable file with the specified name.
|
||||
// On success, stores a pointer to the new file in *result and returns OK.
|
||||
// On failure stores nullptr in *result and returns non-OK. If the file does
|
||||
// not exist, returns a non-OK status.
|
||||
//
|
||||
// The returned file will only be accessed by one thread at a time.
|
||||
Status NewSequentialFile(
|
||||
const std::string& fname,
|
||||
std::unique_ptr<SequentialFile>* result,
|
||||
const EnvOptions& options);
|
||||
|
||||
// Create a brand new random access read-only file with the
|
||||
// specified name. On success, stores a pointer to the new file in
|
||||
// *result and returns OK. On failure stores nullptr in *result and
|
||||
// returns non-OK. If the file does not exist, returns a non-OK
|
||||
// status.
|
||||
//
|
||||
// The returned file may be concurrently accessed by multiple threads.
|
||||
Status NewRandomAccessFile(
|
||||
const std::string& fname,
|
||||
std::unique_ptr<RandomAccessFile>* result,
|
||||
const EnvOptions& options);
|
||||
|
||||
// Create an object that writes to a new file with the specified
|
||||
// name. Deletes any existing file with the same name and creates a
|
||||
// new file. On success, stores a pointer to the new file in
|
||||
// *result and returns OK. On failure stores nullptr in *result and
|
||||
// returns non-OK.
|
||||
//
|
||||
// The returned file will only be accessed by one thread at a time.
|
||||
Status NewWritableFile(
|
||||
const std::string& fname,
|
||||
std::unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& options);
|
||||
|
||||
// Reuse an existing file by renaming it and opening it as writable.
|
||||
Status ReuseWritableFile(
|
||||
const std::string& fname,
|
||||
const std::string& old_fname,
|
||||
std::unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& options);
|
||||
|
||||
// Create an object that represents a directory. Will fail if directory
|
||||
// doesn't exist. If the directory exists, it will open the directory
|
||||
// and create a new Directory object.
|
||||
//
|
||||
// On success, stores a pointer to the new Directory in
|
||||
// *result and returns OK. On failure stores nullptr in *result and
|
||||
// returns non-OK.
|
||||
Status NewDirectory(
|
||||
const std::string& name,
|
||||
std::unique_ptr<Directory>* result);
|
||||
|
||||
// Returns OK if the named file exists.
|
||||
// NotFound if the named file does not exist,
|
||||
// the calling process does not have permission to determine
|
||||
// whether this file exists, or if the path is invalid.
|
||||
// IOError if an IO Error was encountered
|
||||
Status FileExists(const std::string& fname);
|
||||
|
||||
// Store in *result the names of the children of the specified directory.
|
||||
// The names are relative to "dir".
|
||||
// Original contents of *results are dropped.
|
||||
Status GetChildren(const std::string& dir,
|
||||
std::vector<std::string>* result);
|
||||
|
||||
// Delete the named file.
|
||||
Status DeleteFile(const std::string& fname);
|
||||
|
||||
// Create the specified directory. Returns error if directory exists.
|
||||
Status CreateDir(const std::string& dirname);
|
||||
|
||||
// Creates directory if missing. Return Ok if it exists, or successful in
|
||||
// Creating.
|
||||
Status CreateDirIfMissing(const std::string& dirname);
|
||||
|
||||
// Delete the specified directory.
|
||||
Status DeleteDir(const std::string& dirname);
|
||||
|
||||
// Store the size of fname in *file_size.
|
||||
Status GetFileSize(const std::string& fname, uint64_t* file_size);
|
||||
|
||||
// Store the last modification time of fname in *file_mtime.
|
||||
Status GetFileModificationTime(const std::string& fname,
|
||||
uint64_t* file_mtime);
|
||||
// Rename file src to target.
|
||||
Status RenameFile(const std::string& src,
|
||||
const std::string& target);
|
||||
// Hard Link file src to target.
|
||||
Status LinkFile(const std::string& src, const std::string& target);
|
||||
|
||||
// Lock the specified file. Used to prevent concurrent access to
|
||||
// the same db by multiple processes. On failure, stores nullptr in
|
||||
// *lock and returns non-OK.
|
||||
//
|
||||
// On success, stores a pointer to the object that represents the
|
||||
// acquired lock in *lock and returns OK. The caller should call
|
||||
// UnlockFile(*lock) to release the lock. If the process exits,
|
||||
// the lock will be automatically released.
|
||||
//
|
||||
// If somebody else already holds the lock, finishes immediately
|
||||
// with a failure. I.e., this call does not wait for existing locks
|
||||
// to go away.
|
||||
//
|
||||
// May create the named file if it does not already exist.
|
||||
Status LockFile(const std::string& fname, FileLock** lock);
|
||||
|
||||
// Release the lock acquired by a previous successful call to LockFile.
|
||||
// REQUIRES: lock was returned by a successful LockFile() call
|
||||
// REQUIRES: lock has not already been unlocked.
|
||||
Status UnlockFile(FileLock* lock);
|
||||
|
||||
// Get full directory name for this db.
|
||||
Status GetAbsolutePath(const std::string& db_path,
|
||||
std::string* output_path);
|
||||
|
||||
// Generate unique id
|
||||
std::string GenerateUniqueId();
|
||||
|
||||
// Get default EnvLibrados
|
||||
static EnvLibrados* Default();
|
||||
|
||||
explicit EnvLibrados(const std::string& db_name,
|
||||
const std::string& config_path,
|
||||
const std::string& db_pool);
|
||||
|
||||
explicit EnvLibrados(const std::string& client_name, // first 3 parameters are for RADOS client init
|
||||
const std::string& cluster_name,
|
||||
const uint64_t flags,
|
||||
const std::string& db_name,
|
||||
const std::string& config_path,
|
||||
const std::string& db_pool,
|
||||
const std::string& wal_dir,
|
||||
const std::string& wal_pool,
|
||||
const uint64_t write_buffer_size);
|
||||
~EnvLibrados() {
|
||||
_rados.shutdown();
|
||||
}
|
||||
private:
|
||||
std::string _client_name;
|
||||
std::string _cluster_name;
|
||||
uint64_t _flags;
|
||||
std::string _db_name; // get from user, readable string; Also used as db_id for db metadata
|
||||
std::string _config_path;
|
||||
librados::Rados _rados; // RADOS client
|
||||
std::string _db_pool_name;
|
||||
librados::IoCtx _db_pool_ioctx; // IoCtx for connecting db_pool
|
||||
std::string _wal_dir; // WAL dir path
|
||||
std::string _wal_pool_name;
|
||||
librados::IoCtx _wal_pool_ioctx; // IoCtx for connecting wal_pool
|
||||
uint64_t _write_buffer_size; // WritableFile buffer max size
|
||||
|
||||
/* private function to communicate with rados */
|
||||
std::string _CreateFid();
|
||||
Status _GetFid(const std::string& fname, std::string& fid);
|
||||
Status _GetFid(const std::string& fname, std::string& fid, int fid_len);
|
||||
Status _RenameFid(const std::string& old_fname, const std::string& new_fname);
|
||||
Status _AddFid(const std::string& fname, const std::string& fid);
|
||||
Status _DelFid(const std::string& fname);
|
||||
Status _GetSubFnames(
|
||||
const std::string& dirname,
|
||||
std::vector<std::string> * result
|
||||
);
|
||||
librados::IoCtx* _GetIoctx(const std::string& prefix);
|
||||
friend class LibradosWritableFile;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace rocksdb {
|
||||
// Try to migrate DB created with old_opts to be use new_opts.
|
||||
// Multiple column families is not supported.
|
||||
// It is best-effort. No guarantee to succeed.
|
||||
// A full compaction may be executed.
|
||||
Status OptionChangeMigration(std::string dbname, const Options& old_opts,
|
||||
const Options& new_opts);
|
||||
} // namespace rocksdb
|
||||
@@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#define ROCKSDB_MAJOR 4
|
||||
#define ROCKSDB_MINOR 9
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_MINOR 11
|
||||
#define ROCKSDB_PATCH 2
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
+10
-2
@@ -150,8 +150,16 @@ if(NOT EXISTS ${JAVA_ASSERTJ_JAR})
|
||||
file(RENAME ${JAVA_TMP_JAR} ${JAVA_ASSERTJ_JAR})
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND javac -cp ${JAVA_TESTCLASSPATH} -d ${PROJECT_SOURCE_DIR}/java/classes ${PROJECT_SOURCE_DIR}/java/src/main/java/org/rocksdb/util/*.java ${PROJECT_SOURCE_DIR}/java/src/main/java/org/rocksdb/*.java ${PROJECT_SOURCE_DIR}/java/src/test/java/org/rocksdb/*.java)
|
||||
execute_process(COMMAND javah -cp ${PROJECT_SOURCE_DIR}/java/classes -d ${PROJECT_SOURCE_DIR}/java/include -jni ${NATIVE_JAVA_CLASSES})
|
||||
if(WIN32)
|
||||
set(JAVAC cmd /c javac)
|
||||
set(JAVAH cmd /c javah)
|
||||
else()
|
||||
set(JAVAC javac)
|
||||
set(JAVAH javah)
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${JAVAC} -cp ${JAVA_TESTCLASSPATH} -d ${PROJECT_SOURCE_DIR}/java/classes ${PROJECT_SOURCE_DIR}/java/src/main/java/org/rocksdb/util/*.java ${PROJECT_SOURCE_DIR}/java/src/main/java/org/rocksdb/*.java ${PROJECT_SOURCE_DIR}/java/src/test/java/org/rocksdb/*.java)
|
||||
execute_process(COMMAND ${JAVAH} -cp ${PROJECT_SOURCE_DIR}/java/classes -d ${PROJECT_SOURCE_DIR}/java/include -jni ${NATIVE_JAVA_CLASSES})
|
||||
add_library(rocksdbjni${ARTIFACT_SUFFIX} SHARED ${JNI_NATIVE_SOURCES})
|
||||
set_target_properties(rocksdbjni${ARTIFACT_SUFFIX} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/rocksdbjni${ARTIFACT_SUFFIX}.pdb")
|
||||
target_link_libraries(rocksdbjni${ARTIFACT_SUFFIX} rocksdblib${ARTIFACT_SUFFIX} ${LIBS})
|
||||
+3
-1
@@ -181,7 +181,9 @@ java_test: resolve_test_deps
|
||||
$(TEST_SRC)/org/rocksdb/*.java
|
||||
$(AM_V_at)javah -cp $(MAIN_CLASSES):$(TEST_CLASSES) -d $(NATIVE_INCLUDE) -jni $(NATIVE_JAVA_TEST_CLASSES)
|
||||
|
||||
test: java resolve_test_deps java_test
|
||||
test: java resolve_test_deps java_test run_test
|
||||
|
||||
run_test:
|
||||
java -ea -Xcheck:jni -Djava.library.path=target -cp "$(MAIN_CLASSES):$(TEST_CLASSES):$(JAVA_TESTCLASSPATH):target/*" org.rocksdb.test.RocksJunitRunner $(JAVA_TESTS)
|
||||
|
||||
db_bench: java
|
||||
|
||||
@@ -22,6 +22,30 @@ LoggerJniCallback::LoggerJniCallback(
|
||||
// across multiple method calls, so we create a global ref
|
||||
m_jLogger = env->NewGlobalRef(jlogger);
|
||||
m_jLogMethodId = LoggerJni::getLogMethodId(env);
|
||||
|
||||
jobject jdebug_level = InfoLogLevelJni::DEBUG_LEVEL(env);
|
||||
assert(jdebug_level != nullptr);
|
||||
m_jdebug_level = env->NewGlobalRef(jdebug_level);
|
||||
|
||||
jobject jinfo_level = InfoLogLevelJni::INFO_LEVEL(env);
|
||||
assert(jinfo_level != nullptr);
|
||||
m_jinfo_level = env->NewGlobalRef(jinfo_level);
|
||||
|
||||
jobject jwarn_level = InfoLogLevelJni::WARN_LEVEL(env);
|
||||
assert(jwarn_level != nullptr);
|
||||
m_jwarn_level = env->NewGlobalRef(jwarn_level);
|
||||
|
||||
jobject jerror_level = InfoLogLevelJni::ERROR_LEVEL(env);
|
||||
assert(jerror_level != nullptr);
|
||||
m_jerror_level = env->NewGlobalRef(jerror_level);
|
||||
|
||||
jobject jfatal_level = InfoLogLevelJni::FATAL_LEVEL(env);
|
||||
assert(jfatal_level != nullptr);
|
||||
m_jfatal_level = env->NewGlobalRef(jfatal_level);
|
||||
|
||||
jobject jheader_level = InfoLogLevelJni::HEADER_LEVEL(env);
|
||||
assert(jheader_level != nullptr);
|
||||
m_jheader_level = env->NewGlobalRef(jheader_level);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,31 +67,30 @@ void LoggerJniCallback::Logv(const char* format, va_list ap) {
|
||||
void LoggerJniCallback::Logv(const InfoLogLevel log_level,
|
||||
const char* format, va_list ap) {
|
||||
if (GetInfoLogLevel() <= log_level) {
|
||||
JNIEnv* env = getJniEnv();
|
||||
|
||||
// determine InfoLogLevel java enum instance
|
||||
jobject jlog_level;
|
||||
switch (log_level) {
|
||||
case rocksdb::InfoLogLevel::DEBUG_LEVEL:
|
||||
jlog_level = InfoLogLevelJni::DEBUG_LEVEL(env);
|
||||
jlog_level = m_jdebug_level;
|
||||
break;
|
||||
case rocksdb::InfoLogLevel::INFO_LEVEL:
|
||||
jlog_level = InfoLogLevelJni::INFO_LEVEL(env);
|
||||
jlog_level = m_jinfo_level;
|
||||
break;
|
||||
case rocksdb::InfoLogLevel::WARN_LEVEL:
|
||||
jlog_level = InfoLogLevelJni::WARN_LEVEL(env);
|
||||
jlog_level = m_jwarn_level;
|
||||
break;
|
||||
case rocksdb::InfoLogLevel::ERROR_LEVEL:
|
||||
jlog_level = InfoLogLevelJni::ERROR_LEVEL(env);
|
||||
jlog_level = m_jerror_level;
|
||||
break;
|
||||
case rocksdb::InfoLogLevel::FATAL_LEVEL:
|
||||
jlog_level = InfoLogLevelJni::FATAL_LEVEL(env);
|
||||
jlog_level = m_jfatal_level;
|
||||
break;
|
||||
case rocksdb::InfoLogLevel::HEADER_LEVEL:
|
||||
jlog_level = InfoLogLevelJni::HEADER_LEVEL(env);
|
||||
jlog_level = m_jheader_level;
|
||||
break;
|
||||
default:
|
||||
jlog_level = InfoLogLevelJni::FATAL_LEVEL(env);
|
||||
jlog_level = m_jfatal_level;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -104,6 +127,8 @@ void LoggerJniCallback::Logv(const InfoLogLevel log_level,
|
||||
assert(p < limit);
|
||||
*p++ = '\0';
|
||||
|
||||
JNIEnv* env = getJniEnv();
|
||||
|
||||
// pass java string to callback handler
|
||||
env->CallVoidMethod(
|
||||
m_jLogger,
|
||||
@@ -123,6 +148,14 @@ void LoggerJniCallback::Logv(const InfoLogLevel log_level,
|
||||
LoggerJniCallback::~LoggerJniCallback() {
|
||||
JNIEnv* env = getJniEnv();
|
||||
env->DeleteGlobalRef(m_jLogger);
|
||||
|
||||
env->DeleteGlobalRef(m_jdebug_level);
|
||||
env->DeleteGlobalRef(m_jinfo_level);
|
||||
env->DeleteGlobalRef(m_jwarn_level);
|
||||
env->DeleteGlobalRef(m_jerror_level);
|
||||
env->DeleteGlobalRef(m_jfatal_level);
|
||||
env->DeleteGlobalRef(m_jheader_level);
|
||||
|
||||
m_jvm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,12 @@ namespace rocksdb {
|
||||
JavaVM* m_jvm;
|
||||
jobject m_jLogger;
|
||||
jmethodID m_jLogMethodId;
|
||||
jobject m_jdebug_level;
|
||||
jobject m_jinfo_level;
|
||||
jobject m_jwarn_level;
|
||||
jobject m_jerror_level;
|
||||
jobject m_jfatal_level;
|
||||
jobject m_jheader_level;
|
||||
};
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
+109
-2
@@ -1104,7 +1104,8 @@ std::vector<rocksdb::CompressionType> rocksdb_compression_vector_helper(
|
||||
*/
|
||||
jbyteArray rocksdb_compression_list_helper(JNIEnv* env,
|
||||
std::vector<rocksdb::CompressionType> compressionLevels) {
|
||||
jbyte jbuf[compressionLevels.size()];
|
||||
std::unique_ptr<jbyte[]> jbuf =
|
||||
std::unique_ptr<jbyte[]>(new jbyte[compressionLevels.size()]);
|
||||
for (std::vector<rocksdb::CompressionType>::size_type i = 0;
|
||||
i != compressionLevels.size(); i++) {
|
||||
jbuf[i] = compressionLevels[i];
|
||||
@@ -1113,7 +1114,7 @@ jbyteArray rocksdb_compression_list_helper(JNIEnv* env,
|
||||
jbyteArray jcompressionLevels = env->NewByteArray(
|
||||
static_cast<jsize>(compressionLevels.size()));
|
||||
env->SetByteArrayRegion(jcompressionLevels, 0,
|
||||
static_cast<jsize>(compressionLevels.size()), jbuf);
|
||||
static_cast<jsize>(compressionLevels.size()), jbuf.get());
|
||||
return jcompressionLevels;
|
||||
}
|
||||
|
||||
@@ -3924,6 +3925,90 @@ jboolean Java_org_rocksdb_ReadOptions_tailing(
|
||||
return reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->tailing;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: managed
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_ReadOptions_managed(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->managed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: setManaged
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_ReadOptions_setManaged(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jboolean jmanaged) {
|
||||
reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->managed =
|
||||
static_cast<bool>(jmanaged);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: totalOrderSeek
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_ReadOptions_totalOrderSeek(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->total_order_seek;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: setTotalOrderSeek
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_ReadOptions_setTotalOrderSeek(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jboolean jtotal_order_seek) {
|
||||
reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->total_order_seek =
|
||||
static_cast<bool>(jtotal_order_seek);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: prefixSameAsStart
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_ReadOptions_prefixSameAsStart(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->prefix_same_as_start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: setPrefixSameAsStart
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_ReadOptions_setPrefixSameAsStart(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jboolean jprefix_same_as_start) {
|
||||
reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->prefix_same_as_start =
|
||||
static_cast<bool>(jprefix_same_as_start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: pinData
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_ReadOptions_pinData(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->pin_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: setPinData
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_ReadOptions_setPinData(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jboolean jpin_data) {
|
||||
reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->pin_data =
|
||||
static_cast<bool>(jpin_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: setSnapshot
|
||||
@@ -3947,6 +4032,28 @@ jlong Java_org_rocksdb_ReadOptions_snapshot(
|
||||
return reinterpret_cast<jlong>(snapshot);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: readTier
|
||||
* Signature: (J)B
|
||||
*/
|
||||
jbyte Java_org_rocksdb_ReadOptions_readTier(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return static_cast<jbyte>(
|
||||
reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->read_tier);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ReadOptions
|
||||
* Method: setReadTier
|
||||
* Signature: (JB)V
|
||||
*/
|
||||
void Java_org_rocksdb_ReadOptions_setReadTier(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jbyte jread_tier) {
|
||||
reinterpret_cast<rocksdb::ReadOptions*>(jhandle)->read_tier =
|
||||
static_cast<rocksdb::ReadTier>(jread_tier);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// rocksdb::ComparatorOptions
|
||||
|
||||
|
||||
@@ -115,14 +115,15 @@ jlongArray rocksdb_open_helper(JNIEnv* env, jlong jopt_handle,
|
||||
// check if open operation was successful
|
||||
if (s.ok()) {
|
||||
jsize resultsLen = 1 + len_cols; //db handle + column family handles
|
||||
jlong results[resultsLen];
|
||||
std::unique_ptr<jlong[]> results =
|
||||
std::unique_ptr<jlong[]>(new jlong[resultsLen]);
|
||||
results[0] = reinterpret_cast<jlong>(db);
|
||||
for(int i = 1; i <= len_cols; i++) {
|
||||
results[i] = reinterpret_cast<jlong>(handles[i - 1]);
|
||||
}
|
||||
|
||||
jlongArray jresults = env->NewLongArray(resultsLen);
|
||||
env->SetLongArrayRegion(jresults, 0, resultsLen, results);
|
||||
env->SetLongArrayRegion(jresults, 0, resultsLen, results.get());
|
||||
return jresults;
|
||||
} else {
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "include/org_rocksdb_TtlDB.h"
|
||||
#include "rocksdb/utilities/db_ttl.h"
|
||||
@@ -95,14 +96,15 @@ jlongArray
|
||||
// check if open operation was successful
|
||||
if (s.ok()) {
|
||||
jsize resultsLen = 1 + len_cols; //db handle + column family handles
|
||||
jlong results[resultsLen];
|
||||
std::unique_ptr<jlong[]> results =
|
||||
std::unique_ptr<jlong[]>(new jlong[resultsLen]);
|
||||
results[0] = reinterpret_cast<jlong>(db);
|
||||
for(int i = 1; i <= len_cols; i++) {
|
||||
results[i] = reinterpret_cast<jlong>(handles[i - 1]);
|
||||
}
|
||||
|
||||
jlongArray jresults = env->NewLongArray(resultsLen);
|
||||
env->SetLongArrayRegion(jresults, 0, resultsLen, results);
|
||||
env->SetLongArrayRegion(jresults, 0, resultsLen, results.get());
|
||||
return jresults;
|
||||
} else {
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||
|
||||
@@ -87,6 +87,10 @@ public class NativeLibraryLoader {
|
||||
temp = File.createTempFile(tempFilePrefix, tempFileSuffix);
|
||||
} else {
|
||||
temp = new File(tmpDir, jniLibraryFileName);
|
||||
if (temp.exists() && !temp.delete()) {
|
||||
throw new RuntimeException("File: " + temp.getAbsolutePath()
|
||||
+ " already exists and cannot be removed.");
|
||||
}
|
||||
if (!temp.createNewFile()) {
|
||||
throw new RuntimeException("File: " + temp.getAbsolutePath()
|
||||
+ " could not be created.");
|
||||
|
||||
@@ -15,7 +15,6 @@ public class ReadOptions extends RocksObject {
|
||||
public ReadOptions() {
|
||||
super(newReadOptions());
|
||||
}
|
||||
private native static long newReadOptions();
|
||||
|
||||
/**
|
||||
* If true, all data read from underlying storage will be
|
||||
@@ -28,7 +27,6 @@ public class ReadOptions extends RocksObject {
|
||||
assert(isOwningHandle());
|
||||
return verifyChecksums(nativeHandle_);
|
||||
}
|
||||
private native boolean verifyChecksums(long handle);
|
||||
|
||||
/**
|
||||
* If true, all data read from underlying storage will be
|
||||
@@ -45,8 +43,6 @@ public class ReadOptions extends RocksObject {
|
||||
setVerifyChecksums(nativeHandle_, verifyChecksums);
|
||||
return this;
|
||||
}
|
||||
private native void setVerifyChecksums(
|
||||
long handle, boolean verifyChecksums);
|
||||
|
||||
// TODO(yhchiang): this option seems to be block-based table only.
|
||||
// move this to a better place?
|
||||
@@ -61,7 +57,6 @@ public class ReadOptions extends RocksObject {
|
||||
assert(isOwningHandle());
|
||||
return fillCache(nativeHandle_);
|
||||
}
|
||||
private native boolean fillCache(long handle);
|
||||
|
||||
/**
|
||||
* Fill the cache when loading the block-based sst formatted db.
|
||||
@@ -77,8 +72,21 @@ public class ReadOptions extends RocksObject {
|
||||
setFillCache(nativeHandle_, fillCache);
|
||||
return this;
|
||||
}
|
||||
private native void setFillCache(
|
||||
long handle, boolean fillCache);
|
||||
|
||||
/**
|
||||
* Returns the currently assigned Snapshot instance.
|
||||
*
|
||||
* @return the Snapshot assigned to this instance. If no Snapshot
|
||||
* is assigned null.
|
||||
*/
|
||||
public Snapshot snapshot() {
|
||||
assert(isOwningHandle());
|
||||
long snapshotHandle = snapshot(nativeHandle_);
|
||||
if (snapshotHandle != 0) {
|
||||
return new Snapshot(snapshotHandle);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>If "snapshot" is non-nullptr, read as of the supplied snapshot
|
||||
@@ -99,23 +107,30 @@ public class ReadOptions extends RocksObject {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
private native void setSnapshot(long handle, long snapshotHandle);
|
||||
|
||||
/**
|
||||
* Returns the currently assigned Snapshot instance.
|
||||
* Returns the current read tier.
|
||||
*
|
||||
* @return the Snapshot assigned to this instance. If no Snapshot
|
||||
* is assigned null.
|
||||
* @return the read tier in use, by default {@link ReadTier#READ_ALL_TIER}
|
||||
*/
|
||||
public Snapshot snapshot() {
|
||||
public ReadTier readTier() {
|
||||
assert(isOwningHandle());
|
||||
long snapshotHandle = snapshot(nativeHandle_);
|
||||
if (snapshotHandle != 0) {
|
||||
return new Snapshot(snapshotHandle);
|
||||
}
|
||||
return null;
|
||||
return ReadTier.getReadTier(readTier(nativeHandle_));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if this read request should process data that ALREADY
|
||||
* resides on a particular cache. If the required data is not
|
||||
* found at the specified cache, then {@link RocksDBException} is thrown.
|
||||
*
|
||||
* @param readTier {@link ReadTier} instance
|
||||
* @return the reference to the current ReadOptions.
|
||||
*/
|
||||
public ReadOptions setReadTier(final ReadTier readTier) {
|
||||
assert(isOwningHandle());
|
||||
setReadTier(nativeHandle_, readTier.getValue());
|
||||
return this;
|
||||
}
|
||||
private native long snapshot(long handle);
|
||||
|
||||
/**
|
||||
* Specify to create a tailing iterator -- a special iterator that has a
|
||||
@@ -132,7 +147,6 @@ public class ReadOptions extends RocksObject {
|
||||
assert(isOwningHandle());
|
||||
return tailing(nativeHandle_);
|
||||
}
|
||||
private native boolean tailing(long handle);
|
||||
|
||||
/**
|
||||
* Specify to create a tailing iterator -- a special iterator that has a
|
||||
@@ -150,8 +164,130 @@ public class ReadOptions extends RocksObject {
|
||||
setTailing(nativeHandle_, tailing);
|
||||
return this;
|
||||
}
|
||||
private native void setTailing(
|
||||
long handle, boolean tailing);
|
||||
|
||||
/**
|
||||
* Returns whether managed iterators will be used.
|
||||
*
|
||||
* @return the setting of whether managed iterators will be used, by default false
|
||||
*/
|
||||
public boolean managed() {
|
||||
assert(isOwningHandle());
|
||||
return managed(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify to create a managed iterator -- a special iterator that
|
||||
* uses less resources by having the ability to free its underlying
|
||||
* resources on request.
|
||||
*
|
||||
* @param managed if true, then managed iterators will be enabled.
|
||||
* @return the reference to the current ReadOptions.
|
||||
*/
|
||||
public ReadOptions setManaged(final boolean managed) {
|
||||
assert(isOwningHandle());
|
||||
setManaged(nativeHandle_, managed);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a total seek order will be used
|
||||
*
|
||||
* @return the setting of whether a total seek order will be used
|
||||
*/
|
||||
public boolean totalOrderSeek() {
|
||||
assert(isOwningHandle());
|
||||
return totalOrderSeek(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a total order seek regardless of index format (e.g. hash index)
|
||||
* used in the table. Some table format (e.g. plain table) may not support
|
||||
* this option.
|
||||
*
|
||||
* @param totalOrderSeek if true, then total order seek will be enabled.
|
||||
* @return the reference to the current ReadOptions.
|
||||
*/
|
||||
public ReadOptions setTotalOrderSeek(final boolean totalOrderSeek) {
|
||||
assert(isOwningHandle());
|
||||
setTotalOrderSeek(nativeHandle_, totalOrderSeek);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the iterator only iterates over the same prefix as the seek
|
||||
*
|
||||
* @return the setting of whether the iterator only iterates over the same
|
||||
* prefix as the seek, default is false
|
||||
*/
|
||||
public boolean prefixSameAsStart() {
|
||||
assert(isOwningHandle());
|
||||
return prefixSameAsStart(nativeHandle_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enforce that the iterator only iterates over the same prefix as the seek.
|
||||
* This option is effective only for prefix seeks, i.e. prefix_extractor is
|
||||
* non-null for the column family and {@link #totalOrderSeek()} is false.
|
||||
* Unlike iterate_upper_bound, {@link #setPrefixSameAsStart(boolean)} only
|
||||
* works within a prefix but in both directions.
|
||||
*
|
||||
* @param prefixSameAsStart if true, then the iterator only iterates over the
|
||||
* same prefix as the seek
|
||||
* @return the reference to the current ReadOptions.
|
||||
*/
|
||||
public ReadOptions setPrefixSameAsStart(final boolean prefixSameAsStart) {
|
||||
assert(isOwningHandle());
|
||||
setPrefixSameAsStart(nativeHandle_, prefixSameAsStart);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the blocks loaded by the iterator will be pinned in memory
|
||||
*
|
||||
* @return the setting of whether the blocks loaded by the iterator will be
|
||||
* pinned in memory
|
||||
*/
|
||||
public boolean pinData() {
|
||||
assert(isOwningHandle());
|
||||
return pinData(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the blocks loaded by the iterator pinned in memory as long as the
|
||||
* iterator is not deleted, If used when reading from tables created with
|
||||
* BlockBasedTableOptions::use_delta_encoding = false,
|
||||
* Iterator's property "rocksdb.iterator.is-key-pinned" is guaranteed to
|
||||
* return 1.
|
||||
*
|
||||
* @param pinData if true, the blocks loaded by the iterator will be pinned
|
||||
* @return the reference to the current ReadOptions.
|
||||
*/
|
||||
public ReadOptions setPinData(final boolean pinData) {
|
||||
assert(isOwningHandle());
|
||||
setPinData(nativeHandle_, pinData);
|
||||
return this;
|
||||
}
|
||||
|
||||
private native static long newReadOptions();
|
||||
private native boolean verifyChecksums(long handle);
|
||||
private native void setVerifyChecksums(long handle, boolean verifyChecksums);
|
||||
private native boolean fillCache(long handle);
|
||||
private native void setFillCache(long handle, boolean fillCache);
|
||||
private native long snapshot(long handle);
|
||||
private native void setSnapshot(long handle, long snapshotHandle);
|
||||
private native byte readTier(long handle);
|
||||
private native void setReadTier(long handle, byte readTierValue);
|
||||
private native boolean tailing(long handle);
|
||||
private native void setTailing(long handle, boolean tailing);
|
||||
private native boolean managed(long handle);
|
||||
private native void setManaged(long handle, boolean managed);
|
||||
private native boolean totalOrderSeek(long handle);
|
||||
private native void setTotalOrderSeek(long handle, boolean totalOrderSeek);
|
||||
private native boolean prefixSameAsStart(long handle);
|
||||
private native void setPrefixSameAsStart(long handle, boolean prefixSameAsStart);
|
||||
private native boolean pinData(long handle);
|
||||
private native void setPinData(long handle, boolean pinData);
|
||||
|
||||
@Override protected final native void disposeInternal(final long handle);
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* RocksDB {@link ReadOptions} read tiers.
|
||||
*/
|
||||
public enum ReadTier {
|
||||
READ_ALL_TIER((byte)0),
|
||||
BLOCK_CACHE_TIER((byte)1),
|
||||
PERSISTED_TIER((byte)2);
|
||||
|
||||
private final byte value;
|
||||
|
||||
ReadTier(final byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte value of the enumerations value
|
||||
*
|
||||
* @return byte representation
|
||||
*/
|
||||
public byte getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ReadTier by byte value.
|
||||
*
|
||||
* @param value byte representation of ReadTier.
|
||||
*
|
||||
* @return {@link org.rocksdb.ReadTier} instance or null.
|
||||
* @throws java.lang.IllegalArgumentException if an invalid
|
||||
* value is provided.
|
||||
*/
|
||||
public static ReadTier getReadTier(final byte value) {
|
||||
for (final ReadTier readTier : ReadTier.values()) {
|
||||
if (readTier.getValue() == value){
|
||||
return readTier;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Illegal value provided for ReadTier.");
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.rocksdb.util.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
|
||||
@@ -28,4 +29,13 @@ public class NativeLibraryLoaderTest {
|
||||
assertThat(Files.exists(path)).isTrue();
|
||||
assertThat(Files.isReadable(path)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overridesExistingLibrary() throws IOException {
|
||||
File first = NativeLibraryLoader.getInstance().loadLibraryFromJarToTemp(
|
||||
temporaryFolder.getRoot().getAbsolutePath());
|
||||
NativeLibraryLoader.getInstance().loadLibraryFromJarToTemp(
|
||||
temporaryFolder.getRoot().getAbsolutePath());
|
||||
assertThat(first.exists()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,46 @@ public class ReadOptionsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readTier() {
|
||||
try (final ReadOptions opt = new ReadOptions()) {
|
||||
opt.setReadTier(ReadTier.BLOCK_CACHE_TIER);
|
||||
assertThat(opt.readTier()).isEqualTo(ReadTier.BLOCK_CACHE_TIER);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void managed() {
|
||||
try (final ReadOptions opt = new ReadOptions()) {
|
||||
opt.setManaged(true);
|
||||
assertThat(opt.managed()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void totalOrderSeek() {
|
||||
try (final ReadOptions opt = new ReadOptions()) {
|
||||
opt.setTotalOrderSeek(true);
|
||||
assertThat(opt.totalOrderSeek()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixSameAsStart() {
|
||||
try (final ReadOptions opt = new ReadOptions()) {
|
||||
opt.setPrefixSameAsStart(true);
|
||||
assertThat(opt.prefixSameAsStart()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pinData() {
|
||||
try (final ReadOptions opt = new ReadOptions()) {
|
||||
opt.setPinData(true);
|
||||
assertThat(opt.pinData()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failSetVerifyChecksumUninitialized() {
|
||||
try (final ReadOptions readOptions =
|
||||
|
||||
@@ -131,9 +131,12 @@ LIB_SOURCES = \
|
||||
utilities/merge_operators/string_append/stringappend2.cc \
|
||||
utilities/merge_operators/string_append/stringappend.cc \
|
||||
utilities/merge_operators/uint64add.cc \
|
||||
utilities/option_change_migration/option_change_migration.cc \
|
||||
utilities/options/options_util.cc \
|
||||
utilities/persistent_cache/persistent_cache_tier.cc \
|
||||
utilities/persistent_cache/volatile_tier_impl.cc \
|
||||
utilities/persistent_cache/block_cache_tier_file.cc \
|
||||
utilities/persistent_cache/block_cache_tier_metadata.cc \
|
||||
utilities/redis/redis_lists.cc \
|
||||
utilities/simulator_cache/sim_cache.cc \
|
||||
utilities/spatialdb/spatial_db.cc \
|
||||
@@ -215,6 +218,7 @@ MAIN_SOURCES = \
|
||||
db/db_compaction_filter_test.cc \
|
||||
db/db_compaction_test.cc \
|
||||
db/db_dynamic_level_test.cc \
|
||||
db/db_flush_test.cc \
|
||||
db/db_inplace_update_test.cc \
|
||||
db/db_iterator_test.cc \
|
||||
db/db_log_iter_test.cc \
|
||||
@@ -274,6 +278,7 @@ MAIN_SOURCES = \
|
||||
util/env_test.cc \
|
||||
util/filelock_test.cc \
|
||||
util/histogram_test.cc \
|
||||
util/statistics_test.cc \
|
||||
utilities/backupable/backupable_db_test.cc \
|
||||
utilities/checkpoint/checkpoint_test.cc \
|
||||
utilities/document/document_db_test.cc \
|
||||
@@ -282,6 +287,7 @@ MAIN_SOURCES = \
|
||||
utilities/geodb/geodb_test.cc \
|
||||
utilities/memory/memory_test.cc \
|
||||
utilities/merge_operators/string_append/stringappend_test.cc \
|
||||
utilities/option_change_migration/option_change_migration_test.cc \
|
||||
utilities/options/options_util_test.cc \
|
||||
utilities/redis/redis_lists_test.cc \
|
||||
utilities/simulator_cache/sim_cache_test.cc \
|
||||
|
||||
@@ -43,7 +43,8 @@ extern const uint64_t kCuckooTableMagicNumber;
|
||||
Status AdaptiveTableFactory::NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table) const {
|
||||
unique_ptr<TableReader>* table,
|
||||
bool prefetch_index_and_filter_in_cache) const {
|
||||
Footer footer;
|
||||
auto s = ReadFooterFromFile(file.get(), file_size, &footer);
|
||||
if (!s.ok()) {
|
||||
|
||||
@@ -33,10 +33,11 @@ class AdaptiveTableFactory : public TableFactory {
|
||||
|
||||
const char* Name() const override { return "AdaptiveTableFactory"; }
|
||||
|
||||
Status NewTableReader(const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file,
|
||||
uint64_t file_size,
|
||||
unique_ptr<TableReader>* table) const override;
|
||||
Status NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table,
|
||||
bool prefetch_index_and_filter_in_cache = true) const override;
|
||||
|
||||
TableBuilder* NewTableBuilder(
|
||||
const TableBuilderOptions& table_builder_options,
|
||||
|
||||
@@ -162,6 +162,8 @@ class BlockIter : public InternalIterator {
|
||||
|
||||
virtual bool IsKeyPinned() const override { return key_pinned_; }
|
||||
|
||||
virtual bool IsValuePinned() const override { return true; }
|
||||
|
||||
private:
|
||||
const Comparator* comparator_;
|
||||
const char* data_; // underlying block contents
|
||||
|
||||
@@ -651,11 +651,16 @@ void BlockBasedTableBuilder::WriteBlock(const Slice& raw_block_contents,
|
||||
auto type = r->compression_type;
|
||||
Slice block_contents;
|
||||
bool abort_compression = false;
|
||||
|
||||
StopWatchNano timer(r->ioptions.env,
|
||||
ShouldReportDetailedTime(r->ioptions.env, r->ioptions.statistics));
|
||||
|
||||
if (raw_block_contents.size() < kCompressionSizeLimit) {
|
||||
Slice compression_dict;
|
||||
if (is_data_block && r->compression_dict && r->compression_dict->size()) {
|
||||
compression_dict = *r->compression_dict;
|
||||
}
|
||||
|
||||
block_contents = CompressBlock(raw_block_contents, r->compression_opts,
|
||||
&type, r->table_options.format_version,
|
||||
compression_dict, &r->compressed_output);
|
||||
@@ -668,7 +673,8 @@ void BlockBasedTableBuilder::WriteBlock(const Slice& raw_block_contents,
|
||||
BlockContents contents;
|
||||
Status stat = UncompressBlockContentsForCompressionType(
|
||||
block_contents.data(), block_contents.size(), &contents,
|
||||
r->table_options.format_version, compression_dict, type);
|
||||
r->table_options.format_version, compression_dict, type,
|
||||
r->ioptions);
|
||||
|
||||
if (stat.ok()) {
|
||||
bool compressed_ok = contents.data.compare(raw_block_contents) == 0;
|
||||
@@ -698,6 +704,15 @@ void BlockBasedTableBuilder::WriteBlock(const Slice& raw_block_contents,
|
||||
type = kNoCompression;
|
||||
block_contents = raw_block_contents;
|
||||
}
|
||||
else if (type != kNoCompression &&
|
||||
ShouldReportDetailedTime(r->ioptions.env,
|
||||
r->ioptions.statistics)) {
|
||||
MeasureTime(r->ioptions.statistics, COMPRESSION_TIMES_NANOS,
|
||||
timer.ElapsedNanos());
|
||||
MeasureTime(r->ioptions.statistics, BYTES_COMPRESSED,
|
||||
raw_block_contents.size());
|
||||
RecordTick(r->ioptions.statistics, NUMBER_BLOCK_COMPRESSED);
|
||||
}
|
||||
|
||||
WriteRawBlock(block_contents, type, handle);
|
||||
r->compressed_output.clear();
|
||||
|
||||
@@ -50,20 +50,12 @@ BlockBasedTableFactory::BlockBasedTableFactory(
|
||||
Status BlockBasedTableFactory::NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table_reader) const {
|
||||
return NewTableReader(table_reader_options, std::move(file), file_size,
|
||||
table_reader,
|
||||
/*prefetch_index_and_filter=*/true);
|
||||
}
|
||||
|
||||
Status BlockBasedTableFactory::NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table_reader, const bool prefetch_enabled) const {
|
||||
unique_ptr<TableReader>* table_reader,
|
||||
bool prefetch_index_and_filter_in_cache) const {
|
||||
return BlockBasedTable::Open(
|
||||
table_reader_options.ioptions, table_reader_options.env_options,
|
||||
table_options_, table_reader_options.internal_comparator, std::move(file),
|
||||
file_size, table_reader, prefetch_enabled,
|
||||
file_size, table_reader, prefetch_index_and_filter_in_cache,
|
||||
table_reader_options.skip_filters, table_reader_options.level);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,18 +33,11 @@ class BlockBasedTableFactory : public TableFactory {
|
||||
|
||||
const char* Name() const override { return "BlockBasedTable"; }
|
||||
|
||||
Status NewTableReader(const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file,
|
||||
uint64_t file_size,
|
||||
unique_ptr<TableReader>* table_reader) const override;
|
||||
|
||||
// This is a variant of virtual member function NewTableReader function with
|
||||
// added capability to disable pre-fetching of blocks on BlockBasedTable::Open
|
||||
Status NewTableReader(const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file,
|
||||
uint64_t file_size,
|
||||
unique_ptr<TableReader>* table_reader,
|
||||
bool prefetch_index_and_filter) const;
|
||||
Status NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table_reader,
|
||||
bool prefetch_index_and_filter_in_cache = true) const override;
|
||||
|
||||
TableBuilder* NewTableBuilder(
|
||||
const TableBuilderOptions& table_builder_options,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
@@ -41,6 +42,7 @@
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -60,14 +62,13 @@ namespace {
|
||||
// dictionary.
|
||||
Status ReadBlockFromFile(RandomAccessFileReader* file, const Footer& footer,
|
||||
const ReadOptions& options, const BlockHandle& handle,
|
||||
std::unique_ptr<Block>* result, Env* env,
|
||||
std::unique_ptr<Block>* result,
|
||||
const ImmutableCFOptions &ioptions,
|
||||
bool do_uncompress, const Slice& compression_dict,
|
||||
const PersistentCacheOptions& cache_options,
|
||||
Logger* info_log) {
|
||||
const PersistentCacheOptions& cache_options) {
|
||||
BlockContents contents;
|
||||
Status s = ReadBlockContents(file, footer, options, handle, &contents, env,
|
||||
do_uncompress, compression_dict, cache_options,
|
||||
info_log);
|
||||
Status s = ReadBlockContents(file, footer, options, handle, &contents, ioptions,
|
||||
do_uncompress, compression_dict, cache_options);
|
||||
if (s.ok()) {
|
||||
result->reset(new Block(std::move(contents)));
|
||||
}
|
||||
@@ -177,19 +178,18 @@ class BinarySearchIndexReader : public IndexReader {
|
||||
// On success, index_reader will be populated; otherwise it will remain
|
||||
// unmodified.
|
||||
static Status Create(RandomAccessFileReader* file, const Footer& footer,
|
||||
const BlockHandle& index_handle, Env* env,
|
||||
const BlockHandle& index_handle,
|
||||
const ImmutableCFOptions &ioptions,
|
||||
const Comparator* comparator, IndexReader** index_reader,
|
||||
const PersistentCacheOptions& cache_options,
|
||||
Statistics* statistics) {
|
||||
const PersistentCacheOptions& cache_options) {
|
||||
std::unique_ptr<Block> index_block;
|
||||
auto s = ReadBlockFromFile(file, footer, ReadOptions(), index_handle,
|
||||
&index_block, env, true /* decompress */,
|
||||
Slice() /*compression dict*/, cache_options,
|
||||
/*info_log*/ nullptr);
|
||||
&index_block, ioptions, true /* decompress */,
|
||||
Slice() /*compression dict*/, cache_options);
|
||||
|
||||
if (s.ok()) {
|
||||
*index_reader = new BinarySearchIndexReader(
|
||||
comparator, std::move(index_block), statistics);
|
||||
comparator, std::move(index_block), ioptions.statistics);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -226,15 +226,15 @@ class HashIndexReader : public IndexReader {
|
||||
public:
|
||||
static Status Create(
|
||||
const SliceTransform* hash_key_extractor, const Footer& footer,
|
||||
RandomAccessFileReader* file, Env* env, const Comparator* comparator,
|
||||
const BlockHandle& index_handle, InternalIterator* meta_index_iter,
|
||||
IndexReader** index_reader, bool hash_index_allow_collision,
|
||||
const PersistentCacheOptions& cache_options, Statistics* statistics) {
|
||||
RandomAccessFileReader* file, const ImmutableCFOptions &ioptions,
|
||||
const Comparator* comparator, const BlockHandle& index_handle,
|
||||
InternalIterator* meta_index_iter, IndexReader** index_reader,
|
||||
bool hash_index_allow_collision,
|
||||
const PersistentCacheOptions& cache_options) {
|
||||
std::unique_ptr<Block> index_block;
|
||||
auto s = ReadBlockFromFile(file, footer, ReadOptions(), index_handle,
|
||||
&index_block, env, true /* decompress */,
|
||||
Slice() /*compression dict*/, cache_options,
|
||||
/*info_log*/ nullptr);
|
||||
&index_block, ioptions, true /* decompress */,
|
||||
Slice() /*compression dict*/, cache_options);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -245,7 +245,8 @@ class HashIndexReader : public IndexReader {
|
||||
// So, Create will succeed regardless, from this point on.
|
||||
|
||||
auto new_index_reader =
|
||||
new HashIndexReader(comparator, std::move(index_block), statistics);
|
||||
new HashIndexReader(comparator, std::move(index_block),
|
||||
ioptions.statistics);
|
||||
*index_reader = new_index_reader;
|
||||
|
||||
// Get prefixes block
|
||||
@@ -269,14 +270,14 @@ class HashIndexReader : public IndexReader {
|
||||
// Read contents for the blocks
|
||||
BlockContents prefixes_contents;
|
||||
s = ReadBlockContents(file, footer, ReadOptions(), prefixes_handle,
|
||||
&prefixes_contents, env, true /* decompress */,
|
||||
&prefixes_contents, ioptions, true /* decompress */,
|
||||
Slice() /*compression dict*/, cache_options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
BlockContents prefixes_meta_contents;
|
||||
s = ReadBlockContents(file, footer, ReadOptions(), prefixes_meta_handle,
|
||||
&prefixes_meta_contents, env, true /* decompress */,
|
||||
&prefixes_meta_contents, ioptions, true /* decompress */,
|
||||
Slice() /*compression dict*/, cache_options);
|
||||
if (!s.ok()) {
|
||||
// TODO: log error
|
||||
@@ -515,7 +516,7 @@ Status BlockBasedTable::Open(const ImmutableCFOptions& ioptions,
|
||||
unique_ptr<RandomAccessFileReader>&& file,
|
||||
uint64_t file_size,
|
||||
unique_ptr<TableReader>* table_reader,
|
||||
const bool prefetch_index_and_filter,
|
||||
const bool prefetch_index_and_filter_in_cache,
|
||||
const bool skip_filters, const int level) {
|
||||
table_reader->reset();
|
||||
|
||||
@@ -533,12 +534,19 @@ Status BlockBasedTable::Open(const ImmutableCFOptions& ioptions,
|
||||
|
||||
// We've successfully read the footer and the index block: we're
|
||||
// ready to serve requests.
|
||||
// Better not mutate rep_ after the creation. eg. internal_prefix_transform
|
||||
// raw pointer will be used to create HashIndexReader, whose reset may
|
||||
// access a dangling pointer.
|
||||
Rep* rep = new BlockBasedTable::Rep(ioptions, env_options, table_options,
|
||||
internal_comparator, skip_filters);
|
||||
rep->file = std::move(file);
|
||||
rep->footer = footer;
|
||||
rep->index_type = table_options.index_type;
|
||||
rep->hash_index_allow_collision = table_options.hash_index_allow_collision;
|
||||
// We need to wrap data with internal_prefix_transform to make sure it can
|
||||
// handle prefix correctly.
|
||||
rep->internal_prefix_transform.reset(
|
||||
new InternalKeySliceTransform(rep->ioptions.prefix_extractor));
|
||||
SetupCacheKeyPrefix(rep, file_size);
|
||||
unique_ptr<BlockBasedTable> new_table(new BlockBasedTable(rep));
|
||||
|
||||
@@ -547,7 +555,7 @@ Status BlockBasedTable::Open(const ImmutableCFOptions& ioptions,
|
||||
PersistentCacheOptions(rep->table_options.persistent_cache,
|
||||
std::string(rep->persistent_cache_key_prefix,
|
||||
rep->persistent_cache_key_prefix_size),
|
||||
rep->ioptions.statistics);
|
||||
rep->ioptions.statistics);
|
||||
|
||||
// Read meta index
|
||||
std::unique_ptr<Block> meta;
|
||||
@@ -585,8 +593,7 @@ Status BlockBasedTable::Open(const ImmutableCFOptions& ioptions,
|
||||
TableProperties* table_properties = nullptr;
|
||||
if (s.ok()) {
|
||||
s = ReadProperties(meta_iter->value(), rep->file.get(), rep->footer,
|
||||
rep->ioptions.env, rep->ioptions.info_log,
|
||||
&table_properties);
|
||||
rep->ioptions, &table_properties);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
@@ -613,7 +620,7 @@ Status BlockBasedTable::Open(const ImmutableCFOptions& ioptions,
|
||||
// true.
|
||||
unique_ptr<BlockContents> compression_dict_block{new BlockContents()};
|
||||
s = rocksdb::ReadMetaBlock(rep->file.get(), file_size,
|
||||
kBlockBasedTableMagicNumber, rep->ioptions.env,
|
||||
kBlockBasedTableMagicNumber, rep->ioptions,
|
||||
rocksdb::kCompressionDictBlock,
|
||||
compression_dict_block.get());
|
||||
if (!s.ok()) {
|
||||
@@ -637,10 +644,11 @@ Status BlockBasedTable::Open(const ImmutableCFOptions& ioptions,
|
||||
BlockBasedTablePropertyNames::kPrefixFiltering, rep->ioptions.info_log);
|
||||
}
|
||||
|
||||
if (prefetch_index_and_filter) {
|
||||
// pre-fetching of blocks is turned on
|
||||
// Will use block cache for index/filter blocks access?
|
||||
if (table_options.cache_index_and_filter_blocks) {
|
||||
// Will use block cache for index/filter blocks access
|
||||
// Always prefetch index and filter for level 0
|
||||
if (table_options.cache_index_and_filter_blocks) {
|
||||
if (prefetch_index_and_filter_in_cache || level == 0) {
|
||||
assert(table_options.block_cache != nullptr);
|
||||
// Hack: Call NewIndexIterator() to implicitly add index to the
|
||||
// block_cache
|
||||
@@ -664,7 +672,7 @@ Status BlockBasedTable::Open(const ImmutableCFOptions& ioptions,
|
||||
// if pin_l0_filter_and_index_blocks_in_cache is true, and this is
|
||||
// a level0 file, then save it in rep_->filter_entry; it will be
|
||||
// released in the destructor only, hence it will be pinned in the
|
||||
// cache until this reader is alive
|
||||
// cache while this reader is alive
|
||||
if (rep->table_options.pin_l0_filter_and_index_blocks_in_cache &&
|
||||
level == 0) {
|
||||
rep->filter_entry = filter_entry;
|
||||
@@ -672,23 +680,23 @@ Status BlockBasedTable::Open(const ImmutableCFOptions& ioptions,
|
||||
filter_entry.Release(table_options.block_cache.get());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we don't use block cache for index/filter blocks access, we'll
|
||||
// pre-load these blocks, which will kept in member variables in Rep
|
||||
// and with a same life-time as this table object.
|
||||
IndexReader* index_reader = nullptr;
|
||||
s = new_table->CreateIndexReader(&index_reader, meta_iter.get());
|
||||
}
|
||||
} else {
|
||||
// If we don't use block cache for index/filter blocks access, we'll
|
||||
// pre-load these blocks, which will kept in member variables in Rep
|
||||
// and with a same life-time as this table object.
|
||||
IndexReader* index_reader = nullptr;
|
||||
s = new_table->CreateIndexReader(&index_reader, meta_iter.get());
|
||||
|
||||
if (s.ok()) {
|
||||
rep->index_reader.reset(index_reader);
|
||||
if (s.ok()) {
|
||||
rep->index_reader.reset(index_reader);
|
||||
|
||||
// Set filter block
|
||||
if (rep->filter_policy) {
|
||||
rep->filter.reset(ReadFilter(rep));
|
||||
}
|
||||
} else {
|
||||
delete index_reader;
|
||||
// Set filter block
|
||||
if (rep->filter_policy) {
|
||||
rep->filter.reset(ReadFilter(rep));
|
||||
}
|
||||
} else {
|
||||
delete index_reader;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,9 +753,9 @@ Status BlockBasedTable::ReadMetaBlock(Rep* rep,
|
||||
std::unique_ptr<Block> meta;
|
||||
Status s = ReadBlockFromFile(
|
||||
rep->file.get(), rep->footer, ReadOptions(),
|
||||
rep->footer.metaindex_handle(), &meta, rep->ioptions.env,
|
||||
rep->footer.metaindex_handle(), &meta, rep->ioptions,
|
||||
true /* decompress */, Slice() /*compression dict*/,
|
||||
rep->persistent_cache_options, rep->ioptions.info_log);
|
||||
rep->persistent_cache_options);
|
||||
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, rep->ioptions.info_log,
|
||||
@@ -764,13 +772,14 @@ Status BlockBasedTable::ReadMetaBlock(Rep* rep,
|
||||
|
||||
Status BlockBasedTable::GetDataBlockFromCache(
|
||||
const Slice& block_cache_key, const Slice& compressed_block_cache_key,
|
||||
Cache* block_cache, Cache* block_cache_compressed, Statistics* statistics,
|
||||
const ReadOptions& read_options,
|
||||
Cache* block_cache, Cache* block_cache_compressed,
|
||||
const ImmutableCFOptions &ioptions, const ReadOptions& read_options,
|
||||
BlockBasedTable::CachableEntry<Block>* block, uint32_t format_version,
|
||||
const Slice& compression_dict) {
|
||||
Status s;
|
||||
Block* compressed_block = nullptr;
|
||||
Cache::Handle* block_cache_compressed_handle = nullptr;
|
||||
Statistics* statistics = ioptions.statistics;
|
||||
|
||||
// Lookup uncompressed cache first
|
||||
if (block_cache != nullptr) {
|
||||
@@ -811,7 +820,8 @@ Status BlockBasedTable::GetDataBlockFromCache(
|
||||
BlockContents contents;
|
||||
s = UncompressBlockContents(compressed_block->data(),
|
||||
compressed_block->size(), &contents,
|
||||
format_version, compression_dict);
|
||||
format_version, compression_dict,
|
||||
ioptions);
|
||||
|
||||
// Insert uncompressed block into block cache
|
||||
if (s.ok()) {
|
||||
@@ -840,7 +850,7 @@ Status BlockBasedTable::GetDataBlockFromCache(
|
||||
Status BlockBasedTable::PutDataBlockToCache(
|
||||
const Slice& block_cache_key, const Slice& compressed_block_cache_key,
|
||||
Cache* block_cache, Cache* block_cache_compressed,
|
||||
const ReadOptions& read_options, Statistics* statistics,
|
||||
const ReadOptions& read_options, const ImmutableCFOptions &ioptions,
|
||||
CachableEntry<Block>* block, Block* raw_block, uint32_t format_version,
|
||||
const Slice& compression_dict) {
|
||||
assert(raw_block->compression_type() == kNoCompression ||
|
||||
@@ -849,9 +859,10 @@ Status BlockBasedTable::PutDataBlockToCache(
|
||||
Status s;
|
||||
// Retrieve the uncompressed contents into a new buffer
|
||||
BlockContents contents;
|
||||
Statistics* statistics = ioptions.statistics;
|
||||
if (raw_block->compression_type() != kNoCompression) {
|
||||
s = UncompressBlockContents(raw_block->data(), raw_block->size(), &contents,
|
||||
format_version, compression_dict);
|
||||
format_version, compression_dict, ioptions);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
delete raw_block;
|
||||
@@ -913,7 +924,7 @@ FilterBlockReader* BlockBasedTable::ReadFilter(Rep* rep) {
|
||||
}
|
||||
BlockContents block;
|
||||
if (!ReadBlockContents(rep->file.get(), rep->footer, ReadOptions(),
|
||||
rep->filter_handle, &block, rep->ioptions.env,
|
||||
rep->filter_handle, &block, rep->ioptions,
|
||||
false /* decompress */, Slice() /*compression dict*/,
|
||||
rep->persistent_cache_options)
|
||||
.ok()) {
|
||||
@@ -1050,7 +1061,11 @@ InternalIterator* BlockBasedTable::NewIndexIterator(
|
||||
} else {
|
||||
// Create index reader and put it in the cache.
|
||||
Status s;
|
||||
TEST_SYNC_POINT("BlockBasedTable::NewIndexIterator::thread2:2");
|
||||
s = CreateIndexReader(&index_reader);
|
||||
TEST_SYNC_POINT("BlockBasedTable::NewIndexIterator::thread1:1");
|
||||
TEST_SYNC_POINT("BlockBasedTable::NewIndexIterator::thread2:3");
|
||||
TEST_SYNC_POINT("BlockBasedTable::NewIndexIterator::thread1:4");
|
||||
if (s.ok()) {
|
||||
assert(index_reader != nullptr);
|
||||
s = block_cache->Insert(key, index_reader, index_reader->usable_size(),
|
||||
@@ -1148,7 +1163,7 @@ InternalIterator* BlockBasedTable::NewDataBlockIterator(
|
||||
}
|
||||
|
||||
s = GetDataBlockFromCache(
|
||||
key, ckey, block_cache, block_cache_compressed, statistics, ro, &block,
|
||||
key, ckey, block_cache, block_cache_compressed, rep->ioptions, ro, &block,
|
||||
rep->table_options.format_version, compression_dict);
|
||||
|
||||
if (block.value == nullptr && !no_io && ro.fill_cache) {
|
||||
@@ -1156,15 +1171,14 @@ InternalIterator* BlockBasedTable::NewDataBlockIterator(
|
||||
{
|
||||
StopWatch sw(rep->ioptions.env, statistics, READ_BLOCK_GET_MICROS);
|
||||
s = ReadBlockFromFile(rep->file.get(), rep->footer, ro, handle,
|
||||
&raw_block, rep->ioptions.env,
|
||||
&raw_block, rep->ioptions,
|
||||
block_cache_compressed == nullptr,
|
||||
compression_dict, rep->persistent_cache_options,
|
||||
rep->ioptions.info_log);
|
||||
compression_dict, rep->persistent_cache_options);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
s = PutDataBlockToCache(key, ckey, block_cache, block_cache_compressed,
|
||||
ro, statistics, &block, raw_block.release(),
|
||||
ro, rep->ioptions, &block, raw_block.release(),
|
||||
rep->table_options.format_version,
|
||||
compression_dict);
|
||||
}
|
||||
@@ -1184,9 +1198,8 @@ InternalIterator* BlockBasedTable::NewDataBlockIterator(
|
||||
}
|
||||
std::unique_ptr<Block> block_value;
|
||||
s = ReadBlockFromFile(rep->file.get(), rep->footer, ro, handle,
|
||||
&block_value, rep->ioptions.env, true /* compress */,
|
||||
compression_dict, rep->persistent_cache_options,
|
||||
rep->ioptions.info_log);
|
||||
&block_value, rep->ioptions, true /* compress */,
|
||||
compression_dict, rep->persistent_cache_options);
|
||||
if (s.ok()) {
|
||||
block.value = block_value.release();
|
||||
}
|
||||
@@ -1382,6 +1395,10 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
||||
BlockIter iiter;
|
||||
NewIndexIterator(read_options, &iiter);
|
||||
|
||||
PinnedIteratorsManager* pinned_iters_mgr = get_context->pinned_iters_mgr();
|
||||
bool pin_blocks = pinned_iters_mgr && pinned_iters_mgr->PinningEnabled();
|
||||
BlockIter* biter = nullptr;
|
||||
|
||||
bool done = false;
|
||||
for (iiter.Seek(key); iiter.Valid() && !done; iiter.Next()) {
|
||||
Slice handle_value = iiter.value();
|
||||
@@ -1399,37 +1416,60 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
||||
RecordTick(rep_->ioptions.statistics, BLOOM_FILTER_USEFUL);
|
||||
break;
|
||||
} else {
|
||||
BlockIter biter;
|
||||
NewDataBlockIterator(rep_, read_options, iiter.value(), &biter);
|
||||
BlockIter stack_biter;
|
||||
if (pin_blocks) {
|
||||
// We need to create the BlockIter on heap because we may need to
|
||||
// pin it if we encounterd merge operands
|
||||
biter = static_cast<BlockIter*>(
|
||||
NewDataBlockIterator(rep_, read_options, iiter.value()));
|
||||
} else {
|
||||
biter = &stack_biter;
|
||||
NewDataBlockIterator(rep_, read_options, iiter.value(), biter);
|
||||
}
|
||||
|
||||
if (read_options.read_tier == kBlockCacheTier &&
|
||||
biter.status().IsIncomplete()) {
|
||||
biter->status().IsIncomplete()) {
|
||||
// couldn't get block from block_cache
|
||||
// Update Saver.state to Found because we are only looking for whether
|
||||
// we can guarantee the key is not there when "no_io" is set
|
||||
get_context->MarkKeyMayExist();
|
||||
break;
|
||||
}
|
||||
if (!biter.status().ok()) {
|
||||
s = biter.status();
|
||||
if (!biter->status().ok()) {
|
||||
s = biter->status();
|
||||
break;
|
||||
}
|
||||
|
||||
// Call the *saver function on each entry/block until it returns false
|
||||
for (biter.Seek(key); biter.Valid(); biter.Next()) {
|
||||
for (biter->Seek(key); biter->Valid(); biter->Next()) {
|
||||
ParsedInternalKey parsed_key;
|
||||
if (!ParseInternalKey(biter.key(), &parsed_key)) {
|
||||
if (!ParseInternalKey(biter->key(), &parsed_key)) {
|
||||
s = Status::Corruption(Slice());
|
||||
}
|
||||
|
||||
if (!get_context->SaveValue(parsed_key, biter.value())) {
|
||||
if (!get_context->SaveValue(parsed_key, biter->value(), pin_blocks)) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
s = biter.status();
|
||||
s = biter->status();
|
||||
|
||||
if (pin_blocks) {
|
||||
if (get_context->State() == GetContext::kMerge) {
|
||||
// Pin blocks as long as we are merging
|
||||
pinned_iters_mgr->PinIteratorIfNeeded(biter);
|
||||
} else {
|
||||
delete biter;
|
||||
}
|
||||
biter = nullptr;
|
||||
} else {
|
||||
// biter is on stack, Nothing to clean
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pin_blocks && biter != nullptr) {
|
||||
delete biter;
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = iiter.status();
|
||||
}
|
||||
@@ -1510,8 +1550,9 @@ bool BlockBasedTable::TEST_KeyInCache(const ReadOptions& options,
|
||||
handle, cache_key_storage);
|
||||
Slice ckey;
|
||||
|
||||
s = GetDataBlockFromCache(cache_key, ckey, block_cache, nullptr, nullptr,
|
||||
options, &block, rep_->table_options.format_version,
|
||||
s = GetDataBlockFromCache(cache_key, ckey, block_cache, nullptr,
|
||||
rep_->ioptions, options, &block,
|
||||
rep_->table_options.format_version,
|
||||
rep_->compression_dict_block
|
||||
? rep_->compression_dict_block->data
|
||||
: Slice());
|
||||
@@ -1544,11 +1585,8 @@ Status BlockBasedTable::CreateIndexReader(
|
||||
}
|
||||
|
||||
auto file = rep_->file.get();
|
||||
auto env = rep_->ioptions.env;
|
||||
auto comparator = &rep_->internal_comparator;
|
||||
const Footer& footer = rep_->footer;
|
||||
Statistics* stats = rep_->ioptions.statistics;
|
||||
|
||||
if (index_type_on_file == BlockBasedTableOptions::kHashSearch &&
|
||||
rep_->ioptions.prefix_extractor == nullptr) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, rep_->ioptions.info_log,
|
||||
@@ -1561,8 +1599,8 @@ Status BlockBasedTable::CreateIndexReader(
|
||||
switch (index_type_on_file) {
|
||||
case BlockBasedTableOptions::kBinarySearch: {
|
||||
return BinarySearchIndexReader::Create(
|
||||
file, footer, footer.index_handle(), env, comparator, index_reader,
|
||||
rep_->persistent_cache_options, stats);
|
||||
file, footer, footer.index_handle(), rep_->ioptions, comparator,
|
||||
index_reader, rep_->persistent_cache_options);
|
||||
}
|
||||
case BlockBasedTableOptions::kHashSearch: {
|
||||
std::unique_ptr<Block> meta_guard;
|
||||
@@ -1577,21 +1615,16 @@ Status BlockBasedTable::CreateIndexReader(
|
||||
"Unable to read the metaindex block."
|
||||
" Fall back to binary search index.");
|
||||
return BinarySearchIndexReader::Create(
|
||||
file, footer, footer.index_handle(), env, comparator,
|
||||
index_reader, rep_->persistent_cache_options, stats);
|
||||
file, footer, footer.index_handle(), rep_->ioptions, comparator,
|
||||
index_reader, rep_->persistent_cache_options);
|
||||
}
|
||||
meta_index_iter = meta_iter_guard.get();
|
||||
}
|
||||
|
||||
// We need to wrap data with internal_prefix_transform to make sure it can
|
||||
// handle prefix correctly.
|
||||
rep_->internal_prefix_transform.reset(
|
||||
new InternalKeySliceTransform(rep_->ioptions.prefix_extractor));
|
||||
return HashIndexReader::Create(
|
||||
rep_->internal_prefix_transform.get(), footer, file, env, comparator,
|
||||
footer.index_handle(), meta_index_iter, index_reader,
|
||||
rep_->hash_index_allow_collision, rep_->persistent_cache_options,
|
||||
stats);
|
||||
rep_->internal_prefix_transform.get(), footer, file, rep_->ioptions,
|
||||
comparator, footer.index_handle(), meta_index_iter, index_reader,
|
||||
rep_->hash_index_allow_collision, rep_->persistent_cache_options);
|
||||
}
|
||||
default: {
|
||||
std::string error_message =
|
||||
@@ -1711,7 +1744,7 @@ Status BlockBasedTable::DumpTable(WritableFile* out_file) {
|
||||
BlockContents block;
|
||||
if (ReadBlockContents(
|
||||
rep_->file.get(), rep_->footer, ReadOptions(), handle, &block,
|
||||
rep_->ioptions.env, false /*decompress*/,
|
||||
rep_->ioptions, false /*decompress*/,
|
||||
Slice() /*compression dict*/, rep_->persistent_cache_options)
|
||||
.ok()) {
|
||||
rep_->filter.reset(new BlockBasedFilterBlockReader(
|
||||
|
||||
@@ -69,17 +69,19 @@ class BlockBasedTable : public TableReader {
|
||||
// to nullptr and returns a non-ok status.
|
||||
//
|
||||
// @param file must remain live while this Table is in use.
|
||||
// @param prefetch_index_and_filter can be used to disable prefetching of
|
||||
// index and filter blocks at startup
|
||||
// @param prefetch_index_and_filter_in_cache can be used to disable
|
||||
// prefetching of
|
||||
// index and filter blocks into block cache at startup
|
||||
// @param skip_filters Disables loading/accessing the filter block. Overrides
|
||||
// prefetch_index_and_filter, so filter will be skipped if both are set.
|
||||
// prefetch_index_and_filter_in_cache, so filter will be skipped if both
|
||||
// are set.
|
||||
static Status Open(const ImmutableCFOptions& ioptions,
|
||||
const EnvOptions& env_options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const InternalKeyComparator& internal_key_comparator,
|
||||
unique_ptr<RandomAccessFileReader>&& file,
|
||||
uint64_t file_size, unique_ptr<TableReader>* table_reader,
|
||||
bool prefetch_index_and_filter = true,
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
bool skip_filters = false, int level = -1);
|
||||
|
||||
bool PrefixMayMatch(const Slice& internal_key);
|
||||
@@ -177,8 +179,8 @@ class BlockBasedTable : public TableReader {
|
||||
// dictionary.
|
||||
static Status GetDataBlockFromCache(
|
||||
const Slice& block_cache_key, const Slice& compressed_block_cache_key,
|
||||
Cache* block_cache, Cache* block_cache_compressed, Statistics* statistics,
|
||||
const ReadOptions& read_options,
|
||||
Cache* block_cache, Cache* block_cache_compressed,
|
||||
const ImmutableCFOptions &ioptions, const ReadOptions& read_options,
|
||||
BlockBasedTable::CachableEntry<Block>* block, uint32_t format_version,
|
||||
const Slice& compression_dict);
|
||||
|
||||
@@ -195,7 +197,7 @@ class BlockBasedTable : public TableReader {
|
||||
static Status PutDataBlockToCache(
|
||||
const Slice& block_cache_key, const Slice& compressed_block_cache_key,
|
||||
Cache* block_cache, Cache* block_cache_compressed,
|
||||
const ReadOptions& read_options, Statistics* statistics,
|
||||
const ReadOptions& read_options, const ImmutableCFOptions &ioptions,
|
||||
CachableEntry<Block>* block, Block* raw_block, uint32_t format_version,
|
||||
const Slice& compression_dict);
|
||||
|
||||
|
||||
@@ -49,12 +49,16 @@ class CuckooBuilderTest : public testing::Test {
|
||||
uint64_t read_file_size;
|
||||
ASSERT_OK(env_->GetFileSize(fname, &read_file_size));
|
||||
|
||||
Options options;
|
||||
options.allow_mmap_reads = true;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
|
||||
// Assert Table Properties.
|
||||
TableProperties* props = nullptr;
|
||||
unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(read_file)));
|
||||
ASSERT_OK(ReadTableProperties(file_reader.get(), read_file_size,
|
||||
kCuckooTableMagicNumber, env_, nullptr,
|
||||
kCuckooTableMagicNumber, ioptions,
|
||||
&props));
|
||||
// Check unused bucket.
|
||||
std::string unused_key = props->user_collected_properties[
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace rocksdb {
|
||||
Status CuckooTableFactory::NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
std::unique_ptr<TableReader>* table) const {
|
||||
std::unique_ptr<TableReader>* table,
|
||||
bool prefetch_index_and_filter_in_cache) const {
|
||||
std::unique_ptr<CuckooTableReader> new_reader(new CuckooTableReader(
|
||||
table_reader_options.ioptions, std::move(file), file_size,
|
||||
table_reader_options.internal_comparator.user_comparator(), nullptr));
|
||||
|
||||
@@ -55,10 +55,11 @@ class CuckooTableFactory : public TableFactory {
|
||||
|
||||
const char* Name() const override { return "CuckooTable"; }
|
||||
|
||||
Status NewTableReader(const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file,
|
||||
uint64_t file_size,
|
||||
unique_ptr<TableReader>* table) const override;
|
||||
Status NewTableReader(
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table,
|
||||
bool prefetch_index_and_filter_in_cache = true) const override;
|
||||
|
||||
TableBuilder* NewTableBuilder(
|
||||
const TableBuilderOptions& table_builder_options,
|
||||
|
||||
@@ -45,7 +45,7 @@ CuckooTableReader::CuckooTableReader(
|
||||
}
|
||||
TableProperties* props = nullptr;
|
||||
status_ = ReadTableProperties(file_.get(), file_size, kCuckooTableMagicNumber,
|
||||
ioptions.env, ioptions.info_log, &props);
|
||||
ioptions, &props);
|
||||
if (!status_.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
+30
-11
@@ -23,6 +23,9 @@
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/xxhash.h"
|
||||
#include "util/statistics.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -39,6 +42,11 @@ const uint64_t kPlainTableMagicNumber = 0;
|
||||
#endif
|
||||
const uint32_t DefaultStackBufferSize = 5000;
|
||||
|
||||
bool ShouldReportDetailedTime(Env* env, Statistics* stats) {
|
||||
return env != nullptr && stats != nullptr &&
|
||||
stats->stats_level_ > kExceptDetailedTimers;
|
||||
}
|
||||
|
||||
void BlockHandle::EncodeTo(std::string* dst) const {
|
||||
// Sanity check that all fields have been set
|
||||
assert(offset_ != ~static_cast<uint64_t>(0));
|
||||
@@ -297,10 +305,10 @@ Status ReadBlock(RandomAccessFileReader* file, const Footer& footer,
|
||||
Status ReadBlockContents(RandomAccessFileReader* file, const Footer& footer,
|
||||
const ReadOptions& read_options,
|
||||
const BlockHandle& handle, BlockContents* contents,
|
||||
Env* env, bool decompression_requested,
|
||||
const ImmutableCFOptions &ioptions,
|
||||
bool decompression_requested,
|
||||
const Slice& compression_dict,
|
||||
const PersistentCacheOptions& cache_options,
|
||||
Logger* info_log) {
|
||||
const PersistentCacheOptions& cache_options) {
|
||||
Status status;
|
||||
Slice slice;
|
||||
size_t n = static_cast<size_t>(handle.size());
|
||||
@@ -318,9 +326,9 @@ Status ReadBlockContents(RandomAccessFileReader* file, const Footer& footer,
|
||||
return status;
|
||||
} else {
|
||||
// uncompressed page is not found
|
||||
if (info_log && !status.IsNotFound()) {
|
||||
if (ioptions.info_log && !status.IsNotFound()) {
|
||||
assert(!status.ok());
|
||||
Log(InfoLogLevel::INFO_LEVEL, info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions.info_log,
|
||||
"Error reading from persistent cache. %s",
|
||||
status.ToString().c_str());
|
||||
}
|
||||
@@ -341,9 +349,9 @@ Status ReadBlockContents(RandomAccessFileReader* file, const Footer& footer,
|
||||
used_buf = heap_buf.get();
|
||||
slice = Slice(heap_buf.get(), n);
|
||||
} else {
|
||||
if (info_log && !status.IsNotFound()) {
|
||||
if (ioptions.info_log && !status.IsNotFound()) {
|
||||
assert(!status.ok());
|
||||
Log(InfoLogLevel::INFO_LEVEL, info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions.info_log,
|
||||
"Error reading from persistent cache. %s", status.ToString().c_str());
|
||||
}
|
||||
// cache miss read from device
|
||||
@@ -378,7 +386,8 @@ Status ReadBlockContents(RandomAccessFileReader* file, const Footer& footer,
|
||||
if (decompression_requested && compression_type != kNoCompression) {
|
||||
// compressed page, uncompress, update cache
|
||||
status = UncompressBlockContents(slice.data(), n, contents,
|
||||
footer.version(), compression_dict);
|
||||
footer.version(), compression_dict,
|
||||
ioptions);
|
||||
} else if (slice.data() != used_buf) {
|
||||
// the slice content is not the buffer provided
|
||||
*contents = BlockContents(Slice(slice.data(), n), false, compression_type);
|
||||
@@ -405,11 +414,13 @@ Status ReadBlockContents(RandomAccessFileReader* file, const Footer& footer,
|
||||
Status UncompressBlockContentsForCompressionType(
|
||||
const char* data, size_t n, BlockContents* contents,
|
||||
uint32_t format_version, const Slice& compression_dict,
|
||||
CompressionType compression_type) {
|
||||
CompressionType compression_type, const ImmutableCFOptions &ioptions) {
|
||||
std::unique_ptr<char[]> ubuf;
|
||||
|
||||
assert(compression_type != kNoCompression && "Invalid compression type");
|
||||
|
||||
StopWatchNano timer(ioptions.env,
|
||||
ShouldReportDetailedTime(ioptions.env, ioptions.statistics));
|
||||
int decompress_size = 0;
|
||||
switch (compression_type) {
|
||||
case kSnappyCompression: {
|
||||
@@ -501,6 +512,13 @@ Status UncompressBlockContentsForCompressionType(
|
||||
return Status::Corruption("bad block type");
|
||||
}
|
||||
|
||||
if(ShouldReportDetailedTime(ioptions.env, ioptions.statistics)){
|
||||
MeasureTime(ioptions.statistics, DECOMPRESSION_TIMES_NANOS,
|
||||
timer.ElapsedNanos());
|
||||
MeasureTime(ioptions.statistics, BYTES_DECOMPRESSED, contents->data.size());
|
||||
RecordTick(ioptions.statistics, NUMBER_BLOCK_DECOMPRESSED);
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -513,11 +531,12 @@ Status UncompressBlockContentsForCompressionType(
|
||||
// format_version is the block format as defined in include/rocksdb/table.h
|
||||
Status UncompressBlockContents(const char* data, size_t n,
|
||||
BlockContents* contents, uint32_t format_version,
|
||||
const Slice& compression_dict) {
|
||||
const Slice& compression_dict,
|
||||
const ImmutableCFOptions &ioptions) {
|
||||
assert(data[n] != kNoCompression);
|
||||
return UncompressBlockContentsForCompressionType(
|
||||
data, n, contents, format_version, compression_dict,
|
||||
(CompressionType)data[n]);
|
||||
(CompressionType)data[n], ioptions);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+8
-6
@@ -24,6 +24,8 @@ class Block;
|
||||
class RandomAccessFile;
|
||||
struct ReadOptions;
|
||||
|
||||
extern bool ShouldReportDetailedTime(Env* env, Statistics* stats);
|
||||
|
||||
// the length of the magic number in bytes.
|
||||
const int kMagicNumberLengthByte = 8;
|
||||
|
||||
@@ -212,10 +214,9 @@ struct BlockContents {
|
||||
extern Status ReadBlockContents(
|
||||
RandomAccessFileReader* file, const Footer& footer,
|
||||
const ReadOptions& options, const BlockHandle& handle,
|
||||
BlockContents* contents, Env* env, bool do_uncompress = true,
|
||||
const Slice& compression_dict = Slice(),
|
||||
const PersistentCacheOptions& cache_options = PersistentCacheOptions(),
|
||||
Logger* info_log = nullptr);
|
||||
BlockContents* contents, const ImmutableCFOptions &ioptions,
|
||||
bool do_uncompress = true, const Slice& compression_dict = Slice(),
|
||||
const PersistentCacheOptions& cache_options = PersistentCacheOptions());
|
||||
|
||||
// The 'data' points to the raw block contents read in from file.
|
||||
// This method allocates a new heap buffer and the raw block
|
||||
@@ -227,7 +228,8 @@ extern Status ReadBlockContents(
|
||||
extern Status UncompressBlockContents(const char* data, size_t n,
|
||||
BlockContents* contents,
|
||||
uint32_t compress_format_version,
|
||||
const Slice& compression_dict);
|
||||
const Slice& compression_dict,
|
||||
const ImmutableCFOptions &ioptions);
|
||||
|
||||
// This is an extension to UncompressBlockContents that accepts
|
||||
// a specific compression type. This is used by un-wrapped blocks
|
||||
@@ -235,7 +237,7 @@ extern Status UncompressBlockContents(const char* data, size_t n,
|
||||
extern Status UncompressBlockContentsForCompressionType(
|
||||
const char* data, size_t n, BlockContents* contents,
|
||||
uint32_t compress_format_version, const Slice& compression_dict,
|
||||
CompressionType compression_type);
|
||||
CompressionType compression_type, const ImmutableCFOptions &ioptions);
|
||||
|
||||
// Implementation details follow. Clients should ignore,
|
||||
|
||||
|
||||
Loaded 100 of 159 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user