mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
812c461c96 | ||
|
|
b588505a7f | ||
|
|
944043d683 | ||
|
|
d5de04d20e | ||
|
|
08b6b3796e | ||
|
|
04feaeebb9 | ||
|
|
4a855c0799 | ||
|
|
b0fdda4ff0 | ||
|
|
74f3832d85 | ||
|
|
0c8017dbae | ||
|
|
fb5bdbf987 | ||
|
|
6fa7085121 | ||
|
|
3f0867c0fe | ||
|
|
a66f643e97 | ||
|
|
7413306d94 | ||
|
|
8c52788f0c | ||
|
|
5aad881298 | ||
|
|
ec43a8b9fb | ||
|
|
714fcc067d | ||
|
|
bc68bd5a13 | ||
|
|
df1f87a882 | ||
|
|
14431e971d | ||
|
|
dbd95b7532 | ||
|
|
b5881762bc | ||
|
|
711465ccec | ||
|
|
3996fff8a1 | ||
|
|
d978139063 | ||
|
|
2fe24e4b5f | ||
|
|
beda81c538 | ||
|
|
50eab9cf30 | ||
|
|
962f8ba332 | ||
|
|
37bbd31856 | ||
|
|
77a5a543a5 | ||
|
|
65fe1cfbb3 | ||
|
|
df4130ad85 | ||
|
|
7ea769487f | ||
|
|
a2c4cc7562 | ||
|
|
9aa011fa36 | ||
|
|
36a7408896 | ||
|
|
9bdbaab94f | ||
|
|
2ab7065af1 | ||
|
|
88044340c1 | ||
|
|
d2346c2cf0 | ||
|
|
b6b100fe04 | ||
|
|
197f01b7b8 | ||
|
|
aa094e8ea3 | ||
|
|
dddceefe5e | ||
|
|
d4540654e9 | ||
|
|
fd96b55402 | ||
|
|
7246ad34d0 | ||
|
|
7f47ba0e26 | ||
|
|
aba005c44e | ||
|
|
d6f39c5ae3 | ||
|
|
a087f80e9d | ||
|
|
1bb4928da9 | ||
|
|
3db81d535a | ||
|
|
4961a9622c | ||
|
|
93ab1473dc | ||
|
|
6ede020dc4 | ||
|
|
3d1af4ae6f | ||
|
|
242f9b4c26 | ||
|
|
794ccfde89 | ||
|
|
98a44559d5 | ||
|
|
d01bbb53ae | ||
|
|
e003d3864c | ||
|
|
fd7a357318 | ||
|
|
aa14670b27 | ||
|
|
2dc421df48 | ||
|
|
9bf40b64d0 | ||
|
|
397b6588bd | ||
|
|
283a042969 | ||
|
|
618d07b068 | ||
|
|
0a91bca5db | ||
|
|
6e359419fe | ||
|
|
78dbd087d1 | ||
|
|
6a5ffee0cc | ||
|
|
108a927f0e | ||
|
|
a58fd74276 | ||
|
|
d85d08c7b3 | ||
|
|
2db96dca18 | ||
|
|
7d136994c9 | ||
|
|
79c1b021a7 | ||
|
|
742fa9e316 | ||
|
|
79c21ec0c4 | ||
|
|
6059bdf86a | ||
|
|
acf8a4141d | ||
|
|
b5400f90fe | ||
|
|
48b0a045da | ||
|
|
00c2afcd38 | ||
|
|
019ecd1932 | ||
|
|
98ef21d2ff | ||
|
|
5b7131c751 | ||
|
|
566f652716 | ||
|
|
6997aa0b6b | ||
|
|
582c4b0f74 | ||
|
|
6cfb2150d4 | ||
|
|
d71e8f7adf | ||
|
|
debaf85ef5 | ||
|
|
12d7d3d28d | ||
|
|
a1271c6c6f | ||
|
|
fcb206b667 | ||
|
|
281db8bb62 | ||
|
|
e8808b9128 | ||
|
|
1983fadcbc | ||
|
|
9da8748016 | ||
|
|
9b983befa8 | ||
|
|
d41a565a4a | ||
|
|
08be1803ee | ||
|
|
0a0501c8d5 | ||
|
|
e7ad14926a | ||
|
|
abb4052278 | ||
|
|
894e9f7454 | ||
|
|
590fadc407 | ||
|
|
47b8743984 | ||
|
|
753dd1fdd0 | ||
|
|
697380f3d7 | ||
|
|
2f66d7f925 | ||
|
|
0feeee6433 | ||
|
|
7b9581bc3b | ||
|
|
fabc115690 | ||
|
|
9741dec0e5 | ||
|
|
465b25ca93 | ||
|
|
d2a056241a | ||
|
|
316ec80bf8 | ||
|
|
ed229a0dee | ||
|
|
91df4e969d | ||
|
|
84c5bd7eb9 | ||
|
|
ee9bdd38a1 | ||
|
|
2b019a1512 | ||
|
|
b1bbdd7919 | ||
|
|
edbb08b5df | ||
|
|
199313dc38 | ||
|
|
b118238a57 | ||
|
|
e7adfe690b | ||
|
|
4e7543dcf6 | ||
|
|
58346b9e29 | ||
|
|
db6569cd4a | ||
|
|
cba5920011 | ||
|
|
c66483c132 | ||
|
|
de22c7bd1f | ||
|
|
f12614070f | ||
|
|
824e646341 | ||
|
|
5e067a7b19 | ||
|
|
e3ee98b38a | ||
|
|
a45e7581b7 | ||
|
|
3be82bc894 | ||
|
|
953a885ebf | ||
|
|
d2a92c13bc | ||
|
|
a7ac6cef1f | ||
|
|
089509b847 | ||
|
|
afbafeaeae | ||
|
|
d0695f3e26 | ||
|
|
51c8133a72 | ||
|
|
df71c6b9ed | ||
|
|
38a01ed1b0 | ||
|
|
76d63b4525 | ||
|
|
b23bbaa82a | ||
|
|
2511b7d947 | ||
|
|
1bd70fb54a | ||
|
|
fd3dbef22b | ||
|
|
99ec2412e5 | ||
|
|
2158e0f831 | ||
|
|
d61cb0b9de | ||
|
|
1c47c433ba | ||
|
|
e018892bb6 | ||
|
|
cfa576402c | ||
|
|
e9fddb7a2b | ||
|
|
bcd8a71a28 | ||
|
|
030859eb5d | ||
|
|
3539e06448 | ||
|
|
a3e4b32483 |
No files matched your search
+3
-1
@@ -3,10 +3,12 @@
|
||||
"conduit_uri" : "https://reviews.facebook.net/",
|
||||
"copyright_holder" : "Facebook",
|
||||
"load" : [
|
||||
"linters"
|
||||
"arcanist_util"
|
||||
],
|
||||
"lint.engine" : "FacebookFbcodeLintEngine",
|
||||
"lint.engine.single.linter" : "FbcodeCppLinter",
|
||||
"unit.engine" : "FacebookFbcodeUnitTestEngine",
|
||||
"arcanist_configuration" : "FacebookArcanistConfiguration",
|
||||
"base" : "git:HEAD^, hg:.^",
|
||||
"git.default-relative-commit" : "HEAD^",
|
||||
"git:arc.feature.start.default" : "origin/master",
|
||||
|
||||
@@ -50,3 +50,5 @@ java/**.asc
|
||||
java/javadoc
|
||||
|
||||
scan_build_report/
|
||||
t
|
||||
LOG
|
||||
+1
-1
@@ -5,7 +5,7 @@ before_install:
|
||||
# which needs the following ugly dependency incantations to build RocksDB:
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y -qq gcc-4.9 g++-4.9 zlib1g-dev libbz2-dev libsnappy-dev libjemalloc-dev
|
||||
- sudo apt-get install -y -qq gcc-4.9 g++-4.9 zlib1g-dev libbz2-dev libsnappy-dev
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 50
|
||||
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 50
|
||||
- wget https://gflags.googlecode.com/files/libgflags0_2.0-1_amd64.deb
|
||||
|
||||
+14
@@ -1,5 +1,19 @@
|
||||
# Rocksdb Change Log
|
||||
|
||||
## 3.11.0 (5/19/2015)
|
||||
|
||||
### New Features
|
||||
* Added a new API Cache::SetCapacity(size_t capacity) to dynamically change the maximum configured capacity of the cache. If the new capacity is less than the existing cache usage, the implementation will try to lower the usage by evicting the necessary number of elements following a strict LRU policy.
|
||||
|
||||
### New Features
|
||||
* Added an experimental API for handling flashcache devices (blacklists background threads from caching their reads) -- NewFlashcacheAwareEnv
|
||||
* If universal compaction is used and options.num_levels > 1, compact files are tried to be stored in none-L0 with smaller files based on options.target_file_size_base. The limitation of DB size when using universal compaction is greatly mitigated by using more levels. You can set num_levels = 1 to make universal compaction behave as before. If you set num_levels > 1 and want to roll back to a previous version, you need to compact all files to a big file in level 0 (by setting target_file_size_base to be large and CompactRange(<cf_handle>, nullptr, nullptr, true, 0) and reopen the DB with the same version to rewrite the manifest, and then you can open it using previous releases.
|
||||
* More information about rocksdb background threads are available in Env::GetThreadList(), including the number of bytes read / written by a compaction job, mem-table size and current number of bytes written by a flush job and many more. Check include/rocksdb/thread_status.h for more detail.
|
||||
|
||||
### Public API changes
|
||||
* TablePropertiesCollector::AddUserKey() is added to replace TablePropertiesCollector::Add(). AddUserKey() exposes key type, sequence number and file size up to now to users.
|
||||
* DBOptions::bytes_per_sync used to apply to both WAL and table files. As of 3.11 it applies only to table files. If you want to use this option to sync WAL in the background, please use wal_bytes_per_sync
|
||||
|
||||
## 3.10.0 (3/24/2015)
|
||||
### New Features
|
||||
* GetThreadStatus() is now able to report detailed thread status, including:
|
||||
|
||||
@@ -6,27 +6,76 @@
|
||||
|
||||
#-----------------------------------------------
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
CXXFLAGS += ${EXTRA_CXXFLAGS}
|
||||
LDFLAGS += $(EXTRA_LDFLAGS)
|
||||
MACHINE ?= $(shell uname -m)
|
||||
ARFLAGS = rs
|
||||
|
||||
ifneq ($(MAKECMDGOALS),dbg)
|
||||
# Transform parallel LOG output into something more readable.
|
||||
perl_command = perl -n \
|
||||
-e '@a=split("\t",$$_,-1); $$t=$$a[8]; $$t =~ s,^\./,,;' \
|
||||
-e '$$t =~ s, >.*,,; chomp $$t;' \
|
||||
-e '$$t =~ /.*--gtest_filter=(.*?\.[\w\/]+)/ and $$t=$$1;' \
|
||||
-e 'printf "%7.3f %s %s\n", $$a[3], $$a[6] == 0 ? "PASS" : "FAIL", $$t'
|
||||
quoted_perl_command = $(subst ','\'',$(perl_command))
|
||||
|
||||
# DEBUG_LEVEL can have three values:
|
||||
# * DEBUG_LEVEL=2; this is the ultimate debug mode. It will compile rocksdb
|
||||
# without any optimizations. To compile with level 2, issue `make dbg`
|
||||
# * DEBUG_LEVEL=1; debug level 1 enables all assertions and debug code, but
|
||||
# compiles rocksdb with -O2 optimizations. this is the default debug level.
|
||||
# `make all` or `make <binary_target>` compile RocksDB with debug level 1.
|
||||
# We use this debug level when developing RocksDB.
|
||||
# * DEBUG_LEVEL=0; this is the debug level we use for release. If you're
|
||||
# running rocksdb in production you most definitely want to compile RocksDB
|
||||
# with debug level 0. To compile with level 0, run `make shared_lib`,
|
||||
# `make install-shared`, `make static_lib`, `make install-static` or
|
||||
# `make install`
|
||||
DEBUG_LEVEL=1
|
||||
|
||||
ifeq ($(MAKECMDGOALS),dbg)
|
||||
DEBUG_LEVEL=2
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),shared_lib)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),install-shared)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),static_lib)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),install-static)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),install)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastatic)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
# compile with -O2 if debug level is not 2
|
||||
ifneq ($(DEBUG_LEVEL), 2)
|
||||
OPT += -O2 -fno-omit-frame-pointer
|
||||
ifneq ($(MACHINE),ppc64) # ppc64 doesn't support -momit-leaf-frame-pointer
|
||||
OPT += -momit-leaf-frame-pointer
|
||||
endif
|
||||
else
|
||||
# intentionally left blank
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),shared_lib)
|
||||
OPT += -DNDEBUG
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),static_lib)
|
||||
# if we're compiling for release, compile without debug code (-DNDEBUG) and
|
||||
# don't treat warnings as errors
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
OPT += -DNDEBUG
|
||||
DISABLE_WARNING_AS_ERROR=1
|
||||
endif
|
||||
|
||||
#-----------------------------------------------
|
||||
@@ -64,6 +113,7 @@ AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS
|
||||
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
|
||||
include make_config.mk
|
||||
CLEAN_FILES += make_config.mk
|
||||
|
||||
ifneq ($(PLATFORM), IOS)
|
||||
CFLAGS += -g
|
||||
@@ -102,8 +152,8 @@ endif
|
||||
|
||||
ifndef DISABLE_JEMALLOC
|
||||
EXEC_LDFLAGS := $(JEMALLOC_LIB) $(EXEC_LDFLAGS)
|
||||
PLATFORM_CXXFLAGS += $(JEMALLOC_INCLUDE) -DHAVE_JEMALLOC
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE) -DHAVE_JEMALLOC
|
||||
PLATFORM_CXXFLAGS += $(JEMALLOC_INCLUDE)
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE)
|
||||
endif
|
||||
|
||||
export GTEST_THROW_ON_FAILURE=1 GTEST_HAS_EXCEPTIONS=1
|
||||
@@ -114,27 +164,6 @@ PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
|
||||
# This (the first rule) must depend on "all".
|
||||
default: all
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
|
||||
uninstall:
|
||||
rm -rf $(INSTALL_PATH)/include/rocksdb \
|
||||
$(INSTALL_PATH)/lib/$(LIBRARY) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED)
|
||||
|
||||
install:
|
||||
install -d $(INSTALL_PATH)/lib
|
||||
for header_dir in `find "include/rocksdb" -type d`; do \
|
||||
install -d $(INSTALL_PATH)/$$header_dir; \
|
||||
done
|
||||
for header in `find "include/rocksdb" -type f -name *.h`; do \
|
||||
install -C -m 644 $$header $(INSTALL_PATH)/$$header; \
|
||||
done
|
||||
[ ! -e $(LIBRARY) ] || install -C -m 644 $(LIBRARY) $(INSTALL_PATH)/lib
|
||||
[ ! -e $(SHARED) ] || install -C -m 644 $(SHARED) $(INSTALL_PATH)/lib
|
||||
#-------------------------------------------------
|
||||
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
|
||||
@@ -148,12 +177,16 @@ CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverl
|
||||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
|
||||
date := $(shell date +%F)
|
||||
git_sha := $(shell git describe HEAD 2>/dev/null)
|
||||
ifdef FORCE_GIT_SHA
|
||||
git_sha := $(FORCE_GIT_SHA)
|
||||
else
|
||||
git_sha := $(shell git rev-parse HEAD 2>/dev/null)
|
||||
endif
|
||||
gen_build_version = \
|
||||
printf '%s\n' \
|
||||
'\#include "build_version.h"' \
|
||||
'const char* rocksdb_build_git_sha = \
|
||||
"rocksdb_build_git_sha:$(git_sha)";' \
|
||||
"rocksdb_build_git_sha:$(git_sha)";' \
|
||||
'const char* rocksdb_build_git_date = \
|
||||
"rocksdb_build_git_date:$(date)";' \
|
||||
'const char* rocksdb_build_compile_date = __DATE__;'
|
||||
@@ -163,10 +196,14 @@ gen_build_version = \
|
||||
# as a regular source file as part of the compilation process.
|
||||
# One can run "strings executable_filename | grep _build_" to find
|
||||
# the version of the source that we used to build the executable file.
|
||||
util/build_version.cc:
|
||||
$(AM_V_GEN)$(gen_build_version) > $@.tmp
|
||||
$(AM_V_at)if test -f $@; then \
|
||||
cmp -s $@.tmp $@ && : || mv -f $@.tmp $@; else mv -f $@.tmp $@; fi
|
||||
CLEAN_FILES += util/build_version.cc:
|
||||
FORCE:
|
||||
util/build_version.cc: FORCE
|
||||
$(AM_V_GEN)rm -f $@-t
|
||||
$(AM_V_at)$(gen_build_version) > $@-t
|
||||
$(AM_V_at)if test -f $@; then \
|
||||
cmp -s $@-t $@ && rm -f $@-t || mv -f $@-t $@; \
|
||||
else mv -f $@-t $@; fi
|
||||
|
||||
LIBOBJECTS = $(LIB_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_SOURCES:.cc=.o)
|
||||
@@ -174,7 +211,6 @@ MOCKOBJECTS = $(MOCK_SOURCES:.cc=.o)
|
||||
GTEST = $(GTEST_DIR)/gtest/gtest-all.o
|
||||
TESTUTIL = ./util/testutil.o
|
||||
TESTHARNESS = ./util/testharness.o $(TESTUTIL) $(MOCKOBJECTS) $(GTEST)
|
||||
BENCHHARNESS = ./util/benchharness.o
|
||||
VALGRIND_ERROR = 2
|
||||
VALGRIND_DIR = build_tools/VALGRIND_LOGS
|
||||
VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
|
||||
@@ -190,7 +226,6 @@ TESTS = \
|
||||
table_properties_collector_test \
|
||||
arena_test \
|
||||
auto_roll_logger_test \
|
||||
benchharness_test \
|
||||
block_test \
|
||||
bloom_test \
|
||||
dynamic_bloom_test \
|
||||
@@ -212,6 +247,7 @@ TESTS = \
|
||||
manual_compaction_test \
|
||||
memenv_test \
|
||||
mock_env_test \
|
||||
memtable_list_test \
|
||||
merge_test \
|
||||
merger_test \
|
||||
redis_test \
|
||||
@@ -232,6 +268,7 @@ TESTS = \
|
||||
version_builder_test \
|
||||
file_indexer_test \
|
||||
write_batch_test \
|
||||
write_batch_with_index_test \
|
||||
write_controller_test\
|
||||
deletefile_test \
|
||||
table_test \
|
||||
@@ -249,19 +286,19 @@ TESTS = \
|
||||
compaction_job_test \
|
||||
thread_list_test \
|
||||
sst_dump_test \
|
||||
compact_files_test
|
||||
compact_files_test \
|
||||
perf_context_test
|
||||
|
||||
SUBSET := $(shell echo $(TESTS) |sed s/^.*$(ROCKSDBTESTS_START)/$(ROCKSDBTESTS_START)/)
|
||||
|
||||
TOOLS = \
|
||||
sst_dump \
|
||||
sst_dump \
|
||||
db_sanity_test \
|
||||
db_stress \
|
||||
ldb \
|
||||
db_repl_stress \
|
||||
options_test \
|
||||
db_stress \
|
||||
ldb \
|
||||
db_repl_stress
|
||||
|
||||
PROGRAMS = db_bench signal_test table_reader_bench log_and_apply_bench cache_bench perf_context_test memtablerep_bench $(TOOLS)
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench
|
||||
|
||||
# The library name is configurable since we are maintaining libraries of both
|
||||
# debug/release mode.
|
||||
@@ -305,42 +342,178 @@ $(SHARED3): $(SHARED4)
|
||||
endif
|
||||
|
||||
$(SHARED4):
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(LIB_SOURCES) $(LDFLAGS) -o $@
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(LIB_SOURCES) $(LDFLAGS) -o $@
|
||||
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
|
||||
.PHONY: blackbox_crash_test check clean coverage crash_test ldb_tests package \
|
||||
release tags valgrind_check whitebox_crash_test format static_lib shared_lib all \
|
||||
dbg rocksdbjavastatic rocksdbjava install uninstall analyze
|
||||
dbg rocksdbjavastatic rocksdbjava install install-static install-shared uninstall \
|
||||
analyze
|
||||
|
||||
all: $(LIBRARY) $(PROGRAMS) $(TESTS)
|
||||
all: $(LIBRARY) $(BENCHMARKS) $(TOOLS) $(TESTS)
|
||||
|
||||
static_lib: $(LIBRARY)
|
||||
|
||||
shared_lib: $(SHARED)
|
||||
|
||||
dbg: $(LIBRARY) $(PROGRAMS) $(TESTS)
|
||||
dbg: $(LIBRARY) $(BENCHMARKS) $(TOOLS) $(TESTS)
|
||||
|
||||
# creates static library and programs
|
||||
release:
|
||||
$(MAKE) clean
|
||||
OPT="-DNDEBUG -O2" $(MAKE) static_lib $(PROGRAMS) -j32
|
||||
OPT="-DNDEBUG -O2" $(MAKE) static_lib $(TOOLS) db_bench -j32
|
||||
|
||||
coverage:
|
||||
$(MAKE) clean
|
||||
COVERAGEFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS+="-lgcov" $(MAKE) all check -j32
|
||||
(cd coverage; ./coverage_test.sh)
|
||||
# Delete intermediate files
|
||||
cd coverage && ./coverage_test.sh
|
||||
# Delete intermediate files
|
||||
find . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm {} \;
|
||||
|
||||
check: $(TESTS) $(PROGRAMS)
|
||||
for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done
|
||||
python tools/ldb_test.py
|
||||
# Extract the names of its tests by running db_test with --gtest_list_tests.
|
||||
# This filter removes the "#"-introduced comments, and expands to
|
||||
# fully-qualified names by changing input like this:
|
||||
#
|
||||
# DBTest.
|
||||
# Empty
|
||||
# WriteEmptyBatch
|
||||
# MultiThreaded/MultiThreadedDBTest.
|
||||
# MultiThreaded/0 # GetParam() = 0
|
||||
# MultiThreaded/1 # GetParam() = 1
|
||||
#
|
||||
# into this:
|
||||
#
|
||||
# DBTest.Empty
|
||||
# DBTest.WriteEmptyBatch
|
||||
# MultiThreaded/MultiThreadedDBTest.MultiThreaded/0
|
||||
# MultiThreaded/MultiThreadedDBTest.MultiThreaded/1
|
||||
#
|
||||
test_names = \
|
||||
./db_test --gtest_list_tests \
|
||||
| perl -n \
|
||||
-e 's/ *\#.*//;' \
|
||||
-e '/^(\s*)(\S+)/; !$$1 and do {$$p=$$2; break};' \
|
||||
-e 'print qq! $$p$$2!'
|
||||
|
||||
check_some: $(SUBSET) ldb
|
||||
for t in $(SUBSET); do echo "***** Running $$t"; ./$$t || exit 1; done
|
||||
python tools/ldb_test.py
|
||||
ifeq ($(MAKECMDGOALS),check)
|
||||
# Use /dev/shm if it has the sticky bit set (otherwise, /tmp),
|
||||
# and create a randomly-named rocksdb.XXXX directory therein.
|
||||
# We'll use that directory in the "make check" rules.
|
||||
ifeq ($(TMPD),)
|
||||
TMPD := $(shell f=/dev/shm; test -k $$f || f=/tmp; \
|
||||
perl -le 'use File::Temp "tempdir";' \
|
||||
-e 'print tempdir("'$$f'/rocksdb.XXXX", CLEANUP => 0)')
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(T),)
|
||||
|
||||
# Run all tests in parallel, accumulating per-test logs in t/log-*.
|
||||
|
||||
# t_sanitized is each $(T) with "-" in place of each "/".
|
||||
t_sanitized = $(subst /,-,$(T))
|
||||
|
||||
# t_run is each sanitized name with a leading "t/".
|
||||
t_run = $(patsubst %,t/%,$(t_sanitized))
|
||||
|
||||
# Each t_run file is a tiny generated bourne shell script
|
||||
# that invokes one of db_tests's sub-tests. Why use a file
|
||||
# for this? Because that makes the invocation of parallel
|
||||
# below simpler, which in turn makes the parsing of parallel's
|
||||
# LOG simpler (the latter is for live monitoring as parallel
|
||||
# tests run).
|
||||
filter = --gtest_filter=$(subst -,/,$(@F))
|
||||
$(t_run): Makefile db_test
|
||||
$(AM_V_GEN)mkdir -p t
|
||||
$(AM_V_at)rm -f $@ $@-t
|
||||
$(AM_V_at)printf '%s\n' \
|
||||
'#!/bin/sh' \
|
||||
'd=$(TMPD)/$(@F)' \
|
||||
'mkdir -p $$d' \
|
||||
'TEST_TMPDIR=$$d ./db_test $(filter)' \
|
||||
> $@-t
|
||||
$(AM_V_at)chmod a=rx $@-t
|
||||
$(AM_V_at)mv $@-t $@
|
||||
|
||||
# Reorder input lines (which are one per test) so that the
|
||||
# longest-running tests appear first in the output.
|
||||
# Do this by prefixing each selected name with its duration,
|
||||
# sort the resulting names, and remove the leading numbers.
|
||||
# FIXME: the "100" we prepend is a fake time, for now.
|
||||
# FIXME: squirrel away timings from each run and use them
|
||||
# (when present) on subsequent runs to order these tests.
|
||||
#
|
||||
# Without this reordering, these two tests would happen to start only
|
||||
# after almost all other tests had completed, thus adding 100 seconds
|
||||
# to the duration of parallel "make check". That's the difference
|
||||
# between 4 minutes (old) and 2m20s (new).
|
||||
#
|
||||
# 152.120 PASS t/DBTest.FileCreationRandomFailure
|
||||
# 107.816 PASS t/DBTest.EncodeDecompressedBlockSizeTest
|
||||
#
|
||||
slow_test_regexp = \
|
||||
^t/DBTest\.(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$
|
||||
prioritize_long_running_tests = \
|
||||
perl -pe 's,($(slow_test_regexp)),100 $$1,' \
|
||||
| sort -k1,1gr \
|
||||
| sed 's/^[.0-9]* //'
|
||||
|
||||
# "make check" uses
|
||||
# Run with "make J=1 check" to disable parallelism in "make check".
|
||||
# Run with "make J=200% check" to run two parallel jobs per core.
|
||||
# The default is to run one job per core (J=100%).
|
||||
# See "man parallel" for its "-j ..." option.
|
||||
J = 100%
|
||||
|
||||
# Use this regexp to select the subset of tests whose names match.
|
||||
tests-regexp = .
|
||||
|
||||
.PHONY: check_0
|
||||
check_0: $(t_run)
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
test -t 1 && eta=--eta || eta=; \
|
||||
{ \
|
||||
printf './%s\n' $(filter-out db_test, $(TESTS)); \
|
||||
printf '%s\n' $(t_run); \
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| parallel -j$(J) --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
endif
|
||||
|
||||
CLEAN_FILES += t LOG $(TMPD)
|
||||
|
||||
# When running parallel "make check", you can monitor its progress
|
||||
# from another window.
|
||||
# Run "make watch_LOG" to show the duration,PASS/FAIL,name of parallel
|
||||
# tests as they are being run. We sort them so that longer-running ones
|
||||
# appear at the top of the list and any failing tests remain at the top
|
||||
# regardless of their duration. As with any use of "watch", hit ^C to
|
||||
# interrupt.
|
||||
watch-log:
|
||||
watch --interval=0 'sort -k7,7nr -k4,4gr LOG|$(quoted_perl_command)'
|
||||
|
||||
# If GNU parallel is installed, run the tests in parallel,
|
||||
# via the check_0 rule above. Otherwise, run them sequentially.
|
||||
check: all
|
||||
$(AM_V_GEN)case $$(parallel --gnu --help 2>/dev/null) in \
|
||||
*'GNU Parallel'*) \
|
||||
t=$$($(test_names)); \
|
||||
$(MAKE) T="$$t" TMPD=$(TMPD) check_0;; \
|
||||
*) \
|
||||
for t in $(TESTS); do \
|
||||
echo "===== Running $$t"; ./$$t || exit 1; done;; \
|
||||
esac
|
||||
rm -rf $(TMPD)
|
||||
|
||||
check_some: $(SUBSET) ldb_tests
|
||||
for t in $(SUBSET); do echo "===== Running $$t"; ./$$t || exit 1; done
|
||||
|
||||
.PHONY: ldb_tests
|
||||
ldb_tests: ldb
|
||||
python tools/ldb_test.py
|
||||
|
||||
@@ -362,7 +535,7 @@ asan_crash_test:
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
valgrind_check: all $(PROGRAMS) $(TESTS)
|
||||
valgrind_check: $(TESTS)
|
||||
mkdir -p $(VALGRIND_DIR)
|
||||
echo TESTS THAT HAVE VALGRIND ERRORS > $(VALGRIND_DIR)/valgrind_failed_tests; \
|
||||
echo TIMES in seconds TAKEN BY TESTS ON VALGRIND > $(VALGRIND_DIR)/valgrind_tests_times; \
|
||||
@@ -382,7 +555,8 @@ analyze: clean
|
||||
-o $(CURDIR)/scan_build_report \
|
||||
$(MAKE) dbg
|
||||
|
||||
unity.cc:
|
||||
CLEAN_FILES += unity.cc
|
||||
unity.cc: Makefile
|
||||
rm -f $@ $@-t
|
||||
for source_file in $(LIB_SOURCES); do \
|
||||
echo "#include <$$source_file>" >> $@-t; \
|
||||
@@ -395,9 +569,9 @@ unity: unity.o
|
||||
$(AM_LINK)
|
||||
|
||||
clean:
|
||||
rm -f $(PROGRAMS) $(TESTS) $(LIBRARY) $(SHARED) make_config.mk unity.cc
|
||||
rm -rf ios-x86 ios-arm scan_build_report
|
||||
find . -name "*.[oda]" -exec rm {} \;
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(LIBRARY) $(SHARED)
|
||||
rm -rf $(CLEAN_FILES) ios-x86 ios-arm scan_build_report
|
||||
find . -name "*.[oda]" -exec rm -f {} \;
|
||||
find . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm {} \;
|
||||
rm -rf bzip2* snappy* zlib* lz4*
|
||||
|
||||
@@ -439,9 +613,6 @@ db_sanity_test: tools/db_sanity_test.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
db_repl_stress: tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
signal_test: util/signal_test.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -475,9 +646,6 @@ stringappend_test: utilities/merge_operators/string_append/stringappend_test.o $
|
||||
redis_test: utilities/redis/redis_lists_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
benchharness_test: util/benchharness_test.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
histogram_test: util/histogram_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -512,9 +680,6 @@ comparator_db_test: db/comparator_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
table_reader_bench: table/table_reader_bench.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK) $(pg)
|
||||
|
||||
log_and_apply_bench: db/log_and_apply_bench.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS)
|
||||
$(AM_LINK) $(pg)
|
||||
|
||||
perf_context_test: db/perf_context_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
|
||||
@@ -620,7 +785,7 @@ geodb_test: utilities/geodb/geodb_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cuckoo_table_builder_test: table/cuckoo_table_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_reader_test: table/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS)
|
||||
cuckoo_table_reader_test: table/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_db_test: db/cuckoo_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -662,17 +827,56 @@ filelock_test: util/filelock_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
auto_roll_logger_test: util/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memtable_list_test: db/memtable_list_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sst_dump: tools/sst_dump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
ldb: tools/ldb.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
|
||||
uninstall:
|
||||
rm -rf $(INSTALL_PATH)/include/rocksdb \
|
||||
$(INSTALL_PATH)/lib/$(LIBRARY) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED4) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED3) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED2) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED1)
|
||||
|
||||
install-headers:
|
||||
install -d $(INSTALL_PATH)/lib
|
||||
for header_dir in `find "include/rocksdb" -type d`; do \
|
||||
install -d $(INSTALL_PATH)/$$header_dir; \
|
||||
done
|
||||
for header in `find "include/rocksdb" -type f -name *.h`; do \
|
||||
install -C -m 644 $$header $(INSTALL_PATH)/$$header; \
|
||||
done
|
||||
|
||||
install-static: install-headers $(LIBRARY)
|
||||
install -C -m 755 $(LIBRARY) $(INSTALL_PATH)/lib
|
||||
|
||||
install-shared: install-headers $(SHARED4)
|
||||
install -C -m 755 $(SHARED4) $(INSTALL_PATH)/lib && \
|
||||
ln -fs $(SHARED4) $(INSTALL_PATH)/lib/$(SHARED3) && \
|
||||
ln -fs $(SHARED4) $(INSTALL_PATH)/lib/$(SHARED2) && \
|
||||
ln -fs $(SHARED4) $(INSTALL_PATH)/lib/$(SHARED1)
|
||||
|
||||
# install static by default + install shared if it exists
|
||||
install: install-static
|
||||
[ -e $(SHARED4) ] && $(MAKE) install-shared || :
|
||||
|
||||
#-------------------------------------------------
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Jni stuff
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
JNI_NATIVE_SOURCES = ./java/rocksjni/*.cc
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ARCH := $(shell getconf LONG_BIT)
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
@@ -721,12 +925,21 @@ liblz4.a:
|
||||
cd lz4-r127/lib && make CFLAGS='-fPIC' all
|
||||
cp lz4-r127/lib/liblz4.a .
|
||||
|
||||
# A version of each $(LIBOBJECTS) compiled with -fPIC
|
||||
java_libobjects = $(patsubst %,jl/%,$(LIBOBJECTS))
|
||||
CLEAN_FILES += jl
|
||||
|
||||
rocksdbjavastatic: libz.a libbz2.a libsnappy.a liblz4.a
|
||||
OPT="-fPIC -DNDEBUG -O2" $(MAKE) $(LIBRARY) -j
|
||||
$(java_libobjects): jl/%.o: %.cc
|
||||
$(AM_V_CC)mkdir -p $(@D)
|
||||
@$(CXX) $(CXXFLAGS) -fPIC -c $< -o $@ $(COVERAGEFLAGS)
|
||||
|
||||
rocksdbjavastatic: $(java_libobjects) libz.a libbz2.a libsnappy.a liblz4.a
|
||||
cd java;$(MAKE) javalib;
|
||||
rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIBOBJECTS) $(COVERAGEFLAGS) libz.a libbz2.a libsnappy.a liblz4.a
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC \
|
||||
-o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) \
|
||||
$(java_libobjects) $(COVERAGEFLAGS) \
|
||||
libz.a libbz2.a libsnappy.a liblz4.a
|
||||
cd java/target;strip -S -x $(ROCKSDBJNILIB)
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
@@ -748,11 +961,10 @@ rocksdbjavastaticpublish: rocksdbjavastaticrelease
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-osx.jar -Dclassifier=osx
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
|
||||
rocksdbjava:
|
||||
OPT="-fPIC -DNDEBUG -O2" $(MAKE) $(LIBRARY) -j32
|
||||
rocksdbjava: $(java_libobjects)
|
||||
cd java;$(MAKE) javalib;
|
||||
rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIBOBJECTS) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(java_libobjects) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
@@ -770,7 +982,14 @@ commit-prereq:
|
||||
$(MAKE) clean && $(MAKE) all check;
|
||||
$(MAKE) clean && $(MAKE) rocksdbjava;
|
||||
$(MAKE) clean && USE_CLANG=1 $(MAKE) all;
|
||||
$(MAKE) clean && OPT=-DROCKSDB_LITE $(MAKE) release;
|
||||
$(MAKE) clean && OPT=-DROCKSDB_LITE $(MAKE) static_lib;
|
||||
|
||||
xfunc:
|
||||
for xftest in $(XFUNC_TESTS); do \
|
||||
echo "===== Running xftest $$xftest"; \
|
||||
make check ROCKSDB_XFUNC_TEST="$$xftest" tests-regexp="DBTest" ;\
|
||||
done
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform-specific compilation
|
||||
@@ -810,18 +1029,18 @@ endif
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
all_sources = $(LIB_SOURCES) $(TEST_BENCH_SOURCES) $(MOCK_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.d)
|
||||
|
||||
# Add proper dependency support so changing a .h file forces a .cc file to
|
||||
# rebuild.
|
||||
|
||||
# The .d file indicates .cc file's dependencies on .h files. We generate such
|
||||
# dependency by g++'s -MM option, whose output is a make dependency rule.
|
||||
%.d: %.cc
|
||||
$(DEPFILES): %.d: %.cc
|
||||
@$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) \
|
||||
-MM -MT'$@' -MT'$(<:.cc=.o)' "$<" -o '$@'
|
||||
|
||||
all_sources = $(LIB_SOURCES) $(TEST_BENCH_SOURCES) $(MOCK_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.d)
|
||||
|
||||
depend: $(DEPFILES)
|
||||
|
||||
# if the make goal is either "clean" or "format", we shouldn't
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
Additional Grant of Patent Rights
|
||||
Additional Grant of Patent Rights Version 2
|
||||
|
||||
“Software” means the rocksdb software distributed by Facebook, Inc.
|
||||
"Software" means the RocksDB software distributed by Facebook, Inc.
|
||||
|
||||
Facebook hereby grants you a perpetual, worldwide, royalty-free,
|
||||
non-exclusive, irrevocable (subject to the termination provision below)
|
||||
license under any rights in any patent claims owned by Facebook, to make,
|
||||
have made, use, sell, offer to sell, import, and otherwise transfer the
|
||||
Software. For avoidance of doubt, no license is granted under Facebook’s
|
||||
rights in any patent claims that are infringed by (i) modifications to the
|
||||
Software made by you or a third party, or (ii) the Software in combination
|
||||
with any software or other technology provided by you or a third party.
|
||||
Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
|
||||
("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
|
||||
(subject to the termination provision below) license under any Necessary
|
||||
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
|
||||
transfer the Software. For avoidance of doubt, no license is granted under
|
||||
Facebook’s rights in any patent claims that are infringed by (i) modifications
|
||||
to the Software made by you or any third party or (ii) the Software in
|
||||
combination with any software or other technology.
|
||||
|
||||
The license granted hereunder will terminate, automatically and without
|
||||
notice, for anyone that makes any claim (including by filing any lawsuit,
|
||||
assertion or other action) alleging (a) direct, indirect, or contributory
|
||||
infringement or inducement to infringe any patent: (i) by Facebook or any
|
||||
of its subsidiaries or affiliates, whether or not such claim is related
|
||||
to the Software, (ii) by any party if such claim arises in whole or in
|
||||
part from any software, product or service of Facebook or any of its
|
||||
subsidiaries or affiliates, whether or not such claim is related to the
|
||||
Software, or (iii) by any party relating to the Software; or (b) that
|
||||
any right in any patent claim of Facebook is invalid or unenforceable.
|
||||
The license granted hereunder will terminate, automatically and without notice,
|
||||
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
|
||||
directly or indirectly, or take a direct financial interest in, any Patent
|
||||
Assertion: (i) against Facebook or any of its subsidiaries or corporate
|
||||
affiliates, (ii) against any party if such Patent Assertion arises in whole or
|
||||
in part from any software, technology, product or service of Facebook or any of
|
||||
its subsidiaries or corporate affiliates, or (iii) against any party relating
|
||||
to the Software. Notwithstanding the foregoing, if Facebook or any of its
|
||||
subsidiaries or corporate affiliates files a lawsuit alleging patent
|
||||
infringement against you in the first instance, and you respond by filing a
|
||||
patent infringement counterclaim in that lawsuit against that party that is
|
||||
unrelated to the Software, the license granted hereunder will not terminate
|
||||
under section (i) of this paragraph due to such counterclaim.
|
||||
|
||||
A "Necessary Claim" is a claim of a patent owned by Facebook that is
|
||||
necessarily infringed by the Software standing alone.
|
||||
|
||||
A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
|
||||
or contributory infringement or inducement to infringe any patent, including a
|
||||
cross-claim or counterclaim.
|
||||
@@ -0,0 +1,36 @@
|
||||
This document lists users of RocksDB and their use cases. If you are using RocksDB, please open a pull request and add yourself to the list.
|
||||
|
||||
## Facebook
|
||||
At Facebook, we use RocksDB as a backend for many different stateful services. We're also experimenting with running RocksDB as a storage engine for two databases:
|
||||
|
||||
1. MyRocks -- https://github.com/MySQLOnRocksDB/mysql-5.6
|
||||
2. MongoRocks -- https://github.com/mongodb-partners/mongo-rocks
|
||||
|
||||
## LinkedIn
|
||||
Two different use cases at Linkedin are using RocksDB as a storage engine:
|
||||
|
||||
1. LinkedIn's follow feed for storing user's activities
|
||||
2. Apache Samza, open source framework for stream processing
|
||||
|
||||
Learn more about those use cases in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
|
||||
|
||||
## Yahoo
|
||||
Yahoo is using RocksDB as a storage engine for their biggest distributed data store Sherpa.
|
||||
|
||||
## CockroachDB
|
||||
CockroachDB is an open-source geo-replicated transactional database (still in development). They are using RocksDB as their storage engine. Check out their github: https://github.com/cockroachdb/cockroach
|
||||
|
||||
## DNANexus
|
||||
DNANexus is using RocksDB to speed up processing of genomics data.
|
||||
You can learn more from this great blog post by Mike Lin: http://devblog.dnanexus.com/faster-bam-sorting-with-samtools-and-rocksdb/
|
||||
|
||||
## Iron.io
|
||||
Iron.io is using RocksDB as a storage engine for their distributed queueing system.
|
||||
Learn more from Tech Talk by Reed Allman: http://www.youtube.com/watch?v=HTjt6oj-RL4
|
||||
|
||||
## Tango Me
|
||||
Tango is using RocksDB as a graph storage to store all users' connection data and other social activity data.
|
||||
|
||||
## Turn
|
||||
Turn is using RocksDB as a storage layer for their key/value store, serving at peak 2.4MM QPS out of different datacenters.
|
||||
Check out our RocksDB Protobuf merge operator at: https://github.com/vladb38/rocksdb_protobuf
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
phutil_register_library('arcanist_util', __FILE__);
|
||||
@@ -10,17 +10,22 @@ phutil_register_library_map(array(
|
||||
'__library_version__' => 2,
|
||||
'class' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'cpp_linter/ArcanistCpplintLinter.php',
|
||||
'FacebookArcanistConfiguration' => 'config/FacebookArcanistConfiguration.php',
|
||||
'FacebookFbcodeLintEngine' => 'lint_engine/FacebookFbcodeLintEngine.php',
|
||||
'FacebookFbcodeUnitTestEngine' => 'unit_engine/FacebookFbcodeUnitTestEngine.php',
|
||||
'FbcodeCppLinter' => 'cpp_linter/FbcodeCppLinter.php',
|
||||
'PfffCppLinter' => 'cpp_linter/PfffCppLinter.php',
|
||||
'ArcanistCpplintLinter' => 'cpp_linter/ArcanistCpplintLinter.php',
|
||||
),
|
||||
'function' =>
|
||||
array(
|
||||
),
|
||||
'xmap' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'ArcanistLinter',
|
||||
'FacebookArcanistConfiguration' => 'ArcanistConfiguration',
|
||||
'FacebookFbcodeLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookFbcodeUnitTestEngine' => 'ArcanistBaseUnitTestEngine',
|
||||
'FbcodeCppLinter' => 'ArcanistLinter',
|
||||
'PfffCppLinter' => 'ArcanistLinter',
|
||||
),
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
class FacebookArcanistConfiguration extends ArcanistConfiguration {
|
||||
|
||||
public function didRunWorkflow($command,
|
||||
ArcanistBaseWorkflow $workflow,
|
||||
$error_code) {
|
||||
if ($command == 'diff' && !$workflow->isRawDiffSource()) {
|
||||
$this->maybePushToJenkins($workflow);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* Send off builds to jenkins */
|
||||
function maybePushToJenkins($workflow) {
|
||||
$diffID = $workflow->getDiffID();
|
||||
if ($diffID === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$results = $workflow->getTestResults();
|
||||
if (!$results) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = "https://ci-builds.fb.com/view/rocksdb/job/rocksdb_diff_check/"
|
||||
."buildWithParameters?token=AUTH&DIFF_ID=$diffID";
|
||||
system("curl --noproxy '*' \"$url\" > /dev/null 2>&1");
|
||||
}
|
||||
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistBaseUnitTestEngine {
|
||||
|
||||
public function run() {
|
||||
// Here we create a new unit test "jenkins_async_test" and promise we'll
|
||||
// update the results later.
|
||||
// Jenkins updates the results using `arc call-conduit
|
||||
// differential.updateunitresults` call. If you change the name here, also
|
||||
// make sure to change the name in Jenkins script that updates the test
|
||||
// result -- they have to be the same.
|
||||
$result = new ArcanistUnitTestResult();
|
||||
$result->setName("jenkins_async_test");
|
||||
$result->setResult(ArcanistUnitTestResult::RESULT_POSTPONED);
|
||||
return array($result);
|
||||
}
|
||||
}
|
||||
@@ -90,12 +90,11 @@ fi
|
||||
COMMON_FLAGS="$COMMON_FLAGS ${CFLAGS}"
|
||||
CROSS_COMPILE=
|
||||
PLATFORM_CCFLAGS=
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS ${CXXFLAGS}"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS"
|
||||
PLATFORM_SHARED_EXT="so"
|
||||
PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl,"
|
||||
PLATFORM_SHARED_CFLAGS="-fPIC"
|
||||
PLATFORM_SHARED_VERSIONED=false
|
||||
PLATFORM_SHARED_VERSIONED=true
|
||||
|
||||
# generic port files (working on all platform by #ifdef) go directly in /port
|
||||
GENERIC_PORT_FILES=`cd "$ROCKSDB_ROOT"; find port -name '*.cc' | tr "\n" " "`
|
||||
@@ -115,6 +114,7 @@ case "$TARGET_OS" in
|
||||
PLATFORM_SHARED_EXT=dylib
|
||||
PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name "
|
||||
CROSS_COMPILE=true
|
||||
PLATFORM_SHARED_VERSIONED=
|
||||
;;
|
||||
Linux)
|
||||
PLATFORM=OS_LINUX
|
||||
@@ -155,6 +155,16 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread"
|
||||
# PORT_FILES=port/dragonfly/dragonfly_specific.cc
|
||||
;;
|
||||
Cygwin)
|
||||
PLATFORM=CYGWIN
|
||||
PLATFORM_CXXFLAGS="-std=gnu++11"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DCYGWIN"
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
OS_ANDROID_CROSSCOMPILE)
|
||||
PLATFORM=OS_ANDROID
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX"
|
||||
@@ -167,6 +177,7 @@ case "$TARGET_OS" in
|
||||
exit 1
|
||||
esac
|
||||
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS ${CXXFLAGS}"
|
||||
JAVA_LDFLAGS="$PLATFORM_LDFLAGS"
|
||||
|
||||
if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
@@ -177,6 +188,7 @@ else
|
||||
# Test whether fallocate is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
int fd = open("/dev/null", 0);
|
||||
fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 0, 1024);
|
||||
@@ -198,11 +210,10 @@ EOF
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lsnappy"
|
||||
fi
|
||||
|
||||
|
||||
# Test whether gflags library is installed
|
||||
# http://code.google.com/p/gflags/
|
||||
# check if the namespace is gflags
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace gflags;
|
||||
int main() {}
|
||||
@@ -210,17 +221,17 @@ EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=gflags"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
fi
|
||||
|
||||
# check if namespace is google
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace google;
|
||||
int main() {}
|
||||
else
|
||||
# check if namespace is google
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace google;
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=google"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=google"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether zlib library is installed
|
||||
@@ -269,13 +280,18 @@ EOF
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lnuma"
|
||||
fi
|
||||
|
||||
# Test whether tcmalloc is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -ltcmalloc 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltcmalloc"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltcmalloc"
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null -ljemalloc \
|
||||
2>/dev/null; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ljemalloc"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ljemalloc"
|
||||
else
|
||||
# jemalloc is not available. Let's try tcmalloc
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null \
|
||||
-ltcmalloc 2>/dev/null; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltcmalloc"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltcmalloc"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -299,7 +315,7 @@ if test "$USE_HDFS"; then
|
||||
exit 1
|
||||
fi
|
||||
HDFS_CCFLAGS="$HDFS_CCFLAGS -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -DUSE_HDFS"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -Wl,--no-whole-archive -lhdfs -L$JAVA_HOME/jre/lib/amd64"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -lhdfs -L$JAVA_HOME/jre/lib/amd64"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -L$JAVA_HOME/jre/lib/amd64/server -L$GLIBC_RUNTIME_PATH/lib"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -ldl -lverify -ljava -ljvm"
|
||||
COMMON_FLAGS="$COMMON_FLAGS $HDFS_CCFLAGS"
|
||||
|
||||
@@ -69,7 +69,7 @@ CFLAGS+=" -DGFLAGS=google"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I /mnt/gvfs/third-party2/jemalloc/bcd68e5e419efa4e61b9486d6854564d6d75a0b5/3.6.0/gcc-4.9-glibc-2.20/2aafc78/include/"
|
||||
JEMALLOC_LIB=" -Wl,--whole-archive /mnt/gvfs/third-party2/jemalloc/bcd68e5e419efa4e61b9486d6854564d6d75a0b5/3.6.0/gcc-4.9-glibc-2.20/2aafc78/lib/libjemalloc.a"
|
||||
JEMALLOC_LIB=" /mnt/gvfs/third-party2/jemalloc/bcd68e5e419efa4e61b9486d6854564d6d75a0b5/3.6.0/gcc-4.9-glibc-2.20/2aafc78/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
@@ -130,7 +130,8 @@ CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.9-glibc-2.20/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--no-whole-archive $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.9-glibc-2.20/lib"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ GFLAGS_LIBS=" /mnt/gvfs/third-party2/gflags/1ad047a6e6f6673991918ecadc670868205a
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I /mnt/gvfs/third-party2/jemalloc/c60d854f7824f334195fe7fd34b2bc9057e3c1f9/3.6.0/gcc-4.8.1-glibc-2.17/4d53c6f/include"
|
||||
JEMALLOC_LIB=" -Wl,--whole-archive /mnt/gvfs/third-party2/jemalloc/c60d854f7824f334195fe7fd34b2bc9057e3c1f9/3.6.0/gcc-4.8.1-glibc-2.17/4d53c6f/lib/libjemalloc.a"
|
||||
JEMALLOC_LIB=" /mnt/gvfs/third-party2/jemalloc/c60d854f7824f334195fe7fd34b2bc9057e3c1f9/3.6.0/gcc-4.8.1-glibc-2.17/4d53c6f/lib/libjemalloc.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_REV=829d10dac0230f99cd7e1778869d2adf3da24b65
|
||||
@@ -93,7 +93,7 @@ CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--no-whole-archive $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
|
||||
+56
-26
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/builder.h"
|
||||
|
||||
#include <vector>
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/merge_helper.h"
|
||||
@@ -20,34 +21,41 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block_based_table_builder.h"
|
||||
#include "util/iostats_context_imp.h"
|
||||
#include "util/thread_status_util.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class TableFactory;
|
||||
|
||||
TableBuilder* NewTableBuilder(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file,
|
||||
const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
const bool skip_filters) {
|
||||
return ioptions.table_factory->NewTableBuilder(ioptions, internal_comparator,
|
||||
file, compression_type,
|
||||
compression_opts,
|
||||
skip_filters);
|
||||
TableBuilder* NewTableBuilder(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
WritableFile* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts, const bool skip_filters) {
|
||||
return ioptions.table_factory->NewTableBuilder(
|
||||
TableBuilderOptions(ioptions, internal_comparator,
|
||||
int_tbl_prop_collector_factories, compression_type,
|
||||
compression_opts, skip_filters),
|
||||
file);
|
||||
}
|
||||
|
||||
Status BuildTable(const std::string& dbname, Env* env,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const EnvOptions& env_options, TableCache* table_cache,
|
||||
Iterator* iter, FileMetaData* meta,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const SequenceNumber newest_snapshot,
|
||||
const SequenceNumber earliest_seqno_in_memtable,
|
||||
const CompressionType compression,
|
||||
const CompressionOptions& compression_opts,
|
||||
const Env::IOPriority io_priority) {
|
||||
Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& ioptions,
|
||||
const EnvOptions& env_options, TableCache* table_cache, Iterator* iter,
|
||||
FileMetaData* meta, const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
const SequenceNumber newest_snapshot,
|
||||
const SequenceNumber earliest_seqno_in_memtable,
|
||||
const CompressionType compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
const Env::IOPriority io_priority, TableProperties* table_properties) {
|
||||
// Reports the IOStats for flush for every following bytes.
|
||||
const size_t kReportFlushIOStatsEvery = 1048576;
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
meta->smallest_seqno = meta->largest_seqno = 0;
|
||||
@@ -72,8 +80,8 @@ Status BuildTable(const std::string& dbname, Env* env,
|
||||
file->SetIOPriority(io_priority);
|
||||
|
||||
TableBuilder* builder = NewTableBuilder(
|
||||
ioptions, internal_comparator, file.get(),
|
||||
compression, compression_opts);
|
||||
ioptions, internal_comparator, int_tbl_prop_collector_factories,
|
||||
file.get(), compression, compression_opts);
|
||||
|
||||
{
|
||||
// the first key is the smallest key
|
||||
@@ -172,6 +180,13 @@ Status BuildTable(const std::string& dbname, Env* env,
|
||||
}
|
||||
}
|
||||
|
||||
if (io_priority == Env::IO_HIGH &&
|
||||
IOSTATS(bytes_written) >= kReportFlushIOStatsEvery) {
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::FLUSH_BYTES_WRITTEN,
|
||||
IOSTATS(bytes_written));
|
||||
IOSTATS_RESET(bytes_written);
|
||||
}
|
||||
if (!iterator_at_next) iter->Next();
|
||||
}
|
||||
|
||||
@@ -189,19 +204,29 @@ Status BuildTable(const std::string& dbname, Env* env,
|
||||
SequenceNumber seqno = GetInternalKeySeqno(key);
|
||||
meta->smallest_seqno = std::min(meta->smallest_seqno, seqno);
|
||||
meta->largest_seqno = std::max(meta->largest_seqno, seqno);
|
||||
if (io_priority == Env::IO_HIGH &&
|
||||
IOSTATS(bytes_written) >= kReportFlushIOStatsEvery) {
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::FLUSH_BYTES_WRITTEN,
|
||||
IOSTATS(bytes_written));
|
||||
IOSTATS_RESET(bytes_written);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finish and check for builder errors
|
||||
if (s.ok()) {
|
||||
s = builder->Finish();
|
||||
if (s.ok()) {
|
||||
meta->fd.file_size = builder->FileSize();
|
||||
assert(meta->fd.GetFileSize() > 0);
|
||||
}
|
||||
} else {
|
||||
builder->Abandon();
|
||||
}
|
||||
if (s.ok()) {
|
||||
meta->fd.file_size = builder->FileSize();
|
||||
assert(meta->fd.GetFileSize() > 0);
|
||||
if (table_properties) {
|
||||
*table_properties = builder->GetTableProperties();
|
||||
}
|
||||
}
|
||||
delete builder;
|
||||
|
||||
// Finish and check for file errors
|
||||
@@ -223,6 +248,11 @@ Status BuildTable(const std::string& dbname, Env* env,
|
||||
Iterator* it = table_cache->NewIterator(ReadOptions(), env_options,
|
||||
internal_comparator, meta->fd);
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {}
|
||||
s = it->status();
|
||||
}
|
||||
|
||||
delete it;
|
||||
}
|
||||
}
|
||||
|
||||
+25
-17
@@ -6,12 +6,17 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/immutable_options.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -26,28 +31,31 @@ class VersionEdit;
|
||||
class TableBuilder;
|
||||
class WritableFile;
|
||||
|
||||
TableBuilder* NewTableBuilder(const ImmutableCFOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file,
|
||||
const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
const bool skip_filters = false);
|
||||
TableBuilder* NewTableBuilder(
|
||||
const ImmutableCFOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
WritableFile* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
const bool skip_filters = false);
|
||||
|
||||
// Build a Table file from the contents of *iter. The generated file
|
||||
// will be named according to number specified in meta. On success, the rest of
|
||||
// *meta will be filled with metadata about the generated table.
|
||||
// If no data is present in *iter, meta->file_size will be set to
|
||||
// zero, and no Table file will be produced.
|
||||
extern Status BuildTable(const std::string& dbname, Env* env,
|
||||
const ImmutableCFOptions& options,
|
||||
const EnvOptions& env_options,
|
||||
TableCache* table_cache, Iterator* iter,
|
||||
FileMetaData* meta,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const SequenceNumber newest_snapshot,
|
||||
const SequenceNumber earliest_seqno_in_memtable,
|
||||
const CompressionType compression,
|
||||
const CompressionOptions& compression_opts,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH);
|
||||
extern Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& options,
|
||||
const EnvOptions& env_options, TableCache* table_cache, Iterator* iter,
|
||||
FileMetaData* meta, const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
const SequenceNumber newest_snapshot,
|
||||
const SequenceNumber earliest_seqno_in_memtable,
|
||||
const CompressionType compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH,
|
||||
TableProperties* table_properties = nullptr);
|
||||
|
||||
} // namespace rocksdb
|
||||
+53
-27
@@ -100,6 +100,22 @@ const Comparator* ColumnFamilyHandleImpl::user_comparator() const {
|
||||
return cfd()->user_comparator();
|
||||
}
|
||||
|
||||
void GetIntTblPropCollectorFactory(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories) {
|
||||
auto& collector_factories = cf_options.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < cf_options.table_properties_collector_factories.size();
|
||||
++i) {
|
||||
assert(collector_factories[i]);
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
new UserKeyTablePropertiesCollectorFactory(collector_factories[i]));
|
||||
}
|
||||
// Add collector to collect internal key statistics
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
new InternalKeyPropertiesCollectorFactory);
|
||||
}
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
const InternalKeyComparator* icmp,
|
||||
const ColumnFamilyOptions& src) {
|
||||
@@ -120,7 +136,6 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
result.min_write_buffer_number_to_merge =
|
||||
std::min(result.min_write_buffer_number_to_merge,
|
||||
result.max_write_buffer_number - 1);
|
||||
result.compression_per_level = src.compression_per_level;
|
||||
if (result.max_mem_compaction_level >= result.num_levels) {
|
||||
result.max_mem_compaction_level = result.num_levels - 1;
|
||||
}
|
||||
@@ -139,21 +154,23 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
}
|
||||
}
|
||||
|
||||
// -- Sanitize the table properties collector
|
||||
// All user defined properties collectors will be wrapped by
|
||||
// UserKeyTablePropertiesCollector since for them they only have the
|
||||
// knowledge of the user keys; internal keys are invisible to them.
|
||||
auto& collector_factories = result.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < result.table_properties_collector_factories.size();
|
||||
++i) {
|
||||
assert(collector_factories[i]);
|
||||
collector_factories[i] =
|
||||
std::make_shared<UserKeyTablePropertiesCollectorFactory>(
|
||||
collector_factories[i]);
|
||||
if (!src.compression_per_level.empty()) {
|
||||
for (size_t level = 0; level < src.compression_per_level.size(); ++level) {
|
||||
if (!CompressionTypeSupported(src.compression_per_level[level])) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options.info_log,
|
||||
"Compression type chosen for level %zu is not supported: %s. "
|
||||
"RocksDB "
|
||||
"will not compress data on level %zu.",
|
||||
level, CompressionTypeToString(src.compression_per_level[level]),
|
||||
level);
|
||||
}
|
||||
}
|
||||
} else if (!CompressionTypeSupported(src.compression)) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options.info_log,
|
||||
"Compression type chosen is not supported: %s. RocksDB will not "
|
||||
"compress data.",
|
||||
CompressionTypeToString(src.compression));
|
||||
}
|
||||
// Add collector to collect internal key statistics
|
||||
collector_factories.push_back(
|
||||
std::make_shared<InternalKeyPropertiesCollectorFactory>());
|
||||
|
||||
if (result.compaction_style == kCompactionStyleFIFO) {
|
||||
result.num_levels = 1;
|
||||
@@ -297,6 +314,9 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
pending_compaction_(false) {
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
GetIntTblPropCollectorFactory(options_, &int_tbl_prop_collector_factories_);
|
||||
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
internal_stats_.reset(
|
||||
@@ -426,18 +446,18 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"(waiting for flush), max_write_buffer_number is set to %d",
|
||||
name_.c_str(), imm()->size(),
|
||||
mutable_cf_options.max_write_buffer_number);
|
||||
} else if (vstorage->NumLevelFiles(0) >=
|
||||
} else if (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, 1);
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
name_.c_str(), vstorage->NumLevelFiles(0));
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
vstorage->NumLevelFiles(0) >=
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
uint64_t slowdown =
|
||||
SlowdownAmount(vstorage->NumLevelFiles(0),
|
||||
SlowdownAmount(vstorage->l0_delay_trigger_count(),
|
||||
mutable_cf_options.level0_slowdown_writes_trigger,
|
||||
mutable_cf_options.level0_stop_writes_trigger);
|
||||
write_controller_token_ = write_controller->GetDelayToken(slowdown);
|
||||
@@ -445,7 +465,7 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stalling writes because we have %d level-0 files (%" PRIu64
|
||||
"us)",
|
||||
name_.c_str(), vstorage->NumLevelFiles(0), slowdown);
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count(), slowdown);
|
||||
} else if (mutable_cf_options.hard_rate_limit > 1.0 &&
|
||||
score > mutable_cf_options.hard_rate_limit) {
|
||||
uint64_t kHardLimitSlowdown = 1000;
|
||||
@@ -515,6 +535,9 @@ Compaction* ColumnFamilyData::PickCompaction(
|
||||
return result;
|
||||
}
|
||||
|
||||
const int ColumnFamilyData::kCompactAllLevels = -1;
|
||||
const int ColumnFamilyData::kCompactToBaseLevel = -2;
|
||||
|
||||
Compaction* ColumnFamilyData::CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int input_level, int output_level, uint32_t output_path_id,
|
||||
@@ -592,7 +615,7 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
|
||||
void* expected = SuperVersion::kSVInUse;
|
||||
if (local_sv_->CompareAndSwap(static_cast<void*>(sv), expected)) {
|
||||
// When we see kSVInUse in the ThreadLocal, we are sure ThreadLocal
|
||||
// storage has not been altered and no Scrape has happend. The
|
||||
// storage has not been altered and no Scrape has happened. The
|
||||
// SuperVersion is still current.
|
||||
return true;
|
||||
} else {
|
||||
@@ -608,18 +631,21 @@ void ColumnFamilyData::NotifyOnCompactionCompleted(
|
||||
DB* db, Compaction* c, const Status& status) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto listeners = ioptions()->listeners;
|
||||
assert(listeners.size() > 0U);
|
||||
CompactionJobInfo info;
|
||||
info.cf_name = c->column_family_data()->GetName();
|
||||
info.status = status;
|
||||
info.output_level = c->output_level();
|
||||
for (const auto fmd : *c->inputs(c->level())) {
|
||||
info.input_files.push_back(
|
||||
TableFileName(options_.db_paths,
|
||||
fmd->fd.GetNumber(),
|
||||
fmd->fd.GetPathId()));
|
||||
for (size_t i = 0; i < c->num_input_levels(); ++i) {
|
||||
for (const auto fmd : *c->inputs(i)) {
|
||||
info.input_files.push_back(
|
||||
TableFileName(options_.db_paths,
|
||||
fmd->fd.GetNumber(),
|
||||
fmd->fd.GetPathId()));
|
||||
}
|
||||
}
|
||||
for (const auto newf : c->edit()->GetNewFiles()) {
|
||||
info.input_files.push_back(
|
||||
info.output_files.push_back(
|
||||
TableFileName(options_.db_paths,
|
||||
newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()));
|
||||
|
||||
+32
-6
@@ -21,6 +21,7 @@
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
@@ -84,24 +85,23 @@ class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
||||
|
||||
// holds references to memtable, all immutable memtables and version
|
||||
struct SuperVersion {
|
||||
// Accessing members of this class is not thread-safe and requires external
|
||||
// synchronization (ie db mutex held or on write thread).
|
||||
MemTable* mem;
|
||||
MemTableListVersion* imm;
|
||||
Version* current;
|
||||
MutableCFOptions mutable_cf_options;
|
||||
std::atomic<uint32_t> refs;
|
||||
// We need to_delete because during Cleanup(), imm->Unref() returns
|
||||
// all memtables that we need to free through this vector. We then
|
||||
// delete all those memtables outside of mutex, during destruction
|
||||
autovector<MemTable*> to_delete;
|
||||
// Version number of the current SuperVersion
|
||||
uint64_t version_number;
|
||||
|
||||
InstrumentedMutex* db_mutex;
|
||||
|
||||
// should be called outside the mutex
|
||||
SuperVersion() = default;
|
||||
~SuperVersion();
|
||||
SuperVersion* Ref();
|
||||
|
||||
// If Unref() returns true, Cleanup() should be called with mutex held
|
||||
// before deleting this SuperVersion.
|
||||
bool Unref();
|
||||
|
||||
// call these two methods with db mutex held
|
||||
@@ -120,11 +120,25 @@ struct SuperVersion {
|
||||
static int dummy;
|
||||
static void* const kSVInUse;
|
||||
static void* const kSVObsolete;
|
||||
|
||||
private:
|
||||
std::atomic<uint32_t> refs;
|
||||
// We need to_delete because during Cleanup(), imm->Unref() returns
|
||||
// all memtables that we need to free through this vector. We then
|
||||
// delete all those memtables outside of mutex, during destruction
|
||||
autovector<MemTable*> to_delete;
|
||||
};
|
||||
|
||||
extern ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
const InternalKeyComparator* icmp,
|
||||
const ColumnFamilyOptions& src);
|
||||
// Wrap user defined table proproties collector factories `from cf_options`
|
||||
// into internal ones in int_tbl_prop_collector_factories. Add a system internal
|
||||
// one too.
|
||||
extern void GetIntTblPropCollectorFactory(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories);
|
||||
|
||||
class ColumnFamilySet;
|
||||
|
||||
@@ -222,6 +236,11 @@ class ColumnFamilyData {
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* PickCompaction(const MutableCFOptions& mutable_options,
|
||||
LogBuffer* log_buffer);
|
||||
// A flag to tell a manual compaction is to compact all levels together
|
||||
// instad of for specific level.
|
||||
static const int kCompactAllLevels;
|
||||
// A flag to tell a manual compaction's output is base level.
|
||||
static const int kCompactToBaseLevel;
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
@@ -239,6 +258,11 @@ class ColumnFamilyData {
|
||||
return internal_comparator_;
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories() const {
|
||||
return &int_tbl_prop_collector_factories_;
|
||||
}
|
||||
|
||||
SuperVersion* GetSuperVersion() { return super_version_; }
|
||||
// thread-safe
|
||||
// Return a already referenced SuperVersion to be used safely.
|
||||
@@ -307,6 +331,8 @@ class ColumnFamilyData {
|
||||
bool dropped_; // true if client dropped it
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
|
||||
int_tbl_prop_collector_factories_;
|
||||
|
||||
const Options options_;
|
||||
const ImmutableCFOptions ioptions_;
|
||||
|
||||
@@ -118,7 +118,11 @@ class ColumnFamilyTest : public testing::Test {
|
||||
int GetProperty(int cf, std::string property) {
|
||||
std::string value;
|
||||
EXPECT_TRUE(dbfull()->GetProperty(handles_[cf], property, &value));
|
||||
#ifndef CYGWIN
|
||||
return std::stoi(value);
|
||||
#else
|
||||
return std::strtol(value.c_str(), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
@@ -264,7 +268,7 @@ class ColumnFamilyTest : public testing::Test {
|
||||
VectorLogPtr wal_files;
|
||||
Status s;
|
||||
// GetSortedWalFiles is a flakey function -- it gets all the wal_dir
|
||||
// children files and then later checks for their existance. if some of the
|
||||
// children files and then later checks for their existence. if some of the
|
||||
// log files doesn't exist anymore, it reports an error. it does all of this
|
||||
// without DB mutex held, so if a background process deletes the log file
|
||||
// while the function is being executed, it returns an error. We retry the
|
||||
@@ -548,9 +552,9 @@ TEST_F(ColumnFamilyTest, FlushTest) {
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
uint64_t max_total_in_memory_state =
|
||||
dbfull()->TEST_max_total_in_memory_state();
|
||||
dbfull()->TEST_MaxTotalInMemoryState();
|
||||
Flush(i);
|
||||
ASSERT_EQ(dbfull()->TEST_max_total_in_memory_state(),
|
||||
ASSERT_EQ(dbfull()->TEST_MaxTotalInMemoryState(),
|
||||
max_total_in_memory_state);
|
||||
}
|
||||
ASSERT_OK(Put(1, "foofoo", "bar"));
|
||||
@@ -720,7 +724,9 @@ TEST_F(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
WaitForFlush(1);
|
||||
AssertNumberOfImmutableMemtables({0, 0, 0, 1});
|
||||
ASSERT_EQ(CountLiveLogFiles(), 5);
|
||||
PutRandomData(3, 90*6, 1000);
|
||||
PutRandomData(3, 240, 1000);
|
||||
WaitForFlush(3);
|
||||
PutRandomData(3, 300, 1000);
|
||||
WaitForFlush(3);
|
||||
AssertNumberOfImmutableMemtables({0, 0, 0, 0});
|
||||
ASSERT_EQ(CountLiveLogFiles(), 12);
|
||||
@@ -807,6 +813,7 @@ TEST_F(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
one.compaction_style = kCompactionStyleUniversal;
|
||||
one.num_levels = 1;
|
||||
// trigger compaction if there are >= 4 files
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 100000;
|
||||
|
||||
+98
-111
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -35,83 +36,91 @@ void Compaction::SetInputVersion(Version* _input_version) {
|
||||
|
||||
cfd_->Ref();
|
||||
input_version_->Ref();
|
||||
edit_ = new VersionEdit();
|
||||
edit_->SetColumnFamily(cfd_->GetID());
|
||||
edit_.SetColumnFamily(cfd_->GetID());
|
||||
}
|
||||
|
||||
Compaction::Compaction(int number_levels, int _start_level, int out_level,
|
||||
uint64_t target_file_size,
|
||||
uint64_t max_grandparent_overlap_bytes,
|
||||
uint32_t output_path_id,
|
||||
CompressionType output_compression, bool seek_compaction,
|
||||
bool deletion_compaction)
|
||||
: start_level_(_start_level),
|
||||
output_level_(out_level),
|
||||
max_output_file_size_(target_file_size),
|
||||
max_grandparent_overlap_bytes_(max_grandparent_overlap_bytes),
|
||||
input_version_(nullptr),
|
||||
edit_(nullptr),
|
||||
number_levels_(number_levels),
|
||||
cfd_(nullptr),
|
||||
output_path_id_(output_path_id),
|
||||
output_compression_(output_compression),
|
||||
seek_compaction_(seek_compaction),
|
||||
deletion_compaction_(deletion_compaction),
|
||||
grandparent_index_(0),
|
||||
seen_key_(false),
|
||||
overlapped_bytes_(0),
|
||||
base_index_(-1),
|
||||
parent_index_(-1),
|
||||
score_(0),
|
||||
bottommost_level_(false),
|
||||
is_full_compaction_(false),
|
||||
is_manual_compaction_(false),
|
||||
level_ptrs_(std::vector<size_t>(number_levels_)) {
|
||||
for (int i = 0; i < number_levels_; i++) {
|
||||
level_ptrs_[i] = 0;
|
||||
// helper function to determine if compaction is creating files at the
|
||||
// bottommost level
|
||||
bool Compaction::IsBottommostLevel(
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs) {
|
||||
if (inputs[0].level == 0 &&
|
||||
inputs[0].files.back() != vstorage->LevelFiles(0).back()) {
|
||||
return false;
|
||||
}
|
||||
int num_levels = output_level_ - start_level_ + 1;
|
||||
input_levels_.resize(num_levels);
|
||||
inputs_.resize(num_levels);
|
||||
for (int i = 0; i < num_levels; ++i) {
|
||||
inputs_[i].level = start_level_ + i;
|
||||
|
||||
// checks whether there are files living beyond the output_level.
|
||||
for (int i = output_level + 1; i < vstorage->num_levels(); i++) {
|
||||
if (vstorage->NumLevelFiles(i) > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Compaction::IsFullCompaction(
|
||||
VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs) {
|
||||
int num_files_in_compaction = 0;
|
||||
int total_num_files = 0;
|
||||
for (int l = 0; l < vstorage->num_levels(); l++) {
|
||||
total_num_files += vstorage->NumLevelFiles(l);
|
||||
}
|
||||
for (size_t i = 0; i < inputs.size(); i++) {
|
||||
num_files_in_compaction += inputs[i].size();
|
||||
}
|
||||
return num_files_in_compaction == total_num_files;
|
||||
}
|
||||
|
||||
Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
const autovector<CompactionInputFiles>& _inputs,
|
||||
int _start_level, int _output_level,
|
||||
uint64_t _max_grandparent_overlap_bytes,
|
||||
const CompactionOptions& _options,
|
||||
bool _deletion_compaction)
|
||||
: start_level_(_start_level),
|
||||
const MutableCFOptions& _mutable_cf_options,
|
||||
std::vector<CompactionInputFiles> _inputs,
|
||||
int _output_level, uint64_t _target_file_size,
|
||||
uint64_t _max_grandparent_overlap_bytes,
|
||||
uint32_t _output_path_id, CompressionType _compression,
|
||||
std::vector<FileMetaData*> _grandparents,
|
||||
bool _manual_compaction, double _score,
|
||||
bool _deletion_compaction)
|
||||
: start_level_(_inputs[0].level),
|
||||
output_level_(_output_level),
|
||||
max_output_file_size_(_options.output_file_size_limit),
|
||||
max_output_file_size_(_target_file_size),
|
||||
max_grandparent_overlap_bytes_(_max_grandparent_overlap_bytes),
|
||||
mutable_cf_options_(_mutable_cf_options),
|
||||
input_version_(nullptr),
|
||||
number_levels_(vstorage->num_levels()),
|
||||
cfd_(nullptr),
|
||||
output_compression_(_options.compression),
|
||||
seek_compaction_(false),
|
||||
output_path_id_(_output_path_id),
|
||||
output_compression_(_compression),
|
||||
deletion_compaction_(_deletion_compaction),
|
||||
inputs_(_inputs),
|
||||
inputs_(std::move(_inputs)),
|
||||
grandparents_(std::move(_grandparents)),
|
||||
grandparent_index_(0),
|
||||
seen_key_(false),
|
||||
overlapped_bytes_(0),
|
||||
base_index_(-1),
|
||||
parent_index_(-1),
|
||||
score_(0),
|
||||
bottommost_level_(false),
|
||||
is_full_compaction_(false),
|
||||
is_manual_compaction_(false),
|
||||
level_ptrs_(std::vector<size_t>(number_levels_)) {
|
||||
for (int i = 0; i < number_levels_; i++) {
|
||||
level_ptrs_[i] = 0;
|
||||
score_(_score),
|
||||
bottommost_level_(IsBottommostLevel(output_level_, vstorage, inputs_)),
|
||||
is_full_compaction_(IsFullCompaction(vstorage, inputs_)),
|
||||
is_manual_compaction_(_manual_compaction),
|
||||
level_ptrs_(std::vector<size_t>(number_levels_, 0)) {
|
||||
MarkFilesBeingCompacted(true);
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i < inputs_.size(); ++i) {
|
||||
assert(inputs_[i].level > inputs_[i - 1].level);
|
||||
}
|
||||
#endif
|
||||
|
||||
// setup input_levels_
|
||||
{
|
||||
input_levels_.resize(num_input_levels());
|
||||
for (size_t which = 0; which < num_input_levels(); which++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
|
||||
&arena_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Compaction::~Compaction() {
|
||||
delete edit_;
|
||||
if (input_version_ != nullptr) {
|
||||
input_version_->Unref();
|
||||
}
|
||||
@@ -122,12 +131,16 @@ Compaction::~Compaction() {
|
||||
}
|
||||
}
|
||||
|
||||
void Compaction::GenerateFileLevels() {
|
||||
input_levels_.resize(num_input_levels());
|
||||
for (size_t which = 0; which < num_input_levels(); which++) {
|
||||
DoGenerateLevelFilesBrief(&input_levels_[which], inputs_[which].files,
|
||||
&arena_);
|
||||
bool Compaction::InputCompressionMatchesOutput() const {
|
||||
int base_level = input_version_->storage_info()->base_level();
|
||||
bool matches = (GetCompressionType(*cfd_->ioptions(), start_level_,
|
||||
base_level) == output_compression_);
|
||||
if (matches) {
|
||||
TEST_SYNC_POINT("Compaction::InputCompressionMatchesOutput:Matches");
|
||||
return true;
|
||||
}
|
||||
TEST_SYNC_POINT("Compaction::InputCompressionMatchesOutput:DidntMatch");
|
||||
return matches;
|
||||
}
|
||||
|
||||
bool Compaction::IsTrivialMove() const {
|
||||
@@ -136,11 +149,10 @@ bool Compaction::IsTrivialMove() const {
|
||||
// a very expensive merge later on.
|
||||
// If start_level_== output_level_, the purpose is to force compaction
|
||||
// filter to be applied to that level, and thus cannot be a trivia move.
|
||||
return (start_level_ != output_level_ &&
|
||||
num_input_levels() == 2 &&
|
||||
return (start_level_ != output_level_ && num_input_levels() == 1 &&
|
||||
num_input_files(0) == 1 &&
|
||||
num_input_files(1) == 0 &&
|
||||
input(0, 0)->fd.GetPathId() == GetOutputPathId() &&
|
||||
InputCompressionMatchesOutput() &&
|
||||
TotalFileSize(grandparents_) <= max_grandparent_overlap_bytes_);
|
||||
}
|
||||
|
||||
@@ -217,31 +229,6 @@ void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) {
|
||||
}
|
||||
}
|
||||
|
||||
// Is this compaction producing files at the bottommost level?
|
||||
void Compaction::SetupBottomMostLevel(VersionStorageInfo* vstorage,
|
||||
bool is_manual, bool level0_only) {
|
||||
if (level0_only) {
|
||||
// If universal compaction style is used and manual
|
||||
// compaction is occuring, then we are guaranteed that
|
||||
// all files will be picked in a single compaction
|
||||
// run. We can safely set bottommost_level_ = true.
|
||||
// If it is not manual compaction, then bottommost_level_
|
||||
// is already set when the Compaction was created.
|
||||
if (is_manual) {
|
||||
bottommost_level_ = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
bottommost_level_ = true;
|
||||
// checks whether there are files living beyond the output_level.
|
||||
for (int i = output_level_ + 1; i < number_levels_; i++) {
|
||||
if (vstorage->NumLevelFiles(i) > 0) {
|
||||
bottommost_level_ = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sample output:
|
||||
// If compacting 3 L0 files, 2 L3 files and 1 L4 file, and outputting to L5,
|
||||
// print: "3@0 + 2@3 + 1@4 files to L5"
|
||||
@@ -268,7 +255,18 @@ const char* Compaction::InputLevelSummary(
|
||||
return scratch->buffer;
|
||||
}
|
||||
|
||||
uint64_t Compaction::CalculateTotalInputSize() const {
|
||||
uint64_t size = 0;
|
||||
for (auto& input_level : inputs_) {
|
||||
for (auto f : input_level.files) {
|
||||
size += f->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void Compaction::ReleaseCompactionFiles(Status status) {
|
||||
MarkFilesBeingCompacted(false);
|
||||
cfd_->compaction_picker()->ReleaseCompactionFiles(this, status);
|
||||
}
|
||||
|
||||
@@ -300,9 +298,9 @@ int InputSummary(const std::vector<FileMetaData*>& files, char* output,
|
||||
void Compaction::Summary(char* output, int len) {
|
||||
int write =
|
||||
snprintf(output, len, "Base version %" PRIu64
|
||||
" Base level %d, seek compaction:%d, inputs: [",
|
||||
" Base level %d, inputs: [",
|
||||
input_version_->GetVersionNumber(),
|
||||
start_level_, seek_compaction_);
|
||||
start_level_);
|
||||
if (write < 0 || write >= len) {
|
||||
return;
|
||||
}
|
||||
@@ -324,17 +322,17 @@ void Compaction::Summary(char* output, int len) {
|
||||
snprintf(output + write, len - write, "]");
|
||||
}
|
||||
|
||||
uint64_t Compaction::OutputFilePreallocationSize(
|
||||
const MutableCFOptions& mutable_options) {
|
||||
uint64_t Compaction::OutputFilePreallocationSize() {
|
||||
uint64_t preallocation_size = 0;
|
||||
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
preallocation_size = mutable_options.MaxFileSizeForLevel(output_level());
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0) {
|
||||
preallocation_size = max_output_file_size_;
|
||||
} else {
|
||||
for (size_t level_iter = 0; level_iter < num_input_levels(); ++level_iter) {
|
||||
for (const auto& f : inputs_[level_iter].files) {
|
||||
preallocation_size += f->fd.GetFileSize();
|
||||
}
|
||||
// output_level() == 0
|
||||
assert(num_input_levels() > 0);
|
||||
for (const auto& f : inputs_[0].files) {
|
||||
preallocation_size += f->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
// Over-estimate slightly so we don't end up just barely crossing
|
||||
@@ -342,15 +340,4 @@ uint64_t Compaction::OutputFilePreallocationSize(
|
||||
return preallocation_size * 1.1;
|
||||
}
|
||||
|
||||
Compaction* Compaction::TEST_NewCompaction(
|
||||
int num_levels, int start_level, int out_level, uint64_t target_file_size,
|
||||
uint64_t max_grandparent_overlap_bytes, uint32_t output_path_id,
|
||||
CompressionType output_compression, bool seek_compaction,
|
||||
bool deletion_compaction) {
|
||||
return new Compaction(num_levels, start_level, out_level, target_file_size,
|
||||
max_grandparent_overlap_bytes, output_path_id,
|
||||
output_compression, seek_compaction,
|
||||
deletion_compaction);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+37
-60
@@ -34,11 +34,13 @@ class VersionStorageInfo;
|
||||
class Compaction {
|
||||
public:
|
||||
Compaction(VersionStorageInfo* input_version,
|
||||
const autovector<CompactionInputFiles>& inputs,
|
||||
int start_level, int output_level,
|
||||
uint64_t max_grandparent_overlap_bytes,
|
||||
const CompactionOptions& options,
|
||||
bool deletion_compaction);
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
std::vector<CompactionInputFiles> inputs, int output_level,
|
||||
uint64_t target_file_size, uint64_t max_grandparent_overlap_bytes,
|
||||
uint32_t output_path_id, CompressionType compression,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
bool manual_compaction = false, double score = -1,
|
||||
bool deletion_compaction = false);
|
||||
|
||||
// No copying allowed
|
||||
Compaction(const Compaction&) = delete;
|
||||
@@ -62,7 +64,7 @@ class Compaction {
|
||||
|
||||
// Return the object that holds the edits to the descriptor done
|
||||
// by this compaction.
|
||||
VersionEdit* edit() const { return edit_; }
|
||||
VersionEdit* edit() { return &edit_; }
|
||||
|
||||
// Returns the number of input files associated to the specified
|
||||
// compaction input level.
|
||||
@@ -113,10 +115,6 @@ class Compaction {
|
||||
// Whether need to write output file to second DB path.
|
||||
uint32_t GetOutputPathId() const { return output_path_id_; }
|
||||
|
||||
// Generate input_levels_ from inputs_
|
||||
// Should be called when inputs_ is stable
|
||||
void GenerateFileLevels();
|
||||
|
||||
// Is this a trivial compaction that can be implemented by just
|
||||
// moving a single input file to the next level (no merging or splitting)
|
||||
bool IsTrivialMove() const;
|
||||
@@ -158,8 +156,6 @@ class Compaction {
|
||||
// Was this compaction triggered manually by the client?
|
||||
bool IsManualCompaction() { return is_manual_compaction_; }
|
||||
|
||||
void SetOutputPathId(uint32_t path_id) { output_path_id_ = path_id; }
|
||||
|
||||
// Return the MutableCFOptions that should be used throughout the compaction
|
||||
// procedure
|
||||
const MutableCFOptions* mutable_cf_options() { return &mutable_cf_options_; }
|
||||
@@ -167,46 +163,33 @@ class Compaction {
|
||||
// Returns the size in bytes that the output file should be preallocated to.
|
||||
// In level compaction, that is max_file_size_. In universal compaction, that
|
||||
// is the sum of all input file sizes.
|
||||
uint64_t OutputFilePreallocationSize(const MutableCFOptions& mutable_options);
|
||||
uint64_t OutputFilePreallocationSize();
|
||||
|
||||
void SetInputVersion(Version* input_version);
|
||||
|
||||
// mark (or clear) all files that are being compacted
|
||||
void MarkFilesBeingCompacted(bool mark_as_compacted);
|
||||
|
||||
// Initialize whether the compaction is producing files at the
|
||||
// bottommost level.
|
||||
//
|
||||
// @see BottomMostLevel()
|
||||
void SetupBottomMostLevel(VersionStorageInfo* vstorage, bool is_manual,
|
||||
bool level0_only);
|
||||
|
||||
static Compaction* TEST_NewCompaction(
|
||||
int num_levels, int start_level, int out_level, uint64_t target_file_size,
|
||||
uint64_t max_grandparent_overlap_bytes, uint32_t output_path_id,
|
||||
CompressionType output_compression, bool seek_compaction = false,
|
||||
bool deletion_compaction = false);
|
||||
|
||||
CompactionInputFiles* TEST_GetInputFiles(int l) {
|
||||
return &inputs_[l];
|
||||
}
|
||||
|
||||
struct InputLevelSummaryBuffer {
|
||||
char buffer[128];
|
||||
};
|
||||
|
||||
const char* InputLevelSummary(InputLevelSummaryBuffer* scratch) const;
|
||||
|
||||
private:
|
||||
friend class CompactionPicker;
|
||||
friend class UniversalCompactionPicker;
|
||||
friend class FIFOCompactionPicker;
|
||||
friend class LevelCompactionPicker;
|
||||
uint64_t CalculateTotalInputSize() const;
|
||||
|
||||
Compaction(int num_levels, int start_level, int out_level,
|
||||
uint64_t target_file_size, uint64_t max_grandparent_overlap_bytes,
|
||||
uint32_t output_path_id, CompressionType output_compression,
|
||||
bool seek_compaction = false, bool deletion_compaction = false);
|
||||
// In case of compaction error, reset the nextIndex that is used
|
||||
// to pick up the next file to be compacted from files_by_size_
|
||||
void ResetNextCompactionIndex();
|
||||
|
||||
private:
|
||||
// mark (or clear) all files that are being compacted
|
||||
void MarkFilesBeingCompacted(bool mark_as_compacted);
|
||||
|
||||
// helper function to determine if compaction with inputs and storage is
|
||||
// bottommost
|
||||
static bool IsBottommostLevel(
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs);
|
||||
static bool IsFullCompaction(VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs);
|
||||
|
||||
const int start_level_; // the lowest level to be compacted
|
||||
const int output_level_; // levels to which output files are stored
|
||||
@@ -214,43 +197,38 @@ class Compaction {
|
||||
uint64_t max_grandparent_overlap_bytes_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
Version* input_version_;
|
||||
VersionEdit* edit_;
|
||||
int number_levels_;
|
||||
VersionEdit edit_;
|
||||
const int number_levels_;
|
||||
ColumnFamilyData* cfd_;
|
||||
Arena arena_; // Arena used to allocate space for file_levels_
|
||||
|
||||
uint32_t output_path_id_;
|
||||
const uint32_t output_path_id_;
|
||||
CompressionType output_compression_;
|
||||
bool seek_compaction_;
|
||||
// If true, then the comaction can be done by simply deleting input files.
|
||||
bool deletion_compaction_;
|
||||
const bool deletion_compaction_;
|
||||
|
||||
// Compaction input files organized by level.
|
||||
autovector<CompactionInputFiles> inputs_;
|
||||
// Compaction input files organized by level. Constant after construction
|
||||
const std::vector<CompactionInputFiles> inputs_;
|
||||
|
||||
// A copy of inputs_, organized more closely in memory
|
||||
autovector<LevelFilesBrief, 2> input_levels_;
|
||||
|
||||
// State used to check for number of of overlapping grandparent files
|
||||
// (grandparent == "output_level_ + 1")
|
||||
// This vector is updated by Version::GetOverlappingInputs().
|
||||
std::vector<FileMetaData*> grandparents_;
|
||||
size_t grandparent_index_; // Index in grandparent_starts_
|
||||
bool seen_key_; // Some output key has been seen
|
||||
uint64_t overlapped_bytes_; // Bytes of overlap between current output
|
||||
// and grandparent files
|
||||
int base_index_; // index of the file in files_[start_level_]
|
||||
int parent_index_; // index of some file with same range in
|
||||
// files_[start_level_+1]
|
||||
double score_; // score that was used to pick this compaction.
|
||||
const double score_; // score that was used to pick this compaction.
|
||||
|
||||
// Is this compaction creating a file in the bottom most level?
|
||||
bool bottommost_level_;
|
||||
const bool bottommost_level_;
|
||||
// Does this compaction include all sst files?
|
||||
bool is_full_compaction_;
|
||||
const bool is_full_compaction_;
|
||||
|
||||
// Is this compaction requested by the client?
|
||||
bool is_manual_compaction_;
|
||||
const bool is_manual_compaction_;
|
||||
|
||||
// "level_ptrs_" holds indices into "input_version_->levels_", where each
|
||||
// index remembers which file of an associated level we are currently used
|
||||
@@ -259,9 +237,8 @@ class Compaction {
|
||||
// records indices for all levels beyond "output_level_".
|
||||
std::vector<size_t> level_ptrs_;
|
||||
|
||||
// In case of compaction error, reset the nextIndex that is used
|
||||
// to pick up the next file to be compacted from files_by_size_
|
||||
void ResetNextCompactionIndex();
|
||||
// Does input compression match the output compression?
|
||||
bool InputCompressionMatchesOutput() const;
|
||||
};
|
||||
|
||||
// Utility function
|
||||
|
||||
+133
-53
@@ -22,6 +22,7 @@
|
||||
#include "db/builder.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_logger_helpers.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
@@ -49,6 +50,7 @@
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/iostats_context_imp.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/thread_status_util.h"
|
||||
|
||||
@@ -57,12 +59,6 @@ namespace rocksdb {
|
||||
struct CompactionJob::CompactionState {
|
||||
Compaction* const compaction;
|
||||
|
||||
// If there were two snapshots with seq numbers s1 and
|
||||
// s2 and s1 < s2, and if we find two instances of a key k1 then lies
|
||||
// entirely within s1 and s2, then the earlier version of k1 can be safely
|
||||
// deleted because that version is not visible in any snapshot.
|
||||
std::vector<SequenceNumber> existing_snapshots;
|
||||
|
||||
// Files produced by compaction
|
||||
struct Output {
|
||||
uint64_t number;
|
||||
@@ -203,17 +199,17 @@ struct CompactionJob::CompactionState {
|
||||
|
||||
CompactionJob::CompactionJob(
|
||||
int job_id, Compaction* compaction, const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
VersionSet* versions, std::atomic<bool>* shutting_down,
|
||||
LogBuffer* log_buffer, Directory* db_directory, Directory* output_directory,
|
||||
Statistics* stats, SnapshotList* snapshots, bool is_snapshot_supported,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory, Statistics* stats,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
std::shared_ptr<Cache> table_cache,
|
||||
std::function<uint64_t()> yield_callback)
|
||||
std::function<uint64_t()> yield_callback, EventLogger* event_logger,
|
||||
bool paranoid_file_checks)
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_stats_(1),
|
||||
db_options_(db_options),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
env_options_(env_options),
|
||||
env_(db_options.env),
|
||||
versions_(versions),
|
||||
@@ -222,13 +218,14 @@ CompactionJob::CompactionJob(
|
||||
db_directory_(db_directory),
|
||||
output_directory_(output_directory),
|
||||
stats_(stats),
|
||||
snapshots_(snapshots),
|
||||
is_snapshot_supported_(is_snapshot_supported),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
table_cache_(std::move(table_cache)),
|
||||
yield_callback_(std::move(yield_callback)) {
|
||||
ThreadStatusUtil::SetColumnFamily(
|
||||
compact_->compaction->column_family_data());
|
||||
yield_callback_(std::move(yield_callback)),
|
||||
event_logger_(event_logger),
|
||||
paranoid_file_checks_(paranoid_file_checks) {
|
||||
ThreadStatusUtil::SetColumnFamily(compact_->compaction->column_family_data());
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
|
||||
ReportStartedCompaction(compaction);
|
||||
}
|
||||
|
||||
CompactionJob::~CompactionJob() {
|
||||
@@ -236,29 +233,53 @@ CompactionJob::~CompactionJob() {
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
}
|
||||
|
||||
void CompactionJob::ReportStartedCompaction(
|
||||
Compaction* compaction) {
|
||||
ThreadStatusUtil::SetColumnFamily(
|
||||
compact_->compaction->column_family_data());
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_JOB_ID,
|
||||
job_id_);
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_INPUT_OUTPUT_LEVEL,
|
||||
(static_cast<uint64_t>(compact_->compaction->start_level()) << 32) +
|
||||
compact_->compaction->output_level());
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_PROP_FLAGS,
|
||||
compaction->IsManualCompaction() +
|
||||
(compaction->IsDeletionCompaction() << 1) +
|
||||
(compaction->IsTrivialMove() << 2));
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_TOTAL_INPUT_BYTES,
|
||||
compaction->CalculateTotalInputSize());
|
||||
|
||||
IOSTATS_RESET(bytes_written);
|
||||
IOSTATS_RESET(bytes_read);
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_BYTES_WRITTEN, 0);
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_BYTES_READ, 0);
|
||||
|
||||
// Set the thread operation after operation properties
|
||||
// to ensure GetThreadList() can always show them all together.
|
||||
ThreadStatusUtil::SetThreadOperation(
|
||||
ThreadStatus::OP_COMPACTION);
|
||||
}
|
||||
|
||||
void CompactionJob::Prepare() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PREPARE);
|
||||
compact_->CleanupBatchBuffer();
|
||||
compact_->CleanupMergedBuffer();
|
||||
|
||||
auto* compaction = compact_->compaction;
|
||||
|
||||
// Generate file_levels_ for compaction berfore making Iterator
|
||||
compaction->GenerateFileLevels();
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
ColumnFamilyData* cfd __attribute__((unused)) =
|
||||
compact_->compaction->column_family_data();
|
||||
assert(cfd != nullptr);
|
||||
{
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
LogToBuffer(log_buffer_, "[%s] [JOB %d] Compacting %s, score %.2f",
|
||||
cfd->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary),
|
||||
compaction->score());
|
||||
}
|
||||
char scratch[2345];
|
||||
compact_->compaction->Summary(scratch, sizeof(scratch));
|
||||
LogToBuffer(log_buffer_, "[%s] Compaction start summary: %s\n",
|
||||
cfd->GetName().c_str(), scratch);
|
||||
|
||||
assert(cfd->current()->storage_info()->NumLevelFiles(
|
||||
compact_->compaction->level()) > 0);
|
||||
@@ -267,18 +288,16 @@ void CompactionJob::Prepare() {
|
||||
|
||||
visible_at_tip_ = 0;
|
||||
latest_snapshot_ = 0;
|
||||
// TODO(icanadi) move snapshots_ out of CompactionJob
|
||||
snapshots_->getAll(compact_->existing_snapshots);
|
||||
if (compact_->existing_snapshots.size() == 0) {
|
||||
if (existing_snapshots_.size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = versions_->LastSequence();
|
||||
earliest_snapshot_ = visible_at_tip_;
|
||||
} else {
|
||||
latest_snapshot_ = compact_->existing_snapshots.back();
|
||||
latest_snapshot_ = existing_snapshots_.back();
|
||||
// Add the current seqno as the 'latest' virtual
|
||||
// snapshot to the end of this list.
|
||||
compact_->existing_snapshots.push_back(versions_->LastSequence());
|
||||
earliest_snapshot_ = compact_->existing_snapshots[0];
|
||||
existing_snapshots_.push_back(versions_->LastSequence());
|
||||
earliest_snapshot_ = existing_snapshots_[0];
|
||||
}
|
||||
|
||||
// Is this compaction producing files at the bottommost level?
|
||||
@@ -292,6 +311,35 @@ Status CompactionJob::Run() {
|
||||
log_buffer_->FlushBufferToLog();
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
|
||||
auto* compaction = compact_->compaction;
|
||||
// Let's check if anything will get logged. Don't prepare all the info if
|
||||
// we're not logging
|
||||
if (db_options_.info_log_level <= InfoLogLevel::INFO_LEVEL) {
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Compacting %s, score %.2f", cfd->GetName().c_str(),
|
||||
job_id_, compaction->InputLevelSummary(&inputs_summary),
|
||||
compaction->score());
|
||||
char scratch[2345];
|
||||
compact_->compaction->Summary(scratch, sizeof(scratch));
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Compaction start summary: %s\n", cfd->GetName().c_str(), scratch);
|
||||
// build event logger report
|
||||
auto stream = event_logger_->Log();
|
||||
stream << "job" << job_id_ << "event"
|
||||
<< "compaction_started";
|
||||
for (size_t i = 0; i < compaction->num_input_levels(); ++i) {
|
||||
stream << ("files_L" + ToString(compaction->level(i)));
|
||||
stream.StartArray();
|
||||
for (auto f : *compaction->inputs(i)) {
|
||||
stream << f->fd.GetNumber();
|
||||
}
|
||||
stream.EndArray();
|
||||
}
|
||||
stream << "score" << compaction->score() << "input_data_size"
|
||||
<< compaction->CalculateTotalInputSize();
|
||||
}
|
||||
|
||||
const uint64_t start_micros = env_->NowMicros();
|
||||
std::unique_ptr<Iterator> input(
|
||||
versions_->MakeInputIterator(compact_->compaction));
|
||||
@@ -482,7 +530,6 @@ Status CompactionJob::Run() {
|
||||
if (compact_->num_input_records > compact_->num_output_records) {
|
||||
compaction_stats_.num_dropped_records +=
|
||||
compact_->num_input_records - compact_->num_output_records;
|
||||
compact_->num_input_records = compact_->num_output_records = 0;
|
||||
}
|
||||
|
||||
RecordCompactionIOStats();
|
||||
@@ -492,7 +539,9 @@ Status CompactionJob::Run() {
|
||||
return status;
|
||||
}
|
||||
|
||||
void CompactionJob::Install(Status* status, InstrumentedMutex* db_mutex) {
|
||||
void CompactionJob::Install(Status* status,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
InstrumentedMutex* db_mutex) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_INSTALL);
|
||||
db_mutex->AssertHeld();
|
||||
@@ -501,17 +550,17 @@ void CompactionJob::Install(Status* status, InstrumentedMutex* db_mutex) {
|
||||
compact_->compaction->output_level(), compaction_stats_);
|
||||
|
||||
if (status->ok()) {
|
||||
*status = InstallCompactionResults(db_mutex);
|
||||
*status = InstallCompactionResults(db_mutex, mutable_cf_options);
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
auto vstorage = cfd->current()->storage_info();
|
||||
const auto& stats = compaction_stats_;
|
||||
LogToBuffer(log_buffer_,
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
"files in(%d, %d) out(%d) "
|
||||
"MB in(%.1f, %.1f) out(%.1f), read-write-amplify(%.1f) "
|
||||
"write-amplify(%.1f) %s, records in: %d, records dropped: %d\n",
|
||||
cfd->GetName().c_str(),
|
||||
cfd->current()->storage_info()->LevelSummary(&tmp),
|
||||
cfd->GetName().c_str(), vstorage->LevelSummary(&tmp),
|
||||
(stats.bytes_readn + stats.bytes_readnp1) /
|
||||
static_cast<double>(stats.micros),
|
||||
stats.bytes_written / static_cast<double>(stats.micros),
|
||||
@@ -525,6 +574,21 @@ void CompactionJob::Install(Status* status, InstrumentedMutex* db_mutex) {
|
||||
status->ToString().c_str(), stats.num_input_records,
|
||||
stats.num_dropped_records);
|
||||
|
||||
auto stream = event_logger_->LogToBuffer(log_buffer_);
|
||||
stream << "job" << job_id_ << "event"
|
||||
<< "compaction_finished"
|
||||
<< "output_level" << compact_->compaction->output_level()
|
||||
<< "num_output_files" << compact_->outputs.size()
|
||||
<< "total_output_size" << compact_->total_bytes << "num_input_records"
|
||||
<< compact_->num_input_records << "num_output_records"
|
||||
<< compact_->num_output_records;
|
||||
stream << "lsm_state";
|
||||
stream.StartArray();
|
||||
for (int level = 0; level < vstorage->num_levels(); ++level) {
|
||||
stream << vstorage->NumLevelFiles(level);
|
||||
}
|
||||
stream.EndArray();
|
||||
|
||||
CleanupCompaction(*status);
|
||||
}
|
||||
|
||||
@@ -684,11 +748,8 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
|
||||
SequenceNumber visible =
|
||||
visible_at_tip_
|
||||
? visible_at_tip_
|
||||
: is_snapshot_supported_
|
||||
? findEarliestVisibleSnapshot(ikey.sequence,
|
||||
compact_->existing_snapshots,
|
||||
&prev_snapshot)
|
||||
: 0;
|
||||
: findEarliestVisibleSnapshot(ikey.sequence, existing_snapshots_,
|
||||
&prev_snapshot);
|
||||
|
||||
if (visible_in_snapshot == visible) {
|
||||
// If the earliest snapshot is which this key is visible in
|
||||
@@ -950,6 +1011,7 @@ Status CompactionJob::FinishCompactionOutputFile(Iterator* input) {
|
||||
const uint32_t output_path_id = compact_->current_output()->path_id;
|
||||
assert(output_number != 0);
|
||||
|
||||
TableProperties table_properties;
|
||||
// Check for iterator errors
|
||||
Status s = input->status();
|
||||
const uint64_t current_entries = compact_->builder->NumEntries();
|
||||
@@ -958,6 +1020,9 @@ Status CompactionJob::FinishCompactionOutputFile(Iterator* input) {
|
||||
} else {
|
||||
compact_->builder->Abandon();
|
||||
}
|
||||
if (s.ok()) {
|
||||
table_properties = compact_->builder->GetTableProperties();
|
||||
}
|
||||
const uint64_t current_bytes = compact_->builder->FileSize();
|
||||
compact_->current_output()->file_size = current_bytes;
|
||||
compact_->total_bytes += current_bytes;
|
||||
@@ -985,6 +1050,12 @@ Status CompactionJob::FinishCompactionOutputFile(Iterator* input) {
|
||||
Iterator* iter = cfd->table_cache()->NewIterator(
|
||||
ReadOptions(), env_options_, cfd->internal_comparator(), fd);
|
||||
s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {}
|
||||
s = iter->status();
|
||||
}
|
||||
|
||||
delete iter;
|
||||
if (s.ok()) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
@@ -992,12 +1063,16 @@ Status CompactionJob::FinishCompactionOutputFile(Iterator* input) {
|
||||
" keys, %" PRIu64 " bytes",
|
||||
cfd->GetName().c_str(), job_id_, output_number, current_entries,
|
||||
current_bytes);
|
||||
EventLoggerHelpers::LogTableFileCreation(event_logger_, job_id_,
|
||||
output_number, current_bytes,
|
||||
table_properties);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status CompactionJob::InstallCompactionResults(InstrumentedMutex* db_mutex) {
|
||||
Status CompactionJob::InstallCompactionResults(
|
||||
InstrumentedMutex* db_mutex, const MutableCFOptions& mutable_cf_options) {
|
||||
db_mutex->AssertHeld();
|
||||
|
||||
auto* compaction = compact_->compaction;
|
||||
@@ -1032,7 +1107,7 @@ Status CompactionJob::InstallCompactionResults(InstrumentedMutex* db_mutex) {
|
||||
out.smallest, out.largest, out.smallest_seqno, out.largest_seqno);
|
||||
}
|
||||
return versions_->LogAndApply(compaction->column_family_data(),
|
||||
mutable_cf_options_, compaction->edit(),
|
||||
mutable_cf_options, compaction->edit(),
|
||||
db_mutex, db_directory_);
|
||||
}
|
||||
|
||||
@@ -1067,8 +1142,12 @@ inline SequenceNumber CompactionJob::findEarliestVisibleSnapshot(
|
||||
|
||||
void CompactionJob::RecordCompactionIOStats() {
|
||||
RecordTick(stats_, COMPACT_READ_BYTES, IOSTATS(bytes_read));
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_BYTES_READ, IOSTATS(bytes_read));
|
||||
IOSTATS_RESET(bytes_read);
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, IOSTATS(bytes_written));
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_BYTES_WRITTEN, IOSTATS(bytes_written));
|
||||
IOSTATS_RESET(bytes_written);
|
||||
}
|
||||
|
||||
@@ -1100,8 +1179,8 @@ Status CompactionJob::OpenCompactionOutputFile() {
|
||||
|
||||
compact_->outputs.push_back(out);
|
||||
compact_->outfile->SetIOPriority(Env::IO_LOW);
|
||||
compact_->outfile->SetPreallocationBlockSize(static_cast<size_t>(
|
||||
compact_->compaction->OutputFilePreallocationSize(mutable_cf_options_)));
|
||||
compact_->outfile->SetPreallocationBlockSize(
|
||||
static_cast<size_t>(compact_->compaction->OutputFilePreallocationSize()));
|
||||
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
bool skip_filters = false;
|
||||
@@ -1115,7 +1194,8 @@ Status CompactionJob::OpenCompactionOutputFile() {
|
||||
}
|
||||
|
||||
compact_->builder.reset(NewTableBuilder(
|
||||
*cfd->ioptions(), cfd->internal_comparator(), compact_->outfile.get(),
|
||||
*cfd->ioptions(), cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), compact_->outfile.get(),
|
||||
compact_->compaction->OutputCompressionType(),
|
||||
cfd->ioptions()->compression_opts, skip_filters));
|
||||
LogFlush(db_options_.info_log);
|
||||
|
||||
+21
-13
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/snapshot.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/memtable_list.h"
|
||||
@@ -30,6 +29,7 @@
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "util/scoped_arena_iterator.h"
|
||||
@@ -50,17 +50,15 @@ class Arena;
|
||||
|
||||
class CompactionJob {
|
||||
public:
|
||||
// TODO(icanadi) make effort to reduce number of parameters here
|
||||
// IMPORTANT: mutable_cf_options needs to be alive while CompactionJob is
|
||||
// alive
|
||||
CompactionJob(int job_id, Compaction* compaction, const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory,
|
||||
Statistics* stats, SnapshotList* snapshot_list,
|
||||
bool is_snapshot_supported, std::shared_ptr<Cache> table_cache,
|
||||
std::function<uint64_t()> yield_callback);
|
||||
Statistics* stats,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
std::shared_ptr<Cache> table_cache,
|
||||
std::function<uint64_t()> yield_callback,
|
||||
EventLogger* event_logger, bool paranoid_file_checks);
|
||||
|
||||
~CompactionJob();
|
||||
|
||||
@@ -75,9 +73,12 @@ class CompactionJob {
|
||||
Status Run();
|
||||
// REQUIRED: mutex held
|
||||
// status is the return of Run()
|
||||
void Install(Status* status, InstrumentedMutex* db_mutex);
|
||||
void Install(Status* status, const MutableCFOptions& mutable_cf_options,
|
||||
InstrumentedMutex* db_mutex);
|
||||
|
||||
private:
|
||||
// update the thread status for starting a compaction.
|
||||
void ReportStartedCompaction(Compaction* compaction);
|
||||
void AllocateCompactionOutputFileNumbers();
|
||||
// Call compaction filter if is_compaction_v2 is not true. Then iterate
|
||||
// through input and compact the kv-pairs
|
||||
@@ -87,7 +88,8 @@ class CompactionJob {
|
||||
void CallCompactionFilterV2(CompactionFilterV2* compaction_filter_v2,
|
||||
uint64_t* time);
|
||||
Status FinishCompactionOutputFile(Iterator* input);
|
||||
Status InstallCompactionResults(InstrumentedMutex* db_mutex);
|
||||
Status InstallCompactionResults(InstrumentedMutex* db_mutex,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
SequenceNumber findEarliestVisibleSnapshot(
|
||||
SequenceNumber in, const std::vector<SequenceNumber>& snapshots,
|
||||
SequenceNumber* prev_snapshot);
|
||||
@@ -110,7 +112,6 @@ class CompactionJob {
|
||||
|
||||
// DBImpl state
|
||||
const DBOptions& db_options_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const EnvOptions& env_options_;
|
||||
Env* env_;
|
||||
VersionSet* versions_;
|
||||
@@ -119,12 +120,19 @@ class CompactionJob {
|
||||
Directory* db_directory_;
|
||||
Directory* output_directory_;
|
||||
Statistics* stats_;
|
||||
SnapshotList* snapshots_;
|
||||
bool is_snapshot_supported_;
|
||||
// If there were two snapshots with seq numbers s1 and
|
||||
// s2 and s1 < s2, and if we find two instances of a key k1 then lies
|
||||
// entirely within s1 and s2, then the earlier version of k1 can be safely
|
||||
// deleted because that version is not visible in any snapshot.
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
// yield callback
|
||||
std::function<uint64_t()> yield_callback_;
|
||||
|
||||
EventLogger* event_logger_;
|
||||
|
||||
bool paranoid_file_checks_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+17
-14
@@ -66,6 +66,9 @@ class CompactionJobTest : public testing::Test {
|
||||
auto key = ToString(i * (kKeysPerFile / 2) + k);
|
||||
auto value = ToString(i * kKeysPerFile + k);
|
||||
InternalKey internal_key(key, ++sequence_number, kTypeValue);
|
||||
// This is how the key will look like once it's written in bottommost
|
||||
// file
|
||||
InternalKey bottommost_internal_key(key, 0, kTypeValue);
|
||||
if (k == 0) {
|
||||
smallest = internal_key;
|
||||
smallest_seqno = sequence_number;
|
||||
@@ -74,7 +77,7 @@ class CompactionJobTest : public testing::Test {
|
||||
largest_seqno = sequence_number;
|
||||
}
|
||||
std::pair<std::string, std::string> key_value(
|
||||
{internal_key.Encode().ToString(), value});
|
||||
{bottommost_internal_key.Encode().ToString(), value});
|
||||
contents.insert(key_value);
|
||||
if (i == 1 || k < kKeysPerFile / 2) {
|
||||
expected_results.insert(key_value);
|
||||
@@ -143,16 +146,15 @@ TEST_F(CompactionJobTest, Simple) {
|
||||
auto files = cfd->current()->storage_info()->LevelFiles(0);
|
||||
ASSERT_EQ(2U, files.size());
|
||||
|
||||
std::unique_ptr<Compaction> compaction(Compaction::TEST_NewCompaction(
|
||||
7, 0, 1, 1024 * 1024, 10, 0, kNoCompression));
|
||||
CompactionInputFiles compaction_input_files;
|
||||
compaction_input_files.level = 0;
|
||||
compaction_input_files.files.push_back(files[0]);
|
||||
compaction_input_files.files.push_back(files[1]);
|
||||
std::unique_ptr<Compaction> compaction(new Compaction(
|
||||
cfd->current()->storage_info(), *cfd->GetLatestMutableCFOptions(),
|
||||
{compaction_input_files}, 1, 1024 * 1024, 10, 0, kNoCompression, {}));
|
||||
compaction->SetInputVersion(cfd->current());
|
||||
|
||||
auto compaction_input_files = compaction->TEST_GetInputFiles(0);
|
||||
compaction_input_files->level = 0;
|
||||
compaction_input_files->files.push_back(files[0]);
|
||||
compaction_input_files->files.push_back(files[1]);
|
||||
|
||||
SnapshotList snapshots;
|
||||
int yield_callback_called = 0;
|
||||
std::function<uint64_t()> yield_callback = [&]() {
|
||||
yield_callback_called++;
|
||||
@@ -160,17 +162,18 @@ TEST_F(CompactionJobTest, Simple) {
|
||||
};
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
mutex_.Lock();
|
||||
CompactionJob compaction_job(0, compaction.get(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), env_options_,
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
CompactionJob compaction_job(0, compaction.get(), db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_, &log_buffer,
|
||||
nullptr, nullptr, nullptr, &snapshots, true,
|
||||
table_cache_, std::move(yield_callback));
|
||||
nullptr, nullptr, nullptr, {}, table_cache_,
|
||||
std::move(yield_callback), &event_logger, false);
|
||||
|
||||
compaction_job.Prepare();
|
||||
mutex_.Unlock();
|
||||
ASSERT_OK(compaction_job.Run());
|
||||
mutex_.Lock();
|
||||
Status s;
|
||||
compaction_job.Install(&s, &mutex_);
|
||||
compaction_job.Install(&s, *cfd->GetLatestMutableCFOptions(), &mutex_);
|
||||
ASSERT_OK(s);
|
||||
mutex_.Unlock();
|
||||
|
||||
|
||||
+610
-375
File diff suppressed because it is too large.
Load diff
+78
-49
@@ -64,10 +64,6 @@ class CompactionPicker {
|
||||
uint32_t output_path_id, const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end);
|
||||
|
||||
// Given the current number of levels, returns the lowest allowed level
|
||||
// for compaction input.
|
||||
virtual int MaxInputLevel(int current_num_levels) const = 0;
|
||||
|
||||
// The maximum allowed output level. Default value is NumberLevels() - 1.
|
||||
virtual int MaxOutputLevel() const {
|
||||
return NumberLevels() - 1;
|
||||
@@ -97,14 +93,14 @@ class CompactionPicker {
|
||||
// Takes a list of CompactionInputFiles and returns a Compaction object.
|
||||
Compaction* FormCompaction(
|
||||
const CompactionOptions& compact_options,
|
||||
const autovector<CompactionInputFiles>& input_files,
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options) const;
|
||||
const std::vector<CompactionInputFiles>& input_files, int output_level,
|
||||
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
|
||||
uint32_t output_path_id) const;
|
||||
|
||||
// Converts a set of compaction input file numbers into
|
||||
// a list of CompactionInputFiles.
|
||||
Status GetCompactionInputsFromFileNumbers(
|
||||
autovector<CompactionInputFiles>* input_files,
|
||||
std::vector<CompactionInputFiles>* input_files,
|
||||
std::unordered_set<uint64_t>* input_set,
|
||||
const VersionStorageInfo* vstorage,
|
||||
const CompactionOptions& compact_options) const;
|
||||
@@ -115,14 +111,14 @@ class CompactionPicker {
|
||||
// Stores the minimal range that covers all entries in inputs in
|
||||
// *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty
|
||||
void GetRange(const std::vector<FileMetaData*>& inputs, InternalKey* smallest,
|
||||
InternalKey* largest);
|
||||
void GetRange(const CompactionInputFiles& inputs,
|
||||
InternalKey* smallest, InternalKey* largest);
|
||||
|
||||
// Stores the minimal range that covers all entries in inputs1 and inputs2
|
||||
// in *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty
|
||||
void GetRange(const std::vector<FileMetaData*>& inputs1,
|
||||
const std::vector<FileMetaData*>& inputs2,
|
||||
void GetRange(const CompactionInputFiles& inputs1,
|
||||
const CompactionInputFiles& inputs2,
|
||||
InternalKey* smallest, InternalKey* largest);
|
||||
|
||||
// Add more files to the inputs on "level" to make sure that
|
||||
@@ -136,16 +132,25 @@ class CompactionPicker {
|
||||
//
|
||||
// Will return false if it is impossible to apply this compaction.
|
||||
bool ExpandWhileOverlapping(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage, Compaction* c);
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs);
|
||||
|
||||
// Returns true if any one of the parent files are being compacted
|
||||
bool RangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level, int* index);
|
||||
|
||||
void SetupOtherInputs(const std::string& cf_name,
|
||||
bool SetupOtherInputs(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, Compaction* c);
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs,
|
||||
CompactionInputFiles* output_level_inputs,
|
||||
int* parent_index, int base_index);
|
||||
|
||||
void GetGrandparents(VersionStorageInfo* vstorage,
|
||||
const CompactionInputFiles& inputs,
|
||||
const CompactionInputFiles& output_level_inputs,
|
||||
std::vector<FileMetaData*>* grandparents);
|
||||
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
|
||||
@@ -175,12 +180,6 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
// Returns current_num_levels - 2, meaning the last level cannot be
|
||||
// compaction input level.
|
||||
virtual int MaxInputLevel(int current_num_levels) const override {
|
||||
return current_num_levels - 2;
|
||||
}
|
||||
|
||||
virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const
|
||||
override;
|
||||
|
||||
@@ -190,13 +189,22 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
int level);
|
||||
|
||||
private:
|
||||
// For the specfied level, pick a compaction.
|
||||
// Returns nullptr if there is no compaction to be done.
|
||||
// For the specfied level, pick a file that we want to compact.
|
||||
// Returns false if there is no file to compact.
|
||||
// If it returns true, inputs->files.size() will be exactly one.
|
||||
// If level is 0 and there is already a compaction on that level, this
|
||||
// function will return nullptr.
|
||||
Compaction* PickCompactionBySize(const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int level,
|
||||
double score);
|
||||
// function will return false.
|
||||
bool PickCompactionBySize(VersionStorageInfo* vstorage, int level,
|
||||
int output_level, CompactionInputFiles* inputs,
|
||||
int* parent_index, int* base_index);
|
||||
|
||||
// If there is any file marked for compaction, put put it into inputs.
|
||||
// This is still experimental. It will return meaningful results only if
|
||||
// clients call experimental feature SuggestCompactRange()
|
||||
void PickFilesMarkedForCompactionExperimental(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs,
|
||||
int* level, int* output_level);
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -210,30 +218,58 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
// The maxinum allowed input level. Always returns 0.
|
||||
virtual int MaxInputLevel(int current_num_levels) const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
virtual int MaxOutputLevel() const override {
|
||||
return 0;
|
||||
}
|
||||
virtual int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
|
||||
virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const
|
||||
override;
|
||||
|
||||
private:
|
||||
struct SortedRun {
|
||||
SortedRun(int _level, FileMetaData* _file, uint64_t _size,
|
||||
uint64_t _compensated_file_size, bool _being_compacted)
|
||||
: level(_level),
|
||||
file(_file),
|
||||
size(_size),
|
||||
compensated_file_size(_compensated_file_size),
|
||||
being_compacted(_being_compacted) {
|
||||
assert(compensated_file_size > 0);
|
||||
assert(level != 0 || file != nullptr);
|
||||
}
|
||||
|
||||
void Dump(char* out_buf, size_t out_buf_size,
|
||||
bool print_path = false) const;
|
||||
|
||||
// sorted_run_count is added into the string to print
|
||||
void DumpSizeInfo(char* out_buf, size_t out_buf_size,
|
||||
int sorted_run_count) const;
|
||||
|
||||
int level;
|
||||
// `file` Will be null for level > 0. For level = 0, the sorted run is
|
||||
// for this file.
|
||||
FileMetaData* file;
|
||||
// For level > 0, `size` and `compensated_file_size` are sum of sizes all
|
||||
// files in the level. `being_compacted` should be the same for all files
|
||||
// in a non-zero level. Use the value here.
|
||||
uint64_t size;
|
||||
uint64_t compensated_file_size;
|
||||
bool being_compacted;
|
||||
};
|
||||
|
||||
// Pick Universal compaction to limit read amplification
|
||||
Compaction* PickCompactionUniversalReadAmp(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score, unsigned int ratio,
|
||||
unsigned int num_files, LogBuffer* log_buffer);
|
||||
unsigned int num_files, const std::vector<SortedRun>& sorted_runs,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Pick Universal compaction to limit space amplification.
|
||||
Compaction* PickCompactionUniversalSizeAmp(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score, LogBuffer* log_buffer);
|
||||
VersionStorageInfo* vstorage, double score,
|
||||
const std::vector<SortedRun>& sorted_runs, LogBuffer* log_buffer);
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -258,11 +294,6 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
uint32_t output_path_id, const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end) override;
|
||||
|
||||
// The maxinum allowed input level. Always returns 0.
|
||||
virtual int MaxInputLevel(int current_num_levels) const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
virtual int MaxOutputLevel() const override {
|
||||
return 0;
|
||||
@@ -296,12 +327,6 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Given the current number of levels, returns the highest allowed level
|
||||
// for compaction input.
|
||||
virtual int MaxInputLevel(int current_num_levels) const override {
|
||||
return current_num_levels - 2;
|
||||
}
|
||||
|
||||
// Always returns false.
|
||||
virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const
|
||||
override {
|
||||
@@ -310,4 +335,8 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
};
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
int level, int base_level,
|
||||
const bool enable_compression = true);
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -229,7 +229,7 @@ TEST_F(CompactionPickerTest, Level0TriggerDynamic) {
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(num_levels, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(1, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(num_levels - 1, compaction->output_level());
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ TEST_F(CompactionPickerTest, Level0TriggerDynamic2) {
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(num_levels - 1, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(1, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(num_levels - 2, compaction->output_level());
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ TEST_F(CompactionPickerTest, Level0TriggerDynamic3) {
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(num_levels - 2, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(1, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(num_levels - 3, compaction->output_level());
|
||||
}
|
||||
|
||||
@@ -306,10 +306,11 @@ TEST_F(CompactionPickerTest, Level0TriggerDynamic4) {
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(num_levels - 3));
|
||||
ASSERT_EQ(5U, compaction->input(num_levels - 3, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(6U, compaction->input(num_levels - 3, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(num_levels - 2, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(2U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(num_levels - 3, compaction->level(1));
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(6U, compaction->input(1, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(2, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(num_levels - 3, compaction->output_level());
|
||||
}
|
||||
|
||||
@@ -349,13 +350,16 @@ TEST_F(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
// must return false when there's no files.
|
||||
ASSERT_EQ(universal_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
false);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// verify the trigger given different number of L0 files.
|
||||
for (int i = 1;
|
||||
i <= mutable_cf_options_.level0_file_num_compaction_trigger * 2; ++i) {
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
Add(0, i, ToString((i + 100) * 1000).c_str(),
|
||||
ToString((i + 100) * 1000 + 999).c_str(), 1000000, 0, i * 100,
|
||||
i * 100 + 99);
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(level_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
vstorage_->CompactionScore(0) >= 1);
|
||||
}
|
||||
@@ -372,6 +376,7 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
ioptions_.compaction_options_fifo = fifo_options_;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
// must return false when there's no files.
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), false);
|
||||
|
||||
@@ -379,15 +384,39 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
// size of L0 files.
|
||||
uint64_t current_size = 0;
|
||||
for (int i = 1; i <= kFileCount; ++i) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
Add(0, i, ToString((i + 100) * 1000).c_str(),
|
||||
ToString((i + 100) * 1000 + 999).c_str(),
|
||||
kFileSize, 0, i * 100, i * 100 + 99);
|
||||
current_size += kFileSize;
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(level_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
vstorage_->CompactionScore(0) >= 1);
|
||||
}
|
||||
}
|
||||
|
||||
// This test exhibits the bug where we don't properly reset parent_index in
|
||||
// PickCompaction()
|
||||
TEST_F(CompactionPickerTest, ParentIndexResetBug) {
|
||||
int num_levels = ioptions_.num_levels;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 200;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200"); // <- marked for compaction
|
||||
Add(1, 3U, "400", "500", 600); // <- this one needs compacting
|
||||
Add(2, 4U, "150", "200");
|
||||
Add(2, 5U, "201", "210");
|
||||
Add(2, 6U, "300", "310");
|
||||
Add(2, 7U, "400", "500"); // <- being compacted
|
||||
|
||||
vstorage_->LevelFiles(2)[3]->being_compacted = true;
|
||||
vstorage_->LevelFiles(0)[0]->marked_for_compaction = true;
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ class CorruptionTest : public testing::Test {
|
||||
// db itself will raise errors because data is corrupted.
|
||||
// Instead, we want the reads to be successful and this test
|
||||
// will detect whether the appropriate corruptions have
|
||||
// occured.
|
||||
// occurred.
|
||||
Iterator* iter = db_->NewIterator(ReadOptions(false, true));
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
uint64_t key;
|
||||
|
||||
+164
-54
@@ -24,6 +24,8 @@ int main() {
|
||||
#include <numaif.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <cstddef>
|
||||
#include <sys/types.h>
|
||||
@@ -40,8 +42,10 @@ int main() {
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/utilities/flashcache.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/crc32c.h"
|
||||
@@ -70,6 +74,7 @@ DEFINE_string(benchmarks,
|
||||
"newiteratorwhilewriting,"
|
||||
"seekrandom,"
|
||||
"seekrandomwhilewriting,"
|
||||
"seekrandomwhilemerging,"
|
||||
"readseq,"
|
||||
"readreverse,"
|
||||
"compact,"
|
||||
@@ -129,8 +134,12 @@ DEFINE_string(benchmarks,
|
||||
"\treadrandommergerandom -- perform N random read-or-merge "
|
||||
"operations. Must be used with merge_operator\n"
|
||||
"\tnewiterator -- repeated iterator creation\n"
|
||||
"\tseekrandom -- N random seeks\n"
|
||||
"\tseekrandom -- 1 writer, N threads doing random seeks\n"
|
||||
"\tseekrandom -- N random seeks, call Next seek_nexts times "
|
||||
"per seek\n"
|
||||
"\tseekrandomwhilewriting -- seekrandom and 1 thread doing "
|
||||
"overwrite\n"
|
||||
"\tseekrandomwhilemerging -- seekrandom and 1 thread doing "
|
||||
"merge\n"
|
||||
"\tcrc32c -- repeated crc32c of 4K of data\n"
|
||||
"\txxhash -- repeated xxHash of 4K of data\n"
|
||||
"\tacquireload -- load N*1000 times\n"
|
||||
@@ -181,7 +190,8 @@ DEFINE_int32(value_size, 100, "Size of each value");
|
||||
|
||||
DEFINE_int32(seek_nexts, 0,
|
||||
"How many times to call Next() after Seek() in "
|
||||
"fillseekseq and seekrandom");
|
||||
"fillseekseq, seekrandom, seekrandomwhilewriting and "
|
||||
"seekrandomwhilemerging");
|
||||
|
||||
DEFINE_bool(reverse_iterator, false,
|
||||
"When true use Prev rather than Next for iterators that do "
|
||||
@@ -469,6 +479,9 @@ static rocksdb::Env* FLAGS_env = rocksdb::Env::Default();
|
||||
DEFINE_int64(stats_interval, 0, "Stats are reported every N operations when "
|
||||
"this is greater than zero. When 0 the interval grows over time.");
|
||||
|
||||
DEFINE_int64(stats_interval_seconds, 0, "Report stats every N seconds. This "
|
||||
"overrides stats_interval when both are > 0.");
|
||||
|
||||
DEFINE_int32(stats_per_interval, 0, "Reports additional stats per interval when"
|
||||
" this is greater than 0.");
|
||||
|
||||
@@ -497,6 +510,8 @@ DEFINE_int32(rate_limit_delay_max_milliseconds, 1000,
|
||||
"When hard_rate_limit is set then this is the max time a put will"
|
||||
" be stalled.");
|
||||
|
||||
DEFINE_uint64(rate_limiter_bytes_per_sec, 0, "Set options.rate_limiter value.");
|
||||
|
||||
DEFINE_int32(max_grandparent_overlap_factor, 10, "Control maximum bytes of "
|
||||
"overlaps in grandparent (i.e., level+2) before we stop building a"
|
||||
" single file in a level->level+1 compaction.");
|
||||
@@ -531,18 +546,27 @@ DEFINE_string(compaction_fadvice, "NORMAL",
|
||||
static auto FLAGS_compaction_fadvice_e =
|
||||
rocksdb::Options().access_hint_on_compaction_start;
|
||||
|
||||
DEFINE_bool(disable_flashcache_for_background_threads, false,
|
||||
"Disable flashcache for background threads");
|
||||
|
||||
DEFINE_string(flashcache_dev, "", "Path to flashcache device");
|
||||
|
||||
DEFINE_bool(use_tailing_iterator, false,
|
||||
"Use tailing iterator to access a series of keys instead of get");
|
||||
DEFINE_int64(iter_refresh_interval_us, -1,
|
||||
"How often to refresh iterators. Disable refresh when -1");
|
||||
|
||||
DEFINE_bool(use_adaptive_mutex, rocksdb::Options().use_adaptive_mutex,
|
||||
"Use adaptive mutex");
|
||||
|
||||
DEFINE_uint64(bytes_per_sync, rocksdb::Options().bytes_per_sync,
|
||||
"Allows OS to incrementally sync files to disk while they are"
|
||||
"Allows OS to incrementally sync SST files to disk while they are"
|
||||
" being written, in the background. Issue one request for every"
|
||||
" bytes_per_sync written. 0 turns it off.");
|
||||
|
||||
DEFINE_uint64(wal_bytes_per_sync, rocksdb::Options().wal_bytes_per_sync,
|
||||
"Allows OS to incrementally sync WAL files to disk while they are"
|
||||
" being written, in the background. Issue one request for every"
|
||||
" wal_bytes_per_sync written. 0 turns it off.");
|
||||
|
||||
DEFINE_bool(filter_deletes, false, " On true, deletes use bloom-filter and drop"
|
||||
" the delete if key not present");
|
||||
|
||||
@@ -937,14 +961,14 @@ class Stats {
|
||||
std::vector<ThreadStatus> thread_list;
|
||||
FLAGS_env->GetThreadList(&thread_list);
|
||||
|
||||
fprintf(stderr, "\n%18s %10s %25s %12s %12s %45s %12s\n",
|
||||
fprintf(stderr, "\n%18s %10s %12s %20s %13s %45s %12s %s\n",
|
||||
"ThreadID", "ThreadType", "cfName", "Operation",
|
||||
"ElapsedTime", "Stage", "State");
|
||||
"ElapsedTime", "Stage", "State", "OperationProperties");
|
||||
|
||||
int64_t current_time = 0;
|
||||
Env::Default()->GetCurrentTime(¤t_time);
|
||||
for (auto ts : thread_list) {
|
||||
fprintf(stderr, "%18" PRIu64 " %10s %25s %12s %12s %45s %12s\n",
|
||||
fprintf(stderr, "%18" PRIu64 " %10s %12s %20s %13s %45s %12s",
|
||||
ts.thread_id,
|
||||
ThreadStatus::GetThreadTypeName(ts.thread_type).c_str(),
|
||||
ts.cf_name.c_str(),
|
||||
@@ -952,6 +976,14 @@ class Stats {
|
||||
ThreadStatus::MicrosToString(ts.op_elapsed_micros).c_str(),
|
||||
ThreadStatus::GetOperationStageName(ts.operation_stage).c_str(),
|
||||
ThreadStatus::GetStateName(ts.state_type).c_str());
|
||||
|
||||
auto op_properties = ThreadStatus::InterpretOperationProperties(
|
||||
ts.operation_type, ts.op_properties);
|
||||
for (const auto& op_prop : op_properties) {
|
||||
fprintf(stderr, " %s %" PRIu64" |",
|
||||
op_prop.first.c_str(), op_prop.second);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -980,36 +1012,49 @@ class Stats {
|
||||
fprintf(stderr, "... finished %" PRIu64 " ops%30s\r", done_, "");
|
||||
} else {
|
||||
double now = FLAGS_env->NowMicros();
|
||||
fprintf(stderr,
|
||||
"%s ... thread %d: (%" PRIu64 ",%" PRIu64 ") ops and "
|
||||
"(%.1f,%.1f) ops/second in (%.6f,%.6f) seconds\n",
|
||||
FLAGS_env->TimeToString((uint64_t) now/1000000).c_str(),
|
||||
id_,
|
||||
done_ - last_report_done_, done_,
|
||||
(done_ - last_report_done_) /
|
||||
((now - last_report_finish_) / 1000000.0),
|
||||
done_ / ((now - start_) / 1000000.0),
|
||||
(now - last_report_finish_) / 1000000.0,
|
||||
(now - start_) / 1000000.0);
|
||||
int64_t usecs_since_last = now - last_report_finish_;
|
||||
|
||||
if (FLAGS_stats_per_interval) {
|
||||
std::string stats;
|
||||
// Determine whether to print status where interval is either
|
||||
// each N operations or each N seconds.
|
||||
|
||||
if (db_with_cfh && db_with_cfh->num_created.load()) {
|
||||
for (size_t i = 0; i < db_with_cfh->num_created.load(); ++i) {
|
||||
if (db->GetProperty(db_with_cfh->cfh[i], "rocksdb.cfstats",
|
||||
&stats))
|
||||
fprintf(stderr, "%s\n", stats.c_str());
|
||||
if (FLAGS_stats_interval_seconds &&
|
||||
usecs_since_last < (FLAGS_stats_interval_seconds * 1000000)) {
|
||||
// Don't check again for this many operations
|
||||
next_report_ += FLAGS_stats_interval;
|
||||
|
||||
} else {
|
||||
|
||||
fprintf(stderr,
|
||||
"%s ... thread %d: (%" PRIu64 ",%" PRIu64 ") ops and "
|
||||
"(%.1f,%.1f) ops/second in (%.6f,%.6f) seconds\n",
|
||||
FLAGS_env->TimeToString((uint64_t) now/1000000).c_str(),
|
||||
id_,
|
||||
done_ - last_report_done_, done_,
|
||||
(done_ - last_report_done_) /
|
||||
(usecs_since_last / 1000000.0),
|
||||
done_ / ((now - start_) / 1000000.0),
|
||||
(now - last_report_finish_) / 1000000.0,
|
||||
(now - start_) / 1000000.0);
|
||||
|
||||
if (FLAGS_stats_per_interval) {
|
||||
std::string stats;
|
||||
|
||||
if (db_with_cfh && db_with_cfh->num_created.load()) {
|
||||
for (size_t i = 0; i < db_with_cfh->num_created.load(); ++i) {
|
||||
if (db->GetProperty(db_with_cfh->cfh[i], "rocksdb.cfstats",
|
||||
&stats))
|
||||
fprintf(stderr, "%s\n", stats.c_str());
|
||||
}
|
||||
|
||||
} else if (db && db->GetProperty("rocksdb.stats", &stats)) {
|
||||
fprintf(stderr, "%s\n", stats.c_str());
|
||||
}
|
||||
|
||||
} else if (db && db->GetProperty("rocksdb.stats", &stats)) {
|
||||
fprintf(stderr, "%s\n", stats.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
next_report_ += FLAGS_stats_interval;
|
||||
last_report_finish_ = now;
|
||||
last_report_done_ = done_;
|
||||
next_report_ += FLAGS_stats_interval;
|
||||
last_report_finish_ = now;
|
||||
last_report_done_ = done_;
|
||||
}
|
||||
}
|
||||
if (id_ == 0 && FLAGS_thread_status_per_interval) {
|
||||
PrintThreadStatus();
|
||||
@@ -1161,6 +1206,7 @@ class Benchmark {
|
||||
int64_t readwrites_;
|
||||
int64_t merge_keys_;
|
||||
bool report_file_operations_;
|
||||
int cachedev_fd_;
|
||||
|
||||
bool SanityCheck() {
|
||||
if (FLAGS_compression_ratio > 1) {
|
||||
@@ -1387,7 +1433,8 @@ class Benchmark {
|
||||
? FLAGS_num
|
||||
: ((FLAGS_writes > FLAGS_reads) ? FLAGS_writes : FLAGS_reads)),
|
||||
merge_keys_(FLAGS_merge_keys < 0 ? FLAGS_num : FLAGS_merge_keys),
|
||||
report_file_operations_(FLAGS_report_file_operations) {
|
||||
report_file_operations_(FLAGS_report_file_operations),
|
||||
cachedev_fd_(-1) {
|
||||
if (report_file_operations_) {
|
||||
if (!FLAGS_hdfs.empty()) {
|
||||
fprintf(stderr,
|
||||
@@ -1428,10 +1475,17 @@ class Benchmark {
|
||||
// this will leak, but we're shutting down so nobody cares
|
||||
cache_->DisownData();
|
||||
}
|
||||
if (FLAGS_disable_flashcache_for_background_threads && cachedev_fd_ != -1) {
|
||||
// Dtor for this env should run before cachedev_fd_ is closed
|
||||
flashcache_aware_env_ = nullptr;
|
||||
close(cachedev_fd_);
|
||||
}
|
||||
}
|
||||
|
||||
Slice AllocateKey(std::unique_ptr<const char[]>* key_guard) {
|
||||
key_guard->reset(new char[key_size_]);
|
||||
char* data = new char[key_size_];
|
||||
const char* const_data = data;
|
||||
key_guard->reset(const_data);
|
||||
return Slice(key_guard->get(), key_size_);
|
||||
}
|
||||
|
||||
@@ -1582,6 +1636,9 @@ class Benchmark {
|
||||
} else if (name == Slice("seekrandomwhilewriting")) {
|
||||
num_threads++; // Add extra thread for writing
|
||||
method = &Benchmark::SeekRandomWhileWriting;
|
||||
} else if (name == Slice("seekrandomwhilemerging")) {
|
||||
num_threads++; // Add extra thread for merging
|
||||
method = &Benchmark::SeekRandomWhileMerging;
|
||||
} else if (name == Slice("readrandomsmall")) {
|
||||
reads_ /= 1000;
|
||||
method = &Benchmark::ReadRandom;
|
||||
@@ -1678,6 +1735,8 @@ class Benchmark {
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Env> flashcache_aware_env_;
|
||||
|
||||
struct ThreadArg {
|
||||
Benchmark* bm;
|
||||
SharedState* shared;
|
||||
@@ -1990,7 +2049,25 @@ class Benchmark {
|
||||
FLAGS_env->LowerThreadPoolIOPriority(Env::LOW);
|
||||
FLAGS_env->LowerThreadPoolIOPriority(Env::HIGH);
|
||||
}
|
||||
options.env = FLAGS_env;
|
||||
if (FLAGS_disable_flashcache_for_background_threads &&
|
||||
cachedev_fd_ == -1) {
|
||||
// Avoid creating the env twice when an use_existing_db is true
|
||||
cachedev_fd_ = open(FLAGS_flashcache_dev.c_str(), O_RDONLY);
|
||||
if (cachedev_fd_ < 0) {
|
||||
fprintf(stderr, "Open flash device failed\n");
|
||||
exit(1);
|
||||
}
|
||||
flashcache_aware_env_ =
|
||||
std::move(NewFlashcacheAwareEnv(FLAGS_env, cachedev_fd_));
|
||||
if (flashcache_aware_env_.get() == nullptr) {
|
||||
fprintf(stderr, "Failed to open flashcahce device at %s\n",
|
||||
FLAGS_flashcache_dev.c_str());
|
||||
std::abort();
|
||||
}
|
||||
options.env = flashcache_aware_env_.get();
|
||||
} else {
|
||||
options.env = FLAGS_env;
|
||||
}
|
||||
options.disableDataSync = FLAGS_disable_data_sync;
|
||||
options.use_fsync = FLAGS_use_fsync;
|
||||
options.wal_dir = FLAGS_wal_dir;
|
||||
@@ -2153,6 +2230,7 @@ class Benchmark {
|
||||
options.access_hint_on_compaction_start = FLAGS_compaction_fadvice_e;
|
||||
options.use_adaptive_mutex = FLAGS_use_adaptive_mutex;
|
||||
options.bytes_per_sync = FLAGS_bytes_per_sync;
|
||||
options.wal_bytes_per_sync = FLAGS_wal_bytes_per_sync;
|
||||
|
||||
// merge operator options
|
||||
options.merge_operator = MergeOperators::CreateFromStringId(
|
||||
@@ -2188,6 +2266,10 @@ class Benchmark {
|
||||
if (FLAGS_thread_status_per_interval > 0) {
|
||||
options.enable_thread_tracking = true;
|
||||
}
|
||||
if (FLAGS_rate_limiter_bytes_per_sec > 0) {
|
||||
options.rate_limiter.reset(
|
||||
NewGenericRateLimiter(FLAGS_rate_limiter_bytes_per_sec));
|
||||
}
|
||||
|
||||
if (FLAGS_num_multi_db <= 1) {
|
||||
OpenDb(options, FLAGS_db, &db_);
|
||||
@@ -2514,6 +2596,7 @@ class Benchmark {
|
||||
void ReadRandom(ThreadState* thread) {
|
||||
int64_t read = 0;
|
||||
int64_t found = 0;
|
||||
int64_t bytes = 0;
|
||||
ReadOptions options(FLAGS_verify_checksum, true);
|
||||
std::unique_ptr<const char[]> key_guard;
|
||||
Slice key = AllocateKey(&key_guard);
|
||||
@@ -2537,6 +2620,7 @@ class Benchmark {
|
||||
}
|
||||
if (s.ok()) {
|
||||
found++;
|
||||
bytes += key.size() + value.size();
|
||||
} else if (!s.IsNotFound()) {
|
||||
fprintf(stderr, "Get returned an error: %s\n", s.ToString().c_str());
|
||||
abort();
|
||||
@@ -2548,6 +2632,7 @@ class Benchmark {
|
||||
snprintf(msg, sizeof(msg), "(%" PRIu64 " of %" PRIu64 " found)\n",
|
||||
found, read);
|
||||
|
||||
thread->stats.AddBytes(bytes);
|
||||
thread->stats.AddMessage(msg);
|
||||
|
||||
if (FLAGS_perf_level > 0) {
|
||||
@@ -2619,6 +2704,7 @@ class Benchmark {
|
||||
void SeekRandom(ThreadState* thread) {
|
||||
int64_t read = 0;
|
||||
int64_t found = 0;
|
||||
int64_t bytes = 0;
|
||||
ReadOptions options(FLAGS_verify_checksum, true);
|
||||
options.tailing = FLAGS_use_tailing_iterator;
|
||||
|
||||
@@ -2631,7 +2717,6 @@ class Benchmark {
|
||||
multi_iters.push_back(db_with_cfh.db->NewIterator(options));
|
||||
}
|
||||
}
|
||||
uint64_t last_refresh = FLAGS_env->NowMicros();
|
||||
|
||||
std::unique_ptr<const char[]> key_guard;
|
||||
Slice key = AllocateKey(&key_guard);
|
||||
@@ -2639,23 +2724,19 @@ class Benchmark {
|
||||
Duration duration(FLAGS_duration, reads_);
|
||||
char value_buffer[256];
|
||||
while (!duration.Done(1)) {
|
||||
if (!FLAGS_use_tailing_iterator && FLAGS_iter_refresh_interval_us >= 0) {
|
||||
uint64_t now = FLAGS_env->NowMicros();
|
||||
if (now - last_refresh > (uint64_t)FLAGS_iter_refresh_interval_us) {
|
||||
if (db_.db != nullptr) {
|
||||
delete single_iter;
|
||||
single_iter = db_.db->NewIterator(options);
|
||||
} else {
|
||||
for (auto iter : multi_iters) {
|
||||
delete iter;
|
||||
}
|
||||
multi_iters.clear();
|
||||
for (const auto& db_with_cfh : multi_dbs_) {
|
||||
multi_iters.push_back(db_with_cfh.db->NewIterator(options));
|
||||
}
|
||||
if (!FLAGS_use_tailing_iterator) {
|
||||
if (db_.db != nullptr) {
|
||||
delete single_iter;
|
||||
single_iter = db_.db->NewIterator(options);
|
||||
} else {
|
||||
for (auto iter : multi_iters) {
|
||||
delete iter;
|
||||
}
|
||||
multi_iters.clear();
|
||||
for (const auto& db_with_cfh : multi_dbs_) {
|
||||
multi_iters.push_back(db_with_cfh.db->NewIterator(options));
|
||||
}
|
||||
}
|
||||
last_refresh = now;
|
||||
}
|
||||
// Pick a Iterator to use
|
||||
Iterator* iter_to_use = single_iter;
|
||||
@@ -2675,6 +2756,7 @@ class Benchmark {
|
||||
Slice value = iter_to_use->value();
|
||||
memcpy(value_buffer, value.data(),
|
||||
std::min(value.size(), sizeof(value_buffer)));
|
||||
bytes += iter_to_use->key().size() + iter_to_use->value().size();
|
||||
|
||||
if (!FLAGS_reverse_iterator) {
|
||||
iter_to_use->Next();
|
||||
@@ -2694,6 +2776,7 @@ class Benchmark {
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg), "(%" PRIu64 " of %" PRIu64 " found)\n",
|
||||
found, read);
|
||||
thread->stats.AddBytes(bytes);
|
||||
thread->stats.AddMessage(msg);
|
||||
if (FLAGS_perf_level > 0) {
|
||||
thread->stats.AddMessage(perf_context.ToString());
|
||||
@@ -2708,6 +2791,14 @@ class Benchmark {
|
||||
}
|
||||
}
|
||||
|
||||
void SeekRandomWhileMerging(ThreadState* thread) {
|
||||
if (thread->tid > 0) {
|
||||
SeekRandom(thread);
|
||||
} else {
|
||||
BGWriter(thread, kMerge);
|
||||
}
|
||||
}
|
||||
|
||||
void DoDelete(ThreadState* thread, bool seq) {
|
||||
WriteBatch batch;
|
||||
Duration duration(seq ? 0 : FLAGS_duration, num_);
|
||||
@@ -2763,6 +2854,7 @@ class Benchmark {
|
||||
double last = FLAGS_env->NowMicros();
|
||||
int writes_per_second_by_10 = 0;
|
||||
int num_writes = 0;
|
||||
int64_t bytes = 0;
|
||||
|
||||
// --writes_per_second rate limit is enforced per 100 milliseconds
|
||||
// intervals to avoid a burst of writes at the start of each second.
|
||||
@@ -2799,6 +2891,7 @@ class Benchmark {
|
||||
fprintf(stderr, "put or merge error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
bytes += key.size() + value_size_;
|
||||
thread->stats.FinishedOps(&db_, db_.db, 1);
|
||||
|
||||
++num_writes;
|
||||
@@ -2815,6 +2908,7 @@ class Benchmark {
|
||||
}
|
||||
}
|
||||
}
|
||||
thread->stats.AddBytes(bytes);
|
||||
}
|
||||
|
||||
// Given a key K and value V, this puts (K+"0", V), (K+"1", V), (K+"2", V)
|
||||
@@ -3033,6 +3127,7 @@ class Benchmark {
|
||||
RandomGenerator gen;
|
||||
std::string value;
|
||||
int64_t found = 0;
|
||||
int64_t bytes = 0;
|
||||
Duration duration(FLAGS_duration, readwrites_);
|
||||
|
||||
std::unique_ptr<const char[]> key_guard;
|
||||
@@ -3045,6 +3140,7 @@ class Benchmark {
|
||||
auto status = db->Get(options, key, &value);
|
||||
if (status.ok()) {
|
||||
++found;
|
||||
bytes += key.size() + value.size();
|
||||
} else if (!status.IsNotFound()) {
|
||||
fprintf(stderr, "Get returned an error: %s\n",
|
||||
status.ToString().c_str());
|
||||
@@ -3056,11 +3152,13 @@ class Benchmark {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
bytes += key.size() + value_size_;
|
||||
thread->stats.FinishedOps(nullptr, db, 1);
|
||||
}
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg),
|
||||
"( updates:%" PRIu64 " found:%" PRIu64 ")", readwrites_, found);
|
||||
thread->stats.AddBytes(bytes);
|
||||
thread->stats.AddMessage(msg);
|
||||
}
|
||||
|
||||
@@ -3072,6 +3170,7 @@ class Benchmark {
|
||||
RandomGenerator gen;
|
||||
std::string value;
|
||||
int64_t found = 0;
|
||||
int64_t bytes = 0;
|
||||
|
||||
std::unique_ptr<const char[]> key_guard;
|
||||
Slice key = AllocateKey(&key_guard);
|
||||
@@ -3084,6 +3183,7 @@ class Benchmark {
|
||||
auto status = db->Get(options, key, &value);
|
||||
if (status.ok()) {
|
||||
++found;
|
||||
bytes += key.size() + value.size();
|
||||
} else if (!status.IsNotFound()) {
|
||||
fprintf(stderr, "Get returned an error: %s\n",
|
||||
status.ToString().c_str());
|
||||
@@ -3107,12 +3207,14 @@ class Benchmark {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
bytes += key.size() + value.size();
|
||||
thread->stats.FinishedOps(nullptr, db, 1);
|
||||
}
|
||||
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg), "( updates:%" PRIu64 " found:%" PRIu64 ")",
|
||||
readwrites_, found);
|
||||
thread->stats.AddBytes(bytes);
|
||||
thread->stats.AddMessage(msg);
|
||||
}
|
||||
|
||||
@@ -3128,7 +3230,7 @@ class Benchmark {
|
||||
// FLAGS_merge_keys.
|
||||
void MergeRandom(ThreadState* thread) {
|
||||
RandomGenerator gen;
|
||||
|
||||
int64_t bytes = 0;
|
||||
std::unique_ptr<const char[]> key_guard;
|
||||
Slice key = AllocateKey(&key_guard);
|
||||
// The number of iterations is the larger of read_ or write_
|
||||
@@ -3143,12 +3245,14 @@ class Benchmark {
|
||||
fprintf(stderr, "merge error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
bytes += key.size() + value_size_;
|
||||
thread->stats.FinishedOps(nullptr, db, 1);
|
||||
}
|
||||
|
||||
// Print some statistics
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg), "( updates:%" PRIu64 ")", readwrites_);
|
||||
thread->stats.AddBytes(bytes);
|
||||
thread->stats.AddMessage(msg);
|
||||
}
|
||||
|
||||
@@ -3332,6 +3436,12 @@ int main(int argc, char** argv) {
|
||||
FLAGS_db = default_db_path;
|
||||
}
|
||||
|
||||
if (FLAGS_stats_interval_seconds > 0) {
|
||||
// When both are set then FLAGS_stats_interval determines the frequency
|
||||
// at which the timer is checked for FLAGS_stats_interval_seconds
|
||||
FLAGS_stats_interval = 1000;
|
||||
}
|
||||
|
||||
rocksdb::Benchmark benchmark;
|
||||
benchmark.Run();
|
||||
return 0;
|
||||
|
||||
+207
-90
@@ -31,6 +31,7 @@
|
||||
#include "db/compaction_job.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_logger_helpers.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_reader.h"
|
||||
@@ -67,6 +68,7 @@
|
||||
#include "util/autovector.h"
|
||||
#include "util/build_version.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/db_info_dumper.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/hash_skiplist_rep.h"
|
||||
@@ -91,16 +93,12 @@ void DumpRocksDBBuildVersion(Logger * log);
|
||||
|
||||
struct DBImpl::WriteContext {
|
||||
autovector<SuperVersion*> superversions_to_free_;
|
||||
autovector<log::Writer*> logs_to_free_;
|
||||
bool schedule_bg_work_ = false;
|
||||
|
||||
~WriteContext() {
|
||||
for (auto& sv : superversions_to_free_) {
|
||||
delete sv;
|
||||
}
|
||||
for (auto& log : logs_to_free_) {
|
||||
delete log;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -191,6 +189,18 @@ CompressionType GetCompressionFlush(const ImmutableCFOptions& ioptions) {
|
||||
return kNoCompression;
|
||||
}
|
||||
}
|
||||
|
||||
void DumpCompressionInfo(Logger* logger) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, logger, "Compression algorithms supported:");
|
||||
Log(InfoLogLevel::INFO_LEVEL, logger, "\tSnappy supported: %d",
|
||||
Snappy_Supported());
|
||||
Log(InfoLogLevel::INFO_LEVEL, logger, "\tZlib supported: %d",
|
||||
Zlib_Supported());
|
||||
Log(InfoLogLevel::INFO_LEVEL, logger, "\tBzip supported: %d",
|
||||
BZip2_Supported());
|
||||
Log(InfoLogLevel::INFO_LEVEL, logger, "\tLZ4 supported: %d", LZ4_Supported());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
@@ -223,7 +233,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
last_stats_dump_time_microsec_(0),
|
||||
next_job_id_(1),
|
||||
flush_on_destroy_(false),
|
||||
env_options_(options),
|
||||
env_options_(db_options_),
|
||||
#ifndef ROCKSDB_LITE
|
||||
wal_manager_(db_options_, env_options_),
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -251,12 +261,23 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
DumpRocksDBBuildVersion(db_options_.info_log.get());
|
||||
DumpDBFileSummary(db_options_, dbname_);
|
||||
db_options_.Dump(db_options_.info_log.get());
|
||||
DumpCompressionInfo(db_options_.info_log.get());
|
||||
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
|
||||
void DBImpl::CancelAllBackgroundWork() {
|
||||
// Will only lock the mutex_ and wait for completion if wait is true
|
||||
void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
shutting_down_.store(true, std::memory_order_release);
|
||||
if (!wait) {
|
||||
return;
|
||||
}
|
||||
// Wait for background work to finish
|
||||
mutex_.Lock();
|
||||
while (bg_compaction_scheduled_ || bg_flush_scheduled_ || notifying_events_) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
DBImpl::~DBImpl() {
|
||||
@@ -275,7 +296,11 @@ DBImpl::~DBImpl() {
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
}
|
||||
CancelAllBackgroundWork();
|
||||
// CancelAllBackgroundWork called with false means we just set the
|
||||
// shutdown marker, while holding the mutex_ here. After which we
|
||||
// do a variant of the waiting after we release the lock and unschedule work
|
||||
// (to consider: moving all the waiting into CancelAllBackgroundWork(true))
|
||||
CancelAllBackgroundWork(false);
|
||||
mutex_.Unlock();
|
||||
int compactions_unscheduled = env_->UnSchedule(this, Env::Priority::LOW);
|
||||
int flushes_unscheduled = env_->UnSchedule(this, Env::Priority::HIGH);
|
||||
@@ -330,6 +355,10 @@ DBImpl::~DBImpl() {
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
for (auto l : logs_to_free_) {
|
||||
delete l;
|
||||
}
|
||||
|
||||
// versions need to be destroyed before table_cache since it can hold
|
||||
// references to table_cache.
|
||||
versions_.reset();
|
||||
@@ -396,7 +425,7 @@ const Status DBImpl::CreateArchivalDirectory() {
|
||||
void DBImpl::PrintStatistics() {
|
||||
auto dbstats = db_options_.statistics.get();
|
||||
if (dbstats) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"STATISTICS:\n %s",
|
||||
dbstats->ToString().c_str());
|
||||
}
|
||||
@@ -416,26 +445,30 @@ void DBImpl::MaybeDumpStats() {
|
||||
// period in rare cases.
|
||||
last_stats_dump_time_microsec_ = now_micros;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
bool tmp1 = false;
|
||||
bool tmp2 = false;
|
||||
DBPropertyType cf_property_type =
|
||||
GetPropertyType("rocksdb.cfstats", &tmp1, &tmp2);
|
||||
GetPropertyType(DB::Properties::kCFStats, &tmp1, &tmp2);
|
||||
DBPropertyType db_property_type =
|
||||
GetPropertyType("rocksdb.dbstats", &tmp1, &tmp2);
|
||||
GetPropertyType(DB::Properties::kDBStats, &tmp1, &tmp2);
|
||||
std::string stats;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
cfd->internal_stats()->GetStringProperty(cf_property_type,
|
||||
"rocksdb.cfstats", &stats);
|
||||
DB::Properties::kCFStats,
|
||||
&stats);
|
||||
}
|
||||
default_cf_internal_stats_->GetStringProperty(db_property_type,
|
||||
"rocksdb.dbstats", &stats);
|
||||
DB::Properties::kDBStats,
|
||||
&stats);
|
||||
}
|
||||
Log(InfoLogLevel::INFO_LEVEL,
|
||||
Log(InfoLogLevel::WARN_LEVEL,
|
||||
db_options_.info_log, "------- DUMPING STATS -------");
|
||||
Log(InfoLogLevel::INFO_LEVEL,
|
||||
Log(InfoLogLevel::WARN_LEVEL,
|
||||
db_options_.info_log, "%s", stats.c_str());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
PrintStatistics();
|
||||
}
|
||||
@@ -653,7 +686,7 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state) {
|
||||
// evict from cache
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
fname = TableFileName(db_options_.db_paths, number, path_id);
|
||||
event_logger_.Log() << "event"
|
||||
event_logger_.Log() << "job" << state.job_id << "event"
|
||||
<< "table_file_deletion"
|
||||
<< "file_number" << number;
|
||||
} else {
|
||||
@@ -919,6 +952,18 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
version_edits.insert({cfd->GetID(), edit});
|
||||
}
|
||||
int job_id = next_job_id_.fetch_add(1);
|
||||
{
|
||||
auto stream = event_logger_.Log();
|
||||
stream << "job" << job_id << "event"
|
||||
<< "recovery_started";
|
||||
stream << "log_files";
|
||||
stream.StartArray();
|
||||
for (auto log_number : log_numbers) {
|
||||
stream << log_number;
|
||||
}
|
||||
stream.EndArray();
|
||||
}
|
||||
|
||||
for (auto log_number : log_numbers) {
|
||||
// The previous incarnation may not have written any MANIFEST
|
||||
@@ -998,7 +1043,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
auto iter = version_edits.find(cfd->GetID());
|
||||
assert(iter != version_edits.end());
|
||||
VersionEdit* edit = &iter->second;
|
||||
status = WriteLevel0TableForRecovery(cfd, cfd->mem(), edit);
|
||||
status = WriteLevel0TableForRecovery(job_id, cfd, cfd->mem(), edit);
|
||||
if (!status.ok()) {
|
||||
// Reflect errors immediately so that conditions like full
|
||||
// file-systems cause the DB::Open() to fail.
|
||||
@@ -1040,7 +1085,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
|
||||
// flush the final memtable (if non-empty)
|
||||
if (cfd->mem()->GetFirstSequenceNumber() != 0) {
|
||||
status = WriteLevel0TableForRecovery(cfd, cfd->mem(), edit);
|
||||
status = WriteLevel0TableForRecovery(job_id, cfd, cfd->mem(), edit);
|
||||
if (!status.ok()) {
|
||||
// Recovery failed
|
||||
break;
|
||||
@@ -1069,11 +1114,14 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
}
|
||||
}
|
||||
|
||||
event_logger_.Log() << "job" << job_id << "event"
|
||||
<< "recovery_finished";
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteLevel0TableForRecovery(ColumnFamilyData* cfd, MemTable* mem,
|
||||
VersionEdit* edit) {
|
||||
Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
MemTable* mem, VersionEdit* edit) {
|
||||
mutex_.AssertHeld();
|
||||
const uint64_t start_micros = env_->NowMicros();
|
||||
FileMetaData meta;
|
||||
@@ -1084,6 +1132,7 @@ Status DBImpl::WriteLevel0TableForRecovery(ColumnFamilyData* cfd, MemTable* mem,
|
||||
ro.total_order_seek = true;
|
||||
Arena arena;
|
||||
Status s;
|
||||
TableProperties table_properties;
|
||||
{
|
||||
ScopedArenaIterator iter(mem->NewIterator(ro, &arena));
|
||||
const SequenceNumber newest_snapshot = snapshots_.GetNewest();
|
||||
@@ -1094,23 +1143,34 @@ Status DBImpl::WriteLevel0TableForRecovery(ColumnFamilyData* cfd, MemTable* mem,
|
||||
" Level-0 table #%" PRIu64 ": started",
|
||||
cfd->GetName().c_str(), meta.fd.GetNumber());
|
||||
|
||||
bool paranoid_file_checks =
|
||||
cfd->GetLatestMutableCFOptions()->paranoid_file_checks;
|
||||
{
|
||||
mutex_.Unlock();
|
||||
s = BuildTable(
|
||||
dbname_, env_, *cfd->ioptions(), env_options_, cfd->table_cache(),
|
||||
iter.get(), &meta, cfd->internal_comparator(), newest_snapshot,
|
||||
iter.get(), &meta, cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), newest_snapshot,
|
||||
earliest_seqno_in_memtable, GetCompressionFlush(*cfd->ioptions()),
|
||||
cfd->ioptions()->compression_opts, Env::IO_HIGH);
|
||||
cfd->ioptions()->compression_opts, paranoid_file_checks, Env::IO_HIGH,
|
||||
&table_properties);
|
||||
LogFlush(db_options_.info_log);
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
Log(InfoLogLevel::DEBUG_LEVEL, db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
" Level-0 table #%" PRIu64 ": %" PRIu64 " bytes %s",
|
||||
cfd->GetName().c_str(), meta.fd.GetNumber(), meta.fd.GetFileSize(),
|
||||
s.ToString().c_str());
|
||||
}
|
||||
Log(InfoLogLevel::DEBUG_LEVEL, db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
" Level-0 table #%" PRIu64 ": %" PRIu64 " bytes %s",
|
||||
cfd->GetName().c_str(), meta.fd.GetNumber(), meta.fd.GetFileSize(),
|
||||
s.ToString().c_str());
|
||||
|
||||
// output to event logger
|
||||
if (s.ok()) {
|
||||
EventLoggerHelpers::LogTableFileCreation(
|
||||
&event_logger_, job_id, meta.fd.GetNumber(), meta.fd.GetFileSize(),
|
||||
table_properties);
|
||||
}
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
// Note that if file_size is zero, the file has been deleted and
|
||||
@@ -1241,30 +1301,51 @@ Status DBImpl::CompactRange(ColumnFamilyHandle* column_family,
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
Version* base = cfd->current();
|
||||
for (int level = 1; level < cfd->NumberLevels(); level++) {
|
||||
for (int level = 1; level < base->storage_info()->num_non_empty_levels();
|
||||
level++) {
|
||||
if (base->storage_info()->OverlapInLevel(level, begin, end)) {
|
||||
max_level_with_files = level;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int level = 0; level <= max_level_with_files; level++) {
|
||||
// in case the compaction is unversal or if we're compacting the
|
||||
// bottom-most level, the output level will be the same as input one.
|
||||
// level 0 can never be the bottommost level (i.e. if all files are in level
|
||||
// 0, we will compact to level 1)
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO ||
|
||||
(level == max_level_with_files && level > 0)) {
|
||||
s = RunManualCompaction(cfd, level, level, target_path_id, begin, end);
|
||||
} else {
|
||||
s = RunManualCompaction(cfd, level, level + 1, target_path_id, begin,
|
||||
end);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal &&
|
||||
cfd->NumberLevels() > 1) {
|
||||
// Always compact all files together.
|
||||
s = RunManualCompaction(cfd, ColumnFamilyData::kCompactAllLevels,
|
||||
cfd->NumberLevels() - 1, target_path_id, begin,
|
||||
end);
|
||||
} else {
|
||||
for (int level = 0; level <= max_level_with_files; level++) {
|
||||
// in case the compaction is unversal or if we're compacting the
|
||||
// bottom-most level, the output level will be the same as input one.
|
||||
// level 0 can never be the bottommost level (i.e. if all files are in
|
||||
// level 0, we will compact to level 1)
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO ||
|
||||
(level == max_level_with_files && level > 0)) {
|
||||
s = RunManualCompaction(cfd, level, level, target_path_id, begin, end);
|
||||
} else {
|
||||
int output_level = level + 1;
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleLevel &&
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
level == 0) {
|
||||
output_level = ColumnFamilyData::kCompactToBaseLevel;
|
||||
}
|
||||
s = RunManualCompaction(cfd, level, output_level, target_path_id, begin,
|
||||
end);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::1");
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::2");
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
|
||||
if (reduce_level) {
|
||||
s = ReFitLevel(cfd, max_level_with_files, target_level);
|
||||
@@ -1379,7 +1460,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
return s;
|
||||
}
|
||||
|
||||
autovector<CompactionInputFiles> input_files;
|
||||
std::vector<CompactionInputFiles> input_files;
|
||||
s = cfd->compaction_picker()->GetCompactionInputsFromFileNumbers(
|
||||
&input_files, &input_set, version->storage_info(), compact_options);
|
||||
if (!s.ok()) {
|
||||
@@ -1400,12 +1481,10 @@ Status DBImpl::CompactFilesImpl(
|
||||
unique_ptr<Compaction> c;
|
||||
assert(cfd->compaction_picker());
|
||||
c.reset(cfd->compaction_picker()->FormCompaction(
|
||||
compact_options, input_files,
|
||||
output_level, version->storage_info(),
|
||||
*cfd->GetLatestMutableCFOptions()));
|
||||
compact_options, input_files, output_level, version->storage_info(),
|
||||
*cfd->GetLatestMutableCFOptions(), output_path_id));
|
||||
assert(c);
|
||||
c->SetInputVersion(version);
|
||||
c->SetOutputPathId(static_cast<uint32_t>(output_path_id));
|
||||
// deletion compaction currently not allowed in CompactFiles.
|
||||
assert(!c->IsDeletionCompaction());
|
||||
|
||||
@@ -1414,18 +1493,19 @@ Status DBImpl::CompactFilesImpl(
|
||||
c->column_family_data(), *c->mutable_cf_options(),
|
||||
job_context, log_buffer);
|
||||
};
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), db_options_, *c->mutable_cf_options(),
|
||||
env_options_, versions_.get(), &shutting_down_, log_buffer,
|
||||
directories_.GetDbDir(), directories_.GetDataDir(c->GetOutputPathId()),
|
||||
stats_, &snapshots_, is_snapshot_supported_, table_cache_,
|
||||
std::move(yield_callback));
|
||||
job_context->job_id, c.get(), db_options_, env_options_, versions_.get(),
|
||||
&shutting_down_, log_buffer, directories_.GetDbDir(),
|
||||
directories_.GetDataDir(c->GetOutputPathId()), stats_,
|
||||
snapshots_.GetAll(), table_cache_, std::move(yield_callback),
|
||||
&event_logger_, c->mutable_cf_options()->paranoid_file_checks);
|
||||
compaction_job.Prepare();
|
||||
|
||||
mutex_.Unlock();
|
||||
Status status = compaction_job.Run();
|
||||
mutex_.Lock();
|
||||
compaction_job.Install(&status, &mutex_);
|
||||
compaction_job.Install(&status, *c->mutable_cf_options(), &mutex_);
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionBackground(c->column_family_data(), job_context,
|
||||
*c->mutable_cf_options());
|
||||
@@ -1650,7 +1730,8 @@ SequenceNumber DBImpl::GetLatestSequenceNumber() const {
|
||||
Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
int output_level, uint32_t output_path_id,
|
||||
const Slice* begin, const Slice* end) {
|
||||
assert(input_level >= 0);
|
||||
assert(input_level == ColumnFamilyData::kCompactAllLevels ||
|
||||
input_level >= 0);
|
||||
|
||||
InternalKey begin_storage, end_storage;
|
||||
|
||||
@@ -1668,7 +1749,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
manual.begin = nullptr;
|
||||
} else {
|
||||
begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
begin_storage.SetMaxPossibleForUserKey(*begin);
|
||||
manual.begin = &begin_storage;
|
||||
}
|
||||
if (end == nullptr ||
|
||||
@@ -1676,7 +1757,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
manual.end = nullptr;
|
||||
} else {
|
||||
end_storage = InternalKey(*end, 0, static_cast<ValueType>(0));
|
||||
end_storage.SetMinPossibleForUserKey(*end);
|
||||
manual.end = &end_storage;
|
||||
}
|
||||
|
||||
@@ -1786,9 +1867,6 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
} else if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
// DB is being deleted; no more background compactions
|
||||
return;
|
||||
} else if (bg_manual_only_) {
|
||||
// manual only
|
||||
return;
|
||||
}
|
||||
|
||||
while (unscheduled_flushes_ > 0 &&
|
||||
@@ -1798,6 +1876,12 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
env_->Schedule(&DBImpl::BGWorkFlush, this, Env::Priority::HIGH, this);
|
||||
}
|
||||
|
||||
if (bg_manual_only_) {
|
||||
// only manual compactions are allowed to run. don't schedule automatic
|
||||
// compactions
|
||||
return;
|
||||
}
|
||||
|
||||
if (db_options_.max_background_flushes == 0 &&
|
||||
bg_compaction_scheduled_ < db_options_.max_background_compactions &&
|
||||
unscheduled_flushes_ > 0) {
|
||||
@@ -1967,6 +2051,10 @@ void DBImpl::BackgroundCallFlush() {
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
// We're just cleaning up for DB::Write()
|
||||
job_context.logs_to_free = logs_to_free_;
|
||||
logs_to_free_.clear();
|
||||
|
||||
// If flush failed, we want to delete all temporary files that we might have
|
||||
// created. Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
@@ -2033,6 +2121,10 @@ void DBImpl::BackgroundCallCompaction() {
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
// We're just cleaning up for DB::Write()
|
||||
job_context.logs_to_free = logs_to_free_;
|
||||
logs_to_free_.clear();
|
||||
|
||||
// If compaction failed, we want to delete all temporary files that we might
|
||||
// have created (they might not be all recorded in job_context in case of a
|
||||
// failure). Thus, we force full scan in FindObsoleteFiles()
|
||||
@@ -2148,16 +2240,23 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
m->output_path_id, m->begin, m->end, &manual_end));
|
||||
if (!c) {
|
||||
m->done = true;
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Manual compaction from level-%d from %s .. "
|
||||
"%s; nothing to do\n",
|
||||
m->cfd->GetName().c_str(), m->input_level,
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"));
|
||||
} else {
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Manual compaction from level-%d to level-%d from %s .. "
|
||||
"%s; will stop at %s\n",
|
||||
m->cfd->GetName().c_str(), m->input_level, c->output_level(),
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"),
|
||||
((m->done || manual_end == nullptr)
|
||||
? "(end)"
|
||||
: manual_end->DebugString().c_str()));
|
||||
}
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Manual compaction from level-%d to level-%d from %s .. "
|
||||
"%s; will stop at %s\n",
|
||||
m->cfd->GetName().c_str(), m->input_level, m->output_level,
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"),
|
||||
((m->done || manual_end == nullptr)
|
||||
? "(end)"
|
||||
: manual_end->DebugString().c_str()));
|
||||
} else if (!compaction_queue_.empty()) {
|
||||
// cfd is referenced here
|
||||
auto cfd = PopFirstFromCompactionQueue();
|
||||
@@ -2233,6 +2332,7 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
c->num_input_files(0));
|
||||
*madeProgress = true;
|
||||
} else if (!is_manual && c->IsTrivialMove()) {
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:TrivialMove");
|
||||
// Instrument for event update
|
||||
// TODO(yhchiang): add op details for showing trivial-move.
|
||||
ThreadStatusUtil::SetColumnFamily(c->column_family_data());
|
||||
@@ -2242,7 +2342,7 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
assert(c->num_input_files(0) == 1);
|
||||
FileMetaData* f = c->input(0, 0);
|
||||
c->edit()->DeleteFile(c->level(), f->fd.GetNumber());
|
||||
c->edit()->AddFile(c->level() + 1, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
c->edit()->AddFile(c->output_level(), f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->smallest_seqno, f->largest_seqno);
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
@@ -2255,6 +2355,13 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
c->column_family_data()->internal_stats()->IncBytesMoved(
|
||||
c->level() + 1, f->fd.GetFileSize());
|
||||
{
|
||||
event_logger_.LogToBuffer(log_buffer)
|
||||
<< "job" << job_context->job_id << "event"
|
||||
<< "trivial_move"
|
||||
<< "destination_level" << c->level() + 1 << "file_number"
|
||||
<< f->fd.GetNumber() << "file_size" << f->fd.GetFileSize();
|
||||
}
|
||||
LogToBuffer(
|
||||
log_buffer,
|
||||
"[%s] Moved #%" PRIu64 " to level-%d %" PRIu64 " bytes %s: %s\n",
|
||||
@@ -2266,22 +2373,24 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
// Clear Instrument
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:NonTrivial");
|
||||
auto yield_callback = [&]() {
|
||||
return CallFlushDuringCompaction(c->column_family_data(),
|
||||
*c->mutable_cf_options(), job_context,
|
||||
log_buffer);
|
||||
};
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), db_options_, *c->mutable_cf_options(),
|
||||
env_options_, versions_.get(), &shutting_down_, log_buffer,
|
||||
directories_.GetDbDir(), directories_.GetDataDir(c->GetOutputPathId()),
|
||||
stats_, &snapshots_, is_snapshot_supported_, table_cache_,
|
||||
std::move(yield_callback));
|
||||
job_context->job_id, c.get(), db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_, log_buffer, directories_.GetDbDir(),
|
||||
directories_.GetDataDir(c->GetOutputPathId()), stats_,
|
||||
snapshots_.GetAll(), table_cache_, std::move(yield_callback),
|
||||
&event_logger_, c->mutable_cf_options()->paranoid_file_checks);
|
||||
compaction_job.Prepare();
|
||||
mutex_.Unlock();
|
||||
status = compaction_job.Run();
|
||||
mutex_.Lock();
|
||||
compaction_job.Install(&status, &mutex_);
|
||||
compaction_job.Install(&status, *c->mutable_cf_options(), &mutex_);
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionBackground(c->column_family_data(), job_context,
|
||||
*c->mutable_cf_options());
|
||||
@@ -2335,7 +2444,9 @@ Status DBImpl::BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
// We only compacted part of the requested range. Update *m
|
||||
// to the range that is left to be compacted.
|
||||
// Universal and FIFO compactions should always compact the whole range
|
||||
assert(m->cfd->ioptions()->compaction_style != kCompactionStyleUniversal);
|
||||
assert(m->cfd->ioptions()->compaction_style !=
|
||||
kCompactionStyleUniversal ||
|
||||
m->cfd->ioptions()->num_levels > 1);
|
||||
assert(m->cfd->ioptions()->compaction_style != kCompactionStyleFIFO);
|
||||
m->tmp_storage = *manual_end;
|
||||
m->begin = &m->tmp_storage;
|
||||
@@ -3126,8 +3237,13 @@ Status DBImpl::Write(const WriteOptions& write_options, WriteBatch* my_batch) {
|
||||
status = ScheduleFlushes(&context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok()) &&
|
||||
(write_controller_.IsStopped() || write_controller_.GetDelay() > 0)) {
|
||||
if (UNLIKELY(status.ok() && (write_controller_.IsStopped() ||
|
||||
write_controller_.GetDelay() > 0))) {
|
||||
// If writer is stopped, we need to get it going,
|
||||
// so schedule flushes/compactions
|
||||
if (context.schedule_bg_work_) {
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
status = DelayWrite(expiration_time);
|
||||
}
|
||||
|
||||
@@ -3268,6 +3384,7 @@ Status DBImpl::DelayWrite(uint64_t expiration_time) {
|
||||
mutex_.Unlock();
|
||||
delayed = true;
|
||||
// hopefully we don't have to sleep more than 2 billion microseconds
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Sleep");
|
||||
env_->SleepForMicroseconds(static_cast<int>(delay));
|
||||
mutex_.Lock();
|
||||
}
|
||||
@@ -3275,6 +3392,7 @@ Status DBImpl::DelayWrite(uint64_t expiration_time) {
|
||||
while (bg_error_.ok() && write_controller_.IsStopped()) {
|
||||
delayed = true;
|
||||
if (has_timeout) {
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:TimedWait");
|
||||
bg_cv_.TimedWait(expiration_time);
|
||||
if (env_->NowMicros() > expiration_time) {
|
||||
timed_out = true;
|
||||
@@ -3337,7 +3455,7 @@ Status DBImpl::SetNewMemtableAndNewLogFile(ColumnFamilyData* cfd,
|
||||
if (creating_new_log) {
|
||||
s = env_->NewWritableFile(
|
||||
LogFileName(db_options_.wal_dir, new_log_number), &lfile,
|
||||
env_->OptimizeForLogWrite(env_options_));
|
||||
env_->OptimizeForLogWrite(env_options_, db_options_));
|
||||
if (s.ok()) {
|
||||
// Our final size should be less than write_buffer_size
|
||||
// (compression, etc) but err on the side of caution.
|
||||
@@ -3367,7 +3485,7 @@ Status DBImpl::SetNewMemtableAndNewLogFile(ColumnFamilyData* cfd,
|
||||
if (creating_new_log) {
|
||||
logfile_number_ = new_log_number;
|
||||
assert(new_log != nullptr);
|
||||
context->logs_to_free_.push_back(log_.release());
|
||||
logs_to_free_.push_back(log_.release());
|
||||
log_.reset(new_log);
|
||||
log_empty_ = true;
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
@@ -3427,6 +3545,8 @@ const Options& DBImpl::GetOptions(ColumnFamilyHandle* column_family) const {
|
||||
return *cfh->cfd()->options();
|
||||
}
|
||||
|
||||
const DBOptions& DBImpl::GetDBOptions() const { return db_options_; }
|
||||
|
||||
bool DBImpl::GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) {
|
||||
bool is_int_property = false;
|
||||
@@ -3511,7 +3631,6 @@ void DBImpl::ReturnAndCleanupSuperVersion(ColumnFamilyData* cfd,
|
||||
|
||||
void DBImpl::GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes) {
|
||||
// TODO(opt): better implementation
|
||||
Version* v;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
@@ -3525,9 +3644,7 @@ void DBImpl::GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
// Convert user_key into a corresponding internal key.
|
||||
InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
uint64_t start = versions_->ApproximateOffsetOf(v, k1);
|
||||
uint64_t limit = versions_->ApproximateOffsetOf(v, k2);
|
||||
sizes[i] = (limit >= start ? limit - start : 0);
|
||||
sizes[i] = versions_->ApproximateSize(v, k1.Encode(), k2.Encode());
|
||||
}
|
||||
|
||||
{
|
||||
@@ -3848,7 +3965,8 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
EnvOptions soptions(db_options);
|
||||
s = impl->db_options_.env->NewWritableFile(
|
||||
LogFileName(impl->db_options_.wal_dir, new_log_number), &lfile,
|
||||
impl->db_options_.env->OptimizeForLogWrite(soptions));
|
||||
impl->db_options_.env->OptimizeForLogWrite(soptions,
|
||||
impl->db_options_));
|
||||
if (s.ok()) {
|
||||
lfile->SetPreallocationBlockSize(1.1 * max_write_buffer_size);
|
||||
impl->logfile_number_ = new_log_number;
|
||||
@@ -3895,15 +4013,14 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
auto* vstorage = cfd->current()->storage_info();
|
||||
for (int i = 1; i < vstorage->num_levels(); ++i) {
|
||||
int num_files = vstorage->NumLevelFiles(i);
|
||||
if (num_files > 0) {
|
||||
s = Status::InvalidArgument(
|
||||
"Not all files are at level 0. Cannot "
|
||||
"open with universal or FIFO compaction style.");
|
||||
"open with FIFO compaction style.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-4
@@ -150,6 +150,8 @@ class DBImpl : public DB {
|
||||
using DB::GetOptions;
|
||||
virtual const Options& GetOptions(
|
||||
ColumnFamilyHandle* column_family) const override;
|
||||
using DB::GetDBOptions;
|
||||
virtual const DBOptions& GetDBOptions() const override;
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
@@ -183,6 +185,12 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
|
||||
// experimental API
|
||||
Status SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end);
|
||||
|
||||
Status PromoteL0(ColumnFamilyHandle* column_family, int target_level);
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// checks if all live files exist on file system and that their file sizes
|
||||
@@ -243,10 +251,12 @@ class DBImpl : public DB {
|
||||
// pass the pointer that you got from TEST_BeginWrite()
|
||||
void TEST_EndWrite(void* w);
|
||||
|
||||
uint64_t TEST_max_total_in_memory_state() {
|
||||
uint64_t TEST_MaxTotalInMemoryState() const {
|
||||
return max_total_in_memory_state_;
|
||||
}
|
||||
|
||||
size_t TEST_LogsToFreeSize();
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
@@ -267,7 +277,7 @@ class DBImpl : public DB {
|
||||
|
||||
const SnapshotList& snapshots() const { return snapshots_; }
|
||||
|
||||
void CancelAllBackgroundWork();
|
||||
void CancelAllBackgroundWork(bool wait);
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
@@ -352,8 +362,8 @@ class DBImpl : public DB {
|
||||
// database is opened) and is heavyweight because it holds the mutex
|
||||
// for the entire period. The second method WriteLevel0Table supports
|
||||
// concurrent flush memtables to storage.
|
||||
Status WriteLevel0TableForRecovery(ColumnFamilyData* cfd, MemTable* mem,
|
||||
VersionEdit* edit);
|
||||
Status WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
MemTable* mem, VersionEdit* edit);
|
||||
Status DelayWrite(uint64_t expiration_time);
|
||||
|
||||
Status ScheduleFlushes(WriteContext* context);
|
||||
@@ -461,6 +471,9 @@ class DBImpl : public DB {
|
||||
// If true, we have only one (default) column family. We use this to optimize
|
||||
// some code-paths
|
||||
bool single_column_family_mode_;
|
||||
// If this is non-empty, we need to delete these log files in background
|
||||
// threads. Protected by db mutex.
|
||||
autovector<log::Writer*> logs_to_free_;
|
||||
|
||||
bool is_snapshot_supported_;
|
||||
|
||||
|
||||
@@ -141,5 +141,10 @@ void DBImpl::TEST_EndWrite(void* w) {
|
||||
delete writer;
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_LogsToFreeSize() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return logs_to_free_.size();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright (c) 2013, 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_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status DBImpl::SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
InternalKey start_key, end_key;
|
||||
if (begin != nullptr) {
|
||||
start_key.SetMaxPossibleForUserKey(*begin);
|
||||
}
|
||||
if (end != nullptr) {
|
||||
end_key.SetMinPossibleForUserKey(*end);
|
||||
}
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto vstorage = cfd->current()->storage_info();
|
||||
for (int level = 0; level < vstorage->num_non_empty_levels() - 1; ++level) {
|
||||
std::vector<FileMetaData*> inputs;
|
||||
vstorage->GetOverlappingInputs(
|
||||
level, begin == nullptr ? nullptr : &start_key,
|
||||
end == nullptr ? nullptr : &end_key, &inputs);
|
||||
for (auto f : inputs) {
|
||||
f->marked_for_compaction = true;
|
||||
}
|
||||
}
|
||||
// Since we have some more files to compact, we should also recompute
|
||||
// compaction score
|
||||
vstorage->ComputeCompactionScore(*cfd->GetLatestMutableCFOptions(),
|
||||
CompactionOptionsFIFO());
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
assert(column_family);
|
||||
|
||||
if (target_level < 1) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"PromoteL0 FAILED. Invalid target level %d\n", target_level);
|
||||
return Status::InvalidArgument("Invalid target level");
|
||||
}
|
||||
|
||||
Status status;
|
||||
VersionEdit edit;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(column_family)->cfd();
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
|
||||
if (target_level >= vstorage->num_levels()) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"PromoteL0 FAILED. Target level %d does not exist\n", target_level);
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument("Target level does not exist");
|
||||
}
|
||||
|
||||
// Sort L0 files by range.
|
||||
const InternalKeyComparator* icmp = &cfd->internal_comparator();
|
||||
auto l0_files = vstorage->LevelFiles(0);
|
||||
std::sort(l0_files.begin(), l0_files.end(),
|
||||
[icmp](FileMetaData* f1, FileMetaData* f2) {
|
||||
return icmp->Compare(f1->largest, f2->largest) < 0;
|
||||
});
|
||||
|
||||
// Check that no L0 file is being compacted and that they have
|
||||
// non-overlapping ranges.
|
||||
for (size_t i = 0; i < l0_files.size(); ++i) {
|
||||
auto f = l0_files[i];
|
||||
if (f->being_compacted) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"PromoteL0 FAILED. File %" PRIu64 " being compacted\n",
|
||||
f->fd.GetNumber());
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument("PromoteL0 called during L0 compaction");
|
||||
}
|
||||
|
||||
if (i == 0) continue;
|
||||
auto prev_f = l0_files[i - 1];
|
||||
if (icmp->Compare(prev_f->largest, f->smallest) >= 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"PromoteL0 FAILED. Files %" PRIu64 " and %" PRIu64
|
||||
" have overlapping ranges\n",
|
||||
prev_f->fd.GetNumber(), f->fd.GetNumber());
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument("L0 has overlapping files");
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all levels up to target_level are empty.
|
||||
for (int level = 1; level <= target_level; ++level) {
|
||||
if (vstorage->NumLevelFiles(level) > 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"PromoteL0 FAILED. Level %d not empty\n", level);
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument(
|
||||
"All levels up to target_level "
|
||||
"must be empty");
|
||||
}
|
||||
}
|
||||
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
for (const auto& f : l0_files) {
|
||||
edit.DeleteFile(0, f->fd.GetNumber());
|
||||
edit.AddFile(target_level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->smallest_seqno, f->largest_seqno);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionBackground(cfd, &job_context,
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
} // lock released here
|
||||
LogFlush(db_options_.info_log);
|
||||
job_context.Clean();
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
+1
-1
@@ -254,7 +254,7 @@ void DBIter::FindNextUserEntryInternal(bool skipping) {
|
||||
}
|
||||
// If we have sequentially iterated via numerous keys and still not
|
||||
// found the next user-key, then it is better to seek so that we can
|
||||
// avoid too many key comparisons. We seek to the last occurence of
|
||||
// avoid too many key comparisons. We seek to the last occurrence of
|
||||
// our current key by looking for sequence number 0.
|
||||
if (skipping && num_skipped > max_skip_) {
|
||||
num_skipped = 0;
|
||||
|
||||
+1213
-219
File diff suppressed because it is too large.
Load diff
@@ -135,6 +135,20 @@ class InternalKey {
|
||||
AppendInternalKey(&rep_, ParsedInternalKey(_user_key, s, t));
|
||||
}
|
||||
|
||||
// sets the internal key to be bigger or equal to all internal keys with this
|
||||
// user key
|
||||
void SetMaxPossibleForUserKey(const Slice& _user_key) {
|
||||
AppendInternalKey(&rep_, ParsedInternalKey(_user_key, kMaxSequenceNumber,
|
||||
kValueTypeForSeek));
|
||||
}
|
||||
|
||||
// sets the internal key to be smaller or equal to all internal keys with this
|
||||
// user key
|
||||
void SetMinPossibleForUserKey(const Slice& _user_key) {
|
||||
AppendInternalKey(
|
||||
&rep_, ParsedInternalKey(_user_key, 0, static_cast<ValueType>(0)));
|
||||
}
|
||||
|
||||
bool Valid() const {
|
||||
ParsedInternalKey parsed;
|
||||
return ParseInternalKey(Slice(rep_), &parsed);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2013, 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.
|
||||
|
||||
#include "db/event_logger_helpers.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
inline double SafeDivide(double a, double b) { return b == 0.0 ? 0 : a / b; }
|
||||
} // namespace
|
||||
|
||||
void EventLoggerHelpers::LogTableFileCreation(
|
||||
EventLogger* event_logger, int job_id, uint64_t file_number,
|
||||
uint64_t file_size, const TableProperties& table_properties) {
|
||||
auto stream = event_logger->Log();
|
||||
stream << "job" << job_id << "event"
|
||||
<< "table_file_creation"
|
||||
<< "file_number" << file_number << "file_size" << file_size
|
||||
<< "table_properties";
|
||||
stream.StartObject();
|
||||
|
||||
// basic properties:
|
||||
stream << "data_size" << table_properties.data_size
|
||||
<< "index_size" << table_properties.index_size
|
||||
<< "filter_size" << table_properties.filter_size
|
||||
<< "raw_key_size" << table_properties.raw_key_size
|
||||
<< "raw_average_key_size" << SafeDivide(table_properties.raw_key_size,
|
||||
table_properties.num_entries)
|
||||
<< "raw_value_size" << table_properties.raw_value_size
|
||||
<< "raw_average_value_size" << SafeDivide(
|
||||
table_properties.raw_value_size, table_properties.num_entries)
|
||||
<< "num_data_blocks" << table_properties.num_data_blocks
|
||||
<< "num_entries" << table_properties.num_entries
|
||||
<< "filter_policy_name" << table_properties.filter_policy_name;
|
||||
|
||||
// user collected properties
|
||||
for (const auto& prop : table_properties.user_collected_properties) {
|
||||
stream << prop.first << prop.second;
|
||||
}
|
||||
|
||||
stream.EndObject();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2013, 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 "util/event_logger.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class EventLoggerHelpers {
|
||||
public:
|
||||
static void LogTableFileCreation(EventLogger* event_logger, int job_id,
|
||||
uint64_t file_number, uint64_t file_size,
|
||||
const TableProperties& table_properties);
|
||||
};
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2014, 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.
|
||||
|
||||
#include "rocksdb/experimental.h"
|
||||
|
||||
#include "db/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace experimental {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
auto dbimpl = dynamic_cast<DBImpl*>(db);
|
||||
if (dbimpl == nullptr) {
|
||||
return Status::InvalidArgument("Didn't recognize DB object");
|
||||
}
|
||||
|
||||
return dbimpl->SuggestCompactRange(column_family, begin, end);
|
||||
}
|
||||
|
||||
Status PromoteL0(DB* db, ColumnFamilyHandle* column_family, int target_level) {
|
||||
auto dbimpl = dynamic_cast<DBImpl*>(db);
|
||||
if (dbimpl == nullptr) {
|
||||
return Status::InvalidArgument("Didn't recognize DB object");
|
||||
}
|
||||
return dbimpl->PromoteL0(column_family, target_level);
|
||||
}
|
||||
|
||||
#else // ROCKSDB_LITE
|
||||
|
||||
Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
return Status::NotSupported("Not supported in RocksDB LITE");
|
||||
}
|
||||
|
||||
Status PromoteL0(DB* db, ColumnFamilyHandle* column_family, int target_level) {
|
||||
return Status::NotSupported("Not supported in RocksDB LITE");
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status SuggestCompactRange(DB* db, const Slice* begin, const Slice* end) {
|
||||
return SuggestCompactRange(db, db->DefaultColumnFamily(), begin, end);
|
||||
}
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace rocksdb
|
||||
@@ -726,6 +726,76 @@ TEST_F(FaultInjectionTest, FaultTest) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
class SleepingBackgroundTask {
|
||||
public:
|
||||
SleepingBackgroundTask()
|
||||
: bg_cv_(&mutex_), should_sleep_(true), done_with_sleep_(false) {}
|
||||
void DoSleep() {
|
||||
MutexLock l(&mutex_);
|
||||
while (should_sleep_) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
done_with_sleep_ = true;
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
void WakeUp() {
|
||||
MutexLock l(&mutex_);
|
||||
should_sleep_ = false;
|
||||
bg_cv_.SignalAll();
|
||||
while (!done_with_sleep_) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
static void DoSleepTask(void* arg) {
|
||||
reinterpret_cast<SleepingBackgroundTask*>(arg)->DoSleep();
|
||||
}
|
||||
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
port::CondVar bg_cv_; // Signalled when background work finishes
|
||||
bool should_sleep_;
|
||||
bool done_with_sleep_;
|
||||
};
|
||||
|
||||
// Disable the test because it is not passing.
|
||||
// Previous log file is not fsynced if sync is forced after log rolling.
|
||||
// TODO(FB internal task#6730880) Fix the bug
|
||||
TEST_F(FaultInjectionTest, DISABLED_WriteOptionSyncTest) {
|
||||
SleepingBackgroundTask sleeping_task_low;
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
// Block the job queue to prevent flush job from running.
|
||||
env_->Schedule(&SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::HIGH);
|
||||
|
||||
WriteOptions write_options;
|
||||
write_options.sync = false;
|
||||
|
||||
std::string key_space, value_space;
|
||||
ASSERT_OK(
|
||||
db_->Put(write_options, Key(1, &key_space), Value(1, &value_space)));
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = false;
|
||||
ASSERT_OK(db_->Flush(flush_options));
|
||||
write_options.sync = true;
|
||||
ASSERT_OK(
|
||||
db_->Put(write_options, Key(2, &key_space), Value(2, &value_space)));
|
||||
|
||||
env_->SetFilesystemActive(false);
|
||||
NoWriteTestReopenWithFault(kResetDropAndDeleteUnsynced);
|
||||
sleeping_task_low.WakeUp();
|
||||
|
||||
ASSERT_OK(OpenDB());
|
||||
std::string val;
|
||||
Value(2, &value_space);
|
||||
ASSERT_OK(ReadValue(2, &val));
|
||||
ASSERT_EQ(value_space, val);
|
||||
|
||||
Value(1, &value_space);
|
||||
ASSERT_OK(ReadValue(1, &val));
|
||||
ASSERT_EQ(value_space, val);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -20,6 +20,9 @@ FileIndexer::FileIndexer(const Comparator* ucmp)
|
||||
size_t FileIndexer::NumLevelIndex() const { return next_level_index_.size(); }
|
||||
|
||||
size_t FileIndexer::LevelIndexSize(size_t level) const {
|
||||
if (level >= next_level_index_.size()) {
|
||||
return 0;
|
||||
}
|
||||
return next_level_index_[level].num_index;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "db/file_indexer.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
@@ -343,6 +344,7 @@ TEST_F(FileIndexerTest, mixed) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+68
-8
@@ -20,6 +20,7 @@
|
||||
#include "db/builder.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_logger_helpers.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
@@ -81,8 +82,7 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
stats_(stats),
|
||||
event_logger_(event_logger) {
|
||||
// Update the thread status to indicate flush.
|
||||
ThreadStatusUtil::SetColumnFamily(cfd_);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_FLUSH);
|
||||
ReportStartedFlush();
|
||||
TEST_SYNC_POINT("FlushJob::FlushJob()");
|
||||
}
|
||||
|
||||
@@ -91,6 +91,31 @@ FlushJob::~FlushJob() {
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
}
|
||||
|
||||
void FlushJob::ReportStartedFlush() {
|
||||
ThreadStatusUtil::SetColumnFamily(cfd_);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_FLUSH);
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_JOB_ID,
|
||||
job_context_->job_id);
|
||||
IOSTATS_RESET(bytes_written);
|
||||
}
|
||||
|
||||
void FlushJob::ReportFlushInputSize(const autovector<MemTable*>& mems) {
|
||||
uint64_t input_size = 0;
|
||||
for (auto* mem : mems) {
|
||||
input_size += mem->ApproximateMemoryUsage();
|
||||
}
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::FLUSH_BYTES_MEMTABLES,
|
||||
input_size);
|
||||
}
|
||||
|
||||
void FlushJob::RecordFlushIOStats() {
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::FLUSH_BYTES_WRITTEN, IOSTATS(bytes_written));
|
||||
IOSTATS_RESET(bytes_written);
|
||||
}
|
||||
|
||||
Status FlushJob::Run(uint64_t* file_number) {
|
||||
AutoThreadOperationStageUpdater stage_run(
|
||||
ThreadStatus::STAGE_FLUSH_RUN);
|
||||
@@ -104,6 +129,7 @@ Status FlushJob::Run(uint64_t* file_number) {
|
||||
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
|
||||
@@ -137,6 +163,18 @@ Status FlushJob::Run(uint64_t* file_number) {
|
||||
if (s.ok() && file_number != nullptr) {
|
||||
*file_number = fn;
|
||||
}
|
||||
RecordFlushIOStats();
|
||||
|
||||
auto stream = event_logger_->LogToBuffer(log_buffer_);
|
||||
stream << "job" << job_context_->job_id << "event"
|
||||
<< "flush_finished";
|
||||
stream << "lsm_state";
|
||||
stream.StartArray();
|
||||
auto vstorage = cfd_->current()->storage_info();
|
||||
for (int level = 0; level < vstorage->num_levels(); ++level) {
|
||||
stream << vstorage->NumLevelFiles(level);
|
||||
}
|
||||
stream.EndArray();
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -166,12 +204,25 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
Arena arena;
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
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());
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
total_num_entries += m->num_entries();
|
||||
total_num_deletes += m->num_deletes();
|
||||
total_memory_usage += m->ApproximateMemoryUsage();
|
||||
}
|
||||
|
||||
event_logger_->Log() << "job" << job_context_->job_id << "event"
|
||||
<< "flush_started"
|
||||
<< "num_memtables" << mems.size() << "num_entries"
|
||||
<< total_num_entries << "num_deletes"
|
||||
<< total_num_deletes << "memory_usage"
|
||||
<< total_memory_usage;
|
||||
TableProperties table_properties;
|
||||
{
|
||||
ScopedArenaIterator iter(
|
||||
NewMergingIterator(&cfd_->internal_comparator(), &memtables[0],
|
||||
@@ -180,21 +231,30 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
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(), env_options_,
|
||||
cfd_->table_cache(), iter.get(), &meta,
|
||||
cfd_->internal_comparator(), newest_snapshot_,
|
||||
cfd_->internal_comparator(),
|
||||
cfd_->int_tbl_prop_collector_factories(), newest_snapshot_,
|
||||
earliest_seqno_in_memtable, output_compression_,
|
||||
cfd_->ioptions()->compression_opts, Env::IO_HIGH);
|
||||
cfd_->ioptions()->compression_opts,
|
||||
mutable_cf_options_.paranoid_file_checks, Env::IO_HIGH,
|
||||
&table_properties);
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": %" PRIu64 " bytes %s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta.fd.GetNumber(),
|
||||
meta.fd.GetFileSize(), s.ToString().c_str());
|
||||
event_logger_->Log() << "event"
|
||||
<< "table_file_creation"
|
||||
<< "file_number" << meta.fd.GetNumber() << "file_size"
|
||||
<< meta.fd.GetFileSize();
|
||||
|
||||
// output to event logger
|
||||
if (s.ok()) {
|
||||
EventLoggerHelpers::LogTableFileCreation(
|
||||
event_logger_, job_context_->job_id, meta.fd.GetNumber(),
|
||||
meta.fd.GetFileSize(), table_properties);
|
||||
}
|
||||
|
||||
if (!db_options_.disableDataSync && output_file_directory_ != nullptr) {
|
||||
output_file_directory_->Fsync();
|
||||
}
|
||||
|
||||
@@ -67,6 +67,9 @@ class FlushJob {
|
||||
Status Run(uint64_t* file_number = nullptr);
|
||||
|
||||
private:
|
||||
void ReportStartedFlush();
|
||||
void ReportFlushInputSize(const autovector<MemTable*>& mems);
|
||||
void RecordFlushIOStats();
|
||||
Status WriteLevel0Table(const autovector<MemTable*>& mems, VersionEdit* edit,
|
||||
uint64_t* filenumber);
|
||||
const std::string& dbname_;
|
||||
|
||||
+188
-51
@@ -13,6 +13,7 @@
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
|
||||
@@ -82,12 +83,84 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
}
|
||||
}
|
||||
|
||||
static const std::string rocksdb_prefix = "rocksdb.";
|
||||
|
||||
static const std::string num_files_at_level_prefix = "num-files-at-level";
|
||||
static const std::string allstats = "stats";
|
||||
static const std::string sstables = "sstables";
|
||||
static const std::string cfstats = "cfstats";
|
||||
static const std::string dbstats = "dbstats";
|
||||
static const std::string levelstats = "levelstats";
|
||||
static const std::string num_immutable_mem_table = "num-immutable-mem-table";
|
||||
static const std::string mem_table_flush_pending = "mem-table-flush-pending";
|
||||
static const std::string compaction_pending = "compaction-pending";
|
||||
static const std::string background_errors = "background-errors";
|
||||
static const std::string cur_size_active_mem_table =
|
||||
"cur-size-active-mem-table";
|
||||
static const std::string cur_size_all_mem_tables = "cur-size-all-mem-tables";
|
||||
static const std::string num_entries_active_mem_table =
|
||||
"num-entries-active-mem-table";
|
||||
static const std::string num_entries_imm_mem_tables =
|
||||
"num-entries-imm-mem-tables";
|
||||
static const std::string num_deletes_active_mem_table =
|
||||
"num-deletes-active-mem-table";
|
||||
static const std::string num_deletes_imm_mem_tables =
|
||||
"num-deletes-imm-mem-tables";
|
||||
static const std::string estimate_num_keys = "estimate-num-keys";
|
||||
static const std::string estimate_table_readers_mem =
|
||||
"estimate-table-readers-mem";
|
||||
static const std::string is_file_deletions_enabled =
|
||||
"is-file-deletions-enabled";
|
||||
static const std::string num_snapshots = "num-snapshots";
|
||||
static const std::string oldest_snapshot_time = "oldest-snapshot-time";
|
||||
static const std::string num_live_versions = "num-live-versions";
|
||||
static const std::string base_level = "base-level";
|
||||
|
||||
const std::string DB::Properties::kNumFilesAtLevelPrefix =
|
||||
rocksdb_prefix + num_files_at_level_prefix;
|
||||
const std::string DB::Properties::kStats = rocksdb_prefix + allstats;
|
||||
const std::string DB::Properties::kSSTables = rocksdb_prefix + sstables;
|
||||
const std::string DB::Properties::kCFStats = rocksdb_prefix + cfstats;
|
||||
const std::string DB::Properties::kDBStats = rocksdb_prefix + dbstats;
|
||||
const std::string DB::Properties::kNumImmutableMemTable =
|
||||
rocksdb_prefix + num_immutable_mem_table;
|
||||
const std::string DB::Properties::kMemTableFlushPending =
|
||||
rocksdb_prefix + mem_table_flush_pending;
|
||||
const std::string DB::Properties::kCompactionPending =
|
||||
rocksdb_prefix + compaction_pending;
|
||||
const std::string DB::Properties::kBackgroundErrors =
|
||||
rocksdb_prefix + background_errors;
|
||||
const std::string DB::Properties::kCurSizeActiveMemTable =
|
||||
rocksdb_prefix + cur_size_active_mem_table;
|
||||
const std::string DB::Properties::kCurSizeAllMemTables =
|
||||
rocksdb_prefix + cur_size_all_mem_tables;
|
||||
const std::string DB::Properties::kNumEntriesActiveMemTable =
|
||||
rocksdb_prefix + num_entries_active_mem_table;
|
||||
const std::string DB::Properties::kNumEntriesImmMemTables =
|
||||
rocksdb_prefix + num_entries_imm_mem_tables;
|
||||
const std::string DB::Properties::kNumDeletesActiveMemTable =
|
||||
rocksdb_prefix + num_deletes_active_mem_table;
|
||||
const std::string DB::Properties::kNumDeletesImmMemTables =
|
||||
rocksdb_prefix + num_deletes_imm_mem_tables;
|
||||
const std::string DB::Properties::kEstimateNumKeys =
|
||||
rocksdb_prefix + estimate_num_keys;
|
||||
const std::string DB::Properties::kEstimateTableReadersMem =
|
||||
rocksdb_prefix + estimate_table_readers_mem;
|
||||
const std::string DB::Properties::kIsFileDeletionsEnabled =
|
||||
rocksdb_prefix + is_file_deletions_enabled;
|
||||
const std::string DB::Properties::kNumSnapshots =
|
||||
rocksdb_prefix + num_snapshots;
|
||||
const std::string DB::Properties::kOldestSnapshotTime =
|
||||
rocksdb_prefix + oldest_snapshot_time;
|
||||
const std::string DB::Properties::kNumLiveVersions =
|
||||
rocksdb_prefix + num_live_versions;
|
||||
|
||||
DBPropertyType GetPropertyType(const Slice& property, bool* is_int_property,
|
||||
bool* need_out_of_mutex) {
|
||||
assert(is_int_property != nullptr);
|
||||
assert(need_out_of_mutex != nullptr);
|
||||
Slice in = property;
|
||||
Slice prefix("rocksdb.");
|
||||
Slice prefix(rocksdb_prefix);
|
||||
*need_out_of_mutex = false;
|
||||
*is_int_property = false;
|
||||
if (!in.starts_with(prefix)) {
|
||||
@@ -95,55 +168,55 @@ DBPropertyType GetPropertyType(const Slice& property, bool* is_int_property,
|
||||
}
|
||||
in.remove_prefix(prefix.size());
|
||||
|
||||
if (in.starts_with("num-files-at-level")) {
|
||||
if (in.starts_with(num_files_at_level_prefix)) {
|
||||
return kNumFilesAtLevel;
|
||||
} else if (in == "levelstats") {
|
||||
} else if (in == levelstats) {
|
||||
return kLevelStats;
|
||||
} else if (in == "stats") {
|
||||
} else if (in == allstats) {
|
||||
return kStats;
|
||||
} else if (in == "cfstats") {
|
||||
} else if (in == cfstats) {
|
||||
return kCFStats;
|
||||
} else if (in == "dbstats") {
|
||||
} else if (in == dbstats) {
|
||||
return kDBStats;
|
||||
} else if (in == "sstables") {
|
||||
} else if (in == sstables) {
|
||||
return kSsTables;
|
||||
}
|
||||
|
||||
*is_int_property = true;
|
||||
if (in == "num-immutable-mem-table") {
|
||||
if (in == num_immutable_mem_table) {
|
||||
return kNumImmutableMemTable;
|
||||
} else if (in == "mem-table-flush-pending") {
|
||||
} else if (in == mem_table_flush_pending) {
|
||||
return kMemtableFlushPending;
|
||||
} else if (in == "compaction-pending") {
|
||||
} else if (in == compaction_pending) {
|
||||
return kCompactionPending;
|
||||
} else if (in == "background-errors") {
|
||||
} else if (in == background_errors) {
|
||||
return kBackgroundErrors;
|
||||
} else if (in == "cur-size-active-mem-table") {
|
||||
} else if (in == cur_size_active_mem_table) {
|
||||
return kCurSizeActiveMemTable;
|
||||
} else if (in == "cur-size-all-mem-tables") {
|
||||
} else if (in == cur_size_all_mem_tables) {
|
||||
return kCurSizeAllMemTables;
|
||||
} else if (in == "num-entries-active-mem-table") {
|
||||
} else if (in == num_entries_active_mem_table) {
|
||||
return kNumEntriesInMutableMemtable;
|
||||
} else if (in == "num-entries-imm-mem-tables") {
|
||||
} else if (in == num_entries_imm_mem_tables) {
|
||||
return kNumEntriesInImmutableMemtable;
|
||||
} else if (in == "num-deletes-active-mem-table") {
|
||||
} else if (in == num_deletes_active_mem_table) {
|
||||
return kNumDeletesInMutableMemtable;
|
||||
} else if (in == "num-deletes-imm-mem-tables") {
|
||||
} else if (in == num_deletes_imm_mem_tables) {
|
||||
return kNumDeletesInImmutableMemtable;
|
||||
} else if (in == "estimate-num-keys") {
|
||||
} else if (in == estimate_num_keys) {
|
||||
return kEstimatedNumKeys;
|
||||
} else if (in == "estimate-table-readers-mem") {
|
||||
} else if (in == estimate_table_readers_mem) {
|
||||
*need_out_of_mutex = true;
|
||||
return kEstimatedUsageByTableReaders;
|
||||
} else if (in == "is-file-deletions-enabled") {
|
||||
} else if (in == is_file_deletions_enabled) {
|
||||
return kIsFileDeletionEnabled;
|
||||
} else if (in == "num-snapshots") {
|
||||
} else if (in == num_snapshots) {
|
||||
return kNumSnapshots;
|
||||
} else if (in == "oldest-snapshot-time") {
|
||||
} else if (in == oldest_snapshot_time) {
|
||||
return kOldestSnapshotTime;
|
||||
} else if (in == "num-live-versions") {
|
||||
} else if (in == num_live_versions) {
|
||||
return kNumLiveVersions;
|
||||
} else if (in == "base-level") {
|
||||
} else if (in == base_level) {
|
||||
return kBaseLevel;
|
||||
}
|
||||
return kUnknown;
|
||||
@@ -203,10 +276,10 @@ bool InternalStats::GetStringProperty(DBPropertyType property_type,
|
||||
return true;
|
||||
}
|
||||
case kStats: {
|
||||
if (!GetStringProperty(kCFStats, "rocksdb.cfstats", value)) {
|
||||
if (!GetStringProperty(kCFStats, DB::Properties::kCFStats, value)) {
|
||||
return false;
|
||||
}
|
||||
if (!GetStringProperty(kDBStats, "rocksdb.dbstats", value)) {
|
||||
if (!GetStringProperty(kDBStats, DB::Properties::kDBStats, value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -324,6 +397,10 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
uint64_t wal_synced = db_stats_[InternalStats::WAL_FILE_SYNCED];
|
||||
uint64_t write_with_wal = db_stats_[InternalStats::WRITE_WITH_WAL];
|
||||
uint64_t write_stall_micros = db_stats_[InternalStats::WRITE_STALL_MICROS];
|
||||
uint64_t compact_bytes_read = 0;
|
||||
uint64_t compact_bytes_write = 0;
|
||||
uint64_t compact_micros = 0;
|
||||
|
||||
const int kHumanMicrosLen = 32;
|
||||
char human_micros[kHumanMicrosLen];
|
||||
|
||||
@@ -336,22 +413,47 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
// writes/batches is the average group commit size.
|
||||
//
|
||||
// The format is the same for interval stats.
|
||||
AppendHumanMicros(write_stall_micros, human_micros, kHumanMicrosLen);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative writes: %" PRIu64 " writes, %" PRIu64 " keys, %" PRIu64
|
||||
" batches, %.1f writes per batch, %.2f GB user ingest, "
|
||||
"stall time: %s\n",
|
||||
write_other + write_self, num_keys_written, write_self,
|
||||
"Cumulative writes: %s writes, %s keys, %s batches, "
|
||||
"%.1f writes per batch, 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(),
|
||||
(write_other + write_self) / static_cast<double>(write_self + 1),
|
||||
user_bytes_written / kGB, human_micros);
|
||||
user_bytes_written / kGB, user_bytes_written / kMB / seconds_up);
|
||||
value->append(buf);
|
||||
// WAL
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative WAL: %" PRIu64 " writes, %" PRIu64 " syncs, "
|
||||
"%.2f writes per sync, %.2f GB written\n",
|
||||
write_with_wal, wal_synced,
|
||||
"Cumulative WAL: %s writes, %s syncs, "
|
||||
"%.2f writes per sync, written: %.2f GB, %.2f MB/s\n",
|
||||
NumberToHumanString(write_with_wal).c_str(),
|
||||
NumberToHumanString(wal_synced).c_str(),
|
||||
write_with_wal / static_cast<double>(wal_synced + 1),
|
||||
wal_bytes / kGB);
|
||||
wal_bytes / kGB, wal_bytes / kMB / seconds_up);
|
||||
value->append(buf);
|
||||
// Compact
|
||||
for (int level = 0; level < number_levels_; level++) {
|
||||
compact_bytes_read += comp_stats_[level].bytes_readnp1 +
|
||||
comp_stats_[level].bytes_readn;
|
||||
compact_bytes_write += comp_stats_[level].bytes_written;
|
||||
compact_micros += comp_stats_[level].micros;
|
||||
}
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative compaction: %.2f GB write, %.2f MB/s write, "
|
||||
"%.2f GB read, %.2f MB/s read, %.1f seconds\n",
|
||||
compact_bytes_write / kGB,
|
||||
compact_bytes_write / kMB / seconds_up,
|
||||
compact_bytes_read / kGB,
|
||||
compact_bytes_read / kMB / seconds_up,
|
||||
compact_micros / 1000000.0);
|
||||
value->append(buf);
|
||||
// Stall
|
||||
AppendHumanMicros(write_stall_micros, human_micros, kHumanMicrosLen, true);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative stall: %s, %.1f percent\n",
|
||||
human_micros,
|
||||
// 10000 = divide by 1M to get secs, then multiply by 100 for pct
|
||||
write_stall_micros / 10000.0 / std::max(seconds_up, 0.001));
|
||||
value->append(buf);
|
||||
|
||||
// Interval
|
||||
@@ -359,19 +461,18 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
uint64_t interval_write_self = write_self - db_stats_snapshot_.write_self;
|
||||
uint64_t interval_num_keys_written =
|
||||
num_keys_written - db_stats_snapshot_.num_keys_written;
|
||||
AppendHumanMicros(
|
||||
write_stall_micros - db_stats_snapshot_.write_stall_micros,
|
||||
human_micros, kHumanMicrosLen);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval writes: %" PRIu64 " writes, %" PRIu64 " keys, %" PRIu64
|
||||
" batches, %.1f writes per batch, %.1f MB user ingest, "
|
||||
"stall time: %s\n",
|
||||
interval_write_other + interval_write_self,
|
||||
interval_num_keys_written, interval_write_self,
|
||||
"Interval writes: %s writes, %s keys, %s batches, "
|
||||
"%.1f writes per batch, ingest: %.2f MB, %.2f MB/s\n",
|
||||
NumberToHumanString(
|
||||
interval_write_other + interval_write_self).c_str(),
|
||||
NumberToHumanString(interval_num_keys_written).c_str(),
|
||||
NumberToHumanString(interval_write_self).c_str(),
|
||||
static_cast<double>(interval_write_other + interval_write_self) /
|
||||
(interval_write_self + 1),
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB,
|
||||
human_micros);
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB /
|
||||
std::max(interval_seconds_up, 0.001)),
|
||||
value->append(buf);
|
||||
|
||||
uint64_t interval_write_with_wal =
|
||||
@@ -380,13 +481,46 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
uint64_t interval_wal_bytes = wal_bytes - db_stats_snapshot_.wal_bytes;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval WAL: %" PRIu64 " writes, %" PRIu64 " syncs, "
|
||||
"%.2f writes per sync, %.2f MB written\n",
|
||||
interval_write_with_wal,
|
||||
interval_wal_synced,
|
||||
"Interval WAL: %s writes, %s syncs, "
|
||||
"%.2f writes per sync, written: %.2f MB, %.2f MB/s\n",
|
||||
NumberToHumanString(interval_write_with_wal).c_str(),
|
||||
NumberToHumanString(interval_wal_synced).c_str(),
|
||||
interval_write_with_wal /
|
||||
static_cast<double>(interval_wal_synced + 1),
|
||||
interval_wal_bytes / kGB);
|
||||
interval_wal_bytes / kGB,
|
||||
interval_wal_bytes / kMB / std::max(interval_seconds_up, 0.001));
|
||||
value->append(buf);
|
||||
|
||||
// Compaction
|
||||
uint64_t interval_compact_bytes_write =
|
||||
compact_bytes_write - db_stats_snapshot_.compact_bytes_write;
|
||||
uint64_t interval_compact_bytes_read =
|
||||
compact_bytes_read - db_stats_snapshot_.compact_bytes_read;
|
||||
uint64_t interval_compact_micros =
|
||||
compact_micros - db_stats_snapshot_.compact_micros;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval compaction: %.2f GB write, %.2f MB/s write, "
|
||||
"%.2f GB read, %.2f MB/s read, %.1f seconds\n",
|
||||
interval_compact_bytes_write / kGB,
|
||||
interval_compact_bytes_write / kMB /
|
||||
std::max(interval_seconds_up, 0.001),
|
||||
interval_compact_bytes_read / kGB,
|
||||
interval_compact_bytes_read / kMB /
|
||||
std::max(interval_seconds_up, 0.001),
|
||||
interval_compact_micros / 1000000.0);
|
||||
value->append(buf);
|
||||
|
||||
// Stall
|
||||
AppendHumanMicros(
|
||||
write_stall_micros - db_stats_snapshot_.write_stall_micros,
|
||||
human_micros, kHumanMicrosLen, true);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval stall: %s, %.1f percent\n",
|
||||
human_micros,
|
||||
// 10000 = divide by 1M to get secs, then multiply by 100 for pct
|
||||
(write_stall_micros - db_stats_snapshot_.write_stall_micros) /
|
||||
10000.0 / std::max(interval_seconds_up, 0.001));
|
||||
value->append(buf);
|
||||
|
||||
db_stats_snapshot_.seconds_up = seconds_up;
|
||||
@@ -398,6 +532,9 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
db_stats_snapshot_.wal_synced = wal_synced;
|
||||
db_stats_snapshot_.write_with_wal = write_with_wal;
|
||||
db_stats_snapshot_.write_stall_micros = write_stall_micros;
|
||||
db_stats_snapshot_.compact_bytes_write = compact_bytes_write;
|
||||
db_stats_snapshot_.compact_bytes_read = compact_bytes_read;
|
||||
db_stats_snapshot_.compact_micros = compact_micros;
|
||||
}
|
||||
|
||||
void InternalStats::DumpCFStats(std::string* value) {
|
||||
@@ -485,7 +622,7 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
value->append(buf);
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Flush(GB): accumulative %.3f, interval %.3f\n",
|
||||
"Flush(GB): cumulative %.3f, interval %.3f\n",
|
||||
curr_ingest / kGB, interval_ingest / kGB);
|
||||
value->append(buf);
|
||||
|
||||
|
||||
+8
-1
@@ -279,6 +279,10 @@ class InternalStats {
|
||||
// another thread.
|
||||
uint64_t write_other;
|
||||
uint64_t write_self;
|
||||
// Stats from compaction jobs - bytes written, bytes read, duration.
|
||||
uint64_t compact_bytes_write;
|
||||
uint64_t compact_bytes_read;
|
||||
uint64_t compact_micros;
|
||||
// Total number of keys written. write_self and write_other measure number
|
||||
// of write requests written, Each of the write request can contain updates
|
||||
// to multiple keys. num_keys_written is total number of keys updated by all
|
||||
@@ -295,6 +299,9 @@ class InternalStats {
|
||||
write_with_wal(0),
|
||||
write_other(0),
|
||||
write_self(0),
|
||||
compact_bytes_write(0),
|
||||
compact_bytes_read(0),
|
||||
compact_micros(0),
|
||||
num_keys_written(0),
|
||||
write_stall_micros(0),
|
||||
seconds_up(0) {}
|
||||
@@ -366,7 +373,7 @@ class InternalStats {
|
||||
|
||||
void IncBytesMoved(int level, uint64_t amount) {}
|
||||
|
||||
void RecordLevelNSlowdown(int level, uint64_t micros, bool soft) {}
|
||||
void RecordLevelNSlowdown(int level, bool soft) {}
|
||||
|
||||
void AddCFStats(InternalCFStatsType type, uint64_t value) {}
|
||||
|
||||
|
||||
+9
-1
@@ -13,6 +13,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/log_writer.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -22,7 +23,8 @@ struct JobContext {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return full_scan_candidate_files.size() || sst_delete_files.size() ||
|
||||
log_delete_files.size() || new_superversion != nullptr ||
|
||||
superversions_to_free.size() > 0 || memtables_to_free.size() > 0;
|
||||
superversions_to_free.size() > 0 || memtables_to_free.size() > 0 ||
|
||||
logs_to_free.size() > 0;
|
||||
}
|
||||
|
||||
// Structure to store information for candidate files to delete.
|
||||
@@ -59,6 +61,8 @@ struct JobContext {
|
||||
|
||||
autovector<SuperVersion*> superversions_to_free;
|
||||
|
||||
autovector<log::Writer*> logs_to_free;
|
||||
|
||||
SuperVersion* new_superversion; // if nullptr no new superversion
|
||||
|
||||
// the current manifest_file_number, log_number and prev_log_number
|
||||
@@ -88,12 +92,16 @@ struct JobContext {
|
||||
for (auto s : superversions_to_free) {
|
||||
delete s;
|
||||
}
|
||||
for (auto l : logs_to_free) {
|
||||
delete l;
|
||||
}
|
||||
// if new_superversion was not used, it will be non-nullptr and needs
|
||||
// to be freed here
|
||||
delete new_superversion;
|
||||
|
||||
memtables_to_free.clear();
|
||||
superversions_to_free.clear();
|
||||
logs_to_free.clear();
|
||||
new_superversion = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,10 +152,14 @@ class EventListenerTest : public testing::Test {
|
||||
class TestCompactionListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionCompleted(DB *db, const CompactionJobInfo& ci) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
compacted_dbs_.push_back(db);
|
||||
ASSERT_GT(ci.input_files.size(), 0U);
|
||||
ASSERT_GT(ci.output_files.size(), 0U);
|
||||
}
|
||||
|
||||
std::vector<DB*> compacted_dbs_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
TEST_F(EventListenerTest, OnSingleDBCompactionTest) {
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright (c) 2013, 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.
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "util/testharness.h"
|
||||
#include "util/benchharness.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
std::string MakeKey(uint64_t num) {
|
||||
char buf[30];
|
||||
snprintf(buf, sizeof(buf), "%016" PRIu64, num);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
void BM_LogAndApply(int iters, int num_base_files) {
|
||||
VersionSet* vset;
|
||||
WriteController wc;
|
||||
ColumnFamilyData* default_cfd;
|
||||
uint64_t fnum = 1;
|
||||
InstrumentedMutex mu;
|
||||
InstrumentedMutexLock l(&mu);
|
||||
|
||||
BENCHMARK_SUSPEND {
|
||||
std::string dbname = test::TmpDir() + "/rocksdb_test_benchmark";
|
||||
ASSERT_OK(DestroyDB(dbname, Options()));
|
||||
|
||||
DB* db = nullptr;
|
||||
Options opts;
|
||||
opts.create_if_missing = true;
|
||||
Status s = DB::Open(opts, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
delete db;
|
||||
db = nullptr;
|
||||
|
||||
Options options;
|
||||
EnvOptions sopt;
|
||||
// Notice we are using the default options not through SanitizeOptions().
|
||||
// We might want to initialize some options manually if needed.
|
||||
options.db_paths.emplace_back(dbname, 0);
|
||||
WriteBuffer wb(options.db_write_buffer_size);
|
||||
// The parameter of table cache is passed in as null, so any file I/O
|
||||
// operation is likely to fail.
|
||||
vset = new VersionSet(dbname, &options, sopt, nullptr, &wb, &wc);
|
||||
std::vector<ColumnFamilyDescriptor> dummy;
|
||||
dummy.push_back(ColumnFamilyDescriptor());
|
||||
ASSERT_OK(vset->Recover(dummy));
|
||||
default_cfd = vset->GetColumnFamilySet()->GetDefault();
|
||||
VersionEdit vbase;
|
||||
for (int i = 0; i < num_base_files; i++) {
|
||||
InternalKey start(MakeKey(2 * fnum), 1, kTypeValue);
|
||||
InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion);
|
||||
vbase.AddFile(2, ++fnum, 0, 1 /* file size */, start, limit, 1, 1);
|
||||
}
|
||||
ASSERT_OK(vset->LogAndApply(default_cfd,
|
||||
*default_cfd->GetLatestMutableCFOptions(), &vbase, &mu));
|
||||
}
|
||||
|
||||
for (int i = 0; i < iters; i++) {
|
||||
VersionEdit vedit;
|
||||
vedit.DeleteFile(2, fnum);
|
||||
InternalKey start(MakeKey(2 * fnum), 1, kTypeValue);
|
||||
InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion);
|
||||
vedit.AddFile(2, ++fnum, 0, 1 /* file size */, start, limit, 1, 1);
|
||||
vset->LogAndApply(default_cfd, *default_cfd->GetLatestMutableCFOptions(),
|
||||
&vedit, &mu);
|
||||
}
|
||||
delete vset;
|
||||
}
|
||||
|
||||
BENCHMARK_NAMED_PARAM(BM_LogAndApply, 1000_iters_1_file, 1000, 1)
|
||||
BENCHMARK_NAMED_PARAM(BM_LogAndApply, 1000_iters_100_files, 1000, 100)
|
||||
BENCHMARK_NAMED_PARAM(BM_LogAndApply, 1000_iters_10000_files, 1000, 10000)
|
||||
BENCHMARK_NAMED_PARAM(BM_LogAndApply, 100_iters_100000_files, 100, 100000)
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::benchmark::RunBenchmarks();
|
||||
return 0;
|
||||
}
|
||||
@@ -22,6 +22,12 @@ using std::unique_ptr;
|
||||
|
||||
namespace log {
|
||||
|
||||
/**
|
||||
* Reader is a general purpose log stream reader implementation. The actual job
|
||||
* of reading from the device is implemented by the SequentialFile interface.
|
||||
*
|
||||
* Please see Writer for details on the file and record layout.
|
||||
*/
|
||||
class Reader {
|
||||
public:
|
||||
// Interface for reporting errors.
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ Status Writer::AddRecord(const Slice& slice) {
|
||||
}
|
||||
|
||||
// Invariant: we never leave < kHeaderSize bytes in a block.
|
||||
assert(kBlockSize - block_offset_ >= kHeaderSize);
|
||||
assert(static_cast<int>(kBlockSize) - block_offset_ >= kHeaderSize);
|
||||
|
||||
const size_t avail = kBlockSize - block_offset_ - kHeaderSize;
|
||||
const size_t fragment_length = (left < avail) ? left : avail;
|
||||
|
||||
@@ -22,6 +22,40 @@ using std::unique_ptr;
|
||||
|
||||
namespace log {
|
||||
|
||||
/**
|
||||
* Writer is a general purpose log stream writer. It provides an append-only
|
||||
* abstraction for writing data. The details of the how the data is written is
|
||||
* handled by the WriteableFile sub-class implementation.
|
||||
*
|
||||
* File format:
|
||||
*
|
||||
* File is broken down into variable sized records. The format of each record
|
||||
* is described below.
|
||||
* +-----+-------------+--+----+----------+------+-- ... ----+
|
||||
* File | r0 | r1 |P | r2 | r3 | r4 | |
|
||||
* +-----+-------------+--+----+----------+------+-- ... ----+
|
||||
* <--- kBlockSize ------>|<-- kBlockSize ------>|
|
||||
* rn = variable size records
|
||||
* P = Padding
|
||||
*
|
||||
* Data is written out in kBlockSize chunks. If next record does not fit
|
||||
* into the space left, the leftover space will be padded with \0.
|
||||
*
|
||||
* Record format:
|
||||
*
|
||||
* +---------+-----------+-----------+--- ... ---+
|
||||
* |CRC (4B) | Size (2B) | Type (1B) | Payload |
|
||||
* +---------+-----------+-----------+--- ... ---+
|
||||
*
|
||||
* CRC = 32bit hash computed over the payload using CRC
|
||||
* Size = Length of the payload data
|
||||
* Type = Type of record
|
||||
* (kZeroType, kFullType, kFirstType, kLastType, kMiddleType )
|
||||
* The type is used to group a bunch of records together to represent
|
||||
* blocks that are larger than kBlockSize
|
||||
* Payload = Byte stream as long as specified by the payload size
|
||||
*
|
||||
*/
|
||||
class Writer {
|
||||
public:
|
||||
// Create a writer that will append data to "*dest".
|
||||
|
||||
@@ -81,4 +81,4 @@ class ManagedIterator : public Iterator {
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
#endif // !ROCKSDB_LITE
|
||||
+48
-4
@@ -54,6 +54,19 @@ struct MemTableOptions {
|
||||
Logger* info_log;
|
||||
};
|
||||
|
||||
// Note: Many of the methods in this class have comments indicating that
|
||||
// external synchromization is required as these methods are not thread-safe.
|
||||
// It is up to higher layers of code to decide how to prevent concurrent
|
||||
// invokation of these methods. This is usually done by acquiring either
|
||||
// the db mutex or the single writer thread.
|
||||
//
|
||||
// Some of these methods are documented to only require external
|
||||
// synchronization if this memtable is immutable. Calling MarkImmutable() is
|
||||
// not sufficient to guarantee immutability. It is up to higher layers of
|
||||
// code to determine if this MemTable can still be modified by other threads.
|
||||
// Eg: The Superversion stores a pointer to the current MemTable (that can
|
||||
// be modified) and a separate list of the MemTables that can no longer be
|
||||
// written to (aka the 'immutable memtables').
|
||||
class MemTable {
|
||||
public:
|
||||
struct KeyComparator : public MemTableRep::KeyComparator {
|
||||
@@ -72,13 +85,18 @@ class MemTable {
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
WriteBuffer* write_buffer);
|
||||
|
||||
// Do not delete this MemTable unless Unref() indicates it not in use.
|
||||
~MemTable();
|
||||
|
||||
// Increase reference count.
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
void Ref() { ++refs_; }
|
||||
|
||||
// Drop reference count.
|
||||
// If the refcount goes to zero return this memtable, otherwise return null
|
||||
// If the refcount goes to zero return this memtable, otherwise return null.
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
MemTable* Unref() {
|
||||
--refs_;
|
||||
assert(refs_ >= 0);
|
||||
@@ -92,7 +110,7 @@ class MemTable {
|
||||
// data structure.
|
||||
//
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
// operations on the same MemTable (unless this Memtable is immutable).
|
||||
size_t ApproximateMemoryUsage();
|
||||
|
||||
// This method heuristically determines if the memtable should continue to
|
||||
@@ -120,6 +138,9 @@ class MemTable {
|
||||
// Add an entry into memtable that maps key to value at the
|
||||
// specified sequence number and with the specified type.
|
||||
// Typically value will be empty if type==kTypeDeletion.
|
||||
//
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
void Add(SequenceNumber seq, ValueType type,
|
||||
const Slice& key,
|
||||
const Slice& value);
|
||||
@@ -142,6 +163,9 @@ class MemTable {
|
||||
// update inplace
|
||||
// else add(key, new_value)
|
||||
// else add(key, new_value)
|
||||
//
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
void Update(SequenceNumber seq,
|
||||
const Slice& key,
|
||||
const Slice& value);
|
||||
@@ -155,6 +179,9 @@ class MemTable {
|
||||
// update inplace
|
||||
// else add(key, new_value)
|
||||
// else return false
|
||||
//
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
bool UpdateCallback(SequenceNumber seq,
|
||||
const Slice& key,
|
||||
const Slice& delta);
|
||||
@@ -165,29 +192,46 @@ class MemTable {
|
||||
size_t CountSuccessiveMergeEntries(const LookupKey& key);
|
||||
|
||||
// Get total number of entries in the mem table.
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable (unless this Memtable is immutable).
|
||||
uint64_t num_entries() const { return num_entries_; }
|
||||
|
||||
// Get total number of deletes in the mem table.
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable (unless this Memtable is immutable).
|
||||
uint64_t num_deletes() const { return num_deletes_; }
|
||||
|
||||
// Returns the edits area that is needed for flushing the memtable
|
||||
VersionEdit* GetEdits() { return &edit_; }
|
||||
|
||||
// Returns if there is no entry inserted to the mem table.
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable (unless this Memtable is immutable).
|
||||
bool IsEmpty() const { return first_seqno_ == 0; }
|
||||
|
||||
// Returns the sequence number of the first element that was inserted
|
||||
// into the memtable
|
||||
// into the memtable.
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable (unless this Memtable is immutable).
|
||||
SequenceNumber GetFirstSequenceNumber() { return first_seqno_; }
|
||||
|
||||
// Returns the next active logfile number when this memtable is about to
|
||||
// be flushed to storage
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
uint64_t GetNextLogNumber() { return mem_next_logfile_number_; }
|
||||
|
||||
// Sets the next active logfile number when this memtable is about to
|
||||
// be flushed to storage
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
void SetNextLogNumber(uint64_t num) { mem_next_logfile_number_ = num; }
|
||||
|
||||
// Notify the underlying storage that no more items will be added
|
||||
// Notify the underlying storage that no more items will be added.
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
// After MarkImmutable() is called, you should not attempt to
|
||||
// write anything to this MemTable(). (Ie. do not call Add() or Update()).
|
||||
void MarkImmutable() {
|
||||
table_->MarkReadOnly();
|
||||
allocator_.DoneAllocating();
|
||||
|
||||
+3
-3
@@ -161,7 +161,7 @@ void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
assert(!mems.empty());
|
||||
|
||||
// If the flush was not successful, then just reset state.
|
||||
// Maybe a suceeding attempt to flush will be successful.
|
||||
// Maybe a succeeding attempt to flush will be successful.
|
||||
for (MemTable* m : mems) {
|
||||
assert(m->flush_in_progress_);
|
||||
assert(m->file_number_ == 0);
|
||||
@@ -184,7 +184,7 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
|
||||
// flush was sucessful
|
||||
// flush was successful
|
||||
for (size_t i = 0; i < mems.size(); ++i) {
|
||||
// All the edits are associated with the first memtable of this batch.
|
||||
assert(i == 0 || mems[i]->GetEdits()->NumEntries() == 0);
|
||||
@@ -193,7 +193,7 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
mems[i]->file_number_ = file_number;
|
||||
}
|
||||
|
||||
// if some other thread is already commiting, then return
|
||||
// if some other thread is already committing, then return
|
||||
Status s;
|
||||
if (commit_in_progress_) {
|
||||
return s;
|
||||
|
||||
+16
-1
@@ -35,6 +35,9 @@ class MergeIteratorBuilder;
|
||||
// keeps a list of immutable memtables in a vector. the list is immutable
|
||||
// if refcount is bigger than one. It is used as a state for Get() and
|
||||
// Iterator code paths
|
||||
//
|
||||
// This class is not thread-safe. External synchronization is required
|
||||
// (such as holding the db mutex or being on the write thread).
|
||||
class MemTableListVersion {
|
||||
public:
|
||||
explicit MemTableListVersion(MemTableListVersion* old = nullptr);
|
||||
@@ -77,6 +80,11 @@ class MemTableListVersion {
|
||||
// flushes can occur concurrently. However, they are 'committed'
|
||||
// to the manifest in FIFO order to maintain correctness and
|
||||
// recoverability from a crash.
|
||||
//
|
||||
//
|
||||
// Other than imm_flush_needed, this class is not thread-safe and requires
|
||||
// external synchronization (such as holding the db mutex or being on the
|
||||
// write thread.)
|
||||
class MemTableList {
|
||||
public:
|
||||
// A list of memtables.
|
||||
@@ -89,6 +97,9 @@ class MemTableList {
|
||||
flush_requested_(false) {
|
||||
current_->Ref();
|
||||
}
|
||||
|
||||
// Should not delete MemTableList without making sure MemTableList::current()
|
||||
// is Unref()'d.
|
||||
~MemTableList() {}
|
||||
|
||||
MemTableListVersion* current() { return current_; }
|
||||
@@ -127,7 +138,11 @@ class MemTableList {
|
||||
// Returns an estimate of the number of bytes of data in use.
|
||||
size_t ApproximateMemoryUsage();
|
||||
|
||||
// Request a flush of all existing memtables to storage
|
||||
// Request a flush of all existing memtables to storage. This will
|
||||
// cause future calls to IsFlushPending() to return true if this list is
|
||||
// non-empty (regardless of the min_write_buffer_number_to_merge
|
||||
// parameter). This flush request will persist until the next time
|
||||
// PickMemtablesToFlush() is called.
|
||||
void FlushRequested() { flush_requested_ = true; }
|
||||
|
||||
// Copying allowed
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DumbLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* format, va_list ap) override {}
|
||||
virtual size_t GetLogFileSize() const override { return 0; }
|
||||
};
|
||||
|
||||
class MemTableListTest : public testing::Test {
|
||||
public:
|
||||
std::string dbname;
|
||||
DB* db;
|
||||
Options options;
|
||||
|
||||
MemTableListTest() : db(nullptr) {
|
||||
dbname = test::TmpDir() + "/memtable_list_test";
|
||||
}
|
||||
|
||||
// Create a test db if not yet created
|
||||
void CreateDB() {
|
||||
if (db == nullptr) {
|
||||
options.create_if_missing = true;
|
||||
DestroyDB(dbname, options);
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
EXPECT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
~MemTableListTest() {
|
||||
if (db) {
|
||||
delete db;
|
||||
DestroyDB(dbname, options);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls MemTableList::InstallMemtableFlushResults() and sets up all
|
||||
// structures needed to call this function.
|
||||
Status Mock_InstallMemtableFlushResults(
|
||||
MemTableList* list, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& m, autovector<MemTable*>* to_delete) {
|
||||
// Create a mock Logger
|
||||
DumbLogger logger;
|
||||
LogBuffer log_buffer(DEBUG_LEVEL, &logger);
|
||||
|
||||
// Create a mock VersionSet
|
||||
DBOptions db_options;
|
||||
EnvOptions env_options;
|
||||
shared_ptr<Cache> table_cache(NewLRUCache(50000, 16));
|
||||
WriteBuffer write_buffer(db_options.db_write_buffer_size);
|
||||
WriteController write_controller;
|
||||
|
||||
CreateDB();
|
||||
VersionSet versions(dbname, &db_options, env_options, table_cache.get(),
|
||||
&write_buffer, &write_controller);
|
||||
|
||||
// Create mock default ColumnFamilyData
|
||||
ColumnFamilyOptions cf_options;
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
EXPECT_OK(versions.Recover(column_families, false));
|
||||
|
||||
auto column_family_set = versions.GetColumnFamilySet();
|
||||
auto cfd = column_family_set->GetColumnFamily(0);
|
||||
EXPECT_TRUE(cfd != nullptr);
|
||||
|
||||
// Create dummy mutex.
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
|
||||
return list->InstallMemtableFlushResults(cfd, mutable_cf_options, m,
|
||||
&versions, &mutex, 1, to_delete,
|
||||
nullptr, &log_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MemTableListTest, Empty) {
|
||||
// Create an empty MemTableList and validate basic functions.
|
||||
MemTableList list(1);
|
||||
|
||||
ASSERT_EQ(0, list.size());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
|
||||
autovector<MemTable*> mems;
|
||||
list.PickMemtablesToFlush(&mems);
|
||||
ASSERT_EQ(0, mems.size());
|
||||
|
||||
autovector<MemTable*> to_delete;
|
||||
list.current()->Unref(&to_delete);
|
||||
ASSERT_EQ(0, to_delete.size());
|
||||
}
|
||||
|
||||
TEST_F(MemTableListTest, GetTest) {
|
||||
// Create MemTableList
|
||||
int min_write_buffer_number_to_merge = 2;
|
||||
MemTableList list(min_write_buffer_number_to_merge);
|
||||
|
||||
SequenceNumber seq = 1;
|
||||
std::string value;
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
|
||||
LookupKey lkey("key1", seq);
|
||||
bool found = list.current()->Get(lkey, &value, &s, &merge_context);
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
// Create a MemTable
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
auto factory = std::make_shared<SkipListFactory>();
|
||||
options.memtable_factory = factory;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
|
||||
WriteBuffer wb(options.db_write_buffer_size);
|
||||
MemTable* mem =
|
||||
new MemTable(cmp, ioptions, MutableCFOptions(options, ioptions), &wb);
|
||||
mem->Ref();
|
||||
|
||||
// Write some keys to this memtable.
|
||||
mem->Add(++seq, kTypeDeletion, "key1", "");
|
||||
mem->Add(++seq, kTypeValue, "key2", "value2");
|
||||
mem->Add(++seq, kTypeValue, "key1", "value1");
|
||||
mem->Add(++seq, kTypeValue, "key2", "value2.2");
|
||||
|
||||
// Fetch the newly written keys
|
||||
merge_context.Clear();
|
||||
found = mem->Get(LookupKey("key1", seq), &value, &s, &merge_context);
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ(value, "value1");
|
||||
|
||||
merge_context.Clear();
|
||||
found = mem->Get(LookupKey("key1", 2), &value, &s, &merge_context);
|
||||
// MemTable found out that this key is *not* found (at this sequence#)
|
||||
ASSERT_TRUE(found && s.IsNotFound());
|
||||
|
||||
merge_context.Clear();
|
||||
found = mem->Get(LookupKey("key2", seq), &value, &s, &merge_context);
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ(value, "value2.2");
|
||||
|
||||
ASSERT_EQ(4, mem->num_entries());
|
||||
ASSERT_EQ(1, mem->num_deletes());
|
||||
|
||||
// Add memtable to list
|
||||
list.Add(mem);
|
||||
|
||||
SequenceNumber saved_seq = seq;
|
||||
|
||||
// Create another memtable and write some keys to it
|
||||
WriteBuffer wb2(options.db_write_buffer_size);
|
||||
MemTable* mem2 =
|
||||
new MemTable(cmp, ioptions, MutableCFOptions(options, ioptions), &wb2);
|
||||
mem2->Ref();
|
||||
|
||||
mem2->Add(++seq, kTypeDeletion, "key1", "");
|
||||
mem2->Add(++seq, kTypeValue, "key2", "value2.3");
|
||||
|
||||
// Add second memtable to list
|
||||
list.Add(mem2);
|
||||
|
||||
// Fetch keys via MemTableList
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key1", seq), &value, &s, &merge_context);
|
||||
ASSERT_TRUE(found && s.IsNotFound());
|
||||
|
||||
merge_context.Clear();
|
||||
found = list.current()->Get(LookupKey("key1", saved_seq), &value, &s,
|
||||
&merge_context);
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ("value1", value);
|
||||
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key2", seq), &value, &s, &merge_context);
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ(value, "value2.3");
|
||||
|
||||
merge_context.Clear();
|
||||
found = list.current()->Get(LookupKey("key2", 1), &value, &s, &merge_context);
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
ASSERT_EQ(2, list.size());
|
||||
|
||||
autovector<MemTable*> to_delete;
|
||||
list.current()->Unref(&to_delete);
|
||||
for (MemTable* m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
const int num_tables = 5;
|
||||
SequenceNumber seq = 1;
|
||||
Status s;
|
||||
|
||||
auto factory = std::make_shared<SkipListFactory>();
|
||||
options.memtable_factory = factory;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
WriteBuffer wb(options.db_write_buffer_size);
|
||||
|
||||
// Create MemTableList
|
||||
int min_write_buffer_number_to_merge = 3;
|
||||
MemTableList list(min_write_buffer_number_to_merge);
|
||||
|
||||
// Create some MemTables
|
||||
std::vector<MemTable*> tables;
|
||||
MutableCFOptions mutable_cf_options(options, ioptions);
|
||||
for (int i = 0; i < num_tables; i++) {
|
||||
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb);
|
||||
mem->Ref();
|
||||
|
||||
std::string value;
|
||||
MergeContext merge_context;
|
||||
|
||||
mem->Add(++seq, kTypeValue, "key1", std::to_string(i));
|
||||
mem->Add(++seq, kTypeValue, "keyN" + std::to_string(i), "valueN");
|
||||
mem->Add(++seq, kTypeValue, "keyX" + std::to_string(i), "value");
|
||||
mem->Add(++seq, kTypeValue, "keyM" + std::to_string(i), "valueM");
|
||||
mem->Add(++seq, kTypeDeletion, "keyX" + std::to_string(i), "");
|
||||
|
||||
tables.push_back(mem);
|
||||
}
|
||||
|
||||
// Nothing to flush
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
autovector<MemTable*> to_flush;
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(0, to_flush.size());
|
||||
|
||||
// Request a flush even though there is nothing to flush
|
||||
list.FlushRequested();
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Attempt to 'flush' to clear request for flush
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(0, to_flush.size());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Request a flush again
|
||||
list.FlushRequested();
|
||||
// No flush pending since the list is empty.
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Add 2 tables
|
||||
list.Add(tables[0]);
|
||||
list.Add(tables[1]);
|
||||
ASSERT_EQ(2, list.size());
|
||||
|
||||
// Even though we have less than the minimum to flush, a flush is
|
||||
// pending since we had previously requested a flush and never called
|
||||
// PickMemtablesToFlush() to clear the flush.
|
||||
ASSERT_TRUE(list.IsFlushPending());
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Pick tables to flush
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(2, to_flush.size());
|
||||
ASSERT_EQ(2, list.size());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Revert flush
|
||||
list.RollbackMemtableFlush(to_flush, 0);
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
to_flush.clear();
|
||||
|
||||
// Add another table
|
||||
list.Add(tables[2]);
|
||||
// We now have the minimum to flush regardles of whether FlushRequested()
|
||||
// was called.
|
||||
ASSERT_TRUE(list.IsFlushPending());
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Pick tables to flush
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(3, to_flush.size());
|
||||
ASSERT_EQ(3, list.size());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Pick tables to flush again
|
||||
autovector<MemTable*> to_flush2;
|
||||
list.PickMemtablesToFlush(&to_flush2);
|
||||
ASSERT_EQ(0, to_flush2.size());
|
||||
ASSERT_EQ(3, list.size());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Add another table
|
||||
list.Add(tables[3]);
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Request a flush again
|
||||
list.FlushRequested();
|
||||
ASSERT_TRUE(list.IsFlushPending());
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Pick tables to flush again
|
||||
list.PickMemtablesToFlush(&to_flush2);
|
||||
ASSERT_EQ(1, to_flush2.size());
|
||||
ASSERT_EQ(4, list.size());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Rollback first pick of tables
|
||||
list.RollbackMemtableFlush(to_flush, 0);
|
||||
ASSERT_TRUE(list.IsFlushPending());
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
to_flush.clear();
|
||||
|
||||
// Add another tables
|
||||
list.Add(tables[4]);
|
||||
ASSERT_EQ(5, list.size());
|
||||
// We now have the minimum to flush regardles of whether FlushRequested()
|
||||
ASSERT_TRUE(list.IsFlushPending());
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Pick tables to flush
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
// Should pick 4 of 5 since 1 table has been picked in to_flush2
|
||||
ASSERT_EQ(4, to_flush.size());
|
||||
ASSERT_EQ(5, list.size());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Pick tables to flush again
|
||||
autovector<MemTable*> to_flush3;
|
||||
ASSERT_EQ(0, to_flush3.size()); // nothing not in progress of being flushed
|
||||
ASSERT_EQ(5, list.size());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
autovector<MemTable*> to_delete;
|
||||
|
||||
// Flush the 4 memtables that were picked in to_flush
|
||||
s = Mock_InstallMemtableFlushResults(
|
||||
&list, MutableCFOptions(options, ioptions), to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Note: now to_flush contains tables[0,1,2,4]. to_flush2 contains
|
||||
// tables[3].
|
||||
// Current implementation will only commit memtables in the order they were
|
||||
// created. So InstallMemtableFlushResults will install the first 3 tables
|
||||
// in to_flush and stop when it encounters a table not yet flushed.
|
||||
ASSERT_EQ(3, to_delete.size());
|
||||
ASSERT_EQ(2, list.size());
|
||||
|
||||
for (const auto& m : to_delete) {
|
||||
// Refcount should be 0 after calling InstallMemtableFlushResults.
|
||||
// Verify this, by Ref'ing then UnRef'ing:
|
||||
m->Ref();
|
||||
ASSERT_EQ(m, m->Unref());
|
||||
delete m;
|
||||
}
|
||||
to_delete.clear();
|
||||
|
||||
// Request a flush again. Should be nothing to flush
|
||||
list.FlushRequested();
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Flush the 1 memtable that was picked in to_flush2
|
||||
s = MemTableListTest::Mock_InstallMemtableFlushResults(
|
||||
&list, MutableCFOptions(options, ioptions), to_flush2, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// This will actually intall 2 tables. The 1 we told it to flush, and also
|
||||
// tables[4] which has been waiting for tables[3] to commit.
|
||||
ASSERT_EQ(2, to_delete.size());
|
||||
ASSERT_EQ(0, list.size());
|
||||
|
||||
for (const auto& m : to_delete) {
|
||||
// Refcount should be 0 after calling InstallMemtableFlushResults.
|
||||
// Verify this, by Ref'ing then UnRef'ing:
|
||||
m->Ref();
|
||||
ASSERT_EQ(m, m->Unref());
|
||||
delete m;
|
||||
}
|
||||
to_delete.clear();
|
||||
|
||||
list.current()->Unref(&to_delete);
|
||||
ASSERT_EQ(0, to_delete.size());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -132,8 +132,6 @@ DEFINE_int64(seed, 0,
|
||||
"Seed base for random number generators. "
|
||||
"When 0 it is deterministic.");
|
||||
|
||||
static rocksdb::Env* FLAGS_env = rocksdb::Env::Default();
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -66,4 +66,3 @@ private:
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+45
-22
@@ -16,6 +16,40 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// TODO(agiardullo): Clean up merge callsites to use this func
|
||||
Status MergeHelper::TimedFullMerge(const Slice& key, const Slice* value,
|
||||
const std::deque<std::string>& operands,
|
||||
const MergeOperator* merge_operator,
|
||||
Statistics* statistics, Env* env,
|
||||
Logger* logger, std::string* result) {
|
||||
if (operands.size() == 0) {
|
||||
result->assign(value->data(), value->size());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
if (merge_operator == nullptr) {
|
||||
return Status::NotSupported("Provide a merge_operator when opening DB");
|
||||
}
|
||||
|
||||
// Setup to time the merge
|
||||
StopWatchNano timer(env, statistics != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
|
||||
// Do the merge
|
||||
bool success =
|
||||
merge_operator->FullMerge(key, value, operands, result, logger);
|
||||
|
||||
RecordTick(statistics, MERGE_OPERATION_TOTAL_TIME,
|
||||
env != nullptr ? timer.ElapsedNanos() : 0);
|
||||
|
||||
if (!success) {
|
||||
RecordTick(statistics, NUMBER_MERGE_FAILURES);
|
||||
return Status::Corruption("Error: Could not perform merge.");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// PRE: iter points to the first merge type entry
|
||||
// POST: iter points to the first entry beyond the merge process (or the end)
|
||||
// keys_, operands_ are updated to reflect the merge result.
|
||||
@@ -81,25 +115,20 @@ void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
// => store result in operands_.back() (and update keys_.back())
|
||||
// => change the entry type to kTypeValue for keys_.back()
|
||||
// We are done! Return a success if the merge passes.
|
||||
{
|
||||
StopWatchNano timer(env_, stats != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
success_ = user_merge_operator_->FullMerge(
|
||||
ikey.user_key, nullptr, operands_, &merge_result, logger_);
|
||||
RecordTick(stats, MERGE_OPERATION_TOTAL_TIME,
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0);
|
||||
}
|
||||
|
||||
Status s = TimedFullMerge(ikey.user_key, nullptr, operands_,
|
||||
user_merge_operator_, stats, env_, logger_,
|
||||
&merge_result);
|
||||
|
||||
// We store the result in keys_.back() and operands_.back()
|
||||
// if nothing went wrong (i.e.: no operand corruption on disk)
|
||||
if (success_) {
|
||||
if (s.ok()) {
|
||||
std::string& original_key =
|
||||
keys_.back(); // The original key encountered
|
||||
orig_ikey.type = kTypeValue;
|
||||
UpdateInternalKey(&original_key[0], original_key.size(),
|
||||
orig_ikey.sequence, orig_ikey.type);
|
||||
swap(operands_.back(), merge_result);
|
||||
} else {
|
||||
RecordTick(stats, NUMBER_MERGE_FAILURES);
|
||||
}
|
||||
|
||||
// move iter to the next entry (before doing anything else)
|
||||
@@ -117,25 +146,19 @@ void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
// => change the entry type to kTypeValue for keys_.back()
|
||||
// We are done! Success!
|
||||
const Slice val = iter->value();
|
||||
{
|
||||
StopWatchNano timer(env_, stats != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
success_ = user_merge_operator_->FullMerge(
|
||||
ikey.user_key, &val, operands_, &merge_result, logger_);
|
||||
RecordTick(stats, MERGE_OPERATION_TOTAL_TIME,
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0);
|
||||
}
|
||||
Status s =
|
||||
TimedFullMerge(ikey.user_key, &val, operands_, user_merge_operator_,
|
||||
stats, env_, logger_, &merge_result);
|
||||
|
||||
// We store the result in keys_.back() and operands_.back()
|
||||
// if nothing went wrong (i.e.: no operand corruption on disk)
|
||||
if (success_) {
|
||||
if (s.ok()) {
|
||||
std::string& original_key =
|
||||
keys_.back(); // The original key encountered
|
||||
orig_ikey.type = kTypeValue;
|
||||
UpdateInternalKey(&original_key[0], original_key.size(),
|
||||
orig_ikey.sequence, orig_ikey.type);
|
||||
swap(operands_.back(), merge_result);
|
||||
} else {
|
||||
RecordTick(stats, NUMBER_MERGE_FAILURES);
|
||||
}
|
||||
|
||||
// move iter to the next entry
|
||||
|
||||
@@ -35,6 +35,15 @@ class MergeHelper {
|
||||
operands_(),
|
||||
success_(false) {}
|
||||
|
||||
// Wrapper around MergeOperator::FullMerge() 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.
|
||||
static Status TimedFullMerge(const Slice& key, const Slice* value,
|
||||
const std::deque<std::string>& operands,
|
||||
const MergeOperator* merge_operator,
|
||||
Statistics* statistics, Env* env, Logger* logger,
|
||||
std::string* result);
|
||||
|
||||
// Merge entries until we hit
|
||||
// - a corrupted key
|
||||
// - a Put/Delete,
|
||||
|
||||
+112
-81
@@ -24,6 +24,7 @@ int FLAGS_total_keys = 100;
|
||||
int FLAGS_write_buffer_size = 1000000000;
|
||||
int FLAGS_max_write_buffer_number = 8;
|
||||
int FLAGS_min_write_buffer_number_to_merge = 7;
|
||||
bool FLAGS_verbose = false;
|
||||
|
||||
// Path to the database on file system
|
||||
const std::string kDbName = rocksdb::test::TmpDir() + "/perf_context_test";
|
||||
@@ -43,9 +44,6 @@ std::shared_ptr<DB> OpenDb(bool read_only = false) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
options.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(0));
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory());
|
||||
#else
|
||||
fprintf(stderr, "Prefix hash is not supported in lite mode\n");
|
||||
exit(1);
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -95,8 +93,10 @@ TEST_F(PerfContextTest, SeekIntoDeletion) {
|
||||
hist_get_time.Add(elapsed_nanos);
|
||||
}
|
||||
|
||||
std::cout << "Get user key comparison: \n" << hist_get.ToString()
|
||||
<< "Get time: \n" << hist_get_time.ToString();
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "Get user key comparison: \n" << hist_get.ToString()
|
||||
<< "Get time: \n" << hist_get_time.ToString();
|
||||
}
|
||||
|
||||
{
|
||||
HistogramImpl hist_seek_to_first;
|
||||
@@ -108,13 +108,15 @@ TEST_F(PerfContextTest, SeekIntoDeletion) {
|
||||
hist_seek_to_first.Add(perf_context.user_key_comparison_count);
|
||||
auto elapsed_nanos = timer.ElapsedNanos();
|
||||
|
||||
std::cout << "SeekToFirst uesr key comparison: \n"
|
||||
<< hist_seek_to_first.ToString()
|
||||
<< "ikey skipped: " << perf_context.internal_key_skipped_count
|
||||
<< "\n"
|
||||
<< "idelete skipped: "
|
||||
<< perf_context.internal_delete_skipped_count << "\n"
|
||||
<< "elapsed: " << elapsed_nanos << "\n";
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "SeekToFirst uesr key comparison: \n"
|
||||
<< hist_seek_to_first.ToString()
|
||||
<< "ikey skipped: " << perf_context.internal_key_skipped_count
|
||||
<< "\n"
|
||||
<< "idelete skipped: "
|
||||
<< perf_context.internal_delete_skipped_count << "\n"
|
||||
<< "elapsed: " << elapsed_nanos << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
HistogramImpl hist_seek;
|
||||
@@ -127,21 +129,28 @@ TEST_F(PerfContextTest, SeekIntoDeletion) {
|
||||
iter->Seek(key);
|
||||
auto elapsed_nanos = timer.ElapsedNanos();
|
||||
hist_seek.Add(perf_context.user_key_comparison_count);
|
||||
std::cout << "seek cmp: " << perf_context.user_key_comparison_count
|
||||
<< " ikey skipped " << perf_context.internal_key_skipped_count
|
||||
<< " idelete skipped " << perf_context.internal_delete_skipped_count
|
||||
<< " elapsed: " << elapsed_nanos << "ns\n";
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "seek cmp: " << perf_context.user_key_comparison_count
|
||||
<< " ikey skipped " << perf_context.internal_key_skipped_count
|
||||
<< " idelete skipped "
|
||||
<< perf_context.internal_delete_skipped_count
|
||||
<< " elapsed: " << elapsed_nanos << "ns\n";
|
||||
}
|
||||
|
||||
perf_context.Reset();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
StopWatchNano timer2(Env::Default(), true);
|
||||
iter->Next();
|
||||
auto elapsed_nanos2 = timer2.ElapsedNanos();
|
||||
std::cout << "next cmp: " << perf_context.user_key_comparison_count
|
||||
<< "elapsed: " << elapsed_nanos2 << "ns\n";
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "next cmp: " << perf_context.user_key_comparison_count
|
||||
<< "elapsed: " << elapsed_nanos2 << "ns\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Seek uesr key comparison: \n" << hist_seek.ToString();
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "Seek uesr key comparison: \n" << hist_seek.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PerfContextTest, StopWatchNanoOverhead) {
|
||||
@@ -159,7 +168,9 @@ TEST_F(PerfContextTest, StopWatchNanoOverhead) {
|
||||
histogram.Add(timing);
|
||||
}
|
||||
|
||||
std::cout << histogram.ToString();
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << histogram.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PerfContextTest, StopWatchOverhead) {
|
||||
@@ -180,7 +191,9 @@ TEST_F(PerfContextTest, StopWatchOverhead) {
|
||||
prev_timing = timing;
|
||||
}
|
||||
|
||||
std::cout << histogram.ToString();
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << histogram.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileQueries(bool enabled_time = false) {
|
||||
@@ -213,7 +226,9 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
|
||||
uint64_t total_db_mutex_nanos = 0;
|
||||
|
||||
std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";
|
||||
}
|
||||
|
||||
std::vector<int> keys;
|
||||
const int kFlushFlag = -1;
|
||||
@@ -287,37 +302,39 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
hist_mget.Add(perf_context.user_key_comparison_count);
|
||||
}
|
||||
|
||||
std::cout << "Put uesr key comparison: \n" << hist_put.ToString()
|
||||
<< "Get uesr key comparison: \n" << hist_get.ToString()
|
||||
<< "MultiGet uesr key comparison: \n" << hist_get.ToString();
|
||||
std::cout << "Put(): Pre and Post Process Time: \n"
|
||||
<< hist_write_pre_post.ToString()
|
||||
<< " Writing WAL time: \n"
|
||||
<< hist_write_wal_time.ToString() << "\n"
|
||||
<< " Writing Mem Table time: \n"
|
||||
<< hist_write_memtable_time.ToString() << "\n"
|
||||
<< " Total DB mutex nanos: \n" << total_db_mutex_nanos << "\n";
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "Put uesr key comparison: \n" << hist_put.ToString()
|
||||
<< "Get uesr key comparison: \n" << hist_get.ToString()
|
||||
<< "MultiGet uesr key comparison: \n" << hist_get.ToString();
|
||||
std::cout << "Put(): Pre and Post Process Time: \n"
|
||||
<< hist_write_pre_post.ToString() << " Writing WAL time: \n"
|
||||
<< hist_write_wal_time.ToString() << "\n"
|
||||
<< " Writing Mem Table time: \n"
|
||||
<< hist_write_memtable_time.ToString() << "\n"
|
||||
<< " Total DB mutex nanos: \n" << total_db_mutex_nanos << "\n";
|
||||
|
||||
std::cout << "Get(): Time to get snapshot: \n" << hist_get_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
<< hist_get_memtable.ToString() << "\n"
|
||||
<< " Time to get value from output files: \n"
|
||||
<< hist_get_files.ToString() << "\n"
|
||||
<< " Number of memtables checked: \n"
|
||||
<< hist_num_memtable_checked.ToString() << "\n"
|
||||
<< " Time to post process: \n" << hist_get_post_process.ToString()
|
||||
<< "\n";
|
||||
std::cout << "Get(): Time to get snapshot: \n"
|
||||
<< hist_get_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
<< hist_get_memtable.ToString() << "\n"
|
||||
<< " Time to get value from output files: \n"
|
||||
<< hist_get_files.ToString() << "\n"
|
||||
<< " Number of memtables checked: \n"
|
||||
<< hist_num_memtable_checked.ToString() << "\n"
|
||||
<< " Time to post process: \n" << hist_get_post_process.ToString()
|
||||
<< "\n";
|
||||
|
||||
std::cout << "MultiGet(): Time to get snapshot: \n"
|
||||
<< hist_mget_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
<< hist_mget_memtable.ToString() << "\n"
|
||||
<< " Time to get value from output files: \n"
|
||||
<< hist_mget_files.ToString() << "\n"
|
||||
<< " Number of memtables checked: \n"
|
||||
<< hist_mget_num_memtable_checked.ToString() << "\n"
|
||||
<< " Time to post process: \n" << hist_mget_post_process.ToString()
|
||||
<< "\n";
|
||||
std::cout << "MultiGet(): Time to get snapshot: \n"
|
||||
<< hist_mget_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
<< hist_mget_memtable.ToString() << "\n"
|
||||
<< " Time to get value from output files: \n"
|
||||
<< hist_mget_files.ToString() << "\n"
|
||||
<< " Number of memtables checked: \n"
|
||||
<< hist_mget_num_memtable_checked.ToString() << "\n"
|
||||
<< " Time to post process: \n"
|
||||
<< hist_mget_post_process.ToString() << "\n";
|
||||
}
|
||||
|
||||
if (enabled_time) {
|
||||
ASSERT_GT(hist_get.Average(), 0);
|
||||
@@ -381,31 +398,33 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
hist_mget.Add(perf_context.user_key_comparison_count);
|
||||
}
|
||||
|
||||
std::cout << "ReadOnly Get uesr key comparison: \n" << hist_get.ToString()
|
||||
<< "ReadOnly MultiGet uesr key comparison: \n"
|
||||
<< hist_mget.ToString();
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "ReadOnly Get uesr key comparison: \n" << hist_get.ToString()
|
||||
<< "ReadOnly MultiGet uesr key comparison: \n"
|
||||
<< hist_mget.ToString();
|
||||
|
||||
std::cout << "ReadOnly Get(): Time to get snapshot: \n"
|
||||
<< hist_get_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
<< hist_get_memtable.ToString() << "\n"
|
||||
<< " Time to get value from output files: \n"
|
||||
<< hist_get_files.ToString() << "\n"
|
||||
<< " Number of memtables checked: \n"
|
||||
<< hist_num_memtable_checked.ToString() << "\n"
|
||||
<< " Time to post process: \n" << hist_get_post_process.ToString()
|
||||
<< "\n";
|
||||
std::cout << "ReadOnly Get(): Time to get snapshot: \n"
|
||||
<< hist_get_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
<< hist_get_memtable.ToString() << "\n"
|
||||
<< " Time to get value from output files: \n"
|
||||
<< hist_get_files.ToString() << "\n"
|
||||
<< " Number of memtables checked: \n"
|
||||
<< hist_num_memtable_checked.ToString() << "\n"
|
||||
<< " Time to post process: \n" << hist_get_post_process.ToString()
|
||||
<< "\n";
|
||||
|
||||
std::cout << "ReadOnly MultiGet(): Time to get snapshot: \n"
|
||||
<< hist_mget_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
<< hist_mget_memtable.ToString() << "\n"
|
||||
<< " Time to get value from output files: \n"
|
||||
<< hist_mget_files.ToString() << "\n"
|
||||
<< " Number of memtables checked: \n"
|
||||
<< hist_mget_num_memtable_checked.ToString() << "\n"
|
||||
<< " Time to post process: \n" << hist_mget_post_process.ToString()
|
||||
<< "\n";
|
||||
std::cout << "ReadOnly MultiGet(): Time to get snapshot: \n"
|
||||
<< hist_mget_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
<< hist_mget_memtable.ToString() << "\n"
|
||||
<< " Time to get value from output files: \n"
|
||||
<< hist_mget_files.ToString() << "\n"
|
||||
<< " Number of memtables checked: \n"
|
||||
<< hist_mget_num_memtable_checked.ToString() << "\n"
|
||||
<< " Time to post process: \n"
|
||||
<< hist_mget_post_process.ToString() << "\n";
|
||||
}
|
||||
|
||||
if (enabled_time) {
|
||||
ASSERT_GT(hist_get.Average(), 0);
|
||||
@@ -454,7 +473,9 @@ TEST_F(PerfContextTest, SeekKeyComparison) {
|
||||
WriteOptions write_options;
|
||||
ReadOptions read_options;
|
||||
|
||||
std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";
|
||||
}
|
||||
|
||||
std::vector<int> keys;
|
||||
for (int i = 0; i < FLAGS_total_keys; ++i) {
|
||||
@@ -484,9 +505,11 @@ TEST_F(PerfContextTest, SeekKeyComparison) {
|
||||
hist_time_diff.Add(put_time - perf_context.write_wal_time);
|
||||
}
|
||||
|
||||
std::cout << "Put time:\n" << hist_put_time.ToString()
|
||||
<< "WAL time:\n" << hist_wal_time.ToString()
|
||||
<< "time diff:\n" << hist_time_diff.ToString();
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "Put time:\n" << hist_put_time.ToString() << "WAL time:\n"
|
||||
<< hist_wal_time.ToString() << "time diff:\n"
|
||||
<< hist_time_diff.ToString();
|
||||
}
|
||||
|
||||
HistogramImpl hist_seek;
|
||||
HistogramImpl hist_next;
|
||||
@@ -510,8 +533,10 @@ TEST_F(PerfContextTest, SeekKeyComparison) {
|
||||
hist_next.Add(perf_context.user_key_comparison_count);
|
||||
}
|
||||
|
||||
std::cout << "Seek:\n" << hist_seek.ToString()
|
||||
<< "Next:\n" << hist_next.ToString();
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << "Seek:\n" << hist_seek.ToString() << "Next:\n"
|
||||
<< hist_next.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -541,9 +566,15 @@ int main(int argc, char** argv) {
|
||||
FLAGS_use_set_based_memetable = n;
|
||||
}
|
||||
|
||||
if (sscanf(argv[i], "--verbose=%d%c", &n, &junk) == 1 &&
|
||||
(n == 0 || n == 1)) {
|
||||
FLAGS_verbose = n;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << kDbName << "\n";
|
||||
if (FLAGS_verbose) {
|
||||
std::cout << kDbName << "\n";
|
||||
}
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+49
-15
@@ -7,24 +7,53 @@
|
||||
// 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.
|
||||
//
|
||||
// We recover the contents of the descriptor from the other files we find.
|
||||
// (1) Any log files are first converted to tables
|
||||
// (2) We scan every table to compute
|
||||
// (a) smallest/largest for the table
|
||||
// (b) largest sequence number in the table
|
||||
// (3) We generate descriptor contents:
|
||||
// - log number is set to zero
|
||||
// - next-file-number is set to 1 + largest file number we found
|
||||
// - last-sequence-number is set to largest sequence# found across
|
||||
// all tables (see 2c)
|
||||
// - compaction pointers are cleared
|
||||
// - every table file is added at level 0
|
||||
// Repairer does best effort recovery to recover as much data as possible after
|
||||
// a disaster without compromising consistency. It does not guarantee bringing
|
||||
// the database to a time consistent state.
|
||||
//
|
||||
// Repair process is broken into 4 phases:
|
||||
// (a) Find files
|
||||
// (b) Convert logs to tables
|
||||
// (c) Extract metadata
|
||||
// (d) Write Descriptor
|
||||
//
|
||||
// (a) Find files
|
||||
//
|
||||
// The repairer goes through all the files in the directory, and classifies them
|
||||
// based on their file name. Any file that cannot be identified by name will be
|
||||
// ignored.
|
||||
//
|
||||
// (b) Convert logs to table
|
||||
//
|
||||
// Every log file that is active is replayed. All sections of the file where the
|
||||
// checksum does not match is skipped over. We intentionally give preference to
|
||||
// data consistency.
|
||||
//
|
||||
// (c) Extract metadata
|
||||
//
|
||||
// We scan every table to compute
|
||||
// (1) smallest/largest for the table
|
||||
// (2) largest sequence number in the table
|
||||
//
|
||||
// If we are unable to scan the file, then we ignore the table.
|
||||
//
|
||||
// (d) Write Descriptor
|
||||
//
|
||||
// We generate descriptor contents:
|
||||
// - log number is set to zero
|
||||
// - next-file-number is set to 1 + largest file number we found
|
||||
// - last-sequence-number is set to largest sequence# found across
|
||||
// all tables (see 2c)
|
||||
// - compaction pointers are cleared
|
||||
// - every table file is added at level 0
|
||||
//
|
||||
// Possible optimization 1:
|
||||
// (a) Compute total size and use to pick appropriate max-level M
|
||||
// (b) Sort tables by largest sequence# in the table
|
||||
// (c) For each table: if it overlaps earlier table, place in level-0,
|
||||
// else place in level-M.
|
||||
// (d) We can provide options for time consistent recovery and unsafe recovery
|
||||
// (ignore checksum failure when applicable)
|
||||
// Possible optimization 2:
|
||||
// Store per-table metadata (smallest, largest, largest-seq#, ...)
|
||||
// in the table's meta section to speed up ScanTable.
|
||||
@@ -71,6 +100,8 @@ class Repairer {
|
||||
// once.
|
||||
NewLRUCache(10, options_.table_cache_numshardbits)),
|
||||
next_file_number_(1) {
|
||||
GetIntTblPropCollectorFactory(options, &int_tbl_prop_collector_factories_);
|
||||
|
||||
table_cache_ =
|
||||
new TableCache(ioptions_, env_options_, raw_table_cache_.get());
|
||||
edit_ = new VersionEdit();
|
||||
@@ -115,6 +146,8 @@ class Repairer {
|
||||
std::string const dbname_;
|
||||
Env* const env_;
|
||||
const InternalKeyComparator icmp_;
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
|
||||
int_tbl_prop_collector_factories_;
|
||||
const Options options_;
|
||||
const ImmutableCFOptions ioptions_;
|
||||
std::shared_ptr<Cache> raw_table_cache_;
|
||||
@@ -213,7 +246,7 @@ class Repairer {
|
||||
// corruptions cause entire commits to be skipped instead of
|
||||
// propagating bad information (like overly large sequence
|
||||
// numbers).
|
||||
log::Reader reader(std::move(lfile), &reporter, false/*do not checksum*/,
|
||||
log::Reader reader(std::move(lfile), &reporter, true /*enable checksum*/,
|
||||
0/*initial_offset*/);
|
||||
|
||||
// Read all the records and add to a memtable
|
||||
@@ -254,8 +287,9 @@ class Repairer {
|
||||
Arena arena;
|
||||
ScopedArenaIterator iter(mem->NewIterator(ro, &arena));
|
||||
status = BuildTable(dbname_, env_, ioptions_, env_options_, table_cache_,
|
||||
iter.get(), &meta, icmp_, 0, 0, kNoCompression,
|
||||
CompressionOptions());
|
||||
iter.get(), &meta, icmp_,
|
||||
&int_tbl_prop_collector_factories_, 0, 0,
|
||||
kNoCompression, CompressionOptions(), false);
|
||||
}
|
||||
delete mem->Unref();
|
||||
delete cf_mems_default;
|
||||
|
||||
+9
-3
@@ -8,6 +8,8 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -69,13 +71,17 @@ class SnapshotList {
|
||||
}
|
||||
|
||||
// retrieve all snapshot numbers. They are sorted in ascending order.
|
||||
void getAll(std::vector<SequenceNumber>& ret) {
|
||||
if (empty()) return;
|
||||
std::vector<SequenceNumber> GetAll() {
|
||||
std::vector<SequenceNumber> ret;
|
||||
if (empty()) {
|
||||
return ret;
|
||||
}
|
||||
SnapshotImpl* s = &list_;
|
||||
while (s->next_ != &list_) {
|
||||
ret.push_back(s->next_->number_);
|
||||
s = s ->next_;
|
||||
s = s->next_;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// get the sequence number of the most recent snapshot
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
Status InternalKeyPropertiesCollector::Add(
|
||||
const Slice& key, const Slice& value) {
|
||||
Status InternalKeyPropertiesCollector::InternalAdd(const Slice& key,
|
||||
const Slice& value,
|
||||
uint64_t file_size) {
|
||||
ParsedInternalKey ikey;
|
||||
if (!ParseInternalKey(key, &ikey)) {
|
||||
return Status::InvalidArgument("Invalid internal key");
|
||||
@@ -45,15 +46,31 @@ InternalKeyPropertiesCollector::GetReadableProperties() const {
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
EntryType GetEntryType(ValueType value_type) {
|
||||
switch (value_type) {
|
||||
case kTypeValue:
|
||||
return kEntryPut;
|
||||
case kTypeDeletion:
|
||||
return kEntryDelete;
|
||||
case kTypeMerge:
|
||||
return kEntryMerge;
|
||||
default:
|
||||
return kEntryOther;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status UserKeyTablePropertiesCollector::Add(
|
||||
const Slice& key, const Slice& value) {
|
||||
Status UserKeyTablePropertiesCollector::InternalAdd(const Slice& key,
|
||||
const Slice& value,
|
||||
uint64_t file_size) {
|
||||
ParsedInternalKey ikey;
|
||||
if (!ParseInternalKey(key, &ikey)) {
|
||||
return Status::InvalidArgument("Invalid internal key");
|
||||
}
|
||||
|
||||
return collector_->Add(ikey.user_key, value);
|
||||
return collector_->AddUserKey(ikey.user_key, value, GetEntryType(ikey.type),
|
||||
ikey.sequence, file_size);
|
||||
}
|
||||
|
||||
Status UserKeyTablePropertiesCollector::Finish(
|
||||
|
||||
@@ -18,11 +18,39 @@ struct InternalKeyTablePropertiesNames {
|
||||
static const std::string kDeletedKeys;
|
||||
};
|
||||
|
||||
// Base class for internal table properties collector.
|
||||
class IntTblPropCollector {
|
||||
public:
|
||||
virtual ~IntTblPropCollector() {}
|
||||
virtual Status Finish(UserCollectedProperties* properties) = 0;
|
||||
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
// @params key the user key that is inserted into the table.
|
||||
// @params value the value that is inserted into the table.
|
||||
virtual Status InternalAdd(const Slice& key, const Slice& value,
|
||||
uint64_t file_size) = 0;
|
||||
|
||||
virtual UserCollectedProperties GetReadableProperties() const = 0;
|
||||
};
|
||||
|
||||
// Facrtory for internal table properties collector.
|
||||
class IntTblPropCollectorFactory {
|
||||
public:
|
||||
virtual ~IntTblPropCollectorFactory() {}
|
||||
// has to be thread-safe
|
||||
virtual IntTblPropCollector* CreateIntTblPropCollector() = 0;
|
||||
|
||||
// The name of the properties collector can be used for debugging purpose.
|
||||
virtual const char* Name() const = 0;
|
||||
};
|
||||
|
||||
// Collecting the statistics for internal keys. Visible only by internal
|
||||
// rocksdb modules.
|
||||
class InternalKeyPropertiesCollector : public TablePropertiesCollector {
|
||||
class InternalKeyPropertiesCollector : public IntTblPropCollector {
|
||||
public:
|
||||
virtual Status Add(const Slice& key, const Slice& value) override;
|
||||
virtual Status InternalAdd(const Slice& key, const Slice& value,
|
||||
uint64_t file_size) override;
|
||||
|
||||
virtual Status Finish(UserCollectedProperties* properties) override;
|
||||
|
||||
@@ -37,9 +65,9 @@ class InternalKeyPropertiesCollector : public TablePropertiesCollector {
|
||||
};
|
||||
|
||||
class InternalKeyPropertiesCollectorFactory
|
||||
: public TablePropertiesCollectorFactory {
|
||||
: public IntTblPropCollectorFactory {
|
||||
public:
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() override {
|
||||
virtual IntTblPropCollector* CreateIntTblPropCollector() override {
|
||||
return new InternalKeyPropertiesCollector();
|
||||
}
|
||||
|
||||
@@ -53,7 +81,7 @@ class InternalKeyPropertiesCollectorFactory
|
||||
//
|
||||
// This class extracts user key from the encoded internal key when Add() is
|
||||
// invoked.
|
||||
class UserKeyTablePropertiesCollector : public TablePropertiesCollector {
|
||||
class UserKeyTablePropertiesCollector : public IntTblPropCollector {
|
||||
public:
|
||||
// transfer of ownership
|
||||
explicit UserKeyTablePropertiesCollector(TablePropertiesCollector* collector)
|
||||
@@ -61,7 +89,8 @@ class UserKeyTablePropertiesCollector : public TablePropertiesCollector {
|
||||
|
||||
virtual ~UserKeyTablePropertiesCollector() {}
|
||||
|
||||
virtual Status Add(const Slice& key, const Slice& value) override;
|
||||
virtual Status InternalAdd(const Slice& key, const Slice& value,
|
||||
uint64_t file_size) override;
|
||||
|
||||
virtual Status Finish(UserCollectedProperties* properties) override;
|
||||
|
||||
@@ -74,12 +103,12 @@ class UserKeyTablePropertiesCollector : public TablePropertiesCollector {
|
||||
};
|
||||
|
||||
class UserKeyTablePropertiesCollectorFactory
|
||||
: public TablePropertiesCollectorFactory {
|
||||
: public IntTblPropCollectorFactory {
|
||||
public:
|
||||
explicit UserKeyTablePropertiesCollectorFactory(
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> user_collector_factory)
|
||||
: user_collector_factory_(user_collector_factory) {}
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() override {
|
||||
virtual IntTblPropCollector* CreateIntTblPropCollector() override {
|
||||
return new UserKeyTablePropertiesCollector(
|
||||
user_collector_factory_->CreateTablePropertiesCollector());
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
@@ -22,7 +23,13 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class TablePropertiesTest : public testing::Test {};
|
||||
class TablePropertiesTest : public testing::Test,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
virtual void SetUp() override { backward_mode_ = GetParam(); }
|
||||
|
||||
bool backward_mode_;
|
||||
};
|
||||
|
||||
// TODO(kailiu) the following classes should be moved to some more general
|
||||
// places, so that other tests can also make use of them.
|
||||
@@ -85,15 +92,16 @@ class DumbLogger : public Logger {
|
||||
|
||||
// Utilities test functions
|
||||
namespace {
|
||||
void MakeBuilder(const Options& options,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
void MakeBuilder(const Options& options, const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
std::unique_ptr<FakeWritableFile>* writable,
|
||||
std::unique_ptr<TableBuilder>* builder) {
|
||||
writable->reset(new FakeWritableFile);
|
||||
builder->reset(ioptions.table_factory->NewTableBuilder(
|
||||
ioptions, internal_comparator, writable->get(),
|
||||
options.compression, options.compression_opts));
|
||||
builder->reset(NewTableBuilder(
|
||||
ioptions, internal_comparator, int_tbl_prop_collector_factories,
|
||||
writable->get(), options.compression, options.compression_opts));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -104,23 +112,80 @@ class RegularKeysStartWithA: public TablePropertiesCollector {
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
std::string encoded;
|
||||
std::string encoded_num_puts;
|
||||
std::string encoded_num_deletes;
|
||||
std::string encoded_num_size_changes;
|
||||
PutVarint32(&encoded, count_);
|
||||
*properties = UserCollectedProperties {
|
||||
{ "TablePropertiesTest", "Rocksdb" },
|
||||
{ "Count", encoded }
|
||||
PutVarint32(&encoded_num_puts, num_puts_);
|
||||
PutVarint32(&encoded_num_deletes, num_deletes_);
|
||||
PutVarint32(&encoded_num_size_changes, num_size_changes_);
|
||||
*properties = UserCollectedProperties{
|
||||
{"TablePropertiesTest", message_},
|
||||
{"Count", encoded},
|
||||
{"NumPuts", encoded_num_puts},
|
||||
{"NumDeletes", encoded_num_deletes},
|
||||
{"NumSizeChanges", encoded_num_size_changes},
|
||||
};
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
Status Add(const Slice& user_key, const Slice& value) override {
|
||||
// simply asssume all user keys are not empty.
|
||||
if (user_key.data()[0] == 'A') {
|
||||
++count_;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
Status AddUserKey(const Slice& user_key, const Slice& value, EntryType type,
|
||||
SequenceNumber seq, uint64_t file_size) override {
|
||||
// simply asssume all user keys are not empty.
|
||||
if (user_key.data()[0] == 'A') {
|
||||
++count_;
|
||||
}
|
||||
if (type == kEntryPut) {
|
||||
num_puts_++;
|
||||
} else if (type == kEntryDelete) {
|
||||
num_deletes_++;
|
||||
}
|
||||
if (file_size < file_size_) {
|
||||
message_ = "File size should not decrease.";
|
||||
} else if (file_size != file_size_) {
|
||||
num_size_changes_++;
|
||||
}
|
||||
|
||||
virtual UserCollectedProperties GetReadableProperties() const override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string message_ = "Rocksdb";
|
||||
uint32_t count_ = 0;
|
||||
uint32_t num_puts_ = 0;
|
||||
uint32_t num_deletes_ = 0;
|
||||
uint32_t num_size_changes_ = 0;
|
||||
uint64_t file_size_ = 0;
|
||||
};
|
||||
|
||||
// Collects keys that starts with "A" in a table. Backward compatible mode
|
||||
// It is also used to test internal key table property collector
|
||||
class RegularKeysStartWithABackwardCompatible
|
||||
: public TablePropertiesCollector {
|
||||
public:
|
||||
const char* Name() const override { return "RegularKeysStartWithA"; }
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
std::string encoded;
|
||||
PutVarint32(&encoded, count_);
|
||||
*properties = UserCollectedProperties{{"TablePropertiesTest", "Rocksdb"},
|
||||
{"Count", encoded}};
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Add(const Slice& user_key, const Slice& value) override {
|
||||
// simply asssume all user keys are not empty.
|
||||
if (user_key.data()[0] == 'A') {
|
||||
++count_;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
@@ -128,44 +193,121 @@ class RegularKeysStartWithA: public TablePropertiesCollector {
|
||||
uint32_t count_ = 0;
|
||||
};
|
||||
|
||||
class RegularKeysStartWithAFactory : public TablePropertiesCollectorFactory {
|
||||
class RegularKeysStartWithAInternal : public IntTblPropCollector {
|
||||
public:
|
||||
const char* Name() const override { return "RegularKeysStartWithA"; }
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
std::string encoded;
|
||||
PutVarint32(&encoded, count_);
|
||||
*properties = UserCollectedProperties{{"TablePropertiesTest", "Rocksdb"},
|
||||
{"Count", encoded}};
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status InternalAdd(const Slice& user_key, const Slice& value,
|
||||
uint64_t file_size) override {
|
||||
// simply asssume all user keys are not empty.
|
||||
if (user_key.data()[0] == 'A') {
|
||||
++count_;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t count_ = 0;
|
||||
};
|
||||
|
||||
class RegularKeysStartWithAFactory : public IntTblPropCollectorFactory,
|
||||
public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
explicit RegularKeysStartWithAFactory(bool backward_mode)
|
||||
: backward_mode_(backward_mode) {}
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() override {
|
||||
return new RegularKeysStartWithA();
|
||||
if (!backward_mode_) {
|
||||
return new RegularKeysStartWithA();
|
||||
} else {
|
||||
return new RegularKeysStartWithABackwardCompatible();
|
||||
}
|
||||
}
|
||||
virtual IntTblPropCollector* CreateIntTblPropCollector() override {
|
||||
return new RegularKeysStartWithAInternal();
|
||||
}
|
||||
const char* Name() const override { return "RegularKeysStartWithA"; }
|
||||
|
||||
bool backward_mode_;
|
||||
};
|
||||
|
||||
class FlushBlockEveryThreePolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
virtual bool Update(const Slice& key, const Slice& value) override {
|
||||
return (++count_ % 3U == 0);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t count_ = 0;
|
||||
};
|
||||
|
||||
class FlushBlockEveryThreePolicyFactory : public FlushBlockPolicyFactory {
|
||||
public:
|
||||
explicit FlushBlockEveryThreePolicyFactory() {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "FlushBlockEveryThreePolicyFactory";
|
||||
}
|
||||
|
||||
FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new FlushBlockEveryThreePolicy;
|
||||
}
|
||||
};
|
||||
|
||||
extern uint64_t kBlockBasedTableMagicNumber;
|
||||
extern uint64_t kPlainTableMagicNumber;
|
||||
namespace {
|
||||
void TestCustomizedTablePropertiesCollector(
|
||||
uint64_t magic_number, bool encode_as_internal, const Options& options,
|
||||
const InternalKeyComparator& internal_comparator) {
|
||||
bool backward_mode, uint64_t magic_number, bool test_int_tbl_prop_collector,
|
||||
const Options& options, const InternalKeyComparator& internal_comparator) {
|
||||
const std::string kDeleteFlag = "D";
|
||||
// make sure the entries will be inserted with order.
|
||||
std::map<std::string, std::string> kvs = {
|
||||
{"About ", "val5"}, // starts with 'A'
|
||||
{"Abstract", "val2"}, // starts with 'A'
|
||||
{"Around ", "val7"}, // starts with 'A'
|
||||
{"Beyond ", "val3"},
|
||||
{"Builder ", "val1"},
|
||||
{"Cancel ", "val4"},
|
||||
{"Find ", "val6"},
|
||||
{"About ", "val5"}, // starts with 'A'
|
||||
{"Abstract", "val2"}, // starts with 'A'
|
||||
{"Around ", "val7"}, // starts with 'A'
|
||||
{"Beyond ", "val3"},
|
||||
{"Builder ", "val1"},
|
||||
{"Love ", kDeleteFlag},
|
||||
{"Cancel ", "val4"},
|
||||
{"Find ", "val6"},
|
||||
{"Rocks ", kDeleteFlag},
|
||||
};
|
||||
|
||||
// -- Step 1: build table
|
||||
std::unique_ptr<TableBuilder> builder;
|
||||
std::unique_ptr<FakeWritableFile> writable;
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
MakeBuilder(options, ioptions, internal_comparator, &writable, &builder);
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
|
||||
int_tbl_prop_collector_factories;
|
||||
if (test_int_tbl_prop_collector) {
|
||||
int_tbl_prop_collector_factories.emplace_back(
|
||||
new RegularKeysStartWithAFactory(backward_mode));
|
||||
} else {
|
||||
GetIntTblPropCollectorFactory(options, &int_tbl_prop_collector_factories);
|
||||
}
|
||||
MakeBuilder(options, ioptions, internal_comparator,
|
||||
&int_tbl_prop_collector_factories, &writable, &builder);
|
||||
|
||||
SequenceNumber seqNum = 0U;
|
||||
for (const auto& kv : kvs) {
|
||||
if (encode_as_internal) {
|
||||
InternalKey ikey(kv.first, 0, ValueType::kTypeValue);
|
||||
builder->Add(ikey.Encode(), kv.second);
|
||||
} else {
|
||||
builder->Add(kv.first, kv.second);
|
||||
}
|
||||
InternalKey ikey(kv.first, seqNum++, (kv.second != kDeleteFlag)
|
||||
? ValueType::kTypeValue
|
||||
: ValueType::kTypeDeletion);
|
||||
builder->Add(ikey.Encode(), kv.second);
|
||||
}
|
||||
ASSERT_OK(builder->Finish());
|
||||
|
||||
@@ -185,64 +327,88 @@ void TestCustomizedTablePropertiesCollector(
|
||||
|
||||
auto user_collected = props->user_collected_properties;
|
||||
|
||||
ASSERT_TRUE(user_collected.find("TablePropertiesTest") !=
|
||||
user_collected.end());
|
||||
ASSERT_EQ("Rocksdb", user_collected.at("TablePropertiesTest"));
|
||||
|
||||
uint32_t starts_with_A = 0;
|
||||
ASSERT_TRUE(user_collected.find("Count") != user_collected.end());
|
||||
Slice key(user_collected.at("Count"));
|
||||
ASSERT_TRUE(GetVarint32(&key, &starts_with_A));
|
||||
ASSERT_EQ(3u, starts_with_A);
|
||||
|
||||
if (!backward_mode && !test_int_tbl_prop_collector) {
|
||||
uint32_t num_deletes;
|
||||
ASSERT_TRUE(user_collected.find("NumDeletes") != user_collected.end());
|
||||
Slice key_deletes(user_collected.at("NumDeletes"));
|
||||
ASSERT_TRUE(GetVarint32(&key_deletes, &num_deletes));
|
||||
ASSERT_EQ(2u, num_deletes);
|
||||
|
||||
uint32_t num_puts;
|
||||
ASSERT_TRUE(user_collected.find("NumPuts") != user_collected.end());
|
||||
Slice key_puts(user_collected.at("NumPuts"));
|
||||
ASSERT_TRUE(GetVarint32(&key_puts, &num_puts));
|
||||
ASSERT_EQ(7u, num_puts);
|
||||
|
||||
uint32_t num_size_changes;
|
||||
ASSERT_TRUE(user_collected.find("NumSizeChanges") != user_collected.end());
|
||||
Slice key_size_changes(user_collected.at("NumSizeChanges"));
|
||||
ASSERT_TRUE(GetVarint32(&key_size_changes, &num_size_changes));
|
||||
ASSERT_GE(num_size_changes, 2u);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_F(TablePropertiesTest, CustomizedTablePropertiesCollector) {
|
||||
TEST_P(TablePropertiesTest, CustomizedTablePropertiesCollector) {
|
||||
// Test properties collectors with internal keys or regular keys
|
||||
// for block based table
|
||||
for (bool encode_as_internal : { true, false }) {
|
||||
Options options;
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> collector_factory(
|
||||
new RegularKeysStartWithAFactory());
|
||||
if (encode_as_internal) {
|
||||
options.table_properties_collector_factories.emplace_back(
|
||||
new UserKeyTablePropertiesCollectorFactory(collector_factory));
|
||||
} else {
|
||||
options.table_properties_collector_factories.resize(1);
|
||||
options.table_properties_collector_factories[0] = collector_factory;
|
||||
if (!backward_mode_ && !encode_as_internal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Options options;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.flush_block_policy_factory =
|
||||
std::make_shared<FlushBlockEveryThreePolicyFactory>();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
test::PlainInternalKeyComparator ikc(options.comparator);
|
||||
TestCustomizedTablePropertiesCollector(kBlockBasedTableMagicNumber,
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> collector_factory(
|
||||
new RegularKeysStartWithAFactory(backward_mode_));
|
||||
options.table_properties_collector_factories.resize(1);
|
||||
options.table_properties_collector_factories[0] = collector_factory;
|
||||
|
||||
TestCustomizedTablePropertiesCollector(backward_mode_,
|
||||
kBlockBasedTableMagicNumber,
|
||||
encode_as_internal, options, ikc);
|
||||
|
||||
// test plain table
|
||||
PlainTableOptions plain_table_options;
|
||||
plain_table_options.user_key_len = 8;
|
||||
plain_table_options.bloom_bits_per_key = 8;
|
||||
plain_table_options.hash_table_ratio = 0;
|
||||
|
||||
options.table_factory =
|
||||
std::make_shared<PlainTableFactory>(plain_table_options);
|
||||
TestCustomizedTablePropertiesCollector(backward_mode_,
|
||||
kPlainTableMagicNumber,
|
||||
encode_as_internal, options, ikc);
|
||||
}
|
||||
|
||||
// test plain table
|
||||
Options options;
|
||||
options.table_properties_collector_factories.emplace_back(
|
||||
new RegularKeysStartWithAFactory());
|
||||
|
||||
PlainTableOptions plain_table_options;
|
||||
plain_table_options.user_key_len = 8;
|
||||
plain_table_options.bloom_bits_per_key = 8;
|
||||
plain_table_options.hash_table_ratio = 0;
|
||||
|
||||
options.table_factory =
|
||||
std::make_shared<PlainTableFactory>(plain_table_options);
|
||||
test::PlainInternalKeyComparator ikc(options.comparator);
|
||||
TestCustomizedTablePropertiesCollector(kPlainTableMagicNumber, true, options,
|
||||
ikc);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void TestInternalKeyPropertiesCollector(
|
||||
uint64_t magic_number,
|
||||
bool sanitized,
|
||||
bool backward_mode, uint64_t magic_number, bool sanitized,
|
||||
std::shared_ptr<TableFactory> table_factory) {
|
||||
InternalKey keys[] = {
|
||||
InternalKey("A ", 0, ValueType::kTypeValue),
|
||||
InternalKey("B ", 0, ValueType::kTypeValue),
|
||||
InternalKey("C ", 0, ValueType::kTypeValue),
|
||||
InternalKey("W ", 0, ValueType::kTypeDeletion),
|
||||
InternalKey("X ", 0, ValueType::kTypeDeletion),
|
||||
InternalKey("Y ", 0, ValueType::kTypeDeletion),
|
||||
InternalKey("Z ", 0, ValueType::kTypeDeletion),
|
||||
InternalKey("A ", 0, ValueType::kTypeValue),
|
||||
InternalKey("B ", 1, ValueType::kTypeValue),
|
||||
InternalKey("C ", 2, ValueType::kTypeValue),
|
||||
InternalKey("W ", 3, ValueType::kTypeDeletion),
|
||||
InternalKey("X ", 4, ValueType::kTypeDeletion),
|
||||
InternalKey("Y ", 5, ValueType::kTypeDeletion),
|
||||
InternalKey("Z ", 6, ValueType::kTypeDeletion),
|
||||
};
|
||||
|
||||
std::unique_ptr<TableBuilder> builder;
|
||||
@@ -250,10 +416,12 @@ void TestInternalKeyPropertiesCollector(
|
||||
Options options;
|
||||
test::PlainInternalKeyComparator pikc(options.comparator);
|
||||
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
|
||||
int_tbl_prop_collector_factories;
|
||||
options.table_factory = table_factory;
|
||||
if (sanitized) {
|
||||
options.table_properties_collector_factories.emplace_back(
|
||||
new RegularKeysStartWithAFactory());
|
||||
new RegularKeysStartWithAFactory(backward_mode));
|
||||
// with sanitization, even regular properties collector will be able to
|
||||
// handle internal keys.
|
||||
auto comparator = options.comparator;
|
||||
@@ -263,15 +431,17 @@ void TestInternalKeyPropertiesCollector(
|
||||
options = SanitizeOptions("db", // just a place holder
|
||||
&pikc,
|
||||
options);
|
||||
GetIntTblPropCollectorFactory(options, &int_tbl_prop_collector_factories);
|
||||
options.comparator = comparator;
|
||||
} else {
|
||||
options.table_properties_collector_factories = {
|
||||
std::make_shared<InternalKeyPropertiesCollectorFactory>()};
|
||||
int_tbl_prop_collector_factories.emplace_back(
|
||||
new InternalKeyPropertiesCollectorFactory);
|
||||
}
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
MakeBuilder(options, ioptions, pikc, &writable, &builder);
|
||||
MakeBuilder(options, ioptions, pikc, &int_tbl_prop_collector_factories,
|
||||
&writable, &builder);
|
||||
for (const auto& k : keys) {
|
||||
builder->Add(k.Encode(), "val");
|
||||
}
|
||||
@@ -292,25 +462,38 @@ void TestInternalKeyPropertiesCollector(
|
||||
|
||||
if (sanitized) {
|
||||
uint32_t starts_with_A = 0;
|
||||
ASSERT_TRUE(user_collected.find("Count") != user_collected.end());
|
||||
Slice key(user_collected.at("Count"));
|
||||
ASSERT_TRUE(GetVarint32(&key, &starts_with_A));
|
||||
ASSERT_EQ(1u, starts_with_A);
|
||||
|
||||
if (!backward_mode) {
|
||||
uint32_t num_deletes;
|
||||
ASSERT_TRUE(user_collected.find("NumDeletes") != user_collected.end());
|
||||
Slice key_deletes(user_collected.at("NumDeletes"));
|
||||
ASSERT_TRUE(GetVarint32(&key_deletes, &num_deletes));
|
||||
ASSERT_EQ(4u, num_deletes);
|
||||
|
||||
uint32_t num_puts;
|
||||
ASSERT_TRUE(user_collected.find("NumPuts") != user_collected.end());
|
||||
Slice key_puts(user_collected.at("NumPuts"));
|
||||
ASSERT_TRUE(GetVarint32(&key_puts, &num_puts));
|
||||
ASSERT_EQ(3u, num_puts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_F(TablePropertiesTest, InternalKeyPropertiesCollector) {
|
||||
TEST_P(TablePropertiesTest, InternalKeyPropertiesCollector) {
|
||||
TestInternalKeyPropertiesCollector(
|
||||
kBlockBasedTableMagicNumber,
|
||||
true /* sanitize */,
|
||||
std::make_shared<BlockBasedTableFactory>()
|
||||
);
|
||||
TestInternalKeyPropertiesCollector(
|
||||
kBlockBasedTableMagicNumber,
|
||||
true /* not sanitize */,
|
||||
std::make_shared<BlockBasedTableFactory>()
|
||||
);
|
||||
backward_mode_, kBlockBasedTableMagicNumber, true /* sanitize */,
|
||||
std::make_shared<BlockBasedTableFactory>());
|
||||
if (backward_mode_) {
|
||||
TestInternalKeyPropertiesCollector(
|
||||
backward_mode_, kBlockBasedTableMagicNumber, false /* not sanitize */,
|
||||
std::make_shared<BlockBasedTableFactory>());
|
||||
}
|
||||
|
||||
PlainTableOptions plain_table_options;
|
||||
plain_table_options.user_key_len = 8;
|
||||
@@ -318,10 +501,16 @@ TEST_F(TablePropertiesTest, InternalKeyPropertiesCollector) {
|
||||
plain_table_options.hash_table_ratio = 0;
|
||||
|
||||
TestInternalKeyPropertiesCollector(
|
||||
kPlainTableMagicNumber, false /* not sanitize */,
|
||||
backward_mode_, kPlainTableMagicNumber, false /* not sanitize */,
|
||||
std::make_shared<PlainTableFactory>(plain_table_options));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InternalKeyPropertiesCollector, TablePropertiesTest,
|
||||
::testing::Bool());
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CustomizedTablePropertiesCollector, TablePropertiesTest,
|
||||
::testing::Bool());
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+5
-1
@@ -87,6 +87,9 @@ struct FileMetaData {
|
||||
bool init_stats_from_file; // true if the data-entry stats of this file
|
||||
// has initialized from file.
|
||||
|
||||
bool marked_for_compaction; // True if client asked us nicely to compact this
|
||||
// file.
|
||||
|
||||
FileMetaData()
|
||||
: refs(0),
|
||||
being_compacted(false),
|
||||
@@ -96,7 +99,8 @@ struct FileMetaData {
|
||||
num_deletions(0),
|
||||
raw_key_size(0),
|
||||
raw_value_size(0),
|
||||
init_stats_from_file(false) {}
|
||||
init_stats_from_file(false),
|
||||
marked_for_compaction(false) {}
|
||||
};
|
||||
|
||||
// A compressed copy of file meta data that just contain
|
||||
|
||||
+188
-63
@@ -275,7 +275,7 @@ class FilePicker {
|
||||
static_cast<uint32_t>(search_right_bound_));
|
||||
} else {
|
||||
// search_left_bound > search_right_bound, key does not exist in
|
||||
// this level. Since no comparision is done in this level, it will
|
||||
// this level. Since no comparison is done in this level, it will
|
||||
// need to search all files in the next level.
|
||||
search_left_bound_ = 0;
|
||||
search_right_bound_ = FileIndexer::kLevelMaxIndex;
|
||||
@@ -397,7 +397,8 @@ bool SomeFileOverlapsRange(
|
||||
uint32_t index = 0;
|
||||
if (smallest_user_key != nullptr) {
|
||||
// Find the earliest possible internal key for smallest_user_key
|
||||
InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek);
|
||||
InternalKey small;
|
||||
small.SetMaxPossibleForUserKey(*smallest_user_key);
|
||||
index = FindFile(icmp, file_level, small.Encode());
|
||||
}
|
||||
|
||||
@@ -655,8 +656,8 @@ void Version::GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta) {
|
||||
|
||||
|
||||
uint64_t VersionStorageInfo::GetEstimatedActiveKeys() const {
|
||||
// Estimation will be not accurate when:
|
||||
// (1) there is merge keys
|
||||
// Estimation will be inaccurate when:
|
||||
// (1) there exist merge keys
|
||||
// (2) keys are directly overwritten
|
||||
// (3) deletion on non-existing keys
|
||||
// (4) low number of samples
|
||||
@@ -664,6 +665,12 @@ uint64_t VersionStorageInfo::GetEstimatedActiveKeys() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (accumulated_num_non_deletions_ <= accumulated_num_deletions_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t est = accumulated_num_non_deletions_ - accumulated_num_deletions_;
|
||||
|
||||
uint64_t file_count = 0;
|
||||
for (int level = 0; level < num_levels_; ++level) {
|
||||
file_count += files_[level].size();
|
||||
@@ -671,11 +678,9 @@ uint64_t VersionStorageInfo::GetEstimatedActiveKeys() const {
|
||||
|
||||
if (num_samples_ < file_count) {
|
||||
// casting to avoid overflowing
|
||||
return static_cast<uint64_t>(static_cast<double>(
|
||||
accumulated_num_non_deletions_ - accumulated_num_deletions_) *
|
||||
static_cast<double>(file_count) / num_samples_);
|
||||
return (est * static_cast<double>(file_count) / num_samples_);
|
||||
} else {
|
||||
return accumulated_num_non_deletions_ - accumulated_num_deletions_;
|
||||
return est;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,11 +731,12 @@ VersionStorageInfo::VersionStorageInfo(
|
||||
file_indexer_(user_comparator),
|
||||
compaction_style_(compaction_style),
|
||||
files_(new std::vector<FileMetaData*>[num_levels_]),
|
||||
base_level_(1),
|
||||
base_level_(num_levels_ == 1 ? -1 : 1),
|
||||
files_by_size_(num_levels_),
|
||||
next_file_to_compact_by_size_(num_levels_),
|
||||
compaction_score_(num_levels_),
|
||||
compaction_level_(num_levels_),
|
||||
l0_delay_trigger_count_(0),
|
||||
accumulated_file_size_(0),
|
||||
accumulated_raw_key_size_(0),
|
||||
accumulated_raw_value_size_(0),
|
||||
@@ -991,32 +997,44 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
// file size is small (perhaps because of a small write-buffer
|
||||
// setting, or very high compression ratios, or lots of
|
||||
// overwrites/deletions).
|
||||
int numfiles = 0;
|
||||
int num_sorted_runs = 0;
|
||||
uint64_t total_size = 0;
|
||||
for (unsigned int i = 0; i < files_[level].size(); i++) {
|
||||
if (!files_[level][i]->being_compacted) {
|
||||
total_size += files_[level][i]->compensated_file_size;
|
||||
numfiles++;
|
||||
for (auto* f : files_[level]) {
|
||||
if (!f->being_compacted) {
|
||||
total_size += f->compensated_file_size;
|
||||
num_sorted_runs++;
|
||||
}
|
||||
}
|
||||
if (compaction_style_ == kCompactionStyleUniversal) {
|
||||
// For universal compaction, we use level0 score to indicate
|
||||
// compaction score for the whole DB. Adding other levels as if
|
||||
// they are L0 files.
|
||||
for (int i = 1; i < num_levels(); i++) {
|
||||
if (!files_[i].empty() && !files_[i][0]->being_compacted) {
|
||||
num_sorted_runs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compaction_style_ == kCompactionStyleFIFO) {
|
||||
score = static_cast<double>(total_size) /
|
||||
compaction_options_fifo.max_table_files_size;
|
||||
} else if (numfiles >= mutable_cf_options.level0_stop_writes_trigger) {
|
||||
} else if (num_sorted_runs >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
// If we are slowing down writes, then we better compact that first
|
||||
score = 1000000;
|
||||
} else if (numfiles >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
} else if (num_sorted_runs >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
score = 10000;
|
||||
} else {
|
||||
score = static_cast<double>(numfiles) /
|
||||
score = static_cast<double>(num_sorted_runs) /
|
||||
mutable_cf_options.level0_file_num_compaction_trigger;
|
||||
}
|
||||
} else {
|
||||
// Compute the ratio of current size to size limit.
|
||||
uint64_t level_bytes_no_compacting = 0;
|
||||
for (auto f : files_[level]) {
|
||||
if (f && f->being_compacted == false) {
|
||||
if (!f->being_compacted) {
|
||||
level_bytes_no_compacting += f->compensated_file_size;
|
||||
}
|
||||
}
|
||||
@@ -1049,6 +1067,18 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
}
|
||||
}
|
||||
}
|
||||
ComputeFilesMarkedForCompaction();
|
||||
}
|
||||
|
||||
void VersionStorageInfo::ComputeFilesMarkedForCompaction() {
|
||||
files_marked_for_compaction_.clear();
|
||||
for (int level = 0; level <= MaxInputLevel(); level++) {
|
||||
for (auto* f : files_[level]) {
|
||||
if (!f->being_compacted && f->marked_for_compaction) {
|
||||
files_marked_for_compaction_.emplace_back(level, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -1090,7 +1120,12 @@ void VersionStorageInfo::AddFile(int level, FileMetaData* f) {
|
||||
void VersionStorageInfo::SetFinalized() {
|
||||
finalized_ = true;
|
||||
#ifndef NDEBUG
|
||||
assert(base_level_ >= 1 && (num_levels() <= 1 || base_level_ < num_levels()));
|
||||
if (compaction_style_ != kCompactionStyleLevel) {
|
||||
// Not level based compaction.
|
||||
return;
|
||||
}
|
||||
assert(base_level_ < 0 || num_levels() == 1 ||
|
||||
(base_level_ >= 1 && base_level_ < num_levels()));
|
||||
// Verify all levels newer than base_level are empty except L0
|
||||
for (int level = 1; level < base_level(); level++) {
|
||||
assert(NumLevelBytes(level) == 0);
|
||||
@@ -1184,6 +1219,10 @@ bool Version::Unref() {
|
||||
bool VersionStorageInfo::OverlapInLevel(int level,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key) {
|
||||
if (level >= num_non_empty_levels_) {
|
||||
// empty level, no overlap
|
||||
return false;
|
||||
}
|
||||
return SomeFileOverlapsRange(*internal_comparator_, (level > 0),
|
||||
level_files_brief_[level], smallest_user_key,
|
||||
largest_user_key);
|
||||
@@ -1196,7 +1235,8 @@ int VersionStorageInfo::PickLevelForMemTableOutput(
|
||||
if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) {
|
||||
// Push to next level if there is no overlap in next level,
|
||||
// and the #bytes overlapping in the level after that are limited.
|
||||
InternalKey start(smallest_user_key, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey start;
|
||||
start.SetMaxPossibleForUserKey(smallest_user_key);
|
||||
InternalKey limit(largest_user_key, 0, static_cast<ValueType>(0));
|
||||
std::vector<FileMetaData*> overlaps;
|
||||
while (mutable_cf_options.max_mem_compaction_level > 0 &&
|
||||
@@ -1227,6 +1267,11 @@ int VersionStorageInfo::PickLevelForMemTableOutput(
|
||||
void VersionStorageInfo::GetOverlappingInputs(
|
||||
int level, const InternalKey* begin, const InternalKey* end,
|
||||
std::vector<FileMetaData*>* inputs, int hint_index, int* file_index) {
|
||||
if (level >= num_non_empty_levels_) {
|
||||
// this level is empty, no overlapping inputs
|
||||
return;
|
||||
}
|
||||
|
||||
inputs->clear();
|
||||
Slice user_begin, user_end;
|
||||
if (begin != nullptr) {
|
||||
@@ -1442,14 +1487,14 @@ uint64_t VersionStorageInfo::NumLevelBytes(int level) const {
|
||||
const char* VersionStorageInfo::LevelSummary(
|
||||
LevelSummaryStorage* scratch) const {
|
||||
int len = 0;
|
||||
if (num_levels() > 1) {
|
||||
if (compaction_style_ == kCompactionStyleLevel && num_levels() > 1) {
|
||||
assert(base_level_ < static_cast<int>(level_max_bytes_.size()));
|
||||
len = snprintf(scratch->buffer, sizeof(scratch->buffer),
|
||||
"base level %d max bytes base %" PRIu64, base_level_,
|
||||
"base level %d max bytes base %" PRIu64 " ", base_level_,
|
||||
level_max_bytes_[base_level_]);
|
||||
}
|
||||
len +=
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, " files[");
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, "files[");
|
||||
for (int i = 0; i < num_levels(); i++) {
|
||||
int sz = sizeof(scratch->buffer) - len;
|
||||
int ret = snprintf(scratch->buffer + len, sz, "%d ", int(files_[i].size()));
|
||||
@@ -1460,7 +1505,8 @@ const char* VersionStorageInfo::LevelSummary(
|
||||
// overwrite the last space
|
||||
--len;
|
||||
}
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, "]");
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
|
||||
"] max score %.2f", compaction_score_[0]);
|
||||
return scratch->buffer;
|
||||
}
|
||||
|
||||
@@ -1512,9 +1558,24 @@ uint64_t VersionStorageInfo::MaxBytesForLevel(int level) const {
|
||||
|
||||
void VersionStorageInfo::CalculateBaseBytes(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& options) {
|
||||
// Special logic to set number of sorted runs.
|
||||
// It is to match the previous behavior when all files are in L0.
|
||||
int num_l0_count = static_cast<int>(files_[0].size());
|
||||
if (compaction_style_ == kCompactionStyleUniversal) {
|
||||
// For universal compaction, we use level0 score to indicate
|
||||
// compaction score for the whole DB. Adding other levels as if
|
||||
// they are L0 files.
|
||||
for (int i = 1; i < num_levels(); i++) {
|
||||
if (!files_[i].empty()) {
|
||||
num_l0_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
set_l0_delay_trigger_count(num_l0_count);
|
||||
|
||||
level_max_bytes_.resize(ioptions.num_levels);
|
||||
if (!ioptions.level_compaction_dynamic_level_bytes) {
|
||||
base_level_ = 1;
|
||||
base_level_ = (ioptions.compaction_style == kCompactionStyleLevel) ? 1 : -1;
|
||||
|
||||
// Calculate for static bytes base case
|
||||
for (int i = 0; i < ioptions.num_levels; ++i) {
|
||||
@@ -1524,7 +1585,7 @@ void VersionStorageInfo::CalculateBaseBytes(const ImmutableCFOptions& ioptions,
|
||||
level_max_bytes_[i] = MultiplyCheckOverflow(
|
||||
MultiplyCheckOverflow(level_max_bytes_[i - 1],
|
||||
options.max_bytes_for_level_multiplier),
|
||||
options.max_bytes_for_level_multiplier_additional[i - 1]);
|
||||
options.MaxBytesMultiplerAdditional(i - 1));
|
||||
} else {
|
||||
level_max_bytes_[i] = options.max_bytes_for_level_base;
|
||||
}
|
||||
@@ -1571,12 +1632,12 @@ void VersionStorageInfo::CalculateBaseBytes(const ImmutableCFOptions& ioptions,
|
||||
}
|
||||
|
||||
// Calculate base level and its size.
|
||||
int base_level_size;
|
||||
uint64_t base_level_size;
|
||||
if (cur_level_size <= base_bytes_min) {
|
||||
// Case 1. If we make target size of last level to be max_level_size,
|
||||
// target size of the first non-empty level would be smaller than
|
||||
// base_bytes_min. We set it be base_bytes_min.
|
||||
base_level_size = static_cast<int>(base_bytes_min + 1);
|
||||
base_level_size = base_bytes_min + 1U;
|
||||
base_level_ = first_non_empty_level;
|
||||
Warn(ioptions.info_log,
|
||||
"More existing levels in DB than needed. "
|
||||
@@ -1592,16 +1653,17 @@ void VersionStorageInfo::CalculateBaseBytes(const ImmutableCFOptions& ioptions,
|
||||
if (cur_level_size > base_bytes_max) {
|
||||
// Even L1 will be too large
|
||||
assert(base_level_ == 1);
|
||||
base_level_size = static_cast<int>(base_bytes_max);
|
||||
base_level_size = base_bytes_max;
|
||||
} else {
|
||||
base_level_size = static_cast<int>(cur_level_size);
|
||||
base_level_size = cur_level_size;
|
||||
}
|
||||
}
|
||||
|
||||
int level_size = base_level_size;
|
||||
uint64_t level_size = base_level_size;
|
||||
for (int i = base_level_; i < num_levels_; i++) {
|
||||
if (i > base_level_) {
|
||||
level_size = level_size * options.max_bytes_for_level_multiplier;
|
||||
level_size = MultiplyCheckOverflow(
|
||||
level_size, options.max_bytes_for_level_multiplier);
|
||||
}
|
||||
level_max_bytes_[i] = level_size;
|
||||
}
|
||||
@@ -2740,40 +2802,101 @@ bool VersionSet::ManifestContains(uint64_t manifest_file_num,
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t VersionSet::ApproximateSize(Version* v, const Slice& start,
|
||||
const Slice& end) {
|
||||
// pre-condition
|
||||
assert(v->cfd_->internal_comparator().Compare(start, end) <= 0);
|
||||
|
||||
uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) {
|
||||
uint64_t result = 0;
|
||||
uint64_t size = 0;
|
||||
const auto* vstorage = v->storage_info();
|
||||
for (int level = 0; level < vstorage->num_levels(); level++) {
|
||||
const std::vector<FileMetaData*>& files = vstorage->LevelFiles(level);
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
if (v->cfd_->internal_comparator().Compare(files[i]->largest, ikey) <=
|
||||
0) {
|
||||
// Entire file is before "ikey", so just add the file size
|
||||
result += files[i]->fd.GetFileSize();
|
||||
} else if (v->cfd_->internal_comparator().Compare(files[i]->smallest,
|
||||
ikey) > 0) {
|
||||
// Entire file is after "ikey", so ignore
|
||||
if (level > 0) {
|
||||
// Files other than level 0 are sorted by meta->smallest, so
|
||||
// no further files in this level will contain data for
|
||||
// "ikey".
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// "ikey" falls in the range for this table. Add the
|
||||
// approximate offset of "ikey" within the table.
|
||||
TableReader* table_reader_ptr;
|
||||
Iterator* iter = v->cfd_->table_cache()->NewIterator(
|
||||
ReadOptions(), env_options_, v->cfd_->internal_comparator(),
|
||||
files[i]->fd, &table_reader_ptr);
|
||||
if (table_reader_ptr != nullptr) {
|
||||
result += table_reader_ptr->ApproximateOffsetOf(ikey.Encode());
|
||||
}
|
||||
delete iter;
|
||||
|
||||
for (int level = 0; level < vstorage->num_non_empty_levels(); level++) {
|
||||
const LevelFilesBrief& files_brief = vstorage->LevelFilesBrief(level);
|
||||
if (!files_brief.num_files) {
|
||||
// empty level, skip exploration
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!level) {
|
||||
// level 0 data is sorted order, handle the use case explicitly
|
||||
size += ApproximateSizeLevel0(v, files_brief, start, end);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(level > 0);
|
||||
assert(files_brief.num_files > 0);
|
||||
|
||||
// identify the file position for starting key
|
||||
const uint64_t idx_start = FindFileInRange(
|
||||
v->cfd_->internal_comparator(), files_brief, start,
|
||||
/*start=*/0, static_cast<uint32_t>(files_brief.num_files - 1));
|
||||
assert(idx_start < files_brief.num_files);
|
||||
|
||||
// scan all files from the starting position until the ending position
|
||||
// inferred from the sorted order
|
||||
for (uint64_t i = idx_start; i < files_brief.num_files; i++) {
|
||||
uint64_t val;
|
||||
val = ApproximateSize(v, files_brief.files[i], end);
|
||||
if (!val) {
|
||||
// the files after this will not have the range
|
||||
break;
|
||||
}
|
||||
|
||||
size += val;
|
||||
|
||||
if (i == idx_start) {
|
||||
// subtract the bytes needed to be scanned to get to the starting
|
||||
// key
|
||||
val = ApproximateSize(v, files_brief.files[i], start);
|
||||
assert(size >= val);
|
||||
size -= val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
uint64_t VersionSet::ApproximateSizeLevel0(Version* v,
|
||||
const LevelFilesBrief& files_brief,
|
||||
const Slice& key_start,
|
||||
const Slice& key_end) {
|
||||
// level 0 files are not in sorted order, we need to iterate through
|
||||
// the list to compute the total bytes that require scanning
|
||||
uint64_t size = 0;
|
||||
for (size_t i = 0; i < files_brief.num_files; i++) {
|
||||
const uint64_t start = ApproximateSize(v, files_brief.files[i], key_start);
|
||||
const uint64_t end = ApproximateSize(v, files_brief.files[i], key_end);
|
||||
assert(end >= start);
|
||||
size += end - start;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
uint64_t VersionSet::ApproximateSize(Version* v, const FdWithKeyRange& f,
|
||||
const Slice& key) {
|
||||
// pre-condition
|
||||
assert(v);
|
||||
|
||||
uint64_t result = 0;
|
||||
if (v->cfd_->internal_comparator().Compare(f.largest_key, key) <= 0) {
|
||||
// Entire file is before "key", so just add the file size
|
||||
result = f.fd.GetFileSize();
|
||||
} else if (v->cfd_->internal_comparator().Compare(f.smallest_key, key) > 0) {
|
||||
// Entire file is after "key", so ignore
|
||||
result = 0;
|
||||
} else {
|
||||
// "key" falls in the range for this table. Add the
|
||||
// approximate offset of "key" within the table.
|
||||
TableReader* table_reader_ptr;
|
||||
Iterator* iter = v->cfd_->table_cache()->NewIterator(
|
||||
ReadOptions(), env_options_, v->cfd_->internal_comparator(), f.fd,
|
||||
&table_reader_ptr);
|
||||
if (table_reader_ptr != nullptr) {
|
||||
result = table_reader_ptr->ApproximateOffsetOf(key);
|
||||
}
|
||||
delete iter;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2868,7 +2991,9 @@ bool VersionSet::VerifyCompactionFileConsistency(Compaction* c) {
|
||||
"[%s] compaction output being applied to a different base version from"
|
||||
" input version",
|
||||
c->column_family_data()->GetName().c_str());
|
||||
if (c->start_level() == 0 && c->num_input_levels() > 2U) {
|
||||
|
||||
if (vstorage->compaction_style_ == kCompactionStyleLevel &&
|
||||
c->start_level() == 0 && c->num_input_levels() > 2U) {
|
||||
// We are doing a L0->base_level compaction. The assumption is if
|
||||
// base level is not L1, levels from L1 to base_level - 1 is empty.
|
||||
// This is ensured by having one compaction from L0 going on at the
|
||||
|
||||
+42
-7
@@ -18,13 +18,15 @@
|
||||
// synchronization on all accesses.
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/version_builder.h"
|
||||
#include "db/version_edit.h"
|
||||
@@ -119,6 +121,10 @@ class VersionStorageInfo {
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const CompactionOptionsFIFO& compaction_options_fifo);
|
||||
|
||||
// This computes files_marked_for_compaction_ and is called by
|
||||
// ComputeCompactionScore()
|
||||
void ComputeFilesMarkedForCompaction();
|
||||
|
||||
// Generate level_files_brief_ from files_
|
||||
void GenerateLevelFilesBrief();
|
||||
// Sort all files for this version based on their file size and
|
||||
@@ -189,6 +195,14 @@ class VersionStorageInfo {
|
||||
return num_non_empty_levels_;
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been finalized.
|
||||
// (CalculateBaseBytes() is called)
|
||||
// This may or may not return number of level files. It is to keep backward
|
||||
// compatible behavior in universal compaction.
|
||||
int l0_delay_trigger_count() const { return l0_delay_trigger_count_; }
|
||||
|
||||
void set_l0_delay_trigger_count(int v) { l0_delay_trigger_count_ = v; }
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
int NumLevelFiles(int level) const {
|
||||
assert(finalized_);
|
||||
@@ -214,6 +228,14 @@ class VersionStorageInfo {
|
||||
return files_by_size_[level];
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
// REQUIRES: DB mutex held during access
|
||||
const autovector<std::pair<int, FileMetaData*>>& FilesMarkedForCompaction()
|
||||
const {
|
||||
assert(finalized_);
|
||||
return files_marked_for_compaction_;
|
||||
}
|
||||
|
||||
int base_level() const { return base_level_; }
|
||||
|
||||
// REQUIRES: lock is held
|
||||
@@ -312,7 +334,7 @@ class VersionStorageInfo {
|
||||
std::vector<FileMetaData*>* files_;
|
||||
|
||||
// Level that L0 data should be compacted to. All levels < base_level_ should
|
||||
// be empty.
|
||||
// be empty. -1 if it is not level-compaction so it's not applicable.
|
||||
int base_level_;
|
||||
|
||||
// A list for the same set of files that are stored in files_,
|
||||
@@ -332,6 +354,11 @@ class VersionStorageInfo {
|
||||
// seconds/minutes (because of concurrent compactions).
|
||||
static const size_t number_of_files_to_sort_ = 50;
|
||||
|
||||
// This vector contains list of files marked for compaction and also not
|
||||
// currently being compacted. It is protected by DB mutex. It is calculated in
|
||||
// ComputeCompactionScore()
|
||||
autovector<std::pair<int, FileMetaData*>> files_marked_for_compaction_;
|
||||
|
||||
// Level that should be compacted next and its compaction score.
|
||||
// Score < 1 means compaction is not strictly needed. These fields
|
||||
// are initialized by Finalize().
|
||||
@@ -341,6 +368,8 @@ class VersionStorageInfo {
|
||||
std::vector<int> compaction_level_;
|
||||
double max_compaction_score_ = 0.0; // max score in l1 to ln-1
|
||||
int max_compaction_score_level_ = 0; // level on which max score occurs
|
||||
int l0_delay_trigger_count_ = 0; // Count used to trigger slow down and stop
|
||||
// for number of L0 files.
|
||||
|
||||
// the following are the sampled temporary stats.
|
||||
// the current accumulated size of sampled files.
|
||||
@@ -589,9 +618,8 @@ class VersionSet {
|
||||
// Add all files listed in any live version to *live.
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live_list);
|
||||
|
||||
// Return the approximate offset in the database of the data for
|
||||
// "key" as of version "v".
|
||||
uint64_t ApproximateOffsetOf(Version* v, const InternalKey& key);
|
||||
// Return the approximate size of data to be scanned for range [start, end)
|
||||
uint64_t ApproximateSize(Version* v, const Slice& start, const Slice& end);
|
||||
|
||||
// Return the size of the current manifest file
|
||||
uint64_t manifest_file_size() const { return manifest_file_size_; }
|
||||
@@ -628,6 +656,13 @@ class VersionSet {
|
||||
}
|
||||
};
|
||||
|
||||
// ApproximateSize helper
|
||||
uint64_t ApproximateSizeLevel0(Version* v, const LevelFilesBrief& files_brief,
|
||||
const Slice& start, const Slice& end);
|
||||
|
||||
uint64_t ApproximateSize(Version* v, const FdWithKeyRange& f,
|
||||
const Slice& key);
|
||||
|
||||
// Save current contents to *log
|
||||
Status WriteSnapshot(log::Writer* log);
|
||||
|
||||
|
||||
@@ -215,6 +215,25 @@ TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicLotsOfData) {
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
}
|
||||
|
||||
TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicLargeLevel) {
|
||||
uint64_t kOneGB = 1000U * 1000U * 1000U;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10U * kOneGB;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
Add(0, 1U, "1", "2", 50U);
|
||||
Add(3, 4U, "1", "2", 32U * kOneGB);
|
||||
Add(4, 5U, "1", "2", 500U * kOneGB);
|
||||
Add(5, 6U, "1", "2", 3000U * kOneGB);
|
||||
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(5), 3000U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 300U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 30U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 3U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.base_level(), 2);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
}
|
||||
|
||||
class FindLevelFileTest : public testing::Test {
|
||||
public:
|
||||
LevelFilesBrief file_level_;
|
||||
|
||||
+11
-9
@@ -14,6 +14,7 @@
|
||||
#include "db/column_family.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "util/mock_env.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
@@ -27,7 +28,7 @@ namespace rocksdb {
|
||||
class WalManagerTest : public testing::Test {
|
||||
public:
|
||||
WalManagerTest()
|
||||
: env_(Env::Default()),
|
||||
: env_(new MockEnv(Env::Default())),
|
||||
dbname_(test::TmpDir() + "/wal_manager_test"),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_(db_options_.db_write_buffer_size),
|
||||
@@ -41,6 +42,7 @@ class WalManagerTest : public testing::Test {
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
db_options_.wal_dir = dbname_;
|
||||
db_options_.env = env_.get();
|
||||
|
||||
versions_.reset(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_,
|
||||
@@ -91,7 +93,7 @@ class WalManagerTest : public testing::Test {
|
||||
return std::move(iter);
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
std::unique_ptr<MockEnv> env_;
|
||||
std::string dbname_;
|
||||
WriteController write_controller_;
|
||||
EnvOptions env_options_;
|
||||
@@ -212,22 +214,22 @@ TEST_F(WalManagerTest, WALArchivalSizeLimit) {
|
||||
CreateArchiveLogs(20, 5000);
|
||||
|
||||
std::vector<std::uint64_t> log_files =
|
||||
ListSpecificFiles(env_, archive_dir, kLogFile);
|
||||
ListSpecificFiles(env_.get(), archive_dir, kLogFile);
|
||||
ASSERT_EQ(log_files.size(), 20U);
|
||||
|
||||
db_options_.WAL_size_limit_MB = 8;
|
||||
Reopen();
|
||||
wal_manager_->PurgeObsoleteWALFiles();
|
||||
|
||||
uint64_t archive_size = GetLogDirSize(archive_dir, env_);
|
||||
uint64_t archive_size = GetLogDirSize(archive_dir, env_.get());
|
||||
ASSERT_TRUE(archive_size <= db_options_.WAL_size_limit_MB * 1024 * 1024);
|
||||
|
||||
db_options_.WAL_ttl_seconds = 1;
|
||||
env_->SleepForMicroseconds(2 * 1000 * 1000);
|
||||
env_->FakeSleepForMicroseconds(2 * 1000 * 1000);
|
||||
Reopen();
|
||||
wal_manager_->PurgeObsoleteWALFiles();
|
||||
|
||||
log_files = ListSpecificFiles(env_, archive_dir, kLogFile);
|
||||
log_files = ListSpecificFiles(env_.get(), archive_dir, kLogFile);
|
||||
ASSERT_TRUE(log_files.empty());
|
||||
}
|
||||
|
||||
@@ -245,15 +247,15 @@ TEST_F(WalManagerTest, WALArchivalTtl) {
|
||||
CreateArchiveLogs(20, 5000);
|
||||
|
||||
std::vector<uint64_t> log_files =
|
||||
ListSpecificFiles(env_, archive_dir, kLogFile);
|
||||
ListSpecificFiles(env_.get(), archive_dir, kLogFile);
|
||||
ASSERT_GT(log_files.size(), 0U);
|
||||
|
||||
db_options_.WAL_ttl_seconds = 1;
|
||||
env_->SleepForMicroseconds(3 * 1000 * 1000);
|
||||
env_->FakeSleepForMicroseconds(3 * 1000 * 1000);
|
||||
Reopen();
|
||||
wal_manager_->PurgeObsoleteWALFiles();
|
||||
|
||||
log_files = ListSpecificFiles(env_, archive_dir, kLogFile);
|
||||
log_files = ListSpecificFiles(env_.get(), archive_dir, kLogFile);
|
||||
ASSERT_TRUE(log_files.empty());
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@ TEST_F(WriteControllerTest, SanityTest) {
|
||||
auto stop_token_1 = controller.GetStopToken();
|
||||
auto stop_token_2 = controller.GetStopToken();
|
||||
|
||||
ASSERT_EQ(true, controller.IsStopped());
|
||||
ASSERT_TRUE(controller.IsStopped());
|
||||
stop_token_1.reset();
|
||||
ASSERT_EQ(true, controller.IsStopped());
|
||||
ASSERT_TRUE(controller.IsStopped());
|
||||
stop_token_2.reset();
|
||||
ASSERT_EQ(false, controller.IsStopped());
|
||||
ASSERT_FALSE(controller.IsStopped());
|
||||
|
||||
auto delay_token_1 = controller.GetDelayToken(5);
|
||||
ASSERT_EQ(static_cast<uint64_t>(5), controller.GetDelay());
|
||||
@@ -32,7 +32,7 @@ TEST_F(WriteControllerTest, SanityTest) {
|
||||
delay_token_1.reset();
|
||||
ASSERT_EQ(static_cast<uint64_t>(0), controller.GetDelay());
|
||||
delay_token_1.reset();
|
||||
ASSERT_EQ(false, controller.IsStopped());
|
||||
ASSERT_FALSE(controller.IsStopped());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -92,6 +92,12 @@ class Cache {
|
||||
// its cache keys.
|
||||
virtual uint64_t NewId() = 0;
|
||||
|
||||
// sets the maximum configured capacity of the cache. When the new
|
||||
// capacity is less than the old capacity and the existing usage is
|
||||
// greater than new capacity, the implementation will do its best job to
|
||||
// purge the released entries from the cache in order to lower the usage
|
||||
virtual void SetCapacity(size_t capacity) = 0;
|
||||
|
||||
// returns the maximum configured capacity of the cache
|
||||
virtual size_t GetCapacity() const = 0;
|
||||
|
||||
|
||||
+32
-1
@@ -331,6 +331,32 @@ class DB {
|
||||
// See version_set.h for details. More live versions often mean more SST
|
||||
// files are held from being deleted, by iterators or unfinished
|
||||
// compactions.
|
||||
#ifndef ROCKSDB_LITE
|
||||
struct Properties {
|
||||
static const std::string kNumFilesAtLevelPrefix;
|
||||
static const std::string kStats;
|
||||
static const std::string kSSTables;
|
||||
static const std::string kCFStats;
|
||||
static const std::string kDBStats;
|
||||
static const std::string kNumImmutableMemTable;
|
||||
static const std::string kMemTableFlushPending;
|
||||
static const std::string kCompactionPending;
|
||||
static const std::string kBackgroundErrors;
|
||||
static const std::string kCurSizeActiveMemTable;
|
||||
static const std::string kCurSizeAllMemTables;
|
||||
static const std::string kNumEntriesActiveMemTable;
|
||||
static const std::string kNumEntriesImmMemTables;
|
||||
static const std::string kNumDeletesActiveMemTable;
|
||||
static const std::string kNumDeletesImmMemTables;
|
||||
static const std::string kEstimateNumKeys;
|
||||
static const std::string kEstimateTableReadersMem;
|
||||
static const std::string kIsFileDeletionsEnabled;
|
||||
static const std::string kNumSnapshots;
|
||||
static const std::string kOldestSnapshotTime;
|
||||
static const std::string kNumLiveVersions;
|
||||
};
|
||||
#endif /* ROCKSDB_LITE */
|
||||
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) = 0;
|
||||
virtual bool GetProperty(const Slice& property, std::string* value) {
|
||||
@@ -459,13 +485,18 @@ class DB {
|
||||
// Get Env object from the DB
|
||||
virtual Env* GetEnv() const = 0;
|
||||
|
||||
// Get DB Options that we use
|
||||
// Get DB Options that we use. During the process of opening the
|
||||
// column family, the options provided when calling DB::Open() or
|
||||
// DB::CreateColumnFamily() will have been "sanitized" and transformed
|
||||
// in an implementation-defined manner.
|
||||
virtual const Options& GetOptions(ColumnFamilyHandle* column_family)
|
||||
const = 0;
|
||||
virtual const Options& GetOptions() const {
|
||||
return GetOptions(DefaultColumnFamily());
|
||||
}
|
||||
|
||||
virtual const DBOptions& GetDBOptions() const = 0;
|
||||
|
||||
// Flush all mem-table data.
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) = 0;
|
||||
|
||||
@@ -300,7 +300,8 @@ class Env {
|
||||
// OptimizeForLogWrite will create a new EnvOptions object that is a copy of
|
||||
// the EnvOptions in the parameters, but is optimized for writing log files.
|
||||
// Default implementation returns the copy of the same object.
|
||||
virtual EnvOptions OptimizeForLogWrite(const EnvOptions& env_options) const;
|
||||
virtual EnvOptions OptimizeForLogWrite(const EnvOptions& env_options,
|
||||
const DBOptions& db_options) const;
|
||||
// OptimizeForManifestWrite will create a new EnvOptions object that is a copy
|
||||
// of the EnvOptions in the parameters, but is optimized for writing manifest
|
||||
// files. Default implementation returns the copy of the same object.
|
||||
@@ -532,6 +533,8 @@ class WritableFile {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
size_t preallocation_block_size() { return preallocation_block_size_; }
|
||||
|
||||
private:
|
||||
size_t last_preallocated_block_;
|
||||
size_t preallocation_block_size_;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2014, 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 "rocksdb/db.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace experimental {
|
||||
|
||||
// Supported only for Leveled compaction
|
||||
Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end);
|
||||
Status SuggestCompactRange(DB* db, const Slice* begin, const Slice* end);
|
||||
|
||||
// Move all L0 files to target_level skipping compaction.
|
||||
// This operation succeeds only if the files in L0 have disjoint ranges; this
|
||||
// is guaranteed to happen, for instance, if keys are inserted in sorted
|
||||
// order. Furthermore, all levels between 1 and target_level must be empty.
|
||||
// If any of the above condition is violated, InvalidArgument will be
|
||||
// returned.
|
||||
Status PromoteL0(DB* db, ColumnFamilyHandle* column_family,
|
||||
int target_level = 1);
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace rocksdb
|
||||
@@ -45,6 +45,18 @@ struct CompactionJobInfo {
|
||||
// actual thread that involves in that specific event. For example, it
|
||||
// is the RocksDB background flush thread that does the actual flush to
|
||||
// call EventListener::OnFlushCompleted().
|
||||
//
|
||||
// [Locking] All EventListener callbacks are designed to be called without
|
||||
// the current thread holding any DB mutex. This is to prevent potential
|
||||
// deadlock and performance issue when using EventListener callback
|
||||
// in a complex way. However, all EventListener call-back functions
|
||||
// should not run for an extended period of time before the function
|
||||
// returns, otherwise RocksDB may be blocked. For example, it is not
|
||||
// suggested to do DB::CompactFiles() (as it may run for a long while)
|
||||
// or issue many of DB::Put() (as Put may be blocked in certain cases)
|
||||
// in the same thread in the EventListener callback. However, doing
|
||||
// DB::CompactFiles() and DB::Put() in a thread other than the
|
||||
// EventListener callback thread is considered safe.
|
||||
class EventListener {
|
||||
public:
|
||||
// A call-back function to RocksDB which will be called whenever a
|
||||
|
||||
@@ -84,7 +84,9 @@ class MemTableRep {
|
||||
virtual bool Contains(const char* key) const = 0;
|
||||
|
||||
// Notify this table rep that it will no longer be added to. By default, does
|
||||
// nothing.
|
||||
// nothing. After MarkReadOnly() is called, this table rep will not be
|
||||
// written to (ie No more calls to Allocate(), Insert(), or any writes done
|
||||
// directly to entries accessed through the iterator.)
|
||||
virtual void MarkReadOnly() { }
|
||||
|
||||
// Look up key from the mem table, since the first key in the mem table whose
|
||||
|
||||
@@ -55,6 +55,12 @@ enum CompressionType : char {
|
||||
kBZip2Compression = 0x3, kLZ4Compression = 0x4, kLZ4HCCompression = 0x5
|
||||
};
|
||||
|
||||
// returns true if RocksDB was correctly linked with compression library and
|
||||
// supports the compression type
|
||||
extern bool CompressionTypeSupported(CompressionType compression_type);
|
||||
// Returns a human-readable name of the compression type
|
||||
extern const char* CompressionTypeToString(CompressionType compression_type);
|
||||
|
||||
enum CompactionStyle : char {
|
||||
// level based compaction style
|
||||
kCompactionStyleLevel = 0x0,
|
||||
@@ -699,6 +705,10 @@ struct ColumnFamilyOptions {
|
||||
// Default: false
|
||||
bool optimize_filters_for_hits;
|
||||
|
||||
// After writing every SST file, reopen it and read all the keys.
|
||||
// Default: false
|
||||
bool paranoid_file_checks;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// A vector of EventListeners which call-back functions will be called
|
||||
// when specific RocksDB event happens.
|
||||
@@ -991,8 +1001,14 @@ struct DBOptions {
|
||||
// You may consider using rate_limiter to regulate write rate to device.
|
||||
// When rate limiter is enabled, it automatically enables bytes_per_sync
|
||||
// to 1MB.
|
||||
//
|
||||
// This option applies to table files
|
||||
uint64_t bytes_per_sync;
|
||||
|
||||
// Same as bytes_per_sync, but applies to WAL files
|
||||
// Default: 0, turned off
|
||||
uint64_t wal_bytes_per_sync;
|
||||
|
||||
// If true, then the status of the threads involved in this DB will
|
||||
// be tracked and available via GetThreadList() API.
|
||||
//
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#define STORAGE_ROCKSDB_INCLUDE_SLICE_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <cstdio>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
@@ -240,7 +240,7 @@ enum Histograms : uint32_t {
|
||||
NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
DB_SEEK,
|
||||
WRITE_STALL,
|
||||
HISTOGRAM_ENUM_MAX,
|
||||
HISTOGRAM_ENUM_MAX, // TODO(ldemailly): enforce HistogramsNameMap match
|
||||
};
|
||||
|
||||
const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
|
||||
@@ -263,6 +263,7 @@ const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
|
||||
{ SOFT_RATE_LIMIT_DELAY_COUNT, "rocksdb.soft.rate.limit.delay.count"},
|
||||
{ NUM_FILES_IN_SINGLE_COMPACTION, "rocksdb.numfiles.in.singlecompaction" },
|
||||
{ DB_SEEK, "rocksdb.db.seek.micros" },
|
||||
{ WRITE_STALL, "rocksdb.db.write.stall" },
|
||||
};
|
||||
|
||||
struct HistogramData {
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace rocksdb {
|
||||
// -- Block-based Table
|
||||
class FlushBlockPolicyFactory;
|
||||
class RandomAccessFile;
|
||||
struct TableBuilderOptions;
|
||||
class TableBuilder;
|
||||
class TableReader;
|
||||
class WritableFile;
|
||||
@@ -370,11 +371,8 @@ class TableFactory {
|
||||
// after closing the table builder. compression_type is the compression type
|
||||
// to use in this table.
|
||||
virtual TableBuilder* NewTableBuilder(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator& internal_comparator, WritableFile* file,
|
||||
const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
const bool skipFilters = false) const = 0;
|
||||
const TableBuilderOptions& table_builder_options,
|
||||
WritableFile* file) const = 0;
|
||||
|
||||
// Sanitizes the specified DB Options and ColumnFamilyOptions.
|
||||
//
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/types.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -77,6 +78,13 @@ struct TablePropertiesNames {
|
||||
|
||||
extern const std::string kPropertiesBlock;
|
||||
|
||||
enum EntryType {
|
||||
kEntryPut,
|
||||
kEntryDelete,
|
||||
kEntryMerge,
|
||||
kEntryOther,
|
||||
};
|
||||
|
||||
// `TablePropertiesCollector` provides the mechanism for users to collect
|
||||
// their own interested properties. This class is essentially a collection
|
||||
// of callback functions that will be invoked during table building.
|
||||
@@ -87,10 +95,27 @@ class TablePropertiesCollector {
|
||||
public:
|
||||
virtual ~TablePropertiesCollector() {}
|
||||
|
||||
// DEPRECATE User defined collector should implement AddUserKey(), though
|
||||
// this old function still works for backward compatible reason.
|
||||
// Add() will be called when a new key/value pair is inserted into the table.
|
||||
// @params key the original key that is inserted into the table.
|
||||
// @params value the original value that is inserted into the table.
|
||||
virtual Status Add(const Slice& key, const Slice& value) = 0;
|
||||
// @params key the user key that is inserted into the table.
|
||||
// @params value the value that is inserted into the table.
|
||||
virtual Status Add(const Slice& key, const Slice& value) {
|
||||
return Status::InvalidArgument(
|
||||
"TablePropertiesCollector::Add() deprecated.");
|
||||
}
|
||||
|
||||
// AddUserKey() will be called when a new key/value pair is inserted into the
|
||||
// table.
|
||||
// @params key the user key that is inserted into the table.
|
||||
// @params value the value that is inserted into the table.
|
||||
// @params file_size file size up to now
|
||||
virtual Status AddUserKey(const Slice& key, const Slice& value,
|
||||
EntryType type, SequenceNumber seq,
|
||||
uint64_t file_size) {
|
||||
// For backward-compatible.
|
||||
return Add(key, value);
|
||||
}
|
||||
|
||||
// Finish() will be called when a table has already been built and is ready
|
||||
// for writing the properties block.
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef ROCKSDB_USING_THREAD_STATUS
|
||||
#define ROCKSDB_USING_THREAD_STATUS \
|
||||
@@ -26,6 +29,10 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// TODO(yhchiang): remove this function once c++14 is available
|
||||
// as std::max will be able to cover this.
|
||||
constexpr int constexpr_max(int a, int b) { return a > b ? a : b; }
|
||||
|
||||
// A structure that describes the current status of a thread.
|
||||
// The status of active threads can be fetched using
|
||||
// rocksdb::GetThreadList().
|
||||
@@ -64,6 +71,28 @@ struct ThreadStatus {
|
||||
NUM_OP_STAGES
|
||||
};
|
||||
|
||||
enum CompactionPropertyType : int {
|
||||
COMPACTION_JOB_ID = 0,
|
||||
COMPACTION_INPUT_OUTPUT_LEVEL,
|
||||
COMPACTION_PROP_FLAGS,
|
||||
COMPACTION_TOTAL_INPUT_BYTES,
|
||||
COMPACTION_BYTES_READ,
|
||||
COMPACTION_BYTES_WRITTEN,
|
||||
NUM_COMPACTION_PROPERTIES
|
||||
};
|
||||
|
||||
enum FlushPropertyType : int {
|
||||
FLUSH_JOB_ID = 0,
|
||||
FLUSH_BYTES_MEMTABLES,
|
||||
FLUSH_BYTES_WRITTEN,
|
||||
NUM_FLUSH_PROPERTIES
|
||||
};
|
||||
|
||||
// The maximum number of properties of an operation.
|
||||
// This number should be set to the biggest NUM_XXX_PROPERTIES.
|
||||
static const int kNumOperationProperties =
|
||||
constexpr_max(NUM_COMPACTION_PROPERTIES, NUM_FLUSH_PROPERTIES);
|
||||
|
||||
// The type used to refer to a thread state.
|
||||
// A state describes lower-level action of a thread
|
||||
// such as reading / writing a file or waiting for a mutex.
|
||||
@@ -80,6 +109,7 @@ struct ThreadStatus {
|
||||
const OperationType _operation_type,
|
||||
const uint64_t _op_elapsed_micros,
|
||||
const OperationStage _operation_stage,
|
||||
const uint64_t _op_props[],
|
||||
const StateType _state_type) :
|
||||
thread_id(_id), thread_type(_thread_type),
|
||||
db_name(_db_name),
|
||||
@@ -87,7 +117,11 @@ struct ThreadStatus {
|
||||
operation_type(_operation_type),
|
||||
op_elapsed_micros(_op_elapsed_micros),
|
||||
operation_stage(_operation_stage),
|
||||
state_type(_state_type) {}
|
||||
state_type(_state_type) {
|
||||
for (int i = 0; i < kNumOperationProperties; ++i) {
|
||||
op_properties[i] = _op_props[i];
|
||||
}
|
||||
}
|
||||
|
||||
// An unique ID for the thread.
|
||||
const uint64_t thread_id;
|
||||
@@ -116,6 +150,11 @@ struct ThreadStatus {
|
||||
// in the current operation.
|
||||
const OperationStage operation_stage;
|
||||
|
||||
// A list of properties that describe some details about the current
|
||||
// operation. Same field in op_properties[] might have different
|
||||
// meanings for different operations.
|
||||
uint64_t op_properties[kNumOperationProperties];
|
||||
|
||||
// The state (lower-level action) that the current thread is involved.
|
||||
const StateType state_type;
|
||||
|
||||
@@ -133,6 +172,17 @@ struct ThreadStatus {
|
||||
static const std::string& GetOperationStageName(
|
||||
OperationStage stage);
|
||||
|
||||
// Obtain the name of the "i"th operation property of the
|
||||
// specified operation.
|
||||
static const std::string& GetOperationPropertyName(
|
||||
OperationType op_type, int i);
|
||||
|
||||
// Translate the "i"th property of the specified operation given
|
||||
// a property value.
|
||||
static std::map<std::string, uint64_t>
|
||||
InterpretOperationProperties(
|
||||
OperationType op_type, const uint64_t* op_properties);
|
||||
|
||||
// Obtain the name of a state given its type.
|
||||
static const std::string& GetStateName(StateType state_type);
|
||||
};
|
||||
|
||||
@@ -56,7 +56,8 @@ Status GetBlockBasedTableOptionsFromString(
|
||||
Status GetOptionsFromString(const Options& base_options,
|
||||
const std::string& opts_str, Options* new_options);
|
||||
|
||||
void CancelAllBackgroundWork(DB* db);
|
||||
/// Request stopping background work, if wait is true wait until it's done
|
||||
void CancelAllBackgroundWork(DB* db, bool wait = false);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2014, 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/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// This API is experimental. We will mark it stable once we run it in production
|
||||
// for a while.
|
||||
// NewFlashcacheAwareEnv() creates and Env that blacklists all background
|
||||
// threads (used for flush and compaction) from using flashcache to cache their
|
||||
// reads. Reads from compaction thread don't need to be cached because they are
|
||||
// going to be soon made obsolete (due to nature of compaction)
|
||||
// Usually you would pass Env::Default() as base.
|
||||
// cachedev_fd is a file descriptor of the flashcache device. Caller has to
|
||||
// open flashcache device before calling this API.
|
||||
extern std::unique_ptr<Env> NewFlashcacheAwareEnv(
|
||||
Env* base, const int cachedev_fd);
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -175,6 +175,11 @@ class StackableDB : public DB {
|
||||
return db_->GetOptions(column_family);
|
||||
}
|
||||
|
||||
using DB::GetDBOptions;
|
||||
virtual const DBOptions& GetDBOptions() const override {
|
||||
return db_->GetDBOptions();
|
||||
}
|
||||
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const FlushOptions& fopts,
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/slice.h"
|
||||
@@ -22,6 +24,9 @@ namespace rocksdb {
|
||||
|
||||
class ColumnFamilyHandle;
|
||||
class Comparator;
|
||||
class DB;
|
||||
struct ReadOptions;
|
||||
struct DBOptions;
|
||||
|
||||
enum WriteType { kPutRecord, kMergeRecord, kDeleteRecord, kLogDataRecord };
|
||||
|
||||
@@ -118,6 +123,37 @@ class WriteBatchWithIndex : public WriteBatchBase {
|
||||
// default column family
|
||||
Iterator* NewIteratorWithBase(Iterator* base_iterator);
|
||||
|
||||
// Similar to DB::Get() but will only read the key from this batch.
|
||||
// If the batch does not have enough data to resolve Merge operations,
|
||||
// MergeInProgress status may be returned.
|
||||
Status GetFromBatch(ColumnFamilyHandle* column_family,
|
||||
const DBOptions& options, const Slice& key,
|
||||
std::string* value);
|
||||
|
||||
// Similar to previous function but does not require a column_family.
|
||||
// Note: An InvalidArgument status will be returned if there are any Merge
|
||||
// operators for this key.
|
||||
Status GetFromBatch(const DBOptions& options, const Slice& key,
|
||||
std::string* value) {
|
||||
return GetFromBatch(nullptr, options, key, value);
|
||||
}
|
||||
|
||||
// Similar to DB::Get() but will also read writes from this batch.
|
||||
//
|
||||
// This function will query both this batch and the DB and then merge
|
||||
// the results using the DB's merge operator (if the batch contains any
|
||||
// merge requests).
|
||||
//
|
||||
// Setting read_options.snapshot will affect what is read from the DB
|
||||
// but will NOT change which keys are read from the batch (the keys in
|
||||
// this batch do not yet belong to any snapshot and will be fetched
|
||||
// regardless).
|
||||
Status GetFromBatchAndDB(DB* db, const ReadOptions& read_options,
|
||||
const Slice& key, std::string* value);
|
||||
Status GetFromBatchAndDB(DB* db, const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value);
|
||||
|
||||
private:
|
||||
struct Rep;
|
||||
Rep* rep;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#define ROCKSDB_MAJOR 3
|
||||
#define ROCKSDB_MINOR 10
|
||||
#define ROCKSDB_MINOR 11
|
||||
#define ROCKSDB_PATCH 0
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
|
||||
@@ -94,7 +94,7 @@ class Stats {
|
||||
|
||||
void stop() {
|
||||
finish_ = System.nanoTime();
|
||||
seconds_ = (double) (finish_ - start_) / 1000000;
|
||||
seconds_ = (double) (finish_ - start_) * 1e-9;
|
||||
}
|
||||
|
||||
void addMessage(String msg) {
|
||||
@@ -140,15 +140,15 @@ class Stats {
|
||||
if (bytes_ > 0) {
|
||||
// Rate is computed on actual elapsed time, not the sum of per-thread
|
||||
// elapsed times.
|
||||
double elapsed = (finish_ - start_) * 1e-6;
|
||||
double elapsed = (finish_ - start_) * 1e-9;
|
||||
extra.append(String.format("%6.1f MB/s", (bytes_ / 1048576.0) / elapsed));
|
||||
}
|
||||
extra.append(message_.toString());
|
||||
double elapsed = (finish_ - start_) * 1e-6;
|
||||
double throughput = (double) done_ / elapsed;
|
||||
double elapsed = (finish_ - start_);
|
||||
double throughput = (double) done_ / (elapsed * 1e-9);
|
||||
|
||||
System.out.format("%-12s : %11.3f micros/op %d ops/sec;%s%s\n",
|
||||
name, elapsed * 1e6 / done_,
|
||||
name, (elapsed * 1e-6) / done_,
|
||||
(long) throughput, (extra.length() == 0 ? "" : " "), extra.toString());
|
||||
}
|
||||
}
|
||||
@@ -541,8 +541,6 @@ public class DbBenchmark {
|
||||
(Integer)flags_.get(Flag.max_background_flushes));
|
||||
options.setMaxOpenFiles(
|
||||
(Integer)flags_.get(Flag.open_files));
|
||||
options.setTableCacheRemoveScanCountLimit(
|
||||
(Integer)flags_.get(Flag.cache_remove_scan_count_limit));
|
||||
options.setDisableDataSync(
|
||||
(Boolean)flags_.get(Flag.disable_data_sync));
|
||||
options.setUseFsync(
|
||||
@@ -1200,11 +1198,6 @@ public class DbBenchmark {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
},
|
||||
cache_remove_scan_count_limit(32,"") {
|
||||
@Override public Object parseValue(String value) {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
},
|
||||
verify_checksum(false,"Verify checksum for every block read\n" +
|
||||
"\tfrom storage.") {
|
||||
@Override public Object parseValue(String value) {
|
||||
|
||||
@@ -695,27 +695,6 @@ void Java_org_rocksdb_Options_setTableCacheNumshardbits(
|
||||
static_cast<int>(table_cache_numshardbits);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: tableCacheRemoveScanCountLimit
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_Options_tableCacheRemoveScanCountLimit(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
// deprecated
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setTableCacheRemoveScanCountLimit
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setTableCacheRemoveScanCountLimit(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jint limit) {
|
||||
// deprecated
|
||||
}
|
||||
|
||||
/*
|
||||
* Method: useFixedLengthPrefixExtractor
|
||||
* Signature: (JI)V
|
||||
@@ -890,28 +869,6 @@ void Java_org_rocksdb_Options_setIsFdCloseOnExec(
|
||||
static_cast<bool>(is_fd_close_on_exec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: skipLogErrorOnRecovery
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_Options_skipLogErrorOnRecovery(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)
|
||||
->skip_log_error_on_recovery;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setSkipLogErrorOnRecovery
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setSkipLogErrorOnRecovery(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jboolean skip) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->skip_log_error_on_recovery =
|
||||
static_cast<bool>(skip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: statsDumpPeriodSec
|
||||
@@ -3523,27 +3480,6 @@ jint Java_org_rocksdb_DBOptions_tableCacheNumshardbits(
|
||||
table_cache_numshardbits;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: setTableCacheRemoveScanCountLimit
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_DBOptions_setTableCacheRemoveScanCountLimit(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jint limit) {
|
||||
// deprecated
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: tableCacheRemoveScanCountLimit
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_DBOptions_tableCacheRemoveScanCountLimit(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
// deprecated
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: setWalTtlSeconds
|
||||
@@ -3697,28 +3633,6 @@ jboolean Java_org_rocksdb_DBOptions_isFdCloseOnExec(
|
||||
return reinterpret_cast<rocksdb::DBOptions*>(jhandle)->is_fd_close_on_exec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: setSkipLogErrorOnRecovery
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_DBOptions_setSkipLogErrorOnRecovery(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jboolean skip) {
|
||||
reinterpret_cast<rocksdb::DBOptions*>(jhandle)->skip_log_error_on_recovery =
|
||||
static_cast<bool>(skip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: skipLogErrorOnRecovery
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_DBOptions_skipLogErrorOnRecovery(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::DBOptions*>(jhandle)
|
||||
->skip_log_error_on_recovery;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: setStatsDumpPeriodSec
|
||||
|
||||
@@ -11,19 +11,6 @@ package org.rocksdb;
|
||||
*/
|
||||
public class ColumnFamilyDescriptor {
|
||||
|
||||
/**
|
||||
* <p>Creates a new Column Family using a name and default
|
||||
* options,</p>
|
||||
*
|
||||
* @param columnFamilyName name of column family.
|
||||
* @deprecated will be removed in RocksDB 3.10.0. Use
|
||||
* {@link #ColumnFamilyDescriptor(byte[])} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public ColumnFamilyDescriptor(final String columnFamilyName){
|
||||
this(columnFamilyName.getBytes(), new ColumnFamilyOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new Column Family using a name and default
|
||||
* options,</p>
|
||||
@@ -35,22 +22,6 @@ public class ColumnFamilyDescriptor {
|
||||
this(columnFamilyName, new ColumnFamilyOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new Column Family using a name and custom
|
||||
* options.</p>
|
||||
*
|
||||
* @param columnFamilyName name of column family.
|
||||
* @param columnFamilyOptions options to be used with
|
||||
* column family.
|
||||
* @deprecated will be removed in RocksDB 3.10.0. Use
|
||||
* {@link #ColumnFamilyDescriptor(byte[], ColumnFamilyOptions)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public ColumnFamilyDescriptor(final String columnFamilyName,
|
||||
final ColumnFamilyOptions columnFamilyOptions) {
|
||||
this(columnFamilyName.getBytes(), columnFamilyOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new Column Family using a name and custom
|
||||
* options.</p>
|
||||
|
||||
@@ -382,20 +382,6 @@ public class DBOptions extends RocksObject implements DBOptionsInterface {
|
||||
return tableCacheNumshardbits(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBOptions setTableCacheRemoveScanCountLimit(
|
||||
final int limit) {
|
||||
assert(isInitialized());
|
||||
setTableCacheRemoveScanCountLimit(nativeHandle_, limit);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int tableCacheRemoveScanCountLimit() {
|
||||
assert(isInitialized());
|
||||
return tableCacheRemoveScanCountLimit(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBOptions setWalTtlSeconds(
|
||||
final long walTtlSeconds) {
|
||||
@@ -494,22 +480,6 @@ public class DBOptions extends RocksObject implements DBOptionsInterface {
|
||||
return isFdCloseOnExec(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public DBOptions setSkipLogErrorOnRecovery(
|
||||
final boolean skip) {
|
||||
assert(isInitialized());
|
||||
setSkipLogErrorOnRecovery(nativeHandle_, skip);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean skipLogErrorOnRecovery() {
|
||||
assert(isInitialized());
|
||||
return skipLogErrorOnRecovery(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBOptions setStatsDumpPeriodSec(
|
||||
final int statsDumpPeriodSec) {
|
||||
@@ -648,9 +618,6 @@ public class DBOptions extends RocksObject implements DBOptionsInterface {
|
||||
private native void setTableCacheNumshardbits(
|
||||
long handle, int tableCacheNumshardbits);
|
||||
private native int tableCacheNumshardbits(long handle);
|
||||
private native void setTableCacheRemoveScanCountLimit(
|
||||
long handle, int limit);
|
||||
private native int tableCacheRemoveScanCountLimit(long handle);
|
||||
private native void setWalTtlSeconds(long handle, long walTtlSeconds);
|
||||
private native long walTtlSeconds(long handle);
|
||||
private native void setWalSizeLimitMB(long handle, long sizeLimitMB);
|
||||
@@ -670,9 +637,6 @@ public class DBOptions extends RocksObject implements DBOptionsInterface {
|
||||
private native void setIsFdCloseOnExec(
|
||||
long handle, boolean isFdCloseOnExec);
|
||||
private native boolean isFdCloseOnExec(long handle);
|
||||
private native void setSkipLogErrorOnRecovery(
|
||||
long handle, boolean skip);
|
||||
private native boolean skipLogErrorOnRecovery(long handle);
|
||||
private native void setStatsDumpPeriodSec(
|
||||
long handle, int statsDumpPeriodSec);
|
||||
private native int statsDumpPeriodSec(long handle);
|
||||
|
||||
@@ -505,39 +505,6 @@ public interface DBOptionsInterface {
|
||||
*/
|
||||
int tableCacheNumshardbits();
|
||||
|
||||
/**
|
||||
* During data eviction of table's LRU cache, it would be inefficient
|
||||
* to strictly follow LRU because this piece of memory will not really
|
||||
* be released unless its refcount falls to zero. Instead, make two
|
||||
* passes: the first pass will release items with refcount = 1,
|
||||
* and if not enough space releases after scanning the number of
|
||||
* elements specified by this parameter, we will remove items in LRU
|
||||
* order.
|
||||
*
|
||||
* @param limit scan count limit
|
||||
* @return the instance of the current Object.
|
||||
*
|
||||
* @deprecated This function is depreciated.
|
||||
*/
|
||||
@Deprecated
|
||||
Object setTableCacheRemoveScanCountLimit(int limit);
|
||||
|
||||
/**
|
||||
* During data eviction of table's LRU cache, it would be inefficient
|
||||
* to strictly follow LRU because this piece of memory will not really
|
||||
* be released unless its refcount falls to zero. Instead, make two
|
||||
* passes: the first pass will release items with refcount = 1,
|
||||
* and if not enough space releases after scanning the number of
|
||||
* elements specified by this parameter, we will remove items in LRU
|
||||
* order.
|
||||
*
|
||||
* @return scan count limit
|
||||
*
|
||||
* @deprecated This function is depreciated.
|
||||
*/
|
||||
@Deprecated
|
||||
int tableCacheRemoveScanCountLimit();
|
||||
|
||||
/**
|
||||
* {@link #walTtlSeconds()} and {@link #walSizeLimitMB()} affect how archived logs
|
||||
* will be deleted.
|
||||
@@ -715,31 +682,6 @@ public interface DBOptionsInterface {
|
||||
*/
|
||||
boolean isFdCloseOnExec();
|
||||
|
||||
/**
|
||||
* Skip log corruption error on recovery (If client is ok with
|
||||
* losing most recent changes)
|
||||
* Default: false
|
||||
*
|
||||
* @param skip true if log corruption errors are skipped during recovery.
|
||||
* @return the instance of the current Object.
|
||||
*
|
||||
* @deprecated will be removed in RocksDB 3.11.0. Not used anymore.
|
||||
*/
|
||||
@Deprecated
|
||||
Object setSkipLogErrorOnRecovery(boolean skip);
|
||||
|
||||
/**
|
||||
* Skip log corruption error on recovery (If client is ok with
|
||||
* losing most recent changes)
|
||||
* Default: false
|
||||
*
|
||||
* @return true if log corruption errors are skipped during recovery.
|
||||
*
|
||||
* @deprecated will be removed in RocksDB 3.11.0. Not used anymore.
|
||||
*/
|
||||
@Deprecated
|
||||
boolean skipLogErrorOnRecovery();
|
||||
|
||||
/**
|
||||
* if not zero, dump rocksdb.stats to LOG every stats_dump_period_sec
|
||||
* Default: 3600 (1 hour)
|
||||
|
||||
Loaded 100 of 200 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user