mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Compare commits
14 Commits
release_fix
...
v7.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b6ca63ed0 | |||
| 9fe3a53443 | |||
| 22e011fe0a | |||
| bbae679ce7 | |||
| fe9e363ba1 | |||
| 3a4d73d337 | |||
| 4ab2fe77f3 | |||
| 596c606739 | |||
| a6fd3332e5 | |||
| f278e0e2d3 | |||
| 0344b1d331 | |||
| a4bf9311da | |||
| b251c4f5a9 | |||
| a12be569af |
+12
-28
@@ -43,7 +43,6 @@ env:
|
||||
- JOB_NAME=cmake-gcc9 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9-c++20 # 3-5 minutes
|
||||
- JOB_NAME=cmake-mingw # 3 minutes
|
||||
- JOB_NAME=status_checked
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
@@ -58,10 +57,6 @@ matrix:
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
compiler: clang
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x # Temporary while working through gcc-7 + assembler issue
|
||||
env: TEST_GROUP=platform_dependent
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: arm64
|
||||
@@ -190,18 +185,6 @@ matrix:
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=status_checked
|
||||
|
||||
install:
|
||||
- CC=gcc-7 && CXX=g++-7
|
||||
@@ -247,21 +230,25 @@ before_script:
|
||||
script:
|
||||
- date; ${CXX} --version
|
||||
- if [ `command -v ccache` ]; then ccache -C; fi
|
||||
- export MK_PARALLEL=4;
|
||||
if [[ "$TRAVIS_CPU_ARCH" == s390x ]]; then
|
||||
export MK_PARALLEL=1;
|
||||
fi
|
||||
- case $TEST_GROUP in
|
||||
platform_dependent)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=only make -j4 all_but_some_tests check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=only make -j$MK_PARALLEL all_but_some_tests check_some
|
||||
;;
|
||||
1)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_END=backupable_db_test make -j4 check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_END=backupable_db_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
2)
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 make -j4 tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=backupable_db_test ROCKSDBTESTS_END=db_universal_compaction_test make -j4 check_some
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 make -j$MK_PARALLEL tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=backupable_db_test ROCKSDBTESTS_END=db_universal_compaction_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
3)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=db_universal_compaction_test ROCKSDBTESTS_END=table_properties_collector_test make -j4 check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=db_universal_compaction_test ROCKSDBTESTS_END=table_properties_collector_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
4)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=table_properties_collector_test make -j4 check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=table_properties_collector_test make -j$MK_PARALLEL check_some
|
||||
;;
|
||||
esac
|
||||
- case $JOB_NAME in
|
||||
@@ -269,10 +256,10 @@ script:
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' LIB_MODE=shared V=1 make -j4 all
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' LIB_MODE=shared V=1 make -j$MK_PARALLEL all
|
||||
;;
|
||||
examples)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 make -j4 static_lib && cd examples && make -j4
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 make -j$MK_PARALLEL static_lib && cd examples && make -j$MK_PARALLEL
|
||||
;;
|
||||
cmake-mingw)
|
||||
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix;
|
||||
@@ -285,10 +272,7 @@ script:
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=0 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 .. && make -j4 && cd .. && rm -rf build && mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
status_checked)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ASSERT_STATUS_CHECKED=1 make -j4 check_some
|
||||
mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=0 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 .. && make -j$MK_PARALLEL && cd .. && rm -rf build && mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j$MK_PARALLEL rocksdb rocksdbjni
|
||||
;;
|
||||
esac
|
||||
notifications:
|
||||
|
||||
+8
-6
@@ -321,7 +321,8 @@ if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
if (NOT PORTABLE OR FORCE_SSE42)
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
@@ -333,11 +334,12 @@ int main() {
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check if -latomic is required or not
|
||||
|
||||
+10
-1
@@ -1,5 +1,14 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 7.0.2 (03/12/2022)
|
||||
* Fixed a bug that DisableManualCompaction may assert when disable an unscheduled manual compaction.
|
||||
|
||||
## 7.0.1 (03/02/2022)
|
||||
### Bug Fixes
|
||||
* Fix a race condition when cancel manual compaction with `DisableManualCompaction`. Also DB close can cancel the manual compaction thread.
|
||||
* Fixed a data race on `versions_` between `DBImpl::ResumeImpl()` and threads waiting for recovery to complete (#9496)
|
||||
* Fixed a bug caused by race among flush, incoming writes and taking snapshots. Queries to snapshots created with these race condition can return incorrect result, e.g. resurfacing deleted data.
|
||||
|
||||
## 7.0.0 (02/20/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a major bug in which batched MultiGet could return old values for keys deleted by DeleteRange when memtable Bloom filter is enabled (memtable_prefix_bloom_size_ratio > 0). (The fix includes a substantial MultiGet performance improvement in the unusual case of both memtable_whole_key_filtering and prefix_extractor.)
|
||||
* Fixed more cases of EventListener::OnTableFileCreated called with OK status, file_size==0, and no SST file kept. Now the status is Aborted.
|
||||
|
||||
@@ -329,8 +329,8 @@ ifneq ($(MACHINE), arm64)
|
||||
# linking with jemalloc (as it won't be arm64-compatible) and remove some other options
|
||||
# set during platform detection
|
||||
DISABLE_JEMALLOC=1
|
||||
PLATFORM_CFLAGS := $(filter-out -march=native -DHAVE_SSE42, $(PLATFORM_CFLAGS))
|
||||
PLATFORM_CXXFLAGS := $(filter-out -march=native -DHAVE_SSE42, $(PLATFORM_CXXFLAGS))
|
||||
PLATFORM_CFLAGS := $(filter-out -march=native -DHAVE_SSE42 -DHAVE_AVX2, $(PLATFORM_CFLAGS))
|
||||
PLATFORM_CXXFLAGS := $(filter-out -march=native -DHAVE_SSE42 -DHAVE_AVX2, $(PLATFORM_CXXFLAGS))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -2116,7 +2116,9 @@ CURL_SSL_OPTS ?= --tlsv1
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
ifeq (,$(findstring librocksdbjni-osx,$(ROCKSDBJNILIB)))
|
||||
ifeq ($(MACHINE),arm64)
|
||||
ROCKSDBJNILIB = librocksdbjni-osx-aarch64.jnilib
|
||||
ROCKSDBJNILIB = librocksdbjni-osx-arm64.jnilib
|
||||
else ifeq ($(MACHINE),x86_64)
|
||||
ROCKSDBJNILIB = librocksdbjni-osx-x86_64.jnilib
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-osx.jnilib
|
||||
endif
|
||||
@@ -2247,15 +2249,20 @@ endif
|
||||
$(MAKE) rocksdbjavastatic_deps
|
||||
$(MAKE) rocksdbjavastatic_libobjects
|
||||
$(MAKE) rocksdbjavastatic_javalib
|
||||
$(MAKE) rocksdbjavastatic_jar
|
||||
$(MAKE) rocksdbjava_jar
|
||||
|
||||
rocksdbjavastaticosx: rocksdbjavastaticosx_archs
|
||||
mv java/target/librocksdbjni-osx-x86_64.jnilib java/target/librocksdbjni-osx.jnilib
|
||||
mv java/target/librocksdbjni-osx-arm64.jnilib java/target/librocksdbjni-osx-aarch64.jnilib
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR) librocksdbjni-osx-x86_64.jnilib librocksdbjni-osx-arm64.jnilib
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
|
||||
rocksdbjavastaticosx_ub: rocksdbjavastaticosx_archs
|
||||
lipo -create -output ./java/target/$(ROCKSDBJNILIB) java/target/librocksdbjni-osx-x86_64.jnilib java/target/librocksdbjni-osx-arm64.jnilib
|
||||
$(MAKE) rocksdbjavastatic_jar
|
||||
cd java/target; lipo -create -output librocksdbjni-osx.jnilib librocksdbjni-osx-x86_64.jnilib librocksdbjni-osx-arm64.jnilib
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR) librocksdbjni-osx.jnilib
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
|
||||
rocksdbjavastaticosx_archs:
|
||||
$(MAKE) rocksdbjavastaticosx_arch_x86_64
|
||||
@@ -2289,28 +2296,32 @@ rocksdbjavastatic_javalib:
|
||||
strip $(STRIPFLAGS) $(ROCKSDBJNILIB); \
|
||||
fi
|
||||
|
||||
rocksdbjavastatic_jar:
|
||||
rocksdbjava_jar:
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
cd java/target/apidocs; $(JAR_CMD) -cf ../$(ROCKSDB_JAVADOCS_JAR) *
|
||||
cd java/src/main/java; $(JAR_CMD) -cf ../../../target/$(ROCKSDB_SOURCES_JAR) org
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
|
||||
rocksdbjava_javadocs_jar:
|
||||
cd java/target/apidocs; $(JAR_CMD) -cf ../$(ROCKSDB_JAVADOCS_JAR) *
|
||||
openssl sha1 java/target/$(ROCKSDB_JAVADOCS_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAVADOCS_JAR).sha1
|
||||
|
||||
rocksdbjava_sources_jar:
|
||||
cd java/src/main/java; $(JAR_CMD) -cf ../../../target/$(ROCKSDB_SOURCES_JAR) org
|
||||
openssl sha1 java/target/$(ROCKSDB_SOURCES_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_SOURCES_JAR).sha1
|
||||
|
||||
rocksdbjavastatic_deps: $(JAVA_COMPRESSIONS)
|
||||
|
||||
rocksdbjavastatic_libobjects: $(LIB_OBJECTS)
|
||||
|
||||
rocksdbjavastaticrelease: rocksdbjavastaticosx
|
||||
rocksdbjavastaticrelease: rocksdbjavastaticosx rocksdbjava_javadocs_jar rocksdbjava_sources_jar
|
||||
cd java/crossbuild && (vagrant destroy -f || true) && vagrant up linux32 && vagrant halt linux32 && vagrant up linux64 && vagrant halt linux64 && vagrant up linux64-musl && vagrant halt linux64-musl
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR_ALL) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR_ALL).sha1
|
||||
|
||||
rocksdbjavastaticreleasedocker: rocksdbjavastaticosx rocksdbjavastaticdockerx86 rocksdbjavastaticdockerx86_64 rocksdbjavastaticdockerx86musl rocksdbjavastaticdockerx86_64musl
|
||||
rocksdbjavastaticreleasedocker: rocksdbjavastaticosx rocksdbjavastaticdockerx86 rocksdbjavastaticdockerx86_64 rocksdbjavastaticdockerx86musl rocksdbjavastaticdockerx86_64musl rocksdbjava_javadocs_jar rocksdbjava_sources_jar
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
|
||||
@@ -632,7 +632,7 @@ if test "0$PORTABLE" -eq 0; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ \
|
||||
-fsyntax-only -march=native - -o /dev/null 2>/dev/null; then
|
||||
-march=native - -o /dev/null 2>/dev/null; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
|
||||
+165
-2
@@ -6889,6 +6889,112 @@ TEST_F(DBCompactionTest, FIFOWarm) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
const int kNumL0Files = 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// Generate 2 levels of file to make sure the manual compaction is not skipped
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), "value"));
|
||||
if (i % 2) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), "value"));
|
||||
if (i % 2) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
// Block compaction queue
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
|
||||
port::Thread compact_thread1([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
std::string begin_str = Key(0);
|
||||
std::string end_str = Key(3);
|
||||
Slice b = begin_str;
|
||||
Slice e = end_str;
|
||||
auto s = db_->CompactRange(cro, &b, &e);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
port::Thread compact_thread2([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
std::string begin_str = Key(4);
|
||||
std::string end_str = Key(7);
|
||||
Slice b = begin_str;
|
||||
Slice e = end_str;
|
||||
auto s = db_->CompactRange(cro, &b, &e);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
// Disable manual compaction should cancel both manual compactions and both
|
||||
// compaction should return incomplete.
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread1.join();
|
||||
compact_thread2.join();
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// generate files, but avoid trigger auto compaction
|
||||
for (int i = 0; i < kNumL0Files / 2; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
// make sure the manual compaction background is started but not yet set the
|
||||
// status to in_progress, then cancel the manual compaction, which should not
|
||||
// result in segfault
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkCompaction",
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"PreDisableManualCompaction"},
|
||||
{"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"ManualCompactionReturn",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
port::Thread compact_thread([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = true;
|
||||
auto s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"ManualCompactionReturn");
|
||||
});
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"PreDisableManualCompaction");
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFull) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
@@ -6951,7 +7057,7 @@ TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFullDBClose) {
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::RunManualCompaction:Scheduled",
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFull:"
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
|
||||
"PreDisableManualCompaction"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -6979,7 +7085,7 @@ TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFullDBClose) {
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFull:"
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
|
||||
"PreDisableManualCompaction");
|
||||
|
||||
// Generate more files to trigger auto compaction which is scheduled after
|
||||
@@ -7006,6 +7112,63 @@ TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFullDBClose) {
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DBCloseWithManualCompaction) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::RunManualCompaction:Scheduled",
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
|
||||
"PreDisableManualCompaction"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// Block compaction queue
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
|
||||
// generate files, but avoid trigger auto compaction
|
||||
for (int i = 0; i < kNumL0Files / 2; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
port::Thread compact_thread([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = true;
|
||||
auto s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
|
||||
"PreDisableManualCompaction");
|
||||
|
||||
// Generate more files to trigger auto compaction which is scheduled after
|
||||
// manual compaction. Has to generate 4 more files because existing files are
|
||||
// pending compaction
|
||||
for (int i = 0; i < kNumL0Files; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(ToString(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
|
||||
// Close DB with manual compaction and auto triggered compaction in the queue.
|
||||
auto s = db_->Close();
|
||||
ASSERT_OK(s);
|
||||
|
||||
// manual compaction thread should return with Incomplete().
|
||||
compact_thread.join();
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest,
|
||||
DisableManualCompactionDoesNotWaitForDrainingAutomaticCompaction) {
|
||||
// When `CompactRangeOptions::exclusive_manual_compaction == true`, we wait
|
||||
|
||||
@@ -676,6 +676,7 @@ class TestFlushListener : public EventListener {
|
||||
~TestFlushListener() override {
|
||||
prev_fc_info_.status.PermitUncheckedError(); // Ignore the status
|
||||
}
|
||||
|
||||
void OnTableFileCreated(const TableFileCreationInfo& info) override {
|
||||
// remember the info for later checking the FlushJobInfo.
|
||||
prev_fc_info_ = info;
|
||||
@@ -1999,6 +2000,61 @@ TEST_P(DBFlushTestBlobError, FlushError) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBFlushTest, TombstoneVisibleInSnapshot) {
|
||||
class SimpleTestFlushListener : public EventListener {
|
||||
public:
|
||||
explicit SimpleTestFlushListener(DBFlushTest* _test) : test_(_test) {}
|
||||
~SimpleTestFlushListener() override {}
|
||||
|
||||
void OnFlushBegin(DB* db, const FlushJobInfo& info) override {
|
||||
ASSERT_EQ(static_cast<uint32_t>(0), info.cf_id);
|
||||
|
||||
ASSERT_OK(db->Delete(WriteOptions(), "foo"));
|
||||
snapshot_ = db->GetSnapshot();
|
||||
ASSERT_OK(db->Put(WriteOptions(), "foo", "value"));
|
||||
|
||||
auto* dbimpl = static_cast_with_check<DBImpl>(db);
|
||||
assert(dbimpl);
|
||||
|
||||
ColumnFamilyHandle* cfh = db->DefaultColumnFamily();
|
||||
auto* cfhi = static_cast_with_check<ColumnFamilyHandleImpl>(cfh);
|
||||
assert(cfhi);
|
||||
ASSERT_OK(dbimpl->TEST_SwitchMemtable(cfhi->cfd()));
|
||||
}
|
||||
|
||||
DBFlushTest* test_ = nullptr;
|
||||
const Snapshot* snapshot_ = nullptr;
|
||||
};
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
auto* listener = new SimpleTestFlushListener(this);
|
||||
options.listeners.emplace_back(listener);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value0"));
|
||||
|
||||
ManagedSnapshot snapshot_guard(db_);
|
||||
|
||||
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), default_cf));
|
||||
|
||||
const Snapshot* snapshot = listener->snapshot_;
|
||||
assert(snapshot);
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.snapshot = snapshot;
|
||||
|
||||
// Using snapshot should not see "foo".
|
||||
{
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, "foo", &value);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
@@ -171,7 +171,9 @@ Status CompactedDBImpl::Open(const Options& options,
|
||||
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
||||
Status s = db->Init(options);
|
||||
if (s.ok()) {
|
||||
db->StartPeriodicWorkScheduler();
|
||||
s = db->StartPeriodicWorkScheduler();
|
||||
}
|
||||
if (s.ok()) {
|
||||
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
|
||||
+38
-16
@@ -400,14 +400,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
|
||||
JobContext job_context(0);
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
if (s.ok()) {
|
||||
s = error_handler_.ClearBGError();
|
||||
} else {
|
||||
// NOTE: this is needed to pass ASSERT_STATUS_CHECKED
|
||||
// in the DBSSTTest.DBWithMaxSpaceAllowedRandomized test.
|
||||
// See https://github.com/facebook/rocksdb/pull/7715#issuecomment-754947952
|
||||
error_handler_.GetRecoveryError().PermitUncheckedError();
|
||||
}
|
||||
mutex_.Unlock();
|
||||
|
||||
job_context.manifest_file_number = 1;
|
||||
@@ -428,11 +420,31 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
immutable_db_options_.info_log,
|
||||
"DB resume requested but could not enable file deletions [%s]",
|
||||
s.ToString().c_str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Successfully resumed DB");
|
||||
}
|
||||
|
||||
mutex_.Lock();
|
||||
if (s.ok()) {
|
||||
// This will notify and unblock threads waiting for error recovery to
|
||||
// finish. Those previouly waiting threads can now proceed, which may
|
||||
// include closing the db.
|
||||
s = error_handler_.ClearBGError();
|
||||
} else {
|
||||
// NOTE: this is needed to pass ASSERT_STATUS_CHECKED
|
||||
// in the DBSSTTest.DBWithMaxSpaceAllowedRandomized test.
|
||||
// See https://github.com/facebook/rocksdb/pull/7715#issuecomment-754947952
|
||||
error_handler_.GetRecoveryError().PermitUncheckedError();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Successfully resumed DB");
|
||||
} else {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Failed to resume DB [%s]",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
|
||||
// Check for shutdown again before scheduling further compactions,
|
||||
// since we released and re-acquired the lock above
|
||||
if (shutdown_initiated_) {
|
||||
@@ -527,10 +539,19 @@ Status DBImpl::CloseHelper() {
|
||||
// marker. After this we do a variant of the waiting and unschedule work
|
||||
// (to consider: moving all the waiting into CancelAllBackgroundWork(true))
|
||||
CancelAllBackgroundWork(false);
|
||||
|
||||
// Cancel manual compaction if there's any
|
||||
if (HasPendingManualCompaction()) {
|
||||
DisableManualCompaction();
|
||||
}
|
||||
mutex_.Lock();
|
||||
env_->UnSchedule(this, Env::Priority::BOTTOM);
|
||||
env_->UnSchedule(this, Env::Priority::LOW);
|
||||
env_->UnSchedule(this, Env::Priority::HIGH);
|
||||
// Unschedule all tasks for this DB
|
||||
for (uint8_t i = 0; i < static_cast<uint8_t>(TaskType::kCount); i++) {
|
||||
env_->UnSchedule(GetTaskTag(i), Env::Priority::BOTTOM);
|
||||
env_->UnSchedule(GetTaskTag(i), Env::Priority::LOW);
|
||||
env_->UnSchedule(GetTaskTag(i), Env::Priority::HIGH);
|
||||
}
|
||||
|
||||
Status ret = Status::OK();
|
||||
|
||||
// Wait for background work to finish
|
||||
@@ -749,7 +770,7 @@ void DBImpl::PrintStatistics() {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::StartPeriodicWorkScheduler() {
|
||||
Status DBImpl::StartPeriodicWorkScheduler() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -759,7 +780,7 @@ void DBImpl::StartPeriodicWorkScheduler() {
|
||||
"DBImpl::StartPeriodicWorkScheduler:DisableScheduler",
|
||||
&disable_scheduler);
|
||||
if (disable_scheduler) {
|
||||
return;
|
||||
return Status::OK();
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
|
||||
@@ -770,10 +791,11 @@ void DBImpl::StartPeriodicWorkScheduler() {
|
||||
&periodic_work_scheduler_);
|
||||
}
|
||||
|
||||
periodic_work_scheduler_->Register(
|
||||
return periodic_work_scheduler_->Register(
|
||||
this, mutable_db_options_.stats_dump_period_sec,
|
||||
mutable_db_options_.stats_persist_period_sec);
|
||||
#endif // !ROCKSDB_LITE
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// esitmate the total size of stats_history_
|
||||
@@ -1207,7 +1229,7 @@ Status DBImpl::SetDBOptions(
|
||||
mutable_db_options_.stats_persist_period_sec) {
|
||||
mutex_.Unlock();
|
||||
periodic_work_scheduler_->Unregister(this);
|
||||
periodic_work_scheduler_->Register(
|
||||
s = periodic_work_scheduler_->Register(
|
||||
this, new_options.stats_dump_period_sec,
|
||||
new_options.stats_persist_period_sec);
|
||||
mutex_.Lock();
|
||||
|
||||
+46
-12
@@ -1187,7 +1187,10 @@ class DBImpl : public DB {
|
||||
// WriteToWAL need different synchronization: log_empty_, alive_log_files_,
|
||||
// logs_, logfile_number_. Refer to the definition of each variable below for
|
||||
// more description.
|
||||
mutable InstrumentedMutex mutex_;
|
||||
//
|
||||
// `mutex_` can be a hot lock in some workloads, so it deserves dedicated
|
||||
// cachelines.
|
||||
mutable CacheAlignedInstrumentedMutex mutex_;
|
||||
|
||||
ColumnFamilyHandleImpl* default_cf_handle_;
|
||||
InternalStats* default_cf_internal_stats_;
|
||||
@@ -1511,19 +1514,31 @@ class DBImpl : public DB {
|
||||
|
||||
// Information for a manual compaction
|
||||
struct ManualCompactionState {
|
||||
ManualCompactionState(ColumnFamilyData* _cfd, int _input_level,
|
||||
int _output_level, uint32_t _output_path_id,
|
||||
bool _exclusive, bool _disallow_trivial_move,
|
||||
std::atomic<bool>* _canceled)
|
||||
: cfd(_cfd),
|
||||
input_level(_input_level),
|
||||
output_level(_output_level),
|
||||
output_path_id(_output_path_id),
|
||||
exclusive(_exclusive),
|
||||
disallow_trivial_move(_disallow_trivial_move),
|
||||
canceled(_canceled) {}
|
||||
|
||||
ColumnFamilyData* cfd;
|
||||
int input_level;
|
||||
int output_level;
|
||||
uint32_t output_path_id;
|
||||
Status status;
|
||||
bool done;
|
||||
bool in_progress; // compaction request being processed?
|
||||
bool incomplete; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin; // nullptr means beginning of key range
|
||||
const InternalKey* end; // nullptr means end of key range
|
||||
InternalKey* manual_end; // how far we are compacting
|
||||
bool done = false;
|
||||
bool in_progress = false; // compaction request being processed?
|
||||
bool incomplete = false; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin = nullptr; // nullptr means beginning of key range
|
||||
const InternalKey* end = nullptr; // nullptr means end of key range
|
||||
InternalKey* manual_end = nullptr; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
std::atomic<bool>* canceled; // Compaction canceled by the user?
|
||||
@@ -1533,10 +1548,10 @@ class DBImpl : public DB {
|
||||
Compaction* compaction;
|
||||
// caller retains ownership of `manual_compaction_state` as it is reused
|
||||
// across background compactions.
|
||||
ManualCompactionState* manual_compaction_state; // nullptr if non-manual
|
||||
std::shared_ptr<ManualCompactionState>
|
||||
manual_compaction_state; // nullptr if non-manual
|
||||
// task limiter token is requested during compaction picking.
|
||||
std::unique_ptr<TaskLimiterToken> task_token;
|
||||
bool is_canceled = false;
|
||||
};
|
||||
|
||||
struct CompactionArg {
|
||||
@@ -1731,6 +1746,25 @@ class DBImpl : public DB {
|
||||
}
|
||||
}
|
||||
|
||||
// TaskType is used to identify tasks in thread-pool, currently only
|
||||
// differentiate manual compaction, which could be unscheduled from the
|
||||
// thread-pool.
|
||||
enum class TaskType : uint8_t {
|
||||
kDefault = 0,
|
||||
kManualCompaction = 1,
|
||||
kCount = 2,
|
||||
};
|
||||
|
||||
// Task tag is used to identity tasks in thread-pool, which is
|
||||
// dbImpl obj address + type
|
||||
inline void* GetTaskTag(TaskType type) {
|
||||
return GetTaskTag(static_cast<uint8_t>(type));
|
||||
}
|
||||
|
||||
inline void* GetTaskTag(uint8_t type) {
|
||||
return static_cast<uint8_t*>(static_cast<void*>(this)) + type;
|
||||
}
|
||||
|
||||
// REQUIRES: mutex locked and in write thread.
|
||||
void AssignAtomicFlushSeq(const autovector<ColumnFamilyData*>& cfds);
|
||||
|
||||
@@ -1845,7 +1879,7 @@ class DBImpl : public DB {
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Schedule background tasks
|
||||
void StartPeriodicWorkScheduler();
|
||||
Status StartPeriodicWorkScheduler();
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
|
||||
@@ -170,6 +170,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
const bool needs_to_sync_closed_wals =
|
||||
logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1;
|
||||
|
||||
// If needs_to_sync_closed_wals is true, we need to record the current
|
||||
// maximum memtable ID of this column family so that a later PickMemtables()
|
||||
// call will not pick memtables whose IDs are higher. This is due to the fact
|
||||
@@ -177,9 +178,33 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// happen for this column family in the meantime. The newly created memtables
|
||||
// have their data backed by unsynced WALs, thus they cannot be included in
|
||||
// this flush job.
|
||||
// Another reason why we must record the current maximum memtable ID of this
|
||||
// column family: SyncClosedLogs() may release db mutex, thus it's possible
|
||||
// for application to continue to insert into memtables increasing db's
|
||||
// sequence number. The application may take a snapshot, but this snapshot is
|
||||
// not included in `snapshot_seqs` which will be passed to flush job because
|
||||
// `snapshot_seqs` has already been computed before this function starts.
|
||||
// Recording the max memtable ID ensures that the flush job does not flush
|
||||
// a memtable without knowing such snapshot(s).
|
||||
uint64_t max_memtable_id = needs_to_sync_closed_wals
|
||||
? cfd->imm()->GetLatestMemTableID()
|
||||
: port::kMaxUint64;
|
||||
|
||||
// If needs_to_sync_closed_wals is false, then the flush job will pick ALL
|
||||
// existing memtables of the column family when PickMemTable() is called
|
||||
// later. Although we won't call SyncClosedLogs() in this case, we may still
|
||||
// call the callbacks of the listeners, i.e. NotifyOnFlushBegin() which also
|
||||
// releases and re-acquires the db mutex. In the meantime, the application
|
||||
// can still insert into the memtables and increase the db's sequence number.
|
||||
// The application can take a snapshot, hoping that the latest visible state
|
||||
// to this snapshto is preserved. This is hard to guarantee since db mutex
|
||||
// not held. This newly-created snapshot is not included in `snapshot_seqs`
|
||||
// and the flush job is unaware of its presence. Consequently, the flush job
|
||||
// may drop certain keys when generating the L0, causing incorrect data to be
|
||||
// returned for snapshot read using this snapshot.
|
||||
// To address this, we make sure NotifyOnFlushBegin() executes after memtable
|
||||
// picking so that no new snapshot can be taken between the two functions.
|
||||
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options, max_memtable_id,
|
||||
file_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
||||
@@ -192,11 +217,6 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
&blob_callback_);
|
||||
FileMetaData file_meta;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status s;
|
||||
bool need_cancel = false;
|
||||
IOStatus log_io_s = IOStatus::OK();
|
||||
@@ -221,6 +241,12 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables", &flush_job);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
bool switched_to_mempurge = false;
|
||||
// Within flush_job.Run, rocksdb may call event listener to notify
|
||||
// file creation and deletion.
|
||||
@@ -1763,35 +1789,29 @@ Status DBImpl::RunManualCompaction(
|
||||
CompactionArg* ca = nullptr;
|
||||
|
||||
bool scheduled = false;
|
||||
Env::Priority thread_pool_priority = Env::Priority::TOTAL;
|
||||
bool manual_conflict = false;
|
||||
ManualCompactionState manual;
|
||||
manual.cfd = cfd;
|
||||
manual.input_level = input_level;
|
||||
manual.output_level = output_level;
|
||||
manual.output_path_id = compact_range_options.target_path_id;
|
||||
manual.done = false;
|
||||
manual.in_progress = false;
|
||||
manual.incomplete = false;
|
||||
manual.exclusive = exclusive;
|
||||
manual.disallow_trivial_move = disallow_trivial_move;
|
||||
manual.canceled = compact_range_options.canceled;
|
||||
|
||||
auto manual = std::make_shared<ManualCompactionState>(
|
||||
cfd, input_level, output_level, compact_range_options.target_path_id,
|
||||
exclusive, disallow_trivial_move, compact_range_options.canceled);
|
||||
// For universal compaction, we enforce every manual compaction to compact
|
||||
// all files.
|
||||
if (begin == nullptr ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
manual.begin = nullptr;
|
||||
manual->begin = nullptr;
|
||||
} else {
|
||||
begin_storage.SetMinPossibleForUserKey(*begin);
|
||||
manual.begin = &begin_storage;
|
||||
manual->begin = &begin_storage;
|
||||
}
|
||||
if (end == nullptr ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
manual.end = nullptr;
|
||||
manual->end = nullptr;
|
||||
} else {
|
||||
end_storage.SetMaxPossibleForUserKey(*end);
|
||||
manual.end = &end_storage;
|
||||
manual->end = &end_storage;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:0");
|
||||
@@ -1803,10 +1823,10 @@ Status DBImpl::RunManualCompaction(
|
||||
// `DisableManualCompaction()` just waited for the manual compaction queue
|
||||
// to drain. So return immediately.
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:PausedAtStart");
|
||||
manual.status =
|
||||
manual->status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
manual.done = true;
|
||||
return manual.status;
|
||||
manual->done = true;
|
||||
return manual->status;
|
||||
}
|
||||
|
||||
// When a manual compaction arrives, temporarily disable scheduling of
|
||||
@@ -1826,7 +1846,7 @@ Status DBImpl::RunManualCompaction(
|
||||
// However, only one of them will actually schedule compaction, while
|
||||
// others will wait on a condition variable until it completes.
|
||||
|
||||
AddManualCompaction(&manual);
|
||||
AddManualCompaction(manual.get());
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::RunManualCompaction:NotScheduled", &mutex_);
|
||||
if (exclusive) {
|
||||
// Limitation: there's no way to wake up the below loop when user sets
|
||||
@@ -1835,11 +1855,11 @@ Status DBImpl::RunManualCompaction(
|
||||
while (bg_bottom_compaction_scheduled_ > 0 ||
|
||||
bg_compaction_scheduled_ > 0) {
|
||||
if (manual_compaction_paused_ > 0 ||
|
||||
(manual.canceled != nullptr && *manual.canceled == true)) {
|
||||
(manual->canceled != nullptr && *manual->canceled == true)) {
|
||||
// Pretend the error came from compaction so the below cleanup/error
|
||||
// handling code can process it.
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
manual->done = true;
|
||||
manual->status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
break;
|
||||
}
|
||||
@@ -1861,68 +1881,80 @@ Status DBImpl::RunManualCompaction(
|
||||
// We don't check bg_error_ here, because if we get the error in compaction,
|
||||
// the compaction will set manual.status to bg_error_ and set manual.done to
|
||||
// true.
|
||||
while (!manual.done) {
|
||||
while (!manual->done) {
|
||||
assert(HasPendingManualCompaction());
|
||||
manual_conflict = false;
|
||||
Compaction* compaction = nullptr;
|
||||
if (ShouldntRunManualCompaction(&manual) || (manual.in_progress == true) ||
|
||||
scheduled ||
|
||||
(((manual.manual_end = &manual.tmp_storage1) != nullptr) &&
|
||||
((compaction = manual.cfd->CompactRange(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
manual.input_level, manual.output_level, compact_range_options,
|
||||
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
||||
max_file_num_to_ignore)) == nullptr &&
|
||||
if (ShouldntRunManualCompaction(manual.get()) ||
|
||||
(manual->in_progress == true) || scheduled ||
|
||||
(((manual->manual_end = &manual->tmp_storage1) != nullptr) &&
|
||||
((compaction = manual->cfd->CompactRange(
|
||||
*manual->cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
manual->input_level, manual->output_level, compact_range_options,
|
||||
manual->begin, manual->end, &manual->manual_end,
|
||||
&manual_conflict, max_file_num_to_ignore)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
// exclusive manual compactions should not see a conflict during
|
||||
// CompactRange
|
||||
assert(!exclusive || !manual_conflict);
|
||||
// Running either this or some other manual compaction
|
||||
bg_cv_.Wait();
|
||||
if (manual_compaction_paused_ > 0 && !manual.done &&
|
||||
!manual.in_progress) {
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
if (manual_compaction_paused_ > 0) {
|
||||
manual->done = true;
|
||||
manual->status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
if (ca && ca->prepicked_compaction) {
|
||||
ca->prepicked_compaction->is_canceled = true;
|
||||
if (scheduled) {
|
||||
assert(thread_pool_priority != Env::Priority::TOTAL);
|
||||
auto unscheduled_task_num = env_->UnSchedule(
|
||||
GetTaskTag(TaskType::kManualCompaction), thread_pool_priority);
|
||||
if (unscheduled_task_num > 0) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"[%s] Unscheduled %d number of manual compactions from the "
|
||||
"thread-pool",
|
||||
cfd->GetName().c_str(), unscheduled_task_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (scheduled && manual.incomplete == true) {
|
||||
assert(!manual.in_progress);
|
||||
if (scheduled && manual->incomplete == true) {
|
||||
assert(!manual->in_progress);
|
||||
scheduled = false;
|
||||
manual.incomplete = false;
|
||||
manual->incomplete = false;
|
||||
}
|
||||
} else if (!scheduled) {
|
||||
if (compaction == nullptr) {
|
||||
manual.done = true;
|
||||
manual->done = true;
|
||||
bg_cv_.SignalAll();
|
||||
continue;
|
||||
}
|
||||
ca = new CompactionArg;
|
||||
ca->db = this;
|
||||
ca->prepicked_compaction = new PrepickedCompaction;
|
||||
ca->prepicked_compaction->manual_compaction_state = &manual;
|
||||
ca->prepicked_compaction->manual_compaction_state = manual;
|
||||
ca->prepicked_compaction->compaction = compaction;
|
||||
if (!RequestCompactionToken(
|
||||
cfd, true, &ca->prepicked_compaction->task_token, &log_buffer)) {
|
||||
// Don't throttle manual compaction, only count outstanding tasks.
|
||||
assert(false);
|
||||
}
|
||||
manual.incomplete = false;
|
||||
manual->incomplete = false;
|
||||
if (compaction->bottommost_level() &&
|
||||
env_->GetBackgroundThreads(Env::Priority::BOTTOM) > 0) {
|
||||
bg_bottom_compaction_scheduled_++;
|
||||
ca->compaction_pri_ = Env::Priority::BOTTOM;
|
||||
env_->Schedule(&DBImpl::BGWorkBottomCompaction, ca,
|
||||
Env::Priority::BOTTOM, this,
|
||||
Env::Priority::BOTTOM,
|
||||
GetTaskTag(TaskType::kManualCompaction),
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
thread_pool_priority = Env::Priority::BOTTOM;
|
||||
} else {
|
||||
bg_compaction_scheduled_++;
|
||||
ca->compaction_pri_ = Env::Priority::LOW;
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW, this,
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW,
|
||||
GetTaskTag(TaskType::kManualCompaction),
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
thread_pool_priority = Env::Priority::LOW;
|
||||
}
|
||||
scheduled = true;
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:Scheduled");
|
||||
@@ -1930,11 +1962,18 @@ Status DBImpl::RunManualCompaction(
|
||||
}
|
||||
|
||||
log_buffer.FlushBufferToLog();
|
||||
assert(!manual.in_progress);
|
||||
assert(!manual->in_progress);
|
||||
assert(HasPendingManualCompaction());
|
||||
RemoveManualCompaction(&manual);
|
||||
RemoveManualCompaction(manual.get());
|
||||
// if the manual job is unscheduled, try schedule other jobs in case there's
|
||||
// any unscheduled compaction job which was blocked by exclusive manual
|
||||
// compaction.
|
||||
if (manual->status.IsIncomplete() &&
|
||||
manual->status.subcode() == Status::SubCode::kManualCompactionPaused) {
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
bg_cv_.SignalAll();
|
||||
return manual.status;
|
||||
return manual->status;
|
||||
}
|
||||
|
||||
void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
@@ -2661,6 +2700,8 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
if (ca.prepicked_compaction != nullptr) {
|
||||
if (ca.prepicked_compaction->compaction != nullptr) {
|
||||
ca.prepicked_compaction->compaction->ReleaseCompactionFiles(
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused));
|
||||
delete ca.prepicked_compaction->compaction;
|
||||
}
|
||||
delete ca.prepicked_compaction;
|
||||
@@ -2851,106 +2892,93 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
Env::Priority bg_thread_pri) {
|
||||
bool made_progress = false;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:0");
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
if (prepicked_compaction && prepicked_compaction->is_canceled) {
|
||||
assert(prepicked_compaction->compaction);
|
||||
ROCKS_LOG_BUFFER(&log_buffer, "[%s] Skip canceled manual compaction job",
|
||||
prepicked_compaction->compaction->column_family_data()
|
||||
->GetName()
|
||||
.c_str());
|
||||
prepicked_compaction->compaction->ReleaseCompactionFiles(
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused));
|
||||
delete prepicked_compaction->compaction;
|
||||
} else {
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
// This call will unlock/lock the mutex to wait for current running
|
||||
// IngestExternalFile() calls to finish.
|
||||
WaitForIngestFile();
|
||||
// This call will unlock/lock the mutex to wait for current running
|
||||
// IngestExternalFile() calls to finish.
|
||||
WaitForIngestFile();
|
||||
|
||||
num_running_compactions_++;
|
||||
num_running_compactions_++;
|
||||
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>
|
||||
pending_outputs_inserted_elem(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>
|
||||
pending_outputs_inserted_elem(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
|
||||
assert((bg_thread_pri == Env::Priority::BOTTOM &&
|
||||
bg_bottom_compaction_scheduled_) ||
|
||||
(bg_thread_pri == Env::Priority::LOW && bg_compaction_scheduled_));
|
||||
Status s = BackgroundCompaction(&made_progress, &job_context, &log_buffer,
|
||||
prepicked_compaction, bg_thread_pri);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:1");
|
||||
if (s.IsBusy()) {
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
immutable_db_options_.clock->SleepForMicroseconds(
|
||||
10000); // prevent hot loop
|
||||
mutex_.Lock();
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() && !s.IsColumnFamilyDropped()) {
|
||||
// Wait a little bit before retrying background compaction in
|
||||
// case this is an environmental problem and we do not want to
|
||||
// chew up resources for failed compactions for the duration of
|
||||
// the problem.
|
||||
uint64_t error_cnt =
|
||||
default_cf_internal_stats_->BumpAndGetBackgroundErrorCount();
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
log_buffer.FlushBufferToLog();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Waiting after background compaction error: %s, "
|
||||
"Accumulated background error counts: %" PRIu64,
|
||||
s.ToString().c_str(), error_cnt);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
immutable_db_options_.clock->SleepForMicroseconds(1000000);
|
||||
mutex_.Lock();
|
||||
} else if (s.IsManualCompactionPaused()) {
|
||||
assert(prepicked_compaction);
|
||||
ManualCompactionState* m =
|
||||
prepicked_compaction->manual_compaction_state;
|
||||
assert(m);
|
||||
ROCKS_LOG_BUFFER(&log_buffer, "[%s] [JOB %d] Manual compaction paused",
|
||||
m->cfd->GetName().c_str(), job_context.job_id);
|
||||
}
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
// If compaction failed, we want to delete all temporary files that we
|
||||
// might have created (they might not be all recorded in job_context in
|
||||
// case of a failure). Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() &&
|
||||
!s.IsColumnFamilyDropped() &&
|
||||
!s.IsBusy());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:FoundObsoleteFiles");
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
// states of DB so info_log might not be available after that point.
|
||||
// It also applies to access other states that DB owns.
|
||||
log_buffer.FlushBufferToLog();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles");
|
||||
}
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
assert(num_running_compactions_ > 0);
|
||||
num_running_compactions_--;
|
||||
assert((bg_thread_pri == Env::Priority::BOTTOM &&
|
||||
bg_bottom_compaction_scheduled_) ||
|
||||
(bg_thread_pri == Env::Priority::LOW && bg_compaction_scheduled_));
|
||||
Status s = BackgroundCompaction(&made_progress, &job_context, &log_buffer,
|
||||
prepicked_compaction, bg_thread_pri);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:1");
|
||||
if (s.IsBusy()) {
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
immutable_db_options_.clock->SleepForMicroseconds(
|
||||
10000); // prevent hot loop
|
||||
mutex_.Lock();
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() && !s.IsColumnFamilyDropped()) {
|
||||
// Wait a little bit before retrying background compaction in
|
||||
// case this is an environmental problem and we do not want to
|
||||
// chew up resources for failed compactions for the duration of
|
||||
// the problem.
|
||||
uint64_t error_cnt =
|
||||
default_cf_internal_stats_->BumpAndGetBackgroundErrorCount();
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
log_buffer.FlushBufferToLog();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Waiting after background compaction error: %s, "
|
||||
"Accumulated background error counts: %" PRIu64,
|
||||
s.ToString().c_str(), error_cnt);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
immutable_db_options_.clock->SleepForMicroseconds(1000000);
|
||||
mutex_.Lock();
|
||||
} else if (s.IsManualCompactionPaused()) {
|
||||
assert(prepicked_compaction);
|
||||
auto m = prepicked_compaction->manual_compaction_state;
|
||||
assert(m);
|
||||
ROCKS_LOG_BUFFER(&log_buffer, "[%s] [JOB %d] Manual compaction paused",
|
||||
m->cfd->GetName().c_str(), job_context.job_id);
|
||||
}
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
// If compaction failed, we want to delete all temporary files that we
|
||||
// might have created (they might not be all recorded in job_context in
|
||||
// case of a failure). Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() &&
|
||||
!s.IsColumnFamilyDropped() &&
|
||||
!s.IsBusy());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:FoundObsoleteFiles");
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
// states of DB so info_log might not be available after that point.
|
||||
// It also applies to access other states that DB owns.
|
||||
log_buffer.FlushBufferToLog();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles");
|
||||
}
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
assert(num_running_compactions_ > 0);
|
||||
num_running_compactions_--;
|
||||
|
||||
if (bg_thread_pri == Env::Priority::LOW) {
|
||||
bg_compaction_scheduled_--;
|
||||
} else {
|
||||
@@ -2995,7 +3023,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
LogBuffer* log_buffer,
|
||||
PrepickedCompaction* prepicked_compaction,
|
||||
Env::Priority thread_pri) {
|
||||
ManualCompactionState* manual_compaction =
|
||||
std::shared_ptr<ManualCompactionState> manual_compaction =
|
||||
prepicked_compaction == nullptr
|
||||
? nullptr
|
||||
: prepicked_compaction->manual_compaction_state;
|
||||
@@ -3039,6 +3067,10 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
if (!status.ok()) {
|
||||
if (is_manual) {
|
||||
manual_compaction->status = status;
|
||||
manual_compaction->status
|
||||
.PermitUncheckedError(); // the manual compaction thread may exit
|
||||
// first, which won't be able to check the
|
||||
// status
|
||||
manual_compaction->done = true;
|
||||
manual_compaction->in_progress = false;
|
||||
manual_compaction = nullptr;
|
||||
@@ -3061,7 +3093,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// InternalKey* manual_end = &manual_end_storage;
|
||||
bool sfm_reserved_compact_space = false;
|
||||
if (is_manual) {
|
||||
ManualCompactionState* m = manual_compaction;
|
||||
auto m = manual_compaction;
|
||||
assert(m->in_progress);
|
||||
if (!c) {
|
||||
m->done = true;
|
||||
@@ -3445,7 +3477,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c.reset();
|
||||
|
||||
if (is_manual) {
|
||||
ManualCompactionState* m = manual_compaction;
|
||||
auto m = manual_compaction;
|
||||
if (!status.ok()) {
|
||||
m->status = status;
|
||||
m->done = true;
|
||||
|
||||
@@ -1889,8 +1889,9 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
persist_options_status.ToString().c_str());
|
||||
}
|
||||
if (s.ok()) {
|
||||
impl->StartPeriodicWorkScheduler();
|
||||
} else {
|
||||
s = impl->StartPeriodicWorkScheduler();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
for (auto* h : *handles) {
|
||||
delete h;
|
||||
}
|
||||
|
||||
@@ -16,31 +16,41 @@ PeriodicWorkScheduler::PeriodicWorkScheduler(
|
||||
timer = std::unique_ptr<Timer>(new Timer(clock.get()));
|
||||
}
|
||||
|
||||
void PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec) {
|
||||
Status PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec) {
|
||||
MutexLock l(&timer_mu_);
|
||||
static std::atomic<uint64_t> initial_delay(0);
|
||||
timer->Start();
|
||||
if (stats_dump_period_sec > 0) {
|
||||
timer->Add([dbi]() { dbi->DumpStats(); }, GetTaskName(dbi, "dump_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_dump_period_sec) *
|
||||
kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond);
|
||||
bool succeeded = timer->Add(
|
||||
[dbi]() { dbi->DumpStats(); }, GetTaskName(dbi, "dump_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task DumpStats");
|
||||
}
|
||||
}
|
||||
if (stats_persist_period_sec > 0) {
|
||||
timer->Add(
|
||||
bool succeeded = timer->Add(
|
||||
[dbi]() { dbi->PersistStats(); }, GetTaskName(dbi, "pst_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_persist_period_sec) * kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_persist_period_sec) * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task PersistStats");
|
||||
}
|
||||
}
|
||||
timer->Add([dbi]() { dbi->FlushInfoLog(); },
|
||||
GetTaskName(dbi, "flush_info_log"),
|
||||
initial_delay.fetch_add(1) % kDefaultFlushInfoLogPeriodSec *
|
||||
kMicrosInSecond,
|
||||
kDefaultFlushInfoLogPeriodSec * kMicrosInSecond);
|
||||
bool succeeded = timer->Add(
|
||||
[dbi]() { dbi->FlushInfoLog(); }, GetTaskName(dbi, "flush_info_log"),
|
||||
initial_delay.fetch_add(1) % kDefaultFlushInfoLogPeriodSec *
|
||||
kMicrosInSecond,
|
||||
kDefaultFlushInfoLogPeriodSec * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task PersistStats");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void PeriodicWorkScheduler::Unregister(DBImpl* dbi) {
|
||||
|
||||
@@ -30,8 +30,8 @@ class PeriodicWorkScheduler {
|
||||
PeriodicWorkScheduler& operator=(const PeriodicWorkScheduler&) = delete;
|
||||
PeriodicWorkScheduler& operator=(PeriodicWorkScheduler&&) = delete;
|
||||
|
||||
void Register(DBImpl* dbi, unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec);
|
||||
Status Register(DBImpl* dbi, unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec);
|
||||
|
||||
void Unregister(DBImpl* dbi);
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
#define ROCKSDB_MAJOR 6
|
||||
#define ROCKSDB_MINOR 29
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_MAJOR 7
|
||||
#define ROCKSDB_MINOR 0
|
||||
#define ROCKSDB_PATCH 2
|
||||
|
||||
// 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
|
||||
|
||||
@@ -18,7 +18,11 @@ public class NativeLibraryLoader {
|
||||
|
||||
private static final String sharedLibraryName = Environment.getSharedLibraryName("rocksdb");
|
||||
private static final String jniLibraryName = Environment.getJniLibraryName("rocksdb");
|
||||
private static final /* @Nullable */ String fallbackJniLibraryName =
|
||||
Environment.getFallbackJniLibraryName("rocksdb");
|
||||
private static final String jniLibraryFileName = Environment.getJniLibraryFileName("rocksdb");
|
||||
private static final /* @Nullable */ String fallbackJniLibraryFileName =
|
||||
Environment.getFallbackJniLibraryFileName("rocksdb");
|
||||
private static final String tempFilePrefix = "librocksdbjni";
|
||||
private static final String tempFileSuffix = Environment.getJniLibraryExtension();
|
||||
|
||||
@@ -49,14 +53,33 @@ public class NativeLibraryLoader {
|
||||
*/
|
||||
public synchronized void loadLibrary(final String tmpDir) throws IOException {
|
||||
try {
|
||||
System.loadLibrary(sharedLibraryName);
|
||||
} catch(final UnsatisfiedLinkError ule1) {
|
||||
// try dynamic library
|
||||
System.loadLibrary(sharedLibraryName);
|
||||
return;
|
||||
} catch (final UnsatisfiedLinkError ule) {
|
||||
// ignore - try from static library
|
||||
}
|
||||
|
||||
try {
|
||||
// try static library
|
||||
System.loadLibrary(jniLibraryName);
|
||||
return;
|
||||
} catch (final UnsatisfiedLinkError ule) {
|
||||
// ignore - then try static library fallback or from jar
|
||||
}
|
||||
|
||||
if (fallbackJniLibraryName != null) {
|
||||
try {
|
||||
System.loadLibrary(jniLibraryName);
|
||||
} catch(final UnsatisfiedLinkError ule2) {
|
||||
loadLibraryFromJar(tmpDir);
|
||||
// try static library fallback
|
||||
System.loadLibrary(fallbackJniLibraryName);
|
||||
return;
|
||||
} catch (final UnsatisfiedLinkError ule) {
|
||||
// ignore - then try from jar
|
||||
}
|
||||
}
|
||||
|
||||
// try jar
|
||||
loadLibraryFromJar(tmpDir);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,43 +106,62 @@ public class NativeLibraryLoader {
|
||||
|
||||
File loadLibraryFromJarToTemp(final String tmpDir)
|
||||
throws IOException {
|
||||
final File temp;
|
||||
if (tmpDir == null || tmpDir.isEmpty()) {
|
||||
temp = File.createTempFile(tempFilePrefix, tempFileSuffix);
|
||||
} else {
|
||||
final File parentDir = new File(tmpDir);
|
||||
if (!parentDir.exists()) {
|
||||
throw new RuntimeException(
|
||||
"Directory: " + parentDir.getAbsolutePath() + " does not exist!");
|
||||
}
|
||||
temp = new File(parentDir, jniLibraryFileName);
|
||||
if (temp.exists() && !temp.delete()) {
|
||||
throw new RuntimeException("File: " + temp.getAbsolutePath()
|
||||
+ " already exists and cannot be removed.");
|
||||
}
|
||||
if (!temp.createNewFile()) {
|
||||
throw new RuntimeException("File: " + temp.getAbsolutePath()
|
||||
+ " could not be created.");
|
||||
}
|
||||
}
|
||||
InputStream is = null;
|
||||
try {
|
||||
// attempt to look up the static library in the jar file
|
||||
String libraryFileName = jniLibraryFileName;
|
||||
is = getClass().getClassLoader().getResourceAsStream(libraryFileName);
|
||||
|
||||
if (!temp.exists()) {
|
||||
throw new RuntimeException("File " + temp.getAbsolutePath() + " does not exist.");
|
||||
} else {
|
||||
temp.deleteOnExit();
|
||||
}
|
||||
|
||||
// attempt to copy the library from the Jar file to the temp destination
|
||||
try (final InputStream is = getClass().getClassLoader().
|
||||
getResourceAsStream(jniLibraryFileName)) {
|
||||
if (is == null) {
|
||||
throw new RuntimeException(jniLibraryFileName + " was not found inside JAR.");
|
||||
// is there a fallback we can try
|
||||
if (fallbackJniLibraryFileName == null) {
|
||||
throw new RuntimeException(libraryFileName + " was not found inside JAR.");
|
||||
}
|
||||
|
||||
// attempt to look up the fallback static library in the jar file
|
||||
libraryFileName = fallbackJniLibraryFileName;
|
||||
is = getClass().getClassLoader().getResourceAsStream(libraryFileName);
|
||||
if (is == null) {
|
||||
throw new RuntimeException(libraryFileName + " was not found inside JAR.");
|
||||
}
|
||||
}
|
||||
|
||||
// create a temporary file to copy the library to
|
||||
final File temp;
|
||||
if (tmpDir == null || tmpDir.isEmpty()) {
|
||||
temp = File.createTempFile(tempFilePrefix, tempFileSuffix);
|
||||
} else {
|
||||
Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
final File parentDir = new File(tmpDir);
|
||||
if (!parentDir.exists()) {
|
||||
throw new RuntimeException(
|
||||
"Directory: " + parentDir.getAbsolutePath() + " does not exist!");
|
||||
}
|
||||
temp = new File(parentDir, libraryFileName);
|
||||
if (temp.exists() && !temp.delete()) {
|
||||
throw new RuntimeException(
|
||||
"File: " + temp.getAbsolutePath() + " already exists and cannot be removed.");
|
||||
}
|
||||
if (!temp.createNewFile()) {
|
||||
throw new RuntimeException("File: " + temp.getAbsolutePath() + " could not be created.");
|
||||
}
|
||||
}
|
||||
if (!temp.exists()) {
|
||||
throw new RuntimeException("File " + temp.getAbsolutePath() + " does not exist.");
|
||||
} else {
|
||||
temp.deleteOnExit();
|
||||
}
|
||||
|
||||
// copy the library from the Jar file to the temp destination
|
||||
Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// return the temporary library file
|
||||
return temp;
|
||||
|
||||
} finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,8 +110,14 @@ public class Environment {
|
||||
return String.format("%sjni-linux%s%s", name, arch, getLibcPostfix());
|
||||
}
|
||||
} else if (isMac()) {
|
||||
if (isAarch64()) {
|
||||
return String.format("%sjni-osx-%s", name, ARCH);
|
||||
if (is64Bit()) {
|
||||
final String arch;
|
||||
if (isAarch64()) {
|
||||
arch = "arm64";
|
||||
} else {
|
||||
arch = "x86_64";
|
||||
}
|
||||
return String.format("%sjni-osx-%s", name, arch);
|
||||
} else {
|
||||
return String.format("%sjni-osx", name);
|
||||
}
|
||||
@@ -131,10 +137,25 @@ public class Environment {
|
||||
throw new UnsupportedOperationException(String.format("Cannot determine JNI library name for ARCH='%s' OS='%s' name='%s'", ARCH, OS, name));
|
||||
}
|
||||
|
||||
public static /*@Nullable*/ String getFallbackJniLibraryName(final String name) {
|
||||
if (isMac() && is64Bit()) {
|
||||
return String.format("%sjni-osx", name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getJniLibraryFileName(final String name) {
|
||||
return appendLibOsSuffix("lib" + getJniLibraryName(name), false);
|
||||
}
|
||||
|
||||
public static /*@Nullable*/ String getFallbackJniLibraryFileName(final String name) {
|
||||
final String fallbackJniLibraryName = getFallbackJniLibraryName(name);
|
||||
if (fallbackJniLibraryName == null) {
|
||||
return null;
|
||||
}
|
||||
return appendLibOsSuffix("lib" + fallbackJniLibraryName, false);
|
||||
}
|
||||
|
||||
private static String appendLibOsSuffix(final String libraryFileName, final boolean shared) {
|
||||
if (isUnix() || isAix() || isSolaris() || isFreeBSD() || isOpenBSD()) {
|
||||
return libraryFileName + ".so";
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -37,18 +36,21 @@ public class EnvironmentTest {
|
||||
isEqualTo(".jnilib");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni-osx.jnilib");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni.dylib");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mac64() {
|
||||
setEnvironmentClassFields("mac", "x86-64");
|
||||
public void mac64_x86_64() {
|
||||
setEnvironmentClassFields("mac", "x86_64");
|
||||
assertThat(Environment.isWindows()).isFalse();
|
||||
assertThat(Environment.getJniLibraryExtension()).
|
||||
isEqualTo(".jnilib");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni-osx.jnilib");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb"))
|
||||
.isEqualTo("librocksdbjni-osx-x86_64.jnilib");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb"))
|
||||
.isEqualTo("librocksdbjni-osx.jnilib");
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni.dylib");
|
||||
}
|
||||
@@ -59,7 +61,9 @@ public class EnvironmentTest {
|
||||
assertThat(Environment.isWindows()).isFalse();
|
||||
assertThat(Environment.getJniLibraryExtension()).isEqualTo(".jnilib");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb"))
|
||||
.isEqualTo("librocksdbjni-osx-aarch64.jnilib");
|
||||
.isEqualTo("librocksdbjni-osx-arm64.jnilib");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb"))
|
||||
.isEqualTo("librocksdbjni-osx.jnilib");
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).isEqualTo("librocksdbjni.dylib");
|
||||
}
|
||||
|
||||
@@ -73,6 +77,7 @@ public class EnvironmentTest {
|
||||
isEqualTo(".so");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni-linux32.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni.so");
|
||||
// Linux musl-libc (Alpine)
|
||||
@@ -103,7 +108,8 @@ public class EnvironmentTest {
|
||||
assertThat(Environment.isWindows()).isFalse();
|
||||
assertThat(Environment.getJniLibraryExtension()).
|
||||
isEqualTo(".so");
|
||||
Environment.getJniLibraryFileName("rocksdb");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).isEqualTo("blah");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -115,6 +121,7 @@ public class EnvironmentTest {
|
||||
isEqualTo(".so");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni-linux64.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni.so");
|
||||
// Linux musl-libc (Alpine)
|
||||
@@ -124,6 +131,7 @@ public class EnvironmentTest {
|
||||
isEqualTo(".so");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni-linux64-musl.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni.so");
|
||||
// UNIX
|
||||
@@ -134,6 +142,7 @@ public class EnvironmentTest {
|
||||
isEqualTo(".so");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni-linux64.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni.so");
|
||||
// AIX
|
||||
@@ -143,6 +152,7 @@ public class EnvironmentTest {
|
||||
isEqualTo(".so");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni-aix64.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni.so");
|
||||
}
|
||||
@@ -161,6 +171,7 @@ public class EnvironmentTest {
|
||||
isEqualTo(".dll");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni-win64.dll");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).
|
||||
isEqualTo("librocksdbjni.dll");
|
||||
}
|
||||
@@ -177,6 +188,7 @@ public class EnvironmentTest {
|
||||
assertThat(Environment.getJniLibraryName("rocksdb")).isEqualTo("rocksdbjni-linux-ppc64le");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb"))
|
||||
.isEqualTo("librocksdbjni-linux-ppc64le.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).isEqualTo("librocksdbjni.so");
|
||||
// Linux musl-libc (Alpine)
|
||||
setEnvironmentClassField(MUSL_LIBC_FIELD_NAME, true);
|
||||
@@ -189,6 +201,7 @@ public class EnvironmentTest {
|
||||
assertThat(Environment.getJniLibraryName("rocksdb")).isEqualTo("rocksdbjni-linux-ppc64le-musl");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb"))
|
||||
.isEqualTo("librocksdbjni-linux-ppc64le-musl.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).isEqualTo("librocksdbjni.so");
|
||||
setEnvironmentClassField(MUSL_LIBC_FIELD_NAME, false);
|
||||
}
|
||||
@@ -205,6 +218,7 @@ public class EnvironmentTest {
|
||||
assertThat(Environment.getJniLibraryName("rocksdb")).isEqualTo("rocksdbjni-linux-aarch64");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb"))
|
||||
.isEqualTo("librocksdbjni-linux-aarch64.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).isEqualTo("librocksdbjni.so");
|
||||
// Linux musl-libc (Alpine)
|
||||
setEnvironmentClassField(MUSL_LIBC_FIELD_NAME, true);
|
||||
@@ -217,6 +231,7 @@ public class EnvironmentTest {
|
||||
assertThat(Environment.getJniLibraryName("rocksdb")).isEqualTo("rocksdbjni-linux-aarch64-musl");
|
||||
assertThat(Environment.getJniLibraryFileName("rocksdb"))
|
||||
.isEqualTo("librocksdbjni-linux-aarch64-musl.so");
|
||||
assertThat(Environment.getFallbackJniLibraryFileName("rocksdb")).isNull();
|
||||
assertThat(Environment.getSharedLibraryFileName("rocksdb")).isEqualTo("librocksdbjni.so");
|
||||
setEnvironmentClassField(MUSL_LIBC_FIELD_NAME, false);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,14 @@ class InstrumentedMutex {
|
||||
int stats_code_;
|
||||
};
|
||||
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) CacheAlignedInstrumentedMutex
|
||||
: public InstrumentedMutex {
|
||||
using InstrumentedMutex::InstrumentedMutex;
|
||||
char padding[(CACHE_LINE_SIZE - sizeof(InstrumentedMutex) % CACHE_LINE_SIZE) %
|
||||
CACHE_LINE_SIZE] ROCKSDB_FIELD_UNUSED;
|
||||
};
|
||||
static_assert(sizeof(CacheAlignedInstrumentedMutex) % CACHE_LINE_SIZE == 0);
|
||||
|
||||
// RAII wrapper for InstrumentedMutex
|
||||
class InstrumentedMutexLock {
|
||||
public:
|
||||
|
||||
@@ -3158,6 +3158,7 @@ Status BlockBasedTable::CreateIndexReader(
|
||||
uint64_t BlockBasedTable::ApproximateDataOffsetOf(
|
||||
const InternalIteratorBase<IndexValue>& index_iter,
|
||||
uint64_t data_size) const {
|
||||
assert(index_iter.status().ok());
|
||||
if (index_iter.Valid()) {
|
||||
BlockHandle handle = index_iter.value().handle;
|
||||
return handle.offset();
|
||||
@@ -3200,8 +3201,16 @@ uint64_t BlockBasedTable::ApproximateOffsetOf(const Slice& key,
|
||||
}
|
||||
|
||||
index_iter->Seek(key);
|
||||
uint64_t offset;
|
||||
if (index_iter->status().ok()) {
|
||||
offset = ApproximateDataOffsetOf(*index_iter, data_size);
|
||||
} else {
|
||||
// Split in half to avoid skewing one way or another,
|
||||
// since we don't know whether we're operating on lower bound or
|
||||
// upper bound.
|
||||
return rep_->file_size / 2;
|
||||
}
|
||||
|
||||
uint64_t offset = ApproximateDataOffsetOf(*index_iter, data_size);
|
||||
// Pro-rate file metadata (incl filters) size-proportionally across data
|
||||
// blocks.
|
||||
double size_ratio =
|
||||
@@ -3217,7 +3226,9 @@ uint64_t BlockBasedTable::ApproximateSize(const Slice& start, const Slice& end,
|
||||
uint64_t data_size = GetApproximateDataSize();
|
||||
if (UNLIKELY(data_size == 0)) {
|
||||
// Hmm. Assume whole file is involved, since we have lower and upper
|
||||
// bound.
|
||||
// bound. This likely skews the estimate if we consider that this function
|
||||
// is typically called with `[start, end]` fully contained in the file's
|
||||
// key-range.
|
||||
return rep_->file_size;
|
||||
}
|
||||
|
||||
@@ -3235,9 +3246,24 @@ uint64_t BlockBasedTable::ApproximateSize(const Slice& start, const Slice& end,
|
||||
}
|
||||
|
||||
index_iter->Seek(start);
|
||||
uint64_t start_offset = ApproximateDataOffsetOf(*index_iter, data_size);
|
||||
uint64_t start_offset;
|
||||
if (index_iter->status().ok()) {
|
||||
start_offset = ApproximateDataOffsetOf(*index_iter, data_size);
|
||||
} else {
|
||||
// Assume file is involved from the start. This likely skews the estimate
|
||||
// but is consistent with the above error handling.
|
||||
start_offset = 0;
|
||||
}
|
||||
|
||||
index_iter->Seek(end);
|
||||
uint64_t end_offset = ApproximateDataOffsetOf(*index_iter, data_size);
|
||||
uint64_t end_offset;
|
||||
if (index_iter->status().ok()) {
|
||||
end_offset = ApproximateDataOffsetOf(*index_iter, data_size);
|
||||
} else {
|
||||
// Assume file is involved until the end. This likely skews the estimate
|
||||
// but is consistent with the above error handling.
|
||||
end_offset = data_size;
|
||||
}
|
||||
|
||||
assert(end_offset >= start_offset);
|
||||
// Pro-rate file metadata (incl filters) size-proportionally across data
|
||||
|
||||
+5
-4
@@ -744,9 +744,6 @@ inline bool Zlib_Compress(const CompressionInfo& info,
|
||||
output_header_len = compression::PutDecompressedSizeInfo(
|
||||
output, static_cast<uint32_t>(length));
|
||||
}
|
||||
// Resize output to be the plain data length.
|
||||
// This may not be big enough if the compression actually expands data.
|
||||
output->resize(output_header_len + length);
|
||||
|
||||
// The memLevel parameter specifies how much memory should be allocated for
|
||||
// the internal compression state.
|
||||
@@ -780,12 +777,16 @@ inline bool Zlib_Compress(const CompressionInfo& info,
|
||||
}
|
||||
}
|
||||
|
||||
// Get an upper bound on the compressed size.
|
||||
size_t upper_bound = deflateBound(&_stream, length);
|
||||
output->resize(output_header_len + upper_bound);
|
||||
|
||||
// Compress the input, and put compressed data in output.
|
||||
_stream.next_in = (Bytef*)input;
|
||||
_stream.avail_in = static_cast<unsigned int>(length);
|
||||
|
||||
// Initialize the output size.
|
||||
_stream.avail_out = static_cast<unsigned int>(length);
|
||||
_stream.avail_out = static_cast<unsigned int>(upper_bound);
|
||||
_stream.next_out = reinterpret_cast<Bytef*>(&(*output)[output_header_len]);
|
||||
|
||||
bool compressed = false;
|
||||
|
||||
+15
-7
@@ -92,18 +92,25 @@ inline int CountTrailingZeroBits(T v) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_M_X64)
|
||||
// Not all MSVC compile settings will use `BitsSetToOneFallback()`. We include
|
||||
// the following code at coarse granularity for simpler macros. It's important
|
||||
// to exclude at least so our non-MSVC unit test coverage tool doesn't see it.
|
||||
#ifdef _MSC_VER
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
int BitsSetToOneFallback(T v) {
|
||||
const int kBits = static_cast<int>(sizeof(T)) * 8;
|
||||
static_assert((kBits & (kBits - 1)) == 0, "must be power of two bits");
|
||||
// we static_cast these bit patterns in order to truncate them to the correct
|
||||
// size
|
||||
// size. Warning C4309 dislikes this technique, so disable it here.
|
||||
#pragma warning(disable : 4309)
|
||||
v = static_cast<T>(v - ((v >> 1) & static_cast<T>(0x5555555555555555ull)));
|
||||
v = static_cast<T>((v & static_cast<T>(0x3333333333333333ull)) +
|
||||
((v >> 2) & static_cast<T>(0x3333333333333333ull)));
|
||||
v = static_cast<T>((v + (v >> 4)) & static_cast<T>(0x0F0F0F0F0F0F0F0Full));
|
||||
#pragma warning(default : 4309)
|
||||
for (int shift_bits = 8; shift_bits < kBits; shift_bits <<= 1) {
|
||||
v += static_cast<T>(v >> shift_bits);
|
||||
}
|
||||
@@ -113,7 +120,8 @@ int BitsSetToOneFallback(T v) {
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
#endif
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
// Number of bits set to 1. Also known as "population count".
|
||||
template <typename T>
|
||||
@@ -126,21 +134,21 @@ inline int BitsSetToOne(T v) {
|
||||
constexpr auto mm = 8 * sizeof(uint32_t) - 1;
|
||||
// The bit mask is to neutralize sign extension on small signed types
|
||||
constexpr uint32_t m = (uint32_t{1} << ((8 * sizeof(T)) & mm)) - 1;
|
||||
#if defined(_M_X64) || defined(_M_IX86)
|
||||
#if defined(HAVE_SSE42) && (defined(_M_X64) || defined(_M_IX86))
|
||||
return static_cast<int>(__popcnt(static_cast<uint32_t>(v) & m));
|
||||
#else
|
||||
return static_cast<int>(detail::BitsSetToOneFallback(v) & m);
|
||||
#endif
|
||||
} else if (sizeof(T) == sizeof(uint32_t)) {
|
||||
#if defined(_M_X64) || defined(_M_IX86)
|
||||
#if defined(HAVE_SSE42) && (defined(_M_X64) || defined(_M_IX86))
|
||||
return static_cast<int>(__popcnt(static_cast<uint32_t>(v)));
|
||||
#else
|
||||
return detail::BitsSetToOneFallback(static_cast<uint32_t>(v));
|
||||
#endif
|
||||
} else {
|
||||
#ifdef _M_X64
|
||||
#if defined(HAVE_SSE42) && defined(_M_X64)
|
||||
return static_cast<int>(__popcnt64(static_cast<uint64_t>(v)));
|
||||
#elif defined(_M_IX86)
|
||||
#elif defined(HAVE_SSE42) && defined(_M_IX86)
|
||||
return static_cast<int>(
|
||||
__popcnt(static_cast<uint32_t>(static_cast<uint64_t>(v) >> 32) +
|
||||
__popcnt(static_cast<uint32_t>(v))));
|
||||
|
||||
+40
-31
@@ -48,36 +48,38 @@ class Timer {
|
||||
~Timer() { Shutdown(); }
|
||||
|
||||
// Add a new function to run.
|
||||
// fn_name has to be identical, otherwise, the new one overrides the existing
|
||||
// one, regardless if the function is pending removed (invalid) or not.
|
||||
// fn_name has to be identical, otherwise it will fail to add and return false
|
||||
// start_after_us is the initial delay.
|
||||
// repeat_every_us is the interval between ending time of the last call and
|
||||
// starting time of the next call. For example, repeat_every_us = 2000 and
|
||||
// the function takes 1000us to run. If it starts at time [now]us, then it
|
||||
// finishes at [now]+1000us, 2nd run starting time will be at [now]+3000us.
|
||||
// repeat_every_us == 0 means do not repeat.
|
||||
void Add(std::function<void()> fn,
|
||||
const std::string& fn_name,
|
||||
uint64_t start_after_us,
|
||||
uint64_t repeat_every_us) {
|
||||
std::unique_ptr<FunctionInfo> fn_info(new FunctionInfo(
|
||||
std::move(fn), fn_name, clock_->NowMicros() + start_after_us,
|
||||
repeat_every_us));
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto it = map_.find(fn_name);
|
||||
if (it == map_.end()) {
|
||||
heap_.push(fn_info.get());
|
||||
map_.emplace(std::make_pair(fn_name, std::move(fn_info)));
|
||||
} else {
|
||||
// If it already exists, overriding it.
|
||||
it->second->fn = std::move(fn_info->fn);
|
||||
it->second->valid = true;
|
||||
it->second->next_run_time_us = clock_->NowMicros() + start_after_us;
|
||||
it->second->repeat_every_us = repeat_every_us;
|
||||
}
|
||||
bool Add(std::function<void()> fn, const std::string& fn_name,
|
||||
uint64_t start_after_us, uint64_t repeat_every_us) {
|
||||
auto fn_info = std::make_unique<FunctionInfo>(std::move(fn), fn_name, 0,
|
||||
repeat_every_us);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// Assign time within mutex to make sure the next_run_time is larger than
|
||||
// the current running one
|
||||
fn_info->next_run_time_us = clock_->NowMicros() + start_after_us;
|
||||
// the new task start time should never before the current task executing
|
||||
// time, as the executing task can only be running if it's next_run_time_us
|
||||
// is due (<= clock_->NowMicros()).
|
||||
if (executing_task_ &&
|
||||
fn_info->next_run_time_us < heap_.top()->next_run_time_us) {
|
||||
return false;
|
||||
}
|
||||
auto it = map_.find(fn_name);
|
||||
if (it == map_.end()) {
|
||||
heap_.push(fn_info.get());
|
||||
map_.try_emplace(fn_name, std::move(fn_info));
|
||||
} else {
|
||||
// timer doesn't support duplicated function name
|
||||
return false;
|
||||
}
|
||||
cond_var_.SignalAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cancel(const std::string& fn_name) {
|
||||
@@ -116,7 +118,7 @@ class Timer {
|
||||
}
|
||||
|
||||
running_ = true;
|
||||
thread_.reset(new port::Thread(&Timer::Run, this));
|
||||
thread_ = std::make_unique<port::Thread>(&Timer::Run, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -140,8 +142,8 @@ class Timer {
|
||||
|
||||
bool HasPendingTask() const {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto it = map_.begin(); it != map_.end(); it++) {
|
||||
if (it->second->IsValid()) {
|
||||
for (const auto& fn_info : map_) {
|
||||
if (fn_info.second->IsValid()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -155,7 +157,7 @@ class Timer {
|
||||
// here to bump current time and trigger Timer. See timer_test for example.
|
||||
//
|
||||
// Note: only support one caller of this method.
|
||||
void TEST_WaitForRun(std::function<void()> callback = nullptr) {
|
||||
void TEST_WaitForRun(const std::function<void()>& callback = nullptr) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// It act as a spin lock
|
||||
while (executing_task_ ||
|
||||
@@ -177,8 +179,8 @@ class Timer {
|
||||
size_t TEST_GetPendingTaskNum() const {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
size_t ret = 0;
|
||||
for (auto it = map_.begin(); it != map_.end(); it++) {
|
||||
if (it->second->IsValid()) {
|
||||
for (const auto& fn_info : map_) {
|
||||
if (fn_info.second->IsValid()) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
@@ -220,10 +222,13 @@ class Timer {
|
||||
executing_task_ = false;
|
||||
cond_var_.SignalAll();
|
||||
|
||||
// Remove the work from the heap once it is done executing.
|
||||
// Remove the work from the heap once it is done executing, make sure
|
||||
// it's the same function after executing the work while mutex is
|
||||
// released.
|
||||
// Note that we are just removing the pointer from the heap. Its
|
||||
// memory is still managed in the map (as it holds a unique ptr).
|
||||
// So current_fn is still a valid ptr.
|
||||
assert(heap_.top() == current_fn);
|
||||
heap_.pop();
|
||||
|
||||
// current_fn may be cancelled already.
|
||||
@@ -234,6 +239,10 @@ class Timer {
|
||||
|
||||
// Schedule new work into the heap with new time.
|
||||
heap_.push(current_fn);
|
||||
} else {
|
||||
// if current_fn is cancelled or no need to repeat, remove it from the
|
||||
// map to avoid leak.
|
||||
map_.erase(current_fn->name);
|
||||
}
|
||||
} else {
|
||||
cond_var_.TimedWait(current_fn->next_run_time_us);
|
||||
@@ -280,10 +289,10 @@ class Timer {
|
||||
// calls `Cancel()`.
|
||||
bool valid;
|
||||
|
||||
FunctionInfo(std::function<void()>&& _fn, const std::string& _name,
|
||||
FunctionInfo(std::function<void()>&& _fn, std::string _name,
|
||||
const uint64_t _next_run_time_us, uint64_t _repeat_every_us)
|
||||
: fn(std::move(_fn)),
|
||||
name(_name),
|
||||
name(std::move(_name)),
|
||||
next_run_time_us(_next_run_time_us),
|
||||
repeat_every_us(_repeat_every_us),
|
||||
valid(true) {}
|
||||
|
||||
+10
-11
@@ -273,33 +273,32 @@ TEST_F(TimerTest, AddSameFuncName) {
|
||||
ASSERT_TRUE(timer.Start());
|
||||
|
||||
int func_counter1 = 0;
|
||||
timer.Add([&] { func_counter1++; }, "duplicated_func", kInitDelayUs,
|
||||
kRepeat1Us);
|
||||
ASSERT_TRUE(timer.Add([&] { func_counter1++; }, "duplicated_func",
|
||||
kInitDelayUs, kRepeat1Us));
|
||||
|
||||
int func2_counter = 0;
|
||||
timer.Add([&] { func2_counter++; }, "func2", kInitDelayUs, kRepeat2Us);
|
||||
ASSERT_TRUE(
|
||||
timer.Add([&] { func2_counter++; }, "func2", kInitDelayUs, kRepeat2Us));
|
||||
|
||||
// New function with the same name should override the existing one
|
||||
// New function with the same name should fail to add
|
||||
int func_counter2 = 0;
|
||||
timer.Add([&] { func_counter2++; }, "duplicated_func", kInitDelayUs,
|
||||
kRepeat1Us);
|
||||
ASSERT_FALSE(timer.Add([&] { func_counter2++; }, "duplicated_func",
|
||||
kInitDelayUs, kRepeat1Us));
|
||||
|
||||
ASSERT_EQ(0, func_counter1);
|
||||
ASSERT_EQ(0, func2_counter);
|
||||
ASSERT_EQ(0, func_counter2);
|
||||
|
||||
timer.TEST_WaitForRun(
|
||||
[&] { mock_clock_->SleepForMicroseconds(kInitDelayUs); });
|
||||
|
||||
ASSERT_EQ(0, func_counter1);
|
||||
ASSERT_EQ(1, func_counter1);
|
||||
ASSERT_EQ(1, func2_counter);
|
||||
ASSERT_EQ(1, func_counter2);
|
||||
|
||||
timer.TEST_WaitForRun([&] { mock_clock_->SleepForMicroseconds(kRepeat1Us); });
|
||||
|
||||
ASSERT_EQ(0, func_counter1);
|
||||
ASSERT_EQ(2, func_counter1);
|
||||
ASSERT_EQ(2, func2_counter);
|
||||
ASSERT_EQ(2, func_counter2);
|
||||
ASSERT_EQ(0, func_counter2);
|
||||
|
||||
ASSERT_TRUE(timer.Shutdown());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user