mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-08 07:05:23 +08:00
Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce87213126 | |||
| 4ab7ccfd88 | |||
| c4d32b9336 | |||
| 390bbf4a47 | |||
| 6834fb8c3a | |||
| 8df16cf9e7 | |||
| 1fdd726a82 | |||
| d67500a591 | |||
| 4cb631aa0d | |||
| cfd0946bee | |||
| 9b58c73c7c | |||
| a84234a61b | |||
| 8ed70fc209 | |||
| 7f19bb93c6 | |||
| 8438a19360 | |||
| 6614a48418 | |||
| 076bd01a29 | |||
| 990df99a61 | |||
| 7dcadb1d37 | |||
| 985a31cf0e | |||
| f09329cb01 | |||
| 7e9f28cb23 | |||
| d20b8cfaa1 | |||
| 0f9c43ea36 | |||
| 722d80c374 | |||
| 22a0a60dc4 | |||
| be25ee44fe | |||
| 0c26e76b28 | |||
| 1d23b5c470 | |||
| 2a8faf797c | |||
| 7f71448388 | |||
| d977e55596 | |||
| d5bd6c772b | |||
| 6b46f78434 | |||
| 528a11c635 | |||
| 536e9973e3 | |||
| 4142a3e783 | |||
| 1913ce27b9 | |||
| 808e809366 | |||
| 0c39f54dfb | |||
| b6fd7811eb | |||
| 9dcb75b6d9 | |||
| 1755581f19 | |||
| d9c0785812 | |||
| bda6f3363d | |||
| 0db6b028e7 | |||
| ff6ec0eb17 | |||
| 23861857c4 | |||
| a98badff16 | |||
| 66f62e5c78 | |||
| 384400128f | |||
| 17b54aea53 | |||
| 050869177e | |||
| 42ea795209 | |||
| bb530c0f47 | |||
| f76eda74d6 | |||
| 08be7f5266 | |||
| 47b452cfcf | |||
| 562b7a1f28 | |||
| 63a2215c63 | |||
| e173bf9c19 | |||
| 10720a5587 | |||
| 01cbdd2aae | |||
| 045575ad0c | |||
| 7c5173d27f | |||
| 6929b08616 | |||
| 50b790c6d4 | |||
| 162b8151f1 | |||
| 28b5c76004 | |||
| 5585e00279 | |||
| 68eed8c9f0 | |||
| 36e759d199 | |||
| a6fd14c881 | |||
| c7037153c3 | |||
| c8ecfaedd0 | |||
| 570ba5aca8 | |||
| 625b9ef4e0 | |||
| 59a2763d5c | |||
| f611935e9a | |||
| 58b0f9d890 | |||
| 2da53b1e06 | |||
| 6c4c159b2c | |||
| 8dfe2fdd51 | |||
| 58c49466d2 | |||
| 6a2be31f14 | |||
| e91ebf1399 | |||
| 5a5953b388 | |||
| 5e642403a9 | |||
| 0c9dc9f8e0 | |||
| bcefedeb6d | |||
| 48081777f3 | |||
| 0138b8eba8 | |||
| 1562653ba0 | |||
| 218857b3f5 | |||
| 5d0074c471 | |||
| 3fcf7b26b9 | |||
| 2fa643466d | |||
| 06a52bda64 | |||
| 9674c11d01 |
+16
@@ -1,6 +1,21 @@
|
||||
# Rocksdb Change Log
|
||||
|
||||
### Unreleased
|
||||
|
||||
----- Past Releases -----
|
||||
|
||||
## 3.5.0 (9/3/2014)
|
||||
### New Features
|
||||
* Add include/utilities/write_batch_with_index.h, providing a utilitiy class to query data out of WriteBatch when building it.
|
||||
* Move BlockBasedTable related options to BlockBasedTableOptions from Options. Change corresponding JNI interface. Options affected include:
|
||||
no_block_cache, block_cache, block_cache_compressed, block_size, block_size_deviation, block_restart_interval, filter_policy, whole_key_filtering. filter_policy is changed to shared_ptr from a raw pointer.
|
||||
* Remove deprecated options: disable_seek_compaction and db_stats_log_interval
|
||||
* OptimizeForPointLookup() takes one parameter for block cache size. It now builds hash index, bloom filter, and block cache.
|
||||
|
||||
### Public API changes
|
||||
* The Prefix Extractor used with V2 compaction filters is now passed user key to SliceTransform::Transform instead of unparsed RocksDB key.
|
||||
|
||||
## 3.4.0 (8/18/2014)
|
||||
### New Features
|
||||
* Support Multiple DB paths in universal style compactions
|
||||
* Add feature of storing plain table index and bloom filter in SST file.
|
||||
@@ -13,6 +28,7 @@
|
||||
* Statistics APIs now take uint32_t as type instead of Tickers. Also make two access functions getTickerCount and histogramData const
|
||||
* Add DB property rocksdb.estimate-num-keys, estimated number of live keys in DB.
|
||||
* Add DB::GetIntProperty(), which returns DB properties that are integer as uint64_t.
|
||||
* The Prefix Extractor used with V2 compaction filters is now passed user key to SliceTransform::Transform instead of unparsed RocksDB key.
|
||||
|
||||
## 3.3.0 (7/10/2014)
|
||||
### New Features
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
# Inherit some settings from environment variables, if available
|
||||
INSTALL_PATH ?= $(CURDIR)
|
||||
|
||||
#-----------------------------------------------
|
||||
|
||||
@@ -49,7 +48,27 @@ else
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE) -DHAVE_JEMALLOC
|
||||
endif
|
||||
|
||||
WARNING_FLAGS = -Wall -Werror -Wsign-compare
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
|
||||
uninstall:
|
||||
@rm -rf $(INSTALL_PATH)/include/rocksdb
|
||||
@rm -rf $(INSTALL_PATH)/lib/$(LIBRARY)
|
||||
@rm -rf $(INSTALL_PATH)/lib/$(SHARED)
|
||||
|
||||
install:
|
||||
@install -d $(INSTALL_PATH)/lib
|
||||
@for header_dir in `find "include/rocksdb" -type d`; do \
|
||||
install -d $(INSTALL_PATH)/$$header_dir; \
|
||||
done
|
||||
@for header in `find "include/rocksdb" -type f -name *.h`; do \
|
||||
install -C -m 644 $$header $(INSTALL_PATH)/$$header; \
|
||||
done
|
||||
@[ ! -e $(LIBRARY) ] || install -C -m 644 $(LIBRARY) $(INSTALL_PATH)/lib
|
||||
@[ ! -e $(SHARED) ] || install -C -m 644 $(SHARED) $(INSTALL_PATH)/lib
|
||||
#-------------------------------------------------
|
||||
|
||||
CFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverloaded-virtual
|
||||
|
||||
@@ -116,10 +135,12 @@ TESTS = \
|
||||
table_test \
|
||||
thread_local_test \
|
||||
geodb_test \
|
||||
rate_limiter_test \
|
||||
cuckoo_table_builder_test \
|
||||
rate_limiter_test \
|
||||
options_test \
|
||||
cuckoo_table_reader_test
|
||||
cuckoo_table_builder_test \
|
||||
cuckoo_table_reader_test \
|
||||
cuckoo_table_db_test \
|
||||
write_batch_with_index_test
|
||||
|
||||
TOOLS = \
|
||||
sst_dump \
|
||||
@@ -155,7 +176,7 @@ SHARED = $(SHARED1)
|
||||
else
|
||||
# Update db.h if you change these.
|
||||
SHARED_MAJOR = 3
|
||||
SHARED_MINOR = 4
|
||||
SHARED_MINOR = 5
|
||||
SHARED1 = ${LIBNAME}.$(PLATFORM_SHARED_EXT)
|
||||
SHARED2 = $(SHARED1).$(SHARED_MAJOR)
|
||||
SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
|
||||
@@ -234,7 +255,7 @@ valgrind_check: all $(PROGRAMS) $(TESTS)
|
||||
echo $$t $$((etime - stime)) >> $(VALGRIND_DIR)/valgrind_tests_times; \
|
||||
done
|
||||
|
||||
unity.cc:
|
||||
unity.cc:
|
||||
$(shell (export ROCKSDB_ROOT="$(CURDIR)"; "$(CURDIR)/build_tools/unity" "$(CURDIR)/unity.cc"))
|
||||
|
||||
unity: unity.cc unity.o
|
||||
@@ -373,6 +394,9 @@ spatial_db_test: utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNE
|
||||
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
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)
|
||||
|
||||
dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
@@ -430,6 +454,9 @@ cuckoo_table_builder_test: table/cuckoo_table_builder_test.o $(LIBOBJECTS) $(TES
|
||||
cuckoo_table_reader_test: table/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS)
|
||||
$(CXX) table/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
cuckoo_table_db_test: db/cuckoo_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/cuckoo_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
options_test: util/options_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/options_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
|
||||
@@ -49,13 +49,7 @@ COMMON_FLAGS="-DROCKSDB_PLATFORM_POSIX"
|
||||
if [ -d /mnt/gvfs/third-party -a -z "$CXX" ]; then
|
||||
FBCODE_BUILD="true"
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
CENTOS_VERSION=`rpm -q --qf "%{VERSION}" \
|
||||
$(rpm -q --whatprovides redhat-release)`
|
||||
if [ "$CENTOS_VERSION" = "6" ]; then
|
||||
source "$PWD/build_tools/fbcode.gcc481.sh"
|
||||
else
|
||||
source "$PWD/build_tools/fbcode.gcc471.sh"
|
||||
fi
|
||||
source $PWD/build_tools/fbcode_config.sh
|
||||
else
|
||||
source "$PWD/build_tools/fbcode.clang31.sh"
|
||||
fi
|
||||
@@ -242,7 +236,7 @@ EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=google"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=gflags"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/963d9aeda70cc4779885b1277484fe7544a04e3e/9.0.0/platform007/9e92d53/
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/ab9f09bba370e7066cafd4eb59752db93f2e8312/2.29.1/platform007/15a3614
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies.sh"
|
||||
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=" -msse -msse4.2 "
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $LZ4_INCLUDE $GFLAGS_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
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+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
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"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
@@ -55,6 +55,7 @@ using rocksdb::MergeOperator;
|
||||
using rocksdb::NewBloomFilterPolicy;
|
||||
using rocksdb::NewLRUCache;
|
||||
using rocksdb::Options;
|
||||
using rocksdb::BlockBasedTableOptions;
|
||||
using rocksdb::RandomAccessFile;
|
||||
using rocksdb::Range;
|
||||
using rocksdb::ReadOptions;
|
||||
@@ -81,6 +82,7 @@ struct rocksdb_fifo_compaction_options_t { CompactionOptionsFIFO rep; };
|
||||
struct rocksdb_readoptions_t { ReadOptions rep; };
|
||||
struct rocksdb_writeoptions_t { WriteOptions rep; };
|
||||
struct rocksdb_options_t { Options rep; };
|
||||
struct rocksdb_block_based_table_options_t { BlockBasedTableOptions rep; };
|
||||
struct rocksdb_seqfile_t { SequentialFile* rep; };
|
||||
struct rocksdb_randomfile_t { RandomAccessFile* rep; };
|
||||
struct rocksdb_writablefile_t { WritableFile* rep; };
|
||||
@@ -1053,6 +1055,74 @@ const char* rocksdb_writebatch_data(rocksdb_writebatch_t* b, size_t* size) {
|
||||
return b->rep.Data().c_str();
|
||||
}
|
||||
|
||||
rocksdb_block_based_table_options_t*
|
||||
rocksdb_block_based_options_create() {
|
||||
return new rocksdb_block_based_table_options_t;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_destroy(
|
||||
rocksdb_block_based_table_options_t* options) {
|
||||
delete options;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_block_size(
|
||||
rocksdb_block_based_table_options_t* options, size_t block_size) {
|
||||
options->rep.block_size = block_size;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_block_size_deviation(
|
||||
rocksdb_block_based_table_options_t* options, int block_size_deviation) {
|
||||
options->rep.block_size_deviation = block_size_deviation;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_block_restart_interval(
|
||||
rocksdb_block_based_table_options_t* options, int block_restart_interval) {
|
||||
options->rep.block_restart_interval = block_restart_interval;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_filter_policy(
|
||||
rocksdb_block_based_table_options_t* options,
|
||||
rocksdb_filterpolicy_t* filter_policy) {
|
||||
options->rep.filter_policy.reset(filter_policy);
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_no_block_cache(
|
||||
rocksdb_block_based_table_options_t* options,
|
||||
unsigned char no_block_cache) {
|
||||
options->rep.no_block_cache = no_block_cache;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_block_cache(
|
||||
rocksdb_block_based_table_options_t* options,
|
||||
rocksdb_cache_t* block_cache) {
|
||||
if (block_cache) {
|
||||
options->rep.block_cache = block_cache->rep;
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_block_cache_compressed(
|
||||
rocksdb_block_based_table_options_t* options,
|
||||
rocksdb_cache_t* block_cache_compressed) {
|
||||
if (block_cache_compressed) {
|
||||
options->rep.block_cache_compressed = block_cache_compressed->rep;
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_whole_key_filtering(
|
||||
rocksdb_block_based_table_options_t* options, unsigned char v) {
|
||||
options->rep.whole_key_filtering = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_block_based_table_factory(
|
||||
rocksdb_options_t *opt,
|
||||
rocksdb_block_based_table_options_t* table_options) {
|
||||
if (table_options) {
|
||||
opt->rep.table_factory.reset(
|
||||
rocksdb::NewBlockBasedTableFactory(table_options->rep));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rocksdb_options_t* rocksdb_options_create() {
|
||||
return new rocksdb_options_t;
|
||||
}
|
||||
@@ -1067,8 +1137,8 @@ void rocksdb_options_increase_parallelism(
|
||||
}
|
||||
|
||||
void rocksdb_options_optimize_for_point_lookup(
|
||||
rocksdb_options_t* opt) {
|
||||
opt->rep.OptimizeForPointLookup();
|
||||
rocksdb_options_t* opt, uint64_t block_cache_size_mb) {
|
||||
opt->rep.OptimizeForPointLookup(block_cache_size_mb);
|
||||
}
|
||||
|
||||
void rocksdb_options_optimize_level_style_compaction(
|
||||
@@ -1111,12 +1181,6 @@ void rocksdb_options_set_compaction_filter_factory_v2(
|
||||
opt->rep.compaction_filter_factory_v2 = std::shared_ptr<CompactionFilterFactoryV2>(compaction_filter_factory_v2);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_filter_policy(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_filterpolicy_t* policy) {
|
||||
opt->rep.filter_policy = policy;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_create_if_missing(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.create_if_missing = v;
|
||||
@@ -1160,26 +1224,6 @@ void rocksdb_options_set_max_open_files(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_open_files = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_cache(rocksdb_options_t* opt, rocksdb_cache_t* c) {
|
||||
if (c) {
|
||||
opt->rep.block_cache = c->rep;
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_options_set_cache_compressed(rocksdb_options_t* opt, rocksdb_cache_t* c) {
|
||||
if (c) {
|
||||
opt->rep.block_cache_compressed = c->rep;
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_options_set_block_size(rocksdb_options_t* opt, size_t s) {
|
||||
opt->rep.block_size = s;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_block_restart_interval(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.block_restart_interval = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_target_file_size_base(
|
||||
rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.target_file_size_base = n;
|
||||
@@ -1272,11 +1316,6 @@ void rocksdb_options_set_prefix_extractor(
|
||||
opt->rep.prefix_extractor.reset(prefix_extractor);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_whole_key_filtering(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.whole_key_filtering = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_disable_data_sync(
|
||||
rocksdb_options_t* opt, int disable_data_sync) {
|
||||
opt->rep.disableDataSync = disable_data_sync;
|
||||
@@ -1287,11 +1326,6 @@ void rocksdb_options_set_use_fsync(
|
||||
opt->rep.use_fsync = use_fsync;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_stats_log_interval(
|
||||
rocksdb_options_t* opt, int db_stats_log_interval) {
|
||||
opt->rep.db_stats_log_interval = db_stats_log_interval;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_log_dir(
|
||||
rocksdb_options_t* opt, const char* db_log_dir) {
|
||||
opt->rep.db_log_dir = db_log_dir;
|
||||
@@ -1351,11 +1385,6 @@ void rocksdb_options_set_stats_dump_period_sec(
|
||||
opt->rep.stats_dump_period_sec = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_block_size_deviation(
|
||||
rocksdb_options_t* opt, int v) {
|
||||
opt->rep.block_size_deviation = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_advise_random_on_open(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.advise_random_on_open = v;
|
||||
@@ -1450,11 +1479,6 @@ void rocksdb_options_set_max_manifest_file_size(
|
||||
opt->rep.max_manifest_file_size = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_no_block_cache(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.no_block_cache = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_table_cache_numshardbits(
|
||||
rocksdb_options_t* opt, int v) {
|
||||
opt->rep.table_cache_numshardbits = v;
|
||||
@@ -1474,10 +1498,6 @@ void rocksdb_options_set_disable_auto_compactions(rocksdb_options_t* opt, int di
|
||||
opt->rep.disable_auto_compactions = disable;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_disable_seek_compaction(rocksdb_options_t* opt, int disable) {
|
||||
opt->rep.disable_seek_compaction = disable;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_delete_obsolete_files_period_micros(
|
||||
rocksdb_options_t* opt, uint64_t v) {
|
||||
opt->rep.delete_obsolete_files_period_micros = v;
|
||||
|
||||
+40
-10
@@ -261,6 +261,29 @@ static void CompactionFilterV2Filter(
|
||||
}
|
||||
}
|
||||
|
||||
// Custom prefix extractor for compaction filter V2 which extracts first 3 characters.
|
||||
static void CFV2PrefixExtractorDestroy(void* arg) { }
|
||||
static char* CFV2PrefixExtractorTransform(void* arg, const char* key, size_t length, size_t* dst_length) {
|
||||
// Verify keys are maximum length 4; this verifies fix for a
|
||||
// prior bug which was passing the RocksDB-encoded key with
|
||||
// logical timestamp suffix instead of parsed user key.
|
||||
if (length > 4) {
|
||||
fprintf(stderr, "%s:%d: %s: key %s is not user key\n", __FILE__, __LINE__, phase, key);
|
||||
abort();
|
||||
}
|
||||
*dst_length = length < 3 ? length : 3;
|
||||
return (char*)key;
|
||||
}
|
||||
static unsigned char CFV2PrefixExtractorInDomain(void* state, const char* key, size_t length) {
|
||||
return 1;
|
||||
}
|
||||
static unsigned char CFV2PrefixExtractorInRange(void* state, const char* key, size_t length) {
|
||||
return 1;
|
||||
}
|
||||
static const char* CFV2PrefixExtractorName(void* state) {
|
||||
return "TestCFV2PrefixExtractor";
|
||||
}
|
||||
|
||||
// Custom compaction filter factory V2.
|
||||
static void CompactionFilterFactoryV2Destroy(void* arg) {
|
||||
rocksdb_slicetransform_destroy((rocksdb_slicetransform_t*)arg);
|
||||
@@ -312,6 +335,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_cache_t* cache;
|
||||
rocksdb_env_t* env;
|
||||
rocksdb_options_t* options;
|
||||
rocksdb_block_based_table_options_t* table_options;
|
||||
rocksdb_readoptions_t* roptions;
|
||||
rocksdb_writeoptions_t* woptions;
|
||||
char* err = NULL;
|
||||
@@ -330,14 +354,15 @@ int main(int argc, char** argv) {
|
||||
options = rocksdb_options_create();
|
||||
rocksdb_options_set_comparator(options, cmp);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
rocksdb_options_set_cache(options, cache);
|
||||
rocksdb_options_set_env(options, env);
|
||||
rocksdb_options_set_info_log(options, NULL);
|
||||
rocksdb_options_set_write_buffer_size(options, 100000);
|
||||
rocksdb_options_set_paranoid_checks(options, 1);
|
||||
rocksdb_options_set_max_open_files(options, 10);
|
||||
rocksdb_options_set_block_size(options, 1024);
|
||||
rocksdb_options_set_block_restart_interval(options, 8);
|
||||
table_options = rocksdb_block_based_options_create();
|
||||
rocksdb_block_based_options_set_block_cache(table_options, cache);
|
||||
rocksdb_options_set_block_based_table_factory(options, table_options);
|
||||
|
||||
rocksdb_options_set_compression(options, rocksdb_no_compression);
|
||||
rocksdb_options_set_compression_options(options, -14, -1, 0);
|
||||
int compression_levels[] = {rocksdb_no_compression, rocksdb_no_compression,
|
||||
@@ -517,10 +542,12 @@ int main(int argc, char** argv) {
|
||||
policy = rocksdb_filterpolicy_create_bloom(10);
|
||||
}
|
||||
|
||||
rocksdb_block_based_options_set_filter_policy(table_options, policy);
|
||||
|
||||
// Create new database
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
rocksdb_options_set_filter_policy(options, policy);
|
||||
rocksdb_options_set_block_based_table_factory(options, table_options);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
|
||||
@@ -542,8 +569,9 @@ int main(int argc, char** argv) {
|
||||
CheckGet(db, roptions, "foo", "foovalue");
|
||||
CheckGet(db, roptions, "bar", "barvalue");
|
||||
}
|
||||
rocksdb_options_set_filter_policy(options, NULL);
|
||||
rocksdb_filterpolicy_destroy(policy);
|
||||
// Reset the policy
|
||||
rocksdb_block_based_options_set_filter_policy(table_options, NULL);
|
||||
rocksdb_options_set_block_based_table_factory(options, table_options);
|
||||
}
|
||||
|
||||
StartPhase("compaction_filter");
|
||||
@@ -585,7 +613,10 @@ int main(int argc, char** argv) {
|
||||
{
|
||||
rocksdb_compactionfilterfactoryv2_t* factory;
|
||||
rocksdb_slicetransform_t* prefix_extractor;
|
||||
prefix_extractor = rocksdb_slicetransform_create_fixed_prefix(3);
|
||||
prefix_extractor = rocksdb_slicetransform_create(
|
||||
NULL, CFV2PrefixExtractorDestroy, CFV2PrefixExtractorTransform,
|
||||
CFV2PrefixExtractorInDomain, CFV2PrefixExtractorInRange,
|
||||
CFV2PrefixExtractorName);
|
||||
factory = rocksdb_compactionfilterfactoryv2_create(
|
||||
prefix_extractor, prefix_extractor, CompactionFilterFactoryV2Destroy,
|
||||
CompactionFilterFactoryV2Create, CompactionFilterFactoryV2Name);
|
||||
@@ -731,8 +762,7 @@ int main(int argc, char** argv) {
|
||||
StartPhase("prefix");
|
||||
{
|
||||
// Create new database
|
||||
rocksdb_filterpolicy_t* policy = rocksdb_filterpolicy_create_bloom(10);
|
||||
rocksdb_options_set_filter_policy(options, policy);
|
||||
rocksdb_options_set_allow_mmap_reads(options, 1);
|
||||
rocksdb_options_set_prefix_extractor(options, rocksdb_slicetransform_create_fixed_prefix(3));
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
|
||||
rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
|
||||
@@ -769,13 +799,13 @@ int main(int argc, char** argv) {
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_filterpolicy_destroy(policy);
|
||||
}
|
||||
|
||||
|
||||
StartPhase("cleanup");
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_destroy(options);
|
||||
rocksdb_block_based_options_destroy(table_options);
|
||||
rocksdb_readoptions_destroy(roptions);
|
||||
rocksdb_writeoptions_destroy(woptions);
|
||||
rocksdb_cache_destroy(cache);
|
||||
|
||||
+10
-11
@@ -50,11 +50,9 @@ ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
uint32_t ColumnFamilyHandleImpl::GetID() const { return cfd()->GetID(); }
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const InternalKeyComparator* icmp,
|
||||
const InternalFilterPolicy* ipolicy,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions result = src;
|
||||
result.comparator = icmp;
|
||||
result.filter_policy = (src.filter_policy != nullptr) ? ipolicy : nullptr;
|
||||
#ifdef OS_MACOSX
|
||||
// TODO(icanadi) make write_buffer_size uint64_t instead of size_t
|
||||
ClipToRange(&result.write_buffer_size, ((size_t)64) << 10, ((size_t)1) << 30);
|
||||
@@ -70,13 +68,7 @@ ColumnFamilyOptions SanitizeOptions(const InternalKeyComparator* icmp,
|
||||
result.min_write_buffer_number_to_merge =
|
||||
std::min(result.min_write_buffer_number_to_merge,
|
||||
result.max_write_buffer_number - 1);
|
||||
if (result.block_cache == nullptr && !result.no_block_cache) {
|
||||
result.block_cache = NewLRUCache(8 << 20);
|
||||
}
|
||||
result.compression_per_level = src.compression_per_level;
|
||||
if (result.block_size_deviation < 0 || result.block_size_deviation > 100) {
|
||||
result.block_size_deviation = 0;
|
||||
}
|
||||
if (result.max_mem_compaction_level >= result.num_levels) {
|
||||
result.max_mem_compaction_level = result.num_levels - 1;
|
||||
}
|
||||
@@ -195,9 +187,7 @@ ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
refs_(0),
|
||||
dropped_(false),
|
||||
internal_comparator_(options.comparator),
|
||||
internal_filter_policy_(options.filter_policy),
|
||||
options_(*db_options, SanitizeOptions(&internal_comparator_,
|
||||
&internal_filter_policy_, options)),
|
||||
options_(*db_options, SanitizeOptions(&internal_comparator_, options)),
|
||||
mem_(nullptr),
|
||||
imm_(options_.min_write_buffer_number_to_merge),
|
||||
super_version_(nullptr),
|
||||
@@ -631,4 +621,13 @@ ColumnFamilyHandle* ColumnFamilyMemTablesImpl::GetColumnFamilyHandle() {
|
||||
return &handle_;
|
||||
}
|
||||
|
||||
uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family) {
|
||||
uint32_t column_family_id = 0;
|
||||
if (column_family != nullptr) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
column_family_id = cfh->GetID();
|
||||
}
|
||||
return column_family_id;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+2
-2
@@ -113,7 +113,6 @@ struct SuperVersion {
|
||||
};
|
||||
|
||||
extern ColumnFamilyOptions SanitizeOptions(const InternalKeyComparator* icmp,
|
||||
const InternalFilterPolicy* ipolicy,
|
||||
const ColumnFamilyOptions& src);
|
||||
|
||||
class ColumnFamilySet;
|
||||
@@ -272,7 +271,6 @@ class ColumnFamilyData {
|
||||
bool dropped_; // true if client dropped it
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
const InternalFilterPolicy internal_filter_policy_;
|
||||
|
||||
Options const options_;
|
||||
|
||||
@@ -457,4 +455,6 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
ColumnFamilyHandleInternal handle_;
|
||||
};
|
||||
|
||||
extern uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -408,9 +408,15 @@ TEST(ColumnFamilyTest, WriteBatchFailure) {
|
||||
Open();
|
||||
CreateColumnFamiliesAndReopen({"one", "two"});
|
||||
WriteBatch batch;
|
||||
batch.Put(handles_[0], Slice("existing"), Slice("column-family"));
|
||||
batch.Put(handles_[1], Slice("non-existing"), Slice("column-family"));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
DropColumnFamilies({1});
|
||||
WriteOptions woptions_ignore_missing_cf;
|
||||
woptions_ignore_missing_cf.ignore_missing_column_families = true;
|
||||
batch.Put(handles_[0], Slice("still here"), Slice("column-family"));
|
||||
ASSERT_OK(db_->Write(woptions_ignore_missing_cf, &batch));
|
||||
ASSERT_EQ("column-family", Get(0, "still here"));
|
||||
Status s = db_->Write(WriteOptions(), &batch);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
Close();
|
||||
@@ -746,9 +752,10 @@ TEST(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.filter_policy = nullptr;
|
||||
default_cf.no_block_cache = true;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
one.compaction_style = kCompactionStyleUniversal;
|
||||
// trigger compaction if there are >= 4 files
|
||||
@@ -913,7 +920,9 @@ TEST(ColumnFamilyTest, DontRollEmptyLogs) {
|
||||
}
|
||||
int num_writable_file_start = env_->GetNumberOfNewWritableFileCalls();
|
||||
// this will trigger the flushes
|
||||
ASSERT_OK(db_->Write(WriteOptions(), nullptr));
|
||||
for (size_t i = 0; i <= 4; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[i]);
|
||||
|
||||
+30
-18
@@ -173,9 +173,12 @@ void CompactionPicker::GetRange(const std::vector<FileMetaData*>& inputs1,
|
||||
}
|
||||
|
||||
bool CompactionPicker::ExpandWhileOverlapping(Compaction* c) {
|
||||
assert(c != nullptr);
|
||||
// If inputs are empty then there is nothing to expand.
|
||||
if (!c || c->inputs_[0].empty()) {
|
||||
return true;
|
||||
if (c->inputs_[0].empty()) {
|
||||
assert(c->inputs_[1].empty());
|
||||
// This isn't good compaction
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetOverlappingInputs will always do the right thing for level-0.
|
||||
@@ -427,7 +430,7 @@ Compaction* LevelCompactionPicker::PickCompaction(Version* version,
|
||||
level = version->compaction_level_[i];
|
||||
if ((version->compaction_score_[i] >= 1)) {
|
||||
c = PickCompactionBySize(version, level, version->compaction_score_[i]);
|
||||
if (ExpandWhileOverlapping(c) == false) {
|
||||
if (c == nullptr || ExpandWhileOverlapping(c) == false) {
|
||||
delete c;
|
||||
c = nullptr;
|
||||
} else {
|
||||
@@ -720,10 +723,11 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
// first candidate to be compacted.
|
||||
uint64_t candidate_size = f != nullptr? f->compensated_file_size : 0;
|
||||
if (f != nullptr) {
|
||||
LogToBuffer(
|
||||
log_buffer, "[%s] Universal: Possible candidate file %s[%d].",
|
||||
version->cfd_->GetName().c_str(),
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId()).c_str(), loop);
|
||||
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].",
|
||||
version->cfd_->GetName().c_str(), file_num_buf, loop);
|
||||
}
|
||||
|
||||
// Check if the suceeding files need compaction.
|
||||
@@ -815,12 +819,14 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
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);
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
FormatFileNumber(f->fd.GetNumber(), f->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(),
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId()).c_str(),
|
||||
i, f->fd.GetFileSize(), f->compensated_file_size);
|
||||
version->cfd_->GetName().c_str(), file_num_buf, i,
|
||||
f->fd.GetFileSize(), f->compensated_file_size);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -854,29 +860,35 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
start_index = loop; // Consider this as the first candidate.
|
||||
break;
|
||||
}
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
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(),
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId()).c_str(),
|
||||
loop, " cannot be a candidate to reduce size amp.\n");
|
||||
version->cfd_->GetName().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
|
||||
}
|
||||
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
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(),
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId()).c_str(),
|
||||
start_index, " to reduce size amp.\n");
|
||||
version->cfd_->GetName().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(),
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId()).c_str(), loop,
|
||||
version->cfd_->GetName().c_str(), file_num_buf, loop,
|
||||
" is already being compacted. No size amp reduction possible.\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@ class CorruptionTest {
|
||||
|
||||
db_ = nullptr;
|
||||
options_.create_if_missing = true;
|
||||
options_.block_size_deviation = 0; // make unit test pass for now
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size_deviation = 0; // make unit test pass for now
|
||||
options_.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen();
|
||||
options_.create_if_missing = false;
|
||||
}
|
||||
@@ -60,9 +62,11 @@ class CorruptionTest {
|
||||
db_ = nullptr;
|
||||
Options opt = (options ? *options : options_);
|
||||
opt.env = &env_;
|
||||
opt.block_cache = tiny_cache_;
|
||||
opt.block_size_deviation = 0;
|
||||
opt.arena_block_size = 4096;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = tiny_cache_;
|
||||
table_options.block_size_deviation = 0;
|
||||
opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
return DB::Open(opt, dbname_, &db_);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "table/cuckoo_table_factory.h"
|
||||
#include "table/cuckoo_table_reader.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class CuckooTableDBTest {
|
||||
private:
|
||||
std::string dbname_;
|
||||
Env* env_;
|
||||
DB* db_;
|
||||
|
||||
public:
|
||||
CuckooTableDBTest() : env_(Env::Default()) {
|
||||
dbname_ = test::TmpDir() + "/cuckoo_table_db_test";
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
db_ = nullptr;
|
||||
Reopen();
|
||||
}
|
||||
|
||||
~CuckooTableDBTest() {
|
||||
delete db_;
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
}
|
||||
|
||||
Options CurrentOptions() {
|
||||
Options options;
|
||||
options.table_factory.reset(NewCuckooTableFactory());
|
||||
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true));
|
||||
options.allow_mmap_reads = true;
|
||||
options.create_if_missing = true;
|
||||
options.max_mem_compaction_level = 0;
|
||||
return options;
|
||||
}
|
||||
|
||||
DBImpl* dbfull() {
|
||||
return reinterpret_cast<DBImpl*>(db_);
|
||||
}
|
||||
|
||||
// The following util methods are copied from plain_table_db_test.
|
||||
void Reopen(Options* options = nullptr) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
Options opts;
|
||||
if (options != nullptr) {
|
||||
opts = *options;
|
||||
} else {
|
||||
opts = CurrentOptions();
|
||||
opts.create_if_missing = true;
|
||||
}
|
||||
ASSERT_OK(DB::Open(opts, dbname_, &db_));
|
||||
}
|
||||
|
||||
Status Put(const Slice& k, const Slice& v) {
|
||||
return db_->Put(WriteOptions(), k, v);
|
||||
}
|
||||
|
||||
Status Delete(const std::string& k) {
|
||||
return db_->Delete(WriteOptions(), k);
|
||||
}
|
||||
|
||||
std::string Get(const std::string& k) {
|
||||
ReadOptions options;
|
||||
std::string result;
|
||||
Status s = db_->Get(options, k, &result);
|
||||
if (s.IsNotFound()) {
|
||||
result = "NOT_FOUND";
|
||||
} else if (!s.ok()) {
|
||||
result = s.ToString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int NumTableFilesAtLevel(int level) {
|
||||
std::string property;
|
||||
ASSERT_TRUE(
|
||||
db_->GetProperty("rocksdb.num-files-at-level" + NumberToString(level),
|
||||
&property));
|
||||
return atoi(property.c_str());
|
||||
}
|
||||
|
||||
// Return spread of files per level
|
||||
std::string FilesPerLevel() {
|
||||
std::string result;
|
||||
int last_non_zero_offset = 0;
|
||||
for (int level = 0; level < db_->NumberLevels(); level++) {
|
||||
int f = NumTableFilesAtLevel(level);
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f);
|
||||
result += buf;
|
||||
if (f > 0) {
|
||||
last_non_zero_offset = result.size();
|
||||
}
|
||||
}
|
||||
result.resize(last_non_zero_offset);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(CuckooTableDBTest, Flush) {
|
||||
// Try with empty DB first.
|
||||
ASSERT_TRUE(dbfull() != nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("key2"));
|
||||
|
||||
// Add some values to db.
|
||||
Options options = CurrentOptions();
|
||||
Reopen(&options);
|
||||
|
||||
ASSERT_OK(Put("key1", "v1"));
|
||||
ASSERT_OK(Put("key2", "v2"));
|
||||
ASSERT_OK(Put("key3", "v3"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
|
||||
TablePropertiesCollection ptc;
|
||||
reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc);
|
||||
ASSERT_EQ(1U, ptc.size());
|
||||
ASSERT_EQ(3U, ptc.begin()->second->num_entries);
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
|
||||
ASSERT_EQ("v1", Get("key1"));
|
||||
ASSERT_EQ("v2", Get("key2"));
|
||||
ASSERT_EQ("v3", Get("key3"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key4"));
|
||||
|
||||
// Now add more keys and flush.
|
||||
ASSERT_OK(Put("key4", "v4"));
|
||||
ASSERT_OK(Put("key5", "v5"));
|
||||
ASSERT_OK(Put("key6", "v6"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
|
||||
reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc);
|
||||
ASSERT_EQ(2U, ptc.size());
|
||||
auto row = ptc.begin();
|
||||
ASSERT_EQ(3U, row->second->num_entries);
|
||||
ASSERT_EQ(3U, (++row)->second->num_entries);
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
ASSERT_EQ("v1", Get("key1"));
|
||||
ASSERT_EQ("v2", Get("key2"));
|
||||
ASSERT_EQ("v3", Get("key3"));
|
||||
ASSERT_EQ("v4", Get("key4"));
|
||||
ASSERT_EQ("v5", Get("key5"));
|
||||
ASSERT_EQ("v6", Get("key6"));
|
||||
|
||||
ASSERT_OK(Delete("key6"));
|
||||
ASSERT_OK(Delete("key5"));
|
||||
ASSERT_OK(Delete("key4"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc);
|
||||
ASSERT_EQ(3U, ptc.size());
|
||||
row = ptc.begin();
|
||||
ASSERT_EQ(3U, row->second->num_entries);
|
||||
ASSERT_EQ(3U, (++row)->second->num_entries);
|
||||
ASSERT_EQ(3U, (++row)->second->num_entries);
|
||||
ASSERT_EQ("3", FilesPerLevel());
|
||||
ASSERT_EQ("v1", Get("key1"));
|
||||
ASSERT_EQ("v2", Get("key2"));
|
||||
ASSERT_EQ("v3", Get("key3"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key4"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key5"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key6"));
|
||||
}
|
||||
|
||||
TEST(CuckooTableDBTest, FlushWithDuplicateKeys) {
|
||||
Options options = CurrentOptions();
|
||||
Reopen(&options);
|
||||
ASSERT_OK(Put("key1", "v1"));
|
||||
ASSERT_OK(Put("key2", "v2"));
|
||||
ASSERT_OK(Put("key1", "v3")); // Duplicate
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
|
||||
TablePropertiesCollection ptc;
|
||||
reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc);
|
||||
ASSERT_EQ(1U, ptc.size());
|
||||
ASSERT_EQ(2U, ptc.begin()->second->num_entries);
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
ASSERT_EQ("v3", Get("key1"));
|
||||
ASSERT_EQ("v2", Get("key2"));
|
||||
}
|
||||
|
||||
namespace {
|
||||
static std::string Key(int i) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "key_______%06d", i);
|
||||
return std::string(buf);
|
||||
}
|
||||
static std::string Uint64Key(uint64_t i) {
|
||||
std::string str;
|
||||
str.resize(8);
|
||||
memcpy(&str[0], static_cast<void*>(&i), 8);
|
||||
return str;
|
||||
}
|
||||
} // namespace.
|
||||
|
||||
TEST(CuckooTableDBTest, Uint64Comparator) {
|
||||
Options options = CurrentOptions();
|
||||
options.comparator = test::Uint64Comparator();
|
||||
Reopen(&options);
|
||||
|
||||
ASSERT_OK(Put(Uint64Key(1), "v1"));
|
||||
ASSERT_OK(Put(Uint64Key(2), "v2"));
|
||||
ASSERT_OK(Put(Uint64Key(3), "v3"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
|
||||
ASSERT_EQ("v1", Get(Uint64Key(1)));
|
||||
ASSERT_EQ("v2", Get(Uint64Key(2)));
|
||||
ASSERT_EQ("v3", Get(Uint64Key(3)));
|
||||
ASSERT_EQ("NOT_FOUND", Get(Uint64Key(4)));
|
||||
|
||||
// Add more keys.
|
||||
ASSERT_OK(Delete(Uint64Key(2))); // Delete.
|
||||
ASSERT_OK(Put(Uint64Key(3), "v0")); // Update.
|
||||
ASSERT_OK(Put(Uint64Key(4), "v4"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
ASSERT_EQ("v1", Get(Uint64Key(1)));
|
||||
ASSERT_EQ("NOT_FOUND", Get(Uint64Key(2)));
|
||||
ASSERT_EQ("v0", Get(Uint64Key(3)));
|
||||
ASSERT_EQ("v4", Get(Uint64Key(4)));
|
||||
}
|
||||
|
||||
TEST(CuckooTableDBTest, CompactionTrigger) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
Reopen(&options);
|
||||
|
||||
// Write 11 values, each 10016 B
|
||||
for (int idx = 0; idx < 11; ++idx) {
|
||||
ASSERT_OK(Put(Key(idx), std::string(10000, 'a' + idx)));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
|
||||
// Generate one more file in level-0, and should trigger level-0 compaction
|
||||
for (int idx = 11; idx < 22; ++idx) {
|
||||
ASSERT_OK(Put(Key(idx), std::string(10000, 'a' + idx)));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr);
|
||||
|
||||
ASSERT_EQ("0,2", FilesPerLevel());
|
||||
for (int idx = 0; idx < 22; ++idx) {
|
||||
ASSERT_EQ(std::string(10000, 'a' + idx), Get(Key(idx)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CuckooTableDBTest, SameKeyInsertedInTwoDifferentFilesAndCompacted) {
|
||||
// Insert same key twice so that they go to different SST files. Then wait for
|
||||
// compaction and check if the latest value is stored and old value removed.
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
Reopen(&options);
|
||||
|
||||
// Write 11 values, each 10016 B
|
||||
for (int idx = 0; idx < 11; ++idx) {
|
||||
ASSERT_OK(Put(Key(idx), std::string(10000, 'a')));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
|
||||
// Generate one more file in level-0, and should trigger level-0 compaction
|
||||
for (int idx = 0; idx < 11; ++idx) {
|
||||
ASSERT_OK(Put(Key(idx), std::string(10000, 'a' + idx)));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr);
|
||||
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
for (int idx = 0; idx < 11; ++idx) {
|
||||
ASSERT_EQ(std::string(10000, 'a' + idx), Get(Key(idx)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CuckooTableDBTest, AdaptiveTable) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
// Write some keys using cuckoo table.
|
||||
options.table_factory.reset(NewCuckooTableFactory());
|
||||
Reopen(&options);
|
||||
|
||||
ASSERT_OK(Put("key1", "v1"));
|
||||
ASSERT_OK(Put("key2", "v2"));
|
||||
ASSERT_OK(Put("key3", "v3"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
|
||||
// Write some keys using plain table.
|
||||
options.create_if_missing = false;
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
Reopen(&options);
|
||||
ASSERT_OK(Put("key4", "v4"));
|
||||
ASSERT_OK(Put("key1", "v5"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
|
||||
// Write some keys using block based table.
|
||||
std::shared_ptr<TableFactory> block_based_factory(
|
||||
NewBlockBasedTableFactory());
|
||||
options.table_factory.reset(NewAdaptiveTableFactory(block_based_factory));
|
||||
Reopen(&options);
|
||||
ASSERT_OK(Put("key5", "v6"));
|
||||
ASSERT_OK(Put("key2", "v7"));
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
|
||||
ASSERT_EQ("v5", Get("key1"));
|
||||
ASSERT_EQ("v7", Get("key2"));
|
||||
ASSERT_EQ("v3", Get("key3"));
|
||||
ASSERT_EQ("v4", Get("key4"));
|
||||
ASSERT_EQ("v6", Get("key5"));
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
+159
-78
@@ -146,6 +146,7 @@ DEFINE_int64(merge_keys, -1,
|
||||
"Number of distinct keys to use for MergeRandom and "
|
||||
"ReadRandomMergeRandom. "
|
||||
"If negative, there will be FLAGS_num keys.");
|
||||
DEFINE_int32(num_column_families, 1, "Number of Column Families to use.");
|
||||
|
||||
DEFINE_int64(reads, -1, "Number of read operations to do. "
|
||||
"If negative, do FLAGS_num reads.");
|
||||
@@ -162,6 +163,7 @@ DEFINE_int32(duration, 0, "Time in seconds for the random-ops tests to run."
|
||||
|
||||
DEFINE_int32(value_size, 100, "Size of each value");
|
||||
|
||||
DEFINE_bool(use_uint64_comparator, false, "use Uint64 user comparator");
|
||||
|
||||
static bool ValidateKeySize(const char* flagname, int32_t value) {
|
||||
return true;
|
||||
@@ -238,10 +240,11 @@ DEFINE_int32(universal_compression_size_percent, -1,
|
||||
DEFINE_int64(cache_size, -1, "Number of bytes to use as a cache of uncompressed"
|
||||
"data. Negative means use default settings.");
|
||||
|
||||
DEFINE_int32(block_size, rocksdb::Options().block_size,
|
||||
DEFINE_int32(block_size, rocksdb::BlockBasedTableOptions().block_size,
|
||||
"Number of bytes in a block.");
|
||||
|
||||
DEFINE_int32(block_restart_interval, rocksdb::Options().block_restart_interval,
|
||||
DEFINE_int32(block_restart_interval,
|
||||
rocksdb::BlockBasedTableOptions().block_restart_interval,
|
||||
"Number of keys between restart points "
|
||||
"for delta encoding of keys.");
|
||||
|
||||
@@ -507,6 +510,8 @@ DEFINE_int32(prefix_size, 0, "control the prefix size for HashSkipList and "
|
||||
DEFINE_int64(keys_per_prefix, 0, "control average number of keys generated "
|
||||
"per prefix, 0 means no special handling of the prefix, "
|
||||
"i.e. use the prefix comes with the generated random number.");
|
||||
DEFINE_bool(enable_io_prio, false, "Lower the background flush/compaction "
|
||||
"threads' IO priority");
|
||||
|
||||
enum RepFactory {
|
||||
kSkipList,
|
||||
@@ -541,6 +546,8 @@ DEFINE_string(memtablerep, "skip_list", "");
|
||||
DEFINE_int64(hash_bucket_count, 1024 * 1024, "hash bucket count");
|
||||
DEFINE_bool(use_plain_table, false, "if use plain table "
|
||||
"instead of block-based table format");
|
||||
DEFINE_bool(use_cuckoo_table, false, "if use cuckoo table format");
|
||||
DEFINE_double(cuckoo_hash_ratio, 0.9, "Hash ratio for Cuckoo SST table.");
|
||||
DEFINE_bool(use_hash_search, false, "if use kHashSearch "
|
||||
"instead of kBinarySearch. "
|
||||
"This is valid if only we use BlockTable");
|
||||
@@ -839,12 +846,19 @@ class Duration {
|
||||
|
||||
class Benchmark {
|
||||
private:
|
||||
shared_ptr<Cache> cache_;
|
||||
shared_ptr<Cache> compressed_cache_;
|
||||
const FilterPolicy* filter_policy_;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<Cache> compressed_cache_;
|
||||
std::shared_ptr<const FilterPolicy> filter_policy_;
|
||||
const SliceTransform* prefix_extractor_;
|
||||
DB* db_;
|
||||
std::vector<DB*> multi_dbs_;
|
||||
struct DBWithColumnFamilies {
|
||||
std::vector<ColumnFamilyHandle*> cfh;
|
||||
DB* db;
|
||||
DBWithColumnFamilies() : db(nullptr) {
|
||||
cfh.clear();
|
||||
}
|
||||
};
|
||||
DBWithColumnFamilies db_;
|
||||
std::vector<DBWithColumnFamilies> multi_dbs_;
|
||||
int64_t num_;
|
||||
int value_size_;
|
||||
int key_size_;
|
||||
@@ -1064,7 +1078,6 @@ class Benchmark {
|
||||
? NewBloomFilterPolicy(FLAGS_bloom_bits)
|
||||
: nullptr),
|
||||
prefix_extractor_(NewFixedPrefixTransform(FLAGS_prefix_size)),
|
||||
db_(nullptr),
|
||||
num_(FLAGS_num),
|
||||
value_size_(FLAGS_value_size),
|
||||
key_size_(FLAGS_key_size),
|
||||
@@ -1095,8 +1108,7 @@ class Benchmark {
|
||||
}
|
||||
|
||||
~Benchmark() {
|
||||
delete db_;
|
||||
delete filter_policy_;
|
||||
delete db_.db;
|
||||
delete prefix_extractor_;
|
||||
}
|
||||
|
||||
@@ -1155,6 +1167,16 @@ class Benchmark {
|
||||
return base_name + std::to_string(id);
|
||||
}
|
||||
|
||||
std::string ColumnFamilyName(int i) {
|
||||
if (i == 0) {
|
||||
return kDefaultColumnFamilyName;
|
||||
} else {
|
||||
char name[100];
|
||||
snprintf(name, sizeof(name), "column_family_name_%06d", i);
|
||||
return std::string(name);
|
||||
}
|
||||
}
|
||||
|
||||
void Run() {
|
||||
if (!SanityCheck()) {
|
||||
exit(1);
|
||||
@@ -1309,13 +1331,14 @@ class Benchmark {
|
||||
name.ToString().c_str());
|
||||
method = nullptr;
|
||||
} else {
|
||||
if (db_ != nullptr) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
if (db_.db != nullptr) {
|
||||
delete db_.db;
|
||||
db_.db = nullptr;
|
||||
db_.cfh.clear();
|
||||
DestroyDB(FLAGS_db, Options());
|
||||
}
|
||||
for (size_t i = 0; i < multi_dbs_.size(); i++) {
|
||||
delete multi_dbs_[i];
|
||||
delete multi_dbs_[i].db;
|
||||
DestroyDB(GetDbNameForMultiple(FLAGS_db, i), Options());
|
||||
}
|
||||
multi_dbs_.clear();
|
||||
@@ -1487,7 +1510,7 @@ class Benchmark {
|
||||
|
||||
void Compress(ThreadState *thread) {
|
||||
RandomGenerator gen;
|
||||
Slice input = gen.Generate(Options().block_size);
|
||||
Slice input = gen.Generate(FLAGS_block_size);
|
||||
int64_t bytes = 0;
|
||||
int64_t produced = 0;
|
||||
bool ok = true;
|
||||
@@ -1537,7 +1560,7 @@ class Benchmark {
|
||||
|
||||
void Uncompress(ThreadState *thread) {
|
||||
RandomGenerator gen;
|
||||
Slice input = gen.Generate(Options().block_size);
|
||||
Slice input = gen.Generate(FLAGS_block_size);
|
||||
std::string compressed;
|
||||
|
||||
bool ok;
|
||||
@@ -1613,14 +1636,10 @@ class Benchmark {
|
||||
}
|
||||
|
||||
void Open() {
|
||||
assert(db_ == nullptr);
|
||||
assert(db_.db == nullptr);
|
||||
Options options;
|
||||
options.create_if_missing = !FLAGS_use_existing_db;
|
||||
options.block_cache = cache_;
|
||||
options.block_cache_compressed = compressed_cache_;
|
||||
if (cache_ == nullptr) {
|
||||
options.no_block_cache = true;
|
||||
}
|
||||
options.create_missing_column_families = FLAGS_num_column_families > 1;
|
||||
options.write_buffer_size = FLAGS_write_buffer_size;
|
||||
options.max_write_buffer_number = FLAGS_max_write_buffer_number;
|
||||
options.min_write_buffer_number_to_merge =
|
||||
@@ -1628,17 +1647,25 @@ class Benchmark {
|
||||
options.max_background_compactions = FLAGS_max_background_compactions;
|
||||
options.max_background_flushes = FLAGS_max_background_flushes;
|
||||
options.compaction_style = FLAGS_compaction_style_e;
|
||||
options.block_size = FLAGS_block_size;
|
||||
options.block_restart_interval = FLAGS_block_restart_interval;
|
||||
options.filter_policy = filter_policy_;
|
||||
if (FLAGS_prefix_size != 0) {
|
||||
options.prefix_extractor.reset(
|
||||
NewFixedPrefixTransform(FLAGS_prefix_size));
|
||||
}
|
||||
if (FLAGS_use_uint64_comparator) {
|
||||
options.comparator = test::Uint64Comparator();
|
||||
if (FLAGS_key_size != 8) {
|
||||
fprintf(stderr, "Using Uint64 comparator but key size is not 8.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
options.memtable_prefix_bloom_bits = FLAGS_memtable_bloom_bits;
|
||||
options.bloom_locality = FLAGS_bloom_locality;
|
||||
options.max_open_files = FLAGS_open_files;
|
||||
options.statistics = dbstats;
|
||||
if (FLAGS_enable_io_prio) {
|
||||
FLAGS_env->LowerThreadPoolIOPriority(Env::LOW);
|
||||
FLAGS_env->LowerThreadPoolIOPriority(Env::HIGH);
|
||||
}
|
||||
options.env = FLAGS_env;
|
||||
options.disableDataSync = FLAGS_disable_data_sync;
|
||||
options.use_fsync = FLAGS_use_fsync;
|
||||
@@ -1699,6 +1726,13 @@ class Benchmark {
|
||||
plain_table_options.hash_table_ratio = 0.75;
|
||||
options.table_factory = std::shared_ptr<TableFactory>(
|
||||
NewPlainTableFactory(plain_table_options));
|
||||
} else if (FLAGS_use_cuckoo_table) {
|
||||
if (FLAGS_cuckoo_hash_ratio > 1 || FLAGS_cuckoo_hash_ratio < 0) {
|
||||
fprintf(stderr, "Invalid cuckoo_hash_ratio\n");
|
||||
exit(1);
|
||||
}
|
||||
options.table_factory = std::shared_ptr<TableFactory>(
|
||||
NewCuckooTableFactory(FLAGS_cuckoo_hash_ratio));
|
||||
} else {
|
||||
BlockBasedTableOptions block_based_options;
|
||||
if (FLAGS_use_hash_search) {
|
||||
@@ -1711,6 +1745,14 @@ class Benchmark {
|
||||
} else {
|
||||
block_based_options.index_type = BlockBasedTableOptions::kBinarySearch;
|
||||
}
|
||||
if (cache_ == nullptr) {
|
||||
block_based_options.no_block_cache = true;
|
||||
}
|
||||
block_based_options.block_cache = cache_;
|
||||
block_based_options.block_cache_compressed = compressed_cache_;
|
||||
block_based_options.block_size = FLAGS_block_size;
|
||||
block_based_options.block_restart_interval = FLAGS_block_restart_interval;
|
||||
block_based_options.filter_policy = filter_policy_;
|
||||
options.table_factory.reset(
|
||||
NewBlockBasedTableFactory(block_based_options));
|
||||
}
|
||||
@@ -1801,10 +1843,9 @@ class Benchmark {
|
||||
OpenDb(options, FLAGS_db, &db_);
|
||||
} else {
|
||||
multi_dbs_.clear();
|
||||
multi_dbs_.resize(FLAGS_num_multi_db);
|
||||
for (int i = 0; i < FLAGS_num_multi_db; i++) {
|
||||
DB* db;
|
||||
OpenDb(options, GetDbNameForMultiple(FLAGS_db, i), &db);
|
||||
multi_dbs_.push_back(db);
|
||||
OpenDb(options, GetDbNameForMultiple(FLAGS_db, i), &multi_dbs_[i]);
|
||||
}
|
||||
}
|
||||
if (FLAGS_min_level_to_compress >= 0) {
|
||||
@@ -1812,12 +1853,27 @@ class Benchmark {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenDb(Options options, std::string db_name, DB** db) {
|
||||
void OpenDb(const Options& options, const std::string& db_name,
|
||||
DBWithColumnFamilies* db) {
|
||||
Status s;
|
||||
if(FLAGS_readonly) {
|
||||
s = DB::OpenForReadOnly(options, db_name, db);
|
||||
// Open with column families if necessary.
|
||||
if (FLAGS_num_column_families > 1) {
|
||||
db->cfh.resize(FLAGS_num_column_families);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < FLAGS_num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
ColumnFamilyName(i), ColumnFamilyOptions(options)));
|
||||
}
|
||||
if (FLAGS_readonly) {
|
||||
s = DB::OpenForReadOnly(options, db_name, column_families,
|
||||
&db->cfh, &db->db);
|
||||
} else {
|
||||
s = DB::Open(options, db_name, column_families, &db->cfh, &db->db);
|
||||
}
|
||||
} else if (FLAGS_readonly) {
|
||||
s = DB::OpenForReadOnly(options, db_name, &db->db);
|
||||
} else {
|
||||
s = DB::Open(options, db_name, db);
|
||||
s = DB::Open(options, db_name, &db->db);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "open error: %s\n", s.ToString().c_str());
|
||||
@@ -1885,10 +1941,18 @@ class Benchmark {
|
||||
};
|
||||
|
||||
DB* SelectDB(ThreadState* thread) {
|
||||
if (db_ != nullptr) {
|
||||
return db_;
|
||||
} else {
|
||||
return multi_dbs_[thread->rand.Next() % multi_dbs_.size()];
|
||||
return SelectDBWithCfh(thread)->db;
|
||||
}
|
||||
|
||||
DBWithColumnFamilies* SelectDBWithCfh(ThreadState* thread) {
|
||||
return SelectDBWithCfh(thread->rand.Next());
|
||||
}
|
||||
|
||||
DBWithColumnFamilies* SelectDBWithCfh(uint64_t rand_int) {
|
||||
if (db_.db != nullptr) {
|
||||
return &db_;
|
||||
} else {
|
||||
return &multi_dbs_[rand_int % multi_dbs_.size()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1897,7 +1961,7 @@ class Benchmark {
|
||||
const int64_t num_ops = writes_ == 0 ? num_ : writes_;
|
||||
|
||||
size_t num_key_gens = 1;
|
||||
if (db_ == nullptr) {
|
||||
if (db_.db == nullptr) {
|
||||
num_key_gens = multi_dbs_.size();
|
||||
}
|
||||
std::vector<std::unique_ptr<KeyGenerator>> key_gens(num_key_gens);
|
||||
@@ -1920,20 +1984,25 @@ class Benchmark {
|
||||
Slice key = AllocateKey();
|
||||
std::unique_ptr<const char[]> key_guard(key.data());
|
||||
while (!duration.Done(entries_per_batch_)) {
|
||||
size_t id = 0;
|
||||
DB* db_to_write = db_;
|
||||
if (db_to_write == nullptr) {
|
||||
id = thread->rand.Next() % num_key_gens;
|
||||
db_to_write = multi_dbs_[id];
|
||||
}
|
||||
size_t id = thread->rand.Next() % num_key_gens;
|
||||
DBWithColumnFamilies* db_with_cfh = SelectDBWithCfh(id);
|
||||
batch.Clear();
|
||||
for (int64_t j = 0; j < entries_per_batch_; j++) {
|
||||
GenerateKeyFromInt(key_gens[id]->Next(), FLAGS_num, &key);
|
||||
batch.Put(key, gen.Generate(value_size_));
|
||||
int64_t rand_num = key_gens[id]->Next();
|
||||
GenerateKeyFromInt(rand_num, FLAGS_num, &key);
|
||||
if (FLAGS_num_column_families <= 1) {
|
||||
batch.Put(key, gen.Generate(value_size_));
|
||||
} else {
|
||||
// We use same rand_num as seed for key and column family so that we
|
||||
// can deterministically find the cfh corresponding to a particular
|
||||
// key while reading the key.
|
||||
batch.Put(db_with_cfh->cfh[rand_num % db_with_cfh->cfh.size()],
|
||||
key, gen.Generate(value_size_));
|
||||
}
|
||||
bytes += value_size_ + key_size_;
|
||||
}
|
||||
s = db_to_write->Write(write_options_, &batch);
|
||||
thread->stats.FinishedOps(db_to_write, entries_per_batch_);
|
||||
s = db_with_cfh->db->Write(write_options_, &batch);
|
||||
thread->stats.FinishedOps(db_with_cfh->db, entries_per_batch_);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
@@ -1943,11 +2012,11 @@ class Benchmark {
|
||||
}
|
||||
|
||||
void ReadSequential(ThreadState* thread) {
|
||||
if (db_ != nullptr) {
|
||||
ReadSequential(thread, db_);
|
||||
if (db_.db != nullptr) {
|
||||
ReadSequential(thread, db_.db);
|
||||
} else {
|
||||
for (DB* db : multi_dbs_) {
|
||||
ReadSequential(thread, db);
|
||||
for (const auto& db_with_cfh : multi_dbs_) {
|
||||
ReadSequential(thread, db_with_cfh.db);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1966,11 +2035,11 @@ class Benchmark {
|
||||
}
|
||||
|
||||
void ReadReverse(ThreadState* thread) {
|
||||
if (db_ != nullptr) {
|
||||
ReadReverse(thread, db_);
|
||||
if (db_.db != nullptr) {
|
||||
ReadReverse(thread, db_.db);
|
||||
} else {
|
||||
for (DB* db : multi_dbs_) {
|
||||
ReadReverse(thread, db);
|
||||
for (const auto& db_with_cfh : multi_dbs_) {
|
||||
ReadReverse(thread, db_with_cfh.db);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1981,7 +2050,7 @@ class Benchmark {
|
||||
int64_t bytes = 0;
|
||||
for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) {
|
||||
bytes += iter->key().size() + iter->value().size();
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
thread->stats.FinishedOps(db, 1);
|
||||
++i;
|
||||
}
|
||||
delete iter;
|
||||
@@ -1998,13 +2067,24 @@ class Benchmark {
|
||||
|
||||
Duration duration(FLAGS_duration, reads_);
|
||||
while (!duration.Done(1)) {
|
||||
DB* db = SelectDB(thread);
|
||||
GenerateKeyFromInt(thread->rand.Next() % FLAGS_num, FLAGS_num, &key);
|
||||
DBWithColumnFamilies* db_with_cfh = SelectDBWithCfh(thread);
|
||||
// We use same key_rand as seed for key and column family so that we can
|
||||
// deterministically find the cfh corresponding to a particular key, as it
|
||||
// is done in DoWrite method.
|
||||
int64_t key_rand = thread->rand.Next() % FLAGS_num;
|
||||
GenerateKeyFromInt(key_rand, FLAGS_num, &key);
|
||||
read++;
|
||||
if (db->Get(options, key, &value).ok()) {
|
||||
Status s;
|
||||
if (FLAGS_num_column_families > 1) {
|
||||
s = db_with_cfh->db->Get(options,
|
||||
db_with_cfh->cfh[key_rand % db_with_cfh->cfh.size()], key, &value);
|
||||
} else {
|
||||
s = db_with_cfh->db->Get(options, key, &value);
|
||||
}
|
||||
if (s.ok()) {
|
||||
found++;
|
||||
}
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
thread->stats.FinishedOps(db_with_cfh->db, 1);
|
||||
}
|
||||
|
||||
char msg[100];
|
||||
@@ -2046,6 +2126,7 @@ class Benchmark {
|
||||
++found;
|
||||
}
|
||||
}
|
||||
thread->stats.FinishedOps(db, entries_per_batch_);
|
||||
}
|
||||
for (auto& k : keys) {
|
||||
delete k.data();
|
||||
@@ -2084,11 +2165,11 @@ class Benchmark {
|
||||
|
||||
Iterator* single_iter = nullptr;
|
||||
std::vector<Iterator*> multi_iters;
|
||||
if (db_ != nullptr) {
|
||||
single_iter = db_->NewIterator(options);
|
||||
if (db_.db != nullptr) {
|
||||
single_iter = db_.db->NewIterator(options);
|
||||
} else {
|
||||
for (DB* db : multi_dbs_) {
|
||||
multi_iters.push_back(db->NewIterator(options));
|
||||
for (const auto& db_with_cfh : multi_dbs_) {
|
||||
multi_iters.push_back(db_with_cfh.db->NewIterator(options));
|
||||
}
|
||||
}
|
||||
uint64_t last_refresh = FLAGS_env->NowMicros();
|
||||
@@ -2101,16 +2182,16 @@ class Benchmark {
|
||||
if (!FLAGS_use_tailing_iterator && FLAGS_iter_refresh_interval_us >= 0) {
|
||||
uint64_t now = FLAGS_env->NowMicros();
|
||||
if (now - last_refresh > (uint64_t)FLAGS_iter_refresh_interval_us) {
|
||||
if (db_ != nullptr) {
|
||||
if (db_.db != nullptr) {
|
||||
delete single_iter;
|
||||
single_iter = db_->NewIterator(options);
|
||||
single_iter = db_.db->NewIterator(options);
|
||||
} else {
|
||||
for (auto iter : multi_iters) {
|
||||
delete iter;
|
||||
}
|
||||
multi_iters.clear();
|
||||
for (DB* db : multi_dbs_) {
|
||||
multi_iters.push_back(db->NewIterator(options));
|
||||
for (const auto& db_with_cfh : multi_dbs_) {
|
||||
multi_iters.push_back(db_with_cfh.db->NewIterator(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2128,7 +2209,7 @@ class Benchmark {
|
||||
if (iter_to_use->Valid() && iter_to_use->key().compare(key) == 0) {
|
||||
found++;
|
||||
}
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
thread->stats.FinishedOps(db_.db, 1);
|
||||
}
|
||||
delete single_iter;
|
||||
for (auto iter : multi_iters) {
|
||||
@@ -2228,7 +2309,7 @@ class Benchmark {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
thread->stats.FinishedOps(db_.db, 1);
|
||||
|
||||
++num_writes;
|
||||
if (writes_per_second_by_10 && num_writes >= writes_per_second_by_10) {
|
||||
@@ -2388,7 +2469,7 @@ class Benchmark {
|
||||
deletes_done++;
|
||||
}
|
||||
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
thread->stats.FinishedOps(db_.db, 1);
|
||||
}
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg),
|
||||
@@ -2527,7 +2608,7 @@ class Benchmark {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
thread->stats.FinishedOps(db, 1);
|
||||
}
|
||||
|
||||
char msg[100];
|
||||
@@ -2563,7 +2644,7 @@ class Benchmark {
|
||||
fprintf(stderr, "merge error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
thread->stats.FinishedOps(db, 1);
|
||||
}
|
||||
|
||||
// Print some statistics
|
||||
@@ -2624,7 +2705,7 @@ class Benchmark {
|
||||
|
||||
}
|
||||
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
thread->stats.FinishedOps(db, 1);
|
||||
}
|
||||
|
||||
char msg[100];
|
||||
@@ -2641,11 +2722,11 @@ class Benchmark {
|
||||
}
|
||||
|
||||
void PrintStats(const char* key) {
|
||||
if (db_ != nullptr) {
|
||||
PrintStats(db_, key, false);
|
||||
if (db_.db != nullptr) {
|
||||
PrintStats(db_.db, key, false);
|
||||
}
|
||||
for (DB* db : multi_dbs_) {
|
||||
PrintStats(db, key, true);
|
||||
for (const auto& db_with_cfh : multi_dbs_) {
|
||||
PrintStats(db_with_cfh.db, key, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,10 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
int DBImpl::IsFileDeletionsEnabled() const {
|
||||
return disable_delete_obsolete_files_;
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable) {
|
||||
|
||||
+377
-251
File diff suppressed because it is too large
Load Diff
+35
-5
@@ -127,6 +127,7 @@ class DBImpl : public DB {
|
||||
#ifndef ROCKSDB_LITE
|
||||
virtual Status DisableFileDeletions();
|
||||
virtual Status EnableFileDeletions(bool force);
|
||||
virtual int IsFileDeletionsEnabled() const;
|
||||
// All the returned filenames start with "/"
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* manifest_file_size,
|
||||
@@ -309,6 +310,7 @@ class DBImpl : public DB {
|
||||
friend struct SuperVersion;
|
||||
struct CompactionState;
|
||||
struct Writer;
|
||||
struct WriteContext;
|
||||
|
||||
Status NewDB();
|
||||
|
||||
@@ -347,13 +349,38 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t SlowdownAmount(int n, double bottom, double top);
|
||||
|
||||
// TODO(icanadi) free superversion_to_free and old_log outside of mutex
|
||||
// Before applying write operation (such as DBImpl::Write, DBImpl::Flush)
|
||||
// thread should grab the mutex_ and be the first on writers queue.
|
||||
// BeginWrite is used for it.
|
||||
// Be aware! Writer's job can be done by other thread (see DBImpl::Write
|
||||
// for examples), so check it via w.done before applying changes.
|
||||
//
|
||||
// Writer* w: writer to be placed in the queue
|
||||
// uint64_t expiration_time: maximum time to be in the queue
|
||||
// See also: EndWrite
|
||||
Status BeginWrite(Writer* w, uint64_t expiration_time);
|
||||
|
||||
// After doing write job, we need to remove already used writers from
|
||||
// writers_ queue and notify head of the queue about it.
|
||||
// EndWrite is used for this.
|
||||
//
|
||||
// Writer* w: Writer, that was added by BeginWrite function
|
||||
// Writer* last_writer: Since we can join a few Writers (as DBImpl::Write
|
||||
// does)
|
||||
// we should pass last_writer as a parameter to
|
||||
// EndWrite
|
||||
// (if you don't touch other writers, just pass w)
|
||||
// Status status: Status of write operation
|
||||
// See also: BeginWrite
|
||||
void EndWrite(Writer* w, Writer* last_writer, Status status);
|
||||
|
||||
Status MakeRoomForWrite(ColumnFamilyData* cfd,
|
||||
bool force /* flush even if there is room? */,
|
||||
autovector<SuperVersion*>* superversions_to_free,
|
||||
autovector<log::Writer*>* logs_to_free,
|
||||
WriteContext* context,
|
||||
uint64_t expiration_time);
|
||||
|
||||
Status SetNewMemtableAndNewLogFile(ColumnFamilyData* cfd,
|
||||
WriteContext* context);
|
||||
|
||||
void BuildBatchGroup(Writer** last_writer,
|
||||
autovector<WriteBatch*>* write_batch_group);
|
||||
|
||||
@@ -643,7 +670,6 @@ class DBImpl : public DB {
|
||||
// it is not equal to src.info_log.
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
const InternalKeyComparator* icmp,
|
||||
const InternalFilterPolicy* ipolicy,
|
||||
const Options& src);
|
||||
extern DBOptions SanitizeOptions(const std::string& db, const DBOptions& src);
|
||||
|
||||
@@ -654,4 +680,8 @@ static void ClipToRange(T* ptr, V minvalue, V maxvalue) {
|
||||
if (static_cast<V>(*ptr) < minvalue) *ptr = minvalue;
|
||||
}
|
||||
|
||||
// Dump db file summary, implemented in util/
|
||||
extern void DumpDBFileSummary(const DBOptions& options,
|
||||
const std::string& dbname);
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
|
||||
@@ -74,6 +74,8 @@ class DBImplReadOnly : public DBImpl {
|
||||
uint32_t target_path_id = 0) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
virtual Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
@@ -85,6 +87,8 @@ class DBImplReadOnly : public DBImpl {
|
||||
bool flush_memtable = true) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
using DBImpl::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
|
||||
+20
-11
@@ -194,9 +194,8 @@ void DBIter::Next() {
|
||||
// NOTE: In between, saved_key_ can point to a user key that has
|
||||
// a delete marker
|
||||
inline void DBIter::FindNextUserEntry(bool skipping) {
|
||||
PERF_TIMER_AUTO(find_next_user_entry_time);
|
||||
PERF_TIMER_GUARD(find_next_user_entry_time);
|
||||
FindNextUserEntryInternal(skipping);
|
||||
PERF_TIMER_STOP(find_next_user_entry_time);
|
||||
}
|
||||
|
||||
// Actual implementation of DBIter::FindNextUserEntry()
|
||||
@@ -552,12 +551,17 @@ void DBIter::FindParseableKey(ParsedInternalKey* ikey, Direction direction) {
|
||||
}
|
||||
|
||||
void DBIter::Seek(const Slice& target) {
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
|
||||
saved_key_.Clear();
|
||||
// now savved_key is used to store internal key.
|
||||
saved_key_.SetInternalKey(target, sequence_);
|
||||
PERF_TIMER_AUTO(seek_internal_seek_time);
|
||||
iter_->Seek(saved_key_.GetKey());
|
||||
PERF_TIMER_STOP(seek_internal_seek_time);
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->Seek(saved_key_.GetKey());
|
||||
}
|
||||
|
||||
if (iter_->Valid()) {
|
||||
direction_ = kForward;
|
||||
ClearSavedValue();
|
||||
@@ -575,9 +579,12 @@ void DBIter::SeekToFirst() {
|
||||
}
|
||||
direction_ = kForward;
|
||||
ClearSavedValue();
|
||||
PERF_TIMER_AUTO(seek_internal_seek_time);
|
||||
iter_->SeekToFirst();
|
||||
PERF_TIMER_STOP(seek_internal_seek_time);
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->SeekToFirst();
|
||||
}
|
||||
|
||||
if (iter_->Valid()) {
|
||||
FindNextUserEntry(false /* not skipping */);
|
||||
} else {
|
||||
@@ -593,9 +600,11 @@ void DBIter::SeekToLast() {
|
||||
}
|
||||
direction_ = kReverse;
|
||||
ClearSavedValue();
|
||||
PERF_TIMER_AUTO(seek_internal_seek_time);
|
||||
iter_->SeekToLast();
|
||||
PERF_TIMER_STOP(seek_internal_seek_time);
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->SeekToLast();
|
||||
}
|
||||
|
||||
PrevInternal();
|
||||
}
|
||||
|
||||
+212
-57
@@ -30,6 +30,7 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/plain_table_factory.h"
|
||||
#include "util/hash.h"
|
||||
@@ -101,6 +102,11 @@ class AtomicCounter {
|
||||
count_ = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct OptionsOverride {
|
||||
std::shared_ptr<const FilterPolicy> filter_policy = nullptr;
|
||||
};
|
||||
|
||||
} // namespace anon
|
||||
|
||||
static std::string Key(int i) {
|
||||
@@ -303,9 +309,6 @@ class SpecialEnv : public EnvWrapper {
|
||||
};
|
||||
|
||||
class DBTest {
|
||||
private:
|
||||
const FilterPolicy* filter_policy_;
|
||||
|
||||
protected:
|
||||
// Sequence of option configurations to try
|
||||
enum OptionConfig {
|
||||
@@ -359,9 +362,9 @@ class DBTest {
|
||||
kSkipFIFOCompaction = 128,
|
||||
};
|
||||
|
||||
|
||||
DBTest() : option_config_(kDefault),
|
||||
env_(new SpecialEnv(Env::Default())) {
|
||||
filter_policy_ = NewBloomFilterPolicy(10);
|
||||
dbname_ = test::TmpDir() + "/db_test";
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
db_ = nullptr;
|
||||
@@ -377,7 +380,6 @@ class DBTest {
|
||||
options.db_paths.emplace_back(dbname_ + "_4", 0);
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
delete env_;
|
||||
delete filter_policy_;
|
||||
}
|
||||
|
||||
// Switch to a fresh database with the next option configuration to
|
||||
@@ -445,14 +447,19 @@ class DBTest {
|
||||
}
|
||||
|
||||
// Return the current option configuration.
|
||||
Options CurrentOptions() {
|
||||
Options CurrentOptions(
|
||||
const anon::OptionsOverride& options_override = anon::OptionsOverride()) {
|
||||
Options options;
|
||||
return CurrentOptions(options);
|
||||
return CurrentOptions(options, options_override);
|
||||
}
|
||||
|
||||
Options CurrentOptions(const Options& defaultOptions) {
|
||||
Options CurrentOptions(
|
||||
const Options& defaultOptions,
|
||||
const anon::OptionsOverride& options_override = anon::OptionsOverride()) {
|
||||
// this redudant copy is to minimize code change w/o having lint error.
|
||||
Options options = defaultOptions;
|
||||
BlockBasedTableOptions table_options;
|
||||
bool set_block_based_table_factory = true;
|
||||
switch (option_config_) {
|
||||
case kHashSkipList:
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
@@ -464,18 +471,20 @@ class DBTest {
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.allow_mmap_reads = true;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
set_block_based_table_factory = false;
|
||||
break;
|
||||
case kPlainTableAllBytesPrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
options.allow_mmap_reads = true;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
set_block_based_table_factory = false;
|
||||
break;
|
||||
case kMergePut:
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
break;
|
||||
case kFilter:
|
||||
options.filter_policy = filter_policy_;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
break;
|
||||
case kUncompressed:
|
||||
options.compression = kNoCompression;
|
||||
@@ -520,15 +529,13 @@ class DBTest {
|
||||
break;
|
||||
case kCompressedBlockCache:
|
||||
options.allow_mmap_writes = true;
|
||||
options.block_cache_compressed = NewLRUCache(8*1024*1024);
|
||||
table_options.block_cache_compressed = NewLRUCache(8*1024*1024);
|
||||
break;
|
||||
case kInfiniteMaxOpenFiles:
|
||||
options.max_open_files = -1;
|
||||
break;
|
||||
case kxxHashChecksum: {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.checksum = kxxHash;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
break;
|
||||
}
|
||||
case kFIFOCompaction: {
|
||||
@@ -536,22 +543,25 @@ class DBTest {
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithPrefixHashIndex: {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithWholeKeyHashIndex: {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (options_override.filter_policy) {
|
||||
table_options.filter_policy = options_override.filter_policy;
|
||||
}
|
||||
if (set_block_based_table_factory) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -651,7 +661,6 @@ class DBTest {
|
||||
opts.create_if_missing = true;
|
||||
}
|
||||
last_options_ = opts;
|
||||
|
||||
return DB::Open(opts, dbname_, &db_);
|
||||
}
|
||||
|
||||
@@ -1140,6 +1149,31 @@ TEST(DBTest, Empty) {
|
||||
|
||||
ASSERT_EQ("v1", Get(1, "foo"));
|
||||
env_->delay_sstable_sync_.Release_Store(nullptr); // Release sync calls
|
||||
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("1", num);
|
||||
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("2", num);
|
||||
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("3", num);
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions(false));
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("2", num);
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("0", num);
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
@@ -1161,18 +1195,27 @@ TEST(DBTest, ReadOnlyDB) {
|
||||
}
|
||||
ASSERT_EQ(count, 2);
|
||||
delete iter;
|
||||
Close();
|
||||
|
||||
// Reopen and flush memtable.
|
||||
Reopen();
|
||||
Flush();
|
||||
Close();
|
||||
// Now check keys in read only mode.
|
||||
ASSERT_OK(ReadOnlyReopen(&options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
}
|
||||
|
||||
// Make sure that when options.block_cache is set, after a new table is
|
||||
// created its index/filter blocks are added to block cache.
|
||||
TEST(DBTest, IndexAndFilterBlocksOfNewTableAddedToCache) {
|
||||
Options options = CurrentOptions();
|
||||
std::unique_ptr<const FilterPolicy> filter_policy(NewBloomFilterPolicy(20));
|
||||
options.filter_policy = filter_policy.get();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, &options);
|
||||
|
||||
@@ -1458,8 +1501,9 @@ TEST(DBTest, KeyMayExist) {
|
||||
do {
|
||||
ReadOptions ropts;
|
||||
std::string value;
|
||||
Options options = CurrentOptions();
|
||||
options.filter_policy = NewBloomFilterPolicy(20);
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
Options options = CurrentOptions(options_override);
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
CreateAndReopenWithCF({"pikachu"}, &options);
|
||||
|
||||
@@ -1510,8 +1554,6 @@ TEST(DBTest, KeyMayExist) {
|
||||
ASSERT_EQ(numopen, TestGetTickerCount(options, NO_FILE_OPENS));
|
||||
ASSERT_EQ(cache_added, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
delete options.filter_policy;
|
||||
|
||||
// KeyMayExist function only checks data in block caches, which is not used
|
||||
// by plain table format.
|
||||
} while (
|
||||
@@ -1586,8 +1628,9 @@ TEST(DBTest, NonBlockingIteration) {
|
||||
// Tests Writebatch consistency and proper delete behaviour
|
||||
TEST(DBTest, FilterDeletes) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.filter_policy = NewBloomFilterPolicy(20);
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
Options options = CurrentOptions(options_override);
|
||||
options.filter_deletes = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, &options);
|
||||
WriteBatch batch;
|
||||
@@ -1617,8 +1660,6 @@ TEST(DBTest, FilterDeletes) {
|
||||
dbfull()->Write(WriteOptions(), &batch);
|
||||
ASSERT_EQ(AllEntriesFor("c", 1), "[ DEL, d ]"); // Delete issued
|
||||
batch.Clear();
|
||||
|
||||
delete options.filter_policy;
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
@@ -3332,32 +3373,37 @@ TEST(DBTest, CompressedCache) {
|
||||
// Iteration 4: both block cache and compressed cache, but DB is not
|
||||
// compressed
|
||||
for (int iter = 0; iter < 4; iter++) {
|
||||
Options options = CurrentOptions();
|
||||
Options options;
|
||||
options.write_buffer_size = 64*1024; // small write buffer
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
switch (iter) {
|
||||
case 0:
|
||||
// only uncompressed block cache
|
||||
options.block_cache = NewLRUCache(8*1024);
|
||||
options.block_cache_compressed = nullptr;
|
||||
table_options.block_cache = NewLRUCache(8*1024);
|
||||
table_options.block_cache_compressed = nullptr;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
break;
|
||||
case 1:
|
||||
// no block cache, only compressed cache
|
||||
options.no_block_cache = true;
|
||||
options.block_cache = nullptr;
|
||||
options.block_cache_compressed = NewLRUCache(8*1024);
|
||||
table_options.no_block_cache = true;
|
||||
table_options.block_cache = nullptr;
|
||||
table_options.block_cache_compressed = NewLRUCache(8*1024);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
break;
|
||||
case 2:
|
||||
// both compressed and uncompressed block cache
|
||||
options.block_cache = NewLRUCache(1024);
|
||||
options.block_cache_compressed = NewLRUCache(8*1024);
|
||||
table_options.block_cache = NewLRUCache(1024);
|
||||
table_options.block_cache_compressed = NewLRUCache(8*1024);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
break;
|
||||
case 3:
|
||||
// both block cache and compressed cache, but DB is not compressed
|
||||
// also, make block cache sizes bigger, to trigger block cache hits
|
||||
options.block_cache = NewLRUCache(1024 * 1024);
|
||||
options.block_cache_compressed = NewLRUCache(8 * 1024 * 1024);
|
||||
table_options.block_cache = NewLRUCache(1024 * 1024);
|
||||
table_options.block_cache_compressed = NewLRUCache(8 * 1024 * 1024);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.compression = kNoCompression;
|
||||
break;
|
||||
default:
|
||||
@@ -3366,9 +3412,11 @@ TEST(DBTest, CompressedCache) {
|
||||
CreateAndReopenWithCF({"pikachu"}, &options);
|
||||
// default column family doesn't have block cache
|
||||
Options no_block_cache_opts;
|
||||
no_block_cache_opts.no_block_cache = true;
|
||||
no_block_cache_opts.statistics = options.statistics;
|
||||
options = CurrentOptions(options);
|
||||
BlockBasedTableOptions table_options_no_bc;
|
||||
table_options_no_bc.no_block_cache = true;
|
||||
no_block_cache_opts.table_factory.reset(
|
||||
NewBlockBasedTableFactory(table_options_no_bc));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"},
|
||||
{&no_block_cache_opts, &options});
|
||||
|
||||
@@ -5228,7 +5276,6 @@ TEST(DBTest, CustomComparator) {
|
||||
new_options = CurrentOptions();
|
||||
new_options.create_if_missing = true;
|
||||
new_options.comparator = &cmp;
|
||||
new_options.filter_policy = nullptr; // Cannot use bloom filters
|
||||
new_options.write_buffer_size = 1000; // Compact more often
|
||||
new_options = CurrentOptions(new_options);
|
||||
DestroyAndReopen(&new_options);
|
||||
@@ -5636,11 +5683,16 @@ TEST(DBTest, FilesDeletedAfterCompaction) {
|
||||
|
||||
TEST(DBTest, BloomFilter) {
|
||||
do {
|
||||
env_->count_random_reads_ = true;
|
||||
Options options = CurrentOptions();
|
||||
env_->count_random_reads_ = true;
|
||||
options.env = env_;
|
||||
options.no_block_cache = true;
|
||||
options.filter_policy = NewBloomFilterPolicy(10);
|
||||
// ChangeCompactOptions() only changes compaction style, which does not
|
||||
// trigger reset of table_factory
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, &options);
|
||||
|
||||
// Populate multiple layers
|
||||
@@ -5678,7 +5730,6 @@ TEST(DBTest, BloomFilter) {
|
||||
|
||||
env_->delay_sstable_sync_.Release_Store(nullptr);
|
||||
Close();
|
||||
delete options.filter_policy;
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
@@ -5898,7 +5949,8 @@ TEST(DBTest, CompactOnFlush) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::vector<std::uint64_t> ListLogFiles(Env* env, const std::string& path) {
|
||||
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> log_files;
|
||||
env->GetChildren(path, &files);
|
||||
@@ -5906,15 +5958,45 @@ std::vector<std::uint64_t> ListLogFiles(Env* env, const std::string& path) {
|
||||
FileType type;
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
if (ParseFileName(files[i], &number, &type)) {
|
||||
if (type == kLogFile) {
|
||||
if (type == expected_file_type) {
|
||||
log_files.push_back(number);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::move(log_files);
|
||||
}
|
||||
|
||||
std::vector<std::uint64_t> ListLogFiles(Env* env, const std::string& path) {
|
||||
return ListSpecificFiles(env, path, kLogFile);
|
||||
}
|
||||
|
||||
std::vector<std::uint64_t> ListTableFiles(Env* env, const std::string& path) {
|
||||
return ListSpecificFiles(env, path, kTableFile);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(DBTest, FlushOneColumnFamily) {
|
||||
Options options;
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
&options);
|
||||
|
||||
ASSERT_OK(Put(0, "Default", "Default"));
|
||||
ASSERT_OK(Put(1, "pikachu", "pikachu"));
|
||||
ASSERT_OK(Put(2, "ilya", "ilya"));
|
||||
ASSERT_OK(Put(3, "muromec", "muromec"));
|
||||
ASSERT_OK(Put(4, "dobrynia", "dobrynia"));
|
||||
ASSERT_OK(Put(5, "nikitich", "nikitich"));
|
||||
ASSERT_OK(Put(6, "alyosha", "alyosha"));
|
||||
ASSERT_OK(Put(7, "popovich", "popovich"));
|
||||
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
Flush(i);
|
||||
auto tables = ListTableFiles(env_, dbname_);
|
||||
ASSERT_EQ(tables.size(), i + 1U);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DBTest, WALArchivalTtl) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
@@ -6023,6 +6105,52 @@ TEST(DBTest, WALArchivalSizeLimit) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST(DBTest, PurgeInfoLogs) {
|
||||
Options options = CurrentOptions();
|
||||
options.keep_log_file_num = 5;
|
||||
options.create_if_missing = true;
|
||||
for (int mode = 0; mode <= 1; mode++) {
|
||||
if (mode == 1) {
|
||||
options.db_log_dir = dbname_ + "_logs";
|
||||
env_->CreateDirIfMissing(options.db_log_dir);
|
||||
} else {
|
||||
options.db_log_dir = "";
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Reopen(&options);
|
||||
}
|
||||
|
||||
std::vector<std::string> files;
|
||||
env_->GetChildren(options.db_log_dir.empty() ? dbname_ : options.db_log_dir,
|
||||
&files);
|
||||
int info_log_count = 0;
|
||||
for (std::string file : files) {
|
||||
if (file.find("LOG") != std::string::npos) {
|
||||
info_log_count++;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(5, info_log_count);
|
||||
|
||||
Destroy(&options);
|
||||
// For mode (1), test DestoryDB() to delete all the logs under DB dir.
|
||||
// For mode (2), no info log file should have been put under DB dir.
|
||||
std::vector<std::string> db_files;
|
||||
env_->GetChildren(dbname_, &db_files);
|
||||
for (std::string file : db_files) {
|
||||
ASSERT_TRUE(file.find("LOG") == std::string::npos);
|
||||
}
|
||||
|
||||
if (mode == 1) {
|
||||
// Cleaning up
|
||||
env_->GetChildren(options.db_log_dir, &files);
|
||||
for (std::string file : files) {
|
||||
env_->DeleteFile(options.db_log_dir + "/" + file);
|
||||
}
|
||||
env_->DeleteDir(options.db_log_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
SequenceNumber ReadRecords(
|
||||
std::unique_ptr<TransactionLogIterator>& iter,
|
||||
@@ -6367,13 +6495,26 @@ static void MTThreadBody(void* arg) {
|
||||
// into each of the CFs
|
||||
// We add some padding for force compactions.
|
||||
int unique_id = rnd.Uniform(1000000);
|
||||
WriteBatch batch;
|
||||
for (int cf = 0; cf < kColumnFamilies; ++cf) {
|
||||
snprintf(valbuf, sizeof(valbuf), "%d.%d.%d.%d.%-1000d", key, id,
|
||||
static_cast<int>(counter), cf, unique_id);
|
||||
batch.Put(t->state->test->handles_[cf], Slice(keybuf), Slice(valbuf));
|
||||
|
||||
// Half of the time directly use WriteBatch. Half of the time use
|
||||
// WriteBatchWithIndex.
|
||||
if (rnd.OneIn(2)) {
|
||||
WriteBatch batch;
|
||||
for (int cf = 0; cf < kColumnFamilies; ++cf) {
|
||||
snprintf(valbuf, sizeof(valbuf), "%d.%d.%d.%d.%-1000d", key, id,
|
||||
static_cast<int>(counter), cf, unique_id);
|
||||
batch.Put(t->state->test->handles_[cf], Slice(keybuf), Slice(valbuf));
|
||||
}
|
||||
ASSERT_OK(db->Write(WriteOptions(), &batch));
|
||||
} else {
|
||||
WriteBatchWithIndex batch(db->GetOptions().comparator);
|
||||
for (int cf = 0; cf < kColumnFamilies; ++cf) {
|
||||
snprintf(valbuf, sizeof(valbuf), "%d.%d.%d.%d.%-1000d", key, id,
|
||||
static_cast<int>(counter), cf, unique_id);
|
||||
batch.Put(t->state->test->handles_[cf], Slice(keybuf), Slice(valbuf));
|
||||
}
|
||||
ASSERT_OK(db->Write(WriteOptions(), batch.GetWriteBatch()));
|
||||
}
|
||||
ASSERT_OK(db->Write(WriteOptions(), &batch));
|
||||
} else {
|
||||
// Read a value and verify that it matches the pattern written above
|
||||
// and that writes to all column families were atomic (unique_id is the
|
||||
@@ -7054,15 +7195,18 @@ TEST(DBTest, PrefixScan) {
|
||||
env_->count_random_reads_ = true;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.no_block_cache = true;
|
||||
options.filter_policy = NewBloomFilterPolicy(10);
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
|
||||
options.whole_key_filtering = false;
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_background_compactions = 2;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
// 11 RAND I/Os
|
||||
DestroyAndReopen(&options);
|
||||
PrefixScanInit(this);
|
||||
@@ -7080,7 +7224,6 @@ TEST(DBTest, PrefixScan) {
|
||||
ASSERT_EQ(count, 2);
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), 2);
|
||||
Close();
|
||||
delete options.filter_policy;
|
||||
}
|
||||
|
||||
TEST(DBTest, TailingIteratorSingle) {
|
||||
@@ -7411,7 +7554,7 @@ TEST(DBTest, SimpleWriteTimeoutTest) {
|
||||
ASSERT_OK(Put(Key(2), Key(2) + std::string(100000, 'v'), write_opt));
|
||||
// As the only two write buffers are full in this moment, the third
|
||||
// Put is expected to be timed-out.
|
||||
write_opt.timeout_hint_us = 300;
|
||||
write_opt.timeout_hint_us = 50;
|
||||
ASSERT_TRUE(
|
||||
Put(Key(3), Key(3) + std::string(100000, 'v'), write_opt).IsTimedOut());
|
||||
}
|
||||
@@ -7588,6 +7731,18 @@ TEST(DBTest, RateLimitingTest) {
|
||||
ASSERT_TRUE(ratio < 0.6);
|
||||
}
|
||||
|
||||
TEST(DBTest, TableOptionsSanitizeTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(&options);
|
||||
ASSERT_EQ(db_->GetOptions().allow_mmap_reads, false);
|
||||
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
Destroy(&options);
|
||||
ASSERT_TRUE(TryReopen(&options).IsNotSupported());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -127,26 +127,6 @@ void InternalKeyComparator::FindShortSuccessor(std::string* key) const {
|
||||
}
|
||||
}
|
||||
|
||||
const char* InternalFilterPolicy::Name() const {
|
||||
return user_policy_->Name();
|
||||
}
|
||||
|
||||
void InternalFilterPolicy::CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const {
|
||||
// We rely on the fact that the code in table.cc does not mind us
|
||||
// adjusting keys[].
|
||||
Slice* mkey = const_cast<Slice*>(keys);
|
||||
for (int i = 0; i < n; i++) {
|
||||
mkey[i] = ExtractUserKey(keys[i]);
|
||||
// TODO(sanjay): Suppress dups?
|
||||
}
|
||||
user_policy_->CreateFilter(keys, n, dst);
|
||||
}
|
||||
|
||||
bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const {
|
||||
return user_policy_->KeyMayMatch(ExtractUserKey(key), f);
|
||||
}
|
||||
|
||||
LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
|
||||
size_t usize = user_key.size();
|
||||
size_t needed = usize + 13; // A conservative estimate
|
||||
|
||||
+8
-11
@@ -124,17 +124,6 @@ class InternalKeyComparator : public Comparator {
|
||||
int Compare(const ParsedInternalKey& a, const ParsedInternalKey& b) const;
|
||||
};
|
||||
|
||||
// Filter policy wrapper that converts from internal keys to user keys
|
||||
class InternalFilterPolicy : public FilterPolicy {
|
||||
private:
|
||||
const FilterPolicy* const user_policy_;
|
||||
public:
|
||||
explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { }
|
||||
virtual const char* Name() const;
|
||||
virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const;
|
||||
virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const;
|
||||
};
|
||||
|
||||
// Modules in this directory should keep internal keys wrapped inside
|
||||
// the following class instead of plain strings so that we do not
|
||||
// incorrectly use string comparisons instead of an InternalKeyComparator.
|
||||
@@ -401,4 +390,12 @@ class InternalKeySliceTransform : public SliceTransform {
|
||||
const SliceTransform* const transform_;
|
||||
};
|
||||
|
||||
// Read record from a write batch piece from input.
|
||||
// tag, column_family, key, value and blob are return values. Callers own the
|
||||
// Slice they point to.
|
||||
// Tag is defined as ValueType.
|
||||
// input will be advanced to after the record.
|
||||
extern Status ReadRecordFromWriteBatch(Slice* input, char* tag,
|
||||
uint32_t* column_family, Slice* key,
|
||||
Slice* value, Slice* blob);
|
||||
} // namespace rocksdb
|
||||
|
||||
+63
-31
@@ -6,8 +6,9 @@
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include "db/filename.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
@@ -19,14 +20,16 @@
|
||||
namespace rocksdb {
|
||||
|
||||
// Given a path, flatten the path name by replacing all chars not in
|
||||
// {[0-9,a-z,A-Z,-,_,.]} with _. And append '\0' at the end.
|
||||
// {[0-9,a-z,A-Z,-,_,.]} with _. And append '_LOG\0' at the end.
|
||||
// Return the number of chars stored in dest not including the trailing '\0'.
|
||||
static int FlattenPath(const std::string& path, char* dest, int len) {
|
||||
int write_idx = 0;
|
||||
int i = 0;
|
||||
int src_len = path.size();
|
||||
static size_t GetInfoLogPrefix(const std::string& path, char* dest, int len) {
|
||||
const char suffix[] = "_LOG";
|
||||
|
||||
while (i < src_len && write_idx < len - 1) {
|
||||
size_t write_idx = 0;
|
||||
size_t i = 0;
|
||||
size_t src_len = path.size();
|
||||
|
||||
while (i < src_len && write_idx < len - sizeof(suffix)) {
|
||||
if ((path[i] >= 'a' && path[i] <= 'z') ||
|
||||
(path[i] >= '0' && path[i] <= '9') ||
|
||||
(path[i] >= 'A' && path[i] <= 'Z') ||
|
||||
@@ -40,8 +43,10 @@ static int FlattenPath(const std::string& path, char* dest, int len) {
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
dest[write_idx] = '\0';
|
||||
assert(sizeof(suffix) <= len - write_idx);
|
||||
// "\0" is automatically added by snprintf
|
||||
snprintf(dest + write_idx, len - write_idx, suffix);
|
||||
write_idx += sizeof(suffix) - 1;
|
||||
return write_idx;
|
||||
}
|
||||
|
||||
@@ -83,11 +88,17 @@ std::string TableFileName(const std::vector<DbPath>& db_paths, uint64_t number,
|
||||
return MakeTableFileName(path, number);
|
||||
}
|
||||
|
||||
std::string FormatFileNumber(uint64_t number, uint32_t path_id) {
|
||||
const size_t kFormatFileNumberBufSize = 38;
|
||||
|
||||
void FormatFileNumber(uint64_t number, uint32_t path_id, char* out_buf,
|
||||
size_t out_buf_size) {
|
||||
if (path_id == 0) {
|
||||
return std::to_string(number);
|
||||
snprintf(out_buf, out_buf_size, "%" PRIu64, number);
|
||||
} else {
|
||||
return std::to_string(number) + "(path " + std::to_string(path_id) + ")";
|
||||
snprintf(out_buf, out_buf_size, "%" PRIu64
|
||||
"(path "
|
||||
"%" PRIu32 ")",
|
||||
number, path_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,14 +122,26 @@ std::string TempFileName(const std::string& dbname, uint64_t number) {
|
||||
return MakeFileName(dbname, number, "dbtmp");
|
||||
}
|
||||
|
||||
InfoLogPrefix::InfoLogPrefix(bool has_log_dir,
|
||||
const std::string& db_absolute_path) {
|
||||
if (!has_log_dir) {
|
||||
const char kInfoLogPrefix[] = "LOG";
|
||||
// "\0" is automatically added to the end
|
||||
snprintf(buf, sizeof(buf), kInfoLogPrefix);
|
||||
prefix = Slice(buf, sizeof(kInfoLogPrefix) - 1);
|
||||
} else {
|
||||
size_t len = GetInfoLogPrefix(db_absolute_path, buf, sizeof(buf));
|
||||
prefix = Slice(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
std::string InfoLogFileName(const std::string& dbname,
|
||||
const std::string& db_path, const std::string& log_dir) {
|
||||
if (log_dir.empty())
|
||||
return dbname + "/LOG";
|
||||
|
||||
char flatten_db_path[256];
|
||||
FlattenPath(db_path, flatten_db_path, 256);
|
||||
return log_dir + "/" + flatten_db_path + "_LOG";
|
||||
InfoLogPrefix info_log_prefix(true, db_path);
|
||||
return log_dir + "/" + info_log_prefix.buf;
|
||||
}
|
||||
|
||||
// Return the name of the old info log file for "dbname".
|
||||
@@ -130,9 +153,8 @@ std::string OldInfoLogFileName(const std::string& dbname, uint64_t ts,
|
||||
if (log_dir.empty())
|
||||
return dbname + "/LOG.old." + buf;
|
||||
|
||||
char flatten_db_path[256];
|
||||
FlattenPath(db_path, flatten_db_path, 256);
|
||||
return log_dir + "/" + flatten_db_path + "_LOG.old." + buf;
|
||||
InfoLogPrefix info_log_prefix(true, db_path);
|
||||
return log_dir + "/" + info_log_prefix.buf + ".old." + buf;
|
||||
}
|
||||
|
||||
std::string MetaDatabaseName(const std::string& dbname, uint64_t number) {
|
||||
@@ -150,8 +172,8 @@ std::string IdentityFileName(const std::string& dbname) {
|
||||
// dbname/IDENTITY
|
||||
// dbname/CURRENT
|
||||
// dbname/LOCK
|
||||
// dbname/LOG
|
||||
// dbname/LOG.old.[0-9]+
|
||||
// dbname/<info_log_name_prefix>
|
||||
// dbname/<info_log_name_prefix>.old.[0-9]+
|
||||
// dbname/MANIFEST-[0-9]+
|
||||
// dbname/[0-9]+.(log|sst)
|
||||
// dbname/METADB-[0-9]+
|
||||
@@ -160,6 +182,12 @@ bool ParseFileName(const std::string& fname,
|
||||
uint64_t* number,
|
||||
FileType* type,
|
||||
WalFileType* log_type) {
|
||||
return ParseFileName(fname, number, "", type, log_type);
|
||||
}
|
||||
|
||||
bool ParseFileName(const std::string& fname, uint64_t* number,
|
||||
const Slice& info_log_name_prefix, FileType* type,
|
||||
WalFileType* log_type) {
|
||||
Slice rest(fname);
|
||||
if (fname.length() > 1 && fname[0] == '/') {
|
||||
rest.remove_prefix(1);
|
||||
@@ -173,18 +201,22 @@ bool ParseFileName(const std::string& fname,
|
||||
} else if (rest == "LOCK") {
|
||||
*number = 0;
|
||||
*type = kDBLockFile;
|
||||
} else if (rest == "LOG" || rest == "LOG.old") {
|
||||
*number = 0;
|
||||
*type = kInfoLogFile;
|
||||
} else if (rest.starts_with("LOG.old.")) {
|
||||
uint64_t ts_suffix;
|
||||
// sizeof also counts the trailing '\0'.
|
||||
rest.remove_prefix(sizeof("LOG.old.") - 1);
|
||||
if (!ConsumeDecimalNumber(&rest, &ts_suffix)) {
|
||||
return false;
|
||||
} else if (info_log_name_prefix.size() > 0 &&
|
||||
rest.starts_with(info_log_name_prefix)) {
|
||||
rest.remove_prefix(info_log_name_prefix.size());
|
||||
if (rest == "" || rest == ".old") {
|
||||
*number = 0;
|
||||
*type = kInfoLogFile;
|
||||
} else if (rest.starts_with(".old.")) {
|
||||
uint64_t ts_suffix;
|
||||
// sizeof also counts the trailing '\0'.
|
||||
rest.remove_prefix(sizeof(".old.") - 1);
|
||||
if (!ConsumeDecimalNumber(&rest, &ts_suffix)) {
|
||||
return false;
|
||||
}
|
||||
*number = ts_suffix;
|
||||
*type = kInfoLogFile;
|
||||
}
|
||||
*number = ts_suffix;
|
||||
*type = kInfoLogFile;
|
||||
} else if (rest.starts_with("MANIFEST-")) {
|
||||
rest.remove_prefix(strlen("MANIFEST-"));
|
||||
uint64_t num;
|
||||
|
||||
+21
-4
@@ -61,7 +61,11 @@ extern std::string MakeTableFileName(const std::string& name, uint64_t number);
|
||||
extern std::string TableFileName(const std::vector<DbPath>& db_paths,
|
||||
uint64_t number, uint32_t path_id);
|
||||
|
||||
extern std::string FormatFileNumber(uint64_t number, uint32_t path_id);
|
||||
// Sufficient buffer size for FormatFileNumber.
|
||||
extern const size_t kFormatFileNumberBufSize;
|
||||
|
||||
extern void FormatFileNumber(uint64_t number, uint32_t path_id, char* out_buf,
|
||||
size_t out_buf_size);
|
||||
|
||||
// Return the name of the descriptor file for the db named by
|
||||
// "dbname" and the specified incarnation number. The result will be
|
||||
@@ -82,6 +86,16 @@ extern std::string LockFileName(const std::string& dbname);
|
||||
// The result will be prefixed with "dbname".
|
||||
extern std::string TempFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
// A helper structure for prefix of info log names.
|
||||
struct InfoLogPrefix {
|
||||
char buf[260];
|
||||
Slice prefix;
|
||||
// Prefix with DB absolute path encoded
|
||||
explicit InfoLogPrefix(bool has_log_dir, const std::string& db_absolute_path);
|
||||
// Default Prefix
|
||||
explicit InfoLogPrefix();
|
||||
};
|
||||
|
||||
// Return the name of the info log file for "dbname".
|
||||
extern std::string InfoLogFileName(const std::string& dbname,
|
||||
const std::string& db_path="", const std::string& log_dir="");
|
||||
@@ -103,10 +117,13 @@ extern std::string IdentityFileName(const std::string& dbname);
|
||||
// If filename is a rocksdb file, store the type of the file in *type.
|
||||
// The number encoded in the filename is stored in *number. If the
|
||||
// filename was successfully parsed, returns true. Else return false.
|
||||
extern bool ParseFileName(const std::string& filename,
|
||||
uint64_t* number,
|
||||
FileType* type,
|
||||
// info_log_name_prefix is the path of info logs.
|
||||
extern bool ParseFileName(const std::string& filename, uint64_t* number,
|
||||
const Slice& info_log_name_prefix, FileType* type,
|
||||
WalFileType* log_type = nullptr);
|
||||
// Same as previous function, but skip info log files.
|
||||
extern bool ParseFileName(const std::string& filename, uint64_t* number,
|
||||
FileType* type, WalFileType* log_type = nullptr);
|
||||
|
||||
// Make the CURRENT file point to the descriptor file with the
|
||||
// specified number.
|
||||
|
||||
+54
-18
@@ -23,30 +23,50 @@ TEST(FileNameTest, Parse) {
|
||||
FileType type;
|
||||
uint64_t number;
|
||||
|
||||
char kDefautInfoLogDir = 1;
|
||||
char kDifferentInfoLogDir = 2;
|
||||
char kNoCheckLogDir = 4;
|
||||
char kAllMode = kDefautInfoLogDir | kDifferentInfoLogDir | kNoCheckLogDir;
|
||||
|
||||
// Successful parses
|
||||
static struct {
|
||||
const char* fname;
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
char mode;
|
||||
} cases[] = {
|
||||
{ "100.log", 100, kLogFile },
|
||||
{ "0.log", 0, kLogFile },
|
||||
{ "0.sst", 0, kTableFile },
|
||||
{ "CURRENT", 0, kCurrentFile },
|
||||
{ "LOCK", 0, kDBLockFile },
|
||||
{ "MANIFEST-2", 2, kDescriptorFile },
|
||||
{ "MANIFEST-7", 7, kDescriptorFile },
|
||||
{ "METADB-2", 2, kMetaDatabase },
|
||||
{ "METADB-7", 7, kMetaDatabase },
|
||||
{ "LOG", 0, kInfoLogFile },
|
||||
{ "LOG.old", 0, kInfoLogFile },
|
||||
{ "18446744073709551615.log", 18446744073709551615ull, kLogFile },
|
||||
};
|
||||
for (unsigned int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
std::string f = cases[i].fname;
|
||||
ASSERT_TRUE(ParseFileName(f, &number, &type)) << f;
|
||||
ASSERT_EQ(cases[i].type, type) << f;
|
||||
ASSERT_EQ(cases[i].number, number) << f;
|
||||
{"100.log", 100, kLogFile, kAllMode},
|
||||
{"0.log", 0, kLogFile, kAllMode},
|
||||
{"0.sst", 0, kTableFile, kAllMode},
|
||||
{"CURRENT", 0, kCurrentFile, kAllMode},
|
||||
{"LOCK", 0, kDBLockFile, kAllMode},
|
||||
{"MANIFEST-2", 2, kDescriptorFile, kAllMode},
|
||||
{"MANIFEST-7", 7, kDescriptorFile, kAllMode},
|
||||
{"METADB-2", 2, kMetaDatabase, kAllMode},
|
||||
{"METADB-7", 7, kMetaDatabase, kAllMode},
|
||||
{"LOG", 0, kInfoLogFile, kDefautInfoLogDir},
|
||||
{"LOG.old", 0, kInfoLogFile, kDefautInfoLogDir},
|
||||
{"LOG.old.6688", 6688, kInfoLogFile, kDefautInfoLogDir},
|
||||
{"rocksdb_dir_LOG", 0, kInfoLogFile, kDifferentInfoLogDir},
|
||||
{"rocksdb_dir_LOG.old", 0, kInfoLogFile, kDifferentInfoLogDir},
|
||||
{"rocksdb_dir_LOG.old.6688", 6688, kInfoLogFile, kDifferentInfoLogDir},
|
||||
{"18446744073709551615.log", 18446744073709551615ull, kLogFile,
|
||||
kAllMode}, };
|
||||
for (char mode : {kDifferentInfoLogDir, kDefautInfoLogDir, kNoCheckLogDir}) {
|
||||
for (unsigned int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
InfoLogPrefix info_log_prefix(mode != kDefautInfoLogDir, "/rocksdb/dir");
|
||||
if (cases[i].mode & mode) {
|
||||
std::string f = cases[i].fname;
|
||||
if (mode == kNoCheckLogDir) {
|
||||
ASSERT_TRUE(ParseFileName(f, &number, &type)) << f;
|
||||
} else {
|
||||
ASSERT_TRUE(ParseFileName(f, &number, info_log_prefix.prefix, &type))
|
||||
<< f;
|
||||
}
|
||||
ASSERT_EQ(cases[i].type, type) << f;
|
||||
ASSERT_EQ(cases[i].number, number) << f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Errors
|
||||
@@ -85,6 +105,22 @@ TEST(FileNameTest, Parse) {
|
||||
};
|
||||
}
|
||||
|
||||
TEST(FileNameTest, InfoLogFileName) {
|
||||
std::string dbname = ("/data/rocksdb");
|
||||
std::string db_absolute_path;
|
||||
Env::Default()->GetAbsolutePath(dbname, &db_absolute_path);
|
||||
|
||||
ASSERT_EQ("/data/rocksdb/LOG", InfoLogFileName(dbname, db_absolute_path, ""));
|
||||
ASSERT_EQ("/data/rocksdb/LOG.old.666",
|
||||
OldInfoLogFileName(dbname, 666u, db_absolute_path, ""));
|
||||
|
||||
ASSERT_EQ("/data/rocksdb_log/data_rocksdb_LOG",
|
||||
InfoLogFileName(dbname, db_absolute_path, "/data/rocksdb_log"));
|
||||
ASSERT_EQ(
|
||||
"/data/rocksdb_log/data_rocksdb_LOG.old.666",
|
||||
OldInfoLogFileName(dbname, 666u, db_absolute_path, "/data/rocksdb_log"));
|
||||
}
|
||||
|
||||
TEST(FileNameTest, Construction) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
|
||||
+70
-13
@@ -6,9 +6,10 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "db/forward_iterator.h"
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <limits>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/column_family.h"
|
||||
@@ -37,12 +38,16 @@ class LevelIterator : public Iterator {
|
||||
assert(file_index < files_.size());
|
||||
if (file_index != file_index_) {
|
||||
file_index_ = file_index;
|
||||
file_iter_.reset(cfd_->table_cache()->NewIterator(
|
||||
read_options_, *(cfd_->soptions()), cfd_->internal_comparator(),
|
||||
files_[file_index_]->fd, nullptr /* table_reader_ptr */, false));
|
||||
Reset();
|
||||
}
|
||||
valid_ = false;
|
||||
}
|
||||
void Reset() {
|
||||
assert(file_index_ < files_.size());
|
||||
file_iter_.reset(cfd_->table_cache()->NewIterator(
|
||||
read_options_, *(cfd_->soptions()), cfd_->internal_comparator(),
|
||||
files_[file_index_]->fd, nullptr /* table_reader_ptr */, false));
|
||||
}
|
||||
void SeekToLast() override {
|
||||
status_ = Status::NotSupported("LevelIterator::SeekToLast()");
|
||||
valid_ = false;
|
||||
@@ -63,12 +68,15 @@ class LevelIterator : public Iterator {
|
||||
assert(file_iter_ != nullptr);
|
||||
file_iter_->Seek(internal_key);
|
||||
valid_ = file_iter_->Valid();
|
||||
assert(valid_);
|
||||
}
|
||||
void Next() override {
|
||||
assert(valid_);
|
||||
file_iter_->Next();
|
||||
while (!file_iter_->Valid()) {
|
||||
for (;;) {
|
||||
if (file_iter_->status().IsIncomplete() || file_iter_->Valid()) {
|
||||
valid_ = !file_iter_->status().IsIncomplete();
|
||||
return;
|
||||
}
|
||||
if (file_index_ + 1 >= files_.size()) {
|
||||
valid_ = false;
|
||||
return;
|
||||
@@ -76,7 +84,6 @@ class LevelIterator : public Iterator {
|
||||
SetFileIndex(file_index_ + 1);
|
||||
file_iter_->SeekToFirst();
|
||||
}
|
||||
valid_ = file_iter_->Valid();
|
||||
}
|
||||
Slice key() const override {
|
||||
assert(valid_);
|
||||
@@ -160,6 +167,8 @@ void ForwardIterator::SeekToFirst() {
|
||||
if (sv_ == nullptr ||
|
||||
sv_ ->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
RebuildIterators();
|
||||
} else if (status_.IsIncomplete()) {
|
||||
ResetIncompleteIterators();
|
||||
}
|
||||
SeekInternal(Slice(), true);
|
||||
}
|
||||
@@ -168,6 +177,8 @@ void ForwardIterator::Seek(const Slice& internal_key) {
|
||||
if (sv_ == nullptr ||
|
||||
sv_ ->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
RebuildIterators();
|
||||
} else if (status_.IsIncomplete()) {
|
||||
ResetIncompleteIterators();
|
||||
}
|
||||
SeekInternal(internal_key, false);
|
||||
}
|
||||
@@ -211,7 +222,15 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
}
|
||||
l0_iters_[i]->Seek(internal_key);
|
||||
}
|
||||
if (l0_iters_[i]->Valid()) {
|
||||
|
||||
if (l0_iters_[i]->status().IsIncomplete()) {
|
||||
// if any of the immutable iterators is incomplete (no-io option was
|
||||
// used), we are unable to reliably find the smallest key
|
||||
assert(read_options_.read_tier == kBlockCacheTier);
|
||||
status_ = l0_iters_[i]->status();
|
||||
valid_ = false;
|
||||
return;
|
||||
} else if (l0_iters_[i]->Valid()) {
|
||||
immutable_min_heap_.push(l0_iters_[i]);
|
||||
}
|
||||
}
|
||||
@@ -280,7 +299,14 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
level_iters_[level - 1]->SetFileIndex(f_idx);
|
||||
seek_to_first ? level_iters_[level - 1]->SeekToFirst() :
|
||||
level_iters_[level - 1]->Seek(internal_key);
|
||||
if (level_iters_[level - 1]->Valid()) {
|
||||
|
||||
if (level_iters_[level - 1]->status().IsIncomplete()) {
|
||||
// see above
|
||||
assert(read_options_.read_tier == kBlockCacheTier);
|
||||
status_ = level_iters_[level - 1]->status();
|
||||
valid_ = false;
|
||||
return;
|
||||
} else if (level_iters_[level - 1]->Valid()) {
|
||||
immutable_min_heap_.push(level_iters_[level - 1]);
|
||||
}
|
||||
}
|
||||
@@ -304,7 +330,7 @@ void ForwardIterator::Next() {
|
||||
assert(valid_);
|
||||
|
||||
if (sv_ == nullptr ||
|
||||
sv_ ->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
sv_->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
std::string current_key = key().ToString();
|
||||
Slice old_key(current_key.data(), current_key.size());
|
||||
|
||||
@@ -320,9 +346,17 @@ void ForwardIterator::Next() {
|
||||
}
|
||||
|
||||
current_->Next();
|
||||
if (current_->Valid() && current_ != mutable_iter_) {
|
||||
immutable_min_heap_.push(current_);
|
||||
if (current_ != mutable_iter_) {
|
||||
if (current_->status().IsIncomplete()) {
|
||||
assert(read_options_.read_tier == kBlockCacheTier);
|
||||
status_ = current_->status();
|
||||
valid_ = false;
|
||||
return;
|
||||
} else if (current_->Valid()) {
|
||||
immutable_min_heap_.push(current_);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
@@ -389,6 +423,29 @@ void ForwardIterator::RebuildIterators() {
|
||||
is_prev_set_ = false;
|
||||
}
|
||||
|
||||
void ForwardIterator::ResetIncompleteIterators() {
|
||||
const auto& l0_files = sv_->current->files_[0];
|
||||
for (uint32_t i = 0; i < l0_iters_.size(); ++i) {
|
||||
assert(i < l0_files.size());
|
||||
if (!l0_iters_[i]->status().IsIncomplete()) {
|
||||
continue;
|
||||
}
|
||||
delete l0_iters_[i];
|
||||
l0_iters_[i] = cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
|
||||
l0_files[i]->fd);
|
||||
}
|
||||
|
||||
for (auto* level_iter : level_iters_) {
|
||||
if (level_iter && level_iter->status().IsIncomplete()) {
|
||||
level_iter->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
current_ = nullptr;
|
||||
is_prev_set_ = false;
|
||||
}
|
||||
|
||||
void ForwardIterator::UpdateCurrent() {
|
||||
if (immutable_min_heap_.empty() && !mutable_iter_->Valid()) {
|
||||
current_ = nullptr;
|
||||
@@ -417,7 +474,7 @@ void ForwardIterator::UpdateCurrent() {
|
||||
}
|
||||
|
||||
bool ForwardIterator::NeedToSeekImmutable(const Slice& target) {
|
||||
if (!is_prev_set_) {
|
||||
if (!valid_ || !is_prev_set_) {
|
||||
return true;
|
||||
}
|
||||
Slice prev_key = prev_key_.GetKey();
|
||||
|
||||
@@ -73,6 +73,7 @@ class ForwardIterator : public Iterator {
|
||||
private:
|
||||
void Cleanup();
|
||||
void RebuildIterators();
|
||||
void ResetIncompleteIterators();
|
||||
void SeekInternal(const Slice& internal_key, bool seek_to_first);
|
||||
void UpdateCurrent();
|
||||
bool NeedToSeekImmutable(const Slice& internal_key);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -133,6 +134,8 @@ DBPropertyType GetPropertyType(const Slice& property, bool* is_int_property,
|
||||
} else if (in == "estimate-table-readers-mem") {
|
||||
*need_out_of_mutex = true;
|
||||
return kEstimatedUsageByTableReaders;
|
||||
} else if (in == "is-file-deletions-enabled") {
|
||||
return kIsFileDeletionEnabled;
|
||||
}
|
||||
return kUnknown;
|
||||
}
|
||||
@@ -215,7 +218,7 @@ bool InternalStats::GetStringProperty(DBPropertyType property_type,
|
||||
}
|
||||
|
||||
bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
uint64_t* value) const {
|
||||
uint64_t* value, DBImpl* db) const {
|
||||
Version* current = cfd_->current();
|
||||
|
||||
switch (property_type) {
|
||||
@@ -254,6 +257,11 @@ bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
cfd_->imm()->current()->GetTotalNumEntries() +
|
||||
current->GetEstimatedActiveKeys();
|
||||
return true;
|
||||
#ifndef ROCKSDB_LITE
|
||||
case kIsFileDeletionEnabled:
|
||||
*value = db->IsFileDeletionsEnabled();
|
||||
return true;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
+4
-1
@@ -42,6 +42,8 @@ enum DBPropertyType : uint32_t {
|
||||
// the immutable mem tables.
|
||||
kEstimatedNumKeys, // Estimated total number of keys in the database.
|
||||
kEstimatedUsageByTableReaders, // Estimated memory by table readers.
|
||||
kIsFileDeletionEnabled, // Equals disable_delete_obsolete_files_,
|
||||
// 0 means file deletions enabled
|
||||
};
|
||||
|
||||
extern DBPropertyType GetPropertyType(const Slice& property,
|
||||
@@ -197,7 +199,8 @@ class InternalStats {
|
||||
bool GetStringProperty(DBPropertyType property_type, const Slice& property,
|
||||
std::string* value);
|
||||
|
||||
bool GetIntProperty(DBPropertyType property_type, uint64_t* value) const;
|
||||
bool GetIntProperty(DBPropertyType property_type, uint64_t* value,
|
||||
DBImpl* db) const;
|
||||
|
||||
bool GetIntPropertyOutOfMutex(DBPropertyType property_type, Version* version,
|
||||
uint64_t* value) const;
|
||||
|
||||
+12
-9
@@ -174,13 +174,13 @@ const char* EncodeKey(std::string* scratch, const Slice& target) {
|
||||
|
||||
class MemTableIterator: public Iterator {
|
||||
public:
|
||||
MemTableIterator(const MemTable& mem, const ReadOptions& options,
|
||||
bool enforce_total_order, Arena* arena)
|
||||
MemTableIterator(
|
||||
const MemTable& mem, const ReadOptions& options, Arena* arena)
|
||||
: bloom_(nullptr),
|
||||
prefix_extractor_(mem.prefix_extractor_),
|
||||
valid_(false),
|
||||
arena_mode_(arena != nullptr) {
|
||||
if (prefix_extractor_ != nullptr && !enforce_total_order) {
|
||||
if (prefix_extractor_ != nullptr && !options.total_order_seek) {
|
||||
bloom_ = mem.prefix_bloom_.get();
|
||||
iter_ = mem.table_->GetDynamicPrefixIterator(arena);
|
||||
} else {
|
||||
@@ -248,14 +248,13 @@ class MemTableIterator: public Iterator {
|
||||
void operator=(const MemTableIterator&);
|
||||
};
|
||||
|
||||
Iterator* MemTable::NewIterator(const ReadOptions& options,
|
||||
bool enforce_total_order, Arena* arena) {
|
||||
Iterator* MemTable::NewIterator(const ReadOptions& options, Arena* arena) {
|
||||
if (arena == nullptr) {
|
||||
return new MemTableIterator(*this, options, enforce_total_order, nullptr);
|
||||
return new MemTableIterator(*this, options, nullptr);
|
||||
} else {
|
||||
auto mem = arena->AllocateAligned(sizeof(MemTableIterator));
|
||||
return new (mem)
|
||||
MemTableIterator(*this, options, enforce_total_order, arena);
|
||||
MemTableIterator(*this, options, arena);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +417,12 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
|
||||
bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext& merge_context, const Options& options) {
|
||||
PERF_TIMER_AUTO(get_from_memtable_time);
|
||||
// The sequence number is updated synchronously in version_set.h
|
||||
if (first_seqno_ == 0) {
|
||||
// Avoiding recording stats for speed.
|
||||
return false;
|
||||
}
|
||||
PERF_TIMER_GUARD(get_from_memtable_time);
|
||||
|
||||
Slice user_key = key.user_key();
|
||||
bool found_final_value = false;
|
||||
@@ -448,7 +452,6 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
if (!found_final_value && merge_in_progress) {
|
||||
*s = Status::MergeInProgress("");
|
||||
}
|
||||
PERF_TIMER_STOP(get_from_memtable_time);
|
||||
PERF_COUNTER_ADD(get_from_memtable_count, 1);
|
||||
return found_final_value;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@ class MemTable {
|
||||
// Calling ~Iterator of the iterator will destroy all the states but
|
||||
// those allocated in arena.
|
||||
Iterator* NewIterator(const ReadOptions& options,
|
||||
bool enforce_total_order = false,
|
||||
Arena* arena = nullptr);
|
||||
|
||||
// Add an entry into memtable that maps key to value at the
|
||||
|
||||
+7
-7
@@ -57,8 +57,7 @@ class Repairer {
|
||||
: dbname_(dbname),
|
||||
env_(options.env),
|
||||
icmp_(options.comparator),
|
||||
ipolicy_(options.filter_policy),
|
||||
options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, options)),
|
||||
options_(SanitizeOptions(dbname, &icmp_, options)),
|
||||
raw_table_cache_(
|
||||
// TableCache can be small since we expect each table to be opened
|
||||
// once.
|
||||
@@ -109,7 +108,6 @@ class Repairer {
|
||||
std::string const dbname_;
|
||||
Env* const env_;
|
||||
InternalKeyComparator const icmp_;
|
||||
InternalFilterPolicy const ipolicy_;
|
||||
Options const options_;
|
||||
std::shared_ptr<Cache> raw_table_cache_;
|
||||
TableCache* table_cache_;
|
||||
@@ -239,7 +237,8 @@ class Repairer {
|
||||
FileMetaData meta;
|
||||
meta.fd = FileDescriptor(next_file_number_++, 0, 0);
|
||||
ReadOptions ro;
|
||||
Iterator* iter = mem->NewIterator(ro, true /* enforce_total_order */);
|
||||
ro.total_order_seek = true;
|
||||
Iterator* iter = mem->NewIterator(ro);
|
||||
status = BuildTable(dbname_, env_, options_, storage_options_, table_cache_,
|
||||
iter, &meta, icmp_, 0, 0, kNoCompression);
|
||||
delete iter;
|
||||
@@ -265,9 +264,10 @@ class Repairer {
|
||||
if (!status.ok()) {
|
||||
std::string fname = TableFileName(
|
||||
options_.db_paths, t.meta.fd.GetNumber(), t.meta.fd.GetPathId());
|
||||
Log(options_.info_log, "Table #%s: ignoring %s",
|
||||
FormatFileNumber(t.meta.fd.GetNumber(), t.meta.fd.GetPathId())
|
||||
.c_str(),
|
||||
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,
|
||||
status.ToString().c_str());
|
||||
ArchiveFile(fname);
|
||||
} else {
|
||||
|
||||
@@ -555,6 +555,14 @@ public:
|
||||
const InternalKeyComparator& internal_key,
|
||||
WritableFile* file,
|
||||
CompressionType compression_type) const;
|
||||
|
||||
virtual Status SanitizeDBOptions(const DBOptions* db_opts) const override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual std::string GetPrintableTableOptions() const override {
|
||||
return std::string();
|
||||
}
|
||||
};
|
||||
|
||||
Status SimpleTableFactory::NewTableReader(
|
||||
|
||||
@@ -257,7 +257,7 @@ void TestInternalKeyPropertiesCollector(
|
||||
// SanitizeOptions().
|
||||
options.info_log = std::make_shared<DumbLogger>();
|
||||
options = SanitizeOptions("db", // just a place holder
|
||||
&pikc, nullptr, // don't care filter policy
|
||||
&pikc,
|
||||
options);
|
||||
options.comparator = comparator;
|
||||
} else {
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "db/tailing_iter.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/column_family.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "table/merger.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
TailingIterator::TailingIterator(Env* const env, DBImpl* db,
|
||||
const ReadOptions& read_options, ColumnFamilyData* cfd)
|
||||
: env_(env),
|
||||
db_(db),
|
||||
read_options_(read_options),
|
||||
cfd_(cfd),
|
||||
super_version_(nullptr),
|
||||
current_(nullptr),
|
||||
status_(Status::InvalidArgument("Seek() not called on this iterator")) {}
|
||||
|
||||
TailingIterator::~TailingIterator() {
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
bool TailingIterator::Valid() const {
|
||||
return current_ != nullptr;
|
||||
}
|
||||
|
||||
void TailingIterator::SeekToFirst() {
|
||||
if (!IsCurrentVersion()) {
|
||||
CreateIterators();
|
||||
}
|
||||
|
||||
mutable_->SeekToFirst();
|
||||
immutable_->SeekToFirst();
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
void TailingIterator::Seek(const Slice& target) {
|
||||
if (!IsCurrentVersion()) {
|
||||
CreateIterators();
|
||||
}
|
||||
|
||||
mutable_->Seek(target);
|
||||
|
||||
// We maintain the interval (prev_key_, immutable_->key()] such that there
|
||||
// are no records with keys within that range in immutable_ other than
|
||||
// immutable_->key(). Since immutable_ can't change in this version, we don't
|
||||
// need to do a seek if 'target' belongs to that interval (i.e. immutable_ is
|
||||
// already at the correct position)!
|
||||
//
|
||||
// If prefix seek is used and immutable_ is not valid, seek if target has a
|
||||
// different prefix than prev_key.
|
||||
//
|
||||
// prev_key_ is updated by Next(). SeekImmutable() sets prev_key_ to
|
||||
// 'target' -- in this case, prev_key_ is included in the interval, so
|
||||
// prev_inclusive_ has to be set.
|
||||
|
||||
const Comparator* cmp = cfd_->user_comparator();
|
||||
if (!is_prev_set_ || cmp->Compare(prev_key_, target) >= !is_prev_inclusive_ ||
|
||||
(immutable_->Valid() && cmp->Compare(target, immutable_->key()) > 0) ||
|
||||
(cfd_->options()->prefix_extractor != nullptr && !IsSamePrefix(target))) {
|
||||
SeekImmutable(target);
|
||||
}
|
||||
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
void TailingIterator::Next() {
|
||||
assert(Valid());
|
||||
|
||||
if (!IsCurrentVersion()) {
|
||||
// save the current key, create new iterators and then seek
|
||||
std::string current_key = key().ToString();
|
||||
Slice key_slice(current_key.data(), current_key.size());
|
||||
|
||||
CreateIterators();
|
||||
Seek(key_slice);
|
||||
|
||||
if (!Valid() || key().compare(key_slice) != 0) {
|
||||
// record with current_key no longer exists
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (current_ == immutable_.get()) {
|
||||
// immutable iterator is advanced -- update prev_key_
|
||||
prev_key_ = key().ToString();
|
||||
is_prev_inclusive_ = false;
|
||||
is_prev_set_ = true;
|
||||
}
|
||||
|
||||
current_->Next();
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
Slice TailingIterator::key() const {
|
||||
assert(Valid());
|
||||
return current_->key();
|
||||
}
|
||||
|
||||
Slice TailingIterator::value() const {
|
||||
assert(Valid());
|
||||
return current_->value();
|
||||
}
|
||||
|
||||
Status TailingIterator::status() const {
|
||||
if (!status_.ok()) {
|
||||
return status_;
|
||||
} else if (!mutable_->status().ok()) {
|
||||
return mutable_->status();
|
||||
} else {
|
||||
return immutable_->status();
|
||||
}
|
||||
}
|
||||
|
||||
void TailingIterator::Prev() {
|
||||
status_ = Status::NotSupported("This iterator doesn't support Prev()");
|
||||
}
|
||||
|
||||
void TailingIterator::SeekToLast() {
|
||||
status_ = Status::NotSupported("This iterator doesn't support SeekToLast()");
|
||||
}
|
||||
|
||||
void TailingIterator::Cleanup() {
|
||||
// Release old super version if necessary
|
||||
mutable_.reset();
|
||||
immutable_.reset();
|
||||
if (super_version_ != nullptr && super_version_->Unref()) {
|
||||
DBImpl::DeletionState deletion_state;
|
||||
db_->mutex_.Lock();
|
||||
super_version_->Cleanup();
|
||||
db_->FindObsoleteFiles(deletion_state, false, true);
|
||||
db_->mutex_.Unlock();
|
||||
delete super_version_;
|
||||
if (deletion_state.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(deletion_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TailingIterator::CreateIterators() {
|
||||
Cleanup();
|
||||
super_version_= cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
|
||||
Iterator* mutable_iter = super_version_->mem->NewIterator(read_options_);
|
||||
// create a DBIter that only uses memtable content; see NewIterator()
|
||||
mutable_.reset(
|
||||
NewDBIterator(env_, *cfd_->options(), cfd_->user_comparator(),
|
||||
mutable_iter, kMaxSequenceNumber));
|
||||
|
||||
std::vector<Iterator*> list;
|
||||
super_version_->imm->AddIterators(read_options_, &list);
|
||||
super_version_->current->AddIterators(
|
||||
read_options_, *cfd_->soptions(), &list);
|
||||
Iterator* immutable_iter =
|
||||
NewMergingIterator(&cfd_->internal_comparator(), &list[0], list.size());
|
||||
|
||||
// create a DBIter that only uses memtable content; see NewIterator()
|
||||
immutable_.reset(
|
||||
NewDBIterator(env_, *cfd_->options(), cfd_->user_comparator(),
|
||||
immutable_iter, kMaxSequenceNumber));
|
||||
|
||||
current_ = nullptr;
|
||||
is_prev_set_ = false;
|
||||
}
|
||||
|
||||
void TailingIterator::UpdateCurrent() {
|
||||
current_ = nullptr;
|
||||
|
||||
if (mutable_->Valid()) {
|
||||
current_ = mutable_.get();
|
||||
}
|
||||
const Comparator* cmp = cfd_->user_comparator();
|
||||
if (immutable_->Valid() &&
|
||||
(current_ == nullptr ||
|
||||
cmp->Compare(immutable_->key(), current_->key()) < 0)) {
|
||||
current_ = immutable_.get();
|
||||
}
|
||||
|
||||
if (!status_.ok()) {
|
||||
// reset status that was set by Prev() or SeekToLast()
|
||||
status_ = Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
bool TailingIterator::IsCurrentVersion() const {
|
||||
return super_version_ != nullptr &&
|
||||
super_version_->version_number == cfd_->GetSuperVersionNumber();
|
||||
}
|
||||
|
||||
bool TailingIterator::IsSamePrefix(const Slice& target) const {
|
||||
const SliceTransform* extractor = cfd_->options()->prefix_extractor.get();
|
||||
|
||||
assert(extractor);
|
||||
assert(is_prev_set_);
|
||||
|
||||
return extractor->Transform(target)
|
||||
.compare(extractor->Transform(prev_key_)) == 0;
|
||||
}
|
||||
|
||||
void TailingIterator::SeekImmutable(const Slice& target) {
|
||||
prev_key_ = target.ToString();
|
||||
is_prev_inclusive_ = true;
|
||||
is_prev_set_ = true;
|
||||
|
||||
immutable_->Seek(target);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBImpl;
|
||||
class Env;
|
||||
struct SuperVersion;
|
||||
class ColumnFamilyData;
|
||||
|
||||
/**
|
||||
* TailingIterator is a special type of iterator that doesn't use an (implicit)
|
||||
* snapshot. In other words, it can be used to read data that was added to the
|
||||
* db after the iterator had been created.
|
||||
*
|
||||
* TailingIterator is optimized for sequential reading. It doesn't support
|
||||
* Prev() and SeekToLast() operations.
|
||||
*/
|
||||
class TailingIterator : public Iterator {
|
||||
public:
|
||||
TailingIterator(Env* const env, DBImpl* db, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd);
|
||||
virtual ~TailingIterator();
|
||||
|
||||
virtual bool Valid() const override;
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
virtual Slice key() const override;
|
||||
virtual Slice value() const override;
|
||||
virtual Status status() const override;
|
||||
|
||||
private:
|
||||
void Cleanup();
|
||||
|
||||
Env* const env_;
|
||||
DBImpl* const db_;
|
||||
const ReadOptions read_options_;
|
||||
ColumnFamilyData* const cfd_;
|
||||
SuperVersion* super_version_;
|
||||
|
||||
// TailingIterator merges the contents of the two iterators below (one using
|
||||
// mutable memtable contents only, other over SSTs and immutable memtables).
|
||||
// See DBIter::GetTailingIteratorPair().
|
||||
std::unique_ptr<Iterator> mutable_;
|
||||
std::unique_ptr<Iterator> immutable_;
|
||||
|
||||
// points to either mutable_ or immutable_
|
||||
Iterator* current_;
|
||||
|
||||
// key that precedes immutable iterator's current key
|
||||
std::string prev_key_;
|
||||
|
||||
// unless prev_set is true, prev_key/prev_head is not valid and shouldn't be
|
||||
// used; reset by createIterators()
|
||||
bool is_prev_set_;
|
||||
|
||||
// prev_key_ was set by SeekImmutable(), which means that the interval of
|
||||
// keys covered by immutable_ is [prev_key_, current], i.e. it includes the
|
||||
// left endpoint
|
||||
bool is_prev_inclusive_;
|
||||
|
||||
// internal iterator status
|
||||
Status status_;
|
||||
|
||||
// check if this iterator's version matches DB's version
|
||||
bool IsCurrentVersion() const;
|
||||
|
||||
// check if SeekImmutable() is needed due to target having a different prefix
|
||||
// than prev_key_ (used when in prefix seek mode)
|
||||
bool IsSamePrefix(const Slice& target) const;
|
||||
|
||||
// creates mutable_ and immutable_ iterators and updates version_number_
|
||||
void CreateIterators();
|
||||
|
||||
// set current_ to be one of the iterators with the smallest key
|
||||
void UpdateCurrent();
|
||||
|
||||
// seek on immutable_ and update prev_key
|
||||
void SeekImmutable(const Slice& target);
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
+4
-1
@@ -81,6 +81,8 @@ struct FileMetaData {
|
||||
uint64_t num_deletions; // the number of deletion entries.
|
||||
uint64_t raw_key_size; // total uncompressed key size.
|
||||
uint64_t raw_value_size; // total uncompressed value size.
|
||||
bool init_stats_from_file; // true if the data-entry stats of this file
|
||||
// has initialized from file.
|
||||
|
||||
FileMetaData()
|
||||
: refs(0),
|
||||
@@ -90,7 +92,8 @@ struct FileMetaData {
|
||||
num_entries(0),
|
||||
num_deletions(0),
|
||||
raw_key_size(0),
|
||||
raw_value_size(0) {}
|
||||
raw_value_size(0),
|
||||
init_stats_from_file(false) {}
|
||||
};
|
||||
|
||||
// A compressed copy of file meta data that just contain
|
||||
|
||||
+6
-2
@@ -879,12 +879,16 @@ void Version::PrepareApply(std::vector<uint64_t>& size_being_compacted) {
|
||||
}
|
||||
|
||||
bool Version::MaybeInitializeFileMetaData(FileMetaData* file_meta) {
|
||||
if (file_meta->num_entries > 0) {
|
||||
if (file_meta->init_stats_from_file) {
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
Status s = GetTableProperties(&tp, file_meta);
|
||||
file_meta->init_stats_from_file = true;
|
||||
if (!s.ok()) {
|
||||
Log(vset_->options_->info_log,
|
||||
"Unable to load table properties for file %" PRIu64 " --- %s\n",
|
||||
file_meta->fd.GetNumber(), s.ToString().c_str());
|
||||
return false;
|
||||
}
|
||||
if (tp.get() == nullptr) return false;
|
||||
@@ -1393,7 +1397,7 @@ const char* Version::LevelFileSummary(FileSummaryStorage* scratch,
|
||||
for (const auto& f : files_[level]) {
|
||||
int sz = sizeof(scratch->buffer) - len;
|
||||
char sztxt[16];
|
||||
AppendHumanBytes(f->fd.GetFileSize(), sztxt, 16);
|
||||
AppendHumanBytes(f->fd.GetFileSize(), sztxt, sizeof(sztxt));
|
||||
int ret = snprintf(scratch->buffer + len, sz,
|
||||
"#%" PRIu64 "(seq=%" PRIu64 ",sz=%s,%d) ",
|
||||
f->fd.GetNumber(), f->smallest_seqno, sztxt,
|
||||
|
||||
+89
-73
@@ -27,6 +27,7 @@
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/snapshot.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
@@ -80,6 +81,58 @@ int WriteBatch::Count() const {
|
||||
return WriteBatchInternal::Count(this);
|
||||
}
|
||||
|
||||
Status ReadRecordFromWriteBatch(Slice* input, char* tag,
|
||||
uint32_t* column_family, Slice* key,
|
||||
Slice* value, Slice* blob) {
|
||||
assert(key != nullptr && value != nullptr);
|
||||
*tag = (*input)[0];
|
||||
input->remove_prefix(1);
|
||||
*column_family = 0; // default
|
||||
switch (*tag) {
|
||||
case kTypeColumnFamilyValue:
|
||||
if (!GetVarint32(input, column_family)) {
|
||||
return Status::Corruption("bad WriteBatch Put");
|
||||
}
|
||||
// intentional fallthrough
|
||||
case kTypeValue:
|
||||
if (!GetLengthPrefixedSlice(input, key) ||
|
||||
!GetLengthPrefixedSlice(input, value)) {
|
||||
return Status::Corruption("bad WriteBatch Put");
|
||||
}
|
||||
break;
|
||||
case kTypeColumnFamilyDeletion:
|
||||
if (!GetVarint32(input, column_family)) {
|
||||
return Status::Corruption("bad WriteBatch Delete");
|
||||
}
|
||||
// intentional fallthrough
|
||||
case kTypeDeletion:
|
||||
if (!GetLengthPrefixedSlice(input, key)) {
|
||||
return Status::Corruption("bad WriteBatch Delete");
|
||||
}
|
||||
break;
|
||||
case kTypeColumnFamilyMerge:
|
||||
if (!GetVarint32(input, column_family)) {
|
||||
return Status::Corruption("bad WriteBatch Merge");
|
||||
}
|
||||
// intentional fallthrough
|
||||
case kTypeMerge:
|
||||
if (!GetLengthPrefixedSlice(input, key) ||
|
||||
!GetLengthPrefixedSlice(input, value)) {
|
||||
return Status::Corruption("bad WriteBatch Merge");
|
||||
}
|
||||
break;
|
||||
case kTypeLogData:
|
||||
assert(blob != nullptr);
|
||||
if (!GetLengthPrefixedSlice(input, blob)) {
|
||||
return Status::Corruption("bad WriteBatch Blob");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return Status::Corruption("unknown WriteBatch tag");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status WriteBatch::Iterate(Handler* handler) const {
|
||||
Slice input(rep_);
|
||||
if (input.size() < kHeader) {
|
||||
@@ -91,57 +144,33 @@ Status WriteBatch::Iterate(Handler* handler) const {
|
||||
int found = 0;
|
||||
Status s;
|
||||
while (s.ok() && !input.empty() && handler->Continue()) {
|
||||
char tag = input[0];
|
||||
input.remove_prefix(1);
|
||||
char tag = 0;
|
||||
uint32_t column_family = 0; // default
|
||||
|
||||
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
|
||||
&blob);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case kTypeColumnFamilyValue:
|
||||
if (!GetVarint32(&input, &column_family)) {
|
||||
return Status::Corruption("bad WriteBatch Put");
|
||||
}
|
||||
// intentional fallthrough
|
||||
case kTypeValue:
|
||||
if (GetLengthPrefixedSlice(&input, &key) &&
|
||||
GetLengthPrefixedSlice(&input, &value)) {
|
||||
s = handler->PutCF(column_family, key, value);
|
||||
found++;
|
||||
} else {
|
||||
return Status::Corruption("bad WriteBatch Put");
|
||||
}
|
||||
s = handler->PutCF(column_family, key, value);
|
||||
found++;
|
||||
break;
|
||||
case kTypeColumnFamilyDeletion:
|
||||
if (!GetVarint32(&input, &column_family)) {
|
||||
return Status::Corruption("bad WriteBatch Delete");
|
||||
}
|
||||
// intentional fallthrough
|
||||
case kTypeDeletion:
|
||||
if (GetLengthPrefixedSlice(&input, &key)) {
|
||||
s = handler->DeleteCF(column_family, key);
|
||||
found++;
|
||||
} else {
|
||||
return Status::Corruption("bad WriteBatch Delete");
|
||||
}
|
||||
s = handler->DeleteCF(column_family, key);
|
||||
found++;
|
||||
break;
|
||||
case kTypeColumnFamilyMerge:
|
||||
if (!GetVarint32(&input, &column_family)) {
|
||||
return Status::Corruption("bad WriteBatch Merge");
|
||||
}
|
||||
// intentional fallthrough
|
||||
case kTypeMerge:
|
||||
if (GetLengthPrefixedSlice(&input, &key) &&
|
||||
GetLengthPrefixedSlice(&input, &value)) {
|
||||
s = handler->MergeCF(column_family, key, value);
|
||||
found++;
|
||||
} else {
|
||||
return Status::Corruption("bad WriteBatch Merge");
|
||||
}
|
||||
s = handler->MergeCF(column_family, key, value);
|
||||
found++;
|
||||
break;
|
||||
case kTypeLogData:
|
||||
if (GetLengthPrefixedSlice(&input, &blob)) {
|
||||
handler->LogData(blob);
|
||||
} else {
|
||||
return Status::Corruption("bad WriteBatch Blob");
|
||||
}
|
||||
handler->LogData(blob);
|
||||
break;
|
||||
default:
|
||||
return Status::Corruption("unknown WriteBatch tag");
|
||||
@@ -186,17 +215,6 @@ void WriteBatchInternal::Put(WriteBatch* b, uint32_t column_family_id,
|
||||
PutLengthPrefixedSlice(&b->rep_, value);
|
||||
}
|
||||
|
||||
namespace {
|
||||
inline uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family) {
|
||||
uint32_t column_family_id = 0;
|
||||
if (column_family != nullptr) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
column_family_id = cfh->GetID();
|
||||
}
|
||||
return column_family_id;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void WriteBatch::Put(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value) {
|
||||
WriteBatchInternal::Put(this, GetColumnFamilyID(column_family), key, value);
|
||||
@@ -281,17 +299,17 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
public:
|
||||
SequenceNumber sequence_;
|
||||
ColumnFamilyMemTables* cf_mems_;
|
||||
bool recovery_;
|
||||
bool ignore_missing_column_families_;
|
||||
uint64_t log_number_;
|
||||
DBImpl* db_;
|
||||
const bool dont_filter_deletes_;
|
||||
|
||||
MemTableInserter(SequenceNumber sequence, ColumnFamilyMemTables* cf_mems,
|
||||
bool recovery, uint64_t log_number, DB* db,
|
||||
const bool dont_filter_deletes)
|
||||
bool ignore_missing_column_families, uint64_t log_number,
|
||||
DB* db, const bool dont_filter_deletes)
|
||||
: sequence_(sequence),
|
||||
cf_mems_(cf_mems),
|
||||
recovery_(recovery),
|
||||
ignore_missing_column_families_(ignore_missing_column_families),
|
||||
log_number_(log_number),
|
||||
db_(reinterpret_cast<DBImpl*>(db)),
|
||||
dont_filter_deletes_(dont_filter_deletes) {
|
||||
@@ -303,12 +321,18 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
bool SeekToColumnFamily(uint32_t column_family_id, Status* s) {
|
||||
bool found = cf_mems_->Seek(column_family_id);
|
||||
if (recovery_ && (!found || log_number_ < cf_mems_->GetLogNumber())) {
|
||||
// if in recovery envoronment:
|
||||
// * If column family was not found, it might mean that the WAL write
|
||||
// batch references to the column family that was dropped after the
|
||||
// insert. We don't want to fail the whole write batch in that case -- we
|
||||
// just ignore the update.
|
||||
if (!found) {
|
||||
if (ignore_missing_column_families_) {
|
||||
*s = Status::OK();
|
||||
} else {
|
||||
*s = Status::InvalidArgument(
|
||||
"Invalid column family specified in write batch");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (log_number_ != 0 && log_number_ < cf_mems_->GetLogNumber()) {
|
||||
// This is true only in recovery environment (log_number_ is always 0 in
|
||||
// non-recovery, regular write code-path)
|
||||
// * If log_number_ < cf_mems_->GetLogNumber(), this means that column
|
||||
// family already contains updates from this log. We can't apply updates
|
||||
// twice because of update-in-place or merge workloads -- ignore the
|
||||
@@ -316,18 +340,8 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
*s = Status::OK();
|
||||
return false;
|
||||
}
|
||||
if (!found) {
|
||||
assert(!recovery_);
|
||||
// If the column family was not found in non-recovery enviornment
|
||||
// (client's write code-path), we have to fail the write and return
|
||||
// the failure status to the client.
|
||||
*s = Status::InvalidArgument(
|
||||
"Invalid column family specified in write batch");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) {
|
||||
Status seek_status;
|
||||
@@ -485,10 +499,12 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
Status WriteBatchInternal::InsertInto(const WriteBatch* b,
|
||||
ColumnFamilyMemTables* memtables,
|
||||
bool recovery, uint64_t log_number,
|
||||
DB* db, const bool dont_filter_deletes) {
|
||||
bool ignore_missing_column_families,
|
||||
uint64_t log_number, DB* db,
|
||||
const bool dont_filter_deletes) {
|
||||
MemTableInserter inserter(WriteBatchInternal::Sequence(b), memtables,
|
||||
recovery, log_number, db, dont_filter_deletes);
|
||||
ignore_missing_column_families, log_number, db,
|
||||
dont_filter_deletes);
|
||||
return b->Iterate(&inserter);
|
||||
}
|
||||
|
||||
|
||||
@@ -106,18 +106,18 @@ class WriteBatchInternal {
|
||||
// Inserts batch entries into memtable
|
||||
// If dont_filter_deletes is false AND options.filter_deletes is true,
|
||||
// then --> Drops deletes in batch if db->KeyMayExist returns false
|
||||
// If recovery == true, this means InsertInto is executed on a recovery
|
||||
// code-path. WriteBatch referencing a dropped column family can be
|
||||
// found on a recovery code-path and should be ignored (recovery should not
|
||||
// fail). Additionally, the memtable will be updated only if
|
||||
// If ignore_missing_column_families == true. WriteBatch referencing
|
||||
// non-existing column family should be ignored.
|
||||
// However, if ignore_missing_column_families == false, any WriteBatch
|
||||
// referencing non-existing column family will return a InvalidArgument()
|
||||
// failure.
|
||||
//
|
||||
// If log_number is non-zero, the memtable will be updated only if
|
||||
// memtables->GetLogNumber() >= log_number
|
||||
// However, if recovery == false, any WriteBatch referencing
|
||||
// non-existing column family will return a failure. Also, log_number is
|
||||
// ignored in that case
|
||||
static Status InsertInto(const WriteBatch* batch,
|
||||
ColumnFamilyMemTables* memtables,
|
||||
bool recovery = false, uint64_t log_number = 0,
|
||||
DB* db = nullptr,
|
||||
bool ignore_missing_column_families = false,
|
||||
uint64_t log_number = 0, DB* db = nullptr,
|
||||
const bool dont_filter_deletes = true);
|
||||
|
||||
static void Append(WriteBatch* dst, const WriteBatch* src);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
@@ -316,6 +317,88 @@ TEST(WriteBatchTest, ColumnFamiliesBatchTest) {
|
||||
handler.seen);
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, ColumnFamiliesBatchWithIndexTest) {
|
||||
WriteBatchWithIndex batch(BytewiseComparator(), 20);
|
||||
ColumnFamilyHandleImplDummy zero(0), two(2), three(3), eight(8);
|
||||
batch.Put(&zero, Slice("foo"), Slice("bar"));
|
||||
batch.Put(&two, Slice("twofoo"), Slice("bar2"));
|
||||
batch.Put(&eight, Slice("eightfoo"), Slice("bar8"));
|
||||
batch.Delete(&eight, Slice("eightfoo"));
|
||||
batch.Merge(&three, Slice("threethree"), Slice("3three"));
|
||||
batch.Put(&zero, Slice("foo"), Slice("bar"));
|
||||
batch.Merge(Slice("omom"), Slice("nom"));
|
||||
|
||||
std::unique_ptr<WBWIIterator> iter;
|
||||
|
||||
iter.reset(batch.NewIterator(&eight));
|
||||
iter->Seek("eightfoo");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type);
|
||||
ASSERT_EQ("eightfoo", iter->Entry().key.ToString());
|
||||
ASSERT_EQ("bar8", iter->Entry().value.ToString());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(WriteType::kDeleteRecord, iter->Entry().type);
|
||||
ASSERT_EQ("eightfoo", iter->Entry().key.ToString());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
|
||||
iter.reset(batch.NewIterator());
|
||||
iter->Seek("gggg");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(WriteType::kMergeRecord, iter->Entry().type);
|
||||
ASSERT_EQ("omom", iter->Entry().key.ToString());
|
||||
ASSERT_EQ("nom", iter->Entry().value.ToString());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
|
||||
iter.reset(batch.NewIterator(&zero));
|
||||
iter->Seek("foo");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type);
|
||||
ASSERT_EQ("foo", iter->Entry().key.ToString());
|
||||
ASSERT_EQ("bar", iter->Entry().value.ToString());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type);
|
||||
ASSERT_EQ("foo", iter->Entry().key.ToString());
|
||||
ASSERT_EQ("bar", iter->Entry().value.ToString());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(WriteType::kMergeRecord, iter->Entry().type);
|
||||
ASSERT_EQ("omom", iter->Entry().key.ToString());
|
||||
ASSERT_EQ("nom", iter->Entry().value.ToString());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
|
||||
TestHandler handler;
|
||||
batch.GetWriteBatch()->Iterate(&handler);
|
||||
ASSERT_EQ(
|
||||
"Put(foo, bar)"
|
||||
"PutCF(2, twofoo, bar2)"
|
||||
"PutCF(8, eightfoo, bar8)"
|
||||
"DeleteCF(8, eightfoo)"
|
||||
"MergeCF(3, threethree, 3three)"
|
||||
"Put(foo, bar)"
|
||||
"Merge(omom, nom)",
|
||||
handler.seen);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+31
-12
@@ -75,6 +75,8 @@ typedef struct rocksdb_iterator_t rocksdb_iterator_t;
|
||||
typedef struct rocksdb_logger_t rocksdb_logger_t;
|
||||
typedef struct rocksdb_mergeoperator_t rocksdb_mergeoperator_t;
|
||||
typedef struct rocksdb_options_t rocksdb_options_t;
|
||||
typedef struct rocksdb_block_based_table_options_t
|
||||
rocksdb_block_based_table_options_t;
|
||||
typedef struct rocksdb_randomfile_t rocksdb_randomfile_t;
|
||||
typedef struct rocksdb_readoptions_t rocksdb_readoptions_t;
|
||||
typedef struct rocksdb_seqfile_t rocksdb_seqfile_t;
|
||||
@@ -346,6 +348,34 @@ extern void rocksdb_writebatch_iterate(
|
||||
void (*deleted)(void*, const char* k, size_t klen));
|
||||
extern const char* rocksdb_writebatch_data(rocksdb_writebatch_t*, size_t *size);
|
||||
|
||||
/* Block based table options */
|
||||
|
||||
extern rocksdb_block_based_table_options_t*
|
||||
rocksdb_block_based_options_create();
|
||||
extern void rocksdb_block_based_options_destroy(
|
||||
rocksdb_block_based_table_options_t* options);
|
||||
extern void rocksdb_block_based_options_set_block_size(
|
||||
rocksdb_block_based_table_options_t* options, size_t block_size);
|
||||
extern void rocksdb_block_based_options_set_block_size_deviation(
|
||||
rocksdb_block_based_table_options_t* options, int block_size_deviation);
|
||||
extern void rocksdb_block_based_options_set_block_restart_interval(
|
||||
rocksdb_block_based_table_options_t* options, int block_restart_interval);
|
||||
extern void rocksdb_block_based_options_set_filter_policy(
|
||||
rocksdb_block_based_table_options_t* options,
|
||||
rocksdb_filterpolicy_t* filter_policy);
|
||||
extern void rocksdb_block_based_options_set_no_block_cache(
|
||||
rocksdb_block_based_table_options_t* options,
|
||||
unsigned char no_block_cache);
|
||||
extern void rocksdb_block_based_options_set_block_cache(
|
||||
rocksdb_block_based_table_options_t* options, rocksdb_cache_t* block_cache);
|
||||
extern void rocksdb_block_based_options_set_block_cache_compressed(
|
||||
rocksdb_block_based_table_options_t* options,
|
||||
rocksdb_cache_t* block_cache_compressed);
|
||||
extern void rocksdb_block_based_options_set_whole_key_filtering(
|
||||
rocksdb_block_based_table_options_t*, unsigned char);
|
||||
extern void rocksdb_options_set_block_based_table_factory(
|
||||
rocksdb_options_t *opt, rocksdb_block_based_table_options_t* table_options);
|
||||
|
||||
/* Options */
|
||||
|
||||
extern rocksdb_options_t* rocksdb_options_create();
|
||||
@@ -353,7 +383,7 @@ extern void rocksdb_options_destroy(rocksdb_options_t*);
|
||||
extern void rocksdb_options_increase_parallelism(
|
||||
rocksdb_options_t* opt, int total_threads);
|
||||
extern void rocksdb_options_optimize_for_point_lookup(
|
||||
rocksdb_options_t* opt);
|
||||
rocksdb_options_t* opt, uint64_t block_cache_size_mb);
|
||||
extern void rocksdb_options_optimize_level_style_compaction(
|
||||
rocksdb_options_t* opt, uint64_t memtable_memory_budget);
|
||||
extern void rocksdb_options_optimize_universal_style_compaction(
|
||||
@@ -376,9 +406,6 @@ extern void rocksdb_options_set_compression_per_level(
|
||||
rocksdb_options_t* opt,
|
||||
int* level_values,
|
||||
size_t num_levels);
|
||||
extern void rocksdb_options_set_filter_policy(
|
||||
rocksdb_options_t*,
|
||||
rocksdb_filterpolicy_t*);
|
||||
extern void rocksdb_options_set_create_if_missing(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern void rocksdb_options_set_create_missing_column_families(
|
||||
@@ -392,13 +419,8 @@ extern void rocksdb_options_set_info_log(rocksdb_options_t*, rocksdb_logger_t*);
|
||||
extern void rocksdb_options_set_info_log_level(rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_write_buffer_size(rocksdb_options_t*, size_t);
|
||||
extern void rocksdb_options_set_max_open_files(rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_cache(rocksdb_options_t*, rocksdb_cache_t*);
|
||||
extern void rocksdb_options_set_cache_compressed(rocksdb_options_t*, rocksdb_cache_t*);
|
||||
extern void rocksdb_options_set_block_size(rocksdb_options_t*, size_t);
|
||||
extern void rocksdb_options_set_block_restart_interval(rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_compression_options(
|
||||
rocksdb_options_t*, int, int, int);
|
||||
extern void rocksdb_options_set_whole_key_filtering(rocksdb_options_t*, unsigned char);
|
||||
extern void rocksdb_options_set_prefix_extractor(
|
||||
rocksdb_options_t*, rocksdb_slicetransform_t*);
|
||||
extern void rocksdb_options_set_num_levels(rocksdb_options_t*, int);
|
||||
@@ -449,8 +471,6 @@ extern void rocksdb_options_set_arena_block_size(
|
||||
rocksdb_options_t*, size_t);
|
||||
extern void rocksdb_options_set_use_fsync(
|
||||
rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_db_stats_log_interval(
|
||||
rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_db_log_dir(
|
||||
rocksdb_options_t*, const char*);
|
||||
extern void rocksdb_options_set_wal_dir(
|
||||
@@ -493,7 +513,6 @@ extern void rocksdb_options_set_max_sequential_skip_in_iterations(
|
||||
rocksdb_options_t*, uint64_t);
|
||||
extern void rocksdb_options_set_disable_data_sync(rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_disable_auto_compactions(rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_disable_seek_compaction(rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_delete_obsolete_files_period_micros(
|
||||
rocksdb_options_t*, uint64_t);
|
||||
extern void rocksdb_options_set_source_compaction_factor(rocksdb_options_t*, int);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef STORAGE_ROCKSDB_INCLUDE_COMPACTION_FILTER_H_
|
||||
#define STORAGE_ROCKSDB_INCLUDE_COMPACTION_FILTER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ class Env {
|
||||
// Priority for scheduling job in thread pool
|
||||
enum Priority { LOW, HIGH, TOTAL };
|
||||
|
||||
// Priority for scheduling job in thread pool
|
||||
// Priority for requesting bytes in rate limiter scheduler
|
||||
enum IOPriority {
|
||||
IO_LOW = 0,
|
||||
IO_HIGH = 1,
|
||||
@@ -272,6 +272,9 @@ class Env {
|
||||
// default number: 1
|
||||
virtual void SetBackgroundThreads(int number, Priority pri = LOW) = 0;
|
||||
|
||||
// Lower IO priority for threads from the specified pool.
|
||||
virtual void LowerThreadPoolIOPriority(Priority pool = LOW) {}
|
||||
|
||||
// Converts seconds-since-Jan-01-1970 to a printable string
|
||||
virtual std::string TimeToString(uint64_t time) = 0;
|
||||
|
||||
@@ -779,6 +782,9 @@ class EnvWrapper : public Env {
|
||||
void SetBackgroundThreads(int num, Priority pri) {
|
||||
return target_->SetBackgroundThreads(num, pri);
|
||||
}
|
||||
void LowerThreadPoolIOPriority(Priority pool = LOW) override {
|
||||
target_->LowerThreadPoolIOPriority(pool);
|
||||
}
|
||||
std::string TimeToString(uint64_t time) {
|
||||
return target_->TimeToString(time);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "rocksdb/table.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -37,7 +38,8 @@ class FlushBlockPolicyFactory {
|
||||
// Callers must delete the result after any database that is using the
|
||||
// result has been closed.
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const Options& options, const BlockBuilder& data_block_builder) const = 0;
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const BlockBuilder& data_block_builder) const = 0;
|
||||
|
||||
virtual ~FlushBlockPolicyFactory() { }
|
||||
};
|
||||
@@ -51,7 +53,7 @@ class FlushBlockBySizePolicyFactory : public FlushBlockPolicyFactory {
|
||||
}
|
||||
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const Options& options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const BlockBuilder& data_block_builder) const override;
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ struct IOStatsContext {
|
||||
uint64_t bytes_read;
|
||||
};
|
||||
|
||||
#ifndef IOS_CROSS_COMPILE
|
||||
extern __thread IOStatsContext iostats_context;
|
||||
#endif // IOS_CROSS_COMPILE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
+49
-82
@@ -97,7 +97,8 @@ struct ColumnFamilyOptions {
|
||||
|
||||
// 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
|
||||
ColumnFamilyOptions* OptimizeForPointLookup();
|
||||
ColumnFamilyOptions* OptimizeForPointLookup(
|
||||
uint64_t block_cache_size_mb);
|
||||
|
||||
// Default values for some parameters in ColumnFamilyOptions are not
|
||||
// optimized for heavy workloads and big datasets, which means you might
|
||||
@@ -206,34 +207,6 @@ struct ColumnFamilyOptions {
|
||||
// individual write buffers. Default: 1
|
||||
int min_write_buffer_number_to_merge;
|
||||
|
||||
// Control over blocks (user data is stored in a set of blocks, and
|
||||
// a block is the unit of reading from disk).
|
||||
|
||||
// If non-NULL use the specified cache for blocks.
|
||||
// If NULL, rocksdb will automatically create and use an 8MB internal cache.
|
||||
// Default: nullptr
|
||||
std::shared_ptr<Cache> block_cache;
|
||||
|
||||
// If non-NULL use the specified cache for compressed blocks.
|
||||
// If NULL, rocksdb will not use a compressed block cache.
|
||||
// Default: nullptr
|
||||
std::shared_ptr<Cache> block_cache_compressed;
|
||||
|
||||
// Approximate size of user data packed per block. Note that the
|
||||
// block size specified here corresponds to uncompressed data. The
|
||||
// actual size of the unit read from disk may be smaller if
|
||||
// compression is enabled. This parameter can be changed dynamically.
|
||||
//
|
||||
// Default: 4K
|
||||
size_t block_size;
|
||||
|
||||
// Number of keys between restart points for delta encoding of keys.
|
||||
// This parameter can be changed dynamically. Most clients should
|
||||
// leave this parameter alone.
|
||||
//
|
||||
// Default: 16
|
||||
int block_restart_interval;
|
||||
|
||||
// Compress blocks using the specified compression algorithm. This
|
||||
// parameter can be changed dynamically.
|
||||
//
|
||||
@@ -267,13 +240,6 @@ struct ColumnFamilyOptions {
|
||||
// different options for compression algorithms
|
||||
CompressionOptions compression_opts;
|
||||
|
||||
// If non-nullptr, use the specified filter policy to reduce disk reads.
|
||||
// Many applications will benefit from passing the result of
|
||||
// NewBloomFilterPolicy() here.
|
||||
//
|
||||
// Default: nullptr
|
||||
const FilterPolicy* filter_policy;
|
||||
|
||||
// If non-nullptr, use the specified function to determine the
|
||||
// prefixes for keys. These prefixes will be placed in the filter.
|
||||
// Depending on the workload, this can reduce the number of read-IOP
|
||||
@@ -290,12 +256,6 @@ struct ColumnFamilyOptions {
|
||||
// Default: nullptr
|
||||
std::shared_ptr<const SliceTransform> prefix_extractor;
|
||||
|
||||
// If true, place whole keys in the filter (not just prefixes).
|
||||
// This must generally be true for gets to be efficient.
|
||||
//
|
||||
// Default: true
|
||||
bool whole_key_filtering;
|
||||
|
||||
// Number of levels for this database
|
||||
int num_levels;
|
||||
|
||||
@@ -375,18 +335,6 @@ struct ColumnFamilyOptions {
|
||||
// stop building a single file in a level->level+1 compaction.
|
||||
int max_grandparent_overlap_factor;
|
||||
|
||||
// We decided to remove seek compaction from RocksDB because:
|
||||
// 1) It makes more sense for spinning disk workloads, while RocksDB is
|
||||
// primarily designed for flash and memory,
|
||||
// 2) It added some complexity to the important code-paths,
|
||||
// 3) None of our internal customers were really using it.
|
||||
//
|
||||
// Since we removed seek compaction, this option is now obsolete.
|
||||
// We left it here for backwards compatiblity (otherwise it would break the
|
||||
// build), but we'll remove it at some point.
|
||||
// Default: true
|
||||
bool disable_seek_compaction;
|
||||
|
||||
// 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
|
||||
@@ -404,12 +352,6 @@ struct ColumnFamilyOptions {
|
||||
// Default: 1000
|
||||
unsigned int rate_limit_delay_max_milliseconds;
|
||||
|
||||
// Disable block cache. If this is set to true,
|
||||
// then no block cache should be used, and the block_cache should
|
||||
// point to a nullptr object.
|
||||
// Default: false
|
||||
bool no_block_cache;
|
||||
|
||||
// size of one block in arena memory allocation.
|
||||
// If <= 0, a proper value is automatically calculated (usually 1/10 of
|
||||
// writer_buffer_size).
|
||||
@@ -433,14 +375,6 @@ struct ColumnFamilyOptions {
|
||||
// Default: true
|
||||
bool purge_redundant_kvs_while_flush;
|
||||
|
||||
// This is used to close a block before it reaches the configured
|
||||
// 'block_size'. If the percentage of free space in the current block is less
|
||||
// than this specified number and adding a new record to the block will
|
||||
// exceed the configured block size, then this block will be closed and the
|
||||
// new record will be written to the next block.
|
||||
// Default is 10.
|
||||
int block_size_deviation;
|
||||
|
||||
// The compaction style. Default: kCompactionStyleLevel
|
||||
CompactionStyle compaction_style;
|
||||
|
||||
@@ -475,10 +409,24 @@ struct ColumnFamilyOptions {
|
||||
std::shared_ptr<MemTableRepFactory> memtable_factory;
|
||||
|
||||
// This is a factory that provides TableFactory objects.
|
||||
// Default: a factory that provides a default implementation of
|
||||
// Table and TableBuilder.
|
||||
// Default: a block-based table factory that provides a default
|
||||
// implementation of TableBuilder and TableReader with default
|
||||
// BlockBasedTableOptions.
|
||||
std::shared_ptr<TableFactory> table_factory;
|
||||
|
||||
// Block-based table related options are moved to BlockBasedTableOptions.
|
||||
// Related options that were originally here but now moved include:
|
||||
// no_block_cache
|
||||
// block_cache
|
||||
// block_cache_compressed
|
||||
// block_size
|
||||
// block_size_deviation
|
||||
// block_restart_interval
|
||||
// filter_policy
|
||||
// whole_key_filtering
|
||||
// If you'd like to customize some of these options, you will need to
|
||||
// use NewBlockBasedTableFactory() to construct a new table factory.
|
||||
|
||||
// This option allows user to to collect their own interested statistics of
|
||||
// the tables.
|
||||
// Default: empty vector -- no user-defined statistics collection will be
|
||||
@@ -636,6 +584,7 @@ struct DBOptions {
|
||||
|
||||
// Use to control write rate of flush and compaction. Flush has higher
|
||||
// priority than compaction. Rate limiting is disabled if nullptr.
|
||||
// If rate limiter is enabled, bytes_per_sync is set to 1MB by default.
|
||||
// Default: nullptr
|
||||
std::shared_ptr<RateLimiter> rate_limiter;
|
||||
|
||||
@@ -683,9 +632,6 @@ struct DBOptions {
|
||||
// Default: false
|
||||
bool use_fsync;
|
||||
|
||||
// This options is not used!!
|
||||
int db_stats_log_interval;
|
||||
|
||||
// A list of paths where SST files can be put into, with its target size.
|
||||
// Newer data is placed into paths specified earlier in the vector while
|
||||
// older data gradually moves to paths specified later in the vector.
|
||||
@@ -857,12 +803,6 @@ struct DBOptions {
|
||||
// Default: false
|
||||
bool use_adaptive_mutex;
|
||||
|
||||
// Allows OS to incrementally sync files to disk while they are being
|
||||
// written, asynchronously, in the background.
|
||||
// Issue one request for every bytes_per_sync written. 0 turns it off.
|
||||
// Default: 0
|
||||
uint64_t bytes_per_sync;
|
||||
|
||||
// Allow RocksDB to use thread local storage to optimize performance.
|
||||
// Default: true
|
||||
bool allow_thread_local;
|
||||
@@ -873,6 +813,16 @@ struct DBOptions {
|
||||
explicit DBOptions(const Options& options);
|
||||
|
||||
void Dump(Logger* log) const;
|
||||
|
||||
// Allows OS to incrementally sync files to disk while they are being
|
||||
// written, asynchronously, in the background.
|
||||
// Issue one request for every bytes_per_sync written. 0 turns it off.
|
||||
// Default: 0
|
||||
//
|
||||
// You may consider using rate_limiter to regulate write rate to device.
|
||||
// When rate limiter is enabled, it automatically enables bytes_per_sync
|
||||
// to 1MB.
|
||||
uint64_t bytes_per_sync;
|
||||
};
|
||||
|
||||
// Options to control the behavior of a database (passed to DB::Open)
|
||||
@@ -967,18 +917,25 @@ struct ReadOptions {
|
||||
// Not supported in ROCKSDB_LITE mode!
|
||||
bool tailing;
|
||||
|
||||
// Enable a total order seek regardless of index format (e.g. hash index)
|
||||
// used in the table. Some table format (e.g. plain table) may not support
|
||||
// this option.
|
||||
bool total_order_seek;
|
||||
|
||||
ReadOptions()
|
||||
: verify_checksums(true),
|
||||
fill_cache(true),
|
||||
snapshot(nullptr),
|
||||
read_tier(kReadAllTier),
|
||||
tailing(false) {}
|
||||
tailing(false),
|
||||
total_order_seek(false) {}
|
||||
ReadOptions(bool cksum, bool cache)
|
||||
: verify_checksums(cksum),
|
||||
fill_cache(cache),
|
||||
snapshot(nullptr),
|
||||
read_tier(kReadAllTier),
|
||||
tailing(false) {}
|
||||
tailing(false),
|
||||
total_order_seek(false) {}
|
||||
};
|
||||
|
||||
// Options that control write operations
|
||||
@@ -1016,7 +973,17 @@ struct WriteOptions {
|
||||
// Default: 0
|
||||
uint64_t timeout_hint_us;
|
||||
|
||||
WriteOptions() : sync(false), disableWAL(false), timeout_hint_us(0) {}
|
||||
// If true and if user is trying to write to column families that don't exist
|
||||
// (they were dropped), ignore the write (don't return an error). If there
|
||||
// are multiple writes in a WriteBatch, other writes will succeed.
|
||||
// Default: false
|
||||
bool ignore_missing_column_families;
|
||||
|
||||
WriteOptions()
|
||||
: sync(false),
|
||||
disableWAL(false),
|
||||
timeout_hint_us(0),
|
||||
ignore_missing_column_families(false) {}
|
||||
};
|
||||
|
||||
// Options that control flush operations
|
||||
|
||||
@@ -219,6 +219,7 @@ enum Histograms : uint32_t {
|
||||
HARD_RATE_LIMIT_DELAY_COUNT,
|
||||
SOFT_RATE_LIMIT_DELAY_COUNT,
|
||||
NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
DB_SEEK,
|
||||
HISTOGRAM_ENUM_MAX,
|
||||
};
|
||||
|
||||
@@ -241,6 +242,7 @@ const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
|
||||
{ HARD_RATE_LIMIT_DELAY_COUNT, "rocksdb.hard.rate.limit.delay.count"},
|
||||
{ SOFT_RATE_LIMIT_DELAY_COUNT, "rocksdb.soft.rate.limit.delay.count"},
|
||||
{ NUM_FILES_IN_SINGLE_COMPACTION, "rocksdb.numfiles.in.singlecompaction" },
|
||||
{ DB_SEEK, "rocksdb.db.seek.micros" },
|
||||
};
|
||||
|
||||
struct HistogramData {
|
||||
|
||||
@@ -96,7 +96,7 @@ class Status {
|
||||
// Returns true iff the status indicates Incomplete
|
||||
bool IsIncomplete() const { return code() == kIncomplete; }
|
||||
|
||||
// Returns true iff the status indicates Incomplete
|
||||
// Returns true iff the status indicates Shutdown In progress
|
||||
bool IsShutdownInProgress() const { return code() == kShutdownInProgress; }
|
||||
|
||||
bool IsTimedOut() const { return code() == kTimedOut; }
|
||||
|
||||
+125
-38
@@ -84,6 +84,46 @@ struct BlockBasedTableOptions {
|
||||
// protected with this checksum type. Old table files will still be readable,
|
||||
// even though they have different checksum type.
|
||||
ChecksumType checksum = kCRC32c;
|
||||
|
||||
// Disable block cache. If this is set to true,
|
||||
// then no block cache should be used, and the block_cache should
|
||||
// point to a nullptr object.
|
||||
bool no_block_cache = false;
|
||||
|
||||
// If non-NULL use the specified cache for blocks.
|
||||
// If NULL, rocksdb will automatically create and use an 8MB internal cache.
|
||||
std::shared_ptr<Cache> block_cache = nullptr;
|
||||
|
||||
// If non-NULL use the specified cache for compressed blocks.
|
||||
// If NULL, rocksdb will not use a compressed block cache.
|
||||
std::shared_ptr<Cache> block_cache_compressed = nullptr;
|
||||
|
||||
// Approximate size of user data packed per block. Note that the
|
||||
// block size specified here corresponds to uncompressed data. The
|
||||
// actual size of the unit read from disk may be smaller if
|
||||
// compression is enabled. This parameter can be changed dynamically.
|
||||
size_t block_size = 4 * 1024;
|
||||
|
||||
// This is used to close a block before it reaches the configured
|
||||
// 'block_size'. If the percentage of free space in the current block is less
|
||||
// than this specified number and adding a new record to the block will
|
||||
// exceed the configured block size, then this block will be closed and the
|
||||
// new record will be written to the next block.
|
||||
int block_size_deviation = 10;
|
||||
|
||||
// Number of keys between restart points for delta encoding of keys.
|
||||
// This parameter can be changed dynamically. Most clients should
|
||||
// leave this parameter alone.
|
||||
int block_restart_interval = 16;
|
||||
|
||||
// If non-nullptr, use the specified filter policy to reduce disk reads.
|
||||
// Many applications will benefit from passing the result of
|
||||
// NewBloomFilterPolicy() here.
|
||||
std::shared_ptr<const FilterPolicy> filter_policy = nullptr;
|
||||
|
||||
// If true, place whole keys in the filter (not just prefixes).
|
||||
// This must generally be true for gets to be efficient.
|
||||
bool whole_key_filtering = true;
|
||||
};
|
||||
|
||||
// Table Properties that are specific to block-based table properties.
|
||||
@@ -126,47 +166,49 @@ struct PlainTablePropertyNames {
|
||||
const uint32_t kPlainTableVariableLength = 0;
|
||||
|
||||
struct PlainTableOptions {
|
||||
// @user_key_len: plain table has optimization for fix-sized keys, which can be
|
||||
// specified via user_key_len. Alternatively, you can pass
|
||||
// `kPlainTableVariableLength` if your keys have variable
|
||||
// lengths.
|
||||
uint32_t user_key_len = kPlainTableVariableLength;
|
||||
// @user_key_len: plain table has optimization for fix-sized keys, which can
|
||||
// be specified via user_key_len. Alternatively, you can pass
|
||||
// `kPlainTableVariableLength` if your keys have variable
|
||||
// lengths.
|
||||
uint32_t user_key_len = kPlainTableVariableLength;
|
||||
|
||||
// @bloom_bits_per_key: the number of bits used for bloom filer per prefix. You
|
||||
// may disable it by passing a zero.
|
||||
int bloom_bits_per_key = 10;
|
||||
// @bloom_bits_per_key: the number of bits used for bloom filer per prefix.
|
||||
// You may disable it by passing a zero.
|
||||
int bloom_bits_per_key = 10;
|
||||
|
||||
// @hash_table_ratio: the desired utilization of the hash table used for prefix
|
||||
// hashing. hash_table_ratio = number of prefixes / #buckets
|
||||
// in the hash table
|
||||
double hash_table_ratio = 0.75;
|
||||
// @hash_table_ratio: the desired utilization of the hash table used for
|
||||
// prefix hashing.
|
||||
// hash_table_ratio = number of prefixes / #buckets in the
|
||||
// hash table
|
||||
double hash_table_ratio = 0.75;
|
||||
|
||||
// @index_sparseness: inside each prefix, need to build one index record for how
|
||||
// many keys for binary search inside each hash bucket.
|
||||
// For encoding type kPrefix, the value will be used when
|
||||
// writing to determine an interval to rewrite the full key.
|
||||
// It will also be used as a suggestion and satisfied when
|
||||
// possible.
|
||||
size_t index_sparseness = 16;
|
||||
// @index_sparseness: inside each prefix, need to build one index record for
|
||||
// how many keys for binary search inside each hash bucket.
|
||||
// For encoding type kPrefix, the value will be used when
|
||||
// writing to determine an interval to rewrite the full
|
||||
// key. It will also be used as a suggestion and satisfied
|
||||
// when possible.
|
||||
size_t index_sparseness = 16;
|
||||
|
||||
// @huge_page_tlb_size: if <=0, allocate hash indexes and blooms from malloc.
|
||||
// Otherwise from huge page TLB. The user needs to reserve
|
||||
// huge pages for it to be allocated, like:
|
||||
// sysctl -w vm.nr_hugepages=20
|
||||
// See linux doc Documentation/vm/hugetlbpage.txt
|
||||
size_t huge_page_tlb_size = 0;
|
||||
// @huge_page_tlb_size: if <=0, allocate hash indexes and blooms from malloc.
|
||||
// Otherwise from huge page TLB. The user needs to
|
||||
// reserve huge pages for it to be allocated, like:
|
||||
// sysctl -w vm.nr_hugepages=20
|
||||
// See linux doc Documentation/vm/hugetlbpage.txt
|
||||
size_t huge_page_tlb_size = 0;
|
||||
|
||||
// @encoding_type: how to encode the keys. See enum EncodingType above for
|
||||
// the choices. The value will determine how to encode keys
|
||||
// when writing to a new SST file. This value will be stored
|
||||
// inside the SST file which will be used when reading from the
|
||||
// file, which makes it possible for users to choose different
|
||||
// encoding type when reopening a DB. Files with different
|
||||
// encoding types can co-exist in the same DB and can be read.
|
||||
EncodingType encoding_type = kPlain;
|
||||
// @encoding_type: how to encode the keys. See enum EncodingType above for
|
||||
// the choices. The value will determine how to encode keys
|
||||
// when writing to a new SST file. This value will be stored
|
||||
// inside the SST file which will be used when reading from
|
||||
// the file, which makes it possible for users to choose
|
||||
// different encoding type when reopening a DB. Files with
|
||||
// different encoding types can co-exist in the same DB and
|
||||
// can be read.
|
||||
EncodingType encoding_type = kPlain;
|
||||
|
||||
// @full_scan_mode: mode for reading the whole file one record by one without
|
||||
// using the index.
|
||||
// @full_scan_mode: mode for reading the whole file one record by one without
|
||||
// using the index.
|
||||
bool full_scan_mode = false;
|
||||
|
||||
// @store_index_in_file: compute plain table index and bloom filter during
|
||||
@@ -185,13 +227,47 @@ extern TableFactory* NewPlainTableFactory(const PlainTableOptions& options =
|
||||
PlainTableOptions());
|
||||
|
||||
struct CuckooTablePropertyNames {
|
||||
// The key that is used to fill empty buckets.
|
||||
static const std::string kEmptyKey;
|
||||
// Fixed length of value.
|
||||
static const std::string kValueLength;
|
||||
static const std::string kNumHashTable;
|
||||
static const std::string kMaxNumBuckets;
|
||||
// Number of hash functions used in Cuckoo Hash.
|
||||
static const std::string kNumHashFunc;
|
||||
// It denotes the number of buckets in a Cuckoo Block. Given a key and a
|
||||
// particular hash function, a Cuckoo Block is a set of consecutive buckets,
|
||||
// where starting bucket id is given by the hash function on the key. In case
|
||||
// of a collision during inserting the key, the builder tries to insert the
|
||||
// key in other locations of the cuckoo block before using the next hash
|
||||
// function. This reduces cache miss during read operation in case of
|
||||
// collision.
|
||||
static const std::string kCuckooBlockSize;
|
||||
// Size of the hash table. Use this number to compute the modulo of hash
|
||||
// function. The actual number of buckets will be kMaxHashTableSize +
|
||||
// kCuckooBlockSize - 1. The last kCuckooBlockSize-1 buckets are used to
|
||||
// accommodate the Cuckoo Block from end of hash table, due to cache friendly
|
||||
// implementation.
|
||||
static const std::string kHashTableSize;
|
||||
// Denotes if the key sorted in the file is Internal Key (if false)
|
||||
// or User Key only (if true).
|
||||
static const std::string kIsLastLevel;
|
||||
};
|
||||
|
||||
// Cuckoo Table Factory for SST table format using Cache Friendly Cuckoo Hashing
|
||||
// @hash_table_ratio: Determines the utilization of hash tables. Smaller values
|
||||
// result in larger hash tables with fewer collisions.
|
||||
// @max_search_depth: A property used by builder to determine the depth to go to
|
||||
// to search for a path to displace elements in case of
|
||||
// collision. See Builder.MakeSpaceForKey method. Higher
|
||||
// values result in more efficient hash tables with fewer
|
||||
// lookups but take more time to build.
|
||||
// @cuckoo_block_size: In case of collision while inserting, the builder
|
||||
// attempts to insert in the next cuckoo_block_size
|
||||
// locations before skipping over to the next Cuckoo hash
|
||||
// function. This makes lookups more cache friendly in case
|
||||
// of collisions.
|
||||
extern TableFactory* NewCuckooTableFactory(double hash_table_ratio = 0.9,
|
||||
uint32_t max_search_depth = 100, uint32_t cuckoo_block_size = 5);
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// A base class for table factories.
|
||||
@@ -250,6 +326,16 @@ class TableFactory {
|
||||
virtual TableBuilder* NewTableBuilder(
|
||||
const Options& options, const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file, CompressionType compression_type) const = 0;
|
||||
|
||||
// Sanitizes the specified DB Options.
|
||||
//
|
||||
// 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;
|
||||
|
||||
// Return a string that contains printable format of table configurations.
|
||||
// RocksDB prints configurations at DB Open().
|
||||
virtual std::string GetPrintableTableOptions() const = 0;
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -263,7 +349,8 @@ class TableFactory {
|
||||
extern TableFactory* NewAdaptiveTableFactory(
|
||||
std::shared_ptr<TableFactory> table_factory_to_write = nullptr,
|
||||
std::shared_ptr<TableFactory> block_based_table_factory = nullptr,
|
||||
std::shared_ptr<TableFactory> plain_table_factory = nullptr);
|
||||
std::shared_ptr<TableFactory> plain_table_factory = nullptr,
|
||||
std::shared_ptr<TableFactory> cuckoo_table_factory = nullptr);
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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.
|
||||
//
|
||||
// A WriteBatchWithIndex with a binary searchable index built for all the keys
|
||||
// inserted.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class ColumnFamilyHandle;
|
||||
struct SliceParts;
|
||||
class Comparator;
|
||||
|
||||
enum WriteType { kPutRecord, kMergeRecord, kDeleteRecord, kLogDataRecord };
|
||||
|
||||
// an entry for Put, Merge or Delete entry for write batches. Used in
|
||||
// WBWIIterator.
|
||||
struct WriteEntry {
|
||||
WriteType type;
|
||||
Slice key;
|
||||
Slice value;
|
||||
};
|
||||
|
||||
// Iterator of one column family out of a WriteBatchWithIndex.
|
||||
class WBWIIterator {
|
||||
public:
|
||||
virtual ~WBWIIterator() {}
|
||||
|
||||
virtual bool Valid() const = 0;
|
||||
|
||||
virtual void Seek(const Slice& key) = 0;
|
||||
|
||||
virtual void Next() = 0;
|
||||
|
||||
virtual const WriteEntry& Entry() const = 0;
|
||||
|
||||
virtual Status status() const = 0;
|
||||
};
|
||||
|
||||
// A WriteBatchWithIndex with a binary searchable index built for all the keys
|
||||
// inserted.
|
||||
// In Put(), Merge() or Delete(), the same function of the wrapped will be
|
||||
// called. At the same time, indexes will be built.
|
||||
// By calling GetWriteBatch(), a user will get the WriteBatch for the data
|
||||
// they inserted, which can be used for DB::Write().
|
||||
// A user can call NewIterator() to create an iterator.
|
||||
class WriteBatchWithIndex {
|
||||
public:
|
||||
// index_comparator indicates the order when iterating data in the write
|
||||
// batch. Technically, it doesn't have to be the same as the one used in
|
||||
// the DB.
|
||||
// reserved_bytes: reserved bytes in underlying WriteBatch
|
||||
explicit WriteBatchWithIndex(const Comparator* index_comparator,
|
||||
size_t reserved_bytes = 0);
|
||||
virtual ~WriteBatchWithIndex();
|
||||
|
||||
WriteBatch* GetWriteBatch();
|
||||
|
||||
virtual void Put(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value);
|
||||
|
||||
virtual void Put(const Slice& key, const Slice& value);
|
||||
|
||||
virtual void Merge(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value);
|
||||
|
||||
virtual void Merge(const Slice& key, const Slice& value);
|
||||
|
||||
virtual 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);
|
||||
|
||||
// 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);
|
||||
// Create an iterator of the default column family.
|
||||
virtual WBWIIterator* NewIterator();
|
||||
|
||||
private:
|
||||
struct Rep;
|
||||
Rep* rep;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
// Also update Makefile if you change these
|
||||
#define ROCKSDB_MAJOR 3
|
||||
#define ROCKSDB_MINOR 4
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_MINOR 5
|
||||
#define ROCKSDB_PATCH 1
|
||||
|
||||
// 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
|
||||
|
||||
@@ -152,6 +152,7 @@ class WriteBatch {
|
||||
private:
|
||||
friend class WriteBatchInternal;
|
||||
|
||||
protected:
|
||||
std::string rep_; // See comment in write_batch.cc for the format of rep_
|
||||
|
||||
// Intentionally copyable
|
||||
|
||||
+1
-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.ReadOptions org.rocksdb.Filter org.rocksdb.BloomFilter org.rocksdb.RestoreOptions org.rocksdb.RestoreBackupableDB org.rocksdb.RocksEnv
|
||||
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
|
||||
|
||||
NATIVE_INCLUDE = ./include
|
||||
ROCKSDB_JAR = rocksdbjni.jar
|
||||
|
||||
@@ -35,16 +35,11 @@ public class RocksDBSample {
|
||||
assert(db == null);
|
||||
}
|
||||
|
||||
Filter filter = new BloomFilter(10);
|
||||
options.setCreateIfMissing(true)
|
||||
.createStatistics()
|
||||
.setWriteBufferSize(8 * SizeUnit.KB)
|
||||
.setMaxWriteBufferNumber(3)
|
||||
.setDisableSeekCompaction(true)
|
||||
.setBlockSize(64 * SizeUnit.KB)
|
||||
.setMaxBackgroundCompactions(10)
|
||||
.setFilter(filter)
|
||||
.setCacheNumShardBits(6)
|
||||
.setCompressionType(CompressionType.SNAPPY_COMPRESSION)
|
||||
.setCompactionStyle(CompactionStyle.UNIVERSAL);
|
||||
Statistics stats = options.statisticsPtr();
|
||||
@@ -52,10 +47,7 @@ public class RocksDBSample {
|
||||
assert(options.createIfMissing() == true);
|
||||
assert(options.writeBufferSize() == 8 * SizeUnit.KB);
|
||||
assert(options.maxWriteBufferNumber() == 3);
|
||||
assert(options.disableSeekCompaction() == true);
|
||||
assert(options.blockSize() == 64 * SizeUnit.KB);
|
||||
assert(options.maxBackgroundCompactions() == 10);
|
||||
assert(options.cacheNumShardBits() == 6);
|
||||
assert(options.compressionType() == CompressionType.SNAPPY_COMPRESSION);
|
||||
assert(options.compactionStyle() == CompactionStyle.UNIVERSAL);
|
||||
|
||||
@@ -82,6 +74,15 @@ public class RocksDBSample {
|
||||
options.setTableFormatConfig(new PlainTableConfig());
|
||||
assert(options.tableFactoryName().equals("PlainTable"));
|
||||
|
||||
BlockBasedTableConfig table_options = new BlockBasedTableConfig();
|
||||
table_options.setBlockCacheSize(64 * SizeUnit.KB)
|
||||
.setFilterBitsPerKey(10)
|
||||
.setCacheNumShardBits(6);
|
||||
assert(table_options.blockCacheSize() == 64 * SizeUnit.KB);
|
||||
assert(table_options.cacheNumShardBits() == 6);
|
||||
options.setTableFormatConfig(table_options);
|
||||
assert(options.tableFactoryName().equals("BlockBasedTable"));
|
||||
|
||||
try {
|
||||
db = RocksDB.open(options, db_path_not_found);
|
||||
db.put("hello".getBytes(), "world".getBytes());
|
||||
@@ -254,6 +255,5 @@ public class RocksDBSample {
|
||||
// be sure to dispose c++ pointers
|
||||
options.dispose();
|
||||
readOptions.dispose();
|
||||
filter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,15 @@ public class BackupableDB extends RocksDB {
|
||||
public void createNewBackup(boolean flushBeforeBackup) {
|
||||
createNewBackup(nativeHandle_, flushBeforeBackup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes old backups, keeping latest numBackupsToKeep alive.
|
||||
*
|
||||
* @param numBackupsToKeep Number of latest backups to keep.
|
||||
*/
|
||||
public void purgeOldBackups(int numBackupsToKeep) {
|
||||
purgeOldBackups(nativeHandle_, numBackupsToKeep);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -77,4 +86,5 @@ 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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* The config for plain table sst format.
|
||||
*
|
||||
* BlockBasedTable is a RocksDB's default SST file format.
|
||||
*/
|
||||
public class BlockBasedTableConfig extends TableFormatConfig {
|
||||
|
||||
public BlockBasedTableConfig() {
|
||||
noBlockCache_ = false;
|
||||
blockCacheSize_ = 8 * 1024 * 1024;
|
||||
blockSize_ = 4 * 1024;
|
||||
blockSizeDeviation_ =10;
|
||||
blockRestartInterval_ =16;
|
||||
wholeKeyFiltering_ = true;
|
||||
bitsPerKey_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable block cache. If this is set to true,
|
||||
* then no block cache should be used, and the block_cache should
|
||||
* point to a nullptr object.
|
||||
* Default: false
|
||||
*
|
||||
* @param noBlockCache if use block cache
|
||||
* @return the reference to the current config.
|
||||
*/
|
||||
public BlockBasedTableConfig setNoBlockCache(boolean noBlockCache) {
|
||||
noBlockCache_ = noBlockCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if block cache is disabled
|
||||
*/
|
||||
public boolean noBlockCache() {
|
||||
return noBlockCache_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of cache in bytes that will be used by RocksDB.
|
||||
* If cacheSize is non-positive, then cache will not be used.
|
||||
* DEFAULT: 8M
|
||||
*
|
||||
* @param blockCacheSize block cache size in bytes
|
||||
* @return the reference to the current config.
|
||||
*/
|
||||
public BlockBasedTableConfig setBlockCacheSize(long blockCacheSize) {
|
||||
blockCacheSize_ = blockCacheSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return block cache size in bytes
|
||||
*/
|
||||
public long blockCacheSize() {
|
||||
return blockCacheSize_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the number of shards for the block cache.
|
||||
* This is applied only if cacheSize is set to non-negative.
|
||||
*
|
||||
* @param numShardBits the number of shard bits. The resulting
|
||||
* number of shards would be 2 ^ numShardBits. Any negative
|
||||
* number means use default settings."
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
public BlockBasedTableConfig setCacheNumShardBits(int numShardBits) {
|
||||
numShardBits_ = numShardBits;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of shard bits used in the block cache.
|
||||
* The resulting number of shards would be 2 ^ (returned value).
|
||||
* Any negative number means use default settings.
|
||||
*
|
||||
* @return the number of shard bits used in the block cache.
|
||||
*/
|
||||
public int cacheNumShardBits() {
|
||||
return numShardBits_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximate size of user data packed per block. Note that the
|
||||
* block size specified here corresponds to uncompressed data. The
|
||||
* actual size of the unit read from disk may be smaller if
|
||||
* compression is enabled. This parameter can be changed dynamically.
|
||||
* Default: 4K
|
||||
*
|
||||
* @param blockSize block size in bytes
|
||||
* @return the reference to the current config.
|
||||
*/
|
||||
public BlockBasedTableConfig setBlockSize(long blockSize) {
|
||||
blockSize_ = blockSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return block size in bytes
|
||||
*/
|
||||
public long blockSize() {
|
||||
return blockSize_;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to close a block before it reaches the configured
|
||||
* 'block_size'. If the percentage of free space in the current block is less
|
||||
* than this specified number and adding a new record to the block will
|
||||
* exceed the configured block size, then this block will be closed and the
|
||||
* new record will be written to the next block.
|
||||
* Default is 10.
|
||||
*
|
||||
* @param blockSizeDeviation the deviation to block size allowed
|
||||
* @return the reference to the current config.
|
||||
*/
|
||||
public BlockBasedTableConfig setBlockSizeDeviation(int blockSizeDeviation) {
|
||||
blockSizeDeviation_ = blockSizeDeviation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the hash table ratio.
|
||||
*/
|
||||
public int blockSizeDeviation() {
|
||||
return blockSizeDeviation_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set block restart interval
|
||||
*
|
||||
* @param restartInterval block restart interval.
|
||||
* @return the reference to the current config.
|
||||
*/
|
||||
public BlockBasedTableConfig setBlockRestartInterval(int restartInterval) {
|
||||
blockRestartInterval_ = restartInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return block restart interval
|
||||
*/
|
||||
public int blockRestartInterval() {
|
||||
return blockRestartInterval_;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, place whole keys in the filter (not just prefixes).
|
||||
* This must generally be true for gets to be efficient.
|
||||
* Default: true
|
||||
*
|
||||
* @param wholeKeyFiltering if enable whole key filtering
|
||||
* @return the reference to the current config.
|
||||
*/
|
||||
public BlockBasedTableConfig setWholeKeyFiltering(boolean wholeKeyFiltering) {
|
||||
wholeKeyFiltering_ = wholeKeyFiltering;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if whole key filtering is enabled
|
||||
*/
|
||||
public boolean wholeKeyFiltering() {
|
||||
return wholeKeyFiltering_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the specified filter policy to reduce disk reads.
|
||||
*
|
||||
* Filter should not be disposed before options instances using this filter is
|
||||
* disposed. If dispose() function is not called, then filter object will be
|
||||
* GC'd automatically.
|
||||
*
|
||||
* Filter instance can be re-used in multiple options instances.
|
||||
*
|
||||
* @param Filter policy java instance.
|
||||
* @return the reference to the current config.
|
||||
*/
|
||||
public BlockBasedTableConfig setFilterBitsPerKey(int bitsPerKey) {
|
||||
bitsPerKey_ = bitsPerKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected long newTableFactoryHandle() {
|
||||
return newTableFactoryHandle(noBlockCache_, blockCacheSize_, numShardBits_,
|
||||
blockSize_, blockSizeDeviation_, blockRestartInterval_,
|
||||
wholeKeyFiltering_, bitsPerKey_);
|
||||
}
|
||||
|
||||
private native long newTableFactoryHandle(
|
||||
boolean noBlockCache, long blockCacheSize, int numShardbits,
|
||||
long blockSize, int blockSizeDeviation, int blockRestartInterval,
|
||||
boolean wholeKeyFiltering, int bitsPerKey);
|
||||
|
||||
private boolean noBlockCache_;
|
||||
private long blockCacheSize_;
|
||||
private int numShardBits_;
|
||||
private long shard;
|
||||
private long blockSize_;
|
||||
private int blockSizeDeviation_;
|
||||
private int blockRestartInterval_;
|
||||
private boolean wholeKeyFiltering_;
|
||||
private int bitsPerKey_;
|
||||
}
|
||||
+13
-237
@@ -136,135 +136,6 @@ public class Options extends RocksObject {
|
||||
return maxWriteBufferNumber(nativeHandle_);
|
||||
}
|
||||
|
||||
/*
|
||||
* Approximate size of user data packed per block. Note that the
|
||||
* block size specified here corresponds to uncompressed data. The
|
||||
* actual size of the unit read from disk may be smaller if
|
||||
* compression is enabled. This parameter can be changed dynamically.
|
||||
*
|
||||
* Default: 4K
|
||||
*
|
||||
* @param blockSize the size of each block in bytes.
|
||||
* @return the instance of the current Options.
|
||||
* @see RocksDB.open()
|
||||
*/
|
||||
public Options setBlockSize(long blockSize) {
|
||||
assert(isInitialized());
|
||||
setBlockSize(nativeHandle_, blockSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the size of a block in bytes.
|
||||
*
|
||||
* @return block size.
|
||||
* @see setBlockSize()
|
||||
*/
|
||||
public long blockSize() {
|
||||
assert(isInitialized());
|
||||
return blockSize(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the specified filter policy to reduce disk reads.
|
||||
*
|
||||
* Filter should not be disposed before options instances using this filter is
|
||||
* disposed. If dispose() function is not called, then filter object will be
|
||||
* GC'd automatically.
|
||||
*
|
||||
* Filter instance can be re-used in multiple options instances.
|
||||
*
|
||||
* @param Filter policy java instance.
|
||||
* @return the instance of the current Options.
|
||||
* @see RocksDB.open()
|
||||
*/
|
||||
public Options setFilter(Filter filter) {
|
||||
assert(isInitialized());
|
||||
setFilterHandle(nativeHandle_, filter.nativeHandle_);
|
||||
filter_ = filter;
|
||||
return this;
|
||||
}
|
||||
private native void setFilterHandle(long optHandle, long filterHandle);
|
||||
|
||||
/*
|
||||
* Disable compaction triggered by seek.
|
||||
* With bloomfilter and fast storage, a miss on one level
|
||||
* is very cheap if the file handle is cached in table cache
|
||||
* (which is true if max_open_files is large).
|
||||
* Default: true
|
||||
*
|
||||
* @param disableSeekCompaction a boolean value to specify whether
|
||||
* to disable seek compaction.
|
||||
* @return the instance of the current Options.
|
||||
* @see RocksDB.open()
|
||||
*/
|
||||
public Options setDisableSeekCompaction(boolean disableSeekCompaction) {
|
||||
assert(isInitialized());
|
||||
setDisableSeekCompaction(nativeHandle_, disableSeekCompaction);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if disable seek compaction is set to true.
|
||||
*
|
||||
* @return true if disable seek compaction is set to true.
|
||||
* @see setDisableSeekCompaction()
|
||||
*/
|
||||
public boolean disableSeekCompaction() {
|
||||
assert(isInitialized());
|
||||
return disableSeekCompaction(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of cache in bytes that will be used by RocksDB.
|
||||
* If cacheSize is non-positive, then cache will not be used.
|
||||
*
|
||||
* DEFAULT: 8M
|
||||
* @see setCacheNumShardBits()
|
||||
*/
|
||||
public Options setCacheSize(long cacheSize) {
|
||||
cacheSize_ = cacheSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of cache in bytes that will be used by RocksDB.
|
||||
*
|
||||
* @see cacheNumShardBits()
|
||||
*/
|
||||
public long cacheSize() {
|
||||
return cacheSize_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the number of shards for the block cache.
|
||||
* This is applied only if cacheSize is set to non-negative.
|
||||
*
|
||||
* @param numShardBits the number of shard bits. The resulting
|
||||
* number of shards would be 2 ^ numShardBits. Any negative
|
||||
* number means use default settings."
|
||||
* @return the reference to the current option.
|
||||
*
|
||||
* @see setCacheSize()
|
||||
*/
|
||||
public Options setCacheNumShardBits(int numShardBits) {
|
||||
numShardBits_ = numShardBits;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of shard bits used in the block cache.
|
||||
* The resulting number of shards would be 2 ^ (returned value).
|
||||
* Any negative number means use default settings.
|
||||
*
|
||||
* @return the number of shard bits used in the block cache.
|
||||
*
|
||||
* @see cacheSize()
|
||||
*/
|
||||
public int cacheNumShardBits() {
|
||||
return numShardBits_;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, an error will be thrown during RocksDB.open() if the
|
||||
* database already exists.
|
||||
@@ -1344,26 +1215,26 @@ public class Options extends RocksObject {
|
||||
}
|
||||
private native void setBlockRestartInterval(
|
||||
long handle, int blockRestartInterval);
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
*/
|
||||
public CompressionType compressionType() {
|
||||
return CompressionType.values()[compressionType(nativeHandle_)];
|
||||
}
|
||||
private native byte compressionType(long handle);
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -1372,22 +1243,22 @@ public class Options extends RocksObject {
|
||||
return this;
|
||||
}
|
||||
private native void setCompressionType(long handle, byte compressionType);
|
||||
|
||||
|
||||
/**
|
||||
* Compaction style for DB.
|
||||
*
|
||||
*
|
||||
* @return Compaction style.
|
||||
*/
|
||||
*/
|
||||
public CompactionStyle compactionStyle() {
|
||||
return CompactionStyle.values()[compactionStyle(nativeHandle_)];
|
||||
}
|
||||
private native byte compactionStyle(long handle);
|
||||
|
||||
|
||||
/**
|
||||
* Set compaction style for DB.
|
||||
*
|
||||
*
|
||||
* Default: LEVEL.
|
||||
*
|
||||
*
|
||||
* @param compactionStyle Compaction style.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
@@ -1397,33 +1268,6 @@ public class Options extends RocksObject {
|
||||
}
|
||||
private native void setCompactionStyle(long handle, byte compactionStyle);
|
||||
|
||||
/**
|
||||
* If true, place whole keys in the filter (not just prefixes).
|
||||
* This must generally be true for gets to be efficient.
|
||||
* Default: true
|
||||
*
|
||||
* @return if true, then whole-key-filtering is on.
|
||||
*/
|
||||
public boolean wholeKeyFiltering() {
|
||||
return wholeKeyFiltering(nativeHandle_);
|
||||
}
|
||||
private native boolean wholeKeyFiltering(long handle);
|
||||
|
||||
/**
|
||||
* If true, place whole keys in the filter (not just prefixes).
|
||||
* This must generally be true for gets to be efficient.
|
||||
* Default: true
|
||||
*
|
||||
* @param wholeKeyFiltering if true, then whole-key-filtering is on.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
public Options setWholeKeyFiltering(boolean wholeKeyFiltering) {
|
||||
setWholeKeyFiltering(nativeHandle_, wholeKeyFiltering);
|
||||
return this;
|
||||
}
|
||||
private native void setWholeKeyFiltering(
|
||||
long handle, boolean wholeKeyFiltering);
|
||||
|
||||
/**
|
||||
* If level-styled compaction is used, then this number determines
|
||||
* the total number of levels.
|
||||
@@ -1897,35 +1741,6 @@ public class Options extends RocksObject {
|
||||
private native void setRateLimitDelayMaxMilliseconds(
|
||||
long handle, int rateLimitDelayMaxMilliseconds);
|
||||
|
||||
/**
|
||||
* Disable block cache. If this is set to true,
|
||||
* then no block cache should be used, and the block_cache should
|
||||
* point to a nullptr object.
|
||||
* Default: false
|
||||
*
|
||||
* @return true if block cache is disabled.
|
||||
*/
|
||||
public boolean noBlockCache() {
|
||||
return noBlockCache(nativeHandle_);
|
||||
}
|
||||
private native boolean noBlockCache(long handle);
|
||||
|
||||
/**
|
||||
* Disable block cache. If this is set to true,
|
||||
* then no block cache should be used, and the block_cache should
|
||||
* point to a nullptr object.
|
||||
* Default: false
|
||||
*
|
||||
* @param noBlockCache true if block-cache is disabled.
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
public Options setNoBlockCache(boolean noBlockCache) {
|
||||
setNoBlockCache(nativeHandle_, noBlockCache);
|
||||
return this;
|
||||
}
|
||||
private native void setNoBlockCache(
|
||||
long handle, boolean noBlockCache);
|
||||
|
||||
/**
|
||||
* The size of one block in arena memory allocation.
|
||||
* If <= 0, a proper value is automatically calculated (usually 1/10 of
|
||||
@@ -2023,39 +1838,6 @@ public class Options extends RocksObject {
|
||||
private native void setPurgeRedundantKvsWhileFlush(
|
||||
long handle, boolean purgeRedundantKvsWhileFlush);
|
||||
|
||||
/**
|
||||
* This is used to close a block before it reaches the configured
|
||||
* 'block_size'. If the percentage of free space in the current block is less
|
||||
* than this specified number and adding a new record to the block will
|
||||
* exceed the configured block size, then this block will be closed and the
|
||||
* new record will be written to the next block.
|
||||
* Default is 10.
|
||||
*
|
||||
* @return the target block size
|
||||
*/
|
||||
public int blockSizeDeviation() {
|
||||
return blockSizeDeviation(nativeHandle_);
|
||||
}
|
||||
private native int blockSizeDeviation(long handle);
|
||||
|
||||
/**
|
||||
* This is used to close a block before it reaches the configured
|
||||
* 'block_size'. If the percentage of free space in the current block is less
|
||||
* than this specified number and adding a new record to the block will
|
||||
* exceed the configured block size, then this block will be closed and the
|
||||
* new record will be written to the next block.
|
||||
* Default is 10.
|
||||
*
|
||||
* @param blockSizeDeviation the target block size
|
||||
* @return the reference to the current option.
|
||||
*/
|
||||
public Options setBlockSizeDeviation(int blockSizeDeviation) {
|
||||
setBlockSizeDeviation(nativeHandle_, blockSizeDeviation);
|
||||
return this;
|
||||
}
|
||||
private native void setBlockSizeDeviation(
|
||||
long handle, int blockSizeDeviation);
|
||||
|
||||
/**
|
||||
* If true, compaction will verify checksum on every read that happens
|
||||
* as part of compaction
|
||||
@@ -2437,11 +2219,6 @@ public class Options extends RocksObject {
|
||||
private native void setMaxWriteBufferNumber(
|
||||
long handle, int maxWriteBufferNumber);
|
||||
private native int maxWriteBufferNumber(long handle);
|
||||
private native void setBlockSize(long handle, long blockSize);
|
||||
private native long blockSize(long handle);
|
||||
private native void setDisableSeekCompaction(
|
||||
long handle, boolean disableSeekCompaction);
|
||||
private native boolean disableSeekCompaction(long handle);
|
||||
private native void setMaxBackgroundCompactions(
|
||||
long handle, int maxBackgroundCompactions);
|
||||
private native int maxBackgroundCompactions(long handle);
|
||||
@@ -2459,6 +2236,5 @@ public class Options extends RocksObject {
|
||||
|
||||
long cacheSize_;
|
||||
int numShardBits_;
|
||||
Filter filter_;
|
||||
RocksEnv env_;
|
||||
}
|
||||
|
||||
@@ -99,11 +99,11 @@ public class RocksDB extends RocksObject {
|
||||
/**
|
||||
* The factory constructor of RocksDB that opens a RocksDB instance given
|
||||
* the path to the database using the specified options and db path.
|
||||
*
|
||||
*
|
||||
* Options instance *should* not be disposed before all DBs using this options
|
||||
* instance have been closed. If user doesn't call options dispose explicitly,
|
||||
* then this options instance will be GC'd automatically.
|
||||
*
|
||||
*
|
||||
* Options instance can be re-used to open multiple DBs if DB statistics is
|
||||
* not used. If DB statistics are required, then its recommended to open DB
|
||||
* with new Options instance as underlying native statistics instance does not
|
||||
@@ -115,13 +115,12 @@ public class RocksDB extends RocksObject {
|
||||
// in RocksDB can prevent Java to GC during the life-time of
|
||||
// the currently-created RocksDB.
|
||||
RocksDB db = new RocksDB();
|
||||
db.open(options.nativeHandle_, options.cacheSize_,
|
||||
options.numShardBits_, path);
|
||||
|
||||
db.open(options.nativeHandle_, path);
|
||||
|
||||
db.storeOptionsInstance(options);
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
private void storeOptionsInstance(Options options) {
|
||||
options_ = options;
|
||||
}
|
||||
@@ -334,8 +333,7 @@ public class RocksDB extends RocksObject {
|
||||
|
||||
// native methods
|
||||
protected native void open(
|
||||
long optionsHandle, long cacheSize, int numShardBits,
|
||||
String path) throws RocksDBException;
|
||||
long optionsHandle, String path) throws RocksDBException;
|
||||
protected native void put(
|
||||
long handle, byte[] key, int keyLen,
|
||||
byte[] value, int valueLen) throws RocksDBException;
|
||||
|
||||
@@ -446,7 +446,6 @@ public class DbBenchmark {
|
||||
randSeed_ = (Long) flags.get(Flag.seed);
|
||||
databaseDir_ = (String) flags.get(Flag.db);
|
||||
writesPerSeconds_ = (Integer) flags.get(Flag.writes_per_second);
|
||||
cacheSize_ = (Long) flags.get(Flag.cache_size);
|
||||
memtable_ = (String) flags.get(Flag.memtablerep);
|
||||
maxWriteBufferNumber_ = (Integer) flags.get(Flag.max_write_buffer_number);
|
||||
prefixSize_ = (Integer) flags.get(Flag.prefix_size);
|
||||
@@ -491,7 +490,6 @@ public class DbBenchmark {
|
||||
}
|
||||
|
||||
private void prepareOptions(Options options) {
|
||||
options.setCacheSize(cacheSize_);
|
||||
if (!useExisting_) {
|
||||
options.setCreateIfMissing(true);
|
||||
} else {
|
||||
@@ -521,6 +519,13 @@ public class DbBenchmark {
|
||||
if (usePlainTable_) {
|
||||
options.setTableFormatConfig(
|
||||
new PlainTableConfig().setKeySize(keySize_));
|
||||
} else {
|
||||
BlockBasedTableConfig table_options = new BlockBasedTableConfig();
|
||||
table_options.setBlockSize((Long)flags_.get(Flag.block_size))
|
||||
.setBlockCacheSize((Long)flags_.get(Flag.cache_size))
|
||||
.setFilterBitsPerKey((Integer)flags_.get(Flag.bloom_bits))
|
||||
.setCacheNumShardBits((Integer)flags_.get(Flag.cache_numshardbits));
|
||||
options.setTableFormatConfig(table_options);
|
||||
}
|
||||
options.setWriteBufferSize(
|
||||
(Long)flags_.get(Flag.write_buffer_size));
|
||||
@@ -532,12 +537,6 @@ public class DbBenchmark {
|
||||
(Integer)flags_.get(Flag.max_background_compactions));
|
||||
options.setMaxBackgroundFlushes(
|
||||
(Integer)flags_.get(Flag.max_background_flushes));
|
||||
options.setCacheSize(
|
||||
(Long)flags_.get(Flag.cache_size));
|
||||
options.setCacheNumShardBits(
|
||||
(Integer)flags_.get(Flag.cache_numshardbits));
|
||||
options.setBlockSize(
|
||||
(Long)flags_.get(Flag.block_size));
|
||||
options.setMaxOpenFiles(
|
||||
(Integer)flags_.get(Flag.open_files));
|
||||
options.setTableCacheRemoveScanCountLimit(
|
||||
@@ -548,8 +547,6 @@ public class DbBenchmark {
|
||||
(Boolean)flags_.get(Flag.use_fsync));
|
||||
options.setWalDir(
|
||||
(String)flags_.get(Flag.wal_dir));
|
||||
options.setDisableSeekCompaction(
|
||||
(Boolean)flags_.get(Flag.disable_seek_compaction));
|
||||
options.setDeleteObsoleteFilesPeriodMicros(
|
||||
(Integer)flags_.get(Flag.delete_obsolete_files_period_micros));
|
||||
options.setTableCacheNumshardbits(
|
||||
@@ -604,15 +601,6 @@ public class DbBenchmark {
|
||||
(Integer)flags_.get(Flag.max_successive_merges));
|
||||
options.setWalTtlSeconds((Long)flags_.get(Flag.wal_ttl_seconds));
|
||||
options.setWalSizeLimitMB((Long)flags_.get(Flag.wal_size_limit_MB));
|
||||
int bloomBits = (Integer)flags_.get(Flag.bloom_bits);
|
||||
if (bloomBits > 0) {
|
||||
// Internally, options will keep a reference to this BloomFilter.
|
||||
// This will disallow Java to GC this BloomFilter. In addition,
|
||||
// options.dispose() will release the c++ object of this BloomFilter.
|
||||
// As a result, the caller should not directly call
|
||||
// BloomFilter.dispose().
|
||||
options.setFilter(new BloomFilter(bloomBits));
|
||||
}
|
||||
/* TODO(yhchiang): enable the following parameters
|
||||
options.setCompressionType((String)flags_.get(Flag.compression_type));
|
||||
options.setCompressionLevel((Integer)flags_.get(Flag.compression_level));
|
||||
@@ -1160,7 +1148,7 @@ public class DbBenchmark {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
},
|
||||
block_size(defaultOptions_.blockSize(),
|
||||
block_size(defaultBlockBasedTableOptions_.blockSize(),
|
||||
"Number of bytes in a block.") {
|
||||
@Override public Object parseValue(String value) {
|
||||
return Long.parseLong(value);
|
||||
@@ -1312,12 +1300,6 @@ public class DbBenchmark {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
},
|
||||
disable_seek_compaction(false,"Option to disable compaction\n" +
|
||||
"\ttriggered by read.") {
|
||||
@Override public Object parseValue(String value) {
|
||||
return parseBoolean(value);
|
||||
}
|
||||
},
|
||||
delete_obsolete_files_period_micros(0,"Option to delete\n" +
|
||||
"\tobsolete files periodically. 0 means that obsolete files are\n" +
|
||||
"\tdeleted after every compaction run.") {
|
||||
@@ -1597,7 +1579,6 @@ public class DbBenchmark {
|
||||
final int threadNum_;
|
||||
final int writesPerSeconds_;
|
||||
final long randSeed_;
|
||||
final long cacheSize_;
|
||||
final boolean useExisting_;
|
||||
final String databaseDir_;
|
||||
double compressionRatio_;
|
||||
@@ -1620,6 +1601,8 @@ public class DbBenchmark {
|
||||
// as the scope of a static member equals to the scope of the problem,
|
||||
// we let its c++ pointer to be disposed in its finalizer.
|
||||
static Options defaultOptions_ = new Options();
|
||||
static BlockBasedTableConfig defaultBlockBasedTableOptions_ =
|
||||
new BlockBasedTableConfig();
|
||||
String compressionType_;
|
||||
CompressionType compression_;
|
||||
}
|
||||
|
||||
@@ -214,24 +214,6 @@ public class OptionsTest {
|
||||
assert(opt.minWriteBufferNumberToMerge() == intValue);
|
||||
}
|
||||
|
||||
{ // BlockSize test
|
||||
long longValue = rand.nextLong();
|
||||
opt.setBlockSize(longValue);
|
||||
assert(opt.blockSize() == longValue);
|
||||
}
|
||||
|
||||
{ // BlockRestartInterval test
|
||||
int intValue = rand.nextInt();
|
||||
opt.setBlockRestartInterval(intValue);
|
||||
assert(opt.blockRestartInterval() == intValue);
|
||||
}
|
||||
|
||||
{ // WholeKeyFiltering test
|
||||
boolean boolValue = rand.nextBoolean();
|
||||
opt.setWholeKeyFiltering(boolValue);
|
||||
assert(opt.wholeKeyFiltering() == boolValue);
|
||||
}
|
||||
|
||||
{ // NumLevels test
|
||||
int intValue = rand.nextInt();
|
||||
opt.setNumLevels(intValue);
|
||||
@@ -304,12 +286,6 @@ public class OptionsTest {
|
||||
assert(opt.maxGrandparentOverlapFactor() == intValue);
|
||||
}
|
||||
|
||||
{ // DisableSeekCompaction test
|
||||
boolean boolValue = rand.nextBoolean();
|
||||
opt.setDisableSeekCompaction(boolValue);
|
||||
assert(opt.disableSeekCompaction() == boolValue);
|
||||
}
|
||||
|
||||
{ // SoftRateLimit test
|
||||
double doubleValue = rand.nextDouble();
|
||||
opt.setSoftRateLimit(doubleValue);
|
||||
@@ -328,12 +304,6 @@ public class OptionsTest {
|
||||
assert(opt.rateLimitDelayMaxMilliseconds() == intValue);
|
||||
}
|
||||
|
||||
{ // NoBlockCache test
|
||||
boolean boolValue = rand.nextBoolean();
|
||||
opt.setNoBlockCache(boolValue);
|
||||
assert(opt.noBlockCache() == boolValue);
|
||||
}
|
||||
|
||||
{ // ArenaBlockSize test
|
||||
long longValue = rand.nextLong();
|
||||
opt.setArenaBlockSize(longValue);
|
||||
@@ -352,12 +322,6 @@ public class OptionsTest {
|
||||
assert(opt.purgeRedundantKvsWhileFlush() == boolValue);
|
||||
}
|
||||
|
||||
{ // BlockSizeDeviation test
|
||||
int intValue = rand.nextInt();
|
||||
opt.setBlockSizeDeviation(intValue);
|
||||
assert(opt.blockSizeDeviation() == intValue);
|
||||
}
|
||||
|
||||
{ // VerifyChecksumsInCompaction test
|
||||
boolean boolValue = rand.nextBoolean();
|
||||
opt.setVerifyChecksumsInCompaction(boolValue);
|
||||
|
||||
@@ -40,7 +40,26 @@ void Java_org_rocksdb_BackupableDB_open(
|
||||
*/
|
||||
void Java_org_rocksdb_BackupableDB_createNewBackup(
|
||||
JNIEnv* env, jobject jbdb, jlong jhandle, jboolean jflag) {
|
||||
reinterpret_cast<rocksdb::BackupableDB*>(jhandle)->CreateNewBackup(jflag);
|
||||
rocksdb::Status s =
|
||||
reinterpret_cast<rocksdb::BackupableDB*>(jhandle)->CreateNewBackup(jflag);
|
||||
if (!s.ok()) {
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupableDB
|
||||
* Method: purgeOldBackups
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupableDB_purgeOldBackups(
|
||||
JNIEnv* env, jobject jbdb, jlong jhandle, jboolean jnumBackupsToKeep) {
|
||||
rocksdb::Status s =
|
||||
reinterpret_cast<rocksdb::BackupableDB*>(jhandle)->
|
||||
PurgeOldBackups(jnumBackupsToKeep);
|
||||
if (!s.ok()) {
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
@@ -118,17 +117,6 @@ jlong Java_org_rocksdb_Options_statisticsPtr(
|
||||
return reinterpret_cast<jlong>(st);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setFilterHandle
|
||||
* Signature: (JJ)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setFilterHandle(
|
||||
JNIEnv* env, jobject jobj, jlong jopt_handle, jlong jfilter_handle) {
|
||||
reinterpret_cast<rocksdb::Options*>(jopt_handle)->filter_policy =
|
||||
reinterpret_cast<rocksdb::FilterPolicy*>(jfilter_handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: maxWriteBufferNumber
|
||||
@@ -139,49 +127,6 @@ jint Java_org_rocksdb_Options_maxWriteBufferNumber(
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->max_write_buffer_number;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setBlockSize
|
||||
* Signature: (JJ)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setBlockSize(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jlong jblock_size) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->block_size =
|
||||
static_cast<size_t>(jblock_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: blockSize
|
||||
* Signature: (J)J
|
||||
*/
|
||||
jlong Java_org_rocksdb_Options_blockSize(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->block_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setDisableSeekCompaction
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setDisableSeekCompaction(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle,
|
||||
jboolean jdisable_seek_compaction) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->disable_seek_compaction =
|
||||
jdisable_seek_compaction;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: disableSeekCompaction
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_Options_disableSeekCompaction(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->disable_seek_compaction;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: errorIfExists
|
||||
@@ -287,27 +232,6 @@ void Java_org_rocksdb_Options_setUseFsync(
|
||||
static_cast<bool>(use_fsync);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: dbStatsLogInterval
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_Options_dbStatsLogInterval(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->db_stats_log_interval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setDbStatsLogInterval
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setDbStatsLogInterval(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jint db_stats_log_interval) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->db_stats_log_interval =
|
||||
static_cast<int>(db_stats_log_interval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: dbLogDir
|
||||
@@ -914,27 +838,6 @@ void Java_org_rocksdb_Options_setMinWriteBufferNumberToMerge(
|
||||
static_cast<int>(jmin_write_buffer_number_to_merge);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: blockRestartInterval
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_Options_blockRestartInterval(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->block_restart_interval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setBlockRestartInterval
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setBlockRestartInterval(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jint jblock_restart_interval) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->block_restart_interval =
|
||||
static_cast<int>(jblock_restart_interval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setCompressionType
|
||||
@@ -977,27 +880,6 @@ jbyte Java_org_rocksdb_Options_compactionStyle(
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->compaction_style;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: wholeKeyFiltering
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_Options_wholeKeyFiltering(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->whole_key_filtering;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setWholeKeyFiltering
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setWholeKeyFiltering(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jboolean jwhole_key_filtering) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->whole_key_filtering =
|
||||
static_cast<bool>(jwhole_key_filtering);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: numLevels
|
||||
@@ -1345,27 +1227,6 @@ void Java_org_rocksdb_Options_setRateLimitDelayMaxMilliseconds(
|
||||
static_cast<int>(jrate_limit_delay_max_milliseconds);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: noBlockCache
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_Options_noBlockCache(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->no_block_cache;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setNoBlockCache
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setNoBlockCache(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jboolean jno_block_cache) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->no_block_cache =
|
||||
static_cast<bool>(jno_block_cache);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: arenaBlockSize
|
||||
@@ -1435,28 +1296,6 @@ void Java_org_rocksdb_Options_setPurgeRedundantKvsWhileFlush(
|
||||
static_cast<bool>(jpurge_redundant_kvs_while_flush);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: blockSizeDeviation
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_Options_blockSizeDeviation(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->block_size_deviation;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setBlockSizeDeviation
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setBlockSizeDeviation(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle,
|
||||
jint jblock_size_deviation) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->block_size_deviation =
|
||||
static_cast<int>(jblock_size_deviation);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: verifyChecksumsInCompaction
|
||||
|
||||
@@ -26,21 +26,8 @@
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
void Java_org_rocksdb_RocksDB_open(
|
||||
JNIEnv* env, jobject jdb, jlong jopt_handle,
|
||||
jlong jcache_size, jint jnum_shardbits, jstring jdb_path) {
|
||||
JNIEnv* env, jobject jdb, jlong jopt_handle, jstring jdb_path) {
|
||||
auto opt = reinterpret_cast<rocksdb::Options*>(jopt_handle);
|
||||
if (jcache_size > 0) {
|
||||
opt->no_block_cache = false;
|
||||
if (jnum_shardbits >= 1) {
|
||||
opt->block_cache = rocksdb::NewLRUCache(jcache_size, jnum_shardbits);
|
||||
} else {
|
||||
opt->block_cache = rocksdb::NewLRUCache(jcache_size);
|
||||
}
|
||||
} else {
|
||||
opt->no_block_cache = true;
|
||||
opt->block_cache = nullptr;
|
||||
}
|
||||
|
||||
rocksdb::DB* db = nullptr;
|
||||
const char* db_path = env->GetStringUTFChars(jdb_path, 0);
|
||||
rocksdb::Status s = rocksdb::DB::Open(*opt, db_path, &db);
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
|
||||
#include <jni.h>
|
||||
#include "include/org_rocksdb_PlainTableConfig.h"
|
||||
#include "include/org_rocksdb_BlockBasedTableConfig.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_PlainTableConfig
|
||||
@@ -24,3 +27,34 @@ jlong Java_org_rocksdb_PlainTableConfig_newTableFactoryHandle(
|
||||
options.index_sparseness = jindex_sparseness;
|
||||
return reinterpret_cast<jlong>(rocksdb::NewPlainTableFactory(options));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BlockBasedTableConfig
|
||||
* Method: newTableFactoryHandle
|
||||
* Signature: (ZJIJIIZI)J
|
||||
*/
|
||||
jlong Java_org_rocksdb_BlockBasedTableConfig_newTableFactoryHandle(
|
||||
JNIEnv* env, jobject jobj, jboolean no_block_cache, jlong block_cache_size,
|
||||
jint num_shardbits, jlong block_size, jint block_size_deviation,
|
||||
jint block_restart_interval, jboolean whole_key_filtering,
|
||||
jint bits_per_key) {
|
||||
rocksdb::BlockBasedTableOptions options;
|
||||
options.no_block_cache = no_block_cache;
|
||||
|
||||
if (!no_block_cache && block_cache_size > 0) {
|
||||
if (num_shardbits > 0) {
|
||||
options.block_cache =
|
||||
rocksdb::NewLRUCache(block_cache_size, num_shardbits);
|
||||
} else {
|
||||
options.block_cache = rocksdb::NewLRUCache(block_cache_size);
|
||||
}
|
||||
}
|
||||
options.block_size = block_size;
|
||||
options.block_size_deviation = block_size_deviation;
|
||||
options.block_restart_interval = block_restart_interval;
|
||||
options.whole_key_filtering = whole_key_filtering;
|
||||
if (bits_per_key > 0) {
|
||||
options.filter_policy.reset(rocksdb::NewBloomFilterPolicy(bits_per_key));
|
||||
}
|
||||
return reinterpret_cast<jlong>(rocksdb::NewBlockBasedTableFactory(options));
|
||||
}
|
||||
|
||||
+3
-1
@@ -20,7 +20,9 @@ void PrintStack(int first_frames_to_skip) {}
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
// It's odd that including this breaks in GCC 7 but the build doesn't break
|
||||
// if I remove it even under GCC 4.8.
|
||||
// #include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
@@ -12,10 +12,12 @@ namespace rocksdb {
|
||||
AdaptiveTableFactory::AdaptiveTableFactory(
|
||||
std::shared_ptr<TableFactory> table_factory_to_write,
|
||||
std::shared_ptr<TableFactory> block_based_table_factory,
|
||||
std::shared_ptr<TableFactory> plain_table_factory)
|
||||
std::shared_ptr<TableFactory> plain_table_factory,
|
||||
std::shared_ptr<TableFactory> cuckoo_table_factory)
|
||||
: table_factory_to_write_(table_factory_to_write),
|
||||
block_based_table_factory_(block_based_table_factory),
|
||||
plain_table_factory_(plain_table_factory) {
|
||||
plain_table_factory_(plain_table_factory),
|
||||
cuckoo_table_factory_(cuckoo_table_factory) {
|
||||
if (!table_factory_to_write_) {
|
||||
table_factory_to_write_ = block_based_table_factory_;
|
||||
}
|
||||
@@ -25,12 +27,16 @@ AdaptiveTableFactory::AdaptiveTableFactory(
|
||||
if (!block_based_table_factory_) {
|
||||
block_based_table_factory_.reset(NewBlockBasedTableFactory());
|
||||
}
|
||||
if (!cuckoo_table_factory_) {
|
||||
cuckoo_table_factory_.reset(NewCuckooTableFactory());
|
||||
}
|
||||
}
|
||||
|
||||
extern const uint64_t kPlainTableMagicNumber;
|
||||
extern const uint64_t kLegacyPlainTableMagicNumber;
|
||||
extern const uint64_t kBlockBasedTableMagicNumber;
|
||||
extern const uint64_t kLegacyBlockBasedTableMagicNumber;
|
||||
extern const uint64_t kCuckooTableMagicNumber;
|
||||
|
||||
Status AdaptiveTableFactory::NewTableReader(
|
||||
const Options& options, const EnvOptions& soptions,
|
||||
@@ -49,6 +55,9 @@ Status AdaptiveTableFactory::NewTableReader(
|
||||
footer.table_magic_number() == kLegacyBlockBasedTableMagicNumber) {
|
||||
return block_based_table_factory_->NewTableReader(
|
||||
options, soptions, icomp, std::move(file), file_size, table);
|
||||
} else if (footer.table_magic_number() == kCuckooTableMagicNumber) {
|
||||
return cuckoo_table_factory_->NewTableReader(
|
||||
options, soptions, icomp, std::move(file), file_size, table);
|
||||
} else {
|
||||
return Status::NotSupported("Unidentified table format");
|
||||
}
|
||||
@@ -61,12 +70,46 @@ TableBuilder* AdaptiveTableFactory::NewTableBuilder(
|
||||
file, compression_type);
|
||||
}
|
||||
|
||||
std::string AdaptiveTableFactory::GetPrintableTableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(20000);
|
||||
const int kBufferSize = 200;
|
||||
char buffer[kBufferSize];
|
||||
|
||||
if (!table_factory_to_write_) {
|
||||
snprintf(buffer, kBufferSize, " write factory (%s) options:\n%s\n",
|
||||
table_factory_to_write_->Name(),
|
||||
table_factory_to_write_->GetPrintableTableOptions().c_str());
|
||||
ret.append(buffer);
|
||||
}
|
||||
if (!plain_table_factory_) {
|
||||
snprintf(buffer, kBufferSize, " %s options:\n%s\n",
|
||||
plain_table_factory_->Name(),
|
||||
plain_table_factory_->GetPrintableTableOptions().c_str());
|
||||
ret.append(buffer);
|
||||
}
|
||||
if (!block_based_table_factory_) {
|
||||
snprintf(buffer, kBufferSize, " %s options:\n%s\n",
|
||||
block_based_table_factory_->Name(),
|
||||
block_based_table_factory_->GetPrintableTableOptions().c_str());
|
||||
ret.append(buffer);
|
||||
}
|
||||
if (!cuckoo_table_factory_) {
|
||||
snprintf(buffer, kBufferSize, " %s options:\n%s\n",
|
||||
cuckoo_table_factory_->Name(),
|
||||
cuckoo_table_factory_->GetPrintableTableOptions().c_str());
|
||||
ret.append(buffer);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern TableFactory* NewAdaptiveTableFactory(
|
||||
std::shared_ptr<TableFactory> table_factory_to_write,
|
||||
std::shared_ptr<TableFactory> block_based_table_factory,
|
||||
std::shared_ptr<TableFactory> plain_table_factory) {
|
||||
return new AdaptiveTableFactory(
|
||||
table_factory_to_write, block_based_table_factory, plain_table_factory);
|
||||
std::shared_ptr<TableFactory> plain_table_factory,
|
||||
std::shared_ptr<TableFactory> cuckoo_table_factory) {
|
||||
return new AdaptiveTableFactory(table_factory_to_write,
|
||||
block_based_table_factory, plain_table_factory, cuckoo_table_factory);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <string>
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table.h"
|
||||
|
||||
@@ -28,7 +29,8 @@ class AdaptiveTableFactory : public TableFactory {
|
||||
explicit AdaptiveTableFactory(
|
||||
std::shared_ptr<TableFactory> table_factory_to_write,
|
||||
std::shared_ptr<TableFactory> block_based_table_factory,
|
||||
std::shared_ptr<TableFactory> plain_table_factory);
|
||||
std::shared_ptr<TableFactory> plain_table_factory,
|
||||
std::shared_ptr<TableFactory> cuckoo_table_factory);
|
||||
const char* Name() const override { return "AdaptiveTableFactory"; }
|
||||
Status NewTableReader(const Options& options, const EnvOptions& soptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
@@ -40,10 +42,22 @@ class AdaptiveTableFactory : public TableFactory {
|
||||
CompressionType compression_type) const
|
||||
override;
|
||||
|
||||
// Sanitizes the specified DB Options.
|
||||
Status SanitizeDBOptions(const DBOptions* db_opts) const override {
|
||||
if (db_opts->allow_mmap_reads == false) {
|
||||
return Status::NotSupported(
|
||||
"AdaptiveTable with allow_mmap_reads == false is not supported.");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::string GetPrintableTableOptions() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<TableFactory> table_factory_to_write_;
|
||||
std::shared_ptr<TableFactory> block_based_table_factory_;
|
||||
std::shared_ptr<TableFactory> plain_table_factory_;
|
||||
std::shared_ptr<TableFactory> cuckoo_table_factory_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+9
-3
@@ -321,7 +321,8 @@ Block::~Block() {
|
||||
}
|
||||
}
|
||||
|
||||
Iterator* Block::NewIterator(const Comparator* cmp, BlockIter* iter) {
|
||||
Iterator* Block::NewIterator(
|
||||
const Comparator* cmp, BlockIter* iter, bool total_order_seek) {
|
||||
if (size_ < 2*sizeof(uint32_t)) {
|
||||
if (iter != nullptr) {
|
||||
iter->SetStatus(Status::Corruption("bad block contents"));
|
||||
@@ -339,12 +340,17 @@ Iterator* Block::NewIterator(const Comparator* cmp, BlockIter* iter) {
|
||||
return NewEmptyIterator();
|
||||
}
|
||||
} else {
|
||||
BlockHashIndex* hash_index_ptr =
|
||||
total_order_seek ? nullptr : hash_index_.get();
|
||||
BlockPrefixIndex* prefix_index_ptr =
|
||||
total_order_seek ? nullptr : prefix_index_.get();
|
||||
|
||||
if (iter != nullptr) {
|
||||
iter->Initialize(cmp, data_, restart_offset_, num_restarts,
|
||||
hash_index_.get(), prefix_index_.get());
|
||||
hash_index_ptr, prefix_index_ptr);
|
||||
} else {
|
||||
iter = new BlockIter(cmp, data_, restart_offset_, num_restarts,
|
||||
hash_index_.get(), prefix_index_.get());
|
||||
hash_index_ptr, prefix_index_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -45,8 +45,12 @@ class Block {
|
||||
//
|
||||
// If iter is null, return new Iterator
|
||||
// If iter is not null, update this one and return it as Iterator*
|
||||
//
|
||||
// If total_order_seek is true, hash_index_ and prefix_index_ are ignored.
|
||||
// This option only applies for index block. For data block, hash_index_
|
||||
// and prefix_index_ are null, so this option does not matter.
|
||||
Iterator* NewIterator(const Comparator* comparator,
|
||||
BlockIter* iter = nullptr);
|
||||
BlockIter* iter = nullptr, bool total_order_seek = true);
|
||||
void SetBlockHashIndex(BlockHashIndex* hash_index);
|
||||
void SetBlockPrefixIndex(BlockPrefixIndex* prefix_index);
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ class ShortenedIndexBuilder : public IndexBuilder {
|
||||
public:
|
||||
explicit ShortenedIndexBuilder(const Comparator* comparator)
|
||||
: IndexBuilder(comparator),
|
||||
index_block_builder_(1 /* block_restart_interval == 1 */, comparator) {}
|
||||
index_block_builder_(1 /* block_restart_interval == 1 */) {}
|
||||
|
||||
virtual void AddIndexEntry(std::string* last_key_in_current_block,
|
||||
const Slice* first_key_in_next_block,
|
||||
@@ -385,7 +385,8 @@ class BlockBasedTableBuilder::BlockBasedTablePropertiesCollector
|
||||
};
|
||||
|
||||
struct BlockBasedTableBuilder::Rep {
|
||||
Options options;
|
||||
const Options options;
|
||||
const BlockBasedTableOptions table_options;
|
||||
const InternalKeyComparator& internal_comparator;
|
||||
WritableFile* file;
|
||||
uint64_t offset = 0;
|
||||
@@ -397,7 +398,6 @@ struct BlockBasedTableBuilder::Rep {
|
||||
|
||||
std::string last_key;
|
||||
CompressionType compression_type;
|
||||
ChecksumType checksum_type;
|
||||
TableProperties props;
|
||||
|
||||
bool closed = false; // Either Finish() or Abandon() has been called.
|
||||
@@ -413,31 +413,32 @@ struct BlockBasedTableBuilder::Rep {
|
||||
std::vector<std::unique_ptr<TablePropertiesCollector>>
|
||||
table_properties_collectors;
|
||||
|
||||
Rep(const Options& opt, const InternalKeyComparator& icomparator,
|
||||
WritableFile* f, FlushBlockPolicyFactory* flush_block_policy_factory,
|
||||
CompressionType compression_type, IndexType index_block_type,
|
||||
ChecksumType checksum_type)
|
||||
Rep(const Options& opt, const BlockBasedTableOptions& table_opt,
|
||||
const InternalKeyComparator& icomparator,
|
||||
WritableFile* f, CompressionType compression_type)
|
||||
: options(opt),
|
||||
table_options(table_opt),
|
||||
internal_comparator(icomparator),
|
||||
file(f),
|
||||
data_block(options, &internal_comparator),
|
||||
data_block(table_options.block_restart_interval),
|
||||
internal_prefix_transform(options.prefix_extractor.get()),
|
||||
index_builder(CreateIndexBuilder(index_block_type, &internal_comparator,
|
||||
&this->internal_prefix_transform)),
|
||||
index_builder(CreateIndexBuilder(
|
||||
table_options.index_type, &internal_comparator,
|
||||
&this->internal_prefix_transform)),
|
||||
compression_type(compression_type),
|
||||
checksum_type(checksum_type),
|
||||
filter_block(opt.filter_policy == nullptr
|
||||
? nullptr
|
||||
: new FilterBlockBuilder(opt, &internal_comparator)),
|
||||
flush_block_policy(flush_block_policy_factory->NewFlushBlockPolicy(
|
||||
options, data_block)) {
|
||||
filter_block(table_options.filter_policy == nullptr ?
|
||||
nullptr :
|
||||
new FilterBlockBuilder(opt, table_options, &internal_comparator)),
|
||||
flush_block_policy(
|
||||
table_options.flush_block_policy_factory->NewFlushBlockPolicy(
|
||||
table_options, data_block)) {
|
||||
for (auto& collector_factories :
|
||||
options.table_properties_collector_factories) {
|
||||
table_properties_collectors.emplace_back(
|
||||
collector_factories->CreateTablePropertiesCollector());
|
||||
}
|
||||
table_properties_collectors.emplace_back(
|
||||
new BlockBasedTablePropertiesCollector(index_block_type));
|
||||
new BlockBasedTablePropertiesCollector(table_options.index_type));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -445,16 +446,14 @@ BlockBasedTableBuilder::BlockBasedTableBuilder(
|
||||
const Options& options, const BlockBasedTableOptions& table_options,
|
||||
const InternalKeyComparator& internal_comparator, WritableFile* file,
|
||||
CompressionType compression_type)
|
||||
: rep_(new Rep(options, internal_comparator, file,
|
||||
table_options.flush_block_policy_factory.get(),
|
||||
compression_type, table_options.index_type,
|
||||
table_options.checksum)) {
|
||||
: rep_(new Rep(options, table_options, internal_comparator,
|
||||
file, compression_type)) {
|
||||
if (rep_->filter_block != nullptr) {
|
||||
rep_->filter_block->StartBlock(0);
|
||||
}
|
||||
if (options.block_cache_compressed.get() != nullptr) {
|
||||
if (table_options.block_cache_compressed.get() != nullptr) {
|
||||
BlockBasedTable::GenerateCachePrefix(
|
||||
options.block_cache_compressed.get(), file,
|
||||
table_options.block_cache_compressed.get(), file,
|
||||
&rep_->compressed_cache_key_prefix[0],
|
||||
&rep_->compressed_cache_key_prefix_size);
|
||||
}
|
||||
@@ -493,7 +492,7 @@ void BlockBasedTableBuilder::Add(const Slice& key, const Slice& value) {
|
||||
}
|
||||
|
||||
if (r->filter_block != nullptr) {
|
||||
r->filter_block->AddKey(key);
|
||||
r->filter_block->AddKey(ExtractUserKey(key));
|
||||
}
|
||||
|
||||
r->last_key.assign(key.data(), key.size());
|
||||
@@ -566,7 +565,7 @@ void BlockBasedTableBuilder::WriteRawBlock(const Slice& block_contents,
|
||||
char trailer[kBlockTrailerSize];
|
||||
trailer[0] = type;
|
||||
char* trailer_without_type = trailer + 1;
|
||||
switch (r->checksum_type) {
|
||||
switch (r->table_options.checksum) {
|
||||
case kNoChecksum:
|
||||
// we don't support no checksum yet
|
||||
assert(false);
|
||||
@@ -612,7 +611,7 @@ Status BlockBasedTableBuilder::InsertBlockInCache(const Slice& block_contents,
|
||||
const CompressionType type,
|
||||
const BlockHandle* handle) {
|
||||
Rep* r = rep_;
|
||||
Cache* block_cache_compressed = r->options.block_cache_compressed.get();
|
||||
Cache* block_cache_compressed = r->table_options.block_cache_compressed.get();
|
||||
|
||||
if (type != kNoCompression && block_cache_compressed != nullptr) {
|
||||
|
||||
@@ -701,7 +700,7 @@ Status BlockBasedTableBuilder::Finish() {
|
||||
// Add mapping from "<filter_block_prefix>.Name" to location
|
||||
// of filter data.
|
||||
std::string key = BlockBasedTable::kFilterBlockPrefix;
|
||||
key.append(r->options.filter_policy->Name());
|
||||
key.append(r->table_options.filter_policy->Name());
|
||||
meta_index_builder.Add(key, filter_block_handle);
|
||||
}
|
||||
|
||||
@@ -709,8 +708,8 @@ Status BlockBasedTableBuilder::Finish() {
|
||||
{
|
||||
PropertyBlockBuilder property_block_builder;
|
||||
std::vector<std::string> failed_user_prop_collectors;
|
||||
r->props.filter_policy_name = r->options.filter_policy != nullptr ?
|
||||
r->options.filter_policy->Name() : "";
|
||||
r->props.filter_policy_name = r->table_options.filter_policy != nullptr ?
|
||||
r->table_options.filter_policy->Name() : "";
|
||||
r->props.index_size =
|
||||
r->index_builder->EstimatedSize() + kBlockTrailerSize;
|
||||
|
||||
@@ -750,12 +749,12 @@ Status BlockBasedTableBuilder::Finish() {
|
||||
// TODO(icanadi) at some point in the future, when we're absolutely sure
|
||||
// nobody will roll back to RocksDB 2.x versions, retire the legacy magic
|
||||
// number and always write new table files with new magic number
|
||||
bool legacy = (r->checksum_type == kCRC32c);
|
||||
bool legacy = (r->table_options.checksum == kCRC32c);
|
||||
Footer footer(legacy ? kLegacyBlockBasedTableMagicNumber
|
||||
: kBlockBasedTableMagicNumber);
|
||||
footer.set_metaindex_handle(metaindex_block_handle);
|
||||
footer.set_index_handle(index_block_handle);
|
||||
footer.set_checksum(r->checksum_type);
|
||||
footer.set_checksum(r->table_options.checksum);
|
||||
std::string footer_encoding;
|
||||
footer.EncodeTo(&footer_encoding);
|
||||
r->status = r->file->Append(footer_encoding);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rocksdb/flush_block_policy.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "table/block_based_table_builder.h"
|
||||
#include "table/block_based_table_reader.h"
|
||||
#include "port/port.h"
|
||||
@@ -28,6 +29,15 @@ BlockBasedTableFactory::BlockBasedTableFactory(
|
||||
table_options_.flush_block_policy_factory.reset(
|
||||
new FlushBlockBySizePolicyFactory());
|
||||
}
|
||||
if (table_options_.no_block_cache) {
|
||||
table_options_.block_cache.reset();
|
||||
} else if (table_options_.block_cache == nullptr) {
|
||||
table_options_.block_cache = NewLRUCache(8 << 20);
|
||||
}
|
||||
if (table_options_.block_size_deviation < 0 ||
|
||||
table_options_.block_size_deviation > 100) {
|
||||
table_options_.block_size_deviation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Status BlockBasedTableFactory::NewTableReader(
|
||||
@@ -43,12 +53,73 @@ Status BlockBasedTableFactory::NewTableReader(
|
||||
TableBuilder* BlockBasedTableFactory::NewTableBuilder(
|
||||
const Options& options, const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file, CompressionType compression_type) const {
|
||||
|
||||
auto table_builder = new BlockBasedTableBuilder(
|
||||
options, table_options_, internal_comparator, file, compression_type);
|
||||
|
||||
return table_builder;
|
||||
}
|
||||
|
||||
std::string BlockBasedTableFactory::GetPrintableTableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(20000);
|
||||
const int kBufferSize = 200;
|
||||
char buffer[kBufferSize];
|
||||
|
||||
snprintf(buffer, kBufferSize, " flush_block_policy_factory: %s (%p)\n",
|
||||
table_options_.flush_block_policy_factory->Name(),
|
||||
table_options_.flush_block_policy_factory.get());
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " cache_index_and_filter_blocks: %d\n",
|
||||
table_options_.cache_index_and_filter_blocks);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " index_type: %d\n",
|
||||
table_options_.index_type);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " hash_index_allow_collision: %d\n",
|
||||
table_options_.hash_index_allow_collision);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " checksum: %d\n",
|
||||
table_options_.checksum);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " no_block_cache: %d\n",
|
||||
table_options_.no_block_cache);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " block_cache: %p\n",
|
||||
table_options_.block_cache.get());
|
||||
ret.append(buffer);
|
||||
if (table_options_.block_cache) {
|
||||
snprintf(buffer, kBufferSize, " block_cache_size: %zd\n",
|
||||
table_options_.block_cache->GetCapacity());
|
||||
ret.append(buffer);
|
||||
}
|
||||
snprintf(buffer, kBufferSize, " block_cache_compressed: %p\n",
|
||||
table_options_.block_cache_compressed.get());
|
||||
ret.append(buffer);
|
||||
if (table_options_.block_cache_compressed) {
|
||||
snprintf(buffer, kBufferSize, " block_cache_compressed_size: %zd\n",
|
||||
table_options_.block_cache_compressed->GetCapacity());
|
||||
ret.append(buffer);
|
||||
}
|
||||
snprintf(buffer, kBufferSize, " block_size: %zd\n",
|
||||
table_options_.block_size);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " block_size_deviation: %d\n",
|
||||
table_options_.block_size_deviation);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " block_restart_interval: %d\n",
|
||||
table_options_.block_restart_interval);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " filter_policy: %s\n",
|
||||
table_options_.filter_policy == nullptr ?
|
||||
"nullptr" : table_options_.filter_policy->Name());
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " whole_key_filtering: %d\n",
|
||||
table_options_.whole_key_filtering);
|
||||
ret.append(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
TableFactory* NewBlockBasedTableFactory(
|
||||
const BlockBasedTableOptions& table_options) {
|
||||
return new BlockBasedTableFactory(table_options);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "rocksdb/flush_block_policy.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "db/dbformat.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -43,6 +44,13 @@ class BlockBasedTableFactory : public TableFactory {
|
||||
const Options& options, const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file, CompressionType compression_type) const override;
|
||||
|
||||
// Sanitizes the specified DB Options.
|
||||
Status SanitizeDBOptions(const DBOptions* db_opts) const override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::string GetPrintableTableOptions() const override;
|
||||
|
||||
private:
|
||||
BlockBasedTableOptions table_options_;
|
||||
};
|
||||
|
||||
@@ -137,7 +137,8 @@ class BlockBasedTable::IndexReader {
|
||||
// Create an iterator for index access.
|
||||
// An iter is passed in, if it is not null, update this one and return it
|
||||
// If it is null, create a new Iterator
|
||||
virtual Iterator* NewIterator(BlockIter* iter = nullptr) = 0;
|
||||
virtual Iterator* NewIterator(
|
||||
BlockIter* iter = nullptr, bool total_order_seek = true) = 0;
|
||||
|
||||
// The size of the index.
|
||||
virtual size_t size() const = 0;
|
||||
@@ -174,8 +175,9 @@ class BinarySearchIndexReader : public IndexReader {
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual Iterator* NewIterator(BlockIter* iter = nullptr) override {
|
||||
return index_block_->NewIterator(comparator_, iter);
|
||||
virtual Iterator* NewIterator(
|
||||
BlockIter* iter = nullptr, bool dont_care = true) override {
|
||||
return index_block_->NewIterator(comparator_, iter, true);
|
||||
}
|
||||
|
||||
virtual size_t size() const override { return index_block_->size(); }
|
||||
@@ -295,8 +297,9 @@ class HashIndexReader : public IndexReader {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Iterator* NewIterator(BlockIter* iter = nullptr) override {
|
||||
return index_block_->NewIterator(comparator_, iter);
|
||||
virtual Iterator* NewIterator(
|
||||
BlockIter* iter = nullptr, bool total_order_seek = true) override {
|
||||
return index_block_->NewIterator(comparator_, iter, total_order_seek);
|
||||
}
|
||||
|
||||
virtual size_t size() const override { return index_block_->size(); }
|
||||
@@ -334,11 +337,16 @@ class HashIndexReader : public IndexReader {
|
||||
|
||||
struct BlockBasedTable::Rep {
|
||||
Rep(const EnvOptions& storage_options,
|
||||
const BlockBasedTableOptions& table_opt,
|
||||
const InternalKeyComparator& internal_comparator)
|
||||
: soptions(storage_options), internal_comparator(internal_comparator) {}
|
||||
: soptions(storage_options), table_options(table_opt),
|
||||
filter_policy(table_opt.filter_policy.get()),
|
||||
internal_comparator(internal_comparator) {}
|
||||
|
||||
Options options;
|
||||
const EnvOptions& soptions;
|
||||
const BlockBasedTableOptions& table_options;
|
||||
const FilterPolicy* const filter_policy;
|
||||
const InternalKeyComparator& internal_comparator;
|
||||
Status status;
|
||||
unique_ptr<RandomAccessFile> file;
|
||||
@@ -398,13 +406,13 @@ void BlockBasedTable::SetupCacheKeyPrefix(Rep* rep) {
|
||||
assert(kMaxCacheKeyPrefixSize >= 10);
|
||||
rep->cache_key_prefix_size = 0;
|
||||
rep->compressed_cache_key_prefix_size = 0;
|
||||
if (rep->options.block_cache != nullptr) {
|
||||
GenerateCachePrefix(rep->options.block_cache.get(), rep->file.get(),
|
||||
if (rep->table_options.block_cache != nullptr) {
|
||||
GenerateCachePrefix(rep->table_options.block_cache.get(), rep->file.get(),
|
||||
&rep->cache_key_prefix[0],
|
||||
&rep->cache_key_prefix_size);
|
||||
}
|
||||
if (rep->options.block_cache_compressed != nullptr) {
|
||||
GenerateCachePrefix(rep->options.block_cache_compressed.get(),
|
||||
if (rep->table_options.block_cache_compressed != nullptr) {
|
||||
GenerateCachePrefix(rep->table_options.block_cache_compressed.get(),
|
||||
rep->file.get(), &rep->compressed_cache_key_prefix[0],
|
||||
&rep->compressed_cache_key_prefix_size);
|
||||
}
|
||||
@@ -452,7 +460,8 @@ Status BlockBasedTable::Open(const Options& options, const EnvOptions& soptions,
|
||||
|
||||
// We've successfully read the footer and the index block: we're
|
||||
// ready to serve requests.
|
||||
Rep* rep = new BlockBasedTable::Rep(soptions, internal_comparator);
|
||||
Rep* rep = new BlockBasedTable::Rep(
|
||||
soptions, table_options, internal_comparator);
|
||||
rep->options = options;
|
||||
rep->file = std::move(file);
|
||||
rep->footer = footer;
|
||||
@@ -493,7 +502,8 @@ Status BlockBasedTable::Open(const Options& options, const EnvOptions& soptions,
|
||||
}
|
||||
|
||||
// Will use block cache for index/filter blocks access?
|
||||
if (options.block_cache && table_options.cache_index_and_filter_blocks) {
|
||||
if (table_options.block_cache &&
|
||||
table_options.cache_index_and_filter_blocks) {
|
||||
// Hack: Call NewIndexIterator() to implicitly add index to the block_cache
|
||||
unique_ptr<Iterator> iter(new_table->NewIndexIterator(ReadOptions()));
|
||||
s = iter->status();
|
||||
@@ -501,7 +511,7 @@ Status BlockBasedTable::Open(const Options& options, const EnvOptions& soptions,
|
||||
if (s.ok()) {
|
||||
// Hack: Call GetFilter() to implicitly add filter to the block_cache
|
||||
auto filter_entry = new_table->GetFilter();
|
||||
filter_entry.Release(options.block_cache.get());
|
||||
filter_entry.Release(table_options.block_cache.get());
|
||||
}
|
||||
} else {
|
||||
// If we don't use block cache for index/filter blocks access, we'll
|
||||
@@ -515,9 +525,9 @@ Status BlockBasedTable::Open(const Options& options, const EnvOptions& soptions,
|
||||
rep->index_reader.reset(index_reader);
|
||||
|
||||
// Set filter block
|
||||
if (rep->options.filter_policy) {
|
||||
if (rep->filter_policy) {
|
||||
std::string key = kFilterBlockPrefix;
|
||||
key.append(rep->options.filter_policy->Name());
|
||||
key.append(rep->filter_policy->Name());
|
||||
BlockHandle handle;
|
||||
if (FindMetaBlock(meta_iter.get(), key, &handle).ok()) {
|
||||
rep->filter.reset(ReadFilter(handle, rep));
|
||||
@@ -745,7 +755,7 @@ FilterBlockReader* BlockBasedTable::ReadFilter(const BlockHandle& filter_handle,
|
||||
}
|
||||
|
||||
return new FilterBlockReader(
|
||||
rep->options, block.data, block.heap_allocated);
|
||||
rep->options, rep->table_options, block.data, block.heap_allocated);
|
||||
}
|
||||
|
||||
BlockBasedTable::CachableEntry<FilterBlockReader> BlockBasedTable::GetFilter(
|
||||
@@ -755,13 +765,13 @@ BlockBasedTable::CachableEntry<FilterBlockReader> BlockBasedTable::GetFilter(
|
||||
return {rep_->filter.get(), nullptr /* cache handle */};
|
||||
}
|
||||
|
||||
if (rep_->options.filter_policy == nullptr /* do not use filter at all */ ||
|
||||
rep_->options.block_cache == nullptr /* no block cache at all */) {
|
||||
Cache* block_cache = rep_->table_options.block_cache.get();
|
||||
if (rep_->filter_policy == nullptr /* do not use filter */ ||
|
||||
block_cache == nullptr /* no block cache at all */) {
|
||||
return {nullptr /* filter */, nullptr /* cache handle */};
|
||||
}
|
||||
|
||||
// Fetching from the cache
|
||||
Cache* block_cache = rep_->options.block_cache.get();
|
||||
char cache_key[kMaxCacheKeyPrefixSize + kMaxVarint64Length];
|
||||
auto key = GetCacheKey(
|
||||
rep_->cache_key_prefix,
|
||||
@@ -790,7 +800,7 @@ BlockBasedTable::CachableEntry<FilterBlockReader> BlockBasedTable::GetFilter(
|
||||
|
||||
if (s.ok()) {
|
||||
std::string filter_block_key = kFilterBlockPrefix;
|
||||
filter_block_key.append(rep_->options.filter_policy->Name());
|
||||
filter_block_key.append(rep_->filter_policy->Name());
|
||||
BlockHandle handle;
|
||||
if (FindMetaBlock(iter.get(), filter_block_key, &handle).ok()) {
|
||||
filter = ReadFilter(handle, rep_, &filter_size);
|
||||
@@ -811,11 +821,12 @@ Iterator* BlockBasedTable::NewIndexIterator(const ReadOptions& read_options,
|
||||
BlockIter* input_iter) {
|
||||
// index reader has already been pre-populated.
|
||||
if (rep_->index_reader) {
|
||||
return rep_->index_reader->NewIterator(input_iter);
|
||||
return rep_->index_reader->NewIterator(
|
||||
input_iter, read_options.total_order_seek);
|
||||
}
|
||||
|
||||
bool no_io = read_options.read_tier == kBlockCacheTier;
|
||||
Cache* block_cache = rep_->options.block_cache.get();
|
||||
Cache* block_cache = rep_->table_options.block_cache.get();
|
||||
char cache_key[kMaxCacheKeyPrefixSize + kMaxVarint64Length];
|
||||
auto key = GetCacheKey(rep_->cache_key_prefix, rep_->cache_key_prefix_size,
|
||||
rep_->footer.index_handle(), cache_key);
|
||||
@@ -859,10 +870,9 @@ Iterator* BlockBasedTable::NewIndexIterator(const ReadOptions& read_options,
|
||||
}
|
||||
|
||||
assert(cache_handle);
|
||||
Iterator* iter;
|
||||
iter = index_reader->NewIterator(input_iter);
|
||||
auto* iter = index_reader->NewIterator(
|
||||
input_iter, read_options.total_order_seek);
|
||||
iter->RegisterCleanup(&ReleaseCachedEntry, block_cache, cache_handle);
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
@@ -874,9 +884,9 @@ Iterator* BlockBasedTable::NewDataBlockIterator(Rep* rep,
|
||||
const ReadOptions& ro, const Slice& index_value,
|
||||
BlockIter* input_iter) {
|
||||
const bool no_io = (ro.read_tier == kBlockCacheTier);
|
||||
Cache* block_cache = rep->options.block_cache.get();
|
||||
Cache* block_cache_compressed = rep->options.
|
||||
block_cache_compressed.get();
|
||||
Cache* block_cache = rep->table_options.block_cache.get();
|
||||
Cache* block_cache_compressed =
|
||||
rep->table_options.block_cache_compressed.get();
|
||||
CachableEntry<Block> block;
|
||||
|
||||
BlockHandle handle;
|
||||
@@ -981,6 +991,9 @@ class BlockBasedTable::BlockEntryIteratorState : public TwoLevelIteratorState {
|
||||
}
|
||||
|
||||
bool PrefixMayMatch(const Slice& internal_key) override {
|
||||
if (read_options_.total_order_seek) {
|
||||
return true;
|
||||
}
|
||||
return table_->PrefixMayMatch(internal_key);
|
||||
}
|
||||
|
||||
@@ -992,8 +1005,8 @@ class BlockBasedTable::BlockEntryIteratorState : public TwoLevelIteratorState {
|
||||
|
||||
// This will be broken if the user specifies an unusual implementation
|
||||
// of Options.comparator, or if the user specifies an unusual
|
||||
// definition of prefixes in Options.filter_policy. In particular, we
|
||||
// require the following three properties:
|
||||
// definition of prefixes in BlockBasedTableOptions.filter_policy.
|
||||
// In particular, we require the following three properties:
|
||||
//
|
||||
// 1) key.starts_with(prefix(key))
|
||||
// 2) Compare(prefix(key), key) <= 0.
|
||||
@@ -1003,7 +1016,7 @@ class BlockBasedTable::BlockEntryIteratorState : public TwoLevelIteratorState {
|
||||
//
|
||||
// REQUIRES: this method shouldn't be called while the DB lock is held.
|
||||
bool BlockBasedTable::PrefixMayMatch(const Slice& internal_key) {
|
||||
if (!rep_->options.filter_policy) {
|
||||
if (!rep_->filter_policy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1054,10 +1067,9 @@ bool BlockBasedTable::PrefixMayMatch(const Slice& internal_key) {
|
||||
s = handle.DecodeFrom(&handle_value);
|
||||
assert(s.ok());
|
||||
auto filter_entry = GetFilter(true /* no io */);
|
||||
may_match =
|
||||
filter_entry.value == nullptr ||
|
||||
filter_entry.value->PrefixMayMatch(handle.offset(), internal_prefix);
|
||||
filter_entry.Release(rep_->options.block_cache.get());
|
||||
may_match = filter_entry.value == nullptr ||
|
||||
filter_entry.value->PrefixMayMatch(handle.offset(), prefix);
|
||||
filter_entry.Release(rep_->table_options.block_cache.get());
|
||||
}
|
||||
|
||||
Statistics* statistics = rep_->options.statistics.get();
|
||||
@@ -1092,9 +1104,8 @@ Status BlockBasedTable::Get(
|
||||
|
||||
BlockHandle handle;
|
||||
bool may_not_exist_in_filter =
|
||||
filter != nullptr &&
|
||||
handle.DecodeFrom(&handle_value).ok() &&
|
||||
!filter->KeyMayMatch(handle.offset(), key);
|
||||
filter != nullptr && handle.DecodeFrom(&handle_value).ok() &&
|
||||
!filter->KeyMayMatch(handle.offset(), ExtractUserKey(key));
|
||||
|
||||
if (may_not_exist_in_filter) {
|
||||
// Not found
|
||||
@@ -1135,7 +1146,7 @@ Status BlockBasedTable::Get(
|
||||
}
|
||||
}
|
||||
|
||||
filter_entry.Release(rep_->options.block_cache.get());
|
||||
filter_entry.Release(rep_->table_options.block_cache.get());
|
||||
if (s.ok()) {
|
||||
s = iiter.status();
|
||||
}
|
||||
@@ -1154,7 +1165,7 @@ bool BlockBasedTable::TEST_KeyInCache(const ReadOptions& options,
|
||||
Slice input = iiter->value();
|
||||
Status s = handle.DecodeFrom(&input);
|
||||
assert(s.ok());
|
||||
Cache* block_cache = rep_->options.block_cache.get();
|
||||
Cache* block_cache = rep_->table_options.block_cache.get();
|
||||
assert(block_cache != nullptr);
|
||||
|
||||
char cache_key_storage[kMaxCacheKeyPrefixSize + kMaxVarint64Length];
|
||||
|
||||
@@ -41,10 +41,8 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
BlockBuilder::BlockBuilder(int block_restart_interval,
|
||||
const Comparator* comparator)
|
||||
BlockBuilder::BlockBuilder(int block_restart_interval)
|
||||
: block_restart_interval_(block_restart_interval),
|
||||
comparator_(comparator),
|
||||
restarts_(),
|
||||
counter_(0),
|
||||
finished_(false) {
|
||||
@@ -52,9 +50,6 @@ BlockBuilder::BlockBuilder(int block_restart_interval,
|
||||
restarts_.push_back(0); // First restart point is at offset 0
|
||||
}
|
||||
|
||||
BlockBuilder::BlockBuilder(const Options& options, const Comparator* comparator)
|
||||
: BlockBuilder(options.block_restart_interval, comparator) {}
|
||||
|
||||
void BlockBuilder::Reset() {
|
||||
buffer_.clear();
|
||||
restarts_.clear();
|
||||
@@ -99,8 +94,6 @@ void BlockBuilder::Add(const Slice& key, const Slice& value) {
|
||||
Slice last_key_piece(last_key_);
|
||||
assert(!finished_);
|
||||
assert(counter_ <= block_restart_interval_);
|
||||
assert(buffer_.empty() // No values yet?
|
||||
|| comparator_->Compare(key, last_key_piece) > 0);
|
||||
size_t shared = 0;
|
||||
if (counter_ < block_restart_interval_) {
|
||||
// See how much sharing to do with previous string
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
struct Options;
|
||||
class Comparator;
|
||||
|
||||
class BlockBuilder {
|
||||
public:
|
||||
BlockBuilder(int block_builder, const Comparator* comparator);
|
||||
explicit BlockBuilder(const Options& options, const Comparator* comparator);
|
||||
|
||||
BlockBuilder(const BlockBuilder&) = delete;
|
||||
void operator=(const BlockBuilder&) = delete;
|
||||
|
||||
explicit BlockBuilder(int block_restart_interval);
|
||||
|
||||
// Reset the contents as if the BlockBuilder was just constructed.
|
||||
void Reset();
|
||||
|
||||
@@ -49,17 +50,12 @@ class BlockBuilder {
|
||||
|
||||
private:
|
||||
const int block_restart_interval_;
|
||||
const Comparator* comparator_;
|
||||
|
||||
std::string buffer_; // Destination buffer
|
||||
std::vector<uint32_t> restarts_; // Restart points
|
||||
int counter_; // Number of entries emitted since restart
|
||||
bool finished_; // Has Finish() been called?
|
||||
std::string last_key_;
|
||||
|
||||
// No copying allowed
|
||||
BlockBuilder(const BlockBuilder&);
|
||||
void operator=(const BlockBuilder&);
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+3
-4
@@ -76,7 +76,7 @@ TEST(BlockTest, SimpleTest) {
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
BlockBuilder builder(options, ic.get());
|
||||
BlockBuilder builder(16);
|
||||
int num_records = 100000;
|
||||
|
||||
GenerateRandomKVs(&keys, &values, 0, num_records);
|
||||
@@ -132,8 +132,7 @@ BlockContents GetBlockContents(std::unique_ptr<BlockBuilder> *builder,
|
||||
const std::vector<std::string> &keys,
|
||||
const std::vector<std::string> &values,
|
||||
const int prefix_group_size = 1) {
|
||||
builder->reset(
|
||||
new BlockBuilder(1 /* restart interval */, BytewiseComparator()));
|
||||
builder->reset(new BlockBuilder(1 /* restart interval */));
|
||||
|
||||
// Add only half of the keys
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
@@ -172,7 +171,7 @@ void CheckBlockContents(BlockContents contents, const int max_key,
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> hash_iter(
|
||||
reader1.NewIterator(BytewiseComparator()));
|
||||
reader1.NewIterator(BytewiseComparator(), nullptr, false));
|
||||
|
||||
std::unique_ptr<Iterator> regular_iter(
|
||||
reader2.NewIterator(BytewiseComparator()));
|
||||
|
||||
+150
-103
@@ -16,6 +16,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block_builder.h"
|
||||
#include "table/cuckoo_table_factory.h"
|
||||
#include "table/format.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "util/autovector.h"
|
||||
@@ -24,29 +25,35 @@
|
||||
namespace rocksdb {
|
||||
const std::string CuckooTablePropertyNames::kEmptyKey =
|
||||
"rocksdb.cuckoo.bucket.empty.key";
|
||||
const std::string CuckooTablePropertyNames::kNumHashTable =
|
||||
const std::string CuckooTablePropertyNames::kNumHashFunc =
|
||||
"rocksdb.cuckoo.hash.num";
|
||||
const std::string CuckooTablePropertyNames::kMaxNumBuckets =
|
||||
"rocksdb.cuckoo.bucket.maxnum";
|
||||
const std::string CuckooTablePropertyNames::kHashTableSize =
|
||||
"rocksdb.cuckoo.hash.size";
|
||||
const std::string CuckooTablePropertyNames::kValueLength =
|
||||
"rocksdb.cuckoo.value.length";
|
||||
const std::string CuckooTablePropertyNames::kIsLastLevel =
|
||||
"rocksdb.cuckoo.file.islastlevel";
|
||||
const std::string CuckooTablePropertyNames::kCuckooBlockSize =
|
||||
"rocksdb.cuckoo.hash.cuckooblocksize";
|
||||
|
||||
// Obtained by running echo rocksdb.table.cuckoo | sha1sum
|
||||
extern const uint64_t kCuckooTableMagicNumber = 0x926789d0c5f17873ull;
|
||||
|
||||
CuckooTableBuilder::CuckooTableBuilder(
|
||||
WritableFile* file, double hash_table_ratio,
|
||||
WritableFile* file, double max_hash_table_ratio,
|
||||
uint32_t max_num_hash_table, uint32_t max_search_depth,
|
||||
const Comparator* user_comparator, uint32_t cuckoo_block_size,
|
||||
uint64_t (*get_slice_hash)(const Slice&, uint32_t, uint64_t))
|
||||
: num_hash_table_(2),
|
||||
: num_hash_func_(2),
|
||||
file_(file),
|
||||
hash_table_ratio_(hash_table_ratio),
|
||||
max_num_hash_table_(max_num_hash_table),
|
||||
max_hash_table_ratio_(max_hash_table_ratio),
|
||||
max_num_hash_func_(max_num_hash_table),
|
||||
max_search_depth_(max_search_depth),
|
||||
cuckoo_block_size_(std::max(1U, cuckoo_block_size)),
|
||||
hash_table_size_(2),
|
||||
is_last_level_file_(false),
|
||||
has_seen_first_key_(false),
|
||||
ucomp_(user_comparator),
|
||||
get_slice_hash_(get_slice_hash),
|
||||
closed_(false) {
|
||||
properties_.num_entries = 0;
|
||||
@@ -73,6 +80,8 @@ void CuckooTableBuilder::Add(const Slice& key, const Slice& value) {
|
||||
if (!has_seen_first_key_) {
|
||||
is_last_level_file_ = ikey.sequence == 0;
|
||||
has_seen_first_key_ = true;
|
||||
smallest_user_key_.assign(ikey.user_key.data(), ikey.user_key.size());
|
||||
largest_user_key_.assign(ikey.user_key.data(), ikey.user_key.size());
|
||||
}
|
||||
// Even if one sequence number is non-zero, then it is not last level.
|
||||
assert(!is_last_level_file_ || ikey.sequence == 0);
|
||||
@@ -83,28 +92,24 @@ void CuckooTableBuilder::Add(const Slice& key, const Slice& value) {
|
||||
kvs_.emplace_back(std::make_pair(key.ToString(), value.ToString()));
|
||||
}
|
||||
|
||||
properties_.num_entries++;
|
||||
|
||||
// We assume that the keys are inserted in sorted order as determined by
|
||||
// Byte-wise comparator. To identify an unused key, which will be used in
|
||||
// filling empty buckets in the table, we try to find gaps between successive
|
||||
// keys inserted (ie, latest key and previous in kvs_).
|
||||
if (unused_user_key_.empty() && kvs_.size() > 1) {
|
||||
std::string prev_key = is_last_level_file_ ? kvs_[kvs_.size()-1].first
|
||||
: ExtractUserKey(kvs_[kvs_.size()-1].first).ToString();
|
||||
std::string new_user_key = prev_key;
|
||||
new_user_key.back()++;
|
||||
// We ignore carry-overs and check that it is larger than previous key.
|
||||
if (Slice(new_user_key).compare(Slice(prev_key)) > 0 &&
|
||||
Slice(new_user_key).compare(ikey.user_key) < 0) {
|
||||
unused_user_key_ = new_user_key;
|
||||
}
|
||||
// In order to fill the empty buckets in the hash table, we identify a
|
||||
// key which is not used so far (unused_user_key). We determine this by
|
||||
// maintaining smallest and largest keys inserted so far in bytewise order
|
||||
// and use them to find a key outside this range in Finish() operation.
|
||||
// Note that this strategy is independent of user comparator used here.
|
||||
if (ikey.user_key.compare(smallest_user_key_) < 0) {
|
||||
smallest_user_key_.assign(ikey.user_key.data(), ikey.user_key.size());
|
||||
} else if (ikey.user_key.compare(largest_user_key_) > 0) {
|
||||
largest_user_key_.assign(ikey.user_key.data(), ikey.user_key.size());
|
||||
}
|
||||
if (hash_table_size_ < kvs_.size() / max_hash_table_ratio_) {
|
||||
hash_table_size_ *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
Status CuckooTableBuilder::MakeHashTable(std::vector<CuckooBucket>* buckets) {
|
||||
uint64_t num_buckets = kvs_.size() / hash_table_ratio_;
|
||||
buckets->resize(num_buckets);
|
||||
uint64_t hash_table_size_minus_one = hash_table_size_ - 1;
|
||||
buckets->resize(hash_table_size_minus_one + cuckoo_block_size_);
|
||||
uint64_t make_space_for_key_call_id = 0;
|
||||
for (uint32_t vector_idx = 0; vector_idx < kvs_.size(); vector_idx++) {
|
||||
uint64_t bucket_id;
|
||||
@@ -112,39 +117,50 @@ Status CuckooTableBuilder::MakeHashTable(std::vector<CuckooBucket>* buckets) {
|
||||
autovector<uint64_t> hash_vals;
|
||||
Slice user_key = is_last_level_file_ ? kvs_[vector_idx].first :
|
||||
ExtractUserKey(kvs_[vector_idx].first);
|
||||
for (uint32_t hash_cnt = 0; hash_cnt < num_hash_table_; ++hash_cnt) {
|
||||
uint64_t hash_val = get_slice_hash_(user_key, hash_cnt, num_buckets);
|
||||
if ((*buckets)[hash_val].vector_idx == kMaxVectorIdx) {
|
||||
bucket_id = hash_val;
|
||||
bucket_found = true;
|
||||
break;
|
||||
} else {
|
||||
if (user_key.compare(is_last_level_file_
|
||||
? Slice(kvs_[(*buckets)[hash_val].vector_idx].first)
|
||||
: ExtractUserKey(
|
||||
kvs_[(*buckets)[hash_val].vector_idx].first)) == 0) {
|
||||
return Status::NotSupported("Same key is being inserted again.");
|
||||
for (uint32_t hash_cnt = 0; hash_cnt < num_hash_func_ && !bucket_found;
|
||||
++hash_cnt) {
|
||||
uint64_t hash_val = CuckooHash(user_key, hash_cnt,
|
||||
hash_table_size_minus_one, get_slice_hash_);
|
||||
// If there is a collision, check next cuckoo_block_size_ locations for
|
||||
// empty locations. While checking, if we reach end of the hash table,
|
||||
// stop searching and proceed for next hash function.
|
||||
for (uint32_t block_idx = 0; block_idx < cuckoo_block_size_;
|
||||
++block_idx, ++hash_val) {
|
||||
if ((*buckets)[hash_val].vector_idx == kMaxVectorIdx) {
|
||||
bucket_id = hash_val;
|
||||
bucket_found = true;
|
||||
break;
|
||||
} else {
|
||||
if (ucomp_->Compare(user_key, is_last_level_file_
|
||||
? Slice(kvs_[(*buckets)[hash_val].vector_idx].first)
|
||||
: ExtractUserKey(
|
||||
kvs_[(*buckets)[hash_val].vector_idx].first)) == 0) {
|
||||
return Status::NotSupported("Same key is being inserted again.");
|
||||
}
|
||||
hash_vals.push_back(hash_val);
|
||||
}
|
||||
hash_vals.push_back(hash_val);
|
||||
}
|
||||
}
|
||||
while (!bucket_found && !MakeSpaceForKey(hash_vals,
|
||||
++make_space_for_key_call_id, buckets, &bucket_id)) {
|
||||
// Rehash by increashing number of hash tables.
|
||||
if (num_hash_table_ >= max_num_hash_table_) {
|
||||
return Status::NotSupported("Too many collissions. Unable to hash.");
|
||||
if (num_hash_func_ >= max_num_hash_func_) {
|
||||
return Status::NotSupported("Too many collisions. Unable to hash.");
|
||||
}
|
||||
// We don't really need to rehash the entire table because old hashes are
|
||||
// still valid and we only increased the number of hash functions.
|
||||
uint64_t hash_val = get_slice_hash_(user_key,
|
||||
num_hash_table_, num_buckets);
|
||||
++num_hash_table_;
|
||||
if ((*buckets)[hash_val].vector_idx == kMaxVectorIdx) {
|
||||
bucket_found = true;
|
||||
bucket_id = hash_val;
|
||||
break;
|
||||
} else {
|
||||
hash_vals.push_back(hash_val);
|
||||
uint64_t hash_val = CuckooHash(user_key, num_hash_func_,
|
||||
hash_table_size_minus_one, get_slice_hash_);
|
||||
++num_hash_func_;
|
||||
for (uint32_t block_idx = 0; block_idx < cuckoo_block_size_;
|
||||
++block_idx, ++hash_val) {
|
||||
if ((*buckets)[hash_val].vector_idx == kMaxVectorIdx) {
|
||||
bucket_found = true;
|
||||
bucket_id = hash_val;
|
||||
break;
|
||||
} else {
|
||||
hash_vals.push_back(hash_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
(*buckets)[bucket_id].vector_idx = vector_idx;
|
||||
@@ -156,38 +172,46 @@ Status CuckooTableBuilder::Finish() {
|
||||
assert(!closed_);
|
||||
closed_ = true;
|
||||
std::vector<CuckooBucket> buckets;
|
||||
Status s = MakeHashTable(&buckets);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (unused_user_key_.empty() && !kvs_.empty()) {
|
||||
// Try to find the key next to last key by handling carryovers.
|
||||
std::string last_key =
|
||||
is_last_level_file_ ? kvs_[kvs_.size()-1].first
|
||||
: ExtractUserKey(kvs_[kvs_.size()-1].first).ToString();
|
||||
std::string new_user_key = last_key;
|
||||
int curr_pos = new_user_key.size() - 1;
|
||||
Status s;
|
||||
std::string unused_bucket;
|
||||
if (!kvs_.empty()) {
|
||||
s = MakeHashTable(&buckets);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// Determine unused_user_key to fill empty buckets.
|
||||
std::string unused_user_key = smallest_user_key_;
|
||||
int curr_pos = unused_user_key.size() - 1;
|
||||
while (curr_pos >= 0) {
|
||||
++new_user_key[curr_pos];
|
||||
if (new_user_key > last_key) {
|
||||
unused_user_key_ = new_user_key;
|
||||
--unused_user_key[curr_pos];
|
||||
if (Slice(unused_user_key).compare(smallest_user_key_) < 0) {
|
||||
break;
|
||||
}
|
||||
--curr_pos;
|
||||
}
|
||||
if (curr_pos < 0) {
|
||||
// Try using the largest key to identify an unused key.
|
||||
unused_user_key = largest_user_key_;
|
||||
curr_pos = unused_user_key.size() - 1;
|
||||
while (curr_pos >= 0) {
|
||||
++unused_user_key[curr_pos];
|
||||
if (Slice(unused_user_key).compare(largest_user_key_) > 0) {
|
||||
break;
|
||||
}
|
||||
--curr_pos;
|
||||
}
|
||||
}
|
||||
if (curr_pos < 0) {
|
||||
return Status::Corruption("Unable to find unused key");
|
||||
}
|
||||
}
|
||||
std::string unused_bucket;
|
||||
if (!kvs_.empty()) {
|
||||
if (is_last_level_file_) {
|
||||
unused_bucket = unused_user_key_;
|
||||
unused_bucket = unused_user_key;
|
||||
} else {
|
||||
ParsedInternalKey ikey(unused_user_key_, 0, kTypeValue);
|
||||
ParsedInternalKey ikey(unused_user_key, 0, kTypeValue);
|
||||
AppendInternalKey(&unused_bucket, ikey);
|
||||
}
|
||||
}
|
||||
properties_.num_entries = kvs_.size();
|
||||
properties_.fixed_key_len = unused_bucket.size();
|
||||
uint32_t value_length = kvs_.empty() ? 0 : kvs_[0].second.size();
|
||||
uint32_t bucket_size = value_length + properties_.fixed_key_len;
|
||||
@@ -213,22 +237,31 @@ Status CuckooTableBuilder::Finish() {
|
||||
}
|
||||
}
|
||||
assert(num_added == NumEntries());
|
||||
properties_.raw_key_size = num_added * properties_.fixed_key_len;
|
||||
properties_.raw_value_size = num_added * value_length;
|
||||
|
||||
uint64_t offset = buckets.size() * bucket_size;
|
||||
properties_.data_size = offset;
|
||||
unused_bucket.resize(properties_.fixed_key_len);
|
||||
properties_.user_collected_properties[
|
||||
CuckooTablePropertyNames::kEmptyKey] = unused_bucket;
|
||||
properties_.user_collected_properties[
|
||||
CuckooTablePropertyNames::kNumHashTable].assign(
|
||||
reinterpret_cast<char*>(&num_hash_table_), sizeof(num_hash_table_));
|
||||
uint64_t num_buckets = buckets.size();
|
||||
CuckooTablePropertyNames::kNumHashFunc].assign(
|
||||
reinterpret_cast<char*>(&num_hash_func_), sizeof(num_hash_func_));
|
||||
|
||||
uint64_t hash_table_size = buckets.size() - cuckoo_block_size_ + 1;
|
||||
properties_.user_collected_properties[
|
||||
CuckooTablePropertyNames::kMaxNumBuckets].assign(
|
||||
reinterpret_cast<const char*>(&num_buckets), sizeof(num_buckets));
|
||||
CuckooTablePropertyNames::kHashTableSize].assign(
|
||||
reinterpret_cast<const char*>(&hash_table_size),
|
||||
sizeof(hash_table_size));
|
||||
properties_.user_collected_properties[
|
||||
CuckooTablePropertyNames::kIsLastLevel].assign(
|
||||
reinterpret_cast<const char*>(&is_last_level_file_),
|
||||
sizeof(is_last_level_file_));
|
||||
properties_.user_collected_properties[
|
||||
CuckooTablePropertyNames::kCuckooBlockSize].assign(
|
||||
reinterpret_cast<const char*>(&cuckoo_block_size_),
|
||||
sizeof(cuckoo_block_size_));
|
||||
|
||||
// Write meta blocks.
|
||||
MetaIndexBuilder meta_index_builder;
|
||||
@@ -272,7 +305,7 @@ void CuckooTableBuilder::Abandon() {
|
||||
}
|
||||
|
||||
uint64_t CuckooTableBuilder::NumEntries() const {
|
||||
return properties_.num_entries;
|
||||
return kvs_.size();
|
||||
}
|
||||
|
||||
uint64_t CuckooTableBuilder::FileSize() const {
|
||||
@@ -281,11 +314,17 @@ uint64_t CuckooTableBuilder::FileSize() const {
|
||||
} else if (properties_.num_entries == 0) {
|
||||
return 0;
|
||||
}
|
||||
// This is not the actual size of the file as we need to account for
|
||||
// hash table ratio. This returns the size of filled buckets in the table
|
||||
// scaled up by a factor of 1/hash_table_ratio.
|
||||
return ((kvs_[0].first.size() + kvs_[0].second.size()) *
|
||||
properties_.num_entries) / hash_table_ratio_;
|
||||
|
||||
// Account for buckets being a power of two.
|
||||
// As elements are added, file size remains constant for a while and doubles
|
||||
// its size. Since compaction algorithm stops adding elements only after it
|
||||
// exceeds the file limit, we account for the extra element being added here.
|
||||
uint64_t expected_hash_table_size = hash_table_size_;
|
||||
if (expected_hash_table_size < (kvs_.size() + 1) / max_hash_table_ratio_) {
|
||||
expected_hash_table_size *= 2;
|
||||
}
|
||||
return (kvs_[0].first.size() + kvs_[0].second.size()) *
|
||||
expected_hash_table_size;
|
||||
}
|
||||
|
||||
// This method is invoked when there is no place to insert the target key.
|
||||
@@ -315,41 +354,49 @@ bool CuckooTableBuilder::MakeSpaceForKey(
|
||||
std::vector<CuckooNode> tree;
|
||||
// We want to identify already visited buckets in the current method call so
|
||||
// that we don't add same buckets again for exploration in the tree.
|
||||
// We do this by maintaining a count of current method call, which acts as a
|
||||
// unique id for this invocation of the method. We store this number into
|
||||
// the nodes that we explore in current method call.
|
||||
// We do this by maintaining a count of current method call in
|
||||
// make_space_for_key_call_id, which acts as a unique id for this invocation
|
||||
// of the method. We store this number into the nodes that we explore in
|
||||
// current method call.
|
||||
// It is unlikely for the increment operation to overflow because the maximum
|
||||
// no. of times this will be called is <= max_num_hash_table_ + kvs_.size().
|
||||
for (uint32_t hash_cnt = 0; hash_cnt < num_hash_table_; ++hash_cnt) {
|
||||
// no. of times this will be called is <= max_num_hash_func_ + kvs_.size().
|
||||
for (uint32_t hash_cnt = 0; hash_cnt < num_hash_func_; ++hash_cnt) {
|
||||
uint64_t bucket_id = hash_vals[hash_cnt];
|
||||
(*buckets)[bucket_id].make_space_for_key_call_id =
|
||||
make_space_for_key_call_id;
|
||||
tree.push_back(CuckooNode(bucket_id, 0, 0));
|
||||
}
|
||||
uint64_t hash_table_size_minus_one = hash_table_size_ - 1;
|
||||
bool null_found = false;
|
||||
uint32_t curr_pos = 0;
|
||||
while (!null_found && curr_pos < tree.size()) {
|
||||
CuckooNode& curr_node = tree[curr_pos];
|
||||
if (curr_node.depth >= max_search_depth_) {
|
||||
uint32_t curr_depth = curr_node.depth;
|
||||
if (curr_depth >= max_search_depth_) {
|
||||
break;
|
||||
}
|
||||
CuckooBucket& curr_bucket = (*buckets)[curr_node.bucket_id];
|
||||
for (uint32_t hash_cnt = 0; hash_cnt < num_hash_table_; ++hash_cnt) {
|
||||
uint64_t child_bucket_id = get_slice_hash_(
|
||||
is_last_level_file_ ? kvs_[curr_bucket.vector_idx].first
|
||||
: ExtractUserKey(Slice(kvs_[curr_bucket.vector_idx].first)),
|
||||
hash_cnt, buckets->size());
|
||||
if ((*buckets)[child_bucket_id].make_space_for_key_call_id ==
|
||||
make_space_for_key_call_id) {
|
||||
continue;
|
||||
}
|
||||
(*buckets)[child_bucket_id].make_space_for_key_call_id =
|
||||
make_space_for_key_call_id;
|
||||
tree.push_back(CuckooNode(child_bucket_id, curr_node.depth + 1,
|
||||
curr_pos));
|
||||
if ((*buckets)[child_bucket_id].vector_idx == kMaxVectorIdx) {
|
||||
null_found = true;
|
||||
break;
|
||||
for (uint32_t hash_cnt = 0;
|
||||
hash_cnt < num_hash_func_ && !null_found; ++hash_cnt) {
|
||||
uint64_t child_bucket_id = CuckooHash(
|
||||
(is_last_level_file_ ? kvs_[curr_bucket.vector_idx].first :
|
||||
ExtractUserKey(Slice(kvs_[curr_bucket.vector_idx].first))),
|
||||
hash_cnt, hash_table_size_minus_one, get_slice_hash_);
|
||||
// Iterate inside Cuckoo Block.
|
||||
for (uint32_t block_idx = 0; block_idx < cuckoo_block_size_;
|
||||
++block_idx, ++child_bucket_id) {
|
||||
if ((*buckets)[child_bucket_id].make_space_for_key_call_id ==
|
||||
make_space_for_key_call_id) {
|
||||
continue;
|
||||
}
|
||||
(*buckets)[child_bucket_id].make_space_for_key_call_id =
|
||||
make_space_for_key_call_id;
|
||||
tree.push_back(CuckooNode(child_bucket_id, curr_depth + 1,
|
||||
curr_pos));
|
||||
if ((*buckets)[child_bucket_id].vector_idx == kMaxVectorIdx) {
|
||||
null_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
++curr_pos;
|
||||
@@ -359,10 +406,10 @@ bool CuckooTableBuilder::MakeSpaceForKey(
|
||||
// There is an empty node in tree.back(). Now, traverse the path from this
|
||||
// empty node to top of the tree and at every node in the path, replace
|
||||
// child with the parent. Stop when first level is reached in the tree
|
||||
// (happens when 0 <= bucket_to_replace_pos < num_hash_table_) and return
|
||||
// (happens when 0 <= bucket_to_replace_pos < num_hash_func_) and return
|
||||
// this location in first level for target key to be inserted.
|
||||
uint32_t bucket_to_replace_pos = tree.size()-1;
|
||||
while (bucket_to_replace_pos >= num_hash_table_) {
|
||||
while (bucket_to_replace_pos >= num_hash_func_) {
|
||||
CuckooNode& curr_node = tree[bucket_to_replace_pos];
|
||||
(*buckets)[curr_node.bucket_id] =
|
||||
(*buckets)[tree[curr_node.parent_pos].bucket_id];
|
||||
|
||||
@@ -21,8 +21,9 @@ namespace rocksdb {
|
||||
class CuckooTableBuilder: public TableBuilder {
|
||||
public:
|
||||
CuckooTableBuilder(
|
||||
WritableFile* file, double hash_table_ratio, uint32_t max_num_hash_table,
|
||||
uint32_t max_search_depth,
|
||||
WritableFile* file, double max_hash_table_ratio,
|
||||
uint32_t max_num_hash_func, uint32_t max_search_depth,
|
||||
const Comparator* user_comparator, uint32_t cuckoo_block_size,
|
||||
uint64_t (*get_slice_hash)(const Slice&, uint32_t, uint64_t));
|
||||
|
||||
// REQUIRES: Either Finish() or Abandon() has been called.
|
||||
@@ -60,7 +61,7 @@ class CuckooTableBuilder: public TableBuilder {
|
||||
CuckooBucket()
|
||||
: vector_idx(kMaxVectorIdx), make_space_for_key_call_id(0) {}
|
||||
uint32_t vector_idx;
|
||||
// This number will not exceed kvs_.size() + max_num_hash_table_.
|
||||
// This number will not exceed kvs_.size() + max_num_hash_func_.
|
||||
// We assume number of items is <= 2^32.
|
||||
uint32_t make_space_for_key_call_id;
|
||||
};
|
||||
@@ -73,19 +74,23 @@ class CuckooTableBuilder: public TableBuilder {
|
||||
uint64_t* bucket_id);
|
||||
Status MakeHashTable(std::vector<CuckooBucket>* buckets);
|
||||
|
||||
uint32_t num_hash_table_;
|
||||
uint32_t num_hash_func_;
|
||||
WritableFile* file_;
|
||||
const double hash_table_ratio_;
|
||||
const uint32_t max_num_hash_table_;
|
||||
const double max_hash_table_ratio_;
|
||||
const uint32_t max_num_hash_func_;
|
||||
const uint32_t max_search_depth_;
|
||||
const uint32_t cuckoo_block_size_;
|
||||
uint64_t hash_table_size_;
|
||||
bool is_last_level_file_;
|
||||
Status status_;
|
||||
std::vector<std::pair<std::string, std::string>> kvs_;
|
||||
TableProperties properties_;
|
||||
bool has_seen_first_key_;
|
||||
const Comparator* ucomp_;
|
||||
uint64_t (*get_slice_hash_)(const Slice& s, uint32_t index,
|
||||
uint64_t max_num_buckets);
|
||||
std::string unused_user_key_ = "";
|
||||
std::string largest_user_key_ = "";
|
||||
std::string smallest_user_key_ = "";
|
||||
|
||||
bool closed_; // Either Finish() or Abandon() has been called.
|
||||
|
||||
|
||||
@@ -37,8 +37,9 @@ class CuckooBuilderTest {
|
||||
void CheckFileContents(const std::vector<std::string>& keys,
|
||||
const std::vector<std::string>& values,
|
||||
const std::vector<uint64_t>& expected_locations,
|
||||
std::string expected_unused_bucket, uint64_t expected_max_buckets,
|
||||
uint32_t expected_num_hash_fun, bool expected_is_last_level) {
|
||||
std::string expected_unused_bucket, uint64_t expected_table_size,
|
||||
uint32_t expected_num_hash_func, bool expected_is_last_level,
|
||||
uint32_t expected_cuckoo_block_size = 1) {
|
||||
// Read file
|
||||
unique_ptr<RandomAccessFile> read_file;
|
||||
ASSERT_OK(env_->NewRandomAccessFile(fname, &read_file, env_options_));
|
||||
@@ -51,6 +52,9 @@ class CuckooBuilderTest {
|
||||
kCuckooTableMagicNumber, env_, nullptr, &props));
|
||||
ASSERT_EQ(props->num_entries, keys.size());
|
||||
ASSERT_EQ(props->fixed_key_len, keys.empty() ? 0 : keys[0].size());
|
||||
ASSERT_EQ(props->data_size, expected_unused_bucket.size() *
|
||||
(expected_table_size + expected_cuckoo_block_size - 1));
|
||||
ASSERT_EQ(props->raw_key_size, keys.size()*props->fixed_key_len);
|
||||
|
||||
// Check unused bucket.
|
||||
std::string unused_key = props->user_collected_properties[
|
||||
@@ -62,14 +66,19 @@ class CuckooBuilderTest {
|
||||
*reinterpret_cast<const uint32_t*>(props->user_collected_properties[
|
||||
CuckooTablePropertyNames::kValueLength].data());
|
||||
ASSERT_EQ(values.empty() ? 0 : values[0].size(), value_len_found);
|
||||
const uint64_t max_buckets =
|
||||
ASSERT_EQ(props->raw_value_size, values.size()*value_len_found);
|
||||
const uint64_t table_size =
|
||||
*reinterpret_cast<const uint64_t*>(props->user_collected_properties[
|
||||
CuckooTablePropertyNames::kMaxNumBuckets].data());
|
||||
ASSERT_EQ(expected_max_buckets, max_buckets);
|
||||
const uint32_t num_hash_fun_found =
|
||||
CuckooTablePropertyNames::kHashTableSize].data());
|
||||
ASSERT_EQ(expected_table_size, table_size);
|
||||
const uint32_t num_hash_func_found =
|
||||
*reinterpret_cast<const uint32_t*>(props->user_collected_properties[
|
||||
CuckooTablePropertyNames::kNumHashTable].data());
|
||||
ASSERT_EQ(expected_num_hash_fun, num_hash_fun_found);
|
||||
CuckooTablePropertyNames::kNumHashFunc].data());
|
||||
ASSERT_EQ(expected_num_hash_func, num_hash_func_found);
|
||||
const uint32_t cuckoo_block_size =
|
||||
*reinterpret_cast<const uint32_t*>(props->user_collected_properties[
|
||||
CuckooTablePropertyNames::kCuckooBlockSize].data());
|
||||
ASSERT_EQ(expected_cuckoo_block_size, cuckoo_block_size);
|
||||
const bool is_last_level_found =
|
||||
*reinterpret_cast<const bool*>(props->user_collected_properties[
|
||||
CuckooTablePropertyNames::kIsLastLevel].data());
|
||||
@@ -79,7 +88,7 @@ class CuckooBuilderTest {
|
||||
// Check contents of the bucket.
|
||||
std::vector<bool> keys_found(keys.size(), false);
|
||||
uint32_t bucket_size = expected_unused_bucket.size();
|
||||
for (uint32_t i = 0; i < max_buckets; ++i) {
|
||||
for (uint32_t i = 0; i < table_size + cuckoo_block_size - 1; ++i) {
|
||||
Slice read_slice;
|
||||
ASSERT_OK(read_file->Read(i*bucket_size, bucket_size,
|
||||
&read_slice, nullptr));
|
||||
@@ -105,6 +114,14 @@ class CuckooBuilderTest {
|
||||
return ikey.GetKey().ToString();
|
||||
}
|
||||
|
||||
uint64_t NextPowOf2(uint64_t num) {
|
||||
uint64_t n = 2;
|
||||
while (n <= num) {
|
||||
n *= 2;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
EnvOptions env_options_;
|
||||
std::string fname;
|
||||
@@ -113,10 +130,10 @@ class CuckooBuilderTest {
|
||||
|
||||
TEST(CuckooBuilderTest, SuccessWithEmptyFile) {
|
||||
unique_ptr<WritableFile> writable_file;
|
||||
fname = test::TmpDir() + "/NoCollisionFullKey";
|
||||
fname = test::TmpDir() + "/EmptyFile";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
4, 100, GetSliceHash);
|
||||
4, 100, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
@@ -143,7 +160,7 @@ TEST(CuckooBuilderTest, WriteSuccessNoCollisionFullKey) {
|
||||
fname = test::TmpDir() + "/NoCollisionFullKey";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 100, GetSliceHash);
|
||||
num_hash_fun, 100, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(keys[i]), Slice(values[i]));
|
||||
@@ -153,11 +170,11 @@ TEST(CuckooBuilderTest, WriteSuccessNoCollisionFullKey) {
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
uint32_t expected_max_buckets = keys.size() / kHashTableRatio;
|
||||
std::string expected_unused_bucket = GetInternalKey("key05", true);
|
||||
uint32_t expected_table_size = NextPowOf2(keys.size() / kHashTableRatio);
|
||||
std::string expected_unused_bucket = GetInternalKey("key00", true);
|
||||
expected_unused_bucket += std::string(values[0].size(), 'a');
|
||||
CheckFileContents(keys, values, expected_locations,
|
||||
expected_unused_bucket, expected_max_buckets, 2, false);
|
||||
expected_unused_bucket, expected_table_size, 2, false);
|
||||
}
|
||||
|
||||
TEST(CuckooBuilderTest, WriteSuccessWithCollisionFullKey) {
|
||||
@@ -180,7 +197,7 @@ TEST(CuckooBuilderTest, WriteSuccessWithCollisionFullKey) {
|
||||
fname = test::TmpDir() + "/WithCollisionFullKey";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 100, GetSliceHash);
|
||||
num_hash_fun, 100, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(keys[i]), Slice(values[i]));
|
||||
@@ -190,11 +207,49 @@ TEST(CuckooBuilderTest, WriteSuccessWithCollisionFullKey) {
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
uint32_t expected_max_buckets = keys.size() / kHashTableRatio;
|
||||
std::string expected_unused_bucket = GetInternalKey("key05", true);
|
||||
uint32_t expected_table_size = NextPowOf2(keys.size() / kHashTableRatio);
|
||||
std::string expected_unused_bucket = GetInternalKey("key00", true);
|
||||
expected_unused_bucket += std::string(values[0].size(), 'a');
|
||||
CheckFileContents(keys, values, expected_locations,
|
||||
expected_unused_bucket, expected_max_buckets, 4, false);
|
||||
expected_unused_bucket, expected_table_size, 4, false);
|
||||
}
|
||||
|
||||
TEST(CuckooBuilderTest, WriteSuccessWithCollisionAndCuckooBlock) {
|
||||
uint32_t num_hash_fun = 4;
|
||||
std::vector<std::string> user_keys = {"key01", "key02", "key03", "key04"};
|
||||
std::vector<std::string> values = {"v01", "v02", "v03", "v04"};
|
||||
hash_map = {
|
||||
{user_keys[0], {0, 1, 2, 3}},
|
||||
{user_keys[1], {0, 1, 2, 3}},
|
||||
{user_keys[2], {0, 1, 2, 3}},
|
||||
{user_keys[3], {0, 1, 2, 3}},
|
||||
};
|
||||
std::vector<uint64_t> expected_locations = {0, 1, 2, 3};
|
||||
std::vector<std::string> keys;
|
||||
for (auto& user_key : user_keys) {
|
||||
keys.push_back(GetInternalKey(user_key, false));
|
||||
}
|
||||
|
||||
unique_ptr<WritableFile> writable_file;
|
||||
uint32_t cuckoo_block_size = 2;
|
||||
fname = test::TmpDir() + "/WithCollisionFullKey2";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 100, BytewiseComparator(), cuckoo_block_size, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(keys[i]), Slice(values[i]));
|
||||
ASSERT_EQ(builder.NumEntries(), i + 1);
|
||||
ASSERT_OK(builder.status());
|
||||
}
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
uint32_t expected_table_size = NextPowOf2(keys.size() / kHashTableRatio);
|
||||
std::string expected_unused_bucket = GetInternalKey("key00", true);
|
||||
expected_unused_bucket += std::string(values[0].size(), 'a');
|
||||
CheckFileContents(keys, values, expected_locations,
|
||||
expected_unused_bucket, expected_table_size, 3, false, cuckoo_block_size);
|
||||
}
|
||||
|
||||
TEST(CuckooBuilderTest, WithCollisionPathFullKey) {
|
||||
@@ -222,7 +277,7 @@ TEST(CuckooBuilderTest, WithCollisionPathFullKey) {
|
||||
fname = test::TmpDir() + "/WithCollisionPathFullKey";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 100, GetSliceHash);
|
||||
num_hash_fun, 100, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(keys[i]), Slice(values[i]));
|
||||
@@ -232,11 +287,50 @@ TEST(CuckooBuilderTest, WithCollisionPathFullKey) {
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
uint32_t expected_max_buckets = keys.size() / kHashTableRatio;
|
||||
std::string expected_unused_bucket = GetInternalKey("key06", true);
|
||||
uint32_t expected_table_size = NextPowOf2(keys.size() / kHashTableRatio);
|
||||
std::string expected_unused_bucket = GetInternalKey("key00", true);
|
||||
expected_unused_bucket += std::string(values[0].size(), 'a');
|
||||
CheckFileContents(keys, values, expected_locations,
|
||||
expected_unused_bucket, expected_max_buckets, 2, false);
|
||||
expected_unused_bucket, expected_table_size, 2, false);
|
||||
}
|
||||
|
||||
TEST(CuckooBuilderTest, WithCollisionPathFullKeyAndCuckooBlock) {
|
||||
uint32_t num_hash_fun = 2;
|
||||
std::vector<std::string> user_keys = {"key01", "key02", "key03",
|
||||
"key04", "key05"};
|
||||
std::vector<std::string> values = {"v01", "v02", "v03", "v04", "v05"};
|
||||
hash_map = {
|
||||
{user_keys[0], {0, 1}},
|
||||
{user_keys[1], {1, 2}},
|
||||
{user_keys[2], {3, 4}},
|
||||
{user_keys[3], {4, 5}},
|
||||
{user_keys[4], {0, 3}},
|
||||
};
|
||||
std::vector<uint64_t> expected_locations = {2, 1, 3, 4, 0};
|
||||
std::vector<std::string> keys;
|
||||
for (auto& user_key : user_keys) {
|
||||
keys.push_back(GetInternalKey(user_key, false));
|
||||
}
|
||||
|
||||
unique_ptr<WritableFile> writable_file;
|
||||
fname = test::TmpDir() + "/WithCollisionPathFullKeyAndCuckooBlock";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 100, BytewiseComparator(), 2, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(keys[i]), Slice(values[i]));
|
||||
ASSERT_EQ(builder.NumEntries(), i + 1);
|
||||
ASSERT_OK(builder.status());
|
||||
}
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
uint32_t expected_table_size = NextPowOf2(keys.size() / kHashTableRatio);
|
||||
std::string expected_unused_bucket = GetInternalKey("key00", true);
|
||||
expected_unused_bucket += std::string(values[0].size(), 'a');
|
||||
CheckFileContents(keys, values, expected_locations,
|
||||
expected_unused_bucket, expected_table_size, 2, false, 2);
|
||||
}
|
||||
|
||||
TEST(CuckooBuilderTest, WriteSuccessNoCollisionUserKey) {
|
||||
@@ -255,7 +349,7 @@ TEST(CuckooBuilderTest, WriteSuccessNoCollisionUserKey) {
|
||||
fname = test::TmpDir() + "/NoCollisionUserKey";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 100, GetSliceHash);
|
||||
num_hash_fun, 100, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(GetInternalKey(user_keys[i], true)), Slice(values[i]));
|
||||
@@ -265,11 +359,11 @@ TEST(CuckooBuilderTest, WriteSuccessNoCollisionUserKey) {
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
uint32_t expected_max_buckets = user_keys.size() / kHashTableRatio;
|
||||
std::string expected_unused_bucket = "key05";
|
||||
uint32_t expected_table_size = NextPowOf2(user_keys.size() / kHashTableRatio);
|
||||
std::string expected_unused_bucket = "key00";
|
||||
expected_unused_bucket += std::string(values[0].size(), 'a');
|
||||
CheckFileContents(user_keys, values, expected_locations,
|
||||
expected_unused_bucket, expected_max_buckets, 2, true);
|
||||
expected_unused_bucket, expected_table_size, 2, true);
|
||||
}
|
||||
|
||||
TEST(CuckooBuilderTest, WriteSuccessWithCollisionUserKey) {
|
||||
@@ -288,7 +382,7 @@ TEST(CuckooBuilderTest, WriteSuccessWithCollisionUserKey) {
|
||||
fname = test::TmpDir() + "/WithCollisionUserKey";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 100, GetSliceHash);
|
||||
num_hash_fun, 100, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(GetInternalKey(user_keys[i], true)), Slice(values[i]));
|
||||
@@ -298,11 +392,11 @@ TEST(CuckooBuilderTest, WriteSuccessWithCollisionUserKey) {
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
uint32_t expected_max_buckets = user_keys.size() / kHashTableRatio;
|
||||
std::string expected_unused_bucket = "key05";
|
||||
uint32_t expected_table_size = NextPowOf2(user_keys.size() / kHashTableRatio);
|
||||
std::string expected_unused_bucket = "key00";
|
||||
expected_unused_bucket += std::string(values[0].size(), 'a');
|
||||
CheckFileContents(user_keys, values, expected_locations,
|
||||
expected_unused_bucket, expected_max_buckets, 4, true);
|
||||
expected_unused_bucket, expected_table_size, 4, true);
|
||||
}
|
||||
|
||||
TEST(CuckooBuilderTest, WithCollisionPathUserKey) {
|
||||
@@ -323,7 +417,7 @@ TEST(CuckooBuilderTest, WithCollisionPathUserKey) {
|
||||
fname = test::TmpDir() + "/WithCollisionPathUserKey";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 2, GetSliceHash);
|
||||
num_hash_fun, 2, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(GetInternalKey(user_keys[i], true)), Slice(values[i]));
|
||||
@@ -333,11 +427,11 @@ TEST(CuckooBuilderTest, WithCollisionPathUserKey) {
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
uint32_t expected_max_buckets = user_keys.size() / kHashTableRatio;
|
||||
std::string expected_unused_bucket = "key06";
|
||||
uint32_t expected_table_size = NextPowOf2(user_keys.size() / kHashTableRatio);
|
||||
std::string expected_unused_bucket = "key00";
|
||||
expected_unused_bucket += std::string(values[0].size(), 'a');
|
||||
CheckFileContents(user_keys, values, expected_locations,
|
||||
expected_unused_bucket, expected_max_buckets, 2, true);
|
||||
expected_unused_bucket, expected_table_size, 2, true);
|
||||
}
|
||||
|
||||
TEST(CuckooBuilderTest, FailWhenCollisionPathTooLong) {
|
||||
@@ -359,7 +453,7 @@ TEST(CuckooBuilderTest, FailWhenCollisionPathTooLong) {
|
||||
fname = test::TmpDir() + "/WithCollisionPathUserKey";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 2, GetSliceHash);
|
||||
num_hash_fun, 2, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t i = 0; i < user_keys.size(); i++) {
|
||||
builder.Add(Slice(GetInternalKey(user_keys[i], false)), Slice("value"));
|
||||
@@ -379,7 +473,7 @@ TEST(CuckooBuilderTest, FailWhenSameKeyInserted) {
|
||||
fname = test::TmpDir() + "/FailWhenSameKeyInserted";
|
||||
ASSERT_OK(env_->NewWritableFile(fname, &writable_file, env_options_));
|
||||
CuckooTableBuilder builder(writable_file.get(), kHashTableRatio,
|
||||
num_hash_fun, 100, GetSliceHash);
|
||||
num_hash_fun, 100, BytewiseComparator(), 1, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
|
||||
builder.Add(Slice(GetInternalKey(user_key, false)), Slice("value1"));
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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.
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "table/cuckoo_table_factory.h"
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "table/cuckoo_table_builder.h"
|
||||
#include "table/cuckoo_table_reader.h"
|
||||
|
||||
namespace rocksdb {
|
||||
Status CuckooTableFactory::NewTableReader(const Options& options,
|
||||
const EnvOptions& soptions, const InternalKeyComparator& icomp,
|
||||
std::unique_ptr<RandomAccessFile>&& file, uint64_t file_size,
|
||||
std::unique_ptr<TableReader>* table) const {
|
||||
std::unique_ptr<CuckooTableReader> new_reader(new CuckooTableReader(options,
|
||||
std::move(file), file_size, icomp.user_comparator(), nullptr));
|
||||
Status s = new_reader->status();
|
||||
if (s.ok()) {
|
||||
*table = std::move(new_reader);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
TableBuilder* CuckooTableFactory::NewTableBuilder(
|
||||
const Options& options, const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file, CompressionType compression_type) const {
|
||||
return new CuckooTableBuilder(file, hash_table_ratio_, 64, max_search_depth_,
|
||||
internal_comparator.user_comparator(), cuckoo_block_size_, nullptr);
|
||||
}
|
||||
|
||||
std::string CuckooTableFactory::GetPrintableTableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(2000);
|
||||
const int kBufferSize = 200;
|
||||
char buffer[kBufferSize];
|
||||
|
||||
snprintf(buffer, kBufferSize, " hash_table_ratio: %lf\n",
|
||||
hash_table_ratio_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " max_search_depth: %u\n",
|
||||
max_search_depth_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " cuckoo_block_size: %u\n",
|
||||
cuckoo_block_size_);
|
||||
ret.append(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
TableFactory* NewCuckooTableFactory(double hash_table_ratio,
|
||||
uint32_t max_search_depth, uint32_t cuckoo_block_size) {
|
||||
return new CuckooTableFactory(
|
||||
hash_table_ratio, max_search_depth, cuckoo_block_size);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -6,27 +6,66 @@
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <string>
|
||||
#include "rocksdb/table.h"
|
||||
#include "util/murmurhash.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
static const uint32_t kMaxNumHashTable = 64;
|
||||
|
||||
uint64_t GetSliceMurmurHash(const Slice& s, uint32_t index,
|
||||
uint64_t max_num_buckets) {
|
||||
static constexpr uint32_t seeds[kMaxNumHashTable] = {
|
||||
816922183, 506425713, 949485004, 22513986, 421427259, 500437285,
|
||||
888981693, 847587269, 511007211, 722295391, 934013645, 566947683,
|
||||
193618736, 428277388, 770956674, 819994962, 755946528, 40807421,
|
||||
263144466, 241420041, 444294464, 731606396, 304158902, 563235655,
|
||||
968740453, 336996831, 462831574, 407970157, 985877240, 637708754,
|
||||
736932700, 205026023, 755371467, 729648411, 807744117, 46482135,
|
||||
847092855, 620960699, 102476362, 314094354, 625838942, 550889395,
|
||||
639071379, 834567510, 397667304, 151945969, 443634243, 196618243,
|
||||
421986347, 407218337, 964502417, 327741231, 493359459, 452453139,
|
||||
692216398, 108161624, 816246924, 234779764, 618949448, 496133787,
|
||||
156374056, 316589799, 982915425, 553105889 };
|
||||
return MurmurHash(s.data(), s.size(), seeds[index]) % max_num_buckets;
|
||||
const uint32_t kCuckooMurmurSeedMultiplier = 816922183;
|
||||
static inline uint64_t CuckooHash(
|
||||
const Slice& user_key, uint32_t hash_cnt, uint64_t table_size_minus_one,
|
||||
uint64_t (*get_slice_hash)(const Slice&, uint32_t, uint64_t)) {
|
||||
#ifndef NDEBUG
|
||||
// This part is used only in unit tests.
|
||||
if (get_slice_hash != nullptr) {
|
||||
return get_slice_hash(user_key, hash_cnt, table_size_minus_one + 1);
|
||||
}
|
||||
#endif
|
||||
return MurmurHash(user_key.data(), user_key.size(),
|
||||
kCuckooMurmurSeedMultiplier * hash_cnt) & table_size_minus_one;
|
||||
}
|
||||
|
||||
// Cuckoo Table is designed for applications that require fast point lookups
|
||||
// but not fast range scans.
|
||||
//
|
||||
// Some assumptions:
|
||||
// - Key length and Value length are fixed.
|
||||
// - Does not support Snapshot.
|
||||
// - Does not support Merge operations.
|
||||
class CuckooTableFactory : public TableFactory {
|
||||
public:
|
||||
CuckooTableFactory(double hash_table_ratio, uint32_t max_search_depth,
|
||||
uint32_t cuckoo_block_size)
|
||||
: hash_table_ratio_(hash_table_ratio),
|
||||
max_search_depth_(max_search_depth),
|
||||
cuckoo_block_size_(cuckoo_block_size) {}
|
||||
~CuckooTableFactory() {}
|
||||
|
||||
const char* Name() const override { return "CuckooTable"; }
|
||||
|
||||
Status NewTableReader(
|
||||
const Options& options, const EnvOptions& soptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
unique_ptr<RandomAccessFile>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table) const override;
|
||||
|
||||
TableBuilder* NewTableBuilder(const Options& options,
|
||||
const InternalKeyComparator& icomparator, WritableFile* file,
|
||||
CompressionType compression_type) const override;
|
||||
|
||||
// Sanitizes the specified DB Options.
|
||||
Status SanitizeDBOptions(const DBOptions* db_opts) const override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::string GetPrintableTableOptions() const override;
|
||||
|
||||
private:
|
||||
const double hash_table_ratio_;
|
||||
const uint32_t max_search_depth_;
|
||||
const uint32_t cuckoo_block_size_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -17,10 +17,14 @@
|
||||
#include <vector>
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "table/cuckoo_table_factory.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace {
|
||||
static const uint64_t CACHE_LINE_MASK = ~((uint64_t)CACHE_LINE_SIZE - 1);
|
||||
}
|
||||
|
||||
extern const uint64_t kCuckooTableMagicNumber;
|
||||
|
||||
@@ -28,8 +32,10 @@ CuckooTableReader::CuckooTableReader(
|
||||
const Options& options,
|
||||
std::unique_ptr<RandomAccessFile>&& file,
|
||||
uint64_t file_size,
|
||||
const Comparator* comparator,
|
||||
uint64_t (*get_slice_hash)(const Slice&, uint32_t, uint64_t))
|
||||
: file_(std::move(file)),
|
||||
ucomp_(comparator),
|
||||
get_slice_hash_(get_slice_hash) {
|
||||
if (!options.allow_mmap_reads) {
|
||||
status_ = Status::InvalidArgument("File is not mmaped");
|
||||
@@ -42,12 +48,12 @@ CuckooTableReader::CuckooTableReader(
|
||||
}
|
||||
table_props_.reset(props);
|
||||
auto& user_props = props->user_collected_properties;
|
||||
auto hash_funs = user_props.find(CuckooTablePropertyNames::kNumHashTable);
|
||||
auto hash_funs = user_props.find(CuckooTablePropertyNames::kNumHashFunc);
|
||||
if (hash_funs == user_props.end()) {
|
||||
status_ = Status::InvalidArgument("Number of hash functions not found");
|
||||
return;
|
||||
}
|
||||
num_hash_fun_ = *reinterpret_cast<const uint32_t*>(hash_funs->second.data());
|
||||
num_hash_func_ = *reinterpret_cast<const uint32_t*>(hash_funs->second.data());
|
||||
auto unused_key = user_props.find(CuckooTablePropertyNames::kEmptyKey);
|
||||
if (unused_key == user_props.end()) {
|
||||
status_ = Status::InvalidArgument("Empty bucket value not found");
|
||||
@@ -65,18 +71,29 @@ CuckooTableReader::CuckooTableReader(
|
||||
value_length->second.data());
|
||||
bucket_length_ = key_length_ + value_length_;
|
||||
|
||||
auto num_buckets = user_props.find(CuckooTablePropertyNames::kMaxNumBuckets);
|
||||
if (num_buckets == user_props.end()) {
|
||||
status_ = Status::InvalidArgument("Num buckets not found");
|
||||
auto hash_table_size = user_props.find(
|
||||
CuckooTablePropertyNames::kHashTableSize);
|
||||
if (hash_table_size == user_props.end()) {
|
||||
status_ = Status::InvalidArgument("Hash table size not found");
|
||||
return;
|
||||
}
|
||||
num_buckets_ = *reinterpret_cast<const uint64_t*>(num_buckets->second.data());
|
||||
table_size_minus_one_ = *reinterpret_cast<const uint64_t*>(
|
||||
hash_table_size->second.data()) - 1;
|
||||
auto is_last_level = user_props.find(CuckooTablePropertyNames::kIsLastLevel);
|
||||
if (is_last_level == user_props.end()) {
|
||||
status_ = Status::InvalidArgument("Is last level not found");
|
||||
return;
|
||||
}
|
||||
is_last_level_ = *reinterpret_cast<const bool*>(is_last_level->second.data());
|
||||
auto cuckoo_block_size = user_props.find(
|
||||
CuckooTablePropertyNames::kCuckooBlockSize);
|
||||
if (cuckoo_block_size == user_props.end()) {
|
||||
status_ = Status::InvalidArgument("Cuckoo block size not found");
|
||||
return;
|
||||
}
|
||||
cuckoo_block_size_ = *reinterpret_cast<const uint32_t*>(
|
||||
cuckoo_block_size->second.data());
|
||||
cuckoo_block_bytes_minus_one_ = cuckoo_block_size_ * bucket_length_ - 1;
|
||||
status_ = file_->Read(0, file_size, &file_data_, nullptr);
|
||||
}
|
||||
|
||||
@@ -85,30 +102,51 @@ Status CuckooTableReader::Get(
|
||||
bool (*result_handler)(void* arg, const ParsedInternalKey& k,
|
||||
const Slice& v),
|
||||
void (*mark_key_may_exist_handler)(void* handle_context)) {
|
||||
ParsedInternalKey ikey;
|
||||
if (!ParseInternalKey(key, &ikey)) {
|
||||
return Status::Corruption("Unable to parse key into inernal key.");
|
||||
}
|
||||
for (uint32_t hash_cnt = 0; hash_cnt < num_hash_fun_; ++hash_cnt) {
|
||||
uint64_t hash_val = get_slice_hash_(ikey.user_key, hash_cnt, num_buckets_);
|
||||
assert(hash_val < num_buckets_);
|
||||
uint64_t offset = hash_val * bucket_length_;
|
||||
assert(key.size() == key_length_ + (is_last_level_ ? 8 : 0));
|
||||
Slice user_key = ExtractUserKey(key);
|
||||
for (uint32_t hash_cnt = 0; hash_cnt < num_hash_func_; ++hash_cnt) {
|
||||
uint64_t offset = bucket_length_ * CuckooHash(
|
||||
user_key, hash_cnt, table_size_minus_one_, get_slice_hash_);
|
||||
const char* bucket = &file_data_.data()[offset];
|
||||
if (unused_key_.compare(0, key_length_, bucket, key_length_) == 0) {
|
||||
return Status::OK();
|
||||
}
|
||||
// Here, we compare only the user key part as we support only one entry
|
||||
// per user key and we don't support sanpshot.
|
||||
if (ikey.user_key.compare(Slice(bucket, ikey.user_key.size())) == 0) {
|
||||
Slice value = Slice(&bucket[key_length_], value_length_);
|
||||
result_handler(handle_context, ikey, value);
|
||||
// We don't support merge operations. So, we return here.
|
||||
return Status::OK();
|
||||
for (uint32_t block_idx = 0; block_idx < cuckoo_block_size_;
|
||||
++block_idx, bucket += bucket_length_) {
|
||||
if (ucomp_->Compare(Slice(unused_key_.data(), user_key.size()),
|
||||
Slice(bucket, user_key.size())) == 0) {
|
||||
return Status::OK();
|
||||
}
|
||||
// Here, we compare only the user key part as we support only one entry
|
||||
// per user key and we don't support sanpshot.
|
||||
if (ucomp_->Compare(user_key, Slice(bucket, user_key.size())) == 0) {
|
||||
Slice value = Slice(&bucket[key_length_], value_length_);
|
||||
if (is_last_level_) {
|
||||
ParsedInternalKey found_ikey(
|
||||
Slice(bucket, key_length_), 0, kTypeValue);
|
||||
result_handler(handle_context, found_ikey, value);
|
||||
} else {
|
||||
Slice full_key(bucket, key_length_);
|
||||
ParsedInternalKey found_ikey;
|
||||
ParseInternalKey(full_key, &found_ikey);
|
||||
result_handler(handle_context, found_ikey, value);
|
||||
}
|
||||
// We don't support merge operations. So, we return here.
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void CuckooTableReader::Prepare(const Slice& key) {
|
||||
// Prefetch the first Cuckoo Block.
|
||||
Slice user_key = ExtractUserKey(key);
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(file_data_.data()) +
|
||||
bucket_length_ * CuckooHash(user_key, 0, table_size_minus_one_, nullptr);
|
||||
uint64_t end_addr = addr + cuckoo_block_bytes_minus_one_;
|
||||
for (addr &= CACHE_LINE_MASK; addr < end_addr; addr += CACHE_LINE_SIZE) {
|
||||
PREFETCH(reinterpret_cast<const char*>(addr), 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
class CuckooTableIterator : public Iterator {
|
||||
public:
|
||||
explicit CuckooTableIterator(CuckooTableReader* reader);
|
||||
@@ -125,17 +163,29 @@ class CuckooTableIterator : public Iterator {
|
||||
void LoadKeysFromReader();
|
||||
|
||||
private:
|
||||
struct {
|
||||
struct CompareKeys {
|
||||
CompareKeys(const Comparator* ucomp, const bool last_level)
|
||||
: ucomp_(ucomp),
|
||||
is_last_level_(last_level) {}
|
||||
bool operator()(const std::pair<Slice, uint32_t>& first,
|
||||
const std::pair<Slice, uint32_t>& second) const {
|
||||
return first.first.compare(second.first) < 0;
|
||||
if (is_last_level_) {
|
||||
return ucomp_->Compare(first.first, second.first) < 0;
|
||||
} else {
|
||||
return ucomp_->Compare(ExtractUserKey(first.first),
|
||||
ExtractUserKey(second.first)) < 0;
|
||||
}
|
||||
}
|
||||
} CompareKeys;
|
||||
|
||||
private:
|
||||
const Comparator* ucomp_;
|
||||
const bool is_last_level_;
|
||||
};
|
||||
const CompareKeys comparator_;
|
||||
void PrepareKVAtCurrIdx();
|
||||
CuckooTableReader* reader_;
|
||||
Status status_;
|
||||
// Contains a map of keys to bucket_id sorted in key order.
|
||||
// We assume byte-wise comparison for key ordering.
|
||||
std::vector<std::pair<Slice, uint32_t>> key_to_bucket_id_;
|
||||
// We assume that the number of items can be stored in uint32 (4 Billion).
|
||||
uint32_t curr_key_idx_;
|
||||
@@ -147,7 +197,8 @@ class CuckooTableIterator : public Iterator {
|
||||
};
|
||||
|
||||
CuckooTableIterator::CuckooTableIterator(CuckooTableReader* reader)
|
||||
: reader_(reader),
|
||||
: comparator_(reader->ucomp_, reader->is_last_level_),
|
||||
reader_(reader),
|
||||
curr_key_idx_(std::numeric_limits<int32_t>::max()) {
|
||||
key_to_bucket_id_.clear();
|
||||
curr_value_.clear();
|
||||
@@ -156,7 +207,9 @@ CuckooTableIterator::CuckooTableIterator(CuckooTableReader* reader)
|
||||
|
||||
void CuckooTableIterator::LoadKeysFromReader() {
|
||||
key_to_bucket_id_.reserve(reader_->GetTableProperties()->num_entries);
|
||||
for (uint32_t bucket_id = 0; bucket_id < reader_->num_buckets_; bucket_id++) {
|
||||
uint64_t num_buckets = reader_->table_size_minus_one_ +
|
||||
reader_->cuckoo_block_size_;
|
||||
for (uint32_t bucket_id = 0; bucket_id < num_buckets; bucket_id++) {
|
||||
Slice read_key;
|
||||
status_ = reader_->file_->Read(bucket_id * reader_->bucket_length_,
|
||||
reader_->key_length_, &read_key, nullptr);
|
||||
@@ -166,7 +219,7 @@ void CuckooTableIterator::LoadKeysFromReader() {
|
||||
}
|
||||
assert(key_to_bucket_id_.size() ==
|
||||
reader_->GetTableProperties()->num_entries);
|
||||
std::sort(key_to_bucket_id_.begin(), key_to_bucket_id_.end(), CompareKeys);
|
||||
std::sort(key_to_bucket_id_.begin(), key_to_bucket_id_.end(), comparator_);
|
||||
curr_key_idx_ = key_to_bucket_id_.size();
|
||||
}
|
||||
|
||||
@@ -188,7 +241,7 @@ void CuckooTableIterator::Seek(const Slice& target) {
|
||||
auto seek_it = std::lower_bound(key_to_bucket_id_.begin(),
|
||||
key_to_bucket_id_.end(),
|
||||
std::make_pair(target_to_search, 0),
|
||||
CompareKeys);
|
||||
comparator_);
|
||||
curr_key_idx_ = std::distance(key_to_bucket_id_.begin(), seek_it);
|
||||
PrepareKVAtCurrIdx();
|
||||
}
|
||||
@@ -251,10 +304,17 @@ Slice CuckooTableIterator::value() const {
|
||||
return curr_value_;
|
||||
}
|
||||
|
||||
Iterator* CuckooTableReader::NewIterator(const ReadOptions&, Arena* arena) {
|
||||
extern Iterator* NewErrorIterator(const Status& status, Arena* arena);
|
||||
|
||||
Iterator* CuckooTableReader::NewIterator(
|
||||
const ReadOptions& read_options, Arena* arena) {
|
||||
if (!status().ok()) {
|
||||
return NewErrorIterator(
|
||||
Status::Corruption("CuckooTableReader status is not okay."));
|
||||
Status::Corruption("CuckooTableReader status is not okay."), arena);
|
||||
}
|
||||
if (read_options.total_order_seek) {
|
||||
return NewErrorIterator(
|
||||
Status::InvalidArgument("total_order_seek is not supported."), arena);
|
||||
}
|
||||
CuckooTableIterator* iter;
|
||||
if (arena == nullptr) {
|
||||
|
||||
@@ -29,6 +29,7 @@ class CuckooTableReader: public TableReader {
|
||||
const Options& options,
|
||||
std::unique_ptr<RandomAccessFile>&& file,
|
||||
uint64_t file_size,
|
||||
const Comparator* user_comparator,
|
||||
uint64_t (*get_slice_hash)(const Slice&, uint32_t, uint64_t));
|
||||
~CuckooTableReader() {}
|
||||
|
||||
@@ -46,6 +47,7 @@ class CuckooTableReader: public TableReader {
|
||||
override;
|
||||
|
||||
Iterator* NewIterator(const ReadOptions&, Arena* arena = nullptr) override;
|
||||
void Prepare(const Slice& target) override;
|
||||
|
||||
// Report an approximation of how much memory has been used.
|
||||
size_t ApproximateMemoryUsage() const override;
|
||||
@@ -53,7 +55,6 @@ class CuckooTableReader: public TableReader {
|
||||
// Following methods are not implemented for Cuckoo Table Reader
|
||||
uint64_t ApproximateOffsetOf(const Slice& key) override { return 0; }
|
||||
void SetupForCompaction() override {}
|
||||
void Prepare(const Slice& target) override {}
|
||||
// End of methods not implemented.
|
||||
|
||||
private:
|
||||
@@ -64,12 +65,15 @@ class CuckooTableReader: public TableReader {
|
||||
bool is_last_level_;
|
||||
std::shared_ptr<const TableProperties> table_props_;
|
||||
Status status_;
|
||||
uint32_t num_hash_fun_;
|
||||
uint32_t num_hash_func_;
|
||||
std::string unused_key_;
|
||||
uint32_t key_length_;
|
||||
uint32_t value_length_;
|
||||
uint32_t bucket_length_;
|
||||
uint64_t num_buckets_;
|
||||
uint32_t cuckoo_block_size_;
|
||||
uint32_t cuckoo_block_bytes_minus_one_;
|
||||
uint64_t table_size_minus_one_;
|
||||
const Comparator* ucomp_;
|
||||
uint64_t (*get_slice_hash_)(const Slice& s, uint32_t index,
|
||||
uint64_t max_num_buckets);
|
||||
};
|
||||
|
||||
@@ -33,11 +33,11 @@ using GFLAGS::SetUsageMessage;
|
||||
DEFINE_string(file_dir, "", "Directory where the files will be created"
|
||||
" for benchmark. Added for using tmpfs.");
|
||||
DEFINE_bool(enable_perf, false, "Run Benchmark Tests too.");
|
||||
DEFINE_bool(write, false,
|
||||
"Should write new values to file in performance tests?");
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
extern const uint64_t kCuckooTableMagicNumber;
|
||||
|
||||
namespace {
|
||||
const uint32_t kNumHashFunc = 10;
|
||||
// Methods, variables related to Hash functions.
|
||||
@@ -101,11 +101,12 @@ class CuckooReaderTest {
|
||||
return std::string(reinterpret_cast<char*>(&i), sizeof(i));
|
||||
}
|
||||
|
||||
void CreateCuckooFileAndCheckReader() {
|
||||
unique_ptr<WritableFile> writable_file;
|
||||
void CreateCuckooFileAndCheckReader(
|
||||
const Comparator* ucomp = BytewiseComparator()) {
|
||||
std::unique_ptr<WritableFile> writable_file;
|
||||
ASSERT_OK(env->NewWritableFile(fname, &writable_file, env_options));
|
||||
CuckooTableBuilder builder(
|
||||
writable_file.get(), 0.9, kNumHashFunc, 100, GetSliceHash);
|
||||
writable_file.get(), 0.9, kNumHashFunc, 100, ucomp, 2, GetSliceHash);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint32_t key_idx = 0; key_idx < num_items; ++key_idx) {
|
||||
builder.Add(Slice(keys[key_idx]), Slice(values[key_idx]));
|
||||
@@ -118,12 +119,13 @@ class CuckooReaderTest {
|
||||
ASSERT_OK(writable_file->Close());
|
||||
|
||||
// Check reader now.
|
||||
unique_ptr<RandomAccessFile> read_file;
|
||||
std::unique_ptr<RandomAccessFile> read_file;
|
||||
ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
|
||||
CuckooTableReader reader(
|
||||
options,
|
||||
std::move(read_file),
|
||||
file_size,
|
||||
ucomp,
|
||||
GetSliceHash);
|
||||
ASSERT_OK(reader.status());
|
||||
for (uint32_t i = 0; i < num_items; ++i) {
|
||||
@@ -142,13 +144,14 @@ class CuckooReaderTest {
|
||||
}
|
||||
}
|
||||
|
||||
void CheckIterator() {
|
||||
unique_ptr<RandomAccessFile> read_file;
|
||||
void CheckIterator(const Comparator* ucomp = BytewiseComparator()) {
|
||||
std::unique_ptr<RandomAccessFile> read_file;
|
||||
ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
|
||||
CuckooTableReader reader(
|
||||
options,
|
||||
std::move(read_file),
|
||||
file_size,
|
||||
ucomp,
|
||||
GetSliceHash);
|
||||
ASSERT_OK(reader.status());
|
||||
Iterator* it = reader.NewIterator(ReadOptions(), nullptr);
|
||||
@@ -240,12 +243,40 @@ TEST(CuckooReaderTest, WhenKeyExists) {
|
||||
CreateCuckooFileAndCheckReader();
|
||||
}
|
||||
|
||||
TEST(CuckooReaderTest, WhenKeyExistsWithUint64Comparator) {
|
||||
SetUp(kNumHashFunc);
|
||||
fname = test::TmpDir() + "/CuckooReaderUint64_WhenKeyExists";
|
||||
for (uint64_t i = 0; i < num_items; i++) {
|
||||
user_keys[i].resize(8);
|
||||
memcpy(&user_keys[i][0], static_cast<void*>(&i), 8);
|
||||
ParsedInternalKey ikey(user_keys[i], i + 1000, kTypeValue);
|
||||
AppendInternalKey(&keys[i], ikey);
|
||||
values[i] = "value" + NumToStr(i);
|
||||
// Give disjoint hash values.
|
||||
AddHashLookups(user_keys[i], i, kNumHashFunc);
|
||||
}
|
||||
CreateCuckooFileAndCheckReader(test::Uint64Comparator());
|
||||
// Last level file.
|
||||
UpdateKeys(true);
|
||||
CreateCuckooFileAndCheckReader(test::Uint64Comparator());
|
||||
// Test with collision. Make all hash values collide.
|
||||
hash_map.clear();
|
||||
for (uint32_t i = 0; i < num_items; i++) {
|
||||
AddHashLookups(user_keys[i], 0, kNumHashFunc);
|
||||
}
|
||||
UpdateKeys(false);
|
||||
CreateCuckooFileAndCheckReader(test::Uint64Comparator());
|
||||
// Last level file.
|
||||
UpdateKeys(true);
|
||||
CreateCuckooFileAndCheckReader(test::Uint64Comparator());
|
||||
}
|
||||
|
||||
TEST(CuckooReaderTest, CheckIterator) {
|
||||
SetUp(2*kNumHashFunc);
|
||||
fname = test::TmpDir() + "/CuckooReader_CheckIterator";
|
||||
for (uint64_t i = 0; i < num_items; i++) {
|
||||
user_keys[i] = "key" + NumToStr(i);
|
||||
ParsedInternalKey ikey(user_keys[i], 0, kTypeValue);
|
||||
ParsedInternalKey ikey(user_keys[i], 1000, kTypeValue);
|
||||
AppendInternalKey(&keys[i], ikey);
|
||||
values[i] = "value" + NumToStr(i);
|
||||
// Give disjoint hash values, in reverse order.
|
||||
@@ -259,6 +290,26 @@ TEST(CuckooReaderTest, CheckIterator) {
|
||||
CheckIterator();
|
||||
}
|
||||
|
||||
TEST(CuckooReaderTest, CheckIteratorUint64) {
|
||||
SetUp(2*kNumHashFunc);
|
||||
fname = test::TmpDir() + "/CuckooReader_CheckIterator";
|
||||
for (uint64_t i = 0; i < num_items; i++) {
|
||||
user_keys[i].resize(8);
|
||||
memcpy(&user_keys[i][0], static_cast<void*>(&i), 8);
|
||||
ParsedInternalKey ikey(user_keys[i], 1000, kTypeValue);
|
||||
AppendInternalKey(&keys[i], ikey);
|
||||
values[i] = "value" + NumToStr(i);
|
||||
// Give disjoint hash values, in reverse order.
|
||||
AddHashLookups(user_keys[i], num_items-i-1, kNumHashFunc);
|
||||
}
|
||||
CreateCuckooFileAndCheckReader(test::Uint64Comparator());
|
||||
CheckIterator(test::Uint64Comparator());
|
||||
// Last level file.
|
||||
UpdateKeys(true);
|
||||
CreateCuckooFileAndCheckReader(test::Uint64Comparator());
|
||||
CheckIterator(test::Uint64Comparator());
|
||||
}
|
||||
|
||||
TEST(CuckooReaderTest, WhenKeyNotFound) {
|
||||
// Add keys with colliding hash values.
|
||||
SetUp(kNumHashFunc);
|
||||
@@ -272,12 +323,13 @@ TEST(CuckooReaderTest, WhenKeyNotFound) {
|
||||
AddHashLookups(user_keys[i], 0, kNumHashFunc);
|
||||
}
|
||||
CreateCuckooFileAndCheckReader();
|
||||
unique_ptr<RandomAccessFile> read_file;
|
||||
std::unique_ptr<RandomAccessFile> read_file;
|
||||
ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
|
||||
CuckooTableReader reader(
|
||||
options,
|
||||
std::move(read_file),
|
||||
file_size,
|
||||
BytewiseComparator(),
|
||||
GetSliceHash);
|
||||
ASSERT_OK(reader.status());
|
||||
// Search for a key with colliding hash values.
|
||||
@@ -302,15 +354,6 @@ TEST(CuckooReaderTest, WhenKeyNotFound) {
|
||||
ASSERT_EQ(0, v.call_count);
|
||||
ASSERT_OK(reader.status());
|
||||
|
||||
// Test read with corrupted key.
|
||||
Slice corrupt_key("corrupt_ikey");
|
||||
ASSERT_TRUE(!ParseInternalKey(corrupt_key, &ikey));
|
||||
ASSERT_TRUE(reader.Get(
|
||||
ReadOptions(), corrupt_key, &v,
|
||||
AssertValues, nullptr).IsCorruption());
|
||||
ASSERT_EQ(0, v.call_count);
|
||||
ASSERT_OK(reader.status());
|
||||
|
||||
// Test read when key is unused key.
|
||||
std::string unused_key =
|
||||
reader.GetTableProperties()->user_collected_properties.at(
|
||||
@@ -339,94 +382,150 @@ bool CheckValue(void* cnt_ptr, const ParsedInternalKey& k, const Slice& v) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetKeys(uint64_t num, std::vector<std::string>* keys) {
|
||||
IterKey k;
|
||||
k.SetInternalKey("", 0, kTypeValue);
|
||||
std::string internal_key_suffix = k.GetKey().ToString();
|
||||
ASSERT_EQ(static_cast<size_t>(8), internal_key_suffix.size());
|
||||
for (uint64_t key_idx = 0; key_idx < num; ++key_idx) {
|
||||
std::string new_key(reinterpret_cast<char*>(&key_idx), sizeof(key_idx));
|
||||
new_key += internal_key_suffix;
|
||||
keys->push_back(new_key);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetFileName(uint64_t num) {
|
||||
if (FLAGS_file_dir.empty()) {
|
||||
FLAGS_file_dir = test::TmpDir();
|
||||
}
|
||||
return FLAGS_file_dir + "/cuckoo_read_benchmark" +
|
||||
std::to_string(num/1000000) + "Mkeys";
|
||||
}
|
||||
|
||||
// Create last level file as we are interested in measuring performance of
|
||||
// last level file only.
|
||||
void BM_CuckooRead(uint64_t num, uint32_t key_length,
|
||||
uint32_t value_length, uint64_t num_reads, double hash_ratio) {
|
||||
assert(value_length <= key_length);
|
||||
assert(8 <= key_length);
|
||||
std::vector<std::string> keys;
|
||||
void WriteFile(const std::vector<std::string>& keys,
|
||||
const uint64_t num, double hash_ratio) {
|
||||
Options options;
|
||||
options.allow_mmap_reads = true;
|
||||
Env* env = options.env;
|
||||
EnvOptions env_options = EnvOptions(options);
|
||||
uint64_t file_size;
|
||||
if (FLAGS_file_dir.empty()) {
|
||||
FLAGS_file_dir = test::TmpDir();
|
||||
}
|
||||
std::string fname = FLAGS_file_dir + "/cuckoo_read_benchmark";
|
||||
std::string fname = GetFileName(num);
|
||||
|
||||
unique_ptr<WritableFile> writable_file;
|
||||
std::unique_ptr<WritableFile> writable_file;
|
||||
ASSERT_OK(env->NewWritableFile(fname, &writable_file, env_options));
|
||||
CuckooTableBuilder builder(
|
||||
writable_file.get(), hash_ratio,
|
||||
kMaxNumHashTable, 1000, GetSliceMurmurHash);
|
||||
64, 1000, test::Uint64Comparator(), 5, nullptr);
|
||||
ASSERT_OK(builder.status());
|
||||
for (uint64_t key_idx = 0; key_idx < num; ++key_idx) {
|
||||
// Value is just a part of key.
|
||||
std::string new_key(reinterpret_cast<char*>(&key_idx), sizeof(key_idx));
|
||||
new_key = std::string(key_length - new_key.size(), 'k') + new_key;
|
||||
ParsedInternalKey ikey(new_key, 0, kTypeValue);
|
||||
std::string full_key;
|
||||
AppendInternalKey(&full_key, ikey);
|
||||
builder.Add(Slice(full_key), Slice(&full_key[0], value_length));
|
||||
builder.Add(Slice(keys[key_idx]), Slice(&keys[key_idx][0], 4));
|
||||
ASSERT_EQ(builder.NumEntries(), key_idx + 1);
|
||||
ASSERT_OK(builder.status());
|
||||
keys.push_back(full_key);
|
||||
}
|
||||
ASSERT_OK(builder.Finish());
|
||||
ASSERT_EQ(num, builder.NumEntries());
|
||||
file_size = builder.FileSize();
|
||||
ASSERT_OK(writable_file->Close());
|
||||
unique_ptr<RandomAccessFile> read_file;
|
||||
|
||||
uint64_t file_size;
|
||||
env->GetFileSize(fname, &file_size);
|
||||
std::unique_ptr<RandomAccessFile> read_file;
|
||||
ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
|
||||
|
||||
CuckooTableReader reader(
|
||||
options,
|
||||
std::move(read_file),
|
||||
file_size,
|
||||
GetSliceMurmurHash);
|
||||
options, std::move(read_file), file_size,
|
||||
test::Uint64Comparator(), nullptr);
|
||||
ASSERT_OK(reader.status());
|
||||
ReadOptions r_options;
|
||||
for (uint64_t i = 0; i < num; ++i) {
|
||||
int cnt = 0;
|
||||
ASSERT_OK(reader.Get(r_options, Slice(keys[i]), &cnt, CheckValue, nullptr));
|
||||
if (cnt != 1) {
|
||||
fprintf(stderr, "%" PRIu64 " not found.\n", i);
|
||||
ASSERT_EQ(1, cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadKeys(uint64_t num, uint32_t batch_size) {
|
||||
Options options;
|
||||
options.allow_mmap_reads = true;
|
||||
Env* env = options.env;
|
||||
EnvOptions env_options = EnvOptions(options);
|
||||
std::string fname = GetFileName(num);
|
||||
|
||||
uint64_t file_size;
|
||||
env->GetFileSize(fname, &file_size);
|
||||
std::unique_ptr<RandomAccessFile> read_file;
|
||||
ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
|
||||
|
||||
CuckooTableReader reader(
|
||||
options, std::move(read_file), file_size, test::Uint64Comparator(),
|
||||
nullptr);
|
||||
ASSERT_OK(reader.status());
|
||||
const UserCollectedProperties user_props =
|
||||
reader.GetTableProperties()->user_collected_properties;
|
||||
const uint32_t num_hash_fun = *reinterpret_cast<const uint32_t*>(
|
||||
user_props.at(CuckooTablePropertyNames::kNumHashTable).data());
|
||||
fprintf(stderr, "With %" PRIu64 " items and hash table ratio %f, number of"
|
||||
" hash functions used: %u.\n", num, hash_ratio, num_hash_fun);
|
||||
user_props.at(CuckooTablePropertyNames::kNumHashFunc).data());
|
||||
const uint64_t table_size = *reinterpret_cast<const uint64_t*>(
|
||||
user_props.at(CuckooTablePropertyNames::kHashTableSize).data());
|
||||
fprintf(stderr, "With %" PRIu64 " items, utilization is %.2f%%, number of"
|
||||
" hash functions: %u.\n", num, num * 100.0 / (table_size), num_hash_fun);
|
||||
ReadOptions r_options;
|
||||
for (auto& key : keys) {
|
||||
int cnt = 0;
|
||||
ASSERT_OK(reader.Get(r_options, Slice(key), &cnt, CheckValue, nullptr));
|
||||
ASSERT_EQ(1, cnt);
|
||||
}
|
||||
// Shuffle Keys.
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
|
||||
uint64_t time_now = env->NowMicros();
|
||||
reader.NewIterator(ReadOptions(), nullptr);
|
||||
fprintf(stderr, "Time taken for preparing iterator for %" PRIu64 " items: %" PRIu64 " ms.\n",
|
||||
num, (env->NowMicros() - time_now)/1000);
|
||||
time_now = env->NowMicros();
|
||||
for (uint64_t i = 0; i < num_reads; ++i) {
|
||||
reader.Get(r_options, Slice(keys[i % num]), nullptr, DoNothing, nullptr);
|
||||
uint64_t start_time = env->NowMicros();
|
||||
if (batch_size > 0) {
|
||||
for (uint64_t i = 0; i < num; i += batch_size) {
|
||||
for (uint64_t j = i; j < i+batch_size && j < num; ++j) {
|
||||
reader.Prepare(Slice(reinterpret_cast<char*>(&j), 16));
|
||||
}
|
||||
for (uint64_t j = i; j < i+batch_size && j < num; ++j) {
|
||||
reader.Get(r_options, Slice(reinterpret_cast<char*>(&j), 16),
|
||||
nullptr, DoNothing, nullptr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint64_t i = 0; i < num; i++) {
|
||||
reader.Get(r_options, Slice(reinterpret_cast<char*>(&i), 16), nullptr,
|
||||
DoNothing, nullptr);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Time taken per op is %.3fus\n",
|
||||
(env->NowMicros() - time_now)*1.0/num_reads);
|
||||
float time_per_op = (env->NowMicros() - start_time) * 1.0 / num;
|
||||
fprintf(stderr,
|
||||
"Time taken per op is %.3fus (%.1f Mqps) with batch size of %u\n",
|
||||
time_per_op, 1.0 / time_per_op, batch_size);
|
||||
}
|
||||
} // namespace.
|
||||
|
||||
TEST(CuckooReaderTest, Performance) {
|
||||
// In all these tests, num_reads = 10*num_items.
|
||||
TEST(CuckooReaderTest, TestReadPerformance) {
|
||||
if (!FLAGS_enable_perf) {
|
||||
return;
|
||||
}
|
||||
BM_CuckooRead(100000, 8, 4, 1000000, 0.9);
|
||||
BM_CuckooRead(1000000, 8, 4, 10000000, 0.9);
|
||||
BM_CuckooRead(1000000, 8, 4, 10000000, 0.7);
|
||||
BM_CuckooRead(10000000, 8, 4, 100000000, 0.9);
|
||||
BM_CuckooRead(10000000, 8, 4, 100000000, 0.7);
|
||||
double hash_ratio = 0.95;
|
||||
// These numbers are chosen to have a hash utilizaiton % close to
|
||||
// 0.9, 0.75, 0.6 and 0.5 respectively.
|
||||
// They all create 128 M buckets.
|
||||
std::vector<uint64_t> nums = {120*1000*1000, 100*1000*1000, 80*1000*1000,
|
||||
70*1000*1000};
|
||||
#ifndef NDEBUG
|
||||
fprintf(stdout,
|
||||
"WARNING: Not compiled with DNDEBUG. Performance tests may be slow.\n");
|
||||
#endif
|
||||
std::vector<std::string> keys;
|
||||
GetKeys(*std::max_element(nums.begin(), nums.end()), &keys);
|
||||
for (uint64_t num : nums) {
|
||||
if (FLAGS_write || !Env::Default()->FileExists(GetFileName(num))) {
|
||||
WriteFile(keys, num, hash_ratio);
|
||||
}
|
||||
ReadKeys(num, 0);
|
||||
ReadKeys(num, 10);
|
||||
ReadKeys(num, 25);
|
||||
ReadKeys(num, 50);
|
||||
ReadKeys(num, 100);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+11
-15
@@ -22,10 +22,11 @@ static const size_t kFilterBaseLg = 11;
|
||||
static const size_t kFilterBase = 1 << kFilterBaseLg;
|
||||
|
||||
FilterBlockBuilder::FilterBlockBuilder(const Options& opt,
|
||||
const BlockBasedTableOptions& table_opt,
|
||||
const Comparator* internal_comparator)
|
||||
: policy_(opt.filter_policy),
|
||||
: policy_(table_opt.filter_policy.get()),
|
||||
prefix_extractor_(opt.prefix_extractor.get()),
|
||||
whole_key_filtering_(opt.whole_key_filtering),
|
||||
whole_key_filtering_(table_opt.whole_key_filtering),
|
||||
comparator_(internal_comparator) {}
|
||||
|
||||
void FilterBlockBuilder::StartBlock(uint64_t block_offset) {
|
||||
@@ -70,20 +71,14 @@ void FilterBlockBuilder::AddKey(const Slice& key) {
|
||||
}
|
||||
|
||||
// add prefix to filter if needed
|
||||
if (prefix_extractor_ && prefix_extractor_->InDomain(ExtractUserKey(key))) {
|
||||
// If prefix_extractor_, this filter_block layer assumes we only
|
||||
// operate on internal keys.
|
||||
Slice user_key = ExtractUserKey(key);
|
||||
if (prefix_extractor_ && prefix_extractor_->InDomain(key)) {
|
||||
// this assumes prefix(prefix(key)) == prefix(key), as the last
|
||||
// entry in entries_ may be either a key or prefix, and we use
|
||||
// prefix(last entry) to get the prefix of the last key.
|
||||
if (prev.size() == 0 ||
|
||||
!SamePrefix(user_key, ExtractUserKey(prev))) {
|
||||
Slice prefix = prefix_extractor_->Transform(user_key);
|
||||
InternalKey internal_prefix_tmp(prefix, 0, kTypeValue);
|
||||
Slice internal_prefix = internal_prefix_tmp.Encode();
|
||||
if (prev.size() == 0 || !SamePrefix(key, prev)) {
|
||||
Slice prefix = prefix_extractor_->Transform(key);
|
||||
start_.push_back(entries_.size());
|
||||
entries_.append(internal_prefix.data(), internal_prefix.size());
|
||||
entries_.append(prefix.data(), prefix.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,10 +126,11 @@ void FilterBlockBuilder::GenerateFilter() {
|
||||
}
|
||||
|
||||
FilterBlockReader::FilterBlockReader(
|
||||
const Options& opt, const Slice& contents, bool delete_contents_after_use)
|
||||
: policy_(opt.filter_policy),
|
||||
const Options& opt, const BlockBasedTableOptions& table_opt,
|
||||
const Slice& contents, bool delete_contents_after_use)
|
||||
: policy_(table_opt.filter_policy.get()),
|
||||
prefix_extractor_(opt.prefix_extractor.get()),
|
||||
whole_key_filtering_(opt.whole_key_filtering),
|
||||
whole_key_filtering_(table_opt.whole_key_filtering),
|
||||
data_(nullptr),
|
||||
offset_(nullptr),
|
||||
num_(0),
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "util/hash.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -36,6 +37,7 @@ class FilterPolicy;
|
||||
class FilterBlockBuilder {
|
||||
public:
|
||||
explicit FilterBlockBuilder(const Options& opt,
|
||||
const BlockBasedTableOptions& table_opt,
|
||||
const Comparator* internal_comparator);
|
||||
|
||||
void StartBlock(uint64_t block_offset);
|
||||
@@ -70,6 +72,7 @@ class FilterBlockReader {
|
||||
// REQUIRES: "contents" and *policy must stay live while *this is live.
|
||||
FilterBlockReader(
|
||||
const Options& opt,
|
||||
const BlockBasedTableOptions& table_opt,
|
||||
const Slice& contents,
|
||||
bool delete_contents_after_use = false);
|
||||
bool KeyMayMatch(uint64_t block_offset, const Slice& key);
|
||||
|
||||
@@ -45,26 +45,26 @@ class TestHashFilter : public FilterPolicy {
|
||||
|
||||
class FilterBlockTest {
|
||||
public:
|
||||
TestHashFilter policy_;
|
||||
Options options_;
|
||||
BlockBasedTableOptions table_options_;
|
||||
|
||||
FilterBlockTest() {
|
||||
options_ = Options();
|
||||
options_.filter_policy = &policy_;
|
||||
table_options_.filter_policy.reset(new TestHashFilter());
|
||||
}
|
||||
};
|
||||
|
||||
TEST(FilterBlockTest, EmptyBuilder) {
|
||||
FilterBlockBuilder builder(options_, options_.comparator);
|
||||
FilterBlockBuilder builder(options_, table_options_, options_.comparator);
|
||||
Slice block = builder.Finish();
|
||||
ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block));
|
||||
FilterBlockReader reader(options_, block);
|
||||
FilterBlockReader reader(options_, table_options_, block);
|
||||
ASSERT_TRUE(reader.KeyMayMatch(0, "foo"));
|
||||
ASSERT_TRUE(reader.KeyMayMatch(100000, "foo"));
|
||||
}
|
||||
|
||||
TEST(FilterBlockTest, SingleChunk) {
|
||||
FilterBlockBuilder builder(options_, options_.comparator);
|
||||
FilterBlockBuilder builder(options_, table_options_, options_.comparator);
|
||||
builder.StartBlock(100);
|
||||
builder.AddKey("foo");
|
||||
builder.AddKey("bar");
|
||||
@@ -74,7 +74,7 @@ TEST(FilterBlockTest, SingleChunk) {
|
||||
builder.StartBlock(300);
|
||||
builder.AddKey("hello");
|
||||
Slice block = builder.Finish();
|
||||
FilterBlockReader reader(options_, block);
|
||||
FilterBlockReader reader(options_, table_options_, block);
|
||||
ASSERT_TRUE(reader.KeyMayMatch(100, "foo"));
|
||||
ASSERT_TRUE(reader.KeyMayMatch(100, "bar"));
|
||||
ASSERT_TRUE(reader.KeyMayMatch(100, "box"));
|
||||
@@ -85,7 +85,7 @@ TEST(FilterBlockTest, SingleChunk) {
|
||||
}
|
||||
|
||||
TEST(FilterBlockTest, MultiChunk) {
|
||||
FilterBlockBuilder builder(options_, options_.comparator);
|
||||
FilterBlockBuilder builder(options_, table_options_, options_.comparator);
|
||||
|
||||
// First filter
|
||||
builder.StartBlock(0);
|
||||
@@ -105,7 +105,7 @@ TEST(FilterBlockTest, MultiChunk) {
|
||||
builder.AddKey("hello");
|
||||
|
||||
Slice block = builder.Finish();
|
||||
FilterBlockReader reader(options_, block);
|
||||
FilterBlockReader reader(options_, table_options_, block);
|
||||
|
||||
// Check first filter
|
||||
ASSERT_TRUE(reader.KeyMayMatch(0, "foo"));
|
||||
|
||||
@@ -62,9 +62,11 @@ class FlushBlockBySizePolicy : public FlushBlockPolicy {
|
||||
};
|
||||
|
||||
FlushBlockPolicy* FlushBlockBySizePolicyFactory::NewFlushBlockPolicy(
|
||||
const Options& options, const BlockBuilder& data_block_builder) const {
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const BlockBuilder& data_block_builder) const {
|
||||
return new FlushBlockBySizePolicy(
|
||||
options.block_size, options.block_size_deviation, data_block_builder);
|
||||
table_options.block_size, table_options.block_size_deviation,
|
||||
data_block_builder);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+8
-6
@@ -211,10 +211,13 @@ Status ReadBlock(RandomAccessFile* file, const Footer& footer,
|
||||
const ReadOptions& options, const BlockHandle& handle,
|
||||
Slice* contents, /* result of reading */ char* buf) {
|
||||
size_t n = static_cast<size_t>(handle.size());
|
||||
Status s;
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(block_read_time);
|
||||
s = file->Read(handle.offset(), n + kBlockTrailerSize, contents, buf);
|
||||
}
|
||||
|
||||
PERF_TIMER_AUTO(block_read_time);
|
||||
Status s = file->Read(handle.offset(), n + kBlockTrailerSize, contents, buf);
|
||||
PERF_TIMER_MEASURE(block_read_time);
|
||||
PERF_COUNTER_ADD(block_read_count, 1);
|
||||
PERF_COUNTER_ADD(block_read_byte, n + kBlockTrailerSize);
|
||||
|
||||
@@ -228,6 +231,7 @@ Status ReadBlock(RandomAccessFile* file, const Footer& footer,
|
||||
// Check the crc of the type and the block contents
|
||||
const char* data = contents->data(); // Pointer to where Read put the data
|
||||
if (options.verify_checksums) {
|
||||
PERF_TIMER_GUARD(block_checksum_time);
|
||||
uint32_t value = DecodeFixed32(data + n + 1);
|
||||
uint32_t actual = 0;
|
||||
switch (footer.checksum()) {
|
||||
@@ -247,7 +251,6 @@ Status ReadBlock(RandomAccessFile* file, const Footer& footer,
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
PERF_TIMER_STOP(block_checksum_time);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -265,7 +268,7 @@ Status DecompressBlock(BlockContents* result, size_t block_size,
|
||||
result->cachable = false;
|
||||
result->heap_allocated = false;
|
||||
|
||||
PERF_TIMER_AUTO(block_decompress_time);
|
||||
PERF_TIMER_GUARD(block_decompress_time);
|
||||
rocksdb::CompressionType compression_type =
|
||||
static_cast<rocksdb::CompressionType>(data[n]);
|
||||
// If the caller has requested that the block not be uncompressed
|
||||
@@ -295,7 +298,6 @@ Status DecompressBlock(BlockContents* result, size_t block_size,
|
||||
} else {
|
||||
s = UncompressBlockContents(data, n, result);
|
||||
}
|
||||
PERF_TIMER_STOP(block_decompress_time);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+7
-10
@@ -116,12 +116,12 @@ class MergingIterator : public Iterator {
|
||||
// Invalidate the heap.
|
||||
use_heap_ = false;
|
||||
IteratorWrapper* first_child = nullptr;
|
||||
PERF_TIMER_DECLARE();
|
||||
|
||||
for (auto& child : children_) {
|
||||
PERF_TIMER_START(seek_child_seek_time);
|
||||
child.Seek(target);
|
||||
PERF_TIMER_STOP(seek_child_seek_time);
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_child_seek_time);
|
||||
child.Seek(target);
|
||||
}
|
||||
PERF_COUNTER_ADD(seek_child_seek_count, 1);
|
||||
|
||||
if (child.Valid()) {
|
||||
@@ -134,24 +134,21 @@ class MergingIterator : public Iterator {
|
||||
} else {
|
||||
// We have more than one children with valid keys. Initialize
|
||||
// the heap and put the first child into the heap.
|
||||
PERF_TIMER_START(seek_min_heap_time);
|
||||
PERF_TIMER_GUARD(seek_min_heap_time);
|
||||
ClearHeaps();
|
||||
minHeap_.push(first_child);
|
||||
PERF_TIMER_STOP(seek_min_heap_time);
|
||||
}
|
||||
}
|
||||
if (use_heap_) {
|
||||
PERF_TIMER_START(seek_min_heap_time);
|
||||
PERF_TIMER_GUARD(seek_min_heap_time);
|
||||
minHeap_.push(&child);
|
||||
PERF_TIMER_STOP(seek_min_heap_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (use_heap_) {
|
||||
// If heap is valid, need to put the smallest key to curent_.
|
||||
PERF_TIMER_START(seek_min_heap_time);
|
||||
PERF_TIMER_GUARD(seek_min_heap_time);
|
||||
FindSmallest();
|
||||
PERF_TIMER_STOP(seek_min_heap_time);
|
||||
} else {
|
||||
// The heap is not valid, then the current_ iterator is the first
|
||||
// one, or null if there is no first child.
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
namespace rocksdb {
|
||||
|
||||
MetaIndexBuilder::MetaIndexBuilder()
|
||||
: meta_index_block_(
|
||||
new BlockBuilder(1 /* restart interval */, BytewiseComparator())) {
|
||||
}
|
||||
: meta_index_block_(new BlockBuilder(1 /* restart interval */)) {}
|
||||
|
||||
void MetaIndexBuilder::Add(const std::string& key,
|
||||
const BlockHandle& handle) {
|
||||
@@ -35,9 +33,7 @@ Slice MetaIndexBuilder::Finish() {
|
||||
}
|
||||
|
||||
PropertyBlockBuilder::PropertyBlockBuilder()
|
||||
: properties_block_(
|
||||
new BlockBuilder(1 /* restart interval */, BytewiseComparator())) {
|
||||
}
|
||||
: properties_block_(new BlockBuilder(1 /* restart interval */)) {}
|
||||
|
||||
void PropertyBlockBuilder::Add(const std::string& name,
|
||||
const std::string& val) {
|
||||
@@ -253,8 +249,7 @@ Status ReadTableProperties(RandomAccessFile* file, uint64_t file_size,
|
||||
s = ReadProperties(meta_iter->value(), file, footer, env, info_log,
|
||||
properties);
|
||||
} else {
|
||||
s = Status::Corruption("Unable to read the property block.");
|
||||
Log(WARN_LEVEL, info_log, "Cannot find Properties block from file.");
|
||||
s = Status::NotFound();
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
@@ -35,6 +35,39 @@ TableBuilder* PlainTableFactory::NewTableBuilder(
|
||||
store_index_in_file_);
|
||||
}
|
||||
|
||||
std::string PlainTableFactory::GetPrintableTableOptions() const {
|
||||
std::string ret;
|
||||
ret.reserve(20000);
|
||||
const int kBufferSize = 200;
|
||||
char buffer[kBufferSize];
|
||||
|
||||
snprintf(buffer, kBufferSize, " user_key_len: %u\n",
|
||||
user_key_len_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " bloom_bits_per_key: %d\n",
|
||||
bloom_bits_per_key_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " hash_table_ratio: %lf\n",
|
||||
hash_table_ratio_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " index_sparseness: %zd\n",
|
||||
index_sparseness_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " huge_page_tlb_size: %zd\n",
|
||||
huge_page_tlb_size_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " encoding_type: %d\n",
|
||||
encoding_type_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " full_scan_mode: %d\n",
|
||||
full_scan_mode_);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " store_index_in_file: %d\n",
|
||||
store_index_in_file_);
|
||||
ret.append(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern TableFactory* NewPlainTableFactory(const PlainTableOptions& options) {
|
||||
return new PlainTableFactory(options);
|
||||
}
|
||||
|
||||
@@ -164,8 +164,19 @@ class PlainTableFactory : public TableFactory {
|
||||
CompressionType compression_type) const
|
||||
override;
|
||||
|
||||
std::string GetPrintableTableOptions() const override;
|
||||
|
||||
static const char kValueTypeSeqId0 = 0xFF;
|
||||
|
||||
// Sanitizes the specified DB Options.
|
||||
Status SanitizeDBOptions(const DBOptions* db_opts) const override {
|
||||
if (db_opts->allow_mmap_reads == false) {
|
||||
return Status::NotSupported(
|
||||
"PlainTable with allow_mmap_reads == false is not supported.");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t user_key_len_;
|
||||
int bloom_bits_per_key_;
|
||||
|
||||
@@ -187,6 +187,10 @@ void PlainTableReader::SetupForCompaction() {
|
||||
|
||||
Iterator* PlainTableReader::NewIterator(const ReadOptions& options,
|
||||
Arena* arena) {
|
||||
if (options.total_order_seek && !IsTotalOrderMode()) {
|
||||
return NewErrorIterator(
|
||||
Status::InvalidArgument("total_order_seek not supported"), arena);
|
||||
}
|
||||
if (arena == nullptr) {
|
||||
return new PlainTableIterator(this, prefix_extractor_ != nullptr);
|
||||
} else {
|
||||
|
||||
+27
-14
@@ -232,7 +232,9 @@ DEFINE_bool(iterator, false, "For test iterator");
|
||||
DEFINE_bool(through_db, false, "If enable, a DB instance will be created and "
|
||||
"the query will be against DB. Otherwise, will be directly against "
|
||||
"a table reader.");
|
||||
DEFINE_bool(plain_table, false, "Use PlainTable");
|
||||
DEFINE_string(table_factory, "block_based",
|
||||
"Table factory to use: `block_based` (default), `plain_table` or "
|
||||
"`cuckoo_hash`.");
|
||||
DEFINE_string(time_unit, "microsecond",
|
||||
"The time unit used for measuring performance. User can specify "
|
||||
"`microsecond` (default) or `nanosecond`");
|
||||
@@ -242,7 +244,7 @@ int main(int argc, char** argv) {
|
||||
" [OPTIONS]...");
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
rocksdb::TableFactory* tf = new rocksdb::BlockBasedTableFactory();
|
||||
std::shared_ptr<rocksdb::TableFactory> tf;
|
||||
rocksdb::Options options;
|
||||
if (FLAGS_prefix_len < 16) {
|
||||
options.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(
|
||||
@@ -253,7 +255,12 @@ int main(int argc, char** argv) {
|
||||
options.create_if_missing = true;
|
||||
options.compression = rocksdb::CompressionType::kNoCompression;
|
||||
|
||||
if (FLAGS_plain_table) {
|
||||
if (FLAGS_table_factory == "cuckoo_hash") {
|
||||
options.allow_mmap_reads = true;
|
||||
env_options.use_mmap_reads = true;
|
||||
|
||||
tf.reset(rocksdb::NewCuckooTableFactory(0.75));
|
||||
} else if (FLAGS_table_factory == "plain_table") {
|
||||
options.allow_mmap_reads = true;
|
||||
env_options.use_mmap_reads = true;
|
||||
|
||||
@@ -262,22 +269,28 @@ int main(int argc, char** argv) {
|
||||
plain_table_options.bloom_bits_per_key = (FLAGS_prefix_len == 16) ? 0 : 8;
|
||||
plain_table_options.hash_table_ratio = 0.75;
|
||||
|
||||
tf = new rocksdb::PlainTableFactory(plain_table_options);
|
||||
tf.reset(new rocksdb::PlainTableFactory(plain_table_options));
|
||||
options.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(
|
||||
FLAGS_prefix_len));
|
||||
} else if (FLAGS_table_factory == "block_based") {
|
||||
tf.reset(new rocksdb::BlockBasedTableFactory());
|
||||
} else {
|
||||
tf = new rocksdb::BlockBasedTableFactory();
|
||||
fprintf(stderr, "Invalid table type %s\n", FLAGS_table_factory.c_str());
|
||||
}
|
||||
|
||||
if (tf) {
|
||||
// if user provides invalid options, just fall back to microsecond.
|
||||
bool measured_by_nanosecond = FLAGS_time_unit == "nanosecond";
|
||||
|
||||
options.table_factory = tf;
|
||||
rocksdb::TableReaderBenchmark(options, env_options, ro, FLAGS_num_keys1,
|
||||
FLAGS_num_keys2, FLAGS_iter, FLAGS_prefix_len,
|
||||
FLAGS_query_empty, FLAGS_iterator,
|
||||
FLAGS_through_db, measured_by_nanosecond);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
// if user provides invalid options, just fall back to microsecond.
|
||||
bool measured_by_nanosecond = FLAGS_time_unit == "nanosecond";
|
||||
|
||||
options.table_factory =
|
||||
std::shared_ptr<rocksdb::TableFactory>(tf);
|
||||
rocksdb::TableReaderBenchmark(options, env_options, ro, FLAGS_num_keys1,
|
||||
FLAGS_num_keys2, FLAGS_iter, FLAGS_prefix_len,
|
||||
FLAGS_query_empty, FLAGS_iterator,
|
||||
FLAGS_through_db, measured_by_nanosecond);
|
||||
delete tf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+175
-63
@@ -194,6 +194,7 @@ class Constructor {
|
||||
// been added so far. Returns the keys in sorted order in "*keys"
|
||||
// and stores the key/value pairs in "*kvmap"
|
||||
void Finish(const Options& options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
std::vector<std::string>* keys, KVMap* kvmap) {
|
||||
last_internal_key_ = &internal_comparator;
|
||||
@@ -205,12 +206,13 @@ class Constructor {
|
||||
keys->push_back(it->first);
|
||||
}
|
||||
data_.clear();
|
||||
Status s = FinishImpl(options, internal_comparator, *kvmap);
|
||||
Status s = FinishImpl(options, table_options, internal_comparator, *kvmap);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
}
|
||||
|
||||
// Construct the data structure from the data in "data"
|
||||
virtual Status FinishImpl(const Options& options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const KVMap& data) = 0;
|
||||
|
||||
@@ -237,11 +239,12 @@ class BlockConstructor: public Constructor {
|
||||
delete block_;
|
||||
}
|
||||
virtual Status FinishImpl(const Options& options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const KVMap& data) {
|
||||
delete block_;
|
||||
block_ = nullptr;
|
||||
BlockBuilder builder(options, &internal_comparator);
|
||||
BlockBuilder builder(table_options.block_restart_interval);
|
||||
|
||||
for (KVMap::const_iterator it = data.begin();
|
||||
it != data.end();
|
||||
@@ -319,6 +322,7 @@ class TableConstructor: public Constructor {
|
||||
~TableConstructor() { Reset(); }
|
||||
|
||||
virtual Status FinishImpl(const Options& options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const KVMap& data) {
|
||||
Reset();
|
||||
@@ -377,7 +381,7 @@ class TableConstructor: public Constructor {
|
||||
sink_->contents().size(), &table_reader_);
|
||||
}
|
||||
|
||||
virtual TableReader* table_reader() {
|
||||
virtual TableReader* GetTableReader() {
|
||||
return table_reader_.get();
|
||||
}
|
||||
|
||||
@@ -417,6 +421,7 @@ class MemTableConstructor: public Constructor {
|
||||
delete memtable_->Unref();
|
||||
}
|
||||
virtual Status FinishImpl(const Options& options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const KVMap& data) {
|
||||
delete memtable_->Unref();
|
||||
@@ -455,6 +460,7 @@ class DBConstructor: public Constructor {
|
||||
delete db_;
|
||||
}
|
||||
virtual Status FinishImpl(const Options& options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const KVMap& data) {
|
||||
delete db_;
|
||||
@@ -670,12 +676,9 @@ class Harness {
|
||||
delete constructor_;
|
||||
constructor_ = nullptr;
|
||||
options_ = Options();
|
||||
|
||||
options_.block_restart_interval = args.restart_interval;
|
||||
options_.compression = args.compression;
|
||||
// Use shorter block size for tests to exercise block boundary
|
||||
// conditions more.
|
||||
options_.block_size = 256;
|
||||
if (args.reverse_compare) {
|
||||
options_.comparator = &reverse_key_comparator;
|
||||
}
|
||||
@@ -685,12 +688,14 @@ class Harness {
|
||||
|
||||
support_prev_ = true;
|
||||
only_support_prefix_seek_ = false;
|
||||
BlockBasedTableOptions table_options;
|
||||
switch (args.type) {
|
||||
case BLOCK_BASED_TABLE_TEST:
|
||||
table_options.flush_block_policy_factory.reset(
|
||||
table_options_.flush_block_policy_factory.reset(
|
||||
new FlushBlockBySizePolicyFactory());
|
||||
options_.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
table_options_.block_size = 256;
|
||||
table_options_.block_restart_interval = args.restart_interval;
|
||||
options_.table_factory.reset(
|
||||
new BlockBasedTableFactory(table_options_));
|
||||
constructor_ = new TableConstructor(options_.comparator);
|
||||
break;
|
||||
case PLAIN_TABLE_SEMI_FIXED_PREFIX:
|
||||
@@ -733,12 +738,21 @@ class Harness {
|
||||
new InternalKeyComparator(options_.comparator));
|
||||
break;
|
||||
case BLOCK_TEST:
|
||||
table_options_.block_size = 256;
|
||||
options_.table_factory.reset(
|
||||
new BlockBasedTableFactory(table_options_));
|
||||
constructor_ = new BlockConstructor(options_.comparator);
|
||||
break;
|
||||
case MEMTABLE_TEST:
|
||||
table_options_.block_size = 256;
|
||||
options_.table_factory.reset(
|
||||
new BlockBasedTableFactory(table_options_));
|
||||
constructor_ = new MemTableConstructor(options_.comparator);
|
||||
break;
|
||||
case DB_TEST:
|
||||
table_options_.block_size = 256;
|
||||
options_.table_factory.reset(
|
||||
new BlockBasedTableFactory(table_options_));
|
||||
constructor_ = new DBConstructor(options_.comparator);
|
||||
break;
|
||||
}
|
||||
@@ -755,7 +769,8 @@ class Harness {
|
||||
void Test(Random* rnd) {
|
||||
std::vector<std::string> keys;
|
||||
KVMap data;
|
||||
constructor_->Finish(options_, *internal_comparator_, &keys, &data);
|
||||
constructor_->Finish(options_, table_options_, *internal_comparator_,
|
||||
&keys, &data);
|
||||
|
||||
TestForwardScan(keys, data);
|
||||
if (support_prev_) {
|
||||
@@ -924,6 +939,7 @@ class Harness {
|
||||
|
||||
private:
|
||||
Options options_ = Options();
|
||||
BlockBasedTableOptions table_options_ = BlockBasedTableOptions();
|
||||
Constructor* constructor_;
|
||||
bool support_prev_;
|
||||
bool only_support_prefix_seek_;
|
||||
@@ -1018,12 +1034,14 @@ TEST(BlockBasedTableTest, BasicBlockBasedTableProperties) {
|
||||
KVMap kvmap;
|
||||
Options options;
|
||||
options.compression = kNoCompression;
|
||||
options.block_restart_interval = 1;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_restart_interval = 1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
c.Finish(options, GetPlainInternalComparator(options.comparator), &keys,
|
||||
&kvmap);
|
||||
c.Finish(options, table_options,
|
||||
GetPlainInternalComparator(options.comparator), &keys, &kvmap);
|
||||
|
||||
auto& props = *c.table_reader()->GetTableProperties();
|
||||
auto& props = *c.GetTableReader()->GetTableProperties();
|
||||
ASSERT_EQ(kvmap.size(), props.num_entries);
|
||||
|
||||
auto raw_key_size = kvmap.size() * 2ul;
|
||||
@@ -1035,7 +1053,7 @@ TEST(BlockBasedTableTest, BasicBlockBasedTableProperties) {
|
||||
ASSERT_EQ("", props.filter_policy_name); // no filter policy is used
|
||||
|
||||
// Verify data size.
|
||||
BlockBuilder block_builder(options, options.comparator);
|
||||
BlockBuilder block_builder(1);
|
||||
for (const auto& item : kvmap) {
|
||||
block_builder.Add(item.first, item.second);
|
||||
}
|
||||
@@ -1044,20 +1062,104 @@ TEST(BlockBasedTableTest, BasicBlockBasedTableProperties) {
|
||||
}
|
||||
|
||||
TEST(BlockBasedTableTest, FilterPolicyNameProperties) {
|
||||
TableConstructor c(BytewiseComparator());
|
||||
TableConstructor c(BytewiseComparator(), true);
|
||||
c.Add("a1", "val1");
|
||||
std::vector<std::string> keys;
|
||||
KVMap kvmap;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
Options options;
|
||||
std::unique_ptr<const FilterPolicy> filter_policy(NewBloomFilterPolicy(10));
|
||||
options.filter_policy = filter_policy.get();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
c.Finish(options, GetPlainInternalComparator(options.comparator), &keys,
|
||||
&kvmap);
|
||||
auto& props = *c.table_reader()->GetTableProperties();
|
||||
c.Finish(options, table_options,
|
||||
GetPlainInternalComparator(options.comparator), &keys, &kvmap);
|
||||
auto& props = *c.GetTableReader()->GetTableProperties();
|
||||
ASSERT_EQ("rocksdb.BuiltinBloomFilter", props.filter_policy_name);
|
||||
}
|
||||
|
||||
TEST(BlockBasedTableTest, TotalOrderSeekOnHashIndex) {
|
||||
BlockBasedTableOptions table_options;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Options options;
|
||||
// Make each key/value an individual block
|
||||
table_options.block_size = 64;
|
||||
switch (i) {
|
||||
case 0:
|
||||
// Binary search index
|
||||
table_options.index_type = BlockBasedTableOptions::kBinarySearch;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
break;
|
||||
case 1:
|
||||
// Hash search index
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(4));
|
||||
break;
|
||||
case 2:
|
||||
// Hash search index with hash_index_allow_collision
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
table_options.hash_index_allow_collision = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(4));
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
// Hash search index with filter policy
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(4));
|
||||
break;
|
||||
}
|
||||
|
||||
TableConstructor c(BytewiseComparator(), true);
|
||||
c.Add("aaaa1", std::string('a', 56));
|
||||
c.Add("bbaa1", std::string('a', 56));
|
||||
c.Add("cccc1", std::string('a', 56));
|
||||
c.Add("bbbb1", std::string('a', 56));
|
||||
c.Add("baaa1", std::string('a', 56));
|
||||
c.Add("abbb1", std::string('a', 56));
|
||||
c.Add("cccc2", std::string('a', 56));
|
||||
std::vector<std::string> keys;
|
||||
KVMap kvmap;
|
||||
c.Finish(options, table_options,
|
||||
GetPlainInternalComparator(options.comparator), &keys, &kvmap);
|
||||
auto props = c.GetTableReader()->GetTableProperties();
|
||||
ASSERT_EQ(7u, props->num_data_blocks);
|
||||
auto* reader = c.GetTableReader();
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
std::unique_ptr<Iterator> iter(reader->NewIterator(ro));
|
||||
|
||||
iter->Seek(InternalKey("b", 0, kTypeValue).Encode());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("baaa1", ExtractUserKey(iter->key()).ToString());
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("bbaa1", ExtractUserKey(iter->key()).ToString());
|
||||
|
||||
iter->Seek(InternalKey("bb", 0, kTypeValue).Encode());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("bbaa1", ExtractUserKey(iter->key()).ToString());
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("bbbb1", ExtractUserKey(iter->key()).ToString());
|
||||
|
||||
iter->Seek(InternalKey("bbb", 0, kTypeValue).Encode());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("bbbb1", ExtractUserKey(iter->key()).ToString());
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("cccc1", ExtractUserKey(iter->key()).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
static std::string RandomString(Random* rnd, int len) {
|
||||
std::string r;
|
||||
test::RandomString(rnd, len, &r);
|
||||
@@ -1094,21 +1196,20 @@ TEST(TableTest, HashIndexTest) {
|
||||
std::vector<std::string> keys;
|
||||
KVMap kvmap;
|
||||
Options options;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
table_options.hash_index_allow_collision = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
options.block_cache = NewLRUCache(1024);
|
||||
options.block_size = 1700;
|
||||
table_options.block_size = 1700;
|
||||
table_options.block_cache = NewLRUCache(1024);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
std::unique_ptr<InternalKeyComparator> comparator(
|
||||
new InternalKeyComparator(BytewiseComparator()));
|
||||
c.Finish(options, *comparator, &keys, &kvmap);
|
||||
auto reader = c.table_reader();
|
||||
c.Finish(options, table_options, *comparator, &keys, &kvmap);
|
||||
auto reader = c.GetTableReader();
|
||||
|
||||
auto props = c.table_reader()->GetTableProperties();
|
||||
auto props = reader->GetTableProperties();
|
||||
ASSERT_EQ(5u, props->num_data_blocks);
|
||||
|
||||
std::unique_ptr<Iterator> hash_iter(reader->NewIterator(ReadOptions()));
|
||||
@@ -1209,11 +1310,13 @@ TEST(BlockBasedTableTest, IndexSizeStat) {
|
||||
KVMap kvmap;
|
||||
Options options;
|
||||
options.compression = kNoCompression;
|
||||
options.block_restart_interval = 1;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_restart_interval = 1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
c.Finish(options, GetPlainInternalComparator(options.comparator), &ks,
|
||||
&kvmap);
|
||||
auto index_size = c.table_reader()->GetTableProperties()->index_size;
|
||||
c.Finish(options, table_options,
|
||||
GetPlainInternalComparator(options.comparator), &ks, &kvmap);
|
||||
auto index_size = c.GetTableReader()->GetTableProperties()->index_size;
|
||||
ASSERT_GT(index_size, last_index_size);
|
||||
last_index_size = index_size;
|
||||
}
|
||||
@@ -1224,8 +1327,10 @@ TEST(BlockBasedTableTest, NumBlockStat) {
|
||||
TableConstructor c(BytewiseComparator());
|
||||
Options options;
|
||||
options.compression = kNoCompression;
|
||||
options.block_restart_interval = 1;
|
||||
options.block_size = 1000;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_restart_interval = 1;
|
||||
table_options.block_size = 1000;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// the key/val are slightly smaller than block size, so that each block
|
||||
@@ -1235,10 +1340,10 @@ TEST(BlockBasedTableTest, NumBlockStat) {
|
||||
|
||||
std::vector<std::string> ks;
|
||||
KVMap kvmap;
|
||||
c.Finish(options, GetPlainInternalComparator(options.comparator), &ks,
|
||||
&kvmap);
|
||||
c.Finish(options, table_options,
|
||||
GetPlainInternalComparator(options.comparator), &ks, &kvmap);
|
||||
ASSERT_EQ(kvmap.size(),
|
||||
c.table_reader()->GetTableProperties()->num_data_blocks);
|
||||
c.GetTableReader()->GetTableProperties()->num_data_blocks);
|
||||
}
|
||||
|
||||
// A simple tool that takes the snapshot of block cache statistics.
|
||||
@@ -1300,23 +1405,22 @@ TEST(BlockBasedTableTest, BlockCacheDisabledTest) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.block_cache = NewLRUCache(1024);
|
||||
std::unique_ptr<const FilterPolicy> filter_policy(NewBloomFilterPolicy(10));
|
||||
options.filter_policy = filter_policy.get();
|
||||
BlockBasedTableOptions table_options;
|
||||
// Intentionally commented out: table_options.cache_index_and_filter_blocks =
|
||||
// true;
|
||||
table_options.block_cache = NewLRUCache(1024);
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
std::vector<std::string> keys;
|
||||
KVMap kvmap;
|
||||
|
||||
TableConstructor c(BytewiseComparator());
|
||||
TableConstructor c(BytewiseComparator(), true);
|
||||
c.Add("key", "value");
|
||||
c.Finish(options, GetPlainInternalComparator(options.comparator), &keys,
|
||||
&kvmap);
|
||||
c.Finish(options, table_options,
|
||||
GetPlainInternalComparator(options.comparator), &keys, &kvmap);
|
||||
|
||||
// preloading filter/index blocks is enabled.
|
||||
auto reader = dynamic_cast<BlockBasedTable*>(c.table_reader());
|
||||
auto reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
|
||||
ASSERT_TRUE(reader->TEST_filter_block_preloaded());
|
||||
ASSERT_TRUE(reader->TEST_index_reader_preloaded());
|
||||
|
||||
@@ -1343,10 +1447,10 @@ TEST(BlockBasedTableTest, FilterBlockInBlockCache) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.block_cache = NewLRUCache(1024);
|
||||
|
||||
// Enable the cache for index/filter blocks
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewLRUCache(1024);
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
std::vector<std::string> keys;
|
||||
@@ -1354,10 +1458,10 @@ TEST(BlockBasedTableTest, FilterBlockInBlockCache) {
|
||||
|
||||
TableConstructor c(BytewiseComparator());
|
||||
c.Add("key", "value");
|
||||
c.Finish(options, GetPlainInternalComparator(options.comparator), &keys,
|
||||
&kvmap);
|
||||
c.Finish(options, table_options,
|
||||
GetPlainInternalComparator(options.comparator), &keys, &kvmap);
|
||||
// preloading filter/index blocks is prohibited.
|
||||
auto reader = dynamic_cast<BlockBasedTable*>(c.table_reader());
|
||||
auto reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
|
||||
ASSERT_TRUE(!reader->TEST_filter_block_preloaded());
|
||||
ASSERT_TRUE(!reader->TEST_index_reader_preloaded());
|
||||
|
||||
@@ -1404,9 +1508,12 @@ TEST(BlockBasedTableTest, FilterBlockInBlockCache) {
|
||||
iter.reset();
|
||||
|
||||
// -- PART 2: Open without block cache
|
||||
options.block_cache.reset();
|
||||
table_options.no_block_cache = true;
|
||||
table_options.block_cache.reset();
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics(); // reset the stats
|
||||
c.Reopen(options);
|
||||
table_options.no_block_cache = false;
|
||||
|
||||
{
|
||||
iter.reset(c.NewIterator());
|
||||
@@ -1420,7 +1527,8 @@ TEST(BlockBasedTableTest, FilterBlockInBlockCache) {
|
||||
// -- PART 3: Open with very small block cache
|
||||
// In this test, no block will ever get hit since the block cache is
|
||||
// too small to fit even one entry.
|
||||
options.block_cache = NewLRUCache(1);
|
||||
table_options.block_cache = NewLRUCache(1);
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
c.Reopen(options);
|
||||
{
|
||||
BlockCachePropertiesSnapshot props(options.statistics.get());
|
||||
@@ -1458,11 +1566,12 @@ TEST(BlockBasedTableTest, BlockCacheLeak) {
|
||||
Options opt;
|
||||
unique_ptr<InternalKeyComparator> ikc;
|
||||
ikc.reset(new test::PlainInternalKeyComparator(opt.comparator));
|
||||
opt.block_size = 1024;
|
||||
opt.compression = kNoCompression;
|
||||
opt.block_cache =
|
||||
NewLRUCache(16 * 1024 * 1024); // big enough so we don't ever
|
||||
// lose cached values.
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
// big enough so we don't ever lose cached values.
|
||||
table_options.block_cache = NewLRUCache(16 * 1024 * 1024);
|
||||
opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
TableConstructor c(BytewiseComparator());
|
||||
c.Add("k01", "hello");
|
||||
@@ -1474,7 +1583,7 @@ TEST(BlockBasedTableTest, BlockCacheLeak) {
|
||||
c.Add("k07", std::string(100000, 'x'));
|
||||
std::vector<std::string> keys;
|
||||
KVMap kvmap;
|
||||
c.Finish(opt, *ikc, &keys, &kvmap);
|
||||
c.Finish(opt, table_options, *ikc, &keys, &kvmap);
|
||||
|
||||
unique_ptr<Iterator> iter(c.NewIterator());
|
||||
iter->SeekToFirst();
|
||||
@@ -1486,15 +1595,16 @@ TEST(BlockBasedTableTest, BlockCacheLeak) {
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_OK(c.Reopen(opt));
|
||||
auto table_reader = dynamic_cast<BlockBasedTable*>(c.table_reader());
|
||||
auto table_reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
|
||||
for (const std::string& key : keys) {
|
||||
ASSERT_TRUE(table_reader->TEST_KeyInCache(ReadOptions(), key));
|
||||
}
|
||||
|
||||
// rerun with different block cache
|
||||
opt.block_cache = NewLRUCache(16 * 1024 * 1024);
|
||||
table_options.block_cache = NewLRUCache(16 * 1024 * 1024);
|
||||
opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_OK(c.Reopen(opt));
|
||||
table_reader = dynamic_cast<BlockBasedTable*>(c.table_reader());
|
||||
table_reader = dynamic_cast<BlockBasedTable*>(c.GetTableReader());
|
||||
for (const std::string& key : keys) {
|
||||
ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), key));
|
||||
}
|
||||
@@ -1551,9 +1661,10 @@ TEST(GeneralTableTest, ApproximateOffsetOfPlain) {
|
||||
KVMap kvmap;
|
||||
Options options;
|
||||
test::PlainInternalKeyComparator internal_comparator(options.comparator);
|
||||
options.block_size = 1024;
|
||||
options.compression = kNoCompression;
|
||||
c.Finish(options, internal_comparator, &keys, &kvmap);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
c.Finish(options, table_options, internal_comparator, &keys, &kvmap);
|
||||
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
|
||||
@@ -1580,9 +1691,10 @@ static void DoCompressionTest(CompressionType comp) {
|
||||
KVMap kvmap;
|
||||
Options options;
|
||||
test::PlainInternalKeyComparator ikc(options.comparator);
|
||||
options.block_size = 1024;
|
||||
options.compression = comp;
|
||||
c.Finish(options, ikc, &keys, &kvmap);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
c.Finish(options, table_options, ikc, &keys, &kvmap);
|
||||
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
|
||||
|
||||
@@ -172,8 +172,9 @@ void TwoLevelIterator::InitDataBlock() {
|
||||
SetSecondLevelIterator(nullptr);
|
||||
} else {
|
||||
Slice handle = first_level_iter_.value();
|
||||
if (second_level_iter_.iter() != nullptr
|
||||
&& handle.compare(data_block_handle_) == 0) {
|
||||
if (second_level_iter_.iter() != nullptr &&
|
||||
!second_level_iter_.status().IsIncomplete() &&
|
||||
handle.compare(data_block_handle_) == 0) {
|
||||
// second_level_iter is already constructed with this iterator, so
|
||||
// no need to change anything
|
||||
} else {
|
||||
|
||||
+35
-16
@@ -31,6 +31,7 @@ int main() {
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <exception>
|
||||
#include <gflags/gflags.h>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/version_set.h"
|
||||
@@ -41,7 +42,6 @@ int main() {
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "port/port.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
@@ -154,7 +154,7 @@ DEFINE_int32(level0_stop_writes_trigger,
|
||||
rocksdb::Options().level0_stop_writes_trigger,
|
||||
"Number of files in level-0 that will trigger put stop.");
|
||||
|
||||
DEFINE_int32(block_size, rocksdb::Options().block_size,
|
||||
DEFINE_int32(block_size, rocksdb::BlockBasedTableOptions().block_size,
|
||||
"Number of bytes in a block.");
|
||||
|
||||
DEFINE_int32(max_background_compactions,
|
||||
@@ -760,7 +760,7 @@ class StressTest {
|
||||
? NewBloomFilterPolicy(FLAGS_bloom_bits)
|
||||
: nullptr),
|
||||
db_(nullptr),
|
||||
new_column_family_name_(0),
|
||||
new_column_family_name_(1),
|
||||
num_times_reopened_(0) {
|
||||
if (FLAGS_destroy_db_initially) {
|
||||
std::vector<std::string> files;
|
||||
@@ -780,7 +780,6 @@ class StressTest {
|
||||
}
|
||||
column_families_.clear();
|
||||
delete db_;
|
||||
delete filter_policy_;
|
||||
}
|
||||
|
||||
bool Run() {
|
||||
@@ -1219,12 +1218,20 @@ class StressTest {
|
||||
Status s __attribute__((unused));
|
||||
s = db_->DropColumnFamily(column_families_[cf]);
|
||||
delete column_families_[cf];
|
||||
assert(s.ok());
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "dropping column family error: %s\n",
|
||||
s.ToString().c_str());
|
||||
std::terminate();
|
||||
}
|
||||
s = db_->CreateColumnFamily(ColumnFamilyOptions(options_), new_name,
|
||||
&column_families_[cf]);
|
||||
column_family_names_[cf] = new_name;
|
||||
thread->shared->ClearColumnFamily(cf);
|
||||
assert(s.ok());
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "creating column family error: %s\n",
|
||||
s.ToString().c_str());
|
||||
std::terminate();
|
||||
}
|
||||
thread->shared->UnlockColumnFamily(cf);
|
||||
}
|
||||
}
|
||||
@@ -1299,10 +1306,15 @@ class StressTest {
|
||||
}
|
||||
}
|
||||
thread->shared->Put(rand_column_family, rand_key, value_base);
|
||||
Status s;
|
||||
if (FLAGS_use_merge) {
|
||||
db_->Merge(write_opts, column_family, key, v);
|
||||
s = db_->Merge(write_opts, column_family, key, v);
|
||||
} else {
|
||||
db_->Put(write_opts, column_family, key, v);
|
||||
s = db_->Put(write_opts, column_family, key, v);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "put or merge error: %s\n", s.ToString().c_str());
|
||||
std::terminate();
|
||||
}
|
||||
thread->stats.AddBytesForWrites(1, sz);
|
||||
} else {
|
||||
@@ -1313,8 +1325,12 @@ class StressTest {
|
||||
// OPERATION delete
|
||||
if (!FLAGS_test_batches_snapshots) {
|
||||
thread->shared->Delete(rand_column_family, rand_key);
|
||||
db_->Delete(write_opts, column_family, key);
|
||||
Status s = db_->Delete(write_opts, column_family, key);
|
||||
thread->stats.AddDeletes(1);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "delete error: %s\n", s.ToString().c_str());
|
||||
std::terminate();
|
||||
}
|
||||
} else {
|
||||
MultiDelete(thread, write_opts, column_family, key);
|
||||
}
|
||||
@@ -1550,8 +1566,13 @@ class StressTest {
|
||||
|
||||
void Open() {
|
||||
assert(db_ == nullptr);
|
||||
options_.block_cache = cache_;
|
||||
options_.block_cache_compressed = compressed_cache_;
|
||||
BlockBasedTableOptions block_based_options;
|
||||
block_based_options.block_cache = cache_;
|
||||
block_based_options.block_cache_compressed = compressed_cache_;
|
||||
block_based_options.block_size = FLAGS_block_size;
|
||||
block_based_options.filter_policy = filter_policy_;
|
||||
options_.table_factory.reset(
|
||||
NewBlockBasedTableFactory(block_based_options));
|
||||
options_.write_buffer_size = FLAGS_write_buffer_size;
|
||||
options_.max_write_buffer_number = FLAGS_max_write_buffer_number;
|
||||
options_.min_write_buffer_number_to_merge =
|
||||
@@ -1560,8 +1581,6 @@ class StressTest {
|
||||
options_.max_background_flushes = FLAGS_max_background_flushes;
|
||||
options_.compaction_style =
|
||||
static_cast<rocksdb::CompactionStyle>(FLAGS_compaction_style);
|
||||
options_.block_size = FLAGS_block_size;
|
||||
options_.filter_policy = filter_policy_;
|
||||
options_.prefix_extractor.reset(NewFixedPrefixTransform(FLAGS_prefix_size));
|
||||
options_.max_open_files = FLAGS_open_files;
|
||||
options_.statistics = dbstats;
|
||||
@@ -1718,9 +1737,9 @@ class StressTest {
|
||||
}
|
||||
|
||||
private:
|
||||
shared_ptr<Cache> cache_;
|
||||
shared_ptr<Cache> compressed_cache_;
|
||||
const FilterPolicy* filter_policy_;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<Cache> compressed_cache_;
|
||||
std::shared_ptr<const FilterPolicy> filter_policy_;
|
||||
DB* db_;
|
||||
Options options_;
|
||||
std::vector<ColumnFamilyHandle*> column_families_;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user