mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 22:55:23 +08:00
Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92df749a96 | |||
| 09040bff08 | |||
| 2591096d40 | |||
| c381701bdd | |||
| da2ae09293 | |||
| 1f303c11a3 | |||
| a51fbf5f3f | |||
| 3f53d2762f | |||
| 5ff6633588 | |||
| 1fc71a4b16 | |||
| 105c1e099b | |||
| 01700b6911 | |||
| 36de0e5359 | |||
| 30c81e7717 | |||
| 536f4b31a6 | |||
| f0a8be253e | |||
| 222cf2555a | |||
| 8a7d1fe616 | |||
| f697cad15c | |||
| cae316d758 | |||
| 70828557ef | |||
| 0a4d930264 | |||
| b6caaea9d3 | |||
| 21e522673f | |||
| 05bd545000 | |||
| ef1aad97f9 | |||
| 73d7147096 | |||
| 8a9cc7885c | |||
| 534357ca3a | |||
| 5ef1ba7ff5 | |||
| b278ae8e50 | |||
| 1b95bf734d | |||
| fd3fb4b0bf | |||
| db1a10e9b8 | |||
| 5491c486eb | |||
| 058421dea8 | |||
| e9b18b6b89 | |||
| 4216ca36ae | |||
| 637744ce15 | |||
| 01159aa802 | |||
| 4adf64e068 | |||
| 862227769a | |||
| 8a03935f8c | |||
| 8f6e9ab209 | |||
| 13a130cc00 | |||
| 3f7104d7c5 | |||
| 91bede79cc | |||
| 0bf5589c74 | |||
| 97bfcd6a16 | |||
| c9cfb88256 | |||
| 4efca96bb3 | |||
| f0660d5253 | |||
| e8c592c625 | |||
| 0bc5fa9f40 | |||
| 47f0cf6d38 | |||
| da12f9ec4e | |||
| 54d7a2c0c5 | |||
| efbb4bd387 | |||
| bc708e0012 | |||
| 1a34aaaef0 | |||
| 9fc23d0c56 | |||
| 7b85c1e900 | |||
| 8d9a46fcd1 | |||
| d33657a4a5 | |||
| 90a6aca48e | |||
| a1df6c1fc8 | |||
| d4d338de33 | |||
| 6b835c6009 | |||
| 70aa243fa1 | |||
| 4203431e71 | |||
| 6580685260 | |||
| 2459f7ec4e | |||
| f146cab261 | |||
| d3f63f03ad | |||
| 1d05006740 | |||
| 30b20604db | |||
| 9c332aa11a | |||
| 6634844dba | |||
| c4018e771c | |||
| f5d4df1c02 | |||
| a2e0d890ed | |||
| 5656367416 | |||
| f799c8be5f | |||
| 918a355e00 | |||
| b2625a4cc1 | |||
| abf9920386 | |||
| dd337bc0b2 | |||
| a3594867ba | |||
| 81c5d98900 | |||
| 19de6a7aad | |||
| 55531fd089 | |||
| 8898a0a0d1 | |||
| e813f5b6d9 | |||
| faa8d21922 | |||
| 85f9bb4ef4 | |||
| e7c9412a93 | |||
| fb54eef744 | |||
| c9ad282e4a | |||
| 82c31792d0 | |||
| bf4b1528d8 | |||
| e5e6f55bd1 | |||
| 96663410b0 | |||
| 854abaf777 | |||
| dfb31d152d | |||
| 3b0dc76699 | |||
| 3aae401726 | |||
| a1ddfc610b | |||
| a1b5650a75 | |||
| 4938032549 | |||
| 718029fc38 | |||
| e0ebea6cc2 | |||
| 143ee5a1c1 | |||
| 403636842a | |||
| 21e18ffa73 | |||
| 583feae8dd | |||
| 558050939f | |||
| 00b26c3a83 | |||
| 9fe87b17aa | |||
| d4a8423334 | |||
| 107e08baa7 |
+2
-3
@@ -10,9 +10,8 @@ the CLA and we can cross-check with your GitHub username.
|
||||
|
||||
Complete your CLA here: <https://code.facebook.com/cla>
|
||||
|
||||
If you don't have a Facebook account, we can send you a PDF that you can
|
||||
sign offline. Send us an e-mail or create a new github issue to
|
||||
request the CLA in PDF format.
|
||||
If you prefer to sign a paper copy, we can send you a PDF. Send us an
|
||||
e-mail or create a new github issue to request the CLA in PDF format.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+29
-2
@@ -1,15 +1,42 @@
|
||||
# Rocksdb Change Log
|
||||
|
||||
## 3.3.0 (7/10/2014)
|
||||
### New Features
|
||||
* Added JSON API prototype.
|
||||
* HashLinklist reduces performance outlier caused by skewed bucket by switching data in the bucket from linked list to skip list. Add parameter threshold_use_skiplist in NewHashLinkListRepFactory().
|
||||
* RocksDB is now able to reclaim storage space more effectively during the compaction process. This is done by compensating the size of each deletion entry by the 2X average value size, which makes compaction to be triggerred by deletion entries more easily.
|
||||
* Add TimeOut API to write. Now WriteOptions have a variable called timeout_hint_us. With timeout_hint_us set to non-zero, any write associated with this timeout_hint_us may be aborted when it runs longer than the specified timeout_hint_us, and it is guaranteed that any write completes earlier than the specified time-out will not be aborted due to the time-out condition.
|
||||
* Add a rate_limiter option, which controls total throughput of flush and compaction. The throughput is specified in bytes/sec. Flush always has precedence over compaction when available bandwidth is constrained.
|
||||
|
||||
### Public API changes
|
||||
* Removed NewTotalOrderPlainTableFactory because it is not used and implemented semantically incorrect.
|
||||
|
||||
## 3.2.0 (06/20/2014)
|
||||
|
||||
### Public API changes
|
||||
* We removed seek compaction as a concept 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.
|
||||
Because of that, Options::disable_seek_compaction is now obsolete. It is still a parameter in Options, so it does not break the build, but it does not have any effect. We plan to completely remove it at some point, so we ask users to please remove this option from your code base.
|
||||
* Add two paramters to NewHashLinkListRepFactory() for logging on too many entries in a hash bucket when flushing.
|
||||
* Added new option BlockBasedTableOptions::hash_index_allow_collision. When enabled, prefix hash index for block-based table will not store prefix and allow hash collision, reducing memory consumption.
|
||||
|
||||
### New Features
|
||||
* PlainTable now supports a new key encoding: for keys of the same prefix, the prefix is only written once. It can be enabled through encoding_type paramter of NewPlainTableFactory()
|
||||
* Add AdaptiveTableFactory, which is used to convert from a DB of PlainTable to BlockBasedTabe, or vise versa. It can be created using NewAdaptiveTableFactory()
|
||||
|
||||
### Performance Improvements
|
||||
* Tailing Iterator re-implemeted with ForwardIterator + Cascading Search Hint , see ~20% throughput improvement.
|
||||
|
||||
## 3.1.0 (05/21/2014)
|
||||
|
||||
### Public API changes
|
||||
* Replaced ColumnFamilyOptions::table_properties_collectors with ColumnFamilyOptions::table_properties_collector_factories
|
||||
* Add two paramters to NewHashLinkListRepFactory() for logging on too many entries in a hash bucket when flushing.
|
||||
|
||||
### New Features
|
||||
* Hash index for block-based table will be materialized and reconstructed more efficiently. Previously hash index is constructed by scanning the whole table during every table open.
|
||||
* FIFO compaction style
|
||||
* Add AdaptiveTableFactory, which is used to convert from a DB of PlainTable to BlockBasedTabe, or vise versa. It can be created using NewAdaptiveTableFactory()
|
||||
|
||||
## 3.0.0 (05/05/2014)
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ else
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE) -DHAVE_JEMALLOC
|
||||
endif
|
||||
|
||||
WARNING_FLAGS = -Wall -Werror
|
||||
CFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverloaded-virtual
|
||||
|
||||
@@ -104,6 +103,8 @@ TESTS = \
|
||||
stringappend_test \
|
||||
ttl_test \
|
||||
backupable_db_test \
|
||||
document_db_test \
|
||||
json_document_test \
|
||||
version_edit_test \
|
||||
version_set_test \
|
||||
file_indexer_test \
|
||||
@@ -111,7 +112,8 @@ TESTS = \
|
||||
deletefile_test \
|
||||
table_test \
|
||||
thread_local_test \
|
||||
geodb_test
|
||||
geodb_test \
|
||||
rate_limiter_test
|
||||
|
||||
TOOLS = \
|
||||
sst_dump \
|
||||
@@ -146,7 +148,7 @@ SHARED = $(SHARED1)
|
||||
else
|
||||
# Update db.h if you change these.
|
||||
SHARED_MAJOR = 3
|
||||
SHARED_MINOR = 2
|
||||
SHARED_MINOR = 3
|
||||
SHARED1 = ${LIBNAME}.$(PLATFORM_SHARED_EXT)
|
||||
SHARED2 = $(SHARED1).$(SHARED_MAJOR)
|
||||
SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
|
||||
@@ -343,6 +345,12 @@ prefix_test: db/prefix_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
document_db_test: utilities/document/document_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/document/document_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
json_document_test: utilities/document/json_document_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/document/json_document_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
@@ -352,6 +360,9 @@ dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
rate_limiter_test: util/rate_limiter_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/rate_limiter_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
@@ -435,7 +446,7 @@ ROCKSDBJNILIB = ./java/librocksdbjni.jnilib
|
||||
JAVA_INCLUDE = -I/System/Library/Frameworks/JavaVM.framework/Headers/
|
||||
endif
|
||||
|
||||
rocksdbjava: clean
|
||||
rocksdbjava:
|
||||
OPT="-fPIC -DNDEBUG -O2" $(MAKE) $(LIBRARY) -j32
|
||||
cd java;$(MAKE) java;
|
||||
rm -f $(ROCKSDBJNILIB)
|
||||
|
||||
@@ -8,7 +8,7 @@ and Jeff Dean (jeff@google.com)
|
||||
|
||||
This code is a library that forms the core building block for a fast
|
||||
key value server, especially suited for storing data on flash drives.
|
||||
It has an Log-Structured-Merge-Database (LSM) design with flexible tradeoffs
|
||||
It has a Log-Structured-Merge-Database (LSM) design with flexible tradeoffs
|
||||
between Write-Amplification-Factor (WAF), Read-Amplification-Factor (RAF)
|
||||
and Space-Amplification-Factor (SAF). It has multi-threaded compactions,
|
||||
making it specially suitable for storing multiple terabytes of data in a
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
# -DLEVELDB_PLATFORM_NOATOMIC if it is not
|
||||
# -DSNAPPY if the Snappy library is present
|
||||
# -DLZ4 if the LZ4 library is present
|
||||
# -DNUMA if the NUMA library is present
|
||||
#
|
||||
# Using gflags in rocksdb:
|
||||
# Our project depends on gflags, which requires users to take some extra steps
|
||||
@@ -47,13 +48,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
|
||||
@@ -237,7 +232,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
|
||||
|
||||
@@ -272,6 +267,17 @@ EOF
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -llz4"
|
||||
fi
|
||||
|
||||
# Test whether numa is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null <<EOF
|
||||
#include <numa.h>
|
||||
#inlcude <numaif.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DNUMA"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lnuma"
|
||||
fi
|
||||
|
||||
# Test whether tcmalloc is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -ltcmalloc 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
|
||||
@@ -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
|
||||
@@ -55,22 +55,27 @@ GFLAGS_LIBS=" $TOOLCHAIN_LIB_BASE/gflags/gflags-1.6/c3f970a/lib/libgflags.a"
|
||||
JEMALLOC_INCLUDE=" -I $TOOLCHAIN_LIB_BASE/jemalloc/jemalloc-3.4.1/4d53c6f/include/"
|
||||
JEMALLOC_LIB=" -Wl,--whole-archive $TOOLCHAIN_LIB_BASE/jemalloc/jemalloc-3.4.1/4d53c6f/lib/libjemalloc.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_REV=829d10dac0230f99cd7e1778869d2adf3da24b65
|
||||
NUMA_INCLUDE=" -I /mnt/gvfs/third-party2/numa/$NUMA_REV/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a/include/"
|
||||
NUMA_LIB=" /mnt/gvfs/third-party2/numa/$NUMA_REV/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a/lib/libnuma.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=" -msse -msse4.2 "
|
||||
|
||||
CC="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.8.1/cc6c9dc/bin/gcc"
|
||||
CXX="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.8.1/cc6c9dc/bin/g++ $JINCLUDE $SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $GFLAGS_INCLUDE"
|
||||
CXX="$TOOLCHAIN_EXECUTABLES/gcc/gcc-4.8.1/cc6c9dc/bin/g++ $JINCLUDE $SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
AR=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ar
|
||||
RANLIB=$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/ranlib
|
||||
|
||||
CFLAGS="-B$TOOLCHAIN_EXECUTABLES/binutils/binutils-2.21.1/da39a3e/bin/gold -m64 -mtune=generic"
|
||||
CFLAGS+=" $LIBGCC_INCLUDE $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_ATOMIC_PRESENT -DROCKSDB_FALLOCATE_PRESENT"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DNUMA"
|
||||
|
||||
EXEC_LDFLAGS="-Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--no-whole-archive $TOOLCHAIN_LIB_BASE/libunwind/libunwind-1.0.1/675d945/lib/libunwind.a"
|
||||
EXEC_LDFLAGS+=" $HDFSLIB $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS"
|
||||
EXEC_LDFLAGS+=" $HDFSLIB $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS "
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -22,4 +22,4 @@ echo ""
|
||||
echo "-----------------------------------------------------------------------------"
|
||||
echo "| Installation Completed |"
|
||||
echo "-----------------------------------------------------------------------------"
|
||||
echo "Please run `. ~/bash_profile` to be able to compile with gflags"
|
||||
echo "Please run \`. ~/.bash_profile\` to be able to compile with gflags"
|
||||
|
||||
@@ -109,7 +109,6 @@ make release
|
||||
--cache_numshardbits=6 \
|
||||
--table_cache_numshardbits=4 \
|
||||
--open_files=55000 \
|
||||
--disable_seek_compaction=1 \
|
||||
--statistics=1 \
|
||||
--histogram=1 \
|
||||
--disable_data_sync=1 \
|
||||
@@ -129,7 +128,6 @@ make release
|
||||
--cache_numshardbits=6 \
|
||||
--table_cache_numshardbits=4 \
|
||||
--open_files=55000 \
|
||||
--disable_seek_compaction=1 \
|
||||
--use_tailing_iterator=1 \
|
||||
--statistics=1 \
|
||||
--histogram=1 \
|
||||
@@ -150,7 +148,6 @@ make release
|
||||
--cache_numshardbits=6 \
|
||||
--table_cache_numshardbits=4 \
|
||||
--open_files=55000 \
|
||||
--disable_seek_compaction=1 \
|
||||
--statistics=1 \
|
||||
--histogram=1 \
|
||||
--disable_data_sync=1 \
|
||||
@@ -172,7 +169,6 @@ make release
|
||||
--table_cache_numshardbits=4 \
|
||||
--write_buffer_size=1000000000 \
|
||||
--open_files=55000 \
|
||||
--disable_seek_compaction=1 \
|
||||
--statistics=1 \
|
||||
--histogram=1 \
|
||||
--disable_data_sync=1 \
|
||||
@@ -231,7 +227,6 @@ make release
|
||||
--cache_numshardbits=6 \
|
||||
--table_cache_numshardbits=4 \
|
||||
--open_files=55000 \
|
||||
--disable_seek_compaction=1 \
|
||||
--disable_auto_compactions=1 \
|
||||
--statistics=1 \
|
||||
--histogram=1 \
|
||||
@@ -254,7 +249,6 @@ make release
|
||||
--cache_numshardbits=6 \
|
||||
--table_cache_numshardbits=4 \
|
||||
--open_files=55000 \
|
||||
--disable_seek_compaction=1 \
|
||||
--statistics=1 \
|
||||
--histogram=1 \
|
||||
--disable_data_sync=1 \
|
||||
@@ -274,7 +268,6 @@ make release
|
||||
--table_cache_numshardbits=4 \
|
||||
--write_buffer_size=1000000000 \
|
||||
--open_files=55000 \
|
||||
--disable_seek_compaction=1 \
|
||||
--statistics=1 \
|
||||
--histogram=1 \
|
||||
--disable_data_sync=1 \
|
||||
@@ -291,7 +284,6 @@ common_in_mem_args="--db=/dev/shm/rocksdb \
|
||||
--value_size=100 \
|
||||
--compression_type=none \
|
||||
--compression_ratio=1 \
|
||||
--disable_seek_compaction=1 \
|
||||
--hard_rate_limit=2 \
|
||||
--write_buffer_size=134217728 \
|
||||
--max_write_buffer_number=4 \
|
||||
|
||||
+5
-2
@@ -40,7 +40,8 @@ Status BuildTable(const std::string& dbname, Env* env, const Options& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const SequenceNumber newest_snapshot,
|
||||
const SequenceNumber earliest_seqno_in_memtable,
|
||||
const CompressionType compression) {
|
||||
const CompressionType compression,
|
||||
const Env::IOPriority io_priority) {
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
meta->smallest_seqno = meta->largest_seqno = 0;
|
||||
@@ -54,13 +55,15 @@ Status BuildTable(const std::string& dbname, Env* env, const Options& options,
|
||||
purge = false;
|
||||
}
|
||||
|
||||
std::string fname = TableFileName(dbname, meta->fd.GetNumber());
|
||||
std::string fname = TableFileName(options.db_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
if (iter->Valid()) {
|
||||
unique_ptr<WritableFile> file;
|
||||
s = env->NewWritableFile(fname, &file, soptions);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
file->SetIOPriority(io_priority);
|
||||
|
||||
TableBuilder* builder =
|
||||
NewTableBuilder(options, internal_comparator, file.get(), compression);
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#pragma once
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/options.h"
|
||||
@@ -40,6 +41,7 @@ extern Status BuildTable(const std::string& dbname, Env* env,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const SequenceNumber newest_snapshot,
|
||||
const SequenceNumber earliest_seqno_in_memtable,
|
||||
const CompressionType compression);
|
||||
const CompressionType compression,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH);
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -31,10 +31,16 @@
|
||||
#include "rocksdb/table.h"
|
||||
|
||||
using rocksdb::Cache;
|
||||
using rocksdb::ColumnFamilyDescriptor;
|
||||
using rocksdb::ColumnFamilyHandle;
|
||||
using rocksdb::ColumnFamilyOptions;
|
||||
using rocksdb::CompactionFilter;
|
||||
using rocksdb::CompactionFilterFactory;
|
||||
using rocksdb::CompactionOptionsFIFO;
|
||||
using rocksdb::Comparator;
|
||||
using rocksdb::CompressionType;
|
||||
using rocksdb::DB;
|
||||
using rocksdb::DBOptions;
|
||||
using rocksdb::Env;
|
||||
using rocksdb::InfoLogLevel;
|
||||
using rocksdb::FileLock;
|
||||
@@ -68,6 +74,7 @@ struct rocksdb_iterator_t { Iterator* rep; };
|
||||
struct rocksdb_writebatch_t { WriteBatch rep; };
|
||||
struct rocksdb_snapshot_t { const Snapshot* rep; };
|
||||
struct rocksdb_flushoptions_t { FlushOptions rep; };
|
||||
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; };
|
||||
@@ -78,6 +85,11 @@ struct rocksdb_filelock_t { FileLock* rep; };
|
||||
struct rocksdb_logger_t { shared_ptr<Logger> rep; };
|
||||
struct rocksdb_cache_t { shared_ptr<Cache> rep; };
|
||||
struct rocksdb_livefiles_t { std::vector<LiveFileMetaData> rep; };
|
||||
struct rocksdb_column_family_handle_t { ColumnFamilyHandle* rep; };
|
||||
|
||||
struct rocksdb_compactionfiltercontext_t {
|
||||
CompactionFilter::Context rep;
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
void* state_;
|
||||
@@ -122,6 +134,26 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
}
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
rocksdb_compactionfilter_t* (*create_compaction_filter_)(
|
||||
void*, rocksdb_compactionfiltercontext_t* context);
|
||||
const char* (*name_)(void*);
|
||||
|
||||
virtual ~rocksdb_compactionfilterfactory_t() { (*destructor_)(state_); }
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) {
|
||||
rocksdb_compactionfiltercontext_t ccontext;
|
||||
ccontext.rep = context;
|
||||
CompactionFilter* cf = (*create_compaction_filter_)(state_, &ccontext);
|
||||
return std::unique_ptr<CompactionFilter>(cf);
|
||||
}
|
||||
|
||||
virtual const char* Name() const { return (*name_)(state_); }
|
||||
};
|
||||
|
||||
struct rocksdb_comparator_t : public Comparator {
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
@@ -394,6 +426,120 @@ void rocksdb_close(rocksdb_t* db) {
|
||||
delete db;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_column_families(
|
||||
const rocksdb_options_t* db_options,
|
||||
const char* name,
|
||||
int num_column_families,
|
||||
const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles,
|
||||
char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
DB* db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
if (SaveError(errptr, DB::Open(DBOptions(db_options->rep),
|
||||
std::string(name), column_families, &handles, &db))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < handles.size(); i++) {
|
||||
rocksdb_column_family_handle_t* c_handle = new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
const rocksdb_options_t* db_options,
|
||||
const char* name,
|
||||
int num_column_families,
|
||||
const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles,
|
||||
unsigned char error_if_log_file_exist,
|
||||
char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
DB* db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
if (SaveError(errptr, DB::OpenForReadOnly(DBOptions(db_options->rep),
|
||||
std::string(name), column_families, &handles, &db, error_if_log_file_exist))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < handles.size(); i++) {
|
||||
rocksdb_column_family_handle_t* c_handle = new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
char** rocksdb_list_column_families(
|
||||
const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
size_t* lencfs,
|
||||
char** errptr) {
|
||||
std::vector<std::string> fams;
|
||||
SaveError(errptr,
|
||||
DB::ListColumnFamilies(DBOptions(options->rep),
|
||||
std::string(name), &fams));
|
||||
|
||||
*lencfs = fams.size();
|
||||
char** column_families = static_cast<char**>(malloc(sizeof(char*) * fams.size()));
|
||||
for (size_t i = 0; i < fams.size(); i++) {
|
||||
column_families[i] = strdup(fams[i].c_str());
|
||||
}
|
||||
return column_families;
|
||||
}
|
||||
|
||||
void rocksdb_list_column_families_destroy(char** list, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
free(list[i]);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_create_column_family(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_options_t* column_family_options,
|
||||
const char* column_family_name,
|
||||
char** errptr) {
|
||||
rocksdb_column_family_handle_t* handle = new rocksdb_column_family_handle_t;
|
||||
SaveError(errptr,
|
||||
db->rep->CreateColumnFamily(ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep)));
|
||||
return handle;
|
||||
}
|
||||
|
||||
void rocksdb_drop_column_family(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* handle,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->DropColumnFamily(handle->rep));
|
||||
}
|
||||
|
||||
void rocksdb_column_family_handle_destroy(rocksdb_column_family_handle_t* handle) {
|
||||
delete handle->rep;
|
||||
delete handle;
|
||||
}
|
||||
|
||||
void rocksdb_put(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
@@ -404,6 +550,18 @@ void rocksdb_put(
|
||||
db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_put_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->Put(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
@@ -412,6 +570,16 @@ void rocksdb_delete(
|
||||
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_delete_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, column_family->rep,
|
||||
Slice(key, keylen)));
|
||||
}
|
||||
|
||||
void rocksdb_merge(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
@@ -422,6 +590,18 @@ void rocksdb_merge(
|
||||
db->rep->Merge(options->rep, Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_merge_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->Merge(options->rep, column_family->rep,
|
||||
Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_write(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
@@ -451,6 +631,29 @@ char* rocksdb_get(
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_get_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
size_t* vallen,
|
||||
char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = db->rep->Get(options->rep, column_family->rep,
|
||||
Slice(key, keylen), &tmp);
|
||||
if (s.ok()) {
|
||||
*vallen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vallen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_create_iterator(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options) {
|
||||
@@ -459,6 +662,15 @@ rocksdb_iterator_t* rocksdb_create_iterator(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_create_iterator_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = db->rep->NewIterator(options->rep, column_family->rep);
|
||||
return result;
|
||||
}
|
||||
|
||||
const rocksdb_snapshot_t* rocksdb_create_snapshot(
|
||||
rocksdb_t* db) {
|
||||
rocksdb_snapshot_t* result = new rocksdb_snapshot_t;
|
||||
@@ -485,6 +697,19 @@ char* rocksdb_property_value(
|
||||
}
|
||||
}
|
||||
|
||||
char* rocksdb_property_value_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* propname) {
|
||||
std::string tmp;
|
||||
if (db->rep->GetProperty(column_family->rep, Slice(propname), &tmp)) {
|
||||
// We use strdup() since we expect human readable output.
|
||||
return strdup(tmp.c_str());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_approximate_sizes(
|
||||
rocksdb_t* db,
|
||||
int num_ranges,
|
||||
@@ -500,6 +725,22 @@ void rocksdb_approximate_sizes(
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
void rocksdb_approximate_sizes_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
int num_ranges,
|
||||
const char* const* range_start_key, const size_t* range_start_key_len,
|
||||
const char* const* range_limit_key, const size_t* range_limit_key_len,
|
||||
uint64_t* sizes) {
|
||||
Range* ranges = new Range[num_ranges];
|
||||
for (int i = 0; i < num_ranges; i++) {
|
||||
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
|
||||
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
|
||||
}
|
||||
db->rep->GetApproximateSizes(column_family->rep, ranges, num_ranges, sizes);
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
void rocksdb_delete_file(
|
||||
rocksdb_t* db,
|
||||
const char* name) {
|
||||
@@ -524,6 +765,19 @@ void rocksdb_compact_range(
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr));
|
||||
}
|
||||
|
||||
void rocksdb_compact_range_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* start_key, size_t start_key_len,
|
||||
const char* limit_key, size_t limit_key_len) {
|
||||
Slice a, b;
|
||||
db->rep->CompactRange(
|
||||
column_family->rep,
|
||||
// Pass nullptr Slice if corresponding "const char*" is nullptr
|
||||
(start_key ? (a = Slice(start_key, start_key_len), &a) : nullptr),
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr));
|
||||
}
|
||||
|
||||
void rocksdb_flush(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_flushoptions_t* options,
|
||||
@@ -607,6 +861,13 @@ rocksdb_writebatch_t* rocksdb_writebatch_create() {
|
||||
return new rocksdb_writebatch_t;
|
||||
}
|
||||
|
||||
rocksdb_writebatch_t* rocksdb_writebatch_create_from(const char* rep,
|
||||
size_t size) {
|
||||
rocksdb_writebatch_t* b = new rocksdb_writebatch_t;
|
||||
b->rep = WriteBatch(std::string(rep, size));
|
||||
return b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_destroy(rocksdb_writebatch_t* b) {
|
||||
delete b;
|
||||
}
|
||||
@@ -626,6 +887,14 @@ void rocksdb_writebatch_put(
|
||||
b->rep.Put(Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_put_cf(
|
||||
rocksdb_writebatch_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen) {
|
||||
b->rep.Put(column_family->rep, Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_merge(
|
||||
rocksdb_writebatch_t* b,
|
||||
const char* key, size_t klen,
|
||||
@@ -633,12 +902,27 @@ void rocksdb_writebatch_merge(
|
||||
b->rep.Merge(Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_merge_cf(
|
||||
rocksdb_writebatch_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen) {
|
||||
b->rep.Merge(column_family->rep, Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete(
|
||||
rocksdb_writebatch_t* b,
|
||||
const char* key, size_t klen) {
|
||||
b->rep.Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete_cf(
|
||||
rocksdb_writebatch_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen) {
|
||||
b->rep.Delete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_iterate(
|
||||
rocksdb_writebatch_t* b,
|
||||
void* state,
|
||||
@@ -676,12 +960,38 @@ void rocksdb_options_destroy(rocksdb_options_t* options) {
|
||||
delete options;
|
||||
}
|
||||
|
||||
void rocksdb_options_increase_parallelism(
|
||||
rocksdb_options_t* opt, int total_threads) {
|
||||
opt->rep.IncreaseParallelism(total_threads);
|
||||
}
|
||||
|
||||
void rocksdb_options_optimize_for_point_lookup(
|
||||
rocksdb_options_t* opt) {
|
||||
opt->rep.OptimizeForPointLookup();
|
||||
}
|
||||
|
||||
void rocksdb_options_optimize_level_style_compaction(
|
||||
rocksdb_options_t* opt, uint64_t memtable_memory_budget) {
|
||||
opt->rep.OptimizeLevelStyleCompaction(memtable_memory_budget);
|
||||
}
|
||||
|
||||
void rocksdb_options_optimize_universal_style_compaction(
|
||||
rocksdb_options_t* opt, uint64_t memtable_memory_budget) {
|
||||
opt->rep.OptimizeUniversalStyleCompaction(memtable_memory_budget);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_compactionfilter_t* filter) {
|
||||
opt->rep.compaction_filter = filter;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_filter_factory(
|
||||
rocksdb_options_t* opt, rocksdb_compactionfilterfactory_t* factory) {
|
||||
opt->rep.compaction_filter_factory =
|
||||
std::shared_ptr<CompactionFilterFactory>(factory);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_comparator(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_comparator_t* cmp) {
|
||||
@@ -705,6 +1015,11 @@ void rocksdb_options_set_create_if_missing(
|
||||
opt->rep.create_if_missing = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_create_missing_column_families(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.create_missing_column_families = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_error_if_exists(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.error_if_exists = v;
|
||||
@@ -1158,6 +1473,12 @@ void rocksdb_options_set_universal_compaction_options(rocksdb_options_t *opt, ro
|
||||
opt->rep.compaction_options_universal = *(uco->rep);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_fifo_compaction_options(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_fifo_compaction_options_t* fifo) {
|
||||
opt->rep.compaction_options_fifo = fifo->rep;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO:
|
||||
DB::OpenForReadOnly
|
||||
@@ -1196,6 +1517,35 @@ void rocksdb_compactionfilter_destroy(rocksdb_compactionfilter_t* filter) {
|
||||
delete filter;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compactionfiltercontext_is_full_compaction(
|
||||
rocksdb_compactionfiltercontext_t* context) {
|
||||
return context->rep.is_full_compaction;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compactionfiltercontext_is_manual_compaction(
|
||||
rocksdb_compactionfiltercontext_t* context) {
|
||||
return context->rep.is_manual_compaction;
|
||||
}
|
||||
|
||||
rocksdb_compactionfilterfactory_t* rocksdb_compactionfilterfactory_create(
|
||||
void* state, void (*destructor)(void*),
|
||||
rocksdb_compactionfilter_t* (*create_compaction_filter)(
|
||||
void*, rocksdb_compactionfiltercontext_t* context),
|
||||
const char* (*name)(void*)) {
|
||||
rocksdb_compactionfilterfactory_t* result =
|
||||
new rocksdb_compactionfilterfactory_t;
|
||||
result->state_ = state;
|
||||
result->destructor_ = destructor;
|
||||
result->create_compaction_filter_ = create_compaction_filter;
|
||||
result->name_ = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_compactionfilterfactory_destroy(
|
||||
rocksdb_compactionfilterfactory_t* factory) {
|
||||
delete factory;
|
||||
}
|
||||
|
||||
rocksdb_comparator_t* rocksdb_comparator_create(
|
||||
void* state,
|
||||
void (*destructor)(void*),
|
||||
@@ -1487,6 +1837,22 @@ void rocksdb_universal_compaction_options_destroy(
|
||||
delete uco;
|
||||
}
|
||||
|
||||
rocksdb_fifo_compaction_options_t* rocksdb_fifo_compaction_options_create() {
|
||||
rocksdb_fifo_compaction_options_t* result = new rocksdb_fifo_compaction_options_t;
|
||||
result->rep = CompactionOptionsFIFO();
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_fifo_compaction_options_set_max_table_files_size(
|
||||
rocksdb_fifo_compaction_options_t* fifo_opts, uint64_t size) {
|
||||
fifo_opts->rep.max_table_files_size = size;
|
||||
}
|
||||
|
||||
void rocksdb_fifo_compaction_options_destroy(
|
||||
rocksdb_fifo_compaction_options_t* fifo_opts) {
|
||||
delete fifo_opts;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_min_level_to_compress(rocksdb_options_t* opt, int level) {
|
||||
if (level >= 0) {
|
||||
assert(level <= opt->rep.num_levels);
|
||||
|
||||
+171
-22
@@ -75,6 +75,22 @@ static void CheckGet(
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckGetCF(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* handle,
|
||||
const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_get_cf(db, options, handle, key, strlen(key), &val_len, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
|
||||
static void CheckIter(rocksdb_iterator_t* iter,
|
||||
const char* key, const char* val) {
|
||||
size_t len;
|
||||
@@ -176,6 +192,39 @@ static unsigned char CFilterFilter(void* arg, int level, const char* key,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void CFilterFactoryDestroy(void* arg) {}
|
||||
static const char* CFilterFactoryName(void* arg) { return "foo"; }
|
||||
static rocksdb_compactionfilter_t* CFilterCreate(
|
||||
void* arg, rocksdb_compactionfiltercontext_t* context) {
|
||||
return rocksdb_compactionfilter_create(NULL, CFilterDestroy, CFilterFilter,
|
||||
CFilterName);
|
||||
}
|
||||
|
||||
static rocksdb_t* CheckCompaction(rocksdb_t* db, rocksdb_options_t* options,
|
||||
rocksdb_readoptions_t* roptions,
|
||||
rocksdb_writeoptions_t* woptions) {
|
||||
char* err = NULL;
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "foo", "foovalue");
|
||||
rocksdb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "bar", "barvalue");
|
||||
rocksdb_put(db, woptions, "baz", 3, "bazvalue", 8, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "baz", "bazvalue");
|
||||
|
||||
// Force compaction
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
// should have filtered bar, but not foo
|
||||
CheckGet(db, roptions, "foo", "foovalue");
|
||||
CheckGet(db, roptions, "bar", NULL);
|
||||
CheckGet(db, roptions, "baz", "newbazvalue");
|
||||
return db;
|
||||
}
|
||||
|
||||
// Custom merge operator
|
||||
static void MergeOperatorDestroy(void* arg) { }
|
||||
static const char* MergeOperatorName(void* arg) {
|
||||
@@ -299,6 +348,24 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
}
|
||||
|
||||
StartPhase("writebatch_rep");
|
||||
{
|
||||
rocksdb_writebatch_t* wb1 = rocksdb_writebatch_create();
|
||||
rocksdb_writebatch_put(wb1, "baz", 3, "d", 1);
|
||||
rocksdb_writebatch_put(wb1, "quux", 4, "e", 1);
|
||||
rocksdb_writebatch_delete(wb1, "quux", 4);
|
||||
size_t repsize1 = 0;
|
||||
const char* rep = rocksdb_writebatch_data(wb1, &repsize1);
|
||||
rocksdb_writebatch_t* wb2 = rocksdb_writebatch_create_from(rep, repsize1);
|
||||
CheckCondition(rocksdb_writebatch_count(wb1) ==
|
||||
rocksdb_writebatch_count(wb2));
|
||||
size_t repsize2 = 0;
|
||||
CheckCondition(
|
||||
memcmp(rep, rocksdb_writebatch_data(wb2, &repsize2), repsize1) == 0);
|
||||
rocksdb_writebatch_destroy(wb1);
|
||||
rocksdb_writebatch_destroy(wb2);
|
||||
}
|
||||
|
||||
StartPhase("iter");
|
||||
{
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
|
||||
@@ -431,6 +498,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
StartPhase("compaction_filter");
|
||||
{
|
||||
rocksdb_options_t* options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options, 1);
|
||||
rocksdb_compactionfilter_t* cfilter;
|
||||
cfilter = rocksdb_compactionfilter_create(NULL, CFilterDestroy,
|
||||
CFilterFilter, CFilterName);
|
||||
@@ -438,26 +507,28 @@ int main(int argc, char** argv) {
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
rocksdb_options_set_compaction_filter(options, cfilter);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "foo", "foovalue");
|
||||
rocksdb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "bar", "barvalue");
|
||||
rocksdb_put(db, woptions, "baz", 3, "bazvalue", 8, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "baz", "bazvalue");
|
||||
|
||||
// Force compaction
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
// should have filtered bar, but not foo
|
||||
CheckGet(db, roptions, "foo", "foovalue");
|
||||
CheckGet(db, roptions, "bar", NULL);
|
||||
CheckGet(db, roptions, "baz", "newbazvalue");
|
||||
db = CheckCompaction(db, options, roptions, woptions);
|
||||
|
||||
rocksdb_options_set_compaction_filter(options, NULL);
|
||||
rocksdb_compactionfilter_destroy(cfilter);
|
||||
rocksdb_options_destroy(options);
|
||||
}
|
||||
|
||||
StartPhase("compaction_filter_factory");
|
||||
{
|
||||
rocksdb_options_t* options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options, 1);
|
||||
rocksdb_compactionfilterfactory_t* factory;
|
||||
factory = rocksdb_compactionfilterfactory_create(
|
||||
NULL, CFilterFactoryDestroy, CFilterCreate, CFilterFactoryName);
|
||||
// Create new database
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
rocksdb_options_set_compaction_filter_factory(options, factory);
|
||||
db = CheckCompaction(db, options, roptions, woptions);
|
||||
|
||||
rocksdb_options_set_compaction_filter_factory(options, NULL);
|
||||
rocksdb_options_destroy(options);
|
||||
}
|
||||
|
||||
StartPhase("merge_operator");
|
||||
@@ -486,16 +557,93 @@ int main(int argc, char** argv) {
|
||||
|
||||
}
|
||||
|
||||
StartPhase("columnfamilies");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err)
|
||||
|
||||
rocksdb_options_t* db_options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(db_options, 1);
|
||||
db = rocksdb_open(db_options, dbname, &err);
|
||||
CheckNoError(err)
|
||||
rocksdb_column_family_handle_t* cfh;
|
||||
cfh = rocksdb_create_column_family(db, db_options, "cf1", &err);
|
||||
rocksdb_column_family_handle_destroy(cfh);
|
||||
CheckNoError(err);
|
||||
rocksdb_close(db);
|
||||
|
||||
size_t cflen;
|
||||
char** column_fams = rocksdb_list_column_families(db_options, dbname, &cflen, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual("default", column_fams[0], 7);
|
||||
CheckEqual("cf1", column_fams[1], 3);
|
||||
CheckCondition(cflen == 2);
|
||||
rocksdb_list_column_families_destroy(column_fams, cflen);
|
||||
|
||||
rocksdb_options_t* cf_options = rocksdb_options_create();
|
||||
|
||||
const char* cf_names[2] = {"default", "cf1"};
|
||||
const rocksdb_options_t* cf_opts[2] = {cf_options, cf_options};
|
||||
rocksdb_column_family_handle_t* handles[2];
|
||||
db = rocksdb_open_column_families(db_options, dbname, 2, cf_names, cf_opts, handles, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_put_cf(db, woptions, handles[1], "foo", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
CheckGetCF(db, roptions, handles[1], "foo", "hello");
|
||||
|
||||
rocksdb_delete_cf(db, woptions, handles[1], "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
CheckGetCF(db, roptions, handles[1], "foo", NULL);
|
||||
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "baz", 3, "a", 1);
|
||||
rocksdb_writebatch_clear(wb);
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
|
||||
rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
|
||||
rocksdb_write(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGetCF(db, roptions, handles[1], "baz", NULL);
|
||||
CheckGetCF(db, roptions, handles[1], "bar", NULL);
|
||||
CheckGetCF(db, roptions, handles[1], "box", "c");
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator_cf(db, roptions, handles[1]);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
|
||||
int i;
|
||||
for (i = 0; rocksdb_iter_valid(iter) != 0; rocksdb_iter_next(iter)) {
|
||||
i++;
|
||||
}
|
||||
CheckCondition(i == 1);
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
|
||||
rocksdb_drop_column_family(db, handles[1], &err);
|
||||
CheckNoError(err);
|
||||
for (i = 0; i < 2; i++) {
|
||||
rocksdb_column_family_handle_destroy(handles[i]);
|
||||
}
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
rocksdb_options_destroy(db_options);
|
||||
rocksdb_options_destroy(cf_options);
|
||||
}
|
||||
|
||||
StartPhase("prefix");
|
||||
{
|
||||
// Create new database
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
|
||||
rocksdb_filterpolicy_t* policy = rocksdb_filterpolicy_create_bloom(10);
|
||||
rocksdb_options_set_filter_policy(options, policy);
|
||||
rocksdb_options_set_prefix_extractor(options, rocksdb_slicetransform_create_fixed_prefix(3));
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 50000, 4, 4);
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
|
||||
rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
|
||||
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
@@ -533,6 +681,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_filterpolicy_destroy(policy);
|
||||
}
|
||||
|
||||
|
||||
StartPhase("cleanup");
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_destroy(options);
|
||||
|
||||
+38
-3
@@ -224,8 +224,7 @@ ColumnFamilyData::ColumnFamilyData(const std::string& dbname, uint32_t id,
|
||||
if (dummy_versions != nullptr) {
|
||||
internal_stats_.reset(new InternalStats(
|
||||
options_.num_levels, db_options->env, db_options->statistics.get()));
|
||||
table_cache_.reset(
|
||||
new TableCache(dbname, &options_, storage_options, table_cache));
|
||||
table_cache_.reset(new TableCache(&options_, storage_options, table_cache));
|
||||
if (options_.compaction_style == kCompactionStyleUniversal) {
|
||||
compaction_picker_.reset(
|
||||
new UniversalCompactionPicker(&options_, &internal_comparator_));
|
||||
@@ -243,6 +242,8 @@ ColumnFamilyData::ColumnFamilyData(const std::string& dbname, uint32_t id,
|
||||
const ColumnFamilyOptions* cf_options = &options_;
|
||||
cf_options->Dump(options_.info_log.get());
|
||||
}
|
||||
|
||||
RecalculateWriteStallConditions();
|
||||
}
|
||||
|
||||
// DB mutex held
|
||||
@@ -295,6 +296,35 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
}
|
||||
}
|
||||
|
||||
void ColumnFamilyData::RecalculateWriteStallConditions() {
|
||||
need_wait_for_num_memtables_ =
|
||||
(imm()->size() == options()->max_write_buffer_number - 1);
|
||||
|
||||
if (current_ != nullptr) {
|
||||
need_wait_for_num_level0_files_ =
|
||||
(current_->NumLevelFiles(0) >= options()->level0_stop_writes_trigger);
|
||||
} else {
|
||||
need_wait_for_num_level0_files_ = false;
|
||||
}
|
||||
|
||||
RecalculateWriteStallRateLimitsConditions();
|
||||
}
|
||||
|
||||
void ColumnFamilyData::RecalculateWriteStallRateLimitsConditions() {
|
||||
if (current_ != nullptr) {
|
||||
exceeds_hard_rate_limit_ =
|
||||
(options()->hard_rate_limit > 1.0 &&
|
||||
current_->MaxCompactionScore() > options()->hard_rate_limit);
|
||||
|
||||
exceeds_soft_rate_limit_ =
|
||||
(options()->soft_rate_limit > 0.0 &&
|
||||
current_->MaxCompactionScore() > options()->soft_rate_limit);
|
||||
} else {
|
||||
exceeds_hard_rate_limit_ = false;
|
||||
exceeds_soft_rate_limit_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
const EnvOptions* ColumnFamilyData::soptions() const {
|
||||
return &(column_family_set_->storage_options_);
|
||||
}
|
||||
@@ -316,7 +346,9 @@ void ColumnFamilyData::CreateNewMemtable() {
|
||||
}
|
||||
|
||||
Compaction* ColumnFamilyData::PickCompaction(LogBuffer* log_buffer) {
|
||||
return compaction_picker_->PickCompaction(current_, log_buffer);
|
||||
auto result = compaction_picker_->PickCompaction(current_, log_buffer);
|
||||
RecalculateWriteStallRateLimitsConditions();
|
||||
return result;
|
||||
}
|
||||
|
||||
Compaction* ColumnFamilyData::CompactRange(int input_level, int output_level,
|
||||
@@ -420,6 +452,9 @@ SuperVersion* ColumnFamilyData::InstallSuperVersion(
|
||||
if (column_family_set_->db_options_->allow_thread_local) {
|
||||
ResetThreadLocalSuperVersions();
|
||||
}
|
||||
|
||||
RecalculateWriteStallConditions();
|
||||
|
||||
if (old_superversion != nullptr && old_superversion->Unref()) {
|
||||
old_superversion->Cleanup();
|
||||
return old_superversion; // will let caller delete outside of mutex
|
||||
|
||||
@@ -229,6 +229,22 @@ class ColumnFamilyData {
|
||||
return need_slowdown_for_num_level0_files_;
|
||||
}
|
||||
|
||||
bool NeedWaitForNumLevel0Files() const {
|
||||
return need_wait_for_num_level0_files_;
|
||||
}
|
||||
|
||||
bool NeedWaitForNumMemtables() const {
|
||||
return need_wait_for_num_memtables_;
|
||||
}
|
||||
|
||||
bool ExceedsSoftRateLimit() const {
|
||||
return exceeds_soft_rate_limit_;
|
||||
}
|
||||
|
||||
bool ExceedsHardRateLimit() const {
|
||||
return exceeds_hard_rate_limit_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(const std::string& dbname, uint32_t id,
|
||||
@@ -238,6 +254,14 @@ class ColumnFamilyData {
|
||||
const EnvOptions& storage_options,
|
||||
ColumnFamilySet* column_family_set);
|
||||
|
||||
// Recalculate some small conditions, which are changed only during
|
||||
// compaction, adding new memtable and/or
|
||||
// recalculation of compaction score. These values are used in
|
||||
// DBImpl::MakeRoomForWrite function to decide, if it need to make
|
||||
// a write stall
|
||||
void RecalculateWriteStallConditions();
|
||||
void RecalculateWriteStallRateLimitsConditions();
|
||||
|
||||
uint32_t id_;
|
||||
const std::string name_;
|
||||
Version* dummy_versions_; // Head of circular doubly-linked list of versions.
|
||||
@@ -282,6 +306,22 @@ class ColumnFamilyData {
|
||||
// we have too many level 0 files
|
||||
bool need_slowdown_for_num_level0_files_;
|
||||
|
||||
// These 4 variables are updated only after compaction,
|
||||
// adding new memtable, flushing memtables to files
|
||||
// and/or add recalculation of compaction score.
|
||||
// That's why theirs values are cached in ColumnFamilyData.
|
||||
// Recalculation is made by RecalculateWriteStallConditions and
|
||||
// RecalculateWriteStallRateLimitsConditions function. They are used
|
||||
// in DBImpl::MakeRoomForWrite function to decide, if it need
|
||||
// to sleep during write operation
|
||||
bool need_wait_for_num_memtables_;
|
||||
|
||||
bool need_wait_for_num_level0_files_;
|
||||
|
||||
bool exceeds_hard_rate_limit_;
|
||||
|
||||
bool exceeds_soft_rate_limit_;
|
||||
|
||||
// An object that keeps all the compaction stats
|
||||
// and picks the next compaction
|
||||
std::unique_ptr<CompactionPicker> compaction_picker_;
|
||||
|
||||
@@ -749,7 +749,6 @@ TEST(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
default_cf.filter_policy = nullptr;
|
||||
default_cf.no_block_cache = true;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
default_cf.disable_seek_compaction = false;
|
||||
|
||||
one.compaction_style = kCompactionStyleUniversal;
|
||||
// trigger compaction if there are >= 4 files
|
||||
@@ -764,33 +763,6 @@ TEST(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
|
||||
Reopen({default_cf, one, two});
|
||||
|
||||
// SETUP column family "default" - test read compaction
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
PutRandomData(0, 1, 4096);
|
||||
ASSERT_OK(Flush(0));
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel(0));
|
||||
// write 8MB
|
||||
PutRandomData(0, 2000, 4096);
|
||||
ASSERT_OK(Flush(0));
|
||||
// clear levels 0 and 1
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[0]);
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[0]);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), 0);
|
||||
// write some new keys into level 0 and 1
|
||||
PutRandomData(0, 1024, 512);
|
||||
ASSERT_OK(Flush(0));
|
||||
WaitForCompaction();
|
||||
PutRandomData(0, 10, 512);
|
||||
ASSERT_OK(Flush(0));
|
||||
// remember number of files in each level
|
||||
int l1 = NumTableFilesAtLevel(0, 0);
|
||||
int l2 = NumTableFilesAtLevel(1, 0);
|
||||
int l3 = NumTableFilesAtLevel(2, 0);
|
||||
ASSERT_NE(l1, 0);
|
||||
ASSERT_NE(l2, 0);
|
||||
ASSERT_NE(l3, 0);
|
||||
|
||||
// SETUP column family "one" -- universal style
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger - 1; ++i) {
|
||||
PutRandomData(1, 11, 10000);
|
||||
@@ -805,12 +777,6 @@ TEST(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
ASSERT_EQ(std::to_string(i + 1), FilesPerLevel(2));
|
||||
}
|
||||
|
||||
// TRIGGER compaction "default"
|
||||
// read a bunch of times, trigger read compaction
|
||||
for (int i = 0; i < 200000; ++i) {
|
||||
Get(0, std::to_string(i));
|
||||
}
|
||||
|
||||
// TRIGGER compaction "one"
|
||||
PutRandomData(1, 12, 10000);
|
||||
|
||||
@@ -820,13 +786,6 @@ TEST(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
// WAIT for compactions
|
||||
WaitForCompaction();
|
||||
|
||||
// VERIFY compaction "default"
|
||||
// verify that the number of files have decreased
|
||||
// in some level, indicating that there was a compaction
|
||||
ASSERT_TRUE(NumTableFilesAtLevel(0, 0) < l1 ||
|
||||
NumTableFilesAtLevel(1, 0) < l2 ||
|
||||
NumTableFilesAtLevel(2, 0) < l3);
|
||||
|
||||
// VERIFY compaction "one"
|
||||
ASSERT_EQ("1", FilesPerLevel(1));
|
||||
|
||||
|
||||
+4
-2
@@ -29,7 +29,8 @@ static uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
|
||||
Compaction::Compaction(Version* input_version, int level, int out_level,
|
||||
uint64_t target_file_size,
|
||||
uint64_t max_grandparent_overlap_bytes,
|
||||
bool seek_compaction, bool enable_compression,
|
||||
uint32_t output_path_id,
|
||||
CompressionType output_compression, bool seek_compaction,
|
||||
bool deletion_compaction)
|
||||
: level_(level),
|
||||
out_level_(out_level),
|
||||
@@ -38,8 +39,9 @@ Compaction::Compaction(Version* input_version, int level, int out_level,
|
||||
input_version_(input_version),
|
||||
number_levels_(input_version_->NumberLevels()),
|
||||
cfd_(input_version_->cfd_),
|
||||
output_path_id_(output_path_id),
|
||||
output_compression_(output_compression),
|
||||
seek_compaction_(seek_compaction),
|
||||
enable_compression_(enable_compression),
|
||||
deletion_compaction_(deletion_compaction),
|
||||
grandparent_index_(0),
|
||||
seen_key_(false),
|
||||
|
||||
+10
-6
@@ -47,10 +47,13 @@ class Compaction {
|
||||
// Maximum size of files to build during this compaction.
|
||||
uint64_t MaxOutputFileSize() const { return max_output_file_size_; }
|
||||
|
||||
// Whether compression will be enabled for compaction outputs
|
||||
bool enable_compression() const { return enable_compression_; }
|
||||
// What compression for output
|
||||
CompressionType OutputCompressionType() const { return output_compression_; }
|
||||
|
||||
// Is this a trivial compaction that can be implemented by just
|
||||
// Whether need to write output file to second DB path.
|
||||
uint32_t GetOutputPathId() const { return output_path_id_; }
|
||||
|
||||
// Is this a trivial compaction that can be implemented by just
|
||||
// moving a single input file to the next level (no merging or splitting)
|
||||
bool IsTrivialMove() const;
|
||||
|
||||
@@ -104,8 +107,8 @@ class Compaction {
|
||||
|
||||
Compaction(Version* input_version, int level, int out_level,
|
||||
uint64_t target_file_size, uint64_t max_grandparent_overlap_bytes,
|
||||
bool seek_compaction = false, bool enable_compression = true,
|
||||
bool deletion_compaction = false);
|
||||
uint32_t output_path_id, CompressionType output_compression,
|
||||
bool seek_compaction = false, bool deletion_compaction = false);
|
||||
|
||||
int level_;
|
||||
int out_level_; // levels to which output files are stored
|
||||
@@ -116,8 +119,9 @@ class Compaction {
|
||||
int number_levels_;
|
||||
ColumnFamilyData* cfd_;
|
||||
|
||||
uint32_t output_path_id_;
|
||||
CompressionType output_compression_;
|
||||
bool seek_compaction_;
|
||||
bool enable_compression_;
|
||||
// if true, just delete files in inputs_[0]
|
||||
bool deletion_compaction_;
|
||||
|
||||
|
||||
+127
-142
@@ -12,17 +12,43 @@
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <limits>
|
||||
#include "db/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/statistics.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
// Determine compression type, based on user options, level of the output
|
||||
// file and whether compression is disabled.
|
||||
// If enable_compression is false, then compression is always disabled no
|
||||
// matter what the values of the other two parameters are.
|
||||
// Otherwise, the compression type is determined based on options and level.
|
||||
CompressionType GetCompressionType(const Options& options, int level,
|
||||
const bool enable_compression = true) {
|
||||
if (!enable_compression) {
|
||||
// disable compression
|
||||
return kNoCompression;
|
||||
}
|
||||
// If the use has specified a different compression level for each level,
|
||||
// then pick the compresison for that level.
|
||||
if (!options.compression_per_level.empty()) {
|
||||
const int n = options.compression_per_level.size() - 1;
|
||||
// It is possible for level_ to be -1; in that case, we use level
|
||||
// 0's compression. This occurs mostly in backwards compatibility
|
||||
// situations when the builder doesn't know what level the file
|
||||
// belongs to. Likewise, if level_ is beyond the end of the
|
||||
// specified compression levels, use the last value.
|
||||
return options.compression_per_level[std::max(0, std::min(level, n))];
|
||||
} else {
|
||||
return options.compression;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
|
||||
uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
|
||||
uint64_t sum = 0;
|
||||
for (size_t i = 0; i < files.size() && files[i]; i++) {
|
||||
sum += files[i]->fd.GetFileSize();
|
||||
sum += files[i]->compensated_file_size;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@@ -80,7 +106,7 @@ void CompactionPicker::SizeBeingCompacted(std::vector<uint64_t>& sizes) {
|
||||
for (auto c : compactions_in_progress_[level]) {
|
||||
assert(c->level() == level);
|
||||
for (int i = 0; i < c->num_input_files(0); i++) {
|
||||
total += c->input(0, i)->fd.GetFileSize();
|
||||
total += c->input(0, i)->compensated_file_size;
|
||||
}
|
||||
}
|
||||
sizes[level] = total;
|
||||
@@ -261,9 +287,9 @@ void CompactionPicker::SetupOtherInputs(Compaction* c) {
|
||||
std::vector<FileMetaData*> expanded0;
|
||||
c->input_version_->GetOverlappingInputs(
|
||||
level, &all_start, &all_limit, &expanded0, c->base_index_, nullptr);
|
||||
const uint64_t inputs0_size = TotalFileSize(c->inputs_[0]);
|
||||
const uint64_t inputs1_size = TotalFileSize(c->inputs_[1]);
|
||||
const uint64_t expanded0_size = TotalFileSize(expanded0);
|
||||
const uint64_t inputs0_size = TotalCompensatedFileSize(c->inputs_[0]);
|
||||
const uint64_t inputs1_size = TotalCompensatedFileSize(c->inputs_[1]);
|
||||
const uint64_t expanded0_size = TotalCompensatedFileSize(expanded0);
|
||||
uint64_t limit = ExpandedCompactionByteSizeLimit(level);
|
||||
if (expanded0.size() > c->inputs_[0].size() &&
|
||||
inputs1_size + expanded0_size < limit &&
|
||||
@@ -278,14 +304,12 @@ void CompactionPicker::SetupOtherInputs(Compaction* c) {
|
||||
if (expanded1.size() == c->inputs_[1].size() &&
|
||||
!FilesInCompaction(expanded1)) {
|
||||
Log(options_->info_log,
|
||||
"[%s] Expanding@%lu %lu+%lu (%lu+%lu bytes) to %lu+%lu (%lu+%lu "
|
||||
"bytes)\n",
|
||||
c->column_family_data()->GetName().c_str(), (unsigned long)level,
|
||||
(unsigned long)(c->inputs_[0].size()),
|
||||
(unsigned long)(c->inputs_[1].size()), (unsigned long)inputs0_size,
|
||||
(unsigned long)inputs1_size, (unsigned long)(expanded0.size()),
|
||||
(unsigned long)(expanded1.size()), (unsigned long)expanded0_size,
|
||||
(unsigned long)inputs1_size);
|
||||
"[%s] Expanding@%d %zu+%zu (%" PRIu64 "+%" PRIu64
|
||||
" bytes) to %zu+%zu (%" PRIu64 "+%" PRIu64 "bytes)\n",
|
||||
c->column_family_data()->GetName().c_str(), level,
|
||||
c->inputs_[0].size(), c->inputs_[1].size(), inputs0_size,
|
||||
inputs1_size, expanded0.size(), expanded1.size(), expanded0_size,
|
||||
inputs1_size);
|
||||
smallest = new_start;
|
||||
largest = new_limit;
|
||||
c->inputs_[0] = expanded0;
|
||||
@@ -335,7 +359,7 @@ Compaction* CompactionPicker::CompactRange(Version* version, int input_level,
|
||||
MaxFileSizeForLevel(input_level) * options_->source_compaction_factor;
|
||||
uint64_t total = 0;
|
||||
for (size_t i = 0; i + 1 < inputs.size(); ++i) {
|
||||
uint64_t s = inputs[i]->fd.GetFileSize();
|
||||
uint64_t s = inputs[i]->compensated_file_size;
|
||||
total += s;
|
||||
if (total >= limit) {
|
||||
**compaction_end = inputs[i + 1]->smallest;
|
||||
@@ -347,7 +371,8 @@ Compaction* CompactionPicker::CompactRange(Version* version, int input_level,
|
||||
}
|
||||
Compaction* c = new Compaction(version, input_level, output_level,
|
||||
MaxFileSizeForLevel(output_level),
|
||||
MaxGrandParentOverlapBytes(input_level));
|
||||
MaxGrandParentOverlapBytes(input_level), 0,
|
||||
GetCompressionType(*options_, output_level));
|
||||
|
||||
c->inputs_[0] = inputs;
|
||||
if (ExpandWhileOverlapping(c) == false) {
|
||||
@@ -407,31 +432,6 @@ Compaction* LevelCompactionPicker::PickCompaction(Version* version,
|
||||
}
|
||||
}
|
||||
|
||||
// Find compactions needed by seeks
|
||||
FileMetaData* f = version->file_to_compact_;
|
||||
if (c == nullptr && f != nullptr && !f->being_compacted) {
|
||||
|
||||
level = version->file_to_compact_level_;
|
||||
int parent_index = -1;
|
||||
|
||||
// Only allow one level 0 compaction at a time.
|
||||
// Do not pick this file if its parents at level+1 are being compacted.
|
||||
if (level != 0 || compactions_in_progress_[0].empty()) {
|
||||
if (!ParentRangeInCompaction(version, &f->smallest, &f->largest, level,
|
||||
&parent_index)) {
|
||||
c = new Compaction(version, level, level + 1,
|
||||
MaxFileSizeForLevel(level + 1),
|
||||
MaxGrandParentOverlapBytes(level), true);
|
||||
c->inputs_[0].push_back(f);
|
||||
c->parent_index_ = parent_index;
|
||||
c->input_version_->file_to_compact_ = nullptr;
|
||||
if (ExpandWhileOverlapping(c) == false) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -492,7 +492,8 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(Version* version,
|
||||
assert(level >= 0);
|
||||
assert(level + 1 < NumberLevels());
|
||||
c = new Compaction(version, level, level + 1, MaxFileSizeForLevel(level + 1),
|
||||
MaxGrandParentOverlapBytes(level));
|
||||
MaxGrandParentOverlapBytes(level), 0,
|
||||
GetCompressionType(*options_, level + 1));
|
||||
c->score_ = score;
|
||||
|
||||
// Pick the largest file in this level that is not already
|
||||
@@ -507,12 +508,12 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(Version* version,
|
||||
int index = file_size[i];
|
||||
FileMetaData* f = c->input_version_->files_[level][index];
|
||||
|
||||
// check to verify files are arranged in descending size
|
||||
assert(
|
||||
(i == file_size.size() - 1) ||
|
||||
(i >= Version::number_of_files_to_sort_ - 1) ||
|
||||
(f->fd.GetFileSize() >=
|
||||
c->input_version_->files_[level][file_size[i + 1]]->fd.GetFileSize()));
|
||||
// Check to verify files are arranged in descending compensated size.
|
||||
assert((i == file_size.size() - 1) ||
|
||||
(i >= Version::number_of_files_to_sort_ - 1) ||
|
||||
(f->compensated_file_size >=
|
||||
c->input_version_->files_[level][file_size[i + 1]]->
|
||||
compensated_file_size));
|
||||
|
||||
// do not pick a file to compact if it is being compacted
|
||||
// from n-1 level.
|
||||
@@ -612,15 +613,9 @@ Compaction* UniversalCompactionPicker::PickCompaction(Version* version,
|
||||
newerfile = f;
|
||||
}
|
||||
|
||||
// The files are sorted from newest first to oldest last.
|
||||
std::vector<int>& file_by_time = c->input_version_->files_by_size_[level];
|
||||
|
||||
// Is the earliest file part of this compaction?
|
||||
int last_index = file_by_time[file_by_time.size()-1];
|
||||
FileMetaData* last_file = c->input_version_->files_[level][last_index];
|
||||
if (c->inputs_[0][c->inputs_[0].size()-1] == last_file) {
|
||||
c->bottommost_level_ = true;
|
||||
}
|
||||
FileMetaData* last_file = c->input_version_->files_[level].back();
|
||||
c->bottommost_level_ = c->inputs_[0].back() == last_file;
|
||||
|
||||
// update statistics
|
||||
MeasureTime(options_->statistics.get(), NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
@@ -655,12 +650,12 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
options_->compaction_options_universal.max_merge_width;
|
||||
|
||||
// The files are sorted from newest first to oldest last.
|
||||
std::vector<int>& file_by_time = version->files_by_size_[level];
|
||||
const auto& files = version->files_[level];
|
||||
|
||||
FileMetaData* f = nullptr;
|
||||
bool done = false;
|
||||
int start_index = 0;
|
||||
unsigned int candidate_count = 0;
|
||||
assert(file_by_time.size() == version->files_[level].size());
|
||||
|
||||
unsigned int max_files_to_compact = std::min(max_merge_width,
|
||||
max_number_of_files_to_compact);
|
||||
@@ -668,42 +663,38 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
|
||||
// Considers a candidate file only if it is smaller than the
|
||||
// total size accumulated so far.
|
||||
for (unsigned int loop = 0; loop < file_by_time.size(); loop++) {
|
||||
for (unsigned int loop = 0; loop < files.size(); loop++) {
|
||||
|
||||
candidate_count = 0;
|
||||
|
||||
// Skip files that are already being compacted
|
||||
for (f = nullptr; loop < file_by_time.size(); loop++) {
|
||||
int index = file_by_time[loop];
|
||||
f = version->files_[level][index];
|
||||
for (f = nullptr; loop < files.size(); loop++) {
|
||||
f = files[loop];
|
||||
|
||||
if (!f->being_compacted) {
|
||||
candidate_count = 1;
|
||||
break;
|
||||
}
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: file %lu[%d] being compacted, skipping",
|
||||
version->cfd_->GetName().c_str(),
|
||||
(unsigned long)f->fd.GetNumber(), loop);
|
||||
LogToBuffer(log_buffer, "[%s] Universal: file %" PRIu64
|
||||
"[%d] being compacted, skipping",
|
||||
version->cfd_->GetName().c_str(), f->fd.GetNumber(), loop);
|
||||
f = nullptr;
|
||||
}
|
||||
|
||||
// This file is not being compacted. Consider it as the
|
||||
// first candidate to be compacted.
|
||||
uint64_t candidate_size = f != nullptr ? f->fd.GetFileSize() : 0;
|
||||
uint64_t candidate_size = f != nullptr? f->compensated_file_size : 0;
|
||||
if (f != nullptr) {
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: Possible candidate file %lu[%d].",
|
||||
version->cfd_->GetName().c_str(),
|
||||
(unsigned long)f->fd.GetNumber(), loop);
|
||||
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);
|
||||
}
|
||||
|
||||
// Check if the suceeding files need compaction.
|
||||
for (unsigned int i = loop+1;
|
||||
candidate_count < max_files_to_compact && i < file_by_time.size();
|
||||
i++) {
|
||||
int index = file_by_time[i];
|
||||
FileMetaData* f = version->files_[level][index];
|
||||
for (unsigned int i = loop + 1;
|
||||
candidate_count < max_files_to_compact && i < files.size(); i++) {
|
||||
FileMetaData* f = files[i];
|
||||
if (f->being_compacted) {
|
||||
break;
|
||||
}
|
||||
@@ -728,9 +719,9 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
// by the last-resort read amp strategy which disregards size ratios.
|
||||
break;
|
||||
}
|
||||
candidate_size = f->fd.GetFileSize();
|
||||
candidate_size = f->compensated_file_size;
|
||||
} else { // default kCompactionStopStyleTotalSize
|
||||
candidate_size += f->fd.GetFileSize();
|
||||
candidate_size += f->compensated_file_size;
|
||||
}
|
||||
candidate_count++;
|
||||
}
|
||||
@@ -742,14 +733,14 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
break;
|
||||
} else {
|
||||
for (unsigned int i = loop;
|
||||
i < loop + candidate_count && i < file_by_time.size(); i++) {
|
||||
int index = file_by_time[i];
|
||||
FileMetaData* f = version->files_[level][index];
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: Skipping file %lu[%d] with size %lu %d\n",
|
||||
version->cfd_->GetName().c_str(),
|
||||
(unsigned long)f->fd.GetNumber(), i,
|
||||
(unsigned long)f->fd.GetFileSize(), f->being_compacted);
|
||||
i < loop + candidate_count && i < files.size(); i++) {
|
||||
FileMetaData* f = files[i];
|
||||
LogToBuffer(log_buffer, "[%s] Universal: Skipping file %" PRIu64
|
||||
"[%d] with size %" PRIu64
|
||||
" (compensated size %" PRIu64 ") %d\n",
|
||||
version->cfd_->GetName().c_str(), f->fd.GetNumber(), i,
|
||||
f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -765,29 +756,29 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
if (ratio_to_compress >= 0) {
|
||||
uint64_t total_size = version->NumLevelBytes(level);
|
||||
uint64_t older_file_size = 0;
|
||||
for (unsigned int i = file_by_time.size() - 1; i >= first_index_after;
|
||||
i--) {
|
||||
older_file_size +=
|
||||
version->files_[level][file_by_time[i]]->fd.GetFileSize();
|
||||
for (unsigned int i = files.size() - 1;
|
||||
i >= first_index_after; i--) {
|
||||
older_file_size += files[i]->fd.GetFileSize();
|
||||
if (older_file_size * 100L >= total_size * (long) ratio_to_compress) {
|
||||
enable_compression = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Compaction* c =
|
||||
new Compaction(version, level, level, MaxFileSizeForLevel(level),
|
||||
LLONG_MAX, false, enable_compression);
|
||||
Compaction* c = new Compaction(
|
||||
version, level, level, MaxFileSizeForLevel(level), LLONG_MAX, 0,
|
||||
GetCompressionType(*options_, level, enable_compression));
|
||||
c->score_ = score;
|
||||
|
||||
for (unsigned int i = start_index; i < first_index_after; i++) {
|
||||
int index = file_by_time[i];
|
||||
FileMetaData* f = c->input_version_->files_[level][index];
|
||||
FileMetaData* f = c->input_version_->files_[level][i];
|
||||
c->inputs_[0].push_back(f);
|
||||
LogToBuffer(
|
||||
log_buffer, "[%s] Universal: Picking file %lu[%d] with size %lu\n",
|
||||
version->cfd_->GetName().c_str(), (unsigned long)f->fd.GetNumber(), i,
|
||||
(unsigned long)f->fd.GetFileSize());
|
||||
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);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -807,8 +798,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
max_size_amplification_percent;
|
||||
|
||||
// The files are sorted from newest first to oldest last.
|
||||
std::vector<int>& file_by_time = version->files_by_size_[level];
|
||||
assert(file_by_time.size() == version->files_[level].size());
|
||||
const auto& files = version->files_[level];
|
||||
|
||||
unsigned int candidate_count = 0;
|
||||
uint64_t candidate_size = 0;
|
||||
@@ -816,42 +806,39 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
FileMetaData* f = nullptr;
|
||||
|
||||
// Skip files that are already being compacted
|
||||
for (unsigned int loop = 0; loop < file_by_time.size() - 1; loop++) {
|
||||
int index = file_by_time[loop];
|
||||
f = version->files_[level][index];
|
||||
for (unsigned int loop = 0; loop < files.size() - 1; loop++) {
|
||||
f = files[loop];
|
||||
if (!f->being_compacted) {
|
||||
start_index = loop; // Consider this as the first candidate.
|
||||
break;
|
||||
}
|
||||
LogToBuffer(
|
||||
log_buffer, "[%s] Universal: skipping file %lu[%d] compacted %s",
|
||||
version->cfd_->GetName().c_str(), (unsigned long)f->fd.GetNumber(),
|
||||
loop, " cannot be a candidate to reduce size amp.\n");
|
||||
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");
|
||||
f = nullptr;
|
||||
}
|
||||
if (f == nullptr) {
|
||||
return nullptr; // no candidate files
|
||||
}
|
||||
|
||||
LogToBuffer(log_buffer, "[%s] Universal: First candidate file %lu[%d] %s",
|
||||
LogToBuffer(log_buffer, "[%s] Universal: First candidate file %s[%d] %s",
|
||||
version->cfd_->GetName().c_str(),
|
||||
(unsigned long)f->fd.GetNumber(), start_index,
|
||||
" to reduce size amp.\n");
|
||||
FormatFileNumber(f->fd.GetNumber(), f->fd.GetPathId()).c_str(),
|
||||
start_index, " to reduce size amp.\n");
|
||||
|
||||
// keep adding up all the remaining files
|
||||
for (unsigned int loop = start_index; loop < file_by_time.size() - 1;
|
||||
loop++) {
|
||||
int index = file_by_time[loop];
|
||||
f = version->files_[level][index];
|
||||
for (unsigned int loop = start_index; loop < files.size() - 1; loop++) {
|
||||
f = files[loop];
|
||||
if (f->being_compacted) {
|
||||
LogToBuffer(
|
||||
log_buffer, "[%s] Universal: Possible candidate file %lu[%d] %s.",
|
||||
version->cfd_->GetName().c_str(), (unsigned long)f->fd.GetNumber(),
|
||||
loop,
|
||||
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,
|
||||
" is already being compacted. No size amp reduction possible.\n");
|
||||
return nullptr;
|
||||
}
|
||||
candidate_size += f->fd.GetFileSize();
|
||||
candidate_size += f->compensated_file_size;
|
||||
candidate_count++;
|
||||
}
|
||||
if (candidate_count == 0) {
|
||||
@@ -859,42 +846,40 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
}
|
||||
|
||||
// size of earliest file
|
||||
int index = file_by_time[file_by_time.size() - 1];
|
||||
uint64_t earliest_file_size = version->files_[level][index]->fd.GetFileSize();
|
||||
uint64_t earliest_file_size = files.back()->fd.GetFileSize();
|
||||
|
||||
// size amplification = percentage of additional size
|
||||
if (candidate_size * 100 < ratio * earliest_file_size) {
|
||||
LogToBuffer(
|
||||
log_buffer,
|
||||
"[%s] Universal: size amp not needed. newer-files-total-size %lu "
|
||||
"earliest-file-size %lu",
|
||||
version->cfd_->GetName().c_str(), (unsigned long)candidate_size,
|
||||
(unsigned long)earliest_file_size);
|
||||
"[%s] Universal: size amp not needed. newer-files-total-size %" PRIu64
|
||||
"earliest-file-size %" PRIu64,
|
||||
version->cfd_->GetName().c_str(), candidate_size, earliest_file_size);
|
||||
return nullptr;
|
||||
} else {
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: size amp needed. newer-files-total-size %lu "
|
||||
"earliest-file-size %lu",
|
||||
version->cfd_->GetName().c_str(), (unsigned long)candidate_size,
|
||||
(unsigned long)earliest_file_size);
|
||||
LogToBuffer(
|
||||
log_buffer,
|
||||
"[%s] Universal: size amp needed. newer-files-total-size %" PRIu64
|
||||
"earliest-file-size %" PRIu64,
|
||||
version->cfd_->GetName().c_str(), candidate_size, earliest_file_size);
|
||||
}
|
||||
assert(start_index >= 0 && start_index < file_by_time.size() - 1);
|
||||
assert(start_index >= 0 && start_index < files.size() - 1);
|
||||
|
||||
// create a compaction request
|
||||
// We always compact all the files, so always compress.
|
||||
Compaction* c =
|
||||
new Compaction(version, level, level, MaxFileSizeForLevel(level),
|
||||
LLONG_MAX, false, true);
|
||||
LLONG_MAX, 0, GetCompressionType(*options_, level));
|
||||
c->score_ = score;
|
||||
for (unsigned int loop = start_index; loop < file_by_time.size(); loop++) {
|
||||
int index = file_by_time[loop];
|
||||
f = c->input_version_->files_[level][index];
|
||||
for (unsigned int loop = start_index; loop < files.size(); loop++) {
|
||||
f = c->input_version_->files_[level][loop];
|
||||
c->inputs_[0].push_back(f);
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: size amp picking file %lu[%d] with size %lu",
|
||||
version->cfd_->GetName().c_str(),
|
||||
(unsigned long)f->fd.GetNumber(), index,
|
||||
(unsigned long)f->fd.GetFileSize());
|
||||
"[%s] Universal: size amp picking file %" PRIu64 "[%d] "
|
||||
"with size %" PRIu64 " (compensated size %" PRIu64 ")",
|
||||
version->cfd_->GetName().c_str(),
|
||||
f->fd.GetNumber(), loop,
|
||||
f->fd.GetFileSize(), f->compensated_file_size);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -904,7 +889,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(Version* version,
|
||||
assert(version->NumberLevels() == 1);
|
||||
uint64_t total_size = 0;
|
||||
for (const auto& file : version->files_[0]) {
|
||||
total_size += file->fd.GetFileSize();
|
||||
total_size += file->compensated_file_size;
|
||||
}
|
||||
|
||||
if (total_size <= options_->compaction_options_fifo.max_table_files_size ||
|
||||
@@ -926,13 +911,13 @@ Compaction* FIFOCompactionPicker::PickCompaction(Version* version,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(version, 0, 0, 0, 0, false, false,
|
||||
Compaction* c = new Compaction(version, 0, 0, 0, 0, 0, kNoCompression, false,
|
||||
true /* is deletion compaction */);
|
||||
// delete old files (FIFO)
|
||||
for (auto ritr = version->files_[0].rbegin();
|
||||
ritr != version->files_[0].rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
total_size -= f->fd.GetFileSize();
|
||||
total_size -= f->compensated_file_size;
|
||||
c->inputs_[0].push_back(f);
|
||||
char tmp_fsize[16];
|
||||
AppendHumanBytes(f->fd.GetFileSize(), tmp_fsize, sizeof(tmp_fsize));
|
||||
|
||||
+81
-27
@@ -17,6 +17,11 @@ int main() {
|
||||
}
|
||||
#else
|
||||
|
||||
#ifdef NUMA
|
||||
#include <numa.h>
|
||||
#include <numaif.h>
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cstddef>
|
||||
#include <sys/types.h>
|
||||
@@ -173,6 +178,14 @@ DEFINE_double(compression_ratio, 0.5, "Arrange to generate values that shrink"
|
||||
|
||||
DEFINE_bool(histogram, false, "Print histogram of operation timings");
|
||||
|
||||
DEFINE_bool(enable_numa, false,
|
||||
"Make operations aware of NUMA architecture and bind memory "
|
||||
"and cpus corresponding to nodes together. In NUMA, memory "
|
||||
"in same node as CPUs are closer when compared to memory in "
|
||||
"other nodes. Reads can be faster when the process is bound to "
|
||||
"CPU and memory of same node. Use \"$numactl --hardware\" command "
|
||||
"to see NUMA memory architecture.");
|
||||
|
||||
DEFINE_int64(write_buffer_size, rocksdb::Options().write_buffer_size,
|
||||
"Number of bytes to buffer in memtable before compacting");
|
||||
|
||||
@@ -333,9 +346,6 @@ DEFINE_int32(deletepercent, 2, "Percentage of deletes out of reads/writes/"
|
||||
"deletepercent), so deletepercent must be smaller than (100 - "
|
||||
"FLAGS_readwritepercent)");
|
||||
|
||||
DEFINE_int32(disable_seek_compaction, false, "Option to disable compaction"
|
||||
" triggered by read.");
|
||||
|
||||
DEFINE_uint64(delete_obsolete_files_period_micros, 0, "Option to delete "
|
||||
"obsolete files periodically. 0 means that obsolete files are"
|
||||
" deleted after every compaction run.");
|
||||
@@ -553,6 +563,9 @@ static const bool FLAGS_cache_numshardbits_dummy __attribute__((unused)) =
|
||||
static const bool FLAGS_readwritepercent_dummy __attribute__((unused)) =
|
||||
RegisterFlagValidator(&FLAGS_readwritepercent, &ValidateInt32Percent);
|
||||
|
||||
DEFINE_int32(disable_seek_compaction, false,
|
||||
"Not used, left here for backwards compatibility");
|
||||
|
||||
static const bool FLAGS_deletepercent_dummy __attribute__((unused)) =
|
||||
RegisterFlagValidator(&FLAGS_deletepercent, &ValidateInt32Percent);
|
||||
static const bool FLAGS_table_cache_numshardbits_dummy __attribute__((unused)) =
|
||||
@@ -584,9 +597,9 @@ class RandomGenerator {
|
||||
}
|
||||
|
||||
Slice Generate(unsigned int len) {
|
||||
assert(len <= data_.size());
|
||||
if (pos_ + len > data_.size()) {
|
||||
pos_ = 0;
|
||||
assert(len < data_.size());
|
||||
}
|
||||
pos_ += len;
|
||||
return Slice(data_.data() + pos_ - len, len);
|
||||
@@ -664,7 +677,7 @@ class Stats {
|
||||
void SetId(int id) { id_ = id; }
|
||||
void SetExcludeFromMerge() { exclude_from_merge_ = true; }
|
||||
|
||||
void FinishedSingleOp(DB* db) {
|
||||
void FinishedOps(DB* db, int64_t num_ops) {
|
||||
if (FLAGS_histogram) {
|
||||
double now = FLAGS_env->NowMicros();
|
||||
double micros = now - last_op_finish_;
|
||||
@@ -676,7 +689,7 @@ class Stats {
|
||||
last_op_finish_ = now;
|
||||
}
|
||||
|
||||
done_++;
|
||||
done_ += num_ops;
|
||||
if (done_ >= next_report_) {
|
||||
if (!FLAGS_stats_interval) {
|
||||
if (next_report_ < 1000) next_report_ += 100;
|
||||
@@ -722,7 +735,7 @@ class Stats {
|
||||
|
||||
void Report(const Slice& name) {
|
||||
// Pretend at least one op was done in case we are running a benchmark
|
||||
// that does not call FinishedSingleOp().
|
||||
// that does not call FinishedOps().
|
||||
if (done_ < 1) done_ = 1;
|
||||
|
||||
std::string extra;
|
||||
@@ -837,6 +850,15 @@ class Benchmark {
|
||||
int64_t writes_;
|
||||
int64_t readwrites_;
|
||||
int64_t merge_keys_;
|
||||
|
||||
bool SanityCheck() {
|
||||
if (FLAGS_compression_ratio > 1) {
|
||||
fprintf(stderr, "compression_ratio should be between 0 and 1\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrintHeader() {
|
||||
PrintEnvironment();
|
||||
fprintf(stdout, "Keys: %d bytes each\n", FLAGS_key_size);
|
||||
@@ -854,6 +876,18 @@ class Benchmark {
|
||||
* num_)
|
||||
/ 1048576.0));
|
||||
fprintf(stdout, "Write rate limit: %d\n", FLAGS_writes_per_second);
|
||||
if (FLAGS_enable_numa) {
|
||||
fprintf(stderr, "Running in NUMA enabled mode.\n");
|
||||
#ifndef NUMA
|
||||
fprintf(stderr, "NUMA is not defined in the system.\n");
|
||||
exit(1);
|
||||
#else
|
||||
if (numa_available() == -1) {
|
||||
fprintf(stderr, "NUMA is not supported by the system.\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
switch (FLAGS_compression_type_e) {
|
||||
case rocksdb::kNoCompression:
|
||||
fprintf(stdout, "Compression: none\n");
|
||||
@@ -1116,6 +1150,9 @@ class Benchmark {
|
||||
}
|
||||
|
||||
void Run() {
|
||||
if (!SanityCheck()) {
|
||||
exit(1);
|
||||
}
|
||||
PrintHeader();
|
||||
Open();
|
||||
const char* benchmarks = FLAGS_benchmarks.c_str();
|
||||
@@ -1336,7 +1373,25 @@ class Benchmark {
|
||||
shared.start = false;
|
||||
|
||||
ThreadArg* arg = new ThreadArg[n];
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
#ifdef NUMA
|
||||
if (FLAGS_enable_numa) {
|
||||
// Performs a local allocation of memory to threads in numa node.
|
||||
int n_nodes = numa_num_task_nodes(); // Number of nodes in NUMA.
|
||||
numa_exit_on_error = 1;
|
||||
int numa_node = i % n_nodes;
|
||||
bitmask* nodes = numa_allocate_nodemask();
|
||||
numa_bitmask_clearall(nodes);
|
||||
numa_bitmask_setbit(nodes, numa_node);
|
||||
// numa_bind() call binds the process to the node and these
|
||||
// properties are passed on to the thread that is created in
|
||||
// StartThread method called later in the loop.
|
||||
numa_bind(nodes);
|
||||
numa_set_strict(1);
|
||||
numa_free_nodemask(nodes);
|
||||
}
|
||||
#endif
|
||||
arg[i].bm = this;
|
||||
arg[i].method = method;
|
||||
arg[i].shared = &shared;
|
||||
@@ -1379,7 +1434,7 @@ class Benchmark {
|
||||
uint32_t crc = 0;
|
||||
while (bytes < 500 * 1048576) {
|
||||
crc = crc32c::Value(data.data(), size);
|
||||
thread->stats.FinishedSingleOp(nullptr);
|
||||
thread->stats.FinishedOps(nullptr, 1);
|
||||
bytes += size;
|
||||
}
|
||||
// Print so result is not dead
|
||||
@@ -1398,7 +1453,7 @@ class Benchmark {
|
||||
unsigned int xxh32 = 0;
|
||||
while (bytes < 500 * 1048576) {
|
||||
xxh32 = XXH32(data.data(), size, 0);
|
||||
thread->stats.FinishedSingleOp(nullptr);
|
||||
thread->stats.FinishedOps(nullptr, 1);
|
||||
bytes += size;
|
||||
}
|
||||
// Print so result is not dead
|
||||
@@ -1419,7 +1474,7 @@ class Benchmark {
|
||||
ptr = ap.Acquire_Load();
|
||||
}
|
||||
count++;
|
||||
thread->stats.FinishedSingleOp(nullptr);
|
||||
thread->stats.FinishedOps(nullptr, 1);
|
||||
}
|
||||
if (ptr == nullptr) exit(1); // Disable unused variable warning.
|
||||
}
|
||||
@@ -1460,7 +1515,7 @@ class Benchmark {
|
||||
}
|
||||
produced += compressed.size();
|
||||
bytes += input.size();
|
||||
thread->stats.FinishedSingleOp(nullptr);
|
||||
thread->stats.FinishedOps(nullptr, 1);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
@@ -1541,7 +1596,7 @@ class Benchmark {
|
||||
}
|
||||
delete[] uncompressed;
|
||||
bytes += input.size();
|
||||
thread->stats.FinishedSingleOp(nullptr);
|
||||
thread->stats.FinishedOps(nullptr, 1);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
@@ -1663,7 +1718,6 @@ class Benchmark {
|
||||
options.compression_per_level[i] = FLAGS_compression_type_e;
|
||||
}
|
||||
}
|
||||
options.disable_seek_compaction = FLAGS_disable_seek_compaction;
|
||||
options.delete_obsolete_files_period_micros =
|
||||
FLAGS_delete_obsolete_files_period_micros;
|
||||
options.soft_rate_limit = FLAGS_soft_rate_limit;
|
||||
@@ -1851,9 +1905,9 @@ class Benchmark {
|
||||
GenerateKeyFromInt(key_gens[id]->Next(), FLAGS_num, &key);
|
||||
batch.Put(key, gen.Generate(value_size_));
|
||||
bytes += value_size_ + key_size_;
|
||||
thread->stats.FinishedSingleOp(db_to_write);
|
||||
}
|
||||
s = db_to_write->Write(write_options_, &batch);
|
||||
thread->stats.FinishedOps(db_to_write, entries_per_batch_);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
@@ -1878,7 +1932,7 @@ class Benchmark {
|
||||
int64_t bytes = 0;
|
||||
for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) {
|
||||
bytes += iter->key().size() + iter->value().size();
|
||||
thread->stats.FinishedSingleOp(db);
|
||||
thread->stats.FinishedOps(db, 1);
|
||||
++i;
|
||||
}
|
||||
delete iter;
|
||||
@@ -1901,7 +1955,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.FinishedSingleOp(db_);
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
++i;
|
||||
}
|
||||
delete iter;
|
||||
@@ -1924,7 +1978,7 @@ class Benchmark {
|
||||
if (db->Get(options, key, &value).ok()) {
|
||||
found++;
|
||||
}
|
||||
thread->stats.FinishedSingleOp(db_);
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
}
|
||||
|
||||
char msg[100];
|
||||
@@ -1984,7 +2038,7 @@ class Benchmark {
|
||||
DB* db = SelectDB(thread);
|
||||
Iterator* iter = db->NewIterator(options);
|
||||
delete iter;
|
||||
thread->stats.FinishedSingleOp(db);
|
||||
thread->stats.FinishedOps(db, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2048,7 +2102,7 @@ class Benchmark {
|
||||
if (iter_to_use->Valid() && iter_to_use->key().compare(key) == 0) {
|
||||
found++;
|
||||
}
|
||||
thread->stats.FinishedSingleOp(db_);
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
}
|
||||
delete single_iter;
|
||||
for (auto iter : multi_iters) {
|
||||
@@ -2086,9 +2140,9 @@ class Benchmark {
|
||||
const int64_t k = seq ? i + j : (thread->rand.Next() % FLAGS_num);
|
||||
GenerateKeyFromInt(k, FLAGS_num, &key);
|
||||
batch.Delete(key);
|
||||
thread->stats.FinishedSingleOp(db);
|
||||
}
|
||||
auto s = db->Write(write_options_, &batch);
|
||||
thread->stats.FinishedOps(db, entries_per_batch_);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "del error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
@@ -2148,7 +2202,7 @@ class Benchmark {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
thread->stats.FinishedSingleOp(db_);
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
|
||||
++num_writes;
|
||||
if (writes_per_second_by_10 && num_writes >= writes_per_second_by_10) {
|
||||
@@ -2308,7 +2362,7 @@ class Benchmark {
|
||||
deletes_done++;
|
||||
}
|
||||
|
||||
thread->stats.FinishedSingleOp(db_);
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
}
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg),
|
||||
@@ -2366,7 +2420,7 @@ class Benchmark {
|
||||
put_weight--;
|
||||
writes_done++;
|
||||
}
|
||||
thread->stats.FinishedSingleOp(db);
|
||||
thread->stats.FinishedOps(db, 1);
|
||||
}
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg), "( reads:%" PRIu64 " writes:%" PRIu64 \
|
||||
@@ -2400,7 +2454,7 @@ class Benchmark {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
thread->stats.FinishedSingleOp(db);
|
||||
thread->stats.FinishedOps(db, 1);
|
||||
}
|
||||
char msg[100];
|
||||
snprintf(msg, sizeof(msg),
|
||||
@@ -2447,7 +2501,7 @@ class Benchmark {
|
||||
fprintf(stderr, "put error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
thread->stats.FinishedSingleOp(db_);
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
}
|
||||
|
||||
char msg[100];
|
||||
@@ -2483,7 +2537,7 @@ class Benchmark {
|
||||
fprintf(stderr, "merge error: %s\n", s.ToString().c_str());
|
||||
exit(1);
|
||||
}
|
||||
thread->stats.FinishedSingleOp(db_);
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
}
|
||||
|
||||
// Print some statistics
|
||||
@@ -2544,7 +2598,7 @@ class Benchmark {
|
||||
|
||||
}
|
||||
|
||||
thread->stats.FinishedSingleOp(db_);
|
||||
thread->stats.FinishedOps(db_, 1);
|
||||
}
|
||||
|
||||
char msg[100];
|
||||
|
||||
@@ -98,7 +98,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
|
||||
// Make a set of all of the live *.sst files
|
||||
std::set<uint64_t> live;
|
||||
std::vector<FileDescriptor> live;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
cfd->current()->AddLiveFiles(&live);
|
||||
}
|
||||
@@ -109,7 +109,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
// create names of the live files. The names are not absolute
|
||||
// paths, instead they are relative to dbname_;
|
||||
for (auto live_file : live) {
|
||||
ret.push_back(TableFileName("", live_file));
|
||||
ret.push_back(MakeTableFileName("", live_file.GetNumber()));
|
||||
}
|
||||
|
||||
ret.push_back(CurrentFileName(""));
|
||||
|
||||
+308
-174
File diff suppressed because it is too large
Load Diff
+23
-15
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/stats_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "db/internal_stats.h"
|
||||
|
||||
@@ -198,6 +200,17 @@ class DBImpl : public DB {
|
||||
Status TEST_ReadFirstLine(const std::string& fname, SequenceNumber* sequence);
|
||||
#endif // NDEBUG
|
||||
|
||||
// Structure to store information for candidate files to delete.
|
||||
struct CandidateFileInfo {
|
||||
std::string file_name;
|
||||
uint32_t path_id;
|
||||
CandidateFileInfo(std::string name, uint32_t path)
|
||||
: file_name(name), path_id(path) {}
|
||||
bool operator==(const CandidateFileInfo& other) const {
|
||||
return file_name == other.file_name && path_id == other.path_id;
|
||||
}
|
||||
};
|
||||
|
||||
// needed for CleanupIteratorState
|
||||
struct DeletionState {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
@@ -209,10 +222,10 @@ class DBImpl : public DB {
|
||||
// a list of all files that we'll consider deleting
|
||||
// (every once in a while this is filled up with all files
|
||||
// in the DB directory)
|
||||
std::vector<std::string> candidate_files;
|
||||
std::vector<CandidateFileInfo> candidate_files;
|
||||
|
||||
// the list of all live sst files that cannot be deleted
|
||||
std::vector<uint64_t> sst_live;
|
||||
std::vector<FileDescriptor> sst_live;
|
||||
|
||||
// a list of sst files that we need to delete
|
||||
std::vector<FileMetaData*> sst_delete_files;
|
||||
@@ -334,7 +347,8 @@ class DBImpl : public DB {
|
||||
Status MakeRoomForWrite(ColumnFamilyData* cfd,
|
||||
bool force /* flush even if there is room? */,
|
||||
autovector<SuperVersion*>* superversions_to_free,
|
||||
autovector<log::Writer*>* logs_to_free);
|
||||
autovector<log::Writer*>* logs_to_free,
|
||||
uint64_t expiration_time);
|
||||
|
||||
void BuildBatchGroup(Writer** last_writer,
|
||||
autovector<WriteBatch*>* write_batch_group);
|
||||
@@ -345,6 +359,9 @@ class DBImpl : public DB {
|
||||
// Wait for memtable flushed
|
||||
Status WaitForFlushMemTable(ColumnFamilyData* cfd);
|
||||
|
||||
void RecordFlushIOStats();
|
||||
void RecordCompactionIOStats();
|
||||
|
||||
void MaybeScheduleLogDBDeployStats();
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -501,7 +518,8 @@ class DBImpl : public DB {
|
||||
|
||||
// Set of table files to protect from deletion because they are
|
||||
// part of ongoing compactions.
|
||||
std::set<uint64_t> pending_outputs_;
|
||||
// map from pending file number ID to their path IDs.
|
||||
FileNumToPathIdMap pending_outputs_;
|
||||
|
||||
// At least one compaction or flush job is pending but not yet scheduled
|
||||
// because of the max background thread limit.
|
||||
@@ -566,6 +584,7 @@ class DBImpl : public DB {
|
||||
bool flush_on_destroy_; // Used when disableWAL is true.
|
||||
|
||||
static const int KEEP_LOG_FILE_NUM = 1000;
|
||||
static const uint64_t kNoTimeOut = std::numeric_limits<uint64_t>::max();
|
||||
std::string db_absolute_path_;
|
||||
|
||||
// count of the number of contiguous delaying writes
|
||||
@@ -625,15 +644,4 @@ extern Options SanitizeOptions(const std::string& db,
|
||||
const Options& src);
|
||||
extern DBOptions SanitizeOptions(const std::string& db, const DBOptions& src);
|
||||
|
||||
// Determine compression type, based on user options, level of the output
|
||||
// file and whether compression is disabled.
|
||||
// If enable_compression is false, then compression is always disabled no
|
||||
// matter what the values of the other two parameters are.
|
||||
// Otherwise, the compression type is determined based on options and level.
|
||||
CompressionType GetCompressionType(const Options& options, int level,
|
||||
const bool enable_compression);
|
||||
|
||||
// Determine compression type for L0 file written by memtable flush.
|
||||
CompressionType GetCompressionFlush(const Options& options);
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -65,9 +65,7 @@ Status DBImplReadOnly::Get(const ReadOptions& options,
|
||||
if (super_version->mem->Get(lkey, value, &s, merge_context,
|
||||
*cfd->options())) {
|
||||
} else {
|
||||
Version::GetStats stats;
|
||||
super_version->current->Get(options, lkey, value, &s, &merge_context,
|
||||
&stats);
|
||||
super_version->current->Get(options, lkey, value, &s, &merge_context);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
+390
-77
@@ -27,6 +27,7 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/plain_table_factory.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/statistics.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -98,7 +100,12 @@ class AtomicCounter {
|
||||
count_ = 0;
|
||||
}
|
||||
};
|
||||
} // namespace anon
|
||||
|
||||
static std::string Key(int i) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "key%06d", i);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// Special Env used to delay background operations
|
||||
@@ -130,6 +137,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
anon::AtomicCounter sleep_counter_;
|
||||
|
||||
std::atomic<int64_t> bytes_written_;
|
||||
|
||||
explicit SpecialEnv(Env* base) : EnvWrapper(base) {
|
||||
delay_sstable_sync_.Release_Store(nullptr);
|
||||
no_space_.Release_Store(nullptr);
|
||||
@@ -139,7 +148,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
manifest_sync_error_.Release_Store(nullptr);
|
||||
manifest_write_error_.Release_Store(nullptr);
|
||||
log_write_error_.Release_Store(nullptr);
|
||||
}
|
||||
bytes_written_ = 0;
|
||||
}
|
||||
|
||||
Status NewWritableFile(const std::string& f, unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& soptions) {
|
||||
@@ -158,6 +168,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Drop writes on the floor
|
||||
return Status::OK();
|
||||
} else {
|
||||
env_->bytes_written_ += data.size();
|
||||
return base_->Append(data);
|
||||
}
|
||||
}
|
||||
@@ -169,6 +180,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
}
|
||||
return base_->Sync();
|
||||
}
|
||||
void SetIOPriority(Env::IOPriority pri) {
|
||||
base_->SetIOPriority(pri);
|
||||
}
|
||||
};
|
||||
class ManifestFile : public WritableFile {
|
||||
private:
|
||||
@@ -355,7 +369,10 @@ class DBTest {
|
||||
|
||||
~DBTest() {
|
||||
Close();
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
Options options;
|
||||
options.db_paths.push_back(dbname_);
|
||||
options.db_paths.push_back(dbname_ + "_2");
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
delete env_;
|
||||
delete filter_policy_;
|
||||
}
|
||||
@@ -436,7 +453,8 @@ class DBTest {
|
||||
switch (option_config_) {
|
||||
case kHashSkipList:
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory());
|
||||
options.memtable_factory.reset(
|
||||
NewHashSkipListRepFactory(16));
|
||||
break;
|
||||
case kPlainTableFirstBytePrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
@@ -466,7 +484,7 @@ class DBTest {
|
||||
options.db_log_dir = test::TmpDir();
|
||||
break;
|
||||
case kWalDir:
|
||||
options.wal_dir = "/tmp/wal";
|
||||
options.wal_dir = test::TmpDir() + "/wal";
|
||||
break;
|
||||
case kManifestFileSize:
|
||||
options.max_manifest_file_size = 50; // 50 bytes
|
||||
@@ -487,7 +505,8 @@ class DBTest {
|
||||
break;
|
||||
case kHashLinkList:
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0));
|
||||
options.memtable_factory.reset(
|
||||
NewHashLinkListRepFactory(4, 0, 3, true, 4));
|
||||
break;
|
||||
case kHashCuckoo:
|
||||
options.memtable_factory.reset(
|
||||
@@ -895,6 +914,30 @@ class DBTest {
|
||||
return property;
|
||||
}
|
||||
|
||||
int GetSstFileCount(std::string path) {
|
||||
std::vector<std::string> files;
|
||||
env_->GetChildren(path, &files);
|
||||
|
||||
int sst_count = 0;
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
if (ParseFileName(files[i], &number, &type) && type == kTableFile) {
|
||||
sst_count++;
|
||||
}
|
||||
}
|
||||
return sst_count;
|
||||
}
|
||||
|
||||
void GenerateNewFile(Random* rnd, int* key_idx) {
|
||||
for (int i = 0; i < 11; i++) {
|
||||
ASSERT_OK(Put(Key(*key_idx), RandomString(rnd, (i == 10) ? 1 : 10000)));
|
||||
(*key_idx)++;
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
std::string IterStatus(Iterator* iter) {
|
||||
std::string result;
|
||||
if (iter->Valid()) {
|
||||
@@ -1035,12 +1078,6 @@ class DBTest {
|
||||
|
||||
};
|
||||
|
||||
static std::string Key(int i) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "key%06d", i);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
static long TestGetTickerCount(const Options& options, Tickers ticker_type) {
|
||||
return options.statistics->getTickerCount(ticker_type);
|
||||
}
|
||||
@@ -2318,6 +2355,7 @@ TEST(DBTest, NumImmutableMemTable) {
|
||||
std::string big_value(1000000 * 2, 'x');
|
||||
std::string num;
|
||||
SetPerfLevel(kEnableTime);;
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableTime);
|
||||
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k1", big_value));
|
||||
ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
|
||||
@@ -2380,6 +2418,7 @@ TEST(DBTest, NumImmutableMemTable) {
|
||||
// break if we change the default skiplist implementation
|
||||
ASSERT_EQ(num, "200");
|
||||
SetPerfLevel(kDisable);
|
||||
ASSERT_TRUE(GetPerfLevel() == kDisable);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
@@ -2726,6 +2765,119 @@ TEST(DBTest, CompactionTrigger) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 1), 1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
static const int kCDTValueSize = 1000;
|
||||
static const int kCDTKeysPerBuffer = 4;
|
||||
static const int kCDTNumLevels = 8;
|
||||
Options DeletionTriggerOptions() {
|
||||
Options options;
|
||||
options.compression = kNoCompression;
|
||||
options.write_buffer_size = kCDTKeysPerBuffer * (kCDTValueSize + 24);
|
||||
options.min_write_buffer_number_to_merge = 1;
|
||||
options.num_levels = kCDTNumLevels;
|
||||
options.max_mem_compaction_level = 0;
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.target_file_size_base = options.write_buffer_size * 2;
|
||||
options.target_file_size_multiplier = 2;
|
||||
options.max_bytes_for_level_base =
|
||||
options.target_file_size_base * options.target_file_size_multiplier;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.disable_auto_compactions = false;
|
||||
return options;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
TEST(DBTest, CompactionDeletionTrigger) {
|
||||
Options options = DeletionTriggerOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
for (int tid = 0; tid < 2; ++tid) {
|
||||
uint64_t db_size[2];
|
||||
|
||||
DestroyAndReopen(&options);
|
||||
Random rnd(301);
|
||||
|
||||
const int kTestSize = kCDTKeysPerBuffer * 512;
|
||||
std::vector<std::string> values;
|
||||
for (int k = 0; k < kTestSize; ++k) {
|
||||
values.push_back(RandomString(&rnd, kCDTValueSize));
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
db_size[0] = Size(Key(0), Key(kTestSize - 1));
|
||||
|
||||
for (int k = 0; k < kTestSize; ++k) {
|
||||
ASSERT_OK(Delete(Key(k)));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
db_size[1] = Size(Key(0), Key(kTestSize - 1));
|
||||
|
||||
// must have much smaller db size.
|
||||
ASSERT_GT(db_size[0] / 3, db_size[1]);
|
||||
|
||||
// repeat the test with universal compaction
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 1;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DBTest, CompactionDeletionTriggerReopen) {
|
||||
for (int tid = 0; tid < 2; ++tid) {
|
||||
uint64_t db_size[3];
|
||||
Options options = DeletionTriggerOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
DestroyAndReopen(&options);
|
||||
Random rnd(301);
|
||||
|
||||
// round 1 --- insert key/value pairs.
|
||||
const int kTestSize = kCDTKeysPerBuffer * 512;
|
||||
std::vector<std::string> values;
|
||||
for (int k = 0; k < kTestSize; ++k) {
|
||||
values.push_back(RandomString(&rnd, kCDTValueSize));
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
db_size[0] = Size(Key(0), Key(kTestSize - 1));
|
||||
Close();
|
||||
|
||||
// round 2 --- disable auto-compactions and issue deletions.
|
||||
options.create_if_missing = false;
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(&options);
|
||||
|
||||
for (int k = 0; k < kTestSize; ++k) {
|
||||
ASSERT_OK(Delete(Key(k)));
|
||||
}
|
||||
db_size[1] = Size(Key(0), Key(kTestSize - 1));
|
||||
Close();
|
||||
// as auto_compaction is off, we shouldn't see too much reduce
|
||||
// in db size.
|
||||
ASSERT_LT(db_size[0] / 3, db_size[1]);
|
||||
|
||||
// round 3 --- reopen db with auto_compaction on and see if
|
||||
// deletion compensation still work.
|
||||
options.disable_auto_compactions = false;
|
||||
Reopen(&options);
|
||||
// insert relatively small amount of data to trigger auto compaction.
|
||||
for (int k = 0; k < kTestSize / 10; ++k) {
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
db_size[2] = Size(Key(0), Key(kTestSize - 1));
|
||||
// this time we're expecting significant drop in size.
|
||||
ASSERT_GT(db_size[0] / 3, db_size[2]);
|
||||
|
||||
// repeat the test with universal compaction
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a static filter used for filtering
|
||||
// kvs during the compaction process.
|
||||
static int cfilter_count;
|
||||
@@ -3319,6 +3471,13 @@ TEST(DBTest, UniversalCompactionCompressRatio2) {
|
||||
ASSERT_LT((int)dbfull()->TEST_GetLevel0TotalSize(),
|
||||
120000 * 12 * 0.8 + 120000 * 2);
|
||||
}
|
||||
|
||||
TEST(DBTest, FailMoreDbPaths) {
|
||||
Options options;
|
||||
options.db_paths.push_back(dbname_);
|
||||
options.db_paths.push_back(dbname_ + "_2");
|
||||
ASSERT_TRUE(TryReopen(&options).IsNotSupported());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(DBTest, ConvertCompactionStyle) {
|
||||
@@ -5869,69 +6028,6 @@ TEST(DBTest, ReadFirstRecordCache) {
|
||||
ASSERT_EQ(env_->sequential_read_counter_.Read(), 1);
|
||||
}
|
||||
|
||||
TEST(DBTest, ReadCompaction) {
|
||||
std::string value(4096, '4'); // a string of size 4K
|
||||
{
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.max_open_files = 20; // only 10 file in file-cache
|
||||
options.target_file_size_base = 512;
|
||||
options.write_buffer_size = 64 * 1024;
|
||||
options.filter_policy = nullptr;
|
||||
options.block_size = 4096;
|
||||
options.no_block_cache = true;
|
||||
options.disable_seek_compaction = false;
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, &options);
|
||||
|
||||
// Write 8MB (2000 values, each 4K)
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
|
||||
std::vector<std::string> values;
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
ASSERT_OK(Put(1, Key(i), value));
|
||||
}
|
||||
|
||||
// clear level 0 and 1 if necessary.
|
||||
Flush(1);
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 1), 0);
|
||||
|
||||
// write some new keys into level 0
|
||||
for (int i = 0; i < 2000; i = i + 16) {
|
||||
ASSERT_OK(Put(1, Key(i), value));
|
||||
}
|
||||
Flush(1);
|
||||
|
||||
// Wait for any write compaction to finish
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// remember number of files in each level
|
||||
int l1 = NumTableFilesAtLevel(0, 1);
|
||||
int l2 = NumTableFilesAtLevel(1, 1);
|
||||
int l3 = NumTableFilesAtLevel(2, 1);
|
||||
ASSERT_NE(NumTableFilesAtLevel(0, 1), 0);
|
||||
ASSERT_NE(NumTableFilesAtLevel(1, 1), 0);
|
||||
ASSERT_NE(NumTableFilesAtLevel(2, 1), 0);
|
||||
|
||||
// read a bunch of times, trigger read compaction
|
||||
for (int j = 0; j < 100; j++) {
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
Get(1, Key(i));
|
||||
}
|
||||
}
|
||||
// wait for read compaction to finish
|
||||
env_->SleepForMicroseconds(1000000);
|
||||
|
||||
// verify that the number of files have decreased
|
||||
// in some level, indicating that there was a compaction
|
||||
ASSERT_TRUE(NumTableFilesAtLevel(0, 1) < l1 ||
|
||||
NumTableFilesAtLevel(1, 1) < l2 ||
|
||||
NumTableFilesAtLevel(2, 1) < l3);
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-threaded test:
|
||||
namespace {
|
||||
|
||||
@@ -6641,8 +6737,7 @@ TEST(DBTest, PrefixScan) {
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_background_compactions = 2;
|
||||
options.create_if_missing = true;
|
||||
options.disable_seek_compaction = true;
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory());
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
|
||||
// 11 RAND I/Os
|
||||
DestroyAndReopen(&options);
|
||||
@@ -6799,7 +6894,7 @@ TEST(DBTest, TailingIteratorPrefixSeek) {
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory());
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
DestroyAndReopen(&options);
|
||||
CreateAndReopenWithCF({"pikachu"}, &options);
|
||||
|
||||
@@ -6822,6 +6917,28 @@ TEST(DBTest, TailingIteratorPrefixSeek) {
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
}
|
||||
|
||||
TEST(DBTest, TailingIteratorIncomplete) {
|
||||
CreateAndReopenWithCF({"pikachu"});
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
read_options.read_tier = kBlockCacheTier;
|
||||
|
||||
std::string key("key");
|
||||
std::string value("value");
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
iter->SeekToFirst();
|
||||
// we either see the entry or it's not in cache
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(nullptr, nullptr));
|
||||
iter->SeekToFirst();
|
||||
// should still be true after compaction
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
}
|
||||
|
||||
TEST(DBTest, BlockBasedTablePrefixIndexTest) {
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -6917,6 +7034,202 @@ TEST(DBTest, FIFOCompactionTest) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DBTest, SimpleWriteTimeoutTest) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 100000;
|
||||
options.max_background_flushes = 0;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.max_total_wal_size = std::numeric_limits<uint64_t>::max();
|
||||
WriteOptions write_opt = WriteOptions();
|
||||
write_opt.timeout_hint_us = 0;
|
||||
DestroyAndReopen(&options);
|
||||
// fill the two write buffer
|
||||
ASSERT_OK(Put(Key(1), Key(1) + std::string(100000, 'v'), write_opt));
|
||||
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;
|
||||
ASSERT_TRUE(
|
||||
Put(Key(3), Key(3) + std::string(100000, 'v'), write_opt).IsTimedOut());
|
||||
}
|
||||
|
||||
// Multi-threaded Timeout Test
|
||||
namespace {
|
||||
|
||||
static const int kValueSize = 1000;
|
||||
static const int kWriteBufferSize = 100000;
|
||||
|
||||
struct TimeoutWriterState {
|
||||
int id;
|
||||
DB* db;
|
||||
std::atomic<bool> done;
|
||||
std::map<int, std::string> success_kvs;
|
||||
};
|
||||
|
||||
static void RandomTimeoutWriter(void* arg) {
|
||||
TimeoutWriterState* state = reinterpret_cast<TimeoutWriterState*>(arg);
|
||||
static const uint64_t kTimerBias = 50;
|
||||
int thread_id = state->id;
|
||||
DB* db = state->db;
|
||||
|
||||
Random rnd(1000 + thread_id);
|
||||
WriteOptions write_opt = WriteOptions();
|
||||
write_opt.timeout_hint_us = 500;
|
||||
int timeout_count = 0;
|
||||
int num_keys = kNumKeys * 5;
|
||||
|
||||
for (int k = 0; k < num_keys; ++k) {
|
||||
int key = k + thread_id * num_keys;
|
||||
std::string value = RandomString(&rnd, kValueSize);
|
||||
// only the second-half is randomized
|
||||
if (k > num_keys / 2) {
|
||||
switch (rnd.Next() % 5) {
|
||||
case 0:
|
||||
write_opt.timeout_hint_us = 500 * thread_id;
|
||||
break;
|
||||
case 1:
|
||||
write_opt.timeout_hint_us = num_keys - k;
|
||||
break;
|
||||
case 2:
|
||||
write_opt.timeout_hint_us = 1;
|
||||
break;
|
||||
default:
|
||||
write_opt.timeout_hint_us = 0;
|
||||
state->success_kvs.insert({key, value});
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t time_before_put = db->GetEnv()->NowMicros();
|
||||
Status s = db->Put(write_opt, Key(key), value);
|
||||
uint64_t put_duration = db->GetEnv()->NowMicros() - time_before_put;
|
||||
if (write_opt.timeout_hint_us == 0 ||
|
||||
put_duration + kTimerBias < write_opt.timeout_hint_us) {
|
||||
ASSERT_OK(s);
|
||||
std::string result;
|
||||
}
|
||||
if (s.IsTimedOut()) {
|
||||
timeout_count++;
|
||||
ASSERT_GT(put_duration + kTimerBias, write_opt.timeout_hint_us);
|
||||
}
|
||||
}
|
||||
|
||||
state->done = true;
|
||||
}
|
||||
|
||||
TEST(DBTest, MTRandomTimeoutTest) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.compression = kNoCompression;
|
||||
options.level0_slowdown_writes_trigger = 10;
|
||||
options.level0_stop_writes_trigger = 20;
|
||||
options.write_buffer_size = kWriteBufferSize;
|
||||
DestroyAndReopen(&options);
|
||||
|
||||
TimeoutWriterState thread_states[kNumThreads];
|
||||
for (int tid = 0; tid < kNumThreads; ++tid) {
|
||||
thread_states[tid].id = tid;
|
||||
thread_states[tid].db = db_;
|
||||
thread_states[tid].done = false;
|
||||
env_->StartThread(RandomTimeoutWriter, &thread_states[tid]);
|
||||
}
|
||||
|
||||
for (int tid = 0; tid < kNumThreads; ++tid) {
|
||||
while (thread_states[tid].done == false) {
|
||||
env_->SleepForMicroseconds(100000);
|
||||
}
|
||||
}
|
||||
|
||||
Flush();
|
||||
|
||||
for (int tid = 0; tid < kNumThreads; ++tid) {
|
||||
auto& success_kvs = thread_states[tid].success_kvs;
|
||||
for (auto it = success_kvs.begin(); it != success_kvs.end(); ++it) {
|
||||
ASSERT_EQ(Get(Key(it->first)), it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* This test is not reliable enough as it heavily depends on disk behavior.
|
||||
*
|
||||
TEST(DBTest, RateLimitingTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 1 << 20; // 1MB
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.target_file_size_base = 1 << 20; // 1MB
|
||||
options.max_bytes_for_level_base = 4 << 20; // 4MB
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
options.IncreaseParallelism(4);
|
||||
DestroyAndReopen(&options);
|
||||
|
||||
WriteOptions wo;
|
||||
wo.disableWAL = true;
|
||||
|
||||
// # no rate limiting
|
||||
Random rnd(301);
|
||||
uint64_t start = env_->NowMicros();
|
||||
// Write ~96M data
|
||||
for (int64_t i = 0; i < (96 << 10); ++i) {
|
||||
ASSERT_OK(Put(RandomString(&rnd, 32),
|
||||
RandomString(&rnd, (1 << 10) + 1), wo));
|
||||
}
|
||||
uint64_t elapsed = env_->NowMicros() - start;
|
||||
double raw_rate = env_->bytes_written_ * 1000000 / elapsed;
|
||||
Close();
|
||||
|
||||
// # rate limiting with 0.7 x threshold
|
||||
options.rate_limiter.reset(
|
||||
NewRateLimiter(static_cast<int64_t>(0.7 * raw_rate)));
|
||||
env_->bytes_written_ = 0;
|
||||
DestroyAndReopen(&options);
|
||||
|
||||
start = env_->NowMicros();
|
||||
// Write ~96M data
|
||||
for (int64_t i = 0; i < (96 << 10); ++i) {
|
||||
ASSERT_OK(Put(RandomString(&rnd, 32),
|
||||
RandomString(&rnd, (1 << 10) + 1), wo));
|
||||
}
|
||||
elapsed = env_->NowMicros() - start;
|
||||
Close();
|
||||
ASSERT_TRUE(options.rate_limiter->GetTotalBytesThrough() ==
|
||||
env_->bytes_written_);
|
||||
double ratio = env_->bytes_written_ * 1000000 / elapsed / raw_rate;
|
||||
fprintf(stderr, "write rate ratio = %.2lf, expected 0.7\n", ratio);
|
||||
ASSERT_TRUE(ratio > 0.6 && ratio < 0.8);
|
||||
|
||||
// # rate limiting with half of the raw_rate
|
||||
options.rate_limiter.reset(
|
||||
NewRateLimiter(static_cast<int64_t>(raw_rate / 2)));
|
||||
env_->bytes_written_ = 0;
|
||||
DestroyAndReopen(&options);
|
||||
|
||||
start = env_->NowMicros();
|
||||
// Write ~96M data
|
||||
for (int64_t i = 0; i < (96 << 10); ++i) {
|
||||
ASSERT_OK(Put(RandomString(&rnd, 32),
|
||||
RandomString(&rnd, (1 << 10) + 1), wo));
|
||||
}
|
||||
elapsed = env_->NowMicros() - start;
|
||||
Close();
|
||||
ASSERT_TRUE(options.rate_limiter->GetTotalBytesThrough() ==
|
||||
env_->bytes_written_);
|
||||
ratio = env_->bytes_written_ * 1000000 / elapsed / raw_rate;
|
||||
fprintf(stderr, "write rate ratio = %.2lf, expected 0.5\n", ratio);
|
||||
ASSERT_TRUE(ratio > 0.4 && ratio < 0.6);
|
||||
}
|
||||
*/
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -297,6 +297,13 @@ class IterKey {
|
||||
parsed_key_suffix.sequence, parsed_key_suffix.type);
|
||||
}
|
||||
|
||||
void EncodeLengthPrefixedKey(const Slice& key) {
|
||||
auto size = key.size();
|
||||
EnlargeBufferIfNeeded(size + VarintLength(size));
|
||||
char* ptr = EncodeVarint32(key_, size);
|
||||
memcpy(ptr, key.data(), size);
|
||||
}
|
||||
|
||||
private:
|
||||
char* key_;
|
||||
size_t buf_size_;
|
||||
|
||||
+22
-2
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/logging.h"
|
||||
@@ -66,9 +67,28 @@ std::string ArchivedLogFileName(const std::string& name, uint64_t number) {
|
||||
return MakeFileName(name + "/" + ARCHIVAL_DIR, number, "log");
|
||||
}
|
||||
|
||||
std::string TableFileName(const std::string& name, uint64_t number) {
|
||||
std::string MakeTableFileName(const std::string& path, uint64_t number) {
|
||||
return MakeFileName(path, number, "sst");
|
||||
}
|
||||
|
||||
std::string TableFileName(const std::vector<std::string> db_paths,
|
||||
uint64_t number, uint32_t path_id) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(name, number, "sst");
|
||||
std::string path;
|
||||
if (path_id >= db_paths.size()) {
|
||||
path = db_paths.back();
|
||||
} else {
|
||||
path = db_paths[path_id];
|
||||
}
|
||||
return MakeTableFileName(path, number);
|
||||
}
|
||||
|
||||
std::string FormatFileNumber(uint64_t number, uint32_t path_id) {
|
||||
if (path_id == 0) {
|
||||
return std::to_string(number);
|
||||
} else {
|
||||
return std::to_string(number) + "(path " + std::to_string(path_id) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
std::string DescriptorFileName(const std::string& dbname, uint64_t number) {
|
||||
|
||||
+11
-1
@@ -11,7 +11,9 @@
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
@@ -34,6 +36,9 @@ enum FileType {
|
||||
kIdentityFile
|
||||
};
|
||||
|
||||
// map from file number to path ID.
|
||||
typedef std::unordered_map<uint64_t, uint32_t> FileNumToPathIdMap;
|
||||
|
||||
// Return the name of the log file with the specified number
|
||||
// in the db named by "dbname". The result will be prefixed with
|
||||
// "dbname".
|
||||
@@ -48,10 +53,15 @@ extern std::string ArchivalDirectory(const std::string& dbname);
|
||||
extern std::string ArchivedLogFileName(const std::string& dbname,
|
||||
uint64_t num);
|
||||
|
||||
extern std::string MakeTableFileName(const std::string& name, uint64_t number);
|
||||
|
||||
// Return the name of the sstable with the specified number
|
||||
// in the db named by "dbname". The result will be prefixed with
|
||||
// "dbname".
|
||||
extern std::string TableFileName(const std::string& dbname, uint64_t number);
|
||||
extern std::string TableFileName(const std::vector<std::string> db_paths,
|
||||
uint64_t number, uint32_t path_id);
|
||||
|
||||
extern std::string FormatFileNumber(uint64_t number, uint32_t path_id);
|
||||
|
||||
// Return the name of the descriptor file for the db named by
|
||||
// "dbname" and the specified incarnation number. The result will be
|
||||
|
||||
+3
-1
@@ -108,7 +108,9 @@ TEST(FileNameTest, Construction) {
|
||||
ASSERT_EQ(192U, number);
|
||||
ASSERT_EQ(kLogFile, type);
|
||||
|
||||
fname = TableFileName("bar", 200);
|
||||
fname = TableFileName({"bar"}, 200, 0);
|
||||
std::string fname1 = TableFileName({"foo", "bar"}, 200, 1);
|
||||
ASSERT_EQ(fname, fname1);
|
||||
ASSERT_EQ("bar/", std::string(fname.data(), 4));
|
||||
ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
|
||||
ASSERT_EQ(200U, number);
|
||||
|
||||
+81
-4
@@ -87,7 +87,12 @@ class LevelIterator : public Iterator {
|
||||
return file_iter_->value();
|
||||
}
|
||||
Status status() const override {
|
||||
return status_;
|
||||
if (!status_.ok()) {
|
||||
return status_;
|
||||
} else if (file_iter_ && !file_iter_->status().ok()) {
|
||||
return file_iter_->status();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -189,6 +194,10 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
}
|
||||
}
|
||||
|
||||
Slice user_key;
|
||||
if (!seek_to_first) {
|
||||
user_key = ExtractUserKey(internal_key);
|
||||
}
|
||||
auto* files = sv_->current->files_;
|
||||
for (uint32_t i = 0; i < files[0].size(); ++i) {
|
||||
if (seek_to_first) {
|
||||
@@ -196,7 +205,7 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
} else {
|
||||
// If the target key passes over the larget key, we are sure Next()
|
||||
// won't go over this file.
|
||||
if (user_comparator_->Compare(ExtractUserKey(internal_key),
|
||||
if (user_comparator_->Compare(user_key,
|
||||
files[0][i]->largest.user_key()) > 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -206,16 +215,67 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
immutable_min_heap_.push(l0_iters_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t search_left_bound = 0;
|
||||
int32_t search_right_bound = FileIndexer::kLevelMaxIndex;
|
||||
for (int32_t level = 1; level < sv_->current->NumberLevels(); ++level) {
|
||||
if (files[level].empty()) {
|
||||
search_left_bound = 0;
|
||||
search_right_bound = FileIndexer::kLevelMaxIndex;
|
||||
continue;
|
||||
}
|
||||
assert(level_iters_[level - 1] != nullptr);
|
||||
uint32_t f_idx = 0;
|
||||
if (!seek_to_first) {
|
||||
f_idx = FindFileInRange(
|
||||
files[level], internal_key, 0, files[level].size());
|
||||
// TODO(ljin): remove before committing
|
||||
// f_idx = FindFileInRange(
|
||||
// files[level], internal_key, 0, files[level].size());
|
||||
|
||||
if (search_left_bound == search_right_bound) {
|
||||
f_idx = search_left_bound;
|
||||
} else if (search_left_bound < search_right_bound) {
|
||||
f_idx = FindFileInRange(
|
||||
files[level], internal_key, search_left_bound,
|
||||
search_right_bound == FileIndexer::kLevelMaxIndex ?
|
||||
files[level].size() : search_right_bound);
|
||||
} else {
|
||||
// search_left_bound > search_right_bound
|
||||
// There are only 2 cases this can happen:
|
||||
// (1) target key is smaller than left most file
|
||||
// (2) target key is larger than right most file
|
||||
assert(search_left_bound == (int32_t)files[level].size() ||
|
||||
search_right_bound == -1);
|
||||
if (search_right_bound == -1) {
|
||||
assert(search_left_bound == 0);
|
||||
f_idx = 0;
|
||||
} else {
|
||||
sv_->current->file_indexer_.GetNextLevelIndex(
|
||||
level, files[level].size() - 1,
|
||||
1, 1, &search_left_bound, &search_right_bound);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare hints for the next level
|
||||
if (f_idx < files[level].size()) {
|
||||
int cmp_smallest = user_comparator_->Compare(
|
||||
user_key, files[level][f_idx]->smallest.user_key());
|
||||
int cmp_largest = -1;
|
||||
if (cmp_smallest >= 0) {
|
||||
cmp_smallest = user_comparator_->Compare(
|
||||
user_key, files[level][f_idx]->smallest.user_key());
|
||||
}
|
||||
sv_->current->file_indexer_.GetNextLevelIndex(level, f_idx,
|
||||
cmp_smallest, cmp_largest,
|
||||
&search_left_bound, &search_right_bound);
|
||||
} else {
|
||||
sv_->current->file_indexer_.GetNextLevelIndex(
|
||||
level, files[level].size() - 1,
|
||||
1, 1, &search_left_bound, &search_right_bound);
|
||||
}
|
||||
}
|
||||
|
||||
// Seek
|
||||
if (f_idx < files[level].size()) {
|
||||
level_iters_[level - 1]->SetFileIndex(f_idx);
|
||||
seek_to_first ? level_iters_[level - 1]->SeekToFirst() :
|
||||
@@ -279,6 +339,23 @@ Status ForwardIterator::status() const {
|
||||
} else if (!mutable_iter_->status().ok()) {
|
||||
return mutable_iter_->status();
|
||||
}
|
||||
|
||||
for (auto *it : imm_iters_) {
|
||||
if (it && !it->status().ok()) {
|
||||
return it->status();
|
||||
}
|
||||
}
|
||||
for (auto *it : l0_iters_) {
|
||||
if (it && !it->status().ok()) {
|
||||
return it->status();
|
||||
}
|
||||
}
|
||||
for (auto *it : level_iters_) {
|
||||
if (it && !it->status().ok()) {
|
||||
return it->status();
|
||||
}
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -294,9 +294,9 @@ bool InternalStats::GetProperty(DBPropertyType property_type,
|
||||
snprintf(
|
||||
buf, sizeof(buf),
|
||||
"Amplification interval: %.1f write, %.1f compaction\n",
|
||||
(double)(interval_bytes_written + wal_bytes) /
|
||||
(double)(interval_bytes_written + interval_wal_bytes) /
|
||||
(interval_bytes_new + 1),
|
||||
(double)(interval_bytes_written + interval_bytes_read + wal_bytes) /
|
||||
(double)(interval_bytes_written + interval_bytes_read + interval_wal_bytes) /
|
||||
(interval_bytes_new + 1));
|
||||
value->append(buf);
|
||||
|
||||
|
||||
+2
-2
@@ -118,12 +118,12 @@ class InternalStats {
|
||||
|
||||
void RecordWriteStall(WriteStallType write_stall_type, uint64_t micros) {
|
||||
stall_micros_[write_stall_type] += micros;
|
||||
stall_counts_[write_stall_type]++;
|
||||
++stall_counts_[write_stall_type];
|
||||
}
|
||||
|
||||
void RecordLevelNSlowdown(int level, uint64_t micros) {
|
||||
stall_leveln_slowdown_[level] += micros;
|
||||
stall_leveln_slowdown_count_[level] += micros;
|
||||
++stall_leveln_slowdown_count_[level];
|
||||
}
|
||||
|
||||
uint64_t GetBackgroundErrorCount() const { return bg_error_count_; }
|
||||
|
||||
@@ -51,7 +51,7 @@ void BM_LogAndApply(int iters, int num_base_files) {
|
||||
for (int i = 0; i < num_base_files; i++) {
|
||||
InternalKey start(MakeKey(2 * fnum), 1, kTypeValue);
|
||||
InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion);
|
||||
vbase.AddFile(2, ++fnum, 1 /* file size */, start, limit, 1, 1);
|
||||
vbase.AddFile(2, ++fnum, 0, 1 /* file size */, start, limit, 1, 1);
|
||||
}
|
||||
ASSERT_OK(vset->LogAndApply(default_cfd, &vbase, &mu));
|
||||
}
|
||||
@@ -61,7 +61,7 @@ void BM_LogAndApply(int iters, int num_base_files) {
|
||||
vedit.DeleteFile(2, fnum);
|
||||
InternalKey start(MakeKey(2 * fnum), 1, kTypeValue);
|
||||
InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion);
|
||||
vedit.AddFile(2, ++fnum, 1 /* file size */, start, limit, 1, 1);
|
||||
vedit.AddFile(2, ++fnum, 0, 1 /* file size */, start, limit, 1, 1);
|
||||
vset->LogAndApply(default_cfd, &vedit, &mu);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -55,6 +55,7 @@ MemTable::MemTable(const InternalKeyComparator& cmp, const Options& options)
|
||||
assert(!should_flush_);
|
||||
if (prefix_extractor_ && options.memtable_prefix_bloom_bits > 0) {
|
||||
prefix_bloom_.reset(new DynamicBloom(
|
||||
&arena_,
|
||||
options.memtable_prefix_bloom_bits, options.bloom_locality,
|
||||
options.memtable_prefix_bloom_probes, nullptr,
|
||||
options.memtable_prefix_bloom_huge_page_tlb_size,
|
||||
@@ -449,7 +450,7 @@ void MemTable::Update(SequenceNumber seq,
|
||||
Slice mem_key = lkey.memtable_key();
|
||||
|
||||
std::unique_ptr<MemTableRep::Iterator> iter(
|
||||
table_->GetIterator(lkey.user_key()));
|
||||
table_->GetDynamicPrefixIterator());
|
||||
iter->Seek(lkey.internal_key(), mem_key.data());
|
||||
|
||||
if (iter->Valid()) {
|
||||
@@ -508,7 +509,7 @@ bool MemTable::UpdateCallback(SequenceNumber seq,
|
||||
Slice memkey = lkey.memtable_key();
|
||||
|
||||
std::unique_ptr<MemTableRep::Iterator> iter(
|
||||
table_->GetIterator(lkey.user_key()));
|
||||
table_->GetDynamicPrefixIterator());
|
||||
iter->Seek(lkey.internal_key(), memkey.data());
|
||||
|
||||
if (iter->Valid()) {
|
||||
@@ -583,7 +584,7 @@ size_t MemTable::CountSuccessiveMergeEntries(const LookupKey& key) {
|
||||
// reps). By passing in the user key, we allow efficient iterator creation.
|
||||
// The iterator only needs to be ordered within the same user key.
|
||||
std::unique_ptr<MemTableRep::Iterator> iter(
|
||||
table_->GetIterator(key.user_key()));
|
||||
table_->GetDynamicPrefixIterator());
|
||||
iter->Seek(key.internal_key(), memkey.data());
|
||||
|
||||
size_t num_successive_merges = 0;
|
||||
@@ -610,7 +611,7 @@ size_t MemTable::CountSuccessiveMergeEntries(const LookupKey& key) {
|
||||
|
||||
void MemTableRep::Get(const LookupKey& k, void* callback_args,
|
||||
bool (*callback_func)(void* arg, const char* entry)) {
|
||||
auto iter = GetIterator(k.user_key());
|
||||
auto iter = GetDynamicPrefixIterator();
|
||||
for (iter->Seek(k.internal_key(), k.memtable_key().data());
|
||||
iter->Valid() && callback_func(callback_args, iter->key());
|
||||
iter->Next()) {
|
||||
|
||||
+4
-4
@@ -140,7 +140,7 @@ void MemTableList::PickMemtablesToFlush(autovector<MemTable*>* ret) {
|
||||
|
||||
void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
uint64_t file_number,
|
||||
std::set<uint64_t>* pending_outputs) {
|
||||
FileNumToPathIdMap* pending_outputs) {
|
||||
assert(!mems.empty());
|
||||
|
||||
// If the flush was not successful, then just reset state.
|
||||
@@ -162,7 +162,7 @@ void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
Status MemTableList::InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const autovector<MemTable*>& mems, VersionSet* vset,
|
||||
port::Mutex* mu, Logger* info_log, uint64_t file_number,
|
||||
std::set<uint64_t>& pending_outputs, autovector<MemTable*>* to_delete,
|
||||
FileNumToPathIdMap* pending_outputs, autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory, LogBuffer* log_buffer) {
|
||||
mu->AssertHeld();
|
||||
|
||||
@@ -219,7 +219,7 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
// has been written to a committed version so that other concurrently
|
||||
// executing compaction threads do not mistakenly assume that this
|
||||
// file is not live.
|
||||
pending_outputs.erase(m->file_number_);
|
||||
pending_outputs->erase(m->file_number_);
|
||||
if (m->Unref() != nullptr) {
|
||||
to_delete->push_back(m);
|
||||
}
|
||||
@@ -233,7 +233,7 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
m->flush_in_progress_ = false;
|
||||
m->edit_.Clear();
|
||||
num_flush_not_started_++;
|
||||
pending_outputs.erase(m->file_number_);
|
||||
pending_outputs->erase(m->file_number_);
|
||||
m->file_number_ = 0;
|
||||
imm_flush_needed.Release_Store((void *)1);
|
||||
}
|
||||
|
||||
+7
-9
@@ -15,6 +15,7 @@
|
||||
#include "rocksdb/iterator.h"
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/skiplist.h"
|
||||
#include "db/memtable.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -108,17 +109,14 @@ class MemTableList {
|
||||
// they can get picked up again on the next round of flush.
|
||||
void RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
uint64_t file_number,
|
||||
std::set<uint64_t>* pending_outputs);
|
||||
FileNumToPathIdMap* pending_outputs);
|
||||
|
||||
// Commit a successful flush in the manifest file
|
||||
Status InstallMemtableFlushResults(ColumnFamilyData* cfd,
|
||||
const autovector<MemTable*>& m,
|
||||
VersionSet* vset, port::Mutex* mu,
|
||||
Logger* info_log, uint64_t file_number,
|
||||
std::set<uint64_t>& pending_outputs,
|
||||
autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
Status InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const autovector<MemTable*>& m, VersionSet* vset,
|
||||
port::Mutex* mu, Logger* info_log, uint64_t file_number,
|
||||
FileNumToPathIdMap* pending_outputs, autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory, LogBuffer* log_buffer);
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
// Takes ownership of the referenced held on *m by the caller of Add().
|
||||
|
||||
+10
-12
@@ -62,7 +62,7 @@ class PlainTableDBTest {
|
||||
Options CurrentOptions() {
|
||||
Options options;
|
||||
options.table_factory.reset(NewPlainTableFactory(0, 2, 0.8, 3, 0, kPrefix));
|
||||
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true));
|
||||
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true, 3));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
|
||||
options.allow_mmap_reads = true;
|
||||
return options;
|
||||
@@ -262,16 +262,14 @@ TEST(PlainTableDBTest, Flush) {
|
||||
for (EncodingType encoding_type : {kPlain, kPrefix}) {
|
||||
for (int bloom_bits = 0; bloom_bits <= 117; bloom_bits += 117) {
|
||||
for (int total_order = 0; total_order <= 1; total_order++) {
|
||||
if (encoding_type == kPrefix && total_order == 1) {
|
||||
continue;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
// Set only one bucket to force bucket conflict.
|
||||
// Test index interval for the same prefix to be 1, 2 and 4
|
||||
if (total_order) {
|
||||
options.table_factory.reset(NewTotalOrderPlainTableFactory(
|
||||
0, bloom_bits, 2, huge_page_tlb_size));
|
||||
options.prefix_extractor.reset();
|
||||
options.table_factory.reset(NewPlainTableFactory(
|
||||
0, bloom_bits, 0, 2, huge_page_tlb_size, encoding_type));
|
||||
} else {
|
||||
options.table_factory.reset(NewPlainTableFactory(
|
||||
0, bloom_bits, 0.75, 16, huge_page_tlb_size, encoding_type));
|
||||
@@ -290,8 +288,8 @@ TEST(PlainTableDBTest, Flush) {
|
||||
auto tp = row->second;
|
||||
ASSERT_EQ(total_order ? "4" : "12", (tp->user_collected_properties).at(
|
||||
"plain_table_hash_table_size"));
|
||||
ASSERT_EQ(total_order ? "9" : "0", (tp->user_collected_properties).at(
|
||||
"plain_table_sub_index_size"));
|
||||
ASSERT_EQ("0", (tp->user_collected_properties)
|
||||
.at("plain_table_sub_index_size"));
|
||||
|
||||
ASSERT_EQ("v3", Get("1000000000000foo"));
|
||||
ASSERT_EQ("v2", Get("0000000000000bar"));
|
||||
@@ -487,7 +485,7 @@ std::string MakeLongKey(size_t length, char c) {
|
||||
|
||||
TEST(PlainTableDBTest, IteratorLargeKeys) {
|
||||
Options options = CurrentOptions();
|
||||
options.table_factory.reset(NewTotalOrderPlainTableFactory(0, 0, 16, 0));
|
||||
options.table_factory.reset(NewPlainTableFactory(0, 0, 0));
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset();
|
||||
DestroyAndReopen(&options);
|
||||
@@ -668,7 +666,7 @@ TEST(PlainTableDBTest, HashBucketConflict) {
|
||||
// Set only one bucket to force bucket conflict.
|
||||
// Test index interval for the same prefix to be 1, 2 and 4
|
||||
options.table_factory.reset(
|
||||
NewTotalOrderPlainTableFactory(16, 0, 2 ^ i, huge_page_tlb_size));
|
||||
NewPlainTableFactory(16, 0, 0, 2 ^ i, huge_page_tlb_size));
|
||||
DestroyAndReopen(&options);
|
||||
ASSERT_OK(Put("5000000000000fo0", "v1"));
|
||||
ASSERT_OK(Put("5000000000000fo1", "v2"));
|
||||
@@ -755,7 +753,7 @@ TEST(PlainTableDBTest, HashBucketConflictReverseSuffixComparator) {
|
||||
// Set only one bucket to force bucket conflict.
|
||||
// Test index interval for the same prefix to be 1, 2 and 4
|
||||
options.table_factory.reset(
|
||||
NewTotalOrderPlainTableFactory(16, 0, 2 ^ i, huge_page_tlb_size));
|
||||
NewPlainTableFactory(16, 0, 0, 2 ^ i, huge_page_tlb_size));
|
||||
DestroyAndReopen(&options);
|
||||
ASSERT_OK(Put("5000000000000fo0", "v1"));
|
||||
ASSERT_OK(Put("5000000000000fo1", "v2"));
|
||||
@@ -835,7 +833,7 @@ TEST(PlainTableDBTest, NonExistingKeyToNonEmptyBucket) {
|
||||
options.create_if_missing = true;
|
||||
// Set only one bucket to force bucket conflict.
|
||||
// Test index interval for the same prefix to be 1, 2 and 4
|
||||
options.table_factory.reset(NewTotalOrderPlainTableFactory(16, 0, 5));
|
||||
options.table_factory.reset(NewPlainTableFactory(16, 0, 0, 5));
|
||||
DestroyAndReopen(&options);
|
||||
ASSERT_OK(Put("5000000000000fo0", "v1"));
|
||||
ASSERT_OK(Put("5000000000000fo1", "v2"));
|
||||
|
||||
@@ -189,6 +189,10 @@ class PrefixTest {
|
||||
options.memtable_factory.reset(
|
||||
NewHashLinkListRepFactory(bucket_count, 2 * 1024 * 1024));
|
||||
return true;
|
||||
case kHashLinkListTriggerSkipList:
|
||||
options.memtable_factory.reset(
|
||||
NewHashLinkListRepFactory(bucket_count, 0, 3));
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -208,6 +212,7 @@ class PrefixTest {
|
||||
kHashSkipList,
|
||||
kHashLinkList,
|
||||
kHashLinkListHugePageTlb,
|
||||
kHashLinkListTriggerSkipList,
|
||||
kEnd
|
||||
};
|
||||
int option_config_;
|
||||
|
||||
+70
-60
@@ -31,6 +31,8 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include "db/builder.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
@@ -63,8 +65,8 @@ class Repairer {
|
||||
NewLRUCache(10, options_.table_cache_numshardbits,
|
||||
options_.table_cache_remove_scan_count_limit)),
|
||||
next_file_number_(1) {
|
||||
table_cache_ = new TableCache(dbname_, &options_, storage_options_,
|
||||
raw_table_cache_.get());
|
||||
table_cache_ =
|
||||
new TableCache(&options_, storage_options_, raw_table_cache_.get());
|
||||
edit_ = new VersionEdit();
|
||||
}
|
||||
|
||||
@@ -82,18 +84,17 @@ class Repairer {
|
||||
status = WriteDescriptor();
|
||||
}
|
||||
if (status.ok()) {
|
||||
unsigned long long bytes = 0;
|
||||
uint64_t bytes = 0;
|
||||
for (size_t i = 0; i < tables_.size(); i++) {
|
||||
bytes += tables_[i].meta.fd.GetFileSize();
|
||||
}
|
||||
Log(options_.info_log,
|
||||
"**** Repaired rocksdb %s; "
|
||||
"recovered %d files; %llu bytes. "
|
||||
"recovered %zu files; %" PRIu64
|
||||
"bytes. "
|
||||
"Some data may have been lost. "
|
||||
"****",
|
||||
dbname_.c_str(),
|
||||
static_cast<int>(tables_.size()),
|
||||
bytes);
|
||||
dbname_.c_str(), tables_.size(), bytes);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -115,7 +116,7 @@ class Repairer {
|
||||
VersionEdit* edit_;
|
||||
|
||||
std::vector<std::string> manifests_;
|
||||
std::vector<uint64_t> table_numbers_;
|
||||
std::vector<FileDescriptor> table_fds_;
|
||||
std::vector<uint64_t> logs_;
|
||||
std::vector<TableInfo> tables_;
|
||||
uint64_t next_file_number_;
|
||||
@@ -123,35 +124,43 @@ class Repairer {
|
||||
|
||||
Status FindFiles() {
|
||||
std::vector<std::string> filenames;
|
||||
Status status = env_->GetChildren(dbname_, &filenames);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (filenames.empty()) {
|
||||
return Status::Corruption(dbname_, "repair found no files");
|
||||
}
|
||||
bool found_file = false;
|
||||
for (uint32_t path_id = 0; path_id < options_.db_paths.size(); path_id++) {
|
||||
Status status = env_->GetChildren(options_.db_paths[path_id], &filenames);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (!filenames.empty()) {
|
||||
found_file = true;
|
||||
}
|
||||
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, &type)) {
|
||||
if (type == kDescriptorFile) {
|
||||
manifests_.push_back(filenames[i]);
|
||||
} else {
|
||||
if (number + 1 > next_file_number_) {
|
||||
next_file_number_ = number + 1;
|
||||
}
|
||||
if (type == kLogFile) {
|
||||
logs_.push_back(number);
|
||||
} else if (type == kTableFile) {
|
||||
table_numbers_.push_back(number);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, &type)) {
|
||||
if (type == kDescriptorFile) {
|
||||
assert(path_id == 0);
|
||||
manifests_.push_back(filenames[i]);
|
||||
} else {
|
||||
// Ignore other files
|
||||
if (number + 1 > next_file_number_) {
|
||||
next_file_number_ = number + 1;
|
||||
}
|
||||
if (type == kLogFile) {
|
||||
assert(path_id == 0);
|
||||
logs_.push_back(number);
|
||||
} else if (type == kTableFile) {
|
||||
table_fds_.emplace_back(number, path_id, 0);
|
||||
} else {
|
||||
// Ignore other files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
if (!found_file) {
|
||||
return Status::Corruption(dbname_, "repair found no files");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void ConvertLogFilesToTables() {
|
||||
@@ -159,8 +168,8 @@ class Repairer {
|
||||
std::string logname = LogFileName(dbname_, logs_[i]);
|
||||
Status status = ConvertLogToTable(logs_[i]);
|
||||
if (!status.ok()) {
|
||||
Log(options_.info_log, "Log #%llu: ignoring conversion error: %s",
|
||||
(unsigned long long) logs_[i],
|
||||
Log(options_.info_log,
|
||||
"Log #%" PRIu64 ": ignoring conversion error: %s", logs_[i],
|
||||
status.ToString().c_str());
|
||||
}
|
||||
ArchiveFile(logname);
|
||||
@@ -174,10 +183,8 @@ class Repairer {
|
||||
uint64_t lognum;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
// We print error messages for corruption, but continue repairing.
|
||||
Log(info_log, "Log #%llu: dropping %d bytes; %s",
|
||||
(unsigned long long) lognum,
|
||||
static_cast<int>(bytes),
|
||||
s.ToString().c_str());
|
||||
Log(info_log, "Log #%" PRIu64 ": dropping %d bytes; %s", lognum,
|
||||
static_cast<int>(bytes), s.ToString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -220,8 +227,7 @@ class Repairer {
|
||||
if (status.ok()) {
|
||||
counter += WriteBatchInternal::Count(&batch);
|
||||
} else {
|
||||
Log(options_.info_log, "Log #%llu: ignoring %s",
|
||||
(unsigned long long) log,
|
||||
Log(options_.info_log, "Log #%" PRIu64 ": ignoring %s", log,
|
||||
status.ToString().c_str());
|
||||
status = Status::OK(); // Keep going with rest of file
|
||||
}
|
||||
@@ -230,7 +236,7 @@ class Repairer {
|
||||
// Do not record a version edit for this conversion to a Table
|
||||
// since ExtractMetaData() will also generate edits.
|
||||
FileMetaData meta;
|
||||
meta.fd.number = next_file_number_++;
|
||||
meta.fd = FileDescriptor(next_file_number_++, 0, 0);
|
||||
ReadOptions ro;
|
||||
Iterator* iter = mem->NewIterator(ro, true /* enforce_total_order */);
|
||||
status = BuildTable(dbname_, env_, options_, storage_options_, table_cache_,
|
||||
@@ -241,24 +247,26 @@ class Repairer {
|
||||
mem = nullptr;
|
||||
if (status.ok()) {
|
||||
if (meta.fd.GetFileSize() > 0) {
|
||||
table_numbers_.push_back(meta.fd.GetNumber());
|
||||
table_fds_.push_back(meta.fd);
|
||||
}
|
||||
}
|
||||
Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s",
|
||||
(unsigned long long)log, counter,
|
||||
(unsigned long long)meta.fd.GetNumber(), status.ToString().c_str());
|
||||
Log(options_.info_log,
|
||||
"Log #%" PRIu64 ": %d ops saved to Table #%" PRIu64 " %s", log, counter,
|
||||
meta.fd.GetNumber(), status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
|
||||
void ExtractMetaData() {
|
||||
for (size_t i = 0; i < table_numbers_.size(); i++) {
|
||||
for (size_t i = 0; i < table_fds_.size(); i++) {
|
||||
TableInfo t;
|
||||
t.meta.fd.number = table_numbers_[i];
|
||||
t.meta.fd = table_fds_[i];
|
||||
Status status = ScanTable(&t);
|
||||
if (!status.ok()) {
|
||||
std::string fname = TableFileName(dbname_, table_numbers_[i]);
|
||||
Log(options_.info_log, "Table #%llu: ignoring %s",
|
||||
(unsigned long long) table_numbers_[i],
|
||||
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(),
|
||||
status.ToString().c_str());
|
||||
ArchiveFile(fname);
|
||||
} else {
|
||||
@@ -268,9 +276,13 @@ class Repairer {
|
||||
}
|
||||
|
||||
Status ScanTable(TableInfo* t) {
|
||||
std::string fname = TableFileName(dbname_, t->meta.fd.GetNumber());
|
||||
std::string fname = TableFileName(options_.db_paths, t->meta.fd.GetNumber(),
|
||||
t->meta.fd.GetPathId());
|
||||
int counter = 0;
|
||||
Status status = env_->GetFileSize(fname, &t->meta.fd.file_size);
|
||||
uint64_t file_size;
|
||||
Status status = env_->GetFileSize(fname, &file_size);
|
||||
t->meta.fd = FileDescriptor(t->meta.fd.GetNumber(), t->meta.fd.GetPathId(),
|
||||
file_size);
|
||||
if (status.ok()) {
|
||||
Iterator* iter = table_cache_->NewIterator(
|
||||
ReadOptions(), storage_options_, icmp_, t->meta.fd);
|
||||
@@ -281,9 +293,8 @@ class Repairer {
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
Slice key = iter->key();
|
||||
if (!ParseInternalKey(key, &parsed)) {
|
||||
Log(options_.info_log, "Table #%llu: unparsable key %s",
|
||||
(unsigned long long)t->meta.fd.GetNumber(),
|
||||
EscapeString(key).c_str());
|
||||
Log(options_.info_log, "Table #%" PRIu64 ": unparsable key %s",
|
||||
t->meta.fd.GetNumber(), EscapeString(key).c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -305,9 +316,8 @@ class Repairer {
|
||||
}
|
||||
delete iter;
|
||||
}
|
||||
Log(options_.info_log, "Table #%llu: %d entries %s",
|
||||
(unsigned long long)t->meta.fd.GetNumber(), counter,
|
||||
status.ToString().c_str());
|
||||
Log(options_.info_log, "Table #%" PRIu64 ": %d entries %s",
|
||||
t->meta.fd.GetNumber(), counter, status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -335,9 +345,9 @@ class Repairer {
|
||||
for (size_t i = 0; i < tables_.size(); i++) {
|
||||
// TODO(opt): separate out into multiple levels
|
||||
const TableInfo& t = tables_[i];
|
||||
edit_->AddFile(0, t.meta.fd.GetNumber(), t.meta.fd.GetFileSize(),
|
||||
t.meta.smallest, t.meta.largest, t.min_sequence,
|
||||
t.max_sequence);
|
||||
edit_->AddFile(0, t.meta.fd.GetNumber(), t.meta.fd.GetPathId(),
|
||||
t.meta.fd.GetFileSize(), t.meta.smallest, t.meta.largest,
|
||||
t.min_sequence, t.max_sequence);
|
||||
}
|
||||
|
||||
//fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str());
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
|
||||
Status Get(const ReadOptions&, const Slice& key, void* arg,
|
||||
bool (*handle_result)(void* arg, const ParsedInternalKey& k,
|
||||
const Slice& v, bool),
|
||||
const Slice& v),
|
||||
void (*mark_key_may_exist)(void*) = nullptr) override;
|
||||
|
||||
uint64_t ApproximateOffsetOf(const Slice& key) override;
|
||||
@@ -285,7 +285,7 @@ Status SimpleTableReader::GetOffset(const Slice& target, uint64_t* offset) {
|
||||
Status SimpleTableReader::Get(const ReadOptions& options, const Slice& k,
|
||||
void* arg,
|
||||
bool (*saver)(void*, const ParsedInternalKey&,
|
||||
const Slice&, bool),
|
||||
const Slice&),
|
||||
void (*mark_key_may_exist)(void*)) {
|
||||
Status s;
|
||||
SimpleTableIterator* iter = new SimpleTableIterator(this);
|
||||
@@ -295,7 +295,7 @@ Status SimpleTableReader::Get(const ReadOptions& options, const Slice& k,
|
||||
return Status::Corruption(Slice());
|
||||
}
|
||||
|
||||
if (!(*saver)(arg, parsed_key, iter->value(), true)) {
|
||||
if (!(*saver)(arg, parsed_key, iter->value())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+12
-15
@@ -36,10 +36,10 @@ static Slice GetSliceForFileNumber(const uint64_t* file_number) {
|
||||
sizeof(*file_number));
|
||||
}
|
||||
|
||||
TableCache::TableCache(const std::string& dbname, const Options* options,
|
||||
TableCache::TableCache(const Options* options,
|
||||
const EnvOptions& storage_options, Cache* const cache)
|
||||
: env_(options->env),
|
||||
dbname_(dbname),
|
||||
db_paths_(options->db_paths),
|
||||
options_(options),
|
||||
storage_options_(storage_options),
|
||||
cache_(cache) {}
|
||||
@@ -58,18 +58,17 @@ void TableCache::ReleaseHandle(Cache::Handle* handle) {
|
||||
Status TableCache::FindTable(const EnvOptions& toptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& fd, Cache::Handle** handle,
|
||||
bool* table_io, const bool no_io) {
|
||||
const bool no_io) {
|
||||
Status s;
|
||||
Slice key = GetSliceForFileNumber(&fd.number);
|
||||
uint64_t number = fd.GetNumber();
|
||||
Slice key = GetSliceForFileNumber(&number);
|
||||
*handle = cache_->Lookup(key);
|
||||
if (*handle == nullptr) {
|
||||
if (no_io) { // Dont do IO and return a not-found status
|
||||
return Status::Incomplete("Table not found in table_cache, no_io is set");
|
||||
}
|
||||
if (table_io != nullptr) {
|
||||
*table_io = true; // we had to do IO from storage
|
||||
}
|
||||
std::string fname = TableFileName(dbname_, fd.GetNumber());
|
||||
std::string fname =
|
||||
TableFileName(db_paths_, fd.GetNumber(), fd.GetPathId());
|
||||
unique_ptr<RandomAccessFile> file;
|
||||
unique_ptr<TableReader> table_reader;
|
||||
s = env_->NewRandomAccessFile(fname, &file, toptions);
|
||||
@@ -110,7 +109,7 @@ Iterator* TableCache::NewIterator(const ReadOptions& options,
|
||||
Cache::Handle* handle = nullptr;
|
||||
Status s;
|
||||
if (table_reader == nullptr) {
|
||||
s = FindTable(toptions, icomparator, fd, &handle, nullptr,
|
||||
s = FindTable(toptions, icomparator, fd, &handle,
|
||||
options.read_tier == kBlockCacheTier);
|
||||
if (!s.ok()) {
|
||||
return NewErrorIterator(s, arena);
|
||||
@@ -137,13 +136,13 @@ Status TableCache::Get(const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& fd, const Slice& k, void* arg,
|
||||
bool (*saver)(void*, const ParsedInternalKey&,
|
||||
const Slice&, bool),
|
||||
bool* table_io, void (*mark_key_may_exist)(void*)) {
|
||||
const Slice&),
|
||||
void (*mark_key_may_exist)(void*)) {
|
||||
TableReader* t = fd.table_reader;
|
||||
Status s;
|
||||
Cache::Handle* handle = nullptr;
|
||||
if (!t) {
|
||||
s = FindTable(storage_options_, internal_comparator, fd, &handle, table_io,
|
||||
s = FindTable(storage_options_, internal_comparator, fd, &handle,
|
||||
options.read_tier == kBlockCacheTier);
|
||||
if (s.ok()) {
|
||||
t = GetTableReaderFromHandle(handle);
|
||||
@@ -174,10 +173,8 @@ Status TableCache::GetTableProperties(
|
||||
return s;
|
||||
}
|
||||
|
||||
bool table_io;
|
||||
Cache::Handle* table_handle = nullptr;
|
||||
s = FindTable(toptions, internal_comparator, fd, &table_handle, &table_io,
|
||||
no_io);
|
||||
s = FindTable(toptions, internal_comparator, fd, &table_handle, no_io);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
+7
-6
@@ -11,6 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
@@ -28,8 +29,8 @@ struct FileDescriptor;
|
||||
|
||||
class TableCache {
|
||||
public:
|
||||
TableCache(const std::string& dbname, const Options* options,
|
||||
const EnvOptions& storage_options, Cache* cache);
|
||||
TableCache(const Options* options, const EnvOptions& storage_options,
|
||||
Cache* cache);
|
||||
~TableCache();
|
||||
|
||||
// Return an iterator for the specified file number (the corresponding
|
||||
@@ -52,8 +53,8 @@ class TableCache {
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& file_fd, const Slice& k, void* arg,
|
||||
bool (*handle_result)(void*, const ParsedInternalKey&,
|
||||
const Slice&, bool),
|
||||
bool* table_io, void (*mark_key_may_exist)(void*) = nullptr);
|
||||
const Slice&),
|
||||
void (*mark_key_may_exist)(void*) = nullptr);
|
||||
|
||||
// Evict any entry for the specified file number
|
||||
static void Evict(Cache* cache, uint64_t file_number);
|
||||
@@ -62,7 +63,7 @@ class TableCache {
|
||||
Status FindTable(const EnvOptions& toptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& file_fd, Cache::Handle**,
|
||||
bool* table_io = nullptr, const bool no_io = false);
|
||||
const bool no_io = false);
|
||||
|
||||
// Get TableReader from a cache handle.
|
||||
TableReader* GetTableReaderFromHandle(Cache::Handle* handle);
|
||||
@@ -84,7 +85,7 @@ class TableCache {
|
||||
|
||||
private:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
const std::vector<std::string> db_paths_;
|
||||
const Options* options_;
|
||||
const EnvOptions& storage_options_;
|
||||
Cache* const cache_;
|
||||
|
||||
+51
-17
@@ -18,25 +18,30 @@ namespace rocksdb {
|
||||
// Tag numbers for serialized VersionEdit. These numbers are written to
|
||||
// disk and should not be changed.
|
||||
enum Tag {
|
||||
kComparator = 1,
|
||||
kLogNumber = 2,
|
||||
kNextFileNumber = 3,
|
||||
kLastSequence = 4,
|
||||
kCompactPointer = 5,
|
||||
kDeletedFile = 6,
|
||||
kNewFile = 7,
|
||||
kComparator = 1,
|
||||
kLogNumber = 2,
|
||||
kNextFileNumber = 3,
|
||||
kLastSequence = 4,
|
||||
kCompactPointer = 5,
|
||||
kDeletedFile = 6,
|
||||
kNewFile = 7,
|
||||
// 8 was used for large value refs
|
||||
kPrevLogNumber = 9,
|
||||
kPrevLogNumber = 9,
|
||||
|
||||
// these are new formats divergent from open source leveldb
|
||||
kNewFile2 = 100, // store smallest & largest seqno
|
||||
|
||||
kColumnFamily = 200, // specify column family for version edit
|
||||
kColumnFamilyAdd = 201,
|
||||
kColumnFamilyDrop = 202,
|
||||
kMaxColumnFamily = 203,
|
||||
kNewFile2 = 100,
|
||||
kNewFile3 = 102,
|
||||
kColumnFamily = 200, // specify column family for version edit
|
||||
kColumnFamilyAdd = 201,
|
||||
kColumnFamilyDrop = 202,
|
||||
kMaxColumnFamily = 203,
|
||||
};
|
||||
|
||||
uint64_t PackFileNumberAndPathId(uint64_t number, uint64_t path_id) {
|
||||
assert(number <= kFileNumberMask);
|
||||
return number | (path_id * (kFileNumberMask + 1));
|
||||
}
|
||||
|
||||
void VersionEdit::Clear() {
|
||||
comparator_.clear();
|
||||
max_level_ = 0;
|
||||
@@ -93,9 +98,18 @@ void VersionEdit::EncodeTo(std::string* dst) const {
|
||||
|
||||
for (size_t i = 0; i < new_files_.size(); i++) {
|
||||
const FileMetaData& f = new_files_[i].second;
|
||||
PutVarint32(dst, kNewFile2);
|
||||
if (f.fd.GetPathId() == 0) {
|
||||
// Use older format to make sure user can roll back the build if they
|
||||
// don't config multiple DB paths.
|
||||
PutVarint32(dst, kNewFile2);
|
||||
} else {
|
||||
PutVarint32(dst, kNewFile3);
|
||||
}
|
||||
PutVarint32(dst, new_files_[i].first); // level
|
||||
PutVarint64(dst, f.fd.GetNumber());
|
||||
if (f.fd.GetPathId() != 0) {
|
||||
PutVarint32(dst, f.fd.GetPathId());
|
||||
}
|
||||
PutVarint64(dst, f.fd.GetFileSize());
|
||||
PutLengthPrefixedSlice(dst, f.smallest.Encode());
|
||||
PutLengthPrefixedSlice(dst, f.largest.Encode());
|
||||
@@ -237,7 +251,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
GetVarint64(&input, &file_size) &&
|
||||
GetInternalKey(&input, &f.smallest) &&
|
||||
GetInternalKey(&input, &f.largest)) {
|
||||
f.fd = FileDescriptor(number, file_size);
|
||||
f.fd = FileDescriptor(number, 0, file_size);
|
||||
new_files_.push_back(std::make_pair(level, f));
|
||||
} else {
|
||||
if (!msg) {
|
||||
@@ -255,7 +269,27 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
GetInternalKey(&input, &f.largest) &&
|
||||
GetVarint64(&input, &f.smallest_seqno) &&
|
||||
GetVarint64(&input, &f.largest_seqno)) {
|
||||
f.fd = FileDescriptor(number, file_size);
|
||||
f.fd = FileDescriptor(number, 0, file_size);
|
||||
new_files_.push_back(std::make_pair(level, f));
|
||||
} else {
|
||||
if (!msg) {
|
||||
msg = "new-file2 entry";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kNewFile3: {
|
||||
uint64_t number;
|
||||
uint32_t path_id;
|
||||
uint64_t file_size;
|
||||
if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
|
||||
GetVarint32(&input, &path_id) && GetVarint64(&input, &file_size) &&
|
||||
GetInternalKey(&input, &f.smallest) &&
|
||||
GetInternalKey(&input, &f.largest) &&
|
||||
GetVarint64(&input, &f.smallest_seqno) &&
|
||||
GetVarint64(&input, &f.largest_seqno)) {
|
||||
f.fd = FileDescriptor(number, path_id, file_size);
|
||||
new_files_.push_back(std::make_pair(level, f));
|
||||
} else {
|
||||
if (!msg) {
|
||||
|
||||
+84
-20
@@ -19,43 +19,108 @@ namespace rocksdb {
|
||||
|
||||
class VersionSet;
|
||||
|
||||
const uint64_t kFileNumberMask = 0x3FFFFFFFFFFFFFFF;
|
||||
|
||||
extern uint64_t PackFileNumberAndPathId(uint64_t number, uint64_t path_id);
|
||||
|
||||
// A copyable structure contains information needed to read data from an SST
|
||||
// file. It can contains a pointer to a table reader opened for the file, or
|
||||
// file number and size, which can be used to create a new table reader for it.
|
||||
// The behavior is undefined when a copied of the structure is used when the
|
||||
// file is not in any live version any more.
|
||||
struct FileDescriptor {
|
||||
uint64_t number;
|
||||
uint64_t file_size; // File size in bytes
|
||||
// Table reader in table_reader_handle
|
||||
TableReader* table_reader;
|
||||
uint64_t packed_number_and_path_id;
|
||||
uint64_t file_size; // File size in bytes
|
||||
|
||||
FileDescriptor(uint64_t number, uint64_t file_size)
|
||||
: number(number), file_size(file_size), table_reader(nullptr) {}
|
||||
FileDescriptor() : FileDescriptor(0, 0, 0) {}
|
||||
|
||||
uint64_t GetNumber() const { return number; }
|
||||
FileDescriptor(uint64_t number, uint32_t path_id, uint64_t file_size)
|
||||
: table_reader(nullptr),
|
||||
packed_number_and_path_id(PackFileNumberAndPathId(number, path_id)),
|
||||
file_size(file_size) {}
|
||||
|
||||
FileDescriptor& operator=(const FileDescriptor& fd) {
|
||||
table_reader = fd.table_reader;
|
||||
packed_number_and_path_id = fd.packed_number_and_path_id;
|
||||
file_size = fd.file_size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint64_t GetNumber() const {
|
||||
return packed_number_and_path_id & kFileNumberMask;
|
||||
}
|
||||
uint32_t GetPathId() const {
|
||||
return packed_number_and_path_id / (kFileNumberMask + 1);
|
||||
}
|
||||
uint64_t GetFileSize() const { return file_size; }
|
||||
};
|
||||
|
||||
struct FileMetaData {
|
||||
int refs;
|
||||
FileDescriptor fd;
|
||||
int allowed_seeks; // Seeks allowed until compaction
|
||||
InternalKey smallest; // Smallest internal key served by table
|
||||
InternalKey largest; // Largest internal key served by table
|
||||
bool being_compacted; // Is this file undergoing compaction?
|
||||
SequenceNumber smallest_seqno;// The smallest seqno in this file
|
||||
SequenceNumber largest_seqno; // The largest seqno in this file
|
||||
InternalKey smallest; // Smallest internal key served by table
|
||||
InternalKey largest; // Largest internal key served by table
|
||||
bool being_compacted; // Is this file undergoing compaction?
|
||||
SequenceNumber smallest_seqno; // The smallest seqno in this file
|
||||
SequenceNumber largest_seqno; // The largest seqno in this file
|
||||
|
||||
// Needs to be disposed when refs becomes 0.
|
||||
Cache::Handle* table_reader_handle;
|
||||
|
||||
// Stats for compensating deletion entries during compaction
|
||||
|
||||
// File size compensated by deletion entry.
|
||||
// This is updated in Version::UpdateTemporaryStats() first time when the
|
||||
// file is created or loaded. After it is updated, it is immutable.
|
||||
uint64_t compensated_file_size;
|
||||
uint64_t num_entries; // the number of entries.
|
||||
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.
|
||||
|
||||
FileMetaData()
|
||||
: refs(0),
|
||||
fd(0, 0),
|
||||
allowed_seeks(1 << 30),
|
||||
being_compacted(false),
|
||||
table_reader_handle(nullptr) {}
|
||||
table_reader_handle(nullptr),
|
||||
compensated_file_size(0),
|
||||
num_entries(0),
|
||||
num_deletions(0),
|
||||
raw_key_size(0),
|
||||
raw_value_size(0) {}
|
||||
};
|
||||
|
||||
// A compressed copy of file meta data that just contain
|
||||
// smallest and largest key's slice
|
||||
struct FdWithKeyRange {
|
||||
FileDescriptor fd;
|
||||
Slice smallest_key; // slice that contain smallest key
|
||||
Slice largest_key; // slice that contain largest key
|
||||
|
||||
FdWithKeyRange()
|
||||
: fd(),
|
||||
smallest_key(),
|
||||
largest_key() {
|
||||
}
|
||||
|
||||
FdWithKeyRange(FileDescriptor fd,
|
||||
Slice smallest_key, Slice largest_key)
|
||||
: fd(fd),
|
||||
smallest_key(smallest_key),
|
||||
largest_key(largest_key) {
|
||||
}
|
||||
};
|
||||
|
||||
// Data structure to store an array of FdWithKeyRange in one level
|
||||
// Actual data is guaranteed to be stored closely
|
||||
struct FileLevel {
|
||||
size_t num_files;
|
||||
FdWithKeyRange* files;
|
||||
FileLevel() {
|
||||
num_files = 0;
|
||||
files = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class VersionEdit {
|
||||
@@ -93,15 +158,13 @@ class VersionEdit {
|
||||
// Add the specified file at the specified number.
|
||||
// REQUIRES: This version has not been saved (see VersionSet::SaveTo)
|
||||
// REQUIRES: "smallest" and "largest" are smallest and largest keys in file
|
||||
void AddFile(int level, uint64_t file,
|
||||
uint64_t file_size,
|
||||
const InternalKey& smallest,
|
||||
const InternalKey& largest,
|
||||
const SequenceNumber& smallest_seqno,
|
||||
void AddFile(int level, uint64_t file, uint64_t file_size,
|
||||
uint64_t file_path_id, const InternalKey& smallest,
|
||||
const InternalKey& largest, const SequenceNumber& smallest_seqno,
|
||||
const SequenceNumber& largest_seqno) {
|
||||
assert(smallest_seqno <= largest_seqno);
|
||||
FileMetaData f;
|
||||
f.fd = FileDescriptor(file, file_size);
|
||||
f.fd = FileDescriptor(file, file_size, file_path_id);
|
||||
f.smallest = smallest;
|
||||
f.largest = largest;
|
||||
f.smallest_seqno = smallest_seqno;
|
||||
@@ -151,6 +214,7 @@ class VersionEdit {
|
||||
|
||||
private:
|
||||
friend class VersionSet;
|
||||
friend class Version;
|
||||
|
||||
typedef std::set< std::pair<int, uint64_t>> DeletedFileSet;
|
||||
|
||||
|
||||
@@ -30,11 +30,10 @@ TEST(VersionEditTest, EncodeDecode) {
|
||||
VersionEdit edit;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
TestEncodeDecode(edit);
|
||||
edit.AddFile(3, kBig + 300 + i, kBig + 400 + i,
|
||||
edit.AddFile(3, kBig + 300 + i, kBig + 400 + i, 0,
|
||||
InternalKey("foo", kBig + 500 + i, kTypeValue),
|
||||
InternalKey("zoo", kBig + 600 + i, kTypeDeletion),
|
||||
kBig + 500 + i,
|
||||
kBig + 600 + i);
|
||||
kBig + 500 + i, kBig + 600 + i);
|
||||
edit.DeleteFile(4, kBig + 700 + i);
|
||||
}
|
||||
|
||||
|
||||
+299
-227
@@ -16,6 +16,7 @@
|
||||
#include <set>
|
||||
#include <climits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "db/filename.h"
|
||||
@@ -47,6 +48,15 @@ static uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
static uint64_t TotalCompensatedFileSize(
|
||||
const std::vector<FileMetaData*>& files) {
|
||||
uint64_t sum = 0;
|
||||
for (size_t i = 0; i < files.size() && files[i]; i++) {
|
||||
sum += files[i]->compensated_file_size;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
Version::~Version() {
|
||||
assert(refs_ == 0);
|
||||
|
||||
@@ -99,31 +109,61 @@ int FindFile(const InternalKeyComparator& icmp,
|
||||
return FindFileInRange(icmp, files, key, 0, files.size());
|
||||
}
|
||||
|
||||
// Find File in FileLevel data structure
|
||||
// Within an index range defined by left and right
|
||||
int FindFileInRange(const InternalKeyComparator& icmp,
|
||||
const FileLevel& file_level,
|
||||
const Slice& key,
|
||||
uint32_t left,
|
||||
uint32_t right) {
|
||||
while (left < right) {
|
||||
uint32_t mid = (left + right) / 2;
|
||||
const FdWithKeyRange& f = file_level.files[mid];
|
||||
if (icmp.InternalKeyComparator::Compare(f.largest_key, key) < 0) {
|
||||
// Key at "mid.largest" is < "target". Therefore all
|
||||
// files at or before "mid" are uninteresting.
|
||||
left = mid + 1;
|
||||
} else {
|
||||
// Key at "mid.largest" is >= "target". Therefore all files
|
||||
// after "mid" are uninteresting.
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
return right;
|
||||
}
|
||||
|
||||
int FindFile(const InternalKeyComparator& icmp,
|
||||
const FileLevel& file_level,
|
||||
const Slice& key) {
|
||||
return FindFileInRange(icmp, file_level, key, 0, file_level.num_files);
|
||||
}
|
||||
|
||||
static bool AfterFile(const Comparator* ucmp,
|
||||
const Slice* user_key, const FileMetaData* f) {
|
||||
const Slice* user_key, const FdWithKeyRange* f) {
|
||||
// nullptr user_key occurs before all keys and is therefore never after *f
|
||||
return (user_key != nullptr &&
|
||||
ucmp->Compare(*user_key, f->largest.user_key()) > 0);
|
||||
ucmp->Compare(*user_key, ExtractUserKey(f->largest_key)) > 0);
|
||||
}
|
||||
|
||||
static bool BeforeFile(const Comparator* ucmp,
|
||||
const Slice* user_key, const FileMetaData* f) {
|
||||
const Slice* user_key, const FdWithKeyRange* f) {
|
||||
// nullptr user_key occurs after all keys and is therefore never before *f
|
||||
return (user_key != nullptr &&
|
||||
ucmp->Compare(*user_key, f->smallest.user_key()) < 0);
|
||||
ucmp->Compare(*user_key, ExtractUserKey(f->smallest_key)) < 0);
|
||||
}
|
||||
|
||||
|
||||
bool SomeFileOverlapsRange(
|
||||
const InternalKeyComparator& icmp,
|
||||
bool disjoint_sorted_files,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const FileLevel& file_level,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key) {
|
||||
const Comparator* ucmp = icmp.user_comparator();
|
||||
if (!disjoint_sorted_files) {
|
||||
// Need to check against all files
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
const FileMetaData* f = files[i];
|
||||
for (size_t i = 0; i < file_level.num_files; i++) {
|
||||
const FdWithKeyRange* f = &(file_level.files[i]);
|
||||
if (AfterFile(ucmp, smallest_user_key, f) ||
|
||||
BeforeFile(ucmp, largest_user_key, f)) {
|
||||
// No overlap
|
||||
@@ -139,15 +179,15 @@ bool SomeFileOverlapsRange(
|
||||
if (smallest_user_key != nullptr) {
|
||||
// Find the earliest possible internal key for smallest_user_key
|
||||
InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek);
|
||||
index = FindFile(icmp, files, small.Encode());
|
||||
index = FindFile(icmp, file_level, small.Encode());
|
||||
}
|
||||
|
||||
if (index >= files.size()) {
|
||||
if (index >= file_level.num_files) {
|
||||
// beginning of range is after all files, so no overlap.
|
||||
return false;
|
||||
}
|
||||
|
||||
return !BeforeFile(ucmp, largest_user_key, files[index]);
|
||||
return !BeforeFile(ucmp, largest_user_key, &file_level.files[index]);
|
||||
}
|
||||
|
||||
// An internal iterator. For a given version/level pair, yields
|
||||
@@ -162,7 +202,7 @@ class Version::LevelFileNumIterator : public Iterator {
|
||||
: icmp_(icmp),
|
||||
flist_(flist),
|
||||
index_(flist->size()),
|
||||
current_value_(0, 0) { // Marks as invalid
|
||||
current_value_(0, 0, 0) { // Marks as invalid
|
||||
}
|
||||
virtual bool Valid() const {
|
||||
return index_ < flist_->size();
|
||||
@@ -241,53 +281,72 @@ class Version::LevelFileIteratorState : public TwoLevelIteratorState {
|
||||
bool for_compaction_;
|
||||
};
|
||||
|
||||
Status Version::GetPropertiesOfAllTables(TablePropertiesCollection* props) {
|
||||
Status Version::GetTableProperties(std::shared_ptr<const TableProperties>* tp,
|
||||
const FileMetaData* file_meta,
|
||||
const std::string* fname) {
|
||||
auto table_cache = cfd_->table_cache();
|
||||
auto options = cfd_->options();
|
||||
Status s = table_cache->GetTableProperties(
|
||||
vset_->storage_options_, cfd_->internal_comparator(), file_meta->fd,
|
||||
tp, true /* no io */);
|
||||
if (s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// We only ignore error type `Incomplete` since it's by design that we
|
||||
// disallow table when it's not in table cache.
|
||||
if (!s.IsIncomplete()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// 2. Table is not present in table cache, we'll read the table properties
|
||||
// directly from the properties block in the file.
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
if (fname != nullptr) {
|
||||
s = options->env->NewRandomAccessFile(
|
||||
*fname, &file, vset_->storage_options_);
|
||||
} else {
|
||||
s = options->env->NewRandomAccessFile(
|
||||
TableFileName(vset_->options_->db_paths, file_meta->fd.GetNumber(),
|
||||
file_meta->fd.GetPathId()),
|
||||
&file, vset_->storage_options_);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
TableProperties* raw_table_properties;
|
||||
// By setting the magic number to kInvalidTableMagicNumber, we can by
|
||||
// pass the magic number check in the footer.
|
||||
s = ReadTableProperties(
|
||||
file.get(), file_meta->fd.GetFileSize(),
|
||||
Footer::kInvalidTableMagicNumber /* table's magic number */,
|
||||
vset_->env_, options->info_log.get(), &raw_table_properties);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
RecordTick(options->statistics.get(),
|
||||
NUMBER_DIRECT_LOAD_TABLE_PROPERTIES);
|
||||
|
||||
*tp = std::shared_ptr<const TableProperties>(raw_table_properties);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status Version::GetPropertiesOfAllTables(TablePropertiesCollection* props) {
|
||||
for (int level = 0; level < num_levels_; level++) {
|
||||
for (const auto& file_meta : files_[level]) {
|
||||
auto fname = TableFileName(vset_->dbname_, file_meta->fd.GetNumber());
|
||||
auto fname =
|
||||
TableFileName(vset_->options_->db_paths, file_meta->fd.GetNumber(),
|
||||
file_meta->fd.GetPathId());
|
||||
// 1. If the table is already present in table cache, load table
|
||||
// properties from there.
|
||||
std::shared_ptr<const TableProperties> table_properties;
|
||||
Status s = table_cache->GetTableProperties(
|
||||
vset_->storage_options_, cfd_->internal_comparator(), file_meta->fd,
|
||||
&table_properties, true /* no io */);
|
||||
Status s = GetTableProperties(&table_properties, file_meta, &fname);
|
||||
if (s.ok()) {
|
||||
props->insert({fname, table_properties});
|
||||
continue;
|
||||
}
|
||||
|
||||
// We only ignore error type `Incomplete` since it's by design that we
|
||||
// disallow table when it's not in table cache.
|
||||
if (!s.IsIncomplete()) {
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
|
||||
// 2. Table is not present in table cache, we'll read the table properties
|
||||
// directly from the properties block in the file.
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
s = options->env->NewRandomAccessFile(fname, &file,
|
||||
vset_->storage_options_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
TableProperties* raw_table_properties;
|
||||
// By setting the magic number to kInvalidTableMagicNumber, we can by
|
||||
// pass the magic number check in the footer.
|
||||
s = ReadTableProperties(
|
||||
file.get(), file_meta->fd.GetFileSize(),
|
||||
Footer::kInvalidTableMagicNumber /* table's magic number */,
|
||||
vset_->env_, options->info_log.get(), &raw_table_properties);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
RecordTick(options->statistics.get(),
|
||||
NUMBER_DIRECT_LOAD_TABLE_PROPERTIES);
|
||||
|
||||
props->insert({fname, std::shared_ptr<const TableProperties>(
|
||||
raw_table_properties)});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +421,6 @@ struct Saver {
|
||||
// the merge operations encountered;
|
||||
MergeContext* merge_context;
|
||||
Logger* logger;
|
||||
bool didIO; // did we do any disk io?
|
||||
Statistics* statistics;
|
||||
};
|
||||
}
|
||||
@@ -381,15 +439,14 @@ static void MarkKeyMayExist(void* arg) {
|
||||
}
|
||||
|
||||
static bool SaveValue(void* arg, const ParsedInternalKey& parsed_key,
|
||||
const Slice& v, bool didIO) {
|
||||
const Slice& v) {
|
||||
Saver* s = reinterpret_cast<Saver*>(arg);
|
||||
MergeContext* merge_contex = s->merge_context;
|
||||
std::string merge_result; // temporary area for merge results later
|
||||
|
||||
assert(s != nullptr && merge_contex != nullptr);
|
||||
|
||||
// TODO: didIO and Merge?
|
||||
s->didIO = didIO;
|
||||
// TODO: Merge?
|
||||
if (s->ucmp->Compare(parsed_key.user_key, s->user_key) == 0) {
|
||||
// Key matches. Process it
|
||||
switch (parsed_key.type) {
|
||||
@@ -445,9 +502,6 @@ static bool SaveValue(void* arg, const ParsedInternalKey& parsed_key,
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool NewestFirst(FileMetaData* a, FileMetaData* b) {
|
||||
return a->fd.GetNumber() > b->fd.GetNumber();
|
||||
}
|
||||
bool NewestFirstBySeqNo(FileMetaData* a, FileMetaData* b) {
|
||||
if (a->smallest_seqno != b->smallest_seqno) {
|
||||
return a->smallest_seqno > b->smallest_seqno;
|
||||
@@ -456,7 +510,7 @@ bool NewestFirstBySeqNo(FileMetaData* a, FileMetaData* b) {
|
||||
return a->largest_seqno > b->largest_seqno;
|
||||
}
|
||||
// Break ties by file number
|
||||
return NewestFirst(a, b);
|
||||
return a->fd.GetNumber() > b->fd.GetNumber();
|
||||
}
|
||||
bool BySmallestKey(FileMetaData* a, FileMetaData* b,
|
||||
const InternalKeyComparator* cmp) {
|
||||
@@ -493,13 +547,21 @@ Version::Version(ColumnFamilyData* cfd, VersionSet* vset,
|
||||
files_(new std::vector<FileMetaData*>[num_levels_]),
|
||||
files_by_size_(num_levels_),
|
||||
next_file_to_compact_by_size_(num_levels_),
|
||||
file_to_compact_(nullptr),
|
||||
file_to_compact_level_(-1),
|
||||
compaction_score_(num_levels_),
|
||||
compaction_level_(num_levels_),
|
||||
version_number_(version_number),
|
||||
file_indexer_(num_levels_, cfd == nullptr ? nullptr
|
||||
: cfd->internal_comparator().user_comparator()) {
|
||||
: cfd->internal_comparator().user_comparator()),
|
||||
total_file_size_(0),
|
||||
total_raw_key_size_(0),
|
||||
total_raw_value_size_(0),
|
||||
num_non_deletions_(0) {
|
||||
if (cfd != nullptr && cfd->current() != nullptr) {
|
||||
total_file_size_ = cfd->current()->total_file_size_;
|
||||
total_raw_key_size_ = cfd->current()->total_raw_key_size_;
|
||||
total_raw_value_size_ = cfd->current()->total_raw_value_size_;
|
||||
num_non_deletions_ = cfd->current()->num_non_deletions_;
|
||||
}
|
||||
}
|
||||
|
||||
void Version::Get(const ReadOptions& options,
|
||||
@@ -507,7 +569,6 @@ void Version::Get(const ReadOptions& options,
|
||||
std::string* value,
|
||||
Status* status,
|
||||
MergeContext* merge_context,
|
||||
GetStats* stats,
|
||||
bool* value_found) {
|
||||
Slice ikey = k.internal_key();
|
||||
Slice user_key = k.user_key();
|
||||
@@ -522,14 +583,8 @@ void Version::Get(const ReadOptions& options,
|
||||
saver.merge_operator = merge_operator_;
|
||||
saver.merge_context = merge_context;
|
||||
saver.logger = info_log_;
|
||||
saver.didIO = false;
|
||||
saver.statistics = db_statistics_;
|
||||
|
||||
stats->seek_file = nullptr;
|
||||
stats->seek_file_level = -1;
|
||||
FileMetaData* last_file_read = nullptr;
|
||||
int last_file_read_level = -1;
|
||||
|
||||
// We can search level-by-level since entries never hop across
|
||||
// levels. Therefore we are guaranteed that if we find data
|
||||
// in an smaller level, later levels are irrelevant (unless we
|
||||
@@ -538,7 +593,7 @@ void Version::Get(const ReadOptions& options,
|
||||
int32_t search_left_bound = 0;
|
||||
int32_t search_right_bound = FileIndexer::kLevelMaxIndex;
|
||||
for (int level = 0; level < num_non_empty_levels_; ++level) {
|
||||
int num_files = files_[level].size();
|
||||
int num_files = file_levels_[level].num_files;
|
||||
if (num_files == 0) {
|
||||
// When current level is empty, the search bound generated from upper
|
||||
// level must be [0, -1] or [0, FileIndexer::kLevelMaxIndex] if it is
|
||||
@@ -556,7 +611,7 @@ void Version::Get(const ReadOptions& options,
|
||||
// Prefetch table data to avoid cache miss if possible
|
||||
if (level == 0) {
|
||||
for (int i = 0; i < num_files; ++i) {
|
||||
auto* r = files_[0][i]->fd.table_reader;
|
||||
auto* r = file_levels_[0].files[i].fd.table_reader;
|
||||
if (r) {
|
||||
r->Prepare(ikey);
|
||||
}
|
||||
@@ -564,7 +619,7 @@ void Version::Get(const ReadOptions& options,
|
||||
}
|
||||
|
||||
// Get the list of files to search in this level
|
||||
FileMetaData* const* files = &files_[level][0];
|
||||
FdWithKeyRange* files = file_levels_[level].files;
|
||||
|
||||
// Some files may overlap each other. We find
|
||||
// all files that overlap user_key and process them in order from
|
||||
@@ -586,7 +641,8 @@ void Version::Get(const ReadOptions& options,
|
||||
search_right_bound = num_files - 1;
|
||||
}
|
||||
start_index = FindFileInRange(cfd_->internal_comparator(),
|
||||
files_[level], ikey, search_left_bound, search_right_bound);
|
||||
file_levels_[level], ikey,
|
||||
search_left_bound, search_right_bound);
|
||||
} else {
|
||||
// search_left_bound > search_right_bound, key does not exist in this
|
||||
// level. Since no comparision is done in this level, it will need to
|
||||
@@ -598,11 +654,11 @@ void Version::Get(const ReadOptions& options,
|
||||
}
|
||||
// Traverse each relevant file to find the desired key
|
||||
#ifndef NDEBUG
|
||||
FileMetaData* prev_file = nullptr;
|
||||
FdWithKeyRange* prev_file = nullptr;
|
||||
#endif
|
||||
|
||||
for (int32_t i = start_index; i < num_files;) {
|
||||
FileMetaData* f = files[i];
|
||||
FdWithKeyRange* f = &files[i];
|
||||
int cmp_largest = -1;
|
||||
|
||||
// Do key range filtering of files or/and fractional cascading if:
|
||||
@@ -617,15 +673,14 @@ void Version::Get(const ReadOptions& options,
|
||||
// Check if key is within a file's range. If search left bound and right
|
||||
// bound point to the same find, we are sure key falls in range.
|
||||
assert(
|
||||
level == 0 || i == start_index
|
||||
|| user_comparator_->Compare(user_key, f->smallest.user_key())
|
||||
<= 0);
|
||||
level == 0 || i == start_index || user_comparator_->Compare(
|
||||
user_key, ExtractUserKey(f->smallest_key)) <= 0);
|
||||
|
||||
int cmp_smallest = user_comparator_->Compare(user_key,
|
||||
f->smallest.user_key());
|
||||
ExtractUserKey(f->smallest_key));
|
||||
if (cmp_smallest >= 0) {
|
||||
cmp_largest = user_comparator_->Compare(user_key,
|
||||
f->largest.user_key());
|
||||
ExtractUserKey(f->largest_key));
|
||||
}
|
||||
|
||||
// Setup file search bound for the next level based on the comparison
|
||||
@@ -650,46 +705,25 @@ void Version::Get(const ReadOptions& options,
|
||||
// Sanity check to make sure that the files are correctly sorted
|
||||
if (prev_file) {
|
||||
if (level != 0) {
|
||||
int comp_sign =
|
||||
internal_comparator_->Compare(prev_file->largest, f->smallest);
|
||||
int comp_sign = internal_comparator_->Compare(prev_file->largest_key,
|
||||
f->smallest_key);
|
||||
assert(comp_sign < 0);
|
||||
} else {
|
||||
// level == 0, the current file cannot be newer than the previous one.
|
||||
if (cfd_->options()->compaction_style == kCompactionStyleUniversal) {
|
||||
assert(!NewestFirstBySeqNo(f, prev_file));
|
||||
} else {
|
||||
assert(!NewestFirst(f, prev_file));
|
||||
}
|
||||
// Use compressed data structure, has no attribute seqNo
|
||||
assert(i > 0);
|
||||
assert(!NewestFirstBySeqNo(files_[0][i], files_[0][i-1]));
|
||||
}
|
||||
}
|
||||
prev_file = f;
|
||||
#endif
|
||||
bool tableIO = false;
|
||||
*status = table_cache_->Get(options, *internal_comparator_, f->fd, ikey,
|
||||
&saver, SaveValue, &tableIO, MarkKeyMayExist);
|
||||
&saver, SaveValue, MarkKeyMayExist);
|
||||
// TODO: examine the behavior for corrupted key
|
||||
if (!status->ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_file_read != nullptr && stats->seek_file == nullptr) {
|
||||
// We have had more than one seek for this read. Charge the 1st file.
|
||||
stats->seek_file = last_file_read;
|
||||
stats->seek_file_level = last_file_read_level;
|
||||
}
|
||||
|
||||
// If we did any IO as part of the read, then we remember it because
|
||||
// it is a possible candidate for seek-based compaction. saver.didIO
|
||||
// is true if the block had to be read in from storage and was not
|
||||
// pre-exisiting in the block cache. Also, if this file was not pre-
|
||||
// existing in the table cache and had to be freshly opened that needed
|
||||
// the index blocks to be read-in, then tableIO is true. One thing
|
||||
// to note is that the index blocks are not part of the block cache.
|
||||
if (saver.didIO || tableIO) {
|
||||
last_file_read = f;
|
||||
last_file_read_level = level;
|
||||
}
|
||||
|
||||
switch (saver.state) {
|
||||
case kNotFound:
|
||||
break; // Keep searching in other files
|
||||
@@ -730,23 +764,97 @@ void Version::Get(const ReadOptions& options,
|
||||
}
|
||||
}
|
||||
|
||||
bool Version::UpdateStats(const GetStats& stats) {
|
||||
FileMetaData* f = stats.seek_file;
|
||||
if (f != nullptr) {
|
||||
f->allowed_seeks--;
|
||||
if (f->allowed_seeks <= 0 && file_to_compact_ == nullptr) {
|
||||
file_to_compact_ = f;
|
||||
file_to_compact_level_ = stats.seek_file_level;
|
||||
return true;
|
||||
void Version::GenerateFileLevels() {
|
||||
file_levels_.resize(num_non_empty_levels_);
|
||||
for (int level = 0; level < num_non_empty_levels_; level++) {
|
||||
const auto& files = files_[level];
|
||||
auto& file_level = file_levels_[level];
|
||||
|
||||
size_t num = files.size();
|
||||
file_level.num_files = num;
|
||||
char* mem = arena_.AllocateAligned(num * sizeof(FdWithKeyRange));
|
||||
file_level.files = new (mem)FdWithKeyRange[num];
|
||||
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
Slice smallest_key = files[i]->smallest.Encode();
|
||||
Slice largest_key = files[i]->largest.Encode();
|
||||
|
||||
// Copy key slice to sequential memory
|
||||
size_t smallest_size = smallest_key.size();
|
||||
size_t largest_size = largest_key.size();
|
||||
mem = arena_.AllocateAligned(smallest_size + largest_size);
|
||||
memcpy(mem, smallest_key.data(), smallest_size);
|
||||
memcpy(mem + smallest_size, largest_key.data(), largest_size);
|
||||
|
||||
file_level.files[i].fd = files[i]->fd;
|
||||
file_level.files[i].smallest_key = Slice(mem, smallest_size);
|
||||
file_level.files[i].largest_key = Slice(mem+smallest_size, largest_size);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Version::PrepareApply(std::vector<uint64_t>& size_being_compacted) {
|
||||
UpdateTemporaryStats();
|
||||
ComputeCompactionScore(size_being_compacted);
|
||||
UpdateFilesBySize();
|
||||
UpdateNumNonEmptyLevels();
|
||||
GenerateFileLevels();
|
||||
}
|
||||
|
||||
bool Version::MaybeInitializeFileMetaData(FileMetaData* file_meta) {
|
||||
if (file_meta->num_entries > 0) {
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
Status s = GetTableProperties(&tp, file_meta);
|
||||
if (!s.ok()) {
|
||||
return false;
|
||||
}
|
||||
if (tp.get() == nullptr) return false;
|
||||
file_meta->num_entries = tp->num_entries;
|
||||
file_meta->num_deletions = GetDeletedKeys(tp->user_collected_properties);
|
||||
file_meta->raw_value_size = tp->raw_value_size;
|
||||
file_meta->raw_key_size = tp->raw_key_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Version::UpdateTemporaryStats() {
|
||||
static const int kDeletionWeightOnCompaction = 2;
|
||||
|
||||
// incrementally update the average value size by
|
||||
// including newly added files into the global stats
|
||||
int init_count = 0;
|
||||
int total_count = 0;
|
||||
for (int level = 0; level < num_levels_; level++) {
|
||||
for (auto* file_meta : files_[level]) {
|
||||
if (MaybeInitializeFileMetaData(file_meta)) {
|
||||
// each FileMeta will be initialized only once.
|
||||
total_file_size_ += file_meta->fd.GetFileSize();
|
||||
total_raw_key_size_ += file_meta->raw_key_size;
|
||||
total_raw_value_size_ += file_meta->raw_value_size;
|
||||
num_non_deletions_ +=
|
||||
file_meta->num_entries - file_meta->num_deletions;
|
||||
init_count++;
|
||||
}
|
||||
total_count++;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t average_value_size = GetAverageValueSize();
|
||||
|
||||
// compute the compensated size
|
||||
for (int level = 0; level < num_levels_; level++) {
|
||||
for (auto* file_meta : files_[level]) {
|
||||
// Here we only compute compensated_file_size for those file_meta
|
||||
// which compensated_file_size is uninitialized (== 0).
|
||||
if (file_meta->compensated_file_size == 0) {
|
||||
file_meta->compensated_file_size = file_meta->fd.GetFileSize() +
|
||||
file_meta->num_deletions * average_value_size *
|
||||
kDeletionWeightOnCompaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Version::ComputeCompactionScore(
|
||||
@@ -778,7 +886,7 @@ void Version::ComputeCompactionScore(
|
||||
uint64_t total_size = 0;
|
||||
for (unsigned int i = 0; i < files_[level].size(); i++) {
|
||||
if (!files_[level][i]->being_compacted) {
|
||||
total_size += files_[level][i]->fd.GetFileSize();
|
||||
total_size += files_[level][i]->compensated_file_size;
|
||||
numfiles++;
|
||||
}
|
||||
}
|
||||
@@ -797,7 +905,7 @@ void Version::ComputeCompactionScore(
|
||||
} else {
|
||||
// Compute the ratio of current size to size limit.
|
||||
const uint64_t level_bytes =
|
||||
TotalFileSize(files_[level]) - size_being_compacted[level];
|
||||
TotalCompensatedFileSize(files_[level]) - size_being_compacted[level];
|
||||
score = static_cast<double>(level_bytes) /
|
||||
cfd_->compaction_picker()->MaxBytesForLevel(level);
|
||||
if (max_score < score) {
|
||||
@@ -830,25 +938,13 @@ void Version::ComputeCompactionScore(
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Compator that is used to sort files based on their size
|
||||
// In normal mode: descending size
|
||||
bool CompareSizeDescending(const Version::Fsize& first,
|
||||
const Version::Fsize& second) {
|
||||
return (first.file->fd.GetFileSize() > second.file->fd.GetFileSize());
|
||||
bool CompareCompensatedSizeDescending(const Version::Fsize& first,
|
||||
const Version::Fsize& second) {
|
||||
return (first.file->compensated_file_size >
|
||||
second.file->compensated_file_size);
|
||||
}
|
||||
// A static compator used to sort files based on their seqno
|
||||
// In universal style : descending seqno
|
||||
bool CompareSeqnoDescending(const Version::Fsize& first,
|
||||
const Version::Fsize& second) {
|
||||
if (first.file->smallest_seqno > second.file->smallest_seqno) {
|
||||
assert(first.file->largest_seqno > second.file->largest_seqno);
|
||||
return true;
|
||||
}
|
||||
assert(first.file->largest_seqno <= second.file->largest_seqno);
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void Version::UpdateNumNonEmptyLevels() {
|
||||
@@ -863,19 +959,15 @@ void Version::UpdateNumNonEmptyLevels() {
|
||||
}
|
||||
|
||||
void Version::UpdateFilesBySize() {
|
||||
if (cfd_->options()->compaction_style == kCompactionStyleFIFO) {
|
||||
if (cfd_->options()->compaction_style == kCompactionStyleFIFO ||
|
||||
cfd_->options()->compaction_style == kCompactionStyleUniversal) {
|
||||
// don't need this
|
||||
return;
|
||||
}
|
||||
// No need to sort the highest level because it is never compacted.
|
||||
int max_level =
|
||||
(cfd_->options()->compaction_style == kCompactionStyleUniversal)
|
||||
? NumberLevels()
|
||||
: NumberLevels() - 1;
|
||||
|
||||
for (int level = 0; level < max_level; level++) {
|
||||
for (int level = 0; level < NumberLevels() - 1; level++) {
|
||||
const std::vector<FileMetaData*>& files = files_[level];
|
||||
std::vector<int>& files_by_size = files_by_size_[level];
|
||||
auto& files_by_size = files_by_size_[level];
|
||||
assert(files_by_size.size() == 0);
|
||||
|
||||
// populate a temp vector for sorting based on size
|
||||
@@ -886,18 +978,12 @@ void Version::UpdateFilesBySize() {
|
||||
}
|
||||
|
||||
// sort the top number_of_files_to_sort_ based on file size
|
||||
if (cfd_->options()->compaction_style == kCompactionStyleUniversal) {
|
||||
int num = temp.size();
|
||||
std::partial_sort(temp.begin(), temp.begin() + num, temp.end(),
|
||||
CompareSeqnoDescending);
|
||||
} else {
|
||||
int num = Version::number_of_files_to_sort_;
|
||||
if (num > (int)temp.size()) {
|
||||
num = temp.size();
|
||||
}
|
||||
std::partial_sort(temp.begin(), temp.begin() + num, temp.end(),
|
||||
CompareSizeDescending);
|
||||
size_t num = Version::number_of_files_to_sort_;
|
||||
if (num > temp.size()) {
|
||||
num = temp.size();
|
||||
}
|
||||
std::partial_sort(temp.begin(), temp.begin() + num, temp.end(),
|
||||
CompareCompensatedSizeDescending);
|
||||
assert(temp.size() == files.size());
|
||||
|
||||
// initialize files_by_size_
|
||||
@@ -924,9 +1010,6 @@ bool Version::Unref() {
|
||||
}
|
||||
|
||||
bool Version::NeedsCompaction() const {
|
||||
if (file_to_compact_ != nullptr) {
|
||||
return true;
|
||||
}
|
||||
// In universal compaction case, this check doesn't really
|
||||
// check the compaction condition, but checks num of files threshold
|
||||
// only. We are not going to miss any compaction opportunity
|
||||
@@ -951,7 +1034,7 @@ bool Version::OverlapInLevel(int level,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key) {
|
||||
return SomeFileOverlapsRange(cfd_->internal_comparator(), (level > 0),
|
||||
files_[level], smallest_user_key,
|
||||
file_levels_[level], smallest_user_key,
|
||||
largest_user_key);
|
||||
}
|
||||
|
||||
@@ -1013,16 +1096,16 @@ void Version::GetOverlappingInputs(int level,
|
||||
hint_index, file_index);
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < files_[level].size(); ) {
|
||||
FileMetaData* f = files_[level][i++];
|
||||
const Slice file_start = f->smallest.user_key();
|
||||
const Slice file_limit = f->largest.user_key();
|
||||
for (size_t i = 0; i < file_levels_[level].num_files; ) {
|
||||
FdWithKeyRange* f = &(file_levels_[level].files[i++]);
|
||||
const Slice file_start = ExtractUserKey(f->smallest_key);
|
||||
const Slice file_limit = ExtractUserKey(f->largest_key);
|
||||
if (begin != nullptr && user_cmp->Compare(file_limit, user_begin) < 0) {
|
||||
// "f" is completely before specified range; skip it
|
||||
} else if (end != nullptr && user_cmp->Compare(file_start, user_end) > 0) {
|
||||
// "f" is completely after specified range; skip it
|
||||
} else {
|
||||
inputs->push_back(f);
|
||||
inputs->push_back(files_[level][i-1]);
|
||||
if (level == 0) {
|
||||
// Level-0 files may overlap each other. So check if the newly
|
||||
// added file has expanded the range. If so, restart search.
|
||||
@@ -1070,9 +1153,9 @@ void Version::GetOverlappingInputsBinarySearch(
|
||||
|
||||
while (!foundOverlap && min <= max) {
|
||||
mid = (min + max)/2;
|
||||
FileMetaData* f = files_[level][mid];
|
||||
const Slice file_start = f->smallest.user_key();
|
||||
const Slice file_limit = f->largest.user_key();
|
||||
FdWithKeyRange* f = &(file_levels_[level].files[mid]);
|
||||
const Slice file_start = ExtractUserKey(f->smallest_key);
|
||||
const Slice file_limit = ExtractUserKey(f->largest_key);
|
||||
if (user_cmp->Compare(file_limit, user_begin) < 0) {
|
||||
min = mid + 1;
|
||||
} else if (user_cmp->Compare(user_end, file_start) < 0) {
|
||||
@@ -1098,6 +1181,7 @@ void Version::GetOverlappingInputsBinarySearch(
|
||||
// The midIndex specifies the index of at least one file that
|
||||
// overlaps the specified range. From that file, iterate backward
|
||||
// and forward to find all overlapping files.
|
||||
// Use compressed file meda data, make search faster
|
||||
void Version::ExtendOverlappingInputs(
|
||||
int level,
|
||||
const Slice& user_begin,
|
||||
@@ -1106,13 +1190,14 @@ void Version::ExtendOverlappingInputs(
|
||||
unsigned int midIndex) {
|
||||
|
||||
const Comparator* user_cmp = cfd_->internal_comparator().user_comparator();
|
||||
const FdWithKeyRange* files = file_levels_[level].files;
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
// assert that the file at midIndex overlaps with the range
|
||||
assert(midIndex < files_[level].size());
|
||||
FileMetaData* f = files_[level][midIndex];
|
||||
const Slice fstart = f->smallest.user_key();
|
||||
const Slice flimit = f->largest.user_key();
|
||||
assert(midIndex < file_levels_[level].num_files);
|
||||
const FdWithKeyRange* f = &files[midIndex];
|
||||
const Slice fstart = ExtractUserKey(f->smallest_key);
|
||||
const Slice flimit = ExtractUserKey(f->largest_key);
|
||||
if (user_cmp->Compare(fstart, user_begin) >= 0) {
|
||||
assert(user_cmp->Compare(fstart, user_end) <= 0);
|
||||
} else {
|
||||
@@ -1126,8 +1211,8 @@ void Version::ExtendOverlappingInputs(
|
||||
|
||||
// check backwards from 'mid' to lower indices
|
||||
for (int i = midIndex; i >= 0 ; i--) {
|
||||
FileMetaData* f = files_[level][i];
|
||||
const Slice file_limit = f->largest.user_key();
|
||||
const FdWithKeyRange* f = &files[i];
|
||||
const Slice file_limit = ExtractUserKey(f->largest_key);
|
||||
if (user_cmp->Compare(file_limit, user_begin) >= 0) {
|
||||
startIndex = i;
|
||||
assert((count++, true));
|
||||
@@ -1136,9 +1221,9 @@ void Version::ExtendOverlappingInputs(
|
||||
}
|
||||
}
|
||||
// check forward from 'mid+1' to higher indices
|
||||
for (unsigned int i = midIndex+1; i < files_[level].size(); i++) {
|
||||
FileMetaData* f = files_[level][i];
|
||||
const Slice file_start = f->smallest.user_key();
|
||||
for (unsigned int i = midIndex+1; i < file_levels_[level].num_files; i++) {
|
||||
const FdWithKeyRange* f = &files[i];
|
||||
const Slice file_start = ExtractUserKey(f->smallest_key);
|
||||
if (user_cmp->Compare(file_start, user_end) <= 0) {
|
||||
assert((count++, true));
|
||||
endIndex = i;
|
||||
@@ -1170,16 +1255,19 @@ bool Version::HasOverlappingUserKey(
|
||||
}
|
||||
|
||||
const Comparator* user_cmp = cfd_->internal_comparator().user_comparator();
|
||||
const std::vector<FileMetaData*>& files = files_[level];
|
||||
const size_t kNumFiles = files.size();
|
||||
const FileLevel& file_level = file_levels_[level];
|
||||
const FdWithKeyRange* files = file_levels_[level].files;
|
||||
const size_t kNumFiles = file_level.num_files;
|
||||
|
||||
// Check the last file in inputs against the file after it
|
||||
size_t last_file = FindFile(cfd_->internal_comparator(), files,
|
||||
size_t last_file = FindFile(cfd_->internal_comparator(), file_level,
|
||||
inputs->back()->largest.Encode());
|
||||
assert(0 <= last_file && last_file < kNumFiles); // File should exist!
|
||||
if (last_file < kNumFiles-1) { // If not the last file
|
||||
const Slice last_key_in_input = files[last_file]->largest.user_key();
|
||||
const Slice first_key_after = files[last_file+1]->smallest.user_key();
|
||||
const Slice last_key_in_input = ExtractUserKey(
|
||||
files[last_file].largest_key);
|
||||
const Slice first_key_after = ExtractUserKey(
|
||||
files[last_file+1].smallest_key);
|
||||
if (user_cmp->Compare(last_key_in_input, first_key_after) == 0) {
|
||||
// The last user key in input overlaps with the next file's first key
|
||||
return true;
|
||||
@@ -1187,12 +1275,14 @@ bool Version::HasOverlappingUserKey(
|
||||
}
|
||||
|
||||
// Check the first file in inputs against the file just before it
|
||||
size_t first_file = FindFile(cfd_->internal_comparator(), files,
|
||||
size_t first_file = FindFile(cfd_->internal_comparator(), file_level,
|
||||
inputs->front()->smallest.Encode());
|
||||
assert(0 <= first_file && first_file <= last_file); // File should exist!
|
||||
if (first_file > 0) { // If not first file
|
||||
const Slice& first_key_in_input = files[first_file]->smallest.user_key();
|
||||
const Slice& last_key_before = files[first_file-1]->largest.user_key();
|
||||
const Slice& first_key_in_input = ExtractUserKey(
|
||||
files[first_file].smallest_key);
|
||||
const Slice& last_key_before = ExtractUserKey(
|
||||
files[first_file-1].largest_key);
|
||||
if (user_cmp->Compare(first_key_in_input, last_key_before) == 0) {
|
||||
// The first user key in input overlaps with the previous file's last key
|
||||
return true;
|
||||
@@ -1262,11 +1352,11 @@ int64_t Version::MaxNextLevelOverlappingBytes() {
|
||||
return result;
|
||||
}
|
||||
|
||||
void Version::AddLiveFiles(std::set<uint64_t>* live) {
|
||||
void Version::AddLiveFiles(std::vector<FileDescriptor>* live) {
|
||||
for (int level = 0; level < NumberLevels(); level++) {
|
||||
const std::vector<FileMetaData*>& files = files_[level];
|
||||
for (const auto& file : files) {
|
||||
live->insert(file->fd.GetNumber());
|
||||
live->push_back(file->fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1318,22 +1408,18 @@ struct VersionSet::ManifestWriter {
|
||||
class VersionSet::Builder {
|
||||
private:
|
||||
// Helper to sort v->files_
|
||||
// kLevel0LevelCompaction -- NewestFirst (also used for FIFO compaction)
|
||||
// kLevel0UniversalCompaction -- NewestFirstBySeqNo
|
||||
// kLevel0 -- NewestFirstBySeqNo
|
||||
// kLevelNon0 -- BySmallestKey
|
||||
struct FileComparator {
|
||||
enum SortMethod {
|
||||
kLevel0LevelCompaction = 0,
|
||||
kLevel0UniversalCompaction = 1,
|
||||
kLevelNon0 = 2,
|
||||
kLevel0 = 0,
|
||||
kLevelNon0 = 1,
|
||||
} sort_method;
|
||||
const InternalKeyComparator* internal_comparator;
|
||||
|
||||
bool operator()(FileMetaData* f1, FileMetaData* f2) const {
|
||||
switch (sort_method) {
|
||||
case kLevel0LevelCompaction:
|
||||
return NewestFirst(f1, f2);
|
||||
case kLevel0UniversalCompaction:
|
||||
case kLevel0:
|
||||
return NewestFirstBySeqNo(f1, f2);
|
||||
case kLevelNon0:
|
||||
return BySmallestKey(f1, f2, internal_comparator);
|
||||
@@ -1359,10 +1445,7 @@ class VersionSet::Builder {
|
||||
Builder(ColumnFamilyData* cfd) : cfd_(cfd), base_(cfd->current()) {
|
||||
base_->Ref();
|
||||
levels_ = new LevelState[base_->NumberLevels()];
|
||||
level_zero_cmp_.sort_method =
|
||||
(cfd_->options()->compaction_style == kCompactionStyleUniversal)
|
||||
? FileComparator::kLevel0UniversalCompaction
|
||||
: FileComparator::kLevel0LevelCompaction;
|
||||
level_zero_cmp_.sort_method = FileComparator::kLevel0;
|
||||
level_nonzero_cmp_.sort_method = FileComparator::kLevelNon0;
|
||||
level_nonzero_cmp_.internal_comparator = &cfd->internal_comparator();
|
||||
|
||||
@@ -1408,9 +1491,7 @@ class VersionSet::Builder {
|
||||
auto f2 = v->files_[level][i];
|
||||
if (level == 0) {
|
||||
assert(level_zero_cmp_(f1, f2));
|
||||
if (cfd_->options()->compaction_style == kCompactionStyleUniversal) {
|
||||
assert(f1->largest_seqno > f2->largest_seqno);
|
||||
}
|
||||
assert(f1->largest_seqno > f2->largest_seqno);
|
||||
} else {
|
||||
assert(level_nonzero_cmp_(f1, f2));
|
||||
|
||||
@@ -1428,7 +1509,7 @@ class VersionSet::Builder {
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckConsistencyForDeletes(VersionEdit* edit, unsigned int number,
|
||||
void CheckConsistencyForDeletes(VersionEdit* edit, uint64_t number,
|
||||
int level) {
|
||||
#ifndef NDEBUG
|
||||
// a file to be deleted better exist in the previous version
|
||||
@@ -1470,6 +1551,9 @@ class VersionSet::Builder {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fprintf(stderr, "not found %" PRIu64 "\n", number);
|
||||
}
|
||||
assert(found);
|
||||
#endif
|
||||
}
|
||||
@@ -1493,22 +1577,6 @@ class VersionSet::Builder {
|
||||
FileMetaData* f = new FileMetaData(new_file.second);
|
||||
f->refs = 1;
|
||||
|
||||
// We arrange to automatically compact this file after
|
||||
// a certain number of seeks. Let's assume:
|
||||
// (1) One seek costs 10ms
|
||||
// (2) Writing or reading 1MB costs 10ms (100MB/s)
|
||||
// (3) A compaction of 1MB does 25MB of IO:
|
||||
// 1MB read from this level
|
||||
// 10-12MB read from next level (boundaries may be misaligned)
|
||||
// 10-12MB written to next level
|
||||
// This implies that 25 seeks cost the same as the compaction
|
||||
// of 1MB of data. I.e., one seek costs approximately the
|
||||
// same as the compaction of 40KB of data. We are a little
|
||||
// conservative and allow approximately one seek for every 16KB
|
||||
// of data before triggering a compaction.
|
||||
f->allowed_seeks = (f->fd.GetFileSize() / 16384);
|
||||
if (f->allowed_seeks < 100) f->allowed_seeks = 100;
|
||||
|
||||
levels_[level].deleted_files.erase(f->fd.GetNumber());
|
||||
levels_[level].added_files->insert(f);
|
||||
}
|
||||
@@ -1555,10 +1623,9 @@ class VersionSet::Builder {
|
||||
for (int level = 0; level < cfd_->NumberLevels(); level++) {
|
||||
for (auto& file_meta : *(levels_[level].added_files)) {
|
||||
assert (!file_meta->table_reader_handle);
|
||||
bool table_io;
|
||||
cfd_->table_cache()->FindTable(
|
||||
base_->vset_->storage_options_, cfd_->internal_comparator(),
|
||||
file_meta->fd, &file_meta->table_reader_handle, &table_io, false);
|
||||
file_meta->fd, &file_meta->table_reader_handle, false);
|
||||
if (file_meta->table_reader_handle != nullptr) {
|
||||
// Load table_reader
|
||||
file_meta->fd.table_reader =
|
||||
@@ -2179,17 +2246,15 @@ Status VersionSet::Recover(
|
||||
last_sequence_ = last_sequence;
|
||||
prev_log_number_ = prev_log_number;
|
||||
|
||||
Log(options_->info_log, "Recovered from manifest file:%s succeeded,"
|
||||
Log(options_->info_log,
|
||||
"Recovered from manifest file:%s succeeded,"
|
||||
"manifest_file_number is %lu, next_file_number is %lu, "
|
||||
"last_sequence is %lu, log_number is %lu,"
|
||||
"prev_log_number is %lu,"
|
||||
"max_column_family is %u\n",
|
||||
manifest_filename.c_str(),
|
||||
(unsigned long)manifest_file_number_,
|
||||
(unsigned long)next_file_number_,
|
||||
(unsigned long)last_sequence_,
|
||||
(unsigned long)log_number,
|
||||
(unsigned long)prev_log_number_,
|
||||
manifest_filename.c_str(), (unsigned long)manifest_file_number_,
|
||||
(unsigned long)next_file_number_, (unsigned long)last_sequence_,
|
||||
(unsigned long)log_number, (unsigned long)prev_log_number_,
|
||||
column_family_set_->GetMaxColumnFamily());
|
||||
|
||||
for (auto cfd : *column_family_set_) {
|
||||
@@ -2576,9 +2641,9 @@ Status VersionSet::WriteSnapshot(log::Writer* log) {
|
||||
|
||||
for (int level = 0; level < cfd->NumberLevels(); level++) {
|
||||
for (const auto& f : cfd->current()->files_[level]) {
|
||||
edit.AddFile(level, f->fd.GetNumber(), f->fd.GetFileSize(),
|
||||
f->smallest, f->largest, f->smallest_seqno,
|
||||
f->largest_seqno);
|
||||
edit.AddFile(level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->smallest_seqno, f->largest_seqno);
|
||||
}
|
||||
}
|
||||
edit.SetLogNumber(cfd->GetLogNumber());
|
||||
@@ -2660,7 +2725,7 @@ uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void VersionSet::AddLiveFiles(std::vector<uint64_t>* live_list) {
|
||||
void VersionSet::AddLiveFiles(std::vector<FileDescriptor>* live_list) {
|
||||
// pre-calculate space requirement
|
||||
int64_t total_files = 0;
|
||||
for (auto cfd : *column_family_set_) {
|
||||
@@ -2682,7 +2747,7 @@ void VersionSet::AddLiveFiles(std::vector<uint64_t>* live_list) {
|
||||
v = v->next_) {
|
||||
for (int level = 0; level < v->NumberLevels(); level++) {
|
||||
for (const auto& f : v->files_[level]) {
|
||||
live_list->push_back(f->fd.GetNumber());
|
||||
live_list->push_back(f->fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2805,7 +2870,14 @@ void VersionSet::GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) {
|
||||
for (const auto& file : cfd->current()->files_[level]) {
|
||||
LiveFileMetaData filemetadata;
|
||||
filemetadata.column_family_name = cfd->GetName();
|
||||
filemetadata.name = TableFileName("", file->fd.GetNumber());
|
||||
uint32_t path_id = file->fd.GetPathId();
|
||||
if (path_id < options_->db_paths.size()) {
|
||||
filemetadata.db_path = options_->db_paths[path_id];
|
||||
} else {
|
||||
assert(!options_->db_paths.empty());
|
||||
filemetadata.db_path = options_->db_paths.back();
|
||||
}
|
||||
filemetadata.name = MakeTableFileName("", file->fd.GetNumber());
|
||||
filemetadata.level = level;
|
||||
filemetadata.size = file->fd.GetFileSize();
|
||||
filemetadata.smallestkey = file->smallest.user_key().ToString();
|
||||
|
||||
+63
-23
@@ -53,6 +53,7 @@ class ColumnFamilySet;
|
||||
class TableCache;
|
||||
class MergeIteratorBuilder;
|
||||
|
||||
|
||||
// Return the smallest index i such that files[i]->largest >= key.
|
||||
// Return files.size() if there is no such file.
|
||||
// REQUIRES: "files" contains a sorted list of non-overlapping files.
|
||||
@@ -60,16 +61,24 @@ extern int FindFile(const InternalKeyComparator& icmp,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const Slice& key);
|
||||
|
||||
// Return the smallest index i such that file_level.files[i]->largest >= key.
|
||||
// Return file_level.num_files if there is no such file.
|
||||
// REQUIRES: "file_level.files" contains a sorted list of
|
||||
// non-overlapping files.
|
||||
extern int FindFile(const InternalKeyComparator& icmp,
|
||||
const FileLevel& file_level,
|
||||
const Slice& key);
|
||||
|
||||
// Returns true iff some file in "files" overlaps the user key range
|
||||
// [*smallest,*largest].
|
||||
// smallest==nullptr represents a key smaller than all keys in the DB.
|
||||
// largest==nullptr represents a key largest than all keys in the DB.
|
||||
// REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges
|
||||
// in sorted order.
|
||||
// REQUIRES: If disjoint_sorted_files, file_level.files[]
|
||||
// contains disjoint ranges in sorted order.
|
||||
extern bool SomeFileOverlapsRange(
|
||||
const InternalKeyComparator& icmp,
|
||||
bool disjoint_sorted_files,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const FileLevel& file_level,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key);
|
||||
|
||||
@@ -85,29 +94,22 @@ class Version {
|
||||
MergeIteratorBuilder* merger_iter_builder);
|
||||
|
||||
// Lookup the value for key. If found, store it in *val and
|
||||
// return OK. Else return a non-OK status. Fills *stats.
|
||||
// return OK. Else return a non-OK status.
|
||||
// Uses *operands to store merge_operator operations to apply later
|
||||
// REQUIRES: lock is not held
|
||||
struct GetStats {
|
||||
FileMetaData* seek_file;
|
||||
int seek_file_level;
|
||||
};
|
||||
|
||||
void Get(const ReadOptions&, const LookupKey& key, std::string* val,
|
||||
Status* status, MergeContext* merge_context, GetStats* stats,
|
||||
Status* status, MergeContext* merge_context,
|
||||
bool* value_found = nullptr);
|
||||
|
||||
// Adds "stats" into the current state. Returns true if a new
|
||||
// compaction may need to be triggered, false otherwise.
|
||||
// REQUIRES: lock is held
|
||||
bool UpdateStats(const GetStats& stats);
|
||||
|
||||
// Updates internal structures that keep track of compaction scores
|
||||
// We use compaction scores to figure out which compaction to do next
|
||||
// REQUIRES: If Version is not yet saved to current_, it can be called without
|
||||
// a lock. Once a version is saved to current_, call only with mutex held
|
||||
void ComputeCompactionScore(std::vector<uint64_t>& size_being_compacted);
|
||||
|
||||
// Generate file_levels_ from files_
|
||||
void GenerateFileLevels();
|
||||
|
||||
// Update scores, pre-calculated variables. It needs to be called before
|
||||
// applying the version to the version set.
|
||||
void PrepareApply(std::vector<uint64_t>& size_being_compacted);
|
||||
@@ -198,7 +200,7 @@ class Version {
|
||||
int64_t MaxNextLevelOverlappingBytes();
|
||||
|
||||
// Add all files listed in the current version to *live.
|
||||
void AddLiveFiles(std::set<uint64_t>* live);
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live);
|
||||
|
||||
// Return a human readable string that describes this version's contents.
|
||||
std::string DebugString(bool hex = false) const;
|
||||
@@ -206,6 +208,25 @@ class Version {
|
||||
// Returns the version nuber of this version
|
||||
uint64_t GetVersionNumber() const { return version_number_; }
|
||||
|
||||
uint64_t GetAverageValueSize() const {
|
||||
if (num_non_deletions_ == 0) {
|
||||
return 0;
|
||||
}
|
||||
assert(total_raw_key_size_ + total_raw_value_size_ > 0);
|
||||
assert(total_file_size_ > 0);
|
||||
return total_raw_value_size_ / num_non_deletions_ * total_file_size_ /
|
||||
(total_raw_key_size_ + total_raw_value_size_);
|
||||
}
|
||||
|
||||
// REQUIRES: lock is held
|
||||
// On success, "tp" will contains the table properties of the file
|
||||
// specified in "file_meta". If the file name of "file_meta" is
|
||||
// known ahread, passing it by a non-null "fname" can save a
|
||||
// file-name conversion.
|
||||
Status GetTableProperties(std::shared_ptr<const TableProperties>* tp,
|
||||
const FileMetaData* file_meta,
|
||||
const std::string* fname = nullptr);
|
||||
|
||||
// REQUIRES: lock is held
|
||||
// On success, *props will be populated with all SSTables' table properties.
|
||||
// The keys of `props` are the sst file name, the values of `props` are the
|
||||
@@ -238,6 +259,15 @@ class Version {
|
||||
// Update num_non_empty_levels_.
|
||||
void UpdateNumNonEmptyLevels();
|
||||
|
||||
// The helper function of UpdateTemporaryStats, which may fill the missing
|
||||
// fields of file_mata from its associated TableProperties.
|
||||
// Returns true if it does initialize FileMetaData.
|
||||
bool MaybeInitializeFileMetaData(FileMetaData* file_meta);
|
||||
|
||||
// Update the temporary stats associated with the current version.
|
||||
// This temporary stats will be used in compaction.
|
||||
void UpdateTemporaryStats();
|
||||
|
||||
// Sort all files for this version based on their file size and
|
||||
// record results in files_by_size_. The largest files are listed first.
|
||||
void UpdateFilesBySize();
|
||||
@@ -247,12 +277,15 @@ class Version {
|
||||
const Comparator* user_comparator_;
|
||||
TableCache* table_cache_;
|
||||
const MergeOperator* merge_operator_;
|
||||
|
||||
autovector<FileLevel> file_levels_; // A copy of list of files per level
|
||||
Logger* info_log_;
|
||||
Statistics* db_statistics_;
|
||||
int num_levels_; // Number of levels
|
||||
int num_non_empty_levels_; // Number of levels. Any level larger than it
|
||||
// is guaranteed to be empty.
|
||||
VersionSet* vset_; // VersionSet to which this Version belongs
|
||||
Arena arena_; // Used to allocate space for file_levels_
|
||||
Version* next_; // Next version in linked list
|
||||
Version* prev_; // Previous version in linked list
|
||||
int refs_; // Number of live refs to this version
|
||||
@@ -261,6 +294,7 @@ class Version {
|
||||
// in increasing order of keys
|
||||
std::vector<FileMetaData*>* files_;
|
||||
|
||||
|
||||
// A list for the same set of files that are stored in files_,
|
||||
// but files in each level are now sorted based on file
|
||||
// size. The file with the largest size is at the front.
|
||||
@@ -276,11 +310,7 @@ class Version {
|
||||
// that on a running system, we need to look at only the first
|
||||
// few largest files because a new version is created every few
|
||||
// seconds/minutes (because of concurrent compactions).
|
||||
static const int number_of_files_to_sort_ = 50;
|
||||
|
||||
// Next file to compact based on seek stats.
|
||||
FileMetaData* file_to_compact_;
|
||||
int file_to_compact_level_;
|
||||
static const size_t number_of_files_to_sort_ = 50;
|
||||
|
||||
// Level that should be compacted next and its compaction score.
|
||||
// Score < 1 means compaction is not strictly needed. These fields
|
||||
@@ -299,6 +329,16 @@ class Version {
|
||||
Version(ColumnFamilyData* cfd, VersionSet* vset, uint64_t version_number = 0);
|
||||
FileIndexer file_indexer_;
|
||||
|
||||
// total file size
|
||||
uint64_t total_file_size_;
|
||||
// the total size of all raw keys.
|
||||
uint64_t total_raw_key_size_;
|
||||
// the total size of all raw values.
|
||||
uint64_t total_raw_value_size_;
|
||||
// total number of non-deletion entries
|
||||
uint64_t num_non_deletions_;
|
||||
|
||||
|
||||
~Version();
|
||||
|
||||
// re-initializes the index that is used to offset into files_by_size_
|
||||
@@ -375,7 +415,7 @@ class VersionSet {
|
||||
// Arrange to reuse "file_number" unless a newer file number has
|
||||
// already been allocated.
|
||||
// REQUIRES: "file_number" was returned by a call to NewFileNumber().
|
||||
void ReuseFileNumber(uint64_t file_number) {
|
||||
void ReuseLogFileNumber(uint64_t file_number) {
|
||||
if (next_file_number_ == file_number + 1) {
|
||||
next_file_number_ = file_number;
|
||||
}
|
||||
@@ -416,7 +456,7 @@ class VersionSet {
|
||||
Iterator* MakeInputIterator(Compaction* c);
|
||||
|
||||
// Add all files listed in any live version to *live.
|
||||
void AddLiveFiles(std::vector<uint64_t>* live_list);
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live_list);
|
||||
|
||||
// Return the approximate offset in the database of the data for
|
||||
// "key" as of version "v".
|
||||
|
||||
+107
-8
@@ -31,7 +31,7 @@ class FindFileTest {
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100) {
|
||||
FileMetaData* f = new FileMetaData;
|
||||
f->fd = FileDescriptor(files_.size() + 1, 0);
|
||||
f->fd = FileDescriptor(files_.size() + 1, 0, 0);
|
||||
f->smallest = InternalKey(smallest, smallest_seq, kTypeValue);
|
||||
f->largest = InternalKey(largest, largest_seq, kTypeValue);
|
||||
files_.push_back(f);
|
||||
@@ -42,18 +42,108 @@ class FindFileTest {
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
return FindFile(cmp, files_, target.Encode());
|
||||
}
|
||||
};
|
||||
|
||||
TEST(FindFileTest, Empty) {
|
||||
ASSERT_EQ(0, Find("foo"));
|
||||
}
|
||||
|
||||
TEST(FindFileTest, Single) {
|
||||
Add("p", "q");
|
||||
ASSERT_EQ(0, Find("a"));
|
||||
ASSERT_EQ(0, Find("p"));
|
||||
ASSERT_EQ(0, Find("p1"));
|
||||
ASSERT_EQ(0, Find("q"));
|
||||
ASSERT_EQ(1, Find("q1"));
|
||||
ASSERT_EQ(1, Find("z"));
|
||||
}
|
||||
|
||||
|
||||
TEST(FindFileTest, Multiple) {
|
||||
Add("150", "200");
|
||||
Add("200", "250");
|
||||
Add("300", "350");
|
||||
Add("400", "450");
|
||||
ASSERT_EQ(0, Find("100"));
|
||||
ASSERT_EQ(0, Find("150"));
|
||||
ASSERT_EQ(0, Find("151"));
|
||||
ASSERT_EQ(0, Find("199"));
|
||||
ASSERT_EQ(0, Find("200"));
|
||||
ASSERT_EQ(1, Find("201"));
|
||||
ASSERT_EQ(1, Find("249"));
|
||||
ASSERT_EQ(1, Find("250"));
|
||||
ASSERT_EQ(2, Find("251"));
|
||||
ASSERT_EQ(2, Find("299"));
|
||||
ASSERT_EQ(2, Find("300"));
|
||||
ASSERT_EQ(2, Find("349"));
|
||||
ASSERT_EQ(2, Find("350"));
|
||||
ASSERT_EQ(3, Find("351"));
|
||||
ASSERT_EQ(3, Find("400"));
|
||||
ASSERT_EQ(3, Find("450"));
|
||||
ASSERT_EQ(4, Find("451"));
|
||||
}
|
||||
|
||||
class FindLevelFileTest {
|
||||
public:
|
||||
FileLevel level_files_;
|
||||
bool disjoint_sorted_files_;
|
||||
Arena arena_;
|
||||
|
||||
FindLevelFileTest() : disjoint_sorted_files_(true) { }
|
||||
|
||||
~FindLevelFileTest() {
|
||||
}
|
||||
|
||||
void LevelFileInit(size_t num = 0) {
|
||||
char* mem = arena_.AllocateAligned(num * sizeof(FdWithKeyRange));
|
||||
level_files_.files = new (mem)FdWithKeyRange[num];
|
||||
level_files_.num_files = 0;
|
||||
}
|
||||
|
||||
void Add(const char* smallest, const char* largest,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100) {
|
||||
InternalKey smallest_key = InternalKey(smallest, smallest_seq, kTypeValue);
|
||||
InternalKey largest_key = InternalKey(largest, largest_seq, kTypeValue);
|
||||
|
||||
Slice smallest_slice = smallest_key.Encode();
|
||||
Slice largest_slice = largest_key.Encode();
|
||||
|
||||
char* mem = arena_.AllocateAligned(
|
||||
smallest_slice.size() + largest_slice.size());
|
||||
memcpy(mem, smallest_slice.data(), smallest_slice.size());
|
||||
memcpy(mem + smallest_slice.size(), largest_slice.data(),
|
||||
largest_slice.size());
|
||||
|
||||
// add compressd_level_
|
||||
size_t num = level_files_.num_files;
|
||||
auto& file = level_files_.files[num];
|
||||
file.fd = FileDescriptor(num + 1, 0, 0);
|
||||
file.smallest_key = Slice(mem, smallest_slice.size());
|
||||
file.largest_key = Slice(mem + smallest_slice.size(),
|
||||
largest_slice.size());
|
||||
level_files_.num_files++;
|
||||
}
|
||||
|
||||
int Find(const char* key) {
|
||||
InternalKey target(key, 100, kTypeValue);
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
return FindFile(cmp, level_files_, target.Encode());
|
||||
}
|
||||
|
||||
bool Overlaps(const char* smallest, const char* largest) {
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
Slice s(smallest != nullptr ? smallest : "");
|
||||
Slice l(largest != nullptr ? largest : "");
|
||||
return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_,
|
||||
return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, level_files_,
|
||||
(smallest != nullptr ? &s : nullptr),
|
||||
(largest != nullptr ? &l : nullptr));
|
||||
}
|
||||
};
|
||||
|
||||
TEST(FindFileTest, Empty) {
|
||||
TEST(FindLevelFileTest, LevelEmpty) {
|
||||
LevelFileInit(0);
|
||||
|
||||
ASSERT_EQ(0, Find("foo"));
|
||||
ASSERT_TRUE(! Overlaps("a", "z"));
|
||||
ASSERT_TRUE(! Overlaps(nullptr, "z"));
|
||||
@@ -61,7 +151,9 @@ TEST(FindFileTest, Empty) {
|
||||
ASSERT_TRUE(! Overlaps(nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST(FindFileTest, Single) {
|
||||
TEST(FindLevelFileTest, LevelSingle) {
|
||||
LevelFileInit(1);
|
||||
|
||||
Add("p", "q");
|
||||
ASSERT_EQ(0, Find("a"));
|
||||
ASSERT_EQ(0, Find("p"));
|
||||
@@ -91,8 +183,9 @@ TEST(FindFileTest, Single) {
|
||||
ASSERT_TRUE(Overlaps(nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST(FindLevelFileTest, LevelMultiple) {
|
||||
LevelFileInit(4);
|
||||
|
||||
TEST(FindFileTest, Multiple) {
|
||||
Add("150", "200");
|
||||
Add("200", "250");
|
||||
Add("300", "350");
|
||||
@@ -130,7 +223,9 @@ TEST(FindFileTest, Multiple) {
|
||||
ASSERT_TRUE(Overlaps("450", "500"));
|
||||
}
|
||||
|
||||
TEST(FindFileTest, MultipleNullBoundaries) {
|
||||
TEST(FindLevelFileTest, LevelMultipleNullBoundaries) {
|
||||
LevelFileInit(4);
|
||||
|
||||
Add("150", "200");
|
||||
Add("200", "250");
|
||||
Add("300", "350");
|
||||
@@ -150,7 +245,9 @@ TEST(FindFileTest, MultipleNullBoundaries) {
|
||||
ASSERT_TRUE(Overlaps("450", nullptr));
|
||||
}
|
||||
|
||||
TEST(FindFileTest, OverlapSequenceChecks) {
|
||||
TEST(FindLevelFileTest, LevelOverlapSequenceChecks) {
|
||||
LevelFileInit(1);
|
||||
|
||||
Add("200", "200", 5000, 3000);
|
||||
ASSERT_TRUE(! Overlaps("199", "199"));
|
||||
ASSERT_TRUE(! Overlaps("201", "300"));
|
||||
@@ -159,7 +256,9 @@ TEST(FindFileTest, OverlapSequenceChecks) {
|
||||
ASSERT_TRUE(Overlaps("200", "210"));
|
||||
}
|
||||
|
||||
TEST(FindFileTest, OverlappingFiles) {
|
||||
TEST(FindLevelFileTest, LevelOverlappingFiles) {
|
||||
LevelFileInit(2);
|
||||
|
||||
Add("150", "600");
|
||||
Add("400", "500");
|
||||
disjoint_sorted_files_ = false;
|
||||
|
||||
@@ -236,6 +236,23 @@ void WriteBatch::Delete(ColumnFamilyHandle* column_family, const Slice& key) {
|
||||
WriteBatchInternal::Delete(this, GetColumnFamilyID(column_family), key);
|
||||
}
|
||||
|
||||
void WriteBatchInternal::Delete(WriteBatch* b, uint32_t column_family_id,
|
||||
const SliceParts& key) {
|
||||
WriteBatchInternal::SetCount(b, WriteBatchInternal::Count(b) + 1);
|
||||
if (column_family_id == 0) {
|
||||
b->rep_.push_back(static_cast<char>(kTypeDeletion));
|
||||
} else {
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyDeletion));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
PutLengthPrefixedSliceParts(&b->rep_, key);
|
||||
}
|
||||
|
||||
void WriteBatch::Delete(ColumnFamilyHandle* column_family,
|
||||
const SliceParts& key) {
|
||||
WriteBatchInternal::Delete(this, GetColumnFamilyID(column_family), key);
|
||||
}
|
||||
|
||||
void WriteBatchInternal::Merge(WriteBatch* b, uint32_t column_family_id,
|
||||
const Slice& key, const Slice& value) {
|
||||
WriteBatchInternal::SetCount(b, WriteBatchInternal::Count(b) + 1);
|
||||
|
||||
@@ -71,6 +71,9 @@ class WriteBatchInternal {
|
||||
static void Put(WriteBatch* batch, uint32_t column_family_id,
|
||||
const SliceParts& key, const SliceParts& value);
|
||||
|
||||
static void Delete(WriteBatch* batch, uint32_t column_family_id,
|
||||
const SliceParts& key);
|
||||
|
||||
static void Delete(WriteBatch* batch, uint32_t column_family_id,
|
||||
const Slice& key);
|
||||
|
||||
|
||||
@@ -642,10 +642,6 @@ Default:1, i.e. pick maxfilesize amount of data as the source of a compaction.
|
||||
<li> <code>Options::max_grandparent_overlap_factor</code> - Control maximum bytes of overlaps in grandparent (i.e., level+2) before we
|
||||
stop building a single file in a level->level+1 compaction.
|
||||
<p>
|
||||
<li> <code>Options::disable_seek_compaction</code> - 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).
|
||||
<p>
|
||||
<li> <code>Options::max_background_compactions</code> - Maximum number of concurrent background jobs, submitted to
|
||||
the default LOW priority thread pool
|
||||
</ul>
|
||||
|
||||
+157
-1
@@ -57,8 +57,13 @@ extern "C" {
|
||||
typedef struct rocksdb_t rocksdb_t;
|
||||
typedef struct rocksdb_cache_t rocksdb_cache_t;
|
||||
typedef struct rocksdb_compactionfilter_t rocksdb_compactionfilter_t;
|
||||
typedef struct rocksdb_compactionfiltercontext_t
|
||||
rocksdb_compactionfiltercontext_t;
|
||||
typedef struct rocksdb_compactionfilterfactory_t
|
||||
rocksdb_compactionfilterfactory_t;
|
||||
typedef struct rocksdb_comparator_t rocksdb_comparator_t;
|
||||
typedef struct rocksdb_env_t rocksdb_env_t;
|
||||
typedef struct rocksdb_fifo_compaction_options_t rocksdb_fifo_compaction_options_t;
|
||||
typedef struct rocksdb_filelock_t rocksdb_filelock_t;
|
||||
typedef struct rocksdb_filterpolicy_t rocksdb_filterpolicy_t;
|
||||
typedef struct rocksdb_flushoptions_t rocksdb_flushoptions_t;
|
||||
@@ -76,6 +81,7 @@ typedef struct rocksdb_writebatch_t rocksdb_writebatch_t;
|
||||
typedef struct rocksdb_writeoptions_t rocksdb_writeoptions_t;
|
||||
typedef struct rocksdb_universal_compaction_options_t rocksdb_universal_compaction_options_t;
|
||||
typedef struct rocksdb_livefiles_t rocksdb_livefiles_t;
|
||||
typedef struct rocksdb_column_family_handle_t rocksdb_column_family_handle_t;
|
||||
|
||||
/* DB operations */
|
||||
|
||||
@@ -90,6 +96,45 @@ extern rocksdb_t* rocksdb_open_for_read_only(
|
||||
unsigned char error_if_log_file_exist,
|
||||
char** errptr);
|
||||
|
||||
extern rocksdb_t* rocksdb_open_column_families(
|
||||
const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
int num_column_families,
|
||||
const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles,
|
||||
char** errptr);
|
||||
|
||||
extern rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
int num_column_families,
|
||||
const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles,
|
||||
unsigned char error_if_log_file_exist,
|
||||
char** errptr);
|
||||
|
||||
char** rocksdb_list_column_families(
|
||||
const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
size_t* lencf,
|
||||
char** errptr);
|
||||
void rocksdb_list_column_families_destroy(char** list, size_t len);
|
||||
|
||||
extern rocksdb_column_family_handle_t* rocksdb_create_column_family(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_options_t* column_family_options,
|
||||
const char* column_family_name,
|
||||
char** errptr);
|
||||
|
||||
extern void rocksdb_drop_column_family(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* handle,
|
||||
char** errptr);
|
||||
|
||||
extern void rocksdb_column_family_handle_destroy(rocksdb_column_family_handle_t*);
|
||||
|
||||
extern void rocksdb_close(rocksdb_t* db);
|
||||
|
||||
extern void rocksdb_put(
|
||||
@@ -99,12 +144,27 @@ extern void rocksdb_put(
|
||||
const char* val, size_t vallen,
|
||||
char** errptr);
|
||||
|
||||
extern void rocksdb_put_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
const char* val, size_t vallen,
|
||||
char** errptr);
|
||||
|
||||
extern void rocksdb_delete(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t keylen,
|
||||
char** errptr);
|
||||
|
||||
void rocksdb_delete_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
char** errptr);
|
||||
|
||||
extern void rocksdb_merge(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
@@ -112,6 +172,14 @@ extern void rocksdb_merge(
|
||||
const char* val, size_t vallen,
|
||||
char** errptr);
|
||||
|
||||
extern void rocksdb_merge_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
const char* val, size_t vallen,
|
||||
char** errptr);
|
||||
|
||||
extern void rocksdb_write(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
@@ -127,10 +195,23 @@ extern char* rocksdb_get(
|
||||
size_t* vallen,
|
||||
char** errptr);
|
||||
|
||||
extern char* rocksdb_get_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
size_t* vallen,
|
||||
char** errptr);
|
||||
|
||||
extern rocksdb_iterator_t* rocksdb_create_iterator(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options);
|
||||
|
||||
extern rocksdb_iterator_t* rocksdb_create_iterator_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family);
|
||||
|
||||
extern const rocksdb_snapshot_t* rocksdb_create_snapshot(
|
||||
rocksdb_t* db);
|
||||
|
||||
@@ -144,6 +225,11 @@ extern char* rocksdb_property_value(
|
||||
rocksdb_t* db,
|
||||
const char* propname);
|
||||
|
||||
extern char* rocksdb_property_value_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* propname);
|
||||
|
||||
extern void rocksdb_approximate_sizes(
|
||||
rocksdb_t* db,
|
||||
int num_ranges,
|
||||
@@ -151,11 +237,25 @@ extern void rocksdb_approximate_sizes(
|
||||
const char* const* range_limit_key, const size_t* range_limit_key_len,
|
||||
uint64_t* sizes);
|
||||
|
||||
extern void rocksdb_approximate_sizes_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
int num_ranges,
|
||||
const char* const* range_start_key, const size_t* range_start_key_len,
|
||||
const char* const* range_limit_key, const size_t* range_limit_key_len,
|
||||
uint64_t* sizes);
|
||||
|
||||
extern void rocksdb_compact_range(
|
||||
rocksdb_t* db,
|
||||
const char* start_key, size_t start_key_len,
|
||||
const char* limit_key, size_t limit_key_len);
|
||||
|
||||
extern void rocksdb_compact_range_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* start_key, size_t start_key_len,
|
||||
const char* limit_key, size_t limit_key_len);
|
||||
|
||||
extern void rocksdb_delete_file(
|
||||
rocksdb_t* db,
|
||||
const char* name);
|
||||
@@ -205,6 +305,8 @@ extern void rocksdb_iter_get_error(const rocksdb_iterator_t*, char** errptr);
|
||||
/* Write batch */
|
||||
|
||||
extern rocksdb_writebatch_t* rocksdb_writebatch_create();
|
||||
extern rocksdb_writebatch_t* rocksdb_writebatch_create_from(const char* rep,
|
||||
size_t size);
|
||||
extern void rocksdb_writebatch_destroy(rocksdb_writebatch_t*);
|
||||
extern void rocksdb_writebatch_clear(rocksdb_writebatch_t*);
|
||||
extern int rocksdb_writebatch_count(rocksdb_writebatch_t*);
|
||||
@@ -212,13 +314,27 @@ extern void rocksdb_writebatch_put(
|
||||
rocksdb_writebatch_t*,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen);
|
||||
extern void rocksdb_writebatch_put_cf(
|
||||
rocksdb_writebatch_t*,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen);
|
||||
extern void rocksdb_writebatch_merge(
|
||||
rocksdb_writebatch_t*,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen);
|
||||
extern void rocksdb_writebatch_merge_cf(
|
||||
rocksdb_writebatch_t*,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen);
|
||||
extern void rocksdb_writebatch_delete(
|
||||
rocksdb_writebatch_t*,
|
||||
const char* key, size_t klen);
|
||||
extern void rocksdb_writebatch_delete_cf(
|
||||
rocksdb_writebatch_t*,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen);
|
||||
extern void rocksdb_writebatch_iterate(
|
||||
rocksdb_writebatch_t*,
|
||||
void* state,
|
||||
@@ -230,9 +346,19 @@ extern const char* rocksdb_writebatch_data(rocksdb_writebatch_t*, size_t *size);
|
||||
|
||||
extern rocksdb_options_t* rocksdb_options_create();
|
||||
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);
|
||||
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(
|
||||
rocksdb_options_t* opt, uint64_t memtable_memory_budget);
|
||||
extern void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t*,
|
||||
rocksdb_compactionfilter_t*);
|
||||
extern void rocksdb_options_set_compaction_filter_factory(
|
||||
rocksdb_options_t*, rocksdb_compactionfilterfactory_t*);
|
||||
extern void rocksdb_options_set_comparator(
|
||||
rocksdb_options_t*,
|
||||
rocksdb_comparator_t*);
|
||||
@@ -247,6 +373,8 @@ extern void rocksdb_options_set_filter_policy(
|
||||
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(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern void rocksdb_options_set_error_if_exists(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern void rocksdb_options_set_paranoid_checks(
|
||||
@@ -401,10 +529,13 @@ extern void rocksdb_options_set_compression(rocksdb_options_t*, int);
|
||||
|
||||
enum {
|
||||
rocksdb_level_compaction = 0,
|
||||
rocksdb_universal_compaction = 1
|
||||
rocksdb_universal_compaction = 1,
|
||||
rocksdb_fifo_compaction = 2
|
||||
};
|
||||
extern void rocksdb_options_set_compaction_style(rocksdb_options_t*, int);
|
||||
extern void rocksdb_options_set_universal_compaction_options(rocksdb_options_t*, rocksdb_universal_compaction_options_t*);
|
||||
extern void rocksdb_options_set_fifo_compaction_options(rocksdb_options_t* opt,
|
||||
rocksdb_fifo_compaction_options_t* fifo);
|
||||
|
||||
/* Compaction Filter */
|
||||
|
||||
@@ -421,6 +552,25 @@ extern rocksdb_compactionfilter_t* rocksdb_compactionfilter_create(
|
||||
const char* (*name)(void*));
|
||||
extern void rocksdb_compactionfilter_destroy(rocksdb_compactionfilter_t*);
|
||||
|
||||
/* Compaction Filter Context */
|
||||
|
||||
extern unsigned char rocksdb_compactionfiltercontext_is_full_compaction(
|
||||
rocksdb_compactionfiltercontext_t* context);
|
||||
|
||||
extern unsigned char rocksdb_compactionfiltercontext_is_manual_compaction(
|
||||
rocksdb_compactionfiltercontext_t* context);
|
||||
|
||||
/* Compaction Filter Factory */
|
||||
|
||||
extern rocksdb_compactionfilterfactory_t*
|
||||
rocksdb_compactionfilterfactory_create(
|
||||
void* state, void (*destructor)(void*),
|
||||
rocksdb_compactionfilter_t* (*create_compaction_filter)(
|
||||
void*, rocksdb_compactionfiltercontext_t* context),
|
||||
const char* (*name)(void*));
|
||||
extern void rocksdb_compactionfilterfactory_destroy(
|
||||
rocksdb_compactionfilterfactory_t*);
|
||||
|
||||
/* Comparator */
|
||||
|
||||
extern rocksdb_comparator_t* rocksdb_comparator_create(
|
||||
@@ -566,6 +716,12 @@ extern void rocksdb_universal_compaction_options_set_stop_style(
|
||||
extern void rocksdb_universal_compaction_options_destroy(
|
||||
rocksdb_universal_compaction_options_t*);
|
||||
|
||||
extern rocksdb_fifo_compaction_options_t* rocksdb_fifo_compaction_options_create();
|
||||
extern void rocksdb_fifo_compaction_options_set_max_table_files_size(
|
||||
rocksdb_fifo_compaction_options_t* fifo_opts, uint64_t size);
|
||||
extern void rocksdb_fifo_compaction_options_destroy(
|
||||
rocksdb_fifo_compaction_options_t* fifo_opts);
|
||||
|
||||
extern int rocksdb_livefiles_count(
|
||||
const rocksdb_livefiles_t*);
|
||||
extern const char* rocksdb_livefiles_name(
|
||||
|
||||
@@ -86,7 +86,7 @@ class CompactionFilterV2 {
|
||||
//
|
||||
// Each entry in the return vector indicates if the corresponding kv should
|
||||
// be preserved in the output of this compaction run. The application can
|
||||
// inspect the exisitng values of the keys and make decision based on it.
|
||||
// inspect the existing values of the keys and make decision based on it.
|
||||
//
|
||||
// When a value is to be preserved, the application has the option
|
||||
// to modify the entry in existing_values and pass it back through an entry
|
||||
@@ -108,7 +108,7 @@ class CompactionFilterV2 {
|
||||
};
|
||||
|
||||
// Each compaction will create a new CompactionFilter allowing the
|
||||
// application to know about different campactions
|
||||
// application to know about different compactions
|
||||
class CompactionFilterFactory {
|
||||
public:
|
||||
virtual ~CompactionFilterFactory() { }
|
||||
@@ -120,7 +120,7 @@ class CompactionFilterFactory {
|
||||
virtual const char* Name() const = 0;
|
||||
};
|
||||
|
||||
// Default implementaion of CompactionFilterFactory which does not
|
||||
// Default implementation of CompactionFilterFactory which does not
|
||||
// return any filter
|
||||
class DefaultCompactionFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
@@ -175,7 +175,7 @@ class CompactionFilterFactoryV2 {
|
||||
const SliceTransform* prefix_extractor_;
|
||||
};
|
||||
|
||||
// Default implementaion of CompactionFilterFactoryV2 which does not
|
||||
// Default implementation of CompactionFilterFactoryV2 which does not
|
||||
// return any filter
|
||||
class DefaultCompactionFilterFactoryV2 : public CompactionFilterFactoryV2 {
|
||||
public:
|
||||
|
||||
@@ -55,6 +55,7 @@ class Env;
|
||||
// Metadata associated with each SST file.
|
||||
struct LiveFileMetaData {
|
||||
std::string column_family_name; // Name of the column family
|
||||
std::string db_path;
|
||||
std::string name; // Name of the file
|
||||
int level; // Level at which this file resides.
|
||||
size_t size; // File size in bytes.
|
||||
|
||||
+27
-1
@@ -35,6 +35,7 @@ class WritableFile;
|
||||
class RandomRWFile;
|
||||
class Directory;
|
||||
struct DBOptions;
|
||||
class RateLimiter;
|
||||
|
||||
using std::unique_ptr;
|
||||
using std::shared_ptr;
|
||||
@@ -74,6 +75,9 @@ struct EnvOptions {
|
||||
// write. By default, we set it to true for MANIFEST writes and false for
|
||||
// WAL writes
|
||||
bool fallocate_with_keep_size = true;
|
||||
|
||||
// If not nullptr, write rate limiting is enabled for flush and compaction
|
||||
RateLimiter* rate_limiter = nullptr;
|
||||
};
|
||||
|
||||
class Env {
|
||||
@@ -194,8 +198,16 @@ class Env {
|
||||
// REQUIRES: lock has not already been unlocked.
|
||||
virtual Status UnlockFile(FileLock* lock) = 0;
|
||||
|
||||
// Priority for scheduling job in thread pool
|
||||
enum Priority { LOW, HIGH, TOTAL };
|
||||
|
||||
// Priority for scheduling job in thread pool
|
||||
enum IOPriority {
|
||||
IO_LOW = 0,
|
||||
IO_HIGH = 1,
|
||||
IO_TOTAL = 2
|
||||
};
|
||||
|
||||
// Arrange to run "(*function)(arg)" once in a background thread, in
|
||||
// the thread pool specified by pri. By default, jobs go to the 'LOW'
|
||||
// priority thread pool.
|
||||
@@ -371,7 +383,10 @@ class RandomAccessFile {
|
||||
// at a time to the file.
|
||||
class WritableFile {
|
||||
public:
|
||||
WritableFile() : last_preallocated_block_(0), preallocation_block_size_ (0) {
|
||||
WritableFile()
|
||||
: last_preallocated_block_(0),
|
||||
preallocation_block_size_(0),
|
||||
io_priority_(Env::IO_TOTAL) {
|
||||
}
|
||||
virtual ~WritableFile();
|
||||
|
||||
@@ -390,6 +405,14 @@ class WritableFile {
|
||||
return Sync();
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the priority in rate limiter if rate limiting is enabled.
|
||||
* If rate limiting is not enabled, this call has no effect.
|
||||
*/
|
||||
virtual void SetIOPriority(Env::IOPriority pri) {
|
||||
io_priority_ = pri;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the size of valid data in the file.
|
||||
*/
|
||||
@@ -474,6 +497,9 @@ class WritableFile {
|
||||
// No copying allowed
|
||||
WritableFile(const WritableFile&);
|
||||
void operator=(const WritableFile&);
|
||||
|
||||
protected:
|
||||
Env::IOPriority io_priority_;
|
||||
};
|
||||
|
||||
// A file abstraction for random reading and writing.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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 INCLUDE_ROCKSDB_IOSTATS_CONTEXT_H_
|
||||
#define INCLUDE_ROCKSDB_IOSTATS_CONTEXT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
// A thread local context for gathering io-stats efficiently and transparently.
|
||||
namespace rocksdb {
|
||||
|
||||
struct IOStatsContext {
|
||||
// reset all io-stats counter to zero
|
||||
void Reset();
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
// the thread pool id
|
||||
uint64_t thread_pool_id;
|
||||
|
||||
// number of bytes that has been written.
|
||||
uint64_t bytes_written;
|
||||
// number of bytes that has been read.
|
||||
uint64_t bytes_read;
|
||||
};
|
||||
|
||||
extern __thread IOStatsContext iostats_context;
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // INCLUDE_ROCKSDB_IOSTATS_CONTEXT_H_
|
||||
@@ -4,13 +4,32 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
#ifndef ROCKSDB_LITE
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// An interface for converting a slice to a readable string
|
||||
class SliceFormatter {
|
||||
public:
|
||||
virtual ~SliceFormatter() {}
|
||||
virtual std::string Format(const Slice& s) const = 0;
|
||||
};
|
||||
|
||||
// Options for customizing ldb tool (beyond the DB Options)
|
||||
struct LDBOptions {
|
||||
// Create LDBOptions with default values for all fields
|
||||
LDBOptions();
|
||||
|
||||
// Key formatter that converts a slice to a readable string.
|
||||
// Default: Slice::ToString()
|
||||
std::shared_ptr<SliceFormatter> key_formatter;
|
||||
};
|
||||
|
||||
class LDBTool {
|
||||
public:
|
||||
void Run(int argc, char** argv, Options = Options());
|
||||
void Run(int argc, char** argv, Options db_options= Options(),
|
||||
const LDBOptions& ldb_options = LDBOptions());
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -148,13 +148,6 @@ class MemTableRep {
|
||||
// all the states but those allocated in arena.
|
||||
virtual Iterator* GetIterator(Arena* arena = nullptr) = 0;
|
||||
|
||||
// Return an iterator over at least the keys with the specified user key. The
|
||||
// iterator may also allow access to other keys, but doesn't have to. Default:
|
||||
// GetIterator().
|
||||
virtual Iterator* GetIterator(const Slice& user_key) {
|
||||
return GetIterator(nullptr);
|
||||
}
|
||||
|
||||
// Return an iterator that has a special Seek semantics. The result of
|
||||
// a Seek might only include keys with the same prefix as the target key.
|
||||
// arena: If not null, the arena needs to be used to allocate the Iterator.
|
||||
@@ -234,9 +227,10 @@ extern MemTableRepFactory* NewHashSkipListRepFactory(
|
||||
int32_t skiplist_branching_factor = 4
|
||||
);
|
||||
|
||||
// The factory is to create memtables with a hashed linked list:
|
||||
// it contains a fixed array of buckets, each pointing to a sorted single
|
||||
// linked list (null if the bucket is empty).
|
||||
// The factory is to create memtables based on a hash table:
|
||||
// it contains a fixed array of buckets, each pointing to either a linked list
|
||||
// or a skip list if number of entries inside the bucket exceeds
|
||||
// threshold_use_skiplist.
|
||||
// @bucket_count: number of fixed array buckets
|
||||
// @huge_page_tlb_size: if <=0, allocate the hash table bytes from malloc.
|
||||
// Otherwise from huge page TLB. The user needs to reserve
|
||||
@@ -247,10 +241,13 @@ extern MemTableRepFactory* NewHashSkipListRepFactory(
|
||||
// exceeds this number, log about it.
|
||||
// @if_log_bucket_dist_when_flash: if true, log distribution of number of
|
||||
// entries when flushing.
|
||||
// @threshold_use_skiplist: a bucket switches to skip list if number of
|
||||
// entries exceed this parameter.
|
||||
extern MemTableRepFactory* NewHashLinkListRepFactory(
|
||||
size_t bucket_count = 50000, size_t huge_page_tlb_size = 0,
|
||||
int bucket_entries_logging_threshold = 4096,
|
||||
bool if_log_bucket_dist_when_flash = true);
|
||||
bool if_log_bucket_dist_when_flash = true,
|
||||
uint32_t threshold_use_skiplist = 256);
|
||||
|
||||
// This factory creates a cuckoo-hashing based mem-table representation.
|
||||
// Cuckoo-hash is a closed-hash strategy, in which all key/value pairs
|
||||
|
||||
+55
-12
@@ -34,12 +34,12 @@ class Snapshot;
|
||||
class TableFactory;
|
||||
class MemTableRepFactory;
|
||||
class TablePropertiesCollectorFactory;
|
||||
class RateLimiter;
|
||||
class Slice;
|
||||
class SliceTransform;
|
||||
class Statistics;
|
||||
class InternalKeyComparator;
|
||||
|
||||
using std::shared_ptr;
|
||||
class RateLimiter;
|
||||
|
||||
// DB contents are stored in a set of blocks, each of which holds a
|
||||
// sequence of key,value pairs. Each block may be compressed before
|
||||
@@ -132,7 +132,7 @@ struct ColumnFamilyOptions {
|
||||
// for the first time. It's necessary to specify a merge operator when
|
||||
// openning the DB in this case.
|
||||
// Default: nullptr
|
||||
shared_ptr<MergeOperator> merge_operator;
|
||||
std::shared_ptr<MergeOperator> merge_operator;
|
||||
|
||||
// A single CompactionFilter instance to call into during compaction.
|
||||
// Allows an application to modify/delete a key-value during background
|
||||
@@ -205,12 +205,12 @@ struct ColumnFamilyOptions {
|
||||
// If non-NULL use the specified cache for blocks.
|
||||
// If NULL, rocksdb will automatically create and use an 8MB internal cache.
|
||||
// Default: nullptr
|
||||
shared_ptr<Cache> block_cache;
|
||||
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
|
||||
shared_ptr<Cache> block_cache_compressed;
|
||||
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
|
||||
@@ -368,10 +368,15 @@ struct ColumnFamilyOptions {
|
||||
// stop building a single file in a level->level+1 compaction.
|
||||
int max_grandparent_overlap_factor;
|
||||
|
||||
// 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).
|
||||
// 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;
|
||||
|
||||
@@ -620,11 +625,16 @@ struct DBOptions {
|
||||
// Default: Env::Default()
|
||||
Env* env;
|
||||
|
||||
// Use to control write rate of flush and compaction. Flush has higher
|
||||
// priority than compaction. Rate limiting is disabled if nullptr.
|
||||
// Default: nullptr
|
||||
std::shared_ptr<RateLimiter> rate_limiter;
|
||||
|
||||
// Any internal progress/error information generated by the db will
|
||||
// be written to info_log if it is non-nullptr, or to a file stored
|
||||
// in the same directory as the DB contents if info_log is nullptr.
|
||||
// Default: nullptr
|
||||
shared_ptr<Logger> info_log;
|
||||
std::shared_ptr<Logger> info_log;
|
||||
|
||||
InfoLogLevel info_log_level;
|
||||
|
||||
@@ -647,7 +657,7 @@ struct DBOptions {
|
||||
// If non-null, then we should collect metrics about database operations
|
||||
// Statistics objects should not be shared between DB instances as
|
||||
// it does not use any locks to prevent concurrent updates.
|
||||
shared_ptr<Statistics> statistics;
|
||||
std::shared_ptr<Statistics> statistics;
|
||||
|
||||
// If true, then the contents of data files are not synced
|
||||
// to stable storage. Their contents remain in the OS buffers till the
|
||||
@@ -670,6 +680,13 @@ struct DBOptions {
|
||||
// Default value is 1800 (half an hour).
|
||||
int db_stats_log_interval;
|
||||
|
||||
// A list paths where SST files can be put into. A compaction style can
|
||||
// determine which of those paths it will put the file to.
|
||||
// If left empty, only one path will be used, which is db_name passed when
|
||||
// opening the DB.
|
||||
// Default: empty
|
||||
std::vector<std::string> db_paths;
|
||||
|
||||
// This specifies the info LOG dir.
|
||||
// If it is empty, the log files will be in the same dir as data.
|
||||
// If it is non empty, the log files will be in the specified dir,
|
||||
@@ -963,7 +980,18 @@ struct WriteOptions {
|
||||
// and the write may got lost after a crash.
|
||||
bool disableWAL;
|
||||
|
||||
WriteOptions() : sync(false), disableWAL(false) {}
|
||||
// If non-zero, then associated write waiting longer than the specified
|
||||
// time MAY be aborted and returns Status::TimedOut. A write that takes
|
||||
// less than the specified time is guaranteed to not fail with
|
||||
// Status::TimedOut.
|
||||
//
|
||||
// The number of times a write call encounters a timeout is recorded in
|
||||
// Statistics.WRITE_TIMEDOUT
|
||||
//
|
||||
// Default: 0
|
||||
uint64_t timeout_hint_us;
|
||||
|
||||
WriteOptions() : sync(false), disableWAL(false), timeout_hint_us(0) {}
|
||||
};
|
||||
|
||||
// Options that control flush operations
|
||||
@@ -975,6 +1003,21 @@ struct FlushOptions {
|
||||
FlushOptions() : wait(true) {}
|
||||
};
|
||||
|
||||
|
||||
// Create a RateLimiter object, which can be shared among RocksDB instances to
|
||||
// control write rate of flush and compaction.
|
||||
// @rate_bytes_per_sec: desired total write rate in bytes per second.
|
||||
// @refill_period_us: token refill interval in micro-second.
|
||||
// @fairness: RateLimiter accepts high-pri requests and low-pri requests.
|
||||
// low-pri request is usually blocked in favor of hi-pri request. To prevent
|
||||
// low-pri request from being blocked for too long, it can get processed first
|
||||
// by 1/fairness chance.
|
||||
extern RateLimiter* NewRateLimiter(
|
||||
int64_t rate_bytes_per_sec,
|
||||
int64_t refill_period_us = 100 * 1000,
|
||||
int32_t fairness = 10);
|
||||
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // STORAGE_ROCKSDB_INCLUDE_OPTIONS_H_
|
||||
|
||||
@@ -20,6 +20,9 @@ enum PerfLevel {
|
||||
// set the perf stats level
|
||||
void SetPerfLevel(PerfLevel level);
|
||||
|
||||
// get current perf stats level
|
||||
PerfLevel GetPerfLevel();
|
||||
|
||||
// A thread local context for gathering performance counter efficiently
|
||||
// and transparently.
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ class Slice {
|
||||
struct SliceParts {
|
||||
SliceParts(const Slice* _parts, int _num_parts) :
|
||||
parts(_parts), num_parts(_num_parts) { }
|
||||
SliceParts() : parts(nullptr), num_parts(0) {}
|
||||
|
||||
const Slice* parts;
|
||||
int num_parts;
|
||||
|
||||
@@ -115,9 +115,11 @@ enum Tickers {
|
||||
// head of the writers queue.
|
||||
WRITE_DONE_BY_SELF,
|
||||
WRITE_DONE_BY_OTHER,
|
||||
WRITE_TIMEDOUT, // Number of writes ending up with timed-out.
|
||||
WRITE_WITH_WAL, // Number of Write calls that request WAL
|
||||
COMPACT_READ_BYTES, // Bytes read during compaction
|
||||
COMPACT_WRITE_BYTES, // Bytes written during compaction
|
||||
FLUSH_WRITE_BYTES, // Bytes written during flush
|
||||
|
||||
// Number of table's properties loaded directly from file, without creating
|
||||
// table reader object.
|
||||
@@ -176,7 +178,9 @@ const std::vector<std::pair<Tickers, std::string>> TickersNameMap = {
|
||||
{WAL_FILE_BYTES, "rocksdb.wal.bytes"},
|
||||
{WRITE_DONE_BY_SELF, "rocksdb.write.self"},
|
||||
{WRITE_DONE_BY_OTHER, "rocksdb.write.other"},
|
||||
{WRITE_TIMEDOUT, "rocksdb.write.timedout"},
|
||||
{WRITE_WITH_WAL, "rocksdb.write.wal"},
|
||||
{FLUSH_WRITE_BYTES, "rocksdb.flush.write.bytes"},
|
||||
{COMPACT_READ_BYTES, "rocksdb.compact.read.bytes"},
|
||||
{COMPACT_WRITE_BYTES, "rocksdb.compact.write.bytes"},
|
||||
{NUMBER_DIRECT_LOAD_TABLE_PROPERTIES,
|
||||
|
||||
@@ -65,6 +65,12 @@ class Status {
|
||||
const Slice& msg2 = Slice()) {
|
||||
return Status(kShutdownInProgress, msg, msg2);
|
||||
}
|
||||
static Status TimedOut() {
|
||||
return Status(kTimedOut);
|
||||
}
|
||||
static Status TimedOut(const Slice& msg, const Slice& msg2 = Slice()) {
|
||||
return Status(kTimedOut, msg, msg2);
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates success.
|
||||
bool ok() const { return code() == kOk; }
|
||||
@@ -93,6 +99,8 @@ class Status {
|
||||
// Returns true iff the status indicates Incomplete
|
||||
bool IsShutdownInProgress() const { return code() == kShutdownInProgress; }
|
||||
|
||||
bool IsTimedOut() const { return code() == kTimedOut; }
|
||||
|
||||
// Return a string representation of this status suitable for printing.
|
||||
// Returns the string "OK" for success.
|
||||
std::string ToString() const;
|
||||
@@ -106,7 +114,8 @@ class Status {
|
||||
kIOError = 5,
|
||||
kMergeInProgress = 6,
|
||||
kIncomplete = 7,
|
||||
kShutdownInProgress = 8
|
||||
kShutdownInProgress = 8,
|
||||
kTimedOut = 9
|
||||
};
|
||||
|
||||
Code code() const {
|
||||
|
||||
+4
-28
@@ -155,35 +155,11 @@ struct PlainTablePropertyNames {
|
||||
// encoding types can co-exist in the same DB and can be read.
|
||||
|
||||
const uint32_t kPlainTableVariableLength = 0;
|
||||
extern TableFactory* NewPlainTableFactory(uint32_t user_key_len =
|
||||
kPlainTableVariableLength,
|
||||
int bloom_bits_per_prefix = 10,
|
||||
double hash_table_ratio = 0.75,
|
||||
size_t index_sparseness = 16,
|
||||
size_t huge_page_tlb_size = 0,
|
||||
EncodingType encoding_type = kPlain);
|
||||
|
||||
// -- Plain Table
|
||||
// This factory of plain table ignores Options.prefix_extractor and assumes no
|
||||
// hashable prefix available to the key structure. Lookup will be based on
|
||||
// binary search index only. Total order seek() can be issued.
|
||||
// @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.
|
||||
// @bloom_bits_per_key: the number of bits used for bloom filer per key. You may
|
||||
// disable it by passing a zero.
|
||||
// @index_sparseness: need to build one index record for how many keys for
|
||||
// binary search.
|
||||
// @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
|
||||
extern TableFactory* NewTotalOrderPlainTableFactory(
|
||||
extern TableFactory* NewPlainTableFactory(
|
||||
uint32_t user_key_len = kPlainTableVariableLength,
|
||||
int bloom_bits_per_key = 0, size_t index_sparseness = 16,
|
||||
size_t huge_page_tlb_size = 0, bool full_scan_mode = false);
|
||||
int bloom_bits_per_prefix = 10, double hash_table_ratio = 0.75,
|
||||
size_t index_sparseness = 16, size_t huge_page_tlb_size = 0,
|
||||
EncodingType encoding_type = kPlain, bool full_scan_mode = false);
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <climits>
|
||||
#include <vector>
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -61,6 +62,7 @@ class CompactionOptionsUniversal {
|
||||
// well as the total size of C1...Ct as total_C, the compaction output file
|
||||
// will be compressed iff
|
||||
// total_C / total_size < this percentage
|
||||
// Default: -1
|
||||
int compression_size_percent;
|
||||
|
||||
// The algorithm used to stop picking files into a single compaction run
|
||||
@@ -68,14 +70,13 @@ class CompactionOptionsUniversal {
|
||||
CompactionStopStyle stop_style;
|
||||
|
||||
// Default set of parameters
|
||||
CompactionOptionsUniversal() :
|
||||
size_ratio(1),
|
||||
min_merge_width(2),
|
||||
max_merge_width(UINT_MAX),
|
||||
max_size_amplification_percent(200),
|
||||
compression_size_percent(-1),
|
||||
stop_style(kCompactionStopStyleTotalSize) {
|
||||
}
|
||||
CompactionOptionsUniversal()
|
||||
: size_ratio(1),
|
||||
min_merge_width(2),
|
||||
max_merge_width(UINT_MAX),
|
||||
max_size_amplification_percent(200),
|
||||
compression_size_percent(-1),
|
||||
stop_style(kCompactionStopStyleTotalSize) {}
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
// Also update Makefile if you change these
|
||||
#define ROCKSDB_MAJOR 3
|
||||
#define ROCKSDB_MINOR 2
|
||||
#define ROCKSDB_MINOR 3
|
||||
#define ROCKSDB_PATCH 0
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
|
||||
@@ -67,6 +67,10 @@ class WriteBatch {
|
||||
void Delete(ColumnFamilyHandle* column_family, const Slice& key);
|
||||
void Delete(const Slice& key) { Delete(nullptr, key); }
|
||||
|
||||
// variant that takes SliceParts
|
||||
void Delete(ColumnFamilyHandle* column_family, const SliceParts& key);
|
||||
void Delete(const SliceParts& key) { Delete(nullptr, key); }
|
||||
|
||||
// Append a blob of arbitrary size to the records in this batch. The blob will
|
||||
// be stored in the transaction log but not in any other file. In particular,
|
||||
// it will not be persisted to the SST files. When iterating over this
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// 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 <vector>
|
||||
|
||||
#include "utilities/stackable_db.h"
|
||||
#include "utilities/json_document.h"
|
||||
#include "rocksdb/db.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// IMPORTANT: DocumentDB is a work in progress. It is unstable and we might
|
||||
// change the API without warning. Talk to RocksDB team before using this in
|
||||
// production ;)
|
||||
|
||||
// DocumentDB is a layer on top of RocksDB that provides a very simple JSON API.
|
||||
// When creating a DB, you specify a list of indexes you want to keep on your
|
||||
// data. You can insert a JSON document to the DB, which is automatically
|
||||
// indexed. Every document added to the DB needs to have "_id" field which is
|
||||
// automatically indexed and is an unique primary key. All other indexes are
|
||||
// non-unique.
|
||||
|
||||
// NOTE: field names in the JSON are NOT allowed to start with '$' or
|
||||
// contain '.'. We don't currently enforce that rule, but will start behaving
|
||||
// badly.
|
||||
|
||||
// Cursor is what you get as a result of executing query. To get all
|
||||
// results from a query, call Next() on a Cursor while Valid() returns true
|
||||
class Cursor {
|
||||
public:
|
||||
Cursor() = default;
|
||||
virtual ~Cursor() {}
|
||||
|
||||
virtual bool Valid() const = 0;
|
||||
virtual void Next() = 0;
|
||||
// Lifecycle of the returned JSONDocument is until the next Next() call
|
||||
virtual const JSONDocument& document() const = 0;
|
||||
virtual Status status() const = 0;
|
||||
|
||||
private:
|
||||
// No copying allowed
|
||||
Cursor(const Cursor&);
|
||||
void operator=(const Cursor&);
|
||||
};
|
||||
|
||||
struct DocumentDBOptions {
|
||||
int background_threads = 4;
|
||||
uint64_t memtable_size = 128 * 1024 * 1024; // 128 MB
|
||||
uint64_t cache_size = 1 * 1024 * 1024 * 1024; // 1 GB
|
||||
};
|
||||
|
||||
// TODO(icanadi) Add `JSONDocument* info` parameter to all calls that can be
|
||||
// used by the caller to get more information about the call execution (number
|
||||
// of dropped records, number of updated records, etc.)
|
||||
class DocumentDB : public StackableDB {
|
||||
public:
|
||||
struct IndexDescriptor {
|
||||
// Currently, you can only define an index on a single field. To specify an
|
||||
// index on a field X, set index description to JSON "{X: 1}"
|
||||
// Currently the value needs to be 1, which means ascending.
|
||||
// In the future, we plan to also support indexes on multiple keys, where
|
||||
// you could mix ascending sorting (1) with descending sorting indexes (-1)
|
||||
JSONDocument* description;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Open DocumentDB with specified indexes. The list of indexes has to be
|
||||
// complete, i.e. include all indexes present in the DB, except the primary
|
||||
// key index.
|
||||
// Otherwise, Open() will return an error
|
||||
static Status Open(const DocumentDBOptions& options, const std::string& name,
|
||||
const std::vector<IndexDescriptor>& indexes,
|
||||
DocumentDB** db, bool read_only = false);
|
||||
|
||||
explicit DocumentDB(DB* db) : StackableDB(db) {}
|
||||
|
||||
// Create a new index. It will stop all writes for the duration of the call.
|
||||
// All current documents in the DB are scanned and corresponding index entries
|
||||
// are created
|
||||
virtual Status CreateIndex(const WriteOptions& write_options,
|
||||
const IndexDescriptor& index) = 0;
|
||||
|
||||
// Drop an index. Client is responsible to make sure that index is not being
|
||||
// used by currently executing queries
|
||||
virtual Status DropIndex(const std::string& name) = 0;
|
||||
|
||||
// Insert a document to the DB. The document needs to have a primary key "_id"
|
||||
// which can either be a string or an integer. Otherwise the write will fail
|
||||
// with InvalidArgument.
|
||||
virtual Status Insert(const WriteOptions& options,
|
||||
const JSONDocument& document) = 0;
|
||||
|
||||
// Deletes all documents matching a filter atomically
|
||||
virtual Status Remove(const ReadOptions& read_options,
|
||||
const WriteOptions& write_options,
|
||||
const JSONDocument& query) = 0;
|
||||
|
||||
// Does this sequence of operations:
|
||||
// 1. Find all documents matching a filter
|
||||
// 2. For all documents, atomically:
|
||||
// 2.1. apply the update operators
|
||||
// 2.2. update the secondary indexes
|
||||
//
|
||||
// Currently only $set update operator is supported.
|
||||
// Syntax is: {$set: {key1: value1, key2: value2, etc...}}
|
||||
// This operator will change a document's key1 field to value1, key2 to
|
||||
// value2, etc. New values will be set even if a document didn't have an entry
|
||||
// for the specified key.
|
||||
//
|
||||
// You can not change a primary key of a document.
|
||||
//
|
||||
// Update example: Update({id: {$gt: 5}, $index: id}, {$set: {enabled: true}})
|
||||
virtual Status Update(const ReadOptions& read_options,
|
||||
const WriteOptions& write_options,
|
||||
const JSONDocument& filter,
|
||||
const JSONDocument& updates) = 0;
|
||||
|
||||
// query has to be an array in which every element is an operator. Currently
|
||||
// only $filter operator is supported. Syntax of $filter operator is:
|
||||
// {$filter: {key1: condition1, key2: condition2, etc.}} where conditions can
|
||||
// be either:
|
||||
// 1) a single value in which case the condition is equality condition, or
|
||||
// 2) a defined operators, like {$gt: 4}, which will match all documents that
|
||||
// have key greater than 4.
|
||||
//
|
||||
// Supported operators are:
|
||||
// 1) $gt -- greater than
|
||||
// 2) $gte -- greater than or equal
|
||||
// 3) $lt -- less than
|
||||
// 4) $lte -- less than or equal
|
||||
// If you want the filter to use an index, you need to specify it like this:
|
||||
// {$filter: {...(conditions)..., $index: index_name}}
|
||||
//
|
||||
// Example query:
|
||||
// * [{$filter: {name: John, age: {$gte: 18}, $index: age}}]
|
||||
// will return all Johns whose age is greater or equal to 18 and it will use
|
||||
// index "age" to satisfy the query.
|
||||
virtual Cursor* Query(const ReadOptions& read_options,
|
||||
const JSONDocument& query) = 0;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -0,0 +1,174 @@
|
||||
// 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 <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
// We use JSONDocument for DocumentDB API
|
||||
// Implementation inspired by folly::dynamic and rapidjson
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// NOTE: none of this is thread-safe
|
||||
class JSONDocument {
|
||||
public:
|
||||
// return nullptr on parse failure
|
||||
static JSONDocument* ParseJSON(const char* json);
|
||||
|
||||
enum Type {
|
||||
kNull,
|
||||
kArray,
|
||||
kBool,
|
||||
kDouble,
|
||||
kInt64,
|
||||
kObject,
|
||||
kString,
|
||||
};
|
||||
|
||||
JSONDocument(); // null
|
||||
/* implicit */ JSONDocument(bool b);
|
||||
/* implicit */ JSONDocument(double d);
|
||||
/* implicit */ JSONDocument(int64_t i);
|
||||
/* implicit */ JSONDocument(const std::string& s);
|
||||
/* implicit */ JSONDocument(const char* s);
|
||||
// constructs JSONDocument of specific type with default value
|
||||
explicit JSONDocument(Type type);
|
||||
|
||||
// copy constructor
|
||||
JSONDocument(const JSONDocument& json_document);
|
||||
|
||||
~JSONDocument();
|
||||
|
||||
Type type() const;
|
||||
|
||||
// REQUIRES: IsObject()
|
||||
bool Contains(const std::string& key) const;
|
||||
// Returns nullptr if !Contains()
|
||||
// don't delete the returned pointer
|
||||
// REQUIRES: IsObject()
|
||||
const JSONDocument* Get(const std::string& key) const;
|
||||
// REQUIRES: IsObject()
|
||||
JSONDocument& operator[](const std::string& key);
|
||||
// REQUIRES: IsObject()
|
||||
const JSONDocument& operator[](const std::string& key) const;
|
||||
// returns `this`, so you can chain operations.
|
||||
// Copies value
|
||||
// REQUIRES: IsObject()
|
||||
JSONDocument* Set(const std::string& key, const JSONDocument& value);
|
||||
|
||||
// REQUIRES: IsArray() == true || IsObject() == true
|
||||
size_t Count() const;
|
||||
|
||||
// REQUIRES: IsArray()
|
||||
const JSONDocument* GetFromArray(size_t i) const;
|
||||
// REQUIRES: IsArray()
|
||||
JSONDocument& operator[](size_t i);
|
||||
// REQUIRES: IsArray()
|
||||
const JSONDocument& operator[](size_t i) const;
|
||||
// returns `this`, so you can chain operations.
|
||||
// Copies the value
|
||||
// REQUIRES: IsArray() && i < Count()
|
||||
JSONDocument* SetInArray(size_t i, const JSONDocument& value);
|
||||
// REQUIRES: IsArray()
|
||||
JSONDocument* PushBack(const JSONDocument& value);
|
||||
|
||||
bool IsNull() const;
|
||||
bool IsArray() const;
|
||||
bool IsBool() const;
|
||||
bool IsDouble() const;
|
||||
bool IsInt64() const;
|
||||
bool IsObject() const;
|
||||
bool IsString() const;
|
||||
|
||||
// REQUIRES: IsBool() == true
|
||||
bool GetBool() const;
|
||||
// REQUIRES: IsDouble() == true
|
||||
double GetDouble() const;
|
||||
// REQUIRES: IsInt64() == true
|
||||
int64_t GetInt64() const;
|
||||
// REQUIRES: IsString() == true
|
||||
const std::string& GetString() const;
|
||||
|
||||
bool operator==(const JSONDocument& rhs) const;
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
class ItemsIteratorGenerator;
|
||||
|
||||
public:
|
||||
// REQUIRES: IsObject()
|
||||
ItemsIteratorGenerator Items() const;
|
||||
|
||||
// appends serialized object to dst
|
||||
void Serialize(std::string* dst) const;
|
||||
// returns nullptr if Slice doesn't represent valid serialized JSONDocument
|
||||
static JSONDocument* Deserialize(const Slice& src);
|
||||
|
||||
private:
|
||||
void SerializeInternal(std::string* dst, bool type_prefix) const;
|
||||
// returns false if Slice doesn't represent valid serialized JSONDocument.
|
||||
// Otherwise, true
|
||||
bool DeserializeInternal(Slice* input);
|
||||
|
||||
typedef std::vector<JSONDocument*> Array;
|
||||
typedef std::unordered_map<std::string, JSONDocument*> Object;
|
||||
|
||||
// iteration on objects
|
||||
class const_item_iterator {
|
||||
public:
|
||||
typedef Object::const_iterator It;
|
||||
typedef Object::value_type value_type;
|
||||
/* implicit */ const_item_iterator(It it) : it_(it) {}
|
||||
It& operator++() { return ++it_; }
|
||||
bool operator!=(const const_item_iterator& other) {
|
||||
return it_ != other.it_;
|
||||
}
|
||||
value_type operator*() { return *it_; }
|
||||
|
||||
private:
|
||||
It it_;
|
||||
};
|
||||
class ItemsIteratorGenerator {
|
||||
public:
|
||||
/* implicit */ ItemsIteratorGenerator(const Object& object)
|
||||
: object_(object) {}
|
||||
const_item_iterator begin() { return object_.begin(); }
|
||||
const_item_iterator end() { return object_.end(); }
|
||||
|
||||
private:
|
||||
const Object& object_;
|
||||
};
|
||||
|
||||
union Data {
|
||||
Data() : n(nullptr) {}
|
||||
~Data() {}
|
||||
|
||||
void* n;
|
||||
Array a;
|
||||
bool b;
|
||||
double d;
|
||||
int64_t i;
|
||||
std::string s;
|
||||
Object o;
|
||||
} data_;
|
||||
const Type type_;
|
||||
|
||||
// Our serialization format's first byte specifies the encoding version. That
|
||||
// way, we can easily change our format while providing backwards
|
||||
// compatibility. This constant specifies the current version of the
|
||||
// serialization format
|
||||
static const char kSerializationFormatVersion;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -171,6 +171,11 @@ class StackableDB : public DB {
|
||||
return db_->EnableFileDeletions(force);
|
||||
}
|
||||
|
||||
virtual void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData>* metadata) override {
|
||||
db_->GetLiveFilesMetaData(metadata);
|
||||
}
|
||||
|
||||
virtual Status GetLiveFiles(std::vector<std::string>& vec, uint64_t* mfs,
|
||||
bool flush_memtable = true) override {
|
||||
return db_->GetLiveFiles(vec, mfs, flush_memtable);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# RocksJava Change Log
|
||||
|
||||
## By 06/15/2014
|
||||
### New Features
|
||||
* Added basic Java binding for rocksdb::Env such that multiple RocksDB can share the same thread pool and environment.
|
||||
* Added RestoreBackupableDB
|
||||
|
||||
## By 05/30/2014
|
||||
### Internal Framework Improvement
|
||||
* Added disOwnNativeHandle to RocksObject, which allows a RocksObject to give-up the ownership of its native handle. This method is useful when sharing and transferring the ownership of RocksDB C++ resources.
|
||||
|
||||
## By 05/15/2014
|
||||
### New Features
|
||||
* Added RocksObject --- the base class of all RocksDB classes which holds some RocksDB resources in the C++ side.
|
||||
* Use environmental variable JAVA_HOME in Makefile for RocksJava
|
||||
### Public API changes
|
||||
* Renamed org.rocksdb.Iterator to org.rocksdb.RocksIterator to avoid potential confliction with Java built-in Iterator.
|
||||
|
||||
## By 04/30/2014
|
||||
### New Features
|
||||
* Added Java binding for MultiGet.
|
||||
* Added static method RocksDB.loadLibrary(), which loads necessary library files.
|
||||
* Added Java bindings for 60+ rocksdb::Options.
|
||||
* Added Java binding for BloomFilter.
|
||||
* Added Java binding for ReadOptions.
|
||||
* Added Java binding for memtables.
|
||||
* Added Java binding for sst formats.
|
||||
* Added Java binding for RocksDB Iterator which enables sequential scan operation.
|
||||
* Added Java binding for Statistics
|
||||
* Added Java binding for BackupableDB.
|
||||
|
||||
### DB Benchmark
|
||||
* Added filluniquerandom, readseq benchmark.
|
||||
* 70+ command-line options.
|
||||
* Enabled BloomFilter configuration.
|
||||
|
||||
## By 04/15/2014
|
||||
### New Features
|
||||
* Added Java binding for WriteOptions.
|
||||
* Added Java binding for WriteBatch, which enables batch-write.
|
||||
* Added Java binding for rocksdb::Options.
|
||||
* Added Java binding for block cache.
|
||||
* Added Java version DB Benchmark.
|
||||
|
||||
### DB Benchmark
|
||||
* Added readwhilewriting benchmark.
|
||||
|
||||
### Internal Framework Improvement
|
||||
* Avoid a potential byte-array-copy between c++ and Java in RocksDB.get.
|
||||
* Added SizeUnit in org.rocksdb.util to store consts like KB and GB.
|
||||
|
||||
### 03/28/2014
|
||||
* RocksJava project started.
|
||||
* Added Java binding for RocksDB, which supports Open, Close, Get and Put.
|
||||
+9
-1
@@ -3,6 +3,11 @@ NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB org.rocksdb.Options org.rocksdb.WriteB
|
||||
NATIVE_INCLUDE = ./include
|
||||
ROCKSDB_JAR = rocksdbjni.jar
|
||||
|
||||
ROCKSDBJNILIB = ./librocksdbjni.so
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
ROCKSDBJNILIB = ./librocksdbjni.jnilib
|
||||
endif
|
||||
|
||||
clean:
|
||||
-find . -name "*.class" -exec rm {} \;
|
||||
-find . -name "hs*.log" -exec rm {} \;
|
||||
@@ -10,7 +15,9 @@ clean:
|
||||
|
||||
java:
|
||||
javac org/rocksdb/util/*.java org/rocksdb/*.java
|
||||
jar -cf $(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
@cp ../HISTORY.md ./HISTORY-CPP.md
|
||||
jar -cf $(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class HISTORY*.md $(ROCKSDBJNILIB)
|
||||
@rm -f ./HISTORY-CPP.md
|
||||
javah -d $(NATIVE_INCLUDE) -jni $(NATIVE_JAVA_CLASSES)
|
||||
|
||||
sample: java
|
||||
@@ -27,6 +34,7 @@ test: java
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.BackupableDBTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.OptionsTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ReadOptionsTest
|
||||
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.StatisticsCollectorTest
|
||||
|
||||
db_bench: java
|
||||
javac org/rocksdb/benchmark/*.java
|
||||
|
||||
@@ -43,7 +43,8 @@ public class RocksDBSample {
|
||||
.setDisableSeekCompaction(true)
|
||||
.setBlockSize(64 * SizeUnit.KB)
|
||||
.setMaxBackgroundCompactions(10)
|
||||
.setFilter(filter);
|
||||
.setFilter(filter)
|
||||
.setCompressionType(CompressionType.SNAPPY_COMPRESSION);
|
||||
Statistics stats = options.statisticsPtr();
|
||||
|
||||
assert(options.createIfMissing() == true);
|
||||
@@ -52,6 +53,7 @@ public class RocksDBSample {
|
||||
assert(options.disableSeekCompaction() == true);
|
||||
assert(options.blockSize() == 64 * SizeUnit.KB);
|
||||
assert(options.maxBackgroundCompactions() == 10);
|
||||
assert(options.compressionType() == CompressionType.SNAPPY_COMPRESSION);
|
||||
|
||||
assert(options.memTableFactoryName().equals("SkipListFactory"));
|
||||
options.setMemTableConfig(
|
||||
|
||||
@@ -38,10 +38,10 @@ public class BackupableDBOptions extends RocksObject {
|
||||
boolean destroyOldData, boolean backupLogFiles, long backupRateLimit,
|
||||
long restoreRateLimit) {
|
||||
super();
|
||||
|
||||
|
||||
backupRateLimit = (backupRateLimit <= 0) ? 0 : backupRateLimit;
|
||||
restoreRateLimit = (restoreRateLimit <= 0) ? 0 : restoreRateLimit;
|
||||
|
||||
|
||||
newBackupableDBOptions(path, shareTableFiles, sync, destroyOldData,
|
||||
backupLogFiles, backupRateLimit, restoreRateLimit);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
public enum CompressionType {
|
||||
NO_COMPRESSION((byte) 0),
|
||||
SNAPPY_COMPRESSION((byte) 1),
|
||||
ZLIB_COMPRESSION((byte) 2),
|
||||
BZLIB2_COMPRESSION((byte) 3),
|
||||
LZ4_COMPRESSION((byte) 4),
|
||||
LZ4HC_COMPRESSION((byte) 5);
|
||||
|
||||
private final byte value_;
|
||||
|
||||
private CompressionType(byte value) {
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
public byte getValue() {
|
||||
return value_;
|
||||
}
|
||||
}
|
||||
@@ -1307,6 +1307,34 @@ 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.
|
||||
*/
|
||||
public Options setCompressionType(CompressionType compressionType) {
|
||||
setCompressionType(nativeHandle_, compressionType.getValue());
|
||||
return this;
|
||||
}
|
||||
private native void setCompressionType(long handle, byte compressionType);
|
||||
|
||||
/**
|
||||
* If true, place whole keys in the filter (not just prefixes).
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.rocksdb.util.Environment;
|
||||
public class RocksDB extends RocksObject {
|
||||
public static final int NOT_FOUND = -1;
|
||||
private static final String[] compressionLibs_ = {
|
||||
"snappy", "zlib", "bzip2", "lz4", "lz4hc"};
|
||||
"snappy", "z", "bzip2", "lz4", "lz4hc"};
|
||||
|
||||
/**
|
||||
* Loads the necessary library files.
|
||||
@@ -93,10 +93,7 @@ public class RocksDB extends RocksObject {
|
||||
// This allows to use the rocksjni default Options instead of
|
||||
// the c++ one.
|
||||
Options options = new Options();
|
||||
db.open(options.nativeHandle_, options.cacheSize_, path);
|
||||
db.transferCppRawPointersOwnershipFrom(options);
|
||||
options.dispose();
|
||||
return db;
|
||||
return open(options, path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,12 +6,17 @@
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* RocksObject is the base-class of all RocksDB related class that has
|
||||
* a pointer to some c++ rocksdb object. Although RocksObject
|
||||
* will release its c++ resource on its finalize() once it has been
|
||||
* garbage-collected, it is suggested to call dispose() manually to
|
||||
* release its c++ resource once an instance of RocksObject is no
|
||||
* longer used.
|
||||
* RocksObject is the base-class of all RocksDB classes that has a pointer to
|
||||
* some c++ rocksdb object.
|
||||
*
|
||||
* RocksObject has dispose() function, which releases its associated c++ resource.
|
||||
* This function can be either called manually, or being called automatically
|
||||
* during the regular Java GC process. However, since Java may wrongly assume a
|
||||
* RocksObject only contains a long member variable and think it is small in size,
|
||||
* Java may give RocksObject low priority in the GC process. For this, it is
|
||||
* suggested to call dispose() manually. However, it is safe to let RocksObject go
|
||||
* out-of-scope without manually calling dispose() as dispose() will be called
|
||||
* in the finalizer during the regular GC process.
|
||||
*/
|
||||
public abstract class RocksObject {
|
||||
protected RocksObject() {
|
||||
@@ -20,7 +25,14 @@ public abstract class RocksObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the c++ object pointed by the native handle.
|
||||
* Release the c++ object manually pointed by the native handle.
|
||||
*
|
||||
* Note that dispose() will also be called during the GC process
|
||||
* if it was not called before its RocksObject went out-of-scope.
|
||||
* However, since Java may wrongly wrongly assume those objects are
|
||||
* small in that they seems to only hold a long variable. As a result,
|
||||
* they might have low priority in the GC process. To prevent this,
|
||||
* it is suggested to call dispose() manually.
|
||||
*
|
||||
* Note that once an instance of RocksObject has been disposed,
|
||||
* calling its function will lead undefined behavior.
|
||||
@@ -47,26 +59,59 @@ public abstract class RocksObject {
|
||||
* takes over ownership of the native object or both will attempt to delete
|
||||
* the underlying object when garbage collected.
|
||||
*
|
||||
* When disOwnNativeHandle is called, dispose() will simply set nativeHandle_
|
||||
* When disOwnNativeHandle() is called, dispose() will simply set nativeHandle_
|
||||
* to 0 without releasing its associated C++ resource. As a result,
|
||||
* incorrectly use this function may cause memory leak.
|
||||
* incorrectly use this function may cause memory leak, and this function call
|
||||
* will not affect the return value of isInitialized().
|
||||
*
|
||||
* @see dispose()
|
||||
* @see isInitialized()
|
||||
*/
|
||||
protected void disOwnNativeHandle() {
|
||||
owningHandle_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current RocksObject is responsable to release its
|
||||
* native handle.
|
||||
*
|
||||
* @return true if the current RocksObject is responsible to release its
|
||||
* native handle.
|
||||
*
|
||||
* @see disOwnNativeHandle()
|
||||
* @see dispose()
|
||||
*/
|
||||
protected boolean isOwningNativeHandle() {
|
||||
return owningHandle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the associated native handle has been initialized.
|
||||
*
|
||||
* @return true if the associated native handle has been initialized.
|
||||
*
|
||||
* @see dispose()
|
||||
*/
|
||||
protected boolean isInitialized() {
|
||||
return (nativeHandle_ != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply calls dispose() and release its c++ resource if it has not
|
||||
* yet released.
|
||||
*/
|
||||
@Override protected void finalize() {
|
||||
dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* A long variable holding c++ pointer pointing to some RocksDB C++ object.
|
||||
*/
|
||||
protected long nativeHandle_;
|
||||
|
||||
/**
|
||||
* A flag indicating whether the current RocksObject is responsible to
|
||||
* release the c++ object stored in its nativeHandle_.
|
||||
*/
|
||||
private boolean owningHandle_;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Helper class to collect DB statistics periodically at a period specified in
|
||||
* constructor. Callback function (provided in constructor) is called with
|
||||
* every statistics collection.
|
||||
*
|
||||
* Caller should call start() to start statistics collection. Shutdown() should
|
||||
* be called to stop stats collection and should be called before statistics (
|
||||
* provided in constructor) reference has been disposed.
|
||||
*/
|
||||
public class StatisticsCollector {
|
||||
private final Statistics _statistics;
|
||||
private final ThreadPoolExecutor _threadPoolExecutor;
|
||||
private final int _statsCollectionInterval;
|
||||
private final StatisticsCollectorCallback _statsCallback;
|
||||
private volatile boolean _isRunning = true;
|
||||
|
||||
/**
|
||||
* Constructor for statistics collector.
|
||||
* @param statistics Reference of DB statistics.
|
||||
* @param statsCollectionIntervalInMilliSeconds Statistics collection time
|
||||
* period (specified in milliseconds)
|
||||
* @param statsCallback Reference of statistics callback interface.
|
||||
*/
|
||||
public StatisticsCollector(Statistics statistics,
|
||||
int statsCollectionIntervalInMilliSeconds,
|
||||
StatisticsCollectorCallback statsCallback) {
|
||||
_statistics = statistics;
|
||||
_statsCollectionInterval = statsCollectionIntervalInMilliSeconds;
|
||||
_statsCallback = statsCallback;
|
||||
|
||||
_threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
|
||||
new ArrayBlockingQueue<Runnable>(1));
|
||||
}
|
||||
|
||||
public void start() {
|
||||
_threadPoolExecutor.submit(collectStatistics());
|
||||
}
|
||||
|
||||
public void shutDown() throws InterruptedException {
|
||||
_isRunning = false;
|
||||
|
||||
_threadPoolExecutor.shutdown();
|
||||
// Wait for collectStatistics runnable to finish so that disposal of
|
||||
// statistics does not cause any exceptions to be thrown.
|
||||
_threadPoolExecutor.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private Runnable collectStatistics() {
|
||||
return new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (_isRunning) {
|
||||
try {
|
||||
// Collect ticker data
|
||||
for(TickerType ticker : TickerType.values()) {
|
||||
long tickerValue = _statistics.getTickerCount(ticker);
|
||||
_statsCallback.tickerCallback(ticker, tickerValue);
|
||||
}
|
||||
|
||||
// Collect histogram data
|
||||
for(HistogramType histogramType : HistogramType.values()) {
|
||||
HistogramData histogramData =
|
||||
_statistics.geHistogramData(histogramType);
|
||||
_statsCallback.histogramCallback(histogramType, histogramData);
|
||||
}
|
||||
|
||||
Thread.sleep(_statsCollectionInterval);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("Thread got interrupted!", e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Error while calculating statistics", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* Callback interface provided to StatisticsCollector.
|
||||
*
|
||||
* Thread safety:
|
||||
* StatisticsCollector doesn't make any guarantees about thread safety.
|
||||
* If the same reference of StatisticsCollectorCallback is passed to multiple
|
||||
* StatisticsCollector references, then its the responsibility of the
|
||||
* user to make StatisticsCollectorCallback' implementation thread-safe.
|
||||
*
|
||||
* @param tickerType
|
||||
* @param tickerCount
|
||||
*/
|
||||
public interface StatisticsCollectorCallback {
|
||||
/**
|
||||
* Callback function to get ticker values.
|
||||
* @param tickerType Ticker type.
|
||||
* @param tickerCount Value of ticker type.
|
||||
*/
|
||||
void tickerCallback(TickerType tickerType, long tickerCount);
|
||||
|
||||
/**
|
||||
* Callback function to get histogram values.
|
||||
* @param histType Histogram type.
|
||||
* @param histData Histogram data.
|
||||
*/
|
||||
void histogramCallback(HistogramType histType, HistogramData histData);
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
package org.rocksdb.benchmark;
|
||||
|
||||
import java.lang.Runnable;
|
||||
import java.lang.Math;
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
@@ -240,7 +241,8 @@ public class DbBenchmark {
|
||||
if (entriesPerBatch_ == 1) {
|
||||
for (long i = 0; i < numEntries_; ++i) {
|
||||
getKey(key, i, keyRange_);
|
||||
db_.put(writeOpt_, key, DbBenchmark.this.gen_.generate(valueSize_));
|
||||
DbBenchmark.this.gen_.generate(value);
|
||||
db_.put(writeOpt_, key, value);
|
||||
stats_.finishedSingleOp(keySize_ + valueSize_);
|
||||
writeRateControl(i);
|
||||
if (isFinished()) {
|
||||
@@ -252,7 +254,8 @@ public class DbBenchmark {
|
||||
WriteBatch batch = new WriteBatch();
|
||||
for (long j = 0; j < entriesPerBatch_; j++) {
|
||||
getKey(key, i + j, keyRange_);
|
||||
batch.put(key, DbBenchmark.this.gen_.generate(valueSize_));
|
||||
DbBenchmark.this.gen_.generate(value);
|
||||
db_.put(writeOpt_, key, value);
|
||||
stats_.finishedSingleOp(keySize_ + valueSize_);
|
||||
}
|
||||
db_.write(writeOpt_, batch);
|
||||
@@ -460,7 +463,7 @@ public class DbBenchmark {
|
||||
if (compressionType_.equals("snappy")) {
|
||||
System.loadLibrary("snappy");
|
||||
} else if (compressionType_.equals("zlib")) {
|
||||
System.loadLibrary("zlib");
|
||||
System.loadLibrary("z");
|
||||
} else if (compressionType_.equals("bzip2")) {
|
||||
System.loadLibrary("bzip2");
|
||||
} else if (compressionType_.equals("lz4")) {
|
||||
@@ -473,7 +476,6 @@ public class DbBenchmark {
|
||||
"No compression is used.%n",
|
||||
compressionType_, e.toString());
|
||||
compressionType_ = "none";
|
||||
compressionRatio_ = 1.0;
|
||||
}
|
||||
gen_ = new RandomGenerator(randSeed_, compressionRatio_);
|
||||
}
|
||||
@@ -1522,24 +1524,54 @@ public class DbBenchmark {
|
||||
private final byte[] data_;
|
||||
private int dataLength_;
|
||||
private int position_;
|
||||
private double compressionRatio_;
|
||||
Random rand_;
|
||||
|
||||
private RandomGenerator(long seed, double compressionRatio) {
|
||||
// We use a limited amount of data over and over again and ensure
|
||||
// that it is larger than the compression window (32KB), and also
|
||||
byte[] value = new byte[100];
|
||||
// large enough to serve all typical value sizes we want to write.
|
||||
rand_ = new Random(seed);
|
||||
dataLength_ = 1048576 + 100;
|
||||
dataLength_ = value.length * 10000;
|
||||
data_ = new byte[dataLength_];
|
||||
// TODO(yhchiang): mimic test::CompressibleString?
|
||||
for (int i = 0; i < dataLength_; ++i) {
|
||||
data_[i] = (byte) (' ' + rand_.nextInt(95));
|
||||
compressionRatio_ = compressionRatio;
|
||||
int pos = 0;
|
||||
while (pos < dataLength_) {
|
||||
compressibleBytes(value);
|
||||
System.arraycopy(value, 0, data_, pos,
|
||||
Math.min(value.length, dataLength_ - pos));
|
||||
pos += value.length;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] generate(int length) {
|
||||
position_ = rand_.nextInt(data_.length - length);
|
||||
return Arrays.copyOfRange(data_, position_, position_ + length);
|
||||
private void compressibleBytes(byte[] value) {
|
||||
int baseLength = value.length;
|
||||
if (compressionRatio_ < 1.0d) {
|
||||
baseLength = (int) (compressionRatio_ * value.length + 0.5);
|
||||
}
|
||||
if (baseLength <= 0) {
|
||||
baseLength = 1;
|
||||
}
|
||||
int pos;
|
||||
for (pos = 0; pos < baseLength; ++pos) {
|
||||
value[pos] = (byte) (' ' + rand_.nextInt(95)); // ' ' .. '~'
|
||||
}
|
||||
while (pos < value.length) {
|
||||
System.arraycopy(value, 0, value, pos,
|
||||
Math.min(baseLength, value.length - pos));
|
||||
pos += baseLength;
|
||||
}
|
||||
}
|
||||
|
||||
private void generate(byte[] value) {
|
||||
if (position_ + value.length > data_.length) {
|
||||
position_ = 0;
|
||||
assert(value.length <= data_.length);
|
||||
}
|
||||
position_ += value.length;
|
||||
System.arraycopy(data_, position_ - value.length,
|
||||
value, 0, value.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
package org.rocksdb.test;
|
||||
|
||||
import org.rocksdb.*;
|
||||
|
||||
public class StatisticsCollectorTest {
|
||||
static final String db_path = "/tmp/backupablejni_db";
|
||||
static {
|
||||
RocksDB.loadLibrary();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
throws InterruptedException, RocksDBException {
|
||||
Options opt = new Options().createStatistics().setCreateIfMissing(true);
|
||||
Statistics stats = opt.statisticsPtr();
|
||||
|
||||
RocksDB db = RocksDB.open(db_path);
|
||||
|
||||
StatsCallbackMock callback = new StatsCallbackMock();
|
||||
StatisticsCollector statsCollector = new StatisticsCollector(stats, 100,
|
||||
callback);
|
||||
statsCollector.start();
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
assert(callback.tickerCallbackCount > 0);
|
||||
assert(callback.histCallbackCount > 0);
|
||||
|
||||
statsCollector.shutDown();
|
||||
|
||||
db.close();
|
||||
opt.dispose();
|
||||
|
||||
System.out.println("Stats collector test passed.!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.test;
|
||||
|
||||
import org.rocksdb.*;
|
||||
|
||||
public class StatsCallbackMock implements StatisticsCollectorCallback {
|
||||
public int tickerCallbackCount = 0;
|
||||
public int histCallbackCount = 0;
|
||||
|
||||
public void tickerCallback(TickerType tickerType, long tickerCount) {
|
||||
tickerCallbackCount++;
|
||||
}
|
||||
|
||||
public void histogramCallback(HistogramType histType,
|
||||
HistogramData histData) {
|
||||
histCallbackCount++;
|
||||
}
|
||||
}
|
||||
@@ -935,6 +935,27 @@ void Java_org_rocksdb_Options_setBlockRestartInterval(
|
||||
static_cast<int>(jblock_restart_interval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setCompressionType
|
||||
* Signature: (JB)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setCompressionType(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle, jbyte compression) {
|
||||
reinterpret_cast<rocksdb::Options*>(jhandle)->compression =
|
||||
static_cast<rocksdb::CompressionType>(compression);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: compressionType
|
||||
* Signature: (J)B
|
||||
*/
|
||||
jbyte Java_org_rocksdb_Options_compressionType(
|
||||
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||
return reinterpret_cast<rocksdb::Options*>(jhandle)->compression;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: wholeKeyFiltering
|
||||
|
||||
+13
-14
@@ -22,7 +22,7 @@ class RocksDBJni {
|
||||
public:
|
||||
// Get the java class id of org.rocksdb.RocksDB.
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/RocksDB");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/RocksDB");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class RocksDBExceptionJni {
|
||||
public:
|
||||
// Get the jclass of org.rocksdb.RocksDBException
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/RocksDBException");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/RocksDBException");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ class OptionsJni {
|
||||
public:
|
||||
// Get the java class id of org.rocksdb.Options.
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/Options");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/Options");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ class WriteOptionsJni {
|
||||
public:
|
||||
// Get the java class id of org.rocksdb.WriteOptions.
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/WriteOptions");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/WriteOptions");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -148,7 +148,7 @@ class ReadOptionsJni {
|
||||
public:
|
||||
// Get the java class id of org.rocksdb.ReadOptions.
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/ReadOptions");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/ReadOptions");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -181,7 +181,7 @@ class ReadOptionsJni {
|
||||
class WriteBatchJni {
|
||||
public:
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/WriteBatch");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/WriteBatch");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -211,8 +211,7 @@ class WriteBatchJni {
|
||||
class HistogramDataJni {
|
||||
public:
|
||||
static jmethodID getConstructorMethodId(JNIEnv* env, jclass jclazz) {
|
||||
static jmethodID mid = env->GetMethodID(
|
||||
jclazz, "<init>", "(DDDDD)V");
|
||||
static jmethodID mid = env->GetMethodID(jclazz, "<init>", "(DDDDD)V");
|
||||
assert(mid != nullptr);
|
||||
return mid;
|
||||
}
|
||||
@@ -222,7 +221,7 @@ class BackupableDBOptionsJni {
|
||||
public:
|
||||
// Get the java class id of org.rocksdb.BackupableDBOptions.
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/BackupableDBOptions");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/BackupableDBOptions");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -255,7 +254,7 @@ class IteratorJni {
|
||||
public:
|
||||
// Get the java class id of org.rocksdb.Iteartor.
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/RocksIterator");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/RocksIterator");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -288,7 +287,7 @@ class FilterJni {
|
||||
public:
|
||||
// Get the java class id of org.rocksdb.FilterPolicy.
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("org/rocksdb/Filter");
|
||||
jclass jclazz = env->FindClass("org/rocksdb/Filter");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
@@ -321,21 +320,21 @@ class ListJni {
|
||||
public:
|
||||
// Get the java class id of java.util.List.
|
||||
static jclass getListClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("java/util/List");
|
||||
jclass jclazz = env->FindClass("java/util/List");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
|
||||
// Get the java class id of java.util.ArrayList.
|
||||
static jclass getArrayListClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("java/util/ArrayList");
|
||||
jclass jclazz = env->FindClass("java/util/ArrayList");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
|
||||
// Get the java class id of java.util.Iterator.
|
||||
static jclass getIteratorClass(JNIEnv* env) {
|
||||
static jclass jclazz = env->FindClass("java/util/Iterator");
|
||||
jclass jclazz = env->FindClass("java/util/Iterator");
|
||||
assert(jclazz != nullptr);
|
||||
return jclazz;
|
||||
}
|
||||
|
||||
+33
-7
@@ -9,26 +9,29 @@
|
||||
|
||||
#include "port/port_posix.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
#include <cstdlib>
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace port {
|
||||
|
||||
static void PthreadCall(const char* label, int result) {
|
||||
if (result != 0) {
|
||||
static int PthreadCall(const char* label, int result) {
|
||||
if (result != 0 && result != ETIMEDOUT) {
|
||||
fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
|
||||
abort();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Mutex::Mutex(bool adaptive) {
|
||||
#ifdef OS_LINUX
|
||||
if (!adaptive) {
|
||||
PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL));
|
||||
PthreadCall("init mutex", pthread_mutex_init(&mu_, nullptr));
|
||||
} else {
|
||||
pthread_mutexattr_t mutex_attr;
|
||||
PthreadCall("init mutex attr", pthread_mutexattr_init(&mutex_attr));
|
||||
@@ -40,7 +43,7 @@ Mutex::Mutex(bool adaptive) {
|
||||
pthread_mutexattr_destroy(&mutex_attr));
|
||||
}
|
||||
#else // ignore adaptive for non-linux platform
|
||||
PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL));
|
||||
PthreadCall("init mutex", pthread_mutex_init(&mu_, nullptr));
|
||||
#endif // OS_LINUX
|
||||
}
|
||||
|
||||
@@ -68,7 +71,7 @@ void Mutex::AssertHeld() {
|
||||
|
||||
CondVar::CondVar(Mutex* mu)
|
||||
: mu_(mu) {
|
||||
PthreadCall("init cv", pthread_cond_init(&cv_, NULL));
|
||||
PthreadCall("init cv", pthread_cond_init(&cv_, nullptr));
|
||||
}
|
||||
|
||||
CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); }
|
||||
@@ -83,6 +86,27 @@ void CondVar::Wait() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CondVar::TimedWait(uint64_t abs_time_us) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = abs_time_us / 1000000;
|
||||
ts.tv_nsec = (abs_time_us % 1000000) * 1000;
|
||||
|
||||
#ifndef NDEBUG
|
||||
mu_->locked_ = false;
|
||||
#endif
|
||||
int err = pthread_cond_timedwait(&cv_, &mu_->mu_, &ts);
|
||||
#ifndef NDEBUG
|
||||
mu_->locked_ = true;
|
||||
#endif
|
||||
if (err == ETIMEDOUT) {
|
||||
return true;
|
||||
}
|
||||
if (err != 0) {
|
||||
PthreadCall("timedwait", err);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CondVar::Signal() {
|
||||
PthreadCall("signal", pthread_cond_signal(&cv_));
|
||||
}
|
||||
@@ -91,7 +115,9 @@ void CondVar::SignalAll() {
|
||||
PthreadCall("broadcast", pthread_cond_broadcast(&cv_));
|
||||
}
|
||||
|
||||
RWMutex::RWMutex() { PthreadCall("init mutex", pthread_rwlock_init(&mu_, NULL)); }
|
||||
RWMutex::RWMutex() {
|
||||
PthreadCall("init mutex", pthread_rwlock_init(&mu_, nullptr));
|
||||
}
|
||||
|
||||
RWMutex::~RWMutex() { PthreadCall("destroy mutex", pthread_rwlock_destroy(&mu_)); }
|
||||
|
||||
|
||||
@@ -137,6 +137,8 @@ class CondVar {
|
||||
explicit CondVar(Mutex* mu);
|
||||
~CondVar();
|
||||
void Wait();
|
||||
// Timed condition wait. Returns true if timeout occurred.
|
||||
bool TimedWait(uint64_t abs_time_us);
|
||||
void Signal();
|
||||
void SignalAll();
|
||||
private:
|
||||
|
||||
+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>
|
||||
|
||||
@@ -56,13 +56,11 @@ const size_t kMaxCacheKeyPrefixSize __attribute__((unused)) =
|
||||
|
||||
// Read the block identified by "handle" from "file".
|
||||
// The only relevant option is options.verify_checksums for now.
|
||||
// Set *didIO to true if didIO is not null.
|
||||
// On failure return non-OK.
|
||||
// On success fill *result and return OK - caller owns *result
|
||||
Status ReadBlockFromFile(RandomAccessFile* file, const Footer& footer,
|
||||
const ReadOptions& options, const BlockHandle& handle,
|
||||
Block** result, Env* env, bool* didIO = nullptr,
|
||||
bool do_uncompress = true) {
|
||||
Block** result, Env* env, bool do_uncompress = true) {
|
||||
BlockContents contents;
|
||||
Status s = ReadBlockContents(file, footer, options, handle, &contents, env,
|
||||
do_uncompress);
|
||||
@@ -70,9 +68,6 @@ Status ReadBlockFromFile(RandomAccessFile* file, const Footer& footer,
|
||||
*result = new Block(contents);
|
||||
}
|
||||
|
||||
if (didIO != nullptr) {
|
||||
*didIO = true;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -834,7 +829,7 @@ Iterator* BlockBasedTable::NewIndexIterator(const ReadOptions& read_options) {
|
||||
// Convert an index iterator value (i.e., an encoded BlockHandle)
|
||||
// into an iterator over the contents of the corresponding block.
|
||||
Iterator* BlockBasedTable::NewDataBlockIterator(Rep* rep,
|
||||
const ReadOptions& ro, bool* didIO, const Slice& index_value) {
|
||||
const ReadOptions& ro, const Slice& index_value) {
|
||||
const bool no_io = (ro.read_tier == kBlockCacheTier);
|
||||
Cache* block_cache = rep->options.block_cache.get();
|
||||
Cache* block_cache_compressed = rep->options.
|
||||
@@ -872,7 +867,7 @@ Iterator* BlockBasedTable::NewDataBlockIterator(Rep* rep,
|
||||
}
|
||||
|
||||
s = GetDataBlockFromCache(key, ckey, block_cache, block_cache_compressed,
|
||||
statistics, ro, &block);
|
||||
statistics, ro, &block);
|
||||
|
||||
if (block.value == nullptr && !no_io && ro.fill_cache) {
|
||||
Histograms histogram = READ_BLOCK_GET_MICROS;
|
||||
@@ -880,7 +875,7 @@ Iterator* BlockBasedTable::NewDataBlockIterator(Rep* rep,
|
||||
{
|
||||
StopWatch sw(rep->options.env, statistics, histogram);
|
||||
s = ReadBlockFromFile(rep->file.get(), rep->footer, ro, handle,
|
||||
&raw_block, rep->options.env, didIO,
|
||||
&raw_block, rep->options.env,
|
||||
block_cache_compressed == nullptr);
|
||||
}
|
||||
|
||||
@@ -898,7 +893,7 @@ Iterator* BlockBasedTable::NewDataBlockIterator(Rep* rep,
|
||||
return NewErrorIterator(Status::Incomplete("no blocking io"));
|
||||
}
|
||||
s = ReadBlockFromFile(rep->file.get(), rep->footer, ro, handle,
|
||||
&block.value, rep->options.env, didIO);
|
||||
&block.value, rep->options.env);
|
||||
}
|
||||
|
||||
Iterator* iter;
|
||||
@@ -919,13 +914,13 @@ Iterator* BlockBasedTable::NewDataBlockIterator(Rep* rep,
|
||||
class BlockBasedTable::BlockEntryIteratorState : public TwoLevelIteratorState {
|
||||
public:
|
||||
BlockEntryIteratorState(BlockBasedTable* table,
|
||||
const ReadOptions& read_options, bool* did_io)
|
||||
: TwoLevelIteratorState(table->rep_->options.prefix_extractor != nullptr),
|
||||
table_(table), read_options_(read_options), did_io_(did_io) {}
|
||||
const ReadOptions& read_options)
|
||||
: TwoLevelIteratorState(table->rep_->options.prefix_extractor != nullptr),
|
||||
table_(table),
|
||||
read_options_(read_options) {}
|
||||
|
||||
Iterator* NewSecondaryIterator(const Slice& index_value) override {
|
||||
return NewDataBlockIterator(table_->rep_, read_options_, did_io_,
|
||||
index_value);
|
||||
return NewDataBlockIterator(table_->rep_, read_options_, index_value);
|
||||
}
|
||||
|
||||
bool PrefixMayMatch(const Slice& internal_key) override {
|
||||
@@ -936,8 +931,6 @@ class BlockBasedTable::BlockEntryIteratorState : public TwoLevelIteratorState {
|
||||
// Don't own table_
|
||||
BlockBasedTable* table_;
|
||||
const ReadOptions read_options_;
|
||||
// Don't own did_io_
|
||||
bool* did_io_;
|
||||
};
|
||||
|
||||
// This will be broken if the user specifies an unusual implementation
|
||||
@@ -1021,15 +1014,14 @@ bool BlockBasedTable::PrefixMayMatch(const Slice& internal_key) {
|
||||
|
||||
Iterator* BlockBasedTable::NewIterator(const ReadOptions& read_options,
|
||||
Arena* arena) {
|
||||
return NewTwoLevelIterator(
|
||||
new BlockEntryIteratorState(this, read_options, nullptr),
|
||||
NewIndexIterator(read_options), arena);
|
||||
return NewTwoLevelIterator(new BlockEntryIteratorState(this, read_options),
|
||||
NewIndexIterator(read_options), arena);
|
||||
}
|
||||
|
||||
Status BlockBasedTable::Get(
|
||||
const ReadOptions& read_options, const Slice& key, void* handle_context,
|
||||
bool (*result_handler)(void* handle_context, const ParsedInternalKey& k,
|
||||
const Slice& v, bool didIO),
|
||||
const Slice& v),
|
||||
void (*mark_key_may_exist_handler)(void* handle_context)) {
|
||||
Status s;
|
||||
Iterator* iiter = NewIndexIterator(read_options);
|
||||
@@ -1052,9 +1044,8 @@ Status BlockBasedTable::Get(
|
||||
RecordTick(rep_->options.statistics.get(), BLOOM_FILTER_USEFUL);
|
||||
break;
|
||||
} else {
|
||||
bool didIO = false;
|
||||
unique_ptr<Iterator> block_iter(
|
||||
NewDataBlockIterator(rep_, read_options, &didIO, iiter->value()));
|
||||
NewDataBlockIterator(rep_, read_options, iiter->value()));
|
||||
|
||||
if (read_options.read_tier && block_iter->status().IsIncomplete()) {
|
||||
// couldn't get block from block_cache
|
||||
@@ -1071,8 +1062,8 @@ Status BlockBasedTable::Get(
|
||||
s = Status::Corruption(Slice());
|
||||
}
|
||||
|
||||
if (!(*result_handler)(handle_context, parsed_key, block_iter->value(),
|
||||
didIO)) {
|
||||
if (!(*result_handler)(handle_context, parsed_key,
|
||||
block_iter->value())) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
@@ -1089,22 +1080,34 @@ Status BlockBasedTable::Get(
|
||||
return s;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool SaveDidIO(void* arg, const ParsedInternalKey& key, const Slice& value,
|
||||
bool didIO) {
|
||||
*reinterpret_cast<bool*>(arg) = didIO;
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool BlockBasedTable::TEST_KeyInCache(const ReadOptions& options,
|
||||
const Slice& key) {
|
||||
// We use Get() as it has logic that checks whether we read the
|
||||
// block from the disk or not.
|
||||
bool didIO = false;
|
||||
Status s = Get(options, key, &didIO, SaveDidIO);
|
||||
std::unique_ptr<Iterator> iiter(NewIndexIterator(options));
|
||||
iiter->Seek(key);
|
||||
assert(iiter->Valid());
|
||||
CachableEntry<Block> block;
|
||||
|
||||
BlockHandle handle;
|
||||
Slice input = iiter->value();
|
||||
Status s = handle.DecodeFrom(&input);
|
||||
assert(s.ok());
|
||||
return !didIO;
|
||||
Cache* block_cache = rep_->options.block_cache.get();
|
||||
assert(block_cache != nullptr);
|
||||
|
||||
char cache_key_storage[kMaxCacheKeyPrefixSize + kMaxVarint64Length];
|
||||
Slice cache_key =
|
||||
GetCacheKey(rep_->cache_key_prefix, rep_->cache_key_prefix_size, handle,
|
||||
cache_key_storage);
|
||||
Slice ckey;
|
||||
|
||||
s = GetDataBlockFromCache(cache_key, ckey, block_cache, nullptr, nullptr,
|
||||
options, &block);
|
||||
assert(s.ok());
|
||||
bool in_cache = block.value != nullptr;
|
||||
if (in_cache) {
|
||||
ReleaseCachedEntry(block_cache, block.cache_handle);
|
||||
}
|
||||
return in_cache;
|
||||
}
|
||||
|
||||
// REQUIRES: The following fields of rep_ should have already been populated:
|
||||
|
||||
@@ -73,8 +73,7 @@ class BlockBasedTable : public TableReader {
|
||||
Status Get(const ReadOptions& readOptions, const Slice& key,
|
||||
void* handle_context,
|
||||
bool (*result_handler)(void* handle_context,
|
||||
const ParsedInternalKey& k, const Slice& v,
|
||||
bool didIO),
|
||||
const ParsedInternalKey& k, const Slice& v),
|
||||
void (*mark_key_may_exist_handler)(void* handle_context) =
|
||||
nullptr) override;
|
||||
|
||||
@@ -87,7 +86,7 @@ class BlockBasedTable : public TableReader {
|
||||
uint64_t ApproximateOffsetOf(const Slice& key) override;
|
||||
|
||||
// Returns true if the block for the specified key is in cache.
|
||||
// REQUIRES: key is in this table.
|
||||
// REQUIRES: key is in this table && block cache enabled
|
||||
bool TEST_KeyInCache(const ReadOptions& options, const Slice& key);
|
||||
|
||||
// Set up the table for Compaction. Might change some parameters with
|
||||
@@ -113,7 +112,7 @@ class BlockBasedTable : public TableReader {
|
||||
|
||||
class BlockEntryIteratorState;
|
||||
static Iterator* NewDataBlockIterator(Rep* rep, const ReadOptions& ro,
|
||||
bool* didIO, const Slice& index_value);
|
||||
const Slice& index_value);
|
||||
|
||||
// For the following two functions:
|
||||
// if `no_io == true`, we will not try to read filter/index from sst file
|
||||
|
||||
@@ -33,25 +33,13 @@ TableBuilder* PlainTableFactory::NewTableBuilder(
|
||||
index_sparseness_);
|
||||
}
|
||||
|
||||
extern TableFactory* NewPlainTableFactory(uint32_t user_key_len,
|
||||
int bloom_bits_per_key,
|
||||
double hash_table_ratio,
|
||||
size_t index_sparseness,
|
||||
size_t huge_page_tlb_size,
|
||||
EncodingType encoding_type) {
|
||||
return new PlainTableFactory(user_key_len, bloom_bits_per_key,
|
||||
hash_table_ratio, index_sparseness,
|
||||
huge_page_tlb_size, encoding_type);
|
||||
}
|
||||
|
||||
extern TableFactory* NewTotalOrderPlainTableFactory(uint32_t user_key_len,
|
||||
int bloom_bits_per_key,
|
||||
size_t index_sparseness,
|
||||
size_t huge_page_tlb_size,
|
||||
bool full_scan_mode) {
|
||||
return new PlainTableFactory(user_key_len, bloom_bits_per_key, 0,
|
||||
index_sparseness, huge_page_tlb_size, kPlain,
|
||||
full_scan_mode);
|
||||
extern TableFactory* NewPlainTableFactory(
|
||||
uint32_t user_key_len, int bloom_bits_per_key, double hash_table_ratio,
|
||||
size_t index_sparseness, size_t huge_page_tlb_size,
|
||||
EncodingType encoding_type, bool full_scan_mode) {
|
||||
return new PlainTableFactory(
|
||||
user_key_len, bloom_bits_per_key, hash_table_ratio, index_sparseness,
|
||||
huge_page_tlb_size, encoding_type, full_scan_mode);
|
||||
}
|
||||
|
||||
const std::string PlainTablePropertyNames::kPrefixExtractorName =
|
||||
|
||||
@@ -198,7 +198,8 @@ Status PlainTableKeyDecoder::NextPlainEncodingKey(
|
||||
user_key_size = static_cast<size_t>(tmp_size);
|
||||
*bytes_read = key_ptr - start;
|
||||
}
|
||||
bool decoded_internal_key_valid;
|
||||
// dummy initial value to avoid compiler complain
|
||||
bool decoded_internal_key_valid = true;
|
||||
Slice decoded_internal_key;
|
||||
Status s =
|
||||
ReadInternalKey(key_ptr, limit, user_key_size, parsed_key, bytes_read,
|
||||
@@ -227,7 +228,8 @@ Status PlainTableKeyDecoder::NextPrefixEncodingKey(
|
||||
bool expect_suffix = false;
|
||||
do {
|
||||
size_t size = 0;
|
||||
bool decoded_internal_key_valid;
|
||||
// dummy initial value to avoid compiler complain
|
||||
bool decoded_internal_key_valid = true;
|
||||
const char* pos = DecodeSize(key_ptr, limit, &entry_type, &size);
|
||||
if (pos == nullptr) {
|
||||
return Status::Corruption("Unexpected EOF when reading size of the key");
|
||||
|
||||
@@ -333,7 +333,7 @@ void PlainTableReader::AllocateIndexAndBloom(int num_prefixes,
|
||||
uint32_t bloom_total_bits = num_prefixes * bloom_bits_per_key;
|
||||
if (bloom_total_bits > 0) {
|
||||
enable_bloom_ = true;
|
||||
bloom_.SetTotalBits(bloom_total_bits, options_.bloom_locality,
|
||||
bloom_.SetTotalBits(&arena_, bloom_total_bits, options_.bloom_locality,
|
||||
huge_page_tlb_size, options_.info_log.get());
|
||||
}
|
||||
}
|
||||
@@ -465,7 +465,7 @@ Status PlainTableReader::PopulateIndex(TableProperties* props,
|
||||
table_properties_->num_entries * bloom_bits_per_key;
|
||||
if (num_bloom_bits > 0) {
|
||||
enable_bloom_ = true;
|
||||
bloom_.SetTotalBits(num_bloom_bits, options_.bloom_locality,
|
||||
bloom_.SetTotalBits(&arena_, num_bloom_bits, options_.bloom_locality,
|
||||
huge_page_tlb_size, options_.info_log.get());
|
||||
}
|
||||
}
|
||||
@@ -643,7 +643,7 @@ void PlainTableReader::Prepare(const Slice& target) {
|
||||
Status PlainTableReader::Get(const ReadOptions& ro, const Slice& target,
|
||||
void* arg,
|
||||
bool (*saver)(void*, const ParsedInternalKey&,
|
||||
const Slice&, bool),
|
||||
const Slice&),
|
||||
void (*mark_key_may_exist)(void*)) {
|
||||
// Check bloom filter first.
|
||||
Slice prefix_slice;
|
||||
@@ -699,7 +699,7 @@ Status PlainTableReader::Get(const ReadOptions& ro, const Slice& target,
|
||||
prefix_match = true;
|
||||
}
|
||||
if (internal_comparator_.Compare(found_key, parsed_target) >= 0) {
|
||||
if (!(*saver)(arg, found_key, found_value, true)) {
|
||||
if (!(*saver)(arg, found_key, found_value)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class PlainTableReader: public TableReader {
|
||||
|
||||
Status Get(const ReadOptions&, const Slice& key, void* arg,
|
||||
bool (*result_handler)(void* arg, const ParsedInternalKey& k,
|
||||
const Slice& v, bool),
|
||||
const Slice& v),
|
||||
void (*mark_key_may_exist)(void*) = nullptr);
|
||||
|
||||
uint64_t ApproximateOffsetOf(const Slice& key);
|
||||
|
||||
@@ -55,8 +55,8 @@ class TableReader {
|
||||
// Calls (*result_handler)(handle_context, ...) repeatedly, starting with
|
||||
// the entry found after a call to Seek(key), until result_handler returns
|
||||
// false, where k is the actual internal key for a row found and v as the
|
||||
// value of the key. didIO is true if I/O is involved in the operation. May
|
||||
// not make such a call if filter policy says that key is not present.
|
||||
// value of the key. May not make such a call if filter policy says that key
|
||||
// is not present.
|
||||
//
|
||||
// mark_key_may_exist_handler needs to be called when it is configured to be
|
||||
// memory only and the key is not found in the block cache, with
|
||||
@@ -67,7 +67,7 @@ class TableReader {
|
||||
virtual Status Get(
|
||||
const ReadOptions& readOptions, const Slice& key, void* handle_context,
|
||||
bool (*result_handler)(void* arg, const ParsedInternalKey& k,
|
||||
const Slice& v, bool didIO),
|
||||
const Slice& v),
|
||||
void (*mark_key_may_exist_handler)(void* handle_context) = nullptr) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ static std::string MakeKey(int i, int j, bool through_db) {
|
||||
}
|
||||
|
||||
static bool DummySaveValue(void* arg, const ParsedInternalKey& ikey,
|
||||
const Slice& v, bool didIO) {
|
||||
const Slice& v) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -718,7 +718,8 @@ class Harness {
|
||||
only_support_prefix_seek_ = false;
|
||||
options_.prefix_extractor = nullptr;
|
||||
options_.allow_mmap_reads = true;
|
||||
options_.table_factory.reset(NewTotalOrderPlainTableFactory());
|
||||
options_.table_factory.reset(
|
||||
NewPlainTableFactory(kPlainTableVariableLength, 0, 0));
|
||||
constructor_ = new TableConstructor(options_.comparator, true);
|
||||
internal_comparator_.reset(
|
||||
new InternalKeyComparator(options_.comparator));
|
||||
@@ -1481,6 +1482,14 @@ TEST(BlockBasedTableTest, BlockCacheLeak) {
|
||||
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);
|
||||
ASSERT_OK(c.Reopen(opt));
|
||||
table_reader = dynamic_cast<BlockBasedTable*>(c.table_reader());
|
||||
for (const std::string& key : keys) {
|
||||
ASSERT_TRUE(!table_reader->TEST_KeyInCache(ReadOptions(), key));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PlainTableTest, BasicPlainTableProperties) {
|
||||
|
||||
Vendored
+1
-1
@@ -419,7 +419,7 @@ private:
|
||||
Ch c = s.Take();
|
||||
if (c == '\\') { // Escape
|
||||
Ch e = s.Take();
|
||||
if ((sizeof(Ch) == 1 || e < 256) && escape[(unsigned char)e])
|
||||
if ((sizeof(Ch) == 1 || (int)e < 256) && escape[(unsigned char)e])
|
||||
RAPIDJSON_PUT(escape[(unsigned char)e]);
|
||||
else if (e == 'u') { // Unicode
|
||||
unsigned codepoint = ParseHex4(s);
|
||||
|
||||
@@ -81,7 +81,6 @@ def main(argv):
|
||||
--iterpercent=10
|
||||
--db=%s
|
||||
--max_key=100000000
|
||||
--disable_seek_compaction=%s
|
||||
--mmap_read=%s
|
||||
--block_size=16384
|
||||
--cache_size=1048576
|
||||
@@ -104,7 +103,6 @@ def main(argv):
|
||||
write_buf_size,
|
||||
dbname,
|
||||
random.randint(0, 1),
|
||||
random.randint(0, 1),
|
||||
random.randint(0, 1)))
|
||||
|
||||
child = subprocess.Popen([cmd],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user