mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
764bfcd19b | ||
|
|
1fdb34fe99 | ||
|
|
5d2b9971e8 | ||
|
|
e8830cab4b | ||
|
|
73d59f235e | ||
|
|
91b2c6d1f4 | ||
|
|
f445eadba6 |
No files matched your search
@@ -1,4 +1,7 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.17.4 (12/05/2018)
|
||||
### Bug Fixes
|
||||
* Fix a deadlock caused by compaction and file ingestion waiting for each other in the event of write stalls.
|
||||
|
||||
# 5.17.2 (10/24/2018)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -53,11 +53,13 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
if [ -n "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_5xx" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/6e8e715624fd15256a7970073387793dfcf79b46/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/ef37e1faa1c29782abfac1ae65a291b9b7966f6d/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c67031f0f739ac61575a061518d6ef5038f99f90/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/60d6f124a78798b73944f5ba87c2306ae3460153/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/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/907b498203d297947f3bb70b9466f47e100f1873/r131/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/3ee276cbacfad3074e3f07bf826ac47f06970f4e/1.3.5/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/9c910d36d6235cc40e8ff559358f1833452300ca/master/platform007/5b0f53e
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/92ff90349e2f43ea0a8246d8b1cf17b6869013e3/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -0,0 +1,157 @@
|
||||
#!/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_platform007.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"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
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 numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_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 $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
@@ -85,8 +85,9 @@ NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
LITE="OPT=\"-DROCKSDB_LITE -g\""
|
||||
TSAN="COMPILE_WITH_TSAN=1"
|
||||
# in gcc-5 there are known problems with TSAN like https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71090.
|
||||
# using platform007 gives us gcc-8 or higher which has that bug fixed.
|
||||
TSAN="ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007=1 COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
TSAN_CRASH='CRASH_TEST_EXT_ARGS="--compression_type=zstd --log2_keys_per_lock=22"'
|
||||
NON_TSAN_CRASH="CRASH_TEST_EXT_ARGS=--compression_type=zstd"
|
||||
|
||||
@@ -53,6 +53,45 @@ function get_lib_base()
|
||||
log_variable $__res_var
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# platform007 dependencies #
|
||||
###########################################################
|
||||
|
||||
OUTPUT="$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/7.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 7.x platform007
|
||||
get_lib_base glibc 2.26 platform007
|
||||
get_lib_base snappy LATEST platform007
|
||||
get_lib_base zlib LATEST platform007
|
||||
get_lib_base bzip2 LATEST platform007
|
||||
get_lib_base lz4 LATEST platform007
|
||||
get_lib_base zstd LATEST platform007
|
||||
get_lib_base gflags LATEST platform007
|
||||
get_lib_base jemalloc LATEST platform007
|
||||
get_lib_base numa LATEST platform007
|
||||
get_lib_base libunwind LATEST platform007
|
||||
get_lib_base tbb LATEST platform007
|
||||
|
||||
get_lib_base kernel-headers fb platform007
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base valgrind LATEST platform007
|
||||
get_lib_base lua 5.3.4 platform007
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
###########################################################
|
||||
# 5.x dependencies #
|
||||
###########################################################
|
||||
|
||||
@@ -4005,6 +4005,83 @@ TEST_F(DBCompactionTest, PartialManualCompaction) {
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// FixFileIngestionCompactionDeadlock tests and verifies that compaction and
|
||||
// file ingestion do not cause deadlock in the event of write stall triggered
|
||||
// by number of L0 files reaching level0_stop_writes_trigger.
|
||||
TEST_P(DBCompactionTestWithParam, FixFileIngestionCompactionDeadlock) {
|
||||
const int kNumKeysPerFile = 100;
|
||||
// Generate SST files.
|
||||
Options options = CurrentOptions();
|
||||
|
||||
// Generate an external SST file containing a single key, i.e. 99
|
||||
std::string sst_files_dir = dbname_ + "/sst_files/";
|
||||
test::DestroyDir(env_, sst_files_dir);
|
||||
ASSERT_OK(env_->CreateDir(sst_files_dir));
|
||||
SstFileWriter sst_writer(EnvOptions(), options);
|
||||
const std::string sst_file_path = sst_files_dir + "test.sst";
|
||||
ASSERT_OK(sst_writer.Open(sst_file_path));
|
||||
ASSERT_OK(sst_writer.Put(Key(kNumKeysPerFile - 1), "value"));
|
||||
ASSERT_OK(sst_writer.Finish());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::IngestExternalFile:AfterIncIngestFileCounter",
|
||||
"BackgroundCallCompaction:0"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options.write_buffer_size = 110 << 10; // 110KB
|
||||
options.level0_file_num_compaction_trigger =
|
||||
options.level0_stop_writes_trigger;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
|
||||
// Generate level0_stop_writes_trigger L0 files to trigger write stop
|
||||
for (int i = 0; i != options.level0_file_num_compaction_trigger; ++i) {
|
||||
for (int j = 0; j != kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 990)));
|
||||
}
|
||||
if (0 == i) {
|
||||
// When we reach here, the memtables have kNumKeysPerFile keys. Note that
|
||||
// flush is not yet triggered. We need to write an extra key so that the
|
||||
// write path will call PreprocessWrite and flush the previous key-value
|
||||
// pairs to e flushed. After that, there will be the newest key in the
|
||||
// memtable, and a bunch of L0 files. Since there is already one key in
|
||||
// the memtable, then for i = 1, 2, ..., we do not have to write this
|
||||
// extra key to trigger flush.
|
||||
ASSERT_OK(Put("", ""));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0 /*level*/, 0 /*cf*/), i + 1);
|
||||
}
|
||||
// When we reach this point, there will be level0_stop_writes_trigger L0
|
||||
// files and one extra key (99) in memory, which overlaps with the external
|
||||
// SST file. Write stall triggers, and can be cleared only after compaction
|
||||
// reduces the number of L0 files.
|
||||
|
||||
// Compaction will also be triggered since we have reached the threshold for
|
||||
// auto compaction. Note that compaction may begin after the following file
|
||||
// ingestion thread and waits for ingestion to finish.
|
||||
|
||||
// Thread to ingest file with overlapping key range with the current
|
||||
// memtable. Consequently ingestion will trigger a flush. The flush MUST
|
||||
// proceed without waiting for the write stall condition to clear, otherwise
|
||||
// deadlock can happen.
|
||||
port::Thread ingestion_thr([&]() {
|
||||
IngestExternalFileOptions ifo;
|
||||
Status s = db_->IngestExternalFile({sst_file_path}, ifo);
|
||||
ASSERT_OK(s);
|
||||
});
|
||||
|
||||
// More write to trigger write stop
|
||||
ingestion_thr.join();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
Close();
|
||||
}
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
+4
-1
@@ -3059,6 +3059,7 @@ Status DBImpl::IngestExternalFile(
|
||||
}
|
||||
|
||||
num_running_ingest_file_++;
|
||||
TEST_SYNC_POINT("DBImpl::IngestExternalFile:AfterIncIngestFileCounter");
|
||||
|
||||
// We cannot ingest a file into a dropped CF
|
||||
if (cfd->IsDropped()) {
|
||||
@@ -3074,7 +3075,9 @@ Status DBImpl::IngestExternalFile(
|
||||
&need_flush);
|
||||
if (status.ok() && need_flush) {
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(),
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
status = FlushMemTable(cfd, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
mutex_.Lock();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#define ROCKSDB_MAJOR 5
|
||||
#define ROCKSDB_MINOR 17
|
||||
#define ROCKSDB_PATCH 2
|
||||
#define ROCKSDB_PATCH 4
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
@@ -197,6 +197,27 @@ Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FaultInjectionTestEnv::ReopenWritableFile(
|
||||
const std::string& fname, unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& soptions) {
|
||||
if (!IsFilesystemActive()) {
|
||||
return GetError();
|
||||
}
|
||||
Status s = target()->ReopenWritableFile(fname, result, soptions);
|
||||
if (s.ok()) {
|
||||
result->reset(new TestWritableFile(fname, std::move(*result), this));
|
||||
// WritableFileWriter* file is opened
|
||||
// again then it will be truncated - so forget our saved state.
|
||||
UntrackFile(fname);
|
||||
MutexLock l(&mutex_);
|
||||
open_files_.insert(fname);
|
||||
auto dir_and_name = GetDirAndName(fname);
|
||||
auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
|
||||
list.insert(dir_and_name.second);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FaultInjectionTestEnv::NewRandomAccessFile(
|
||||
const std::string& fname, std::unique_ptr<RandomAccessFile>* result,
|
||||
const EnvOptions& soptions) {
|
||||
|
||||
@@ -110,6 +110,10 @@ class FaultInjectionTestEnv : public EnvWrapper {
|
||||
unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& soptions) override;
|
||||
|
||||
Status ReopenWritableFile(const std::string& fname,
|
||||
unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& soptions) override;
|
||||
|
||||
Status NewRandomAccessFile(const std::string& fname,
|
||||
std::unique_ptr<RandomAccessFile>* result,
|
||||
const EnvOptions& soptions) override;
|
||||
|
||||
@@ -404,82 +404,91 @@ std::shared_ptr<BlobFile> BlobDBImpl::FindBlobFileLocked(
|
||||
return (b1 || b2) ? nullptr : (*finditr);
|
||||
}
|
||||
|
||||
std::shared_ptr<Writer> BlobDBImpl::CheckOrCreateWriterLocked(
|
||||
const std::shared_ptr<BlobFile>& bfile) {
|
||||
std::shared_ptr<Writer> writer = bfile->GetWriter();
|
||||
if (writer) return writer;
|
||||
|
||||
Status s = CreateWriterLocked(bfile);
|
||||
if (!s.ok()) return nullptr;
|
||||
|
||||
writer = bfile->GetWriter();
|
||||
return writer;
|
||||
Status BlobDBImpl::CheckOrCreateWriterLocked(
|
||||
const std::shared_ptr<BlobFile>& blob_file,
|
||||
std::shared_ptr<Writer>* writer) {
|
||||
assert(writer != nullptr);
|
||||
*writer = blob_file->GetWriter();
|
||||
if (*writer != nullptr) {
|
||||
return Status::OK();
|
||||
}
|
||||
Status s = CreateWriterLocked(blob_file);
|
||||
if (s.ok()) {
|
||||
*writer = blob_file->GetWriter();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::shared_ptr<BlobFile> BlobDBImpl::SelectBlobFile() {
|
||||
Status BlobDBImpl::SelectBlobFile(std::shared_ptr<BlobFile>* blob_file) {
|
||||
assert(blob_file != nullptr);
|
||||
{
|
||||
ReadLock rl(&mutex_);
|
||||
if (open_non_ttl_file_ != nullptr) {
|
||||
return open_non_ttl_file_;
|
||||
*blob_file = open_non_ttl_file_;
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK again
|
||||
WriteLock wl(&mutex_);
|
||||
if (open_non_ttl_file_ != nullptr) {
|
||||
return open_non_ttl_file_;
|
||||
*blob_file = open_non_ttl_file_;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::shared_ptr<BlobFile> bfile = NewBlobFile("SelectBlobFile");
|
||||
assert(bfile);
|
||||
*blob_file = NewBlobFile("SelectBlobFile");
|
||||
assert(*blob_file != nullptr);
|
||||
|
||||
// file not visible, hence no lock
|
||||
std::shared_ptr<Writer> writer = CheckOrCreateWriterLocked(bfile);
|
||||
if (!writer) {
|
||||
std::shared_ptr<Writer> writer;
|
||||
Status s = CheckOrCreateWriterLocked(*blob_file, &writer);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"Failed to get writer from blob file: %s",
|
||||
bfile->PathName().c_str());
|
||||
return nullptr;
|
||||
"Failed to get writer from blob file: %s, error: %s",
|
||||
(*blob_file)->PathName().c_str(), s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
bfile->file_size_ = BlobLogHeader::kSize;
|
||||
bfile->header_.compression = bdb_options_.compression;
|
||||
bfile->header_.has_ttl = false;
|
||||
bfile->header_.column_family_id =
|
||||
(*blob_file)->file_size_ = BlobLogHeader::kSize;
|
||||
(*blob_file)->header_.compression = bdb_options_.compression;
|
||||
(*blob_file)->header_.has_ttl = false;
|
||||
(*blob_file)->header_.column_family_id =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(DefaultColumnFamily())->GetID();
|
||||
bfile->header_valid_ = true;
|
||||
bfile->SetColumnFamilyId(bfile->header_.column_family_id);
|
||||
bfile->SetHasTTL(false);
|
||||
bfile->SetCompression(bdb_options_.compression);
|
||||
(*blob_file)->header_valid_ = true;
|
||||
(*blob_file)->SetColumnFamilyId((*blob_file)->header_.column_family_id);
|
||||
(*blob_file)->SetHasTTL(false);
|
||||
(*blob_file)->SetCompression(bdb_options_.compression);
|
||||
|
||||
Status s = writer->WriteHeader(bfile->header_);
|
||||
s = writer->WriteHeader((*blob_file)->header_);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"Failed to write header to new blob file: %s"
|
||||
" status: '%s'",
|
||||
bfile->PathName().c_str(), s.ToString().c_str());
|
||||
return nullptr;
|
||||
(*blob_file)->PathName().c_str(), s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
blob_files_.insert(std::make_pair(bfile->BlobFileNumber(), bfile));
|
||||
open_non_ttl_file_ = bfile;
|
||||
blob_files_.insert(
|
||||
std::make_pair((*blob_file)->BlobFileNumber(), *blob_file));
|
||||
open_non_ttl_file_ = *blob_file;
|
||||
total_blob_size_ += BlobLogHeader::kSize;
|
||||
return bfile;
|
||||
return s;
|
||||
}
|
||||
|
||||
std::shared_ptr<BlobFile> BlobDBImpl::SelectBlobFileTTL(uint64_t expiration) {
|
||||
Status BlobDBImpl::SelectBlobFileTTL(uint64_t expiration,
|
||||
std::shared_ptr<BlobFile>* blob_file) {
|
||||
assert(blob_file != nullptr);
|
||||
assert(expiration != kNoExpiration);
|
||||
uint64_t epoch_read = 0;
|
||||
std::shared_ptr<BlobFile> bfile;
|
||||
{
|
||||
ReadLock rl(&mutex_);
|
||||
bfile = FindBlobFileLocked(expiration);
|
||||
*blob_file = FindBlobFileLocked(expiration);
|
||||
epoch_read = epoch_of_.load();
|
||||
}
|
||||
|
||||
if (bfile) {
|
||||
assert(!bfile->Immutable());
|
||||
return bfile;
|
||||
if (*blob_file != nullptr) {
|
||||
assert(!(*blob_file)->Immutable());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
uint64_t exp_low =
|
||||
@@ -487,61 +496,66 @@ std::shared_ptr<BlobFile> BlobDBImpl::SelectBlobFileTTL(uint64_t expiration) {
|
||||
uint64_t exp_high = exp_low + bdb_options_.ttl_range_secs;
|
||||
ExpirationRange expiration_range = std::make_pair(exp_low, exp_high);
|
||||
|
||||
bfile = NewBlobFile("SelectBlobFileTTL");
|
||||
assert(bfile);
|
||||
*blob_file = NewBlobFile("SelectBlobFileTTL");
|
||||
assert(*blob_file != nullptr);
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log, "New blob file TTL range: %s %d %d",
|
||||
bfile->PathName().c_str(), exp_low, exp_high);
|
||||
(*blob_file)->PathName().c_str(), exp_low, exp_high);
|
||||
LogFlush(db_options_.info_log);
|
||||
|
||||
// we don't need to take lock as no other thread is seeing bfile yet
|
||||
std::shared_ptr<Writer> writer = CheckOrCreateWriterLocked(bfile);
|
||||
if (!writer) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"Failed to get writer from blob file with TTL: %s",
|
||||
bfile->PathName().c_str());
|
||||
return nullptr;
|
||||
std::shared_ptr<Writer> writer;
|
||||
Status s = CheckOrCreateWriterLocked(*blob_file, &writer);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(
|
||||
db_options_.info_log,
|
||||
"Failed to get writer from blob file with TTL: %s, error: %s",
|
||||
(*blob_file)->PathName().c_str(), s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
bfile->header_.expiration_range = expiration_range;
|
||||
bfile->header_.compression = bdb_options_.compression;
|
||||
bfile->header_.has_ttl = true;
|
||||
bfile->header_.column_family_id =
|
||||
(*blob_file)->header_.expiration_range = expiration_range;
|
||||
(*blob_file)->header_.compression = bdb_options_.compression;
|
||||
(*blob_file)->header_.has_ttl = true;
|
||||
(*blob_file)->header_.column_family_id =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(DefaultColumnFamily())->GetID();
|
||||
;
|
||||
bfile->header_valid_ = true;
|
||||
bfile->SetColumnFamilyId(bfile->header_.column_family_id);
|
||||
bfile->SetHasTTL(true);
|
||||
bfile->SetCompression(bdb_options_.compression);
|
||||
bfile->file_size_ = BlobLogHeader::kSize;
|
||||
(*blob_file)->header_valid_ = true;
|
||||
(*blob_file)->SetColumnFamilyId((*blob_file)->header_.column_family_id);
|
||||
(*blob_file)->SetHasTTL(true);
|
||||
(*blob_file)->SetCompression(bdb_options_.compression);
|
||||
(*blob_file)->file_size_ = BlobLogHeader::kSize;
|
||||
|
||||
// set the first value of the range, since that is
|
||||
// concrete at this time. also necessary to add to open_ttl_files_
|
||||
bfile->expiration_range_ = expiration_range;
|
||||
(*blob_file)->expiration_range_ = expiration_range;
|
||||
|
||||
WriteLock wl(&mutex_);
|
||||
// in case the epoch has shifted in the interim, then check
|
||||
// check condition again - should be rare.
|
||||
if (epoch_of_.load() != epoch_read) {
|
||||
auto bfile2 = FindBlobFileLocked(expiration);
|
||||
if (bfile2) return bfile2;
|
||||
std::shared_ptr<BlobFile> blob_file2 = FindBlobFileLocked(expiration);
|
||||
if (blob_file2 != nullptr) {
|
||||
*blob_file = std::move(blob_file2);
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
Status s = writer->WriteHeader(bfile->header_);
|
||||
s = writer->WriteHeader((*blob_file)->header_);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"Failed to write header to new blob file: %s"
|
||||
" status: '%s'",
|
||||
bfile->PathName().c_str(), s.ToString().c_str());
|
||||
return nullptr;
|
||||
(*blob_file)->PathName().c_str(), s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
blob_files_.insert(std::make_pair(bfile->BlobFileNumber(), bfile));
|
||||
open_ttl_files_.insert(bfile);
|
||||
blob_files_.insert(
|
||||
std::make_pair((*blob_file)->BlobFileNumber(), *blob_file));
|
||||
open_ttl_files_.insert(*blob_file);
|
||||
total_blob_size_ += BlobLogHeader::kSize;
|
||||
epoch_of_++;
|
||||
|
||||
return bfile;
|
||||
return s;
|
||||
}
|
||||
|
||||
class BlobDBImpl::BlobInserter : public WriteBatch::Handler {
|
||||
@@ -695,36 +709,41 @@ Status BlobDBImpl::PutBlobValue(const WriteOptions& /*options*/,
|
||||
return s;
|
||||
}
|
||||
|
||||
std::shared_ptr<BlobFile> bfile = (expiration != kNoExpiration)
|
||||
? SelectBlobFileTTL(expiration)
|
||||
: SelectBlobFile();
|
||||
assert(bfile != nullptr);
|
||||
assert(bfile->compression() == bdb_options_.compression);
|
||||
|
||||
s = AppendBlob(bfile, headerbuf, key, value_compressed, expiration,
|
||||
&index_entry);
|
||||
if (expiration == kNoExpiration) {
|
||||
RecordTick(statistics_, BLOB_DB_WRITE_BLOB);
|
||||
std::shared_ptr<BlobFile> blob_file;
|
||||
if (expiration != kNoExpiration) {
|
||||
s = SelectBlobFileTTL(expiration, &blob_file);
|
||||
} else {
|
||||
RecordTick(statistics_, BLOB_DB_WRITE_BLOB_TTL);
|
||||
s = SelectBlobFile(&blob_file);
|
||||
}
|
||||
if (s.ok()) {
|
||||
assert(blob_file != nullptr);
|
||||
assert(blob_file->compression() == bdb_options_.compression);
|
||||
s = AppendBlob(blob_file, headerbuf, key, value_compressed, expiration,
|
||||
&index_entry);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
if (expiration != kNoExpiration) {
|
||||
bfile->ExtendExpirationRange(expiration);
|
||||
blob_file->ExtendExpirationRange(expiration);
|
||||
}
|
||||
s = CloseBlobFileIfNeeded(bfile);
|
||||
if (s.ok()) {
|
||||
s = WriteBatchInternal::PutBlobIndex(batch, column_family_id, key,
|
||||
index_entry);
|
||||
s = CloseBlobFileIfNeeded(blob_file);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = WriteBatchInternal::PutBlobIndex(batch, column_family_id, key,
|
||||
index_entry);
|
||||
}
|
||||
if (s.ok()) {
|
||||
if (expiration == kNoExpiration) {
|
||||
RecordTick(statistics_, BLOB_DB_WRITE_BLOB);
|
||||
} else {
|
||||
RecordTick(statistics_, BLOB_DB_WRITE_BLOB_TTL);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"Failed to append blob to FILE: %s: KEY: %s VALSZ: %d"
|
||||
" status: '%s' blob_file: '%s'",
|
||||
bfile->PathName().c_str(), key.ToString().c_str(),
|
||||
blob_file->PathName().c_str(), key.ToString().c_str(),
|
||||
value.size(), s.ToString().c_str(),
|
||||
bfile->DumpState().c_str());
|
||||
blob_file->DumpState().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -867,9 +886,10 @@ Status BlobDBImpl::AppendBlob(const std::shared_ptr<BlobFile>& bfile,
|
||||
uint64_t key_offset = 0;
|
||||
{
|
||||
WriteLock lockbfile_w(&bfile->mutex_);
|
||||
std::shared_ptr<Writer> writer = CheckOrCreateWriterLocked(bfile);
|
||||
if (!writer) {
|
||||
return Status::IOError("Failed to create blob writer");
|
||||
std::shared_ptr<Writer> writer;
|
||||
s = CheckOrCreateWriterLocked(bfile, &writer);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// write the blob to the blob log.
|
||||
@@ -1575,7 +1595,13 @@ Status BlobDBImpl::GCFileAndUpdateLSM(const std::shared_ptr<BlobFile>& bfptr,
|
||||
reason += bfptr->PathName();
|
||||
newfile = NewBlobFile(reason);
|
||||
|
||||
new_writer = CheckOrCreateWriterLocked(newfile);
|
||||
s = CheckOrCreateWriterLocked(newfile, &new_writer);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"Failed to open file %s for writer, error: %s",
|
||||
newfile->PathName().c_str(), s.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
// Can't use header beyond this point
|
||||
newfile->header_ = std::move(header);
|
||||
newfile->header_valid_ = true;
|
||||
|
||||
@@ -255,10 +255,11 @@ class BlobDBImpl : public BlobDB {
|
||||
|
||||
// find an existing blob log file based on the expiration unix epoch
|
||||
// if such a file does not exist, return nullptr
|
||||
std::shared_ptr<BlobFile> SelectBlobFileTTL(uint64_t expiration);
|
||||
Status SelectBlobFileTTL(uint64_t expiration,
|
||||
std::shared_ptr<BlobFile>* blob_file);
|
||||
|
||||
// find an existing blob log file to append the value to
|
||||
std::shared_ptr<BlobFile> SelectBlobFile();
|
||||
Status SelectBlobFile(std::shared_ptr<BlobFile>* blob_file);
|
||||
|
||||
std::shared_ptr<BlobFile> FindBlobFileLocked(uint64_t expiration) const;
|
||||
|
||||
@@ -309,8 +310,8 @@ class BlobDBImpl : public BlobDB {
|
||||
|
||||
// returns a Writer object for the file. If writer is not
|
||||
// already present, creates one. Needs Write Mutex to be held
|
||||
std::shared_ptr<Writer> CheckOrCreateWriterLocked(
|
||||
const std::shared_ptr<BlobFile>& bfile);
|
||||
Status CheckOrCreateWriterLocked(const std::shared_ptr<BlobFile>& blob_file,
|
||||
std::shared_ptr<Writer>* writer);
|
||||
|
||||
// Iterate through keys and values on Blob and write into
|
||||
// separate file the remaining blobs and delete/update pointers
|
||||
|
||||
@@ -374,6 +374,19 @@ TEST_F(BlobDBTest, GetIOError) {
|
||||
fault_injection_env_->SetFilesystemActive(true);
|
||||
}
|
||||
|
||||
TEST_F(BlobDBTest, PutIOError) {
|
||||
Options options;
|
||||
options.env = fault_injection_env_.get();
|
||||
BlobDBOptions bdb_options;
|
||||
bdb_options.min_blob_size = 0; // Make sure value write to blob file
|
||||
bdb_options.disable_background_tasks = true;
|
||||
Open(bdb_options, options);
|
||||
fault_injection_env_->SetFilesystemActive(false, Status::IOError());
|
||||
ASSERT_TRUE(Put("foo", "v1").IsIOError());
|
||||
fault_injection_env_->SetFilesystemActive(true, Status::IOError());
|
||||
ASSERT_OK(Put("bar", "v1"));
|
||||
}
|
||||
|
||||
TEST_F(BlobDBTest, WriteBatch) {
|
||||
Random rnd(301);
|
||||
BlobDBOptions bdb_options;
|
||||
|
||||
Reference in new issue
Block a user