mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22c7a01c2c | ||
|
|
f0db718ac7 | ||
|
|
495253ca73 | ||
|
|
cc34f73a08 | ||
|
|
7affe40309 | ||
|
|
8d87467bb0 | ||
|
|
64d302d304 | ||
|
|
cd5c0925ac | ||
|
|
c4bf07c245 | ||
|
|
e526b71402 | ||
|
|
0c2be0de38 | ||
|
|
5fd33d26f1 | ||
|
|
9f20395cd6 | ||
|
|
c02338a698 | ||
|
|
367a3f9cb4 | ||
|
|
b52b144c73 | ||
|
|
ac95ae1b5d | ||
|
|
59d5497989 | ||
|
|
ea18b944a7 | ||
|
|
2ea1219eb6 | ||
|
|
e4211d10c1 | ||
|
|
29a9161f34 | ||
|
|
fd24ae9d05 | ||
|
|
b1267750fb | ||
|
|
83bf09144b | ||
|
|
da5daa061c | ||
|
|
b0cda4a116 | ||
|
|
8810850dd4 | ||
|
|
71783f6524 | ||
|
|
614bbcbe2c | ||
|
|
d8e1196635 | ||
|
|
2a019f1d03 | ||
|
|
469d474ba0 | ||
|
|
ac6afaf9ef | ||
|
|
09899f0b51 | ||
|
|
636e57b52d | ||
|
|
30ca3752ba | ||
|
|
051c67ff53 | ||
|
|
d0e7e49ae6 | ||
|
|
94e31ac227 | ||
|
|
b060d30065 | ||
|
|
9fd65e5667 | ||
|
|
8e79ce68ce | ||
|
|
45a612f990 | ||
|
|
27129c739f | ||
|
|
86905e3cbb | ||
|
|
74eb4fbe93 | ||
|
|
8ddddd62d0 | ||
|
|
46c14c6661 | ||
|
|
72cb7cf201 | ||
|
|
0e526eb9d7 | ||
|
|
8db24f4b35 | ||
|
|
5594d446ff | ||
|
|
82e3ae5403 | ||
|
|
bc9f36fd5e | ||
|
|
c645250ee0 | ||
|
|
f7e6c856ab | ||
|
|
c76dcb44d4 | ||
|
|
b452dede5c | ||
|
|
29d83cc33a | ||
|
|
c1a924b9f0 | ||
|
|
7e01d12026 | ||
|
|
9f7fc3ac45 | ||
|
|
98849a35fa | ||
|
|
4d2ba38b65 | ||
|
|
2b1f23dcae | ||
|
|
ccaf1aa7cb | ||
|
|
a29118ffc7 | ||
|
|
85b04ca765 | ||
|
|
df7abb4e8d | ||
|
|
171be0ed55 | ||
|
|
39464a990f | ||
|
|
0f7f3b8605 | ||
|
|
73605d9179 | ||
|
|
fda592d902 | ||
|
|
c73d13bb81 | ||
|
|
b011e201fa | ||
|
|
2c1bd8846f | ||
|
|
e770d61751 | ||
|
|
41af0f56b0 | ||
|
|
c5db7f2605 | ||
|
|
37e9b63701 | ||
|
|
217cc217d7 | ||
|
|
635905481d | ||
|
|
fd95745a59 | ||
|
|
c3dd0f75da | ||
|
|
6afafa3694 | ||
|
|
e7ad69b9fe | ||
|
|
bbd9c53457 | ||
|
|
065766b8d2 | ||
|
|
5c82a8837e | ||
|
|
86de2007b8 | ||
|
|
17be187ff9 | ||
|
|
c2999f54bd | ||
|
|
76d1c28e82 | ||
|
|
01e6f85098 | ||
|
|
082e49ba82 | ||
|
|
c4b468000b | ||
|
|
34d436b7db | ||
|
|
cda9943f9f | ||
|
|
7b3a618f94 | ||
|
|
2d4fe048f4 | ||
|
|
9ab0132360 | ||
|
|
76d54530d3 | ||
|
|
44f0ff31c2 | ||
|
|
97451f837e | ||
|
|
e130e88bc6 | ||
|
|
34f3c5a20f | ||
|
|
cdc7230e4c | ||
|
|
5a921b8950 | ||
|
|
c9c935923e | ||
|
|
2110e43a5a | ||
|
|
412b7f85bb | ||
|
|
c082853340 | ||
|
|
abac3d6476 | ||
|
|
fb3f8ffe5e | ||
|
|
60fa7d1365 | ||
|
|
3772a3d09d | ||
|
|
c49dedbe04 | ||
|
|
a39e931e50 | ||
|
|
efa2fb33b0 | ||
|
|
f7c9730696 | ||
|
|
7e12ae5a21 | ||
|
|
eb357af58c | ||
|
|
f981e08139 | ||
|
|
834c67d77f | ||
|
|
a28b3c4388 | ||
|
|
5187d896b9 | ||
|
|
75d7e2c374 | ||
|
|
db52419cf0 | ||
|
|
45e756f04a | ||
|
|
f94f1a97d7 | ||
|
|
b08c39e14f | ||
|
|
b680033e63 | ||
|
|
56ef2caaa5 | ||
|
|
c1c68bce43 | ||
|
|
7c303f0e78 | ||
|
|
179c23021e | ||
|
|
48842ab316 | ||
|
|
f37048ad1d | ||
|
|
679a9671fe | ||
|
|
714c63c584 | ||
|
|
f1841985e4 | ||
|
|
a04929aa49 | ||
|
|
a1bae76c87 | ||
|
|
b8ce526487 | ||
|
|
bc3bc4bc2f | ||
|
|
42f0eaceb7 | ||
|
|
965d9d50b8 | ||
|
|
001ce64dc7 | ||
|
|
240ed0cd7b | ||
|
|
724fba2b39 | ||
|
|
720c1c056d | ||
|
|
b794194ade | ||
|
|
122f98e0b9 | ||
|
|
1fee591e74 | ||
|
|
574028679b | ||
|
|
bd4fbaee37 | ||
|
|
1eb545721d | ||
|
|
9aa9668a83 | ||
|
|
4b1786e959 | ||
|
|
05b2e60ddd | ||
|
|
9383922cc3 | ||
|
|
90f156402c | ||
|
|
2717422e21 | ||
|
|
3b5fe3a1f3 | ||
|
|
c584d2b538 | ||
|
|
2a8e5203d8 | ||
|
|
d755e53b87 | ||
|
|
839c376bd1 | ||
|
|
0fd985f427 | ||
|
|
e11a5e776f | ||
|
|
6398e6a6a5 | ||
|
|
0d3145198e | ||
|
|
a6fb7f312d | ||
|
|
c63494fb61 | ||
|
|
5e25274110 | ||
|
|
25641bfc9c | ||
|
|
fc12cb83f2 | ||
|
|
d6fe8dacc8 | ||
|
|
700f6ec3ff | ||
|
|
5bfb7f5d0b | ||
|
|
ff8f74c204 | ||
|
|
2dd9bfe3a8 | ||
|
|
dbcfe27d6c | ||
|
|
6c66918645 | ||
|
|
5db9e76644 | ||
|
|
f4363fb81c | ||
|
|
8f01bf80cc | ||
|
|
ee80fb4b4a | ||
|
|
c12f571d31 | ||
|
|
d2e60f5cee | ||
|
|
274dc81c92 | ||
|
|
d6c8dba727 | ||
|
|
4d5708aa56 | ||
|
|
065a67c4f0 | ||
|
|
dc50a1a593 | ||
|
|
6a150c0118 | ||
|
|
bafbc23baa | ||
|
|
ca250d71a1 | ||
|
|
5cc9adf5ba | ||
|
|
7658bcc1e5 | ||
|
|
1b97934a2c | ||
|
|
2ef3ed86f3 | ||
|
|
a40ce219b9 | ||
|
|
cc6c883f59 | ||
|
|
4942f6efb6 | ||
|
|
ee28f431d0 | ||
|
|
b5dd7eed68 | ||
|
|
18004d2f20 | ||
|
|
5908d08a0b | ||
|
|
9e5f2a952b | ||
|
|
4f5a687254 | ||
|
|
16d2ebdbcf | ||
|
|
70294c9114 | ||
|
|
833357402c | ||
|
|
4f65fbd197 | ||
|
|
f441b273ae | ||
|
|
3ead857a0d | ||
|
|
5a76186340 | ||
|
|
cd0d581ff5 | ||
|
|
f18b4a4847 | ||
|
|
b7d3d6ebc5 | ||
|
|
c5f54a8f33 | ||
|
|
ced6129574 | ||
|
|
5e43155b3f |
No files matched your search
@@ -31,8 +31,14 @@ coverage/COVERAGE_REPORT
|
||||
package/
|
||||
.phutil_module_cache
|
||||
tags
|
||||
|
||||
java/out
|
||||
java/*.log
|
||||
java/include/org_rocksdb_*.h
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
unity.cc
|
||||
java/crossbuild/.vagrant
|
||||
.vagrant/
|
||||
|
||||
+1
-1
@@ -14,6 +14,6 @@ before_install:
|
||||
- sudo dpkg -i libgflags-dev_2.0-1_amd64.deb
|
||||
# Lousy hack to disable use and testing of fallocate, which doesn't behave quite
|
||||
# as EnvPosixTest::AllocateTest expects within the Travis OpenVZ environment.
|
||||
script: OPT=-DTRAVIS make unity && OPT=-DTRAVIS make check -j8
|
||||
script: OPT=-DTRAVIS make unity && make clean && OPT="-DTRAVIS -Wshadow" make check -j8
|
||||
notifications:
|
||||
email: false
|
||||
+10
@@ -1,5 +1,15 @@
|
||||
# Rocksdb Change Log
|
||||
|
||||
## Unreleased
|
||||
|
||||
|
||||
## 3.7.0 (11/6/2014)
|
||||
### Public API changes
|
||||
* Introduce SetOptions() API to allow adjusting a subset of options dynamically online
|
||||
* Introduce 4 new convenient functions for converting Options from string: GetColumnFamilyOptionsFromMap(), GetColumnFamilyOptionsFromString(), GetDBOptionsFromMap(), GetDBOptionsFromString()
|
||||
* Remove WriteBatchWithIndex.Delete() overloads using SliceParts
|
||||
* When opening a DB, if options.max_background_compactions is larger than the existing low pri pool of options.env, it will enlarge it. Similarly, options.max_background_flushes is larger than the existing high pri pool of options.env, it will enlarge it.
|
||||
|
||||
## 3.6.0 (10/7/2014)
|
||||
### Disk format changes
|
||||
* If you're using RocksDB on ARM platforms and you're using default bloom filter, there is a disk format change you need to be aware of. There are three steps you need to do when you convert to new release: 1. turn off filter policy, 2. compact the whole database, 3. turn on filter policy
|
||||
|
||||
@@ -75,11 +75,11 @@ CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverl
|
||||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
|
||||
LIBOBJECTS = $(SOURCES:.cc=.o)
|
||||
LIBOBJECTS += $(SOURCESCPP:.cpp=.o)
|
||||
MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_SOURCES:.cc=.o)
|
||||
|
||||
TESTUTIL = ./util/testutil.o
|
||||
TESTHARNESS = ./util/testharness.o $(TESTUTIL)
|
||||
TESTHARNESS = ./util/testharness.o $(TESTUTIL) $(MOCKOBJECTS)
|
||||
BENCHHARNESS = ./util/benchharness.o
|
||||
VALGRIND_ERROR = 2
|
||||
VALGRIND_DIR = build_tools/VALGRIND_LOGS
|
||||
@@ -115,11 +115,13 @@ TESTS = \
|
||||
log_test \
|
||||
manual_compaction_test \
|
||||
memenv_test \
|
||||
mock_env_test \
|
||||
merge_test \
|
||||
merger_test \
|
||||
redis_test \
|
||||
reduce_levels_test \
|
||||
plain_table_db_test \
|
||||
comparator_db_test \
|
||||
prefix_test \
|
||||
skiplist_test \
|
||||
stringappend_test \
|
||||
@@ -130,6 +132,8 @@ TESTS = \
|
||||
spatial_db_test \
|
||||
version_edit_test \
|
||||
version_set_test \
|
||||
compaction_picker_test \
|
||||
version_builder_test \
|
||||
file_indexer_test \
|
||||
write_batch_test \
|
||||
write_controller_test\
|
||||
@@ -142,7 +146,9 @@ TESTS = \
|
||||
cuckoo_table_builder_test \
|
||||
cuckoo_table_reader_test \
|
||||
cuckoo_table_db_test \
|
||||
write_batch_with_index_test
|
||||
write_batch_with_index_test \
|
||||
flush_job_test \
|
||||
wal_manager_test
|
||||
|
||||
TOOLS = \
|
||||
sst_dump \
|
||||
@@ -381,6 +387,9 @@ log_write_bench: util/log_write_bench.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
plain_table_db_test: db/plain_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/plain_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
comparator_db_test: db/comparator_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/comparator_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
table_reader_bench: table/table_reader_bench.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/table_reader_bench.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS) -pg
|
||||
|
||||
@@ -411,6 +420,12 @@ ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
write_batch_with_index_test: utilities/write_batch_with_index/write_batch_with_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/write_batch_with_index/write_batch_with_index_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
flush_job_test: db/flush_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/flush_job_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
wal_manager_test: db/wal_manager_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/wal_manager_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
@@ -447,6 +462,12 @@ version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
compaction_picker_test: db/compaction_picker_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/compaction_picker_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
version_builder_test: db/version_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/version_builder_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
file_indexer_test : db/file_indexer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/file_indexer_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
@@ -490,6 +511,9 @@ $(MEMENVLIBRARY) : $(MEMENVOBJECTS)
|
||||
memenv_test : helpers/memenv/memenv_test.o $(MEMENVOBJECTS) $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) helpers/memenv/memenv_test.o $(MEMENVOBJECTS) $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
mock_env_test : util/mock_env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/mock_env_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
manual_compaction_test: util/manual_compaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/manual_compaction_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
@@ -527,7 +551,11 @@ ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PAT
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
ROCKSDBJNILIB = librocksdbjni-osx.jnilib
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-osx.jar
|
||||
JAVA_INCLUDE = -I/System/Library/Frameworks/JavaVM.framework/Headers/
|
||||
ifneq ("$(wildcard $(JAVA_HOME)/include/darwin)","")
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include -I $(JAVA_HOME)/include/darwin
|
||||
else
|
||||
JAVA_INCLUDE = -I/System/Library/Frameworks/JavaVM.framework/Headers/
|
||||
endif
|
||||
endif
|
||||
|
||||
libz.a:
|
||||
|
||||
@@ -48,11 +48,7 @@ COMMON_FLAGS="-DROCKSDB_PLATFORM_POSIX"
|
||||
# Default to fbcode gcc on internal fb machines
|
||||
if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
source $PWD/build_tools/fbcode_config.sh
|
||||
else
|
||||
source "$PWD/build_tools/fbcode.clang31.sh"
|
||||
fi
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
@@ -167,14 +163,15 @@ DIRS="util db table utilities"
|
||||
set -f # temporarily disable globbing so that our patterns arent expanded
|
||||
PRUNE_TEST="-name *test*.cc -prune"
|
||||
PRUNE_BENCH="-name *bench*.cc -prune"
|
||||
PORTABLE_FILES=`cd "$ROCKSDB_ROOT"; find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cc' -print | sort | tr "\n" " "`
|
||||
PORTABLE_CPP=`cd "$ROCKSDB_ROOT"; find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cpp' -print | sort | tr "\n" " "`
|
||||
PRUNE_MOCK="-name *mock*.cc -prune"
|
||||
PORTABLE_FILES=`cd "$ROCKSDB_ROOT"; find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_MOCK -o -name '*.cc' -print | sort | tr "\n" " "`
|
||||
MOCK_SOURCES=`cd "$ROCKSDB_ROOT"; find $DIRS -name '*mock*.cc' -print | grep -v "test" | sort | tr "\n" " "`
|
||||
set +f # re-enable globbing
|
||||
|
||||
# The sources consist of the portable files, plus the platform-specific port
|
||||
# file.
|
||||
echo "SOURCES=$PORTABLE_FILES $GENERIC_PORT_FILES $PORT_FILES" >> "$OUTPUT"
|
||||
echo "SOURCESCPP=$PORTABLE_CPP" >> "$OUTPUT"
|
||||
echo "MOCK_SOURCES=$MOCK_SOURCES" >> "$OUTPUT"
|
||||
echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> "$OUTPUT"
|
||||
|
||||
if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
@@ -182,15 +179,6 @@ if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
# Also don't need any compilation tests if compiling on fbcode
|
||||
true
|
||||
else
|
||||
# If -std=c++0x works, use <atomic>. Otherwise use port_posix.h.
|
||||
$CXX $CFLAGS -std=c++0x -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <atomic>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_ATOMIC_PRESENT"
|
||||
fi
|
||||
|
||||
# Test whether fallocate is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
@@ -236,7 +224,7 @@ EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=gflags"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=google"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile leveldb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
# uses jemalloc
|
||||
|
||||
TOOLCHAIN_REV=fbe3b095a4cc4a3713730050d182b7b4a80c342f
|
||||
TOOLCHAIN_EXECUTABLES="/mnt/gvfs/third-party/$TOOLCHAIN_REV/centos5.2-native"
|
||||
TOOLCHAIN_LIB_BASE="/mnt/gvfs/third-party/$TOOLCHAIN_REV/gcc-4.7.1-glibc-2.14.1"
|
||||
TOOL_JEMALLOC=jemalloc-3.3.1/9202ce3
|
||||
GLIBC_RUNTIME_PATH=/usr/local/fbcode/gcc-4.7.1-glibc-2.14.1
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.7.1/afc21dc/include"
|
||||
LIBGCC_LIBS=" -L $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.7.1/afc21dc/libs"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/glibc/glibc-2.14.1/99df8fc/include"
|
||||
GLIBC_LIBS=" -L $TOOLCHAIN_LIB_BASE/glibc/glibc-2.14.1/99df8fc/lib"
|
||||
|
||||
# location of snappy headers and libraries
|
||||
SNAPPY_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/snappy/snappy-1.0.3/7518bbe/include"
|
||||
SNAPPY_LIBS=" $TOOLCHAIN_LIB_BASE/snappy/snappy-1.0.3/7518bbe/lib/libsnappy.a"
|
||||
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/zlib/zlib-1.2.5/91ddd43/include"
|
||||
ZLIB_LIBS=" $TOOLCHAIN_LIB_BASE/zlib/zlib-1.2.5/91ddd43/lib/libz.a"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/91ddd43/include"
|
||||
GFLAGS_LIBS=" $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/91ddd43/lib/libgflags.a"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/bzip2/bzip2-1.0.6/91ddd43/include"
|
||||
BZIP_LIBS=" $TOOLCHAIN_LIB_BASE/bzip2/bzip2-1.0.6/91ddd43/lib/libbz2.a"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/91ddd43/include"
|
||||
GFLAGS_LIBS=" $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/91ddd43/lib/libgflags.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=" -msse -msse4.2 "
|
||||
|
||||
CC="$TOOLCHAIN_EXECUTABLES/clang/clang-3.2/0b7c69d/bin/clang $CLANG_INCLUDES"
|
||||
CXX="$TOOLCHAIN_EXECUTABLES/clang/clang-3.2/0b7c69d/bin/clang++ $CLANG_INCLUDES $JINCLUDE $SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $GFLAGS_INCLUDE"
|
||||
AR=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ar
|
||||
RANLIB=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ranlib
|
||||
|
||||
CFLAGS="-B$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin -nostdlib "
|
||||
CFLAGS+=" -nostdinc -isystem $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1 "
|
||||
CFLAGS+=" -isystem $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/backward "
|
||||
CFLAGS+=" -isystem $TOOLCHAIN_LIB_BASE/glibc/glibc-2.14.1/99df8fc/include "
|
||||
CFLAGS+=" -isystem $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.7.1/afc21dc/include "
|
||||
CFLAGS+=" -isystem $TOOLCHAIN_LIB_BASE/clang/clang-3.2/0b7c69d/lib/clang/3.2/include "
|
||||
CFLAGS+=" -isystem $TOOLCHAIN_LIB_BASE/kernel-headers/kernel-headers-3.2.18_70_fbk11_00129_gc8882d0/da39a3e/include/linux "
|
||||
CFLAGS+=" -isystem $TOOLCHAIN_LIB_BASE/kernel-headers/kernel-headers-3.2.18_70_fbk11_00129_gc8882d0/da39a3e/include "
|
||||
CFLAGS+=" -Wall -Wno-sign-compare -Wno-unused-variable -Winvalid-pch -Wno-deprecated -Woverloaded-virtual"
|
||||
CFLAGS+=" $LIBGCC_INCLUDE $GLIBC_INCLUDE"
|
||||
CXXFLAGS="$CFLAGS -nostdinc++"
|
||||
|
||||
CFLAGS+=" -I $TOOLCHAIN_LIB_BASE/jemalloc/$TOOL_JEMALLOC/include -DHAVE_JEMALLOC"
|
||||
|
||||
EXEC_LDFLAGS=" -Wl,--whole-archive $TOOLCHAIN_LIB_BASE/jemalloc/$TOOL_JEMALLOC/lib/libjemalloc.a"
|
||||
EXEC_LDFLAGS+=" -Wl,--no-whole-archive $TOOLCHAIN_LIB_BASE/libunwind/libunwind-1.0.1/350336c/lib/libunwind.a"
|
||||
EXEC_LDFLAGS+=" $HDFSLIB $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $GFLAGS_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,$GLIBC_RUNTIME_PATH/lib/ld-linux-x86-64.so.2"
|
||||
EXEC_LDFLAGS+=" -B$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS "
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $GFLAGS_LIBS"
|
||||
|
||||
export CC CXX AR RANLIB CFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile leveldb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
# uses jemalloc
|
||||
|
||||
TOOLCHAIN_REV=fbe3b095a4cc4a3713730050d182b7b4a80c342f
|
||||
TOOLCHAIN_EXECUTABLES="/mnt/gvfs/third-party/$TOOLCHAIN_REV/centos5.2-native"
|
||||
TOOLCHAIN_LIB_BASE="/mnt/gvfs/third-party/$TOOLCHAIN_REV/gcc-4.7.1-glibc-2.14.1"
|
||||
TOOL_JEMALLOC=jemalloc-3.3.1/9202ce3
|
||||
|
||||
# location of libhdfs libraries
|
||||
if test "$USE_HDFS"; then
|
||||
JAVA_HOME="/usr/local/jdk-6u22-64"
|
||||
JINCLUDE="-I$JAVA_HOME/include -I$JAVA_HOME/include/linux"
|
||||
GLIBC_RUNTIME_PATH="/usr/local/fbcode/gcc-4.7.1-glibc-2.14.1"
|
||||
HDFSLIB=" -Wl,--no-whole-archive hdfs/libhdfs.a -L$JAVA_HOME/jre/lib/amd64 "
|
||||
HDFSLIB+=" -L$JAVA_HOME/jre/lib/amd64/server -L$GLIBC_RUNTIME_PATH/lib "
|
||||
HDFSLIB+=" -ldl -lverify -ljava -ljvm "
|
||||
fi
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.7.1/afc21dc/include"
|
||||
LIBGCC_LIBS=" -L $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.7.1/afc21dc/libs"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/glibc/glibc-2.14.1/99df8fc/include"
|
||||
GLIBC_LIBS=" -L $TOOLCHAIN_LIB_BASE/glibc/glibc-2.14.1/99df8fc/lib"
|
||||
|
||||
# location of snappy headers and libraries
|
||||
SNAPPY_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/snappy/snappy-1.0.3/7518bbe/include"
|
||||
SNAPPY_LIBS=" $TOOLCHAIN_LIB_BASE/snappy/snappy-1.0.3/7518bbe/lib/libsnappy.a"
|
||||
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/zlib/zlib-1.2.5/91ddd43/include"
|
||||
ZLIB_LIBS=" $TOOLCHAIN_LIB_BASE/zlib/zlib-1.2.5/91ddd43/lib/libz.a"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/bzip2/bzip2-1.0.6/91ddd43/include"
|
||||
BZIP_LIBS=" $TOOLCHAIN_LIB_BASE/bzip2/bzip2-1.0.6/91ddd43/lib/libbz2.a"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/91ddd43/include"
|
||||
GFLAGS_LIBS=" $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/91ddd43/lib/libgflags.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=" -msse -msse4.2 "
|
||||
|
||||
CC="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.7.1-glibc-2.14.1/bin/gcc"
|
||||
CXX="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.7.1-glibc-2.14.1/bin/g++ $JINCLUDE $SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $GFLAGS_INCLUDE"
|
||||
AR=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ar
|
||||
RANLIB=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ranlib
|
||||
|
||||
CFLAGS="-B$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/gold -m64 -mtune=generic"
|
||||
CFLAGS+=" -I $TOOLCHAIN_LIB_BASE/jemalloc/$TOOL_JEMALLOC/include -DHAVE_JEMALLOC"
|
||||
CFLAGS+=" $LIBGCC_INCLUDE $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_ATOMIC_PRESENT -DROCKSDB_FALLOCATE_PRESENT"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2"
|
||||
|
||||
EXEC_LDFLAGS=" -Wl,--whole-archive $TOOLCHAIN_LIB_BASE/jemalloc/$TOOL_JEMALLOC/lib/libjemalloc.a"
|
||||
EXEC_LDFLAGS+=" -Wl,--no-whole-archive $TOOLCHAIN_LIB_BASE/libunwind/libunwind-1.0.1/350336c/lib/libunwind.a"
|
||||
EXEC_LDFLAGS+=" $HDFSLIB $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $GFLAGS_LIBS"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS "
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $GFLAGS_LIBS"
|
||||
|
||||
VALGRIND_VER="$TOOLCHAIN_LIB_BASE/valgrind/valgrind-3.8.1/91ddd43/bin/"
|
||||
|
||||
export CC CXX AR RANLIB CFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
# uses jemalloc
|
||||
|
||||
TOOLCHAIN_REV=53dc1fe83f84e9145b9ffb81b81aa7f6a49c87cc
|
||||
CENTOS_VERSION=`rpm -q --qf "%{VERSION}" $(rpm -q --whatprovides redhat-release)`
|
||||
if [ "$CENTOS_VERSION" = "6" ]; then
|
||||
TOOLCHAIN_EXECUTABLES="/mnt/gvfs/third-party/$TOOLCHAIN_REV/centos6-native"
|
||||
else
|
||||
TOOLCHAIN_EXECUTABLES="/mnt/gvfs/third-party/$TOOLCHAIN_REV/centos5.2-native"
|
||||
fi
|
||||
TOOLCHAIN_LIB_BASE="/mnt/gvfs/third-party/$TOOLCHAIN_REV/gcc-4.8.1-glibc-2.17"
|
||||
|
||||
# location of libhdfs libraries
|
||||
if test "$USE_HDFS"; then
|
||||
JAVA_HOME="/usr/local/jdk-6u22-64"
|
||||
JINCLUDE="-I$JAVA_HOME/include -I$JAVA_HOME/include/linux"
|
||||
GLIBC_RUNTIME_PATH="/usr/local/fbcode/gcc-4.8.1-glibc-2.17"
|
||||
HDFSLIB=" -Wl,--no-whole-archive hdfs/libhdfs.a -L$JAVA_HOME/jre/lib/amd64 "
|
||||
HDFSLIB+=" -L$JAVA_HOME/jre/lib/amd64/server -L$GLIBC_RUNTIME_PATH/lib "
|
||||
HDFSLIB+=" -ldl -lverify -ljava -ljvm "
|
||||
fi
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.8.1/8aac7fc/include"
|
||||
LIBGCC_LIBS=" -L $TOOLCHAIN_LIB_BASE/libgcc/libgcc-4.8.1/8aac7fc/libs"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/glibc/glibc-2.17/99df8fc/include"
|
||||
GLIBC_LIBS=" -L $TOOLCHAIN_LIB_BASE/glibc/glibc-2.17/99df8fc/lib"
|
||||
|
||||
# location of snappy headers and libraries
|
||||
SNAPPY_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/snappy/snappy-1.0.3/43d84e2/include"
|
||||
SNAPPY_LIBS=" $TOOLCHAIN_LIB_BASE/snappy/snappy-1.0.3/43d84e2/lib/libsnappy.a"
|
||||
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/zlib/zlib-1.2.5/c3f970a/include"
|
||||
ZLIB_LIBS=" $TOOLCHAIN_LIB_BASE/zlib/zlib-1.2.5/c3f970a/lib/libz.a"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/bzip2/bzip2-1.0.6/c3f970a/include"
|
||||
BZIP_LIBS=" $TOOLCHAIN_LIB_BASE/bzip2/bzip2-1.0.6/c3f970a/lib/libbz2.a"
|
||||
|
||||
LZ4_REV=065ec7e38fe83329031f6668c43bef83eff5808b
|
||||
LZ4_INCLUDE=" -I /mnt/gvfs/third-party2/lz4/$LZ4_REV/r108/gcc-4.8.1-glibc-2.17/c3f970a/include"
|
||||
LZ4_LIBS=" /mnt/gvfs/third-party2/lz4/$LZ4_REV/r108/gcc-4.8.1-glibc-2.17/c3f970a/lib/liblz4.a"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/c3f970a/include"
|
||||
GFLAGS_LIBS=" $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/c3f970a/lib/libgflags.a"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/jemalloc/jemalloc-3.4.1/4d53c6f/include/"
|
||||
JEMALLOC_LIB=" -Wl,--whole-archive $TOOLCHAIN_LIB_BASE/jemalloc/jemalloc-3.4.1/4d53c6f/lib/libjemalloc.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_REV=829d10dac0230f99cd7e1778869d2adf3da24b65
|
||||
NUMA_INCLUDE=" -I /mnt/gvfs/third-party2/numa/$NUMA_REV/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a/include/"
|
||||
NUMA_LIB=" /mnt/gvfs/third-party2/numa/$NUMA_REV/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a/lib/libnuma.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=" -msse -msse4.2 "
|
||||
|
||||
CC="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.8.1/cc6c9dc/bin/gcc"
|
||||
CXX="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.8.1/cc6c9dc/bin/g++ $JINCLUDE $SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
AR=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ar
|
||||
RANLIB=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ranlib
|
||||
|
||||
CFLAGS="-B$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/gold -m64 -mtune=generic"
|
||||
CFLAGS+=" $LIBGCC_INCLUDE $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_ATOMIC_PRESENT -DROCKSDB_FALLOCATE_PRESENT"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DNUMA"
|
||||
|
||||
EXEC_LDFLAGS="-Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--no-whole-archive $TOOLCHAIN_LIB_BASE/libunwind/libunwind-1.0.1/675d945/lib/libunwind.a"
|
||||
EXEC_LDFLAGS+=" $HDFSLIB $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS "
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS"
|
||||
|
||||
VALGRIND_VER="$TOOLCHAIN_LIB_BASE/valgrind/valgrind-3.8.1/c3f970a/bin/"
|
||||
|
||||
export CC CXX AR RANLIB CFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE
|
||||
@@ -35,9 +35,18 @@ if test -z $PIC_BUILD; then
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
|
||||
# location of gflags headers and libraries
|
||||
@@ -54,6 +63,11 @@ JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
@@ -73,7 +87,7 @@ export USE_SSE=" -msse -msse4.2 "
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $LZ4_INCLUDE $GFLAGS_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -117,7 +131,7 @@ CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
@@ -127,7 +141,7 @@ EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ fi
|
||||
ROOT=".."
|
||||
# Fetch right version of gcov
|
||||
if [ -d /mnt/gvfs/third-party -a -z "$CXX" ]; then
|
||||
source $ROOT/build_tools/fbcode.gcc471.sh
|
||||
GCOV=$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.7.1/cc6c9dc/bin/gcov
|
||||
source $ROOT/build_tools/fbcode_config.sh
|
||||
GCOV=$GCC_BASE/bin/gcov
|
||||
else
|
||||
GCOV=$(which gcov)
|
||||
fi
|
||||
|
||||
+7
-5
@@ -72,11 +72,13 @@ Status BuildTable(const std::string& dbname, Env* env,
|
||||
ioptions, internal_comparator, file.get(),
|
||||
compression, compression_opts);
|
||||
|
||||
// the first key is the smallest key
|
||||
Slice key = iter->key();
|
||||
meta->smallest.DecodeFrom(key);
|
||||
meta->smallest_seqno = GetInternalKeySeqno(key);
|
||||
meta->largest_seqno = meta->smallest_seqno;
|
||||
{
|
||||
// the first key is the smallest key
|
||||
Slice key = iter->key();
|
||||
meta->smallest.DecodeFrom(key);
|
||||
meta->smallest_seqno = GetInternalKeySeqno(key);
|
||||
meta->largest_seqno = meta->smallest_seqno;
|
||||
}
|
||||
|
||||
MergeHelper merge(internal_comparator.user_comparator(),
|
||||
ioptions.merge_operator, ioptions.info_log,
|
||||
|
||||
+16
-14
@@ -576,37 +576,39 @@ int main(int argc, char** argv) {
|
||||
|
||||
StartPhase("compaction_filter");
|
||||
{
|
||||
rocksdb_options_t* options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options, 1);
|
||||
rocksdb_options_t* options_with_filter = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options_with_filter, 1);
|
||||
rocksdb_compactionfilter_t* cfilter;
|
||||
cfilter = rocksdb_compactionfilter_create(NULL, CFilterDestroy,
|
||||
CFilterFilter, CFilterName);
|
||||
// Create new database
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
rocksdb_options_set_compaction_filter(options, cfilter);
|
||||
db = CheckCompaction(db, options, roptions, woptions);
|
||||
rocksdb_destroy_db(options_with_filter, dbname, &err);
|
||||
rocksdb_options_set_compaction_filter(options_with_filter, cfilter);
|
||||
db = CheckCompaction(db, options_with_filter, roptions, woptions);
|
||||
|
||||
rocksdb_options_set_compaction_filter(options, NULL);
|
||||
rocksdb_options_set_compaction_filter(options_with_filter, NULL);
|
||||
rocksdb_compactionfilter_destroy(cfilter);
|
||||
rocksdb_options_destroy(options);
|
||||
rocksdb_options_destroy(options_with_filter);
|
||||
}
|
||||
|
||||
StartPhase("compaction_filter_factory");
|
||||
{
|
||||
rocksdb_options_t* options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options, 1);
|
||||
rocksdb_options_t* options_with_filter_factory = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options_with_filter_factory, 1);
|
||||
rocksdb_compactionfilterfactory_t* factory;
|
||||
factory = rocksdb_compactionfilterfactory_create(
|
||||
NULL, CFilterFactoryDestroy, CFilterCreate, CFilterFactoryName);
|
||||
// Create new database
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
rocksdb_options_set_compaction_filter_factory(options, factory);
|
||||
db = CheckCompaction(db, options, roptions, woptions);
|
||||
rocksdb_destroy_db(options_with_filter_factory, dbname, &err);
|
||||
rocksdb_options_set_compaction_filter_factory(options_with_filter_factory,
|
||||
factory);
|
||||
db = CheckCompaction(db, options_with_filter_factory, roptions, woptions);
|
||||
|
||||
rocksdb_options_set_compaction_filter_factory(options, NULL);
|
||||
rocksdb_options_destroy(options);
|
||||
rocksdb_options_set_compaction_filter_factory(
|
||||
options_with_filter_factory, NULL);
|
||||
rocksdb_options_destroy(options_with_filter_factory);
|
||||
}
|
||||
|
||||
StartPhase("compaction_filter_v2");
|
||||
|
||||
+70
-54
@@ -20,6 +20,7 @@
|
||||
#include <limits>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/compaction_picker.h"
|
||||
@@ -61,9 +62,9 @@ uint64_t SlowdownAmount(int n, double bottom, double top) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(ColumnFamilyData* cfd,
|
||||
DBImpl* db, port::Mutex* mutex)
|
||||
: cfd_(cfd), db_(db), mutex_(mutex) {
|
||||
ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(
|
||||
ColumnFamilyData* column_family_data, DBImpl* db, port::Mutex* mutex)
|
||||
: cfd_(column_family_data), db_(db), mutex_(mutex) {
|
||||
if (cfd_ != nullptr) {
|
||||
cfd_->Ref();
|
||||
}
|
||||
@@ -71,15 +72,15 @@ ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(ColumnFamilyData* cfd,
|
||||
|
||||
ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
if (cfd_ != nullptr) {
|
||||
DBImpl::DeletionState deletion_state;
|
||||
JobContext job_context;
|
||||
mutex_->Lock();
|
||||
if (cfd_->Unref()) {
|
||||
delete cfd_;
|
||||
}
|
||||
db_->FindObsoleteFiles(deletion_state, false, true);
|
||||
db_->FindObsoleteFiles(&job_context, false, true);
|
||||
mutex_->Unlock();
|
||||
if (deletion_state.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(deletion_state);
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,14 +217,15 @@ void SuperVersionUnrefHandle(void* ptr) {
|
||||
} // anonymous namespace
|
||||
|
||||
ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
Version* _dummy_versions,
|
||||
Cache* _table_cache,
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const DBOptions* db_options,
|
||||
const EnvOptions& env_options,
|
||||
ColumnFamilySet* column_family_set)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(dummy_versions),
|
||||
dummy_versions_(_dummy_versions),
|
||||
current_(nullptr),
|
||||
refs_(0),
|
||||
dropped_(false),
|
||||
@@ -242,11 +244,11 @@ ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
column_family_set_(column_family_set) {
|
||||
Ref();
|
||||
|
||||
// if dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (dummy_versions != nullptr) {
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
internal_stats_.reset(
|
||||
new InternalStats(ioptions_.num_levels, db_options->env, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, env_options, table_cache));
|
||||
table_cache_.reset(new TableCache(ioptions_, env_options, _table_cache));
|
||||
if (ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
compaction_picker_.reset(
|
||||
new UniversalCompactionPicker(ioptions_, &internal_comparator_));
|
||||
@@ -259,10 +261,10 @@ ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
new FIFOCompactionPicker(ioptions_, &internal_comparator_));
|
||||
}
|
||||
|
||||
Log(ioptions_.info_log, "Options for column family \"%s\":\n",
|
||||
Log(InfoLogLevel::INFO_LEVEL,
|
||||
ioptions_.info_log, "Options for column family \"%s\":\n",
|
||||
name.c_str());
|
||||
const ColumnFamilyOptions* cf_options = &options_;
|
||||
cf_options->Dump(ioptions_.info_log);
|
||||
options_.Dump(ioptions_.info_log);
|
||||
}
|
||||
|
||||
RecalculateWriteStallConditions(mutable_cf_options_);
|
||||
@@ -304,8 +306,9 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
|
||||
if (dummy_versions_ != nullptr) {
|
||||
// List must be empty
|
||||
assert(dummy_versions_->next_ == dummy_versions_);
|
||||
delete dummy_versions_;
|
||||
assert(dummy_versions_->TEST_Next() == dummy_versions_);
|
||||
bool deleted __attribute__((unused)) = dummy_versions_->Unref();
|
||||
assert(deleted);
|
||||
}
|
||||
|
||||
if (mem_ != nullptr) {
|
||||
@@ -321,56 +324,59 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
if (current_ != nullptr) {
|
||||
const double score = current_->MaxCompactionScore();
|
||||
const int max_level = current_->MaxCompactionScoreLevel();
|
||||
auto* vstorage = current_->storage_info();
|
||||
const double score = vstorage->max_compaction_score();
|
||||
const int max_level = vstorage->max_compaction_score_level();
|
||||
|
||||
auto write_controller = column_family_set_->write_controller_;
|
||||
|
||||
if (imm()->size() == options_.max_write_buffer_number) {
|
||||
if (imm()->size() >= mutable_cf_options.max_write_buffer_number) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_COMPACTION, 1);
|
||||
Log(ioptions_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d immutable memtables "
|
||||
"(waiting for flush)",
|
||||
name_.c_str(), imm()->size());
|
||||
} else if (current_->NumLevelFiles(0) >=
|
||||
"(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) >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::LEVEL0_NUM_FILES, 1);
|
||||
Log(ioptions_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
name_.c_str(), current_->NumLevelFiles(0));
|
||||
name_.c_str(), vstorage->NumLevelFiles(0));
|
||||
} else if (mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
current_->NumLevelFiles(0) >=
|
||||
vstorage->NumLevelFiles(0) >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
uint64_t slowdown = SlowdownAmount(
|
||||
current_->NumLevelFiles(0),
|
||||
mutable_cf_options.level0_slowdown_writes_trigger,
|
||||
mutable_cf_options.level0_stop_writes_trigger);
|
||||
uint64_t slowdown =
|
||||
SlowdownAmount(vstorage->NumLevelFiles(0),
|
||||
mutable_cf_options.level0_slowdown_writes_trigger,
|
||||
mutable_cf_options.level0_stop_writes_trigger);
|
||||
write_controller_token_ = write_controller->GetDelayToken(slowdown);
|
||||
internal_stats_->AddCFStats(InternalStats::LEVEL0_SLOWDOWN, slowdown);
|
||||
Log(ioptions_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stalling writes because we have %d level-0 files (%" PRIu64
|
||||
"us)",
|
||||
name_.c_str(), current_->NumLevelFiles(0), slowdown);
|
||||
} else if (options_.hard_rate_limit > 1.0 &&
|
||||
score > options_.hard_rate_limit) {
|
||||
name_.c_str(), vstorage->NumLevelFiles(0), slowdown);
|
||||
} else if (mutable_cf_options.hard_rate_limit > 1.0 &&
|
||||
score > mutable_cf_options.hard_rate_limit) {
|
||||
uint64_t kHardLimitSlowdown = 1000;
|
||||
write_controller_token_ =
|
||||
write_controller->GetDelayToken(kHardLimitSlowdown);
|
||||
internal_stats_->RecordLevelNSlowdown(max_level, kHardLimitSlowdown,
|
||||
false);
|
||||
Log(ioptions_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stalling writes because we hit hard limit on level %d. "
|
||||
"(%" PRIu64 "us)",
|
||||
name_.c_str(), max_level, kHardLimitSlowdown);
|
||||
} else if (options_.soft_rate_limit > 0.0 &&
|
||||
score > options_.soft_rate_limit) {
|
||||
uint64_t slowdown = SlowdownAmount(score, options_.soft_rate_limit,
|
||||
options_.hard_rate_limit);
|
||||
} else if (mutable_cf_options.soft_rate_limit > 0.0 &&
|
||||
score > mutable_cf_options.soft_rate_limit) {
|
||||
uint64_t slowdown = SlowdownAmount(score,
|
||||
mutable_cf_options.soft_rate_limit,
|
||||
mutable_cf_options.hard_rate_limit);
|
||||
write_controller_token_ = write_controller->GetDelayToken(slowdown);
|
||||
internal_stats_->RecordLevelNSlowdown(max_level, slowdown, true);
|
||||
Log(ioptions_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stalling writes because we hit soft limit on level %d (%" PRIu64
|
||||
"us)",
|
||||
name_.c_str(), max_level, slowdown);
|
||||
@@ -384,21 +390,27 @@ const EnvOptions* ColumnFamilyData::soptions() const {
|
||||
return &(column_family_set_->env_options_);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::SetCurrent(Version* current) { current_ = current; }
|
||||
void ColumnFamilyData::SetCurrent(Version* current_version) {
|
||||
current_ = current_version;
|
||||
}
|
||||
|
||||
void ColumnFamilyData::CreateNewMemtable(const MemTableOptions& moptions) {
|
||||
void ColumnFamilyData::CreateNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
assert(current_ != nullptr);
|
||||
if (mem_ != nullptr) {
|
||||
delete mem_->Unref();
|
||||
}
|
||||
mem_ = new MemTable(internal_comparator_, ioptions_, moptions);
|
||||
mem_ = new MemTable(internal_comparator_, ioptions_, mutable_cf_options);
|
||||
mem_->Ref();
|
||||
}
|
||||
|
||||
Compaction* ColumnFamilyData::PickCompaction(
|
||||
const MutableCFOptions& mutable_options, LogBuffer* log_buffer) {
|
||||
auto result = compaction_picker_->PickCompaction(
|
||||
mutable_options, current_, log_buffer);
|
||||
auto* result = compaction_picker_->PickCompaction(
|
||||
GetName(), mutable_options, current_->storage_info(), log_buffer);
|
||||
if (result != nullptr) {
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -407,9 +419,13 @@ Compaction* ColumnFamilyData::CompactRange(
|
||||
int input_level, int output_level, uint32_t output_path_id,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end) {
|
||||
return compaction_picker_->CompactRange(
|
||||
mutable_cf_options, current_, input_level, output_level,
|
||||
output_path_id, begin, end, compaction_end);
|
||||
auto* result = compaction_picker_->CompactRange(
|
||||
GetName(), mutable_cf_options, current_->storage_info(), input_level,
|
||||
output_level, output_path_id, begin, end, compaction_end);
|
||||
if (result != nullptr) {
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SuperVersion* ColumnFamilyData::GetReferencedSuperVersion(
|
||||
@@ -531,16 +547,16 @@ void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
}
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::SetOptions(
|
||||
Status ColumnFamilyData::SetOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
MutableCFOptions new_mutable_cf_options;
|
||||
if (GetMutableOptionsFromStrings(mutable_cf_options_, options_map,
|
||||
&new_mutable_cf_options)) {
|
||||
Status s = GetMutableOptionsFromStrings(mutable_cf_options_, options_map,
|
||||
&new_mutable_cf_options);
|
||||
if (s.ok()) {
|
||||
mutable_cf_options_ = new_mutable_cf_options;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return s;
|
||||
}
|
||||
|
||||
ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
|
||||
+5
-5
@@ -70,7 +70,7 @@ class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
||||
ColumnFamilyHandleInternal()
|
||||
: ColumnFamilyHandleImpl(nullptr, nullptr, nullptr) {}
|
||||
|
||||
void SetCFD(ColumnFamilyData* cfd) { internal_cfd_ = cfd; }
|
||||
void SetCFD(ColumnFamilyData* _cfd) { internal_cfd_ = _cfd; }
|
||||
virtual ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
|
||||
private:
|
||||
@@ -166,7 +166,7 @@ class ColumnFamilyData {
|
||||
bool IsDropped() const { return dropped_; }
|
||||
|
||||
// thread-safe
|
||||
int NumberLevels() const { return options_.num_levels; }
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
|
||||
void SetLogNumber(uint64_t log_number) { log_number_ = log_number; }
|
||||
uint64_t GetLogNumber() const { return log_number_; }
|
||||
@@ -178,7 +178,7 @@ class ColumnFamilyData {
|
||||
// REQUIRES: DB mutex held
|
||||
// This returns the MutableCFOptions used by current SuperVersion
|
||||
// You shoul use this API to reference MutableCFOptions most of the time.
|
||||
const MutableCFOptions* mutable_cf_options() const {
|
||||
const MutableCFOptions* GetCurrentMutableCFOptions() const {
|
||||
return &(super_version_->mutable_cf_options);
|
||||
}
|
||||
// REQUIRES: DB mutex held
|
||||
@@ -187,7 +187,7 @@ class ColumnFamilyData {
|
||||
return &mutable_cf_options_;
|
||||
}
|
||||
// REQUIRES: DB mutex held
|
||||
bool SetOptions(
|
||||
Status SetOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map);
|
||||
|
||||
InternalStats* internal_stats() { return internal_stats_.get(); }
|
||||
@@ -198,7 +198,7 @@ class ColumnFamilyData {
|
||||
Version* dummy_versions() { return dummy_versions_; }
|
||||
void SetMemtable(MemTable* new_mem) { mem_ = new_mem; }
|
||||
void SetCurrent(Version* current);
|
||||
void CreateNewMemtable(const MemTableOptions& moptions);
|
||||
void CreateNewMemtable(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
TableCache* table_cache() const { return table_cache_.get(); }
|
||||
|
||||
|
||||
+34
-22
@@ -29,7 +29,17 @@ uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
Compaction::Compaction(Version* input_version, int start_level, int out_level,
|
||||
void Compaction::SetInputVersion(Version* _input_version) {
|
||||
input_version_ = _input_version;
|
||||
cfd_ = input_version_->cfd();
|
||||
|
||||
cfd_->Ref();
|
||||
input_version_->Ref();
|
||||
edit_ = new VersionEdit();
|
||||
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,
|
||||
@@ -39,9 +49,10 @@ Compaction::Compaction(Version* input_version, int start_level, int out_level,
|
||||
output_level_(out_level),
|
||||
max_output_file_size_(target_file_size),
|
||||
max_grandparent_overlap_bytes_(max_grandparent_overlap_bytes),
|
||||
input_version_(input_version),
|
||||
number_levels_(input_version_->NumberLevels()),
|
||||
cfd_(input_version_->cfd_),
|
||||
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),
|
||||
@@ -56,10 +67,6 @@ Compaction::Compaction(Version* input_version, int start_level, int out_level,
|
||||
is_full_compaction_(false),
|
||||
is_manual_compaction_(false),
|
||||
level_ptrs_(std::vector<size_t>(number_levels_)) {
|
||||
cfd_->Ref();
|
||||
input_version_->Ref();
|
||||
edit_ = new VersionEdit();
|
||||
edit_->SetColumnFamily(cfd_->GetID());
|
||||
for (int i = 0; i < number_levels_; i++) {
|
||||
level_ptrs_[i] = 0;
|
||||
}
|
||||
@@ -86,7 +93,8 @@ Compaction::~Compaction() {
|
||||
void Compaction::GenerateFileLevels() {
|
||||
input_levels_.resize(num_input_levels());
|
||||
for (int which = 0; which < num_input_levels(); which++) {
|
||||
DoGenerateFileLevel(&input_levels_[which], inputs_[which].files, &arena_);
|
||||
DoGenerateLevelFilesBrief(
|
||||
&input_levels_[which], inputs_[which].files, &arena_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,15 +111,16 @@ bool Compaction::IsTrivialMove() const {
|
||||
TotalFileSize(grandparents_) <= max_grandparent_overlap_bytes_);
|
||||
}
|
||||
|
||||
void Compaction::AddInputDeletions(VersionEdit* edit) {
|
||||
void Compaction::AddInputDeletions(VersionEdit* out_edit) {
|
||||
for (int which = 0; which < num_input_levels(); which++) {
|
||||
for (size_t i = 0; i < inputs_[which].size(); i++) {
|
||||
edit->DeleteFile(level(which), inputs_[which][i]->fd.GetNumber());
|
||||
out_edit->DeleteFile(level(which), inputs_[which][i]->fd.GetNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Compaction::KeyNotExistsBeyondOutputLevel(const Slice& user_key) {
|
||||
assert(input_version_ != nullptr);
|
||||
assert(cfd_->ioptions()->compaction_style != kCompactionStyleFIFO);
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleUniversal) {
|
||||
return bottommost_level_;
|
||||
@@ -119,7 +128,8 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(const Slice& user_key) {
|
||||
// Maybe use binary search to find right entry instead of linear search?
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
const std::vector<FileMetaData*>& files = input_version_->files_[lvl];
|
||||
const std::vector<FileMetaData*>& files =
|
||||
input_version_->storage_info()->LevelFiles(lvl);
|
||||
for (; level_ptrs_[lvl] < files.size(); ) {
|
||||
FileMetaData* f = files[level_ptrs_[lvl]];
|
||||
if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) {
|
||||
@@ -175,9 +185,9 @@ void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) {
|
||||
}
|
||||
|
||||
// Is this compaction producing files at the bottommost level?
|
||||
void Compaction::SetupBottomMostLevel(bool is_manual) {
|
||||
assert(cfd_->ioptions()->compaction_style != kCompactionStyleFIFO);
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleUniversal) {
|
||||
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
|
||||
@@ -192,7 +202,7 @@ void Compaction::SetupBottomMostLevel(bool is_manual) {
|
||||
bottommost_level_ = true;
|
||||
// checks whether there are files living beyond the output_level.
|
||||
for (int i = output_level_ + 1; i < number_levels_; i++) {
|
||||
if (input_version_->NumLevelFiles(i) > 0) {
|
||||
if (vstorage->NumLevelFiles(i) > 0) {
|
||||
bottommost_level_ = false;
|
||||
break;
|
||||
}
|
||||
@@ -217,7 +227,8 @@ void Compaction::ReleaseCompactionFiles(Status status) {
|
||||
}
|
||||
|
||||
void Compaction::ResetNextCompactionIndex() {
|
||||
input_version_->ResetNextCompactionIndex(start_level_);
|
||||
assert(input_version_ != nullptr);
|
||||
input_version_->storage_info()->ResetNextCompactionIndex(start_level_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -250,14 +261,15 @@ void Compaction::Summary(char* output, int len) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int level = 0; level < num_input_levels(); ++level) {
|
||||
if (level > 0) {
|
||||
for (int level_iter = 0; level_iter < num_input_levels(); ++level_iter) {
|
||||
if (level_iter > 0) {
|
||||
write += snprintf(output + write, len - write, "], [");
|
||||
if (write < 0 || write >= len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
write += InputSummary(inputs_[level].files, output + write, len - write);
|
||||
write +=
|
||||
InputSummary(inputs_[level_iter].files, output + write, len - write);
|
||||
if (write < 0 || write >= len) {
|
||||
return;
|
||||
}
|
||||
@@ -273,8 +285,8 @@ uint64_t Compaction::OutputFilePreallocationSize(
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
preallocation_size = mutable_options.MaxFileSizeForLevel(output_level());
|
||||
} else {
|
||||
for (int level = 0; level < num_input_levels(); ++level) {
|
||||
for (const auto& f : inputs_[level].files) {
|
||||
for (int level_iter = 0; level_iter < num_input_levels(); ++level_iter) {
|
||||
for (const auto& f : inputs_[level_iter].files) {
|
||||
preallocation_size += f->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -28,6 +28,7 @@ struct CompactionInputFiles {
|
||||
|
||||
class Version;
|
||||
class ColumnFamilyData;
|
||||
class VersionStorageInfo;
|
||||
|
||||
// A Compaction encapsulates information about a compaction.
|
||||
class Compaction {
|
||||
@@ -89,8 +90,8 @@ class Compaction {
|
||||
return &inputs_[compaction_input_level].files;
|
||||
}
|
||||
|
||||
// Returns the FileLevel of the specified compaction input level.
|
||||
FileLevel* input_levels(int compaction_input_level) {
|
||||
// Returns the LevelFilesBrief of the specified compaction input level.
|
||||
LevelFilesBrief* input_levels(int compaction_input_level) {
|
||||
return &input_levels_[compaction_input_level];
|
||||
}
|
||||
|
||||
@@ -161,13 +162,15 @@ class Compaction {
|
||||
// is the sum of all input file sizes.
|
||||
uint64_t OutputFilePreallocationSize(const MutableCFOptions& mutable_options);
|
||||
|
||||
void SetInputVersion(Version* input_version);
|
||||
|
||||
private:
|
||||
friend class CompactionPicker;
|
||||
friend class UniversalCompactionPicker;
|
||||
friend class FIFOCompactionPicker;
|
||||
friend class LevelCompactionPicker;
|
||||
|
||||
Compaction(Version* input_version, int start_level, int out_level,
|
||||
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);
|
||||
@@ -193,7 +196,7 @@ class Compaction {
|
||||
autovector<CompactionInputFiles> inputs_;
|
||||
|
||||
// A copy of inputs_, organized more closely in memory
|
||||
autovector<FileLevel, 2> input_levels_;
|
||||
autovector<LevelFilesBrief, 2> input_levels_;
|
||||
|
||||
// State used to check for number of of overlapping grandparent files
|
||||
// (grandparent == "output_level_ + 1")
|
||||
@@ -230,7 +233,8 @@ class Compaction {
|
||||
// bottommost level.
|
||||
//
|
||||
// @see BottomMostLevel()
|
||||
void SetupBottomMostLevel(bool is_manual);
|
||||
void SetupBottomMostLevel(VersionStorageInfo* vstorage, bool is_manual,
|
||||
bool level0_only);
|
||||
|
||||
// In case of compaction error, reset the nextIndex that is used
|
||||
// to pick up the next file to be compacted from files_by_size_
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,130 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#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"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "util/scoped_arena_iterator.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "db/job_context.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class MemTable;
|
||||
class TableCache;
|
||||
class Version;
|
||||
class VersionEdit;
|
||||
class VersionSet;
|
||||
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(Compaction* compaction, const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
port::Mutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
FileNumToPathIdMap* pending_outputs, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Statistics* stats,
|
||||
SnapshotList* snapshot_list, bool is_snapshot_supported,
|
||||
std::shared_ptr<Cache> table_cache,
|
||||
std::function<uint64_t()> yield_callback);
|
||||
|
||||
~CompactionJob() { assert(compact_ == nullptr); }
|
||||
|
||||
// no copy/move
|
||||
CompactionJob(CompactionJob&& job) = delete;
|
||||
CompactionJob(const CompactionJob& job) = delete;
|
||||
CompactionJob& operator=(const CompactionJob& job) = delete;
|
||||
|
||||
// REQUIRED: mutex held
|
||||
void Prepare();
|
||||
// REQUIRED mutex not held
|
||||
Status Run();
|
||||
// REQUIRED: mutex held
|
||||
// status is the return of Run()
|
||||
Status Install(Status status);
|
||||
|
||||
private:
|
||||
void AllocateCompactionOutputFileNumbers();
|
||||
// Call compaction filter if is_compaction_v2 is not true. Then iterate
|
||||
// through input and compact the kv-pairs
|
||||
Status ProcessKeyValueCompaction(int64_t* imm_micros, Iterator* input,
|
||||
bool is_compaction_v2);
|
||||
// Call compaction_filter_v2->Filter() on kv-pairs in compact
|
||||
void CallCompactionFilterV2(CompactionFilterV2* compaction_filter_v2);
|
||||
Status FinishCompactionOutputFile(Iterator* input);
|
||||
Status InstallCompactionResults();
|
||||
SequenceNumber findEarliestVisibleSnapshot(
|
||||
SequenceNumber in, const std::vector<SequenceNumber>& snapshots,
|
||||
SequenceNumber* prev_snapshot);
|
||||
void RecordCompactionIOStats();
|
||||
void ReleaseCompactionUnusedFileNumbers();
|
||||
Status OpenCompactionOutputFile();
|
||||
void CleanupCompaction(Status status);
|
||||
|
||||
// CompactionJob state
|
||||
struct CompactionState;
|
||||
CompactionState* compact_;
|
||||
|
||||
bool bottommost_level_;
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber visible_at_tip_;
|
||||
SequenceNumber latest_snapshot_;
|
||||
|
||||
InternalStats::CompactionStats compaction_stats_;
|
||||
|
||||
// DBImpl state
|
||||
const DBOptions& db_options_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const EnvOptions& env_options_;
|
||||
Env* env_;
|
||||
VersionSet* versions_;
|
||||
port::Mutex* db_mutex_;
|
||||
std::atomic<bool>* shutting_down_;
|
||||
FileNumToPathIdMap* pending_outputs_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
Statistics* stats_;
|
||||
SnapshotList* snapshots_;
|
||||
bool is_snapshot_supported_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
// yield callback
|
||||
std::function<uint64_t()> yield_callback_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+172
-159
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include "db/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/statistics.h"
|
||||
@@ -121,7 +122,9 @@ void CompactionPicker::GetRange(const std::vector<FileMetaData*>& inputs1,
|
||||
GetRange(all, smallest, largest);
|
||||
}
|
||||
|
||||
bool CompactionPicker::ExpandWhileOverlapping(Compaction* c) {
|
||||
bool CompactionPicker::ExpandWhileOverlapping(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
Compaction* c) {
|
||||
assert(c != nullptr);
|
||||
// If inputs are empty then there is nothing to expand.
|
||||
if (c->inputs_[0].empty()) {
|
||||
@@ -148,9 +151,9 @@ bool CompactionPicker::ExpandWhileOverlapping(Compaction* c) {
|
||||
old_size = c->inputs_[0].size();
|
||||
GetRange(c->inputs_[0].files, &smallest, &largest);
|
||||
c->inputs_[0].clear();
|
||||
c->input_version_->GetOverlappingInputs(
|
||||
level, &smallest, &largest, &c->inputs_[0].files,
|
||||
hint_index, &hint_index);
|
||||
vstorage->GetOverlappingInputs(level, &smallest, &largest,
|
||||
&c->inputs_[0].files, hint_index,
|
||||
&hint_index);
|
||||
} while(c->inputs_[0].size() > old_size);
|
||||
|
||||
// Get the new range
|
||||
@@ -160,16 +163,22 @@ bool CompactionPicker::ExpandWhileOverlapping(Compaction* c) {
|
||||
// compaction, then we must drop/cancel this compaction.
|
||||
int parent_index = -1;
|
||||
if (c->inputs_[0].empty()) {
|
||||
Log(ioptions_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] ExpandWhileOverlapping() failure because zero input files",
|
||||
c->column_family_data()->GetName().c_str());
|
||||
cf_name.c_str());
|
||||
}
|
||||
if (c->inputs_[0].empty() || FilesInCompaction(c->inputs_[0].files) ||
|
||||
(c->level() != c->output_level() &&
|
||||
ParentRangeInCompaction(c->input_version_, &smallest, &largest, level,
|
||||
ParentRangeInCompaction(vstorage, &smallest, &largest, level,
|
||||
&parent_index))) {
|
||||
c->inputs_[0].clear();
|
||||
c->inputs_[1].clear();
|
||||
if (!c->inputs_[0].empty()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] ExpandWhileOverlapping() failure because some of the necessary"
|
||||
" compaction input files are currently being compacted.",
|
||||
c->column_family_data()->GetName().c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -186,15 +195,15 @@ bool CompactionPicker::FilesInCompaction(std::vector<FileMetaData*>& files) {
|
||||
}
|
||||
|
||||
// Returns true if any one of the parent files are being compacted
|
||||
bool CompactionPicker::ParentRangeInCompaction(Version* version,
|
||||
bool CompactionPicker::ParentRangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest,
|
||||
int level, int* parent_index) {
|
||||
std::vector<FileMetaData*> inputs;
|
||||
assert(level + 1 < NumberLevels());
|
||||
|
||||
version->GetOverlappingInputs(level + 1, smallest, largest, &inputs,
|
||||
*parent_index, parent_index);
|
||||
vstorage->GetOverlappingInputs(level + 1, smallest, largest, &inputs,
|
||||
*parent_index, parent_index);
|
||||
return FilesInCompaction(inputs);
|
||||
}
|
||||
|
||||
@@ -203,7 +212,8 @@ bool CompactionPicker::ParentRangeInCompaction(Version* version,
|
||||
// or cause "level" to include a file for compaction that has an overlapping
|
||||
// user-key with another file.
|
||||
void CompactionPicker::SetupOtherInputs(
|
||||
const MutableCFOptions& mutable_cf_options, Compaction* c) {
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, Compaction* c) {
|
||||
// If inputs are empty, then there is nothing to expand.
|
||||
// If both input and output levels are the same, no need to consider
|
||||
// files at level "level+1"
|
||||
@@ -218,10 +228,9 @@ void CompactionPicker::SetupOtherInputs(
|
||||
GetRange(c->inputs_[0].files, &smallest, &largest);
|
||||
|
||||
// Populate the set of next-level files (inputs_[1]) to include in compaction
|
||||
c->input_version_->GetOverlappingInputs(
|
||||
level + 1, &smallest, &largest,
|
||||
&c->inputs_[1].files, c->parent_index_,
|
||||
&c->parent_index_);
|
||||
vstorage->GetOverlappingInputs(level + 1, &smallest, &largest,
|
||||
&c->inputs_[1].files, c->parent_index_,
|
||||
&c->parent_index_);
|
||||
|
||||
// Get entire range covered by compaction
|
||||
InternalKey all_start, all_limit;
|
||||
@@ -234,8 +243,8 @@ void CompactionPicker::SetupOtherInputs(
|
||||
// can happen when one user key spans multiple files.
|
||||
if (!c->inputs_[1].empty()) {
|
||||
std::vector<FileMetaData*> expanded0;
|
||||
c->input_version_->GetOverlappingInputs(
|
||||
level, &all_start, &all_limit, &expanded0, c->base_index_, nullptr);
|
||||
vstorage->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0,
|
||||
c->base_index_, nullptr);
|
||||
const uint64_t inputs0_size = TotalCompensatedFileSize(c->inputs_[0].files);
|
||||
const uint64_t inputs1_size = TotalCompensatedFileSize(c->inputs_[1].files);
|
||||
const uint64_t expanded0_size = TotalCompensatedFileSize(expanded0);
|
||||
@@ -243,22 +252,21 @@ void CompactionPicker::SetupOtherInputs(
|
||||
if (expanded0.size() > c->inputs_[0].size() &&
|
||||
inputs1_size + expanded0_size < limit &&
|
||||
!FilesInCompaction(expanded0) &&
|
||||
!c->input_version_->HasOverlappingUserKey(&expanded0, level)) {
|
||||
!vstorage->HasOverlappingUserKey(&expanded0, level)) {
|
||||
InternalKey new_start, new_limit;
|
||||
GetRange(expanded0, &new_start, &new_limit);
|
||||
std::vector<FileMetaData*> expanded1;
|
||||
c->input_version_->GetOverlappingInputs(level + 1, &new_start, &new_limit,
|
||||
&expanded1, c->parent_index_,
|
||||
&c->parent_index_);
|
||||
vstorage->GetOverlappingInputs(level + 1, &new_start, &new_limit,
|
||||
&expanded1, c->parent_index_,
|
||||
&c->parent_index_);
|
||||
if (expanded1.size() == c->inputs_[1].size() &&
|
||||
!FilesInCompaction(expanded1)) {
|
||||
Log(ioptions_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"[%s] Expanding@%d %zu+%zu (%" PRIu64 "+%" PRIu64
|
||||
" bytes) to %zu+%zu (%" PRIu64 "+%" PRIu64 "bytes)\n",
|
||||
c->column_family_data()->GetName().c_str(), level,
|
||||
c->inputs_[0].size(), c->inputs_[1].size(), inputs0_size,
|
||||
inputs1_size, expanded0.size(), expanded1.size(), expanded0_size,
|
||||
inputs1_size);
|
||||
cf_name.c_str(), level, c->inputs_[0].size(), c->inputs_[1].size(),
|
||||
inputs0_size, inputs1_size, expanded0.size(), expanded1.size(),
|
||||
expanded0_size, inputs1_size);
|
||||
smallest = new_start;
|
||||
largest = new_limit;
|
||||
c->inputs_[0].files = expanded0;
|
||||
@@ -272,15 +280,15 @@ void CompactionPicker::SetupOtherInputs(
|
||||
// Compute the set of grandparent files that overlap this compaction
|
||||
// (parent == level+1; grandparent == level+2)
|
||||
if (level + 2 < NumberLevels()) {
|
||||
c->input_version_->GetOverlappingInputs(level + 2, &all_start, &all_limit,
|
||||
&c->grandparents_);
|
||||
vstorage->GetOverlappingInputs(level + 2, &all_start, &all_limit,
|
||||
&c->grandparents_);
|
||||
}
|
||||
}
|
||||
|
||||
Compaction* CompactionPicker::CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options, Version* version,
|
||||
int input_level, int output_level, uint32_t output_path_id,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t output_path_id, const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end) {
|
||||
// CompactionPickerFIFO has its own implementation of compact range
|
||||
assert(ioptions_.compaction_style != kCompactionStyleFIFO);
|
||||
@@ -294,7 +302,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
begin = nullptr;
|
||||
end = nullptr;
|
||||
}
|
||||
version->GetOverlappingInputs(input_level, begin, end, &inputs);
|
||||
vstorage->GetOverlappingInputs(input_level, begin, end, &inputs);
|
||||
if (inputs.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -320,22 +328,20 @@ Compaction* CompactionPicker::CompactRange(
|
||||
}
|
||||
assert(output_path_id < static_cast<uint32_t>(ioptions_.db_paths.size()));
|
||||
Compaction* c = new Compaction(
|
||||
version, input_level, output_level,
|
||||
vstorage->num_levels(), input_level, output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(input_level),
|
||||
output_path_id,
|
||||
GetCompressionType(ioptions_, output_level));
|
||||
output_path_id, GetCompressionType(ioptions_, output_level));
|
||||
|
||||
c->inputs_[0].files = inputs;
|
||||
if (ExpandWhileOverlapping(c) == false) {
|
||||
if (ExpandWhileOverlapping(cf_name, vstorage, c) == false) {
|
||||
delete c;
|
||||
Log(ioptions_.info_log,
|
||||
"[%s] Could not compact due to expansion failure.\n",
|
||||
version->cfd_->GetName().c_str());
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Could not compact due to expansion failure.\n", cf_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SetupOtherInputs(mutable_cf_options, c);
|
||||
SetupOtherInputs(cf_name, mutable_cf_options, vstorage, c);
|
||||
|
||||
if (covering_the_whole_range) {
|
||||
*compaction_end = nullptr;
|
||||
@@ -347,7 +353,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
c->MarkFilesBeingCompacted(true);
|
||||
|
||||
// Is this compaction creating a file at the bottommost level
|
||||
c->SetupBottomMostLevel(true);
|
||||
c->SetupBottomMostLevel(
|
||||
vstorage, true, ioptions_.compaction_style == kCompactionStyleUniversal);
|
||||
|
||||
c->is_manual_compaction_ = true;
|
||||
c->mutable_cf_options_ = mutable_cf_options;
|
||||
@@ -356,8 +363,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, LogBuffer* log_buffer) {
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
Compaction* c = nullptr;
|
||||
int level = -1;
|
||||
|
||||
@@ -365,20 +372,23 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
// and also in LogAndApply(), otherwise the values could be stale.
|
||||
std::vector<uint64_t> size_being_compacted(NumberLevels() - 1);
|
||||
SizeBeingCompacted(size_being_compacted);
|
||||
version->ComputeCompactionScore(mutable_cf_options, size_being_compacted);
|
||||
|
||||
CompactionOptionsFIFO dummy_compaction_options_fifo;
|
||||
vstorage->ComputeCompactionScore(
|
||||
mutable_cf_options, dummy_compaction_options_fifo, size_being_compacted);
|
||||
|
||||
// We prefer compactions triggered by too much data in a level over
|
||||
// the compactions triggered by seeks.
|
||||
//
|
||||
// Find the compactions by size on all levels.
|
||||
for (int i = 0; i < NumberLevels() - 1; i++) {
|
||||
assert(i == 0 ||
|
||||
version->compaction_score_[i] <= version->compaction_score_[i - 1]);
|
||||
level = version->compaction_level_[i];
|
||||
if ((version->compaction_score_[i] >= 1)) {
|
||||
c = PickCompactionBySize(mutable_cf_options, version, level,
|
||||
version->compaction_score_[i]);
|
||||
if (c == nullptr || ExpandWhileOverlapping(c) == false) {
|
||||
double score = vstorage->CompactionScore(i);
|
||||
level = vstorage->CompactionScoreLevel(i);
|
||||
assert(i == 0 || score <= vstorage->CompactionScore(i - 1));
|
||||
if ((score >= 1)) {
|
||||
c = PickCompactionBySize(mutable_cf_options, vstorage, level, score);
|
||||
if (c == nullptr ||
|
||||
ExpandWhileOverlapping(cf_name, vstorage, c) == false) {
|
||||
delete c;
|
||||
c = nullptr;
|
||||
} else {
|
||||
@@ -401,14 +411,14 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
// c->inputs_[0] earlier and replace it with an overlapping set
|
||||
// which will include the picked file.
|
||||
c->inputs_[0].clear();
|
||||
c->input_version_->GetOverlappingInputs(0, &smallest, &largest,
|
||||
&c->inputs_[0].files);
|
||||
vstorage->GetOverlappingInputs(0, &smallest, &largest,
|
||||
&c->inputs_[0].files);
|
||||
|
||||
// If we include more L0 files in the same compaction run it can
|
||||
// cause the 'smallest' and 'largest' key to get extended to a
|
||||
// larger range. So, re-invoke GetRange to get the new key range
|
||||
GetRange(c->inputs_[0].files, &smallest, &largest);
|
||||
if (ParentRangeInCompaction(c->input_version_, &smallest, &largest, level,
|
||||
if (ParentRangeInCompaction(vstorage, &smallest, &largest, level,
|
||||
&c->parent_index_)) {
|
||||
delete c;
|
||||
return nullptr;
|
||||
@@ -417,13 +427,13 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
}
|
||||
|
||||
// Setup "level+1" files (inputs_[1])
|
||||
SetupOtherInputs(mutable_cf_options, c);
|
||||
SetupOtherInputs(cf_name, mutable_cf_options, vstorage, c);
|
||||
|
||||
// mark all the files that are being compacted
|
||||
c->MarkFilesBeingCompacted(true);
|
||||
|
||||
// Is this compaction creating a file at the bottommost level
|
||||
c->SetupBottomMostLevel(false);
|
||||
c->SetupBottomMostLevel(vstorage, false, false);
|
||||
|
||||
// remember this currently undergoing compaction
|
||||
compactions_in_progress_[level].insert(c);
|
||||
@@ -433,8 +443,8 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, int level, double score) {
|
||||
const MutableCFOptions& mutable_cf_options, VersionStorageInfo* vstorage,
|
||||
int level, double score) {
|
||||
Compaction* c = nullptr;
|
||||
|
||||
// level 0 files are overlapping. So we cannot pick more
|
||||
@@ -447,7 +457,7 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
|
||||
assert(level >= 0);
|
||||
assert(level + 1 < NumberLevels());
|
||||
c = new Compaction(version, level, level + 1,
|
||||
c = new Compaction(vstorage->num_levels(), level, level + 1,
|
||||
mutable_cf_options.MaxFileSizeForLevel(level + 1),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(level), 0,
|
||||
GetCompressionType(ioptions_, level + 1));
|
||||
@@ -455,22 +465,21 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
|
||||
// Pick the largest file in this level that is not already
|
||||
// being compacted
|
||||
std::vector<int>& file_size = c->input_version_->files_by_size_[level];
|
||||
const std::vector<int>& file_size = vstorage->FilesBySize(level);
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(level);
|
||||
|
||||
// record the first file that is not yet compacted
|
||||
int nextIndex = -1;
|
||||
|
||||
for (unsigned int i = c->input_version_->next_file_to_compact_by_size_[level];
|
||||
for (unsigned int i = vstorage->NextCompactionIndex(level);
|
||||
i < file_size.size(); i++) {
|
||||
int index = file_size[i];
|
||||
FileMetaData* f = c->input_version_->files_[level][index];
|
||||
FileMetaData* f = level_files[index];
|
||||
|
||||
// Check to verify files are arranged in descending compensated size.
|
||||
assert((i == file_size.size() - 1) ||
|
||||
(i >= Version::number_of_files_to_sort_ - 1) ||
|
||||
(i >= VersionStorageInfo::kNumberFilesToSort - 1) ||
|
||||
(f->compensated_file_size >=
|
||||
c->input_version_->files_[level][file_size[i + 1]]->
|
||||
compensated_file_size));
|
||||
level_files[file_size[i + 1]]->compensated_file_size));
|
||||
|
||||
// do not pick a file to compact if it is being compacted
|
||||
// from n-1 level.
|
||||
@@ -486,8 +495,8 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
// Do not pick this file if its parents at level+1 are being compacted.
|
||||
// Maybe we can avoid redoing this work in SetupOtherInputs
|
||||
int parent_index = -1;
|
||||
if (ParentRangeInCompaction(c->input_version_, &f->smallest, &f->largest,
|
||||
level, &parent_index)) {
|
||||
if (ParentRangeInCompaction(vstorage, &f->smallest, &f->largest, level,
|
||||
&parent_index)) {
|
||||
continue;
|
||||
}
|
||||
c->inputs_[0].files.push_back(f);
|
||||
@@ -502,7 +511,7 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
}
|
||||
|
||||
// store where to start the iteration in the next call to PickCompaction
|
||||
version->next_file_to_compact_by_size_[level] = nextIndex;
|
||||
vstorage->SetNextCompactionIndex(level, nextIndex);
|
||||
|
||||
return c;
|
||||
}
|
||||
@@ -511,62 +520,63 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
// time-range to compact.
|
||||
//
|
||||
Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, LogBuffer* log_buffer) {
|
||||
int level = 0;
|
||||
double score = version->compaction_score_[0];
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
const int kLevel0 = 0;
|
||||
double score = vstorage->CompactionScore(kLevel0);
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
|
||||
if ((version->files_[level].size() <
|
||||
if ((level_files.size() <
|
||||
(unsigned int)mutable_cf_options.level0_file_num_compaction_trigger)) {
|
||||
LogToBuffer(log_buffer, "[%s] Universal: nothing to do\n",
|
||||
version->cfd_->GetName().c_str());
|
||||
LogToBuffer(log_buffer, "[%s] Universal: nothing to do\n", cf_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
Version::FileSummaryStorage tmp;
|
||||
VersionStorageInfo::FileSummaryStorage tmp;
|
||||
LogToBuffer(log_buffer, 3072, "[%s] Universal: candidate files(%zu): %s\n",
|
||||
version->cfd_->GetName().c_str(), version->files_[level].size(),
|
||||
version->LevelFileSummary(&tmp, 0));
|
||||
cf_name.c_str(), level_files.size(),
|
||||
vstorage->LevelFileSummary(&tmp, kLevel0));
|
||||
|
||||
// Check for size amplification first.
|
||||
Compaction* c;
|
||||
if ((c = PickCompactionUniversalSizeAmp(
|
||||
mutable_cf_options, version, score, log_buffer)) != nullptr) {
|
||||
if ((c = PickCompactionUniversalSizeAmp(cf_name, mutable_cf_options, vstorage,
|
||||
score, log_buffer)) != nullptr) {
|
||||
LogToBuffer(log_buffer, "[%s] Universal: compacting for size amp\n",
|
||||
version->cfd_->GetName().c_str());
|
||||
cf_name.c_str());
|
||||
} else {
|
||||
// Size amplification is within limits. Try reducing read
|
||||
// amplification while maintaining file size ratios.
|
||||
unsigned int ratio = ioptions_.compaction_options_universal.size_ratio;
|
||||
|
||||
if ((c = PickCompactionUniversalReadAmp(
|
||||
mutable_cf_options, version, score, ratio,
|
||||
UINT_MAX, log_buffer)) != nullptr) {
|
||||
if ((c = PickCompactionUniversalReadAmp(cf_name, mutable_cf_options,
|
||||
vstorage, score, ratio, UINT_MAX,
|
||||
log_buffer)) != nullptr) {
|
||||
LogToBuffer(log_buffer, "[%s] Universal: compacting for size ratio\n",
|
||||
version->cfd_->GetName().c_str());
|
||||
cf_name.c_str());
|
||||
} else {
|
||||
// Size amplification and file size ratios are within configured limits.
|
||||
// If max read amplification is exceeding configured limits, then force
|
||||
// compaction without looking at filesize ratios and try to reduce
|
||||
// the number of files to fewer than level0_file_num_compaction_trigger.
|
||||
unsigned int num_files = version->files_[level].size() -
|
||||
unsigned int num_files = level_files.size() -
|
||||
mutable_cf_options.level0_file_num_compaction_trigger;
|
||||
if ((c = PickCompactionUniversalReadAmp(
|
||||
mutable_cf_options, version, score, UINT_MAX,
|
||||
cf_name, mutable_cf_options, vstorage, score, UINT_MAX,
|
||||
num_files, log_buffer)) != nullptr) {
|
||||
LogToBuffer(log_buffer, "[%s] Universal: compacting for file num\n",
|
||||
version->cfd_->GetName().c_str());
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(c->inputs_[0].size() > 1);
|
||||
assert(c->inputs_[kLevel0].size() > 1);
|
||||
|
||||
// validate that all the chosen files are non overlapping in time
|
||||
FileMetaData* newerfile __attribute__((unused)) = nullptr;
|
||||
for (unsigned int i = 0; i < c->inputs_[0].size(); i++) {
|
||||
FileMetaData* f = c->inputs_[0][i];
|
||||
for (unsigned int i = 0; i < c->inputs_[kLevel0].size(); i++) {
|
||||
FileMetaData* f = c->inputs_[kLevel0][i];
|
||||
assert (f->smallest_seqno <= f->largest_seqno);
|
||||
assert(newerfile == nullptr ||
|
||||
newerfile->smallest_seqno > f->largest_seqno);
|
||||
@@ -574,23 +584,22 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
}
|
||||
|
||||
// Is the earliest file part of this compaction?
|
||||
FileMetaData* last_file = c->input_version_->files_[level].back();
|
||||
c->bottommost_level_ = c->inputs_[0].files.back() == last_file;
|
||||
FileMetaData* last_file = level_files.back();
|
||||
c->bottommost_level_ = c->inputs_[kLevel0].files.back() == last_file;
|
||||
|
||||
// update statistics
|
||||
MeasureTime(ioptions_.statistics,
|
||||
NUM_FILES_IN_SINGLE_COMPACTION, c->inputs_[0].size());
|
||||
NUM_FILES_IN_SINGLE_COMPACTION, c->inputs_[kLevel0].size());
|
||||
|
||||
// mark all the files that are being compacted
|
||||
c->MarkFilesBeingCompacted(true);
|
||||
|
||||
// remember this currently undergoing compaction
|
||||
compactions_in_progress_[level].insert(c);
|
||||
compactions_in_progress_[kLevel0].insert(c);
|
||||
|
||||
// Record whether this compaction includes all sst files.
|
||||
// For now, it is only relevant in universal compaction mode.
|
||||
c->is_full_compaction_ =
|
||||
(c->inputs_[0].size() == c->input_version_->files_[0].size());
|
||||
c->is_full_compaction_ = (c->inputs_[kLevel0].size() == level_files.size());
|
||||
|
||||
c->mutable_cf_options_ = mutable_cf_options;
|
||||
return c;
|
||||
@@ -632,10 +641,10 @@ uint32_t UniversalCompactionPicker::GetPathId(
|
||||
// the next file in time order.
|
||||
//
|
||||
Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
const MutableCFOptions& mutable_cf_options, Version* version,
|
||||
double score, unsigned int ratio,
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score, unsigned int ratio,
|
||||
unsigned int max_number_of_files_to_compact, LogBuffer* log_buffer) {
|
||||
int level = 0;
|
||||
const int kLevel0 = 0;
|
||||
|
||||
unsigned int min_merge_width =
|
||||
ioptions_.compaction_options_universal.min_merge_width;
|
||||
@@ -643,7 +652,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
ioptions_.compaction_options_universal.max_merge_width;
|
||||
|
||||
// The files are sorted from newest first to oldest last.
|
||||
const auto& files = version->files_[level];
|
||||
const auto& files = vstorage->LevelFiles(kLevel0);
|
||||
|
||||
FileMetaData* f = nullptr;
|
||||
bool done = false;
|
||||
@@ -670,7 +679,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
}
|
||||
LogToBuffer(log_buffer, "[%s] Universal: file %" PRIu64
|
||||
"[%d] being compacted, skipping",
|
||||
version->cfd_->GetName().c_str(), f->fd.GetNumber(), loop);
|
||||
cf_name.c_str(), f->fd.GetNumber(), loop);
|
||||
f = nullptr;
|
||||
}
|
||||
|
||||
@@ -682,14 +691,14 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId(), file_num_buf,
|
||||
sizeof(file_num_buf));
|
||||
LogToBuffer(log_buffer, "[%s] Universal: Possible candidate file %s[%d].",
|
||||
version->cfd_->GetName().c_str(), file_num_buf, loop);
|
||||
cf_name.c_str(), file_num_buf, loop);
|
||||
}
|
||||
|
||||
// Check if the suceeding files need compaction.
|
||||
for (unsigned int i = loop + 1;
|
||||
candidate_count < max_files_to_compact && i < files.size(); i++) {
|
||||
FileMetaData* f = files[i];
|
||||
if (f->being_compacted) {
|
||||
FileMetaData* suceeding_file = files[i];
|
||||
if (suceeding_file->being_compacted) {
|
||||
break;
|
||||
}
|
||||
// Pick files if the total/last candidate file size (increased by the
|
||||
@@ -699,14 +708,14 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
// kCompactionStopStyleSimilarSize, it's simply the size of the last
|
||||
// picked file.
|
||||
double sz = candidate_size * (100.0 + ratio) / 100.0;
|
||||
if (sz < static_cast<double>(f->fd.GetFileSize())) {
|
||||
if (sz < static_cast<double>(suceeding_file->fd.GetFileSize())) {
|
||||
break;
|
||||
}
|
||||
if (ioptions_.compaction_options_universal.stop_style ==
|
||||
kCompactionStopStyleSimilarSize) {
|
||||
// Similar-size stopping rule: also check the last picked file isn't
|
||||
// far larger than the next candidate file.
|
||||
sz = (f->fd.GetFileSize() * (100.0 + ratio)) / 100.0;
|
||||
sz = (suceeding_file->fd.GetFileSize() * (100.0 + ratio)) / 100.0;
|
||||
if (sz < static_cast<double>(candidate_size)) {
|
||||
// If the small file we've encountered begins a run of similar-size
|
||||
// files, we'll pick them up on a future iteration of the outer
|
||||
@@ -714,9 +723,9 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
// by the last-resort read amp strategy which disregards size ratios.
|
||||
break;
|
||||
}
|
||||
candidate_size = f->compensated_file_size;
|
||||
} else { // default kCompactionStopStyleTotalSize
|
||||
candidate_size += f->compensated_file_size;
|
||||
candidate_size = suceeding_file->compensated_file_size;
|
||||
} else { // default kCompactionStopStyleTotalSize
|
||||
candidate_size += suceeding_file->compensated_file_size;
|
||||
}
|
||||
candidate_count++;
|
||||
}
|
||||
@@ -729,13 +738,14 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
} else {
|
||||
for (unsigned int i = loop;
|
||||
i < loop + candidate_count && i < files.size(); i++) {
|
||||
FileMetaData* f = files[i];
|
||||
FileMetaData* skipping_file = files[i];
|
||||
LogToBuffer(log_buffer, "[%s] Universal: Skipping file %" PRIu64
|
||||
"[%d] with size %" PRIu64
|
||||
" (compensated size %" PRIu64 ") %d\n",
|
||||
version->cfd_->GetName().c_str(), f->fd.GetNumber(), i,
|
||||
f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
cf_name.c_str(), f->fd.GetNumber(), i,
|
||||
skipping_file->fd.GetFileSize(),
|
||||
skipping_file->compensated_file_size,
|
||||
skipping_file->being_compacted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,7 +759,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
int ratio_to_compress =
|
||||
ioptions_.compaction_options_universal.compression_size_percent;
|
||||
if (ratio_to_compress >= 0) {
|
||||
uint64_t total_size = version->NumLevelBytes(level);
|
||||
uint64_t total_size = vstorage->NumLevelBytes(kLevel0);
|
||||
uint64_t older_file_size = 0;
|
||||
for (unsigned int i = files.size() - 1;
|
||||
i >= first_index_after; i--) {
|
||||
@@ -768,22 +778,23 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
uint32_t path_id = GetPathId(ioptions_, estimated_total_size);
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
version, level, level, mutable_cf_options.MaxFileSizeForLevel(level),
|
||||
LLONG_MAX, path_id, GetCompressionType(ioptions_, level,
|
||||
enable_compression));
|
||||
vstorage->num_levels(), kLevel0, kLevel0,
|
||||
mutable_cf_options.MaxFileSizeForLevel(kLevel0), LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, kLevel0, enable_compression));
|
||||
c->score_ = score;
|
||||
|
||||
for (unsigned int i = start_index; i < first_index_after; i++) {
|
||||
FileMetaData* f = c->input_version_->files_[level][i];
|
||||
c->inputs_[0].files.push_back(f);
|
||||
FileMetaData* picking_file = files[i];
|
||||
c->inputs_[0].files.push_back(picking_file);
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId(), file_num_buf,
|
||||
sizeof(file_num_buf));
|
||||
FormatFileNumber(picking_file->fd.GetNumber(), picking_file->fd.GetPathId(),
|
||||
file_num_buf, sizeof(file_num_buf));
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: Picking file %s[%d] "
|
||||
"with size %" PRIu64 " (compensated size %" PRIu64 ")\n",
|
||||
version->cfd_->GetName().c_str(), file_num_buf, i,
|
||||
f->fd.GetFileSize(), f->compensated_file_size);
|
||||
cf_name.c_str(), file_num_buf, i,
|
||||
picking_file->fd.GetFileSize(),
|
||||
picking_file->compensated_file_size);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -795,16 +806,16 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
// min_merge_width and max_merge_width).
|
||||
//
|
||||
Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
const MutableCFOptions& mutable_cf_options, Version* version,
|
||||
double score, LogBuffer* log_buffer) {
|
||||
int level = 0;
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score, LogBuffer* log_buffer) {
|
||||
const int kLevel = 0;
|
||||
|
||||
// percentage flexibilty while reducing size amplification
|
||||
uint64_t ratio = ioptions_.compaction_options_universal.
|
||||
max_size_amplification_percent;
|
||||
|
||||
// The files are sorted from newest first to oldest last.
|
||||
const auto& files = version->files_[level];
|
||||
const auto& files = vstorage->LevelFiles(kLevel);
|
||||
|
||||
unsigned int candidate_count = 0;
|
||||
uint64_t candidate_size = 0;
|
||||
@@ -822,10 +833,11 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId(), file_num_buf,
|
||||
sizeof(file_num_buf));
|
||||
LogToBuffer(log_buffer, "[%s] Universal: skipping file %s[%d] compacted %s",
|
||||
version->cfd_->GetName().c_str(), file_num_buf, loop,
|
||||
cf_name.c_str(), file_num_buf, loop,
|
||||
" cannot be a candidate to reduce size amp.\n");
|
||||
f = nullptr;
|
||||
}
|
||||
|
||||
if (f == nullptr) {
|
||||
return nullptr; // no candidate files
|
||||
}
|
||||
@@ -834,19 +846,18 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId(), file_num_buf,
|
||||
sizeof(file_num_buf));
|
||||
LogToBuffer(log_buffer, "[%s] Universal: First candidate file %s[%d] %s",
|
||||
version->cfd_->GetName().c_str(), file_num_buf, start_index,
|
||||
cf_name.c_str(), file_num_buf, start_index,
|
||||
" to reduce size amp.\n");
|
||||
|
||||
// keep adding up all the remaining files
|
||||
for (unsigned int loop = start_index; loop < files.size() - 1; loop++) {
|
||||
f = files[loop];
|
||||
if (f->being_compacted) {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId(), file_num_buf,
|
||||
sizeof(file_num_buf));
|
||||
LogToBuffer(
|
||||
log_buffer, "[%s] Universal: Possible candidate file %s[%d] %s.",
|
||||
version->cfd_->GetName().c_str(), file_num_buf, loop,
|
||||
cf_name.c_str(), file_num_buf, loop,
|
||||
" is already being compacted. No size amp reduction possible.\n");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -866,14 +877,14 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
log_buffer,
|
||||
"[%s] Universal: size amp not needed. newer-files-total-size %" PRIu64
|
||||
"earliest-file-size %" PRIu64,
|
||||
version->cfd_->GetName().c_str(), candidate_size, earliest_file_size);
|
||||
cf_name.c_str(), candidate_size, earliest_file_size);
|
||||
return nullptr;
|
||||
} else {
|
||||
LogToBuffer(
|
||||
log_buffer,
|
||||
"[%s] Universal: size amp needed. newer-files-total-size %" PRIu64
|
||||
"earliest-file-size %" PRIu64,
|
||||
version->cfd_->GetName().c_str(), candidate_size, earliest_file_size);
|
||||
cf_name.c_str(), candidate_size, earliest_file_size);
|
||||
}
|
||||
assert(start_index < files.size() - 1);
|
||||
|
||||
@@ -887,39 +898,41 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
// create a compaction request
|
||||
// We always compact all the files, so always compress.
|
||||
Compaction* c =
|
||||
new Compaction(version, level, level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(level),
|
||||
LLONG_MAX, path_id, GetCompressionType(ioptions_, level));
|
||||
new Compaction(vstorage->num_levels(), kLevel, kLevel,
|
||||
mutable_cf_options.MaxFileSizeForLevel(kLevel), LLONG_MAX,
|
||||
path_id, GetCompressionType(ioptions_, kLevel));
|
||||
c->score_ = score;
|
||||
for (unsigned int loop = start_index; loop < files.size(); loop++) {
|
||||
f = c->input_version_->files_[level][loop];
|
||||
f = files[loop];
|
||||
c->inputs_[0].files.push_back(f);
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: size amp picking file %" PRIu64 "[%d] "
|
||||
"with size %" PRIu64 " (compensated size %" PRIu64 ")",
|
||||
version->cfd_->GetName().c_str(),
|
||||
f->fd.GetNumber(), loop,
|
||||
f->fd.GetFileSize(), f->compensated_file_size);
|
||||
"[%s] Universal: size amp picking file %" PRIu64
|
||||
"[%d] "
|
||||
"with size %" PRIu64 " (compensated size %" PRIu64 ")",
|
||||
cf_name.c_str(), f->fd.GetNumber(), loop, f->fd.GetFileSize(),
|
||||
f->compensated_file_size);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, LogBuffer* log_buffer) {
|
||||
assert(version->NumberLevels() == 1);
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(vstorage->num_levels() == 1);
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
uint64_t total_size = 0;
|
||||
for (const auto& file : version->files_[0]) {
|
||||
for (const auto& file : level_files) {
|
||||
total_size += file->compensated_file_size;
|
||||
}
|
||||
|
||||
if (total_size <= ioptions_.compaction_options_fifo.max_table_files_size ||
|
||||
version->files_[0].size() == 0) {
|
||||
level_files.size() == 0) {
|
||||
// total size not exceeded
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] FIFO compaction: nothing to do. Total size %" PRIu64
|
||||
", max size %" PRIu64 "\n",
|
||||
version->cfd_->GetName().c_str(), total_size,
|
||||
cf_name.c_str(), total_size,
|
||||
ioptions_.compaction_options_fifo.max_table_files_size);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -928,15 +941,14 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] FIFO compaction: Already executing compaction. No need "
|
||||
"to run parallel compactions since compactions are very fast",
|
||||
version->cfd_->GetName().c_str());
|
||||
cf_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(version, 0, 0, 0, 0, 0, kNoCompression, false,
|
||||
Compaction* c = new Compaction(1, 0, 0, 0, 0, 0, kNoCompression, false,
|
||||
true /* is deletion compaction */);
|
||||
// delete old files (FIFO)
|
||||
for (auto ritr = version->files_[0].rbegin();
|
||||
ritr != version->files_[0].rend(); ++ritr) {
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
total_size -= f->compensated_file_size;
|
||||
c->inputs_[0].files.push_back(f);
|
||||
@@ -944,7 +956,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
AppendHumanBytes(f->fd.GetFileSize(), tmp_fsize, sizeof(tmp_fsize));
|
||||
LogToBuffer(log_buffer, "[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with size %s for deletion",
|
||||
version->cfd_->GetName().c_str(), f->fd.GetNumber(), tmp_fsize);
|
||||
cf_name.c_str(), f->fd.GetNumber(), tmp_fsize);
|
||||
if (total_size <= ioptions_.compaction_options_fifo.max_table_files_size) {
|
||||
break;
|
||||
}
|
||||
@@ -957,15 +969,16 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, int input_level, int output_level,
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t output_path_id, const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end) {
|
||||
assert(input_level == 0);
|
||||
assert(output_level == 0);
|
||||
*compaction_end = nullptr;
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, ioptions_.info_log);
|
||||
Compaction* c = PickCompaction(mutable_cf_options, version, &log_buffer);
|
||||
Compaction* c =
|
||||
PickCompaction(cf_name, mutable_cf_options, vstorage, &log_buffer);
|
||||
if (c != nullptr) {
|
||||
assert(output_path_id < static_cast<uint32_t>(ioptions_.db_paths.size()));
|
||||
c->output_path_id_ = output_path_id;
|
||||
|
||||
+37
-28
@@ -18,12 +18,13 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class LogBuffer;
|
||||
class Compaction;
|
||||
class Version;
|
||||
class VersionStorageInfo;
|
||||
|
||||
class CompactionPicker {
|
||||
public:
|
||||
@@ -35,9 +36,10 @@ class CompactionPicker {
|
||||
// Returns nullptr if there is no compaction to be done.
|
||||
// Otherwise returns a pointer to a heap-allocated object that
|
||||
// describes the compaction. Caller should delete the result.
|
||||
virtual Compaction* PickCompaction(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, LogBuffer* log_buffer) = 0;
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) = 0;
|
||||
|
||||
// Return a compaction object for compacting the range [begin,end] in
|
||||
// the specified level. Returns nullptr if there is nothing in that
|
||||
@@ -51,9 +53,9 @@ class CompactionPicker {
|
||||
// Client is responsible for compaction_end storage -- when called,
|
||||
// *compaction_end should point to valid InternalKey!
|
||||
virtual Compaction* CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options, Version* version,
|
||||
int input_level, int output_level, uint32_t output_path_id,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
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
|
||||
@@ -93,18 +95,21 @@ class CompactionPicker {
|
||||
// populated.
|
||||
//
|
||||
// Will return false if it is impossible to apply this compaction.
|
||||
bool ExpandWhileOverlapping(Compaction* c);
|
||||
bool ExpandWhileOverlapping(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage, Compaction* c);
|
||||
|
||||
// Returns true if any one of the specified files are being compacted
|
||||
bool FilesInCompaction(std::vector<FileMetaData*>& files);
|
||||
|
||||
// Returns true if any one of the parent files are being compacted
|
||||
bool ParentRangeInCompaction(Version* version, const InternalKey* smallest,
|
||||
bool ParentRangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level,
|
||||
int* index);
|
||||
|
||||
void SetupOtherInputs(const MutableCFOptions& mutable_cf_options,
|
||||
Compaction* c);
|
||||
void SetupOtherInputs(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, Compaction* c);
|
||||
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
|
||||
@@ -121,9 +126,10 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
UniversalCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
virtual Compaction* PickCompaction(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, LogBuffer* log_buffer) override;
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
// The maxinum allowed input level. Always return 0.
|
||||
virtual int MaxInputLevel(int current_num_levels) const override {
|
||||
@@ -133,14 +139,14 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
private:
|
||||
// Pick Universal compaction to limit read amplification
|
||||
Compaction* PickCompactionUniversalReadAmp(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, double score, unsigned int ratio,
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score, unsigned int ratio,
|
||||
unsigned int num_files, LogBuffer* log_buffer);
|
||||
|
||||
// Pick Universal compaction to limit space amplification.
|
||||
Compaction* PickCompactionUniversalSizeAmp(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, double score, LogBuffer* log_buffer);
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score, LogBuffer* log_buffer);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -153,9 +159,10 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
LevelCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
virtual Compaction* PickCompaction(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, LogBuffer* log_buffer) override;
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
// Returns current_num_levels - 2, meaning the last level cannot be
|
||||
// compaction input level.
|
||||
@@ -169,7 +176,8 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
// 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,
|
||||
Version* version, int level, double score);
|
||||
VersionStorageInfo* vstorage, int level,
|
||||
double score);
|
||||
};
|
||||
|
||||
class FIFOCompactionPicker : public CompactionPicker {
|
||||
@@ -178,14 +186,15 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
|
||||
virtual Compaction* PickCompaction(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
Version* version, LogBuffer* log_buffer) override;
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual Compaction* CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options, Version* version,
|
||||
int input_level, int output_level, uint32_t output_path_id,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t output_path_id, const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end) override;
|
||||
|
||||
// The maxinum allowed input level. Always return 0.
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
// 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/compaction_picker.h"
|
||||
#include <string>
|
||||
#include "util/logging.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
virtual void Logv(const char* format, va_list ap) override { log_count++; }
|
||||
size_t log_count;
|
||||
};
|
||||
|
||||
class CompactionPickerTest {
|
||||
public:
|
||||
const Comparator* ucmp;
|
||||
InternalKeyComparator icmp;
|
||||
Options options;
|
||||
ImmutableCFOptions ioptions;
|
||||
MutableCFOptions mutable_cf_options;
|
||||
LevelCompactionPicker level_compaction_picker;
|
||||
std::string cf_name;
|
||||
CountingLogger logger;
|
||||
LogBuffer log_buffer;
|
||||
VersionStorageInfo vstorage;
|
||||
uint32_t file_num;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
std::vector<uint64_t> size_being_compacted;
|
||||
|
||||
CompactionPickerTest()
|
||||
: ucmp(BytewiseComparator()),
|
||||
icmp(ucmp),
|
||||
ioptions(options),
|
||||
mutable_cf_options(options, ioptions),
|
||||
level_compaction_picker(ioptions, &icmp),
|
||||
cf_name("dummy"),
|
||||
log_buffer(InfoLogLevel::INFO_LEVEL, &logger),
|
||||
vstorage(&icmp, ucmp, options.num_levels, kCompactionStyleLevel,
|
||||
nullptr),
|
||||
file_num(1) {
|
||||
fifo_options.max_table_files_size = 1;
|
||||
mutable_cf_options.RefreshDerivedOptions(ioptions);
|
||||
size_being_compacted.resize(options.num_levels);
|
||||
}
|
||||
|
||||
~CompactionPickerTest() {
|
||||
for (int i = 0; i < vstorage.num_levels(); i++) {
|
||||
for (auto* f : vstorage.LevelFiles(i)) {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 0, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100) {
|
||||
assert(level < vstorage.num_levels());
|
||||
FileMetaData* f = new FileMetaData;
|
||||
f->fd = FileDescriptor(file_number, path_id, file_size);
|
||||
f->smallest = InternalKey(smallest, smallest_seq, kTypeValue);
|
||||
f->largest = InternalKey(largest, largest_seq, kTypeValue);
|
||||
f->compensated_file_size = file_size;
|
||||
f->refs = 0;
|
||||
vstorage.MaybeAddFile(level, f);
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
vstorage.ComputeCompactionScore(mutable_cf_options, fifo_options,
|
||||
size_being_compacted);
|
||||
vstorage.UpdateFilesBySize();
|
||||
vstorage.UpdateNumNonEmptyLevels();
|
||||
vstorage.GenerateFileIndexer();
|
||||
vstorage.GenerateLevelFilesBrief();
|
||||
vstorage.SetFinalized();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(CompactionPickerTest, Empty) {
|
||||
UpdateVersionStorageInfo();
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name, mutable_cf_options, &vstorage, &log_buffer));
|
||||
ASSERT_TRUE(compaction.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, Single) {
|
||||
mutable_cf_options.level0_file_num_compaction_trigger = 2;
|
||||
Add(0, 1U, "p", "q");
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name, mutable_cf_options, &vstorage, &log_buffer));
|
||||
ASSERT_TRUE(compaction.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, Level0Trigger) {
|
||||
mutable_cf_options.level0_file_num_compaction_trigger = 2;
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(0, 2U, "200", "250");
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name, mutable_cf_options, &vstorage, &log_buffer));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, Level1Trigger) {
|
||||
Add(1, 66U, "150", "200", 1000000000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name, mutable_cf_options, &vstorage, &log_buffer));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1, compaction->num_input_files(0));
|
||||
ASSERT_EQ(66U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, Level1Trigger2) {
|
||||
Add(1, 66U, "150", "200", 1000000001U);
|
||||
Add(1, 88U, "201", "300", 1000000000U);
|
||||
Add(2, 6U, "150", "179", 1000000000U);
|
||||
Add(2, 7U, "180", "220", 1000000000U);
|
||||
Add(2, 8U, "221", "300", 1000000000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name, mutable_cf_options, &vstorage, &log_buffer));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1, compaction->num_input_files(0));
|
||||
ASSERT_EQ(2, compaction->num_input_files(1));
|
||||
ASSERT_EQ(66U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(6U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(7U, compaction->input(1, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, LevelMaxScore) {
|
||||
mutable_cf_options.target_file_size_base = 10000000;
|
||||
mutable_cf_options.target_file_size_multiplier = 10;
|
||||
Add(0, 1U, "150", "200", 1000000000U);
|
||||
// Level 1 score 1.2
|
||||
Add(1, 66U, "150", "200", 6000000U);
|
||||
Add(1, 88U, "201", "300", 6000000U);
|
||||
// Level 2 score 1.8. File 7 is the largest. Should be picked
|
||||
Add(2, 6U, "150", "179", 60000000U);
|
||||
Add(2, 7U, "180", "220", 60000001U);
|
||||
Add(2, 8U, "221", "300", 60000000U);
|
||||
// Level 3 score slightly larger than 1
|
||||
Add(3, 26U, "150", "170", 260000000U);
|
||||
Add(3, 27U, "171", "179", 260000000U);
|
||||
Add(3, 28U, "191", "220", 260000000U);
|
||||
Add(3, 29U, "221", "300", 260000000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name, mutable_cf_options, &vstorage, &log_buffer));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1, compaction->num_input_files(0));
|
||||
ASSERT_EQ(7U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
@@ -0,0 +1,434 @@
|
||||
// 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.
|
||||
// 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 <map>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace rocksdb {
|
||||
namespace {
|
||||
|
||||
static const Comparator* comparator;
|
||||
|
||||
// A comparator for std::map, using comparator
|
||||
struct MapComparator {
|
||||
bool operator()(const std::string& a, const std::string& b) const {
|
||||
return comparator->Compare(a, b) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<std::string, std::string, MapComparator> KVMap;
|
||||
|
||||
class KVIter : public Iterator {
|
||||
public:
|
||||
explicit KVIter(const KVMap* map) : map_(map), iter_(map_->end()) {}
|
||||
virtual bool Valid() const { return iter_ != map_->end(); }
|
||||
virtual void SeekToFirst() { iter_ = map_->begin(); }
|
||||
virtual void SeekToLast() {
|
||||
if (map_->empty()) {
|
||||
iter_ = map_->end();
|
||||
} else {
|
||||
iter_ = map_->find(map_->rbegin()->first);
|
||||
}
|
||||
}
|
||||
virtual void Seek(const Slice& k) { iter_ = map_->lower_bound(k.ToString()); }
|
||||
virtual void Next() { ++iter_; }
|
||||
virtual void Prev() {
|
||||
if (iter_ == map_->begin()) {
|
||||
iter_ = map_->end();
|
||||
return;
|
||||
}
|
||||
--iter_;
|
||||
}
|
||||
|
||||
virtual Slice key() const { return iter_->first; }
|
||||
virtual Slice value() const { return iter_->second; }
|
||||
virtual Status status() const { return Status::OK(); }
|
||||
|
||||
private:
|
||||
const KVMap* const map_;
|
||||
KVMap::const_iterator iter_;
|
||||
};
|
||||
|
||||
void AssertItersEqual(Iterator* iter1, Iterator* iter2) {
|
||||
ASSERT_EQ(iter1->Valid(), iter2->Valid());
|
||||
if (iter1->Valid()) {
|
||||
ASSERT_EQ(iter1->key().ToString(), iter2->key().ToString());
|
||||
ASSERT_EQ(iter1->value().ToString(), iter2->value().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Measuring operations on DB (expect to be empty).
|
||||
// source_strings are candidate keys
|
||||
void DoRandomIteraratorTest(DB* db, std::vector<std::string> source_strings,
|
||||
Random* rnd, int num_writes, int num_iter_ops,
|
||||
int num_trigger_flush) {
|
||||
KVMap map;
|
||||
|
||||
for (int i = 0; i < num_writes; i++) {
|
||||
if (num_trigger_flush > 0 && i != 0 && i % num_trigger_flush == 0) {
|
||||
db->Flush(FlushOptions());
|
||||
}
|
||||
|
||||
int type = rnd->Uniform(2);
|
||||
int index = rnd->Uniform(source_strings.size());
|
||||
auto& key = source_strings[index];
|
||||
switch (type) {
|
||||
case 0:
|
||||
// put
|
||||
map[key] = key;
|
||||
ASSERT_OK(db->Put(WriteOptions(), key, key));
|
||||
break;
|
||||
case 1:
|
||||
// delete
|
||||
if (map.find(key) != map.end()) {
|
||||
map.erase(key);
|
||||
}
|
||||
ASSERT_OK(db->Delete(WriteOptions(), key));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> iter(db->NewIterator(ReadOptions()));
|
||||
std::unique_ptr<Iterator> result_iter(new KVIter(&map));
|
||||
|
||||
bool is_valid = false;
|
||||
for (int i = 0; i < num_iter_ops; i++) {
|
||||
// Random walk and make sure iter and result_iter returns the
|
||||
// same key and value
|
||||
int type = rnd->Uniform(6);
|
||||
ASSERT_OK(iter->status());
|
||||
switch (type) {
|
||||
case 0:
|
||||
// Seek to First
|
||||
iter->SeekToFirst();
|
||||
result_iter->SeekToFirst();
|
||||
break;
|
||||
case 1:
|
||||
// Seek to last
|
||||
iter->SeekToLast();
|
||||
result_iter->SeekToLast();
|
||||
break;
|
||||
case 2: {
|
||||
// Seek to random key
|
||||
auto key_idx = rnd->Uniform(source_strings.size());
|
||||
auto key = source_strings[key_idx];
|
||||
iter->Seek(key);
|
||||
result_iter->Seek(key);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
// Next
|
||||
if (is_valid) {
|
||||
iter->Next();
|
||||
result_iter->Next();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
// Prev
|
||||
if (is_valid) {
|
||||
iter->Prev();
|
||||
result_iter->Prev();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
assert(type == 5);
|
||||
auto key_idx = rnd->Uniform(source_strings.size());
|
||||
auto key = source_strings[key_idx];
|
||||
std::string result;
|
||||
auto status = db->Get(ReadOptions(), key, &result);
|
||||
if (map.find(key) == map.end()) {
|
||||
ASSERT_TRUE(status.IsNotFound());
|
||||
} else {
|
||||
ASSERT_EQ(map[key], result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
AssertItersEqual(iter.get(), result_iter.get());
|
||||
is_valid = iter->Valid();
|
||||
}
|
||||
}
|
||||
|
||||
class DoubleComparator : public Comparator {
|
||||
public:
|
||||
DoubleComparator() {}
|
||||
|
||||
virtual const char* Name() const { return "DoubleComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
double da = std::stod(a.ToString());
|
||||
double db = std::stod(b.ToString());
|
||||
if (da == db) {
|
||||
return a.compare(b);
|
||||
} else if (da > db) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const {}
|
||||
};
|
||||
|
||||
class HashComparator : public Comparator {
|
||||
public:
|
||||
HashComparator() {}
|
||||
|
||||
virtual const char* Name() const { return "HashComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
uint32_t ha = Hash(a.data(), a.size(), 66);
|
||||
uint32_t hb = Hash(b.data(), b.size(), 66);
|
||||
if (ha == hb) {
|
||||
return a.compare(b);
|
||||
} else if (ha > hb) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const {}
|
||||
};
|
||||
|
||||
class TwoStrComparator : public Comparator {
|
||||
public:
|
||||
TwoStrComparator() {}
|
||||
|
||||
virtual const char* Name() const { return "TwoStrComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
assert(a.size() >= 2);
|
||||
assert(b.size() >= 2);
|
||||
size_t size_a1 = static_cast<size_t>(a[0]);
|
||||
size_t size_b1 = static_cast<size_t>(b[0]);
|
||||
size_t size_a2 = static_cast<size_t>(a[1]);
|
||||
size_t size_b2 = static_cast<size_t>(b[1]);
|
||||
assert(size_a1 + size_a2 + 2 == a.size());
|
||||
assert(size_b1 + size_b2 + 2 == b.size());
|
||||
|
||||
Slice a1 = Slice(a.data() + 2, size_a1);
|
||||
Slice b1 = Slice(b.data() + 2, size_b1);
|
||||
Slice a2 = Slice(a.data() + 2 + size_a1, size_a2);
|
||||
Slice b2 = Slice(b.data() + 2 + size_b1, size_b2);
|
||||
|
||||
if (a1 != b1) {
|
||||
return a1.compare(b1);
|
||||
}
|
||||
return a2.compare(b2);
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const {}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class ComparatorDBTest {
|
||||
private:
|
||||
std::string dbname_;
|
||||
Env* env_;
|
||||
DB* db_;
|
||||
Options last_options_;
|
||||
std::unique_ptr<const Comparator> comparator_guard;
|
||||
|
||||
public:
|
||||
ComparatorDBTest() : env_(Env::Default()), db_(nullptr) {
|
||||
comparator = BytewiseComparator();
|
||||
dbname_ = test::TmpDir() + "/comparator_db_test";
|
||||
ASSERT_OK(DestroyDB(dbname_, last_options_));
|
||||
}
|
||||
|
||||
~ComparatorDBTest() {
|
||||
delete db_;
|
||||
ASSERT_OK(DestroyDB(dbname_, last_options_));
|
||||
comparator = BytewiseComparator();
|
||||
}
|
||||
|
||||
DB* GetDB() { return db_; }
|
||||
|
||||
void SetOwnedComparator(const Comparator* cmp) {
|
||||
comparator_guard.reset(cmp);
|
||||
comparator = cmp;
|
||||
last_options_.comparator = cmp;
|
||||
}
|
||||
|
||||
// Return the current option configuration.
|
||||
Options* GetOptions() { return &last_options_; }
|
||||
|
||||
void DestroyAndReopen() {
|
||||
// Destroy using last options
|
||||
Destroy();
|
||||
ASSERT_OK(TryReopen());
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
ASSERT_OK(DestroyDB(dbname_, last_options_));
|
||||
}
|
||||
|
||||
Status TryReopen() {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
last_options_.create_if_missing = true;
|
||||
|
||||
return DB::Open(last_options_, dbname_, &db_);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ComparatorDBTest, Bytewise) {
|
||||
for (int rand_seed = 301; rand_seed < 306; rand_seed++) {
|
||||
DestroyAndReopen();
|
||||
Random rnd(rand_seed);
|
||||
DoRandomIteraratorTest(GetDB(),
|
||||
{"a", "b", "c", "d", "e", "f", "g", "h", "i"}, &rnd,
|
||||
8, 100, 3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, SimpleSuffixReverseComparator) {
|
||||
SetOwnedComparator(new test::SimpleSuffixReverseComparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
Options* opt = GetOptions();
|
||||
opt->comparator = comparator;
|
||||
DestroyAndReopen();
|
||||
Random rnd(rnd_seed);
|
||||
|
||||
std::vector<std::string> source_strings;
|
||||
std::vector<std::string> source_prefixes;
|
||||
// Randomly generate 5 prefixes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
source_prefixes.push_back(test::RandomHumanReadableString(&rnd, 8));
|
||||
}
|
||||
for (int j = 0; j < 20; j++) {
|
||||
int prefix_index = rnd.Uniform(source_prefixes.size());
|
||||
std::string key = source_prefixes[prefix_index] +
|
||||
test::RandomHumanReadableString(&rnd, rnd.Uniform(8));
|
||||
source_strings.push_back(key);
|
||||
}
|
||||
|
||||
DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 30, 600, 66);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, Uint64Comparator) {
|
||||
SetOwnedComparator(test::Uint64Comparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
Options* opt = GetOptions();
|
||||
opt->comparator = comparator;
|
||||
DestroyAndReopen();
|
||||
Random rnd(rnd_seed);
|
||||
Random64 rnd64(rnd_seed);
|
||||
|
||||
std::vector<std::string> source_strings;
|
||||
// Randomly generate source keys
|
||||
for (int i = 0; i < 100; i++) {
|
||||
uint64_t r = rnd64.Next();
|
||||
std::string str;
|
||||
str.resize(8);
|
||||
memcpy(&str[0], static_cast<void*>(&r), 8);
|
||||
source_strings.push_back(str);
|
||||
}
|
||||
|
||||
DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, DoubleComparator) {
|
||||
SetOwnedComparator(new DoubleComparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
Options* opt = GetOptions();
|
||||
opt->comparator = comparator;
|
||||
DestroyAndReopen();
|
||||
Random rnd(rnd_seed);
|
||||
|
||||
std::vector<std::string> source_strings;
|
||||
// Randomly generate source keys
|
||||
for (int i = 0; i < 100; i++) {
|
||||
uint32_t r = rnd.Next();
|
||||
uint32_t divide_order = rnd.Uniform(8);
|
||||
double to_divide = 1.0;
|
||||
for (uint32_t j = 0; j < divide_order; j++) {
|
||||
to_divide *= 10.0;
|
||||
}
|
||||
source_strings.push_back(std::to_string(r / to_divide));
|
||||
}
|
||||
|
||||
DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, HashComparator) {
|
||||
SetOwnedComparator(new HashComparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
Options* opt = GetOptions();
|
||||
opt->comparator = comparator;
|
||||
DestroyAndReopen();
|
||||
Random rnd(rnd_seed);
|
||||
|
||||
std::vector<std::string> source_strings;
|
||||
// Randomly generate source keys
|
||||
for (int i = 0; i < 100; i++) {
|
||||
source_strings.push_back(test::RandomKey(&rnd, 8));
|
||||
}
|
||||
|
||||
DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, TwoStrComparator) {
|
||||
SetOwnedComparator(new TwoStrComparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
Options* opt = GetOptions();
|
||||
opt->comparator = comparator;
|
||||
DestroyAndReopen();
|
||||
Random rnd(rnd_seed);
|
||||
|
||||
std::vector<std::string> source_strings;
|
||||
// Randomly generate source keys
|
||||
for (int i = 0; i < 100; i++) {
|
||||
std::string str;
|
||||
uint32_t size1 = rnd.Uniform(8);
|
||||
uint32_t size2 = rnd.Uniform(8);
|
||||
str.append(1, static_cast<char>(size1));
|
||||
str.append(1, static_cast<char>(size2));
|
||||
str.append(test::RandomKey(&rnd, size1));
|
||||
str.append(test::RandomKey(&rnd, size2));
|
||||
source_strings.push_back(str);
|
||||
}
|
||||
|
||||
DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
+176
-8
@@ -168,8 +168,9 @@ DEFINE_int32(duration, 0, "Time in seconds for the random-ops tests to run."
|
||||
|
||||
DEFINE_int32(value_size, 100, "Size of each value");
|
||||
|
||||
DEFINE_int32(seekseq_next, 0, "How many times to call Next() after Seek() in "
|
||||
"fillseekseq");
|
||||
DEFINE_int32(seek_nexts, 0,
|
||||
"How many times to call Next() after Seek() in "
|
||||
"fillseekseq and seekrandom");
|
||||
|
||||
DEFINE_bool(use_uint64_comparator, false, "use Uint64 user comparator");
|
||||
|
||||
@@ -574,6 +575,8 @@ DEFINE_string(merge_operator, "", "The merge operator to use with the database."
|
||||
DEFINE_int32(skip_list_lookahead, 0, "Used with skip_list memtablerep; try "
|
||||
"linear search first for this many steps from the previous "
|
||||
"position");
|
||||
DEFINE_bool(report_file_operations, false, "if report number of file "
|
||||
"operations");
|
||||
|
||||
static const bool FLAGS_soft_rate_limit_dummy __attribute__((unused)) =
|
||||
RegisterFlagValidator(&FLAGS_soft_rate_limit, &ValidateRateLimit);
|
||||
@@ -605,6 +608,131 @@ static const bool FLAGS_table_cache_numshardbits_dummy __attribute__((unused)) =
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
struct ReportFileOpCounters {
|
||||
std::atomic<int> open_counter_;
|
||||
std::atomic<int> read_counter_;
|
||||
std::atomic<int> append_counter_;
|
||||
std::atomic<uint64_t> bytes_read_;
|
||||
std::atomic<uint64_t> bytes_written_;
|
||||
};
|
||||
|
||||
// A special Env to records and report file operations in db_bench
|
||||
class ReportFileOpEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit ReportFileOpEnv(Env* base) : EnvWrapper(base) { reset(); }
|
||||
|
||||
void reset() {
|
||||
counters_.open_counter_ = 0;
|
||||
counters_.read_counter_ = 0;
|
||||
counters_.append_counter_ = 0;
|
||||
counters_.bytes_read_ = 0;
|
||||
counters_.bytes_written_ = 0;
|
||||
}
|
||||
|
||||
Status NewSequentialFile(const std::string& f, unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) {
|
||||
class CountingFile : public SequentialFile {
|
||||
private:
|
||||
unique_ptr<SequentialFile> target_;
|
||||
ReportFileOpCounters* counters_;
|
||||
|
||||
public:
|
||||
CountingFile(unique_ptr<SequentialFile>&& target,
|
||||
ReportFileOpCounters* counters)
|
||||
: target_(std::move(target)), counters_(counters) {}
|
||||
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) {
|
||||
counters_->read_counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
Status rv = target_->Read(n, result, scratch);
|
||||
counters_->bytes_read_.fetch_add(result->size(),
|
||||
std::memory_order_relaxed);
|
||||
return rv;
|
||||
}
|
||||
|
||||
virtual Status Skip(uint64_t n) { return target_->Skip(n); }
|
||||
};
|
||||
|
||||
Status s = target()->NewSequentialFile(f, r, soptions);
|
||||
if (s.ok()) {
|
||||
counters()->open_counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
r->reset(new CountingFile(std::move(*r), counters()));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status NewRandomAccessFile(const std::string& f,
|
||||
unique_ptr<RandomAccessFile>* r,
|
||||
const EnvOptions& soptions) {
|
||||
class CountingFile : public RandomAccessFile {
|
||||
private:
|
||||
unique_ptr<RandomAccessFile> target_;
|
||||
ReportFileOpCounters* counters_;
|
||||
|
||||
public:
|
||||
CountingFile(unique_ptr<RandomAccessFile>&& target,
|
||||
ReportFileOpCounters* counters)
|
||||
: target_(std::move(target)), counters_(counters) {}
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const {
|
||||
counters_->read_counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
Status rv = target_->Read(offset, n, result, scratch);
|
||||
counters_->bytes_read_.fetch_add(result->size(),
|
||||
std::memory_order_relaxed);
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
Status s = target()->NewRandomAccessFile(f, r, soptions);
|
||||
if (s.ok()) {
|
||||
counters()->open_counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
r->reset(new CountingFile(std::move(*r), counters()));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status NewWritableFile(const std::string& f, unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& soptions) {
|
||||
class CountingFile : public WritableFile {
|
||||
private:
|
||||
unique_ptr<WritableFile> target_;
|
||||
ReportFileOpCounters* counters_;
|
||||
|
||||
public:
|
||||
CountingFile(unique_ptr<WritableFile>&& target,
|
||||
ReportFileOpCounters* counters)
|
||||
: target_(std::move(target)), counters_(counters) {}
|
||||
|
||||
Status Append(const Slice& data) {
|
||||
counters_->append_counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
Status rv = target_->Append(data);
|
||||
counters_->bytes_written_.fetch_add(data.size(),
|
||||
std::memory_order_relaxed);
|
||||
return rv;
|
||||
}
|
||||
|
||||
Status Close() { return target_->Close(); }
|
||||
Status Flush() { return target_->Flush(); }
|
||||
Status Sync() { return target_->Sync(); }
|
||||
};
|
||||
|
||||
Status s = target()->NewWritableFile(f, r, soptions);
|
||||
if (s.ok()) {
|
||||
counters()->open_counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
r->reset(new CountingFile(std::move(*r), counters()));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// getter
|
||||
ReportFileOpCounters* counters() { return &counters_; }
|
||||
|
||||
private:
|
||||
ReportFileOpCounters counters_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Helper for quickly generating random data.
|
||||
class RandomGenerator {
|
||||
private:
|
||||
@@ -809,6 +937,21 @@ class Stats {
|
||||
if (FLAGS_histogram) {
|
||||
fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
|
||||
}
|
||||
if (FLAGS_report_file_operations) {
|
||||
ReportFileOpEnv* env = static_cast<ReportFileOpEnv*>(FLAGS_env);
|
||||
ReportFileOpCounters* counters = env->counters();
|
||||
fprintf(stdout, "Num files opened: %d\n",
|
||||
counters->open_counter_.load(std::memory_order_relaxed));
|
||||
fprintf(stdout, "Num Read(): %d\n",
|
||||
counters->read_counter_.load(std::memory_order_relaxed));
|
||||
fprintf(stdout, "Num Append(): %d\n",
|
||||
counters->append_counter_.load(std::memory_order_relaxed));
|
||||
fprintf(stdout, "Num bytes read: %" PRIu64 "\n",
|
||||
counters->bytes_read_.load(std::memory_order_relaxed));
|
||||
fprintf(stdout, "Num bytes written: %" PRIu64 "\n",
|
||||
counters->bytes_written_.load(std::memory_order_relaxed));
|
||||
env->reset();
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
};
|
||||
@@ -898,6 +1041,7 @@ class Benchmark {
|
||||
int64_t writes_;
|
||||
int64_t readwrites_;
|
||||
int64_t merge_keys_;
|
||||
bool report_file_operations_;
|
||||
|
||||
bool SanityCheck() {
|
||||
if (FLAGS_compression_ratio > 1) {
|
||||
@@ -1117,7 +1261,18 @@ class Benchmark {
|
||||
readwrites_((FLAGS_writes < 0 && FLAGS_reads < 0)? FLAGS_num :
|
||||
((FLAGS_writes > FLAGS_reads) ? FLAGS_writes : FLAGS_reads)
|
||||
),
|
||||
merge_keys_(FLAGS_merge_keys < 0 ? FLAGS_num : FLAGS_merge_keys) {
|
||||
merge_keys_(FLAGS_merge_keys < 0 ? FLAGS_num : FLAGS_merge_keys),
|
||||
report_file_operations_(FLAGS_report_file_operations) {
|
||||
if (report_file_operations_) {
|
||||
if (!FLAGS_hdfs.empty()) {
|
||||
fprintf(stderr,
|
||||
"--hdfs and --report_file_operations cannot be enabled "
|
||||
"at the same time");
|
||||
exit(1);
|
||||
}
|
||||
FLAGS_env = new ReportFileOpEnv(rocksdb::Env::Default());
|
||||
}
|
||||
|
||||
if (FLAGS_prefix_size > FLAGS_key_size) {
|
||||
fprintf(stderr, "prefix size is larger than key size");
|
||||
exit(1);
|
||||
@@ -1231,7 +1386,7 @@ class Benchmark {
|
||||
writes_ = (FLAGS_writes < 0 ? FLAGS_num : FLAGS_writes);
|
||||
value_size_ = FLAGS_value_size;
|
||||
key_size_ = FLAGS_key_size;
|
||||
entries_per_batch_ = 1;
|
||||
entries_per_batch_ = FLAGS_batch_size;
|
||||
write_options_ = WriteOptions();
|
||||
if (FLAGS_sync) {
|
||||
write_options_.sync = true;
|
||||
@@ -1286,7 +1441,6 @@ class Benchmark {
|
||||
} else if (name == Slice("readrandomfast")) {
|
||||
method = &Benchmark::ReadRandomFast;
|
||||
} else if (name == Slice("multireadrandom")) {
|
||||
entries_per_batch_ = FLAGS_batch_size;
|
||||
fprintf(stderr, "entries_per_batch = %" PRIi64 "\n",
|
||||
entries_per_batch_);
|
||||
method = &Benchmark::MultiReadRandom;
|
||||
@@ -1533,13 +1687,13 @@ class Benchmark {
|
||||
|
||||
void AcquireLoad(ThreadState* thread) {
|
||||
int dummy;
|
||||
port::AtomicPointer ap(&dummy);
|
||||
std::atomic<void*> ap(&dummy);
|
||||
int count = 0;
|
||||
void *ptr = nullptr;
|
||||
thread->stats.AddMessage("(each op is 1000 loads)");
|
||||
while (count < 100000) {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
ptr = ap.Acquire_Load();
|
||||
ptr = ap.load(std::memory_order_acquire);
|
||||
}
|
||||
count++;
|
||||
thread->stats.FinishedOps(nullptr, nullptr, 1);
|
||||
@@ -2265,6 +2419,7 @@ class Benchmark {
|
||||
std::unique_ptr<const char[]> key_guard(key.data());
|
||||
|
||||
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();
|
||||
@@ -2296,6 +2451,16 @@ class Benchmark {
|
||||
if (iter_to_use->Valid() && iter_to_use->key().compare(key) == 0) {
|
||||
found++;
|
||||
}
|
||||
|
||||
for (int j = 0; j < FLAGS_seek_nexts && iter_to_use->Valid(); ++j) {
|
||||
// Copy out iterator's value to make sure we read them.
|
||||
Slice value = iter_to_use->value();
|
||||
memcpy(value_buffer, value.data(),
|
||||
std::min(value.size(), sizeof(value_buffer)));
|
||||
iter_to_use->Next();
|
||||
assert(iter_to_use->status().ok());
|
||||
}
|
||||
|
||||
thread->stats.FinishedOps(&db_, db_.db, 1);
|
||||
}
|
||||
delete single_iter;
|
||||
@@ -2820,7 +2985,7 @@ class Benchmark {
|
||||
assert(iter->Valid() && iter->key() == key);
|
||||
thread->stats.FinishedOps(nullptr, db, 1);
|
||||
|
||||
for (int j = 0; j < FLAGS_seekseq_next && i+1 < FLAGS_num; ++j) {
|
||||
for (int j = 0; j < FLAGS_seek_nexts && i + 1 < FLAGS_num; ++j) {
|
||||
iter->Next();
|
||||
GenerateKeyFromInt(++i, FLAGS_num, &key);
|
||||
assert(iter->Valid() && iter->key() == key);
|
||||
@@ -2904,6 +3069,9 @@ int main(int argc, char** argv) {
|
||||
// The number of background threads should be at least as much the
|
||||
// max number of concurrent compactions.
|
||||
FLAGS_env->SetBackgroundThreads(FLAGS_max_background_compactions);
|
||||
FLAGS_env->SetBackgroundThreads(FLAGS_max_background_flushes,
|
||||
rocksdb::Env::Priority::HIGH);
|
||||
|
||||
// Choose a location for the test database if none given with --db=<path>
|
||||
if (FLAGS_db.empty()) {
|
||||
std::string default_db_path;
|
||||
|
||||
+14
-61
@@ -32,9 +32,10 @@ Status DBImpl::DisableFileDeletions() {
|
||||
MutexLock l(&mutex_);
|
||||
++disable_delete_obsolete_files_;
|
||||
if (disable_delete_obsolete_files_ == 1) {
|
||||
Log(db_options_.info_log, "File Deletions Disabled");
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"File Deletions Disabled");
|
||||
} else {
|
||||
Log(db_options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
@@ -42,7 +43,7 @@ Status DBImpl::DisableFileDeletions() {
|
||||
}
|
||||
|
||||
Status DBImpl::EnableFileDeletions(bool force) {
|
||||
DeletionState deletion_state;
|
||||
JobContext job_context;
|
||||
bool should_purge_files = false;
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
@@ -53,17 +54,18 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
if (disable_delete_obsolete_files_ == 0) {
|
||||
Log(db_options_.info_log, "File Deletions Enabled");
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"File Deletions Enabled");
|
||||
should_purge_files = true;
|
||||
FindObsoleteFiles(deletion_state, true);
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
} else {
|
||||
Log(db_options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
}
|
||||
if (should_purge_files) {
|
||||
PurgeObsoleteFiles(deletion_state);
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
LogFlush(db_options_.info_log);
|
||||
return Status::OK();
|
||||
@@ -98,8 +100,8 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
if (!status.ok()) {
|
||||
mutex_.Unlock();
|
||||
Log(db_options_.info_log, "Cannot Flush data %s\n",
|
||||
status.ToString().c_str());
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"Cannot Flush data %s\n", status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -120,67 +122,18 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
|
||||
ret.push_back(CurrentFileName(""));
|
||||
ret.push_back(DescriptorFileName("", versions_->ManifestFileNumber()));
|
||||
ret.push_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
|
||||
// find length of manifest file while holding the mutex lock
|
||||
*manifest_file_size = versions_->ManifestFileSize();
|
||||
*manifest_file_size = versions_->manifest_file_size();
|
||||
|
||||
mutex_.Unlock();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// First get sorted files in db dir, then get sorted files from archived
|
||||
// dir, to avoid a race condition where a log file is moved to archived
|
||||
// dir in between.
|
||||
Status s;
|
||||
// list wal files in main db dir.
|
||||
VectorLogPtr logs;
|
||||
s = GetSortedWalsOfType(db_options_.wal_dir, logs, kAliveLogFile);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Reproduce the race condition where a log file is moved
|
||||
// to archived dir, between these two sync points, used in
|
||||
// (DBTest,TransactionLogIteratorRace)
|
||||
TEST_SYNC_POINT("DBImpl::GetSortedWalFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetSortedWalFiles:2");
|
||||
|
||||
files.clear();
|
||||
// list wal files in archive dir.
|
||||
std::string archivedir = ArchivalDirectory(db_options_.wal_dir);
|
||||
if (env_->FileExists(archivedir)) {
|
||||
s = GetSortedWalsOfType(archivedir, files, kArchivedLogFile);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t latest_archived_log_number = 0;
|
||||
if (!files.empty()) {
|
||||
latest_archived_log_number = files.back()->LogNumber();
|
||||
Log(db_options_.info_log, "Latest Archived log: %" PRIu64,
|
||||
latest_archived_log_number);
|
||||
}
|
||||
|
||||
files.reserve(files.size() + logs.size());
|
||||
for (auto& log : logs) {
|
||||
if (log->LogNumber() > latest_archived_log_number) {
|
||||
files.push_back(std::move(log));
|
||||
} else {
|
||||
// When the race condition happens, we could see the
|
||||
// same log in both db dir and archived dir. Simply
|
||||
// ignore the one in db dir. Note that, if we read
|
||||
// archived dir first, we would have missed the log file.
|
||||
Log(db_options_.info_log, "%s already moved to archive",
|
||||
log->PathName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
return wal_manager_.GetSortedWalFiles(files);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
+401
-1868
File diff suppressed because it is too large.
Load diff
+31
-175
@@ -21,6 +21,7 @@
|
||||
#include "db/snapshot.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/wal_manager.h"
|
||||
#include "memtable_list.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "db/write_controller.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "db/job_context.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -113,7 +115,7 @@ class DBImpl : public DB {
|
||||
uint32_t target_path_id = 0);
|
||||
|
||||
using DB::SetOptions;
|
||||
bool SetOptions(ColumnFamilyHandle* column_family,
|
||||
Status SetOptions(ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map);
|
||||
|
||||
using DB::NumberLevels;
|
||||
@@ -192,25 +194,12 @@ class DBImpl : public DB {
|
||||
// Return the current manifest file no.
|
||||
uint64_t TEST_Current_Manifest_FileNo();
|
||||
|
||||
// Trigger's a background call for testing.
|
||||
void TEST_PurgeObsoleteteWAL();
|
||||
|
||||
// get total level0 file size. Only for testing.
|
||||
uint64_t TEST_GetLevel0TotalSize();
|
||||
|
||||
void TEST_SetDefaultTimeToCheck(uint64_t default_interval_to_delete_obsolete_WAL)
|
||||
{
|
||||
default_interval_to_delete_obsolete_WAL_ = default_interval_to_delete_obsolete_WAL;
|
||||
}
|
||||
|
||||
void TEST_GetFilesMetaData(ColumnFamilyHandle* column_family,
|
||||
std::vector<std::vector<FileMetaData>>* metadata);
|
||||
|
||||
Status TEST_ReadFirstRecord(const WalFileType type, const uint64_t number,
|
||||
SequenceNumber* sequence);
|
||||
|
||||
Status TEST_ReadFirstLine(const std::string& fname, SequenceNumber* sequence);
|
||||
|
||||
void TEST_LockMutex();
|
||||
|
||||
void TEST_UnlockMutex();
|
||||
@@ -223,88 +212,19 @@ class DBImpl : public DB {
|
||||
void TEST_EndWrite(void* w);
|
||||
#endif // NDEBUG
|
||||
|
||||
// Structure to store information for candidate files to delete.
|
||||
struct CandidateFileInfo {
|
||||
std::string file_name;
|
||||
uint32_t path_id;
|
||||
CandidateFileInfo(std::string name, uint32_t path)
|
||||
: file_name(name), path_id(path) {}
|
||||
bool operator==(const CandidateFileInfo& other) const {
|
||||
return file_name == other.file_name && path_id == other.path_id;
|
||||
}
|
||||
};
|
||||
|
||||
// needed for CleanupIteratorState
|
||||
struct DeletionState {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return candidate_files.size() ||
|
||||
sst_delete_files.size() ||
|
||||
log_delete_files.size();
|
||||
}
|
||||
|
||||
// a list of all files that we'll consider deleting
|
||||
// (every once in a while this is filled up with all files
|
||||
// in the DB directory)
|
||||
std::vector<CandidateFileInfo> candidate_files;
|
||||
|
||||
// the list of all live sst files that cannot be deleted
|
||||
std::vector<FileDescriptor> sst_live;
|
||||
|
||||
// a list of sst files that we need to delete
|
||||
std::vector<FileMetaData*> sst_delete_files;
|
||||
|
||||
// a list of log files that we need to delete
|
||||
std::vector<uint64_t> log_delete_files;
|
||||
|
||||
// a list of memtables to be free
|
||||
autovector<MemTable*> memtables_to_free;
|
||||
|
||||
autovector<SuperVersion*> superversions_to_free;
|
||||
|
||||
SuperVersion* new_superversion; // if nullptr no new superversion
|
||||
|
||||
// the current manifest_file_number, log_number and prev_log_number
|
||||
// that corresponds to the set of files in 'live'.
|
||||
uint64_t manifest_file_number, pending_manifest_file_number, log_number,
|
||||
prev_log_number;
|
||||
|
||||
explicit DeletionState(bool create_superversion = false) {
|
||||
manifest_file_number = 0;
|
||||
pending_manifest_file_number = 0;
|
||||
log_number = 0;
|
||||
prev_log_number = 0;
|
||||
new_superversion = create_superversion ? new SuperVersion() : nullptr;
|
||||
}
|
||||
|
||||
~DeletionState() {
|
||||
// free pending memtables
|
||||
for (auto m : memtables_to_free) {
|
||||
delete m;
|
||||
}
|
||||
// free superversions
|
||||
for (auto s : superversions_to_free) {
|
||||
delete s;
|
||||
}
|
||||
// if new_superversion was not used, it will be non-nullptr and needs
|
||||
// to be freed here
|
||||
delete new_superversion;
|
||||
}
|
||||
};
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
// of all files in the filesystem in 'candidate_files'.
|
||||
// If force == false and the last call was less than
|
||||
// db_options_.delete_obsolete_files_period_micros microseconds ago,
|
||||
// it will not fill up the deletion_state
|
||||
void FindObsoleteFiles(DeletionState& deletion_state,
|
||||
bool force,
|
||||
// it will not fill up the job_context
|
||||
void FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
bool no_full_scan = false);
|
||||
|
||||
// Diffs the files listed in filenames and those that do not
|
||||
// belong to live files are posibly removed. Also, removes all the
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
void PurgeObsoleteFiles(DeletionState& deletion_state);
|
||||
void PurgeObsoleteFiles(const JobContext& background_contet);
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const;
|
||||
|
||||
@@ -347,9 +267,10 @@ class DBImpl : public DB {
|
||||
|
||||
// Flush the in-memory write buffer to storage. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful.
|
||||
Status FlushMemTableToOutputFile(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
bool* madeProgress, DeletionState& deletion_state, LogBuffer* log_buffer);
|
||||
Status FlushMemTableToOutputFile(ColumnFamilyData* cfd,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
@@ -362,12 +283,7 @@ class DBImpl : public DB {
|
||||
// concurrent flush memtables to storage.
|
||||
Status WriteLevel0TableForRecovery(ColumnFamilyData* cfd, MemTable* mem,
|
||||
VersionEdit* edit);
|
||||
Status WriteLevel0Table(ColumnFamilyData* cfd,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& mems,
|
||||
VersionEdit* edit, uint64_t* filenumber, LogBuffer* log_buffer);
|
||||
|
||||
void DelayWrite(uint64_t expiration_time);
|
||||
Status DelayWrite(uint64_t expiration_time);
|
||||
|
||||
Status ScheduleFlushes(WriteContext* context);
|
||||
|
||||
@@ -388,75 +304,17 @@ class DBImpl : public DB {
|
||||
static void BGWorkFlush(void* db);
|
||||
void BackgroundCallCompaction();
|
||||
void BackgroundCallFlush();
|
||||
Status BackgroundCompaction(bool* madeProgress, DeletionState& deletion_state,
|
||||
Status BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
Status BackgroundFlush(bool* madeProgress, DeletionState& deletion_state,
|
||||
Status BackgroundFlush(bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
void CleanupCompaction(CompactionState* compact, Status status);
|
||||
Status DoCompactionWork(CompactionState* compact,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
DeletionState& deletion_state,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// This function is called as part of compaction. It enables Flush process to
|
||||
// preempt compaction, since it's higher prioirty
|
||||
// Returns: micros spent executing
|
||||
uint64_t CallFlushDuringCompaction(ColumnFamilyData* cfd,
|
||||
const MutableCFOptions& mutable_cf_options, DeletionState& deletion_state,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Call compaction filter if is_compaction_v2 is not true. Then iterate
|
||||
// through input and compact the kv-pairs
|
||||
Status ProcessKeyValueCompaction(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
bool is_snapshot_supported,
|
||||
SequenceNumber visible_at_tip,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber latest_snapshot,
|
||||
DeletionState& deletion_state,
|
||||
bool bottommost_level,
|
||||
int64_t& imm_micros,
|
||||
Iterator* input,
|
||||
CompactionState* compact,
|
||||
bool is_compaction_v2,
|
||||
int* num_output_records,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Call compaction_filter_v2->Filter() on kv-pairs in compact
|
||||
void CallCompactionFilterV2(CompactionState* compact,
|
||||
CompactionFilterV2* compaction_filter_v2);
|
||||
|
||||
Status OpenCompactionOutputFile(CompactionState* compact,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input);
|
||||
Status InstallCompactionResults(CompactionState* compact,
|
||||
const MutableCFOptions& mutable_cf_options, LogBuffer* log_buffer);
|
||||
void AllocateCompactionOutputFileNumbers(CompactionState* compact);
|
||||
void ReleaseCompactionUnusedFileNumbers(CompactionState* compact);
|
||||
|
||||
#ifdef ROCKSDB_LITE
|
||||
void PurgeObsoleteWALFiles() {
|
||||
// this function is used for archiving WAL files. we don't need this in
|
||||
// ROCKSDB_LITE
|
||||
}
|
||||
#else
|
||||
void PurgeObsoleteWALFiles();
|
||||
|
||||
Status GetSortedWalsOfType(const std::string& path,
|
||||
VectorLogPtr& log_files,
|
||||
WalFileType type);
|
||||
|
||||
// Requires: all_logs should be sorted with earliest log file first
|
||||
// Retains all log files in all_logs which contain updates with seq no.
|
||||
// Greater Than or Equal to the requested SequenceNumber.
|
||||
Status RetainProbableWalFiles(VectorLogPtr& all_logs,
|
||||
const SequenceNumber target);
|
||||
|
||||
Status ReadFirstRecord(const WalFileType type, const uint64_t number,
|
||||
SequenceNumber* sequence);
|
||||
|
||||
Status ReadFirstLine(const std::string& fname, SequenceNumber* sequence);
|
||||
#endif // ROCKSDB_LITE
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
@@ -488,7 +346,7 @@ class DBImpl : public DB {
|
||||
|
||||
// State below is protected by mutex_
|
||||
port::Mutex mutex_;
|
||||
port::AtomicPointer shutting_down_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
// This condition variable is signaled on these conditions:
|
||||
// * whenever bg_compaction_scheduled_ goes down to 0
|
||||
// * if bg_manual_only_ > 0, whenever a compaction finishes, even if it hasn't
|
||||
@@ -532,10 +390,6 @@ class DBImpl : public DB {
|
||||
|
||||
SnapshotList snapshots_;
|
||||
|
||||
// cache for ReadFirstRecord() calls
|
||||
std::unordered_map<uint64_t, SequenceNumber> read_first_record_cache_;
|
||||
port::Mutex read_first_record_cache_mutex_;
|
||||
|
||||
// Set of table files to protect from deletion because they are
|
||||
// part of ongoing compactions.
|
||||
// map from pending file number ID to their path IDs.
|
||||
@@ -585,16 +439,9 @@ class DBImpl : public DB {
|
||||
// last time when DeleteObsoleteFiles was invoked
|
||||
uint64_t delete_obsolete_files_last_run_;
|
||||
|
||||
// last time when PurgeObsoleteWALFiles ran.
|
||||
uint64_t purge_wal_files_last_run_;
|
||||
|
||||
// last time stats were dumped to LOG
|
||||
std::atomic<uint64_t> last_stats_dump_time_microsec_;
|
||||
|
||||
// obsolete files will be deleted every this seconds if ttl deletion is
|
||||
// enabled and archive size_limit is disabled.
|
||||
uint64_t default_interval_to_delete_obsolete_WAL_;
|
||||
|
||||
bool flush_on_destroy_; // Used when disableWAL is true.
|
||||
|
||||
static const int KEEP_LOG_FILE_NUM = 1000;
|
||||
@@ -603,6 +450,10 @@ class DBImpl : public DB {
|
||||
// The options to access storage files
|
||||
const EnvOptions env_options_;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
WalManager wal_manager_;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// A value of true temporarily disables scheduling of background work
|
||||
bool bg_work_gate_closed_;
|
||||
|
||||
@@ -624,11 +475,16 @@ class DBImpl : public DB {
|
||||
SequenceNumber* prev_snapshot);
|
||||
|
||||
// Background threads call this function, which is just a wrapper around
|
||||
// the cfd->InstallSuperVersion() function. Background threads carry
|
||||
// deletion_state which can have new_superversion already allocated.
|
||||
void InstallSuperVersion(ColumnFamilyData* cfd,
|
||||
DeletionState& deletion_state,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
// the InstallSuperVersion() function. Background threads carry
|
||||
// job_context which can have new_superversion already
|
||||
// allocated.
|
||||
void InstallSuperVersionBackground(
|
||||
ColumnFamilyData* cfd, JobContext* job_context,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
SuperVersion* InstallSuperVersion(
|
||||
ColumnFamilyData* cfd, SuperVersion* new_sv,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Find Super version and reference it. Based on options, it might return
|
||||
// the thread local cached one.
|
||||
|
||||
+5
-17
@@ -13,11 +13,9 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
void DBImpl::TEST_PurgeObsoleteteWAL() { PurgeObsoleteWALFiles(); }
|
||||
|
||||
uint64_t DBImpl::TEST_GetLevel0TotalSize() {
|
||||
MutexLock l(&mutex_);
|
||||
return default_cf_handle_->cfd()->current()->NumLevelBytes(0);
|
||||
return default_cf_handle_->cfd()->current()->storage_info()->NumLevelBytes(0);
|
||||
}
|
||||
|
||||
Iterator* DBImpl::TEST_NewInternalIterator(Arena* arena,
|
||||
@@ -47,7 +45,7 @@ int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
cfd = cfh->cfd();
|
||||
}
|
||||
MutexLock l(&mutex_);
|
||||
return cfd->current()->MaxNextLevelOverlappingBytes();
|
||||
return cfd->current()->storage_info()->MaxNextLevelOverlappingBytes();
|
||||
}
|
||||
|
||||
void DBImpl::TEST_GetFilesMetaData(
|
||||
@@ -58,7 +56,8 @@ void DBImpl::TEST_GetFilesMetaData(
|
||||
MutexLock l(&mutex_);
|
||||
metadata->resize(NumberLevels());
|
||||
for (int level = 0; level < NumberLevels(); level++) {
|
||||
const std::vector<FileMetaData*>& files = cfd->current()->files_[level];
|
||||
const std::vector<FileMetaData*>& files =
|
||||
cfd->current()->storage_info()->LevelFiles(level);
|
||||
|
||||
(*metadata)[level].clear();
|
||||
for (const auto& f : files) {
|
||||
@@ -68,7 +67,7 @@ void DBImpl::TEST_GetFilesMetaData(
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_Current_Manifest_FileNo() {
|
||||
return versions_->ManifestFileNumber();
|
||||
return versions_->manifest_file_number();
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
@@ -120,17 +119,6 @@ Status DBImpl::TEST_WaitForCompact() {
|
||||
return bg_error_;
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_ReadFirstRecord(const WalFileType type,
|
||||
const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
return ReadFirstRecord(type, number, sequence);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_ReadFirstLine(const std::string& fname,
|
||||
SequenceNumber* sequence) {
|
||||
return ReadFirstLine(fname, sequence);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_LockMutex() {
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
? reinterpret_cast<const SnapshotImpl*>(
|
||||
read_options.snapshot)->number_
|
||||
: latest_snapshot),
|
||||
cfd->options()->max_sequential_skip_in_iterations);
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
read_options, cfd, super_version, db_iter->GetArena());
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
@@ -72,16 +72,17 @@ Status DBImplReadOnly::NewIterators(
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
|
||||
for (auto cfh : column_families) {
|
||||
auto cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
auto* sv = cfd->GetSuperVersion()->Ref();
|
||||
auto* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, *cfd->ioptions(), cfd->user_comparator(),
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(
|
||||
read_options.snapshot)->number_
|
||||
: latest_snapshot),
|
||||
cfd->options()->max_sequential_skip_in_iterations);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
read_options, cfd, cfd->GetSuperVersion()->Ref(), db_iter->GetArena());
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations);
|
||||
auto* internal_iter = NewInternalIterator(
|
||||
read_options, cfd, sv, db_iter->GetArena());
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
iterators->push_back(db_iter);
|
||||
}
|
||||
|
||||
+10
-8
@@ -162,7 +162,8 @@ class DBIter: public Iterator {
|
||||
inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
if (!ParseInternalKey(iter_->key(), ikey)) {
|
||||
status_ = Status::Corruption("corrupted internal key in DBIter");
|
||||
Log(logger_, "corrupted internal key in DBIter: %s",
|
||||
Log(InfoLogLevel::ERROR_LEVEL,
|
||||
logger_, "corrupted internal key in DBIter: %s",
|
||||
iter_->key().ToString(true).c_str());
|
||||
return false;
|
||||
} else {
|
||||
@@ -278,7 +279,8 @@ void DBIter::FindNextUserEntryInternal(bool skipping) {
|
||||
// iter_ points to the next entry (or invalid)
|
||||
void DBIter::MergeValuesNewToOld() {
|
||||
if (!user_merge_operator_) {
|
||||
Log(logger_, "Options::merge_operator is null.");
|
||||
Log(InfoLogLevel::ERROR_LEVEL,
|
||||
logger_, "Options::merge_operator is null.");
|
||||
throw std::logic_error("DBIter::MergeValuesNewToOld() with"
|
||||
" Options::merge_operator null");
|
||||
}
|
||||
@@ -310,8 +312,8 @@ void DBIter::MergeValuesNewToOld() {
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
// ignore corruption if there is any.
|
||||
const Slice value = iter_->value();
|
||||
user_merge_operator_->FullMerge(ikey.user_key, &value, operands,
|
||||
const Slice val = iter_->value();
|
||||
user_merge_operator_->FullMerge(ikey.user_key, &val, operands,
|
||||
&saved_value_, logger_);
|
||||
// iter_ is positioned after put
|
||||
iter_->Next();
|
||||
@@ -321,8 +323,8 @@ void DBIter::MergeValuesNewToOld() {
|
||||
if (kTypeMerge == ikey.type) {
|
||||
// hit a merge, add the value as an operand and run associative merge.
|
||||
// when complete, add result to operands and continue.
|
||||
const Slice& value = iter_->value();
|
||||
operands.push_front(value.ToString());
|
||||
const Slice& val = iter_->value();
|
||||
operands.push_front(val.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,8 +505,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Slice& value = iter_->value();
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), &value, operands,
|
||||
const Slice& val = iter_->value();
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), &val, operands,
|
||||
&saved_value_, logger_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
|
||||
+22
-22
@@ -33,20 +33,23 @@ class TestIterator : public Iterator {
|
||||
iter_(0),
|
||||
cmp(comparator) {}
|
||||
|
||||
void AddMerge(std::string key, std::string value) {
|
||||
Add(key, kTypeMerge, value);
|
||||
void AddMerge(std::string argkey, std::string argvalue) {
|
||||
Add(argkey, kTypeMerge, argvalue);
|
||||
}
|
||||
|
||||
void AddDeletion(std::string key) { Add(key, kTypeDeletion, std::string()); }
|
||||
|
||||
void AddPut(std::string key, std::string value) {
|
||||
Add(key, kTypeValue, value);
|
||||
void AddDeletion(std::string argkey) {
|
||||
Add(argkey, kTypeDeletion, std::string());
|
||||
}
|
||||
|
||||
void Add(std::string key, ValueType type, std::string value) {
|
||||
void AddPut(std::string argkey, std::string argvalue) {
|
||||
Add(argkey, kTypeValue, argvalue);
|
||||
}
|
||||
|
||||
void Add(std::string argkey, ValueType type, std::string argvalue) {
|
||||
valid_ = true;
|
||||
ParsedInternalKey internal_key(key, sequence_number_++, type);
|
||||
data_.push_back(std::pair<std::string, std::string>(std::string(), value));
|
||||
ParsedInternalKey internal_key(argkey, sequence_number_++, type);
|
||||
data_.push_back(
|
||||
std::pair<std::string, std::string>(std::string(), argvalue));
|
||||
AppendInternalKey(&data_.back().first, internal_key);
|
||||
}
|
||||
|
||||
@@ -217,7 +220,6 @@ TEST(DBIteratorTest, DBIteratorPrevNext) {
|
||||
}
|
||||
|
||||
{
|
||||
Options options;
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddPut("a", "val_a");
|
||||
internal_iter->AddPut("b", "val_b");
|
||||
@@ -254,7 +256,6 @@ TEST(DBIteratorTest, DBIteratorPrevNext) {
|
||||
}
|
||||
|
||||
{
|
||||
Options options;
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddPut("a", "val_a");
|
||||
internal_iter->AddPut("a", "val_a");
|
||||
@@ -364,8 +365,8 @@ TEST(DBIteratorTest, DBIteratorUseSkip) {
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddMerge("b", "merge_1");
|
||||
internal_iter->AddMerge("a", "merge_2");
|
||||
for (size_t i = 0; i < 200; ++i) {
|
||||
internal_iter->AddPut("c", std::to_string(i));
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddPut("c", std::to_string(k));
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
@@ -400,7 +401,7 @@ TEST(DBIteratorTest, DBIteratorUseSkip) {
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddMerge("b", "merge_1");
|
||||
internal_iter->AddMerge("a", "merge_2");
|
||||
for (size_t i = 0; i < 200; ++i) {
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddDeletion("c");
|
||||
}
|
||||
internal_iter->AddPut("c", "200");
|
||||
@@ -463,7 +464,7 @@ TEST(DBIteratorTest, DBIteratorUseSkip) {
|
||||
{
|
||||
for (size_t i = 0; i < 200; ++i) {
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
for (size_t i = 0; i < 200; ++i) {
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddDeletion("c");
|
||||
}
|
||||
internal_iter->AddPut("c", "200");
|
||||
@@ -511,12 +512,12 @@ TEST(DBIteratorTest, DBIteratorUseSkip) {
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddMerge("b", "merge_1");
|
||||
internal_iter->AddMerge("a", "merge_2");
|
||||
for (size_t i = 0; i < 200; ++i) {
|
||||
internal_iter->AddPut("d", std::to_string(i));
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddPut("d", std::to_string(k));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 200; ++i) {
|
||||
internal_iter->AddPut("c", std::to_string(i));
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddPut("c", std::to_string(k));
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
@@ -550,8 +551,8 @@ TEST(DBIteratorTest, DBIteratorUseSkip) {
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddMerge("b", "b");
|
||||
internal_iter->AddMerge("a", "a");
|
||||
for (size_t i = 0; i < 200; ++i) {
|
||||
internal_iter->AddMerge("c", std::to_string(i));
|
||||
for (size_t k = 0; k < 200; ++k) {
|
||||
internal_iter->AddMerge("c", std::to_string(k));
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
@@ -1390,7 +1391,6 @@ TEST(DBIteratorTest, DBIterator) {
|
||||
}
|
||||
|
||||
{
|
||||
Options options;
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddDeletion("a");
|
||||
internal_iter->AddPut("a", "0");
|
||||
|
||||
+992
-871
File diff suppressed because it is too large.
Load diff
+3
-3
@@ -127,8 +127,8 @@ void InternalKeyComparator::FindShortSuccessor(std::string* key) const {
|
||||
}
|
||||
}
|
||||
|
||||
LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
|
||||
size_t usize = user_key.size();
|
||||
LookupKey::LookupKey(const Slice& _user_key, SequenceNumber s) {
|
||||
size_t usize = _user_key.size();
|
||||
size_t needed = usize + 13; // A conservative estimate
|
||||
char* dst;
|
||||
if (needed <= sizeof(space_)) {
|
||||
@@ -139,7 +139,7 @@ LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
|
||||
start_ = dst;
|
||||
dst = EncodeVarint32(dst, usize + 8);
|
||||
kstart_ = dst;
|
||||
memcpy(dst, user_key.data(), usize);
|
||||
memcpy(dst, _user_key.data(), usize);
|
||||
dst += usize;
|
||||
EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));
|
||||
dst += 8;
|
||||
|
||||
+3
-3
@@ -132,8 +132,8 @@ class InternalKey {
|
||||
std::string rep_;
|
||||
public:
|
||||
InternalKey() { } // Leave rep_ as empty to indicate it is invalid
|
||||
InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) {
|
||||
AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t));
|
||||
InternalKey(const Slice& _user_key, SequenceNumber s, ValueType t) {
|
||||
AppendInternalKey(&rep_, ParsedInternalKey(_user_key, s, t));
|
||||
}
|
||||
|
||||
bool Valid() const {
|
||||
@@ -201,7 +201,7 @@ class LookupKey {
|
||||
public:
|
||||
// Initialize *this for looking up user_key at a snapshot with
|
||||
// the specified sequence number.
|
||||
LookupKey(const Slice& user_key, SequenceNumber sequence);
|
||||
LookupKey(const Slice& _user_key, SequenceNumber sequence);
|
||||
|
||||
~LookupKey();
|
||||
|
||||
|
||||
@@ -287,6 +287,75 @@ TEST(DeleteFileTest, DeleteLogFiles) {
|
||||
CloseDB();
|
||||
}
|
||||
|
||||
TEST(DeleteFileTest, DeleteNonDefaultColumnFamily) {
|
||||
CloseDB();
|
||||
DBOptions db_options;
|
||||
db_options.create_if_missing = true;
|
||||
db_options.create_missing_column_families = true;
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.emplace_back();
|
||||
column_families.emplace_back("new_cf", ColumnFamilyOptions());
|
||||
|
||||
std::vector<rocksdb::ColumnFamilyHandle*> handles;
|
||||
rocksdb::DB* db;
|
||||
ASSERT_OK(DB::Open(db_options, dbname_, column_families, &handles, &db));
|
||||
|
||||
Random rnd(5);
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), handles[1], test::RandomKey(&rnd, 10),
|
||||
test::RandomKey(&rnd, 10)));
|
||||
}
|
||||
ASSERT_OK(db->Flush(FlushOptions(), handles[1]));
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
ASSERT_OK(db->Put(WriteOptions(), handles[1], test::RandomKey(&rnd, 10),
|
||||
test::RandomKey(&rnd, 10)));
|
||||
}
|
||||
ASSERT_OK(db->Flush(FlushOptions(), handles[1]));
|
||||
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db->GetLiveFilesMetaData(&metadata);
|
||||
ASSERT_EQ(2U, metadata.size());
|
||||
ASSERT_EQ("new_cf", metadata[0].column_family_name);
|
||||
ASSERT_EQ("new_cf", metadata[1].column_family_name);
|
||||
auto old_file = metadata[0].smallest_seqno < metadata[1].smallest_seqno
|
||||
? metadata[0].name
|
||||
: metadata[1].name;
|
||||
auto new_file = metadata[0].smallest_seqno > metadata[1].smallest_seqno
|
||||
? metadata[0].name
|
||||
: metadata[1].name;
|
||||
ASSERT_TRUE(db->DeleteFile(new_file).IsInvalidArgument());
|
||||
ASSERT_OK(db->DeleteFile(old_file));
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> itr(db->NewIterator(ReadOptions(), handles[1]));
|
||||
int count = 0;
|
||||
for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
|
||||
ASSERT_OK(itr->status());
|
||||
++count;
|
||||
}
|
||||
ASSERT_EQ(count, 1000);
|
||||
}
|
||||
|
||||
delete handles[0];
|
||||
delete handles[1];
|
||||
delete db;
|
||||
|
||||
ASSERT_OK(DB::Open(db_options, dbname_, column_families, &handles, &db));
|
||||
{
|
||||
std::unique_ptr<Iterator> itr(db->NewIterator(ReadOptions(), handles[1]));
|
||||
int count = 0;
|
||||
for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
|
||||
ASSERT_OK(itr->status());
|
||||
++count;
|
||||
}
|
||||
ASSERT_EQ(count, 1000);
|
||||
}
|
||||
|
||||
delete handles[0];
|
||||
delete handles[1];
|
||||
delete db;
|
||||
}
|
||||
|
||||
} //namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+4
-4
@@ -17,17 +17,17 @@ namespace rocksdb {
|
||||
FileIndexer::FileIndexer(const Comparator* ucmp)
|
||||
: num_levels_(0), ucmp_(ucmp), level_rb_(nullptr) {}
|
||||
|
||||
uint32_t FileIndexer::NumLevelIndex() {
|
||||
uint32_t FileIndexer::NumLevelIndex() const {
|
||||
return next_level_index_.size();
|
||||
}
|
||||
|
||||
uint32_t FileIndexer::LevelIndexSize(uint32_t level) {
|
||||
uint32_t FileIndexer::LevelIndexSize(uint32_t level) const {
|
||||
return next_level_index_[level].num_index;
|
||||
}
|
||||
|
||||
void FileIndexer::GetNextLevelIndex(
|
||||
const uint32_t level, const uint32_t file_index, const int cmp_smallest,
|
||||
const int cmp_largest, int32_t* left_bound, int32_t* right_bound) {
|
||||
const int cmp_largest, int32_t* left_bound, int32_t* right_bound) const {
|
||||
assert(level > 0);
|
||||
|
||||
// Last level, no hint
|
||||
@@ -100,7 +100,7 @@ void FileIndexer::UpdateIndex(Arena* arena, const uint32_t num_levels,
|
||||
}
|
||||
IndexLevel& index_level = next_level_index_[level];
|
||||
index_level.num_index = upper_size;
|
||||
char* mem = arena->AllocateAligned(upper_size * sizeof(IndexUnit));
|
||||
mem = arena->AllocateAligned(upper_size * sizeof(IndexUnit));
|
||||
index_level.index_units = new (mem) IndexUnit[upper_size];
|
||||
|
||||
CalculateLB(
|
||||
|
||||
+3
-3
@@ -42,9 +42,9 @@ class FileIndexer {
|
||||
public:
|
||||
explicit FileIndexer(const Comparator* ucmp);
|
||||
|
||||
uint32_t NumLevelIndex();
|
||||
uint32_t NumLevelIndex() const;
|
||||
|
||||
uint32_t LevelIndexSize(uint32_t level);
|
||||
uint32_t LevelIndexSize(uint32_t level) const;
|
||||
|
||||
// Return a file index range in the next level to search for a key based on
|
||||
// smallest and largest key comparision for the current file specified by
|
||||
@@ -52,7 +52,7 @@ class FileIndexer {
|
||||
// be valid and fit in the vector size.
|
||||
void GetNextLevelIndex(
|
||||
const uint32_t level, const uint32_t file_index, const int cmp_smallest,
|
||||
const int cmp_largest, int32_t* left_bound, int32_t* right_bound);
|
||||
const int cmp_largest, int32_t* left_bound, int32_t* right_bound) const;
|
||||
|
||||
void UpdateIndex(Arena* arena, const uint32_t num_levels,
|
||||
std::vector<FileMetaData*>* const files);
|
||||
|
||||
@@ -94,7 +94,6 @@ TEST(FileIndexerTest, Empty) {
|
||||
// Case 1: no overlap, files are on the left of next level files
|
||||
TEST(FileIndexerTest, no_overlap_left) {
|
||||
Arena arena;
|
||||
uint32_t kNumLevels = 4;
|
||||
indexer = new FileIndexer(&ucmp);
|
||||
// level 1
|
||||
AddFile(1, 100, 200);
|
||||
@@ -135,7 +134,6 @@ TEST(FileIndexerTest, no_overlap_left) {
|
||||
// Case 2: no overlap, files are on the right of next level files
|
||||
TEST(FileIndexerTest, no_overlap_right) {
|
||||
Arena arena;
|
||||
uint32_t kNumLevels = 4;
|
||||
indexer = new FileIndexer(&ucmp);
|
||||
// level 1
|
||||
AddFile(1, 2100, 2200);
|
||||
@@ -178,7 +176,6 @@ TEST(FileIndexerTest, no_overlap_right) {
|
||||
// Case 3: empty L2
|
||||
TEST(FileIndexerTest, empty_L2) {
|
||||
Arena arena;
|
||||
uint32_t kNumLevels = 4;
|
||||
indexer = new FileIndexer(&ucmp);
|
||||
for (uint32_t i = 1; i < kNumLevels; ++i) {
|
||||
ASSERT_EQ(0U, indexer->LevelIndexSize(i));
|
||||
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
// 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/flush_job.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "port/port.h"
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/iostats_context_imp.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
port::Mutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
FileNumToPathIdMap* pending_outputs,
|
||||
SequenceNumber newest_snapshot, JobContext* job_context,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
CompressionType output_compression, Statistics* stats)
|
||||
: dbname_(dbname),
|
||||
cfd_(cfd),
|
||||
db_options_(db_options),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
env_options_(env_options),
|
||||
versions_(versions),
|
||||
db_mutex_(db_mutex),
|
||||
shutting_down_(shutting_down),
|
||||
pending_outputs_(pending_outputs),
|
||||
newest_snapshot_(newest_snapshot),
|
||||
job_context_(job_context),
|
||||
log_buffer_(log_buffer),
|
||||
db_directory_(db_directory),
|
||||
output_compression_(output_compression),
|
||||
stats_(stats) {}
|
||||
|
||||
Status FlushJob::Run() {
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
uint64_t file_number;
|
||||
autovector<MemTable*> mems;
|
||||
cfd_->imm()->PickMemtablesToFlush(&mems);
|
||||
if (mems.empty()) {
|
||||
LogToBuffer(log_buffer_, "[%s] Nothing in memtable to flush",
|
||||
cfd_->GetName().c_str());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// entries mems are (implicitly) sorted in ascending order by their created
|
||||
// time. We will use the first memtable's `edit` to keep the meta info for
|
||||
// this flush.
|
||||
MemTable* m = mems[0];
|
||||
VersionEdit* edit = m->GetEdits();
|
||||
edit->SetPrevLogNumber(0);
|
||||
// SetLogNumber(log_num) indicates logs with number smaller than log_num
|
||||
// will no longer be picked up for recovery.
|
||||
edit->SetLogNumber(mems.back()->GetNextLogNumber());
|
||||
edit->SetColumnFamily(cfd_->GetID());
|
||||
|
||||
// This will release and re-acquire the mutex.
|
||||
Status s = WriteLevel0Table(mems, edit, &file_number);
|
||||
|
||||
if (s.ok() &&
|
||||
(shutting_down_->load(std::memory_order_acquire) || cfd_->IsDropped())) {
|
||||
s = Status::ShutdownInProgress(
|
||||
"Database shutdown or Column family drop during flush");
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
cfd_->imm()->RollbackMemtableFlush(mems, file_number, pending_outputs_);
|
||||
} else {
|
||||
// Replace immutable memtable with the generated Table
|
||||
s = cfd_->imm()->InstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems, versions_, db_mutex_, file_number,
|
||||
pending_outputs_, &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
VersionEdit* edit, uint64_t* filenumber) {
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = db_options_.env->NowMicros();
|
||||
FileMetaData meta;
|
||||
|
||||
meta.fd = FileDescriptor(versions_->NewFileNumber(), 0, 0);
|
||||
*filenumber = meta.fd.GetNumber();
|
||||
// path 0 for level 0 file.
|
||||
pending_outputs_->insert({meta.fd.GetNumber(), 0});
|
||||
|
||||
const SequenceNumber earliest_seqno_in_memtable =
|
||||
mems[0]->GetFirstSequenceNumber();
|
||||
Version* base = cfd_->current();
|
||||
base->Ref(); // it is likely that we do not need this reference
|
||||
Status s;
|
||||
{
|
||||
db_mutex_->Unlock();
|
||||
if (log_buffer_) {
|
||||
log_buffer_->FlushBufferToLog();
|
||||
}
|
||||
std::vector<Iterator*> memtables;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
Arena arena;
|
||||
for (MemTable* m : mems) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Flushing memtable with next log file: %" PRIu64 "\n",
|
||||
cfd_->GetName().c_str(), m->GetNextLogNumber());
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
}
|
||||
{
|
||||
ScopedArenaIterator iter(NewMergingIterator(&cfd_->internal_comparator(),
|
||||
&memtables[0],
|
||||
memtables.size(), &arena));
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), meta.fd.GetNumber());
|
||||
|
||||
s = BuildTable(dbname_, db_options_.env, *cfd_->ioptions(), env_options_,
|
||||
cfd_->table_cache(), iter.get(), &meta,
|
||||
cfd_->internal_comparator(), newest_snapshot_,
|
||||
earliest_seqno_in_memtable, output_compression_,
|
||||
cfd_->ioptions()->compression_opts, Env::IO_HIGH);
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Level-0 flush table #%" PRIu64 ": %" PRIu64 " bytes %s",
|
||||
cfd_->GetName().c_str(), meta.fd.GetNumber(), meta.fd.GetFileSize(),
|
||||
s.ToString().c_str());
|
||||
|
||||
if (!db_options_.disableDataSync && db_directory_ != nullptr) {
|
||||
db_directory_->Fsync();
|
||||
}
|
||||
db_mutex_->Lock();
|
||||
}
|
||||
base->Unref();
|
||||
|
||||
// re-acquire the most current version
|
||||
base = cfd_->current();
|
||||
|
||||
// There could be multiple threads writing to its own level-0 file.
|
||||
// The pending_outputs cannot be cleared here, otherwise this newly
|
||||
// created file might not be considered as a live-file by another
|
||||
// compaction thread that is concurrently deleting obselete files.
|
||||
// The pending_outputs can be cleared only after the new version is
|
||||
// committed so that other threads can recognize this file as a
|
||||
// valid one.
|
||||
// pending_outputs_.erase(meta.number);
|
||||
|
||||
// Note that if file_size is zero, the file has been deleted and
|
||||
// should not be added to the manifest.
|
||||
int level = 0;
|
||||
if (s.ok() && meta.fd.GetFileSize() > 0) {
|
||||
const Slice min_user_key = meta.smallest.user_key();
|
||||
const Slice max_user_key = meta.largest.user_key();
|
||||
// if we have more than 1 background thread, then we cannot
|
||||
// insert files directly into higher levels because some other
|
||||
// threads could be concurrently producing compacted files for
|
||||
// that key range.
|
||||
if (base != nullptr && db_options_.max_background_compactions <= 1 &&
|
||||
db_options_.max_background_flushes == 0 &&
|
||||
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
level = base->storage_info()->PickLevelForMemTableOutput(
|
||||
mutable_cf_options_, min_user_key, max_user_key);
|
||||
}
|
||||
edit->AddFile(level, meta.fd.GetNumber(), meta.fd.GetPathId(),
|
||||
meta.fd.GetFileSize(), meta.smallest, meta.largest,
|
||||
meta.smallest_seqno, meta.largest_seqno);
|
||||
}
|
||||
|
||||
InternalStats::CompactionStats stats(1);
|
||||
stats.micros = db_options_.env->NowMicros() - start_micros;
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
cfd_->internal_stats()->AddCompactionStats(level, stats);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
meta.fd.GetFileSize());
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, meta.fd.GetFileSize());
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#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"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "util/scoped_arena_iterator.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "db/job_context.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class MemTable;
|
||||
class TableCache;
|
||||
class Version;
|
||||
class VersionEdit;
|
||||
class VersionSet;
|
||||
class Arena;
|
||||
|
||||
class FlushJob {
|
||||
public:
|
||||
// TODO(icanadi) make effort to reduce number of parameters here
|
||||
// IMPORTANT: mutable_cf_options needs to be alive while FlushJob is alive
|
||||
FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
port::Mutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
FileNumToPathIdMap* pending_outputs, SequenceNumber newest_snapshot,
|
||||
JobContext* job_context, LogBuffer* log_buffer,
|
||||
Directory* db_directory, CompressionType output_compression,
|
||||
Statistics* stats);
|
||||
~FlushJob() {}
|
||||
|
||||
Status Run();
|
||||
|
||||
private:
|
||||
Status WriteLevel0Table(const autovector<MemTable*>& mems, VersionEdit* edit,
|
||||
uint64_t* filenumber);
|
||||
const std::string& dbname_;
|
||||
ColumnFamilyData* cfd_;
|
||||
const DBOptions& db_options_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const EnvOptions& env_options_;
|
||||
VersionSet* versions_;
|
||||
port::Mutex* db_mutex_;
|
||||
std::atomic<bool>* shutting_down_;
|
||||
FileNumToPathIdMap* pending_outputs_;
|
||||
SequenceNumber newest_snapshot_;
|
||||
JobContext* job_context_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
CompressionType output_compression_;
|
||||
Statistics* stats_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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 <map>
|
||||
#include <string>
|
||||
|
||||
#include "db/flush_job.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "table/mock_table.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// TODO(icanadi) Mock out everything else:
|
||||
// 1. VersionSet
|
||||
// 2. Memtable
|
||||
class FlushJobTest {
|
||||
public:
|
||||
FlushJobTest()
|
||||
: env_(Env::Default()),
|
||||
dbname_(test::TmpDir() + "/flush_job_test"),
|
||||
table_cache_(NewLRUCache(50000, 16, 8)),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_controller_)),
|
||||
shutting_down_(false),
|
||||
mock_table_factory_(new MockTableFactory()) {
|
||||
ASSERT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
// TODO(icanadi) Remove this once we mock out VersionSet
|
||||
NewDB();
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options_);
|
||||
|
||||
ASSERT_OK(versions_->Recover(column_families, false));
|
||||
}
|
||||
|
||||
void NewDB() {
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
new_db.SetNextFile(2);
|
||||
new_db.SetLastSequence(0);
|
||||
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
unique_ptr<WritableFile> file;
|
||||
Status s = env_->NewWritableFile(
|
||||
manifest, &file, env_->OptimizeForManifestWrite(env_options_));
|
||||
ASSERT_OK(s);
|
||||
{
|
||||
log::Writer log(std::move(file));
|
||||
std::string record;
|
||||
new_db.EncodeTo(&record);
|
||||
s = log.AddRecord(record);
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
std::string dbname_;
|
||||
EnvOptions env_options_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
WriteController write_controller_;
|
||||
DBOptions db_options_;
|
||||
ColumnFamilyOptions cf_options_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
port::Mutex mutex_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
FileNumToPathIdMap pending_outputs_;
|
||||
std::shared_ptr<MockTableFactory> mock_table_factory_;
|
||||
};
|
||||
|
||||
TEST(FlushJobTest, Empty) {
|
||||
JobContext job_context;
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
&pending_outputs_, SequenceNumber(), &job_context, nullptr,
|
||||
nullptr, kNoCompression, nullptr);
|
||||
ASSERT_OK(flush_job.Run());
|
||||
}
|
||||
|
||||
TEST(FlushJobTest, NonEmpty) {
|
||||
JobContext job_context;
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
|
||||
auto new_mem = new MemTable(cfd->internal_comparator(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
new_mem->Ref();
|
||||
std::map<std::string, std::string> inserted_keys;
|
||||
for (int i = 1; i < 10000; ++i) {
|
||||
std::string key(std::to_string(i));
|
||||
std::string value("value" + std::to_string(i));
|
||||
new_mem->Add(SequenceNumber(i), kTypeValue, key, value);
|
||||
InternalKey internal_key(key, SequenceNumber(i), kTypeValue);
|
||||
inserted_keys.insert({internal_key.Encode().ToString(), value});
|
||||
}
|
||||
cfd->imm()->Add(new_mem);
|
||||
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
&pending_outputs_, SequenceNumber(), &job_context, nullptr,
|
||||
nullptr, kNoCompression, nullptr);
|
||||
mutex_.Lock();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
mutex_.Unlock();
|
||||
mock_table_factory_->AssertSingleFile(inserted_keys);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
+65
-51
@@ -10,6 +10,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "db/job_context.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/column_family.h"
|
||||
@@ -114,25 +115,29 @@ class LevelIterator : public Iterator {
|
||||
};
|
||||
|
||||
ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd)
|
||||
ColumnFamilyData* cfd, SuperVersion* current_sv)
|
||||
: db_(db),
|
||||
read_options_(read_options),
|
||||
cfd_(cfd),
|
||||
prefix_extractor_(cfd->options()->prefix_extractor.get()),
|
||||
user_comparator_(cfd->user_comparator()),
|
||||
immutable_min_heap_(MinIterComparator(&cfd_->internal_comparator())),
|
||||
sv_(nullptr),
|
||||
sv_(current_sv),
|
||||
mutable_iter_(nullptr),
|
||||
current_(nullptr),
|
||||
valid_(false),
|
||||
is_prev_set_(false),
|
||||
is_prev_inclusive_(false) {}
|
||||
|
||||
ForwardIterator::~ForwardIterator() {
|
||||
Cleanup();
|
||||
is_prev_inclusive_(false) {
|
||||
if (sv_) {
|
||||
RebuildIterators(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ForwardIterator::Cleanup() {
|
||||
ForwardIterator::~ForwardIterator() {
|
||||
Cleanup(true);
|
||||
}
|
||||
|
||||
void ForwardIterator::Cleanup(bool release_sv) {
|
||||
if (mutable_iter_ != nullptr) {
|
||||
mutable_iter_->~Iterator();
|
||||
}
|
||||
@@ -149,15 +154,17 @@ void ForwardIterator::Cleanup() {
|
||||
}
|
||||
level_iters_.clear();
|
||||
|
||||
if (sv_ != nullptr && sv_->Unref()) {
|
||||
DBImpl::DeletionState deletion_state;
|
||||
db_->mutex_.Lock();
|
||||
sv_->Cleanup();
|
||||
db_->FindObsoleteFiles(deletion_state, false, true);
|
||||
db_->mutex_.Unlock();
|
||||
delete sv_;
|
||||
if (deletion_state.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(deletion_state);
|
||||
if (release_sv) {
|
||||
if (sv_ != nullptr && sv_->Unref()) {
|
||||
JobContext job_context;
|
||||
db_->mutex_.Lock();
|
||||
sv_->Cleanup();
|
||||
db_->FindObsoleteFiles(&job_context, false, true);
|
||||
db_->mutex_.Unlock();
|
||||
delete sv_;
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +176,7 @@ bool ForwardIterator::Valid() const {
|
||||
void ForwardIterator::SeekToFirst() {
|
||||
if (sv_ == nullptr ||
|
||||
sv_ ->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
RebuildIterators();
|
||||
RebuildIterators(true);
|
||||
} else if (status_.IsIncomplete()) {
|
||||
ResetIncompleteIterators();
|
||||
}
|
||||
@@ -179,7 +186,7 @@ void ForwardIterator::SeekToFirst() {
|
||||
void ForwardIterator::Seek(const Slice& internal_key) {
|
||||
if (sv_ == nullptr ||
|
||||
sv_ ->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
RebuildIterators();
|
||||
RebuildIterators(true);
|
||||
} else if (status_.IsIncomplete()) {
|
||||
ResetIncompleteIterators();
|
||||
}
|
||||
@@ -188,6 +195,7 @@ void ForwardIterator::Seek(const Slice& internal_key) {
|
||||
|
||||
void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
bool seek_to_first) {
|
||||
assert(mutable_iter_);
|
||||
// mutable
|
||||
seek_to_first ? mutable_iter_->SeekToFirst() :
|
||||
mutable_iter_->Seek(internal_key);
|
||||
@@ -212,15 +220,16 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
if (!seek_to_first) {
|
||||
user_key = ExtractUserKey(internal_key);
|
||||
}
|
||||
auto* files = sv_->current->files_;
|
||||
for (uint32_t i = 0; i < files[0].size(); ++i) {
|
||||
const VersionStorageInfo* vstorage = sv_->current->storage_info();
|
||||
const std::vector<FileMetaData*>& l0 = vstorage->LevelFiles(0);
|
||||
for (uint32_t i = 0; i < l0.size(); ++i) {
|
||||
if (seek_to_first) {
|
||||
l0_iters_[i]->SeekToFirst();
|
||||
} else {
|
||||
// If the target key passes over the larget key, we are sure Next()
|
||||
// won't go over this file.
|
||||
if (user_comparator_->Compare(user_key,
|
||||
files[0][i]->largest.user_key()) > 0) {
|
||||
l0[i]->largest.user_key()) > 0) {
|
||||
continue;
|
||||
}
|
||||
l0_iters_[i]->Seek(internal_key);
|
||||
@@ -240,65 +249,64 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
|
||||
int32_t search_left_bound = 0;
|
||||
int32_t search_right_bound = FileIndexer::kLevelMaxIndex;
|
||||
for (int32_t level = 1; level < sv_->current->NumberLevels(); ++level) {
|
||||
if (files[level].empty()) {
|
||||
for (int32_t level = 1; level < vstorage->num_levels(); ++level) {
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage->LevelFiles(level);
|
||||
if (level_files.empty()) {
|
||||
search_left_bound = 0;
|
||||
search_right_bound = FileIndexer::kLevelMaxIndex;
|
||||
continue;
|
||||
}
|
||||
assert(level_iters_[level - 1] != nullptr);
|
||||
uint32_t f_idx = 0;
|
||||
const auto& indexer = vstorage->file_indexer();
|
||||
if (!seek_to_first) {
|
||||
// TODO(ljin): remove before committing
|
||||
// f_idx = FindFileInRange(
|
||||
// files[level], internal_key, 0, files[level].size());
|
||||
|
||||
if (search_left_bound == search_right_bound) {
|
||||
f_idx = search_left_bound;
|
||||
} else if (search_left_bound < search_right_bound) {
|
||||
f_idx = FindFileInRange(
|
||||
files[level], internal_key, search_left_bound,
|
||||
level_files, internal_key, search_left_bound,
|
||||
search_right_bound == FileIndexer::kLevelMaxIndex ?
|
||||
files[level].size() : search_right_bound);
|
||||
level_files.size() : search_right_bound);
|
||||
} else {
|
||||
// search_left_bound > search_right_bound
|
||||
// There are only 2 cases this can happen:
|
||||
// (1) target key is smaller than left most file
|
||||
// (2) target key is larger than right most file
|
||||
assert(search_left_bound == (int32_t)files[level].size() ||
|
||||
assert(search_left_bound == (int32_t)level_files.size() ||
|
||||
search_right_bound == -1);
|
||||
if (search_right_bound == -1) {
|
||||
assert(search_left_bound == 0);
|
||||
f_idx = 0;
|
||||
} else {
|
||||
sv_->current->file_indexer_.GetNextLevelIndex(
|
||||
level, files[level].size() - 1,
|
||||
indexer.GetNextLevelIndex(
|
||||
level, level_files.size() - 1,
|
||||
1, 1, &search_left_bound, &search_right_bound);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare hints for the next level
|
||||
if (f_idx < files[level].size()) {
|
||||
if (f_idx < level_files.size()) {
|
||||
int cmp_smallest = user_comparator_->Compare(
|
||||
user_key, files[level][f_idx]->smallest.user_key());
|
||||
user_key, level_files[f_idx]->smallest.user_key());
|
||||
int cmp_largest = -1;
|
||||
if (cmp_smallest >= 0) {
|
||||
cmp_smallest = user_comparator_->Compare(
|
||||
user_key, files[level][f_idx]->smallest.user_key());
|
||||
user_key, level_files[f_idx]->smallest.user_key());
|
||||
}
|
||||
sv_->current->file_indexer_.GetNextLevelIndex(level, f_idx,
|
||||
indexer.GetNextLevelIndex(level, f_idx,
|
||||
cmp_smallest, cmp_largest,
|
||||
&search_left_bound, &search_right_bound);
|
||||
} else {
|
||||
sv_->current->file_indexer_.GetNextLevelIndex(
|
||||
level, files[level].size() - 1,
|
||||
indexer.GetNextLevelIndex(
|
||||
level, level_files.size() - 1,
|
||||
1, 1, &search_left_bound, &search_right_bound);
|
||||
}
|
||||
}
|
||||
|
||||
// Seek
|
||||
if (f_idx < files[level].size()) {
|
||||
if (f_idx < level_files.size()) {
|
||||
level_iters_[level - 1]->SetFileIndex(f_idx);
|
||||
seek_to_first ? level_iters_[level - 1]->SeekToFirst() :
|
||||
level_iters_[level - 1]->Seek(internal_key);
|
||||
@@ -338,7 +346,7 @@ void ForwardIterator::Next() {
|
||||
std::string current_key = key().ToString();
|
||||
Slice old_key(current_key.data(), current_key.size());
|
||||
|
||||
RebuildIterators();
|
||||
RebuildIterators(true);
|
||||
SeekInternal(old_key, false);
|
||||
if (!valid_ || key().compare(old_key) != 0) {
|
||||
return;
|
||||
@@ -412,26 +420,32 @@ Status ForwardIterator::status() const {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void ForwardIterator::RebuildIterators() {
|
||||
void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
// Clean up
|
||||
Cleanup();
|
||||
// New
|
||||
sv_ = cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
Cleanup(refresh_sv);
|
||||
if (refresh_sv) {
|
||||
// New
|
||||
sv_ = cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
}
|
||||
mutable_iter_ = sv_->mem->NewIterator(read_options_, &arena_);
|
||||
sv_->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
const auto& l0_files = sv_->current->files_[0];
|
||||
|
||||
const auto* vstorage = sv_->current->storage_info();
|
||||
const auto& l0_files = vstorage->LevelFiles(0);
|
||||
l0_iters_.reserve(l0_files.size());
|
||||
for (const auto* l0 : l0_files) {
|
||||
l0_iters_.push_back(cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(), l0->fd));
|
||||
}
|
||||
level_iters_.reserve(sv_->current->NumberLevels() - 1);
|
||||
for (int32_t level = 1; level < sv_->current->NumberLevels(); ++level) {
|
||||
if (sv_->current->files_[level].empty()) {
|
||||
level_iters_.reserve(vstorage->num_levels() - 1);
|
||||
for (int32_t level = 1; level < vstorage->num_levels(); ++level) {
|
||||
const auto& level_files = vstorage->LevelFiles(level);
|
||||
|
||||
if (level_files.empty()) {
|
||||
level_iters_.push_back(nullptr);
|
||||
} else {
|
||||
level_iters_.push_back(new LevelIterator(cfd_, read_options_,
|
||||
sv_->current->files_[level]));
|
||||
level_iters_.push_back(
|
||||
new LevelIterator(cfd_, read_options_, level_files));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +454,7 @@ void ForwardIterator::RebuildIterators() {
|
||||
}
|
||||
|
||||
void ForwardIterator::ResetIncompleteIterators() {
|
||||
const auto& l0_files = sv_->current->files_[0];
|
||||
const auto& l0_files = sv_->current->storage_info()->LevelFiles(0);
|
||||
for (uint32_t i = 0; i < l0_iters_.size(); ++i) {
|
||||
assert(i < l0_files.size());
|
||||
if (!l0_iters_[i]->status().IsIncomplete()) {
|
||||
|
||||
@@ -51,7 +51,7 @@ typedef std::priority_queue<Iterator*,
|
||||
class ForwardIterator : public Iterator {
|
||||
public:
|
||||
ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd);
|
||||
ColumnFamilyData* cfd, SuperVersion* current_sv = nullptr);
|
||||
virtual ~ForwardIterator();
|
||||
|
||||
void SeekToLast() override {
|
||||
@@ -72,8 +72,8 @@ class ForwardIterator : public Iterator {
|
||||
virtual Status status() const override;
|
||||
|
||||
private:
|
||||
void Cleanup();
|
||||
void RebuildIterators();
|
||||
void Cleanup(bool release_sv);
|
||||
void RebuildIterators(bool refresh_sv);
|
||||
void ResetIncompleteIterators();
|
||||
void SeekInternal(const Slice& internal_key, bool seek_to_first);
|
||||
void UpdateCurrent();
|
||||
|
||||
+84
-71
@@ -30,7 +30,7 @@ void PrintLevelStatsHeader(char* buf, size_t len, const std::string& cf_name) {
|
||||
"Level Files Size(MB) Score Read(GB) Rn(GB) Rnp1(GB) "
|
||||
"Write(GB) Wnew(GB) RW-Amp W-Amp Rd(MB/s) Wr(MB/s) Rn(cnt) "
|
||||
"Rnp1(cnt) Wnp1(cnt) Wnew(cnt) Comp(sec) Comp(cnt) Avg(sec) "
|
||||
"Stall(sec) Stall(cnt) Avg(ms) RecordIn RecordDrop\n"
|
||||
"Stall(sec) Stall(cnt) Avg(ms) RecordIn RecordDrop\n"
|
||||
"--------------------------------------------------------------------"
|
||||
"--------------------------------------------------------------------"
|
||||
"--------------------------------------------------------------------\n",
|
||||
@@ -46,50 +46,42 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
double elapsed = (stats.micros + 1) / 1000000.0;
|
||||
|
||||
snprintf(buf, len,
|
||||
"%4s %5d/%-3d %8.0f %5.1f " /* Level, Files, Size(MB), Score */
|
||||
"%8.1f " /* Read(GB) */
|
||||
"%7.1f " /* Rn(GB) */
|
||||
"%8.1f " /* Rnp1(GB) */
|
||||
"%9.1f " /* Write(GB) */
|
||||
"%8.1f " /* Wnew(GB) */
|
||||
"%6.1f " /* RW-Amp */
|
||||
"%5.1f " /* W-Amp */
|
||||
"%8.1f " /* Rd(MB/s) */
|
||||
"%8.1f " /* Wr(MB/s) */
|
||||
"%8d " /* Rn(cnt) */
|
||||
"%9d " /* Rnp1(cnt) */
|
||||
"%9d " /* Wnp1(cnt) */
|
||||
"%9d " /* Wnew(cnt) */
|
||||
"%10.0f " /* Comp(sec) */
|
||||
"%9d " /* Comp(cnt) */
|
||||
"%8.3f " /* Avg(sec) */
|
||||
"%10.2f " /* Stall(sec) */
|
||||
"%10" PRIu64 " " /* Stall(cnt) */
|
||||
"%7.2f" /* Avg(ms) */
|
||||
"%8d " /* input entries */
|
||||
"%10d\n" /* number of records reduced */,
|
||||
name.c_str(), num_files, being_compacted, total_file_size / kMB, score,
|
||||
bytes_read / kGB,
|
||||
stats.bytes_readn / kGB,
|
||||
stats.bytes_readnp1 / kGB,
|
||||
stats.bytes_written / kGB,
|
||||
bytes_new / kGB,
|
||||
rw_amp,
|
||||
w_amp,
|
||||
bytes_read / kMB / elapsed,
|
||||
stats.bytes_written / kMB / elapsed,
|
||||
stats.files_in_leveln,
|
||||
stats.files_in_levelnp1,
|
||||
stats.files_out_levelnp1,
|
||||
stats.files_out_levelnp1 - stats.files_in_levelnp1,
|
||||
stats.micros / 1000000.0,
|
||||
stats.count,
|
||||
stats.count == 0 ? 0 : stats.micros / 1000000.0 / stats.count,
|
||||
stall_us / 1000000.0,
|
||||
stalls,
|
||||
stalls == 0 ? 0 : stall_us / 1000.0 / stalls,
|
||||
stats.num_input_records,
|
||||
stats.num_dropped_records);
|
||||
"%4s %5d/%-3d %8.0f %5.1f " /* Level, Files, Size(MB), Score */
|
||||
"%8.1f " /* Read(GB) */
|
||||
"%7.1f " /* Rn(GB) */
|
||||
"%8.1f " /* Rnp1(GB) */
|
||||
"%9.1f " /* Write(GB) */
|
||||
"%8.1f " /* Wnew(GB) */
|
||||
"%6.1f " /* RW-Amp */
|
||||
"%5.1f " /* W-Amp */
|
||||
"%8.1f " /* Rd(MB/s) */
|
||||
"%8.1f " /* Wr(MB/s) */
|
||||
"%8d " /* Rn(cnt) */
|
||||
"%9d " /* Rnp1(cnt) */
|
||||
"%9d " /* Wnp1(cnt) */
|
||||
"%9d " /* Wnew(cnt) */
|
||||
"%10.0f " /* Comp(sec) */
|
||||
"%9d " /* Comp(cnt) */
|
||||
"%8.3f " /* Avg(sec) */
|
||||
"%10.2f " /* Stall(sec) */
|
||||
"%10" PRIu64
|
||||
" " /* Stall(cnt) */
|
||||
"%7.2f " /* Avg(ms) */
|
||||
"%12" PRIu64
|
||||
" " /* input entries */
|
||||
"%12" PRIu64 "\n" /* number of records reduced */,
|
||||
name.c_str(), num_files, being_compacted, total_file_size / kMB,
|
||||
score, bytes_read / kGB, stats.bytes_readn / kGB,
|
||||
stats.bytes_readnp1 / kGB, stats.bytes_written / kGB,
|
||||
bytes_new / kGB, rw_amp, w_amp, bytes_read / kMB / elapsed,
|
||||
stats.bytes_written / kMB / elapsed, stats.files_in_leveln,
|
||||
stats.files_in_levelnp1, stats.files_out_levelnp1,
|
||||
stats.files_out_levelnp1 - stats.files_in_levelnp1,
|
||||
stats.micros / 1000000.0, stats.count,
|
||||
stats.count == 0 ? 0 : stats.micros / 1000000.0 / stats.count,
|
||||
stall_us / 1000000.0, stalls,
|
||||
stalls == 0 ? 0 : stall_us / 1000.0 / stalls,
|
||||
stats.num_input_records, stats.num_dropped_records);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +125,8 @@ DBPropertyType GetPropertyType(const Slice& property, bool* is_int_property,
|
||||
return kBackgroundErrors;
|
||||
} else if (in == "cur-size-active-mem-table") {
|
||||
return kCurSizeActiveMemTable;
|
||||
} else if (in == "cur-size-all-mem-tables") {
|
||||
return kCurSizeAllMemTables;
|
||||
} else if (in == "num-entries-active-mem-table") {
|
||||
return kNumEntriesInMutableMemtable;
|
||||
} else if (in == "num-entries-imm-mem-tables") {
|
||||
@@ -167,7 +161,8 @@ bool InternalStats::GetStringProperty(DBPropertyType property_type,
|
||||
const Slice& property,
|
||||
std::string* value) {
|
||||
assert(value != nullptr);
|
||||
Version* current = cfd_->current();
|
||||
auto* current = cfd_->current();
|
||||
const auto* vstorage = current->storage_info();
|
||||
Slice in = property;
|
||||
|
||||
switch (property_type) {
|
||||
@@ -180,7 +175,7 @@ bool InternalStats::GetStringProperty(DBPropertyType property_type,
|
||||
} else {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "%d",
|
||||
current->NumLevelFiles(static_cast<int>(level)));
|
||||
vstorage->NumLevelFiles(static_cast<int>(level)));
|
||||
*value = buf;
|
||||
return true;
|
||||
}
|
||||
@@ -194,8 +189,8 @@ bool InternalStats::GetStringProperty(DBPropertyType property_type,
|
||||
|
||||
for (int level = 0; level < number_levels_; level++) {
|
||||
snprintf(buf, sizeof(buf), "%3d %8d %8.0f\n", level,
|
||||
current->NumLevelFiles(level),
|
||||
current->NumLevelBytes(level) / kMB);
|
||||
vstorage->NumLevelFiles(level),
|
||||
vstorage->NumLevelBytes(level) / kMB);
|
||||
value->append(buf);
|
||||
}
|
||||
return true;
|
||||
@@ -227,7 +222,7 @@ bool InternalStats::GetStringProperty(DBPropertyType property_type,
|
||||
|
||||
bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
uint64_t* value, DBImpl* db) const {
|
||||
Version* current = cfd_->current();
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
|
||||
switch (property_type) {
|
||||
case kNumImmutableMemTable:
|
||||
@@ -240,7 +235,7 @@ bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
case kCompactionPending:
|
||||
// 1 if the system already determines at least one compacdtion is needed.
|
||||
// 0 otherwise,
|
||||
*value = (current->NeedsCompaction() ? 1 : 0);
|
||||
*value = (vstorage->NeedsCompaction() ? 1 : 0);
|
||||
return true;
|
||||
case kBackgroundErrors:
|
||||
// Accumulated number of errors in background flushes or compactions.
|
||||
@@ -250,12 +245,17 @@ bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
// Current size of the active memtable
|
||||
*value = cfd_->mem()->ApproximateMemoryUsage();
|
||||
return true;
|
||||
case kCurSizeAllMemTables:
|
||||
// Current size of the active memtable + immutable memtables
|
||||
*value = cfd_->mem()->ApproximateMemoryUsage() +
|
||||
cfd_->imm()->ApproximateMemoryUsage();
|
||||
return true;
|
||||
case kNumEntriesInMutableMemtable:
|
||||
// Current size of the active memtable
|
||||
// Current number of entires in the active memtable
|
||||
*value = cfd_->mem()->GetNumEntries();
|
||||
return true;
|
||||
case kNumEntriesInImmutableMemtable:
|
||||
// Current size of the active memtable
|
||||
// Current number of entries in the immutable memtables
|
||||
*value = cfd_->imm()->current()->GetTotalNumEntries();
|
||||
return true;
|
||||
case kEstimatedNumKeys:
|
||||
@@ -263,7 +263,7 @@ bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
// Use estimated entries in tables + total entries in memtables.
|
||||
*value = cfd_->mem()->GetNumEntries() +
|
||||
cfd_->imm()->current()->GetTotalNumEntries() +
|
||||
current->GetEstimatedActiveKeys();
|
||||
vstorage->GetEstimatedActiveKeys();
|
||||
return true;
|
||||
#ifndef ROCKSDB_LITE
|
||||
case kIsFileDeletionEnabled:
|
||||
@@ -286,16 +286,25 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
value->append(buf);
|
||||
// Cumulative
|
||||
uint64_t user_bytes_written = db_stats_[InternalStats::BYTES_WRITTEN];
|
||||
uint64_t num_keys_written = db_stats_[InternalStats::NUMBER_KEYS_WRITTEN];
|
||||
uint64_t write_other = db_stats_[InternalStats::WRITE_DONE_BY_OTHER];
|
||||
uint64_t write_self = db_stats_[InternalStats::WRITE_DONE_BY_SELF];
|
||||
uint64_t wal_bytes = db_stats_[InternalStats::WAL_FILE_BYTES];
|
||||
uint64_t wal_synced = db_stats_[InternalStats::WAL_FILE_SYNCED];
|
||||
uint64_t write_with_wal = db_stats_[InternalStats::WRITE_WITH_WAL];
|
||||
// Data
|
||||
// writes: total number of write requests.
|
||||
// keys: total number of key updates issued by all the write requests
|
||||
// batches: number of group commits issued to the DB. Each group can contain
|
||||
// one or more writes.
|
||||
// so writes/keys is the average number of put in multi-put or put
|
||||
// writes/batches is the average group commit size.
|
||||
//
|
||||
// The format is the same for interval stats.
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative writes: %" PRIu64 " writes, %" PRIu64 " batches, "
|
||||
"%.1f writes per batch, %.2f GB user ingest\n",
|
||||
write_other + write_self, write_self,
|
||||
"Cumulative writes: %" PRIu64 " writes, %" PRIu64 " keys, %" PRIu64
|
||||
" batches, %.1f writes per batch, %.2f GB user ingest\n",
|
||||
write_other + write_self, num_keys_written, write_self,
|
||||
(write_other + write_self) / static_cast<double>(write_self + 1),
|
||||
user_bytes_written / kGB);
|
||||
value->append(buf);
|
||||
@@ -311,11 +320,13 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
// Interval
|
||||
uint64_t interval_write_other = write_other - db_stats_snapshot_.write_other;
|
||||
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;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval writes: %" PRIu64 " writes, %" PRIu64 " batches, "
|
||||
"%.1f writes per batch, %.1f MB user ingest\n",
|
||||
"Interval writes: %" PRIu64 " writes, %" PRIu64 " keys, %" PRIu64
|
||||
" batches, %.1f writes per batch, %.1f MB user ingest\n",
|
||||
interval_write_other + interval_write_self,
|
||||
interval_write_self,
|
||||
interval_num_keys_written, interval_write_self,
|
||||
static_cast<double>(interval_write_other + interval_write_self) /
|
||||
(interval_write_self + 1),
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB);
|
||||
@@ -340,30 +351,32 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
db_stats_snapshot_.ingest_bytes = user_bytes_written;
|
||||
db_stats_snapshot_.write_other = write_other;
|
||||
db_stats_snapshot_.write_self = write_self;
|
||||
db_stats_snapshot_.num_keys_written = num_keys_written;
|
||||
db_stats_snapshot_.wal_bytes = wal_bytes;
|
||||
db_stats_snapshot_.wal_synced = wal_synced;
|
||||
db_stats_snapshot_.write_with_wal = write_with_wal;
|
||||
}
|
||||
|
||||
void InternalStats::DumpCFStats(std::string* value) {
|
||||
Version* current = cfd_->current();
|
||||
const VersionStorageInfo* vstorage = cfd_->current()->storage_info();
|
||||
|
||||
int num_levels_to_check =
|
||||
(cfd_->options()->compaction_style != kCompactionStyleUniversal &&
|
||||
cfd_->options()->compaction_style != kCompactionStyleFIFO)
|
||||
? current->NumberLevels() - 1
|
||||
? vstorage->num_levels() - 1
|
||||
: 1;
|
||||
|
||||
// Compaction scores are sorted base on its value. Restore them to the
|
||||
// level order
|
||||
std::vector<double> compaction_score(number_levels_, 0);
|
||||
for (int i = 0; i < num_levels_to_check; ++i) {
|
||||
compaction_score[current->compaction_level_[i]] =
|
||||
current->compaction_score_[i];
|
||||
compaction_score[vstorage->CompactionScoreLevel(i)] =
|
||||
vstorage->CompactionScore(i);
|
||||
}
|
||||
// Count # of files being compacted for each level
|
||||
std::vector<int> files_being_compacted(number_levels_, 0);
|
||||
for (int level = 0; level < num_levels_to_check; ++level) {
|
||||
for (auto* f : current->files_[level]) {
|
||||
for (auto* f : vstorage->LevelFiles(level)) {
|
||||
if (f->being_compacted) {
|
||||
++files_being_compacted[level];
|
||||
}
|
||||
@@ -386,7 +399,7 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
uint64_t total_stall_count = 0;
|
||||
double total_stall_us = 0;
|
||||
for (int level = 0; level < number_levels_; level++) {
|
||||
int files = current->NumLevelFiles(level);
|
||||
int files = vstorage->NumLevelFiles(level);
|
||||
total_files += files;
|
||||
total_files_being_compacted += files_being_compacted[level];
|
||||
if (comp_stats_[level].micros > 0 || files > 0) {
|
||||
@@ -405,7 +418,7 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
stall_leveln_slowdown_hard_[level]);
|
||||
|
||||
stats_sum.Add(comp_stats_[level]);
|
||||
total_file_size += current->NumLevelBytes(level);
|
||||
total_file_size += vstorage->NumLevelBytes(level);
|
||||
total_stall_us += stall_us;
|
||||
total_stall_count += stalls;
|
||||
total_slowdown_soft += stall_leveln_slowdown_soft_[level];
|
||||
@@ -420,10 +433,10 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
double w_amp = (comp_stats_[level].bytes_readn == 0) ? 0.0
|
||||
: comp_stats_[level].bytes_written /
|
||||
static_cast<double>(comp_stats_[level].bytes_readn);
|
||||
PrintLevelStats(buf, sizeof(buf), "L" + std::to_string(level),
|
||||
files, files_being_compacted[level], current->NumLevelBytes(level),
|
||||
compaction_score[level], rw_amp, w_amp, stall_us, stalls,
|
||||
comp_stats_[level]);
|
||||
PrintLevelStats(buf, sizeof(buf), "L" + std::to_string(level), files,
|
||||
files_being_compacted[level],
|
||||
vstorage->NumLevelBytes(level), compaction_score[level],
|
||||
rw_amp, w_amp, stall_us, stalls, comp_stats_[level]);
|
||||
value->append(buf);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-4
@@ -36,6 +36,8 @@ enum DBPropertyType : uint32_t {
|
||||
kCompactionPending, // Return 1 if a compaction is pending. Otherwise 0.
|
||||
kBackgroundErrors, // Return accumulated background errors encountered.
|
||||
kCurSizeActiveMemTable, // Return current size of the active memtable
|
||||
kCurSizeAllMemTables, // Return current size of all (active + immutable)
|
||||
// memtables
|
||||
kNumEntriesInMutableMemtable, // Return number of entries in the mutable
|
||||
// memtable.
|
||||
kNumEntriesInImmutableMemtable, // Return sum of number of entries in all
|
||||
@@ -65,6 +67,7 @@ class InternalStats {
|
||||
WAL_FILE_BYTES,
|
||||
WAL_FILE_SYNCED,
|
||||
BYTES_WRITTEN,
|
||||
NUMBER_KEYS_WRITTEN,
|
||||
WRITE_DONE_BY_OTHER,
|
||||
WRITE_DONE_BY_SELF,
|
||||
WRITE_WITH_WAL,
|
||||
@@ -124,16 +127,16 @@ class InternalStats {
|
||||
int files_out_levelnp1;
|
||||
|
||||
// Total incoming entries during compaction between levels N and N+1
|
||||
int num_input_records;
|
||||
uint64_t num_input_records;
|
||||
|
||||
// Accumulated diff number of entries
|
||||
// (num input entries - num output entires) for compaction levels N and N+1
|
||||
int num_dropped_records;
|
||||
uint64_t num_dropped_records;
|
||||
|
||||
// Number of compactions done
|
||||
int count;
|
||||
|
||||
explicit CompactionStats(int count = 0)
|
||||
explicit CompactionStats(int _count = 0)
|
||||
: micros(0),
|
||||
bytes_readn(0),
|
||||
bytes_readnp1(0),
|
||||
@@ -143,7 +146,7 @@ class InternalStats {
|
||||
files_out_levelnp1(0),
|
||||
num_input_records(0),
|
||||
num_dropped_records(0),
|
||||
count(count) {}
|
||||
count(_count) {}
|
||||
|
||||
explicit CompactionStats(const CompactionStats& c)
|
||||
: micros(c.micros),
|
||||
@@ -262,6 +265,11 @@ class InternalStats {
|
||||
// another thread.
|
||||
uint64_t write_other;
|
||||
uint64_t write_self;
|
||||
// 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
|
||||
// those writes.
|
||||
uint64_t num_keys_written;
|
||||
double seconds_up;
|
||||
|
||||
DBStatsSnapshot()
|
||||
@@ -271,6 +279,7 @@ class InternalStats {
|
||||
write_with_wal(0),
|
||||
write_other(0),
|
||||
write_self(0),
|
||||
num_keys_written(0),
|
||||
seconds_up(0) {}
|
||||
} db_stats_snapshot_;
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class MemTable;
|
||||
|
||||
struct JobContext {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return candidate_files.size() || sst_delete_files.size() ||
|
||||
log_delete_files.size();
|
||||
}
|
||||
|
||||
// Structure to store information for candidate files to delete.
|
||||
struct CandidateFileInfo {
|
||||
std::string file_name;
|
||||
uint32_t path_id;
|
||||
CandidateFileInfo(std::string name, uint32_t path)
|
||||
: file_name(std::move(name)), path_id(path) {}
|
||||
bool operator==(const CandidateFileInfo& other) const {
|
||||
return file_name == other.file_name && path_id == other.path_id;
|
||||
}
|
||||
};
|
||||
|
||||
// a list of all files that we'll consider deleting
|
||||
// (every once in a while this is filled up with all files
|
||||
// in the DB directory)
|
||||
std::vector<CandidateFileInfo> candidate_files;
|
||||
|
||||
// the list of all live sst files that cannot be deleted
|
||||
std::vector<FileDescriptor> sst_live;
|
||||
|
||||
// a list of sst files that we need to delete
|
||||
std::vector<FileMetaData*> sst_delete_files;
|
||||
|
||||
// a list of log files that we need to delete
|
||||
std::vector<uint64_t> log_delete_files;
|
||||
|
||||
// a list of memtables to be free
|
||||
autovector<MemTable*> memtables_to_free;
|
||||
|
||||
autovector<SuperVersion*> superversions_to_free;
|
||||
|
||||
SuperVersion* new_superversion; // if nullptr no new superversion
|
||||
|
||||
// the current manifest_file_number, log_number and prev_log_number
|
||||
// that corresponds to the set of files in 'live'.
|
||||
uint64_t manifest_file_number, pending_manifest_file_number, log_number,
|
||||
prev_log_number;
|
||||
|
||||
explicit JobContext(bool create_superversion = false) {
|
||||
manifest_file_number = 0;
|
||||
pending_manifest_file_number = 0;
|
||||
log_number = 0;
|
||||
prev_log_number = 0;
|
||||
new_superversion = create_superversion ? new SuperVersion() : nullptr;
|
||||
}
|
||||
|
||||
~JobContext() {
|
||||
// free pending memtables
|
||||
for (auto m : memtables_to_free) {
|
||||
delete m;
|
||||
}
|
||||
// free superversions
|
||||
for (auto s : superversions_to_free) {
|
||||
delete s;
|
||||
}
|
||||
// if new_superversion was not used, it will be non-nullptr and needs
|
||||
// to be freed here
|
||||
delete new_superversion;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+3
-4
@@ -20,9 +20,9 @@ namespace log {
|
||||
Reader::Reporter::~Reporter() {
|
||||
}
|
||||
|
||||
Reader::Reader(unique_ptr<SequentialFile>&& file, Reporter* reporter,
|
||||
Reader::Reader(unique_ptr<SequentialFile>&& _file, Reporter* reporter,
|
||||
bool checksum, uint64_t initial_offset)
|
||||
: file_(std::move(file)),
|
||||
: file_(std::move(_file)),
|
||||
reporter_(reporter),
|
||||
checksum_(checksum),
|
||||
backing_store_(new char[kBlockSize]),
|
||||
@@ -32,8 +32,7 @@ Reader::Reader(unique_ptr<SequentialFile>&& file, Reporter* reporter,
|
||||
eof_offset_(0),
|
||||
last_record_offset_(0),
|
||||
end_of_buffer_offset_(0),
|
||||
initial_offset_(initial_offset) {
|
||||
}
|
||||
initial_offset_(initial_offset) {}
|
||||
|
||||
Reader::~Reader() {
|
||||
delete[] backing_store_;
|
||||
|
||||
+24
-21
@@ -32,7 +32,8 @@
|
||||
namespace rocksdb {
|
||||
|
||||
MemTableOptions::MemTableOptions(
|
||||
const MutableCFOptions& mutable_cf_options, const Options& options)
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options)
|
||||
: write_buffer_size(mutable_cf_options.write_buffer_size),
|
||||
arena_block_size(mutable_cf_options.arena_block_size),
|
||||
memtable_prefix_bloom_bits(mutable_cf_options.memtable_prefix_bloom_bits),
|
||||
@@ -40,21 +41,23 @@ MemTableOptions::MemTableOptions(
|
||||
mutable_cf_options.memtable_prefix_bloom_probes),
|
||||
memtable_prefix_bloom_huge_page_tlb_size(
|
||||
mutable_cf_options.memtable_prefix_bloom_huge_page_tlb_size),
|
||||
inplace_update_support(options.inplace_update_support),
|
||||
inplace_update_num_locks(options.inplace_update_num_locks),
|
||||
inplace_callback(options.inplace_callback),
|
||||
inplace_update_support(ioptions.inplace_update_support),
|
||||
inplace_update_num_locks(mutable_cf_options.inplace_update_num_locks),
|
||||
inplace_callback(ioptions.inplace_callback),
|
||||
max_successive_merges(mutable_cf_options.max_successive_merges),
|
||||
filter_deletes(mutable_cf_options.filter_deletes) {}
|
||||
filter_deletes(mutable_cf_options.filter_deletes),
|
||||
statistics(ioptions.statistics),
|
||||
merge_operator(ioptions.merge_operator),
|
||||
info_log(ioptions.info_log) {}
|
||||
|
||||
MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MemTableOptions& moptions)
|
||||
const MutableCFOptions& mutable_cf_options)
|
||||
: comparator_(cmp),
|
||||
ioptions_(ioptions),
|
||||
moptions_(moptions),
|
||||
moptions_(ioptions, mutable_cf_options),
|
||||
refs_(0),
|
||||
kArenaBlockSize(OptimizeBlockSize(moptions.arena_block_size)),
|
||||
arena_(moptions.arena_block_size),
|
||||
kArenaBlockSize(OptimizeBlockSize(moptions_.arena_block_size)),
|
||||
arena_(moptions_.arena_block_size),
|
||||
table_(ioptions.memtable_factory->CreateMemTableRep(
|
||||
comparator_, &arena_, ioptions.prefix_extractor, ioptions.info_log)),
|
||||
num_entries_(0),
|
||||
@@ -63,20 +66,20 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
file_number_(0),
|
||||
first_seqno_(0),
|
||||
mem_next_logfile_number_(0),
|
||||
locks_(moptions.inplace_update_support ? moptions.inplace_update_num_locks
|
||||
: 0),
|
||||
locks_(moptions_.inplace_update_support ?
|
||||
moptions_.inplace_update_num_locks : 0),
|
||||
prefix_extractor_(ioptions.prefix_extractor),
|
||||
should_flush_(ShouldFlushNow()),
|
||||
flush_scheduled_(false) {
|
||||
// if should_flush_ == true without an entry inserted, something must have
|
||||
// gone wrong already.
|
||||
assert(!should_flush_);
|
||||
if (prefix_extractor_ && moptions.memtable_prefix_bloom_bits > 0) {
|
||||
if (prefix_extractor_ && moptions_.memtable_prefix_bloom_bits > 0) {
|
||||
prefix_bloom_.reset(new DynamicBloom(
|
||||
&arena_,
|
||||
moptions.memtable_prefix_bloom_bits, ioptions.bloom_locality,
|
||||
moptions.memtable_prefix_bloom_probes, nullptr,
|
||||
moptions.memtable_prefix_bloom_huge_page_tlb_size,
|
||||
moptions_.memtable_prefix_bloom_bits, ioptions.bloom_locality,
|
||||
moptions_.memtable_prefix_bloom_probes, nullptr,
|
||||
moptions_.memtable_prefix_bloom_huge_page_tlb_size,
|
||||
ioptions.info_log));
|
||||
}
|
||||
}
|
||||
@@ -454,10 +457,10 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
saver.status = s;
|
||||
saver.mem = this;
|
||||
saver.merge_context = merge_context;
|
||||
saver.merge_operator = ioptions_.merge_operator;
|
||||
saver.logger = ioptions_.info_log;
|
||||
saver.merge_operator = moptions_.merge_operator;
|
||||
saver.logger = moptions_.info_log;
|
||||
saver.inplace_update_support = moptions_.inplace_update_support;
|
||||
saver.statistics = ioptions_.statistics;
|
||||
saver.statistics = moptions_.statistics;
|
||||
table_->Get(key, &saver, SaveValue);
|
||||
}
|
||||
|
||||
@@ -578,12 +581,12 @@ bool MemTable::UpdateCallback(SequenceNumber seq,
|
||||
memcpy(p, prev_buffer, new_prev_size);
|
||||
}
|
||||
}
|
||||
RecordTick(ioptions_.statistics, NUMBER_KEYS_UPDATED);
|
||||
RecordTick(moptions_.statistics, NUMBER_KEYS_UPDATED);
|
||||
should_flush_ = ShouldFlushNow();
|
||||
return true;
|
||||
} else if (status == UpdateStatus::UPDATED) {
|
||||
Add(seq, kTypeValue, key, Slice(str_value));
|
||||
RecordTick(ioptions_.statistics, NUMBER_KEYS_WRITTEN);
|
||||
RecordTick(moptions_.statistics, NUMBER_KEYS_WRITTEN);
|
||||
should_flush_ = ShouldFlushNow();
|
||||
return true;
|
||||
} else if (status == UpdateStatus::UPDATE_FAILED) {
|
||||
|
||||
+6
-5
@@ -32,8 +32,8 @@ class MergeContext;
|
||||
|
||||
struct MemTableOptions {
|
||||
explicit MemTableOptions(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Options& options);
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
size_t write_buffer_size;
|
||||
size_t arena_block_size;
|
||||
uint32_t memtable_prefix_bloom_bits;
|
||||
@@ -47,6 +47,9 @@ struct MemTableOptions {
|
||||
std::string* merged_value);
|
||||
size_t max_successive_merges;
|
||||
bool filter_deletes;
|
||||
Statistics* statistics;
|
||||
MergeOperator* merge_operator;
|
||||
Logger* info_log;
|
||||
};
|
||||
|
||||
class MemTable {
|
||||
@@ -64,7 +67,7 @@ class MemTable {
|
||||
// is zero and the caller must call Ref() at least once.
|
||||
explicit MemTable(const InternalKeyComparator& comparator,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MemTableOptions& moptions);
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
~MemTable();
|
||||
|
||||
@@ -199,7 +202,6 @@ class MemTable {
|
||||
|
||||
const Arena& TEST_GetArena() const { return arena_; }
|
||||
|
||||
const ImmutableCFOptions* GetImmutableOptions() const { return &ioptions_; }
|
||||
const MemTableOptions* GetMemTableOptions() const { return &moptions_; }
|
||||
|
||||
private:
|
||||
@@ -211,7 +213,6 @@ class MemTable {
|
||||
friend class MemTableList;
|
||||
|
||||
KeyComparator comparator_;
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
const MemTableOptions moptions_;
|
||||
int refs_;
|
||||
const size_t kArenaBlockSize;
|
||||
|
||||
+25
-22
@@ -5,6 +5,11 @@
|
||||
//
|
||||
#include "db/memtable_list.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include "rocksdb/db.h"
|
||||
#include "db/memtable.h"
|
||||
@@ -114,7 +119,7 @@ void MemTableListVersion::Remove(MemTable* m) {
|
||||
bool MemTableList::IsFlushPending() const {
|
||||
if ((flush_requested_ && num_flush_not_started_ >= 1) ||
|
||||
(num_flush_not_started_ >= min_write_buffer_number_to_merge_)) {
|
||||
assert(imm_flush_needed.NoBarrier_Load() != nullptr);
|
||||
assert(imm_flush_needed.load(std::memory_order_relaxed));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -129,7 +134,7 @@ void MemTableList::PickMemtablesToFlush(autovector<MemTable*>* ret) {
|
||||
assert(!m->flush_completed_);
|
||||
num_flush_not_started_--;
|
||||
if (num_flush_not_started_ == 0) {
|
||||
imm_flush_needed.Release_Store(nullptr);
|
||||
imm_flush_needed.store(false, std::memory_order_release);
|
||||
}
|
||||
m->flush_in_progress_ = true; // flushing will start very soon
|
||||
ret->push_back(m);
|
||||
@@ -155,16 +160,16 @@ void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
num_flush_not_started_++;
|
||||
}
|
||||
pending_outputs->erase(file_number);
|
||||
imm_flush_needed.Release_Store(reinterpret_cast<void *>(1));
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
// Record a successful flush in the manifest file
|
||||
Status MemTableList::InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& mems, VersionSet* vset,
|
||||
port::Mutex* mu, Logger* info_log, uint64_t file_number,
|
||||
FileNumToPathIdMap* pending_outputs, autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory, LogBuffer* log_buffer) {
|
||||
const autovector<MemTable*>& mems, VersionSet* vset, port::Mutex* mu,
|
||||
uint64_t file_number, FileNumToPathIdMap* pending_outputs,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer) {
|
||||
mu->AssertHeld();
|
||||
|
||||
// flush was sucessful
|
||||
@@ -194,8 +199,8 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
break;
|
||||
}
|
||||
|
||||
LogToBuffer(log_buffer, "[%s] Level-0 commit table #%lu started",
|
||||
cfd->GetName().c_str(), (unsigned long)m->file_number_);
|
||||
LogToBuffer(log_buffer, "[%s] Level-0 commit table #%" PRIu64 " started",
|
||||
cfd->GetName().c_str(), m->file_number_);
|
||||
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(cfd, mutable_cf_options, &m->edit_, mu, db_directory);
|
||||
@@ -209,10 +214,9 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
uint64_t mem_id = 1; // how many memtables has been flushed.
|
||||
do {
|
||||
if (s.ok()) { // commit new state
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Level-0 commit table #%lu: memtable #%lu done",
|
||||
cfd->GetName().c_str(), (unsigned long)m->file_number_,
|
||||
(unsigned long)mem_id);
|
||||
LogToBuffer(log_buffer, "[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " done",
|
||||
cfd->GetName().c_str(), m->file_number_, mem_id);
|
||||
current_->Remove(m);
|
||||
assert(m->file_number_ > 0);
|
||||
|
||||
@@ -226,17 +230,16 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
}
|
||||
} else {
|
||||
//commit failed. setup state so that we can flush again.
|
||||
Log(info_log,
|
||||
"Level-0 commit table #%lu: memtable #%lu failed",
|
||||
(unsigned long)m->file_number_,
|
||||
(unsigned long)mem_id);
|
||||
LogToBuffer(log_buffer, "Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " failed",
|
||||
m->file_number_, mem_id);
|
||||
m->flush_completed_ = false;
|
||||
m->flush_in_progress_ = false;
|
||||
m->edit_.Clear();
|
||||
num_flush_not_started_++;
|
||||
pending_outputs->erase(m->file_number_);
|
||||
m->file_number_ = 0;
|
||||
imm_flush_needed.Release_Store((void *)1);
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
++mem_id;
|
||||
} while (!current_->memlist_.empty() && (m = current_->memlist_.back()) &&
|
||||
@@ -259,17 +262,17 @@ void MemTableList::Add(MemTable* m) {
|
||||
m->MarkImmutable();
|
||||
num_flush_not_started_++;
|
||||
if (num_flush_not_started_ == 1) {
|
||||
imm_flush_needed.Release_Store((void *)1);
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an estimate of the number of bytes of data in use.
|
||||
size_t MemTableList::ApproximateMemoryUsage() {
|
||||
size_t size = 0;
|
||||
size_t total_size = 0;
|
||||
for (auto& memtable : current_->memlist_) {
|
||||
size += memtable->ApproximateMemoryUsage();
|
||||
total_size += memtable->ApproximateMemoryUsage();
|
||||
}
|
||||
return size;
|
||||
return total_size;
|
||||
}
|
||||
|
||||
void MemTableList::InstallNewVersion() {
|
||||
|
||||
+7
-7
@@ -78,12 +78,12 @@ class MemTableList {
|
||||
public:
|
||||
// A list of memtables.
|
||||
explicit MemTableList(int min_write_buffer_number_to_merge)
|
||||
: min_write_buffer_number_to_merge_(min_write_buffer_number_to_merge),
|
||||
: imm_flush_needed(false),
|
||||
min_write_buffer_number_to_merge_(min_write_buffer_number_to_merge),
|
||||
current_(new MemTableListVersion()),
|
||||
num_flush_not_started_(0),
|
||||
commit_in_progress_(false),
|
||||
flush_requested_(false) {
|
||||
imm_flush_needed.Release_Store(nullptr);
|
||||
current_->Ref();
|
||||
}
|
||||
~MemTableList() {}
|
||||
@@ -92,7 +92,7 @@ class MemTableList {
|
||||
|
||||
// so that background threads can detect non-nullptr pointer to
|
||||
// determine whether there is anything more to start flushing.
|
||||
port::AtomicPointer imm_flush_needed;
|
||||
std::atomic<bool> imm_flush_needed;
|
||||
|
||||
// Returns the total number of memtables in the list
|
||||
int size() const;
|
||||
@@ -114,10 +114,10 @@ class MemTableList {
|
||||
// Commit a successful flush in the manifest file
|
||||
Status InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& m, VersionSet* vset,
|
||||
port::Mutex* mu, Logger* info_log, uint64_t file_number,
|
||||
FileNumToPathIdMap* pending_outputs, autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory, LogBuffer* log_buffer);
|
||||
const autovector<MemTable*>& m, VersionSet* vset, port::Mutex* mu,
|
||||
uint64_t file_number, FileNumToPathIdMap* pending_outputs,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
// Takes ownership of the referenced held on *m by the caller of Add().
|
||||
|
||||
+11
-10
@@ -85,9 +85,10 @@ void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
// We store the result in keys_.back() and operands_.back()
|
||||
// if nothing went wrong (i.e.: no operand corruption on disk)
|
||||
if (success_) {
|
||||
std::string& key = keys_.back(); // The original key encountered
|
||||
std::string& original_key =
|
||||
keys_.back(); // The original key encountered
|
||||
orig_ikey.type = kTypeValue;
|
||||
UpdateInternalKey(&key[0], key.size(),
|
||||
UpdateInternalKey(&original_key[0], original_key.size(),
|
||||
orig_ikey.sequence, orig_ikey.type);
|
||||
swap(operands_.back(), merge_result);
|
||||
} else {
|
||||
@@ -108,17 +109,17 @@ 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! Success!
|
||||
const Slice value = iter->value();
|
||||
success_ = user_merge_operator_->FullMerge(ikey.user_key, &value,
|
||||
operands_, &merge_result,
|
||||
logger_);
|
||||
const Slice val = iter->value();
|
||||
success_ = user_merge_operator_->FullMerge(ikey.user_key, &val, operands_,
|
||||
&merge_result, logger_);
|
||||
|
||||
// We store the result in keys_.back() and operands_.back()
|
||||
// if nothing went wrong (i.e.: no operand corruption on disk)
|
||||
if (success_) {
|
||||
std::string& key = keys_.back(); // The original key encountered
|
||||
std::string& original_key =
|
||||
keys_.back(); // The original key encountered
|
||||
orig_ikey.type = kTypeValue;
|
||||
UpdateInternalKey(&key[0], key.size(),
|
||||
UpdateInternalKey(&original_key[0], original_key.size(),
|
||||
orig_ikey.sequence, orig_ikey.type);
|
||||
swap(operands_.back(), merge_result);
|
||||
} else {
|
||||
@@ -177,9 +178,9 @@ void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
logger_);
|
||||
|
||||
if (success_) {
|
||||
std::string& key = keys_.back(); // The original key encountered
|
||||
std::string& original_key = keys_.back(); // The original key encountered
|
||||
orig_ikey.type = kTypeValue;
|
||||
UpdateInternalKey(&key[0], key.size(),
|
||||
UpdateInternalKey(&original_key[0], original_key.size(),
|
||||
orig_ikey.sequence, orig_ikey.type);
|
||||
|
||||
// The final value() is always stored in operands_.back()
|
||||
|
||||
+13
-12
@@ -407,14 +407,6 @@ void testSingleBatchSuccessiveMerge(
|
||||
}
|
||||
|
||||
void runTest(int argc, const string& dbname, const bool use_ttl = false) {
|
||||
auto db = OpenDb(dbname, use_ttl);
|
||||
|
||||
{
|
||||
cout << "Test read-modify-write counters... \n";
|
||||
Counters counters(db, 0);
|
||||
testCounters(counters, db.get(), true);
|
||||
}
|
||||
|
||||
bool compact = false;
|
||||
if (argc > 1) {
|
||||
compact = true;
|
||||
@@ -422,13 +414,22 @@ void runTest(int argc, const string& dbname, const bool use_ttl = false) {
|
||||
}
|
||||
|
||||
{
|
||||
cout << "Test merge-based counters... \n";
|
||||
MergeBasedCounters counters(db, 0);
|
||||
testCounters(counters, db.get(), compact);
|
||||
auto db = OpenDb(dbname, use_ttl);
|
||||
|
||||
{
|
||||
cout << "Test read-modify-write counters... \n";
|
||||
Counters counters(db, 0);
|
||||
testCounters(counters, db.get(), true);
|
||||
}
|
||||
|
||||
{
|
||||
cout << "Test merge-based counters... \n";
|
||||
MergeBasedCounters counters(db, 0);
|
||||
testCounters(counters, db.get(), compact);
|
||||
}
|
||||
}
|
||||
|
||||
DestroyDB(dbname, Options());
|
||||
db.reset();
|
||||
|
||||
{
|
||||
cout << "Test merge in memtable... \n";
|
||||
|
||||
@@ -696,40 +696,12 @@ TEST(PlainTableDBTest, IteratorLargeKeysWithPrefix) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
// A test comparator which compare two strings in this way:
|
||||
// (1) first compare prefix of 8 bytes in alphabet order,
|
||||
// (2) if two strings share the same prefix, sort the other part of the string
|
||||
// in the reverse alphabet order.
|
||||
class SimpleSuffixReverseComparator : public Comparator {
|
||||
public:
|
||||
SimpleSuffixReverseComparator() {}
|
||||
|
||||
virtual const char* Name() const { return "SimpleSuffixReverseComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
Slice prefix_a = Slice(a.data(), 8);
|
||||
Slice prefix_b = Slice(b.data(), 8);
|
||||
int prefix_comp = prefix_a.compare(prefix_b);
|
||||
if (prefix_comp != 0) {
|
||||
return prefix_comp;
|
||||
} else {
|
||||
Slice suffix_a = Slice(a.data() + 8, a.size() - 8);
|
||||
Slice suffix_b = Slice(b.data() + 8, b.size() - 8);
|
||||
return -(suffix_a.compare(suffix_b));
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const {}
|
||||
};
|
||||
|
||||
TEST(PlainTableDBTest, IteratorReverseSuffixComparator) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
// Set only one bucket to force bucket conflict.
|
||||
// Test index interval for the same prefix to be 1, 2 and 4
|
||||
SimpleSuffixReverseComparator comp;
|
||||
test::SimpleSuffixReverseComparator comp;
|
||||
options.comparator = ∁
|
||||
DestroyAndReopen(&options);
|
||||
|
||||
@@ -892,7 +864,7 @@ TEST(PlainTableDBTest, HashBucketConflictReverseSuffixComparator) {
|
||||
for (unsigned char i = 1; i <= 3; i++) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
SimpleSuffixReverseComparator comp;
|
||||
test::SimpleSuffixReverseComparator comp;
|
||||
options.comparator = ∁
|
||||
// Set only one bucket to force bucket conflict.
|
||||
// Test index interval for the same prefix to be 1, 2 and 4
|
||||
|
||||
+2
-1
@@ -52,7 +52,8 @@ struct TestKey {
|
||||
uint64_t prefix;
|
||||
uint64_t sorted;
|
||||
|
||||
TestKey(uint64_t prefix, uint64_t sorted) : prefix(prefix), sorted(sorted) {}
|
||||
TestKey(uint64_t _prefix, uint64_t _sorted)
|
||||
: prefix(_prefix), sorted(_sorted) {}
|
||||
};
|
||||
|
||||
// return a slice backed by test_key
|
||||
|
||||
+18
-12
@@ -94,7 +94,7 @@ class Repairer {
|
||||
for (size_t i = 0; i < tables_.size(); i++) {
|
||||
bytes += tables_[i].meta.fd.GetFileSize();
|
||||
}
|
||||
Log(options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
|
||||
"**** Repaired rocksdb %s; "
|
||||
"recovered %zu files; %" PRIu64
|
||||
"bytes. "
|
||||
@@ -175,7 +175,7 @@ class Repairer {
|
||||
std::string logname = LogFileName(dbname_, logs_[i]);
|
||||
Status status = ConvertLogToTable(logs_[i]);
|
||||
if (!status.ok()) {
|
||||
Log(options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
|
||||
"Log #%" PRIu64 ": ignoring conversion error: %s", logs_[i],
|
||||
status.ToString().c_str());
|
||||
}
|
||||
@@ -190,7 +190,8 @@ class Repairer {
|
||||
uint64_t lognum;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
// We print error messages for corruption, but continue repairing.
|
||||
Log(info_log, "Log #%" PRIu64 ": dropping %d bytes; %s", lognum,
|
||||
Log(InfoLogLevel::ERROR_LEVEL, info_log,
|
||||
"Log #%" PRIu64 ": dropping %d bytes; %s", lognum,
|
||||
static_cast<int>(bytes), s.ToString().c_str());
|
||||
}
|
||||
};
|
||||
@@ -220,7 +221,7 @@ class Repairer {
|
||||
Slice record;
|
||||
WriteBatch batch;
|
||||
MemTable* mem = new MemTable(icmp_, ioptions_,
|
||||
MemTableOptions(MutableCFOptions(options_, ioptions_), options_));
|
||||
MutableCFOptions(options_, ioptions_));
|
||||
auto cf_mems_default = new ColumnFamilyMemTablesDefault(mem, &options_);
|
||||
mem->Ref();
|
||||
int counter = 0;
|
||||
@@ -235,7 +236,8 @@ class Repairer {
|
||||
if (status.ok()) {
|
||||
counter += WriteBatchInternal::Count(&batch);
|
||||
} else {
|
||||
Log(options_.info_log, "Log #%" PRIu64 ": ignoring %s", log,
|
||||
Log(InfoLogLevel::WARN_LEVEL,
|
||||
options_.info_log, "Log #%" PRIu64 ": ignoring %s", log,
|
||||
status.ToString().c_str());
|
||||
status = Status::OK(); // Keep going with rest of file
|
||||
}
|
||||
@@ -262,9 +264,9 @@ class Repairer {
|
||||
table_fds_.push_back(meta.fd);
|
||||
}
|
||||
}
|
||||
Log(options_.info_log,
|
||||
"Log #%" PRIu64 ": %d ops saved to Table #%" PRIu64 " %s", log, counter,
|
||||
meta.fd.GetNumber(), status.ToString().c_str());
|
||||
Log(InfoLogLevel::INFO_LEVEL, options_.info_log,
|
||||
"Log #%" PRIu64 ": %d ops saved to Table #%" PRIu64 " %s",
|
||||
log, counter, meta.fd.GetNumber(), status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -279,7 +281,8 @@ class Repairer {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
FormatFileNumber(t.meta.fd.GetNumber(), t.meta.fd.GetPathId(),
|
||||
file_num_buf, sizeof(file_num_buf));
|
||||
Log(options_.info_log, "Table #%s: ignoring %s", file_num_buf,
|
||||
Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
|
||||
"Table #%s: ignoring %s", file_num_buf,
|
||||
status.ToString().c_str());
|
||||
ArchiveFile(fname);
|
||||
} else {
|
||||
@@ -306,7 +309,8 @@ class Repairer {
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
Slice key = iter->key();
|
||||
if (!ParseInternalKey(key, &parsed)) {
|
||||
Log(options_.info_log, "Table #%" PRIu64 ": unparsable key %s",
|
||||
Log(InfoLogLevel::ERROR_LEVEL,
|
||||
options_.info_log, "Table #%" PRIu64 ": unparsable key %s",
|
||||
t->meta.fd.GetNumber(), EscapeString(key).c_str());
|
||||
continue;
|
||||
}
|
||||
@@ -329,7 +333,8 @@ class Repairer {
|
||||
}
|
||||
delete iter;
|
||||
}
|
||||
Log(options_.info_log, "Table #%" PRIu64 ": %d entries %s",
|
||||
Log(InfoLogLevel::INFO_LEVEL,
|
||||
options_.info_log, "Table #%" PRIu64 ": %d entries %s",
|
||||
t->meta.fd.GetNumber(), counter, status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
@@ -406,7 +411,8 @@ class Repairer {
|
||||
new_file.append("/");
|
||||
new_file.append((slash == nullptr) ? fname.c_str() : slash + 1);
|
||||
Status s = env_->RenameFile(fname, new_file);
|
||||
Log(options_.info_log, "Archiving %s: %s\n",
|
||||
Log(InfoLogLevel::INFO_LEVEL,
|
||||
options_.info_log, "Archiving %s: %s\n",
|
||||
fname.c_str(), s.ToString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
+11
-11
@@ -32,6 +32,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <assert.h>
|
||||
#include <atomic>
|
||||
#include <stdlib.h>
|
||||
#include "util/arena.h"
|
||||
#include "port/port.h"
|
||||
@@ -115,15 +116,14 @@ class SkipList {
|
||||
|
||||
// Modified only by Insert(). Read racily by readers, but stale
|
||||
// values are ok.
|
||||
port::AtomicPointer max_height_; // Height of the entire list
|
||||
std::atomic<int> max_height_; // Height of the entire list
|
||||
|
||||
// Used for optimizing sequential insert patterns
|
||||
Node** prev_;
|
||||
int32_t prev_height_;
|
||||
|
||||
inline int GetMaxHeight() const {
|
||||
return static_cast<int>(
|
||||
reinterpret_cast<intptr_t>(max_height_.NoBarrier_Load()));
|
||||
return max_height_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Read/written only by Insert().
|
||||
@@ -169,35 +169,35 @@ struct SkipList<Key, Comparator>::Node {
|
||||
assert(n >= 0);
|
||||
// Use an 'acquire load' so that we observe a fully initialized
|
||||
// version of the returned Node.
|
||||
return reinterpret_cast<Node*>(next_[n].Acquire_Load());
|
||||
return (next_[n].load(std::memory_order_acquire));
|
||||
}
|
||||
void SetNext(int n, Node* x) {
|
||||
assert(n >= 0);
|
||||
// Use a 'release store' so that anybody who reads through this
|
||||
// pointer observes a fully initialized version of the inserted node.
|
||||
next_[n].Release_Store(x);
|
||||
next_[n].store(x, std::memory_order_release);
|
||||
}
|
||||
|
||||
// No-barrier variants that can be safely used in a few locations.
|
||||
Node* NoBarrier_Next(int n) {
|
||||
assert(n >= 0);
|
||||
return reinterpret_cast<Node*>(next_[n].NoBarrier_Load());
|
||||
return next_[n].load(std::memory_order_relaxed);
|
||||
}
|
||||
void NoBarrier_SetNext(int n, Node* x) {
|
||||
assert(n >= 0);
|
||||
next_[n].NoBarrier_Store(x);
|
||||
next_[n].store(x, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
// Array of length equal to the node height. next_[0] is lowest level link.
|
||||
port::AtomicPointer next_[1];
|
||||
std::atomic<Node*> next_[1];
|
||||
};
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
typename SkipList<Key, Comparator>::Node*
|
||||
SkipList<Key, Comparator>::NewNode(const Key& key, int height) {
|
||||
char* mem = arena_->AllocateAligned(
|
||||
sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1));
|
||||
sizeof(Node) + sizeof(std::atomic<Node*>) * (height - 1));
|
||||
return new (mem) Node(key);
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ SkipList<Key, Comparator>::SkipList(const Comparator cmp, Arena* arena,
|
||||
compare_(cmp),
|
||||
arena_(arena),
|
||||
head_(NewNode(0 /* any key will do */, max_height)),
|
||||
max_height_(reinterpret_cast<void*>(1)),
|
||||
max_height_(1),
|
||||
prev_height_(1),
|
||||
rnd_(0xdeadbeef) {
|
||||
assert(kMaxHeight_ > 0);
|
||||
@@ -402,7 +402,7 @@ void SkipList<Key, Comparator>::Insert(const Key& key) {
|
||||
// the loop below. In the former case the reader will
|
||||
// immediately drop to the next level since nullptr sorts after all
|
||||
// keys. In the latter case the reader will use the new node.
|
||||
max_height_.NoBarrier_Store(reinterpret_cast<void*>(height));
|
||||
max_height_.store(height, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
x = NewNode(key, height);
|
||||
|
||||
+12
-17
@@ -191,13 +191,11 @@ class ConcurrentTest {
|
||||
|
||||
// Per-key generation
|
||||
struct State {
|
||||
port::AtomicPointer generation[K];
|
||||
void Set(int k, intptr_t v) {
|
||||
generation[k].Release_Store(reinterpret_cast<void*>(v));
|
||||
}
|
||||
intptr_t Get(int k) {
|
||||
return reinterpret_cast<intptr_t>(generation[k].Acquire_Load());
|
||||
std::atomic<int> generation[K];
|
||||
void Set(int k, int v) {
|
||||
generation[k].store(v, std::memory_order_release);
|
||||
}
|
||||
int Get(int k) { return generation[k].load(std::memory_order_acquire); }
|
||||
|
||||
State() {
|
||||
for (unsigned int k = 0; k < K; k++) {
|
||||
@@ -221,9 +219,9 @@ class ConcurrentTest {
|
||||
// REQUIRES: External synchronization
|
||||
void WriteStep(Random* rnd) {
|
||||
const uint32_t k = rnd->Next() % K;
|
||||
const intptr_t g = current_.Get(k) + 1;
|
||||
const Key key = MakeKey(k, g);
|
||||
list_.Insert(key);
|
||||
const int g = current_.Get(k) + 1;
|
||||
const Key new_key = MakeKey(k, g);
|
||||
list_.Insert(new_key);
|
||||
current_.Set(k, g);
|
||||
}
|
||||
|
||||
@@ -303,7 +301,7 @@ class TestState {
|
||||
public:
|
||||
ConcurrentTest t_;
|
||||
int seed_;
|
||||
port::AtomicPointer quit_flag_;
|
||||
std::atomic<bool> quit_flag_;
|
||||
|
||||
enum ReaderState {
|
||||
STARTING,
|
||||
@@ -312,10 +310,7 @@ class TestState {
|
||||
};
|
||||
|
||||
explicit TestState(int s)
|
||||
: seed_(s),
|
||||
quit_flag_(nullptr),
|
||||
state_(STARTING),
|
||||
state_cv_(&mu_) {}
|
||||
: seed_(s), quit_flag_(false), state_(STARTING), state_cv_(&mu_) {}
|
||||
|
||||
void Wait(ReaderState s) {
|
||||
mu_.Lock();
|
||||
@@ -343,7 +338,7 @@ static void ConcurrentReader(void* arg) {
|
||||
Random rnd(state->seed_);
|
||||
int64_t reads = 0;
|
||||
state->Change(TestState::RUNNING);
|
||||
while (!state->quit_flag_.Acquire_Load()) {
|
||||
while (!state->quit_flag_.load(std::memory_order_acquire)) {
|
||||
state->t_.ReadStep(&rnd);
|
||||
++reads;
|
||||
}
|
||||
@@ -362,10 +357,10 @@ static void RunConcurrent(int run) {
|
||||
TestState state(seed + 1);
|
||||
Env::Default()->Schedule(ConcurrentReader, &state);
|
||||
state.Wait(TestState::RUNNING);
|
||||
for (int i = 0; i < kSize; i++) {
|
||||
for (int k = 0; k < kSize; k++) {
|
||||
state.t_.WriteStep(&rnd);
|
||||
}
|
||||
state.quit_flag_.Release_Store(&state); // Any non-nullptr arg will do
|
||||
state.quit_flag_.store(true, std::memory_order_release);
|
||||
state.Wait(TestState::DONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ Status TableCache::FindTable(const EnvOptions& env_options,
|
||||
// We do not cache error results so that if the error is transient,
|
||||
// or somebody repairs the file, we recover automatically.
|
||||
} else {
|
||||
assert(file.get() == nullptr);
|
||||
*handle = cache_->Insert(key, table_reader.release(), 1, &DeleteEntry);
|
||||
}
|
||||
}
|
||||
|
||||
+25
-22
@@ -4,6 +4,11 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "db/transaction_log_impl.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
|
||||
@@ -13,7 +18,7 @@ TransactionLogIteratorImpl::TransactionLogIteratorImpl(
|
||||
const std::string& dir, const DBOptions* options,
|
||||
const TransactionLogIterator::ReadOptions& read_options,
|
||||
const EnvOptions& soptions, const SequenceNumber seq,
|
||||
std::unique_ptr<VectorLogPtr> files, DBImpl const* const dbimpl)
|
||||
std::unique_ptr<VectorLogPtr> files, VersionSet const* const versions)
|
||||
: dir_(dir),
|
||||
options_(options),
|
||||
read_options_(read_options),
|
||||
@@ -25,9 +30,9 @@ TransactionLogIteratorImpl::TransactionLogIteratorImpl(
|
||||
currentFileIndex_(0),
|
||||
currentBatchSeq_(0),
|
||||
currentLastSeq_(0),
|
||||
dbimpl_(dbimpl) {
|
||||
versions_(versions) {
|
||||
assert(files_ != nullptr);
|
||||
assert(dbimpl_ != nullptr);
|
||||
assert(versions_ != nullptr);
|
||||
|
||||
reporter_.env = options_->env;
|
||||
reporter_.info_log = options_->info_log.get();
|
||||
@@ -43,14 +48,14 @@ Status TransactionLogIteratorImpl::OpenLogFile(
|
||||
return env->NewSequentialFile(fname, file, soptions_);
|
||||
} else {
|
||||
std::string fname = LogFileName(dir_, logFile->LogNumber());
|
||||
Status status = env->NewSequentialFile(fname, file, soptions_);
|
||||
if (!status.ok()) {
|
||||
Status s = env->NewSequentialFile(fname, file, soptions_);
|
||||
if (!s.ok()) {
|
||||
// If cannot open file in DB directory.
|
||||
// Try the archive dir, as it could have moved in the meanwhile.
|
||||
fname = ArchivedLogFileName(dir_, logFile->LogNumber());
|
||||
status = env->NewSequentialFile(fname, file, soptions_);
|
||||
s = env->NewSequentialFile(fname, file, soptions_);
|
||||
}
|
||||
return status;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +79,7 @@ bool TransactionLogIteratorImpl::RestrictedRead(
|
||||
Slice* record,
|
||||
std::string* scratch) {
|
||||
// Don't read if no more complete entries to read from logs
|
||||
if (currentLastSeq_ >= dbimpl_->GetLatestSequenceNumber()) {
|
||||
if (currentLastSeq_ >= versions_->LastSequence()) {
|
||||
return false;
|
||||
}
|
||||
return currentLogReader_->ReadRecord(record, scratch);
|
||||
@@ -177,15 +182,15 @@ void TransactionLogIteratorImpl::NextImpl(bool internal) {
|
||||
// Open the next file
|
||||
if (currentFileIndex_ < files_->size() - 1) {
|
||||
++currentFileIndex_;
|
||||
Status status =OpenLogReader(files_->at(currentFileIndex_).get());
|
||||
if (!status.ok()) {
|
||||
Status s = OpenLogReader(files_->at(currentFileIndex_).get());
|
||||
if (!s.ok()) {
|
||||
isValid_ = false;
|
||||
currentStatus_ = status;
|
||||
currentStatus_ = s;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
isValid_ = false;
|
||||
if (currentLastSeq_ == dbimpl_->GetLatestSequenceNumber()) {
|
||||
if (currentLastSeq_ == versions_->LastSequence()) {
|
||||
currentStatus_ = Status::OK();
|
||||
} else {
|
||||
currentStatus_ = Status::Corruption("NO MORE DATA LEFT");
|
||||
@@ -203,12 +208,10 @@ bool TransactionLogIteratorImpl::IsBatchExpected(
|
||||
if (batchSeq != expectedSeq) {
|
||||
char buf[200];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Discontinuity in log records. Got seq=%lu, Expected seq=%lu, "
|
||||
"Last flushed seq=%lu.Log iterator will reseek the correct "
|
||||
"batch.",
|
||||
(unsigned long)batchSeq,
|
||||
(unsigned long)expectedSeq,
|
||||
(unsigned long)dbimpl_->GetLatestSequenceNumber());
|
||||
"Discontinuity in log records. Got seq=%" PRIu64
|
||||
", Expected seq=%" PRIu64 ", Last flushed seq=%" PRIu64
|
||||
".Log iterator will reseek the correct batch.",
|
||||
batchSeq, expectedSeq, versions_->LastSequence());
|
||||
reporter_.Info(buf);
|
||||
return false;
|
||||
}
|
||||
@@ -240,7 +243,7 @@ void TransactionLogIteratorImpl::UpdateCurrentWriteBatch(const Slice& record) {
|
||||
currentLastSeq_ = currentBatchSeq_ +
|
||||
WriteBatchInternal::Count(batch.get()) - 1;
|
||||
// currentBatchSeq_ can only change here
|
||||
assert(currentLastSeq_ <= dbimpl_->GetLatestSequenceNumber());
|
||||
assert(currentLastSeq_ <= versions_->LastSequence());
|
||||
|
||||
currentBatch_ = move(batch);
|
||||
isValid_ = true;
|
||||
@@ -249,9 +252,9 @@ void TransactionLogIteratorImpl::UpdateCurrentWriteBatch(const Slice& record) {
|
||||
|
||||
Status TransactionLogIteratorImpl::OpenLogReader(const LogFile* logFile) {
|
||||
unique_ptr<SequentialFile> file;
|
||||
Status status = OpenLogFile(logFile, &file);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
Status s = OpenLogFile(logFile, &file);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
assert(file);
|
||||
currentLogReader_.reset(new log::Reader(std::move(file), &reporter_,
|
||||
|
||||
+18
-15
@@ -11,23 +11,12 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/filename.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
Log(info_log, "dropping %zu bytes; %s", bytes, s.ToString().c_str());
|
||||
}
|
||||
virtual void Info(const char* s) {
|
||||
Log(info_log, "%s", s);
|
||||
}
|
||||
};
|
||||
|
||||
class LogFileImpl : public LogFile {
|
||||
public:
|
||||
LogFileImpl(uint64_t logNum, WalFileType logType, SequenceNumber startSeq,
|
||||
@@ -71,7 +60,7 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
const std::string& dir, const DBOptions* options,
|
||||
const TransactionLogIterator::ReadOptions& read_options,
|
||||
const EnvOptions& soptions, const SequenceNumber seqNum,
|
||||
std::unique_ptr<VectorLogPtr> files, DBImpl const* const dbimpl);
|
||||
std::unique_ptr<VectorLogPtr> files, VersionSet const* const versions);
|
||||
|
||||
virtual bool Valid();
|
||||
|
||||
@@ -95,10 +84,24 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
std::unique_ptr<WriteBatch> currentBatch_;
|
||||
unique_ptr<log::Reader> currentLogReader_;
|
||||
Status OpenLogFile(const LogFile* logFile, unique_ptr<SequentialFile>* file);
|
||||
LogReporter reporter_;
|
||||
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, info_log, "dropping %zu bytes; %s", bytes,
|
||||
s.ToString().c_str());
|
||||
}
|
||||
virtual void Info(const char* s) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, info_log, "%s", s);
|
||||
}
|
||||
} reporter_;
|
||||
|
||||
SequenceNumber currentBatchSeq_; // sequence number at start of current batch
|
||||
SequenceNumber currentLastSeq_; // last sequence in the current batch
|
||||
DBImpl const * const dbimpl_; // The db on whose log files this iterates
|
||||
// Used only to get latest seq. num
|
||||
// TODO(icanadi) can this be just a callback?
|
||||
VersionSet const* const versions_;
|
||||
|
||||
// Reads from transaction log only if the writebatch record has been written
|
||||
bool RestrictedRead(Slice* record, std::string* scratch);
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
// 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/version_builder.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_set.h"
|
||||
#include "table/table_reader.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
bool NewestFirstBySeqNo(FileMetaData* a, FileMetaData* b) {
|
||||
if (a->smallest_seqno != b->smallest_seqno) {
|
||||
return a->smallest_seqno > b->smallest_seqno;
|
||||
}
|
||||
if (a->largest_seqno != b->largest_seqno) {
|
||||
return a->largest_seqno > b->largest_seqno;
|
||||
}
|
||||
// Break ties by file number
|
||||
return a->fd.GetNumber() > b->fd.GetNumber();
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool BySmallestKey(FileMetaData* a, FileMetaData* b,
|
||||
const InternalKeyComparator* cmp) {
|
||||
int r = cmp->Compare(a->smallest, b->smallest);
|
||||
if (r != 0) {
|
||||
return (r < 0);
|
||||
}
|
||||
// Break ties by file number
|
||||
return (a->fd.GetNumber() < b->fd.GetNumber());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class VersionBuilder::Rep {
|
||||
private:
|
||||
// Helper to sort files_ in v
|
||||
// kLevel0 -- NewestFirstBySeqNo
|
||||
// kLevelNon0 -- BySmallestKey
|
||||
struct FileComparator {
|
||||
enum SortMethod { kLevel0 = 0, kLevelNon0 = 1, } sort_method;
|
||||
const InternalKeyComparator* internal_comparator;
|
||||
|
||||
bool operator()(FileMetaData* f1, FileMetaData* f2) const {
|
||||
switch (sort_method) {
|
||||
case kLevel0:
|
||||
return NewestFirstBySeqNo(f1, f2);
|
||||
case kLevelNon0:
|
||||
return BySmallestKey(f1, f2, internal_comparator);
|
||||
}
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<FileMetaData*, FileComparator> FileSet;
|
||||
struct LevelState {
|
||||
std::set<uint64_t> deleted_files;
|
||||
FileSet* added_files;
|
||||
};
|
||||
|
||||
const EnvOptions& env_options_;
|
||||
TableCache* table_cache_;
|
||||
VersionStorageInfo* base_vstorage_;
|
||||
LevelState* levels_;
|
||||
FileComparator level_zero_cmp_;
|
||||
FileComparator level_nonzero_cmp_;
|
||||
|
||||
public:
|
||||
Rep(const EnvOptions& env_options, TableCache* table_cache,
|
||||
VersionStorageInfo* base_vstorage)
|
||||
: env_options_(env_options),
|
||||
table_cache_(table_cache),
|
||||
base_vstorage_(base_vstorage) {
|
||||
levels_ = new LevelState[base_vstorage_->num_levels()];
|
||||
level_zero_cmp_.sort_method = FileComparator::kLevel0;
|
||||
level_nonzero_cmp_.sort_method = FileComparator::kLevelNon0;
|
||||
level_nonzero_cmp_.internal_comparator =
|
||||
base_vstorage_->InternalComparator();
|
||||
|
||||
levels_[0].added_files = new FileSet(level_zero_cmp_);
|
||||
for (int level = 1; level < base_vstorage_->num_levels(); level++) {
|
||||
levels_[level].added_files = new FileSet(level_nonzero_cmp_);
|
||||
}
|
||||
}
|
||||
|
||||
~Rep() {
|
||||
for (int level = 0; level < base_vstorage_->num_levels(); level++) {
|
||||
const FileSet* added = levels_[level].added_files;
|
||||
std::vector<FileMetaData*> to_unref;
|
||||
to_unref.reserve(added->size());
|
||||
for (FileSet::const_iterator it = added->begin(); it != added->end();
|
||||
++it) {
|
||||
to_unref.push_back(*it);
|
||||
}
|
||||
delete added;
|
||||
for (uint32_t i = 0; i < to_unref.size(); i++) {
|
||||
FileMetaData* f = to_unref[i];
|
||||
f->refs--;
|
||||
if (f->refs <= 0) {
|
||||
if (f->table_reader_handle) {
|
||||
assert(table_cache_ != nullptr);
|
||||
table_cache_->ReleaseHandle(f->table_reader_handle);
|
||||
f->table_reader_handle = nullptr;
|
||||
}
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] levels_;
|
||||
}
|
||||
|
||||
void CheckConsistency(VersionStorageInfo* vstorage) {
|
||||
#ifndef NDEBUG
|
||||
// make sure the files are sorted correctly
|
||||
for (int level = 0; level < vstorage->num_levels(); level++) {
|
||||
auto& level_files = vstorage->LevelFiles(level);
|
||||
for (size_t i = 1; i < level_files.size(); i++) {
|
||||
auto f1 = level_files[i - 1];
|
||||
auto f2 = level_files[i];
|
||||
if (level == 0) {
|
||||
assert(level_zero_cmp_(f1, f2));
|
||||
assert(f1->largest_seqno > f2->largest_seqno);
|
||||
} else {
|
||||
assert(level_nonzero_cmp_(f1, f2));
|
||||
|
||||
// Make sure there is no overlap in levels > 0
|
||||
if (vstorage->InternalComparator()->Compare(f1->largest,
|
||||
f2->smallest) >= 0) {
|
||||
fprintf(stderr, "overlapping ranges in same level %s vs. %s\n",
|
||||
(f1->largest).DebugString().c_str(),
|
||||
(f2->smallest).DebugString().c_str());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckConsistencyForDeletes(VersionEdit* edit, uint64_t number,
|
||||
int level) {
|
||||
#ifndef NDEBUG
|
||||
// a file to be deleted better exist in the previous version
|
||||
bool found = false;
|
||||
for (int l = 0; !found && l < base_vstorage_->num_levels(); l++) {
|
||||
const std::vector<FileMetaData*>& base_files =
|
||||
base_vstorage_->LevelFiles(l);
|
||||
for (unsigned int i = 0; i < base_files.size(); i++) {
|
||||
FileMetaData* f = base_files[i];
|
||||
if (f->fd.GetNumber() == number) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the file did not exist in the previous version, then it
|
||||
// is possibly moved from lower level to higher level in current
|
||||
// version
|
||||
for (int l = level + 1; !found && l < base_vstorage_->num_levels(); l++) {
|
||||
const FileSet* added = levels_[l].added_files;
|
||||
for (FileSet::const_iterator added_iter = added->begin();
|
||||
added_iter != added->end(); ++added_iter) {
|
||||
FileMetaData* f = *added_iter;
|
||||
if (f->fd.GetNumber() == number) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// maybe this file was added in a previous edit that was Applied
|
||||
if (!found) {
|
||||
const FileSet* added = levels_[level].added_files;
|
||||
for (FileSet::const_iterator added_iter = added->begin();
|
||||
added_iter != added->end(); ++added_iter) {
|
||||
FileMetaData* f = *added_iter;
|
||||
if (f->fd.GetNumber() == number) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fprintf(stderr, "not found %" PRIu64 "\n", number);
|
||||
}
|
||||
assert(found);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Apply all of the edits in *edit to the current state.
|
||||
void Apply(VersionEdit* edit) {
|
||||
CheckConsistency(base_vstorage_);
|
||||
|
||||
// Delete files
|
||||
const VersionEdit::DeletedFileSet& del = edit->GetDeletedFiles();
|
||||
for (const auto& del_file : del) {
|
||||
const auto level = del_file.first;
|
||||
const auto number = del_file.second;
|
||||
levels_[level].deleted_files.insert(number);
|
||||
CheckConsistencyForDeletes(edit, number, level);
|
||||
}
|
||||
|
||||
// Add new files
|
||||
for (const auto& new_file : edit->GetNewFiles()) {
|
||||
const int level = new_file.first;
|
||||
FileMetaData* f = new FileMetaData(new_file.second);
|
||||
f->refs = 1;
|
||||
|
||||
levels_[level].deleted_files.erase(f->fd.GetNumber());
|
||||
levels_[level].added_files->insert(f);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the current state in *v.
|
||||
void SaveTo(VersionStorageInfo* vstorage) {
|
||||
CheckConsistency(base_vstorage_);
|
||||
CheckConsistency(vstorage);
|
||||
|
||||
for (int level = 0; level < base_vstorage_->num_levels(); level++) {
|
||||
const auto& cmp = (level == 0) ? level_zero_cmp_ : level_nonzero_cmp_;
|
||||
// Merge the set of added files with the set of pre-existing files.
|
||||
// Drop any deleted files. Store the result in *v.
|
||||
const auto& base_files = base_vstorage_->LevelFiles(level);
|
||||
auto base_iter = base_files.begin();
|
||||
auto base_end = base_files.end();
|
||||
const auto& added_files = *levels_[level].added_files;
|
||||
vstorage->Reserve(level, base_files.size() + added_files.size());
|
||||
|
||||
for (const auto& added : added_files) {
|
||||
// Add all smaller files listed in base_
|
||||
for (auto bpos = std::upper_bound(base_iter, base_end, added, cmp);
|
||||
base_iter != bpos; ++base_iter) {
|
||||
MaybeAddFile(vstorage, level, *base_iter);
|
||||
}
|
||||
|
||||
MaybeAddFile(vstorage, level, added);
|
||||
}
|
||||
|
||||
// Add remaining base files
|
||||
for (; base_iter != base_end; ++base_iter) {
|
||||
MaybeAddFile(vstorage, level, *base_iter);
|
||||
}
|
||||
}
|
||||
|
||||
CheckConsistency(vstorage);
|
||||
}
|
||||
|
||||
void LoadTableHandlers() {
|
||||
assert(table_cache_ != nullptr);
|
||||
for (int level = 0; level < base_vstorage_->num_levels(); level++) {
|
||||
for (auto& file_meta : *(levels_[level].added_files)) {
|
||||
assert(!file_meta->table_reader_handle);
|
||||
table_cache_->FindTable(
|
||||
env_options_, *(base_vstorage_->InternalComparator()),
|
||||
file_meta->fd, &file_meta->table_reader_handle, false);
|
||||
if (file_meta->table_reader_handle != nullptr) {
|
||||
// Load table_reader
|
||||
file_meta->fd.table_reader = table_cache_->GetTableReaderFromHandle(
|
||||
file_meta->table_reader_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaybeAddFile(VersionStorageInfo* vstorage, int level, FileMetaData* f) {
|
||||
if (levels_[level].deleted_files.count(f->fd.GetNumber()) > 0) {
|
||||
// File is deleted: do nothing
|
||||
} else {
|
||||
vstorage->MaybeAddFile(level, f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VersionBuilder::VersionBuilder(const EnvOptions& env_options,
|
||||
TableCache* table_cache,
|
||||
VersionStorageInfo* base_vstorage)
|
||||
: rep_(new Rep(env_options, table_cache, base_vstorage)) {}
|
||||
VersionBuilder::~VersionBuilder() { delete rep_; }
|
||||
void VersionBuilder::CheckConsistency(VersionStorageInfo* vstorage) {
|
||||
rep_->CheckConsistency(vstorage);
|
||||
}
|
||||
void VersionBuilder::CheckConsistencyForDeletes(VersionEdit* edit,
|
||||
uint64_t number, int level) {
|
||||
rep_->CheckConsistencyForDeletes(edit, number, level);
|
||||
}
|
||||
void VersionBuilder::Apply(VersionEdit* edit) { rep_->Apply(edit); }
|
||||
void VersionBuilder::SaveTo(VersionStorageInfo* vstorage) {
|
||||
rep_->SaveTo(vstorage);
|
||||
}
|
||||
void VersionBuilder::LoadTableHandlers() { rep_->LoadTableHandlers(); }
|
||||
void VersionBuilder::MaybeAddFile(VersionStorageInfo* vstorage, int level,
|
||||
FileMetaData* f) {
|
||||
rep_->MaybeAddFile(vstorage, level, f);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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.
|
||||
//
|
||||
#pragma once
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class TableCache;
|
||||
class VersionStorageInfo;
|
||||
class VersionEdit;
|
||||
struct FileMetaData;
|
||||
|
||||
// A helper class so we can efficiently apply a whole sequence
|
||||
// of edits to a particular state without creating intermediate
|
||||
// Versions that contain full copies of the intermediate state.
|
||||
class VersionBuilder {
|
||||
public:
|
||||
VersionBuilder(const EnvOptions& env_options, TableCache* table_cache,
|
||||
VersionStorageInfo* base_vstorage);
|
||||
~VersionBuilder();
|
||||
void CheckConsistency(VersionStorageInfo* vstorage);
|
||||
void CheckConsistencyForDeletes(VersionEdit* edit, uint64_t number,
|
||||
int level);
|
||||
void Apply(VersionEdit* edit);
|
||||
void SaveTo(VersionStorageInfo* vstorage);
|
||||
void LoadTableHandlers();
|
||||
void MaybeAddFile(VersionStorageInfo* vstorage, int level, FileMetaData* f);
|
||||
|
||||
private:
|
||||
class Rep;
|
||||
Rep* rep_;
|
||||
};
|
||||
|
||||
extern bool NewestFirstBySeqNo(FileMetaData* a, FileMetaData* b);
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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 <string>
|
||||
#include "db/version_edit.h"
|
||||
#include "db/version_set.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class VersionBuilderTest {
|
||||
public:
|
||||
const Comparator* ucmp;
|
||||
InternalKeyComparator icmp;
|
||||
Options options;
|
||||
ImmutableCFOptions ioptions;
|
||||
MutableCFOptions mutable_cf_options;
|
||||
VersionStorageInfo vstorage;
|
||||
uint32_t file_num;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
std::vector<uint64_t> size_being_compacted;
|
||||
|
||||
VersionBuilderTest()
|
||||
: ucmp(BytewiseComparator()),
|
||||
icmp(ucmp),
|
||||
ioptions(options),
|
||||
mutable_cf_options(options, ioptions),
|
||||
vstorage(&icmp, ucmp, options.num_levels, kCompactionStyleLevel,
|
||||
nullptr),
|
||||
file_num(1) {
|
||||
mutable_cf_options.RefreshDerivedOptions(ioptions);
|
||||
size_being_compacted.resize(options.num_levels);
|
||||
}
|
||||
|
||||
~VersionBuilderTest() {
|
||||
for (int i = 0; i < vstorage.num_levels(); i++) {
|
||||
for (auto* f : vstorage.LevelFiles(i)) {
|
||||
if (--f->refs == 0) {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InternalKey GetInternalKey(const char* ukey,
|
||||
SequenceNumber smallest_seq = 100) {
|
||||
return InternalKey(ukey, smallest_seq, kTypeValue);
|
||||
}
|
||||
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 0, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100) {
|
||||
assert(level < vstorage.num_levels());
|
||||
FileMetaData* f = new FileMetaData;
|
||||
f->fd = FileDescriptor(file_number, path_id, file_size);
|
||||
f->smallest = GetInternalKey(smallest, smallest_seq);
|
||||
f->largest = GetInternalKey(largest, largest_seq);
|
||||
f->compensated_file_size = file_size;
|
||||
f->refs = 0;
|
||||
vstorage.MaybeAddFile(level, f);
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
vstorage.ComputeCompactionScore(mutable_cf_options, fifo_options,
|
||||
size_being_compacted);
|
||||
vstorage.UpdateFilesBySize();
|
||||
vstorage.UpdateNumNonEmptyLevels();
|
||||
vstorage.GenerateFileIndexer();
|
||||
vstorage.GenerateLevelFilesBrief();
|
||||
vstorage.SetFinalized();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(VersionBuilderTest, ApplyAndSaveTo) {
|
||||
Add(0, 1U, "150", "200", 100U);
|
||||
// Level 1 score 1.2
|
||||
Add(1, 66U, "150", "200", 100U);
|
||||
Add(1, 88U, "201", "300", 100U);
|
||||
// Level 2 score 1.8. File 7 is the largest. Should be picked
|
||||
Add(2, 6U, "150", "179", 100U);
|
||||
Add(2, 7U, "180", "220", 100U);
|
||||
Add(2, 8U, "221", "300", 100U);
|
||||
// Level 3 score slightly larger than 1
|
||||
Add(3, 26U, "150", "170", 100U);
|
||||
Add(3, 27U, "171", "179", 100U);
|
||||
Add(3, 28U, "191", "220", 100U);
|
||||
Add(3, 29U, "221", "300", 100U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(2, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200);
|
||||
version_edit.DeleteFile(3, 27U);
|
||||
|
||||
EnvOptions env_options;
|
||||
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage);
|
||||
|
||||
VersionStorageInfo new_vstorage(&icmp, ucmp, options.num_levels,
|
||||
kCompactionStyleLevel, nullptr);
|
||||
version_builder.Apply(&version_edit);
|
||||
version_builder.SaveTo(&new_vstorage);
|
||||
|
||||
ASSERT_EQ(400U, new_vstorage.NumLevelBytes(2));
|
||||
ASSERT_EQ(300U, new_vstorage.NumLevelBytes(3));
|
||||
|
||||
for (int i = 0; i < new_vstorage.num_levels(); i++) {
|
||||
for (auto* f : new_vstorage.LevelFiles(i)) {
|
||||
if (--f->refs == 0) {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
+5
-5
@@ -168,7 +168,6 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
|
||||
// Temporary storage for parsing
|
||||
int level;
|
||||
uint64_t number;
|
||||
FileMetaData f;
|
||||
Slice str;
|
||||
InternalKey key;
|
||||
@@ -237,9 +236,9 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
}
|
||||
break;
|
||||
|
||||
case kDeletedFile:
|
||||
if (GetLevel(&input, &level, &msg) &&
|
||||
GetVarint64(&input, &number)) {
|
||||
case kDeletedFile: {
|
||||
uint64_t number;
|
||||
if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number)) {
|
||||
deleted_files_.insert(std::make_pair(level, number));
|
||||
} else {
|
||||
if (!msg) {
|
||||
@@ -247,6 +246,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kNewFile: {
|
||||
uint64_t number;
|
||||
@@ -297,7 +297,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
new_files_.push_back(std::make_pair(level, f));
|
||||
} else {
|
||||
if (!msg) {
|
||||
msg = "new-file2 entry";
|
||||
msg = "new-file3 entry";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+15
-13
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -38,10 +39,10 @@ struct FileDescriptor {
|
||||
|
||||
FileDescriptor() : FileDescriptor(0, 0, 0) {}
|
||||
|
||||
FileDescriptor(uint64_t number, uint32_t path_id, uint64_t file_size)
|
||||
FileDescriptor(uint64_t number, uint32_t path_id, uint64_t _file_size)
|
||||
: table_reader(nullptr),
|
||||
packed_number_and_path_id(PackFileNumberAndPathId(number, path_id)),
|
||||
file_size(file_size) {}
|
||||
file_size(_file_size) {}
|
||||
|
||||
FileDescriptor& operator=(const FileDescriptor& fd) {
|
||||
table_reader = fd.table_reader;
|
||||
@@ -74,7 +75,7 @@ struct FileMetaData {
|
||||
// Stats for compensating deletion entries during compaction
|
||||
|
||||
// File size compensated by deletion entry.
|
||||
// This is updated in Version::UpdateTemporaryStats() first time when the
|
||||
// This is updated in Version::UpdateAccumulatedStats() first time when the
|
||||
// file is created or loaded. After it is updated, it is immutable.
|
||||
uint64_t compensated_file_size;
|
||||
uint64_t num_entries; // the number of entries.
|
||||
@@ -109,20 +110,16 @@ struct FdWithKeyRange {
|
||||
largest_key() {
|
||||
}
|
||||
|
||||
FdWithKeyRange(FileDescriptor fd,
|
||||
Slice smallest_key, Slice largest_key)
|
||||
: fd(fd),
|
||||
smallest_key(smallest_key),
|
||||
largest_key(largest_key) {
|
||||
}
|
||||
FdWithKeyRange(FileDescriptor _fd, Slice _smallest_key, Slice _largest_key)
|
||||
: fd(_fd), smallest_key(_smallest_key), largest_key(_largest_key) {}
|
||||
};
|
||||
|
||||
// Data structure to store an array of FdWithKeyRange in one level
|
||||
// Actual data is guaranteed to be stored closely
|
||||
struct FileLevel {
|
||||
struct LevelFilesBrief {
|
||||
size_t num_files;
|
||||
FdWithKeyRange* files;
|
||||
FileLevel() {
|
||||
LevelFilesBrief() {
|
||||
num_files = 0;
|
||||
files = nullptr;
|
||||
}
|
||||
@@ -216,14 +213,19 @@ class VersionEdit {
|
||||
bool EncodeTo(std::string* dst) const;
|
||||
Status DecodeFrom(const Slice& src);
|
||||
|
||||
typedef std::set<std::pair<int, uint64_t>> DeletedFileSet;
|
||||
|
||||
const DeletedFileSet& GetDeletedFiles() { return deleted_files_; }
|
||||
const std::vector<std::pair<int, FileMetaData>>& GetNewFiles() {
|
||||
return new_files_;
|
||||
}
|
||||
|
||||
std::string DebugString(bool hex_key = false) const;
|
||||
|
||||
private:
|
||||
friend class VersionSet;
|
||||
friend class Version;
|
||||
|
||||
typedef std::set< std::pair<int, uint64_t>> DeletedFileSet;
|
||||
|
||||
bool GetLevel(Slice* input, int* level, const char** msg);
|
||||
|
||||
int max_level_;
|
||||
|
||||
+413
-540
File diff suppressed because it is too large.
Load diff
+277
-169
@@ -26,6 +26,7 @@
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
#include "db/dbformat.h"
|
||||
#include "db/version_builder.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "port/port.h"
|
||||
#include "db/table_cache.h"
|
||||
@@ -38,10 +39,11 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace log { class Writer; }
|
||||
namespace log {
|
||||
class Writer;
|
||||
}
|
||||
|
||||
class Compaction;
|
||||
class CompactionPicker;
|
||||
class Iterator;
|
||||
class LogBuffer;
|
||||
class LookupKey;
|
||||
@@ -59,7 +61,7 @@ class MergeIteratorBuilder;
|
||||
// REQUIRES: "file_level.files" contains a sorted list of
|
||||
// non-overlapping files.
|
||||
extern int FindFile(const InternalKeyComparator& icmp,
|
||||
const FileLevel& file_level,
|
||||
const LevelFilesBrief& file_level,
|
||||
const Slice& key);
|
||||
|
||||
// Returns true iff some file in "files" overlaps the user key range
|
||||
@@ -71,96 +73,101 @@ extern int FindFile(const InternalKeyComparator& icmp,
|
||||
extern bool SomeFileOverlapsRange(
|
||||
const InternalKeyComparator& icmp,
|
||||
bool disjoint_sorted_files,
|
||||
const FileLevel& file_level,
|
||||
const LevelFilesBrief& file_level,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key);
|
||||
|
||||
// Generate FileLevel from vector<FdWithKeyRange*>
|
||||
// Generate LevelFilesBrief from vector<FdWithKeyRange*>
|
||||
// Would copy smallest_key and largest_key data to sequential memory
|
||||
// arena: Arena used to allocate the memory
|
||||
extern void DoGenerateFileLevel(FileLevel* file_level,
|
||||
extern void DoGenerateLevelFilesBrief(LevelFilesBrief* file_level,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
Arena* arena);
|
||||
|
||||
class Version {
|
||||
class VersionStorageInfo {
|
||||
public:
|
||||
// Append to *iters a sequence of iterators that will
|
||||
// yield the contents of this Version when merged together.
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
VersionStorageInfo(const InternalKeyComparator* internal_comparator,
|
||||
const Comparator* user_comparator, int num_levels,
|
||||
CompactionStyle compaction_style,
|
||||
VersionStorageInfo* src_vstorage);
|
||||
~VersionStorageInfo();
|
||||
|
||||
void AddIterators(const ReadOptions&, const EnvOptions& soptions,
|
||||
MergeIteratorBuilder* merger_iter_builder);
|
||||
void Reserve(int level, size_t size) { files_[level].reserve(size); }
|
||||
|
||||
// Lookup the value for key. If found, store it in *val and
|
||||
// return OK. Else return a non-OK status.
|
||||
// Uses *operands to store merge_operator operations to apply later
|
||||
// REQUIRES: lock is not held
|
||||
void Get(const ReadOptions&, const LookupKey& key, std::string* val,
|
||||
Status* status, MergeContext* merge_context,
|
||||
bool* value_found = nullptr);
|
||||
void MaybeAddFile(int level, FileMetaData* f);
|
||||
|
||||
void SetFinalized() { finalized_ = true; }
|
||||
|
||||
// Update num_non_empty_levels_.
|
||||
void UpdateNumNonEmptyLevels();
|
||||
|
||||
void GenerateFileIndexer() {
|
||||
file_indexer_.UpdateIndex(&arena_, num_non_empty_levels_, files_);
|
||||
}
|
||||
|
||||
// Update the accumulated stats from a file-meta.
|
||||
void UpdateAccumulatedStats(FileMetaData* file_meta);
|
||||
|
||||
void ComputeCompensatedSizes();
|
||||
|
||||
// Updates internal structures that keep track of compaction scores
|
||||
// We use compaction scores to figure out which compaction to do next
|
||||
// REQUIRES: If Version is not yet saved to current_, it can be called without
|
||||
// a lock. Once a version is saved to current_, call only with mutex held
|
||||
// TODO find a better way to pass compaction_options_fifo.
|
||||
void ComputeCompactionScore(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const CompactionOptionsFIFO& compaction_options_fifo,
|
||||
std::vector<uint64_t>& size_being_compacted);
|
||||
|
||||
// Generate file_levels_ from files_
|
||||
void GenerateFileLevels();
|
||||
// Generate level_files_brief_ from files_
|
||||
void GenerateLevelFilesBrief();
|
||||
// Sort all files for this version based on their file size and
|
||||
// record results in files_by_size_. The largest files are listed first.
|
||||
void UpdateFilesBySize();
|
||||
|
||||
// Update scores, pre-calculated variables. It needs to be called before
|
||||
// applying the version to the version set.
|
||||
void PrepareApply(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
std::vector<uint64_t>& size_being_compacted);
|
||||
|
||||
// Reference count management (so Versions do not disappear out from
|
||||
// under live iterators)
|
||||
void Ref();
|
||||
// Decrease reference count. Delete the object if no reference left
|
||||
// and return true. Otherwise, return false.
|
||||
bool Unref();
|
||||
int MaxInputLevel() const;
|
||||
|
||||
// Returns true iff some level needs a compaction.
|
||||
bool NeedsCompaction() const;
|
||||
|
||||
// Returns the maxmimum compaction score for levels 1 to max
|
||||
double MaxCompactionScore() const { return max_compaction_score_; }
|
||||
double max_compaction_score() const { return max_compaction_score_; }
|
||||
|
||||
// See field declaration
|
||||
int MaxCompactionScoreLevel() const { return max_compaction_score_level_; }
|
||||
int max_compaction_score_level() const { return max_compaction_score_level_; }
|
||||
|
||||
// Return level number that has idx'th highest score
|
||||
int CompactionScoreLevel(int idx) const { return compaction_level_[idx]; }
|
||||
|
||||
// Return idx'th highest score
|
||||
double CompactionScore(int idx) const { return compaction_score_[idx]; }
|
||||
|
||||
void GetOverlappingInputs(
|
||||
int level,
|
||||
const InternalKey* begin, // nullptr means before all keys
|
||||
const InternalKey* end, // nullptr means after all keys
|
||||
int level, const InternalKey* begin, // nullptr means before all keys
|
||||
const InternalKey* end, // nullptr means after all keys
|
||||
std::vector<FileMetaData*>* inputs,
|
||||
int hint_index = -1, // index of overlap file
|
||||
int* file_index = nullptr); // return index of overlap file
|
||||
int hint_index = -1, // index of overlap file
|
||||
int* file_index = nullptr); // return index of overlap file
|
||||
|
||||
void GetOverlappingInputsBinarySearch(
|
||||
int level,
|
||||
const Slice& begin, // nullptr means before all keys
|
||||
const Slice& end, // nullptr means after all keys
|
||||
int level, const Slice& begin, // nullptr means before all keys
|
||||
const Slice& end, // nullptr means after all keys
|
||||
std::vector<FileMetaData*>* inputs,
|
||||
int hint_index, // index of overlap file
|
||||
int* file_index); // return index of overlap file
|
||||
int hint_index, // index of overlap file
|
||||
int* file_index); // return index of overlap file
|
||||
|
||||
void ExtendOverlappingInputs(
|
||||
int level,
|
||||
const Slice& begin, // nullptr means before all keys
|
||||
const Slice& end, // nullptr means after all keys
|
||||
int level, const Slice& begin, // nullptr means before all keys
|
||||
const Slice& end, // nullptr means after all keys
|
||||
std::vector<FileMetaData*>* inputs,
|
||||
unsigned int index); // start extending from this index
|
||||
unsigned int index); // start extending from this index
|
||||
|
||||
// Returns true iff some file in the specified level overlaps
|
||||
// some part of [*smallest_user_key,*largest_user_key].
|
||||
// smallest_user_key==NULL represents a key smaller than all keys in the DB.
|
||||
// largest_user_key==NULL represents a key largest than all keys in the DB.
|
||||
bool OverlapInLevel(int level,
|
||||
const Slice* smallest_user_key,
|
||||
bool OverlapInLevel(int level, const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key);
|
||||
|
||||
// Returns true iff the first or last file in inputs contains
|
||||
@@ -170,21 +177,69 @@ class Version {
|
||||
bool HasOverlappingUserKey(const std::vector<FileMetaData*>* inputs,
|
||||
int level);
|
||||
|
||||
|
||||
// Return the level at which we should place a new memtable compaction
|
||||
// result that covers the range [smallest_user_key,largest_user_key].
|
||||
int PickLevelForMemTableOutput(const MutableCFOptions& mutable_cf_options,
|
||||
const Slice& smallest_user_key,
|
||||
const Slice& largest_user_key);
|
||||
|
||||
int NumberLevels() const { return num_levels_; }
|
||||
int num_levels() const { return num_levels_; }
|
||||
|
||||
// REQUIRES: lock is held
|
||||
int NumLevelFiles(int level) const { return files_[level].size(); }
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
int num_non_empty_levels() const {
|
||||
assert(finalized_);
|
||||
return num_non_empty_levels_;
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
int NumLevelFiles(int level) const {
|
||||
assert(finalized_);
|
||||
return files_[level].size();
|
||||
}
|
||||
|
||||
// Return the combined file size of all files at the specified level.
|
||||
uint64_t NumLevelBytes(int level) const;
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
const std::vector<FileMetaData*>& LevelFiles(int level) const {
|
||||
return files_[level];
|
||||
}
|
||||
|
||||
const rocksdb::LevelFilesBrief& LevelFilesBrief(int level) const {
|
||||
return level_files_brief_[level];
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
const std::vector<int>& FilesBySize(int level) const {
|
||||
assert(finalized_);
|
||||
return files_by_size_[level];
|
||||
}
|
||||
|
||||
// REQUIRES: lock is held
|
||||
// Set the index that is used to offset into files_by_size_ to find
|
||||
// the next compaction candidate file.
|
||||
void SetNextCompactionIndex(int level, int index) {
|
||||
next_file_to_compact_by_size_[level] = index;
|
||||
}
|
||||
|
||||
// REQUIRES: lock is held
|
||||
int NextCompactionIndex(int level) const {
|
||||
return next_file_to_compact_by_size_[level];
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
const FileIndexer& file_indexer() const {
|
||||
assert(finalized_);
|
||||
return file_indexer_;
|
||||
}
|
||||
|
||||
// Only the first few entries of files_by_size_ are sorted.
|
||||
// There is no need to sort all the files because it is likely
|
||||
// that on a running system, we need to look at only the first
|
||||
// few largest files because a new version is created every few
|
||||
// seconds/minutes (because of concurrent compactions).
|
||||
static const size_t kNumberFilesToSort = 50;
|
||||
|
||||
// Return a human-readable short (single-line) summary of the number
|
||||
// of files per level. Uses *scratch as backing store.
|
||||
struct LevelSummaryStorage {
|
||||
@@ -202,103 +257,44 @@ class Version {
|
||||
// file at a level >= 1.
|
||||
int64_t MaxNextLevelOverlappingBytes();
|
||||
|
||||
// Add all files listed in the current version to *live.
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live);
|
||||
|
||||
// Return a human readable string that describes this version's contents.
|
||||
std::string DebugString(bool hex = false) const;
|
||||
|
||||
// Returns the version nuber of this version
|
||||
uint64_t GetVersionNumber() const { return version_number_; }
|
||||
|
||||
uint64_t GetAverageValueSize() const {
|
||||
if (num_non_deletions_ == 0) {
|
||||
if (accumulated_num_non_deletions_ == 0) {
|
||||
return 0;
|
||||
}
|
||||
assert(total_raw_key_size_ + total_raw_value_size_ > 0);
|
||||
assert(total_file_size_ > 0);
|
||||
return total_raw_value_size_ / num_non_deletions_ * total_file_size_ /
|
||||
(total_raw_key_size_ + total_raw_value_size_);
|
||||
assert(accumulated_raw_key_size_ + accumulated_raw_value_size_ > 0);
|
||||
assert(accumulated_file_size_ > 0);
|
||||
return accumulated_raw_value_size_ / accumulated_num_non_deletions_ *
|
||||
accumulated_file_size_ /
|
||||
(accumulated_raw_key_size_ + accumulated_raw_value_size_);
|
||||
}
|
||||
|
||||
// REQUIRES: lock is held
|
||||
// On success, "tp" will contains the table properties of the file
|
||||
// specified in "file_meta". If the file name of "file_meta" is
|
||||
// known ahread, passing it by a non-null "fname" can save a
|
||||
// file-name conversion.
|
||||
Status GetTableProperties(std::shared_ptr<const TableProperties>* tp,
|
||||
const FileMetaData* file_meta,
|
||||
const std::string* fname = nullptr);
|
||||
uint64_t GetEstimatedActiveKeys() const;
|
||||
|
||||
// REQUIRES: lock is held
|
||||
// On success, *props will be populated with all SSTables' table properties.
|
||||
// The keys of `props` are the sst file name, the values of `props` are the
|
||||
// tables' propertis, represented as shared_ptr.
|
||||
Status GetPropertiesOfAllTables(TablePropertiesCollection* props);
|
||||
// re-initializes the index that is used to offset into files_by_size_
|
||||
// to find the next compaction candidate file.
|
||||
void ResetNextCompactionIndex(int level) {
|
||||
next_file_to_compact_by_size_[level] = 0;
|
||||
}
|
||||
|
||||
uint64_t GetEstimatedActiveKeys();
|
||||
|
||||
size_t GetMemoryUsageByTableReaders();
|
||||
|
||||
// used to sort files by size
|
||||
struct Fsize {
|
||||
int index;
|
||||
FileMetaData* file;
|
||||
};
|
||||
const InternalKeyComparator* InternalComparator() {
|
||||
return internal_comparator_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Compaction;
|
||||
friend class VersionSet;
|
||||
friend class DBImpl;
|
||||
friend class CompactedDBImpl;
|
||||
friend class ColumnFamilyData;
|
||||
friend class CompactionPicker;
|
||||
friend class LevelCompactionPicker;
|
||||
friend class UniversalCompactionPicker;
|
||||
friend class FIFOCompactionPicker;
|
||||
friend class ForwardIterator;
|
||||
friend class InternalStats;
|
||||
|
||||
class LevelFileNumIterator;
|
||||
class LevelFileIteratorState;
|
||||
|
||||
bool PrefixMayMatch(const ReadOptions& read_options, Iterator* level_iter,
|
||||
const Slice& internal_prefix) const;
|
||||
|
||||
// Update num_non_empty_levels_.
|
||||
void UpdateNumNonEmptyLevels();
|
||||
|
||||
// The helper function of UpdateTemporaryStats, which may fill the missing
|
||||
// fields of file_mata from its associated TableProperties.
|
||||
// Returns true if it does initialize FileMetaData.
|
||||
bool MaybeInitializeFileMetaData(FileMetaData* file_meta);
|
||||
|
||||
// Update the temporary stats associated with the current version.
|
||||
// This temporary stats will be used in compaction.
|
||||
void UpdateTemporaryStats();
|
||||
|
||||
// Sort all files for this version based on their file size and
|
||||
// record results in files_by_size_. The largest files are listed first.
|
||||
void UpdateFilesBySize();
|
||||
|
||||
ColumnFamilyData* cfd_; // ColumnFamilyData to which this Version belongs
|
||||
const InternalKeyComparator* internal_comparator_;
|
||||
const Comparator* user_comparator_;
|
||||
TableCache* table_cache_;
|
||||
const MergeOperator* merge_operator_;
|
||||
|
||||
autovector<FileLevel> file_levels_; // A copy of list of files per level
|
||||
Logger* info_log_;
|
||||
Statistics* db_statistics_;
|
||||
int num_levels_; // Number of levels
|
||||
int num_non_empty_levels_; // Number of levels. Any level larger than it
|
||||
// is guaranteed to be empty.
|
||||
int num_levels_; // Number of levels
|
||||
int num_non_empty_levels_; // Number of levels. Any level larger than it
|
||||
// is guaranteed to be empty.
|
||||
// A short brief metadata of files per level
|
||||
autovector<rocksdb::LevelFilesBrief> level_files_brief_;
|
||||
FileIndexer file_indexer_;
|
||||
VersionSet* vset_; // VersionSet to which this Version belongs
|
||||
Arena arena_; // Used to allocate space for file_levels_
|
||||
Version* next_; // Next version in linked list
|
||||
Version* prev_; // Previous version in linked list
|
||||
int refs_; // Number of live refs to this version
|
||||
Arena arena_; // Used to allocate space for file_levels_
|
||||
|
||||
CompactionStyle compaction_style_;
|
||||
|
||||
// List of files per level, files in each level are arranged
|
||||
// in increasing order of keys
|
||||
@@ -331,31 +327,145 @@ class Version {
|
||||
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
|
||||
|
||||
// the following are the sampled temporary stats.
|
||||
// the current accumulated size of sampled files.
|
||||
uint64_t accumulated_file_size_;
|
||||
// the current accumulated size of all raw keys based on the sampled files.
|
||||
uint64_t accumulated_raw_key_size_;
|
||||
// the current accumulated size of all raw keys based on the sampled files.
|
||||
uint64_t accumulated_raw_value_size_;
|
||||
// total number of non-deletion entries
|
||||
uint64_t accumulated_num_non_deletions_;
|
||||
// total number of deletion entries
|
||||
uint64_t accumulated_num_deletions_;
|
||||
// the number of samples
|
||||
uint64_t num_samples_;
|
||||
|
||||
bool finalized_;
|
||||
|
||||
friend class Version;
|
||||
friend class VersionSet;
|
||||
// No copying allowed
|
||||
VersionStorageInfo(const VersionStorageInfo&) = delete;
|
||||
void operator=(const VersionStorageInfo&) = delete;
|
||||
};
|
||||
|
||||
class Version {
|
||||
public:
|
||||
// Append to *iters a sequence of iterators that will
|
||||
// yield the contents of this Version when merged together.
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
void AddIterators(const ReadOptions&, const EnvOptions& soptions,
|
||||
MergeIteratorBuilder* merger_iter_builder);
|
||||
|
||||
// Lookup the value for key. If found, store it in *val and
|
||||
// return OK. Else return a non-OK status.
|
||||
// Uses *operands to store merge_operator operations to apply later
|
||||
// REQUIRES: lock is not held
|
||||
void Get(const ReadOptions&, const LookupKey& key, std::string* val,
|
||||
Status* status, MergeContext* merge_context,
|
||||
bool* value_found = nullptr);
|
||||
|
||||
// Update scores, pre-calculated variables. It needs to be called before
|
||||
// applying the version to the version set.
|
||||
void PrepareApply(const MutableCFOptions& mutable_cf_options,
|
||||
std::vector<uint64_t>& size_being_compacted);
|
||||
|
||||
// Reference count management (so Versions do not disappear out from
|
||||
// under live iterators)
|
||||
void Ref();
|
||||
// Decrease reference count. Delete the object if no reference left
|
||||
// and return true. Otherwise, return false.
|
||||
bool Unref();
|
||||
|
||||
// Add all files listed in the current version to *live.
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live);
|
||||
|
||||
// Return a human readable string that describes this version's contents.
|
||||
std::string DebugString(bool hex = false) const;
|
||||
|
||||
// Returns the version nuber of this version
|
||||
uint64_t GetVersionNumber() const { return version_number_; }
|
||||
|
||||
// REQUIRES: lock is held
|
||||
// On success, "tp" will contains the table properties of the file
|
||||
// specified in "file_meta". If the file name of "file_meta" is
|
||||
// known ahread, passing it by a non-null "fname" can save a
|
||||
// file-name conversion.
|
||||
Status GetTableProperties(std::shared_ptr<const TableProperties>* tp,
|
||||
const FileMetaData* file_meta,
|
||||
const std::string* fname = nullptr);
|
||||
|
||||
// REQUIRES: lock is held
|
||||
// On success, *props will be populated with all SSTables' table properties.
|
||||
// The keys of `props` are the sst file name, the values of `props` are the
|
||||
// tables' propertis, represented as shared_ptr.
|
||||
Status GetPropertiesOfAllTables(TablePropertiesCollection* props);
|
||||
|
||||
uint64_t GetEstimatedActiveKeys() {
|
||||
return storage_info_.GetEstimatedActiveKeys();
|
||||
}
|
||||
|
||||
size_t GetMemoryUsageByTableReaders();
|
||||
|
||||
ColumnFamilyData* cfd() const { return cfd_; }
|
||||
|
||||
|
||||
// Return the next Version in the linked list. Used for debug only
|
||||
Version* TEST_Next() const {
|
||||
return next_;
|
||||
}
|
||||
|
||||
VersionStorageInfo* storage_info() { return &storage_info_; }
|
||||
|
||||
VersionSet* version_set() { return vset_; }
|
||||
|
||||
private:
|
||||
friend class VersionSet;
|
||||
|
||||
const InternalKeyComparator* internal_comparator() const {
|
||||
return storage_info_.internal_comparator_;
|
||||
}
|
||||
const Comparator* user_comparator() const {
|
||||
return storage_info_.user_comparator_;
|
||||
}
|
||||
|
||||
bool PrefixMayMatch(const ReadOptions& read_options, Iterator* level_iter,
|
||||
const Slice& internal_prefix) const;
|
||||
|
||||
// The helper function of UpdateAccumulatedStats, which may fill the missing
|
||||
// fields of file_mata from its associated TableProperties.
|
||||
// Returns true if it does initialize FileMetaData.
|
||||
bool MaybeInitializeFileMetaData(FileMetaData* file_meta);
|
||||
|
||||
// Update the accumulated stats associated with the current version.
|
||||
// This accumulated stats will be used in compaction.
|
||||
void UpdateAccumulatedStats();
|
||||
|
||||
// Sort all files for this version based on their file size and
|
||||
// record results in files_by_size_. The largest files are listed first.
|
||||
void UpdateFilesBySize();
|
||||
|
||||
ColumnFamilyData* cfd_; // ColumnFamilyData to which this Version belongs
|
||||
Logger* info_log_;
|
||||
Statistics* db_statistics_;
|
||||
TableCache* table_cache_;
|
||||
const MergeOperator* merge_operator_;
|
||||
|
||||
VersionStorageInfo storage_info_;
|
||||
VersionSet* vset_; // VersionSet to which this Version belongs
|
||||
Version* next_; // Next version in linked list
|
||||
Version* prev_; // Previous version in linked list
|
||||
int refs_; // Number of live refs to this version
|
||||
|
||||
// A version number that uniquely represents this version. This is
|
||||
// used for debugging and logging purposes only.
|
||||
uint64_t version_number_;
|
||||
|
||||
Version(ColumnFamilyData* cfd, VersionSet* vset, uint64_t version_number = 0);
|
||||
|
||||
// total file size
|
||||
uint64_t total_file_size_;
|
||||
// the total size of all raw keys.
|
||||
uint64_t total_raw_key_size_;
|
||||
// the total size of all raw values.
|
||||
uint64_t total_raw_value_size_;
|
||||
// total number of non-deletion entries
|
||||
uint64_t num_non_deletions_;
|
||||
// total number of deletion entries
|
||||
uint64_t num_deletions_;
|
||||
|
||||
~Version();
|
||||
|
||||
// re-initializes the index that is used to offset into files_by_size_
|
||||
// to find the next compaction candidate file.
|
||||
void ResetNextCompactionIndex(int level) {
|
||||
next_file_to_compact_by_size_[level] = 0;
|
||||
}
|
||||
|
||||
// No copying allowed
|
||||
Version(const Version&);
|
||||
void operator=(const Version&);
|
||||
@@ -374,13 +484,12 @@ class VersionSet {
|
||||
// column_family_options has to be set if edit is column family add
|
||||
// REQUIRES: *mu is held on entry.
|
||||
// REQUIRES: no other thread concurrently calls LogAndApply()
|
||||
Status LogAndApply(ColumnFamilyData* column_family_data,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionEdit* edit,
|
||||
port::Mutex* mu, Directory* db_directory = nullptr,
|
||||
bool new_descriptor_log = false,
|
||||
const ColumnFamilyOptions* column_family_options =
|
||||
nullptr);
|
||||
Status LogAndApply(
|
||||
ColumnFamilyData* column_family_data,
|
||||
const MutableCFOptions& mutable_cf_options, VersionEdit* edit,
|
||||
port::Mutex* mu, Directory* db_directory = nullptr,
|
||||
bool new_descriptor_log = false,
|
||||
const ColumnFamilyOptions* column_family_options = nullptr);
|
||||
|
||||
// Recover the last saved descriptor from persistent storage.
|
||||
// If read_only == true, Recover() will not complain if some column families
|
||||
@@ -415,9 +524,9 @@ class VersionSet {
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Return the current manifest file number
|
||||
uint64_t ManifestFileNumber() const { return manifest_file_number_; }
|
||||
uint64_t manifest_file_number() const { return manifest_file_number_; }
|
||||
|
||||
uint64_t PendingManifestFileNumber() const {
|
||||
uint64_t pending_manifest_file_number() const {
|
||||
return pending_manifest_file_number_;
|
||||
}
|
||||
|
||||
@@ -449,7 +558,7 @@ class VersionSet {
|
||||
|
||||
// Return the log file number for the log file that is currently
|
||||
// being compacted, or zero if there is no such log file.
|
||||
uint64_t PrevLogNumber() const { return prev_log_number_; }
|
||||
uint64_t prev_log_number() const { return prev_log_number_; }
|
||||
|
||||
// Returns the minimum log number such that all
|
||||
// log numbers less than or equal to it can be deleted
|
||||
@@ -475,7 +584,7 @@ class VersionSet {
|
||||
uint64_t ApproximateOffsetOf(Version* v, const InternalKey& key);
|
||||
|
||||
// Return the size of the current manifest file
|
||||
uint64_t ManifestFileSize() const { return manifest_file_size_; }
|
||||
uint64_t manifest_file_size() const { return manifest_file_size_; }
|
||||
|
||||
// verify that the files that we started with for a compaction
|
||||
// still exist in the current version and in the same original level.
|
||||
@@ -486,15 +595,14 @@ class VersionSet {
|
||||
Status GetMetadataForFile(uint64_t number, int* filelevel,
|
||||
FileMetaData** metadata, ColumnFamilyData** cfd);
|
||||
|
||||
void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData> *metadata);
|
||||
void GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata);
|
||||
|
||||
void GetObsoleteFiles(std::vector<FileMetaData*>* files);
|
||||
|
||||
ColumnFamilySet* GetColumnFamilySet() { return column_family_set_.get(); }
|
||||
const EnvOptions& env_options() { return env_options_; }
|
||||
|
||||
private:
|
||||
class Builder;
|
||||
struct ManifestWriter;
|
||||
|
||||
friend class Version;
|
||||
@@ -554,7 +662,7 @@ class VersionSet {
|
||||
void operator=(const VersionSet&);
|
||||
|
||||
void LogAndApplyCFHelper(VersionEdit* edit);
|
||||
void LogAndApplyHelper(ColumnFamilyData* cfd, Builder* b, Version* v,
|
||||
void LogAndApplyHelper(ColumnFamilyData* cfd, VersionBuilder* b, Version* v,
|
||||
VersionEdit* edit, port::Mutex* mu);
|
||||
};
|
||||
|
||||
|
||||
+11
-11
@@ -14,15 +14,15 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class GenerateFileLevelTest {
|
||||
class GenerateLevelFilesBriefTest {
|
||||
public:
|
||||
std::vector<FileMetaData*> files_;
|
||||
FileLevel file_level_;
|
||||
LevelFilesBrief file_level_;
|
||||
Arena arena_;
|
||||
|
||||
GenerateFileLevelTest() { }
|
||||
GenerateLevelFilesBriefTest() { }
|
||||
|
||||
~GenerateFileLevelTest() {
|
||||
~GenerateLevelFilesBriefTest() {
|
||||
for (unsigned int i = 0; i < files_.size(); i++) {
|
||||
delete files_[i];
|
||||
}
|
||||
@@ -49,33 +49,33 @@ class GenerateFileLevelTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST(GenerateFileLevelTest, Empty) {
|
||||
DoGenerateFileLevel(&file_level_, files_, &arena_);
|
||||
TEST(GenerateLevelFilesBriefTest, Empty) {
|
||||
DoGenerateLevelFilesBrief(&file_level_, files_, &arena_);
|
||||
ASSERT_EQ(0u, file_level_.num_files);
|
||||
ASSERT_EQ(0, Compare());
|
||||
}
|
||||
|
||||
TEST(GenerateFileLevelTest, Single) {
|
||||
TEST(GenerateLevelFilesBriefTest, Single) {
|
||||
Add("p", "q");
|
||||
DoGenerateFileLevel(&file_level_, files_, &arena_);
|
||||
DoGenerateLevelFilesBrief(&file_level_, files_, &arena_);
|
||||
ASSERT_EQ(1u, file_level_.num_files);
|
||||
ASSERT_EQ(0, Compare());
|
||||
}
|
||||
|
||||
|
||||
TEST(GenerateFileLevelTest, Multiple) {
|
||||
TEST(GenerateLevelFilesBriefTest, Multiple) {
|
||||
Add("150", "200");
|
||||
Add("200", "250");
|
||||
Add("300", "350");
|
||||
Add("400", "450");
|
||||
DoGenerateFileLevel(&file_level_, files_, &arena_);
|
||||
DoGenerateLevelFilesBrief(&file_level_, files_, &arena_);
|
||||
ASSERT_EQ(4u, file_level_.num_files);
|
||||
ASSERT_EQ(0, Compare());
|
||||
}
|
||||
|
||||
class FindLevelFileTest {
|
||||
public:
|
||||
FileLevel file_level_;
|
||||
LevelFilesBrief file_level_;
|
||||
bool disjoint_sorted_files_;
|
||||
Arena arena_;
|
||||
|
||||
|
||||
@@ -0,0 +1,456 @@
|
||||
// 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/wal_manager.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "db/filename.h"
|
||||
#include "db/transaction_log_impl.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status WalManager::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// First get sorted files in db dir, then get sorted files from archived
|
||||
// dir, to avoid a race condition where a log file is moved to archived
|
||||
// dir in between.
|
||||
Status s;
|
||||
// list wal files in main db dir.
|
||||
VectorLogPtr logs;
|
||||
s = GetSortedWalsOfType(db_options_.wal_dir, logs, kAliveLogFile);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Reproduce the race condition where a log file is moved
|
||||
// to archived dir, between these two sync points, used in
|
||||
// (DBTest,TransactionLogIteratorRace)
|
||||
TEST_SYNC_POINT("WalManager::GetSortedWalFiles:1");
|
||||
TEST_SYNC_POINT("WalManager::GetSortedWalFiles:2");
|
||||
|
||||
files.clear();
|
||||
// list wal files in archive dir.
|
||||
std::string archivedir = ArchivalDirectory(db_options_.wal_dir);
|
||||
if (env_->FileExists(archivedir)) {
|
||||
s = GetSortedWalsOfType(archivedir, files, kArchivedLogFile);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t latest_archived_log_number = 0;
|
||||
if (!files.empty()) {
|
||||
latest_archived_log_number = files.back()->LogNumber();
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"Latest Archived log: %" PRIu64,
|
||||
latest_archived_log_number);
|
||||
}
|
||||
|
||||
files.reserve(files.size() + logs.size());
|
||||
for (auto& log : logs) {
|
||||
if (log->LogNumber() > latest_archived_log_number) {
|
||||
files.push_back(std::move(log));
|
||||
} else {
|
||||
// When the race condition happens, we could see the
|
||||
// same log in both db dir and archived dir. Simply
|
||||
// ignore the one in db dir. Note that, if we read
|
||||
// archived dir first, we would have missed the log file.
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"%s already moved to archive", log->PathName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status WalManager::GetUpdatesSince(
|
||||
SequenceNumber seq, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options,
|
||||
VersionSet* version_set) {
|
||||
|
||||
// Get all sorted Wal Files.
|
||||
// Do binary search and open files and find the seq number.
|
||||
|
||||
std::unique_ptr<VectorLogPtr> wal_files(new VectorLogPtr);
|
||||
Status s = GetSortedWalFiles(*wal_files);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
s = RetainProbableWalFiles(*wal_files, seq);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
iter->reset(new TransactionLogIteratorImpl(
|
||||
db_options_.wal_dir, &db_options_, read_options, env_options_, seq,
|
||||
std::move(wal_files), version_set));
|
||||
return (*iter)->status();
|
||||
}
|
||||
|
||||
// 1. Go through all archived files and
|
||||
// a. if ttl is enabled, delete outdated files
|
||||
// b. if archive size limit is enabled, delete empty files,
|
||||
// compute file number and size.
|
||||
// 2. If size limit is enabled:
|
||||
// a. compute how many files should be deleted
|
||||
// b. get sorted non-empty archived logs
|
||||
// c. delete what should be deleted
|
||||
void WalManager::PurgeObsoleteWALFiles() {
|
||||
bool const ttl_enabled = db_options_.WAL_ttl_seconds > 0;
|
||||
bool const size_limit_enabled = db_options_.WAL_size_limit_MB > 0;
|
||||
if (!ttl_enabled && !size_limit_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t current_time;
|
||||
Status s = env_->GetCurrentTime(¤t_time);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"Can't get current time: %s", s.ToString().c_str());
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
uint64_t const now_seconds = static_cast<uint64_t>(current_time);
|
||||
uint64_t const time_to_check = (ttl_enabled && !size_limit_enabled)
|
||||
? db_options_.WAL_ttl_seconds / 2
|
||||
: kDefaultIntervalToDeleteObsoleteWAL;
|
||||
|
||||
if (purge_wal_files_last_run_ + time_to_check > now_seconds) {
|
||||
return;
|
||||
}
|
||||
|
||||
purge_wal_files_last_run_ = now_seconds;
|
||||
|
||||
std::string archival_dir = ArchivalDirectory(db_options_.wal_dir);
|
||||
std::vector<std::string> files;
|
||||
s = env_->GetChildren(archival_dir, &files);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"Can't get archive files: %s", s.ToString().c_str());
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t log_files_num = 0;
|
||||
uint64_t log_file_size = 0;
|
||||
|
||||
for (auto& f : files) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(f, &number, &type) && type == kLogFile) {
|
||||
std::string const file_path = archival_dir + "/" + f;
|
||||
if (ttl_enabled) {
|
||||
uint64_t file_m_time;
|
||||
s = env_->GetFileModificationTime(file_path, &file_m_time);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"Can't get file mod time: %s: %s",
|
||||
file_path.c_str(), s.ToString().c_str());
|
||||
continue;
|
||||
}
|
||||
if (now_seconds - file_m_time > db_options_.WAL_ttl_seconds) {
|
||||
s = env_->DeleteFile(file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"Can't delete file: %s: %s",
|
||||
file_path.c_str(), s.ToString().c_str());
|
||||
continue;
|
||||
} else {
|
||||
MutexLock l(&read_first_record_cache_mutex_);
|
||||
read_first_record_cache_.erase(number);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (size_limit_enabled) {
|
||||
uint64_t file_size;
|
||||
s = env_->GetFileSize(file_path, &file_size);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"Unable to get file size: %s: %s",
|
||||
file_path.c_str(), s.ToString().c_str());
|
||||
return;
|
||||
} else {
|
||||
if (file_size > 0) {
|
||||
log_file_size = std::max(log_file_size, file_size);
|
||||
++log_files_num;
|
||||
} else {
|
||||
s = env_->DeleteFile(file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"Unable to delete file: %s: %s",
|
||||
file_path.c_str(), s.ToString().c_str());
|
||||
continue;
|
||||
} else {
|
||||
MutexLock l(&read_first_record_cache_mutex_);
|
||||
read_first_record_cache_.erase(number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == log_files_num || !size_limit_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t const files_keep_num =
|
||||
db_options_.WAL_size_limit_MB * 1024 * 1024 / log_file_size;
|
||||
if (log_files_num <= files_keep_num) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t files_del_num = log_files_num - files_keep_num;
|
||||
VectorLogPtr archived_logs;
|
||||
GetSortedWalsOfType(archival_dir, archived_logs, kArchivedLogFile);
|
||||
|
||||
if (files_del_num > archived_logs.size()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"Trying to delete more archived log files than "
|
||||
"exist. Deleting all");
|
||||
files_del_num = archived_logs.size();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < files_del_num; ++i) {
|
||||
std::string const file_path = archived_logs[i]->PathName();
|
||||
s = env_->DeleteFile(db_options_.wal_dir + "/" + file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"Unable to delete file: %s: %s", file_path.c_str(),
|
||||
s.ToString().c_str());
|
||||
continue;
|
||||
} else {
|
||||
MutexLock l(&read_first_record_cache_mutex_);
|
||||
read_first_record_cache_.erase(archived_logs[i]->LogNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WalManager::ArchiveWALFile(const std::string& fname, uint64_t number) {
|
||||
auto archived_log_name = ArchivedLogFileName(db_options_.wal_dir, number);
|
||||
// The sync point below is used in (DBTest,TransactionLogIteratorRace)
|
||||
TEST_SYNC_POINT("WalManager::PurgeObsoleteFiles:1");
|
||||
Status s = env_->RenameFile(fname, archived_log_name);
|
||||
// The sync point below is used in (DBTest,TransactionLogIteratorRace)
|
||||
TEST_SYNC_POINT("WalManager::PurgeObsoleteFiles:2");
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"Move log file %s to %s -- %s\n", fname.c_str(),
|
||||
archived_log_name.c_str(), s.ToString().c_str());
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct CompareLogByPointer {
|
||||
bool operator()(const std::unique_ptr<LogFile>& a,
|
||||
const std::unique_ptr<LogFile>& b) {
|
||||
LogFileImpl* a_impl = dynamic_cast<LogFileImpl*>(a.get());
|
||||
LogFileImpl* b_impl = dynamic_cast<LogFileImpl*>(b.get());
|
||||
return *a_impl < *b_impl;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Status WalManager::GetSortedWalsOfType(const std::string& path,
|
||||
VectorLogPtr& log_files,
|
||||
WalFileType log_type) {
|
||||
std::vector<std::string> all_files;
|
||||
const Status status = env_->GetChildren(path, &all_files);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
log_files.reserve(all_files.size());
|
||||
for (const auto& f : all_files) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(f, &number, &type) && type == kLogFile) {
|
||||
SequenceNumber sequence;
|
||||
Status s = ReadFirstRecord(log_type, number, &sequence);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (sequence == 0) {
|
||||
// empty file
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reproduce the race condition where a log file is moved
|
||||
// to archived dir, between these two sync points, used in
|
||||
// (DBTest,TransactionLogIteratorRace)
|
||||
TEST_SYNC_POINT("WalManager::GetSortedWalsOfType:1");
|
||||
TEST_SYNC_POINT("WalManager::GetSortedWalsOfType:2");
|
||||
|
||||
uint64_t size_bytes;
|
||||
s = env_->GetFileSize(LogFileName(path, number), &size_bytes);
|
||||
// re-try in case the alive log file has been moved to archive.
|
||||
if (!s.ok() && log_type == kAliveLogFile &&
|
||||
env_->FileExists(ArchivedLogFileName(path, number))) {
|
||||
s = env_->GetFileSize(ArchivedLogFileName(path, number), &size_bytes);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
log_files.push_back(std::move(std::unique_ptr<LogFile>(
|
||||
new LogFileImpl(number, log_type, sequence, size_bytes))));
|
||||
}
|
||||
}
|
||||
CompareLogByPointer compare_log_files;
|
||||
std::sort(log_files.begin(), log_files.end(), compare_log_files);
|
||||
return status;
|
||||
}
|
||||
|
||||
Status WalManager::RetainProbableWalFiles(VectorLogPtr& all_logs,
|
||||
const SequenceNumber target) {
|
||||
int64_t start = 0; // signed to avoid overflow when target is < first file.
|
||||
int64_t end = static_cast<int64_t>(all_logs.size()) - 1;
|
||||
// Binary Search. avoid opening all files.
|
||||
while (end >= start) {
|
||||
int64_t mid = start + (end - start) / 2; // Avoid overflow.
|
||||
SequenceNumber current_seq_num = all_logs.at(mid)->StartSequence();
|
||||
if (current_seq_num == target) {
|
||||
end = mid;
|
||||
break;
|
||||
} else if (current_seq_num < target) {
|
||||
start = mid + 1;
|
||||
} else {
|
||||
end = mid - 1;
|
||||
}
|
||||
}
|
||||
// end could be -ve.
|
||||
size_t start_index = std::max(static_cast<int64_t>(0), end);
|
||||
// The last wal file is always included
|
||||
all_logs.erase(all_logs.begin(), all_logs.begin() + start_index);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status WalManager::ReadFirstRecord(const WalFileType type,
|
||||
const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
if (type != kAliveLogFile && type != kArchivedLogFile) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"[WalManger] Unknown file type %s", std::to_string(type).c_str());
|
||||
return Status::NotSupported(
|
||||
"File Type Not Known " + std::to_string(type));
|
||||
}
|
||||
{
|
||||
MutexLock l(&read_first_record_cache_mutex_);
|
||||
auto itr = read_first_record_cache_.find(number);
|
||||
if (itr != read_first_record_cache_.end()) {
|
||||
*sequence = itr->second;
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
Status s;
|
||||
if (type == kAliveLogFile) {
|
||||
std::string fname = LogFileName(db_options_.wal_dir, number);
|
||||
s = ReadFirstLine(fname, sequence);
|
||||
if (env_->FileExists(fname) && !s.ok()) {
|
||||
// return any error that is not caused by non-existing file
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == kArchivedLogFile || !s.ok()) {
|
||||
// check if the file got moved to archive.
|
||||
std::string archived_file =
|
||||
ArchivedLogFileName(db_options_.wal_dir, number);
|
||||
s = ReadFirstLine(archived_file, sequence);
|
||||
}
|
||||
|
||||
if (s.ok() && *sequence != 0) {
|
||||
MutexLock l(&read_first_record_cache_mutex_);
|
||||
read_first_record_cache_.insert({number, *sequence});
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// the function returns status.ok() and sequence == 0 if the file exists, but is
|
||||
// empty
|
||||
Status WalManager::ReadFirstLine(const std::string& fname,
|
||||
SequenceNumber* sequence) {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
const char* fname;
|
||||
|
||||
Status* status;
|
||||
bool ignore_error; // true if db_options_.paranoid_checks==false
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, info_log,
|
||||
"[WalManager] %s%s: dropping %d bytes; %s",
|
||||
(this->ignore_error ? "(ignoring error) " : ""), fname,
|
||||
static_cast<int>(bytes), s.ToString().c_str());
|
||||
if (this->status->ok()) {
|
||||
// only keep the first error
|
||||
*this->status = s;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<SequentialFile> file;
|
||||
Status status = env_->NewSequentialFile(fname, &file, env_options_);
|
||||
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
LogReporter reporter;
|
||||
reporter.env = env_;
|
||||
reporter.info_log = db_options_.info_log.get();
|
||||
reporter.fname = fname.c_str();
|
||||
reporter.status = &status;
|
||||
reporter.ignore_error = !db_options_.paranoid_checks;
|
||||
log::Reader reader(std::move(file), &reporter, true /*checksum*/,
|
||||
0 /*initial_offset*/);
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
|
||||
if (reader.ReadRecord(&record, &scratch) &&
|
||||
(status.ok() || !db_options_.paranoid_checks)) {
|
||||
if (record.size() < 12) {
|
||||
reporter.Corruption(record.size(),
|
||||
Status::Corruption("log record too small"));
|
||||
// TODO read record's till the first no corrupt entry?
|
||||
} else {
|
||||
WriteBatch batch;
|
||||
WriteBatchInternal::SetContents(&batch, record);
|
||||
*sequence = WriteBatchInternal::Sequence(&batch);
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
// ReadRecord returns false on EOF, which means that the log file is empty. we
|
||||
// return status.ok() in that case and set sequence number to 0
|
||||
*sequence = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,95 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "port/port.h"
|
||||
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
#include "db/version_set.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class WalManager {
|
||||
public:
|
||||
WalManager(const DBOptions& db_options, const EnvOptions& env_options)
|
||||
: db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
env_(db_options.env),
|
||||
purge_wal_files_last_run_(0) {}
|
||||
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& files);
|
||||
|
||||
virtual Status GetUpdatesSince(
|
||||
SequenceNumber seq_number, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options,
|
||||
VersionSet* version_set);
|
||||
|
||||
void PurgeObsoleteWALFiles();
|
||||
|
||||
void ArchiveWALFile(const std::string& fname, uint64_t number);
|
||||
|
||||
Status TEST_ReadFirstRecord(const WalFileType type, const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
return ReadFirstRecord(type, number, sequence);
|
||||
}
|
||||
|
||||
Status TEST_ReadFirstLine(const std::string& fname,
|
||||
SequenceNumber* sequence) {
|
||||
return ReadFirstLine(fname, sequence);
|
||||
}
|
||||
|
||||
private:
|
||||
Status GetSortedWalsOfType(const std::string& path, VectorLogPtr& log_files,
|
||||
WalFileType type);
|
||||
// Requires: all_logs should be sorted with earliest log file first
|
||||
// Retains all log files in all_logs which contain updates with seq no.
|
||||
// Greater Than or Equal to the requested SequenceNumber.
|
||||
Status RetainProbableWalFiles(VectorLogPtr& all_logs,
|
||||
const SequenceNumber target);
|
||||
|
||||
Status ReadFirstRecord(const WalFileType type, const uint64_t number,
|
||||
SequenceNumber* sequence);
|
||||
|
||||
Status ReadFirstLine(const std::string& fname, SequenceNumber* sequence);
|
||||
|
||||
// ------- state from DBImpl ------
|
||||
const DBOptions& db_options_;
|
||||
const EnvOptions& env_options_;
|
||||
Env* env_;
|
||||
|
||||
// ------- WalManager state -------
|
||||
// cache for ReadFirstRecord() calls
|
||||
std::unordered_map<uint64_t, SequenceNumber> read_first_record_cache_;
|
||||
port::Mutex read_first_record_cache_mutex_;
|
||||
|
||||
// last time when PurgeObsoleteWALFiles ran.
|
||||
uint64_t purge_wal_files_last_run_;
|
||||
|
||||
// obsolete files will be deleted every this seconds if ttl deletion is
|
||||
// enabled and archive size_limit is disabled.
|
||||
static const uint64_t kDefaultIntervalToDeleteObsoleteWAL = 600;
|
||||
};
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,279 @@
|
||||
// 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 <map>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
|
||||
#include "db/wal_manager.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/version_set.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "db/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// TODO(icanadi) mock out VersionSet
|
||||
// TODO(icanadi) move other WalManager-specific tests from db_test here
|
||||
class WalManagerTest {
|
||||
public:
|
||||
WalManagerTest()
|
||||
: env_(Env::Default()),
|
||||
dbname_(test::TmpDir() + "/wal_manager_test"),
|
||||
table_cache_(NewLRUCache(50000, 16, 8)),
|
||||
current_log_number_(0) {
|
||||
DestroyDB(dbname_, Options());
|
||||
}
|
||||
|
||||
void Init() {
|
||||
ASSERT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
ASSERT_OK(env_->CreateDirIfMissing(ArchivalDirectory(dbname_)));
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
db_options_.wal_dir = dbname_;
|
||||
|
||||
versions_.reset(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_controller_));
|
||||
|
||||
wal_manager_.reset(new WalManager(db_options_, env_options_));
|
||||
}
|
||||
|
||||
void Reopen() {
|
||||
wal_manager_.reset(new WalManager(db_options_, env_options_));
|
||||
}
|
||||
|
||||
// NOT thread safe
|
||||
void Put(const std::string& key, const std::string& value) {
|
||||
assert(current_log_writer_.get() != nullptr);
|
||||
uint64_t seq = versions_->LastSequence() + 1;
|
||||
WriteBatch batch;
|
||||
batch.Put(key, value);
|
||||
WriteBatchInternal::SetSequence(&batch, seq);
|
||||
current_log_writer_->AddRecord(WriteBatchInternal::Contents(&batch));
|
||||
versions_->SetLastSequence(seq);
|
||||
}
|
||||
|
||||
// NOT thread safe
|
||||
void RollTheLog(bool archived) {
|
||||
current_log_number_++;
|
||||
std::string fname = ArchivedLogFileName(dbname_, current_log_number_);
|
||||
unique_ptr<WritableFile> file;
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &file, env_options_));
|
||||
current_log_writer_.reset(new log::Writer(std::move(file)));
|
||||
}
|
||||
|
||||
void CreateArchiveLogs(int num_logs, int entries_per_log) {
|
||||
for (int i = 1; i <= num_logs; ++i) {
|
||||
RollTheLog(true);
|
||||
for (int k = 0; k < entries_per_log; ++k) {
|
||||
Put(std::to_string(k), std::string(1024, 'a'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<TransactionLogIterator> OpenTransactionLogIter(
|
||||
const SequenceNumber seq) {
|
||||
unique_ptr<TransactionLogIterator> iter;
|
||||
Status status = wal_manager_->GetUpdatesSince(
|
||||
seq, &iter, TransactionLogIterator::ReadOptions(), versions_.get());
|
||||
ASSERT_OK(status);
|
||||
return std::move(iter);
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
std::string dbname_;
|
||||
WriteController write_controller_;
|
||||
EnvOptions env_options_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
DBOptions db_options_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
std::unique_ptr<WalManager> wal_manager_;
|
||||
|
||||
std::unique_ptr<log::Writer> current_log_writer_;
|
||||
uint64_t current_log_number_;
|
||||
};
|
||||
|
||||
TEST(WalManagerTest, ReadFirstRecordCache) {
|
||||
Init();
|
||||
std::string path = dbname_ + "/000001.log";
|
||||
unique_ptr<WritableFile> file;
|
||||
ASSERT_OK(env_->NewWritableFile(path, &file, EnvOptions()));
|
||||
|
||||
SequenceNumber s;
|
||||
ASSERT_OK(wal_manager_->TEST_ReadFirstLine(path, &s));
|
||||
ASSERT_EQ(s, 0U);
|
||||
|
||||
ASSERT_OK(wal_manager_->TEST_ReadFirstRecord(kAliveLogFile, 1, &s));
|
||||
ASSERT_EQ(s, 0U);
|
||||
|
||||
log::Writer writer(std::move(file));
|
||||
WriteBatch batch;
|
||||
batch.Put("foo", "bar");
|
||||
WriteBatchInternal::SetSequence(&batch, 10);
|
||||
writer.AddRecord(WriteBatchInternal::Contents(&batch));
|
||||
|
||||
// TODO(icanadi) move SpecialEnv outside of db_test, so we can reuse it here.
|
||||
// Waiting for lei to finish with db_test
|
||||
// env_->count_sequential_reads_ = true;
|
||||
// sequential_read_counter_ sanity test
|
||||
// ASSERT_EQ(env_->sequential_read_counter_.Read(), 0);
|
||||
|
||||
ASSERT_OK(wal_manager_->TEST_ReadFirstRecord(kAliveLogFile, 1, &s));
|
||||
ASSERT_EQ(s, 10U);
|
||||
// did a read
|
||||
// TODO(icanadi) move SpecialEnv outside of db_test, so we can reuse it here
|
||||
// ASSERT_EQ(env_->sequential_read_counter_.Read(), 1);
|
||||
|
||||
ASSERT_OK(wal_manager_->TEST_ReadFirstRecord(kAliveLogFile, 1, &s));
|
||||
ASSERT_EQ(s, 10U);
|
||||
// no new reads since the value is cached
|
||||
// TODO(icanadi) move SpecialEnv outside of db_test, so we can reuse it here
|
||||
// ASSERT_EQ(env_->sequential_read_counter_.Read(), 1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
uint64_t GetLogDirSize(std::string dir_path, Env* env) {
|
||||
uint64_t dir_size = 0;
|
||||
std::vector<std::string> files;
|
||||
env->GetChildren(dir_path, &files);
|
||||
for (auto& f : files) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(f, &number, &type) && type == kLogFile) {
|
||||
std::string const file_path = dir_path + "/" + f;
|
||||
uint64_t file_size;
|
||||
env->GetFileSize(file_path, &file_size);
|
||||
dir_size += file_size;
|
||||
}
|
||||
}
|
||||
return dir_size;
|
||||
}
|
||||
std::vector<std::uint64_t> ListSpecificFiles(
|
||||
Env* env, const std::string& path, const FileType expected_file_type) {
|
||||
std::vector<std::string> files;
|
||||
std::vector<uint64_t> file_numbers;
|
||||
env->GetChildren(path, &files);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
if (ParseFileName(files[i], &number, &type)) {
|
||||
if (type == expected_file_type) {
|
||||
file_numbers.push_back(number);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::move(file_numbers);
|
||||
}
|
||||
|
||||
int CountRecords(TransactionLogIterator* iter) {
|
||||
int count = 0;
|
||||
SequenceNumber lastSequence = 0;
|
||||
BatchResult res;
|
||||
while (iter->Valid()) {
|
||||
res = iter->GetBatch();
|
||||
ASSERT_TRUE(res.sequence > lastSequence);
|
||||
++count;
|
||||
lastSequence = res.sequence;
|
||||
ASSERT_OK(iter->status());
|
||||
iter->Next();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(WalManagerTest, WALArchivalSizeLimit) {
|
||||
db_options_.WAL_ttl_seconds = 0;
|
||||
db_options_.WAL_size_limit_MB = 1000;
|
||||
Init();
|
||||
|
||||
// TEST : Create WalManager with huge size limit and no ttl.
|
||||
// Create some archived files and call PurgeObsoleteWALFiles().
|
||||
// Count the archived log files that survived.
|
||||
// Assert that all of them did.
|
||||
// Change size limit. Re-open WalManager.
|
||||
// Assert that archive is not greater than WAL_size_limit_MB after
|
||||
// PurgeObsoleteWALFiles()
|
||||
// Set ttl and time_to_check_ to small values. Re-open db.
|
||||
// Assert that there are no archived logs left.
|
||||
|
||||
std::string archive_dir = ArchivalDirectory(dbname_);
|
||||
CreateArchiveLogs(20, 5000);
|
||||
|
||||
std::vector<std::uint64_t> log_files =
|
||||
ListSpecificFiles(env_, 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_);
|
||||
ASSERT_TRUE(archive_size <= db_options_.WAL_size_limit_MB * 1024 * 1024);
|
||||
|
||||
db_options_.WAL_ttl_seconds = 1;
|
||||
env_->SleepForMicroseconds(2 * 1000 * 1000);
|
||||
Reopen();
|
||||
wal_manager_->PurgeObsoleteWALFiles();
|
||||
|
||||
log_files = ListSpecificFiles(env_, archive_dir, kLogFile);
|
||||
ASSERT_TRUE(log_files.empty());
|
||||
}
|
||||
|
||||
TEST(WalManagerTest, WALArchivalTtl) {
|
||||
db_options_.WAL_ttl_seconds = 1000;
|
||||
Init();
|
||||
|
||||
// TEST : Create WalManager with a ttl and no size limit.
|
||||
// Create some archived log files and call PurgeObsoleteWALFiles().
|
||||
// Assert that files are not deleted
|
||||
// Reopen db with small ttl.
|
||||
// Assert that all archived logs was removed.
|
||||
|
||||
std::string archive_dir = ArchivalDirectory(dbname_);
|
||||
CreateArchiveLogs(20, 5000);
|
||||
|
||||
std::vector<uint64_t> log_files =
|
||||
ListSpecificFiles(env_, archive_dir, kLogFile);
|
||||
ASSERT_GT(log_files.size(), 0U);
|
||||
|
||||
db_options_.WAL_ttl_seconds = 1;
|
||||
env_->SleepForMicroseconds(3 * 1000 * 1000);
|
||||
Reopen();
|
||||
wal_manager_->PurgeObsoleteWALFiles();
|
||||
|
||||
log_files = ListSpecificFiles(env_, archive_dir, kLogFile);
|
||||
ASSERT_TRUE(log_files.empty());
|
||||
}
|
||||
|
||||
TEST(WalManagerTest, TransactionLogIteratorMoveOverZeroFiles) {
|
||||
Init();
|
||||
RollTheLog(false);
|
||||
Put("key1", std::string(1024, 'a'));
|
||||
// Create a zero record WAL file.
|
||||
RollTheLog(false);
|
||||
RollTheLog(false);
|
||||
|
||||
Put("key2", std::string(1024, 'a'));
|
||||
|
||||
auto iter = OpenTransactionLogIter(0);
|
||||
ASSERT_EQ(2, CountRecords(iter.get()));
|
||||
}
|
||||
|
||||
TEST(WalManagerTest, TransactionLogIteratorJustEmptyFile) {
|
||||
Init();
|
||||
RollTheLog(false);
|
||||
auto iter = OpenTransactionLogIter(0);
|
||||
// Check that an empty iterator is returned
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
+7
-10
@@ -349,13 +349,12 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return seek_status;
|
||||
}
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
auto* ioptions = mem->GetImmutableOptions();
|
||||
auto* moptions = mem->GetMemTableOptions();
|
||||
if (!moptions->inplace_update_support) {
|
||||
mem->Add(sequence_, kTypeValue, key, value);
|
||||
} else if (moptions->inplace_callback == nullptr) {
|
||||
mem->Update(sequence_, key, value);
|
||||
RecordTick(ioptions->statistics, NUMBER_KEYS_UPDATED);
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_UPDATED);
|
||||
} else {
|
||||
if (mem->UpdateCallback(sequence_, key, value)) {
|
||||
} else {
|
||||
@@ -382,11 +381,11 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
if (status == UpdateStatus::UPDATED_INPLACE) {
|
||||
// prev_value is updated in-place with final value.
|
||||
mem->Add(sequence_, kTypeValue, key, Slice(prev_buffer, prev_size));
|
||||
RecordTick(ioptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
} else if (status == UpdateStatus::UPDATED) {
|
||||
// merged_value contains the final value.
|
||||
mem->Add(sequence_, kTypeValue, key, Slice(merged_value));
|
||||
RecordTick(ioptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,7 +405,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return seek_status;
|
||||
}
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
auto* ioptions = mem->GetImmutableOptions();
|
||||
auto* moptions = mem->GetMemTableOptions();
|
||||
bool perform_merge = false;
|
||||
|
||||
@@ -441,16 +439,16 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
Slice get_value_slice = Slice(get_value);
|
||||
|
||||
// 2) Apply this merge
|
||||
auto merge_operator = ioptions->merge_operator;
|
||||
auto merge_operator = moptions->merge_operator;
|
||||
assert(merge_operator);
|
||||
|
||||
std::deque<std::string> operands;
|
||||
operands.push_front(value.ToString());
|
||||
std::string new_value;
|
||||
if (!merge_operator->FullMerge(key, &get_value_slice, operands,
|
||||
&new_value, ioptions->info_log)) {
|
||||
&new_value, moptions->info_log)) {
|
||||
// Failed to merge!
|
||||
RecordTick(ioptions->statistics, NUMBER_MERGE_FAILURES);
|
||||
RecordTick(moptions->statistics, NUMBER_MERGE_FAILURES);
|
||||
|
||||
// Store the delta in memtable
|
||||
perform_merge = false;
|
||||
@@ -477,7 +475,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return seek_status;
|
||||
}
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
auto* ioptions = mem->GetImmutableOptions();
|
||||
auto* moptions = mem->GetMemTableOptions();
|
||||
if (!dont_filter_deletes_ && moptions->filter_deletes) {
|
||||
SnapshotImpl read_from_snapshot;
|
||||
@@ -490,7 +487,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
cf_handle = db_->DefaultColumnFamily();
|
||||
}
|
||||
if (!db_->KeyMayExist(ropts, cf_handle, key, &value)) {
|
||||
RecordTick(ioptions->statistics, NUMBER_FILTERED_DELETES);
|
||||
RecordTick(moptions->statistics, NUMBER_FILTERED_DELETES);
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ static std::string PrintContents(WriteBatch* b) {
|
||||
options.memtable_factory = factory;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
MemTable* mem = new MemTable(cmp, ioptions,
|
||||
MemTableOptions(MutableCFOptions(options, ioptions), options));
|
||||
MutableCFOptions(options, ioptions));
|
||||
mem->Ref();
|
||||
std::string state;
|
||||
ColumnFamilyMemTablesDefault cf_mems_default(mem, &options);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
include ../build_config.mk
|
||||
|
||||
.PHONY: main clean
|
||||
|
||||
all: simple_example column_families_example
|
||||
|
||||
simple_example: simple_example.cc
|
||||
@@ -7,3 +9,6 @@ simple_example: simple_example.cc
|
||||
|
||||
column_families_example: column_families_example.cc
|
||||
$(CXX) $(CXXFLAGS) $@.cc -o$@ ../librocksdb.a -I../include -O2 -std=c++11 $(PLATFORM_LDFLAGS) $(PLATFORM_CXXFLAGS) $(EXEC_LDFLAGS)
|
||||
|
||||
clean: simple_example column_families_example
|
||||
rm -rf ./simple_example ./column_families_example
|
||||
+5
-1
@@ -145,6 +145,10 @@ class HdfsEnv : public Env {
|
||||
posixEnv->SetBackgroundThreads(number, pri);
|
||||
}
|
||||
|
||||
virtual void IncBackgroundThreadsIfNeeded(int number, Priority pri) override {
|
||||
posixEnv->IncBackgroundThreadsIfNeeded(number, pri);
|
||||
}
|
||||
|
||||
virtual std::string TimeToString(uint64_t number) {
|
||||
return posixEnv->TimeToString(number);
|
||||
}
|
||||
@@ -319,7 +323,7 @@ class HdfsEnv : public Env {
|
||||
std::string* outputpath) {return notsup;}
|
||||
|
||||
virtual void SetBackgroundThreads(int number, Priority pri = LOW) {}
|
||||
|
||||
virtual void IncBackgroundThreadsIfNeeded(int number, Priority pri) {}
|
||||
virtual std::string TimeToString(uint64_t number) { return "";}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -351,15 +351,14 @@ class InMemoryEnv : public EnvWrapper {
|
||||
return Status::NotSupported("getFileMTime", "Not supported in MemEnv");
|
||||
}
|
||||
|
||||
virtual Status RenameFile(const std::string& src,
|
||||
const std::string& target) {
|
||||
virtual Status RenameFile(const std::string& src, const std::string& dest) {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(src) == file_map_.end()) {
|
||||
return Status::IOError(src, "File not found");
|
||||
}
|
||||
|
||||
DeleteFileInternal(target);
|
||||
file_map_[target] = file_map_[src];
|
||||
DeleteFileInternal(dest);
|
||||
file_map_[dest] = file_map_[src];
|
||||
file_map_.erase(src);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -195,6 +195,8 @@ class DB {
|
||||
}
|
||||
|
||||
// Apply the specified updates to the database.
|
||||
// If `updates` contains no update, WAL will still be synced if
|
||||
// options.sync=true.
|
||||
// Returns OK on success, non-OK on failure.
|
||||
// Note: consider setting options.sync = true.
|
||||
virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0;
|
||||
@@ -359,11 +361,11 @@ class DB {
|
||||
return CompactRange(DefaultColumnFamily(), begin, end, reduce_level,
|
||||
target_level, target_path_id);
|
||||
}
|
||||
virtual bool SetOptions(ColumnFamilyHandle* column_family,
|
||||
virtual Status SetOptions(ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& new_options) {
|
||||
return true;
|
||||
return Status::NotSupported("Not implemented");
|
||||
}
|
||||
virtual bool SetOptions(
|
||||
virtual Status SetOptions(
|
||||
const std::unordered_map<std::string, std::string>& new_options) {
|
||||
return SetOptions(DefaultColumnFamily(), new_options);
|
||||
}
|
||||
|
||||
@@ -272,6 +272,11 @@ class Env {
|
||||
// default number: 1
|
||||
virtual void SetBackgroundThreads(int number, Priority pri = LOW) = 0;
|
||||
|
||||
// Enlarge number of background worker threads of a specific thread pool
|
||||
// for this environment if it is smaller than specified. 'LOW' is the default
|
||||
// pool.
|
||||
virtual void IncBackgroundThreadsIfNeeded(int number, Priority pri) = 0;
|
||||
|
||||
// Lower IO priority for threads from the specified pool.
|
||||
virtual void LowerThreadPoolIOPriority(Priority pool = LOW) {}
|
||||
|
||||
@@ -782,6 +787,11 @@ class EnvWrapper : public Env {
|
||||
void SetBackgroundThreads(int num, Priority pri) {
|
||||
return target_->SetBackgroundThreads(num, pri);
|
||||
}
|
||||
|
||||
void IncBackgroundThreadsIfNeeded(int num, Priority pri) {
|
||||
return target_->IncBackgroundThreadsIfNeeded(num, pri);
|
||||
}
|
||||
|
||||
void LowerThreadPoolIOPriority(Priority pool = LOW) override {
|
||||
target_->LowerThreadPoolIOPriority(pool);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
@@ -36,6 +37,13 @@ struct ImmutableCFOptions {
|
||||
|
||||
CompactionFilterFactoryV2* compaction_filter_factory_v2;
|
||||
|
||||
bool inplace_update_support;
|
||||
|
||||
UpdateStatus (*inplace_callback)(char* existing_value,
|
||||
uint32_t* existing_value_size,
|
||||
Slice delta_value,
|
||||
std::string* merged_value);
|
||||
|
||||
Logger* info_log;
|
||||
|
||||
Statistics* statistics;
|
||||
|
||||
+62
-12
@@ -191,13 +191,18 @@ struct ColumnFamilyOptions {
|
||||
// the next time the database is opened.
|
||||
//
|
||||
// Default: 4MB
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t write_buffer_size;
|
||||
|
||||
// The maximum number of write buffers that are built up in memory.
|
||||
// The default and the minimum number is 2, so that when 1 write buffer
|
||||
// is being flushed to storage, new writes can continue to the other
|
||||
// write buffer.
|
||||
//
|
||||
// Default: 2
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int max_write_buffer_number;
|
||||
|
||||
// The minimum number of write buffers that will be merged together
|
||||
@@ -260,14 +265,20 @@ struct ColumnFamilyOptions {
|
||||
// level-0 compaction will not be triggered by number of files at all.
|
||||
//
|
||||
// Default: 4
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int level0_file_num_compaction_trigger;
|
||||
|
||||
// Soft limit on number of level-0 files. We start slowing down writes at this
|
||||
// point. A value <0 means that no writing slow down will be triggered by
|
||||
// number of files in level-0.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int level0_slowdown_writes_trigger;
|
||||
|
||||
// Maximum number of level-0 files. We stop writes at this point.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int level0_stop_writes_trigger;
|
||||
|
||||
// Maximum level to which a new compacted memtable is pushed if it
|
||||
@@ -276,6 +287,8 @@ struct ColumnFamilyOptions {
|
||||
// expensive manifest file operations. We do not push all the way to
|
||||
// the largest level since that can generate a lot of wasted disk
|
||||
// space if the same key space is being repeatedly overwritten.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int max_mem_compaction_level;
|
||||
|
||||
// Target file size for compaction.
|
||||
@@ -286,11 +299,15 @@ struct ColumnFamilyOptions {
|
||||
// target_file_size_multiplier is 10, then each file on level-1 will
|
||||
// be 2MB, and each file on level 2 will be 20MB,
|
||||
// and each file on level-3 will be 200MB.
|
||||
|
||||
// by default target_file_size_base is 2MB.
|
||||
//
|
||||
// Default: 2MB.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
uint64_t target_file_size_base;
|
||||
// by default target_file_size_multiplier is 1, which means
|
||||
// By default target_file_size_multiplier is 1, which means
|
||||
// by default files in different levels will have similar size.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int target_file_size_multiplier;
|
||||
|
||||
// Control maximum total data size for a level.
|
||||
@@ -301,22 +318,31 @@ struct ColumnFamilyOptions {
|
||||
// max_bytes_for_level_multiplier is 10, total data size for level-1
|
||||
// will be 20MB, total file size for level-2 will be 200MB,
|
||||
// and total file size for level-3 will be 2GB.
|
||||
|
||||
// by default 'max_bytes_for_level_base' is 10MB.
|
||||
//
|
||||
// Default: 10MB.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
uint64_t max_bytes_for_level_base;
|
||||
// by default 'max_bytes_for_level_base' is 10.
|
||||
// Default: 10.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int max_bytes_for_level_multiplier;
|
||||
|
||||
// Different max-size multipliers for different levels.
|
||||
// These are multiplied by max_bytes_for_level_multiplier to arrive
|
||||
// at the max-size of each level.
|
||||
//
|
||||
// Default: 1
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
std::vector<int> max_bytes_for_level_multiplier_additional;
|
||||
|
||||
// Maximum number of bytes in all compacted files. We avoid expanding
|
||||
// the lower level file set of a compaction if it would make the
|
||||
// total compaction cover more than
|
||||
// (expanded_compaction_factor * targetFileSizeLevel()) many bytes.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int expanded_compaction_factor;
|
||||
|
||||
// Maximum number of bytes in all source files to be compacted in a
|
||||
@@ -326,22 +352,32 @@ struct ColumnFamilyOptions {
|
||||
// (source_compaction_factor * targetFileSizeLevel()) many bytes.
|
||||
// Default:1, i.e. pick maxfilesize amount of data as the source of
|
||||
// a compaction.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int source_compaction_factor;
|
||||
|
||||
// Control maximum bytes of overlaps in grandparent (i.e., level+2) before we
|
||||
// stop building a single file in a level->level+1 compaction.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int max_grandparent_overlap_factor;
|
||||
|
||||
// Puts are delayed 0-1 ms when any level has a compaction score that exceeds
|
||||
// soft_rate_limit. This is ignored when == 0.0.
|
||||
// CONSTRAINT: soft_rate_limit <= hard_rate_limit. If this constraint does not
|
||||
// hold, RocksDB will set soft_rate_limit = hard_rate_limit
|
||||
//
|
||||
// Default: 0 (disabled)
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
double soft_rate_limit;
|
||||
|
||||
// Puts are delayed 1ms at a time when any level has a compaction score that
|
||||
// exceeds hard_rate_limit. This is ignored when <= 1.0.
|
||||
//
|
||||
// Default: 0 (disabled)
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
double hard_rate_limit;
|
||||
|
||||
// DEPRECATED -- this options is no longer used
|
||||
@@ -360,10 +396,14 @@ struct ColumnFamilyOptions {
|
||||
// conforms to the restrictions.
|
||||
//
|
||||
// Default: 0
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t arena_block_size;
|
||||
|
||||
// Disable automatic compactions. Manual compactions can still
|
||||
// be issued on this column family
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
bool disable_auto_compactions;
|
||||
|
||||
// Purge duplicate/deleted keys when a memtable is flushed to storage.
|
||||
@@ -388,14 +428,20 @@ struct ColumnFamilyOptions {
|
||||
// If KeyMayExist returns false, i.e. the key definitely does not exist, then
|
||||
// the delete is a noop. KeyMayExist only incurs in-memory look up.
|
||||
// This optimization avoids writing the delete to storage when appropriate.
|
||||
//
|
||||
// Default: false
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
bool filter_deletes;
|
||||
|
||||
// An iteration->Next() sequentially skips over keys with the same
|
||||
// user-key unless this option is set. This number specifies the number
|
||||
// of keys (with the same userkey) that will be sequentially
|
||||
// skipped before a reseek is issued.
|
||||
//
|
||||
// Default: 8
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
uint64_t max_sequential_skip_in_iterations;
|
||||
|
||||
// This is a factory that provides MemTableRep objects.
|
||||
@@ -444,6 +490,8 @@ struct ColumnFamilyOptions {
|
||||
|
||||
// Number of locks used for inplace update
|
||||
// Default: 10000, if inplace_update_support = true, else 0.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t inplace_update_num_locks;
|
||||
|
||||
// existing_value - pointer to previous value (from both memtable and sst).
|
||||
@@ -490,9 +538,13 @@ struct ColumnFamilyOptions {
|
||||
|
||||
// if prefix_extractor is set and bloom_bits is not 0, create prefix bloom
|
||||
// for memtable
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
uint32_t memtable_prefix_bloom_bits;
|
||||
|
||||
// number of hash probes per key
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
uint32_t memtable_prefix_bloom_probes;
|
||||
|
||||
// Page size for huge page TLB for bloom in memtable. If <=0, not allocate
|
||||
@@ -500,7 +552,8 @@ struct ColumnFamilyOptions {
|
||||
// Need to reserve huge pages for it to be allocated. For example:
|
||||
// sysctl -w vm.nr_hugepages=20
|
||||
// See linux doc Documentation/vm/hugetlbpage.txt
|
||||
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t memtable_prefix_bloom_huge_page_tlb_size;
|
||||
|
||||
// Control locality of bloom filter probes to improve cache miss rate.
|
||||
@@ -520,6 +573,8 @@ struct ColumnFamilyOptions {
|
||||
// operations in the memtable.
|
||||
//
|
||||
// Default: 0 (disabled)
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t max_successive_merges;
|
||||
|
||||
// The number of partial merge operands to accumulate before partial
|
||||
@@ -1012,11 +1067,6 @@ extern Options GetOptions(size_t total_write_buffer_limit,
|
||||
int write_amplification_threshold = 32,
|
||||
uint64_t target_db_size = 68719476736 /* 64GB */);
|
||||
|
||||
bool GetOptionsFromStrings(
|
||||
const Options& base_options,
|
||||
const std::unordered_map<std::string, std::string>& options_map,
|
||||
Options* new_options);
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // STORAGE_ROCKSDB_INCLUDE_OPTIONS_H_
|
||||
@@ -130,8 +130,8 @@ class Status {
|
||||
Code code_;
|
||||
const char* state_;
|
||||
|
||||
explicit Status(Code code) : code_(code), state_(nullptr) { }
|
||||
Status(Code code, const Slice& msg, const Slice& msg2);
|
||||
explicit Status(Code _code) : code_(_code), state_(nullptr) {}
|
||||
Status(Code _code, const Slice& msg, const Slice& msg2);
|
||||
static const char* CopyState(const char* s);
|
||||
};
|
||||
|
||||
|
||||
@@ -355,11 +355,13 @@ class TableFactory {
|
||||
WritableFile* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts) const = 0;
|
||||
|
||||
// Sanitizes the specified DB Options.
|
||||
// Sanitizes the specified DB Options and ColumnFamilyOptions.
|
||||
//
|
||||
// If the function cannot find a way to sanitize the input DB Options,
|
||||
// a non-ok Status will be returned.
|
||||
virtual Status SanitizeDBOptions(const DBOptions* db_opts) const = 0;
|
||||
virtual Status SanitizeOptions(
|
||||
const DBOptions& db_opts,
|
||||
const ColumnFamilyOptions& cf_opts) const = 0;
|
||||
|
||||
// Return a string that contains printable format of table configurations.
|
||||
// RocksDB prints configurations at DB Open().
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "rocksdb/db.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -203,6 +204,12 @@ class StackableDB : public DB {
|
||||
return db_->GetDbIdentity(identity);
|
||||
}
|
||||
|
||||
using DB::SetOptions;
|
||||
virtual Status SetOptions(
|
||||
const std::unordered_map<std::string, std::string>& new_options) override {
|
||||
return db_->SetOptions(new_options);
|
||||
}
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
virtual Status GetPropertiesOfAllTables(ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
@@ -19,7 +20,6 @@
|
||||
namespace rocksdb {
|
||||
|
||||
class ColumnFamilyHandle;
|
||||
struct SliceParts;
|
||||
class Comparator;
|
||||
|
||||
enum WriteType { kPutRecord, kMergeRecord, kDeleteRecord, kLogDataRecord };
|
||||
@@ -39,10 +39,16 @@ class WBWIIterator {
|
||||
|
||||
virtual bool Valid() const = 0;
|
||||
|
||||
virtual void SeekToFirst() = 0;
|
||||
|
||||
virtual void SeekToLast() = 0;
|
||||
|
||||
virtual void Seek(const Slice& key) = 0;
|
||||
|
||||
virtual void Next() = 0;
|
||||
|
||||
virtual void Prev() = 0;
|
||||
|
||||
virtual const WriteEntry& Entry() const = 0;
|
||||
|
||||
virtual Status status() const = 0;
|
||||
@@ -62,40 +68,44 @@ class WriteBatchWithIndex {
|
||||
// interface, or we can't find a column family from the column family handle
|
||||
// passed in, backup_index_comparator will be used for the column family.
|
||||
// reserved_bytes: reserved bytes in underlying WriteBatch
|
||||
// overwrite_key: if true, overwrite the key in the index when inserting
|
||||
// the same key as previously, so iterator will never
|
||||
// show two entries with the same key.
|
||||
explicit WriteBatchWithIndex(
|
||||
const Comparator* backup_index_comparator = BytewiseComparator(),
|
||||
size_t reserved_bytes = 0);
|
||||
size_t reserved_bytes = 0, bool overwrite_key = false);
|
||||
virtual ~WriteBatchWithIndex();
|
||||
|
||||
WriteBatch* GetWriteBatch();
|
||||
|
||||
virtual void Put(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value);
|
||||
void Put(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value);
|
||||
|
||||
virtual void Put(const Slice& key, const Slice& value);
|
||||
void Put(const Slice& key, const Slice& value);
|
||||
|
||||
virtual void Merge(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value);
|
||||
void Merge(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value);
|
||||
|
||||
virtual void Merge(const Slice& key, const Slice& value);
|
||||
void Merge(const Slice& key, const Slice& value);
|
||||
|
||||
virtual void PutLogData(const Slice& blob);
|
||||
void PutLogData(const Slice& blob);
|
||||
|
||||
virtual void Delete(ColumnFamilyHandle* column_family, const Slice& key);
|
||||
virtual void Delete(const Slice& key);
|
||||
|
||||
virtual void Delete(ColumnFamilyHandle* column_family, const SliceParts& key);
|
||||
|
||||
virtual void Delete(const SliceParts& key);
|
||||
void Delete(ColumnFamilyHandle* column_family, const Slice& key);
|
||||
void Delete(const Slice& key);
|
||||
|
||||
// Create an iterator of a column family. User can call iterator.Seek() to
|
||||
// search to the next entry of or after a key. Keys will be iterated in the
|
||||
// order given by index_comparator. For multiple updates on the same key,
|
||||
// each update will be returned as a separate entry, in the order of update
|
||||
// time.
|
||||
virtual WBWIIterator* NewIterator(ColumnFamilyHandle* column_family);
|
||||
WBWIIterator* NewIterator(ColumnFamilyHandle* column_family);
|
||||
// Create an iterator of the default column family.
|
||||
virtual WBWIIterator* NewIterator();
|
||||
WBWIIterator* NewIterator();
|
||||
|
||||
// Will create a new Iterator that will use WBWIIterator as a delta and
|
||||
// base_iterator as base
|
||||
Iterator* NewIteratorWithBase(ColumnFamilyHandle* column_family,
|
||||
Iterator* base_iterator);
|
||||
|
||||
private:
|
||||
struct Rep;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#define ROCKSDB_MAJOR 3
|
||||
#define ROCKSDB_MINOR 6
|
||||
#define ROCKSDB_PATCH 2
|
||||
#define ROCKSDB_MINOR 7
|
||||
#define ROCKSDB_PATCH 0
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 <unordered_map>
|
||||
#include <string>
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Take a map of option name and option value, apply them into the
|
||||
// base_options, and return the new options as a result
|
||||
bool GetColumnFamilyOptionsFromMap(
|
||||
const ColumnFamilyOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
ColumnFamilyOptions* new_options);
|
||||
|
||||
bool GetDBOptionsFromMap(
|
||||
const DBOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
DBOptions* new_options);
|
||||
|
||||
// Take a string representation of option names and values, apply them into the
|
||||
// base_options, and return the new options as a result. The string has the
|
||||
// following format:
|
||||
// "write_buffer_size=1024;max_write_buffer_number=2"
|
||||
bool GetColumnFamilyOptionsFromString(
|
||||
const ColumnFamilyOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
ColumnFamilyOptions* new_options);
|
||||
|
||||
bool GetDBOptionsFromString(
|
||||
const DBOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
DBOptions* new_options);
|
||||
|
||||
} // namespace rocksdb
|
||||
+16
-1
@@ -1,4 +1,4 @@
|
||||
NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB org.rocksdb.Options org.rocksdb.WriteBatch org.rocksdb.WriteBatchInternal org.rocksdb.WriteBatchTest org.rocksdb.WriteOptions org.rocksdb.BackupableDB org.rocksdb.BackupableDBOptions org.rocksdb.Statistics org.rocksdb.RocksIterator org.rocksdb.VectorMemTableConfig org.rocksdb.SkipListMemTableConfig org.rocksdb.HashLinkedListMemTableConfig org.rocksdb.HashSkipListMemTableConfig org.rocksdb.PlainTableConfig org.rocksdb.BlockBasedTableConfig org.rocksdb.ReadOptions org.rocksdb.Filter org.rocksdb.BloomFilter org.rocksdb.RestoreOptions org.rocksdb.RestoreBackupableDB org.rocksdb.RocksEnv org.rocksdb.GenericRateLimiterConfig
|
||||
NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB org.rocksdb.Options org.rocksdb.DBOptions org.rocksdb.WriteBatch org.rocksdb.WriteBatchInternal org.rocksdb.WriteBatchTest org.rocksdb.WriteOptions org.rocksdb.BackupableDB org.rocksdb.BackupableDBOptions org.rocksdb.Statistics org.rocksdb.RocksIterator org.rocksdb.VectorMemTableConfig org.rocksdb.SkipListMemTableConfig org.rocksdb.HashLinkedListMemTableConfig org.rocksdb.HashSkipListMemTableConfig org.rocksdb.PlainTableConfig org.rocksdb.BlockBasedTableConfig org.rocksdb.ReadOptions org.rocksdb.Filter org.rocksdb.BloomFilter org.rocksdb.ComparatorOptions org.rocksdb.AbstractComparator org.rocksdb.Comparator org.rocksdb.DirectComparator org.rocksdb.AbstractSlice org.rocksdb.Slice org.rocksdb.DirectSlice org.rocksdb.RestoreOptions org.rocksdb.RestoreBackupableDB org.rocksdb.RocksEnv org.rocksdb.GenericRateLimiterConfig org.rocksdb.ColumnFamilyHandle org.rocksdb.MergeOperator org.rocksdb.StringAppendOperator org.rocksdb.ComparatorOptions org.rocksdb.AbstractComparator org.rocksdb.Comparator org.rocksdb.DirectComparator org.rocksdb.AbstractSlice org.rocksdb.Slice org.rocksdb.DirectSlice
|
||||
|
||||
ROCKSDB_MAJOR = $(shell egrep "ROCKSDB_MAJOR.[0-9]" ../include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_MINOR = $(shell egrep "ROCKSDB_MINOR.[0-9]" ../include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
@@ -35,13 +35,28 @@ sample: java
|
||||
@rm -rf /tmp/rocksdbjni_not_found
|
||||
|
||||
test: java
|
||||
@rm -rf /tmp/rocksdbjni_*
|
||||
javac org/rocksdb/test/*.java
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.WriteBatchTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.BackupableDBTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.BlockBasedTableConfigTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.DBOptionsTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ColumnFamilyTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.FilterTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.KeyMayExistTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.MemTableTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.OptionsTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.PlainTableConfigTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ReadOnlyTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.MergeTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ReadOptionsTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.RocksIteratorTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.SnapshotTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.StatisticsCollectorTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ComparatorOptionsTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ComparatorTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.DirectComparatorTest
|
||||
@rm -rf /tmp/rocksdbjni_*
|
||||
|
||||
db_bench: java
|
||||
javac org/rocksdb/benchmark/*.java
|
||||
+42
-33
@@ -35,13 +35,18 @@ public class RocksDBSample {
|
||||
assert(db == null);
|
||||
}
|
||||
|
||||
options.setCreateIfMissing(true)
|
||||
.createStatistics()
|
||||
.setWriteBufferSize(8 * SizeUnit.KB)
|
||||
.setMaxWriteBufferNumber(3)
|
||||
.setMaxBackgroundCompactions(10)
|
||||
.setCompressionType(CompressionType.SNAPPY_COMPRESSION)
|
||||
.setCompactionStyle(CompactionStyle.UNIVERSAL);
|
||||
try {
|
||||
options.setCreateIfMissing(true)
|
||||
.createStatistics()
|
||||
.setWriteBufferSize(8 * SizeUnit.KB)
|
||||
.setMaxWriteBufferNumber(3)
|
||||
.setMaxBackgroundCompactions(10)
|
||||
.setCompressionType(CompressionType.SNAPPY_COMPRESSION)
|
||||
.setCompactionStyle(CompactionStyle.UNIVERSAL);
|
||||
} catch (RocksDBException e) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
Statistics stats = options.statisticsPtr();
|
||||
|
||||
assert(options.createIfMissing() == true);
|
||||
@@ -51,34 +56,38 @@ public class RocksDBSample {
|
||||
assert(options.compressionType() == CompressionType.SNAPPY_COMPRESSION);
|
||||
assert(options.compactionStyle() == CompactionStyle.UNIVERSAL);
|
||||
|
||||
assert(options.memTableFactoryName().equals("SkipListFactory"));
|
||||
options.setMemTableConfig(
|
||||
new HashSkipListMemTableConfig()
|
||||
.setHeight(4)
|
||||
.setBranchingFactor(4)
|
||||
.setBucketCount(2000000));
|
||||
assert(options.memTableFactoryName().equals("HashSkipListRepFactory"));
|
||||
try {
|
||||
assert(options.memTableFactoryName().equals("SkipListFactory"));
|
||||
options.setMemTableConfig(
|
||||
new HashSkipListMemTableConfig()
|
||||
.setHeight(4)
|
||||
.setBranchingFactor(4)
|
||||
.setBucketCount(2000000));
|
||||
assert(options.memTableFactoryName().equals("HashSkipListRepFactory"));
|
||||
|
||||
options.setMemTableConfig(
|
||||
new HashLinkedListMemTableConfig()
|
||||
.setBucketCount(100000));
|
||||
assert(options.memTableFactoryName().equals("HashLinkedListRepFactory"));
|
||||
options.setMemTableConfig(
|
||||
new HashLinkedListMemTableConfig()
|
||||
.setBucketCount(100000));
|
||||
assert(options.memTableFactoryName().equals("HashLinkedListRepFactory"));
|
||||
|
||||
options.setMemTableConfig(
|
||||
new VectorMemTableConfig().setReservedSize(10000));
|
||||
assert(options.memTableFactoryName().equals("VectorRepFactory"));
|
||||
options.setMemTableConfig(
|
||||
new VectorMemTableConfig().setReservedSize(10000));
|
||||
assert(options.memTableFactoryName().equals("VectorRepFactory"));
|
||||
|
||||
options.setMemTableConfig(new SkipListMemTableConfig());
|
||||
assert(options.memTableFactoryName().equals("SkipListFactory"));
|
||||
options.setMemTableConfig(new SkipListMemTableConfig());
|
||||
assert(options.memTableFactoryName().equals("SkipListFactory"));
|
||||
|
||||
options.setTableFormatConfig(new PlainTableConfig());
|
||||
// Plain-Table requires mmap read
|
||||
options.setAllowMmapReads(true);
|
||||
assert(options.tableFactoryName().equals("PlainTable"));
|
||||
|
||||
options.setRateLimiterConfig(new GenericRateLimiterConfig(10000000,
|
||||
10000, 10));
|
||||
options.setRateLimiterConfig(new GenericRateLimiterConfig(10000000));
|
||||
options.setTableFormatConfig(new PlainTableConfig());
|
||||
// Plain-Table requires mmap read
|
||||
options.setAllowMmapReads(true);
|
||||
assert(options.tableFactoryName().equals("PlainTable"));
|
||||
|
||||
options.setRateLimiterConfig(new GenericRateLimiterConfig(10000000,
|
||||
10000, 10));
|
||||
options.setRateLimiterConfig(new GenericRateLimiterConfig(10000000));
|
||||
} catch (RocksDBException e) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
Filter bloomFilter = new BloomFilter(10);
|
||||
BlockBasedTableConfig table_options = new BlockBasedTableConfig();
|
||||
@@ -91,7 +100,7 @@ public class RocksDBSample {
|
||||
.setHashIndexAllowCollision(false)
|
||||
.setBlockCacheCompressedSize(64 * SizeUnit.KB)
|
||||
.setBlockCacheCompressedNumShardBits(10);
|
||||
|
||||
|
||||
assert(table_options.blockCacheSize() == 64 * SizeUnit.KB);
|
||||
assert(table_options.cacheNumShardBits() == 6);
|
||||
assert(table_options.blockSizeDeviation() == 5);
|
||||
@@ -100,7 +109,7 @@ public class RocksDBSample {
|
||||
assert(table_options.hashIndexAllowCollision() == false);
|
||||
assert(table_options.blockCacheCompressedSize() == 64 * SizeUnit.KB);
|
||||
assert(table_options.blockCacheCompressedNumShardBits() == 10);
|
||||
|
||||
|
||||
options.setTableFormatConfig(table_options);
|
||||
assert(options.tableFactoryName().equals("BlockBasedTable"));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# install all required packages for rocksdb that are available through yum
|
||||
ARCH=$(uname -i)
|
||||
sudo yum -y install java-1.6.0-openjdk-devel.$ARCH zlib zlib-devel bzip2 bzip2-devel
|
||||
sudo yum -y install java-1.7.0-openjdk-devel.$ARCH zlib zlib-devel bzip2 bzip2-devel
|
||||
|
||||
# install gcc/g++ 4.7 via CERN (http://linux.web.cern.ch/linux/devtoolset/)
|
||||
sudo wget -O /etc/yum.repos.d/slc5-devtoolset.repo http://linuxsoft.cern.ch/cern/devtoolset/slc5-devtoolset.repo
|
||||
@@ -12,7 +12,7 @@ tar xvfz gflags-1.6.tar.gz; cd gflags-1.6; scl enable devtoolset-1.1 ./configure
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
|
||||
# set java home so we can build rocksdb jars
|
||||
export JAVA_HOME=/usr/lib/jvm/java-1.6.0
|
||||
export JAVA_HOME=/usr/lib/jvm/java-1.7.0
|
||||
|
||||
# build rocksdb
|
||||
cd /rocksdb
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// 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.
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* Comparators are used by RocksDB to determine
|
||||
* the ordering of keys.
|
||||
*
|
||||
* This class is package private, implementers
|
||||
* should extend either of the public abstract classes:
|
||||
* @see org.rocksdb.Comparator
|
||||
* @see org.rocksdb.DirectComparator
|
||||
*/
|
||||
public abstract class AbstractComparator<T extends AbstractSlice>
|
||||
extends RocksObject {
|
||||
|
||||
/**
|
||||
* The name of the comparator. Used to check for comparator
|
||||
* mismatches (i.e., a DB created with one comparator is
|
||||
* accessed using a different comparator).
|
||||
*
|
||||
* A new name should be used whenever
|
||||
* the comparator implementation changes in a way that will cause
|
||||
* the relative ordering of any two keys to change.
|
||||
*
|
||||
* Names starting with "rocksdb." are reserved and should not be used.
|
||||
*
|
||||
* @return The name of this comparator implementation
|
||||
*/
|
||||
public abstract String name();
|
||||
|
||||
/**
|
||||
* Three-way key comparison
|
||||
*
|
||||
* @param a Slice access to first key
|
||||
* @param b Slice access to second key
|
||||
*
|
||||
* @return Should return either:
|
||||
* 1) < 0 if "a" < "b"
|
||||
* 2) == 0 if "a" == "b"
|
||||
* 3) > 0 if "a" > "b"
|
||||
*/
|
||||
public abstract int compare(final T a, final T b);
|
||||
|
||||
/**
|
||||
* Used to reduce the space requirements
|
||||
* for internal data structures like index blocks.
|
||||
*
|
||||
* If start < limit, you may return a new start which is a
|
||||
* shorter string in [start, limit).
|
||||
*
|
||||
* Simple comparator implementations may return null if they
|
||||
* wish to use start unchanged. i.e., an implementation of
|
||||
* this method that does nothing is correct.
|
||||
*
|
||||
* @return a shorter start, or null
|
||||
*/
|
||||
public String findShortestSeparator(final String start, final T limit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to reduce the space requirements
|
||||
* for internal data structures like index blocks.
|
||||
*
|
||||
* You may return a new short key (key1) where
|
||||
* key1 ≥ key.
|
||||
*
|
||||
* Simple comparator implementations may return null if they
|
||||
* wish to leave the key unchanged. i.e., an implementation of
|
||||
* this method that does nothing is correct.
|
||||
*
|
||||
* @return a shorter key, or null
|
||||
*/
|
||||
public String findShortSuccessor(final String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes underlying C++ comparator pointer.
|
||||
*
|
||||
* Note that this function should be called only after all
|
||||
* RocksDB instances referencing the comparator are closed.
|
||||
* Otherwise an undefined behavior will occur.
|
||||
*/
|
||||
@Override protected void disposeInternal() {
|
||||
assert(isInitialized());
|
||||
disposeInternal(nativeHandle_);
|
||||
}
|
||||
|
||||
private native void disposeInternal(long handle);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
// 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.
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* Slices are used by RocksDB to provide
|
||||
* efficient access to keys and values.
|
||||
*
|
||||
* This class is package private, implementers
|
||||
* should extend either of the public abstract classes:
|
||||
* @see org.rocksdb.Slice
|
||||
* @see org.rocksdb.DirectSlice
|
||||
*
|
||||
* Regards the lifecycle of Java Slices in RocksDB:
|
||||
* At present when you configure a Comparator from Java, it creates an
|
||||
* instance of a C++ BaseComparatorJniCallback subclass and
|
||||
* passes that to RocksDB as the comparator. That subclass of
|
||||
* BaseComparatorJniCallback creates the Java
|
||||
* @see org.rocksdb.AbstractSlice subclass Objects. When you dispose
|
||||
* the Java @see org.rocksdb.AbstractComparator subclass, it disposes the
|
||||
* C++ BaseComparatorJniCallback subclass, which in turn destroys the
|
||||
* Java @see org.rocksdb.AbstractSlice subclass Objects.
|
||||
*/
|
||||
abstract class AbstractSlice<T> extends RocksObject {
|
||||
|
||||
/**
|
||||
* Returns the data of the slice.
|
||||
*
|
||||
* @return The slice data. Note, the type of access is
|
||||
* determined by the subclass
|
||||
* @see org.rocksdb.AbstractSlice#data0(long)
|
||||
*/
|
||||
public T data() {
|
||||
assert (isInitialized());
|
||||
return data0(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to the data is provided by the
|
||||
* subtype as it needs to handle the
|
||||
* generic typing.
|
||||
*
|
||||
* @param handle The address of the underlying
|
||||
* native object.
|
||||
*
|
||||
* @return Java typed access to the data.
|
||||
*/
|
||||
protected abstract T data0(long handle);
|
||||
|
||||
/**
|
||||
* Return the length (in bytes) of the data.
|
||||
*
|
||||
* @return The length in bytes.
|
||||
*/
|
||||
public int size() {
|
||||
assert (isInitialized());
|
||||
return size0(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the length of the
|
||||
* data is zero.
|
||||
*
|
||||
* @return true if there is no data, false otherwise.
|
||||
*/
|
||||
public boolean empty() {
|
||||
assert (isInitialized());
|
||||
return empty0(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the data
|
||||
*
|
||||
* @param hex When true, the representation
|
||||
* will be encoded in hexadecimal.
|
||||
*
|
||||
* @return The string representation of the data.
|
||||
*/
|
||||
public String toString(final boolean hex) {
|
||||
assert (isInitialized());
|
||||
return toString0(nativeHandle_, hex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Three-way key comparison
|
||||
*
|
||||
* @param other A slice to compare against
|
||||
*
|
||||
* @return Should return either:
|
||||
* 1) < 0 if this < other
|
||||
* 2) == 0 if this == other
|
||||
* 3) > 0 if this > other
|
||||
*/
|
||||
public int compare(final AbstractSlice other) {
|
||||
assert (other != null);
|
||||
assert (isInitialized());
|
||||
return compare0(nativeHandle_, other.nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* If other is a slice object, then
|
||||
* we defer to {@link #compare(AbstractSlice) compare}
|
||||
* to check equality, otherwise we return false.
|
||||
*
|
||||
* @param other Object to test for equality
|
||||
*
|
||||
* @return true when {@code this.compare(other) == 0},
|
||||
* false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other != null && other instanceof AbstractSlice) {
|
||||
return compare((AbstractSlice)other) == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this slice starts with
|
||||
* another slice
|
||||
*
|
||||
* @param prefix Another slice which may of may not
|
||||
* be a prefix of this slice.
|
||||
*
|
||||
* @return true when this slice starts with the
|
||||
* {@code prefix} slice
|
||||
*/
|
||||
public boolean startsWith(final AbstractSlice prefix) {
|
||||
if (prefix != null) {
|
||||
assert (isInitialized());
|
||||
return startsWith0(nativeHandle_, prefix.nativeHandle_);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes underlying C++ slice pointer.
|
||||
* Note that this function should be called only after all
|
||||
* RocksDB instances referencing the slice are closed.
|
||||
* Otherwise an undefined behavior will occur.
|
||||
*/
|
||||
@Override
|
||||
protected void disposeInternal() {
|
||||
assert(isInitialized());
|
||||
disposeInternal(nativeHandle_);
|
||||
}
|
||||
|
||||
protected native void createNewSliceFromString(String str);
|
||||
private native int size0(long handle);
|
||||
private native boolean empty0(long handle);
|
||||
private native String toString0(long handle, boolean hex);
|
||||
private native int compare0(long handle, long otherHandle);
|
||||
private native boolean startsWith0(long handle, long otherHandle);
|
||||
private native void disposeInternal(long handle);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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.
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* Instances of this class describe a Backup made by
|
||||
* {@link org.rocksdb.BackupableDB}.
|
||||
*/
|
||||
public class BackupInfo {
|
||||
|
||||
/**
|
||||
* Package private constructor used to create instances
|
||||
* of BackupInfo by {@link org.rocksdb.BackupableDB} and
|
||||
* {@link org.rocksdb.RestoreBackupableDB}.
|
||||
*
|
||||
* @param backupId id of backup
|
||||
* @param timestamp timestamp of backup
|
||||
* @param size size of backup
|
||||
* @param numberFiles number of files related to this backup.
|
||||
*/
|
||||
BackupInfo(int backupId, long timestamp, long size,
|
||||
int numberFiles) {
|
||||
backupId_ = backupId;
|
||||
timestamp_ = timestamp;
|
||||
size_ = size;
|
||||
numberFiles_ = numberFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the backup id.
|
||||
*/
|
||||
public int backupId() {
|
||||
return backupId_;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the timestamp of the backup.
|
||||
*/
|
||||
public long timestamp() {
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the size of the backup
|
||||
*/
|
||||
public long size() {
|
||||
return size_;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the number of files of this backup.
|
||||
*/
|
||||
public int numberFiles() {
|
||||
return numberFiles_;
|
||||
}
|
||||
|
||||
private int backupId_;
|
||||
private long timestamp_;
|
||||
private long size_;
|
||||
private int numberFiles_;
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A subclass of RocksDB which supports backup-related operations.
|
||||
*
|
||||
@@ -43,8 +45,10 @@ public class BackupableDB extends RocksDB {
|
||||
*
|
||||
* @param flushBeforeBackup if true, then all data will be flushed
|
||||
* before creating backup.
|
||||
* @throws org.rocksdb.RocksDBException
|
||||
*/
|
||||
public void createNewBackup(boolean flushBeforeBackup) {
|
||||
public void createNewBackup(boolean flushBeforeBackup)
|
||||
throws RocksDBException {
|
||||
createNewBackup(nativeHandle_, flushBeforeBackup);
|
||||
}
|
||||
|
||||
@@ -52,11 +56,32 @@ public class BackupableDB extends RocksDB {
|
||||
* Deletes old backups, keeping latest numBackupsToKeep alive.
|
||||
*
|
||||
* @param numBackupsToKeep Number of latest backups to keep.
|
||||
* @throws org.rocksdb.RocksDBException
|
||||
*/
|
||||
public void purgeOldBackups(int numBackupsToKeep) {
|
||||
public void purgeOldBackups(int numBackupsToKeep)
|
||||
throws RocksDBException {
|
||||
purgeOldBackups(nativeHandle_, numBackupsToKeep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a specific backup.
|
||||
*
|
||||
* @param backupId of backup to delete.
|
||||
* @throws org.rocksdb.RocksDBException
|
||||
*/
|
||||
public void deleteBackup(int backupId) throws RocksDBException {
|
||||
deleteBackup0(nativeHandle_, backupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link BackupInfo} instances, which describe
|
||||
* already made backups.
|
||||
*
|
||||
* @return List of {@link BackupInfo} instances.
|
||||
*/
|
||||
public List<BackupInfo> getBackupInfos() {
|
||||
return getBackupInfo(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the BackupableDB instance and release resource.
|
||||
@@ -85,6 +110,11 @@ public class BackupableDB extends RocksDB {
|
||||
}
|
||||
|
||||
protected native void open(long rocksDBHandle, long backupDBOptionsHandle);
|
||||
protected native void createNewBackup(long handle, boolean flag);
|
||||
protected native void purgeOldBackups(long handle, int numBackupsToKeep);
|
||||
protected native void createNewBackup(long handle, boolean flag)
|
||||
throws RocksDBException;
|
||||
protected native void purgeOldBackups(long handle, int numBackupsToKeep)
|
||||
throws RocksDBException;
|
||||
private native void deleteBackup0(long nativeHandle, int backupId)
|
||||
throws RocksDBException;
|
||||
protected native List<BackupInfo> getBackupInfo(long handle);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ public class BlockBasedTableConfig extends TableFormatConfig {
|
||||
public BlockBasedTableConfig() {
|
||||
noBlockCache_ = false;
|
||||
blockCacheSize_ = 8 * 1024 * 1024;
|
||||
blockCacheNumShardBits_ = 0;
|
||||
blockSize_ = 4 * 1024;
|
||||
blockSizeDeviation_ = 10;
|
||||
blockRestartInterval_ = 16;
|
||||
@@ -22,6 +23,9 @@ public class BlockBasedTableConfig extends TableFormatConfig {
|
||||
cacheIndexAndFilterBlocks_ = false;
|
||||
hashIndexAllowCollision_ = true;
|
||||
blockCacheCompressedSize_ = 0;
|
||||
blockCacheCompressedNumShardBits_ = 0;
|
||||
checksumType_ = ChecksumType.kCRC32c;
|
||||
indexType_ = IndexType.kBinarySearch;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,6 +297,44 @@ public class BlockBasedTableConfig extends TableFormatConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the checksum type to be used with this table.
|
||||
*
|
||||
* @param checksumType {@link org.rocksdb.ChecksumType} value.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
public BlockBasedTableConfig setChecksumType(ChecksumType checksumType) {
|
||||
checksumType_ = checksumType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the currently set checksum type
|
||||
*/
|
||||
public ChecksumType checksumType() {
|
||||
return checksumType_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index type to used with this table.
|
||||
*
|
||||
* @param indexType {@link org.rocksdb.IndexType} value
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
public BlockBasedTableConfig setIndexType(IndexType indexType) {
|
||||
indexType_ = indexType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the currently set index type
|
||||
*/
|
||||
public IndexType indexType() {
|
||||
return indexType_;
|
||||
}
|
||||
|
||||
@Override protected long newTableFactoryHandle() {
|
||||
long filterHandle = 0;
|
||||
if (filter_ != null) {
|
||||
@@ -304,7 +346,8 @@ public class BlockBasedTableConfig extends TableFormatConfig {
|
||||
blockRestartInterval_, wholeKeyFiltering_,
|
||||
filterHandle, cacheIndexAndFilterBlocks_,
|
||||
hashIndexAllowCollision_, blockCacheCompressedSize_,
|
||||
blockCacheCompressedNumShardBits_);
|
||||
blockCacheCompressedNumShardBits_,
|
||||
checksumType_.getValue(), indexType_.getValue());
|
||||
}
|
||||
|
||||
private native long newTableFactoryHandle(
|
||||
@@ -312,19 +355,21 @@ public class BlockBasedTableConfig extends TableFormatConfig {
|
||||
long blockSize, int blockSizeDeviation, int blockRestartInterval,
|
||||
boolean wholeKeyFiltering, long filterPolicyHandle,
|
||||
boolean cacheIndexAndFilterBlocks, boolean hashIndexAllowCollision,
|
||||
long blockCacheCompressedSize, int blockCacheCompressedNumShardBits);
|
||||
long blockCacheCompressedSize, int blockCacheCompressedNumShardBits,
|
||||
byte checkSumType, byte indexType);
|
||||
|
||||
private boolean cacheIndexAndFilterBlocks_;
|
||||
private IndexType indexType_;
|
||||
private boolean hashIndexAllowCollision_;
|
||||
private ChecksumType checksumType_;
|
||||
private boolean noBlockCache_;
|
||||
private long blockSize_;
|
||||
private long blockCacheSize_;
|
||||
private int blockCacheNumShardBits_;
|
||||
private long shard;
|
||||
private long blockSize_;
|
||||
private int blockSizeDeviation_;
|
||||
private int blockRestartInterval_;
|
||||
private boolean wholeKeyFiltering_;
|
||||
private Filter filter_;
|
||||
private boolean cacheIndexAndFilterBlocks_;
|
||||
private boolean hashIndexAllowCollision_;
|
||||
private long blockCacheCompressedSize_;
|
||||
private int blockCacheCompressedNumShardBits_;
|
||||
private int blockSizeDeviation_;
|
||||
private int blockRestartInterval_;
|
||||
private Filter filter_;
|
||||
private boolean wholeKeyFiltering_;
|
||||
}
|
||||
@@ -6,32 +6,84 @@
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* This class creates a new filter policy that uses a bloom filter
|
||||
* with approximately the specified number of bits per key.
|
||||
* A good value for bitsPerKey is 10, which yields a filter
|
||||
* with ~ 1% false positive rate.
|
||||
* Bloom filter policy that uses a bloom filter with approximately
|
||||
* the specified number of bits per key.
|
||||
*
|
||||
* Default value of bits per key is 10.
|
||||
* <p>
|
||||
* Note: if you are using a custom comparator that ignores some parts
|
||||
* of the keys being compared, you must not use this {@code BloomFilter}
|
||||
* and must provide your own FilterPolicy that also ignores the
|
||||
* corresponding parts of the keys. For example, if the comparator
|
||||
* ignores trailing spaces, it would be incorrect to use a
|
||||
* FilterPolicy (like {@code BloomFilter}) that does not ignore
|
||||
* trailing spaces in keys.</p>
|
||||
*/
|
||||
public class BloomFilter extends Filter {
|
||||
private static final int DEFAULT_BITS_PER_KEY = 10;
|
||||
private final int bitsPerKey_;
|
||||
|
||||
private static final int DEFAULT_BITS_PER_KEY = 10;
|
||||
private static final boolean DEFAULT_MODE = true;
|
||||
private final int bitsPerKey_;
|
||||
private final boolean useBlockBasedMode_;
|
||||
|
||||
/**
|
||||
* BloomFilter constructor
|
||||
*
|
||||
* <p>
|
||||
* Callers must delete the result after any database that is using the
|
||||
* result has been closed.</p>
|
||||
*/
|
||||
public BloomFilter() {
|
||||
this(DEFAULT_BITS_PER_KEY);
|
||||
this(DEFAULT_BITS_PER_KEY, DEFAULT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* BloomFilter constructor
|
||||
*
|
||||
* <p>
|
||||
* bits_per_key: bits per key in bloom filter. A good value for bits_per_key
|
||||
* is 10, which yields a filter with ~ 1% false positive rate.
|
||||
* </p>
|
||||
* <p>
|
||||
* Callers must delete the result after any database that is using the
|
||||
* result has been closed.</p>
|
||||
*
|
||||
* @param bitsPerKey number of bits to use
|
||||
*/
|
||||
public BloomFilter(int bitsPerKey) {
|
||||
this(bitsPerKey, DEFAULT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* BloomFilter constructor
|
||||
*
|
||||
* <p>
|
||||
* bits_per_key: bits per key in bloom filter. A good value for bits_per_key
|
||||
* is 10, which yields a filter with ~ 1% false positive rate.
|
||||
* <p><strong>default bits_per_key</strong>: 10</p>
|
||||
*
|
||||
* <p>use_block_based_builder: use block based filter rather than full filter.
|
||||
* If you want to builder full filter, it needs to be set to false.
|
||||
* </p>
|
||||
* <p><strong>default mode: block based filter</strong></p>
|
||||
* <p>
|
||||
* Callers must delete the result after any database that is using the
|
||||
* result has been closed.</p>
|
||||
*
|
||||
* @param bitsPerKey number of bits to use
|
||||
* @param useBlockBasedMode use block based mode or full filter mode
|
||||
*/
|
||||
public BloomFilter(int bitsPerKey, boolean useBlockBasedMode) {
|
||||
super();
|
||||
bitsPerKey_ = bitsPerKey;
|
||||
|
||||
useBlockBasedMode_ = useBlockBasedMode;
|
||||
createNewFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createNewFilter() {
|
||||
createNewFilter0(bitsPerKey_);
|
||||
createNewBloomFilter(bitsPerKey_, useBlockBasedMode_);
|
||||
}
|
||||
|
||||
private native void createNewFilter0(int bitsKeyKey);
|
||||
private native void createNewBloomFilter(int bitsKeyKey,
|
||||
boolean useBlockBasedMode);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* Builtin RocksDB comparators
|
||||
*
|
||||
* <ol>
|
||||
* <li>BYTEWISE_COMPARATOR - Sorts all keys in ascending bytewise
|
||||
* order.</li>
|
||||
* <li>REVERSE_BYTEWISE_COMPARATOR - Sorts all keys in descending bytewise
|
||||
* order</li>
|
||||
* </ol>
|
||||
*/
|
||||
public enum BuiltinComparator {
|
||||
BYTEWISE_COMPARATOR, REVERSE_BYTEWISE_COMPARATOR
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* Checksum types used in conjunction with BlockBasedTable.
|
||||
*/
|
||||
public enum ChecksumType {
|
||||
/**
|
||||
* Not implemented yet.
|
||||
*/
|
||||
kNoChecksum((byte) 0),
|
||||
/**
|
||||
* CRC32 Checksum
|
||||
*/
|
||||
kCRC32c((byte) 1),
|
||||
/**
|
||||
* XX Hash
|
||||
*/
|
||||
kxxHash((byte) 2);
|
||||
|
||||
/**
|
||||
* Returns the byte value of the enumerations value
|
||||
*
|
||||
* @return byte representation
|
||||
*/
|
||||
public byte getValue() {
|
||||
return value_;
|
||||
}
|
||||
|
||||
private ChecksumType(byte value) {
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
private final byte value_;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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.
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* ColumnFamilyHandle class to hold handles to underlying rocksdb
|
||||
* ColumnFamily Pointers.
|
||||
*/
|
||||
public class ColumnFamilyHandle extends RocksObject {
|
||||
ColumnFamilyHandle(RocksDB rocksDB, long nativeHandle) {
|
||||
super();
|
||||
nativeHandle_ = nativeHandle;
|
||||
// rocksDB must point to a valid RocksDB instance;
|
||||
assert(rocksDB != null);
|
||||
// ColumnFamilyHandle must hold a reference to the related RocksDB instance
|
||||
// to guarantee that while a GC cycle starts ColumnFamilyHandle instances
|
||||
// are freed prior to RocksDB instances.
|
||||
rocksDB_ = rocksDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deletes underlying C++ iterator pointer.</p>
|
||||
*
|
||||
* <p>Note: the underlying handle can only be safely deleted if the RocksDB
|
||||
* instance related to a certain ColumnFamilyHandle is still valid and initialized.
|
||||
* Therefore {@code disposeInternal()} checks if the RocksDB is initialized
|
||||
* before freeing the native handle.</p>
|
||||
*/
|
||||
@Override protected void disposeInternal() {
|
||||
assert(isInitialized());
|
||||
if (rocksDB_.isInitialized()) {
|
||||
disposeInternal(nativeHandle_);
|
||||
}
|
||||
}
|
||||
|
||||
private native void disposeInternal(long handle);
|
||||
|
||||
private RocksDB rocksDB_;
|
||||
}
|
||||
@@ -0,0 +1,979 @@
|
||||
// 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.
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
public interface ColumnFamilyOptionsInterface {
|
||||
|
||||
/**
|
||||
* Use this if you don't need to keep the data sorted, i.e. you'll never use
|
||||
* an iterator, only Put() and Get() API calls
|
||||
*
|
||||
* @param blockCacheSizeMb Block cache size in MB
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
Object optimizeForPointLookup(long blockCacheSizeMb);
|
||||
|
||||
/**
|
||||
* <p>Default values for some parameters in ColumnFamilyOptions are not
|
||||
* optimized for heavy workloads and big datasets, which means you might
|
||||
* observe write stalls under some conditions. As a starting point for tuning
|
||||
* RocksDB options, use the following for level style compaction.</p>
|
||||
*
|
||||
* <p>Make sure to also call IncreaseParallelism(), which will provide the
|
||||
* biggest performance gains.</p>
|
||||
* <p>Note: we might use more memory than memtable_memory_budget during high
|
||||
* write rate period</p>
|
||||
*
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
Object optimizeLevelStyleCompaction();
|
||||
|
||||
/**
|
||||
* <p>Default values for some parameters in ColumnFamilyOptions are not
|
||||
* optimized for heavy workloads and big datasets, which means you might
|
||||
* observe write stalls under some conditions. As a starting point for tuning
|
||||
* RocksDB options, use the following for level style compaction.</p>
|
||||
*
|
||||
* <p>Make sure to also call IncreaseParallelism(), which will provide the
|
||||
* biggest performance gains.</p>
|
||||
* <p>Note: we might use more memory than memtable_memory_budget during high
|
||||
* write rate period</p>
|
||||
*
|
||||
* @param memtableMemoryBudget memory budget in bytes
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
Object optimizeLevelStyleCompaction(long memtableMemoryBudget);
|
||||
|
||||
/**
|
||||
* <p>Default values for some parameters in ColumnFamilyOptions are not
|
||||
* optimized for heavy workloads and big datasets, which means you might
|
||||
* observe write stalls under some conditions. As a starting point for tuning
|
||||
* RocksDB options, use the following for universal style compaction.</p>
|
||||
*
|
||||
* <p>Universal style compaction is focused on reducing Write Amplification
|
||||
* Factor for big data sets, but increases Space Amplification.</p>
|
||||
*
|
||||
* <p>Make sure to also call IncreaseParallelism(), which will provide the
|
||||
* biggest performance gains.</p>
|
||||
*
|
||||
* <p>Note: we might use more memory than memtable_memory_budget during high
|
||||
* write rate period</p>
|
||||
*
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
Object optimizeUniversalStyleCompaction();
|
||||
|
||||
/**
|
||||
* <p>Default values for some parameters in ColumnFamilyOptions are not
|
||||
* optimized for heavy workloads and big datasets, which means you might
|
||||
* observe write stalls under some conditions. As a starting point for tuning
|
||||
* RocksDB options, use the following for universal style compaction.</p>
|
||||
*
|
||||
* <p>Universal style compaction is focused on reducing Write Amplification
|
||||
* Factor for big data sets, but increases Space Amplification.</p>
|
||||
*
|
||||
* <p>Make sure to also call IncreaseParallelism(), which will provide the
|
||||
* biggest performance gains.</p>
|
||||
*
|
||||
* <p>Note: we might use more memory than memtable_memory_budget during high
|
||||
* write rate period</p>
|
||||
*
|
||||
* @param memtableMemoryBudget memory budget in bytes
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
Object optimizeUniversalStyleCompaction(long memtableMemoryBudget);
|
||||
|
||||
/**
|
||||
* Set {@link BuiltinComparator} to be used with RocksDB.
|
||||
*
|
||||
* Note: Comparator can be set once upon database creation.
|
||||
*
|
||||
* Default: BytewiseComparator.
|
||||
* @param builtinComparator a {@link BuiltinComparator} type.
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
Object setComparator(BuiltinComparator builtinComparator);
|
||||
|
||||
/**
|
||||
* Use the specified comparator for key ordering.
|
||||
*
|
||||
* Comparator should not be disposed before options instances using this comparator is
|
||||
* disposed. If dispose() function is not called, then comparator object will be
|
||||
* GC'd automatically.
|
||||
*
|
||||
* Comparator instance can be re-used in multiple options instances.
|
||||
*
|
||||
* @param comparator java instance.
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
Object setComparator(AbstractComparator comparator);
|
||||
|
||||
/**
|
||||
* <p>Set the merge operator to be used for merging two merge operands
|
||||
* of the same key. The merge function is invoked during
|
||||
* compaction and at lookup time, if multiple key/value pairs belonging
|
||||
* to the same key are found in the database.</p>
|
||||
*
|
||||
* @param name the name of the merge function, as defined by
|
||||
* the MergeOperators factory (see utilities/MergeOperators.h)
|
||||
* The merge function is specified by name and must be one of the
|
||||
* standard merge operators provided by RocksDB. The available
|
||||
* operators are "put", "uint64add", "stringappend" and "stringappendtest".
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
public Object setMergeOperatorName(String name);
|
||||
|
||||
/**
|
||||
* <p>Set the merge operator to be used for merging two different key/value
|
||||
* pairs that share the same key. The merge function is invoked during
|
||||
* compaction and at lookup time, if multiple key/value pairs belonging
|
||||
* to the same key are found in the database.</p>
|
||||
*
|
||||
* @param mergeOperator {@link MergeOperator} instance.
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
public Object setMergeOperator(MergeOperator mergeOperator);
|
||||
|
||||
/**
|
||||
* Amount of data to build up in memory (backed by an unsorted log
|
||||
* on disk) before converting to a sorted on-disk file.
|
||||
*
|
||||
* Larger values increase performance, especially during bulk loads.
|
||||
* Up to {@code max_write_buffer_number} write buffers may be held in memory
|
||||
* at the same time, so you may wish to adjust this parameter
|
||||
* to control memory usage.
|
||||
*
|
||||
* Also, a larger write buffer will result in a longer recovery time
|
||||
* the next time the database is opened.
|
||||
*
|
||||
* Default: 4MB
|
||||
* @param writeBufferSize the size of write buffer.
|
||||
* @return the instance of the current Object.
|
||||
* @throws org.rocksdb.RocksDBException
|
||||
*/
|
||||
Object setWriteBufferSize(long writeBufferSize)
|
||||
throws RocksDBException;
|
||||
|
||||
/**
|
||||
* Return size of write buffer size.
|
||||
*
|
||||
* @return size of write buffer.
|
||||
* @see #setWriteBufferSize(long)
|
||||
*/
|
||||
long writeBufferSize();
|
||||
|
||||
/**
|
||||
* The maximum number of write buffers that are built up in memory.
|
||||
* The default is 2, so that when 1 write buffer is being flushed to
|
||||
* storage, new writes can continue to the other write buffer.
|
||||
* Default: 2
|
||||
*
|
||||
* @param maxWriteBufferNumber maximum number of write buffers.
|
||||
* @return the instance of the current Object.
|
||||
*/
|
||||
Object setMaxWriteBufferNumber(
|
||||
int maxWriteBufferNumber);
|
||||
|
||||
/**
|
||||
* Returns maximum number of write buffers.
|
||||
*
|
||||
* @return maximum number of write buffers.
|
||||
* @see #setMaxWriteBufferNumber(int)
|
||||
*/
|
||||
int maxWriteBufferNumber();
|
||||
|
||||
/**
|
||||
* The minimum number of write buffers that will be merged together
|
||||
* before writing to storage. If set to 1, then
|
||||
* all write buffers are flushed to L0 as individual files and this increases
|
||||
* read amplification because a get request has to check in all of these
|
||||
* files. Also, an in-memory merge may result in writing lesser
|
||||
* data to storage if there are duplicate records in each of these
|
||||
* individual write buffers. Default: 1
|
||||
*
|
||||
* @param minWriteBufferNumberToMerge the minimum number of write buffers
|
||||
* that will be merged together.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setMinWriteBufferNumberToMerge(
|
||||
int minWriteBufferNumberToMerge);
|
||||
|
||||
/**
|
||||
* The minimum number of write buffers that will be merged together
|
||||
* before writing to storage. If set to 1, then
|
||||
* all write buffers are flushed to L0 as individual files and this increases
|
||||
* read amplification because a get request has to check in all of these
|
||||
* files. Also, an in-memory merge may result in writing lesser
|
||||
* data to storage if there are duplicate records in each of these
|
||||
* individual write buffers. Default: 1
|
||||
*
|
||||
* @return the minimum number of write buffers that will be merged together.
|
||||
*/
|
||||
int minWriteBufferNumberToMerge();
|
||||
|
||||
/**
|
||||
* This prefix-extractor uses the first n bytes of a key as its prefix.
|
||||
*
|
||||
* In some hash-based memtable representation such as HashLinkedList
|
||||
* and HashSkipList, prefixes are used to partition the keys into
|
||||
* several buckets. Prefix extractor is used to specify how to
|
||||
* extract the prefix given a key.
|
||||
*
|
||||
* @param n use the first n bytes of a key as its prefix.
|
||||
*/
|
||||
Object useFixedLengthPrefixExtractor(int n);
|
||||
|
||||
/**
|
||||
* Compress blocks using the specified compression algorithm. This
|
||||
* parameter can be changed dynamically.
|
||||
*
|
||||
* Default: SNAPPY_COMPRESSION, which gives lightweight but fast compression.
|
||||
*
|
||||
* @param compressionType Compression Type.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setCompressionType(CompressionType compressionType);
|
||||
|
||||
/**
|
||||
* Compress blocks using the specified compression algorithm. This
|
||||
* parameter can be changed dynamically.
|
||||
*
|
||||
* Default: SNAPPY_COMPRESSION, which gives lightweight but fast compression.
|
||||
*
|
||||
* @return Compression type.
|
||||
*/
|
||||
CompressionType compressionType();
|
||||
|
||||
/**
|
||||
* Set the number of levels for this database
|
||||
* If level-styled compaction is used, then this number determines
|
||||
* the total number of levels.
|
||||
*
|
||||
* @param numLevels the number of levels.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setNumLevels(int numLevels);
|
||||
|
||||
/**
|
||||
* If level-styled compaction is used, then this number determines
|
||||
* the total number of levels.
|
||||
*
|
||||
* @return the number of levels.
|
||||
*/
|
||||
int numLevels();
|
||||
|
||||
/**
|
||||
* Number of files to trigger level-0 compaction. A value < 0 means that
|
||||
* level-0 compaction will not be triggered by number of files at all.
|
||||
* Default: 4
|
||||
*
|
||||
* @param numFiles the number of files in level-0 to trigger compaction.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setLevelZeroFileNumCompactionTrigger(
|
||||
int numFiles);
|
||||
|
||||
/**
|
||||
* The number of files in level 0 to trigger compaction from level-0 to
|
||||
* level-1. A value < 0 means that level-0 compaction will not be
|
||||
* triggered by number of files at all.
|
||||
* Default: 4
|
||||
*
|
||||
* @return the number of files in level 0 to trigger compaction.
|
||||
*/
|
||||
int levelZeroFileNumCompactionTrigger();
|
||||
|
||||
/**
|
||||
* Soft limit on number of level-0 files. We start slowing down writes at this
|
||||
* point. A value < 0 means that no writing slow down will be triggered by
|
||||
* number of files in level-0.
|
||||
*
|
||||
* @param numFiles soft limit on number of level-0 files.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setLevelZeroSlowdownWritesTrigger(
|
||||
int numFiles);
|
||||
|
||||
/**
|
||||
* Soft limit on the number of level-0 files. We start slowing down writes
|
||||
* at this point. A value < 0 means that no writing slow down will be
|
||||
* triggered by number of files in level-0.
|
||||
*
|
||||
* @return the soft limit on the number of level-0 files.
|
||||
*/
|
||||
int levelZeroSlowdownWritesTrigger();
|
||||
|
||||
/**
|
||||
* Maximum number of level-0 files. We stop writes at this point.
|
||||
*
|
||||
* @param numFiles the hard limit of the number of level-0 files.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setLevelZeroStopWritesTrigger(int numFiles);
|
||||
|
||||
/**
|
||||
* Maximum number of level-0 files. We stop writes at this point.
|
||||
*
|
||||
* @return the hard limit of the number of level-0 file.
|
||||
*/
|
||||
int levelZeroStopWritesTrigger();
|
||||
|
||||
/**
|
||||
* The highest level to which a new compacted memtable is pushed if it
|
||||
* does not create overlap. We try to push to level 2 to avoid the
|
||||
* relatively expensive level 0≥1 compactions and to avoid some
|
||||
* expensive manifest file operations. We do not push all the way to
|
||||
* the largest level since that can generate a lot of wasted disk
|
||||
* space if the same key space is being repeatedly overwritten.
|
||||
*
|
||||
* @param maxMemCompactionLevel the highest level to which a new compacted
|
||||
* mem-table will be pushed.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setMaxMemCompactionLevel(
|
||||
int maxMemCompactionLevel);
|
||||
|
||||
/**
|
||||
* The highest level to which a new compacted memtable is pushed if it
|
||||
* does not create overlap. We try to push to level 2 to avoid the
|
||||
* relatively expensive level 0≥1 compactions and to avoid some
|
||||
* expensive manifest file operations. We do not push all the way to
|
||||
* the largest level since that can generate a lot of wasted disk
|
||||
* space if the same key space is being repeatedly overwritten.
|
||||
*
|
||||
* @return the highest level where a new compacted memtable will be pushed.
|
||||
*/
|
||||
int maxMemCompactionLevel();
|
||||
|
||||
/**
|
||||
* The target file size for compaction.
|
||||
* This targetFileSizeBase determines a level-1 file size.
|
||||
* Target file size for level L can be calculated by
|
||||
* targetFileSizeBase * (targetFileSizeMultiplier ^ (L-1))
|
||||
* For example, if targetFileSizeBase is 2MB and
|
||||
* target_file_size_multiplier is 10, then each file on level-1 will
|
||||
* be 2MB, and each file on level 2 will be 20MB,
|
||||
* and each file on level-3 will be 200MB.
|
||||
* by default targetFileSizeBase is 2MB.
|
||||
*
|
||||
* @param targetFileSizeBase the target size of a level-0 file.
|
||||
* @return the reference to the current option.
|
||||
*
|
||||
* @see #setTargetFileSizeMultiplier(int)
|
||||
*/
|
||||
Object setTargetFileSizeBase(long targetFileSizeBase);
|
||||
|
||||
/**
|
||||
* The target file size for compaction.
|
||||
* This targetFileSizeBase determines a level-1 file size.
|
||||
* Target file size for level L can be calculated by
|
||||
* targetFileSizeBase * (targetFileSizeMultiplier ^ (L-1))
|
||||
* For example, if targetFileSizeBase is 2MB and
|
||||
* target_file_size_multiplier is 10, then each file on level-1 will
|
||||
* be 2MB, and each file on level 2 will be 20MB,
|
||||
* and each file on level-3 will be 200MB.
|
||||
* by default targetFileSizeBase is 2MB.
|
||||
*
|
||||
* @return the target size of a level-0 file.
|
||||
*
|
||||
* @see #targetFileSizeMultiplier()
|
||||
*/
|
||||
long targetFileSizeBase();
|
||||
|
||||
/**
|
||||
* targetFileSizeMultiplier defines the size ratio between a
|
||||
* level-L file and level-(L+1) file.
|
||||
* By default target_file_size_multiplier is 1, meaning
|
||||
* files in different levels have the same target.
|
||||
*
|
||||
* @param multiplier the size ratio between a level-(L+1) file
|
||||
* and level-L file.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setTargetFileSizeMultiplier(int multiplier);
|
||||
|
||||
/**
|
||||
* targetFileSizeMultiplier defines the size ratio between a
|
||||
* level-(L+1) file and level-L file.
|
||||
* By default targetFileSizeMultiplier is 1, meaning
|
||||
* files in different levels have the same target.
|
||||
*
|
||||
* @return the size ratio between a level-(L+1) file and level-L file.
|
||||
*/
|
||||
int targetFileSizeMultiplier();
|
||||
|
||||
/**
|
||||
* The upper-bound of the total size of level-1 files in bytes.
|
||||
* Maximum number of bytes for level L can be calculated as
|
||||
* (maxBytesForLevelBase) * (maxBytesForLevelMultiplier ^ (L-1))
|
||||
* For example, if maxBytesForLevelBase is 20MB, and if
|
||||
* max_bytes_for_level_multiplier is 10, total data size for level-1
|
||||
* will be 20MB, total file size for level-2 will be 200MB,
|
||||
* and total file size for level-3 will be 2GB.
|
||||
* by default 'maxBytesForLevelBase' is 10MB.
|
||||
*
|
||||
* @return the reference to the current option.
|
||||
* @see #setMaxBytesForLevelMultiplier(int)
|
||||
*/
|
||||
Object setMaxBytesForLevelBase(
|
||||
long maxBytesForLevelBase);
|
||||
|
||||
/**
|
||||
* The upper-bound of the total size of level-1 files in bytes.
|
||||
* Maximum number of bytes for level L can be calculated as
|
||||
* (maxBytesForLevelBase) * (maxBytesForLevelMultiplier ^ (L-1))
|
||||
* For example, if maxBytesForLevelBase is 20MB, and if
|
||||
* max_bytes_for_level_multiplier is 10, total data size for level-1
|
||||
* will be 20MB, total file size for level-2 will be 200MB,
|
||||
* and total file size for level-3 will be 2GB.
|
||||
* by default 'maxBytesForLevelBase' is 10MB.
|
||||
*
|
||||
* @return the upper-bound of the total size of leve-1 files in bytes.
|
||||
* @see #maxBytesForLevelMultiplier()
|
||||
*/
|
||||
long maxBytesForLevelBase();
|
||||
|
||||
/**
|
||||
* The ratio between the total size of level-(L+1) files and the total
|
||||
* size of level-L files for all L.
|
||||
* DEFAULT: 10
|
||||
*
|
||||
* @param multiplier the ratio between the total size of level-(L+1)
|
||||
* files and the total size of level-L files for all L.
|
||||
* @return the reference to the current option.
|
||||
* @see #setMaxBytesForLevelBase(long)
|
||||
*/
|
||||
Object setMaxBytesForLevelMultiplier(int multiplier);
|
||||
|
||||
/**
|
||||
* The ratio between the total size of level-(L+1) files and the total
|
||||
* size of level-L files for all L.
|
||||
* DEFAULT: 10
|
||||
*
|
||||
* @return the ratio between the total size of level-(L+1) files and
|
||||
* the total size of level-L files for all L.
|
||||
* @see #maxBytesForLevelBase()
|
||||
*/
|
||||
int maxBytesForLevelMultiplier();
|
||||
|
||||
/**
|
||||
* Maximum number of bytes in all compacted files. We avoid expanding
|
||||
* the lower level file set of a compaction if it would make the
|
||||
* total compaction cover more than
|
||||
* (expanded_compaction_factor * targetFileSizeLevel()) many bytes.
|
||||
*
|
||||
* @param expandedCompactionFactor the maximum number of bytes in all
|
||||
* compacted files.
|
||||
* @return the reference to the current option.
|
||||
* @see #setSourceCompactionFactor(int)
|
||||
*/
|
||||
Object setExpandedCompactionFactor(int expandedCompactionFactor);
|
||||
|
||||
/**
|
||||
* Maximum number of bytes in all compacted files. We avoid expanding
|
||||
* the lower level file set of a compaction if it would make the
|
||||
* total compaction cover more than
|
||||
* (expanded_compaction_factor * targetFileSizeLevel()) many bytes.
|
||||
*
|
||||
* @return the maximum number of bytes in all compacted files.
|
||||
* @see #sourceCompactionFactor()
|
||||
*/
|
||||
int expandedCompactionFactor();
|
||||
|
||||
/**
|
||||
* Maximum number of bytes in all source files to be compacted in a
|
||||
* single compaction run. We avoid picking too many files in the
|
||||
* source level so that we do not exceed the total source bytes
|
||||
* for compaction to exceed
|
||||
* (source_compaction_factor * targetFileSizeLevel()) many bytes.
|
||||
* Default:1, i.e. pick maxfilesize amount of data as the source of
|
||||
* a compaction.
|
||||
*
|
||||
* @param sourceCompactionFactor the maximum number of bytes in all
|
||||
* source files to be compacted in a single compaction run.
|
||||
* @return the reference to the current option.
|
||||
* @see #setExpandedCompactionFactor(int)
|
||||
*/
|
||||
Object setSourceCompactionFactor(int sourceCompactionFactor);
|
||||
|
||||
/**
|
||||
* Maximum number of bytes in all source files to be compacted in a
|
||||
* single compaction run. We avoid picking too many files in the
|
||||
* source level so that we do not exceed the total source bytes
|
||||
* for compaction to exceed
|
||||
* (source_compaction_factor * targetFileSizeLevel()) many bytes.
|
||||
* Default:1, i.e. pick maxfilesize amount of data as the source of
|
||||
* a compaction.
|
||||
*
|
||||
* @return the maximum number of bytes in all source files to be compactedo.
|
||||
* @see #expandedCompactionFactor()
|
||||
*/
|
||||
int sourceCompactionFactor();
|
||||
|
||||
/**
|
||||
* Control maximum bytes of overlaps in grandparent (i.e., level+2) before we
|
||||
* stop building a single file in a level->level+1 compaction.
|
||||
*
|
||||
* @param maxGrandparentOverlapFactor maximum bytes of overlaps in
|
||||
* "grandparent" level.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setMaxGrandparentOverlapFactor(
|
||||
int maxGrandparentOverlapFactor);
|
||||
|
||||
/**
|
||||
* Control maximum bytes of overlaps in grandparent (i.e., level+2) before we
|
||||
* stop building a single file in a level->level+1 compaction.
|
||||
*
|
||||
* @return maximum bytes of overlaps in "grandparent" level.
|
||||
*/
|
||||
int maxGrandparentOverlapFactor();
|
||||
|
||||
/**
|
||||
* Puts are delayed 0-1 ms when any level has a compaction score that exceeds
|
||||
* soft_rate_limit. This is ignored when == 0.0.
|
||||
* CONSTRAINT: soft_rate_limit ≤ hard_rate_limit. If this constraint does not
|
||||
* hold, RocksDB will set soft_rate_limit = hard_rate_limit
|
||||
* Default: 0 (disabled)
|
||||
*
|
||||
* @param softRateLimit the soft-rate-limit of a compaction score
|
||||
* for put delay.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setSoftRateLimit(double softRateLimit);
|
||||
|
||||
/**
|
||||
* Puts are delayed 0-1 ms when any level has a compaction score that exceeds
|
||||
* soft_rate_limit. This is ignored when == 0.0.
|
||||
* CONSTRAINT: soft_rate_limit ≤ hard_rate_limit. If this constraint does not
|
||||
* hold, RocksDB will set soft_rate_limit = hard_rate_limit
|
||||
* Default: 0 (disabled)
|
||||
*
|
||||
* @return soft-rate-limit for put delay.
|
||||
*/
|
||||
double softRateLimit();
|
||||
|
||||
/**
|
||||
* Puts are delayed 1ms at a time when any level has a compaction score that
|
||||
* exceeds hard_rate_limit. This is ignored when ≤ 1.0.
|
||||
* Default: 0 (disabled)
|
||||
*
|
||||
* @param hardRateLimit the hard-rate-limit of a compaction score for put
|
||||
* delay.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setHardRateLimit(double hardRateLimit);
|
||||
|
||||
/**
|
||||
* Puts are delayed 1ms at a time when any level has a compaction score that
|
||||
* exceeds hard_rate_limit. This is ignored when ≤ 1.0.
|
||||
* Default: 0 (disabled)
|
||||
*
|
||||
* @return the hard-rate-limit of a compaction score for put delay.
|
||||
*/
|
||||
double hardRateLimit();
|
||||
|
||||
/**
|
||||
* The maximum time interval a put will be stalled when hard_rate_limit
|
||||
* is enforced. If 0, then there is no limit.
|
||||
* Default: 1000
|
||||
*
|
||||
* @param rateLimitDelayMaxMilliseconds the maximum time interval a put
|
||||
* will be stalled.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setRateLimitDelayMaxMilliseconds(
|
||||
int rateLimitDelayMaxMilliseconds);
|
||||
|
||||
/**
|
||||
* The maximum time interval a put will be stalled when hard_rate_limit
|
||||
* is enforced. If 0, then there is no limit.
|
||||
* Default: 1000
|
||||
*
|
||||
* @return the maximum time interval a put will be stalled when
|
||||
* hard_rate_limit is enforced.
|
||||
*/
|
||||
int rateLimitDelayMaxMilliseconds();
|
||||
|
||||
/**
|
||||
* The size of one block in arena memory allocation.
|
||||
* If ≤ 0, a proper value is automatically calculated (usually 1/10 of
|
||||
* writer_buffer_size).
|
||||
*
|
||||
* There are two additonal restriction of the The specified size:
|
||||
* (1) size should be in the range of [4096, 2 << 30] and
|
||||
* (2) be the multiple of the CPU word (which helps with the memory
|
||||
* alignment).
|
||||
*
|
||||
* We'll automatically check and adjust the size number to make sure it
|
||||
* conforms to the restrictions.
|
||||
* Default: 0
|
||||
*
|
||||
* @param arenaBlockSize the size of an arena block
|
||||
* @return the reference to the current option.
|
||||
* @throws org.rocksdb.RocksDBException
|
||||
*/
|
||||
Object setArenaBlockSize(long arenaBlockSize)
|
||||
throws RocksDBException;
|
||||
|
||||
/**
|
||||
* The size of one block in arena memory allocation.
|
||||
* If ≤ 0, a proper value is automatically calculated (usually 1/10 of
|
||||
* writer_buffer_size).
|
||||
*
|
||||
* There are two additonal restriction of the The specified size:
|
||||
* (1) size should be in the range of [4096, 2 << 30] and
|
||||
* (2) be the multiple of the CPU word (which helps with the memory
|
||||
* alignment).
|
||||
*
|
||||
* We'll automatically check and adjust the size number to make sure it
|
||||
* conforms to the restrictions.
|
||||
* Default: 0
|
||||
*
|
||||
* @return the size of an arena block
|
||||
*/
|
||||
long arenaBlockSize();
|
||||
|
||||
/**
|
||||
* Disable automatic compactions. Manual compactions can still
|
||||
* be issued on this column family
|
||||
*
|
||||
* @param disableAutoCompactions true if auto-compactions are disabled.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setDisableAutoCompactions(boolean disableAutoCompactions);
|
||||
|
||||
/**
|
||||
* Disable automatic compactions. Manual compactions can still
|
||||
* be issued on this column family
|
||||
*
|
||||
* @return true if auto-compactions are disabled.
|
||||
*/
|
||||
boolean disableAutoCompactions();
|
||||
|
||||
/**
|
||||
* Purge duplicate/deleted keys when a memtable is flushed to storage.
|
||||
* Default: true
|
||||
*
|
||||
* @param purgeRedundantKvsWhileFlush true if purging keys is disabled.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setPurgeRedundantKvsWhileFlush(
|
||||
boolean purgeRedundantKvsWhileFlush);
|
||||
|
||||
/**
|
||||
* Purge duplicate/deleted keys when a memtable is flushed to storage.
|
||||
* Default: true
|
||||
*
|
||||
* @return true if purging keys is disabled.
|
||||
*/
|
||||
boolean purgeRedundantKvsWhileFlush();
|
||||
|
||||
/**
|
||||
* Set compaction style for DB.
|
||||
*
|
||||
* Default: LEVEL.
|
||||
*
|
||||
* @param compactionStyle Compaction style.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setCompactionStyle(CompactionStyle compactionStyle);
|
||||
|
||||
/**
|
||||
* Compaction style for DB.
|
||||
*
|
||||
* @return Compaction style.
|
||||
*/
|
||||
CompactionStyle compactionStyle();
|
||||
|
||||
/**
|
||||
* If true, compaction will verify checksum on every read that happens
|
||||
* as part of compaction
|
||||
* Default: true
|
||||
*
|
||||
* @param verifyChecksumsInCompaction true if compaction verifies
|
||||
* checksum on every read.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setVerifyChecksumsInCompaction(
|
||||
boolean verifyChecksumsInCompaction);
|
||||
|
||||
/**
|
||||
* If true, compaction will verify checksum on every read that happens
|
||||
* as part of compaction
|
||||
* Default: true
|
||||
*
|
||||
* @return true if compaction verifies checksum on every read.
|
||||
*/
|
||||
boolean verifyChecksumsInCompaction();
|
||||
|
||||
/**
|
||||
* Use KeyMayExist API to filter deletes when this is true.
|
||||
* If KeyMayExist returns false, i.e. the key definitely does not exist, then
|
||||
* the delete is a noop. KeyMayExist only incurs in-memory look up.
|
||||
* This optimization avoids writing the delete to storage when appropriate.
|
||||
* Default: false
|
||||
*
|
||||
* @param filterDeletes true if filter-deletes behavior is on.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setFilterDeletes(boolean filterDeletes);
|
||||
|
||||
/**
|
||||
* Use KeyMayExist API to filter deletes when this is true.
|
||||
* If KeyMayExist returns false, i.e. the key definitely does not exist, then
|
||||
* the delete is a noop. KeyMayExist only incurs in-memory look up.
|
||||
* This optimization avoids writing the delete to storage when appropriate.
|
||||
* Default: false
|
||||
*
|
||||
* @return true if filter-deletes behavior is on.
|
||||
*/
|
||||
boolean filterDeletes();
|
||||
|
||||
/**
|
||||
* An iteration->Next() sequentially skips over keys with the same
|
||||
* user-key unless this option is set. This number specifies the number
|
||||
* of keys (with the same userkey) that will be sequentially
|
||||
* skipped before a reseek is issued.
|
||||
* Default: 8
|
||||
*
|
||||
* @param maxSequentialSkipInIterations the number of keys could
|
||||
* be skipped in a iteration.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setMaxSequentialSkipInIterations(long maxSequentialSkipInIterations);
|
||||
|
||||
/**
|
||||
* An iteration->Next() sequentially skips over keys with the same
|
||||
* user-key unless this option is set. This number specifies the number
|
||||
* of keys (with the same userkey) that will be sequentially
|
||||
* skipped before a reseek is issued.
|
||||
* Default: 8
|
||||
*
|
||||
* @return the number of keys could be skipped in a iteration.
|
||||
*/
|
||||
long maxSequentialSkipInIterations();
|
||||
|
||||
/**
|
||||
* Set the config for mem-table.
|
||||
*
|
||||
* @param config the mem-table config.
|
||||
* @return the instance of the current Object.
|
||||
* @throws org.rocksdb.RocksDBException
|
||||
*/
|
||||
Object setMemTableConfig(MemTableConfig config)
|
||||
throws RocksDBException;
|
||||
|
||||
/**
|
||||
* Returns the name of the current mem table representation.
|
||||
* Memtable format can be set using setTableFormatConfig.
|
||||
*
|
||||
* @return the name of the currently-used memtable factory.
|
||||
* @see #setTableFormatConfig(org.rocksdb.TableFormatConfig)
|
||||
*/
|
||||
String memTableFactoryName();
|
||||
|
||||
/**
|
||||
* Set the config for table format.
|
||||
*
|
||||
* @param config the table format config.
|
||||
* @return the reference of the current Options.
|
||||
*/
|
||||
Object setTableFormatConfig(TableFormatConfig config);
|
||||
|
||||
/**
|
||||
* @return the name of the currently used table factory.
|
||||
*/
|
||||
String tableFactoryName();
|
||||
|
||||
/**
|
||||
* Allows thread-safe inplace updates.
|
||||
* If inplace_callback function is not set,
|
||||
* Put(key, new_value) will update inplace the existing_value iff
|
||||
* * key exists in current memtable
|
||||
* * new sizeof(new_value) ≤ sizeof(existing_value)
|
||||
* * existing_value for that key is a put i.e. kTypeValue
|
||||
* If inplace_callback function is set, check doc for inplace_callback.
|
||||
* Default: false.
|
||||
*
|
||||
* @param inplaceUpdateSupport true if thread-safe inplace updates
|
||||
* are allowed.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setInplaceUpdateSupport(boolean inplaceUpdateSupport);
|
||||
|
||||
/**
|
||||
* Allows thread-safe inplace updates.
|
||||
* If inplace_callback function is not set,
|
||||
* Put(key, new_value) will update inplace the existing_value iff
|
||||
* * key exists in current memtable
|
||||
* * new sizeof(new_value) ≤ sizeof(existing_value)
|
||||
* * existing_value for that key is a put i.e. kTypeValue
|
||||
* If inplace_callback function is set, check doc for inplace_callback.
|
||||
* Default: false.
|
||||
*
|
||||
* @return true if thread-safe inplace updates are allowed.
|
||||
*/
|
||||
boolean inplaceUpdateSupport();
|
||||
|
||||
/**
|
||||
* Number of locks used for inplace update
|
||||
* Default: 10000, if inplace_update_support = true, else 0.
|
||||
*
|
||||
* @param inplaceUpdateNumLocks the number of locks used for
|
||||
* inplace updates.
|
||||
* @return the reference to the current option.
|
||||
* @throws org.rocksdb.RocksDBException
|
||||
*/
|
||||
Object setInplaceUpdateNumLocks(long inplaceUpdateNumLocks)
|
||||
throws RocksDBException;
|
||||
|
||||
/**
|
||||
* Number of locks used for inplace update
|
||||
* Default: 10000, if inplace_update_support = true, else 0.
|
||||
*
|
||||
* @return the number of locks used for inplace update.
|
||||
*/
|
||||
long inplaceUpdateNumLocks();
|
||||
|
||||
/**
|
||||
* Sets the number of bits used in the prefix bloom filter.
|
||||
*
|
||||
* This value will be used only when a prefix-extractor is specified.
|
||||
*
|
||||
* @param memtablePrefixBloomBits the number of bits used in the
|
||||
* prefix bloom filter.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setMemtablePrefixBloomBits(int memtablePrefixBloomBits);
|
||||
|
||||
/**
|
||||
* Returns the number of bits used in the prefix bloom filter.
|
||||
*
|
||||
* This value will be used only when a prefix-extractor is specified.
|
||||
*
|
||||
* @return the number of bloom-bits.
|
||||
* @see #useFixedLengthPrefixExtractor(int)
|
||||
*/
|
||||
int memtablePrefixBloomBits();
|
||||
|
||||
/**
|
||||
* The number of hash probes per key used in the mem-table.
|
||||
*
|
||||
* @param memtablePrefixBloomProbes the number of hash probes per key.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setMemtablePrefixBloomProbes(int memtablePrefixBloomProbes);
|
||||
|
||||
/**
|
||||
* The number of hash probes per key used in the mem-table.
|
||||
*
|
||||
* @return the number of hash probes per key.
|
||||
*/
|
||||
int memtablePrefixBloomProbes();
|
||||
|
||||
/**
|
||||
* Control locality of bloom filter probes to improve cache miss rate.
|
||||
* This option only applies to memtable prefix bloom and plaintable
|
||||
* prefix bloom. It essentially limits the max number of cache lines each
|
||||
* bloom filter check can touch.
|
||||
* This optimization is turned off when set to 0. The number should never
|
||||
* be greater than number of probes. This option can boost performance
|
||||
* for in-memory workload but should use with care since it can cause
|
||||
* higher false positive rate.
|
||||
* Default: 0
|
||||
*
|
||||
* @param bloomLocality the level of locality of bloom-filter probes.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setBloomLocality(int bloomLocality);
|
||||
|
||||
/**
|
||||
* Control locality of bloom filter probes to improve cache miss rate.
|
||||
* This option only applies to memtable prefix bloom and plaintable
|
||||
* prefix bloom. It essentially limits the max number of cache lines each
|
||||
* bloom filter check can touch.
|
||||
* This optimization is turned off when set to 0. The number should never
|
||||
* be greater than number of probes. This option can boost performance
|
||||
* for in-memory workload but should use with care since it can cause
|
||||
* higher false positive rate.
|
||||
* Default: 0
|
||||
*
|
||||
* @return the level of locality of bloom-filter probes.
|
||||
* @see #setMemtablePrefixBloomProbes(int)
|
||||
*/
|
||||
int bloomLocality();
|
||||
|
||||
/**
|
||||
* Maximum number of successive merge operations on a key in the memtable.
|
||||
*
|
||||
* When a merge operation is added to the memtable and the maximum number of
|
||||
* successive merges is reached, the value of the key will be calculated and
|
||||
* inserted into the memtable instead of the merge operation. This will
|
||||
* ensure that there are never more than max_successive_merges merge
|
||||
* operations in the memtable.
|
||||
*
|
||||
* Default: 0 (disabled)
|
||||
*
|
||||
* @param maxSuccessiveMerges the maximum number of successive merges.
|
||||
* @return the reference to the current option.
|
||||
* @throws org.rocksdb.RocksDBException
|
||||
*/
|
||||
Object setMaxSuccessiveMerges(long maxSuccessiveMerges)
|
||||
throws RocksDBException;
|
||||
|
||||
/**
|
||||
* Maximum number of successive merge operations on a key in the memtable.
|
||||
*
|
||||
* When a merge operation is added to the memtable and the maximum number of
|
||||
* successive merges is reached, the value of the key will be calculated and
|
||||
* inserted into the memtable instead of the merge operation. This will
|
||||
* ensure that there are never more than max_successive_merges merge
|
||||
* operations in the memtable.
|
||||
*
|
||||
* Default: 0 (disabled)
|
||||
*
|
||||
* @return the maximum number of successive merges.
|
||||
*/
|
||||
long maxSuccessiveMerges();
|
||||
|
||||
/**
|
||||
* The number of partial merge operands to accumulate before partial
|
||||
* merge will be performed. Partial merge will not be called
|
||||
* if the list of values to merge is less than min_partial_merge_operands.
|
||||
*
|
||||
* If min_partial_merge_operands < 2, then it will be treated as 2.
|
||||
*
|
||||
* Default: 2
|
||||
*
|
||||
* @param minPartialMergeOperands min partial merge operands
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
Object setMinPartialMergeOperands(int minPartialMergeOperands);
|
||||
|
||||
/**
|
||||
* The number of partial merge operands to accumulate before partial
|
||||
* merge will be performed. Partial merge will not be called
|
||||
* if the list of values to merge is less than min_partial_merge_operands.
|
||||
*
|
||||
* If min_partial_merge_operands < 2, then it will be treated as 2.
|
||||
*
|
||||
* Default: 2
|
||||
*
|
||||
* @return min partial merge operands
|
||||
*/
|
||||
int minPartialMergeOperands();
|
||||
|
||||
/**
|
||||
* Default memtable memory budget used with the following methods:
|
||||
*
|
||||
* <ol>
|
||||
* <li>{@link #optimizeLevelStyleCompaction()}</li>
|
||||
* <li>{@link #optimizeUniversalStyleCompaction()}</li>
|
||||
* </ol>
|
||||
*/
|
||||
long DEFAULT_COMPACTION_MEMTABLE_MEMORY_BUDGET = 512 * 1024 * 1024;
|
||||
}
|
||||
@@ -5,6 +5,31 @@
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* Enum CompactionStyle
|
||||
*
|
||||
* RocksDB supports different styles of compaction. Available
|
||||
* compaction styles can be chosen using this enumeration.
|
||||
*
|
||||
* <ol>
|
||||
* <li><strong>LEVEL</strong> - Level based Compaction style</li>
|
||||
* <li><strong>UNIVERSAL</strong> - Universal Compaction Style is a
|
||||
* compaction style, targeting the use cases requiring lower write
|
||||
* amplification, trading off read amplification and space
|
||||
* amplification.</li>
|
||||
* <li><strong>FIFO</strong> - FIFO compaction style is the simplest
|
||||
* compaction strategy. It is suited for keeping event log data with
|
||||
* very low overhead (query log for example). It periodically deletes
|
||||
* the old data, so it's basically a TTL compaction style.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @see <a
|
||||
* href="https://github.com/facebook/rocksdb/wiki/Universal-Compaction">
|
||||
* Universal Compaction</a>
|
||||
* @see <a
|
||||
* href="https://github.com/facebook/rocksdb/wiki/FIFO-compaction-style">
|
||||
* FIFO Compaction</a>
|
||||
*/
|
||||
public enum CompactionStyle {
|
||||
LEVEL((byte) 0),
|
||||
UNIVERSAL((byte) 1),
|
||||
@@ -16,6 +41,11 @@ public enum CompactionStyle {
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte value of the enumerations value
|
||||
*
|
||||
* @return byte representation
|
||||
*/
|
||||
public byte getValue() {
|
||||
return value_;
|
||||
}
|
||||
|
||||
Loaded 100 of 241 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user