mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
20
Commits
release_fix
...
v7.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7119ff818 | ||
|
|
418e58e6a2 | ||
|
|
0e4c063c16 | ||
|
|
fddc1a79dd | ||
|
|
9db48d6e08 | ||
|
|
348414d502 | ||
|
|
7b6ca63ed0 | ||
|
|
9fe3a53443 | ||
|
|
22e011fe0a | ||
|
|
bbae679ce7 | ||
|
|
fe9e363ba1 | ||
|
|
3a4d73d337 | ||
|
|
4ab2fe77f3 | ||
|
|
596c606739 | ||
|
|
a6fd3332e5 | ||
|
|
f278e0e2d3 | ||
|
|
0344b1d331 | ||
|
|
a4bf9311da | ||
|
|
b251c4f5a9 | ||
|
|
a12be569af |
No files matched your search
+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
|
||||
|
||||
+18
-1
@@ -1,5 +1,22 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 7.0.3 (03/23/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a major performance bug in which Bloom filters generated by pre-7.0 releases are not read by early 7.0.x releases (and vice-versa) due to changes to FilterPolicy::Name() in #9590. This can severely impact read performance and read I/O on upgrade or downgrade with existing DB, but not data correctness.
|
||||
* Fixed a bug that `Iterator::Refresh()` reads stale keys after DeleteRange() performed.
|
||||
|
||||
### Public API changes
|
||||
* Added pure virtual FilterPolicy::CompatibilityName(), which is needed for fixing major performance bug involving FilterPolicy naming in SST metadata without affecting Customizable aspect of FilterPolicy. For source code, this change only affects those with their own custom or wrapper FilterPolicy classes, but does break compiled library binary compatibility in a patch release.
|
||||
|
||||
## 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 "
|
||||
|
||||
+47
-22
@@ -58,30 +58,55 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
while (true) {
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
// Refresh range-tombstones in MemTable
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
SuperVersion* sv = cfd_->GetThreadLocalSuperVersion(db_impl_);
|
||||
ReadRangeDelAggregator* range_del_agg =
|
||||
db_iter_->GetRangeDelAggregator();
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter;
|
||||
range_del_iter.reset(
|
||||
sv->mem->NewRangeTombstoneIterator(read_options_, latest_seq));
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
cfd_->ReturnThreadLocalSuperVersion(sv);
|
||||
}
|
||||
// Refresh latest sequence number
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_valid(false);
|
||||
// Check again if the latest super version number is changed
|
||||
uint64_t latest_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (latest_sv_number != cur_sv_number) {
|
||||
// If the super version number is changed after refreshing,
|
||||
// fallback to Re-Init the InternalIterator
|
||||
cur_sv_number = latest_sv_number;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(db_impl_->GetLatestSequenceNumber());
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -3757,6 +3757,9 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
return rep_->CompatibilityName();
|
||||
}
|
||||
// No need to override GetFilterBitsBuilder if this one is overridden
|
||||
ROCKSDB_NAMESPACE::FilterBitsBuilder* GetBuilderWithContext(
|
||||
const ROCKSDB_NAMESPACE::FilterBuildingContext& context)
|
||||
@@ -3794,6 +3797,9 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_format(
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
return rep_->CompatibilityName();
|
||||
}
|
||||
ROCKSDB_NAMESPACE::FilterBitsBuilder* GetBuilderWithContext(
|
||||
const ROCKSDB_NAMESPACE::FilterBuildingContext& context)
|
||||
const override {
|
||||
|
||||
@@ -1551,9 +1551,15 @@ class LevelAndStyleCustomFilterPolicy : public FilterPolicy {
|
||||
policy_l0_other_(NewBloomFilterPolicy(bpk_l0_other)),
|
||||
policy_otherwise_(NewBloomFilterPolicy(bpk_otherwise)) {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "LevelAndStyleCustomFilterPolicy";
|
||||
}
|
||||
|
||||
// OK to use built-in policy name because we are deferring to a
|
||||
// built-in builder. We aren't changing the serialized format.
|
||||
const char* Name() const override { return policy_fifo_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
return policy_fifo_->CompatibilityName();
|
||||
}
|
||||
|
||||
FilterBitsBuilder* GetBuilderWithContext(
|
||||
const FilterBuildingContext& context) const override {
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
@@ -1724,6 +1724,34 @@ TEST_F(DBRangeDelTest, OverlappedKeys) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, IteratorRefresh) {
|
||||
// Refreshing an iterator after a range tombstone is added should cause the
|
||||
// deleted range of keys to disappear.
|
||||
for (bool sv_changed : {false, true}) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key1", "value1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key2", "value2"));
|
||||
|
||||
auto* iter = db_->NewIterator(ReadOptions());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
"key2", "key3"));
|
||||
|
||||
if (sv_changed) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->Refresh());
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ("key1", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+6
-3
@@ -114,15 +114,18 @@ Status TableCache::GetTableReader(
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, fopts, &file, nullptr);
|
||||
}
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
if (s.IsPathNotFound()) {
|
||||
if (s.ok()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
} else if (s.IsPathNotFound()) {
|
||||
fname = Rocks2LevelTableFileName(fname);
|
||||
s = PrepareIOFromReadOptions(ro, ioptions_.clock, fopts.io_options);
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, file_options, &file,
|
||||
nullptr);
|
||||
}
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
if (s.ok()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
|
||||
@@ -90,6 +90,19 @@ class FilterPolicy : public Customizable {
|
||||
virtual ~FilterPolicy();
|
||||
static const char* Type() { return "FilterPolicy"; }
|
||||
|
||||
// The name used for identifying whether a filter on disk is readable
|
||||
// by this FilterPolicy. If this FilterPolicy is part of a family that
|
||||
// can read each others filters, such as built-in BloomFilterPolcy and
|
||||
// RibbonFilterPolicy, the CompatibilityName is a shared family name,
|
||||
// while kinds of filters in the family can have distinct Customizable
|
||||
// Names. This function is pure virtual so that wrappers around built-in
|
||||
// policies are prompted to defer to CompatibilityName() of the wrapped
|
||||
// policy, which is important for compatibility.
|
||||
//
|
||||
// For custom filter policies that are not part of a read-compatible
|
||||
// family (rare), implementations may return Name().
|
||||
virtual const char* CompatibilityName() const = 0;
|
||||
|
||||
// Creates a new FilterPolicy based on the input value string and returns the
|
||||
// result The value might be an ID, and ID with properties, or an old-style
|
||||
// policy string.
|
||||
|
||||
@@ -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 3
|
||||
|
||||
// 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
|
||||
|
||||
@@ -4260,9 +4260,10 @@ void Java_org_rocksdb_ColumnFamilyOptions_setSstPartitionerFactory(
|
||||
JNIEnv*, jobject, jlong jhandle, jlong factory_handle) {
|
||||
auto* options =
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyOptions*>(jhandle);
|
||||
auto* factory = reinterpret_cast<ROCKSDB_NAMESPACE::SstPartitionerFactory*>(
|
||||
auto factory = reinterpret_cast<
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::SstPartitionerFactory>*>(
|
||||
factory_handle);
|
||||
options->sst_partitioner_factory.reset(factory);
|
||||
options->sst_partitioner_factory = *factory;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
@@ -21,7 +22,7 @@ public class SstPartitionerTest {
|
||||
@Rule public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void sstFixedPrefix() throws InterruptedException, RocksDBException {
|
||||
public void sstFixedPrefix() throws RocksDBException {
|
||||
try (SstPartitionerFixedPrefixFactory factory = new SstPartitionerFixedPrefixFactory(4);
|
||||
final Options opt =
|
||||
new Options().setCreateIfMissing(true).setSstPartitionerFactory(factory);
|
||||
@@ -31,7 +32,8 @@ public class SstPartitionerTest {
|
||||
db.put("bbbb1".getBytes(), "B".getBytes());
|
||||
db.flush(new FlushOptions());
|
||||
|
||||
db.put("aaaa1".getBytes(), "A2".getBytes());
|
||||
db.put("aaaa0".getBytes(), "A2".getBytes());
|
||||
db.put("aaaa2".getBytes(), "A2".getBytes());
|
||||
db.flush(new FlushOptions());
|
||||
|
||||
db.compactRange();
|
||||
@@ -40,4 +42,31 @@ public class SstPartitionerTest {
|
||||
assertThat(metadata.size()).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sstFixedPrefixFamily() throws RocksDBException {
|
||||
final byte[] cfName = "new_cf".getBytes(UTF_8);
|
||||
final ColumnFamilyDescriptor cfDescriptor = new ColumnFamilyDescriptor(cfName,
|
||||
new ColumnFamilyOptions().setSstPartitionerFactory(
|
||||
new SstPartitionerFixedPrefixFactory(4)));
|
||||
|
||||
try (final Options opt = new Options().setCreateIfMissing(true);
|
||||
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) {
|
||||
final ColumnFamilyHandle columnFamilyHandle = db.createColumnFamily(cfDescriptor);
|
||||
|
||||
// writing (long)100 under key
|
||||
db.put(columnFamilyHandle, "aaaa1".getBytes(), "A".getBytes());
|
||||
db.put(columnFamilyHandle, "bbbb1".getBytes(), "B".getBytes());
|
||||
db.flush(new FlushOptions(), columnFamilyHandle);
|
||||
|
||||
db.put(columnFamilyHandle, "aaaa0".getBytes(), "A2".getBytes());
|
||||
db.put(columnFamilyHandle, "aaaa2".getBytes(), "A2".getBytes());
|
||||
db.flush(new FlushOptions(), columnFamilyHandle);
|
||||
|
||||
db.compactRange(columnFamilyHandle);
|
||||
|
||||
List<LiveFileMetaData> metadata = db.getLiveFilesMetaData();
|
||||
assertThat(metadata.size()).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -1487,6 +1487,7 @@ class MockFilterPolicy : public FilterPolicy {
|
||||
public:
|
||||
static const char* kClassName() { return "MockFilterPolicy"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
const char* CompatibilityName() const override { return Name(); }
|
||||
FilterBitsBuilder* GetBuilderWithContext(
|
||||
const FilterBuildingContext&) const override {
|
||||
return nullptr;
|
||||
|
||||
+3
-1
@@ -9,6 +9,8 @@
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
#include "port/win/env_win.h"
|
||||
|
||||
#include <direct.h> // _rmdir, _mkdir, _getcwd
|
||||
#include <errno.h>
|
||||
#include <io.h> // _access
|
||||
@@ -17,6 +19,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
@@ -27,7 +30,6 @@
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/port_dirent.h"
|
||||
#include "port/win/env_win.h"
|
||||
#include "port/win/io_win.h"
|
||||
#include "port/win/win_logger.h"
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
@@ -1605,7 +1605,7 @@ void BlockBasedTableBuilder::WriteFilterBlock(
|
||||
? BlockBasedTable::kPartitionedFilterBlockPrefix
|
||||
: BlockBasedTable::kFullFilterBlockPrefix;
|
||||
}
|
||||
key.append(rep_->table_options.filter_policy->Name());
|
||||
key.append(rep_->table_options.filter_policy->CompatibilityName());
|
||||
meta_index_builder->Add(key, filter_block_handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -50,6 +51,7 @@
|
||||
#include "table/block_based/block_prefix_index.h"
|
||||
#include "table/block_based/block_type.h"
|
||||
#include "table/block_based/filter_block.h"
|
||||
#include "table/block_based/filter_policy_internal.h"
|
||||
#include "table/block_based/full_filter_block.h"
|
||||
#include "table/block_based/hash_index_reader.h"
|
||||
#include "table/block_based/partitioned_filter_block.h"
|
||||
@@ -899,33 +901,59 @@ Status BlockBasedTable::PrefetchIndexAndFilterBlocks(
|
||||
const BlockBasedTableOptions& table_options, const int level,
|
||||
size_t file_size, size_t max_file_size_for_l0_meta_pin,
|
||||
BlockCacheLookupContext* lookup_context) {
|
||||
Status s;
|
||||
|
||||
// Find filter handle and filter type
|
||||
if (rep_->filter_policy) {
|
||||
for (auto filter_type :
|
||||
{Rep::FilterType::kFullFilter, Rep::FilterType::kPartitionedFilter,
|
||||
Rep::FilterType::kBlockFilter}) {
|
||||
std::string prefix;
|
||||
switch (filter_type) {
|
||||
case Rep::FilterType::kFullFilter:
|
||||
prefix = kFullFilterBlockPrefix;
|
||||
auto name = rep_->filter_policy->CompatibilityName();
|
||||
bool builtin_compatible =
|
||||
strcmp(name, BuiltinFilterPolicy::kCompatibilityName()) == 0;
|
||||
|
||||
for (const auto& [filter_type, prefix] :
|
||||
{std::make_pair(Rep::FilterType::kFullFilter, kFullFilterBlockPrefix),
|
||||
std::make_pair(Rep::FilterType::kPartitionedFilter,
|
||||
kPartitionedFilterBlockPrefix),
|
||||
std::make_pair(Rep::FilterType::kBlockFilter, kFilterBlockPrefix)}) {
|
||||
if (builtin_compatible) {
|
||||
// This code is only here to deal with a hiccup in early 7.0.x where
|
||||
// there was an unintentional name change in the SST files metadata.
|
||||
// It should be OK to remove this in the future (late 2022) and just
|
||||
// have the 'else' code.
|
||||
// NOTE: the test:: names below are likely not needed but included
|
||||
// out of caution
|
||||
static const std::unordered_set<std::string> kBuiltinNameAndAliases = {
|
||||
BuiltinFilterPolicy::kCompatibilityName(),
|
||||
test::LegacyBloomFilterPolicy::kClassName(),
|
||||
test::FastLocalBloomFilterPolicy::kClassName(),
|
||||
test::Standard128RibbonFilterPolicy::kClassName(),
|
||||
DeprecatedBlockBasedBloomFilterPolicy::kClassName(),
|
||||
BloomFilterPolicy::kClassName(),
|
||||
RibbonFilterPolicy::kClassName(),
|
||||
};
|
||||
|
||||
// For efficiency, do a prefix seek and see if the first match is
|
||||
// good.
|
||||
meta_iter->Seek(prefix);
|
||||
if (meta_iter->status().ok() && meta_iter->Valid()) {
|
||||
Slice key = meta_iter->key();
|
||||
if (key.starts_with(prefix)) {
|
||||
key.remove_prefix(prefix.size());
|
||||
if (kBuiltinNameAndAliases.find(key.ToString()) !=
|
||||
kBuiltinNameAndAliases.end()) {
|
||||
Slice v = meta_iter->value();
|
||||
Status s = rep_->filter_handle.DecodeFrom(&v);
|
||||
if (s.ok()) {
|
||||
rep_->filter_type = filter_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::string filter_block_key = prefix + name;
|
||||
if (FindMetaBlock(meta_iter, filter_block_key, &rep_->filter_handle)
|
||||
.ok()) {
|
||||
rep_->filter_type = filter_type;
|
||||
break;
|
||||
case Rep::FilterType::kPartitionedFilter:
|
||||
prefix = kPartitionedFilterBlockPrefix;
|
||||
break;
|
||||
case Rep::FilterType::kBlockFilter:
|
||||
prefix = kFilterBlockPrefix;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
std::string filter_block_key = prefix;
|
||||
filter_block_key.append(rep_->filter_policy->Name());
|
||||
if (FindMetaBlock(meta_iter, filter_block_key, &rep_->filter_handle)
|
||||
.ok()) {
|
||||
rep_->filter_type = filter_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -934,8 +962,8 @@ Status BlockBasedTable::PrefetchIndexAndFilterBlocks(
|
||||
rep_->index_type == BlockBasedTableOptions::kTwoLevelIndexSearch);
|
||||
|
||||
// Find compression dictionary handle
|
||||
s = FindOptionalMetaBlock(meta_iter, kCompressionDictBlockName,
|
||||
&rep_->compression_dict_handle);
|
||||
Status s = FindOptionalMetaBlock(meta_iter, kCompressionDictBlockName,
|
||||
&rep_->compression_dict_handle);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -3158,6 +3186,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 +3229,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 +3254,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 +3274,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
|
||||
|
||||
@@ -1325,6 +1325,16 @@ bool BuiltinFilterPolicy::IsInstanceOf(const std::string& name) const {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kBuiltinFilterMetadataName = "rocksdb.BuiltinBloomFilter";
|
||||
|
||||
const char* BuiltinFilterPolicy::kCompatibilityName() {
|
||||
return kBuiltinFilterMetadataName;
|
||||
}
|
||||
|
||||
const char* BuiltinFilterPolicy::CompatibilityName() const {
|
||||
return kBuiltinFilterMetadataName;
|
||||
}
|
||||
|
||||
BloomLikeFilterPolicy::BloomLikeFilterPolicy(double bits_per_key)
|
||||
: warned_(false), aggregate_rounding_balance_(0) {
|
||||
// Sanitize bits_per_key
|
||||
@@ -1372,7 +1382,7 @@ bool BloomLikeFilterPolicy::IsInstanceOf(const std::string& name) const {
|
||||
}
|
||||
|
||||
const char* ReadOnlyBuiltinFilterPolicy::kClassName() {
|
||||
return "rocksdb.BuiltinBloomFilter";
|
||||
return kBuiltinFilterMetadataName;
|
||||
}
|
||||
|
||||
const char* DeprecatedBlockBasedBloomFilterPolicy::kClassName() {
|
||||
|
||||
@@ -135,6 +135,9 @@ class BuiltinFilterPolicy : public FilterPolicy {
|
||||
FilterBitsReader* GetFilterBitsReader(const Slice& contents) const override;
|
||||
static const char* kClassName();
|
||||
bool IsInstanceOf(const std::string& id) const override;
|
||||
// All variants of BuiltinFilterPolicy can read each others filters.
|
||||
const char* CompatibilityName() const override;
|
||||
static const char* kCompatibilityName();
|
||||
|
||||
public: // new
|
||||
// An internal function for the implementation of
|
||||
|
||||
@@ -84,6 +84,7 @@ class TestFilterBitsReader : public FilterBitsReader {
|
||||
class TestHashFilter : public FilterPolicy {
|
||||
public:
|
||||
const char* Name() const override { return "TestHashFilter"; }
|
||||
const char* CompatibilityName() const override { return Name(); }
|
||||
|
||||
FilterBitsBuilder* GetBuilderWithContext(
|
||||
const FilterBuildingContext&) const override {
|
||||
|
||||
+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